在线运行和编辑此教程中的代码
运行代码pandas 拼接(Concatenate)
先导入本教程中将一直使用的库: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() 函数,并通过 columns 参数指定列名:
df1 = pd.DataFrame(dummy_data1, columns = ['id', 'Feature1', 'Feature2'])
df1
| id | Feature1 | Feature2 | |
|---|---|---|---|
| 0 | 1 | A | B |
| 1 | 2 | C | D |
| 2 | 3 | E | F |
| 3 | 4 | G | H |
| 4 | 5 | I | J |
如您所见,现在有一个包含三列 id、Feature1 和 Feature2 的 DataFrame。还有一列未命名列,是 pandas 自动创建的行标签。与前面的 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 | Feature1 | Feature2 | |
|---|---|---|---|
| 0 | 1 | K | L |
| 1 | 2 | M | N |
| 2 | 6 | O | P |
| 3 | 7 | Q | R |
| 4 | 8 | S | T |
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 | Feature3 | |
|---|---|---|
| 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 |
concat()
如果只是想沿行简单地拼接 DataFrame,可以使用 pandas 的 concat() 函数。您需要把要拼接的 DataFrame 名称以列表形式传入 concat() 的参数:
df_row = pd.concat([df1, df2])
df_row
| id | Feature1 | Feature2 | |
|---|---|---|---|
| 0 | 1 | A | B |
| 1 | 2 | C | D |
| 2 | 3 | E | F |
| 3 | 4 | G | H |
| 4 | 5 | I | J |
| 0 | 1 | K | L |
| 1 | 2 | M | N |
| 2 | 6 | O | P |
| 3 | 7 | Q | R |
| 4 | 8 | S | T |
您会看到,两个 DataFrame(df1 和 df2)已沿行拼接为一个新的 DataFrame(df_row)。不过行标签看起来不太对。如果希望行标签根据拼接自动调整,请在调用 concat() 时将 ignore_index 参数设置为 True:
df_row_reindex = pd.concat([df1, df2], ignore_index=True)
df_row_reindex
| id | Feature1 | Feature2 | |
|---|---|---|---|
| 0 | 1 | A | B |
| 1 | 2 | C | D |
| 2 | 3 | E | F |
| 3 | 4 | G | H |
| 4 | 5 | I | J |
| 5 | 1 | K | L |
| 6 | 2 | M | N |
| 7 | 6 | O | P |
| 8 | 7 | Q | R |
| 9 | 8 | S | T |
现在行标签正确了!
pandas 还提供了在拼接后为各个 DataFrame 打上标签的选项,这样您就能知道每条数据来自哪个 DataFrame。为此,可以通过 keys 传入标签名列表。在这里,仍对 df1 和 df2 进行相同的拼接,并将其键分别设为 x 和 y。
frames = [df1,df2]
df_keys = pd.concat(frames, keys=['x', 'y'])
df_keys
| id | Feature1 | Feature2 | ||
|---|---|---|---|---|
| x | 0 | 1 | A | B |
| 1 | 2 | C | D | |
| 2 | 3 | E | F | |
| 3 | 4 | G | H | |
| 4 | 5 | I | J | |
| y | 0 | 1 | K | L |
| 1 | 2 | M | N | |
| 2 | 6 | O | P | |
| 3 | 7 | Q | R | |
| 4 | 8 | S | T |
通过指定 keys,检索某个特定 DataFrame 的数据也更容易。例如可以使用 loc 方法取回标签为 y 的 df2 数据:
df_keys.loc['y']
| id | Feature1 | Feature2 | |
|---|---|---|---|
| 0 | 1 | K | L |
| 1 | 2 | M | N |
| 2 | 6 | O | P |
| 3 | 7 | Q | R |
| 4 | 8 | S | T |
您也可以向 concat() 传入字典,此时字典的键将被用作 keys 参数(除非另行指定 keys):
pieces = {'x': df1, 'y': df2}
df_piece = pd.concat(pieces)
df_piece
| id | Feature1 | Feature2 | ||
|---|---|---|---|---|
| x | 0 | 1 | A | B |
| 1 | 2 | C | D | |
| 2 | 3 | E | F | |
| 3 | 4 | G | H | |
| 4 | 5 | I | J | |
| y | 0 | 1 | K | L |
| 1 | 2 | M | N | |
| 2 | 6 | O | P | |
| 3 | 7 | Q | R | |
| 4 | 8 | S | T |
值得注意的是,concat() 会对数据进行完整拷贝,频繁使用可能造成明显的性能开销。对于大规模数据操作,可考虑了解pandas 2.0 与 polars 在高性能数据处理上的对比。如果需要对多个数据集执行此操作,请使用列表推导式。
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 | Feature1 | Feature2 | id | Feature1 | Feature2 | |
|---|---|---|---|---|---|---|
| 0 | 1 | A | B | 1 | K | L |
| 1 | 2 | C | D | 2 | M | N |
| 2 | 3 | E | F | 6 | O | P |
| 3 | 4 | G | H | 7 | Q | R |
| 4 | 5 | I | J | 8 | S | T |
pandas 合并 DataFrame
与 DataFrame 相关的另一类常见操作是合并(merge)。两个 DataFrame 可能基于某个共同特征/列,保存关于同一实体的不同信息。为连接这些 DataFrame,pandas 提供了多种函数,如 concat()、merge()、join() 等。本节将练习使用 pandas 的 merge() 函数。
您可以将沿行拼接得到的 df_row(由 df1 和 df2 拼接而成)与 df3 在共有列(或键)id 上进行连接。为此,将 DataFrame 名称和 on 参数(共有列名,这里为 id)传给 merge() 函数:
df_merge_col = pd.merge(df_row, df3, on='id')
df_merge_col
| id | Feature1 | Feature2 | Feature3 | |
|---|---|---|---|---|
| 0 | 1 | A | B | 12 |
| 1 | 1 | K | L | 12 |
| 2 | 2 | C | D | 13 |
| 3 | 2 | M | N | 13 |
| 4 | 3 | E | F | 14 |
| 5 | 4 | G | H | 15 |
| 6 | 5 | I | J | 16 |
| 7 | 7 | Q | R | 17 |
| 8 | 8 | S | T | 15 |
可以看到,两个 DataFrame 已根据二者 id 列中的共有值合并为一个 DataFrame。比如,这里 id 值 1 在 df_row 中同时对应 A、B 和 K、L,因此在最终的 df_merge_col 中该 id 出现了两次;对应的 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 | Feature1 | Feature2 | Feature3 | |
|---|---|---|---|---|
| 0 | 1 | A | B | 12 |
| 1 | 1 | K | L | 12 |
| 2 | 2 | C | D | 13 |
| 3 | 2 | M | N | 13 |
| 4 | 3 | E | F | 14 |
| 5 | 4 | G | H | 15 |
| 6 | 5 | I | J | 16 |
| 7 | 7 | Q | R | 17 |
| 8 | 8 | S | T | 15 |
您也可以使用 pd.concat() 向 DataFrame 添加新行。先用行数据创建一个新的 DataFrame,再与原 DataFrame 拼接即可:
add_row = pd.DataFrame([['10', 'X1', 'X2', 'X3']],
columns=['id', 'Feature1', 'Feature2', 'Feature3'])
df_add_row = pd.concat([df_merge_col, add_row], ignore_index=True)
df_add_row
| id | Feature1 | Feature2 | Feature3 | |
|---|---|---|---|---|
| 0 | 1 | A | B | 12 |
| 1 | 1 | K | L | 12 |
| 2 | 2 | C | D | 13 |
| 3 | 2 | M | N | 13 |
| 4 | 3 | E | F | 14 |
| 5 | 4 | G | H | 15 |
| 6 | 5 | I | J | 16 |
| 7 | 7 | Q | R | 17 |
| 8 | 8 | S | T | 15 |
| 9 | 10 | X1 | X2 | X3 |
pandas 连接类型
本节将练习在共有列/键上合并 pandas DataFrame 的多种连接逻辑。这些逻辑与在 SQL 中连接表的方式非常相似。如果您熟悉SQL 中的内连接与外连接,会觉得这些概念很亲切。
| 连接类型 | SQL 等价 | 结果 | 使用场景 |
|---|---|---|---|
inner | INNER JOIN | 仅来自两个 DataFrame 的匹配行 | 当您只需要两个表中都存在的记录时 |
left | LEFT OUTER JOIN | 左表全部行 + 右表匹配行 | 保留主 DataFrame 的全部记录 |
right | RIGHT OUTER JOIN | 右表全部行 + 左表匹配行 | 保留次要 DataFrame 的全部记录 |
outer | FULL OUTER JOIN | 两个 DataFrame 的所有行 | 当您需要两个表的完全并集时 |
全外连接(Full Outer Join)
FULL OUTER JOIN 相当于左外连接和右外连接结果的并集。合并后的 DataFrame 将包含两个 DataFrame 的所有记录,并在任一侧缺少匹配时填充NaN 值。在 merge() 中将 how 参数指定为 outer 即可执行全外连接:
df_outer = pd.merge(df1, df2, on='id', how='outer')
df_outer
| id | Feature1_x | Feature2_x | Feature1_y | Feature2_y | |
|---|---|---|---|---|---|
| 0 | 1 | A | B | K | L |
| 1 | 2 | C | D | M | N |
| 2 | 3 | E | F | NaN | NaN |
| 3 | 4 | G | H | NaN | NaN |
| 4 | 5 | I | J | NaN | NaN |
| 5 | 6 | NaN | NaN | O | P |
| 6 | 7 | NaN | NaN | Q | R |
| 7 | 8 | NaN | NaN | S | T |
您会发现,结果 DataFrame 包含了两个表的所有记录,且在任一侧没有匹配时以 NaN 填充。另一个需要注意的是,为区分列来源而自动追加的列名后缀。默认后缀是 x 和 y,不过您可以通过 merge() 的 suffixes 参数进行自定义:
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 | A | B | K | L |
| 1 | 2 | C | D | M | N |
| 2 | 3 | E | F | NaN | NaN |
| 3 | 4 | G | H | NaN | NaN |
| 4 | 5 | I | J | NaN | NaN |
| 5 | 6 | NaN | NaN | O | P |
| 6 | 7 | NaN | NaN | Q | R |
| 7 | 8 | NaN | NaN | S | T |
内连接(Inner Join)
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 | A | B | K | L |
| 1 | 2 | C | D | M | N |
右连接(Right Join)
RIGHT JOIN 会产生来自 DataFrame B(右侧 DataFrame)的完整记录集,并在可匹配的地方补上 DataFrame A(左侧 DataFrame)的记录。若无匹配,右侧将包含空值。将 merge() 的 how 参数设为 right 即可执行右连接:
df_right = pd.merge(df1, df2, on='id', how='right')
df_right
| id | Feature1_x | Feature2_x | Feature1_y | Feature2_y | |
|---|---|---|---|---|---|
| 0 | 1 | A | B | K | L |
| 1 | 2 | C | D | M | N |
| 2 | 6 | NaN | NaN | O | P |
| 3 | 7 | NaN | NaN | Q | R |
| 4 | 8 | NaN | NaN | S | T |
左连接(Left Join)
LEFT JOIN 会产生来自 DataFrame A(左侧 DataFrame)的完整记录集,并在可匹配的地方补上 DataFrame B(右侧 DataFrame)的记录。若无匹配,左侧将包含空值。将 merge() 的 how 参数设为 left 即可执行左连接:
df_left = pd.merge(df1, df2, on='id', how='left')
df_left
| id | Feature1_x | Feature2_x | Feature1_y | Feature2_y | |
|---|---|---|---|---|---|
| 0 | 1 | A | B | K | L |
| 1 | 2 | C | D | M | N |
| 2 | 3 | E | F | NaN | NaN |
| 3 | 4 | G | H | NaN | NaN |
| 4 | 5 | I | J | 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 | A | B | 1 | K | L |
| 1 | 2 | C | D | 2 | M | N |
| 2 | 3 | E | F | 6 | O | P |
| 3 | 4 | G | H | 7 | Q | R |
| 4 | 5 | I | J | 8 | S | T |
pandas Join
pandas 的 DataFrame.join() 方法用于在唯一索引上连接 DataFrame。您可以使用可选参数 on 指定基于索引进行连接的列名(们),并通过 how 参数控制两个对象的连接方式。默认使用内连接。
pandas 连接两个 DataFrame
我们来用 .join 连接两个数据框。这里提供了 `lsuffix` 和 `rsuffix` 以避免列名重叠报错。它是基于索引而非列进行连接,因此需要更改 ‘id’ 列或提供后缀。
df2.join(df3, lsuffix='_left', rsuffix='_right')
| id_left | Feature1 | Feature2 | id_right | Feature3 | |
|---|---|---|---|---|---|
| 0 | 1 | K | L | 1 | 12 |
| 1 | 2 | M | N | 2 | 13 |
| 2 | 6 | O | P | 3 | 14 |
| 3 | 7 | Q | R | 4 | 15 |
| 4 | 8 | S | T | 5 | 16 |
我们也可以使用 on 参数在索引上连接列。要成功应用该连接,需要将 df3 的 ‘id’ 列设为索引,并在 on 参数中提供 ‘id’ 列。默认情况下,它会使用左连接。
df2.join(df3.set_index('id'), on='id')
| id | Feature1 | Feature2 | Feature3 | |
|---|---|---|---|---|
| 0 | 1 | K | L | 12.0 |
| 1 | 2 | M | N | 13.0 |
| 2 | 6 | O | P | NaN |
| 3 | 7 | Q | R | 17.0 |
| 4 | 8 | S | T | 15.0 |
与 merge 函数类似,我们可以通过 how 参数改变 join 的方式。这里将使用内连接。
df2.join(df3.set_index('id'), on='id', how = "inner")
| id_left | Feature1 | Feature2 | Feature3 | |
|---|---|---|---|---|
| 0 | 1 | K | L | 12 |
| 1 | 2 | M | N | 13 |
| 3 | 7 | Q | R | 17 |
| 4 | 8 | S | T | 15 |
时间序列友好型合并
pandas 为合并时间序列 DataFrame 提供了特殊函数,其中最常用也最有用的是 merge_asof()。merge_asof() 类似于有序的左连接,但匹配的是最近的键而非完全相等的键。对于左侧 DataFrame 的每一行,它会选择右侧 DataFrame 中 on 键小于左侧键的最后一行。两个 DataFrame 都必须按该键排序。
可选地,asof 合并可以执行分组级别的合并。它在对 on 键进行最近匹配的同时,还要求 by 键相等。
例如,您可能有交易数据(trades)和报价数据(quotes),希望对它们进行 asof 合并。这里将左侧 DataFrame 设为 trades,右侧 DataFrame 设为 quotes。它们按时间键 time 进行 asof 合并,并按股票代码进行分组合并。
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
| time | ticker | price | quantity | |
|---|---|---|---|---|
| 0 | 2016-05-25 13:30:00.023 | MSFT | 51.95 | 75 |
| 1 | 2016-05-25 13:30:00.038 | MSFT | 51.95 | 155 |
| 2 | 2016-05-25 13:30:00.048 | GOOG | 720.77 | 100 |
| 3 | 2016-05-25 13:30:00.048 | GOOG | 720.92 | 100 |
| 4 | 2016-05-25 13:30:00.048 | AAPL | 98.00 | 100 |
quotes
| time | ticker | bid | ask | |
|---|---|---|---|---|
| 0 | 2016-05-25 13:30:00.023 | GOOG | 720.50 | 720.93 |
| 1 | 2016-05-25 13:30:00.023 | MSFT | 51.95 | 51.96 |
| 2 | 2016-05-25 13:30:00.030 | MSFT | 51.97 | 51.98 |
| 3 | 2016-05-25 13:30:00.041 | MSFT | 51.99 | 52.00 |
| 4 | 2016-05-25 13:30:00.048 | GOOG | 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 | GOOG | 720.50 | 720.88 |
| 7 | 2016-05-25 13:30:00.075 | MSFT | 52.01 | 52.03 |
df_merge_asof = pd.merge_asof(trades, quotes,
on='time',
by='ticker')
df_merge_asof
| time | ticker | price | quantity | bid | ask | |
|---|---|---|---|---|---|---|
| 0 | 2016-05-25 13:30:00.023 | MSFT | 51.95 | 75 | 51.95 | 51.96 |
| 1 | 2016-05-25 13:30:00.038 | MSFT | 51.95 | 155 | 51.97 | 51.98 |
| 2 | 2016-05-25 13:30:00.048 | GOOG | 720.77 | 100 | 720.50 | 720.93 |
| 3 | 2016-05-25 13:30:00.048 | GOOG | 720.92 | 100 | 720.50 | 720.93 |
| 4 | 2016-05-25 13:30:00.048 | AAPL | 98.00 | 100 | NaN | NaN |
如果您细心观察,会发现 AAPL 这一行出现 NaN 的原因。因为右侧的 quotes DataFrame 中,对于 AAPL,并不存在一个时间小于左表 13:30:00.048 的记录,所以 bid 和 ask 列被填充为 NaN。
您还可以为时间列设置预定义的容差水平。假设您只希望报价时间与成交时间在 2ms 以内才进行 asof 合并,那么需要指定 tolerance 参数:
df_merge_asof_tolerance = pd.merge_asof(trades, quotes,
on='time',
by='ticker',
tolerance=pd.Timedelta('2ms'))
df_merge_asof_tolerance
| time | ticker | price | quantity | bid | ask | |
|---|---|---|---|---|---|---|
| 0 | 2016-05-25 13:30:00.023 | MSFT | 51.95 | 75 | 51.95 | 51.96 |
| 1 | 2016-05-25 13:30:00.038 | MSFT | 51.95 | 155 | NaN | NaN |
| 2 | 2016-05-25 13:30:00.048 | GOOG | 720.77 | 100 | 720.50 | 720.93 |
| 3 | 2016-05-25 13:30:00.048 | GOOG | 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 进行拼接与合并。最后还实践了用于时间序列 DataFrame 的特殊函数 merge_asof()。在此过程中,您也学习了如何处理DataFrame 索引。pandas 中还有许多用于连接 DataFrame 的选项,建议您查阅其精彩的官方文档。祝您探索愉快!
本教程参考了这份 pandas 文档。
如果您想进一步学习 pandas,请参加 DataCamp 的 Data Manipulation with pandas 课程,并查看我们的 Python Pandas DataFrame 教程。
DataCamp 还提供多篇实用的 pandas 教程,包括:
- 在 pandas 中导入 CSV 数据
- 在 pandas 中添加列
- 在 pandas 中排序
- 使用
drop_duplicates在 pandas 中去重 - 使用 pandas groupby 分组数据
- 在 pandas 中重置 DataFrame 索引
学习愉快!
常见问题
pandas 中 merge() 与 join() 有何区别?
merge() 更灵活,可在任意列上进行连接,类似 SQL 的连接。join() 主要用于基于索引的连接,是对 merge() 的便捷封装。若按列连接,使用 merge();若连接键已设为索引,使用 join()。
如何在 pandas 中基于多列进行合并?
将列名列表传给 on 参数:pd.merge(df1, df2, on=['col1', 'col2'])。如果两个 DataFrame 的列名不同,使用带列表的 left_on 和 right_on:pd.merge(df1, df2, left_on=['a', 'b'], right_on=['c', 'd'])。
合并 DataFrame 时缺失值会怎样处理?
具体行为取决于连接类型。inner 连接会排除无匹配的行。left、right 或 outer 连接会保留未匹配行,并在来自另一 DataFrame 的列中填充 NaN。您可以在合并后使用 fillna() 或 dropna() 进行处理。
在 pandas 中,何时应使用 concat(),何时应使用 merge()?
使用 concat() 根据索引对齐,纵向(添加行)或横向(添加列)拼接 DataFrame。需要基于共有列值进行组合、类似 SQL 连接时使用 merge()。可将 concat() 理解为把 DataFrame “粘”在一起,而 merge() 则是关系型连接。
合并后遇到重复列名应如何处理?
默认情况下,pandas 会对重复的列名添加后缀 _x 和 _y。可通过 suffixes 参数自定义:pd.merge(df1, df2, on='id', suffixes=('_left', '_right'))。合并后,您可以使用 df.rename(columns={'old': 'new'}) 重命名列,或用 df.drop(columns=['col_y']) 删除重复列。
pandas 中 merge() 与 join() 有何区别?
merge() 更灵活,可在任意列上进行连接,类似 SQL 的连接。join() 主要用于基于索引的连接,是对 merge() 的便捷封装。若按列连接,使用 merge();若连接键已设为索引,使用 join()。
如何在 pandas 中基于多列进行合并?
将列名列表传给 on 参数:pd.merge(df1, df2, on=['col1', 'col2'])。如果两个 DataFrame 的列名不同,使用带列表的 left_on 和 right_on:pd.merge(df1, df2, left_on=['a', 'b'], right_on=['c', 'd'])。
合并 DataFrame 时缺失值会怎样处理?
具体行为取决于连接类型。inner 连接会排除无匹配的行。left、right 或 outer 连接会保留未匹配行,并在来自另一 DataFrame 的列中填充 NaN。您可以在合并后使用 fillna() 或 dropna() 进行处理。
在 pandas 中,何时应使用 concat(),何时应使用 merge()?
使用 concat() 根据索引对齐,纵向(添加行)或横向(添加列)拼接 DataFrame。需要基于共有列值进行组合、类似 SQL 连接时使用 merge()。可将 concat() 理解为把 DataFrame “粘”在一起,而 merge() 则是关系型连接。
合并后遇到重复列名应如何处理?
默认情况下,pandas 会对重复的列名添加后缀 _x 和 _y。可通过 suffixes 参数自定义:pd.merge(df1, df2, on='id', suffixes=('_left', '_right'))。合并后,您可以使用 df.rename(columns={'old': 'new'}) 重命名列,或用 df.drop(columns=['col_y']) 删除重复列。
您是否尝试过解 Kaggle 挑战?如果试过,您可能会注意到,大多数挑战提供的数据分散在多个文件中,而且有些列会在不止一个文件里出现。那么,您首先想到的是什么?当然是把它们“连接”起来!
在本教程中,您将练习几种标准的 pandas 连接技巧。更具体地说,您将学会:
在此过程中,您还会学到一些在连接前后常用的小技巧。
要点速览
pd.concat()纵向(行)或横向(列)堆叠 DataFramepd.merge()在共有列上连接 DataFrame,类似 SQL 的 joinshow参数指定连接类型:'inner'、'outer'、'left'或'right'.join()基于索引连接 DataFramepd.merge_asof()处理时间序列数据中的“最近键”匹配pandas 连接
连接和合并 DataFrame 是开展数据分析与机器学习任务的核心步骤之一。每位数据分析师或数据科学家都应熟练掌握这套工具,因为几乎所有情况下,数据都来自多个来源和文件。您可能需要通过某种连接逻辑把所有数据汇总到一起,然后再开始分析。熟悉SQL 连接的人会深知此任务的重要性。即使是要在某些数据上训练机器学习模型,也可能需要先把多个 CSV 文件合并到一个 DataFrame 中。
幸好,Python 中最受欢迎的库之一 pandas 可以助您一臂之力!pandas 提供多种便捷方式,将 Series 和 DataFrame 组合在一起,并支持基于索引的集合逻辑以及面向连接/合并操作的关系代数功能。