Python数据分析实战四:条件筛选与自定义函数

上一章节( https://juejin.cn/editor/drafts/7268594193933254719 )介绍了对于一个特定的数据集,如何从总体上通过统计分析的方法对其进行分析,以便了解数据集的规模,数值特点以及数据的分布情况。本章在了解数据集的基础上,介绍如何对数据集进行条件筛选,选出想要的数据。同时对于一些复杂的场景,现有的函数无法支持时,如何将自定义函数加入到pandas的数据分析中。

为了继续说明问题,本章继续引用上一章节的测试数据集,即两个班级5名学生的成绩单数据:

import pandas as pd  
data = {  
    'NAME': ['Arial Johnson', 'Derek Davis', 'Latoya Mitchell', 'Tanisha Harris', 'Devin Price'],  
    'CLASS': ['A', 'A', 'A', 'B', 'B'],  
    'COURSE1': [99, 88, 81, 61, 97],  
    'COURSE2': [67, 76, 98, 58, 77],  
    'COURSE3': [93, 97, 91, 94, 51],  
    'COURSE4': [95, 65, 80, 53, 65]  
}  
df = pd.DataFrame(data)

6.条件筛选

6.1 条件判断方法

常用的条件运算符:

  • >/>=:大于/大于等于
  • </<=:小于/小于等于
  • ==/!=:等于/不等于

常用的逻辑运算符

  • &:逻辑与
  • |:逻辑或
  • ~:逻辑非

通过使用条件运算符和逻辑运算符的组合,我们可以创建复杂的布尔条件来进行数据集的筛选和选择。除此之外,由于df的一列相当于Series,可以用Series中的条件判断函数来实现。

  • isin/notin:用于判断Series中的值是否在给定的列表或集合中;
  • isnull/notnull:用于判断Series中的值是否为NaN;

对于Series中的字符类型,可以用pandas.core.strings.StringMethods中的关于字符型的条件判断方法。

  • str.contains():检查字符串中是否包含指定的子串
  • str.isalnum():检查字符串是否由字母和数字组成
  • str.isalpha():检查字符串是否由字母组成
  • str.isdigit():检查字符串是否由数字组成
  • str.isdecimal():检查字符串是否由十进制数字组成
  • str.islower():检查字符串是否全小写
  • str.isupper():检查字符串是否全大写
  • str.istitle():检查字符串是否以大写字母开头
  • str.match():检查字符串是否与指定的正则表达式匹配
  • str.startswith():检查字符串是否以指定的前缀开头

在Pandas中,可以使用条件筛选来过滤数据集中符合特定条件的行,Pandas 提供了多种方法来进行条件筛选,包括使用布尔索引、使用 query() 函数和使用 lociloc 索引器。下面逐一介绍这些方法的用法。

6.2 布尔索引

在 Pandas 中,布尔索引是一种使用布尔条件(True/False)来选择数据集中行的方法。通过使用布尔条件,我们可以筛选出符合特定条件的行,并进行数据集的条件筛选和子集选择。布尔索引也叫布尔数组,数组的长度与数据的行数相同。

我们将各门课程考试分数大于等于80分作为优秀,并以此作为条件判断,可生成一个布尔数组:

>>> df['COURSE1'] > 80
0     True
1     True
2     True
3    False
4     True
Name: COURSE1, dtype: bool

# 通过布尔数组,筛选出课程1考试优秀的记录:
>>> df[df['COURSE1'] >= 80]
              NAME CLASS  COURSE1  COURSE2  COURSE3  COURSE4
0    Arial Johnson     A       99       67       93       95
1      Derek Davis     A       88       76       97       65
2  Latoya Mitchell     A       81       98       91       80
4      Devin Price     B       97       77       51       65

# 还可以用到上面说的条件表达式,筛选出所有课程考试都优秀的考生
# 注意,当用到多个条件时,要用括号括起来

>>> df[(df['COURSE1'] >= 80) & (df['COURSE2'] >= 80)  & (df['COURSE3'] >= 80)  & (df['COURSE4'] >= 80)]
              NAME CLASS  COURSE1  COURSE2  COURSE3  COURSE4
2  Latoya Mitchell     A       81       98       91       80

6.2 query函数

Pandas中的query函数是一种特殊的条件表达式,可以用于在DataFrame中执行复杂的筛选、排序和聚合操作。它允许使用Python中的所有标准运算符和函数,以及自定义函数,来构建复杂的查询语句。query函数使用起来可读性要大于用布尔索引,但布尔索引在大数据集的情况下,比前者效率更高。上面的两个条件表达式查询,可以表示为:

# 求课程1考试优秀的学生
>>> df.query('COURSE1 > 80')
              NAME CLASS  COURSE1  COURSE2  COURSE3  COURSE4
0    Arial Johnson     A       99       67       93       95
1      Derek Davis     A       88       76       97       65
2  Latoya Mitchell     A       81       98       91       80
4      Devin Price     B       97       77       51       65

# 求每一门科都优秀的学生
>>> df.query('COURSE1 >= 80 and COURSE2 >= 80 and COURSE3 >= 80 and COURSE4 >= 80')
              NAME CLASS  COURSE1  COURSE2  COURSE3  COURSE4
2  Latoya Mitchell     A       81       98       91       80

6.3 使用行和列的标签或整数位置进行筛选

关于loc和iloc两个方法我们在前面的第二章节中已经做了介绍,在此不再赘述,用这两个函数来做条件筛选,依然要依赖于布尔数组:

# 使用行标签
>>> df.loc[df['COURSE1'] > 80]
>>> df.loc[(df['COURSE1'] >= 80) & (df['COURSE2'] >= 80)  & (df['COURSE3'] >= 80)  & (df['COURSE4'] >= 80)]
# 结果略,与上面的结果是相同的

# 使用整数位置
>>> df.iloc[(df['COURSE1'] > 80).values]
>>> df.iloc[(df['COURSE1'] >= 80) & (df['COURSE2'] >= 80)  & (df['COURSE3'] >= 80)  & (df['COURSE4'] >= 80).values]

7.自定义函数

7.1 Lambda表达式与自定义函数

Lambda表达式是Python中定义匿名函数的一种方法。所谓匿名函数,即没有名字的函数,使用lambda关键字定义,可以在需要函数对象的任何位置使用。Lambda函数常用于函数引用传递参数等情况下,可用于一行代码实现一些简单的功能。
一个lambda表达式的例子:

>>> f = lambda x: x**2  
>>> f(5) 
25

Lambda表达式可以让代码变得很简洁,对于一些简单逻辑,提高代码的可读性,但对于复杂逻辑,很难在一个表达式中进行拆解,这种情况下则不建议使用lambda表达式。在Pandas中,Lambda表达式通常用于DataFrame数据的处理,可以实现一些非常灵活的数据处理方法。

7.2 常用函数

Pandas提供的丰富的统计分析相关的函数,可以满足我们大多数的数据分析要求,但是对于一些特殊的逻辑,要求逻辑更加灵活且更贴近于业务,这就不得不要求我们利用pandas的一些函数嵌套自定义函数进行分析,常用的函数有:apply,map,applymap,agg和transform接下来逐一介绍它们的用法。

7.2.1 apply

apply()函数用于对DataFrame或Series中的每个元素应用一个自定义的函数,生成一个新的DataFrame或Series。apply()函数的参数可以是一个函数或一个lambda表达式,用于将原始值映射为新的值。apply()函数可以用于对每个元素进行逐个处理,也可以用于对每个列或每个行进行处理。

基本语法:

DataFrame.apply(func, axis=0, raw=False, result_type=None, args=(), **kwds)

Series.apply(func, convert_dtype=True, args=(), **kwds)

参数说明:

func:自定义函数或lambda表达式,用于将原始值映射为新的值。

axis:指定应用函数的轴,0表示列,1表示行,默认为0。

raw:是否将每行或每列作为一个Series对象传递给函数,默认为False,即将每个元素作为一个标量值传递给函数。

result_type:指定返回值的类型,可以是None、‘expand’或’reduce’。如果为None,则返回一个Series或DataFrame对象;如果为’expand’,则返回一个DataFrame对象;如果为’reduce’,则返回一个Series对象。

args:传递给函数的其他参数,以元组的形式传递。

convert_dtype:是否将Series对象的数据类型转换为函数返回值的数据类型,默认为True。

对每一列执行apply函数的场景如下:

# 判断科目1是否及格
>>> df.apply(lambda x:x['COURSE2'] > 60, axis=1)
0     True
1     True
2     True
3    False
4     True
dtype: bool

对每一行执行apply函数的场景如下:

# 判断所有的科目是否及格
>>> df[['COURSE1', 'COURSE2', 'COURSE3', 'COURSE4']].apply(lambda x:(x > 60), axis=1)
   COURSE1  COURSE2  COURSE3  COURSE4
0     True     True     True     True
1     True     True     True     True
2     True     True     True     True
3     True    False     True    False
4     True     True    False     True

当然,除了lambda表达式,还可以支持更加复杂的计算逻辑,为了不显示分数,可以以分数等级来划分成绩,这个时候就可以用自定义函数。

分数等级分数范围
A90-100
B80-89
C70-79
D60-69
F<60
对应的自定义函数如下:
def score_level(score):
    if score < 60:
        return 'F'
    elif score < 70:
        return 'D'
    elif score < 80:
        return 'C'
    elif score < 90:
        return 'B'
    else:
        return 'A'

此时可以用apply+自定义函数的形式求得科目1的分数等级:

>>> df.apply(lambda x: score_level(x['COURSE1']), axis=1)
0    A
1    B
2    B
3    D
4    A
dtype: object

7.2.2 map

map()函数用于对Series中的每个元素应用一个自定义的函数,生成一个新的Series。与apply()函数类似,但是只能用于Series,不能用于DataFrame。
用map函数,上一节的功能也可以写成:

>>> df['COURSE1'].map(lambda x: score_level(x))
0    A
1    B
2    B
3    D
4    A
Name: COURSE1, dtype: object

7.2.3 applymap

applymap()函数用于对DataFrame中的每个元素应用一个自定义的函数,生成一个新的DataFrame。与apply()函数类似,但是可以用于DataFrame中的每个元素,而不是每个列或每个行。

用applymap函数,可以求得每门课的分数等级。

>>> df[['COURSE1', 'COURSE2', 'COURSE3', 'COURSE4']].applymap(lambda x: score_level(x))
  COURSE1 COURSE2 COURSE3 COURSE4
0       A       D       A       A
1       B       C       A       D
2       B       A       A       B
3       D       F       A       F
4       A       C       F       D

7.2.4 agg

agg() 函数agg 函数是 aggregate 函数的简写。它的作用是对一个 DataFrame 或 Series 中的数据进行聚合计算,可以用于一次性计算多个统计量,并将结果返回为一个 DataFrame 或 Series。agg 函数常用的参数是一个字典,字典的键是用于聚合的列名,值是要进行聚合的统计量。

agg()函数主要用于聚合,相比对apply函数有更多的限制,函数仅适用于聚合函数。但使用agg()函数的好处是可以用不同的方法对不同的列进行聚合:

# 同时求每一门的最大值,最小值和平均值
>>> df[['COURSE1', 'COURSE2', 'COURSE3']].agg(['max', 'min', 'mean'])
      COURSE1  COURSE2  COURSE3
max      99.0     98.0     97.0
min      61.0     58.0     51.0
mean     85.2     75.2     85.2
# 在一个agg式子中,同时求科目1的最大值,科目2的最小值和科目三的平均值
>>> df[['COURSE1', 'COURSE2', 'COURSE3']].agg({'COURSE1':'max', 'COURSE2':'min', 'COURSE3': 'mean'})
COURSE1    99.0
COURSE2    58.0
COURSE3    85.2
dtype: float64

7.2.5 transform

transform()函数用于对DataFrame中的每个列应用一个自定义的函数,生成一个新的DataFrame。与agg()函数类似,但是可以保留原始的行和列索引。与apply函数不同,transform函数在处理后将数据返回为与原始数据相同的形状,这在某些情况下非常有用。
使用transform函数可以用于标准化数据:

>>> df[['COURSE1', 'COURSE2', 'COURSE3', 'COURSE4']].transform(lambda x: (x - x.min()) / (x.max() - x.min()))
    COURSE1  COURSE2   COURSE3   COURSE4
0  1.000000    0.225  0.913043  1.000000
1  0.710526    0.450  1.000000  0.285714
2  0.526316    1.000  0.869565  0.642857
3  0.000000    0.000  0.934783  0.000000
4  0.947368    0.475  0.000000  0.285714

计算排名:

>>> df[['COURSE1', 'COURSE2', 'COURSE3', 'COURSE4']].transform('rank', method='dense', ascending=False)
   COURSE1  COURSE2  COURSE3  COURSE4
0      1.0      4.0      3.0      1.0
1      3.0      3.0      1.0      3.0
2      4.0      1.0      4.0      2.0
3      5.0      5.0      2.0      4.0
4      2.0      2.0      5.0      3.0

7.2.6 总结

以上函数中,apply是最灵活的,但是对于分组数据的聚合,用agg是最方便的,对于分组转换和展开,transform是最方便的,各有所长。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值