首页 > temp > 简明python教程 >
-
Pandas进阶笔记 (一) Groupby 重难点总结(3)
<class 'pandas.core.series.Series'>
2 1
3 3
Name: a, dtype: int64
<class 'pandas.core.series.Series'>
2 3
3 11
Name: b, dtype: int64
<class 'pandas.core.frame.DataFrame'>
a b
2 1 3
3 3 11
<class 'pandas.core.series.Series'>
0 4
1 5
Name: a, dtype: int64
<class 'pandas.core.series.Series'>
0 6
1 10
Name: b, dtype: int64
从打印结果我们也清晰地看到两点:transform
每次只计算一列;会出现计算了一个组整体的情况,这有点令人费解,待研究。
从上面的对比,我们直接得到了一个有用的警示:不要传一个同时涉及到多列的函数给transform
方法,因为那么做只会得到错误。例如下面的代码所示:
def subtract(x):
return x["a"] - x["b"]
try:
df_6.groupby("State").transform(subtract)
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
formatted_lines = traceback.format_exc().splitlines()
print(formatted_lines[-1])
KeyError: ('a', 'occurred at index a')
另一个警示则是:在使用 transform
方法的时候,不要去试图修改返回结果的长度,那样不仅会引发错误,而且traceback的信息非常隐晦,很可能你需要花很长时间才能真正意识到错误所在。
def return_more(x):
return np.arange(3)
try:
df_6.groupby("State").transform(return_more)
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
formatted_lines = traceback.format_exc().splitlines()
print(formatted_lines[-1])
ValueError: Length mismatch: Expected axis has 6 elements, new values have 4 elements
这个报错信息有点别扭,期待返回6个元素,但是返回的结果只有4个元素;其实,应该说预期的返回为4个元素,但是现在却返回6个元素,这样比较容易理解错误所在。
最后,让我们以一条有用的经验结束这个talk:如果你确信自己想要的操作时同时作用于多列,并且速度最好还很快,请不要用transform
方法,Talk9
有一个这方面的好例子。
Talk 7:agg 用法总结
(1)一次对所有列调用多个函数
df_0.groupby("A").agg([np.sum, np.mean, np.min])
C | D | |||||
---|---|---|---|---|---|---|
sum | mean | amin | sum | mean | amin | |
A | ||||||
bar | 0.978077 | 0.244519 | -1.343518 | -2.042817 | -0.510704 | -2.064735 |
foo | 0.184686 | 0.046172 | -1.854274 | 3.113988 | 0.778497 | 0.210586 |
(2)一次对特定列调用多个函数
df_0.groupby("A")["C"].agg([np.sum, np.mean, np.min])
sum | mean | amin | |
---|---|---|---|
A | |||
bar | 0.978077 | 0.244519 | -1.343518 |
foo | 0.184686 | 0.046172 | -1.854274 |
(3)对不同列调用不同函数
df_0.groupby("A").agg({"C": [np.sum, np.mean], "D": [np.max, np.min]})
C | D | |||
---|---|---|---|---|
sum | mean | amax | amin | |
A | ||||
bar | 0.978077 | 0.244519 | 0.917027 | -2.064735 |
foo | 0.184686 | 0.046172 | 1.348597 | 0.210586 |
df_0.groupby("A").agg({"C": "sum", "D": "min"})
C | D | |
---|---|---|
A | ||
bar | 0.978077 | -2.064735 |
foo | 0.184686 | 0.210586 |
(4)对同一列调用不同函数,并且直接重命名
df_0.groupby("A")["C"].agg([("Largest", "max"), ("Smallest", "min")])
Largest | Smallest | |
---|---|---|
A | ||
bar | 1.477379 | -1.343518 |
foo | 1.145852 | -1.854274 |
(5)对多个列调用同一个函数
agg_keys = {}.fromkeys(["C", "D"], "sum")
df_0.groupby("A").agg(agg_keys)
C | D | |
---|---|---|
A | ||
bar | 0.978077 | -2.042817 |
foo | 0.184686 | 3.113988 |
(6)注意agg
会忽略缺失值,这在计数时需要加以注意
df_7 = pd.DataFrame({"ID":["A","A","A","B","B"], "Num": [1,np.nan, 1,1,1]})
df_7
ID | Num | |
---|---|---|
0 | A | 1.0 |
1 | A | NaN |
2 | A | 1.0 |
3 | B | 1.0 |
4 | B | 1.0 |
df_7.groupby("ID").agg({"Num":"count"})
Num | |
---|---|
ID | |
A | 2 |
B | 2 |
注意:Pandas
中的 count
,sum
,mean
,median
,std
,var
,min
,max
等函数都用C语言优化过。所以,还是那句话,如果你在大数据集上使用agg
,最好使用这些函数而非从numpy
那里借用np.sum
等方法,一个缓慢的程序是由每一步的缓慢积累而成的。
Talk 8:**Filtration ** 易错点剖析
通常,在对一个 dataframe
分组并且完成既定的操作之后,可以直接返回结果,也可以视需求对结果作一层过滤。这个过滤一般都是指 filter
操作,但是务必要理解清楚自己到底需要对组作过滤还是对组内的每一行作过滤。这个Talk就来讨论过滤这个话题。
【例子】找出每门课程考试分数低于这门课程平均分的学生
df_8 = pd.DataFrame({"Subject": list(chain(*[["Math"]*3,["Computer"]*3])),
"Student": list(chain(*[["Chan", "Ida", "Ada"]*2])),
"Score": [80,90,85,90,85,95]})
df_8
Subject | Student | Score | |
---|---|---|---|
0 | Math | Chan | 80 |
1 | Math | Ida | 90 |
2 | Math | Ada | 85 |
3 | Computer | Chan | 90 |
4 | Computer | Ida | 85 |
5 | Computer | Ada | 95 |
这样一个需求是否适合用 filter
来处理呢?我们试试看:
try:
df_8.groupby("Subject").filter(lambda x: x["Score"] < x["Score"].mean())
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
formatted_lines = traceback.format_exc().splitlines()
print(formatted_lines[-1])
TypeError: filter function returned a Series, but expected a scalar bool
显然不行,因为 filter
实际上做的事情是要么留下这个组,要么过滤掉这个组。我们在这里弄混淆的东西,和我们初学 SQL
时弄混 WHERE
和 HAVING
是一回事。就像需要记住 HAVING
是一个组内语法一样,请记住 filter
是一个组内方法。
我们先解决这个例子,正确的做法如下:
df_8.groupby("Subject").apply(lambda g: g[g.Score < g.Score.mean()])
Subject | Student | Score | ||
---|---|---|---|---|
Subject | ||||
Computer | 4 | Computer | Ida | 85 |
Math | 0 | Math | Chan | 80 |
而关于 filter
,我们援引官方文档上的例子作为对比
df_9 = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
'foo', 'bar'],
'B' : [1, 2, 3, 4, 5, 6],
'C' : [2.0, 5., 8., 1., 2., 9.]})
df_9
A | B | C | |
---|---|---|---|
0 | foo | 1 | 2.0 |
1 | bar | 2 | 5.0 |
2 | foo | 3 | 8.0 |
3 | bar | 4 | 1.0 |
4 | foo | 5 | 2.0 |
5 | bar | 6 | 9.0 |
df_9.groupby('A').filter(lambda x: x['B'].mean() > 3.)
A | B | C | |
---|---|---|---|
1 | bar | 2 | 5.0 |
3 | bar | 4 | 1.0 |
5 | bar | 6 | 9.0 |
Part 3:groupby 应用举例
Talk 9:组内缺失值填充
df_10 = pd.DataFrame({"ID":["A","A","A","B","B","B"], "Num": [100,np.nan,300,np.nan,500,600]})
df_10
ID | Num | |
---|---|---|
0 | A | 100.0 |
1 | A | NaN |
2 | A | 300.0 |
3 | B | NaN |
4 | B | 500.0 |
5 | B | 600.0 |