专栏名称: 走天涯徐小洋地理数据科学
一个爱生活的地理土博,分享GIS、遥感、空间分析、R语言、景观生态等地理数据科学实操教程、经典文献、数据资源
目录
相关文章推荐
51好读  ›  专栏  ›  走天涯徐小洋地理数据科学

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

走天涯徐小洋地理数据科学  · 公众号  ·  · 2025-02-06 16:35

正文

请到「今天看啥」查看全文


欢迎大家参加明晚 8 点的直播课:「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
#>    股票名称 被投资公司名称                                     投资比例
#>                                                         
#>  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
#>    股票名称 被投资公司名称           投资比例
#>                               
#>  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
#>        
#>  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 投资比例
#>        
#> 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.050.2)) + 
  scale_size_continuous(range = c(0.21)) + 
  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
#>        
#>  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 投资比例
#>        
#> 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.050.2)) + 
  scale_size_continuous(range = c(0.21)) + 
  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.050.2)) + 
  scale_size_continuous(range = c(0.21)) + 
  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://geodatasci.duanshu.com/#/course/970c074747e54302a0ab0214e751eed6

使用 R 语言绘制中国地图 + 空间网络图:https://geodatasci.duanshu.com/#/course/45f58efbaf404043a9e756edb057c6b1

文献计量从0到SCI(五)R语言分析SCI论文研究国家和机构:

https://geodatasci.duanshu.com/#/course/575e40f19889490aacbbe1bac8d40386


直播信息


欢迎大家参加明晚 8 点的直播课:「R 语言:如何绘制上市公司投资网络图」

  1. 直播地址:腾讯会议(需要报名 地理数据科学 培训班参加)
  2. 讲义材料:需要购买 地理数据科学培训班 会员,详情可阅读: 人工智能时代你的GIS和遥感数据分析技能不要落伍哦

更多关于 地理数据科学培训班 会员的更多信息可添加微信咨询:

附件下载(点击文末的阅读原文即可跳转):

https://geodatasci.duanshu.com/#/course/ccef302a76174eadaa0392a7a2467451







请到「今天看啥」查看全文