第 8章 数据规整:连接、联合与重塑

  • 在许多应用中,数据可能分散在许多文件或数据库中,存储的形式也不利于分析。本章关注可以聚合、合并、重塑数据的方法。
  • 首先,我会介绍pandas的层次化索引,它广泛用于以上操作。然后,我深入介绍了一些特殊的数据操作。在第14章,你可以看到这些工具的多种应用。

8.1 分层索引

  • 层次化索引(hierarchical indexing)是pandas的一项重要功能,它使你能在一个轴上拥有多个(两个以上)索引级别。抽象点说,它使你能以低维度形式处理高维度数据。我们先来看一个简单的例子:创建一个Series,并用一个由列表或数组组成的列表作为索引:
    在这里插入图片描述
  • 看到的结果是经过美化的带有MultiIndex索引的Series的格式。索引之间的“间隔”表示“直接使用上面的标签”:
    在这里插入图片描述
  • 对于一个层次化索引的对象,可以使用所谓的部分索引,使用它选取数据子集的操作更简单:
    在这里插入图片描述
  • 有时甚至还可以在“内层”中进行选取:
    在这里插入图片描述
  • 层次化索引在数据重塑和基于分组的操作(如透视表生成)中扮演着重要的角色。例如,可以通过unstack方法将这段数据重新安排到一个DataFrame中:
    在这里插入图片描述
  • unstack的逆运算是stack:
    在这里插入图片描述
  • stack和unstack将在本章后面详细讲解。
  • 对于一个DataFrame,每条轴都可以有分层索引:
    在这里插入图片描述
  • 各层都可以有名字(可以是字符串,也可以是别的Python对象)。如果指定了名称,它们就会显示在控制台输出中:
    在这里插入图片描述
  • 注意:小心区分索引名state、color与行标签。
  • 通过分列索引,你可以选出列中的组:
    在这里插入图片描述

8.1.1 重排序和层级排序

  • 有时,你需要重新调整某条轴上各级别的顺序,或根据指定级别上的值对数据进行排序。swaplevel接受两个级别编号或名称,并返回一个互换了级别的新对象(但数据不会发生变化):
    在这里插入图片描述
  • 而sort_index则根据单个级别中的值对数据进行排序。交换级别时,常常也会用到sort_index,这样最终结果就是按照指定顺序进行字母排序了:
    在这里插入图片描述
  • 如果索引按照字典顺序从最外层开始排序,那么数据选择性能会更好–调用 sort_index(level=0) 或sort_index可以得到这样的结果。

8.1.2 按层级进行汇总统计

  • 许多对DataFrame和Series的描述和汇总统计都有一个level选项,它用于指定在某条轴上求和的级别。再以上面那个DataFrame为例,我们可以根据行或列上的级别来进行求和:
    在这里插入图片描述
  • 这其实是利用了pandas的groupby功能,本书稍后将对其进行详细讲解。

8.1.3 使用DataFrame的列进行索引

  • 人们经常想要将DataFrame的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame的列。以下面这个DataFrame为例:
    在这里插入图片描述
  • DataFrame的set_index函数会将其一个或多个列转换为行索引,并创建一个新的DataFrame:
    在这里插入图片描述
  • 默认情况下,那些列会从DataFrame中移除,但也可以将其保留下来:
    在这里插入图片描述
  • reset_index的功能跟set_index刚好相反,层次化索引的级别会被转移到列里面:
    在这里插入图片描述

8.2 联合与合并数据集

  • pandas对象中的数据可以通过一些方式进行合并:
  • pandas.merge可根据一个或多个键将不同DataFrame中的行连接起来。SQL或其他关系型数据库的用户对此应该会比较熟悉,因为它实现的就是数据库的join操作。
  • pandas.concat可以沿着一条轴将多个对象堆叠到一起。
  • 实例方法combine_first可以将重复数据拼接在一起,用一个对象中的值填充另一个对象中的缺失值。
  • 我将分别对它们进行讲解,并给出一些例子。本书剩余部分的示例中将经常用到它们。

