加权中位数计算

Weighted median computation

本文关键字:计算 中位数 加权      更新时间:2023-10-16

我正在寻找有关C++计算加权中值算法和/或示例代码的良好学习材料。我的中位数的权重是 0 到 1 之间的值。你能推荐我一些链接吗?

加权中位数定义如下:

如果 xN 个元素的排序数组,而w是总权重W的权重数组,则加权中位数是最后一个x[i]使得 w[i] 和所有先前权重的总和小于或等于 S/2

在C++中,这可以这样表示(假设xwW被定义为上面(

double sum = 0;
int i;
for(i = 0; i < N; ++i)
{
    sum += w[i];
    if(sum > W/2)
        break;
}
double median = x[i-1];

编辑

所以看来我回答这个问题太仓促了,犯了一些错误。我从 R 文档中找到了加权中位数的简洁描述,描述如下:

对于n元素x = c(x[1], x[2], ..., x[n])正 权重w = c(w[1], w[2], ..., w[n])使得sum(w) = S , 加权中位数x[k]定义为初始 x[i] < x[k]的所有元素的总重量小于或等于S/2 并且所有元素的总重量x[i] > x[k]较少 或等于 S/2

从这个描述中,我们有一个非常简单的算法实现。如果我们从k == 0开始,那么在x[k]之前没有元素,所以元素x[i] < x[k]的总重量将小于S/2。根据数据,元素x[i] > x[k]的总重量可能小于也可能不小于S/2。因此,我们可以在数组中向前移动,直到第二个总和小于或等于S/2

#include <cstddef>
#include <numeric>
#include <iostream>
int main()
{
  std::size_t const N = 5;
  double x[N] = {0, 1, 2, 3, 4};
  double w[N] = {.1, .2, .3, .4, .5};
  double S = std::accumulate(w, w+N, 0.0); // the total weight
  int k = 0;
  double sum = S - w[0]; // sum is the total weight of all `x[i] > x[k]`
  while(sum > S/2)
  {
    ++k;
    sum -= w[k];
  }
  std::cout << x[k] << std::endl;
}

请注意,如果中位数是最后一个元素 ( medianIndex == N-1 (,则sum == 0 ,因此条件sum > S/2失败。因此,k永远不会越界(除非N == 0!此外,如果有两个元素满足条件,则算法始终选择第一个元素。

下面是最初未排序向量的加权中位数的实现。它建立在 @Ken Wayne VanderLinde 对中位数计算的非常好的答案以及此线程中给出的索引排序器的基础上。

    template <typename VectorType>
    auto sort_indexes(VectorType const& v)
    {
        std::vector<int> idx(v.size());
        std::iota(std::begin(idx), std::end(idx), 0);
        std::sort(std::begin(idx), std::end(idx), [&v](int i1, int i2) {return v[i1] < v[i2];});
        return idx;
    }
    template<typename VectorType1, typename VectorType2>
    auto weightedMedian(VectorType1 const& x, VectorType2 const& weight)
    {
        double totalWeight = 0.0;
        for (int i = 0; i < static_cast<int>(x.size()); ++i)
        {
            totalWeight += weight[i];
        }
        auto ind = sort_indexes(x);
        int k = ind[0];
        double sum = totalWeight - weight[k];
        for (int i = 1; i < static_cast<int>(ind.size()); ++i)
        {
            k = ind[i];
            sum -= weight[k];
            if (sum <= 0.5 * totalWeight)
            {
                break;
            }
        }
        return x[k];
    }

它适用于任何支持 operator[](int)size() 的矢量类型(因此不使用std::accumulate等(。