Matplotlib Extrem langsam

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

Hallo zusammen,
ich bin ziemlich neu im dem Thema, versuche aber dennoch mich hier zu verbessern.
Zu meinem Problem:
Ich habe zwei Scripts, das erste macht alle 10 Sekunden einen Ping an eine IP und schreibt das ergebnis in eine txt datei.
Ein zweites soll diese Datei dann auslesen und das Ergebnis plotten.

Hier mal der Code zum auslesen und Plotten.

Code: Alles auswählen

import matplotlib.pyplot as plt
import time
ping = []
zeit = []
with open ("C:\\Users\\Benjamin.Baehrle\\Desktop\\HL_COM_SGD\\Ping_TXT\\06.04.2019.txt", 'r') as file:
    for x in file:
        if x != '\n':
            datum_zeit = x.strip().split('[')[0].split(' ')[1]
            ip_ziel = x.strip().split('[')[1].split(',')[1].split(' ')[4][:-1]
            zeit.append(datum_zeit)
            if x.strip().split('[')[1].split(',')[1].split(' ')[1][1:] == '64':
                ping_zeit = float(x.strip().split('[')[1].split(',')[1].split(' ')[7].split('=')[1])
                ping.append(ping_zeit)
            else:
                ping .append(0.0)


print(ping)
print(zeit)


plt.xlabel('Zeit von ' + zeit[0] + ' bis ' + zeit[-1], fontsize=15)
plt.ylabel('Latenz in ms an IP' + ip_ziel)
plt.plot(ping, zeit)
plt.show()
Bis zum Print läuft alles in unter einer Sekunde durch. Der Plot dauert dann ca. 30 Minuten...
Kann ich das irgendwie in einen Brauchbaren Rahmen bringen? so 30-60 Sekunden wären noch Akzeptabel.
Es sind nach Adam Riese ja ca. 8500 Datenpunkte

Bitte um Hilfe :-)

Danke schonmal.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also mal abgesehen von dem viel, viel, viel zu komplizierten Code um deine Daten einzulesen (da wuerde sich anbieten, die Daten vernuenftig zu speichen und dann zB in einer Zeile Code mit pandas einzulesen) scheint dein Problem daran zu liegen, dass du die Zeiten als strings vorliegen hast. Dadurch muss matplotlib da viel rumroedeln um rauszufinden, was das sein koennte. Nimm einen passenden Datentypen, und es flutscht:

Code: Alles auswählen

import matplotlib.pyplot as plt
import time
import random
import datetime

COUNT = 8500
start = datetime.datetime.now()

zeit = [start + datetime.timedelta(seconds=i) for i in range(COUNT)]
ping = [random.randint(10, 100) for _ in range(COUNT)]

ip_ziel = "egal"

#plt.xlabel('Zeit von ' + zeit[0] + ' bis ' + zeit[-1], fontsize=15)
plt.ylabel('Latenz in ms an IP' + ip_ziel)
plt.plot(ping, zeit)
plt.show()
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Zusätzlich zu dem was __deets__ geschrieben hat:
Hast du einen Bildschirm der 8500 Pixel breit ist? Matplotlib zeigt dir die doch gar nicht alle an.
Wie wäre es, wenn du deine ping Daten etwas aufbereitest,
also z.B über je 12 Messwerte (2 min) einen Mean berechnen und die dann anzeigen zu lassen.
Oder den max-Wert aus den 12 ermittlen, etc.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

__deets__ hat geschrieben: Montag 8. April 2019, 12:00 Also mal abgesehen von dem viel, viel, viel zu komplizierten Code um deine Daten einzulesen (da wuerde sich anbieten, die Daten vernuenftig zu speichen und dann zB in einer Zeile Code mit pandas einzulesen) scheint dein Problem daran zu liegen, dass du die Zeiten als strings vorliegen hast. Dadurch muss matplotlib da viel rumroedeln um rauszufinden, was das sein koennte. Nimm einen passenden Datentypen, und es flutscht:

