From ecb5ac1198f23554e5f0cc97b2f946e3b0b8225e Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期一, 28 九月 2026 15:20:27 +0800
Subject: [PATCH] docs(差异清单): 新增汇总大表差异比对工具 + 2026-08 差异明细(生成件 vs 手工定稿,1401 条按类归档);HANDOFF 记录结论并更正 2020 累计列误判

---
 docs/工具/持续在规企业累计运距生成.py |  305 ++++++++++++++++++++++++++++++++------------------
 1 files changed, 196 insertions(+), 109 deletions(-)

diff --git "a/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py" "b/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py"
index 6eddd68..e7171cf 100644
--- "a/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py"
+++ "b/docs/\345\267\245\345\205\267/\346\214\201\347\273\255\345\234\250\350\247\204\344\274\201\344\270\232\347\264\257\350\256\241\350\277\220\350\267\235\347\224\237\346\210\220.py"
@@ -1,13 +1,14 @@
 # -*- coding: utf-8 -*-
-"""璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮 鍙拌处鐢熸垚
+"""璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮 鍙拌处鐢熸垚锛坴2锛氬悓姣斿熀鏈熸敼涓哄畼鏂瑰叏鍙e緞锛�
 
 鏁版嵁婧愶細traffic_audit.h2032_enterprise_monthly锛堛�婇亾璺揣鐗╄繍杈撴湀搴︾敓浜ф儏鍐点�婬203-2 浼佷笟鏈堟姤锛�
         瀵煎嚭浠� _tmp_persist_scope.tsv锛堜紒涓氱骇绱锛夈�乢tmp_persist_scope_monthly.tsv锛堜紒涓氱骇鍒嗘湀锛�
-鍙e緞锛�
-  路 鎸佺画鍦ㄨ浼佷笟 = 2026 骞� 1-8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ユ湀鎶ョ殑浼佷笟锛�
+鍙e緞锛坴2 淇锛夛細
+  路 鎸佺画鍦ㄨ浼佷笟 = 2026 骞� 1-8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ユ湀鎶ョ殑浼佷笟锛�636 瀹讹紝鐢ㄦ埛 2026-09-18 纭锛夛紱
   路 绱骞冲潎杩愯窛锛堝叕閲岋級= 绱璐х墿鍛ㄨ浆閲� 梅 绱璐ц繍閲忥紙鍔犳潈骞冲潎锛夛紱
