Cómo agregar una columna adicional a una matriz NumPy

292

Digamos que tengo una matriz NumPy a:

a = np.array([
    [1, 2, 3],
    [2, 3, 4]
    ])

Y me gustaría agregar una columna de ceros para obtener una matriz b:

b = np.array([
    [1, 2, 3, 0],
    [2, 3, 4, 0]
    ])

¿Cómo puedo hacer esto fácilmente en NumPy?

Peter Smit
fuente

Respuestas:

181

Creo que una solución más sencilla y más rápida de arranque es hacer lo siguiente:

import numpy as np
N = 10
a = np.random.rand(N,N)
b = np.zeros((N,N+1))
b[:,:-1] = a

Y horarios:

In [23]: N = 10

In [24]: a = np.random.rand(N,N)

In [25]: %timeit b = np.hstack((a,np.zeros((a.shape[0],1))))
10000 loops, best of 3: 19.6 us per loop

In [27]: %timeit b = np.zeros((a.shape[0],a.shape[1]+1)); b[:,:-1] = a
100000 loops, best of 3: 5.62 us per loop
JoshAdel
fuente
16
Quiero agregar (985,1) shape np araay a (985,2) np array para hacerlo (985,3) np array, pero no funciona. Recibo el error "no se pudo transmitir la matriz de entrada desde la forma (985) a la forma (985,1)". ¿Qué tiene de malo mi código? Código: np.hstack (data, data1)
Outlier
55
@Outlier debería publicar una nueva pregunta en lugar de hacerla en los comentarios de esta.
JoshAdel
44
@JoshAdel: probé su código en ipython, y creo que hay un error de sintaxis. Es posible que desee intentar cambiar a = np.random.rand((N,N))aa = np.random.rand(N,N)
hlin117
Supongo que esto es una exageración para lo que OP pidió. ¡La respuesta de Op es apta!
lft93ryt
Esto es solo un truco para realizar agregar, insertar o apilar. y no debe ser aceptado como respuesta. Los ingenieros deben considerar usar las respuestas a continuación.
cinqS
326

np.r_[ ... ]y np.c_[ ... ] son alternativas útiles para vstacky hstack, con corchetes [] en lugar de round ().
Un par de ejemplos:

: import numpy as np
: N = 3
: A = np.eye(N)

: np.c_[ A, np.ones(N) ]              # add a column
array([[ 1.,  0.,  0.,  1.],
       [ 0.,  1.,  0.,  1.],
       [ 0.,  0.,  1.,  1.]])

: np.c_[ np.ones(N), A, np.ones(N) ]  # or two
array([[ 1.,  1.,  0.,  0.,  1.],
       [ 1.,  0.,  1.,  0.,  1.],
       [ 1.,  0.,  0.,  1.,  1.]])

: np.r_[ A, [A[1]] ]              # add a row
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 0.,  1.,  0.]])
: # not np.r_[ A, A[1] ]

: np.r_[ A[0], 1, 2, 3, A[1] ]    # mix vecs and scalars
  array([ 1.,  0.,  0.,  1.,  2.,  3.,  0.,  1.,  0.])

: np.r_[ A[0], [1, 2, 3], A[1] ]  # lists
  array([ 1.,  0.,  0.,  1.,  2.,  3.,  0.,  1.,  0.])

: np.r_[ A[0], (1, 2, 3), A[1] ]  # tuples
  array([ 1.,  0.,  0.,  1.,  2.,  3.,  0.,  1.,  0.])

: np.r_[ A[0], 1:4, A[1] ]        # same, 1:4 == arange(1,4) == 1,2,3
  array([ 1.,  0.,  0.,  1.,  2.,  3.,  0.,  1.,  0.])

(La razón de los corchetes [] en lugar de round () es que Python se expande, por ejemplo, 1: 4 en cuadrado, las maravillas de la sobrecarga).

