At my workplace we are looking into consolidation of tools. At the moment, some people are writing dashboards in Dash and some are doing it in Shiny. Personally, I have a python background, so i never tried Shiny - but to participate in the discussion, i figured i should at least try it out. Even though I am new to R, I couldn’t helt but notice the simplicity
of their syntax, in particular their reactive expressions.
As an example, consider this this small Dash app that calculates an intermediate result (x to some power, i know it is not necessary to do this; but the point is to illustrate more complicated workflows where this is necessary) and displays the result in two different views (text and graph),
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
from dash.dependencies import Output, Input
from dash_extensions.enrich import DashProxy
app = DashProxy()
app.layout = html.Div([dcc.Input(value=1, id='x', type='number'),
dcc.Input(value=1, id='power', type='number'),
html.Div(id='result'), dcc.Graph(id='graph'),
dcc.Store(id='z')])
@app.callback(Output('z', 'data'), Input('x', 'value'), Input('power', 'value'))
def calculate_z(x, y):
return x ** y if (x and y) else None
@app.callback(Output('result', 'children'), Input('x', 'value'), Input('power', 'value'), Input('z', 'data'))
def display_result(x, y, z):
return f"{x}^{y} is {z}"
@app.callback(Output('graph', 'figure'), Input('x', 'value'), Input('power', 'value'), Input('z', 'data'))
def plot_result(x, y, z):
return go.Figure([go.Bar(x=['x', 'y', 'x**y'], y=[x, y, z])])
if __name__ == "__main__":
app.run_server()
Looking at the code, there is a lot of boilerplate code. I have written a small transpiler of what a more Shiny-like syntax could look like in Dash. Here is the code for the same app,
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
from dash_extensions.enrich import DashProxy
app = DashProxy()
app.layout = html.Div([dcc.Input(value=1, id='x', type='number'),
dcc.Input(value=1, id='power', type='number'),
html.Div(id='result'), dcc.Graph(id='graph')])
app.reactive('z', lambda x='x', y='power': x ** y if (x and y) else None) # reactive variable, no need for Store
app.output('result', lambda x='x', y='power', z='z': f"{x}^{y} is {z}") # default prop for input is value, default for output is children
app.output(('graph', 'figure'), lambda x='x', y='power', z='z': go.Figure([go.Bar(x=['x', 'y', 'x**y'], y=[x, y, z])]))
if __name__ == "__main__":
app.run_server(port=8056)
The Shiny-like syntax is about half (!) the length of the original Dash code. I find the Shiny-like syntax just as readable, maybe even more so. Especially for beginners, as you don’t need to understand decorators, imports from dash dependencies (i always get confused about the Input
import) and deal with Store
objects. What do you think, @chriddyp ? If anyone else have been playing with similar projects and/or have comments, please chip in