【问题标题】:Reshaping data to wide format in R [duplicate]在R中将数据重塑为宽格式[重复]
【发布时间】:2021-08-27 23:09:21
【问题描述】:

我想将数据从长格式转换为宽格式。我有一个id 变量,每个id 有10 个不同级别的x。此外,还有更多信息y 和日期变量startstop,它们指示x 何时开始和停止。这是代码和数据预览:

library(tidyverse)

set.seed(123)
id <- rep(1:10, each = 10)
x <- rep(LETTERS[1:10], 10)
y <- sample(letters[11:26], 100, replace = T)
start <- sample(seq(as.Date('2020-01-01'), as.Date('2021-01-01'), by="day"), 100, replace = T)
stop <- sample(seq(as.Date('2021-01-01'), as.Date('2022-01-01'), by="day"), 100, replace = T)
NA_index <- sample(seq(1:100), 60)

# replace dates with NA
start[NA_index] <- NA
stop[NA_index] <- NA

df <- data.frame(id, x, y, start, stop)
head(df, 13)
   id x y      start       stop

1   1 A y 2020-11-03 2021-10-19
2   1 B y       <NA>       <NA>
3   1 C m 2020-03-29 2021-02-02
4   1 D x 2020-01-25 2021-02-09
5   1 E m 2020-10-17 2021-01-10
6   1 F t 2020-10-12 2021-07-19
7   1 G l 2020-06-07 2021-05-05
8   1 H p       <NA>       <NA>
9   1 I u       <NA>       <NA>
10  1 J o       <NA>       <NA>
11  2 A n       <NA>       <NA>
12  2 B x       <NA>       <NA>
13  2 C p       <NA>       <NA>

我想将数据减少到具有 10 个唯一 id 的 10 行,其中每个 id 将其所有信息存储在一行中。输出的形状应类似于以下预览:

   id x     y     start      stop        x_id x2    y2    start2     stop2      x3    y3 

1     1 A     y     2020-11-03 2021-10-19     1 B     y     NA         NA         C     m    
2     2 A     n     NA         NA             1 B     x     NA         NA         C     p    
3     3 A     s     NA         NA             1 B     v     NA         NA         C     s    
4     4 A     s     NA         NA             1 B     x     NA         NA         C     m    
5     5 A     y     NA         NA             1 B     z     NA         NA         C     t    
6     6 A     v     2020-10-14 2021-09-02     1 B     o     2020-10-03 2021-06-03 C     q    

我已经有了一个可行的解决方案,但这种方式似乎效率不高。在这里,我沿id 创建了一个序列,改变了新列并将所有信息存储在每个id 组的第一行中。之后,我过滤了每个 id 组的第一行并获得所需的输出。代码如下:

df_wide <- df %>% 
  group_by(id)%>%
  mutate(x_id = seq_along(id)) %>%
  mutate(
    x2 = lead(x), 
    y2 = lead(y), 
    start2 = lead(start), stop2 = lead(stop),
    x3 = lead(x, n = 2), 
    y3 = lead(y, n = 2), 
    start3 = lead(start, n = 2), stop3 = lead(stop, n = 2),
    x4 = lead(x, n = 3), 
    y4 = lead(y, n = 3), 
    start4 = lead(start, n = 3), stop4 = lead(stop, n = 3),
    x5 = lead(x, n = 4), 
    y5 = lead(y, n = 4),
    start5 = lead(start, n = 4), stop5 = lead(stop, n = 4),
    x6 = lead(x, n = 5), 
    y6 = lead(y, n = 5), 
    start6 = lead(start, n = 5), stop6 = lead(stop, n = 5),
    x7 = lead(x, n = 6),
    y7 = lead(y, n = 6),
    start7 = lead(start, n = 6), stop7 = lead(stop, n = 6),
    x8 = lead(x, n = 7),
    y8 = lead(y, n = 7),
    start8 = lead(start, n = 7), stop8 = lead(stop, n = 7),#
    x9 = lead(x, n = 8),
    y9 = lead(y, n = 8),
    start9 = lead(start, n = 8), stop9 = lead(stop, n = 8),
    x10 = lead(x, n = 9),
    y10 = lead(y, n = 9),
    start10 = lead(start, n = 9), stop10 = lead(stop, n = 9)
  ) %>%
  filter(x_id == 1)

