2016-08-04 17 views
5

NumPy jest bardzo pomocny przy tworzeniu tablic. Jeśli pierwszy argument dla numpy.array ma metodę __getitem__ i __len__, są one używane na tej podstawie, że może to być poprawna sekwencja.Zapobieganie tworzeniu się wielowymiarowej tablicy numpy

Niestety, chcę utworzyć tablicę zawierającą dtype=object, a NumPy nie będzie "pomocna".

podziale na minimalnym przykład klasa chciałby to:

import numpy as np 

class Test(object): 
    def __init__(self, iterable): 
     self.data = iterable 

    def __getitem__(self, idx): 
     return self.data[idx] 

    def __len__(self): 
     return len(self.data) 

    def __repr__(self): 
     return '{}({})'.format(self.__class__.__name__, self.data) 

i jeśli „iterables” mają różne długości wszystko jest w porządku i mam dokładnie taki wynik chcę mieć:

>>> np.array([Test([1,2,3]), Test([3,2])], dtype=object) 
array([Test([1, 2, 3]), Test([3, 2])], dtype=object) 

ale NumPy tworzy wielowymiarową tablicę jeśli zdarzy się one mają taką samą długość:

>>> np.array([Test([1,2,3]), Test([3,2,1])], dtype=object) 
array([[1, 2, 3], 
     [3, 2, 1]], dtype=object) 

Niestety istnieje tylko argument ndmin, więc zastanawiałem się, czy istnieje sposób na wymuszenie ndmax lub w jakiś sposób uniemożliwić NumPy interpretowanie niestandardowych klas jako innego wymiaru (bez usuwania __len__ lub __getitem__)?

Odpowiedz

3

Rozwiązaniem jest oczywiście, aby utworzyć tablicę o pożądanym kształcie, a następnie skopiować dane:

In [19]: lst = [Test([1, 2, 3]), Test([3, 2, 1])] 

In [20]: arr = np.empty(len(lst), dtype=object) 

In [21]: arr[:] = lst[:] 

In [22]: arr 
Out[22]: array([Test([1, 2, 3]), Test([3, 2, 1])], dtype=object) 

zauważyć, że w żadnym przypadku nie będzie zaskoczony jeśli numpy zachowanie w.r.t. Interpretacja obiektów iterowalnych (która jest tym, czego chcesz użyć, prawda?) jest zależna od numpy. I prawdopodobnie buggy. A może niektóre z tych błędów są w rzeczywistości funkcjami. W każdym razie, bym się obawiał złamania, gdy zmieni się wersja numpy.

Wręcz przeciwnie, kopiowanie do wstępnie utworzonej tablicy powinno być znacznie solidniejsze.

5

Zachowanie to omówiono kilka razy przed (np Override a dict with numpy support). np.array stara się uczynić tak wysoką macierz wymiarową, jak tylko może. Przypadek modelu to zagnieżdżone listy. Jeśli potrafi iterować, a podlisty mają równą długość, "wierci" w dół.

Tutaj zszedł 2 poziomy przed napotykając list o różnej długości:

In [250]: np.array([[[1,2],[3]],[1,2]],dtype=object) 
Out[250]: 
array([[[1, 2], [3]], 
     [1, 2]], dtype=object) 
In [251]: _.shape 
Out[251]: (2, 2) 

Bez kształtu lub ndmax parametru nie ma możliwości dowiedzenia się, czy chcę go mieć (2,) lub (2,2). Oba będą działać z dtype.

To skompilowany kod, więc nie jest to łatwe, aby zobaczyć dokładnie to, co sprawdza używa. Próbuje iterować na listach i krotkach, ale nie na zestawach lub słownikach.

Najpewniejszą sposób, aby szereg obiektów o określonym wymiarze ma się rozpocząć z pustej, i napełnić

In [266]: A=np.empty((2,3),object) 
In [267]: A.fill([[1,'one']]) 
In [276]: A[:]={1,2} 
In [277]: A[:]=[1,2] # broadcast error 

Innym sposobem jest rozpoczęcie co najmniej jednego innego pierwiastka (np None) , a następnie zastąp to.

Jest bardziej prymitywny twórca, ndarray że nabiera kształtu:

In [280]: np.ndarray((2,3),dtype=object) 
Out[280]: 
array([[None, None, None], 
     [None, None, None]], dtype=object) 

Ale to jest w zasadzie taki sam jak np.empty (chyba dam to bufor).

To są krówki, ale nie są drogie (czasowo).

================ (edit)

https://github.com/numpy/numpy/issues/5933, Enh: Object array creation function. jest prośba akcesorium. Również https://github.com/numpy/numpy/issues/5303the error message for accidentally irregular arrays is confusing.

Nastrój dewelopera wydaje się faworyzować oddzielną funkcję tworzenia macierzy dtype=object, z większą kontrolą nad początkowymi wymiarami i głębokością iteracji. Mogą nawet wzmocnić sprawdzanie błędów, aby nie tworzyć "nieregularnych" tablic.

Taka funkcja może wykryć kształt zwykłej zagnieżdżonej iteracji do określonej głębokości i zbudować macierz typu obiektu do wypełnienia.

def objarray(alist, depth=1): 
    shape=[]; l=alist 
    for _ in range(depth): 
     shape.append(len(l)) 
     l = l[0] 
    arr = np.empty(shape, dtype=object) 
    arr[:]=alist 
    return arr 

Z różnych głębokościach:

In [528]: alist=[[Test([1,2,3])], [Test([3,2,1])]] 
In [529]: objarray(alist,1) 
Out[529]: array([[Test([1, 2, 3])], [Test([3, 2, 1])]], dtype=object) 
In [530]: objarray(alist,2) 
Out[530]: 
array([[Test([1, 2, 3])], 
     [Test([3, 2, 1])]], dtype=object) 
In [531]: objarray(alist,3) 
Out[531]: 
array([[[1, 2, 3]], 

     [[3, 2, 1]]], dtype=object) 
In [532]: objarray(alist,4) 
... 
TypeError: object of type 'int' has no len() 
+0

Próbowałem szuka podobnych pytań, ale nie znalazłem żadnego. Może po prostu szukałem niewłaściwych zdań. Jeśli masz jakieś odniesienia do wcześniejszych pytań, które byłyby świetne. Dziękuję za odpowiedź, ale w rzeczywistości nie szukam obejścia. Bardziej interesuje mnie bardziej ogólne podejście do definiowania maksymalnej głębokości (wymiarów) tablicy bez znajomości dokładnej długości wcześniej lub wyłączenia tego numpy interpretującego instancję klasy niestandardowej jako sekwencję. – MSeifert

+0

Zmieniając klasę na podklasę 'dict', mogę zatrzymać ją od iterowania instancji. Oznacza to, że 'np.array' testuje więcej niż' __getitem__'. Ale nie udało mi się znaleźć kodu, który przeprowadza takie sprawdzanie. – hpaulj

+0

http://stackoverflow.com/questions/36663919/override-a-dict-with-numpy-support - zmaga się z tym samym problemem; kontrolowanie, czy 'np.array' iteruje na twojej klasie niestandardowej, czy nie. Taki sam rodzaj pracy wokół. – hpaulj

Powiązane problemy