个性化阅读
专注于IT技术分析

在Pandas中加入DataFrames

本文概述

你是否尝试过解决Kaggle挑战?如果是, 你可能已经注意到, 在大多数挑战中, 提供给你的数据存在于多个文件中, 而某些列存在于多个文件中。好吧, 首先想到的是什么?当然加入他们!

连接和合并DataFrame是从数据分析和机器学习任务开始的核心过程。它是每个数据分析师或数据科学家都应掌握的工具箱之一, 因为在几乎所有情况下, 数据都来自多个源和文件。你可能需要通过某种联接逻辑将所有数据放在一个位置, 然后开始分析。使用诸如查询语言之类的SQL的人可能知道此任务的重要性。即使你要在某些数据上构建一些机器学习模型, 也可能需要将多个csv文件合并到一个DataFrame中。

值得庆幸的是, 你拥有python中最受欢迎的库, 熊猫来助你一臂之力! pandas提供了各种功能, 可以在连接/合并类型操作的情况下轻松地将Series, DataFrame和Panel对象与各种用于索引和关系代数功能的设置逻辑组合在一起。

在本教程中, 你将练习一些标准技术。更具体地说, 你将学习:

  • 沿行和列连接DataFrame。
  • 通过不同的联接逻辑(例如左联接, 内联接等)在特定键上合并DataFrame。
  • 熊猫提供的时间序列友好合并

在此过程中, 你还将学习加入之前和之后所需的一些技巧。

连接数据帧

首先导入将在整个教程中使用的库:pandas

import pandas as pd

你将在要创建的虚拟DataFrame上执行本教程中的所有操作。要创建一个DataFrame, 你可以使用python字典, 例如:

dummy_data1 = {
        'id': ['1', '2', '3', '4', '5'], 'Feature1': ['A', 'C', 'E', 'G', 'I'], 'Feature2': ['B', 'D', 'F', 'H', 'J']}

在这里, dummy_data1词典的关键字是列名, 列表中的值是与每个观察值或行对应的数据。要将其转换为pandas DataFrame, 你将使用pandas的DataFrame()函数以及其column参数来命名你的列:

df1 = pd.DataFrame(dummy_data1, columns = ['id', 'Feature1', 'Feature2'])

df1
id 功能1 功能2
0 1 一个
1 2 C d
2 3 F
3 4 G H
4 5 一世 Ĵ

如你所见, 你现在拥有一个包含3列ID, Feature1和Feature2的DataFrame。熊猫在内部另外创建了一个未命名的列作为行标签。与先前的DataFrame df1类似, 你将再创建两个DataFrame df2和df3:

dummy_data2 = {
        'id': ['1', '2', '6', '7', '8'], 'Feature1': ['K', 'M', 'O', 'Q', 'S'], 'Feature2': ['L', 'N', 'P', 'R', 'T']}
df2 = pd.DataFrame(dummy_data2, columns = ['id', 'Feature1', 'Feature2'])

