Skip to Content
L’échange d’apprentissage de HIC débute le 13 Juillet 2026. Voir le programme
AnalytiqueAnalyse spatiale et SIGGuide de visualisation

Guide de visualisation

Une bibliothèque de modèles de graphiques stylisés et prêts pour la présentation, destinés aux rapports de santé, disponibles en Python (Matplotlib) et R (ggplot2). Chaque entrée est complète et autonome : dépliez Afficher le code, choisissez votre langage, et tout ce qui est nécessaire à la reproduction du résultat affiché en dessous s’y trouve — imports, données, style et mise en page. Remplacez le dataframe d’exemple par vos propres données et le style est conservé. Les types de graphiques et le style suivent la charte graphique du HIC. Le notebook source Python est analysis/spatial-analysis/Bar-Plots.ipynb.

Les jeux de données de ces exemples sont des données fictives d’illustration, utilisées uniquement pour démontrer la mise en page et le style. Ils ne contiennent aucune donnée réelle de santé, de patients ou d’établissements.

Barres horizontales

Barres horizontales avec une catégorie mise en évidence

Classe des catégories nommées et attire l’œil sur une seule ligne grâce à une couleur d’accent unique, le reste servant de contexte neutre.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import pandas as pd # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default. " "Install it and re-run for the exact look.") # Data HIGHLIGHT = "Spain" COLOR_DEFAULT = "#1a4a7a" COLOR_HIGHLIGHT = "#5fc4c0" BG_COLOR = "#dce8f0" df = pd.DataFrame({ "country": ["Germany", "Spain", "Austria", "France", "Luxembourg", "Portugal", "Britain", "Denmark", "Sweden"], "consultations_per_person": [8.2, 7.5, 6.8, 6.7, 6.3, 5.8, 5.1, 4.7, 2.9], }) df["highlight"] = df["country"] == HIGHLIGHT df["color"] = df["highlight"].map({True: COLOR_HIGHLIGHT, False: COLOR_DEFAULT}) df["y_pos"] = range(len(df) - 1, -1, -1) # top-to-bottom order print(df.to_string(index=False)) fig, ax = plt.subplots(figsize=(7, 5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) # Bars ax.barh(df["y_pos"], df["consultations_per_person"], color=df["color"], height=0.6) # Axes ax.set_xlim(0, 8.5) ax.set_xticks([0, 2, 4, 6, 8]) ax.xaxis.set_tick_params(labelsize=9, colors="#555555") ax.xaxis.tick_top() ax.set_yticks(df["y_pos"]) ax.set_yticklabels( [f"$\\bf{{{c}}}$" if c == HIGHLIGHT else c for c in df["country"]], fontsize=10 ) ax.tick_params(axis="y", length=0) # Grid & spines ax.xaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) ax.tick_params(axis="x", length=0) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.055, 0.016, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.965, "Salud", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.930, "Doctors' consultations per person", fontsize=9, va="top", color="#333333", transform=fig.transFigure) fig.text(0.02, 0.905, "Selected countries, 2009", fontsize=8.5, va="top", color="#555555", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: Health Management Information System", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.79, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) # rect=[left, bottom, right, top] — lower 'top' pushes the plot area down plt.tight_layout(rect=[0, 0.03, 1, 0.78]) plt.show()
charts/doctors_consultations.png

Barres horizontales, deux séries

Compare deux mesures par catégorie (par exemple deux périodes ou deux indicateurs) à l’aide de barres appariées.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import pandas as pd import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default.") # Data df = pd.DataFrame({ "country": ["Rubavu", "Rusizi", "Bugesera", "Nyabihu", "Musanze", "Burera", "Rulindo", "Gasabo", "Nyamasheke"], "middle_class": [80, 72, 60, 58, 55, 50, 62, 50, 48], "low_income": [68, 63, 50, 62, 38, 42, 55, 48, 42], }) # Reverse so Chile appears on top df = df.iloc[::-1].reset_index(drop=True) print(df.to_string(index=False)) # Colors COLOR_MIDDLE = "#1a4a7a" COLOR_LOW = "#5fc4c0" BG_COLOR = "#dce8f0" # Layout n = len(df) y = np.arange(n) height = 0.35 # bar height for each trace fig, ax = plt.subplots(figsize=(7, 5.5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) # Bars ax.barh(y + height / 2, df["middle_class"], height=height, color=COLOR_MIDDLE, label="Middle class") ax.barh(y - height / 2, df["low_income"], height=height, color=COLOR_LOW, label="Low income") # Axes ax.set_xlim(0, 85) ax.set_xticks([0, 20, 40, 60, 80]) ax.xaxis.set_tick_params(labelsize=9, colors="#555555") ax.xaxis.tick_top() ax.set_yticks(y) ax.set_yticklabels(df["country"], fontsize=10, fontweight="bold") ax.tick_params(axis="y", length=0) # Grid & spines ax.xaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) ax.tick_params(axis="x", length=0) # Legend legend = ax.legend( loc="lower right", fontsize=9, frameon=False, handlelength=1.2, handleheight=0.9, handletextpad=0.5, ) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.965), 0.045, 0.016, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.955, "Defenders of democracy", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.915, "Respondents saying that honest elections held regularly\n" "with a choice of at least two political parties are very\n" "important, 2007, %", fontsize=8.5, va="top", color="#444444", transform=fig.transFigure, linespacing=1.5) # Footer fig.text(0.02, 0.02, "Source: Pew Global Attitudes Survey", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.72, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.03, 1, 0.78]) plt.show()
charts/defenders_of_democracy.png

