Làm cách nào để chuẩn hóa một mảng NumPy trong một phạm vi nhất định?


135

Sau khi thực hiện một số xử lý trên một mảng âm thanh hoặc hình ảnh, nó cần được chuẩn hóa trong một phạm vi trước khi có thể ghi lại vào một tệp. Điều này có thể được thực hiện như vậy:

# Normalize audio channels to between -1.0 and +1.0
audio[:,0] = audio[:,0]/abs(audio[:,0]).max()
audio[:,1] = audio[:,1]/abs(audio[:,1]).max()

# Normalize image to between 0 and 255
image = image/(image.max()/255.0)

Có một cách ít dài dòng, chức năng thuận tiện để làm điều này? matplotlib.colors.Normalize()dường như không liên quan.

Câu trả lời:


136
audio /= np.max(np.abs(audio),axis=0)
image *= (255.0/image.max())

Sử dụng /=*=cho phép bạn loại bỏ một mảng tạm thời trung gian, do đó tiết kiệm một số bộ nhớ. Phép nhân ít tốn kém hơn phép chia, vì vậy

image *= 255.0/image.max()    # Uses 1 division and image.size multiplications

nhanh hơn một chút

image /= image.max()/255.0    # Uses 1+image.size divisions

Vì chúng ta đang sử dụng các phương pháp numpy cơ bản ở đây, tôi nghĩ rằng đây là một giải pháp hiệu quả trong numpy nhất có thể.


Các hoạt động tại chỗ không thay đổi dtype của mảng container. Vì các giá trị chuẩn hóa mong muốn là float, audioimagecác mảng cần phải có dtype điểm nổi trước khi các thao tác tại chỗ được thực hiện. Nếu chúng chưa phải là dtype dấu phẩy động, bạn sẽ cần chuyển đổi chúng bằng cách sử dụng astype. Ví dụ,

image = image.astype('float64')

7
Tại sao phép nhân ít tốn kém hơn phép chia?
endolith

19
Tôi không biết chính xác tại sao. Tuy nhiên, tôi tự tin về khiếu nại, đã kiểm tra nó với thời gian. Với phép nhân, bạn có thể làm việc với một chữ số cùng một lúc. Với phép chia, đặc biệt là với các ước lớn, bạn phải làm việc với nhiều chữ số và "đoán" số lần chia cho số chia. Cuối cùng, bạn sẽ thực hiện nhiều bài toán nhân để giải một bài toán chia. Thuật toán máy tính để thực hiện phép chia có thể không giống như phép chia dài của con người, tuy nhiên tôi tin rằng nó phức tạp hơn phép nhân.
unutbu

14
Có lẽ đáng để đề cập đến một chia cho số không cho hình ảnh trống.
cjm2671

7
Phép nhân @endolith ít tốn kém hơn phép chia vì cách thức thực hiện ở cấp độ hội. Các thuật toán chia không thể song song cũng như các thuật toán nhân. vi.wikipedia.org/wiki/Binary_multiplier
mjones.udri

5
Giảm thiểu số lượng các bộ phận có lợi cho phép nhân là một kỹ thuật tối ưu hóa được biết rõ.
mjones.udri

71

Nếu mảng chứa cả dữ liệu dương và âm, tôi sẽ sử dụng:

import numpy as np

a = np.random.rand(3,2)

# Normalised [0,1]
b = (a - np.min(a))/np.ptp(a)

# Normalised [0,255] as integer: don't forget the parenthesis before astype(int)
c = (255*(a - np.min(a))/np.ptp(a)).astype(int)        

# Normalised [-1,1]
d = 2.*(a - np.min(a))/np.ptp(a)-1

Nếu mảng chứa nan, một giải pháp có thể là loại bỏ chúng như:

def nan_ptp(a):
    return np.ptp(a[np.isfinite(a)])

b = (a - np.nanmin(a))/nan_ptp(a)

Tuy nhiên, tùy thuộc vào bối cảnh bạn có thể muốn đối xử nankhác nhau. Ví dụ: nội suy giá trị, thay thế bằng ví dụ 0 hoặc đưa ra lỗi.

Cuối cùng, đáng nói ngay cả khi đó không phải là câu hỏi của OP, tiêu chuẩn hóa :

e = (a - np.mean(a)) / np.std(a)

2
Tùy thuộc vào những gì bạn muốn, điều này là không chính xác, vì nó lật dữ liệu. Ví dụ: chuẩn hóa thành [0, 1] đặt giá trị tối đa là 0 và tối thiểu là 1. Với [0, 1], bạn có thể đơn giản trừ kết quả từ 1 để có được chuẩn hóa chính xác.
Alan Turing

Cảm ơn bạn đã chỉ ra @AlanTuring rất cẩu thả. Mã, như đã đăng, CHỈ hoạt động nếu dữ liệu chứa cả giá trị dương và âm. Điều đó có thể khá phổ biến đối với dữ liệu âm thanh. Tuy nhiên, câu trả lời được cập nhật để bình thường hóa bất kỳ giá trị thực nào.
Tactopoda

