pandas筛选、修改并实现分类

虽然学pandas有段时间了,但是真正应用的场合其实很少,大多数功能用excel就已经足够,最近发现wps表格某些功能比 excel还好用,比如筛选重复身份证,wps表格就做得比excel2016要好。

最近发现了一个比较麻烦的问题,表格内容大概如下:

 需求是把这些数据按村分类,听着是挺简单的,主要就是这个通讯地址数据不完整,存在以下问题:

1.有些地址没写村,如:护龙镇;

2.有些地址存在多个村,如:护龙镇木厂村护龙镇金盆村4组

3.有些地址中有空格,如:两板桥镇街 村1;

4.有些村沿用的是合并前的村名,如:鲤云村、聪明村现合并为聪明村

5.有些地址直接是空白,什么都没有;

我个人思考的结果是,尽量把所有数据都分到各村去,实现找不到村的数据,就划分到社区去。护龙镇只有1个社区(金盆村、长林社区合并为长林社区)。

一、处理非法字符

import pandas as pd
import re

df = pd.read_excel("护龙-新冠疫苗第一针接种明细(截止9月6日).xls",header=1,index_col=0)
df2 = df.fillna("护龙镇")

这个其实很简单,就是如果地址为空,就修改为”护龙镇“。但是这种方法并不精确,把所有的非法字符都修改了,而且可能还有其它的情况,如:

大致可以采用以下的方式:

df2 = df.copy()
df2["通讯地址"] = df["通讯地址"].fillna("护龙镇")
#用正则表达式匹配,当然一般先单独测试筛选的数据是否准确,再做下列修改
df2.loc[df2["通讯地址"].str.match("护龙镇\d+"),"通讯地址"]="护龙镇"

 虽然上面的方式并不通用,但是方式方法还是很可取的,不管遇见什么情况,都可以采取类似的方式。

二、对比户籍册

 

将地址为”护龙镇“的数据,通过身份证比对户籍花名册,将此人的详细地址找出来,找不到的就还是默认为”护龙镇“。

户籍信息大体如下:

 实现的步骤大体是,先判断地址是不是“护龙镇”,如果是就取出此行数据的“证件号码”,即身份证,然后再通过此身份证号码到户籍花名册中找到对面的行数据,然后再把此行数据的“村别”取出来。

df_hu = pd.read_excel("护龙镇户籍.xlsx",header=0)
def modify_address(sers):
    address = sers['通讯地址']
    idcard = sers['证件号码']
    
    if(address == "护龙镇"):
        dff=df_hu[df_hu['身份证']==str(idcard)]
        if(dff.shape[0]>0):#有数据,就取出地址
            return dff.iloc[0]['村别']
    return address
    
df2['通讯地址'] = df2.apply(modify_address,axis=1)

 上面代码的功能与 excel中的vlookup函数非常像,但是在pandas中要使用此功能并不容易,虽然细粒度的操作每条数据。上面代码中,用到了apply函数,axis参数=1的意思是将数据以行为单位,传递给modify_address函数。

df_hu[df_hu['身份证']==str(idcard)]

上面代码的意思就是通过身份证号码找到户籍花名册中,和这个身份证相等的那条记录,注意这记录是DataFrame,也就是说pandas并不确定找到的记录只有一条,所以要通过iloc方法来取:

dff.iloc[0]['村别']

最后返回的结果其实是一个Series,它由每一行数据修改后的地址合并而成。所以需要重新赋值给原DataFrame:

df2['通讯地址'] = df2.apply(.....)

上面代码可以再精简下,就是只传入通讯地址为“护龙镇”的数据:

def modify_address(sers):
    address = sers['通讯地址']
    idcard = sers['证件号码']
    
    dff=df_hu[df_hu['身份证']==str(idcard)]
    if(dff.shape[0]>0):#有数据,就取出地址
        return dff.iloc[0]['村别']
    return address
    
# df2['通讯地址'] = df2.apply(modify_address,axis=1)
df2.loc[df2['通讯地址']=="护龙镇","通讯地址"] = df2[df2['通讯地址']=="护龙镇"].apply(modify_address,axis=1)

 虽然看起来要复杂一点,但是性能肯定会好很多。

三、找出村别

