Assignment 2 - Redesign a Notable Information Graphic

Introduction

On 28 March 1854 Britain and France declared war on Russia, and for the next two years British, French, Sardinian, and Turkish troops fought against Russians in the Crimean War. The loss of life in the war was colossal; of 1,650 000 soldiers who began the war (of all nations), 900,000 died. The majority of those who perished did not die from wounds; rather they died from diseases brought about by the terrible living conditions which they suffered.


Florence Nightingale was not only the first one to realize the role of hygiene and care for wounded soldiers to prevent the spread of disease and death, she also was the first to understand that sound decisions can only be made on data.


After the war, Nightingale returned to Great Britain and continued to fight for better conditions in hospitals. She created graphs, which are often described as roses or coxcombs (although she did not refer to them as such), to highlight the death toll from diseases above the death toll from wounds in the Crimean War.

from google.colab import drive drive.mount('/content/drive')
# @title variable_name = "" from IPython.display import Image, display from google.colab import drive drive.mount('/content/drive') display(Image("/content/drive/MyDrive/Visualization and Storytelling/Assignment 2/Nightingale's Coxcomb.png", width=1000, height=550))
Output image

Why is a redesign necessary?

For many readers, Nightingale’s coxcomb diagram can be challenging to interpret. Because this type of visualization is far less common than familiar formats such as line graphs or bar charts, most people are not accustomed to reading it.


The difficulty is further compounded by the fact that each chart combines data from two different years, which can be confusing for users with little experience in data visualization.

Redesign process

To address these issues, I redesigned the diagram using a more modern approach while preserving its distinctive visual identity.


I started by dividing the original graphic into three separate diagrams, one for each year, to make it easier for users to follow the timeline of the data.


Then, I incorporated an interactive legend that allows users to toggle between Disease, Wounds, and Other causes, along with hover effects that reveal the exact number of deaths for each wedge. I also standardized the layout so that all months appear in a clockwise sequence starting from January, aligning the design with contemporary visualization practices. Besides that, a few brief notes about the situation at the time were added below the annotation to provide an overview.


Finally, I consolidated the three diagrams into a single animated visualization controlled by a slider and play/pause buttons, enabling users to explore changes across years more engagingly and intuitively.

# @title import pandas as pd import numpy as np import plotly.graph_objects as go df = pd.read_csv("/content/drive/MyDrive/Visualization and Storytelling/Assignment 2/nightingale.csv") df = df[['Month', 'Year', 'Disease', 'Wounds', 'Other']] for cause in ["Disease", "Wounds", "Other"]: df[cause + "_radius"] = np.sqrt(df[cause]) df[cause + "_deaths"] = df[cause] colors = {"Disease": "skyblue", "Wounds": "deeppink", "Other": "black"} year_notes = { 1854: ( "<b>1854 — War Begins</b><br><br>" "- British & French forces arrive in Crimea.<br>" "- Hospitals are overwhelmed and unsanitary.<br>" "- Disease deaths dwarf combat casualties<br>" "almost immediately." ), 1855: ( "<b>1855 — Nightingale Intervenes</b><br><br>" "- Florence Nightingale arrives at Scutari.<br>" "- Sanitary Commission sent Feb 1855 flushes<br>" "sewers and improves ventilation.<br>" "- Disease mortality begins to plummet." ), 1856: ( "<b>1856 — War Ends</b><br><br>" "- Treaty of Paris signed March 1856.<br>" "- Death rates near peacetime levels.<br>" "- Nightingale's reforms prove that most<br>" "deaths were preventable." ), } def note_annotation(year): return dict( text=year_notes.get(year, ""), x=1.3, y=0.50, xref="paper", yref="paper", showarrow=False, align="left", font=dict(size=13, family="'Open Sans', serif", color="#333"), bgcolor="rgba(245,245,240,0.92)", bordercolor="#aaa", borderwidth=1, borderpad=10, width=280, ) def watermark_annotation(year): return dict( text=str(year), x=0.5, y=0.5, xref="paper", yref="paper", showarrow=False, font=dict(size=60, color="gray"), opacity=0.15, ) fig = go.Figure() years = sorted(df['Year'].unique()) for cause in ["Other", "Wounds", "Disease"]: subset = df[df['Year'] == years[0]] fig.add_trace(go.Barpolar( r=subset[cause + "_radius"], theta=subset['Month'], name=cause, marker_color=colors[cause], opacity=0.85, customdata=np.stack( ([cause.capitalize()] * len(subset), subset[cause + "_deaths"]), axis=-1 ), hovertemplate="%{theta}<br>%{customdata[0]}: %{customdata[1]} deaths<extra></extra>" )) frames = [] for year in years: subset = df[df['Year'] == year] frame_data = [] for cause in ["Other", "Wounds", "Disease"]: frame_data.append(go.Barpolar( r=subset[cause + "_radius"], theta=subset['Month'], marker_color=colors[cause], opacity=0.85, customdata=np.stack( ([cause.capitalize()] * len(subset), subset[cause + "_deaths"]), axis=-1 ), hovertemplate="%{theta}<br>%{customdata[0]}: %{customdata[1]} deaths<extra></extra>" )) frames.append(go.Frame( data=frame_data, name=str(year), layout=go.Layout(annotations=[ watermark_annotation(year), note_annotation(year), ]) )) fig.update(frames=frames) fig.update_layout( title={ "text": "Redesign of Nightingale's Coxcomb Diagram<br>" "<sup>Deaths of Soldiers by Month and Cause, 1854–1856</sup>", "x": 0.46, "xanchor": "center", "yanchor": "top", "font": dict(size=22, family="Arial, bold") }, template="plotly_white", showlegend=True, legend_title_text="Cause of Death", legend=dict( x=0.82, y=1, xanchor="left", yanchor="top" ), margin=dict(r=280), polar=dict( radialaxis=dict(showticklabels=False, ticks=''), angularaxis=dict( direction="clockwise", rotation=90, categoryorder="array", categoryarray=["Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"] ) ), annotations=[ watermark_annotation(years[0]), note_annotation(years[0]), ], updatemenus=[{ "buttons": [ {"args": [None, {"frame": {"duration": 1500, "redraw": True}, "fromcurrent": True, "transition": {"duration": 200, "easing": "cubic-in-out"}}], "label": "▶ Play", "method": "animate"}, {"args": [[None], {"frame": {"duration": 0, "redraw": True}, "mode": "immediate", "transition": {"duration": 0}}], "label": "⏸ Pause", "method": "animate"} ], "direction": "left", "pad": {"r": 10, "t": 40}, "showactive": True, "type": "buttons", "x": 0.1, "xanchor": "right", "y": 1.15, "yanchor": "top" }], sliders=[{ "steps": [ {"args": [[str(year)], {"frame": {"duration": 200, "redraw": True}, "mode": "immediate", "transition": {"duration": 200, "easing": "cubic-in-out"}}], "label": str(year), "method": "animate"} for year in years ], "x": 0.5, "xanchor": "center", "y": 0, "yanchor": "top", "len": 0.3, }], height=700 ) fig.show()

