【问题标题】:Updating column data based on row condition根据行条件更新列数据
【发布时间】:2021-11-25 16:37:30
【问题描述】:

我正在尝试更新一个包含 153 行和 291 列的数据框,其中第一列是产品描述,所有其他列都是数字。

大多数行的总和为 1,但由于一些错误的数据输入,一些行的值翻了一番。

我想根据行和是否大于 1 来更新列。这样行和变为 1。

一个小例子数据:

test <- data.frame(product = c("First", "Second", "Third"),
                   a = c(0.2, 0.1, 0.2),
                   b = c(0.4, 0.1, 0.2),
                   c = c(0.4, 0.8, 1.6)
                   )
  product   a   b   c
1   First 0.2 0.4 0.4
2  Second 0.1 0.1 0.8
3   Third 0.2 0.2 1.6

我试过了

test %>% mutate(across(2:ncol(test), if_else(rowSums(.) > 1, . / 2, .)))

但得到以下错误:

错误:mutate() 输入 ..1 有问题。 我..1 = across(2:ncol(test), if_else(rowSums(.) &gt; 1, ./2, .))。 x 'x' 必须是数字。

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

    这是如何确保每行总和不超过1:

    library(tidyverse)
    
    test <- data.frame(
      product = c("First", "Second", "Third"),
      a = c(0.2, 0.1, 0.2),
      b = c(0.4, 0.1, 0.2),
      c = c(0.4, 0.8, 1.6)
    )
    test
    #>   product   a   b   c
    #> 1   First 0.2 0.4 0.4
    #> 2  Second 0.1 0.1 0.8
    #> 3   Third 0.2 0.2 1.6
    
    test_long <-
      test %>%
      pivot_longer(-product) %>%
      group_by(product)
    test_long
    #> # A tibble: 9 x 3
    #> # Groups:   product [3]
    #>   product name  value
    #>   <chr>   <chr> <dbl>
    #> 1 First   a       0.2
    #> 2 First   b       0.4
    #> 3 First   c       0.4
    #> 4 Second  a       0.1
    #> 5 Second  b       0.1
    #> 6 Second  c       0.8
    #> 7 Third   a       0.2
    #> 8 Third   b       0.2
    #> 9 Third   c       1.6
    
    scaled_test <-
      test_long %>%
      filter(sum(value) > 1) %>%
      mutate(value = value / sum(value))
    scaled_test
    #> # A tibble: 3 x 3
    #> # Groups:   product [1]
    #>   product name  value
    #>   <chr>   <chr> <dbl>
    #> 1 Third   a       0.1
    #> 2 Third   b       0.1
    #> 3 Third   c       0.8
    
    test_long %>%
      filter(sum(value) <= 1) %>%
      bind_rows(scaled_test) %>%
      pivot_wider(names_from = name, values_from = value)
    #> # A tibble: 3 x 4
    #> # Groups:   product [3]
    #>   product     a     b     c
    #>   <chr>   <dbl> <dbl> <dbl>
    #> 1 First     0.2   0.4   0.4
    #> 2 Second    0.1   0.1   0.8
    #> 3 Third     0.1   0.1   0.8
    

    由reprex package (v2.0.1) 于 2021-10-05 创建

    【讨论】:

    • 但是现在所有的行都一样了?我想基本上只将第三行的每一列值除以 2,以便该行的总和等于 1。与其他行类似。
    • 您是否只想对数据进行规范化,以使每行的总和大于 1 到 1,仅此而已?
    • 我原则上是的。就在这种情况下,它都是双倍的价值。但更通用的方法也可以。
    • @Godrim 我修改了我的答案
    【解决方案2】:

    在base,可以这样解决:

    idx <- rowSums(test[, -1]) > 1
    
    test[idx, -1] <- test[idx, -1] / 2
    
    test
    
    #  product   a   b   c
    #1   First 0.2 0.4 0.4
    #2  Second 0.1 0.1 0.8
    #3   Third 0.1 0.1 0.8
    

    【讨论】:

      【解决方案3】:

      这是使用across的另一种方式-

      library(dplyr)
      
      test %>%
        mutate(across(a:c, ~./ifelse(rowSums(select(cur_data(), a:c)) > 1, 2, 1)))
      
      #  product   a   b   c
      #1   First 0.2 0.4 0.4
      #2  Second 0.1 0.1 0.8
      #3   Third 0.1 0.1 0.8
      

      【讨论】:

        【解决方案4】:

        使用data.table

        library(data.table)
        setDT(test)[, (names(test)[-1]) := .SD/(1 + (rowSums(.SD) > 1))[col(.SD)], .SDcols = a:c]
        test
           product   a   b   c
        1:   First 0.2 0.4 0.2
        2:  Second 0.1 0.1 0.4
        3:   Third 0.2 0.2 0.8
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-02-20
          • 1970-01-01
          • 1970-01-01
          • 2020-06-25
          • 1970-01-01
          • 1970-01-01
          • 2021-12-03
          • 2022-01-23
          相关资源
          最近更新 更多