2011-02-10 18 views
13

Robię trochę pracy statystycznej, mam (duży) zbiór liczb losowych do obliczenia średniej, chciałbym pracować z generatorami, ponieważ po prostu muszę obliczyć znaczy, więc nie muszę przechowywać liczb.średnia obliczeniowa w python dla generatora

Problem polega na tym, że numpy.mean zepsuje się, jeśli przekażesz mu generator. Mogę napisać prostą funkcję do robienia tego, co chcę, ale zastanawiam się, czy istnieje odpowiedni, wbudowany sposób, aby to zrobić?

Byłoby miło, gdybym mógł powiedzieć "sum (values)/len (values)", ale len nie działa dla genetów i sumuje już zużyte wartości.

oto przykład:

import numpy 

def my_mean(values): 
    n = 0 
    Sum = 0.0 
    try: 
     while True: 
      Sum += next(values) 
      n += 1 
    except StopIteration: pass 
    return float(Sum)/n 

X = [k for k in range(1,7)] 
Y = (k for k in range(1,7)) 

print numpy.mean(X) 
print my_mean(Y) 

te obie dają ten sam, prawidłowe, odpowiedź, kupić my_mean nie działa na listach, a numpy.mean nie działa dla generatorów.

Bardzo podoba mi się pomysł pracy z generatorami, ale szczegóły takie jak ta wydają się zepsuć rzeczy.

+2

wiedziałbyś ile losowy generator liczb Twój przyniosłoby, prawda? –

+0

@Sven Marnach: załóżmy, że generator czyta z pliku? – Jimmy

+1

Jeśli naprawdę nie chcesz przechowywać danych (i nie implementujesz własnej wolniejszej funkcji 'sum') możesz utworzyć generator zliczający i nazwać go w ten sposób:' co = countingGen(); mean = sum (co (data))/co.getCount() ' –

Odpowiedz

5

Wystarczy jedna prosta zmiana kodu, aby umożliwić korzystanie z obu. Generatory miały być używane zamiennie z listami w pętli for.

def my_mean(values): 
    n = 0 
    Sum = 0.0 
    for v in values: 
     Sum += v 
     n += 1 
    return Sum/n 
+2

Wielkie litery, takie jak Suma, są zwykle zarezerwowane dla klas. – xApple

+0

@xApple, próbowałem zrobić to podobnie do kodu w pytaniu; zobaczysz, że zmienna nazywa się również "Sum". Osobiście postępowałbym zgodnie z konwencją w PEP 8. –

+2

i 'sum' jest wbudowanym, więc powinieneś użyć' sum_' lub 'total' –

-1

Spróbuj:

import itertools 

def mean(i): 
    (i1, i2) = itertools.tee(i, 2) 
    return sum(i1)/sum(1 for _ in i2) 

print mean([1,2,3,4,5]) 

tee będzie powielać swój iterator dla każdej iterowalny i (np generator, listy, etc.), co pozwala na korzystanie z jednego duplikatu na zsumowanie a druga do liczenia.

(Należy pamiętać, że "tee" nadal będzie używać pamięci pośredniej).

+2

Tymczasowo przechowuje całą listę. Z punktu widzenia pamięci, jest to równoznaczne z konwersją na listę jako pierwszą i użyciem 'sum (a)/len (a)', ale użycie listy będzie szybsze. –

+0

Dobra rada, prawda - właśnie patrzyłem na to, jak zaimplementowano tee(). Nienawidzę tego, kiedy to się dzieje. :-) – payne

+0

Można by pomyśleć, że 'tee' może być zaimplementowany tylko przez przechowywanie" różnicy "pomiędzy klonowanymi iteratorami, tj. Elementami, które się zużyło, ale inne jeszcze nie. –

1

Jednym ze sposobów byłoby

numpy.fromiter(Y, int).mean() 

ale to faktycznie tymczasowo przechowuje numery.

0
def my_mean(values): 
    n = 0 
    sum = 0 
    for v in values: 
     sum += v 
     n += 1 
    return sum/n 

Powyższe jest bardzo podobny do kodu, z wyjątkiem przy użyciu for iteracyjne values jesteś dobry bez względu na to czy masz listę lub iterator. Metoda Pythona sum jest jednak bardzo zoptymalizowana, więc jeśli lista nie jest naprawdę długa, możesz być bardziej szczęśliwy, tymczasowo przechowując dane.

(również zauważyć, że skoro używasz python3, nie trzeba float(sum)/n)

+1

Wykonując 'sum = 0', maskujesz wbudowane funkcje. – xApple

1

Twoje podejście jest dobry, ale należy zamiast użyć for x in y idiom zamiast wielokrotnie nazywając next aż pojawi się StopIteration . Działa to dla obu list i generatorów:

def my_mean(values): 
    n = 0 
    Sum = 0.0 

    for value in values: 
     Sum += value 
     n += 1 
    return float(Sum)/n 
+0

Wielkie litery, takie jak "Sumy", są zwykle zarezerwowane dla klas. – xApple

3

Staromodny sposób to zrobić:

def my_mean(values): 
    sum, n = 0, 0 
    for x in values: 
     sum += x 
     n += 1 
    return float(sum)/n 
4
def my_mean(values): 
    total = 0 
    for n, v in enumerate(values, 1): 
     total += v 
    return total/n 

print my_mean(X) 
print my_mean(Y) 

Jest statistics.mean() in Python 3.4 ale it calls list() on the input:

def mean(data): 
    if iter(data) is data: 
     data = list(data) 
    n = len(data) 
    if n < 1: 
     raise StatisticsError('mean requires at least one data point') 
    return _sum(data)/n 

gdzie _sum() zwraca dokładną sumę (math.fsum() -jak funkcja oprócz obsługi float również s Fraction, Decimal).

+0

Plus jeden dla' wyliczenia' - to najbardziej pytonowy sposób, IMO. – Tgsmith61591

0

Jeśli znasz długość generatora z góry i chcesz uniknąć zapisywania pełną listę w pamięci, można użyć:

reduce(np.add, generator)/length 
14

Generalnie jeśli robisz strumieniowe znaczy obliczenie pływające liczby punktowe, prawdopodobnie lepiej jest użyć bardziej stabilnego liczbowo algorytmu niż zwykłe zsumowanie generatora i podzielenie przez długość.

Najprostszy z nich (który znam) to zwykle credited to Knuth, a także oblicza wariancję. Link zawiera implementację Pythona, ale tylko średnia część jest tu kopiowana dla kompletności.

def mean(data): 
    n = 0 
    mean = 0.0 

    for x in data: 
     n += 1 
     mean += (x - mean)/n 

    if n < 1: 
     return float('nan'); 
    else: 
     return mean 

Wiem, że to pytanie jest bardzo stary, ale nadal jest pierwszym hitem na google, więc wydawało się właściwe, aby móc pisać. Nadal jestem smutny, że biblioteka standardowa Pythona nie zawiera tego prostego fragmentu kodu.

0

Można użyć zmniejszyć bez znajomości rozmiaru tablicy:

from itertools import izip, count 
reduce(lambda c,i: (c*(i[1]-1) + float(i[0]))/i[1], izip(values,count(1)),0)