当某些列没有定界符时,在多个列上使用tidyr中的separate_rows

时间:2019-07-01 21:34:48

标签: r string dataframe split tidyr

我有以下模拟数据集:

A <- c("Acura", "BMW", "Toyota", NA)
B <- c("1993;2004;2010", "2013", "2003;2011", NA)
C <- c("Blue;Black;Gold", "Silver", NA, NA)

df <- data.frame(A = A, B = B, C = C)

因此数据框如下所示:

> df
         A                B                 C
  1  Acura   1993;2004;2010   Blue;Black;Gold
  2    BMW             2013            Silver
  3 Toyota        2003;2011              <NA>
  4   <NA>             <NA>              <NA>

我想将数据集扩展为多行,如下所示:

> new_df
          A         B          C
  1   Acura      1993       Blue
  2   Acura      2004      Black
  3   Acura      2010       Gold
  4     BMW      2013     Silver
  5  Toyota      2003       <NA>
  6  Toyota      2011       <NA>
  7    <NA>      <NA>       <NA>

我尝试使用tidyr :: separate_rows,但是出现此错误,因为单独的行在每一行中需要相同数量的定界符。这意味着第3行(A = Toyota)是一个问题,因为该行的C列中有一个NA,而不是“ NA; NA”之类的东西。这是我收到的命令和错误:

df %>% separate_rows(B, C, sep = ";", convert = TRUE)
   Error: All nested columns must have the same number of elements.

df[c(1:2,4),] %>% separate_rows(B, C, sep = ";", convert = TRUE)
      A    B      C
1 Acura 1993   Blue
2 Acura 2004  Black
3 Acura 2010   Gold
4   BMW 2013 Silver
5  <NA>   NA   <NA>

df[c(3),] %>% separate_rows(B, C, sep = ";", convert = TRUE)
    Error: All nested columns must have the same number of elements.

有人可以帮助实现new_df吗?!

2 个答案:

答案 0 :(得分:0)

好的,最简单的解决方案可能是安装tidyr(0.8.3.9000)的开发版本,因为它似乎已在此处固定。使用devtools::install_github("tidyverse/tidyr")来实现。

但是,对于那些无法更新或不想使用软件包的预发行版本的人,一种解决方法是,我们可以计算每行中所需的分隔符数量,并用分隔符填充列中的缺失值。这样一来separate_rows就可以工作并创建空字符串,然后我们将其替换为NA

library(tidyverse)
A <- c("Acura", "BMW", "Toyota", NA)
B <- c("1993;2004;2010", "2013", "2003;2011", NA)
C <- c("Blue;Black;Gold", "Silver", NA, NA)
df <- data.frame(A = A, B = B, C = C, stringsAsFactors = FALSE)

df %>%
  mutate(seps = str_pad("", width = str_count(B, ";"), pad = ";")) %>%
  mutate_at(vars(B, C), ~ coalesce(., seps)) %>%
  separate_rows(B, C, sep = ";") %>%
  mutate_at(vars(B, C), ~ str_replace(., "^$", NA_character_))
#>        A    B      C seps
#> 1  Acura 1993   Blue   ;;
#> 2  Acura 2004  Black   ;;
#> 3  Acura 2010   Gold   ;;
#> 4    BMW 2013 Silver     
#> 5 Toyota 2003   <NA>    ;
#> 6 Toyota 2011   <NA>    ;
#> 7   <NA> <NA>   <NA> <NA>

reprex package(v0.3.0)于2019-07-01创建

答案 1 :(得分:0)

在最新的tidyr中可以轻松完成

library(tidyr)

df %>% separate_rows(B, C, sep = ';')
# A tibble: 7 x 3
  A      B     C     
  <chr>  <chr> <chr> 
1 Acura  1993  Blue  
2 Acura  2004  Black 
3 Acura  2010  Gold  
4 BMW    2013  Silver
5 Toyota 2003  NA    
6 Toyota 2011  NA    
7 NA     NA    NA