Py.Cafe

marie-anne/

2025-figurefriday-w16-alternative

Infographic percentage of households with a dog, cat, bird or fish by continent

DocsPricing
  • assets/
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import dash
from dash import html, dcc
import pandas as pd
import plotly.graph_objects as go

import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template

# Data prep
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],
}
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)
means = df.groupby('Continent')[animals].mean().reset_index()
means = means.sort_values('Continent').reset_index(drop=True)
continent_names = means['Continent'].tolist()

# Generate the Countries per continent paragraph
continent_counts = df.groupby('Continent')['Country'].nunique().to_dict()
# To keep the same order as in your plot:
ordered_counts = [f"{cont} ({continent_counts.get(cont, 0)})" for cont in continent_names]
continent_text = "This visual is based on the data of 22 countries divided as follows: " + ", ".join(ordered_counts)

colors = ["#ad3b02", "#ce4602"]

fig = go.Figure()

# Draw alternating background rectangles per continent
for i, continent in enumerate(continent_names):
    fig.add_shape(
        type="rect",
        x0=i - 0.49, x1=i + 0.49,
        y0=0, y1=100,
        xref="x", yref="y",
        fillcolor=colors[i % 2],
        opacity=0.42,
        layer="below",
        line_width=0,
    )

# For each continent, put all animal icons (and labels!) at the same x
icon_size = 5  # percent units, since y is percentage
for i, row in means.iterrows():
    for j, animal in enumerate(animals):
        y_val = row[animal]
        x_val = i
        # Animal icon
        fig.add_layout_image(dict(
            source=f"assets/images/{animal_filenames[animal]}.png",
            x=x_val,
            y=y_val,
            xref="x", yref="y",
            xanchor="center", yanchor="middle",
            sizex=0.27, sizey=icon_size,
            sizing='contain',
            layer="above",
            opacity=.4
        ))
        # LEFT OF IMAGE: Text annotation
        text_x_offset = 0.18  # tweak for padding
        fig.add_annotation(
            x=x_val - text_x_offset,
            y=y_val,
            text=f'<b>{int(round(y_val))}%</b>',
            showarrow=False,
            font=dict(size=16, color="white", family='"Indie Flower", cursive'),
            xanchor="right",
            yanchor="middle"
        )


# X & Y axis styling
fig.update_xaxes(
    showgrid=False, zeroline=False,
    linecolor="#202020",  # Make axis line the background color (hidden)
    showline=False,
    tickvals=list(range(len(continent_names))),
    ticktext=[f"<b>{c}</b>" for c in continent_names],
    tickfont=dict(size=26, color="white", family='"Indie Flower", cursive'),
    range=[-0.7, len(continent_names) - 0.3]
)
fig.update_yaxes(
    showgrid=False, zeroline=False,
    color='rgba(0,0,0,0)',
    range=[0, 70],   # y-axis from 0% to 80%
    tickvals=[0, 20, 40, 60, 80],
    ticktext=["0%", "20%", "40%", "60%", "80%"],
    showticklabels=False
)

fig.update_layout(
    plot_bgcolor="#202020",
    paper_bgcolor="#202020",
    margin=dict(t=60, l=60, r=60, b=60),
    xaxis_title=None, yaxis_title=None,

    height=700,
    #width = 1000
)

# Dash app layout
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.CYBORG])
app.layout = dbc.Container ([ html.Div([
    html.H1("Average percentage of households who own a dog, cat, fish or bird by continent",
            style={ 'color': 'white',
                   "textAlign": "center",
                   'fontFamily': '"Indie Flower", cursive'}),
    dcc.Graph(figure=fig, config={"displayModeBar": False}),
    html.P(continent_text, 
        style={
            "color": "white", 
            "fontFamily": '"Indie Flower", cursive',
            "textAlign": "center",
            "fontSize": "1.25em",
            "marginTop": "16px"
        }),
], style={"background": "#202020", "minHeight": "100vh", "padding": "20px"})
    ])

if __name__ == "__main__":
    app.run(debug=True)