数据分析常见业务指标

以下资源来源于B站秦路老师《七周成为数据分析师》

一、好的指标应该有哪些特征?

  1. 有一个核心指标,和公司的战略目标保持一致;
  2. 好的指标应该是比率;
  3. 好的指标应该能带来显著效果;
  4. 好的指标不应该是虚荣指标(指表面看起来不错但实际上无法落地)
  5. 好的指标不应该复杂

二、常见的指标

1.市场营销指标

  • 客户/用户生命周期:潜在客户、兴趣客户、新客户、老客户、流失客户及回流客户
  • 用户价值:用户为企业贡献的价值,通过指数或四则运算对指标进行组合
    2.产品运营指标
    AARRR模型(用户获取Acquisition、用户活跃Activation、用户留存Retention、用户营收Revenue、用户传播Refer)
  • 用户获取:
    a. 渠道达到量(曝光量)
    b. 渠道转化率:用多少用户因为曝光而心动,包含CPM(按千次计费)、CPC(按点击)、CPS(按销售)、CPD(按下载)、CPT(按时间)
    c. ROI(投资回报率):利润/成本
    d. 用户下载数:app的下载量(比较难获取,如使用APP Store我们很难获取下载量,只能获取通过微信点开下载页面的人数)
    e. 日新增用户数:以提交注册为基准;
    f. 获客成本:为获取一名顾客所需要支付的费用
    g.一次会话用户数占比:(可能是机器人刷粉)指新用户下载完APP,仅打开过一次,且使用时长在2分钟内
  • 用户活跃
    a. 活跃用户占比:衡量产品健康程度
    b. 用户会话session次数:用户打开产品操作和使用直至退出产品的整个周期(5分钟内没有操作,默认会话操作结束)
    c.用户访问时长:一次会话的持续时间
    d.用户访问次数:一段时间内的用户平均产生会话次数;
  • 用户留存:一周留存率/次日留存率。根据实际业务设置时间段。
  • 用户营收
    a. 付费用户数
    b. 付费用户数占比
    c. APPU:某时间段内每位用户的平均收入
    d. ARPPU:某时间段内每位付费用户平均收入,排除了未付费的用户
    e. 客单价:每一位用户平均购买商品的金额(销售总额 / 顾客总数),和ARPPU的区别在于没有时间段的限制
    f. LTV:用户生命周期价值,适用于用户生命周期短频快的业务,如游戏。经验计算公式:LTV=APPU*1/流失率。
  • 传播
    a. k因子,指一个用户能够带来几个新用户
    b. 用户分享率,分享分数占总浏览人数的比例
    3.用户行为指标
  • 功能使用:功能使用率,使用某些功能的用户数占总用户数的比例;
  • 用户会话:用户打开产品操作和使用直至退出产品的一次操作,一般在网页端超过30分钟啥也不干视作一次新的会话。
    4.电子商务指标
  • 笔单价:营业额/订单数
  • 件单价:平均每件商品的价格

三、数据分析Python实战

1.数据源
购买CD的用户id、日期、数量和金额。

image.png

2.数据预处理

  • 使用df.describe()函数查看数据相关的统计量:平均数、总数、标准差、分位数等


    image.png

    可得到订单总数、订单总金额、平均订单数、平均订单金额

  • 日期格式转换。将order_dt转换为datetime格式。 并增加一列'month'留待后续处理。
df['order_dt'] = pd.to_datetime(df.order_dt, format='%Y%m%d')
df['month'] = df.order_dt.astype('datetime64[M]')

image.png

3.整体数据特征分析

  • 按月进行分组查询每月的销售量。与sql中的group by相似,df数据类型也有groupby函数。同样按照先分组再聚合的思路来分析。
grouped_month = df.groupby(by = ['month'])
order_month_amount = grouped_month.order_amount.sum()
# 使用可视化
import matplotlib.pyplot as plt
# 可视化在网页上,而不用使用plt.show()
%matplotlib inline
# 更改作图风格
plt.style.use('ggplot')
order_month_amount.plot()
image.png
  • 同理查看每月的订单数和销售总数量,以及每月份不同的购买人数
# 求每个月的订单数
grouped_month.user_id.count().plot()
# 求每个月的订单数
grouped_month.user_id.count().plot()
# 求每月份的不同购买人数(要对分组后的序列进行去重)
grouped_month.user_id.apply(lambda x: len(x.drop_duplicates())).plot()

重点学会apply函数的使用,以及去重函数

  • 使用数据透视表完成上述操作
# 练习使用数据透视表完成上述操作
df.pivot_table(index='month',values=['order_products','order_amount','user_id'],
                          aggfunc = {'order_products':'sum',
                                   'order_amount':'sum',
                                   'user_id':'count'}).head()
image.png

数据透视表的index和column可以理解为groupby的字段,按照两个字段进行分组,values表示聚合的值

  • 求每个用户的平均消费次数
    思路:总订单数/不同的消费人数
distinct_user = grouped_month.user_id.apply(lambda x: len(x.drop_duplicates()))
avg_order_quantity = grouped_month.user_id.count()/distinct_user
avg_order_quantity.plot()

image.png

3.用户个体消费分析

  • 按照用户个体进行分组
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
image.png
  • 看消费金额和消费数量的关系
grouped_user.sum().plot.scatter(x='order_amount',y = 'order_products')
image.png

将一些比较离群点进行剔除,使用query函数(相当于sql中的where)

grouped_user.sum().query('order_amount < 4000').plot.scatter(x='order_amount',y = 'order_products')
image.png
  • 查看订单数量
# 使用切比雪夫定理:95%的数据分布在平均数5个标准差内,求出上界。如order_products: 92
grouped_user.sum().query('order_products < 92').order_products.hist(bins=40)
image.png
  • 查看一个累计值
