查看原文
其他

Python 教学 | Pandas 数据匹配(含实操案例)

专注Python教学的 数据Seminar 2023-07-24


Python教学专栏,旨在为初学者提供系统、全面的Python编程学习体验。通过逐步讲解Python基础语言和编程逻辑,结合实操案例,让小白也能轻松搞懂Python!
>>>点击此处查看往期Python教学内容

本文目录

一、前言

二、Excel数据匹配

三、Pandas数据匹配(四种方式)

  1. 左连接

  2. 右连接

  3. 内连接

  4. 外连接

  5. 数据匹配的注意事项

四、Pandas数据匹配的其他情形

  1. 连接字段不唯一

  2. 字段名称相同

五、总结

六、Python教程

本文共7920个字,阅读大约需要20分钟,欢迎指正!

Part1前言

数据合并常用在相同结构数据之间,是一种机械式拼接数据的做法,另一种同样十分常见的扩展数据的方式是数据匹配(Merge),常用在描述相同主体,但具有不同(字段)信息的两份数据之间,通过匹配的方式将两张表中的所有或部分信息都合并到一张表中,且不论匹配前两份数据顺序如何,匹配后都能保证不同个体的信息保持对应,不会出现“张冠李戴”的现象。在 Excel 中,匹配数据主要使用 Vlookup 系列函数;在 SQL(数据库)中,则依靠 LEFT JOININNER JOIN 等关键字;本文我们将学习如何在 Pandas 中进行数据匹配。

本教程基于 pandas 1.5.3 版本书写。本文中所有 Python 代码均在集成开发环境 Visual Studio Code (VScode) 中使用交互式开发环境 Jupyter Notebook 中编写,本文分享的代码请使用 Jupyter Notebook 打开。

💡后台回复关键词“20230630”即可获取本文所有演示代码以及演示用的数据。

Part2Excel 数据匹配

在 Excel 中,可以使用VLOOKUPHLOOKUP函数进行简单的数据匹配,这两个函数的使用方法可以点击下方链接查看教学视频(每个视频约 1 分 20 秒)。

  1. Excel VLOOKUP 函数使用方法:https://www.bilibili.com/video/BV1JK4y197A9/?p=83&spm_id_from=pageDriver&vd_source=2db6433a3a8e6067c12115a74cc74b58(复制链接至浏览器查看)
  2. Excel HLOOKUP 函数使用方法:https://www.bilibili.com/video/BV1JK4y197A9/?p=84&share_source=copy_web&vd_source=2efabe2748476f46396be13acacbf488(复制链接至浏览器查看)

以上两个函数在 Excel 和 WPS 中使用起来比较方便,但是对参与匹配数据也有很高的要求,首先是一次只能匹配一列数据,如果需要匹配多列,则需要反复操作,十分繁琐;其次是受限于 Excel 等 office 工具的限制,无法处理大数据集,虽然 Excel 表最多能存储 1048576 行数据,但是实际使用中发现数据量一旦超过 20 万行,连打开表格都变得十分艰难,更不要说使用函数匹配数据了。所以 Excel 中的匹配函数只适合处理小数据集,当我们需要对数十万甚至是百万级别的数据做匹配时,就需要使用其他工具了。

Part3Pandas 数据匹配(四种方式)

张三有两份企业数据,一份是工业企业数据,另一份是企业专利申请数据,他希望根据数据中的企业名称将两份数据联系起来,这样就能从不同的角度分析这两个数据。张三将这两个数据联系起来的过程就是数据匹配,在数据库(SQL)中,数据匹配被称为连接或联结,数据科学中共有四种数据匹配方式,即左连接右连接内连接外连接

左连接
右连接
内连接
外连接

接下来向大家介绍如何使用 Pandas 进行以上四种方式的数据匹配,在此之前我们先使用 Pandas 读取本文演示用的数据(张三的两份数据)。为了更直观地展示匹配的结果和清楚地了解匹配的规律,演示数据中仅包含少许样例数据。

import pandas as pd         # 导入 pandas 库,起别名为 pd
data_工企 = pd.read_excel('./工业企业数据样例.xlsx')   # 读取数据
data_专利 = pd.read_excel('./专利数据样例.xlsx')
data_工企,data_专利

观察上图可知,两份数据描述的都是企业数据,但并不完全是同一批企业,且工企数据中一家企业占一行,而专利数据则是一条申请记录占一行,那么一家企业就有可能有多条专利申请记录。

在 Pandas 中,主要通过pd.merge()函数实现数据匹配,该函数形式和主要参数的含义如下。