Barres horizontales groupées avec sous-catégories

Décompose chaque catégorie en barres de sous-catégories groupées, accompagnées d’étiquettes de groupe.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import matplotlib.text as mtext import pandas as pd import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default.") # Data: two forecast vintages (June / September) per district and year df = pd.DataFrame({ "country": ["Burera", "Burera", "Nyabihu", "Nyabihu", "Rulindo", "Rulindo", "Gasabo", "Gasabo", "Musanze", "Musanze"], "year": [2011, 2012] * 5, "june": [3.0, 1.8, 2.1, 1.4, 2.0, 1.5, 0.8, 1.1, 1.0, 0.3], "september": [2.7, 1.3, 1.7, 1.0, 1.6, 1.1, 0.7, 0.3, 0.6, 0.1], }) print(df.to_string(index=False)) # Colors COLOR_JUNE = "#1a4a7a" COLOR_SEP = "#5fc4c0" BG_COLOR = "#dce8f0" # Build y positions # Layout (bottom→top): Italy_2012, Italy_2011, gap, Spain_2012, Spain_2011 … # Each year-row occupies 2 bar slots stacked: june on top, sep below. # june centre = y_row + BAR_H/2 # sep centre = y_row - BAR_H/2 BAR_H = 0.30 # height of each individual bar ROW_STEP = BAR_H * 2 + 0.05 # vertical space per year-row GROUP_GAP = 0.30 # extra gap between country groups countries = df["country"].unique() # original order: Germany … Italy positions = [] y = 0 # Build bottom-up: last country first, within each country 2012 first for country in reversed(countries): sub = df[df["country"] == country].sort_values("year", ascending=False) # 2012, 2011 # Place 2012 at bottom of group, 2011 above it for _, row in sub.iterrows(): positions.append({ "country": country, "year": int(row["year"]), "june": row["june"], "sep": row["september"], "y": y }) y += ROW_STEP y += GROUP_GAP pos_df = pd.DataFrame(positions) # After building bottom-up: lower y = 2012, higher y = 2011 ✓ # Plot fig, ax = plt.subplots(figsize=(6, 6.5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) for _, r in pos_df.iterrows(): # June on top (+BAR_H/2), September below (-BAR_H/2) ax.barh(r["y"] + BAR_H / 2, r["june"], height=BAR_H, color=COLOR_JUNE, align="center") ax.barh(r["y"] - BAR_H / 2, r["sep"], height=BAR_H, color=COLOR_SEP, align="center") # Y-axis: year labels only, country drawn via ax.text ax.set_yticks(pos_df["y"]) ax.set_yticklabels([str(r["year"]) for _, r in pos_df.iterrows()], fontsize=9, color="#555555") ax.tick_params(axis="y", length=0, pad=2) # Draw country name centred between its two year rows for country in countries: rows = pos_df[pos_df["country"] == country] mid_y = rows["y"].mean() ax.text(-0.18, mid_y, country, ha="right", va="center", fontsize=9, fontweight="bold", transform=ax.get_yaxis_transform()) # X-axis ax.set_xlim(0, 3.3) ax.set_xticks([0, 1, 2, 3]) ax.xaxis.set_tick_params(labelsize=9, colors="#555555") ax.xaxis.tick_top() ax.tick_params(axis="x", length=0) # Grid & spines ax.xaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Legend from matplotlib.patches import Patch legend_elements = [Patch(facecolor=COLOR_JUNE, label="June"), Patch(facecolor=COLOR_SEP, label="September")] ax.legend(handles=legend_elements, loc="lower right", fontsize=8, frameon=False, title="Forecasts made in:", title_fontsize=8) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.045, 0.013, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Autumn fall", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.936, "GDP, % increase on previous year", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: HMIS", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.79, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.03, 1, 0.80]) plt.show()
charts/autumn_fall_gdp.png

