精通 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)
“`
解释:
- 我们将
df1和df2放在一个列表中[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 的列。df3和df4共同拥有的列是['商品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时,原始的索引(df5和df6中的 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沿着列方向进行连接。 df7和df8的行索引是完全相同的 (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'。它会取df9和df8行索引的并集作为结果 DataFrame 的行索引。这里的并集是['user_001', 'user_002', 'user_003', 'user_004', 'user_005']。- 对于结果 DataFrame 中的每一行索引,如果在某个原始 DataFrame 中不存在该索引,那么该原始 DataFrame 对应的所有列的值都将填充
NaN。例如,user_003只存在于df8的索引中,所以结果中user_003这一行的姓名和年龄列为NaN。user_004和user_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=1和join='inner'。它会取df9和df8行索引的交集作为结果 DataFrame 的行索引。df9和df8共同拥有的行索引是['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=1和keys=['基本信息', '联系方式']。 - 结果 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. concat 与 append (已弃用)
在旧版本的 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 中经常令人困惑的地方。理解 concat 与 merge/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) 拼接 df7 和 df8:
“`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的右侧。新列的名称是s4的name属性 (用户等级)。 - 如果 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参数控制如何处理非连接轴上的索引/列 (outer或inner)。ignore_index=True用于生成新的连续整数索引,丢弃原始索引。keys参数用于创建 MultiIndex,方便标记数据来源。
熟练掌握 concat() 及其参数,能够帮助您高效地整合分散的数据,为后续的数据清洗、分析和建模奠定坚实基础。请务必结合 merge() 和 join() 的使用场景,选择最适合您数据合并需求的工具。通过实践和尝试不同的参数组合,您将能够更自信地处理各种复杂的数据整合任务。
希望本文的详细讲解和丰富的示例能够帮助您全面掌握 pandas.concat() 的用法。祝您在数据探索的旅程中一帆风顺!