【问题标题】:Preserve order of columns when going from wide to long format从宽格式到长格式时保留列的顺序
【发布时间】:2018-04-09 10:07:52
【问题描述】:

当我将列从宽格式收集到长格式时,我试图保持它们的顺序。我遇到的问题是在我gathersummarize 之后订单丢失了。列的数量很大,所以我不想手动输入顺序。

这是一个例子:

library(tidyr)
library(dplyr)

N <- 4
df <- data.frame(sample = c(1,1,2,2),
                 y1.1 = rnorm(N), y2.1 = rnorm(N), y10.1 = rnorm(N))
> df
  sample      y1.1      y2.1      y10.1
1      1  1.040938 0.8851727 -0.3617224
2      1  1.175879 1.0009824 -1.1352406
3      2 -1.501832 0.3446469 -1.8687008
4      2 -1.326817 0.4434628 -0.8795962

我想要的是保留列的顺序。在我进行一些操作后,订单丢失了。在这里看到:

dfg <- df %>% 
  gather(key="key", value="value", -sample) %>%
  group_by(sample, key) %>%
  summarize(mean = mean(value))

> filter(dfg, sample == 1)
  sample   key       mean
   <dbl> <chr>      <dbl>
1      1  y1.1  0.2936335
2      1 y10.1  0.6170505
3      1  y2.1 -0.2250543

您可以看到它是如何将 y10.1 置于 y2.1 之前的,这是我不想要的。我想要的是保持这个顺序,在这里看到:

dfg <- df %>% 
  gather(key="key", value="value", -sample)

> filter(dfg, sample == 1)
  sample   key       value
1      1  y1.1  0.60171521
2      1  y1.1 -0.01444823
3      1  y2.1  0.81566726
4      1  y2.1 -1.26577581
5      1 y10.1  0.41686388
6      1 y10.1  0.81723707

由于某种原因,group_bysummarize 操作改变了顺序。我不确定为什么。我尝试了ungroup 命令,但没有任何作用。正如我之前所说,我的实际数据框有很多列,我需要保留顺序。保留顺序的原因是我可以按正确的顺序绘制数据。

有什么想法吗?

【问题讨论】:

    标签: r tidyr


    【解决方案1】:

    或者您可以将键列转换为具有反映原始列名顺序的级别的因子:

    df %>% 
        gather(key="key", value="value", -sample) %>%
        mutate(key=factor(key, levels=names(df)[-1])) %>% # add this line to convert the key to a factor
        group_by(sample, key) %>%
        summarize(mean = mean(value)) %>%
        filter(sample == 1)
    
    # A tibble: 3 x 3
    # Groups:   sample [1]
    #  sample    key       mean
    #   <dbl> <fctr>      <dbl>
    #1      1   y1.1  0.8310786
    #2      1   y2.1 -1.2596933
    #3      1  y10.1  0.8208812
    

    【讨论】:

    • 我选择了这个作为答案,因为它是最通用的解决方案。然而,@Moody_Mudskipper 的解决方案提供了一个独特的选项,它可以按数字排序,这在相同的情况下可能是需要的(列的顺序不是所需的)。
    【解决方案2】:

    tidyverse 包现在可以提供优雅的解决方案:

        library(tidyverse)
        N <- 4
        df <- data.frame(sample = c(1,1,2,2),
                        y1.1 = rnorm(N), y2.1 = rnorm(N), y10.1 = rnorm(N))
        df %>% 
            gather("key", "value", -sample, factor_key = T) %>% 
            group_by(sample, key) %>%
            summarise(mean = mean(value))
    

    导致

        # A tibble: 6 x 3
        # Groups:   sample [2]
        sample key      mean
        <dbl> <fct>   <dbl>
        1      1 y1.1   0.0894
        2      1 y2.1   0.551 
        3      1 y10.1  0.254 
        4      2 y1.1  -0.555 
        5      2 y2.1  -1.36  
        6      2 y10.1 -0.794 
    

    【讨论】:

    • 这是一个非常好的解决方案。 gather 现在被pivot_longer 贬值了,但乍一看帮助文件并没有显示pivot_longer 具有此功能。
    • tidyr 的下一个版本(1.0.2 之后)为pivot_longer() 引入了names_transform 参数。此参数将函数应用于pivot_longer() 创建的名称。 factor_key = TRUE 参数等价于 names_transform = list(key = forcats::fct_inorder)
    【解决方案3】:

    我通过使用查找表找到了一个可行的解决方案。这似乎对我有用,因为我可以提取列名并为列名分配一个有序编号,然后与我的 data.frame 配对。

    解决办法如下:

    lookup <- tibble(key = c("y1.1", "y2.1", "y10.1"),
                     index = c(1,2,3))
    
    > left_join(dfg, lookup, by="key")
    # A tibble: 6 x 4
      sample   key       mean index
       <dbl> <chr>      <dbl> <dbl>
    1      1  y1.1  0.2936335     1
    2      1 y10.1  0.6170505     3
    3      1  y2.1 -0.2250543     2
    4      2  y1.1  1.3652070     1
    5      2 y10.1  0.9889233     3
    6      2  y2.1  0.5216553     2
    

    【讨论】:

      【解决方案4】:

      如果您的列确实按其包含的数字排序,则应该可以:

      library(readr)
      
      df %>% 
        gather(key="key", value="value", -sample) %>%
        group_by(sample, key)         %>%
        summarize(mean = mean(value)) %>%
        arrange(parse_number(key))    %>%  # <- sorting by number contained in key
        filter(sample == 1)
      
      # # A tibble: 3 x 3
      # # Groups:   sample [1]
      #     sample   key       mean
      # <dbl> <chr>      <dbl>
      #   1      1  y1.1 -0.9236688
      #   2      1  y2.1 -0.2168337
      #   3      1 y10.1  0.5041981
      

      【讨论】:

        【解决方案5】:

        另一种方法是使用您要排序的键列的自定义版本来arrange 数据框:

        library(dplyr)
        library(tidyr)
        
        df %>% 
          gather(key="key", value="value", -sample) %>%
          group_by(sample, key) %>%
          summarize(mean = mean(value)) %>%
          arrange(as.numeric(stringr::str_replace(key, "y", "")), .by_group = TRUE)
        
        #> # A tibble: 6 x 3
        #> # Groups:   sample [2]
        #>   sample   key        mean
        #>    <dbl> <chr>       <dbl>
        #> 1      1  y1.1  0.07001689
        #> 2      1  y2.1  1.15349430
        #> 3      1 y10.1  1.18266024
        #> 4      2  y1.1  0.42616604
        #> 5      2  y2.1  1.05891682
        #> 6      2 y10.1 -0.12561209
        

        【讨论】:

        • 这实质上是不是将键转换为数字,然后根据它进行排序?
        • 嗨@LloydChristmas,是的,你是对的。它与 Moody_Mudskipper 最近的答案相同,具有更易读的parse_number 函数。如果您希望最终结果首先按分组变量排序,然后是键,则需要将 .by_group = TRUE 添加到 arrange
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-19
        • 2021-12-24
        • 2015-08-05
        • 2018-06-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多