2011-03-01 14 views
9

Me gustaría hacer una subclase numpy ndarray. Sin embargo, no puedo cambiar la matriz. ¿Por qué self = ... no cambia la matriz? Gracias.Subclase numpy ndarray problema

import numpy as np 

class Data(np.ndarray): 

    def __new__(cls, inputarr): 
     obj = np.asarray(inputarr).view(cls) 
     return obj 

    def remove_some(self, t): 
     test_cols, test_vals = zip(*t) 
     test_cols = self[list(test_cols)] 
     test_vals = np.array(test_vals, test_cols.dtype) 

     self = self[test_cols != test_vals] # Is this part correct? 

     print len(self) # correct result 

z = np.array([(1,2,3), (4,5,6), (7,8,9)], 
    dtype=[('a', int), ('b', int), ('c', int)]) 
d = Data(z) 
d.remove_some([('a',4)]) 

print len(d) # output the same size as original. Why? 
+0

por favor proporcione su salida esperada, no está claro lo que quiere lograr. –

+0

Quiero eliminar las filas de la instancia de Datos. – riza

+0

Ok, podrías usar una máscara, pero mejor si haces otra pregunta, ya que esto no tiene mucho que ver con la subclasificación de ndarray –

Respuesta

4

tal vez hacer esta función, en lugar de un método:

import numpy as np 

def remove_row(arr,col,val): 
    return arr[arr[col]!=val] 

z = np.array([(1,2,3), (4,5,6), (7,8,9)], 
    dtype=[('a', int), ('b', int), ('c', int)]) 

z=remove_row(z,'a',4) 
print(repr(z)) 

# array([(1, 2, 3), (7, 8, 9)], 
#  dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<i4')]) 

O, si lo quieres como un método,

import numpy as np 

class Data(np.ndarray): 

    def __new__(cls, inputarr): 
     obj = np.asarray(inputarr).view(cls) 
     return obj 

    def remove_some(self, col, val): 
     return self[self[col] != val] 

z = np.array([(1,2,3), (4,5,6), (7,8,9)], 
    dtype=[('a', int), ('b', int), ('c', int)]) 
d = Data(z) 
d = d.remove_some('a', 4) 
print(d) 

La diferencia clave aquí es que remove_some no intenta modificar self, simplemente devuelve una nueva instancia de Data.

+0

Esta es quizás una respuesta útil a la pregunta, pero no es una respuesta. ¿Por qué self = ... no cambia el valor? Tal vez la respuesta está abajo? Voy a publicar de otra manera. – mathtick

+0

Imagine 'd', su instancia' Data'. Está apuntando a un bloque de memoria que contiene los datos subyacentes. Para eliminar una columna * en el lugar *, tendría que mover las otras columnas juntas y luego cambiar el tamaño de la matriz. Cuando dice 'self = some_other_array' está redirigiendo el nombre de la variable' self' a otro bloque de memoria. Sin embargo, fuera del método 'remove_row', el nombre de la variable' d' aún apunta al bloque de memoria original. Por lo tanto, no puede modificar 'd'. – unutbu

+0

Todos los consejos que he leído sobre numpy dicen que uno no debería intentar cambiar el tamaño de las matrices numpy. Es posible hacerlo, pero toda la copia lo hace lento. Es mejor usar sectores para crear vistas, o indexación elegante para crear nuevas matrices con los datos deseados. Sí, crear una nueva matriz también implica copiar, pero al menos se salva de la complejidad de cambiar datos en el lugar. – unutbu

6

La razón por la que no está recibiendo el resultado que espera es porque se está reasignando self dentro del método remove_some. Solo está creando una nueva variable local self. Si la forma de su matriz no cambiara, podría simplemente hacer self [:] = ... y podría mantener la referencia a self y todo estaría bien, pero está intentando cambiar la forma de self. Lo que significa que tenemos que volver a asignar una nueva memoria y cambiar dónde señalamos cuando nos referimos a self.

No sé cómo hacer esto. Pensé que podría lograrse por __array_finalize__ o __array__ o __array_wrap__. Pero todo lo que he intentado es quedarse corto.

Ahora, hay otra manera de hacerlo que no incluye la subclase ndarray. Se puede hacer una nueva clase que mantiene un atributo que es un ndarray y luego anular todo lo habitual __add__, __mul__, etc .. Algo como esto:

Class Data(object): 
    def __init__(self, inarr): 
     self._array = np.array(inarr) 
    def remove_some(x): 
     self._array = self._array[x] 
    def __add__(self, other): 
     return np.add(self._array, other) 

así, se obtiene la imagen. Es una pena anular todos los operadores, pero a la larga, creo que es más flexible.

Deberá leer this a fondo para hacerlo bien. Hay métodos como __array_finalize__ que deben llamarse en el momento adecuado para realizar la "limpieza".

+0

Pensé que '__array_finalize__' se usa cuando se inicia una nueva instancia, por ejemplo, al agregar atributos adicionales. – riza

+0

Pensé que debía llamarlo cada vez que reasignaba la matriz como lo está haciendo OP. Pero para ser honesto, esto está por encima de mi cabeza.'__array_wrap__' parece estar más cerca de lo que se quiere, pero solo regresa cuando lo llama un ufunc. – Paul

3

Traté de hacer lo mismo, pero es realmente muy complejo para la subclase ndarray.

Si solo tiene que agregar alguna funcionalidad, le sugiero crear una clase que almacene la matriz como atributo.

class Data(object): 

    def __init__(self, array): 
     self.array = array 

    def remove_some(self, t): 
     //operate on self.array 
     pass 

d = Data(z) 
print(d.array) 
+0

[Documentación sobre cómo subclase ndarray] (https://docs.scipy.org/doc/numpy/user/basics.subclassing.html) podría ayudar a hacerlo más fácil. – Sardathrion

Cuestiones relacionadas