数据分析:数据预处理之数据集成
介绍
对数据进行清洗之后,可能会需要对多个数据集进行关联、映射、合并等操作,这也就是数据集成所涉及到的内容。
知识点
- Merge 合并数据方法
- Join 按照索引合并
- concat 轴堆叠方法
- map 数据映射方法
- Group by 数据分组聚合方法
将多个数据库或者数据文件里面包含的数据进行集成处理。其中主要涉及到数据合并、数据映射等。
数据合并是数据转换中最常见的情况之一。当我们从关系型数据库中读取到多个表时,往往会依据键值进行数据合并,使得数据产生关联。当然,这一点也会体现到手中存在多个关联文件的情况下。
使用 Pandas 进行数据合并处理是再方便不过的事情了,Pandas 提供的一系列操作可以满足我们对任意数据合并的需求。下面,我们将了解一些最常用的手段和方法。
键合并数据
Merge 是最常用的合并方法之一,该操作可以通过 1 个或多个键将 DataFrame 链接起来,从而达到数据表合并的效果。下面,我们对 pandas.DataFrame.merge
进行示例说明。
import pandas as pd
df1 = pd.DataFrame({'key': ['a', 'b'] * 3, 'A': range(6)})
df1
df2 = pd.DataFrame({'key': ['a', 'b', 'c'], 'B': ['one', 'two', 'three']})
df2
首先,我们可以通过 Merge 操作将不同表按指定 key
合并到一起。
# 按照 df1 和 df2 中 key 对应合并
pd.merge(df1, df2, on='key') # Merge1
如果两张表中并未出现同样的列 key
,则可以手动指定合并达到上面一致的效果。
df1 = pd.DataFrame({'key1': ['a', 'b'] * 3, 'A': range(6)})
df2 = pd.DataFrame({'key2': ['a', 'b', 'c'], 'B': ['one', 'two', 'three']})
# 按照 df1 中 key1 与 df2 中 key2 合并
pd.merge(df1, df2, left_on='key1', right_on='key2') # Merge2
上面的 df2
中出现了 c
及对应值 three
,但是合并之后的 DataFrame 中却没有,这是因为 Merge 操作默认的策略是内连接(Inner),也就是保留两个表中 key
存在交集的内容。
当然,你可以指定 left
(以左表为准),right
,outer
(两表键并集)等策略,使之按自己需求保留内容。
# 按照 df1 中 key1 与 df2 中 key2 进行外连接合并
pd.merge(df1, df2, left_on='key1', right_on='key2', how='outer') # Merge3
上方则以表中指定键求并集进行合并,但由于 c
在 df1
中并不存在,则想要的值为 NaN
。
有些时候,我们希望使用 DataFrame 的 Index 作为键进行合并。那么,Merge 中还有一些实用的参数需要告诉大家。
df3 = pd.DataFrame({'C': ['alpha', 'beta']}, index=['a', 'b'])
df3
此时,我们希望将 df1
中的 key1
和 df3
中的 index
对应在一起。
# 按照 df1 中 key1 与 df3 中 index 合并
pd.merge(df1, df3, left_on='key1', right_index=True) # Merge4
Pandas 中的 Merge 方法其实是效仿 SQL 中的 Join 操作进行设计,非常实用。
索引合并数据
其实,Pandas 虽然提供了 Merge 方法,但同时也提供了 Join 方法。Join 操作主要是方便按照索引进行数据合并。
例如,我们可以重新使用 pandas.DataFrame.join
将上方的 #Merge4
操作重新实现一次。
# 按照 df1 中 key1 与 df3 中 index 合并
df1.join(df3, on='key1') # Join1
可以看到 #Join1
得到的值和 #Merge4
一致,不过 #Join1
操作更好地保留了索引的序号。
轴堆叠数据
除了 Merge 和 Join 方法,Pandas 中还有一个常用于数据合并的方法叫 pandas.concat
,Concat 可以按照指定轴对不同的 DataFrame 进行连接,同时也可以指定连接的方式进行 Join 操作,Concat 相当于 SQL 中的全连接操作。
df1 = pd.DataFrame({'A': [0, 1], 'B': [2, 3]})
df2 = pd.DataFrame({'A': [4, 5], 'B': [6, 7]})
df3 = pd.DataFrame({'A': [8, 9], 'B': [10, 11]})
# 默认按照列名轴 axis=0 进行堆叠
pd.concat([df1, df2, df3], sort=True) # Concat1
# 按照索引轴 axis=1 进行堆叠
pd.concat([df1, df2, df3], axis=1, sort=True) # Concat2
Concat 操作中有一个特别实用的参数是通过 ignore_index=True
重建索引。可以对比 #Concat1
操作。
# 堆叠时重建索引
pd.concat([df1, df2, df3], ignore_index=True, sort=True) # Concat3
看完上面的 merge,join,concat
方法的示例,你可能被绕晕了。最后,我们总结一下三种方法的不同适应场景。
merge
: 多用于不同 DataFrame 按照指定键进行列合并。join
: 多用于不同 DataFrame 按照索引进行列合并。concat
: 多用于不同 DataFrame 按照不同轴进行合并。
在实际运用时,很多情形下的数据合并可以通过不同的思路由不同的方法完成。所以,这三种合并方法在某些场景下有重复的情况,这也反应出 Pandas 的灵活性。
数据映射
数据映射也是时常会碰到的情形,一般会使用 map
方法完成,其中可以是反应映射关系匿名函数或者字典。
df = pd.DataFrame({'name': ['amy', 'david', 'jam'], 'age': [14, 13, 12]})
df
name_to_gender = {'amy': 'girl', 'david': 'boy', 'jam': 'boy'} # 建立映射字典
df['gender'] = df['name'].map(name_to_gender)
df
你可能会想到,上面的映射过程可以通过合并两个 DataFrame 完成。当然,这里只是一个简单的示例,并没有反应出数据映射的优越性。不过,如果你遇到一个很大的数据集,且需要匹配姓名所对应的性别。我们都知道有常用的女士名和男士名,那么用映射就能更快完成。
还有一种情况,也可以归纳为数据映射,其实类似于区间替换的过程。例如我们有下面这样一个动物年龄的数据集。
df = pd.DataFrame({'animal': ['unknown'] * 10, 'age': range(1, 11)})
df
假设我们需要对年龄进行区间替换,1-3 岁为 young,4-7 岁为 middle,8-10 岁为 old,该怎么办呢?
此时,我们可以使用 Pandas 提供的 pandas.cut
方法。其中,bins=
用于指定数值区间,labels
则为映射后的标签。
df['age1'] = pd.cut(df.age, bins=[0, 3, 7, 10],
labels=['young', 'middle', 'old'])
df
分组聚合
Pandas 提供了强大的 GroupBy 功能,方便我们对 DataFrame 进行分组,再执行求和、求平均等运算。我们相信,GroupBy 是使用 Pandas 完成数据预处理过程中不得不接触到的一个功能,并称之为「分组聚合」。
下面,通过一个图示来了解 pandas.DataFrame.groupby
实现分组 → 聚合的过程。下图中,我们首先将字母列作为键,对数据集进行分组。然后,对各组进行聚合。这里执行了求和操作,当然还可以进行计数,求平均值,求标准差,甚至使用自定义函数。
我们通过 Pandas 来完成上图所示的过程。
df = pd.DataFrame({'key1': ['A', 'B', 'C', 'A', 'B', 'C'],
'key2': ['X', 'Y', 'X', 'Y', 'X', 'Y'],
'data': [1, 2, 3, 4, 5, 6]})
df
下面,针对 key1
执行 GroupBy 操作,并求和。
df.groupby(by='key1').sum()
如果将 sum()
,更换为 mean()
,count()
等其他函数即可实现更多聚合的功能,你可以自行练习。
下面,我们设定 2 个分组键,数据表即被处理为多索引。
df.groupby(by=['key1', 'key2']).mean()
GroupBy 使用广泛。例如对商品数据按品类进行聚合,对股票数据按时间进行聚合等。所以,了解并学习 GroupBy 非常重要。
小结
数据集成所涉及到的步骤和方法需要我们根据实际情况灵活应对。很多时候,我们预想到的结果非常简单,但可能需要进行多次合并、映射操作才能得到想要的结果。关于这里所讨论的灵活应对,你需要从接下来的一些挑战中获得更多的经验。特别地,Pandas 提供的分组聚合类 GroupBy 非常强大,务必多加练习。