2012-04-19 30 views
8

Chcę obliczyć indeksowaną wagę na dużą (1000,000 x 3000) tablicę typu boolean numpy. Duża tablica boolowska zmienia się rzadko, ale wagi przychodzą w czasie zapytania i potrzebuję odpowiedzi bardzo szybko, bez kopiowania całej dużej tablicy lub rozszerzania małej tablicy wagi o rozmiar na rozmiar dużej tablicy.Skutecznie sumuje małą tablicę numpy, transmitowaną przez gigantyczną tablicę numpy?

Wynik powinien być tablicą zawierającą 1 000 000 wpisów, z których każda ma sumę wpisów w tablicy wag odpowiadającą wartościom tego wiersza True tego wiersza.

Przyjrzałem się za pomocą maskowanych tablic, ale wydaje się, że wymagają one zbudowania tablicy wag o rozmiarze równym mojej dużej tablicy boolowskiej.

Poniższy kod podaje poprawne wyniki, ale nie mogę sobie pozwolić na ten egzemplarz podczas kroku mnożenia. Mnożenie nie jest nawet konieczne, ponieważ tablica wartości jest wartością logiczną, ale przynajmniej poprawnie obsługuje nadawanie .

Jestem nowy na numpy i kocham go, ale mam zamiar porzucić to dla tego konkretnego problemu. Nauczyłem się dość numpy, aby wiedzieć, aby pozostać z dala od wszystkiego, co pętle w python.

Moim następnym krokiem będzie napisanie tej procedury w C (która ma dodatkową zaletą pozwalając mi zaoszczędzić pamięć za pomocą bitów zamiast bajtów, przez drogę.)

ile z was NumPy guru może ocalić mnie przed cythonem?

from numpy import array, multiply, sum 

# Construct an example values array, alternating True and False. 
# This represents four records of three attributes each: 
# array([[False, True, False], 
#   [ True, False, True], 
#   [False, True, False], 
#   [ True, False, True]], dtype=bool) 
values = array([(x % 2) for x in range(12)], dtype=bool).reshape((4,3)) 

# Construct example weights, one for each attribute: 
# array([1, 2, 3]) 
weights = array(range(1, 4)) 

# Create expensive NEW array with the weights for the True attributes. 
# Broadcast the weights array into the values array. 
# array([[0, 2, 0], 
#   [1, 0, 3], 
#   [0, 2, 0], 
#   [1, 0, 3]]) 
weighted = multiply(values, weights) 

# Add up the weights: 
# array([2, 4, 2, 4]) 
answers = sum(weighted, axis=1) 

print answers 

# Rejected masked_array solution is too expensive (and oddly inverts 
# the results): 
masked = numpy.ma.array([[1,2,3]] * 4, mask=values) 
+2

Świetna robota z przykładem tego, czego potrzebujesz. – steveha

Odpowiedz

4

Produkt punktowy (lub produkt wewnętrzny) jest tym, czego potrzebujesz. Pozwala to na wzięcie macierzy o rozmiarze m×n i wektora o długości n i pomnożenie ich razem, dając wektor o długości m, gdzie każdy wpis jest sumą ważoną rzędu macierzy z pozycjami wektora jako wag.

Numpy implementuje to jako array1.dot(array2) (lub numpy.dot(array1, array2) w starszych wersjach). np .:

from numpy import array 

values = array([(x % 2) for x in range(12)], dtype=bool).reshape((4,3)) 

weights = array(range(1, 4)) 

answers = values.dot(weights) 
print answers 
# output: [ 2 4 2 4 ] 

(Należy to jednak odniesienia, używając timeit module.)

+0

senderle zawarł szybki benchmark z jego odpowiedzią; to dobrze wypadło. – agf

+0

To jest niesamowite, nie zrozumiałem funkcji kropki w ogóle z mojej wędrówki po dokumentach.Zrobiłem na to czas i niestety nie jest wystarczająco szybki, nawet na moim wysokim instancji EC2 CPU, ale o to właśnie prosiłem i cieszę się, że o tym wiem, dziękuję! –

1

Czy ta praca jest dla Ciebie?

a = np.array([sum(row * weights) for row in values]) 

używa sum() natychmiast suma wartości row * weights, więc nie trzeba pamięć do przechowywania wszystkich wartości pośrednich. Następnie zrozumienie listy zbiera wszystkie wartości.

Powiedziałeś, że chcesz uniknąć wszystkiego, co "pętle w Pythonie". To przynajmniej robi pętlę z odwzorowaniami C w Pythonie, zamiast jawnej pętli Pythona, ale nie może być tak szybkie jak rozwiązanie NumPy, ponieważ używa skompilowanego C lub Fortran.

+0

Zostawię to, ale @dbaupp go przybił. Czyste rozwiązanie NumPy będzie lepsze niż to. – steveha

+0

Tak, czysty numpy to wygrana, ale jest to również bardzo zwięzłe rozwiązanie, dzięki! –

0

Nie sądzę, trzeba numpy czegoś takiego. I 1000000 przez 3000 to ogromna tablica; to prawdopodobnie nie pasuje do twojej pamięci RAM.

chciałbym zrobić to w ten sposób:

