【问题标题】:Spliting columns with different number of rows including titles拆分具有不同行数的列,包括标题
【发布时间】:2019-10-28 18:14:57
【问题描述】:

我有如下数据框

ID  age type_location          value_1  value_2    
1   83  country:province:city  X:A:J    X:A:I
2   15  country:city           X:K      X:J
3   2   country:province:city  Y:B:I    Y:B:I
4   12  country:city           X:L      Z:K
5   2   country:city           Y:J      X:J
6   2   country:province:city  Y:A:M    Y:B:I
7   18  country:province:city  X:B:J    X:A:L
8   85  country:province:city  X:A:I    Y:B:M

描述一下:第三列(type_location),其中记录包含以“:”分隔的字符串,长度不同,这是第四列和第五列中的值的名称。

因此,我需要这样获取data.frame:

ID  age value_1_country value_1_province value_1_city value_2_country value_2_province value_2_city
1   83  X               A                J            X               A                I
2   15  X               'NA'             K            X               'NA'             J
3   2   Y               B                I            Y               B                I
4   12  X               'NA'             L            Z               'NA'             K
5   2   Y               'NA'             J            X               'NA'             J
6   2   Y               A                M            Y               B                I
7   18  X               B                J            X               A                L
8   85  X               A                I            Y               B                M

描述 - 我需要将第 4 列和第 5 列中的值分开,放入新列并根据第 3 列中的记录给它们命名。在第三列中有不同数量的值用“:”分隔。

