OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

How to update axis labels in Plotly Dashboard?

  • Thread starter Thread starter Sorade
  • Start date Start date
S

Sorade

Guest
I am using the following code to monitor an output file from another application named TOUGH.

I define a graph component and update its figure using the update_graph_live() call back function. In order for my graphs to remain legible, I adjust the x-axis time units. But although the data appears to update correctly, the x-axis labels remain what they were when the app is initially launched, despite me returning the figure as part of the callback function. I run the script from Spyder IDE in an Anaconda virtual env.

I've scratched my head some more when I actually checked the Dash debugger and saw that my debugging counter was actually incrementing fine on the Dashboard, but somehow the figure is not updated.

enter image description here

Any help would be most welcome.

Here is what the dash looks like: Dash Output

Code:
import os
import time
import pandas as pd
import numpy as np
import plotly.graph_objs as go
from dash import Dash, dcc, html
from dash.dependencies import Input, Output, State
from plotly.subplots import make_subplots

# Create Dash app
app = Dash(__name__)

# Layout of the app
app.layout = html.Div([
    dcc.Input(
        id='file-path',
        type='text',
        placeholder='Enter the file path...',
        style={'width': '100%'}
    ),
    dcc.Graph(id='live-graph', animate=True),
    dcc.Interval(
        id='graph-update',
        interval=1000,  # in milliseconds (1 seconds)
        n_intervals=0
    )
])

def load_data(file_path):
    try:
        data = pd.read_csv(file_path, header=0, skiprows=0, index_col=False)
        new_headers = [s.strip() for s in data.columns.values] # remove whitespaces
        data.columns = new_headers

        # Adjust the times in a legible unit
        max_time = data["TIME(S)"].max()
        if max_time > 2 * 365 * 24 * 3600:
            data["TIME(Years)"] = data["TIME(S)"] / (365 * 24 * 3600)
            time_col = "TIME(Years)"
            time_label = "Time (Years)"
        elif max_time > 2 * 30 * 24 * 3600:
            data["TIME(Months)"] = data["TIME(S)"] / (30 * 24 * 3600)
            time_col = "TIME(Months)"
            time_label = "Time (Months)"
        elif max_time > 2 * 7 * 24 * 3600:
            data["TIME(Weeks)"] = data["TIME(S)"] / (7 * 24 * 3600)
            time_col = "TIME(Weeks)"
            time_label = "Time (Weeks)"
        elif max_time > 1 * 24 * 3600:
            data["TIME(Days)"] = data["TIME(S)"] / (24 * 3600)
            time_col = "TIME(Days)"
            time_label = "Time (Days)"
        elif max_time > 0.5 * 3600:
            data["TIME(Hours)"] = data["TIME(S)"] / 3600
            time_col = "TIME(Hours)"
            time_label = "Time (Hours)"
        elif max_time > 24:
            data["TIME(Minutes)"] = data["TIME(S)"] / 60
            time_col = "TIME(Minutes)"
            time_label = "Time (Minutes)"
        else:
            time_col = "TIME(S)"
            time_label = "Time (Seconds)"
        print(time_label)
        data.iloc[:, 1:] = data.iloc[:, 1:].apply(pd.to_numeric)
        return data, time_col, time_label
    except Exception as e:
        print(f"Error loading data: {e}")
        return None, None, None

