繁体   English   中英

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

[英]regexp R - extract string between commas

由于我的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将逗号视为定界符的原因)。

这是一个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"捕获数字字符组将不再成功。

使用Stringr的解决方案

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软件包 (v0.3.0)创建于2019-06-27

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

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

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

这样做的想法是照常使用sep = "," fread()读取文件sep = ","这会导致数据集未对齐。 重新整形为长格式后,可以按行标识第一,第二和最后一列以及中间列。 这些条目可以指定各自的列名称。 在最终ColC为宽格式期间,中间列折叠为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 

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM