数据分析上一节已经使用ItemCF构建了baseline,得到了一个结果。如果我们需要在基线的基础上进一步改进,就需要进一步分析数据。导入库importpandasaspdimportnumpyasnpimportmatplotlib.pyplotaspltimportseabornassnsplt.rc('font',family='SimHei',size=13)importosimportreimportwarningsimportsys%matplotlibinline#donotprintignorelevelmessageswarnings.filterwarnings("ignore")读取数据data_dir='./data'#训练数据train_user_click_df=pd.read_csv(data_dir+'/train_click_log.csv')item_df=pd.read_csv(data_dir+'/articles.csv')item_df=item_df.rename(columns={'article_id':'click_article_id'})#重命名方便后续matchitem_emb_df=pd.read_csv(data_dir+'articles_emb.csv')#测试数据test_user_click_df=pd.read_csv(data_dir+'testA_click_log.csv')数据预处理对每个用户的点击时间戳进行降序排序得到rank'rank']=test_user_click_df.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)计算用户点击文章的次数,新增一列counttrain_user_click_df['click_cnts']=train_user_click_df.grouppby(['user_id'])['click_timestamp'].transform('count')test_user_click_df['click_cnts']=test_user_click_df.groupby(['user_id'])['click_timestamp'].transform('count')数据浏览用户点击日志files_trainingsettrain_user_click_df=trn_click.merge(item_df,how='left',on=['click_article_id'])train_user_click_df.head()train_click_log.csv文件数据各字段含义1.user_id:唯一标识用户2.click_article_id:用户点击文章的唯一标识3.click_timestamp:用户点击文章的时间戳4.click_environment:用户点击文章的环境5.click_deviceGroup:用户所在的设备组6.click_os:用户点击文章的操作系统7.click_country:用户点击文章的国家8.click_region:用户点击文章的地区9.click_referrer_type:文章来源当用户点击文章时#Userclicklog信息train_user_click_df.info()train_user_click_df.describe()train_user_click_df.user_id.nunique(),train_user_click_df.groupby('user_id')['click_article_id'].count().min()200000,2看一下不同字段plt通过直方图的分布.figure()plt.figure(figsize=(15,20))i=1forcolin['click_article_id','click_timestamp','click_environment','click_deviceGroup','click_os','点击_country','click_region','click_referrer_type','rank','click_cnts']:plot_envs=plt.subplot(5,2,i)i+=1v=train_user_click_df[col].value_counts().reset_index()[:10]fig=sns.barplot(x=v['index'],y=v[col])对于fig.get_xticklabels()中的项目:item.set_rotation(90)plt.title(col)plt.tight_layout()plt.show()从点击时间clik_timestamp来看,分布比较平均,不需要特殊处理。由于时间戳是13位数字,为了方便计算,时间格式会转换为10位数字。从点击环境click_environment来看,只有1922(0.1%)点击环境为1;只有24617(2.3%)点击环境为2;其余(97.6%)点击环境为4。从点击设备组click_deviceGroup中,设备1占多数(60.4%),设备3占36%。测试集用户点击日志test_user_click_df=train_user_click_df.merge(item_df,how='left',on=['click_article_id'])tst_click.head()test_user_click_df.describe()可以看出训练集中的用户和测试集完全不同同一个训练集的用户ID范围是0到199999,而测试集A的用户ID范围是200000到249999。#测试集中用户数为5wtest_user_click_df.user_id.nunique(),test_user_click_df.groupby('user_id')['click_article_id'].count().min()50000,1新闻文章信息数据表#新闻文章数据setbrowseitem_df.head().append(item_df.tail())item_df['words_count'].value_counts()item_df.shape(364047,4)新闻文章嵌入向量表示item_emb_df.head()item_emb_df.shape(364047,251)数据分析用户重复点击#mergeuser_click_merge=train_user_click_df.append(test_user_click_df)#用户重复点击user_click_count=user_click_merge.groupby(['user_id','click_article_id'])['click_timestamp'].agg({'count'}).reset_index()user_click_count[:10]user_click_count[user_click_count['count']>7]user_click_count['count'].unique()#用户点击新闻的次数user_click_count.loc[:,'count'].value_counts()可以看出:有1,605,541(约99.2%)用户没有重复阅读文章,只有极少数用户有反复点击一篇文章。这个也可以单独做成特征用户点击环境变化分析defplot_envs(df,cols,r,c):plt.figure()plt.figure(figsize=(10,5))i=1forcolincols:plt.subplot(r,c,i)i+=1v=df[col].value_counts().reset_index()图=sns.barplot(x=v['index'],y=v[col])foriteminfig.get_xticklabels():item.set_rotation(90)plt.title(col)plt.tight_layout()plt.show()#分析用户的点击环境是否有明显变化,这里随机抽取10个用户进行点击分析这些用户的环境分布sample_user_ids=np.random.choice(test_user_click_df['user_id'].unique(),size=5,replace=False)sample_users=user_click_merge[user_click_merge['user_id'].isin(sample_user_ids)]cols=['click_environment','click_deviceGroup','click_os','click_country','click_region','click_referrer_type']for_,user_dfinsample_users.groupby('user_id'):plot_envs(user_df,cols,2,3)可以绝对可以看出大多数用户的点击环境是相对的主动固定。思路:根据这些环境的统计特征,将user_click_item_count=sorted(user_click_merge.groupby('user_id')['click_article_id'].count(),reverse=True)plt.plot(user_click_item_count)可以根据用户点击文章的次数看用户的活跃度#点击次数前50的用户plt.plot(user_click_item_count[:50])点击次数前50的用户超过100次。思路:我们可以将点击次数大于等于100的用户定义为活跃用户。这是一个简单的处理思路。判断用户活跃度,结合点击时间更全面。后面我们会根据点击次数和点击时间来判断。判断用户活跃度有两个方面。#点击次数排在[25000:50000]之间plt.plot(user_click_item_count[25000:50000])可以看出点击次数小于等于两次的用户很多,这些用户可以认为是非活跃用户新闻点击量分析item_click_count=sorted(user_click_merge.groupby('click_article_id')['user_id'].count(),reverse=True)plt.plot(item_click_count)plt.plot(item_click_count[:100])可以看到top100withthemostclicks点击次数超过1000的新闻文章plt.plot(item_click_count[:20])点击次数最多的前20条新闻,点击次数超过2500。思路:这些新闻可以定义为热点新闻,这也是一种简单的处理方式。后面我们也会按照点击次数和时间来划分文章的热度。plt.plot(item_click_count[3500:])可以发现很多新闻只被点击了一两次。思路:这些新闻可以定义为冷门新闻。新闻共现频率:两篇新闻文章连续出现的次数x.shift(-1))union_item=tmp.groupby(['click_article_id','next_item'])['click_timestamp'].agg({'count'}).reset_index().sort_values('count',ascending=False)union_item[['count']].describe()从统计数据可以看出,平均同现数为2.88,且最高的是1687,说明用户看新闻的力度比较大。#画个图直观看x=union_item['click_article_id']y=union_item['count']plt.scatter(x,y)plt.plot(union_item['count'].values[40000:])大约有70,000对至少同时出现一次。新闻文章信息#不同类型新闻出现次数plt.plot(user_click_merge['category_id'].value_counts().values)#出现次数比较少的新闻类型,有些新闻类型基本出现几次plt.plot(user_click_merge['category_id'].value_counts().values[150:])plt.plot(user_click_merge['words_count'].values)用户点击的新闻类型偏好这个特征可以用来衡量是否用户的兴趣广泛。plt.plot(sorted(user_click_merge.groupby('user_id')['category_id'].nunique(),reverse=True))从上图可以看出小部分用户的范围很广阅读类型,大部分人都在20种以下的新闻类型。user_click_merge.groupby('user_id')['category_id'].nunique().reset_index().describe()用户浏览的文章长度分布通过统计不同用户点击的平均字数,可以反映表明用户对长文章更感兴趣或对短文本更感兴趣。plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(),reverse=True))从上图可以发现少量people很高,还有一小部分人平均浏览的文章数量很低。大多数人更喜欢阅读200-400字之间的新闻。#挑出大多数人的区间,仔细看plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(),reverse=True)[1000:45000])你可以找到最多人全部阅读250字以下的文章#更详细的参数user_click_merge.groupby('user_id')['words_count'].mean().reset_index().describe()用户点击新闻的时间分析#为了更好的可视化,这里时间从sklearn.preprocessingimportMinMaxScalermm=MinMaxScaler()user_click_merge['click_timestamp']=mm.fit_transform(user_click_merge[['click_timestamp']])user_click_merge['created_at_ts']=mm.fit_transform(user_click_merge[['created_at_ts']])user_click_merge=user_click_merge.sort_values('click_timestamp')user_click_merge.head()defmean_diff_time_func(df,col):df=pd.DataFrame(df,columns={col})df['time_shift1']=df[col].shift(1).fillna(0)df['diff_time']=abs(df[col]-df['time_shift1'])returndf['diff_time'].mean()#平均点击时间差mean_diff_click_time=user_click_merge.groupby('user_id')['click_timestamp','created_at_ts'].apply(lambdax:mean_diff_time_func(x,'click_timestamp'))plt.plot(sorted(mean_diff_click_time.values,reverse=True))从上图可以发现不同用户点击文章的时间差不同Mean_diff_created_time=user_click_merge.groupby('user_id')['click_timestamp','created_at_ts'].apply(lambdax:mean_diff_time_func(x,'created_at_ts'))plt.plot(sorted(mean_diff_created_time.values,reverse=True))来自从图中可以发现,用户是依次点击文章的,文章的创建时间也是不一样的。items_idx_2_rawid_dict=dict(zip(item_emb_df['article_id'],item_emb_df.index))#随机抽取5个用户,查看这些用户前后浏览的文章相似度sub_user_ids=np.random.choice(user_click_merge.user_id.unique(),size=15,replace=False)sub_user_info=user_click_merge[user_click_merge['user_id'].isin(sub_user_ids)]sub_user_info.head()defget_item_sim_list(df):sim_list=[]item_list=df['click_article_id'].valuesforiinrange(0,len(item_list)-1):emb1=item_emb_np[item_idx_2_rawid_dict[item_list[i]]]emb2=item_emb_np[item_idx_2_rawid_dict[item_list[i]+1]]]sim_list.append(np.dot(emb1,emb2)/(np.linalg.norm(emb1)*(np.linalg.norm(emb2))))sim_list.append(0)returnsim_listfor_,user_dfinsub_user_info.groupby('user_id'):item_sim_list=get_item_sim_list(user_df)plt.plot(item_sim_list)从图中可以看出,用户前后查看的一些商品的相似度有比较大的波动,并且有些波动比较小,有一定的通过数据分析的过程,我们目前可以得到以下重要信息,对我们后面做特征和分析特征很有帮助:训练集和测试集的userids不重复,即测试集中的用户模型是没有看到的。训练集中用户点击最少的文章数是2,而测试集中用户点击最少的文章数是1。同一个用户不是唯一的。后面做这部分特征的时候可以用到统计特征。用户点击文章的次数有很大的差异化程度,可以用来衡量后面的用户活跃度。一篇文章被用户点击的次数也有很大的区分度,可以用来衡量后面文章的热度。用户阅读的新闻比较强,所以我们在判断一个用户是否对某篇文章感兴趣的时候,很大程度上会和他历史上点击过的文章有关。差异,这个可以反映用户对文章字数的差异。用户点击的文章主题也大不相同,可以体现用户的主题偏好。不同用户点击文章的时间差也会不同,可以体现用户对文章时效性的偏好。因此,根据上面的一些分析,可以更好的帮助我们后面做特征工程,充分挖掘数据的隐藏信息。[1]天池新闻推荐词条大赛【数据分析】Task02
