如何在NumPy中基于汇总(观测值、响应数)数据计算中位数与百分位数
基于汇总统计数据计算中位数与百分位数
刚好碰到过类似的需求,咱们一步步来解决这个问题——不用展开原始数据,直接从[观测值, 响应次数]的汇总数据里算出和原始数据一致的中位数、百分位数结果。
先回顾原始数据的常规计算方式
percentile和median方法通常直接作用在原始未处理数据上,示例代码如下:
import numpy as np raw_data = [1,1,1,1,2,3,4,4,5,5] # 原始观测列表 a = np.array(raw_data) print(np.median(a)) # 输出中位数:2 print(np.percentile(a, 50)) # 输出50分位数(和中位数结果一致):2
核心问题:用汇总数据实现相同计算
现在我们只有汇总后的统计数据:
summarised_data = [[1,4],[2,1],[3,1],[4,2],[5,2]] # 结构:[[观测值, 出现次数], ...]
核心思路是通过累计计数定位目标位置,完全不需要展开原始数据。
方法1:纯Python手动实现
这种方式不用依赖任何库,逻辑清晰易懂:
summarised_data = [[1,4],[2,1],[3,1],[4,2],[5,2]] # 拆分观测值和对应出现次数 values, counts = zip(*summarised_data) # 计算累计出现次数 cumulative_counts = [] total_observations = 0 for cnt in counts: total_observations += cnt cumulative_counts.append(total_observations) # 计算中位数对应的位置 if total_observations % 2 == 1: median_position = (total_observations + 1) // 2 else: median_position = total_observations // 2 # 定位中位数对应的观测值 median = None for val, cum_cnt in zip(values, cumulative_counts): if cum_cnt >= median_position: median = val break print(median) # 输出:2,和原始数据计算结果一致
如果要计算任意百分位数(比如75分位数),可以封装成通用函数:
def calculate_percentile(summarised_data, percentile): values, counts = zip(*summarised_data) cumulative_counts = [] total_obs = 0 for cnt in counts: total_obs += cnt cumulative_counts.append(total_obs) # 计算百分位数对应的位置 target_pos = (total_obs - 1) * percentile / 100 # 找到对应区间的观测值 for idx, cum_cnt in enumerate(cumulative_counts): if cum_cnt > target_pos: if target_pos.is_integer(): return values[int(target_pos)] return values[idx] return values[-1] # 验证50分位数(中位数) print(calculate_percentile(summarised_data, 50)) # 输出:2 # 计算75分位数 print(calculate_percentile(summarised_data, 75)) # 输出:4,和原始数据的np.percentile(a,75)结果一致
方法2:用Numpy简化实现
如果习惯用Numpy,可以利用其数组操作快速实现:
import numpy as np summarised_data = np.array([[1,4],[2,1],[3,1],[4,2],[5,2]]) values = summarised_data[:, 0] counts = summarised_data[:, 1] # 计算累计次数 cumulative_counts = np.cumsum(counts) total_obs = cumulative_counts[-1] # 定位中位数 median_pos = (total_obs - 1) * 0.5 median_idx = np.argmax(cumulative_counts > median_pos) median = values[median_idx] print(median) # 输出:2 # 封装成通用百分位数计算函数 def np_calculate_percentile(summarised_data, percentile): values = summarised_data[:, 0] counts = summarised_data[:, 1] cumulative_counts = np.cumsum(counts) total_obs = cumulative_counts[-1] target_pos = (total_obs - 1) * percentile / 100 idx = np.argmax(cumulative_counts > target_pos) return values[idx] # 验证结果 print(np_calculate_percentile(summarised_data, 50)) # 2 print(np_calculate_percentile(summarised_data, 75)) # 4
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

