ฉันจะสร้างอาร์เรย์ numpy จากเครื่องกำเนิดไฟฟ้าได้อย่างไร


166

ฉันจะสร้างอาร์เรย์ numpy จากวัตถุตัวสร้างได้อย่างไร

ให้ฉันอธิบายปัญหา:

>>> import numpy
>>> def gimme():
...   for x in xrange(10):
...     yield x
...
>>> gimme()
<generator object at 0x28a1758>
>>> list(gimme())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numpy.array(xrange(10))
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> numpy.array(gimme())
array(<generator object at 0x28a1758>, dtype=object)
>>> numpy.array(list(gimme()))
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

ในตัวอย่างนี้gimme()เป็นตัวกำเนิดที่มีผลลัพธ์ที่ฉันต้องการเปลี่ยนเป็นอาร์เรย์ อย่างไรก็ตามตัวสร้างอาร์เรย์ไม่ได้วนซ้ำกับตัวกำเนิดมันเพียงเก็บตัวเอง พฤติกรรมที่ฉันต้องการคือจากnumpy.array(list(gimme()))แต่ฉันไม่ต้องการจ่ายค่าใช้จ่ายหน่วยความจำของการมีรายการกลางและอาร์เรย์สุดท้ายในหน่วยความจำในเวลาเดียวกัน มีวิธีที่ประหยัดพื้นที่มากกว่านี้ไหม?


6
นี่เป็นปัญหาที่น่าสนใจ ฉันได้มาโดยนี้from numpy import *; print any(False for i in range(1))- เงาในตัวany()และสร้างผลลัพธ์ที่ตรงกันข้าม (เท่าที่ฉันรู้ตอนนี้)
moooeeeep

4
@moooeeeep มันแย่มาก หากnumpyไม่สามารถ (หรือไม่ต้องการ) ปฏิบัติต่อเครื่องกำเนิดไฟฟ้าตามที่ Python ทำอย่างน้อยก็ควรเพิ่มข้อยกเว้นเมื่อได้รับตัวสร้างเป็นอาร์กิวเมนต์
สูงสุด

1
@ max ฉันเหยียบลงไปบนเหมืองเดียวกัน เห็นได้ชัดว่าสิ่งนี้ได้รับการยกขึ้นในรายการ NumPy (และก่อนหน้านี้ ) สรุปว่าสิ่งนี้จะไม่ถูกเปลี่ยนแปลงเพื่อยกระดับข้อยกเว้นและควรใช้เนมสเปซเสมอ
alexei

คำตอบ:


128

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

โดยคำนึงถึงเรื่องนี้เป็นไปไม่ได้ในทางเทคนิคที่จะนำตัวกำเนิดวัตถุและเปลี่ยนเป็นอาร์เรย์ยกเว้นว่าคุณจะ:

  1. สามารถทำนายจำนวนองค์ประกอบที่จะให้ผลเมื่อทำงาน:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
  2. ยินดีที่จะเก็บองค์ประกอบไว้ในรายการระดับกลาง:

    my_array = numpy.array(list(gimme()))
  3. สามารถสร้างเครื่องกำเนิดไฟฟ้าที่เหมือนกันสองเครื่องวิ่งผ่านเครื่องแรกเพื่อค้นหาความยาวทั้งหมดเริ่มต้นอาร์เรย์จากนั้นเรียกใช้เครื่องกำเนิดไฟฟ้าอีกครั้งเพื่อค้นหาแต่ละองค์ประกอบ:

    length = sum(1 for el in gimme())
    my_array = numpy.empty(length)
    for i, el in enumerate(gimme()): my_array[i] = el

1อาจเป็นสิ่งที่คุณกำลังมองหา 2คือพื้นที่ไม่มีประสิทธิภาพและ3คือเวลาไม่มีประสิทธิภาพ (คุณต้องผ่านตัวกำเนิดสองครั้ง)


11
builtin เป็นรายการที่ไม่ได้เชื่อมโยงต่อเนื่องกันและคุณก็สามารถarray.array array.array('f', generator)การพูดว่าเป็นไปไม่ได้นั้นทำให้เข้าใจผิด มันเป็นเพียงการจัดสรรแบบไดนามิก
Cuadue

1
ทำไม numpy.array ไม่ทำการจัดสรรหน่วยความจำแบบเดียวกับ builtin array.array ตามที่ Cuadue กล่าว การค้าขายคืออะไร? ฉันถามเพราะทั้งสองตัวอย่างมีหน่วยความจำที่จัดสรรอย่างต่อเนื่อง หรือไม่?
jgomo3

