【案例-数据分析】Google Play Store Apps

from kaggle:
https://www.kaggle.com/lava18/google-play-store-apps

分析思路:

0、数据准备
1、数据概览
2、种类对Rating的影响
3、定价策略
4、因素相关性分析
5、用户评价
6、总结

0、数据准备

(1)模块及数据导入

导入所需数据模块:

import pandas as pd
import numpy as np 
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
import seaborn as sns 

导入数据,并检查数据的完整性:

review=pd.read_csv(r'D:\Users\wuxiao\Desktop\数据分析\数据分析案例\google-play-store-apps\googleplaystore_user_reviews.csv')
app=pd.read_csv(r'D:\Users\wuxiao\Desktop\数据分析\数据分析案例\google-play-store-apps\googleplaystore.csv')
review.info()
print('\n')
app.info()
image.png

包含两个数据表:app和review,其中app是主要的分析来源。

(2)对app表进行处理:

app中共13个字段,每个字段10841行,分别解释为:

  • App:app名称
  • Category:app所属类别
  • Rating:评分
  • Reviews:评论数
  • Size:app内存大小
  • Installs:安装次数
  • Type:免费或收费
  • Price:价格
  • Content Rating:内容等级
  • Genres: 体裁类型(对Category的二级分类)
  • Last Updated:最近更新日期
  • Current Ver:版本号
  • Andriod Ver:安卓版本号

数据清洗:
A、去重和补数据
检查数据重复性:

app.duplicated().value_counts()
image.png

对数据进行去重(因为app是不允许重名的,而在数据里面有app重名,但个别类目有差异的情况,可以认为数据错误)、缺失值不处理(因为如果补0的话,会对数据有很大的影响,比如评分补0):

app.drop_duplicates(subset='App',inplace=True)
app.info()

B、数据转换

  • Size全部转化为以MB为单位的float
  • Installs转化为int
  • Price转化为float
  • Reviews转化为int
#Size转换
def size_transform(x):
    if 'Varies with device' in x:
        return float(str(x).replace('Varies with device', 'NaN'))
    elif 'M' in x:
        return float(str(x).replace('M',''))
    elif 'k' in x:
        return float(str(x).replace('k',''))/1000
    
app.Size=app.Size.transform(size_transform)
#Installs转换
def installs_transform(x):
    if '+' in x:
        x=x.replace('+','')
    if ',' in x:
        x=x.replace(',','')
    if 'Free' in x:
        x=0
    return int(x)
app.Installs=app.Installs.transform(installs_transform)
#Price转换
def price_transform(x):
    if '$' in x:
        x=x.replace('$','')
    if 'Everyone' in x:
        x=0
    return float(x)
app.Price=app.Price.transform(price_transform)
#Reviews转换为数值型
def reviews_transform(x):
    if 'M' in x:
        x=x.replace('M','')
        x=float(x)*1000000
    return int(x)
app.Reviews=app.Reviews.transform(reviews_transform)

C、不良数据处理
查看处理后的数据整体情况:


image.png

发现Rating中的存在19的值,将其删除:

def Rating_transform(x):
    if x>5:
        return float('NaN')
    else:return x
app.Rating=app.Rating.transform(Rating_transform)

数据处理完后变为:


image.png

(3)对review表进行处理:

review表共包含5个字段,每个字段64295行,分别解释为:

  • App:app名称
  • Translated_Review:用户评价
  • Sentiment:态度
  • Sentiment_Polarity:情绪极性
    -Sentiment_Subjectivity:情绪主观性(下面的分析不会用到这一点)

数据清洗:
A、去无效数据和去重
由于存在大量的无评论数据,认为是无效数据;同时存在大量的重复数据:

review=review.dropna()
review.drop_duplicates()

处理后数据变为:


image.png

1、数据概览

(1)数据相关性(如果之前对Rating缺失值补0,会很大程度影响实际评价)

x=app.Rating
y=app.Size
z=np.log(app.Installs[app.Installs!=0])
p=np.log10(app.Reviews[app.Reviews!=0])
t=app.Type[(app.Type=='Free') | (app.Type=='Paid')]
price=app.Price

aa=pd.DataFrame(list(zip(x,y,z,p,t,price)),columns=['Rating','Size','Installs','Reviews','Type','Price'])
pp=sns.pairplot(aa,hue='Type')

(2)不同种类app的个数占比

