Python 数据处理(三十一)—— 合并连接之 concat

前言

pandas 提供了各种工具,可以轻松地将不同的 SeriesDataFrame 连接、合并在一起

此外,pandas 还提供了比较两个 SeriesDataFrame 对象差异的实用工具

连接对象

concat() 函数能够沿指定轴执行连接操作,同时对其他轴上的索引(如果有的话,Series 只有一个轴)执行可选的集合运算(并集或交集)

下面是一个简单的示例

In [1]: df1 = pd.DataFrame(
   ...:     {
   ...:         "A": ["A0", "A1", "A2", "A3"],
   ...:         "B": ["B0", "B1", "B2", "B3"],
   ...:         "C": ["C0", "C1", "C2", "C3"],
   ...:         "D": ["D0", "D1", "D2", "D3"],
   ...:     },
   ...:     index=[0, 1, 2, 3],
   ...: )
   ...: 

In [2]: df2 = pd.DataFrame(
   ...:     {
   ...:         "A": ["A4", "A5", "A6", "A7"],
   ...:         "B": ["B4", "B5", "B6", "B7"],
   ...:         "C": ["C4", "C5", "C6", "C7"],
   ...:         "D": ["D4", "D5", "D6", "D7"],
   ...:     },
   ...:     index=[4, 5, 6, 7],
   ...: )
   ...: 

In [3]: df3 = pd.DataFrame(
   ...:     {
   ...:         "A": ["A8", "A9", "A10", "A11"],
   ...:         "B": ["B8", "B9", "B10", "B11"],
   ...:         "C": ["C8", "C9", "C10", "C11"],
   ...:         "D": ["D8", "D9", "D10", "D11"],
   ...:     },
   ...:     index=[8, 9, 10, 11],
   ...: )
   ...: 

In [4]: frames = [df1, df2, df3]

In [5]: result = pd.concat(frames)

就像 ndarrays 上的函数 numpy.concatenate 一样,pandas.concat 接收一个相同类型的对象列表或字典,并通过一些配置来设置处理方式并将它们连接起来

pd.concat(
    objs,
    axis=0,
    join="outer",
    ignore_index=False,
    keys=None,
    levels=None,
    names=None,
    verify_integrity=False,
    copy=True,
)

让我们再来看看上面的例子。假设我们想将特定的键与被切割的 DataFrame 的每一个片段关联起来。我们可以使用 keys 参数来实现。

In [6]: result = pd.concat(frames, keys=["x", "y", "z"])

从图中可以看出,在结果对象的索引的最外层添加了相应的索引(变为层次索引)。这意味着我们现在可以按键选择每个块

In [7]: result.loc["y"]
Out[7]: 
    A   B   C   D
4  A4  B4  C4  D4
5  A5  B5  C5  D5
6  A6  B6  C6  D6
7  A7  B7  C7  D7

下面将详细介绍此功能

注意

concat()append() 都会对数据进行完整的复制,不断重复使用这个函数会造成显著的性能下降。

如果你想在多个数据集上执行该操作,可以使用列表推导式。

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

同时,当在指定轴上连接 DataFrames 时,pandas 将尽可能尝试保留这些索引或列名。

1 在其他轴上的设置逻辑

当将多个 DataFrame 连接在一起时,您可以通过以下两种方式来选择如何处理其他轴上的数据(即除被连接的轴外)。

  • 并集 join='outer',这是默认选项,因为它不会丢失信息
  • 交集 join='inner'

下面是这些方法的一个示例。首先,对于默认的 join='outer'

In [8]: df4 = pd.DataFrame(
   ...:     {
   ...:         "B": ["B2", "B3", "B6", "B7"],
   ...:         "D": ["D2", "D3", "D6", "D7"],
   ...:         "F": ["F2", "F3", "F6", "F7"],
   ...:     },
   ...:     index=[2, 3, 6, 7],
   ...: )
   ...: 

In [9]: result = pd.concat([df1, df4], axis=1)

对于 join='inner' 也是一样的

In [10]: result = pd.concat([df1, df4], axis=1, join="inner")

最后,假设我们只是想重用原始 DataFrame 中的确切索引

In [11]: result = pd.concat([df1, df4], axis=1).reindex(df1.index)

类似地,我们可以在连接之前进行索引

In [12]: pd.concat([df1, df4.reindex(df1.index)], axis=1)
Out[12]: 
    A   B   C   D    B    D    F
