.txt einlesen und zeilenweise in neue Dateien ausgeben

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.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo,

ich bin seit gestern mit Python beschäftigt und hätte eine Frage. Ich benutze LTSpice um Schaltungen zu simulieren und exportiere die Signalverläufe als .txt. Das ganze sieht dann z.B. so aus:
time I(R1) I(R2)
6.049563621167206e-004 1.070672e+000 2.319437e+000-
6.049569155835339e-004 1.070674e+000 2.319518e+000-
6.049574886377249e-004 1.070676e+000 2.319600e+000-
6.049581213901325e-004 1.070679e+000 2.319689e+000-
6.049586516181264e-004 1.070680e+000 2.319762e+000-
6.049591893931547e-004 1.070682e+000 2.319836e+000-
6.049598205178729e-004 1.070684e+000 2.319922e+000-
6.049605335699310e-004 1.070686e+000 2.320020e+000-
Für ein weiteres Programm benötige ich die einzelnen Signalverläufe im folgenden Format:
Inhalt von IR1.txt:
time 1 2 3 4 5 6 7
I(R1) 1 2 3 4 5 6 7

Inhalt von IR2.txt:
time 1 2 3 4 5 6 7
I(R2) 1 2 3 4 5 6 7

#die Zahlen stehen hier natürlich für die Werte der einzelnen Zeitschritte
Im Internet bin ich auf diesen Quellcode gestoßen, der dem ganzen sehr nahe kommt:

Code: Alles auswählen

for i in range(1,16):
    with open('Datei.txt', 'r') as input:
        with open('I_%i.tmp' %i, 'w') as output:
            input.seek(0)
            for line in input:
                columns = line.strip().split()
                output.write('{} {}\n'.format(columns[0], columns[i]))
als output habe ich dann die Signalverläufe in einzelnen Dateien aber als Spalte:
time I(R1)
6.049563621167206e-004 1.070672e+000
6.049569155835339e-004 1.070674e+000
6.049574886377249e-004 1.070676e+000
6.049581213901325e-004 1.070679e+000
6.049586516181264e-004 1.070680e+000
6.049591893931547e-004 1.070682e+000
6.049598205178729e-004 1.070684e+000
6.049605335699310e-004 1.070686e+000


Ich habe schon einiges probiert um die columns[0] und columns in andere Formate zu bringen, es klappt aber nichts. Ich nehme an für euch ist das ein Kindenspiel, ich zerbreche mir daran den Kopf :D

Danke im Voraus,

Max
Zuletzt geändert von Anonymous am Dienstag 9. August 2016, 15:22, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 18276
Registriert: Sonntag 21. Oktober 2012, 17:20

@maxwell87: das Stichwort heißt "Transponieren": Du willst Spalten in Zeilen umwandeln. Dazu mußt Du ja erst die ganze Datei in eine passende Datenstruktur (Listen) einlesen und anschließend so ausgeben, dass eben eine Spalte in einer Zeile geschrieben wird (' '.join(spalte)).
BlackJack

Komplett ungetestet (Python 2):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from itertools import izip


def load(filename):
    with open(filename, 'r') as lines:
        headers = next(lines).split()
        columns = [list() for _ in headers]
        for line in lines:
            for column, item in zip(columns, line.split()):
                column.append(item)
    return izip(headers, columns)


def format_line(header, values, delimiter=' '):
    return '{0}{1}{2}\n'.format(header, delimiter, delimiter.join(values))


def save(data):
    data = iter(data)
    header, values = next(data)
    index_line = format_line(header, values)
    for header, values in data:
        with open('{0}.txt'.format(header), 'w') as out_file:
            out_file.writelines([index_line, format_line(header, values)])


def main():
    save(load('input.txt'))


if __name__ == '__main__':
    main()
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo,

erstmal vielen Dank für die schnellen Antworten.

@Sirius3

das hat tatsächlich was bewirkt. Ich bekommen jetzt mit diesem Quellcode:

Code: Alles auswählen

f_in=open("datei.txt","r")

lines = f_in.readlines();
werte=[]

for l in lines:
    werte.append(l.strip().split("\t"))

twerte=zip(*werte)

f_neu=open("f_out.txt","w")

#i=0
for i in range(len(werte[0])):
    f_neu.write(str(twerte[i]))
    f_neu.write("\n")
    #i=i+1
    
