推荐系统实践1——数据分析

数据分析

数据分析的价值主要在于熟悉了解整个数据集的基本情况包括每个文件里有哪些数据,具体的文件中的每个字段表示什么实际含义,以及数据集中特征之间的相关性,在推荐场景下主要就是分析用户本身的基本属性,文章基本属性,以及用户和文章交互的一些分布,这些都有利于后面的召回策略的选择,以及特征工程。

建议:当特征工程和模型调参已经很难继续上分了,可以回来在重新从新的角度去分析这些数据,或许可以找到上分的灵感

导包

%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")

读取数据

path = './data/'

#####train
trn_click = pd.read_csv(path+'train_click_log.csv')
#trn_click = pd.read_csv(path+'train_click_log.csv', names=['user_id','item_id','click_time','click_environment','click_deviceGroup','click_os','click_country','click_region','click_referrer_type'])
item_df = pd.read_csv(path+'articles.csv')
item_df = item_df.rename(columns={'article_id': 'click_article_id'})  #重命名,方便后续match
item_emb_df = pd.read_csv(path+'articles_emb.csv')

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

数据预处理

计算用户点击rank和点击次数

# 对每个用户的点击时间戳进行排序
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)
#计算用户点击文章的次数,并添加新的一列count
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')

数据浏览

用户点击日志文件_训练集

trn_click = trn_click.merge(item_df, how='left', on=['click_article_id'])
trn_click.head()
 user_idclick_article_idclick_timestampclick_environmentclick_deviceGroupclick_osclick_countryclick_regionclick_referrer_typerankclick_cntscategory_idcreated_at_tswords_count
019999916041715070295701904117113111112811506942089000173
11999995408150702957147841171131101141506994257000118
219999950823150702960147841171131911991507013614000213
319999815777015070295322004117125540402811506983935000201
41999989661315070296718314117125539402091506938444000185

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: 用户点击文章时,文章的来源
#用户点击日志信息
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
trn_click.describe()

 

 user_idclick_article_idclick_timestampclick_environmentclick_deviceGroupclick_osclick_countryclick_regionclick_referrer_typerankclick_cntscategory_idcreated_at_tswords_count
count1.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+061.112623e+06
mean1.221198e+051.951541e+051.507588e+123.947786e+001.815981e+001.301976e+011.310776e+001.813587e+011.910063e+007.118518e+001.323704e+013.056176e+021.506598e+122.011981e+02
std5.540349e+049.292286e+043.363466e+083.276715e-011.035170e+006.967844e+001.618264e+007.105832e+001.220012e+001.016095e+011.631503e+011.155791e+028.343066e+095.223881e+01
min0.000000e+003.000000e+001.507030e+121.000000e+001.000000e+002.000000e+001.000000e+001.000000e+001.000000e+001.000000e+002.000000e+001.000000e+001.166573e+120.000000e+00
25%7.934700e+041.239090e+051.507297e+124.000000e+001.000000e+002.000000e+001.000000e+001.300000e+011.000000e+002.000000e+004.000000e+002.500000e+021.507220e+121.700000e+02
50%1.309670e+052.038900e+051.507596e+124.000000e+001.000000e+001.700000e+011.000000e+002.100000e+012.000000e+004.000000e+008.000000e+003.280000e+021.507553e+121.970000e+02
75%1.704010e+052.777120e+051.507841e+124.000000e+003.000000e+001.700000e+011.000000e+002.500000e+012.000000e+008.000000e+001.600000e+014.100000e+021.507756e+122.280000e+02
max1.999990e+053.640460e+051.510603e+124.000000e+005.000000e+002.000000e+011.100000e+012.800000e+017.000000e+002.410000e+022.410000e+024.600000e+021.510666e+126.690000e+03
#训练集中的用户数量为20w
trn_click.user_id.nunique()
200000
trn_click.groupby('user_id')['click_article_id'].count().min()  # 训练集里面每个用户至少点击了两篇文章
2

