Seite 1 von 1

sorted mean

Verfasst: Freitag 26. Juli 2013, 14:24
von pythy
Liebe Python-Freunde,

ich möchte die Performance meines Skriptes optimieren. Ziel des Skriptes ist es, den Mittelwerte mehrerer Einträge eines bestimmten Schlüssels zu berechnen, möglichst ohne viele Schleifen. Dafür liegt eine Liste vor, in der sich die jeweiligen Monatsnamen (ohne Tag) sowie die jeweilige Tagestemperatur als Spalten abgebildet sind. Leider habe ich keine spezielle Funktion dafür gefunden.

Code: Alles auswählen

#compute avgTemp
for i in range(0, len(key), 1):
	for j in range (0, len(symbols), 1):
		if key[i] == symbols[j]:
			sum[j] = sum[j]+float(lines[i][5])
			numEntries[j] =numEntries[j] +1

			
result=[]			
for ind in range (0, len(symbols), 1):
	result.append([symbols[ind],'%.2f' %(round((float(sum[ind])/float(numEntries[ind])),2))])
Viele grüße

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 14:58
von snafu
Es gibt `sum()`:

Code: Alles auswählen

sum(deine_werte) / len(deine_werte)

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:00
von pillmuncher
Der Hinweis von snafu zeigt auch, warum es sehr schlecht ist, eine Liste sum zu nennen.

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:02
von /me
Der Code ist äußerst schwer zu lesen, da du auf eine für Python höchst umständliche Weise auf die Listenelemente zugreifst.

Dein Code sieht etwa so aus:

Code: Alles auswählen

elements = [1, 2, 3, 4, 5]
for i in range(0, len(elements), 1):
    print(elements[i] ** 2)
Du kannst direkt über die Elemente einer Liste (und anderer Datentypen iterieren):

Code: Alles auswählen

elements = [1, 2, 3, 4, 5]
for value in elements:
    print(value ** 2)

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:13
von pillmuncher
Zu dem, was /me völlig zurecht anmerkt noch dies: falls du zusätzlich zu den Werten der Liste auch die Indizes derselben benötigst, verwende enumerate().

Code: Alles auswählen

elements = [1, 2, 3, 4, 5]
for index, value in enumerate(elements):
    print(index, value ** 2)
Python 3.x, übrigens.

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:14
von pythy
Vielen Dank für eure Antworten. Mein Problem ist, dass meine Liste in der ersten Spalte Monatsnamen umfasst (welche für einen Monat mehrmals auftreten) und in der zweiten Spalte Temperaturangaben.

Daher muss ich
1. gleiche Monate zusammen gruppieren
2. aus den Temperaturangeben gleicher Monate den entsprechenden Durchschnitt berechnen

Also funktioniert sum(...)/len(...) nicht, jedenfalls nicht direkt

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:41
von BlackJack
@pythy: Dann solltest Du das erst einmal umorganisieren, zum Beispiel zu einem Wörterbuch (`dict`) welches Monatsnamen auf eine Liste mit Temperaturwerten abbildet. Damit kanst Du dann mit `sum()` und `len()` die Mittelwerte berechnen. Ein `collections.defaultdict()` bietet sich eventuell an. Oder Du erstellst am Anfang ein Wörterbuch was alle Monatsnamen jeweils auf eine leere Liste abbildet. Oder Du erstellst Dir einen Datentyp der die Summe und die Anzahl kapselt und nach dem Durchschnitt gefragt werden kann.

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:42
von pillmuncher
Na, wenn es nicht direkt funktioniert, dann eben indirekt (was auch immer das heißen mag...):

Code: Alles auswählen

from random import randint
from itertools import groupby
from operator import itemgetter

temperatures = [
    (month, day, randint(100, 300) / 10.)
        for month in range(1, 13)
            for day in range(1, 31)]  # der Einfachheit halber haben alle Monate 30 Tage

for month, grouped in groupby(temperatures, itemgetter(0)):
    grouped = list(grouped)
    avg = sum(temperature for month, day, temperature in grouped) / len(grouped)
    print(month, '{0:.2f}'.format(round(avg, 2)))