1
Cái cuối cùng cũng có sẵn như scipy.stats.zscore.
Lewistrick

d có thể lật dấu hiệu của các mẫu. Nếu bạn muốn giữ dấu, bạn có thể sử dụng: f = a / np.max(np.abs(a))... trừ khi toàn bộ mảng tất cả các số 0 (tránh DivideByZero).
Pimin Konstantin Kefaloukos

1
numpy.ptp()trả về 0, nếu đó là phạm vi, nhưng nannếu có một nantrong mảng. Tuy nhiên, nếu phạm vi là 0, chuẩn hóa không được xác định. Điều này gây ra lỗi khi chúng tôi cố gắng chia cho 0.
Tactopoda

37

Bạn cũng có thể bán lại bằng cách sử dụng sklearn. Ưu điểm là bạn có thể điều chỉnh bình thường hóa độ lệch chuẩn, ngoài việc định tâm dữ liệu trung bình và bạn có thể thực hiện việc này trên một trong hai trục, theo tính năng hoặc bằng bản ghi.

from sklearn.preprocessing import scale
X = scale( X, axis=0, with_mean=True, with_std=True, copy=True )

Các đối số từ khóa axis, with_mean, with_stdlà tự giải thích, và được thể hiện trong trạng thái mặc định của họ. Đối số copythực hiện thao tác tại chỗ nếu nó được đặt thành False. Tài liệu ở đây .


X = scale ([1,2,3,4], angle = 0, with_mean = True, with_std = True, copy = True) gây ra lỗi cho tôi
Yfiua

X = scale (np.array ([1,2,3,4]), angle = 0, with_mean = True, with_std = True, copy = True) cung cấp cho tôi một mảng [0,0,0,0]
Yfiua

sklearn.pre Processing.scale () có phần rút tiền mà bạn không biết chuyện gì đang xảy ra. Yếu tố là gì? Nén gì của khoảng?
MasterControl

Các phương thức tiền xử lý scikit này (scale, minmax_scale, maxabs_scale) chỉ được sử dụng dọc theo một trục (vì vậy hãy chia tỷ lệ các mẫu (hàng) hoặc các tính năng (cột) riêng lẻ. để tính toán phạm vi trên toàn bộ mảng hoặc sử dụng các mảng có nhiều hơn hai chiều.
Toby

11

Bạn có thể sử dụng phiên bản "i" (như trong idiv, imul ..) và nó trông không tệ bằng một nửa:

image /= (image.max()/255.0)

Trong trường hợp khác, bạn có thể viết một hàm để chuẩn hóa một mảng n chiều bằng colums:

def normalize_columns(arr):
    rows, cols = arr.shape
    for col in xrange(cols):
        arr[:,col] /= abs(arr[:,col]).max()

Bạn có thể làm rõ điều này? Các dấu ngoặc đơn làm cho nó hành xử khác với không?
endolith

1
parantheses không thay đổi bất cứ điều gì. điểm quan trọng là sử dụng /=thay vì = .. / ..
u0b34a0f6ae

7

Bạn đang cố gắng tăng tỷ lệ tối đa các giá trị audiotừ -1 đến +1 và imagetừ 0 đến 255.

Sử dụng sklearn.preprocessing.minmax_scale, nên dễ dàng giải quyết vấn đề của bạn.

ví dụ:

audio_scaled = minmax_scale(audio, feature_range=(-1,1))

shape = image.shape
image_scaled = minmax_scale(image.ravel(), feature_range=(0,255)).reshape(shape)

Lưu ý : Không nên nhầm lẫn với những hoạt động đó quy mô các tiêu chuẩn (chiều dài) của một vector đến một giá trị nhất định (thường là 1), cũng thường được gọi là bình thường.


4

Một giải pháp đơn giản là sử dụng các bộ chia tỷ lệ được cung cấp bởi thư viện sklearn.pre Processing.

scaler = sk.MinMaxScaler(feature_range=(0, 250))
scaler = scaler.fit(X)
X_scaled = scaler.transform(X)
# Checking reconstruction
X_rec = scaler.inverse_transform(X_scaled)

Lỗi X_rec-X sẽ bằng không. Bạn có thể điều chỉnh Feature_range cho nhu cầu của mình hoặc thậm chí sử dụng bộ chia tỷ lệ độc lập sk.St ChuẩnScaler ()


3

Tôi đã thử làm theo điều này và nhận được lỗi

TypeError: ufunc 'true_divide' output (typecode 'd') could not be coerced to provided output parameter (typecode 'l') according to the casting rule ''same_kind''

Các numpymảng Tôi đã cố gắng để bình thường hóa là một integermảng. Có vẻ như họ không dùng kiểu đúc trong các phiên bản> 1.10và bạn phải sử dụng numpy.true_divide()để giải quyết điều đó.

arr = np.array(img)
arr = np.true_divide(arr,[255.0],out=None)

imglà một PIL.Imageđối tượng.

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.