Risk Dashboard#
Comprehensive visualization framework for corporate social responsibility metrics#
Imports and Configuration#
Importing all required libraries and setting up configuration for the visualization system.
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import warnings
import os
# Suppress warnings for cleaner notebook output
warnings.filterwarnings('ignore')
Visual Design System#
Establishing a consistent visual design system with carefully selected colors, typography, and layouts for all visualizations.
# Design system: Color palette
COLOR_PALETTE = {
"primary": "#2E5EAA", # Primary blue for main elements
"secondary": "#5E366E", # Secondary purple for accents
"accent": "#F08B33", # Orange accent for highlights
"background": "#F9F9F9", # Light background
"text": "#333333", # Dark text
"success": "#3AA655", # Green for positive metrics
"warning": "#FFC857", # Yellow for medium risk
"danger": "#D64045", # Red for high risk
"neutral": "#7D8491" # Gray for neutral elements
}
# Industry-specific colors for consistent visualization
INDUSTRY_COLORS = {
"Energy": "#E63946",
"Technology": "#457B9D",
"Manufacturing": "#F8961E",
"Financial Services": "#43AA8B",
"Healthcare": "#577590",
"Consumer Goods": "#90BE6D",
"Others": "#9D4EDD"
}
# Typography settings
TITLE_FONT = {
"family": "Arial, sans-serif",
"size": 22,
"color": COLOR_PALETTE["text"]
}
AXIS_FONT = {
"family": "Arial, sans-serif",
"size": 14,
"color": COLOR_PALETTE["text"]
}
Sample Data Generator#
Creating a sample data generator to ensure the notebook can run even without access to the original data files.
def generate_sample_data(n_companies=50):
"""
Generate sample data for demonstration purposes when actual data files are not available.
Args:
n_companies: Number of synthetic companies to generate
Returns:
DataFrame with synthetic data
"""
# Set random seed for reproducibility
np.random.seed(42)
# Define industries
industries = list(INDUSTRY_COLORS.keys())
# Generate company data
data = {
'Company Name': [f"Company {i+1}" for i in range(n_companies)],
'Industry': np.random.choice(industries, size=n_companies),
'CO2_Emissions': np.random.exponential(scale=10000, size=n_companies),
'Legal_Penalties': np.random.exponential(scale=10, size=n_companies),
'Alive_Compliance': np.random.uniform(20, 100, size=n_companies),
'Year': np.random.choice([2023, 2024], size=n_companies),
'Patterns': np.random.uniform(1, 10, size=n_companies)
}
# Create DataFrame
df = pd.DataFrame(data)
# Calculate PSI (Penalty Severity Index)
revenue = np.random.uniform(500, 5000, size=n_companies)
df['Revenue'] = revenue
epsilon = 1e-6
df['PSI'] = np.log10((df['Legal_Penalties'] / revenue * 1e6) + epsilon)
# Calculate SRI (Social Responsibility Index)
compliance_weight = 0.4
emissions_weight = 0.3
penalties_weight = 0.3
# Normalize emissions (higher values are worse, so invert)
max_emissions = df['CO2_Emissions'].max()
emissions_score = 100 - (df['CO2_Emissions'] / max_emissions * 100)
# Normalize penalties (higher values are worse, so invert)
max_penalties = df['Legal_Penalties'].max()
penalties_score = 100 - (df['Legal_Penalties'] / max_penalties * 100)
# Calculate SRI
df['SRI'] = (
compliance_weight * df['Alive_Compliance'] +
emissions_weight * emissions_score +
penalties_weight * penalties_score
)
# Add anomaly tag to 10% of companies
n_anomalies = int(n_companies * 0.1)
anomaly_indices = np.random.choice(n_companies, size=n_anomalies, replace=False)
df['Anomaly'] = 'Normal'
df.loc[anomaly_indices, 'Anomaly'] = 'Anomaly'
return df
Data Loading#
Loading and preparing data with fallback to sample data if actual files are not available.
def load_data():
"""
Load data from files or generate sample data if files are not available
Returns:
DataFrame with data for visualization and PDF insights text
"""
excel_path = "Alive Analysis_WEF Partners.xlsx"
pdf_path = "Alive Analysis One-Pager.pdf"
try:
# Try to read actual data from Excel file
print(f"Attempting to load data from {excel_path}...")
df = pd.read_excel(excel_path, sheet_name="WEF Partners")
# Process numeric columns
df["CO2_Emissions"] = pd.to_numeric(df["CO2 output per year 2023/24"], errors="coerce").fillna(0)
df["Patterns"] = pd.to_numeric(df["Patterns detected numerical 2023/24"], errors="coerce").fillna(0)
# Extract legal penalties assuming format like "$X.YM"
df["Legal_Penalties"] = df["Legal Penalties and Fines in 2023 / 2024"].str.extract(r"\$(\d+\.?\d*)M").astype(float)
# Extract Alive compliance score
df["Alive_Compliance"] = pd.to_numeric(df["Alive Standards Compliance Score"], errors="coerce").fillna(50)
# Add additional derived metrics
# SRI calculation would go here
df['SRI'] = df['Alive_Compliance'] # Simplified placeholder
# Try to load PDF insights
insights = "Corporate social responsibility insights"
if os.path.exists(pdf_path):
try:
from PyPDF2 import PdfReader
with open(pdf_path, "rb") as f:
reader = PdfReader(f)
insights = " ".join([page.extract_text() for page in reader.pages[:2]])
except Exception as e:
print(f"Error reading PDF: {e}. Using placeholder insights.")
print("Real data loaded successfully.")
return df, insights
except Exception as e:
print(f"Error loading real data: {e}")
print("Generating sample data instead...")
# Generate sample data
df = generate_sample_data(50)
# Sample insights text
insights = """This analysis shows corporate social responsibility metrics across industries.
The data highlights how various companies perform in terms of environmental impact,
compliance with standards, and legal penalties. Key observations include significant
variations across industries, with Energy showing higher emissions and Technology
demonstrating better compliance rates overall."""
print("Sample data generated successfully.")
return df, insights
# Load data
df, insights = load_data()
Visualization Components#
Building visualization functions for different types of analysis.
def create_risk_matrix(df, selected_industries=None, year=2024):
"""
Create an interactive risk matrix scatter plot
Args:
df: Processed dataframe with required columns
selected_industries: List of industries to filter by (None for all)
year: Year to filter data by
Returns:
Plotly figure object
"""
# Filter data by selected industries and year
filtered_df = df.copy()
if selected_industries:
filtered_df = filtered_df[filtered_df['Industry'].isin(selected_industries)]
if 'Year' in filtered_df.columns:
filtered_df = filtered_df[filtered_df['Year'] == year]
# Create quadrant boundaries
compliance_mid = 50
penalty_mid = filtered_df['Legal_Penalties'].median()
# Create the scatter plot
fig = px.scatter(
filtered_df,
x='Alive_Compliance',
y='Legal_Penalties',
size='CO2_Emissions',
color='Industry',
color_discrete_map=INDUSTRY_COLORS,
hover_name='Company Name',
title=f'Social Responsibility Risk Matrix {year}',
labels={
'Alive_Compliance': 'Alive Standards Compliance Score (%)',
'Legal_Penalties': 'Legal Penalties ($ Millions)',
'CO2_Emissions': 'CO₂ Emissions (tons)',
'Industry': 'Industry'
}
)
# Add quadrant lines
fig.add_shape(
type="line", line=dict(dash="dash", color=COLOR_PALETTE["neutral"]),
x0=compliance_mid, y0=0, x1=compliance_mid, y1=filtered_df['Legal_Penalties'].max()*1.1
)
fig.add_shape(
type="line", line=dict(dash="dash", color=COLOR_PALETTE["neutral"]),
x0=0, y0=penalty_mid, x1=100, y1=penalty_mid
)
# Add quadrant labels
annotations = [
dict(
x=25, y=filtered_df['Legal_Penalties'].max()*0.9,
text="Critical Zone (Q4):<br>Low compliance<br>High penalties",
showarrow=False, font=dict(color=COLOR_PALETTE["danger"], size=12)
),
dict(
x=75, y=filtered_df['Legal_Penalties'].max()*0.9,
text="Greenwashing Alert (Q2):<br>High compliance<br>High penalties",
showarrow=False, font=dict(color=COLOR_PALETTE["warning"], size=12)
),
dict(
x=25, y=filtered_df['Legal_Penalties'].min()*1.5,
text="Risk Potential (Q3):<br>Low compliance<br>Low penalties",
showarrow=False, font=dict(color=COLOR_PALETTE["warning"], size=12)
),
dict(
x=75, y=filtered_df['Legal_Penalties'].min()*1.5,
text="Leading Zone (Q1):<br>High compliance<br>Low penalties",
showarrow=False, font=dict(color=COLOR_PALETTE["success"], size=12)
)
]
for annotation in annotations:
fig.add_annotation(annotation)
# Set axes ranges
fig.update_xaxes(range=[0, 100])
fig.update_yaxes(range=[0, filtered_df['Legal_Penalties'].max()*1.1])
# Apply consistent styling
fig.update_layout(
template="plotly_white",
paper_bgcolor=COLOR_PALETTE["background"],
plot_bgcolor=COLOR_PALETTE["background"],
font=TITLE_FONT,
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1
),
margin=dict(l=60, r=30, t=80, b=60),
hoverlabel=dict(
bgcolor="white",
font_size=14,
font_family="Arial, sans-serif"
)
)
return fig
def create_emissions_chart(df, selected_industries=None):
"""
Create a bar chart comparing CO2 emissions by industry
Args:
df: Processed dataframe with required columns
selected_industries: List of industries to filter by (None for all)
Returns:
Plotly figure object
"""
# Filter data by selected industries
filtered_df = df.copy()
if selected_industries:
filtered_df = filtered_df[filtered_df['Industry'].isin(selected_industries)]
# Group by industry and calculate total emissions
industry_emissions = filtered_df.groupby('Industry')['CO2_Emissions'].sum().reset_index()
industry_emissions = industry_emissions.sort_values('CO2_Emissions', ascending=False)
# Calculate the percentage of total emissions
total_emissions = industry_emissions['CO2_Emissions'].sum()
industry_emissions['Percentage'] = (industry_emissions['CO2_Emissions'] / total_emissions * 100).round(1)
# Create the bar chart
fig = go.Figure()
# Add bars
fig.add_trace(go.Bar(
x=industry_emissions['Industry'],
y=industry_emissions['CO2_Emissions'],
text=industry_emissions['Percentage'].map('{}%'.format),
textposition='auto',
marker_color=[INDUSTRY_COLORS.get(industry, COLOR_PALETTE["neutral"])
for industry in industry_emissions['Industry']],
hovertemplate='<b>%{x}</b><br>CO₂ Emissions: %{y:,.0f} tons<br>%{text} of total<extra></extra>'
))
# Update layout
fig.update_layout(
title='CO₂ Emissions by Industry',
xaxis=dict(title='Industry'),
yaxis=dict(title='CO₂ Emissions (tons)'),
template="plotly_white",
paper_bgcolor=COLOR_PALETTE["background"],
plot_bgcolor=COLOR_PALETTE["background"],
font=TITLE_FONT,
margin=dict(l=60, r=30, t=80, b=120),
)
# Rotate x-axis labels for better readability
fig.update_xaxes(tickangle=45)
return fig
Display Visualizations#
Creating and displaying the visualizations.
# Create risk matrix figure
risk_matrix_fig = create_risk_matrix(df)
# Display figure inline
risk_matrix_fig
# Create emissions chart
emissions_fig = create_emissions_chart(df)
# Display figure inline
emissions_fig
Interactive Dashboard (Static Preview)#
A preview of the dashboard that would be interactive when run in JupyterDash.
# Create interactive dashboard (static preview in the notebook)
print("""Interactive Dashboard Preview
This dashboard provides:
- Interactive filtering by industry and year
- Dynamic visualization updates
- Hover information for detailed data exploration
- Coordinated views across multiple visualizations
To run the interactive version, execute this notebook in an interactive Jupyter session or JupyterLab environment.
""")
# Filter by Energy and Technology sectors only
filtered_risk_matrix = create_risk_matrix(df, selected_industries=['Energy', 'Technology'])
filtered_risk_matrix