vs-suzuki commited on
Commit
d8fb4f4
·
verified ·
1 Parent(s): dc7e013

Upload 6 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # 依存ライブラリのインストール
6
+ COPY requirements.txt .
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # アプリ本体とデータ(parquet)をコピー
10
+ COPY . .
11
+
12
+ # Streamlit設定(HF Spaces向け:ポート8501・外部公開・使用統計オフ)
13
+ ENV STREAMLIT_SERVER_PORT=8501 \
14
+ STREAMLIT_SERVER_ADDRESS=0.0.0.0 \
15
+ STREAMLIT_SERVER_HEADLESS=true \
16
+ STREAMLIT_BROWSER_GATHER_USAGE_STATS=false \
17
+ HOME=/app
18
+
19
+ EXPOSE 8501
20
+
21
+ CMD ["streamlit", "run", "app_fast.py"]
README.md CHANGED
@@ -1,10 +1,34 @@
1
  ---
2
- title: Psearch
3
- emoji: 📊
4
- colorFrom: pink
5
- colorTo: purple
6
  sdk: docker
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: パチンコ統合分析システム
3
+ emoji: 🎰
4
+ colorFrom: red
5
+ colorTo: blue
6
  sdk: docker
7
+ app_port: 8501
8
  pinned: false
9
  ---
10
 