我应该怎么做才能传播值并保留第四列和第五列的名称?

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    如果您使用tidyverse(也可以导入tidyr),您可以使用separate:

    library(tidyverse)
    
    new_df = df %>%
      separate('value_1', c('value_1_country', 'value_1_province', 'value_1_city'), sep = ":") %>%
      separate('value_2', c('value_2_country', 'value_2_province', 'value_2_city'), sep = ":")
    
    new_df
    

    我从那一步得到的结果:

    > new_df
      ID age         type_location value_1_country value_1_province value_1_city value_2_country value_2_province value_2_city
    1  1  83 country:province:city               X                A            J               X                A            I
    2  2  15          country:city               X                K         <NA>               X                J         <NA>
    3  3   2 country:province:city               Y                B            I               Y                B            I
    4  4  12          country:city               X                L         <NA>               Z                K         <NA>
    5  5   2          country:city               Y                J         <NA>               X                J         <NA>
    6  6   2 country:province:city               Y                A            M               Y                B            I
    7  7  18 country:province:city               X                B            J               X                A            L
    8  8  85 country:province:city               X                A            I               Y                B            M
    

    对于分隔,第一个参数是您的数据框,第二个是要分隔的列,第三个是列名的向量,sep='' 是要分隔的字符。

    您还可以指定 NAN 处理和其他内容,请参见此处:

    https://tidyr.tidyverse.org/reference/separate.html

    另一个人更快地得到了完整的答案,所以我不会完成输入完整的解决方案。

    【讨论】:

    • 但是线索是从第三列中取名字并放入新列的名称中(并且它们的数量不同,所以我无法构建带有名称的固定向量)。
    • 好的,现在我实际上修正了我的(几个)错别字,它现在可以工作了。
    【解决方案2】:

    我敢肯定,有比我更优雅的方法,但使用 dplyr/tidyverse 你可以做到以下几点。

    将您的数据作为dput(df) 的输出提供总是被认为是礼貌的,因为这可以更容易地读入R。

    df=structure(list(ID = 1:8, 
                      age = c(83L, 15L, 2L, 12L, 2L, 2L, 18L, 85L), 
                      type_location = c("country:province:city", "country:city", 
                                        "country:province:city", "country:city",
                                        "country:city", "country:province:city", 
                                        "country:province:city", "country:province:city"),
                      value_1 = c("X:A:J","X:K", "Y:B:I", "X:L", "Y:J", "Y:A:M", "X:B:J", "X:A:I"), 
                      value_2 = c("X:A:I", "X:J", "Y:B:I", "Z:K", "X:J", "Y:B:I", "X:A:L", "Y:B:M")),
                 class = "data.frame", row.names = c(NA, -8L))
    
    library(tidyverse)
    
    df_sep=df %>% 
      separate(col=value_1,into=c(paste0("value_1_",c("country","province","city"))),sep=":",remove = F) %>% 
      mutate(value_1_city=ifelse(nchar(value_1)<4,value_1_province,value_1_city),
             value_1_province=ifelse(nchar(value_1)<4,NA,value_1_province)) %>% 
      separate(col=value_2,into=c(paste0("value_2_",c("country","province","city"))),sep=":",remove = F) %>% 
      mutate(value_2_city=ifelse(nchar(value_2)<4,value_2_province,value_2_city),
             value_2_province=ifelse(nchar(value_2)<4,NA,value_2_province))
    

    我设置了remove=FALSE,以便您可以更轻松地检查输出。如果要删除原始列,只需设置remove=T(默认值)。

    【讨论】:

      【解决方案3】:

      这是一种 data.table 方法(可能可以缩短)...

      library( data.table )
      
      dt <- fread("ID  age type_location          value_1  value_2    
      1   83  country:province:city  X:A:J    X:A:I
      2   15  country:city           X:K      X:J
      3   2   country:province:city  Y:B:I    Y:B:I
      4   12  country:city           X:L      Z:K
      5   2   country:city           Y:J      X:J
      6   2   country:province:city  Y:A:M    Y:B:I
      7   18  country:province:city  X:B:J    X:A:L
      8   85  country:province:city  X:A:I    Y:B:M")
      
      #get #types of location
      z <- length(unique(unlist(strsplit(dt$type_location, ":"))))
      #create new columns
      dt[, paste0( "location_",1:z) := tstrsplit( type_location, ":" )]
      dt[, paste0( "value_1_",1:z) := tstrsplit( value_1, ":" )]
      dt[, paste0( "value_2_",1:z) := tstrsplit( value_2, ":" )]
      #melt
      ans <- melt( dt[, -(3:5)], 
                   id.vars = c("ID", "age"), 
                   measure.vars = patterns(location = "^location", 
                                           value_1 = "^value_1", 
                                           value_2 = "^value_2") )
      #remove rows with NA
      ans <- ans[ complete.cases(ans), ]
      #cast to desired format
      dcast(ans, ID + age ~ location, value.var = c("value_1", "value_2"))
      

      输出

      #    ID age value_1_city value_1_country value_1_province value_2_city value_2_country value_2_province
      # 1:  1  83            J               X                A            I               X                A
      # 2:  2  15            K               X             <NA>            J               X             <NA>
      # 3:  3   2            I               Y                B            I               Y                B
      # 4:  4  12            L               X             <NA>            K               Z             <NA>
      # 5:  5   2            J               Y             <NA>            J               X             <NA>
      # 6:  6   2            M               Y                A            I               Y                B
      # 7:  7  18            J               X                B            L               X                A
      # 8:  8  85            I               X                A            M               Y                B
      

      【讨论】:

        【解决方案4】:

        当然我会选择Ryan's answer,但如果有人想避免硬编码名称,这行得通。

        library(tidyr)
        library(dplyr)
        library(purrr)
        
        df1 %>% 
          group_split(ID) %>% 
          map(., ~ separate_rows(.x, -c(ID, age))) %>% 
          map(., ~ mutate(.x, type_location2 = type_location, 
                              type_location1 = type_location, 
                              type_location = NULL)) %>% 
          map(., ~pivot_wider(.x, names_from = type_location1, values_from = value_1,
                              names_prefix = "V_1_")) %>% 
          map(., ~pivot_wider(.x, names_from = type_location2, values_from = value_2,
                              names_prefix = "V_2_")) %>% 
          map(., ~group_by(.x, ID, age) %>% summarise_all(., na.omit)) %>% 
          bind_rows()
        
        # # A tibble: 8 x 8
        # # Groups:   ID [8]
        #      ID   age V_1_country V_1_province V_1_city V_2_country V_2_province V_2_city
        #   <int> <int> <chr>       <chr>        <chr>    <chr>       <chr>        <chr>   
        # 1     1    83 X           A            J        X           A            I       
        # 2     2    15 X           NA           K        X           NA           J       
        # 3     3     2 Y           B            I        Y           B            I       
        # 4     4    12 X           NA           L        Z           NA           K       
        # 5     5     2 Y           NA           J        X           NA           J       
        # 6     6     2 Y           A            M        Y           B            I       
        # 7     7    18 X           B            J        X           A            L       
        # 8     8    85 X           A            I        Y           B            M       
        

        数据:

        df1 <- read.table(text="ID  age type_location          value_1  value_2    
                                1   83  country:province:city  X:A:J    X:A:I
                                2   15  country:city           X:K      X:J
                                3   2   country:province:city  Y:B:I    Y:B:I
                                4   12  country:city           X:L      Z:K
                                5   2   country:city           Y:J      X:J
                                6   2   country:province:city  Y:A:M    Y:B:I
                                7   18  country:province:city  X:B:J    X:A:L
                                8   85  country:province:city  X:A:I    Y:B:M", 
                          header=T, stringsAsFactors=F)
        

        【讨论】:

          猜你喜欢
          • 2015-12-06
          • 1970-01-01
          • 2014-12-08
          • 2020-11-19
          • 2020-09-16
          • 2017-04-01
          • 2022-09-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多