画直方图大体看一下基本的属性分布

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)
    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():
        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%。

测试集用户点击日志

tst_click = tst_click.merge(item_df, how='left', on=['click_article_id'])
tst_click.head()
 user_idclick_article_idclick_timestampclick_environmentclick_deviceGroupclick_osclick_countryclick_regionclick_referrer_typerankclick_cntscategory_idcreated_at_tswords_count
024999916097415069591428204117113219192811506912747000259
124999916041715069591728204117113218192811506942089000173
2249998160974150695905606641121132552811506912747000259
3249998202557150695908606641121132453271506938401000219
4249997183665150695908861341171155773011500895686000256
tst_click.describe()
 user_idclick_article_idclick_timestampclick_environmentclick_deviceGroupclick_osclick_countryclick_regionclick_referrer_typerankclick_cntscategory_idcreated_at_tswords_count
count518010.000000518010.0000005.180100e+05518010.000000518010.000000518010.000000518010.000000518010.000000518010.000000518010.000000518010.000000518010.0000005.180100e+05518010.000000
mean227342.428169193803.7925501.507387e+123.9473001.73828513.6284671.34820918.2502501.81961415.52178530.043586305.3249611.506883e+12210.966331
std14613.90718888279.3881773.706127e+080.3239161.0208586.6255641.7035247.0607981.08265733.95770256.868021110.4115135.816668e+0983.040065
min200000.000000137.0000001.506959e+121.0000001.0000002.0000001.0000001.0000001.0000001.0000001.0000001.0000001.265812e+120.000000
25%214926.000000128551.0000001.507026e+124.0000001.00000012.0000001.00000013.0000001.0000004.00000010.000000252.0000001.506970e+12176.000000
50%229109.000000199197.0000001.507308e+124.0000001.00000017.0000001.00000021.0000002.0000008.00000019.000000323.0000001.507249e+12199.000000
75%240182.000000272143.0000001.507666e+124.0000003.00000017.0000001.00000025.0000002.00000018.00000035.000000399.0000001.507630e+12232.000000
max249999.000000364043.0000001.508832e+124.0000005.00000020.00000011.00000028.0000007.000000938.000000938.000000460.0000001.509949e+123082.000000

我们可以看出训练集和测试集的用户是完全不一样的

训练集的用户ID由0 ~ 199999,而测试集A的用户ID由200000 ~ 249999。

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

新闻文章信息数据表

#新闻文章数据集浏览
item_df.head().append(item_df.tail())
 click_article_idcategory_idcreated_at_tswords_count
0001513144419000168
1111405341936000189
2211408667706000250
3311408468313000230
4411407071171000162
3640423640424601434034118000144
3640433640434601434148472000463
3640443640444601457974279000177
3640453640454601515964737000126
3640463640464601505811330000479
item_df['words_count'].value_counts()

 

176     3485
182     3480
179     3463
178     3458
174     3456
        ... 
845        1
710        1
965        1
847        1
1535       1
Name: words_count, Length: 866, dtype: int64
print(item_df['category_id'].nunique())     # 461个文章主题
item_df['category_id'].hist()
461

<matplotlib.axes._subplots.AxesSubplot at 0x7fb37d803150>

item_df.shape       # 364047篇文章
  • 1
(364047, 4)

新闻文章embedding向量表示

item_emb_df.head()
 article_idemb_0emb_1emb_2emb_3emb_4emb_5emb_6emb_7emb_8...emb_240emb_241emb_242emb_243emb_244emb_245emb_246emb_247emb_248emb_249