number=app['Category'].value_counts()#自动降序排列
plt.rcParams['figure.figsize'] = (20.0, 10.0)
plt.pie(number.values,labels=number.index,autopct='%.2f%%')
image.png
  • 从图中可以看出,排名前五的为FAMILY、GAME、TOOLS 、BUSINESS 和MEDICAL类。
    (由于种类数太多,lable发生重叠,主要有两种调整方案:
  • 针对text依次调整:https://www.jianshu.com/p/0a76c94e9db7
  • 或者采用plotly在线绘图api,绘图优化更好,好像是可以实现lable单列的)

2、种类对Rating的影响

(1)Rating整体情况

plt.figure(figsize=(20,10))
plt.hist(app.Rating,bins=50)
print('app平均得分为%.3f'%(np.mean(app.Rating)))#np.average不会忽略nan值
image.png

可知:平均得分4.173分;得分在4.0~4.5分比较集中。

(2)种类对Rating的影响

A、不同种类Rating的差异性分析

import scipy.stats as stats
#挑选占比前十的种类进行单因素方差分析
#f_oneway函数不接受nan参数
f=stats.f_oneway(app.loc[app.Category=='FAMILY','Rating'].dropna(),app.loc[app.Category=='GAME','Rating'].dropna(),app.loc[app.Category=='TOOLS','Rating'].dropna(),app.loc[app.Category=='BUSINESS','Rating'].dropna(),app.loc[app.Category=='MEDICAL','Rating'].dropna(),app.loc[app.Category=='PERSONALIZATION','Rating'].dropna(),app.loc[app.Category=='PRODUCTIVITY','Rating'].dropna(),app.loc[app.Category=='LIFESTYLE','Rating'].dropna(),app.loc[app.Category=='FINANCE','Rating'].dropna(),app.loc[app.Category=='SPORTS','Rating'].dropna())
f

显示:


image.png

由于p值是远小于给定显著性水平(好像是0.05),因此认为不同Category具有明显不同的Rating情况。

groups=app.groupby('Category').filter(lambda x:len(x)>=288).reset_index()
groups.Rating.hist(by=groups.Category,figsize=(20,20))
#只能用’方法‘来画图,因为plt.hist的函数没有by这个选项)
image.png

可以看出不同种类的Rating,有的比较集中,有的分散,总体来说,具有明显差异性。
B、表现最好的种类

fig,ax=plt.subplots(figsize=(10,10))
ax.set_title('App ratings across major categories')
plt.xticks(rotation=90)
sns.boxplot(x=groups.Category,y=groups.Rating)
image.png

