【问题标题】:Trying to find the number of diagonals of 1 in each 3x3 tile in a 15x15 binary matrix?试图在 15x15 二进制矩阵中找到每个 3x3 瓦片中 1 的对角线数?
【发布时间】:2019-10-27 15:37:38
【问题描述】:

我正在尝试在每个 3x3 瓷砖中找到对角线 1 的计数,例如

0 0 1         1 0 0
0 1 0         0 1 0
1 0 0    or   0 0 1

来自下面的 15x15 矩阵。

set.seed(99)
mat <- matrix(sample(c(0,1), 225, prob=c(0.8,0.2), replace=TRUE), nrow=15)
print(mat)

    [,1][,2][,3][,4][,5][,6][,7][,8][,9][,10][,11][,12][,13][,14][,15]
[1,]  0   0   1   0   0   0   0   0   0    0    0    0    0    0   0
[2,]  0   1   0   1   0   0   1   0   0    0    1    0    0    0   1
[3,]  0   0   0   1   0   0   0   0   1    0    0    1    0    0   0
[4,]  0   0   0   0   0   0   0   1   1    0    0    0    0    0   1
[5,]  0   0   0   0   1   0   0   1   1    1    0    0    0    0   0
[6,]  0   0   0   0   0   0   1   0   0    0    0    0    1    0   0
[7,]  0   0   0   0   0   0   0   0   0    0    0    0    0    0   0
[8,]  0   0   0   0   0   0   0   1   0    1    0    0    0    0   0
[9,]  0   0   0   0   0   1   0   0   1    1    0    0    1    0   1
[10,] 0   0   0   0   0   0   0   0   1    0    1    1    0    1   0
[11,] 0   0   0   0   0   0   1   0   0    1    0    1    0    0   0
[12,] 0   0   0   0   0   0   1   0   0    1    0    0    0    0   0
[13,] 0   0   0   0   0   1   0   1   0    0    1    0    1    0   0
[14,] 1   1   0   1   1   0   0   0   0    1    0    0    0    0   1
[15,] 1   0   1   0   1   1   0   0   0    1    0    1    0    0   0

我希望上述矩阵的输出为 2。有没有办法用 for 循环和 if 语句来做到这一点?

【问题讨论】:

  • 3x3 的图块可以重叠吗?
  • @jay.sf 是的,瓷砖可以重叠 :)
  • 您要匹配身份 3x3s 吗?或者一个全为 1 的 3x3 矩阵是否也符合您的标准?

标签: r matrix tile


【解决方案1】:

我们可以使用outer()。为此,我们编写了两个小的向量化函数,用于计算矩阵的 3x3 切片的对角线元素;如果sum3,我们就有一个有效的对角线。

对于对角线,我们从 this solution 借用代码。

counterdiag <- function(M) M[(n<-nrow(M))^2-(1:n)*(n-1)]

现在我们只需要一些坐标。

m <- n <- mapply(function(i) i:(i+2), 1:13)

还有我们的计数功能。

fun1 <- Vectorize(function(x, y) sum(diag(mat[m[,x], n[,y]])) == 3, SIMPLIFY=FALSE)
fun2 <- Vectorize(function(x, y) sum(counterdiag(mat[m[,x], n[,y]])) == 3, SIMPLIFY=FALSE)
  

用法

sum(unlist(outer(1:13, 1:13, fun1)))  # diagonals
# [1] 1

sum(unlist(outer(1:13, 1:13, fun2)))  # counterdiagonals
# [1] 3

【讨论】:

  • 感谢您的贡献!如果可能,您能否向我解释一下您使用的 mapply 函数以及“1:13”的来源?
  • @Rebecca 当然。其实我们也可以做sapply(1:13, function(i) i:(i+2))mapply()在有多个参数时变得有趣,试试?mapply1:13ms和ns的列数,即mat每个维度的切片可能组合。
  • @Rebecca 我相信是的。您可以将其包装成一个函数并使用lapply(),请参阅?lapply。这可能是如何进行的示例:stackoverflow.com/a/50631083/6574038
【解决方案2】:

这是一个嵌套的 for 循环(使用 sapply())。请注意,我没有与您相同的数据集,因此存在不同的种子。

set.seed(123)
mat <- matrix(sample(c(0,1), 225, prob=c(0.8,0.2), replace=TRUE), nrow=15)

n_by_n <- 3L

reg_diag <- diag(n_by_n)
rev_diag <- reg_diag[nrow(reg_diag):1, ]

sum(
  sapply(seq_len(ncol(mat)- n_by_n + 1),
       function(col) {
         sapply(seq_len(nrow(mat) - n_by_n + 1),
                function(row) {
                  tmp <- mat[row:(row + n_by_n - 1), col:(col + n_by_n - 1)]
                  all(tmp == reg_diag) | all(tmp == rev_diag)
                })
       })
)

#[1] 1

如果您只对对角线感兴趣而不关心子矩阵中的其他值,则将矩阵按每个对角线拆分,然后计算滚动和以查看它们的总和是否为 3:

library(RcppRoll)

set.seed(99)
mat <- matrix(sample(c(0,1), 225, prob=c(0.8,0.2), replace=TRUE), nrow=15)

n_by_n <- 3

diags <- row(mat)- col(mat)
cross_diags <- row(mat) + col(mat)

#could use data.table::frollsum instead of RcppRoll::roll_sumr)
sum(unlist(lapply(split(mat, diags), RcppRoll::roll_sumr, n_by_n), use.names = F) == n_by_n, na.rm = T)
#[1] 1

sum(unlist(lapply(split(mat, cross_diags), RcppRoll::roll_sumr, n_by_n), use.names = F) == n_by_n, na.rm = T)
# [1] 3

一个完整的基本方法是:

base_rollr <- function(x, roll) {
 #from user @flodel  
    if (length(x) >= roll)  tail(cumsum(x) - cumsum(c(rep(0, roll), head(x, -roll))), -roll + 1)
}

sum(unlist(lapply(split(mat, cross_diags), base_rollr, n_by_n), use.names = F) == n_by_n, na.rm = T)

另请参阅:Get all diagonal vectors from matrix

并且: Consecutive/Rolling sums in a vector in R

【讨论】:

  • 只是为了澄清@Cole 是 'n_by_n' 维数?当我运行它时,它会出错,说找不到对象,所以只是想知道。
  • 对不起,我跳过了一行。已编辑,但应该是 3
  • @Rebecca,请参阅编辑以了解另一种仅关注对角线而不是查看整个 3x3 子矩阵的方法。
  • 例如,我将如何执行您首先提到的嵌套 for 循环(使用 sapply())?我是否必须将所有内容包装到一个函数中,例如 'diag_function',然后 lapply 例如 'lapply(list, diag_function)'
  • sapply() 是一个双循环。你可以用lapply(mat_list, function (mat) {...}) 包裹所有东西
猜你喜欢
  • 2011-06-21
  • 1970-01-01
  • 2014-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
相关资源
最近更新 更多