Visual Vocabulary - Financial Times Guide

A comprehensive guide to choosing the right chart for your data story using the FT Data Visualization Guide

This work is an adaptation from the FT Data Visualization Guide from the Financial Times. This work is licenced under the Creative Commons Attribution-ShareAlike 4.0 International License.

This ressource has been made available by The official portal for European data, i.e. data.europa.eu.

The FT Data Visualization Guide

Visual Vocabulary - FT Data Visualization Guide
Visual Vocabulary - FT Data Visualization Guide

License: CC BY-SA 4.0

Deviation

Emphasize variations (+/-) from a fixed reference point. Often the reference point is zero but it can also be a target or a long-term average. Can also be used to show sentiment (positive/neutral/negative).

Example FT uses

Trade surplus/deficit, climate change

Chart Types

Diverging bar

  • A simple standard bar chart that can handle both negative and positive magnitude values

Diverging stacked bar

  • Perfect for presenting survey results which involve sentiment (eg disagree/neutral/agree)

Spine

  • Splits a single value into 2 contrasting components (eg Male/Female)

Surplus/deficit filled line

  • The shaded area of these charts allows a balance to be shown – either against a baseline or between two series

Matplotlib implementation examples

Correlation

Show the relationship between two or more variables. Be mindful that, unless you tell them otherwise, many readers will assume the relationships you show them to be causal (i.e. one causes the other).

Example FT uses

Inflation & unemployment, income & life expectancy

Chart Types

Scatterplot

  • The standard way to show the relationship between two continuous variables, each of which has its own axis

Column + line (dual axis)

  • A chart which allows you to look at the relationship between two scaled axis. TAKE CARE: this axis is very easy to manipulate to show nothing or anything

Connected scatterplot

  • Usually used to show how the relationship between 2 variables has changed over time

Bubble

  • Like a scatterplot, but adds additional detail by sizing the circles according to a third variable

XY heatmap

  • A good way of showing the patterns between 2 categories of data, less good at showing fine differences in amounts

Matplotlib implementation examples

For these correlation examples, we use economic data from 2024:

Data Sources: World Bank, OECD, UN Statistics, Eurostat, Our World in Data.
python
import pandas as pd

data = [
    {"Country": "France", "Year": 2024, "Inflation": 4.1, "Unemployment": 7.4, "Income": 42800, "LifeExpectancy": 82.4},
    {"Country": "Germany", "Year": 2024, "Inflation": 2.6, "Unemployment": 3.2, "Income": 51800, "LifeExpectancy": 81.1},
    {"Country": "USA", "Year": 2024, "Inflation": 3.2, "Unemployment": 3.8, "Income": 76650, "LifeExpectancy": 79.2},
    {"Country": "Japan", "Year": 2024, "Inflation": 2.8, "Unemployment": 2.7, "Income": 41400, "LifeExpectancy": 84.7},
    {"Country": "Brazil", "Year": 2024, "Inflation": 3.9, "Unemployment": 8.4, "Income": 11653, "LifeExpectancy": 75.6},
    {"Country": "South Africa", "Year": 2024, "Inflation": 6.0, "Unemployment": 32.0, "Income": 7600, "LifeExpectancy": 65.2},
    {"Country": "India", "Year": 2024, "Inflation": 5.5, "Unemployment": 7.1, "Income": 2600, "LifeExpectancy": 70.8},
]

df = pd.DataFrame(data)

The original data values are based on open international datasets and aggregate statistics from sources like the World Bank, OECD, United Nations, Eurostat, and compilations by Our World in Data. For robust research or publication, refer directly to these organizations' portals or download raw datasets from their official sites.

  • Strong positive correlation between: Income and Life Expectancy
  • Phillips Curve visible: Higher: unemployment with lower inflation
  • South Africa: high unemployment (32%) (could be considered as an outlier depending on the context)
  • USA: Highest income but not highest life expectancy
  • Japan: Highest life expectancy with moderate income

Ranking

Use where an item's position in an ordered list is more important than its absolute or relative value. Don't be afraid to highlight the points of interest.

Example FT uses

Wealth, deprivation, league tables, constituency election results

Chart Types

Ordered bar

  • A standard bar chart which orders the bars (longest, shortest, or other)

Ordered column

  • A standard column chart which orders the columns (longest, shortest, or other)

Ordered proportional symbol

  • Use when there are big variations between values and/or seeing fine differences in amounts

