5

我尝试将字符串向量拆分为 data.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吗?恐怕一个元素一个元素地做一个 long vector 需要很长时间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

4 回答 4

4

这是一种糟糕的技术,但有时ept( eval 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
于 2013-11-12T12:03:46.923 回答
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
于 2013-11-12T12:17:33.643 回答
2

效率不高,使用外部包。

  1. 将每一行转换为 data.frame
  2. rbind.fill使用from绑定它们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
于 2013-11-12T11:59:59.590 回答
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))

这实际上与其他两种解决方案非常相似。我只是稍微不同地创建了数据框。

于 2013-11-12T12:28:34.807 回答