如上所述,我认为这不是处理此问题的好方法,但至少对我有用。我也用tidyr::pivot_wider 做了很多尝试,但没有得到想要的输出数据。
有人知道如何重塑数据,但不像我的方法那么冗长?

我将不胜感激。

最好的 弗洛里安

【问题讨论】:

    标签: r dataframe tidyverse reshape


    【解决方案1】:

    为每个id 创建一个行号列,并将数据重新整形为宽格式。

    library(dplyr)
    library(tidyr)
    
    df %>%
      group_by(id) %>%
      mutate(col = row_number()) %>%
      ungroup %>%
      pivot_wider(names_from = col, values_from = x:stop)
    
    # A tibble: 10 x 41
    #      id x_1   x_2   x_3   x_4   x_5   x_6   x_7   x_8   x_9   x_10 
    #   <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
    # 1     1 A     B     C     D     E     F     G     H     I     J    
    # 2     2 A     B     C     D     E     F     G     H     I     J    
    # 3     3 A     B     C     D     E     F     G     H     I     J    
    # 4     4 A     B     C     D     E     F     G     H     I     J    
    # 5     5 A     B     C     D     E     F     G     H     I     J    
    # 6     6 A     B     C     D     E     F     G     H     I     J    
    # 7     7 A     B     C     D     E     F     G     H     I     J    
    # 8     8 A     B     C     D     E     F     G     H     I     J    
    # 9     9 A     B     C     D     E     F     G     H     I     J    
    #10    10 A     B     C     D     E     F     G     H     I     J    
    # … with 30 more variables: y_1 <chr>, y_2 <chr>, y_3 <chr>,
    #   y_4 <chr>, y_5 <chr>, y_6 <chr>, y_7 <chr>, y_8 <chr>, y_9 <chr>,
    #   y_10 <chr>, start_1 <date>, start_2 <date>, start_3 <date>,
    #   start_4 <date>, start_5 <date>, start_6 <date>, start_7 <date>,
    #   start_8 <date>, start_9 <date>, start_10 <date>, stop_1 <date>,
    #   stop_2 <date>, stop_3 <date>, stop_4 <date>, stop_5 <date>,
    #   stop_6 <date>, stop_7 <date>, stop_8 <date>, stop_9 <date>,
    #   stop_10 <date>
    

    【讨论】:

    • 感谢 Ronak,这看起来很简单。要为每个 id 定义行号,我在代码中使用了 mutate = seq_along(id)。它提供与mutate = row_number() 相同的输出。你有什么想法,我如何定义输出列的顺序?理想情况下,它的顺序应该是id - x_1 - y_1 - start_1 - stop1 - x_2 等等。当然,有机会在进一步的管道之后使用select(id, x_1, y_1 ...),但是有没有更简单的方法而不需要输入每个变量?
    • @fbeese,实际上这样排列的列仍然是open issue on github
    • @fbeese 是的,没有简单的方法可以做到这一点。话虽如此,这是一个丑陋的解决方法%&gt;% select(id, order(readr::parse_number(names(.))[-1]) + 1)
    【解决方案2】:

    为列命名花费了大部分时间:

    library(tidyverse)
    
    set.seed(123)
    id <- rep(1:10, each = 10)
    x <- rep(LETTERS[1:10], 10)
    y <- sample(letters[11:26], 100, replace = T)
    start <- sample(seq(as.Date('2020-01-01'), as.Date('2021-01-01'), by="day"), 100, replace = T)
    stop <- sample(seq(as.Date('2021-01-01'), as.Date('2022-01-01'), by="day"), 100, replace = T)
    NA_index <- sample(seq(1:100), 60)
    
    # replace dates with NA
    start[NA_index] <- NA
    stop[NA_index] <- NA
    
    df <- data.frame(id, x, y, start, stop)
    
    #setting the names of the columns        
    col_names <- 
        colnames(df) %>%
        map(~ map2(., 1:length(unique(df$id)), ~paste0(.x, '_', .y) )) %>%
        transpose() %>%
        map(~ reduce(.x, cbind)) 
    
    
    df_wide <- group_split(df, x) %>%
        map2(col_names, ~ set_names(.x, .y)) %>%
        reduce(cbind) 
    
    head(as_tibble(df_wide))
    #> # A tibble: 6 x 50
    #>    id_1 x_1   y_1   start_1    stop_1      id_2 x_2   y_2   start_2   
    #>   <int> <chr> <chr> <date>     <date>     <int> <chr> <chr> <date>    
    #> 1     1 A     y     2020-11-03 2021-10-19     1 B     y     NA        
    #> 2     2 A     n     NA         NA             2 B     x     NA        
    #> 3     3 A     s     NA         NA             3 B     v     NA        
    #> 4     4 A     s     NA         NA             4 B     x     NA        
    #> 5     5 A     y     NA         NA             5 B     z     NA        
    #> 6     6 A     v     2020-10-14 2021-09-02     6 B     o     2020-10-03
    #> # … with 41 more variables: stop_2 <date>, id_3 <int>, x_3 <chr>, y_3 <chr>,
    #> #   start_3 <date>, stop_3 <date>, id_4 <int>, x_4 <chr>, y_4 <chr>,
    #> #   start_4 <date>, stop_4 <date>, id_5 <int>, x_5 <chr>, y_5 <chr>,
    #> #   start_5 <date>, stop_5 <date>, id_6 <int>, x_6 <chr>, y_6 <chr>,
    #> #   start_6 <date>, stop_6 <date>, id_7 <int>, x_7 <chr>, y_7 <chr>,
    #> #   start_7 <date>, stop_7 <date>, id_8 <int>, x_8 <chr>, y_8 <chr>,
    #> #   start_8 <date>, stop_8 <date>, id_9 <int>, x_9 <chr>, y_9 <chr>,
    #> #   start_9 <date>, stop_9 <date>, id_10 <int>, x_10 <chr>, y_10 <chr>,
    #> #   start_10 <date>, stop_10 <date>
    
    head(as_tibble(df_wide[, 11:16]))
    #> # A tibble: 6 x 6
    #>    id_3 x_3   y_3   start_3    stop_3      id_4
    #>   <int> <chr> <chr> <date>     <date>     <int>
    #> 1     1 C     m     2020-03-29 2021-02-02     1
    #> 2     2 C     p     NA         NA             2
    #> 3     3 C     s     NA         NA             3
    #> 4     4 C     m     NA         NA             4
    #> 5     5 C     t     NA         NA             5
    #> 6     6 C     q     2020-02-24 2021-02-10     6
    

    reprex package (v2.0.0) 于 2021 年 6 月 11 日创建

    【讨论】:

    • 感谢 jpdugo17,这将根据需要创建输出。老实说,我对map 函数不是很熟悉(尽管熟悉它会很有帮助)。我刚刚运行了您的代码并且它有效,但是我有问题要理解,到底发生了什么。在第一个代码块中,我们创建一个包含列名的列表。当我阅读col_names 的输出时,列表中的每个元素上方都有outelt 之类的术语。这是什么意思?在下一个块中,我们根据xdf拆分为每个组,分配col_names,然后将拆分的组合并在一起。
    猜你喜欢
    • 1970-01-01
    • 2020-10-23
    • 1970-01-01
    • 2021-09-15
    • 1970-01-01
    • 2017-07-20
    • 2022-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多