我正在使用data.frame:
data.frame("A"=c(NA,5,NA,NA,NA),
"B"=c(1,2,3,4,NA),
"C"=c(NA,NA,NA,2,3),
"D"=c(NA,NA,NA,7,NA))
以这种形式提供data.frame:
A B C D
1 NA 1 NA NA
2 5 2 NA NA
3 NA 3 NA NA
4 NA 4 2 7
5 NA NA 3 NA
我的目标是检查data.frame的每一行,如果值大于特定值(让我们假设为2)并获取名称在这种情况下的列。
所需的输出(值大于2)应为:
for row 1 of the data.frame
x[1,]: c()
for row 2
x[2,]: c("A")
for row3
x[3,]: c("B")
for row4
x[4,]: c("B","D")
and for row5 of the data.frame
x[5,]: c("C")
感谢您的帮助!
答案 0 :(得分:6)
您可以使用which
:
lapply(apply(dat, 1, function(x)which(x>2)), names)
dat
是您的数据框。
[[1]]
character(0)
[[2]]
[1] "A"
[[3]]
[1] "B"
[[4]]
[1] "B" "D"
[[5]]
[1] "C"
修改强> flodel建议的更短版本:
lapply(apply(dat > 2, 1, which), names)
编辑:(来自Arun)
首先,不需要lapply
和apply
。您只需使用apply
:
apply(dat > 2, 1, function(x) names(which(x)))
但是,在apply
上使用data.frame
会将其强制转换为矩阵,如果data.frame很大,这可能不明智。
答案 1 :(得分:3)
要回答@ flodel的疑虑,我会将其作为一个单独的答案写出来:
lapply
获取一个列表,而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
的列表?
在函数参数中创建list
,然后将unlist
与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"
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
lapply(split(df, rownames(df)), function(x)names(x)[which(x > 2)])
首先,我不知道出了什么问题。如果您在谈论列表为unnamed
,则可以通过在结尾处仅设置一次来更改此名称。
其次,遗憾的是,在巨大的data.frame 上使用split
会导致分割元素太多非常慢(由于影响因素很大)水平)。
# 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
或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
答案 2 :(得分:1)
为什么不
colnames(df[,df[i,]>2])
对于每一行,其中df是您的数据帧,而i是行号;)