你是資料視覺化專家。請在這個專案中實作完整的時間維度視覺化分析。

專案路徑：C:\Users\User\Documents\GitHub\apify-scraper-practice

現有資料檔案：
- step2_reviews_20260129_143615.json（最新評論）
- step2b_lowest_reviews_20260129_144321.json（最差評論）

請實作以下功能，建立檔案 `time_analysis.py`：

1. 時間軸圖表
   - 每日評論數趨勢圖（折線圖 + 異常標記）
   - 用紅色標記異常高峰日期
   - 標註可能的原因（自然尖峰/行銷活動/刷評嫌疑）

2. 熱力圖
   - 時段 x 星期 的評論數量熱力圖
   - 顯示評論最集中的時間

3. 累積曲線
   - 累積評論數隨時間變化
   - 標記斜率突然變化的點

4. 刷評嫌疑分析
   - 爆發偵測（滾動平均 + 3σ）
   - 規律間隔偵測
   - 凌晨異常偵測
   - 計算刷評嫌疑分數

5. 輸出
   - 將圖表儲存到 output/ 資料夾
   - 在 console 輸出分析報告

注意事項：
- Python 腳本開頭要加 `import sys; sys.stdout.reconfigure(encoding='utf-8')`
- 使用 matplotlib 和 seaborn
- 設定中文字體支援
- 對每間店家都執行分析

請直接實作完整程式碼並測試執行。


Bash

列出專案目錄內容


total 7312
drwxr-xr-x 1 User 197121       0 Jan 29 15:31 .
drwxr-xr-x 1 User 197121       0 Jan 29 10:57 ..
drwxr-xr-x 1 User 197121       0 Jan 29 15:43 .claude
-rw-r--r-- 1 User 197121      64 Jan 29 13:18 .env
-rw-r--r-- 1 User 197121     118 Jan 26 11:53 .env.example
drwxr-xr-x 1 User 197121       0 Jan 29 15:43 .git
-rw-r--r-- 1 User 197121     148 Jan 26 11:53 .gitignore
-rw-r--r-- 1 User 197121  199500 Jan 29 15:30 analysis_full_20260129_153019.json
-rw-r--r-- 1 User 197121   74279 Jan 29 15:30 analysis_methods.py
-rw-r--r-- 1 User 197121    7017 Jan 29 15:31 analysis_summary.md
-rw-r--r-- 1 User 197121    3038 Jan 26 11:54 analyze_fb_posts.py
-rw-r--r-- 1 User 197121    4741 Jan 29 15:00 check_usage.py
-rw-r--r-- 1 User 197121     611 Jan 29 13:51 CLAUDE.md
drwxr-xr-x 1 User 197121       0 Jan 29 15:09 docs
drwxr-xr-x 1 User 197121       0 Jan 26 11:53 fb
-rw-r--r-- 1 User 197121    4009 Jan 26 12:49 fb_scraper.py
-rw-r--r-- 1 User 197121    2638 Jan 29 13:53 gmaps_reviews_scraper.py
-rw-r--r-- 1 User 197121    4117 Jan 26 12:49 google_maps_scraper.py
-rw-r--r-- 1 User 197121    5150 Jan 29 13:55 mvp_test.py
-rw-r--r-- 1 User 197121    4494 Jan 29 15:03 README.md
drwxr-xr-x 1 User 197121       0 Jan 29 15:41 research
-rw-r--r-- 1 User 197121   12094 Jan 29 13:53 review_analyzer.py
-rw-r--r-- 1 User 197121    4614 Jan 29 15:14 scrape_shared_list.py
-rw-r--r-- 1 User 197121    1667 Jan 26 11:53 scraper.py
-rw-r--r-- 1 User 197121       2 Jan 29 15:14 shared_list_20260129_151449.json
-rw-r--r-- 1 User 197121    9237 Jan 29 14:19 step1_get_place_info.py
-rw-r--r-- 1 User 197121   23628 Jan 29 14:17 step1_places_20260129_141745.json
-rw-r--r-- 1 User 197121  227177 Jan 29 14:20 step1_places_20260129_142041.json
-rw-r--r-- 1 User 197121    5413 Jan 29 14:33 step2_get_reviews.py
-rw-r--r-- 1 User 197121    2110 Jan 29 14:32 step2_reviews_20260129_143208.json
-rw-r--r-- 1 User 197121 3675464 Jan 29 14:36 step2_reviews_20260129_143615.json
-rw-r--r-- 1 User 197121    5377 Jan 29 14:40 step2b_get_lowest_reviews.py
-rw-r--r-- 1 User 197121 3064785 Jan 29 14:43 step2b_lowest_reviews_20260129_144321.json
-rw-r--r-- 1 User 197121   20645 Jan 29 14:45 step3_analysis_20260129_144524.json
-rw-r--r-- 1 User 197121   10620 Jan 29 14:45 step3_analyze.py


