我有一个数据框列表,我想为其获取(在一个单独的数据框中)指定列的行均值,该列可能不存在于列表的所有数据框中。当指定的列在列表的至少一个数据框中不存在时,就会出现我的问题。
假定以下示例数据帧列表:
df1 <- read.table(text = 'X A B C
name1 1 2 3
name2 5 10 4',
header = TRUE)
df2 <- read.table(text = 'X B C A
name1 8 1 31
name2 9 9 8',
header = TRUE)
df3 <- read.table(text = 'X B A E
name1 9 9 29
name2 5 15 55',
header = TRUE)
mylist_old <-list(df1, df2)
mylist_new <-list(df1, df2, df3)
假设我要在rowMeans
列C
中,当数据帧列表(mylist_old
)由元素df1
和{{1组成时, }},:
df2
当列表由至少一个不存在列Mean_C <- rowMeans(do.call(cbind, lapply(mylist_old, "[", "C")))
Mean_C <- as.data.frame(Mean_C)
的数据帧组成时,麻烦就来了,在我的示例中,C
就是列表{{1} }:
df3
导致:“ mylist_new
(X [[i]],...)出错:选择了未定义的列
规避此问题的一种方法是将Mean_C <- rowMeans(do.call(cbind, lapply(mylist_new, "[", "C")))
从[.data.frame
中排除。但是,我的真实程序有一个64个数据帧的列表,我不知道它们是否存在列df3
。仅当检测到列mylist_new
存在时,我才想C
我的代码,即将命令应用于数据框列表,但仅适用于存在列{{1}的数据框}是真的。
我尝试过
lapply
但是什么也没有发生,可能是因为C
是指列表而不是列表的每个数据帧。对于64个数据框,我无法“手动”引用每个数据框,因此需要一个自动化的过程。
答案 0 :(得分:6)
这里是Filter
个list
元素的一个选项,然后将lapply
应用于过滤后的list
rowMeans(do.call(cbind, lapply(Filter(function(x) "C" %in% names(x),
mylist_new), `[[`, "C")))
#[1] 2.0 6.5
或使用tidyverse
而不使用Filter
,但使用select
忽略不存在该列的情况
library(tidyverse)
map(mylist_new, ~ .x %>%
select(one_of("C"))) %>% # gives a warning
bind_cols %>%
rowMeans
#[1] 2.0 6.5
最好警告一下该列不存在
或者没有警告
map(mylist_new, ~ .x %>%
select(matches("^C$"))) %>%
bind_cols %>%
rowMeans
#[1] 2.0 6.5
答案 1 :(得分:3)
我们可以在使用子集之前使用 if 检查名称
rowMeans(do.call(cbind,
lapply(mylist_new, function(x) if('C' %in% names(x)) x['C'] else NA)),na.rm = TRUE)
或在 purrr 0.3.2
中使用map_iflibrary(purrr)
rowMeans(do.call(cbind,map_if(mylist_new,
function(x) 'C' %in% names(x),
'C', .else=~return(NA))),na.rm = TRUE)
[1] 2.0 6.5
答案 2 :(得分:0)
一种方法是使用purrr::safely
,它将为每次迭代返回一个包含result
和error
元素的列表,然后我们可以转置,提取result
并删除NULL
与compact
的结果:
library(tidyverse)
rowMeans(do.call(cbind, transpose(
lapply(mylist_new, safely(`[`), "C"))$result %>% compact()))
# [1] 2.0 6.5
我们还可以使用otherwise
参数获得NA
而不是NULL
的结果,并且可以在{{1中将na.rm
设置为TRUE
}}。
rowMeans
这是为了以最小的修改解决您的问题。如果我必须解决这个确切的问题,我可以通过以下方式做到:
rowMeans(na.rm = TRUE, do.call(cbind, transpose(
lapply(mylist_new, safely(`[`, otherwise= NA), "C"))$result))
# [1] 2.0 6.5
我们提取map(mylist_new, "C") %>% compact() %>% pmap_dbl(~mean(c(...)))
# [1] 2.0 6.5
元素,将其C
删除,然后按元素计算均值。
这可能更有效(不确定):
NULL
再一次,这次使用重塑:
map(set_names(mylist_new), "C") %>% compact() %>% as_tibble() %>% rowMeans()
# [1] 2.0 6.5
还有一个基本版本,可读性强,步骤有些笨拙,其中几列具有相同的名称,但是它位于临时对象上,所以还不错:
map_dfr(mylist_new, ~gather(.,,,-1)) %>%
group_by(X) %>%
filter(key == "C") %>%
summarize_at("value", mean)
# # A tibble: 2 x 2
# X value
# <fct> <dbl>
# 1 name1 2
# 2 name2 6.5