본문 바로가기

[중급] 가볍게 이것저것

[뷰티 상품] 추천 시스템 구축하기

import pandas as pd

data = pd.read_csv('data/ratings.csv')
# include='all' 을 해야 숫자가 아닌 컬럼들도 보이게 된다.
data.describe(include='all')

UserId ProductId Rating Timestamp
count 2023070 2023070 2.023070e+06 2.023070e+06
unique 1210271 249274 NaN NaN
top A3KEZLJ59C1JVH B001MA0QY2 NaN NaN
freq 389 7533 NaN NaN
mean NaN NaN 4.149036e+00 1.360389e+09
std NaN NaN 1.311505e+00 4.611860e+07
min NaN NaN 1.000000e+00 9.087552e+08
25% NaN NaN 4.000000e+00 1.350259e+09
50% NaN NaN 5.000000e+00 1.372810e+09
75% NaN NaN 5.000000e+00 1.391472e+09
max NaN NaN 5.000000e+00 1.406074e+09
import numpy as np
product = data.groupby('ProductId').agg({'Rating' : [np.size, np.mean]})
user = data.groupby('UserId').agg({'Rating' : [np.size, np.mean]})
# 이 번 분석에서 가장 중요하다 생각하는 부분은
# 100회 미만 언급된 상품과, 20회 미만 참여한 사람을 제외한다는 것.
# 불리언 인덱싱으로 100회 이상 언급된 상품만 추려내기
%matplotlib inline
product_idx = product.loc[product['Rating']['size'] > 100]
product_idx.hist(('Rating', 'size'), bins=100)
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000001DD97C0EE48>]],
      dtype=object)

# 불리언 인덱싱으로 20회 이상 참여한 사람만 추려내기
user_idx = user.loc[user['Rating']['size'] > 20]
user_idx.hist(('Rating', 'size'), bins=100)
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000001DD97BF8470>]],
      dtype=object)

product_idx['Rating']['size'].describe()
count    2687.000000
mean      227.289170
std       246.450571
min       101.000000
25%       122.000000
50%       160.000000
75%       241.000000
max      7533.000000
Name: size, dtype: float64
user_idx['Rating']['size'].describe()
count    2549.000000
mean       36.218125
std        26.325197
min        21.000000
25%        23.000000
50%        28.000000
75%        39.000000
max       389.000000
Name: size, dtype: float64
product_idx.head()

Rating
size mean
ProductId
B00004TMFE 137.0 3.445255
B00004TUBL 558.0 4.634409
B00004TUBV 171.0 4.269006
B00004U9UY 146.0 4.726027
B000050B6U 377.0 4.175066
user_idx.head()

Rating
size mean
UserId
A03364251DGXSGA9PSR99 44.0 4.000000
A100WO06OQR8BQ 54.0 3.592593
A100ZQDV7L8PVV 22.0 3.863636
A103979529MRJY0U56QI4 42.0 4.523810
A103BJIOJSDJL1 26.0 3.115385
user_filtered = data.loc[data['UserId'].isin(user_idx.index)]
p_u_filtered = user_filtered.loc[user_filtered['ProductId'].isin(product_idx.index)]
p_u_filtered.describe(include='all')

UserId ProductId Rating Timestamp
count 26734 26734 26734.000000 2.673400e+04
unique 2512 2497 NaN NaN
top ALNFHVS3SC4FV B009FKNGGQ NaN NaN
freq 94 136 NaN NaN
mean NaN NaN 4.201055 1.357656e+09
std NaN NaN 1.119406 4.002301e+07
min NaN NaN 1.000000 1.066003e+09
25% NaN NaN 4.000000 1.344989e+09
50% NaN NaN 5.000000 1.364774e+09
75% NaN NaN 5.000000 1.383696e+09
max NaN NaN 5.000000 1.406074e+09
# timestamp 가 초 단위로 되어있으므로, .astype() 에 datetime64[s] 로 바꿔줍니다.
p_u_filtered['datetime'] = p_u_filtered['Timestamp'].astype("datetime64[s]")
p_u_filtered['hour'] = p_u_filtered['datetime'].dt.hour
p_u_filtered['dow'] = p_u_filtered['datetime'].dt.dayofweek
C:\Users\one\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  """Entry point for launching an IPython kernel.
C:\Users\one\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

C:\Users\one\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  This is separate from the ipykernel package so we can avoid doing imports until
p_u_filtered.head()

UserId ProductId Rating Timestamp datetime hour dow
2456 AVM6OR01CPJNQ B00004TMFE 2.0 1365724800 2013-04-12 0 4
2465 A34M18U6T33YDZ B00004TMFE 5.0 1396828800 2014-04-07 0 0
2492 A2VOGNBUMXSW13 B00004TMFE 2.0 1357516800 2013-01-07 0 0
2549 A38VDDNZ7A3QM0 B00004TMFE 5.0 1386028800 2013-12-03 0 1
2712 A2RQOO8VYAEZZG B00004TUBL 3.0 1403136000 2014-06-19 0 3
# 피벗 테이블은 3 가지 컬럼 정보만 필요하므로, 대상이 되는 3 개만 넣어서 새로 만들어준다.
pivot_cleaning = p_u_filtered[['Rating', 'hour', 'dow']]
# 시간, 요일 기준으로 평점정보를 피벗 테이블 한 후 heatmap 으로 확인한다.
# hour 가 0 밖에 없다는 것은, 모든 데이터가 00시에 일괄 집계되어 있음을 의미.
import seaborn as sns
pivoted = pivot_cleaning.pivot_table(index='dow', columns='hour', values='Rating').fillna(0)
sns.heatmap(pivoted)
<matplotlib.axes._subplots.AxesSubplot at 0x1dd9a744828>

pivoted = p_u_filtered.pivot_table(index='UserId', columns='ProductId', values='Rating')
corr = pivoted.corr()
corr.head()

ProductId B00004TMFE B00004TUBL B00004TUBV B00004U9UY B000050B6U B000052WYD B000052WYN B000052XW5 B000052XZP B000052YJH ... B00JL2TURM B00JX979CQ B00KAL5JAU B00KCFAZTE B00KCTER3U B00KD73PBQ B00KHGIK54 B00KHH2VOY B00KQBR9FM B00L5JHZJO
ProductId
B00004TMFE 1.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
B00004TUBL NaN 1.0 NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
B00004TUBV NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
B00004U9UY NaN NaN NaN 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
B000050B6U NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 2497 columns

# 물건을 골랐을 때, 해당 물건을 언급했던 사람들 사이의 상관관계로부터 유사한 물건을 골라주게 된다.