首页
学习
活动
专区
圈层
工具
发布

非NA
EN

Stack Overflow用户
提问于 2016-04-06 23:18:15
回答 2查看 763关注 0票数 12

我有一个矩阵,其中每一行至少有一个NA单元格,每一列至少有一个NA单元格。我需要的是找到这个矩阵的最大子集,它不包含NAs。

例如,对于这个矩阵A

代码语言:javascript
复制
A <- 
  structure(c(NA, NA, NA, NA, 2L, NA,
              1L, 1L, 1L, 0L, NA, NA,
              1L, 8L, NA, 1L, 1L, NA, 
              NA, 1L, 1L, 6L, 1L, 3L, 
              NA, 1L, 5L, 1L, 1L, NA),
            .Dim = c(6L, 5L),
            .Dimnames = 
              list(paste0("R", 1:6),
                   paste0("C", 1:5)))

A
    C1  C2  C3  C4  C5
R1  NA  1   1   NA  NA
R2  NA  1   8   1   1
R3  NA  1   NA  1   5
R4  NA  0   1   6   1
R5  2   NA  1   1   1
R6  NA  NA  NA  3   NA

有两个解决方案(8个单元):A[c(2, 4), 2:5]A[2:5, 4:5],但仅找到一个有效的解决方案就足够了。我的实际矩阵的尺寸是77x132。

作为一个菜鸟,我看不出有什么明显的方法可以做到这一点。有人能帮我提点主意吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-07 00:56:23

1) optim在该方法中,我们将问题放宽为一个用optim求解的连续优化问题。

目标函数是f,它的输入是0-1向量,其第一个nrow(A)条目对应行,其余条目对应列。f使用一个矩阵Ainf,该矩阵由A导出,用大负数替换NAs,用1代替非NAs。在Ainf方面,与x对应的矩形行和列中元素数的负数为-x[seq(6)] %*% Ainf %*$ x[-seq(6)],作为x的函数,这是x的一个函数,每个元素位于0到1之间。

尽管这是对原问题进行连续优化的一种放松,但我们似乎得到了一个整数解,正如我们所期望的那样。

实际上,下面的大部分代码只是为了获得起始值。为此,我们首先应用序列化。这就改变了列和行,给出了一个更大的块状结构,然后在置换矩阵中找到了最大的平方子矩阵。

对于问题中的特定A,最大的矩形子矩阵恰好是平方的,并且初始值已经足够好,从而产生了最优结果,但是无论如何,我们都会进行优化,所以它通常是有效的。如果你愿意的话,你可以玩不同的起跑线。例如,将klargestSquare中的1改为更高的数字,在这种情况下,largestSquare将返回k列,给出k的起始值,这些值可以用于以最佳方式运行optimk运行。

如果起始值足够好,那么就会产生最佳值。

代码语言:javascript
复制
library(seriation) # only used for starting values

A.na <- is.na(A) + 0

Ainf <- ifelse(A.na, -prod(dim(A)), 1) # used by f
nr <- nrow(A) # used by f
f <- function(x) - c(x[seq(nr)] %*% Ainf %*% x[-seq(nr)])

# starting values

# Input is a square matrix of zeros and ones.
# Output is a matrix with k columns such that first column defines the
# largest square submatrix of ones, second defines next largest and so on.
# Based on algorithm given here:
# http://www.geeksforgeeks.org/maximum-size-sub-matrix-with-all-1s-in-a-binary-matrix/
largestSquare <- function(M, k = 1) {
  nr <- nrow(M); nc <- ncol(M)
  S <- 0*M; S[1, ] <- M[1, ]; S[, 1] <- M[, 1]
  for(i in 2:nr) 
    for(j in 2:nc)
      if (M[i, j] == 1) S[i, j] = min(S[i, j-1], S[i-1, j], S[i-1, j-1]) + 1
  o <- head(order(-S), k)
  d <- data.frame(row = row(M)[o], col = col(M)[o], mx = S[o])
  apply(d, 1, function(x) { 
    dn <- dimnames(M[x[1] - 1:x[3] + 1, x[2] - 1:x[3] + 1])
    out <- c(rownames(M) %in% dn[[1]], colnames(M) %in% dn[[2]]) + 0
    setNames(out, unlist(dimnames(M)))
  })
}
s <- seriate(A.na)
p <- permute(A.na, s)
# calcualte largest square submatrix in p of zeros rearranging to be in A's  order
st <- largestSquare(1-p)[unlist(dimnames(A)), 1]

