本文通过Apriori算法来对某公司的微信推文属性的关联分析。
本文的目的
找出可让 [‘阅读量超过1K’] 的因素,以便达到这些因素使微信推文阅读量提升。
本文的结论
若想让微信推文阅读量超过1K,该公司可从[‘公司为信息源’]、[‘文章置顶’] 、[‘公司or厂’] 或综合手段进行。
- [‘公司为信息源’] —> [‘阅读量超过1K’] ,几率为50%
- [‘文章置顶’] —> [‘阅读量超过1K’] ,几率为69%
- [‘公司or厂’] —> [‘阅读量超过1K’] ,几率为64%
- [‘公司为信息源’, ‘文章置顶’] —> [‘阅读量超过1K’] ,几率为69%
- [‘公司为信息源’, ‘公司or厂’] —> [‘阅读量超过1K’] ,几率为68%
数据源
从某订阅号下载过去半年的统计数据共计160笔(栏位见下方栏位),并做好受欢迎程度的类型标签,并整理成Excel档案。
分析代码
from numpy import *
# apriori.py
import apriori
# 加载数据
import openpyxl
wb = openpyxl.load_workbook('WeChat20200923_np2.xlsx')
sheet = wb['sheet1']
row = sheet.max_row
column = sheet.max_column
set_for_all_row = []
for i in range(2,row+1):
set_for_one_row = []
for j in range(1,column+1):
values = sheet.cell(row=i, column=j).value
if values !=0:
set_for_one_row.append(values)
if len(set_for_one_row) > 0:
set_for_all_row.append(set_for_one_row)
# 示范数据打印
print(set_for_all_row[0])
# ['文章置顶', '表彰表扬', '社团活动or员工活动', '公司为信息源', '公司or厂']
data_arr = set_for_all_row
# 创建项个数为1 的候选项集
set_0 = apriori.create_set_0(data_arr)
# 扫描项个数为1的候选项集,生成项个数为1 的频繁项集,以及所有候选项集的支持度
set_0_scanned, support_data = apriori.scan_data_set(list(map(set, data_arr)), set_0, 0.3)
print('支持度较高的数据')
print(set_0_scanned)
#Out:
#支持度较高的数据
#[frozenset({'公司or厂'}), frozenset({'公司为信息源'}), frozenset({'文章置顶'}), frozenset({'社团活动or员工活动'}), frozenset({'两周内前后关联'}), frozenset({'新颖吸引'}), frozenset({'阅读量超过1K'})]
print('支持度的数据')
support_data
#Out:
#{frozenset({'公司or厂'}): 0.5174418604651163,
# frozenset({'公司为信息源'}): 0.8372093023255814,
# frozenset({'文章置顶'}): 0.5058139534883721,
# frozenset({'社团活动or员工活动'}): 0.5,
# frozenset({'表彰表扬'}): 0.11627906976744186,
# frozenset({'两周内前后关联'}): 0.3313953488372093,
# frozenset({'内容有视频'}): 0.22093023255813954,
# frozenset({'文章封面吸引'}): 0.12790697674418605,
# frozenset({'新颖吸引'}): 0.3081395348837209,
# frozenset({'阅读量超过1K'}): 0.46511627906976744,
# frozenset({'公司防疫相关or政策相关'}): 0.22674418604651161,
# frozenset({'可点赞、投票、直播'}): 0.06395348837209303}
# 执行程序1--分析支持度:
s, all_support = apriori.apriori(data_arr, 0.3)
print('支持度超过阈值的数据')
s
#Out:
#支持度超过阈值的数据
#[[frozenset({'公司or厂'}),
# frozenset({'公司为信息源'}),
# frozenset({'文章置顶'}),
# frozenset({'社团活动or员工活动'}),
# frozenset({'两周内前后关联'}),
# frozenset({'新颖吸引'}),
# frozenset({'阅读量超过1K'})],
# [frozenset({'公司or厂', '公司为信息源'}),
# frozenset({'公司or厂', '文章置顶'}),
# frozenset({'公司为信息源', '文章置顶'}),
# frozenset({'公司为信息源', '社团活动or员工活动'}),
# frozenset({'公司为信息源', '新颖吸引'}),
# frozenset({'公司为信息源', '阅读量超过1K'}),
# frozenset({'文章置顶', '阅读量超过1K'}),
# frozenset({'公司or厂', '阅读量超过1K'})],
# [frozenset({'公司or厂', '公司为信息源', '文章置顶'}),
# frozenset({'公司为信息源', '文章置顶', '阅读量超过1K'}),
# frozenset({'公司or厂', '公司为信息源', '阅读量超过1K'})],
# []]
print('所有的支持度数据')
all_support
#Out[24]:
#所有的支持度数据
#{frozenset({'公司or厂'}): 0.5174418604651163,
# frozenset({'公司为信息源'}): 0.8372093023255814,
# frozenset({'文章置顶'}): 0.5058139534883721,
# frozenset({'社团活动or员工活动'}): 0.5,
# frozenset({'表彰表扬'}): 0.11627906976744186,
# frozenset({'两周内前后关联'}): 0.3313953488372093,
# frozenset({'内容有视频'}): 0.22093023255813954,
# frozenset({'文章封面吸引'}): 0.12790697674418605,
# frozenset({'新颖吸引'}): 0.3081395348837209,
# frozenset({'阅读量超过1K'}): 0.46511627906976744,
# frozenset({'公司防疫相关or政策相关'}): 0.22674418604651161,
# frozenset({'可点赞、投票、直播'}): 0.06395348837209303,
# frozenset({'公司or厂', '公司为信息源'}): 0.4418604651162791,
# frozenset({'公司or厂', '文章置顶'}): 0.3430232558139535,
# frozenset({'公司or厂', '社团活动or员工活动'}): 0.20348837209302326,
# frozenset({'公司为信息源', '文章置顶'}): 0.47093023255813954,
# frozenset({'公司为信息源', '社团活动or员工活动'}): 0.5,
# frozenset({'文章置顶', '社团活动or员工活动'}): 0.29651162790697677,
# frozenset({'两周内前后关联', '公司为信息源'}): 0.2616279069767442,
# frozenset({'公司为信息源', '新颖吸引'}): 0.3023255813953488,
# frozenset({'公司为信息源', '阅读量超过1K'}): 0.4186046511627907,
# frozenset({'两周内前后关联', '文章置顶'}): 0.1569767441860465,
# frozenset({'文章置顶', '新颖吸引'}): 0.2441860465116279,
# frozenset({'文章置顶', '阅读量超过1K'}): 0.3488372093023256,
# frozenset({'两周内前后关联', '社团活动or员工活动'}): 0.19186046511627908,
# frozenset({'新颖吸引', '社团活动or员工活动'}): 0.18023255813953487,
# frozenset({'社团活动or员工活动', '阅读量超过1K'}): 0.20930232558139536,
# frozenset({'两周内前后关联', '新颖吸引'}): 0.13372093023255813,
# frozenset({'两周内前后关联', '阅读量超过1K'}): 0.1569767441860465,
# frozenset({'新颖吸引', '阅读量超过1K'}): 0.27325581395348836,
# frozenset({'公司or厂', '阅读量超过1K'}): 0.3313953488372093,
# frozenset({'公司or厂', '新颖吸引'}): 0.22093023255813954,
# frozenset({'两周内前后关联', '公司or厂'}): 0.16279069767441862,
# frozenset({'公司or厂', '公司为信息源', '文章置顶'}): 0.31976744186046513,
# frozenset({'公司为信息源', '文章置顶', '新颖吸引'}): 0.2441860465116279,
# frozenset({'公司为信息源', '文章置顶', '阅读量超过1K'}): 0.32558139534883723,
# frozenset({'公司or厂', '公司为信息源', '阅读量超过1K'}): 0.3023255813953488,
# frozenset({'公司or厂', '文章置顶', '阅读量超过1K'}): 0.26744186046511625,
# frozenset({'公司or厂', '公司为信息源', '文章置顶', '阅读量超过1K'}): 0.2558139534883721}
# 执行程序2---挖掘关联关系:
L, supportData = apriori.apriori(data_arr)
bigRuleList = apriori.generate_rules(L, supportData, 0.3)
# 导出原始数据
bigRuleList
#Out:
#[(frozenset({'公司为信息源'}), frozenset({'公司or厂'}), 0.5277777777777778),
# (frozenset({'公司or厂'}), frozenset({'公司为信息源'}), 0.8539325842696629),
# (frozenset({'文章置顶'}), frozenset({'公司or厂'}), 0.6781609195402298),
# (frozenset({'公司or厂'}), frozenset({'文章置顶'}), 0.6629213483146067),
# (frozenset({'公司为信息源'}), frozenset({'文章置顶'}), 0.5625),
# (frozenset({'文章置顶'}), frozenset({'公司为信息源'}), 0.9310344827586207),
# (frozenset({'公司为信息源'}), frozenset({'社团活动or员工活动'}), 0.5972222222222222),
# (frozenset({'社团活动or员工活动'}), frozenset({'公司为信息源'}), 1.0),
# (frozenset({'公司为信息源'}), frozenset({'新颖吸引'}), 0.36111111111111105),
# (frozenset({'新颖吸引'}), frozenset({'公司为信息源'}), 0.9811320754716981),
# (frozenset({'公司为信息源'}), frozenset({'阅读量超过1K'}), 0.5),
# (frozenset({'阅读量超过1K'}), frozenset({'公司为信息源'}), 0.9),
# (frozenset({'文章置顶'}), frozenset({'阅读量超过1K'}), 0.6896551724137931),
# (frozenset({'阅读量超过1K'}), frozenset({'文章置顶'}), 0.75),
# (frozenset({'公司or厂'}), frozenset({'阅读量超过1K'}), 0.6404494382022471),
# (frozenset({'阅读量超过1K'}), frozenset({'公司or厂'}), 0.7124999999999999),
# (frozenset({'公司为信息源', '文章置顶'}), frozenset({'公司or厂'}), 0.6790123456790124),
# (frozenset({'公司or厂', '公司为信息源'}), frozenset({'文章置顶'}), 0.7236842105263158),
# (frozenset({'公司or厂', '文章置顶'}), frozenset({'公司为信息源'}), 0.9322033898305085),
# (frozenset({'公司为信息源'}), frozenset({'公司or厂', '文章置顶'}), 0.3819444444444444),
# (frozenset({'文章置顶'}), frozenset({'公司or厂', '公司为信息源'}), 0.632183908045977),
# (frozenset({'公司or厂'}), frozenset({'公司为信息源', '文章置顶'}), 0.6179775280898876),
# (frozenset({'公司为信息源', '文章置顶'}), frozenset({'阅读量超过1K'}), 0.6913580246913581),
# (frozenset({'公司为信息源', '阅读量超过1K'}), frozenset({'文章置顶'}), 0.7777777777777778),
# (frozenset({'文章置顶', '阅读量超过1K'}), frozenset({'公司为信息源'}), 0.9333333333333333),
# (frozenset({'公司为信息源'}), frozenset({'文章置顶', '阅读量超过1K'}), 0.3888888888888889),
# (frozenset({'文章置顶'}), frozenset({'公司为信息源', '阅读量超过1K'}), 0.6436781609195402),
# (frozenset({'阅读量超过1K'}), frozenset({'公司为信息源', '文章置顶'}), 0.7000000000000001),
# (frozenset({'公司or厂', '公司为信息源'}), frozenset({'阅读量超过1K'}), 0.6842105263157894),
# (frozenset({'公司为信息源', '阅读量超过1K'}), frozenset({'公司or厂'}), 0.7222222222222221),
# (frozenset({'公司or厂', '阅读量超过1K'}), frozenset({'公司为信息源'}), 0.912280701754386),
# (frozenset({'公司为信息源'}), frozenset({'公司or厂', '阅读量超过1K'}), 0.36111111111111105),
# (frozenset({'公司or厂'}), frozenset({'公司为信息源', '阅读量超过1K'}), 0.5842696629213483),
# (frozenset({'阅读量超过1K'}), frozenset({'公司or厂', '公司为信息源'}), 0.65)]
# 导出相关数据1
for i in range(len(bigRuleList)):
print(list(bigRuleList[i][0]),'--->',list(bigRuleList[i][1]),',几率为{:.0%}'.format(bigRuleList[i][2]))
#Out:
#['公司为信息源'] ---> ['公司or厂'] ,几率为53%
#['公司or厂'] ---> ['公司为信息源'] ,几率为85%
#['文章置顶'] ---> ['公司or厂'] ,几率为68%
#['公司or厂'] ---> ['文章置顶'] ,几率为66%
#['文章置顶'] ---> ['公司为信息源'] ,几率为93%
#['公司为信息源'] ---> ['文章置顶'] ,几率为56%
#['公司为信息源'] ---> ['社团活动or员工活动'] ,几率为60%
#['社团活动or员工活动'] ---> ['公司为信息源'] ,几率为100%
#['新颖吸引'] ---> ['公司为信息源'] ,几率为98%
#['公司为信息源'] ---> ['新颖吸引'] ,几率为36%
#['公司为信息源'] ---> ['阅读量超过1K'] ,几率为50%
#['阅读量超过1K'] ---> ['公司为信息源'] ,几率为90%
#['文章置顶'] ---> ['阅读量超过1K'] ,几率为69%
#['阅读量超过1K'] ---> ['文章置顶'] ,几率为75%
#['公司or厂'] ---> ['阅读量超过1K'] ,几率为64%
#['阅读量超过1K'] ---> ['公司or厂'] ,几率为71%
#['公司为信息源', '文章置顶'] ---> ['公司or厂'] ,几率为68%
#['公司or厂', '公司为信息源'] ---> ['文章置顶'] ,几率为72%
#['公司or厂', '文章置顶'] ---> ['公司为信息源'] ,几率为93%
#['公司为信息源'] ---> ['公司or厂', '文章置顶'] ,几率为38%
#['文章置顶'] ---> ['公司or厂', '公司为信息源'] ,几率为63%
#['公司or厂'] ---> ['公司为信息源', '文章置顶'] ,几率为62%
#['公司为信息源', '文章置顶'] ---> ['阅读量超过1K'] ,几率为69%
#['阅读量超过1K', '文章置顶'] ---> ['公司为信息源'] ,几率为93%
#['阅读量超过1K', '公司为信息源'] ---> ['文章置顶'] ,几率为78%
#['文章置顶'] ---> ['阅读量超过1K', '公司为信息源'] ,几率为64%
#['公司为信息源'] ---> ['阅读量超过1K', '文章置顶'] ,几率为39%
#['阅读量超过1K'] ---> ['公司为信息源', '文章置顶'] ,几率为70%
#['公司为信息源', '公司or厂'] ---> ['阅读量超过1K'] ,几率为68%
#['阅读量超过1K', '公司or厂'] ---> ['公司为信息源'] ,几率为91%
#['阅读量超过1K', '公司为信息源'] ---> ['公司or厂'] ,几率为72%
#['公司or厂'] ---> ['阅读量超过1K', '公司为信息源'] ,几率为58%
#['公司为信息源'] ---> ['阅读量超过1K', '公司or厂'] ,几率为36%
#['阅读量超过1K'] ---> ['公司为信息源', '公司or厂'] ,几率为65%
# 导出相关数据2
for i in range(len(bigRuleList)):
if list(bigRuleList[i][1]) == ['阅读量超过1K']:
print(list(bigRuleList[i][0]),'--->',list(bigRuleList[i][1]),',几率为{:.0%}'.format(bigRuleList[i][2]))
#Out:
#['公司为信息源'] ---> ['阅读量超过1K'] ,几率为50%
#['文章置顶'] ---> ['阅读量超过1K'] ,几率为69%
#['公司or厂'] ---> ['阅读量超过1K'] ,几率为64%
#['公司为信息源', '文章置顶'] ---> ['阅读量超过1K'] ,几率为69%
#['公司为信息源', '公司or厂'] ---> ['阅读量超过1K'] ,几率为68%
apriori.py
from numpy import *
# 加载数据集
def load_data_arr():
return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
# 初始化候选项集,创建元素个数为1的候选项集
def create_set_0(data_arr):
# 数组
set_0 = []
# 对每一条交易记录
for transaction in data_arr:
# 对交易记录中的每件物品
for item in transaction:
# 如果该物品没有出现在候选项集中,则添加
if not [item] in set_0:
set_0.append([item])
set_0.sort()
# 返回一个冻结的集合,集合冻结后不能再添加或者删除元素
return map(frozenset, set_0)
# 扫描交易记录集,计算每个候选项集的支持度,返回满足条件的频繁项集以及所有候选项集的支持度
def scan_data_set(data_set_list, set_k, min_support):
set_k_list = list(set_k)
# 候选项集计数
items_cnt = {}
for transaction in data_set_list:
for items in set_k_list:
if items.issubset(transaction):
if items not in items_cnt:
items_cnt[items] = 1
else:
items_cnt[items] += 1
num_transaction = len(data_set_list)
# 数组
return_set_k = []
# 字典
support_data = {}
for items in items_cnt.keys():
# 计算每个候选项集的支持度
support = items_cnt[items]/num_transaction
# 判断项集的支持度是否满足最小支持度
if support >= min_support:
return_set_k.append(items)
# 保存每个候选项集的支持度
support_data[items] = support
return return_set_k, support_data
# 根据项个数为k+1的频繁项集set_k,生成项个数为k+2的候选项集set_k_plus
def apriori_gen(set_k, k):
return_set_k_plus = []
num_set_k = len(set_k)
for i in range(num_set_k):
for j in range(i+1, num_set_k):
l1 = list(set_k[i])[:k]
l2 = list(set_k[j])[:k]
l1.sort()
l2.sort()
if l1 == l2:
return_set_k_plus.append(set_k[i] | set_k[j]) #set union
return return_set_k_plus
def apriori(data_arr, min_support=0.3):
# 创建项个数为1的候选项集
set_0 = create_set_0(data_arr)
data_set_list = list(map(set, data_arr))
# 计算项个数为1的频繁项集
set_0_scanned, all_support = scan_data_set(data_set_list, set_0, min_support)
# 将项个数为1的频繁项集放入到总的频繁项集中,下标为0
s = [set_0_scanned]
k = 0
while len(s[k]) > 0:
# 根据项个数为k+1的频繁项集set_k,生成项个数为k+2的候选项集set_k_plus
set_k_plus = apriori_gen(s[k], k)
# 计算项个数为k+2的频繁项集set_k_plus_scanned
set_k_plus_scanned, k_plus_support = scan_data_set(data_set_list, set_k_plus, min_support)
# 更新
all_support.update(k_plus_support)
s.append(set_k_plus_scanned)
k += 1
return s, all_support
# 计算规则的可信度
def calculate_conf(freq_item, H, support_data, big_rules_list, min_conf=0.3):
pruned_H = []
for consequence in H:
# 通过支持度计算可信度
# 规则的左侧通过作差求得
conf = support_data[freq_item]/support_data[freq_item - consequence]
if conf >= min_conf:
big_rules_list.append((freq_item - consequence, consequence, conf))
# 需要返回可信的规则的右侧,以便再次进行合并,同时抛弃不可信规则的右侧
pruned_H.append(consequence)
return pruned_H
# 从序列中提取规则
# H是规则的右侧
def rules_from_consequence(freq_item, H, support_data, big_rules_list, min_conf=0.3):
m = len(H[0])
# 原书代码中此处没有等号,是不对的,读者可以结合前面给出的结束条件,判断此处是否需要添加等号
if len(freq_item) >= (m+1):
# 下面的两行代码,原书给出的顺序是错误的,应该先计算可信度,再合并
# 读者可以根据计算出的频繁项集和支持度手动计算一下,可以发现原执行顺序会漏掉一部分规则
hmp1 = calculate_conf(freq_item, H, support_data, big_rules_list, min_conf)
hmp1 = apriori_gen(hmp1, m - 1)
# 下面这行代码使没有用的,因为合并右侧后,肯定是需要去提取规则并计算规则可信度的。
# if len(hmp1) > 1:
rules_from_consequence(freq_item, hmp1, support_data, big_rules_list, min_conf)
# 生成关联规则
def generate_rules(s, support_data, min_conf=0.3):
big_rules_list = []
for i in range(1, len(s)):
for freq_set in s[i]:
h1 = [frozenset([item]) for item in freq_set]
if i > 1:
# 项数大于2,至少为3,可以组合项,提取规则
rules_from_consequence(freq_set, h1, support_data, big_rules_list, min_conf)
else:
calculate_conf(freq_set, h1, support_data, big_rules_list, min_conf)
return big_rules_list
部分代码源自于https://blog.csdn.net/dzjin1234/article/details/105799974