Kann man in Python eine Datei von Zeile n zu Zeile m einlesen...

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.
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

...ohne die ganze Datei einzulesen?
Wenn ja, wären sie so nett und würden den Code zeigen, wie das geht?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, kann man auch in keiner anderen Sprache. Du kannst bestenfalls abbrechen, wenn du genug Zeilen gelesen hast. Und zb die ersten Zeilen die ungewünscht sind verwerfen.

Wenn du sowas effizienter machen willst, nimm eine Zeitserien Datenbank. Oder sogar eine normale.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Dateiobjekte verhalten sich wie Iteratoren, d.h. beim Iterieren lassen sich die Zeilen mitzählen und man könnte z. B. nur die behalten, die einen interessieren. Die Verwendung von seek() wäre auch möglich, wenn der Dateiaufbau genau bekannt ist. Das ist bei Textdateien aber eher selten der Fall.
nezzcarth
User
Beiträge: 1647
Registriert: Samstag 16. April 2011, 12:47

__deets__ hat geschrieben: Montag 16. März 2020, 22:04 Nein, kann man auch in keiner anderen Sprache.
Das Perlmodul Tie::File bietet ein Interface, das in die Richtung geht und sich in der Benutzung so anfühlt (auch wenn es nicht so implementiert ist; intern wird da viel gecached und "rum-geseeked"). Allerdings habe ich so etwas noch für keine andere Sprache gesehen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das die da ein nettes Interface drüber gelegt haben mag ja sein. itertools.dropwhile und takewhile sind auch nett. Aber um die fundamentalen Wahrheiten der Filesystemabstraktion kommen die auch nicht rum. Dateien liest man im Block. Wenn man die an unbekannt langen Zeilen auftrennen will, muss man das machen. Bestenfalls kann man bisecten und seeken, das geht aber auch nur bei speziellen Daten.
Sirius3
User
Beiträge: 17828
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie groß sind denn die Dateien? Lohnt es sich überhaupt?

Code: Alles auswählen

from itertools import islice
with open(filename) as lines:
    relevant_lines = list(islice(lines, start, end))
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

Danke.

Die Frage hat sich jetzt erübrigt.

Ich habe jetzt die Performancebremse in unserem Projekt herausgefunden.

In einem Mockup Programm habe ich verschiedene Szenarien auf Laufzeit gemessen:
Als Quelle kommt diese Datei: https://www.cjoint.com/c/JCmrBtJBWj1. das Programm läuft auf einem Raspberry Pi 3.
Gesucht wird was gegen Ende der Datei...

Szenario 1: die Datei wird Zeile für Zeile eingelesen, und auf schiere Textübereinstimmung (das nur ind den in den Spalten [2:9] vorkommen kann) geprüft. Laufzeit: weniger als 700 ms.
Szenario 2: die Datei wird Zeile für Zeile eingelesen, der Text in den Spalten [2:9] wird nach datetime() konvertiert und mit > und < gegen zwei datetime() Grenzen geprüft: Laufzeit: ca. 8,8 s
Szenario 3: die Datei wird komplett in eine Liste eingelesen: Laufzeit 170 ms
Szenario 4: Szenario drei + Analyse der Liste auf Textübereinstimmung wie bei 1 : Laufzeit ca. 1 s
Szenario 5: Szenario drei + der Text in den Spalten [2:9] wird nach datetime() konvertiert und mit > und < gegen zwei datetime() Grenzen geprüft: Laufzeit: ca. 10 s.

Es wird mir langsam klar: die datetime() Konvertierungen* sind der Zeitdieb!
*

Code: Alles auswählen

i = 0
found_start = False
found_stop= False
size_of_lines = len(lines)    
while i < size_of_lines:
    line = lines[i]
    if line[0:1] == "d":
        log_line_time_str = line[2:9]
        log_line_time = DateTime.strptime(log_line_time_str, '%H:%M:%S')
        if log_line_time >= log_start_time and not found_start:
            start_line = current_line
            found_start = True
        elif log_line_time <= log_stop_time and not found_stop:
            stop_line = current_line
            found_stop = True    
    i += 1
Die Lösung: wir müssen den Log überdenken und die Zeit dort im datetime Format abspeichern.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1038
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Hast du die Laufzeit vom Start des Programms festgehalten oder nur in einem bestimmten Programmteil?

Beim Raspberry Pi sollte man auch beachten, dass der Zugriff auf die SD-Karte nicht gerade besonders schnell ist.
Je nachdem was alles importiert wird, kann sich der Start des eigentlichen Programms signifikant verzögern.

