розділити стовпці символів та отримати назви полів у рядку


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

Найпростіший спосіб отримати це було б дуже вдячно! ( Примітка: я не бажаю їхати шляхю / тидиром )

Відповіді:


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))]

Я отримую дуже довге попередження "Неправильне .internal.selfref виявлено та виправлено, зробивши (дрібну) копію data.table ..."
Moody_Mudskipper

також введіть тут символ та кінець, не впевнені, чи очікується це
Moody_Mudskipper

1
@Moody_Mudskipper Дякую за коментар. (1) (Це попередження (я думаю) викликане тим, що створено data.table, structure()я оновив відповідь, щоб уникнути цієї проблеми (2) Вони є спеціально символами ... Я відчув, що їх правильно розібрати, було б важко і окреме питання. Здається, ви вирішили це, хоча у своїй відповіді я
погляну

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

Додаткові параметри запитів див. У посібнику .


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")]

@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")]

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 пакетом reprex (v0.3.0)


У мене немає потреби змінювати ";" в "," і не любить 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.