Làm cách nào để xóa một cột chỉ chứa các số không trong Pandas?


87

Tôi hiện có một khung dữ liệu bao gồm các cột có giá trị là 1 và 0, tôi muốn lặp qua các cột và xóa những cột chỉ được tạo thành từ 0. Đây là những gì tôi đã thử cho đến nay:

ones = []
zeros = []
for year in years:
    for i in range(0,599):
        if year[str(i)].values.any() == 1:
            ones.append(i)
        if year[str(i)].values.all() == 0:
            zeros.append(i)
    for j in ones:
        if j in zeros:
            zeros.remove(j)
    for q in zeros:
        del year[str(q)]

Trong đó năm là danh sách các khung dữ liệu cho các năm khác nhau mà tôi đang phân tích, các khung này bao gồm các cột có một trong đó và các số không là danh sách các cột chứa tất cả các số không. Có cách nào tốt hơn để xóa một cột dựa trên một điều kiện không? Vì một số lý do, tôi phải kiểm tra xem các cột đó có nằm trong danh sách số không hay không và xóa chúng khỏi danh sách số không để có được danh sách tất cả các cột 0.


Câu trả lời:


215
df.loc[:, (df != 0).any(axis=0)]

Dưới đây là phân tích về cách nó hoạt động:

In [74]: import pandas as pd

In [75]: df = pd.DataFrame([[1,0,0,0], [0,0,1,0]])

In [76]: df
Out[76]: 
   0  1  2  3
0  1  0  0  0
1  0  0  1  0

[2 rows x 4 columns]

df != 0tạo một DataFrame boolean là True trong đó dfnonzero:

In [77]: df != 0
Out[77]: 
       0      1      2      3
0   True  False  False  False
1  False  False   True  False

[2 rows x 4 columns]

(df != 0).any(axis=0)trả về một Chuỗi boolean cho biết cột nào có các mục nhập khác không. (Phép anytoán tổng hợp các giá trị dọc theo trục 0 - tức là dọc theo các hàng - thành một giá trị boolean duy nhất. Do đó, kết quả là một giá trị boolean cho mỗi cột.)

In [78]: (df != 0).any(axis=0)
Out[78]: 
0     True
1    False
2     True
3    False
dtype: bool

df.loccó thể được sử dụng để chọn các cột đó:

In [79]: df.loc[:, (df != 0).any(axis=0)]
Out[79]: 
   0  2
0  1  0
1  0  1

[2 rows x 2 columns]

Để "xóa" các cột 0, hãy gán lại df:

df = df.loc[:, (df != 0).any(axis=0)]

Tôi đang cố gắng điều này để giảm một cột nếu nó có 0 hoặc 1 trong đó và nó xuất hiện lỗi: df = df.loc [:, (df! = 0 & df! = 1) .any (axis = 0)]
morpheus

1
df.loc[:, (~df.isin([0,1])).any(axis=0)]cũng sẽ hoạt động.
unutbu

1
@IgorFobia: Nhiều thứ là Sai mà không phải là 0. Ví dụ: chuỗi rỗng hoặc Không có hoặc NaN. Để chứng minh sự khác biệt, if df = pd.DataFrame([[np.nan]*10]), sau đó df.loc[:, df.any(axis=0)]trả về một DataFrame trống, trong khi df.loc[:, (df != 0).any(axis=0)]trả về một DataFrame có 10 cột.
unutbu

4
Tôi tin rằng sẽ dễ hiểu hơn nếu chúng ta kiểm tra một điều kiện là đúng, thay vì kiểm tra nếu điều kiện không đúng thì không bao giờ là không thỏa mãn. Tôi nghĩ (df == 0).all(axis=0)là thẳng thắn hơn.
Ryszard Cetnarski

2
Cảm ơn vì sự cố. Nó làm cho mọi thứ rất rõ ràng.
Regi Mathew

7

Đây là một cách thay thế để sử dụng là

df.replace(0,np.nan).dropna(axis=1,how="all")

So với giải pháp của unutbu, cách này rõ ràng là chậm hơn:

%timeit df.loc[:, (df != 0).any(axis=0)]
652 µs ± 5.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.replace(0,np.nan).dropna(axis=1,how="all")
1.75 ms ± 9.49 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

0

Trong trường hợp bạn muốn một cách rõ ràng hơn để lấy các tên cột 0 để bạn có thể in / ghi nhật ký chúng và thả chúng tại chỗ theo tên của chúng :

zero_cols = [ col for col, is_zero in ((df == 0).sum() == df.shape[0]).items() if is_zero ]
df.drop(zero_cols, axis=1, inplace=True)

Một số chia nhỏ:

# a pandas Series with {col: is_zero} items
# is_zero is True when the number of zero items in that column == num_all_rows
(df == 0).sum() == df.shape[0])

# a list comprehension of zero_col_names is built from the_series
[ col for col, is_zero in the_series.items() if is_zero ]
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.