r语言 - 如何计算n个序列重复移位1的频率?



我有这个数字序列:

> Results
[1] 0 0 0 1 1 1 0 1 0 1 1

在前面的问题中,我学习了如何编写一个函数来计算连续的"n"出现在字符串中的序列(从左到右)。

使用上一个问题提供的答案-我在我的数据上尝试了这个函数:

n_sequences <- function(n, results) {
helper <- function(i, n) if (n < 1) "" else sprintf(
"%s%s", 
helper(i, n - 1), 
results[i + n - 1]
)
result <- data.frame(
table(
sapply(
1:(length(results) - n),
function(i) helper(i, n)
)
)
)
colnames(result) <- c("Sequence", "Frequency")
result
}
# consecutive 4 sequence
n_sequences(4, Results)

这给了我以下的答案:

Sequence Frequency
1     0001         1
2     0011         1
3     0101         1
4     0111         1
5     1010         1
6     1101         1
7     1110         1

一切似乎都很好——但仔细看:

> Results
[1] 0 0 0 1 1 1 0 1 0 1 1

我认为上表缺少最后一个组合1,0,1,1

有没有人知道如何修改上述函数以包括最后一个序列?

注意:在未来,我有兴趣使用这个表来计算条件概率。例如,给定出现了3个0,下一个数字是1的概率是多少?

注意:我总是有兴趣学习编写可以完成相同任务的函数的替代方法-如果其他人有计算这些频率计数的替代方法,我会很感兴趣!

这是一个使用data.table分组操作的选项。

library(data.table)
Results <- c(0,0,0,1,1,1,0,1,0,1,1)
n_sequences <- function(n, results) {
as.data.table(
matrix(
results[sequence(rep(length(results) - n + 1, n), 1:n)],
ncol = n
)
)[
, .(sequence = paste0(.BY, collapse = ""), Frequency = .N), by = eval(paste0("V", 1:n))
][
,paste0("V", 1:n) := NULL
]
}
n_sequences(4, Results)[]
#>    sequence Frequency
#> 1:     0001         1
#> 2:     0011         1
#> 3:     0111         1
#> 4:     1110         1
#> 5:     1101         1
#> 6:     1010         1
#> 7:     0101         1
#> 8:     1011         1

vapply方法相比,它的性能非常好。

f <- function(x, n, ordered=TRUE) {
len <- length(x)
stopifnot(!anyNA(x) && n > 0L && n <= len)
v <- paste(x, collapse='')
sq <- vapply(0:(len - n), function(i) substr(v, 1L + i, n + i), character(1L))
out <- as.data.frame(table(sq))
if (!ordered) `rownames<-`(out[match(out$sq, unique(sq)), ], NULL) else out
}
Results <- sample(0:1, 1e7, 1)
system.time(Frequency1 <- f(Results, 6))
#>    user  system elapsed 
#>   15.30    0.17   15.49
system.time(Frequency2 <- n_sequences(6, Results))
#>    user  system elapsed 
#>    1.03    0.50    0.80
identical(Frequency1$Freq, setorder(Frequency2, sequence)$Frequency)
#> [1] TRUE

我们可以将向量paste化为字符串,并使用substr1 + in + i,使用vapplyi0递增到len - n,得到可以轻松表示tabled的序列。

f <- (x, n, ordered=TRUE) {
len <- length(x)
stopifnot(!anyNA(x) && n > 0L && n <= len)
v <- paste(x, collapse='')
sq <- vapply(0:(len - n), (i) substr(v, 1L + i, n + i), character(1L))
out <- as.data.frame(table(sq))
if (!ordered) `rownames<-`(out[match(out$sq, unique(sq)), ], NULL) else out
}
x <- c(0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1)
f(x, 4)
#     sq Freq
# 1 0001    1
# 2 0011    1
# 3 0101    1
# 4 0111    1
# 5 1010    1
# 6 1011    1
# 7 1101    1
# 8 1110    1

如果我们想要外观顺序而不是字母顺序:

f(x, 4, ordered=FALSE)
#     sq Freq
# 1 0001    1
# 2 0011    1
# 3 1101    1
# 4 0101    1
# 5 1011    1
# 6 1110    1
# 7 1010    1
# 8 0111    1

这个工作起来很快,

set.seed(42)
system.time(res <- f(sample(0:1, 1e3, replace=TRUE), 5))
#  user  system elapsed 
# 0.005   0.001   0.004 
res
#       sq Freq
# 1  00000   18
# 2  00001   26
# 3  00010   41
# ...
# 30 11101   32
# 31 11110   34
# 32 11111   20

对于相对较大的向量也是如此。

f(sample(0:1, 1e7, replace=TRUE), 6) |> system.time()
#   user  system elapsed 
# 34.668   0.000  34.643 

最新更新