如何用 R 绘制交互式社会网络图?

挖掘社会关系网络,助你洞若观火。

需求

最近有个学生问我,如何绘制交互式社会网络图(Interactive Social Network Graph)?

之所以一定要交互式,是因为他的应用场景,是演示给客户。

他解释说,如果客户有选项,可以根据需要来缩放图形、聚焦类别,甚至是可以拖拽图形元素,以不同视角来查看,那展示效果显然会更好。

我对学生这次把握客户需求的能力,还是比较赞赏的。确实,数据可视化的目的就是为了与人沟通。而人是需要控制感的。

在数据科学里,这个需求,属于网络可视化(Network Visualization)范畴。应用上,除了描绘社会网络外,这种可视化的对象还可以包括引文网络、贸易网络、信息(或疾病)传播网络等。甚至,网络可视化操作还能与时序数据结合,例如 Maximilian Noichl 绘制的这张古代哲学家的动态关系网络图。

网络可视化的工具,是非常多的。

然而,一旦涉及了交互式,选项就大幅减少了。特别是,他还要求免费易学格式开放、演示环境易于部署……

这条件,够苛刻了吧?

还好,我还真就帮他找到了。

虽然易学,可是由于他是文科生,我觉得还是给他写个教程,比较妥帖。

教程写完之后,我觉得你可能也会用得上,所以一并分享给你。

演示数据,我自然不能用他的。否则有泄露商业机密的风险。这里我采用的,是斯坦福大学开放课程《数据库基础》中的一个简单数据样例。我在国际班讲的英文数据库课程,一直用它作为练习数据。

这个演示数据的特点,就是简单。

有多简单?往下看。

数据

该数据刻画的,是某高中里4个年级部分学生之间的社交关系。

我们一共需要用到3张数据表格,分别是学生信息、朋友关系和“喜欢”关系。

先看学生信息表。我已经把它上传到了这个 github 链接,你可以点击查看。

表格包含3列,分别是学号、姓名和年级。

然后是朋友关系表。请点击这个链接查看。

除去表头,一共20行。包含两列,代表了关系两端的两个学生的学号。

注意“朋友”关系是一种双向(或者无向)关系。两个人都认可的,才算是朋友。我在人群中,一眼就能认出某影视巨星。可惜,人家不认识我,这显然就不算朋友关系。

再后面,是“喜欢”(likes)关系表。我放在了这个链接

注意这里虽然也描述了关系的起点和终点,但是“喜欢”关系是一种有向关系。张三喜欢李四,李四可能并不喜欢张三。

好了,这就是我们需要用到的全部数据了。下面我们来看看运行环境。

环境

我们使用的,是 R 的集成开发环境(integrated development environment, IDE) RStudio 。

你可以在本机安装 R 以及 RStudio 。我已经把安装和设置步骤写在了《如何用 R 快速了解科研领域?》一文中。

配套的代码和数据,我放在了这个 github 仓库中。你可以下载使用。

然而,此处为了你更快捷地上手,我直接为你准备了云端的运行环境。你只需要点击这个链接http://t.cn/EJ5U8o6),就可以直接在浏览器里面开启一个 RStudio 。其中,已包含了我为你准备好的数据和代码。

双击右下方目录区域里面的 demo.Rmd,你就能看到对应的笔记本了。

怎么样?够方便吧?

其实,你自己也可以构建这样的云端环境。具体的方法,我已经在《如何用iPad运行Python代码?》一文中为你详细介绍了。如果你感兴趣,可以在学过本篇教程后,尝试练练手。

下面我们开始介绍代码了。请你根据我的介绍,逐步点击代码模块旁边的运行按钮,查看运行的结果。

代码

首先,我们需要读入本教程中最重要的软件包,也就是 R 环境下的网络交互可视化工具—— visNetwork

visNetwork 基于 Javascript 可视化工具库 vis.js 开发,为 R 用户提供了简单易用的界面,而且功能也很强大。

我们使用 library 命令来读入它。

library('visNetwork')

下面我们来读入数据。

首先,我们观察“朋友”关系。读入学生信息表和朋友关系表。

nodes <- read.csv("data/Highschooler.csv", header=T, as.is=T)
friends <- read.csv("data/Friend.csv", header=T, as.is=T)

注意这里的参数:

  • header=T 是指我们读入的数据里包含了表头,需要让 R 注意,不要把表头也当成了数据来处理;
  • as.is=T 是指读入的字符串数据,不要默认按照因子来转换。这其实是一种技巧。因为如果大量数据默认做转换,可能会导致读取效率很低。当然,对于我们的例子来说,因为数据量很小,实际上效率差别不大。但是好习惯还是需要养成的。

下面我们要在节点上生成一些属性。我们希望把鼠标挪到某个节点上的时候,显示该学生属于“某年级”;而生成图像的时候,直接在节点旁边标明学生的姓名。

因此,我们就需要分别对 nodes$titlenodes$label 赋值。

之后,用 visNetwork 可视化我们的节点和关系连线。

nodes$title <- paste0("grade", nodes$grade)
nodes$label <- nodes$name
visNetwork(nodes, friends)

结果如下图所示:

注意,这可不是一张静态图,你把鼠标悬浮到 Alexis 人名上试试看。

年级属性就出现了。

你还可以拖动任意一个学生节点,感受一下什么叫做“牵一发而动全身”。

有趣吧?

