sorted mean

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
pythy
User
Beiträge: 7
Registriert: Donnerstag 25. Juli 2013, 12:41

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
Zuletzt geändert von Anonymous am Freitag 26. Juli 2013, 14:58, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Es gibt `sum()`:

Code: Alles auswählen

sum(deine_werte) / len(deine_werte)
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Der Hinweis von snafu zeigt auch, warum es sehr schlecht ist, eine Liste sum zu nennen.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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)
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

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.
In specifications, Murphy's Law supersedes Ohm's.
pythy
User
Beiträge: 7
Registriert: Donnerstag 25. Juli 2013, 12:41

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
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.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

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.
Zuletzt geändert von pillmuncher am Freitag 26. Juli 2013, 15:45, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
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.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

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]
pythy
User
Beiträge: 7
Registriert: Donnerstag 25. Juli 2013, 12:41

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!
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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"))
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()
Antworten