2014-10-13 16 views
7

Ok, więc po przejściu przez tutoriali na strukturyzowanych tablic numpy za Jestem w stanie stworzyć kilka prostych przykładów:Brak operatorów binarnych dla strukturowanych tablic w Numpy?

from numpy import array, ones 
names=['scalar', '1d-array', '2d-array'] 
formats=['float64', '(3,)float64', '(2,2)float64'] 
my_dtype = dict(names=names, formats=formats) 
struct_array1 = ones(1, dtype=my_dtype) 
struct_array2 = array([(42., [0., 1., 2.], [[5., 6.],[4., 3.]])], dtype=my_dtype) 

(My przeznaczone przypadek użycia miałby więcej niż trzy pozycje i będzie używać bardzo długich 1D tablic.) Więc wszystko idzie dobrze, dopóki nie spróbujemy wykonać podstawowej matematyki. I pojawiają się błędy dla wszystkich z następujących czynności:

struct_array1 + struct_array2 
struct_array1 * struct_array2 
1.0 + struct_array1 
2.0 * struct_array2 

Najwyraźniej prostych operatorów (+, -, *, /) nie są obsługiwane nawet dla najprostszych strukturyzowanych tablic. Czy może czegoś brakuje? Czy powinienem spojrzeć na jakąś inną paczkę (i nie powiedzieć Pand, bo to totalna przesada)? To wydaje się być oczywistą możliwością, więc jestem nieco zaskoczony. Ale trudno jest o tym mówić w Internecie. Czy to nie ogranicza znacząco przydatności uporządkowanych tablic? Dlaczego ktokolwiek użyłby tablicy struktur, a nie tablic upakowanych w dyktando? Czy istnieje techniczny powód, dla którego może to być trudne? Lub, jeśli właściwym rozwiązaniem jest wykonanie żmudnej pracy z przeciążeniem, to jak to zrobić, zachowując szybkie operacje?

Odpowiedz

2

Innym sposobem działania na całej macierzy jest użycie typu "union" opisanego w dokumentacji. W przykładzie, można poszerzyć swoją dtype dodając pole „Unia”, a określenie nakładających „przesunięcia”:

from numpy import array, ones, zeros 

names=['scalar', '1d-array', '2d-array', 'union'] 
formats=['float64', '(3,)float64', '(2,2)float64', '(8,)float64'] 
offsets=[0, 8, 32, 0] 
my_dtype = dict(names=names, formats=formats, offsets=offsets) 
struct_array3=zeros((4,), dtype=my_dtype) 

['union'] teraz daje dostęp do wszystkich danych jako (n,8) tablicy

struct_array3['union'] # == struct_array3.view('(8,)f8') 
struct_array3['union'].shape # (4,8) 

można pracować na „Unia” lub innych dziedzin:

struct_array3['union'] += 2 
struct_array3['scalar']= 1 

pole „Unia” może innym kompatybilnym kształcie, takich jak '(2,4)float64' . "Rząd" takiej tablicy może wyglądać następująco:

array([ (3.0, [0.0, 0.0, 0.0], [[2.0, 2.0], [0.0, 0.0]], 
     [[3.0, 0.0, 0.0, 0.0], [2.0, 2.0, 0.0, 0.0]])], 
     dtype={'names':['scalar','1d-array','2d-array','union'], 
      'formats':['<f8',('<f8', (3,)),('<f8', (2, 2)),('<f8', (2, 4))], 
      'offsets':[0,8,32,0], 
      'itemsize':64}) 
4

Na stronach dokumentu struktury tabeli numpy, większość przykładów dotyczy mieszanych typów danych - floats, ints i string. Na SO większość pytań z tablicy strukturalnej ma związek z ładowaniem mieszanych danych z plików CSV. Z drugiej strony, w twoim przykładzie wydaje się, że głównym celem struktury jest nadanie nazw "kolumnom".

Możesz wykonywać matematykę na nazwanych kolumnach, np.

