数据预处理之数据集成

2406 字 · 529 阅读 · 2023 年 06 月 12 日

本文已更新,你可以访问 Hands-on AI 以获得更好的阅读体验。
本篇文章需 特别授权许可,内容版权归作者所有,未经授权,禁止转载。

介绍

对数据进行清洗之后,可能会需要对多个数据集进行关联、映射、合并等操作,这也就是数据集成所涉及到的内容。

知识点

  • 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(以左表为准),rightouter(两表键并集)等策略,使之按自己需求保留内容。

# 按照 df1 中 key1 与 df2 中 key2 进行外连接合并
pd.merge(df1, df2, left_on='key1', right_on='key2', how='outer')  # Merge3

上方则以表中指定键求并集进行合并,但由于 cdf1 中并不存在,则想要的值为 NaN

有些时候,我们希望使用 DataFrame 的 Index 作为键进行合并。那么,Merge 中还有一些实用的参数需要告诉大家。

df3 = pd.DataFrame({'C': ['alpha', 'beta']}, index=['a', 'b'])
df3

此时,我们希望将 df1 中的 key1df3 中的 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 非常强大,务必多加练习。

本篇文章需 特别授权许可,内容版权归作者所有,未经授权,禁止转载。

系列文章