如何使用 Pandas concat 合并 DataFrame – wiki基地


精通 Pandas concat:数据合并的艺术与实践

在数据分析和处理的浩瀚海洋中,数据往往不是以单一、完美的表格形式呈现的。我们经常需要从多个来源收集数据,或者将经过分割处理的数据重新整合在一起。这时,Pandas 库中强大的数据合并功能就显得尤为重要。Pandas 提供了多种合并数据的函数,其中 pandas.concat() 是一个基石,它允许我们在指定的轴(行或列)上简单地堆叠(stack)或连接(concatenate)多个 Series 或 DataFrame 对象。

与侧重于数据库风格的、基于列值进行匹配连接的 merge()join() 不同,concat() 的核心是沿着轴方向进行数据的“拼接”。理解并熟练掌握 concat() 是高效处理复杂数据集的关键一步。

本文将深入探讨 pandas.concat() 的各种用法、参数及其背后的逻辑,通过丰富的代码示例,帮助您全面掌握这一强大的工具。

1. 理解 pandas.concat() 的基本原理

pandas.concat() 函数用于在某个轴向上连接 Pandas 对象(Series 或 DataFrame)。它的基本思想是将多个对象“粘合”在一起,就像拼接乐高积木一样。

核心参数:

  • objs: 这是唯一必须指定的参数,它是一个包含要连接的 Pandas 对象的序列(如列表或元组)。这些对象通常是 DataFrame,也可以是 Series。
  • axis: 指定在哪一个轴上进行连接。
    • axis=0 (默认值): 沿着行方向进行连接,将对象垂直堆叠。
    • axis=1: 沿着列方向进行连接,将对象水平拼接。
  • join: 指定如何处理其他轴上的索引(当 axis=0 时处理列索引,当 axis=1 时处理行索引)。
    • 'outer' (默认值): 取所有对象的索引的并集,缺失的地方填充 NaN
    • 'inner': 取所有对象的索引的交集,只保留所有对象中都存在的索引。
  • ignore_index: 如果设置为 True,连接后的结果将生成一个新的从 0 到 n-1 的整数索引,而忽略原始对象的索引。这在原始索引没有意义或包含重复项时非常有用。如果设置为 False (默认值),则保留原始索引。
  • keys: 如果指定,它将为连接后的结果生成一个分层索引(MultiIndex)。当 axis=0 时,keys 会成为新的行索引的最外层;当 axis=1 时,keys 会成为新的列索引的最外层。这对于标记每个数据块的来源非常有用。
  • names: 与 keys 结合使用,为生成的 MultiIndex 的层级指定名称。
  • verify_integrity: 如果设置为 True,在连接完成后检查新轴上的索引是否有重复项。如果有,将抛出 ValueError。默认是 False,允许重复索引。
  • sort: 在非连接轴上按字典顺序对连接结果进行排序。默认是 False,保留原始对象的列/行顺序(取决于 axis)。

2. 垂直堆叠:沿着行轴连接 (axis=0)

这是 concat() 最常见的用法,用于将具有相似结构(列名)的数据集堆叠起来,形成一个更长的 DataFrame。

示例 1: 简单的垂直堆叠 (列名完全相同)

假设我们有两个 DataFrame,代表两个不同时间段的销售数据,它们的列结构是相同的。

“`python
import pandas as pd
import numpy as np

数据框 1:上半年的销售数据

data1 = {‘商品ID’: [‘A001’, ‘A002’, ‘A003’, ‘A004’],
‘销售额’: [1500, 2000, 1200, 1800],
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’]}
df1 = pd.DataFrame(data1, index=[‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’])

print(“DataFrame 1 (df1):”)
print(df1)
print(“-” * 30)

数据框 2:下半年的销售数据

data2 = {‘商品ID’: [‘A005’, ‘A006’, ‘A007’, ‘A008’],
‘销售额’: [2500, 3000, 1600, 2200],
‘城市’: [‘北京’, ‘上海’, ‘深圳’, ‘广州’]}
df2 = pd.DataFrame(data2, index=[‘May’, ‘Jun’, ‘Jul’, ‘Aug’])

print(“DataFrame 2 (df2):”)
print(df2)
print(“-” * 30)

使用 concat 垂直堆叠

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

print(“垂直堆叠结果 (result_v_simple):”)
print(result_v_simple)
print(“-” * 30)
“`

