我一直在尝试在 PyShiny 中重现 这个最小的 R Shiny 应用程序,但没有成功。我尝试将 R 代码字面翻译为 python 代码,但看起来我需要在
@render.plot
装饰器内动态命名plotname()函数。我所有的尝试都没有返回任何情节。我知道问题出在底部附近的 exec() 行。如何动态更改循环中的函数名称?有关修复以下代码的任何帮助:
from shiny import App, render, ui
import matplotlib.pyplot as plt
#========== helper function =======================
def do_call(what, args=[], kwargs = {}): # code picked from https://stackoverflow.com/questions/38722804/equivalent-to-rs-do-call-in-python
return what(*args, **kwargs)
#=================================================
max_plots = 5
app_ui = ui.page_fluid(
ui.input_slider("n", "Number of plots", value=1, min=1, max=5),
ui.output_ui("plots")
)
def server(input, output, session):
@output
@render.ui
def plots():
plot_output_list = []
for i in range(1, input.n()+1):
plotname = f"plot{i}"
plot_output_list.append(ui.output_plot(plotname))
return do_call(ui.TagList, plot_output_list)
for j in range(1, max_plots+1):
#my_i = j
#plotname = f"plot{my_i}"
@output
@render.plot
def exec(f"plot{j}")(): # this line is not correct
fig = plt.plot(range(1, j+1), range(1, j+1))
return fig
app = App(app_ui, server)
from shiny import App, render, ui
import matplotlib.pyplot as plt
## ========== helper function =======================##
def do_call(what, args=[],
kwargs={}): # code picked from https://stackoverflow.com/questions/38722804/equivalent-to-rs-do-call-in-python
return what(*args, **kwargs)
# =================================================
max_plots = 5
app_ui = ui.page_fluid(
ui.input_slider("n", "Number of plots", value=1, min=1, max=5),
ui.output_ui("plots")
)
def server(input, output, session):
@output
@render.ui
def plots():
plot_output_list = []
for i in range(1, input.n() + 1):
plotname = f"plot{i}"
plot_output_list.append(ui.output_plot(plotname))
return do_call(ui.TagList, plot_output_list)
for j in range(1, max_plots + 1):
@output(f"plot{j}")
@render.plot
def exec():
fig = plt.plot(range(1, j + 1), range(1, j + 1))
return fig
app = App(app_ui, server)
我终于找到了一个有效的解决方案。我正在发布解决方案,以防其他人需要它。
from shiny import App, render, ui
import matplotlib.pyplot as plt
#========== helper function =======================
def do_call(what, args=[], kwargs = {}): # code picked from https://stackoverflow.com/questions/38722804/equivalent-to-rs-do-call-in-python
return what(*args, **kwargs)
#=================================================
max_plots = 5
app_ui = ui.page_fluid(
ui.input_slider("n", "Number of plots", value=1, min=1, max=5),
ui.output_ui("plots")
)
def server(input, output, session):
@output
@render.ui
def plots():
plot_output_list = []
for i in range(1, input.n()+1):
plotname = f"plot{i}"
plot_output_list.append(ui.output_plot(plotname))
return do_call(ui.TagList, plot_output_list)
for j in range(1, max_plots+1):
exec(f"@output\[email protected]\ndef plot{j}():\n fig = plt.plot(range(1, {j+1}), range(1, {j+1}))\n return fig")
app = App(app_ui, server)
该解决方案正确捕获了每个图的上下文(每个图都是不同的——查看坐标轴)。原始代码使用循环中定义的单个闭包,因此所有 5 个图都是相同的。
do_call
助手是不必要的——只需调用 ui.tagList()
。
请注意,您也可以直接在
render.plot
中调用 render_plot_func
,而不是使用装饰器,即 return render.plot(f)
并删除 @render.plot
。在输出的情况下,您不能使用装饰器语法,因为那里没有可以注释的函数定义。
from shiny import App, render, ui
import matplotlib.pyplot as plt
# =================================================
max_plots = 5
app_ui = ui.page_fluid(
ui.input_slider("n", "Number of plots", value=1, min=1, max=5),
ui.output_ui("plots")
)
def server(input, output, session):
def render_plot_func(j):
@render.plot
def f():
fig = plt.plot(range(1, j + 1), range(1, j + 1))
return fig
return f
@output
@render.ui
def plots():
plot_output_list = []
for i in range(1, input.n() + 1):
plotname = f"plot{i}"
plot_output_list.append(ui.output_plot(plotname))
output(render_plot_func(i), id=plotname)
return ui.TagList(plot_output_list)
app = App(app_ui, server)
@sbik:您对自己问题的回答看起来很有效,但我建议首选此版本(没有
exec
调用),以提高可读性。