Code: Alles auswählen

import matplotlib.pyplot as plt
import time
import random
import datetime

COUNT = 8500
start = datetime.datetime.now()

zeit = [start + datetime.timedelta(seconds=i) for i in range(COUNT)]
ping = [random.randint(10, 100) for _ in range(COUNT)]

ip_ziel = "egal"

#plt.xlabel('Zeit von ' + zeit[0] + ' bis ' + zeit[-1], fontsize=15)
plt.ylabel('Latenz in ms an IP' + ip_ziel)
plt.plot(ping, zeit)
plt.show()
Danke, teste das gleich mal.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

@ThomasL: ich wuerde das nur machen, wenn *das* der Grund fuer Performance-Probleme ist. Ist es aber nicht, und man kann ja mit matlotlib reinzoomen in bestimmte Bereiche. Wenn du da also vorher rumoptimiert hast, fehlt da ploetzlich Information.
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

@ __deets__,
Danke, hab aus dem String n Datum gemacht und schon läufts in 2 Sekunden durch. Perfekt!
In der TXT Datei stehen halt noch andere werte, die schneid ich hier ab und verwerte nur das was ich benötige. Ich könnte die beiden Werte in eine eigene Datei schreiben, glaub in dem Fall aber nicht das es die Mühe wert wäre. Ich lass das Script nur laufen wenn ich Fehlermeldungen bekomme und dann mal was auswerten will. Da bin ich jetzt voll zufrieden!

Vielen Dank nochmal.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich denke trotzdem, dass dein geparse deutlich zu aufwendig ist. Um das zu beurteilen muesste man mal eine ganze Zeile sehen.
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

__deets__ hat geschrieben: Montag 8. April 2019, 16:33 Ich denke trotzdem, dass dein geparse deutlich zu aufwendig ist. Um das zu beurteilen muesste man mal eine ganze Zeile sehen.
05.04.2019 00:00:08['PING 217.69.224.73 (217.69.224.73) 56(84) bytes of data.', '64 bytes from 217.69.224.73: icmp_seq=1 ttl=60 time=26.2 ms', '', '--- 217.69.224.73 ping statistics ---', '1 packets transmitted, 1 received, 0% packet loss, time 0ms', 'rtt min/avg/max/mdev = 26.232/26.232/26.232/0.000 ms', '']

Ich lerne gerne dazu :-)
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also was da ja schon mal auffaellt ist, das dort recht viel unsinnige Zeichen drin sind, und jede Menge redundante Information. Wie schreibst du denn diese Zeile? Das sieht danach aus, als ob da ein Python Listen-Objekt einfach serialisiert wird. Statt es vernuenftig mit zb dem csv-Modul zu schreiben. Ausserdem ist es mehr oder weniger die gesamte ping-Ausgabe, da wuerde ich die Logik die sinnvoll aufzuarbeiten an die Stelle packen, die das Logfile *schreibt*, statt an allen Orten, wo es verarbeitet werden muss muehselig das unguenstige Format zu verarbeiten.

Wenn man jetzt mal das Format als gegeben ansieht, dann ist da mit regulaeren Ausdruecken zB einfach zu arbeiten:

Code: Alles auswählen

import re

line = """05.04.2019 00:00:08['PING 217.69.224.73 (217.69.224.73) 56(84) bytes of data.', '64 bytes from 217.69.224.73: icmp_seq=1 ttl=60 time=26.2 ms', '', '--- 217.69.224.73 ping statistics ---', '1 packets transmitted, 1 received, 0% packet loss, time 0ms', 'rtt min/avg/max/mdev = 26.232/26.232/26.232/0.000 ms', '']"""

