Я работаю над информационной панелью, используя Shiny для Python и Plotly Express. Я пытаюсь создать диаграмму Ганта (используя px.timeline) для визуализации периодов работы различных котлов (состояния ВКЛ/ВЫКЛ).
Цель: я хочу отобразить горизонтальную гистограмму, где ось X представляет временную шкалу (дата и время), а столбцы представляют интервалы, в течение которых котел был активен («ВКЛ»).
Проблема: ось X на диаграмме неправильно отображает дату и время. Несмотря на то, что я передаю объекты datetime и устанавливаю формат галочки, метки осей кажутся неправильными (или не соответствуют формату).
Мое преобразование данных: я создал вспомогательную функцию create_gantt_data для преобразования моих данных временных рядов (поминутные снимки) в DataFrame интервалов с fecha_inicio (начало) и fecha_fin (конец). Кажется, эта логика работает нормально и возвращает правильные интервалы:
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)
Отображает временную диаграмму (стиль Ганта) для визуализации интервалов включения каждого котла.
Я работаю над информационной панелью, используя Shiny для Python и Plotly Express. Я пытаюсь создать диаграмму Ганта (используя px.timeline) для визуализации периодов работы различных котлов (состояния ВКЛ/ВЫКЛ). Цель: я хочу отобразить горизонтальную гистограмму, где ось X представляет временную шкалу (дата и время), а столбцы представляют интервалы, в течение которых котел был активен («ВКЛ»). Проблема: ось X на диаграмме неправильно отображает дату и время. Несмотря на то, что я передаю объекты datetime и устанавливаю формат галочки, метки осей кажутся неправильными (или не соответствуют формату). Мое преобразование данных: я создал вспомогательную функцию create_gantt_data для преобразования моих данных временных рядов (поминутные снимки) в DataFrame интервалов с fecha_inicio (начало) и fecha_fin (конец). Кажется, эта логика работает нормально и возвращает правильные интервалы: [code]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)
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) [/code] Отображает временную диаграмму (стиль Ганта) для визуализации интервалов включения каждого котла. [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() 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 [/code] пример CSV. Обратите внимание, что я динамически конвертирую в временные метки: [code]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 [/code] [img]https://i.sstatic.net/1Kicmlt3.png[/img]