df2
id 功能1 功能2
0 1
1 2 中号 ñ
2 6 P
3 7 [R
4 8 小号 Ť
dummy_data3 = {
        'id': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'], 'Feature3': [12, 13, 14, 15, 16, 17, 15, 12, 13, 23]}
df3 = pd.DataFrame(dummy_data3, columns = ['id', 'Feature3'])

df3
id 功能3
0 1 12
1 2 13
2 3 14
3 4 15
4 5 16
5 7 17
6 8 15
7 9 12
8 10 13
9 11 23

要简单地沿行连接DataFrame, 可以在pandas中使用concat()函数。你将必须在列表中传递数据帧的名称作为concat()函数的参数:

df_row = pd.concat([df1, df2])

df_row
id 功能1 功能2
0 1 一个
1 2 C d
2 3 F
3 4 G H
4 5 一世 Ĵ
0 1
1 2 中号 ñ
2 6 P
3 7 [R
4 8 小号 Ť

你会注意到, 两个DataFrame df1和df2现在沿着该行串联到单个DataFrame df_row中。但是, 行标签似乎是错误的!如果希望行标签根据联接自动调整, 则必须在调用concat()函数时将参数ignore_index设置为True:

df_row_reindex = pd.concat([df1, df2], ignore_index=True)

df_row_reindex
id 功能1 功能2
0 1 一个
1 2 C d
2 3 F
3 4 G H
4 5 一世 Ĵ
5 1
6 2 中号 ñ
7 6 P
8 7 [R
9 8 小号 Ť

现在行标签正确了!

pandas还为你提供了一个选项, 可以在串联后为键标记DataFrame, 以便你可以知道哪些数据来自哪个DataFrame。你可以通过传递其他参数键来实现相同目的, 这些参数键在列表中指定DataFrame的标签名称。在这里, 你将分别使用与x和y相同的键对DataFrames df1和df2执行相同的串联操作。

frames = [df1, df2]
df_keys = pd.concat(frames, keys=['x', 'y'])

df_keys
id 功能1 功能2
X 0 1 一个
1 2 C d
2 3 F
3 4 G H
4 5 一世 Ĵ
0 1
1 2 中号 ñ
2 6 P
3 7 [R
4 8 小号 Ť

提及密钥还可以轻松检索与特定DataFrame对应的数据。你可以使用loc方法检索标签为y的DataFrame df2的数据:

df_keys.loc['y']
id 功能1 功能2
0 1
1 2 中号 ñ
2 6 P
3 7 [R
4 8 小号 Ť

你还可以将字典传递给concat(), 在这种情况下, 字典键将用于keys参数(除非指定了其他键):

pieces = {'x': df1, 'y': df2}

df_piece = pd.concat(pieces)

df_piece
id 功能1 功能2
X 0 1 一个
1 2 C d
2 3 F
3 4 G H
4 5 一世 Ĵ
0 1
1 2 中号 ñ
2 6 P
3 7 [R
4 8 小号 Ť

值得注意的是, concat()会完整复制数据, 并且连续地重用此函数可能会严重影响性能。如果需要对多个数据集使用该操作, 请使用列表推导。

frames = [ process_your_file(f) for f in files ]
result = pd.concat(frames)

要沿列连接DataFrame, 可以将axis参数指定为1:

df_col = pd.concat([df1, df2], axis=1)

df_col
id 功能1 功能2 id 功能1 功能2
0 1 一个 1
1 2 C d 2 中号 ñ
2 3 F 6 P
3 4 G H 7 [R
4 5 一世 Ĵ 8 小号 Ť

Works数据帧

与DataFrames有关的另一个普遍存在的操作是合并操作。两个DataFrame可能保存有关同一实体的不同种类的信息, 并通过某个共同的功能/列进行链接。为了连接这些DataFrame, pandas提供了诸如concat(), merge(), join()等多个功能。在本节中, 你将练习使用pandas的merge()函数。

你可以将DataFrames df_row(通过沿行串联df1和df2来创建)和df3联接到公共列(或键)ID上。为此, 请将DataFrames的名称和其他参数作为公共列的名称(此处为id)传递给merge()函数:

df_merge_col = pd.merge(df_row, df3, on='id')

df_merge_col
id 功能1 功能2 功能3
0 1 一个 12
1 1 12
2 2 C d 13
3 2 中号 ñ 13
4 3 F 14
5 4 G H 15
6 5 一世 Ĵ 16
7 7 [R 17
8 8 小号 Ť 15

你会注意到, DataFrame现在基于两个DataFrame的id列中存在的公共值合并到单个DataFrame中。例如, 此处的ID值1在数据帧df_row中同时包含A, B和K, L, 因此此ID在最终的数据帧df_merge_col中重复了两次, 其中Feature3的重复值12来自数据帧df3。

可能要在其上合并DataFrame的列具有不同的名称(在这种情况下不一样)。对于此类合并, 你将必须将参数left_on指定为左侧DataFrame名称, 将right_on指定为右侧DataFrame名称, 例如:

df_merge_difkey = pd.merge(df_row, df3, left_on='id', right_on='id')

df_merge_difkey
id 功能1 功能2 功能3
0 1 一个 12
1 1 12
2 2 C d 13
3 2 中号 ñ 13
4 3 F 14
5 4 G H 15
6 5 一世 Ĵ 16
7 7 [R 17
8 8 小号 Ť 15

你还可以通过将Series或dict传递给append()函数来将行追加到DataFrame, 该函数返回一个新的DataFrame:

add_row = pd.Series(['10', 'X1', 'X2', 'X3'], index=['id', 'Feature1', 'Feature2', 'Feature3'])

df_add_row = df_merge_col.append(add_row, ignore_index=True)

df_add_row
id 功能1 功能2 功能3
0 1 一个 12
1 1 12
2 2 C d 13
3 2 中号 ñ 13
4 3 F 14
5 4 G H 15
6 5 一世 Ĵ 16
7 7 [R 17
8 8 小号 Ť 15
9 10 X1 X2 X3

加入数据帧

在本节中, 你将练习基于各种常见列/键的各种合并逻辑, 可用于合并pandas DataFrame。这些联接背后的逻辑与联接表时在SQL中的逻辑非常相似。

完全外部加入

FULL OUTER JOIN合并左右外部联接的结果。联接的DataFrame将包含两个DataFrame的所有记录, 并填写NaN以弥补任一侧缺少的匹配项。你可以通过在merge()函数中将how参数指定为external来执行完全外部联接:

df_outer = pd.merge(df1, df2, on='id', how='outer')

df_outer
id Feature1_x Feature2_x Feature1_y Feature2_y
0 1 一个
1 2 C d 中号 ñ
2 3 F NaN NaN
3 4 G H NaN NaN
4 5 一世 Ĵ NaN NaN
5 6 NaN NaN P
6 7 NaN NaN [R
7 8 NaN NaN 小号 Ť

你会注意到, 生成的DataFrame在两个表中都具有NaN值的两个表中的所有条目, 以便在任一侧丢失匹配项。但是, 还有一点需要注意的是后缀, 该后缀被附加到列名以显示哪个列来自哪个DataFrame。默认后缀是x和y, 但是, 你可以通过在merge()函数中指定后缀参数来修改它们:

df_suffix = pd.merge(df1, df2, left_on='id', right_on='id', how='outer', suffixes=('_left', '_right'))

df_suffix
id Feature1_left Feature2_left Feature1_right Feature2_right
0 1 一个
1 2 C d 中号 ñ
2 3 F NaN NaN
3 4 G H NaN NaN
4 5 一世 Ĵ NaN NaN
5 6 NaN NaN P
6 7 NaN NaN [R
7 8 NaN NaN 小号 Ť

内部联接

INNER JOIN仅生成在DataFrame A和DataFrame B中都匹配的记录集。你必须在merge()函数的how参数中传递inner来进行内部联接:

df_inner = pd.merge(df1, df2, on='id', how='inner')

df_inner
id Feature1_x Feature2_x Feature1_y Feature2_y
0 1 一个
1 2 C d 中号 ñ

正确加入

RIGHT JOIN会从DataFrame B(右DataFrame)生成完整的记录集, 并在DataFrame A(左DataFrame)中生成匹配的记录(如果有)。如果没有匹配项, 则右侧将包含null。你必须直接在merge()函数的how参数中进行正确的连接:

df_right = pd.merge(df1, df2, on='id', how='right')

df_right
id Feature1_x Feature2_x Feature1_y Feature2_y
0 1 一个
1 2 C d 中号 ñ
2 6 NaN NaN P
3 7 NaN NaN [R
4 8 NaN NaN 小号 Ť

左加入

LEFT JOIN会从DataFrame A(左侧DataFrame)生成完整的记录集, 并在DataFrame B(右侧DataFrame)中生成匹配的记录(如果有)。如果没有匹配项, 则左侧将包含null。你必须在merge()函数的how参数中向左传递以进行左连接:

df_left = pd.merge(df1, df2, on='id', how='left')

df_left
id Feature1_x Feature2_x Feature1_y Feature2_y
0 1 一个
1 2 C d 中号 ñ
2 3 F NaN NaN
3 4 G H NaN NaN
4 5 一世 Ĵ NaN NaN

加入索引

有时你可能必须对索引或行标签执行联接。为此, 你必须将right_index(用于右侧DataFrame的索引)和left_index(用于左侧DataFrame的索引)指定为True:

df_index = pd.merge(df1, df2, right_index=True, left_index=True)

df_index
id_x Feature1_x Feature2_x id_y Feature1_y Feature2_y
0 1 一个 1
1 2 C d 2 中号 ñ
2 3 F 6 P
3 4 G H 7 [R
4 5 一世 Ĵ 8 小号 Ť

时间序列友好合并

熊猫提供了用于合并时间序列数据帧的特殊功能。也许最有用和最受欢迎的是merge_asof()函数。 merge_asof()与有序左联接相似, 不同之处在于你匹配的是最接近的键而不是相等的键。对于左侧DataFrame中的每一行, 请选择右侧DataFrame中其on键小于左侧键的最后一行。两个DataFrame必须按键排序。

可选地, asof合并可以执行逐组合并。除on键上最接近的匹配项外, 此键均等地匹配by键。

例如, 你可能有交易和报价, 并且想要将它们合并。在这里, 左边的DataFrame被选作交易, 右边的DataFrame被选为报价。它们在关键时间开始合并, 并通过股票代码进行分组合并。

trades = pd.DataFrame({
    'time': pd.to_datetime(['20160525 13:30:00.023', '20160525 13:30:00.038', '20160525 13:30:00.048', '20160525 13:30:00.048', '20160525 13:30:00.048']), 'ticker': ['MSFT', 'MSFT', 'GOOG', 'GOOG', 'AAPL'], 'price': [51.95, 51.95, 720.77, 720.92, 98.00], 'quantity': [75, 155, 100, 100, 100]}, columns=['time', 'ticker', 'price', 'quantity'])

quotes = pd.DataFrame({
    'time': pd.to_datetime(['20160525 13:30:00.023', '20160525 13:30:00.023', '20160525 13:30:00.030', '20160525 13:30:00.041', '20160525 13:30:00.048', '20160525 13:30:00.049', '20160525 13:30:00.072', '20160525 13:30:00.075']), 'ticker': ['GOOG', 'MSFT', 'MSFT', 'MSFT', 'GOOG', 'AAPL', 'GOOG', 'MSFT'], 'bid': [720.50, 51.95, 51.97, 51.99, 720.50, 97.99, 720.50, 52.01], 'ask': [720.93, 51.96, 51.98, 52.00, 720.93, 98.01, 720.88, 52.03]}, columns=['time', 'ticker', 'bid', 'ask'])
trades
时间 股票代码 价钱 数量
0 2016-05-25 13:30:00.023 微软 51.95 75
1 2016-05-25 13:30:00.038 微软 51.95 155
2 2016-05-25 13:30:00.048 高格 720.77 100
3 2016-05-25 13:30:00.048 高格 720.92 100
4 2016-05-25 13:30:00.048 AAPL 98.00 100
quotes
时间 股票代码 出价
0 2016-05-25 13:30:00.023 高格 720.50 720.93
1 2016-05-25 13:30:00.023 微软 51.95 51.96
2 2016-05-25 13:30:00.030 微软 51.97 51.98
3 2016-05-25 13:30:00.041 微软 51.99 52.00
4 2016-05-25 13:30:00.048 高格 720.50 720.93
5 2016-05-25 13:30:00.049 AAPL 97.99 98.01
6 2016-05-25 13:30:00.072 高格 720.50 720.88
7 2016-05-25 13:30:00.075 微软 52.01 52.03
df_merge_asof = pd.merge_asof(trades, quotes, on='time', by='ticker')

df_merge_asof
时间 股票代码 价钱 数量 出价
0 2016-05-25 13:30:00.023 微软 51.95 75 51.95 51.96
1 2016-05-25 13:30:00.038 微软 51.95 155 51.97 51.98
2 2016-05-25 13:30:00.048 高格 720.77 100 720.50 720.93
3 2016-05-25 13:30:00.048 高格 720.92 100 720.50 720.93
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN

如果仔细观察, 你会发现NaN出现在AAPL代码行中的原因。由于正确的DataFrame报价没有任何时间值小于13:30:00.048(左表中的时间), 因此APN报价中引入了NaN。

你还可以为时间列设置预定义的公差级别。假设你只希望在报价时间和交易时间之间的2ms之内进行asof合并, 那么你将必须指定公差参数:

df_merge_asof_tolerance = pd.merge_asof(trades, quotes, on='time', by='ticker', tolerance=pd.Timedelta('2ms'))

df_merge_asof_tolerance
时间 股票代码 价钱 数量 出价
0 2016-05-25 13:30:00.023 微软 51.95 75 51.95 51.96
1 2016-05-25 13:30:00.038 微软 51.95 155 NaN NaN
2 2016-05-25 13:30:00.048 高格 720.77 100 720.50 720.93
3 2016-05-25 13:30:00.048 高格 720.92 100 720.50 720.93
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN

注意上述结果与先前结果之间的区别。如果时间公差不匹配2ms, 则行不会合并。

总结

欢呼!你到了本教程的结尾。在本教程中, 你学习了使用pandas库的concat()和merge()函数基于几种逻辑来串联和合并DataFrame。最后, 你还练习了特殊功能merge_asof()来合并时间序列DataFrame。在此过程中, 你还学会了如何使用DataFrames的索引。你还可以探索其他几种选择, 以在熊猫中加入DataFrames, 我鼓励你查看其出色的文档。探索愉快!

本教程使用以下资源来帮助编写它:

  • https://pandas.pydata.org/pandas-docs/stable/merging.html

如果你想了解有关熊猫的更多信息, 请参加srcmini的熊猫基础课程。

赞(0)
未经允许不得转载:srcmini » 在Pandas中加入DataFrames

评论 抢沙发

评论前必须登录!