3.Pandas高级函数应用

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# plt.style.use("bmh")
plt.rc('font', family='SimHei', size=13)
%matplotlib inline

import warnings
warnings.filterwarnings('ignore')

pd.__version__
'2.1.1'

1.1 函数应用

1.1.1 apply

apply()作为一种可自定义的函数,可以对series和dataframe的行列进行自定义函数操作并返回结果。对于分组、窗口、重采样下的dataframe子集等场景也同样适用,是复杂逻辑实现的一个非常强力的方法。
需要注意的是,apply()在对series和dataframe的用法上有所差别:

  • series:apply操作的对象是series的每个元素,不可设置行列轴方向;
  • dataframe:apply操作的对象是dataframe中的每一列或者每一行,可以设置行列方向。
# 定义数据集
df_english=pd.DataFrame(
     {
         "student": ["John", "James", "Jennifer"],
         "gender": ["male", "male", "female"],
         "score": [20, 30, 30],
         "subject": "english"
     }
 )
 
df_math=pd.DataFrame(
     {
         "student": ["John", "James", "Jennifer"],
         "gender": ["male", "male", "female"],
         "score": [90, 100, 95],
         "subject": "math"
     }
 )
df = pd.concat([df_english, df_math],ignore_index=True)
df = df.sort_values(['student','subject']).reset_index(drop=True)
df
student gender score subject
0 James male 30 english
1 James male 100 math
2 Jennifer female 30 english
3 Jennifer female 95 math
4 John male 20 english
5 John male 90 math
Series
df['score'].apply(lambda x:x-3 if x>90 else x)
0    30
1    97
2    30
3    92
4    20
5    90
Name: score, dtype: int64
DataFrame
# axis=0 -->>对列操作
def col(x):
    if x.name=='score':
        return x+5
    else:
        return x
    
df = df.apply(col, axis=0)
df
student gender score subject
0 James male 35 english
1 James male 105 math
2 Jennifer female 35 english
3 Jennifer female 100 math
4 John male 25 english
5 John male 95 math
# axis=1 -->>对行操作
def row(x):
    if x['subject']=='english':
        para=1.1
    else:
        para=1.2
    return x['score']*para

df['new_score'] = df.apply(row, axis=1,result_type='expand')
df
student gender score subject new_score
0 James male 35 english 38.5
1 James male 105 math 126.0
2 Jennifer female 35 english 38.5
3 Jennifer female 100 math 120.0
4 John male 25 english 27.5
5 John male 95 math 114.0
传入参数

函数参数需要在args参数中设置,以元组方式args=(x1, x2, ...)完成。

# args参数
def score_bias(x, bias):
    if x > 90:
        return x+bias
    else:
        return x
    
df["score"] = df["score"].apply(score_bias, args=(-3,))     # bias是形参,-3是实参
df
student gender score subject new_score
0 James male 35 english 38.5
1 James male 102 math 126.0
2 Jennifer female 35 english 38.5
3 Jennifer female 97 math 120.0
4 John male 25 english 27.5
5 John male 92 math 114.0
传入关键字

传入关键字english=0math=1。函数内通过kwargs['english']=0kwargs['math']=1

# 传入关键字
def subject_map(x, **kwargs):
    return kwargs[x]
    
df["subject_no"] = df["subject"].apply(subject_map, english=0, math=1)
df
student gender score subject new_score subject_no
0 James male 35 english 38.5 0
1 James male 102 math 126.0 1
2 Jennifer female 35 english 38.5 0
3 Jennifer female 97 math 120.0 1
4 John male 25 english 27.5 0
5 John male 92 math 114.0 1

1.1.2 applymap

applymap()应用于dataframe时(且只能用于dataframe),操作对象是每个元素,即接收一个标量元素经过函数处理后再返回一个标量函数,是点对点的操作。

def el_cook(x):
    if isinstance(x,str):
        return 's_'+x
    else:
        return str(x)
df.applymap(el_cook)
student gender score subject new_score subject_no
0 s_James s_male 35 s_english 38.5 0
1 s_James s_male 102 s_math 126.0 1
2 s_Jennifer s_female 35 s_english 38.5 0
3 s_Jennifer s_female 97 s_math 120.0 1
4 s_John s_male 25 s_english 27.500000000000004 0
5 s_John s_male 92 s_math 114.0 1

