ndrezner commited on
Commit
5092dd6
·
0 Parent(s):

Initial commit

Browse files
Files changed (3) hide show
  1. app.py +332 -0
  2. assets/styles.css +107 -0
  3. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dash
2
+ import dash_mantine_components as dmc
3
+ import plotly.express as px
4
+ from dash import Input, Output, callback, dcc, html
5
+ from dash_iconify import DashIconify
6
+
7
+
8
+ app = dash.Dash(__name__)
9
+ server = app.server
10
+
11
+ df = px.data.gapminder()
12
+
13
+
14
+ def create_scatter_plot(selected_year, selected_continent=None):
15
+ filtered_df = df[df["year"] == selected_year]
16
+
17
+ if selected_continent and selected_continent != "All":
18
+ filtered_df = filtered_df[filtered_df["continent"] == selected_continent]
19
+
20
+ fig = px.scatter(
21
+ filtered_df,
22
+ x="gdpPercap",
23
+ y="lifeExp",
24
+ size="pop",
25
+ color="continent",
26
+ hover_name="country",
27
+ log_x=True,
28
+ size_max=60,
29
+ title=f"Life Expectancy vs GDP per Capita ({selected_year})",
30
+ )
31
+
32
+ fig.update_layout(
33
+ template="plotly_dark",
34
+ paper_bgcolor="rgba(0,0,0,0)",
35
+ plot_bgcolor="rgba(0,0,0,0)",
36
+ )
37
+
38
+ return fig
39
+
40
+
41
+ def create_line_chart(selected_country):
42
+ country_data = df[df["country"] == selected_country]
43
+ fig = px.line(
44
+ country_data,
45
+ x="year",
46
+ y="lifeExp",
47
+ title=f"{selected_country} - Life Expectancy",
48
+ )
49
+ fig.update_layout(
50
+ template="plotly_dark",
51
+ paper_bgcolor="rgba(0,0,0,0)",
52
+ plot_bgcolor="rgba(0,0,0,0)",
53
+ )
54
+ return fig
55
+
56
+
57
+ def create_bar_chart(selected_year):
58
+ year_data = df[df["year"] == selected_year]
59
+ continent_stats = year_data.groupby("continent")["lifeExp"].mean().reset_index()
60
+ fig = px.bar(
61
+ continent_stats,
62
+ x="continent",
63
+ y="lifeExp",
64
+ color="continent",
65
+ title=f"Average Life Expectancy by Continent ({selected_year})",
66
+ )
67
+ fig.update_layout(
68
+ template="plotly_dark",
69
+ paper_bgcolor="rgba(0,0,0,0)",
70
+ plot_bgcolor="rgba(0,0,0,0)",
71
+ showlegend=False,
72
+ )
73
+ return fig
74
+
75
+
76
+ def create_datacard(title, value, icon, color):
77
+ return dmc.Card(
78
+ [
79
+ dmc.Group(
80
+ [
81
+ DashIconify(icon=icon, width=30, color=color),
82
+ html.Div(
83
+ [
84
+ dmc.Text(value, size="xl", fw=700, c="white"),
85
+ dmc.Text(title, size="sm", c="dimmed"),
86
+ ]
87
+ ),
88
+ ],
89
+ align="center",
90
+ gap="md",
91
+ )
92
+ ],
93
+ p="md",
94
+ className="datacard",
95
+ )
96
+
97
+
98
+ app.layout = dmc.MantineProvider(
99
+ [
100
+ html.Link(
101
+ href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap",
102
+ rel="stylesheet",
103
+ ),
104
+ dmc.Group(
105
+ [
106
+ DashIconify(icon="twemoji:globe-with-meridians", width=45),
107
+ dmc.Text(
108
+ "Gapminder World Data Explorer", ml=10, size="xl", fw=900, c="white"
109
+ ),
110
+ ],
111
+ align="center",
112
+ className="header",
113
+ mb="md",
114
+ ),
115
+ dmc.Grid(
116
+ [
117
+ dmc.GridCol(
118
+ [
119
+ dmc.Stack(
120
+ [
121
+ dmc.Card(
122
+ [
123
+ dmc.Text("Controls", size="lg", mb="md"),
124
+ dmc.Stack(
125
+ [
126
+ html.Div(
127
+ [
128
+ dmc.Text(
129
+ "Year:", size="sm", mb=5
130
+ ),
131
+ dmc.Slider(
132
+ id="year-slider",
133
+ min=1952,
134
+ max=2007,
135
+ step=5,
136
+ value=2007,
137
+ marks=[
138
+ {
139
+ "value": year,
140
+ "label": str(year),
141
+ }
142
+ for year in [
143
+ 1952,
144
+ 1967,
145
+ 1982,
146
+ 1997,
147
+ 2007,
148
+ ]
149
+ ],
150
+ ),
151
+ ]
152
+ ),
153
+ html.Div(
154
+ [
155
+ dmc.Text(
156
+ "Continent Filter:",
157
+ size="sm",
158
+ mb=5,
159
+ ),
160
+ dmc.Select(
161
+ id="continent-dropdown",
162
+ data=[
163
+ {
164
+ "value": "All",
165
+ "label": "All Continents",
166
+ }
167
+ ]
168
+ + [
169
+ {
170
+ "value": cont,
171
+ "label": cont,
172
+ }
173
+ for cont in sorted(
174
+ df[
175
+ "continent"
176
+ ].unique()
177
+ )
178
+ ],
179
+ value="All",
180
+ ),
181
+ ]
182
+ ),
183
+ html.Div(
184
+ [
185
+ dmc.Text(
186
+ "Select Country:",
187
+ size="sm",
188
+ mb=5,
189
+ ),
190
+ dmc.Select(
191
+ id="country-dropdown",
192
+ data=[
193
+ {
194
+ "value": country,
195
+ "label": country,
196
+ }
197
+ for country in sorted(
198
+ df[
199
+ "country"
200
+ ].unique()
201
+ )
202
+ ],
203
+ value="United States",
204
+ searchable=True,
205
+ ),
206
+ ]
207
+ ),
208
+ ],
209
+ gap="lg",
210
+ ),
211
+ ],
212
+ p="md",
213
+ className="control-card",
214
+ )
215
+ ]
216
+ )
217
+ ],
218
+ span=3,
219
+ ),
220
+ dmc.GridCol(
221
+ [
222
+ dmc.Stack(
223
+ [
224
+ html.Div(id="stats-cards"),
225
+ dmc.Card(
226
+ [dcc.Graph(id="scatter-plot")],
227
+ p="sm",
228
+ className="chart-card",
229
+ ),
230
+ ],
231
+ gap="md",
232
+ )
233
+ ],
234
+ span=9,
235
+ ),
236
+ ],
237
+ gutter="md",
238
+ ),
239
+ dmc.Grid(
240
+ [
241
+ dmc.GridCol(
242
+ [
243
+ dmc.Card(
244
+ [dcc.Graph(id="line-chart")], p="sm", className="chart-card"
245
+ )
246
+ ],
247
+ span=6,
248
+ ),
249
+ dmc.GridCol(
250
+ [
251
+ dmc.Card(
252
+ [dcc.Graph(id="bar-chart")], p="sm", className="chart-card"
253
+ )
254
+ ],
255
+ span=6,
256
+ ),
257
+ ],
258
+ gutter="md",
259
+ mt="md",
260
+ ),
261
+ ],
262
+ forceColorScheme="dark",
263
+ theme={"colorScheme": "dark"},
264
+ )
265
+
266
+
267
+ @callback(
268
+ Output("scatter-plot", "figure"),
269
+ [Input("year-slider", "value"), Input("continent-dropdown", "value")],
270
+ )
271
+ def update_scatter_plot(selected_year, selected_continent):
272
+ return create_scatter_plot(selected_year, selected_continent)
273
+
274
+
275
+ @callback(Output("line-chart", "figure"), Input("country-dropdown", "value"))
276
+ def update_line_chart(selected_country):
277
+ return create_line_chart(selected_country)
278
+
279
+
280
+ @callback(Output("bar-chart", "figure"), Input("year-slider", "value"))
281
+ def update_bar_chart(selected_year):
282
+ return create_bar_chart(selected_year)
283
+
284
+
285
+ @callback(Output("stats-cards", "children"), Input("year-slider", "value"))
286
+ def update_stats(selected_year):
287
+ year_data = df[df["year"] == selected_year]
288
+
289
+ avg_life_exp = round(year_data["lifeExp"].mean(), 1)
290
+ total_pop = year_data["pop"].sum()
291
+ num_countries = len(year_data)
292
+ avg_gdp = round(year_data["gdpPercap"].mean(), 0)
293
+
294
+ return dmc.Grid(
295
+ [
296
+ dmc.GridCol(
297
+ create_datacard(
298
+ "Life Expectancy",
299
+ f"{avg_life_exp} years",
300
+ "mdi:heart-pulse",
301
+ "#ff6b35",
302
+ ),
303
+ span=3,
304
+ ),
305
+ dmc.GridCol(
306
+ create_datacard(
307
+ "Population",
308
+ f"{total_pop / 1e9:.1f}B",
309
+ "mdi:account-group",
310
+ "#1f77b4",
311
+ ),
312
+ span=3,
313
+ ),
314
+ dmc.GridCol(
315
+ create_datacard(
316
+ "Countries", str(num_countries), "mdi:earth", "#2ca02c"
317
+ ),
318
+ span=3,
319
+ ),
320
+ dmc.GridCol(
321
+ create_datacard(
322
+ "GDP per Capita", f"${avg_gdp:,.0f}", "mdi:currency-usd", "#d62728"
323
+ ),
324
+ span=3,
325
+ ),
326
+ ],
327
+ gutter="sm",
328
+ )
329
+
330
+
331
+ if __name__ == "__main__":
332
+ app.run(debug=True, port=8050)
assets/styles.css ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: 'Outfit', sans-serif;
3
+ background: linear-gradient(135deg, #1e1e2e 0%, #2a2a3e 100%);
4
+ margin: 0;
5
+ padding: 20px;
6
+ min-height: 100vh;
7
+ }
8
+
9
+ .header {
10
+ background: linear-gradient(135deg, #ff6b35, #d43425);
11
+ padding: 20px 30px;
12
+ border-radius: 15px;
13
+ box-shadow: 0 8px 25px rgba(255, 107, 53, 0.3);
14
+ margin-bottom: 20px;
15
+ }
16
+
17
+ .control-card {
18
+ background: rgba(15, 15, 20, 0.9);
19
+ border: 1px solid rgba(255, 255, 255, 0.1);
20
+ backdrop-filter: blur(10px);
21
+ border-radius: 15px;
22
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
23
+ }
24
+
25
+ .chart-card {
26
+ background: rgba(15, 15, 20, 0.9);
27
+ border: 1px solid rgba(255, 255, 255, 0.1);
28
+ backdrop-filter: blur(10px);
29
+ border-radius: 15px;
30
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
31
+ }
32
+
33
+ .datacard {
34
+ background: linear-gradient(135deg, rgba(255, 107, 53, 0.1), rgba(212, 52, 37, 0.1));
35
+ border: 1px solid rgba(255, 107, 53, 0.3);
36
+ border-radius: 12px;
37
+ transition: all 0.3s ease;
38
+ backdrop-filter: blur(10px);
39
+ }
40
+
41
+ .datacard:hover {
42
+ transform: translateY(-2px);
43
+ box-shadow: 0 12px 30px rgba(255, 107, 53, 0.2);
44
+ }
45
+
46
+ .year-slider .mantine-Slider-track {
47
+ background: rgba(255, 255, 255, 0.2);
48
+ }
49
+
50
+ .year-slider .mantine-Slider-bar {
51
+ background: linear-gradient(90deg, #ff6b35, #d43425);
52
+ }
53
+
54
+ .year-slider .mantine-Slider-thumb {
55
+ background: #ff6b35;
56
+ border: 2px solid white;
57
+ }
58
+
59
+ .continent-select .mantine-Select-input,
60
+ .country-select .mantine-Select-input {
61
+ background: rgba(255, 255, 255, 0.1);
62
+ border: 1px solid rgba(255, 255, 255, 0.2);
63
+ color: white;
64
+ }
65
+
66
+ .continent-select .mantine-Select-input:focus,
67
+ .country-select .mantine-Select-input:focus {
68
+ border-color: #ff6b35;
69
+ box-shadow: 0 0 10px rgba(255, 107, 53, 0.3);
70
+ }
71
+
72
+ /* Custom scrollbar for dropdowns */
73
+ .mantine-Select-dropdown {
74
+ background: rgba(15, 15, 20, 0.95);
75
+ border: 1px solid rgba(255, 255, 255, 0.1);
76
+ backdrop-filter: blur(10px);
77
+ }
78
+
79
+ .mantine-Select-item {
80
+ color: white;
81
+ }
82
+
83
+ .mantine-Select-item:hover {
84
+ background: rgba(255, 107, 53, 0.2);
85
+ }
86
+
87
+ /* Graph styling adjustments */
88
+ .js-plotly-plot {
89
+ border-radius: 10px;
90
+ overflow: hidden;
91
+ }
92
+
93
+ /* Responsive design */
94
+ @media (max-width: 768px) {
95
+ body {
96
+ padding: 10px;
97
+ }
98
+
99
+ .header {
100
+ padding: 15px 20px;
101
+ }
102
+
103
+ .control-card,
104
+ .chart-card {
105
+ margin: 10px 0;
106
+ }
107
+ }
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ dash
2
+ dash-mantine-components
3
+ dash-iconify
4
+ plotly
5
+ pandas