【问题标题】:transforming data in R在 R 中转换数据
【发布时间】:2017-10-07 03:37:40
【问题描述】:

我的数据在导入时看起来像这样;

ID col1 col2 col3 col4
1  a    e    i    r
             j    s
             k    t
2  b    f    l    u
             m    v
             n    w
             o    x
3  c    g    p    y
4  d    h    q    z

并希望对其进行转换,以便每行有一个唯一的 ID,即 IE:

ID col1 col2 col3 col4 col5 col6 col7 col8 col9 col10
1  a    e    i    r    j    s    k    t
2  b    f    l    u    m    v    n    w    o    x
3  c    g    p    y
4  d    h    q    z

易于消化的数据:

df <- data.frame(ID = c(1, NA, NA, 2, NA, NA, NA, 3, 4),
                 col1 = c('a', NA, NA, 'b', NA, NA, NA, 'c', 'd'),
                 col2 = c('e', NA, NA, 'f', NA, NA, NA, 'g', 'h'),
                 col3 = letters[9:17],
                 col4 = letters[18:26])

【问题讨论】:

  • 你能提供你的数据吗?我不确定如何按原样处理第 3 列和第 4 列的值。

标签: r reshape tidyr


【解决方案1】:

tidyverse解决方案:

df %>%
  mutate(ID = zoo::na.locf(ID)) %>%
  mutate(row = row_number()) %>%
  tidyr::gather(col, val, col1:col4) %>%
  filter(!is.na(val)) %>%
  arrange(ID, row, col) %>%
  select(-row) %>%
  group_by(ID) %>%
  mutate(col = row_number()) %>%
  mutate(col = paste0('col', stringr::str_pad(col, side = 'left', pad = '0', width = 2))) %>%
  tidyr::spread(col, val)

【讨论】:

    【解决方案2】:

    这是一个结合使用dplyrtidyr 以及一些基础的解决方案:

    library(dplyr)
    library(tidyr)
    
    df <- fill(df, ID, .direction = 'down')
    numCols <- max(sapply(split(df, df$ID), function(x) sum(!is.na(x[, -1]))))
    
    df %>%
      group_by(ID) %>%
      do(summarise(., l = paste(unlist(.[, -1])[!is.na(unlist(.[, -1]))], collapse = ' '))) %>%
      separate(l, into = paste0('col', 1:numCols), sep = ' ')
    

    输出如下:

         ID  col1  col2  col3  col4  col5  col6  col7  col8  col9 col10
    * <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
    1     1     a     e     i     j     k     r     s     t  <NA>  <NA>
    2     2     b     f     l     m     n     o     u     v     w     x
    3     3     c     g     p     y  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>
    4     4     d     h     q     z  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>
    

    【讨论】:

      【解决方案3】:

      需要注意的是,对于这种情况,长格式几乎总是更有用,有两种选择:

      library(tidyverse)
      
      df <- data.frame(ID = c(1, NA, NA, 2, NA, NA, NA, 3, 4),
                       col1 = c('a', NA, NA, 'b', NA, NA, NA, 'c', 'd'),
                       col2 = c('e', NA, NA, 'f', NA, NA, NA, 'g', 'h'),
                       col3 = letters[9:17],
                       col4 = letters[18:26])
      
      df %>% fill(ID) %>% 
          gather(var, val, -ID) %>% 
          drop_na(val) %>% 
          group_by(ID) %>% 
          mutate(var = sprintf('col%02d', row_number())) %>% 
          spread(var, val)
      
      #> # A tibble: 4 × 11
      #> # Groups: ID [4]
      #>      ID col01 col02 col03 col04 col05 col06 col07 col08 col09 col10
      #> * <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
      #> 1     1     a     e     i     j     k     r     s     t  <NA>  <NA>
      #> 2     2     b     f     l     m     n     o     u     v     w     x
      #> 3     3     c     g     p     y  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>
      #> 4     4     d     h     q     z  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>
      

      或将所有内容折叠成字符串并分开:

      df %>% mutate_at(vars(-ID), as.character) %>% 
          fill(ID) %>% 
          group_by(ID) %>% 
          summarise(lets = toString(na.omit(c(col1, col2, col3, col4)))) %>% 
          separate(lets, sprintf('col%02d', 1:10), fill = 'right')
      
      #> # A tibble: 4 × 11
      #>      ID col01 col02 col03 col04 col05 col06 col07 col08 col09 col10
      #> * <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
      #> 1     1     a     e     i     j     k     r     s     t  <NA>  <NA>
      #> 2     2     b     f     l     m     n     o     u     v     w     x
      #> 3     3     c     g     p     y  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>
      #> 4     4     d     h     q     z  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>
      

      【讨论】:

      • 非常好。比我下面的解决方案要清晰得多。我更喜欢第一个版本,因为它与列数无关(类似于我的目标)。
      • 顺便说一句 - 您的第二个解决方案是硬编码 10,这在“更完整的数据”中可能不是真的。正如我所做的那样,最好计算。另一个原因,我喜欢第一个。
      • 是的,您可以在summarise 步骤中计算长度,但如果您对输入列进行硬编码,这并不值得。另一个 hacky 解决方法是只创建比您需要的更多的列,然后删除任何完全为 NA 的列。
      【解决方案4】:

      Base R 有时还不错:

      tmp <- na.omit(data.frame(id=cummax(replace(df$ID, is.na(df$ID), 0)), col=unlist(df[-1]) ))
      reshape(transform(tmp, time=ave(id,id,FUN=seq_along)), direction="wide", idvar="id", sep="")
      
      #      id col1 col2 col3 col4 col5 col6 col7 col8 col9 col10
      #col11  1    a    e    i    j    k    r    s    t <NA>  <NA>
      #col14  2    b    f    l    m    n    o    u    v    w     x
      #col18  3    c    g    p    y <NA> <NA> <NA> <NA> <NA>  <NA>
      #col19  4    d    h    q    z <NA> <NA> <NA> <NA> <NA>  <NA>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-04
        • 1970-01-01
        • 2014-04-03
        • 2015-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多