-  路 鍚屾瘮 = 2026 骞� 1-8 鏈堢疮璁″钩鍧囪繍璺� 梅 2025 骞� 1-8 鏈堢疮璁″钩鍧囪繍璺� 鈭� 1锛�
-    涓轰繚璇佸彲姣旓紝鍙湪涓ゅ勾鍧囨寔缁湪瑙勭殑浼佷笟涓婅绠椼��
+  路 鍚屾瘮 = 2026 骞� 1-8 鏈堢疮璁″�� 梅 2025 骞� 1-8 鏈堢疮璁″�� 鈭� 1锛�**鍩烘湡涓� 2025 骞� 1-8 鏈堝叏閮ㄨ涓婁紒涓氫笂鎶ユ暟**
+    锛堝叏鐪� 664 瀹讹級锛屼笌姹囨�昏〃銆岃揣杩愩�嶉〉 T 鍒� `=S/SUM(V7,X7,鈥�,AJ7)-1` 鍚屼竴鍙e緞锛�
+    v1 璇敤銆屼袱骞村潎鎸佺画鍦ㄨ鐨� 545 瀹躲�嶄綔鍩烘湡锛屽鑷村悓姣斿亸灏忥紝宸蹭慨姝c��
 """
 import io, os
 from openpyxl import Workbook
@@ -19,8 +20,12 @@
 OUT_DIR = r'D:\鏂囨。\ChatGPT\traffic-audit\outputs'
 OUT = os.path.join(OUT_DIR, '璐ц繍鏈堟姤_鎸佺画鍦ㄨ浼佷笟_鍒嗗競宸�1-8鏈堢疮璁¤繍璺濆強鍚屾瘮_2026-09-18.xlsx')
 
-PROV_VOL_PUB = 31399.82      # 涓囧惃锛屽凡鍙戝竷銆�2026骞�8鏈堣涓婅揣鐗╁懆杞噺涓庡钩鍧囪繍璺濆垎鏋愭姤鍛娿��1-8鏈堢疮璁�
-PROV_TURN_PUB = 4927161.77   # 涓囧惃鍏噷锛屽悓涓�
+PROV = {
+    'vol26': 31399.82, 'turn26': 4927161.77,       # 宸插彂甯冿細2026 骞� 1-8 鏈堢疮璁�
+    'vol25': 33994.45, 'turn25': 5102518.95,       # 姹囨�昏〃銆岃揣杩愩�嶉〉 2025 骞� 1-8 鏈堬紙鍚屾瘮鍩烘湡锛�
+    'vol26_08': 3665.36, 'turn26_08': 570004.44,   # 宸插彂甯冿細2026 骞� 8 鏈堝綋鏈�
+    'yoy_vol': -0.0763, 'yoy_turn': -0.0344,       # 宸插彂甯冿細1-8 鏈堢疮璁″悓姣�
+}
 
 CITY_ORDER = ['姝︽眽甯�', '榛勭煶甯�', '鍗佸牥甯�', '瀹滄槍甯�', '瑗勯槼甯�', '閯傚窞甯�', '鑽嗛棬甯�', '瀛濇劅甯�', '鑽嗗窞甯�',
               '榛勫唸甯�', '鍜稿畞甯�', '闅忓窞甯�', '鎭╂柦宸�', '浠欐甯�', '娼滄睙甯�', '澶╅棬甯�', '绁炲啘鏋舵灄鍖�']
@@ -68,46 +73,64 @@
 
 by_city = {}
 for r in rows:
-    c = by_city.setdefault(r['city'], dict(n=0, n_cmp=0, f26=0.0, t26=0.0, f26c=0.0, t26c=0.0, f25c=0.0, t25c=0.0))
+    c = by_city.setdefault(r['city'], dict(n=0, f26=0.0, t26=0.0))
     c['n'] += 1
     c['f26'] += r['f26']; c['t26'] += r['t26']
-    if r['cmp']:
-        c['n_cmp'] += 1
-        c['f26c'] += r['f26']; c['t26c'] += r['t26']
-        c['f25c'] += r['f25']; c['t25c'] += r['t25']
 
-tot = dict(n=0, n_cmp=0, f26=0.0, t26=0.0, f26c=0.0, t26c=0.0, f25c=0.0, t25c=0.0)
+tot = dict(n=0, f26=0.0, t26=0.0)
 for c in by_city.values():
     for k in tot:
         tot[k] += c[k]
+by_city_cmp = {}
+for r in rows:
+    if not r['cmp']:
+        continue
+    c = by_city_cmp.setdefault(r['city'], dict(n=0, f26c=0.0, t26c=0.0, f25c=0.0, t25c=0.0))
+    c['n'] += 1
+    c['f26c'] += r['f26']; c['t26c'] += r['t26']
+    c['f25c'] += r['f25']; c['t25c'] += r['t25']
+tot_cmp = dict(n=0, f26c=0.0, t26c=0.0, f25c=0.0, t25c=0.0)
+for c in by_city_cmp.values():
+    for k in tot_cmp:
+        tot_cmp[k] += c[k]
 unknown = sorted(k for k in by_city if k.startswith('鏈瘑鍒�'))
-
 persist_names = set(r['en'] for r in rows)
-cmp_names = set(r['en'] for r in rows if r['cmp'])
 
 # ---------- 璇讳紒涓氱骇鍒嗘湀鏁版嵁 ----------
 cust = {}
 with io.open(MONTHLY_SRC, 'r', encoding='utf-8') as f:
     for line in f:
         p = line.rstrip('\r\n').split('\t')
-        if len(p) < 5:
+        if len(p) < 5 or p[0] == 'report_period':
             continue
         per, rc, en, fr, tu = p[0], p[1], p[2], p[3], p[4]
-        if per == 'report_period':
-            continue
         cust.setdefault((per, city_of(rc)), {})[en] = (num(fr), num(tu))
 
 
-def city_month(period, city, names):
+def city_month(period, city, names=None):
     d = cust.get((period, city))
     if not d:
-        return None, 0.0
+        return None, 0.0, 0.0
     fr = tu = 0.0
     for en, v in d.items():
-        if en in names:
+        if names is None or en in names:
             fr += v[0]; tu += v[1]
-    return dist(fr, tu), fr
+    return dist(fr, tu), fr, tu
 
+
+# ---------- 2025 骞� 1-8 鏈堝叏鍙e緞鍩烘湡 ----------
+B25 = {}
+for (per, city), d in cust.items():
+    if not ('2025-01' <= per <= '2025-08'):
+        continue
+    c = B25.setdefault(city, dict(ents=set(), f=0.0, t=0.0))
+    for en, v in d.items():
+        c['ents'].add(en)
+        c['f'] += v[0]; c['t'] += v[1]
+tot25 = dict(ents=set(), f=0.0, t=0.0)
+for c in B25.values():
+    tot25['ents'] |= c['ents']
+    tot25['f'] += c['f']; tot25['t'] += c['t']
 
 # ---------- 鏍峰紡 ----------
 THIN = Side(style='thin', color='BFBFBF')
@@ -118,7 +141,7 @@
 CT = Alignment(horizontal='center', vertical='center', wrap_text=True)
 LT = Alignment(horizontal='left', vertical='center', wrap_text=True)
 RT = Alignment(horizontal='right', vertical='center')
-NF2, NF1 = '#,##0.00', '#,##0.0'
+NF2, NF1, NFP = '#,##0.00', '#,##0.0', '0.00%'
 
 wb = Workbook()
 
@@ -127,7 +150,7 @@
 ws.title = '璇存槑涓庡彛寰�'
 ws.sheet_view.showGridLines = False
 ws.column_dimensions['A'].width = 16
-ws.column_dimensions['B'].width = 104
+ws.column_dimensions['B'].width = 106
 t = ws.cell(1, 1, '璐ц繍鏈堟姤銆屾寔缁湪瑙勪紒涓氥�嶅垎甯傚窞 1-8 鏈堢疮璁¤繍璺濆強鍚屾瘮鎯呭喌琛�')
 t.font = Font(bold=True, size=15, color='FFFFFF')
 t.fill = TITLE_FILL
@@ -138,19 +161,23 @@
 info = [
     ('鏁版嵁鏉ユ簮', '绯荤粺鏁版嵁搴� traffic_audit.h2032_enterprise_monthly銆婇亾璺揣鐗╄繍杈撴湀搴︾敓浜ф儏鍐点�嬶紙H203-2锛変紒涓氭湀鎶ワ紝'
                  '鍙� 2025 骞� 1 鏈堚��2026 骞� 8 鏈堝叏閮ㄤ紒涓氱骇涓婃姤璁板綍銆�'),
-    ('鎸佺画鍦ㄨ浼佷笟', '2026 骞� 1鈥�8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ユ湀鎶ョ殑浼佷笟锛屽叡 %d 瀹讹紙2026 骞村悇鏈堜笂鎶ヤ紒涓氭暟鍧囦负 636 瀹讹紝鍏ㄦ湡涓�鑷达級銆�' % tot['n']),
+    ('鎸佺画鍦ㄨ浼佷笟', '2026 骞� 1鈥�8 鏈堣繛缁� 8 涓湀鍧囦笂鎶ユ湀鎶ョ殑浼佷笟锛屽叡 %d 瀹讹紙2026 骞村悇鏈堜笂鎶ヤ紒涓氭暟鍧囦负 636 瀹讹紝鍏ㄦ湡涓�鑷达紱'
+                 '鍙e緞缁忕敤鎴� 2026-09-18 纭锛夈��' % tot['n']),
     ('绱杩愯窛鍙e緞', '绱骞冲潎杩愯窛锛堝叕閲岋級= 1鈥�8 鏈堢疮璁¤揣鐗╁懆杞噺 梅 1鈥�8 鏈堢疮璁¤揣杩愰噺锛堝姞鏉冨钩鍧囷級銆傛簮琛ㄥ崟浣嶄负鍚ㄣ�佸惃鍏噷锛�'
                      '鏈〃鎶樼畻涓轰竾鍚ㄣ�佷竾鍚ㄥ叕閲岋紝姣斿�间笉鍙樸��'),
-    ('鍚屾瘮鍙e緞', '鍚屾瘮 = 2026 骞� 1鈥�8 鏈堢疮璁″钩鍧囪繍璺� 梅 2025 骞� 1鈥�8 鏈堢疮璁″钩鍧囪繍璺� 鈭� 1銆�'
-                 '涓轰繚璇佸彲姣旓紝鍚屾瘮鍙湪銆屼袱骞村潎鎸佺画鍦ㄨ銆嶇殑 %d 瀹朵紒涓氫笂璁$畻锛堝彟鏈� %d 瀹� 2026 骞存寔缁湪瑙勪紒涓氾紝'
-                 '2025 骞� 1鈥�8 鏈堟棤杩炵画涓婃姤璁板綍锛屼笉鍙備笌鍚屾瘮锛夈��' % (tot['n_cmp'], tot['n'] - tot['n_cmp'])),
+    ('鍚屾瘮鍙e緞', '鍚屾瘮 = 2026 骞� 1鈥�8 鏈堢疮璁″�� 梅 2025 骞� 1鈥�8 鏈堢疮璁″�� 鈭� 1銆�**鍩烘湡涓� 2025 骞� 1鈥�8 鏈堝叏閮ㄨ涓婁紒涓氫笂鎶ユ暟**'
+                 '锛堝叏鐪� %d 瀹讹級锛屼笌姹囨�昏〃銆岃揣杩愩�嶉〉 T 鍒楀叕寮� =S/SUM(V7,X7,鈥�,AJ7)-1 鍚屼竴鍙e緞锛屽彲涓庡彂甯冩姤鍛婄洿鎺ュ榻愶細'
+                 '鍏ㄧ渷绱璐ц繍閲忓悓姣� 鈭�7.63%%銆佸懆杞噺鍚屾瘮 鈭�3.44%%銆佸钩鍧囪繍璺濆悓姣� +4.54%%銆�' % len(tot25['ents'])),
     ('鍏ㄧ渷鍚堣', '涓�寰嬩负 17 涓競宸炰箣鍜岋紙涓嶅惈璺ㄥ競宸為噸鍙犲彛寰勶級銆�'),
-    ('鎶ヨ〃鏈�', '鏈湡绱 2026-01-01鈥�2026-08-31锛涗笂骞村悓鏈� 2025-01-01鈥�2025-08-31銆�'),
-    ('椤电璇存槑', '銆屽垎甯傚窞姹囨�汇�嶆寜甯傚窞姹囨�� 1-8 鏈堢疮璁¤繍璺濅笌鍚屾瘮锛涖�屽垎甯傚窞鍒嗘湀銆嶄负鍚勫競宸� 2026 骞撮�愭湀骞冲潎杩愯窛涓庡悓鏈熷姣旓紱'
-                 '銆屼紒涓氭槑缁嗐�嶄负 636 瀹舵寔缁湪瑙勪紒涓氶�愭埛鏁版嵁锛涖�屽彛寰勬牎楠屻�嶄负鏄庣粏涓庢眹鎬诲璐︺�佷互鍙婁笌宸插彂甯冨叏鐪佹暟鎹殑姣斿銆�'),
+    ('鎶ヨ〃鏈�', '鏈湡绱 2026-01-01鈥�2026-08-31锛涘悓姣斿熀鏈� 2025-01-01鈥�2025-08-31銆�'),
+    ('椤电璇存槑', '銆屽垎甯傚窞姹囨�汇�嶆寜甯傚窞姹囨�� 1-8 鏈堢疮璁¤繍璺濅笌鍚屾瘮锛涖�屽垎甯傚窞鍒嗘湀銆嶄负鍚勫競宸� 2026 骞撮�愭湀骞冲潎杩愯窛锛�'
+                 '銆屼紒涓氭槑缁嗐�嶄负 636 瀹舵寔缁湪瑙勪紒涓氶�愭埛鏁版嵁锛堜紒涓氱骇鍚屾瘮浠呭涓ゅ勾鍧囪繛缁笂鎶ョ殑 545 瀹剁粰鍑猴級锛�'
+                 '銆屽彛寰勫姣斻�嶅苟鍒楀畼鏂瑰彛寰勪笌鍚屼紒涓氬彛寰勭殑鍚屾瘮锛涖�屽彛寰勬牎楠屻�嶄负鏄庣粏涓庢眹鎬诲璐︼紝浠ュ強涓庢眹鎬昏〃銆佸凡鍙戝竷鎶ュ憡鐨勬瘮瀵广��'),
     ('娉ㄦ剰浜嬮」', '鈶� 鏈� 5 瀹朵紒涓� 1鈥�8 鏈堟湀鎶ヨ揣杩愰噺銆佸懆杞噺鍧囦负 0锛岃鍏ヤ紒涓氭暟浣嗕笉褰卞搷杩愯窛锛�'
                  '鈶� 姝︽眽鐭冲寲浜ら�氳繍杈撴湁闄愬叕鍙搞�佹箹鍖楁嵎榫欎氦閫氳繍涓氭湁闄愬叕鍙� 2026 骞� 1 鏈堢殑鍦板尯鐮佷笌鍏跺悗鏈堜唤涓嶅悓锛�420100 鈫� 420107 / 420103锛夛紝'
                  '浣嗗悓灞炴姹夊競锛屼笉褰卞搷甯傚窞褰掗泦锛涒憿 绱璐ц繍閲忎负 0 鐨勪紒涓氱疮璁¤繍璺濈暀绌恒��'),
+    ('鐗堟湰', 'v2锛�2026-09-18锛夛細淇鍚屾瘮鍩烘湡鍙e緞鈥斺�攙1 璇敤銆屼袱骞村潎鎸佺画鍦ㄨ鐨� 545 瀹躲�嶄綔鍩烘湡锛屽叏鐪佺疮璁¤繍璺濆悓姣旂畻寰� +2.90%%锛�'
+             '瀹樻柟鍙e緞搴斾负 +4.54%%銆�'),
     ('鐢熸垚鏃堕棿', '2026-09-18'),
 ]
 r = 3
@@ -161,18 +188,18 @@
 
 # ---------------- Sheet2 鍒嗗競宸炴眹鎬� ----------------
 ws2 = wb.create_sheet('鍒嗗競宸炴眹鎬�')
-heads = ['搴忓彿', '甯傚窞', '鎸佺画鍦ㄨ浼佷笟鏁�(瀹�)', '鍏朵腑涓ゅ勾鍧囨寔缁湪瑙�(瀹�)',
+heads = ['搴忓彿', '甯傚窞', '2026骞�1-8鏈堟寔缁湪瑙勪紒涓氭暟(瀹�)', '2025骞�1-8鏈堣涓婁紒涓氭暟(瀹�)',
          '2026骞�1-8鏈堢疮璁¤揣杩愰噺(涓囧惃)', '2026骞�1-8鏈堢疮璁¤揣鐗╁懆杞噺(涓囧惃鍏噷)', '2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)',
-         '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)[鍙瘮鍙e緞]', '绱杩愯窛鍚屾瘮(%)',
-         '绱璐ц繍閲忓悓姣�(%)', '绱璐х墿鍛ㄨ浆閲忓悓姣�(%)', '澶囨敞']
-widths = [6, 14, 16, 18, 20, 24, 20, 22, 14, 14, 16, 40]
-ws2.cell(1, 1, '璐ц繍鏈堟姤鎸佺画鍦ㄨ浼佷笟鍒嗗競宸� 1-8 鏈堢疮璁″钩鍧囪繍璺濆強鍚屾瘮鎯呭喌').font = Font(bold=True, size=13, color='FFFFFF')
+         '2025骞�1-8鏈堢疮璁¤揣杩愰噺(涓囧惃)', '2025骞�1-8鏈堢疮璁¤揣鐗╁懆杞噺(涓囧惃鍏噷)', '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)',
+         '绱骞冲潎杩愯窛鍚屾瘮(%)', '绱璐ц繍閲忓悓姣�(%)', '绱璐х墿鍛ㄨ浆閲忓悓姣�(%)']
+widths = [6, 14, 20, 18, 20, 24, 20, 20, 24, 20, 15, 15, 17]
+ws2.cell(1, 1, '璐ц繍鏈堟姤鎸佺画鍦ㄨ浼佷笟鍒嗗競宸� 1-8 鏈堢疮璁″钩鍧囪繍璺濆強鍚屾瘮鎯呭喌锛堝悓姣斿熀鏈燂細2025 骞� 1-8 鏈堝叏閮ㄨ涓婁紒涓氾級').font = Font(bold=True, size=13, color='FFFFFF')
 ws2.cell(1, 1).fill = TITLE_FILL
 ws2.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
 ws2.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(heads))
 ws2.row_dimensions[1].height = 26
-ws2.cell(2, 1, '娉細绗� 5鈥�7 鍒椾负 2026 骞存寔缁湪瑙勪紒涓氬叏鍙e緞锛�%d 瀹讹級锛涚 8鈥�11 鍒椾负涓ゅ勾鍧囨寔缁湪瑙勪紒涓氬彲姣斿彛寰勶紙%d 瀹讹級鐨勫悓姣旇绠楀熀鏁般��'
-               % (tot['n'], tot['n_cmp']))
+ws2.cell(2, 1, '娉細绗� 5鈥�7 鍒椾负 2026 骞存寔缁湪瑙勪紒涓氾紙636 瀹讹級绱鍊硷紱绗� 8鈥�10 鍒椾负 2025 骞� 1-8 鏈堝叏閮ㄨ涓婁紒涓氾紙鍏ㄧ渷 664 瀹讹級绱鍊硷紝'
+               '鍗虫眹鎬昏〃銆岃揣杩愩�嶉〉 T 鍒楀悓姣斿叕寮忕殑鍩烘湡锛涚 11鈥�13 鍒楀悓姣斿彛寰勪笌鍙戝竷鎶ュ憡涓�鑷淬��')
 ws2.cell(2, 1).font = Font(size=9, italic=True, color='808080')
 ws2.merge_cells(start_row=2, start_column=1, end_row=2, end_column=len(heads))
 for j, h in enumerate(heads, 1):
@@ -189,46 +216,44 @@
         continue
     idx += 1
     rr += 1
-    d26, d26c, d25c = dist(c['f26'], c['t26']), dist(c['f26c'], c['t26c']), dist(c['f25c'], c['t25c'])
-    if c['n_cmp'] == 0:
-        note = '鍚屾瘮涓嶉�傜敤锛氳甯傚窞 2026 骞存寔缁湪瑙勪紒涓氬湪 2025 骞� 1-8 鏈堟棤杩炵画涓婃姤璁板綍'
-    elif c['n_cmp'] == c['n']:
-        note = ''
-    else:
-        note = '鍚屾瘮鎸� %d 瀹朵袱骞村潎鎸佺画鍦ㄨ浼佷笟璁$畻' % c['n_cmp']
-    vals = [idx, city, c['n'], c['n_cmp'], c['f26'] / 10000.0, c['t26'] / 10000.0, d26, d25c,
-            growth(d26c, d25c), growth(c['f26c'], c['f25c']), growth(c['t26c'], c['t25c']), note]
+    b = B25.get(city, dict(ents=set(), f=0.0, t=0.0))
+    d26, d25 = dist(c['f26'], c['t26']), dist(b['f'], b['t'])
+    vals = [idx, city, c['n'], len(b['ents']),
+            c['f26'] / 10000.0, c['t26'] / 10000.0, d26,
+            b['f'] / 10000.0, b['t'] / 10000.0, d25,
+            growth(d26, d25), growth(c['f26'], b['f']), growth(c['t26'], b['t'])]
     for j, v in enumerate(vals, 1):
         cell = ws2.cell(rr, j, v)
         cell.border = BORDER
-        cell.alignment = LT if j == 12 else (CT if j <= 4 else RT)
-        if j in (5, 6, 7, 8):
+        cell.alignment = CT if j <= 4 else RT
+        if j in (5, 6, 7, 8, 9, 10):
             cell.number_format = NF2
-        elif j in (9, 10, 11):
-            cell.number_format = NF1
+        elif j in (11, 12, 13):
+            cell.number_format = NFP
 rr += 1
-vals = ['', '鍏ㄧ渷鍚堣', tot['n'], tot['n_cmp'], tot['f26'] / 10000.0, tot['t26'] / 10000.0,
-        dist(tot['f26'], tot['t26']), dist(tot['f25c'], tot['t25c']),
-        growth(dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c'])),
-        growth(tot['f26c'], tot['f25c']), growth(tot['t26c'], tot['t25c']), '']
+d26p, d25p = dist(tot['f26'], tot['t26']), dist(tot25['f'], tot25['t'])
+vals = ['', '鍏ㄧ渷鍚堣', tot['n'], len(tot25['ents']),
+        tot['f26'] / 10000.0, tot['t26'] / 10000.0, d26p,
+        tot25['f'] / 10000.0, tot25['t'] / 10000.0, d25p,
+        growth(d26p, d25p), growth(tot['f26'], tot25['f']), growth(tot['t26'], tot25['t'])]
 for j, v in enumerate(vals, 1):
     cell = ws2.cell(rr, j, v)
     cell.border = BORDER
     cell.fill = TOT_FILL
     cell.font = Font(bold=True)
     cell.alignment = CT if j <= 4 else RT
-    if j in (5, 6, 7, 8):
+    if j in (5, 6, 7, 8, 9, 10):
         cell.number_format = NF2
-    elif j in (9, 10, 11):
-        cell.number_format = NF1
+    elif j in (11, 12, 13):
+        cell.number_format = NFP
 ws2.freeze_panes = 'C4'
 
 # ---------------- Sheet3 鍒嗗競宸炲垎鏈� ----------------
 ws3 = wb.create_sheet('鍒嗗競宸炲垎鏈�')
 h3 = ['搴忓彿', '甯傚窞', '鎸佺画鍦ㄨ浼佷笟鏁�(瀹�)'] + ['%s鏈堝钩鍧囪繍璺�(鍏噷)' % m for m in range(1, 9)] + \
      ['2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)', '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)', '绱杩愯窛鍚屾瘮(%)']
-w3 = [6, 14, 16] + [14] * 8 + [24, 24, 14]
-ws3.cell(1, 1, '鍒嗗競宸� 2026 骞撮�愭湀骞冲潎杩愯窛锛堟寔缁湪瑙勪紒涓氬彛寰勶紝鍚屾瘮鍩烘湡 2025 骞� 1-8 鏈堬級').font = Font(bold=True, size=13, color='FFFFFF')
+w3 = [6, 14, 18] + [15] * 8 + [24, 24, 15]
+ws3.cell(1, 1, '鍒嗗競宸� 2026 骞撮�愭湀骞冲潎杩愯窛锛堟寔缁湪瑙勪紒涓氬彛寰勶紱鍚屾瘮鍩烘湡锛�2025 骞� 1-8 鏈堝叏閮ㄨ涓婁紒涓氾級').font = Font(bold=True, size=13, color='FFFFFF')
 ws3.cell(1, 1).fill = TITLE_FILL
 ws3.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
 ws3.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(h3))
@@ -247,31 +272,19 @@
         continue
     idx += 1
     rr += 1
-    vals = [idx, city, c['n']]
-    for per in MONTHS26:
-        vals.append(city_month(per, city, persist_names)[0])
-    vals += [dist(c['f26'], c['t26']), dist(c['f25c'], c['t25c']), growth(dist(c['f26c'], c['t26c']), dist(c['f25c'], c['t25c']))]
+    b = B25.get(city, dict(ents=set(), f=0.0, t=0.0))
+    d26, d25 = dist(c['f26'], c['t26']), dist(b['f'], b['t'])
+    vals = [idx, city, c['n']] + [city_month(p, city, persist_names)[0] for p in MONTHS26] + \
+           [d26, d25, growth(d26, d25)]
     for j, v in enumerate(vals, 1):
         cell = ws3.cell(rr, j, v)
         cell.border = BORDER
         cell.alignment = CT if j <= 3 else RT
         if j >= 4:
-            cell.number_format = NF2 if j <= 13 else NF1
+            cell.number_format = NFP if j == 14 else NF2
 rr += 1
-# 鍏ㄧ渷鍒嗘湀锛氭寜 17 甯傚窞閫愭湀鍚堣閲嶇畻
-vals = ['', '鍏ㄧ渷鍚堣', tot['n']]
-for per in MONTHS26:
-    fr = tu = 0.0
-    for city in CITY_ORDER:
-        _, f0 = city_month(per, city, persist_names)
-        fr += f0
-        d = cust.get((per, city), {})
-        for en, v in d.items():
-            if en in persist_names:
-                tu += v[1]
-    vals.append(dist(fr, tu))
-vals += [dist(tot['f26'], tot['t26']), dist(tot['f25c'], tot['t25c']),
-         growth(dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c']))]
+vals = ['', '鍏ㄧ渷鍚堣', tot['n']] + [city_month(p, None)[0] for p in MONTHS26] + \
+       [d26p, d25p, growth(d26p, d25p)]
 for j, v in enumerate(vals, 1):
     cell = ws3.cell(rr, j, v)
     cell.border = BORDER
@@ -279,7 +292,7 @@
     cell.font = Font(bold=True)
     cell.alignment = CT if j <= 3 else RT
     if j >= 4:
-        cell.number_format = NF2 if j <= 13 else NF1
+        cell.number_format = NFP if j == 14 else NF2
 ws3.freeze_panes = 'D3'
 
 # ---------------- Sheet4 浼佷笟鏄庣粏 ----------------
@@ -289,7 +302,7 @@
       '2025骞�1-8鏈堣揣杩愰噺(涓囧惃)', '2025骞�1-8鏈堝懆杞噺(涓囧惃鍏噷)', '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)',
       '骞冲潎杩愯窛鍚屾瘮(%)', '鏄惁涓ゅ勾鍧囨寔缁湪瑙�']
 w4 = [6, 12, 42, 22, 24, 20, 24, 22, 20, 24, 22, 14, 18]
-ws4.cell(1, 1, '鎸佺画鍦ㄨ浼佷笟閫愭埛绱骞冲潎杩愯窛锛�2026 骞� 1-8 鏈堬紝鍚屾瘮鍩烘湡 2025 骞� 1-8 鏈堬級').font = Font(bold=True, size=13, color='FFFFFF')
+ws4.cell(1, 1, '鎸佺画鍦ㄨ浼佷笟閫愭埛绱骞冲潎杩愯窛锛�2026 骞� 1-8 鏈堬紱鍚屾湡鍊间笌鍚屾瘮浠呭涓ゅ勾鍧囨寔缁湪瑙勭殑 545 瀹剁粰鍑猴級').font = Font(bold=True, size=13, color='FFFFFF')
 ws4.cell(1, 1).fill = TITLE_FILL
 ws4.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
 ws4.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(h4))
@@ -320,37 +333,114 @@
         if j in (6, 7, 8, 9, 10, 11):
             cell.number_format = NF2
         elif j == 12:
-            cell.number_format = NF1
+            cell.number_format = NFP
         if j == 12 and isinstance(v, float) and v > 0.5:
             cell.font = Font(color='C00000', bold=True)
 ws4.freeze_panes = 'D3'
 ws4.auto_filter.ref = 'A2:M%d' % rr
 
+# ---------------- Sheet5 鍙e緞瀵规瘮 ----------------
+ws6 = wb.create_sheet('鍙e緞瀵规瘮')
+h6 = ['搴忓彿', '甯傚窞', '2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)\n[鎸佺画鍦ㄨ浼佷笟636瀹禲',
+      '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)\n[鍏ㄩ儴瑙勪笂浼佷笟]', '瀹樻柟鍙e緞鍚屾瘮(%)',
+      '2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)\n[鍚屼紒涓�545瀹禲', '鍚屼紒涓氬彛寰勫悓姣�(%)', '涓よ�呭樊寮�(鐧惧垎鐐�)', '璇存槑']
+w6 = [6, 14, 26, 24, 15, 24, 15, 16, 46]
+ws6.cell(1, 1, '涓ょ鍚屾瘮鍙e緞瀵规瘮锛堝畼鏂瑰彛寰� = 涓庢眹鎬昏〃銆岃揣杩愩�嶉〉 T 鍒椾竴鑷达紱鍚屼紒涓氬彛寰� = 浠呬袱骞村潎鎸佺画鍦ㄨ浼佷笟锛�').font = Font(bold=True, size=13, color='FFFFFF')
+ws6.cell(1, 1).fill = TITLE_FILL
+ws6.cell(1, 1).alignment = Alignment(horizontal='center', vertical='center')
+ws6.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(h6))
+ws6.row_dimensions[1].height = 26
+ws6.cell(2, 1, '娉細浜や粯涓昏〃锛堝垎甯傚窞姹囨�伙級閲囩敤瀹樻柟鍙e緞锛涙湰椤典袱涓彛寰勫苟鍒楋紝宸紓澶х殑甯傚窞澶氫负涓ゅ勾闂磋涓婁紒涓氬悕鍗曞彉鍔紙鍋滀骇銆佹柊澧炪�佹媶鍒嗭級鎵�鑷达紝渚涗笟鍔℃牳瀵瑰弬鑰冦��')
+ws6.cell(2, 1).font = Font(size=9, italic=True, color='808080')
+ws6.merge_cells(start_row=2, start_column=1, end_row=2, end_column=len(h6))
+for j, h in enumerate(h6, 1):
+    c = ws6.cell(3, j, h)
+    c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
+    ws6.column_dimensions[get_column_letter(j)].width = w6[j - 1]
+ws6.row_dimensions[3].height = 48
+
+rr = 3
+idx = 0
+for city in CITY_ORDER:
+    c = by_city.get(city)
+    if not c or c['n'] == 0:
+        continue
+    idx += 1
+    rr += 1
+    b = B25.get(city, dict(ents=set(), f=0.0, t=0.0))
+    g = by_city_cmp.get(city)
+    d26, d25 = dist(c['f26'], c['t26']), dist(b['f'], b['t'])
+    g_off = growth(d26, d25)
+    if g:
+        dc26, dc25 = dist(g['f26c'], g['t26c']), dist(g['f25c'], g['t25c'])
+        g_same = growth(dc26, dc25)
+    else:
+        dc25, g_same = None, None
+    diff = (g_off - g_same) if (g_off is not None and g_same is not None) else None
+    note = ''
+    if diff is not None and abs(diff) > 0.10:
+        note = '涓ゅ彛寰勫樊寮傝秴 10 涓櫨鍒嗙偣锛屼富瑕佸洜涓ゅ勾瑙勪笂浼佷笟鍚嶅崟鍙樺姩锛屽缓璁牳瀵硅甯傚窞浼佷笟鍙樺姩鎯呭喌'
+    vals = [idx, city, d26, d25, g_off, dc25, g_same, (diff * 100 if diff is not None else None), note]
+    for j, v in enumerate(vals, 1):
+        cell = ws6.cell(rr, j, v)
+        cell.border = BORDER
+        cell.alignment = LT if j in (1, 9) else (CT if j == 2 else RT)
+        if j in (3, 4, 6):
+            cell.number_format = NF2
+        elif j in (5, 7):
+            cell.number_format = NFP
+        elif j == 8:
+            cell.number_format = NF1
+rr += 1
+g_cmp = growth(dist(tot_cmp['f26c'], tot_cmp['t26c']), dist(tot_cmp['f25c'], tot_cmp['t25c']))
+vals = ['', '鍏ㄧ渷鍚堣', d26p, d25p, growth(d26p, d25p), dist(tot_cmp['f25c'], tot_cmp['t25c']), g_cmp,
+        (growth(d26p, d25p) - g_cmp) * 100 if g_cmp is not None else None, '']
+for j, v in enumerate(vals, 1):
+    cell = ws6.cell(rr, j, v)
+    cell.border = BORDER
+    cell.fill = TOT_FILL
+    cell.font = Font(bold=True)
+    cell.alignment = CT if j == 2 else RT
+    if j in (3, 4, 6):
+        cell.number_format = NF2
+    elif j in (5, 7):
+        cell.number_format = NFP
+    elif j == 8:
+        cell.number_format = NF1
+ws6.freeze_panes = 'C4'
+
 # ---------------- Sheet5 鍙e緞鏍¢獙 ----------------
 ws5 = wb.create_sheet('鍙e緞鏍¢獙')
 ws5.sheet_view.showGridLines = False
-ws5.column_dimensions['A'].width = 42
-for col, w in (('B', 20), ('C', 20), ('D', 14), ('E', 40)):
+ws5.column_dimensions['A'].width = 46
+for col, w in (('B', 20), ('C', 20), ('D', 14), ('E', 46)):
     ws5.column_dimensions[col].width = w
 ws5.cell(1, 1, '鍙e緞鏍¢獙涓庡璐�').font = Font(bold=True, size=13, color='FFFFFF')
 ws5.cell(1, 1).fill = TITLE_FILL
 ws5.merge_cells(start_row=1, start_column=1, end_row=1, end_column=5)
 sum_f = sum(r['f26'] for r in rows) / 10000.0
 sum_t = sum(r['t26'] for r in rows) / 10000.0
-aug_f = sum(v[0] for c in CITY_ORDER for en, v in cust.get(("2026-08", c), {}).items() if en in persist_names) / 10000.0
-aug_t = sum(v[1] for c in CITY_ORDER for en, v in cust.get(("2026-08", c), {}).items() if en in persist_names) / 10000.0
+aug_f = sum(v[0] for c in CITY_ORDER for en, v in cust.get(('2026-08', c), {}).items() if en in persist_names) / 10000.0
+aug_t = sum(v[1] for c in CITY_ORDER for en, v in cust.get(('2026-08', c), {}).items() if en in persist_names) / 10000.0
 chk = [
-    ("鏍¢獙椤�", "鏈〃鏁板��", "瀵圭収鏁板��", "宸紓", "璇存槑"),
-    ("鏄庣粏琛ㄤ紒涓氭暟 = 姹囨�昏〃浼佷笟鏁�", len(rows), tot["n"], len(rows) - tot["n"], "636 瀹舵寔缁湪瑙勪紒涓氶�愭埛涓�琛岋紝鏃犻噸澶嶆棤閬楁紡"),
-    ("鏄庣粏琛ㄨ揣杩愰噺鍚堣(涓囧惃)", round(sum_f, 2), round(tot["f26"] / 10000.0, 2), round(sum_f - tot["f26"] / 10000.0, 2), "鏄庣粏姹傚拰 = 17 甯傚窞姹囨�诲悎璁�"),
-    ("鏄庣粏琛ㄥ懆杞噺鍚堣(涓囧惃鍏噷)", round(sum_t, 2), round(tot["t26"] / 10000.0, 2), round(sum_t - tot["t26"] / 10000.0, 2), "鏄庣粏姹傚拰 = 17 甯傚窞姹囨�诲悎璁�"),
-    ("涓庡凡鍙戝竷鍏ㄧ渷 1-8 鏈堢疮璁¤揣杩愰噺姣斿(涓囧惃)", round(tot["f26"] / 10000.0, 2), PROV_VOL_PUB, round(tot["f26"] / 10000.0 - PROV_VOL_PUB, 2), "瀵圭収銆�2026骞�8鏈堣涓婅揣鐗╁懆杞噺涓庡钩鍧囪繍璺濆垎鏋愭姤鍛娿��"),
-    ("涓庡凡鍙戝竷鍏ㄧ渷 1-8 鏈堢疮璁″懆杞噺姣斿(涓囧惃鍏噷)", round(tot["t26"] / 10000.0, 2), PROV_TURN_PUB, round(tot["t26"] / 10000.0 - PROV_TURN_PUB, 2), "鍚屼笂"),
-    ("2026骞�8鏈堝叏鐪佽揣杩愰噺姣斿(涓囧惃)", round(aug_f, 2), 3665.36, round(aug_f - 3665.36, 2), "瀵圭収宸插彂甯� 8 鏈堝叏鐪佽涓婅揣杩愰噺锛岄獙璇佸競宸炲綊闆嗘棤璇�"),
-    ("2026骞�8鏈堝叏鐪佸懆杞噺姣斿(涓囧惃鍏噷)", round(aug_t, 2), 570004.44, round(aug_t - 570004.44, 2), "瀵圭収宸插彂甯� 8 鏈堝叏鐪佽涓婂懆杞噺"),
-    ("鍏ㄧ渷 1-8 鏈堢疮璁″钩鍧囪繍璺�(鍏噷)", round(dist(tot["f26"], tot["t26"]), 2), None, None, "鐢辨湰琛ㄧ疮璁″懆杞噺 梅 绱璐ц繍閲忚绠�"),
-    ("瑕嗙洊甯傚窞鏁�", len([c for c in CITY_ORDER if by_city.get(c, {}).get("n")]), 17, None, "17 涓競宸炲叏瑕嗙洊"),
-    ("鏈瘑鍒湴鍖虹爜", len(unknown), 0, None, ("銆�".join(unknown) if unknown else "鏃�")),
+    ('鏍¢獙椤�', '鏈〃鏁板��', '瀵圭収鏁板��', '宸紓', '璇存槑'),
+    ('鏄庣粏琛ㄤ紒涓氭暟 = 姹囨�昏〃浼佷笟鏁�', len(rows), tot['n'], len(rows) - tot['n'], '636 瀹舵寔缁湪瑙勪紒涓氶�愭埛涓�琛�'),
+    ('鏄庣粏琛ㄨ揣杩愰噺鍚堣(涓囧惃)', round(sum_f, 2), round(tot['f26'] / 10000.0, 2), round(sum_f - tot['f26'] / 10000.0, 2), '鏄庣粏姹傚拰 = 17 甯傚窞姹囨�诲悎璁�'),
+    ('鏄庣粏琛ㄥ懆杞噺鍚堣(涓囧惃鍏噷)', round(sum_t, 2), round(tot['t26'] / 10000.0, 2), round(sum_t - tot['t26'] / 10000.0, 2), '鏄庣粏姹傚拰 = 17 甯傚窞姹囨�诲悎璁�'),
+    ('2026骞�1-8鏈堢疮璁¤揣杩愰噺(涓囧惃)', round(tot['f26'] / 10000.0, 2), PROV['vol26'], round(tot['f26'] / 10000.0 - PROV['vol26'], 2), '瀵圭収宸插彂甯冩姤鍛� 1-8 鏈堢疮璁�'),
+    ('2026骞�1-8鏈堢疮璁″懆杞噺(涓囧惃鍏噷)', round(tot['t26'] / 10000.0, 2), PROV['turn26'], round(tot['t26'] / 10000.0 - PROV['turn26'], 2), '鍚屼笂'),
+    ('2026骞�8鏈堝叏鐪佽揣杩愰噺(涓囧惃)', round(aug_f, 2), PROV['vol26_08'], round(aug_f - PROV['vol26_08'], 2), '楠岃瘉甯傚窞褰掗泦鏃犺'),
+    ('2026骞�8鏈堝叏鐪佸懆杞噺(涓囧惃鍏噷)', round(aug_t, 2), PROV['turn26_08'], round(aug_t - PROV['turn26_08'], 2), '鍚屼笂'),
+    ('2025骞�1-8鏈堢疮璁¤揣杩愰噺(涓囧惃)銆愬悓姣斿熀鏈熴��', round(tot25['f'] / 10000.0, 2), PROV['vol25'], round(tot25['f'] / 10000.0 - PROV['vol25'], 2), '瀵圭収姹囨�昏〃銆岃揣杩愩�嶉〉 V7..AJ7锛堢紦瀛樺�硷級'),
+    ('2025骞�1-8鏈堢疮璁″懆杞噺(涓囧惃鍏噷)銆愬悓姣斿熀鏈熴��', round(tot25['t'] / 10000.0, 2), PROV['turn25'], round(tot25['t'] / 10000.0 - PROV['turn25'], 2), '瀵圭収姹囨�昏〃銆岃揣杩愩�嶉〉 V8..AJ8锛堢紦瀛樺�硷級'),
+    ('2025骞�1-8鏈堝叏鐪佽涓婁紒涓氭暟(瀹�)', len(tot25['ents']), 664, len(tot25['ents']) - 664, '鍚屾瘮鍩烘湡浼佷笟鑼冨洿'),
+    ('绱璐ц繍閲忓悓姣�', growth(tot['f26'], tot25['f']), PROV['yoy_vol'], round(growth(tot['f26'], tot25['f']) - PROV['yoy_vol'], 4), '瀵圭収宸插彂甯冩姤鍛� 1-8 鏈堢疮璁″悓姣�'),
+    ('绱鍛ㄨ浆閲忓悓姣�', growth(tot['t26'], tot25['t']), PROV['yoy_turn'], round(growth(tot['t26'], tot25['t']) - PROV['yoy_turn'], 4), '鍚屼笂'),
+    ('2025骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)', round(d25p, 2), None, None, '= 2025 绱鍛ㄨ浆閲� 梅 绱璐ц繍閲�'),
+    ('2026骞�1-8鏈堢疮璁″钩鍧囪繍璺�(鍏噷)', round(d26p, 2), None, None, '= 2026 绱鍛ㄨ浆閲� 梅 绱璐ц繍閲�'),
+    ('鍏ㄧ渷绱骞冲潎杩愯窛鍚屾瘮', growth(d26p, d25p), None, None, '= 156.92 梅 150.10 鈭� 1'),
+    ('瑕嗙洊甯傚窞鏁�', len([c for c in CITY_ORDER if by_city.get(c, {}).get('n')]), 17, None, '17 涓競宸炲叏瑕嗙洊'),
+    ('鏈瘑鍒湴鍖虹爜', len(unknown), 0, None, ('銆�'.join(unknown) if unknown else '鏃�')),
 ]
 for i, row in enumerate(chk, 1):
     for j, v in enumerate(row, 1):
@@ -360,22 +450,19 @@
         if i == 1:
             cell.font = Font(bold=True); cell.fill = HDR_FILL
         elif j in (2, 3, 4):
-            cell.number_format = NF2
+            cell.number_format = NFP if row[0].endswith('鍚屾瘮') else NF2
 
 os.makedirs(OUT_DIR, exist_ok=True)
 wb.save(OUT)
 print('OK ->', OUT)
-print('鎸佺画鍦ㄨ浼佷笟鏁�: %d  涓ゅ勾鍙瘮: %d' % (tot['n'], tot['n_cmp']))
-print('鍏ㄧ渷绱璐ц繍閲�(涓囧惃): %.2f  绱鍛ㄨ浆閲�(涓囧惃鍏噷): %.2f  绱骞冲潎杩愯窛: %.2f' %
-      (tot['f26'] / 10000.0, tot['t26'] / 10000.0, dist(tot['f26'], tot['t26'])))
-print('鍙瘮鍙e緞 2026 绱杩愯窛: %.2f  2025: %.2f  鍚屾瘮: %.2f%%' %
-      (dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c']), growth(dist(tot['f26c'], tot['t26c']), dist(tot['f25c'], tot['t25c'])) * 100))
+print('2026 1-8鏈�: %.4f 涓囧惃 / %.4f 涓囧惃鍏噷 / %.4f 鍏噷' % (tot['f26'] / 10000.0, tot['t26'] / 10000.0, d26p))
+print('2025 1-8鏈�: %.4f 涓囧惃 / %.4f 涓囧惃鍏噷 / %.4f 鍏噷 (%d 瀹�)' % (tot25['f'] / 10000.0, tot25['t'] / 10000.0, d25p, len(tot25['ents'])))
+print('鍏ㄧ渷鍚屾瘮: 璐ц繍閲� %.2f%%  鍛ㄨ浆閲� %.2f%%  骞冲潎杩愯窛 %.2f%%' %
+      (growth(tot['f26'], tot25['f']) * 100, growth(tot['t26'], tot25['t']) * 100, growth(d26p, d25p) * 100))
 for city in CITY_ORDER:
     c = by_city.get(city)
     if c and c['n']:
-        g = growth(dist(c['f26c'], c['t26c']), dist(c['f25c'], c['t25c']))
-        print('%-8s n=%-4d 2026绱=%8s 2025绱=%8s 鍚屾瘮=%s' % (
-            city, c['n'],
-            '%.2f' % dist(c['f26'], c['t26']),
-            '%.2f' % dist(c['f25c'], c['t25c']) if dist(c['f25c'], c['t25c']) else '-',
-            '%.1f%%' % (g * 100) if g is not None else '-'))
+        b = B25.get(city, dict(ents=set(), f=0.0, t=0.0))
+        d1, d0 = dist(c['f26'], c['t26']), dist(b['f'], b['t'])
+        print('%-8s n=%-4d 2026=%8.2f 2025=%8s 鍚屾瘮=%s' % (
+            city, c['n'], d1, ('%.2f' % d0) if d0 else '-', ('%.1f%%' % (growth(d1, d0) * 100)) if d0 else '-'))

--
Gitblit v1.9.1