yokoha commited on
Commit
dc2be38
ยท
verified ยท
1 Parent(s): 072cbd4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -84
app.py CHANGED
@@ -3,26 +3,36 @@ import pandas as pd
3
  import numpy as np
4
  from prophet import Prophet
5
  import plotly.express as px
6
- import plotly.graph_objects as go
7
  import seaborn as sns
8
  import matplotlib.pyplot as plt
9
  from datetime import date
 
10
 
11
- # ---------------------------------------------
12
- # CONFIG --------------------------------------
13
- # ---------------------------------------------
14
- DATA_PATH = "price_data.csv" # CSV: date, item, price
 
15
  MACRO_START, MACRO_END = "1996-01-01", "2030-12-31"
16
  MICRO_START, MICRO_END = "2020-01-01", "2026-12-31"
17
 
18
  st.set_page_config(page_title="ํ’ˆ๋ชฉ๋ณ„ ๊ฐ€๊ฒฉ ์˜ˆ์ธก", page_icon="๐Ÿ“ˆ", layout="wide")
19
 
20
- # ---------------------------------------------
21
- # UTILITIES -----------------------------------
22
- # ---------------------------------------------
23
  @st.cache_data(show_spinner=False)
24
- def load_data(path: str) -> pd.DataFrame:
25
- df = pd.read_csv(path, parse_dates=["date"])
 
 
 
 
 
 
 
 
 
26
  df.sort_values("date", inplace=True)
27
  return df
28
 
@@ -38,108 +48,80 @@ def fit_prophet(df: pd.DataFrame, horizon_end: str):
38
  forecast = m.predict(future)
39
  return m, forecast
40
 
41
- # ---------------------------------------------
42
- # LOAD ----------------------------------------
43
- # ---------------------------------------------
44
- raw_df = load_data(DATA_PATH)
45
- selected_item = st.sidebar.selectbox("๐Ÿ” ํ’ˆ๋ชฉ ์„ ํƒ", get_items(raw_df))
 
 
46
  current_date = date.today()
47
- st.sidebar.write(f"**์˜ค๋Š˜:** {current_date}")
48
 
49
- item_df = raw_df[raw_df["item"] == selected_item].copy()
50
  if item_df.empty:
