推薦系統入門之數據分析-天池新聞推薦

數據分析

在上一節的內容中,已經使用了ItemCF構建了一個baseline,並獲得了一個結果。若是咱們須要在baseline的基礎上進一步提高,就須要對數據進行進一步的分析。python

導入庫app

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
import re
import warnings
import sys
%matplotlib inline
# 不打印 ignore 級別的消息
warnings.filterwarnings("ignore")

讀取數據dom

data_dir = './data'

# train 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'})  #重命名,方便後續match
item_emb_df = pd.read_csv(data_dir+'articles_emb.csv')

# test data
test_user_click_df = pd.read_csv(data_dir+'testA_click_log.csv')

數據預處理測試

對每一個用戶的點擊時間戳進行降序排序,獲得排名spa

train_user_click_df['rank'] = train_user_click_df.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)

test_user_click_df['rank'] = test_user_click_df.groupby(['user_id'])['click_timestamp'].rank(ascending=False).astype(int)

計算用戶點擊文章的次數,並添加新的一列count操作系統

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

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

數據瀏覽
用戶點擊日誌文件_訓練集3d

train_user_click_df = trn_click.merge(item_df, how='left', on=['click_article_id'])
train_user_click_df.head()

image.png

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: 用戶點擊文章時,文章的來源
#用戶點擊日誌信息
train_user_click_df.info()

image.png

train_user_click_df.describe()

image.png

train_user_click_df.user_id.nunique(), train_user_click_df.groupby('user_id')['click_article_id'].count().min()
200000, 2

下面經過直方圖來看一下,不一樣字段的分佈code

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 = train_user_click_df[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()

image.png

從點擊時間clik_timestamp來看,分佈較爲平均,可不作特殊處理。因爲時間戳是13位的,後續將時間格式轉換成10位方便計算。orm

從點擊環境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()

image.png

test_user_click_df.describe()

image.png
能夠看出訓練集和測試集的用戶是徹底不同的

訓練集的用戶ID由0 ~ 199999,而測試集A的用戶ID由200000 ~ 249999。

#測試集中的用戶數量爲5w
test_user_click_df.user_id.nunique(), test_user_click_df.groupby('user_id')['click_article_id'].count().min()
50000, 1

新聞文章信息數據表

#新聞文章數據集瀏覽
item_df.head().append(item_df.tail())

image.png

item_df['words_count'].value_counts()

image.png

item_df.shape
(364047, 4)

新聞文章embedding向量表示

item_emb_df.head()

image.png

item_emb_df.shape
(364047, 251)

數據分析

用戶重複點擊

# merge
user_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]

image.png

user_click_count[user_click_count['count']>7]

image.png

user_click_count['count'].unique()

image.png

#用戶點擊新聞次數
user_click_count.loc[:,'count'].value_counts()

image.png

能夠看出:有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()
    
# 分析用戶點擊環境變化是否明顯,這裏隨機採樣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_df in sample_users.groupby('user_id'):
    plot_envs(user_df, cols, 2, 3)

image.png
image.png
image.png
image.png
image.png
能夠看出絕大多數數的用戶的點擊環境是比較固定的。思路:能夠基於這些環境的統計特徵來表明該用戶自己的屬性

用戶點擊新聞數量的分佈

user_click_item_count = sorted(user_click_merge.groupby('user_id')['click_article_id'].count(), reverse=True)
plt.plot(user_click_item_count)

image.png
能夠根據用戶的點擊文章次數看出用戶的活躍度

#點擊次數在前50的用戶
plt.plot(user_click_item_count[:50])

image.png

點擊次數排前50的用戶的點擊次數都在100次以上。思路:咱們能夠定義點擊次數大於等於100次的用戶爲活躍用戶,這是一種簡單的處理思路, 判斷用戶活躍度,更加全面的是再結合上點擊時間,後面咱們會基於點擊次數和點擊時間兩個方面來判斷用戶活躍度。

#點擊次數排名在[25000:50000]之間
plt.plot(user_click_item_count[25000:50000])

image.png

能夠看出點擊次數小於等於兩次的用戶很是的多,這些用戶能夠認爲是非活躍用戶

新聞點擊次數分析

item_click_count = sorted(user_click_merge.groupby('click_article_id')['user_id'].count(), reverse=True) 

plt.plot(item_click_count)

image.png

plt.plot(item_click_count[:100])

image.png

能夠看出點擊次數最多的前100篇新聞,點擊次數大於1000次

plt.plot(item_click_count[:20])

image.png

點擊次數最多的前20篇新聞,點擊次數大於2500。思路:能夠定義這些新聞爲熱門新聞, 這個也是簡單的處理方式,後面咱們也是根據點擊次數和時間進行文章熱度的一個劃分。

