2015-07-08 41 views
9

Trên khung dữ liệu gấu, tôi biết tôi có thể nhóm trên một hoặc nhiều cột và sau đó lọc các giá trị xuất hiện nhiều hơn/ít hơn số đã cho.Pandas: Lọc dataframe cho các giá trị quá thường xuyên hoặc quá hiếm

Nhưng tôi muốn thực hiện điều này trên mọi cột trên khung dữ liệu. Tôi muốn xóa các giá trị quá ít (giả sử xảy ra ít hơn 5% số lần) hoặc quá thường xuyên. Ví dụ, hãy xem xét một khung dữ liệu với các cột sau: city of origin, city of destination, distance, type of transport (air/car/foot), time of day, price-interval.

import pandas as pd 
import string 
import numpy as np 
vals = [(c, np.random.choice(list(string.lowercase), 100, replace=True)) for c in 
    'city of origin', 'city of destination', 'distance, type of transport (air/car/foot)', 'time of day, price-interval'] 
df = pd.DataFrame(dict(vals)) 
>> df.head() 
    city of destination  city of origin distance, type of transport (air/car/foot) time of day, price-interval 
0 f p a n 
1 k b a f 
2 q s n j 
3 h c g u 
4 w d m h 

Nếu đây là một dataframe lớn, nó làm cho tinh thần để loại bỏ các hàng có mặt hàng giả mạo, ví dụ, nếu time of day = night xảy ra chỉ 3% thời gian, hoặc nếu foot phương thức vận tải là rất hiếm, và vân vân .

Tôi muốn xóa tất cả các giá trị như vậy khỏi tất cả các cột (hoặc danh sách các cột). Một ý tưởng tôi có là làm một value_counts trên mỗi cột, transform và thêm một cột cho mỗi giá trị_giá trị; sau đó lọc dựa trên việc chúng có ở trên hay dưới ngưỡng. Nhưng tôi nghĩ rằng phải có một cách tốt hơn để đạt được điều này?

+0

Hãy xem sklearn tính năng phát hiện – Moritz

Trả lời

7

Quy trình này sẽ đi qua từng cột của DataFrame và loại bỏ các hàng có danh mục nhất định nhỏ hơn tỷ lệ ngưỡng đã cho, thu hẹp DataFrame trên mỗi vòng lặp.

Câu trả lời này cũng tương tự như được cung cấp bởi @Ami Tavory, nhưng với một vài sự khác biệt tinh tế:

  • Nó bình thường hóa đếm giá trị vì vậy bạn chỉ có thể sử dụng một ngưỡng phần trăm.
  • Tính toán chỉ tính một lần cho mỗi cột thay vì hai lần. Điều này dẫn đến việc thực hiện nhanh hơn.

Code:

threshold = 0.03 
for col in df: 
    counts = df[col].value_counts(normalize=True) 
    df = df.loc[df[col].isin(counts[counts > threshold].index), :] 

Mã thời gian:

df2 = pd.DataFrame(np.random.choice(list(string.lowercase), [1e6, 4], replace=True), 
        columns=list('ABCD')) 

%%timeit df=df2.copy() 
threshold = 0.03 
for col in df: 
    counts = df[col].value_counts(normalize=True) 
    df = df.loc[df[col].isin(counts[counts > threshold].index), :] 

1 loops, best of 3: 485 ms per loop 

%%timeit df=df2.copy() 
m = 0.03 * len(df) 
for c in df: 
    df = df[df[c].isin(df[c].value_counts()[df[c].value_counts() > m].index)] 

1 loops, best of 3: 688 ms per loop 
+0

bạn nên chỉnh sửa 'cho col trong df' thành:' cho col trong df.columns' – vpk

+0

Cách khác xung quanh (bạn không cần phải xác định '.columns'). Cảm ơn bạn đã chỉ ra sự không thống nhất. – Alexander

2

Tôi mới sử dụng Python và sử dụng Pandas. Tôi đã đưa ra giải pháp sau đây. Có lẽ những người khác có thể có cách tiếp cận tốt hơn hoặc hiệu quả hơn.

Giả sử khung dữ liệu của bạn là DF, bạn có thể sử dụng mã sau bên dưới để lọc ra tất cả các giá trị không thường xuyên. Chỉ cần chắc chắn cập nhật biến số colbin_freq. DF_Filtered là DataFrame được lọc mới của bạn.

# Column you want to filter 
col = 'time of day' 

# Set your frequency to filter out. Currently set to 5% 
bin_freq = float(5)/float(100) 

DF_Filtered = pd.DataFrame() 

for i in DF[col].unique(): 
    counts = DF[DF[col]==i].count()[col] 
    total_counts = DF[col].count() 
    freq = float(counts)/float(total_counts) 

    if freq > bin_freq: 
     DF_Filtered = pd.concat([DF[DF[col]==i],DF_Filtered]) 

print DF_Filtered 
3

tôi sẽ đi với một trong các cách sau:

Lựa chọn A

m = 0.03 * len(df) 
df[np.all(
    df.apply(
     lambda c: c.isin(c.value_counts()[c.value_counts() > m].index).as_matrix()), 
    axis=1)] 

Giải thích:

  • m = 0.03 * len(df) là ngưỡng (tốt nhất là lấy hằng số ra khỏi biểu thức phức tạp)

  • df[np.all(..., axis=1)] giữ lại các hàng ở đó một số điều kiện được lấy trên tất cả các cột.

  • df.apply(...).as_matrix áp dụng chức năng cho tất cả các cột và tạo ma trận kết quả.

  • c.isin(...) kiểm tra, cho từng mục cột, cho dù là trong một số bộ.

  • c.value_counts()[c.value_counts() > m].index là tập hợp tất cả các giá trị trong một cột có số trên m.

Lựa chọn B

m = 0.03 * len(df) 
for c in df.columns: 
    df = df[df[c].isin(df[c].value_counts()[df[c].value_counts() > m].index)] 

Lời giải thích tương tự như trên.


Cân bằng:

  • Cá nhân, tôi thấy B dễ đọc hơn.

  • B tạo một DataFrame mới cho mỗi lần lọc cột; cho DataFrames lớn, nó có thể đắt hơn.

+0

Tôi thấy điều này trong một thread SO (http://stackoverflow.com/a/ 23202269/228177): df_f = df [(np.abs (stats.zscore (df)) <2) .all (axis = 1)] Bộ lọc này lọc tất cả các giá trị có độ lệch chuẩn 2, sử dụng điểm z kiểm tra. Đề xuất của bạn trông cũng tương tự. Cảm ơn – vpk

+1

Điều đó đúng - nó tương tự. FWIW, tôi nghĩ câu trả lời có những gì bạn nên sử dụng, trong khi câu trả lời ở đây là một câu trả lời nghiêm ngặt cho câu hỏi của bạn. –

+0

BTW, chỉ để tạo một MWE, làm thế nào để tạo ra một loạt các bảng chữ cái ngẫu nhiên, hoặc một tập hợp các bảng chữ cái? ví dụ: [A, A, A, B, Z, X, X, A]. Bạn tạo chuỗi số bằng np.random.randn (10), nhưng có cái gì đó tương tự cho chuỗi? – vpk

0

DataFrames hỗ trợ clip_lower(threshold, axis=None)clip_upper(threshold, axis=None), mà loại bỏ tất cả các giá trị bên dưới hoặc ở trên (tương ứng) một ngưỡng nhất định.

+0

Xin lỗi, nhưng điều này * cắt ngắn *, là 1. hoàn toàn khác nhau và 2. áp dụng cho các giá trị số. –

Các vấn đề liên quan