谢谢你留下时光匆匆
Pandas 选取行、选取列方式梳理

这篇笔记对Pandas DataFrame取数的各种写法与对应的各种返回结果做一个梳理。

如没有特殊声明,df变量的数据如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> df

   col_1 col_2
a      0   foo
b      1   foo
c      2   bar
d      3   bar
e      4   foo
f      5   foo
g      6   bar
h      7   bar

df.loc[] 基于标签的筛选

single label 单一标签

输入为某个单一行标签label时,返回label所对应单一的行,返回类型为Series(如果行标签与行一一对应)。

1
2
3
4
>>> df.loc['a']

col_1      0
col_2    foo

注1:如果没有对应参数的label,则报KeyError。

1
2
3
>>> df.loc['z']

KeyError: 'z'

注2:如果index中label不唯一,多个行有相同的行标签时,则返回所有有该标签的行,返回类型为DataFrame,如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> df_duplicate_index

   col_1 col_2
a      0   foo
a      1   foo
b      2   bar

>>> df_duplicate_index.loc['a']

   col_1 col_2
a      0   foo
a      1   foo

list of labels 标签

输入为一个行标签list时,按照list中顺序,返回这些标签对应的所有行,返回类型为DataFrame。

1
2
3
4
5
6
>>> df.loc[['a', 'b', 'c']]

   col_1 col_2
a      0   foo
b      1   foo
c      2   bar

注1:如果list里没有对应参数的标签,则报KeyError。

1
2
3
>>> df.loc[['a', 'b', 'z']]

KeyError: "['z'] not in index"

注2:与单一标签一致的,如果index中label不唯一时,则所有带入参标签的行都会被返回。

注3:loc入参list中的label是可以重复的,通过这样来得到重复选取某行的返回结果。

1
2
3
4
5
6
>>> df.loc[['a', 'a', 'a']]

   col_1 col_2
a      0   foo
a      0   foo
a      0   foo

slice object 切片对象

slice 对象是指Python中带冒号的表达式,如1:5。返回类型为DataFrame。

返回结果的需要根据index是否有序,分情况讨论:

  • 当index是有序的,slice中的参数无需为index中某一个label,返回label在slice区间内的行,返回类型为DataFrame。
  • 当index是无序的,slice中的参数必须为index中某个label,否则会报KeyError,返回label为slice参数中的行,以及这两行中间的行,返回类型为DataFrame。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> df.loc["a":"e"]

   col_1 col_2
a      0   foo
b      1   foo
c      2   bar
d      3   bar
e      4   foo

>>> df.loc["a":"z"]  # 注意z不在index里

   col_1 col_2
a      0   foo
b      1   foo
c      2   bar
d      3   bar
e      4   foo
f      5   foo
g      6   bar
h      7   bar

>>> df_unsorted_index = df.loc[['f', 'a', 'c', 'g', 'h', 'd', 'e', 'b']]
>>> df_unsorted_index.loc["a":"e"]

   col_1 col_2
a      0   foo
c      2   bar
g      6   bar
h      7   bar
d      3   bar
e      4   foo

注1:loc中slice两端端点的处理与Python中slice有些不同,python中slice是“左开右闭”的,即冒号右边的值不会囊括,如 [i for i in range(10)][0:3] 返回 [0, 1, 2],而在loc中slice是both inclusive的,意味着冒号前后的值都会被囊括。

注2:可以用df.index.is_monotonic_increasingdf.index.is_monotonic_decreasing(是一个属性非方法,不带括号)来判断dataframe是否已经按照index排好序。

注3:如果slice中冒号前一个label对应行在冒号后一个label对应行的后面,则会返回一个空的DataFrame。

1
2
3
4
5
>>> df.loc["e":"a"]

Empty DataFrame
Columns: [col_1, col_2]
Index: []

注4:当index中label不唯一时,使用slice可能会报错。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> df_duplicate_index_2

   col_1 col_2
a      0   foo
b      1   foo
c      2   bar
a      3   bar

>>> df_duplicate_index_2["a":"b"]

