2015-07-08 26 views
9

Na pandasowej ramce danych wiem, że mogę grupować jedną lub więcej kolumn, a następnie filtrować wartości, które występują więcej/mniej niż podana liczba.Pandy: filtruj ramkę danych dla wartości, które są zbyt częste lub zbyt rzadkie.

Ale chcę to zrobić w każdej kolumnie na ramce danych. Chcę usunąć wartości, które są zbyt rzadkie (powiedzmy, że występują mniej niż 5% razy) lub zbyt często. Jako przykład można rozważyć ramkę danych z następującymi kolumnami: 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 

Jeśli jest to duża dataframe, ma sens, aby usunąć wiersze, które mają fałszywe przedmioty, na przykład, jeśli time of day = night występuje tylko 3% czasu, lub jeśli foot środek transportu jest rzadkie, i tak dalej .

Chcę usunąć wszystkie takie wartości ze wszystkich kolumn (lub listy kolumn). Jednym z pomysłów jest wykonanie value_counts w każdej kolumnie, transform i dodanie jednej kolumny dla każdego value_counts; następnie filtruj na podstawie tego, czy są powyżej czy poniżej progu. Ale myślę, że musi być lepszy sposób na osiągnięcie tego?

+0

Spójrz sklearn wykrywania funkcji – Moritz

Odpowiedz

7

Ta procedura przechodzi przez każdą kolumnę elementu DataFrame i eliminuje wiersze, w których dana kategoria jest mniejsza od określonego progu procentowego, zmniejszając wartość DataFrame w każdej pętli.

Ta odpowiedź jest podobna do przewidzianego przez @Ami Tavory, ale z kilku subtelnych różnic:

  • normalizuje ona liczy się wartość, dzięki czemu można po prostu użyć próg percentyla.
  • Oblicza liczbę tylko raz na kolumnę zamiast dwa razy. Powoduje to szybsze wykonanie.

Kod:

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

czas Kod:

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

powinieneś edytować 'dla col in df' na:' dla col in df.columns' – vpk

+0

W inny sposób (nie musisz określać '.columns'). Dziękuję za wskazanie niespójności. – Alexander

2

Jestem nowy w Pythonie i korzystam z Pand. Poniżej przedstawiam poniższe rozwiązanie. Być może inne osoby mogą mieć lepsze lub bardziej efektywne podejście.

Zakładając, że DataFrame to DF, możesz użyć poniższego kodu poniżej, aby odfiltrować wszystkie rzadkie wartości. Po prostu zaktualizuj zmienną col i bin_freq. DF_Filtered to Twoja nowa filtrowana DataFrame.

# 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

chciałbym przejść z jednego z następujących powodów:

Wariant 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)] 

Objaśnienie:

  • m = 0.03 * len(df) to próg (dobrze jest wziąć stałą ze skomplikowanej frazy).

  • df.apply(...).as_matrix stosuje funkcję do wszystkich kolumn i tworzy macierz wyników.

  • c.isin(...) sprawdza, dla każdego elementu kolumny, czy jest w jakimś zestawie.

  • c.value_counts()[c.value_counts() > m].index to zestaw wszystkich wartości w kolumnie, której liczba jest większa niż m.

Wariant 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)] 

Wyjaśnienie jest podobny do tego powyżej.


Kompromisy:

  • Osobiście uważam B bardziej czytelny.

  • B tworzy nową ramkę DataFrame dla każdego filtrowania kolumny; w przypadku dużych DataFrames jest to prawdopodobnie droższe.

+0

I znaleziono w tym w innym SO gwintu (http://stackoverflow.com/a/ 23202269/228177): df_f = df [(np.abs (stats.zscore (df)) <2) .all (oś = 1)] Filtruje wszystkie wartości, które są 2 odchyleniami standardowymi, używając z-score test. Twoje sugestie również wyglądają podobnie. Dzięki – vpk

+1

To prawda - jest podobnie. FWIW, myślę, że odpowiedź na to pytanie jest tym, czego powinieneś użyć, podczas gdy odpowiedź tutaj jest ścisłą odpowiedzią na twoje pytanie. –

+0

BTW, aby utworzyć MWE, jak stworzyć serię losowych alfabetów lub zestawu alfabetów? np. [A, A, A, B, Z, X, X, A]. Tworzysz serie numeryczne według np.random.randn (10), ale czy istnieje coś podobnego do ciągów? – vpk

0

DataFrames wspierać clip_lower(threshold, axis=None) i clip_upper(threshold, axis=None), które usuwają wszystkie wartości poniżej lub powyżej (odpowiednio) określonego progu.

+0

Przepraszam, ale ten * obcina *, który jest 1. całkowicie inny i 2. dotyczy wartości numerycznych. –

Powiązane problemy