R 语言:如何绘制上市公司投资网络图

最近给大家分享了上市公司子公司数据,里面绘制了一副上市公司的投资网络图:

1999~2023 年上市公司子公司经纬度及其所处的省市区县数据:https://rstata.duanshu.com/#/brief/course/24376370188e4622b124f4a3199e395b

这幅图使用 R 语言绘制。今天我们一起来学习下如何在 R 语言中绘制这样的网络图。

在附件中我准备了三份数据:

  1. 2023年上市公司列表.dta
  2. 2023年上市公司子公司数据.dta
  3. 上市公司被投资公司列表.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

#> # A tibble: 114,991 × 3
#> 股票名称 被投资公司名称 投资比例
#> <chr> <chr> <dbl>
#> 1 广济药业 武穴长投广济生物医药产业投资基金合伙企业(有限合伙) 50
#> 2 广济药业 广济药业(孟州)有限公司 97.4
#> 3 广济药业 广济药业(济宁)有限公司 90
#> 4 格林美 格林美(郴州)固体废物处理有限公司 100
#> 5 中国人寿 中国人寿-沪发1号股权投资计划 99.2
#> 6 永安期货 浙江中邦实业发展有限公司 100
#> 7 世纪华通 江苏酷动乐创网络科技有限公司 51
#> 8 博士眼镜 江西博士新云程商贸有限公司 60
#> 9 远达环保 重庆远达碳基环保科技有限公司 51
#> 10 谱尼测试 谱尼测试科技(天津)有限公司 100
#> # ℹ 114,981 more rows

去除部分词汇:

df3 %>%
mutate(被投资公司名称 = str_remove_all(被投资公司名称, "有限公司|有限责任公司|股份有限公司")) -> df3a

不过大多数公司的子公司都非常少、绘制所有公司的投资网络图也非常耗时,因此我们值选择子公司数量最多的 20 家公司:

df3a %>%
count(股票名称, sort = T) %>%
slice(1:20) %>%
select(-n) %>%
inner_join(df3a) -> df3b

df3b

#> # A tibble: 9,103 × 3
#> 股票名称 被投资公司名称 投资比例
#> <chr> <chr> <dbl>
#> 1 新城控股 上海麦鹏置业 96.9
#> 2 新城控股 天津新城恒瑞房地产开发 99.0
#> 3 新城控股 上海骋崧企业管理 99.2
#> 4 新城控股 上海铭世置业 99.0
#> 5 新城控股 北京新城万隆房地产开发 1
#> 6 新城控股 兴化新城恒昇商业运营管理 99.0
#> 7 新城控股 香港创贤发展 99.0
#> 8 新城控股 南昌恒韵房地产开发 99.1
#> 9 新城控股 合肥新城亿盛房地产 95.8
#> 10 新城控股 武汉新城创置置业 95.2
#> # ℹ 9,093 more rows

转换成 tbl_graph 数据:

as_tbl_graph(df3b) %>%
mutate(size = centrality_degree(mode = 'out')) -> df4

df4

#> # A tbl_graph: 9118 nodes and 9103 edges
#> #
#> # A directed multigraph with 19 components
#> #
#> # Node Data: 9,118 × 2 (active)
#> name size
#> <chr> <dbl>
#> 1 新城控股 1225
#> 2 荣盛发展 806
#> 3 怡亚通 552
#> 4 豫园股份 523
#> 5 华能国际 504
#> 6 爱尔眼科 476
#> 7 海大集团 455
#> 8 中航产融 452
#> 9 欧普康视 438
#> 10 国电电力 428
#> # ℹ 9,108 more rows
#> #
#> # Edge Data: 9,103 × 3
#> from to 投资比例
#> <int> <int> <dbl>
#> 1 1 21 96.9
#> 2 1 22 99.0
#> 3 1 23 99.2
#> # ℹ 9,100 more rows

然后就可以绘图了:

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

#> # A tbl_graph: 1116 nodes and 1255 edges
#> #
#> # A directed acyclic multigraph with 9 components
#> #
#> # Node Data: 1,116 × 2 (active)
#> name size
#> <chr> <dbl>
#> 1 长飞光纤 73
#> 2 博创科技 13
#> 3 长虹美菱 24
#> 4 四川长虹 147
#> 5 中科美菱 2
#> 6 贝特瑞 35
#> 7 中国宝安 222
#> 8 中集集团 57
#> 9 中集车辆 101
#> 10 华润三九 121
#> # ℹ 1,106 more rows
#> #
#> # Edge Data: 1,255 × 3
#> from to 投资比例
#> <int> <int> <dbl>
#> 1 1 20 100
#> 2 1 21 100
#> 3 1 22 51
#> # ℹ 1,252 more rows

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

# 20 家公司
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 语言:如何绘制上市公司投资网络图

评论