【问题标题】:Tidy way to convert numeric columns from counts to proportions将数字列从计数转换为比例的整洁方法
【发布时间】:2021-07-16 14:04:43
【问题描述】:

我只想将下面数据框中的数字行转换为按行比例。

df <- data.frame(
  "id" = c("A", "B", "C", "D"),
  "x" = c(1, 2, 3, 4),
  "y" = c(2, 4, 6, 8)
)

所以 df$x[1]

我目前的尝试,基于阅读了许多类似的帖子,如下

df %>%
  mutate_if(is.numeric, . / rowSums(across(where(is.numeric))))

这会返回以下错误:Error: across() must only be used inside dplyr verbs.

请帮忙!

【问题讨论】:

    标签: r tidyverse dplyr


    【解决方案1】:

    改写如下:

    df %>%
      mutate_if(is.numeric, ~ . / rowSums(select(df, where(is.numeric))))
    

    输出:

      id         x         y
    1  A 0.3333333 0.6666667
    2  B 0.3333333 0.6666667
    3  C 0.3333333 0.6666667
    4  D 0.3333333 0.6666667
    

    编辑:如果您想要一个不使用除 dplyr 和 base 之外的任何其他包的答案,并且可以更轻松地通过管道传输,这里是另一种(hacky)解决方案:

    df %>%
      group_by(id) %>% 
      mutate(sum = as.character(rowSums(select(cur_data(), is.numeric)))) %>%
      summarise_if(is.numeric, ~ . / as.numeric(sum))
    

    在函数中引用当前数据的常用 dplyr 方法(例如 cur_data)似乎与我最初的措辞中的 rowSums 不兼容,所以我在这里采取了稍微不同的方法。不过可能有更好的方法,所以我愿意接受建议。

    【讨论】:

    • 我认为这是最好的解决方案,因为它不需要新的对象或导入
    • 其实这个方案还是有问题的。也就是说,因为我必须引用rowSums 中的输入对象,所以我不能在管道序列中使用这个单行。有没有办法调整它以便可以在管道序列中使用?
    • @ADF 我添加了一些 dplyr-only 代码,删除了对 df 的引用。不是那个漂亮的解决方案imo(其他一些答案可能“更好”并且与管道同样兼容),但它适用于您的示例。
    【解决方案2】:

    我认为您可以使用以下解决方案:

    library(dplyr)
    library(purrr)
    
    df[1] %>%
      bind_cols(
        pmap_df(df[-1], ~ prop.table(c(...))))
    
      id         x         y
    1  A 0.3333333 0.6666667
    2  B 0.3333333 0.6666667
    3  C 0.3333333 0.6666667
    4  D 0.3333333 0.6666667
    

    还有这个,虽然有点冗长:

    library(dplyr)
    library(tidyr)
    
    df %>%
      rowwise() %>%
      mutate(output = list(prop.table(c_across(where(is.numeric))))) %>%
      unnest_wider(output) %>%
      select(-c(x, y)) %>%
      setNames(names(df))
    
    # A tibble: 4 x 3
      id        x     y
      <chr> <dbl> <dbl>
    1 A     0.333 0.667
    2 B     0.333 0.667
    3 C     0.333 0.667
    4 D     0.333 0.667
    

    【讨论】:

      【解决方案3】:

      您可以按行计算总和并将其存储在一个变量中,然后将每一列除以该列中的值。

      library(dplyr)
      
      rs <- rowSums(df %>% select(where(is.numeric)), na.rm = TRUE)
        
      df %>% mutate(across(where(is.numeric), ~./rs))
      
      #  id         x         y
      #1  A 0.3333333 0.6666667
      #2  B 0.3333333 0.6666667
      #3  C 0.3333333 0.6666667
      #4  D 0.3333333 0.6666667
      

      【讨论】:

        【解决方案4】:

        考虑janitor 包中的adorn_percentages()

        janitor::adorn_percentages(df)
        
        id         x         y
          A 0.3333333 0.6666667
          B 0.3333333 0.6666667
          C 0.3333333 0.6666667
          D 0.3333333 0.6666667
        

        【讨论】:

          【解决方案5】:

          我们可能会使用reduce

          library(dplyr)
          library(purrr)
          df %>%
              mutate(across(where(is.numeric))/select(cur_data(), 
                   where(is.numeric)) %>% 
                  reduce(`+`))
            id         x         y
          1  A 0.3333333 0.6666667
          2  B 0.3333333 0.6666667
          3  C 0.3333333 0.6666667
          4  D 0.3333333 0.6666667
          

          【讨论】:

            【解决方案6】:

            使用proportions 的基本 R 选项

            idx <- sapply(df, is.numeric)
            df[idx] <- proportions(as.matrix(df[idx]), 1)
            

            给予

            > df
              id         x         y
            1  A 0.3333333 0.6666667
            2  B 0.3333333 0.6666667
            3  C 0.3333333 0.6666667
            4  D 0.3333333 0.6666667
            

            【讨论】:

              猜你喜欢
              • 2015-08-28
              • 2013-04-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-21
              • 2012-12-04
              • 1970-01-01
              相关资源
              最近更新 更多