pandas 的 apply()
函数可以作用于 Series
或者整个 DataFrame
,功能也是自动遍历整个 Series
或者 DataFrame
, 对每一个元素运行指定的函数。
Pandas提供了很多数据处理的API,但当提供的API不能满足需求的时候,需要自己编写数据处理函数, 这个时候可以使用apply函数
apply函数可以接收一个自定义函数, 可以将DataFrame的行/列数据传递给自定义函数处理
apply函数类似于编写一个for循环, 遍历行、列的每一个元素, 但比使用for循环效率高很多
Series的apply自定义函数接收的参数x是一个数值
def my_exp(x,e):
return x+e
# series.apply(自定义函数名, args=(参数1, 参数2, ...))
# args参数必须接收一个元祖;没有参数就不用写args=
df['列名'].apply(my_exp, args=(3,))
使用方法和Series的apply自定义函数的用法一致,比如对自定义函数传入多个参数的用法都是一样的
默认按列遍历执行
df的apply自定义函数接收的参数x默认是一列数据
def my_exp2(x):
return x+2
df.apply(my_exp2)
按行遍历执行
参数axis=1
使x的值是一行数据
def my_exp2(x):
return x+2
df.apply(my_exp2, axis=1)
按df中的每一个值遍历执行
def my_exp2(x):
return x+2
df.applymap(my_exp2)
np.vectorize()
def foo(x, y):
if x != 0: # x,y都是series对象,不能和数字类型做比较判断
return x + y
# foo(df['列名1'], df['列名2']) # 报错!
import numpy as np
bar = np.vectorize(foo)
bar(df['列名1'], df['列名2'])
对自定义函数使用装饰器
import numpy as np
@np.vectorize
def foo(x, y):
if x != 0: # x,y都是series对象,不能和数字类型做比较判断
return x + y
foo(df['列名1'], df['列名2'])
概念
不用写函数名字的函数表达式,可以作为参数直接使用
作用
让代码更加精简
提高代码可读性
不需要考虑函数命名的问题
用法
# 关键字lambda表示匿名函数,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值
df.apply(lambda x: x+1)
分组后单列使用多个聚合函数
同时使用多个内置聚合函数或者numpy聚合函数,全部放入一个Python列表中, 然后把整个列表传入agg或aggregate中;返回以分组列作为索引,每一个聚合计算结果作为列的全新df
df.groupby('列名1').列名2.agg(['max', 'min'])
# max, min 为pandas内置的聚合函数名
# 也可以传入numpy的聚合函数,np.mean,np.max
df.groupby('列名1').列名2.agg(['max',np.mean])
分组后对多列分别使用聚合函数
agg
函数中可以传入字典,字典的key是df的列名,与key对应的value是pandas内置的聚合计算函数、其名称的字符串;;返回以分组列作为索引,每一个聚合计算结果作为列的全新df
df.groupby('列名1').agg({
'列名2':'mean',
'列名3':'sum',
'列名4':np.max
})
分组后使用自定义聚合函数
def my_mean(values, args1, args2):
'''自定义的计算平均值函数
values是固定参数,为传入的series对象
'''
args1+args2
n = len(values) # 获取数据条目数
sum = 0
for value in values:
sum += value
return(sum/n)
# 调用自定义函数
df.groupby('列名1')['列名2'].agg(my_mean, args1=xxx, args2=xxx)
transform 转换,需要把DataFrame中的值传递给一个函数, 而后由该函数"转换"数据。
aggregate(聚合) 返回单个聚合值,分组列有多少组就返回多少条数据,但transform 不会减少数据量
分组后使用内置函数
根据列名1分组后,对列名2的值做聚合计算,返回series对象
df.groupby('列名1')['列名2'].transform('sum')
# sum是pandas内置聚合函数的函数名,求和
分组后使用自定义函数
# 自定义一个计算函数
def foo(x, y):
return x + y
df.groupby('列名1')['列名2'].transform(foo, y=3)
groupby方法后接filter方法,filter传入一个返回布尔值的匿名函数,该函数的入参就是groupby分组之后的每一组数据,返回False的数据会被过滤掉;最终返回新的df数据
df.groupby('列名').filter(匿名函数)
查看groupby分组后每一个分组对象中数据所在的行
df.groupby('列名').groups
# 返回字典 {'Female': [198, 124, 101], 'Male': [24, 6, 153, 211, 176, 192, 9]}
# 那么df分组列中一定只有Female和Male两种数据
# 字典中的key是原df中分组列的类别值;value是一个python列表,里面的数字是是原始数据df的行索引值
根据分组列的列值获取分组对象并返回df
df.groupby('列名').get_group('分组列中的某个类别值')
分组对象只能遍历不能用下标获取具体的分组
for group in df.groupby('列名'):
print(group)
### 以下代码是错误的!!!
df.groupby('列名')[0]
数据透视表就是基于原数据表、按照一定规则呈现汇总数据;和excel的透视表在数据呈现上功能相同
df.pivot_table(
index='列名1', # 分组的列名作为行索引
columns='列名2', # 分组的列名作为列索引
values='列名3', # 要聚合的列
aggfunc='内置聚合函数名', # 聚合函数
margins=True # 默认是False, 如果为True,就在最后一行和最后一列,按行按列分别执行aggfunc参数规定的聚合函数
)
pd.pivot_table(
data, # 要透视操作的df
index='列名1', # 分组的列名作为行索引
columns='列名2', # 分组的列名作为列索引
values='列名3', # 要聚合的列
aggfunc='内置聚合函数名', # 聚合函数
margins=True # 默认是False, 如果为True,就在最后一行和最后一列,按行按列分别执行aggfunc参数规定的聚合函数
)
使用说明:以列名1作为索引,根据列名2进行分组,对列名3使用pandas内置的聚合函数进行计算,返回新的df对象
参数说明:
index:返回df的行索引,并依据其做分组;传入原始数据的列名
columns:返回df的列索引;传入原始数据的列名,根据该列做分组
values: 要做聚合操作的原始数据的列名
aggfunc:内置聚合函数名字符串
pd.to_datetime()
df['列名'] = pd.to_datetime(df['列名'])
加载数据时通过时parse_dates=[列下标/列名]
pd.read_csv('path', parse_dates=[列下标/'列名'])
时间戳类型对象提取日期中的各个部分
d = pd.to_datetime('2020-06-20')
# d ==> Timestamp('2020-06-20 00:00:00')
d.year
d.month
d.day
d.hour
d.minute
d.second
d.quarter # 季度
d.weekday() # 星期几 0是星期一 1是星期二 6是星期日
df列中的时间日期对象提取日期中的各个部分
df['Date'].dt.year
df['Date'].dt.month
df['Date'].dt.day
df['Date'].dt.hour
df['Date'].dt.minute
df['Date'].dt.second
df['Date'].dt.quarter # 季度
直接调用聚合函数
df['Date'].min()
直接进行时间差运算
df['Date'] - df['Date'].min()
# 结果是timedelta类型
时间序列数据,连续的按照一定时间距离或时间点记录的数据
将datetime时间类型的列设为df的索引就变为DatetimeIndex类型此时可以分别按年月日取出数据
# 设定时间类型列为索引列
df.index = df['Date']
# 此时索引列值的样子是 2015-08-19
df['2016'] # 按年取子集df
df['2016-06'] # 按年月取子集df:所有符合条件的完整行数据
####注意!!!以上是日期时间类型行索引为连续的日期获取数据操作####
#当日期时间类型行索引不是连续的时候通过以下方法获取数据,df.loc[]
df.loc['2016']
df.loc['2016-06']
将timedelta时间差类型的列设为df的索引就变为TimedeltaIndex类型此时可以按范围取出数据
# 设定timedelta时间差类型为索引列
df.index = df['Date'] - df['Date'].min()
# 此时索引列值的样子是
#0 days
#1 days
#2 days
#...
# 此时可以用范围取子集df:所有符合条件的完整行数据
tesla['0 days':'4 days']
生成连续的DatetimeIndex类型数据
pd.date_range(start='2014-12-31', end='2015-01-05', freq='D')
# 参数freq='D'表示连续的日期
# 参数freq='B'表示只返回工作日
#### 返回如下 ####
###########################################
DatetimeIndex(
[
'2014-12-31',
'2015-01-01',
'2015-01-02',
'2015-01-03',
'2015-01-04',
'2015-01-05'
],
dtype='datetime64[ns]',
freq='D' # freq参数详见最后 时间周期参数
)
###########################################
用连续的DatetimeIndex类型数据重建不连续的DatetimeIndex索引
df.index = df['Date']
date_range = pd.date_range(start='2014-12-31', end='2015-01-05', freq='D')
df = df.reindex(date_range)
对DatetimeIndex作为索引的df可以按起止时间查询完整数据行
# between_time函数,include_end=False表示不包含结束时间,默认为True,同样也有include_start参数
df.between_time('4:00', '5:00', include_end=False)
对DatetimeIndex作为索引的df可以按时间点采样返回完整数据行
df.at_time('5:47')
将某列设置为df的行索引
df = df.set_index('列名')
# df.reset_index() 重置索引
对行索引进行排序操作
df = df.sort_index()
# 默认升序,ascending=False降序
按时间序列分组统计
df.resample('W').size() # 按每周统计数据的总数
df.resample('Q')['列名1', '列名2'].sum() # 按每季度对指定列进行聚合统计