每一个村都是三个字,即木厂村、新桥村等。

bool_cun = df2['通讯地址'].str.contains("聪明村|鲤云村|木厂村|遂安村|高棚村|高鹏村|新桥村|河堰村|夹石村|四新村|玉泉村|高红村|天堂村|天山村|羊角村|湾桥村|川七村")
df3 = df2[bool_cun]#取出为True的数据

如果一条记录的通讯地址包含了上面contains函数内的某一个村名,那么那条记录对应的数据就是True,否则为False,如下:

 只取索引值为True的数据,这个确实有点意思,这种方式不像编程,有点像一种特有的规则。

四、按合并后的村进行分类

def get_second_cun(s):#获得匹配到的第二个XX村
    #\s是为了处理有空格的情况,如:两板桥镇街 村1
    pattern = re.compile("[\w\s]{2}村")
    result = pattern.findall(s)
    return result[1]

list_cun = ["聪明村","鲤云村","木厂村","遂安村","高棚村","高鹏村","新桥村","河堰村","夹石村","四新村","玉泉村","高红村","天堂村","天山村","羊角村","湾桥村","川七村"]

def get_cun(df):#获得匹配到的第一个村别
    s=df['通讯地址']
    pos=s.find("村")
    start=pos-2
    end=pos+1
    cun = s[start:end]
    #如果前面匹配到的不是想要村别,比如:护龙镇金盆村护龙镇木厂村,取第匹配到的第二个村别
    if(cun not in list_cun):
        cun = get_second_cun(s)
    #如果遇见合并后已弃用的村,就返回合并的村别
    if(cun=="鲤云村"):
        return "聪明村"
    elif(cun=="高棚村"or cun=="高鹏村"):
        return "遂安村"
    elif(cun=="河堰村"):
        return "新桥村"
    elif(cun=="四新村"):
        return "夹石村"
    elif(cun=="高红村"):
        return "玉泉村"
    elif(cun=="天山村"or cun=="羊角村"):
        return "天堂村"
    elif(cun=="川七村"):
        return "湾桥村"
        
    return cun

df3 = df2[bool_cun]
df4=df3.copy()#用副本操作修改数据,不然有时会报警告
df4.loc[:,"合并村"] = df3.apply(get_cun, axis=1)

 上面的代码注释得还算详细,讲得重要的,比如查找的数据肯定包含下列村中的一个:

"聪明村|鲤云村|木厂村|遂安村|高棚村|高鹏村|新桥村|河堰村|夹石村|四新村|玉泉村|高红村|天堂村|天山村|羊角村|湾桥村|川七村"

 但是有个潜在的问题,那就是我们默认是取第一匹配到的村别,如果社区合并前的村(金盆村)而包含在通讯地址中,且排在前面,如:护龙镇金盆村护龙镇木厂村,那么我们取到的就会是金盆村,所以要判断:

if(cun not in list_cun):
        cun = get_second_cun(s)

如果不包含,就再取一次,取匹配到的第二个村别。

五、剩余的数据分配到社区

#社区1468
df5 = df2[~bool_cun]
df6 = df5.copy()
df6['合并村'] = "长林社区"

直接加一列“合并村”,并赋值为“长林社区即可”。

六、存储为excel文件

df4.to_excel('3.xlsx')
df6.to_excel("4.xlsx")

上面代码非常简单,把处理好的数据储存为excel。

七、指定添加列的位置

上面添加的“合并村”,默认是添加到最后一列的位置,我们也可以把它放在前面点,比如第二列的位置:

#虽然下面的df4.loc[:,"合并村"]可以直接增加列,但是默认是增加到最后面的一列
#下面这三行代码的作用是将“合并村"增加到指定位置,即第2列
col_name=df4.columns.tolist() 
col_name.insert(0,"合并村")
df4=df4.reindex(columns=col_name)

df4.loc[:,"合并村"] = df3.apply(get_cun, axis=1)

得到的结果大体如下:

 总结

上面代码所实现的功能,在 excel或者wps表格中都不可能实现,当然可以使用宏编程,但是结果应该要比这个复杂得多。因为python比VBA代码更精简、友好,再加上pandas这种专门操作表格的框架,所以代码精简了很多。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值