plt.plot(item_click_count[3500:])

image.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()

image.png
由統計數據能夠看出,平均共現次數2.88,最高爲1687。

說明用戶看的新聞,相關性是比較強的。

#畫個圖直觀地看一看
x = union_item['click_article_id']
y = union_item['count']
plt.scatter(x, y)

image.png

plt.plot(union_item['count'].values[40000:])

image.png
大概有70000個pair至少共現一次。

新聞文章信息

#不一樣類型的新聞出現的次數
plt.plot(user_click_merge['category_id'].value_counts().values)

image.png

#出現次數比較少的新聞類型, 有些新聞類型,基本上就出現過幾回
plt.plot(user_click_merge['category_id'].value_counts().values[150:])

image.png

plt.plot(user_click_merge['words_count'].values)

image.png

用戶點擊的新聞類型的偏好

此特徵能夠用於度量用戶的興趣是否普遍。

plt.plot(sorted(user_click_merge.groupby('user_id')['category_id'].nunique(), reverse=True))

image.png
從上圖中能夠看出有一小部分用戶閱讀類型是極其普遍的,大部分人都處在20個新聞類型如下。

user_click_merge.groupby('user_id')['category_id'].nunique().reset_index().describe()

image.png

用戶查看文章的長度的分佈

經過統計不一樣用戶點擊新聞的平均字數,這個能夠反映用戶是對長文更感興趣仍是對短文更感興趣。

plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(), reverse=True))

image.png
從上圖中能夠發現有一小部分人看的文章平均詞數很是高,也有一小部分人看的平均文章次數很是低。

大多數人偏好於閱讀字數在200-400字之間的新聞。

#挑出大多數人的區間仔細看看
plt.plot(sorted(user_click_merge.groupby('user_id')['words_count'].mean(), reverse=True)[1000:45000])

image.png
能夠發現大多數人都是看250字如下的文章

#更加詳細的參數
user_click_merge.groupby('user_id')['words_count'].mean().reset_index().describe()

image.png

用戶點擊新聞的時間分析

#爲了更好的可視化,這裏把時間進行歸一化操做
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') 
user_click_merge.head()

image.png

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()
    
# 點擊時間差的平均值
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'))

plt.plot(sorted(mean_diff_click_time.values, reverse=True))

image.png

從上圖能夠發現不一樣用戶點擊文章的時間差是有差別的。

先後點擊文章的建立時間差的平均值

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'))
plt.plot(sorted(mean_diff_created_time.values, reverse=True))

image.png
從圖中能夠發現用戶前後點擊文章,文章的建立時間也是有差別的

用戶先後點擊文章的類似性分佈

item_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()

image.png

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

for _, user_df in sub_user_info.groupby('user_id'):
    item_sim_list = get_item_sim_list(user_df)
    plt.plot(item_sim_list)

image.png
從圖中能夠看出有些用戶先後看的商品的類似度波動比較大,有些波動比較小,也是有必定的區分度的。

總結

經過數據分析的過程, 咱們目前能夠獲得如下幾點重要的信息, 這個對於咱們進行後面的特徵製做和分析很是有幫助:

  1. 訓練集和測試集的用戶id沒有重複,也就是測試集裏面的用戶模型是沒有見過的。
  2. 訓練集中用戶最少的點擊文章數是2, 而測試集裏面用戶最少的點擊文章數是1。
  3. 用戶對於文章存在重複點擊的狀況, 但這個都存在於訓練集裏面
  4. 同一用戶的點擊環境存在不惟一的狀況,後面作這部分特徵的時候能夠採用統計特徵。
  5. 用戶點擊文章的次數有很大的區分度,後面能夠根據這個製做衡量用戶活躍度的特徵。
  6. 文章被用戶點擊的次數也有很大的區分度,後面能夠根據這個製做衡量文章熱度的特徵。
  7. 用戶看的新聞,相關性是比較強的,因此每每咱們判斷用戶是否對某篇文章感興趣的時候, 在很大程度上會和他歷史點擊過的文章有關
  8. 用戶點擊的文章字數有比較大的區別, 這個能夠反映用戶對於文章字數的區別。
  9. 用戶點擊過的文章主題也有很大的區別, 這個能夠反映用戶的主題偏好。
  10. 不一樣用戶點擊文章的時間差也會有所區別, 這個能夠反映用戶對於文章時效性的偏好。

因此根據上面的一些分析,能夠更好的幫助咱們後面作好特徵工程, 充分挖掘數據的隱含信息。

[1] 天池新聞推薦入門賽之【數據分析】Task02

相關文章
相關標籤/搜索