Exemple de problème
À titre d'exemple simple, considérons le tableau numpy arr
comme défini ci-dessous :
import numpy as np
arr = np.array([[5, np.nan, np.nan, 7, 2],
[3, np.nan, 1, 8, np.nan],
[4, 9, 6, np.nan, np.nan]])
où arr
ressemble à ça en sortie de console :
array([[ 5., nan, nan, 7., 2.],
[ 3., nan, 1., 8., nan],
[ 4., 9., 6., nan, nan]])
J'aimerais maintenant remplir les rangées en avant de l'écran. nan
valeurs dans le tableau arr
. J'entends par là le remplacement de chaque nan
avec la valeur valide la plus proche à partir de la gauche. Le résultat souhaité ressemblerait à ceci :
array([[ 5., 5., 5., 7., 2.],
[ 3., 3., 1., 8., 8.],
[ 4., 9., 6., 6., 6.]])
Essayé jusqu'à présent
J'ai essayé d'utiliser des boucles for :
for row_idx in range(arr.shape[0]):
for col_idx in range(arr.shape[1]):
if np.isnan(arr[row_idx][col_idx]):
arr[row_idx][col_idx] = arr[row_idx][col_idx - 1]
J'ai également essayé d'utiliser un cadre de données pandas comme étape intermédiaire (puisque les cadres de données pandas ont une méthode intégrée très soignée pour le remplissage avant) :
import pandas as pd
df = pd.DataFrame(arr)
df.fillna(method='ffill', axis=1, inplace=True)
arr = df.as_matrix()
Les deux stratégies ci-dessus produisent le résultat souhaité, mais je continue à me demander : une stratégie qui utilise uniquement des opérations vectorielles numpy ne serait-elle pas la plus efficace ?
Résumé
Existe-t-il un autre moyen plus efficace de "remplir à l'avance" nan
des valeurs dans des tableaux numpy ? (par exemple, en utilisant des opérations vectorielles numpy)
Mise à jour : Comparaison des solutions
J'ai essayé toutes les solutions jusqu'à présent. C'était ma configuration script :
import numba as nb
import numpy as np
import pandas as pd
def random_array():
choices = [1, 2, 3, 4, 5, 6, 7, 8, 9, np.nan]
out = np.random.choice(choices, size=(1000, 10))
return out
def loops_fill(arr):
out = arr.copy()
for row_idx in range(out.shape[0]):
for col_idx in range(1, out.shape[1]):
if np.isnan(out[row_idx, col_idx]):
out[row_idx, col_idx] = out[row_idx, col_idx - 1]
return out
@nb.jit
def numba_loops_fill(arr):
'''Numba decorator solution provided by shx2.'''
out = arr.copy()
for row_idx in range(out.shape[0]):
for col_idx in range(1, out.shape[1]):
if np.isnan(out[row_idx, col_idx]):
out[row_idx, col_idx] = out[row_idx, col_idx - 1]
return out
def pandas_fill(arr):
df = pd.DataFrame(arr)
df.fillna(method='ffill', axis=1, inplace=True)
out = df.as_matrix()
return out
def numpy_fill(arr):
'''Solution provided by Divakar.'''
mask = np.isnan(arr)
idx = np.where(~mask,np.arange(mask.shape[1]),0)
np.maximum.accumulate(idx,axis=1, out=idx)
out = arr[np.arange(idx.shape[0])[:,None], idx]
return out
suivi de cette entrée de console :
%timeit -n 1000 loops_fill(random_array())
%timeit -n 1000 numba_loops_fill(random_array())
%timeit -n 1000 pandas_fill(random_array())
%timeit -n 1000 numpy_fill(random_array())
ce qui donne cette sortie de console :
1000 loops, best of 3: 9.64 ms per loop
1000 loops, best of 3: 377 µs per loop
1000 loops, best of 3: 455 µs per loop
1000 loops, best of 3: 351 µs per loop
4 votes
Ce qui doit se passer si le premier élément d'une ligne est
nan
?0 votes
@TadhgMcDonald-Jensen Dans ce cas, pandas laisse l'élément
NaN
intacte. Je suppose que le PO veut le même comportement par souci de cohérence.4 votes
Remplir les valeurs nulles d'un tableau numpy 1d avec les dernières valeurs non nulles . Vous pourriez trouver cela utile.
0 votes
Ah, bonne question. Dans mes cas d'utilisation, la première colonne du tableau d'entrée n'est pas censée contenir de l'information.
nan
valeurs. Je suis donc d'accord lorsque le code (à la rencontre d'unnan
dans la première colonne) soit lève une exception, soit laisse cette option en suspens.nan
en place.0 votes
Je crois que pandas utilise en fait des opérations vectorielles sur le tableau numpy sous-jacent original. La seule surcharge de votre deuxième option est d'envelopper le tableau avec un dataframe et de le désenvelopper à nouveau, ce qui est probablement assez modeste pour les grandes matrices.
1 votes
BTW, il n'est même pas nécessaire d'appeler
as_matrix()
: l'originalarr
est modifié.0 votes
@DYZ Je viens de le tester, et l'original
arr
est en effet mis à jour comme vous l'avez dit. Merci de l'avoir signalé.0 votes
Un petit conseil :
arr[i,j]
est meilleur et plus rapide quearr[i][j]
.0 votes
@shx2 Noté, merci.
0 votes
Je cherche la solution pour les tableaux 3D, pour les tableaux 2D la façon la plus stupide est de les convertir en df d'abord, puis d'utiliser fillna.