如何根据包含键值对的数据框的值(例如ensembl_end_phase=-1
)有效地派生新变量?
变量的名称应该是键,内容应该是值。我寻找像tidyr::separate
这样的东西,其中into: Names of new variables to create as character vector
向量是根据观察到的密钥动态创建的。
我想为遗传分析中使用的GTF/GFF3 gene files提供一个整洁的R
数据框。
以下是此类文件(制表符分隔符)的示例:
seqname source feature start end score strand frame attribute
1 havana three 191356960 191356995 . - . Parent=transcript:ENSMUST00000191925
1 havana exon 191357792 191357851 . - . Parent=transcript:ENSMUST00000191925;Name=ENSMUSE00001336486;constitutive=0;ensembl_end_phase=-1;ensembl_phase=-1;exon_id=ENSMUSE00001336486;rank=4;version=1
1 havana three 191357792 191357851 . - . Parent=transcript:ENSMUST00000191925
1 havana three 191358925 191359076 . - . Parent=transcript:ENSMUST00000191925
15 ensembl CDS__ 98186839 98187790 . - 1 ID=CDS:ENSMUSP00000132237;Parent=transcript:ENSMUST00000165379;protein_id=ENSMUSP00000132237
由于这些文件很容易包含数百万行,因此有效的方法至关重要。
使用read_delim
包中的readr
可以有效地读取文件。现在,我很难根据attribute
列中的键值对创建变量。
我通常的方法是应用tidyr::separate
这样:
annotation %>% separate(attribute, into, sep = ";")
这会失败,因为行之间的键值对不同。因此,必须为每一行动态创建 into 向量。由于密钥在不同的GTF / GFF文件源之间也没有标准化,因此在处理密钥名称之前无法知道密钥名称。
我想做tidyr::separate
之类的事情,但是使用从值的第一个单词(键)派生的变量名,这是通过将“em>属性分离在”;“。< / p>
你能否告诉我如何做到这一点?使用for循环单独解析所有值 way 太慢。
答案 0 :(得分:1)
你可以尝试
library(tidyr)
lst <- lapply(strsplit(df$attribute, ";", T), strsplit, "=", T)
df$att <- lapply(lst, function(x) setNames(do.call(rbind.data.frame, x), c("key", "value")) )
df <- unnest(df)
str(df)
# 'data.frame': 14 obs. of 11 variables:
# $ seqname : int 1 1 1 1 1 1 1 1 1 1 ...
# $ source : chr "havana" "havana" "havana" "havana" ...
# $ feature : chr "three" "exon" "exon" "exon" ...
# $ start : int 191356960 191357792 191357792 191357792 191357792 191357792 191357792 191357792 191357792 191357792 ...
# $ end : int 191356995 191357851 191357851 191357851 191357851 191357851 191357851 191357851 191357851 191357851 ...
# $ score : chr "." "." "." "." ...
# $ strand : chr "-" "-" "-" "-" ...
# $ frame : chr "." "." "." "." ...
# $ attribute: chr "Parent=transcript:ENSMUST00000191925" "Parent=transcript:ENSMUST00000191925;Name=ENSMUSE00001336486;constitutive=0;ensembl_end_phase=-1;ensembl_phase=-1;exon_id=ENSMU"| __truncated__ "Parent=transcript:ENSMUST00000191925;Name=ENSMUSE00001336486;constitutive=0;ensembl_end_phase=-1;ensembl_phase=-1;exon_id=ENSMU"| __truncated__ "Parent=transcript:ENSMUST00000191925;Name=ENSMUSE00001336486;constitutive=0;ensembl_end_phase=-1;ensembl_phase=-1;exon_id=ENSMU"| __truncated__ ...
# $ key : chr "Parent" "Parent" "Name" "constitutive" ...
# $ value : chr "transcript:ENSMUST00000191925" "transcript:ENSMUST00000191925" "ENSMUSE00001336486" "0" ...
答案 1 :(得分:1)
您可以使用splitstackshape
包,
library(splitstackshape)
fun1 <- function(x){
dd1 <- cSplit(x, 'attribute', sep = ';', 'long')
dd2 <- cSplit(dd1, 'attribute', sep = '=', 'wide', drop = FALSE)
return(dd2)
}
fun1(df)
比较system.time
,
df1 <- df[rep(row.names(df), 100000),]
fun_lukeA <- function(x, var) {
lst <- lapply(strsplit(var, ";", T), strsplit, "=", T)
x$att <- lapply(lst, function(x) setNames(do.call(rbind.data.frame, x), c("key", "value")))
unnest(x)
}
system.time(fun_lukeA(df1, df1$attribute))
# user system elapsed
# 296.89 0.36 298.15
system.time(fun1(df1))
# user system elapsed
# 9.16 0.00 9.20