Die Standard Lib ist dein Freund. Schau sie dir an.

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:44
von BlackJack
Anmerkung zu pillmuncher's Code: Die Liste muss in diesem Fall nach Monaten sortiert/gruppiert vorliegen. Also ggf. müsste man die Daten vorher nach der ersten Spalte sortieren.

Re: sorted mean

Verfasst: Freitag 26. Juli 2013, 15:54
von Sirius3
Um die Diskussion zu erleichtern hab ich mir erlaubt pythys Code nach Python zu konvertieren:

Code: Alles auswählen

from collections import defaultdict

def mean(values):
    return sum(values)/len(values)

grouped_entries = defaultdict(list)
for k, line in zip(key, lines):
    grouped_entries[k].append(float(line[5]))
result = [(k, '%.2f'%mean(grouped_entries[k])) for k in symbols]

Re: sorted mean

Verfasst: Samstag 27. Juli 2013, 11:03
von pythy
Hallo,

vielen Dank für die lehrreiche Diskussion, ich habe mir die von euch vorgeschlagenen Funktionen angeschaut und - es hat jetzt geklappt! :D

Anbei findet ihr meinen Code:

Code: Alles auswählen

from collections import defaultdict

# define: extract columns
def column(matrix, i):
    return [row[i] for row in matrix]

# dictionary
data = zip(column(lines, 0), column(lines, 5))

# group values by key
dict = defaultdict(list)
for v, k in data: 
	dict[v].append(float(k))

# define mean fct
def mean(values):
    return sum(values)/len(values)

# compute average value
avgVals = [(k, '%.2f'%mean(dict[k])) for k in dict]

Viele Grüße und ein schönes WE!

Re: sorted mean

Verfasst: Samstag 27. Juli 2013, 11:56
von cofi
Damit dein Ansatz noch stimmt, wenn du nur Ganzzahlen hast, solltest du noch `from __future__ import division` benutzen.

Und, noch weit wichtiger, nenne dein Dictionary nicht `dict`, wenn dir nicht klar ist, warum nicht, dann schau dir mal das Highlighting an. Und wenn es dir dann immernoch nicht klar ist, probiere mal

Code: Alles auswählen

dict(zip(range(10), "abcdef"))

Re: sorted mean

Verfasst: Samstag 27. Juli 2013, 11:58
von BlackJack
@pythy: Die Kommentare sind teilweise unpassend oder überflüssig. Bei ``# define…`` steht ja schon im Code, dass dort Funktionen definiert werden und wie die heissen. ``# dictionary`` ist falsch, weil dort überhaupt kein Wörterbuch definiert wird.

`dict` ist der Name des eingebauten Datentyps, den sollte man nicht an andere Werte binden, denn dann kann man das Objekt nicht mehr aufrufen oder anderweitig verwenden.

Alternativ mit weniger selbst geschriebenen Funktionen und mehr aus der Standardbibliothek (ungetestet):

Code: Alles auswählen

from itertools import imap
from operator import itemgetter
from collections import defaultdict


def main():
    # 
    # ...
    # 
    month2temperatures = defaultdict(list)
    for month, temperature in imap(itemgetter([5, 0]), rows):
        month2temperatures[month].append(float(temperature))
    month2average_temperature = dict(
        (m, sum(ts) / len(ts)) for m, ts in month2temperatures.iteritems()
    )


if __name__ == '__main__':
    main()
Und die Idee keine Listen aufzubauen, sondern einen Aggregator-Datentyp zu schreiben (ungetestet):

Code: Alles auswählen

from itertools import imap
from operator import itemgetter
from collections import defaultdict


class Averager(object):
    def __init__(self, value=0.0, count=0):
        self.sum = value
        self.count = count

    def __iadd__(self, value):
        self.sum += value
        self.count += 1
        return self

    @property
    def average(self):
        return self.sum / self.count


def main():
    # 
    # ...
    # 
    month2temperatures_averager = defaultdict(Averager)
    for month, temperature in imap(itemgetter([5, 0]), rows):
        month2temperatures_averager[month] += float(temperature)
    
    for month, aggregate in sorted(month2temperatures_averager.iteritems()):
        print 'Average temperature in {0} was {1:.2f}.'.format(
            month, aggregate.average
        )


if __name__ == '__main__':
    main()