【问题标题】:How to mutate multiple columns with dynamic variable using purrr:map function?如何使用 purrr:map 函数改变具有动态变量的多个列?
【发布时间】:2019-12-02 14:43:50
【问题描述】:

我有一个如下的数据框:

df <- data.frame(
  id  = c(1:5),
  a   = c(3,10,4,0,15),
  b   = c(2,1,1,0,3),
  c   = c(12,3,0,3,1),
  d   = c(9,7,8,0,0),
  e   = c(1,2,0,2,2)
  )

我需要添加多个列,其名称由a:c3:5 的组合给出。 3:5也用在sum函数中:

df %>% mutate(
  usa_3 = sum(1+3),
  usa_4 = sum(1+4),
  usa_5 = sum(1+5),
  canada_3 = sum(1+3),
  canada_4 = sum(1+4),
  canada_5 = sum(1+5),
  nz_3 = sum(1+3),
  nz_4 = sum(1+4),
  nz_5 = sum(1+5)
  )

结果很简单,但我不想重复放类似的代码。

  id  a b  c d e usa_3 usa_4 usa_5 canada_3 canada_4 canada_5 nz_3 nz_4 nz_5
1  1  3 2 12 9 1     4     5     6        4        5        6    4    5    6
2  2 10 1  3 7 2     4     5     6        4        5        6    4    5    6
3  3  4 1  0 8 0     4     5     6        4        5        6    4    5    6
4  4  0 0  3 0 2     4     5     6        4        5        6    4    5    6
5  5 15 3  1 0 2     4     5     6        4        5        6    4    5    6

变量是字母前缀和整数范围作为后缀。 后缀也与sum 函数相关为1+postfix。 在这种情况下,它们各有 3 个值,因此结果有 9 个附加列。

我不喜欢在一堆代码之外定义函数,并假设purrr 中的mapfunctino 可能会有所帮助。

你知道如何让它工作吗? 特别是在管道中很难给出动态列名。

我发现了一些类似的问题,但不符合我的需要。

Multivariate mutate
How to use map from purrr with dplyr::mutate to create multiple new columns based on column pairs

===== 附加信息 =====
让我澄清一下这个问题的一些条件。 实际上sum(1+3)sum(1+4)... 部分被as.factor(cutree(X,k=X)) 替换,其中X 是聚类分析的结果,Y 是示例中定义为3:5 的变量。 cutree() 是一个函数,用于定义我们在哪个部分切割存储在聚类分析结果中的树状图。

对于列名usa_3, usa_4 ... nz_5,国家名替换为聚类分析方法,如ward、McQuitty、Median方法等(七种方法),参数定义为整数3、4、5按照说明,我需要在哪一部分切割树状图。

对于函数as.factor(cutree(X,k=X))中的一个X,聚类分析的结果也有多个数据框,每个方法对应。我意识到如何将函数应用于每个数据帧的另一个问题(存储在不同数据帧中的聚类分析结果)。
我目前使用的实际脚本是这样的:

cluste_number <- original_df %>% mutate(
    ## Ward
    ward_3=as.factor(cutree(clst.ward,k=3)),
    ward_4=as.factor(cutree(clst.ward,k=4)),
    ward_5=as.factor(cutree(clst.ward,k=5)),
    ward_6=as.factor(cutree(clst.ward,k=6)),
    ## Single
    sing_3=as.factor(cutree(clst.sing,k=3)),
    sing_4=as.factor(cutree(clst.sing,k=4)),
    sing_5=as.factor(cutree(clst.sing,k=5)),
    sing_6=as.factor(cutree(clst.sing,k=6)))

很抱歉没有澄清实际问题;但是,由于上述原因,usa, canada, nz 的国家数量和1:3 的参数数量不匹配。 还有一些使用i + .的建议不符合实际操作中使用as.factor(cutree(X,k=X))函数的问题。

感谢您的支持。