11
+ # パチンコ・スロット統合分析システム(高速版)
12
+
13
+ 事前生成した parquet を読み込んで分析する Streamlit アプリです。
14
+ Hugging Face Spaces では Docker SDK で動かします。
15
+
16
+ ## 必要なファイル
17
+ - `Dockerfile` … 起動設定(Streamlit を 8501 で起動)
18
+ - `app_fast.py` … アプリ本体
19
+ - `requirements.txt` … 依存ライブラリ
20
+ - 以下の parquet(`build_data.py` を手元で実行して生成)
21
+ - `processed_analysis_data.parquet`(メイン分析データ)
22
+ - `tab6_analysis_master.parquet`(新台導入分析用)
23
+
24
+ ## パスワードの設定
25
+ このアプリは簡易パスワードで保護されています。
26
+ Space の **Settings → Variables and secrets** で、以下のシークレットを追加してください。
27
+
28
+ - Name: `app_password`
29
+ - Value: 任意の共有パスワード
30
+
31
+ ## データ更新の手順
32
+ 1. 手元の PC で最新の CSV を `data/` に置き、`python build_data.py` を実行
33
+ 2. 生成された parquet を、この Space にアップロード(差し替え)
34
+ 3. Space が自動で再起動し、最新データが反映されます
app_fast.py ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import os
5
+ import plotly.graph_objects as go
6
+
7
+ st.set_page_config(page_title="パチンコ統合分析システム", layout="wide")
8
+
9
+ # --- 簡易パスワード認証 ---
10
+ def _get_app_password():
11
+ """パスワードの取得元。HF(Docker)では環境変数、ローカルでは secrets.toml。"""
12
+ pw = os.environ.get("app_password")
13
+ if pw:
14
+ return pw
15
+ try:
16
+ return st.secrets["app_password"]
17
+ except Exception:
18
+ return None
19
+
20
+ def check_password():
21
+ """共有パスワードによる簡易認証。正しいパスワードが入るまで本体を表示しない。"""
22
+ def password_entered():
23
+ if st.session_state.get("password", "") == _get_app_password():
24
+ st.session_state["auth_ok"] = True
25
+ del st.session_state["password"] # パスワードを保持しない
26
+ else:
27
+ st.session_state["auth_ok"] = False
28
+
29
+ if st.session_state.get("auth_ok", False):
30
+ return True
31
+
32
+ st.text_input("パスワードを入力してください", type="password",
33
+ on_change=password_entered, key="password")
34
+ if st.session_state.get("auth_ok") is False:
35
+ st.error("パスワードが違います。")
36
+ return False
37
+
38
+ if not check_password():
39
+ st.stop()
40
+ # --- 認証ここまで ---
41
+
42
+ # --- 強制右詰めCSS ---
43
+ st.markdown(
44
+ """
45
+ <style>
46
+ [data-testid="stTableCell"] { text-align: right !important; justify-content: flex-end !important; }
47
+ [data-testid="stHeaderCell"] { text-align: right !important; }
48
+ [data-testid="stTableCell"]:nth-child(1), [data-testid="stTableCell"]:nth-child(2), [data-testid="stTableCell"]:nth-child(3),
49
+ [data-testid="stHeaderCell"]:nth-child(1), [data-testid="stHeaderCell"]:nth-child(2), [data-testid="stHeaderCell"]:nth-child(3) {
50
+ text-align: left !important;
51
+ justify-content: flex-start !important;
52
+ }
53
+ </style>
54
+ """,
55
+ unsafe_allow_html=True
56
+ )
57
+
58
+ @st.cache_data
59
+ def load_final_data():
60
+ if not os.path.exists("processed_analysis_data.parquet"):
61
+ return None
62
+ df = pd.read_parquet("processed_analysis_data.parquet")
63
+
64
+ # --- 列名の標準化 ---
65
+ rename_map = {
66
+ "パチンコタイプ名": "タイプ名_P",
67
+ "パチンコタイプID": "ソートID_P",
68
+ "スロットタイプ名": "タイプ名_S",
69
+ "スロットタイプID": "ソートID_S"
70
+ }
71
+ df = df.rename(columns=rename_map)
72
+
73
+ # 型の統一
74
+ if "Pcode" in df.columns:
75
+ df["Pcode"] = df["Pcode"].astype(str)
76
+
77
+ for c in ["ソートID_P", "ソートID_S"]:
78
+ if c in df.columns:
79
+ df[c] = pd.to_numeric(df[c], errors='coerce').fillna(999)
80
+
81
+ for col in ["店舗ID", "グループID", "都道府県ID"]:
82
+ if col in df.columns:
83
+ df[col] = df[col].astype(str)
84
+
85
+ if "販売年月日" in df.columns:
86
+ df["販売年月日"] = pd.to_datetime(df["販売年月日"].astype(str), errors='coerce').dt.strftime('%Y/%m/%d')
87
+ df["販売年月日"] = df["販売年月日"].fillna("-")
88
+
89
+ # 資産価値計算用の数値化
90
+ if "中古単価" in df.columns:
91
+ df["中古単価_num"] = pd.to_numeric(df["中古単価"], errors='coerce').fillna(0)
92
+ if "設置台数" in df.columns:
93
+ df["設置台数"] = pd.to_numeric(df["設置台数"], errors='coerce').fillna(0)
94
+
95
+ return df
96
+
97
+ @st.cache_data
98
+ def load_shindai_data():
99
+ target = "tab6_analysis_master.parquet"
100
+ if os.path.exists(target):
101
+ try:
102
+ df = pd.read_parquet(target)
103
+ df['導入月'] = pd.to_datetime(df['導入月'])
104
+ return df
105
+ except Exception as e:
106
+ st.error(f"データ読み込みエラー: {e}")
107
+ return None
108
+
109
+ # --- メイン処理 ---
110
+ df = load_final_data()
111
+
112
+ if df is None:
113
+ st.error("分析用データが見つかりません。build_data.pyを実行してください。")
114
+ else:
115
+ st.title("🎰 パチンコ・スロット統合分析 (高速版)")
116
+
117
+ # --- サイドバー ---
118
+ st.sidebar.title("分析軸選択")
119
+ axis_options = [c for c in ["店舗ID", "グループID", "グループ名", "都道府県ID"] if c in df.columns]
120
+ axis = st.sidebar.selectbox("集計軸", axis_options)
121
+
122
+ if axis == "店舗ID" and "店舗名" in df.columns:
123
+ id_name_map = df.drop_duplicates("店舗ID").set_index("店舗ID")["店舗名"].to_dict()
124
+ options = ["すべて"] + [f"{tid}: {id_name_map.get(tid, '')}" for tid in sorted(df["店舗ID"].unique())]
125
+ selected_option = st.sidebar.selectbox("表示対象の店舗を選択", options)
126
+ selected_value = selected_option.split(":")[0] if selected_option != "すべて" else "すべて"
127
+ else:
128
+ selected_value = st.sidebar.selectbox(f"表示対象の{axis}を選択", ["すべて"] + sorted(df[axis].unique().tolist()))
129
+
130
+ menu = st.sidebar.radio("メニュー", [
131
+ "貸玉別集計",
132
+ "競合比較分析",
133
+ "資産価値分析",
134
+ "設置機種詳細",
135
+ "タイプ別集計",
136
+ "新台導入分析"
137
+ ])
138
+
139
+ # --- 1. 貸玉別集計 ---
140
+ if menu == "貸玉別集計":
141
+ st.subheader(f"📊 {axis}別 設置台数")
142
+ temp_df = df if selected_value == "すべて" else df[df[axis] == selected_value]
143
+ if not temp_df.empty:
144
+ st.dataframe(temp_df.groupby([axis, '貸玉区分']).size().unstack(fill_value=0), use_container_width=True)
145
+
146
+ # --- 2. 競合比較分析---
147
+ elif menu == "競合比較分析":
148
+ st.subheader("⚔️ 商圏内 詳細構成分析 & レポート")
149
+
150
+ if selected_value == "すべて" or axis != "店舗ID":
151
+ st.info("サイドバーで特定の「店舗ID」を選択すると、詳細な商圏比較を表示します。")
152
+ else:
153
+ target_shop_id = selected_value
154
+
155
+ # --- A. 基礎データの作成 (店舗単位サマリー) ---
156
+ # 1. 全国集計
157
+ national_shop_sum = df.groupby("店舗ID").agg({"設置台数":"sum"})
158
+ for cat in ["通常P", "低貸P", "通常S", "低貸S"]:
159
+ national_shop_sum[cat] = df[df["貸玉区分"]==cat].groupby("店舗ID")["設置台数"].sum()
160
+ national_shop_sum = national_shop_sum.fillna(0)
161
+
162
+ # 2. 全国・都道府県平均比率の算出
163
+ def calc_avg_ratios(source_df):
164
+ t = source_df["設置台数"].sum()
165
+ if t == 0: return [0,0,0,0]
166
+ return [
167
+ (source_df["通常P"].sum() / t * 100),
168
+ (source_df["低貸P"].sum() / t * 100),
169
+ (source_df["通常S"].sum() / t * 100),
170
+ (source_df["低貸S"].sum() / t * 100)
171
+ ]
172
+
173
+ avg_national = calc_avg_ratios(national_shop_sum)
174
+
175
+ p_id = df[df["店舗ID"] == target_shop_id]["都道府県ID"].iloc[0]
176
+ pref_shops = national_shop_sum.loc[national_shop_sum.index.isin(df[df["都道府県ID"]==p_id]["店舗ID"].unique())]
177
+ avg_pref = calc_avg_ratios(pref_shops)
178
+
179
+ # 3. エリア(商圏)サマリーの作成
180
+ # ※ここでは例として「同じ都道府県内」をエリアとしていますが、距離データがある場合はここでフィルタリング
181
+ area_summary = pref_shops.copy()
182
+ area_summary["店舗名"] = df.drop_duplicates("店舗ID").set_index("店舗ID")["店舗名"]
183
+ avg_area = calc_avg_ratios(area_summary)
184
+
185
+ # --- B. グラフ用データの作成 (平均データの差し込み) ---
186
+ target_shop_row = area_summary.loc[target_shop_id]
187
+ target_ratios = [
188
+ (target_shop_row["通常P"] / target_shop_row["設置台数"] * 100),
189
+ (target_shop_row["低貸P"] / target_shop_row["設置台数"] * 100),
190
+ (target_shop_row["通常S"] / target_shop_row["設置台数"] * 100),
191
+ (target_shop_row["低貸S"] / target_shop_row["設置台数"] * 100)
192
+ ]
193
+
194
+ # 比較用データフレーム
195
+ compare_ratios_df = pd.DataFrame([
196
+ {"区分": "選択店", "P通常": target_ratios[0], "P低貸": target_ratios[1], "S通常": target_ratios[2], "S低貸": target_ratios[3]},
197
+ {"区分": "エリア平均", "P通常": avg_area[0], "P低貸": avg_area[1], "S通常": avg_area[2], "S低貸": avg_area[3]},
198
+ {"区分": "都道府県平均", "P通常": avg_pref[0], "P低貸": avg_pref[1], "S通常": avg_pref[2], "S低貸": avg_pref[3]},
199
+ {"区分": "全国平均", "P通常": avg_national[0], "P低貸": avg_national[1], "S通常": avg_national[2], "S低貸": avg_national[3]},
200
+ ])
201
+
202
+ # --- C. 表示処理 ---
203
+ st.write("#### ⚖️ 構成比率の比較(全体100%)")
204
+ st.bar_chart(compare_ratios_df.set_index("区分"), use_container_width=True)
205
+
206
+ # --- 表示セクション ---
207
+ col1, col2 = st.columns([1, 1])
208
+
209
+ with col1:
210
+ st.markdown(f"#### 🏟️ {target_shop_name} の部門構成")
211
+ shop_row = area_summary[area_summary["店舗ID"]==target_shop_id].iloc[0]
212
+
213
+ fig = go.Figure()
214
+ categories = ['通常P', '低貸P', '通常S', '低貸S']
215
+ fig.add_trace(go.Bar(
216
+ y=categories,
217
+ x=[shop_row['通常P'], shop_row['低貸P'], shop_row['通常S'], shop_row['低貸S']],
218
+ orientation='h',
219
+ marker_color=['#d62728', '#ff9896', '#1f77b4', '#aec7e8']
220
+ ))
221
+ fig.update_layout(height=300, margin=dict(l=20, r=20, t=20, b=20))
222
+ st.plotly_chart(fig, use_container_width=True)
223
+
224
+ with col2:
225
+ st.markdown("#### 📝 戦略アセスメント")
226
+ p_rank = area_summary["通常P"].rank(ascending=False).iloc[0]
227
+ total_shops = len(area_summary)
228
+ st.success(f"**商圏内ポジション:** 通常P設置台数 第{int(p_rank)}位 / {total_shops}店舗中")
229
+
230
+ # 自動コメント生成
231
+ if shop_row["通常S"] > shop_row["通常P"] * 0.8:
232
+ comment = "スロット比率が高く、若年層や高稼働層をターゲットとした構成です。"
233
+ else:
234
+ comment = "パチンコ主体の安定型構成です。地域密着型の営業スタイルが推測されます。"
235
+ st.write(comment)
236
+
237
+ st.markdown("---")
238
+ st.markdown("#### 🛰️ 周辺店舗との比較リスト")
239
+ st.dataframe(area_summary.sort_values("設置台数", ascending=False), use_container_width=True, hide_index=True)
240
+
241
+ # --- 3. 資産価値分析 ---
242
+ elif menu == "資産価値分析":
243
+ st.subheader("🏢 店舗別・1台あたり資産価値比較")
244
+ # (既存の資産価値ロジックを維持)
245
+ df_val = df[["店舗ID", "店舗名", "都道府県ID", "グループID", "貸玉区分", "中古単価_num", "設置台数"]].copy()
246
+ df_val["資産総額"] = df_val["中古単価_num"] * df_val["設置台数"]
247
+ cat_map = {"全体": ["通常P", "低貸P", "通常S", "低貸S"], "通常パチンコ": ["通常P"], "通常スロット": ["通常S"]}
248
+
249
+ def calc_unit_fast(target_df, label):
250
+ res = {"区分": label}
251
+ for name, cats in cat_map.items():
252
+ tmp = target_df[target_df["貸玉区分"].isin(cats)]
253
+ t_a, t_q = tmp["資産総額"].sum(), tmp["設置台数"].sum()
254
+ res[name] = t_a / t_q if t_q > 0 else 0
255
+ return res
256
+
257
+ if selected_value != "すべて":
258
+ selected_df = df_val[df_val[axis] == selected_value]
259
+ comparison_rows = [calc_unit_fast(df_val, "🌏 全国平均")]
260
+ if "都道府県ID" in selected_df.columns and not selected_df.empty:
261
+ p_id = selected_df["都道府県ID"].iloc[0]
262
+ comparison_rows.append(calc_unit_fast(df_val[df_val["都道府県ID"] == p_id], "🗾 都道府県平均"))
263
+ comparison_rows.append(calc_unit_fast(selected_df, f"★選択中: {selected_value}"))
264
+ display_compare_df = pd.DataFrame(comparison_rows)
265
+ else:
266
+ agg_list = []
267
+ for name, cats in cat_map.items():
268
+ f_df = df_val[df_val["貸玉区分"].isin(cats)]
269
+ if f_df.empty: continue
270
+ agg = f_df.groupby(axis).agg({"資産総額":"sum", "設置台数":"sum"})
271
+ agg[name] = agg["資産総額"] / agg["設置台数"]
272
+ agg_list.append(agg[[name]])
273
+ display_compare_df = pd.concat(agg_list, axis=1).fillna(0).reset_index().rename(columns={axis: "区分"})
274
+
275
+ st.dataframe(display_compare_df.style.format(precision=0, thousands=","), use_container_width=True, hide_index=True)
276
+
277
+ # --- 4. 設置機種詳細 ---
278
+ elif menu == "設置機種詳細":
279
+ st.subheader("🎰 設置機種・市場比較レポート")
280
+ # (既存の設置機種詳細ロジックを維持)
281
+ neighbor_ids = []
282
+ if selected_value != "すべて" and axis == "店舗ID":
283
+ p_id = df[df["店舗ID"] == selected_value]["都道府県ID"].iloc[0]
284
+ neighbor_ids = df[df["都道府県ID"] == p_id]["店舗ID"].unique().tolist()
285
+ if selected_value in neighbor_ids:
286
+ neighbor_ids.remove(selected_value)
287
+ neighbor_ids = [selected_value] + neighbor_ids
288
+
289
+ categories = {"🔴 通常P": "通常P", "🟢 低貸P": "低貸P", "🔵 通常S": "通常S", "🟡 低貸S": "低貸S"}
290
+ sub_tabs = st.tabs(list(categories.keys()))
291
+
292
+ for i, (label, internal_name) in enumerate(categories.items()):
293
+ with sub_tabs[i]:
294
+ tab_df = df[df["貸玉区分"] == internal_name].copy()
295
+ if selected_value == "すべて":
296
+ display_df = tab_df
297
+ elif neighbor_ids:
298
+ display_df = tab_df[tab_df["店舗ID"].isin(neighbor_ids)]
299
+ else:
300
+ display_df = tab_df[tab_df[axis] == selected_value]
301
+
302
+ if display_df.empty:
303
+ st.info(f"{label} のデータがありません。")
304
+ continue
305
+
306
+ agg_cols = {"機種名": "first", "メーカー名": "first", "販売年月日": "first", "中古単価": "max", "全国台数": "max", "都道府県台数": "max"}
307
+ extra = ["確率区分", "スマパチ", "デカヘソ", "機種タイプ区分", "スマスロ", "30パイ", "BT機"]
308
+ for c in extra:
309
+ if c in display_df.columns: agg_cols[c] = "first"
310
+
311
+ summary = display_df.groupby("Pcode").agg(agg_cols)
312
+ col_axis = "店舗名" if "店舗名" in display_df.columns else axis
313
+ pivot = display_df.pivot_table(index="Pcode", columns=col_axis, values="設置台数", aggfunc="sum", fill_value=0)
314
+
315
+ if neighbor_ids:
316
+ id_to_name = display_df.drop_duplicates("店舗ID").set_index("店舗ID")["店舗名"].to_dict()
317
+ target_order = [id_to_name[tid] for tid in neighbor_ids if tid in id_to_name and id_to_name[tid] in pivot.columns]
318
+ pivot = pivot.reindex(columns=target_order, fill_value=0)
319
+
320
+ final_display = pd.concat([summary, pivot], axis=1)
321
+ shop_names = pivot.columns.tolist()
322
+ final_display["商圏合計"] = final_display[shop_names].sum(axis=1)
323
+
324
+ sum_row = pd.Series(index=final_display.columns, dtype=object).fillna("")
325
+ sum_row["機種名"] = "■店舗合計台数"
326
+ for s_col in shop_names: sum_row[s_col] = final_display[s_col].sum()
327
+ sum_row["商圏合計"] = final_display["商圏合計"].sum()
328
+
329
+ output_df = pd.concat([pd.DataFrame([sum_row]), final_display.sort_values("商圏合計", ascending=False)], ignore_index=True)
330
+ st.dataframe(output_df, use_container_width=True, height=650, hide_index=True)
331
+
332
+ # --- 5. タイプ別集計 ---
333
+ elif menu == "タイプ別集計":
334
+ st.subheader("📊 タイプ別設置構成レポート")
335
+ categories = {"🔴 通常P": "通常P", "🟢 低貸P": "低貸P", "🔵 通常S": "通常S", "🟡 低貸S": "低貸S"}
336
+ sub_tabs = st.tabs(list(categories.keys()))
337
+ for i, (label, internal_name) in enumerate(categories.items()):
338
+ with sub_tabs[i]:
339
+ tab_df = df[df["貸玉区分"] == internal_name].copy()
340
+ if tab_df.empty: continue
341
+ suffix = "P" if "P" in internal_name else "S"
342
+ t_col = f"タイプ名_{suffix}"
343
+ actual_t_col = t_col if t_col in tab_df.columns else ("確率区分" if suffix == "P" else "機種タイプ区分")
344
+
345
+ display_label = "店舗名" if "店舗名" in tab_df.columns else axis
346
+ pivot_count = tab_df.pivot_table(index=display_label, columns=actual_t_col, values="設置台数", aggfunc="sum", fill_value=0)
347
+ pivot_count["■合計"] = pivot_count.sum(axis=1)
348
+ st.dataframe(pivot_count.sort_values("■合計", ascending=False), use_container_width=True)
349
+
350
+ # --- 6. 新台導入分析 ---
351
+ elif menu == "新台導入分析":
352
+ st.subheader("📅 新台導入推移")
353
+ shindai_df = load_shindai_data()
354
+ if shindai_df is not None:
355
+ plot_df = shindai_df if selected_value == "すべて" else shindai_df[shindai_df[axis] == selected_value]
356
+ if plot_df.empty:
357
+ st.warning("データがありません。")
358
+ else:
359
+ plot_df["月"] = plot_df["導入月"].dt.strftime('%Y/%m')
360
+ selected_kind = st.selectbox("種別", ["店舗全体", "パチンコ", "スロット"])
361
+ view_df = plot_df[plot_df["種別"] == selected_kind]
362
+
363
+ if not view_df.empty:
364
+ pivot_table = view_df.pivot_table(index=["店舗ID", "店舗名"], columns="月", values="導入台数", aggfunc="sum", fill_value=0)
365
+ sorted_cols = sorted(pivot_table.columns, reverse=True)
366
+ pivot_table = pivot_table.reindex(columns=sorted_cols)
367
+ display_pivot = pivot_table.reset_index()
368
+ st.dataframe(
369
+ display_pivot,
370
+ use_container_width=True,
371
+ hide_index=True,
372
+ column_config={col: st.column_config.NumberColumn(format="%d") for col in sorted_cols}
373
+ )
374
+ else:
375
+ st.error("新台分析用ファイルが見つかりません。")
processed_analysis_data.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:853fde6e2dacf6a02e802340a9ab7400f252897764b91817e4c385089ff719ad
3
+ size 22704892
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ streamlit==1.58.0
2
+ pandas>=2.0
3
+ numpy>=1.24
4
+ plotly>=5.18
5
+ pyarrow>=14.0
tab6_analysis_master.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0bc33ebb94c6e02f69cbb6c28b2221a3a88a7ecd05f6d1a4135d0a8d22a19e2d
3
+ size 10552242