123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import faicons as fa
- import plotly.express as px
- # Load data and compute static values
- from shared import app_dir, tips
- from shinywidgets import render_plotly
- from shiny import reactive, render
- from shiny.express import input, ui
- bill_rng = (min(tips.total_bill), max(tips.total_bill))
- # Add page title and sidebar
- ui.page_opts(title="Restaurant tipping", fillable=True)
- with ui.sidebar(open="desktop"):
- ui.input_slider(
- "total_bill",
- "Bill amount",
- min=bill_rng[0],
- max=bill_rng[1],
- value=bill_rng,
- pre="$",
- )
- ui.input_checkbox_group(
- "time",
- "Food service",
- ["Lunch", "Dinner"],
- selected=["Lunch", "Dinner"],
- inline=True,
- )
- ui.input_action_button("reset", "Reset filter")
- # Add main content
- ICONS = {
- "user": fa.icon_svg("user", "regular"),
- "wallet": fa.icon_svg("wallet"),
- "currency-dollar": fa.icon_svg("dollar-sign"),
- "ellipsis": fa.icon_svg("ellipsis"),
- }
- with ui.layout_columns(fill=False):
- with ui.value_box(showcase=ICONS["user"]):
- "Total tippers"
- @render.express
- def total_tippers():
- tips_data().shape[0]
- with ui.value_box(showcase=ICONS["wallet"]):
- "Average tip"
- @render.express
- def average_tip():
- d = tips_data()
- if d.shape[0] > 0:
- perc = d.tip / d.total_bill
- f"{perc.mean():.1%}"
- with ui.value_box(showcase=ICONS["currency-dollar"]):
- "Average bill"
- @render.express
- def average_bill():
- d = tips_data()
- if d.shape[0] > 0:
- bill = d.total_bill.mean()
- f"${bill:.2f}"
- with ui.layout_columns(col_widths=[6, 6, 12]):
- with ui.card(full_screen=True):
- ui.card_header("Tips data")
- @render.data_frame
- def table():
- return render.DataGrid(tips_data())
- with ui.card(full_screen=True):
- with ui.card_header(class_="d-flex justify-content-between align-items-center"):
- "Total bill vs tip"
- with ui.popover(title="Add a color variable", placement="top"):
- ICONS["ellipsis"]
- ui.input_radio_buttons(
- "scatter_color",
- None,
- ["none", "sex", "smoker", "day", "time"],
- inline=True,
- )
- @render_plotly
- def scatterplot():
- color = input.scatter_color()
- return px.scatter(
- tips_data(),
- x="total_bill",
- y="tip",
- color=None if color == "none" else color,
- trendline="lowess",
- )
- with ui.card(full_screen=True):
- with ui.card_header(class_="d-flex justify-content-between align-items-center"):
- "Tip percentages"
- with ui.popover(title="Add a color variable"):
- ICONS["ellipsis"]
- ui.input_radio_buttons(
- "tip_perc_y",
- "Split by:",
- ["sex", "smoker", "day", "time"],
- selected="day",
- inline=True,
- )
- @render_plotly
- def tip_perc():
- from ridgeplot import ridgeplot
- dat = tips_data()
- dat["percent"] = dat.tip / dat.total_bill
- yvar = input.tip_perc_y()
- uvals = dat[yvar].unique()
- samples = [[dat.percent[dat[yvar] == val]] for val in uvals]
- plt = ridgeplot(
- samples=samples,
- labels=uvals,
- bandwidth=0.01,
- colorscale="viridis",
- colormode="row-index",
- )
- plt.update_layout(
- legend=dict(
- orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5
- )
- )
- return plt
- ui.include_css(app_dir / "styles.css")
- # --------------------------------------------------------
- # Reactive calculations and effects
- # --------------------------------------------------------
- @reactive.calc
- def tips_data():
- bill = input.total_bill()
- idx1 = tips.total_bill.between(bill[0], bill[1])
- idx2 = tips.time.isin(input.time())
- return tips[idx1 & idx2]
- @reactive.effect
- @reactive.event(input.reset)
- def _():
- ui.update_slider("total_bill", value=bill_rng)
- ui.update_checkbox_group("time", selected=["Lunch", "Dinner"])
|