拆分字符列并获取字符串中的字段名称
split character columns and get names of field in string
我需要将包含信息的列拆分为多个列。
我会使用 tstrsplit
但相同类型的信息在行中的顺序不同,我需要在变量中提取新列的名称。重要提示:可能有很多信息(要成为新变量的字段),我不知道所有信息,所以我不想要 "field by field" 解决方案。
下面是我所拥有的示例:
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路)
现在,我设法用下面的代码得到了我想要的东西:
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(他在评论中给出了它们):
。在 dcast
之前有一个双 cSplit
:
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")]
我们可以在 ";"
上拆分,然后从宽到长整形,然后在 "="
上再次拆分,然后再整形回长到宽:
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")]
您可以为每个所需的提取字段单独调用 sub
,例如对于 type
:
myDT$type <- sub("^.*\btype=([^;]+)\b.*$", "\1", myDT$info)
这是我的做法:
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
由 reprex package (v0.3.0)
创建于 2019-11-29
使用 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))]
我猜你的数据来自 VCF file, if so there is a dedicated tool for such problems - 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。作为示例,我们从 INFO 列 AF 和 DP 进行子集化:
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
有关更多 query 选项,请参阅手册。
我需要将包含信息的列拆分为多个列。
我会使用 tstrsplit
但相同类型的信息在行中的顺序不同,我需要在变量中提取新列的名称。重要提示:可能有很多信息(要成为新变量的字段),我不知道所有信息,所以我不想要 "field by field" 解决方案。
下面是我所拥有的示例:
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路)
现在,我设法用下面的代码得到了我想要的东西:
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(他在评论中给出了它们):
。在 dcast
之前有一个双 cSplit
:
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")]
我们可以在 ";"
上拆分,然后从宽到长整形,然后在 "="
上再次拆分,然后再整形回长到宽:
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")]
您可以为每个所需的提取字段单独调用 sub
,例如对于 type
:
myDT$type <- sub("^.*\btype=([^;]+)\b.*$", "\1", myDT$info)
这是我的做法:
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
由 reprex package (v0.3.0)
创建于 2019-11-29使用 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))]
我猜你的数据来自 VCF file, if so there is a dedicated tool for such problems - 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。作为示例,我们从 INFO 列 AF 和 DP 进行子集化:
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
有关更多 query 选项,请参阅手册。