regexp R - извлечь строку между запятыми - PullRequest
1 голос
/ 27 июня 2019

Поскольку мой 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 имеет строку перед 1-й запятой;
  • В ColB есть все между 1-й и 2-й запятой;
  • В ColD есть строка после последней запятой;
  • В ColC есть строка в средней части (она может содержать дополнительные запятые).

Как выполнить приведенную выше логику?

Edit_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

Итак, все до 1-й запятой определенно являются числами, как и все между 1-й и 2-й запятой.Все, что идет после последней запятой, определенно является 8-значным числом, похожим на дату.Средняя часть может содержать несколько запятых, но без кавычек (что, я думаю, является причиной того, что fread рассматривает запятую как разделитель).

Ответы [ 3 ]

6 голосов
/ 27 июня 2019

Вот шаблон regex, который позволит вам заменять запятые, которые примыкают к двум начальным и конечным числовым значениям, и отделять их друг от друга и от окружающего текста пробелами.Возможно, безопаснее использовать непробельный символ для разделения, так как мой следующий шаг - прочитать «строки» с одной из 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 голос
/ 27 июня 2019

Решение с использованием 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

Создано в 2019-06-27 с помощью пакета Представить (v0.3.0)

0 голосов
/ 28 июня 2019

Ответы, опубликованные до сих пор, предлагают решения с использованием регулярных выражений.

В качестве альтернативы можно рассмотреть позиции столбцов. Как указала ОП

  • ColA имеет строку перед 1-й запятой;
  • ColB имеет все между 1 и 2 запятой;
  • У 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

Теперь элементы данных выровнены по соответствующим именам столбцов.

...