所以我在R
(可能是数据框),mat1
和mat2
中有两个矩阵,如下所示:
> mat1
A B C
1 A1 B1 C1
2 A2 B1 C1
3 A1 B2 C1
4 A2 B2 C1
5 A1 B1 C2
6 A2 B1 C2
7 A1 B2 C2
8 A2 B2 C2
9 A1 B1 C3
10 A2 B1 C3
11 A1 B2 C3
12 A2 B2 C3
> mat2
B C
1 B1 C1
2 B2 C1
3 B1 C2
4 B2 C2
5 B1 C3
6 B2 C3
我想要做的是弄清楚mat2
的哪些行是mat1
行的子集。例如,mat2
的第1行是B1 C1
,它出现在mat1
的第1行和第2行中,因此输出为1,1。现在,我需要遍历所有12行的mat1
等等,使用上面的矩阵作为我的例子,我想要的输出是一个读取1,1,2,2,3,3,4,4,5,5,6的矢量6。
我能够以循环的方式实现这一目标,但这对我来说并不太有效。我想知道是否有1)内置命令(可能是子集?)和2)如果有更多"高效"或替代方式来做到这一点。
这是我的代码:
mat1 = expand.grid(list(A=c("A1","A2"),B=c("B1","B2"),C=c("C1","C2","C3")),stringsAsFactors=FALSE)
mat2 = expand.grid(list(B=c("B1","B2"),C=c("C1","C2","C3")),stringsAsFactors=FALSE)
N1 = nrow(mat1)
N2 = nrow(mat2)
idx = rep(NA,N1)
for(j in 1:N1){
for(k in 1:N2){
if(sum(mat2[k,]%in%mat1[j,])==length(mat2[k,])){
idx[j] = k
}
}
}
> idx
[1] 1 1 2 2 3 3 4 4 5 5 6 6
答案 0 :(得分:3)
我认为这是一个非常酷的解决方案,虽然它肯定会以速度换掉内存,因此对于非常大的输入可能会出现问题。它调用rep()
重复每个data.frame的行,一个使用(默认)times
参数,一个使用each
参数,因此我们可以获得一个巨大的矢量化比较两个data.frames:
r <- rep(seq_len(nrow(mat2)),nrow(mat1));
r[rowSums(mat2[r,]==mat1[rep(seq_len(nrow(mat1)),each=nrow(mat2)),names(mat2)])==ncol(mat2)];
## [1] 1 1 2 2 3 3 4 4 5 5 6 6
我的第一直觉是使用lapply()
和rep()
sum()
,但可能会有更有效的解决方案:
unlist(lapply(seq_len(nrow(mat2)),function(ri) rep(ri,sum(mat2[ri,1L]==mat1[,2L] & mat2[ri,2L]==mat1[,3L]))))
## [1] 1 1 2 2 3 3 4 4 5 5 6 6
以上是对上述内容的概括。现在它应该能够处理任意数量的列,并将mat2
中的列名称与mat1
匹配:
unlist(lapply(seq_len(nrow(mat2)),function(ri) rep(ri,sum(Reduce(`&`,lapply(names(mat2),function(n) mat2[ri,n]==mat1[,n]))))))
## [1] 1 1 2 2 3 3 4 4 5 5 6 6
library(microbenchmark);
library(data.table);
bgoldst1 <- function(mat1,mat2) { r <- rep(seq_len(nrow(mat2)),nrow(mat1)); r[rowSums(mat2[r,]==mat1[rep(seq_len(nrow(mat1)),each=nrow(mat2)),names(mat2)])==ncol(mat2)]; };
bgoldst2 <- function(mat1,mat2) unlist(lapply(seq_len(nrow(mat2)),function(ri) rep(ri,sum(Reduce(`&`,lapply(names(mat2),function(n) mat2[ri,n]==mat1[,n]))))));
mtoto <- function(mat1,mat2) mat1[mat2[,id := 1 : .N], on = setdiff(names(mat2),'id')]$id; ## setdiff() only necessary to allow reusing the same data.tables
bonhomme <- function(mat1,mat2) match(apply(mat1[, names(mat2)], 1, paste0, collapse=""),apply(mat2, 1, paste0, collapse=""));
## OP's example data
mat1 = expand.grid(list(A=c("A1","A2"),B=c("B1","B2"),C=c("C1","C2","C3")),stringsAsFactors=FALSE);
mat2 = expand.grid(list(B=c("B1","B2"),C=c("C1","C2","C3")),stringsAsFactors=FALSE);
mat1.dt <- as.data.table(mat1);
mat2.dt <- as.data.table(mat2);
ex <- bgoldst1(mat1,mat2);
identical(ex,bgoldst2(mat1,mat2));
## [1] TRUE
identical(ex,mtoto(mat1.dt,mat2.dt));
## [1] TRUE
identical(ex,bonhomme(mat1,mat2));
## [1] TRUE
microbenchmark(bgoldst1(mat1,mat2),bgoldst2(mat1,mat2),mtoto(mat1.dt,mat2.dt),bonhomme(mat1,mat2));
## Unit: microseconds
## expr min lq mean median uq max neval
## bgoldst1(mat1, mat2) 262.579 295.9365 320.3254 308.5525 335.7080 452.884 100
## bgoldst2(mat1, mat2) 292.515 334.6390 358.5704 353.4555 368.2095 542.692 100
## mtoto(mat1.dt, mat2.dt) 936.133 968.4200 1053.2665 1004.1290 1067.2080 2204.978 100
## bonhomme(mat1, mat2) 169.779 204.8465 229.4792 218.7450 245.4735 550.390 100
set.seed(2L);
NC <- 10L; N1 <- 1e4L; N2 <- 1e2L;
ns <- make.unique(rep(LETTERS,len=NC));
mat1 <- setNames(nm=ns,as.data.frame(stringsAsFactors=F,lapply(ns,function(n) paste0(n,sample(1:2,N1,T)))));
repeat { mat2 <- setNames(nm=ns,as.data.frame(stringsAsFactors=F,lapply(ns,function(n) paste0(n,sample(1:2,N2,T))))); if (!anyDuplicated(mat2)) break; }; ## ensure we get unique mat2 because bonhomme's solution depends on it
mat1.dt <- as.data.table(mat1);
mat2.dt <- as.data.table(mat2);
ex <- sort(bgoldst1(mat1,mat2));
identical(ex,sort(bgoldst2(mat1,mat2)));
## [1] TRUE
identical(ex,sort(mtoto(mat1.dt,mat2.dt)));
## [1] TRUE
identical(ex,sort(na.omit(bonhomme(mat1,mat2))));
## [1] TRUE
microbenchmark(bgoldst1(mat1,mat2),bgoldst2(mat1,mat2),mtoto(mat1.dt,mat2.dt),bonhomme(mat1,mat2));
## Unit: milliseconds
## expr min lq mean median uq max neval
## bgoldst1(mat1, mat2) 2911.58409 3124.520068 3327.642060 3343.112398 3482.843856 4032.33696 100
## bgoldst2(mat1, mat2) 512.21419 526.099003 580.715562 567.748314 586.783137 805.94574 100
## mtoto(mat1.dt, mat2.dt) 2.75109 3.124645 3.411087 3.271116 3.407965 12.83215 100
## bonhomme(mat1, mat2) 37.60436 39.928438 54.498556 41.010399 42.958784 213.24218 100
结论:我的解决方案太糟糕了。
答案 1 :(得分:3)
我会用它,但可能有更优雅的方式:
match(apply(mat1[, c("B", "C")], 1, paste0, collapse=""),
apply(mat2[, c("B", "C")], 1, paste0, collapse=""))
[1] 1 1 2 2 3 3 4 4 5 5 6 6
答案 2 :(得分:0)
以下解决方案不使用列名称。它应该适用于所有情况。它需要矩阵,而不是数据帧。
mat1 = as.matrix(expand.grid(list(A=c("A1","A2"),B=c("B1","B2"),C=c("C1","C2","C3")),stringsAsFactors=FALSE))
mat2 = as.matrix(expand.grid(list(B=c("B1","B2"),C=c("C1","C2","C3")),stringsAsFactors=FALSE))
match.rows = outer(seq(nrow(mat2)), seq(nrow(mat1)), Vectorize(function(x,y)length(setdiff(mat2[x,],mat1[y,]))==0))
apply(match.rows,2,function(x)ifelse(any(x),which(x)[1],0))