Làm thế nào để tôi xây dựng một mảng numpy từ một máy phát điện?


166

Làm thế nào tôi có thể xây dựng một mảng numpy từ một đối tượng máy phát điện?

Hãy để tôi minh họa vấn đề:

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

Trong trường hợp này, gimme()là trình tạo có đầu ra mà tôi muốn biến thành một mảng. Tuy nhiên, hàm tạo mảng không lặp qua trình tạo, nó chỉ đơn giản là lưu trữ chính trình tạo. Hành vi tôi mong muốn là từ đó numpy.array(list(gimme())), nhưng tôi không muốn trả chi phí bộ nhớ khi có danh sách trung gian và mảng cuối cùng trong bộ nhớ cùng một lúc. Có cách nào hiệu quả hơn về không gian?


6
Đây là một vấn đề thú vị. Tôi đã tìm hiểu điều này bằng cách from numpy import *; print any(False for i in range(1))- làm mờ phần tích hợp any()và tạo ra kết quả ngược lại (như tôi biết bây giờ).
moooeeeep

4
@moooeeeep thật kinh khủng. nếu numpykhông thể (hoặc không muốn) đối xử với các trình tạo như Python, thì ít nhất nó sẽ đưa ra một ngoại lệ khi nhận được một trình tạo như một đối số.
tối đa

1
@max Tôi bước chính xác vào cùng một mỏ. Rõ ràng điều này đã được nêu ra trong danh sách NumPy (và trước đó ) kết luận rằng điều này sẽ không được thay đổi để tăng ngoại lệ và người ta phải luôn sử dụng các không gian tên.
alexei

Câu trả lời:


128

Mảng Numpy yêu cầu độ dài của chúng phải được đặt rõ ràng tại thời điểm tạo, không giống như danh sách python. Điều này là cần thiết để không gian cho mỗi mục có thể được phân bổ liên tiếp trong bộ nhớ. Phân bổ liên tục là tính năng chính của mảng numpy: điều này kết hợp với triển khai mã gốc cho phép các thao tác trên chúng thực thi nhanh hơn nhiều so với danh sách thông thường.

Hãy ghi nhớ điều này, về mặt kỹ thuật không thể lấy một đối tượng trình tạo và biến nó thành một mảng trừ khi bạn:

  1. có thể dự đoán có bao nhiêu phần tử sẽ mang lại khi chạy:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
  2. sẵn sàng lưu trữ các yếu tố của nó trong một danh sách trung gian:

    my_array = numpy.array(list(gimme()))
  3. có thể tạo hai bộ tạo giống nhau, chạy qua cái đầu tiên để tìm tổng chiều dài, khởi tạo mảng và sau đó chạy lại bộ tạo để tìm từng phần tử:

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

1 có lẽ là những gì bạn đang tìm kiếm. 2 là không hiệu quả về không gian và 3 là không hiệu quả về thời gian (bạn phải đi qua trình tạo hai lần).


11
Nội dung array.arraylà một danh sách không liên kết liền kề và bạn có thể chỉ cần đơn giản array.array('f', generator). Nói rằng không thể là sai lầm. Đó chỉ là phân bổ động.
Cuadue

1
Tại sao numpy.array không thực hiện phân bổ bộ nhớ theo cách tương tự như mảng dựng sẵn.array, như Cuadue nói. Sự đánh đổi là gì? Tôi hỏi bởi vì có bộ nhớ được phân bổ liền kề trong cả hai ví dụ. Hay không?
jgomo3

3
numpy giả định kích thước mảng của nó không thay đổi. Nó phụ thuộc rất nhiều vào các khung nhìn khác nhau của cùng một đoạn bộ nhớ, vì vậy, cho phép các mảng được mở rộng và phân bổ lại sẽ cần một lớp bổ sung để kích hoạt các khung nhìn, ví dụ.
tham gia

2
Sử dụng trống nhanh hơn một chút. Vì bạn sẽ khởi tạo các giá trị theo bất kỳ cách nào, không cần phải làm điều này hai lần.
Kaushik Ghose

Xem thêm câu trả lời của @ dhill dưới đây nhanh hơn 1.
Bill

206

Một google đằng sau kết quả stackoverflow này, tôi thấy rằng có một numpy.fromiter(data, dtype, count). Mặc định count=-1có tất cả các yếu tố từ iterable. Nó đòi hỏi dtypephải được thiết lập rõ ràng. Trong trường hợp của tôi, điều này đã làm việc:

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


Làm thế nào bạn sẽ áp dụng điều này cho câu hỏi? numpy.fromiter(gimme(), float, count=-1)không hoạt động. Không đại diện somethingcho cái gì?
Matthias 009

1
@ Matthias009 numpy.fromiter(gimme(), float, count=-1)làm việc cho tôi.
moooeeeep

14
Một chủ đề giải thích lý do tại sao fromiterchỉ hoạt động trên mảng 1D: mail.scipy.org/pipermail/numpy-discussion/2007-August/ .
tối đa

2
fwiw, count=-1không cần phải được chỉ định, vì nó là mặc định.
askewchan

5
Nếu bạn biết chiều dài của lần lặp trước, hãy chỉ định countđể cải thiện hiệu suất. Bằng cách này, nó phân bổ bộ nhớ trước khi lấp đầy nó bằng các giá trị thay vì thay đổi kích thước theo yêu cầu (xem tài liệu của numpy.fromiter)
Eddy

15

Trong khi bạn có thể tạo mảng 1D từ trình tạo numpy.fromiter(), bạn có thể tạo mảng ND từ trình tạo với numpy.stack:

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

Nó cũng hoạt động cho mảng 1D:

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

Lưu ý rằng numpy.stackđang tiêu thụ nội bộ máy phát điện và tạo một danh sách trung gian với arrays = [asanyarray(arr) for arr in arrays]. Việc thực hiện có thể được tìm thấy ở đây .


1
Đây là một giải pháp gọn gàng, cảm ơn vì đã chỉ ra. Nhưng nó dường như chậm hơn một chút (trong ứng dụng của tôi) so với sử dụng np.array(tuple(mygen)). Dưới đây là kết quả kiểm tra: %timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loopso với%timeit np.array(tuple(permutations(range(10), 7))) 1 loop, best of 3: 427 ms per loop
Bill Bill

13
Điều này có vẻ tuyệt vời và làm việc cho tôi. Nhưng với Numpy 1.16.1, tôi nhận được cảnh báo này: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

Hơi tiếp tuyến, nhưng nếu trình tạo của bạn là một sự hiểu biết danh sách, bạn có thể sử dụng numpy.wheređể có được kết quả của mình hiệu quả hơn (tôi đã phát hiện ra điều này trong mã của riêng tôi sau khi xem bài đăng này)


0

Các hàm vstack , hstackdstack có thể lấy làm các trình tạo đầu vào tạo ra các mảng đa chiều.


3
Bạn có thể đưa ra một ví dụ trong trường hợp các liên kết thay đổi hoặc một cái gì đó? :)
Ari Cooper-Davis

Các hàm này có thể lấy một bộ tạo các mảng, không phải là một bộ tạo các giá trị
retnikt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.