2

I am working on a dashboard using Shiny for Python and Plotly Express. I am trying to create a Gantt chart (using px.timeline) to visualize the operating periods of different boilers (ON/OFF states).

The Goal: I want to display a horizontal bar chart where the X-axis represents the timeline (Date and Time) and the bars represent the intervals where a boiler was active ("ON").

The Problem: The X-axis on the chart is not rendering the dates and times correctly. Even though I am passing datetime objects and setting the tickformat, the axis labels seem incorrect (or are not respecting the format).

My Data Transformation: I created a helper function create_gantt_data to transform my time-series data (minute-by-minute snapshots) into a DataFrame of intervals with fecha_inicio (start) and fecha_fin (end). This logic seems to work fine and returns the correct intervals:

def prepare_dataframe(df):
    # Map original CSV column names to internal aliases for easier access
    df.rename(columns={
        'Bomba Calor - Temperatura de Aire (°C)': 'temp_aire',
        'Bomba Calor - Temperatura Entrada (°C)': 'temp_entrada',
        'Bomba Calor - Temperatura Salida (°C)' : 'temp_salida',
        'Bomba Calor - Estado Caldera 2 (estado)':'estado_caldera2',
        'Bomba Calor - Estado Caldera 1 (estado)': 'estado_caldera1',
        'Bomba Calor - Estado Bomba de Calor (estado)': 'estado_bomba_calor'
    }, inplace=True, errors='raise') # Using 'raise' to enforce strict checking (errors if columns are missing)

    if ('timestamp' in df.columns):
        df['timestamp'] = pd.to_datetime(df['timestamp'], format="%d-%m-%y %H:%M").astype("datetime64[s]")
        # Set 'timestamp' as the index to enable efficient time-based filtering
        df.set_index('timestamp', inplace=True)
        df.sort_index(inplace=True)   # Sort the index chronologically (required for .loc slicing)
        
    elif ('Fecha' in df.columns and 'Hora' in df.columns):
        # If Date and Time come in separate columns, combine them
        df['timestamp'] = pd.to_datetime(df['Fecha'] + ' ' + df['Hora'], format="%d-%m-%y %H:%M").astype("datetime64[s]")
        # Set 'timestamp' as the index to enable efficient time-based filtering
        df.set_index('timestamp', inplace=True)
        df.sort_index(inplace=True)  # Sort the index chronologically (required for .loc slicing)

    final_columns = [
        'temp_aire', 'temp_entrada', 'temp_salida', 
        'estado_caldera2', 'estado_caldera1', 'estado_bomba_calor'
    ]
    
    existing_columns = []
    for col in final_columns:
        if col in df.columns:
            existing_columns.append(col)
    
    return df[existing_columns]
def create_gantt_data(df_recorte):
    gantt_rows = []
    
    for col in ['estado_caldera1', 'estado_caldera2', 'estado_bomba_calor']:

        serie = df_recorte[col]
        
        # 1. Detect state changes
        cambios = serie.diff()
        
        # 2. Identify turn-on and turn-off timestamps based on diff
        turn_on = serie[cambios == 1.0].index
        turn_off = serie[cambios == -1.0].index
        
        # 3. Handle Edge Cases
        # If it starts ON (diff misses this because there is no previous value)
        if serie.iloc[0] == 1.0:
            # Prepend the first timestamp to turn_on
            turn_on = serie.index[:1].union(turn_on)
            
        # If it ends ON (it never turned off within the selected range)
        if serie.iloc[-1] == 1.0:
            # Append the last timestamp to turn_off
            turn_off = turn_off.union(serie.index[-1:])
        
        # 4. Create the intervals
        # zip() pairs the first ON with the corresponding first OFF, etc.
        for start, end in zip(turn_on, turn_off):
            gantt_rows.append({
                'caldera': col, 
                'fecha_inicio': start,
                'fecha_fin': end
            })
    
    return pd.DataFrame(gantt_rows)

