คุณจะได้ขนาดของเวกเตอร์ใน Numpy ได้อย่างไร


158

ในการรักษาด้วย "มีเพียงวิธีเดียวที่ชัดเจนในการทำ" คุณจะได้ขนาดของเวกเตอร์ (อาร์เรย์ 1D) ใน Numpy ได้อย่างไร

def mag(x): 
    return math.sqrt(sum(i**2 for i in x))

ผลงานดังกล่าวข้างต้น แต่ฉันไม่สามารถเชื่อได้ว่าฉันจะต้องระบุฟังก์ชั่นที่น่าสนใจและสำคัญดังกล่าวด้วยตัวเอง


1
ฉันมักจะใช้linalg.normตามที่ระบุไว้ด้านล่าง แต่ง่ายกว่าสิ่งที่แลมบ์ดาของคุณเล็กน้อยกับการนำเข้าไม่จำเป็นต้องเป็นเพียงsum(x*x)**0.5
Wim

7
อย่างไรก็ตามไม่มีเหตุผลที่ดีที่จะกำหนดฟังก์ชั่นแลมบ์ดาให้กับชื่อ
Wim

@ ทำไมต้องเป็นอย่างนั้น? ฉันควรจะใช้เฉพาะdefเมื่อประกาศฟังก์ชั่นเช่นนั้น? ฉันคิดว่าถ้ามันถูกต้องตามกฎหมายบรรทัดเดียวมันจะทำให้อ่านง่ายขึ้น
Nick T

6
แลมบ์ดาตั้งใจที่จะเป็นฟังก์ชั่นที่ไม่ระบุตัวตนดังนั้นโดยตั้งชื่อที่คุณทำผิด มันเป็นแค่เวอร์ชัน def ที่พิการแล้ว และถ้าคุณยืนยันคุณสามารถใส่ def หนึ่งบรรทัด สถานที่ปกติที่คุณอาจได้รับการพิสูจน์ว่าใช้แลมบ์ดาสำหรับใช้ในรายการอาร์กิวเมนต์บางอย่างที่เรียกได้ คนที่ผิดพลาดโดยใช้มันเหมือนที่ปรากฏข้างต้นเป็นหนึ่งในเหตุผลว่าทำไมมันทำมันลงบนรายการ Guido ของความเสียใจหลาม (ดูสไลด์ 4)
Wim

6
ลิงค์ตาย! ลองลิงค์!
daviewales

คำตอบ:


209

numpy.linalg.normฟังก์ชั่นที่คุณหลังจากเป็น (ฉันคิดว่ามันควรจะเป็นฐาน numpy เป็นคุณสมบัติของอาร์เรย์ - พูดx.norm()- แต่แหมดี)

import numpy as np
x = np.array([1,2,3,4,5])
np.linalg.norm(x)

นอกจากนี้คุณยังสามารถป้อนตัวเลือกordสำหรับบรรทัดฐานลำดับที่ n ที่คุณต้องการ สมมติว่าคุณต้องการ 1-norm

np.linalg.norm(x,ord=1)

และอื่น ๆ


14
"ควรเป็นสมบัติของอาร์เรย์: x.norm ()" ฉันเห็นด้วยอย่างยิ่ง โดยปกติเมื่อทำงานกับ numpy ฉันใช้คลาสย่อย Array และ Matrix ของตัวเองที่มีฟังก์ชั่นทั้งหมดที่ฉันใช้เป็นวิธีการดึง Matrix.randn([5,5])
mdaoust

3
นอกจากนี้สำหรับเมทริกซ์ที่ประกอบด้วยเวกเตอร์np.linalg.normตอนนี้มีaxisอาร์กิวเมนต์ใหม่ที่กล่าวถึงที่นี่: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis

95

หากคุณกังวลเรื่องความเร็วคุณควรใช้:

mag = np.sqrt(x.dot(x))

นี่คือมาตรฐานบางประการ:

