Uncovering the Unknown: Violence Against Humanitarian Aid Workers

Code
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import pycountry

df = pd.read_csv('data/cleaned_security_incidents.csv')

# map ISO codes
def get_iso_code(name):
    try:
        return pycountry.countries.lookup(name).alpha_3
    except:
        return None

df['ISO'] = df['country'].apply(get_iso_code)

# filter years
df = df[(df['year'] >= 1997) & (df['year'] <= 2025)]
df_grouped = df.groupby(['country', 'ISO', 'year'], as_index=False)['total_affected'].sum()
sorted_years = sorted(df_grouped['year'].unique())

# choropleth base
fig = px.choropleth(
    df_grouped,
    locations='ISO',
    locationmode='ISO-3',
    color='total_affected',
    hover_name='country',
    animation_frame='year',
    color_continuous_scale='Reds',
    title='Global Humanitarian Crises & Aid Workers Affected by Violence, 1997-2025',
    subtitle='An interactive timeline of major 21st-century humanitarian crises and aid worker casualties by country',
    range_color=[0, df_grouped['total_affected'].max()],  # FIXED SCALE
)

# sorting the frames by year NUMERICALLY
fig.frames = sorted(fig.frames, key=lambda x: int(x.name))

# timing parameters
FRAME_DURATION = 1000 
TRANSITION_DURATION = 500  

# styling & layout
fig.update_layout(
    coloraxis_colorbar=dict(
        title="Total Humanitarian Aid<br>Workers Affected",
        title_font=dict(
            family="Merriweather, serif",
            size=12
        )
    ),
    geo=dict(
        scope='world',
        projection=dict(type='natural earth'),
        showland=True,
        landcolor='rgb(243, 243, 243)',
        countrycolor='rgb(204, 204, 204)',
    ),
    transition=dict(
        duration=TRANSITION_DURATION,
        easing='cubic-in-out'
    ),
    updatemenus=[{
        'type': 'buttons',
        'showactive': False,
        'y': 0.05,  
        'x': 0.08,  
        'xanchor': 'center',
        'yanchor': 'middle',
        'buttons': [{
            'label': 'Play',
            'method': 'animate',
            'args': [None, {
                'frame': {'duration': FRAME_DURATION, 'redraw': True},
                'fromcurrent': True,
                'transition': {'duration': TRANSITION_DURATION, 'easing': 'cubic-in-out'},
                'mode': 'immediate'
            }]
        }, {
            'label': 'Pause',
            'method': 'animate',
            'args': [[None], {
                'frame': {'duration': 0, 'redraw': False},
                'mode': 'immediate',
                'transition': {'duration': 0}
            }]
        }]
    }],
    # Keep consistent timing in slider steps
    sliders=[{
        'active': 0,
        'currentvalue': {"prefix": "year=", "font": {"size": 12}},
        'steps': [
            {
                'method': 'animate',
                'label': str(year),
                'args': [[str(year)], {
                    'frame': {'duration': FRAME_DURATION, 'redraw': True},
                    'mode': 'immediate',
                    'transition': {'duration': TRANSITION_DURATION, 'easing': 'cubic-in-out'}
                }]
            } 
            for year in sorted_years
        ],
        'transition': {'duration': TRANSITION_DURATION},
        'x': 0.1,
        'len': 0.9,
        'y': 0,
        'yanchor': 'top',
        'pad': {'b': 10, 't': 50},
    }],
    # change font
    font=dict(
        family="Merriweather, serif",
        size=12,
        color="black"
    ),
    title=dict(
        font=dict(
            family="Merriweather, serif",
            size=20,
            color="black"
        )
    )
)

