Pandas 的 concat 函数中的“级别”、“键”和名称参数是啥?
Posted
技术标签:
【中文标题】Pandas 的 concat 函数中的“级别”、“键”和名称参数是啥?【英文标题】:What are the 'levels', 'keys', and names arguments for in Pandas' concat function?Pandas 的 concat 函数中的“级别”、“键”和名称参数是什么? 【发布时间】:2018-09-12 05:12:32 【问题描述】:问题
如何使用pd.concat
?
levels
参数的用途是什么?
keys
参数的用途是什么?
是否有大量示例可以帮助解释如何使用所有参数?
Pandas 的 concat
函数是合并实用程序的 Swiss Army knife。它有用的各种情况很多。现有的文档遗漏了一些关于一些可选参数的细节。其中包括levels
和keys
参数。我着手弄清楚这些论点的作用。
我将提出一个问题,作为通往pd.concat
多个方面的门户。
考虑数据框d1
、d2
和d3
:
import pandas as pd
d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), [2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), [1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), [1, 3])
如果我将这些连接在一起
pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'])
我的columns
对象得到了pandas.MultiIndex
的预期结果:
A B C D
d1 2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
d2 1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
d3 1 0.7 0.8 NaN 0.9
3 0.7 0.8 NaN 0.9
但是,我想使用levels
argument documentation:
levels:序列列表,默认无。 用于构造 MultiIndex 的特定级别(唯一值)。否则,它们将从键中推断出来。
所以我通过了
pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2']])
并获得KeyError
ValueError: Key d3 not in level Index(['d1', 'd2'], dtype='object')
这是有道理的。我通过的级别不足以描述按键指示的必要级别。如果我没有像上面所做的那样通过任何东西,那么会推断出级别(如文档中所述)。但是我还能如何使用这个论点来获得更好的效果呢?
如果我尝试这样做:
pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3']])
我得到了和上面一样的结果。但是当我在关卡中再添加一个值时,
df = pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3', 'd4']])
我最终得到了相同的数据框,但生成的 MultiIndex
有一个未使用的级别。
df.index.levels[0]
Index(['d1', 'd2', 'd3', 'd4'], dtype='object')
那么level
参数的意义何在?我应该以不同的方式使用keys
吗?
我正在使用 Python 3.6 和 Pandas 0.22。
【问题讨论】:
【参考方案1】:在自己回答这个问题的过程中,我学到了很多东西,我想整理一个例子目录和一些解释。
levels
论点的具体答案将在最后。
pandas.concat
: 缺失的手册
Link To Current Documentation
导入和定义对象
import pandas as pd
d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), index=[2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), index=[1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), index=[1, 3])
s1 = pd.Series([1, 2], index=[2, 3])
s2 = pd.Series([3, 4], index=[1, 2])
s3 = pd.Series([5, 6], index=[1, 3])
参数
objs
我们遇到的第一个参数是objs
:
我们通常会看到它与objs:Series、DataFrame 或 Panel 对象的序列或映射 如果传递了 dict,则排序后的键将用作 keys 参数,除非它被传递,在这种情况下将选择值(见下文)。任何 None 对象都将被静默删除,除非它们都是 None 在这种情况下会引发 ValueError
Series
或DataFrame
对象列表一起使用。
我将证明dict
也非常有用。
也可以使用生成器,并且在使用map
时很有用,如map(f, list_of_df)
现在,我们将坚持使用上面定义的一些DataFrame
和Series
对象的列表。
稍后我将展示如何利用字典来提供非常有用的MultiIndex
结果。
pd.concat([d1, d2])
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
axis
我们遇到的第二个参数是axis
,其默认值为0
:
轴:0/'index', 1/'columns',默认 0 要连接的轴。
两个DataFrame
s 和axis=0
(堆叠)
对于0
或index
的值,我们的意思是:“沿列对齐并添加到索引中”。
如上所示,我们使用axis=0
,因为0
是默认值,我们看到d2
的索引扩展了d1
的索引,尽管2
的值有重叠:
pd.concat([d1, d2], axis=0)
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
两个DataFrame
s 和axis=1
(并排)
对于值1
或columns
,我们的意思是:“沿索引对齐并添加到列中”,
pd.concat([d1, d2], axis=1)
A B C B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
我们可以看到生成的索引是索引的并集,生成的列是d1
的列与d2
的列的扩展。
两个(或三个)Series
和 axis=0
(堆叠)
当结合pandas.Series
和axis=0
时,我们得到一个pandas.Series
。生成的 Series
的名称将是 None
,除非所有 Series
组合在一起具有相同的名称。当我们打印出结果Series
时,请注意'Name: A'
。当它不存在时,我们可以假设 Series
名称是 None
。
| | | pd.concat(
| pd.concat( | pd.concat( | [s1.rename('A'),
pd.concat( | [s1.rename('A'), | [s1.rename('A'), | s2.rename('B'),
[s1, s2]) | s2]) | s2.rename('A')]) | s3.rename('A')])
-------------- | --------------------- | ---------------------- | ----------------------
2 1 | 2 1 | 2 1 | 2 1
3 2 | 3 2 | 3 2 | 3 2
1 3 | 1 3 | 1 3 | 1 3
2 4 | 2 4 | 2 4 | 2 4
dtype: int64 | dtype: int64 | Name: A, dtype: int64 | 1 5
| | | 3 6
| | | dtype: int64
两个(或三个)Series
和 axis=1
(并排)
当将pandas.Series
与axis=1
组合在一起时,我们引用的是name
属性,以便在生成的pandas.DataFrame
中推断列名。
| | pd.concat(
| pd.concat( | [s1.rename('X'),
pd.concat( | [s1.rename('X'), | s2.rename('Y'),
[s1, s2], axis=1) | s2], axis=1) | s3.rename('Z')], axis=1)
---------------------- | --------------------- | ------------------------------
0 1 | X 0 | X Y Z
1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 5.0
2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 NaN
3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN 6.0
Series
和 DataFrame
与 axis=0
混合(堆叠)
在将Series
和DataFrame
与axis=0
串联时,我们将所有Series
转换为单列DataFrame
s。
请特别注意,这是axis=0
的串联;这意味着在对齐列的同时扩展索引(行)。在下面的示例中,我们看到索引变为[2, 3, 2, 3]
,这是一个不加选择的索引附加。除非我强制使用 to_frame
的参数命名 Series
列,否则列不会重叠:
pd.concat( |
[s1.to_frame(), d1]) | pd.concat([s1, d1])
------------------------- | ---------------------
0 A B C | 0 A B C
2 1.0 NaN NaN NaN | 2 1.0 NaN NaN NaN
3 2.0 NaN NaN NaN | 3 2.0 NaN NaN NaN
2 NaN 0.1 0.2 0.3 | 2 NaN 0.1 0.2 0.3
3 NaN 0.1 0.2 0.3 | 3 NaN 0.1 0.2 0.3
您可以看到pd.concat([s1, d1])
的结果与我自己执行to_frame
的结果相同。
但是,我可以使用to_frame
的参数来控制结果列的名称。使用rename
方法重命名Series
并不控制生成的DataFrame
中的列名。
# Effectively renames | |
# `s1` but does not align | # Does not rename. So | # Renames to something
# with columns in `d1` | # Pandas defaults to `0` | # that does align with `d1`
pd.concat( | pd.concat( | pd.concat(
[s1.to_frame('X'), d1]) | [s1.rename('X'), d1]) | [s1.to_frame('B'), d1])
---------------------------- | -------------------------- | ----------------------------
A B C X | 0 A B C | A B C
2 NaN NaN NaN 1.0 | 2 1.0 NaN NaN NaN | 2 NaN 1.0 NaN
3 NaN NaN NaN 2.0 | 3 2.0 NaN NaN NaN | 3 NaN 2.0 NaN
2 0.1 0.2 0.3 NaN | 2 NaN 0.1 0.2 0.3 | 2 0.1 0.2 0.3
3 0.1 0.2 0.3 NaN | 3 NaN 0.1 0.2 0.3 | 3 0.1 0.2 0.3
将Series
和DataFrame
与axis=1
混合在一起(并排)
这是相当直观的。当name
属性不可用时,Series
列名默认为此类Series
对象的枚举。
| pd.concat(
pd.concat( | [s1.rename('X'),
[s1, d1], | s2, s3, d1],
axis=1) | axis=1)
------------------- | -------------------------------
0 A B C | X 0 1 A B C
2 1 0.1 0.2 0.3 | 1 NaN 3.0 5.0 NaN NaN NaN
3 2 0.1 0.2 0.3 | 2 1.0 4.0 NaN 0.1 0.2 0.3
| 3 2.0 NaN 6.0 0.1 0.2 0.3
join
第三个参数是 join
,它描述了生成的合并应该是外部合并(默认)还是内部合并。
加入:‘inner’, ‘outer’,默认‘outer’ 如何处理其他轴上的索引。
事实证明,没有left
或right
选项,因为pd.concat
可以处理两个以上要合并的对象。
对于d1
和d2
,选项如下所示:
outer
pd.concat([d1, d2], axis=1, join='outer')
A B C B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
inner
pd.concat([d1, d2], axis=1, join='inner')
A B C B C D
2 0.1 0.2 0.3 0.4 0.5 0.6
join_axes
第四个参数允许我们进行left
合并等等。
join_axes:索引对象列表 用于其他 n - 1 个轴的特定索引,而不是执行内部/外部集逻辑。
左合并
pd.concat([d1, d2, d3], axis=1, join_axes=[d1.index])
A B C B C D A B D
2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
右合并
pd.concat([d1, d2, d3], axis=1, join_axes=[d3.index])
A B C B C D A B D
1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
ignore_index
ignore_index:布尔值,默认为 False 如果为 True,则不要沿连接轴使用索引值。生成的轴将标记为 0, ..., n - 1。如果您在连接轴没有有意义的索引信息的情况下连接对象,这将很有用。请注意,其他轴上的索引值在连接中仍然受到尊重。
就像当我将d1
堆叠在d2
之上时,如果我不关心索引值,我可以重置它们或忽略它们。
| pd.concat( | pd.concat(
| [d1, d2], | [d1, d2]
pd.concat([d1, d2]) | ignore_index=True) | ).reset_index(drop=True)
--------------------- | ----------------------- | -------------------------
A B C D | A B C D | A B C D
2 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6
而当使用axis=1
:
| pd.concat(
| [d1, d2], axis=1,
pd.concat([d1, d2], axis=1) | ignore_index=True)
------------------------------- | -------------------------------
A B C B C D | 0 1 2 3 4 5
1 NaN NaN NaN 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN | 3 0.1 0.2 0.3 NaN NaN NaN
keys
我们可以传递一个标量值或元组列表,以便将元组或标量值分配给相应的 MultiIndex。传递的列表的长度必须与要连接的项目数的长度相同。
keys:序列,默认无 如果通过了多个级别,则应包含元组。以传递的键为最外层构造层次索引
axis=0
沿axis=0
连接Series
对象时(扩展索引)。
那些键,成为索引属性中MultiIndex
对象的新初始级别。
# length 3 length 3 # length 2 length 2
# /--------\ /-----------\ # /----\ /------\
pd.concat([s1, s2, s3], keys=['A', 'B', 'C']) pd.concat([s1, s2], keys=['A', 'B'])
---------------------------------------------- -------------------------------------
A 2 1 A 2 1
3 2 3 2
B 1 3 B 1 3
2 4 2 4
C 1 5 dtype: int64
3 6
dtype: int64
但是,我们可以在keys
参数中使用更多的标量值来创建更深的MultiIndex
。在这里,我们将长度为 2 的 tuples
传递给 MultiIndex
的两个新级别:
pd.concat(
[s1, s2, s3],
keys=[('A', 'X'), ('A', 'Y'), ('B', 'X')])
-----------------------------------------------
A X 2 1
3 2
Y 1 3
2 4
B X 1 5
3 6
dtype: int64
axis=1
沿列延伸时有点不同。当我们使用axis=0
(见上文)时,除了现有索引之外,我们的keys
还充当MultiIndex
级别。对于axis=1
,我们指的是Series
对象没有的轴,即columns
属性。
Series
和axis=1
的变体
请注意,只要没有传递 keys
,命名 s1
和 s2
就很重要,但如果传递了 keys
,它将被覆盖。
| | | pd.concat(
| pd.concat( | pd.concat( | [s1.rename('U'),
pd.concat( | [s1, s2], | [s1.rename('U'), | s2.rename('V')],
[s1, s2], | axis=1, | s2.rename('V')], | axis=1,
axis=1) | keys=['X', 'Y']) | axis=1) | keys=['X', 'Y'])
-------------- | --------------------- | ---------------------- | ----------------------
0 1 | X Y | U V | X Y
1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0
2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0
3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN
MultiIndex
与 Series
和 axis=1
pd.concat(
[s1, s2],
axis=1,
keys=[('W', 'X'), ('W', 'Y')])
-----------------------------------
W
X Y
1 NaN 3.0
2 1.0 4.0
3 2.0 NaN
两个DataFrame
和axis=1
与axis=0
示例一样,keys
将级别添加到MultiIndex
,但这次添加到存储在columns
属性中的对象。
pd.concat( | pd.concat(
[d1, d2], | [d1, d2],
axis=1, | axis=1,
keys=['X', 'Y']) | keys=[('First', 'X'), ('Second', 'X')])
------------------------------- | --------------------------------------------
X Y | First Second
A B C B C D | X X
1 NaN NaN NaN 0.4 0.5 0.6 | A B C B C D
2 0.1 0.2 0.3 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6
| 3 0.1 0.2 0.3 NaN NaN NaN
Series
和 DataFrame
和 axis=1
这很棘手。在这种情况下,标量键值不能作为Series
对象在成为列时的唯一索引级别,同时也作为DataFrame
的MultiIndex
的第一级别。所以 Pandas 将再次使用Series
对象的name
属性作为列名的来源。
pd.concat( | pd.concat(
[s1, d1], | [s1.rename('Z'), d1],
axis=1, | axis=1,
keys=['X', 'Y']) | keys=['X', 'Y'])
--------------------- | --------------------------
X Y | X Y
0 A B C | Z A B C
2 1 0.1 0.2 0.3 | 2 1 0.1 0.2 0.3
3 2 0.1 0.2 0.3 | 3 2 0.1 0.2 0.3
keys
和 MultiIndex
推理的局限性。
Pandas 似乎只能从 Series
名称推断列名称,但在对具有不同列级别数的数据帧进行类似连接时,它不会填补空白。
d1_ = pd.concat(
[d1], axis=1,
keys=['One'])
d1_
One
A B C
2 0.1 0.2 0.3
3 0.1 0.2 0.3
然后将其与列对象中只有一个级别的另一个数据框连接起来,Pandas 将拒绝尝试制作MultiIndex
对象的元组,并将所有数据框组合成一个级别的对象、标量和元组。
pd.concat([d1_, d2], axis=1)
(One, A) (One, B) (One, C) B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
传递 dict
而不是 list
传递字典时,pandas.concat
将使用字典中的键作为keys
参数。
# axis=0 | # axis=1
pd.concat( | pd.concat(
0: d1, 1: d2) | 0: d1, 1: d2, axis=1)
----------------------- | -------------------------------
A B C D | 0 1
0 2 0.1 0.2 0.3 NaN | A B C B C D
3 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6
1 1 NaN 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN
levels
这与keys
参数一起使用。当levels
保留其默认值None
时,Pandas 将采用生成的MultiIndex
的每个级别的唯一值并将其用作生成的index.levels
属性中使用的对象。
levels:序列列表,默认无 用于构造 MultiIndex 的特定级别(唯一值)。否则它们将从键中推断出来。
如果 Pandas 已经推断出这些级别应该是什么,那么我们自己指定它有什么好处?我将展示一个示例,并由您自己思考为什么这可能有用的其他原因。
示例
根据文档,levels
参数是一个序列列表。这意味着我们可以使用另一个pandas.Index
作为这些序列之一。
考虑数据框df
,它是d1
、d2
和d3
的串联:
df = pd.concat(
[d1, d2, d3], axis=1,
keys=['First', 'Second', 'Fourth'])
df
First Second Fourth
A B C B C D A B D
1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9
2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
columns 对象的级别是:
print(df, *df.columns.levels, sep='\n')
Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')
如果我们在groupby
中使用sum
,我们会得到:
df.groupby(axis=1, level=0).sum()
First Fourth Second
1 0.0 2.4 1.5
2 0.6 0.0 1.5
3 0.6 2.4 0.0
但是,如果不是['First', 'Second', 'Fourth']
而是缺少另一个名为Third
和Fifth
的类别怎么办?我希望它们包含在groupby
聚合的结果中?如果我们有pandas.CategoricalIndex
,我们可以做到这一点。我们可以使用 levels
参数提前指定。
因此,让我们将df
定义为:
cats = ['First', 'Second', 'Third', 'Fourth', 'Fifth']
lvl = pd.CategoricalIndex(cats, categories=cats, ordered=True)
df = pd.concat(
[d1, d2, d3], axis=1,
keys=['First', 'Second', 'Fourth'],
levels=[lvl]
)
df
First Fourth Second
1 0.0 2.4 1.5
2 0.6 0.0 1.5
3 0.6 2.4 0.0
但是columns对象的第一层是:
df.columns.levels[0]
CategoricalIndex(
['First', 'Second', 'Third', 'Fourth', 'Fifth'],
categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
ordered=True, dtype='category')
我们的groupby
总和看起来像:
df.groupby(axis=1, level=0).sum()
First Second Third Fourth Fifth
1 0.0 1.5 0.0 2.4 0.0
2 0.6 1.5 0.0 0.0 0.0
3 0.6 0.0 0.0 2.4 0.0
names
这用于命名生成的MultiIndex
的级别。 names
列表的长度应与生成的 MultiIndex
中的级别数匹配。
名称:列表,默认无 生成的分层索引中的级别名称
# axis=0 | # axis=1
pd.concat( | pd.concat(
[d1, d2], | [d1, d2],
keys=[0, 1], | axis=1, keys=[0, 1],
names=['lvl0', 'lvl1']) | names=['lvl0', 'lvl1'])
----------------------------- | ----------------------------------
A B C D | lvl0 0 1
lvl0 lvl1 | lvl1 A B C B C D
0 2 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6
1 1 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN
2 NaN 0.4 0.5 0.6 |
verify_integrity
自解释文档
verify_integrity:布尔值,默认为 False 检查新的连接轴是否包含重复项。相对于实际的数据连接,这可能非常昂贵。
因为连接d1
和d2
得到的索引不是唯一的,所以完整性检查会失败。
pd.concat([d1, d2])
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
和
pd.concat([d1, d2], verify_integrity=True)
> ValueError:索引具有重叠值:[2]
【讨论】:
对社区来说,简单地做一个拉取请求来添加一些缺失的例子(只是几个)到主文档中真的会更有用; SO 只能搜索,不能浏览;在这里进一步放置文档链接会很有用 - 其中绝大多数已经很好并且完全记录在案 @Jeff 我的成长有些缓慢。使用 git 就是其中之一。我保证这就是我想要开始做的事情。 使用pd.concat(..., levels=[lvl]).groupby(axis=1, level=0).sum()
产生的结果与pd.concat(..., levels=[cats]).groupby(axis=1, level=0).sum()
不同。你知道为什么吗?文档只说levels
应该是一个序列列表。
很好的答案,但我认为Passing a dict instead of a list
部分需要一个使用字典而不是列表的示例。
@unutbu 我已经修复了dict
示例,谢谢。原因是lvl
是一个分类索引,cats
只是一个列表。当按分类类型分组时,缺失的类别会在适当的地方用零和空值填充。 See this以上是关于Pandas 的 concat 函数中的“级别”、“键”和名称参数是啥?的主要内容,如果未能解决你的问题,请参考以下文章
pandas 合并数据函数merge join concat combine_first 区分
Pandas 中的 Concat 2 列 - AttributeError:“DataFrame”对象没有属性“concat”