data_res = pd.merge(left,
                    right,
                    left_on=None,
                    right_on=None,
                    # on=None,  on 参数与 left_on 和 right_on 互斥
                    how='inner')
参数名称含义
left必要参数,表示左表,参数必须是 DataFrame 或 Series 类型,一般是 DataFrame。
right必要参数,表示右表,类型与 left 一致。
left_on左表中连接字段的名称,如果存在多个连接字段,可以传入一个包含多个连接字段字段名的列表。该参数必须与 right_on 参数一同使用,且不能与 on 参数一起使用。
right_on右表中连接字段的名称,如果存在多个连接字段,可以传入一个包含多个连接字段字段名的列表。该参数必须与 left_on参数一同使用,且不能与 on 参数一起使用。
on如果左表和右表的连接字段名称完全一样,那么只需要把连接字段的字段名传给该参数即可(多个字段时可传入列表)。该参数与 left_on 和 right_on 参数互斥,使用该参数后就不能再使用 left_on 和 right_on 这两个参数
how匹配的方式,可选的参数有字符串 'left','right','inner' 和 'outer',其含义显而易见,默认的参数值为 'inner',即内连接。

pd.merge()函数的功能是将传入的左表和右表匹配起来,并返回匹配后的结果,例如上述介绍函数形式的代码就是将匹配的结果赋给一个叫data_res的变量。

1左连接

数据匹配是一个两张表之间的操作,参与匹配的两个表,我们可以称其中一个为左表,那么另一个自然就是右表。而左连接就是以左表为基准,保留左表中的所有数据,并根据两表中的连接键(连接字段)将右表中的字段信息对应地添加到左表中。这可能会有些抽象,下面我们通过实际操作来理解左连接操作。

data_leftmerge = pd.merge(left=data_工企,         # 左表
                          right=data_专利,        # 右表
                          left_on='工业企业名称',  # 左表的连接字段
                          right_on='专利申请人',   # 右表的连接字段
                          how='left')             # how='left' 表示进行左连接
data_leftmerge

上述操作是根据两表中的存放企业名称的'工业企业名称''专利申请人'两个字段分别为连接字段进行匹配,根据参数含义表中的介绍,由于两表中的连接字段的名称不一样,所以需要分别使用left_onright_on 参数分别指定连接字段。如果两表中连接字段的名称都是'企业名称',那我们直接设置参数on='企业名称'就可以了(不再使用left_onright_on参数),这样做也会更加方便。

观察上面的左连接结果,我们可以从中获取到很多信息,来看笔者分析。

  1. 匹配后,匹配结果中同时包含了工企数据中的字段和专利数据中的字段,且这些字段内容是根据企业的名称(连接字段)对应起来的
  2. 匹配结果中存在两行数据的专利信息部分为空值,这是因为专利表中并没有常州里戈勃劳伊特新亚电机有限公司河南省雅宝家俱有限公司两家企业的专利信息,所以匹配后对应的位置就会是空值。
  3. 匹配结果中共 10 条数据,比工企数据还多了一条,这是因为专利表中存在两条双恒阀门有限公司的专利申请记录。所以匹配过程中会有“一对多”的匹配操作,即将右表中双恒阀门有限公司的两条数据分别对应到左表中。
  4. 由于左连接是保留左表数据,所以右表专利数据中不在左表中的吴江市隆泰喷织厂等三家企业的专利申请数据不会出现在匹配结果中。

2右连接

右连接是指在匹配过程中,以右表为基准,保留右表中的所有数据,并根据两表中的连接键(连接字段)将左表中的字段信息对应地添加到右表中。事实上右连接是一个很鸡肋的存在,因为我们只需要在左连接代码中将左表与右表的位置互换,就可以得到与右连接几乎一样的结果。

下面我们先以工企数据为左表,专利数据为右表进行一次右连接,匹配代码和匹配结果如下。

data_rightmerge = pd.merge(left=data_工企,        # 左表
                          right=data_专利,        # 右表
                          left_on='工业企业名称',  # 左表的连接字段
                          right_on='专利申请人',   # 右表的连接字段
                          how='right')            # how='right' 表示进行右连接
data_rightmerge

根据上图所示匹配结果,可以发现右连接与左连接的匹配逻辑基本一样,只是匹配的基准数据变成了右表。当右表专利表中存在两条双恒阀门有限公司的专利数据时,匹配结果中也保留了两条数据,但是这两条数据的工企信息部分是一样的数据,这是一个“多对一”的情形。

