在一款游戏中,数值策划会在游戏的设计阶段,规划玩家的等级成长的曲线,
包括每个等级需要的资源数,不同付费级别的玩家在每个等级的驻留时间等。
在游戏上线后,就需要去获取玩家的实际的成长曲线,和策划的设计曲线做对比,看看有没有偏差之类的。
本篇主要讲怎么去分析玩家的等级成长曲线,并绘制图形。
首先确定做这个分析一共需要的数据:
时间戳,玩家的注册时间,玩家id,各个时间戳下的游戏内等级和玩家的付费等级。
随便在日志中找个经常被触发的命令,带有时间戳和当前等级就行。
比如下面这种:
然后需要找到玩家的注册时间,得到每一行数据对于该玩家的注册时间的天数差,
再去个重就可以得到玩家的游戏天数和等级的数据了。
查询天数差的sql如下
SELECT
item.uid,
item.level,
--当前时间减去注册时间得到天数差--
datetime_diff(datetime(item.ts), datetime(reg.ts), DAY) AS day
FROM
`cmd表` AS item
LEFT JOIN
`regist表` AS reg
ON
reg.uid = item.uid
WHERE
DATE(item.ts, '+08') BETWEEN "2020-10-12" AND "2020-11-26"
GROUP BY
1, 2, 3
ORDER BY
1, 2
用pandas找到玩家每天的最大等级
#按照uid和day分组,取level的最大值的索引,再根据索引筛选数据,同时也是去除重复的数据
level = level.loc[level.groupby(by=['day','uid'])['level'].idxmax()]
使用pivot_table转换下行列
le = level.pivot_table(index='uid', columns='day', values='level')
image.png
这样就得到了玩家等级随天数的变化情况。
但是这样还有两个问题, 第一个是上图第一个框中的,在两天数据中有缺失值。
这表示玩家在这天没有触发过这条命令,如果你选的是登陆必触发的命令则说明玩家在当天没有登陆。
这种可以直接按照上一次的等级填充。
然后是第二种情况,玩家直接弃坑了,这种就不需要填充数据。
因为弃坑后他的等级是不会变的,如果纳入统计会很大的拉低整体的数据值。
所以综合下就是在玩家弃坑前的缺失值要填充,弃坑后就不填充。
那么直接从后往前遍历,找到第一个不为空的数据,就是玩家的弃坑时间,然后填充前面的值就好了。
def fill_level(row):
for i in range(45,-1,-1):
if not np.isnan(row.iloc[i]):
return row[:i+1].fillna(method='ffill')
le = le.apply(fill_level, axis=1)
现在拿到了基本的玩家等级随天数变化的数据,然后和充值表连表,找到每个玩家的充值等级。
SELECT
uid, sum(value ) as total_buy
FROM
`充值表`
where server = '28'
group by 1
拿到玩家的充值数据后,按不同金额分段,比如按照100内为小R,100-1000为中R,1000以上为大R
buy['r'] = pd.cut(buy['total_buy'], [0, 100,1000,np.inf],
right=False, labels=['R', 'SR', 'SSR'])
看下各个等级有多少人:
buy['r'].value_counts()
R 184
SR 42
SSR 9
然后把我们的等级变化表和充值表连起来
leb = pd.merge(le, buy[['uid','r']], how='left', on='uid')
连表后充值玩家的等级已经和uid对应起来的,但是未充值玩家的这一列仍是空值,所以需要填充一下。
由于上面我们分等级的时候用了pd.cut,这一列的类型变为了category,直接使用fillna是会报错的。
因为category只允许插入分类中已有的值。
具体来说,在上面的labels中有['R', 'SR', 'SSR']这三个类型,那么fillna的值只能填这三个类型。
所以需要先插入一个未充值玩家的类型,在使用fillna填充。
# 在分类中插入N类型,再使用fillna做填充
leb['r'].cat.add_categories(['N'], inplace=True)
leb['r'].fillna('N', inplace=True)
leb['r'] = leb['r'].astype('str')
leb['r'].value_counts()
N 2045
R 138
SR 31
SSR 8
然后我们把每一个等级的数据提取出来,准备绘图
leN = leb[leb['r']=='N'].copy()
leR = leb[leb['r']=='R'].copy()
leSR = leb[leb['r']=='SR'].copy()
leSSR = leb[leb['r']=='SSR'].copy()
这里使用箱线图,因为箱线图可以表示一个数据的分布,再加上平均值,可以较好的展示真实的数据。
plt.figure(figsize=(24, 9), dpi=80)
t = le.drop(['uid', 'r'], axis=1)
lm = t.mean()
# 这里索引不加1的话,折线图的X轴会和箱线图的X轴是对不上,具体我也不知道为啥。。。
lm.index += 1
lm.plot(c='g', label=' N average line')
t.boxplot(color='r')
plt.legend()
现在拿到了玩家的成长数据了,接下来把策划设计的线加上就可以了。
plt.figure(figsize=(24, 9), dpi=80)
t = le.drop(['uid', 'r'], axis=1)
lm = t.mean()
# 这里索引不加1的话,折线图的X轴会和箱线图的X轴是对不上,具体我也不知道为啥。。。
lm.index += 1
lm.plot(c='g', label=' N average line')
# 根据策划给出的等级成长规划得到的数据
N_up_line.plot(c='b', label='N design line')
t.boxplot(color='r')
# 设置标题
plt.title('all user')
# 画出线段说明,下图左上角的那个
plt.legend()
plt.show()
剩下的就是画出不同充值等级的玩家的图表了,代码都是一样的,
改下
t = le.drop(['uid', 'r'], axis=1)
就可以了。可以从图中看出,前期玩家至少有一半是没跟上策划的规划线的,那么就可以从其他方便查找一下没跟上的原因,然后采取一些手段优化玩家的成长体验。
箱线图的具体说明如上下限、异常值怎么算的,请自行百度。
-- end --
网友评论