【问题标题】:Select or subset variables whose column sums are not zero选择或子集列总和不为零的变量
【发布时间】:2018-10-31 07:13:22
【问题描述】:

我想在列总和不为零的数据框中选择或子集变量,但也保留其他因子变量。它应该相当简单,但我不知道如何使用dplyr 对变量子集运行select_if() 函数:

df <- data.frame(
  A = c("a", "a", "b", "c", "c", "d"),
  B = c(0, 0, 0, 0, 0, 0),
  C = c(3, 0, 0, 1, 1, 2),
  D = c(0, 3, 2, 1, 4, 5)
)

require(dplyr)
df %>% 
  select_if(funs(sum(.) > 0))

#Error in Summary.factor(c(1L, 1L, 2L, 3L, 3L, 4L), na.rm = FALSE) : 
#  ‘sum’ not meaningful for factors

然后我尝试只选择B, C, D,这可行,但我不会有变量A

df %>% 
  select(-A) %>% 
  select_if(funs(sum(.) > 0)) -> df2
df2
#  C D
#1 3 0
#2 0 3
#3 0 2
#4 1 1
#5 1 4
#6 2 5

我可以简单地做cbind(A = df$A, df2),但由于我有一个包含 3000 行和 200 列的数据集,我担心这可能会引入错误(例如,如果值排序不同)。

尝试在sum() 函数中对变量B, C, D 进行子集化也不起作用:

df %>% 
  select_if(funs(sum(names(.[2:4])) > 0))
#data frame with 0 columns and 6 rows

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    试试这个:

    df %>% select_if(~ !is.numeric(.) || sum(.) != 0)
    #   A C D
    # 1 a 3 0
    # 2 a 0 3
    # 3 b 0 2
    # 4 c 1 1
    # 5 c 1 4
    # 6 d 2 5
    

    基本原理是,对于||,如果左侧是TRUE,则不会评估右侧。

    注意:

    • select_if 的第二个参数应该是函数名称或公式(lambda 函数)。 ~ 是告诉select_if !is.numeric(.) || sum(.) != 0 应该转换为函数的必要条件。
    • 正如@zx8754 在下面评论的那样,如果只想保留factor 列,则应使用is.factor(.)

    编辑:基础 R 解决方案

    cols <- c('B', 'C', 'D')
    cols.to.keep <- cols[colSums(df[cols]) != 0]
    df[!names(df) %in% cols || names(df) %in% cols.to.keep]
    

    【讨论】:

    • 我认为他们想检查是否因素,也许:df %&gt;% select_if(~ is.factor(.) || sum(.) != 0) ?
    • 你能解释一下,为什么~
    • @zx8754,好点。我将对其进行编辑以反映这一点并解释~
    • @Stefan,抱歉,我可以想到一个很好的方法来使用dplyr。但是,您始终可以使用基本 R 来实现。请记住,dplyr 是一个选项,但不是必需的。
    • @Stefan,我在上一条评论中错过了“可以”之后的“不”。并添加了基础 R 溶液。
    【解决方案2】:

    对于想要使用没有作用域变体的新 dplyr 1.0.0(如 @mt1022 很好地展示但已弃用的 select_if)的每个人来说,这里是一个更新:

    df %>% 
      select(where(is.numeric)) %>% 
      select(where(~sum(.) != 0))
    

    如果您想将两个 select 语句压缩为一个,则不能通过元素方式 &amp; 而是更长的形式 &amp;&amp; 来执行此操作,因为这会产生所需的布尔输出:

    df %>% select(where(~ is.numeric(.x) && sum(.x) !=0 ))
    

    【讨论】:

      【解决方案3】:

      这是一个使用 data.table 的解决方案

      df<-data.table(
        A = c("a", "a", "b", "c", "c", "d"),
        B = c(0, 0, 0, 0, 0, 0),
        C = c(3, 0, 0, 1, 1, 2),
        D = c(0, 3, 2, 1, 4, 5)
      )
      
      df2<-df[,lapply(X = .SD,FUN = function(x){sum(as.numeric(x))}),.SDcols = colnames(df)]
      df[,which(is.na(df[1,]) == F),with = F]
      

      【讨论】:

      • 最好将数据表表示为DTdt,因为它们不是dataframes。
      • +1 不想听起来居高临下。请记住我刚开始时的困惑。一些结果被称为小标题,一些以1, 2, 3, ... 作为行号,另一些以1:, 2:, 3:, ...。把我搞糊涂了。
      • @Roman 我同意你不应该将你的数据命名为df,因为这是一个函数的名称。 (曾经有人质疑这会导致一些问题,但我找不到。)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多