8.2.1 数据库风格的DataFrame连接

  • 数据集的合并(merge)或连接(join)运算是通过一个或多个键将行连接起来的。这些运算是关系型数据库(基于SQL)的核心。pandas的merge函数是对数据应用这些算法的主要切入点。
  • 以一个简单的例子开始:
    在这里插入图片描述
  • 这是一种多对一的合并。df1中的数据有多个被标记为a和b的行,而df2中key列的每个值则仅对应一行。对这些对象调用merge即可得到:
    在这里插入图片描述
  • 注意,我并没有指明要用哪个列进行连接。如果没有指定,merge就会将重叠列的列名当做键。不过,最好明确指定一下:
    在这里插入图片描述
  • 如果两个对象的列名不同,也可以分别进行指定:
    在这里插入图片描述
  • 可能你已经注意到了,结果里面c和d以及与之相关的数据消失了。默认情况下,merge做的是“内连接”;结果中的键是交集。其他方式有"left"、“right"以及"outer”。外连接求取的是键的并集,组合了左连接和右连接的效果:
    在这里插入图片描述
  • 表8-1是对how选项的总结。
    在这里插入图片描述
  • 多对多的合并有些不直观。看下面的例子:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 多对多连接产生的是行的笛卡尔积。由于左边的DataFrame有3个"b"行,右边的有2个,所以最终结果中就有6个"b"行。连接方式只影响出现在结果中的不同的键的值:
    在这里插入图片描述
  • 要根据多个键进行合并,传入一个由列名组成的列表即可:
    在这里插入图片描述
  • 结果中会出现哪些键组合取决于所选的合并方式,你可以这样来理解:多个键形成一系列元组,并将其当做单个连接键(当然,实际上并不是这么回事)。
  • 注意:在进行列-列连接时,DataFrame对象中的索引会被丢弃。
  • 对于合并运算需要考虑的最后一个问题是对重复列名的处理。虽然你可以手工处理列名重叠的问题(查看前面介绍的重命名轴标签),但merge有一个更实用的suffixes选项,用于指定附加到左右两个DataFrame对象的重叠列名上的字符串:
    在这里插入图片描述
  • merge的参数请参见表8-2。使用DataFrame的行索引合并是下一节的主题。
    在这里插入图片描述

