Quel est le moyen le plus efficace de supprimer le dernier élément d'un tableau numpy à 1 dimension? (comme pop pour liste)
Les tableaux NumPy ont une taille fixe, vous ne pouvez donc pas supprimer un élément sur place. Par exemple, utiliser del
ne fonctionne pas:
>>> import numpy as np
>>> arr = np.arange(5)
>>> del arr[-1]
ValueError: cannot delete array elements
Notez que l'index -1
représente le dernier élément. En effet, les indices négatifs en Python (et NumPy) sont comptés à partir de la fin, donc -1
est le dernier, -2
est celui avant dernier et -len
est en fait le premier élément. C'est juste pour votre information au cas où vous ne le sauriez pas.
Les listes Python étant de tailles variables, il est facile d’ajouter ou de supprimer des éléments.
Donc, si vous souhaitez supprimer un élément, vous devez créer un nouveau tableau ou une nouvelle vue.
Vous pouvez créer une nouvelle vue contenant tous les éléments sauf le dernier en utilisant la notation slice:
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> arr[:-1] # all but the last element
array([0, 1, 2, 3])
>>> arr[:-2] # all but the last two elements
array([0, 1, 2])
>>> arr[1:] # all but the first element
array([1, 2, 3, 4])
>>> arr[1:-1] # all but the first and last element
array([1, 2, 3])
Cependant, une vue partage les données avec le tableau d'origine, donc si l'une est modifiée, l'autre l'est:
>>> sub = arr[:-1]
>>> sub
array([0, 1, 2, 3])
>>> sub[0] = 100
>>> sub
array([100, 1, 2, 3])
>>> arr
array([100, 1, 2, 3, 4])
Si vous n'aimez pas ce partage de mémoire, vous devez créer un nouveau tableau. Dans ce cas, il est probablement plus simple de créer une vue, puis de la copier (par exemple, en utilisant la méthode copy()
de tableaux)
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> sub_arr = arr[:-1].copy()
>>> sub_arr
array([0, 1, 2, 3])
>>> sub_arr[0] = 100
>>> sub_arr
array([100, 1, 2, 3])
>>> arr
array([0, 1, 2, 3, 4])
Cependant, vous pouvez également utiliser l'indexation de tableaux entiers pour supprimer le dernier élément et obtenir un nouveau tableau. Cette indexation de tableaux entiers créera toujours (et non à 100%) une copie et non une vue:
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> indices_to_keep = [0, 1, 2, 3]
>>> sub_arr = arr[indices_to_keep]
>>> sub_arr
array([0, 1, 2, 3])
>>> sub_arr[0] = 100
>>> sub_arr
array([100, 1, 2, 3])
>>> arr
array([0, 1, 2, 3, 4])
Cette indexation de tableau de nombres entiers peut être utile pour supprimer des éléments arbitraires d'un tableau (ce qui peut être délicat ou impossible lorsque vous voulez une vue):
>>> arr = np.arange(5, 10)
>>> arr
array([5, 6, 7, 8, 9])
>>> arr[[0, 1, 3, 4]] # keep first, second, fourth and fifth element
array([5, 6, 8, 9])
Si vous souhaitez une fonction généralisée qui supprime le dernier élément à l'aide de l'indexation de tableaux entiers:
def remove_last_element(arr):
return arr[np.arange(arr.size - 1)]
Il existe également une indexation booléenne qui pourrait être utilisée, par exemple:
>>> arr = np.arange(5, 10)
>>> arr
array([5, 6, 7, 8, 9])
>>> keep = [True, True, True, True, False]
>>> arr[keep]
array([5, 6, 7, 8])
Cela crée également une copie! Et une approche généralisée pourrait ressembler à ceci:
def remove_last_element(arr):
if not arr.size:
raise IndexError('cannot remove last element of empty array')
keep = np.ones(arr.shape, dtype=bool)
keep[-1] = False
return arr[keep]
Si vous souhaitez plus d'informations sur NumPys, la documentation sur "l'indexation" est très utile et couvre de nombreux cas.
np.delete()
Normalement, je ne recommanderais pas les fonctions NumPy qui "semblent" comme si elles modifiaient le tableau sur place (comme np.append
et np.insert
), mais renvoient des copies car elles sont généralement inutilement lentes et trompeuses. Vous devriez les éviter autant que possible, c'est pourquoi c'est le dernier point de ma réponse. Cependant, dans ce cas, c'est un ajustement parfait, je dois donc le mentionner:
>>> arr = np.arange(10, 20)
>>> arr
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
>>> np.delete(arr, -1)
array([10, 11, 12, 13, 14, 15, 16, 17, 18])
np.resize()
NumPy a une autre méthode qui ressemble à une opération sur place, mais renvoie en réalité un nouveau tableau:
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> np.resize(arr, arr.size - 1)
array([0, 1, 2, 3])
Pour supprimer le dernier élément, j'ai simplement fourni une nouvelle forme qui est 1 plus petite qu'avant, ce qui supprime effectivement le dernier élément.
Oui, j'ai déjà écrit que vous ne pouvez pas modifier un tableau à la place. Mais j'ai dit cela parce que dans la plupart des cas, ce n'est pas possible ou seulement en désactivant certaines vérifications de sécurité (tout à fait utiles). Je ne suis pas sûr des éléments internes, mais en fonction de l'ancienne taille et de la nouvelle taille, il est possible que cela inclue une opération de copie (interne uniquement), de sorte que pourrait être plus lent que la création d'une vue.
np.ndarray.resize()
Si le tableau ne partage pas sa mémoire avec un autre tableau, il est alors possible de redimensionner le tableau en place:
>>> arr = np.arange(5, 10)
>>> arr.resize(4)
>>> arr
array([5, 6, 7, 8])
Cependant, cela lancera ValueError
s dans le cas où il est également référencé par un autre tableau:
>>> arr = np.arange(5)
>>> view = arr[1:]
>>> arr.resize(4)
ValueError: cannot resize an array that references or is referenced by another array in this way. Use the resize function
Vous pouvez désactiver cette vérification de sécurité en définissant refcheck=False
, mais cela ne devrait pas être fait à la légère, car vous vous rendez vulnérable aux erreurs de segmentation et à la corruption de mémoire au cas où l'autre référence tente d'accéder aux éléments supprimés! Cet argument refcheck
doit être traité comme une option réservée aux experts!
La création d'une vue est très rapide et ne nécessite pas beaucoup de mémoire supplémentaire. Par conséquent, essayez autant que possible de travailler autant que possible avec les vues. Cependant, selon les cas d'utilisation, il n'est pas si facile de supprimer des éléments arbitraires à l'aide du découpage de base. Bien qu'il soit facile de supprimer les n premiers éléments et/ou les n derniers éléments ou de supprimer chaque élément x (l'argument de pas pour le découpage en tranches), c'est tout ce que vous pouvez faire avec.
Mais dans votre cas de suppression du dernier élément d'un tableau à une dimension, je vous recommanderais:
arr[:-1] # if you want a view
arr[:-1].copy() # if you want a new array
parce qu’ils expriment très clairement l’intention et que toute personne ayant une expérience Python/NumPy le reconnaîtra.
Basé sur le cadre temporel de cette réponse :
# Setup
import numpy as np
def view(arr):
return arr[:-1]
def array_copy_view(arr):
return arr[:-1].copy()
def array_int_index(arr):
return arr[np.arange(arr.size - 1)]
def array_bool_index(arr):
if not arr.size:
raise IndexError('cannot remove last element of empty array')
keep = np.ones(arr.shape, dtype=bool)
keep[-1] = False
return arr[keep]
def array_delete(arr):
return np.delete(arr, -1)
def array_resize(arr):
return np.resize(arr, arr.size - 1)
# Timing setup
timings = {view: [],
array_copy_view: [], array_int_index: [], array_bool_index: [],
array_delete: [], array_resize: []}
sizes = [2**i for i in range(1, 20, 2)]
# Timing
for size in sizes:
print(size)
func_input = np.random.random(size=size)
for func in timings:
print(func.__name__.ljust(20), ' ', end='')
res = %timeit -o func(func_input) # if you use IPython, otherwise use the "timeit" module
timings[func].append(res)
# Plotting
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(1)
ax = plt.subplot(111)
for func in timings:
ax.plot(sizes,
[time.best for time in timings[func]],
label=func.__name__)
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xlabel('size')
ax.set_ylabel('time [seconds]')
ax.grid(which='both')
ax.legend()
plt.tight_layout()
J'obtiens les timings suivants sous forme de log-log plot pour couvrir tous les détails, plus le temps est bas, plus vite, mais l'intervalle entre deux graduations représente un ordre de grandeur au lieu d'un montant fixe. Si les valeurs spécifiques vous intéressent, je les ai copiées dans ce Gist :
Selon ces temps, ces deux approches sont aussi les plus rapides. (Python 3.6 et NumPy 1.14.0)
Pour supprimer le dernier élément d'un tableau NumPy à 1 dimension, utilisez la méthode numpy.delete , comme suit:
import numpy as np
# Create a 1-dimensional NumPy array that holds 5 values
values = np.array([1, 2, 3, 4, 5])
# Remove the last element of the array using the numpy.delete method
values = np.delete(values, -1)
print(values)
Sortie: [1 2 3 4]
La dernière valeur du tableau NumPy, qui était 5, est maintenant supprimée.