| | |
| | | # -*- coding: utf-8 -*- |
| | | """è´§è¿ææ¥ãæç»å¨è§ä¼ä¸ãåå¸å· 1-8 æç´¯è®¡è¿è·å忝 å°è´¦çæ |
| | | """è´§è¿ææ¥ãæç»å¨è§ä¼ä¸ãåå¸å· 1-8 æç´¯è®¡è¿è·å忝 å°è´¦çæï¼v2ï¼åæ¯åºææ¹ä¸ºå®æ¹å
¨å£å¾ï¼ |
| | | |
| | | æ°æ®æºï¼traffic_audit.h2032_enterprise_monthlyï¼ãé路货ç©è¿è¾æåº¦ç产æ
åµãH203-2 ä¼ä¸ææ¥ï¼ |
| | | 导åºä»¶ _tmp_persist_scope.tsvï¼ä¼ä¸çº§ç´¯è®¡ï¼ã_tmp_persist_scope_monthly.tsvï¼ä¼ä¸çº§åæï¼ |
| | | å£å¾ï¼ |
| | | · æç»å¨è§ä¼ä¸ = 2026 å¹´ 1-8 æè¿ç» 8 个æå䏿¥ææ¥çä¼ä¸ï¼ |
| | | å£å¾ï¼v2 修订ï¼ï¼ |
| | | · æç»å¨è§ä¼ä¸ = 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` åä¸å£å¾ï¼ |
| | | v1 误ç¨ã两年åæç»å¨è§ç 545 å®¶ãä½åºæï¼å¯¼è´åæ¯åå°ï¼å·²ä¿®æ£ã |
| | | """ |
| | | import io, os |
| | | from openpyxl import Workbook |
| | |
| | | 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 = ['æ¦æ±å¸', 'é»ç³å¸', 'åå °å¸', '宿å¸', 'è¥é³å¸', 'éå·å¸', 'èé¨å¸', 'åæå¸', 'èå·å¸', |
| | | 'é»åå¸', 'å¸å®å¸', 'éå·å¸', 'æ©æ½å·', '仿¡å¸', 'æ½æ±å¸', '天é¨å¸', 'ç¥åæ¶æåº'] |
| | |
| | | |
| | | 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 æå
¨å£å¾åºæ ---------- |
| | | 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') |
| | |
| | | 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() |
| | | |
| | |
| | | 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 |
| | |
| | | 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 å®¶ï¼å
¨æä¸è´ï¼' |
| | | 'å£å¾ç»ç¨æ· 2026-09-18 确认ï¼ã' % tot['n']), |
| | | ('累计è¿è·å£å¾', '累计平åè¿è·ï¼å
¬éï¼= 1â8 æç´¯è®¡è´§ç©å¨è½¬é ÷ 1â8 æç´¯è®¡è´§è¿éï¼å æå¹³åï¼ãæºè¡¨åä½ä¸ºå¨ãå¨å
¬éï¼' |
| | | 'æ¬è¡¨æç®ä¸ºä¸å¨ãä¸å¨å
¬éï¼æ¯å¼ä¸åã'), |
| | | ('忝å£å¾', '忝 = 2026 å¹´ 1â8 æç´¯è®¡å¹³åè¿è· ÷ 2025 å¹´ 1â8 æç´¯è®¡å¹³åè¿è· â 1ã' |
| | | '为ä¿è¯å¯æ¯ï¼åæ¯åªå¨ã两年åæç»å¨è§ãç %d å®¶ä¼ä¸ä¸è®¡ç®ï¼å¦æ %d å®¶ 2026 å¹´æç»å¨è§ä¼ä¸ï¼' |
| | | '2025 å¹´ 1â8 ææ è¿ç»ä¸æ¥è®°å½ï¼ä¸åä¸åæ¯ï¼ã' % (tot['n_cmp'], tot['n'] - tot['n_cmp'])), |
| | | ('忝å£å¾', '忝 = 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 å®¶æç»å¨è§ä¼ä¸éæ·æ°æ®ï¼ãå£å¾æ ¡éªã为æç»ä¸æ±æ»å¯¹è´¦ã以åä¸å·²åå¸å
¨çæ°æ®çæ¯å¯¹ã'), |
| | | ('æ¥è¡¨æ', 'æ¬æç´¯è®¡ 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 |
| | |
| | | |
| | | # ---------------- 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æç´¯è®¡å¹³åè¿è·(å
¬é)[坿¯å£å¾]', '累计è¿è·åæ¯(%)', |
| | | '累计货è¿é忝(%)', '累计货ç©å¨è½¬é忝(%)', '夿³¨'] |
| | | 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 å¹´æç»å¨è§ä¼ä¸å
¨å£å¾ï¼%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): |
| | |
| | | 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)) |
| | |
| | | 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 |
| | |
| | | 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 ä¼ä¸æç» ---------------- |
| | |
| | | '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)) |
| | |
| | | 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 å£å¾å¯¹æ¯ ---------------- |
| | | 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 = 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, 'å£å¾æ ¡éªä¸å¯¹è´¦').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): |
| | |
| | | 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('坿¯å£å¾ 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 '-')) |