разделить символьные столбцы и получить имена полей в строке

11

Мне нужно разделить столбец, содержащий информацию, на несколько столбцов.
Я бы использовал, tstrsplitно информация такого же рода не в том же порядке среди строк, и мне нужно извлечь имя нового столбца в переменной. Важно знать: может быть много информации (поля, которые станут новыми переменными), и я не знаю всех из них, поэтому я не хочу решения «поле за полем».

Ниже приведен пример того, что у меня есть:

library(data.table)

myDT <- structure(list(chr = c("chr1", "chr2", "chr4"), pos = c(123L,
                  435L, 120L), info = c("type=3;end=4", "end=6", "end=5;pos=TRUE;type=2"
                  )), class = c("data.table", "data.frame"), row.names = c(NA,-3L))

#    chr pos                  info
#1: chr1 123          type=3;end=4
#2: chr2 435                 end=6
#3: chr4 120 end=5;pos=TRUE;type=2

И я хотел бы получить:

#    chr pos end  pos type
#1: chr1 123   4 <NA>    3
#2: chr2 435   6 <NA> <NA>
#3: chr4 120   5 TRUE    2

Самый простой способ получить это будет высоко ценится! ( Примечание: я не хочу идти по пути dplyr / tidyr )

Cath
источник

Ответы:

5

Использование regexи stringiпакеты:

setDT(myDT) # After creating data.table from structure()

library(stringi)

fields <- unique(unlist(stri_extract_all(regex = "[a-z]+(?==)", myDT$info)))
patterns <- sprintf("(?<=%s=)[^;]+", fields)
myDT[, (fields) := lapply(patterns, function(x) stri_extract(regex = x, info))]
myDT[, !"info"]

    chr  pos type end
1: chr1 <NA>    3   4
2: chr2 <NA> <NA>   6
3: chr4 TRUE    2   5

Изменить: Чтобы получить правильный тип, кажется (?) type.convert()Может быть использован:

myDT[, (fields) := lapply(patterns, function(x) type.convert(stri_extract(regex = x, info), as.is = TRUE))]
sindri_baldur
источник
Я получаю очень длинное предупреждение «Обнаружен и исправлен неверный .internal.selfref путем создания (мелкой) копии data.table ...»
Moody_Mudskipper
также type и end являются символом здесь, не уверен, что это ожидается
Moody_Mudskipper
1
@Moody_Mudskipper Спасибо за комментарий. (1) (Это предупреждение (я думаю) вызвано тем, что создается data.table, structure()я обновил ответ, чтобы избежать этой проблемы) (2) Это специально предназначенные символы ... Я чувствовал, что их правильный анализ будет трудным и отдельный вопрос. Похоже, вы решили это, хотя в своем ответе я посмотрю и посмотрю, смогу ли я узнать что-то новое.
sindri_baldur
4

Я предполагаю, что ваши данные поступают из файла VCF , если так, есть специальный инструмент для таких проблем - bcftools .

Давайте создадим пример файла VCF для тестирования:

# subset some data from 1000genomes data
tabix -h ftp://ftp-trace.ncbi.nih.gov/1000genomes/ftp/release/20100804/ALL.2of4intersection.20100804.genotypes.vcf.gz 17:1471000-1472000 > myFile.vcf
# zip it and index:
bgzip -c myFile.vcf > myFile.vcf.gz
tabix -p vcf myFile.vcf.gz

Теперь мы можем использовать bcftools . Вот в качестве примера, мы устанавливаем AF и DP из столбца INFO :

bcftools query -f '%CHROM %POS %INFO/AF %INFO/DP \n' myFile.vcf.gz 
17  1471199  1916 0.088
17  1471538  2445 0.016
17  1471611  2733 0.239
17  1471623  2815 0.003
17  1471946  1608 0.007
17  1471959  1612 0.014
17  1471975  1610 0.179

Смотрите руководство для более подробной информации .

zx8754
источник
3

Мы могли бы разделить, ";"затем изменить форму от ширины к длине, затем снова разделить, а затем снова "="изменить форму к длине:

