使用Boost计算P50、P95分位数结果不符,如何修正?
问题:流式计算P50、P95分位数结果不符合预期
我尝试为流式整数列表计算P50和P95分位数,示例代码如下:
#include <boost/accumulators/accumulators.hpp> #include <boost/accumulators/statistics.hpp> #include <boost/accumulators/statistics/extended_p_square_quantile.hpp> namespace ba = boost::accumulators; int main() { std::vector<int> values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::vector<double> probs = {0.5, 0.95}; ba::accumulator_set<int, ba::stats<ba::tag::extended_p_square_quantile>> // acc(ba::extended_p_square_probabilities = probs); for (auto val : values) { acc(val); } std::cout << "P: " << ba::quantile(acc, ba::quantile_probability = 0.5) << " " << ba::quantile(acc, ba::quantile_probability = 0.95) << std::endl; }
运行后得到结果:
P: 3 7
但预期结果为5 9,请问如何得到正确的结果?
解决方案
原因分析
Boost的extended_p_square_quantile是流式近似分位数算法,设计目标是处理海量数据流,无需存储全部数据,但这种近似特性在小样本(如仅10个数据)场景下会产生较大偏差,无法得到精确分位数。
方法1:使用精确分位数统计量
改用Boost Accumulators中基于排序的精确分位数实现,通过指定tag::quantile统计量和discrete计算方法,可以得到预期的精确结果。修改后的代码如下:
#include <boost/accumulators/accumulators.hpp> #include <boost/accumulators/statistics.hpp> namespace ba = boost::accumulators; int main() { std::vector<int> values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::vector<double> probs = {0.5, 0.95}; // 初始化精确分位数累加器,指定离散分位数计算方式 ba::accumulator_set<int, ba::stats<ba::tag::quantile>> acc(ba::quantile_probabilities = probs, ba::quantile_method = ba::discrete); for (auto val : values) { acc(val); } std::cout << "P: " << ba::quantile(acc, ba::quantile_probability = 0.5) << " " << ba::quantile(acc, ba::quantile_probability = 0.95) << std::endl; }
这段代码会输出P: 5 9,符合预期。discrete方法直接取排序后对应位置的元素:
- P50对应中位数,即排序后第5个元素(值为5)
- P95对应排序后第10个元素,若需要线性插值的分位数结果,可将
ba::quantile_method改为ba::interpolated,此时会得到9.5的结果。
方法2:适配流式近似算法(大数据场景)
如果实际场景是处理海量流式数据,必须使用extended_p_square_quantile,那么小样本下的偏差是算法固有特性,可通过以下方式优化:
- 增加样本量:当数据量足够大时,近似结果会逐渐趋近于真实分位数
- 调整算法参数:Boost的
extended_p_square_quantile支持调整分位数的跟踪精度,但本质上仍属于近似计算,无法在小样本下达到精确结果
内容的提问来源于stack exchange,提问作者agrawal-d
相关产品推荐
相关产品推荐

