Skip to content

NumPy : Déterminer si ndarray est view ou copy et s’il partage la mémoire

Vous pouvez utiliser l’attribut base pour déterminer si le tableau NumPy numpy.ndarray est une vue ou une copie. De plus, np.shares_memory() peut être utilisé pour déterminer si deux tableaux partagent de la mémoire.

Cet article décrit les éléments suivants :

  • Afficher et copier numpy.ndarray
    • Exemple de création d’une vue
    • Exemple de création d’une copie
    • copier() et afficher()
  • Déterminez si afficher ou copier :base attribute
  • Déterminez si la mémoire est partagée :np.shares_memory()
    • Utilisation de base
    • np.may_share_memory()


La version de NumPy dans l’exemple de code ci-dessous est 1.16.4. Notez que différentes versions peuvent se comporter différemment.

Afficher et copier numpy.ndarray

Il existe deux types de numpy.ndarray : les vues et les copies.

Lorsque vous créez un autre objet tableau à partir d’un objet tableau, l’objet qui partage la mémoire avec l’objet d’origine (fait référence à une partie ou à la totalité de la mémoire de l’objet d’origine) est appelé une vue.

D’autre part, un objet qui alloue de la mémoire séparément de l’objet d’origine est appelé une copie.

Exemple de création d’une vue

Par exemple, les tranches créent des vues.

import numpy as np

a_2d = np.arange(12).reshape(3, 4)
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

a_slice = a_2d[:2, :2]
print(a_slice)
# [[0 1]
#  [4 5]]

L’objet d’origine et la vue se réfèrent à la même mémoire, donc changer la valeur d’un élément dans un objet change la valeur dans l’autre.

a_slice[0, 0] = 100
print(a_slice)
# [[100   1]
#  [  4   5]]

print(a_2d)
# [[100   1   2   3]
#  [  4   5   6   7]
#  [  8   9  10  11]]

a_2d[0, 0] = 0
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(a_slice)
# [[0 1]
#  [4 5]]

En plus des tranches, certaines fonctions et méthodes, telles que reshape(), qui seront utilisées comme exemple dans la section suivante, renvoient une vue.

Exemple de création d’une copie

L’indexation sophistiquée crée des copies.

a_fancy_index = a_2d[[0, 1]]
print(a_fancy_index)
# [[0 1 2 3]
#  [4 5 6 7]]

Puisqu’ils ne partagent pas la mémoire, changer la valeur d’un objet ne change pas la valeur de l’autre.

a_fancy_index[0, 0] = 100
print(a_fancy_index)
# [[100   1   2   3]
#  [  4   5   6   7]]

print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

copier() et afficher()

Vous pouvez utiliser copy() pour créer une copie d’un objet tableau.

Il est également possible de créer une copie de la vue.

a_slice_copy = a_2d[:2, :2].copy()
print(a_slice_copy)
# [[0 1]
#  [4 5]]

Changer la valeur d’un élément d’un objet ne change pas la valeur de l’autre.

a_slice_copy[0, 0] = 100
print(a_slice_copy)
# [[100   1]
#  [  4   5]]

print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

Il existe également une méthode appelée view().

Cependant, par exemple, appeler view() à partir d’un objet créé par indexation sophistiquée ne renverra que la vue d’une copie, pas la vue de l’objet d’origine.

Déterminez si afficher ou copier :base attribute

Utilisez l’attribut base pour déterminer si numpy.ndarray est une vue ou une copie (strictement une vue ou non).

Si numpy.ndarray est une vue, l’attribut de base renvoie le numpy.ndarray d’origine.

Prenez reshape(), qui renvoie une vue autant que possible, comme exemple.

a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]

a_0 = a[:6]
print(a_0)
# [0 1 2 3 4 5]

a_1 = a_0.reshape(2, 3)
print(a_1)
# [[0 1 2]
#  [3 4 5]]

print(a_0.base)
# [0 1 2 3 4 5 6 7 8 9]

print(a_1.base)
# [0 1 2 3 4 5 6 7 8 9]

