在Pandas中加入DataFrames

本文概述

  • 连接数据帧
  • Works数据帧
  • 加入数据帧
  • 时间序列友好合并
  • 总结
你是否尝试过解决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。
【在Pandas中加入DataFrames】你还可以为时间列设置预定义的公差级别。假设你只希望在报价时间和交易时间之间的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的熊猫基础课程。

    推荐阅读