下面我们再以专利数据为左表,工企数据为右表进行左连接(注意左右表顺序改变了,left_on 和 right_on 参数值也跟着改变),看看匹配结果与上述右连接结果有何区别。

data_leftmerge2 = pd.merge(left=data_专利,        # 左表
                          right=data_工企,        # 右表
                          left_on='专利申请人',    # 左表的连接字段
                          right_on='工业企业名称'# 右表的连接字段
                          how='left')             # how='left' 表示进行左连接
data_leftmerge2

仔细观察上图发现,左表和右表互换位置后左连接的匹配结果与没有互换位置的右连接仅仅是数据字段的位置不太一样,这也侧面说明了无论使用哪种匹配方式,pd.merge()函数的匹配结果一定是左表数据字段在前方(左侧),右表数据字段在后方(右侧)

3内连接

内连接是pd.merge()函数的默认连接方式,是指根据两个表的连接字段,保留左表与右表中的共同内容,类似于集合运算中的取交集。例如工企数据和专利数据中,都存在山东杭萧钢构有限公司的数据,那么内连接的结果中就一定有这个企业的所有信息,而只出现在两者其中之一的企业,将不会得到保留。下面是工企数据和专利数据进行内连接的代码和匹配结果。

data_innermerge = pd.merge(left=data_工企,         # 左表
                          right=data_专利,        # 右表
                          left_on='工业企业名称',  # 左表的连接字段
                          right_on='专利申请人',) # how 参数的默认值是 'inner', 所以不必刻意设置
data_innermerge

根据上图所示内连接匹配结果可知,仅保留了左表与右表共有的企业数据。

4外连接

外连接也称全连接,其与内连接的逻辑完全不同,功能是同时保留左表与右表所有出现过的数据,尽可能地匹配数据,匹配不上的内容设置为空,与集合运算中的取并集类似。下面是对两份数据做外连接的代码和匹配结果。

data_outermerge = pd.merge(left=data_工企,
                          right=data_专利,
                          left_on='工业企业名称',
                          right_on='专利申请人',
                          how='outer')
data_outermerge

以上就是 Pandas 四种匹配方式的操作代码,上文讲述的匹配案例是使用企业名称为连接字段对工企库和专利库做匹配,目的是让大家更快速地理解匹配的逻辑和规律。不过这在现实中是无法实现的,因为企业的名称随时都可能发生变更,加之数据质量、标准差异等各种问题,全量工企库和专利库之间的匹配一直以来都是一个不小的问题。不过我们已经在工商库、工企库、专利库、海关库等数据库之间的横向匹配方面做了许多工作,感兴趣的朋友可以点击下方链接跳转阅读原文。

👉点击查看“企研数据处理工作论文系列”更多文章

5数据匹配的注意事项

Pandas 是一个极其强大的数据处理工具,在面对超大体量的数据时,依然能够分批轻松处理。但是数据匹配是一个例外,即使是用 Python 来做匹配也无法逃过内存的限制。这是因为 Pandas 的数据操作都是在内存中进行的,而数据匹配过程要求待匹配的两份数据必须全部位于内存中,不可分批匹配,所以当数据体量特别大时,无法一次性将数据读取到内存中进行匹配

个人计算机的内存规格以 8GB 和 16GB 为主,如果是在内存空间 8GB 的计算机上,数据字段不多且不含长文本字段的前提下,应该可以支撑两个大几百万行的数据进行匹配。如果一定要对两个体量特别大的数据做匹配,例如工商注册的全部企业(9200 万+,含死亡企业)和专利全量数据(4500 万+),那么必须要在数据库中进行,因为数据库独特的设计理念支持数据在硬盘中进行运算,所以超大型数据集之间的匹配,只能依靠数据库(MySQL、Oracle 等)。不过由于硬盘的数据传输速度远不如内存,所以匹配速度自然也比不上使用 Pandas。

Part4Pandas 数据匹配的其他情形

上一节已经介绍了 Pandas 中的四种数据匹配方式,在上文案例中,我们还了解了数据匹配中“一对多”和“多对一”的,实际应用中可能会遇到更多状况,下面我们列举几种情形并给出解决方案。

1连接字段不唯一

上一节的数据匹配案例中,只有一个连接字段,且左表和右表的连接字段名称不一样。那么当存在连接字段不唯一,且部分连接字段名称相同时,要如何编写代码进行匹配,又会得到一个什么样的结果呢?下面我们一起探索。有时候一个字段无法形成严密的对照关系,例如当我们想分析工业企业每一年的专利申请情况时,不仅仅需要用企业名称做匹配,还需要使用年份字段来限制时间。已知工企库中已有“年份”字段,下面先对演示数据做预理,在专利库中也增加一个“年份”字段,这样一来就存在两个连接字段,且其中一个名称相同。

