Pandas进阶:apply的优雅使用方法

647 阅读3分钟

Pandas系列文章


沿 DataFrame 的轴应用函数

Pnadas 的官方文档中是这样介绍apply方法的。通俗点讲就是apply函数需传入一个方法,同时需指定这个方法的参数是按哪个轴进行传递的,传入 axis=0 代表按索引轴传递参数(即竖向传递,把每列的值传入函数中进行计算);传入axis=1代表按 列名轴传递参数(即横向传递,把每行的值传入函数中进行计算);所以我们可以知道无论是按索引轴或是按列名(columns)轴传递的对象类型都是Series

Example

df = pd.DataFrame({
    'name': ['lihua', 'lilei', 'hanmeimei', 'xiaoming', 'xiaohong'],
    'math': [99, 100, 80, 50, 118],
    'english': [94, 83, 99, 79, 108],
    'chinese': [107, 82, 76, 100, 113]
})
df
        name  math  english  chinese
0      lihua    99       94      107
1      lilei   100       83       82
2  hanmeimei    80       99       76
3   xiaoming    50       79      100
4   xiaohong   118      108      113

对一列或某几列数据进行操作

  • 当你想知道各科第一名的成绩时:
s = df[['math', 'english', 'chinese']].apply(max)

math       118
english    108
chinese    113
dtype: int64
  • 当你想知道各科成绩的平均分时:
s = df[['math', 'english', 'chinese']].apply(np.average)

math       89.4
english    92.6
chinese    95.6
dtype: float64
  • 当你想知道每个学生的总分时:
df.index = df['name']
s = df[['math', 'english', 'chinese']].apply(sum, axis=1) # 按columns轴求和,也就是横向求和

name
lihua        300
lilei        265
hanmeimei    255
xiaoming     229
xiaohong     339
dtype: int64

分组后对每组数据进行特定的操作

当我们的DF数据结构是这样的时候:

        name  math  english  chinese    class
0      lihua    99       94      107  class_1
1      lilei   100       83       82  class_2
2  hanmeimei    80       99       76  class_2
3   xiaoming    50       79      100  class_1
4   xiaohong   118      108      113  class_2
  • 按班级分组并求每个班级的各科的平均分
s = df.groupby('class')[['math', 'english', 'chinese']].apply(lambda x: x.apply(np.average))

             math    english     chinese
class                                    
class_1  74.500000  86.500000  103.500000
class_2  99.333333  96.666667   90.333333
  • 求分组后每组总分排第二的学生

def get_second(x: pd.DataFrame):
    x = x.sort_values('sum', ascending=False)
    if len(x) < 2:  # 没有第二名 返回第一名
        return x.iloc[0]
    return x.iloc[1]


# 先求出学生的总分
df['sum'] = df[['math', 'english', 'chinese']].apply(sum, axis=1)
result = df.groupby('class').apply(get_second)

             name  math  english  chinese    class  sum
class                                                  
class_1  xiaoming    50       79      100  class_1  229
class_2     lilei   100       83       82  class_2  265

该例传入了自定义的函数 get_second。其原理为apply方法对分组后形成的两个(按class分组,示例中只有class_1, class_2两个班级的学生)DF对象分别应用get_second函数。get_second函数内部对传入的DF按sum列从大到小进行了排序,同时要取排名第二位的就要校验传入的数据列是否大于2.

下图中按步骤介绍了该例是如何一步一步实现的:

截屏2021-03-25 上午9.28.38.png

使用apply函数的关键是具体的操作思路一定要清晰,根据我们想要的结果推到出是否要分组后再进行应用函数,或者直接可以在原有的DF对象中应用,在原有的DF对象中应用函数也应该先想好是对每行应用还是对每列应用。关键的第一步走好之后,那就是实现应用函数的具体功能了,这就要根据我们实际的需求来实现了。