dcast(
  melt(dt[,  paste0("col", 1:3) := tstrsplit(info, split = ";") ],
       id.vars = c("chr", "pos", "info"))[, -c("info", "variable")][
         ,c("x1", "x2") := tstrsplit(value, split = "=")][
           ,value := NULL][ !is.na(x1), ],
  chr + pos ~ x1, value.var = "x2")

#     chr pos end  pos type
# 1: chr1 123   4 <NA>    3
# 2: chr2 435   6 <NA> <NA>
# 3: chr4 120   5 TRUE    2

Улучшенная / более читаемая версия:

dt[, paste0("col", 1:3) := tstrsplit(info, split = ";")
   ][, melt(.SD, id.vars = c("chr", "pos", "info"), na.rm = TRUE)
     ][, -c("info", "variable")
       ][, c("x1", "x2") := tstrsplit(value, split = "=")
         ][, dcast(.SD, chr + pos ~ x1, value.var = "x2")]
zx8754
источник
@Jaap Спасибо, я знал, что есть лучший способ связать вещи DT.
zx8754
3

На данный момент мне удалось получить то, что я хочу, с помощью следующего кода:

newDT <- reshape(splitstackshape::cSplit(myDT, "info", sep=";", "long")[, 
                  c(.SD, tstrsplit(info, "="))], 
                 idvar=c("chr", "pos"), direction="wide", timevar="V4", drop="info")
setnames(newDT, sub("V5\\.", "", names(newDT)))

newDT
#    chr pos type end  pos
#1: chr1 123    3   4 <NA>
#2: chr2 435 <NA>   6 <NA>
#3: chr4 120    2   5 TRUE

Два варианта улучшения строк выше, благодаря @ A5C1D2H2I1M1N2O1R2T1 (который дал их в комментариях):

, с двойным cSplitдо dcast:

cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")]

, с cSplit/ trstrplitи dcastвместо reshape:

cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]
Cath
источник
1
Я бы сделал дубль cSplit, как это: cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")].
A5C1D2H2I1M1N2O1R2T1
1
Или то же самое понятие: а cSplitзатем tstrsplit, после чего dcast: cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")].
A5C1D2H2I1M1N2O1R2T1
@ A5C1D2H2I1M1N2O1R2T1 Большое спасибо! Оба великолепны, со специальным для двойной cSplitопции :-)
Cath
2

Вот как я это сделаю:

library(data.table)

myDT <- structure(list(chr = c("chr1", "chr2", "chr4"), pos = c(123L,
                                                                435L, 120L), info = c("type=3;end=4", "end=6", "end=5;pos=TRUE;type=2"
                                                                )), class = c("data.table", "data.frame"), row.names = c(NA,-3L))

R_strings <- paste0("list(", chartr(";", ",", myDT$info),")")
lists <- lapply(parse(text=R_strings),eval)
myDT[,info:=NULL]
myDT <- cbind(myDT,rbindlist(lists, fill = TRUE))
myDT
#>     chr pos type end  pos
#> 1: chr1 123    3   4   NA
#> 2: chr2 435   NA   6   NA
#> 3: chr4 120    2   5 TRUE

Создано в 2019-11-29 пакетом представлением (v0.3.0)

Moody_Mudskipper
источник
Мне не нужно менять ";" в "," и не любит eval(parse(text=...))... но, тем не менее, спасибо за ваш ответ
Cath
1
Я не могу спорить с личным вкусом, но parseу меня плохая репутация, потому что она часто используется по неправильной причине, вот как раз тот случай, когда она подходит, переходя от строки к коду. Вы отформатировали текст, но не отформатировали для R, и у вас есть именованные списки, поэтому моя первая строка делает его кодом для списка R, заменяя «a; b» на «list (a, b)». Затем мы оцениваем это и составляем из него таблицу.
Moody_Mudskipper
1

Вы можете использовать отдельные вызовы subдля каждого желаемого извлеченного поля, например, для type:

myDT$type <- sub("^.*\\btype=([^;]+)\\b.*$", "\\1", myDT$info)
Тим Бигелейзен
источник
Я не знаю всех полей, которые будут происходить, и их может быть много, так что это не вариант
Cath
1
Справедливо; Я не знал этого, когда отправил этот ответ.
Тим Бигелейзен
Я добавлю это (кстати, вы не даете желаемый результат, ваш ответ пропускает несколько строк ...)
Cath