在上一節的內容中,已經使用了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()
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()
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
下面經過直方圖來看一下,不一樣字段的分佈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()
從點擊時間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()
test_user_click_df.describe()
能夠看出訓練集和測試集的用戶是徹底不同的
訓練集的用戶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())
item_df['words_count'].value_counts()
item_df.shape
(364047, 4)
新聞文章embedding向量表示
item_emb_df.head()
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]
user_click_count[user_click_count['count']>7]
user_click_count['count'].unique()
#用戶點擊新聞次數 user_click_count.loc[:,'count'].value_counts()
能夠看出:有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)
能夠看出絕大多數數的用戶的點擊環境是比較固定的。思路:能夠基於這些環境的統計特徵來表明該用戶自己的屬性
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])
能夠看出點擊次數最多的前100篇新聞,點擊次數大於1000次
plt.plot(item_click_count[:20])
點擊次數最多的前20篇新聞,點擊次數大於2500。思路:能夠定義這些新聞爲熱門新聞, 這個也是簡單的處理方式,後面咱們也是根據點擊次數和時間進行文章熱度的一個劃分。
plt.plot(item_click_count[3500:])
能夠發現不少新聞只被點擊過一兩次。思路:能夠定義這些新聞是冷門新聞。
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()
由統計數據能夠看出,平均共現次數2.88,最高爲1687。
說明用戶看的新聞,相關性是比較強的。
#畫個圖直觀地看一看 x = union_item['click_article_id'] y = union_item['count'] plt.scatter(x, y)
plt.plot(union_item['count'].values[40000:])
大概有70000個pair至少共現一次。
#不一樣類型的新聞出現的次數 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))
從上圖中能夠發現有一小部分人看的文章平均詞數很是高,也有一小部分人看的平均文章次數很是低。
大多數人偏好於閱讀字數在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()
#爲了更好的可視化,這裏把時間進行歸一化操做 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()
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))
從上圖能夠發現不一樣用戶點擊文章的時間差是有差別的。
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))
從圖中能夠發現用戶前後點擊文章,文章的建立時間也是有差別的
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()
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)
從圖中能夠看出有些用戶先後看的商品的類似度波動比較大,有些波動比較小,也是有必定的區分度的。
經過數據分析的過程, 咱們目前能夠獲得如下幾點重要的信息, 這個對於咱們進行後面的特徵製做和分析很是有幫助:
因此根據上面的一些分析,能夠更好的幫助咱們後面作好特徵工程, 充分挖掘數據的隱含信息。