R中是否有一种方法可以将一列中的字符串拆分为多列并为每个子字符串添加行?

时间:2019-11-28 20:43:32

标签: r dataframe

我正在尝试在R中实现以下目标,而不使用嵌套的for循环:

input_df:                   output_df:
Attr1 Attr2 Dates           Attr1 Attr2 StartDate EndDate
1     2     A-B,B-C,D-E     1     2     A         B
3     4     F-G         --> 1     2     B         C
5     6     H-I,J-K         1     2     D         E
                            3     4     F         G
                            5     6     H         I
                            5     6     J         K

数据框有50,000多行,for循环要花很长时间才能完成工作。

4 个答案:

答案 0 :(得分:2)

使用基数R,您可以执行以下操作。用“,”分隔“日期”列,然后在“-”上再次分隔结果以创建一个两列的数据框。

d <- strsplit(input_df$Dates, ',')
x <- do.call(rbind, strsplit(unlist(d), '-'))

现在,您可以重复原始数据的Attr1,Attr2字段,并重复所产生的','分割的分量:

data.frame(input_df[rep(seq_len(NROW(input_df)), lengths(d)), 1:2], 
           Start = x[,1], 
           End = x[, 2])
# output
#    Attr1 Attr2 Start End
#1       1     2     A   B
#1.1     1     2     B   C
#1.2     1     2     D   E
#2       3     4     F   G
#3       5     6     H   I
#3.1     5     6     J   K

其中input_df的定义如下:

input_df <- data.frame(Attr1 = c(1L, 3L, 5L), 
                       Attr2 = c(2L, 4L, 6L), 
                       Dates = c("A-B,B-C,D-E", "F-G", "H-I,J-K"), 
                       stringsAsFactors = FALSE)

答案 1 :(得分:1)

我们可以将separate_rowsseparate一起使用。在定界符,处分割'日期'列,并用separate_rows展开行,然后用separate在定界符-处将'日期'分为两列< / p>

library(tidyr)
library(dplyr)
separate_rows(input_df, Dates, sep = ",") %>%
     separate(Dates, into = c("StartDate", "EndDate"))
#  Attr1 Attr2 StartDate EndDate
#1     1     2         A       B
#2     1     2         B       C
#3     1     2         D       E
#4     3     4         F       G
#5     5     6         H       I
#6     5     6         J       K

或者另一种选择是将元素提取到list列中,然后unnest列在list列中

library(stringr)
input_df %>%
  transmute(Attr1, Attr2, 
        StartDate = str_extract_all(Dates, "\\w+(?=-)"), 
        EndDate = str_extract_all(Dates, "(?<=-)\\w+")) %>% 
  unnest(c(StartDate, EndDate))

数据

input_df <- structure(list(Attr1 = c(1L, 3L, 5L), Attr2 = c(2L, 4L, 6L), 
    Dates = c("A-B,B-C,D-E", "F-G", "H-I,J-K")), 
    class = "data.frame", row.names = c(NA, 
-3L))

答案 2 :(得分:1)

您可以尝试以下代码,其中substring用于提取第三列中的对:

res <- Reduce(rbind,
       lapply(split(input_df,seq(nrow(input_df))), function(v) {
         l <- nchar(v[3])
         data.frame(v[-3],
                    StartDate = substring(v[3],seq(1,l,4),seq(1,l,4)),
                    EndDate = substring(v[3],seq(3,l,4),seq(3,l,4)),
                    row.names = NULL)}
       )
)

输出

> res
  Attr1 Attr2 StartDate EndDate
1     1     2         A       B
2     1     2         B       C
3     1     2         D       E
4     3     4         F       G
5     5     6         H       I
6     5     6         J       K

答案 3 :(得分:0)

我们可以使用cSplit中的splitstackshape,首先获取长格式然后宽格式的数据。

library(splitstackshape)
cSplit(cSplit(df, "Dates", direction = "long"), "Dates", sep = "-")

#   Attr1 Attr2 Dates_1 Dates_2
#1:     1     2       A       B
#2:     1     2       B       C
#3:     1     2       D       E
#4:     3     4       F       G
#5:     5     6       H       I
#6:     5     6       J       K

数据

df <- structure(list(Attr1 = c(1L, 3L, 5L), Attr2 = c(2L, 4L, 6L), 
Dates = structure(1:3, .Label = c("A-B,B-C,D-E", "F-G", "H-I,J-K"
), class = "factor")), class = "data.frame", row.names = c(NA, -3L))