【问题讨论】:

    标签: r dictionary dplyr purrr


    【解决方案1】:

    不确定你在做什么,但也许这有助于澄清问题..

    library(tidyverse)
    
    df <- data.frame(
      id  = c(1:5),
      a   = c(3,10,4,0,15),
      b   = c(2,1,1,0,3),
      c   = c(12,3,0,3,1),
      d   = c(9,7,8,0,0),
      e   = c(1,2,0,2,2)
    )
    
    ctry <- rep(c("usa", "ca", "nz"), each = 3)
    nr <- rep(seq(3,5), times = 3)
    df %>%
      as_tibble() %>%
      bind_cols(map_dfc(seq_along(ctry), ~1+nr[.x] %>%
                          rep(nrow(df))) %>%
                  set_names(str_c(ctry, nr, sep = "_")))
    
    # A tibble: 5 x 15
         id     a     b     c     d     e usa_3 usa_4 usa_5  ca_3  ca_4  ca_5  nz_3  nz_4  nz_5
      <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    1     1     3     2    12     9     1     4     5     6     4     5     6     4     5     6
    2     2    10     1     3     7     2     4     5     6     4     5     6     4     5     6
    3     3     4     1     0     8     0     4     5     6     4     5     6     4     5     6
    4     4     0     0     3     0     2     4     5     6     4     5     6     4     5     6
    5     5    15     3     1     0     2     4     5     6     4     5     6     4     5     6
    

    【讨论】:

      【解决方案2】:

      我不确定我是否理解问题的实质,但这里有一种方法可以生成包含所需列名和值的数据框。

      您可以将~ function(i) i + . 更改为您想要的i(正在变异的列)的任何函数,并更改setNames(n, n) 中的任一ns 以将不同的值合并到您要使用的函数中创建(第一个 n)或更改结果列的名称(第二个 n)。

      countries <- c('usa', 'canada', 'nz')
      n <- 3:5
      
      as.data.frame(matrix(1, nrow(df), length(n))) %>% 
        rename_all(~countries) %>%
        mutate_all(map(setNames(n, n), ~ function(i) i + .)) %>% 
        select(-countries) %>% 
        bind_cols(df)
      
      #   usa_3 canada_3 nz_3 usa_4 canada_4 nz_4 usa_5 canada_5 nz_5 id  a b  c d e
      # 1     4        4    4     5        5    5     6        6    6  1  3 2 12 9 1
      # 2     4        4    4     5        5    5     6        6    6  2 10 1  3 7 2
      # 3     4        4    4     5        5    5     6        6    6  3  4 1  0 8 0
      # 4     4        4    4     5        5    5     6        6    6  4  0 0  3 0 2
      # 5     4        4    4     5        5    5     6        6    6  5 15 3  1 0 2
      

      【讨论】:

        【解决方案3】:

        有点肮脏的解决方案,但它可以满足您的需求。它结合了两个map_dfc 函数。

        library(dplyr)
        library(purrr)
        
        df <- tibble(id  = c(1:5),
                     a   = c(3,10,4,0,15),
                     b   = c(2,1,1,0,3),
                     c   = c(12,3,0,3,1),
                     d   = c(9,7,8,0,0),
                     e   = c(1,2,0,2,2))
        
        create_postfix_cols <- function(df, country, n) {
          # df = a dataframe
          # country = suffix value (e.g. "canada")
          # n = vector of postfix values (e.g. 3:5)
        
          map2_dfc(.x = rep(country, length(n)),
                   .y = n,
                   ~ tibble(col = rep(1 + .y, nrow(df))) %>%
                     set_names(paste(.x, .y, sep = "_")))
        }
        
        countries <- c("usa", "canada", "nz")
        n <- 3:5
        
        df %>%
          bind_cols(map_dfc(.x = countries, ~create_postfix_cols(df, .x, n)))
        
        
        # A tibble: 5 x 15
             id     a     b     c     d     e usa_3 usa_4 usa_5 canada_3 canada_4 canada_5
          <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>
        1     1     3     2    12     9     1     4     5     6        4        5        6
        2     2    10     1     3     7     2     4     5     6        4        5        6
        3     3     4     1     0     8     0     4     5     6        4        5        6
        4     4     0     0     3     0     2     4     5     6        4        5        6
        5     5    15     3     1     0     2     4     5     6        4        5        6
        # ... with 3 more variables: nz_3 <dbl>, nz_4 <dbl>, nz_5 <dbl>
        

        【讨论】:

          【解决方案4】:

          这是一个基本的 R 解决方案。您可以根据需要重新排列列,但这应该可以帮助您开始:

          # Create column names using an index and country names
          idx <- 3:5
          countries <- c("usa", "canada", "nz")
          new_columns <- unlist(lapply(countries, paste0, "_", idx))
          
          # Adding new values using index & taking advantage of recycling
          df[new_columns] <- sort(rep(1+idx, nrow(df)))
          df
            id  a b  c d e usa_3 usa_4 usa_5 canada_3 canada_4 canada_5 nz_3 nz_4 nz_5
          1  1  3 2 12 9 1     4     5     6        4        5        6    4    5    6
          2  2 10 1  3 7 2     4     5     6        4        5        6    4    5    6
          3  3  4 1  0 8 0     4     5     6        4        5        6    4    5    6
          4  4  0 0  3 0 2     4     5     6        4        5        6    4    5    6
          5  5 15 3  1 0 2     4     5     6        4        5        6    4    5    6
          

          或者,如果您愿意:

          # All in one long line
          df[unlist(lapply(countries, paste0, "_", idx))] <- sort(rep(1+idx, nrow(df)))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-09-04
            • 2021-08-14
            • 2018-05-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多