timestamp, rest = line.split("[", 1)
ip = re.match(r".*PING ([\d\\.]+).*", rest).group(1)
latency = re.match(r".*time=([\d\\.]+).*", rest).group(1)
print(timestamp, ip, latency)
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Frage ist ja, wer dieses schreckliche Format schreibt. Das ist eine String-Repräsentation einer Liste, die nur zu Debuggingzwecken da ist, nicht dass man sie in ein Log-File schreibt. Auch dass man die Ausgabe eines Pings ungeparst in eine Datei schreibt, ist meiner Meinung nach schon falsch. Zeig doch mal das Schreibe-Skript.
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

Also hier erst mal meine Lösung fürs Plotten:

Code: Alles auswählen

import matplotlib.pyplot as plt
from datetime import datetime

ping = []
zeit = []
with open ("C:\\Users\\Benjamin.Baehrle\\Desktop\\HL_COM_SGD\\Ping_TXT\\08.04.2019.txt", 'r') as file:
    for x in file:
        if x != '\n':
            datum_zeit = datetime.strptime(x.strip().split('[')[0], '%d.%m.%Y %H:%M:%S')

            ip_ziel = x.strip().split('[')[1].split(',')[1].split(' ')[4][:-1]
            zeit.append(datum_zeit)
            if x.strip().split('[')[1].split(',')[1].split(' ')[1][1:] == '64':
                ping_zeit = float(x.strip().split('[')[1].split(',')[1].split(' ')[7].split('=')[1])
                ping.append(ping_zeit)
            else:
                ping .append(0.0)


print(ping)
print(zeit)


plt.xlabel('Zeit von ' + str(zeit[0]) + ' bis ' + str(zeit[-1]), fontsize=15)
plt.ylabel('Latenz in ms an IP' + ip_ziel)
plt.plot(zeit, ping)
plt.show()
Und hier die datei die schreibt.

Code: Alles auswählen

from funktions import ping, write_log_file
import time
ip_liste = ['217.69.224.73']
dateiname = 'Ping_Log/ping_log.txt'
timer = 10

while 1:
    date_filename = time.strftime('%d.%m.%Y')
    date_time = time.strftime('%d.%m.%Y %H:%M:%S')
    data_list = []
    for x in ip_liste:
        #data_list.append(date_time)
        ping_erg = ping(x)
        data_list.append(date_time + ping_erg[1])
    write_log_file('Ping_Log/' + date_filename + '.txt',data_list)
    time.sleep(timer)

Die Funktion Ping aus einem früheren Projekt:

Code: Alles auswählen

def ping(ip_addr, timeout=100):

    if platform.system().lower() == 'windows':
        print('Windows System erkannt')
        num_flag = '-n'
        ttl = b'TTL='
        splitter = '\\r\\n'
    else:
        print('Linux System erkannt')
        num_flag = '-c'
        ttl = b'ttl='
        splitter = '\\n'
    completed_ping = subprocess.run(['ping', num_flag, '1', '-w', str(timeout), ip_addr],
                                    stdout=subprocess.PIPE,  # Capture standard out
                                    stderr=subprocess.STDOUT)  # Capture standard error
    bool_out = (completed_ping.returncode == 0) and (ttl in completed_ping.stdout)
    ping_out_raw = (completed_ping.stdout)
    ping_out = str(ping_out_raw).split("'")[1].split(splitter)
    return [bool_out, str(ping_out)]
    
Und die Write Log auch aus dem früheren Projekt:

Code: Alles auswählen

def write_log_file(file, data):
    with open(file, 'a', encoding='UTF-8') as file:
        for x in data:
            file.write(str(x) + '\n')
        file.write('\n')
    return None
 
Sind n bissel viele Schnipsel, aber warum immer alles neu schreiben... Läuft in diesem Fall alles in der selben umgebung...
Wie gesagt, ich bin blutiger Anfänger...

Danke an Alle die mich hier etwas weiterbringen wollen.
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

__deets__ hat geschrieben: Montag 8. April 2019, 16:54 Also was da ja schon mal auffaellt ist, das dort recht viel unsinnige Zeichen drin sind, und jede Menge redundante Information. Wie schreibst du denn diese Zeile? Das sieht danach aus, als ob da ein Python Listen-Objekt einfach serialisiert wird. Statt es vernuenftig mit zb dem csv-Modul zu schreiben. Ausserdem ist es mehr oder weniger die gesamte ping-Ausgabe, da wuerde ich die Logik die sinnvoll aufzuarbeiten an die Stelle packen, die das Logfile *schreibt*, statt an allen Orten, wo es verarbeitet werden muss muehselig das unguenstige Format zu verarbeiten.

Wenn man jetzt mal das Format als gegeben ansieht, dann ist da mit regulaeren Ausdruecken zB einfach zu arbeiten:

Code: Alles auswählen

import re

line = """05.04.2019 00:00:08['PING 217.69.224.73 (217.69.224.73) 56(84) bytes of data.', '64 bytes from 217.69.224.73: icmp_seq=1 ttl=60 time=26.2 ms', '', '--- 217.69.224.73 ping statistics ---', '1 packets transmitted, 1 received, 0% packet loss, time 0ms', 'rtt min/avg/max/mdev = 26.232/26.232/26.232/0.000 ms', '']"""

timestamp, rest = line.split("[", 1)
ip = re.match(r".*PING ([\d\\.]+).*", rest).group(1)
latency = re.match(r".*time=([\d\\.]+).*", rest).group(1)
print(timestamp, ip, latency)
Danke, auf die Idee bin ich nicht gekommen... :shock:
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Benjo84: ein Modul `funktions` zu nennen, ist nicht nur ein Rechtschreibfehler, sondern auch ein schlechter Name, weil ja jedes Modul Funktionen enthält. Es gibt True in Python.
Es ist schlecht, dass das Datum zweimal ermittelt wird, so kann es vorkommen, dass der Eintrag in der falschen Datei landet. Für Dateinamen bietet sich das Format '%Y%m%d' an, weil sich das lexikalisch korrekt sortieren läßt.

Das `bool_out`-Ergebnis von `ping` wird gar nicht benutzt. Normalerweise würde man als Ergebnis auch ein Tuple statt einer Liste liefern. Wenn Dich nur die Zeit interessiert, dann parse die gleich in dieser Funktion.

In `write_log_file` schreibst Du immer eine Leerzeile. Ist das sinnvoll? Du hast ja den Zeitstempel zur Unterscheidung.
Dass die Funktion explizit None zurückliefert ist unsinnig.

Code: Alles auswählen

import csv
from datetime import datetime as DateTime

IPS = ['217.69.224.73']
INTERVAL = 10

def write_log_file(filename, data):
    with open(filename, 'a', encoding='UTF-8') as output:
        writer = csv.writer(output, delimiter=';')
 
def ping(ip_addr, timeout=100):
    if platform.system().lower() == 'windows':
        num_flag = '-n'
    else:
        num_flag = '-c'
    completed_ping = subprocess.run(['ping', num_flag, '1', '-w', str(timeout), ip_addr],
                                    stdout=subprocess.PIPE,  # Capture standard out
                                    stderr=subprocess.STDOUT)  # Capture standard error
    match = re.search('time=([\d.]+)', completed_ping.stdout)
    return match.group(1) if match else None
    
while True:
    now = DateTime.now()
    timings = [
        [now.strftime('%d.%m.%Y %H:%M:%S'), ping(ip)]
        for ip in IPS
    ]
    write_log_file('Ping_Log/{:%Y%m%d}.txt', timings)
    time.sleep(INTERVAL)
Benjo84
User
Beiträge: 11
Registriert: Montag 11. Februar 2019, 10:59

@Sirius3:
Danke, ich führe mir das morgen mal zu Gemüte.
Rückmeldung folgt.

Merci!
Antworten