Select 最小化 R 中标准的列子集
Select subset of columns which minimise a criterion in R
我有一个稀疏二进制文件 data.frame
看起来像这样
set.seed(123)
dat <- as.data.frame(matrix(rep(round(runif(40,0,0.9),0),5),ncol = 20))
# > dat
# V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20
# 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
# 2 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
# 3 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
# 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 5 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0
# 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 7 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0
# 8 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1
# 9 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0
# 10 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
我需要找到 3 列,当我在这些列上调用 rowSums
时,它们使获得的零数最少。
示例:
# > rowSums(dat[,1:3])
# [1] 2 2 2 3 2 2 0 2 0 1
#
# > rowSums(dat[,2:4])
# [1] 3 2 3 3 1 2 1 1 0 1
在这里,当我在前 3 列上调用 rowSums
时,我得到 2 个零,而当我在列 2:4
上调用 rowSums
时,我只得到一个 0
,所以第二种解决方案是首选。
当然,当我应用 rowSums
时,我不需要这些列彼此相邻,所以我需要探索所有可能的组合(例如:我希望 rowSums
还要考虑 ov V1+V5+V17
, ...) 的情况,如果有多个 "optimal" 解决方案,我可以只保留其中一个。
请注意,我的真实 data.frame
是 220.000 行 x 200 列,因此我需要一种在 time/memory 消耗方面的有效方法。
这是最明显的解决方案,尽管可能无法很好地扩展:
which.min(combn(dat,3L,function(x) sum(rowSums(x)==0)));
## [1] 2
2的输出值可以认为是一个组合索引。您可以通过 运行 combn()
在输入对象的完整列索引集上获取属于该组合的列,并索引出该特定索引组合:
cis <- combn(seq_along(dat),3L)[,2L];
cis;
## [1] 1 2 4
然后获取列名很容易:
names(dat)[cis];
## [1] "V1" "V2" "V4"
您可以通过以下方式得到解中零的个数:
sum(rowSums(dat[,cis])==0);
## [1] 1
我用 Rcpp 编写了一个更快的解决方案。
为了使该函数更通用,我将其编写为采用逻辑矩阵而不是 data.frame,旨在找到具有最少全真行的列组合。因此,对于您的情况,您可以将参数计算为 dat==0
。我还将组合中的列数参数化为第二个参数 r
,对于您的情况,这将是 3。
library(Rcpp);
Sys.setenv('PKG_CXXFLAGS'='-std=c++11');
cppFunction('
IntegerVector findColumnComboWithMinimumAllTrue(LogicalMatrix M,int r) {
std::vector<int> rzFull(M.nrow()); std::iota(rzFull.begin(),rzFull.end(),0);
std::vector<int> rzErase;
std::vector<std::vector<int>> rzs(M.ncol(),std::vector<int>(M.nrow()));
std::vector<std::vector<int>*> rzps(M.ncol());
std::vector<int>* rzp = &rzFull;
std::vector<int> com(r);
int bestAllTrueCount = M.nrow()+1;
std::vector<int> bestCom(r);
int pmax0 = M.ncol()-r;
int p = 0;
while (true) {
rzErase.clear();
for (int rzi = 0; rzi < rzp->size(); ++rzi)
if (!M((*rzp)[rzi],com[p])) rzErase.push_back(rzi);
if (p+1==r) {
if (rzp->size()-rzErase.size() < bestAllTrueCount) {
bestAllTrueCount = rzp->size()-rzErase.size();
bestCom = com;
}
if (com[p]==pmax0+p) {
do {
--p;
} while (p >= 0 && com[p]==pmax0+p);
if (p==-1) break;
++com[p];
rzp = p==0 ? &rzFull : rzps[p-1];
} else {
++com[p];
}
} else {
if (rzErase.empty()) {
rzps[p] = rzp;
} else {
rzs[p].clear();
int rzi = -1;
for (int ei = 0; ei < rzErase.size(); ++ei)
for (++rzi; rzi < rzErase[ei]; ++rzi)
rzs[p].push_back((*rzp)[rzi]);
for (++rzi; rzi < rzp->size(); ++rzi)
rzs[p].push_back((*rzp)[rzi]);
rzp = rzps[p] = &rzs[p];
}
++p;
com[p] = com[p-1]+1;
}
}
IntegerVector res(bestCom.size());
for (int i = 0; i < res.size(); ++i)
res[i] = bestCom[i]+1;
return res;
}
');
这是您的示例输入的演示:
set.seed(123L);
dat <- as.data.frame(matrix(rep(round(runif(40,0,0.9),0),5),ncol=20L));
findColumnComboWithMinimumAllTrue(dat==0,3L);
## [1] 1 2 4
这是一个全尺寸测试,在我的系统上需要将近 10 分钟:
set.seed(1L); NR <- 220e3L; NC <- 200L;
dat <- as.data.frame(matrix(sample(0:1,NR*NC,T),NR,NC));
system.time({ findColumnComboWithMinimumAllTrue(dat==0,3L); });
## user system elapsed
## 555.641 0.328 556.401
res;
## [1] 28 64 89
我有一个稀疏二进制文件 data.frame
看起来像这样
set.seed(123)
dat <- as.data.frame(matrix(rep(round(runif(40,0,0.9),0),5),ncol = 20))
# > dat
# V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20
# 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
# 2 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
# 3 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
# 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 5 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0
# 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 7 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0
# 8 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1
# 9 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0
# 10 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
我需要找到 3 列,当我在这些列上调用 rowSums
时,它们使获得的零数最少。
示例:
# > rowSums(dat[,1:3])
# [1] 2 2 2 3 2 2 0 2 0 1
#
# > rowSums(dat[,2:4])
# [1] 3 2 3 3 1 2 1 1 0 1
在这里,当我在前 3 列上调用 rowSums
时,我得到 2 个零,而当我在列 2:4
上调用 rowSums
时,我只得到一个 0
,所以第二种解决方案是首选。
当然,当我应用 rowSums
时,我不需要这些列彼此相邻,所以我需要探索所有可能的组合(例如:我希望 rowSums
还要考虑 ov V1+V5+V17
, ...) 的情况,如果有多个 "optimal" 解决方案,我可以只保留其中一个。
请注意,我的真实 data.frame
是 220.000 行 x 200 列,因此我需要一种在 time/memory 消耗方面的有效方法。
这是最明显的解决方案,尽管可能无法很好地扩展:
which.min(combn(dat,3L,function(x) sum(rowSums(x)==0)));
## [1] 2
2的输出值可以认为是一个组合索引。您可以通过 运行 combn()
在输入对象的完整列索引集上获取属于该组合的列,并索引出该特定索引组合:
cis <- combn(seq_along(dat),3L)[,2L];
cis;
## [1] 1 2 4
然后获取列名很容易:
names(dat)[cis];
## [1] "V1" "V2" "V4"
您可以通过以下方式得到解中零的个数:
sum(rowSums(dat[,cis])==0);
## [1] 1
我用 Rcpp 编写了一个更快的解决方案。
为了使该函数更通用,我将其编写为采用逻辑矩阵而不是 data.frame,旨在找到具有最少全真行的列组合。因此,对于您的情况,您可以将参数计算为 dat==0
。我还将组合中的列数参数化为第二个参数 r
,对于您的情况,这将是 3。
library(Rcpp);
Sys.setenv('PKG_CXXFLAGS'='-std=c++11');
cppFunction('
IntegerVector findColumnComboWithMinimumAllTrue(LogicalMatrix M,int r) {
std::vector<int> rzFull(M.nrow()); std::iota(rzFull.begin(),rzFull.end(),0);
std::vector<int> rzErase;
std::vector<std::vector<int>> rzs(M.ncol(),std::vector<int>(M.nrow()));
std::vector<std::vector<int>*> rzps(M.ncol());
std::vector<int>* rzp = &rzFull;
std::vector<int> com(r);
int bestAllTrueCount = M.nrow()+1;
std::vector<int> bestCom(r);
int pmax0 = M.ncol()-r;
int p = 0;
while (true) {
rzErase.clear();
for (int rzi = 0; rzi < rzp->size(); ++rzi)
if (!M((*rzp)[rzi],com[p])) rzErase.push_back(rzi);
if (p+1==r) {
if (rzp->size()-rzErase.size() < bestAllTrueCount) {
bestAllTrueCount = rzp->size()-rzErase.size();
bestCom = com;
}
if (com[p]==pmax0+p) {
do {
--p;
} while (p >= 0 && com[p]==pmax0+p);
if (p==-1) break;
++com[p];
rzp = p==0 ? &rzFull : rzps[p-1];
} else {
++com[p];
}
} else {
if (rzErase.empty()) {
rzps[p] = rzp;
} else {
rzs[p].clear();
int rzi = -1;
for (int ei = 0; ei < rzErase.size(); ++ei)
for (++rzi; rzi < rzErase[ei]; ++rzi)
rzs[p].push_back((*rzp)[rzi]);
for (++rzi; rzi < rzp->size(); ++rzi)
rzs[p].push_back((*rzp)[rzi]);
rzp = rzps[p] = &rzs[p];
}
++p;
com[p] = com[p-1]+1;
}
}
IntegerVector res(bestCom.size());
for (int i = 0; i < res.size(); ++i)
res[i] = bestCom[i]+1;
return res;
}
');
这是您的示例输入的演示:
set.seed(123L);
dat <- as.data.frame(matrix(rep(round(runif(40,0,0.9),0),5),ncol=20L));
findColumnComboWithMinimumAllTrue(dat==0,3L);
## [1] 1 2 4
这是一个全尺寸测试,在我的系统上需要将近 10 分钟:
set.seed(1L); NR <- 220e3L; NC <- 200L;
dat <- as.data.frame(matrix(sample(0:1,NR*NC,T),NR,NC));
system.time({ findColumnComboWithMinimumAllTrue(dat==0,3L); });
## user system elapsed
## 555.641 0.328 556.401
res;
## [1] 28 64 89