选择包含行中特定值的列的名称

时间:2013-06-23 14:36:39

标签: r dataframe

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

感谢您的帮助!

3 个答案:

答案 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)

首先,不需要lapplyapply。您只需使用apply

即可获得相同的内容
apply(dat > 2, 1, function(x) names(which(x)))

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

答案 1 :(得分:3)

要回答@ flodel的疑虑,我会将其作为一个单独的答案写出来:

1)使用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,然后将unlistrecursive = 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,则可以通过在结尾处仅设置一次来更改此名称。

其次,遗憾的是,在巨大的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。相反,我们可以使用setNamessetnames(来自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)

比较输出并未显示两者之间的任何其他差异(t3t2)。您可以运行此命令来验证输出是否相同(耗时):

all(sapply(names(t2), function(x) all(t2[[x]] == t3[[x]])) == TRUE) # TRUE

答案 2 :(得分:1)

为什么不

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

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