天池新闻推荐——数据分析

数据分析

数据分析是解决一个数据科学问题的第一步。通过数据可视化观察数据,再根据情况进行数据预处理对数据进行清洗。 > 解决数据科学问题的步骤:(源于大佬分享及自己的总结补充) * 数据可视化(观察数据) * 数据预处理(对数据进行清洗) * 文献等资料查阅解决问题的已有方式 * 建立模型,确定目标对象 * 特征工程 * 模型尝试(统计模型、树模型、神经网络) * 特征筛选 * 模型超参数调优 * 交叉验证与模型融合

数据观察与数据分析

导入包

1
2
3
4
5
6
7
8
9
10
%matplotlib inline

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
plt.rc('font', family='SimHei', size=13)

import os, gc, re, warnings, sys
warnings.filterwarnings('ignore')

读取数据

1
2
3
4
5
6
7
8
9
# path = '/media/yons/sda2/raw_data/' # 本地路径
path = './raw_data/' # 本地路径

trn_click = pd.read_csv(path+'train_click_log.csv')
item_df = pd.read_csv(path+'articles.csv')
item_df = item_df.rename(columns={'article_id': 'click_article_id'})
item_emb_df = pd.read_csv(path+'articles_emb.csv')

tst_click = pd.read_csv(path+'testA_click_log.csv')

数据预处理

  • 对每个用户根据用户点击的时间戳进行排序,增加rank列
  • 计算每个用户点击文章的次数(也就是点击时间戳的数量),增加click_cnts列
1
2
3
4
5
trn_click['rank'] = trn_click.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)
tst_click['rank'] = tst_click.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)

trn_click['click_cnts'] = trn_click.groupby(['user_id'])['click_timestamp'].transform('count')
tst_click['click_cnts'] = tst_click.groupby(['user_id'])['click_timestamp'].transform('count')

观察数据

训练集数据:用户点击文章的日志文件

查看时将用户点击文章的数据表和文章数据表根据article_id拼接

1
2
trn_click = trn_click.merge(item_df, how='left', on=['click_article_id'])
trn_click.head()
user_id click_article_id click_timestamp click_environment click_deviceGroup click_os click_country click_region click_referrer_type rank click_cnts category_id created_at_ts words_count
0 199999 160417 1507029570190 4 1 17 1 13 1 11 11 281 1506942089000 173
1 199999 5408 1507029571478 4 1 17 1 13 1 10 11 4 1506994257000 118
2 199999 50823 1507029601478 4 1 17 1 13 1 9 11 99 1507013614000 213
3 199998 157770 1507029532200 4 1 17 1 25 5 40 40 281 1506983935000 201
4 199998 96613 1507029671831 4 1 17 1 25 5 39 40 209 1506938444000 185
1
trn_click.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1112623 entries, 0 to 1112622
Data columns (total 14 columns):
user_id                1112623 non-null int64
click_article_id       1112623 non-null int64
click_timestamp        1112623 non-null int64
click_environment      1112623 non-null int64
click_deviceGroup      1112623 non-null int64
click_os               1112623 non-null int64
click_country          1112623 non-null int64
click_region           1112623 non-null int64
click_referrer_type    1112623 non-null int64
rank                   1112623 non-null int64
click_cnts             1112623 non-null int64
category_id            1112623 non-null int64
created_at_ts          1112623 non-null int64
words_count            1112623 non-null int64
dtypes: int64(14)
memory usage: 127.3 MB
1
2
# 用户点击数据表的各列统计值
trn_click.describe()
user_id click_article_id click_timestamp click_environment click_deviceGroup click_os click_country click_region click_referrer_type rank click_cnts category_id created_at_ts words_count
count 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06 1.112623e+06
mean 1.221198e+05 1.951541e+05 1.507588e+12 3.947786e+00 1.815981e+00 1.301976e+01 1.310776e+00 1.813587e+01 1.910063e+00 7.118518e+00 1.323704e+01 3.056176e+02 1.506598e+12 2.011981e+02
std 5.540349e+04 9.292286e+04 3.363466e+08 3.276715e-01 1.035170e+00 6.967844e+00 1.618264e+00 7.105832e+00 1.220012e+00 1.016095e+01 1.631503e+01 1.155791e+02 8.343066e+09 5.223881e+01
min 0.000000e+00 3.000000e+00 1.507030e+12 1.000000e+00 1.000000e+00 2.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+00 2.000000e+00 1.000000e+00 1.166573e+12 0.000000e+00
25% 7.934700e+04 1.239090e+05 1.507297e+12 4.000000e+00 1.000000e+00 2.000000e+00 1.000000e+00 1.300000e+01 1.000000e+00 2.000000e+00 4.000000e+00 2.500000e+02 1.507220e+12 1.700000e+02
50% 1.309670e+05 2.038900e+05 1.507596e+12 4.000000e+00 1.000000e+00 1.700000e+01 1.000000e+00 2.100000e+01 2.000000e+00 4.000000e+00 8.000000e+00 3.280000e+02 1.507553e+12 1.970000e+02
75% 1.704010e+05 2.777120e+05 1.507841e+12 4.000000e+00 3.000000e+00 1.700000e+01 1.000000e+00 2.500000e+01 2.000000e+00 8.000000e+00 1.600000e+01 4.100000e+02 1.507756e+12 2.280000e+02
max 1.999990e+05 3.640460e+05 1.510603e+12 4.000000e+00 5.000000e+00 2.000000e+01 1.100000e+01 2.800000e+01 7.000000e+00 2.410000e+02 2.410000e+02 4.600000e+02 1.510666e+12 6.690000e+03
1
2
# 训练集中用户的数量
trn_click.user_id.nunique()
200000
1
2
# 训练集中每个用户最少点击的文章数
trn_click.groupby(['user_id'])['click_article_id'].count().min()
2

