如何在Shiny for Python中为Pandas DataFrame添加下载按钮并解决报错
解决Shiny for Python中Pandas DataFrame导出下载的问题
核心问题分析
你的代码存在几个关键问题导致下载报错:
- 下载按钮ID
download_new_df和装饰器绑定的函数名不匹配 - 下载函数直接返回DataFrame,不符合Shiny要求——需要将DataFrame内容写入输出流
new_df定义在reactive.Effect内部,作用域受限,下载时无法正确获取最新的DataFrame
修正后的完整代码
import pandas as pd from shiny import ui, reactive, session, App # A card component wrapper. def ui_card(title, *args): return ( ui.div( {"class": "card mb-4"}, ui.div(title, class_="card-header"), ui.div({"class": "card-body"}, *args), ), ) app_ui = ui.page_fluid( ui.panel_title("pyPARAGON network inference and analysis tool"), ### PANEL 1 Gene list ui.panel_well( ui.tags.h4("Choose a file to upload"), ui.row( ui.input_file( "initial_file", "", multiple=True ) ), ui.row( ui.column( 4, ui.input_select( "Column1", "Select input column", {}) ), ui.column( 4, ui.input_select( "Column2", "Select weight column", {}) ) ) ), # download panel ui_card( ui.download_button("download_new_df", "Download_selected_tsv"), ), ui.output_text_verbatim("txt") ) def server(input, output, session): # 用响应式变量存储处理后的DataFrame和输出文件名 processed_df = reactive.Value(None) output_filename = reactive.Value("output.tab") @reactive.Effect def _(): file_infos = input.initial_file() Column1 = input.Column1() Column2 = input.Column2() if not file_infos: processed_df.set(None) return # 处理上传文件(取最后一个上传的文件) file_info = file_infos[-1] input_df = pd.read_csv(file_info["datapath"], sep="\t") input_name = ".".join(file_info["name"].split(".")[:-1]) columns = input_df.columns.to_list() # 更新下拉选择框选项与选中状态 ui.update_select( id="Column1", label="Select input column", choices=[""] + columns, selected=Column1 if Column1 in columns else "" ) ui.update_select( id="Column2", label="Select weight column", choices=[""] + columns, selected=Column2 if Column2 in columns else "" ) # 生成并存储处理后的DataFrame if Column1 and Column2 and Column1 in columns and Column2 in columns: new_df = input_df[[Column1, Column2]] processed_df.set(new_df) output_filename.set(f"{input_name}.tab") else: processed_df.set(None) # 绑定下载按钮对应的处理函数 @session.download(id="download_new_df", filename=lambda: output_filename()) def download_new_df(): df = processed_df() if df is not None: # 将DataFrame转为TSV格式字节流返回 yield df.to_csv(sep="\t", index=False).encode("utf-8") app = App(app_ui, server, debug=True)
关键修改点说明
- 匹配下载按钮ID:
@session.download的id参数设置为download_new_df,与UI中下载按钮的ID完全对应 - 响应式变量存储数据:用
reactive.Value保存处理后的DataFrame和输出文件名,确保下载时能获取最新的处理结果 - 正确生成下载流:通过
df.to_csv(sep="\t", index=False).encode("utf-8")生成TSV格式的字节流,再通过yield返回给下载组件,实现类似pd.DataFrame.to_csv()的导出效果 - 完善边界检查:增加列存在性验证,避免无效列选择导致的报错;多文件上传时默认取最后一个文件处理
- 优化下拉框选中逻辑:确保选中的列在当前文件的列列表中,避免无效选中状态
内容的提问来源于stack exchange,提问作者M. Kaan ARICI
相关产品推荐
相关产品推荐

