zhizhijie
2026-09-18 b14fec1599277629e10b2bc686f02fd0c2e0bec1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# -*- coding: utf-8 -*-
"""货运月报「持续在规企业」分市州 1-8 月累计运距及同比 台账生成(v2:同比基期改为官方全口径)
 
数据源:traffic_audit.h2032_enterprise_monthly(《道路货物运输月度生产情况》H203-2 企业月报)
        导出件 _tmp_persist_scope.tsv(企业级累计)、_tmp_persist_scope_monthly.tsv(企业级分月)
口径(v2 修订):
  · 持续在规企业 = 2026 年 1-8 月连续 8 个月均上报月报的企业(636 家,用户 2026-09-18 确认);
  · 累计平均运距(公里)= 累计货物周转量 ÷ 累计货运量(加权平均);
  · 同比 = 2026 年 1-8 月累计值 ÷ 2025 年 1-8 月累计值 − 1,**基期为 2025 年 1-8 月全部规上企业上报数**
    (全省 664 家),与汇总表「货运」页 T 列 `=S/SUM(V7,X7,…,AJ7)-1` 同一口径;
    v1 误用「两年均持续在规的 545 家」作基期,导致同比偏小,已修正。
"""
import io, os
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
 
SRC = r'D:\文档\ChatGPT\traffic-audit\_tmp_persist_scope.tsv'
MONTHLY_SRC = r'D:\文档\ChatGPT\traffic-audit\_tmp_persist_scope_monthly.tsv'
OUT_DIR = r'D:\文档\ChatGPT\traffic-audit\outputs'
OUT = os.path.join(OUT_DIR, '货运月报_持续在规企业_分市州1-8月累计运距及同比_2026-09-18.xlsx')
 
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 = ['武汉市', '黄石市', '十堰市', '宜昌市', '襄阳市', '鄂州市', '荆门市', '孝感市', '荆州市',
              '黄冈市', '咸宁市', '随州市', '恩施州', '仙桃市', '潜江市', '天门市', '神农架林区']
CITY = {'4201': '武汉市', '4202': '黄石市', '4203': '十堰市', '4205': '宜昌市', '4206': '襄阳市',
        '4207': '鄂州市', '4208': '荆门市', '4209': '孝感市', '4210': '荆州市', '4211': '黄冈市',
        '4212': '咸宁市', '4213': '随州市', '4228': '恩施州',
        '429004': '仙桃市', '429005': '潜江市', '429006': '天门市', '429021': '神农架林区'}
MONTHS26 = ['2026-%02d' % m for m in range(1, 9)]
 
 
def city_of(rc):
    rc = (rc or '').strip()
    return CITY[rc] if rc in CITY else CITY.get(rc[:4], '未识别(%s)' % rc)
 
 
def num(x):
    try:
        return float(x)
    except Exception:
        return 0.0
 
 
def dist(f, t):
    return (t / f) if f else None
 
 
def growth(now, base):
    return (now / base - 1) if (now is not None and base) else None
 
 
# ---------- 读企业级累计数据 ----------
rows = []
with io.open(SRC, 'r', encoding='utf-8') as f:
    hdr = f.readline().rstrip('\r\n').split('\t')
    for line in f:
        p = line.rstrip('\r\n').split('\t')
        if len(p) < len(hdr):
            continue
        d = dict(zip(hdr, p))
        if int(num(d['m26'])) != 8:
            continue
        rows.append(dict(city=city_of(d['rc']), rc=d['rc'], en=d['en'], ec=d['ec'], ucc=d['ucc'],
                         f26=num(d['f26']), t26=num(d['t26']),
                         f25=num(d['f25']), t25=num(d['t25']), cmp=(int(num(d['m25'])) == 8)))
 
by_city = {}
for r in rows:
    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']
 
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)
 
# ---------- 读企业级分月数据 ----------
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 or p[0] == 'report_period':
            continue
        per, rc, en, fr, tu = p[0], p[1], p[2], p[3], p[4]
        cust.setdefault((per, city_of(rc)), {})[en] = (num(fr), num(tu))
 
 
