จะทำให้อาร์เรย์ NumPy เป็นปกติภายในช่วงที่กำหนดได้อย่างไร


142

หลังจากดำเนินการประมวลผลบางอย่างในอาร์เรย์เสียงหรือรูปภาพแล้วจำเป็นต้องทำให้เป็นมาตรฐานภายในช่วงก่อนจึงจะสามารถเขียนกลับไปยังไฟล์ได้ สามารถทำได้ดังนี้:

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

มีฟังก์ชั่นที่ละเอียดและสะดวกน้อยกว่าในการทำเช่นนี้หรือไม่? matplotlib.colors.Normalize()ดูเหมือนจะไม่เกี่ยวข้องกัน

คำตอบ:


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

การใช้/=และ*=ช่วยให้คุณสามารถกำจัดอาร์เรย์ชั่วคราวระดับกลางซึ่งจะช่วยประหยัดหน่วยความจำบางส่วน การคูณมีค่าใช้จ่ายน้อยกว่าการหารดังนั้น

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

เร็วกว่าเล็กน้อย

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

เนื่องจากเราใช้วิธีการ numpy พื้นฐานที่นี่ฉันจึงคิดว่านี่เป็นวิธีการแก้ปัญหาที่มีประสิทธิภาพพอ ๆ กับที่สามารถทำได้


การดำเนินการในสถานที่ไม่เปลี่ยน dtype ของอาร์เรย์คอนเทนเนอร์ เนื่องจากค่าปกติที่ต้องการเป็นค่าลอยอาร์เรย์audioและimageอาร์เรย์จึงจำเป็นต้องมีประเภทจุดลอยตัวก่อนที่จะดำเนินการในสถานที่ หากพวกเขาไม่ได้อยู่ในจุดลอยตัว dtype astypeคุณจะต้องแปลงให้ใช้ ตัวอย่างเช่น,

image = image.astype('float64')

7
เหตุใดการคูณจึงไม่แพงกว่าการหาร?
endolith

19
ฉันไม่รู้ว่าทำไม อย่างไรก็ตามฉันมั่นใจในการอ้างสิทธิ์โดยได้ตรวจสอบตามเวลา ด้วยการคูณคุณสามารถทำงานกับหนึ่งหลักได้ในแต่ละครั้ง ด้วยการหารโดยเฉพาะอย่างยิ่งกับตัวหารขนาดใหญ่คุณต้องทำงานกับตัวเลขจำนวนมากและ "เดา" ว่าตัวหารจะได้เงินปันผลกี่ครั้ง คุณจบลงด้วยการทำโจทย์การคูณจำนวนมากเพื่อแก้ปัญหาการหารหนึ่ง อัลกอริทึมคอมพิวเตอร์สำหรับการหารอาจไม่เหมือนกับการหารยาวของมนุษย์ แต่ฉันเชื่อว่ามันซับซ้อนกว่าการคูณ
unutbu

14
อาจจะคุ้มค่ากับการหารด้วยศูนย์สำหรับภาพเปล่า
cjm2671

7
การคูณ @endolith มีค่าใช้จ่ายน้อยกว่าการหารเนื่องจากวิธีการใช้งานในระดับ Assembly อัลกอริทึมการหารไม่สามารถขนานได้เช่นเดียวกับอัลกอริทึมการคูณ en.wikipedia.org/wiki/Binary_multiplier
mjones.udri

5
การลดจำนวนการหารให้น้อยที่สุดเพื่อสนับสนุนการคูณเป็นเทคนิคการเพิ่มประสิทธิภาพที่รู้จักกันดี
mjones.udri

79

หากอาร์เรย์มีทั้งข้อมูลบวกและลบฉันจะใช้:

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

หากอาร์เรย์มีnanวิธีแก้ปัญหาหนึ่งวิธีคือลบออกเป็น:

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

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

อย่างไรก็ตามขึ้นอยู่กับบริบทที่คุณอาจต้องการปฏิบัติnanแตกต่างกัน เช่นแก้ไขค่าแทนที่ด้วยเช่น 0 หรือเพิ่มข้อผิดพลาด

สุดท้ายนี้ควรค่าแก่การกล่าวถึงแม้ว่าจะไม่ใช่คำถามของ OP ก็ตามมาตรฐาน :

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

2
ขึ้นอยู่กับสิ่งที่คุณต้องการสิ่งนี้ไม่ถูกต้องเนื่องจากจะพลิกข้อมูล ตัวอย่างเช่นการนอร์มัลไลเซชันเป็น [0, 1] ทำให้ค่าสูงสุดที่ 0 และต่ำสุดที่ 1 สำหรับ [0, 1] คุณสามารถลบผลลัพธ์ออกจาก 1 เพื่อให้ได้ค่านอร์มัลไลซ์ที่ถูกต้อง
Alan Turing

ขอบคุณที่ชี้ให้เห็น @AlanTuring ว่ามันเลอะเทอะมาก รหัสตามที่โพสต์จะใช้งานได้ก็ต่อเมื่อข้อมูลมีทั้งค่าบวกและค่าลบ ซึ่งอาจเป็นเรื่องธรรมดาสำหรับข้อมูลเสียง อย่างไรก็ตามคำตอบได้รับการอัปเดตเพื่อทำให้ค่าที่แท้จริงเป็นปกติ
Tactopoda