KeyError: "Cannot get left slice bound for non-unique label: 'a'"

boolean array 布尔列表

输入为一个数量与表行数相同(必须相同)的布尔列表时,返回对应位置为True的行,返回类型为DataFrame。

这种调用方式常用于按照某个条件过滤dataFrame。

1
2
3
4
5
6
7
>>> df.loc[df["col_2"] == "foo"]

   col_1 col_2
a      0   foo
b      1   foo
e      4   foo
f      5   foo

注1:条件的逻辑运算或与非需要用位运算符&, | , ~,在进行两个条件运算时,因为Python运算优先级,需要将条件语句用圆括号括起来。

1
2
3
4
5
6
7
8
9
>>> df.loc[df["col_1"] > 1 & df["col_2"] == "foo"] #直接做位运算会报错

TypeError: Cannot perform 'rand_' with a dtyped [object] array and scalar of type [bool]

>>> df.loc[(df["col_1"] > 1) & (df["col_2"] == "foo")]      #将条件语句括起来提高运算优先级

   col_1 col_2
e      4   foo
f      5   foo

callable 函数

输入为一个函数时,会将dataframe传入函数的返回结果作为loc的入参,返回对应的结果。函数的返回类型可以为上面提到4种(单一label、label列表、slice对象、布尔列表)中的任意一种。

函数入参主要是为了支持链式写法,当我们需要多重筛选dataframe时,使用链式写法进行代码开发不需要存储中间的dataframe结果,使得我们的代码更加简洁。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 常规写法
df_mid_1 = df.loc[df["col_1"] > 1]
df_mid_2 = df_mid_1.loc[df_mid_1.groupby("col_2")["col_2"].transform("count") > 2]
df_mid_2.loc[df_mid_2.index.isin(["a", "d", "g"])]

# 链式写法
(
    df.loc[df["col_1"] > 1]
        .loc[lambda x: x.groupby("col_2")["col_2"].transform("count") > 2]
        .loc[lambda x: x.index.isin(["a", "d", "g"])]
)

df.iloc[] 基于行号的筛选

iloc与loc最大的不同在于,loc接受的参数是index中的label,而iloc接受的参数是整数,代表dataframe的行号(从0开始)。

single integer 单一整数

输入为单一行数时,返回代数所在单一的行,返回类型为Series。

1
2
3
4
>>> df.iloc[0]

col_1      0
col_2    foo

注1:入参的整数可以为负数,会返回倒数第x行。

1
2
3
>>> df.iloc[-1] 

col_1      7 col_2    bar

注2:如果整数超过了dataframe的总行数,则报IndexError。

1
2
3
>>> df.iloc[10]

IndexError: single positional indexer is out-of-bounds

list of labels 整数列表

输入为一个整数list时,按照list中顺序,返回这些行数对应的所有行,返回类型为DataFrame。

1
2
3
4
5
6
>>> df.iloc[[0, 1, 2]]

   col_1 col_2
a      0   foo
b      1   foo
c      2   bar

注1:如果list里面的整数超过了dataframe的总行数,则报IndexError。