1.1.3 map

map()只能应用于series,可以将series元素通过字典或函数进行映射操作。

  • arg: 映射参数,可以是映射子类如字典、函数或series;
  • na_action:对na空值的处理;
    • None:默认传给函数处理;
    • ignore:忽略并直接传播空值,而不传给函数处理。
字典映射
GENDER_ENCODING= {
     "male": 0,
     "female": 1
 }
df['gender_map'] = df["gender"].map(GENDER_ENCODING)
df
student gender score subject new_score subject_no gender_map
0 James male 35 english 38.5 0 0
1 James male 102 math 126.0 1 0
2 Jennifer female 35 english 38.5 0 1
3 Jennifer female 97 math 120.0 1 1
4 John male 25 english 27.5 0 0
5 John male 92 math 114.0 1 0
函数映射

缺点是不能像apply()一样传入参数和关键字。大数据时映射效率更高。

df['score'].map(np.sqrt).to_frame()
score
0 5.916080
1 10.099505
2 5.916080
3 9.848858
4 5.000000
5 9.591663
df['student'].map(list).to_frame()
student
0 [J, a, m, e, s]
1 [J, a, m, e, s]
2 [J, e, n, n, i, f, e, r]
3 [J, e, n, n, i, f, e, r]
4 [J, o, h, n]
5 [J, o, h, n]
# 自定义函数
df['score'].map(lambda x:x-3 if x>90 else x)
0    35
1    99
2    35
3    94
4    25
5    89
Name: score, dtype: int64

1.1.4 transform

transform()是一种转换函数,其特点是返回结果与自身的形状相同,即前后的shape形状保持不变。因此,transform()的返回结果与自身形状相同,所以不支持有降维功能的函数,比如聚合函数minmeanstd

# 生成测试数据
cols = ["C_0", "C_1", "C_2", "C_3"]
np.random.seed=123
df = pd.DataFrame(np.random.randint(1, 5, size = (5,4)), columns=cols)
df
C_0 C_1 C_2 C_3
0 3 3 3 3
1 4 2 3 1
2 1 1 1 4
3 3 2 1 2
4 4 1 2 2
单个函数

传入的单个函数可以是库函数、自定义函数或匿名函数。

df.transform(np.exp).transform(lambda x:round(x,2))
C_0 C_1 C_2 C_3
0 20.09 20.09 20.09 20.09
1 54.60 7.39 20.09 2.72
2 2.72 2.72 2.72 54.60
3 20.09 7.39 2.72 7.39
4 54.60 2.72 7.39 7.39
多个函数

transform()传入多个函数是列表形式时,会对dataframe所有列依次进行各函数的转换操作。注意如果有名字相同的函数,只有最后一个函数生效,这个不同于agg()函数可以使得多个匿名函数同时生效。

df.transform([np.square, np.sqrt]).transform(lambda x:round(x,2))
C_0 C_1 C_2 C_3
square sqrt square sqrt square sqrt square sqrt
0 9 1.73 9 1.73 9 1.73 9 1.73
1 16 2.00 4 1.41 9 1.73 1 1.00
2 1 1.00 1 1.00 1 1.00 16 2.00
3 9 1.73 4 1.41 1 1.00 4 1.41
4 16 2.00 1 1.00 4 1.41 4 1.41

1.1.5 pipe

前面的applymap()是元素级、apply()transform()是行列级应用的函数,pipe()则是表格级的应用函数,称为管道函数。

单个函数
df.pipe(np.exp).pipe(lambda x: round(x, 2))
C_0 C_1 C_2 C_3
0 20.09 20.09 20.09 20.09
1 54.60 7.39 20.09 2.72
2 2.72 2.72 2.72 54.60
3 20.09 7.39 2.72 7.39
4 54.60 2.72 7.39 7.39
链式调用
df.pipe(np.square).\
   pipe(np.multiply, 1.5).\
   pipe(np.add, 8)
C_0 C_1 C_2 C_3
0 21.5 21.5 21.5 21.5
1 32.0 14.0 21.5 9.5
2 9.5 9.5 9.5 32.0
3 21.5 14.0 9.5 14.0
4 32.0 9.5 14.0 14.0
特殊传参方式

