regexp R-提取逗号之间的字符串

时间:2019-06-26 23:50:11

标签: r regex data.table gsub

由于我的csv文件已损坏,因此我使用以下命令将其读入R:

dataDT <- data.table::fread(".../test.csv", sep = NULL)

它为数据集提供了类似的内容:

dataDT <- data.table("ColA,ColB,ColC,ColD" = c("1,10,some text... , some text,,20190801",
                                               "2,22,some text... , some text,,20190801",
                                               "3,30,some text... , some text,,20170601"))
dataDT
> dataDT
                       ColA,ColB,ColC,ColD
1: 1,10,some text... , some text,,20190801
2: 2,22,some text... , some text,,20190801
3: 3,30,some text... , some text,,20170601

现在,我想要的是将每行中的字符串分成4个新列

targetDT <- data.table(ColA = c(1,2,3), 
                       ColB = c(10,22,30), 
                       ColC = c("some text... , some text,", "some text... , some text,", "some text... , some text,"),
                       ColD = c("20190801","20190801",'20170601'))
targetDT
> targetDT
   ColA ColB                      ColC     ColD
1:    1   10 some text... , some text, 20190801
2:    2   22 some text... , some text, 20190801
3:    3   30 some text... , some text, 20170601

逻辑将是:

  • ColA在第一个逗号之前有一个字符串;
  • ColB包含介于第一和第二个逗号之间的所有内容;
  • ColD在最后一个逗号后有字符串;
  • ColC在中间部分有一个字符串(它可能包含其他逗号)。

如何实现以上逻辑?

编辑_1:

敏感数据,抱歉,我无法提供确切数据。看起来像:

        ID,Code1,Project_Name,Report_Date
1:     123123,1, A & B,20100101
2:     1413,2, C, D and E,20120101
3: 53163,333, F, G,20140303
4: 23453,44,This is a name,20160801
5: 12645,555,5th test, to continue,20190501

因此,第一个逗号之前的所有内容绝对是数字,第一和第二个逗号之间的所有内容也都是数字。上一个逗号之后的所有内容绝对是类似日期的8位数字。中间部分可以包含多个逗号,但不能包含引号(这是fread将逗号视为定界符的原因)。

3 个答案:

答案 0 :(得分:6)

这里是一个regex模式,您可以用它替换前导2和终端数字值附近的逗号,并将它们与周围的文本用空格分隔。使用非空格字符进行分隔可能更安全,因为我的下一步是使用read.*函数之一或也许再次使用fread读取“行”。也许使用“ |”作为分隔符?

sub("(\\d+)[,](\\d+)[,](.+)[,](\\d+)$", 
    "\\1 \\2 '\\3' \\4", 
    dataDT$"ColA,ColB,ColC,ColD" )

[1] "1 10 'some text... , some text,' 20190801" "2 22 'some text... , some text,' 20190801"
[3] "3 30 'some text... , some text,' 20170601"

模式中的括号用于创建“捕获类”,在每种情况下,我都使用“ \ d +”模式“捕获”了任意数量的数字或十进制分隔符。我还将替换模式的单引号引起来的文本(用“。+”捕获)括起来,因此第三列中想要的“内部空间”不会被视为分隔符。 "\\1""\\2"等是对每个捕获类中捕获的字符的引用,按其在模式中的出现顺序进行排序。参见?regex。但是,如果使用其他分隔符,则不需要单引号。

这是一个使用“ |”的测试作为sep

fread(text =sub("(\\d+)[,](\\d+)[,](.+)[,](\\d+)", "\\1|\\2|'\\3'|\\4", dataDT$"ColA,ColB,ColC,ColD" ) ,sep="|")
   V1 V2                          V3       V4
1:  1 10 'some text... , some text,' 20190801
2:  2 22 'some text... , some text,' 20190801
3:  3 30 'some text... , some text,' 20170601

注意:如果您的数字值包含逗号或前导或尾随货币,则您需要更改示例,因为使用"\\d"来捕获数字字符组将不再成功。

答案 1 :(得分:1)

使用纵梁的解决方案

library(data.table)
library(stringr)
library(dplyr)

dataDT <- data.table(data = c("1,10,some text... , some text,,20190801",
                               "2,22,some text... , some text,,20190801",
                               "3,30,some text... , some text,,20170601"))