1
2
3
>>> (df.iloc[[0, 10]]

IndexError: positional indexers are out-of-bounds

注2:iloc入参list中的整数是可以重复的,通过这样来得到重复选取某行的返回结果。

1
2
3
4
5
6
>>> df.iloc[[0, 0, 0]]

   col_1 col_2
a      0   foo
a      0   foo
a      0   foo

slice object 切片对象

输入为一个整数slice时,返回slice对应的行数(左闭右开,只包含冒号前面的行),返回类型为DataFrame。

1
2
3
4
5
6
7
>>> df.iloc[1:5]

   col_1 col_2
b      1   foo
c      2   bar
d      3   bar
e      4   foo

注1:需要注意的是,与loc slice入参冒号前后的label都会被返回不同,iloc中冒号后面的整数对应的行不会被返回。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> df_int_index = pd.DataFrame(["a", "b", "c"], columns=["col"], index=[0, 1, 2])
>>> df_int_index.iloc[0:2]

  col
0   a
1   b

>>> df_int_index.loc[0:2]

  col
0   a
1   b
2   c

boolean array 布尔列表

与loc中 boolean array 布尔列表用法完全相同。

callalbe 函数

与loc中callable函数用法基本相同,唯一不同是函数的返回类型需要为单一整数、整数列表、整数slice、布尔列表四种中的一种。

df[] 筛选

当直接在df[]输入筛选语句时,需要注意输入的参数类型对应的是筛选行还是筛选列,具体情况如下:

single label 单一标签 - 返回列

输入为某个单一标签label,返回label所对应的,返回类型为Series(如果列标签与列一一对应)。df["a"]等价于df.loc[:, "a"]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>> df['col_1']

a    0
b    1
c    2
d    3
e    4
f    5
g    6
h    7

>>> df['col_1'].equals(df.loc[:, 'col_1'])

True

list of labels 标签 - 返回列

输入为一个标签list时,按照list中顺序,返回这些标签对应的所有,返回类型为DataFrame,df[['col_2', 'col_1']] 等价于 df.loc[:, ['col_2', 'col_1']]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
>>> df[['col_2', 'col_1']]

  col_2  col_1
a   foo      0
b   foo      1
c   bar      2
d   bar      3
e   foo      4
f   foo      5
g   bar      6
h   bar      7

>>> df[['col_2', 'col_1']].equals(df.loc[:, ['col_2', 'col_1']])

True

label slice 标签的切片对象 - 返回行

输入为一个 label slice 标签时(e.g. df["d":"g"]),返回slice对应的,返回类型为DataFrame。df["d":"g"] 等价于 df.loc["d":"g"],具体逻辑可以参考 loc 部分的slice-object-切片对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> df["d":"g"]

   col_1 col_2
d      3   bar
e      4   foo
f      5   foo
g      6   bar

>>> df["d":"g"].equals(df.loc["d":"g"])

True

integer slice 整数的切片对象 - 返回行

输入为一个整数slice(e.g. df[1:5]) 时,返回slice对应行数所在的,返回类型为DataFrame。df[1:5] 等价于 df.iloc[1:5],具体逻辑可以参考 .iloc 部分的slice-object-切片对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> df[2:5]

   col_1 col_2
c      2   bar
d      3   bar
e      4   foo

>>> df[2:5].equals(df.iloc[2:5])

True

boolean array 布尔列表 - 返回行

输入为时,返回对应位置为True的(布尔列表长度需与dataFrame行数一致),返回类型为DataFrame。df[df['col_1'] > 3] 等价于 df.loc[df['col_1'] > 3]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> df[df['col_1'] > 3]

   col_1 col_2
e      4   foo
f      5   foo
g      6   bar
h      7   bar

>>> df[df['col_1'] > 3].equals(df.loc[df['col_1'] > 3])

True

callable 函数

我们可以传一个函数,与loc iloc情况类似,会将dataframe传入函数的返回结果作为loc的入参,返回对应的结果。

1
2
3
4
5
6
7
>>> df[lambda x: x['col_1'] > 3]

   col_1 col_2
e      4   foo
f      5   foo
g      6   bar
h      7   bar

其它

整数与label的混用

在筛选行于列一同筛选时,有时候为了方便,在筛选行时用输入整数行数进行选取,在筛选列时输入label进行选取。loc与iloc是不允许同时输入两种类型的,但我们可以借助一些辅助函数完成两种类型的混用。

.iloc[]中,使用dataFrame.columns.get_loc()(单一label)或 dataFrame.columns.get_indexer([])(label列表),将label转换为对应的列数。

1
2
3
4
5
>>> df.iloc[[0, 1], [df.columns.get_loc('col_1')]]

   col_1
a      0
b      1

.loc[]中,使用dataFrame.index[[]],将行数转换为对应的label。

1
2
3
4
5
>>> df.loc[df.index[[0, 1]], ['col_1']

   col_1
a      0
b      1

注意:如果一个lable对应多个行,dataFrame.index[[]]可能会返回某行数label对应的所有的行。