pipe()默认情况下会将dataframe传给调用函数的第一个参数,但一些函数的第一个参数并不是用来接收dataframe输入数据的,如果直接将函数传到pipe()中会提示报错。
为了解决这个问题,pipe()中规定了一种特殊的参数传递方法,是**元组(callable, data_keyword)**的形式。

  • callable:指定pipe()中调用的函数;
  • data_keyword:指定将dataframe传给函数中的哪一个参数。
def spcl(num, df):
    return df.add(num)
df.pipe((spcl,'df'), 2)
C_0 C_1 C_2 C_3
0 5 5 5 5
1 6 4 5 3
2 3 3 3 6
3 5 4 3 4
4 6 3 4 4

1.2 表达式求值

1.2.1 eval

eval()是一个神奇的函数,可以通过字符串表达式的方式对series和dataframe进行计算和解析操作。其有两大优势:

  1. 对数据较大的dataframe对象操作更高效;
  2. 对复杂的算术和布尔运算更快速,因为后端计算引擎默认是numexpr

eval()支持以下算术操作:

  1. 算术运算:除左移<<和右移>>运算符外的算术运算
  2. 比较操作:包括链式比较,比如,2 < df < df2
  3. 布尔运算:例如,df < df2 and df3 < df4 or not df_bool
  4. 列表和元组:如[1, 2],(1, 2)
  5. 属性访问,如df.a
  6. 下标表达式:如df[0]
  7. 变量评估:如pd.eval('df')
  8. 数学函数:如sincos

eval()不允许使用Python语法:

  • 表达式
    • 数学函数以外的函数调用
    • is is not操作
    • if表达式
    • lambda表达式
    • list/set/dict comprehension
    • literal的dictset表达式
    • yield表达
    • 生成器表达式
    • 仅包含标量值的布尔表达式
  • 声明
    • for, while, if

eval()有两种函数形式

  1. pandas.eval()
  2. dataframe.eval(),是前者的高级封装。
单列变量
pd.eval("C_4 = (df.C_0 > 1) & (df.C_2 == 4)", target=df)
C_0 C_1 C_2 C_3 C_4
0 3 3 3 3 False
1 4 2 3 1 False
2 1 1 1 4 False
3 3 2 1 2 False
4 4 1 2 2 False
df.eval("C_4 = (C_0 > 1) & (C_2 == 4)")            # 与上面等价
C_0 C_1 C_2 C_3 C_4
0 3 3 3 3 False
1 4 2 3 1 False
2 1 1 1 4 False
3 3 2 1 2 False
4 4 1 2 2 False
多列变量
df.eval(
    """
    C_4 = C_0 + C_1
    C_5 = C_1 + C_2
    C_6 = C_2 + C_3
    """
)
C_0 C_1 C_2 C_3 C_4 C_5 C_6
0 3 3 3 3 6 6 6
1 4 2 3 1 6 5 4
2 1 1 1 4 2 2 5
3 3 2 1 2 5 3 3
4 4 1 2 2 5 3 4
局部变量

字符串表达式中可加入局部变量参与计算,通过@前缀标识完成,该前缀方法只能应用于dataframe.eval()函数,对pd.eval()不生效。

a = 5
b = 2
df.eval("C_4 = C_0 * @a + @b")
C_0 C_1 C_2 C_3 C_4
0 3 3 3 3 17
1 4 2 3 1 22
2 1 1 1 4 7
3 3 2 1 2 17
4 4 1 2 2 22
a = 5
b = 2
pd.eval("C_4 = df.C_0 * a + b", target=df)
C_0 C_1 C_2 C_3 C_4
0 3 3 3 3 17
1 4 2 3 1 22
2 1 1 1 4 7
3 3 2 1 2 17
4 4 1 2 2 22
类型解析

这里的eval()是Python的内置方法,用来解析字符串。

# 字符类型
a = '[1,2,3]'
type(a) 
str
b=eval(a)
type(b)
list

1.2.2 query

dataframe.query()dataframe.eval()一样,也是pd.eval()的高级封装,可对dataframe对象进行查询操作。

你可能感兴趣的:(Pandas,pandas,数据挖掘,人工智能)