7  Index of Multiple Deprivation (IMD)

8 Index of Multiple Deprivation

The latest available IMD data for England is from 2019.

Updated IMD for England

This is expected to be released in late 2025 by Oxford Consultants for Social Inclusion (OCSI).

The consultation outcome was released in 2022 by the Department for Levelling up, Housing & Communities

Thanks to the NHS-R Community for finding and sharing these links on the NHS-R Slack

IMD is very useful for categorising the area a person lives in for deprivation. Deprivation is by country and do not include the other nations. Deciles are the most commonly used way of referring to IMD and are taken from the scores which are ordered and then cut into 10. For deciles the 1 is the most deprived area and 10 is the least deprived.

Wikipedia link
English IMD 2015 - Guidance

9 Finding the IMD for an address

To get IMD scores or deciles for a local data a join will be needed to the postcode table (like a directory of postcodes) found:


and then to the IMD data:


  • Select File 7 for the dataset.

Other data is available from this dataset including IDAOPI which relates to only older people.

Note that the column headers change, in 2015 it was LADistrictCode2013 and in 2019 it is LADistrictCode2019. Also LADistrictName2013 has become LADistrictName2019.

9.0.1 Postcode spaces

Postcode lengths vary in original data depending on whether one or two spaces are used between the parts. Consequently, it is always better when joining by postcodes to remove the spaces altogether.

In SQL this would be with the code:

REPLACE(postcode, ' ', '')

in R it can be

postcode <- "NG16 1AA"

# base R
gsub(" ", "", postcode)
[1] "NG161AA"
# {stringr} also available in {tidyverse}
stringr::str_remove(postcode, " ")
[1] "NG161AA"

9.0.2 Partial postcodes

Partial postcodes are sometimes provided to protect the data and may be the first part (before spaces) and 1 or 2 characters from the second part. This will not give a sufficiently reliable IMD score.

10 Example join code (SQL)

To get the IMD score the LSOA (Lower Super Output Area) code is required which is taken from the full postcode.

This code will not run and is dependent on the naming conventions of the SQL server. The column names of LSOA11 and LSOAcode2011 will have come from the data sources. The column PostCode_space has been added to the original data.

SELECT imd.*
FROM DIM_AI.PatientData AS p
LEFT JOIN DIM_AI.PostCodes AS pc ON p.PostCode = pc.PostCode_space

11 Creating quintiles

To be able to join to the data scores will need to be put into quintiles (group of 5) rather than deciles (group of 10). Where the number of areas divides into the number of quintiles an equal number of areas can be assigned to each quintile. When it does not divide however, a choice must be made as to which quintiles should have a larger number of areas. The Office of Health Improvement & Disparities recommends using the following approach in their Technical Guide to Assigning Deprivation Categories:

  • Divide the number of small areas within the higher geography by the number of deprivation categories required (up to a maximum of 10), giving an integer and fractional part.

  • The integer-part of this number represents the minimum number of small areas that will be assigned to each deprivation category within each higher geography.

  • The below tables then shows which deprivation categories should be assigned additional small areas based on the fractional part of this number and the number of quintiles being used.

Number after decimal point Deciles receiving an extra area
0.0 None
0.1 1
0.2 1, 6
0.3 1, 4, 7
0.4 1, 3, 6, 8
0.5 1, 3, 5, 7, 9
0.6 1, 2, 4, 6, 7, 9
0.7 1, 2, 3, 5, 6, 8, 9
0.8 1, 2, 3, 4, 6, 7, 8, 9
Number after decimal point Quintiles receiving an extra area
0.0 None
0.2 1
0.4 1, 3
0.6 1, 2, 4
0.8 1, 2, 3, 4
Number after decimal point Quantiles receiving an extra area
0.00 None
0.25 1
0.50 1, 3
0.75 1, 2, 3

11.0.1 Quintiles in SQL

FLOOR((IMDDecile-1)/2) + 1 AS IMDQuintile

11.0.2 Quintiles in R

The PHEindicatormethods provides a convenient function that can be used in R to generate quintiles.