Barres horizontales classées avec mise en évidence

Une liste classée plus longue où une seule barre porte le message.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import pandas as pd import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default.") # Data df = pd.DataFrame({ "sector": ["Internet", "Software", "Tech hardware", "IT services", "Health care", "Total", "Leisure", "Chemicals", "Oil, gas and fuels", "Metals and mining", "Property", "Energy equipment"], "change": [57, 34, 20, 16, 10, 4, -3, -5, -18, -22, -28, -58], "bold": [False, False, False, False, False, True, False, False, False, False, False, False], }) # Reverse so Internet is on top df = df.iloc[::-1].reset_index(drop=True) print(df.to_string(index=False)) # Colors COLOR_BAR = "#1a4a7a" COLOR_ZERO = "#c0392b" BG_COLOR = "#dce8f0" # Plot fig, ax = plt.subplots(figsize=(6, 6.5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) y = np.arange(len(df)) ax.barh(y, df["change"], color=COLOR_BAR, height=0.6, zorder=2) # Zero line (red) ax.axvline(x=0, color=COLOR_ZERO, linewidth=1.5, zorder=3) # Axes ax.set_xlim(-65, 65) ax.set_xticks([-60, -40, -20, 0, 20, 40, 60]) ax.xaxis.set_tick_params(labelsize=9, colors="#555555") ax.xaxis.tick_top() ax.tick_params(axis="x", length=0) ax.set_yticks(y) ax.set_yticklabels(df["sector"], fontsize=10) ax.tick_params(axis="y", length=0) # Bold "Total" label for label, (_, row) in zip(ax.get_yticklabels(), df.iterrows()): if row["bold"]: label.set_fontweight("bold") # Grid & spines ax.xaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.045, 0.013, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Another dotcom boom", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.935, "Change in worldwide capital spending*, %", fontsize=9, va="top", color="#444444", transform=fig.transFigure) fig.text(0.02, 0.912, "2014-17 forecast", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: Goldman Sachs", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.68, 0.02, "*In dollar terms", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.03, 1, 0.78]) plt.show()
charts/dotcom_boom_capex.png

Colonnes verticales

Graphique en colonnes verticales

Catégories ordonnées ou courte série temporelle se lisant de gauche à droite.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import pandas as pd # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default.") # Data df = pd.DataFrame({ "year": ["2004","06","08","10","11","12","13","14","15*"], "gdp": [ 11.7, 12.6, 11.2, 10.0, 11.2, 10.9, 9.7, 10.3, 8.7], "estimate": [ False, False, False, False, False, False, False, False, True], }) # Colors COLOR_DEFAULT = "#1a4a7a" COLOR_ESTIMATE = "#5fc4c0" BG_COLOR = "#dce8f0" df["color"] = df["estimate"].map({False: COLOR_DEFAULT, True: COLOR_ESTIMATE}) print(df.to_string(index=False)) # Plot fig, ax = plt.subplots(figsize=(5, 5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) x = range(len(df)) ax.bar(x, df["gdp"], color=df["color"], width=0.75, zorder=2) # Axes ax.set_xlim(-0.5, len(df) - 0.5) ax.set_ylim(0, 14) ax.set_yticks([0, 2, 4, 6, 8, 10, 12, 14]) ax.yaxis.set_tick_params(labelsize=9, colors="#555555") ax.yaxis.tick_right() # y-axis on the right ax.tick_params(axis="y", length=0) ax.set_xticks(list(x)) ax.set_xticklabels(df["year"], fontsize=9, color="#555555") ax.tick_params(axis="x", length=0) # Grid & spines ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.055, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "African lion", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.935, "Ethiopia's GDP, % change on a year earlier", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: IMF", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.02, 0.055, "*Estimate", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.79, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.06, 1, 0.80]) plt.show()
charts/african_lion_gdp.png

Graphique en colonnes verticales, style alternatif