1
สุดท้ายยังมีให้เป็นscipy.stats.zscore.
Lewistrick

d อาจพลิกสัญลักษณ์ของตัวอย่าง หากคุณต้องการเก็บเครื่องหมายไว้คุณสามารถใช้ได้: f = a / np.max(np.abs(a))... เว้นแต่อาร์เรย์ทั้งหมดจะเป็นศูนย์ทั้งหมด (หลีกเลี่ยง DivideByZero)
Pimin Konstantin Kefaloukos

1
numpy.ptp()ส่งกลับ 0 ถ้านั่นคือช่วง แต่nanถ้ามีหนึ่งnanในอาร์เรย์ อย่างไรก็ตามหากช่วงเป็น 0 จะไม่มีการกำหนดนอร์มัลไลเซชัน สิ่งนี้ทำให้เกิดข้อผิดพลาดเมื่อเราพยายามหารด้วย 0
Tactopoda

37

คุณยังสามารถขายต่อโดยใช้sklearnไฟล์. ข้อดีคือคุณสามารถปรับค่าเบี่ยงเบนมาตรฐานให้เป็นปกติได้นอกเหนือจากการจัดกึ่งกลางข้อมูลและคุณสามารถทำได้บนแกนใดแกนหนึ่งตามคุณสมบัติหรือตามระเบียน

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

ข้อโต้แย้งคำหลักaxis, with_mean, with_stdมีอธิบายตนเองและมีการแสดงในสถานะเริ่มต้นของพวกเขา อาร์กิวเมนต์ทำการดำเนินการในสถานที่หากมีการตั้งค่าให้copy Falseเอกสารที่นี่ .


X = สเกล ([1,2,3,4], แกน = 0, with_mean = True, with_std = True, copy = True) ทำให้ฉันมีข้อผิดพลาด
Yfiua

X = scale (np.array ([1,2,3,4]), axis = 0, with_mean = True, with_std = True, copy = True) ทำให้ฉันมีอาร์เรย์เป็น [0,0,0,0]
Yfiua

sklearn.preprocessing.scale () มี backdraw ที่คุณไม่รู้ว่าเกิดอะไรขึ้น ปัจจัยคืออะไร? การบีบอัดของช่วงเวลาใด
MasterControlProgram

วิธีการก่อนการประมวลผล scikit เหล่านี้ (สเกล, minmax_scale, maxabs_scale) มีไว้เพื่อใช้กับแกนเดียวเท่านั้น (ดังนั้นให้ปรับขนาดตัวอย่าง (แถว) หรือคุณสมบัติ (คอลัมน์) ทีละรายการสิ่งนี้สมเหตุสมผลในการตั้งค่าการใช้เครื่องจักร แต่บางครั้งคุณก็ต้องการ เพื่อคำนวณช่วงของอาร์เรย์ทั้งหมดหรือใช้อาร์เรย์ที่มีมากกว่าสองมิติ
Toby

11

คุณสามารถใช้เวอร์ชัน "i" (เช่นเดียวกับ idiv, imul .. ) และมันก็ไม่ได้ดูแย่ไปสักครึ่ง:

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

สำหรับอีกกรณีหนึ่งคุณสามารถเขียนฟังก์ชันเพื่อทำให้อาร์เรย์ n มิติเป็นปกติโดย colums:

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

คุณสามารถชี้แจงเรื่องนี้ได้หรือไม่? วงเล็บทำให้มันทำงานแตกต่างจากที่ไม่มี?
endolith

1
parantheses ไม่เปลี่ยนแปลงอะไรเลย ประเด็นคือใช้/=แทน = .. / ..
u0b34a0f6ae

7

คุณกำลังพยายามปรับขนาดค่าต่ำสุดให้audioอยู่ระหว่าง -1 ถึง +1 และimageระหว่าง 0 ถึง 255

การใช้sklearn.preprocessing.minmax_scaleควรแก้ปัญหาของคุณได้อย่างง่ายดาย

เช่น:

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

และ

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

หมายเหตุ : เพื่อไม่ให้สับสนกับการดำเนินการที่ปรับขนาดบรรทัดฐาน (ความยาว) ของเวกเตอร์เป็นค่าหนึ่ง (โดยปกติคือ 1) ซึ่งมักเรียกกันทั่วไปว่า normalization


4

วิธีแก้ปัญหาง่ายๆคือการใช้เครื่องชั่งที่เสนอโดยไลบรารี sklearn.preprocessing

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)

ข้อผิดพลาด X_rec-X จะเป็นศูนย์ คุณสามารถปรับ feature_range ตามความต้องการของคุณหรือแม้กระทั่งใช้สเกลเลอร์มาตรฐาน sk.StandardScaler ()


3

ฉันลองทำตามสิ่งนี้และได้รับข้อผิดพลาด

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

numpyอาร์เรย์ผมพยายามที่จะปกติเป็นintegerอาร์เรย์ ดูเหมือนว่าพวกเขาจะเลิกใช้งานประเภทการแคสต์ในเวอร์ชัน> 1.10และคุณต้องใช้numpy.true_divide()เพื่อแก้ไขปัญหานั้น

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

imgเป็นPIL.Imageวัตถุ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.