通过直方图观察用户点击日志文件的各列数据分布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
plt.figure()
plt.figure(figsize=(15, 20))
i = 1
for col in ['click_article_id', 'click_timestamp', 'click_environment', 'click_deviceGroup', 'click_os', 'click_country',
'click_region', 'click_referrer_type', 'rank', 'click_cnts']:
plot_envs = plt.subplot(5, 2, i) # 子图一共5行2列,i是图的索引(1就是第一个,2就是第二个)
i += 1
v = trn_click[col].value_counts().reset_index()[:10]
fig = sns.barplot(x=v['index'], y=v[col])
for item in fig.get_xticklabels():
f=item.set_rotation(90)
plt.title(col)
plt.tight_layout()
plt.show()
<Figure size 432x288 with 0 Axes>
png

分析 * 从点击时间clik_timestamp来看,分布较为平均,可不做特殊处理。由于时间戳是13位的,后续将时间格式转换成10位方便计算。

  • 从点击环境click_environment来看,仅有1922次(占0.1%)点击环境为1;仅有24617次(占2.3%)点击环境为2;剩余(占97.6%)点击环境为4。

  • 从点击设备组click_deviceGroup来看,设备1占大部分(60.4%),设备3占36%。

测试集用户点击日志

1
2
tst_click = tst_click.merge(item_df, how='left', on=['click_article_id'])
tst_click.head()
user_id click_article_id click_timestamp click_environment click_deviceGroup click_os click_country click_region click_referrer_type rank click_cnts category_id created_at_ts words_count
0 249999 160974 1506959142820 4 1 17 1 13 2 19 19 281 1506912747000 259
1 249999 160417 1506959172820 4 1 17 1 13 2 18 19 281 1506942089000 173
2 249998 160974 1506959056066 4 1 12 1 13 2 5 5 281 1506912747000 259
3 249998 202557 1506959086066 4 1 12 1 13 2 4 5 327 1506938401000 219
4 249997 183665 1506959088613 4 1 17 1 15 5 7 7 301 1500895686000 256
1
tst_click.describe()
user_id click_article_id click_timestamp click_environment click_deviceGroup click_os click_country click_region click_referrer_type rank click_cnts category_id created_at_ts words_count
count 518010.000000 518010.000000 5.180100e+05 518010.000000 518010.000000 518010.000000 518010.000000 518010.000000 518010.000000 518010.000000 518010.000000 518010.000000 5.180100e+05 518010.000000
mean 227342.428169 193803.792550 1.507387e+12 3.947300 1.738285 13.628467 1.348209 18.250250 1.819614 15.521785 30.043586 305.324961 1.506883e+12 210.966331
std 14613.907188 88279.388177 3.706127e+08 0.323916 1.020858 6.625564 1.703524 7.060798 1.082657 33.957702 56.868021 110.411513 5.816668e+09 83.040065
min 200000.000000 137.000000 1.506959e+12 1.000000 1.000000 2.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.265812e+12 0.000000
25% 214926.000000 128551.000000 1.507026e+12 4.000000 1.000000 12.000000 1.000000 13.000000 1.000000 4.000000 10.000000 252.000000 1.506970e+12 176.000000
50% 229109.000000 199197.000000 1.507308e+12 4.000000 1.000000 17.000000 1.000000 21.000000 2.000000 8.000000 19.000000 323.000000 1.507249e+12 199.000000
75% 240182.000000 272143.000000 1.507666e+12 4.000000 3.000000 17.000000 1.000000 25.000000 2.000000 18.000000 35.000000 399.000000 1.507630e+12 232.000000
max 249999.000000 364043.000000 1.508832e+12 4.000000 5.000000 20.000000 11.000000 28.000000 7.000000 938.000000 938.000000 460.000000 1.509949e+12 3082.000000

