You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

传入字符串格式时间时Bokeh图表不显示,请求代码排查

问题

通过Anaconda Prompt执行命令bokeh serve --show app.py启动Bokeh Server应用,输入电池最大容量和效率参数后,预期的三个柱状图(充电站24小时能耗、电池电量变化、总能耗)未在Bokeh界面显示。以下是完整代码:

from bokeh.io import curdoc
from bokeh.layouts import column, gridplot
from bokeh.models import ColumnDataSource, TextInput, Button
from bokeh.plotting import figure
from datetime import datetime

class Bateria:
    def __init__(self, capacidade_maxima, eficiencia):
        self.capacidade_maxima = capacidade_maxima
        self.carga_atual = 0
        self.eficiencia = eficiencia
    
    def carregar(self, energia_ev):
        potencia_carregada = max(0, (self.capacidade_maxima - self.carga_atual) * self.eficiencia)
        self.carga_atual += potencia_carregada
    
    def descarregar(self, energia_ev):
        potencia_descarregada = max(0, energia_ev - 88)
        self.carga_atual -= potencia_descarregada
        self.carga_atual = max(0, self.carga_atual)
    
    def nivel_de_carga(self):
        return self.carga_atual


dados = [0, 0, 0, 0, 0, 0, 11, 110, 33, 121, 22, 77, 132, 66, 99, 44, 88, 33, 22, 11, 0, 0, 0, 0]
precos_eletricidade = [0.13, 0.11, 0.12, 0.07, 0.14, 0.12, 0.15, 0.16, 0.18, 0.20, 0.25, 0.26, 0.28, 0.29, 0.30, 0.29, 0.28, 0.27, 0.26, 0.25, 0.23, 0.24, 0.20, 0.16, 0.14]
tempo = [f"{i}:00" for i in range(24)]


source_dados = ColumnDataSource(data=dict(x=tempo, y=dados))
source_cargas_bateria = ColumnDataSource(data=dict(x=tempo, y=[0]*24))
source_consumo_total = ColumnDataSource(data=dict(x=tempo, y=[0]*24))

# Widgets
capacity_input = TextInput(value="50", title="Capacidade Máxima da Bateria (kW):")
efficiency_input = TextInput(value="90", title="Eficiência da Bateria (%):")
update_button = Button(label="Atualizar Gráficos")

def update_plots():
    try:
        capacidade_maxima = int(capacity_input.value)
        eficiencia = int(efficiency_input.value) / 100
        
        bateria = Bateria(capacidade_maxima, eficiencia)
        
        cargas_bateria = []
        consumo_total = []
        dados_atualizados = []
        
        for i, energia_ev in enumerate(dados):
            if i <= 6:
                preco_atual = precos_eletricidade[i]
                if preco_atual == min(precos_eletricidade[:6]):
                    bateria.carregar(capacidade_maxima)
                
            if i > 6 and bateria.carga_atual < bateria.capacidade_maxima and energia_ev <= 88 and bateria.carga_atual + energia_ev < 90:
                bateria.carregar(energia_ev)

            soc = bateria.carga_atual / bateria.capacidade_maxima

            if energia_ev > 88 and soc > 0.2:
                bateria.descarregar(energia_ev)

            soc = bateria.carga_atual / bateria.capacidade_maxima

            cargas_bateria.append(bateria.nivel_de_carga())
            consumo_total.append(energia_ev + bateria.nivel_de_carga())
            dados_atualizados.append(energia_ev)
        
        source_dados.data = dict(x=tempo, y=dados_atualizados)
        source_cargas_bateria.data = dict(x=tempo, y=cargas_bateria)
        source_consumo_total.data = dict(x=tempo, y=consumo_total)
    
    except ValueError as e:
        print(e)

update_button.on_click(update_plots)


plot_dados = figure(title="Consumo da Estação ao Longo de 24 Horas", x_axis_label="Tempo (horas)", y_axis_label="Energia (kWh)")
plot_dados.vbar(x='x', top='y', width=0.4, source=source_dados, color="red")

plot_cargas_bateria = figure(title="Evolução da Energia da Bateria ao Longo de 24 Horas", x_axis_label="Tempo (horas)", y_axis_label="Energia (kWh)")
plot_cargas_bateria.vbar(x='x', top='y', width=0.4, source=source_cargas_bateria, color="blue")

plot_consumo_total = figure(title="Consumo da Estação + Energia da Bateria ao Longo de 24 Horas", x_axis_label="Tempo (horas)", y_axis_label="Energia Total (kWh)")
plot_consumo_total.vbar(x='x', top='y', width=0.4, source=source_consumo_total, color="green")

layout = column(capacity_input, efficiency_input, update_button, gridplot([[plot_dados, plot_cargas_bateria, plot_consumo_total]]))
curdoc().add_root(layout)
代码问题分析与修复

核心问题及解决办法:

  1. x轴类型未指定导致渲染异常
    你的tempo是字符串格式的时间标签(如"0:00"),但创建figure时未明确设置x轴为分类类型,Bokeh默认会尝试按数值解析,导致柱状图无法正确渲染。需要在每个figure初始化时添加x_axis_type="category"。

  2. 电池充电逻辑错误
    Bateria类的carregar方法未使用传入的energia_ev参数,而是直接用电池最大容量计算充电量,导致充电逻辑完全不符合预期,最终电池电量数据异常,图表无有效内容。修正后的充电逻辑应结合可用容量和输入的充电能量,同时考虑效率:

    def carregar(self, energia_ev):
        # 计算实际可充入的能量:不超过剩余容量,且乘以效率
        potencia_carregada = max(0, min(energia_ev * self.eficiencia, self.capacidade_maxima - self.carga_atual))
        self.carga_atual += potencia_carregada
    
  3. 布局适配问题
    三个图表放在一行的gridplot中,可能因屏幕宽度不足导致图表被挤压或隐藏。可以调整gridplot布局为两行,或设置图表的sizing_mode="stretch_width"让其自适应宽度。