Renders a timeline chart (Gantt style) to visualize the ON intervals of each boiler.

with ui.card(style="margin-top: 20px;"):
    ui.tags.h4("Gráfico de Encendido de Calderas")
    @render_plotly
    def show_calderas():
        data = data_for_timeline()
        fig = px.timeline(
            data_frame=data,
            x_start='fecha_inicio',
            height=500,
            x_end='fecha_fin',
            template="simple_white",
            y="caldera",
            color="caldera",
            title="Tiempos de Encendido por Caldera",
            labels={
                "fecha_inicio": "Inicio de Operación",
                "fecha_fin": "Fin de Operación",
                "caldera": "Calderas"
            }
        )

        fig.update_layout(
        xaxis_title="Fecha y Hora", 
        yaxis_title="Calderas", 
        xaxis=dict(tickformat="%d-%m-%Y %H:%M")
    )
        
        fig.update_xaxes(
            tickangle=60,
            nticks=20,
        )
        return fig

example of csv please note that I am dynamically converting to timestamps:

Fecha,Hora,Bomba Calor - Temperatura de Aire (°C),Bomba Calor - Temperatura Entrada (°C),Bomba Calor - Temperatura Salida (°C),Bomba Calor - Estado Caldera 2 (estado),Bomba Calor - Estado Caldera 1 (estado),Bomba Calor - Estado Bomba de Calor (estado)
04-10-25,00:01,22.2,63.4,63.4,0.0,1.0,0.0
04-10-25,00:11,21.9,61.8,61.7,0.0,1.0,1.0
04-10-25,00:21,21.7,60.3,60.3,1.0,1.0,0.0
04-10-25,00:30,22.2,63.4,63.4,1.0,0.0,0.0
04-10-25,00:41,21.9,61.8,61.7,1.0,0.0,1.0
04-10-25,00:51,21.7,60.3,60.3,0.0,1.0,0.0
04-10-25,00:55,22.2,63.4,63.4,0.0,1.0,1.0
04-10-25,01:03,21.9,61.8,61.7,0.0,0.0,1.0
04-10-25,01:10,21.7,60.3,60.3,0.0,0.0,1.0

Plotly timeline chart showing boiler activation intervals with incorrect X-axis date formatting

1
  • please post the part where you load your csv with pandas and convert the date dynamically and data_for_timeline function. The problem may well lie here as plotly generally displays date well (as at plotly.com/python/time-series) Commented Nov 17 at 17:42

1 Answer 1

1

I solved it with the two lines below:

data['fecha_inicio'] = pd.to_datetime(data['fecha_inicio']).astype("datetime64[s]")
data['fecha_fin'] = pd.to_datetime(data['fecha_fin']).astype("datetime64[s]")

included in code:

with ui.card(style="margin-top: 20px;"):
    ui.tags.h4("Gráfico de Encendido de Calderas")
    @render_plotly
    def show_calderas():
        data = data_for_timeline()
        # force datetime here:
        data['fecha_inicio'] = pd.to_datetime(data['fecha_inicio']).astype("datetime64[s]")
        data['fecha_fin'] = pd.to_datetime(data['fecha_fin']).astype("datetime64[s]")
        fig = px.timeline(
            data_frame=data,
            x_start='fecha_inicio',
            height=500,
            x_end='fecha_fin',
            template="simple_white",
            y="caldera",
            color="caldera",
            title="Tiempos de Encendido por Caldera",
            labels={
                "fecha_inicio": "Inicio de Operación",
                "fecha_fin": "Fin de Operación",
                "caldera": "Calderas"
            }
        )

        fig.update_layout(
        xaxis_title="Fecha y Hora", 
        yaxis_title="Calderas", 
        xaxis=dict(tickformat="%d-%m-%Y %H:%M")
    )
        
        fig.update_xaxes(
            tickangle=60,
            nticks=20,
        )
        return fig
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.