I also created a line chart to illustrate the overall trend that Nightingale intended to highlight from the beginning to the end of the study period. The line chart provides a straightforward, chronological view of mortality rates, making it easy for readers to see how deaths from Disease, Wounds, and Other causes changed over time. By adding a line boundary, the chart clearly reveals the dramatic spike in disease-related deaths before the Sanitary Commission and the significant decline that followed its intervention.

# @title import pandas as pd import plotly.graph_objects as go df = pd.read_csv("/content/drive/MyDrive/Visualization and Storytelling/Assignment 2/nightingale.csv") df['Date'] = pd.to_datetime(df['Date']) commission_start = pd.to_datetime("1855-03-01") fig = go.Figure() for cause, color in [("Disease", "skyblue"), ("Wounds", "deeppink"), ("Other", "black")]: fig.add_trace(go.Scatter( x=df['Date'], y=df[f'{cause}.rate'], name=cause, mode='lines+markers', line=dict(color=color, width=2), marker=dict(size=5), hovertemplate=f"<b>{cause}</b><br>Date: %{{x|%b %Y}}<br>Death Rate: %{{y:.1f}}<extra></extra>" )) fig.add_vrect( x0=df['Date'].min(), x1=commission_start, fillcolor="gray", opacity=0.15, layer="below", line_width=0 ) fig.add_vrect( x0=commission_start, x1=df['Date'].max(), fillcolor="white", opacity=0, layer="below", line_width=0 ) annotation_font_style = dict(size=13, family="Open Sans") x_before = df['Date'].min() + (commission_start - df['Date'].min()) / 2 fig.add_annotation( x=x_before, y=0.9, xref="x", yref="paper", text="Before Sanitary Commission", showarrow=False, font=annotation_font_style, xanchor="center", yanchor="middle" ) x_after = commission_start + (df['Date'].max() - commission_start) / 2 fig.add_annotation( x=x_after, y=0.9, xref="x", yref="paper", text="After Sanitary Commission", showarrow=False, font=annotation_font_style, xanchor="center", yanchor="middle" ) fig.add_vline( x=commission_start, line_width=1.5, line_dash="dash", line_color="gray" ) fig.update_layout( title=dict( text="<b>Causes of Mortality of the British Army in the East", font=dict(size=18, family="Open Sans"), x=0.5, xanchor="center" ), xaxis=dict(title="<b>Date", tickformat="%Y-%m", showgrid=True, gridcolor="rgba(0,0,0,0.1)"), yaxis=dict(title="<b>Annual Death Rate", showgrid=True, gridcolor="rgba(0,0,0,0.1)"), hovermode="x unified", font=dict(family="Open Sans"), legend=dict(x=1.01, y=1, xanchor="left"), plot_bgcolor="white", paper_bgcolor="white", height=500, margin=dict(r=120) ) fig.show()