检查作为向量列表一部分的字符串中的每个位置是否可变的方法?

Way to check if each position in a string as part of a list of vectors is variable?

我有以下名为 codes 的 R 列表:

>codes
$`1`
 [1] "000" "000" "111" "000" "100" "000" "100" "000" "100" "100"

$`2`
 [1] "000" "001" "110" "000" "000" "000" "000" "000" "000" "000"

$`3`
 [1] "000" "010" "100" "001" "001" "000" "000" "000" "001" "001"

$`4`
 [1] "000" "100" "000" "011" "011" "000" "000" "000" "011" "011"

我想做的是实现一种方法来检查整个列表中该列表元素中字符串的每个位置是否可变。

例如,对于第一个向量 [1],它将只检查每个字符串项的第一个 number/character 并确定它们是否都以 0 或 1(因此不可变)开头,或者至少有一个是不同的。然后它将查看每个字符串中的第二个字符并执行相同的操作。第三名以此类推。

我的直觉是使用 lapply(codes, strsplit, split = "") 的某种组合将单个字符与 length(unique(x))) != 1) 一起拆分,以便在任何位置可变时吐出 TRUE/FALSE或不,但我不确定实现此目的的有效方法。最终,数据集将更大,代码长度可达 10 个字符,我想确保向量中每个三个字母代码的每个位置都是可变的,并将其应用于列表中的每个向量元素。

如果有任何帮助或建议,我将不胜感激。

这是一种基于字符串拆分的方法:

l <- list(rep.int(strrep("0", 6L), 8L),
          rep.int(strrep(c("10", "01"), 3L), 4L),
          rep.int(strrep(c("10", "100"), c(3L, 2L)), c(4L, 4L)))
l
## [[1]]
## [1] "000000" "000000" "000000" "000000"
## [5] "000000" "000000" "000000" "000000"
## 
## [[2]]
## [1] "101010" "010101" "101010" "010101"
## [5] "101010" "010101" "101010" "010101"
## 
## [[3]]
## [1] "101010" "101010" "101010" "101010"
## [5] "100100" "100100" "100100" "100100"

f <- function(l) {
    m <- nchar(l[[1L]][1L])
    n <- length(l)
    f0 <- function(x) {
        matrix(unlist(strsplit(x, ""), FALSE, FALSE), m)
    }
    X <- do.call(rbind, lapply(l, f0))
    matrix(matrixStats::rowAnys(X != X[, 1L]), n, byrow = TRUE)
}
f(l)
##       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]
## [1,] FALSE FALSE FALSE FALSE FALSE FALSE
## [2,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
## [3,] FALSE FALSE  TRUE  TRUE  TRUE FALSE

如果您的代码可以读取为小于或等于 .Machine$integer.max 的十进制数,那么您可以通过将字符串拆分替换为整数运算来进行优化:

g <- function(l) {
    m <- length(l)
    n <- length(l[[1L]])
    N <- nchar(l[[1L]][1L])
    X <- matrix(as.integer(unlist(l, FALSE, FALSE)), m, n, byrow = TRUE)
    g0 <- function(pow) {
        Y <- X %/% pow
        X <<- X - pow * Y
        matrixStats::rowAnys(Y != Y[, 1L])
    }
    pow <- as.integer(10^((N - 1L):0))
    matrix(unlist(lapply(pow, g0), FALSE, FALSE), m, N)
}
g(l)
##       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]
## [1,] FALSE FALSE FALSE FALSE FALSE FALSE
## [2,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
## [3,] FALSE FALSE  TRUE  TRUE  TRUE FALSE

如果你的代码实际上是二进制的,那么你可以稍微优化一下并省去 matrixStats:

h <- function(l) {
    m <- length(l[[1L]])
    n <- length(l)
    N <- nchar(l[[1L]][1L])
    X <- matrix(as.integer(unlist(l, FALSE, FALSE)), m, n)
    h0 <- function(p) {
        Y <- X %/% p
        X <<- X - p * Y
        .colSums(Y, m, n) %% m > 0L
    }
    pow <- as.integer(10^((N - 1L):0))
    matrix(unlist(lapply(pow, h0), FALSE, FALSE), n, N)
}
h(l)
##       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]
## [1,] FALSE FALSE FALSE FALSE FALSE FALSE
## [2,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
## [3,] FALSE FALSE  TRUE  TRUE  TRUE FALSE

这是长度为 10000 的 6 位二进制代码长度为 8 的字符向量列表的基准。

ll <- rep_len(l, 1e+04L)
microbenchmark::microbenchmark(f(ll), g(ll), h(ll))
## Unit: milliseconds
##   expr       min        lq      mean    median        uq       max neval
##  f(ll) 41.583143 55.960510 66.201555 64.211679 73.542807 127.47810   100
##  g(ll)  8.612173  8.856123  9.725214  8.946077  9.116391  46.66698   100
##  h(ll)  7.622679  7.824789  8.717184  7.887519  7.987128  46.32225   100