Ich weiss der Quelltext ist nicht von Dir, aber hier sind noch ein paar Anmerkungen zum Programm:
Es gibt Zeilen die länger als 80 Zeichen sind und oft ist kein Leerzeichen nach Kommata.
Die Funktionen aus dem `string`-Modul, die verwendet werden sind alle veraltet. Stattdessen sollte man die entsprechenden Methoden auf den Zeichenketten verwenden.
``map(None, X, Y)`` ist ein sehr ungewöhnlicher Ausdruck, wenn man ``zip(X, Y)`` meint. Die beiden Ausdrücke verhalten sich unterschiedlich wenn `X` und `Y` verschieden lang sind, aber das wird in `lineareRegression()` ja gleich am Anfang ausgeschlossen. Das `map()` sollte man durch `zip()` oder `itertools.izip()` ersetzen. Die Funktion könnte man auch in zwei aufteilen. Die Namen `Var_a` und `Var_b` werden berechnet aber nicht verwendet.
`findePeakID()` ist zu "quelltextlastig", es ist zu viel "copy'n'paste" Quelltext, den man in eine Datenstruktur stecken kann. Hier mal exemplarisch und ungetestet für 'CH4':
Code: Alles auswählen
PEAK_IDS = {'CH4': ((16.05, 'EG1','-9999'),
(19.05, 'EG2','-9999'),
(34.05, 'EG3','-9999'),
(37.05, 'EG4','-9999'),
(52.05, 'EG5','-9999'),
(55.05, 'EG6','-9999'),
(70.05, 'EG7','-9999'),
(73.05, 'EG8','-9999'),
(88.05, 'EG9','-9999'),
(91.05, 'EG10','-9999'),
(106.05, 'EG11','-9999'),
(109.05, 'EG12','-9999'),
(124.05, 'EG13','-9999'),
(127.05, 'EG14','-9999'),
(142.05, 'EG15','-9999'),
(145.05, 'EG16','-9999'),
(160.05, 'EG17','-9999'),
(163.05, 'EG18','-9999'),
(4.05, '1.1', 'EG1'),
(7.05, '2.1', 'EG2'),
(10.05, '3.1', 'EG1'),
(13.05, '4.1', 'EG2'),
(22.05, '1.2', 'EG3'),
(25.05, '2.2', 'EG4'),
(28.05, '3.2', 'EG3'),
(31.05, '4.2', 'EG4'),
(40.05, '1.3', 'EG5'),
(43.05, '2.3', 'EG6'),
(46.05, '3.3', 'EG5'),
(49.05, '4.3', 'EG6'),
(58.05, '1.4', 'EG7'),
(61.05, '2.4', 'EG8'),
(64.05, '3.4', 'EG7'),
(67.05, '4.4', 'EG8'),
(76.05, '1.5', 'EG9'),
(79.05, '2.5', 'EG10'),
(82.05, '3.5', 'EG9'),
(85.05, '4.5', 'EG10'),
(94.05, '5.1', 'EG11'),
(97.05, '6.1', 'EG12'),
(100.05, '7.1', 'EG11'),
(103.05, '8.1', 'EG12'),
(112.05, '5.2', 'EG13'),
(115.05, '6.2', 'EG14'),
(118.05, '7.2', 'EG13'),
(121.05, '8.2', 'EG14'),
(130.05, '5.3', 'EG15'),
(133.05, '6.3', 'EG16'),
(136.05, '7.3', 'EG15'),
(139.05, '8.3', 'EG16'),
(148.05, '5.4', 'EG17'),
(151.05, '6.4', 'EG18'),
(154.05, '7.4', 'EG17'),
(157.05, '8.4', 'EG18'),
(166.05, '5.5', 'EG17'),
(169.05, '6.5', 'EG18'),
(172.05, '7.5', 'EG17'),
(175.05, '8.5', 'EG18'))}
def find_peak_id(gas, r):
for lower_limit, time, id_ in PEAK_IDS[gas]:
if r > lower_limit and r < lower_limit + 0.4:
return (time, id_)
return ('-1111', '-1111')
Für weitere Gase braucht man jetzt nur noch die "Tabelle" in `PEAK_IDS` erweitern. Man könnte die Daten auch in Textdateien auslagern oder aus einer Datenbank holen. Das ist viel flexibler als alles hart in ``if``-Abfragen zu kodieren. Ausserdem könnte man eine Funktion schreiben, die die Daten überprüft, zum Beispiel ob sich Bereiche überlappen. Beim eintippen von solchen Zahlenkolonnen passiert schonmal hin und wieder ein Fehler, den man so entdecken könnte.
Die Dateinamenverarbeitung ab Zeile 226 sieht sehr abenteuerlich aus. Das "rumgeslice" mit "magischen" Positionen, und Zählen von Substrings sieht recht kompliziert aus, wenn man sich dann anschaut wie die Dateien später geöffnet werden, sieht es aber so aus, als wenn die einfach die Form 'WEBE???.txt' haben, wobei das die '???' für eine beliebige Zeichenkette stehen, die eine "Flussnummer" darstellen!? So eine Liste mit Dateinamen inklusive Pfad dorthin sollte sich mit einer Zeile erstellen lassen:
Code: Alles auswählen
files1 = glob.glob(os.path.join(pfad, gas, 'WEBE???.txt'))
Logische Zeilenfortsetzungen mit dem '\' am Zeilenende sind übrigens nicht nötig solange noch schliessende Klammern ausstehen.
Wenn man keine Liste mit Zahlen benötigt, sollte man `xrange()` anstelle von `range()` verwenden.
Das einlesen der Datei hätte ich wohl einem Iterator über die Datei (``fin_iter = iter(fin)``) und `islice()` aus dem `itertools`-Modul gemacht. Dann hätte man zum Schluss einfach eine ``for``-Schleife über die restlichen Zeilen machen können.
Die ``elif``-Abfrage in Zeile 322 sollte besser nie zutreffen, weil das Programm sonst in der nächsten Zeile mit einem `NameError` abbricht. Was soll das einsame `as` dort denn bezwecken!?
Eine ``if``/``elif`` bei der Zweige nur aus einem ``pass`` bestehen, kann man immer so umschreiben, dass man diesen Zweige eliminiert.
Den Quelltext von Zeile 354-374 kann man etwas logischer strukturieren und verkürzen. `check` kann man loswerden indem man eine ``for``-Schleife schreibt und diese verlässt, wenn der Test entsprechend ausfällt. Um zu testen ob eine 0 in einer Liste enthalten ist, braucht's auch keine explizite Schleife, das geht mit dem Ausdruck ``0 in liste``. Beide Zweige in der Abfrage in der inneren Schleife testen ob der Wert am aktuellen Index 0 ist, diese Entscheidung kann man also vorziehen. Und die zweite Bedingung beim ``elif`` ist genau das Gegenteil von der Bedingung im ``if``, braucht also nicht explizit überprüft werden. Beide Zweige enden auch mit der gleichen Zeile, die kann man auch herausziehen.
Code: Alles auswählen
for counter in xrange(100):
if 0 not in h:
break
print counter
for i in xrange(len(h)):
if h[i] == 0:
if i <= 1:
neuer_peak = i + 2
else:
neuer_peak = i - 2
h[i] = h[neuer_peak]
Ab Python 2.5 kann man das Ganze noch etwas verkürzen:
Code: Alles auswählen
for counter in xrange(100):
if 0 not in h:
break
print counter
for i in xrange(len(h)):
if h[i] == 0:
h[i] = h[i + (2 if i <= 1 else -2)]
Vom Algorithmus her wäre es vielleicht schlauer als Abbruchbedingung zu testen ob sich `h` im letzten Schleifendurchlauf verändert hat. Wenn das nicht der Fall war, dann wird es das auch in den folgenden nicht mehr tun und man kann aufhören.
Code: Alles auswählen
old_zeroes = h.count(0)
while True:
new_zeroes = h.count(0)
if old_zeroes == new_zeroes:
break
old_zeroes = new_zeroes
for i in xrange(len(h)):
if h[i] == 0:
h[i] = h[i + (2 if i <= 1 else -2)]
`eichgasFlaechen` nach `h` zu kopieren und das Ergebnis wieder zurück zu kopieren scheint übrigens überflüssig. Man hätte `h` auch einfach an das gleiche Objekt binden können.
Ab Zeile 380 hätte man auch direkt über die Elemente von `eichgasFlaechen` iterieren können statt den Umweg über einen Index zu nehmen. Und man sollte nie ein reines ``except:`` verwenden, ohne eine konkrete Ausnahme anzugeben. Sonst werden dort einfach *alle* Ausnahmen behandelt, was die Fehlersuche enorm erschweren kann. Der Kommentar ist ja schon fast richtig: `ZeroDivisionError` heisst das Ding was da hingehört.
Bei der Regressionsberechnung ab Zeile 403 hätte man auch gleich über `ApeakRet` und `ApeakPPM` iterieren können. Das `range()` hier ist noch "schlimmer" als `range(len(irgendwas))` weil hier eine 8 hart kodiert ist von der man a) nicht weiss woher diese Zahl kommt, und die b) falsch ist sobald sich die Grösse von den Datenstrukturen mal ändern sollte. Dann darf man das ganze Programm nach 8en durchsuchen, wo man bei jeder dann entscheiden muss ob es eine 8 ist, die geändert werden muss oder nicht.
Die importierten Module `sys` und `math` werden nicht benutzt.
Man sollte so wenig wie möglich auf Modulebene machen. Den Hauptteil kann man in eine Funktion, zum Beispiel `main()`, stecken und mit folgendem Idion starten, wenn der Quelltext als Programm gestartet wurde:
Das hat den Vorteil, dass man den Quelltext auch als Modul importieren kann ohne dass das Programm ausgeführt wird und man so Funktionen einzeln austesten oder von anderen Programmen aus benutzen kann. Ausserdem fällt so auf, wenn man globale Namen statt Argumentübergabe verwendet. So etwas kann auch aus versehen ganz schnell passieren, wenn man in einer Funktion Namen verwendet, die auch auf Modulebene existieren. Wenn man nicht aufpasst hat man ganz schnell mal ein Objekt ausserhalb der Funktion verändert, was man gar nicht wollte.
Den Hauptteil sollte man auch auf mehrere Funktionen aufteilen. Der ist so etwas lang.