def city_month(period, city, names=None):
    d = cust.get((period, city))
    if not d:
        return None, 0.0, 0.0
    fr = tu = 0.0
    for en, v in d.items():
        if names is None or en in names:
            fr += v[0]; tu += v[1]
    return dist(fr, tu), fr, tu
 
 
# ---------- 2025 年 1-8 月全口径基期 ----------
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')
BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
HDR_FILL = PatternFill('solid', fgColor='DDEBF7')
TOT_FILL = PatternFill('solid', fgColor='FFF2CC')
TITLE_FILL = PatternFill('solid', fgColor='1F4E79')
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, NFP = '#,##0.00', '#,##0.0', '0.00%'
 
wb = Workbook()
 
# ---------------- Sheet1 说明与口径 ----------------
ws = wb.active
ws.title = '说明与口径'
ws.sheet_view.showGridLines = False
ws.column_dimensions['A'].width = 16
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
t.alignment = Alignment(horizontal='center', vertical='center')
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=2)
ws.row_dimensions[1].height = 30
 
info = [
    ('数据来源', '系统数据库 traffic_audit.h2032_enterprise_monthly《道路货物运输月度生产情况》(H203-2)企业月报,'
                 '取 2025 年 1 月—2026 年 8 月全部企业级上报记录。'),
    ('持续在规企业', '2026 年 1—8 月连续 8 个月均上报月报的企业,共 %d 家(2026 年各月上报企业数均为 636 家,全期一致;'
                 '口径经用户 2026-09-18 确认)。' % tot['n']),
    ('累计运距口径', '累计平均运距(公里)= 1—8 月累计货物周转量 ÷ 1—8 月累计货运量(加权平均)。源表单位为吨、吨公里,'
                     '本表折算为万吨、万吨公里,比值不变。'),
    ('同比口径', '同比 = 2026 年 1—8 月累计值 ÷ 2025 年 1—8 月累计值 − 1。**基期为 2025 年 1—8 月全部规上企业上报数**'
                 '(全省 %d 家),与汇总表「货运」页 T 列公式 =S/SUM(V7,X7,…,AJ7)-1 同一口径,可与发布报告直接对齐:'
                 '全省累计货运量同比 −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 家持续在规企业逐户数据(企业级同比仅对两年均连续上报的 545 家给出);'
                 '「口径对比」并列官方口径与同企业口径的同比;「口径校验」为明细与汇总对账,以及与汇总表、已发布报告的比对。'),
    ('注意事项', '① 有 5 家企业 1—8 月月报货运量、周转量均为 0,计入企业数但不影响运距;'
                 '② 武汉石化交通运输有限公司、湖北捷龙交通运业有限公司 2026 年 1 月的地区码与其后月份不同(420100 → 420107 / 420103),'
                 '但同属武汉市,不影响市州归集;③ 累计货运量为 0 的企业累计运距留空。'),
    ('版本', 'v2(2026-09-18):修正同比基期口径——v1 误用「两年均持续在规的 545 家」作基期,全省累计运距同比算得 +2.90%%,'
             '官方口径应为 +4.54%%。'),
    ('生成时间', '2026-09-18'),
]
r = 3
for k, v in info:
    a = ws.cell(r, 1, k); a.font = Font(bold=True); a.alignment = LT; a.border = BORDER
    b = ws.cell(r, 2, v); b.alignment = LT; b.border = BORDER
    r += 1
 
# ---------------- Sheet2 分市州汇总 ----------------
ws2 = wb.create_sheet('分市州汇总')
heads = ['序号', '市州', '2026年1-8月持续在规企业数(家)', '2025年1-8月规上企业数(家)',
         '2026年1-8月累计货运量(万吨)', '2026年1-8月累计货物周转量(万吨公里)', '2026年1-8月累计平均运距(公里)',
         '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 年持续在规企业(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):
    c = ws2.cell(3, j, h)
    c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
    ws2.column_dimensions[get_column_letter(j)].width = widths[j - 1]
ws2.row_dimensions[3].height = 46
 
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))
    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 = CT if j <= 4 else RT
        if j in (5, 6, 7, 8, 9, 10):
            cell.number_format = NF2
        elif j in (11, 12, 13):
            cell.number_format = NFP