解释:

  • 我们将 df1df2 放在一个列表中 [df1, df2] 作为 objs 参数传递给 pd.concat()
  • 由于没有指定 axis 参数,它默认使用 axis=0,即沿着行方向进行连接。
  • 两个 DataFrame 的列名完全相同,因此 concat 直接将 df2 的行附加到 df1 的下方。
  • 原始的行索引(’Jan’, ‘Feb’, …, ‘Aug’)被保留下来。注意到结果中可能存在重复的索引,这在 concat 的默认行为中是允许的。

示例 2: 垂直堆叠 (列名不完全相同) – join='outer' (默认)

现在假设两个 DataFrame 有一些不同的列。

“`python

数据框 3:包含额外信息的上半年的销售数据

data3 = {‘商品ID’: [‘A001’, ‘A002’, ‘A003’, ‘A004’],
‘销售额’: [1500, 2000, 1200, 1800],
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’],
‘类别’: [‘电子’, ‘服装’, ‘家居’, ‘电子’]} # 新增列 ‘类别’
df3 = pd.DataFrame(data3, index=[‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’])

print(“DataFrame 3 (df3):”)
print(df3)
print(“-” * 30)

数据框 4:包含不同额外信息的下半年的销售数据

data4 = {‘商品ID’: [‘A005’, ‘A006’, ‘A007’, ‘A008’],
‘销售额’: [2500, 3000, 1600, 2200],
‘国家’: [‘中国’, ‘中国’, ‘中国’, ‘中国’]} # 新增列 ‘国家’,缺少 ‘城市’ 和 ‘类别’
df4 = pd.DataFrame(data4, index=[‘May’, ‘Jun’, ‘Jul’, ‘Aug’])

print(“DataFrame 4 (df4):”)
print(df4)
print(“-” * 30)

使用 concat 垂直堆叠,默认 join=’outer’

result_v_outer = pd.concat([df3, df4])

print(“垂直堆叠结果 (result_v_outer, join=’outer’):”)
print(result_v_outer)
print(“-” * 30)
“`

解释:

  • df3 有列 商品ID, 销售额, 城市, 类别
  • df4 有列 商品ID, 销售额, 国家
  • concat 默认 join='outer',这意味着它会取所有输入 DataFrame 的列名的并集作为结果 DataFrame 的列。这里的列并集是 ['商品ID', '销售额', '城市', '类别', '国家']
  • 对于每个原始 DataFrame,如果在并集中存在的某个列在它自身中不存在,那么该列的对应位置将填充 NaN (Not a Number),表示缺失值。例如,df3 没有 国家 列,所以在结果中 df3 对应的行,国家 列的值就是 NaN。同理,df4 没有 城市类别 列,对应的值就是 NaN

示例 3: 垂直堆叠 (列名不完全相同) – join='inner'

如果我们只关心所有 DataFrame 都共同拥有的列,可以使用 join='inner'

“`python

使用 concat 垂直堆叠,指定 join=’inner’

result_v_inner = pd.concat([df3, df4], join=’inner’)

print(“垂直堆叠结果 (result_v_inner, join=’inner’):”)
print(result_v_inner)
print(“-” * 30)
“`

解释:

  • concat 使用 join='inner',这意味着它会取所有输入 DataFrame 的列名的交集作为结果 DataFrame 的列。
  • df3df4 共同拥有的列是 ['商品ID', '销售额']
  • 结果 DataFrame 只包含这些共同列,并丢弃了在某些输入 DataFrame 中不存在的列 (城市, 类别, 国家)。

示例 4: 忽略原始索引 (ignore_index=True)

当原始 DataFrame 的索引没有特定意义(例如,它们只是默认的整数索引,或者您不关心它们)时,可以使用 ignore_index=True 来生成一个新的从 0 开始的整数索引。

