diff --git a/.streamlit/config.toml b/.streamlit/config.toml new file mode 100644 index 0000000..df3ef0c --- /dev/null +++ b/.streamlit/config.toml @@ -0,0 +1,6 @@ +[theme] +primaryColor = "#1B4F8A" +backgroundColor = "#F0F4F9" +secondaryBackgroundColor = "#E4EBF4" +textColor = "#1A1A1A" +font = "serif" diff --git a/Homepage.py b/Homepage.py index 08b2250..b14427b 100644 --- a/Homepage.py +++ b/Homepage.py @@ -13,25 +13,57 @@ st.set_page_config(page_title="Weekly U.S. Petroleum Supply", layout="wide") # ========================= -# Sidebar title +# Sidebar title (above nav via CSS) # ========================= -st.sidebar.markdown( +st.markdown( """ -

- U.S. Petroleum & WTI Weekly Monitor -

+ """, unsafe_allow_html=True, ) -st.sidebar.caption("Source: EIA") -st.sidebar.divider() # ========================= # Main page header # ========================= st.title("Weekly U.S. Petroleum Supply") -st.subheader("Team Members: Irina, Indra") -st.caption("Source: U.S. Energy Information Administration (EIA)") +st.caption("Team Members: Irina, Indra ยท Source: U.S. Energy Information Administration (EIA)") # ========================= # Project Proposal @@ -332,63 +364,71 @@ def compute_product_price_sensitivity( st.divider() # ========================= -# Two side-by-side charts +# Stacked charts # ========================= -left_col, right_col = st.columns(TWO_COLUMN_LAYOUT) +st.subheader("Total Product Supplied") -with left_col: - st.subheader("Total Product Supplied") +fig = px.line( + filtered_total, + x="week", + y="total_supply", + labels={"week": "Week", "total_supply": "Total Product Supplied"}, +) +fig.update_layout( + hovermode="x unified", + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", +) +fig.update_traces(hovertemplate="%{x|%b %d, %Y}
Total Supply: %{y:,.0f}") +st.plotly_chart(fig, use_container_width=True) - fig = px.line( - filtered_total, +with st.expander("Show total supply data table"): + total_display = filtered_total.sort_values("week", ascending=False).copy() + total_display["week"] = total_display["week"].dt.strftime("%Y-%m-%d") + st.dataframe(total_display, width="stretch") + +st.divider() + +st.subheader("Product-Level Weekly Supply") + +if not selected_products: + st.warning("Please select at least one product from the sidebar.") +else: + product_plot_df = filtered_product[ + filtered_product["product_name"].isin(selected_products) + ].copy() + + fig2 = px.line( + product_plot_df, x="week", - y="total_supply", - labels={"week": "Week", "total_supply": "Total Product Supplied"}, + y="product_supplied", + color="product_name", + labels={ + "week": "Week", + "product_supplied": "Product Supplied", + "product_name": "Product", + }, ) - fig.update_layout(hovermode="x unified") - fig.update_traces( - hovertemplate="%{x|%b %d, %Y}
Total Supply: %{y:,.0f}" + fig2.update_layout( + hovermode="x unified", + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", ) - st.plotly_chart(fig, use_container_width=True) - - with st.expander("Show total supply data table"): - total_display = filtered_total.sort_values("week", ascending=False).copy() - total_display["week"] = total_display["week"].dt.strftime("%Y-%m-%d") - st.dataframe(total_display, width="stretch") - -with right_col: - st.subheader("Product-Level Weekly Supply") - - if not selected_products: - st.warning("Please select at least one product from the sidebar.") - else: - product_plot_df = filtered_product[ - filtered_product["product_name"].isin(selected_products) - ].copy() - - fig2 = px.line( - product_plot_df, - x="week", - y="product_supplied", - color="product_name", - labels={ - "week": "Week", - "product_supplied": "Product Supplied", - "product_name": "Product", - }, - ) - fig2.update_layout(hovermode="x unified") - fig2.update_traces( - hovertemplate="%{fullData.name}
%{x|%b %d, %Y}: %{y:,.0f}" - ) - st.plotly_chart(fig2, use_container_width=True) - - with st.expander("Show product-level data table"): - product_display = product_plot_df.sort_values( - ["product_name", "week"], ascending=[True, False] - ).copy() - product_display["week"] = product_display["week"].dt.strftime("%Y-%m-%d") - st.dataframe(product_display, width="stretch") + fig2.update_traces( + hovertemplate="%{fullData.name}
%{x|%b %d, %Y}: %{y:,.0f}" + ) + st.plotly_chart(fig2, use_container_width=True) + + with st.expander("Show product-level data table"): + product_display = product_plot_df.sort_values( + ["product_name", "week"], ascending=[True, False] + ).copy() + product_display["week"] = product_display["week"].dt.strftime("%Y-%m-%d") + st.dataframe(product_display, width="stretch") st.divider() diff --git a/pages/2_WTI_Price.py b/pages/2_WTI_Price.py index c45a0d6..de98665 100644 --- a/pages/2_WTI_Price.py +++ b/pages/2_WTI_Price.py @@ -14,18 +14,51 @@ st.set_page_config(page_title="WTI Price", layout="wide") # ========================= -# Sidebar title +# Sidebar title (above nav via CSS) # ========================= -st.sidebar.markdown( +st.markdown( """ -