L’attribut de base de la copie ou du numpy.ndarray d’origine (un numpy.ndarray nouvellement créé qui n’est ni une copie ni une vue) est None.

a_copy = a.copy()
print(a_copy)
# [0 1 2 3 4 5 6 7 8 9]

print(a_copy.base)
# None

print(a.base)
# None

Vous pouvez utiliser l’opérateur is pour comparer l’attribut de base avec None pour déterminer s’il s’agit d’une vue ou non.

print(a_0.base is None)
# False

print(a_copy.base is None)
# True

print(a.base is None)
# True

Vous pouvez également voir qu’ils partagent la mémoire en comparant l’attribut de base de la vue avec le numpy.ndarray d’origine ou en comparant les attributs de base de la vue entre eux.

print(a_0.base is a)
# True

print(a_0.base is a_1.base)
# True

Le np.shares_memory() suivant est plus pratique pour déterminer si la mémoire est partagée.

Déterminez si la mémoire est partagée :np.shares_memory()

Le fait que les deux tableaux partagent la mémoire peut être déterminé par np.shares_memory().

Utilisation de base

Spécifiez deux numpy.ndarrays dans np.shares_memory(). True est renvoyé si ces tableaux partagent de la mémoire.

a = np.arange(6)
print(a)
# [0 1 2 3 4 5]

a_reshape = a.reshape(2, 3)
print(a_reshape)
# [[0 1 2]
#  [3 4 5]]

print(np.shares_memory(a, a_reshape))
# True

Si vous spécifiez deux vues générées à partir du numpy.ndarray commun, True est également renvoyé.

a_slice = a[2:5]
print(a_slice)
# [2 3 4]

print(np.shares_memory(a_reshape, a_slice))
# True

En cas de copie, False est retourné.

a_reshape_copy = a.reshape(2, 3).copy()
print(a_reshape_copy)
# [[0 1 2]
#  [3 4 5]]

print(np.shares_memory(a, a_reshape_copy))
# False

np.may_share_memory()

Il existe également une fonction similaire à np.shares_memory() appelée np.may_share_memory().

np.may_share_memory() est moins strict que np.shares_memory(), comme vous pouvez le voir du fait que le nom de la fonction contient may.

np.may_share_memory() détermine uniquement si les plages d’adresses mémoire se chevauchent, il ne considère pas s’il y a des éléments qui référencent la même mémoire.

Par exemple, dans le cas suivant, les deux tranches sont la même vue de numpy.ndarray et font référence à la plage qui se chevauche, mais chaque élément lui-même fait référence à une mémoire différente.

a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]

a_0 = a[::2]
print(a_0)
# [0 2 4 6 8]

a_1 = a[1::2]
print(a_1)
# [1 3 5 7 9]

np.shares_memory() renvoie False pour un jugement strict, mais np.may_share_memory() renvoie True.

print(np.shares_memory(a_0, a_1))
# False

print(np.may_share_memory(a_0, a_1))
# True

Dans l’exemple ci-dessous, np.may_share_memory() renvoie False car les deux tranches sont la première moitié et la seconde moitié du numpy.ndarray d’origine et les plages ne se chevauchent pas.

a_2 = a[:5]
print(a_2)
# [0 1 2 3 4]

a_3 = a[5:]
print(a_3)
# [5 6 7 8 9]

print(np.shares_memory(a_2, a_3))
# False

print(np.may_share_memory(a_2, a_3))
# False

Le temps de traitement est plus long pour np.shares_memory(), qui rend un jugement strict. Notez que le code ci-dessous utilise la commande magique Jupyter Notebook %%timeit et n’est pas mesuré lorsqu’il est exécuté en tant que script Python.

%%timeit
np.shares_memory(a_0, a_1)
# 839 ns ± 53.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%%timeit
np.may_share_memory(a_0, a_1)
# 275 ns ± 5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Bien que np.may_share_memory() puisse renvoyer True par erreur lorsque chaque élément ne partage pas de mémoire, il ne renvoie pas False par erreur lorsque la mémoire est partagée.