51
- st.error("๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
52
  st.stop()
53
 
54
- # ---------------------------------------------
55
- # MACRO FORECAST ------------------------------
56
- # ---------------------------------------------
57
- st.header(f"๐Ÿ“ˆ {selected_item} ๊ฐ€๊ฒฉ ์ „๋ง")
58
- macro_df = item_df[item_df["date"] >= MACRO_START]
59
 
 
 
60
  m_macro, fc_macro = fit_prophet(macro_df, MACRO_END)
61
  fig_macro = px.line(fc_macro, x="ds", y="yhat", title="Macro Forecast 1996โ€“2030")
62
  fig_macro.add_scatter(x=macro_df["date"], y=macro_df["price"], mode="lines", name="Actual")
63
  st.plotly_chart(fig_macro, use_container_width=True)
64
 
65
- # --- Metrics โ†“
66
  latest_price = macro_df.iloc[-1]["price"]
67
- macro_last = fc_macro[fc_macro["ds"] == MACRO_END]["yhat"].iloc[0]
68
- macro_diff = macro_last - latest_price
69
- macro_pct = macro_diff / latest_price * 100
70
- st.metric(label="2030 ์˜ˆ์ธก ๊ฐ€๊ฒฉ", value=f"{macro_last:,.0f}", delta=f"{macro_pct:+.1f}% vs ์ตœ๊ทผ")
71
-
72
- # ---------------------------------------------
73
- # MICRO FORECAST ------------------------------
74
- # ---------------------------------------------
75
- st.subheader("๐Ÿ”Ž ๋ฏธ์‹œ ์˜ˆ์ธก 2024โ€“2026")
76
 
 
 
77
  micro_df = item_df[item_df["date"] >= MICRO_START]
78
-
79
  m_micro, fc_micro = fit_prophet(micro_df, MICRO_END)
80
  fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast 2024โ€“2026")
81
  fig_micro.add_scatter(x=micro_df["date"], y=micro_df["price"], mode="lines", name="Actual")
82
  st.plotly_chart(fig_micro, use_container_width=True)
83
 
84
- micro_last = fc_micro[fc_micro["ds"] == MICRO_END]["yhat"].iloc[0]
85
- micro_diff = micro_last - latest_price
86
- micro_pct = micro_diff / latest_price * 100
87
- st.metric(label="2026 ์˜ˆ์ธก ๊ฐ€๊ฒฉ", value=f"{micro_last:,.0f}", delta=f"{micro_pct:+.1f}% vs ์ตœ๊ทผ")
88
 
89
- # ---------------------------------------------
90
- # PATTERN & SEASONALITY -----------------------
91
- # ---------------------------------------------
92
- with st.expander("๐Ÿ“† ์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ ๋ถ„์„ ๋ฐ ํŒจํ„ด ํ•ด์„ค"):
93
  comp_fig = m_micro.plot_components(fc_micro)
94
  st.pyplot(comp_fig)
95
-
96
- # ์›”๋ณ„ seasonality summary
97
  month_season = (fc_micro[["ds", "yearly"]]
98
- .assign(month=lambda d: d["ds"].dt.month)
99
  .groupby("month")["yearly"].mean())
100
- peak_month = int(month_season.idxmax())
101
- trough_month = int(month_season.idxmin())
102
-
103
- st.markdown(f"**ํŒจํ„ด ์š”์•ฝ** \n- __์—ฐ๊ฐ„ ํ”ผํฌ__: {peak_month}์›” \n- __์—ฐ๊ฐ„ ์ €์ __: {trough_month}์›” \n- ํ‰๊ท  ๋ณ€๋™ํญ(์—ฐ): {month_season.max() - month_season.min():.1f} ๋‹จ๊ฐ€")
104
-
105
- # ---------------------------------------------
106
- # CORRELATION HEATMAP -------------------------
107
- # ---------------------------------------------
108
- st.subheader("๐Ÿงฎ ํ’ˆ๋ชฉ ๊ฐ„ ์ƒ๊ด€๊ด€๊ณ„ ํžˆํŠธ๋งต")
109
-
110
- corr_df = (raw_df.assign(month=lambda d: d["date"].dt.to_period("M"))
111
- .groupby(["month", "item"], as_index=False)["price"].mean()
112
- .pivot(index="month", columns="item", values="price"))
113
-
114
- corr = corr_df.corr()
115
-
116
- fig, ax = plt.subplots(figsize=(12, 10))
117
  mask = np.triu(np.ones_like(corr, dtype=bool))
 
118
  sns.heatmap(corr, mask=mask, cmap="RdBu_r", center=0, linewidths=.5, ax=ax)
119
  st.pyplot(fig)
120
 
121
- st.markdown("""
122
- **ํ•ด์„ ๊ฐ€์ด๋“œ**
123
- - **๋นจ๊ฐ„์ƒ‰(+)**: ๋‘ ํ’ˆ๋ชฉ ๊ฐ€๊ฒฉ์ด ๋™์กฐํ™” โ€“ ๊ณต๊ธ‰๋ง/์ˆ˜์š” ์—ฐ๋™ ๊ฐ€๋Šฅ์„ฑ.
124
- - **ํŒŒ๋ž€์ƒ‰(-)**: ๋Œ€์ฒด์žฌ ๊ด€๊ณ„.
125
- - ์ ˆ๋Œ“๊ฐ’ โ‰ฅ 0.7 ์€ ์ •์ฑ…ยท์žฌ๊ณ  ์ „๋žต ์„ค๊ณ„ ์‹œ ์ฃผ์˜ ๊นŠ๊ฒŒ ๋ณผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
126
- """)
127
-
128
- # ---------------------------------------------
129
- # EXTRA CHARTS -------------------------------
130
- # ---------------------------------------------
131
- st.subheader("๐Ÿ“Š ์ถ”๊ฐ€ ์ธ์‚ฌ์ดํŠธ: 30์ผ ์ด๋™ ๋ณ€๋™์„ฑ")
132
 
133
- vol_df = (item_df.set_index("date")["price"].rolling(30).std().reset_index())
134
- fig_vol = px.area(vol_df, x="date", y="price", title="30D Rolling Std Dev")
 
 
135
  st.plotly_chart(fig_vol, use_container_width=True)
136
 
137
- st.markdown("""
138
- - ๋ณ€๋™์„ฑ ๊ธ‰๋“ฑ ๊ตฌ๊ฐ„์€ **๊ณต๊ธ‰ ์ถฉ๊ฒฉยท์ˆ˜์š” ์ด๋ฒคํŠธ** ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.
139
- - ์ตœ๊ทผ ๋ณ€๋™์„ฑ์ด ๋‚ฎ์•„์ง€๋ฉด **๊ฐ€๊ฒฉ ์•ˆ์ฐฉ**์œผ๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
140
- """)
141
-
142
- # ---------------------------------------------
143
- # FOOTER --------------------------------------
144
- # ---------------------------------------------
145
- st.caption("๋ฐ์ดํ„ฐ ์ถœ์ฒ˜: ๋‚ด๋ถ€ ๋†์ˆ˜์‚ฐ๋ฌผ ๊ฐ€๊ฒฉ DB ยท Forecast by Prophet ยท Dashboard built with Streamlit")
 
3
  import numpy as np
4
  from prophet import Prophet
5
  import plotly.express as px
 
6
  import seaborn as sns
7
  import matplotlib.pyplot as plt
8
  from datetime import date
9
+ from pathlib import Path
10
 
11
+ # -------------------------------------------------
12
+ # CONFIG ------------------------------------------
13
+ # -------------------------------------------------
14
+ CSV_PATH = Path("price_data.csv")
15
+ PARQUET_PATH = Path("domae-202503.parquet") # 1996โ€‘1993-03 ๊ฐ€๊ฒฉ ๋ฐ์ดํ„ฐ
16
  MACRO_START, MACRO_END = "1996-01-01", "2030-12-31"
17
  MICRO_START, MICRO_END = "2020-01-01", "2026-12-31"
18
 
19
  st.set_page_config(page_title="ํ’ˆ๋ชฉ๋ณ„ ๊ฐ€๊ฒฉ ์˜ˆ์ธก", page_icon="๐Ÿ“ˆ", layout="wide")
20
 
21
+ # -------------------------------------------------
22
+ # UTILITIES ---------------------------------------
23
+ # -------------------------------------------------
24
  @st.cache_data(show_spinner=False)
25
+ def load_data() -> pd.DataFrame:
26
+ """Load price data from Parquet if available, else CSV."""
27
+ if PARQUET_PATH.exists():
28
+ df = pd.read_parquet(PARQUET_PATH)
29
+ elif CSV_PATH.exists():
30
+ df = pd.read_csv(CSV_PATH)
31
+ else:
32
+ st.error("๋ฐ์ดํ„ฐ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. price_data.csv ๋˜๋Š” domae-202503.parquet" )
33
+ st.stop()
34
+ # ํ‘œ์ค€ํ™”
35
+ df["date"] = pd.to_datetime(df["date"])
36
  df.sort_values("date", inplace=True)
37
  return df
38
 
 
48
  forecast = m.predict(future)
49
  return m, forecast
50
 
51
+ # -------------------------------------------------
52
+ # LOAD DATA ---------------------------------------
53
+ # -------------------------------------------------
54
+ raw_df = load_data()
55
+
56
+ st.sidebar.header("๐Ÿ” ํ’ˆ๋ชฉ ์„ ํƒ")
57
+ selected_item = st.sidebar.selectbox("ํ’ˆ๋ชฉ", get_items(raw_df))
58
  current_date = date.today()
59
+ st.sidebar.caption(f"์˜ค๋Š˜: {current_date}")
60
 
61
+ item_df = raw_df.query("item == @selected_item").copy()
62
  if item_df.empty:
63
+ st.error("์„ ํƒํ•œ ํ’ˆ๋ชฉ ๋ฐ์ดํ„ฐ ์—†์Œ")
64
  st.stop()
65
 
66
+ # -------------------------------------------------
67
+ # PLOTS -------------------------------------------
68
+ # -------------------------------------------------
69
+ st.header(f"๐Ÿ“ˆ {selected_item} ๊ฐ€๊ฒฉ ์˜ˆ์ธก ๋Œ€์‹œ๋ณด๋“œ")
 
70
 
71
+ # Macro forecast 1996โ€“2030
72
+ macro_df = item_df[item_df["date"] >= MACRO_START]
73
  m_macro, fc_macro = fit_prophet(macro_df, MACRO_END)
74
  fig_macro = px.line(fc_macro, x="ds", y="yhat", title="Macro Forecast 1996โ€“2030")
75
  fig_macro.add_scatter(x=macro_df["date"], y=macro_df["price"], mode="lines", name="Actual")
76
  st.plotly_chart(fig_macro, use_container_width=True)
77
 
 
78
  latest_price = macro_df.iloc[-1]["price"]
79
+ macro_pred = fc_macro.loc[fc_macro["ds"] == MACRO_END, "yhat"].iloc[0]
80
+ macro_pct = (macro_pred - latest_price) / latest_price * 100
81
+ st.metric("2030 ์˜ˆ์ธก๊ฐ€", f"{macro_pred:,.0f}", f"{macro_pct:+.1f}%")
 
 
 
 
 
 
82
 
83
+ # Micro forecast 2024โ€“2026
84
+ st.subheader("๐Ÿ”Ž 2024โ€“2026 ๋‹จ๊ธฐ ์˜ˆ์ธก")
85
  micro_df = item_df[item_df["date"] >= MICRO_START]
 
86
  m_micro, fc_micro = fit_prophet(micro_df, MICRO_END)
87
  fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast 2024โ€“2026")
88
  fig_micro.add_scatter(x=micro_df["date"], y=micro_df["price"], mode="lines", name="Actual")
89
  st.plotly_chart(fig_micro, use_container_width=True)
90
 
91
+ micro_pred = fc_micro.loc[fc_micro["ds"] == MICRO_END, "yhat"].iloc[0]
92
+ micro_pct = (micro_pred - latest_price) / latest_price * 100
93
+ st.metric("2026 ์˜ˆ์ธก๊ฐ€", f"{micro_pred:,.0f}", f"{micro_pct:+.1f}%")
 
94
 
95
+ # Seasonality components
96
+ with st.expander("๐Ÿ“† ์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ & ํŒจํ„ด ์„ค๋ช…"):
 
 
97
  comp_fig = m_micro.plot_components(fc_micro)
98
  st.pyplot(comp_fig)
 
 
99
  month_season = (fc_micro[["ds", "yearly"]]
100
+ .assign(month=lambda d: d.ds.dt.month)
101
  .groupby("month")["yearly"].mean())
102
+ st.markdown(
103
+ f"**์—ฐ๊ฐ„ ํ”ผํฌ ์›”:** {int(month_season.idxmax())}์›”\n\n"
104
+ f"**์—ฐ๊ฐ„ ์ €์  ์›”:** {int(month_season.idxmin())}์›”\n\n"
105
+ f"**์—ฐ๊ฐ„ ๋ณ€๋™ํญ:** {month_season.max() - month_season.min():.1f}")
106
+
107
+ # Correlation heatmap
108
+ st.subheader("๐Ÿงฎ ํ’ˆ๋ชฉ ๊ฐ„ ์ƒ๊ด€๊ด€๊ณ„")
109
+ monthly_pivot = (raw_df.assign(month=lambda d: d.date.dt.to_period("M"))
110
+ .groupby(["month", "item"], as_index=False)["price"].mean()
111
+ .pivot(index="month", columns="item", values="price"))
112
+
113
+ corr = monthly_pivot.corr()
 
 
 
 
 
114
  mask = np.triu(np.ones_like(corr, dtype=bool))
115
+ fig, ax = plt.subplots(figsize=(12, 10))
116
  sns.heatmap(corr, mask=mask, cmap="RdBu_r", center=0, linewidths=.5, ax=ax)
117
  st.pyplot(fig)
118
 
119
+ st.info("๋นจ๊ฐ„ ์˜์—ญ: ๊ฐ€๊ฒฉ ๋™์กฐํ™” / ํŒŒ๋ž€ ์˜์—ญ: ๋Œ€์ฒด์žฌ ๊ฐ€๋Šฅ์„ฑ.")
 
 
 
 
 
 
 
 
 
 
120
 
121
+ # Volatility Chart
122
+ st.subheader("๐Ÿ“Š 30์ผ ์ด๋™ ํ‘œ์ค€ํŽธ์ฐจ (๊ฐ€๊ฒฉ ๋ณ€๋™์„ฑ)")
123
+ vol = item_df.set_index("date")["price"].rolling(30).std().dropna().reset_index()
124
+ fig_vol = px.area(vol, x="date", y="price", title="Rolling 30D Std Dev")
125
  st.plotly_chart(fig_vol, use_container_width=True)
126
 
127
+ st.caption("๋ฐ์ดํ„ฐ: domae-202503.parquet ยท Prophet ์˜ˆ์ธก ยท Streamlit ๋Œ€์‹œ๋ณด๋“œ")