df <- data.frame(
  region = as.character(rep(c("Region1", "Region2", "Region3", "Region4"),
    each = 250
  smallarea = as.character(paste0("Area", seq_along(1:1000))),
  vals = as.numeric(sample(200, 1000, replace = TRUE)),
  stringsAsFactors = FALSE

# assign small areas to deciles across whole data frame
# print the top 15
PHEindicatormethods::phe_quantile(df, vals, type = "standard") |>
  dplyr::slice_head(n = 15)
    region smallarea vals quantile
1  Region1     Area1  142        3
2  Region1     Area2   89        6
3  Region1     Area3  188        1
4  Region1     Area4  196        1
5  Region1     Area5  118        5
6  Region1     Area6  148        3
7  Region1     Area7  182        1
8  Region1     Area8  109        5
9  Region1     Area9   73        7
10 Region1    Area10   84        6
11 Region1    Area11  200        1
12 Region1    Area12  113        5
13 Region1    Area13   71        7
14 Region1    Area14    3       10
15 Region1    Area15   76        7

12 Creating local IMDs

In areas like Nottingham/Nottinghamshire the differences between the LSOA areas is diminished when ranked against England as a whole, but when ranked locally, the variation is much more pronounced. It is possible to take the original scores and apply deciles or quintiles to those scores in order to create a local IMD.

12.1 Local IMD creation in SQL

To apply a rank use the windows partition function ROW_NUMBER() OVER(ORDER BY IMDRank) to create a new ranking score and NTILE(10) OVER (ORDER BY IMDRank) to create new deciles.

12.2 Local IMD creation in R

library(NHSRpostcodetools) # installed from GitHub not CRAN
library(NHSRpopulation) # installed from GitHub not CRAN

Attaching package: 'janitor'
The following objects are masked from 'package:stats':

    chisq.test, fisher.test
# Generate random example postcodes
# Restricted to NG postcodes from Nottinghamshire because postcodes are drawn
# from all nations and don't validate within the {NHSRpopulation} package
# currently
postcodes <- purrr::map_chr(
  .f = ~ PostcodesioR::random_postcode("NG16") |>

# Create a tibble
tibble_postcodes <- dplyr::tibble(
  random_postcodes = postcodes,

  column = "random_postcodes",
  url_type = "imd"
) |>
  ) |>
  mutate(imd_decile_local = ntile(-imd_score, n = 10)) # creating new deciles from the data provided
Joining with `by = join_by(random_postcodes)`
# A tibble: 10 × 6
   random_postcodes new_postcode imd_decile imd_rank imd_score imd_decile_local
   <chr>            <chr>             <int>    <int>     <dbl>            <int>
 1 NG16 1HU         NG16 1HU              9    29293      6.14               10
 2 NG16 2DW         NG16 2DW              8    25568      9.18                9
 3 NG16 3NG         NG16 3NG              2     4475     39.3                 1
 4 NG16 6QT         NG16 6QT              7    22516     11.7                 7
 5 NG16 3FU         NG16 3FU              6    18158     15.8                 6
 6 NG16 6GL         NG16 6GL              4    10729     25.0                 4
 7 NG16 4DR         NG16 4DR              2     4909     37.9                 2
 8 NG16 6BQ         NG16 6BQ              7    22516     11.7                 8
 9 NG16 4AP         NG16 4AP              2     4909     37.9                 3
10 NG16 5JJ         NG16 5JJ              4    12817     21.9                 5

12.3 LSOAs - by postcode and by Local Authority/District

Note that the previous code example was a very small selection of
postcodes and would require much more data to create locally derived deciles.

This usually comes from whole LSOA areas that are linked to Local Authorities/ Districts and not via row level data postcodes.

For example, Nottinghamshire Healthcare NHS Foundation Trust covers several Local Authority areas in its services and so organisations like this have to find all the geographic areas that the services cover.

Local Authority and District LSOAs are not in {NHSRpostcodetools} (warning)

Currently (March 2024) the package {NHSRpopulation} which relies upon package for postcodes {NHSRpostcodetools} and a join to the IMD data via LSOAs does not have details for Local Authority LSOAs.

There are two local authorities in the service area of Nottinghamshire Healthcare NHS Foundation Trust: Nottingham City Local Authority and several Nottinghamshire County Council LA areas. # often have their data combined, particularly for the Provider # Trusts like Nottinghamshire Healthcare NHS Foundation Trust.

# Define code for Nottingham City LA
la_code_nott_city <- c("E06000018")

# Adapted from a blog:
# https://cdu-data-science-team.github.io/team-blog/posts/2021-05-14-index-of-multiple-deprivation/#imd-in-sql)
la_code_notts_county <- tibble::tribble(
  ~`LSOAcode2011`, ~`LSOAname2011`, ~`LADistrictCode2019`, ~`LADistrictName2019`,
  "E01013812", "Nottingham 018C", "E06000018", "Nottingham",
  "E01013814", "Nottingham 022B", "E06000018", "Nottingham",
  "E01013810", "Nottingham 018A", "E06000018", "Nottingham",
  "E01013811", "Nottingham 018B", "E06000018", "Nottingham",
  "E01013815", "Nottingham 022C", "E06000018", "Nottingham"
) |>
  dplyr::select(LSOAcode2011) |>

# Combine all districts into one object
la_code_nott_city_notts_county <- c(la_code_nott_city, la_code_notts_county)

[1] "E06000018" "E01013812" "E01013814" "E01013810" "E01013811" "E01013815"

14 Referencing IMD in a paper or research

14.1 Suggested citation text

Ministry of Housing, Communities and Local Government. English Indices of Deprivation 2015. https://www.gov.uk/government/statistics/english-indices-of-deprivation-2019 (Accessed 22 March 2024)