denis
fuente
77
solo estaba buscando información sobre esto, y definitivamente esta es una mejor respuesta que la aceptada, porque cubre agregar una columna adicional al principio y al final, no solo al final como las otras respuestas
Ay0
2
@ Ay0 Exactamente, estaba buscando una manera de agregar una unidad de sesgo a mi red neuronal artificial en lotes en todas las capas a la vez, y esta es la respuesta perfecta.
gaborous
¿Y si quieres agregar n columnas a la vez?
Riley
1
@Riley, ¿puedes dar un ejemplo por favor? Python 3 tiene "desempaquetado iterable", por ejemplo np.c_[ * iterable ]; ver listas de expresiones .
denis
@denis, ¡eso era exactamente lo que estaba buscando!
Riley
148

Uso numpy.append:

>>> a = np.array([[1,2,3],[2,3,4]])
>>> a
array([[1, 2, 3],
       [2, 3, 4]])

>>> z = np.zeros((2,1), dtype=int64)
>>> z
array([[0],
       [0]])

>>> np.append(a, z, axis=1)
array([[1, 2, 3, 0],
       [2, 3, 4, 0]])
gemy
fuente
3
Esto es bueno al insertar columnas más complicadas.
Thomas Ahle
66
Esto es más sencillo que la respuesta de @JoshAdel, pero cuando se trata de grandes conjuntos de datos, es más lento. Elegiría entre los dos dependiendo de la importancia de la legibilidad.
dvj
3
appenden realidad solo llamaconcatenate
rll
53

Una forma, usando hstack , es:

b = np.hstack((a, np.zeros((a.shape[0], 1), dtype=a.dtype)))
Peter Smit
fuente
2
Creo que esta es la solución más elegante.
silvado
2
+1: así es como lo haría, me ganas de publicarlo como respuesta :).
Blair
3
Elimine el dtypeparámetro, no es necesario e incluso no está permitido. Si bien su solución es lo suficientemente elegante, preste atención para no usarla si necesita "agregar" con frecuencia a una matriz. Si no puede crear la matriz completa de una vez y llenarla más tarde, cree una lista de matrices y hstacktodo de una vez.
eumiro
1
@eumiro No estoy seguro de cómo logré obtener el tipo de letra en la ubicación incorrecta, pero el np.zeros necesita un tipo de letra para evitar que todo se vuelva flotante (mientras a es int)
Peter Smit
42

Encuentro lo siguiente más elegante:

b = np.insert(a, 3, values=0, axis=1) # Insert values before column 3

Una ventaja de esto insertes que también le permite insertar columnas (o filas) en otros lugares dentro de la matriz. Además, en lugar de insertar un solo valor, puede insertar fácilmente un vector completo, por ejemplo, duplicar la última columna:

b = np.insert(a, insert_index, values=a[:,2], axis=1)

Lo que lleva a:

array([[1, 2, 3, 3],
       [2, 3, 4, 4]])

Para el momento, insertpodría ser más lento que la solución de JoshAdel:

In [1]: N = 10

In [2]: a = np.random.rand(N,N)

In [3]: %timeit b = np.hstack((a, np.zeros((a.shape[0], 1))))
100000 loops, best of 3: 7.5 µs per loop

In [4]: %timeit b = np.zeros((a.shape[0], a.shape[1]+1)); b[:,:-1] = a
100000 loops, best of 3: 2.17 µs per loop

In [5]: %timeit b = np.insert(a, 3, values=0, axis=1)
100000 loops, best of 3: 10.2 µs per loop
Björn
fuente
1
Esto es muy bueno. Lástima que no puedo hacer insert(a, -1, ...)para agregar la columna. Supongo que lo antepondré en su lugar.
Thomas Ahle
2
@ThomasAhle Puede agregar una fila o columna obteniendo el tamaño en ese eje usando a.shape[axis]. I. e. para agregar una fila, lo haces np.insert(a, a.shape[0], 999, axis=0)y para una columna, lo haces np.insert(a, a.shape[1], 999, axis=1).
blubberdiblub
35