“`python

数据框 5:使用默认整数索引

data5 = {‘名称’: [‘张三’, ‘李四’], ‘年龄’: [25, 30]}
df5 = pd.DataFrame(data5) # 默认索引 0, 1

print(“DataFrame 5 (df5):”)
print(df5)
print(“-” * 30)

数据框 6:使用默认整数索引

data6 = {‘名称’: [‘王五’, ‘赵六’], ‘年龄’: [22, 28]}
df6 = pd.DataFrame(data6) # 默认索引 0, 1

print(“DataFrame 6 (df6):”)
print(df6)
print(“-” * 30)

使用 concat 垂直堆叠,忽略原始索引

result_v_ignore_index = pd.concat([df5, df6], ignore_index=True)

print(“垂直堆叠结果 (result_v_ignore_index, ignore_index=True):”)
print(result_v_ignore_index)
print(“-” * 30)

对比不忽略索引的情况 (会有重复索引)

result_v_keep_index = pd.concat([df5, df6], ignore_index=False) # 或者不指定 ignore_index
print(“垂直堆叠结果 (result_v_keep_index, ignore_index=False):”)
print(result_v_keep_index)
print(“-” * 30)
“`

解释:

  • ignore_index=True 时,原始的索引(df5df6 中的 0, 1)被完全丢弃。
  • 结果 DataFrame 获得一个新的、连续的整数索引 (0, 1, 2, 3)。
  • ignore_index=False (默认) 时,原始索引被保留,导致结果 DataFrame 中出现了重复的索引值 (0 和 1 各出现两次)。虽然 Pandas 允许重复索引,但在某些操作中可能会引起混淆或错误。

示例 5: 使用 keys 添加分层索引

当您从多个来源连接数据时,保留每个数据块的来源信息是很重要的。keys 参数可以帮助您做到这一点,它会创建一个 MultiIndex (分层索引)来标记数据来自哪个原始 DataFrame。

“`python

沿用 df1 和 df2

print(“DataFrame 1 (df1):”)
print(df1)
print(“-” * 30)
print(“DataFrame 2 (df2):”)
print(df2)
print(“-” * 30)

使用 keys 进行垂直堆叠,添加分层索引

result_v_keys = pd.concat([df1, df2], keys=[‘上半年’, ‘下半年’])

print(“垂直堆叠结果 (result_v_keys, 使用 keys):”)
print(result_v_keys)
print(“-” * 30)
print(“结果索引类型:”, type(result_v_keys.index))
print(“结果索引层级:”, result_v_keys.index.names)
“`

解释:

  • 我们将一个列表 ['上半年', '下半年'] 作为 keys 参数传递。列表中的每个元素对应于 objs 列表中的一个 DataFrame。
  • 结果 DataFrame 的行索引变为了一个 MultiIndex。最外层索引 (上半年, 下半年) 来源于 keys 参数,用于标识数据来源。内层索引 (Jan, Feb, etc.) 保留了原始 DataFrame 的索引。
  • 这个分层索引使得按来源进行数据选择变得非常方便,例如 result_v_keys.loc['上半年'] 将会选择所有来源于 df1 的行。
  • 可以使用 names 参数为 MultiIndex 的各个层级指定名称,例如 pd.concat([df1, df2], keys=['上半年', '下半年'], names=['时期', '月份'])

3. 水平拼接:沿着列轴连接 (axis=1)

当您想将具有相同行索引的不同数据集水平拼接在一起时,可以使用 axis=1。这类似于 SQL 中的 JOIN 操作,但 concat 是基于索引进行对齐,而不是基于某个特定的列值。

示例 6: 简单的水平拼接 (行索引完全相同)

假设我们有两个 DataFrame,一个包含用户基本信息,另一个包含用户的联系方式,它们的用户ID(作为索引)是完全匹配的。

“`python

数据框 7:用户基本信息

data7 = {‘姓名’: [‘张三’, ‘李四’, ‘王五’],
‘年龄’: [25, 30, 22]}
df7 = pd.DataFrame(data7, index=[‘user_001’, ‘user_002’, ‘user_003’])

print(“DataFrame 7 (df7):”)
print(df7)
print(“-” * 30)

数据框 8:用户联系方式

data8 = {‘邮箱’: [‘[email protected]’, ‘[email protected]’, ‘[email protected]’],
‘电话’: [‘13800001111’, ‘13900002222’, ‘13000003333’]}
df8 = pd.DataFrame(data8, index=[‘user_001’, ‘user_002’, ‘user_003’])

print(“DataFrame 8 (df8):”)
print(df8)
print(“-” * 30)

使用 concat 水平拼接 (行索引完全匹配)

result_h_simple = pd.concat([df7, df8], axis=1)

print(“水平拼接结果 (result_h_simple):”)
print(result_h_simple)
print(“-” * 30)
“`

