Tengo un marco de datos que debe completarse al comprender las filas como lo hacemos en Excel. Si es un entero continuo, se llena con el siguiente número.
¿Hay alguna función en Python como esta?
import pandas as pd d = { 'year': [2019,2020,2019,2020,np.nan,np.nan], 'cat1': [1,2,3,4,np.nan,np.nan], 'cat2': ['c1','c1','c1','c2',np.nan,np.nan]} df = pd.DataFrame(data=d) df year cat1 cat2 0 2019.0 1.0 c1 1 2020.0 2.0 c1 2 2019.0 3.0 c1 3 2020.0 4.0 c2 4 NaN NaN NaN 5 NaN NaN NaNsalida requerida:
year cat1 cat2 0 2019.0 1.0 c1 1 2020.0 2.0 c1 2 2019.0 3.0 c1 3 2020.0 4.0 c2 4 2019.0 5.0 c2 #here can be ignored if it can't understand the earlier pattern 5 2020.0 6.0 c2 #here can be ignored if it can't understand the earlier pattern Intenté df.interpolate(method='krogh') #it fill 1,2,3,4,5,6 but incorrect others .
Probé algunas cosas e investigué un poco más. Parece que pandas actualmente no ofrece la funcionalidad que está buscando.
df['cat'].interpolate(method='linear') solo funcionará si los primeros/últimos valores ya están completos. Tendría que asignar manualmente df.loc[5, 'cat1'] = 6 en este ejemplo, luego funcionaría una interpolación lineal.
Algunas opciones:
Si los datos son lo suficientemente pequeños, siempre puede exportar a Excel y usar el relleno allí, luego volver a traerlos a pandas.
Analice los patrones usted mismo y diseñe sus propios métodos de relleno. Por ejemplo, para obtener el año, puede usar df['year'] = df.index.to_series().apply(lambda x: 2019 if x % 2 == 0 else 2020) .
Hay otras preguntas de Stack Overflow muy similares a esta, y ninguna de las que vi tiene una respuesta genérica.
Intente usar el método fillna (valor) donde reemplaza el Nan con el valor que se le pasa.
A continuación se muestra mi respuesta para el año. Entiendo que se maneja cat1 y se puede ignorar cat2. Una de las suposiciones que hice al analizar la pregunta es que el patrón de repetición es consistente. De lo contrario, es posible que la factorización no funcione.
La idea es usar factorización para extraer el patrón repetido. Luego forme una lista del patrón de repetición. La función de arrastre de Excel es un ciclo del patrón de repetición. Entonces es natural usar el ciclo de itertools. (PD: esto se hace primero en la respuesta de @jabellcu, por lo que no quiero atribuirme el mérito. Si cree que factorizar + ciclo es bueno, verifique su respuesta).
Una ventaja es que los códigos son genéricos. No tiene que codificar los valores. Puede convertirlo en una función y llamarlo para cualquier valor que haya en el marco de datos.
import pandas as pd d = { 'year': [2019,2020,2019,2020,np.nan,np.nan], 'cat1': [1,2,3,4,np.nan,np.nan], 'cat2': ['c1','c1','c1','c2',np.nan,np.nan]} df = pd.DataFrame(data=d) dfRespuesta mejorada:
l = df['year'].factorize()[1].to_list() c = cycle(l) df['year'] = [next(c) for i in range(len(df))] df['cat1'] = df['cat1'].interpolate(method='krogh') df['cat2'] = df['cat2'].fillna(method='ffill') dfPD: dejé una pregunta en la publicación sobre cómo manejar cat2. Actualmente, solo asumo que está lleno por el momento.
De la lectura de la pregunta, asumo que no necesita lógica de detección. Así que no proporcionaré. Solo proporciono la lógica de conversión.
Yo haría lo siguiente:
from pandas.api.types import is_numeric_dtype from itertools import cycle def excel_drag(column): S = column[column.bfill().dropna().index].copy() # drop last empty values numeric = is_numeric_dtype(S) groups = S.groupby(S, sort=False).apply(lambda df: len(df.index)) if (len(groups) == len(S)): if numeric: # Extrapolate return column.interpolate(method='krogh') else: # ffill return column.ffill() elif (groups == groups.iloc[0]).all(): # All equal # Repeat sequence seq_len = len(groups) seq = cycle(S.iloc[:seq_len].values) filling = column[column.bfill().isna()].apply(lambda x: next(seq)) return column.fillna(filling) else: # ffill return column.ffill() Con esa función, df.apply(excel_drag, axis=0) da como resultado:
year cat1 cat2 0 2019.0 1.0 c1 1 2020.0 2.0 c1 2 2019.0 3.0 c1 3 2020.0 4.0 c2 4 2019.0 5.0 c2 5 2020.0 6.0 c2