pisahkan kolom karakter dan dapatkan nama bidang dalam string

11

Saya perlu membagi kolom yang berisi informasi menjadi beberapa kolom.
Saya akan menggunakan tstrsplittetapi jenis informasi yang sama tidak dalam urutan yang sama di antara baris dan saya perlu mengekstrak nama kolom baru dalam variabel. Penting untuk diketahui: mungkin ada banyak informasi (bidang untuk menjadi variabel baru) dan saya tidak tahu semuanya, jadi saya tidak ingin solusi "bidang demi bidang".

Di bawah ini adalah contoh dari apa yang saya miliki:

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

Dan saya ingin mendapatkan:

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

Cara paling mudah untuk mendapatkan itu akan sangat dihargai! ( Catatan: Saya tidak mau menggunakan cara dplyr / tidyr )

Cath
sumber

Jawaban:

5

Menggunakan regexdan stringipaket:

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

Sunting: Untuk mendapatkan jenis yang benar, tampaknya (?) type.convert()Dapat digunakan:

myDT[, (fields) := lapply(patterns, function(x) type.convert(stri_extract(regex = x, info), as.is = TRUE))]
sindri_baldur
sumber
Saya mendapatkan peringatan yang sangat panjang "Tidak valid .internal.selfref terdeteksi dan diperbaiki dengan mengambil salinan (dangkal) data. Tabel ..."
Moody_Mudskipper
juga ketik dan akhiri adalah karakter di sini, tidak yakin apakah itu diharapkan
Moody_Mudskipper
1
@Moody_Mudskipper Terima kasih telah memberikan komentar. (1) (Peringatan ini (saya pikir) disebabkan oleh data. Tabel yang dibuat oleh structure()saya telah memperbarui jawaban untuk menghindari masalah ini (2) Mereka adalah karakter yang sengaja ... Saya merasa menguraikannya dengan benar akan menjadi sulit) dan pertanyaan yang terpisah. Tampaknya Anda menyelesaikannya dalam jawaban Anda dan saya akan melihat dan melihat apakah saya dapat mempelajari sesuatu yang baru.
sindri_baldur
4

Saya menduga data Anda berasal dari file VCF , jika demikian ada alat khusus untuk masalah seperti itu - bcftools .

Mari kita buat contoh file VCF untuk pengujian:

# 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

Sekarang kita bisa menggunakan bcftools . Di sini sebagai contoh, kami menetapkan AF dan DP dari kolom 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

Lihat manual untuk opsi permintaan lainnya.

zx8754
sumber
3

Kita dapat memecah ";"kemudian membentuk kembali lebar-ke-panjang, lalu membelah lagi "=", kemudian membentuk kembali ke panjang-ke-lebar:

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

Versi yang ditingkatkan / lebih siap pakai:

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
sumber
@ Jaap Terima kasih, saya tahu ada cara DT yang lebih baik untuk merantai sesuatu.
zx8754
3

Untuk saat ini, saya berhasil mendapatkan apa yang saya inginkan dengan kode berikut:

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

Dua opsi untuk meningkatkan baris di atas, terima kasih kepada @ A5C1D2H2I1M1N2O1R2T1 (yang memberi mereka komentar):

. dengan ganda cSplitsebelum dcast:

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

. dengan cSplit/ trstrplitdan dcastbukannya reshape:

cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]
Cath
sumber
1
Saya akan melakukan ganda cSplit, seperti ini: cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")].
A5C1D2H2I1M1N2O1R2T1
1
Atau, konsep yang sama: cSplitdiikuti oleh tstrsplit, diikuti oleh dcast: cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")].
A5C1D2H2I1M1N2O1R2T1
@ A5C1D2H2I1M1N2O1R2T1 Terima kasih banyak! Keduanya hebat, dengan spesial untuk cSplitopsi ganda :-)
Cath
2

Begini cara saya melakukannya:

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

Dibuat pada 2019-11-29 oleh paket reprex (v0.3.0)

Moody_Mudskipper
sumber
Saya tidak perlu mengubah ";" ke "," dan tidak menyukai eval(parse(text=...))... tapi terima kasih atas jawaban Anda
Cath
1
Saya tidak bisa berdebat dengan selera pribadi tetapi parsememiliki perwakilan yang buruk karena sering digunakan untuk alasan yang salah, inilah kasus penggunaannya yang tepat, beralih dari string ke kode. Anda telah memformat teks, tetapi tidak diformat untuk R, dan Anda telah menamai daftar, jadi baris pertama saya membuatnya menjadi kode untuk daftar R, dengan mengubah "a; b" menjadi "daftar (a, b)". Kemudian kami mengevaluasinya dan membuat tabel dari itu.
Moody_Mudskipper
1

Anda dapat menggunakan panggilan terpisah untuk subuntuk setiap bidang yang diekstraksi yang diinginkan, misalnya untuk type:

myDT$type <- sub("^.*\\btype=([^;]+)\\b.*$", "\\1", myDT$info)
Tim Biegeleisen
sumber
Saya tidak tahu semua pengajuan yang akan terjadi dan mereka bisa banyak jadi ini bukan pilihan
Cath
1
Cukup adil; Saya tidak tahu ini ketika saya memposting jawaban ini.
Tim Biegeleisen
Saya akan menambahkannya (tapi Anda tidak memberikan hasil yang diinginkan, jawaban Anda melewatkan beberapa baris ...)
Cath