f_neu.close()
f_in.close()
aus dieser Datei:
time I(R1) I(R2)
6.049563621167206e-004 1.070672e+000 2.319437e+000
6.049569155835339e-004 1.070674e+000 2.319518e+000
6.049574886377249e-004 1.070676e+000 2.319600e+000
6.049581213901325e-004 1.070679e+000 2.319689e+000
6.049586516181264e-004 1.070680e+000 2.319762e+000
6.049591893931547e-004 1.070682e+000 2.319836e+000
folgendes Ergebnis:
('time', '6.049563621167206e-004', '6.049569155835339e-004', '6.049574886377249e-004', '6.049581213901325e-004', '6.049586516181264e-004', '6.049591893931547e-004')
('I(R1)', '1.070672e+000', '1.070674e+000', '1.070676e+000', '1.070679e+000', '1.070680e+000', '1.070682e+000')
('I(R2)', '2.319437e+000', '2.319518e+000', '2.319600e+000', '2.319689e+000', '2.319762e+000', '2.319836e+000')
Wie würde denn das ganze jetzt mit einer dynamischen Dateibeschreibung aussehen? Die Quelldatei wird so groß sein, dass ich nicht nachgucken kann wieviele Spalten und Zeilen die Datei hat. Es darf auch nur ein Leerzeichen zwischen den Zahlen stehen.

@BlackJack

danke, aber ich verstehe nach 2 Tagen Python-Crashkurs von diesem Quellcode relative wenig 8) :K

Grüße,

Max
Zuletzt geändert von Anonymous am Dienstag 9. August 2016, 16:34, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 18276
Registriert: Sonntag 21. Oktober 2012, 17:20

@maxwell87: wie schon geschrieben, mußt Du die Zellen mit .join zu einer Zeile verbinden:

Code: Alles auswählen

with open("datei.txt","r") as f_in:
    werte=[]
    for line in f_in:
        werte.append(line.strip().split("\t"))

twerte=zip(*werte)
with open("f_out.txt","w") as f_neu:
    for row in twerte:
        f_neu.write('\t'.join(row) + "\n")
BlackJack

@maxwell87: Wenn Du den Quelltext nicht verstehst, musst Du halt noch ein bisschen mehr Python lernen, bis Du ihn verstehst. So wirklich kompliziertes wird da ja nicht gemacht.

Wieviele Spalten es gibt, kannst Du nachschauen. Es ist sehr unwahrscheinlich, dass die Anzahl der Spalten oder der zur Verfügung stehende Arbeitsspeicher es nicht erlauben die erste Zeile zu laden und zu verarbeiten.

Wenn die Datei insgesamt transponiert werden soll, aber nicht komplett in den Speicher passt, dann kommt man wohl nicht darum herum die Datei mehrfach zu lesen und jeweils die in den Speicher passende(n) Spalte(n) zu lesen. Falls nicht einmal eine gesamte Spalte in den Speicher passt muss man die Eingabedatei für jede Spalte lesen und die Werte am besten auch gleich wieder in die Ergebnisdatei schreiben.

So ähnlich wäre das vorgehen wenn man nur jeweils zwei Spalten aus der Eingabedatei pro Ausgabedatei haben möchte. Falls wirklich nicht einmal eine Spalte in den Arbeitsspeicher passt, müsste man dann sogar für jede Ausgabedatei mit den Daten von zwei Spalten die Eingabedatei zweimal lesen. Dann würde ich mir aber gründlich überlegen ob Textdateien hier noch ein sinnvolles Format sind, oder ob man nicht als ersten Schritt alles in eine Datenbank oder eine HDF5-Datei schreibt und damit weiter arbeitet.

Edit: Deine ``for i in…``-Schleife ist in Python übrigens ein „anti pattern“ weil man *direkt* über die Elemente iterieren kann, statt die Indirektion über eine Indexlaufvariable zu gehen.

``zip(*data)`` ist zwar schön kompakt zu schreiben um die Daten zu transponieren, hat aber den Nachteil, dass die 2D-Listentsruktur doppelt Speicher belegt, einmal die eingelesenen Daten und dann die transponierten Daten. Das wäre an sich schon mal ein Grund das in Funktionen aufzuteilen und so zu schreiben das die untransponierten Daten so schnell wie möglich von der Speicherbereinigung freigegeben werden könnten. Bei meinem Code wird schon beim einlesen transponiert.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo,

@Blackjack, danke dir! Klappt jetzt, hatte an der falsche Stelle rumgefummelt :D

Danke euch beiden!

Grüße,

Max
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Guten Morgen,

