Phân loại tùy chỉnh trong khung dữ liệu gấu trúc


89

Tôi có khung dữ liệu pandas python, trong đó một cột chứa tên tháng.

Làm cách nào để thực hiện sắp xếp tùy chỉnh bằng từ điển, ví dụ:

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

1
Một cột chứa tên tháng có nghĩa là có một cột chứa tên tháng (như câu trả lời của tôi) hoặc nhiều cột có tên cột là tên tháng (như eumiro)?
Andy Hayden

1
Câu trả lời được chấp nhận đã lỗi thời và cũng không chính xác về mặt kỹ thuật, cũng như pd.Categoricalkhông giải thích các danh mục theo thứ tự theo mặc định. Xem câu trả lời này .
cs95

Câu trả lời:


141

Pandas 0.15 đã giới thiệu chuỗi Categorical , cho phép một cách rõ ràng hơn nhiều để làm điều này:

Đầu tiên, hãy đặt cột tháng thành một phân loại và chỉ định thứ tự sử dụng.

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

Bây giờ, khi bạn sắp xếp cột tháng, nó sẽ sắp xếp theo danh sách đó:

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

Lưu ý: nếu một giá trị không có trong danh sách, nó sẽ được chuyển đổi thành NaN.


Một câu trả lời cũ hơn cho những người quan tâm ...

Bạn có thể tạo một chuỗi trung gian và set_indextrên đó:

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

Như đã nhận xét, ở những con gấu trúc mới hơn, Series có một replacephương pháp để làm điều này một cách thanh lịch hơn:

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

Sự khác biệt nhỏ là điều này sẽ không tăng lên nếu có một giá trị bên ngoài từ điển (nó sẽ giữ nguyên).


s = df['m'].replace({'March':0, 'April':1, 'Dec':3})làm việc cho dòng 2 cũng - chỉ vì lợi ích của bất cứ ai học gấu trúc như tôi
kdauria

@kdauria chỗ tốt! (đã lâu kể từ khi tôi viết bài này!) .apply({'March':0, 'April':1, 'Dec':3}.get)Thay thế chắc chắn là lựa chọn tốt nhất, một lựa chọn khác là sử dụng :) Trong 0.15, chúng ta sẽ có Chuỗi / cột Categorical, vì vậy cách tốt nhất sẽ là sử dụng tùy chọn đó và sau đó sắp xếp sẽ hoạt động.
Andy Hayden

@AndyHayden Tôi đã tự do thay thế dòng thứ hai bằng phương thức 'thay thế'. Tôi hy vọng đó là Ok.
Faheem Mitha

@AndyHayden chỉnh sửa bị từ chối, nhưng tôi vẫn nghĩ đó là một thay đổi hợp lý.
Faheem Mitha

7
Chỉ cần chắc chắn rằng bạn sử dụng df.sort_values("m")trong gấu trúc mới hơn (thay vì df.sort("m")), nếu không bạn sẽ nhận được một AttributeError: 'DataFrame' object has no attribute 'sort';)
động não

17

gấu trúc> = 1,1

Bạn sẽ sớm có thể sử dụng sort_valuesvới keyđối số:

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

Đối keysố nhận đầu vào là một Chuỗi và trả về một Chuỗi. Chuỗi này được sắp xếp nội bộ và các chỉ số được sắp xếp được sử dụng để sắp xếp lại DataFrame đầu vào. Nếu có nhiều cột để sắp xếp, chức năng chính sẽ được áp dụng lần lượt cho từng cột. Xem Sắp xếp bằng phím .


gấu trúc <= 1.0.X

Một phương pháp đơn giản là sử dụng đầu ra Series.mapSeries.argsortlập chỉ mục để dfsử dụng DataFrame.iloc(vì argsort tạo ra các vị trí số nguyên được sắp xếp); vì bạn có từ điển; điều này trở nên dễ dàng.

df.iloc[df['m'].map(custom_dict).argsort()]

   a  b      m
0  1  2  March
2  3  4  April
1  5  6    Dec

Nếu bạn cần sắp xếp theo thứ tự giảm dần , hãy đảo ngược ánh xạ.

df.iloc[(-df['m'].map(custom_dict)).argsort()]

   a  b      m
1  5  6    Dec
2  3  4  April
0  1  2  March

Lưu ý rằng điều này chỉ hoạt động trên các mục số. Nếu không, bạn sẽ cần phải giải quyết vấn đề này bằng cách sử dụng sort_valuesvà truy cập chỉ mục:

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

Có nhiều tùy chọn hơn với astype(tính năng này hiện không được dùng nữa), hoặc pd.Categorical, nhưng bạn cần chỉ định ordered=Trueđể nó hoạt động chính xác .

# 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)

Bây giờ, một sort_valuescuộc gọi đơn giản sẽ thực hiện thủ thuật:

df.sort_values('m')
 
   a  b      m
0  1  2  March
2  3  4  April
1  5  6    Dec

Thứ tự phân loại cũng sẽ được thực hiện khi groupbysắp xếp đầu ra.


2
Bạn đã nhấn mạnh nó rồi, nhưng tôi muốn nhắc lại trong trường hợp người khác đọc lướt và bỏ sót nó: Bộ phân loại Pandas ordered=Nonetheo mặc định. Nếu không được đặt, thứ tự sẽ bị sai hoặc bị hỏng trên V23. Đặc biệt, hàm Max đưa ra một TypeError (Categorical không được sắp xếp cho hoạt động max).
Dave Liu

16

Trò chơi hơi muộn, nhưng đây là cách để tạo một hàm sắp xếp các đối tượng Dòng gấu trúc, DataFrame và Multiindex DataFrame bằng các hàm tùy ý.

Tôi sử dụng df.iloc[index]phương thức này, tham chiếu một hàng trong Series / DataFrame theo vị trí (so với df.loc, tham chiếu theo giá trị). Sử dụng điều này, chúng ta chỉ cần có một hàm trả về một loạt các đối số vị trí:

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

Bạn có thể sử dụng điều này để tạo các chức năng sắp xếp tùy chỉnh. Điều này hoạt động trên khung dữ liệu được sử dụng trong câu trả lời của Andy Hayden:

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

Điều này cũng hoạt động trên các đối tượng DataFrames và Series đa chỉ mục:

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

Đối với tôi, điều này cảm thấy sạch sẽ, nhưng nó sử dụng nhiều hoạt động của python thay vì dựa vào các hoạt động của gấu trúc được tối ưu hóa. Tôi chưa thực hiện bất kỳ thử nghiệm căng thẳng nào nhưng tôi tưởng tượng điều này có thể bị chậm trên các DataFrame rất lớn. Không chắc chắn hiệu suất như thế nào so với thêm, sắp xếp, sau đó xóa một cột. Bất kỳ mẹo nào về tăng tốc mã sẽ được đánh giá cao!


Điều này có hoạt động để sắp xếp nhiều cột / chỉ số không?
ConanG

có, nhưng câu trả lời đã chọn là một cách tốt hơn nhiều để làm điều này. Nếu bạn có nhiều chỉ mục, chỉ cần sắp xếp chúng theo thứ tự sắp xếp bạn thích, sau đó sử dụng df.sort_index()để sắp xếp tất cả các cấp chỉ mục.
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))

trả về DataFrame với các cột Tháng 3, Tháng 4, Tháng 12


Điều này sắp xếp các cột thực tế, thay vì sắp xếp các hàng dựa trên vị từ tùy chỉnh trên cột?
cs95
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.