rr += 1
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, 9, 10):
        cell.number_format = NF2
    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, 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))
ws3.row_dimensions[1].height = 26
for j, h in enumerate(h3, 1):
    c = ws3.cell(2, j, h)
    c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
    ws3.column_dimensions[get_column_letter(j)].width = w3[j - 1]
ws3.row_dimensions[2].height = 40
 
rr = 2
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))
    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 = NFP if j == 14 else NF2
rr += 1
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
    cell.fill = TOT_FILL
    cell.font = Font(bold=True)
    cell.alignment = CT if j <= 3 else RT
    if j >= 4:
        cell.number_format = NFP if j == 14 else NF2
ws3.freeze_panes = 'D3'
 
# ---------------- Sheet4 企业明细 ----------------
ws4 = wb.create_sheet('企业明细')
h4 = ['序号', '市州', '企业名称', '企业代码', '统一社会信用代码',
      '2026年1-8月货运量(万吨)', '2026年1-8月周转量(万吨公里)', '2026年1-8月累计平均运距(公里)',
      '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 月;同期值与同比仅对两年均持续在规的 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))
ws4.row_dimensions[1].height = 26
for j, h in enumerate(h4, 1):
    c = ws4.cell(2, j, h)
    c.font = Font(bold=True); c.fill = HDR_FILL; c.alignment = CT; c.border = BORDER
    ws4.column_dimensions[get_column_letter(j)].width = w4[j - 1]
ws4.row_dimensions[2].height = 40
 
order = {c: i for i, c in enumerate(CITY_ORDER)}
rows_sorted = sorted(rows, key=lambda x: (order.get(x['city'], 99), -(x['t26'])))
rr = 2
for i, x in enumerate(rows_sorted, 1):
    rr += 1
    d26, d25 = dist(x['f26'], x['t26']), dist(x['f25'], x['t25'])
    vals = [i, x['city'], x['en'], x['ec'], x['ucc'],
            x['f26'] / 10000.0, x['t26'] / 10000.0, d26,
            (x['f25'] / 10000.0) if x['cmp'] else None,
            (x['t25'] / 10000.0) if x['cmp'] else None,
            d25 if x['cmp'] else None,
            growth(d26, d25) if x['cmp'] else None,
            '是' if x['cmp'] else '否']
    for j, v in enumerate(vals, 1):
        cell = ws4.cell(rr, j, v)
        cell.border = BORDER
        cell.alignment = LT if j in (3, 4, 5) else (CT if j in (1, 2, 13) else RT)
        if j in (6, 7, 8, 9, 10, 11):
            cell.number_format = NF2
        elif j == 12:
            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 口径对比 ----------------
ws6 = wb.create_sheet('口径对比')
h6 = ['序号', '市州', '2026年1-8月累计平均运距(公里)\n[持续在规企业636家]',
      '2025年1-8月累计平均运距(公里)\n[全部规上企业]', '官方口径同比(%)',
      '2025年1-8月累计平均运距(公里)\n[同企业545家]', '同企业口径同比(%)', '两者差异(百分点)', '说明']
w6 = [6, 14, 26, 24, 15, 24, 15, 16, 46]
ws6.cell(1, 1, '两种同比口径对比(官方口径 = 与汇总表「货运」页 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, '注:交付主表(分市州汇总)采用官方口径;本页两个口径并列,差异大的市州多为两年间规上企业名单变动(停产、新增、拆分)所致,供业务核对参考。')
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 口径校验 ----------------
ws5 = wb.create_sheet('口径校验')
ws5.sheet_view.showGridLines = False
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, '口径校验与对账').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
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 市州汇总合计'),
    ('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):
        cell = ws5.cell(i + 2, j, v)
        cell.border = BORDER
        cell.alignment = CT if i == 1 else (LT if j in (1, 5) else RT)
        if i == 1:
            cell.font = Font(bold=True); cell.fill = HDR_FILL
        elif j in (2, 3, 4):
            cell.number_format = NFP if row[0].endswith('同比') else NF2
 
os.makedirs(OUT_DIR, exist_ok=True)
wb.save(OUT)
print('OK ->', OUT)
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']:
        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 '-'))