基于R语言在地理地图上可视化文化距离矩阵网络的方法
没问题,我帮你把文化邻近性网络叠加到已有的欧亚地图上,还会告诉你怎么扩展到时间演变的可视化。咱们一步步来:
步骤1:预处理文化距离矩阵
首先得把你的宽格式矩阵转成ggplot能识别的长格式,同时过滤掉国家自身的连线(数值为0的条目),再根据阈值筛选符合条件的邻近连线。
先加载需要的工具包:
library(dplyr) library(tidyr)
假设你的矩阵已经读入R,这里用你给的示例数据演示:
# 创建示例文化距离矩阵 culture_matrix <- matrix(c(0, 0.00276, 0.148, 0.109, 0.00276, 0, 0.145, 0.112, 0.148, 0.145, 0, 0.257, 0.109, 0.112, 0.257, 0), nrow = 4, byrow = TRUE, dimnames = list(c("AT", "BE", "CH", "CZ"), c("AT", "BE", "CH", "CZ"))) # 转长格式并筛选符合条件的连线 culture_df <- as.data.frame(culture_matrix) %>% mutate(from = rownames(.)) %>% # 添加起点国家列 pivot_longer(cols = -from, names_to = "to", values_to = "distance") %>% # 宽转长 filter(from != to) %>% # 去掉国家自身的连线 mutate(threshold = mean(distance)) %>% # 计算所有距离的均值作为阈值 filter(distance < threshold) # 筛选出文化距离低于阈值的国家对
步骤2:获取国家中心点坐标
要画连线,得先拿到每个国家的地理中心点坐标,推荐从你已有的Shapefile里提取,也可以用工具包快速获取:
方法1:从你的Shapefile提取(匹配度更高)
如果你用的是sp包的readOGR,可以用rgeos包提取每个国家的中心:
library(rgeos) # 提取Shapefile中所有国家的中心点 country_centroids <- gCentroid(shapefile, byid = TRUE) %>% as.data.frame() %>% rename(long = x, lat = y) %>% mutate(country = rownames(.)) # 这里的rownames对应Shapefile里的国家ID # 筛选出你需要的4个国家(确保ID和你的AT/BE等代码匹配,不匹配的话手动映射) target_centroids <- country_centroids %>% filter(country %in% c("AT", "BE", "CH", "CZ"))
方法2:用rnaturalearth快速获取(更便捷)
如果Shapefile的国家ID不好匹配,直接用rnaturalearth包获取标准坐标:
library(rnaturalearth) library(sf) # 获取欧洲国家数据并筛选目标国家 countries <- ne_countries(continent = "europe", returnclass = "sf") %>% select(iso_a2, geometry) %>% filter(iso_a2 %in% c("AT", "BE", "CH", "CZ")) %>% mutate(centroid = st_centroid(geometry)) %>% mutate(long = st_coordinates(centroid)[,1], lat = st_coordinates(centroid)[,2]) %>% select(iso_a2, long, lat) %>% rename(country = iso_a2)
步骤3:生成连线的线段数据
现在要把每个符合条件的国家对,转换成ggplot能绘制线段的格式——每个连线需要两行数据(起点和终点),并用link_id区分不同连线:
# 合并起点和终点的坐标 links_data <- culture_df %>% left_join(countries, by = c("from" = "country")) %>% rename(from_long = long, from_lat = lat) %>% left_join(countries, by = c("to" = "country")) %>% rename(to_long = long, to_lat = lat) # 转换成线段数据格式 segment_data <- links_data %>% rowwise() %>% mutate(segment = list(tibble(long = c(from_long, to_long), lat = c(from_lat, to_lat)))) %>% unnest(segment) %>% group_by(from, to) %>% mutate(link_id = cur_group_id())
步骤4:叠加网络到现有地图
把生成的线段数据加到你原来的ggplot地图上,还能根据文化距离调整连线的颜色/粗细,更直观体现邻近程度:
# 保留你原来的基础地图代码 shapefile <- readOGR("directory_with_file", "name_of_file") shapefile_df <- fortify(shapefile) map <- ggplot() + geom_path(data = shapefile_df, aes(x = long, y = lat, group = group), color = 'black', size = .2) # 添加文化邻近性连线 map_with_network <- map + geom_line(data = segment_data, aes(x = long, y = lat, group = link_id, color = distance), size = 1, alpha = 0.7) + # alpha调整透明度,避免遮挡地图 scale_color_viridis_c(option = "plasma", direction = -1) + # 距离越小颜色越深 theme_minimal() + labs(title = "欧亚国家文化邻近性网络", color = "文化距离") print(map_with_network)
如果想要更美观的曲线连线,把geom_line换成geom_curve即可:
geom_curve(data = links_data, aes(x = from_long, y = from_lat, xend = to_long, yend = to_lat, color = distance), curvature = 0.1, size = 1, alpha = 0.7)
步骤5:处理时间演变(扩展)
如果你的文化距离矩阵是随时间变化的(比如每年一个矩阵),可以用两种方式展示演变:
方法1:分面展示不同年份
把所有年份的数据整理成带year列的长格式,然后用分面:
# 示例多年份数据(实际替换成你的真实数据) culture_df_2010 <- culture_df %>% mutate(year = 2010) culture_df_2020 <- culture_df %>% mutate(year = 2020) culture_all <- bind_rows(culture_df_2010, culture_df_2020) # 重复步骤3生成带年份的线段数据(过程略,保留year列即可) # ... # 分面绘制 map_with_timeline <- map + geom_line(data = segment_data_all, aes(x = long, y = lat, group = link_id, color = distance), size = 1, alpha = 0.7) + scale_color_viridis_c(option = "plasma", direction = -1) + facet_wrap(~year) + theme_minimal() + labs(title = "欧亚国家文化邻近性网络(时间演变)", color = "文化距离")
方法2:动画展示(用gganimate)
想要动态效果的话,用gganimate包生成动画:
library(gganimate) animated_map <- map + geom_line(data = segment_data_all, aes(x = long, y = lat, group = link_id, color = distance), size = 1, alpha = 0.7) + scale_color_viridis_c(option = "plasma", direction = -1) + transition_time(year) + labs(title = "文化邻近性网络:Year {frame_time}", color = "文化距离") + ease_aes('linear') # 渲染动画 animate(animated_map, nframes = 20, fps = 5)
内容的提问来源于stack exchange,提问作者Anastasia




