วิธีที่มีประสิทธิภาพที่สุดในการย้อนกลับอาร์เรย์ numpy


276

เชื่อหรือไม่ว่าหลังจากการทำโปรไฟล์รหัสปัจจุบันของฉันการดำเนินการซ้ำ ๆ ของการกลับรายการอาร์เรย์ที่มีจำนวนมากนั้นกินชิ้นใหญ่ของเวลาทำงาน สิ่งที่ฉันมีตอนนี้เป็นวิธีการดูตามปกติ:

reversed_arr = arr[::-1]

มีวิธีอื่นที่จะทำอย่างมีประสิทธิภาพมากขึ้นหรือเป็นเพียงภาพลวงตาจากความหลงใหลของฉันกับการแสดงที่ไม่สมจริง?


27
เอ่อ ... arr[::-1]แค่ส่งคืนมุมมองที่กลับด้าน มันเร็วเท่าที่คุณจะได้รับและไม่ได้ขึ้นอยู่กับจำนวนของรายการในอาเรย์เพราะมันแค่เปลี่ยนความก้าวหน้า สิ่งที่คุณกำลังย้อนกลับเป็นอาร์เรย์ที่มีค่า
Joe Kington

ใช่แน่นอน, arrเป็นอาร์เรย์ numpy
nye17

12
อืม ... บนแล็ปท็อปของฉันใช้เวลาประมาณ 670 นาโนวินาทีโดยไม่คำนึงถึงความยาวของอาเรย์ หากเป็นคอขวดของคุณคุณอาจต้องเปลี่ยนภาษา ... ฉันค่อนข้างแน่ใจว่าคุณจะไม่พบวิธีที่รวดเร็วกว่าในการย้อนกลับอาร์เรย์แบบ numpy โชคดีในอัตราใด ๆ !
โจคิงตัน

6
คุณจำเป็นต้องเรียกใช้ภายในลูปหรือไม่? ในบางกรณีจะเป็นการดีกว่าที่จะสร้างอาร์เรย์ที่มีจำนวนรายการนับล้านและดำเนินการกับทั้งอาร์เรย์ แม้ว่าคุณจะใช้วิธีผลต่างอันตะ จำกัด หรือสิ่งที่คล้ายกันซึ่งผลลัพธ์ขึ้นอยู่กับผลลัพธ์ก่อนหน้าบางครั้งคุณสามารถทำได้ (เน้นในบางครั้ง ... ) ไม่ว่าในกรณีใดความเร็วถ้าเป็นเป้าหมายหลัก Fortran ยังคงเป็นราชา f2pyเป็นเพื่อนของคุณ! บ่อยครั้งที่การเขียนส่วนที่สำคัญยิ่งของอัลกอริทึม (โดยเฉพาะในการคำนวณทางวิทยาศาสตร์) ในภาษาอื่นและเรียกมันจากงูใหญ่ โชคดี!
Joe Kington

1
@berto มันจะช้าเนื่องจากเป็นเสื้อคลุมสำหรับarr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py def flipudค้นหา ฟังก์ชั่นมีความยาวสี่บรรทัดอย่างแท้จริง
นักฟิสิกส์บ้า

คำตอบ:


239

เมื่อคุณสร้างreversed_arrคุณกำลังสร้างมุมมองในอาร์เรย์เดิม จากนั้นคุณสามารถเปลี่ยนอาร์เรย์เดิมและมุมมองจะอัปเดตเพื่อแสดงถึงการเปลี่ยนแปลง

คุณสร้างมุมมองใหม่บ่อยกว่าที่คุณต้องการหรือไม่ คุณควรทำสิ่งนี้:

arr = np.array(some_sequence)
reversed_arr = arr[::-1]

do_something(arr)
look_at(reversed_arr)
do_something_else(arr)
look_at(reversed_arr)

ฉันไม่ใช่ผู้เชี่ยวชาญ numpy แต่ดูเหมือนว่ามันจะเป็นวิธีที่เร็วที่สุดในการทำสิ่งต่างๆด้วย numpy หากนี่คือสิ่งที่คุณกำลังทำอยู่ฉันไม่คิดว่าคุณจะปรับปรุงมันได้

ป.ล. การสนทนาที่ยอดเยี่ยมเกี่ยวกับมุมมองที่ไม่น่าสนใจที่นี่:

ดูไปยังอาร์เรย์ numpy?


มันช่วยในการสร้างวัตถุชิ้นแล้วนำมันกลับมาใช้ในหลาย ๆ อาร์เรย์?
endolith

1
ที่จริงฉันเพิ่งทดสอบและไม่เห็นความแตกต่างกับวัตถุชิ้นที่สร้างขึ้นนอกวง (โอ้เดี๋ยวก่อนมันเร็วกว่าเล็กน้อยทำซ้ำ 43.4 ms เทียบกับ 44.3 ms สำหรับการวนซ้ำ 1000000)
endolith

อะไรคือlook_atฟังก์ชั่นคิดว่าจะทำอย่างไร
mrgloom

1
@mrgloom มันควรจะเป็นตัวแทนของงานใด ๆ ที่ดูข้อมูล จุดตัวอย่างคือการแสดงว่ามุมมองreversed_arrยังคงใช้งานได้หลังจากที่ข้อมูลพื้นฐานถูกเปลี่ยนแปลง การเขียนค่าใหม่ลงในอาร์เรย์จะไม่ทำให้มุมมองใช้งานไม่ได้ ที่จริงแล้วคุณสามารถใช้มุมมองสำหรับการเขียนค่าใหม่ลงในอาร์เรย์ reversed_arr[0] = 99จะตั้งค่าองค์ประกอบสุดท้ายในอาร์เรย์เป็น 99 เหมือนกับที่arr[-1] = 99ต้องการ
steveha

