算是杂记,经常在微信里看到9块9学python处理excel,一直没机会。正好最近有个本科室友找我,问能不能给他处理个工程测算表,指明了要用python来做。本科的铁哥们,这忙咱得帮。说干就干,首先当然是不去报班买9块9的课程。
首先,看看要处理的表是啥样,他想要的处理结果是啥样
老铁做测量多年,用excel表滑溜的很,他是为了报单位的创新项目,所以才想用python提高一下科技含量。他对自己要处理的表和处理成啥样了然于心。
原表是一张arcgis导出的各地类的测量面积表,里面有上千个图斑,也就对应着上千条记录。要处理成一张汇总表,根据项目、镇界统计各地类的面积。看着不算难,但是各类统计以及按格式填表,着实让我忙活了一阵。
以下为原始数据表:
统计汇总表:
可以从汇总表中看到,统计分两个维度,一个是根据项目及镇、村来统计各地类的总面积,还有一个维度是统计该区域内各地类的总面积。
高效统计法推荐
这里不得不提到一个python库——pandas,能够像操作数据库那样进行分组操作。
data_arcgis = pd.read_excel(inputfile, inputsheetname)
grp1 = data_arcgis.groupby(['权属', '镇界', '界线', 'DLMC', '地类', '分区']).agg({'亩': ['sum']}).reset_index()
通过权属、镇界、地类等对原始数据进行最小粒度的分组,这样操作就能获得最小粒度数据,为后面的汇总打下数据基础
grp3 = grp2.groupby(['镇界'])
Town_list = list(grp3.groups.keys()) ###获取镇界枚举值
通过该方法获得组内的枚举值,为写入汇总表里的镇、村准备数据源
for town in Town_list:
grp4 = grp2[grp2['镇界'] == town]
grp5 = grp4.groupby(['分区'])
Fq_list = list(grp5.groups.keys()) ###获取分区枚举值
town_total = grp4.agg({'亩': ['sum']}).reset_index()['亩'].iloc[0,0]
#print(town + ':' + str(town_total))
sheet.cell(row = startrowindex + lineindex, column=4).value = town
sheet.cell(row = startrowindex + lineindex, column=4).font = font
sheet.cell(row = startrowindex + lineindex, column=8).value = town_total
sheet.cell(row = startrowindex + lineindex, column=8).font = font
lineindex = lineindex + 1
for fq in Fq_list:
grp6 = grp4[grp4['分区'] == fq]
grp7 = grp6.groupby(['界线'])
Jx_list = list(grp7.groups.keys()) ###获取界线枚举值
for jx in Jx_list:
grp8 = grp6[grp6['界线'] == jx]
grp9 = grp8.groupby(['地类'])
Dl_list = list(grp9.groups.keys()) ###获取地类枚举值
jx_total = grp6[grp6['界线'] == jx].agg({'亩': ['sum']}).reset_index()['亩'].iloc[0,0]
#print(' ' + fq + ':' + jx + ':' + str(jx_total))
sheet.cell(row = startrowindex + lineindex, column=2).value = fq
sheet.cell(row = startrowindex + lineindex, column=2).font = font
sheet.cell(row = startrowindex + lineindex, column=5).value = jx
sheet.cell(row = startrowindex + lineindex, column=5).font = font
sheet.cell(row = startrowindex + lineindex, column=8).value = jx_total
sheet.cell(row = startrowindex + lineindex, column=8).font = font
for dl in Dl_list:
dl_total = grp8[grp8['地类'] == dl].agg({'亩': ['sum']}).reset_index()['亩'].iloc[0,0]
dic_dl_value[dl] = dic_dl_value[dl] + dl_total
total_sum = total_sum + dl_total
sheet.cell(row = startrowindex + lineindex, column=dic_dl_column[dl]).value = dl_total
sheet.cell(row = startrowindex + lineindex, column=dic_dl_column[dl]).font = font
#print(' ' + dl + ':' + str(dl_total))
lineindex = lineindex + 1
依次遍历镇界、分区、界线、地类,最后在地类循环中,对相同地类进行累计求和。至此就可以获得相应镇界、分区、界线下的地类汇总面积,同时通过lineindex来计算写入的行号,保证了运算结果写入到争取的行列内。
令人头疼的难点
方法不难,最头疼的还是原始数据的不规范带来的不必要的程序消耗。因为原始数据中地类写入的随意性,以及新增临时地类(即不在汇总表内的地类),则需要进行额外的处理。
处理原则是,只要原始数据中地类名称有和汇总表内的字段名相匹配的,则认为命中,记录下该字段在excel中的列号,如果不在,则放入汇总表内的最后列。程序跑完后,由人工介入处理。
def setDlcolumn(all_dl_list, dl_type_series, dic_dl_column):
dl_column_length = dl_type_series.shape[0]
dl_column_length_bak = dl_column_length
for dl_target in all_dl_list:
colum_index = 0
flag = 0
for dl_type in dl_type_series.array:
colum_index = colum_index + 1
if str(dl_type) == 'nan':
continue
if dl_target in dic_dl_column and dic_dl_column[dl_target] < dl_column_length_bak: ###字典里已存在该地类
flag = 1
break
if dl_target in dl_type: ###如果arcgis中地类包含在样表中,则记录下列号
flag = 1
dic_dl_column[dl_target] = colum_index
break
if flag == 0:
dl_column_length = dl_column_length + 1
dic_dl_column[dl_target] = dl_column_length
return dic_dl_column
处理效果
最后输出的结果,数值和统计结果一致,样式上还有所欠缺,需要利用openpyxl对表格进行样式修饰。基本达到老铁预期。