>>> import timeit
>>> timeit.timeit('np.linalg.norm(x)', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0450878
>>> timeit.timeit('np.sqrt(x.dot(x))', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0181372

แก้ไข: การปรับปรุงความเร็วจริงมาเมื่อคุณต้องใช้บรรทัดฐานของเวกเตอร์จำนวนมาก การใช้ฟังก์ชั่น Pure Numpy นั้นไม่จำเป็นต้องมีการวนซ้ำ ตัวอย่างเช่น:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 4.23 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 18.9 us per loop

In [5]: np.allclose([np.linalg.norm(x) for x in a],np.sqrt((a*a).sum(axis=1)))
Out[5]: True

1
จริง ๆ แล้วฉันใช้วิธีนี้เล็กน้อยน้อยชัดเจนหลังจากการค้นหาที่np.linalg.normเป็นคอขวด แต่จากนั้นฉันไปอีกขั้นหนึ่งและใช้เพียงmath.sqrt(x[0]**2 + x[1]**2)ซึ่งเป็นการปรับปรุงที่สำคัญอีกอย่างหนึ่ง
Nick T

@NickT ดูการแก้ไขของฉันเพื่อการปรับปรุงจริงเมื่อใช้ฟังก์ชั่น pure numpy
user545424

2
แอปพลิเคชั่นสุดยอดของผลิตภัณฑ์ dot!
vktec

1
numpy.linalg.normมีมาตรการป้องกันล้นที่การใช้งานนี้ข้าม [1e200, 1e200]ยกตัวอย่างเช่นลองคำนวณบรรทัดฐานของ มีเหตุผลคือถ้ามันจะช้า ...
Federico Poloni

@FedericoPoloni อย่างน้อยกับรุ่น numpy 1.13.3 ฉันได้รับเมื่อคำนวณinf np.linalg.norm([1e200,1e200])
user545424

16

อีกทางเลือกหนึ่งคือการใช้einsumฟังก์ชั่นใน numpy สำหรับอาร์เรย์อย่างใดอย่างหนึ่ง:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 3.86 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 15.6 µs per loop

In [5]: %timeit np.sqrt(np.einsum('ij,ij->i',a,a))
100000 loops, best of 3: 8.71 µs per loop

หรือเวกเตอร์:

In [5]: a = np.arange(100000)

In [6]: %timeit np.sqrt(a.dot(a))
10000 loops, best of 3: 80.8 µs per loop

In [7]: %timeit np.sqrt(np.einsum('i,i', a, a))
10000 loops, best of 3: 60.6 µs per loop

อย่างไรก็ตามมีดูเหมือนจะมีค่าใช้จ่ายที่เกี่ยวข้องกับการโทรมันที่อาจทำให้มันช้าลงด้วยอินพุตเล็ก ๆ :

In [2]: a = np.arange(100)

In [3]: %timeit np.sqrt(a.dot(a))
100000 loops, best of 3: 3.73 µs per loop

In [4]: %timeit np.sqrt(np.einsum('i,i', a, a))
100000 loops, best of 3: 4.68 µs per loop

numpy.linalg.normมีมาตรการป้องกันล้นที่การใช้งานนี้ข้าม [1e200, 1e200]ยกตัวอย่างเช่นลองคำนวณบรรทัดฐานของ มีเหตุผลถ้ามันช้ากว่านี้ ...
Federico Poloni

7

วิธีที่เร็วที่สุดที่ฉันพบคือผ่าน inner1d นี่เป็นวิธีเปรียบเทียบกับวิธีการอื่น ๆ ของ numpy:

import numpy as np
from numpy.core.umath_tests import inner1d

V = np.random.random_sample((10**6,3,)) # 1 million vectors
A = np.sqrt(np.einsum('...i,...i', V, V))
B = np.linalg.norm(V,axis=1)   
C = np.sqrt((V ** 2).sum(-1))
D = np.sqrt((V*V).sum(axis=1))
E = np.sqrt(inner1d(V,V))

print [np.allclose(E,x) for x in [A,B,C,D]] # [True, True, True, True]

import cProfile
cProfile.run("np.sqrt(np.einsum('...i,...i', V, V))") # 3 function calls in 0.013 seconds
cProfile.run('np.linalg.norm(V,axis=1)')              # 9 function calls in 0.029 seconds
cProfile.run('np.sqrt((V ** 2).sum(-1))')             # 5 function calls in 0.028 seconds
cProfile.run('np.sqrt((V*V).sum(axis=1))')            # 5 function calls in 0.027 seconds
cProfile.run('np.sqrt(inner1d(V,V))')                 # 2 function calls in 0.009 seconds

inner1d คือ ~ 3x เร็วกว่า linalg.norm และผมเร็วกว่า einsum


ที่จริงจากสิ่งที่คุณเขียนข้างต้นlinalg.normเป็นวิธีที่เร็วเนื่องจากไม่เป็น 9 สายใน 29ms ดังนั้น 1 การโทรใน 3.222ms เทียบกับ 1 การโทรใน 4.5ms inner1dสำหรับ
patapouf_ai

@bisounours_tronconneuse เวลาสำหรับการดำเนินการทั้งหมด หากคุณเรียกใช้รหัสข้างต้นคุณจะได้รับการแบ่งเวลาต่อการเรียกใช้ฟังก์ชัน หากคุณยังมีข้อสงสัยให้เปลี่ยนจำนวนเวกเตอร์เป็นสิ่งที่มีขนาดใหญ่มากอย่างเช่น((10**8,3,))จากนั้นเรียกใช้ด้วยตนเองnp.linalg.norm(V,axis=1)ตามด้วยnp.sqrt(inner1d(V,V))คุณจะสังเกตได้ว่าlinalg.normจะล่าช้ากว่า inner1d
Fnord

ตกลง. ขอบคุณสำหรับการชี้แจง
patapouf_ai

numpy.linalg.normมีมาตรการป้องกันล้นที่การใช้งานนี้ข้าม [1e200, 1e200]ยกตัวอย่างเช่นลองคำนวณบรรทัดฐานของ มีเหตุผลถ้ามันช้ากว่านี้ ...
Federico Poloni

3

ใช้ฟังก์ชั่นนอร์มในscipy.linalg (หรือnumpy.linalg )

>>> from scipy import linalg as LA
>>> a = 10*NP.random.randn(6)
>>> a
  array([  9.62141594,   1.29279592,   4.80091404,  -2.93714318,
          17.06608678, -11.34617065])
>>> LA.norm(a)
    23.36461979210312

>>> # compare with OP's function:
>>> import math
>>> mag = lambda x : math.sqrt(sum(i**2 for i in x))
>>> mag(a)
     23.36461979210312

1

คุณสามารถทำเช่นนี้รัดกุมโดยใช้แถบเครื่องมือVG มันเป็นชั้นแสงที่อยู่ด้านบนของ numpy และรองรับค่าเดียวและเวกเตอร์ที่ซ้อนกัน

import numpy as np
import vg

x = np.array([1, 2, 3, 4, 5])
mag1 = np.linalg.norm(x)
mag2 = vg.magnitude(x)
print mag1 == mag2
# True

ฉันสร้างห้องสมุดเมื่อเริ่มต้นครั้งล่าสุดซึ่งมีแรงบันดาลใจจากการใช้งานเช่นนี้: แนวคิดง่ายๆที่เกินความจริงใน NumPy

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