Le même motif de colonnes avec une palette et une mise en évidence différentes.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import matplotlib.patches as mpatches import pandas as pd import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default.") # Data df = pd.DataFrame({ "period": ["20 years", "10 years", "5 years"], "equities": [8.5, 7.5, 8.5], "all_property": [9.5, 8.0, 13.5], "rural": [14.5, 13.5, 13.0], "forestry": [11.0, 18.5, 20.5], }) print(df.to_string(index=False)) # Colors COLORS = { "equities": "#1a4a7a", "all_property": "#5fc4c0", "rural": "#c9a84c", "forestry": "#2d7a4a", } LABELS = { "equities": "Equities", "all_property": "All property", "rural": "Rural property", "forestry": "Forestry", } BG_COLOR = "#dce8f0" keys = list(COLORS.keys()) n_groups = len(df) n_bars = len(keys) width = 0.18 # width of each bar group_w = width * n_bars + 0.08 # total group width incl. gap x = np.arange(n_groups) # Plot fig, ax = plt.subplots(figsize=(6, 5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) for i, key in enumerate(keys): offsets = x - (n_bars - 1) * width / 2 + i * width ax.bar(offsets, df[key], width=width, color=COLORS[key], label=LABELS[key], zorder=2) # Axes ax.set_xlim(-0.5, n_groups - 0.5) ax.set_ylim(0, 22) ax.set_yticks([0, 5, 10, 15, 20]) ax.yaxis.set_tick_params(labelsize=9, colors="#555555") ax.yaxis.tick_right() ax.tick_params(axis="y", length=0) ax.set_xticks(x) ax.set_xticklabels(df["period"], fontsize=9, color="#555555") ax.tick_params(axis="x", length=0) # Grid & spines ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Legend handles = [mpatches.Patch(facecolor=COLORS[k], label=LABELS[k]) for k in keys] ax.legend(handles=handles, ncol=2, fontsize=8, frameon=False, loc="upper left", bbox_to_anchor=(0, 1.02), handlelength=1.0, handleheight=0.9, columnspacing=1.0) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.055, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "The mighty jungle", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.935, "Britain, annualised rate of return\non investment*, %", fontsize=9, va="top", color="#444444", transform=fig.transFigure, linespacing=1.5) # Footer fig.text(0.02, 0.02, "Sources: MSCI; JP Morgan", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.75, 0.02, "*To 2014", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.79, 0.055, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.06, 1, 0.75]) plt.show()
charts/mighty_jungle_returns.png

Graphique en colonnes verticales avec une barre mise en évidence

Un graphique en colonnes qui met en évidence une seule période ou catégorie.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import matplotlib.patches as mpatches import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT print(f"Using font: {FONT}") else: print("Gill Sans not found – using matplotlib default.") # Data # Approximate net private capital inflows/outflows in $bn, 1994–2011 # Values estimated from the chart image years = [ 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 ] values = [ -4, -5, -23, -18, -8, -18, -25, -15, -8, -7, -35, -30, 40, 80, -130, -50, -35, 5 ] # Two-tone: teal for positive inflows, darker teal for outflows (negative) # The chart uses a medium teal for most bars and a lighter teal for the # 2006-2008 positive bars — we'll use a single accent for positive values COLOR_NEG = "#3a9aa0" # muted teal (outflows) COLOR_POS = "#1a6a7a" # darker teal (inflows) COLOR_ZERO = "#c0392b" # red zero line BG_COLOR = "#dce8f0" # light blue-grey house background bar_colors = [COLOR_POS if v >= 0 else COLOR_NEG for v in values] # Plot fig, ax = plt.subplots(figsize=(5, 5.5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) x = np.arange(len(years)) ax.bar(x, values, color=bar_colors, width=0.75, zorder=2) # Zero / baseline (red) ax.axhline(y=0, color=COLOR_ZERO, linewidth=1.5, zorder=3) # Axes ax.set_ylim(-160, 120) ax.set_yticks([100, 50, 0, -50, -100, -150]) ax.yaxis.set_tick_params(labelsize=9, colors="#555555") ax.yaxis.tick_right() ax.tick_params(axis="y", length=0) # X-axis: show subset of year labels to match original label_map = { 1994: "1994", 1996: "96", 1998: "98", 2000: "2000", 2002: "02", 2004: "04", 2006: "06", 2008: "08", 2011: "11*" } xtick_positions = [i for i, yr in enumerate(years) if yr in label_map] xtick_labels = [label_map[years[i]] for i in xtick_positions] ax.set_xticks(xtick_positions) ax.set_xticklabels(xtick_labels, fontsize=8.5, color="#555555") ax.tick_params(axis="x", length=0) ax.xaxis.tick_bottom() # Grid & spines ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Red badge (house style) fig.add_artist(plt.Rectangle( (0.02, 0.975), 0.045, 0.013, transform=fig.transFigure, color="#c0392b", clip_on=False )) # Title block fig.text(0.02, 0.968, "The money drain", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.935, "Net private capital inflows/outflows, $bn", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: Central Bank of Russia", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.55, 0.02, "*To June 30th", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.04, 1, 0.80]) plt.show()
charts/money_drain.png

Colonnes empilées