grouped_user.sum().sort_values('order_amount').apply(lambda x: x.cumsum()/ x.sum())

image.png

4.用户行为分析

  • 查看用户第一次/最后一次购买日期
# 查看用户第一次购买的日期
grouped_user.min().order_dt.value_counts().plot()
image.png
grouped_user.max().order_dt.value_counts().plot()
image.png

第一次购买和最后一次购买都集中在前三个月,说明很多用户都是一次性消费

  • 查看一次性消费的人数
user_life = grouped_user.order_dt.agg(['min','max'])
user_life.head()
(user_life['min']==user_life['max']).value_counts()

返回结果:
True 12054
False 11516
dtype: int64

  • RFM模型,使用数据透视表
rfm = df.pivot_table(index='user_id', values=['order_products','order_amount','order_dt'], 
                     aggfunc={'order_products':'sum',
                         'order_amount':'sum',
                         'order_dt':'max'})
# 将order_dt转换为距最后一天的天数
# 将order_amount和order_products重命名, 并除以一个时间单位转换为数字
rfm['R'] = (rfm.order_dt.max() - rfm.order_dt) / np.timedelta64(1,'D')
#重命名另外两列
rfm.rename(columns={'order_amount':'F', 'order_products': 'M'}, inplace=True)
rfm.head()
# 根据是否大于平均值将数据分为八个维度
user = rfm[['R','F','M']].apply(lambda x : x-x.mean())
# 为每个维度贴上标签,即增加'label'列
def rfm_func(x):
    level = x.apply(lambda x: '1' if x>0 else '0')
    label =level.R + level.M + level.F
    d ={
        '111':'重要价值顾客',
        '011':'重要保持顾客',
        '101':'一般价值顾客',
        '001':'一般保持顾客',
        '110':'重要挽留顾客',
        '010':'重要发展顾客',
        '100':'一般挽留顾客',
        '000':'一般发展顾客'
    }
    return d[label]
rfm['label']=rfm[['R','F','M']].apply(lambda x : x-x.mean()).apply(rfm_func,axis=1)

返回结果:


image.png
rfm.groupby('label').count() #查看各种类型的人数
image.png
  • 按照活跃度进行分层
    新用户:从来没有注册过
    活跃用户:上个月活跃,这个月依旧活跃
    回流用户:上个月不活跃,这个月重新消费
    不活跃用户:上个月不活跃、这个月依旧不消费
pivoted_counts = df.pivot_table(index='user_id',
                               columns='month',
                               values='order_dt',
                               aggfunc='count').fillna(0)
pivoted_counts = df.pivot_table(index='user_id',
                               columns='month',
                               values='order_dt',
                               aggfunc='count').fillna(0)
# 根据新老用户的定义划分用户等级
def active_status(data):
    status = []
    for i in range(18):
        
        # 若本月没有消费
        if data[i] == 0:
            if len(status)>0:
                if status[i-1] == 'unreg':
                    status.append( 'unreg')
                else:
                    status.append('unactive')
            else:
                status.append('unreg')
        
        #若本月消费
        if data[i] == 1:
            if len(status) > 0:
                if status[i-1] == 'unreg':
                    status.append('new')
                if status[i-1] == 'unactive':
                    status.append('return')
                else:
                    status.append('avtive')
            else:
                status.append('new')
                
    data = data.astype(object)
    for i in range(18):
        data[i] = status[i]
#     print(data.index)
#     new_data = pd.Series(status, index=list(data.index))
    return data
# 对每一行应用上述的函数,注意axis=1表示对行处理
purchase_status = df_purchase.apply(active_status, axis=1)
purchase_status.head()

结果


image.png
# 查看每个月各个状态的人数
purchase_status_ct = purchase_status.replace('unreg', np.NaN).apply(lambda x : x.value_counts())
purchase_status_ct
  • 用户购买间隔时间统计-shift()函数
# 对每一个用户,查看其两次购买日期的差值
order_diff = grouped_user.apply(lambda x: x.order_dt - x.order_dt.shift())
order_diff.head(10)
image.png
# 作出直方图
(order_diff / np.timedelta64(1,'D')).hist(bins=20)
image.png
  • 用户生命周期统计
# 查看用户的生命周期
(user_life['max'] - user_life['min'])
# 上述结果得到的是timedelta类型,有单位,将其转换为数字
((user_life['max'] - user_life['min'])/ np.timedelta64(1,'D')).hist(bins=40)
image.png
u_l = (user_life['max'] - user_life['min'])/ np.timedelta64(1,'D')
u_l[u_l>0].hist(bins=40)
image.png
  • 复购率与回购率
    复购率:自然月内购买多次的用户占比
    回购率:曾经购买过的用户在某一时期内再次购买的占比
pivoted_counts.head()
image.png
# 复购率,即消费次数大于1的,将这部分筛选出来,其余设置为0.
# 注意,如果没有消费过,则不能将其加入复购率分母的求和
# 注意apply和applymap函数的区别 applymap函数对所有的数据操作,apply依次对某一行或者某一列数据操作。
purchase_r = pivoted_counts.applymap(lambda x : 1 if x >1  else np.NaN if x==0 else 0)
# 作图
(purchase_r.sum()/purchase_r.count()).plot()
image.png
def status_fun(data):
    status = []
    for i in range(17):
        if data[i] > 0:
            if data[i+1]>0:
                status.append(1)
            else:
                status.append(0)
        else:
            status.append(np.NaN)
    
    data.astype(object)
    for i in range(17):
        data[i]=status[i]
    return data

purchase_b = pivoted_counts.apply(status_fun, axis=1)
(purchase_b.sum()/purchase_b.count()).plot()
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容