但是现在所有的节点,都是一样的颜色。我们希望依据不同的年级,重新绘制节点颜色,这样看得会更清晰。

我们依据年级属性(nodes$grade),来定义节点的背景颜色(nodes$color.background)。

然后再次调用 visNetwork,进行绘制。

nodes$color.background <- c("orange", "pink", "yellow", "blue")[nodes$grade]
visNetwork(nodes, friends)

结果是……

不对呀,说好的颜色变化呢?

别着急。

如果让 R 根据不同属性来区分颜色,我们首先需要保证该属性类型是因子(factor)。可是我们读取的时候,为了效率,没让 R 自动转换。

怎么办?

手动来做吧。

nodes$grade <- as.factor(nodes$grade)
nodes$color.background <- c("orange", "pink", "yellow", "blue")[nodes$grade]
visNetwork(nodes, friends)

这次,我们再看看结果:

嗯,感觉好极了。

这里节点很少,全部同时显示,也能看得清晰。但是假设我们需要处理一所真正学校中的朋友关系,可以想象那会有成百上千个节点。如果我们希望聚焦,那就得给用户更多的交互功能。

这里我给你介绍其中一个选项,就是利用 selectedBy ,指定我们让用户在哪一个属性上进行分组选择。

你需要把它放在 visOptions 中。

visNetwork(nodes, friends) %>%
  visOptions(selectedBy = "grade")

运行效果是这样的:

看起来,跟刚刚区别不大,只是多了一个下拉框而已。

我们尝试选择一下:

交互选项,让当前分组保持高亮,其他分组变灰暗,于是我们的注意力就可以集中。

尝试着玩儿一下,看看你能否发现什么有趣的关系模式?

我发现了一个。

大部分情况下,同一个年级的学生间,总会可以关联起来。但是,11年级的 Austin ,跟本年级的其他3名同学,没有任何直接联系。反而,跟其他年级的学生,保持了朋友关系。

如果你是他的班主任,要不要关注一下?

下面,我们来看看另外一种关系——“喜欢”(likes)。

还是读入 csv 文件,到 likes 数据框里面。参数跟刚才一样。

likes <- read.csv("data/Likes.csv", header=T, as.is=T)

先来绘制看看。

visNetwork(nodes, likes)

我们一下子就发现了,这次的整体图形,不再是全连通的。出现了孤立节点。

这些人,既没有“喜欢”别人,也没有“被喜欢”。你自己找找看,都包括哪几个学生?

注意这个图形,是有问题的。

前面提到过,“喜欢”关系是一种有向关系。因此关系的方向很重要。但是目前这张图里面,方向是缺失的。

没关系,只需要给 likes 加入一个属性 arrow 就好。

likes$arrows <- "to"
visNetwork(nodes, likes)

这次就对了。

下面我们来玩儿一个更有趣的——把两个关系合并,一起观察。

我们需要记录一下,关系来自于哪张关系表格。所以分别把表名称作为属性,填到 relation 字段中。

friends$relation <- c("friend")
likes$relation <- c("likes")

看看这时候的 friends 朋友关系表。

friends

好了,下面我们需要合并两张表格。我们需要用到 tidyverse 软件包中的按行合并(bind_rows)功能。所以,需要首先载入tidyverse 软件包。

library(tidyverse)

然后,把原先的两张关系表,合并成一张,起名叫做 links

links <- bind_rows(friends, likes)
links

再次绘制,注意这次,关系位置上,我们放的是合并的 links

visNetwork(nodes, links)

两种关系,确实都绘制好了。可问题是,关系展示的颜色是一样的蓝色,看着不是很清晰。

仿照刚才对节点的颜色赋值,我们把关系连接的颜色也设置一下。

links$color <- c("green", "red")[links$relation]
visNetwork(nodes, links)

呃……好像没有变化嘛。

聪明的你,一定可以举一反三,发现还是由于“关系”(links$relation)属性不是“因子”导致的。

我们把 relation 属性转换成因子类型。

links$relation <- as.factor(links$relation)
links$color <- c("green", "red")[links$relation]
visNetwork(nodes, links)

太棒了!

下面,我们还是以年级作为交互选项,加入进来。

links$relation <- as.factor(links$relation)
links$color <- c("green", "red")[links$relation]
visNetwork(nodes, links) %>%
  visOptions(selectedBy = "grade")

我们选择一下年级,拖动节点看看:

以上,是样例代码中,出现的内容解释。

小结

通过本文的学习,相信你已经掌握如何把社会网络的表格数据,用交互可视化的方法展现出来。

对于不同的元素,你也已经学会了基本的展现方式。

但请你一定不要止步于此。

首先,你需要继续查看文档。里面还有很多选项参数,此处我们没有涉及。例如说,这里我们只绘制了“关系”(边),但是却没有在其上进行任何文字标记。

如果你需要生成非常严肃的数据可视化作品,这些内容很可能用得上。请你点击这个链接,查看完整的文档。从中选择自己感兴趣的部分深入研读。

另外,这个工具,也绝不仅仅可以帮助你绘制社会网络图。只要是适合用网络图展现的内容,它都可以发挥作用。

希望你充分发挥自己举一反三的能力,把这项新技能用好。

祝学习愉快!

延伸阅读

你可能也会对以下话题感兴趣。点击链接就可以查看。

喜欢请点赞和打赏。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)

如果你对 Python 与数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335