IS455_FP3 / src /streamlit_app.py
lekimngan3010's picture
Update src/streamlit_app.py
099aa6b verified
Raw
History Blame Contribute Delete
11.3 kB
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
@st.cache_data
def load_all_data():
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 = df[df['startYear'] > 1900].copy()
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")
st.write("Le Kim Ngan Hoang - lkhoang2")
st.write("Karen Xiong - karenhx2")
st.write("Liting Chen - lchen235")
st.write("Kelsey Li - yitingl7")
col_nav1, col_nav2, col_nav3 = st.columns(3)
with col_nav1:
st.link_button("📂 Primary Dataset", "https://github.com/Lekimnganhoang3010/IS_445_Final-Project/blob/main/merged_imdb_sample.csv")
with col_nav2:
st.link_button("📂 Contextual Dataset", "https://github.com/Lekimnganhoang3010/IS_445_Final-Project/blob/main/principals_context_sample.csv")
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.
Each square in the heatmap's grid corresponds to a specific genre during a specific decade, with its color correlating
to the average ratings of movies under that genre during that decade (the color scale is denoted to the right of the
heatmap). Uncolored squares indicate a lack of information from our original dataset (i.e., no movies were found to
be under that genre during that decade). Upon clicking a given colored cell, the graph displaying the top 10 directors
(in terms of total votes received) for that specific genre-decade combination is appropriately updated. Clicking the
aforementioned dark cells results in a chart displaying the overall top 10 directors across all of the studied movies.
This can be useful in providing recommendations to users, both for those who are interested in specific genres and/or
decades of movies and those who are generally looking for highly rated types of movies. Furthermore, information about
the directors is important in terms of informing users about which director's movies are most well-rated if they have a
favorite genre and/or decade of movies. This dashboard is extremely informative, providing an overview of the most well-rated
genres and decades, along with the top directors for them, and how those ratings have changed over time.
""")
# Main Interactive Dashboard with Altair
st.header("1. Interactive Trend Explorer")
st.info("💡 **Interaction:** Click a cell in the Heatmap to filter the Top Directors chart below.")
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', axis=alt.Axis(labelLimit=200)),
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
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', axis=alt.Axis(labelLimit=200)),
color=alt.value('#4682B4'),
tooltip=['directorName', alt.Tooltip('total_votes:Q', format=',')]
).transform_filter(
selection # Ensure 'selection' is defined above this block!
).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.
This contextual visualization examines how different movie credit roles are positioned in the IMDb
principal dataset. In this dataset, each row represents a person connected to a movie, such as an actor,
actress, director, writer, or producer. The ordering column shows the order in which that person appears in
the movie’s principal credits. A smaller number means that the person appears earlier in the credits, so
this chart focuses on the first few credit positions to understand which roles are usually given the most
visibility. This visualization is useful because it gives background context for the main movie dataset.
The main dataset focuses on movie-level information, such as title, genre, runtime, year, average rating,
and number of votes. However, movies are not only defined by their ratings or genres - they are also shaped
by the people involved in creating them. By looking at the credit order, readers can better understand
which types of contributors are most visible in IMDb’s movie records. For example, if actors and actresses
appear most often in the earliest credit positions, this suggests that on-screen performers are usually
emphasized more strongly than "behind the scenes" roles. While the main dataset focuses on movie ratings,
genres, and runtimes, this contextual visualization adds background about the people behind those movies.
It suggests that movie records are not only about the films themselves, but also about how different
contributors, such as actors, actresses, directors, and writers, are presented to the public.
""")
# 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', axis=alt.Axis(labelLimit=200)),
color=alt.value('#57A44C'),
tooltip=['category', 'count()']
).properties(
title="Distribution of Professional Roles",
height=400
)
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=400
)
st.altair_chart(collab_density, use_container_width=True)
st.caption("Source: IMDb Principals Sample. Most successful films require 10-25 key leaders.")
# Narrative Paragraph
st.markdown("""
### The Final Story: Complexity and Quality
Our final contextual chart, **Collaboration Density**, highlights the scale of these productions.
This chart portrays the frequency of the different numbers of key staff members working on each
film that was observed from our dataset, with higher bars corresponding to greater frequency (i.e.,
more movies with that particular number of personnel). Most high-quality films in our sample rely on
10 to 25 principal personnel in leadership roles, with the highest peak at 15 to 20 personnel.
Additionally, we do not observe more than 35 personnel working on any of the films from our original
dataset, indicating that this is a general threshold in terms of trends in film staff size.
These observations suggest a strong correlation between cinematic quality and team density, providing further
context for our primary dashboard showcasing the movies' average ratings.
""")
st.divider()
st.markdown("""
### Citation
#### Original Dataset
The name of the dataset is “merged_imdb_sample”. This aggregated dataset is created using IMDb Non-Commercial Datasets: https://datasets.imdbws.com/
Given that the original datasets are computationally expensive and exceed typical project and GitHub storage limits, a sampling and preprocessing strategy was applied:
- A subset of approximately 1021 movie records was randomly sampled from the primary table (**title.basics**) after filtering for valid (non-null) entries.
- Related tables (**title.ratings, title.crew, and name.basics**) were then filtered using matching keys (tconst, nconst) to ensure relational consistency.
- Only relevant columns (e.g., title, year, genre, rating, votes, director name) were retained to reduce dimensionality.
- Missing values were selectively handled to preserve as much usable data as possible without significantly reducing the dataset size.
#### Contextual dataset
To add further depth to the story of how certain genres and directors rose to prominence, I have identified the following contextual dataset:
- **Dataset Name**: IMDb Title Principals
- **Link**: https://datasets.imdbws.com/
- **Contextual Utility**:
While the current dashboard focuses on directors, the title.principals dataset provides data on the entire "creative team," including lead actors, writers, and cinematographers.
This would allow us to investigate if the high ratings in specific genres (like Sci-Fi or Noir) were driven by specific recurring collaborations or technical shifts in cinematography.""")