解释:

  • 指定 axis=1 告诉 concat 沿着列方向进行连接。
  • df7df8 的行索引是完全相同的 (user_001, user_002, user_003)。
  • concat 按照行索引进行对齐,并将 df8 的列附加到 df7 的右侧。结果 DataFrame 的行索引保留了原始索引。

示例 7: 水平拼接 (行索引不完全相同) – join='outer' (默认)

如果两个 DataFrame 的行索引不完全匹配,concat 默认使用 join='outer'

“`python

数据框 9:用户基本信息 (包含一些新增和缺失用户)

data9 = {‘姓名’: [‘张三’, ‘李四’, ‘赵六’, ‘钱七’], # 新增赵六,钱七;缺失王五
‘年龄’: [25, 30, 28, 35]}
df9 = pd.DataFrame(data9, index=[‘user_001’, ‘user_002’, ‘user_004’, ‘user_005’]) # 索引不同

print(“DataFrame 9 (df9):”)
print(df9)
print(“-” * 30)

数据框 10:用户联系方式 (沿用 df8)

print(“DataFrame 10 (df10, 沿用 df8):”)
print(df8)
print(“-” * 30)

使用 concat 水平拼接,默认 join=’outer’

result_h_outer = pd.concat([df9, df8], axis=1)

print(“水平拼接结果 (result_h_outer, axis=1, join=’outer’):”)
print(result_h_outer)
print(“-” * 30)
“`

解释:

  • df9 的索引是 ['user_001', 'user_002', 'user_004', 'user_005']
  • df8 的索引是 ['user_001', 'user_002', 'user_003']
  • concat 使用 axis=1 和默认的 join='outer'。它会取 df9df8 行索引的并集作为结果 DataFrame 的行索引。这里的并集是 ['user_001', 'user_002', 'user_003', 'user_004', 'user_005']
  • 对于结果 DataFrame 中的每一行索引,如果在某个原始 DataFrame 中不存在该索引,那么该原始 DataFrame 对应的所有列的值都将填充 NaN。例如,user_003 只存在于 df8 的索引中,所以结果中 user_003 这一行的 姓名年龄 列为 NaNuser_004user_005 只存在于 df9 的索引中,所以结果中这两行的 邮箱电话 列为 NaN

示例 8: 水平拼接 (行索引不完全相同) – join='inner'

如果我们只关心所有 DataFrame 都共同拥有的行索引,可以使用 join='inner'

“`python

使用 concat 水平拼接,指定 join=’inner’

result_h_inner = pd.concat([df9, df8], axis=1, join=’inner’)

print(“水平拼接结果 (result_h_inner, axis=1, join=’inner’):”)
print(result_h_inner)
print(“-” * 30)
“`

解释:

  • concat 使用 axis=1join='inner'。它会取 df9df8 行索引的交集作为结果 DataFrame 的行索引。
  • df9df8 共同拥有的行索引是 ['user_001', 'user_002']
  • 结果 DataFrame 只包含这两行索引的数据,并丢弃了在某些输入 DataFrame 中不存在的行 (user_003, user_004, user_005)。

示例 9: 使用 keys 添加分层列索引 (axis=1)

axis=0 类似,当沿着列方向拼接多个 DataFrame 时,也可以使用 keys 参数为结果 DataFrame 的列创建一个 MultiIndex,以标识每列的来源。

“`python

沿用 df7 和 df8

print(“DataFrame 7 (df7):”)
print(df7)
print(“-” * 30)
print(“DataFrame 8 (df8):”)
print(df8)
print(“-” * 30)

使用 keys 进行水平拼接,添加分层列索引

result_h_keys = pd.concat([df7, df8], axis=1, keys=[‘基本信息’, ‘联系方式’])

print(“水平拼接结果 (result_h_keys, axis=1, 使用 keys):”)
print(result_h_keys)
print(“-” * 30)
print(“结果列索引类型:”, type(result_h_keys.columns))
print(“结果列索引层级:”, result_h_keys.columns.names)

如何访问特定来源的列

print(“\n访问来自’基本信息’的列:”)
print(result_h_keys[‘基本信息’])
“`

