2016-01-13 73 views
4

我有这种风格的字符向量的:高效分割一个字符向量

vec <- c("id a; sex m; age 16; type 1;","id a; sex m; age 16;","id a; sex m; age 16; type 3") 

vec每个元素是一个“;”分隔的属性的列表,其中所述的每个属性具有“键值”格式(在“;”字符只能出现在分隔符中)。

所以属性的第一列表是: ID =一个 性别=米 年龄= 16 类型= 1

注意,在vec不同元件可以具有略微不同的属性。

我正在寻找一种有效的方式将vec拆分为列表。外部列表中的每个元素都是所有属性值的列表,其中元素名称是属性键。这意味着外部列表的长度将是vec元素的长度,每个内部列表的长度将是属性的长度。

我现在有这个执行,这有助于理解输出我需要:

attributes.list <- sapply(vec, function(x) strsplit(x, split = "(\\;)(\\s+)?", perl = TRUE)[[1]]) 
attributes.lol <- lapply(attributes.list, function(x) { 
    attribute.mat <- sapply(x, function(y) strsplit(y, split = " ")[[1]]) 
    colnames(attribute.mat) <- NULL 
    attribute.list <- as.list(attribute.mat[2,]) 
    names(attribute.list) <- attribute.mat[1,] 
    return(attribute.list) 
}) 

> attributes.lol[[1]] 
$id 
[1] "a" 

$sex 
[1] "m" 

$age 
[1] "16" 

$type 
[1] "1" 

vec现实中的长度很长(〜百万元),所以我在想,如果有一个更有效的方法为了达成这个。

+2

都不能值*包含*';'(例如,作为一个字符串的一部分,或转义)? –

+1

哪部分代码是瓶颈? –

+0

Konrad Rudolph的问题的答案是否定的。 “;”只能显示为分隔符。 – user1701545

回答

3

以下仅使用base R.在每条记录上追加一个分号,以分号拆分记录,删除前导和尾随空格,用冒号和空格替换空格,并使用read.dcf读入。这给出了一个矩阵m,我们将其转换为数据框并使用type.convert来获取正确的类型。 (如果矩阵是足够然后省略第二行)。

m <- read.dcf(textConnection(sub(" ",": ",trimws(unlist(strsplit(paste0(vec, ";"),";")))))) 
as.data.frame(lapply(as.data.frame(m, stringsAsFactors = FALSE), type.convert)) 

,并提供:

id sex age type 
1 a m 16 1 
2 a m 16 NA 
3 a m 16 3 
1

即使这不会带给你不一样的输出,你可以尝试替换“;”如下:

require(data.table) 
l <- lapply(vec, function(x){ 
    fread(gsub(";", "\n", x)) 
}) 

它给你一个列表,然后您可以通过

rbindlist(l, idcol = TRUE) 

这导致合并:

.id id a 
1: 1 sex m 
2: 1 age 16 
3: 1 type 1 
4: 2 sex m 
5: 2 age 16 
6: 3 sex m 
7: 3 age 16 
8: 3 type 3 
+0

调用'fread'一百万次不是一定会很快,是吗? – A5C1D2H2I1M1N2O1R2T1

2

你可以试试这个方法,这是符合@alexis_laz意见建议:

设置:

vec <- c("id a; sex m; age 16; type 1;","id a; sex m; age 16;","id a; sex m; age 16; type 3") 

v <- rep(vec,1e5) 

代码:

z <- strsplit(v, split = "(\\;)(\\s+)?", perl = TRUE) 

out <- lapply(z,function(s) {v <- unlist(strsplit(s,' ')); setNames(as.list(v[c(F,T)]),v[c(T,F)]) }) 
4

我建议 “iotools” 和“数据的组合。表”,沿此线的东西:

library(iotools) 
library(data.table) 
melt(data.table(ind = seq_along(vec), trimws(mstrsplit(vec, ";"))), 
    "ind", na.rm = TRUE)[ 
     , c("key", "val") := tstrsplit(value, " ", TRUE)][ 
     , c("variable", "value") := NULL][] 

