有时我想将几个数据列(通常是字符或因子)转换为一个新列(通常是一个数字)。我尝试使用查找矩阵来做到这一点。例如,我的数据集是
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循环?
答案 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
(如果您有这些尺寸的数组,我很确定您也可以使用三列和四列矩阵。)