3
numpy ถือว่าขนาดอาเรย์ของมันไม่เปลี่ยนแปลง มันอาศัยมุมมองที่แตกต่างกันของหน่วยความจำอันเดียวกันดังนั้นการอนุญาตให้อาร์เรย์ขยายและจัดสรรใหม่จะต้องใช้เลเยอร์ทางอ้อมเพิ่มเติมเพื่อเปิดใช้งานมุมมองตัวอย่างเช่น
joeln

2
ใช้ที่ว่างเปล่าเร็วขึ้นเล็กน้อย เนื่องจากคุณจะเริ่มต้นค่าได้ทุกวิธีไม่จำเป็นต้องทำสองครั้ง
Kaushik Ghose

ดูคำตอบของ @ dhill ด้านล่างซึ่งเร็วกว่า 1
Bill

206

หนึ่งของ Google ที่อยู่เบื้องหลังผล StackOverflow numpy.fromiter(data, dtype, count)นี้ผมพบว่ามี ค่าเริ่มต้นcount=-1ใช้องค์ประกอบทั้งหมดจาก iterable มันต้องมีdtypeการตั้งค่าอย่างชัดเจน ในกรณีของฉันสิ่งนี้ได้ผล:

numpy.fromiter(something.generate(from_this_input), float)


คุณจะใช้สิ่งนี้กับคำถามอย่างไร numpy.fromiter(gimme(), float, count=-1)ไม่ทำงาน, ไม่เป็นผล. สิ่งที่somethingยืนหยัดเพื่อ?
แมทเธียสช 009

1
@ Matthias009 numpy.fromiter(gimme(), float, count=-1)ใช้ได้สำหรับฉัน
moooeeeep

14
ด้ายอธิบายว่าทำไมfromiterทำงานเฉพาะในอาร์เรย์ 1D: mail.scipy.org/pipermail/numpy-discussion/2007-August/...
สูงสุด

2
fwiw count=-1ไม่จำเป็นต้องระบุเนื่องจากเป็นค่าเริ่มต้น
askewchan

5
หากคุณทราบความยาวของการทำซ้ำล่วงหน้าให้ระบุcountเพื่อปรับปรุงประสิทธิภาพ วิธีนี้จะจัดสรรหน่วยความจำก่อนที่จะกรอกด้วยค่ามากกว่าการปรับขนาดตามความต้องการ (ดูเอกสารของnumpy.fromiter)
วน

15

ในขณะที่คุณสามารถสร้างอาร์เรย์ 1D จากตัวสร้างด้วยnumpy.fromiter()คุณสามารถสร้างอาร์เรย์ ND จากตัวสร้างด้วยnumpy.stack:

>>> mygen = (np.ones((5, 3)) for _ in range(10))
>>> x = numpy.stack(mygen)
>>> x.shape
(10, 5, 3)

มันยังใช้งานได้กับ 1D arrays:

>>> numpy.stack(2*i for i in range(10))
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

ทราบว่ามีการบริโภคภายในเครื่องกำเนิดไฟฟ้าและการสร้างรายการกลางด้วยnumpy.stack arrays = [asanyarray(arr) for arr in arrays]การดำเนินงานที่สามารถพบได้ที่นี่


1
นี่เป็นทางออกที่เรียบร้อยขอบคุณสำหรับการชี้ให้เห็น แต่ดูเหมือนว่าจะค่อนข้างช้าลงเล็กน้อย (ในใบสมัครของฉัน) np.array(tuple(mygen))กว่าการใช้ นี่คือผลการทดสอบ: %timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loopเปรียบเทียบกับ%timeit np.array(tuple(permutations(range(10), 7))) 1 loop, best of 3: 427 ms per loop
Bill

13
ดูเหมือนว่าจะดีและใช้งานได้สำหรับฉัน แต่ด้วย Numpy 1.16.1 ฉันได้รับคำเตือนนี้:FutureWarning: arrays to stack must be passed as a "sequence" type such as list or tuple. Support for non-sequence iterables such as generators is deprecated as of NumPy 1.16 and will raise an error in the future.
Joseph Sheedy

6

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


0

vstack , hstackและdstackฟังก์ชั่นสามารถใช้เป็นเครื่องกำเนิดไฟฟ้าการป้อนข้อมูลที่ให้อาร์เรย์หลายมิติ


3
คุณสามารถยกตัวอย่างในกรณีที่ลิงค์เปลี่ยนไปหรือเปล่า? :)
Ari Cooper-Davis

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