频繁项集及关联规则的应用中,购物、搜索引擎中的查询词等,下面看一个美国国会议员投票的例子。
1. 示例:发现国会投票中的模式
这个例子虽然在数据的获取过程中有点复杂,但我觉得还是很有必要分析下整个过程。
1. 收集数据:构建美国国会投票记录的事务数据集
我们希望最终数据的格式:即每一行代表美国国会的一个成员,而每列都是他们投票的对象。
(1)需要提前做的准备:
- 安装python-votesmart
获得API key(这需要申请自己的key)
(2)使用相关的API
bills=votesmart.votes.getBillsByStateRecent()
获得最近的100条议案bill.title:获得议案标题 bill.billId:获得议案ID号
bill=votesmart.votes.getBill(11820)
通过getBill()方法获得每条议案的更多内容,此时的bill是一个BillDetail对象,包含大量的完整信息。bill.actions
:查看议案的所有行为,包括议案被提出时的行为,以及议案在投票时的行为action.stage
和action.actionID
:我们所感兴趣的是投票时的行为,所以得到行为的状态和其相应的ID。voteList=votesmart.votes.getBillActionVotes()
,通过getBillActionVotes()
方法获得某条议案的具体投票信息。其中voteList是一个包含vote对象的列表。
(2)为了将billID转化为actionID,并对actionID进行过滤只保留包含投票数据的actionID,这样得到剩下的议案都是有趣的议案:
# -*- coding: utf-8 -*-
"""
Created on Thu Nov 09 20:52:41 2017
"""
from time import sleep
from votesmart import votesmart # 此模块需要单独下载
votesmart.apikey = 'a7fa40adec6f4a77178799fae4441030' # 这里需要改换成自己的API key
# 收集美国国会议案中action ID的函数
def getActionIds():
actionIdList = []; billTitleList = []
fr = open('recent20bills.txt')
for line in fr.readlines():
billNum = int(line.split('\t')[0]) # 得到了议案的ID
try:
billDetail = votesmart.votes.getBill(billNum) # 得到一个billDetail对象
for action in billDetail.actions: # 遍历议案中的所有行为
if action.level == 'House' and \
(action.stage == 'Passage' or action.stage == 'Amendment Vote'):
actionId = int(action.actionId)
print 'bill: %d has actionId: %d' % (billNum, actionId)
actionIdList.append(actionId)
billTitleList.append(line.strip().split('\t')[1])
except: # API调用时发生错误
print "problem getting bill %d" % billNum
sleep(1) # 礼貌访问网站而做出些延迟,避免过度访问
return actionIdList, billTitleList
此时得到了有actionID的议案,接下来可以获取这些actionID的投票信息。
(3)选举人可以投是或否的表决票,也可以弃权,需要将上述信息转化为类似于项集或者交易数据库之类的东西,而一条交易记录只包含一个项的出现或者不出现,并不包含项出现的次数。
在使用Apriori之前需要构建事务数据库:首先创建一个字典,字典中使用政客的名字作为键值,然后对投票进行编码,其对每条议案使用两个条目:bill+’Yea’以及bill+’Nay’。
投票信息到元素项的转换结果:
下面的函数就是以actionID串作为输入并利用votesmart的API来抓取投票记录的函数,然后将每个选举人的投票转化为一个项集,每个选举人对应于一行或者说事物数据库中的一条记录。
# 基于投票数据的事物列表填充函数
def getTransList(actionIdList, billTitleList):
itemMeaning = ['Republican', 'Democratic'] # 创建一个含义列表
for billTitle in billTitleList: # 遍历所有的议案
itemMeaning.append('%s -- Nay' % billTitle) # 在议案标题后面添加Nay(反对)
itemMeaning.append('%s -- Yea' % billTitle) # 在议案标题后添加Yea(同意)
transDict = {} # 用于加入元素项
voteCount = 2
for actionId in actionIdList: # 遍历getActionIds()返回的每一个actionId
sleep(3) # 延迟访问,防止过于频繁的API调用
print 'getting votes for actionId: %d' % actionId
try:
voteList = votesmart.votes.getBillActionVotes(actionId) # 获得某个特定的actionId的所有投票信息
for vote in voteList: # 遍历投票信息
if not transDict.has_key(vote.candidateName): # 如果没有该政客的名字
transDict[vote.candidateName] = [] # 用该政客的名字作为键来填充transDict
if vote.officeParties == 'Democratic': # 获取该政客的政党信息
transDict[vote.candidateName].append(1)
elif vote.officeParties == 'Republican':
transDict[vote.candidateName].append(0)
if vote.action == 'Nay':
transDict[vote.candidateName].append(voteCount)
elif vote.action == 'Yea':
transDict[vote.candidateName].append(voteCount + 1)
except:
print "problem getting actionId: %d" % actionId
voteCount += 2
return transDict, itemMeaning # 返回事物字典和元素项含义列表
测试算法,基于投票挖掘关联规则
dataSet=[transDict[key] for key in transDict.keys() ]
L,suppData=apriori(dataSet,0.5) # 得到频繁项集
rules=generateRules(L,suppData,0.99)
结果得到:
最后可以基于关联规则和支持度进行分析,解决实际问题。
2. 示例:发现毒蘑菇的相似特征
有时我们并不想寻找所有频繁项集,而只对包含某个特定元素项的项集感兴趣。在本章这个最后的例子中,我们会寻找毒蘑菇中的一些公共特征,利用这些特征就能避免吃到那些有毒的蘑菇。UCI的机器学习数据集合中有一个关于肋形蘑菇的23种特征的数据集,每一个特征都包含一个标称数据值。我们必须将这些标称值转化为一个集合,这一点与前面投票例子中的做法类似。幸运的是,已经有人已经做好了这种转换。Roberto Bayardo对UCI蘑菇数据集进行了解析,将每个蘑燕样本转换成一个特征集合。其中,枚举了每个特征的所有可能值,如果某个样本包含特征,那么该特征对应的整数值被包含数据集中。
此时可以利用Apriori算法寻找包含特征值为2(这里‘2’代表了有毒的特征)的频繁项集。
代码:
# 主函数
mushDataSet=[line.split() for line in open ('mushroom.dat').readlines()]
L,suppData=apriori(mushDataSet,minSupport = 0.5)
print 'L[1]:',L[1]
for item in L[1]: # 在单元素频繁项集中找到包含特征2的频繁集
if item.intersection('2'):
print 'item:',item
这里只给出了调用语句,具体的Apriori算法参考:Apriori算法发现频繁集
只需改动主函数语句即可:
运行结果:
L[1]: [frozenset(['59', '85']), frozenset(['63', '85']), frozenset(['86', '34']), frozenset(['76', '86']), frozenset(['59', '34']), frozenset(['24', '85']), frozenset(['39', '85']), frozenset(['39', '86']), frozenset(['86', '59']), frozenset(['86', '53']), frozenset(['2', '85']), frozenset(['86', '36']), frozenset(['24', '90']), frozenset(['39', '36']), frozenset(['53', '85']), frozenset(['63', '86']), frozenset(['86', '85']), frozenset(['24', '34']), frozenset(['36', '34']), frozenset(['90', '85']), frozenset(['39', '34']), frozenset(['67', '34']), frozenset(['90', '63']), frozenset(['76', '34']), frozenset(['76', '85']), frozenset(['86', '67']), frozenset(['53', '34']), frozenset(['90', '86']), frozenset(['90', '36']), frozenset(['59', '36']), frozenset(['90', '53']), frozenset(['63', '36']), frozenset(['90', '59']), frozenset(['24', '86']), frozenset(['90', '39']), frozenset(['36', '85']), frozenset(['85', '34']), frozenset(['63', '34']), frozenset(['85', '67']), frozenset(['90', '34']), frozenset(['63', '59'])]
item: frozenset(['2', '85'])
由于这里的支持度阈值较高,并且采用的是较小的项集来查看特征‘2’,所以得到的项集只有一个。
通过观察这些特征,以便知道了解野蘑菇的那些方面。如果看到其中任何一个特征,那么这些蘑菇就不要吃了。也即是说关联分析就是通过一个目标特征来分析得到和其相关联的其他特征,进而可以对其采取措施。