如何用tidyverse实现截面相关性的时间序列均值计算
用tidyverse实现年度截面相关性的时间序列均值
我明白你想用tidyverse风格替代base R里Reduce()的写法来计算年度截面相关系数的时间序列均值,之前的尝试返回NULL是因为map()的用法不对——直接对分组后的tibble用map()会遍历整个数据框的列,而不是我们需要的cormat列里的相关系数值。
下面给你两种符合tidyverse惯用风格的解决方案:
方法一:嵌套数据框 + 矩阵转长格式计算均值
这种方法更贴合tidy数据的理念,步骤清晰:
library(tidyverse) set.seed(2001) dat <- data.frame(year = rep(2001:2003, each = 10), x = runif(3*10)) dat <- transform(dat, y = 5*x + runif(3*10)) # 核心代码 dat_mean_cor <- dat %>% # 按年份分组并嵌套数据 group_nest(year) %>% # 计算每组的相关矩阵,并转成长格式tibble mutate(cor_tbl = map(data, ~ cor(.x) %>% as_tibble(rownames = "var1") %>% pivot_longer(-var1, names_to = "var2", values_to = "cor"))) %>% # 展开长格式相关系数数据 unnest(cor_tbl) %>% # 按变量对分组,计算均值 group_by(var1, var2) %>% summarize(mean_cor = mean(cor), .groups = "drop") %>% # 转回宽格式的相关矩阵样式 pivot_wider(names_from = var2, values_from = mean_cor) %>% column_to_rownames("var1") dat_mean_cor
运行后会得到和你base R方法完全一致的结果:
x y x 1.0000000 0.9772068 y 0.9772068 1.0000000
方法二:直接对相关矩阵列表用reduce()求和再取均值
如果你更想直接对应原来base R里Reduce()的逻辑,tidyverse里的purrr::reduce()可以直接替代,步骤更简洁:
dat_mean_cor2 <- dat %>% group_split(year) %>% # 对每组计算相关矩阵,得到矩阵列表 map(~ cor(select(.x, x, y))) %>% # 用reduce对矩阵列表逐元素求和,再除以年份数 reduce(`+`) %>% `/`(length(unique(dat$year))) dat_mean_cor2
这个方法和你原来的base R逻辑几乎一致,只是用purrr::map()替代lapply(),purrr::reduce()替代base::Reduce(),完全符合tidyverse的函数式编程风格。
为什么你之前的代码会返回NULL?
你之前的代码dat %>% group_by(year) %>% do(cormat = cor(.$x, .$y)) %>% map(.$cormat, mean)有两个问题:
do(cormat = cor(.$x, .$y))得到的是一个包含year列和cormat列的tibble,其中cormat是标量(因为你用的是cor(.$x, .$y)而不是cor(select(.,x,y)),前者返回单个相关系数,后者返回矩阵)map(.$cormat, mean)的写法错误,map()的第一个参数应该是要遍历的向量/列表,正确的写法应该是pull(cormat) %>% mean(),但这样只能得到x和y的相关系数均值,得不到完整的相关矩阵均值。
内容的提问来源于stack exchange,提问作者Richard Herron
相关产品推荐
相关产品推荐