训练集和测试集的用户是完全不一样的,训练集的用户ID由0 ~ 199999,而测试集A的用户ID由200000 ~ 249999。

1
2
#测试集中的用户数量为5w
tst_click.user_id.nunique()
50000
1
tst_click.groupby('user_id')['click_article_id'].count().min() # 注意测试集里面有只点击过一次文章的用户
1

新闻文章信息数据表

1
2
#新闻文章数据集浏览
item_df.head().append(item_df.tail())
click_article_id category_id created_at_ts words_count
0 0 0 1513144419000 168
1 1 1 1405341936000 189
2 2 1 1408667706000 250
3 3 1 1408468313000 230
4 4 1 1407071171000 162
364042 364042 460 1434034118000 144
364043 364043 460 1434148472000 463
364044 364044 460 1457974279000 177
364045 364045 460 1515964737000 126
364046 364046 460 1505811330000 479
1
item_df['words_count'].value_counts()
176     3485
182     3480
179     3463
178     3458
174     3456
183     3432
184     3427
173     3414
180     3403
177     3391
170     3387
187     3355
169     3352
185     3348
175     3346
181     3330
186     3328
189     3327
171     3327
172     3322
165     3308
188     3288
167     3269
190     3261
192     3257
168     3248
193     3225
166     3199
191     3182
194     3164
        ... 
601        1
857        1
1977       1
1626       1
697        1
1720       1
696        1
706        1
592        1
1605       1
586        1
582        1
1606       1
972        1
716        1
584        1
1608       1
715        1
841        1
968        1
964        1
587        1
1099       1
1355       1
711        1
845        1
710        1
965        1
847        1
1535       1
Name: words_count, Length: 866, dtype: int64
1
2
print(item_df['category_id'].nunique())     # 461个文章主题
item_df['category_id'].hist()
461





<matplotlib.axes._subplots.AxesSubplot at 0x7f92a1e03080>
png
1
item_df.shape       # 364047篇文章
(364047, 4)

新闻文章embedding向量表示

1
item_emb_df.head()
article_id emb_0 emb_1 emb_2 emb_3 emb_4 emb_5 emb_6 emb_7 emb_8 ... emb_240 emb_241 emb_242 emb_243 emb_244 emb_245 emb_246 emb_247 emb_248 emb_249
0 0 -0.161183 -0.957233 -0.137944 0.050855 0.830055 0.901365 -0.335148 -0.559561 -0.500603 ... 0.321248 0.313999 0.636412 0.169179 0.540524 -0.813182 0.286870 -0.231686 0.597416 0.409623
1 1 -0.523216 -0.974058 0.738608 0.155234 0.626294 0.485297 -0.715657 -0.897996 -0.359747 ... -0.487843 0.823124 0.412688 -0.338654 0.320786 0.588643 -0.594137 0.182828 0.397090 -0.834364
2 2 -0.619619 -0.972960 -0.207360 -0.128861 0.044748 -0.387535 -0.730477 -0.066126 -0.754899 ... 0.454756 0.473184 0.377866 -0.863887 -0.383365 0.137721 -0.810877 -0.447580 0.805932 -0.285284
3 3 -0.740843 -0.975749 0.391698 0.641738 -0.268645 0.191745 -0.825593 -0.710591 -0.040099 ... 0.271535 0.036040 0.480029 -0.763173 0.022627 0.565165 -0.910286 -0.537838 0.243541 -0.885329
4 4 -0.279052 -0.972315 0.685374 0.113056 0.238315 0.271913 -0.568816 0.341194 -0.600554 ... 0.238286 0.809268 0.427521 -0.615932 -0.503697 0.614450 -0.917760 -0.424061 0.185484 -0.580292