Montre comment un total se décompose en parties au fil du temps. Notre charte graphique limite les graphiques empilés à quatre catégories avec la palette standard — au-delà, remplacez-les par d’autres couleurs et utilisez le gris pour « Autres ».

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import pandas as pd import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT # Data df = pd.DataFrame({ "year": np.arange(2008, 2018), "paid_search": [22, 25, 29, 34, 40, 47, 55, 64, 74, 85], "display": [10, 11, 13, 15, 18, 21, 25, 30, 36, 43], "classified": [8, 8, 9, 10, 11, 12, 13, 14, 15, 16], "online_video": [2, 3, 4, 6, 8, 11, 15, 20, 27, 35], }) # Colors (house stack order: darkest at the bottom) COLORS = { "paid_search": "#1a4a7a", "display": "#5fc4c0", "classified": "#c9a84c", "online_video": "#2d7a4a", } LABELS = { "paid_search": "Paid search", "display": "Display", "classified": "Classified", "online_video": "Online video", } BG_COLOR = "#dce8f0" print(df.to_string(index=False)) fig, ax = plt.subplots(figsize=(6, 4.8)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) # Forecast panel behind the last two columns ax.axvspan(2015.5, 2017.6, color="#c3d6e4", zorder=1) ax.text(2016.55, 196, "FORECAST", fontsize=7.5, color="#555555", ha="center", va="top") # Stacked bars bottom = np.zeros(len(df)) for key, color in COLORS.items(): ax.bar(df["year"], df[key], bottom=bottom, color=color, width=0.75, label=LABELS[key], zorder=2) bottom += np.asarray(df[key], dtype=float) # Axes ax.set_xlim(2007.4, 2017.6) ax.set_xticks([2008, 2010, 2012, 2014, 2016, 2017]) ax.set_xticklabels(["2008", "10", "12", "14", "16", "17"]) ax.set_ylim(0, 210) ax.set_yticks([0, 50, 100, 150, 200]) ax.yaxis.tick_right() ax.xaxis.set_tick_params(labelsize=9, colors="#555555", length=0) ax.yaxis.set_tick_params(labelsize=9, colors="#555555", length=0) # Grid & spines ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Legend ax.legend(loc="upper left", frameon=False, fontsize=8, handlelength=1.0, handleheight=0.9, labelspacing=0.4) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.05, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Getting sociable", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.933, "Global internet-advertising spending, $bn", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: ZenithOptimedia", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.72, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.05, 1, 0.82]) plt.show()
charts/getting_sociable_adspend.png

Courbes

Courbe, deux séries

Le choix par défaut pour les séries temporelles. La palette maison ordonne les couleurs des courbes ainsi : bleu foncé, puis sarcelle, puis doré — jusqu’à six séries, quatre étant un plafond pratique.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT # Data — yields over the 13 years following each period start years = np.arange(1, 14) japan = np.array([8.0, 7.2, 6.1, 5.2, 4.4, 3.5, 3.0, 2.4, 1.9, 1.7, 1.5, 1.4, 1.3]) us = np.array([6.6, 6.0, 5.2, 4.6, 4.2, 4.4, 4.7, 4.2, 3.6, 3.2, 2.6, 2.2, 2.0]) # Colors COLOR_JAPAN = "#1a4a7a" COLOR_US = "#5fc4c0" BG_COLOR = "#dce8f0" fig, ax = plt.subplots(figsize=(6, 4.8)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) ax.plot(years, japan, color=COLOR_JAPAN, linewidth=2.2, solid_capstyle="round", label="January 1989 (Japan)", zorder=3) ax.plot(years, us, color=COLOR_US, linewidth=2.2, solid_capstyle="round", label="January 1999 (US)", zorder=3) # Axes ax.set_xlim(0.6, 13.4) ax.set_xticks([1, 5, 10, 13]) ax.set_xlabel("Years since start", fontsize=9, color="#555555") ax.set_ylim(0, 8.4) ax.set_yticks([0, 2, 4, 6, 8]) ax.yaxis.tick_right() ax.xaxis.set_tick_params(labelsize=9, colors="#555555", length=0) ax.yaxis.set_tick_params(labelsize=9, colors="#555555", length=0) # Grid & spines ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Legend under the title block fig.text(0.02, 0.885, "Period beginning:", fontsize=8.5, va="top", color="#444444", transform=fig.transFigure) fig.legend(loc="upper left", bbox_to_anchor=(0.02, 0.87), frameon=False, fontsize=8.5, ncol=2, handlelength=1.4, columnspacing=1.2) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.05, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Where Tokyo leads", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.933, "Ten-year government-bond yields, %", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: Thomson Reuters", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.72, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.05, 1, 0.76]) plt.show()
charts/where_tokyo_leads.png

Aires empilées

