【问题标题】:tidyr unnest, prefix column names with nested name during unnestingtidyr unnest,在取消嵌套时使用嵌套名称为列名添加前缀
【发布时间】:2020-09-06 05:51:23
【问题描述】:

data.frame 上运行unnest 时,是否可以将嵌套项的组名添加到它包含的各个列中(作为后缀或前缀)。还是必须通过rename手动完成重命名?

这与“取消嵌套”多个包含同名列的组特别相关。

在下面的示例中,baseaggregate 命令做得很好(例如 Petal.Length.mn),但我找不到让unnest 做同样事情的选项?

我将nestpurrr::map 一起使用,因为我希望能够灵活地混合功能,例如。计算几个变量的均值和标准差,并运行 t 检验来查看它们之间的差异。


library(dplyr, warn.conflicts = FALSE)

msd_c <- function(x) c(mn = mean(x), sd = sd(x))
msd_df <- function(x) bind_rows(c(mn = mean(x), sd = sd(x)))

aggregate(cbind(Petal.Length, Petal.Width) ~ Species, 
          data = iris, FUN = msd_c)
#>      Species Petal.Length.mn Petal.Length.sd Petal.Width.mn Petal.Width.sd
#> 1     setosa       1.4620000       0.1736640      0.2460000      0.1053856
#> 2 versicolor       4.2600000       0.4699110      1.3260000      0.1977527
#> 3  virginica       5.5520000       0.5518947      2.0260000      0.2746501

iris %>% 
  select(Petal.Length:Species) %>% 
  group_by(Species) %>% 
  tidyr::nest() %>% 
  mutate(
    Petal.Length = purrr::map(data, ~ msd_df(.$Petal.Length)),
    Petal.Width = purrr::map(data, ~ msd_df(.$Petal.Width)),
    Correlation = purrr::map(data, ~ broom::tidy(cor.test(.$Petal.Length, .$Petal.Width))),
  ) %>% 
  select(-data) %>% 
  tidyr::unnest(c(Petal.Length, Petal.Width, Correlation), names_repair = tidyr::tidyr_legacy)
#> # A tibble: 3 x 13
#> # Groups:   Species [3]
#>   Species    mn    sd   mn1   sd1 estimate statistic  p.value parameter conf.low
#>   <fct>   <dbl> <dbl> <dbl> <dbl>    <dbl>     <dbl>    <dbl>     <int>    <dbl>
#> 1 setosa   1.46 0.174 0.246 0.105    0.332      2.44 1.86e- 2        48   0.0587
#> 2 versic~  4.26 0.470 1.33  0.198    0.787      8.83 1.27e-11        48   0.651 
#> 3 virgin~  5.55 0.552 2.03  0.275    0.322      2.36 2.25e- 2        48   0.0481
#> # ... with 3 more variables: conf.high <dbl>, method <chr>, alternative <chr>

reprex package (v0.3.0) 于 2020-05-20 创建