解释:

  • 指定 axis=1keys=['基本信息', '联系方式']
  • 结果 DataFrame 的列索引变为了一个 MultiIndex。最外层索引 (基本信息, 联系方式) 来源于 keys 参数,用于标识列的来源。内层索引 (姓名, 年龄, 邮箱, 电话) 保留了原始 DataFrame 的列名。
  • 可以使用 MultiIndex 的切片或标签访问特定来源的所有列。

4. ignore_index 的深度理解

ignore_index=True 是一个非常实用的参数,尤其是在进行垂直堆叠时,如果您不关心原始行的标识,只想简单地将数据堆叠起来并获得一个新的、干净的序列索引。

何时使用 ignore_index=True?

  • 当原始 DataFrame 的索引是默认的整数索引 (0, 1, 2…),且这些数字本身不代表任何含义。
  • 当原始 DataFrame 的索引有意义,但您希望结果 DataFrame 有一个全新的、从 0 开始的连续索引,例如在进行批量数据加载后重置索引。
  • 当垂直连接多个 DataFrame,且原始索引存在重复项,您希望避免结果中出现重复索引并获得一个唯一的序列索引。

需要注意什么?

  • 使用 ignore_index=True完全丢弃原始的行索引信息。如果您需要保留原始行的来源信息,应该考虑使用 keys 参数来创建 MultiIndex,而不是丢弃索引。
  • 这个参数主要用于 axis=0 的垂直连接。虽然也可以用于 axis=1,但在水平连接中行索引通常用于对齐,忽略行索引会导致结果失去原有的行对应关系,因此在 axis=1 中较少使用 ignore_index=True

5. concatappend (已弃用)

在旧版本的 Pandas 中,DataFrame 和 Series 有一个 append() 方法,用于方便地在末尾添加另一个 DataFrame 或 Series。例如 df1.append(df2)

然而,append() 方法已经被标记为弃用,并在未来的版本中会被移除。官方推荐使用 pandas.concat() 代替 append()

如何使用 concat 替代 append?

df1.append(df2) 等价于 pd.concat([df1, df2])

df1.append(df2, ignore_index=True) 等价于 pd.concat([df1, df2], ignore_index=True)

为什么推荐使用 concat?

  • concat 更加通用和灵活,它可以连接任意数量的对象,控制连接轴 (axis),以及更精细地控制索引 (join, ignore_index, keys)。
  • append 在底层实际上就是调用 concat,但接口功能较少。
  • 使用 concat 有助于编写更具前瞻性和兼容性的代码,避免在 Pandas 版本升级时遇到问题。

因此,在编写新的代码时,应该始终优先使用 pd.concat() 而不是已弃用的 append() 方法。

6. concat vs. merge vs. join

这是 Pandas 中经常令人困惑的地方。理解 concatmerge/join 的区别至关重要。

特性 pandas.concat() pandas.merge() & DataFrame.join()
主要功能 沿着指定轴(行或列)简单地拼接对象。 基于一个或多个索引的共同值进行数据库风格的合并
对齐方式 主要基于索引或直接按位置(当 ignore_index=True 或列名/索引完全匹配时)。 基于指定的列值索引值进行匹配。
输入对象 一个 Pandas 对象的列表或字典。 通常是两个 DataFrame。
连接轴 可以在 axis=0 (垂直) 或 axis=1 (水平) 上进行。 默认是水平连接,生成新的列。
列处理 axis=0: 根据 join 参数处理列的交集或并集axis=1: 直接拼接列。 根据指定的键列进行匹配,合并源 DataFrame 的列。
索引处理 axis=0: 根据 join 参数处理索引的交集或并集,保留或忽略原始索引。 axis=1: 根据 join 参数处理行索引的交集或并集 合并操作的结果索引取决于合并类型 (inner, outer, left, right) 和指定的键。
常见用途 – 堆叠相同结构的数据集 (例如,来自不同文件或批次的数据)。
– 将多个 Series 合并为 DataFrame。
– 将通过切片或筛选分割的数据块重新组合。
– 将来自不同表(DataFrame)的相关信息关联起来 (例如,订单表与客户信息表)。
– 添加基于共同标识符的额外列。

