231 lines
7.4 KiB
Python
231 lines
7.4 KiB
Python
|
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='<br>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()])
|