背景
1.订阅号推送一条图文消息时可一次性组合推送最多8篇文章,如下图,其中1篇头条和最多7篇次条。
2.在推送文章时,需要考虑到文章类型的多样性,因此,每天的推送排版,都要考虑不同的文章类型之间的契合性。要是陈列不当,次条的阅读量就很惨淡。当然,要是选择恰当,头条和次条就能相互促进,而且,头条一般都是确定的,主要是次条文章的挑选。
目的
那针对不同类型的头条文章,应该怎么罗列订阅号的“次条”比较好呢?或者,换句话说,读者可能会同时喜欢看什么类型的内容呢?
通过对用户阅读行为的关联分析,了解顾客的阅读习惯,找出强关联规则,从而为订阅号每日推文的组合提供参考。
获取数据
数据来源:公众号用户访问数据.csv
用户编号 | 文章类别 | 阅读数 | 看一看 | 点赞 | 赞赏 | 被转载 | 访问日期 |
1 | 数据分析 | 20051 | 1203 | 2406 | 80 | 601 | 2020/9/21 |
2 | 数据分析 | 11690 | 584 | 1519 | 46 | 233 | 2020/9/17 |
3 | 数据分析 | 5720 | 400 | 572 | 45 | 228 | 2020/9/2 |
4 | 数据分析 | 22502 | 1125 | 3150 | 45 | 900 | 2020/9/24 |
5 | 数据分析 | 11201 | 560 | 1344 | 89 | 448 | 2020/9/8 |
......
98 | pandas | 7454 | 521 | 819 | 29 | 223 | 2020/9/20 |
98 | pandas | 7587 | 379 | 910 | 22 | 151 | 2020/9/20 |
99 | 数据分析 | 12179 | 852 | 1217 | 48 | 487 | 2020/9/23 |
99 | pandas | 11715 | 585 | 1288 | 46 | 351 | 2020/9/23 |
99 | matplotlib | 11542 | 692 | 1269 | 11 | 230 | 2020/9/23 |
数据处理
1.读入数据,创建DataFrame对象
import pandas as pd
fileinfo_df = pd.read_csv('./公众号用户访问数据.csv',encoding='gbk')
2.查看数据
fileinfo_df.describe()#查看数据集基本概况
fileinfo_df[fileinfo_df.duplicated()]#查看重复数据
describe结果:
duplicated结果:
由结果可见,无重复数据。
3.数据提取
#提取后续分析可能用到的数据列
extract_data_df = fileinfo_df[['用户编号','访问时间','文章类别']]
4.数据清洗
异常值/空值处理。
因刚已查看源文件数据中并不存在异常值和空值,所以此处跳过。
5.数据整理
但我们要分析的是用户所阅读过的文章类型,故在接下来的分析中,我们需要将同一用户在同一天阅读的相同文章类型进行合并为一条,故本次对提取后的数据,即extract_data_df进行数据去重即可。
首先,查看是否存在重复行数据
extract_data_df[extract_data_df.duplicate()]#查看重复数据
查看结果:
存在重复行,需去重
extract_data_df = extract_data_df.drop_duplicates()#去除重复行
另外,为了后续方便分析,还需要将同一用户在同一天阅读的所有文章类型进行合并到一个列表当中。
#定义函数,将原“文章类型”数据转换为列表类型,便于数据合并
def tran_list(data):
if str(data)[0] == '[' :
return data
return [data]
#将文章类别数据由字符串转换为列表
extract_data_df['文章类别'] = extract_data_df['文章类别'].agg(tran_list)
#将同一用户在同一天内所阅读过的文章类别进行合并
extract_data_df = extract_data_df.groupby(['用户编号','访问日期']).sum()
#查看合并后的数据,尾部10行
extract_data_df.tail(10)
合并后数据:
补充:若不事先对“文章类别”数据做列表类型转换的话,直接合并后的结果会是以下这样的,合并后数据无间隔,不利于后续分析使用。
分析
1.最小支持度设定
最小支持度的设定一般是根据业务经验来预设或调整,本次案例,考虑一个用户每月至少3次阅读,9月有30天,且总数据只有99条,那么,我们可以暂且将支持度设定为:3/30/99≈0.1
2.最小置信度设定
最小置信度同样也是需要根据经验来设定,本次我们先预设最小置信度为0,先观察产出的关联规则数量之后再做调整。
3.建模
#提取文章类型数据
extract_type_df = extract_data_df['文章类型']
#导入APR算法模块
from apyori import apriori as apr
#配置参数,最小支持度min_support=0.1 ,最小置信度min_confidence=0
results = apr.extract_type_df(extract_type_df,min_support = 0.1, min_confidence = 0)
#查看算法产出结果
for result in results:
# 获取支持度,并保留3位小数
support = round(result.support, 3)
# 遍历ordered_statistics对象
for rule in result.ordered_statistics:
# 获取前件和后件并转成列表
head_set = list(rule.items_base)
tail_set = list(rule.items_add)
# 跳过前件为空的数据
if head_set == []:
continue
# 将前件、后件拼接成关联规则的形式
related_catogory = str(head_set)+'→'+str(tail_set)
# 提取置信度,并保留3位小数
confidence = round(rule.confidence, 3)
# 提取提升度,并保留3位小数
lift = round(rule.lift, 3)
# 查看强关联规则,支持度,置信度,提升度
print(related_catogory, support, confidence, lift)
结果展示:
在使用默认最小置信度为0的情况下,我们一共筛选出了10 条关联规则。下一步,就要根据我们自己的实际情况,确定一个合适的最小置信度,选出强关联规则。
为此,我单独提取了这 10 条关联规则的置信度,并查看了它们的描述性统计信息,数值情况如下:
# 提取关联规则的置信度
confidences = [0.233, 1.0, 0.744, 0.395, 0.326, 0.452, 1.0, 0.222, 0.235, 0.613]
# 查看置信度的描述性统计信息
s = pd.Series(confidences).describe()
最小值为0.2,最大值为1.0,极值相差较大;另外,业务需求来讲,10条规则未免过多,实际只需要6条即可,故我们重新将最小置信度参数confidence调整为0.3 后再次进行关联规则的产出,并且将产出结果转换为DataFrame以支持度进行排序,便于后续分析:
# 执行Apriori 算法
results = apr(extract_type_df['文章类别'], min_support=0.1, min_confidence=0.3)
# 创建列表
extract_result = []
for result in results:
# 获取支持度,并保留3位小数
support = round(result.support, 3)
# 遍历ordered_statistics对象
for rule in result.ordered_statistics:
# 获取前件和后件并转成列表
head_set = list(rule.items_base)
tail_set = list(rule.items_add)
# 跳过前件为空的数据
if head_set == []:
continue
# 将前件、后件拼接成关联规则的形式
related_catogory = str(head_set)+'→'+str(tail_set)
# 提取置信度,并保留3位小数
confidence = round(rule.confidence, 3)
# 提取提升度,并保留3位小数
lift = round(rule.lift, 3)
# 将提取的数据保存到提取列表中
extract_result.append(
[related_catogory, support, confidence, lift])
# 将数据转成 DataFrame 的形式
rules_data = pd.DataFrame(extract_result, columns=[
'关联规则', '支持度', '置信度', '提升度'])
# 将数据按照“支持度”排序
sorted_by_support = rules_data.sort_values(by='支持度')
# 查看排序后的数据
sorted_by_support
产出结果为:
关系是促进还是抑制,关键是要看它们的提升度。
本次筛选出来的强关联规则提升度大于 1(促进)和小于 1(抑制)的都有,但它们的指导作用是截然相反的,将数据分开来分析是一个明智的选择。
故将提升度>1的规则我们进行单独提取:
# 提取出提升度大于1的数据,并重置数据的索引
promoted_rules = sorted_by_support[sorted_by_support['提升度'] > 1].reset_index(drop=True)
promoted_rules
提取结果为:
由此可知,有促进作用的关联规则一共有 4 个,其中“爬虫”和“Python”互为促进关系,阅读“sql”文章的用户更有可能阅读“Python”文章,喜欢阅读“pandas”文章的读者大概率也会喜欢阅读“数据分析”。
数据可视化展现
1.关联规则的支持度与置信度,促进关系时(即提升度>1)的柱状图绘制
# 功能:绘制提升度大于 1 的强关联规则柱状图
#导入模块
import matplotlib.pyplot as plt
import warnings
# 设置画布尺寸
plt.figure(figsize=(20, 8))
# 设置横纵坐标以及柱子的宽度
width = 0.2
# 画出柱状图
plt.bar(promoted_rules.index-width/2, promoted_rules['支持度'], width=width)
plt.bar(promoted_rules.index+width/2, promoted_rules['置信度'], width=width)
# 设置图例
plt.legend(['支持度', '置信度'], fontsize=20)
# 设置标题
plt.title('促进关系的关联规则的支持度、置信度', fontsize=25)
# 设置刻度名称
plt.xticks(promoted_rules.index, promoted_rules['关联规则'], fontsize=15)
# 设置坐标轴标签
plt.xlabel('关联规则', fontsize=20)
plt.ylabel('数值', fontsize=20)
2.1.关联规则的支持度与置信度,抑制关系时(即提升度<1)的柱状图绘制
# 提取出提升度小于1的数据,并重置数据的索引
restricted_rules = sorted_by_support[sorted_by_support['提升度'] < 1].reset_index(drop=True)
restricted_rules
# 功能:绘制提升度小于 1 的强关联规则柱状图
# 设置画布尺寸
plt.figure(figsize=(20, 8))
# 画出柱状图
plt.bar(restricted_rules.index-width/2, restricted_rules['支持度'], width=width)
plt.bar(restricted_rules.index+width/2, restricted_rules['置信度'], width=width)
# 设置图例
plt.legend(['支持度', '置信度'], fontsize=20)
# 设置标题
plt.title('抑制关系的关联规则的支持度、置信度', fontsize=25)
# 设置刻度名称
plt.xticks(restricted_rules.index, restricted_rules['关联规则'], fontsize=15)
# 设置坐标轴标签
plt.xlabel('关联规则', fontsize=20)
plt.ylabel('数值', fontsize=20)
分析结论
基于这两份结果和公众号的排版需求,现在我们可以提出以下3
条建议:
1)当“头条”文章是介绍有关“Python”的内容时,不妨考虑搭配“爬虫”的“次条”。
2)当“爬虫”类文章成为“头条”时,可以提高“Python”、降低“数据分析”的“次条”排布。
3)阅读“pandas”的用户大概率也喜欢“数据分析”,可在文章末尾,添加“数据分析”相关的拓展链接,提升阅读量。