python, csv Dateien, Arrays und Excel

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.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

`new_list` ist auch kein schöner Name... wieso ist die denn "neu"? Beschreibe im Namen doch eher, *was* für Objekte dahinter stecken und nicht, von welchem Typ diese sind.

Du kannst Slicing auch mit einem einfachen Indexzugriff kombinieren:

Code: Alles auswählen

new_list.append([row[2]] + row[6:11])
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Zum Code-Verständnis:

Code: Alles auswählen

# Erstelle eine Liste mit 5 Tupeln. Liste = [] Tuple = ()
>>> a = [(0,0,1,5,2,3.2312), (0,0,1,5,2,3.2310), (0,0,1,5,2,3.2311), (0,0,1,5,4,4.5311), (0,0,1,5,3,4.5317)]
>>> groups = {} # Erstelle ein dict mit dem Namen groups
>>> for part in a: # Für jedes Element (part) in in a tue (Element sind hier die einzelnen Tuple in der Liste)
        testname = part[0:5] # testname sind die Elemente 0-4 von Part (beim ersten Durchlauf also 0,0,1,5,2)
        if testname not in groups: # wenn noch kein Schlüssel namens Testname in groups ist
                groups[testname] = [] # lege den Schlüssel an und hinterlege als Hinhalt eine leere Liste
        groups[testname].append(part[5]) # füge an die Liste mit Schlüssel 'testname' in groups das 4. Element aus part an
                                                       # dieser wert wird später in der schleifa ls "values" wieder abgerufen

       
>>> groups
{(0, 0, 1, 5, 4): [4.5311], (0, 0, 1, 5, 3): [4.5317], (0, 0, 1, 5, 2): [3.2312, 3.231, 3.2311]}
>>> for testname, values in groups.items(): # für jede schlüssel (testname) in dem dict groups finde den wert (values)
        avg = sum(values) / len(values) # avg ist die summe aller elemente in values durch deren anzahl
        print "Test '%s' hat einen Durchschnitt von: %f" % (testname, avg)

       
Test '(0, 0, 1, 5, 4)' hat einen Durchschnitt von: 4.531100
Test '(0, 0, 1, 5, 3)' hat einen Durchschnitt von: 4.531700
Test '(0, 0, 1, 5, 2)' hat einen Durchschnitt von: 3.231100
Und du solltest dir wirklich das Tutorial ansehen.
snoozy
User
Beiträge: 10
Registriert: Freitag 20. April 2012, 08:31

Bin jetzt einen großen Schritt weiter und bekomme die richtigen Resultate, wenn auch nicht ganz perfekt.
Hier aber mal der Code im ganzen:

Code: Alles auswählen

import csv
import string
import xlwt3
import os
import sys
import time
import re   #needed for whitelist / pattern search
from operator import itemgetter
from datetime import date

if len(sys.argv) < 2:
    print('error: specify input file path\n\nusage: phyton.exe create_xls2.py <input csv file>')
    sys.exit(2)

print('Parsing...')

infile = sys.argv[1] #'infiletest.csv'
outfile = infile.rsplit('\\',1)[0] + '\\Test'  + time.strftime("-%Y-%m-%d_%H-%M-%S") + '.xls'




#open csv file
filecsv = open(infile, "r")
reader = csv.reader(filecsv, delimiter=",")

csv_writer = csv.writer(open(infile.rsplit('\\',1)[0] + '\\Test'  + time.strftime("-%Y-%m-%d_%H-%M-%S") + '.csv', "w"), lineterminator='\n') 
title=("band","datarate","PCL","test","measurement","min","avg","max")
csv_writer.writerow(title)



new_list=[]

for row in reader:
       new_list.append([row[2],row[6],row[7],row[8],row[9],row[10]])

print("sorting")
sorted(new_list, key=itemgetter(0,1,2,3,4))
    
print("Build groups")
groups = {}
for part in new_list:
    testname = part[0:5]
    testname2 = tuple(testname)
    if testname2 not in groups:
        groups[testname2] = []
    groups[testname2].append(part[5])

print("calc avg,min,max")
for testname2, values in groups.items(): 
    for l in range(len(values)):
        values[l] = float(values[l])
    avg = sum(values) / len(values)
    maximum = max(values)
    minimum = min(values)
    
    temp = (testname2,minimum,avg,maximum)
    csv_writer.writerow(temp)

