Spaces:
Running
Running
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
from prophet import Prophet | |
import plotly.express as px | |
import plotly.graph_objects as go | |
import seaborn as sns | |
import matplotlib.pyplot as plt | |
from datetime import date | |
# --------------------------------------------- | |
# CONFIG -------------------------------------- | |
# --------------------------------------------- | |
DATA_PATH = "price_data.csv" # CSV: date, item, price | |
MACRO_START, MACRO_END = "1996-01-01", "2030-12-31" | |
MICRO_START, MICRO_END = "2020-01-01", "2026-12-31" | |
st.set_page_config(page_title="ํ๋ชฉ๋ณ ๊ฐ๊ฒฉ ์์ธก", page_icon="๐", layout="wide") | |
# --------------------------------------------- | |
# UTILITIES ----------------------------------- | |
# --------------------------------------------- | |
def load_data(path: str) -> pd.DataFrame: | |
df = pd.read_csv(path, parse_dates=["date"]) | |
df.sort_values("date", inplace=True) | |
return df | |
def get_items(df: pd.DataFrame): | |
return sorted(df["item"].unique()) | |
def fit_prophet(df: pd.DataFrame, horizon_end: str): | |
m = Prophet(yearly_seasonality=True, weekly_seasonality=False, daily_seasonality=False) | |
m.fit(df.rename(columns={"date": "ds", "price": "y"})) | |
future = m.make_future_dataframe(periods=(pd.Timestamp(horizon_end) - df["date"].max()).days, freq="D") | |
forecast = m.predict(future) | |
return m, forecast | |
# --------------------------------------------- | |
# LOAD ---------------------------------------- | |
# --------------------------------------------- | |
raw_df = load_data(DATA_PATH) | |
selected_item = st.sidebar.selectbox("๐ ํ๋ชฉ ์ ํ", get_items(raw_df)) | |
current_date = date.today() | |
st.sidebar.write(f"**์ค๋:** {current_date}") | |
item_df = raw_df[raw_df["item"] == selected_item].copy() | |
if item_df.empty: | |
st.error("๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.") | |
st.stop() | |
# --------------------------------------------- | |
# MACRO FORECAST ------------------------------ | |
# --------------------------------------------- | |
st.header(f"๐ {selected_item} ๊ฐ๊ฒฉ ์ ๋ง") | |
macro_df = item_df[item_df["date"] >= MACRO_START] | |
m_macro, fc_macro = fit_prophet(macro_df, MACRO_END) | |
fig_macro = px.line(fc_macro, x="ds", y="yhat", title="Macro Forecast 1996โ2030") | |
fig_macro.add_scatter(x=macro_df["date"], y=macro_df["price"], mode="lines", name="Actual") | |
st.plotly_chart(fig_macro, use_container_width=True) | |
# --- Metrics โ | |
latest_price = macro_df.iloc[-1]["price"] | |
macro_last = fc_macro[fc_macro["ds"] == MACRO_END]["yhat"].iloc[0] | |
macro_diff = macro_last - latest_price | |
macro_pct = macro_diff / latest_price * 100 | |
st.metric(label="2030 ์์ธก ๊ฐ๊ฒฉ", value=f"{macro_last:,.0f}", delta=f"{macro_pct:+.1f}% vs ์ต๊ทผ") | |
# --------------------------------------------- | |
# MICRO FORECAST ------------------------------ | |
# --------------------------------------------- | |
st.subheader("๐ ๋ฏธ์ ์์ธก 2024โ2026") | |
micro_df = item_df[item_df["date"] >= MICRO_START] | |
m_micro, fc_micro = fit_prophet(micro_df, MICRO_END) | |
fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast 2024โ2026") | |
fig_micro.add_scatter(x=micro_df["date"], y=micro_df["price"], mode="lines", name="Actual") | |
st.plotly_chart(fig_micro, use_container_width=True) | |
micro_last = fc_micro[fc_micro["ds"] == MICRO_END]["yhat"].iloc[0] | |
micro_diff = micro_last - latest_price | |
micro_pct = micro_diff / latest_price * 100 | |
st.metric(label="2026 ์์ธก ๊ฐ๊ฒฉ", value=f"{micro_last:,.0f}", delta=f"{micro_pct:+.1f}% vs ์ต๊ทผ") | |
# --------------------------------------------- | |
# PATTERN & SEASONALITY ----------------------- | |
# --------------------------------------------- | |
with st.expander("๐ ์์ฆ๋๋ฆฌํฐ ๋ถ์ ๋ฐ ํจํด ํด์ค"): | |
comp_fig = m_micro.plot_components(fc_micro) | |
st.pyplot(comp_fig) | |
# ์๋ณ seasonality summary | |
month_season = (fc_micro[["ds", "yearly"]] | |
.assign(month=lambda d: d["ds"].dt.month) | |
.groupby("month")["yearly"].mean()) | |
peak_month = int(month_season.idxmax()) | |
trough_month = int(month_season.idxmin()) | |
st.markdown(f"**ํจํด ์์ฝ** \n- __์ฐ๊ฐ ํผํฌ__: {peak_month}์ \n- __์ฐ๊ฐ ์ ์ __: {trough_month}์ \n- ํ๊ท ๋ณ๋ํญ(์ฐ): {month_season.max() - month_season.min():.1f} ๋จ๊ฐ") | |
# --------------------------------------------- | |
# CORRELATION HEATMAP ------------------------- | |
# --------------------------------------------- | |
st.subheader("๐งฎ ํ๋ชฉ ๊ฐ ์๊ด๊ด๊ณ ํํธ๋งต") | |
corr_df = (raw_df.assign(month=lambda d: d["date"].dt.to_period("M")) | |
.groupby(["month", "item"], as_index=False)["price"].mean() | |
.pivot(index="month", columns="item", values="price")) | |
corr = corr_df.corr() | |
fig, ax = plt.subplots(figsize=(12, 10)) | |
mask = np.triu(np.ones_like(corr, dtype=bool)) | |
sns.heatmap(corr, mask=mask, cmap="RdBu_r", center=0, linewidths=.5, ax=ax) | |
st.pyplot(fig) | |
st.markdown(""" | |
**ํด์ ๊ฐ์ด๋** | |
- **๋นจ๊ฐ์(+)**: ๋ ํ๋ชฉ ๊ฐ๊ฒฉ์ด ๋์กฐํ โ ๊ณต๊ธ๋ง/์์ ์ฐ๋ ๊ฐ๋ฅ์ฑ. | |
- **ํ๋์(-)**: ๋์ฒด์ฌ ๊ด๊ณ. | |
- ์ ๋๊ฐ โฅ 0.7 ์ ์ ์ฑ ยท์ฌ๊ณ ์ ๋ต ์ค๊ณ ์ ์ฃผ์ ๊น๊ฒ ๋ณผ ํ์๊ฐ ์์ต๋๋ค. | |
""") | |
# --------------------------------------------- | |
# EXTRA CHARTS ------------------------------- | |
# --------------------------------------------- | |
st.subheader("๐ ์ถ๊ฐ ์ธ์ฌ์ดํธ: 30์ผ ์ด๋ ๋ณ๋์ฑ") | |
vol_df = (item_df.set_index("date")["price"].rolling(30).std().reset_index()) | |
fig_vol = px.area(vol_df, x="date", y="price", title="30D Rolling Std Dev") | |
st.plotly_chart(fig_vol, use_container_width=True) | |
st.markdown(""" | |
- ๋ณ๋์ฑ ๊ธ๋ฑ ๊ตฌ๊ฐ์ **๊ณต๊ธ ์ถฉ๊ฒฉยท์์ ์ด๋ฒคํธ** ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. | |
- ์ต๊ทผ ๋ณ๋์ฑ์ด ๋ฎ์์ง๋ฉด **๊ฐ๊ฒฉ ์์ฐฉ**์ผ๋ก ํด์ํ ์ ์์ต๋๋ค. | |
""") | |
# --------------------------------------------- | |
# FOOTER -------------------------------------- | |
# --------------------------------------------- | |
st.caption("๋ฐ์ดํฐ ์ถ์ฒ: ๋ด๋ถ ๋์์ฐ๋ฌผ ๊ฐ๊ฒฉ DB ยท Forecast by Prophet ยท Dashboard built with Streamlit") | |