struct_array1['scalar']+struct_array2['scalar'] 
struct_array1['2d-array']+struct_array2['2d-array'] 

Można również „iterate” nad polami:

for n in my_dtype['names']: 
    print a1[n]+a2[n] 

I tak, do tego celu, co czyni te wartości tablic w słowniku, lub atrybutów obiektu, działa tak samo dobrze.

Jednak myśląc o przypadku CSV, czasami chcemy mówić o konkretnych "wierszach" pliku CSV lub tablicy strukturalnej, np. struct_array[0]. Taki "rząd" jest krotką wartości.

W każdym przypadku podstawowe struktury danych w numpy są wielowymiarowymi tablicami wartości liczbowych, a większość kodu obraca się wokół typów danych liczbowych - float, int itd. Strukturyzowane tablice są uogólnieniem tego, używając elementów, które są, zasadniczo, po prostu ustalonymi zestawami bajtów. Sposób interpretacji tych bajtów jest określony przez dtype.

Pomyśl o tym, jak ewoluował MATLAB - najpierw pojawiły się matryce, następnie komórki (jak listy w Pythonie), następnie struktury, a na końcu klasy i obiekty. Python ma już listy, słowniki i obiekty. numpy dodaje tablice. Nie trzeba wymyślać ogólnych struktur Pythona.

ja skłaniam się ku określeniu klasy tak:

class Foo(object): 
    def __init__(self): 
     self.scalar = 1 
     self._1d_array = np.arange(10) 
     self._2d_array = np.array([[1,2],[3,4]]) 

i wdrażanie jedynie operacje binarne, które naprawdę potrzebne dla danego zastosowania.

+0

Doceniam twoją odpowiedź. To bardzo pomaga wyjaśnić sytuację. Wydaje się, że u podstaw tej sytuacji zdezorientowani programiści mieli jeden przypadek użycia dla uporządkowanych tablic, a mój przypadek użycia nie jest zgodny. C'est la vie. – user2789194

0

Dobra, po dalszych badaniach natknąłem się na odpowiedź. (Brak winy hpaulj - pytanie nie było takie dobrze postawione.) Ale chciałem napisać na wypadek, gdyby ktoś inny miał podobną frustrację.

Odpowiedź pochodzi z dokumentacji numpy na stronie ndarray.view.W szczególności podają przykład, w którym "[tworzą] widok na tablicy strukturalnej, aby można go było wykorzystać w obliczeniach".

Tak więc byłem sfrustrowany, że nie mogłem operować na moich przykładowych uporządkowanych tablicach. W końcu "widzę" moją uporządkowaną tablicę jako zbiór liczb zmiennoprzecinkowych! Cóż, w końcu wszystko, czego potrzebowałem, to poinformować numpy o tej abstrakcji za pomocą "widoku". Błędy w pytaniu można uniknąć stosując:

(struct_array1.view(dtype='float64') + struct_array2.view(dtype='float64')).view(dtype=my_dtype) 
(struct_array1.view(dtype='float64') + struct_array2.view(dtype='float64')).view(dtype=my_dtype) 
(1.0 + struct_array2.view(dtype='float64')).view(dtype=my_dtype) 
(2.0 * struct_array2.view(dtype='float64')).view(dtype=my_dtype) 

To nie jest tak elegancki jak może chcieć, ale przynajmniej numpy ma zdolność.

+0

Może być możliwe zbudowanie tego alternatywnego widoku na oryginalny typ dtype. Spójrz na przykłady, które traktują dane zarówno jako ints, jak i zbiory bajtów. Wypróbuję pomysł, gdy wrócę do komputera z numpy. – hpaulj

+0

Jeśli chcesz zachować kształt swojej tablicy, użyj czegoś takiego jak 'struct_array1.view ('(8,) f8')', gdzie '(8,)' to liczba zmiennych w twoim typie, 'struct_array1. dtype.itemsize/8'. – hpaulj

+0

Dodałem odpowiedź, używając nakładających się pól jako (kompatybilnej) alternatywy dla 'widoku'. – hpaulj