Numpy: divida cada linha por um elemento vetorial

119

Suponha que eu tenha uma matriz numpy:

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

e eu tenho um "vetor:" correspondente

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

Como opero ao datalongo de cada linha para subtrair ou dividir para que o resultado seja:

sub_result = [[0,0,0], [0,0,0], [0,0,0]]
div_result = [[1,1,1], [1,1,1], [1,1,1]]

Resumindo: como faço para executar uma operação em cada linha de um array 2D com um array 1D de escalares que correspondem a cada linha?

BFTM
fonte

Respostas:

181

Aqui está. Você só precisa usar None(ou alternativamente np.newaxis) combinado com a transmissão:

In [6]: data - vector[:,None]
Out[6]:
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])

In [7]: data / vector[:,None]
Out[7]:
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])
JoshAdel
fonte
13
aqui está o doc.
sazary de
2
um exemplo visual
PlsWork
@ user108569 usando a versão mais recente do numpy (1.18.1), Noneainda funciona de forma equivalente ao np.newaxis. Não tenho certeza de qual é a sua configuração ou do problema exato que você está enfrentando, mas a resposta ainda é válida.
JoshAdel
11

Como já foi mencionado, fatiar com Noneou com np.newaxesé uma ótima maneira de fazer isso. Outra alternativa é usar transposes e broadcasting, como em

(data.T - vector).T

e

(data.T / vector).T

Para matrizes de dimensões mais altas, você pode usar o swapaxesmétodo das matrizes NumPy ou a rollaxisfunção NumPy . Realmente, existem muitas maneiras de fazer isso.

Para obter uma explicação mais completa sobre a transmissão, consulte http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html

IanH
fonte
4

A solução de JoshAdel usa np.newaxis para adicionar uma dimensão. Uma alternativa é usar reshape () para alinhar as dimensões na preparação para a transmissão .

data = np.array([[1,1,1],[2,2,2],[3,3,3]])
vector = np.array([1,2,3])

data
# array([[1, 1, 1],
#        [2, 2, 2],
#        [3, 3, 3]])
vector
# array([1, 2, 3])

data.shape
# (3, 3)
vector.shape
# (3,)

data / vector.reshape((3,1))
# array([[1, 1, 1],
#        [1, 1, 1],
#        [1, 1, 1]])

Executar o reshape () permite que as dimensões se alinhem para transmissão:

data:            3 x 3
vector:              3
vector reshaped: 3 x 1

Observe que data/vectorestá tudo bem, mas não lhe dá a resposta que deseja. Ele divide cada coluna de array(em vez de cada linha ) por cada elemento correspondente de vector. É o que você obteria se explicitamente reformulado vectorpara ser em 1x3vez de 3x1.

data / vector
# array([[1, 0, 0],
#        [2, 1, 0],
#        [3, 1, 1]])
data / vector.reshape((1,3))
# array([[1, 0, 0],
#        [2, 1, 0],
#        [3, 1, 1]])
stackoverflowuser2010
fonte
2

A maneira pitônica de fazer isso é ...

np.divide(data.T,vector).T

Isso cuida da remodelagem e também os resultados estão no formato de ponto flutuante. Em outras respostas, os resultados são em formato de número inteiro arredondado.

#NOTA: o número de colunas nos dados e no vetor deve corresponder

Shantanu Pathak
fonte
Nota: Isso não faz o que o OP está solicitando. O resultado final é array ([[1., 0,5, 0,33333333], [2., 1., 0,666666667], [3., 1.5, 1.]]). Pode ser 'Pythônico', mas está incorreto.
Mark Cramer
1
@MarkCramer Obrigado. Eu corrigi minha resposta para fornecer o resultado correto.
shantanu pathak
1

Somando-se à resposta de stackoverflowuser2010, no caso geral, você pode apenas usar

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

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

data / vector.reshape(-1,1)

Isso transformará seu vetor em um column matrix/vector. Permitindo que você faça as operações elementwise como desejar. Pelo menos para mim, esta é a maneira mais intuitiva de fazer isso e como (na maioria dos casos) o numpy usará apenas uma visualização da mesma memória interna para a remodelagem, é eficiente também.

Miau
fonte
Esta deve ser a resposta aceita. Criar um vetor de coluna com .reshape(-1,1) é a maneira mais intuitiva de usar a transmissão.
Paul Rougieux