总结来说:

  • concat 适用于:当您想将数据简单地堆叠起来(增加行数,如果列不同则会产生 NaNs)或者将具有相同行索引的数据水平并排(增加列数,如果索引不同则产生 NaNs)。它不查找值进行匹配,而是直接沿着轴进行拼接。
  • merge/join 适用于:当您想通过查找一个或多个共同的标识符(如用户ID、订单号等)来组合数据。它们是基于值匹配的查找和合并操作。

示例对比:

使用 concat(axis=1) 拼接 df7df8

“`python

基于行索引对齐拼接

result_concat_h = pd.concat([df7, df8], axis=1)
print(“Concat (axis=1) 结果:”)
print(result_concat_h)
“`

使用 merge 基于共同列 (商品ID) 合并数据(假设需要先将索引变成列):

“`python

创建两个需要基于共同列合并的 DataFrame

df_sales = pd.DataFrame({‘商品ID’: [‘A001’, ‘A002’, ‘A003’],
‘销售额’: [100, 200, 150]})

df_product_info = pd.DataFrame({‘商品ID’: [‘A001’, ‘A002’, ‘A004’],
‘商品名称’: [‘电脑’, ‘手机’, ‘耳机’],
‘价格’: [5000, 3000, 500]})

print(“\nDataFrame Sales:”)
print(df_sales)
print(“\nDataFrame Product Info:”)
print(df_product_info)

使用 merge 基于 ‘商品ID’ 列合并

result_merge = pd.merge(df_sales, df_product_info, on=’商品ID’, how=’inner’)
print(“\nMerge 结果 (基于’商品ID’列):”)
print(result_merge)
“`

在这个 merge 示例中,合并是基于 '商品ID' 列的值进行的,只有 'A001''A002' 这两个共同的商品ID被保留下来(因为使用了 how='inner')。concat 则不会执行这种基于值的匹配查找,它只关心索引或位置。

7. 使用 Series 进行 concat

concat 函数不仅可以用于 DataFrame,也可以用于 Series,或者 DataFrame 和 Series 的混合连接。

示例 10: 连接 Series

“`python
s1 = pd.Series([1, 2, 3], index=[‘a’, ‘b’, ‘c’])
s2 = pd.Series([4, 5, 6], index=[‘d’, ‘e’, ‘f’])
s3 = pd.Series([7, 8, 9], index=[‘a’, ‘g’, ‘h’]) # 包含重复索引

print(“Series 1 (s1):”)
print(s1)
print(“\nSeries 2 (s2):”)
print(s2)
print(“\nSeries 3 (s3):”)
print(s3)

垂直连接 Series (默认 axis=0)

result_s_v = pd.concat([s1, s2, s3])
print(“\n垂直连接 Series 结果 (result_s_v):”)
print(result_s_v)
print(“-” * 30)

垂直连接 Series,忽略索引

result_s_v_ignore = pd.concat([s1, s2, s3], ignore_index=True)
print(“\n垂直连接 Series 结果 (result_s_v_ignore, ignore_index=True):”)
print(result_s_v_ignore)
print(“-” * 30)

垂直连接 Series,使用 keys

result_s_v_keys = pd.concat([s1, s2, s3], keys=[‘S1’, ‘S2’, ‘S3’])
print(“\n垂直连接 Series 结果 (result_s_v_keys, keys):”)
print(result_s_v_keys)
print(“-” * 30)
“`

解释:

  • 垂直连接 Series 与垂直连接 DataFrame 类似,只是结果是一个 Series(如果只有一个列)或 DataFrame(如果 Series 之间有索引差异或使用 keys)。
  • 默认 axis=0,直接堆叠 Series 的值,索引被保留(可能重复)。
  • ignore_index=True 丢弃原始索引,生成新的整数索引。
  • keys 参数为结果 Series (如果是一个 Series) 或 DataFrame (如果创建了 MultiIndex) 添加分层索引。

示例 11: 连接 DataFrame 和 Series

当 DataFrame 与 Series 进行 concat(axis=1) 操作时,Series 会被 treated as a single column DataFrame。

“`python

沿用 df7

print(“DataFrame 7 (df7):”)
print(df7)
print(“-” * 30)

Series:用户等级信息,索引与 df7 相同

s4 = pd.Series([‘Gold’, ‘Silver’, ‘Bronze’], index=[‘user_001’, ‘user_002’, ‘user_003′], name=’用户等级’) # 指定 Series 名称

print(“Series 4 (s4):”)
print(s4)
print(“-” * 30)

将 df7 和 s4 水平拼接

result_df_s_h = pd.concat([df7, s4], axis=1)

print(“水平拼接 DataFrame 和 Series 结果:”)
print(result_df_s_h)
print(“-” * 30)
“`