También me interesó esta pregunta y comparé la velocidad de

numpy.c_[a, a]
numpy.stack([a, a]).T
numpy.vstack([a, a]).T
numpy.ascontiguousarray(numpy.stack([a, a]).T)               
numpy.ascontiguousarray(numpy.vstack([a, a]).T)
numpy.column_stack([a, a])
numpy.concatenate([a[:,None], a[:,None]], axis=1)
numpy.concatenate([a[None], a[None]], axis=0).T

que hacen lo mismo para cualquier vector de entrada a. Tiempos para crecer a:

ingrese la descripción de la imagen aquí

Tenga en cuenta que todas las variantes no contiguas (en particular stack/ vstack) son eventualmente más rápidas que todas las variantes contiguas. column_stack(por su claridad y velocidad) parece ser una buena opción si necesita contigüidad.


Código para reproducir la trama:

import numpy
import perfplot

perfplot.save(
    "out.png",
    setup=lambda n: numpy.random.rand(n),
    kernels=[
        lambda a: numpy.c_[a, a],
        lambda a: numpy.ascontiguousarray(numpy.stack([a, a]).T),
        lambda a: numpy.ascontiguousarray(numpy.vstack([a, a]).T),
        lambda a: numpy.column_stack([a, a]),
        lambda a: numpy.concatenate([a[:, None], a[:, None]], axis=1),
        lambda a: numpy.ascontiguousarray(
            numpy.concatenate([a[None], a[None]], axis=0).T
        ),
        lambda a: numpy.stack([a, a]).T,
        lambda a: numpy.vstack([a, a]).T,
        lambda a: numpy.concatenate([a[None], a[None]], axis=0).T,
    ],
    labels=[
        "c_",
        "ascont(stack)",
        "ascont(vstack)",
        "column_stack",
        "concat",
        "ascont(concat)",
        "stack (non-cont)",
        "vstack (non-cont)",
        "concat (non-cont)",
    ],
    n_range=[2 ** k for k in range(20)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)
Nico Schlömer
fuente
1
Bonito gráfico! Sólo pensé que le gustaría saber que bajo el capó, stack, hstack, vstack, column_stack, dstackson todas las funciones auxiliares construidas sobre np.concatenate. Al rastrear la definición de pila , descubrí que np.stack([a,a])está llamando np.concatenate([a[None], a[None]], axis=0). Puede ser bueno agregar np.concatenate([a[None], a[None]], axis=0).Tal perfplot para mostrar que np.concatenatesiempre puede ser al menos tan rápido como sus funciones auxiliares.
unutbu
@unutbu Agregó eso.
Nico Schlömer
Bonita biblioteca, nunca he oído hablar de ella! Es lo suficientemente interesante que obtuve las mismas parcelas, excepto que stack y concat han cambiado de lugar (en las variantes ascont y non-cont). Además, concat-column y column_stack también se intercambian.
Antony Hatchkins
1
¡Guau, amo estas tramas!
jhegedus
Parece que para una operación recursiva de agregar una columna a una matriz, por ejemplo, b = [b, a], algunos de los comandos no funcionan (se genera un error sobre dimensiones desiguales). Los únicos dos que parecen funcionar con matrices de tamaño desigual (es decir, cuando uno es una matriz y otro es un vector 1d) son c_ycolumn_stack
Confundido el
29

Yo creo que:

np.column_stack((a, zeros(shape(a)[0])))

Es más elegante.

usuario2820502
fuente
12

np.concatenate también funciona

>>> a = np.array([[1,2,3],[2,3,4]])
>>> a
array([[1, 2, 3],
       [2, 3, 4]])
>>> z = np.zeros((2,1))
>>> z
array([[ 0.],
       [ 0.]])
>>> np.concatenate((a, z), axis=1)
array([[ 1.,  2.,  3.,  0.],
       [ 2.,  3.,  4.,  0.]])
han4wluc
fuente
np.concatenateparece ser 3 veces más rápido que np.hstackpara las matrices 2x1, 2x2 y 2x3. np.concatenateTambién fue un poco más rápido que copiar las matrices manualmente en una matriz vacía en mis experimentos. Eso es consistente con la respuesta de Nico Schlömer a continuación.
Lenar Hoyt
11

Suponiendo que Mes un (100,3) ndarray y yes un (100,) ndarray appendse puede usar de la siguiente manera:

M=numpy.append(M,y[:,None],1)

El truco es usar

y[:, None]

Esto se convierte yen una (100, 1) matriz 2D.

M.shape

ahora da

(100, 4)
Roel Verhoeven
fuente
Eres un héroe, ¿lo sabes? ¡Eso es precisamente lo que me estoy tirando del pelo durante la última hora! Ty!
John Doe
8

Me gusta la respuesta de JoshAdel debido al enfoque en el rendimiento. Una mejora de rendimiento menor es evitar la sobrecarga de inicializar con ceros, solo para sobrescribirse. Esto tiene una diferencia medible cuando N es grande, se usa vacío en lugar de ceros y la columna de ceros se escribe como un paso separado:

In [1]: import numpy as np

In [2]: N = 10000

In [3]: a = np.ones((N,N))

In [4]: %timeit b = np.zeros((a.shape[0],a.shape[1]+1)); b[:,:-1] = a
1 loops, best of 3: 492 ms per loop

In [5]: %timeit b = np.empty((a.shape[0],a.shape[1]+1)); b[:,:-1] = a; b[:,-1] = np.zeros((a.shape[0],))
1 loops, best of 3: 407 ms per loop
toddInPortland
fuente
Puede utilizar la difusión para llenar la última columna con ceros (o cualquier otro valor), que podría ser más fácil de leer: b[:,-1] = 0. Además, con matrices muy grandes, la diferencia de rendimiento se np.insert()vuelve insignificante, lo que podría ser np.insert()más deseable debido a su brevedad.
blubberdiblub
7

np.insert También sirve el propósito.

matA = np.array([[1,2,3], 
                 [2,3,4]])
idx = 3
new_col = np.array([0, 0])
np.insert(matA, idx, new_col, axis=1)

array([[1, 2, 3, 0],
       [2, 3, 4, 0]])

Inserta valores, aquí new_col, antes de un índice dado, aquí a lo idxlargo de un eje. En otras palabras, los valores recién insertados ocuparán la idxcolumna y moverán lo que estaba originalmente allí y idxhacia atrás.

Tai
fuente
1
Tenga en cuenta que insertno está en su lugar como se podría suponer dado el nombre de la función (ver documentos vinculados en la respuesta).
jneuendorf
5

Agregue una columna adicional a una matriz numpy:

El np.appendmétodo de Numpy toma tres parámetros, los dos primeros son matrices numpy 2D y el tercero es un parámetro de eje que indica a lo largo de qué eje agregar:

import numpy as np  
x = np.array([[1,2,3], [4,5,6]]) 
print("Original x:") 
print(x) 

y = np.array([[1], [1]]) 
print("Original y:") 
print(y) 

print("x appended to y on axis of 1:") 
print(np.append(x, y, axis=1)) 

Huellas dactilares:

Original x:
[[1 2 3]
 [4 5 6]]
Original y:
[[1]
 [1]]
x appended to y on axis of 1:
[[1 2 3 1]
 [4 5 6 1]]
Hassan Bahaloo
fuente
Tenga en cuenta que está agregando y a x aquí en lugar de agregar x a y, es por eso que el vector de columna de y está a la derecha de las columnas de x en el resultado.
Brian Popeck
4

Un poco tarde para la fiesta, pero nadie publicó esta respuesta todavía, así que por razones de integridad: puede hacer esto con la comprensión de la lista, en una simple matriz de Python:

source = a.tolist()
result = [row + [0] for row in source]
b = np.array(result)
btk
fuente
4

Para mí, la siguiente forma parece bastante intuitiva y simple.

zeros = np.zeros((2,1)) #2 is a number of rows in your array.   
b = np.hstack((a, zeros))
Shimon S
fuente
3

En mi caso, tuve que agregar una columna de unos a una matriz NumPy

X = array([ 6.1101, 5.5277, ... ])
X.shape => (97,)
X = np.concatenate((np.ones((m,1), dtype=np.int), X.reshape(m,1)), axis=1)

Después de X.shape => (97, 2)

array([[ 1. , 6.1101],
       [ 1. , 5.5277],
...
Mircea Stanciu
fuente
1

Hay una función específica para esto. Se llama numpy.pad

a = np.array([[1,2,3], [2,3,4]])
b = np.pad(a, ((0, 0), (0, 1)), mode='constant', constant_values=0)
print b
>>> array([[1, 2, 3, 0],
           [2, 3, 4, 0]])

Esto es lo que dice en la cadena de documentación:

Pads an array.

Parameters
----------
array : array_like of rank N
    Input array
pad_width : {sequence, array_like, int}
    Number of values padded to the edges of each axis.
    ((before_1, after_1), ... (before_N, after_N)) unique pad widths
    for each axis.
    ((before, after),) yields same before and after pad for each axis.
    (pad,) or int is a shortcut for before = after = pad width for all
    axes.
mode : str or function
    One of the following string values or a user supplied function.

    'constant'
        Pads with a constant value.
    'edge'
        Pads with the edge values of array.
    'linear_ramp'
        Pads with the linear ramp between end_value and the
        array edge value.
    'maximum'
        Pads with the maximum value of all or part of the
        vector along each axis.
    'mean'
        Pads with the mean value of all or part of the
        vector along each axis.
    'median'
        Pads with the median value of all or part of the
        vector along each axis.
    'minimum'
        Pads with the minimum value of all or part of the
        vector along each axis.
    'reflect'
        Pads with the reflection of the vector mirrored on
        the first and last values of the vector along each
        axis.
    'symmetric'
        Pads with the reflection of the vector mirrored
        along the edge of the array.
    'wrap'
        Pads with the wrap of the vector along the axis.
        The first values are used to pad the end and the
        end values are used to pad the beginning.
    <function>
        Padding function, see Notes.
stat_length : sequence or int, optional
    Used in 'maximum', 'mean', 'median', and 'minimum'.  Number of
    values at edge of each axis used to calculate the statistic value.

    ((before_1, after_1), ... (before_N, after_N)) unique statistic
    lengths for each axis.

    ((before, after),) yields same before and after statistic lengths
    for each axis.

    (stat_length,) or int is a shortcut for before = after = statistic
    length for all axes.

    Default is ``None``, to use the entire axis.
constant_values : sequence or int, optional
    Used in 'constant'.  The values to set the padded values for each
    axis.

    ((before_1, after_1), ... (before_N, after_N)) unique pad constants
    for each axis.

    ((before, after),) yields same before and after constants for each
    axis.

    (constant,) or int is a shortcut for before = after = constant for
    all axes.

    Default is 0.
end_values : sequence or int, optional
    Used in 'linear_ramp'.  The values used for the ending value of the
    linear_ramp and that will form the edge of the padded array.

    ((before_1, after_1), ... (before_N, after_N)) unique end values
    for each axis.

    ((before, after),) yields same before and after end values for each
    axis.

    (constant,) or int is a shortcut for before = after = end value for
    all axes.

    Default is 0.
reflect_type : {'even', 'odd'}, optional
    Used in 'reflect', and 'symmetric'.  The 'even' style is the
    default with an unaltered reflection around the edge value.  For
    the 'odd' style, the extented part of the array is created by
    subtracting the reflected values from two times the edge value.

Returns
-------
pad : ndarray
    Padded array of rank equal to `array` with shape increased
    according to `pad_width`.

Notes
-----
.. versionadded:: 1.7.0

For an array with rank greater than 1, some of the padding of later
axes is calculated from padding of previous axes.  This is easiest to
think about with a rank 2 array where the corners of the padded array
are calculated by using padded values from the first axis.

The padding function, if used, should return a rank 1 array equal in
length to the vector argument with padded values replaced. It has the
following signature::

    padding_func(vector, iaxis_pad_width, iaxis, kwargs)

where

    vector : ndarray
        A rank 1 array already padded with zeros.  Padded values are
        vector[:pad_tuple[0]] and vector[-pad_tuple[1]:].
    iaxis_pad_width : tuple
        A 2-tuple of ints, iaxis_pad_width[0] represents the number of
        values padded at the beginning of vector where
        iaxis_pad_width[1] represents the number of values padded at
        the end of vector.
    iaxis : int
        The axis currently being calculated.
    kwargs : dict
        Any keyword arguments the function requires.

Examples
--------
>>> a = [1, 2, 3, 4, 5]
>>> np.pad(a, (2,3), 'constant', constant_values=(4, 6))
array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6])

>>> np.pad(a, (2, 3), 'edge')
array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5])

