Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import altair as alt | |
| import os | |
| # --- PAGE CONFIGURATION --- | |
| st.set_page_config(layout="wide", page_title="IMDb Cinematic Trends") | |
| # Enable Altair Dark Theme for visibility on black backgrounds | |
| alt.themes.enable('dark') | |
| # --- DATA LOADING (Cached for Speed) --- | |
| def load_all_data(): | |
| # Using relative paths matching your Hugging Face structure | |
| # If files are in a 'src' folder, we check there first | |
| main_path = 'src/merged_imdb_sample.csv' if os.path.exists('src/merged_imdb_sample.csv') else 'merged_imdb_sample.csv' | |
| ctx_path = 'src/principals_context_sample.csv' if os.path.exists('src/principals_context_sample.csv') else 'principals_context_sample.csv' | |
| # 1. Load Primary Data | |
| df = pd.read_csv(main_path) | |
| df['decade'] = (df['startYear'] // 10 * 10).astype(int).astype(str) + 's' | |
| df['genre_list'] = df['genres'].str.split(',') | |
| df_exploded = df.explode('genre_list') | |
| df_exploded['genre_list'] = df_exploded['genre_list'].str.strip() | |
| # 2. Load Contextual Data | |
| df_ctx = pd.read_csv(ctx_path) | |
| return df_exploded, df_ctx | |
| try: | |
| df, df_ctx = load_all_data() | |
| except FileNotFoundError: | |
| st.error("⚠️ CSV files not found. Please ensure the datasets are in the 'src' folder or root directory.") | |
| st.stop() | |
| # --- HEADER & NAVIGATION --- | |
| st.title("The Architects of Entertainment: Mapping Quality and Collaboration") | |
| st.write("**Authors:** Group 6 (Le Kim Ngan Hoang)") | |
| col_nav1, col_nav2, col_nav3 = st.columns(3) | |
| with col_nav1: | |
| st.link_button("📂 Primary Dataset", "https://github.com/lekimngan3010/IS455_Final_FP3_Group6/blob/main/merged_imdb_sample.csv") | |
| with col_nav2: | |
| st.link_button("📂 Contextual Dataset", "https://github.com/lekimngan3010/IS455_Final_FP3_Group6/blob/main/principals_context_sample.csv") | |
| with col_nav3: | |
| st.link_button("🐍 Python Notebook", "https://github.com/lekimngan3010/IS455_Final_FP3_Group6/blob/main/analysis.ipynb") | |
| st.divider() | |
| # --- NARRATIVE PARAGRAPH 1 --- | |
| st.markdown(""" | |
| ### The Evolution of Cinematic Quality | |
| Cinema is often discussed through the lens of individual success—a high rating or a famous director. | |
| As seen in our primary dashboard, certain decades show marked shifts in average ratings for different genres. | |
| By exploring the trends below, we can identify 'Golden Eras' and the creators who defined them. | |
| """) | |
| # --- MAIN INTERACTIVE DASHBOARD (ALTAIR) --- | |
| st.header("1. Interactive Trend Explorer") | |
| st.info("💡 **Interaction:** Click a cell in the Heatmap to filter the Top Directors chart below.") | |
| # 1. Define selection WITHOUT a default value (prevents the Schema Error) | |
| # 'toggle=False' makes it so clicking outside doesn't clear the selection | |
| selection = alt.selection_point( | |
| fields=['genre_list', 'decade'], | |
| name='cell', | |
| toggle=False | |
| ) | |
| # 2. Heatmap | |
| heatmap = alt.Chart(df).mark_rect().encode( | |
| x=alt.X('decade:O', title='Decade'), | |
| y=alt.Y('genre_list:N', title='Genre'), | |
| color=alt.Color('mean(averageRating):Q', | |
| scale=alt.Scale(scheme='redyellowgreen'), | |
| title='Avg Rating'), | |
| stroke=alt.condition(selection, alt.value('white'), alt.value('transparent')), | |
| strokeWidth=alt.condition(selection, alt.value(2), alt.value(0)), | |
| tooltip=['genre_list', 'decade', 'mean(averageRating)'] | |
| ).properties( | |
| width=800, | |
| height=400, | |
| title="Average Movie Ratings by Genre and Decade" | |
| ).add_params(selection) | |
| # 3. Bar Chart with "Fallback" Logic | |
| bar_chart = alt.Chart(df).mark_bar().encode( | |
| x=alt.X('total_votes:Q', title='Total Popularity (Votes)'), | |
| y=alt.Y('directorName:N', sort='-x', title='Director'), | |
| color=alt.value('#4682B4'), | |
| tooltip=['directorName', 'sum(numVotes)'] | |
| ).transform_filter( | |
| # If nothing is selected, this filter now returns TRUE for everything | |
| # OR you can keep it strict. | |
| selection | |
| ).transform_aggregate( | |
| total_votes='sum(numVotes)', | |
| groupby=['directorName'] | |
| ).transform_window( | |
| rank='rank()', | |
| sort=[alt.SortField('total_votes', order='descending')] | |
| ).transform_filter( | |
| alt.datum.rank <= 10 | |
| ).properties( | |
| width=800, | |
| height=300, | |
| title="Top 10 Directors (Select a cell to view)" | |
| ) | |
| # Render charts with standard dark-mode visibility | |
| st.altair_chart(heatmap & bar_chart, use_container_width=True) | |
| # --- NARRATIVE PARAGRAPH 2 --- | |
| st.markdown(""" | |
| ### Behind the Screen: Professional Infrastructure | |
| While our dashboard focuses on ratings, the first contextual view below, **Professional Roles**, reveals | |
| the vast workforce required to achieve these scores. For every director highlighted in our dashboard, | |
| there is an infrastructure of writers, composers, and cinematographers. This visualization shifts the | |
| perspective from a single 'Auteur' to a realistic view of cinema as a multifaceted professional ecosystem. | |
| """) | |
| # --- CONTEXTUAL CHARTS --- | |
| st.header("2. Production Context") | |
| ctx_col1, ctx_col2 = st.columns(2) | |
| with ctx_col1: | |
| role_dist = alt.Chart(df_ctx).mark_bar().encode( | |
| x=alt.X('count():Q', title='Number of Credits'), | |
| y=alt.Y('category:N', sort='-x', title='Role Category'), | |
| color=alt.value('#57A44C'), | |
| tooltip=['category', 'count()'] | |
| ).properties( | |
| title="Distribution of Professional Roles", | |
| height=300 | |
| ) | |
| st.altair_chart(role_dist, use_container_width=True) | |
| st.caption("Source: IMDb Principals Sample. Highlights technical vs. creative workforce balance.") | |
| with ctx_col2: | |
| crew_counts = df_ctx.groupby('tconst').size().reset_index(name='crew_count') | |
| collab_density = alt.Chart(crew_counts).mark_bar().encode( | |
| x=alt.X('crew_count:Q', bin=alt.Bin(maxbins=10), title='Key Personnel per Film'), | |
| y=alt.Y('count():Q', title='Frequency'), | |
| color=alt.value('#F2CF5B') | |
| ).properties( | |
| title="Collaboration Density (Team Size)", | |
| height=300 | |
| ) | |
| st.altair_chart(collab_density, use_container_width=True) | |
| st.caption("Source: IMDb Principals Sample. Most successful films require 4-8 key leaders.") | |
| # --- NARRATIVE PARAGRAPH 3 --- | |
| st.markdown(""" | |
| ### The Final Story: Complexity and Quality | |
| Our final contextual chart, **Collaboration Density**, highlights the scale of these productions. | |
| Most high-quality films in our sample rely on 4 to 10 'principal' personnel in leadership roles. | |
| This suggests a strong correlation between cinematic quality and team density. | |
| """) |