strsplit到data.frame与不完整的输入

时间:2013-11-12 11:23:48

标签: string r dataframe

我尝试将字符串向量拆分为data.frame对象,对于固定顺序,这不是问题(例如写成here),但在我的特定情况下,未来数据的列-frame在字符串对象中不完整。这就是玩具输入的输出结果:

input <- c("an=1;bn=3;cn=45",
           "bn=3.5;cn=76",
           "an=2;dn=5")

res <- do.something(input)

> res
      an  bn  cn  dn
[1,]  1   3   45  NA
[2,]  NA  3.5 76  NA
[3,]  2   NA  NA  5

我现在正在寻找能够以有效方式实现这一目标的函数do.something。我现在的天真解决方案是循环输入对象strsplit ;然后strsplit再次=然后填充data.frame结果的结果。 有没有办法做更多R-like类似的?我担心按元素执行元素会花费很长时间来处理长向量input

编辑:为了完整起见,我天真的解决方案看起来像这样:

  do.something <- function(x){
    temp <- strsplit(x,";")
    temp2 <- sapply(temp,strsplit,"=")
    ul.temp2 <- unlist(temp2)
    label <- sort(unique(ul.temp2[seq(1,length(ul.temp2),2)]))
    res <- data.frame(matrix(NA, nrow = length(x), ncol = length(label)))
    colnames(res) <- label
    for(i in 1:length(temp)){
      for(j in 1:length(label)){
        curInfo <- unlist(temp2[[i]])
        if(sum(is.element(curInfo,label[j]))>0){
          res[i,j] <- curInfo[which(curInfo==label[j])+1]
        }
      }
    }
    res
  }

EDIT2: 不幸的是,我的大输入数据看起来像这样(没有'='的条目):

input <- c("an=1;bn=3;cn=45",
           "an;bn=3.5;cn=76",
           "an=2;dn=5")

所以我无法比较我手头的问题给出的答案。我天真的解决方案是

do.something <- function(x){
    temp <- strsplit(x,";")
    tempNames <- sort(unique(sapply(strsplit(unlist(temp),"="),"[",1)))
    res <- data.frame(matrix(NA, nrow = length(x), ncol = length(tempNames)))
    colnames(res) <- tempNames

    for(i in 1:length(temp)){
      curSplit <- strsplit(unlist(temp[[i]]),"=")
      curNames <- sapply(curSplit,"[",1)
      curValues <- sapply(curSplit,"[",2)
      for(j in 1:length(tempNames)){
        if(is.element(colnames(res)[j],curNames)){
          res[i,j] <- curValues[curNames==colnames(res)[j]]
        }
      }
    }
    res
  }

4 个答案:

答案 0 :(得分:4)

这是一种糟糕的技术,但有时候epteval parse text)很有用。

> library(plyr)
> rbind.fill(lapply(input, function(x) {l <- new.env(); eval(parse(text = x), envir=l); as.data.frame(as.list(l))}))
  an cn  bn dn
1  1 45 3.0 NA
2 NA 76 3.5 NA
3  2 NA  NA  5

<强>更新

> z <- lapply(strsplit(input, ";"), 
+             function(x) {
+               e <- Filter(function(y) length(y)==2, strsplit(x, "="))
+               r <- data.frame(lapply(e, `[`, 2))
+               names(r) <- lapply(e, `[`, 1)
+               r
+             })
> rbind.fill(z)
    an   bn   cn   dn
1    1    3   45 <NA>
2 <NA>  3.5   76 <NA>
3    2 <NA> <NA>    5

答案 1 :(得分:4)

这是另一种方法,即使您已编辑的数据也可以使用。使用regmatches从输入向量中提取列名和值,然后运行与值匹配的每个列表元素到相应的列名。

#  Get column names
tag <- regmatches( input , gregexpr( "[a-z]+" , input ) )

#  Get numbers including floating point, replace missing values with NA
val <- regmatches( input , gregexpr( "\\d+\\.?\\d?|(?<=[a-z]);" , input , perl = TRUE ) )
val <- lapply( val , gsub , pattern = ";" , replacement = NA )

#  Column names
nms <- unique( unlist(tag) )

#  Intermeidate matrices
ll <- mapply( cbind , val , tag )

#  Match to appropriate columns and coerce to data.frame
out <- data.frame( do.call( rbind , lapply( ll , function(x) x[ match( nms , x[,2] ) ]  ) ) )
names(out) <- nms
#    an   bn   cn   dn
#1    1    3   45 <NA>
#2 <NA>  3.5   76 <NA>
#3    2 <NA> <NA>    5

答案 2 :(得分:2)

效率不高,并使用外部包。

  1. 将每一行转换为data.frame
  2. 使用rbind.fill
  3. 中的plyr对其进行绑定

    这是我的代码:

    ll <- lapply(input,function(x){
            xx <- unlist(strsplit(x,";"))
            nn <- sub('([a-z]+)[=].*','\\1',xx)
            vv <- sub('([a-z]+)[=]([0-9]+([.][0-9]+)?)','\\2',xx)
            m <- t(data.frame(vv))
            colnames(m) <- nn
            as.data.frame(m)
    })
    
    library(plyr)
    rbind.fill(ll)
    
    rbind.fill(ll)
        an   bn   cn   dn
    1    1    3   45 <NA>
    2 <NA>  3.5   76 <NA>
    3    2 <NA> <NA>    5
    

答案 3 :(得分:1)

rbind.fill主题的另一个变体:

library(plyr)

mini.df <- function(x) {
  y <- do.call(cbind,strsplit(x,"="))
  z <- as.numeric(y[2,])
  names(z) <- y[1,]
  return(as.data.frame(t(z)))
}
res <- rbind.fill(lapply(strsplit(input,";"),mini.df))

这实际上与其他两种解决方案非常相似。我刚刚创建的数据帧略有不同。