8.2.2 根据索引合并

  • 有时候,DataFrame中的连接键位于其索引中。在这种情况下,你可以传入left_index=True或right_index=True(或两个都传)以说明索引应该被用作连接键:
    在这里插入图片描述
    在这里插入图片描述
  • 由于默认的merge方法是求取连接键的交集,因此你可以通过外连接的方式得到它们的并集:
    在这里插入图片描述
  • 对于层次化索引的数据,事情就有点复杂了,因为索引的合并默认是多键合并:
    在这里插入图片描述
    在这里插入图片描述
  • 这种情况下,你必须以列表的形式指明用作合并键的多个列(注意用how='outer’对重复索引值的处理):
    在这里插入图片描述
  • 同时使用合并双方的索引也没问题:
    在这里插入图片描述
    在这里插入图片描述
  • DataFrame还有一个便捷的join实例方法,它能更为方便地实现按索引合并。它还可用于合并多个带有相同或相似索引的DataFrame对象,但要求没有重叠的列。在上面那个例子中,我们可以编写:
    在这里插入图片描述
  • 因为一些历史版本的遗留原因,DataFrame的join方法默认使用的是左连接,保留左边表的行索引。它还支持在调用的DataFrame的列上,连接传递的DataFrame索引:
    在这里插入图片描述
  • 最后,对于简单的索引合并,你还可以向join传入一组DataFrame,下一节会介绍更为通用的concat函数,也能实现此功能:
    在这里插入图片描述
    在这里插入图片描述

8.2.3 沿轴向连接

  • 另一种数据合并运算也被称作连接(concatenation)、绑定(binding)或堆叠(stacking)。NumPy的concatenation函数可以用NumPy数组来做:
    在这里插入图片描述

  • 对于pandas对象(如Series和DataFrame),带有标签的轴使你能够进一步推广数组的连接运算。具体点说,你还需要考虑以下这些东西:

  • 如果对象在其它轴上的索引不同,我们应该合并这些轴的不同元素还是只使用交集?

  • 连接的数据集是否需要在结果对象中可识别?

  • 连接轴中保存的数据是否需要保留?许多情况下,DataFrame默认的整数标签最好在连接时删掉。

  • pandas的concat函数提供了一种能够解决这些问题的可靠方式。我将给出一些例子来讲解其使用方式。假设有三个没有重叠索引的Series:
    在这里插入图片描述

  • 对这些对象调用concat可以将值和索引粘合在一起:
    在这里插入图片描述

  • 默认情况下,concat是在axis=0上工作的,最终产生一个新的Series。如果传入axis=1,则结果就会变成一个DataFrame(axis=1是列):
    在这里插入图片描述

  • 这种情况下,另外的轴上没有重叠,从索引的有序并集(外连接)上就可以看出来。传入join='inner’即可得到它们的交集:
    在这里插入图片描述

  • 在这个例子中,f和g标签消失了,是因为使用的是join='inner’选项。

  • 不过有个问题,参与连接的片段在结果中区分不开。假设你想要在连接轴上创建一个层次化索引。使用keys参数即可达到这个目的:
    在这里插入图片描述

  • 如果沿着axis=1对Series进行合并,则keys就会成为DataFrame的列头:
    在这里插入图片描述

  • 同样的逻辑也适用于DataFrame对象:
    在这里插入图片描述

  • 如果传入的不是列表而是一个字典,则字典的键就会被当做keys选项的值:
    在这里插入图片描述

  • 此外还有两个用于管理层次化索引创建方式的参数(参见表8-3)。举个例子,我们可以用names参数命名创建的轴级别:
    在这里插入图片描述

  • 最后一个关于DataFrame的问题是,DataFrame的行索引不包含任何相关数据:
    在这里插入图片描述

  • 在这种情况下,传入ignore_index=True即可:
    在这里插入图片描述
    在这里插入图片描述

8.2.4 联合重叠数据

  • 还有一种数据组合问题不能用简单的合并(merge)或连接(concatenation)运算来处理。比如说,你可能有索引全部或部分重叠的两个数据集。举个有启发性的例子,我们使用NumPy的where函数,它表示一种等价于面向数组的if-else:
    在这里插入图片描述
  • Series有一个combine_first方法,实现的也是一样的功能,还带有pandas的数据对齐:
    在这里插入图片描述
  • 对于DataFrame,combine_first自然也会在列上做同样的事情,因此你可以将其看做:用传递对象中的数据为调用对象的缺失数据“打补丁”:
    在这里插入图片描述
    在这里插入图片描述

8.3 重塑和透视

  • 有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑(reshape)或轴向旋转(pivot)运算。

8.3.1 使用多层索引进行重塑

  • 层次化索引为DataFrame数据的重排任务提供了一种具有良好一致性的方式。主要功能有二:
  • stack:将数据的列“旋转”为行。
  • unstack:将数据的行“旋转”为列。
  • 我将通过一系列的范例来讲解这些操作。接下来看一个简单的DataFrame,其中的行列索引均为字符串数组:
    在这里插入图片描述
  • 对该数据使用stack方法即可将列转换为行,得到一个Series:
    在这里插入图片描述
  • 对于一个层次化索引的Series,你可以用unstack将其重排为一个DataFrame:
    在这里插入图片描述
  • 默认情况下,unstack操作的是最内层(stack也是如此)。传入分层级别的编号或名称即可对其它级别进行unstack操作:
    在这里插入图片描述
    在这里插入图片描述
  • 如果不是所有的级别值都能在各分组中找到的话,则unstack操作可能会引入缺失数据:
    在这里插入图片描述
  • stack默认会滤除缺失数据,因此该运算是可逆的:
    在这里插入图片描述
  • 在对DataFrame进行unstack操作时,作为旋转轴的级别将会成为结果中的最低级别:
In [135]: df = pd.DataFrame({'left': result, 'right': result + 5},
   .....:                   columns=pd.Index(['left', 'right'], name='side'))

In [136]: df
Out[136]: 
side             left  right
state    number             
Ohio     one        0      5
         two        1      6
         three      2      7
Colorado one        3      8
         two        4      9
         three      5     10

In [137]: df.unstack('state')
Out[137]: 
side   left          right
state  Ohio Colorado  Ohio Colorado
number                             
one       0        3     5        8
two       1        4     6        9
three     2        5     7       10
  • 当调用stack,我们可以指明轴的名字:
    在这里插入图片描述

8.3.2 将“长”透视为“宽”

  • 多个时间序列数据通常是以所谓的“长格式”(long)或“堆叠格式”(stacked)存储在数据库和CSV中的。我们先加载一些示例数据,做一些时间序列规整和数据清洗:

在这里插入图片描述

  • 这就是多个时间序列(或者其它带有两个或多个键的可观察数据,这里,我们的键是date和item)的长格式。表中的每行代表一次观察。
  • 关系型数据库(如MySQL)中的数据经常都是这样存储的,因为固定架构(即列名和数据类型)有一个好处:随着表中数据的添加,item列中的值的种类能够增加。在前面的例子中,date和item通常就是主键(用关系型数据库的说法),不仅提供了关系完整性,而且提供了更为简单的查询支持。有的情况下,使用这样的数据会很麻烦,你可能会更喜欢DataFrame,不同的item值分别形成一列,date列中的时间戳则用作索引。DataFrame的pivot方法完全可以实现这个转换:
  • 现在,ldata看起来如下:
    在这里插入图片描述
  • 前两个传递的值分别用作行和列索引,最后一个可选值则是用于填充DataFrame的数据列。假设有两个需要同时重塑的数据列:
    在这里插入图片描述
  • 如果忽略最后一个参数,得到的DataFrame就会带有层次化的列:
    在这里插入图片描述
  • 注意,pivot其实就是用set_index创建层次化索引,再用unstack重塑:
    在这里插入图片描述

8.3.3 将“宽”透视为“长”

  • 旋转DataFrame的逆运算是pandas.melt。它不是将一列转换到多个新的DataFrame,而是合并多个列成为一个,产生一个比输入长的DataFrame。看一个例子:
    在这里插入图片描述
  • key列可能是分组指标,其它的列是数据值。当使用pandas.melt,我们必须指明哪些列是分组指标。下面使用key作为唯一的分组指标:
    在这里插入图片描述
  • 使用pivot,可以重塑回原来的样子:
    在这里插入图片描述
  • 因为pivot的结果从列创建了一个索引,用作行标签,我们可以使用reset_index将数据移回列:
    在这里插入图片描述
  • 你还可以指定列的子集,作为值的列:
    在这里插入图片描述
  • pandas.melt也可以不用分组指标:
    在这里插入图片描述
    在这里插入图片描述

8.4 本章小结

  • 现在你已经掌握了pandas数据导入、清洗、重塑,我们可以进一步学习matplotlib数据可视化。我们在稍后会回到pandas,学习更高级的分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值