import dash
from dash import dcc, html
import plotly.graph_objects as go
import pandas as pd
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
# Data
data = {
'Country': ['USA', 'Argentina', 'UK', 'Australia', 'Turkey', 'Belgium', 'Sweden', 'Brazil', 'Spain', 'Canada',
'South Korea', 'Russia', 'Czech Republic', 'China', 'Poland', 'France', 'Netherlands', 'Germany',
'Mexico', 'Hong Kong', 'Japan', 'Italy'],
'Dog': [50, 66, 27, 39, 12, 29, 22, 58, 37, 33, 20, 29, 38, 25, 45, 29, 25, 21, 64, 14, 17, 39],
'Cat': [39, 32, 27, 29, 15, 33, 28, 28, 23, 35, 6, 57, 26, 10, 32, 41, 26, 29, 24, 10, 14, 34],
'Fish': [11, 8, 9, 13, 16, 15, 6, 7, 9, 9, 7, 11, 14, 17, 12, 12, 11, 9, 10, 14, 9, 11],
'Bird': [6, 7, 4, 10, 20, 8, 3, 11, 11, 4, 1, 9, 8, 5, 7, 5, 7, 6, 10, 5, 2, 8],
'Countrycode' : [
"us", "ar", "gb", "au", "tr", "be", "se", "br", "es", "ca",
"kr", "ru", "cz", "cn", "pl", "fr", "nl", "de", "mx", "hk", "jp", "it"
],
# Add this at the top of your script
}
country_to_continent = {
"USA": "North America",
"Argentina": "South America",
"UK": "Europe",
"Australia": "Oceania",
"Turkey": "Europe",
"Belgium": "Europe",
"Sweden": "Europe",
"Brazil": "South America",
"Spain": "Europe",
"Canada": "North America",
"South Korea": "Asia",
"Russia": "Europe",
"Czech Republic": "Europe",
"China": "Asia",
"Poland": "Europe",
"France": "Europe",
"Netherlands": "Europe",
"Germany": "Europe",
"Mexico": "North America",
"Hong Kong": "Asia",
"Japan": "Asia",
"Italy": "Europe"
}
animals = ['Dog', 'Cat', 'Fish', 'Bird']
animal_filenames = {'Dog': 'dog', 'Cat': 'cat', 'Fish': 'fish', 'Bird': 'bird'}
df = pd.DataFrame(data)
df['Continent'] = df['Country'].map(country_to_continent)
df = df.sort_values(['Continent', 'Country'])
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.CYBORG])
app.layout = dbc.Container ([
html.Div([
html.H1("Percentage of households who own a dog, cat, fish or bird", style={"marginTop":'2rem', "marginBottom":'2rem'}),
dcc.Graph(id='flag-animal-plot')
])
], style={'maxWidth':'90%'})
@app.callback(
dash.dependencies.Output('flag-animal-plot', 'figure'),
[dash.dependencies.Input('flag-animal-plot', 'id')]
)
def plot_with_img_markers(_):
fig = go.Figure()
# Add invisible/transparent scatter traces (for tooltips and legend)
for i, animal in enumerate(animals):
fig.add_trace(
go.Scatter(
x=df['Country'],
y=df[animal],
mode='markers',
name=animal,
marker=dict(
size=48, color='rgba(255,255,255,0)', line=dict(width=0)
),
hovertemplate=f"{animal}: %{{y}}%<br>Country: %{{x}}<extra></extra>"
)
)
# Add animal PNG images (icon at each marker)
for idx, row in df.iterrows():
for animal in animals:
fig.add_layout_image(
dict(
source=f"assets/images/{animal_filenames[animal]}.png",
x=row['Country'],
y=row[animal],
xref="x",
yref="y",
xanchor="center",
yanchor="middle",
sizex=.5, # Category width tuning (you may tweak)
sizey=5, # Y scale size tuning (you may tweak!)
sizing="contain",
opacity=.6,
layer="above"
)
)
# Hide x-tick labels, show country flags
fig.update_xaxes(showticklabels=True)
for idx, row in df.iterrows():
fig.add_layout_image(
dict(
source=f"assets/images/{row['Countrycode']}.png",
x=row['Country'],
y=-0.08, # Below the axis (tune as needed)
xref="x",
yref="paper",
xanchor="center",
yanchor="top",
sizex=0.45,
sizey=0.075,
sizing="contain",
opacity=1,
layer="above"
)
)
fig.update_layout(
plot_bgcolor='#742802',
paper_bgcolor='#742802',
height = 800,
showlegend=False,
font=dict(color='white'),
yaxis=dict(
gridcolor='#efefef',
gridwidth=1.2,
griddash='dot',
tickvals=[0, 20, 40, 60, 80, 100], # Adjust as needed
tickfont=dict(color='white'),
showgrid=True
),
xaxis=dict(
showgrid=False,
tickfont=dict(color='white'),
color='white'
),
title=dict(font=dict(color='white')),
legend=dict(
font=dict(color='white'),
orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1
),
margin=dict(l=40, r=20, t=60, b=100)
)
# Ensure only horizontal grid at 20, 40, 60, 80. Remove grid elsewhere:
fig.update_yaxes(
showgrid=True,
gridcolor='#efefef',
griddash='dot',
gridwidth=1.2,
tickvals=[0, 20, 40, 60, 80, 100],
ticktext=["0%", "20%", "40%", "60%", "80%", "100%"],
color='white',
zeroline=False, # Ensure THICK line is gone
layer='below traces'
)
fig.update_yaxes(range=[0, max(df[animals].max())+10])
fig.update_xaxes(
tickfont=dict(color="white"),
tickangle=0, # or your preferred angle
ticklabelposition="outside bottom",
tickson="boundaries",
ticklabelstandoff=20 # Increase this to push labels down; default is about 5
)
#continent squares and annotations
rect_colors = [ "#ce4602", "#ad3b02"]
countries = list(df['Country'])
continents = df['Continent'].unique()
shapes = []
annotations = []
padding = 0.4 # Adjust for icon overhang
for i, continent in enumerate(continents):
continent_rows = df[df['Continent'] == continent]
if len(continent_rows) == 0:
continue
countries_in_cont = list(continent_rows['Country'])
idx0 = countries.index(countries_in_cont[0])
idx1 = countries.index(countries_in_cont[-1])
# Alternate colors
rect_color = rect_colors[i % len(rect_colors)]
shapes.append(
dict(
type="rect",
xref="x",
yref="paper",
x0=idx0 - padding,
x1=idx1 + padding,
y0=0,
y1=1,
fillcolor=rect_color,
opacity=0.5,
layer="below",
line_width=0,
)
)
mid_idx = (idx0 + idx1) / 2
annotations.append(
dict(
x=mid_idx,
y=1.08,
xref="x",
yref="paper",
text=f"<b>{continent}</b>",
showarrow=False,
font=dict(size=22, color='white', family='"Indie Flower", cursive'),
)
)
fig.update_layout(shapes=shapes, annotations=annotations)
return fig
if __name__ == '__main__':
app.run_server(debug=True)