Aussagekräftige Graphen mit Plotly in Python erstellen

Plotly ist eine Open Source Graphing Library mit der ihr interaktive Charts und Maps erstellen könnt. In unserem Beispiel nutzen wir die Plotly Library in Kombination mit Python. Die Plotly Library ist jedoch auch für viele andere Sprachen verfügbar. (siehe Bild unterhalb, Python, R, Julia, Javascript, ggplot2, F#, MATLAB, Dash) https://plotly.com/

Ziel ist es in unserem Beispiel mit Hilfe von pandas (eine Datenanalyse Library für Python https://pandas.pydata.org/) eine Excel Datei im *.xlsx Format einzulesen. Die Excel Datei enthält 4 Spalten mit je 100 Datenpunkten. Es handelt sich um Messdaten eines Tiefpassfilters, welcher mit einem Einsgangssignal von einem Sinus mit +-1 Volt (Frequency) beaufschlagt wurde. Das Ausgangssignal des Filters wurde gemessen und aufgezeichnet (Filter Response (real)). Weiters gibt es noch eine Filter Response (real-fail), welche außerhalb der Limits ist, sowie eine ideale berechnete Filter Antwort (Filter Response (ideal)).

  • Frequency (eine Frequency logarithmische Rampe von 1kHz bis 300kHz)
  • Filter Response (real) – gemessene Filterantwort innerhalb der Limits
  • Filter Response (ideal) – kalkulierte ideale Filterantwort
  • Filter Response (real-fail) – gemessene Filterantwort außerhalb der Limits

Weiters wollen wir die mit pandas eingelesenen Daten in Python mit Hilfe der Plotly Library als interaktiven Graph im Browser darstellen.

Unser Python Script setzt sich aus den folgenden Abschnitten zusammen. Zuerst importieren wird die benötigten Libraries. Pandas, Plotly, Math. Pandas, openpyxl und Plotly haben wir hierzu in unserem Projekt in einem virtual environment installiert.

# import all neccessary libs
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import math

Dann lesen wir die Excel Datei mit den Filter Daten mit Hilfe von Pandas ein.

fileName = 'Filter_Data.xlsx'           # path to our excel file 
dataFrame = pd.read_excel(fileName)     # read the excel file with pandas

Anschließend bereiten wir unsere Filter Daten vor. Die Werte für unseren Tiefpass-Filter (Widerstand R und Kondensator C) sind R = 758.8 Ohm und der Kondensator C hat eine Kapazität von 6.8 nF. Daraus berechnen wir uns die Cut-Off Frequenz. Die Amplitude ist mit 1 Volt fix definiert in diesem Beispiel. Cut-Off Gain wird ebenfalls berechnet. Es gibt ein einstellbares Response Limit (im Beispiel mit 2 % angegeben).

# Filter Values (low-pass)
capacitor = 6.8E-9 # in Farad (6.8nF)
resistor = 745.8 # in Ohm
cutOffFrequency = 1/(2*math.pi*resistor*capacitor)
amplitude = 1 # voltage of signal amplitude
cutOffGain = amplitude / math.sqrt(2)
maxFrequency = dataFrame['Frequency'].max()   ## In pandas you can read a specific column of a data frame by filtering it with ['Column Name']

responseLimit = 2 # in % (user defined)
maxResponse = dataFrame['Filter Response (ideal)'] + dataFrame['Filter Response (ideal)'].max()*responseLimit/100 # Calculate and create max limit channel
minResponse = dataFrame['Filter Response (ideal)'] - dataFrame['Filter Response (ideal)'].max()*responseLimit/100 # Calculate and create min limit channel

Mit dieser Zeit wird bereits Hauptteil des Plots aufbereitet. Es wird das komplette dataFrame übergeben. (alle 4 Spalten eingelesen mit pandas) Aus diesem Data Frame werden jetzt einzelen Spalten verwendet um die Plot Darstellung zu definieren. Als x-Achse wird der Frequency Kanal verwendet. Weiters werden mehrerer y-Achsen definiert. Filter Response (real), Filter Response (ideal) und Filter Response (real-fail). Mit log_x = True wird eine logarythmische Darstellung aktiviert.

fig = px.line(data_frame=dataFrame, x="Frequency", y=["Filter Response (real)","Filter Response (ideal)", "Filter Response (real-fail)"], log_x=True)

Die nächsten 6 Zeilen Code sind optional und eine kosmetische Aufbereitung des Graphen. Die ersten beiden Zeilen fügen eine horizontale und vertikale Line zur Darstellung der Cut-Off Frequenz ein. Weiters wird ein Marker für die Cut-Off Frequenz gesetzt, sowie der Bereich rechts von der Cut-Off Frequenz mit einer leicht roten Hintergrundfarbe versehen.

fig.add_hline(cutOffGain, line_width=2, line_dash='dash', line_color='green')  
fig.add_vline(cutOffFrequency, line_width=2, line_dash='dash', line_color='green') 

fig.add_trace(go.Scatter(x=[cutOffFrequency], y=[cutOffGain], mode='markers', name='CutOff', marker=dict(size=15, color='red', symbol='x', line=dict(width=2))))
fig.add_vrect(x0=cutOffFrequency, x1=maxFrequency, line_width=0, fillcolor="red", opacity=0.1)

fig.add_scatter(x=dataFrame['Frequency'], y=maxResponse, name='Upper Limit', line_color ='red', line_dash='dot', opacity=0.5)
fig.add_scatter(x=dataFrame['Frequency'], y=minResponse, name='Lower Limit', line_color ='red', line_dash='dot', opacity=0.5)

In diesem Abschnitt können noch weitere Texte zur Beschreibung des Graphen gesetzt werden. Zum Beispiel der Titel, sowie die Achsenbeschriftungen. Eine Legende für die Signale wird auch aktiviert.

# Customize the layout
fig.update_layout(
    title_text = "Kreiseder IT Services - Filter Response",
    xaxis_title="Frequency [hz]",
    yaxis_title="Amplitude [volt]",
    legend_title="Legend",
    showlegend=True
)

Abschließend wird der erstellte Plot im Browser visualisiert.

# Show the chart
fig.show()

Interaktiver Graph mit Plotly und Python im Browser

Hier nochmal der gesamte Python Code in einem Block. Unterhalb der Link zum Download des Projekts inklusive Beispiel Daten.

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import math

fileName = 'Filter_Data.xlsx'           # path to our excel file 
dataFrame = pd.read_excel(fileName)     # read the excel file with pandas

# Filter Values (low-pass)
capacitor = 6.8E-9 # in Farad (6.8nF)
resistor = 745.8 # in Ohm
cutOffFrequency = 1/(2*math.pi*resistor*capacitor)
amplitude = 1 # voltage of signal amplitude
cutOffGain = amplitude / math.sqrt(2)
maxFrequency = dataFrame['Frequency'].max()   ## In pandas you can read a specific column of a data frame by filtering it with ['Column Name']
responseLimit = 2 # in % (user defined)
maxResponse = dataFrame['Filter Response (ideal)'] + dataFrame['Filter Response (ideal)'].max()*responseLimit/100 # Calculate and create max limit channel
minResponse = dataFrame['Filter Response (ideal)'] - dataFrame['Filter Response (ideal)'].max()*responseLimit/100 # Calculate and create min limit channel

fig = px.line(data_frame=dataFrame, x="Frequency", y=["Filter Response (real)","Filter Response (ideal)", "Filter Response (real-fail)"], log_x=True)

fig.add_hline(cutOffGain, line_width=2, line_dash='dash', line_color='green')  
fig.add_vline(cutOffFrequency, line_width=2, line_dash='dash', line_color='green')   

fig.add_trace(go.Scatter(x=[cutOffFrequency], y=[cutOffGain], mode='markers', name='CutOff', marker=dict(size=15, color='red', symbol='x', line=dict(width=2))))
fig.add_vrect(x0=cutOffFrequency, x1=maxFrequency, line_width=0, fillcolor="red", opacity=0.1)

fig.add_scatter(x=dataFrame['Frequency'], y=maxResponse, name='Upper Limit', line_color ='red', line_dash='dot', opacity=0.5)
fig.add_scatter(x=dataFrame['Frequency'], y=minResponse, name='Lower Limit', line_color ='red', line_dash='dot', opacity=0.5)

# Customize the layout
fig.update_layout(
    title_text = "Kreiseder IT Services - Filter Response",
    xaxis_title="Frequency [hz]",
    yaxis_title="Amplitude [volt]",
    legend_title="Legend",
    showlegend=True
)

# Show the chart
fig.show()

Diese und viele weitere Tricks lernt ihr in unseren Schulungen und Consultings. Link zu den Terminen

Link zum Beispielprojekt:
Die Programm Beispiele stellen nur einen Ausschnitt einer Gesamtapplikation dar. Verwendung der Code Beispiele auf eigene Gefahr.

Unsere Website enthält Links zu externen Websites, auf deren Inhalt wir keinen Einfluss haben. Aus diesem Grund übernehmen wir keine Gewähr für die Richtigkeit, Vollständigkeit und Aktualität der dort bereitgestellten Informationen. Die Verantwortung für den Inhalt der verlinkten Seiten liegt ausschließlich bei den Betreibern der jeweiligen Websites. Verwendung auf eigene Gefahr.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert