1 apply 自定义函数
Pandas提供了很多数据处理的API,但当提供的API不能满足需求的时候,需要自己编写数据处理函数, 这个时候可以使用apply函数
1 Series的apply方法
sq = df[‘a’].apply(my_sq) 接收的函数my_sq作用在列的每一个元素上,也就是把每个元素传给my_sq函数
ex = df[‘a’].apply(my_exp,e=2),同可以通过apply接收my_exp的参数
2 DataFrame的apply方法
# 修改avg_3函数
def avg_3_apply(col):
x = col[0]
y = col[1]
z = col[2]
return (x+y+z)/3
df.apply(avg_3_apply)
这个函数作用在整个df上,但是每次接收数据时是一次性把一行或者一列传入给接收的函数,axis参数指定按照行还是列
3 apply 使用案例
接下来使用titanic数据集来介绍apply的用法
分别对数据集的行列统计缺失值以及比例
通过axis参数确定按行还是按列统计
4 向量化函数
@np.vectorize
def vec_avg_2_mod(x,y):
if(x==20):
return (np.NaN)
else:
return (x+y)/2
通过np.vectorize对函数进行装饰,这个函数就可以处理向量的数据
5 lambda函数
df.apply(lambda x: x+1)
2 分组操作
在SQL中我们经常使用 GROUP BY 将某个字段,按不同的取值进行分组, 在pandas中也有groupby函数
1 单变量分组聚合
df.groupby(‘year’).lifeExp.mean()
根据指定的字段/列中的取值,把取值相同的分到一个组,再对其他列进行聚合
Pandas内置的聚合方法
其他库的函数
如果使用其他库中的函数进行聚合,则需要使用agg和 aggregate接收聚合函数
自定义函数
如果想在聚合的时候,使用非Pandas或其他库提供的计算, 可以自定义函数,然后再aggregate中调用它
def my_mean(values):
'''计算平均值
'''
n = len(values) # 获取数据条目数
sum = 0
for value in values:
sum += value
return(sum/n)
# 调用自定义函数
df.groupby('year').lifeExp.agg(my_mean)
自定义函数可以有多个参数, 第一个参数接受来自DataFrame分组这之后的值, 其余参数可自定义
# 计算全球平均预期寿命的平均值 与分组之后的平均值做差
def my_mean_diff(values,diff_value):
'''计算平均值和diff_value之差
'''
n = len(values)
sum = 0
for value in values:
sum+=value
mean = sum/n
return(mean-diff_value)
# 计算整个数据集的平均年龄
global_mean = df.lifeExp.mean()
# 调用自定义函数 计算平均值的差值
df.groupby('year').lifeExp.agg(my_mean_diff,diff_value = global_mean)
同时传入多个函数
使用多个函数进行聚合
# 按年计算lifeExp 的非零个数,平均值和标准差
df.groupby('year').lifeExp.agg([np.count_nonzero,np.mean,np.std])
对不同列同时用不同方法进行聚合
df.groupby('year').agg({'lifeExp':['mean','median','max'],'pop':['median', 'min', 'std'],'gdpPercap':'median'})
聚合之后修改列名和索引
df.groupby('year').agg({'lifeExp':'mean','pop':'median','gdpPercap':'median'}).\
rename(columns={'lifeExp':'平均寿命','pop':'人口','gdpPercap':'人均Gdp'}).reset_index()
2 转换
transform 转换,需要把DataFrame中的值传递给一个函数, 而后由该函数"转换"数据。
使用transform分组计算z分数
# 计算z-score x - 平均值/标准差
def my_zscore(x):
return (x-x.mean())/x.std()
#按年分组 计算z-score
df.groupby('year').lifeExp.transform(my_zscore)
对df进行按照year进行分组,取出lifeExp,分别对每个分组进行计算,得到的是每个分组的zscore
transform分组填充缺失值
def fill_na_mean(x):
# 求平均
avg = x.mean()
# 填充缺失值
return(x.fillna(avg))
total_bill_group_mean = tips_10.groupby('sex').total_bill.transform(fill_na_mean)
total_bill_group_mean
transform练习
pcnt_loss = weight_loss.groupby(['Name', 'Month'])['Weight'].transform(find_perc_loss)
3 过滤
使用groupby方法还可以过滤数据,调用filter 方法,传入一个返回布尔值的函数,返回False的数据会被过滤掉
tips_filtered = tips.groupby('size').filter(lambda x: x['size'].count()>30)
###4 DataFrameGroupBy对象
# 调用groupby 创建分组对象
grouped = tips_10.groupby('sex')
如果想查看计算过的分组,可以借助groups属性实现
可以直接对分组结果进行aggregate,transform计算了
通过get_group选择分组,female = grouped.get_group(‘Female’)
遍历分组
for sex_group in grouped:
print(sex_group)
多分组
group_avg = tips_10.groupby([‘sex’,‘time’]).mean()
也可以在分组的时候通过as_index = False参数(默认是True),效果与调用reset_index()一样
3 数据透视表
1 Pandas 透视表概述
在使用Excel做数据分析时,透视表是很常用的功能,Pandas也提供了透视表功能,对应的API为pivot_table
pivot_table参数中最重要的四个参数 values,index,columns,aggfunc,下面通过案例介绍pivot_tabe的使用
2 零售会员数据分析案例
业务背景介绍,需要考虑清楚
数据分析要达成的目标
案例中用到的数据
分析会员运营的基本情况
3 会员存量、增量分析
解析注册时间得到注册年月
custom_info.loc[:,'注册年月'] = custom_info['注册时间'].apply(lambda x : x.strftime('%Y-%m'))
分组统计
month_count = custom_info.groupby('注册年月')[['会员卡号']].count()
dataframe.pivot_table()
custom_info.pivot_table(index = '注册年月',values = '会员卡号',aggfunc = 'count')
计算存量 cumsum 对某一列 做累积求和 1 1+2 1+2+3 1+2+3+4 …
可视化,需要去除第一个月数据,第一个月数据是之前所有会员数量的累积
因为增量和存量值的数量级差异大,
month_count['月增量'].plot(figsize = (20,8),color='red',secondary_y = True)
month_count['存量'].plot.bar(figsize = (20,8),color='gray',xlabel = '年月',legend = True,ylabel = '存量')
4 增量等级分布
会员增量存量不能真实反映会员运营的质量,需要对会员的增量存量数据做进一步拆解
month_degree_count =custom_info.groupby(['注册年月','会员等级'])[['会员卡号']].count()
member_rating = custom_info.pivot_table(index = '注册年月',columns='会员等级',values='会员卡号',aggfunc = 'count') 把长数据转成宽数据
pandas绘制图表
fig, ax1 = plt.subplots(figsize=(20,8),dpi=100)#构建坐标系
ax2 = ax1.twinx()#构建双胞胎坐标系
member_rating[['白银会员','黄金会员']].plot.bar(ax = ax1,rot=0,grid = True,xlabel='年月',ylabel = '白银黄金',legend=True)
member_rating[['铂金会员','钻石会员']].plot(ax = ax2,color = ['red','gray'],ylabel='铂金钻石')
ax2.legend(loc='upper left')
plt.title("会员增量等级分布",fontsize=20)
5 增量等级占比分析
#按行求和
member_rating.loc[:,'总计'] = member_rating.sum(axis = 'columns')
#计算白银和黄金会员等级占比 铂金钻石会员数量太少暂不计算
member_rating.loc[:,'白银会员占比'] = member_rating['白银会员'].div(member_rating['总计'])
member_rating.loc[:,'黄金会员占比'] = member_rating['黄金会员'].div(member_rating['总计'])
member_rating
画图 折线图
member_rating[['白银会员占比','黄金会员占比']].plot(color=['r','g'],ylabel='占比',figsize=(16,8),grid=True)
plt.title("会员等级占比分析",fontsize=20)
color 可以通过'red' 'green' 也可以是颜色符号
6 整体等级分布
思路:按照会员等级分组,计算每组的会员数量,用每组会员数量/全部会员数量
ratio = custom_info.pivot_table(index = '会员等级',values = '会员卡号',aggfunc = 'count')
画图
# autopct 显示数据标签,并指定保留小数位数
ratio.loc[['白银会员','钻石会员','黄金会员','铂金会员'],'占比'].plot.pie(figsize=(16,8),autopct='%.1f%%',fontsize=16)
7 线上线下增量分析
#按会员来源进行分组 使用groupby实现
from_data = custom_info.groupby(['注册年月','会员来源'])[['会员卡号']].count()
from_data = from_data.unstack()
from_data.columns = ['电商入口', '线下扫码']
from_data = from_data[1:]
from_data
# 透视表实现
custom_info.pivot_table(index = ['注册年月'],columns='会员来源',values ='会员卡号',aggfunc = 'count')
8 地区店均会员数量
计算每个地区,平均会员数量
使用查看门店信息表,把这个表和用户信息表合并
custom_info1 = pd.merge(custom_info,store_info[['店铺代码','地区编码']],left_on='所属店铺编码',right_on='店铺代码')
#统计不同地区的会员数量 注意只统计线下,不统计电商渠道 GBL6D01为电商
district = custom_info1[custom_info1['地区编码']!='GBL6D01'].groupby('地区编码')[['会员卡号']].count()
district['店铺数'] = custom_info1[['地区编码','所属店铺编码']].drop_duplicates().groupby('地区编码')['所属店铺编码'].count()
district.loc[:,'每店平均会员数']=round(district['会员数量'].div(district['店铺数']))
#计算总体平均数
district.loc[:,'总平均会员数']=district['会员数量'].sum()/district['店铺数'].sum()
#排序
district['每店平均会员数'].plot.bar(figsize=(20,8),color='r',legend = True,grid=True)
district['总平均会员数'].plot(figsize=(20,8),color='g',legend = True,grid=True)
9 各地区会销比
会销比 = 会员消费的金额 / 全部客户消费的金额
会销比 = 会员消费的订单数 / 全部销售订单数
通过会销比可以衡量会员的整体质量
为会员消费报表添加年月列
custom_consume.loc[:,'年月']=pd.to_datetime(custom_consume['订单日期']).apply(lambda x:datetime.strftime(x,'%Y%m')).astype(np.int)
为会员消费报表添加地区编码,目的是可以按照地区分组统计消费情况
# margins参数 每行每列求和
member_orders=custom_consume[custom_consume['地区编码']!='GBL6D01'].pivot_table(values = '消费数量',index='地区编码',columns='年月',aggfunc=sum,margins=True)
country_sales=all_orders.pivot_table(values = '全部订单数',index='地区代码',columns='年月',aggfunc=sum,margins=True)
计算各地区会销比
result=member_orders/country_sales
result.applymap(lambda x: format(x,".2%"))
applymap 和apply类似,
10 会员连带率分析
连带率是指销售的件数和交易的次数相除后的数值,反映的是顾客平均单次消费的产品件数
连带率的计算
- 连带率 = 消费数量 / 订单数量
统计订单的数量:需要对"订单号"去重,并且只要"下单"的数据,"退单"的不要
order_data=custom_consume.query(" 订单类型=='下单' & 地区编码!='GBL6D01'")
#去重 统计订单量需要去重 后面统计消费数量和消费金额不需要去重
order_count=order_data[['年月','地区编码','订单号']].drop_duplicates()
order_count=order_count.pivot_table(index = '地区编码',columns='年月',values='订单号',aggfunc='count')
统计商品数量
consume_count=order_data.pivot_table(values = '消费数量',index='地区编码',columns='年月',aggfunc=sum)
result=consume_count/order_count
#小数二位显示
result=result.applymap(lambda x:format(x,'.2f'))
result
11 会员复购率分析
复购率的概念和复购率分析的作用
复购率:指会员对该品牌产品或者服务的重复购买次数,重复购买率越多,则反应出会员对品牌的忠诚度就越高,反之则越低。
由于一个会员同一天消费多次也算一次消费,所以会员消费次数按一天一次计算 因此需要对"会员卡号"和"时间"进行去重
order_data=custom_consume.query("订单类型=='下单'")
#因为需要用到地区编号和年月 所以选择 订单日期 卡号 年月 地区编码 四个字段一起去重
order_data=order_data[['订单日期','卡号','年月','地区编码']].drop_duplicates()
consume_count = order_data.pivot_table(index =['地区编码','卡号'],values='订单日期',aggfunc='count').reset_index()
consume_count.rename(columns={'订单日期':'消费次数'},inplace=True)
consume_count
判断是否复购
统计每个地区的购买人数和复购人数
depart_data=consume_count.pivot_table(index = ['地区编码'],values=['消费次数','是否复购'],aggfunc={'消费次数':'count','是否复购':'sum'})
depart_data.loc[:,'复购率']=depart_data['复购人数']/depart_data['购买人数']
统计2018年01月~2018年12月复购率和2018年02月~2019年01月复购率