วิธีทำให้อาร์เรย์ numpy 2 มิติเป็นปกติใน python less verbose?


89

ให้อาร์เรย์ตัวเลข 3 คูณ 3

a = numpy.arange(0,27,3).reshape(3,3)

# array([[ 0,  3,  6],
#        [ 9, 12, 15],
#        [18, 21, 24]])

เพื่อทำให้แถวของอาร์เรย์ 2 มิติเป็นปกติฉันคิดไว้

row_sums = a.sum(axis=1) # array([ 9, 36, 63])
new_matrix = numpy.zeros((3,3))
for i, (row, row_sum) in enumerate(zip(a, row_sums)):
    new_matrix[i,:] = row / row_sum

มันต้องมีวิธีที่ดีกว่านี้ไม่ใช่เหรอ?

บางทีการทำให้ชัดเจน: โดยการทำให้เป็นมาตรฐานฉันหมายถึงผลรวมของรายการต่อแถวต้องเป็นหนึ่ง แต่ผมคิดว่าจะชัดเจนสำหรับคนส่วนใหญ่


17
ระวัง "ปกติ" มักจะหมายถึงตารางผลรวมขององค์ประกอบที่เป็นหนึ่ง คำจำกัดความของคุณแทบจะไม่ชัดเจนสำหรับคนส่วนใหญ่)
coldfix

คำตอบ:


139

การออกอากาศเป็นสิ่งที่ดีมากสำหรับสิ่งนี้:

row_sums = a.sum(axis=1)
new_matrix = a / row_sums[:, numpy.newaxis]

row_sums[:, numpy.newaxis]ปรับรูปแบบ row_sums จากการ(3,)เป็น(3, 1)อยู่ เมื่อคุณทำa / b, aและbมีการออกอากาศกับแต่ละอื่น ๆ

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับการกระจายเสียง ที่นี่หรือดียิ่งขึ้นที่นี่


29
สิ่งนี้สามารถทำให้ง่ายขึ้นโดยใช้ a.sum(axis=1, keepdims=True)np.newaxisเพื่อให้มิติคอลัมน์เดี่ยวซึ่งคุณสามารถออกอากาศแล้วก็ไปตามได้โดยไม่ต้องใช้
ali_m

6
จะเกิดอะไรขึ้นถ้า row_sums ใด ๆ เป็นศูนย์?
asdf

8
นี่คือคำตอบที่ถูกต้องสำหรับคำถามตามที่ระบุไว้ข้างต้น - แต่หากต้องการการทำให้เป็นมาตรฐานตามปกติให้ใช้np.linalg.normแทนa.sum!
แก้หวัด

2
เป็นที่ต้องการrow_sums.reshape(3,1)?
พอล

1
มันไม่แข็งแกร่งเท่าเพราะผลรวมแถวอาจเป็น 0
nos

106

Scikit-learn นำเสนอฟังก์ชันnormalize()ที่ช่วยให้คุณสามารถใช้การปรับมาตรฐานต่างๆได้ "ทำให้ผลรวมเป็น 1" เรียกว่า L1-norm ดังนั้น:

from sklearn.preprocessing import normalize

matrix = numpy.arange(0,27,3).reshape(3,3).astype(numpy.float64)
# array([[  0.,   3.,   6.],
#        [  9.,  12.,  15.],
#        [ 18.,  21.,  24.]])

normed_matrix = normalize(matrix, axis=1, norm='l1')
# [[ 0.          0.33333333  0.66666667]
#  [ 0.25        0.33333333  0.41666667]
#  [ 0.28571429  0.33333333  0.38095238]]

ตอนนี้แถวของคุณจะรวมเป็น 1


3
นอกจากนี้ยังมีข้อได้เปรียบที่ทำงานกับอาร์เรย์แบบกระจัดกระจายซึ่งจะไม่พอดีกับหน่วยความจำเท่ากับอาร์เรย์ที่หนาแน่น
JEM_Mosig

10

ฉันคิดว่ามันน่าจะใช้ได้

a = numpy.arange(0,27.,3).reshape(3,3)

a /=  a.sum(axis=1)[:,numpy.newaxis]

2
ดี. สังเกตการเปลี่ยนแปลงของ dtype เป็น arange โดยต่อท้ายจุดทศนิยมเป็น 27
wim

4

ในกรณีที่คุณพยายามทำให้แต่ละแถวเป็นปกติโดยให้ขนาดเป็นหนึ่ง (เช่นความยาวหน่วยของแถวคือหนึ่งหรือผลรวมของกำลังสองของแต่ละองค์ประกอบในแถวเป็นหนึ่ง):

import numpy as np

a = np.arange(0,27,3).reshape(3,3)

result = a / np.linalg.norm(a, axis=-1)[:, np.newaxis]
# array([[ 0.        ,  0.4472136 ,  0.89442719],
#        [ 0.42426407,  0.56568542,  0.70710678],
#        [ 0.49153915,  0.57346234,  0.65538554]])

กำลังตรวจสอบ:

np.sum( result**2, axis=-1 )
# array([ 1.,  1.,  1.]) 

Axis ดูเหมือนจะไม่ใช่พารามิเตอร์ของ np.linalg.norm (อีกต่อไป?)
Ztyx

โดยเฉพาะอย่างยิ่งสิ่งนี้สอดคล้องกับบรรทัดฐาน l2 (โดยที่แถวที่รวมเป็น 1 สอดคล้องกับบรรทัดฐาน l1)
dpb

3

ฉันคิดว่าคุณสามารถทำให้ปกติรวมองค์ประกอบแถว 1 new_matrix = a / a.sum(axis=1, keepdims=1)ตามนี้: และการทำให้เป็นมาตรฐานคอลัมน์สามารถทำได้ด้วยnew_matrix = a / a.sum(axis=0, keepdims=1). หวังว่านี่จะช่วยได้





0

หรือใช้ฟังก์ชันแลมด้าเช่น

>>> vec = np.arange(0,27,3).reshape(3,3)
>>> import numpy as np
>>> norm_vec = map(lambda row: row/np.linalg.norm(row), vec)

เวกเตอร์ของ vec แต่ละตัวจะมีบรรทัดฐานของหน่วย


0

นี่เป็นอีกหนึ่งวิธีที่เป็นไปได้โดยใช้reshape:

a_norm = (a/a.sum(axis=1).reshape(-1,1)).round(3)
print(a_norm)

หรือใช้Noneงานด้วย:

a_norm = (a/a.sum(axis=1)[:,None]).round(3)
print(a_norm)

เอาท์พุต :

array([[0.   , 0.333, 0.667],
       [0.25 , 0.333, 0.417],
       [0.286, 0.333, 0.381]])

-2
normed_matrix = normalize(input_data, axis=1, norm='l1')
print(normed_matrix)

โดยที่ input_data คือชื่อของอาร์เรย์ 2D ของคุณ

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