dataDT <- dataDT %>% 
    mutate(
        ColA = str_extract(data, "^[^,]*(?=,)"),
        ColB = str_extract(data, "(?<=,)[^,]*(?=,)"),
        ColD = str_extract(data, "(?<=,)[^,]*$"),
        ColC = str_sub(data, nchar(ColA)+nchar(ColB)+3, nchar(data)-nchar(ColD)-1)
    ) %>% 
    select(ColA, ColB, ColC, ColD)

dataDT
#>   ColA ColB                      ColC     ColD
#> 1    1   10 some text... , some text, 20190801
#> 2    2   22 some text... , some text, 20190801
#> 3    3   30 some text... , some text, 20170601

reprex package(v0.3.0)于2019-06-27创建

答案 2 :(得分:0)

到目前为止发布的答案建议使用正则表达式的解决方案。

或者,可以考虑列的位置。正如OP指出的那样

  
      
  • ColA在第一个逗号前有字符串;
  •   
  • ColB包含介于第一和第二个逗号之间的所有内容;
  •   
  • ColD在最后一个逗号后有字符串;
  •   
  • ColC在中间部分有一个字符串(它可能包含其他逗号)。
  •   

这个想法是像往常一样使用fread()sep = ","来读取文件,这会导致数据集未对齐。重新整形为长格式后,可以每行标识第一,第二和最后一列以及中间列。这些条目可以指定各自的列名称。在最后一次改版为宽幅格式期间,中间列折叠为ColC

library(data.table)
# read file
DT <- fread("
1,10,some text...  some text,,20190801
2,22,some text... , some text,,20190801
3,30,some text... ,, some text,,20170601"
            , sep = ","
            , fill = TRUE
            , header = FALSE
            , strip.white = FALSE)

请注意,示例数据集已通过插入其他逗号进行修改以具有更实际的测试用例。

读取操作的结果是未对齐且参差不齐的数据集:

DT
   V1 V2                      V3         V4         V5       V6       V7
1:  1 10 some text...  some text              20190801       NA       NA
2:  2 22           some text...   some text            20190801       NA
3:  3 30           some text...              some text       NA 20170601
cols <- c("ColA", "ColB", "ColC", "ColD")
# reshape from wide to long format
long <- melt(DT[, rn := .I], "rn", na.rm = TRUE)
# create lookup table to rename column names  
lut <- long[, .(variable, col = rep(cols, c(1L, 1L, .N - 3, 1L))), by = rn]
# rename columns by an update join
long[lut,  on = .(rn, variable), variable := col][]
# reshape and collapse 
dcast(long, rn ~ variable, paste, collapse = ",")
   ColA ColB                      ColC     ColD
1:    1   10  some text...  some text, 20190801
2:    2   22 some text... , some text, 20190801
3:    3   30 some text... ,, some text 20170601

如果我们看一下中间结果,可以更好地解释这种方法。

melt()之后,long

    rn variable                   value
 1:  1       V1                       1
 2:  2       V1                       2
 3:  3       V1                       3
 4:  1       V2                      10
 5:  2       V2                      22
 6:  3       V2                      30
 7:  1       V3 some text...  some text
 8:  2       V3           some text... 
 9:  3       V3           some text... 
10:  1       V4                        
11:  2       V4               some text
12:  3       V4                        
13:  1       V5                20190801
14:  2       V5                        
15:  3       V5               some text
16:  2       V6                20190801
17:  3       V7                20170601

由此,创建查找表lut

    rn variable  col
 1:  1       V1 ColA
 2:  1       V2 ColB
 3:  1       V3 ColC
 4:  1       V4 ColC
 5:  1       V5 ColD
 6:  2       V1 ColA
 7:  2       V2 ColB
 8:  2       V3 ColC
 9:  2       V4 ColC
10:  2       V5 ColC
11:  2       V6 ColD
12:  3       V1 ColA
13:  3       V2 ColB
14:  3       V3 ColC
15:  3       V4 ColC
16:  3       V5 ColC
17:  3       V7 ColD

在加入更新之后并重新调整为宽幅格式之前,long看起来像

    rn variable                   value
 1:  1     ColA                       1
 2:  2     ColA                       2
 3:  3     ColA                       3
 4:  1     ColB                      10
 5:  2     ColB                      22
 6:  3     ColB                      30
 7:  1     ColC some text...  some text
 8:  2     ColC           some text... 
 9:  3     ColC           some text... 
10:  1     ColC                        
11:  2     ColC               some text
12:  3     ColC                        
13:  1     ColD                20190801
14:  2     ColC                        
15:  3     ColC               some text
16:  2     ColD                20190801
17:  3     ColD                20170601

现在,数据项已与它们各自的列名对齐。