Montre un total et sa composition en même temps. Étiquetez les bandes directement sur le graphique plutôt que d’utiliser une légende.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT # Data years = np.arange(2002, 2012) china = np.array([0.3, 0.4, 0.6, 0.8, 1.1, 1.5, 1.9, 2.4, 2.9, 3.2]) other = np.array([0.9, 1.0, 1.2, 1.5, 1.8, 2.2, 2.4, 2.6, 3.0, 3.3]) # Colors COLOR_CHINA = "#1a4a7a" COLOR_OTHER = "#5fc4c0" BG_COLOR = "#dce8f0" fig, ax = plt.subplots(figsize=(5.5, 4.8)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) ax.stackplot(years, china, other, colors=[COLOR_CHINA, COLOR_OTHER], zorder=2) # In-chart labels instead of a legend ax.text(2009.6, 1.2, "China", color="white", fontsize=9, fontweight="bold", ha="center", zorder=3) ax.text(2009.2, 4.3, "Other\ndeveloping\ncountries", color="#0e3f4a", fontsize=9, fontweight="bold", ha="center", linespacing=1.3, zorder=3) # Axes ax.set_xlim(2002, 2011) ax.set_xticks([2002, 2004, 2006, 2008, 2010, 2011]) ax.set_xticklabels(["2002", "04", "06", "08", "10*", "11*"]) ax.set_ylim(0, 6.6) ax.set_yticks([0, 2, 4, 6]) ax.yaxis.tick_right() ax.xaxis.set_tick_params(labelsize=9, colors="#555555", length=0) ax.yaxis.set_tick_params(labelsize=9, colors="#555555", length=0) # Grid & spines ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.05, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Foreign hoards", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.933, "Developing-country currency reserves\n$trn", fontsize=9, va="top", color="#444444", transform=fig.transFigure, linespacing=1.4) # Footer fig.text(0.02, 0.02, "Source: IMF", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.78, 0.02, "*Forecast", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.05, 1, 0.80]) plt.show()
charts/foreign_hoards_reserves.png

Nuage de points

Nuage de points annoté

Met en relation deux variables continues, en étiquetant les points directement plutôt qu’au moyen d’une légende.

Afficher le code