Prüfen kann man das auch:

Code: Alles auswählen

python -X importtime programm.py

Bei mir braucht z.B. der Webserver auf dem Raspberry Pi (fastapi + sqlalchemy + anderes) eine halbe Ewigkeit (10 - 15 Sekunden), bis der erste Request möglich ist.
Liegt einfach daran, dass ziemlich viele Zugriffe auf die SD-Karte stattfinden, weil ich so viel importiere.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

In meinem Mock-Up habe ich genau die Laufzeit von verschiedene Programmteile separat gestoppt.
Das Einlesen der große Datei dauerte 165 ms, die darauf folgende Schleife mit

Code: Alles auswählen

log_mid_time = DateTime.strptime(log_mid_time_str, '%H:%M:%S')
log_stop_time = log_mid_time + TimeDelta(minutes=3)
log_start_time = log_mid_time + TimeDelta(minutes=-3)
log_file = log_path + log_date_str + '.log'
log_start_time_str = log_start_time.strftime('%H:%M:%S')
log_stop_time_str = log_stop_time.strftime('%H:%M:%S')
sind das Problem: Hier eiert Python zwischen sechs und acht Sekunden daran.
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

Da offensichtlich die Logs beeinflusst werden können und die Antwort sicher nicht lautet: "Zeit dort im datetime Format abspeichern", steht die Antwort bereits in Beitrag 2 dieses Threads.
Aber das sind noch so viele Beustellen offen, da würde ich mir um Zeit erst einmal Gedanken machen.
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

sparrow hat geschrieben: Donnerstag 19. März 2020, 14:39 Da offensichtlich die Logs beeinflusst werden können und die Antwort sicher nicht lautet: "Zeit dort im datetime Format abspeichern", steht die Antwort bereits in Beitrag 2 dieses Threads.
Aber das sind noch so viele Beustellen offen, da würde ich mir um Zeit erst einmal Gedanken machen.
Die Logs in menschlich lesbarer Form sind auch Teil der Aufgabe. Wäre einer Datenbank infrage gekommen, hätte ich mich bei deinem Beitrag schon bedankt.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1038
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ich hab die Logdatei mal eingelesen. Ist schon erstaunlich wie langsam das ist 3.5 MiB zu verarbeiten (~800 ms auf einem schnellen PC).
Das Programm, dass in die Datei schreibt, kann ja auch noch eine zusätzliche Datei anlegen, wo die letzte Position z.B. der letzten 10 Datensätze ist.

Mit seek kann man dann einfach zur entsprechenden Position springen und dann über die Zeilen iterieren.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

Im Moment ist das Problem, dass die Logdatei ein Jitter* aufweist, denn wir versuchen im Griff zu bekommen.
*Die Timestamps sind oft nicht in Folge, und zeigen Abweichungen von bis zu +- 3Sekunden.

Es ist -wohl gemerkt ein Jitter, kein Drift.
Der serielle Sender sendet absolut regelmäßig und der Langzeitgleichlauf ist nicht das Problem.

Aber die Verarbeitung in Python erfolgt (im Programm SerialPlotter, der die Log-Datei schreibt) machmal zeitverzögert.
Ob die Verzögerung in Python stattfindet oder vom Betriebsystem kommt, dass muss ich noch eruieren.
Ich hatte auch aus Performancegründen* den Python script mit "nice python" gestartet, weil noch andere wichtigere Tasks auf dem keinen Pi laufen müssen.

Das Ausgeben des Matplotlib Plots frisst 90% der Zeit eines Raspberry-Prozessors für 1 bis2 Sekunden.
Das erfolgt im Matplotlib Modul, das kann ich nicht ändern.....