0  A0  B0  C0  D0  NaN  NaN  NaN
1  A1  B1  C1  D1  NaN  NaN  NaN
2  A2  B2  C2  D2   B2   D2   F2
3  A3  B3  C3  D3   B3   D3   F3

2 使用 append 连接

SeriesDataFrame 上的 append() 实例方法是 concat() 的一个简单的快捷方式。

该方法实际上早于 concat。它沿着 axis=0 即索引方向进行连接

In [13]: result = df1.append(df2)

DataFrame 中,索引必须是不相交的,但列不需要

In [14]: result = df1.append(df4, sort=False)

append 可以接受多个对象来进行连接

In [15]: result = df1.append([df2, df3])

注意

这里的 append() 方法不同于 list,该方法不会修改 df1 的值,同时返回是添加 df2 后的拷贝

3 忽略连接轴上的索引

对于某些 DataFrame 对象,其上的索引并没有任何意义,同时你可能希望在连接的时候,忽略这些对象的索引可能存在重叠的情况。

要做到这一点,可以使用 ignore_index 参数

In [16]: result = pd.concat([df1, df4], ignore_index=True, sort=False)

这也是 DataFrame.append() 的一个有效参数

In [17]: result = df1.append(df4, ignore_index=True, sort=False)

4 不同维度数据的连接

您也可以将 SeriesDataFrame 对象连接起来。Series 将被转换为 DataFrame,列名为 Series 的名称

In [18]: s1 = pd.Series(["X0", "X1", "X2", "X3"], name="X")

In [19]: result = pd.concat([df1, s1], axis=1)

对于未命名的 Series,对应的列名将是连续增长的数字

In [20]: s2 = pd.Series(["_0", "_1", "_2", "_3"])

In [21]: result = pd.concat([df1, s2, s2, s2], axis=1)

传递 ignore_index=True 将删除所有名称引用

In [22]: result = pd.concat([df1, s1], axis=1, ignore_index=True)

5 使用 keys 连接

keys 参数的一个常见的用法是,基于现有 Series 创建新 DataFrame 时覆盖列名

默认的行为是如果 Series 的名称存在的话,让生成的 DataFrame 继承

In [23]: s3 = pd.Series([0, 1, 2, 3], name="foo")

In [24]: s4 = pd.Series([0, 1, 2, 3])

In [25]: s5 = pd.Series([0, 1, 4, 5])

In [26]: pd.concat([s3, s4, s5], axis=1)
Out[26]: 
   foo  0  1
0    0  0  0
1    1  1  1
2    2  2  4
3    3  3  5

通过 keys 参数,我们可以覆盖现有的列名

In [27]: pd.concat([s3, s4, s5], axis=1, keys=["red", "blue", "yellow"])
Out[27]: 
   red  blue  yellow
0    0     0       0
1    1     1       1
2    2     2       4
3    3     3       5

让我们考虑第一个例子的一个变体

In [28]: result = pd.concat(frames, keys=["x", "y", "z"])

你也可以在 concat 中传递一个字典,在这种情况下,字典的键将用于 keys 参数(除非指定了其他键)

In [29]: pieces = {"x": df1, "y": df2, "z": df3}

In [30]: result = pd.concat(pieces)
In [31]: result = pd.concat(pieces, keys=["z", "y"])

结果中创建了 MultiIndex,最外层为 keys 参数指定的值,而原 DataFrame 的索引在内层保留了下来

In [32]: result.index.levels
Out[32]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])

如果你想指定其他级别,可以使用 levels 参数:

In [33]: result = pd.concat(
   ....:     pieces, keys=["x", "y", "z"], levels=[["z", "y", "x", "w"]], names=["group_key"]
   ....: )
   ....: 
In [34]: result.index.levels
Out[34]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])

6 将行追加到DataFrame

虽然这样做效率很低,但是你可以通过传递一个 Seriesdict 并将其追加到 DataFrame 中,同时会返回一个新的 DataFrame

In [35]: s2 = pd.Series(["X0", "X1", "X2", "X3"], index=["A", "B", "C", "D"])

In [36]: result = df1.append(s2, ignore_index=True)

你应该使用 ignore_index 来指示 DataFrame 丢弃它的索引。

如果你希望保留索引,你应该为 DataFrame 构造一个合适的索引,然后追加或连接这些对象

你也可以传递字典或 Series 列表:

In [37]: dicts = [{"A": 1, "B": 2, "C": 3, "X": 4}, {"A": 5, "B": 6, "C": 7, "Y": 8}]

In [38]: result = df1.append(dicts, ignore_index=True, sort=False)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容