2009-11-23 9 views
18

En Python puro puede hacer crecer la columna matrices por la columna con bastante facilidad:matrices por columnas crecientes en NumPy

data = [] 
for i in something: 
    newColumn = getColumnDataAsList(i) 
    data.append(newColumn) 

NumPy 's gama no tiene la función de agregación. La función hstack no funciona en las matrices de tamaño cero, por lo que el siguiente no funcionará:

data = numpy.array([]) 
for i in something: 
    newColumn = getColumnDataAsNumpyArray(i) 
    data = numpy.hstack((data, newColumn)) # ValueError: arrays must have same number of dimensions 

Por lo tanto, mis opciones son o bien para eliminar el initalization iside el lazo con la condición apropiada:

data = None 
for i in something: 
    newColumn = getColumnDataAsNumpyArray(i) 
    if data is None: 
     data = newColumn 
    else: 
     data = numpy.hstack((data, newColumn)) # works 

... o para usar una lista de Python y convertir es posterior a la matriz:

data = [] 
for i in something: 
    newColumn = getColumnDataAsNumpyArray(i) 
    data.append(newColumn) 
data = numpy.array(data) 

Ambas variantes parecen un poco incómodo de ser. ¿Hay mejores soluciones?

Respuesta

18

NumPy realidad tiene una función append, que parece que podría hacer lo que quiera, por ejemplo,

import numpy as NP 
my_data = NP.random.random_integers(0, 9, 9).reshape(3, 3) 
new_col = NP.array((5, 5, 5)).reshape(3, 1) 
res = NP.append(my_data, new_col, axis=1) 

su segundo fragmento (hstack) funcionará si se agrega otra línea, por ejemplo,

my_data = NP.random.random_integers(0, 9, 16).reshape(4, 4) 
# the line to add--does not depend on array dimensions 
new_col = NP.zeros_like(my_data[:,-1]).reshape(-1, 1) 
res = NP.hstack((my_data, new_col)) 

hstack g Tiene el mismo resultado que concatenate((my_data, new_col), axis=1), no estoy seguro de cómo se comparan en términos de rendimiento.


Si bien esa es la respuesta más directa a su pregunta, debo mencionar que bucle a través de una fuente de datos para poblar un objetivo a través de anexar , mientras que muy bien en pitón, no es idiomática NumPy. Aquí es por qué:

inicializar un array NumPy es relativamente caro, y con este patrón pitón convencional, de incurrir en que el costo, más o menos, en cada iteración de bucle (es decir, cada anexar a una matriz NumPy está más o menos como la inicialización una nueva matriz con un tamaño diferente).

Por esa razón, el patrón común en NumPy para la adición iterativa de columnas en una matriz 2D es inicializar una matriz de destino vacío una vez (o pre-asignar una única matriz 2D NumPy que tengan todas las columnas vacías) la pueblan sucesivamente esas columnas vacías mediante el establecimiento de la deseada por columnas offset (índice) - mucho más fácil mostrar que explicar:

>>> # initialize your skeleton array using 'empty' for lowest-memory footprint 
>>> M = NP.empty(shape=(10, 5), dtype=float) 

>>> # create a small function to mimic step-wise populating this empty 2D array: 
>>> fnx = lambda v : NP.random.randint(0, 10, v) 

pueblan gama NumPy como en el OP, excepto cada iteración simplemente re-establece los valores de M en desplazamientos sucesivos en columnas

>>> for index, itm in enumerate(range(5)):  
     M[:,index] = fnx(10) 

>>> M 
    array([[ 1., 7., 0., 8., 7.], 
     [ 9., 0., 6., 9., 4.], 
     [ 2., 3., 6., 3., 4.], 
     [ 3., 4., 1., 0., 5.], 
     [ 2., 3., 5., 3., 0.], 
     [ 4., 6., 5., 6., 2.], 
     [ 0., 6., 1., 6., 8.], 
     [ 3., 8., 0., 8., 0.], 
     [ 5., 2., 5., 0., 1.], 
     [ 0., 6., 5., 9., 1.]]) 

, por supuesto, si usted no sabe de antemano lo que el tamaño de la matriz debe ser simplemente crear uno mucho más grande de lo que necesita y recortar las partes no utilizadas '' cuando termine de poblarlo

>>> M[:3,:3] 
    array([[ 9., 3., 1.], 
     [ 9., 6., 8.], 
     [ 9., 7., 5.]]) 
+0

Publicación muy útil para un principiante numpy. Una pregunta rápida: ¿hay alguna razón por la cual se utiliza 'para el índice, ITM en enumerate (rango (5)):' en lugar de, por ejemplo, 'for x in range (5):' ya que el índice y el itm tienen el mismo valor y solo se usa uno. –

+0

@ JohnBarça gracias por los comentarios. Es posible que tenga razón en que los detalles de mi fragmento de código deberían haberse elegido con más cuidado, es decir, en mi ejemplo, el valor de 'índice' en cada iteración es de hecho el mismo que el valor de la variable de ciclo. Sin embargo, eso es un artefacto: los valores de estas dos variables probablemente no sean iguales en la práctica (p. Ej., El iterable es una lista que contiene valores para pasar a una función que crea matrices 1D que luego se "insertan" en la matriz objetivo) – doug

1

Generalmente es costoso seguir reasignando la matriz NumPy, por lo que su tercera solución es realmente la mejor en cuanto a rendimiento.

Sin embargo creo que hstack va a hacer lo que quiere - el taco está en el mensaje de error,

ValueError: arrays must have same number of dimensions`

supongo que newColumn tiene dos dimensiones (en lugar de un vector 1D), por lo que necesita de datos también tiene dos dimensiones ..., por ejemplo, data = np.array([[]]) - o alternativamente convierte newColumn en un vector 1D (generalmente, si las cosas son 1D, es mejor mantenerlos 1D en NumPy, por lo que la difusión, etc. funciona mejor). en cuyo caso use np.squeeze(newColumn) y hstack o vstack para que funcione con su definición original de los datos.

4

Normalmente no se sigue cambiando el tamaño de una matriz NumPy cuando se crea. ¿Qué no te gusta de tu tercera solución? Si se trata de una matriz muy grande/matriz, a continuación, puede ser que valga la asignación de la matriz antes de empezar la asignación de sus valores:

x = len(something) 
y = getColumnDataAsNumpyArray.someLengthProperty 

data = numpy.zeros((x,y)) 
for i in something: 
    data[i] = getColumnDataAsNumpyArray(i) 
3

El hstack puede trabajar en matrices de tamaño cero:

import numpy as np 

N = 5 
M = 15 

a = np.ndarray(shape = (N, 0)) 
for i in range(M): 
    b = np.random.rand(N, 1) 
    a = np.hstack((a, b))