Leider scheint das Threading von Python kein Prioritäten zu kennen.
Ist auch nicht preemptive. :-(

https://github.com/Gordon999/Pi-Arduino_Ser_Plotter
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Selbstverstaendlich ist das Threading preemptive. Oder spickst du deinen Code mit "jetzt darf wer anders"-Aufrufen? Es ist allerdings durch das GIL niemals echt parallel. Und da die Threads von Python auch Systemthreads sind, kannst du sie grundsaetzlich auch priorisieren. ZB mit chrt, wenn man PREEMPT_RT benutzt. Durch die Interaktion mit dem GIL und dem Laufzeitverhalten des Intepreters ist der Vorteil allerdings relativ unklar. Dafuer gibt's dann eben multiprocessing.

Aber der Kernfehler ist konzeptioneller Art: wenn eure Zeitstempel auf dem PI genommen werden, obwohl die Daten vom Arduino kommen, dann ist das schon ungeschickt. Die muessen dort gezeitstempelt werden, und mit einem kleinen Filter bestimmt man relativen Offset und Drift, und kompensiert die Zeitstempel damit in Python. Dann ist es auch egal, wenn da mal ein paar Sekunden(bruchteile) mal nix passiert.
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

__deets__ hat geschrieben: Sonntag 22. März 2020, 19:32 ...obwohl die Daten vom Arduino kommen, dann ist das schon ungeschickt. Die muessen dort gezeitstempelt werden...
Der Arduino (die Arduinos von viele, viele existierende Stationen In Europa) kennen keine Uhrzeit. Höchstens ein Sekundentick, der allerdings von nichts unterbrochen wird.
Sirius3
User
Beiträge: 17828
Registriert: Sonntag 21. Oktober 2012, 17:20

@RIN67630: wenn Dir die grundlegenden Kenntnisse, wie man so ein System aufbaut, fehlen, dann wird das schwierig. Man synchronisiert die Ticks des Arduino mit der Uhrzeit vom Raspi und ist so von der genauen Zeit, wann die Daten verarbeitet werden, unabhängig.
Dass jetzt das Aufnehmen von Daten und das Darstellen im selben Prozess laufen, ist mir neu.
Wie so oft, fehlt da die vollständige Information, was Du eigentlich machst.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und genau mit dem Tick muesst ihr euch eben synchronisieren. Dann reicht es, wenn regelmaessig abgefragt wird, und der Jitter verschwindet.
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

Sirius3 hat geschrieben: Sonntag 22. März 2020, 21:20 @RIN67630: wenn Dir die grundlegenden Kenntnisse, wie man so ein System aufbaut, fehlen, dann wird das schwierig. Man synchronisiert die Ticks des Arduino mit der Uhrzeit vom Raspi und ist so von der genauen Zeit, wann die Daten verarbeitet werden, unabhängig.
Dass jetzt das Aufnehmen von Daten und das Darstellen im selben Prozess laufen, ist mir neu.
Wie so oft, fehlt da die vollständige Information, was Du eigentlich machst.
In Github ist alles vorhanden: Kurzbeschreibung, der Arduino Simulator zum testen, (nicht die echte Stationen, die zu viel Hardware erfordern würden) und SerielPlotter,py, LogPlotter.py, selbst mein Mock-up zum Testen...
Lediglich steht dort nicht drin, dass ich nicht auf eine grüne Wiese aufbaue, sondern eine bestehende Landschaft von 600 Stationen um ein Plot und re-Plotfunktion erweitern möchte, weil die Software auch allgemein gehalten werden sollte.

Abgesehen davon, wäre es der völlig falschen Weg, den groben Jitter des Raspberry Pi auf dem Arduino zu übertragen, dort müssen Messungen mit verbindlichen Takten (125 ms, 1 s) verarbeitet werden.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du überträgst keinen jitter. Der arduino liefert nur einen (überlaufenden) Tick mit. Damit ist der Zeitpunkt der Datenerfassung entkoppelt von der Ablieferung im PI. Und somit ent-jittert.
RIN67630
User
Beiträge: 91
Registriert: Sonntag 29. April 2018, 08:07

__deets__ hat geschrieben: Sonntag 22. März 2020, 22:00 Und genau mit dem Tick muesst ihr euch eben synchronisieren. Dann reicht es, wenn regelmaessig abgefragt wird, und der Jitter verschwindet.
Genau das tun wir ja auch: die Nachrichten mit einer feste Länge kommen exakt im Sekundentakt über die exklusiv genutzte serielle Schnittstelle Im Raspberry Pi hinein, somit ist die Synchronität bei /dev/ttyUSB0 gegeben.
Das Problem entsteht nicht an der Quelle, sondern dadurch, dass der Raspberry Pi (innerhalb des Python Programms SerialPlotter.py oder aufgrund von Verzögerungen im Betriebssystem, das weiß ich noch nicht) die Daten nur mit Jitter verarbeitet, dann gibt es halt den falschen Zeitstempel.
Eine mögliche Lösung könnte gegebenenfalls sein, die serielle Schnittstelle über ein getrenntes Prozess mit höherer Priorität abzufragen, nur den Zeitstempel hinzufügen und dann zum SerialPlotter.py zu pipen?
Wenn SerialPlotter.py dann nur noch mit Parkinson verarbeitet, würde es dann niemand mehr jucken. Ich glaube, im Audiobereich macht man es nicht anders...
Antworten