Comment ajouter une colonne supplémentaire à un tableau NumPy


292

Disons que j'ai un tableau numpy, a:

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

Et je voudrais ajouter une colonne de zéros pour obtenir un tableau b:

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

Comment puis-je le faire facilement dans NumPy?

Réponses:


181

Je pense qu'une solution plus simple et plus rapide à démarrer consiste à procéder comme suit:

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

Et les horaires:

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

16
Je veux ajouter (985,1) forme np araay à (985,2) tableau np pour en faire (985,3) tableau np, mais cela ne fonctionne pas. J'obtiens l'erreur "Impossible de diffuser le tableau d'entrée de la forme (985) à la forme (985,1)". Quel est le problème avec mon code? Code: np.hstack (data, data1)
Valeur aberrante

5
@Plus tôt, vous devriez publier une nouvelle question plutôt que d'en poser une dans les commentaires de celle-ci.
JoshAdel

4
@JoshAdel: J'ai essayé votre code sur ipython, et je pense qu'il y a une erreur de syntaxe. Vous voudrez peut-être essayer de passer a = np.random.rand((N,N))àa = np.random.rand(N,N)
hlin117

Je suppose que c'est une exagération pour ce qu'OP a demandé. La réponse d'Op est juste!
lft93ryt

C'est juste une astuce pour effectuer l'ajout, l'insertion ou la pile. et ne doivent pas être acceptées comme réponses. Les ingénieurs devraient envisager d'utiliser les réponses ci-dessous.
cinqS

326

np.r_[ ... ]et np.c_[ ... ] sont des alternatives utiles à vstacket hstackavec des crochets [] au lieu de round ().
Quelques exemples:

: 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 raison des crochets [] au lieu de round () est que Python se développe par exemple 1: 4 en carré - les merveilles de la surcharge.)


7
je cherchais juste des informations à ce sujet, et c'est définitivement une meilleure réponse que celle acceptée, car elle couvre l'ajout d'une colonne supplémentaire au début et à la fin, pas seulement à la fin comme les autres réponses
Ay0

2
@ Ay0 Exactement, je cherchais un moyen d'ajouter une unité de biais à mon réseau neuronal artificiel en lot sur toutes les couches à la fois, et c'est la réponse parfaite.
génial

Et si vous voulez ajouter n colonnes en un temps?
Riley

1
@Riley, pouvez-vous donner un exemple s'il vous plaît? Python 3 a un "déballage itérable", par exemple np.c_[ * iterable ]; voir les listes d'expressions .
denis

@denis, c'est exactement ce que je cherchais!
Riley

148

Utilisation 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]])

3
C'est bien lorsque vous insérez des colonnes plus compliquées.
Thomas Ahle

6
C'est plus simple que la réponse de @JoshAdel, mais quand il s'agit de grands ensembles de données, c'est plus lent. Je choisirais entre les deux en fonction de l'importance de la lisibilité.
dvj

3
appendappelle justeconcatenate
rll

53

Une façon, en utilisant hstack , est:

b = np.hstack((a, np.zeros((a.shape[0], 1), dtype=a.dtype)))

2
Je pense que c'est la solution la plus élégante.
silvado

2
+1 - c'est comme ça que je le ferais - tu m'as battu pour le poster comme réponse :).
Blair

3
Supprimez le dtypeparamètre, il n'est pas nécessaire et même interdit. Bien que votre solution soit suffisamment élégante, faites attention à ne pas l'utiliser si vous devez "ajouter" fréquemment à une baie. Si vous ne pouvez pas créer le tableau entier à la fois et le remplir plus tard, créez une liste de tableaux et hstacktout cela à la fois.
eumiro

1
@eumiro Je ne sais pas comment je réussi à obtenir le DTYPE au mauvais endroit, mais les np.zeros a besoin d' une DTYPE à tout éviter de devenir flotteur (alors qu'un est int)
Peter Smit

42

Je trouve ce qui suit le plus élégant:

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

Un avantage insertest qu'il vous permet également d'insérer des colonnes (ou lignes) à d'autres endroits à l'intérieur du tableau. De plus, au lieu d'insérer une seule valeur, vous pouvez facilement insérer un vecteur entier, par exemple dupliquer la dernière colonne:

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

Qui conduit à:

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

Pour le timing, insertpourrait être plus lent que la solution 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

1
C'est assez soigné. Dommage que je ne puisse pas faire insert(a, -1, ...)pour ajouter la colonne. Je suppose que je vais juste le préparer à la place.
Thomas Ahle

2
@ThomasAhle Vous pouvez ajouter une ligne ou une colonne en obtenant la taille dans cet axe à l'aide de a.shape[axis]. C'est à dire. pour ajouter une ligne, vous le faites np.insert(a, a.shape[0], 999, axis=0)et pour une colonne, vous le faites np.insert(a, a.shape[1], 999, axis=1).
blubberdiblub

35

J'ai également été intéressé par cette question et j'ai comparé la vitesse 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