(seaborn常见绘图总结:https://blog.csdn.net/qq_40195360/article/details/86605860
从箱图中可以看出:

  • 表现最好的种类是Books and Reference以及Health and Fitness,两者有50%以上的评价在4.5及以上;
  • Business、Travel and local和Tools的表现不够好,普遍评分不高,且存在个别极端低分。

3、app的Size大小对Rating的影响

#要保证x和y的个数一致
x=app[['Size','Rating']].dropna().Size
y=app[['Size','Rating']].dropna().Rating
sns.jointplot(x,y)
image.png

图示结果表明,Size越小,得高分的app越多,当Size增大时,高分app变少。但这可能是由于Size较大的样本量变少导致的,需要进一步分析:

x=list(app[['Size','Rating']].dropna().Size)
y=list(app[['Size','Rating']].dropna().Rating)

a=np.linspace(0,100,21)

#建造arrayy记录对应size段的个数和高分Rating的个数
arrayy=np.zeros((21,2))
for i in range(len(x)):
    arrayy[int(np.floor(x[i]/5)),0]+=1
    if y[i]>=4.173:  #高于平均分的认为是高分app
        arrayy[int(np.floor(x[i]/5)),1]+=1
#b来记录每个size分段的高分率
b=arrayy[:,1]/arrayy[:,0]

plt.bar(a,b,width=3)
plt.xlabel('Size/Mb')
plt.ylabel('HighRating_ratio')
image.png

从图中可以看出,Size越大,似乎高分率更高,需要对此进行进一步挖掘。
对各个种类的Size做差异性分析:

f=stats.f_oneway(app.loc[app.Category=='FAMILY','Size'].dropna(),app.loc[app.Category=='GAME','Size'].dropna(),app.loc[app.Category=='TOOLS','Size'].dropna(),app.loc[app.Category=='BUSINESS','Size'].dropna(),app.loc[app.Category=='MEDICAL','Size'].dropna(),app.loc[app.Category=='PERSONALIZATION','Size'].dropna(),app.loc[app.Category=='PRODUCTIVITY','Size'].dropna(),app.loc[app.Category=='LIFESTYLE','Size'].dropna(),app.loc[app.Category=='FINANCE','Size'].dropna(),app.loc[app.Category=='SPORTS','Size'].dropna())
image.png

落入拒绝域,说明各个种类的Size有明显区别:

fig,ax=plt.subplots(figsize=(10,10))
ax.set_title('App Size across major categories')
plt.xticks(rotation=90)
plt.ylim((0,100))
sns.boxplot(x=groups.Category,y=groups.Size)
image.png

可以看出,Game和Family类别的Size明显比其他种类大,说明该类应用因功能所需,通常需要更大的Size。
对表现最好的Books and Reference和Health and Fitness进行Size-Rating关联性分析:

aaa=app.loc[(app.Category=='BOOKS_AND_REFERENCE')|(app.Category=='HEALTH_AND_FITNESS'),['Size','Rating','Category']]
sns.scatterplot(y=aaa.Size,x=aaa.Rating,hue=aaa.Category)
image.png
  • 从图中可以看出,Size-Rating的关联性不够强,可以一定程度认为,HEALTH类的超低分段主要集中在50M以下,BOOKS的中低分段主要集中在60M以下,因此这两类app的Size不宜做得过小,但实际上当Size较小时仍有很多高得分,因此Size-Rating关联性缺乏数据支撑。

3、定价策略

(1)总体价格策略

image.png
  • 收费软件的价格一般处在0~50之间,但极少数app收费超过了300;
  • 高分收费软件的价格一般处在0~30之间。
groups=app.groupby('Category').filter(lambda x:len(x)>=330).reset_index()
plt.figure(figsize=(10,10))
sns.set(style='darkgrid')
plt.xticks(rotation=45)
sns.stripplot(x='Category',y='Price',data=groups)
image.png
  • 收费较高的种类有:FAMILY、MEDICAL
  • 个别app收费不合理(Price>200),他们是:


    image.png

(2)不同种类的定价策略

针对五大类FAMILY、GAME、TOOLS 、BUSINESS 和MEDICAL的收费app占比:

#确定双饼图所需的数据
aaa=app.loc[(app.Category=='FAMILY')|(app.Category=='GAME')|(app.Category=='TOOLS')|(app.Category=='BUSINESS')|(app.Category=='MEDICAL')]
bbb=aaa[['Type','Category']]
bbb['num']=0  #新建一列用于count,因为groupby的count函数只针对数值栏有效???
ccc=dict(bbb.groupby(['Category','Type']).count().num)

x1=[ccc[('FAMILY', 'Free')],ccc[('FAMILY', 'Paid')],ccc[('GAME', 'Free')],ccc[('GAME', 'Paid')],ccc[('TOOLS', 'Free')],ccc[('TOOLS', 'Paid')],ccc[('BUSINESS', 'Free')],ccc[('BUSINESS', 'Paid')],ccc[('MEDICAL', 'Free')],ccc[('MEDICAL', 'Paid')]]
x2=list(dict(aaa.Category.value_counts()).values())
#画图
label1=['Free','Paid']*5
label2=['FAMILY','GAME','TOOLS','BUSINESS','MEDICAL']

color=['aqua','linen','lightcoral','olive','gold']
plt.figure(figsize=(10,10))
plt.pie(x1,labels=label1,labeldistance=0.5,radius=0.8,wedgeprops=dict(linewidth=2))
plt.pie(x2,labels=label2,autopct='%3.1f%%',colors=color,labeldistance=1.1,radius=1,pctdistance=0.9,wedgeprops=dict(linewidth=3,width=0.18))

(双饼图制作参考https://www.jianshu.com/p/428daad5b3c6

image.png

  • 可以看出,绝大部分的软件仍然是免费形式。
    免费软件和收费软件的下载量差异和评分差异如何?
temp=app[(app.Type=='Paid')|(app.Type=='Free')].groupby('Type').agg({'Rating':'mean','Reviews':'sum','Size':'mean','Installs':'sum','Price':'mean'})
temp.columns=['Rating_mean','Review_sum','Size_mean','Install_sum','Price_mean']
temp
image.png
  • 收费软件整体评价比免费软件更高,说明收费软件在质量上确实更有保证;
  • 收费软件和免费软件的Size大小相差不大,甚至更小,说明收费软件更加注重“质”而不是“量”;
temp=app[(app.Type=='Paid')|(app.Type=='Free')][['Type','Category','Reviews','Installs']]
temp1=temp.groupby(['Type','Category']).Reviews.sum()/temp.groupby(['Type','Category']).Installs.sum()
temp1=temp1.to_frame()
#要对表进行透视
temp2=temp1.pivot_table(index='Category',columns='Type') 
temp2.columns=['Free', 'Paid']
#temp2=pd.DataFrame({'Free':temp_free,'Paid':temp_paid}) #boxplot只能对dataframe画图
fig,ax=plt.subplots(1,1)
ax.set_title('review_ratio',fontsize=15)
sns.boxplot(data=temp2)
image.png

上图是免费/收费软件评论量和下载量的比率,收费软件的review_ratio明显高于免费软件,说明:

  • 愿意选择收费软件的用户也有更强烈的意愿对软件提出意见和建议。

4、因素相关性分析

corrmat=app.corr()
plt.figure(figsize=(15,10))
#用热力学图来表示相关性
sns.heatmap(corrmat,annot=True,cmap=sns.diverging_palette(220, 20, as_cmap=True))
image.png

其中评论量和安装量相关系数达到0.63,可以认为两者是强相关性关系,进一步分析:

temp=app
temp=temp[temp.Reviews>0]
temp=temp[temp.Installs>0]
temp['Reviews(Log Scaled)']=np.log(temp.Reviews)
temp['Installs(Log Scaled)']=np.log(temp.Installs)
sns.lmplot(x='Reviews(Log Scaled)',y='Installs(Log Scaled)',data=temp)
#没有阴影显示置信区间是因为画不出来??
image.png

可以看出,评论数和下载安装量呈现一定的线性正相关关系,这表明:

  • 一方面,可能是用户趋于下载评论量更多的app并反过来促进app下载量增加,另一方面也有可能是用户保持一定的评论活跃度,因此随着app下载增加,评论量也会不断增加;
  • 考虑到以上因素,商家为了促进app的下载和推广,可以考虑增加评论的方式来吸引用户下载。

5、用户评价

(1)用户对不同app的态度

app_merge=pd.merge(app,review,on='App')

mergetemp=app_merge.groupby('Category').agg({'Sentiment':'value_counts'})
#数据转换
mergetemp=mergetemp/mergetemp.groupby('Category').sum()
#表透视
mergetemp=mergetemp.unstack()

mergetemp.plot(kind='bar',figsize=(10,6),stacked=True)
image.png
  • Health and Fitness 类软件表现良好,获得了超过85%的积极评价;
  • Game and Social 类软件表现较差,负面评价较多。
sns.boxplot(x='Type',y='Sentiment_Polarity',data=app_merge)
image.png
  • 免费软件的整体评价较收费软件更差,并且收获了一些极端的负值情绪极性,代表用户对某些免费软件极端不满意;
  • 收费软件收获的情绪极性好得多,表明这些用户对收费软件更加“容忍和宽容”,这与前文中分析得到的“收费软件整体评分比免费软件更高”是一致相合的。

(2)评价关键词

(采用jieba分词+wc做词云图参见:
http://www.mzh.ren/python-jieba-and-wordcloud.html
本文中是英文评论,所以没有用到jieba分词)

from wordcloud import WordCloud
wc=WordCloud(background_color="white", max_words=200, colormap="Set2")

#略过了创建停用词库进行数据清洗的环节
free = app_merge.loc[app_merge.Type=='Free']['Translated_Review_new'].apply(lambda x: '' if x=='nan' else x)
wc.generate(''.join(str(free)))
plt.figure(figsize=(10, 10))
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.show()
image.png
  • 免费软件的高频关键词:
    正面的有: good, love, best, great;
    负面的有:ads, bad, hate。
from wordcloud import WordCloud
wc=WordCloud(background_color="white", max_words=200, colormap="Set2")

#略过了创建停用词库进行数据清洗的环节
Paid = app_merge.loc[app_merge.Type=='Paid']['Translated_Review_new'].apply(lambda x: '' if x=='nan' else x)
wc.generate(''.join(str(Paid)))
plt.figure(figsize=(10, 10))
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.show()
image.png
  • 收费软件的高频关键词:
    正面的有: great, love, easy;
    负面的有:malware, problem。

  • 综上,从免费软件和收费软件的评价上来看,显然收费软件获得的评论更加积极,收费软件的用户也趋于对其提出问题建议,而不是一味的表达自己的不满意。

6、总结

  • 软件平均得分4.173分,Books and Reference以及Health and Fitness类软件表现良好,而Business、Travel and local和Tools的评分普遍不高;
  • 各类软件因其功能所需,Size大小存在差异,例如Game和Family类别的Size明显比其他种类大,而Size-Rating的关联性不够强,需要更多的数据才能探究两者关系;
  • 收费软件的价格一般处在0 ~ 50之间,而定价在0 ~ 30之间更容易获得高评价;
  • 收费软件整体评分比免费软件更高,同时用户评论也更加积极,说明收费软件在质量上确实更有保证;
  • 用户下载量和用户评论存在一定的正相关关系,为了促进app的下载和推广,可以考虑增加评论的方式来吸引用户下载。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268