Skip to content

Instantly share code, notes, and snippets.

@tinybike
Last active July 4, 2023 15:03
Show Gist options
  • Save tinybike/d9ff1dad515b66cc0d87 to your computer and use it in GitHub Desktop.
Save tinybike/d9ff1dad515b66cc0d87 to your computer and use it in GitHub Desktop.
calculate a weighted median
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
calculate a weighted median
@author Jack Peterson (jack@tinybike.net)
"""
from __future__ import division
import numpy as np
def weighted_median(data, weights):
"""
Args:
data (list or numpy.array): data
weights (list or numpy.array): weights
"""
data, weights = np.array(data).squeeze(), np.array(weights).squeeze()
s_data, s_weights = map(np.array, zip(*sorted(zip(data, weights))))
midpoint = 0.5 * sum(s_weights)
if any(weights > midpoint):
w_median = (data[weights == np.max(weights)])[0]
else:
cs_weights = np.cumsum(s_weights)
idx = np.where(cs_weights <= midpoint)[0][-1]
if cs_weights[idx] == midpoint:
w_median = np.mean(s_data[idx:idx+2])
else:
w_median = s_data[idx+1]
return w_median
def test_weighted_median():
data = [
[7, 1, 2, 4, 10],
[7, 1, 2, 4, 10],
[7, 1, 2, 4, 10, 15],
[1, 2, 4, 7, 10, 15],
[0, 10, 20, 30],
[1, 2, 3, 4, 5],
[30, 40, 50, 60, 35],
[2, 0.6, 1.3, 0.3, 0.3, 1.7, 0.7, 1.7, 0.4],
]
weights = [
[1, 1/3, 1/3, 1/3, 1],
[1, 1, 1, 1, 1],
[1, 1/3, 1/3, 1/3, 1, 1],
[1/3, 1/3, 1/3, 1, 1, 1],
[30, 191, 9, 0],
[10, 1, 1, 1, 9],
[1, 3, 5, 4, 2],
[2, 2, 0, 1, 2, 2, 1, 6, 0],
]
answers = [7, 4, 8.5, 8.5, 10, 2.5, 50, 1.7]
for datum, weight, answer in zip(data, weights, answers):
assert(weighted_median(datum, weight) == answer)
if __name__ == "__main__":
test_weighted_median()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment