Bir jeneratörden nasıl numpy dizisi oluşturabilirim?


166

Bir jeneratör nesnesinden nasıl numpy dizisi oluşturabilirim?

Sorunu açıklayayım:

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

Bu örnekte gimme()çıktısını diziye dönüştürmek istediğim jeneratör. Bununla birlikte, dizi yapıcısı jeneratörü tekrarlamaz, sadece jeneratörün kendisini saklar. Arzu ettiğim davranış budur numpy.array(list(gimme())), ancak ara liste ve aynı zamanda bellekte son diziye sahip bellek yükünü ödemek istemiyorum. Yerden daha verimli bir yol var mı?


6
Bu ilginç bir konu. Ben bununla geldim from numpy import *; print any(False for i in range(1))- hangi yerleşik gölgelendirir any()ve zıt sonucu üretir (şimdi bildiğim gibi).
moooeeeep

4
@moooeeeep bu korkunç. eğer numpyyapamaz (ya istemiyor) bir argüman olarak bir jeneratör aldığında Python, en azından bir istisna artırmalıdır yok gibi muamele jeneratörlere.
en fazla

1
@max Ben de aynı madene bastım. Görünüşe göre bu, NumPy listesinde (ve daha önce ) ortaya çıkarıldı , bunun istisna oluşturmak için değiştirilmeyeceği ve her zaman ad alanlarını kullanması gerektiği sonucuna varıldı .
alexei

Yanıtlar:


128

Numpy dizileri, uzunluklarının python listelerinden farklı olarak oluşturma zamanında açıkça ayarlanmasını gerektirir. Bu, her bir öğe için alanın art arda bellekte ayrılabilmesi için gereklidir. Ardışık ayırma, sayısal dizilerin temel özelliğidir: bu, yerel kod uygulamasıyla birlikte, bunların üzerindeki işlemlerin normal listelerden çok daha hızlı yürütülmesini sağlar.

Bunu göz önünde bulundurarak, aşağıdakilerden biri olmadıkça bir jeneratör nesnesini alıp bir diziye dönüştürmek teknik olarak imkansızdır:

  1. çalıştırıldığında kaç element üreteceğini tahmin edebilir:

    my_array = numpy.empty(predict_length())
    for i, el in enumerate(gimme()): my_array[i] = el
  2. öğelerini bir ara listede saklamak istiyorlar:

    my_array = numpy.array(list(gimme()))
  3. iki özdeş jeneratör yapabilir, toplam uzunluğu bulmak için ilkini çalıştırabilir, diziyi başlatabilir ve ardından her bir öğeyi bulmak için jeneratörle tekrar çalışabilirsiniz:

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

1 muhtemelen aradığınız şeydir. 2 , alan verimsiz ve 3 , zaman verimsizdir (jeneratörden iki kez geçmeniz gerekir).


11
Yerleşik array.arraybitişik bağlı olmayan bir listedir ve basitçe yapabilirsiniz array.array('f', generator). Bunun imkansız olduğunu söylemek yanıltıcıdır. Bu sadece dinamik ayırma.
Cuadue

1
Neden numpy.array, bellek ayırmayı Cuadue'nun dediği gibi yerleşik array.array ile aynı şekilde yapmaz. Özellikleri nedir? Soruyorum çünkü her iki örnekte de bitişik ayrılan bellek var. Ya da değil?
jgomo3

3
numpy, dizi boyutlarının değişmeyeceğini varsayar. Aynı bellek yığınının farklı görünümlerine büyük ölçüde güvenir, bu nedenle dizilerin genişletilmesine ve yeniden tahsis edilmesine izin vermek, örneğin görünümleri etkinleştirmek için ek bir dolaylı katman gerektirir.
joeln

2
Boş kullanmak biraz daha hızlıdır. Değerleri herhangi bir şekilde başlatacağınızdan, bunu iki kez yapmanız gerekmez.
Kaushik Ghose

Ayrıca aşağıdaki @ dhill'in 1'den daha hızlı olan cevabına da bakınız.
Bill

206

Bu stackoverflow sonucunun arkasında bir google, bir olduğunu buldum numpy.fromiter(data, dtype, count). Varsayılan count=-1, tüm öğeleri yinelemeden alır. dtypeA'nın açıkça ayarlanmasını gerektirir . Benim durumumda, bu çalıştı:

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


bunu soruya nasıl uygularsınız? numpy.fromiter(gimme(), float, count=-1)çalışmıyor. Ne anlama geliyor something?
Matthias 009

1
@ Matthias009 numpy.fromiter(gimme(), float, count=-1)benim için çalışıyor.
moooeeeep

14
Neden fromiteryalnızca 1D dizilerde çalıştığını açıklayan bir iş parçacığı : mail.scipy.org/pipermail/numpy-discussion/2007-August/… .
en fazla

2
fwiw, count=-1varsayılan olduğu için belirtilmesine gerek yoktur.
askewchan

5
Önceden tekrarlanabilir uzunluğunu biliyorsanız, countperformansı artırmak için. Bu şekilde belleği, talep üzerine yeniden boyutlandırmak yerine değerlerle doldurmadan önce ayırır (belgelerine bakın numpy.fromiter)
Eddy

15

Birlikte bir jeneratörden 1D dizisi oluşturabilirken numpy.fromiter(), aşağıdakileri içeren bir jeneratörden bir ND dizisi oluşturabilirsiniz numpy.stack:

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

Ayrıca 1D dizileri için de çalışır:

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

Not numpy.stackiçten jeneratörü alıcı ve birlikte bir ara liste yaratıyor arrays = [asanyarray(arr) for arr in arrays]. Uygulama burada bulunabilir .


1
Bu temiz bir çözüm, işaret ettiğiniz için teşekkürler. Ancak (uygulamamda) kullanmaktan biraz daha yavaş görünüyor np.array(tuple(mygen)). Test sonuçları: %timeit np.stack(permutations(range(10), 7)) 1 loop, best of 3: 1.9 s per loopkarşılaştırma%timeit np.array(tuple(permutations(range(10), 7))) 1 loop, best of 3: 427 ms per loop
Bill

13
Bu harika görünüyor ve benim için çalışıyor. Ama Numpy 1.16.1 ile şu uyarıyı alıyorum: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

Biraz teğetsel, ancak jeneratörünüz bir liste kavrama ise, numpy.wheresonucunuzu daha etkili bir şekilde elde etmek için kullanabilirsiniz (bu gönderiyi gördükten sonra bunu kendi kodumda keşfettim)


0

Vstack , hstack ve dstack fonksiyonları çok boyutlu diziler elde giriş jeneratörleri olarak sunar.


3
Bağlantıların değişmesi veya bir şey olması durumunda bir örnek verebilir misiniz? :)
Ari Cooper-Davis

Bu işlevler Dizilerin bir jeneratör değil, bir değerler jeneratör alabilir
retnikt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.