2014-09-29 12 views
6

Mam dataframe, który wygląda tak:Python: ważona mediana algorytm z pand

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 

Chcę obliczyć ważoną medianę kolumnie impwealth wykorzystaniem wagi częstotliwości w indweight. Mój pseudo kod wygląda następująco:

# 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'] 

Ta metoda wydaje się niezgrabne, i nie jestem pewien, że to poprawne. Nie znalazłem wbudowanego w to sposobu w odniesieniu do pand. Jaki jest najlepszy sposób na znalezienie ważonej mediany?

+0

Czy jesteś pewien kod pseudo jest poprawna? 'df ['indweight']. sum() * (.5)' da wartość ~ 219', której nie przekroczy żadna z wartości 'indweight'. Wywołanie 'df ['indweight']. Median()' daje 44.835 i 'mean()' daje 43.783 – EdChum

+0

Myślę, że tak .. 'df ['indweight']. Sum() * (.5)' należy obliczyć liczba obserwacji, które mieszczą się w 50. percentylu danych, ponieważ "indweight" jest masą częstotliwości. Więc ma sens, że średnia i mediana "indats" przewyższają jej sumę. – svenkatesh

+0

@svenkatesh, musisz użyć '' .cumsum() '' z '' indweight'', a nie '' indweight''. Zobacz moją odpowiedź poniżej, być może. – prooffreader

Odpowiedz

7

Jeśli chcesz to zrobić w czystej pandzie, oto sposób. Nie interpoluje też. (@svenkatesh, to brakowało skumulowaną sumę w Pseudokod)

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

To daje medianę 925000.

5

Czy wypróbowałeś pakiet wqantiles? Nigdy wcześniej go nie używałem, ale ma ważoną medianę, która wydaje się dawać przynajmniej rozsądną odpowiedź (prawdopodobnie będziesz chciał dwukrotnie sprawdzić, czy używasz podejścia, którego się spodziewasz).

In [12]: import weighted 

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

literówka: wqantiles -> wquantiles – Jaan

+1

Osobiście jestem trochę ostrożny przy instalacji pakietu, w którym zrobi się kilka linii kodu, ale jeśli potrzebujesz interpolowanych ważonych median, być może jest to najlepsze podejście. – prooffreader

1

Możesz również użyć tej funkcji, którą napisałem w tym samym celu.

Uwaga: ważone używa interpolacji na końcu wybrać 0,5 kwantyl (można spojrzeć na kod siebie)

Moja napisana funkcja zwraca tylko jeden obwiedni 0,5 wadze.

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

ważona funkcja mediana jest bardzo podobna do przyjętej odpowiedzi, jeśli spojrzeć na kod, ale nie interpoluje na końcu. – Ash