Die Ausgabe sieht wie folgt aus:
  • band,datarate,PCL,test,measurement,min,avg,max
    ('WLANG', '54', '13000', 'OFDM.CFL', 'FrequencyLeakage'),-47.316831,-41.10720679166666,-37.296583
To do:
1. Die Klammern des Tuples testname2 nicht ins csv schreiben
2. Sortierung, im Prinzip so, wie ich oben schon probiert habe mit sorted...
3. Es gibt Messungen, die sich nicht durch den Namen oder so unterscheiden, sondern nur dadurch, ob das Ergebnis negativ oder positiv ist.
4. Uninteressante Messungen sollen entfernt werden
5. Die Tabellenköpfe in den original Files können nicht durch die Guppierungsfunktion und müssen daher vorher gelöscht werden.

Nun gut, vielen Dank nochmal für die Hilfe und erstmal schönes Wochenende!
BlackJack

@snoozy: Sowas hier ist in Python ein totales Anti-Pattern:

Code: Alles auswählen

    for l in range(len(values)):
        values[l] = float(values[l])
Man kann über die Elemente von `values` *direkt* iterieren, ohne einen Umweg über einen Index. Wenn man zusätzlich zum Wert noch den Index braucht, gibt es die `enumerate()`-Funktion. Man könnte aber auch einfach eine neue Liste mit den umgewandelten Werten erstellen. Mit einer „list comprehension” (LC) oder `map()`. Oder, und das dürfte hier die beste Alternative sein: Man erstellt erst gar keine Liste mit Zeichenketten sondern steckt dort gleich von Anfang an Zahlen rein.

Wenn man anfängt Namen durch zu nummerieren, macht man meistens etwas falsch. Warum gibt es `testname` und `testname2`? Man muss nicht jedes kleine Zwischenergebnis an einen Namen binden. Warum die zweite Schleife mit `testname2`?

