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
| from pathlib import Path from datetime import datetime
import pandas as pd from config import IMPORT_FILE, MANUAL_LEDGER_FILE
result_pub_col = '中标结果公示发布时间' pub_time_col = '招标(资格预审)公告发布时间' FILTER_COL = '是否复评'
LEFT_COLS = ['项目名称', '项目分类', '标段编号', '标段包名称', '招标人名称', '投资预算(万元)', pub_time_col, '开标开始时间', result_pub_col, '中标人', '中标价格(元)', '代理机构名称', FILTER_COL]
LEFT_DTYPES = { '项目名称': 'string', '项目分类': 'string', '标段编号': 'string', '标段包名称': 'string', '招标人名称': 'string',
'投资预算(万元)': 'Float64', pub_time_col: 'datetime64[s]', '开标开始时间': 'datetime64[s]', result_pub_col: 'datetime64[s]', '中标人': 'string',
'中标价格(元)': 'Float64', '代理机构名称': 'string', FILTER_COL: 'category', }
RIGHT_COL_MAP = { 1: '标段编号', 4: '项目状态', 5: '保证金(元)', 6: '保证金数量', 7: '保函数量', 8: '投资主体性质', 9: '所在辖区', 11: "招标监管部门", 13: "交易类别" }
RIGHT_DTYPES = { '标段编号': 'string', '项目状态': 'category', '保证金(元)': 'UInt32', '保证金数量': 'UInt16', '保函数量': 'UInt16',
'投资主体性质': 'category', '所在辖区': 'category', '招标监管部门': 'string', '交易类别': 'category', }
RENAME_MAP = { '开标开始时间': '开标日期', result_pub_col: '中标公告发布时间', '中标人': '拟中标单位', '项目分类': '项目行业分类', '标段包名称': '标段名称',
'保证金数量': '保证金', '保函数量': '保函', }
OUTPUT_COLS = [ '序号', '投资主体性质', '招标监管部门', '项目交易分类', '节支额(万元)',
'拟中标单位', '保证金递交方式', '代理机构名称', '交易类别', '开标日期',
'所在辖区', '招标方式', '项目行业分类', '投资预算(万元)', '项目名称',
'中标价格(万元)', '中标公告发布时间', '是否电子标', '保证金(元)', '标段名称',
'招标人名称', '项目状态', '保证金', '保函', '保函总额(元)',
'保证金总额(元)', '标段编号', ]
def process_data(): leftDf = pd.read_excel(IMPORT_FILE, header=1, usecols=LEFT_COLS, dtype=LEFT_DTYPES, ) leftDf = leftDf.loc[leftDf[FILTER_COL] == '否', [c for c in LEFT_COLS if c != FILTER_COL]] print(f'✅ 左表读取完成:{len(leftDf)} 行') print(leftDf.info())
rightDf = pd.read_excel(MANUAL_LEDGER_FILE, header=None, skiprows=2, usecols=list(RIGHT_COL_MAP.keys()), names=list(RIGHT_COL_MAP.values()), dtype=RIGHT_DTYPES, ) print(f'✅ 右表读取完成:{len(rightDf)} 行') print(rightDf.info())
merged = pd.merge( leftDf, rightDf, on='标段编号', how='left', indicator='匹配状态', )
unmatched = merged[merged['匹配状态'] == 'left_only'] if not unmatched.empty: print(f'⚠️ 警告:{len(unmatched)} 行未在右表找到匹配,标段编号:') print(unmatched['标段编号'].head().tolist())
merged = merged.drop(columns=['匹配状态'])
print(f'连接后的表,共 {len(merged)} 行') print(merged.info())
merged = merged.sort_values( by=[pub_time_col, '标段编号'], ascending=[True, True] ).drop(columns=[pub_time_col]).reset_index(drop=True)
merged['中标价格(万元)'] = merged['中标价格(元)'] / 10000 merged = merged.drop(columns=['中标价格(元)']) merged['节支额(万元)'] = merged['投资预算(万元)'] - merged['中标价格(万元)'] merged['保证金总额(元)'] = merged['保证金数量'] * merged['保证金(元)'] merged['保函总额(元)'] = merged['保函数量'] * merged['保证金(元)']
fixed_cols = { '项目交易分类': '建设工程', '保证金递交方式': '网银转账,电子保函', '招标方式': '公开招标', '是否电子标': '是' } for col, val in fixed_cols.items(): merged[col] = val merged[col] = merged[col].astype('category')
merged.rename(columns=RENAME_MAP, inplace=True)
merged.insert(0, '序号', range(1, len(merged) + 1))
print('列数', len(merged.columns), merged.columns) print(merged.info())
try: final = merged[OUTPUT_COLS] except KeyError as e: print(f'❌ 错误:输出列缺失 {e}') print(f'当前可用列:{merged.columns.tolist()}') raise
timestamp = datetime.now().strftime("%m%d_%H%M%S") output_path = Path(f'导出结果_{timestamp}.xlsx') final.to_excel(output_path, index=False, engine='openpyxl') print(f'✅ 导出成功:{output_path.absolute()}') return final
if __name__ == '__main__': process_data()
|