2012-12-12 22 views
32

Mam ramkę danych Pythona pandy, w której kolumna zawiera nazwę miesiąca.Niestandardowe sortowanie w pandach Dataframe

Jak mogę zrobić zwyczaj sortowania przy użyciu słownika, na przykład:

custom_dict = {'March':0, 'April':1, 'Dec':3} 
+1

Czy kolumna zawiera nazwę miesiąca oznacza, że ​​istnieje kolumna zawierająca nazwy miesięcy (jako moją odpowiedź) lub wiele kolumn z nazwami kolumn jako nazwami miesięcy (jak eumiro)? –

Odpowiedz

48

Pandy 0.15 wprowadzono Categorical Series, który pozwala o wiele bardziej przejrzysty sposób:

Najpierw ustaw kolumnę miesiąca jako kategorię i określ kolejność, której chcesz użyć.

In [21]: df['m'] = pd.Categorical(df['m'], ["March", "April", "Dec"]) 

In [22]: df # looks the same! 
Out[22]: 
    a b  m 
0 1 2 March 
1 5 6 Dec 
2 3 4 April 

Teraz, kiedy posortować kolumnę miesiąc będzie rozwiązać w odniesieniu do tej listy:

In [23]: df.sort("m") 
Out[23]: 
    a b  m 
0 1 2 March 
2 3 4 April 
1 5 6 Dec 

Uwaga: Jeśli wartość nie znajduje się na liście zostanie skonwertowana do Nan.


Starszy odpowiedź dla zainteresowanych ...

Można by stworzyć serię pośrednika i set_index na tym:

df = pd.DataFrame([[1, 2, 'March'],[5, 6, 'Dec'],[3, 4, 'April']], columns=['a','b','m']) 
s = df['m'].apply(lambda x: {'March':0, 'April':1, 'Dec':3}[x]) 
s.sort() 

In [4]: df.set_index(s.index).sort() 
Out[4]: 
    a b  m 
0 1 2 March 
1 3 4 April 
2 5 6 Dec 

Jak zauważył, w nowszych pand, Seria ma metodę replace, aby zrobić to bardziej elegancko:

s = df['m'].replace({'March':0, 'April':1, 'Dec':3}) 

Niewielka różnica polega na tym, że nie podniesie się, jeśli istnieje wartość spoza słownika (pozostanie taka sama).

+0

's = df ['m']. Replace ({'March': 0, 'April': 1, 'Dec': 3})' działa również dla linii 2 - tylko dla dobra nauki pandy jak ja – kdauria

+0

@kdauria dobre miejsce! (od dłuższego czasu napisałem to!) zamień zdecydowanie najlepszą opcję, innym jest użycie '.apply ({'March': 0, 'April': 1, 'Dec': 3} .get)' :) In 0.15 będziemy mieć kategorię/kolumny kategorialne, więc najlepszym sposobem będzie użycie tego, a następnie sortowanie będzie działać. –

+0

@AndyHayden Miałem możliwość zastąpienia drugiej linii metodą "zastąp". Mam nadzieję, że to jest OK. –

2
import pandas as pd 
custom_dict = {'March':0,'April':1,'Dec':3} 

df = pd.DataFrame(...) # with columns April, March, Dec (probably alphabetically) 

df = pd.DataFrame(df, columns=sorted(custom_dict, key=custom_dict.get)) 

zwraca DataFrame z kolumnami marcu, kwietniu, gru

+0

To działa i łatwiej pisać w dowolnym istniejącym kodzie. Dziękuję Ci! – posdef

8

Trochę za późno, ale oto sposób na utworzenie funkcji, która sortuje obiekty Series, DataFrame i multiindex DataFrame przy użyciu dowolnych funkcji.

Korzystam z metody df.iloc[index], która odwołuje się do wiersza w serii/DataFrame według pozycji (w porównaniu do df.loc, która odnosi się według wartości). Korzystanie z tego, po prostu mieć funkcję zwracającą szereg argumentów pozycyjnych:

def sort_pd(key=None,reverse=False,cmp=None): 
    def sorter(series): 
     series_list = list(series) 
     return [series_list.index(i) 
      for i in sorted(series_list,key=key,reverse=reverse,cmp=cmp)] 
    return sorter 

można użyć tego do tworzenia własnych funkcji sortowania. Działa to na dataframe stosowanego w odpowiedzi Andy Haydena:

df = pd.DataFrame([ 
    [1, 2, 'March'], 
    [5, 6, 'Dec'], 
    [3, 4, 'April']], 
    columns=['a','b','m']) 

custom_dict = {'March':0, 'April':1, 'Dec':3} 
sort_by_custom_dict = sort_pd(key=custom_dict.get) 

In [6]: df.iloc[sort_by_custom_dict(df['m'])] 
Out[6]: 
    a b m 
0 1 2 March 
2 3 4 April 
1 5 6 Dec 

ten działa również na multiindex DataFrames i obiektów Seria:

months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] 

df = pd.DataFrame([ 
    ['New York','Mar',12714], 
    ['New York','Apr',89238], 
    ['Atlanta','Jan',8161], 
    ['Atlanta','Sep',5885], 
    ],columns=['location','month','sales']).set_index(['location','month']) 

sort_by_month = sort_pd(key=months.index) 

In [10]: df.iloc[sort_by_month(df.index.get_level_values('month'))] 
Out[10]: 
       sales 
location month 
Atlanta Jan 8161 
New York Mar 12714 
      Apr 89238 
Atlanta Sep 5885 

sort_by_last_digit = sort_pd(key=lambda x: x%10) 

In [12]: pd.Series(list(df['sales'])).iloc[sort_by_last_digit(df['sales'])] 
Out[12]: 
2 8161 
0 12714 
3 5885 
1 89238 

Dla mnie to czuje się czysty, ale używa operacji Pythona mocno zamiast polegania na zoptymalizowanych operacjach pand. Nie przeprowadzałem żadnych testów warunków skrajnych, ale wyobrażam sobie, że może to działać powoli w przypadku bardzo dużych obiektów DataFrames. Nie wiesz, jak wydajność jest porównywana z dodawaniem, sortowaniem i usuwaniem kolumny. Wszelkie wskazówki dotyczące przyspieszenia kodu będą mile widziane!

+0

Czy ta praca służy do sortowania wielu kolumn/indeksów? – ConanG

+0

tak, ale wybrana odpowiedź jest o wiele lepszym sposobem na zrobienie tego. Jeśli masz wiele indeksów, po prostu ułóż je zgodnie z preferowanym porządkiem sortowania, a następnie użyj polecenia 'df.sort_index()', aby posortować wszystkie poziomy indeksu. – delgadom

Powiązane problemy