Dusit-P commited on
Commit
bf786d6
·
verified ·
1 Parent(s): f1f96f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -141
app.py CHANGED
@@ -1,5 +1,5 @@
1
- # app.py — Thai Sentiment Analysis (เรียบง่าย + Shop Analysis)
2
- import os, json, importlib.util, traceback, re, math, tempfile, datetime
3
  import gradio as gr
4
  import torch, pandas as pd
5
  import torch.nn.functional as F
@@ -69,51 +69,48 @@ def _is_substantive_text(s, min_chars=2):
69
  return True
70
 
71
  def _format_pct(x): return f"{x*100:.2f}%"
72
- def _to_datetime_safe(s): return pd.to_datetime(s, errors="coerce", infer_datetime_format=True, utc=False)
73
 
74
- LIKELY_TEXT_COLS = ["text","review","message","comment","content","ข้อความ","รีวิว"]
75
- LIKELY_DATE_COLS = ["date","created_at","time","timestamp","datetime","วันที่","เวลา"]
76
- LIKELY_SHOP_COLS = ["shop","store","branch","ร้าน","สาขา","ชื่อร้าน"]
 
 
 
 
77
 
78
  def detect_columns(df):
79
- """ตรวจหา text, date, shop columns อัตโนมัติ"""
80
  cols = list(df.columns)
81
  low = {c.lower(): c for c in cols}
82
 
83
  # Text column
84
  text_col = None
85
  for k in LIKELY_TEXT_COLS:
86
- if k in low: text_col = low[k]; break
 
 
87
  if text_col is None:
88
  cand = [c for c in cols if df[c].dtype == object]
89
  text_col = cand[0] if cand else cols[0]
90
 
91
- # Date candidates
92
- date_candidates = []
93
  for c in cols:
94
- if c.lower() in LIKELY_DATE_COLS:
95
- date_candidates.append(c)
96
  continue
97
- sample = df[c].head(50)
98
- if _to_datetime_safe(sample).notna().sum() >= max(3, int(len(sample)*0.2)):
99
- date_candidates.append(c)
100
- date_candidates = list(dict.fromkeys(date_candidates))
101
- date_col = date_candidates[0] if len(date_candidates) > 0 else None
102
-
103
- # Shop candidates
104
- shop_candidates = []
105
- for c in cols:
106
- if c.lower() in LIKELY_SHOP_COLS:
107
- shop_candidates.append(c)
108
  continue
 
109
  if df[c].dtype == object:
110
  unique_ratio = df[c].nunique() / len(df)
111
- if 0.01 <= unique_ratio <= 0.5:
112
- shop_candidates.append(c)
113
- shop_candidates = list(dict.fromkeys(shop_candidates))
114
- shop_col = shop_candidates[0] if len(shop_candidates) > 0 else None
115
 
116
- return text_col, date_candidates, date_col, shop_candidates, shop_col
 
 
 
117
 
118
  # ================= Core Predict =================
119
  def _predict_batch(texts, model_name, batch_size=32):
@@ -170,58 +167,53 @@ def make_summary_chart(df):
170
 
171
  return fig, info
172
 
173
- def make_shop_chart(df, shop_col, date_col=None, days_filter=None):
174
- """กราฟแสดงรีวิวแต่ละร้าน - เรียบง่าย"""
175
-
176
- # กรองตามวันที่ถ้าต้องการ
177
- if date_col and days_filter:
178
- cutoff = pd.Timestamp.now() - pd.Timedelta(days=days_filter)
179
- df = df[df[date_col] >= cutoff]
180
 
181
- # สรุปแต่ละร้าน
182
- shop_data = []
183
- for shop in df[shop_col].unique():
184
- if pd.isna(shop):
185
  continue
186
- shop_df = df[df[shop_col] == shop]
187
- neg = len(shop_df[shop_df["label"]=="negative"])
188
- pos = len(shop_df[shop_df["label"]=="positive"])
189
- total = len(shop_df)
190
 
191
- shop_data.append({
192
- "shop": str(shop),
193
  "negative": neg,
194
  "positive": pos,
195
  "total": total,
196
  "pos_pct": pos/total*100 if total > 0 else 0
197
  })
198
 
199
- shop_df = pd.DataFrame(shop_data).sort_values("total", ascending=False)
200
 
