Bagaimana cara menormalkan array numpy 2 dimensi dengan python less verbose?

89

Diberikan 3 kali 3 numpy array

a = numpy.arange(0,27,3).reshape(3,3)

# array([[ 0,  3,  6],
#        [ 9, 12, 15],
#        [18, 21, 24]])

Untuk menormalkan baris dari array 2 dimensi yang saya pikirkan

row_sums = a.sum(axis=1) # array([ 9, 36, 63])
new_matrix = numpy.zeros((3,3))
for i, (row, row_sum) in enumerate(zip(a, row_sums)):
    new_matrix[i,:] = row / row_sum

Pasti ada cara yang lebih baik, bukan?

Mungkin untuk memperjelas: Dengan menormalkan, maksud saya, jumlah entri per baris harus satu. Tapi saya pikir itu akan menjadi jelas bagi kebanyakan orang.

Aufwind
sumber
17
Hati-hati, "normalisasi" biasanya berarti jumlah kuadrat komponen adalah satu. Definisi Anda tidak akan jelas bagi kebanyakan orang;)
coldfix

Jawaban:

139

Penyiaran sangat bagus untuk ini:

row_sums = a.sum(axis=1)
new_matrix = a / row_sums[:, numpy.newaxis]

row_sums[:, numpy.newaxis]membentuk kembali row_sums dari menjadi (3,)menjadi (3, 1). Ketika Anda melakukannya a / b, adan bdisiarkan melawan satu sama lain.

Anda dapat mempelajari lebih lanjut tentang penyiaran di sini atau bahkan lebih baik di sini .

Bi Rico
sumber
29
Ini dapat disederhanakan lebih jauh dengan menggunakan a.sum(axis=1, keepdims=True)untuk mempertahankan dimensi kolom tunggal, yang kemudian dapat Anda siarkan bersama tanpa harus menggunakan np.newaxis.
ali_m
6
bagaimana jika salah satu row_sums adalah nol?
asdf
8
Ini adalah jawaban yang benar untuk pertanyaan seperti yang dinyatakan di atas - tetapi jika normalisasi dalam arti biasa diinginkan, gunakan np.linalg.normsebagai gantinya a.sum!
coldfix
2
apakah ini lebih disukai row_sums.reshape(3,1)?
Paul
1
Ini tidak sekuat karena jumlah baris mungkin 0.
no
105

Scikit-learn menawarkan fungsi normalize()yang memungkinkan Anda menerapkan berbagai normalisasi. The "make it sum to 1" disebut L1-norm. Karena itu:

from sklearn.preprocessing import normalize

matrix = numpy.arange(0,27,3).reshape(3,3).astype(numpy.float64)
# array([[  0.,   3.,   6.],
#        [  9.,  12.,  15.],
#        [ 18.,  21.,  24.]])

normed_matrix = normalize(matrix, axis=1, norm='l1')
# [[ 0.          0.33333333  0.66666667]
#  [ 0.25        0.33333333  0.41666667]
#  [ 0.28571429  0.33333333  0.38095238]]

Sekarang baris Anda akan berjumlah 1.

rogueleaderr
sumber
3
Ini juga memiliki keuntungan karena ia bekerja pada larik-larik jarang yang tidak akan cocok dengan memori sebagai larik padat.
JEM_Mosig
10

Saya pikir ini harus berhasil,

a = numpy.arange(0,27.,3).reshape(3,3)

a /=  a.sum(axis=1)[:,numpy.newaxis]
tom10
sumber
2
baik. perhatikan perubahan dtype menjadi arange, dengan menambahkan koma desimal ke 27.
wim
4

Jika Anda mencoba menormalkan setiap baris sedemikian rupa sehingga besarnya satu (yaitu panjang satuan baris adalah satu atau jumlah kuadrat setiap elemen dalam satu baris adalah satu):

import numpy as np

a = np.arange(0,27,3).reshape(3,3)

result = a / np.linalg.norm(a, axis=-1)[:, np.newaxis]
# array([[ 0.        ,  0.4472136 ,  0.89442719],
#        [ 0.42426407,  0.56568542,  0.70710678],
#        [ 0.49153915,  0.57346234,  0.65538554]])

Memverifikasi:

np.sum( result**2, axis=-1 )
# array([ 1.,  1.,  1.]) 
walt
sumber
Axis sepertinya bukan parameter untuk np.linalg.norm (lagi?).
Ztyx
terutama ini sesuai dengan norma l2 (di mana baris yang dijumlahkan menjadi 1 sesuai dengan norma l1)
dpb
3

Saya pikir Anda dapat menormalkan elemen baris sum untuk 1 dengan ini: new_matrix = a / a.sum(axis=1, keepdims=1). Dan normalisasi kolom dapat dilakukan dengan new_matrix = a / a.sum(axis=0, keepdims=1). Semoga ini bisa hep.

Snoopy
sumber
2

Anda dapat menggunakan fungsi numpy bawaan: np.linalg.norm(a, axis = 1, keepdims = True)

Saurabh Gupta
sumber
1

tampaknya ini juga berfungsi

def normalizeRows(M):
    row_sums = M.sum(axis=1)
    return M / row_sums
Jamesszm
sumber
1

Anda juga bisa menggunakan transposisi matriks:

(a.T / row_sums).T
Maciek
sumber
0

Atau menggunakan fungsi lambda, seperti

>>> vec = np.arange(0,27,3).reshape(3,3)
>>> import numpy as np
>>> norm_vec = map(lambda row: row/np.linalg.norm(row), vec)

setiap vektor vec akan memiliki norma satuan.

XY.W
sumber
0

Berikut satu lagi cara yang mungkin menggunakan reshape:

a_norm = (a/a.sum(axis=1).reshape(-1,1)).round(3)
print(a_norm)

Atau menggunakan Nonekarya juga:

a_norm = (a/a.sum(axis=1)[:,None]).round(3)
print(a_norm)

Keluaran :

array([[0.   , 0.333, 0.667],
       [0.25 , 0.333, 0.417],
       [0.286, 0.333, 0.381]])
Grayrigel
sumber
-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

di mana input_data adalah nama array 2D Anda

sonali b
sumber