解释:

  • concat([df7, s4], axis=1)s4 视为一个单列的 DataFrame,并根据行索引与 df7 进行对齐。
  • 由于行索引完全匹配,s4 的值作为新的一列被添加到 df7 的右侧。新列的名称是 s4name 属性 (用户等级)。
  • 如果 Series 没有指定 name 属性,它在结果中可能获得一个默认的数字列名。

8. 性能考虑

对于小型到中型的数据集,concat 的性能通常不是问题。然而,当处理非常大的数据集时,连接操作可能会消耗较多内存和时间。

  • pd.concat() 通常比在循环中反复使用 append() 更高效,因为 concat 可以一次性处理所有对象,而 append 会在每次迭代中创建新的 DataFrame。
  • 预估结果 DataFrame 的总大小有助于避免内存溢出。
  • 如果连接多个具有相同列(或索引)但顺序不同的 DataFrame,设置 sort=False (默认值) 可以避免不必要的排序开销,但如果需要特定顺序的列/索引,则可能需要排序。
  • 在某些情况下,如果只是添加少量行到现有 DataFrame,预分配空间或使用列表存储数据然后一次性创建 DataFrame 可能比多次 concat 更高效。但对于通用合并任务,concat 是标准且优化的方法。

9. 常见陷阱与故障排除

  • 遗忘 axis 参数: concat 默认 axis=0。如果您想要水平连接,必须明确指定 axis=1。这是新手常犯的错误。结果通常是垂直堆叠而不是预期的水平拼接。
  • 索引不对齐导致 NaN 过多: 在使用 axis=1 (水平拼接) 时,如果两个 DataFrame 的行索引不完全匹配,默认的 join='outer' 会引入大量 NaN。请检查您的索引是否正确,或者考虑使用 join='inner' 如果您只关心共同的行。
  • 列不对齐导致 NaN 过多 (axis=0): 在使用 axis=0 (垂直堆叠) 时,如果两个 DataFrame 的列不完全匹配,默认的 join='outer' 会引入大量 NaN。请检查您的列名是否一致,或者考虑使用 join='inner' 如果您只关心共同的列。
  • 重复索引: 默认情况下 concat 允许重复索引。如果您的下游操作对索引唯一性有要求,可以考虑在 concat 之前或之后重置索引 (reset_index()),或者使用 ignore_index=True,或者在 concat 时设置 verify_integrity=True 来捕获重复索引错误。
  • 数据类型 (dtypes) 改变: 当连接包含不同数据类型的列时,Pandas 会尝试找到一个兼容的数据类型。这可能导致整数列变成浮点列(因为 NaN 是浮点类型)或对象列。在连接后检查 dtypes 是个好习惯,必要时进行类型转换。
  • 连接 Series 和 DataFrame: 当使用 axis=1 连接 Series 和 DataFrame 时,确保 Series 有一个 name 属性,这样在结果 DataFrame 中它会有一个有意义的列名。

10. 总结

pandas.concat() 是 Pandas 库中一个功能强大且灵活的数据合并工具。它允许您在指定的轴上简单地拼接多个 DataFrame 或 Series。

  • 使用 axis=0 进行垂直堆叠,增加行数。
  • 使用 axis=1 进行水平拼接,增加列数。
  • join 参数控制如何处理非连接轴上的索引/列 (outerinner)。
  • ignore_index=True 用于生成新的连续整数索引,丢弃原始索引。
  • keys 参数用于创建 MultiIndex,方便标记数据来源。

熟练掌握 concat() 及其参数,能够帮助您高效地整合分散的数据,为后续的数据清洗、分析和建模奠定坚实基础。请务必结合 merge()join() 的使用场景,选择最适合您数据合并需求的工具。通过实践和尝试不同的参数组合,您将能够更自信地处理各种复杂的数据整合任务。

希望本文的详细讲解和丰富的示例能够帮助您全面掌握 pandas.concat() 的用法。祝您在数据探索的旅程中一帆风顺!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部