2016-05-15 18 views
3

Ich habe folgende Pandas Datenrahmen:Werte ändern in Pandas Datenrahmen nach value_counts()

import pandas as pd 
from pandas import Series, DataFrame 

data = DataFrame({'Qu1': ['apple', 'potato', 'cheese', 'banana', 'cheese', 'banana', 'cheese', 'potato', 'egg'], 
       'Qu2': ['sausage', 'banana', 'apple', 'apple', 'apple', 'sausage', 'banana', 'banana', 'banana'], 
       'Qu3': ['apple', 'potato', 'sausage', 'cheese', 'cheese', 'potato', 'cheese', 'potato', 'egg']}) 

Ich mag würde Werte in Spalten ändern Qu1, Qu2, Qu3 nach value_counts(), wenn der Zählwert groß oder gleich einige Anzahl

zum Beispiel für Qu1 Spalte

>>> pd.value_counts(data.Qu1) >= 2 
cheese  True 
potato  True 
banana  True 
apple  False 
egg  False 

Ich möchte Werte cheese, potato, banana behalten, weil jeder Wert mindestens zwei Erscheinungen hat.

Von Werte apple und egg Ich möchte Wert others

Für Qu2 keine Änderungen Spalte erstellen:

>>> pd.value_counts(data.Qu2) >= 2 
banana  True 
apple  True 
sausage True 

das Endergebnis wie in test_data angebracht

test_data = DataFrame({'Qu1': ['other', 'potato', 'cheese', 'banana', 'cheese', 'banana', 'cheese', 'potato', 'other'], 
        'Qu2': ['sausage', 'banana', 'apple', 'apple', 'apple', 'sausage', 'banana', 'banana', 'banana'], 
        'Qu3': ['other', 'potato', 'other', 'cheese', 'cheese', 'potato', 'cheese', 'potato', 'other']}) 

Dank!

Antwort

8

ich einen Datenrahmen von gleicher Form schaffen würde, wo der entsprechende Eintrag der Zählwert ist:

data.apply(lambda x: x.map(x.value_counts())) 
Out[229]: 
    Qu1 Qu2 Qu3 
0 1 2 1 
1 2 4 3 
2 3 3 1 
3 2 3 3 
4 3 3 3 
5 2 2 3 
6 3 4 3 
7 2 4 3 
8 1 4 1 

Und die Ergebnisse in df.where verwenden „andere“, wo der entsprechende Eintrag kleiner als 2 zurück:

data.where(data.apply(lambda x: x.map(x.value_counts()))>=2, "other") 

     Qu1  Qu2  Qu3 
0 other sausage other 
1 potato banana potato 
2 cheese apple other 
3 banana apple cheese 
4 cheese apple cheese 
5 banana sausage potato 
6 cheese banana cheese 
7 potato banana potato 
8 other banana other 
+0

ein bisschen mehr elegant und schneller als mein Ansatz mit '.replace()'! – Stefan

+0

@StefanJansen Danke. :) Meiner Erfahrung nach ist '.replace()' im Allgemeinen langsamer als '.map()', also neige ich dazu, Map zu verwenden, wenn beides möglich ist.Obwohl ich immer noch denke, dass die Kombination apply-map-value_counts die Dinge wiederholen kann, konnte ich keine bessere Alternative finden. – ayhan

+0

Danke! Elegante Lösung. Wie funktioniert das '.where() mit> = 2'? – Toren

2

Sie könnten:

value_counts = df.apply(lambda x: x.value_counts()) 

     Qu1 Qu2 Qu3 
apple 1.0 3.0 1.0 
banana 2.0 4.0 NaN 
cheese 3.0 NaN 3.0 
egg  1.0 NaN 1.0 
potato 2.0 NaN 3.0 
sausage NaN 2.0 1.0 

Dann ein dictionary bauen, die als Ersatz für jede Spalte enthält:

import cycle 
replacements = {} 
for col, s in value_counts.items(): 
    if s[s<2].any(): 
     replacements[col] = dict(zip(s[s < 2].index.tolist(), cycle(['other']))) 

replacements 
{'Qu1': {'egg': 'other', 'apple': 'other'}, 'Qu3': {'egg': 'other', 'apple': 'other', 'sausage': 'other'}} 

Verwenden Sie die dictionary die Werte ersetzen:

df.replace(replacements) 

     Qu1  Qu2  Qu3 
0 other sausage other 
1 potato banana potato 
2 cheese apple other 
3 banana apple cheese 
4 cheese apple cheese 
5 banana sausage potato 
6 cheese banana cheese 
7 potato banana potato 
8 other banana other 

oder Wickeln Sie die Schleife in eine dictionary Verständnis:

from itertools import cycle 

df.replace({col: dict(zip(s[s < 2].index.tolist(), cycle(['other']))) for col, s in value_counts.items() if s[s < 2].any()}) 

Dies ist jedoch nicht nur mühsam, sondern auch langsamer als .where verwenden. Testen mit 3000 Spalten:

df = pd.concat([df for i in range(1000)], axis=1) 

<class 'pandas.core.frame.DataFrame'> 
RangeIndex: 9 entries, 0 to 8 
Columns: 3000 entries, Qu1 to Qu3 
dtypes: object(3000) 

Mit .replace():

%%timeit 
value_counts = df.apply(lambda x: x.value_counts()) 
df.replace({col: dict(zip(s[s < 2].index.tolist(), cycle(['other']))) for col, s in value_counts.items() if s[s < 2].any()}) 

1 loop, best of 3: 4.97 s per loop 

vs .where():

%%timeit 
df.where(df.apply(lambda x: x.map(x.value_counts()))>=2, "other") 

1 loop, best of 3: 2.01 s per loop 
Verwandte Themen