Làm thế nào để bình thường hóa một mảng trong NumPy?


203

Tôi muốn có định mức của một mảng NumPy. Cụ thể hơn, tôi đang tìm kiếm một phiên bản tương đương của chức năng này

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

Có một cái gì đó như thế trong skearnhoặc numpy?

Hàm này hoạt động trong một tình huống vlà vectơ 0.


3
Có gì sai với những gì bạn đã viết?
ali_m

5
Nếu đây thực sự là một mối quan tâm, bạn nên kiểm tra định mức <epsilon, trong đó epsilon là dung sai nhỏ. Ngoài ra, tôi sẽ không âm thầm trả lại một vectơ không chuẩn, tôi sẽ raiselà một ngoại lệ!
Móc vào

4
chức năng của tôi hoạt động nhưng tôi muốn biết liệu có gì bên trong thư viện phổ biến hơn của con trăn không. Tôi đang viết các chức năng học máy khác nhau và tôi muốn tránh xác định quá nhiều chức năng mới để làm cho mã rõ ràng và dễ đọc hơn
Donbeo

1
Tôi đã thực hiện một vài thử nghiệm nhanh và tôi thấy rằng nó x/np.linalg.norm(x)không chậm hơn nhiều (khoảng 15-20%) so với x/np.sqrt((x**2).sum())1.15.1 trên numpy trên CPU.
Bill

Câu trả lời:


160

Nếu bạn đang sử dụng scikit-learn, bạn có thể sử dụng 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
Cảm ơn câu trả lời nhưng bạn có chắc chắn rằng sklearn.pre Processing.n normalize cũng hoạt động với vectơ hình dạng = (n,) hoặc (n, 1)? Tôi đang gặp một số vấn đề với thư viện này
Donbeo

normalizeyêu cầu đầu vào 2D. Bạn có thể truyền axis=đối số để chỉ định xem bạn có muốn áp dụng chuẩn hóa trên các hàng hoặc cột của mảng đầu vào không.
ali_m

9
Lưu ý rằng đối số 'định mức' của hàm chuẩn hóa có thể là 'l1' hoặc 'l2' và mặc định là 'l2'. Nếu bạn muốn tổng của vectơ của bạn là 1 (ví dụ: phân phối xác suất), bạn nên sử dụng Norm = 'l1' trong hàm chuẩn hóa.
Tro

2
Cũng lưu ý rằng np.linalg.norm(x)tính toán định mức 'l2' theo mặc định. Nếu bạn muốn tổng của vectơ của bạn là 1, bạn nên sử dụngnp.linalg.norm(x, ord=1)
Omid

Lưu ý: x phải ndarraycho nó hoạt động với normalize()hàm. Nếu không thì có thể là a list.
Ramin Melikov

46

Tôi đồng ý rằng thật tuyệt nếu chức năng như vậy là một phần của pin đi kèm. Nhưng nó không phải, theo như tôi biết. Đây là một phiên bản cho các trục tùy ý và cho hiệu suất tối ưu.

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

Tôi đã không kiểm tra sâu giải pháp ali_m nhưng trong một số trường hợp đơn giản, nó dường như đang hoạt động. Có chỗ ngồi mà chức năng của bạn làm tốt hơn?
Donbeo

1
Tôi không biết; nhưng nó hoạt động trên các trục tùy ý và chúng ta có quyền kiểm soát rõ ràng đối với những gì xảy ra đối với các vectơ có độ dài 0.
Eelco Hoogendoorn

1
Rất đẹp! Điều này nên ở trong numpy - mặc dù theo thứ tự có lẽ nên đến trước trục theo ý kiến ​​của tôi.
Neil G

@EelcoHoogendoorn Tò mò muốn hiểu tại sao order = 2 được chọn hơn người khác?
Henry Thornton

7
Bởi vì định mức Euclidian / pythagoran là thứ được sử dụng thường xuyên nhất; bạn có đồng ý không
Eelco Hoogendoorn

21

Bạn có thể chỉ định ord để lấy định mức L1. Để tránh phân chia số 0, tôi sử dụng eps, nhưng điều đó có thể không tuyệt vời.

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

6
bình thường hóa [inf, 1, 2]sản lượng [nan, 0, 0], nhưng không nên [1, 0, 0]?
pasbi

12

Điều này cũng có thể làm việc cho bạn

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

nhưng thất bại khi vcó độ dài 0.


10

Nếu bạn có dữ liệu đa chiều và muốn mỗi trục được chuẩn hóa ở mức tối đa hoặc tổng của nó:

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

Sử dụng hàm numpys để hàm đỉnh .

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

Coi chừng nếu tất cả các giá trị đều giống nhau trong ma trận gốc, thì ptp sẽ là 0. Chia cho 0 sẽ trả về nan.
Milso

8

Ngoài ra còn có chức năng unit_vector()chuẩn hóa các vectơ trong mô đun biến đổi phổ biến của 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))

7

Bạn đã đề cập đến sci-kit tìm hiểu, vì vậy tôi muốn chia sẻ một giải pháp khác.

khoa học MinMaxScaler

Trong sci-kit learn, có một API được gọi là MinMaxScaler có thể tùy chỉnh phạm vi giá trị theo ý muốn.

Nó cũng giải quyết các vấn đề NaN cho chúng tôi.

NaN được coi là giá trị thiếu: không quan tâm đến sự phù hợp và được duy trì trong biến đổi. ... xem tài liệu tham khảo [1]

Mẫu mã

Mã rất đơn giản, chỉ cần gõ

# 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)
Tài liệu tham khảo

6

Không có sklearnvà chỉ sử dụngnumpy . Chỉ cần xác định một chức năng :.

Giả sử rằng các hàng là các biếncác cột mẫu ( 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)

đầu ra:

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

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

4

Nếu bạn muốn bình thường hóa các vectơ đặc trưng n chiều được lưu trữ trong một tenxơ 3D, bạn cũng có thể sử dụng 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

Nếu bạn đang làm việc với các vectơ 3D, bạn có thể thực hiện việc này một cách chính xác bằng cách sử dụng toolbelt vg . Đó là một lớp ánh sáng trên đỉnh của numpy và nó hỗ trợ các giá trị đơn và các vectơ xếp chồng lên nhau.

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

Tôi đã tạo thư viện ở lần khởi động cuối cùng của mình, nơi nó được thúc đẩy bởi những cách sử dụng như thế này: những ý tưởng đơn giản quá dài dòng trong NumPy.


3

Nếu bạn không cần độ chính xác tối đa, chức năng của bạn có thể được giảm xuống:

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

3

Nếu bạn làm việc với mảng nhiều chiều theo giải pháp nhanh là có thể.

Giả sử chúng ta có mảng 2D, mà chúng ta muốn chuẩn hóa theo trục cuối cùng, trong khi một số hàng có định mức bằng không.

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]]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.