资讯类app用户热度及资讯类型分析
一、背景分析
随着今日头条的崛起,资讯类app已经成为各巨头争夺流量入口的又一主要阵地。除了百度和阿里文娱旗下UC浏览器分别推出了百度百家和UC头条,腾讯也在发布天天快报后投资了趣头条,而趣头条也在18年9月赴美上市。这些资讯类app有一个共同的特点,他们都是基于机器学习和数据挖掘的资讯推荐类产品,为用户推荐其感兴趣的信息。
通过百度指数搜索“今日头条”、“百度百家”、“趣头条”以及“UC头条”四个关键字,我们可以发现今日头条搜索指数和搜索趋势均远高于其他几款产品,说明其产品热度一直较高,受到了更多人的关注。
趣头条近期的发展速度也很迅猛,尤其是9月15日前后,其热度超过了今日头条,可能是因为上市新闻引起了大家的关注。相对而言,百度百家和UC头条则一直不温不火,热度一直不高。
二、问题定位
结合以上背景,本文主要对以下几个问题进行探索。
不同app之间横向对比:
- 比较不同app的用户热度,即活跃度(从阅读量、评论量、资讯量及作者量等角度分析)
- 不同app的资讯类型特点(比较阅读量最高、资讯量最多的资讯类型都是哪些,不同app中主要资讯内容是否有差异)
以今日头条为例,不同时段用户热度的纵向对比:
- 不同周期的用户热度趋势特点(一周中,不同周期用户浏览行为和作者产出特点的差异)
- 不同时刻的用户热度趋势特点(一天中,不同时刻用户浏览行为和作者产出特点的差异)
目的:分析不同app用户浏览以及内容创作者产出特点,为用户浏览资讯以及创作者发布资讯提供建议。
三、获取数据
本文数据抓#取于“自媒咖”,抓#取方式是利用某数据抓#取工具在每晚十二点后开始抓#取前两天的数据。最终抓#取了四款app的文章相关信息,其资讯发布时间范围在2018-10-14至2018-10-21日之间。
本文数据分析所使用到的工具:
- Mysql(数据前期的预处理:如数据整合、去重等操作)
- Python (数据进一步清洗及分析:如描述性统计以及可视化等)
因为考虑到如果数据抓#取时间距离资讯发布时间太近时,资讯的阅读量等信息会和较早发布的资讯产生较明显差异。因此为尽量避免这种因素的影响,我们选取数据抓#取时间与资讯发布时间相隔一天以上的数据进行分析,例如在10月22日凌晨抓#取前两日(20日和21日)的数据,取20日的数据进行分析。
四、数据描述
数据量:本文通过Mysql进行数据预处理,最终筛选出122119条数据,并利用Python进行数据处理和分析。
字段释义:
五、数据清洗
因篇幅限制,(5.1部分)Mysql中数据处理过程请详见另一篇,本文主要介绍Python进行数据分析部分
5.1 利用Mysql进行数据前期处理
5.1.1 先将抓#取的数据导入到Mysql
5.1.2 数据预处理
如将时间数据转为时间类型、提取资讯类型等,并创建表存储数据
5.1.3 利用时间间隔选取数据,并整合数据
- 选取数据抓#取时间与资讯发布时间相隔一天以上的数据(因每日采集数据量有限,而趣头条数据量较大,故只能抓#取采集时间前一天的部分数据。所以我们仅利用其数据分析趣头条资讯的类型特点,但在进行app数据横向对比时将剔除这部分数据);
- 重复数据处理;
- 导出为csv文件。
5.2 利用Python进行数据清洗
5.2.1导入相关包及数据
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
filedf=pd.read_csv('Python数据/appdata.csv')
5.2.2查看数据
#查看前三项
filedf.head(3)
#查看数据量及数据格式
filedf.info()
>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 122119 entries, 0 to 122118
Data columns (total 8 columns):
pub_date 122119 non-null object
ex_date 122119 non-null object
title 122119 non-null object
author 122119 non-null object
num_read 122119 non-null int64
num_comment 122119 non-null int64
resource 122119 non-null object
type 122119 non-null object
dtypes: int64(2), object(6)
memory usage: 7.5+ MB
- pub_date及ex_date只需要保留到天;
- 无缺失数据,说明在前期数据处理中缺失值已经被剔除。
#查看数据的描述性统计
filedf.describe()
>>>
num_read num_comment
count 1.221190e+05 122119.000000
mean 5.683611e+03 9.555647
std 4.734009e+04 263.414244
min 0.000000e+00 0.000000
25% 0.000000e+00 0.000000
50% 4.300000e+01 0.000000
75% 8.510000e+02 0.000000
max 4.261331e+06 39120.000000
- 各资讯间阅读量及评论量差异较大,大多数(约75%以上)的资讯评论量为零,阅读量不足1000;
- 阅读量和评论量均无负值,但评论量最多的资讯有近4万条评论,需注意异常值。
#查看不同app平台数据的描述性统计
filedf.groupby('resource').describe()
对各类resource的num_read和num_comment数据进行整理,剔除三个标准差外的数据,并观察其数据分布。
import numpy as np
filedf_cleanread=filedf[np.abs(filedf['num_read']-filedf['num_read'].mean())<=(3*filedf['num_read'].std())]
filedf_cleancomm=filedf[np.abs(filedf['num_comment']-filedf['num_comment'].mean())<=(3*filedf['num_comment'].std())]
sns.boxplot(x='num_read',y='resource',hue='resource',data=filedf_cleanread)
plt.title('Distribution of the num_read in different resources')
plt.show()
sns.boxplot(x='num_comment',y='resource',hue='resource',data=filedf_cleancomm)
plt.title('Distribution of the num_comment in different resources')
plt.show()
- 今日头条上资讯的阅读量和评论量均较高,UC头条次之,百度百家的评论数量数据可能采集有缺失,导致为空值。
- 资讯阅读量及评论量的数据波动较明显,说明每个平台都会存在一定的“爆文”数量,但大多数资讯的阅读和评论量不高。
5.2.3数据清洗
1.提取pub_date中的日期和时刻数据
#截取资讯发布日期
filedf['pub_day']=filedf['pub_date'].map(lambda x:x.split(' ')[0])
#截取资讯发布时段
filedf['pub_hour']=filedf['pub_date'].map(lambda x:x.split(' ')[1][0:2])
#可以先删掉pub_date和ex_date两个字段
filedf.drop(['pub_date','ex_date'],axis=1,inplace=True)
filedf.head()
2.去除重复值
print('去重之前的数据量:',filedf.shape)
filedf=filedf.drop_duplicates()
print('去重之前的数据量:',filedf.shape)
>>>
去重之前的数据量: (122119, 8)
去重之前的数据量: (106355, 8)
3.resource中数据进行替换为资讯来源平台
#先查看有哪些项
filedf['resource'].value_counts()
>>>
qu 40823
uc 30930
toutiao 26833
baidu 7769
Name: resource, dtype: int64
#构造函数,进行替换
def renamefc(x):
if x=='toutiao':
y='今日头条'
elif x=='qu':
y='趣头条'
elif x=='uc':
y='UC头条'
else:
y='百度百家'
return y
filedf['resource']=filedf['resource'].map(lambda a:renamefc(a))
filedf.head()
4.选取“今日头条”,“百度百家”,“UC头条”数据进行分析
#趣头条数据缺失较多,只采集了部分时间段的数据
filedf['pub_hour'][filedf['resource']=='趣头条'].value_counts()
>>>
22 13454
23 7640
21 7531
15 3714
16 3570
20 3498
19 840
00 576
Name: pub_hour, dtype: int64
#不同app采集到的数据量
filedf['resource'].value_counts()
>>>
趣头条 40823
UC头条 30930
今日头条 26833
百度百家 7769
Name: resource, dtype: int64
#不同app资讯量
fig=plt.figure(figsize=(14,6))
plt.subplot(1,2,1)
plt.pie(filedf['resource'].value_counts(),labels=filedf['resource'].value_counts().index,
autopct='%1.2f%%',textprops={'fontsize':'12'},explode=(0,0.1,0,0))
plt.title('The propotion of different resourses',fontsize=15)
plt.subplot(1,2,2)
filedf['resource'].value_counts().plot(kind='bar',width=0.6)
plt.xticks(fontsize=12,rotation=45)
plt.title('The data size of different resources',fontsize=15)
虽然只采集了趣头条部分时间段的数据,但是其发文量已经远超其他类别app的数据量。在本文的研究中,仅利用趣头条的数据来分析其资讯类型特点,不进行用户热度的分析。
#选取指定app数据进行下一步的分析
anaDf=filedf[filedf['resource']=='今日头条']
anaDf=anaDf.append(filedf[filedf['resource']=='百度百家'],ignore_index=True)
anaDf=anaDf.append(filedf[filedf['resource']=='UC头条'],ignore_index=True)
anaDf.info()
>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 65532 entries, 0 to 65531
Data columns (total 8 columns):
title 65532 non-null object
author 65532 non-null object
num_read 65532 non-null int64
num_comment 65532 non-null int64
resource 65532 non-null object
type 65532 non-null object
pub_day 65532 non-null object
pub_hour 65532 non-null object
dtypes: int64(2), object(6)
memory usage: 4.0+ MB
六、分析数据
根据问题,进行数据分析。
6.1不同app之间横向对比用户热度及资讯特点
6.1.1不同app的用户热度
通过分析不同app的周资讯量、周作者量、平均每篇资讯的阅读量及评论量,来对比其用户参与度的差异。
#不同app上周资讯量以及周作者量(有推送资讯的作者只记一次)
print('不同app的周资讯量:\n',anaDf[['title','resource']].groupby(['resource']).count())
print('\n去重后不同app的作者量:\n',anaDf[['author','resource']].drop_duplicates().groupby(['resource']).count())
>>>
不同app的周发文量:
title
resource
UC头条 30930
今日头条 26833
百度百家 7769
去重后不同app的作者量:
author
resource
UC头条 10532
今日头条 12842
百度百家 5205
#不同app资讯平台作者量、发文量以及平均阅读量对比
fig = plt.figure(figsize=(10,6))
ax = fig.add_subplot(1,1,1)
infoWeeklySour.plot(style='b--',alpha=0.6,ax=ax)
avgRead.plot(style='-',ax=ax,color='g')
authorWeeklySour.plot(kind='bar',width=0.4,alpha=0.6,secondary_y=True,ax=ax)
ax.set_xlabel('resource')
ax.set_ylabel('Number of info')
ax.right_ax.set_ylabel('Number of author')
ax.set_title('The sum number of infos and authors in different resource',fontsize=15)
今日头条的内容创作者更多;百度百家上创作者数量及产出效率均较低;UC头条上创作者显得更高产,平均每位创作者每周发布近三篇资讯。
#以资讯title为一篇文章的唯一ID,平均每篇资讯的阅读量及评论量
#不同app相应的总阅读量
week_avg=anaDf[['num_read','resource']].groupby(['resource']).sum()
#不同app相应的总评论量
week_avg['num_comment']=anaDf[['num_comment','resource']].groupby(['resource']).sum()
#不同app相应的总资讯量
week_avg['num_title']=anaDf[['title','resource']].drop_duplicates().groupby(['resource']).count()
#得到不同app每篇资讯的平均阅读量及评论量
week_avg['avg_read']=round(week_avg['num_read']/week_avg['num_title'],1)
week_avg['avg_comment']=round(week_avg['num_comment']/week_avg['num_title'],2)
week_avg['com_read']=round(week_avg['avg_comment']/week_avg['avg_read']*100,3)
week_avg
虽然UC头条的资讯量更多,但今日头条的平均每篇资讯的阅读量及评论量会更高。此外,在今日头条上的资讯平均每100个阅读量就会有0.2条评论,而UC头条上仅有0.08条评论,这说明今日头条上的用户参与度可能更高。
6.1.2不同app的资讯特点
不同app上资讯量(即发文量)、平均阅读量以及平均评论量较多的资讯类型。
首先计算不同app上各类资讯的周阅读量、周评论量、周发文量,以及平均每篇资讯的阅读量和评论量。
#今日头条不同类型资讯的发文量、平均阅读量、平均评论量
info_toutiaoDf=filedf[['num_read','type']][filedf['resource']=='今日头条'].groupby(['type']).sum()
info_toutiaoDf['num_comment']=filedf[['num_comment','type']][filedf['resource']=='今日头条'].groupby(['type']).sum()
info_toutiaoDf['num_title']=filedf['type'][filedf['resource']=='今日头条'].value_counts()
info_toutiaoDf['avg_read']=round(info_toutiaoDf['num_read']/info_toutiaoDf['num_title'],2)
info_toutiaoDf['avg_comment']=round(info_toutiaoDf['num_comment']/info_toutiaoDf['num_title'],2)
info_toutiaoDf.head()
用同样的方法,得到“趣头条”,“百度百家”,“UC头条”的相关指标:
可视化各app资讯平台发文量前十二资讯类型
#可视化各app资讯平台发文量前十二资讯类型
toutiaoTopDf=info_toutiaoDf.sort_values('num_title',ascending=False).head(12)
quTopDf=info_quDf.sort_values('num_title',ascending=False).head(12)
baiduTopDf=info_baiduDf.sort_values('num_title',ascending=False).head(12)
ucTopDf=info_ucDf.sort_values('num_title',ascending=False).head(12)
#toutiao
avgReadtoutiaoTop=toutiaoTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitletoutiaoTop=toutiaoTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
#qu
avgReadquTop=quTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitlequTop=quTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
#baidu
avgReadbaiduTop=baiduTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitlebaiduTop=baiduTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
#uc
avgReaducTop=ucTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitleucTop=ucTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
fig=plt.figure(figsize=(16,8))
ax1=fig.add_subplot(2,2,1)
ax2=fig.add_subplot(2,2,2)
numTitletoutiaoTop.plot(kind='bar',alpha=0.6,ax=ax1)
avgReadtoutiaoTop.plot(style='-',alpha=0.6,ax=ax1,secondary_y=True)
plt.xticks(fontsize=12)
ax1.set_xticklabels(numTitletoutiaoTop.index)
ax1.set_ylabel('Total number of titles')
ax1.set_title('Toutiao',fontsize=15)
numTitlequTop.plot(kind='bar',alpha=0.6,ax=ax2)
avgReadquTop.plot(style='-',alpha=0.6,ax=ax2,secondary_y=True)
plt.xticks(fontsize=12)
ax2.right_ax.set_ylabel('Average number of read')
ax2.set_title('QU',fontsize=15)
plt.show()
fig=plt.figure(figsize=(16,8))
ax1=fig.add_subplot(2,2,1)
ax2=fig.add_subplot(2,2,2)
numTitlebaiduTop.plot(kind='bar',alpha=0.6,ax=ax1)
avgReadbaiduTop.plot(style='-',alpha=0.6,ax=ax1,secondary_y=True)
plt.xticks(fontsize=12)
ax1.set_ylabel('Total number of titles')
ax1.set_title('Baidu',fontsize=15)
numTitleucTop.plot(kind='bar',alpha=0.6,ax=ax2)
avgReaducTop.plot(style='-',alpha=0.6,ax=ax2,secondary_y=True)
plt.xticks(fontsize=12)
ax2.right_ax.set_ylabel('Average number of read')
ax2.set_title('UC',fontsize=15)
plt.show()
对这四款app发文量Top12的资讯类型进行分析:
1. 这四款app上发文量最多的资讯类型基本都是娱乐类资讯,今日头条和趣头条娱乐类资讯明显更多,UC头条娱乐、体育、科技咨询量较多,而百度百家各类资讯的发文量较为平均。
2. 发文量较多的资讯类别,其资讯平均阅读量不一定多,而且不同资讯平台上平均阅读量最多的资讯类型也有差异。今日头条上“国际”、“体育”类资讯用户关注量较高,趣头条及百度百家分别为“热点”、“体育”类资讯较受用户关注,UC头条上则为发文量最多的“娱乐”类资讯更受用户关注。
从平均阅读量和平均评论量两个角度看:
>>>
今日头条平均阅读量前五的资讯类型:
num_title avg_read avg_comment
type
小说 11 119784.00 7.91
时政 255 83907.91 231.33
国际 789 57230.97 86.01
历史 453 42676.92 70.87
体育 922 39331.88 90.93
>>>
今日头条平均评论量前五的资讯类型:
num_title avg_read avg_comment
type
时政 255 83907.91 231.33
文化 432 13829.05 124.25
社会 1631 22107.72 121.55
体育 922 39331.88 90.93
军事 648 35566.68 88.28
与发文量的结果进行对比,平均阅读量高的资讯类型发文量不一定多。例如今日头条中的“小说”类型,可能其发文数量不多,但会引起读者反复的点击或者阅读。
对比不同app资讯平台,今日头条平均评论量Top12资讯类型的平均阅读量及平均评论量都很高,说明其用户参与度较高。
#今日头条“时政”类资讯举例
filedf[(filedf['resource']=='今日头条')&(filedf['type']=='时政')].sort_values(by='num_comment',ascending=False).head()
6.2今日头条不同时段对比用户热度
6.2.1不同周期的用户热度
通过分析不同周期发文量、周作者量、平均每篇资讯的阅读量及评论量,来对比其用户参与度的差异。
#先将发布日期转为时间类型
anaDf['date_formatted']=pd.to_datetime(anaDf['pub_day'],format='%m/%d/%Y')
#再求相应时间的周期
anaDf['weekday']=anaDf['date_formatted'].map(lambda x:x.weekday_name)
#选择今日头条的数据进一步分析
toutiaoDf=anaDf[anaDf['resource']=='今日头条']
toutiaoDf.head()
toutiaoDf.groupby(['weekday']).describe()
通过分析不同周期阅读量及评论量的描述统计性信息,我们发现周期间确实存在较大差异,但没有明显的数据异常情况。
#提取数据
toutiaoWeekDf=toutiaoDf.groupby(['weekday']).describe().reindex(['Monday','Tuesday','Wednesday','Thursday',
'Friday','Saturday','Sunday'])
weekavgreadDf=toutiaoWeekDf['num_read']['mean']
weekavgcomDf=toutiaoWeekDf['num_comment']['mean']
#作图-线性图
fig=plt.figure(figsize=(10,5))
ax=fig.add_subplot(1,1,1)
weekavgreadDf.plot(kind='bar',alpha=0.8)
weekavgcomDf.plot(style='.-',alpha=0.8,secondary_y=True)
ax.set_ylabel('The average number of reading')
ax.right_ax.set_ylabel('The average number of comment')
plt.title('The average number of reading and comment per weekday',fontsize=15)
#求不同周期发文量、作者量平均每篇资讯的阅读量及评论量
weekdayDf=toutiaoDf[['weekday','num_read']].groupby(['weekday']).sum()
weekdayDf['num_comment']=toutiaoDf[['weekday','num_comment']].groupby(['weekday']).sum()
weekdayDf['num_title']=toutiaoDf[['weekday','title']].groupby(['weekday']).count()
weekdayDf['num_author']=toutiaoDf[['weekday','author']].drop_duplicates().groupby(['weekday']).count()
weekdayDf['avg_read']=round(weekdayDf['num_read']/weekdayDf['num_title'],2)
weekdayDf['avg_comment']=round(weekdayDf['num_comment']/weekdayDf['num_title'],2)
weekdayDf['title/author']=round(weekdayDf['num_title']/weekdayDf['num_author'],2)
weekdayDf.sort_values(by='num_title',ascending=False)
- 一周中,几乎所有指标在周一达到最低值,说明周一作者发布内容或用户浏览资讯的意愿均较低;
- 资讯量一般周一、周二较低,这也和内容创作者在这两天也较少发布新资讯相符合;
- 总阅读量在周四达到峰值,但平均阅读量在周二达到峰值,这可能是因为相对于阅读量的降幅,资讯量的降幅更大;
- 总评论量以及人均评论量在周五均达到峰值,可能是因为周五临近周末,大家更愿意刷手机浏览信息并参与话题讨论。
6.2.2不同时刻的用户热度
通过分析不同时刻发文量、周作者量、平均每篇资讯的阅读量及评论量,来对比其用户参与度的差异。
#求不同时刻发文量、作者量平均每篇资讯的阅读量及评论量
hourDf=toutiaoDf[['pub_hour','num_read']].groupby(['pub_hour']).sum()
hourDf['num_comment']=toutiaoDf[['pub_hour','num_comment']].groupby(['pub_hour']).sum()
hourDf['num_title']=toutiaoDf[['pub_hour','title']].groupby(['pub_hour']).count()
hourDf['num_author']=toutiaoDf[['pub_hour','author']].drop_duplicates().groupby(['pub_hour']).count()
hourDf['avg_read']=round(hourDf['num_read']/hourDf['num_title'],2)
hourDf['avg_comment']=round(hourDf['num_comment']/hourDf['num_title'],2)
hourDf['title/author']=round(hourDf['num_title']/hourDf['num_author'],2)
hourDf.sort_values(by='avg_read',ascending=False)
#取数据
houravgreadDf=hourDf['avg_read']
houravgcomDf=hourDf['avg_comment']
#画图
fig=plt.figure(figsize=(10,5))
ax=fig.add_subplot(1,1,1)
houravgreadDf.plot(kind='bar',alpha=0.8)
houravgcomDf.plot(style='.-',alpha=0.8,secondary_y=True)
ax.set_ylabel('The average number of reading')
ax.right_ax.set_ylabel('The average number of comment')
plt.title('The average number of reading and comment per hour',fontsize=15)
- 凌晨1点至5点,发布资讯量以及作者量均较低,但是在此时段所发布资讯的平均阅读量以及评论量(尤其是凌晨2、3点)都很高,这也有可能是因为某几篇资讯的阅读量和评论量异常拉高所致(此时段文章量较少容易被异常数据影响);
- 上午10点及11点资讯发布量最高,但在晚上12点以及凌晨6点,平均每位作者发布的资讯量较高,达1.5篇/人以上;
- 剔除凌晨时段后,平均阅读量和平均评论量在下午6点和上午11点较高,说明人们在这两个时间段浏览资讯并参与话题的意愿较强。
七、得出启示
浏览者角度:
- 今日头条和UC头条上资讯类型更加多样化,更易获取除了娱乐资讯外的资讯;
- 趣头条和今日头条上内容创作者和资讯量更多,可以提供更多内容以便浏览。
创作者角度:
- 在每天早上11点或下午6点前后,浏览者浏览资讯并参与话题的意愿较强,因此如果在此时段前发布内容有利于增加自己文章的曝光量;
- 不同周期浏览者浏览意愿不同,应尽可能选择在用户浏览意愿更强的时候发布资讯,如周四、周五及周六。
八、反思&计划
可能存在的问题:
- 数据抓#取的不稳定性导致可能有部分数据缺失;
- 结果展示形式单一,应注意选择合适的方式进行数据可视化。
下阶段分析及改进方向:
- 寻找更稳定的数据抓#取工具或者数据源;
- 探索更多维度:如内容创作者所发布资讯类型的多少与其阅读量、评论量之间是否有关系?即专注于某领域的内容创作者和涉猎范围较广的内容创作者,哪个更容易获取更多的流量;以及用户生命周期管理视角下,不同类型用户留存以及转化率情况等。
ps.本文最初也有受 @李少(四声) 启发,这篇如何将数据导入Mysql也较详细。
此外附上小编的CSDN学习园地,会记录和分享一些Python和数据库学习总结,希望多多交流。