Spaces:
Sleeping
Sleeping
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" | |
def total_tippers(): | |
tips_data().shape[0] | |
with ui.value_box(showcase=ICONS["wallet"]): | |
"Average tip" | |
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" | |
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") | |
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, | |
) | |
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, | |
) | |
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 | |
# -------------------------------------------------------- | |
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] | |
def _(): | |
ui.update_slider("total_bill", value=bill_rng) | |
ui.update_checkbox_group("time", selected=["Lunch", "Dinner"]) | |