一篇比较好的pandas指南,适合已经熟悉pandas,并想掌握一些进阶用法的读者,不适合对pandas完全不了解的新人。文章大部分是Stack Overflow常见问题集合。
pandas 官网 原文连接: https://pandas.pydata.org/pandas-docs/stable/user_guide/cookbook.html
我会在原文基础上进行增删改,添加一些注释。
常见用法
if-then
- 对一列数据执行 if-then / if-then-else 操作,把计算结果赋值给一列或多列。
AAA BBB CCC
0 4 10 100
1 5 20 50
2 6 30 -30
3 7 40 -50
对其中一列筛选,并操作另一列:
In [3]: df.loc[df.AAA >= 5, 'BBB'] = -1
In [4]: df
Out[4]:
AAA BBB CCC
0 4 10 100
1 5 -1 50
2 6 -1 -30
3 7 -1 -50
操作两列:
In [5]: df.loc[df.AAA >= 5, ['BBB', 'CCC']] = 555
In [6]: df
Out[6]:
AAA BBB CCC
0 4 10 100
1 5 555 555
2 6 555 555
3 7 555 555
可以通过相反逻辑的代码来实现else,如下
df.loc[df.AAA < 5, ["BBB", "CCC"]] = 2000
或者使用mask和pandas的where来实现,下述表示在mask中,True
的地方转化为-1000
In [9]: df_mask = pd.DataFrame({'AAA': [True] * 4,
...: 'BBB': [False] * 4,
...: 'CCC': [True, False] * 2})
...:
In [10]: df.where(df_mask, -1000)
Out[10]:
AAA BBB CCC
0 4 -1000 2000
1 5 -1000 -1000
2 6 -1000 555
3 7 -1000 -1000
也可以使用numpyd where()函数实现
In [13]: df['logic'] = np.where(df['AAA'] > 5, 'high', 'low')
In [14]: df
Out[14]:
AAA BBB CCC logic
0 4 10 100 low
1 5 20 50 low
2 6 30 -30 high
3 7 40 -50 high
Splitting
通过布尔值切分
In [17]: df[df.AAA <= 5]
Out[17]:
AAA BBB CCC
0 4 10 100
1 5 20 50
In [18]: df[df.AAA > 5]
Out[18]:
AAA BBB CCC
2 6 30 -30
3 7 40 -50
条件设置
还是原来的数据df:
AAA BBB CCC
0 4 10 100
1 5 20 50
2 6 30 -30
3 7 40 -50
- 通过“和”符号、或符号筛选,返回Series:
In [21]: df.loc[(df['BBB'] < 25) & (df['CCC'] >= -40), 'AAA']
#切选bbb列小于25且ccc列大于-40的aaa列值
Out[21]:
0 4
1 5
Name: AAA, dtype: int64
In [22]: df.loc[(df['BBB'] > 25) | (df['CCC'] >= -40), 'AAA']
#切选bbb列小于25或ccc列大于-40的aaa列值
Out[22]:
0 4
1 5
2 6
3 7
Name: AAA, dtype: int64
- 筛选后赋值原Dataframe
In [23]: df.loc[(df['BBB'] > 25) | (df['CCC'] >= 75), 'AAA'] = 0.1
#切选bbb列小于25或ccc列大于-40的aaa列值,并赋值为0.1
In [24]: df
Out[24]:
AAA BBB CCC
0 0.1 10 100
1 5.0 20 50
2 0.1 30 -30
3 0.1 40 -50
用 argsort 选择最接近指定值的行
In [27]: aValue = 43.0
In [28]: df.loc[(df.CCC - aValue).abs().argsort()]
Out[28]:
AAA BBB CCC
1 5 20 50
0 4 10 100
2 6 30 -30
3 7 40 -50
用二进制运算符动态减少条件列表
#原始数据
AAA BBB CCC
0 4 10 100
1 5 20 50
2 6 30 -30
3 7 40 -50
In [31]: Crit1 = df.AAA <= 5.5
In [32]: Crit2 = df.BBB == 10.0
In [33]: Crit3 = df.CCC > -40.0
#方法1:通过直接硬编码
In [34]: AllCrit = Crit1 & Crit2 & Crit3
#方法2:通过动态生成条件列表,适用于非常多的条件
In [35]: import functools
In [36]: CritList = [Crit1, Crit2, Crit3]
In [37]: AllCrit = functools.reduce(lambda x, y: x & y, CritList)
In [38]: df[AllCrit]
Out[38]:
AAA BBB CCC
0 4 10 100
有关functools.reduce的官方文档
Selection
- 行标签与值作为条件
用逆运算符 (~)提取掩码,获取反向的内容
In [41]: df[(df.AAA <= 6) & (df.index.isin([0, 2, 4]))]
Out[41]:
AAA BBB CCC
0 4 10 100
2 6 30 -30
In [52]: df[~((df.AAA <= 6) & (df.index.isin([0, 2, 4])))]
Out[52]:
AAA BBB CCC
1 5 20 50
3 7 40 -50
- 标签切片与位置切片
数据:
AAA BBB CCC
foo 4 10 100
bar 5 20 50
boo 6 30 -30
kar 7 40 -50
三种切片方法:
#法1:使用loc根据标签切片,包含结尾
n [43]: df.loc['bar':'kar'] #或通用写成df["bar":"kar"]
Out[43]:
AAA BBB CCC
bar 5 20 50
boo 6 30 -30
kar 7 40 -50
# 使用iloc根据位置切片,不包含结尾
In [44]: df.iloc[0:3] #或通用写成df[0:3]
Out[44]:
AAA BBB CCC
foo 4 10 100
bar 5 20 50
boo 6 30 -30
注意,如果索引由整数组成但不从 0 开始,或不是逐步递增的,会引发歧义,可以通过iloc和loc进行区分。
df2
AAA BBB CCC
1 4 10 100
2 5 20 50
3 6 30 -30
4 7 40 -50
df2.iloc[1:3] # Position-oriented
Out[48]:
AAA BBB CCC
2 5 20 50
3 6 30 -30
In [49]: df2.loc[1:3] # Label-oriented
Out[49]:
AAA BBB CCC
1 4 10 100
2 5 20 50
3 6 30 -30
- 用 applymap 动态生成新列
AAA BBB CCC
0 1 1 2
1 2 1 1
2 1 2 3
3 3 2 1
In [55]: source_cols = df.columns # Or some subset would work too
In [56]: new_cols = [str(x) + "_cat" for x in source_cols]#生成新列名
In [57]: categories = {1: 'Alpha', 2: 'Beta', 3: 'Charlie'}
In [58]: df[new_cols] = df[source_cols].applymap(categories.get)
In [59]: df
Out[59]:
AAA BBB CCC AAA_cat BBB_cat CCC_cat
0 1 1 2 Alpha Alpha Beta
1 2 1 1 Beta Alpha Alpha
2 1 2 3 Alpha Beta Charlie
3 3 2 1 Charlie Beta Alpha
apply、map和applymap 参见这里和这里
- 使用groupby时保留其他列
In [61]: df
Out[61]:
AAA BBB
0 1 2
1 1 1
2 1 3
3 2 4
4 2 5
5 2 1
6 3 2
7 3 3
#方法1:用 idxmin() 提取对应索引
In [62]: df.loc[df.groupby("AAA")["BBB"].idxmin()]
Out[62]:
AAA BBB
1 1 1
5 2 1
6 3 2
#方法2:先排序,再提取每组的第一个值
In [63]: df.sort_values(by="BBB").groupby("AAA", as_index=False).first()
Out[63]:
AAA BBB
0 1 1
1 2 1
2 3 2
这里需注意,虽然提取的结果是一样的,但两者索引不同