【问题标题】:tidyverse pivot_longer several sets of columns, but avoid intermediate mutate_wider steps [duplicate]tidyverse pivot_longer 几组列,但避免中间 mutate_wider 步骤[重复]
【发布时间】:2021-03-03 18:48:30
【问题描述】:

我有以下数据

dat <- data.frame(id         = c("A", "B", "C"),
                  Q1r1_pepsi = c(1,0,1),
                  Q1r1_cola  = c(0,0,1),
                  Q1r2_pepsi = c(1,1,1),
                  Q1r2_cola  = c(0,1,1),
                  stringsAsFactors = FALSE)

其中 Q1r1 和 Q1r2 是调查中的评级问题,百事可乐和可乐是被评级的品牌。所以我对两个品牌(百事可乐、可乐)有两个评分(r1 和 r2):

id      Q1r1_c1    Q1r1_c2    Q1r2_c1    Q1r2_c2
"A"     1          0          1          0
"B"     0          0          1          1
"C"     1          1          1          1

(附带问题:如何格式化 SO 帖子,以便它正确包含在 R 控制台中调用 dat 时会得到的格式良好的输出?)

要分析数据,我需要重塑(透视)数据,以便行表示独特的评级-品牌对。因此,预期的结果是:

id      brand   Q1r1    Q1r2
"A"     "pepsi" 1       1
"A"     "cola"  0       0
"B"     "pepsi" 0       1
"B"     "cola"  0       1
"C"     "pepsi" 1       1
"C"     "cola"  1       1

目前,我总是做pivot_longerpivot_wider的组合,但我希望我可以通过pivoting_longer直接得到这个结果,而不需要做中间步骤:

library(tidyverse)

dat_long <- dat %>%
  pivot_longer(cols = starts_with("Q1")) %>%
  separate(name, into = c("item", "brand"), remove = FALSE)

dat_wide <- dat_long %>%
  pivot_wider(id_cols = c(id, brand),
              names_from = item,
              values_from = value)

对于当前的示例,执行此中间步骤仍然可以,但在其他不太干净的示例中会变得很烦人,例如假设我的列没有以 Q1r1_c1, Q1r1_c2, Q1r2_c1, Q1r2_c2 的良好结构命名,而是命名为 Q4, Q5, Q8r1, Q8r2,其中映射分别位于 Q4 和 Q8r1 以及 Q5/Q8r2 之间。

【问题讨论】:

    标签: r pivot tidyr


    【解决方案1】:

    你可以使用:

    tidyr::pivot_longer(dat, cols = -id, 
                       names_to = c('.value', 'brand'), 
                       names_sep = "_")
    
    
    #  id    brand  Q1r1  Q1r2
    #  <chr> <chr> <dbl> <dbl>
    #1 A     pepsi     1     1
    #2 A     cola      0     0
    #3 B     pepsi     0     1
    #4 B     cola      0     1
    #5 C     pepsi     1     1
    #6 C     cola      1     1
    

    【讨论】:

    • 谢谢。像魅力一样工作,比我的解决方案快得多(它将在我的真实数据集中发挥作用,其中我有数百个评级-品牌组合和数千行)。但是,由于结构化命名,代码运行良好。知道如何将其扩展到没有明确模式的情况吗?就像假设我有列Q4, Q5, Q8r1, Q8r2,其中 Q4 和 Q8r1 是百事可乐评级,Q5/Q8r2 是可乐评级,预期输出将是两列:Q4_Q5Q8? (如果这样更容易,列的名称可能会有所不同)。
    • 我知道这些定制案例有这个build_longer_spec 功能,但我还没有让它工作。
    • 我不确定您是否可以将其直接应用于此类数据集。可能,您可以使用某种模式重命名列,然后像上面一样使用pivot_longer
    • 我也在考虑这个问题。首先重命名列,应用简单的 pivot_longer,然后再次重命名(如有必要)。但是,在上面的代码中,我使用了build_longer_spec 函数。现在它可以工作了。所以我用build_longer_spec函数替换了你的pivot_longer,检查了结构并用这个新的Q4Q5和Q8模式手动替换了内容,然后用那个spec对象调用了pivot_longer。效果很好,虽然我不完全确定它的通用性以及重命名方法是否确实更容易。
    • 你能把它添加为答案吗?在列名没有明确模式的情况下,看看您如何使用build_longer_spec 会很有趣?
    【解决方案2】:

    根据@Ronak Shah 的建议,我将代码粘贴到此处,以防我的列名不那么结构化。我正在根据我在初始帖子中指定的 dat 数据构建此代码。

    names(dat) <- c("id", "Q4", "Q5", "Q8r1", "Q8r2")
    
    spec <- data.frame(.name  = names(dat)[-1],
                       .value = c("Q4Q5", "Q4Q5", "Q8", "Q8"),
                       brand  = rep(c("pepsi", "cola"), 2),
                       stringsAsFactors = FALSE)
    
    dat_long <- pivot_longer_spec(dat, spec)
    

    这给出了与我的结构化名称数据基本相同的结果,只是现在名称不同了。

    同样,我不完全确定这种方法的普遍性,但它在我的情况下有效。

    【讨论】:

      【解决方案3】:

      我们可以使用gather/spread

      library(tidyr)
      library(dplyr)
      dat %>%
          gather(brand, value, -id) %>%
          separate(brand, into = c('name', 'brand')) %>% 
          spread(name, value)
      #  id brand Q1r1 Q1r2
      #1  A  cola    0    0
      #2  A pepsi    1    1
      #3  B  cola    0    1
      #4  B pepsi    0    1
      #5  C  cola    1    1
      #6  C pepsi    1    1
      

      【讨论】:

      • 谢谢,我也想到了旧的收集/传播。但是,由于这些是已退役的功能,我不想再积极依赖它们了。它们可能仍然存在很长一段时间,但在我的所有代码中仍然更喜欢相同的功能。
      猜你喜欢
      • 2018-12-19
      • 2020-11-14
      • 1970-01-01
      • 2012-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多