Spaces:
Running
Running
File size: 5,487 Bytes
1acd6e1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
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
# --------------------------------------------------
# 0. CONFIG & UTILS
# --------------------------------------------------
DATA_PATH = "price_data.csv" # โถ๏ธ CSV: date(YYYY-MM-DD), item, price
@st.cache_data(show_spinner=False)
def load_data(path: str) -> pd.DataFrame:
"""Load & preprocess price data.
Expects columns: date, item, price."""
df = pd.read_csv(path, parse_dates=["date"])
df.sort_values("date", inplace=True)
return df
@st.cache_data(show_spinner=False)
def get_items(df: pd.DataFrame):
return sorted(df["item"].unique())
# Prophet helper ------------------------------------------------------------
def fit_prophet(df: pd.DataFrame, horizon_end: str):
"""Fit Prophet on df(date, price) and forecast till horizon_end (YYYY-MM-DD)."""
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
# --------------------------------------------------
# 1. DATA LOAD
# --------------------------------------------------
st.title("๐ ํ๋ชฉ๋ณ ๊ฐ๊ฒฉ ์์ธก ๋์๋ณด๋")
raw_df = load_data(DATA_PATH)
st.sidebar.header("๐ ํ๋ชฉ ์ ํ")
selected_item = st.sidebar.selectbox("ํ๋ชฉ", get_items(raw_df))
current_date = date.today()
st.sidebar.markdown(f"**์ค๋ ๋ ์ง:** {current_date}")
item_df = raw_df[raw_df["item"] == selected_item].copy()
if item_df.empty:
st.warning("์ ํํ ํ๋ชฉ์ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.")
st.stop()
# --------------------------------------------------
# 2. MACRO FORECAST 1996โ2030
# --------------------------------------------------
st.subheader(f"๐ ๊ฑฐ์ ๊ฐ๊ฒฉ ์ถ์ด ์์ธก: 1996โ2030 ({selected_item})")
macro_start = "1996-01-01"
macro_end = "2030-12-31"
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 (daily)")
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)
# --------------------------------------------------
# 3. MICRO FORECAST 2024โ2026 (์๋ ๋ฐฐ์น)
# --------------------------------------------------
st.subheader("๐ ๋ฏธ์ ๊ฐ๊ฒฉ ์ถ์ด ์์ธก: 2024โ2026")
micro_start = "2020-01-01" # ๋ ์ต๊ทผ ๋ฐ์ดํฐ๋ง ํ์ต
micro_horizon_end = "2026-12-31"
micro_df = item_df[item_df["date"] >= micro_start]
m_micro, fc_micro = fit_prophet(micro_df, micro_horizon_end)
fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast (daily)")
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)
# --------------------------------------------------
# 4. SEASONALITY COMPONENTS
# --------------------------------------------------
st.subheader("๐ ์์ฆ๋๋ฆฌํฐ ๋ถ์")
with st.expander("์์ฆ๋๋ฆฌํฐ ๊ทธ๋ํ ์ด๊ธฐ/๋ซ๊ธฐ"):
comp_fig = m_micro.plot_components(fc_micro)
st.pyplot(comp_fig)
st.markdown("""
**์ค๋ช
**
* **Yearly seasonality**: ๊ณ์ ์ ํจํด(์: ์ํ๊ธฐยท๋ช
์ ์์)
* **Trend**: ์ฅ๊ธฐ ์ถ์ธ.
* ์ฃผ๊ฐ ์ฑ๋ถ์ ์๋ตํ์ต๋๋ค(๊ฐ๊ฒฉ ๋ฐ์ดํฐ๊ฐ ์ฃผ๊ฐ granularity๊ฐ ์๋๋ฏ๋ก).
""")
# --------------------------------------------------
# 5. 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 ์ธ ๊ด๊ณ๋ price elasticityยท์๊ธ ์ฐ๋์ฑ ๋ถ์์ ํ์ฉํ ์ ์์ต๋๋ค.
""")
# --------------------------------------------------
# 6. EXTRA CHART: ๊ฐ๊ฒฉ ๋ณ๋์ฑ(rolling std)
# --------------------------------------------------
st.subheader("๐ 30์ผ ์ด๋ ํ์คํธ์ฐจ โ ๊ฐ๊ฒฉ ๋ณ๋์ฑ")
vol_df = (item_df.set_index("date")["price"]
.rolling(window=30)
.std().reset_index(name="rolling_std"))
fig_vol = px.area(vol_df, x="date", y="rolling_std", title="30D Rolling Std Dev")
st.plotly_chart(fig_vol, use_container_width=True)
st.markdown("""
- **๋์ ๋ณ๋์ฑ ๊ตฌ๊ฐ**์ ์ฌ๊ณ ยท๊ณ์ฝ ์ ๋ต ์กฐ์ ํ์.
- ํนํ ๋ ์จยท์์ ์ด๋ฒคํธ(๋ช
์ , ํญ์ผ ๋ฑ)์ ๊ฒน์น๋์ง ๊ต์ฐจ ๋ถ์ํด ๋ณด์ธ์.
""")
st.success("โ
์๊ฐํ ์๋ฃ! ํ๋ชฉ์ ๋ฐ๊ฟ๋ณด๋ฉฐ ์ธ์ฌ์ดํธ๋ฅผ ํ์ธํ์ธ์.") |