Xóa các hàng khỏi DataFrame dựa trên biểu thức có điều kiện liên quan đến len (chuỗi) cho KeyError


303

Tôi có một DataFrame của gấu trúc và tôi muốn xóa các hàng khỏi nó trong đó độ dài của chuỗi trong một cột cụ thể lớn hơn 2.

Tôi hy vọng có thể làm điều này (theo câu trả lời này ):

df[(len(df['column name']) < 2)]

nhưng tôi chỉ nhận được lỗi:

KeyError: u'no item named False'

Tôi đang làm gì sai?

(Lưu ý: Tôi biết tôi có thể sử dụng df.dropna()để loại bỏ các hàng có chứa bất kỳ NaN, nhưng tôi không thấy cách loại bỏ các hàng dựa trên biểu thức điều kiện.)

Câu trả lời:


168

Khi bạn làm, len(df['column name'])bạn chỉ nhận được một số, cụ thể là số lượng hàng trong DataFrame (nghĩa là độ dài của chính cột). Nếu bạn muốn áp dụng lencho từng thành phần trong cột, hãy sử dụng df['column name'].map(len). Hãy thử

df[df['column name'].map(len) < 2]

3
Tôi đã đưa ra một cách sử dụng một sự hiểu biết danh sách: df[[(len(x) < 2) for x in df['column name']]]nhưng của bạn đẹp hơn nhiều. Cảm ơn bạn đã giúp đỡ!
sjs

13
Trong trường hợp ai đó cần một sự so sánh phức tạp hơn, lambda luôn có thể được sử dụng. df[df['column name'].map(lambda x: str(x)!=".")]
4lber đến

1
Vì một số lý do, không có tùy chọn nào khác phù hợp với tôi, ngoại trừ tùy chọn được đăng bởi @ 4lberto. Tôi pandas 0.23.4và trăn 3.6
goelakash

1
Tôi sẽ thêm một .copy()ở cuối, trong trường hợp bạn muốn chỉnh sửa khung dữ liệu này (ví dụ: việc gán các cột mới sẽ tăng "Giá trị đang cố gắng đặt trên một bản sao của một lát cắt từ cảnh báo DataFrame".
PlasmaBinturong

806

Để trả lời trực tiếp tiêu đề ban đầu của câu hỏi này "Cách xóa hàng khỏi gấu trúc DataFrame dựa trên biểu thức điều kiện" (mà tôi hiểu không nhất thiết là vấn đề của OP nhưng có thể giúp người dùng khác gặp phải câu hỏi này) một cách để làm điều này là sử dụng những giọt phương pháp:

df = df.drop(some labels)

df = df.drop(df[<some boolean condition>].index)

Thí dụ

Để xóa tất cả các hàng trong đó cột 'điểm' <50:

df = df.drop(df[df.score < 50].index)

Trong phiên bản (như đã nêu trong các ý kiến)

df.drop(df[df.score < 50].index, inplace=True)

Nhiều điều kiện

(xem Lập chỉ mục Boolean )

Các toán tử là: |for or, &for and~for not. Chúng phải được nhóm lại bằng cách sử dụng dấu ngoặc đơn.

Để xóa tất cả các hàng trong đó cột 'điểm' là <50 và> 20

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)


32
Tôi chỉ muốn nhận xét rằng chức năng thả hỗ trợ thay thế tại chỗ. I E,. giải pháp của bạn giống như df.drop (df [df.score <50] .index, inplace = True). Tuy nhiên, không biết thủ thuật "chỉ mục". Đã giúp tôi rất nhiều
Quickbeam2k1

9
Chỉ muốn chỉ ra rằng trước khi bạn sử dụng thủ thuật chỉ mục này, bạn cần chắc chắn rằng các giá trị chỉ mục của bạn là duy nhất (hoặc gọi reset_index()). Tôi đã tìm thấy điều này một cách khó khăn khi đường đến nhiều hàng bị rớt khỏi khung dữ liệu của tôi.
Jay

3
Làm thế nào để tôi thả tất cả các hàng trong đó loại cột là str? Tôi muốn chỉ giữ các loại danh sách cột. Tôi đã thử test = df.drop(df[df['col1'].dtype == str].index)nhưng tôi nhận được lỗi KeyError: False tôi cũng đã thử df.drop(df[df.col1.dtype == str].index)df.drop(df[type(df.cleaned_norm_email) == str].index)dường như không có gì để làm việc? Ai có thể tư vấn. Cảm ơn! @ Người dùng
PyRsquared

