将'1-1-1到1-10-1'变换为'1-1-1','1-2-1',......'1-10-1'的十个值

时间:2015-08-13 14:55:02

标签: regex r

我有一个如下数据框:

ID     
1-1-1, 1-2-1
2-1-1
3-1-1 through 3-5-1

我希望将数据帧转换为

ID    
1-1-1
1-2-1
2-1-1
3-1-1
3-2-1
3-3-1
3-4-1
3-5-1

对于第一个数据框中的第一行,我认为融化可以完成这项工作。但对于第三行,我认为我应该以某种方式将“通过”替换为两者之间的ID。我尝试了一些正则表达式,但没有找到一个好方法。

以下问题:

如果有另一列并且我想匹配它们怎么办?

NewColumn  ID
A          1-1-1, 1-2-1
B          2-1-1
C          3-1-1 through 3-5-1

NewColumn ID
A         1-1-1
A         1-2-1
B         2-1-1
C         3-1-1
C         3-2-1
C         3-3-1
C         3-4-1
C         3-5-1
对于多个新列项,

ID中的第一个数字可能相同。

1 个答案:

答案 0 :(得分:1)

在使用cSplit替换splitstackshapedata.table后,我们可以使用through,方法使用sub

使用正则表达式sub,我们会匹配是否有零个或多个空格(\\s*)后跟through后跟零或更多空格(\\s*)并替换为,用于' ID'列。

df1$ID <- sub('\\s*through\\s*', ', ', df1$ID)

现在我们使用cSplit来分割&#39; ID&#39;使用分隔符作为,并将方向指定为&#39; long&#39;的列。输出仍然是非数字的。因此,如果我们想制作一个序列,最好将其拆分为数字&#39;。我们使用cSplit作为分隔符,将默认方向设置为&#39; wide&#39;,然后执行第二次-。我们得到三列。现在,我们可以使用data.table方法。我们可以按照ID_1&#39;分组。和&#39; ID_3&#39;列并检查if组中的元素数量(.N)是否为>1。如果有多个元素,我们得到第一个和最后一个元素的序列(这里只有两个元素,所以第一个和第二个,即ID_2列,最后是paste三个列,并创建一个&# 39; data.frame&#39;

library(splitstackshape)
library(data.table)

 ID <- cSplit(cSplit(df1, 'ID', ', ', 'long'), 'ID', '-', type.convert=TRUE)[, 
       list(ID_2=if(.N>1) ID_2[1]:ID_2[2] else ID_2), by = .(ID_1, ID_3)
        ][, paste(ID_1, ID_2, ID_3, sep="-")]
 d1 <- data.frame(ID, stringsAsFactors=FALSE)
 d1
#ID
#1 1-1-1
#2 1-2-1
#3 2-1-1
#4 3-1-1
#5 3-2-1
#6 3-3-1
#7 3-4-1
#8 3-5-1

为了便于理解,可以将代码拆分为块。我们基于&#39;,&#39;创造一个“长”的格式

 cLong <- cSplit(df1, 'ID', ', ', 'long')

在下一步中,它将分为&#39; - &#39;我们使用选项type.convert=TRUE将列转换为各自的类。

 cLong1 <- cSplit(cLong, 'ID', '-', type.convert=TRUE)

现在,我们使用data.table方法,因为cSplit的输出已经是&#39; data.table&#39;

 DT1 <- cLong1[, list(ID_2=if(.N>1) 
                            ID_2[1]:ID_2[2] 
                            else ID_2),
                                 by = .(ID_1, ID_3)]

我们将列粘贴在一起

 ID <- do.call(paste, c(DT1[,c(1,3,2), with=FALSE], sep='-'))

并创建&#39; data.frame&#39;

 data.frame(ID)

更新

对于后续问题,我们只需要更改cSplit步骤。我们可以添加&#39; NewColumn&#39;作为分组变量。

df1$ID <- sub('\\s*through\\s*', ', ', df1$ID)

cSplit(cSplit(df1, 'ID', ', ', 'long'), 'ID', '-',
    type.convert=TRUE)[,  list(ID_2=if(.N>1) ID_2[1]:ID_2[2] else ID_2),
    by = .(NewColumn, ID_1, ID_3)
    ][,list(ID=paste(ID_1, ID_2, ID_3, sep="-")) ,.(NewColumn)]
#   NewColumn    ID
#1:         A 1-1-1
#2:         A 1-2-1
#3:         B 2-1-1
#4:         C 3-1-1
#5:         C 3-2-1
#6:         C 3-3-1
#7:         C 3-4-1
#8:         C 3-5-1

数据

df1 <- structure(list(ID = c("1-1-1, 1-2-1", "2-1-1",
"3-1-1 through 3-5-1")), .Names = "ID", class = "data.frame",
 row.names = c(NA, -3L))
#newdata
df1 <- structure(list(NewColumn = c("A", "B", "C"),
ID = c("1-1-1, 1-2-1", 
"2-1-1", "3-1-1 through 3-5-1")), .Names = c("NewColumn", "ID"
), class = "data.frame", row.names = c(NA, -3L))