Tengo un marco de datos enorme que tiene valores y espacios en blanco/NA. Quiero eliminar los espacios en blanco del marco de datos y mover los siguientes valores hacia arriba en la columna. Considere el siguiente marco de datos de muestra.
import pandas as pd import numpy as np df = pd.DataFrame(np.random.randn(5,4)) df.iloc[1,2] = np.NaN df.iloc[0,1] = np.NaN df.iloc[2,1] = np.NaN df.iloc[2,0] = np.NaN df 0 1 2 3 0 1.857476 NaN -0.462941 -0.600606 1 0.000267 -0.540645 NaN 0.492480 2 NaN NaN -0.803889 0.527973 3 0.566922 0.036393 -1.584926 2.278294 4 -0.243182 -0.221294 1.403478 1.574097Quiero que mi salida sea la siguiente
0 1 2 3 0 1.857476 -0.540645 -0.462941 -0.600606 1 0.000267 0.036393 -0.803889 0.492480 2 0.566922 -0.221294 -1.584926 0.527973 3 -0.243182 1.403478 2.278294 4 1.574097 Quiero que se elimine el NaN y que el siguiente valor suba. df.shift no fue útil. Probé con múltiples bucles y declaraciones if y logré el resultado deseado, pero ¿hay alguna forma mejor de hacerlo?
Puedes usar apply con dropna :
np.random.seed(100) df = pd.DataFrame(np.random.randn(5,4)) df.iloc[1,2] = np.NaN df.iloc[0,1] = np.NaN df.iloc[2,1] = np.NaN df.iloc[2,0] = np.NaN print (df) 0 1 2 3 0 -1.749765 NaN 1.153036 -0.252436 1 0.981321 0.514219 NaN -1.070043 2 NaN NaN -0.458027 0.435163 3 -0.583595 0.816847 0.672721 -0.104411 4 -0.531280 1.029733 -0.438136 -1.118318 df1 = df.apply(lambda x: pd.Series(x.dropna().values)) print (df1) 0 1 2 3 0 -1.749765 0.514219 1.153036 -0.252436 1 0.981321 0.816847 -0.458027 -1.070043 2 -0.583595 1.029733 0.672721 0.435163 3 -0.531280 NaN -0.438136 -0.104411 4 NaN NaN NaN -1.118318Y luego, si es necesario reemplazar el espacio vacío, lo que crea valores mixtos, cadenas con números, algunas funciones se pueden romper:
df1 = df.apply(lambda x: pd.Series(x.dropna().values)).fillna('') print (df1) 0 1 2 3 0 -1.74977 0.514219 1.15304 -0.252436 1 0.981321 0.816847 -0.458027 -1.070043 2 -0.583595 1.02973 0.672721 0.435163 3 -0.53128 -0.438136 -0.104411 4 -1.118318Un enfoque numpy
La idea es ordenar las columnas por np.isnan para que los np.nan se coloquen en último lugar. Uso kind='mergesort' para preservar el orden dentro de non np.nan . Finalmente, corto la matriz y la reasigno. Sigo esto con un fillna
v = df.values i = np.arange(v.shape[1]) a = np.isnan(v).argsort(0, kind='mergesort') v[:] = v[a, i] print(df.fillna('')) 0 1 2 3 0 1.85748 -0.540645 -0.462941 -0.600606 1 0.000267 0.036393 -0.803889 0.492480 2 0.566922 -0.221294 -1.58493 0.527973 3 -0.243182 1.40348 2.278294 4 1.574097Si no desea modificar el marco de datos en su lugar
v = df.values i = np.arange(v.shape[1]) a = np.isnan(v).argsort(0, kind='mergesort') pd.DataFrame(v[a, i], df.index, df.columns).fillna('') El objetivo de esto es aprovechar la rapidez de numpy
prueba de tiempo ingenua
Agregando a la solución por piRSquared: Esto cambia todos los valores a la izquierda en lugar de hacia arriba.
Si no todos los valores son números, use pd.isnull
v = df.values a = [[n]*v.shape[1] for n in range(v.shape[0])] b = pd.isnull(v).argsort(axis=1, kind = 'mergesort') # a is a matrix used to reference the row index, # b is a matrix used to reference the column index # taking an entry from a and the respective entry from b (Same index), # we have a position that references an entry in v v[a, b]Un poco de explicación:
a es una lista de longitud v.shape[0] , y se parece a esto:
[[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4], ... lo que sucede aquí es que, v es m x n , y he hecho tanto a como b m x n , por lo que lo que estamos haciendo es emparejar cada entrada i,j en a y b para obtener el elemento en la fila con valor del elemento en i,j en a y columna con valor del elemento en i,j , en b . Entonces, si tenemos a y b , ambos se parecen a la matriz anterior, entonces v[a,b] devuelve una matriz donde la primera fila contiene n copias de v[0][0] , la segunda fila contiene n copias de v[1][1] y así sucesivamente.
En la solución piRSquared, su i es una lista, no una matriz. Entonces, la lista se usa para v.shape[0] veces, también conocida como una vez por cada fila. Del mismo modo, podríamos haber hecho:
a = [[n] for n in range(v.shape[0])] # which looks like # [[0],[1],[2],[3]...] # since we are trying to indicate the row indices of the matrix v as opposed to # [0, 1, 2, 3, ...] which refers to column indicesAvísame si algo no está claro, gracias :)