00-0.161183-0.957233-0.1379440.0508550.8300550.901365-0.335148-0.559561-0.500603...0.3212480.3139990.6364120.1691790.540524-0.8131820.286870-0.2316860.5974160.409623
11-0.523216-0.9740580.7386080.1552340.6262940.485297-0.715657-0.897996-0.359747...-0.4878430.8231240.412688-0.3386540.3207860.588643-0.5941370.1828280.397090-0.834364
22-0.619619-0.972960-0.207360-0.1288610.044748-0.387535-0.730477-0.066126-0.754899...0.4547560.4731840.377866-0.863887-0.3833650.137721-0.810877-0.4475800.805932-0.285284
33-0.740843-0.9757490.3916980.641738-0.2686450.191745-0.825593-0.710591-0.040099...0.2715350.0360400.480029-0.7631730.0226270.565165-0.910286-0.5378380.243541-0.885329
44-0.279052-0.9723150.6853740.1130560.2383150.271913-0.5688160.341194-0.600554...0.2382860.8092680.427521-0.615932-0.5036970.614450-0.917760-0.4240610.185484-0.580292

5 rows × 251 columns

item_emb_df.shape
  • 1
(364047, 251)
  • 1

数据分析

用户重复点击

#####merge
user_click_merge = trn_click.append(tst_click)
  • 1
  • 2
#用户重复点击
user_click_count = user_click_merge.groupby(['user_id', 'click_article_id'])['click_timestamp'].agg({'count'}).reset_index()
user_click_count[:10]
  • 1
  • 2
  • 3
 user_idclick_article_idcount
00307601
101575071
21637461
312891971
42361621
521684011
63361621
73506441
84398941
94425671
user_click_count[user_click_count['count']>7]
  • 1
 user_idclick_article_idcount
311242862957425410
311243862957626810
39376110323720594810
39376310323723568910
5769021348506946313
user_click_count['count'].unique()
  • 1
array([ 1,  2,  4,  3,  6,  5, 10,  7, 13])
  • 1
#用户点击新闻次数
user_click_count.loc[:,'count'].value_counts() 
  • 1
  • 2
1     1605541
2       11621
3         422
4          77
5          26
6          12
10          4
7           3
13          1
Name: count, dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

用户点击环境变化分析

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
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
# 分析用户点击环境变化是否明显,这里随机采样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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYsiOfuZ-1606472953037)(output_43_1.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TCnAq1mw-1606472953038)(output_43_3.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wluJbC5V-1606472953039)(output_43_5.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KsAna8QD-1606472953040)(output_43_7.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drBs0QhF-1606472953041)(output_43_9.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlPoQk4V-1606472953041)(output_43_11.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0CbNWIEG-1606472953042)(output_43_13.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjC9HLKT-1606472953042)(output_43_15.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYRHWWEi-1606472953043)(output_43_17.png)]

<Figure size 432x288 with 0 Axes>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xyzFljmf-1606472953043)(output_43_19.png)]

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

用户点击新闻数量的分布

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iz99zUwS-1606472953044)(output_46_1.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EY8zO1UR-1606472953044)(output_48_1.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-geLLITUl-1606472953045)(output_50_1.png)]

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

新闻点击次数分析

item_click_count = sorted(user_click_merge.groupby('click_article_id')['user_id'].count(), reverse=True)
  • 1
plt.plot(item_click_count)
  • 1
[<matplotlib.lines.Line2D at 0x7fb37d587610>]
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gw1p3VTZ-1606472953045)(output_54_1.png)]

plt.plot(item_click_count[:100])
  • 1
[<matplotlib.lines.Line2D at 0x7fb37d490950>]
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-528QKypx-1606472953046)(output_55_1.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XiHkjAzA-1606472953046)(output_57_1.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAOXO0Jl-1606472953047)(output_59_1.png)]

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

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

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()
  • 1
  • 2
  • 3
  • 4
 count
count433597.000000
mean3.184139
std18.851753
min1.000000
25%1.000000
50%1.000000
75%2.000000
max2202.000000

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

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

#画个图直观地看一看
x = union_item['click_article_id']
y = union_item['count']
plt.scatter(x, y)
  • 1
  • 2
  • 3
  • 4
<matplotlib.collections.PathCollection at 0x7fb37ddfc250>
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XIl5xdmY-1606472953047)(output_64_1.png)]

