Як нормалізувати масив у NumPy?


203

Я хотів би мати норму одного масиву NumPy. Більш конкретно, я шукаю еквівалентну версію цієї функції

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

Чи є щось подібне в skearnабо numpy?

Ця функція працює в ситуації, коли vвектор 0.


3
Що не так у тому, що ви написали?
ali_m

5
Якщо це насправді викликає занепокоєння, слід перевірити норму <epsilon, де епсилон - це невелика толерантність. Крім того, я б мовчки не передав назад нульовий вектор норми, я став би raiseвинятком!
Зачепили

4
моя функція працює, але я хотів би знати, чи є щось всередині загальної бібліотеки пітона. Я пишу різні функції машинного навчання, і я хотів би уникати визначення занадто багато нових функцій, щоб зробити код більш зрозумілим і читабельним
Донбео

1
Я зробив кілька швидких тестів і виявив, що x/np.linalg.norm(x)це не набагато повільніше (приблизно 15-20%), ніж x/np.sqrt((x**2).sum())у numpy 1.15.1 на процесорі.
Білл

Відповіді:


160

Якщо ви використовуєте scikit-learn, ви можете використовувати 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

2
Дякую за відповідь, але ви впевнені, що sklearn.preprocessing.normalize працює також з вектором форми = (n,) або (n, 1)? У мене є деякі проблеми з цією бібліотекою
Донбео

normalizeвимагає 2D введення. Ви можете передати axis=аргумент, щоб вказати, чи потрібно застосовувати нормалізацію в рядках або стовпцях вхідного масиву.
ali_m

9
Зауважте, що аргумент "норма" функції нормалізації може бути "l1" або "l2", а за замовчуванням - "l2". Якщо ви хочете, щоб сума вашого вектора дорівнювала 1 (наприклад, розподіл ймовірностей), ви повинні використовувати функцію norma = 'l1' у функції нормалізації.
Еш

2
Також зауважте, що np.linalg.norm(x)за замовчуванням обчислюється норма "l2". Якщо ви хочете, щоб сума вашого вектора дорівнювала 1, вам слід скористатисяnp.linalg.norm(x, ord=1)
Омід,

Примітка: x має бути ndarrayдля роботи з normalize()функцією. Інакше це може бути list.
Рамін Меліков

46

Я погодився б, що було б добре, якби така функція була частиною включених батарей. Але це не так, наскільки я знаю. Ось версія для довільних осей та надання оптимальної продуктивності.

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

Я не глибоко перевіряв рішення рішення Ali_m, але в деяких простих випадках це, здається, працює. Чи є ситуації, коли ваша функція працює краще?
Донбео

1
Не знаю; але він працює над довільними осями, і ми маємо чіткий контроль над тим, що відбувається для векторів довжини 0.
Eelco Hoogendoorn

1
Дуже хороша! Це має бути нумерованим, хоча, на мій погляд, порядок, мабуть, повинен бути перед осі.
Ніл Г

@EelcoHoogendoorn Цікаво зрозуміти, чому порядок = 2 обрано над іншими?
Генрі Торнтон

7
Оскільки норма Евкліда / Піфагора є найбільш часто використовуваною; ти не погодився б?
Eelco Hoogendoorn

21

Ви можете вказати ord для отримання норми L1. Щоб уникнути нульового поділу, я використовую eps, але це, можливо, не чудово.

def normalize(v):
    norm=np.linalg.norm(v, ord=1)
    if norm==0:
        norm=np.finfo(v.dtype).eps
    return v/norm

6
нормалізуючи [inf, 1, 2]врожайність [nan, 0, 0], але чи не має бути [1, 0, 0]?
пасбі

12

Це також може працювати для вас

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

але не вдається, коли vмає довжину 0.


10

Якщо у вас є багатовимірні дані та хочете, щоб кожна вісь нормалізувалася до свого максимуму або до її суми:

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

Використовує функцію numpys пік до піку .

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

Слідкуйте, якщо всі значення однакові в початковій матриці, тоді ptp буде 0. Ділення на 0 поверне нан.
Мільсо

8

Існує також функція unit_vector()нормалізації векторів у популярному модулі перетворень Крістофа Голке:

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

7

Ви згадали про науковий комплект, який навчається, тому я хочу поділитися іншим рішенням.

sci-kit вчитися MinMaxScaler

У науковому комплекті навчання є API, MinMaxScalerякий називається, який може налаштувати діапазон значень за вашим бажанням.

Він також стосується питань NaN для нас.

NaN розглядаються як відсутнє значення: ігнорується у формі та зберігається при перетворенні. ... див. посилання [1]

Зразок коду

Код простий, просто введіть

# 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)
Довідково

6

Без sklearnі просто користування numpy. Просто визначте функцію:.

Якщо припустити, що рядки є змінними, а стовпці - зразками ( 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)

вихід:

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

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

4

Якщо ви хочете нормалізувати n розмірних функціональних векторів, що зберігаються в 3D-тензорі, ви також можете використовувати 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()

4

Якщо ви працюєте з 3D-векторами, ви можете це зробити стисло за допомогою інструментальної стрічки vg . Це легкий шар поверх нумерового, і він підтримує одиничні значення та складені вектори.

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

Я створив бібліотеку під час мого останнього запуску, де її мотивували такі використання: прості ідеї, які занадто багатослівні в NumPy.


3

Якщо вам не потрібна максимальна точність, ваша функція може бути зведена до:

v_norm = v / (np.linalg.norm(v) + 1e-16)

3

Якщо ви працюєте з багатовимірним масивом, можливе наступне швидке рішення.

Скажімо, у нас є двовимірний масив, який ми хочемо нормалізувати за останньою віссю, тоді як деякі рядки мають нульову норму.

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]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.