[读书笔记]机器学习:实用案例解析(11)

 

第11章 分析社交图谱

因为twitter的api方式改变了,因此按照书上的方法已经不能从twitter上获取到数据了,只能采用代码中附上的数据进行分析,而我安装的gephi无法打开图文件(.graphml)。因此本章仅讨论分析社交的思路,如果后面对web理解深入一点,再把调用api的部分补上。

“个体网络”:指在网络中直接包围在一个单独节点周围的社交关系结构;是一个网络的子集,包括一个种子(个体)和它的邻居,也就是直接与种子相连的节点、节点与种子相连的边、以及各节点之间的边。

以图的方式进行思考:无向图、有向图、带权有向图。

“入边”(edges in)与“出边”(edges out):入边可以看做是粉丝;出边可以看做是朋友或者关注的人,对于个体的研究,出边比较有意义,可以推荐个体希望关注的人。

调用API获取数据部分(暂时略)

=============================================================================

分析社交网络数据:

第一步:提取图的核心元素。

(1)“k核分析”:提取图形的2核子图:基于节点的连通性分解一个图,k核分析会得到一个k度的子图,2核分析就是由度大于等于2的节点构成的子图。选择2度的原因是,搜索方式的副作用会在网络外围产生很多单边连接的附属节点,贡献很少,需要删除。

(2)种子节点的个体网络:只考虑出边连接的节点。

 

第二步:对个体网络进行分析。

(1)测量图中所有节点之间的距离

(2)根据距离进行层次聚类,最开始所有节点都在一个大类里,最后分到每个节点单独一个类,画出聚类树状图。

library(igraph)
source('ML_for_Hackers/11-SNA/01_google_sg.R')

#载入数据
user <- 'johnmyleswhite'
user.net <- read.graph(paste("ML_for_Hackers/11-SNA/data/", user, "/", user, "_net.graphml", sep = ""), format = "graphml")
user.net <- set.vertex.attribute(user.net, "Label", value = get.vertex.attribute(user.net, "name"))
#2核分析得到一个2度子图
user.cores <- graph.coreness(user.net, mode = "in")
user.clean <- subgraph(user.net, which(user.cores>1))
#得到种子节点的个体网络子图
user.ego <- subgraph(user.net, c(1, neighbors(user.net, user, mode = "out")))
#测量图中所有 节点之间的距离
user.sp <- shortest.paths(user.ego)
#dist()函数由生成距离矩阵(主要目的是将user.sp的格式转换为hclust()可用的格式);
#hclust()函数进行聚类,返回全部聚类信息的对象
user.hc <- hclust(dist(user.sp))
#画出聚类树状图
plot(user.hc)

  

 

使用Gephi可视化聚类网络(略)

建立“感兴趣的人”引擎:

基本原理:朋友的朋友是朋友(因为所谓的“敌人”在社交网络的好友功能无法体现,因此不考虑“敌人”的情况)

#设置研究对象
user <- 'drewconway'
user.graph <- read.graph(paste("ML_for_Hackers/11-SNA/data/", user, "/", user, "_net.graphml", sep = ""), format = "graphml")
#获取种子的所有朋友的用户名.V()可以返回图的某一特定属性,这里即是name
friends <- V(user.graph)$name[neighbors(user.graph, user, mode = "out")]
#get.edgelist()函数生成图的完整边列表
user.el <- get.edgelist(user.graph)
#检查矩阵中每一行是否包含了种子用户没有关注的“朋友的朋友”
#ifelse()函数的逻辑:首先判断是否是种子用户或者第一个元素(起点)不是种子用户的朋友,两者之一为真就跳过这一行;
#                    再看当前行的第二个元素(目标节点)是否是种子用户的朋友,若为真则跳过这一行
non.friends <- sapply(1:nrow(user.el), function(i) ifelse(any(user.el[i, ]==user | !user.el[i, 1] %in% friends) | user.el[i, 2] %in% friends, FALSE, TRUE))
#提取合适的行,并建立包含名字出现次数的表格
non.friends.el <- user.el[which(non.friends==TRUE), ]
friends.count <- table(non.friends.el[, 2])
#找到这份数据中出现最多的用户,用table()函数创建的向量来创建一个数据框;
#并使用归一化方法计算最应该被关注的推荐用户,即计算种子用户的朋友关注候选推荐用户的百分比
#最后根据百分比递减排序,查看前10条数据
friends.followers <- data.frame(list(Twitter.Users = names(friends.count), Friends.Following = as.numeric(friends.count)), stringsAsFactors = FALSE)
friends.followers$Friends.Norm <- friends.followers$Friends.Following/length(friends)
friends.followers <- friends.followers[with(friends.followers, order(-Friends.Norm)), ]
friends.followers[1:10, ]

  

但是这个推荐结果里面有很多用户是种子已经关注的人了。另一种思路是,除了推荐“朋友的朋友外”,也可以推荐某个已知维度上和种子用户类似的朋友的朋友,也就是通常所说的圈子。

#推荐某个圈子中的“朋友的朋友”
#结果的friends.partitions矩阵中,第一列是分割编号,第二列是用户名
user.ego <- read.graph(paste("ML_for_Hackers/11-SNA/data/", user, "/", user, "_ego.graphml", sep = ""), format = "graphml")
friends.partitions <- cbind(V(user.ego)$HC8, V(user.ego)$name)
head(friends.partitions)

  

#这个函数的目的是,输入分割编号(不同的编号代表不同的圈子),输出这个分割编号里被种子用户的朋友关注最多的用户,也就是要给种子用户推荐的那个用户
partition.follows <- function(i) {
  friends.in <- friends.partitions[which(friends.partitions[, 1] == i), 2]
  partition.non.follow <- non.friends.el[which(!is.na(match(non.friends.el[, 1], friends.in))), ]
  if (nrow(partition.non.follow) < 2) {
    return(c(i, NA))
  } else {
    partition.favorite <- table(partition.non.follow[, 2])
    partition.favorite <- partition.favorite[order(-partition.favorite)]
    return(c(i, names(partition.favorite)[1]))
  }
}

partition.recs <- t(sapply(unique(friends.partitions[, 1]), partition.follows))
partition.recs <- partition.recs[!is.na(partition.recs[, 2]) & !duplicated(partition.recs[, 2]), ]

  

结果如图

 

posted @ 2016-10-30 20:57  gy_jerry  阅读(712)  评论(0编辑  收藏  举报