最近给大家分享了上市公司子公司数据,里面绘制了一副上市公司的投资网络图:
1999~2023 年上市公司子公司经纬度及其所处的省市区县数据:https://rstata.duanshu.com/#/brief/course/24376370188e4622b124f4a3199e395b

这幅图使用 R 语言绘制。今天我们一起来学习下如何在 R 语言中绘制这样的网络图。
在附件中我准备了三份数据:
- 2023年上市公司列表.dta
- 2023年上市公司子公司数据.dta
- 上市公司被投资公司列表.dta
前两份数据来源于国泰安数据库,我选择了 2023 年的数据为样本。第三份数据爬取自天眼查,只保留了关键变量。
上市公司对外投资公司列表数据(截止 2023 年 8 月):https://rstata.duanshu.com/#/brief/course/c5a0234d9e9847b1bcbec3d29d2200b6
首先我们使用前两份数据绘制。
加载所需 R 包:
library(tidyverse) library(tidygraph) library(ggraph)
|
读取和处理数据:
haven::read_dta("2023年上市公司子公司数据.dta") -> df1 haven::read_dta("2023年上市公司列表.dta") -> df2
df1 %>% rename(股票代码 = 证券代码) %>% left_join(df2) %>% mutate(投资比例 = if_else(is.na(直接持股), 间接持股, 直接持股)) %>% select(股票名称 = 股票简称, 被投资公司名称 = 子公司名称, 投资比例) -> df3
df3
|
去除部分词汇:
df3 %>% mutate(被投资公司名称 = str_remove_all(被投资公司名称, "有限公司|有限责任公司|股份有限公司")) -> df3a
|
不过大多数公司的子公司都非常少、绘制所有公司的投资网络图也非常耗时,因此我们值选择子公司数量最多的 20 家公司:
df3a %>% count(股票名称, sort = T) %>% slice(1:20) %>% select(-n) %>% inner_join(df3a) -> df3b
df3b
|
转换成 tbl_graph 数据:
as_tbl_graph(df3b) %>% mutate(size = centrality_degree(mode = 'out')) -> df4
df4
|
然后就可以绘图了:
df4 %>% ggraph(layout = "kk") + geom_edge_link(aes(edge_colour = factor(from), edge_width = 投资比例), show.legend = F, arrow = grid::arrow(type = "closed", length = unit(0.02, "inches"))) + geom_node_point(aes(color = name, size = size), show.legend = F) + geom_node_text(aes(label = name, size = size, color = name), vjust = 1, hjust = 1, check_overlap = T, family = cnfont, show.legend = F) + scale_edge_width(range = c(0.05, 0.2)) + scale_size_continuous(range = c(0.2, 1)) + scale_color_manual(values = sample(paletteer::paletteer_d("ggsci::default_igv", 51), length(df4), replace = T)) + scale_edge_color_manual(values = sample(paletteer::paletteer_d("ggsci::default_igv", 51), nrow(as.list(df4)$edges), replace = T)) + theme_graph(background = 'grey20', base_family = cnfont) -> p1
|

不过这个图难以体现“网络”关系,我们再试试被投资最多的 20 家子公司对应的上市公司投资网络:
df3a %>% count(被投资公司名称, sort = T) %>% slice(1:20) %>% select(-n) %>% left_join(df3a) %>% distinct(股票名称) %>% left_join(df3a) -> df3c
as_tbl_graph(df3c) %>% mutate(size = centrality_degree(mode = 'out')) -> df4
df4
df4 %>% ggraph(layout = "kk") + geom_edge_link(aes(edge_colour = factor(from), edge_width = 投资比例), show.legend = F, arrow = grid::arrow(type = "closed", length = unit(0.02, "inches"))) + geom_node_point(aes(color = name, size = size), show.legend = F) + geom_node_text(aes(label = name, size = size, color = name), vjust = 1, hjust = 1, check_overlap = T, family = cnfont, show.legend = F) + scale_edge_width(range = c(0.05, 0.2)) + scale_size_continuous(range = c(0.2, 1)) + scale_color_manual(values = sample(paletteer::paletteer_d("ggsci::default_igv", 51), length(df4), replace = T)) + scale_edge_color_manual(values = sample(paletteer::paletteer_d("ggsci::default_igv", 51), nrow(as.list(df4)$edges), replace = T)) + theme_graph(background = 'grey20', base_family = cnfont) -> p2
|

依然难以体现网络关系,这可能是因为国泰安的子公司数据收集自上市公司年报,里面只包含了上市公司认定的子公司,并不是所有上市公司有参股的公司,因此我们再试试从天眼查上爬取得到的数据,和国泰安提供的数据不同,该数据一个是反映了历年累计的投资记录,另一个是只要有投资占比都包含在内:
haven::read_dta("上市公司被投资公司列表.dta") -> df
df %>% count(被投资公司名称, sort = T) %>% filter(被投资公司名称 != "") %>% slice(1:20) %>% left_join(df) %>% distinct(股票名称) %>% left_join(df) -> df3a
df3a %>% mutate(被投资公司名称 = str_remove_all(被投资公司名称, "有限公司|有限责任公司|股份有限公司")) -> df3a
as_tbl_graph(df3a) %>% mutate(size = centrality_degree(mode = 'out')) -> df4
df4
df4 %>% ggraph(layout = "kk") + geom_edge_link(aes(edge_colour = factor(from), edge_width = 投资比例), show.legend = F, arrow = grid::arrow(type = "closed", length = unit(0.02, "inches"))) + geom_node_point(aes(color = name, size = size), show.legend = F) + geom_node_text(aes(label = name, size = size, color = name), vjust = 1, hjust = 1, check_overlap = T, family = cnfont, show.legend = F) + scale_edge_width(range = c(0.05, 0.2)) + scale_size_continuous(range = c(0.2, 1)) + scale_color_manual(values = sample(paletteer::paletteer_d("ggsci::default_igv", 51), length(df4), replace = T)) + scale_edge_color_manual(values = sample(paletteer::paletteer_d("ggsci::default_igv", 51), nrow(as.list(df4)$edges), replace = T)) + theme_graph(background = 'grey20', base_family = cnfont) -> p3
|

这样效果更好~
更多关于网络图绘制的内容可以学习:
R 语言文本分析:https://rstata.duanshu.com/#/brief/course/bf37cf50eef04d38b43541cc52114c96
名师讲堂|Stata 中文文本分析:https://rstata.duanshu.com/#/brief/course/b6a9efd94e5a48c2bba52dc9fdfd4291
使用 R 语言绘制中国地图 + 空间网络图:https://rstata.duanshu.com/#/brief/course/82fa1ce28006473ead5b05cfdde1d6e1
使用 Stata 绘制中国地图+空间网络图:https://rstata.duanshu.com/#/brief/course/06ef5cad0db54403ba93916e3b11c404
点击这里跳转到 RStata 短书平台获取附件:R 语言:如何绘制上市公司投资网络图
评论