제12장 협동 필터 추천 알고리즘 프로그램 실현

19681 단어
본고는 저자가 처음으로 실제 데이터에 근거하여 완전한 데이터 분석을 한 것이기 때문에 프로그램의 실현에 있어 자신이 익숙한 문장을 선택해야 한다. 필자는 먼저 한 가지 방법으로 문제를 해결한 다음에 다른 사람의 분석 사고방식을 결합시켜 자신의 해결 방안을 최적화해야 한다고 생각한다.
1. 데이터베이스에 연결하여 원시 데이터를 얻는다
import pandas as pd
import pymysql
conn = pymysql.connect(host='localhost', user='root', password='password', port=3306, db='liuyan', charset='utf8')
cur = conn.cursor()     #       
cursor = cur.execute('select * from all_gzdata')     #   sql  
data = cur.fetchall()     #       
from pandas import DataFrame
df = DataFrame(list(data), columns=['realIP', 'realAreacode', 'userAgent', 'userOS',
                                    'userID', 'clientID', 'timestamp', 'timestamp_format',
                                    'pagePath', 'ymd', 'fullURL', 'fullURLId', 'hostname',
                                    'pageTitle', 'pageTitleCategoryId', 'pageTitleCategoryName',
                                    'pageTitleKw', 'fullReferrer', 'fullReferrerURL', 'organicKeyword',
                                    'source'])
cur.close()
conn.close()

2. 데이터 탐색 분석
탐색 분석은 데이터 분석을 하는 첫걸음이자 우리가 실제 데이터의 상황을 대체적으로 이해하고 그 내재된 규칙을 얻을 수 있도록 하는 것이다.
2.1 웹 유형 분석(웹 유형은'웹 주소 유형'의 상위 3자리 숫자를 가리킨다)
#      (      “    ”  3 )
df['urlcat'] = df['fullURLId'].str.extract('(\d{3})')
counts3 = df.groupby('urlcat').size().sort_values(ascending=False)
counts3 = counts3.reset_index()
counts3.columns = ['index', 'num']
counts3['prec'] = counts3['num'] / counts3['num'].sum() * 100
#         
counts_101 = df[df['urlcat'] == '101'].groupby('fullURLId').size().sort_values(ascending=False)
counts_101 = counts_101.reset_index()
counts_101.columns = ['index', 'num']
counts_101['prec'] = counts_101['num'] / counts_101['num'].sum() * 100
#         
#   107     
def count107(i):
    j = i[['fullURL']][i['fullURLId'].str.contains('107')].copy()     #       107   
    j['type'] = None     #    
    j['type'][j['fullURL'].str.contains('info/.+?/')] = u'    '
    j['type'][j['fullURL'].str.contains('info/.+?/.+?')] = u'     '
    j['type'][j['fullURL'].str.contains('/\d+?_*\d+?\.html')] = u'     '
    return j['type'].value_counts()
counts_107 = count107(df)
counts_107 = counts_107.reset_index()
counts_107.columns = ['index', 'num']
counts_107['prec'] = counts_107['num'] / counts_107['num'].sum() * 100
#             
df['baohan'] = 0
df['baohan'][df['fullURL'].str.contains('\?{0}')] = 0
df['baohan'][df['fullURL'].str.contains('\?')] = 1
df['baohan'].value_counts()
counts_bh = df[df['baohan'] == 1].groupby(df['fullURLId']).size()
counts_bh = counts_bh.reset_index()
counts_bh.columns = ['index', 'num']
#          
df['xiaguang'] = 0
df['xiaguang'][df['fullURL'].str.contains('http.*?/$')] = 1
df['xiaguang'].value_counts()
counts_xg = df[df['xiaguang'] == 1].groupby(df['urlcat']).size()
counts_xg = counts_xg.reset_index()
counts_xg.columns = ['index', 'num']
counts_xg['prec'] = counts_xg['num'] / counts_xg['num'].sum() * 100