res <- optim(st, f, lower = 0*st, upper = st^0, method = "L-BFGS-B")

给予:

代码语言:javascript
复制
> res
$par
R1 R2 R3 R4 R5 R6 C1 C2 C3 C4 C5 
 0  1  1  1  0  0  0  1  0  1  1 

$value
[1] -9

$counts
function gradient 
       1        1 

$convergence
[1] 0

$message
[1] "CONVERGENCE: NORM OF PROJECTED GRADIENT <= PGTOL"

2) GenSA,另一种可能是重复(1),但不要使用optim,而是使用GenSA包中的GenSA。它不需要启动值(尽管您可以使用par参数提供起始值,这在某些情况下可能会改进解决方案),因此代码要短得多,但是由于它使用模拟退火,因此运行时间可能会大大延长。从(1)开始使用f (以及f使用的nrAinf )。下面我们在没有起始值的情况下尝试它。

代码语言:javascript
复制
library(GenSA)
resSA <- GenSA(lower = rep(0, sum(dim(A))), upper = rep(1, sum(dim(A))), fn = f)

给予:

代码语言:javascript
复制
> setNames(resSA$par, unlist(dimnames(A)))
R1 R2 R3 R4 R5 R6 C1 C2 C3 C4 C5 
 0  1  1  1  0  0  0  1  0  1  1 

> resSA$value
[1] -9
票数 7
EN

Stack Overflow用户

发布于 2016-04-07 00:20:21

我有一个解决方案,但规模不太大:

代码语言:javascript
复制
findBiggestSubmatrixNonContiguous <- function(A) {
    A <- !is.na(A); ## don't care about non-NAs
    howmany <- expand.grid(nr=seq_len(nrow(A)),nc=seq_len(ncol(A)));
    howmany <- howmany[order(apply(howmany,1L,prod),decreasing=T),];
    for (ri in seq_len(nrow(howmany))) {
        nr <- howmany$nr[ri];
        nc <- howmany$nc[ri];
        rcom <- combn(nrow(A),nr);
        ccom <- combn(ncol(A),nc);
        comcom <- expand.grid(ri=seq_len(ncol(rcom)),ci=seq_len(ncol(ccom)));
        for (comi in seq_len(nrow(comcom)))
            if (all(A[rcom[,comcom$ri[comi]],ccom[,comcom$ci[comi]]]))
                return(list(ri=rcom[,comcom$ri[comi]],ci=ccom[,comcom$ci[comi]]));
    }; ## end for
    NULL;
}; ## end findBiggestSubmatrixNonContiguous()

这是基于这样的想法,如果矩阵有足够小的NAs密度,那么首先搜索最大的子矩阵,您很可能很快就会找到一个解决方案。

该算法通过计算所有行数和列计数的笛卡尔乘积,这些列可以从原始矩阵中索引以生成子矩阵。然后,根据每对计数产生的子矩阵的大小,对计数集进行递减排序;换句话说,按这两个计数的乘积排序。然后对这些对进行迭代。对于每一对计数,它计算行索引和列索引的所有组合,然后依次尝试每个组合,直到找到包含零NAs的子矩阵为止。在找到这样一个子矩阵时,它会将这组行和列索引作为一个列表返回。

该结果保证是正确的,因为它以递减的顺序尝试子矩阵的大小,因此它找到的第一个子矩阵必须是满足条件的最大(或与最大的)子矩阵相关联。

