
故事背景:
上海此轮疫情到现在已经2周多了,周边的许多同事所在的小区都出现了确诊或者密接,然后小区就封闭了,被迫享受带薪休假14(或者2+12)天。只有我,小区一天没封过,上班一天没落下。
重点是今天周末去公司加班,处理上周五发版后遗留下来的问题。这个时候,之前的一个高中舍友,突然给我打电话,让我帮他处理点事情。
故事背景也是因为青岛受到此轮疫情的波及,需要排查镇上的一些流动人口,进行核酸排查。所以呢,基层的人员就开始挨个打电话,但是有的信息比较过时,有的有改动,而且分了好多人去打电话,最终把结果汇总起来。
大概的内容就是这样,不过因为涉及一些个人公民的隐私信息,我让他给我发几个样例,然后把想要的结果说一下。
产品需求:
最终大概的需求是现在有4个excel表。其中3个是子表,1个汇总表。3个表中都是人员信息,主要是身份证号码、联系电话、工作单位和地址等基本信息。各个表中的关系都通过身份证号码来关联。
现在需要以汇总表为准,将汇总表的信息更新到子表中。如果子表中身份证号码没有出现在汇总表中,则单独标注。如果汇总表中的身份证号码也没有在子表中出现过,也单独汇总。
这骚货(高中同学,之前的宿舍称号叫骚X)说本来呢,之前数据很少,自己搜索一下,复制粘贴就行了。现在太多了,好几万条,不可能一条一条去核对。
我跟他说,你学会一个包,这些基本问题都全部解决了。
解决思路:
问题很简单,Pandas解决一切excel的问题。其实,这个问题可以用excel的vlookup的公式来处理。Excel功能本身很强大,好好学会,基本的表格处理都能解决。不过呢,我没过excel,只是会简单的做个排序,筛选。
那么用pandas怎么处理?
首先读取汇总表作为全局变量dataframe,然后依次读取子表,遍历每一行,找到身份证那一栏的信息,去汇总表找到相应的series,可能有多个,默认就取第一个,把汇总表的series信息覆盖到子表中,如果没有找到,则在最后一栏中标注成“总表未找到”。然后把每次遍历的身份证号码存到全局变量的list中,方便最后统计汇总表中出现,但是3个子表都未出现过的信息。
代码演示:
import os
import pandas as pd
# 指定读取文件夹
path = "E:\\run"
path_list = os.listdir(path)
all_name = None
for i in path_list:
# 汇总表以全域开头
if i.startswith("全域"):
all_name = i
# 找到汇总表,并且读取
all_info = pd.read_excel(path + '\\' + all_name, dtype={'身份证号': str, '*联系电话': str}, header=None)
# 下面是处理汇总表的表头,汇总表的第二栏才是真正的表头
all_info = all_info.iloc[1:]
arr = all_info.values
new_df = pd.DataFrame(arr[1:, 1:], index=arr[1:, 0], columns=arr[0, 1:])
new_df.index.name = arr[0, 0]
all_info = new_df
all_info.index = range(len(all_info))
# 定义一个全局变量,存放子表出现过的身份证号码
all_id_cards = []
def get_one_info(id_card):
"""
通过身份证号码查找汇总表中的信息
:param id_card: 身份证号码
:return: series对象
"""
df1 = all_info[all_info['身份证号'].isin([id_card])]
# 如果没找到,返回None
if df1.empty:
return None
else:
# 如果找到了,就返回第一个,可能有多个,并且把出现过的身份证存全局变量
all_id_cards.append(id_card)
return df1.iloc[0]
def get_child_excel(file_name):
"""
遍历每个子表的信息,一一核对
:param file_name:子表文件名字
:return:
"""
print(path + '\\' + file_name)
# 读取字表
cdf1 = pd.read_excel(path + '\\' + file_name, dtype={'ID_CARD': str, 'MOBILE': str})
cdf2 = cdf1.copy()
# 只有f1 和ID_CARD取子表的,其余的信息取汇总表。f1是序号
cdf2 = cdf2[['f1', 'ID_CARD']]
cdf2['姓名'] = ''
cdf2['户籍地'] = ''
cdf2['现居住地'] = ''
cdf2['联系电话'] = ''
cdf2['工作单位名称'] = ''
cdf2['工作单位地址'] = ''
cdf2['备注'] = ''
cdf2['汇总表匹配'] = ''
columns = list(cdf2.columns)
# 遍历子表每一行
# index 索引 row是数据内容 dataframe 迭代出来的对象
for index, row in cdf1.iterrows():
# 调用前面的查找方法
res = get_one_info(row['ID_CARD'].strip())
if res is None or res.empty:
# 如果没有在汇总表中找到对应的信息,则标注,把部分信息引用原表,其余的为空
cdf2.iloc[index, columns.index('大表匹配')] = "总表未找到信息"
cdf2.iloc[index, list(cdf2.columns).index('姓名')] = row['full_name']
cdf2.iloc[index, list(cdf2.columns).index('联系电话')] = row['MOBILE']
cdf2.iloc[index, list(cdf2.columns).index('工作单位地址')] = row['ADDRESS']
else:
# 如果在汇总表中出现,则把总表的信息copy到子表中
cdf2.iloc[index, list(cdf2.columns).index('姓名')] = res['姓名']
cdf2.iloc[index, list(cdf2.columns).index('户籍地')] = res['户籍地']
cdf2.iloc[index, list(cdf2.columns).index('现居住地')] = res['现居住地']
cdf2.iloc[index, list(cdf2.columns).index('联系电话')] = res['联系电话']
cdf2.iloc[index, list(cdf2.columns).index('工作单位名称')] = res['工作单位名称']
cdf2.iloc[index, list(cdf2.columns).index('工作单位地址')] = res['工作单位地址']
cdf2.iloc[index, list(cdf2.columns).index('备注')] = res['备注']
cdf2.iloc[index, list(cdf2.columns).index('汇总表匹配')] = ""
# 匹配完成后的表格,加上更新的后缀
file_name = file_name.replace(".xlsx", "_更新.xlsx")
# 保存文件
cdf2.to_excel(path + '\\' + file_name, index=False)
def get_file():
"""
读取文件夹内的全部Excel文件
:return:
"""
n_path_list = []
for i in path_list:
if i.startswith("poli") or i.startswith("全域") or i.endswith("更新.xlsx") or i.endswith("未找到.xlsx"):
pass
else:
n_path_list.append(i)
return n_path_list
def get_other_info():
"""
把汇总表中从未在子表中出现过的信息统计到一个表中
:return:
"""
# 获取全部子表中出现过的号码并且去重
id_cards = list(set(all_id_cards))
# 创建建一个新的DataFrame
other = pd.DataFrame(columns=["姓名", "身份证号", "户籍地", "居住地", "联系电话", "工作单位名称", "工作单位地址", "备注"])
# 遍历汇总表
for index, row in all_info.iterrows():
id_card = row['* 身份证号']
# 如果在list中就跳过,没有则添加到新创建的DataFrame中
if id_card in id_cards:
pass
else:
new = pd.DataFrame({'姓名': row['姓名'],
'身份证号': row['身份证号'],
'户籍地': row['户籍地'],
'居住地': row['现居住地'],
'联系电话': row['联系电话'],
'工作单位名称': row['工作单位名称'],
'工作单位地址': row['工作单位地址'],
'备注': row['备注']
},
index=[1])
other = other.append(new, ignore_index=True)
# 保存文件
other.to_excel(path + '\\' + "子表中未找到.xlsx", index=False)
if __name__ == '__main__':
n_path_list = get_file()
for i in n_path_list:
print(i)
get_child_excel(i)
get_other_info()故事后续:
因为他电脑里没有Python环境,我直接给他代码并不能用,所以最后把这个脚本打成exe可执行的文件,直接发给他运行,最终效果是这样。

其实,全部的功能处理完全都用Pandas就可以来完成,当然除了Pandas也有一些excel的处理工具,但是想这种规范化的数据,使用Pandas还是相当便利的。
不仅仅是这骚货,也有一些其他朋友也会找我处理点数据。其实对于代码来说,如果只有三五行数据,完全没必要。
代码的发挥场景是,数据量比较大,再就是需要经常做的工作,这样把流程性的东西做成一个脚本,以后每次跑一下就要可以得到结果。
后面呢,我会在空闲时间,写个如何用Pandas简单的处理点数据的教程,让这些整天想白嫖我的朋友们学习学习。
好了,今天就分享到这里,我是马拉松程序员,可不至于代码!