5 rows × 251 columns

1
item_emb_df.shape
(364047, 251)

数据分析

用户重复点击

1
2
# merge
user_click_merge = trn_click.append(tst_click)
1
2
3
#用户重复点击
user_click_count = user_click_merge.groupby(['user_id', 'click_article_id'])['click_timestamp'].agg({'count'}).reset_index()
user_click_count[:10]
user_id click_article_id count
0 0 30760 1
1 0 157507 1
2 1 63746 1
3 1 289197 1
4 2 36162 1
5 2 168401 1
6 3 36162 1
7 3 50644 1
8 4 39894 1
9 4 42567 1
1
user_click_count[user_click_count['count']>7]
user_id click_article_id count
311242 86295 74254 10
311243 86295 76268 10
393761 103237 205948 10
393763 103237 235689 10
576902 134850 69463 13
1
user_click_count['count'].unique()
array([ 1,  2,  4,  3,  6,  5, 10,  7, 13])
1
2
#用户点击新闻次数
user_click_count.loc[:,'count'].value_counts()
1     1605541
2       11621
3         422
4          77
5          26
6          12
10          4
7           3
13          1
Name: count, dtype: int64

可以看出:有1605541(约占99.2%)的用户未重复阅读过文章,仅有极少数用户重复点击过某篇文章。 这个也可以单独制作成特征

