2014-09-29 2 views
6

У меня есть dataframe, который выглядит следующим образом:Python: взвешенный медианный алгоритм с пандами

Out[14]: 
    impwealth indweight 
16  180000  34.200 
21  384000  37.800 
26  342000  39.715 
30 1154000  44.375 
31  421300  44.375 
32 1210000  45.295 
33 1062500  45.295 
34 1878000  46.653 
35  876000  46.653 
36  925000  53.476 

Я хочу, чтобы вычислить взвешенную медиану столбца impwealth с использованием весов частот в indweight. Мой псевдо код выглядит следующим образом:

# Sort `impwealth` in ascending order 
df.sort('impwealth', 'inplace'=True) 

# Find the 50th percentile weight, P 
P = df['indweight'].sum() * (.5) 

# Search for the first occurrence of `impweight` that is greater than P 
i = df.loc[df['indweight'] > P, 'indweight'].last_valid_index() 

# The value of `impwealth` associated with this index will be the weighted median 
w_median = df.ix[i, 'impwealth'] 

Этот метод кажется неуклюжим, и я не уверен, что это правильно. Я не нашел встроенного способа сделать это в ссылке pandas. Каков наилучший способ найти взвешенную медианную?

+0

Вы уверены, что ваш псевдо-код является правильным? 'df ['indweight']. sum() * (.5)' даст значение ~ '219', значение которого не превышает ваших значений' indweight'. Вызов 'df ['indweight']. Median()' дает 44.835 и 'mean()' дает 43.783 – EdChum

+0

Я так думаю .. 'df ['indweight']. Sum() * (.5)' должен вычислять число наблюдений, которые подпадают под 50-й процентиль данных, поскольку «лишний вес» является частотным весом. Таким образом, имеет смысл, что средняя и медиана «лишнего веса» превышают его сумму. – svenkatesh

+0

@svenkatesh, вам нужно использовать '' .cumsum() '' 'indweight'', а не' 'indweight''. См. Мой ответ ниже, возможно. – prooffreader

ответ

7

Если вы хотите сделать это в чистых пандах, вот путь. Он тоже не интерполирует. (@svenkatesh, вы отсутствовали на накопленную сумму в вашем псевдокод)

df.sort_values('impwealth', inplace=True) 
cumsum = df.indweight.cumsum() 
cutoff = df.indweight.sum()/2.0 
median = df.impwealth[cumsum >= cutoff].iloc[0] 

Это дает медиану 925000.

5

Вы пробовали пакет wqantiles? Я никогда не использовал его раньше, но у него есть взвешенная медианная функция, которая, кажется, дает хотя бы разумный ответ (вы, вероятно, захотите дважды проверить, что он использует ожидаемый вами подход).

In [12]: import weighted 

In [13]: weighted.median(df['impwealth'], df['indweight']) 
Out[13]: 914662.0859091772 
+2

Опечатка: wqantiles -> wquantiles – Jaan

+1

Лично я немного опасаюсь установить пакет, в котором будут выполняться несколько строк кода, но если вам нужны интерполированные взвешенные медианы, возможно, это лучший подход. – prooffreader

1

Вы также можете использовать эту функцию, которую я написал для этой же цели.

Примечание: взвешенная используется интерполяция в конце, чтобы выбрать 0.5 квантиль (вы можете посмотреть на код самостоятельно)

Моя написана функция просто возвращает один ограничительный 0,5 вес.

import numpy as np 

def weighted_median(values, weights): 
    ''' compute the weighted median of values list. The 
weighted median is computed as follows: 
    1- sort both lists (values and weights) based on values. 
    2- select the 0.5 point from the weights and return the corresponding values as results 
    e.g. values = [1, 3, 0] and weights=[0.1, 0.3, 0.6] assuming weights are probabilities. 
    sorted values = [0, 1, 3] and corresponding sorted weights = [0.6,  0.1, 0.3] the 0.5 point on 
    weight corresponds to the first item which is 0. so the weighted  median is 0.''' 

    #convert the weights into probabilities 
    sum_weights = sum(weights) 
    weights = np.array([(w*1.0)/sum_weights for w in weights]) 
    #sort values and weights based on values 
    values = np.array(values) 
    sorted_indices = np.argsort(values) 
    values_sorted = values[sorted_indices] 
    weights_sorted = weights[sorted_indices] 
    #select the median point 
    it = np.nditer(weights_sorted, flags=['f_index']) 
    accumulative_probability = 0 
    median_index = -1 
    while not it.finished: 
     accumulative_probability += it[0] 
     if accumulative_probability > 0.5: 
      median_index = it.index 
      return values_sorted[median_index] 
     elif accumulative_probability == 0.5: 
      median_index = it.index 
      it.iternext() 
      next_median_index = it.index 
      return np.mean(values_sorted[[median_index, next_median_index]]) 
     it.iternext() 

    return values_sorted[median_index] 
#compare weighted_median function and np.median 
print weighted_median([1, 3, 0, 7], [2,3,3,9]) 
print np.median([1,1,0,0,0,3,3,3,7,7,7,7,7,7,7,7,7]) 
+0

взвешенная медианная функция очень похожа на принятый ответ, если вы посмотрите на код, но не интерполируете в конце. – Ash

Смежные вопросы