【问题讨论】:

    标签: r tidyr unnest


    【解决方案1】:

    这个问题的答案有点明显,使用names_sep 选项而不是names_repair 选项。引用自names_sep 下的nest 帮助菜单:

    如果是字符串,内部和外部名称将一起使用。在 nest(),新的外部列的名称将通过粘贴形成 将外部和内部列名放在一起,由 names_sep 分隔。 在 unnest() 中,新的内部名称将具有外部名称 (+ names_sep) 自动剥离。这使得 names_sep 大致 嵌套和取消嵌套是对称的。

    
    library(dplyr, warn.conflicts = FALSE)
    
    msd_c <- function(x) c(mn = mean(x), sd = sd(x))
    msd_df <- function(x) bind_rows(c(mn = mean(x), sd = sd(x)))
    
    iris %>% 
      select(Petal.Length:Species) %>% 
      group_by(Species) %>% 
      tidyr::nest() %>% 
      mutate(
        Petal.Length = purrr::map(data, ~ msd_df(.$Petal.Length)),
        Petal.Width = purrr::map(data, ~ msd_df(.$Petal.Width)),
        Correlation = purrr::map(data, ~ broom::tidy(cor.test(.$Petal.Length, .$Petal.Width))),
      ) %>% 
      select(-data) %>% 
      tidyr::unnest(c(Petal.Length, Petal.Width, Correlation), names_sep = ".")
    #> # A tibble: 3 x 13
    #> # Groups:   Species [3]
    #>   Species Petal.Length.mn Petal.Length.sd Petal.Width.mn Petal.Width.sd
    #>   <fct>             <dbl>           <dbl>          <dbl>          <dbl>
    #> 1 setosa             1.46           0.174          0.246          0.105
    #> 2 versic~            4.26           0.470          1.33           0.198
    #> 3 virgin~            5.55           0.552          2.03           0.275
    #> # ... with 8 more variables: Correlation.estimate <dbl>,
    #> #   Correlation.statistic <dbl>, Correlation.p.value <dbl>,
    #> #   Correlation.parameter <int>, Correlation.conf.low <dbl>,
    #> #   Correlation.conf.high <dbl>, Correlation.method <chr>,
    #> #   Correlation.alternative <chr>
    

    reprex package (v0.3.0) 于 2020-06-10 创建

    【讨论】:

      【解决方案2】:

      要将多个函数应用于多个列,我将使用 summarise_at/mutate_at 而不是嵌套和取消嵌套数据。

      例如,在这种情况下我们可以这样做:

      library(dplyr)
      iris %>% 
        group_by(Species) %>% 
        summarise_at(vars(Petal.Length:Petal.Width), list(mn = mean, sd = sd))
      
      
      #  Species    Petal.Length_mn Petal.Width_mn Petal.Length_sd Petal.Width_sd
      #  <fct>                <dbl>          <dbl>           <dbl>          <dbl>
      #1 setosa                1.46          0.246           0.174          0.105
      #2 versicolor            4.26          1.33            0.470          0.198
      #3 virginica             5.55          2.03            0.552          0.275
      

      这会自动为我们应用函数的列名添加前缀。此外,这相当于您尝试过的aggregate 函数的dplyr 版本。

      另请注意,summarise_at 即将在即将发布的dplyr 版本中替换为across

      【讨论】:

      • 谢谢。如果您希望将相同的函数应用于所有变量(如我的aggregate 示例),则此方法有效。但是,如果您想混合(复杂)功能,它就行不通了——这就是我使用purrr::map 的原因。例如,我无法在上面的“比率”列中添加或添加来自 t.test 的输出。
      • 对不起,我不明白。据我所知,您正在为多个列传递相同的函数,即 msd_cmsd_df。就Ratio 列而言,您可以创建该列并将其传递给相同的函数。 iris %&gt;% mutate(Ratio = Petal.Length/Petal.Width) %&gt;% group_by(Species) %&gt;% summarise_at(vars(Petal.Length,Petal.Width, Ratio), list(mn = mean, sd = sd))
      • 对不起,我没有想出一个好的例子。已编辑问题以将“比率”替换为“相关性”,这使得功能完全不同的情况更加明显。
      • @JWilliman 好吧,在这种情况下您可以使用iris %&gt;% group_by(Species) %&gt;% mutate_at(vars(Petal.Length:Petal.Width), list(mn = mean, sd = sd)) %&gt;% mutate(Correlation = list(broom::tidy(cor.test(Petal.Length, Petal.Width)))) %&gt;% slice(1L) %&gt;% unnest_wider(Correlation)。我仍然不明白nestunnest 的目的。也许我错过了什么。
      【解决方案3】:

      您可以使用setNames,如下所示。有点罗嗦,但您似乎打算为每列指定每个函数,这可能很有趣。

      iris %>% 
        select(Petal.Length:Species) %>% 
        group_by(Species) %>% 
        tidyr::nest() %>% 
        mutate(
          Petal.Length = purrr::map(data, ~ msd_df(.x$Petal.Length) %>%
                                      setNames(paste0("Petal.Length.", names(.)))),
          Petal.Width = purrr::map(data, ~ msd_df(.$Petal.Width) %>%
                                      setNames(paste0("Petal.Width.", names(.)))),
          Ratio = purrr::map(data, ~ msd_df(.$Petal.Length/.$Petal.Width) %>%
                                     setNames(paste0("Ratio.", names(.))))
        ) %>% 
        select(-data) %>% 
        tidyr::unnest(c(Petal.Length, Petal.Width, Ratio))
      # A tibble: 3 x 7
      # Groups:   Species [3]
        Species    Petal.Length.mn Petal.Length.sd Petal.Width.mn Petal.Width.sd Ratio.mn Ratio.sd
        <fct>                <dbl>           <dbl>          <dbl>          <dbl>    <dbl>    <dbl>
      1 setosa                1.46           0.174          0.246          0.105     6.91    2.85 
      2 versicolor            4.26           0.470          1.33           0.198     3.24    0.312
      3 virginica             5.55           0.552          2.03           0.275     2.78    0.407
      

      或者修改你的函数,让它能够像这样修改列名。

      msd_df_name <- function(x, name){
        bind_rows(c(mn = mean(x), sd = sd(x))) %>%
          setNames(paste0(name, ".", names(.)))
      }
      
      iris %>% 
        select(Petal.Length:Species) %>% 
        group_by(Species) %>% 
        tidyr::nest() %>% 
        mutate(
          Petal.Length = purrr::map(data, ~ msd_df_name(.x$Petal.Length, "Petal.Length")),
          Petal.Width = purrr::map(data, ~ msd_df_name(.$Petal.Width, "Petal.Width")),
          Ratio = purrr::map(data, ~ msd_df_name(.$Petal.Length/.$Petal.Width, "Ratio"))
        ) %>% 
        select(-data) %>% 
        tidyr::unnest(c(Petal.Length, Petal.Width, Ratio))
      

      【讨论】:

      • 谢谢。这可能是要走的路。我只是想知道我是否在unnestnames.repair 参数中遗漏了一些东西。它说您可以传递一个函数(用于自定义名称修复)或“一个 purrr 风格的匿名函数”,但我不知道如何编写一个可以捕获组名的函数。
      • @JWilliman 我也不知道如何使用names.repair。如果有人能分享他们的见解,那就太好了。
      • 在我看来,添加组名是避免重复名称 IMO 最直观的方法,但实施起来可能并不容易。我会看看是否有人提出使用它的解决方案,否则我会勾选你的答案。
      • 我发现我忽略了 names_sep 选项,它完全符合我的要求。关于names_repair 选项,这将传递给vctrs::vec_as_names
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-06
      • 1970-01-01
      • 2021-02-16
      • 1970-01-01
      • 2019-05-01
      • 2018-06-29
      • 2019-12-04
      相关资源
      最近更新 更多