plt.plot(union_item['count'].values[40000:])
  • 1
[<matplotlib.lines.Line2D at 0x7fb37dd50f90>]
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2RoSCv2-1606472953048)(output_65_1.png)]

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

新闻文章信息

#不同类型的新闻出现的次数
plt.plot(user_click_merge['category_id'].value_counts().values)
  • 1
  • 2
[<matplotlib.lines.Line2D at 0x7fb37dd0e9d0>]
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5sG6EVen-1606472953049)(output_68_1.png)]

#出现次数比较少的新闻类型, 有些新闻类型,基本上就出现过几次
plt.plot(user_click_merge['category_id'].value_counts().values[150:])
  • 1
  • 2
[<matplotlib.lines.Line2D at 0x7fb374200a10>]
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DnOo3RK3-1606472953049)(output_69_1.png)]

#新闻字数的描述性统计
user_click_merge['words_count'].describe()
  • 1
  • 2
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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
plt.plot(user_click_merge['words_count'].values)
  • 1
[<matplotlib.lines.Line2D at 0x7fb30a844090>]
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJV6xCxE-1606472953050)(output_71_1.png)]

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1xDbKVy-1606472953051)(output_73_1.png)]

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

user_click_merge.groupby('user_id')['category_id'].nunique().reset_index().describe()
  • 1
 user_idcategory_id
count250000.000000250000.000000
mean124999.5000004.573188
std72168.9279864.419800
min0.0000001.000000
25%62499.7500002.000000
50%124999.5000003.000000
75%187499.2500006.000000
max249999.00000095.000000

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOYEDPTI-1606472953051)(output_77_1.png)]

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S3xJah73-1606472953054)(output_79_1.png)]

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

#更加详细的参数
user_click_merge.groupby('user_id')['words_count'].mean().reset_index().describe()
  • 1
  • 2
 user_idwords_count
count250000.000000250000.000000
mean124999.500000205.830189
std72168.92798647.174030
min0.0000008.000000
25%62499.750000187.500000
50%124999.500000202.000000
75%187499.250000217.750000
max249999.0000003434.500000

用户点击新闻的时间分析

#为了更好的可视化,这里把时间进行归一化操作
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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
user_click_merge.head()
  • 1
 user_idclick_article_idclick_timestampclick_environmentclick_deviceGroupclick_osclick_countryclick_regionclick_referrer_typerankclick_cntscategory_idcreated_at_tswords_count
182499901623000.00000043201252552810.989186193
22499981609740.00000241121132552810.989092259
302499851609740.0000034117182882810.989092259
502499791623000.00000441171252222810.989186193
252499881609740.0000044117121217172810.989092259
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
  • 3
  • 4
  • 5
# 点击时间差的平均值
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
  • 2
plt.plot(sorted(mean_diff_click_time.values, reverse=True))
  • 1

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

# 前后点击文章的创建时间差的平均值
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
  • 2
plt.plot(sorted(mean_diff_created_time.values, reverse=True))
  • 1

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

# 用户前后点击文章的相似性分布
item_idx_2_rawid_dict = dict(zip(item_emb_df['article_id'], item_emb_df.index))
  • 1
  • 2
del item_emb_df['article_id']
  • 1
item_emb_np = np.ascontiguousarray(item_emb_df.values, dtype=np.float32)
  • 1
# 随机选择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()
  • 1
  • 2
  • 3
  • 4
  • 5
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
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
for _, user_df in sub_user_info.groupby('user_id'):
    item_sim_list = get_item_sim_list(user_df)
    plt.plot(item_sim_list)
  • 1
  • 2
  • 3

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

总结

通过数据分析的过程, 我们目前可以得到以下几点重要的信息, 这个对于我们进行后面的特征制作和分析非常有帮助:

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

所以根据上面的一些分析,可以更好的帮助我们后面做好特征工程, 充分挖掘数据的隐含信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值