用户点击环境变化分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def plot_envs(df, cols, r, c):
plt.figure()
plt.figure(figsize=(10, 5))
i = 1
for col in cols:
plt.subplot(r, c, i)
i += 1
v = df[col].value_counts().reset_index()
fig = sns.barplot(x=v['index'], y=v[col])
for item in fig.get_xticklabels():
item.set_rotation(90)
plt.title(col)
plt.tight_layout()
plt.show()
1
2
3
4
5
6
# 分析用户点击环境变化是否明显,这里随机采样10个用户分析这些用户的点击环境分布
sample_user_ids = np.random.choice(tst_click['user_id'].unique(), size=10, 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_df in sample_users.groupby('user_id'):
plot_envs(user_df, cols, 2, 3)
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png
<Figure size 432x288 with 0 Axes>
png

可以看出绝大多数数的用户的点击环境是比较固定的。思路:可以基于这些环境的统计特征来代表该用户本身的属性

用户点击新闻数量的分布

1
2
user_click_item_count = sorted(user_click_merge.groupby('user_id')['click_article_id'].count(), reverse=True)
plt.plot(user_click_item_count)
[<matplotlib.lines.Line2D at 0x7f92a03e6cf8>]
png

可以根据用户的点击文章次数看出用户的活跃度

1
2
#点击次数在前50的用户
plt.plot(user_click_item_count[:50])
[<matplotlib.lines.Line2D at 0x7f92a0391ba8>]
png

点击次数排前50的用户的点击次数都在100次以上。思路:我们可以定义点击次数大于等于100次的用户为活跃用户,这是一种简单的处理思路, 判断用户活跃度,更加全面的是再结合上点击时间,后面我们会基于点击次数和点击时间两个方面来判断用户活跃度。

1
2
#点击次数排名在[25000:50000]之间
plt.plot(user_click_item_count[25000:50000])
[<matplotlib.lines.Line2D at 0x7f92a04d3240>]
png

可以看出点击次数小于等于两次的用户非常的多,这些用户可以认为是非活跃用户。

新闻点击次数分析

1
item_click_count = sorted(user_click_merge.groupby('click_article_id')['user_id'].count(), reverse=True)
1
plt.plot(item_click_count)
[<matplotlib.lines.Line2D at 0x7f92a014c4a8>]
png
1
plt.plot(item_click_count[:100])
[<matplotlib.lines.Line2D at 0x7f92a1a04eb8>]
png

可以看出点击次数最多的前100篇新闻,点击次数大于1000次

1
plt.plot(item_click_count[:20])
[<matplotlib.lines.Line2D at 0x7f92a1ad99e8>]
png

点击次数最多的前20篇新闻,点击次数大于2500。思路:可以定义这些新闻为热门新闻, 这个也是简单的处理方式,后面我们也是根据点击次数和时间进行文章热度的一个划分。

1
plt.plot(item_click_count[3500:])
[<matplotlib.lines.Line2D at 0x7f92a1c82208>]
png

可以发现很多新闻只被点击过一两次。思路:可以定义这些新闻是冷门新闻

新闻共现频次:两篇新闻连续出现的次数

1
2
3
4
tmp = user_click_merge.sort_values('click_timestamp')
tmp['next_item'] = tmp.groupby(['user_id'])['click_article_id'].transform(lambda x: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()
count
count 433597.000000
mean 3.184139
std 18.851753
min 1.000000
25% 1.000000
50% 1.000000
75% 2.000000
max 2202.000000

由统计数据可以看出,平均共现次数2.88,最高为1687。

说明用户看的新闻,相关性是比较强的。

1
2
3
4
#画个图直观地看一看
x = union_item['click_article_id']
y = union_item['count']
plt.scatter(x, y)
<matplotlib.collections.PathCollection at 0x7f92a033cdd8>
png
1
plt.plot(union_item['count'].values[40000:])
[<matplotlib.lines.Line2D at 0x7f92a0477d68>]
png

大概有70000个pair至少共现一次

新闻文章信息

1
2
#不同类型的新闻出现的次数
plt.plot(user_click_merge['category_id'].value_counts().values)
[<matplotlib.lines.Line2D at 0x7f92a02d1c50>]
png
1
2
#出现次数比较少的新闻类型, 有些新闻类型,基本上就出现过几次
plt.plot(user_click_merge['category_id'].value_counts().values[150:])
[<matplotlib.lines.Line2D at 0x7f9295dcdf28>]
png
1
2
#新闻字数的描述性统计
user_click_merge['words_count'].describe()
count    1.630633e+06
mean     2.043012e+02
std      6.382198e+01
min      0.000000e+00
25%      1.720000e+02
50%      1.970000e+02
75%      2.290000e+02
max      6.690000e+03
Name: words_count, dtype: float64
1
plt.plot(user_click_merge['words_count'].values)
[<matplotlib.lines.Line2D at 0x7f92a02c2390>]
png

用户点击的新闻类型的偏好

此特征可以用于度量用户的兴趣是否广泛。

1
plt.plot(sorted(user_click_merge.groupby('user_id')['category_id'].nunique(), reverse=True))
[<matplotlib.lines.Line2D at 0x7f92a0283cc0>]
png

可以看出有一小部分用户阅读类型是极其广泛的,大部分人都处在20个新闻类型以下。

1
user_click_merge.groupby('user_id')['category_id'].nunique().reset_index().describe()
user_id category_id
count 250000.000000 250000.000000
mean 124999.500000 4.573188
std 72168.927986 4.419800
min 0.000000 1.000000
25% 62499.750000 2.000000
50% 124999.500000 3.000000
75% 187499.250000 6.000000
max 249999.000000 95.000000

用户查看文章的长度的分布

通过统计不同用户点击新闻的平均字数,这个可以反映用户是对长文更感兴趣还是对短文更感兴趣。

1
plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(), reverse=True))
[<matplotlib.lines.Line2D at 0x7f9294f8c9b0>]
png

可以发现有一小部分人看的文章平均词数非常高,也有一小部分人看的平均文章次数非常低。

大多数人偏好于阅读字数在200-400字之间的新闻。

1
2
#挑出大多数人的区间仔细看看
plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(), reverse=True)[1000:45000])
[<matplotlib.lines.Line2D at 0x7f925a255f98>]
png

可以发现大多数人都是看250字以下的文章

1
2
#更加详细的参数
user_click_merge.groupby('user_id')['words_count'].mean().reset_index().describe()
user_id words_count
count 250000.000000 250000.000000
mean 124999.500000 205.830189
std 72168.927986 47.174030
min 0.000000 8.000000
25% 62499.750000 187.500000
50% 124999.500000 202.000000
75% 187499.250000 217.750000
max 249999.000000 3434.500000

用户点击新闻的时间分析

