目录
一、背景
智能推荐服务是提高电子商务网站销售转化率的重要技术手段之一。它与传统的搜索技术有着重要的区别,智能推荐服务能够更加精准地提供信息,节省用户找寻信息的时间,提高找寻信息的准确度。通过建立智能推荐系统提高服务效率,帮助消费者节约时间成本,帮助企业制定有针对性的营销战略方案,促进企业长期、稳定、高速发展。
二、数据导入
- 找到MySQL安装位置的bin目录(我的是E:\MySQL Server 8.0\bin)
- 使用快捷键WIN+R,输入cmd打开命令行
- 切换盘符(如果MySQL安装在C盘则不需要这一步)
- 切换路径到 E:\MySQL Server 8.0\bin
- 输入mysql -u root -p ,u字母后面是mysql的登录数据库用户名,我的登录数据库用户名是root,然后回车,会让输入登录mysql数据库的密码
2.1 创建数据库
数据库名称:test
字符集:utf8mb4
排序规则:utf8mb4 general_ci
1、创建数据库
create database test default character set utf8mb4 collate utf8mb4_general_ci;
2、使用数据库
use test;
2.2 导入查询
source D:\\My_Data\\chapter11\\demo\\data\\7law.sql
D:\\My_Data\\chapter11\\demo\\data\\7law.sql 是.sql文件存放的位置
注意:这里需要使用双反斜杠,对原有的路径单反斜杠进行转义
2.3 出现的错误
ERROR 1:
ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline.
这个错误信息是MySQL数据库报出的错误,它表示在当前的行格式下,某个表的行大小超过了MySQL所允许的最大值8126字节。造成这个问题的原因是表定义了太多的列,或者表中包含了太多的大型数据类型。在MySQL中,BLOB和TEXT类型的数据会被存储在行中,而不是存储在独立的数据页中。因此,如果表中包含太多的大型数据类型,就会导致行大小超过MySQL的限制。
ERROR 2:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Media Center PC 6.0' at line 1
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.NET4.0C' at line 1
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.NET4.0E)', 'Windows 7', '1408129230.1422933436', '1408129230.1422933436', 14229' at line 1这里展示的是语法错误,实际上是由于7law.sql文件创建表的编码(gbk)与数据库编码(utf8)不一致导致的。(注意:使用Navicat Premium导入时会自动处理这个错误,不代表错误不存在)
2.4 解决办法
使用ROW_FORMAT=DYNAMIC或ROW_FORMAT=COMPRESSED行格式,这会将BLOB类型的数据存储在独立的数据页中,从而避免行大小超过MySQL的限制。修改CHARSET=gbk为CHARSET=utf8
原始7law.sql部分数据
-- --------------------------------------------------------
-- 主机: 127.0.0.1
-- 服务器版本: 10.0.17-MariaDB - mariadb.org binary distribution
-- 服务器操作系统: Win64
-- HeidiSQL 版本: 9.1.0.4867
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-- 导出 表 new.all_gzdata 结构
DROP TABLE IF EXISTS `all_gzdata`;
CREATE TABLE IF NOT EXISTS `all_gzdata` (
`realIP` bigint(20) DEFAULT '0' COMMENT '真实ip',
`realAreacode` int(11) DEFAULT '0' COMMENT '地区编号',
`userAgent` varchar(500) DEFAULT '' COMMENT '浏览器代理',
`userOS` varchar(100) DEFAULT '' COMMENT '用户浏览器类型',
`userID` varchar(100) DEFAULT '' COMMENT 'userid',
`clientID` varchar(100) DEFAULT '' COMMENT 'clientid',
`timestamp` bigint(20) DEFAULT '0' COMMENT '时间戳',
`timestamp_format` varchar(20) DEFAULT '' COMMENT '格式化的时间',
`pagePath` varchar(1000) DEFAULT '' COMMENT '路径',
`ymd` int(11) DEFAULT '0',
`fullURL` varchar(1000) DEFAULT '',
`fullURLId` varchar(100) DEFAULT '',
`hostname` varchar(500) DEFAULT '',
`pageTitle` varchar(1000) DEFAULT '',
`pageTitleCategoryId` int(11) DEFAULT '0',
`pageTitleCategoryName` varchar(500) DEFAULT '',
`pageTitleKw` varchar(1000) DEFAULT '',
`fullReferrer` varchar(1000) DEFAULT '',
`fullReferrerURL` varchar(1000) DEFAULT '',
`organicKeyword` varchar(500) DEFAULT '',
`source` varchar(500) DEFAULT '',
KEY `Index 1` (`realAreacode`,`timestamp_format`,`realIP`,`userOS`,`userID`,`clientID`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk ROW_FORMAT=COMPACT;
-- 正在导出表 new.all_gzdata 的数据:~742,859 rows (大约)
/*!40000 ALTER TABLE `all_gzdata` DISABLE KEYS */;
INSERT INTO `all_gzdata` (`realIP`, `realAreacode`, `userAgent`, `userOS`, `userID`, `clientID`, `timestamp`, `timestamp_format`, `pagePath`, `ymd`, `fullURL`, `fullURLId`, `hostname`, `pageTitle`, `pageTitleCategoryId`, `pageTitleCategoryName`, `pageTitleKw`, `fullReferrer`, `fullReferrerURL`, `organicKeyword`, `source`) VALUES
(2683657840, 140100, 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0', 'Windows XP', '785022225.1422973265', '785022225.1422973265', 1422973268278, '2015-02-03 22:21:08', '/info/hunyin/hunyinfagui/201404102884290_6.html', 20150203, 'http://www.lawtime.cn/info/hunyin/hunyinfagui/201404102884290_6.html', '107001', 'www.lawtime.cn', '广东省人口与计划生育条例全文2014 - 法律快车婚姻法', 31, '故意伤害', '计划生育', NULL, NULL, NULL, NULL),
修改后7law.sql部分数据
删除 ENGINE=InnoDB DEFAULT CHARSET=gbk ROW_FORMAT=COMPACT;
添加 ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-- 导出 表 new.all_gzdata 结构
DROP TABLE IF EXISTS `all_gzdata`;
CREATE TABLE IF NOT EXISTS `all_gzdata` (
`realIP` bigint(20) DEFAULT '0' COMMENT '真实ip',
`realAreacode` int(11) DEFAULT '0' COMMENT '地区编号',
`userAgent` varchar(500) DEFAULT '' COMMENT '浏览器代理',
`userOS` varchar(100) DEFAULT '' COMMENT '用户浏览器类型',
`userID` varchar(100) DEFAULT '' COMMENT 'userid',
`clientID` varchar(100) DEFAULT '' COMMENT 'clientid',
`timestamp` bigint(20) DEFAULT '0' COMMENT '时间戳',
`timestamp_format` varchar(20) DEFAULT '' COMMENT '格式化的时间',
`pagePath` varchar(1000) DEFAULT '' COMMENT '路径',
`ymd` int(11) DEFAULT '0',
`fullURL` varchar(1000) DEFAULT '',
`fullURLId` varchar(100) DEFAULT '',
`hostname` varchar(500) DEFAULT '',
`pageTitle` varchar(1000) DEFAULT '',
`pageTitleCategoryId` int(11) DEFAULT '0',
`pageTitleCategoryName` varchar(500) DEFAULT '',
`pageTitleKw` varchar(1000) DEFAULT '',
`fullReferrer` varchar(1000) DEFAULT '',
`fullReferrerURL` varchar(1000) DEFAULT '',
`organicKeyword` varchar(500) DEFAULT '',
`source` varchar(500) DEFAULT '',
KEY `Index 1` (`realAreacode`,`timestamp_format`,`realIP`,`userOS`,`userID`,`clientID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- 正在导出表 new.all_gzdata 的数据:~742,859 rows (大约)
/*!40000 ALTER TABLE `all_gzdata` DISABLE KEYS */;
INSERT INTO `all_gzdata` (`realIP`, `realAreacode`, `userAgent`, `userOS`, `userID`, `clientID`, `timestamp`, `timestamp_format`, `pagePath`, `ymd`, `fullURL`, `fullURLId`, `hostname`, `pageTitle`, `pageTitleCategoryId`, `pageTitleCategoryName`, `pageTitleKw`, `fullReferrer`, `fullReferrerURL`, `organicKeyword`, `source`) VALUES
(2683657840, 140100, 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0', 'Windows XP', '785022225.1422973265', '785022225.1422973265', 1422973268278, '2015-02-03 22:21:08', '/info/hunyin/hunyinfagui/201404102884290_6.html', 20150203, 'http://www.lawtime.cn/info/hunyin/hunyinfagui/201404102884290_6.html', '107001', 'www.lawtime.cn', '广东省人口与计划生育条例全文2014 - 法律快车婚姻法', 31, '故意伤害', '计划生育', NULL, NULL, NULL, NULL),
重新导入数据
source D:\\My_Data\\chapter11\\demo\\data\\7law.sql
数据信息
mysql> select count(1) from all_gzdata;
+----------+
| count(1) |
+----------+
| 837450 |
+----------+
1 row in set (0.79 sec)
三、数据抽取
# 导入需要用到的库
import time
import pandas as pd
from sqlalchemy import create_engine
# 记录程序开始时间
start_time = time.time()
print("开始时间:", start_time)
# 创建一个 SQLAlchemy 引擎对象,使用 root 用户名和密码连接本地的 test 数据库
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
# 从指定表格 all_gzdata 中读取数据,每次读取 10000 行数据,并返回一个迭代器
data_iter = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
# 遍历迭代器中的每个 DataFrame 对象,依次将它们写入到本地 csv 文件中
for i, df in enumerate(data_iter):
print(f"正在处理第 {i + 1} 个 DataFrame...")
df.to_csv(f'../tmp/all_gzdata.csv', index=False, encoding='utf-8', mode='a')
# 计算程序运行时间并输出
print("运行时间:", time.time() - start_time)
以上代码中,使用了
pd.read_sql()
方法的chunksize
参数,返回了一个迭代器data_iter
,其中包含多个 DataFrame 对象,每个对象中包含10000
行数据。接下来,使用for
循环来遍历迭代器中的每个 DataFrame 对象,并将它们依次写入到本地 csv 文件中。在每次写入时,都需要指定mode='a'
参数,表示以追加模式打开文件,以便将多个 DataFrame 对象的数据都写入到同一个 csv 文件中。
四、数据探索分析
4.1 分析网页类型
import pandas as pd
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
# 分析网页类型
counts = [i['fullURLId'].value_counts() for i in sql] # 逐块统计
counts = counts.copy()
counts = pd.concat(counts).groupby(level=0).sum() # 合并统计结果,把相同的统计项合并(即按index分组并求和)
counts = counts.reset_index() # 重新设置index,将原来的index作为counts的一列。
counts.columns = ['index', 'num'] # 重新设置列名,主要是第二列,默认为0
counts['type'] = counts['index'].str.extract('(\d{3})') # 提取前三个数字作为类别id
counts_ = counts[['type', 'num']].groupby('type').sum() # 按类别合并
counts_.sort_values(by='num', ascending=False, inplace=True) # 降序排列
counts_['ratio'] = counts_.iloc[:, 0] / counts_.iloc[:, 0].sum()
print(counts_)
输出:
num ratio
type
101 411665 0.491570
199 201426 0.240523
107 182900 0.218401
301 18430 0.022007
102 17357 0.020726
106 3957 0.004725
103 1715 0.002048
4.2 网页107类型中的内部统计
import pandas as pd
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
# 因为只有107001一类,但是可以继续细分成三类:知识内容页、知识列表页、知识首页
def count107(i): # 自定义统计函数
j = i[['fullURL']][i['fullURLId'].str.contains('107')].copy() # 找出类别包含107的网址
j['type'] = None # 添加空列
j['type'][j['fullURL'].str.contains('info/.+?/')] = '知识首页'
j['type'][j['fullURL'].str.contains('info/.+?/.+?')] = '知识列表页'
j['type'][j['fullURL'].str.contains('/\d+?_*\d+?\.html')] = '知识内容页'
return j['type'].value_counts()
counts2 = [count107(i) for i in sql] # 逐块统计
counts2 = pd.concat(counts2).groupby(level=0).sum() # 合并统计结果
# 计算各个部分的占比
res107 = pd.DataFrame(counts2)
# res107.reset_index(inplace=True)
res107.index.name = '107类型'
res107.rename(columns={'type': 'num'}, inplace=True)
res107['比例'] = res107['num'] / res107['num'].sum()
res107.reset_index(inplace=True)
print(res107)
输出:
107类型 num 比例
0 知识内容页 164243 0.897993
1 知识列表页 9656 0.052794
2 知识首页 9001 0.049213
4.3 统计带"?"问号网址类型统计
import pandas as pd
from sqlalchemy import create_engine
print('4.3 统计带"?"问号网址类型统计')
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
def countquestion(i): # 自定义统计函数
j = i[['fullURLId']][i['fullURL'].str.contains('\?')].copy() # 找出类别包含107的网址
return j
counts3 = [countquestion(i)['fullURLId'].value_counts() for i in sql]
counts3 = pd.concat(counts3).groupby(level=0).sum()
# 求各个类型的占比并保存数据
df1 = pd.DataFrame(counts3)
df1['perc'] = df1['fullURLId'] / df1['fullURLId'].sum() * 100
df1.sort_values(by='fullURLId', ascending=False, inplace=True)
print(df1.round(4))
输出:
4.3 统计带"?"问号网址类型统计
fullURLId perc
1999001 64718 98.8182
301001 356 0.5436
107001 346 0.5283
101003 47 0.0718
102002 25 0.0382
4.4 统计199类型中的具体类型占比
import pandas as pd
from sqlalchemy import create_engine
print('4.4 统计199类型中的具体类型占比')
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
def page199(i): # 自定义统计函数
j = i[['fullURL', 'pageTitle']][(i['fullURLId'].str.contains('199')) &
(i['fullURL'].str.contains('\?'))]
j['pageTitle'].fillna('空', inplace=True)
j['type'] = '其他' # 添加空列
j['type'][j['pageTitle'].str.contains('法律快车-律师助手')] = '法律快车-律师助手'
j['type'][j['pageTitle'].str.contains('咨询发布成功')] = '咨询发布成功'
j['type'][j['pageTitle'].str.contains('免费发布法律咨询')] = '免费发布法律咨询'
j['type'][j['pageTitle'].str.contains('法律快搜')] = '快搜'
j['type'][j['pageTitle'].str.contains('法律快车法律经验')] = '法律快车法律经验'
j['type'][j['pageTitle'].str.contains('法律快车法律咨询')] = '法律快车法律咨询'
j['type'][(j['pageTitle'].str.contains('_法律快车')) |
(j['pageTitle'].str.contains('-法律快车'))] = '法律快车'
j['type'][j['pageTitle'].str.contains('空')] = '空'
return j
counts4 = [page199(i) for i in sql] # 逐块统计
counts4 = pd.concat(counts4)
d1 = counts4['type'].value_counts()
d2 = counts4[counts4['type'] == '其他']
# 求各个部分的占比并保存数据
df1_ = pd.DataFrame(d1)
df1_['perc'] = df1_['type'] / df1_['type'].sum() * 100
df1_.sort_values(by='type', ascending=False, inplace=True)
print(df1_)
输出:
4.4 统计199类型中的具体类型占比
type perc
法律快车-律师助手 49894 77.094471
法律快车法律咨询 6421 9.921506
咨询发布成功 5220 8.065762
快搜 1943 3.002256
法律快车 818 1.263945
其他 359 0.554714
法律快车法律经验 59 0.091165
空 4 0.006181
4.5 统计瞎逛用户中各个类型占比
import pandas as pd
from sqlalchemy import create_engine
print("4.5 统计瞎逛用户中各个类型占比")
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
def xiaguang(i): # 自定义统计函数
j = i.loc[(i['fullURL'].str.contains('\.html')) == False,
['fullURL', 'fullURLId', 'pageTitle']]
return j
counts5 = [xiaguang(i) for i in sql]
counts5 = pd.concat(counts5)
xg1 = counts5['fullURLId'].value_counts()
# 求各个部分的占比
xg_ = pd.DataFrame(xg1)
xg_.reset_index(inplace=True)
xg_.columns = ['index', 'num']
xg_['perc'] = xg_['num'] / xg_['num'].sum() * 100
xg_.sort_values(by='num', ascending=False, inplace=True)
xg_['type'] = xg_['index'].str.extract('(\d{3})') # 提取前三个数字作为类别id
xgs_ = xg_[['type', 'num']].groupby('type').sum() # 按类别合并
xgs_.sort_values(by='num', ascending=False, inplace=True) # 降序排列
xgs_['percentage'] = xgs_['num'] / xgs_['num'].sum() * 100
print(xgs_.round(4))
输出:
4.5 统计瞎逛用户中各个类型占比
num percentage
type
199 117124 71.2307
107 17843 10.8515
102 17357 10.5559
101 7130 4.3362
106 3957 2.4065
301 1018 0.6191
4.6 统计用户浏览网页次数的情况
import pandas as pd
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
counts1 = [i['realIP'].value_counts() for i in sql] # 分块统计各个IP的出现次数
counts1 = pd.concat(counts1).groupby(level=0).sum() # 合并统计结果,level=0表示按照index分组
print(counts1)
counts1_ = pd.DataFrame(counts1)
counts1['realIP'] = counts1.index.tolist()
counts1_[1] = 1 # 添加1列全为1
hit_count = counts1_.groupby('realIP').sum() # 统计各个“不同点击次数”分别出现的次数
# 也可以使用counts1_['realIP'].value_counts()功能
hit_count.columns = ['用户数']
hit_count.index.name = '点击次数'
# 统计1~7次、7次以上的用户人数
hit_count.sort_index(inplace=True)
hit_count_7 = hit_count.iloc[:7, :]
time = hit_count.iloc[7:, 0].sum() # 统计点击次数7次以上的用户数
hit_count_7 = hit_count_7.append([{'用户数': time}], ignore_index=True)
hit_count_7.index = ['1', '2', '3', '4', '5', '6', '7', '7次以上']
hit_count_7['用户比例'] = hit_count_7['用户数'] / hit_count_7['用户数'].sum()
print(hit_count_7)
输出:
82033 2
95502 1
103182 1
116010 2
136206 1
..
4294809358 2
4294811150 1
4294852154 3
4294865422 2
4294917690 1
Name: realIP, Length: 230149, dtype: int64
用户数 用户比例
1 132119 0.574059
2 44175 0.191941
3 17573 0.076355
4 10156 0.044128
5 5952 0.025862
6 4132 0.017954
7 2632 0.011436
7次以上 13410 0.058267
4.7 分析浏览次数为一次的用户的行为
import pandas as pd
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
sql = pd.read_sql('all_gzdata', engine.connect(), chunksize=10000)
counts1 = [i['realIP'].value_counts() for i in sql] # 分块统计各个IP的出现次数
counts1 = pd.concat(counts1).groupby(level=0).sum() # 合并统计结果,level=0表示按照index分组
print(counts1)
counts1_ = pd.DataFrame(counts1)
counts1['realIP'] = counts1.index.tolist()
counts1_[1] = 1 # 添加1列全为1
hit_count = counts1_.groupby('realIP').sum() # 统计各个“不同点击次数”分别出现的次数
# 也可以使用counts1_['realIP'].value_counts()功能
hit_count.columns = ['用户数']
hit_count.index.name = '点击次数'
# 统计1~7次、7次以上的用户人数
hit_count.sort_index(inplace=True)
hit_count_7 = hit_count.iloc[:7, :]
time = hit_count.iloc[7:, 0].sum() # 统计点击次数7次以上的用户数
hit_count_7 = hit_count_7.append([{'用户数': time}], ignore_index=True)
hit_count_7.index = ['1', '2', '3', '4', '5', '6', '7', '7次以上']
hit_count_7['用户比例'] = hit_count_7['用户数'] / hit_count_7['用户数'].sum()
print(hit_count_7)
# 分析浏览一次的用户行为
engine = create_engine('mysql+pymysql://root:011228@localhost:3306/test?charset=utf8')
all_gzdata = pd.read_sql_table('all_gzdata', con=engine.connect()) # 读取all_gzdata数据
# 对realIP进行统计
# 提取浏览1次网页的数据
real_count = pd.DataFrame(all_gzdata.groupby("realIP")["realIP"].count())
real_count.columns = ["count"]
real_count["realIP"] = real_count.index.tolist()
user_one = real_count[(real_count["count"] == 1)] # 提取只登录一次的用户
user_one = user_one.set_index("realIP")
real_one = pd.merge(user_one, all_gzdata, left_index=True, right_on="realIP")
# 统计浏览一次的网页类型
URL_count = pd.DataFrame(real_one.groupby("fullURLId")["fullURLId"].count())
URL_count.columns = ["count"]
URL_count.sort_values(by='count', ascending=False, inplace=True) # 降序排列
# 统计排名前4和其他的网页类型
URL_count_4 = URL_count.iloc[:4, :]
time = hit_count.iloc[4:, 0].sum() # 统计其他的
URLindex = URL_count_4.index.values
URL_count_4 = URL_count_4.append([{'count': time}], ignore_index=True)
URL_count_4.index = [URLindex[0], URLindex[1], URLindex[2], URLindex[3],
'其他']
URL_count_4['比例'] = URL_count_4['count'] / URL_count_4['count'].sum()
print(URL_count_4)
# 在浏览1次的前提下, 得到的网页被浏览的总次数
fullURL_count = pd.DataFrame(real_one.groupby("fullURL")["fullURL"].count())
fullURL_count.columns = ["count"]
fullURL_count["fullURL"] = fullURL_count.index.tolist()
fullURL_count.sort_values(by='count', ascending=False, inplace=True) # 降序排列
print(fullURL_count)
输出:
count 比例
101003 102560 0.649011
107001 19443 0.123037
1999001 9381 0.059364
301001 515 0.003259
其他 26126 0.165328
count fullURL
fullURL
http://www.lawtime.cn/info/shuifa/slb/201211197... 1013 http://www.lawtime.cn/info/shuifa/slb/20121119...
http://www.lawtime.cn/info/hunyin/lhlawlhxy/201... 501 http://www.lawtime.cn/info/hunyin/lhlawlhxy/20...
http://www.lawtime.cn/ask/question_925675.html 423 http://www.lawtime.cn/ask/question_925675.html
http://www.lawtime.cn/info/shuifa/slb/201211197... 367 http://www.lawtime.cn/info/shuifa/slb/20121119...
http://www.lawtime.cn/ask/exp/13655.html 301 http://www.lawtime.cn/ask/exp/13655.html
... ... ...
http://www.lawtime.cn/ask/question_4230028.html 1 http://www.lawtime.cn/ask/question_4230028.html
http://www.lawtime.cn/ask/question_422994.html 1 http://www.lawtime.cn/ask/question_422994.html
http://www.lawtime.cn/ask/question_4229836.html 1 http://www.lawtime.cn/ask/question_4229836.html
http://www.lawtime.cn/ask/question_4229778.html 1 http://www.lawtime.cn/ask/question_4229778.html
http://zm10.sm.cn/?src=http%3A%2F%2Fwww.lawtime... 1 http://zm10.sm.cn/?src=http%3A%2F%2Fwww.lawtim...
[88035 rows x 2 columns]
五、构建智能推荐模型
基于物品的协同过滤算法(ItemCF)是一种在推荐系统中广泛使用的技术。该技术通过分析用户或者事物之间的相似性,来预测用户可能感兴趣的内容并将此内容推荐给用户。该算法的核心思想就是:给用户推荐那些和他们之前喜欢的物品相似的物品。主要可分为两步:
(1) 计算物品之间的相似度,建立相似度矩阵。
(2) 根据物品的相似度和用户的历史行为给用户生成推荐列表。
5.1 数据清洗
1、删除不符合规则的网页
import re
from random import sample
import pandas as pd
import pymysql as pm
# 读取数据
con = pm.connect(host='localhost', user='root', password='011228', database='test', charset='utf8')
data = pd.read_sql('select * from all_gzdata', con=con)
con.close() # 关闭连接
# 取出107类型数据
index107 = [re.search('107', str(i)) != None for i in data.loc[:, 'fullURLId']]
data_107 = data.loc[index107, :]
# 在107类型中筛选出婚姻类数据
index = [re.search('hunyin', str(i)) != None for i in data_107.loc[:, 'fullURL']]
data_hunyin = data_107.loc[index, :]
# 提取所需字段(realIP、fullURL)
info = data_hunyin.loc[:, ['realIP', 'fullURL']]
# 去除网址中“?”及其后面内容
da = [re.sub('\?.*', '', str(i)) for i in info.loc[:, 'fullURL']]
info.loc[:, 'fullURL'] = da # 将info中‘fullURL’那列换成da
# 去除无html网址
index = [re.search('\.html', str(i)) != None for i in info.loc[:, 'fullURL']]
index.count(True) # True 或者 1 , False 或者 0
info1 = info.loc[index, :]
print(info1.head())
2、还原翻页地址
# 找出翻页和非翻页网址
index = [re.search('/\d+_\d+\.html', i) != None for i in info1.loc[:, 'fullURL']]
index1 = [i == False for i in index]
info1_1 = info1.loc[index, :] # 带翻页网址
info1_2 = info1.loc[index1, :] # 无翻页网址
# 将翻页网址还原
da = [re.sub('_\d+\.html', '.html', str(i)) for i in info1_1.loc[:, 'fullURL']]
info1_1.loc[:, 'fullURL'] = da
# 翻页与非翻页网址合并
frames = [info1_1, info1_2]
info2 = pd.concat(frames)
# 或者
info2 = pd.concat([info1_1, info1_2], axis=0) # 默认为0,即行合并
# 去重(realIP和fullURL两列相同)
info3 = info2.drop_duplicates()
# 将IP转换成字符型数据
info3.iloc[:, 0] = [str(index) for index in info3.iloc[:, 0]]
info3.iloc[:, 1] = [str(index) for index in info3.iloc[:, 1]]
print(len(info3))
3、筛去浏览次数不满两次的用户
# 筛选满足一定浏览次数的IP
IP_count = info3['realIP'].value_counts()
# 找出IP集合
IP = list(IP_count.index)
count = list(IP_count.values)
# 统计每个IP的浏览次数,并存放进IP_count数据框中,第一列为IP,第二列为浏览次数
IP_count = pd.DataFrame({'IP': IP, 'count': count})
# 3.3筛选出浏览网址在n次以上的IP集合
n = 2
index = IP_count.loc[:, 'count'] > n
IP_index = IP_count.loc[index, 'IP']
print(IP_index.head())
5.2 划分IP集合为训练集和测试集
# 划分IP集合为训练集和测试集
index_tr = sample(range(0, len(IP_index)), int(len(IP_index) * 0.8)) # 或者np.random.sample
index_te = [i for i in range(0, len(IP_index)) if i not in index_tr]
IP_tr = IP_index[index_tr]
IP_te = IP_index[index_te]
# 将对应数据集划分为训练集和测试集
index_tr = [i in list(IP_tr) for i in info3.loc[:, 'realIP']]
index_te = [i in list(IP_te) for i in info3.loc[:, 'realIP']]
data_tr = info3.loc[index_tr, :]
data_te = info3.loc[index_te, :]
print(len(data_tr))
IP_tr = data_tr.iloc[:, 0] # 训练集IP
url_tr = data_tr.iloc[:, 1] # 训练集网址
IP_tr = list(set(IP_tr)) # 去重处理
url_tr = list(set(url_tr)) # 去重处理
print(len(url_tr))
5.3 模型构建
# 利用训练集数据构建模型
UI_matrix_tr = pd.DataFrame(0, index=IP_tr, columns=url_tr)
# 求用户-物品矩阵
for i in data_tr.index:
print("********************1-" + str(i) + "-*********************")
UI_matrix_tr.loc[data_tr.loc[i, 'realIP'], data_tr.loc[i, 'fullURL']] = 1
sum(UI_matrix_tr.sum(axis=1))
# 求物品相似度矩阵(因计算量较大,需要耗费的时间较久)
Item_matrix_tr = pd.DataFrame(0, index=url_tr, columns=url_tr)
print(len(Item_matrix_tr))
for i in Item_matrix_tr.index:
print("*********************2-" + str(i) + "-********************")
for j in Item_matrix_tr.index:
print("******************3-" + str(j) + "-***********************")
a = sum(UI_matrix_tr.loc[:, [i, j]].sum(axis=1) == 2)
b = sum(UI_matrix_tr.loc[:, [i, j]].sum(axis=1) != 0)
Item_matrix_tr.loc[i, j] = a / b
# 将物品相似度矩阵对角线处理为零
for i in Item_matrix_tr.index:
print("*****************4-" + str(i) + "-************************")
Item_matrix_tr.loc[i, i] = 0
# 利用测试集数据对模型评价
IP_te = data_te.iloc[:, 0]
url_te = data_te.iloc[:, 1]
IP_te = list(set(IP_te))
url_te = list(set(url_te))
# 测试集数据用户物品矩阵
UI_matrix_te = pd.DataFrame(0, index=IP_te, columns=url_te)
for i in data_te.index:
print("*******************5-" + str(i) + "-**********************")
UI_matrix_te.loc[data_te.loc[i, 'realIP'], data_te.loc[i, 'fullURL']] = 1
# 对测试集IP进行推荐
Res = pd.DataFrame('NaN', index=data_te.index,
columns=['IP', '已浏览网址', '推荐网址', 'T/F'])
Res.loc[:, 'IP'] = list(data_te.iloc[:, 0])
Res.loc[:, '已浏览网址'] = list(data_te.iloc[:, 1])
# 开始推荐
for i in Res.index:
print("******************6-" + str(i) + "-***********************")
if Res.loc[i, '已浏览网址'] in list(Item_matrix_tr.index):
Res.loc[i, '推荐网址'] = Item_matrix_tr.loc[Res.loc[i, '已浏览网址'],
:].argmax()
if Res.loc[i, '推荐网址'] in url_te:
Res.loc[i, 'T/F'] = UI_matrix_te.loc[Res.loc[i, 'IP'],
Res.loc[i, '推荐网址']] == 1
else:
Res.loc[i, 'T/F'] = False
# 保存推荐结果
Res.to_csv('/tmp/Res.csv', index=False, encoding='utf8')
5.4 模型评价
import pandas as pd
# 读取保存的推荐结果
Res = pd.read_csv('../tmp/Res.csv', keep_default_na=False, encoding='utf8')
# 计算推荐准确率
Pre = round(sum(Res.loc[:, 'T/F'] == 'True') / (len(Res.index) - sum(Res.loc[:, 'T/F'] == 'NaN')), 3)
print("准确率:")
print(Pre)
# 计算推荐召回率
Rec = round(sum(Res.loc[:, 'T/F'] == 'True') / (sum(Res.loc[:, 'T/F'] == 'True') + sum(Res.loc[:, 'T/F'] == 'NaN')), 3)
print("召回率:")
print(Rec)
# 计算F1指标
F1 = round(2 * Pre * Rec / (Pre + Rec), 3)
print("F1指标:")
print(F1)
输出:
准确率:
0.128
召回率:
0.186
F1指标:
0.152
总结
本次实验实现了基于 Python 语言的协同过滤推荐算法,并通过连接 MySQL 数据库、读取数据、数据清洗、矩阵计算等一系列操作,构建了基于物品的协同过滤算法,并利用测试集对模型进行了评价。
参考链接
https://blog.csdn.net/ichen820/article/details/121398385
https://blog.csdn.net/luckilyhuihuio/article/details/108018120
源码链接
链接: https://pan.baidu.com/s/1pGdwc2RAxz7nIlwr2CEYKg?pwd=dwzd 提取码: dwzd