5

我正在使用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")

谢谢你的帮助!

4

3 回答 3

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"

编辑 弗洛德尔建议的较短版本:

lapply(apply(dat > 2, 1, which), names)

编辑:(来自阿伦)

首先,不需要lapplyand apply。您可以通过以下方式获得相同的结果apply

apply(dat > 2, 1, function(x) names(which(x)))

但是,使用applyon adata.frame会将其强制转换为矩阵,如果 data.frame 很大,这可能是不明智的。

于 2013-06-23T14:45:42.130 回答
3

为了回答@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然后使用unlistwith 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. 相反,可以只使用setNamesor 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
于 2013-06-23T18:30:17.137 回答
1

为什么不做

colnames(df[,df[i,]>2])

对于每一行,其中 df 是您的数据框, i 是行号;)

于 2019-06-14T16:26:15.360 回答