2.2 클릭 횟수 분석
#      
df['dj'] = 1
dianji = df['dj'].groupby(df['realIP']).size()     #     IP     
dianji = dianji.reset_index()
dianji.columns = ['realIP', 'num']
dianjicishu = dianji.groupby('num').size()     #     ‘      ’       
dianjicishu = dianjicishu.reset_index()
dianjicishu.columns = ['cishu', 'renshu']
dianjicishu['prec'] = dianjicishu['renshu'] / dianjicishu['renshu'].sum() * 100     #      
dianjicishu['jilu_prec'] = dianjicishu['renshu'] / df['dj'].sum() * 100     #      
#  7         
qici = dianjicishu[dianjicishu['cishu'] > 7]
del qici['prec']
del qici['jilu_prec']
import numpy as np
qici2 = DataFrame([['8-50', 0], ['50-100', 0], ['100  ', 0]], columns=['    ', '   '])
qici2['   '][0] = qici[(7 < qici['cishu']) & (qici['cishu'] < 50)]['renshu'].sum()
qici2['   '][1] = qici[(qici['cishu'] > 50) & (qici['cishu'] < 100)]['renshu'].sum()
qici2['   '][2] = qici[qici['cishu'] > 100]['renshu'].sum()
#           
dianji_one = df.groupby(['realIP', 'fullURLId']).size()
dianji_one = dianji_one.reset_index()
dianji_one.columns = ['realIP', 'fullURLId', 'num']
dianji_one = dianji_one[dianji_one['num'] == 1]
dianji_one2 = dianji_one.groupby('fullURLId').size()
dianji_one2 = dianji_one2.reset_index()
dianji_one2.columns = ['fullURLId', 'num']
dianji_one2['prec'] = dianji_one2['num'] / dianji_one2['num'].sum() * 100
dianji_one2 = dianji_one2.sort_values('prec', ascending=False)
#            
dianji_one_web = df.groupby(['fullURL', 'realIP']).size()
dianji_one_web = dianji_one_web.reset_index()
dianji_one_web.columns = ['fullURL', 'realIP', 'num']
dianji_one_web = dianji_one_web[dianji_one_web['num'] == 1]
dianji_one_web2 = dianji_one_web.groupby('fullURL').size()
dianji_one_web2 = dianji_one_web2.reset_index()
dianji_one_web2.columns = ['fullURL', 'num']

2.3 웹 페이지 순위
#     
dianji_web = df.groupby('fullURL').size()
dianji_web = dianji_web.reset_index()
dianji_web.columns = ['fullURL', 'num']
dianji_web = dianji_web.sort_values('num', ascending=False)
#     
dianji2 = df.groupby(['urlcat', 'realIP']).size()
dianji2 = dianji2.reset_index()
dianji2.columns = ['urlcat', 'realIP', 'num']
dianji2['counts'] = 1
leixingdianji = DataFrame([['   (       )', 0, 0, 0.0],
                           ['   ', 0, 0, 0.0]], columns=['html    ','     ', '   ', '     '])
leixingdianji['     '][0] = dianji2[dianji2['urlcat'] == '107']['num'].sum()
leixingdianji['     '][1] = dianji2[dianji2['urlcat'] == '101']['num'].sum()
leixingdianji['   '][0] = dianji2[dianji2['urlcat'] == '107']['counts'].sum()
leixingdianji['   '][1] = dianji2[dianji2['urlcat'] == '101']['counts'].sum()
leixingdianji['     '][0] = leixingdianji['     '][0] / leixingdianji['   '][0]
leixingdianji['     '][1] = leixingdianji['     '][1] / leixingdianji['   '][1]