1
Đây là một câu hỏi cũ nhưng ... @ cá bị thách thức dưới nước nhanh hơn câu hỏi này rất nhiều. Lưu ý rằng bạn tính toán df[(df.score < 50) & (df.score > 20)]như là một phần của câu trả lời của bạn. Nếu bạn đảo ngược điều này để làm, df = df[(df.score >= 50) | (df.score <= 20)]bạn sẽ nhận được câu trả lời của bạn nhanh hơn nhiều.
Roobie Nuby

1
@RoobieNuby - chúng không giống nhau.
Nguai al

106

Bạn có thể gán DataFramephiên bản được lọc của chính nó:

df = df[df.score > 50]

Điều này nhanh hơn drop:

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test[test.x < 0]
# 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test.drop(test[test.x > 0].index, inplace=True)
# 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test.drop(test[test.x > 0].index)
# 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Làm cách nào để kiểm tra nhiều cột bằng cách sử dụng hoặc điều kiện?
Piyush S. Wanare


9

Tôi sẽ mở rộng trên giải pháp chung của @ User để cung cấp giải pháp dropthay thế miễn phí. Điều này dành cho những người được hướng dẫn ở đây dựa trên tiêu đề của câu hỏi (không phải vấn đề của OP)

Giả sử bạn muốn xóa tất cả các hàng có giá trị âm. Một giải pháp lót là: -

df = df[(df > 0).all(axis=1)]

Từng bước giải thích: -

Hãy tạo khung dữ liệu phân phối bình thường ngẫu nhiên 5x5

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

Hãy để điều kiện được xóa tiêu cực. Một df boolean thỏa mãn điều kiện: -

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

Một chuỗi boolean cho tất cả các hàng thỏa mãn điều kiện Lưu ý nếu bất kỳ phần tử nào trong hàng không thành công thì điều kiện hàng được đánh dấu là false

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

Cuối cùng lọc ra các hàng từ khung dữ liệu dựa trên điều kiện

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

Bạn có thể gán lại cho df để thực sự xóa vs bộ lọc được thực hiện ở trên
df = df[(df > 0).all(axis=1)]

Điều này có thể dễ dàng được mở rộng để lọc ra các hàng có chứa NaN (các mục không phải là số): -
df = df[(~df.isnull()).all(axis=1)]

Điều này cũng có thể được đơn giản hóa cho các trường hợp như: Xóa tất cả các hàng trong đó cột E âm

df = df[(df.E>0)]

Tôi muốn kết thúc với một số thống kê định hình về lý do tại sao dropgiải pháp của @ Người dùng chậm hơn so với lọc dựa trên cột thô: -

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Một cột về cơ bản Serieslà một NumPymảng, nó có thể được lập chỉ mục mà không phải trả bất kỳ chi phí nào. Đối với những người quan tâm đến cách tổ chức bộ nhớ cơ bản đóng vai trò vào tốc độ thực thi ở đây là một Liên kết tuyệt vời về Tăng tốc Pandas :


6

Trong gấu trúc, bạn có thể thực hiện str.lenvới ranh giới của mình và sử dụng kết quả Boolean để lọc nó.

df[df['column name'].str.len().lt(2)]

3

Nếu bạn muốn thả các hàng của khung dữ liệu trên cơ sở một số điều kiện phức tạp trên giá trị cột thì việc viết theo cách hiển thị ở trên có thể phức tạp. Tôi có giải pháp đơn giản hơn luôn luôn hoạt động. Chúng ta hãy giả sử rằng bạn muốn thả cột có 'tiêu đề' để có được cột đó trong danh sách trước.

text_data = df['name'].tolist()

bây giờ áp dụng một số chức năng trên mọi yếu tố của danh sách và đưa nó vào chuỗi gấu trúc:

text_length = pd.Series([func(t) for t in text_data])

trong trường hợp của tôi, tôi chỉ cố gắng để có được số lượng mã thông báo:

text_length = pd.Series([len(t.split()) for t in text_data])

Bây giờ thêm một cột bổ sung với chuỗi trên trong khung dữ liệu:

df = df.assign(text_length = text_length .values)

bây giờ chúng ta có thể áp dụng điều kiện trên cột mới, chẳng hạn như:

df = df[df.text_length  >  10]
def pass_filter(df, label, length, pass_type):

    text_data = df[label].tolist()

    text_length = pd.Series([len(t.split()) for t in text_data])

    df = df.assign(text_length = text_length .values)

    if pass_type == 'high':
        df = df[df.text_length  >  length]

    if pass_type == 'low':
        df = df[df.text_length  <  length]

    df = df.drop(columns=['text_length'])

    return df
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.