data_工企 = pd.read_excel('./工业企业数据样例.xlsx')   # 读取数据
data_专利 = pd.read_excel('./专利数据样例.xlsx')
data_专利['年份'] = data_专利['专利申请日期'].apply(lambda x: x.year)  # 根据申请日期得到年份字段
data_专利 = data_专利.drop('专利申请日期', axis=1)  # 删除'专利申请日期'字段

下面根据存放企业名称的字段和年份字段进行左连接匹配。

data_merge = pd.merge(left=data_工企,
                      right=data_专利,
                      left_on=['工业企业名称''年份'],  # 左表的连接字段,不止一个字段时,将字段名称存放在列表中,注意顺序要对应
                      right_on=['专利申请人''年份'],   # 右表的连接字段,注意和左表连接字段顺序一致。
                      how='left')
data_merge

观察上图所示匹配结果,可以两个信息。

  1. 作为连接键的两个连接字段,由于存储企业名称的两个字段名称不同,所以匹配结果中同时保留了这两个字段。而匹配字段中存放年份的两个字段名称相同,都是“年份”,所以匹配结果只保留了其中的一个,避免了数据字段冗余。
  2. 由于这次匹配需要满足企业名称和年份,所以即使专利数据中存在深圳市华星光电技术有限公司的专利申请数据,但由于申请年份在2012年,无法与工企数据对应的2013年联系起来,所以匹配结果中该企业的专利数据部分为空值。

2字段名称相同

如果参与匹配的两个字段存在相同名称的字段,且不是连接字段,那么匹配后会发生什么呢?下面我们依然在专利数据中生成一个“年份”字段,但匹配时只使用存放企业名称的字段作为连接字段进行左连接,看看会有什么结果。

data_工企 = pd.read_excel('./工业企业数据样例.xlsx')   # 读取数据
data_专利 = pd.read_excel('./专利数据样例.xlsx')
data_专利['年份'] = data_专利['专利申请日期'].apply(lambda x: x.year)  # 根据申请日期得到年份字段
data_专利 = data_专利.drop('专利申请日期', axis=1)  # 删除'专利申请日期'字段

data_merge = pd.merge(left=data_工企,
                      right=data_专利,
                      left_on=['工业企业名称'],  # 左表的连接字段,仅一个字段时也可以放在列表中
                      right_on=['专利申请人'],   # 右表的连接字段,
                      how='left')
data_merge

从匹配结果来看,两张表中的年份字段都得到了保留,但是为了区分两个名称相同的字段,Pandas 为两个名称相同的字段做了区分,分别添加了后缀_x_y。后缀的具体样式可以使用参数suffixes来自定义,但是这个设定不能取消,即不可能在匹配结果中存在两个名称一样的字段。如果一定要让两个字段名都恢复为原来的名称,我们可以使用rename()函数修改匹配结果的字段名,代码如下。

data_merge.rename(columns={'年份_x':'年份''年份_y':'年份'}, inplace=True)
# 或:data_merge = data_merge.rename(columns={'年份_x':'年份', '年份_y':'年份'})
data_merge

另外,如果我们希望通过数据匹配获取右表中的一部分字段信息,而不是全部字段,那么有两种做法可以实现。一是在匹配之前对右表做预处理,只保留连接字段和需要的字段;二是先匹配,然后再将匹配结果中不需要的数据字段删除。

Part5总结

数据匹配是社科学者学习数据处理的重要一步。传统 office 办公软件虽然也可以做数据匹配,但功能单一,且受限于数据量,无法满足社科类数据的匹配需求。本文介绍的 Pandas 数据匹配函数pd.merge()功能非常强大,支持的匹配方式也很全面,而且由于其继承 Python 语言在内存中计算的特点,匹配数据的速度极其快。不足是无法运算数据量超大的数据集,如果有超大数据的匹配需求,仍需要依赖数据库平台。

Part6Python 教程

  (向下滑动查看更多)



星标⭐我们不迷路!想要文章及时到,文末“在看”少不了!

点击搜索你感兴趣的内容吧




数据Seminar




这里是大数据、分析技术与学术研究的三叉路口


文 | 《社科领域大数据治理实务手册》


    欢迎扫描👇二维码添加关注    
点击下方“阅读全文”了解更多

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存