我有这个数字序列:
> 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
化为字符串,并使用substr
从1 + i
到n + i
,使用vapply
将i
从0
递增到len - n
,得到可以轻松表示table
d的序列。
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