用R在地图上绘制网络图的三种方法

地理网络图与传统的网络图不同,当引用地理位置进行节点网络可视化时,需要将这些节点放置在地图上,然后绘制他们之间的连结。Markus konrad的帖子(https://datascience.blog.wzb.eu/2018/05/31/three-ways-of-visualizing-a-graph-on-a-map/),非常赞。其中他的部分思路对于我们学习可视化很有帮助。

首先准备需要的R包,当需要一次性加载多个R包时,我们可以利用pacman,它整合了library包中的一些相关函数,利用pacman包中的p_load函数可以自动加载需要的R包,如果没有找到则会自动安装缺失的R包。这样我们就不用写很多行library命令了,从而使代码变得简单些。

library(pacman)
p_load(assertthat,tidyverse,ggraph,igraph,ggmap)

为了方便大家练习,仅挑出部分国家地理位置,如下:

country_coords_txt <- "
 1     3.00000  28.00000       Algeria
 2    54.00000  24.00000           UAE
 3   139.75309  35.68536         Japan
 4    45.00000  25.00000 'Saudi Arabia'
 5     9.00000  34.00000       Tunisia
 6     5.75000  52.50000   Netherlands
 7   103.80000   1.36667     Singapore
 8   124.10000  -8.36667         Korea
 9    -2.69531  54.75844            UK
10    34.91155  39.05901        Turkey
11  -113.64258  60.10867        Canada
12    77.00000  20.00000         India
13    25.00000  46.00000       Romania
14   135.00000 -25.00000     Australia
15    10.00000  62.00000        Norway"

nodes <- read.table(text = country_coords_txt, header = FALSE, quote = "'",sep = "\t",col.names = c("id","lon","lat","name"))

现在我们有了15个国家的地理坐标(LON和LAT)和国家名字,这些就是之后要在地图中展现的节点,下面我们需要在这些节点之间随机创建一些连结,方便之后将不同国家连起来。

# 生成随机数种子,保证结果的重复性
set.seed(42)  
min <- 1
max <- 4
n_categories <- 4

# edges:建立国家之间的随机连结
edges <- map_dfr(nodes$id, function(id){
  n <- floor(runif(1,min,max+1))
  to <- sample(1:max(nodes$id),n ,replace = FALSE)
  to <- to[to!=id]
  categories <- sample(1:n_categories,length(to), replace = TRUE)
  weight <- runif(length(to))
  data_frame(from=id, to=to, weight=weight, category=categories)
})
edges <- edges%>%mutate(category=as.factor(category))

上面我们已经创建好了节点(node)以及连接(edge),并且还生成了连结之间的类别(categories)和权重(weight),下面就进行可视化。

生成图形结构

下面创建一个绘制边缘的数据框架。

(g <- graph_from_data_frame(edges, directed = FALSE, vertices = nodes))

此外,还需要再额外定义四列用来绘制节点的起始位置。

edges_for_plot <- edges%>%
  inner_join(nodes%>%select(id, lon, lat),by=c("from"="id"))%>%
  rename(x=lon, y=lat)%>%
  inner_join(nodes%>%select(id,lon,lat),by=c("to"="id"))%>%
  rename(xend=lon,yend=lat)
assert_that(nrow(edges_for_plot)==nrow(edges))

# 给每个节点一个权重(weight)值,在之后的绘图中将反应在节点的大小上
nodes$weight <- degree(g)

下面再定义以下ggplot2主题用来绘制地图。

# 定义主题
maptheme <- theme(
  panel.grid = element_blank(),
  axis.text = element_blank(),
  axis.ticks = element_blank(),
  axis.title = element_blank(),
  legend.position = "bottom",
  panel.background = element_rect(fill="#596673"),
  plot.margin = unit(c(0,0,0.5,0),"cm")
)

# 指定`data=map_data("world")`保证每个节点共享同一世界地图中的坐标系
country_shape <- geom_polygon(aes(x=long, y=lat, group=group),
                              data=map_data("world"),
                              fill="#CECECE", color="#515151",size=0.1)

# coord_fixed函数可以改变xy轴的范围
mapcoords <- coord_fixed(xlim=c(-150,180), ylim=c(-55,80))

方法一:ggplot2

除了需要世界地图(country_shape)中国家边界外,我们还需要三个几何对象:

  1. geom_point:绘制节点;

  2. geom_text:添加节点的标签名字;

  3. geom_curve:绘制节点间的连线(edge)。

此外我们需要定义aesthetic来规定数据如何可视化地映射在地图上

  1. 对于节点(nodes):将各个地理坐标映射到画板的x、y位置,并且节点的大小取决于权重大小;

  2. 对于连线(edges):使用edges_for_plot数据集,xendyend指定连线的起始和重点,按照category着色,根据weight来指定连线的粗细。

注意:geoms的顺序很重要,因为它定义了先绘制哪个对象,先绘制的将被后面的图层覆盖。因此我们先绘制了连线(edges),然后绘制节点(nodes),最后绘制节点的标签(labels)。

ggplot(nodes)+country_shape+
  geom_curve(aes(x=x,y=y,xend=xend,yend=yend,color=category,size=weight),
             data=edges_for_plot,curvature = 0.33,alpha=0.5)+
  scale_size_continuous(guide = FALSE,range = c(0.25,2))+  # scale for edge widths
  geom_point(aes(x=lon,y=lat,size=weight),  # draw nodes
             shape=21,fill="white",color="black",stroke=0.5)+
  scale_size_continuous(guide = FALSE, range = c(1,6))+ # scale for node size
  geom_text(aes(x=lon,y=lat,label=name), # draw text labels
            hjust=0,nudge_x = 1,nudge_y = 4,
            size=3,color="white",fontface="bold")+
  mapcoords+maptheme

图片

方法二:ggplot2+ggraph

ggplot2有一个名叫gggraph的扩展包(点我了解更多的ggplot2扩展包)专门为网络图的绘制添加了geoms美学,它可以帮助我们对节点和连线使用单独的标度(scales)。

nodes_pos <- nodes%>%
  select(lon,lat)%>%
  rename(x=lon,y=lat)
lay <- create_layout(g,"manual",node.position=nodes_pos)
assert_that(nrow(lay)==nrow(nodes))

# add node degree for scaling the node sizes
lay$weight <- degree(g)

# 使用gggraph包中的geom_edge_arc和geom_node_point函数进行绘图
ggraph(lay)+
  country_shape+
  geom_edge_arc(aes(color=category,edge_width=weight,circular=FALSE),
                data = edges_for_plot,curvature = 0.33,alpha=0.5)+
  scale_edge_width_continuous(range = c(0.5,2),guide=FALSE)+
  geom_node_point(aes(size=weight),shape=21,fill="white",color="black",stroke=0.5)+
  scale_size_continuous(range = c(1,6),guide = FALSE)+
  # 指定repel = TRUE来分发各个节点的标签
  geom_node_text(aes(label=name),repel = TRUE, size=3,color="white",fontface="bold")+
  mapcoords+maptheme

图片

方法三:图形叠加

图形叠加需要一个透明背景,可通过下面的命令创建。

theme_transp_overlay <- theme(
  panel.background = element_rect(fill="transparent",color=NA),
  plot.background = element_rect(fill="transparent",color=NA)
)

在透明的背景上添加地图。

这里介绍一个技巧,我们可以将绘图代码放置在()中,运行一句命令即可将图形显示在你的RStudio中,而不需要再次运行p_base

(p_base <- ggplot() + country_shape + mapcoords + maptheme)

图片

下面创建第一个需要覆盖在地图上的图层——各节点之间的连线(edges)。

(p_edges <- ggplot(edges_for_plot)+
  geom_curve(aes(x=x,y=y,xend=xend,yend=yend,color=category,size=weight), # draw edges as arcs
             curvature = 0.33,alpha=0.33)+
  scale_size_continuous(guide = FALSE, range = c(0.5, 2)) + # scale for edge widths
  mapcoords + maptheme + theme_transp_overlay +
  theme(legend.position = c(0.5, -0.1),
        legend.direction = "horizontal"))

图片

然后是绘制第二个需要叠加的图层——节点(nodes)

(p_nodes <- ggplot(nodes) +
  geom_point(aes(x = lon, y = lat, size = weight),
             shape = 21, fill = "white", color = "black",   # draw nodes
             stroke = 0.5) +
  scale_size_continuous(guide = FALSE, range = c(1, 6)) +   # scale for node size
  geom_text(aes(x = lon, y = lat, label = name),            # draw text labels
            hjust = 0, nudge_x = 1, nudge_y = 4,
            size = 3, color = "white", fontface = "bold") +
  mapcoords + maptheme + theme_transp_overlay)

图片

最后需要用annotation_custom(ggplotGrob)p_edgesp_nodes添加到p_base上,三个图形就叠加在一起了。之后还需要手动多次调整p_edgesp_nodes在垂直方向上的位置。

p <- p_base+
  annotation_custom(ggplotGrob(p_edges), ymin = -74)+
  annotation_custom(ggplotGrob(p_nodes), ymin = -74)
print(p)

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/785247.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统编程——线程控制

目录 一&#xff0c;关于线程控制 二&#xff0c;线程创建 2.1 pthread_create函数 2.2 ps命令查看线程信息 三&#xff0c;线程等待 3.1 pthread_join函数 3.2 创建多个线程 3.3 pthread_join第二个参数 四&#xff0c;线程终止 4.1 关于线程终止 4.2 pthread_exit…

LeetCode 算法:腐烂的橘子 c++

原题链接&#x1f517;&#xff1a;腐烂的橘子 难度&#xff1a;中等⭐️⭐️ 题目 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#…

Java版Flink使用指南——定制RabbitMQ数据源的序列化器

大纲 新建工程新增依赖数据对象序列化器接入数据源 测试修改Slot个数打包、提交、运行 工程代码 在《Java版Flink使用指南——从RabbitMQ中队列中接入消息流》一文中&#xff0c;我们从RabbitMQ队列中读取了字符串型数据。如果我们希望读取的数据被自动化转换为一个对象&#x…

JAVA案例ATM系统

一案例要求&#xff1a; 首先完成ATM的用户登录和用户开户两个大功能&#xff0c;用户开户有账户名&#xff0c;性别&#xff0c;账户密码&#xff0c;确认密码&#xff0c;每次取现额度&#xff0c;并且随机生成一个7位数的账号&#xff0c;用户登录功能有查询&#xff0c;存…

k8s 部署 metribeat 实现 kibana 可视化 es 多集群监控指标

文章目录 [toc]环境介绍老(来)板(把)真(展)帅(示)helm 包准备配置监控集群获取集群 uuid生成 api_key配置 values.yaml 配置 es 集群获取集群 uuid 和 api_key配置 values.yaml 查看监控 缺少角色的报错 开始之前&#xff0c;需要准备好以下场景 一套 k8s 环境 k8s 内有两套不同…

Aqara 发布多款智能照明新品,引领空间智能新时代

7月8日&#xff0c;全球 IoT 独角兽品牌 Aqara 以“光&#xff0c;重塑空间想象”为主题&#xff0c;举办了线上智能照明新品沟通会。 会上&#xff0c;Aqara 正式发布一系列引领行业的智能照明新品&#xff0c;包括银河系列轨道灯 V1 以及繁星系列妙控旋钮 V1 等&#xff0c;…

Hospital Management System v4.0 SQL 注入漏洞(CVE-2022-24263)

前言 CVE-2022-24263 是一个影响 Hospital Management System (HMS) v4.0 的 SQL 注入漏洞。这个漏洞允许攻击者通过注入恶意 SQL 代码来获取数据库的敏感信息&#xff0c;甚至可能控制整个数据库。以下是对这个漏洞的详细介绍&#xff1a; 漏洞描述 在 Hospital Management…

使用Keil 点亮LED灯 F103ZET6

1.新建项目 不截图了 2.startup_stm32f10x_hd.s Keil\Packs\Keil\STM32F1xx_DFP\2.2.0\Device\Source\ARM 搜索startup_stm32f10x_hd.s 复制到项目路径&#xff0c;双击Source Group 1 3.项目文件夹新建stm32f10x.h&#xff0c; 新建文件main.c #include "stm32f10x…

OS-HACKNOS-2.1

确定靶机IP地址 扫描靶机开放端口信息 目录扫描 访问后发现个邮箱地址 尝试爆破二级目录 确定为wordpress站 利用wpscan进行漏洞扫描 #扫描所有插件 wpscan --url http://192.168.0.2/tsweb -e ap 发现存在漏洞插件 cat /usr/share/exploitdb/exploits/php/webapps/46537.txt…

Camera Raw:裁剪

Camera Raw 的裁剪 Crop面板提供了裁剪、旋转、翻转、拉直照片等功能&#xff0c;通过它们可以更精确地调整照片的视角和范围&#xff0c;以达到最佳二次构图的视觉效果。 快捷键&#xff1a;C ◆ ◆ ◆ 使用方法与技巧 1、使用预设 选择多种裁剪预设&#xff08;如 1:1、16:…

前端传到后端的data数组中有些属性值为空

将前端输入框中的值全部放入data中传入后端&#xff0c;但是在后端查看发现后端接收到的数据有些属性值为空。 第一种情况&#xff1a;只有第一个属性为空&#xff0c;其余属性接收正常 可能原因&#xff1a;后端用来接收的 比如前端发送数据&#xff1a; 实际上前端发送的数…

防火墙详解(USG6000V)

0、防火墙组网模式 防火墙能够工作在三种模式下分别是路由模式、透明模式、旁路检测模式、混合模式 0.1、路由模式 路由模式&#xff1a;防火墙全部以第三层对外连接&#xff0c;即接口具有IP 地址。一般都用在防火墙是边界的场景下 防火墙需要的部署/配置&#xff1a; 接…

【Excel】 批量跳转图片

目录标题 1. CtrlA全选图片 → 右键 → 大小和属性2. 取消 锁定纵横比 → 跳转高度宽度 → 关闭窗口3. 最后一图拉到最后一单元格 → Alt吸附边框![](https://i-blog.csdnimg.cn/direct/d56ac1f41af54d54bb8c68339b558dd1.png)4. CtrlA全选图片 → 对齐 → 左对齐 → 纵向分布!…

C++初探究

概述 C可以追溯到1979年&#xff0c;C之父Bjarne Stroustrup在在使用C语言研发工作时发现C语言的不足&#xff0c;并想要将其改进&#xff0c;到1983年&#xff0c;Bjarne Stroustrup在C语言的基础上添加了面向对象编程的特性&#xff0c;设计出了C的雏形。 网址推荐 C官方文…

Java面试八股之MySQL主从复制机制简述

MySQL主从复制机制简述 MySQL的主从复制机制是一种数据复制方案&#xff0c;用于在多个服务器之间同步数据。此机制允许从一个服务器&#xff08;主服务器&#xff09;到一个或多个其他服务器&#xff08;从服务器&#xff09;进行数据的复制&#xff0c;从而增强数据冗余、提…

HTTP 请求走私漏洞详解

超详细的HTTP请求走私漏洞教程&#xff0c;看完还不会你来找我。 1. 简介 HTTP请求走私漏洞&#xff08;HTTP Request Smuggling&#xff09;发生在前端服务器&#xff08;也称代理服务器&#xff0c;一般会进行身份验证或访问控制&#xff09;和后端服务器在解析HTTP请求时&…

YASKAWA安川Σ-V系列伺服驱动器AC设计维护手侧

YASKAWA安川Σ-V系列伺服驱动器AC设计维护手侧

C#——序列化和反序列化概念

(1)序列化 在编程中&#xff0c;序列化是指将对象转换为可存储或传输的格式&#xff0c;例如将对象转换为 JSON 字符串或字节流。 (2)反序列化 在编程中&#xff0c;反序列化则是将存储或传输的数据转换回对象的过程。 序列化和反序列化经常用于数据的持久化、数据交换以及…

JAVA基础-----包装类,自动装箱、拆箱

一、包装类&#xff1a; Java中的数据类型总体上分为基本数据类型和引用数据类型。引用类型的数据可以通过对象的属性和方法来进行操作&#xff0c;但对于基本数据类型的数据&#xff0c;我们能不能像操作对象那样来操作呢&#xff1f;为了实现这个目标&#xff0c;Java为8种基…

WebOffice在线编微软Offfice,并以二进制流的形式打开Word文档

在日常办公场景中&#xff0c;我们经常会遇到这种场景&#xff1a;我们的合同管理系统的各种Word,excel,ppt数据都是以二进制数组的形式存储在数据库中&#xff0c;如何从数据库中读取二进制数据&#xff0c;以二进制数据作为参数&#xff0c;然后加载到浏览器的Office窗口&…