1
2
3
4
5
6
7
#为了更好的可视化,这里把时间进行归一化操作
from sklearn.preprocessing import MinMaxScaler
mm = 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')
1
user_click_merge.head()
user_id click_article_id click_timestamp click_environment click_deviceGroup click_os click_country click_region click_referrer_type rank click_cnts category_id created_at_ts words_count
18 249990 162300 0.000000 4 3 20 1 25 2 5 5 281 0.989186 193
2 249998 160974 0.000002 4 1 12 1 13 2 5 5 281 0.989092 259
30 249985 160974 0.000003 4 1 17 1 8 2 8 8 281 0.989092 259
50 249979 162300 0.000004 4 1 17 1 25 2 2 2 281 0.989186 193
25 249988 160974 0.000004 4 1 17 1 21 2 17 17 281 0.989092 259
1
2
3
4
5
def mean_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'])
return df['diff_time'].mean()
1
2
# 点击时间差的平均值
mean_diff_click_time = user_click_merge.groupby('user_id')['click_timestamp', 'created_at_ts'].apply(lambda x: mean_diff_time_func(x, 'click_timestamp'))
1
plt.plot(sorted(mean_diff_click_time.values, reverse=True))
[<matplotlib.lines.Line2D at 0x7f929880f4e0>]
png

可以发现不同用户点击文章的时间差是有差异的。

1
2
# 前后点击文章的创建时间差的平均值
mean_diff_created_time = user_click_merge.groupby('user_id')['click_timestamp', 'created_at_ts'].apply(lambda x: mean_diff_time_func(x, 'created_at_ts'))
1
plt.plot(sorted(mean_diff_created_time.values, reverse=True))
[<matplotlib.lines.Line2D at 0x7f9298758fd0>]
png

可以发现用户先后点击文章,文章的创建时间也是有差异的

1
2
# 用户前后点击文章的相似性分布
item_idx_2_rawid_dict = dict(zip(item_emb_df['article_id'], item_emb_df.index))
1
del item_emb_df['article_id']
1
item_emb_np = np.ascontiguousarray(item_emb_df.values, dtype=np.float32)
1
2
3
4
5
# 随机选择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()
user_id click_article_id click_timestamp click_environment click_deviceGroup click_os click_country click_region click_referrer_type rank click_cnts category_id created_at_ts words_count
69935 223960 272143 0.005832 4 3 20 6 28 2 4 4 399 0.989235 184
69936 223960 160974 0.005852 4 3 20 6 28 1 3 4 281 0.989092 259
69937 223960 337082 0.005860 4 3 20 6 28 1 2 4 437 0.989214 163
75534 221873 166581 0.006673 4 1 17 1 25 2 9 9 289 0.989241 210
75535 221873 64329 0.006680 4 1 17 1 25 2 8 9 134 0.989259 199
1
2
3
4
5
6
7
8
9
def get_item_sim_list(df):
sim_list = []
item_list = df['click_article_id'].values
for i in range(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)
return sim_list
1
2
3
for _, user_df in sub_user_info.groupby('user_id'):
item_sim_list = get_item_sim_list(user_df)
plt.plot(item_sim_list)
png

可以看出有些用户前后看的商品的相似度波动比较大,有些波动比较小,也是有一定的区分度的。

总结

  1. 训练集和测试集的用户id没有重复,也就是测试集里面的用户模型是没有见过的
  2. 训练集中用户最少的点击文章数是2, 而测试集里面用户最少的点击文章数是1
  3. 用户对于文章存在重复点击的情况, 但这个都存在于训练集里面
  4. 同一用户的点击环境存在不唯一的情况,后面做这部分特征的时候可以采用统计特征
  5. 用户点击文章的次数有很大的区分度,后面可以根据这个制作衡量用户活跃度的特征
  6. 文章被用户点击的次数也有很大的区分度,后面可以根据这个制作衡量文章热度的特征
  7. 用户看的新闻,相关性是比较强的,所以往往我们判断用户是否对某篇文章感兴趣的时候, 在很大程度上会和他历史点击过的文章有关
  8. 用户点击的文章字数有比较大的区别, 这个可以反映用户对于文章字数的区别
  9. 用户点击过的文章主题也有很大的区别, 这个可以反映用户的主题偏好
  10. 不同用户点击文章的时间差也会有所区别, 这个可以反映用户对于文章时效性的偏好