代码语言:javascript
复制
## OP's example matrix
A <- data.frame(C1=c(NA,NA,NA,NA,2L,NA),C2=c(1L,1L,1L,0L,NA,NA),C3=c(1L,8L,NA,1L,1L,NA),C4=c(NA,1L,1L,6L,1L,3L),C5=c(NA,1L,5L,1L,1L,NA),row.names=c('R1','R2','R3','R4','R5','R6'));
A;
##    C1 C2 C3 C4 C5
## R1 NA  1  1 NA NA
## R2 NA  1  8  1  1
## R3 NA  1 NA  1  5
## R4 NA  0  1  6  1
## R5  2 NA  1  1  1
## R6 NA NA NA  3 NA
system.time({ res <- findBiggestSubmatrixNonContiguous(A); });
##    user  system elapsed
##   0.094   0.000   0.100
res;
## $ri
## [1] 2 3 4
##
## $ci
## [1] 2 4 5
##
A[res$ri,res$ci];
##    C2 C4 C5
## R2  1  1  1
## R3  1  1  5
## R4  0  6  1

我们看到该函数在OP的示例矩阵上工作得非常快,并返回正确的结果。

代码语言:javascript
复制
randTest <- function(NR,NC,probNA,seed=1L) {
    set.seed(seed);
    A <- replicate(NC,sample(c(NA,0:9),NR,prob=c(probNA,rep((1-probNA)/10,10L)),replace=T));
    print(A);
    print(system.time({ res <- findBiggestSubmatrixNonContiguous(A); }));
    print(res);
    print(A[res$ri,res$ci,drop=F]);
    invisible(res);
}; ## end randTest()

我编写了上面的函数来简化测试。我们可以调用它来测试大小为NR的随机输入矩阵的NC,在probNA的任何给定单元中选择NA的概率。

下面是一些简单的测试:

代码语言:javascript
复制
randTest(8L,1L,1/3);
##      [,1]
## [1,]   NA
## [2,]    1
## [3,]    4
## [4,]    9
## [5,]   NA
## [6,]    9
## [7,]    0
## [8,]    5
##    user  system elapsed
##   0.016   0.000   0.003
## $ri
## [1] 2 3 4 6 7 8
##
## $ci
## [1] 1
##
##      [,1]
## [1,]    1
## [2,]    4
## [3,]    9
## [4,]    9
## [5,]    0
## [6,]    5
代码语言:javascript
复制
randTest(11L,3L,4/5);
##       [,1] [,2] [,3]
##  [1,]   NA   NA   NA
##  [2,]   NA   NA   NA
##  [3,]   NA   NA   NA
##  [4,]    2   NA   NA
##  [5,]   NA   NA   NA
##  [6,]    5   NA   NA
##  [7,]    8    0    4
##  [8,]   NA   NA   NA
##  [9,]   NA   NA   NA
## [10,]   NA    7   NA
## [11,]   NA   NA   NA
##    user  system elapsed
##   0.297   0.000   0.300
## $ri
## [1] 4 6 7
##
## $ci
## [1] 1
##
##      [,1]
## [1,]    2
## [2,]    5
## [3,]    8
代码语言:javascript
复制
randTest(10L,10L,1/3);
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]   NA   NA    0    3    8    3    9    1    6    NA
##  [2,]    1   NA   NA    4    5    8   NA    8    2    NA
##  [3,]    4    2    5    3    7    6    6    1    1     5
##  [4,]    9    1   NA   NA    4   NA   NA    1   NA     9
##  [5,]   NA    7   NA    8    3   NA    5    3    7     7
##  [6,]    9    3    1    2    7   NA   NA    9   NA     7
##  [7,]    0    2   NA    7   NA   NA    3    8    2     6
##  [8,]    5    0    1   NA    3    3    7    1   NA     6
##  [9,]    5    1    9    2    2    5   NA    7   NA     8
## [10,]   NA    7    1    6    2    6    9    0   NA     5
##    user  system elapsed
##   8.985   0.000   8.979
## $ri
## [1]  3  4  5  6  8  9 10
##
## $ci
## [1]  2  5  8 10
##
##      [,1] [,2] [,3] [,4]
## [1,]    2    7    1    5
## [2,]    1    4    1    9
## [3,]    7    3    3    7
## [4,]    3    7    9    7
## [5,]    0    3    1    6
## [6,]    1    2    7    8
## [7,]    7    2    0    5

我不知道一个简单的方法来验证上面的结果是否正确,但在我看来很好。但是产生这个结果几乎花了9秒的时间。在中等大的矩阵上运行函数,特别是77x132矩阵,可能是一个失败的原因。

等着看是否有人能想出一个很好的解决方案..。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36463996

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档