Czy kolumny zawierają nazwę miesiąca, to znaczy, że istnieje kolumna zawierająca nazwy miesięcy (jak moja odpowiedź), czy też wiele kolumn z nazwami kolumn jako nazwami miesięcy (jak eumiro)?
Andy Hayden
1
Przyjęta odpowiedź jest nieaktualna, a także technicznie niepoprawna, ponieważ pd.Categoricaldomyślnie nie interpretuje kategorii w kolejności. Zobacz tę odpowiedź .
cs95
Odpowiedzi:
141
Pandy 0.15 wprowadziły serię kategorialną , która pozwala na znacznie jaśniejszy sposób:
Najpierw ustaw kolumnę miesiąca jako kategoryczną i określ kolejność, która ma być używana.
In [21]: df['m'] = pd.Categorical(df['m'], ["March", "April", "Dec"])
In [22]: df # looks the same!
Out[22]:
a b m
012 March
156 Dec
234 April
Teraz, kiedy posortujesz kolumnę miesiąca, zostanie ona posortowana według tej listy:
In [23]: df.sort_values("m")
Out[23]:
a b m
012 March
234 April
156 Dec
Uwaga: jeśli wartość nie znajduje się na liście, zostanie przekonwertowana na NaN.
Starsza odpowiedź dla zainteresowanych ...
Możesz stworzyć serię pośrednią i set_indexna 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_values()
In [4]: df.set_index(s.index).sort()
Out[4]:
a b m
012 March
134 April
256 Dec
Jak skomentowano, w nowszych pandach Series ma replacemetodę, aby zrobić to bardziej elegancko:
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
Niewielka różnica polega na tym, że nie wzrośnie, jeśli wartość znajduje się poza słownikiem (po prostu pozostanie taka sama).
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})działa również dla linii 2 - tylko dla dobra każdego uczącego się pandy jak ja
kdauria
@kdauria dobre miejsce! (minęło trochę czasu, odkąd to napisałem!) zastąp zdecydowanie najlepszą opcją, inną jest użycie .apply({'March':0, 'April':1, 'Dec':3}.get):) W 0.15 będziemy mieli Serie / kolumny kategorialne, więc najlepszym sposobem będzie użycie tego i wtedy sortowanie po prostu zadziała.
Andy Hayden
@AndyHayden Pozwoliłem sobie zastąpić drugą linię metodą „zamień”. Mam nadzieję, że wszystko w porządku.
Faheem Mitha
Edycja @AndyHayden odrzucona, ale nadal uważam, że jest to rozsądna zmiana.
Faheem Mitha
7
Tylko upewnij się, że używasz df.sort_values("m")w nowszych pandach (zamiast df.sort("m")), w przeciwnym razie otrzymasz AttributeError: 'DataFrame' object has no attribute 'sort';)
burza mózgów
17
pandy> = 1.1
Wkrótce będziecie mogli korzystać sort_valuesz keyargumentu:
pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'
custom_dict = {'March': 0, 'April': 1, 'Dec': 3}
df
a b m
012 March
156 Dec
234 April
df.sort_values(by=['m'], key=lambda x: x.map(custom_dict))
a b m
012 March
234 April
156 Dec
keyArgumentem bierze jako wejście serii i wraca serii. Ta seria jest wewnętrznie posortowana za pomocą argumentów, a posortowane indeksy są używane do zmiany kolejności wejściowej ramki DataFrame. Jeśli istnieje wiele kolumn do sortowania, funkcja klucza zostanie zastosowana do każdej z nich po kolei. Zobacz Sortowanie za pomocą kluczy .
pandy <= 1.0.X
Jedną z prostych metod jest użycie wyjścia Series.mapi Series.argsortindeksowanie do dfusing DataFrame.iloc(ponieważ argsort tworzy posortowane pozycje liczb całkowitych); ponieważ masz słownik; staje się to łatwe.
df.iloc[df['m'].map(custom_dict).argsort()]
a b m
012 March
234 April
156 Dec
Jeśli chcesz posortować w porządku malejącym , odwróć mapowanie.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
156 Dec
234 April
012 March
Zwróć uwagę, że działa to tylko w przypadku elementów numerycznych. W przeciwnym razie będziesz musiał obejść ten problem, używając sort_valuesi uzyskując dostęp do indeksu:
df.loc[df['m'].map(custom_dict).sort_values(ascending=False).index]
a b m
156 Dec
234 April
012 March
Więcej opcji jest dostępnych z astype(jest to obecnie przestarzałe) lub pd.Categorical, ale musisz określić ordered=True, aby działało poprawnie .
Podkreśliłeś już to, ale chciałbym powtórzyć na wypadek, gdyby ktoś inny go przejrzał i przegapił: ordered=NoneDomyślnie ustawia kategorię Pandy . Jeśli nie zostanie ustawione, kolejność będzie błędna lub zepsuje się na V23. W szczególności funkcja Max daje błąd TypeError (kategoria nie jest uporządkowana dla operacji max).
Dave Liu
16
Trochę późno w grze, ale oto sposób na utworzenie funkcji, która sortuje pandy Series, DataFrame i obiekty DataFrame z wieloma indeksami przy użyciu dowolnych funkcji.
Korzystam z df.iloc[index]metody, która odwołuje się do wiersza w Series / DataFrame według pozycji (w porównaniu z df.locodwołaniami według wartości). Używając tego, musimy po prostu mieć funkcję, która zwraca serię argumentów pozycyjnych:
defsort_pd(key=None,reverse=False,cmp=None):defsorter(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żesz użyć tego do tworzenia niestandardowych funkcji sortowania. Działa to na ramce danych użytej w odpowiedzi Andy'ego 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
012 March
234 April
156 Dec
Działa to również w przypadku obiektów DataFrames i Series z wieloma indeksami:
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]:
2816101271435885189238
Wydaje mi się, że jest to czyste, ale intensywnie wykorzystuje operacje w Pythonie, zamiast polegać na zoptymalizowanych operacjach pand. Nie wykonałem żadnych testów warunków skrajnych, ale wyobrażam sobie, że może to działać wolno na bardzo dużych ramkach DataFrame. Nie wiem, jak wypada porównanie wydajności z dodawaniem, sortowaniem, a następnie usuwaniem kolumny. Wszelkie wskazówki dotyczące przyspieszenia kodu będą mile widziane!
Czy to zadziała w przypadku sortowania wielu kolumn / indeksów?
ConanG
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 df.sort_index()do posortowania wszystkich poziomów indeksów.
Michael Delgado
9
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))
pd.Categorical
domyślnie nie interpretuje kategorii w kolejności. Zobacz tę odpowiedź .Odpowiedzi:
Pandy 0.15 wprowadziły serię kategorialną , która pozwala na znacznie jaśniejszy sposób:
Najpierw ustaw kolumnę miesiąca jako kategoryczną i określ kolejność, która ma być używana.
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 posortujesz kolumnę miesiąca, zostanie ona posortowana według tej listy:
In [23]: df.sort_values("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 przekonwertowana na NaN.
Starsza odpowiedź dla zainteresowanych ...
Możesz stworzyć serię pośrednią 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_values() 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 skomentowano, w nowszych pandach Series ma
replace
metodę, aby zrobić to bardziej elegancko:s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
Niewielka różnica polega na tym, że nie wzrośnie, jeśli wartość znajduje się poza słownikiem (po prostu pozostanie taka sama).
źródło
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
działa również dla linii 2 - tylko dla dobra każdego uczącego się pandy jak ja.apply({'March':0, 'April':1, 'Dec':3}.get)
:) W 0.15 będziemy mieli Serie / kolumny kategorialne, więc najlepszym sposobem będzie użycie tego i wtedy sortowanie po prostu zadziała.df.sort_values("m")
w nowszych pandach (zamiastdf.sort("m")
), w przeciwnym razie otrzymaszAttributeError: 'DataFrame' object has no attribute 'sort'
;)pandy> = 1.1
Wkrótce będziecie mogli korzystać
sort_values
zkey
argumentu:pd.__version__ # '1.1.0.dev0+2004.g8d10bfb6f' custom_dict = {'March': 0, 'April': 1, 'Dec': 3} df a b m 0 1 2 March 1 5 6 Dec 2 3 4 April df.sort_values(by=['m'], key=lambda x: x.map(custom_dict)) a b m 0 1 2 March 2 3 4 April 1 5 6 Dec
key
Argumentem bierze jako wejście serii i wraca serii. Ta seria jest wewnętrznie posortowana za pomocą argumentów, a posortowane indeksy są używane do zmiany kolejności wejściowej ramki DataFrame. Jeśli istnieje wiele kolumn do sortowania, funkcja klucza zostanie zastosowana do każdej z nich po kolei. Zobacz Sortowanie za pomocą kluczy .pandy <= 1.0.X
Jedną z prostych metod jest użycie wyjścia
Series.map
iSeries.argsort
indeksowanie dodf
usingDataFrame.iloc
(ponieważ argsort tworzy posortowane pozycje liczb całkowitych); ponieważ masz słownik; staje się to łatwe.df.iloc[df['m'].map(custom_dict).argsort()] a b m 0 1 2 March 2 3 4 April 1 5 6 Dec
Jeśli chcesz posortować w porządku malejącym , odwróć mapowanie.
df.iloc[(-df['m'].map(custom_dict)).argsort()] a b m 1 5 6 Dec 2 3 4 April 0 1 2 March
Zwróć uwagę, że działa to tylko w przypadku elementów numerycznych. W przeciwnym razie będziesz musiał obejść ten problem, używając
sort_values
i uzyskując dostęp do indeksu:df.loc[df['m'].map(custom_dict).sort_values(ascending=False).index] a b m 1 5 6 Dec 2 3 4 April 0 1 2 March
Więcej opcji jest dostępnych z
astype
(jest to obecnie przestarzałe) lubpd.Categorical
, ale musisz określićordered=True
, aby działało poprawnie .# Older version, # df['m'].astype('category', # categories=sorted(custom_dict, key=custom_dict.get), # ordered=True) df['m'] = pd.Categorical(df['m'], categories=sorted(custom_dict, key=custom_dict.get), ordered=True)
Teraz wystarczy prosty
sort_values
telefon:df.sort_values('m') a b m 0 1 2 March 2 3 4 April 1 5 6 Dec
Porządkowanie kategorialne będzie również uwzględniane podczas
groupby
sortowania wyników.źródło
ordered=None
Domyślnie ustawia kategorię Pandy . Jeśli nie zostanie ustawione, kolejność będzie błędna lub zepsuje się na V23. W szczególności funkcja Max daje błąd TypeError (kategoria nie jest uporządkowana dla operacji max).Trochę późno w grze, ale oto sposób na utworzenie funkcji, która sortuje pandy Series, DataFrame i obiekty DataFrame z wieloma indeksami przy użyciu dowolnych funkcji.
Korzystam z
df.iloc[index]
metody, która odwołuje się do wiersza w Series / DataFrame według pozycji (w porównaniu zdf.loc
odwołaniami według wartości). Używając tego, musimy po prostu mieć funkcję, która zwraca serię 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żesz użyć tego do tworzenia niestandardowych funkcji sortowania. Działa to na ramce danych użytej w odpowiedzi Andy'ego 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
Działa to również w przypadku obiektów DataFrames i Series z wieloma indeksami:
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
Wydaje mi się, że jest to czyste, ale intensywnie wykorzystuje operacje w Pythonie, zamiast polegać na zoptymalizowanych operacjach pand. Nie wykonałem żadnych testów warunków skrajnych, ale wyobrażam sobie, że może to działać wolno na bardzo dużych ramkach DataFrame. Nie wiem, jak wypada porównanie wydajności z dodawaniem, sortowaniem, a następnie usuwaniem kolumny. Wszelkie wskazówki dotyczące przyspieszenia kodu będą mile widziane!
źródło
df.sort_index()
do posortowania wszystkich poziomów indeksów.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 March, April, Dec
źródło