多维矩阵查找,如何改进慢速解决方案

时间:2015-03-22 09:56:20

标签: r for-loop

有时我想将几个数据列(通常是字符或因子)转换为一个新列(通常是一个数字)。我尝试使用查找矩阵来做到这一点。例如,我的数据集是

dset <- data.frame(
   x=c("a", "a", "b"),
   y=c("v", "w", "w"),
   stringsAsFactors=FALSE
)
lookup <- matrix(c(1:4), ncol=2)
rownames(lookup) <- c("a", "b")
colnames(lookup) <- c("v", "w")

理想情况下(为了我的目的),我现在要做

transform(dset, z=lookup[x,y])

并获取我的新数据列。虽然这在一维情况下起作用,但这在此失败,因为lookup[x,y]返回一个矩阵。我想出了这个看起来很慢的功能:

fill_from_matrix <- function(m, ...) {
  arg <- list(...)
  len <- sapply(arg, length)
  if(sum(diff(len))!=0) stop("differing lengths in fill_from_matrix")
  if(length(arg)!=length(dim(m))) stop("differing dimensions in fill_from_matrix")
  n <- len[[1]]
  dims <- length(dim(m))
  res <- rep(NA, n)
  for (i in seq(1,n)) {
    one_arg <- list(m)
    for (j in seq(1,dims)) one_arg[[j+1]] <- arg[[j]][[i]]
    res[i] <- do.call("[", one_arg)
  }
  return(res)
}

使用此功能,我可以调用transform并获得我想要的结果:

transform(dset, z=fill_from_matrix(lookup,x,y))
#   x y z
# 1 a v 1
# 2 a w 3
# 3 b w 4

但是,我对代码不满意,并想知道是否有更优雅(和更快)的方式来执行这种转换。我如何摆脱for循环?

2 个答案:

答案 0 :(得分:1)

您可以将dplyr库用于inner_join,并使用data.frame代替matrix作为查找表:

library(dplyr)

lookup = transform(expand.grid(c('a','b'),c('v','w')), v=1:4) %>%
           setNames(c('x','y','val'))

inner_join(dset, lookup, by=c('x','y'))

#  x y val
#1 a v   1
#2 a w   3
#3 b w   4

快速的方法也是使用data.table包,我的lookup定义:

library(data.table)

setDT(lookup)
setDT(dset)

setkey(lookup, x ,y)[dset]

#   x y val
#1: a v   1
#2: a w   3
#3: b w   4

如果出于任何原因您将矩阵lookup作为输入,请将其转换为dataframe

lookup = transform(expand.grid(rownames(lookup), colnames(lookup)), v=c(lookup))
names(lookup) = c('x','y','val')

答案 1 :(得分:1)

这非常简单,我怀疑使用基本R索引很快,因为“[”函数为了这个目的接受了一个双列矩阵:

> dset$z <- lookup[ with(dset, cbind(x,y)) ]
> dset
  x y z
1 a v 1
2 a w 3
3 b w 4

如果您需要它作为特定功能,那么:

lkup <- function(tbl, rowidx, colidx){ tbl[ cbind(rowidx, colidx)]}
zvals <- lkup(lookup, dset$x, dset$y)
zvals
#[1] 1 3 4

(如果您有这些尺寸的数组,我很确定您也可以使用三列和四列矩阵。)