qui font tous la même chose pour n'importe quel vecteur d'entrée a. Délais de croissance a:

entrez la description de l'image ici

Notez que toutes les variantes non contiguës (en particulier stack/ vstack) sont finalement plus rapides que toutes les variantes contiguës. column_stack(pour sa clarté et sa vitesse) semble être une bonne option si vous avez besoin de contiguïté.


Code pour reproduire l'intrigue:

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,
)

1
Joli graphique! Juste pensé que vous aimeriez savoir que sous le capot, stack, hstack, vstack, column_stack, dstacksont toutes les fonctions d'aide construites sur np.concatenate. En traçant la définition de la pile, j'ai trouvé que cela np.stack([a,a])appelait np.concatenate([a[None], a[None]], axis=0). Il pourrait être intéressant d'ajouter np.concatenate([a[None], a[None]], axis=0).Tau perfplot pour montrer qu'il np.concatenatepeut toujours être au moins aussi rapide que ses fonctions d'aide.
unutbu

@unutbu Ajouté cela.
Nico Schlömer

Belle bibliothèque, je n'en ai jamais entendu parler! Assez intéressant pour que j'obtienne exactement les mêmes tracés, sauf que la pile et le concat ont changé de place (dans les variantes ascont et non cont). De plus, concat-column et column_stack ont ​​également été échangés.
Antony Hatchkins

1
Wow, j'adore ces parcelles!
jhegedus

Il semble que pour une opération récursive d'ajout d'une colonne à un tableau, par exemple b = [b, a], certaines commandes ne fonctionnent pas (une erreur sur les dimensions inégales est déclenchée). Les deux seuls qui semblent fonctionner avec des tableaux de taille inégale (c'est-à-dire quand l'un est une matrice et un autre un vecteur 1d) sont c_etcolumn_stack
Confondu

29

Je pense:

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

est plus élégant.


12

np.concatenate fonctionne également

>>> 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.]])

np.concatenatesemble être 3 fois plus rapide que np.hstackpour les matrices 2x1, 2x2 et 2x3. np.concatenateétait également très légèrement plus rapide que la copie manuelle des matrices dans une matrice vide dans mes expériences. Cela correspond à la réponse de Nico Schlömer ci-dessous.
Lenar Hoyt

11

En supposant Mest un (100,3) ndarray et yest un (100,) ndarray appendpeut être utilisé comme suit:

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

L'astuce consiste à utiliser

y[:, None]

Cela se transforme yen un tableau 2D (100, 1).

M.shape

donne maintenant

(100, 4)

Vous êtes un héros, vous le savez?! C'est précisément ce que je tire mes cheveux depuis 1 heure! Ty!
John Doe

8

J'aime la réponse de JoshAdel en raison de l'accent mis sur la performance. Une amélioration mineure des performances consiste à éviter la surcharge d'initialisation avec des zéros, pour être écrasée uniquement. Cela a une différence mesurable lorsque N est grand, vide est utilisé au lieu de zéros et la colonne de zéros est écrite comme une étape distincte:

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

Vous pouvez utiliser la diffusion pour remplir la dernière colonne avec des zéros (ou toute autre valeur), ce qui pourrait être plus facile à lire: b[:,-1] = 0. De plus, avec de très grands tableaux, la différence de performances np.insert()devient négligeable, ce qui peut être np.insert()plus souhaitable en raison de sa brièveté.
blubberdiblub

7

np.insert sert également le but.

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]])

Il insère des valeurs, ici new_col, devant un indice donné, ici le idxlong d'un axe. En d'autres termes, les valeurs nouvellement insérées occuperont la idxcolonne et déplaceront celles qui s'y trouvaient à l'origine à et après idx.


1
Notez que ce insertn'est pas en place comme on pourrait le supposer étant donné le nom de la fonction (voir les documents liés dans la réponse).
jneuendorf

5

Ajoutez une colonne supplémentaire à un tableau numpy:

La np.appendméthode de Numpy prend trois paramètres, les deux premiers sont des tableaux numpy 2D et le 3ème est un paramètre d'axe indiquant sur quel axe ajouter:

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)) 

Tirages:

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]]

Notez que vous ajoutez y à x ici plutôt que x à y - c'est pourquoi le vecteur de colonne de y est à droite des colonnes de x dans le résultat.
Brian Popeck

4

Un peu tard pour la fête, mais personne n'a encore posté cette réponse, donc par souci d'exhaustivité: vous pouvez le faire avec des listes de compréhension, sur un tableau Python simple:

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

4

Pour moi, la prochaine façon semble assez intuitive et simple.

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

3

Dans mon cas, j'ai dû ajouter une colonne d'un à un tableau 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)

Après X.shape => (97, 2)

array([[ 1. , 6.1101],
       [ 1. , 5.5277],
...

1

Il existe une fonction spécialement pour cela. Il s'appelle 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]])

Voici ce qu'il dit dans la docstring:

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]])
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.