60

ดังกล่าวข้างต้นa[::-1]สร้างมุมมองเท่านั้นดังนั้นจึงเป็นการดำเนินการเวลาคงที่ (และเช่นนี้ไม่ใช้เวลานานขึ้นเมื่ออาร์เรย์เติบโตขึ้น) หากคุณต้องการอาร์เรย์ที่ต่อเนื่องกัน (ตัวอย่างเช่นเนื่องจากคุณกำลังดำเนินการกับเวกเตอร์จำนวนมากด้วย) มันascontiguousarrayเร็วพอ ๆ กับflipup / fliplr:

ป้อนคำอธิบายรูปภาพที่นี่


รหัสเพื่อสร้างพล็อต:

import numpy
import perfplot


perfplot.show(
    setup=lambda n: numpy.random.randint(0, 1000, n),
    kernels=[
        lambda a: a[::-1],
        lambda a: numpy.ascontiguousarray(a[::-1]),
        lambda a: numpy.fliplr([a])[0],
    ],
    labels=["a[::-1]", "ascontiguousarray(a[::-1])", "fliplr"],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)

perfplot ต้องการอย่างน้อย Python 3.6 เนื่องจากใช้ f-strings (การแก้ไขสตริงตามตัวอักษร)
fivef

42

เพราะสิ่งนี้ดูเหมือนจะยังไม่ถูกทำเครื่องหมายว่าตอบแล้ว ... คำตอบของ Thomas Arildsen ควรเป็นคำตอบที่เหมาะสมเพียงใช้

np.flipud(your_array) 

ถ้ามันเป็นอาร์เรย์ 1d (อาร์เรย์คอลัมน์)

ด้วย matrizes ทำ

fliplr(matrix)

ถ้าคุณต้องการย้อนกลับแถวและflipud(matrix)ถ้าคุณต้องการพลิกคอลัมน์ ไม่จำเป็นต้องสร้างอาร์เรย์คอลัมน์ 1d ของคุณเป็นอาร์เรย์แถว 2 มิติ (เมทริกซ์ที่มีหนึ่งเลเยอร์ไม่มี) จากนั้นจึงพลิกมัน


38

np.fliplr() พลิกอาร์เรย์จากซ้ายไปขวา

โปรดทราบว่าสำหรับอาร์เรย์ 1d คุณจะต้องใช้เล่ห์เหลี่ยมเล็กน้อย:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]

34
reversed_arr = np.flipud(arr1d)ดูเหมือนว่าจะทำงานโดยตรง
Thomas Arildsen

3

np.fliplr()ผมจะขยายตัวในคำตอบที่ก่อนหน้านี้เกี่ยวกับ นี่คือรหัสบางส่วนที่แสดงให้เห็นถึงการสร้างอาร์เรย์ 1d เปลี่ยนเป็นอาร์เรย์ 2d พลิกมันแล้วแปลงกลับเป็นอาร์เรย์ 1d time.clock()จะใช้ในการรักษาเวลาซึ่งจะแสดงในรูปของวินาที

import time
import numpy as np

start = time.clock()
x = np.array(range(3))
#transform to 2d
x = np.atleast_2d(x)
#flip array
x = np.fliplr(x)
#take first (and only) element
x = x[0]
#print x
end = time.clock()
print end-start

ด้วยคำสั่งพิมพ์ไม่ใส่เครื่องหมายข้อคิดเห็น:

[2 1 0]
0.00203907123594

ด้วยคำสั่งพิมพ์แสดงความคิดเห็น:

5.59799927506e-05

ดังนั้นในแง่ของประสิทธิภาพฉันคิดว่ามันดี สำหรับผู้ที่รักที่จะทำในบรรทัดเดียวนี่คือรูปแบบที่

np.fliplr(np.atleast_2d(np.array(range(3))))[0]

3
การกำหนดเวลาบางอย่างด้วยอาเรย์ขนาดเล็กนั้นค่อนข้างไร้ประโยชน์ ถ้าคุณต้องการเปรียบเทียบสิ่งต่าง ๆ มันจะเป็นการดีกว่าถ้าคุณใช้สิ่งที่ใช้เวลาสักครู่เช่น 3000 หรืออาจจะเป็นองค์ประกอบมากกว่านี้
Barabas

0

ขยายความในสิ่งที่คนอื่นพูดฉันจะยกตัวอย่างสั้น ๆ

หากคุณมีอาร์เรย์ 1D ...

>>> import numpy as np
>>> x = np.arange(4) # array([0, 1, 2, 3])
>>> x[::-1] # returns a view
Out[1]: 
array([3, 2, 1, 0])

แต่ถ้าคุณทำงานกับอาเรย์ 2 มิติ ...

>>> x = np.arange(10).reshape(2, 5)
>>> x
Out[2]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> x[::-1] # returns a view:
Out[3]: array([[5, 6, 7, 8, 9],
               [0, 1, 2, 3, 4]])

สิ่งนี้ไม่ได้ย้อนกลับเมทริกซ์

ควรใช้ np.flip เพื่อย้อนกลับองค์ประกอบ

>>> np.flip(x)
Out[4]: array([[9, 8, 7, 6, 5],
               [4, 3, 2, 1, 0]])

หากคุณต้องการพิมพ์องค์ประกอบของเมทริกซ์แบบหนึ่งต่อหนึ่งใช้แบนพร้อมพลิก

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0

-1

เพื่อให้มันทำงานกับตัวเลขติดลบและรายการยาวคุณสามารถทำสิ่งต่อไปนี้:

b = numpy.flipud(numpy.array(a.split(),float))

ที่ flipud สำหรับ 1d arra

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