import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='SimHei', size=13)
%matplotlib inlineimport 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
 
def col(x):if x.name=='score':return x+5else:return xdf = 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 | 
|---|
 
def row(x):if x['subject']=='english':para=1.1else:para=1.2return x['score']*paradf['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, ...)完成。
 
def score_bias(x, bias):if x > 90:return x+biaselse:return xdf["score"] = df["score"].apply(score_bias, args=(-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=0和math=1。函数内通过kwargs['english']=0和kwargs['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_'+xelse: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()的返回结果与自身形状相同,所以不支持有降维功能的函数,比如聚合函数min、mean、std。
 
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进行计算和解析操作。其有两大优势:
 
- 对数据较大的dataframe对象操作更高效;
- 对复杂的算术和布尔运算更快速,因为后端计算引擎默认是numexpr。
eval()支持以下算术操作:
 
- 算术运算:除左移<<和右移>>运算符外的算术运算
- 比较操作:包括链式比较,比如,2 < df < df2
- 布尔运算:例如,df < df2 and df3 < df4 or not df_bool
- 列表和元组:如[1, 2],(1, 2)
- 属性访问,如df.a
- 下标表达式:如df[0]
- 变量评估:如pd.eval('df')
- 数学函数:如sin、cos等
eval()不允许使用Python语法:
 
- 表达式 - 数学函数以外的函数调用
- is- is not操作
- if表达式
- lambda表达式
- list/set/dictcomprehension
- literal的dict和set表达式
- yield表达
- 生成器表达式
- 仅包含标量值的布尔表达式
 
- 声明  
eval()有两种函数形式
 
- pandas.eval()
- 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_1C_5 = C_1 + C_2C_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对象进行查询操作。