# MAJOR CONFLICTS
frames_with_annotation = []
for frame in sorted(fig.frames, key=lambda x: int(x.name)):
    year = int(frame.name)
    frame.layout = go.Layout(
        geo=dict(
            scope='world',
            projection=dict(type='natural earth'),
            showland=True,
            landcolor='rgb(243, 243, 243)',
            countrycolor='rgb(204, 204, 204)',
        ),
        font=dict(
            family="Merriweather, serif",
            size=12,
            color="black"
        ),
        coloraxis_colorbar=dict(
            title="Total Humanitarian Aid<br>Workers Affected",
            title_font=dict(
                family="Merriweather, serif",
                size=12
            )
        ),
        # Important: each frame needs same transition settings
        transition=dict(
            duration=TRANSITION_DURATION,
            easing='cubic-in-out'
        )
    )
    
    annotations = []
    
    # conflicts to add: 
    # Afghanistan-US Conflict annotation for years 2009-2016
    if 2009 <= year <= 2016:
        annotations.append(dict(
            x=0.63, # upper right corner
            y=0.76,
            xref='paper',
            yref='paper',
            text="Height of<br>Afghanistan-US Conflict",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=50,  
            ay=-40,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))
    
    # Add South Sudan Civil War annotation for years 2015-2022
    if 2015 <= year <= 2020:
        annotations.append(dict(
            x=0.55,
            y=0.51,
            xref='paper',
            yref='paper',
            text="South Sudan Civil War<br>2013-2018",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=-60, 
            ay=60,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    # Syrian civil war
    if 2011 <= year <= 2024:
        annotations.append(dict(
            x=0.56, 
            y=0.73,
            xref='paper',
            yref='paper',
            text="Syrian Civil War<br>2011-",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=-90, 
            ay=0,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))
    
    # Darfur crisis (Sudan) peak 2003-2006
    if 2003 <= year <= 2007:
        annotations.append(dict(
            x=0.56, 
            y=0.56,
            xref='paper',
            yref='paper',
            text="Darfur Crisis peak<br>2003-2006",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=60,  
            ay=60,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    # War on Ukraine 
    if 2021 <= year <= 2024:
        annotations.append(dict(
            x=0.54, 
            y=0.82,
            xref='paper',
            yref='paper',
            text="Russian Invasion of<br>Ukraine, 2022-",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=0,  
            ay=-40,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    # Palestine
    if 2023 <= year <= 2024:
        annotations.append(dict(
            x=0.55, 
            y=0.7,
            xref='paper',
            yref='paper',
            text="Israeli-Palestinian<br>War, 2023-",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=60, 
            ay=-40,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    # Sudanese civil war: 2023 - 
    if 2023 <= year <= 2024:
        annotations.append(dict(
            x=0.56, 
            y=0.56,
            xref='paper',
            yref='paper',
            text="Sudanese Civil<br>War, 2023-",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=60,  
            ay=60,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    # Mali War: 2012-
    if 2012 <= year <= 2024:
        annotations.append(dict(
            x=0.49, 
            y=0.58,
            xref='paper',
            yref='paper',
            text="Mali War<br>2012-",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=-60,  
            ay=20,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    # War in Somalia: 2006-2009
    if 2006 <= year <= 2009:
        annotations.append(dict(
            x=0.6, 
            y=0.56,
            xref='paper',
            yref='paper',
            text="War in Somalia<br>2006-2009",
            showarrow=True,
            arrowhead=2,
            arrowsize=1,
            arrowwidth=2,
            arrowcolor='red',
            ax=60,  
            ay=20,
            font=dict(
                family="Merriweather, serif",
                size=8, 
                color='black'
            ),
            bgcolor='rgba(255, 0, 0, 0.8)',
            bordercolor='red',
            borderwidth=2,
            borderpad=4
        ))

    if annotations:
        frame.layout.annotations = annotations
    
    frames_with_annotation.append(frame)

# maintain order
fig.frames = sorted(frames_with_annotation, key=lambda x: int(x.name))

# javascript for synchronization
js_code = """
<script>
window.addEventListener('load', function() {
  setTimeout(function() {
    var plotDiv = document.querySelector('.plotly-graph-div');
    
    // Reset the animation when the slider is used
    if (plotDiv && plotDiv._fullLayout && plotDiv._fullLayout.sliders) {
      const slider = plotDiv._fullLayout.sliders[0];
      if (slider.active !== undefined) {
        // Force consistent timing between slider and frames
        Plotly.animate(plotDiv, null, {
          mode: 'immediate',
          frame: {duration: 1000},
          transition: {duration: 500}
        });
      }
    }
  }, 1000);
});
</script>
"""

# export to html 
import plotly.io as pio
html_file = 'humanitarian_workers_map.html'
pio.write_html(fig, file=html_file, include_plotlyjs=True, full_html=True)

# import font and add JavaScript fix
html_template = """
<style>
@import url('https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap');
</style>
{js_code}
{plot_html}
"""

# display in rendered html
from IPython.display import IFrame, HTML, display
plot_html = pio.to_html(fig, include_plotlyjs=True, full_html=False)
final_html = html_template.format(js_code=js_code, plot_html=plot_html)

display(HTML(final_html))

The timeline above reveals where humanitarian crises have been most concentrated in the last three decades— and where aid workers have been killed, wounded, or kidnapped as a result of deliberate violence. Unsurprisingly, regions affected by prolonged conflict show consistently higher numbers of affected humanitarian personnel over the years.

Humanitarian aid workers risk their lives to support those enduring inhumane and unjust conditions. They step into volatile, war-ridden, often dangerous environments out of a commitment to serve others. They should not be targets for standing up for humanity.

The AWSD dataset exists to document these acts of violence, preserving a record of what has been done to those who dedicate themselves to helping others. These records are essential—not only for accountability, but for informing stronger protection, prevention, and recovery strategies moving forward.

Data Source

The Aid Worker Security Database (AWSD) is a comprehensive, publicly accessible repository that documents major violent incidents against humanitarian workers globally. It is maintained by the nonprofit research organization Humanitarian Outcomes and funded by USAID. AWSD gathers its information from multiple sources including media reports, security incident reports from humanitarian organizations, UN security reports, and direct reporting from field security personnel. This data is then reviewed and managed by expert personnel.

In AWSD, each event represents a unit of analysis. The database documents instances of major violence against aid workers, which includes:

  • Killing
  • Wounding (serious injury requiring medical attention)
  • Kidnapping
  • Rape and sexual assault

It also attempts to provide reliable and transparent information on a variety of other variables, including the location of the attack, the means of violence, the humanitarian aid organization the victim(s) belong to, and the identity and affiliation of the perpetrator.

For more information on the methodology underlying this database, visit Humanitarian Outcomes’ AWSD Codebook.

Who Are Humanitatian Aid Workers?

Aid workers are defined as employees and associated personnel (both paid and volunteer) of nonprofit aid agencies providing material and technical assistance in humanitarian contexts. This encompasses:

Intergovernmental Organizations (IGOs):

International Red Cross/Red Crescent Movement:

Non-Governmental Organizations (NGOs)

  • International NGOs
  • National NGOs
  • Local and Community-Based NGOs

Other

  • Donor agencies
  • Local volunteer groups and civil society organizations

The AWSD’s definition of a humanitarian aid worker includes locally contracted staff like drivers and security guards. It also covers contracted workers and vendors of humanitarian organizations when they are affected by violence while supporting humanitarian missions.

Explore the visualization below to compare and follow how different types of humanitarian aid organizations have been impacted by violence over time.

Code
import pandas as pd
import plotly.graph_objects as go

# Aggregate data by year for each organization type
yearly_data = df.groupby('year')[['un', 'icrc', 'nrcs_and_ifrc', 'ingo', 'nngo', 'other']].sum()

# Reset index to make 'year' a column again
yearly_data = yearly_data.reset_index()

# Create a prettier name mapping for labels
org_names = {
    'un': 'United Nations',
    'icrc': 'Int. Committee of the Red Cross',
    'nrcs_and_ifrc': 'National Red Cross & IFRC',
    'ingo': 'International NGO',
    'nngo': 'National NGO',
    'other': 'Other Organizations'
}

# Create the interactive plot
fig = go.Figure()

# Red color palette
colors = ['#0294f5', '#870000', '#e30000', '#fca103', '#fcc203', '#fcde65']

# Add each organization as a separate line
for i, org in enumerate(['un', 'icrc', 'nrcs_and_ifrc', 'ingo', 'nngo', 'other']):
    fig.add_trace(go.Scatter(
        x=yearly_data['year'],
        y=yearly_data[org],
        mode='lines',
        name=org_names[org],
        line=dict(color=colors[i], width=3),
        hovertemplate=f"{org_names[org]}<br>Year: %{{x}}<br>Victims: %{{y}}<extra></extra>"
    ))

# Update layout with Merriweather font
fig.update_layout(
    title='Violence Victims by Aid Organization (1997-Present)',
    xaxis_title='Year',
    yaxis_title='Number of Victims',
    legend_title='Aid Organization',
    hovermode='closest',
    template='plotly_white',
    font=dict(
        family="Merriweather, serif",
        size=12
    ),
    title_font=dict(
        family="Merriweather, serif",
        size=20
    )
)


html_file = 'aid_orgs.html'
pio.write_html(fig, file=html_file, include_plotlyjs=True, full_html=True)

# import font and add JavaScript fix
html_template = """
<style>
@import url('https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap');
</style>
{js_code}
{plot_html}
"""

# display in rendered html
from IPython.display import IFrame, HTML, display
plot_html = pio.to_html(fig, include_plotlyjs=True, full_html=False)
final_html = html_template.format(js_code=js_code, plot_html=plot_html)

display(HTML(final_html))

The AWSD data shows that International NGO workers (excluding UN staff) have experienced the highest number of targeted violence incidents over time, followed by National NGO workers and then UN staff.

Interactive Timeline of Humanitarian Attack Data

This alternative interactive visualization below offers both a high-level overview and detailed insights into humanitarian attack events. This will allow you to gain insight on the scope and trends of violence over time in different parts of the world.

You can customize your exploration by selecting a variable of interest for the y-axis using the dropdown menu labeled “Y-axis” at the bottom. Each point is color-coded by the region where the incident occurred. You can zoom in and out across time, and hovering over a point reveals detailed information—including the country, date, and the number of people affected, based on your selected variable.

Code
import altair as alt
import pandas as pd

def create_viz2():
    df = pd.read_csv('data/cleaned_security_incidents.csv')
    df['Date'] = pd.to_datetime(df[['year', 'month']].assign(Day=1))
    df = df.dropna(subset=['Date'])
    
    # Add UN region mapping for countries with customized regions 
    un_regions = {
        # Africa
        'Algeria': 'Africa', 'Angola': 'Africa', 'Benin': 'Africa', 'Botswana': 'Africa',
        'Burkina Faso': 'Africa', 'Burundi': 'Africa', 'Cabo Verde': 'Africa', 'Cameroon': 'Africa',
        'Central African Republic': 'Africa', 'Chad': 'Africa', 'Comoros': 'Africa',
        'Congo': 'Africa', 'Democratic Republic of the Congo': 'Africa', 'DRC': 'Africa', 'DR Congo': 'Africa',
        'Djibouti': 'Africa', 'Egypt': 'Africa', 'Equatorial Guinea': 'Africa',
        'Eritrea': 'Africa', 'Eswatini': 'Africa', 'Ethiopia': 'Africa', 'Gabon': 'Africa',
        'Gambia': 'Africa', 'Ghana': 'Africa', 'Guinea': 'Africa', 'Guinea-Bissau': 'Africa',
        'Ivory Coast': 'Africa', "Côte d'Ivoire": 'Africa', 'Kenya': 'Africa', 'Lesotho': 'Africa',
        'Liberia': 'Africa', 'Libya': 'Africa', 'Madagascar': 'Africa', 'Malawi': 'Africa',
        'Mali': 'Africa', 'Mauritania': 'Africa', 'Mauritius': 'Africa', 'Morocco': 'Africa',
        'Mozambique': 'Africa', 'Namibia': 'Africa', 'Niger': 'Africa', 'Nigeria': 'Africa',
        'Rwanda': 'Africa', 'Sao Tome and Principe': 'Africa', 'Senegal': 'Africa',
        'Seychelles': 'Africa', 'Sierra Leone': 'Africa', 'Somalia': 'Africa',
        'South Africa': 'Africa', 'South Sudan': 'Africa', 'Sudan': 'Africa',
        'Tanzania': 'Africa', 'Togo': 'Africa', 'Tunisia': 'Africa', 'Uganda': 'Africa',
        'Zambia': 'Africa', 'Zimbabwe': 'Africa',
        
        # Americas
        'Antigua and Barbuda': 'Americas', 'Argentina': 'Americas', 'Bahamas': 'Americas',
        'Barbados': 'Americas', 'Belize': 'Americas', 'Bolivia': 'Americas', 'Brazil': 'Americas',
        'Canada': 'Americas', 'Chile': 'Americas', 'Colombia': 'Americas', 'Costa Rica': 'Americas',
        'Cuba': 'Americas', 'Dominica': 'Americas', 'Dominican Republic': 'Americas',
        'Ecuador': 'Americas', 'El Salvador': 'Americas', 'Grenada': 'Americas',
        'Guatemala': 'Americas', 'Guyana': 'Americas', 'Haiti': 'Americas',
        'Honduras': 'Americas', 'Jamaica': 'Americas', 'Mexico': 'Americas',
        'Nicaragua': 'Americas', 'Panama': 'Americas', 'Paraguay': 'Americas', 'Peru': 'Americas',
        'Saint Kitts and Nevis': 'Americas', 'Saint Lucia': 'Americas',
        'Saint Vincent and the Grenadines': 'Americas', 'Suriname': 'Americas',
        'Trinidad and Tobago': 'Americas', 'United States': 'Americas', 'USA': 'Americas',
        'Uruguay': 'Americas', 'Venezuela': 'Americas',
        
        # Asia (excluding Middle East countries)
        'Afghanistan': 'Asia', 'Bangladesh': 'Asia', 'Bhutan': 'Asia',
        'Brunei': 'Asia', 'Cambodia': 'Asia', 'China': 'Asia', 'India': 'Asia',
        'Indonesia': 'Asia', 'Japan': 'Asia', 
        'Kazakhstan': 'Asia', 'Kyrgyzstan': 'Asia', 'Laos': 'Asia',
        'Malaysia': 'Asia', 'Maldives': 'Asia', 'Mongolia': 'Asia',
        'Myanmar': 'Asia', 'Nepal': 'Asia', 'North Korea': 'Asia',
        'Pakistan': 'Asia', 'Philippines': 'Asia', 
        'Singapore': 'Asia', 'South Korea': 'Asia', 'Sri Lanka': 'Asia',
        'Tajikistan': 'Asia', 'Thailand': 'Asia', 'Timor-Leste': 'Asia',
        'Turkmenistan': 'Asia', 'Uzbekistan': 'Asia', 'Vietnam': 'Asia',
        
        # Middle East 
        'Bahrain': 'Middle East', 'Iran': 'Middle East', 'Iraq': 'Middle East', 
        'Israel': 'Middle East', 'Jordan': 'Middle East', 'Kuwait': 'Middle East', 
        'Lebanon': 'Middle East', 'Oman': 'Middle East', 'Palestine': 'Middle East', 
        'Occupied Palestinian Territories': 'Middle East', 'Palestinian Territories': 'Middle East',
        'Qatar': 'Middle East', 'Saudi Arabia': 'Middle East', 'Syria': 'Middle East',
        'Syrian Arab Republic': 'Middle East', 'Turkey': 'Middle East', 
        'United Arab Emirates': 'Middle East', 'UAE': 'Middle East', 'Yemen': 'Middle East',
        
        # Europe
        'Albania': 'Europe', 'Andorra': 'Europe', 'Armenia': 'Europe', 'Austria': 'Europe',
        'Azerbaijan': 'Europe', 'Belarus': 'Europe', 'Belgium': 'Europe',
        'Bosnia and Herzegovina': 'Europe', 'Bulgaria': 'Europe', 'Croatia': 'Europe',
        'Cyprus': 'Europe', 'Czech Republic': 'Europe', 'Denmark': 'Europe', 'Estonia': 'Europe',
        'Finland': 'Europe', 'France': 'Europe', 'Georgia': 'Europe', 'Germany': 'Europe',
        'Greece': 'Europe', 'Hungary': 'Europe', 'Iceland': 'Europe', 'Ireland': 'Europe',
        'Italy': 'Europe', 'Latvia': 'Europe', 'Liechtenstein': 'Europe', 'Lithuania': 'Europe',
        'Luxembourg': 'Europe', 'Malta': 'Europe', 'Moldova': 'Europe', 'Monaco': 'Europe',
        'Montenegro': 'Europe', 'Netherlands': 'Europe', 'North Macedonia': 'Europe',
        'Norway': 'Europe', 'Poland': 'Europe', 'Portugal': 'Europe', 'Romania': 'Europe',
        'Russia': 'Europe', 'San Marino': 'Europe', 'Serbia': 'Europe', 'Slovakia': 'Europe',
        'Slovenia': 'Europe', 'Spain': 'Europe', 'Sweden': 'Europe', 'Switzerland': 'Europe',
        'Ukraine': 'Europe', 'United Kingdom': 'Europe', 'UK': 'Europe', 'Vatican City': 'Europe',
        
        # Oceania
        'Australia': 'Oceania', 'Fiji': 'Oceania', 'Kiribati': 'Oceania',
        'Marshall Islands': 'Oceania', 'Micronesia': 'Oceania', 'Nauru': 'Oceania',
        'New Zealand': 'Oceania', 'Palau': 'Oceania', 'Papua New Guinea': 'Oceania',
        'Samoa': 'Oceania', 'Solomon Islands': 'Oceania', 'Tonga': 'Oceania',
        'Tuvalu': 'Oceania', 'Vanuatu': 'Oceania',
    }
    
    df['region'] = df['country'].map(lambda x: un_regions.get(x, 'Other'))
    
    # Y-axis dropdown
    y_dropdown = alt.binding_select(
        options=['total_affected', 'total_killed', 'total_wounded', 'total_kidnapped'],
        name='Y-Axis:'
    )
    y_selection = alt.selection_point(fields=['yvar'], bind=y_dropdown, value='total_affected')
    
    # Create a parameter for the color selection
    color_param = alt.param(name='color_choice', value='region',
                          bind=alt.binding_radio(options=['region', 'organization_type'], name='Color By:'))
    
    # The base dataset - melt for Y-axis variables
    df_long = df.melt(
        id_vars=['Date', 'country', 'region', 'un', 'icrc', 'nrcs_and_ifrc', 'ingo', 'nngo', 'other'],
        value_vars=['total_affected', 'total_killed', 'total_wounded', 'total_kidnapped'],
        var_name='yvar',
        value_name='yval'
    )
    
    # Create a combined dataset with an organization type column
    # First, create a list to hold all records
    all_records = []
    
    # Add region-colored data (use all records)
    for _, row in df_long.iterrows():
        new_row = row.copy()
        new_row['color_type'] = 'region'
        new_row['color_value'] = row['region']
        all_records.append(new_row)
    
    # Add organization-colored data (only where relevant)
    org_types = ['un', 'icrc', 'nrcs_and_ifrc', 'ingo', 'nngo', 'other']
    
    for _, row in df_long.iterrows():
        for org in org_types:
            if row[org] == 1:  # This organization was affected
                new_row = row.copy()
                new_row['color_type'] = 'organization_type'
                new_row['color_value'] = org
                all_records.append(new_row)
    
    # Create a new dataframe with all data
    combined_df = pd.DataFrame(all_records)
    
    # Define color schemes
    region_colors = {
        'Africa': 'blue',
        'Asia': 'orange',
        'Middle East': 'red',
        'Americas': 'green',
        'Europe': 'purple',
        'Oceania': 'teal',
        'Other': 'gray'
    }
    
    org_colors = {
        'un': '#1f77b4',        # Blue
        'icrc': '#d62728',      # Red
        'nrcs_and_ifrc': '#ff7f0e',  # Orange
        'ingo': '#2ca02c',      # Green
        'nngo': '#9467bd',      # Purple
        'other': '#7f7f7f'      # Gray
    }
    
    # Create a visualization with conditional color and filtering
    base_chart = alt.Chart(combined_df).mark_circle(opacity=0.6).encode(
        x=alt.X('Date:T', title="Date"),
        y=alt.Y('yval:Q', title='')
    ).add_params(
        y_selection,
        color_param
    ).transform_filter(
        y_selection
    ).transform_filter(
        "datum.color_type == color_choice"  # Only show points matching the color choice
    )
    
    # Create region-colored version
    region_chart = base_chart.encode(
        color=alt.condition(
            "datum.color_type == 'region'",
            alt.Color('color_value:N', 
                     scale=alt.Scale(domain=list(region_colors.keys()), 
                                    range=list(region_colors.values())),
                     legend=alt.Legend(title='Region', orient='top-left')),
            alt.value('gray')  # Fallback color (shouldn't be visible due to filter)
        ),
        tooltip=[
            'country',
            'region',
            'Date',
            alt.Tooltip('yval:Q', title='Value'),
            alt.Tooltip('yvar:N', title='Variable')
        ]
    )
    
    # Create organization-colored version
    org_chart = base_chart.encode(
        color=alt.condition(
            "datum.color_type == 'organization_type'",
            alt.Color('color_value:N', 
                     scale=alt.Scale(domain=list(org_colors.keys()), 
                                    range=list(org_colors.values())),
                     legend=alt.Legend(title='Organization Type', orient='top-left')),
            alt.value('gray')  # Fallback color (shouldn't be visible due to filter)
        ),
        tooltip=[
            'country',
            'region',
            'color_value',
            'Date',
            alt.Tooltip('yval:Q', title='Value'),
            alt.Tooltip('yvar:N', title='Variable')
        ]
    )
    
    # Add trend lines
    def make_trend(chart, condition):
        return chart.mark_line(color='darkred', size=3).encode(
            x='year:T',
            y='mean_val:Q'
        ).transform_filter(
            condition
        ).transform_timeunit(
            year='year(Date)'
        ).transform_aggregate(
            mean_val='mean(yval)',
            groupby=['year']
        )
    
    region_trend = make_trend(base_chart, "datum.color_type == 'region'")
    org_trend = make_trend(base_chart, "datum.color_type == 'organization_type'")
    
    # Combine all plots
    final_chart = alt.layer(
        region_chart,
        org_chart,
        region_trend,
        org_trend
    ).properties(
        width=800,
        height=400
    ).configure(
        font='Merriweather'
    )
    
    return final_chart

alt.data_transformers.disable_max_rows()

chart = create_viz2()
chart