Bagaimana cara menormalkan array di NumPy?

204

Saya ingin memiliki norma satu array NumPy. Lebih khusus lagi, saya mencari versi yang setara dari fungsi ini

def normalize(v):
    norm = np.linalg.norm(v)
    if norm == 0: 
       return v
    return v / norm

Apakah ada sesuatu seperti itu di dalam skearnatau numpy?

Fungsi ini berfungsi dalam situasi di mana vvektor 0 berada.

Donbeo
sumber
3
Apa yang salah dengan apa yang Anda tulis?
ali_m
5
Jika ini benar-benar perhatian, Anda harus memeriksa norma <epsilon, di mana epsilon adalah toleransi kecil. Selain itu, saya tidak akan diam-diam mengembalikan vektor norma nol, saya akan raisepengecualian!
Hooked
4
fungsi saya berfungsi tetapi saya ingin tahu apakah ada sesuatu di dalam perpustakaan python yang lebih umum. Saya menulis fungsi pembelajaran mesin yang berbeda dan saya ingin menghindari untuk mendefinisikan terlalu banyak fungsi baru untuk membuat kode lebih jelas dan mudah dibaca
Donbeo
1
Saya melakukan beberapa tes cepat dan saya menemukan x/np.linalg.norm(x)itu tidak jauh lebih lambat (sekitar 15-20%) daripada x/np.sqrt((x**2).sum())di numpy 1.15.1 pada CPU.
Bill

Jawaban:

161

Jika Anda menggunakan scikit-learn Anda dapat menggunakan sklearn.preprocessing.normalize:

import numpy as np
from sklearn.preprocessing import normalize

x = np.random.rand(1000)*10
norm1 = x / np.linalg.norm(x)
norm2 = normalize(x[:,np.newaxis], axis=0).ravel()
print np.all(norm1 == norm2)
# True
ali_m
sumber
2
Terima kasih atas jawabannya tetapi apakah Anda yakin bahwa sklearn.preprocessing.normalisasi juga berfungsi dengan vektor bentuk = (n,) atau (n, 1)? Saya mengalami beberapa masalah dengan perpustakaan ini
Donbeo
normalizemembutuhkan input 2D. Anda bisa meneruskan axis=argumen untuk menentukan apakah Anda ingin menerapkan normalisasi di baris atau kolom array input Anda.
ali_m
9
Perhatikan bahwa argumen 'norma' dari fungsi normalisasi dapat berupa 'l1' atau 'l2' dan defaultnya adalah 'l2'. Jika Anda ingin jumlah vektor menjadi 1 (mis. Distribusi probabilitas), Anda harus menggunakan norm = 'l1' dalam fungsi normalisasi.
Ash
2
Perhatikan juga bahwa np.linalg.norm(x)menghitung norma 'l2' secara default. Jika Anda ingin jumlah vektor Anda menjadi 1, Anda harus menggunakannp.linalg.norm(x, ord=1)
Omid
Catatan: x harus ndarrayberfungsi agar normalize()berfungsi. Kalau tidak, itu bisa a list.
Ramin Melikov
47

Saya setuju bahwa itu baik jika fungsi seperti itu adalah bagian dari baterai yang disertakan. Tapi tidak, sejauh yang saya tahu. Ini adalah versi untuk sumbu acak, dan memberikan kinerja optimal.

import numpy as np

def normalized(a, axis=-1, order=2):
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2==0] = 1
    return a / np.expand_dims(l2, axis)

A = np.random.randn(3,3,3)
print(normalized(A,0))
print(normalized(A,1))
print(normalized(A,2))

print(normalized(np.arange(3)[:,None]))
print(normalized(np.arange(3)))
Eelco Hoogendoorn
sumber
Saya tidak menguji secara mendalam solusi ali_m tetapi dalam beberapa kasus sederhana tampaknya berfungsi. Apakah ada situasi di mana fungsi Anda lebih baik?
Donbeo
1
Saya tidak tahu; tetapi ia bekerja di atas sumbu sewenang-wenang, dan kami memiliki kendali eksplisit atas apa yang terjadi untuk vektor panjang 0.
Eelco Hoogendoorn
1
Sangat bagus! Ini harus dalam numpy - meskipun pesanan mungkin harus datang sebelum poros menurut saya.
Neil G
@EelcoHoogendoorn Ingin tahu mengapa order = 2 dipilih daripada yang lain?
Henry Thornton
7
Karena norma Euclidian / pythagoran adalah yang paling sering digunakan; tidakkah kamu setuju?
Eelco Hoogendoorn
21

Anda dapat menentukan ord untuk mendapatkan norma L1. Untuk menghindari pembagian nol saya menggunakan eps, tapi itu mungkin tidak bagus.

def normalize(v):
    norm=np.linalg.norm(v, ord=1)
    if norm==0:
        norm=np.finfo(v.dtype).eps
    return v/norm
Eduard Feicho
sumber
6
menormalkan [inf, 1, 2]hasil [nan, 0, 0], tetapi bukankah seharusnya demikian [1, 0, 0]?
pasbi
12

Ini juga bisa bekerja untuk Anda

import numpy as np
normalized_v = v / np.sqrt(np.sum(v**2))

