17 个不稳定版本 (6 个破坏性更新)

使用旧的 Rust 2015

0.7.1 2018年3月1日
0.7.0 2017年12月11日
0.6.3 2017年12月6日
0.6.2 2017年11月20日
0.1.4 2016年7月1日

#164 in 算法

Download history 2502/week @ 2024-03-14 2138/week @ 2024-03-21 2574/week @ 2024-03-28 1109/week @ 2024-04-04 2142/week @ 2024-04-11 1075/week @ 2024-04-18 1303/week @ 2024-04-25 1369/week @ 2024-05-02 1052/week @ 2024-05-09 1290/week @ 2024-05-16 1485/week @ 2024-05-23 1042/week @ 2024-05-30 1117/week @ 2024-06-06 886/week @ 2024-06-13 1597/week @ 2024-06-20 1383/week @ 2024-06-27

5,141 每月下载量
用于 13 个包 (9 个直接)

MIT 许可证

86KB
1.5K SLoC

quantiles

Build Status Codecov Crates.io

这个包旨在成为一组提供空间和计算保证的近似分位数算法的集合。最近的研究已经推动了近似技术,但没有一个是普遍适用的,并且有基本的权衡。

最初的工作是为了支持内部 Postmates 项目,但希望这个包可以普遍有用。

算法

CKMS - 在数据流上有效计算有偏分位数

这是 Cormode, Korn, Muthukrishnan, Srivastava 的论文 "在数据流上有效计算有偏分位数" 中提出的算法的实现。这里的雄心是近似数据流上的分位数,而不需要在内存中保留大量信息。这个实现遵循了 IEEE 版本 的论文。作者的自行出版版本是不正确的,如果使用该版本进行跟随,这个实现将 不会 有意义。只使用了 'full biased' 不变量。这个算法的 'targeted quantiles' 变体在本质上是有缺陷的,这是作者在 "在数据流上有偏分位数的时间和空间效率高的确定性算法" 中纠正的问题。

use quantiles::CKMS;

let mut ckms = CKMS::<u16>::new(0.001);
for i in 1..1001 {
    ckms.insert(i as u16);
}

assert_eq!(ckms.query(0.0), Some((1, 1)));
assert_eq!(ckms.query(0.998), Some((998, 998)));
assert_eq!(ckms.query(0.999), Some((999, 999)));
assert_eq!(ckms.query(1.0), Some((1000, 1000)));

查询提供对真实分位数的近似,+/- εΦn。在上面的例子中,ε 设置为 0.001,n 是 1000。最小和最大分位数--0.0 和 1.0--已经是精确的。中间查询的误差为 +/- 0.998。(这碰巧是精确的分位数,但并不总是这样。)

对于一个误差ε,这种结构将需要T*(floor(1/(2*ε)) + O(1/ε log εn)) + f64 + usize + usize存储空间,其中T是专用类型。

在本地测试中,每点的插入时间约为4微秒,方差为7%。这意味着每秒可以插入250k个点。

Misra Gries - ε近似频率计数

Misra-Gries计算一个ε近似频率计数,用于N个元素的流。输出是k个最频繁的元素。

  1. 近似计数f'[e]小于元素e的真实频率f[e],但最多为εN,即(f[e] - εN) ≤ f'[e] ≤ f[e]
  2. 任何频率f[e] ≥ εN的元素e都会出现在结果集中

误差界限ε = 1/(k+1),其中k是算法中使用的计数器数量。当k = 1时,即单个计数器,该算法等同于Boyer-Moore多数算法。

如果您想检查至少出现εN次的元素,您将需要进行第二次遍历来计算结果集中值的精确频率,这可以在常数空间中完成。

use quantiles::misra_gries::*;

let k: usize = 3;
let numbers: Vec<u32> = vec![1,3,2,1,3,4,3,1,2,1];
let counts = misra_gries(numbers.iter(), k);
let bound = numbers.len() / (k+1);
let in_range = |f_expected: usize, f_approx: usize| {
  f_approx <= f_expected &&
  (bound >= f_expected || f_approx >= (f_expected - bound))
};
assert!(in_range(4usize, *counts.get(&1).unwrap()));
assert!(in_range(2usize, *counts.get(&2).unwrap()));
assert!(in_range(3usize, *counts.get(&3).unwrap()));

Greenwald Khanna - ε近似分位数

Greenwald Khanna计算ε近似分位数。如果所需的分位数是φ,则ε近似分位数是介于(φ-ε)N⌋(φ+ε)N⌋之间的任何元素

流摘要数据结构可以处理最多max[usize]个观察结果。

开始和结束分位数分别被限制在观察到的最小和最大元素。

此页解释了理论: http://www.mathcs.emory.edu/~cheung/Courses/584-StreamDB/Syllabus/08-Quantile/Greenwald.html

use quantiles::greenwald_khanna::*;

let epsilon = 0.01;

let mut stream = Stream::new(epsilon);

let n = 1001;
for i in 1..n {
    stream.insert(i);
}
let in_range = |phi: f64, value: u32| {
  let lower = ((phi - epsilon) * (n as f64)) as u32;
  let upper = ((phi + epsilon) * (n as f64)) as u32;
  (epsilon > phi || lower <= value) && value <= upper
};
assert!(in_range(0f64, *stream.quantile(0f64)));
assert!(in_range(0.1f64, *stream.quantile(0.1f64)));
assert!(in_range(0.2f64, *stream.quantile(0.2f64)));
assert!(in_range(0.3f64, *stream.quantile(0.3f64)));
assert!(in_range(0.4f64, *stream.quantile(0.4f64)));
assert!(in_range(1f64, *stream.quantile(1f64)));

升级

0.2 -> 0.3

本版本引入了两个新算法:“Greenwald Khanna”和“Misra Gries”。现有的CKMS已从根目录移动到自己的子模块。您需要更新您的导入,从

use quantiles::CMKS;

use quantiles::ckms::CKMS;

依赖关系

~170KB