Teilen Sie Zeichenspalten und erhalten Sie die Namen der Felder in der Zeichenfolge


11

Ich muss eine Spalte, die Informationen enthält, in mehrere Spalten aufteilen.
Ich würde verwenden, tstrsplitaber die gleiche Art von Informationen ist nicht in der gleichen Reihenfolge zwischen den Zeilen und ich muss den Namen der neuen Spalte innerhalb der Variablen extrahieren. Wichtig zu wissen: Es kann viele Informationen geben (Felder, die zu neuen Variablen werden), und ich kenne nicht alle, daher möchte ich keine "Feld für Feld" -Lösung.

Unten ist ein Beispiel für das, was ich habe:

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

Und ich würde gerne bekommen:

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

Ein sehr einfacher Weg, dies zu erreichen, wäre sehr dankbar! ( Hinweis: Ich bin nicht bereit, einen dplyr / tidyr-Weg einzuschlagen. )

Antworten:


5

Verwenden regexund die stringiPakete:

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

Bearbeiten: Um den richtigen Typ zu erhalten, kann anscheinend (?) type.convert()Verwendet werden:

myDT[, (fields) := lapply(patterns, function(x) type.convert(stri_extract(regex = x, info), as.is = TRUE))]

Ich erhalte eine sehr lange Warnung "Ungültige .internal.selfref erkannt und behoben, indem eine (flache) Kopie der data.table erstellt wird ..."
Moody_Mudskipper

Auch Typ und Ende sind hier Zeichen, nicht sicher, ob das erwartet wird
Moody_Mudskipper

1
@Moody_Mudskipper Danke für den Kommentar. (1) (Diese Warnung wird (glaube ich) durch die von erstellt. Data.table verursacht. structure()Ich habe die Antwort aktualisiert, um dieses Problem zu vermeiden. (2) Es handelt sich absichtlich um Zeichen. Ich hatte das Gefühl, dass es schwierig wäre, sie richtig zu analysieren und eine separate Frage. Es scheint, dass Sie es in Ihrer Antwort gelöst haben und ich werde einen Blick darauf werfen und sehen, ob ich etwas Neues lernen kann.
sindri_baldur

4

Ich vermute, Ihre Daten stammen aus einer VCF-Datei . Wenn ja, gibt es ein spezielles Tool für solche Probleme - bcftools .

Erstellen wir eine Beispiel- VCF- Datei zum Testen:

# 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

Jetzt können wir bcftools verwenden . Hier als Beispiel unterteilen wir AF und DP aus der INFO- Spalte:

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

Weitere Abfrageoptionen finden Sie im Handbuch .


3

Wir könnten uns aufteilen und ";"dann weit zu lang umformen, dann wieder aufteilen "="und dann wieder zu lang zu weit umformen:

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

Eine verbesserte / besser lesbare Version:

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 Danke, ich wusste, dass es eine bessere DT-Methode gibt, um Dinge zu verketten.
zx8754

3

Im Moment habe ich es geschafft, mit dem folgenden Code das zu bekommen, was ich will:

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

Zwei Optionen zur Verbesserung der obigen Zeilen dank @ A5C1D2H2I1M1N2O1R2T1 (der sie in Kommentaren angegeben hat):

. mit einem Doppel cSplitvor dcast:

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

. mit cSplit/ trstrplitund dcaststatt reshape:

cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]

1
Ich würde ein Double machen cSplit, so : cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")].
A5C1D2H2I1M1N2O1R2T1

1
Oder das gleiche Konzept: cSplitgefolgt von tstrsplit: gefolgt von dcast: cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")].
A5C1D2H2I1M1N2O1R2T1

@ A5C1D2H2I1M1N2O1R2T1 Vielen Dank! Beide sind großartig, mit einem Special für die Doppeloption cSplit:-)
Cath

2

So würde ich es machen:

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

Erstellt am 29.11.2019 durch das reprex-Paket (v0.3.0)


Ich muss mich nicht ändern ";" in "," und nicht gern eval(parse(text=...))... aber trotzdem danke für Ihre Antwort
Cath

1
Ich kann nicht mit persönlichem Geschmack streiten, habe aber parseeinen schlechten Ruf, weil er oft aus falschen Gründen verwendet wird. Hier ist genau der geeignete Anwendungsfall, der von der Zeichenfolge zum Code reicht. Sie haben Text formatiert, aber nicht für R formatiert, und Sie haben Listen benannt. In meiner ersten Zeile wird er für eine R-Liste codiert, indem "a; b" in "Liste (a, b)" geändert wird. Dann bewerten wir es und machen daraus eine Tabelle.
Moody_Mudskipper

1

Sie können separate Aufrufe subfür jedes gewünschte extrahierte Feld verwenden, z. B type.:

myDT$type <- sub("^.*\\btype=([^;]+)\\b.*$", "\\1", myDT$info)

Ich kenne nicht alle Dateien, die auftreten werden, und sie können sehr viel sein, daher ist dies keine Option
Cath

1
Meinetwegen; Ich wusste das nicht, als ich diese Antwort gepostet habe.
Tim Biegeleisen

Ich werde es hinzufügen (übrigens geben Sie nicht die gewünschte Ausgabe, Ihre Antwort verfehlt einige Zeilen ...)
Cath
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.