修复后的完整代码:

from bokeh.io import curdoc
from bokeh.layouts import column, gridplot
from bokeh.models import ColumnDataSource, TextInput, Button
from bokeh.plotting import figure

class Bateria:
    def __init__(self, capacidade_maxima, eficiencia):
        self.capacidade_maxima = capacidade_maxima
        self.carga_atual = 0
        self.eficiencia = eficiencia
    
    def carregar(self, energia_ev):
        # 修正充电逻辑:结合输入能量、剩余容量和效率计算实际充电量
        potencia_carregada = max(0, min(energia_ev * self.eficiencia, self.capacidade_maxima - self.carga_atual))
        self.carga_atual += potencia_carregada
    
    def descarregar(self, energia_ev):
        potencia_descarregada = max(0, energia_ev - 88)
        self.carga_atual -= potencia_descarregada
        self.carga_atual = max(0, self.carga_atual)
    
    def nivel_de_carga(self):
        return self.carga_atual


dados = [0, 0, 0, 0, 0, 0, 11, 110, 33, 121, 22, 77, 132, 66, 99, 44, 88, 33, 22, 11, 0, 0, 0, 0]
precos_eletricidade = [0.13, 0.11, 0.12, 0.07, 0.14, 0.12, 0.15, 0.16, 0.18, 0.20, 0.25, 0.26, 0.28, 0.29, 0.30, 0.29, 0.28, 0.27, 0.26, 0.25, 0.23, 0.24, 0.20, 0.16, 0.14]
tempo = [f"{i}:00" for i in range(24)]


source_dados = ColumnDataSource(data=dict(x=tempo, y=dados))
source_cargas_bateria = ColumnDataSource(data=dict(x=tempo, y=[0]*24))
source_consumo_total = ColumnDataSource(data=dict(x=tempo, y=[0]*24))

# Widgets
capacity_input = TextInput(value="50", title="Capacidade Máxima da Bateria (kW):")
efficiency_input = TextInput(value="90", title="Eficiência da Bateria (%):")
update_button = Button(label="Atualizar Gráficos")

def update_plots():
    try:
        capacidade_maxima = int(capacity_input.value)
        eficiencia = int(efficiency_input.value) / 100
        
        bateria = Bateria(capacidade_maxima, eficiencia)
        
        cargas_bateria = []
        consumo_total = []
        dados_atualizados = []
        
        for i, energia_ev in enumerate(dados):
            if i <= 6:
                preco_atual = precos_eletricidade[i]
                if preco_atual == min(precos_eletricidade[:6]):
                    bateria.carregar(capacidade_maxima)
                
            if i > 6 and bateria.carga_atual < bateria.capacidade_maxima and energia_ev <= 88 and bateria.carga_atual + energia_ev < 90:
                bateria.carregar(energia_ev)

            soc = bateria.carga_atual / bateria.capacidade_maxima

            if energia_ev > 88 and soc > 0.2:
                bateria.descarregar(energia_ev)

            cargas_bateria.append(bateria.nivel_de_carga())
            # 若总能耗是充电站能耗与电池充放电的净量,可根据需求调整,此处保留原逻辑
            consumo_total.append(energia_ev + bateria.nivel_de_carga())
            dados_atualizados.append(energia_ev)
        
        source_dados.data = dict(x=tempo, y=dados_atualizados)
        source_cargas_bateria.data = dict(x=tempo, y=cargas_bateria)
        source_consumo_total.data = dict(x=tempo, y=consumo_total)
    
    except ValueError as e:
        print(e)

update_button.on_click(update_plots)


# 修正:添加x_axis_type="category",设置sizing_mode自适应宽度
plot_dados = figure(title="Consumo da Estação ao Longo de 24 Horas", 
                    x_axis_label="Tempo (horas)", y_axis_label="Energia (kWh)",
                    x_axis_type="category", sizing_mode="stretch_width")
plot_dados.vbar(x='x', top='y', width=0.4, source=source_dados, color="red")

plot_cargas_bateria = figure(title="Evolução da Energia da Bateria ao Longo de 24 Horas", 
                             x_axis_label="Tempo (horas)", y_axis_label="Energia (kWh)",
                             x_axis_type="category", sizing_mode="stretch_width")
plot_cargas_bateria.vbar(x='x', top='y', width=0.4, source=source_cargas_bateria, color="blue")

plot_consumo_total = figure(title="Consumo da Estação + Energia da Bateria ao Longo de 24 Horas", 
                            x_axis_label="Tempo (horas)", y_axis_label="Energia Total (kWh)",
                            x_axis_type="category", sizing_mode="stretch_width")
plot_consumo_total.vbar(x='x', top='y', width=0.4, source=source_consumo_total, color="green")

# 调整布局为两行,避免宽度不足
layout = column(capacity_input, efficiency_input, update_button, 
                gridplot([[plot_dados, plot_cargas_bateria], [plot_consumo_total, None]], 
                         sizing_mode="stretch_width"))
curdoc().add_root(layout)

内容的提问来源于stack exchange,提问作者Joao Ribeiro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:44:55