Slope

  • Perfect for showing how ranks have changed over time or vary between categories. Use point labels or a combination of the start and end measurements

Lollipop

  • Lollipop charts draw attention to the data value by reducing the excess ink of the bar

Dot strip plot

  • Dot strip plots or dot plot arrays also work well for showing ranking in data where values are more closely grouped

Bump

  • Effective for showing changing ranks through multiple stages

Matplotlib implementation examples

See the examples from The Financial Times Guide and Practical work with matplotlib (2/2).

Distribution

Show values in a dataset and how often they occur. The shape (or 'skew') of a distribution can be a memorable way of highlighting the lack of uniformity or equality in the data.

Example FT uses

Income distribution, population (age/sex) distribution

Chart Types

Histogram

  • The standard way to show a statistical distribution - keep the gaps between columns small to highlight the 'shape' of the data

Dot plot

  • A simple way of showing the range (and clustering) of data points

Dot strip plot

  • Good for showing individual values in a distribution, can be a problem when too many dots are in a line

Barcode plot

  • Like dot strip plot - but hides the identity of individual values to focus on the collective pattern

Beeswarm plot

  • A good way of showing all the data points when too many have a single value

Population pyramid

  • A standard way for showing the age and sex breakdown of a population distribution. Used widely to show a population by age and sex

Cumulative curve

  • A good way of showing how unequal a distribution is: y axis is always cumulative frequency, x axis is a measure of what the distribution is showing

Boxplot

  • Summarize multiple distributions by showing the median (center) and the spread of the data

Violin plot

  • Similar to a box plot but more effective with complex distributions (data with two peaks, or multiple humps)

Matplotlib implementation examples

For these distribution examples, we use demographic data from France:

Data Source: INSEE (Institut National de la Statistique et des Études Économiques), France's national statistics bureau.

Disclaimer: We use simplified data for the income distribution, some assumptions are made:
1️⃣ €27k average = Fiscal households (tax units) with declared income
2️⃣ ~40M tax-filing units
3️⃣ Couples = 1 unit
4️⃣ Children excluded
5️⃣ Only taxable income counted

python
import pandas as pd

# France Population Age and Sex Distribution (2025)
data_population = [
    {"AgeGroup": "0-14", "Femmes": 5586, "Hommes": 5861, "Total": 11447},
    {"AgeGroup": "15-19", "Femmes": 2073, "Hommes": 2214, "Total": 4287},
    {"AgeGroup": "20-24", "Femmes": 1939, "Hommes": 2023, "Total": 3962},
    {"AgeGroup": "25-29", "Femmes": 1945, "Hommes": 1943, "Total": 3888},
    {"AgeGroup": "30-34", "Femmes": 2060, "Hommes": 1999, "Total": 4059},
    {"AgeGroup": "35-39", "Femmes": 2205, "Hommes": 2104, "Total": 4309},
    {"AgeGroup": "40-44", "Femmes": 2241, "Hommes": 2130, "Total": 4371},
    {"AgeGroup": "45-49", "Femmes": 2096, "Hommes": 2042, "Total": 4138},
    {"AgeGroup": "50-54", "Femmes": 2283, "Hommes": 2233, "Total": 4516},
    {"AgeGroup": "55-59", "Femmes": 2251, "Hommes": 2157, "Total": 4408},
    {"AgeGroup": "60-64", "Femmes": 2222, "Hommes": 2073, "Total": 4295},
    {"AgeGroup": "65-69", "Femmes": 2093, "Hommes": 1855, "Total": 3948},
    {"AgeGroup": "70-74", "Femmes": 1991, "Hommes": 1679, "Total": 3669},
    {"AgeGroup": "75+", "Femmes": 4342, "Hommes": 2965, "Total": 7307}
]

df_population = pd.DataFrame(data_population)

# Income Distribution in France (Simplified example)
data_income = [
    {"IncomeBracket": "0-10k", "Percentage": 8, "CumulativePercentage": 8},
    {"IncomeBracket": "10-15k", "Percentage": 12, "CumulativePercentage": 20},
    {"IncomeBracket": "15-20k", "Percentage": 15, "CumulativePercentage": 35},
    {"IncomeBracket": "20-25k", "Percentage": 18, "CumulativePercentage": 53},
    {"IncomeBracket": "25-30k", "Percentage": 15, "CumulativePercentage": 68},
    {"IncomeBracket": "30-40k", "Percentage": 17, "CumulativePercentage": 85},
    {"IncomeBracket": "40-50k", "Percentage": 8, "CumulativePercentage": 93},
    {"IncomeBracket": "50-75k", "Percentage": 5, "CumulativePercentage": 98},
    {"IncomeBracket": "75k+", "Percentage": 2, "CumulativePercentage": 100}
]