3. 데이터 사전 처리
원시 데이터의 탐색 분석을 토대로 분석과 무관하거나 모델이 처리해야 할 데이터를 발견하고 이런 데이터에 대해 처리한다.이때의 데이터 예처리는 데이터 세척, 데이터 변환, 속성 규약을 포함한다.
3.1 데이터 세척(상관없는 기록 및 중복된 기록 삭제)
#         ( midques_   )
data1= df[~df['fullURL'].str.contains('midques_')]     #  2989   
count1 = df[df['fullURL'].str.contains('midques_')]['fullURL'].count()     #          =7
df['fullURL'].count()     #       =2996

#  (  -    )       
count2 = data1[data1['pageTitle'] == '    -    ']['pageTitle'].count()     #          =76
data2 = data1[~(data1['pageTitle'] == '    -    ')]     # 2913   
#        
count3 = data2[data2['pageTitle'] == '      ']['pageTitle'].count()     #          =14
data3 = data2[~(data2['pageTitle'] == '      ')]     # 2899   
#           ,    ask,info,fagui,lawyer
data4 = data3[((data3['fullURL'].str.contains('ask')) | (data3['fullURL'].str.contains('info')) |
               (data3['fullURL'].str.contains('fagui')) | (data3['fullURL'].str.contains('lawyer')))]     # 2667   
count4 = data3[~((data3['fullURL'].str.contains('ask')) | (data3['fullURL'].str.contains('info')) |
                 (data3['fullURL'].str.contains('fagui')) | (data3['fullURL'].str.contains('lawyer')))]['fullURL'].count()     #          =232
#              
count5 = data4[((data4['pageTitle'].str.contains('    ')) | (data4['pageTitle'].str.contains('        ')))]['pageTitle'].count()     #          =38
data5 = data4[~((data4['pageTitle'].str.contains('    ')) | (data4['pageTitle'].str.contains('        ')))]     # 2629   
#        ?   (     199)
data6 = data5[~((data5['fullURL'].str.contains('\?')) & (data5['fullURLId'].str.contains('199')))]     # 2627   
count6 = data5[(data5['fullURL'].str.contains('\?')) & (data5['fullURLId'].str.contains('199'))]['fullURL'].count()     #          =2
#   .html         
count7 = data6[~data6['fullURL'].str.contains('html')]['fullURL'].count()     #          =107
data7 = data6[data6['fullURL'].str.contains('html')]     # 2520   
#      
data7[data7.duplicated(['realIP', 'timestamp', 'fullURL'])][['realIP', 'timestamp', 'fullURL']].sort_values(
    ['realIP', 'timestamp'])     # df.duplicated()       Series,         
data8 = data7.drop_duplicates()     # 1583   

3.2 데이터 변환
data8_2=data8.copy()
data8_2['fullURL']=data8_2['fullURL'].str.replace('_\d.html','.html')
#data8_2[data8_2['fullURL'].str.contains('_\d\.html')]     #                    
data8_2=data8_2.drop_duplicates()     #       
del data8_2['urlcat']

#             
####  Navicat Premium    gzdata   # :``             ,                       。
                                                #         ,               ,            
          python list ,             :
( :                  )
def insertNews(data):
    for index in range(len(data)):
        newsobj = data.iloc[index]
        newsobjs = [newsobj.get('realIP'), newsobj.get('realAreacode'),newsobj.get('userAgent'),newsobj.get('userOS'),
                    newsobj.get('userID'), newsobj.get('clientID'),newsobj.get('timestamp'),newsobj.get('timestamp_format'),
                    newsobj.get('pagePath'), newsobj.get('ymd'),newsobj.get('fullURL'), newsobj.get('fullURLId'),
                    newsobj.get('hostname'),newsobj.get('pageTitle'),newsobj.get('pageTitleCategoryId'),
                    newsobj.get('pageTitleCategoryName'),newsobj.get('pageTitleKw'),
                    newsobj.get('fullReferrer'), newsobj.get('fullReferrerURL'),newsobj.get('organicKeyword'), newsobj.get('source')]
        conn = pymysql.connect(host='localhost', user='root', password='password', port=3306, db='liuyan',charset='gbk')
        cur = conn.cursor()
        cur.execute("insert into gzdata(realIP,realAreacode,userAgent,userOS,userID,clientID,timestamp,timestamp_format,pagePath,ymd,fullURL,fullURLId,hostname,pageTitle,pageTitleCategoryId,pageTitleCategoryName,pageTitleKw,fullReferrer,fullReferrerURL,organicKeyword,source)"
                    "VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" %tuple(newsobjs))
        #       ,               
        conn.commit()
        #     
        cur.close()
        #     
        conn.close()