Pfadoperationen sollte man nicht mit Zeichenkettenoperationen selber basteln, sondern das nehmen was im `os.path`-Modul angeboten wird. Dann funktioniert das nicht nur unter Betriebssystemen die \ als Pfadtrenner verwenden und `os.path.dirname(in_filename)`` ist auch verständlicher als ``in_filename.rsplit('\\',1)[0]``. An der Stelle habe ich das mal `in_filename` genannt, weil `infile` irreführend ist. Ein Dateiname ist keine Datei.

`outfile` wird nicht verwendet und die Erstellung wird fast 1:1 später noch einmal gemacht.

Dateien sollte man mit der ``with``-Anweisung öffnen, dann kann man auch nicht vergessen sie wieder zu schliessen.

Der `sorted()`-Aufruf ist nutzlos. Der erstellt eine neue, sortierte Liste die Du dann aber einfach verfallen lässt, weil Du damit nichts machst. Du musst den Rückgabewert schon an einen Namen binden oder anderweitig weiter verwenden. Wobei das Sortieren an der Stelle sowieso nicht viel Sinn macht, denn Wörterbücher sind ungeordnet. Da kommt dann sowieso alles wieder durcheinander.

Binärmodus bei `csv` geht in Python 3 anscheinend wirklich nicht. Das verwende ich noch nicht.

Zu den ToDos: 1. Wenn Du von Klammern des Tupels nicht schreiben sprichst, befindest Du Dich IMHO auf der falschen Abstraktionsebene. Du willst keine Klammern weglassen, sondern für den `writerow()`-Aufruf den Wert richtig aufbauen — keine verschachtelte Sequenz sondern eine flache.

2. Die Sortierung kommt nach der Verarbeitung, weil Wörterbücher wie gesagt ungeordnet sind.

3. Da kann ich so nichts mit Anfangen.

4. Wenn sich „uninteressante Messung” als Prädikat ausdrücken lässt, dann kann man das beim einlesen einfach ausfiltern. `itertools.ifilter()` oder der ``if``-Teil einer LC oder eines Generatorausdrucks.

5. Einfach die erste Zeile aus dem `reader` mit `next()` überspringen.

Du solltest vielleicht auch jetzt schon mal anfangen Funktionen zu erstellen. Das ist auch jetzt schon komplex genug das man es auf mehrere aufteilen kann.

Ich habe das Einlesen und Durchschnitt/Minimal-/Maximalwert ermitteln mal ohne Listen (und für Python 2.x) geschrieben (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import csv
from collections import defaultdict
from itertools import imap
from operator import itemgetter


class Aggregator(object):
    def __init__(self):
        self.count = 0
        self.sum = 0
        self.min = self.max = None
    
    def __call__(self, value):
        self.count += 1
        self.sum += value
        if self.max is None:
            assert self.min is None
            self.max = self.min = value
        else:
            self.max = max(self.max, value)
            self.min = min(self.min, value)
    
    @property
    def average(self):
        return self.sum / self.count


def main():
    with open('test.csv', 'rb') as in_file:
        reader = csv.reader(in_file, delimiter=',')
        _headers = reader.next()
        results = defaultdict(Aggregator)
        rows = imap(itemgetter(2, 6, 7, 8, 9, 10), reader)
        for band, datarate, pcl, test, measurement, result in rows:
            results[(band, datarate, pcl, test, measurement)](float(result))

    for key, result in sorted(results.iteritems()):
        print '%r: #=%d avg=%.3f min=%.3f max=%.3f' % (
            key, result.count, result.average, result.min, result.max
        )


if __name__ == '__main__':
    main()
snoozy
User
Beiträge: 10
Registriert: Freitag 20. April 2012, 08:31

Hey, vielen Dank für die tollen Antworten!

Ein paar der Vorschläge habe ich schon umgesetzt, leider fehlt mir etwas Zeit und skill, um alles umzusetzen.

Die Daten zu filtern und die Mittelwerte in Zusammenhang mit den Messungen auszugeben klappt ja schon sehr gut.
Leider ist damit noch nicht das Ziel erreicht, und mein eigentlicher Plan geht nicht auf :?

Was nun noch gemacht werden muss:
Ich kann jetzt jede Menge csv Dateien erstellen, die die gewünschten Werte enthalten. Die sehen dann so aus:

Code: Alles auswählen

band,datarate,PCL,test,measurement,min,avg,max
('WLANG', '54', '13000', 'OFDM.CFL', 'FrequencyLeakage'),-47.316831,-41.10720679166667,-37.296583
('WLANA', '6', '17000', 'OFDM.Spectral.Mask', 'ReferenceLevelResult'),-53.010223,-52.609291722222224,-51.98594
('WLANB', '11', '18000', 'DSSS.POWERRAMP', 'NumDownRampVectorSamples'),1000.0,1000.0,1000.0
...
Jetzt müssen im Prinzip aus all diesen Dateien die gleichen Messungen (das was in den Klammern steht) herausgesucht werden und zusammen mit dem Dateinamen in eine csv oder excel Datei geschrieben werden, also so:

Code: Alles auswählen

Messung1,('WLANG', '54', '13000', 'OFDM.CFL', 'FrequencyLeakage'),-47.316831,-41.10720679166667,-37.296583
Messung2,('WLANG', '54', '13000', 'OFDM.CFL', 'FrequencyLeakage'),-45.316831,-45.10720679166667,-35296583
Messung3,('WLANG', '54', '13000', 'OFDM.CFL', 'FrequencyLeakage'),-46.316831,-46.10720679166667,-36.296583
...
Ich dachte, wenn ich erstmal alle Messungen in Excel hab, dann geht das schon irgendwie... naja falsch gedacht...

Jetzt hab ich überlegt, ob das nicht auch mit der Group Funktion geht, hab aber keine Ahnung wie genau.
Ich kann die Dateien, die die Messungen mit Mittelwerten enthalten alle in einen Ordner packen. Anschließend müsste ich die ja auch in python öffnen können.
Dann könnte ich doch die einzelnen Dateien gruppieren, oder irgendwie anders vergleichen.
Es sind nicht in allen Dateien die gleichen Messungen.

Hat jemand eine Idee, wie das gehen kann? :K
snoozy
User
Beiträge: 10
Registriert: Freitag 20. April 2012, 08:31

So, ich habe jetzt eine Lösung gefunden. Und zwar füge ich alle Dateien zusammen, öffne sie in Excel und Arbeite ab da mit den Filtern.

Vielen Dank nochmal für die Hilfe! :D
:mrgreen: :mrgreen: :mrgreen:
Antworten