Gấu trúc: giảm một cấp từ chỉ số cột đa cấp?


242

Nếu tôi có chỉ số cột đa cấp:

>>> cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
>>> pd.DataFrame([[1,2], [3,4]], columns=cols)
    một
   --- + -
    b | c
- + --- + -
0 | 1 | 2
1 | 3 | 4

Làm cách nào tôi có thể giảm mức "a" của chỉ mục đó, vì vậy tôi kết thúc bằng:

    b | c
- + --- + -
0 | 1 | 2
1 | 3 | 4

3
Sẽ thật tuyệt nếu có một phương thức DataFrame thực hiện điều đó cho cả chỉ mục và cột. Hoặc là giảm hoặc chọn mức chỉ số.
Sören

@ Sören Kiểm tra stackoverflow.com/a/56080234/3198568 . droplevelcác tác phẩm có thể hoạt động trên các chỉ mục đa cấp hoặc các cột thông qua tham số axis.
irene

Câu trả lời:


306

Bạn có thể sử dụng MultiIndex.droplevel:

>>> cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
>>> df = pd.DataFrame([[1,2], [3,4]], columns=cols)
>>> df
   a   
   b  c
0  1  2
1  3  4

[2 rows x 2 columns]
>>> df.columns = df.columns.droplevel()
>>> df
   b  c
0  1  2
1  3  4

[2 rows x 2 columns]

55
Có lẽ tốt nhất để nói rõ ràng mức nào đang bị giảm. Các cấp độ được lập chỉ mục 0 bắt đầu từ đầu. >>> df.columns = df.columns.droplevel(0)
Ted Petrou

6
Nếu chỉ mục bạn đang cố gắng thả ở bên trái (hàng) chứ không phải phía trên (cột), bạn có thể thay đổi "cột" thành "chỉ mục" và sử dụng cùng một phương pháp: >>> df.index = df.index.droplevel(1)
Idodo

7
Trong phiên bản Panda 0.23.4, df.columns.droplevel() không còn khả dụng.
yoonghm

8
@yoonghm Nó ở đó, có lẽ bạn chỉ đang gọi nó trên các cột không có nhiều chỉ mục
harrison matt

1
Tôi đã có ba cấp độ sâu và muốn giảm xuống chỉ còn cấp trung bình. Tôi thấy rằng việc giảm mức thấp nhất (cấp [2]) và sau đó mức cao nhất (cấp [0]) hoạt động tốt nhất. >>>df.columns = df.columns.droplevel(2) >>>df.columns = df.columns.droplevel(0)
Kyle C

65

Một cách khác để loại bỏ chỉ mục là sử dụng cách hiểu danh sách:

df.columns = [col[1] for col in df.columns]

   b  c
0  1  2
1  3  4

Chiến lược này cũng hữu ích nếu bạn muốn kết hợp các tên từ cả hai cấp độ như trong ví dụ bên dưới nơi cấp độ dưới cùng chứa hai chữ cái:

cols = pd.MultiIndex.from_tuples([("A", "x"), ("A", "y"), ("B", "y")])
df = pd.DataFrame([[1,2, 8 ], [3,4, 9]], columns=cols)

   A     B
   x  y  y
0  1  2  8
1  3  4  9

Giảm mức cao nhất sẽ để lại hai cột có chỉ số 'y'. Điều đó có thể tránh được bằng cách nối các tên với sự hiểu biết danh sách.

df.columns = ['_'.join(col) for col in df.columns]

    A_x A_y B_y
0   1   2   8
1   3   4   9

Đó là một vấn đề tôi gặp phải sau khi làm một nhóm và phải mất một thời gian để tìm thấy câu hỏi khác đã giải quyết nó. Tôi thích nghi giải pháp đó cho trường hợp cụ thể ở đây.


2
[col[1] for col in df.columns]là trực tiếp hơn df.columns.get_level_values(1).
Eric O Lebigot

2
Có một nhu cầu tương tự trong đó một số cột có giá trị mức trống. Được sử dụng như sau:[col[0] if col[1] == '' else col[1] for col in df.columns]
Logan

43

Một cách khác để làm điều này là gán lại dfdựa trên một mặt cắt ngang df, sử dụng phương thức .xs .

>>> df

    a
    b   c
0   1   2
1   3   4

>>> df = df.xs('a', axis=1, drop_level=True)

    # 'a' : key on which to get cross section
    # axis=1 : get cross section of column
    # drop_level=True : returns cross section without the multilevel index

>>> df

    b   c
0   1   2
1   3   4

1
Điều này chỉ hoạt động bất cứ khi nào có một nhãn duy nhất cho toàn bộ cấp cột.
Ted Petrou

1
Không hoạt động khi bạn muốn giảm cấp độ thứ hai.
Sören

Đây là một giải pháp tốt nếu bạn muốn cắt và thả cho cùng cấp. Nếu bạn muốn cắt ở cấp độ thứ hai (giả sử b) thì hãy giảm cấp độ đó và để lại cấp độ đầu tiên ( a), sau đây sẽ hoạt động:df = df.xs('b', axis=1, level=1, drop_level=True)
Tiffany G. Wilson

27

Kể từ Pandas 0.24.0 , giờ đây chúng ta có thể sử dụng DataFrame.droplevel () :

cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
df = pd.DataFrame([[1,2], [3,4]], columns=cols)

df.droplevel(0, axis=1) 

#   b  c
#0  1  2
#1  3  4

Điều này rất hữu ích nếu bạn muốn duy trì chuỗi phương thức DataFrame của mình.


Đây là giải pháp "tinh khiết nhất" trong đó DataFrame mới được trả về thay vì phải sửa đổi "tại chỗ".
EliadL

16

Bạn cũng có thể đạt được điều đó bằng cách đổi tên các cột:

df.columns = ['a', 'b']

Điều này bao gồm một bước thủ công nhưng có thể là một tùy chọn đặc biệt là nếu cuối cùng bạn sẽ đổi tên khung dữ liệu của mình.


Đây thực chất là những gì câu trả lời đầu tiên của Mint. Bây giờ, cũng không cần phải chỉ định danh sách các tên (thường là tẻ nhạt), vì nó được trao cho bạn bởi df.columns.get_level_values(1).
Eric O Lebigot

12

Một mẹo nhỏ sử dụng sum với level = 1 (hoạt động khi level = 1 là duy nhất)

df.sum(level=1,axis=1)
Out[202]: 
   b  c
0  1  2
1  3  4

Giải pháp phổ biến hơn get_level_values

df.columns=df.columns.get_level_values(1)
df
Out[206]: 
   b  c
0  1  2
1  3  4

4

Tôi đã vật lộn với vấn đề này vì tôi không biết tại sao hàm droplevel () của tôi không hoạt động. Làm việc qua một số và tìm hiểu rằng 'a' trong bảng của bạn là tên cột và 'b', 'c' là chỉ mục. Làm như thế này sẽ giúp

df.columns.name = None
df.reset_index() #make index become label

1
Điều này không tái tạo đầu ra mong muốn ở tất cả.
Eric O Lebigot

Dựa vào ngày này được đăng, mức giảm có thể không được bao gồm trong phiên bản Pandas của bạn (nó đã được thêm vào phiên bản ổn định, 24.0, vào tháng 1 năm 2019)
LinkBerest
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.