- U.S. Petroleum & WTI Weekly Monitor -

+ """, unsafe_allow_html=True, ) -st.sidebar.caption("Source: EIA") -st.sidebar.divider() # ========================= # Main page header @@ -256,60 +289,66 @@ def find_top_highest_years(yearly_avg: pd.DataFrame, top_n: int) -> set[int]: st.divider() # ========================= -# Two charts side by side +# Stacked charts # ========================= -left_col, right_col = st.columns(TWO_COLUMN_LAYOUT) - -with left_col: - st.subheader("WTI Price Over Time (Weekly)") - - fig = go.Figure() - fig.add_trace( - go.Scatter( - x=filtered_wti["week"], - y=filtered_wti["wti_price"], - name="WTI Price", - mode="lines", - hovertemplate="WTI Price: $%{y:.2f}", - ) - ) - fig.add_trace( - go.Scatter( - x=filtered_wti["week"], - y=filtered_wti["wti_ma"], - name=f"{ma_window}-Week Moving Average", - mode="lines", - hovertemplate=f"{ma_window}-Week Avg: $%{{y:.2f}}", - ) - ) - fig.update_layout( - xaxis_title="Week", - yaxis_title="WTI Price ($/barrel)", - hovermode="x unified", - hoverlabel=dict(namelength=-1), +st.subheader("WTI Price Over Time (Weekly)") + +fig = go.Figure() +fig.add_trace( + go.Scatter( + x=filtered_wti["week"], + y=filtered_wti["wti_price"], + name="WTI Price", + mode="lines", + hovertemplate="WTI Price: $%{y:.2f}", ) - st.plotly_chart(fig, use_container_width=True) - -with right_col: - st.subheader("Weekly Change in WTI Price") - - fig2 = go.Figure() - fig2.add_trace( - go.Scatter( - x=filtered_wti["week"], - y=filtered_wti["weekly_change"], - mode="lines", - name="Weekly Change", - hovertemplate="%{x|%b %d, %Y}
Change: $%{y:.2f}", - ) +) +fig.add_trace( + go.Scatter( + x=filtered_wti["week"], + y=filtered_wti["wti_ma"], + name=f"{ma_window}-Week Moving Average", + mode="lines", + hovertemplate=f"{ma_window}-Week Avg: $%{{y:.2f}}", ) - fig2.add_hline(y=0, line_color="gray", line_width=1) - fig2.update_layout( - xaxis_title="Week", - yaxis_title="Weekly Change ($/barrel)", - hovermode="x unified", +) +fig.update_layout( + xaxis_title="Week", + yaxis_title="WTI Price ($/barrel)", + hovermode="x unified", + hoverlabel=dict(namelength=-1), + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", +) +st.plotly_chart(fig, use_container_width=True) + +st.divider() + +st.subheader("Weekly Change in WTI Price") + +fig2 = go.Figure() +fig2.add_trace( + go.Scatter( + x=filtered_wti["week"], + y=filtered_wti["weekly_change"], + mode="lines", + name="Weekly Change", + hovertemplate="%{x|%b %d, %Y}
Change: $%{y:.2f}", ) - st.plotly_chart(fig2, use_container_width=True) +) +fig2.add_hline(y=0, line_color="#0D2B5E", line_width=1.5) +fig2.update_layout( + xaxis_title="Week", + yaxis_title="Weekly Change ($/barrel)", + hovermode="x unified", + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", +) +st.plotly_chart(fig2, use_container_width=True) st.divider() st.subheader("Real-Time Interpretation") diff --git a/pages/3_Event_Context.py b/pages/3_Event_Context.py index 775f87f..503ef29 100644 --- a/pages/3_Event_Context.py +++ b/pages/3_Event_Context.py @@ -14,16 +14,51 @@ # ========================= # Sidebar title # ========================= -st.sidebar.markdown( +# Sidebar title (above nav via CSS) +# ========================= +st.markdown( """ -