insertNews(data8_2)

#              
import pymysql
from pandas import DataFrame
conn2=pymysql.connect(host='localhost',user='root',password='password',port=3306,db='liuyan',charset='utf8')
cur2=conn2.cursor()
cursor2=cur2.execute('select * from gzdata')
data2=cur2.fetchall()
df2=DataFrame(list(data2),columns=['realIP', 'realAreacode', 'userAgent',
                                  'userOS','userID', 'clientID', 'timestamp',
                                  'timestamp_format','pagePath', 'ymd',
                                  'fullURL', 'fullURLId', 'hostname',
                                  'pageTitle', 'pageTitleCategoryId',
                                  'pageTitleCategoryName','pageTitleKw',
                                  'fullReferrer', 'fullReferrerURL',
                                  'organicKeyword','source'])
df2['type']='weizhi'
df2['urlcat'] = df2['fullURLId'].str.extract('(\d{3})')
df2.loc[df2['urlcat']=='101','type']='zixun'
df2.loc[df2['urlcat']=='107','type']=u'zhishi'
#   (199)          ,      
df2.loc[((df2['fullURL'].str.contains('ask'))|(df2['fullURL'].str.contains('askzt'))),'type']='zixun'     #     ‘ask’  'askzt'   
df2.loc[((df2['fullURL'].str.contains('zhishi'))|(df2['fullURL'].str.contains('faguizt'))),'type']='zhishi'     #    ‘zhishi’  ‘faguizt’   
df2.groupby('type').size()                                      #df2[df2.type=='weizhi'][['fullURL','fullURLId','urlcat','type']]     'weizhi'      
#       
df2['type2']='weizhi2'
df2.loc[((df2['type']=='zhishi')&(df2['fullURL'].str.contains('info'))),'type2']=df2['fullURL'].str.extract('info\/(.*?)\/',expand=False)
df2['type3']='weizhi3'
df2.loc[((df2['type']=='zhishi')&(df2['fullURL'].str.contains('info'))),'type3']=df2['fullURL'].str.extract('info\/.*?\/(.*?)\/',expand=False)
#          
hunyin_count=df2[((df2['type']=='zhishi')&(df2['type2']=='hunyin'))].groupby('fullURL').size()
hunyin_count=hunyin_count.reset_index()
hunyin_count.columns=['fullURL','num']
hunyin_count2=hunyin_count.groupby('num').size()
hunyin_count2=hunyin_count2.reset_index()
hunyin_count2.columns=['    ','    ']
hunyin_count.groupby('num').sum()

3.3 속성 규약
추천 시스템 모델의 입력 데이터가 필요하기 때문에 처리된 데이터에 대해 속성 규약을 하고 모델이 필요로 하는 속성을 추출해야 한다.본 사례에서 모델에 필요한 데이터 속성은 사용자와 사용자가 방문한 웹 페이지이다.
import numpy as np
a=df2['fullURL'].unique()     #        ,  a        。
b=sorted(a.tolist())     #   'fullURL'         ,     sorted(set(df2['fullURL']))
goods_matrix=DataFrame(np.arange(len(df2['realIP'][:1000])*len(b[:200])).reshape(len(df2['realIP'][:1000]),len(b[:200])),
                       index=df2['realIP'][:1000],columns=b[:200])     #    IP   ,        dataframe,    IP1000 ,  200 。

