R语言入门级实例——用igragh包分析社群
引入——
本文的主要目的是初步实现R的igraph包的基础功能,包括绘制关系网络图(social relationship)、利用算法进行社群发现(community detecting)。对于R语言零基础的同学非常友好。以下R代码中如有含义不清的,建议尝试先在R编辑器中输入?xxx()进行查询(xxx是函数或语句名)。此外,*论坛也帮博主小白看懂了不少报错信息。
主要参考资料为《R语言与网站分析》[李明著][机械工业出版社][2014.04] 的9.3节《关系网络分析》。
0.背景
现已获得超市中商品的名称、分类以及大量顾客购物篮子中的商品信息,任务是分析哪些商品存在相关性,经常被放在一起购买。题外话,这种分析的一例经典应用就是沃尔玛超市的“啤酒与尿布”,感兴趣者可自行搜索或参见Jocelyn_燕的一篇博客.
1.原始数据及初步处理
数据来源是Kaggle竞赛的数据库instacart-market-basket-analysis.下载压缩文件之后,将有用的数据合并到一个Excel文件中,此处需要order_product,order,products,departments的数据.注意,这个文件极大,order_product_prior这个spread sheet里的数据在Excel里已经无法完全显示,博主就截取了前500条信息,形成了mini数据集,以下对数据集的操作都是针对这个mini表进行的.如下:
为了达到参考书上的数据形式,需要先整理这个Excel,形成如下图只有四列数据的形式.这里博主不太熟悉R的操作,就用Python的循环处理了,代码可附在文章最后.
这是当初处理数据集的一些文件,由于不会用R完成所有命令,显得很笨拙hhh.
2.数据集导入
导入的数据集包含四列,原商品编号过大,不便于处理,p_id、d_id分别是商品、商品分类的新编号,如下图:(这些也是用Python代劳的)
3.建立关系网络与绘图
步骤描述:
引用igraph包,建立空关系网络并设置点数据→
为点数据添加商品号以及商品分类属性→
添加线数据→
plot出来发现是非连通图(存在孤立的点的图),有两个未连通的点(点43,点44),只用手动对点的个数减2即可
将点的个数修改后,重新跑前面的所有代码即可
这部分代码如下:(完整代码见文末)
#建立空关系网络并设置点数据
library(igraph)
gdata<-graph.empty(directed=F)
#num<-ncol(cart)
num<-ncol(cart)-2 #修改点的个数
gdata<-add.vertices(gdata,num) #为点数据添加商品号以及商品分类属性
category<-c();item<-c()
for(i in colnames(cart))
{
if(i!=136&& i!=140)
{
category<-c(category,data$d_id[which(data$p_id==i)[1]] )
item<-c(item,data$p_id[which(data$p_id==i)[1]] )
}
}
V(gdata)$category<-category
V(gdata)$item<-item #添加线数据
#依次遍历每个订单,读取每个订单内的商品ID,并存放于向量item.i
for(i in 1:nrow(cart))
{ item.i<-c()
for(j in 1:ncol(cart))
{
if(cart[i,j]==1)
{
item.i<-cbind(item.i,colnames(cart)[j])
}
}
#建立向量内不同商品间的关联联系
item.i.num<-length(item.i)
from<-c();to<-c()
for(m in 1:(item.i.num-1))
{
from<-c(from,item.i[-c((item.i.num-m+1):item.i.num)])
to<-c(to,item.i[-c(1:m)])
}
if(i>1)
{
edges<-rbind(edges,matrix(c(from,to),nc=2))
}
else
{
edges<-matrix(data=c(from,to),nc=2)
}
}
edges0<-edges
labels<-union(unique(edges[,1]), unique(edges[,2]))
ids<-1: length(labels)#对点的编号重新编码,因为在igraph中边信息的ids必须连续
names(ids)<-labels
newfrom<-as.character(edges[,1]);newto<-as.character(edges[,2])
edges<-matrix (c(ids[newfrom],ids[newto]), nc=2) #添加线信息并设置线权重
gdata<-add.edges(gdata,t(edges[-1,]))#t()是矩阵转置函数
E(gdata)$weight<-count.multiple(gdata)
gdata<-simplify(gdata, remove.multiple=TRUE, remove.loops = TRUE, edge.attr.comb = 'mean')
#最后一个参数一定是edge.attr.comb,不是edges.attr.comb
dev.off()#关闭图形设备
plot(gdata,edge.width=E(gdata)$weight,main="gdata", edge.label=E(gdata)$weight) #发现是非连通图,有两个未连通的点(点43,点44),只用手动对点的个数减2即可
#将点的个数修改后,重新跑前面的所有代码
画出来的效果如下:
4.社群发现与绘图
此处采用自旋玻璃法(spinglass community detecting)进行社群发现。其他社群发现的方法包括中心势、标签传播、随机游走等,这几种方法在算法效率与模拟方式上其实存在不同点。但限于篇幅,此处不再介绍。对这几种方法感兴趣者可自行搜索或参考以下论文(引用格式不够规范,但应该能搜索到):
[1]J¨org Reichardt & Stefan Bornholdt (2008) Statistical Mechanics of Community Detection <=spinglass相关
[2]M. Girvan & M. E. J. Newman (2001) Community structure in social and biological networks <=中心势betweeness相关
[3]Jierui Xie & Boleslaw K. Szymanski (2013) LabelRank: A Stabilized Label Propagation Algorithm for Community Detection in Networks <=标签传播labelrank相关
[4]Pascal Pons and Matthieu Latapy (2006) Computing Communities in Large Networks Using Random Walks <=随机游走randomwalk相关
总之,在这里spinglass方法适用于购物车商品分析。
另外,需要注意:
①社群发现必须基于连通图(即,所有点上都在线上,没有孤立的点);
②此处的社群个数对应之后画子图的分组个数。
步骤描述:
对不同商品类别的点配置不同颜色→
建立绘图分组member.list,作为plot函数mark.groups参数的列表对象→
画图并手动添加图例→
可添加点的标签属性vertex.label,呈现原有编号
这部分代码如下:
##社群发现并绘制关系图(自旋玻璃法)
member<-spinglass.community(gdata, weights= E(gdata)$weight)
V(gdata)$member<-member$membership
member.num<-length(table(V(gdata)$member)); member.num #注意:此处的社群个数对应之后的绘图分组 #对不同商品类别的点配置不同颜色
mem.col<-rainbow(length(unique(V(gdata)$category)),alpha=0.5)#注意设置alpha值调节对比度
V(gdata)$color<-mem.col[V(gdata)$category]
#建立设置绘图分组(plot函数的mark.groups参数)的列表对象member.list
member.list<-list()
for(i in 1:member.num)
{
member.list<-c(member.list, list(which(V(gdata)$member==i)))
}
#svg(filename=paste(root, "demol.svg",sep=""), width = 14, height = 14)
#画图并手动添加图例
legend0<-c("dairy eggs","produce","meat seafood","beverages","pantry","bakery","frozen","snacks")
#plot(gdata, vertex.size=10, layout=layout.fruchterman.reingold, vertex.color=V(gdata)$color, edge.width=scale(E(gdata)$weight, center=F)+1, mark.groups=member.list)
plot(gdata, vertex.label=V(gdata)$item, vertex.size=10, layout=layout.fruchterman.reingold, vertex.color=V(gdata)$color, edge.width=scale(E(gdata)$weight, center=F)+1, mark.groups=member.list)
#第二个plot加了label属性
legend("topleft",legend=legend0, pch=16, col=mem.col, bty="n", cex=1)
画出图如下(右图为加了lable标签后的效果,所有点恢复了真实编号,而不是左图中临时的连续编号):
OK! 看上去还不错。
现在我们得到的图里,每个点的颜色对应左侧图例中的不同商品分类(蛋奶制品、烘焙类、冷冻品、零食等等),点与点之间的连线代表两个曾在同一购物篮子(即订单信息order)中出现过。现在利用算法已经发现了五个可能存在的社群,即,在这个图中关系更密切的点的集合,由浅色“冲积扇”形状色块标出。右图中,点的编号就是原mini数据库中的商品号码。现在就可以研究能不能得出有趣的结论了!
对照如下图的数据库,上方右图中编号81,80,31,119的商品位于一个社群中。也许数据量再大些能说明热爱有机蔬果的顾客也偏好矿泉水?
5.绘制子图
为了单独研究形成的各个社群,还可以把关系图拆成子图分别绘制。
有两种方法画子图:
A.设置par,用循环一次性画出;
B.依次画每个图,放大后更清晰
#绘制不同社群内的关系图
#svg(filename=paste(root, "demol.svg",sep=""), width = 14, height = 14)
#par(mfcol=c(3,2))
for(i in 1:length(table(member$membership)))
{
tmp.g<-induced.subgraph(gdata,which(V(gdata)$member==i));V(tmp.g)
member.list<-list()
tmp.category<-as.numeric(names(table(V(tmp.g)$category)))
for(j in tmp.category)
{
member.list<-c(member.list,list(which(V(tmp.g)$category==j)))
}
plot(tmp.g, vertex.size=10,layout=layout.fruchterman.reingold, edge.width=scale(E(tmp.g)$weight,center=F)+1,mark.groups=member.list,vertex.label=V(tmp.g)$item)
#手动添加图例
#legend("topleft",legend= ,pch=16,col=mem.col,bty="n",cex=1)
}
子图如下:
------------------------------------------------分割线----------------------------------------------------
6.完整代码
ls()
rm(list = ls())
#初步读取数据
root="C:/Users/asus/Desktop/"
data<-read.csv(file=paste(root,"购物车.csv",sep=""),header=T,encoding="UTF-8");
colname1<-colnames(data)
colname1[1]<-"order_id"
colnames(data)<-colname1
#由于预先对数据集进行了处理,此处不需要书上分离商品名、类别并编号的步骤
##建立关系网络
#用cast函数转化格式
#重铸函数cast(md,formula,FUN),其中md是已融和的数据,formula描述了想要的结果,
#而FUN是数据整合函数,例如mean,也可自定义多值整合函数。默认为统计函数。 #install.packages('reshape')
library('reshape')
data<-cbind(data,value=1)
#cast返回数据框,再转换成矩阵
cart=as.matrix(cast(data,order_id~p_id,value="value",fill=0))
cart[,-1]<-ifelse(cart[,-1]>=1,1,0)#好像有点多余,因为此数据集中每个购物篮子中的某件商品只被记了一次 #注:这是最开始的数据准备部分,限于篇幅,后面的部分就是前文各小节代码的拼凑综合,不再重复复制粘贴。
参考资料:《R语言与网站分析》[李明著][机械工业出版社][2014.04] 的9.3节《关系网络分析》。
R代码部分引用自原书作者,增加了注释,结合R语言语法的变化也有改动。
7.数据预处理部分的Python代码(可以用R的指令代替)
1.对商品重新编号(商品分类的重新编号类似,此处不赘述)
import openpyxl
import re def Exceldivide(file_dir):
wb=openpyxl.load_workbook(file_dir)
sheet=wb.get_sheet_by_name('prior_order')
tuple(sheet['A1':'E507'])
t=1
for i in range(2,508):
fd=False
for j in range(2,i):
if sheet.cell(row=i, column=4).value==sheet.cell(row=j, column=4).value:
sheet.cell(row=i, column=6).value=sheet.cell(row=j, column=6).value
fd=True
if fd==False:
sheet.cell(row=i, column=6).value=t
t+=1
return wb g=Exceldivide('C:\\Users\\asus\\Desktop\\购物篮子简化版.xlsx')
g.save('C:\\Users\\asus\\Desktop\\购物篮子简化版.xlsx')
2.保留被重复购买过的商品(这一步在数据集较大时可省去)
import openpyxl
import re def Exceldivide(file_dir):
wb=openpyxl.load_workbook(file_dir)
sheet0=wb.get_sheet_by_name('prior_order') #
sheet1=wb.get_sheet_by_name('repeat')
sheet2=wb.get_sheet_by_name('order')
tuple(sheet0['A1':'F507'])
tuple(sheet1['A1':'B45'])
tuple(sheet2['A1':'D45'])
i=1
for rows in sheet0['F2':'F507']:
for cell0 in rows:
for rows2 in sheet1['A2':'A45']:
for cell1 in rows2:
if cell0.value==cell1.value:
i+=1
sheet2.cell(row=i, column=1).value=sheet0.cell(row=int(cell0.coordinate[1:]), column=1).value
sheet2.cell(row=i, column=2).value=sheet0.cell(row=int(cell0.coordinate[1:]), column=2).value
sheet2.cell(row=i, column=3).value=sheet0.cell(row=int(cell0.coordinate[1:]), column=3).value
sheet2.cell(row=i, column=4).value=sheet0.cell(row=int(cell0.coordinate[1:]), column=6).value
return wb g=Exceldivide('C:\\Users\\asus\\Desktop\\购物篮子简化版.xlsx')
g.save('C:\\Users\\asus\\Desktop\\购物篮子简化版.xlsx')
小注:写作本文源于博主小白去年一段做RA的经历,当时与队友们共同学习社会网络分析(Social Network Analysis,SNA),主要参考书是上文提及的《R语言与网站分析》9.3节。博主小白与搭档负责实现书上的两个实例,但由于教材没有提供数据来源、R语言语法近几年的变化,中间费了一番波折,故写作本文,主要内容为博主负责的“购物篮子商品相关性分析”实例,转载请注明来源。如有疏漏,还望指正!
R语言入门级实例——用igragh包分析社群的更多相关文章
-
R语言︱文本挖掘之中文分词包——Rwordseg包(原理、功能、详解)
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 笔者寄语:与前面的RsowballC分词不同的 ...
-
R语言数据分析利器data.table包—数据框结构处理精讲
R语言数据分析利器data.table包-数据框结构处理精讲 R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代 ...
-
R语言&#183;文本挖掘︱Rwordseg/rJava两包的安装(安到吐血)
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- R语言·文本挖掘︱Rwordseg/rJava ...
-
R语言数据分析利器data.table包 —— 数据框结构处理精讲
R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代码简洁,只要一行命令就可以完成诸多任务,另一方面是处理 ...
-
R语言结合概率统计的体系分析---数字特征
现在有一个人,如何对这个人怎么识别这个人?那么就对其存在的特征进行提取,比如,提取其身高,其相貌,其年龄,分析这些特征,从而确定了,这个人就是这个人,我们绝不会认错. 同理,对数据进行分析,也是提取出 ...
-
R语言︱文本挖掘——词云wordcloud2包
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 笔者看到微信公众号探数寻理中提到郎大为Chif ...
-
R语言中的回归诊断-- car包
如何判断我们的线性回归模型是正确的? 1.回归诊断的基本方法opar<-par(no.readOnly=TRUE) fit <- lm(weight ~ height, data = wo ...
-
R语言学习——实例标识符
> patientID<-c(1,2,3,4)> age<-c(25,34,28,52)> diabetes<-c("Type1"," ...
-
R语言简单实现聚类分析计算与分析(基于系统聚类法)
聚类分析计算与分析(基于系统聚类法) 下面以一个具体的例子来实现实证分析.2008年我国其中31个省.市和自治区的农村居民家庭平均每人全年消费性支出. 根据原始数据对我国省份进行归类统计. 原始数据如 ...
随机推荐
-
谈谈LoveLive SIF以及即将诞生的LL练习器
由于课程需要和自身需求以及广大的LLer的需求,这个学期我将做一个造福全世界LLer的安卓app,它的名字是——还没想好(喂),总之是个LL SIF的练习器.什么?你问我LL SIF是什么?看来你不是 ...
-
linux搭建微型git服务器
1.安装git和git-core yum install git git-core -y 2.创建仓库 mkdir /home/git cd /home/git git init 3.设置可以远程pu ...
- Cauchy 级数浓缩判别法
-
Android学习笔记之DocumentBuilder的使用....
PS:当你的才华还撑不起你的野心时,那你需要静下心来学习..... 学习内容: 1.从服务器上获取XML文档... 2.解析XML文档中的内容... XML文件想必大家都非常的熟悉,可扩展的标记语 ...
-
MST(prim)+树形dp-hdu-4756-Install Air Conditioning
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4756 题目意思: n-1个宿舍,1个供电站,n个位置每两个位置都有边相连,其中有一条边不能连,求n个 ...
-
LoadRunner【第五篇】关联
关联的定义及使用场景 关联:将服务器提供动态变化的值存放在变量中,当需要使用该变量时,由LoadRunner自动从服务器响应的信息中获取该值,并在后面使用的过程中进行替换.(也可能是前端页面动态生成的 ...
-
C# -- 接口 (关键字:interface)
C#: 接口(关键字:interface) 1.代码(入门举例) class Program { static void Main(string[] args) { Console.WriteLine ...
-
防火墙iptables 设置
在服务器上架了一个tomcat,指定好端口号,我就开始访问,未果! 公司对服务器(RedHat)端口限制,可谓是滴水不漏! 用iptables 查看防火墙设置: Shell代码 iptables -n ...
-
php学习之mysqli的面向对象
// mySqlTool.php 封装好的工具类 <?php class SqlTool{ private $conn; private $host="localhost" ...
-
Android startActivityForResult 回传数据
一个activity打开新的activity,新的activity关闭之后,返回数据.原来的activity要接收返回的数据,在开启新的activity时,就需要调用startActivityForR ...