2017-11-23 2 views
3

Das Problem: Nehmen wir den Titanic-Datensatz von Kaggle. Ich habe Datenrahmen mit den Spalten "Pclass", "Sex" und "Age". Ich muss NaN in Spalte "Alter" mit einem Median für bestimmte Gruppe füllen. Wenn es eine Frau aus der 1. Klasse ist, würde Ich mag ihr Alter mit dem Median für die 1. Klasse Frauen füllen, nicht mit dem Median für ganze Spalte Alter.Wie man ein Stück von DataFrame und "fillna" in bestimmten Slice mit Python Pandas machen?

Die Frage ist, wie in einer bestimmten Scheibe diese Änderung zu machen?

Ich habe versucht:

data['Age'][(data['Sex'] == 'female')&(data['Pclass'] == 1)&(data['Age'].isnull())].fillna(median) 

, wo die "mittlere" mein Wert ist, aber es ändert sich nichts "Inplace = True" helfen nicht.

Thanks a lot!

+0

I-Lösung hinzufügen für 'NaN's durch Median pro Gruppe Füllung, ist nur notwendig,' groupby'. Überprüfen Sie die Bearbeitung in meiner Antwort. – jezrael

Antwort

2

Ich glaube, Sie von Masken filtern müssen und weisen zurück:

data = pd.DataFrame({'a':list('aaaddd'), 
        'Sex':['female','female','male','female','female','male'], 
        'Pclass':[1,2,1,2,1,1], 
        'Age':[40,20,30,20,np.nan,np.nan]}) 

print (data) 
    Age Pclass  Sex a 
0 40.0  1 female a 
1 20.0  2 female a 
2 30.0  1 male a 
3 20.0  2 female d 
4 NaN  1 female d 
5 NaN  1 male d 

#boolean mask 
mask1 = (data['Sex'] == 'female')&(data['Pclass'] == 1) 

#get median by mask without NaNs 
med = data.loc[mask1, 'Age'].median() 
print (med) 
40.0 

#repalce NaNs 
data.loc[mask1, 'Age'] = data.loc[mask1, 'Age'].fillna(med) 
print (data) 
    Age Pclass  Sex a 
0 40.0  1 female a 
1 20.0  2 female a 
2 30.0  1 male a 
3 20.0  2 female d 
4 40.0  1 female d 
5 NaN  1 male d 

Was gleiche ist, wie:

mask2 = mask1 &(data['Age'].isnull()) 

data.loc[mask2, 'Age'] = med 
print (data) 
    Age Pclass  Sex a 
0 40.0  1 female a 
1 20.0  2 female a 
2 30.0  1 male a 
3 20.0  2 female d 
4 40.0  1 female d 
5 NaN  1 male d 

EDIT:

Wenn brauchen alle Gruppen NaN s durch Median ersetzen:

data['Age'] = data.groupby(["Sex","Pclass"])["Age"].apply(lambda x: x.fillna(x.median())) 
print (data) 

    Age Pclass  Sex a 
0 40.0  1 female a 
1 20.0  2 female a 
2 30.0  1 male a 
3 20.0  2 female d 
4 40.0  1 female d 
5 30.0  1 male d 
+0

Vielen Dank! Es funktionierte! Brauchen Sie mehr über Masken zu lernen ... –

+0

Ja, genau. Weil eklse NaN bekommen. – jezrael

+1

Gern geschehen! Vielleicht kleiner Rat für die Zukunft - [wie man ein großes Pandas-Beispiel bietet] (http://stackoverflow.com/questions/20109391/how-to-make-good-reproducible-pandas-examples);) – jezrael

1

Falls Sie das gleiche tun für alle Gruppen, die Sie diesen Trick

data = pd.DataFrame({'a':list('aaaddd'), 
        'Sex':['female','female','male','female','female','male'], 
        'Pclass':[1,2,1,2,1,1], 
        'Age':[40,20,30,20, np.nan, np.nan]}) 
df = data.groupby(["Sex","Pclass"])["Age"].median().to_frame().reset_index() 
df.rename(columns={"Age":"Med"}, inplace=True) 
data = pd.merge(left=data,right=df, how='left', on=["Sex", "Pclass"]) 
data["Age"] = np.where(data["Age"].isnull(), data["Med"], data["Age"]) 

UPDATE verwenden:

# dummy dataframe 
n = int(1e7) 
data = pd.DataFrame({"Age":np.random.choice([10,20,20,30,30,40,np.nan], n), 
        "Pclass":np.random.choice([1,2,3], n), 
        "Sex":np.random.choice(["male","female"], n), 
        "a":np.random.choice(["a","b","c","d"], n)}) 

In meiner Maschine diese läuft (ist wie die vorherige ohne Umbenennung)

df = data.groupby(["Sex","Pclass"])["Age"].agg(['median']).reset_index() 
data = pd.merge(left=data,right=df, how='left', on=["Sex", "Pclass"]) 
data["Age"] = np.where(data["Age"].isnull(), data["median"], data["Age"]) 

CPU times: user 1.98 s, sys: 216 ms, total: 2.2 s 
Wall time: 2.2 s 

Während die Maske Lösung nahm:

for sex in ["male", "female"]: 
    for pclass in range(1,4): 
     mask1 =(data['Sex'] == sex)&(data['Pclass'] == pclass) 
     med = data.loc[mask1, 'Age'].median() 
     data.loc[mask1, 'Age'] = data.loc[mask1, 'Age'].fillna(med) 

CPU times: user 5.13 s, sys: 60 ms, total: 5.19 s 
Wall time: 5.19 s 

@jezrael Lösung ist noch schneller

data['Age'] = data.groupby(["Sex","Pclass"])["Age"].apply(lambda x: x.fillna(x.median())) 

CPU times: user 1.34 s, sys: 92 ms, total: 1.44 s 
Wall time: 1.44 s 
+0

Danke für deine Antwort! Ich habe eine zusätzliche Frage. Ich benutzte einen anderen Weg mit for-Schleifen. Aber ich denke, ur viel besser sein könnte, weil es numpy und Pandas verwendet, die C++ und muss schneller sein, wenn auf große Datensätze verwendet werden, bin ich richtig? –

+1

Ich habe einige Tests mit einer Dummy-Datenbank mit '1e7'-Zeilen durchgeführt, und das hat besser abgeschnitten als @jezrael's (was ich großartig fand, falls Sie es nicht für alle Kategorien anwenden müssen). Wenn Sie einen massiven Datenrahmen haben, können Sie https://dask.pydata.org/en/latest/ BTW betrachten, wenn Sie die Antwort mögen, können Sie sie aufladen. Wieder sollte die akzeptierte Antwort für das spezifische Problem @ jezrael's sein – user32185

Verwandte Themen