为了回答@flodel 的担忧,我将其写为单独的答案:
1) Usinglapply
得到一个列表,apply
并不总是保证这一点:
一个公平的观点。我将用一个例子来说明这个问题:
df <- structure(list(A = c(3, 5, NA, NA, NA), B = c(1, 2, 3, 1, NA),
C = c(NA, NA, NA, 2, 3), D = c(NA, NA, NA, 7, NA)), .Names = c("A",
"B", "C", "D"), row.names = c(NA, -5L), class = "data.frame")
A B C D
1 3 1 NA NA
2 5 2 NA NA
3 NA 3 NA NA
4 NA 1 2 7
5 NA NA 3 NA
# using `apply` results in a vector:
apply(df, 1, function(x) names(which(x>2)))
# [1] "A" "A" "B" "D" "C"
那么,我们如何保证一个列表apply
呢?
通过在函数参数中创建 alist
然后使用unlist
with recursive = FALSE
,如下所示:
unlist(apply(df, 1, function(x) list(names(which(x>2)))), recursive=FALSE)
[[1]]
[1] "A"
[[2]]
[1] "A"
[[3]]
[1] "B"
[[4]]
[1] "D"
[[5]]
[1] "C"
2)lapply
整体较短,并且不需要匿名函数:
是的,但速度较慢。让我用一个大例子来说明这一点。
set.seed(45)
df <- as.data.frame(matrix(sample(c(1:10, NA), 1e5 * 100, replace=TRUE),
ncol = 100))
system.time(t1 <- lapply(apply(df > 2, 1, which), names))
user system elapsed
5.025 0.342 5.651
system.time(t2 <- unlist(apply(df, 1, function(x)
list(names(which(x>2)))), recursive=FALSE))
user system elapsed
2.860 0.181 3.065
identical(t1, t2) # TRUE
3)所有答案都是错误的,并且适用于所有输入的答案:
lapply(split(df, rownames(df)), function(x)names(x)[which(x > 2)])
首先,我不明白出了什么问题。如果您正在谈论列表unnamed
,则可以通过在最后设置一次名称来更改它。
其次,不幸的是,split
在一个巨大的 data.frame上使用会导致过多的拆分元素将非常缓慢(由于巨大的因素水平)。
# testing on huge data.frame
system.time(t3 <- lapply(split(df, rownames(df)), function(x)names(x)[which(x > 2)]))
user system elapsed
517.545 0.312 517.872
第三,这将元素排序为1, 10, 100, 1000, 10000, 100000, ...
而不是1 .. 1e5
. 相反,可以只使用setNames
or setnames
(来自data.table
包)最终只执行一次,如下所示:
# setting names just once
t2 <- setNames(t2, rownames(df)) # by copy
# or even better using `data.table` `setattr` function to
# set names by reference
require(data.table)
tracemem(t2)
setattr(t2, 'names', rownames(df))
tracemem(t2)
t3
比较输出并没有显示两者(和t2
)之间的任何其他差异。您可以运行它来验证输出是否相同(耗时):
all(sapply(names(t2), function(x) all(t2[[x]] == t3[[x]])) == TRUE) # TRUE