【问题标题】:Select names of columns which contain specific values in row选择行中包含特定值的列的名称
【发布时间】:2013-06-23 14:36:39
【问题描述】:

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

感谢您的帮助!

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    为什么不这样做

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

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

    【讨论】:

      【解决方案2】:

      你可以使用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)
      

      编辑:(来自 Arun)

      首先,不需要lapplyapply。你可以通过apply获得同样的效果:

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

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

      【讨论】:

      • 你为什么需要lapplyapply(df>2, 1, function(x) names(which(x))) 会做到的,不是吗?
      • @Arun, lapply 保证无论如何都会得到一个列表作为输出。使用apply,输出类将取决于输入,这是一件非常糟糕的事情(例如,尝试使用dat[2:3,])。另请注意,lapply 版本总体上更短,并且不需要匿名函数,所以我也觉得它更好,但我同意这是暗示性的。
      • ...但看起来,所有答案仍然是错误的。这适用于所有输入:lapply(split(dat, rownames(dat)), function(x)names(x)[which(x > 2)])
      • @flodel,我的答案不适合 cmets 部分,所以我发布了答案:)。如果您不同意/有其他建议,请告诉我。
      【解决方案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
      

      【讨论】:

      • 首先,我不知道出了什么问题:当时,所有建议的答案要么没有系统地返回一个列表(例如df[2:3,]),要么没有返回正确的结果(例如,df[2:3,] 也是如此。)。你的unlist(apply(... 解决方案解决了这个问题,干得好。
      猜你喜欢
      • 2021-09-15
      • 1970-01-01
      • 2020-08-30
      • 2014-08-04
      • 2012-03-10
      • 2018-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多