NumPy “matriz de grabación” o “matriz estructurada” o “recarray”

¿Cuál es la diferencia entre una “matriz estructurada” NumPy, una “matriz de grabación” y una “recreación”?

Los documentos de NumPy implican que los dos primeros son iguales: si lo son, ¿cuál es el término preferido para este objeto?

La misma documentación dice (en la parte inferior de la página): Aquí puede encontrar más información sobre nuevas versiones y arreglos estructurados (incluida la diferencia entre los dos). ¿Hay una explicación simple de esta diferencia?

Los registros / recargos se implementan en

https://github.com/numpy/numpy/blob/master/numpy/core/records.py

Algunas citas relevantes de este archivo.

Matrices de registros Las matrices de registros exponen los campos de las matrices estructuradas como propiedades. La recuperación es casi idéntica a una matriz estándar (que ya admite campos con nombre) La mayor diferencia es que puede usar la búsqueda de atributos para encontrar los campos y se construye utilizando un registro.

recarray es una subclase de ndarray (de la misma manera que matrix y masked arrays son). Pero tenga en cuenta que su constructor es diferente de np.array . Es más como np.empty(size, dtype) .

 class recarray(ndarray): """Construct an ndarray that allows field access using attributes. This constructor can be compared to ``empty``: it creates a new record array but does not fill it with data. 

La función clave para implementar el campo único como comportamiento de atributo es __getattribute__ ( __getitem__ implementa la indexación):

 def __getattribute__(self, attr): # See if ndarray has this attr, and return it if so. (note that this # means a field with the same name as an ndarray attr cannot be # accessed by attribute). try: return object.__getattribute__(self, attr) except AttributeError: # attr must be a fieldname pass # look for a field with this name fielddict = ndarray.__getattribute__(self, 'dtype').fields try: res = fielddict[attr][:2] except (TypeError, KeyError): raise AttributeError("recarray has no attribute %s" % attr) obj = self.getfield(*res) # At this point obj will always be a recarray, since (see # PyArray_GetField) the type of obj is inherited. Next, if obj.dtype is # non-structured, convert it to an ndarray. If obj is structured leave # it as a recarray, but make sure to convert to the same dtype.type (eg # to preserve numpy.record type if present), since nested structured # fields do not inherit type. if obj.dtype.fields: return obj.view(dtype=(self.dtype.type, obj.dtype.fields)) else: return obj.view(ndarray) 

Primero trata de obtener un atributo regular: cosas como .shape , .strides , .data , así como todos los métodos ( .sum , .reshape , etc.). Si dtype busca el nombre en los nombres de campo de dtype . Así que en realidad es solo una matriz estructurada con algunos métodos de acceso redefinidos.

Lo mejor que puedo decir es que la record array y la recarray record array son las mismas.

Otro archivo muestra algo de la historia.

https://github.com/numpy/numpy/blob/master/numpy/lib/recfunctions.py

Colección de utilidades para manipular matrices estructuradas. La mayoría de estas funciones fueron implementadas inicialmente por John Hunter para matplotlib. Han sido reescritos y extendidos por conveniencia.

Muchas de las funciones de este archivo terminan con:

  if asrecarray: output = output.view(recarray) 

El hecho de que pueda devolver una matriz como recarray vista de recarray muestra cuán “delgada” es esta capa.

numpy tiene una larga historia y fusiona varios proyectos independientes. Mi impresión es que la recarray es una idea más antigua, y las matrices estructuradas de la implementación actual se basan en un tipo de dtype generalizado. recarrays parecen conservarse por conveniencia y compatibilidad con versiones anteriores que cualquier nuevo desarrollo. Pero tendría que estudiar el historial de archivos github , y cualquier problema reciente / solicitudes de extracción para estar seguro.

En pocas palabras, la respuesta es que generalmente se deben usar matrices estructuradas en lugar de reorganizaciones porque las matrices estructuradas son más rápidas y la única ventaja de las repeticiones es permitirle escribir arr.x lugar de arr['x'] , lo que puede ser un atajo conveniente , pero también es propenso a errores si los nombres de sus columnas entran en conflicto con los métodos / atributos numpy.

Vea este extracto del libro de @jakevdp para una explicación más detallada. En particular, señala que el simple acceso a las columnas de arreglos estructurados puede ser de 20 a 30 veces más rápido que el acceso a las columnas de reclasificación. Sin embargo, su ejemplo utiliza un dataframe muy pequeño con solo 4 filas y no realiza ninguna operación estándar.

Para operaciones simples en marcos de datos más grandes, es probable que la diferencia sea mucho menor, aunque las matrices estructuradas son aún más rápidas. Por ejemplo, aquí hay una matriz estructurada y de registro, cada una con 10,000 filas (código para crear las matrices a partir de un dataframe tomado de la respuesta @jpp aquí ).

 n = 10_000 df = pd.DataFrame({ 'x':np.random.randn(n) }) df['y'] = df.x.astype(int) rec_array = df.to_records(index=False) s = df.dtypes struct_array = np.array([tuple(x) for x in df.values], dtype=list(zip(s.index, s))) 

Si hacemos una operación estándar, como multiplicar una columna por 2, es un 50% más rápido para la matriz estructurada:

 %timeit struct_array['x'] * 2 9.18 µs ± 88.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) %timeit rec_array.x * 2 14.2 µs ± 314 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)