201
  # กราฟแท่ง Stacked
202
  fig = go.Figure()
203
  fig.add_bar(
204
  name="😞 เชิงลบ",
205
- x=shop_df["shop"],
206
- y=shop_df["negative"],
207
- marker_color=NEG_COLOR
 
 
208
  )
209
  fig.add_bar(
210
  name="😊 เชิงบวก",
211
- x=shop_df["shop"],
212
- y=shop_df["positive"],
213
- marker_color=POS_COLOR
 
 
214
  )
215
 
216
- title = "🏪 รีวิวแต่ละร้าน/สาขา"
217
- if days_filter:
218
- title += f" ({days_filter} วันล่าสุด)"
219
-
220
  fig.update_layout(
221
- title=title,
222
  barmode='stack',
223
  template=TEMPLATE,
224
- xaxis_title="ร้าน/สาขา",
225
  yaxis_title="จำนวนรีวิว",
226
  height=450,
227
  showlegend=True
@@ -229,11 +221,11 @@ def make_shop_chart(df, shop_col, date_col=None, days_filter=None):
229
 
230
  # ตารางสรุป
231
  summary_df = pd.DataFrame({
232
- "ร้าน/สาขา": shop_df["shop"],
233
- "รีวิวทั้งหมด": shop_df["total"],
234
- "😞 เชิงลบ": shop_df["negative"],
235
- "😊 เชิงบวก": shop_df["positive"],
236
- "% เชิงบวก": shop_df["pos_pct"].apply(lambda x: f"{x:.1f}%")
237
  })
238
 
239
  return fig, summary_df
@@ -264,47 +256,46 @@ def on_file_change(file_obj):
264
  if file_obj is None:
265
  return (gr.update(choices=[], value=None),
266
  gr.update(choices=[], value=None),
267
- gr.update(choices=[], value=None),
268
  gr.update(visible=False),
269
  "⚠️ กรุณาอัปโหลดไฟล์ CSV")
270
 
271
  try:
272
  df = pd.read_csv(file_obj.name)
273
- text_col, date_candidates, date_col, shop_candidates, shop_col = detect_columns(df)
274
-
275
- has_shop = shop_col is not None
276
 
277
- note = f"✅ **ตรวจพบคอลัมน์**\n"
278
- note += f"- 📝 ข้อความ: **{text_col}**\n"
279
 
280
- if date_col:
281
- note += f"- 📅 วันที่: **{date_col}**\n"
282
 
283
- if has_shop:
284
- note += f"- 🏪 ร้าน/สาขา: **{shop_col}** ({df[shop_col].nunique()} ร้าน)\n"
285
  else:
286
- note += f"- 🏪 ร้าน/สาขา: _ไม่พบ_\n"
287
 
288
- note += f"\n_หากไม่ถูกต้อง เลือกใหม่ได้ด้านบน_"
289
 
290
  return (gr.update(choices=list(df.columns), value=text_col),
291
- gr.update(choices=date_candidates if date_candidates else ["ไม่มี"], value=date_col),
292
- gr.update(choices=shop_candidates if shop_candidates else ["ไม่มี"], value=shop_col),
293
- gr.update(visible=has_shop),
 
294
  note)
295
 
296
  except Exception as e:
297
  return (gr.update(choices=[], value=None),
298
  gr.update(choices=[], value=None),
299
- gr.update(choices=[], value=None),
300
  gr.update(visible=False),
301
  f"❌ ไม่สามารถอ่านไฟล์ได้: {str(e)}")
302
 
303
- def predict_csv(file_obj, model_choice, text_col, date_col, shop_col, days_filter):
304
  """วิเคราะห์ CSV"""
305
  if file_obj is None:
306
  return (pd.DataFrame(), go.Figure(),
307
- gr.update(visible=False), pd.DataFrame(),
 
308
  "❌ กรุณาอัปโหลดไฟล์", None)
309
 
310
  try:
@@ -314,7 +305,7 @@ def predict_csv(file_obj, model_choice, text_col, date_col, shop_col, days_filte
314
 
315
  # ตรวจสอบ text column
316
  if text_col not in cols:
317
- text_col, _, _, _, _ = detect_columns(df_raw)
318
 
319
  # ดึงข้อความ
320
  texts = [_norm_text(v) for v in df_raw[text_col].tolist()]
@@ -323,7 +314,8 @@ def predict_csv(file_obj, model_choice, text_col, date_col, shop_col, days_filte
323
 
324
  if not texts_clean:
325
  return (pd.DataFrame(), go.Figure(),
326
- gr.update(visible=False), pd.DataFrame(),
 
327
  "❌ ไม่พบข้อความที่วิเคราะห์ได้", None)
328
 
329
  # ทำนาย
@@ -334,41 +326,26 @@ def predict_csv(file_obj, model_choice, text_col, date_col, shop_col, days_filte
334
  fig_main, info = make_summary_chart(df_out)
335
 
336
  if skipped > 0:
337
- info += f"\n\n⚠️ ข้ามแถวว่าง: {skipped} แถว"
338
 
339
- # วิเคราะห์ Shop (ถ้ามี)
340
- fig_shop = go.Figure()
341
- shop_summary = pd.DataFrame()
342
- show_shop = False
343
 
344
- if shop_col and shop_col in cols and shop_col != "ไม่มี":
345
  # เตรียมข้อมูล
346
- df_shop = df_out.copy()
347
- df_shop[shop_col] = df_raw[shop_col].iloc[:len(df_out)]
348
 
349
- # เพิ่ม date ถ้ามี
350
- if date_col and date_col in cols and date_col != "ไม่มี":
351
- dts = _to_datetime_safe(df_raw[date_col])
352
- df_shop[date_col] = dts.iloc[:len(df_out)]
353
- df_shop = df_shop.dropna(subset=[date_col])
354
-
355
- # แปลง days_filter
356
- days = None
357
- if days_filter == "7 วันล่าสุด":
358
- days = 7
359
- elif days_filter == "15 วันล่าสุด":
360
- days = 15
361
- elif days_filter == "30 วันล่าสุด":
362
- days = 30
363
-
364
- fig_shop, shop_summary = make_shop_chart(df_shop, shop_col, date_col, days)
365
-
366
- if days:
367
- info += f"\n\n📅 แสดงข้อมูล: {days_filter}"
368
- else:
369
- fig_shop, shop_summary = make_shop_chart(df_shop, shop_col)
370
 
371
- show_shop = True
 
 
 
 
372
 
373
  # บันทึกไฟล์
374
  fd, path = tempfile.mkstemp(suffix=".csv")
@@ -376,13 +353,14 @@ def predict_csv(file_obj, model_choice, text_col, date_col, shop_col, days_filte
376
  df_out.to_csv(path, index=False, encoding="utf-8-sig")
377
 
378
  return (df_out, fig_main,
379
- gr.update(visible=show_shop, value=fig_shop),
380
- shop_summary,
381
  info, path)
382
 
383
  except Exception as e:
384
  return (pd.DataFrame(), go.Figure(),
385
- gr.update(visible=False), pd.DataFrame(),
 
386
  f"❌ เกิดข้อผิดพลาด:\n{traceback.format_exc()}", None)
387
 
388
  # ================= Gradio UI =================
@@ -399,14 +377,23 @@ with gr.Blocks(title="Thai Sentiment Analysis", theme=gr.themes.Soft()) as demo:
399
  info="WCB = เร็ว | WCB_BiLSTM = แม่นยำสูงสุด (แนะนำ)"
400
  )
401
 
402
- # =================== Tab 1 ===================
403
  with gr.Tab("📝 วิเคราะห์ข้อความ"):
404
- gr.Markdown("**วิธีใช้:** ป้อนรีวิวหลายรายการ (แต่ละบรรทัด = 1 รีวิว)")
 
 
 
 
 
 
 
 
 
405
 
406
  text_input = gr.Textbox(
407
  lines=8,
408
  label="📄 ข้อความรีวิว",
409
- placeholder="ป้อนรีวิว แต่ละบรรทัด = 1 รีวิว\n\nตัวอย่าง:\nอาหารอร่อยมาก บริการดี\nของแพง รสชาติธรรมดา"
410
  )
411
 
412
  predict_btn_1 = gr.Button("🚀 เริ่มวิเคราะห์", variant="primary", size="lg")
@@ -425,53 +412,64 @@ with gr.Blocks(title="Thai Sentiment Analysis", theme=gr.themes.Soft()) as demo:
425
  [result_df_1, result_chart_1, result_info_1]
426
  )
427
 
428
- # =================== Tab 2 ===================
429
- with gr.Tab("📤 อัปโหลด CSV"):
430
- gr.Markdown("**วิธีใช้:** อัปโหลดไฟล์ CSV ที่มีคอลัมน์รีวิว (และอาจมีวันที่/ร้านด้วย)")
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
  file_input = gr.File(file_types=[".csv"], label="📁 อัปโหลดไฟล์ CSV")
433
 
434
  detect_note = gr.Markdown("⬆️ อัปโหลดไฟล์เพื่อเริ่มต้น")
435
 
436
  with gr.Row():
437
- text_col_dd = gr.Dropdown(label="📝 คอลัมน์ข้อความรีวิว")
438
- date_col_dd = gr.Dropdown(label="📅 คอลัมน์วันที่ (ถ้ามี)")
439
- shop_col_dd = gr.Dropdown(label="🏪 คอลัมน์ร้าน/สาขา (ถ้ามี)")
440
-
441
- days_filter = gr.Radio(
442
- choices=["ทั้งหมด", "7 วันล่าสุด", "15 วันล่าสุด", "30 วันล่าสุด"],
443
- value="ทั้งหมด",
444
- label="📆 ช่วงเวลา",
445
- info="ใช้กรองข้อมูลเฉพาะกราฟร้าน (ถ้ามีวันที่)",
446
- visible=False
447
- )
448
 
449
  predict_btn_2 = gr.Button("🚀 เริ่มวิเคราะห์", variant="primary", size="lg")
450
 
451
- result_df_2 = gr.Dataframe(label="📋 ผลการวิเคราะห์ทั้งหมด")
 
 
452
 
453
  with gr.Row():
454
  with gr.Column(scale=1):
455
- result_chart_2 = gr.Plot(label="📊 กราฟสรุปภาพรวม")
456
  with gr.Column(scale=1):
457
  result_info_2 = gr.Markdown()
458
 
459
- result_shop = gr.Plot(label="🏪 รีวิวแต่ละร้าน/สาขา", visible=False)
460
- shop_summary = gr.Dataframe(label="📊 สรุปแต่ละร้าน", visible=False)
461
 
462
- download_file = gr.File(label="💾 ดาวน์โหลดผลลัพธ์")
463
 
464
  # Events
465
  file_input.change(
466
  on_file_change,
467
  [file_input],
468
- [text_col_dd, date_col_dd, shop_col_dd, days_filter, detect_note]
469
  )
470
 
471
  predict_btn_2.click(
472
  predict_csv,
473
- [file_input, model_radio, text_col_dd, date_col_dd, shop_col_dd, days_filter],
474
- [result_df_2, result_chart_2, result_shop, shop_summary, result_info_2, download_file]
475
  )
476
 
477
  gr.Markdown("""
 
1
+ # app.py — Thai Sentiment Analysis (ยืดหยุ่น + ง่าย)
2
+ import os, json, importlib.util, traceback, re, math, tempfile
3
  import gradio as gr
4
  import torch, pandas as pd
5
  import torch.nn.functional as F
 
69
  return True
70
 
71
  def _format_pct(x): return f"{x*100:.2f}%"
 
72
 
73
+ # คำที่น่าจะเป็นคอลัมน์ข้อความ
74
+ LIKELY_TEXT_COLS = ["text","review","message","comment","content","sentence","body",
75
+ "ข้อความ","รีวิว","ความคิดเห็น"]
76
+
77
+ # คำที่น่าจะเป็นคอลัมน์หมวดหมู่ (ร้าน/product/category)
78
+ LIKELY_GROUP_COLS = ["shop","store","branch","category","product","brand","type","group",
79
+ "ร้าน","สาขา","ชื่อร้าน","หมวดหมู่","ประเภท","แบรนด์"]
80
 
81
  def detect_columns(df):
82
+ """ตรวจหา text และ group columns อัตโนมัติ"""
83
  cols = list(df.columns)
84
  low = {c.lower(): c for c in cols}
85
 
86
  # Text column
87
  text_col = None
88
  for k in LIKELY_TEXT_COLS:
89
+ if k in low:
90
+ text_col = low[k]
91
+ break
92
  if text_col is None:
93
  cand = [c for c in cols if df[c].dtype == object]
94
  text_col = cand[0] if cand else cols[0]
95
 
96
+ # Group candidates (ร้าน/หมวดหมู่)
97
+ group_candidates = []
98
  for c in cols:
99
+ if c == text_col: # ข้ามคอลัมน์ที่เป็น text
 
100
  continue
101
+ if c.lower() in LIKELY_GROUP_COLS:
102
+ group_candidates.append(c)
 
 
 
 
 
 
 
 
 
103
  continue
104
+ # ตรวจว่ามีค่าซ้ำพอสมควร (categorical)
105
  if df[c].dtype == object:
106
  unique_ratio = df[c].nunique() / len(df)
107
+ if 0.01 <= unique_ratio <= 0.5: # 1-50% ของข้อมูลเป็นค่าซ้ำ
108
+ group_candidates.append(c)
 
 
109
 
110
+ group_candidates = list(dict.fromkeys(group_candidates))
111
+ group_col = group_candidates[0] if len(group_candidates) > 0 else None
112
+
113
+ return text_col, group_candidates, group_col
114
 
115
  # ================= Core Predict =================
116
  def _predict_batch(texts, model_name, batch_size=32):
 
167
 
168
  return fig, info
169
 
170
+ def make_group_chart(df, group_col):
171
+ """กราฟแสดงรีวิวแยกตามกลุ่ม (ร้าน/หมวดหมู่/etc)"""
 
 
 
 
 
172
 
173
+ # สรุปแต่ละกลุ่ม
174
+ group_data = []
175
+ for group in df[group_col].unique():
176
+ if pd.isna(group):
177
  continue
178
+ group_df = df[df[group_col] == group]
179
+ neg = len(group_df[group_df["label"]=="negative"])
180
+ pos = len(group_df[group_df["label"]=="positive"])
181
+ total = len(group_df)
182
 
183
+ group_data.append({
184
+ "group": str(group),
185
  "negative": neg,
186
  "positive": pos,
187
  "total": total,
188
  "pos_pct": pos/total*100 if total > 0 else 0
189
  })
190
 
191
+ group_df = pd.DataFrame(group_data).sort_values("total", ascending=False)
192
 
193
  # กราฟแท่ง Stacked
194
  fig = go.Figure()
195
  fig.add_bar(
196
  name="😞 เชิงลบ",
197
+ x=group_df["group"],
198
+ y=group_df["negative"],
199
+ marker_color=NEG_COLOR,
200
+ text=group_df["negative"],
201
+ textposition='inside'
202
  )
203
  fig.add_bar(
204
  name="😊 เชิงบวก",
205
+ x=group_df["group"],
206
+ y=group_df["positive"],
207
+ marker_color=POS_COLOR,
208
+ text=group_df["positive"],
209
+ textposition='inside'
210
  )
211
 
 
 
 
 
212
  fig.update_layout(
213
+ title=f"📊 รีวิวแยกตามกลุ่ม",
214
  barmode='stack',
215
  template=TEMPLATE,
216
+ xaxis_title="",
217
  yaxis_title="จำนวนรีวิว",
218
  height=450,
219
  showlegend=True
 
221
 
222
  # ตารางสรุป
223
  summary_df = pd.DataFrame({
224
+ "กลุ่ม": group_df["group"],
225
+ "รีวิวทั้งหมด": group_df["total"],
226
+ "😞 เชิงลบ": group_df["negative"],
227
+ "😊 เชิงบวก": group_df["positive"],
228
+ "% เชิงบวก": group_df["pos_pct"].apply(lambda x: f"{x:.1f}%")
229
  })
230
 
231
  return fig, summary_df
 
256
  if file_obj is None:
257
  return (gr.update(choices=[], value=None),
258
  gr.update(choices=[], value=None),
259
+ gr.update(visible=False),
260
  gr.update(visible=False),
261
  "⚠️ กรุณาอัปโหลดไฟล์ CSV")
262
 
263
  try:
264
  df = pd.read_csv(file_obj.name)
265
+ text_col, group_candidates, group_col = detect_columns(df)
 
 
266
 
267
+ has_group = group_col is not None
 
268
 
269
+ note = f"✅ **ตรวจพบคอลัมน์**\n\n"
270
+ note += f"📝 **ข้อความรีวิว:** {text_col}\n\n"
271
 
272
+ if has_group:
273
+ note += f"📊 **กลุ่ม/หมวดหมู่:** {group_col} ({df[group_col].nunique()} กลุ่ม)\n\n"
274
  else:
275
+ note += f"📊 **กลุ่ม/หมวดหมู่:** _ไม่พบ_\n\n"
276
 
277
+ note += "_หากต้องการเปลี่ยน สามารถเลือกคอลัมน์ใหม่ได้ด้านบน_"
278
 
279
  return (gr.update(choices=list(df.columns), value=text_col),
280
+ gr.update(choices=group_candidates if group_candidates else ["ไม่มี"],
281
+ value=group_col if group_col else "ไม่มี"),
282
+ gr.update(visible=has_group),
283
+ gr.update(visible=has_group),
284
  note)
285
 
286
  except Exception as e:
287
  return (gr.update(choices=[], value=None),
288
  gr.update(choices=[], value=None),
289
+ gr.update(visible=False),
290
  gr.update(visible=False),
291
  f"❌ ไม่สามารถอ่านไฟล์ได้: {str(e)}")
292
 
293
+ def predict_csv(file_obj, model_choice, text_col, group_col):
294
  """วิเคราะห์ CSV"""
295
  if file_obj is None:
296
  return (pd.DataFrame(), go.Figure(),
297
+ gr.update(visible=False),
298
+ gr.update(visible=False),
299
  "❌ กรุณาอัปโหลดไฟล์", None)
300
 
301
  try:
 
305
 
306
  # ตรวจสอบ text column
307
  if text_col not in cols:
308
+ text_col, _, _ = detect_columns(df_raw)
309
 
310
  # ดึงข้อความ
311
  texts = [_norm_text(v) for v in df_raw[text_col].tolist()]
 
314
 
315
  if not texts_clean:
316
  return (pd.DataFrame(), go.Figure(),
317
+ gr.update(visible=False),
318
+ gr.update(visible=False),
319
  "❌ ไม่พบข้อความที่วิเคราะห์ได้", None)
320
 
321
  # ทำนาย
 
326
  fig_main, info = make_summary_chart(df_out)
327
 
328
  if skipped > 0:
329
+ info += f"\n\n⚠️ ข้ามแถวว่าง: {skipped} แถว (ใช้ {len(texts_clean)}/{total_rows} แถว)"
330
 
331
+ # วิเคราะห์ตามกลุ่ม (ถ้ามี)
332
+ fig_group = go.Figure()
333
+ group_summary = pd.DataFrame()
334
+ show_group = False
335
 
336
+ if group_col and group_col in cols and group_col != "ไม่มี":
337
  # เตรียมข้อมูล
338
+ df_group = df_out.copy()
339
+ df_group[group_col] = df_raw[group_col].iloc[:len(df_out)]
340
 
341
+ # ลบแถวที่ไม่มีข้อมูลกลุ่ม
342
+ df_group = df_group.dropna(subset=[group_col])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
+ if len(df_group) > 0:
345
+ fig_group, group_summary = make_group_chart(df_group, group_col)
346
+ show_group = True
347
+
348
+ info += f"\n\n📊 **วิเคราะห์เพิ่มเติม:** แยกตาม '{group_col}'"
349
 
350
  # บันทึกไฟล์
351
  fd, path = tempfile.mkstemp(suffix=".csv")
 
353
  df_out.to_csv(path, index=False, encoding="utf-8-sig")
354
 
355
  return (df_out, fig_main,
356
+ gr.update(visible=show_group, value=fig_group),
357
+ gr.update(visible=show_group, value=group_summary),
358
  info, path)
359
 
360
  except Exception as e:
361
  return (pd.DataFrame(), go.Figure(),
362
+ gr.update(visible=False),
363
+ gr.update(visible=False),
364
  f"❌ เกิดข้อผิดพลาด:\n{traceback.format_exc()}", None)
365
 
366
  # ================= Gradio UI =================
 
377
  info="WCB = เร็ว | WCB_BiLSTM = แม่นยำสูงสุด (แนะนำ)"
378
  )
379
 
380
+ # =================== Tab 1: วิเคราะห์ข้อความ ===================
381
  with gr.Tab("📝 วิเคราะห์ข้อความ"):
382
+ gr.Markdown("""
383
+ **วิธีใช้:** ป้อนรีวิวหลายรายการ (แต่ละบรรทัด = 1 รีวิว)
384
+
385
+ **ตัวอย่าง:**
386
+ ```
387
+ อาหารอร่อยมาก บริการดี
388
+ ของแพง รสชาติธรรมดา
389
+ บรรยากาศดี แนะนำเลย
390
+ ```
391
+ """)
392
 
393
  text_input = gr.Textbox(
394
  lines=8,
395
  label="📄 ข้อความรีวิว",
396
+ placeholder="ป้อนรีวิว แต่ละบรรทัด = 1 รีวิว..."
397
  )
398
 
399
  predict_btn_1 = gr.Button("🚀 เริ่มวิเคราะห์", variant="primary", size="lg")
 
412
  [result_df_1, result_chart_1, result_info_1]
413
  )
414
 
415
+ # =================== Tab 2: อัปโหลด CSV ===================
416
+ with gr.Tab("📤 วิเคราะห์ไฟล์ CSV"):
417
+ gr.Markdown("""
418
+ **วิธีใช้:** อัปโหลดไฟล์ CSV ที่มีคอลัมน์รีวิว
419
+
420
+ **ระบบจะตรวจหาอัตโนมัติ:**
421
+ - 📝 คอลัมน์ข้อความรีวิว
422
+ - 📊 คอลัมน์กลุ่ม/หมวดหมู่ (เช่น ร้าน, สาขา, ประเภทสินค้า, แบรนด์)
423
+
424
+ **ใช้ได้กับหลายสถานการณ์:**
425
+ - เปรียบเทียบร้านค้า/สาขา
426
+ - วิเคราะห์ตาม product category
427
+ - แยกตามแบรนด์/ประเภทสินค้า
428
+ - หรือข้อมูล categorical อื่นๆ
429
+ """)
430
 
431
  file_input = gr.File(file_types=[".csv"], label="📁 อัปโหลดไฟล์ CSV")
432
 
433
  detect_note = gr.Markdown("⬆️ อัปโหลดไฟล์เพื่อเริ่มต้น")
434
 
435
  with gr.Row():
436
+ text_col_dd = gr.Dropdown(
437
+ label="📝 คอลัมน์ข้อความรีวิว",
438
+ info="เลือกคอลัมน์ที่มีเนื้อหารีวิว"
439
+ )
440
+ group_col_dd = gr.Dropdown(
441
+ label="📊 คอลัมน์กลุ่ม/หมวดหมู่ (ถ้ามี)",
442
+ info="เช่น ร้าน, สาขา, ประเภทสินค้า, แบรนด์"
443
+ )
 
 
 
444
 
445
  predict_btn_2 = gr.Button("🚀 เริ่มวิเคราะห์", variant="primary", size="lg")
446
 
447
+ gr.Markdown("### 📊 ผลการวิเคราะห์")
448
+
449
+ result_df_2 = gr.Dataframe(label="📋 รายละเอียดทุกรีวิว")
450
 
451
  with gr.Row():
452
  with gr.Column(scale=1):
453
+ result_chart_2 = gr.Plot(label="📊 สรุปภาพรวม")
454
  with gr.Column(scale=1):
455
  result_info_2 = gr.Markdown()
456
 
457
+ result_group = gr.Plot(label="📊 เปรียบเทียบแต่ละกลุ่ม", visible=False)
458
+ group_summary = gr.Dataframe(label="📋 สรุปแต่ละกลุ่ม", visible=False)
459
 
460
+ download_file = gr.File(label="💾 ดาวน์โหลดผลลัพธ์ (CSV)")
461
 
462
  # Events
463
  file_input.change(
464
  on_file_change,
465
  [file_input],
466
+ [text_col_dd, group_col_dd, result_group, group_summary, detect_note]
467
  )
468
 
469
  predict_btn_2.click(
470
  predict_csv,
471
+ [file_input, model_radio, text_col_dd, group_col_dd],
472
+ [result_df_2, result_chart_2, result_group, group_summary, result_info_2, download_file]
473
  )
474
 
475
  gr.Markdown("""