df_income = pd.DataFrame(data_income)

Population data shows France's demographic structure as of January 1, 2025 (in thousands). Income distribution represents household income brackets in euros per year. Source: INSEE, Population par sexe et groupe d'âges (2025).

Change over time

Give emphasis to changing trends. These can be short (intra-day) or long (decade) periods. As time is a dimension variable this is best shown in most cases by using a horizontal axis.

Example FT uses

Share price changes, temperature changes, economic time series

Chart Types

Line

  • The standard way to show a changing time series. If data are irregular, consider markers to represent data points

Column

  • Columns work well for showing change over time - but usually best with only one series of data at a time

Column + line timeline

  • A good way of showing the relationship over time between an amount (columns) and a rate (line)

Stock price

  • Usually focused on day-to-day activity, these charts show opening/closing and hi/low points of each day

Slope

  • Good for showing changing data as long as the data can be simply paired

Area chart

  • Use with care - these are good at showing changes to total, but seeing change in components can be very difficult

Fan chart (projections)

  • Use to show the uncertainty in future projections - usually this grows the further forward into the future

Connected scatterplot

  • A good way of showing changing relationship between two variables over time

Calendar heatmap

  • A great way of showing temporal patterns (daily, weekly, monthly) - at the expense of showing precision in quantity

Priestley timeline

  • Great when date and duration are key

Circle timeline

  • Good for showing discrete values of varying size across multiple categories (eg earthquakes by region)

Seismogram

  • Another alternative to the circle timeline for showing series where there are big variations between values

Vertical timeline

  • A simple way of showing the order and timing of events

Matplotlib implementation examples

For these time series examples, we use stock price data for a fictitious technology company "TechCorp":

Magnitude

Show size comparisons. These can be relative (just being able to see larger/bigger) or absolute (need to see fine differences). Usually these show a 'counted' number (for example, barrels, dollars or people) rather than a calculated rate or percent.

Example FT uses

Commodity production, market capitalizations

Chart Types

Column

  • The standard way to compare the size of things. Must have a zero baseline!

Bar

  • See above. Good when the data are not time series and labels have long category names

Proportional stacked bar

  • A multi-set bar chart which includes a gap between each individual series to allow the reader to clearly see the full extent of each series

Paired column

  • As per standard column but allows for multiple series. Can become tricky to read with more than 2 series

Paired bar

  • As per standard bar but allows for multiple series

Proportional symbol

  • Use when there are big variations between values and/or seeing exact amounts is not so important

Isotype (pictogram)

  • Excellent solution in some instances - use only with whole numbers (do not slice off an arm to represent a decimal)

Lollipop

  • Lollipop charts draw attention to the data value by reducing the excess ink of the bar

Radar

  • A space-efficient way of showing magnitude for several categories - but make sure the scales are consistent!

Parallel coordinates

  • An alternative to radar charts - again, ensure the scales are consistent. Usually benefits from highlighting values of interest

Bullet

  • A quick, efficient way of showing actual vs. target values. A good way of showing bars vs. targets without needing too many colors or labels

Grouped symbol

  • An alternative to bar/column charts when showing data that have big variations. Can work well in small multiple format

Matplotlib implementation examples

For these magnitude examples, we use economic and demographic data from various countries:

Data Sources: World Bank, OECD, UN Statistics, Eurostat, Our World in Data.
python
import pandas as pd

# Economic data for G7 countries (2024)
# (2024, values in trillions USD, population in millions)
data_g7 = [
    {"Country": "USA", "GDP": 29.2, "Population": 332, "GDP_per_capita": 88.0, "Exports": 2.1, "Imports": 3.2},
    {"Country": "Japan", "GDP": 4.2, "Population": 124, "GDP_per_capita": 34.0, "Exports": 0.9, "Imports": 0.9},
    {"Country": "Germany", "GDP": 4.7, "Population": 84, "GDP_per_capita": 56.0, "Exports": 2.0, "Imports": 1.7},
    {"Country": "UK", "GDP": 3.5, "Population": 67, "GDP_per_capita": 52.2, "Exports": 0.8, "Imports": 0.9},
    {"Country": "France", "GDP": 3.0, "Population": 65, "GDP_per_capita": 46.0, "Exports": 0.9, "Imports": 1.0},
    {"Country": "Italy", "GDP": 2.1, "Population": 58, "GDP_per_capita": 36.2, "Exports": 0.6, "Imports": 0.5},
    {"Country": "Canada", "GDP": 2.2, "Population": 39, "GDP_per_capita": 57.0, "Exports": 0.5, "Imports": 0.5},
]