import matplotlib matplotlib.rcParams['text.usetex'] = False import matplotlib.pyplot as plt import matplotlib.font_manager as fm import matplotlib.patches as mpatches import numpy as np available = {f.name for f in fm.fontManager.ttflist} FONT = next((f for f in ["Gill Sans","Gill Sans MT","URW Classico","Trebuchet MS"] if f in available), None) if FONT: plt.rcParams["font.family"] = FONT C_MW = "#8b3030" # Midwest — dark red (highlighted) C_OTHER = "#3bbfbf" # All other — teal C_GRAY = "#9e9e9e" # Large cities — grey BG = "#dce8f0" RED_LINE= "#c0392b" RED_BAR = "#c0392b" np.random.seed(42) def make_counties(n, health_mu, health_sd, swing_mu, swing_sd, size_mu, size_sd): """Simulate county-level data. Replace with real arrays when available.""" health = np.random.normal(health_mu, health_sd, n) swing = np.random.normal(swing_mu, swing_sd, n) size = np.abs(np.random.normal(size_mu, size_sd, n)) + 10 return health, swing, size # Other regions (teal) — spread across full x-range, mixed swing h_ot, s_ot, sz_ot = make_counties(600, 48, 8, -2, 16, 20, 18) # Midwest (dark red) — clustered at lower health, stronger R swing h_mw, s_mw, sz_mw = make_counties(450, 38, 5, 16, 10, 18, 14) # Large cities (grey) — few, larger bubbles, less partisan swing h_gr = np.array([35, 45, 52, 58, 40, 30 ]) s_gr = np.array([-5, -8, -12, -2, -15, -3 ]) sz_gr = np.array([180, 220, 300, 250, 160, 140 ]) # Named/highlighted counties — white-outlined dots with leader lines named = { "Jefferson, OH": (32, 42), "Knox, OH": (43, 32), } # Figure fig, ax = plt.subplots(figsize=(7.4, 5.6)) fig.patch.set_facecolor(BG) ax.set_facecolor(BG) fig.subplots_adjust(left=0.11, right=0.76, top=0.77, bottom=0.18) # Layer order: other (bottom) → grey cities → Midwest (top) ax.scatter(h_ot, s_ot, s=sz_ot, color=C_OTHER, alpha=0.55, linewidths=0, zorder=2) ax.scatter(h_gr, s_gr, s=sz_gr, color=C_GRAY, alpha=0.65, linewidths=0.5, edgecolors='white', zorder=3) ax.scatter(h_mw, s_mw, s=sz_mw, color=C_MW, alpha=0.60, linewidths=0, zorder=4) # Named counties — white-filled dot with dark outline + leader line + label for name, (x, y) in named.items(): ax.scatter(x, y, s=38, color='white', linewidths=1.2, edgecolors=C_MW, zorder=6) ox = 1.2 if "Knox" in name else 1.0 ax.annotate(name, xy=(x, y), xytext=(x + ox, y + 3.5), fontsize=8, color="#1a1a1a", zorder=7, arrowprops=dict(arrowstyle="-", color="#555555", lw=0.7)) # Region label inside cluster ax.text(37, 14, "Midwest", fontsize=13, fontweight="bold", color="black", alpha=0.85, zorder=5, style='italic') # Zero reference line ax.axhline(0, color=RED_LINE, linewidth=1.0, zorder=2) # Axes ax.set_xlim(18, 72) ax.set_ylim(-55, 55) ax.set_xticks([20, 30, 40, 50, 60, 70]) ax.set_yticks([-50, -25, 0, 25, 50]) ax.xaxis.set_tick_params(labelsize=8.5, colors="#555555", length=0) ax.yaxis.set_tick_params(labelsize=8.5, colors="#555555", length=0) ax.xaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.yaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for sp in ax.spines.values(): sp.set_visible(False) # Axis labels ax.set_xlabel("Index of county health metrics*", fontsize=9, color="#444444", labelpad=22) ax.text(0.02, -0.13, "◄ WORSE HEALTH", fontsize=7.5, color="#555555", transform=ax.transAxes, va='top') ax.text(0.98, -0.13, "BETTER HEALTH ►", fontsize=7.5, color="#555555", ha='right', transform=ax.transAxes, va='top') ax.text(-0.10, 0.75, "MORE REPUBLICAN", fontsize=7.5, color="#555555", transform=ax.transAxes, va='center', ha='center', rotation=90) ax.text(-0.10, 0.25, "LESS REPUBLICAN", fontsize=7.5, color="#555555", transform=ax.transAxes, va='center', ha='center', rotation=90) # Right-side y-axis description fig.text(0.77, 0.77, "Change in Republican margin over\nDemocrats, 2012-16, % points", fontsize=7.8, color="#444444", va="top", ha="left", linespacing=1.4, transform=fig.transFigure) # Bubble size legend lx, ly = 0.790, 0.62 circ = mpatches.Circle((lx + 0.025, ly - 0.018), 0.026, transform=fig.transFigure, fill=False, edgecolor="#555555", linewidth=0.9, clip_on=False) fig.add_artist(circ) fig.text(lx + 0.060, ly, "Voting\neligible\npopulation", fontsize=7.5, color="#1a1a1a", va="center", transform=fig.transFigure, linespacing=1.3) # Red badge + Title fig.add_artist(plt.Rectangle((0.06, 0.942), 0.038, 0.008, transform=fig.transFigure, color=RED_BAR, clip_on=False)) fig.text(0.06, 0.938, "Vitality and the vote", fontsize=11, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.06, 0.910, "United States, health metrics against swing to Donald Trump", fontsize=8.5, va="top", color="#444444", transform=fig.transFigure) fig.text(0.06, 0.890, "By county", fontsize=8, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.06, 0.022, "Sources: Atlas of US Presidential Elections; Census Bureau;\n" "IPUMS, University of Minnesota; Institute for Health Metrics\n" "and Evaluation; Health Intelligence Center", fontsize=7, color="#666666", transform=fig.transFigure, linespacing=1.4) fig.text(0.52, 0.022, "*Weighted index of obesity, diabetes,\nheavy drinking, physical exercise and\nlife expectancy, 2010-12", fontsize=7, color="#666666", transform=fig.transFigure, linespacing=1.4) print("Saved: vitality_vote_midwest.png")
charts/vitality_vote_midwest.png

Thermomètre

Graphique à intervalle de points

