代码之家  ›  专栏  ›  技术社区  ›  deschen

将列乘以向量的tidyverse解决方案

  •  2
  • deschen  · 技术社区  · 2 年前

    我在这里寻找解决方案: Multiply columns in a data frame by a vector 这里: What is the right way to multiply data frame by vector? ,但它并没有真正起作用。

    我想做的是一种或多或少干净的tidyverse方法,将列乘以向量,然后将这些列作为新列添加到现有数据帧中。以第一个链接中的数据为例:

    c1 <- c(1,2,3)
    c2 <- c(4,5,6)
    c3 <- c(7,8,9)
    d1 <- data.frame(c1,c2,c3)
    
      c1 c2 c3
    1  1  4  7
    2  2  5  8
    3  3  6  9
    
    v1 <- c(1,2,3)
    

    我期望的结果是:

      c1 c2 c3 pro_c1 pro_c2 pro_c3
    1  1  4  7      1      8     21
    2  2  5  8      2     10     24
    3  3  6  9      3     12     27
    

    我试过:

    library(tidyverse)
    d1 |>
      mutate(pro = sweep(across(everything()), 2, v1, "*"))
    

    但这里的问题是,新列实际上是我的数据框中的一个数据框。我正在努力将数据框中的数据框转换为规则列。我想,我可能会先在这个内部数据框中使用集合名,然后再使用unnest,但我想知道是否有一种更直接的方法,通过使用 across 并用第一/第二/第三个元素 v1 ?

    (我知道我可能也可以先创建一个独立的数据框架,包含三个新的相乘列,给它们一个唯一的名称,然后 bind_cols d1和df上都有产品。)

    4 回复  |  直到 2 年前
        1
  •  3
  •   Martin Gal    2 年前

    这也许很荒谬,但你可以

    library(dplyr)
    
    d1 %>% 
      mutate(across(everything(), 
                    ~.x * v1[which(names(d1) == cur_column())],
                    .names = "pro_{.col}"))
    

    它回来了

      c1 c2 c3 pro_c1 pro_c2 pro_c3
    1  1  4  7      1      8     21
    2  2  5  8      2     10     24
    3  3  6  9      3     12     27
    
        2
  •  3
  •   deschen    2 年前

    只是为了好玩,我试用了&看到你的一些解决方案后,我又犯了一点错误。自从我开始忍受使用base R原生管道的痛苦,它还不允许通过一个“.”沉默地争论作为第一次争论,我不得不再胡闹一点:

    library(tidyverse)
    d1 |> 
      (\(x)(bind_cols(x, x |>
                           map2_dfc(v1, `*`) |> 
                           rename_with(.cols = everything(),
                                       .fn   = ~paste0("pro_", .)))))()
    
      c1 c2 c3 pro_c1 pro_c2 pro_c3
    1  1  4  7      1      8     21
    2  2  5  8      2     10     24
    3  3  6  9      3     12     27
    
        3
  •  2
  •   akrun    2 年前

    如果是按行,那么一个选项是 c_across

    library(dplyr)
    library(stringr)
    library(tibble)
    new <- as_tibble(setNames(as.list(v1), names(d1)))
    d1 %>% 
      rowwise %>% 
      mutate(c_across(everything()) * new) %>%
      rename_with(~ str_c("pro_", .x), everything()) %>%
      bind_cols(d1, .)
    

    -输出

       1 c2 c3 pro_c1 pro_c2 pro_c3
    1  1  4  7      1      8     21
    2  2  5  8      2     10     24
    3  3  6  9      3     12     27
    

    或者另一种选择是 map2

    library(purrr)
    map2_dfc(d1, v1,  `*`) %>%
       rename_with(~ str_c("pro_", .x), everything()) %>%
       bind_cols(d1, .)
    

    -输出

     c1 c2 c3 pro_c1 pro_c2 pro_c3
    1  1  4  7      1      8     21
    2  2  5  8      2     10     24
    3  3  6  9      3     12     27
    

    此外,通过OP’方法,它是一种 data.frame 柱有可能 unpack 预计起飞时间

    library(tidyr)
    d1 |> 
        mutate(pro = sweep(cur_data(), 2, v1, `*`)) |> 
        unpack(pro, names_sep = "_")
    

    -输出

    # A tibble: 3 × 6
         c1    c2    c3 pro_c1 pro_c2 pro_c3
      <dbl> <dbl> <dbl>  <dbl>  <dbl>  <dbl>
    1     1     4     7      1      8     21
    2     2     5     8      2     10     24
    3     3     6     9      3     12     27
    

    编辑:基于@deschen的评论 names_sep

        4
  •  2
  •   Anoushiravan R Lochu'an Chang    2 年前

    这里是你可能想使用的另一种方法。下面是一些关于其工作原理的注释:

    • accumulate2 取一个三参数函数,其中第一个参数是原始数据帧 d1 ,第二个参数是向量 v1 第三是 seq_len(length(d1)) 我过去可以迭代并选择所需的列进行乘法。
    • 当我们用 .init ,这将是输出列表中的第一个元素(自 积累2 返回一个列表)。然后在下一次迭代中,两个向量的第一个值用于我们的函数,它们由 ..2 ..3 .
    • 这个过程一直持续到使用所有向量值,最后我将所有元素绑定在一起,形成一个数据帧。
    library(dplyr)
    library(purrr)
    
    v1 %>%
      accumulate2(seq_len(length(d1)), ~ set_names(..2 * d1[..3], paste0("pro_", names(d1)[.y])), .init = d1) %>%
      exec(cbind, !!!.)
    
      c1 c2 c3 pro_c1 pro_c2 pro_c3
    1  1  4  7      1      8     21
    2  2  5  8      2     10     24
    3  3  6  9      3     12     27