df_g7 = pd.DataFrame(data_g7)

print(df_g7)
df_g7 = pd.DataFrame(data_g7)

GDP values are in trillions USD, Population in millions, Exports/Imports in trillions USD. Market capitalizations as of Q4 2024.

Part-to-whole

Show how a single entity can be broken down into its component elements. If the components are too small to see, consider a grouped bar chart instead.

Example FT uses

Fiscal budgets, company structures, national household spending

Chart Types

Stacked column

  • A simple way to show part-to-whole relationships but can be difficult to read with more than a few components

Proportional stacked bar

  • A good way of showing the size and proportion of data at the same time - as long as the data are not too complicated

Pie

  • A common way of showing part-to-whole data - but research shows people find it hard to compare the size of segments

Donut

  • Similar to a pie chart - but the center can be a good way of making space to include more information about the data (eg. total)

Treemap

  • Use for hierarchical part-to-whole relationships; can be difficult to read when there are many small segments

Voronoi

  • A way of turning points in space into polygons while showing part to whole. Irregular division of data might be interpreted as meaningful, when it isn't

Arc

  • A hemicircle, often used for showing political data, with the seats orientated as they would be in parliament

Gridplot

  • Good for showing % information, often used when comparing data for two points in time

Venn

  • Generally only used for illustrating simple set logic. Scale can be (but is not always) related to the size of the population

Waterfall

  • Can be useful for showing part-to-whole relationships where some of the components are negative

Matplotlib implementation examples

Examples using other libraries

Spatial

Used only when precise locations or geographical patterns in data are more important to the reader than anything else.

Example FT uses

Locator maps, regional/country maps, office locations, natural resource locations, natural disaster locations

Chart Types

Basic choropleth (rate/ratio)

  • The standard approach for putting data on a map - should always be used for rates rather than totals

Proportional symbol (count/magnitude)

  • Use for totals rather than rates - be wary that small differences in data may be hard to see

Flow map

  • For showing unambiguous movement across a map

Contour map

  • For showing areas of equal value on a map. Can use deviation colour schemes for showing +/- values

Equalised cartogram

  • Converting each unit on a map to a regular and equally-sized shape - good for representing voting regions with equal value

Scaled cartogram (value)

  • Stretching and shrinking a map so that each area is sized according to a particular value

Dot density

  • Used to show the location of individual events/locations - make sure to annotate any patterns the reader should see

Heat map

  • Grid-based data values mapped with an intensity colour scale. As choropleth map - but not snapped to an admin/political unit

Geopandas implementation examples

Matplotlib is the library used by geopandas to build its plots. However, geopandas provided a high-level API to build maps, with a lot of customization options. In most cases, it is enough to only interact with the geopandas API to build maps.

Flow

Show the reader volumes or intensity of movement between two or more states or conditions. These might be logical sequences or geographical locations.

Example FT uses

Movement of funds, trade, migrants, lawsuits, information, relationships

Chart Types

Sankey

  • Shows changes in flows from one condition to at least one other; good for tracing the multiple paths through a complex system

Waterfall

  • Designed to show the sequencing of data through a flow process, typically budget. Can include +/- components

Chord

  • A complex but powerful diagram which can illustrate 2-way flows. Needs good labeling

Network

  • Used for showing the strength and inter-connectedness of relationships of varying types

Matplotlib implementation examples

Examples using other libraries

Maths.pm  ne collecte aucune donnée.
  • Aucun cookie collecté
  • Aucune ligne de log écrite
  • Pas l'ombre d'une base de données distante
  • nihil omnino

  • Ni par pointcarre.app
  • Ni par notre hébergeur
  • Ni par aucun service tiers

Nous expliquons notre démarche zéro donnée conservée sur cette page.

Maths.pm, par

pointcarre.app

Codes sources
Logo licence AGPLv3
Contenus
Logo licence Creative Commons