import dash
from dash import dcc, html, Input, Output, callback
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
import numpy as np
# Load real data from file
df = pd.read_csv('management_unemployment.csv') # Replace with your file
df = df.rename(columns={
'Female share in management': 'Female_management',
'Female share in employment': 'Female_employment',
'Female share in the working-age population': 'Female_working_age',
'Africa_unemployment_%': 'Africa_unemployment',
'Americas_unemployment_%': 'Americas_unemployment',
'Arab_States_unemployment_%': 'Arab_States_unemployment',
'Asia_Pacific_unemployment_%': 'Asia_Pacific_unemployment',
'Europe_CentralAsia_unemployment_%': 'Europe_CentralAsia_unemployment',
'World_unemployement_%': 'World_unemployment'
})
# Clean data
df = df.dropna(subset=['Year']).sort_values('Year')
# Definition of historical eras
historical_eras = {
'90s: The Awakening': (1991, 1999),
'2000s: The Digital Era': (2000, 2009),
'2010s: The New Economy': (2010, 2019),
'2020s: Transformation': (2020, 2021) # Adjusted to 2021, as data ends in 2021 in stories
}
# Expanded era stories
era_stories = {
'90s: The Awakening': {
'description': 'The fall of the Berlin Wall marks the beginning of a new era. Europe struggles with transition while Asia emerges as an economic power.',
'key_events': ['1991: End of USSR', '1992: Maastricht Treaty', '1995: Commercial Internet', '1997: Asian Crisis', '1999: Euro Introduced'],
'female_trend': 'Slow but steady progress in managerial participation',
'unemployment_trend': 'Severe European crisis, Asian stability'
},
'2000s: The Digital Era': {
'description': 'The digital revolution transforms work. The 2008 crisis marks a before and after in global employment.',
'key_events': ['2001: Dot-com Bubble Burst', '2003: Social Media Emerges', '2007: iPhone Launched', '2008: Financial Crisis', '2009: Recovery Begins'],
'female_trend': 'Acceleration in female participation, especially in technology',
'unemployment_trend': 'Stability until 2008, then severe global crisis'
},
'2010s: The New Economy': {
'description': 'The gig economy, remote work, and gender awareness transform the global labor landscape.',
'key_events': ['2010: Economic Recovery', '2015: UN SDGs', '2016: Gig Economy Surge', '2018: #MeToo Movement', '2019: Climate Change Awareness'],
'female_trend': 'Silent revolution: more women in leadership',
'unemployment_trend': 'Sustained recovery, regional disparities persist'
},
'2020s: Transformation': {
'description': 'The pandemic accelerates changes that would have taken decades. Remote work and gender equality become priorities.',
'key_events': ['2020: COVID-19 Pandemic', '2021: Vaccine Rollout', '2021: Hybrid Work Norm'],
'female_trend': 'Pandemic: initial setback, then digital acceleration',
'unemployment_trend': 'Initial shock, uneven recovery by region'
}
}
# Detailed yearly stories (expanded)
stories = {
# 90s
1991: {'title': '1991: The Dawn of a New Era', 'context': 'The USSR collapses, Germany reunifies. The labor world is in transition. Only 24.3% of managers are women.', 'insight': 'Europe begins its post-communist crisis. Asia remains stable with 3.5% unemployment.'},
1992: {'title': '1992: First Winds of Change', 'context': 'Maastricht Treaty. Barcelona 92 Olympics. Global unemployment rises but hope remains.', 'insight': 'Europe suffers: 7.5% unemployment. Women advance millimeter by millimeter in leadership.'},
1993: {'title': '1993: In the Eye of the Storm', 'context': 'Global recession. Internet is still a dream. Eastern Europe struggles to reinvent itself.', 'insight': 'Europe: 8.7% unemployment. Brutal contrast with Asia (3.7%). Female progress stagnates.'},
1994: {'title': '1994: The Year of Uncertainty', 'context': 'Mandela becomes president, but Europe hits rock bottom. The labor world is fragmented.', 'insight': 'Europe reaches 9.7% unemployment. Asia remains the global economic haven.'},
1995: {'title': '1995: Seeds of the Future', 'context': 'Commercial Internet born. Windows 95. The foundations of the digital revolution are laid.', 'insight': 'Europe: 9.9% unemployment. After 5 years, women only gained 0.7% in management. Is it enough?'},
# 2000s (simulated key years)
2000: {'title': '2000: The New Millennium', 'context': 'Y2K survived. The dot-com bubble is at its peak. Global tech optimism.', 'insight': 'Europe gradually recovers. Women represent ~27% of global management.'},
2001: {'title': '2001: The Digital Awakening', 'context': '9/11 changes the world. The dot-com bubble bursts. Global technical recession.', 'insight': 'Tech crisis impacts employment. Women maintain advances in new sectors.'},
2005: {'title': '2005: Stabilization', 'context': 'YouTube, social media emerge. The global economy stabilizes post-dot-com crisis.', 'insight': 'Global unemployment normalizes. Female participation grows steadily (~30% management).'},
2008: {'title': '2008: The Great Collapse', 'context': 'Global financial crisis. Lehman Brothers. Obama elected. The labor world in shock.', 'insight': 'Unemployment skyrockets globally. Paradoxically, women better maintain their jobs.'},
2009: {'title': '2009: Into the Abyss', 'context': 'Worst recession since 1929. Massive stimuli. Employment is the #1 global priority.', 'insight': 'Peak global unemployment. Europe especially hit. Women emerge more resilient.'},
# 2010s (simulated key years)
2010: {'title': '2010: The Slow Recovery', 'context': 'iPad launched. Gradual economic recovery. Social movements emerge.', 'insight': 'Unemployment still high but decreasing. Women represent ~32% of management.'},
2015: {'title': '2015: The Turning Point', 'context': 'UN SDGs include gender equality. Gig economy takes off. Generational change.', 'insight': 'Goal: 50% female participation by 2030. Progress accelerates.'},
2018: {'title': '2018: The Silent Revolution', 'context': '#MeToo goes global. More women CEOs than ever. Gender awareness explodes.', 'insight': 'Women reach ~38% of management. The gender revolution is unstoppable.'},
2019: {'title': '2019: At the Pinnacle', 'context': 'Global economy at its peak. Greta Thunberg. The future seems bright.', 'insight': 'Global unemployment at decade lows. Women on the verge of 40% in leadership.'},
# 2020s
2020: {'title': '2020: The Great Reset', 'context': 'COVID-19 paralyzes the world. Massive remote work. Unprecedented crisis.', 'insight': 'Unemployment skyrockets. Paradox: some women better lead the digital crisis.'},
2021: {'title': '2021: Digital Renaissance', 'context': 'Vaccines, economic recovery. Hybrid work normalizes. New normal.', 'insight': 'Uneven recovery. Women reach ~42% of global management.'}
}
app = dash.Dash(__name__)
app.title = "Global Labor Market Dashboard"
def create_story_gauge(value, title, color='#3498db', max_val=100, suffix='%'):
fig = go.Figure(go.Indicator(
mode = "gauge+number",
value = value,
domain = {'x': [0, 1], 'y': [0, 1]},
title = {'text': title, 'font': {'size': 16, 'color': '#2c3e50', 'family': 'Arial Black'}},
gauge = {
'axis': {'range': [None, max_val], 'tickcolor': '#34495e'},
'bar': {'color': color, 'thickness': 1.0},
'bgcolor': "#ecf0f1",
'borderwidth': 3,
'bordercolor': color,
'steps': [
{'range': [0, max_val*0.25], 'color': '#ecf0f1'},
{'range': [max_val*0.25, max_val*0.5], 'color': '#d5dbdb'},
{'range': [max_val*0.5, max_val*0.75], 'color': '#aeb6bf'}
],
'threshold': {
'line': {'color': "#e74c3c", 'width': 4},
'thickness': 0.75,
'value': max_val*0.8
}
},
number = {'font': {'size': 24, 'color': color, 'family': 'Arial Black'}, 'suffix': suffix}
))
fig.update_layout(
height=250,
margin=dict(l=20, r=20, t=60, b=20),
paper_bgcolor='rgba(0,0,0,0)',
font={'color': '#2c3e50'}
)
return fig
def create_sparkline_for_unemployment(data_series, color):
"""Crea un sparkline para mostrar la tendencia reciente de desempleo."""
fig = go.Figure(go.Scatter(
x=data_series.index,
y=data_series.values,
mode='lines',
line=dict(color=color, width=2)
))
fig.update_layout(
height=50,
margin=dict(t=0, b=0, l=0, r=0),
xaxis=dict(visible=False),
yaxis=dict(visible=False),
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)'
)
return fig
def create_long_term_evolution():
fig = go.Figure()
# Female participation in management
fig.add_trace(go.Scatter(
x=df['Year'],
y=df['Female_management'],
mode='lines+markers',
name='Women in Management',
line=dict(color='#8e44ad', width=4),
marker=dict(size=6),
yaxis='y'
))
# Global unemployment
fig.add_trace(go.Scatter(
x=df['Year'],
y=df['World_unemployment'],
mode='lines+markers',
name='Global Unemployment',
line=dict(color='#e74c3c', width=3),
marker=dict(size=5),
yaxis='y2'
))
# Add vertical lines to separate eras
for era_name, (start_year, end_year) in historical_eras.items():
if start_year > 1991: # No line at the beginning
fig.add_vline(
x=start_year,
line_dash="dash",
line_color="gray",
annotation_text=era_name.split(':')[0],
annotation_position="top"
)
fig.update_layout(
title={
'text': '30 Years of Evolution: Gender vs. Unemployment',
'x': 0.5,
'font': {'size': 20, 'color': '#2c3e50', 'family': 'Arial Black'}
},
xaxis_title='Year',
yaxis=dict(
title='Female Management Participation (%)',
side='left',
color='#8e44ad'
),
yaxis2=dict(
title='Global Unemployment (%)',
side='right',
overlaying='y',
color='#e74c3c'
),
height=400,
margin=dict(l=60, r=60, t=80, b=60),
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='white',
legend=dict(x=0.02, y=0.98),
hovermode='x unified'
)
return fig
def create_regional_heatmap(year):
# Create unemployment matrix by region
regions = ['Africa', 'Americas', 'Arab_States', 'Asia_Pacific', 'Europe_CentralAsia']
year_data = df[df['Year'] == year].iloc[0]
unemployment_data = [
year_data['Africa_unemployment'],
year_data['Americas_unemployment'],
year_data['Arab_States_unemployment'],
year_data['Asia_Pacific_unemployment'],
year_data['Europe_CentralAsia_unemployment']
]
fig = go.Figure(data=go.Bar(
x=[r.replace('_', ' ') for r in regions],
y=unemployment_data,
marker=dict(
color=unemployment_data,
colorscale='RdYlGn_r', # Invert colorscale for unemployment
cmin=0,
cmax=15
),
text=[f'{val:.1f}%' for val in unemployment_data],
textposition='auto'
))
fig.update_layout(
title={
'text': f'Regional Unemployment in {year}',
'x': 0.5,
'font': {'size': 20, 'color': '#2c3e50', 'family': 'Arial Black'}
},
yaxis_title='Unemployment Rate (%)',
height=400, # Adjusted to have similar height to evolution graph
margin=dict(l=50, r=50, t=60, b=100),
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='white'
)
return fig
# Main layout
app.layout = html.Div([
# Inject custom CSS
html.Div(id='custom-css'),
# Epic Header
html.Div([
html.H1("π THREE DECADES OF LABOR TRANSFORMATION",
style={
'textAlign': 'center',
'color': 'white',
'fontSize': '42px',
'fontFamily': 'Arial Black',
'textShadow': '3px 3px 6px rgba(0,0,0,0.5)',
'marginBottom': '10px'
}),
html.H2("The Epic Story of Global Work (1991-2021)",
style={
'textAlign': 'center',
'color': '#ecf0f1',
'fontSize': '18px',
'fontStyle': 'italic'
})
], style={
'background': 'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)',
'padding': '50px',
'borderRadius': '15px',
'marginBottom': '30px',
'boxShadow': '0 15px 35px rgba(0,0,0,0.3)'
}),
# Era selector with RadioItems
html.Div([
html.H3("π°οΈ Explore Historical Eras",
style={'color': '#2c3e50', 'textAlign': 'center', 'marginBottom': '20px'}),
html.Div(className='radio-items-container', children=[
dcc.RadioItems(
id='era-dropdown',
options=[{'label': era, 'value': era} for era in historical_eras.keys()],
value='90s: The Awakening', # Default value in English
inline=True,
labelStyle={'fontSize': '16px'},
style={'marginBottom': '20px'}
)
]),
html.Div(id='era-description', style={'marginBottom': '20px', 'marginTop': '20px'})
], style={
'backgroundColor': '#f8f9fa',
'padding': '25px',
'borderRadius': '10px',
'marginBottom': '30px',
'textAlign': 'center'
}),
# Year control with RadioItems
html.Div([
html.H3("π
Journey Year by Year",
style={'color': '#2c3e50', 'textAlign': 'center', 'marginBottom': '20px'}),
html.Div(className='radio-items-container', children=[
dcc.RadioItems(
id='year-slider',
options=[{'label': str(y), 'value': y} for y in range(1991, 2022)],
value=1991,
inline=True,
labelStyle={'fontSize': '14px', 'marginRight': '10px', 'marginBottom': '5px'},
style={'display': 'flex', 'flexWrap': 'wrap', 'justifyContent': 'center'}
)
])
], style={
'backgroundColor': '#e8f4fd',
'padding': '25px',
'borderRadius': '10px',
'marginBottom': '30px'
}),
# Specific year storytelling section
html.Div(id='story-section', style={'marginBottom': '30px'}),
# Main dashboard
html.Div([
# Main gauges/big numbers row
html.Div([
# Female Management Gauge (still a gauge)
html.Div([dcc.Graph(id='gauge-female-mgmt', config={'displayModeBar': False})],
style={'width': '25%', 'padding': '5px'}),
# Global Unemployment Big Number
html.Div(id='big-number-world-unemployment',
style={'width': '25%', 'padding': '5px', 'display': 'flex', 'flexDirection': 'column', 'justifyContent': 'center'}),
# Europe Unemployment Big Number
html.Div(id='big-number-europe-unemployment',
style={'width': '25%', 'padding': '5px', 'display': 'flex', 'flexDirection': 'column', 'justifyContent': 'center'}),
# Asia Unemployment Big Number
html.Div(id='big-number-asia-unemployment',
style={'width': '25%', 'padding': '5px', 'display': 'flex', 'flexDirection': 'column', 'justifyContent': 'center'})
], style={
'display': 'flex',
'justifyContent': 'space-around',
'alignItems': 'center',
'marginBottom': '30px'
}),
# Main graph selector
html.Div([
html.H3("π Visualize Key Data",
style={'color': '#2c3e50', 'textAlign': 'center', 'marginBottom': '20px'}),
dcc.RadioItems(
id='graph-selector',
options=[
{'label': 'Long-Term Evolution', 'value': 'long_term'},
{'label': 'Regional Unemployment', 'value': 'regional'}
],
value='long_term',
inline=True,
labelStyle={'display': 'inline-block', 'marginRight': '20px', 'fontSize': '16px'}
),
html.Div(id='main-graph-container', style={'marginTop': '20px'})
], style={
'backgroundColor': '#f8f9fa',
'padding': '25px',
'borderRadius': '10px',
'marginBottom': '30px',
'textAlign': 'center'
}),
]),
# Dynamic insights and conclusions
html.Div(id='insights-section', style={'marginTop': '30px'})
], style={
'padding': '30px',
'backgroundColor': '#ffffff',
'fontFamily': 'Arial, sans-serif',
'minHeight': '100vh'
})
@callback(
[Output('era-description', 'children'),
Output('year-slider', 'options'),
Output('year-slider', 'value')],
[Input('era-dropdown', 'value')]
)
def update_era_info(selected_era):
start_year, end_year = historical_eras[selected_era]
era_info = era_stories[selected_era]
description = html.Div([
html.H4(selected_era, style={'color': '#2c3e50', 'marginBottom': '15px'}),
html.P(era_info['description'],
style={'fontSize': '16px', 'lineHeight': '1.6', 'marginBottom': '15px'}),
# FLEXBOX CONTAINER FOR TRENDS AND EVENTS
html.Div([
html.Div([
html.H5("π Female Trend", style={'color': '#8e44ad', 'marginBottom': '8px'}),
html.P(era_info['female_trend'], style={'fontSize': '14px', 'color': '#34495e'})
], style={'flexGrow': '1', 'margin': '0 10px', 'verticalAlign': 'top', 'backgroundColor': '#fcf4ff', 'padding': '15px', 'borderRadius': '8px', 'border': '1px solid #dcdcdc'}),
html.Div([
html.H5("πΌ Employment Trend", style={'color': '#e74c3c', 'marginBottom': '8px'}),
html.P(era_info['unemployment_trend'], style={'fontSize': '14px', 'color': '#34495e'})
], style={'flexGrow': '1', 'margin': '0 10px', 'verticalAlign': 'top', 'backgroundColor': '#fff4f4', 'padding': '15px', 'borderRadius': '8px', 'border': '1px solid #dcdcdc'}),
html.Div([
html.H5("π― Key Events", style={'color': '#27ae60', 'marginBottom': '8px'}),
html.Ul([html.Li(event, style={'fontSize': '13px', 'listStyleType': 'none', 'paddingLeft': '0', 'marginBottom': '5px'}) for event in era_info['key_events']], style={'paddingLeft': '0'})
], style={'flexGrow': '1', 'margin': '0 10px', 'verticalAlign': 'top', 'backgroundColor': '#f4fff4', 'padding': '15px', 'borderRadius': '8px', 'border': '1px solid #dcdcdc'})
], style={
'display': 'flex',
'justifyContent': 'space-around',
'alignItems': 'flex-start',
'marginBottom': '20px'
})
], style={
'backgroundColor': '#f1f2f6',
'padding': '20px',
'borderRadius': '8px',
'borderLeft': '4px solid #3498db'
})
# Generate RadioItems options for the years of the era
year_options = [{'label': str(year), 'value': year} for year in range(start_year, end_year + 1)]
# Select the first year of the new era as default value
initial_year_value = start_year
return description, year_options, initial_year_value
@callback(
[Output('story-section', 'children'),
Output('gauge-female-mgmt', 'figure'),
Output('big-number-world-unemployment', 'children'),
Output('big-number-europe-unemployment', 'children'),
Output('big-number-asia-unemployment', 'children'),
Output('insights-section', 'children')],
[Input('year-slider', 'value')]
)
def update_story(year):
year_data = df[df['Year'] == year].iloc[0]
# Get previous year data for sparklines and trends
prev_year_data = df[df['Year'] <= year].tail(5).set_index('Year') # Get last 5 years for sparkline
story = stories.get(year, {
'title': f'{year}: Year in Transition',
'context': f'A year of significant changes in the global labor landscape.',
'insight': f'Data reflects global trends of the period.'
})
# Storytelling section
story_section = html.Div([
html.H3(story['title'],
style={
'color': '#2c3e50',
'textAlign': 'center',
'fontSize': '28px',
'marginBottom': '20px',
'fontFamily': 'Arial Black'
}),
html.Div([
html.Div([
html.H4("π Historical Context", style={'color': '#3498db', 'marginBottom': '10px'}),
html.P(story['context'],
style={'fontSize': '16px', 'lineHeight': '1.6', 'color': '#34495e'})
], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}),
html.Div([
html.H4("π‘ Key Insight", style={'color': '#e67e22', 'marginBottom': '10px'}),
html.P(story['insight'],
style={'fontSize': '16px', 'lineHeight': '1.6', 'color': '#34495e'})
], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top', 'marginLeft': '4%'})
])
], style={
'backgroundColor': '#f8f9fa',
'padding': '30px',
'borderRadius': '15px',
'boxShadow': '0 5px 15px rgba(0,0,0,0.1)',
'border': '2px solid #e9ecef'
})
# Create Female Management gauge
gauge_female = create_story_gauge(
year_data['Female_management'],
'Women in Management',
'#8e44ad',
50,
'%'
)
# --- Big Numbers for Unemployment ---
# Global Unemployment Big Number
world_unemployment_value = year_data['World_unemployment']
world_unemployment_change = world_unemployment_value - df[df['Year'] == year-1]['World_unemployment'].iloc[0] if year > 1991 else 0
big_number_world = html.Div([
html.H4("Global Unemployment", style={'textAlign': 'center', 'color': '#e74c3c', 'marginBottom': '5px'}),
html.H3(f"{world_unemployment_value:.1f}%", style={'textAlign': 'center', 'color': '#e74c3c', 'fontSize': '36px', 'margin': '0'}),
html.P(f"vs. prev year: {world_unemployment_change:+.1f}% {'β¬οΈ' if world_unemployment_change > 0.05 else 'β¬οΈ' if world_unemployment_change < -0.05 else 'β‘οΈ'}", style={'textAlign': 'center', 'color': '#34495e', 'fontSize': '14px', 'marginBottom': '5px'}),
dcc.Graph(figure=create_sparkline_for_unemployment(prev_year_data['World_unemployment'], '#e74c3c'), config={'displayModeBar': False})
], className='card', style={'background-color': '#fff5f5', 'padding': '15px', 'border-radius': '10px', 'border': '2px solid #e74c3c', 'height': '100%'})
# Europe Unemployment Big Number
europe_unemployment_value = year_data['Europe_CentralAsia_unemployment']
europe_unemployment_change = europe_unemployment_value - df[df['Year'] == year-1]['Europe_CentralAsia_unemployment'].iloc[0] if year > 1991 else 0
big_number_europe = html.Div([
html.H4("Europe Unemployment", style={'textAlign': 'center', 'color': '#3498db', 'marginBottom': '5px'}),
html.H3(f"{europe_unemployment_value:.1f}%", style={'textAlign': 'center', 'color': '#3498db', 'fontSize': '36px', 'margin': '0'}),
html.P(f"vs. prev year: {europe_unemployment_change:+.1f}% {'β¬οΈ' if europe_unemployment_change > 0.05 else 'β¬οΈ' if europe_unemployment_change < -0.05 else 'β‘οΈ'}", style={'textAlign': 'center', 'color': '#34495e', 'fontSize': '14px', 'marginBottom': '5px'}),
dcc.Graph(figure=create_sparkline_for_unemployment(prev_year_data['Europe_CentralAsia_unemployment'], '#3498db'), config={'displayModeBar': False})
], className='card', style={'background-color': '#e8f4fd', 'padding': '15px', 'border-radius': '10px', 'border': '2px solid #3498db', 'height': '100%'})
# Asia-Pacific Unemployment Big Number
asia_unemployment_value = year_data['Asia_Pacific_unemployment']
asia_unemployment_change = asia_unemployment_value - df[df['Year'] == year-1]['Asia_Pacific_unemployment'].iloc[0] if year > 1991 else 0
big_number_asia = html.Div([
html.H4("Asia-Pacific Unemployment", style={'textAlign': 'center', 'color': '#27ae60', 'marginBottom': '5px'}),
html.H3(f"{asia_unemployment_value:.1f}%", style={'textAlign': 'center', 'color': '#27ae60', 'fontSize': '36px', 'margin': '0'}),
html.P(f"vs. prev year: {asia_unemployment_change:+.1f}% {'β¬οΈ' if asia_unemployment_change > 0.05 else 'β¬οΈ' if asia_unemployment_change < -0.05 else 'β‘οΈ'}", style={'textAlign': 'center', 'color': '#34495e', 'fontSize': '14px', 'marginBottom': '5px'}),
dcc.Graph(figure=create_sparkline_for_unemployment(prev_year_data['Asia_Pacific_unemployment'], '#27ae60'), config={'displayModeBar': False})
], className='card', style={'background-color': '#f0fff4', 'padding': '15px', 'border-radius': '10px', 'border': '2px solid #27ae60', 'height': '100%'})
# Dynamic insights section
insights = generate_insights(year, year_data)
return (story_section, gauge_female, big_number_world, big_number_europe, big_number_asia,
insights)
@callback(
Output('main-graph-container', 'children'),
[Input('graph-selector', 'value'),
Input('year-slider', 'value')]
)
def update_main_graph_visibility(selected_graph, year):
if selected_graph == 'long_term':
long_term_fig = create_long_term_evolution()
long_term_fig.add_vline(
x=year,
line_dash="solid",
line_color="#f39c12",
line_width=3,
annotation_text=f"π {year}",
annotation_position="top",
annotation=dict(
font=dict(color='#f39c12', size=14, family='Arial Black'),
bgcolor='rgba(255,255,255,0.8)',
bordercolor='#f39c12',
borderwidth=2
)
)
return dcc.Graph(figure=long_term_fig, config={'displayModeBar': False})
elif selected_graph == 'regional':
regional_fig = create_regional_heatmap(year)
return dcc.Graph(figure=regional_fig, config={'displayModeBar': False})
return html.Div("Select a graph to display.")
def generate_insights(year, year_data):
"""Generates dynamic insights based on the year and data"""
# Contextual analysis by decade
if 1991 <= year <= 1999:
decade_context = "π
Post-Cold War Transition Era"
color_theme = '#3498db'
elif 2000 <= year <= 2009:
decade_context = "π» Digital Revolution and Financial Crisis"
color_theme = '#e74c3c'
elif 2010 <= year <= 2019:
decade_context = "π New Economy and Empowerment"
color_theme = '#27ae60'
else: # 2020s
decade_context = "π Transformation and Pandemic"
color_theme = '#f39c12'
# Calculate comparisons (ensure previous year exists)
female_change = 0
unemployment_change = 0
female_trend = "β‘οΈ"
unemployment_trend = "β‘οΈ"
if year > df['Year'].min():
prev_data_row = df[df['Year'] == year - 1]
if not prev_data_row.empty:
prev_data = prev_data_row.iloc[0]
female_change = year_data['Female_management'] - prev_data['Female_management']
unemployment_change = year_data['World_unemployment'] - prev_data['World_unemployment']
female_trend = "π" if female_change > 0.05 else "π" if female_change < -0.05 else "β‘οΈ"
unemployment_trend = "π" if unemployment_change > 0.05 else "π" if unemployment_change < -0.05 else "β‘οΈ"
else: # Handle case where prev year might not be in dataframe (e.g. for 1991 if data starts at 1991)
female_trend = "π"
unemployment_trend = "π―"
else:
female_trend = "π"
unemployment_trend = "π―"
# Find region with best/worst unemployment
regions_data = {
'Africa': year_data['Africa_unemployment'],
'Americas': year_data['Americas_unemployment'],
'Arab States': year_data['Arab_States_unemployment'],
'Asia-Pacific': year_data['Asia_Pacific_unemployment'],
'Europe': year_data['Europe_CentralAsia_unemployment']
}
best_region = min(regions_data, key=regions_data.get)
worst_region = max(regions_data, key=regions_data.get)
# Generate female progress message
if year_data['Female_management'] < 30:
female_status = "π΄ Slow Progress"
female_message = "Women still represent less than 30% of business leadership. More effort is needed."
elif year_data['Female_management'] < 40:
female_status = "π‘ Moderate Advance"
female_message = "Visible progress, but still a way to go towards parity. Momentum is building."
else:
female_status = "π’ Revolution in Progress"
female_message = "Women are breaking the glass ceiling! This year shows significant strides."
insights_section = html.Div([
html.H3("π In-Depth Year Analysis",
style={
'color': color_theme,
'textAlign': 'center',
'marginBottom': '25px',
'fontSize': '24px',
'fontFamily': 'Arial Black'
}),
# Decade Context
html.Div([
html.H4(decade_context,
style={'color': color_theme, 'textAlign': 'center', 'marginBottom': '15px'})
], style={
'backgroundColor': f'rgba({int(color_theme[1:3], 16)}, {int(color_theme[3:5], 16)}, {int(color_theme[5:7], 16)}, 0.1)',
'padding': '15px',
'borderRadius': '10px',
'marginBottom': '20px',
'border': f'2px solid {color_theme}'
}),
# Key metrics in cards - NOW IN A SINGLE ROW WITH FLEXBOX
html.Div([
# Card 1: Female Participation
html.Div([
html.H5(f"{female_trend} Female Leadership",
style={'color': '#8e44ad', 'marginBottom': '10px'}),
html.H3(f"{year_data['Female_management']:.1f}%",
style={'color': '#8e44ad', 'fontSize': '32px', 'margin': '0'}),
html.P(female_status, style={'color': '#8e44ad', 'fontSize': '14px', 'fontWeight': 'bold'}),
html.P(female_message, style={'fontSize': '12px', 'color': '#34495e'})
], style={
'backgroundColor': '#f8f5ff',
'padding': '20px',
'borderRadius': '10px',
'textAlign': 'center',
'border': '2px solid #8e44ad',
'flexGrow': '1',
'margin': '0 5px',
'verticalAlign': 'top'
}),
# Card 2: Global Unemployment
html.Div([
html.H5(f"{unemployment_trend} Global Unemployment",
style={'color': '#e74c3c', 'marginBottom': '10px'}),
html.H3(f"{year_data['World_unemployment']:.1f}%",
style={'color': '#e74c3c', 'fontSize': '32px', 'margin': '0'}),
html.P("π Key Indicator", style={'color': '#e74c3c', 'fontSize': '14px', 'fontWeight': 'bold'}),
html.P(f"Change vs. previous year: {unemployment_change:+.1f}%",
style={'fontSize': '12px', 'color': '#34495e'})
], style={
'backgroundColor': '#fff5f5',
'padding': '20px',
'borderRadius': '10px',
'textAlign': 'center',
'border': '2px solid #e74c3c',
'flexGrow': '1',
'margin': '0 5px',
'verticalAlign': 'top'
}),
# Card 3: Best Region
html.Div([
html.H5("π Best Performing Region",
style={'color': '#27ae60', 'marginBottom': '10px'}),
html.H3(f"{regions_data[best_region]:.1f}%",
style={'color': '#27ae60', 'fontSize': '32px', 'margin': '0'}),
html.P(best_region, style={'color': '#27ae60', 'fontSize': '14px', 'fontWeight': 'bold'}),
html.P("Lowest unemployment rate", style={'fontSize': '12px', 'color': '#34495e'})
], style={
'backgroundColor': '#f0fff4',
'padding': '20px',
'borderRadius': '10px',
'textAlign': 'center',
'border': '2px solid #27ae60',
'flexGrow': '1',
'margin': '0 5px',
'verticalAlign': 'top'
}),
# Card 4: Challenged Region
html.Div([
html.H5("β οΈ Most Challenged Region",
style={'color': '#f39c12', 'marginBottom': '10px'}),
html.H3(f"{regions_data[worst_region]:.1f}%",
style={'color': '#f39c12', 'fontSize': '32px', 'margin': '0'}),
html.P(worst_region, style={'color': '#f39c12', 'fontSize': '14px', 'fontWeight': 'bold'}),
html.P("Highest unemployment rate", style={'fontSize': '12px', 'color': '#34495e'})
], style={
'backgroundColor': '#fff9e6',
'padding': '20px',
'borderRadius': '10px',
'textAlign': 'center',
'border': '2px solid #f39c12',
'flexGrow': '1',
'margin': '0 5px',
'verticalAlign': 'top'
})
], style={
'display': 'flex',
'justifyContent': 'space-around',
'alignItems': 'stretch',
'marginBottom': '25px',
'textAlign': 'center'
}),
# Historical reflection
html.Div([
html.H4("π Historical Reflection",
style={'color': '#2c3e50', 'marginBottom': '15px'}),
html.P(f"In {year}, the global labor market was at a unique moment. "
f"While {'women advanced towards' if female_change > 0 else 'women maintained'} "
f"greater representation in business leadership, "
f"global unemployment {'increased' if unemployment_change > 0 else 'decreased' if unemployment_change < 0 else 'remained stable'}, "
f"reflecting the economic dynamics of the era.",
style={'fontSize': '16px', 'lineHeight': '1.6', 'color': '#34495e', 'textAlign': 'justify'})
], style={
'backgroundColor': '#f1f2f6',
'padding': '20px',
'borderRadius': '10px',
'borderLeft': '4px solid #2c3e50'
}),
# Footer
html.Footer(
"Dashboard developed using Plotly-Dash. Data source: International Labour Organization",
style={
'textAlign': 'center',
'marginTop': '40px',
'padding': '20px',
'backgroundColor': '#e9ecef',
'color': '#2c3e50',
'fontSize': '14px',
'borderRadius': '10px',
'boxShadow': '0 -5px 15px rgba(0,0,0,0.1)'
}
)
], style={
'backgroundColor': 'white',
'padding': '30px',
'borderRadius': '15px',
'boxShadow': '0 10px 25px rgba(0,0,0,0.1)',
'border': '1px solid #e9ecef'
})
return insights_section