@app.callback(
    Output('live-graph', 'figure'),
    [Input('graph-update', 'n_intervals')],
    [State('file-path', 'value')]
)
def update_graph_live(n_intervals, file_path):
    if not file_path or not os.path.exists(file_path):
        return go.Figure()

    data, time_col, time_label = load_data(file_path)
    if data is None:
        return go.Figure()

    time = data[time_col]
    pressure = data["PRES"]
    temperature = data["TEMP"]
    saturation_gas = data["SAT_Gas"]
    saturation_aqu = data["SAT_Aqu"]
    time_diff = data["TIME(S)"].diff().dropna()

    fig = make_subplots(rows=2, cols=3, subplot_titles=("Pressure vs Time", "Molar Fractions vs Time", "Time Difference vs Time", "Temperature vs Time", "Saturation vs Time"))

    fig.add_trace(go.Scatter(x=time, y=pressure, mode='lines', name='Pressure (Pa)', line=dict(color='blue')), row=1, col=1)
    fig.add_trace(go.Scatter(x=time, y=temperature, mode='lines', name='Temperature (°C)', line=dict(color='red')), row=2, col=1)
    fig.add_trace(go.Scatter(x=time, y=saturation_gas, mode='lines', name='Gas Phase Saturation', line=dict(color='green')), row=2, col=2)
    fig.add_trace(go.Scatter(x=time, y=saturation_aqu, mode='lines', name='Aqueous Phase Saturation', line=dict(color='blue')), row=2, col=2)
    
    # just sorting out some colouring and styling for legibility
    for col in data.columns[6:]:
        color = 'black'
        linestyle = 'solid'
        if 'H2' in col:
            color = 'green'
        elif 'CH4' in col:
            color = 'lightblue'
        elif 'water' in col:
            color = 'blue'
        
        if 'Gas' in col:
            linestyle = 'solid'
        elif 'Aqu' in col:
            linestyle = 'dash'
            
        if 'TIME' in col:
            continue

        fig.add_trace(go.Scatter(x=time, y=data[col], mode='lines', name=col, line=dict(color=color, dash=linestyle)), row=1, col=2)

    fig.add_trace(go.Scatter(x=time.iloc[1:len(time_diff)+1], y=time_diff, mode='lines', name='Time Step Size', line=dict(color='purple')), row=1, col=3)

    # # PREVIOUS ATTEMPT
    # fig.update_layout(
    #     title='Real-Time TOUGH Simulation Data', 
    #     showlegend=True,
    #     yaxis3_type='log'  # Setting the y-axis of the third subplot to log scale
    # )
    
    # PREVIOUS ATTEMPT
    # # Update x-axis labels individually
    # fig.update_xaxes(title_text=time_label, row=1, col=1)
    # fig.update_xaxes(title_text=time_label, row=1, col=2)
    # fig.update_xaxes(title_text=time_label, row=1, col=3)
    # fig.update_xaxes(title_text=time_label, row=2, col=1)
    # fig.update_xaxes(title_text=time_label, row=2, col=2)
    
    # CURRENT ATTEMPT
    fig.update_layout(
    title='Real-Time TOUGH Simulation Data', 
    showlegend=True,
    yaxis3_type='log',  # Setting the y-axis of the third subplot to log scale
    xaxis_title_text=time_label+str(n_intervals),  # Update x-axis 
    # labels. I add the interval counter for debugging
    xaxis2_title_text=time_label,
    xaxis3_title_text=time_label,
    xaxis4_title_text=time_label,
    xaxis5_title_text=time_label
)
    print('Update> ',time_label) # just a sanity check which demonstrates that the time
    #  conversion and label change is being read correctly - which it is
    
    fig.update_yaxes(title_text='Pressure (Pa)', row=1, col=1)
    fig.update_yaxes(title_text='Molar Fraction (-)', row=1, col=2)
    fig.update_yaxes(title_text='Timestep (s)', row=1, col=3)
    fig.update_yaxes(title_text='Temperature (°C)', row=2, col=1)
    fig.update_yaxes(title_text= 'Saturation (-)', row=2, col=2)


    return fig

if __name__ == '__main__':
    app.run_server(debug=True, port=8050)
<p>I am using the following code to monitor an output file from another application named TOUGH.</p>
<p>I define a graph component and update its figure using the update_graph_live() call back function. In order for my graphs to remain legible, I adjust the x-axis time units. But although the data appears to update correctly, the x-axis labels remain what they were when the app is initially launched, despite me returning the figure as part of the callback function. I run the script from Spyder IDE in an Anaconda virtual env.</p>
<p>I've scratched my head some more when I actually checked the Dash debugger and saw that my debugging counter was actually incrementing fine on the Dashboard, but somehow the figure is not updated.</p>
<p><a href="https://i.sstatic.net/JflCyMW2.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/JflCyMW2.png" alt="enter image description here" /></a></p>
<p>Any help would be most welcome.</p>
<p>Here is what the dash looks like:
<a href="https://i.sstatic.net/87DkVPTK.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/87DkVPTK.png" alt="Dash Output" /></a></p>
<pre><code>import os
import time
import pandas as pd
import numpy as np
import plotly.graph_objs as go
from dash import Dash, dcc, html
from dash.dependencies import Input, Output, State
from plotly.subplots import make_subplots

# Create Dash app
app = Dash(__name__)

# Layout of the app
app.layout = html.Div([
dcc.Input(
id='file-path',
type='text',
placeholder='Enter the file path...',
style={'width': '100%'}
),
dcc.Graph(id='live-graph', animate=True),
dcc.Interval(
id='graph-update',
interval=1000, # in milliseconds (1 seconds)
n_intervals=0
)
])

def load_data(file_path):
try:
data = pd.read_csv(file_path, header=0, skiprows=0, index_col=False)
new_headers = [s.strip() for s in data.columns.values] # remove whitespaces
data.columns = new_headers

