import faicons as fa import plotly.express as px from shared import app_dir, billionaires from shinywidgets import render_plotly import pandas as pd from shiny import reactive, render from shiny.express import input, ui worth_rng = (min(billionaires.finalWorth), max(billionaires.finalWorth)) ui.page_opts(title="Billionaires dataset", fillable=False) with ui.sidebar(open="desktop"): ui.input_slider( "net_worth", "Net worth range in billions", min=worth_rng[0], max=worth_rng[1], value=worth_rng, pre="$", post="B", ) ui.input_selectize( "categories", "Busniess categories", billionaires['category'].unique().tolist(), selected="Technology", ) ui.input_action_button("reset", "Reset filter") # Add main content ICONS = { "user": fa.icon_svg("user", "regular"), "currency-dollar": fa.icon_svg("dollar-sign"), "ellipsis": fa.icon_svg("ellipsis"), "age": fa.icon_svg("hourglass-half") } with ui.layout_columns(fill=False): with ui.value_box(showcase=ICONS["user"]): "Total billionaires" @render.express def total_billionaires(): billionaires_data().shape[0] with ui.value_box(showcase=ICONS["currency-dollar"]): "Average net worth" @render.express def average_net_worth(): d = billionaires_data() if d.shape[0] > 0: avg_worth = d['finalWorth'].mean() f"${avg_worth:,.2f}B" with ui.value_box(showcase=ICONS["age"]): "Average age" @render.express def average_bill(): d = billionaires_data() if d.shape[0] > 0: avg_age = d['age'].mean() f"{avg_age:.1f}" with ui.layout_columns(col_widths=[6, 6, 12]): with ui.card(full_screen=True): ui.card_header("Billionaires data") @render.data_frame def table(): return render.DataGrid(billionaires_data()) with ui.card(full_screen=True): ui.card_header("Billionaires by Country") @render_plotly def billionaires_map(): country_counts = billionaires_data()['country'].value_counts() country_data = pd.DataFrame({ 'country': country_counts.index, 'billionaires': country_counts.values }) fig = px.choropleth( country_data, locations="country", locationmode='country names', color="billionaires", hover_name="country", color_continuous_scale="Plasma", projection="orthographic", labels={'billionaires': 'Number of Billionaires'}, ) fig.update_layout( geo=dict( showframe=False, projection_type="orthographic", showcoastlines=True, showocean=True, showland=True, showcountries=True, landcolor='rgb(243, 243, 243)', countrycolor='rgb(204, 204, 204)' ), showlegend=False, ) return fig with ui.card(full_screen=True): with ui.card_header(class_="d-flex justify-content-between align-items-center"): "Top Billionaires by Net Worth" with ui.popover(title=None): ICONS["ellipsis"] ui.input_radio_buttons( "gender_split", "Choose gender", ["M", "F"], selected="M", inline=True, ) @render_plotly def top_billionaires_plot(): gender_filter = input.gender_split() filtered_data = billionaires_data()[billionaires_data()["gender"] == gender_filter] top_10 = filtered_data.nlargest(10, 'finalWorth') fig = px.bar( top_10, x='finalWorth', y='personName', color='personName', orientation='h', labels={'finalWorth': 'Net Worth (Billion $)', 'personName': 'Name'}, facet_row_spacing=0.1, ) fig.update_layout( xaxis_title='Net Worth (Billion $)', yaxis_title='Name', showlegend=False, ) return fig with ui.layout_columns(col_widths=[6, 6]): with ui.card(full_screen=True): ui.card_header("Number of Billionaires and Total Tax Rate by Country'") @render_plotly def tabl(): billionaires_per_country = billionaires_data()['country'].value_counts().reset_index() billionaires_per_country.columns = ['country', 'number_of_billionaires'] tax_rate_df = billionaires_data()[['country', 'total_tax_rate_country']].drop_duplicates() merged_df = pd.merge(billionaires_per_country, tax_rate_df, on='country') fig = px.scatter(merged_df, x='number_of_billionaires', y='total_tax_rate_country', hover_name='country', labels={ 'number_of_billionaires': 'Number of Billionaires', 'total_tax_rate_country': 'Total Tax Rate (%)' }, size='number_of_billionaires', size_max=60, color='total_tax_rate_country', color_continuous_scale=px.colors.sequential.Plasma) fig.update_layout( title_font_size=20, xaxis_title_font_size=16, yaxis_title_font_size=16, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(0, 0, 0, 0)', xaxis=dict(showgrid=True, gridcolor='lightgrey', type='log'), yaxis=dict(showgrid=True, gridcolor='lightgrey') ) return fig with ui.card(full_screen=True): ui.card_header("Billionaires by City") @render_plotly def billionaire(): top_birth_cities = billionaires_data()['city'].value_counts().head(20) fig = px.bar( top_birth_cities, x=top_birth_cities.index, y=top_birth_cities.values, orientation='v', labels={'index': 'City', 'values': 'Number of Billionaires'}, color=top_birth_cities.values, color_continuous_scale='Viridis', ) fig.update_traces( customdata=top_birth_cities.values, hovertemplate='
Number of Billionaires: %{customdata}' ) fig.update_layout( xaxis_title='Number of Billionaires', yaxis_title='City', showlegend=False, ) return fig ui.include_css(app_dir / "styles.css") @reactive.calc def billionaires_data(): net_worth = input.net_worth() idx1 = billionaires['finalWorth'].between(net_worth[0], net_worth[1]) idx2 = billionaires['category'].isin([input.categories()]) return billionaires[idx1 & idx2] @reactive.effect @reactive.event(input.reset) def _(): ui.update_slider("net_worth", value=worth_rng) ui.update_checkbox_group("categories", selected=[billionaires['category'].unique().tolist()])