Pandas: llenando los valores perdidos por medio en cada grupo.

Esto debería ser sencillo, pero lo más cercano que he encontrado es esta publicación: pandas: rellenando valores perdidos dentro de un grupo , y todavía no puedo resolver mi problema …

Supongamos que tengo el siguiente dataframe

df = pd.DataFrame({'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3], 'name': ['A','A', 'B','B','B','B', 'C','C','C']}) name value 0 A 1 1 A NaN 2 B NaN 3 B 2 4 B 3 5 B 1 6 C 3 7 C NaN 8 C 3 

y me gustaría rellenar “NaN” con valor medio en cada grupo de “nombre”, es decir

  name value 0 A 1 1 A 1 2 B 2 3 B 2 4 B 3 5 B 1 6 C 3 7 C 3 8 C 3 

No estoy seguro de a dónde ir después

 grouped = df.groupby('name').mean() 

Gracias un montón.

Una forma sería usar transform :

 >>> df name value 0 A 1 1 A NaN 2 B NaN 3 B 2 4 B 3 5 B 1 6 C 3 7 C NaN 8 C 3 >>> df["value"] = df.groupby("name").transform(lambda x: x.fillna(x.mean())) >>> df name value 0 A 1 1 A 1 2 B 2 3 B 2 4 B 3 5 B 1 6 C 3 7 C 3 8 C 3 

@DSM tiene la respuesta correcta de IMO, pero me gustaría compartir mi generalización y optimización de la pregunta: columnas múltiples para agrupar y tener columnas de múltiples valores:

 df = pd.DataFrame( { 'category': ['X', 'X', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y'], 'name': ['A','A', 'B','B','B','B', 'C','C','C'], 'other_value': [10, np.nan, np.nan, 20, 30, 10, 30, np.nan, 30], 'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3], } ) 

… da …

  category name other_value value 0 XA 10.0 1.0 1 XA NaN NaN 2 XB NaN NaN 3 XB 20.0 2.0 4 XB 30.0 3.0 5 XB 10.0 1.0 6 YC 30.0 3.0 7 YC NaN NaN 8 YC 30.0 3.0 

En este caso generalizado, nos gustaría agrupar por category y name , e imputar solo sobre el value .

Esto se puede resolver de la siguiente manera:

 df['value'] = df.groupby(['category', 'name'])['value']\ .transform(lambda x: x.fillna(x.mean())) 

Observe la lista de columnas en la cláusula de grupo, y que seleccionamos la columna de value justo después del grupo. Esto hace que la transformación solo se ejecute en esa columna en particular. Puede agregarlo al final, pero luego lo ejecutará para todas las columnas solo para eliminar todas las columnas de una medida al final. Un planificador de consultas SQL estándar podría haber sido capaz de optimizar esto, pero los pandas (0.19.2) no parecen hacer esto.

Prueba de rendimiento aumentando el conjunto de datos haciendo …

 big_df = None for _ in range(10000): if big_df is None: big_df = df.copy() else: big_df = pd.concat([big_df, df]) df = big_df 

… confirma que esto aumenta la velocidad proporcional a la cantidad de columnas que no tiene que imputar:

 import pandas as pd from datetime import datetime def generate_data(): ... t = datetime.now() df = generate_data() df['value'] = df.groupby(['category', 'name'])['value']\ .transform(lambda x: x.fillna(x.mean())) print(datetime.now()-t) # 0:00:00.016012 t = datetime.now() df = generate_data() df["value"] = df.groupby(['category', 'name'])\ .transform(lambda x: x.fillna(x.mean()))['value'] print(datetime.now()-t) # 0:00:00.030022 

En una nota final, puede generalizar aún más si desea imputar más de una columna, pero no todas:

 df[['value', 'other_value']] = df.groupby(['category', 'name'])['value', 'other_value']\ .transform(lambda x: x.fillna(x.mean())) 

Lo haria asi

 df.loc[df.value.isnull(), 'value'] = df.groupby('group').value.transform('mean') 

La respuesta clasificada de alto rango solo funciona para un Dataframe de pandas con solo dos columnas. Si tiene un caso más columnas use en su lugar:

 df['Crude_Birth_rate'] = df.groupby("continent").Crude_Birth_rate.transform( lambda x: x.fillna(x.mean())) 
 def groupMeanValue(group): group['value'] = group['value'].fillna(group['value'].mean()) return group dft = df.groupby("name").transform(groupMeanValue) 

fillna + groupby + transform + mean

Esto parece intuitivo:

 df['value'] = df['value'].fillna(df.groupby('name')['value'].transform('mean')) 

La syntax groupby + transform la media groupwise al índice del dataframe original. Esto es aproximadamente equivalente a la solución de @ DSM , pero evita la necesidad de definir una función lambda anónima.

 df.fillna(df.groupby(['name'], as_index=False).mean(), inplace=True) 

Acabo de hacer esto

 df.fillna(df.mean(), inplace=True) 

Todos los valores perdidos dentro de su DataFrame se llenarán por medio. Si eso es lo que estás buscando. Esto funcionó para mí. Es simple, y hace el trabajo.