주: 본인의 컴퓨터 설정이 너무 낮아서 1000명의 사용자의 정보를 골라 계속 분석했기 때문에 다음 결과는 좋지 않습니다.
4. 모델 구성
협동 필터 추천 알고리즘은 사용자 기반과 물품 기반으로 나뉘는데 본 사례는 물품 기반의 협동 필터 알고리즘을 선택한다. 즉,'어떤 물품을 사용자 B에게 추천합니까?'라고 대답한다.
4.1 사용자가 아이템에 대한 관심 매트릭스 생성
#    goods_matrix   (                   )  , 0-1  。
import time
start=time.clock()
for i in range(goods_matrix.index.size):
    for j in range(goods_matrix.columns.size):
        if df2.loc[i,'fullURL']==goods_matrix.columns[j]:
            goods_matrix.iloc[i, j]=1     #     df2.loc[i,'fullURL'] goods_matrix  j     ,    1,   0
        else:
            goods_matrix.iloc[i, j]=0
end=time.clock()
print(end-start,"seconds process time")

4.2 훈련집과 테스트집 획득
#    goods_matrix   ,    10%            ,   90%            。
import random
#random.shuffle(goods_matrix)              #   :random.shuffle(list)  None,         ,               ,         。
all_matrix=np.random.permutation(goods_matrix)          #   :random.permutation(list)              ,               ,         。all_matrix        。
all_matrix2=DataFrame(all_matrix,index=df2['realIP'][:1000],columns=b[:200])   #       dataframe
#         
train=all_matrix2.iloc[:int(len(all_matrix)*0.9),:]
test=all_matrix2.iloc[int(len(all_matrix)*0.9):,:]
train2=train.as_matrix()     #   
test2=test.as_matrix()     #   

4.3 협동 필터 알고리즘 구축
import numpy as np
def Jaccard(a,b):     #             ,  0-1    
    return 1.0*(a*b).sum()/(a+b-a*b).sum()

class Recommender():
    sim = None     #      
    def similarity(self,x,distance):
        y = np.ones((len(x), len(x)))
        for i in range(len(x)):
            for j in range(len(x)):
                y[i, j] = distance(x[i], x[j])
        return y
    def fit(self,x, distance=Jaccard):     #     ,       ,  x               (0-1  )
        self.sim = self.similarity(x, distance)

    def recommend(self,a):     #     
        return np.dot(self.sim,a)*(1-a)

4.4 테스트 집합에서의 사용자 행동 예측
#         ,              ,          。
train3=train2.T     #              ,               ,       。
test3=test2.T
r=Recommender()
sim=r.fit(train3)        #        
result=r.recommend(test3)     #       
sim2=DataFrame(sim)
result2=DataFrame(result)   #    dataframe,            
result2.index=test.columns
result2.columns=test.index

4.5 IP당 K개 웹 사이트 추천
이곳의 추천 함수는 블로그를 클릭하여 링크를 여는 방법을 참고한 것이다.
4
from pandas import Series
def tuijian_result(K,recomMatrix):
    recomMatrix.fillna(0,inplace=True)
    xietong = ['xietong' + str(K) for K in range(1, K + 1)]
    tuijian = DataFrame([], index=recomMatrix.columns, columns=xietong)
    for i in range(len(recomMatrix.columns)):
        temp = result2.sort_values(by=recomMatrix.columns[i], ascending=False)
        k = 0
        while k < K:
            tuijian.iloc[i, k] = temp.index[k]
            if temp.iloc[k, i] == 0:
                tuijian.iloc[i, k:K] = np.nan
                break
            k = k + 1
        return tuijian

start1=time.clock()
final_result=tuijian_result(3,result2)
end1=time.clock()
이로써 본고는 하나의 추천 알고리즘만 제시했고 다른 추천 모델을 고려할 수 있으며 각 모델이 서로 다른 추천 값에 대한 평가 지표 값을 비교하고 각 모델에 있는 F1 지표를 계산할 수 있다.

좋은 웹페이지 즐겨찾기