- U.S. Petroleum & WTI Weekly Monitor -

+ """, unsafe_allow_html=True, ) -st.sidebar.caption("Source: EIA + GDELT") -st.sidebar.divider() # ========================= # Main page header @@ -263,55 +298,63 @@ def build_anomaly_table(merged_df: pd.DataFrame, top_n: int) -> pd.DataFrame: st.divider() # ========================= -# Two side-by-side charts +# Stacked charts # ========================= -left_col, right_col = st.columns(TWO_COLUMN_LAYOUT) +st.subheader("Weekly Event Count") -with left_col: - st.subheader("Weekly Event Count") +fig1 = px.line( + filtered, + x="week", + y="event_count", + labels={"week": "Week", "event_count": "Event Count"}, +) +fig1.update_layout( + hovermode="x unified", + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", +) +fig1.update_traces(hovertemplate="%{x|%b %d, %Y}
Events: %{y:,.0f}") +st.plotly_chart(fig1, use_container_width=True) - fig1 = px.line( - filtered, - x="week", - y="event_count", - labels={"week": "Week", "event_count": "Event Count"}, - ) - fig1.update_layout(hovermode="x unified") - fig1.update_traces(hovertemplate="%{x|%b %d, %Y}
Events: %{y:,.0f}") - st.plotly_chart(fig1, use_container_width=True) +st.caption( + "This chart shows how many GDELT-recorded events occurred each week. " + "Use it to see whether broader event activity rises during weeks when " + "WTI or petroleum supply experiences unusual movement." +) - st.caption( - "This chart shows how many GDELT-recorded events occurred each week. " - "Use it to see whether broader event activity rises during weeks when " - "WTI or petroleum supply experiences unusual movement." - ) +st.divider() -with right_col: - st.subheader("Average Event Tone") - - fig2 = go.Figure() - fig2.add_trace( - go.Scatter( - x=filtered["week"], - y=filtered["avg_tone"], - mode="lines", - name="Average Tone", - hovertemplate="%{x|%b %d, %Y}
Avg Tone: %{y:.2f}", - ) - ) - fig2.add_hline(y=0, line_color="gray", line_width=1) - fig2.update_layout( - xaxis_title="Week", - yaxis_title="Average Tone", - hovermode="x unified", - ) - st.plotly_chart(fig2, use_container_width=True) +st.subheader("Average Event Tone") - st.caption( - "This chart tracks the average tone of events in each week. " - "More negative values suggest a more adverse event environment, which may " - "help contextualize stress periods in the energy data." +fig2 = go.Figure() +fig2.add_trace( + go.Scatter( + x=filtered["week"], + y=filtered["avg_tone"], + mode="lines", + name="Average Tone", + hovertemplate="%{x|%b %d, %Y}
Avg Tone: %{y:.2f}", ) +) +fig2.add_hline(y=0, line_color="#0D2B5E", line_width=1.5) +fig2.update_layout( + xaxis_title="Week", + yaxis_title="Average Tone", + hovermode="x unified", + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", +) +st.plotly_chart(fig2, use_container_width=True) + +st.caption( + "This chart tracks the average tone of events in each week. " + "More negative values suggest a more adverse event environment, which may " + "help contextualize stress periods in the energy data." +) st.divider() st.subheader("Weekly Event Composition") @@ -361,6 +404,10 @@ def build_anomaly_table(merged_df: pd.DataFrame, top_n: int) -> pd.DataFrame: xaxis_title="Week", yaxis_title="Event Count", hovermode="x unified", + xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"), + plot_bgcolor="rgba(0,0,0,0)", + paper_bgcolor="rgba(0,0,0,0)", ) st.plotly_chart(fig3, use_container_width=True)