apply()
apply方法可以沿着DataFrame数据的行、列执行回调函数
首先看一下apply方法的参数,apply方法返回的数据类型为Series或DataFrame
apply(func, axis=0, raw=False, result_type=None, args=(), **kwargs) -> Series或DataFrame
注意:调用apply方法的必须是DataFrame数据,apply方法并不会改变原始数据
apply方法官方文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html
参数func和axis
func: 回调函数,该函数通常只有一个表示Series数据类型的形参,Series可能是行,也可能是列
axis:0 或 “index” 表示对每一列执行回调函数func, 1或”column”表示对每一行执行回调函数func。默认值为0
假设有如下的DataFrame数据,取变量名为df:
import pandas as pd
obj = {'x': [2, 4], 'y': [6, 8]}
df = pd.DataFrame(obj)
print(df)
x y
0 2 6
1 4 8
该数据中有列名为x、列名为y的数据(也可以说行索引为0、行索引为1的数据),对于其中任意一行(或一列)其数据类型为Series,整个行列的数据类型为DataFrame。
现在我们已经知道了apply方法的前两个参数,也知道了已有数据df,那么我们传入df数据的每一列:
def func(data):
print(data)
df.apply(func, axis=0)
首先,我们定义了回调函数func,然后调用apply方法并传递参数func和axis,这样df中的每一列数据都会作为参数传递到func函数中处理。因为是对每一列数据进行处理,所以axis为0。当然axis默认值为0,也可以省略为:df.apply(func)
。此时,func的参数data表示的是每一列的数据。
执行到上面的代码第4行,可以看到如下按列的输出:
# 第一次列输出,输出x列数据
0 2
1 4
Name: x, dtype: int64
# 第二次列输出,输出y列数据
0 6
1 8
Name: y, dtype: int64
还是最开始数据df,如果传入df数据的每一行,那么应该这样写:
def func(data):
print(data)
df.apply(func, axis=1)
对于上一次的func和axis参数可以发现,axis的参数变为1,表示函数func的参数data值的是df中的每一行数据。在func中data返回。df调用apply方法好后执行到第4行,可以看到如下按行的输出:
# 第一次行输出, 输出索引为0的行
x 2
y 6
Name: 0, dtype: int64
# 第2次行输出, 输出索引为1的行
x 4
y 8
Name: 1, dtype: int64
通常我们会对每一列(或行)中的数据进行其它运算:
def func(data):
return data*2 # 将每一列(或行)的数据乘以2
df2 = df.apply(func, axis=0)
print(df2)
# 输出如下:
x y
0 4 12
1 8 16
注意:apply方法并不会改变原始数据,因此将df.apply()赋值给了df2
对于上面简单的写法可以使用lambda表达式代替:
df2 = df.apply(lambda data: data*2, axis=0)
print(df2)
# 输出如下:
x y
0 4 12
1 8 16
参数raw
raw: 布尔值True或False,默认为False。该参数指明传入回调函数func的data参数是什么数据类型?也就是说df的每一行(或每一列)作为何种数据类型的参数传递到func中。False表示以Series类型传递,True表示以ndaary类型传递。
def func(data):
print(type(data), data)
df.apply(func, raw=False)
# 以下为执行apply时的输出
<class 'pandas.core.series.Series'> 0 2
1 4
Name: x, dtype: int64
<class 'pandas.core.series.Series'> 0 6
1 8
Name: y, dtype: int64
上面的样例中,显式的设置了raw=False(当然raw默认为False,可以省略),通过打印type(data)可以看到数据类型为Series。
下面的样例中,显式的设置raw=True,查看打印的数据类型为ndarray。
def func(data):
print(type(data), data)
df.apply(func, raw=True)
# 以下为执行apply时的输出
<class 'numpy.ndarray'> [2 4]
<class 'numpy.ndarray'> [6 8]
参数result_type
result_type: 该参数仅在axis=1时有效可用,执行func函数后,将处理后的行(或列)数据转变为何种数据类型。默认值为None,它还要其它三种值,其含义如下:
- “expand”: 将类似列表的数据将转变为列
- “reduce”: 尽可能的将数据作为Series数据类型返回
- “broadcast”: 将数据填充至DataFrame的原始结构,原始的索引index和列column将被保留
- “None”: 默认值。 1.类似列表的数据将作为Series返回;2.如果func返回的就是Series类型数据,则该列数据将被添加到df的末尾(即增加一列数据)
参数args和kwargs
args: 该参数是一个元组。如果func有其它位置参数,那么必须放在这里,
**kwargs:该参数是一个字典。如果func有其它关键字参数,那么必须放在这里。
例如:func含有其它位置参数z1、 z2:
def func(data, z1, z2):
return data+z1+z2
df2 = df.apply(func, args=(3,4)) # 注意axis默认为0, z1的值为3, z2的值为4
print(df2)
x y
0 9 13
1 11 15
例如:func含有位置参数z1、z2和一个关键字参数a1、a2:
def func(data, z1, z2, a1=10, a2=11):
return data+z1+z2-a1-a2
kwargs = {'a1':5, 'a2':6}
df2 = df.apply(func, args=(3,4), **kwargs) # func中的参数:z1为3, z2为4, a1为5, a2为6 (如果不传递a1或a2,则a1值为默认值10、a2值为默认值11)
print(df2)
x y
0 -2 2
1 0 4
注意:当回调函数func既有args也有kwargs参数时,调用apply时传递的参数并不必须和定义时一样。例如:
def func(data, z1, z2, a1=10, a2=11):
return data+z1+z2-a1-a2
df2 = df.apply(func, args=(3,4,5)) # 并不会报错
print(df2)
x y
0 -7 -3
1 -5 -1
在上面的例子中,func在定义时既有args参数(z1,z2)也有kwargs参数(a1,a2),但是再调用apply时,我们仅仅给args参数进行赋值,而且运算程序也没有报错,这是因为kwargs参数有默认值,kwargs参数在没有传参时会使用默认值。args=(3,4,5)中的3、4分别传递给了z1、z2,而5传递给了a1,由于没有给a2传递数据,因此a2会使用默认值11。
假如进行如下调用,则会报错:
df.apply(func, args=(3,4,5,6,7))
TypeError: func() takes from 3 to 5 positional arguments but 6 were given
因为func中除了第一个参数外(第一个参数不需要显式传递),仅定义了4个参数(z1, z2, a1, a2),但是args中传递了5个参数,因此args中的前4个参数可以传递,但是args中的第5个数据由于func并没有定义相关参数,所以会报错。