tetapi gagal ketika vmemiliki panjang 0.

mrk
sumber
10

Jika Anda memiliki data multidimensi dan ingin setiap sumbu dinormalisasi ke jumlah maksimum atau jumlahnya:

def normalize(_d, to_sum=True, copy=True):
    # d is a (n x dimension) np array
    d = _d if not copy else np.copy(_d)
    d -= np.min(d, axis=0)
    d /= (np.sum(d, axis=0) if to_sum else np.ptp(d, axis=0))
    return d

Menggunakan fungsi numpys peak to peak .

a = np.random.random((5, 3))

b = normalize(a, copy=False)
b.sum(axis=0) # array([1., 1., 1.]), the rows sum to 1

c = normalize(a, to_sum=False, copy=False)
c.max(axis=0) # array([1., 1., 1.]), the max of each row is 1
Jaden Travnik
sumber
Hati-hati jika semua nilai sama dalam matriks asli, maka ptp akan menjadi 0. Division dengan 0 akan mengembalikan nan.
Milso
8

Ada juga fungsi unit_vector()untuk menormalkan vektor dalam modul transformasi populer oleh Christoph Gohlke:

import transformations as trafo
import numpy as np

data = np.array([[1.0, 1.0, 0.0],
                 [1.0, 1.0, 1.0],
                 [1.0, 2.0, 3.0]])

print(trafo.unit_vector(data, axis=1))
Joe
sumber
7

Anda menyebutkan belajar sci-kit, jadi saya ingin berbagi solusi lain.

belajar sci-kit MinMaxScaler

Dalam pembelajaran sci-kit, ada API yang disebut MinMaxScaleryang dapat menyesuaikan rentang nilai yang Anda inginkan.

Ini juga menangani masalah NaN bagi kami.

NaN diperlakukan sebagai nilai yang hilang: diabaikan fit, dan dipertahankan dalam transformasi. ... lihat referensi [1]

Contoh kode

Kode ini sederhana, cukup ketik

# Let's say X_train is your input dataframe
from sklearn.preprocessing import MinMaxScaler
# call MinMaxScaler object
min_max_scaler = MinMaxScaler()
# feed in a numpy array
X_train_norm = min_max_scaler.fit_transform(X_train.values)
# wrap it up if you need a dataframe
df = pd.DataFrame(X_train_norm)
Referensi
WY Hsu
sumber
6

Tanpa sklearndan menggunakan adil numpy. Cukup tentukan fungsi :.

Dengan asumsi bahwa baris adalah variabel dan kolom sampel ( axis= 1):

import numpy as np

# Example array
X = np.array([[1,2,3],[4,5,6]])

def stdmtx(X):
    means = X.mean(axis =1)
    stds = X.std(axis= 1, ddof=1)
    X= X - means[:, np.newaxis]
    X= X / stds[:, np.newaxis]
    return np.nan_to_num(X)

keluaran:

X
array([[1, 2, 3],
       [4, 5, 6]])

stdmtx(X)
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.]])
seralouk
sumber
4

Jika Anda ingin menormalkan vektor fitur dimensi yang disimpan dalam tensor 3D, Anda juga bisa menggunakan PyTorch:

import numpy as np
from torch import FloatTensor
from torch.nn.functional import normalize

vecs = np.random.rand(3, 16, 16, 16)
norm_vecs = normalize(FloatTensor(vecs), dim=0, eps=1e-16).numpy()
maks0r
sumber
4

Jika Anda bekerja dengan vektor 3D, Anda dapat melakukan ini secara ringkas menggunakan toolbelt vg . Ini adalah lapisan ringan di atas numpy dan mendukung nilai tunggal dan vektor ditumpuk.

import numpy as np
import vg

x = np.random.rand(1000)*10
norm1 = x / np.linalg.norm(x)
norm2 = vg.normalize(x)
print np.all(norm1 == norm2)
# True

Saya membuat perpustakaan di startup terakhir saya, yang dimotivasi oleh penggunaan seperti ini: ide-ide sederhana yang terlalu bertele-tele di NumPy.

paulmelnikow
sumber
3

Jika Anda tidak membutuhkan presisi maksimal, fungsi Anda dapat direduksi menjadi:

v_norm = v / (np.linalg.norm(v) + 1e-16)
sergio verduzco
sumber
3

Jika Anda bekerja dengan array multidimensi, ikuti solusi cepat adalah mungkin.

Katakanlah kita memiliki array 2D, yang ingin dinormalisasi dengan sumbu terakhir, sementara beberapa baris memiliki norma nol.

import numpy as np
arr = np.array([
    [1, 2, 3], 
    [0, 0, 0],
    [5, 6, 7]
], dtype=np.float)

lengths = np.linalg.norm(arr, axis=-1)
print(lengths)  # [ 3.74165739  0.         10.48808848]
arr[lengths > 0] = arr[lengths > 0] / lengths[lengths > 0][:, np.newaxis]
print(arr)
# [[0.26726124 0.53452248 0.80178373]
# [0.         0.         0.        ]
# [0.47673129 0.57207755 0.66742381]]
Stanislav Tsepa
sumber