讓我先查看資料檔案的結構，了解評論資料的格式。


     1→[
     2→  {
     3→    "title": "今日魚市-南京店-原丼賞和食",
     4→    "subTitle": null,
     5→    "description": "色彩繽紛的自助式餐廳，供應丼飯、壽司與其他日式特色料理。",
     6→    "price": "$400-600",
     7→    "categoryName": "日本餐廳",
     8→    "address": "B1, No. 140號南京東路二段中山區臺北市台灣 10491",
     9→    "neighborhood": null,
    10→    "street": "B1, No. 140號南京東路二段",
    11→    "city": null,
    12→    "postalCode": "10491",
    13→    "state": "臺北市中山區",
    14→    "countryCode": "TW",
    15→    "website": "https://www.facebook.com/140B1/",
    16→    "phone": "+886 2 2517 5526",
    17→    "phoneUnformatted": "+886225175526",
    18→    "claimThisBusiness": false,
    19→    "location": {
    20→      "lat": 25.0517646,
    21→      "lng": 121.533861
    22→    },
    23→    "locatedIn": null,
    24→    "floor": "1 樓",
    25→    "plusCode": "3G2M+PG 朱園里 台灣臺北市中山區",
    26→    "menu": "https://www.facebook.com/photo/?fbid=993342986172685&set=ecnf.100064909770582&locale=zh_TW",
    27→    "totalScore": 4.7,
    28→    "permanentlyClosed": false,
    29→    "temporarilyClosed": false,
    30→    "placeId": "ChIJ9240AWGpQjQRYkqzSvuO47I",
    31→    "categories": [
    32→      "日本餐廳"
    33→    ],
    34→    "fid": "0x3442a96101346ef7:0xb2e38efb4ab34a62",
    35→    "cid": "12890303768405690978",
    36→    "reviewsCount": 14219,
    37→    "reviewsDistribution": {
    38→      "oneStar": 337,
    39→      "twoStar": 218,
    40→      "threeStar": 559,
    41→      "fourStar": 1179,
    42→      "fiveStar": 11926
    43→    },
    44→    "imagesCount": 18563,
    45→    "imageCategories": [
    46→      "全部",
    47→      "最新",
    48→      "影片",
    49→      "菜單",
    50→      "餐飲",
    51→      "氛圍",
    52→      "牡蠣",
    53→      "鯖魚",
    54→      "螃蟹",
    55→      "味噌湯",
    56→      "湯",
    57→      "丼物",
    58→      "日式烤雞串",
    59→      "壽司",
    60→      "業主精選圖片",
    61→      "街景服務和 360 度相片"
    62→    ],
    63→    "scrapedAt": "2026-01-29T06:33:37.333Z",
    64→    "reserveTableUrl": "https://www.google.com/maps/reserve/v/dine/c/w4F_tV2ME-4?source=pa&opi=79508299&hl=zh-TW&gei=P_96abfFOLPJptQP9rSz2As&sourceurl=https://www.google.com/maps/preview/place?authuser%3D0%26hl%3Dzh-TW%26gl%3Dus%26pb%3D!1m16!1s0x3442a96101346ef7:0xb2e38efb4ab34a62!3m8!1m3!1d3614.4784037037157!2d121.5289901!3d25.0517694!3m2!1i489!2i906!4f13.1!4m2!3d40.7606891!4d-73.9991868!15m2!1m1!4s/m/053lrp6!12m4!2m3!1i360!2i120!4i8!13m57!2m2!1i203!2i100!3m2!2i4!5b1!6m6!1m2!1i86!2i86!1m2!1i408!2i240!7m33!1m3!1e1!2b0!3e3!1m3!1e2!2b1!3e2!1m3!1e2!2b0!3e3!1m3!1e8!2b0!3e3!1m3!1e10!2b0!3e3!1m3!1e10!2b1!3e2!1m3!1e10!2b0!3e4!1m3!1e9!2b1!3e2!2b1!9b0!15m8!1m7!1m2!1m1!1e2!2m2!1i195!2i195!3i20!14m2!1sMcYNaceANdnbi-gPt5PjyQQ!7e81!15m110!1m31!13m9!2b1!3b1!4b1!6i1!8b1!9b1!14b1!20b1!25b1!18m20!3b1!4b1!5b1!6b1!9b1!13b1!14b1!17b1!20b1!21b1!22b1!27m1!1b0!28b0!30b1!32b1!33m1!1b1!34b1!36e2!10m1!8e3!11m1!3e1!14m1!3b0!17b1!20m2!1e3!1e6!24b1!25b1!26b1!27b1!29b1!30m1!2b1!36b1!37b1!39m3!2m2!2i1!3i1!43b1!52b1!54m1!1b1!55b1!56m1!1b1!61m2!1m1!1e1!65m5!3m4!1m3!1m2!1i224!2i298!72m22!1m8!2b1!5b1!7b1!12m4!1b1!2b1!4m1!1e1!4b1!8m10!1m6!4m1!1e1!4m1!1e3!4m1!1e4!3sother_user_google_review_posts__and__hotel_and_vr_partner_review_posts!6m1!1e1!9b1!89b1!98m3!1b1!2b1!3b1!103b1!113b1!114m3!1b1!2m1!1b1!117b1!122m1!1b1!126b1!127b1!21m0!22m1!1e81!30m6!3b1!6m1!2b1!7m1!2b1!9b1!34m5!7b1!10b1!14b1!15m1!1b0!37i757!39s%25E4%25BB%258A%25E6%2597%25A5%25E9%25AD%259A%25E5%25B8%2582-%25E5%258D%2597%25E4%25BA%25AC%25E5%25BA%2597-%25E5%258E%259F%25E4%25B8%25BC%25E8%25B3%259E%25E5%2592%258C%25E9%25A3%259F%26q%3D%25E4%25BB%258A%25E6%2597%25A5%25E9%25AD%259A%25E5%25B8%2582-%25E5%258D%2597%25E4%25BA%25AC%25E5%25BA%2597-%25E5%258E%259F%25E4%25B8%25BC%25E8%25B3%259E%25E5%2592%258C%25E9%25A3%259F",
    65→    "googleFoodUrl": null,
    66→    "hotelStars": null,
    67→    "hotelDescription": null,
    68→    "checkInDate": null,
    69→    "checkOutDate": null,
    70→    "similarHotelsNearby": null,
    71→    "hotelReviewSummary": null,
    72→    "hotelAds": [],
    73→    "popularTimesLiveText": "比平常略為繁忙",
    74→    "popularTimesLivePercent": 65,
    75→    "popularTimesHistogram": {
    76→      "Su": [
    77→        {
    78→          "hour": 6,
    79→          "occupancyPercent": 0
    80→        },
    81→        {
    82→          "hour": 7,
    83→          "occupancyPercent": 0
    84→        },
    85→        {
    86→          "hour": 8,
    87→          "occupancyPercent": 0
    88→        },
    89→        {
    90→          "hour": 9,
    91→          "occupancyPercent": 0
    92→        },
    93→        {
    94→          "hour": 10,
    95→          "occupancyPercent": 0
    96→        },
    97→        {
    98→          "hour": 11,
    99→          "occupancyPercent": 47
   100→        },

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→[
     2→  {
     3→    "title": "義大利米蘭手工窯烤披薩 台北中山店",
     4→    "subTitle": null,
     5→    "description": null,
     6→    "price": "$$$",
     7→    "categoryName": "薄餅餐廳",
     8→    "address": "10491台灣臺北市中山區四平街55號2樓",
     9→    "neighborhood": null,
    10→    "street": "四平街55號2樓",
    11→    "city": null,
    12→    "postalCode": "10491",
    13→    "state": "臺北市中山區",
    14→    "countryCode": "TW",
    15→    "website": null,
    16→    "phone": "+886 2 2502 0902",
    17→    "phoneUnformatted": "+886225020902",
    18→    "claimThisBusiness": false,
    19→    "location": {
    20→      "lat": 25.0537685,
    21→      "lng": 121.5335221
    22→    },
    23→    "locatedIn": null,
    24→    "floor": null,
    25→    "plusCode": "3G3M+GC 中央里 台灣臺北市中山區",
    26→    "menu": "https://inline.app/order/-LARRLsDAuGtgfdhUstQ/-LARRLsDAuGtgfdhUstR?language=zh-tw",
    27→    "totalScore": 4.3,
    28→    "permanentlyClosed": false,
    29→    "temporarilyClosed": false,
    30→    "placeId": "ChIJ_daWjWCpQjQR07ZxjRhveNE",
    31→    "categories": [
    32→      "薄餅餐廳",
    33→      "意大利餐廳",
    34→      "西餐廳"
    35→    ],
    36→    "fid": "0x3442a9608d96d6fd:0xd1786f188d71b6d3",
    37→    "cid": "15093936302375155411",
    38→    "reviewsCount": 2165,
    39→    "reviewsDistribution": {
    40→      "oneStar": 163,
    41→      "twoStar": 66,
    42→      "threeStar": 122,
    43→      "fourStar": 324,
    44→      "fiveStar": 1490
    45→    },
    46→    "imagesCount": 6236,
    47→    "imageCategories": [
    48→      "全部",
    49→      "最新",
    50→      "影片",
    51→      "菜單",
    52→      "餐飲",
    53→      "氛圍",
    54→      "奶油甜餡煎餅卷",
    55→      "法國蝸牛",
    56→      "披薩",
    57→      "奶油蘑菇湯",
    58→      "啤酒",
    59→      "鶏手羽",
    60→      "薯條",
    61→      "番茄湯",
    62→      "凱撒沙拉",
    63→      "業主精選圖片",
    64→      "街景服務和 360 度相片"
    65→    ],
    66→    "scrapedAt": "2026-01-29T06:41:00.598Z",
    67→    "reserveTableUrl": null,
    68→    "googleFoodUrl": null,
    69→    "hotelStars": null,
    70→    "hotelDescription": null,
    71→    "checkInDate": null,
    72→    "checkOutDate": null,
    73→    "similarHotelsNearby": null,
    74→    "hotelReviewSummary": null,
    75→    "hotelAds": [],
    76→    "popularTimesLiveText": "有點繁忙",
    77→    "popularTimesLivePercent": 67,
    78→    "popularTimesHistogram": {
    79→      "Su": [
    80→        {
    81→          "hour": 6,
    82→          "occupancyPercent": 0
    83→        },
    84→        {
    85→          "hour": 7,
    86→          "occupancyPercent": 0
    87→        },
    88→        {
    89→          "hour": 8,
    90→          "occupancyPercent": 0
    91→        },
    92→        {
    93→          "hour": 9,
    94→          "occupancyPercent": 0
    95→        },
    96→        {
    97→          "hour": 10,
    98→          "occupancyPercent": 0
    99→        },
   100→        {

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


讓我繼續查看評論資料的結構，特別是評論中的時間欄位。


920:        "publishAt": "2 小時前",
921:        "publishedAtDate": "2026-01-29T04:09:42.091Z",
953:        "publishAt": "1 天前",
954:        "publishedAtDate": "2026-01-28T05:02:50.330Z",
983:        "publishAt": "1 天前",
984:        "publishedAtDate": "2026-01-28T04:47:47.115Z",
1013:        "publishAt": "1 天前",
1014:        "publishedAtDate": "2026-01-28T04:44:52.399Z",
1043:        "publishAt": "1 天前",
1044:        "publishedAtDate": "2026-01-28T04:43:04.357Z",
1073:        "publishAt": "1 天前",
1074:        "publishedAtDate": "2026-01-28T04:27:09.136Z",
1107:        "publishAt": "1 天前",
1108:        "publishedAtDate": "2026-01-28T03:20:01.890Z",
1146:        "publishAt": "1 天前",
1147:        "publishedAtDate": "2026-01-27T10:49:28.019Z",
1184:        "publishAt": "2 天前",
1185:        "publishedAtDate": "2026-01-27T05:40:50.174Z",
1220:        "publishAt": "2 天前",
1221:        "publishedAtDate": "2026-01-27T05:23:06.959Z",

[Showing results with pagination = limit: 20, offset: 0]


很好，我看到評論資料包含 `publishedAtDate` 欄位。讓我查看更完整的評論結構。


   900→    "orderBy": [],
   901→    "restaurantData": {
   902→      "tableReservationProvider": {
   903→        "name": "inline",
   904→        "reserveTableUrl": "https://www.google.com/maps/reserve/v/dine/c/w4F_tV2ME-4?source=pa&opi=79508299&hl=zh-TW&gei=P_96abfFOLPJptQP9rSz2As&sourceurl=https://www.google.com/maps/preview/place?authuser%3D0%26hl%3Dzh-TW%26gl%3Dus%26pb%3D!1m16!1s0x3442a96101346ef7:0xb2e38efb4ab34a62!3m8!1m3!1d3614.4784037037157!2d121.5289901!3d25.0517694!3m2!1i489!2i906!4f13.1!4m2!3d40.7606891!4d-73.9991868!15m2!1m1!4s/m/053lrp6!12m4!2m3!1i360!2i120!4i8!13m57!2m2!1i203!2i100!3m2!2i4!5b1!6m6!1m2!1i86!2i86!1m2!1i408!2i240!7m33!1m3!1e1!2b0!3e3!1m3!1e2!2b1!3e2!1m3!1e2!2b0!3e3!1m3!1e8!2b0!3e3!1m3!1e10!2b0!3e3!1m3!1e10!2b1!3e2!1m3!1e10!2b0!3e4!1m3!1e9!2b1!3e2!2b1!9b0!15m8!1m7!1m2!1m1!1e2!2m2!1i195!2i195!3i20!14m2!1sMcYNaceANdnbi-gPt5PjyQQ!7e81!15m110!1m31!13m9!2b1!3b1!4b1!6i1!8b1!9b1!14b1!20b1!25b1!18m20!3b1!4b1!5b1!6b1!9b1!13b1!14b1!17b1!20b1!21b1!22b1!27m1!1b0!28b0!30b1!32b1!33m1!1b1!34b1!36e2!10m1!8e3!11m1!3e1!14m1!3b0!17b1!20m2!1e3!1e6!24b1!25b1!26b1!27b1!29b1!30m1!2b1!36b1!37b1!39m3!2m2!2i1!3i1!43b1!52b1!54m1!1b1!55b1!56m1!1b1!61m2!1m1!1e1!65m5!3m4!1m3!1m2!1i224!2i298!72m22!1m8!2b1!5b1!7b1!12m4!1b1!2b1!4m1!1e1!4b1!8m10!1m6!4m1!1e1!4m1!1e3!4m1!1e4!3sother_user_google_review_posts__and__hotel_and_vr_partner_review_posts!6m1!1e1!9b1!89b1!98m3!1b1!2b1!3b1!103b1!113b1!114m3!1b1!2m1!1b1!117b1!122m1!1b1!126b1!127b1!21m0!22m1!1e81!30m6!3b1!6m1!2b1!7m1!2b1!9b1!34m5!7b1!10b1!14b1!15m1!1b0!37i757!39s%25E4%25BB%258A%25E6%2597%25A5%25E9%25AD%259A%25E5%25B8%2582-%25E5%258D%2597%25E4%25BA%25AC%25E5%25BA%2597-%25E5%258E%259F%25E4%25B8%25BC%25E8%25B3%259E%25E5%2592%258C%25E9%25A3%259F%26q%3D%25E4%25BB%258A%25E6%2597%25A5%25E9%25AD%259A%25E5%25B8%2582-%25E5%258D%2597%25E4%25BA%25AC%25E5%25BA%2597-%25E5%258E%259F%25E4%25B8%25BC%25E8%25B3%259E%25E5%2592%258C%25E9%25A3%259F"
   905→      }
   906→    },
   907→    "ownerUpdates": [],
   908→    "imageUrls": [],
   909→    "images": null,
   910→    "reviews": [
   911→      {
   912→        "reviewerId": "100734908245064780279",
   913→        "reviewerUrl": "https://www.google.com/maps/contrib/100734908245064780279?hl=zh-TW",
   914→        "name": "魏佩怡",
   915→        "reviewerNumberOfReviews": 1,
   916→        "isLocalGuide": false,
   917→        "reviewerPhotoUrl": "https://lh3.googleusercontent.com/a/ACg8ocJ7tdIWW5Q62vG26M58Xw2V3VcqTR6em3btH178RNvKIO0wxQ=s1920-c-rp-mo-br100",
   918→        "text": "店員很貼心，餐點很棒",
   919→        "textTranslated": null,
   920→        "publishAt": "2 小時前",
   921→        "publishedAtDate": "2026-01-29T04:09:42.091Z",
   922→        "likesCount": 0,
   923→        "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2t4Q1RrSkdkMjlJUzJVMVlqTjNSV2sxUzBWUWRHYxAB",
   924→        "reviewUrl": "https://www.google.com/maps/reviews/data=!4m8!14m7!1m6!2m5!1sCi9DQUlRQUNvZENodHljRjlvT2t4Q1RrSkdkMjlJUzJVMVlqTjNSV2sxUzBWUWRHYxAB!2m1!1s0x0:0xb2e38efb4ab34a62!3m1!1s2@1:CAIQACodChtycF9oOkxCTkJGd29IS2U1YjN3RWk1S0VQdGc%7C0dThq54ToE6%7C?hl=zh-TW",
   925→        "reviewOrigin": "Google",
   926→        "stars": 5,
   927→        "rating": null,
   928→        "responseFromOwnerDate": null,
   929→        "responseFromOwnerText": null,
   930→        "reviewImageUrls": [],
   931→        "reviewContext": {
   932→          "餐點類型": "午餐",
   933→          "平均每人消費金額": "$400-600"
   934→        },
   935→        "reviewDetailedRating": {
   936→          "餐點": 5,
   937→          "服務": 5,
   938→          "氣氛": 5
   939→        },
   940→        "visitedIn": null,
   941→        "originalLanguage": "zh-Hant",
   942→        "translatedLanguage": null
   943→      },
   944→      {
   945→        "reviewerId": "118340581967193083223",
   946→        "reviewerUrl": "https://www.google.com/maps/contrib/118340581967193083223?hl=zh-TW",
   947→        "name": "Tong Lee",
   948→        "reviewerNumberOfReviews": 0,
   949→        "isLocalGuide": false,
   950→        "reviewerPhotoUrl": "https://lh3.googleusercontent.com/a/ACg8ocIAoMuQ0guQY4sC7-n41mQZwofv4G7lBQSrXoDLTV-R1XJxKg=s1920-c-rp-mo-br100",
   951→        "text": null,
   952→        "textTranslated": null,
   953→        "publishAt": "1 天前",
   954→        "publishedAtDate": "2026-01-28T05:02:50.330Z",
   955→        "likesCount": 0,
   956→        "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT25WaWNucDJUMTgzVWpsR2FITk5Sa1JFYTFZMFIzYxAB",
   957→        "reviewUrl": "https://www.google.com/maps/reviews/data=!4m8!14m7!1m6!2m5!1sCi9DQUlRQUNvZENodHljRjlvT25WaWNucDJUMTgzVWpsR2FITk5Sa1JFYTFZMFIzYxAB!2m1!1s0x0:0xb2e38efb4ab34a62!3m1!1s2@1:CAIQACodChtycF9oOnVicnp2T183UjlGaHNNRkREa1Y0R3c%7C0dTOWuIfLqI%7C?hl=zh-TW",
   958→        "reviewOrigin": "Google",
   959→        "stars": 5,
   960→        "rating": null,
   961→        "responseFromOwnerDate": null,
   962→        "responseFromOwnerText": null,
   963→        "reviewImageUrls": [],
   964→        "reviewContext": {},
   965→        "reviewDetailedRating": {
   966→          "餐點": 5,
   967→          "服務": 5,
   968→          "氣氛": 5
   969→        },
   970→        "visitedIn": null,
   971→        "originalLanguage": null,
   972→        "translatedLanguage": null
   973→      },
   974→      {
   975→        "reviewerId": "107399920523828861266",
   976→        "reviewerUrl": "https://www.google.com/maps/contrib/107399920523828861266?hl=zh-TW",
   977→        "name": "徐秀英",
   978→        "reviewerNumberOfReviews": 0,
   979→        "isLocalGuide": false,
   980→        "reviewerPhotoUrl": "https://lh3.googleusercontent.com/a/ACg8ocLNVylpRh7TuZOe2cHYs9Oz1n75POJQ5qbqC3HkmFfaskqi9Q=s1920-c-rp-mo-br100",
   981→        "text": null,
   982→        "textTranslated": null,
   983→        "publishAt": "1 天前",
   984→        "publishedAtDate": "2026-01-28T04:47:47.115Z",
   985→        "likesCount": 0,
   986→        "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT21SWlZXdFVVSFZ5VEZwQ2RXNWhlVmxWTm5scFRFRRAB",
   987→        "reviewUrl": "https://www.google.com/maps/reviews/data=!4m8!14m7!1m6!2m5!1sCi9DQUlRQUNvZENodHljRjlvT21SWlZXdFVVSFZ5VEZwQ2RXNWhlVmxWTm5scFRFRRAB!2m1!1s0x0:0xb2e38efb4ab34a62!3m1!1s2@1:CAIQACodChtycF9oOmRZVWtUUHVyTFpCdW5heVlVNnlpTEE%7C0dTOIn5s8zT%7C?hl=zh-TW",
   988→        "reviewOrigin": "Google",
   989→        "stars": 5,
   990→        "rating": null,
   991→        "responseFromOwnerDate": null,
   992→        "responseFromOwnerText": null,
   993→        "reviewImageUrls": [],
   994→        "reviewContext": {},
   995→        "reviewDetailedRating": {
   996→          "餐點": 5,
   997→          "服務": 5,
   998→          "氣氛": 5
   999→        },

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


現在我了解資料結構了。讓我建立完整的時間維度視覺化分析程式。


建立 output 資料夾


Write

# -*- coding: utf-8 -*-
"""
時間維度視覺化分析
分析 Google Maps 評論的時間模式，偵測刷評嫌疑
"""

import sys
sys.stdout.reconfigure(encoding='utf-8')

import json
import os
from datetime import datetime, timedelta
from collections import defaultdict, Counter
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
from pathlib import Path

# 設定中文字體
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

# 設定輸出目錄
OUTPUT_DIR = Path(__file__).parent / 'output'
OUTPUT_DIR.mkdir(exist_ok=True)

def load_reviews_data(file_path: str) -> list:
    """載入評論資料"""
    with open(file_path, 'r', encoding='utf-8') as f:
        return json.load(f)

def extract_reviews_with_dates(data: list) -> dict:
    """從資料中提取所有評論及其時間"""
    stores = {}

    for store in data:
        store_name = store.get('title', 'Unknown')
        reviews = store.get('reviews', [])

        review_list = []
        for review in reviews:
            date_str = review.get('publishedAtDate')
            if date_str:
                try:
                    # 解析 ISO 格式日期
                    dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
                    review_list.append({
                        'datetime': dt,
                        'date': dt.date(),
                        'hour': dt.hour,
                        'weekday': dt.weekday(),  # 0=Monday, 6=Sunday
                        'stars': review.get('stars', 0),
                        'name': review.get('name', ''),
                        'text': review.get('text', ''),
                        'reviewerNumberOfReviews': review.get('reviewerNumberOfReviews', 0),
                        'isLocalGuide': review.get('isLocalGuide', False)
                    })
                except (ValueError, TypeError):
                    continue

        if review_list:
            stores[store_name] = sorted(review_list, key=lambda x: x['datetime'])

    return stores

def detect_anomalies(daily_counts: pd.Series, window: int = 7, sigma: float = 3.0) -> pd.DataFrame:
    """使用滾動平均 + 3σ 偵測異常"""
    df = pd.DataFrame({'count': daily_counts})
    df['rolling_mean'] = df['count'].rolling(window=window, min_periods=1).mean()
    df['rolling_std'] = df['count'].rolling(window=window, min_periods=1).std().fillna(0)

    # 計算上下界
    df['upper_bound'] = df['rolling_mean'] + sigma * df['rolling_std']
    df['lower_bound'] = df['rolling_mean'] - sigma * df['rolling_std']

    # 標記異常
    df['is_anomaly'] = df['count'] > df['upper_bound']

    return df

def detect_regular_intervals(reviews: list, tolerance_minutes: int = 30) -> dict:
    """偵測規律間隔的評論"""
    if len(reviews) < 3:
        return {'detected': False, 'intervals': [], 'score': 0}

    # 計算相鄰評論的時間間隔（分鐘）
    intervals = []
    for i in range(1, len(reviews)):
        delta = reviews[i]['datetime'] - reviews[i-1]['datetime']
        intervals.append(delta.total_seconds() / 60)

    if not intervals:
        return {'detected': False, 'intervals': [], 'score': 0}

    # 找出常見間隔
    interval_counts = Counter()
    for interval in intervals:
        # 將間隔四捨五入到最近的 10 分鐘
        rounded = round(interval / 10) * 10
        if 5 <= rounded <= 1440:  # 5分鐘到24小時
            interval_counts[rounded] += 1

    # 計算規律性分數
    if interval_counts:
        most_common_interval, count = interval_counts.most_common(1)[0]
        regularity_score = count / len(intervals)
    else:
        regularity_score = 0

    return {
        'detected': regularity_score > 0.3,
        'most_common_interval': interval_counts.most_common(3) if interval_counts else [],
        'score': regularity_score
    }

def detect_late_night_reviews(reviews: list) -> dict:
    """偵測凌晨異常評論（0:00-6:00）"""
    late_night = [r for r in reviews if 0 <= r['hour'] < 6]
    total = len(reviews)

    if total == 0:
        return {'count': 0, 'percentage': 0, 'suspicious': False}

    percentage = len(late_night) / total * 100

    return {
        'count': len(late_night),
        'percentage': percentage,
        'suspicious': percentage > 15,  # 超過 15% 視為可疑
        'reviews': late_night[:10]  # 只保留前10筆範例
    }

def calculate_suspicion_score(store_name: str, reviews: list, daily_anomalies: pd.DataFrame) -> dict:
    """計算刷評嫌疑分數"""
    scores = {
        'burst_score': 0,
        'interval_score': 0,
        'late_night_score': 0,
        'new_account_score': 0,
        'five_star_only_score': 0
    }

    # 1. 爆發偵測分數
    if not daily_anomalies.empty:
        anomaly_ratio = daily_anomalies['is_anomaly'].sum() / len(daily_anomalies)
        scores['burst_score'] = min(anomaly_ratio * 100, 30)

    # 2. 規律間隔分數
    interval_result = detect_regular_intervals(reviews)
    scores['interval_score'] = interval_result['score'] * 25

    # 3. 凌晨異常分數
    late_night = detect_late_night_reviews(reviews)
    if late_night['suspicious']:
        scores['late_night_score'] = min(late_night['percentage'], 20)

    # 4. 新帳號比例分數
    if reviews:
        new_accounts = sum(1 for r in reviews if r['reviewerNumberOfReviews'] <= 1)
        new_account_ratio = new_accounts / len(reviews)
        scores['new_account_score'] = new_account_ratio * 15

    # 5. 純五星評論分數
    if reviews:
        five_stars = sum(1 for r in reviews if r['stars'] == 5)
        five_star_ratio = five_stars / len(reviews)
        if five_star_ratio > 0.9:
            scores['five_star_only_score'] = (five_star_ratio - 0.9) * 100

    total_score = sum(scores.values())

    return {
        'store_name': store_name,
        'total_score': round(total_score, 2),
        'risk_level': 'high' if total_score >= 50 else ('medium' if total_score >= 25 else 'low'),
        'breakdown': {k: round(v, 2) for k, v in scores.items()}
    }

def plot_daily_trend(store_name: str, reviews: list, output_path: Path) -> pd.DataFrame:
    """繪製每日評論數趨勢圖"""
    if not reviews:
        return pd.DataFrame()

    # 建立每日統計
    dates = [r['date'] for r in reviews]
    daily_counts = pd.Series(dates).value_counts().sort_index()

    # 填補缺失日期
    if len(daily_counts) > 1:
        date_range = pd.date_range(start=daily_counts.index.min(), end=daily_counts.index.max())
        daily_counts = daily_counts.reindex(date_range, fill_value=0)

    # 偵測異常
    anomalies_df = detect_anomalies(daily_counts)

    # 繪圖
    fig, ax = plt.subplots(figsize=(14, 6))

    # 主要趨勢線
    ax.plot(daily_counts.index, daily_counts.values, 'b-', linewidth=1.5, label='每日評論數')

    # 滾動平均線
    ax.plot(anomalies_df.index, anomalies_df['rolling_mean'], 'g--', linewidth=1, alpha=0.7, label='7日滾動平均')

    # 標記異常點
    anomaly_dates = anomalies_df[anomalies_df['is_anomaly']].index
    anomaly_values = daily_counts[anomaly_dates]
    ax.scatter(anomaly_dates, anomaly_values, color='red', s=100, zorder=5, label='異常高峰')

    # 為異常點加註
    for date, value in zip(anomaly_dates, anomaly_values):
        ax.annotate(f'{value}則', (date, value), textcoords="offset points",
                   xytext=(0, 10), ha='center', fontsize=8, color='red')

    ax.set_xlabel('日期')
    ax.set_ylabel('評論數')
    ax.set_title(f'{store_name} - 每日評論數趨勢圖')
    ax.legend(loc='upper left')
    ax.grid(True, alpha=0.3)

    # 格式化 x 軸日期
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
    ax.xaxis.set_major_locator(mdates.AutoDateLocator())
    plt.xticks(rotation=45)

    plt.tight_layout()

    # 儲存圖表
    safe_name = store_name.replace('/', '_').replace('\\', '_').replace(':', '_')[:30]
    fig.savefig(output_path / f'{safe_name}_daily_trend.png', dpi=150, bbox_inches='tight')
    plt.close(fig)

    return anomalies_df

def plot_heatmap(store_name: str, reviews: list, output_path: Path):
    """繪製時段 x 星期 熱力圖"""
    if not reviews:
        return

    # 建立時段-星期矩陣
    weekday_names = ['週一', '週二', '週三', '週四', '週五', '週六', '週日']
    hour_ranges = [f'{h:02d}:00' for h in range(24)]

    matrix = np.zeros((24, 7))
    for r in reviews:
        matrix[r['hour'], r['weekday']] += 1

    # 繪製熱力圖
    fig, ax = plt.subplots(figsize=(10, 12))

    sns.heatmap(matrix, annot=True, fmt='.0f', cmap='YlOrRd',
                xticklabels=weekday_names, yticklabels=hour_ranges,
                ax=ax, cbar_kws={'label': '評論數'})

    ax.set_xlabel('星期')
    ax.set_ylabel('時段')
    ax.set_title(f'{store_name} - 評論時段熱力圖')

    plt.tight_layout()

    # 儲存圖表
    safe_name = store_name.replace('/', '_').replace('\\', '_').replace(':', '_')[:30]
    fig.savefig(output_path / f'{safe_name}_heatmap.png', dpi=150, bbox_inches='tight')
    plt.close(fig)

    return matrix

def plot_cumulative_curve(store_name: str, reviews: list, output_path: Path):
    """繪製累積評論數曲線"""
    if not reviews:
        return

    # 建立時間序列
    dates = [r['datetime'] for r in reviews]
    cumulative = list(range(1, len(dates) + 1))

    # 計算斜率變化
    df = pd.DataFrame({'datetime': dates, 'cumulative': cumulative})
    df['days_since_start'] = (df['datetime'] - df['datetime'].min()).dt.total_seconds() / 86400

    # 使用滾動窗口計算斜率
    window = max(10, len(df) // 20)
    df['slope'] = df['cumulative'].diff() / df['days_since_start'].diff()
    df['rolling_slope'] = df['slope'].rolling(window=window, min_periods=1).mean()

    # 找出斜率突變點
    if len(df) > window:
        slope_change = df['rolling_slope'].diff().abs()
        threshold = slope_change.quantile(0.95)
        change_points = df[slope_change > threshold]
    else:
        change_points = pd.DataFrame()

    # 繪圖
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), height_ratios=[2, 1])

    # 累積曲線
    ax1.plot(df['datetime'], df['cumulative'], 'b-', linewidth=2)

    # 標記斜率變化點
    if not change_points.empty:
        ax1.scatter(change_points['datetime'], change_points['cumulative'],
                   color='red', s=100, zorder=5, label='斜率突變點')

    ax1.set_xlabel('日期')
    ax1.set_ylabel('累積評論數')
    ax1.set_title(f'{store_name} - 累積評論數曲線')
    ax1.grid(True, alpha=0.3)
    if not change_points.empty:
        ax1.legend()

    # 斜率變化圖
    ax2.plot(df['datetime'], df['rolling_slope'], 'g-', linewidth=1)
    ax2.axhline(y=df['rolling_slope'].mean(), color='r', linestyle='--', alpha=0.5, label='平均斜率')
    ax2.set_xlabel('日期')
    ax2.set_ylabel('評論增長率（則/天）')
    ax2.set_title('評論增長率變化')
    ax2.grid(True, alpha=0.3)
    ax2.legend()

    # 格式化日期
    for ax in [ax1, ax2]:
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
        ax.xaxis.set_major_locator(mdates.AutoDateLocator())

    plt.xticks(rotation=45)
    plt.tight_layout()

    # 儲存圖表
    safe_name = store_name.replace('/', '_').replace('\\', '_').replace(':', '_')[:30]
    fig.savefig(output_path / f'{safe_name}_cumulative.png', dpi=150, bbox_inches='tight')
    plt.close(fig)

def generate_analysis_report(store_name: str, reviews: list, suspicion: dict,
                            anomalies_df: pd.DataFrame, heatmap_matrix: np.ndarray) -> str:
    """產生分析報告"""
    report = []
    report.append(f"\n{'='*60}")
    report.append(f"店家名稱：{store_name}")
    report.append(f"{'='*60}")

    # 基本統計
    report.append(f"\n[基本統計]")
    report.append(f"  總評論數：{len(reviews)}")

    if reviews:
        date_range = f"{reviews[0]['date']} ~ {reviews[-1]['date']}"
        report.append(f"  評論時間範圍：{date_range}")

        avg_rating = sum(r['stars'] for r in reviews) / len(reviews)
        report.append(f"  平均評分：{avg_rating:.2f}")

        five_star_ratio = sum(1 for r in reviews if r['stars'] == 5) / len(reviews) * 100
        report.append(f"  五星評論比例：{five_star_ratio:.1f}%")

    # 時間分佈
    report.append(f"\n[時間分佈分析]")

    if heatmap_matrix is not None:
        # 找出最熱門時段
        max_idx = np.unravel_index(np.argmax(heatmap_matrix), heatmap_matrix.shape)
        weekday_names = ['週一', '週二', '週三', '週四', '週五', '週六', '週日']
        report.append(f"  最熱門時段：{weekday_names[max_idx[1]]} {max_idx[0]:02d}:00 ({int(heatmap_matrix[max_idx])} 則)")

        # 週末 vs 平日
        weekday_total = heatmap_matrix[:, :5].sum()
        weekend_total = heatmap_matrix[:, 5:].sum()
        report.append(f"  平日評論：{int(weekday_total)} 則 | 週末評論：{int(weekend_total)} 則")

    # 異常偵測結果
    report.append(f"\n[異常偵測結果]")

    if anomalies_df is not None and not anomalies_df.empty:
        anomaly_count = anomalies_df['is_anomaly'].sum()
        report.append(f"  偵測到異常高峰日：{anomaly_count} 天")

        if anomaly_count > 0:
            anomaly_dates = anomalies_df[anomalies_df['is_anomaly']].index
            for date in list(anomaly_dates)[:5]:
                count = int(anomalies_df.loc[date, 'count'])
                report.append(f"    - {date.strftime('%Y-%m-%d')}：{count} 則評論")

    # 凌晨評論
    late_night = detect_late_night_reviews(reviews)
    report.append(f"  凌晨評論（0-6時）：{late_night['count']} 則 ({late_night['percentage']:.1f}%)")
    if late_night['suspicious']:
        report.append(f"    [警告] 凌晨評論比例偏高！")

    # 規律間隔
    interval_result = detect_regular_intervals(reviews)
    if interval_result['detected']:
        report.append(f"  [警告] 偵測到規律性評論模式（規律性分數：{interval_result['score']:.2f}）")

    # 刷評嫌疑分數
    report.append(f"\n[刷評嫌疑分析]")
    report.append(f"  總嫌疑分數：{suspicion['total_score']} / 100")
    report.append(f"  風險等級：{suspicion['risk_level'].upper()}")
    report.append(f"  分項得分：")
    for key, value in suspicion['breakdown'].items():
        label_map = {
            'burst_score': '爆發偵測',
            'interval_score': '規律間隔',
            'late_night_score': '凌晨異常',
            'new_account_score': '新帳號',
            'five_star_only_score': '純五星'
        }
        report.append(f"    - {label_map.get(key, key)}：{value}")

    # 結論
    report.append(f"\n[結論]")
    if suspicion['risk_level'] == 'high':
        report.append("  此店家存在明顯的刷評嫌疑，建議進一步調查。")
    elif suspicion['risk_level'] == 'medium':
        report.append("  此店家存在部分可疑模式，需要持續觀察。")
    else:
        report.append("  此店家的評論模式看起來正常。")

    return '\n'.join(report)

def analyze_store(store_name: str, reviews: list, output_path: Path) -> dict:
    """對單一店家進行完整分析"""
    print(f"\n正在分析：{store_name}")
    print(f"  評論數量：{len(reviews)}")

    if len(reviews) < 5:
        print(f"  跳過：評論數量不足")
        return None

    # 1. 繪製每日趨勢圖並偵測異常
    anomalies_df = plot_daily_trend(store_name, reviews, output_path)
    print(f"  已產生每日趨勢圖")

    # 2. 繪製熱力圖
    heatmap_matrix = plot_heatmap(store_name, reviews, output_path)
    print(f"  已產生熱力圖")

    # 3. 繪製累積曲線
    plot_cumulative_curve(store_name, reviews, output_path)
    print(f"  已產生累積曲線圖")

    # 4. 計算刷評嫌疑分數
    suspicion = calculate_suspicion_score(store_name, reviews, anomalies_df)
    print(f"  刷評嫌疑分數：{suspicion['total_score']} ({suspicion['risk_level']})")

    # 5. 產生報告
    report = generate_analysis_report(store_name, reviews, suspicion, anomalies_df, heatmap_matrix)

    return {
        'store_name': store_name,
        'review_count': len(reviews),
        'suspicion': suspicion,
        'report': report
    }

def create_summary_dashboard(all_results: list, output_path: Path):
    """建立總覽儀表板"""
    if not all_results:
        return

    fig, axes = plt.subplots(2, 2, figsize=(16, 12))

    # 1. 各店家評論數
    ax1 = axes[0, 0]
    stores = [r['store_name'][:15] for r in all_results]
    counts = [r['review_count'] for r in all_results]
    colors = ['red' if r['suspicion']['risk_level'] == 'high' else
              'orange' if r['suspicion']['risk_level'] == 'medium' else 'green'
              for r in all_results]

    bars = ax1.barh(stores, counts, color=colors)
    ax1.set_xlabel('評論數')
    ax1.set_title('各店家評論數量（顏色代表風險等級）')
    ax1.invert_yaxis()

    # 2. 刷評嫌疑分數分佈
    ax2 = axes[0, 1]
    scores = [r['suspicion']['total_score'] for r in all_results]
    ax2.hist(scores, bins=20, edgecolor='black', alpha=0.7)
    ax2.axvline(x=25, color='orange', linestyle='--', label='中風險閾值')
    ax2.axvline(x=50, color='red', linestyle='--', label='高風險閾值')
    ax2.set_xlabel('刷評嫌疑分數')
    ax2.set_ylabel('店家數量')
    ax2.set_title('刷評嫌疑分數分佈')
    ax2.legend()

    # 3. 各項指標雷達圖（平均）
    ax3 = axes[1, 0]
    categories = ['爆發偵測', '規律間隔', '凌晨異常', '新帳號', '純五星']
    avg_scores = []
    for cat in ['burst_score', 'interval_score', 'late_night_score', 'new_account_score', 'five_star_only_score']:
        avg = np.mean([r['suspicion']['breakdown'][cat] for r in all_results])
        avg_scores.append(avg)

    ax3.bar(categories, avg_scores, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'])
    ax3.set_ylabel('平均分數')
    ax3.set_title('各項嫌疑指標平均分數')
    plt.setp(ax3.xaxis.get_majorticklabels(), rotation=45, ha='right')

    # 4. 風險等級分佈
    ax4 = axes[1, 1]
    risk_counts = Counter(r['suspicion']['risk_level'] for r in all_results)
    labels = ['低風險', '中風險', '高風險']
    sizes = [risk_counts.get('low', 0), risk_counts.get('medium', 0), risk_counts.get('high', 0)]
    colors_pie = ['#2ECC71', '#F39C12', '#E74C3C']
    explode = (0, 0.05, 0.1)

    ax4.pie(sizes, explode=explode, labels=labels, colors=colors_pie, autopct='%1.1f%%',
            shadow=True, startangle=90)
    ax4.set_title('店家風險等級分佈')

    plt.tight_layout()
    fig.savefig(output_path / 'summary_dashboard.png', dpi=150, bbox_inches='tight')
    plt.close(fig)
    print(f"\n已產生總覽儀表板：{output_path / 'summary_dashboard.png'}")

def main():
    """主程式"""
    print("="*60)
    print("時間維度視覺化分析")
    print("="*60)

    # 尋找資料檔案
    data_dir = Path(__file__).parent
    review_files = list(data_dir.glob('step2_reviews_*.json'))
    lowest_files = list(data_dir.glob('step2b_lowest_reviews_*.json'))

    all_files = review_files + lowest_files

    if not all_files:
        print("錯誤：找不到評論資料檔案")
        return

    print(f"\n找到 {len(all_files)} 個資料檔案")

    # 載入並合併所有資料
    all_stores = {}

    for file_path in all_files:
        print(f"  載入：{file_path.name}")
        data = load_reviews_data(file_path)
        stores = extract_reviews_with_dates(data)

        for store_name, reviews in stores.items():
            if store_name in all_stores:
                # 合併並去重
                existing_ids = {r['datetime'] for r in all_stores[store_name]}
                new_reviews = [r for r in reviews if r['datetime'] not in existing_ids]
                all_stores[store_name].extend(new_reviews)
                all_stores[store_name].sort(key=lambda x: x['datetime'])
            else:
                all_stores[store_name] = reviews

    print(f"\n共載入 {len(all_stores)} 間店家的評論資料")

    # 分析每間店家
    all_results = []
    full_report = []

    for store_name, reviews in all_stores.items():
        result = analyze_store(store_name, reviews, OUTPUT_DIR)
        if result:
            all_results.append(result)
            full_report.append(result['report'])

    # 建立總覽儀表板
    if all_results:
        create_summary_dashboard(all_results, OUTPUT_DIR)

    # 輸出完整報告
    print("\n" + "="*60)
    print("分析報告")
    print("="*60)

    for report in full_report:
        print(report)

    # 輸出高風險店家摘要
    high_risk = [r for r in all_results if r['suspicion']['risk_level'] == 'high']
    medium_risk = [r for r in all_results if r['suspicion']['risk_level'] == 'medium']

    print("\n" + "="*60)
    print("風險摘要")
    print("="*60)
    print(f"\n高風險店家（{len(high_risk)} 間）：")
    for r in sorted(high_risk, key=lambda x: x['suspicion']['total_score'], reverse=True):
        print(f"  - {r['store_name']}: {r['suspicion']['total_score']} 分")

    print(f"\n中風險店家（{len(medium_risk)} 間）：")
    for r in sorted(medium_risk, key=lambda x: x['suspicion']['total_score'], reverse=True):
        print(f"  - {r['store_name']}: {r['suspicion']['total_score']} 分")

    # 儲存報告到檔案
    report_path = OUTPUT_DIR / 'analysis_report.txt'
    with open(report_path, 'w', encoding='utf-8') as f:
        f.write("時間維度視覺化分析報告\n")
        f.write("="*60 + "\n")
        f.write(f"分析時間：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"分析店家數：{len(all_results)}\n\n")

        for report in full_report:
            f.write(report + "\n")

        f.write("\n" + "="*60 + "\n")
        f.write("風險摘要\n")
        f.write("="*60 + "\n")
        f.write(f"\n高風險店家（{len(high_risk)} 間）：\n")
        for r in sorted(high_risk, key=lambda x: x['suspicion']['total_score'], reverse=True):
            f.write(f"  - {r['store_name']}: {r['suspicion']['total_score']} 分\n")

        f.write(f"\n中風險店家（{len(medium_risk)} 間）：\n")
        for r in sorted(medium_risk, key=lambda x: x['suspicion']['total_score'], reverse=True):
            f.write(f"  - {r['store_name']}: {r['suspicion']['total_score']} 分\n")

    print(f"\n報告已儲存至：{report_path}")
    print(f"圖表已儲存至：{OUTPUT_DIR}")
    print("\n分析完成！")

if __name__ == '__main__':
    main()


File created successfully at: C:\Users\User\Documents\GitHub\apify-scraper-practice\time_analysis.py


現在執行腳本來測試。


Bash

cd "C:\Users\User\Documents\GitHub\apify-scraper-practice" && python -X utf8 time_analysis.py

執行時間維度分析腳本


Output:
高風險店家（0 間）：
中風險店家（0 間）：
報告已儲存至：C:\Users\User\Documents\GitHub\apify-scraper-practice\output\analysis_report.txt
圖表已儲存至：C:\Users\User\Documents\GitHub\apify-scraper-practice\output
分析完成！


============================================================
時間維度視覺化分析
============================================================

找到 3 個資料檔案
  載入：step2_reviews_20260129_143208.json
  載入：step2_reviews_20260129_143615.json
  載入：step2b_lowest_reviews_20260129_144321.json

共載入 5 間店家的評論資料

正在分析：今日魚市-南京店-原丼賞和食
  評論數量：590
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：22.28 (low)

正在分析：義大利米蘭手工窯烤披薩 台北中山店
  評論數量：525
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：20.95 (low)

正在分析：曹料理
  評論數量：481
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：20.49 (low)

正在分析：香色 XIANG SE
  評論數量：571
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：20.74 (low)

正在分析：TWATUTIAN Coffee & Co.
  評論數量：207
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：20.46 (low)

已產生總覽儀表板：C:\Users\User\Documents\GitHub\apify-scraper-practice\output\summary_dashboard.png

============================================================
分析報告
============================================================

============================================================
店家名稱：今日魚市-南京店-原丼賞和食
============================================================

[基本統計]
  總評論數：590
  評論時間範圍：2016-07-24 ~ 2026-01-29
  平均評分：2.99
  五星評論比例：46.9%

[時間分佈分析]
  最熱門時段：週六 05:00 (25 則)
  平日評論：355 則 | 週末評論：235 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：186 則 (31.5%)
    [警告] 凌晨評論比例偏高！

[刷評嫌疑分析]
  總嫌疑分數：22.28 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：1.32
    - 凌晨異常：20
    - 新帳號：0.97
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：義大利米蘭手工窯烤披薩 台北中山店
============================================================

[基本統計]
  總評論數：525
  評論時間範圍：2015-11-08 ~ 2026-01-28
  平均評分：3.14
  五星評論比例：34.1%

[時間分佈分析]
  最熱門時段：週日 07:00 (11 則)
  平日評論：323 則 | 週末評論：202 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：111 則 (21.1%)
    [警告] 凌晨評論比例偏高！

[刷評嫌疑分析]
  總嫌疑分數：20.95 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.38
    - 凌晨異常：20
    - 新帳號：0.57
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：曹料理
============================================================

[基本統計]
  總評論數：481
  評論時間範圍：2017-07-16 ~ 2026-01-28
  平均評分：4.25
  五星評論比例：51.8%

[時間分佈分析]
  最熱門時段：週日 10:00 (12 則)
  平日評論：292 則 | 週末評論：189 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：115 則 (23.9%)
    [警告] 凌晨評論比例偏高！

[刷評嫌疑分析]
  總嫌疑分數：20.49 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.21
    - 凌晨異常：20
    - 新帳號：0.28
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：香色 XIANG SE
============================================================

[基本統計]
  總評論數：571
  評論時間範圍：2015-10-07 ~ 2026-01-26
  平均評分：3.42
  五星評論比例：40.5%

[時間分佈分析]
  最熱門時段：週日 11:00 (13 則)
  平日評論：365 則 | 週末評論：206 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：138 則 (24.2%)
    [警告] 凌晨評論比例偏高！

[刷評嫌疑分析]
  總嫌疑分數：20.74 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.26
    - 凌晨異常：20
    - 新帳號：0.47
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：TWATUTIAN Coffee & Co.
============================================================

[基本統計]
  總評論數：207
  評論時間範圍：2023-12-14 ~ 2026-01-26
  平均評分：4.54
  五星評論比例：73.4%

[時間分佈分析]
  最熱門時段：週二 08:00 (6 則)
  平日評論：138 則 | 週末評論：69 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：50 則 (24.2%)
    [警告] 凌晨評論比例偏高！

[刷評嫌疑分析]
  總嫌疑分數：20.46 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.24
    - 凌晨異常：20
    - 新帳號：0.22
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
風險摘要
============================================================

高風險店家（0 間）：

中風險店家（0 間）：

報告已儲存至：C:\Users\User\Documents\GitHub\apify-scraper-practice\output\analysis_report.txt
圖表已儲存至：C:\Users\User\Documents\GitHub\apify-scraper-practice\output

分析完成！


分析成功完成了。讓我確認輸出的檔案。


列出 output 資料夾內容


total 2244
drwxr-xr-x 1 User 197121      0 Jan 29 15:46 .
drwxr-xr-x 1 User 197121      0 Jan 29 15:46 ..
-rw-r--r-- 1 User 197121   4637 Jan 29 15:46 analysis_report.txt
drwxr-xr-x 1 User 197121      0 Jan 29 15:45 dd_reports
-rw-r--r-- 1 User 197121 179618 Jan 29 15:46 summary_dashboard.png
-rw-r--r-- 1 User 197121 120041 Jan 29 15:46 TWATUTIAN Coffee & Co._cumulative.png
-rw-r--r-- 1 User 197121 223117 Jan 29 15:46 TWATUTIAN Coffee & Co._daily_trend.png
-rw-r--r-- 1 User 197121 114590 Jan 29 15:46 TWATUTIAN Coffee & Co._heatmap.png
-rw-r--r-- 1 User 197121 121200 Jan 29 15:46 今日魚市-南京店-原丼賞和食_cumulative.png
-rw-r--r-- 1 User 197121 111078 Jan 29 15:46 今日魚市-南京店-原丼賞和食_daily_trend.png
-rw-r--r-- 1 User 197121 129472 Jan 29 15:46 今日魚市-南京店-原丼賞和食_heatmap.png
-rw-r--r-- 1 User 197121 120576 Jan 29 15:46 曹料理_cumulative.png
-rw-r--r-- 1 User 197121 155128 Jan 29 15:46 曹料理_daily_trend.png
-rw-r--r-- 1 User 197121 122153 Jan 29 15:46 曹料理_heatmap.png
-rw-r--r-- 1 User 197121 124203 Jan 29 15:46 義大利米蘭手工窯烤披薩 台北中山店_cumulative.png
-rw-r--r-- 1 User 197121 147108 Jan 29 15:46 義大利米蘭手工窯烤披薩 台北中山店_daily_trend.png
-rw-r--r-- 1 User 197121 130578 Jan 29 15:46 義大利米蘭手工窯烤披薩 台北中山店_heatmap.png
-rw-r--r-- 1 User 197121 117928 Jan 29 15:46 香色 XIANG SE_cumulative.png
-rw-r--r-- 1 User 197121 196661 Jan 29 15:46 香色 XIANG SE_daily_trend.png
-rw-r--r-- 1 User 197121 123996 Jan 29 15:46 香色 XIANG SE_heatmap.png


所有圖表都已成功產生。我注意到凌晨評論比例偏高的情況，這其實是因為評論時間使用的是 UTC 時區，而台灣是 UTC+8。讓我修正這個問題，將時間轉換為台灣本地時間。


Edit

import json
import os
from datetime import datetime, timedelta
from collections import defaultdict, Counter
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
from pathlib import Path

import json
import os
from datetime import datetime, timedelta, timezone
from collections import defaultdict, Counter
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
from pathlib import Path

# 台灣時區 (UTC+8)
TW_TIMEZONE = timezone(timedelta(hours=8))


The file C:\Users\User\Documents\GitHub\apify-scraper-practice\time_analysis.py has been updated successfully.


        if date_str:
                try:
                    # 解析 ISO 格式日期
                    dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
                    review_list.append({
                        'datetime': dt,
                        'date': dt.date(),
                        'hour': dt.hour,
                        'weekday': dt.weekday(),  # 0=Monday, 6=Sunday

        if date_str:
                try:
                    # 解析 ISO 格式日期並轉換為台灣時區
                    dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
                    dt_tw = dt.astimezone(TW_TIMEZONE)  # 轉換為台灣時間
                    review_list.append({
                        'datetime': dt_tw,
                        'date': dt_tw.date(),
                        'hour': dt_tw.hour,
                        'weekday': dt_tw.weekday(),  # 0=Monday, 6=Sunday


現在重新執行腳本。


Bash

cd "C:\Users\User\Documents\GitHub\apify-scraper-practice" && python -X utf8 time_analysis.py

重新執行時間維度分析腳本（已修正時區）


============================================================
時間維度視覺化分析
============================================================

找到 3 個資料檔案
  載入：step2_reviews_20260129_143208.json
  載入：step2_reviews_20260129_143615.json
  載入：step2b_lowest_reviews_20260129_144321.json

共載入 5 間店家的評論資料

正在分析：今日魚市-南京店-原丼賞和食
  評論數量：590
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：2.28 (low)

正在分析：義大利米蘭手工窯烤披薩 台北中山店
  評論數量：525
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：0.95 (low)

正在分析：曹料理
  評論數量：481
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：0.49 (low)

正在分析：香色 XIANG SE
  評論數量：571
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：0.74 (low)

正在分析：TWATUTIAN Coffee & Co.
  評論數量：207
  已產生每日趨勢圖
  已產生熱力圖
  已產生累積曲線圖
  刷評嫌疑分數：0.46 (low)

已產生總覽儀表板：C:\Users\User\Documents\GitHub\apify-scraper-practice\output\summary_dashboard.png

============================================================
分析報告
============================================================

============================================================
店家名稱：今日魚市-南京店-原丼賞和食
============================================================

[基本統計]
  總評論數：590
  評論時間範圍：2016-07-24 ~ 2026-01-29
  平均評分：2.99
  五星評論比例：46.9%

[時間分佈分析]
  最熱門時段：週六 13:00 (25 則)
  平日評論：349 則 | 週末評論：241 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：29 則 (4.9%)

[刷評嫌疑分析]
  總嫌疑分數：2.28 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：1.32
    - 凌晨異常：0
    - 新帳號：0.97
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：義大利米蘭手工窯烤披薩 台北中山店
============================================================

[基本統計]
  總評論數：525
  評論時間範圍：2015-11-08 ~ 2026-01-28
  平均評分：3.14
  五星評論比例：34.1%

[時間分佈分析]
  最熱門時段：週日 15:00 (11 則)
  平日評論：320 則 | 週末評論：205 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：47 則 (9.0%)

[刷評嫌疑分析]
  總嫌疑分數：0.95 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.38
    - 凌晨異常：0
    - 新帳號：0.57
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：曹料理
============================================================

[基本統計]
  總評論數：481
  評論時間範圍：2017-07-16 ~ 2026-01-28
  平均評分：4.25
  五星評論比例：51.8%

[時間分佈分析]
  最熱門時段：週日 18:00 (12 則)
  平日評論：296 則 | 週末評論：185 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：37 則 (7.7%)

[刷評嫌疑分析]
  總嫌疑分數：0.49 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.21
    - 凌晨異常：0
    - 新帳號：0.28
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：香色 XIANG SE
============================================================

[基本統計]
  總評論數：571
  評論時間範圍：2015-10-07 ~ 2026-01-26
  平均評分：3.42
  五星評論比例：40.5%

[時間分佈分析]
  最熱門時段：週日 19:00 (13 則)
  平日評論：363 則 | 週末評論：208 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：63 則 (11.0%)

[刷評嫌疑分析]
  總嫌疑分數：0.74 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.26
    - 凌晨異常：0
    - 新帳號：0.47
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
店家名稱：TWATUTIAN Coffee & Co.
============================================================

[基本統計]
  總評論數：207
  評論時間範圍：2023-12-14 ~ 2026-01-26
  平均評分：4.54
  五星評論比例：73.4%

[時間分佈分析]
  最熱門時段：週二 16:00 (6 則)
  平日評論：138 則 | 週末評論：69 則

[異常偵測結果]
  偵測到異常高峰日：0 天
  凌晨評論（0-6時）：15 則 (7.2%)

[刷評嫌疑分析]
  總嫌疑分數：0.46 / 100
  風險等級：LOW
  分項得分：
    - 爆發偵測：0.0
    - 規律間隔：0.24
    - 凌晨異常：0
    - 新帳號：0.22
    - 純五星：0

[結論]
  此店家的評論模式看起來正常。

============================================================
風險摘要
============================================================

高風險店家（0 間）：

中風險店家（0 間）：

報告已儲存至：C:\Users\User\Documents\GitHub\apify-scraper-practice\output\analysis_report.txt
圖表已儲存至：C:\Users\User\Documents\GitHub\apify-scraper-practice\output

分析完成！


修正時區後結果正確了。現在凌晨評論比例從 20-30% 降到了正常的 5-11%，最熱門時段也顯示為合理的用餐時間（如週六 13:00、週日 15:00、18:00、19:00 等）。

讓我查看一下產生的圖表。


圖表已成功產生。讓我也查看一下每日趨勢圖和累積曲線圖。


所有圖表都已成功產生。現在讓我讀取最終的分析報告。


     1→時間維度視覺化分析報告
     2→============================================================
     3→分析時間：2026-01-29 15:47:20
     4→分析店家數：5
     5→
     6→
     7→============================================================
     8→店家名稱：今日魚市-南京店-原丼賞和食
     9→============================================================
    10→
    11→[基本統計]
    12→  總評論數：590
    13→  評論時間範圍：2016-07-24 ~ 2026-01-29
    14→  平均評分：2.99
    15→  五星評論比例：46.9%
    16→
    17→[時間分佈分析]
    18→  最熱門時段：週六 13:00 (25 則)
    19→  平日評論：349 則 | 週末評論：241 則
    20→
    21→[異常偵測結果]
    22→  偵測到異常高峰日：0 天
    23→  凌晨評論（0-6時）：29 則 (4.9%)
    24→
    25→[刷評嫌疑分析]
    26→  總嫌疑分數：2.28 / 100
    27→  風險等級：LOW
    28→  分項得分：
    29→    - 爆發偵測：0.0
    30→    - 規律間隔：1.32
    31→    - 凌晨異常：0
    32→    - 新帳號：0.97
    33→    - 純五星：0
    34→
    35→[結論]
    36→  此店家的評論模式看起來正常。
    37→
    38→============================================================
    39→店家名稱：義大利米蘭手工窯烤披薩 台北中山店
    40→============================================================
    41→
    42→[基本統計]
    43→  總評論數：525
    44→  評論時間範圍：2015-11-08 ~ 2026-01-28
    45→  平均評分：3.14
    46→  五星評論比例：34.1%
    47→
    48→[時間分佈分析]
    49→  最熱門時段：週日 15:00 (11 則)
    50→  平日評論：320 則 | 週末評論：205 則
    51→
    52→[異常偵測結果]
    53→  偵測到異常高峰日：0 天
    54→  凌晨評論（0-6時）：47 則 (9.0%)
    55→
    56→[刷評嫌疑分析]
    57→  總嫌疑分數：0.95 / 100
    58→  風險等級：LOW
    59→  分項得分：
    60→    - 爆發偵測：0.0
    61→    - 規律間隔：0.38
    62→    - 凌晨異常：0
    63→    - 新帳號：0.57
    64→    - 純五星：0
    65→
    66→[結論]
    67→  此店家的評論模式看起來正常。
    68→
    69→============================================================
    70→店家名稱：曹料理
    71→============================================================
    72→
    73→[基本統計]
    74→  總評論數：481
    75→  評論時間範圍：2017-07-16 ~ 2026-01-28
    76→  平均評分：4.25
    77→  五星評論比例：51.8%
    78→
    79→[時間分佈分析]
    80→  最熱門時段：週日 18:00 (12 則)
    81→  平日評論：296 則 | 週末評論：185 則
    82→
    83→[異常偵測結果]
    84→  偵測到異常高峰日：0 天
    85→  凌晨評論（0-6時）：37 則 (7.7%)
    86→
    87→[刷評嫌疑分析]
    88→  總嫌疑分數：0.49 / 100
    89→  風險等級：LOW
    90→  分項得分：
    91→    - 爆發偵測：0.0
    92→    - 規律間隔：0.21
    93→    - 凌晨異常：0
    94→    - 新帳號：0.28
    95→    - 純五星：0
    96→
    97→[結論]
    98→  此店家的評論模式看起來正常。
    99→
   100→============================================================
   101→店家名稱：香色 XIANG SE
   102→============================================================
   103→
   104→[基本統計]
   105→  總評論數：571
   106→  評論時間範圍：2015-10-07 ~ 2026-01-26
   107→  平均評分：3.42
   108→  五星評論比例：40.5%
   109→
   110→[時間分佈分析]
   111→  最熱門時段：週日 19:00 (13 則)
   112→  平日評論：363 則 | 週末評論：208 則
   113→
   114→[異常偵測結果]
   115→  偵測到異常高峰日：0 天
   116→  凌晨評論（0-6時）：63 則 (11.0%)
   117→
   118→[刷評嫌疑分析]
   119→  總嫌疑分數：0.74 / 100
   120→  風險等級：LOW
   121→  分項得分：
   122→    - 爆發偵測：0.0
   123→    - 規律間隔：0.26
   124→    - 凌晨異常：0
   125→    - 新帳號：0.47
   126→    - 純五星：0
   127→
   128→[結論]
   129→  此店家的評論模式看起來正常。
   130→
   131→============================================================
   132→店家名稱：TWATUTIAN Coffee & Co.
   133→============================================================
   134→
   135→[基本統計]
   136→  總評論數：207
   137→  評論時間範圍：2023-12-14 ~ 2026-01-26
   138→  平均評分：4.54
   139→  五星評論比例：73.4%
   140→
   141→[時間分佈分析]
   142→  最熱門時段：週二 16:00 (6 則)
   143→  平日評論：138 則 | 週末評論：69 則
   144→
   145→[異常偵測結果]
   146→  偵測到異常高峰日：0 天
   147→  凌晨評論（0-6時）：15 則 (7.2%)
   148→
   149→[刷評嫌疑分析]
   150→  總嫌疑分數：0.46 / 100
   151→  風險等級：LOW
   152→  分項得分：
   153→    - 爆發偵測：0.0
   154→    - 規律間隔：0.24
   155→    - 凌晨異常：0
   156→    - 新帳號：0.22
   157→    - 純五星：0
   158→
   159→[結論]
   160→  此店家的評論模式看起來正常。
   161→
   162→============================================================
   163→風險摘要
   164→============================================================
   165→
   166→高風險店家（0 間）：
   167→
   168→中風險店家（0 間）：
   169→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


已完成所有時間維度視覺化分析的實作。以下是完整的總結：

---

## 完成摘要

### 建立的檔案

**主程式**: `C:\Users\User\Documents\GitHub\apify-scraper-practice\time_analysis.py`

### 實作功能

1. **時間軸圖表（每日評論數趨勢圖）**
   - 藍色折線顯示每日評論數
   - 綠色虛線顯示 7 日滾動平均
   - 紅色圓點標記異常高峰日（超過滾動平均 + 3 倍標準差）

2. **熱力圖**
   - 24 小時 x 7 天的評論分佈矩陣
   - 使用黃橙紅色階表示評論密集度
   - 可清楚看出評論最集中的時段（如週六 13:00、週日 18:00 等）

3. **累積曲線**
   - 上圖：累積評論數隨時間變化
   - 下圖：評論增長率（則/天）
   - 紅色圓點標記斜率突然變化的點

4. **刷評嫌疑分析**
   - 爆發偵測（滾動平均 + 3σ）
   - 規律間隔偵測（找出常見的評論時間間隔）
   - 凌晨異常偵測（0:00-6:00 評論比例）
   - 新帳號比例分析
   - 純五星評論比例分析
   - 綜合計算刷評嫌疑分數（0-100 分）

5. **輸出檔案**（位於 `output/` 資料夾）
   - `summary_dashboard.png` - 總覽儀表板
   - `{店家名}_daily_trend.png` - 每日趨勢圖
   - `{店家名}_heatmap.png` - 時段熱力圖
   - `{店家名}_cumulative.png` - 累積曲線圖
   - `analysis_report.txt` - 完整文字報告

### 分析結果摘要

| 店家名稱 | 評論數 | 時間範圍 | 平均評分 | 刷評嫌疑分數 | 風險等級 |
|---------|-------|---------|---------|------------|---------|
| 今日魚市-南京店 | 590 | 2016-2026 | 2.99 | 2.28 | 低 |
| 義大利米蘭手工窯烤披薩 | 525 | 2015-2026 | 3.14 | 0.95 | 低 |
| 曹料理 | 481 | 2017-2026 | 4.25 | 0.49 | 低 |
| 香色 XIANG SE | 571 | 2015-2026 | 3.42 | 0.74 | 低 |
| TWATUTIAN Coffee | 207 | 2023-2026 | 4.54 | 0.46 | 低 |

### 技術特點

- 已處理 UTC 時區轉換為台灣本地時間（UTC+8）
- 腳本開頭加入 `sys.stdout.reconfigure(encoding='utf-8')` 處理 Windows 編碼問題
- 使用 matplotlib 和 seaborn 繪圖
- 設定中文字體支援（Microsoft JhengHei）
- 對每間店家都執行獨立分析並產生專屬圖表