如何在Shiny Web应用中同时展示绘图与DataFrame表格?
解决Shiny应用同时展示绘图与数据框的问题
问题描述
我正在开发首个Shiny Web应用,通过用户输入的文本执行MySQL查询,从SQL数据库中获取数据。为简洁起见,我在代码片段中移除了查询语句。获取的数据将用于创建pandas数据框df_meetdata和绘图fig。当前应用仅能显示绘图,无法显示数据框,原因是Shiny无法从单个函数返回多个变量。请问如何才能同时展示绘图与数据框?
现有代码
app_ui = ui.page_fluid( ui.layout_sidebar( ui.panel_sidebar( ui.input_slider("n", "N", 0, 100, 20), ui.input_text("txt", "Referentienummer", value=""), ui.input_action_button("run", "Run"), ui.input_action_button("clear", "Clear"), ui.input_action_button("export", "Export") ), ui.panel_main( ui.output_text("nmr"), ui.output_text("print_referentienummer"), ui.output_plot("get_absorptie_spectrum"), ui.output_table("table_model"), ), ), ) def server(input, output, session): @output @render.text def nmr(): return f"input = {input.n()}" @output @render.text def print_referentienummer(): return f"Referentienummer = {input.txt()}" @output @render.plot @reactive.event(input.run, ignore_none=True) def get_absorptie_spectrum(): # Ophalen datum meting + sensorID om absorptiewaarden uit de mysql db te kunnen halen referentienummer = input.txt() #SQL queries for retrieving the data# df_meetdata = pd.DataFrame( {"BlueBox": bluebox_naam, "Referentienummer": referentienummer, "Datum": date_time, "ABS >= 2": max_absorptie}, index=[0]) fig, ax = plt.subplots() ax.plot(golflengte, absorptie_waarden) ax.set_title(referentienummer) return fig
解决方案
核心思路是把数据查询和处理逻辑抽离成响应式变量,让绘图和表格输出都依赖这个变量,避免单个函数返回多个值的问题。具体步骤如下:
- 创建响应式计算函数,统一处理SQL查询、数据框生成和绘图创建,返回包含两者的结果集合
- 为绘图和表格分别编写独立的输出函数,从响应式变量中提取对应内容
修改后的完整代码
import pandas as pd import matplotlib.pyplot as plt from shiny import ui, render, reactive, App app_ui = ui.page_fluid( ui.layout_sidebar( ui.panel_sidebar( ui.input_slider("n", "N", 0, 100, 20), ui.input_text("txt", "Referentienummer", value=""), ui.input_action_button("run", "Run"), ui.input_action_button("clear", "Clear"), ui.input_action_button("export", "Export") ), ui.panel_main( ui.output_text("nmr"), ui.output_text("print_referentienummer"), ui.output_plot("get_absorptie_spectrum"), ui.output_table("table_model"), ), ), ) def server(input, output, session): @output @render.text def nmr(): return f"input = {input.n()}" @output @render.text def print_referentienummer(): return f"Referentienummer = {input.txt()}" # 统一处理数据查询与绘图生成的响应式函数 @reactive.calc @reactive.event(input.run, ignore_none=True) def processed_data(): referentienummer = input.txt() # 此处替换为你的实际SQL查询逻辑,获取所需变量 bluebox_naam = "TestBox" date_time = "2024-05-20" max_absorptie = 2.5 golflengte = [100, 200, 300] absorptie_waarden = [0.5, 1.2, 2.5] df_meetdata = pd.DataFrame( {"BlueBox": bluebox_naam, "Referentienummer": referentienummer, "Datum": date_time, "ABS >= 2": max_absorptie}, index=[0]) fig, ax = plt.subplots() ax.plot(golflengte, absorptie_waarden) ax.set_title(referentienummer) # 返回包含数据框和绘图的字典 return {"df": df_meetdata, "fig": fig} # 绘图输出:从响应式变量中提取绘图对象 @output @render.plot def get_absorptie_spectrum(): return processed_data()["fig"] # 表格输出:从响应式变量中提取数据框 @output @render.table def table_model(): return processed_data()["df"] # 清空按钮逻辑:重置输入框 @reactive.Effect @reactive.event(input.clear) def _clear(): ui.update_text("txt", value="") app = App(app_ui, server)
关键说明
- 响应式变量优势:
processed_data()仅在点击"Run"时执行一次,避免重复查询数据库,提升应用性能;绘图和表格共享同一数据源,保证数据一致性 - 输出拆分:每个输出对应独立的
@output函数,完全符合Shiny的输出规则,解决单个函数无法返回多个值的问题 - 扩展性:后续若需添加导出功能,可直接从
processed_data()中获取数据框进行处理
内容的提问来源于stack exchange,提问作者Marren
相关产品推荐
相关产品推荐