powiedzmy, że wy danych jest początkowo w postaci pliku tekstowego:

False,True,False 
True,False,True 
False,True,False 
True,False,True 

Mój kod:

weight = range(1,4)  
dicto = {'True':1, 'False':0} 

with open ('my_data.txt') as fin: 

    a = sum(sum(dicto[ele]*w for ele,w in zip(line.strip().split(','),weight)) for line in fin) 

Wynik:

>>> a 
12 

EDIT:

Myślę, że źle odczytałem pytanie za pierwszym razem i podsumowałem wszystko razem. Oto rozwiązanie, które daje dokładne rozwiązanie, że PO jest po:

weight = range(1,4) 
dicto = {'True':1, 'False':0} 

with open ('my_data.txt') as fin: 

    a = [sum(dicto[ele]*w for ele,w in zip(line.strip().split(','),weight)) for line in fin] 

Wynik:

>>> a 
[2, 4, 2, 4] 
+2

Tablica 3200 bitów o wartości 1000000 na 3000. Działa na około 11,2 GiB danych. Jeśli jego wartości true/false są wartościami jednobajtowymi, to tylko około 2,8 GB danych. Istnieją 64-bitowe komputery z 32 GB lub więcej pamięci RAM, więc nawet tablica zmiennoprzecinkowa może zmieścić się w zależności od komputera. Ale nie będzie chciał zrobić żadnych kopii, jeśli będzie mógł pomóc! – steveha

+0

OK, rozumiem. Dzięki. Wiem, że nie ma mowy, żeby zmieścił się w mojej pamięci RAM! Po prostu chciałem mieć to rozwiązanie w przypadku, gdy rozmiar jest problemem. – Akavall

+0

steveha ma rację, są one wartościami jednobajtowymi (dtype = bool) i możliwe jest utrzymanie ich w pamięci RAM. I z moich wymagań wydajnościowych, naprawdę nie mogę sobie pozwolić na dotykanie dysku w ogóle, nawet do wymiany. Ale zgadzam się, że jest to użyteczne uzupełnienie dla kogoś, kto chce zrobić to samo na wolniejszej skali czasu, z mniejszym ramą, dzięki! –

3

Wydaje się prawdopodobne, że odpowiedź dbaupp „s jest prawidłowa. Ale tylko ze względu na różnorodność, oto inne rozwiązanie, które oszczędza pamięć. Będzie to działać nawet w przypadku operacji, które nie mają wbudowanego odpowiednika w wersji numpy.

>>> values = numpy.array([(x % 2) for x in range(12)], dtype=bool).reshape((4,3)) 
>>> weights = numpy.array(range(1, 4)) 
>>> weights_stretched = numpy.lib.stride_tricks.as_strided(weights, (4, 3), (0, 8)) 

numpy.lib.stride_tricks.as_strided to wspaniała mała funkcja! Pozwala to określić wartości, które umożliwiają małej macierzy naśladować znacznie większą tablicę. Uwaga: tutaj nie ma czterech rzędów; to właśnie wygląda w ten sposób:

>>> weights_stretched[0][0] = 4 
>>> weights_stretched 
array([[4, 2, 3], 
     [4, 2, 3], 
     [4, 2, 3], 
     [4, 2, 3]]) 

więc zamiast przechodzącej ogromną tablicę MaskedArray, można przekazać mniejszy. (Ale jak już zauważyłeś, maskowanie działa w sposób odwrotny, jakiego możesz się spodziewać, maski prawdy, a nie ujawniające, więc będziesz musiał przechowywać swój kod values odwrócony.) Jak widać, MaskedArray nie kopiuje żadnych dane; to po prostu odzwierciedla to, co jest w weights_stretched:

>>> masked = numpy.ma.MaskedArray(weights_stretched, numpy.logical_not(values)) 
>>> weights_stretched[0][0] = 1 
>>> masked 
masked_array(data = 
[[-- 2 --] 
[1 -- 3] 
[-- 2 --] 
[1 -- 3]], 
     mask = 
[[ True False True] 
[False True False] 
[ True False True] 
[False True False]], 
     fill_value=999999) 

Teraz możemy po prostu przekazać to podsumować:

>>> sum(masked, axis=1) 
masked_array(data = [2 4 2 4], 
     mask = [False False False False], 
     fill_value=999999) 

I porównywana numpy.dot i powyżej przeciw 1.000.000 x 30 tablicy. Jest to wynik o stosunkowo nowoczesnym MacBook Pro (numpy.dot jest dot1; kopalnia jest dot2):

>>> %timeit dot1(values, weights) 
1 loops, best of 3: 194 ms per loop 
>>> %timeit dot2(values, weights) 
1 loops, best of 3: 459 ms per loop 

Jak widać, wbudowany numpy rozwiązanie jest szybsze. Ale warto wiedzieć o stride_tricks, więc odstąpię od tego.

+0

stride_tricks jest warte poznania, aby być pewnym! Zastanawiałem się, czy coś takiego jest możliwe, i próbowałem sprawdzić, czy tablice mogą być zbudowane przez odniesienie, ale zrezygnowałem. Mogę sobie wyobrazić używanie tego w przyszłości, dzięki! –

Powiązane problemy