Compare deux valeurs par catégorie — deux périodes, deux scénarios — avec des extrémités en points reliées par une ligne d’intervalle. Notre charte graphique recommande les extrémités en points lorsque de simples lignes thermomètre deviennent peu lisibles.

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm import pandas as pd import numpy as np # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT # Data df = pd.DataFrame({ "district": ["Gasabo", "Kicukiro", "Nyarugenge", "Musanze", "Rubavu", "Huye", "Rusizi", "Nyagatare", "Burera", "Rwamagana"], "june": [310, 285, 300, 240, 255, 220, 205, 190, 175, 160], "september": [385, 350, 330, 310, 290, 270, 240, 250, 210, 205], }) # Colors COLOR_JUNE = "#5fc4c0" COLOR_SEP = "#1a4a7a" COLOR_RANGE = "#9bb8c8" BG_COLOR = "#dce8f0" print(df.to_string(index=False)) fig, ax = plt.subplots(figsize=(6, 5)) fig.patch.set_facecolor(BG_COLOR) ax.set_facecolor(BG_COLOR) # Range lines with dot terminals y = np.arange(len(df))[::-1] ax.hlines(y, df["june"], df["september"], color=COLOR_RANGE, linewidth=1.6, zorder=2) ax.scatter(df["june"], y, s=42, color=COLOR_JUNE, zorder=3, label="June") ax.scatter(df["september"], y, s=42, color=COLOR_SEP, zorder=3, label="September") # Axes ax.set_xlim(0, 420) ax.set_xticks([0, 100, 200, 300, 400]) ax.xaxis.tick_top() ax.xaxis.set_tick_params(labelsize=9, colors="#555555", length=0) ax.set_yticks(y) ax.set_yticklabels(df["district"], fontsize=10) ax.tick_params(axis="y", length=0) # Grid & spines ax.xaxis.grid(True, color="white", linewidth=0.8, zorder=0) ax.set_axisbelow(True) for spine in ax.spines.values(): spine.set_visible(False) # Legend ax.legend(loc="lower right", frameon=False, fontsize=8.5, handletextpad=0.3, borderaxespad=0.2) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.05, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Catching up", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.933, "Outpatient consultations per 1,000 population*", fontsize=9, va="top", color="#444444", transform=fig.transFigure) # Footer fig.text(0.02, 0.02, "Source: HMIS", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.68, 0.02, "*Illustrative data", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0, 0.04, 1, 0.82]) plt.show()
charts/thermometer_consultations.png

Camembert / anneau

Graphique en anneau

Utilisez les camemberts et anneaux avec prudence : au-delà de quatre catégories, préférez des barres empilées. Limitez-vous à six parts au maximum, ordonnez-les dans le sens horaire à partir de midi et utilisez le gris pour « Autres ».

Afficher le code

import matplotlib.pyplot as plt import matplotlib.font_manager as fm # Font setup available = {f.name for f in fm.fontManager.ttflist} FONT = next( (f for f in ["Gill Sans", "Gill Sans MT", "URW Classico", "Trebuchet MS"] if f in available), None ) if FONT: plt.rcParams["font.family"] = FONT # Data labels = ["Jaguar Land Rover", "Tata Steel", "Tata Consultancy Services", "Tata Chemicals", "Taj (hotels)", "Others"] values = [19.0, 19.4, 4.9, 0.5, 0.4, 1.2] # Colors — standard order, grey last for "Others" colors = ["#1a4a7a", "#5fc4c0", "#c9a84c", "#2d7a4a", "#8b3030", "#b8c4cc"] BG_COLOR = "#dce8f0" fig, ax = plt.subplots(figsize=(6.5, 4.4)) fig.patch.set_facecolor(BG_COLOR) # Doughnut: a pie with a wedge width < 1 wedges, _ = ax.pie(values, colors=colors, startangle=90, counterclock=False, wedgeprops={"width": 0.42, "edgecolor": BG_COLOR, "linewidth": 1}) ax.text(0, 0, "Total\n45.4", ha="center", va="center", fontsize=11, fontweight="bold", color="#1a1a1a", linespacing=1.2) # Color-keyed labels with values, house style legend_labels = [f"{l} {v}" for l, v in zip(labels, values)] ax.legend(wedges, legend_labels, loc="center left", bbox_to_anchor=(1.0, 0.5), frameon=False, fontsize=8.5, labelspacing=0.8, handlelength=0.8, handleheight=1.1) # Red badge fig.add_artist(plt.Rectangle((0.02, 0.975), 0.045, 0.014, transform=fig.transFigure, color="#c0392b", clip_on=False)) # Title block fig.text(0.02, 0.968, "Naan bigger", fontsize=12, fontweight="bold", va="top", color="#1a1a1a", transform=fig.transFigure) fig.text(0.02, 0.933, "Tata's workforce in Britain, September 2011\n'000", fontsize=9, va="top", color="#444444", transform=fig.transFigure, linespacing=1.4) # Footer fig.text(0.02, 0.02, "Source: Tata", fontsize=8, color="#666666", transform=fig.transFigure) fig.text(0.72, 0.02, "Health Intelligence Center", fontsize=8, color="#666666", transform=fig.transFigure) plt.tight_layout(rect=[0.02, 0.05, 0.72, 0.80]) plt.show()
charts/naan_bigger_workforce.png
Dernière mise à jour le