>>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4))
array([ 5,  3,  1,  2,  3,  4,  5,  2, -1, -4])

>>> np.pad(a, (2,), 'maximum')
array([5, 5, 1, 2, 3, 4, 5, 5, 5])

>>> np.pad(a, (2,), 'mean')
array([3, 3, 1, 2, 3, 4, 5, 3, 3])

>>> np.pad(a, (2,), 'median')
array([3, 3, 1, 2, 3, 4, 5, 3, 3])

>>> a = [[1, 2], [3, 4]]
>>> np.pad(a, ((3, 2), (2, 3)), 'minimum')
array([[1, 1, 1, 2, 1, 1, 1],
       [1, 1, 1, 2, 1, 1, 1],
       [1, 1, 1, 2, 1, 1, 1],
       [1, 1, 1, 2, 1, 1, 1],
       [3, 3, 3, 4, 3, 3, 3],
       [1, 1, 1, 2, 1, 1, 1],
       [1, 1, 1, 2, 1, 1, 1]])

>>> a = [1, 2, 3, 4, 5]
>>> np.pad(a, (2, 3), 'reflect')
array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])

>>> np.pad(a, (2, 3), 'reflect', reflect_type='odd')
array([-1,  0,  1,  2,  3,  4,  5,  6,  7,  8])

>>> np.pad(a, (2, 3), 'symmetric')
array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])

>>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd')
array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])

>>> np.pad(a, (2, 3), 'wrap')
array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])

>>> def pad_with(vector, pad_width, iaxis, kwargs):
...     pad_value = kwargs.get('padder', 10)
...     vector[:pad_width[0]] = pad_value
...     vector[-pad_width[1]:] = pad_value
...     return vector
>>> a = np.arange(6)
>>> a = a.reshape((2, 3))
>>> np.pad(a, 2, pad_with)
array([[10, 10, 10, 10, 10, 10, 10],
       [10, 10, 10, 10, 10, 10, 10],
       [10, 10,  0,  1,  2, 10, 10],
       [10, 10,  3,  4,  5, 10, 10],
       [10, 10, 10, 10, 10, 10, 10],
       [10, 10, 10, 10, 10, 10, 10]])
>>> np.pad(a, 2, pad_with, padder=100)
array([[100, 100, 100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100, 100, 100],
       [100, 100,   0,   1,   2, 100, 100],
       [100, 100,   3,   4,   5, 100, 100],
       [100, 100, 100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100, 100, 100]])
Ivan Hoffmann
fuente