ich habe gerade festgestellt, dass z.B. Spaltenköpfe mit Beschriftung (Ix(M1:D) nicht erkannt werden. Das liegt an dem Doppelpunkt. Wenn ich in der Datei die Doppelpunkte entferne funktioniert alles. Wo ließt der Code dieses Kriterium ein?

Gibt es die Möglichkeit erst nach einer bestimmten Zeile die Daten einzulesen (readlines()[7000:] z.B.)? Die ersten Datensätzen sind teilweise uninteressant, der Header aber wichtig.

Danke im Voraus,

Max
BlackJack

@maxwell87: Ohne Deinen Code zu kennen, kann man schlecht sagen warum das mit den Doppelpunkten nicht funktioniert. Wenn Du einfach `split()` verwendest, ohne Argumente, dann sollte das an der Stelle kein Problem sein. Was genau passiert denn da bei Dir? Ich glaube Windows mag keine Doppelpunkte in Dateinamen, kann es eventuell dort das Problem geben, wenn Du so eine Überschrift als Grundlage für den Namen der Ausgabedatei verwendest?

``readlines()[7000:]`` könnte man verwenden, aber das würde dann wieder sehr viele Daten in den Speicher lesen statt die Zeilen einzeln zu verarbeiten, gleich wenn sie rein kommen. `itertools.islice()` könnte man hier verwenden. In der Slice-Syntax fehlende Angaben kann man bei dem Funktionsaufruf als `None` angeben, also ``for line in islice(lines, 7000, None):`` iteriert über alle Zeilen ab der 7001sten, beziehungsweise 7002ten wenn man sich vorher von dem `lines`-Iterator schon die Kopfzeile geholt hat.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo Blackjack,

die Formatierung der Ausgabe kann ich nicht bestimmen. Deswegen mach ich das ganze ja. LT Spice ist beim Export eher spartanisch. Ich kann teilweise die Export-Datei nicht einmal öffnen, weil sie zu groß ist (Der Doppelpunkt lässt sich somit nicht entfernen). Ein Beispiel:
time Ix(M1:D) Ix(M1:G) Ix(M2:D)
6.049563621167206e-004 1.070672e+000 2.319437e+000 -2.663360e+001
6.049569155835339e-004 1.070674e+000 2.319518e+000 -2.663230e+001
6.049574886377249e-004 1.070676e+000 2.319600e+000 -2.663096e+001
6.049581213901325e-004 1.070679e+000 2.319689e+000 -2.662951e+001
6.049586516181264e-004 1.070680e+000 2.319762e+000 -2.662832e+001
6.049591893931547e-004 1.070682e+000 2.319836e+000 -2.662713e+001
6.049598205178729e-004 1.070684e+000 2.319922e+000 -2.662578e+001
6.049605335699310e-004 1.070686e+000 2.320020e+000 -2.662430e+001
6.049611841652504e-004 1.070688e+000 2.320112e+000 -2.662299e+001
6.049618928578032e-004 1.070690e+000 2.320212e+000 -2.662161e+001
6.049626104264496e-004 1.070692e+000 2.320315e+000 -2.662025e+001
6.049631721648925e-004 1.070694e+000 2.320397e+000 -2.661922e+001
Grüße,

Max
Sirius3
User
Beiträge: 18276
Registriert: Sonntag 21. Oktober 2012, 17:20

@maxwell87: python stört sich nicht an dem Doppelpunkt; nur im Dateiname darf keiner Vorkommen. Also ist es das einfachste, wenn Du den Dateinamen von ungültigen Sonderzeichen befreist, z.b. per replace.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo Sirius,

ok, irgendwie sowas?

Code: Alles auswählen

new_header=header.replace(":","_")
wo kommt das hin? Funktioniert nicht 8)

Grüße,

Max
BlackJack

@maxwell87: Für einen Dateinamen ungültige Zeichen entfernen gehört am besten in eine eigene Funktion, die man dann dort aufruft, wo der Datei erstellt/verwendet wird.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo Blackjack,

ich wüsste nicht wie das geht.

Grüße,

Max

edit:

Code: Alles auswählen

def replace(name):
    name=header.replace(":","_")
    return name
sowas in der Art?
BlackJack

@maxwell87: Wie *was* geht? Da ist jetzt nichts dabei was über ein Grundlagentutorial, zum Beispiel dem in der Python-Dokumentation, hinaus geht.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo,

sorry, dass man nicht alles sofort kann BlackJack. Ich habe es jetzt so gemacht:

Code: Alles auswählen

with open('{0}.tmp'.format(header.replace(":","_")),'w') as out_file:
Trotzdem danke für deine / eure Hilfe :D

Grüße,

Max
BlackJack

@maxwell87: Es geht nicht darum alles zu können, sondern das man es halt lernen muss, wenn man es nicht kann.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo Blackjack,

na dann habe ich jetzt noch eine letzte Frage :D Wie bekommt man es hin, den ersten Eintrag der beiden Linie zu löschen und durch einen anderen String zu ersetzen?

Bsp:

time 1 2 3 4
I(R4) 1 2 3 4

zu
time_neu 1 2 3 4
I(R4)_neu 1 2 3 4

Grüße,

Max
Sirius3
User
Beiträge: 18276
Registriert: Sonntag 21. Oktober 2012, 17:20

@maxwell87: Du suchst die Stelle, an der der ersten Eintrag gelesen/geschrieben wird und ersetzt ihn, durch was auch immer Du willst.
maxwell87
User
Beiträge: 11
Registriert: Dienstag 9. August 2016, 14:44

Hallo,

das stimmt 8) Ich habe Probleme bei der zweiten Zeile, da der Name variiert...

out_file.writelines([index_line.replace("time","Time"), format_line(header, values)]) #muss logischerweise überschrieben werden

Grüße,

Max

edit: Ich sehe gerade ich kann einfach einen String anstelle von header einfügen...ok jetzt klappt alles perfekt.

Danke!
Antworten