# Adjust the times in a legible unit
max_time = data["TIME(S)"].max()
if max_time > 2 * 365 * 24 * 3600:
data["TIME(Years)"] = data["TIME(S)"] / (365 * 24 * 3600)
time_col = "TIME(Years)"
time_label = "Time (Years)"
elif max_time > 2 * 30 * 24 * 3600:
data["TIME(Months)"] = data["TIME(S)"] / (30 * 24 * 3600)
time_col = "TIME(Months)"
time_label = "Time (Months)"
elif max_time > 2 * 7 * 24 * 3600:
data["TIME(Weeks)"] = data["TIME(S)"] / (7 * 24 * 3600)
time_col = "TIME(Weeks)"
time_label = "Time (Weeks)"
elif max_time > 1 * 24 * 3600:
data["TIME(Days)"] = data["TIME(S)"] / (24 * 3600)
time_col = "TIME(Days)"
time_label = "Time (Days)"
elif max_time > 0.5 * 3600:
data["TIME(Hours)"] = data["TIME(S)"] / 3600
time_col = "TIME(Hours)"
time_label = "Time (Hours)"
elif max_time > 24:
data["TIME(Minutes)"] = data["TIME(S)"] / 60
time_col = "TIME(Minutes)"
time_label = "Time (Minutes)"
else:
time_col = "TIME(S)"
time_label = "Time (Seconds)"
print(time_label)
data.iloc[:, 1:] = data.iloc[:, 1:].apply(pd.to_numeric)
return data, time_col, time_label
except Exception as e:
print(f"Error loading data: {e}")
return None, None, None

@app.callback(
Output('live-graph', 'figure'),
[Input('graph-update', 'n_intervals')],
[State('file-path', 'value')]
)
def update_graph_live(n_intervals, file_path):
if not file_path or not os.path.exists(file_path):
return go.Figure()

data, time_col, time_label = load_data(file_path)
if data is None:
return go.Figure()

time = data[time_col]
pressure = data["PRES"]
temperature = data["TEMP"]
saturation_gas = data["SAT_Gas"]
saturation_aqu = data["SAT_Aqu"]
time_diff = data["TIME(S)"].diff().dropna()

fig = make_subplots(rows=2, cols=3, subplot_titles=("Pressure vs Time", "Molar Fractions vs Time", "Time Difference vs Time", "Temperature vs Time", "Saturation vs Time"))

fig.add_trace(go.Scatter(x=time, y=pressure, mode='lines', name='Pressure (Pa)', line=dict(color='blue')), row=1, col=1)
fig.add_trace(go.Scatter(x=time, y=temperature, mode='lines', name='Temperature (°C)', line=dict(color='red')), row=2, col=1)
fig.add_trace(go.Scatter(x=time, y=saturation_gas, mode='lines', name='Gas Phase Saturation', line=dict(color='green')), row=2, col=2)
fig.add_trace(go.Scatter(x=time, y=saturation_aqu, mode='lines', name='Aqueous Phase Saturation', line=dict(color='blue')), row=2, col=2)

# just sorting out some colouring and styling for legibility
for col in data.columns[6:]:
color = 'black'
linestyle = 'solid'
if 'H2' in col:
color = 'green'
elif 'CH4' in col:
color = 'lightblue'
elif 'water' in col:
color = 'blue'

if 'Gas' in col:
linestyle = 'solid'
elif 'Aqu' in col:
linestyle = 'dash'

if 'TIME' in col:
continue

fig.add_trace(go.Scatter(x=time, y=data[col], mode='lines', name=col, line=dict(color=color, dash=linestyle)), row=1, col=2)

fig.add_trace(go.Scatter(x=time.iloc[1:len(time_diff)+1], y=time_diff, mode='lines', name='Time Step Size', line=dict(color='purple')), row=1, col=3)

# # PREVIOUS ATTEMPT
# fig.update_layout(
# title='Real-Time TOUGH Simulation Data',
# showlegend=True,
# yaxis3_type='log' # Setting the y-axis of the third subplot to log scale
# )

# PREVIOUS ATTEMPT
# # Update x-axis labels individually
# fig.update_xaxes(title_text=time_label, row=1, col=1)
# fig.update_xaxes(title_text=time_label, row=1, col=2)
# fig.update_xaxes(title_text=time_label, row=1, col=3)
# fig.update_xaxes(title_text=time_label, row=2, col=1)
# fig.update_xaxes(title_text=time_label, row=2, col=2)

# CURRENT ATTEMPT
fig.update_layout(
title='Real-Time TOUGH Simulation Data',
showlegend=True,
yaxis3_type='log', # Setting the y-axis of the third subplot to log scale
xaxis_title_text=time_label+str(n_intervals), # Update x-axis
# labels. I add the interval counter for debugging
xaxis2_title_text=time_label,
xaxis3_title_text=time_label,
xaxis4_title_text=time_label,
xaxis5_title_text=time_label
)
print('Update> ',time_label) # just a sanity check which demonstrates that the time
# conversion and label change is being read correctly - which it is

fig.update_yaxes(title_text='Pressure (Pa)', row=1, col=1)
fig.update_yaxes(title_text='Molar Fraction (-)', row=1, col=2)
fig.update_yaxes(title_text='Timestep (s)', row=1, col=3)
fig.update_yaxes(title_text='Temperature (°C)', row=2, col=1)
fig.update_yaxes(title_text= 'Saturation (-)', row=2, col=2)


return fig

if __name__ == '__main__':
app.run_server(debug=True, port=8050)
</code></pre>
 
Top