案例提要:社交游戏的销售额分析
一款叫做《黑猫拼图》的社交游戏本月的销售额相较于上月有所下滑,于是想调查下滑的原因,并提升销售额,该怎么做呢?
# 加载python所需模块
import pandas as pd
import pandasql
import numpy as np #之后需要用到
import seaborn as sns
import matplotlib as mpl #设置环境变量
import matplotlib.pyplot as plt #绘图专用
from mpl_toolkits.mplot3d import Axes3D #绘制3D图
mpl.rcParams['font.sans-serif'] = ['FangSong']
mpl.rcParams['axes.unicode_minus']=False
现状:从上图可以看出,和上月相比本月的销售额确实下降了。
预期:而无论是从市场环境还是游戏本身的状态来看,这个游戏的销售额还有继续增长的空间。也就是说,本来的预期是“希望能够确保和上月相同的销售额”。
目的:解析现状与预期差距的构造,明确现状与预期之间的差距在哪?
方法:比较本月与上月有哪些地方不同?
具体步骤:
提出假设:在最初阶段,重要的是从大局出发来考量,而不是局限于数据分析的范畴。例如,可以根据业务经验进行下述假设:
作为可能导致销售额减少的原因,上月和本月的不同之处有以下两点。
1、在商业宣传上存在问题
2、每月以不同的主题开展的游戏活动存在问题
验证假设:通过咨询市场部和游戏开发部,得到了以下信息:
1、由于预算的缘故,和上月相比,本月并没有开展那么多的商业宣传活动。
2、游戏活动的内容和上月相比几乎没有变动。
根据第 2 条的内容可知,游戏活动的内容相比之前没有大的变化,那么原因很可能是第 1 条假设——“商业宣传力度不够导致了销售额下降”。然后再根据业务知识进行下一步的假设:由于商业宣传活动减少了,因此就很难有更多的人了解公司产品,产品也就很难获得新的用户,新用户的减少导致了销售额的下降。
假设小结:
● 和上月相比,本月的销售额减少了 (事实)
● 本月的商业宣传活动相比上月减少了 (事实)
● 可能是新用户的数量的减少引起 本月的销售额减少(假设)
● 将商业宣传活动恢复到与上月相同的水平(解决方案)
为了验证假设是否正确,需要通过收集和加工数据进行分析《黑猫拼图》游戏销售额的构成。
DAU(Daily Active User,每天至少来访 1 次的用户数据)
DPU(Daily Payment User,每天至少消费 1 日元的用户数据)
Install(记录每个用户首次玩这个游戏的时间的数据)
另外,对于上述 3 部分数据,具体需要收集的内容如下所示
数据内容 | 字段英文名 | 数据类型 |
---|---|---|
访问时间 | log_data | string |
应用名称 | app_name | string |
用户 ID | user_id | int |
数据内容 | 字段英文名 | 数据类型 |
---|---|---|
消费日期 | log_data | string |
应用名称 | app_name | string |
用户 ID | user_id | int |
消费额 | Payment | int |
数据内容 | 字段英文名 | 数据类型 |
---|---|---|
首次使用日期 | log_data | string |
应用名称 | app_name | string |
用户 ID | user_id | int |
作为基本的日志数据,这些数据每天都会被累积并存储起来。
在确定了分析所需的数据对象后,下一步需要考虑的就是具体的数据收集了。在本例中,各种数据都以 csv 文件的格式保存在服务器上。为了能用分析工具 Python语言来处理这些文件,首先需要读入这些数据。
我们需要先确认从 csv 文件中读入的各种数据的内容。
原始的 DAU 数据里包含了《黑猫拼图》游戏所有用户的数据。我们以下表中第一行为例来介绍一下数据的内容。
该行表示“2013 年6 月 1 日 ID 为 116 的《黑猫拼图》游戏用户玩了这个游戏”。每天玩《黑猫拼图》游戏的全部用户的 ID 都在这份数据里。
dau = pd.read_csv("../../data/section3-dau.csv")
dau.head(5)
log_date | app_name | user_id | |
---|---|---|---|
0 | 2013-06-01 | game-01 | 116 |
1 | 2013-06-01 | game-01 | 13491 |
2 | 2013-06-01 | game-01 | 7006 |
3 | 2013-06-01 | game-01 | 117 |
4 | 2013-06-01 | game-01 | 13492 |
DPU 数据仅包含了在游戏中发生消费行为的用户及其消费金额。
以数据内容的第 1 行为例,它表示“2013 年 6 月 1 日 ID 为 351 的用户支付了 1333 日元”。
另外,正如上述数据所示,这里保存的是用户从游戏开始到结束所消费的金额,所以当同一个用户同一天多次访问游戏时,每一次的消费金额数据都会被记录下来。
dpu = pd.read_csv("../../data/section3-dpu.csv")
dpu.head(5)
log_date | app_name | user_id | payment | |
---|---|---|---|---|
0 | 2013-06-01 | game-01 | 351 | 1333 |
1 | 2013-06-01 | game-01 | 12796 | 81 |
2 | 2013-06-01 | game-01 | 364 | 571 |
3 | 2013-06-01 | game-01 | 13212 | 648 |
4 | 2013-06-01 | game-01 | 13212 | 1142 |
最后介绍的 Install 数据记录了每个用户于何年何月何日首次玩这个游戏。以上表中第 1 行的数据为例,它表示“ID 为 1 的用户在 2013 年4 月 15 日第一次玩《黑猫拼图》游戏”。
install = pd.read_csv("../../data/section3-install.csv")
install.head(5)
install_date | app_name | user_id | |
---|---|---|---|
0 | 2013-04-15 | game-01 | 1 |
1 | 2013-04-15 | game-01 | 2 |
2 | 2013-04-15 | game-01 | 3 |
3 | 2013-04-15 | game-01 | 4 |
4 | 2013-04-15 | game-01 | 5 |
现在我们收集了玩过《黑猫拼图》游戏的用户信息(DAU)、消费信息(DPU)和首次使用日期的信息(Install)。从现在开始我们要对数据进行加工,使其能够应用于数据分析。
把初始数据加工整理成可供分析的数据,这一过程称为“前期处理”。为了配合各种分析方法,我们需要将数据加工成可供这些分析方法使用的形式。每种分析方法所需的数据格式可能都不一样,这就需要我们根据所使用的分析方法来确定如何加工数据。
另外,一些数据分析方法对于噪声数据比较敏感。如果使用这类分析方法,那么还需要将噪声数据去除。在本例中,我们的目的是判断“销售额减少是否受到了新用户因素的影响”。
为了达到这个目的,我们需要对数据进行如下加工。
为了得到某一天首次玩《黑猫拼图》游戏的人数,我们需要将用户ID 作为 key,把具有相同用户 ID 的用户信息和 Install 数据结合起来。
dau_install = dau.merge(install,how='left',on=['user_id','app_name'])
dau_install.head(5)
log_date | app_name | user_id | install_date | |
---|---|---|---|---|
0 | 2013-06-01 | game-01 | 116 | 2013-04-17 |
1 | 2013-06-01 | game-01 | 13491 | 2013-06-01 |
2 | 2013-06-01 | game-01 | 7006 | 2013-05-03 |
3 | 2013-06-01 | game-01 | 117 | 2013-04-17 |
4 | 2013-06-01 | game-01 | 13492 | 2013-06-01 |
为了得到在某一天有消费行为的用户数量,把用户 ID 和消费日期作为 key,将 DAU 和 DPU 的数据结合起来。此时,由于 DPU 中未包含没有消费行为的用户数据,因此最终的数据中不仅保留了各数据相结合的记录,也保留了没有和 DPU 数据相结合的记录。
dau_dpu_install = dau_install.merge(dpu,how='left',on=['user_id','app_name','log_date'])
dau_dpu_install.head(5)
log_date | app_name | user_id | install_date | payment | |
---|---|---|---|---|---|
0 | 2013-06-01 | game-01 | 116 | 2013-04-17 | NaN |
1 | 2013-06-01 | game-01 | 13491 | 2013-06-01 | NaN |
2 | 2013-06-01 | game-01 | 7006 | 2013-05-03 | NaN |
3 | 2013-06-01 | game-01 | 117 | 2013-04-17 | NaN |
4 | 2013-06-01 | game-01 | 13492 | 2013-06-01 | NaN |
在 DAU 中,有消费行为的用户只是其中的一部分。在第 2 步中未能和 DPU 数据相结合的记录也被保存了下来,因此在消费额中出现了缺失值。由于有缺失值的存在,在计算平均值等的过程中就会出现问题。因此需要将数据中的缺失值(NA)替换为 0,以便计算平均值或总值。
dau_dpu_install = dau_dpu_install.fillna(value={'payment':0})
dau_dpu_install.head(5)
log_date | app_name | user_id | install_date | payment | |
---|---|---|---|---|---|
0 | 2013-06-01 | game-01 | 116 | 2013-04-17 | 0.0 |
1 | 2013-06-01 | game-01 | 13491 | 2013-06-01 | 0.0 |
2 | 2013-06-01 | game-01 | 7006 | 2013-05-03 | 0.0 |
3 | 2013-06-01 | game-01 | 117 | 2013-04-17 | 0.0 |
4 | 2013-06-01 | game-01 | 13492 | 2013-06-01 | 0.0 |
在本例中,为了观察上月和本月数据的差别,数据将会按照月份来统计,也就是按月统计用户信息。
#dau_dpu_install['log_month'] = pd.to_datetime(dau_dpu_install['log_date'],format="%Y-%m-%d").to_period("M")
dau_dpu_install['log_month'] = pd.to_datetime(dau_dpu_install['log_date']).map(lambda x : x.strftime("%Y-%m"))
dau_dpu_install['install_month'] = pd.to_datetime(dau_dpu_install['install_date']).map(lambda x : x.strftime("%Y-%m"))
mau_payment = dau_dpu_install.loc[:,['log_month','user_id','install_month','payment']]
mau_payment.head(5)
log_month | user_id | install_month | payment | |
---|---|---|---|---|
0 | 2013-06 | 116 | 2013-04 | 0.0 |
1 | 2013-06 | 13491 | 2013-06 | 0.0 |
2 | 2013-06 | 7006 | 2013-05 | 0.0 |
3 | 2013-06 | 117 | 2013-04 | 0.0 |
4 | 2013-06 | 13492 | 2013-06 | 0.0 |
为了确认新用户的数量是否减少了,我们可以比较某个用户的首次使用月份和访问月份是否相同,如果相同则是新用户,否则便是已有用户。
case_when = lambda x: '新用户' if (x['log_month'] == x['install_month']) else '已有用户'
mau_payment['user_type'] = mau_payment.apply(case_when,axis=1)
mau_payment.head(5)
log_month | user_id | install_month | payment | user_type | |
---|---|---|---|---|---|
0 | 2013-06 | 116 | 2013-04 | 0.0 | 已有用户 |
1 | 2013-06 | 13491 | 2013-06 | 0.0 | 新用户 |
2 | 2013-06 | 7006 | 2013-05 | 0.0 | 已有用户 |
3 | 2013-06 | 117 | 2013-04 | 0.0 | 已有用户 |
4 | 2013-06 | 13492 | 2013-06 | 0.0 | 新用户 |
上月(2013 年 6 月)和本月(2013 年 7 月)的已有用户和新用户的消费额数据统计如下表所示。
# 计算每月已有用户和新用户的销售额
user_type_payment = mau_payment.groupby(['log_month','user_type']).sum().loc[:,['payment']].reset_index()
user_type_payment = user_type_payment.pivot_table(index='log_month',columns='user_type',values='payment').reset_index()
user_type_payment
user_type | log_month | 已有用户 | 新用户 |
---|---|---|---|
0 | 2013-06 | 177886.0 | 49837.0 |
1 | 2013-07 | 177886.0 | 29199.0 |
现在我们已经完成了数据加工,并将数据整理成适合数据分析的状态了。通常有经验的分析师会反复输出、确认整理好的数据,在观察各种数据的过程中找出问题的原因。本书为了让读者更容易理解,将数据转换为数据图后再做分析。柱状图是一种有助于有效把握数据内容的工具,现在我们就用它来将数据可视化。
fontsize = 20
font = {'color': 'yellow',
'size': fontsize
}
# 生成数据
plt.style.use("tableau-colorblind10")
plt.figure(figsize=(12, 9))
x = user_type_payment['log_month']
y1 = user_type_payment.loc[:, '已有用户']
y2 = user_type_payment.loc[:, '新用户']
# 堆积柱状图
plt.bar(x, y1, label='已有用户')
plt.bar(x, y2, bottom=y1, label='新用户')
# 添加图例
plt.legend(loc='best', fontsize=fontsize)
plt.title("《黑猫拼图》游戏的销售额比较(上月 / 本月)", fontsize=fontsize)
plt.xlabel("月份", fontsize=fontsize)
plt.ylabel("销售额", fontsize=fontsize)
plt.xticks(fontsize=fontsize-5)
plt.yticks(fontsize=fontsize-5)
# 设置标签
for x, y1, y2 in zip(range(len(x)), y1, y2):
plt.text(x, y1/2, '%.0f' % y1, ha='center', va='center', fontdict=font)
plt.text(x, y1+(y2/2), '%.0f' %
y2, ha='center', va='center', fontdict=font)
plt.show()
上图中左边的柱子表示的是上月的销售额,右边的柱子表示的是本月的销售额。已有用户分类到“existing”的类别下,而新用户分到“install”的类别下。从图中来看,已有用户带来的销售额几乎没有变化,而新用户带来的销售额却下降了,由此导致本月销售额整体下降。也就是说,我们在初步分析中得到的结果很顺利地验证了之前提出的假设。
下面我们来具体看一下哪个消费层次的消费额减少了。这里我们来做一个只有新用户数据的柱状图,将新用户上月和本月的支付情况可视化。在下图中,横轴表示该月的总计消费额,一个柱子的宽度代表 1000日元,纵轴表示该消费额相应的用户数。
# 取有消费的新用户的数据出来
new_mau_payment = mau_payment.loc[(
mau_payment['user_type'] == '新用户') & (mau_payment['payment'] > 0), :]
# 统计各个新用户每月的消费金额
new_mau_payment = new_mau_payment.groupby(
['log_month', 'user_id']).sum().loc[:, ['payment']].reset_index()
# 根据用户每月的消费金额进行分桶统计数量
new_mau_payment['payment_period'] = pd.cut(new_mau_payment['payment'], range(
0, int(max(new_mau_payment['payment']))+2000, 2000), right=False)
payment_count = new_mau_payment.groupby(
['payment_period', 'log_month']).count().loc[:, ['user_id']].reset_index()
payment_count = payment_count.rename(columns={'user_id': 'user_count'})
payment_count['payment_period'] = payment_count['payment_period'].astype(str)
payment_count = payment_count.fillna(0.0)
# 开始画折线图
payment_countfontsize = 20
font = {'color': 'black',
'size': fontsize
}
# 生成数据
plt.style.use("tableau-colorblind10")
plt.figure(figsize=(20, 9))
x_labels = payment_count['payment_period'].unique()
y1 = payment_count.loc[payment_count['log_month'] == '2013-06', 'user_count']
y2 = payment_count.loc[payment_count['log_month'] == '2013-07', 'user_count']
# 堆积柱状图
x = np.arange(len(x_labels))
bar_width = 0.35 # 设置柱状图的宽度
plt.bar(x, y1, bar_width, label='2017-06')
plt.bar(x+bar_width, y2, bar_width, label='2017-07')
# 添加图例
plt.legend(loc='upper right', fontsize=fontsize,
title='log_moth', title_fontsize=fontsize)
plt.title("《黑猫拼图》游戏中新用户的消费额比较(上月 / 本月)", fontsize=fontsize)
plt.xlabel("payment (JPY)", fontsize=fontsize)
plt.ylabel("count", fontsize=fontsize)
plt.xticks(x, x_labels, fontsize=fontsize-5)
plt.yticks(fontsize=fontsize-5)
# 设置标签
for x, y1, y2 in zip(x, y1, y2):
plt.text(x, y1, '%.0f' % y1, ha='center', va='bottom', fontdict=font)
plt.text(x+bar_width, y2, '%.0f' %
y2, ha='center', va='bottom', fontdict=font)
#设置备注
plt.text(1.5,38,'消费2000日元以下\n的用户数量减少了', ha='center', va='bottom', fontdict=font)
plt.show()
在上图中,数据以柱状图的形式表示了出来。我们可以看出,和上月(2013 年 6 月)相比,本月(2013 年 7 月)消费额在 2000 日元以下的用户数量减少了。
商业数据中经常会出现如上图所示的幂律分布。根据笔者以往的经验,凡是跟人们的心理有关系的数据,比如某类商品的销售状况、旅游景点的人气指数,或者某个时间段关键词的搜索次数等,大多都具有类似上图的分布形状。
下面我们来讨论一下这种数据形状出现的背景。它反映了一种常见的竞争心理:想超越大多数人往上升,只需稍微付出一些努力即可,但想升到最高处,还只是付出和之前一样的努力是很难实现的。实际上与人们心理相关的数据都会表现出这样的分布特性。很多学者根据商业数据中经常出现的这种分布形状提出了各种各样的研究课题和规律,比如,人们发现八成的销售额来源于二成的商品,这一规律被称为“二八
法则”或者“长尾效应”。
在社交游戏中,用户可以用金钱来购买劳动。由于大部分用户消费得很少,所以你只需要花不多的钱就可以让自己的排名大幅上升。然而,如果你想占据排行榜的顶端,那么所要花费的金额马上就会上涨。这也是人们的竞争心理结构在数据分布上的表现。
我们先来回顾一下数据分析之前设立的假说。
1.《黑猫拼图》游戏的销售额相比上月减少了 (事实)
2. 通过观察销售额的构成,发现新用户带来的销售额减少了 (假设)
3. 将商业宣传活动恢复到与上月相同的水平 (解决方案)
而且,根据此前数据分析的结果,我们可以知道:
1.《黑猫拼图》游戏的销售额和上月相比减少了 (事实)
2. 通过观察销售额数据的构成,发现新用户带来的销售额减少了, 其中消费额在 2000 日元以下的轻度消费用户的人数减少所造成的影响最大 (事实)
3. 将商业宣传活动恢复到与上月相同的水平 (确信度较高的解决方案)
基于上述结果,我们可以采取下面的解决对策来提升销售额。
新用户中的消费用户数量减少了,特别是消费金额较少的小额消费用户数量减少了。因此,公司需要再次开展商业宣传活动并恢复到之前的水平,这样才有可能提升潜在用户对公司产品的认知度,增加新的用户。这样一来,才会增加小额消费用户的数量,将销售额恢复到与上月相同的水平。顺便说一下,在实际再次开展商业宣传活动时,需要判断商业宣传活动的花费能否和新用户的顾客终身价值(Life Time Value,LTV)相当。在本案例中,通过比较用其他方法计算出来的 LTV 和获取用户的成本,决定再次开展商业宣传活动。
本章主要介绍了如何使用柱状图来做数据分析。和上月相比,本月销售额下降了,我们将其作为问题,探讨了问题出现的原因。在商业数据分析中,很重要的一点就是在数据分析之前,尽可能地多听取相关部门的意见,充分了解事实。在此基础上,再和相关负责人共同讨论可能的原因,并用数据进行验证。
分析流程 | 第 3 章中数据分析的成本 |
---|---|
现状和预期 | 中 |
发现问题 | 低 |
数据的收集和加工 | 低 |
数据分析 | 中 |
解决对策 | 低 |
本文文字内容主要来源于书籍:《数据分析实战》 [日] 酒卷隆志 里洋平/著 肖峰/译
本文代码是自己手打的
本文github地址:https://github.com/qq1044645270/data_analysis