我有一个向量,说x
只包含整数0
,1
和2
。例如;
x <- c(0,1,0,2,0,0,1,0,0,1,0,0,0,1,0)
由此我想提取每个“模式”中出现零次的次数。在这个简单的例子中,它自己发生了三次,00
发生两次,000
发生一次,所以我希望输出如下内容:
0 3
00 2
000 1
我的实际数据集非常大(向量中有1000-2000个元素),至少在理论上,连续零的最大数量为length(x)
答案 0 :(得分:19)
1)rle 像这样使用rle
和table
。不需要包裹。
tab <- with(rle(x), table(lengths[values == 0]))
,并提供:
> tab
1 2 3
3 2 1
或
> as.data.frame(tab)
Var1 Freq
1 1 3
2 2 2
3 3 1
也就是说,有3次运行,一次零,两次运行两次零,一次运行三次零。
如果有很长的运行时问题中的输出格式是不可行的,但只是为了好玩,它是:
data.frame(Sequence = strrep(0, names(tab)), Freq = as.numeric(tab))
,并提供:
Sequence Freq
1 0 3
2 00 2
3 000 1
2)gregexpr 另一种可能性是使用正则表达式:
tab2 <- table(attr(gregexpr("0+", paste(x, collapse = ""))[[1]], "match.length"))
,并提供:
> tab2
1 2 3
3 2 1
其他输出格式可以如(1)中那样派生。
我用2000的length(x)
来检查速度,(1)在我的笔记本电脑上花了大约1.6毫秒,(2)花了大约9毫秒。
答案 1 :(得分:11)
1)我们可以使用rleid
中的data.table
data.table(x)[, strrep(0, sum(x==0)) ,rleid(x == 0)][V1 != "",.N , V1]
# V1 N
#1: 0 3
#2: 00 2
#3: 000 1
2)或我们可以使用tidyverse
library(tidyverse)
tibble(x) %>%
group_by(grp = cumsum(x != 0)) %>%
filter(x == 0) %>%
count(grp) %>%
ungroup %>%
count(n)
# A tibble: 3 x 2
# n nn
# <int> <int>
#1 1 3
#2 2 2
#3 3 1
3)或者我们可以将tabulate
与rleid
tabulate(tabulate(rleid(x)[x==0]))
#[1] 3 2 1
在{SymbolixAU的数据集
上查看system.time
system.time({
tabulate(tabulate(rleid(x2)[x2==0]))
})
# user system elapsed
# 0.03 0.00 0.03
与Rcpp
功能相比,上述情况并不差
system.time({
m <- zeroPattern(x2)
m[m[,2] > 0, ]
})
# user system elapsed
# 0.01 0.01 0.03
使用microbenchmark
,删除消耗更多时间的方法(基于@ SymbolixAU的比较)并启动新的比较。请注意,这里也不是苹果到苹果,但它仍然比以前的比较更加相似,有data.table
的开销以及一些格式来复制OP的预期输出
microbenchmark(
akrun = {
tabulate(tabulate(rleid(x2)[x2==0]))
},
G = {
with(rle(x2), table(lengths[values == 0]))
},
sym = {
m <- zeroPattern(x2)
m[m[,2] > 0, ]
},
times = 5, unit = "relative"
)
#Unit: relative
# expr min lq mean median uq max neval cld
# akrun 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 5 a
# G 6.049181 8.272782 5.353175 8.106543 7.527412 2.905924 5 b
# sym 1.385976 1.338845 1.661294 1.399635 3.845435 1.211131 5 a
答案 2 :(得分:6)
你提到了一个非常大的&#39;数据集,所以你可以通过Rcpp
来使用C ++来加快速度(但是,基准测试显示基本rle
解决方案相当快)
一个功能可能是
library(Rcpp)
cppFunction('Rcpp::NumericMatrix zeroPattern(Rcpp::NumericVector x) {
int consecutive_counter = 0;
Rcpp::IntegerVector iv = seq(1, x.length());
Rcpp::NumericMatrix m(x.length(), 2);
m(_, 0) = iv;
for (int i = 0; i < x.length(); i++) {
if (x[i] == 0) {
consecutive_counter++;
} else if (consecutive_counter > 0) {
m(consecutive_counter-1, 1)++;
consecutive_counter = 0;
}
}
if (consecutive_counter > 0) {
m(consecutive_counter-1, 1)++;
}
return m;
}')
它为您提供了连续零的计数矩阵
x <- c(0,1,0,2,0,0,1,0,0,1,0,0,0,1,0)
zeroPattern(x)
m <- zeroPattern(x)
m[m[,2] > 0, ]
# [,1] [,2]
# [1,] 1 3
# [2,] 2 2
# [3,] 3 1
在更大的数据集上,我们注意到速度改进
set.seed(20180411)
x2 <- sample(x, 1e6, replace = T)
m <- zeroPattern(x2)
m[m[,2] > 0, ]
library(microbenchmark)
library(data.table)
microbenchmark(
akrun = {
data.table(x2)[, strrep(0, sum(x2==0)) ,rleid(x2 == 0)][V1 != "",.N , V1]
},
G = {
with(rle(x2), table(lengths[values == 0]))
},
sym = {
m <- zeroPattern(x2)
m[m[,2] > 0, ]
},
times = 5
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# akrun 3727.66899 3782.19933 3920.9151 3887.6663 4048.2275 4158.8132 5
# G 236.69043 237.32251 258.4320 246.1470 252.1043 319.8956 5
# sym 97.54988 98.76986 190.3309 225.2611 237.5781 292.4955 5
注意:
我和G的职能部门正在回答一个表格式的答案。 Akrun已将其格式化为包含填充零,因此会产生轻微的成本。