或者,如果你想要一个 “广” 的形式(如@ GGrothendieck的答案):

dcast(
    melt(data.table(ind = seq_along(vec), trimws(mstrsplit(vec, ";"))), 
     "ind", na.rm = TRUE)[ 
     , c("key", "val") := tstrsplit(value, " ", TRUE)][ 
      , c("variable", "value") := NULL][], ind ~ key, value.var = "val") 

我建议上面,因为你比较以下内容:

样本数据长度3,大约100000,大约100万。

vec <- c("id a; sex m; age 16; type 1;","id a; sex m; age 16;","id a; sex m; age 16; type 3") 
v100k <- rep(vec, ceiling(100000/length(vec))) 
v1M <- rep(vec, ceiling(1000000/length(vec))) 

我们要测试的方法:

library(iotools) 
library(data.table) 

funAM_l <- function(invec) { 
    melt(data.table(ind = seq_along(invec), trimws(mstrsplit(invec, ";"))), "ind", na.rm = TRUE)[ 
    , c("key", "val") := tstrsplit(value, " ", TRUE)][ 
     , c("variable", "value") := NULL][] 
} 

funAM_w <- function(invec) dcast(funAM_l(invec), ind ~ key, value.var = "val") 

funMT <- function(v) { 
    z <- strsplit(v, split = "(\\;)(\\s+)?", perl = TRUE) 
    lapply(z,function(s) {v <- unlist(strsplit(s,' ')); setNames(as.list(v[c(F,T)]),v[c(T,F)]) }) 
} 

funF <- function(invec) rbindlist(lapply(invec, function(x) { fread(gsub(";", "\n", x)) }), idcol = TRUE) 

funGG <- function(invec) read.dcf(textConnection(sub(" ",": ",trimws(unlist(strsplit(paste0(invec, ";"),";")))))) 

我的建议是不会赢得任何比赛的小载体:

library(microbenchmark) 
microbenchmark(funAM_l(vec), funAM_w(vec), funF(vec), funGG(vec), funMT(vec)) 
# Unit: microseconds 
#   expr  min  lq  mean median  uq  max neval 
# funAM_l(vec) 1474.163 1525.3765 1614.28414 1573.6325 1601.3815 2828.481 100 
# funAM_w(vec) 3293.376 3482.9510 3741.30381 3553.7240 3714.1730 6787.863 100 
#  funF(vec) 690.761 729.4900 830.61645 756.4610 777.6725 4083.904 100 
# funGG(vec) 182.281 209.8405 220.46376 220.8055 232.1820 280.788 100 
# funMT(vec) 57.288 76.5225 84.81496 83.2755 90.3120 166.352 100 

但看看会发生什么,当我们扩展向量:

system.time(funAM_l(v100k)) 
# user system elapsed 
# 0.24 0.00 0.24 
system.time(funAM_w(v100k)) 
# user system elapsed 
# 0.296 0.000 0.296 
system.time(funMT(v100k)) 
# user system elapsed 
# 1.768 0.000 1.768 
system.time(funF(v100k)) 
# user system elapsed 
# 21.960 0.136 22.068 
system.time(funGG(v100k)) 
# user system elapsed 
# 30.968 0.004 30.940 

下面是它在100万长度的向量上的表现。

system.time(funAM_w(v1M)) 
# user system elapsed 
# 4.316 0.092 4.402 

我的另一个建议将是看cSplit从我的“splitstackshape”包。这比@Marat的方法要好一点。

这是百万值:

library(splitstackshape) 
system.time(dcast(
    cSplit(cSplit(data.table(ind = seq_along(v1M), v1M), "v1M", ";", "long"), "v1M", " "), 
    ind ~ v1M_1, value.var = "v1M_2")) 
# user system elapsed 
# 13.744 0.156 13.882