Tengo que trabajar con matrices grandes, por ejemplo, x = np.arange(0, 750*350*365, dtype=np.int32)

Sé que Python tiene una variable en la memoria siempre que tenga al menos una referencia.

Pero digamos que tengo que importar una gran matriz, hacer algunos cálculos matemáticos y guardar una matriz más pequeña calculada a partir de la gran. ¿La gran matriz aún estaría en memoria?

Por ejemplo:

Class Data:
    value = None

def process(myDataInstance):
    x = np.arange(0, 750*350*365, dtype=np.int32)
    ix = numpy.where(x < 50000)
    myDataInstance.value = x[ix]

d = Data()
process(d)

(en la vida real, no estoy creando una matriz en la función, sino cargando un archivo que contiene matrices grandes, pero esto es, por ejemplo, un propósito)

¿Estará x todavía en la memoria incluso si ya no estamos en la función 'proceso'? Editar : sé que x no será accesible como si escribiera print x fuera de la función, habrá un error porque se definió en el alcance de la función. Estoy preguntando sobre la memoria y la referencia en lugar del nombre de la variable.

En caso afirmativo, ¿debo usar myDataInstance.value = x[ix].copy() para crear otra matriz para que la referencia se elimine al salir de la función?

Si no, ¿dónde lo copia?

Gracias por la explicación

2
Sahasrahla 13 sep. 2018 a las 12:54

3 respuestas

La mejor respuesta

La indexación de fantasía, a diferencia de la división, no devuelve una vista, por lo que no terminará teniendo una referencia a su gran matriz. Consulte la explicación oficial sobre vistas frente a copias en Numpy.

Para responder directamente a su pregunta, la parte donde escribe myDataInstance.value = x[ix] es donde se realiza la copia. no necesita llamar explícitamente a copy a menos que esté haciendo cortes.

Para profundizar, una forma de verificar que una variable es una vista de la matriz numpy es usar función shares_memory

import numpy as np
X = np.arange(10)
x = X[np.where(X > 5)]
np.shares_memory(X, x)  # This outputs False

x = X[np.where(X >= 0)]
np.shares_memory(X, x)  # Still false

También puede usar sys.getrefcount(var) para verificar el número de referencias que apuntan a una variable var al mismo tiempo.

import sys
X = np.arange(10)
print(sys.getrefcount(X)) # This prints 2
x = X[np.where(X > 0)]
print(sys.getrefcount(X)) # This still prints 2

Tenga en cuenta que la razón por la que sys.getrefcount(X) imprime 2 es que la variable X retiene una referencia y la otra función sys.getrefcount() y no x.

En conclusión, no necesita hacer una copia explícita si está haciendo una indexación elegante como en su ejemplo. Si está haciendo cortes, entonces esa es una historia diferente.

2
lightalchemist 13 sep. 2018 a las 14:13

Las variables especificadas en el alcance de su proceso () se eliminarán de la memoria una vez que ya no ejecute esa función. Puede ver esto en acción ejecutando lo siguiente:

class Data:
    value = None

def process(myDataInstance):
    x = np.arange(0, 750*350*365, dtype=np.int32)
    ix = np.where(x < 50000)
    myDataInstance.value = x[ix]

d = Data()
process(d)
print(ix)

>>> Traceback (most recent call last):
  File "/workspace/PRISE/src/datacube/prod_mngr/data_fusion.py", line 26, in <module>
    print(ix)
NameError: name 'ix' is not defined

Obtiene un NameError ya que la variable ix solo se define en el alcance del método process.

NOTA: Si tenía self.ix = np.where(x < 50000) en el método process(), luego de la línea process(d) podrá acceder a la variable ix usando print(Data.ix) porque eso asigna la variable al objeto Data() al que tiene referencia globalmente.

EDITAR para mayor aclaración:

Una vez que una variable está fuera de alcance, se elimina de la memoria automáticamente en Python. consulte Recolección de basura en Python para obtener más información.

0
tda 13 sep. 2018 a las 10:47

Para eliminar un objeto Python an_object de la memoria, llame a del(an_object) y espere a que se inicie la recolección de basura. La recolección de basura también se puede interferir manualmente con el módulo gc, bajo su responsabilidad.

Es importante aclarar que del(an_object) o métodos de eliminación similares no eliminan el objeto de la memoria, solo eliminan el nombre an_object del espacio de nombres. Todavía tiene que esperar la recolección de basura.

ACTUALIZACIÓN Para responder el comentario aquí abajo, podemos verificar si un segmento de una matriz es una referencia a la matriz original o no con el siguiente código:

import numpy as np

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

x_new_1 = x_old[:5] # We slice the array, without calling  .copy()
# x_new = np.array([0,1,2,3,4])

x_old[2]=100 # We change the third element of the original array, from 2 to 100
print(x_new_1) # The output is [  0   1 100   3   4]. x_new_1 is thus a reference to x_old,
# not a new object

x_old[2]= 2 # Restore original value
x_new_2 = x_old[:5].copy() # This time we call .copy() on the slice, or the whole array for that matter.
x_old[2]=100 # again we change the value

print(x_new_2) # The output is array([0, 1, 2, 3, 4])

Por lo tanto, llamar a .copy () en la matriz original creará un nuevo objeto, permitiéndole eliminar el antiguo del espacio de nombres y esperar su eliminación automática de la memoria. Si no llama a .copy () todavía está trabajando con una referencia al objeto antiguo y, como consecuencia, lo que sea que ocurra con el objeto original afecta a la referencia.

¿Qué debe hacer si desea eliminar de la memoria parte de una matriz?

1) Copie el segmento de la matriz original que desea mantener en una nueva matriz con un nuevo nombre.

2) Llame a del o cualquier otra instrucción de eliminación en la matriz original

3) Espere su eliminación automática de la memoria

4) Continuar trabajando con el nuevo objeto.

Sin embargo, dado que está trabajando con matrices grandes, recuerde que tiene ambas matrices cargadas en la memoria durante un cierto período de tiempo si usa este proceso.

ACTUALIZACIÓN 2

OP, como mencionó @lightalchemist en el comentario a continuación, el código que proporcionó no produce una referencia a x, sino una copia. El código que proporcionó como ejemplo no se ajusta a la descripción del problema que enfrenta.

0
Daneel R. 13 sep. 2018 a las 11:21