13  Structural inequalities in General Practice in England

Show the set up code
# Load necessary libraries
library(ggplot2)
library(tidyverse)
library(magrittr)

# Set up the ggplot theme
theme_set(
  theme_minimal() +
    theme(
      axis.title = element_text(size = 100),
      axis.text = element_text(size = 100),
      plot.caption = element_text(size = 80),
      plot.title = element_text(size = 120),
      plot.subtitle = element_text(size = 110),
      panel.grid = element_line(size = 5),
      legend.title = element_text(size = 80),
      legend.text = element_text(size = 80),
      legend.key.width = unit(3.5, "cm"),
      plot.title.position = "plot"
    )
)

update_geom_defaults("point", list(size = 50))

df <- read.csv("https://raw.githubusercontent.com/HealthEquityEvidenceCentre/HEEC/refs/heads/main/datapacks/final_data.csv")

# The following code is used for data at ICB level
# If there were no practices in the most deprived quintile in the ICB, this line will still create a datapoint representing the most deprived practices in that ICB (is not required for country level data)
# df[is.na(df$quin_4) & is.na(df$quin_5), ]
#
# If quin_5 is NA (missing), code assigns the value from quin_4 to quin_5. 
# If quin_5 is not NA, it retains its value
# (Not required for country level data)
# Note the use of the [magrittr pipe %<>%](https://magrittr.tidyverse.org/reference/compound.html?q=%%3C%3E%#ref-usage)
# df %<>%
#   mutate(quin_5 = ifelse(is.na(quin_5), quin_4, quin_5))

13.1 Introduction

  • Strong primary care is associated with more equitable health outcomes.

  • A key role of commissioners is to ensure the equitable distribution of resources across the system.

  • We present the latest NHS primary care data, using Index of Multiple Deprivation (IMD) to examine inequalities existing in primary care access, experience and outcomes, across the following categories:

    • Resources (supply): Payments, Workforce
    • Population (demand): Disease prevalence, Health-related behaviours
    • Service quality: QOF achievement
    • Access: Patient experience, Appointments
    • Impact on secondary care: Emergency admissions, A&E attendances
  • This analysis was produced by the Health Equity Evidence Centre. Additional data and analysis is available on GitHub.

  • For further information or to discuss the results, please contact Dr John Ford or Mr Cameron Appel.

13.2 Inequality in Life Expectancy

Click to show code
year <- 2020

df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == year) %>%
  filter(Indicator %in% c("Life_Expectancy_Female", "Life_Expectancy_Male"))

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "Life_Expectancy_Female" = "Female",
    "Life_Expectancy_Male" = "Male"
  )) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Life expectancy by practice, birth cohort 2016-20 (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles.",
    colour = "",
    caption = "Source: NHS England GP Workforce statistics, 2024. © Health Equity Evidence Centre, 2024."
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with average life expectancy for England GP Practices. Split by female and male.

Average life expectancy for males is 81.7 in the least deprived 20% and 76.1 in the most deprived 20%.

Average life expectancy for females is 85.2 in the least deprived 20% and 80.6 in the most deprived 20%.

13.3 Inequality in NHS Payments

Click to show code
year <- 2023

df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == year) %>%
  filter(Indicator %in% c("payment_per_patient_all", "payment_per_patient_disp", "payment_per_patient_non_disp")) %>%
  mutate(Indicator = factor(Indicator, levels = c("payment_per_patient_disp", "payment_per_patient_non_disp", "payment_per_patient_all")))

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "payment_per_patient_all" = "All practices",
    "payment_per_patient_disp" = "Dispensing practices",
    "payment_per_patient_non_disp" = "Non-dispensing practices"
  )) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Mean payment per weighted patient, 2022/23 (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: NHS England Payments to General Practice, 2022/23. © Health Equity Evidence Centre, 2024."
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with mean payment per weighted patient 2022/23 for England GP Practices. Split by All practices, Non-dispensing practice and Dispensing practices. Non dispensing practices are the closes between least and most deprived whilst Dispensing practices is a higher cost overall.

Average payment per weighted patient is £166.37 in the most deprived 20% of practices, versus £182.6 in the least deprived 20%.

13.4 Inequality in Workforce

Click to show code
year <- 2024

df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == year) %>%
  filter(Indicator %in% c("TOTAL_GP_EXTGL_FTE", "TOTAL_LOCUUM_TRN_FTE", "TOTAL_NURSES_FTE", "TOTAL_ADMIN_FTE", "TOTAL_DPC_FTE", "PCN_staff")) %>%
  mutate(Indicator = factor(Indicator, levels = c("PCN_staff", "TOTAL_ADMIN_FTE", "TOTAL_DPC_FTE", "TOTAL_NURSES_FTE", "TOTAL_LOCUUM_TRN_FTE", "TOTAL_GP_EXTGL_FTE")))


df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "TOTAL_GP_EXTGL_FTE" = "Fully-qualified permanent GPs",
    "TOTAL_LOCUUM_TRN_FTE" = "GPs in training grade & locuums",
    "TOTAL_NURSES_FTE" = "Nurses",
    "TOTAL_ADMIN_FTE" = "Admin/Non-clinical",
    "TOTAL_DPC_FTE" = "Direct patient care",
    "PCN_staff" = "PCN-funded staff (all types)"
  )) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Average staff FTE per 10,000 weighted patients, 2023/24 (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: NHS England GP Workforce & PCN Workforce, 2023-24. © Health Equity Evidence Centre, 2024."
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with average full time equivalent stff per 10,000 per weighted patients 2023/24 for England GP Practices. Split by 6 categories of healthcare staff the highest number of staff are for admin/non-clinical whilst lowest is PCN-funded staff (all types). The only categories where there is some gap between deprivation is for fully qualiffied permanent GPs and direct patient care.

Average fully-qualified GPs FTE per 10,000 weighted patients is 3.8 per weighted patient in the most deprived 20% of practices in England versus 4.9 in the least deprived 20%.

13.6 Inequality in Disease Prevalence

Click to show code
year <- 2023

df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == year) %>%
  filter(Indicator %in% c(
    "Atrial fibrillation: QOF prevalence (all ages)",
    "Asthma: QOF prevalence (6+ yrs)",
    "CHD: QOF prevalence (all ages)", "CKD: QOF prevalence (18+ yrs)", "COPD: QOF prevalence (all ages)", "Dementia: QOF prevalence (all ages)", "Depression: QOF prevalence (18+ yrs)", "Diabetes: QOF prevalence (17+ yrs)", "Epilepsy: QOF prevalence (18+ yrs)", "Heart failure with LVSD: QOF prevalence (all ages)", "Learning disability: QOF prevalence (all ages)", "Mental Health: QOF prevalence (all ages)", "Stroke: QOF prevalence (all ages)"
  ))

# divide by 100 to get percentage
df_filter$quin_1 <- df_filter$quin_1 / 100
df_filter$quin_5 <- df_filter$quin_5 / 100
df_filter$avg <- df_filter$avg / 100

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "Atrial fibrillation: QOF prevalence (all ages)" = "Atrial fibrillation (all ages)",
    "Asthma: QOF prevalence (6+ yrs)" = "Asthma (6+ yrs)",
    "CHD: QOF prevalence (all ages)" = "Coronary heart disease (all ages)",
    "CKD: QOF prevalence (18+ yrs)" = "Chronic kidney disease (18+ yrs)",
    "COPD: QOF prevalence (all ages)" = "Chronic obstructive pulmonary disease (all ages)",
    "Depression: QOF prevalence (18+ yrs)" = "Depression (18+ yrs)",
    "Diabetes: QOF prevalence (17+ yrs)" = "Diabetes (17+ yrs)",
    "Epilepsy: QOF prevalence (18+ yrs)" = "Epilepsy (18+ yrs)",
    "Heart failure with LVSD: QOF prevalence (all ages)" = "Heart failure (all ages)",
    "Learning disability: QOF prevalence (all ages)",
    "Mental Health: QOF prevalence (all ages)" = "Severe mental illness (all ages)",
    "Stroke: QOF prevalence (all ages)" = "Stroke (all ages)"
  )) +
  scale_y_continuous(
    labels = scales::percent
  ) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Average disease prevalence, ", year, " (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: Office for Health Improvements and Disparities National GP Profiles, 2022/23. © Health Equity Evidence Centre, 2024."
  ) +
  theme(
    plot.title = element_text(
      size = 105
    ),
    plot.subtitle = element_text(
      size = 90
    )
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with average disease prevalence for 2023 for England GP Practices. Split by 13 health categories. The highest and also has the widest gap for deprivation is depression (over 18 years), followed by diabetes (over 17 years)

Average prevalence of diabetes (17+ years) is 9% in the most deprived 20% of practices in England, versus 6.3% in the least deprived 20%.

Average prevalence of depression (18+ years) is 14.8% in the most deprived 20% of practices in England, versus 11.9 % in the least deprived 20%.

13.7 Inequality in Quality of Service

Click to show code
df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == 2023 & Indicator %in% c(
    "Last BP reading of patients (<80 yrs, with hypertension), in the last 12 months is <= 140/90 mmHg (denominator incl. PCAs)",
    "IFCC-HbA1c <= 58 mmol/mol in patients with diabetes without frailty (denominator incl. PCAs)"
  ) |
    Year == 2024 & Indicator %in% c(
      "% QOF points achieved",
      "child_imms",
      "Women (25-49 yrs), with a record of cervical screening in the last 3.5 yrs (denominator incl. PCAs)",
      "Women (50-64 yrs), with a record of cervical screening in the last 5.5 yrs (denominator incl. PCAs)"
    )) %>%
  mutate(Indicator = factor(Indicator, levels = c("Women (25-49 yrs), with a record of cervical screening in the last 3.5 yrs (denominator incl. PCAs)", "Women (50-64 yrs), with a record of cervical screening in the last 5.5 yrs (denominator incl. PCAs)", "Last BP reading of patients (<80 yrs, with hypertension), in the last 12 months is <= 140/90 mmHg (denominator incl. PCAs)", "IFCC-HbA1c <= 58 mmol/mol in patients with diabetes without frailty (denominator incl. PCAs)", "child_imms", "% QOF points achieved")))

# divide by 100 to get percentage
df_filter$quin_1 <- df_filter$quin_1 / 100
df_filter$quin_5 <- df_filter$quin_5 / 100
df_filter$avg <- df_filter$avg / 100

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "% QOF points achieved" = "QOF points achieved",
    "Women (25-49 yrs), with a record of cervical screening in the last 3.5 yrs (denominator incl. PCAs)" = "Women (25-49 yrs) receiving cervical cancer screen last 3.5yrs",
    "Women (50-64 yrs), with a record of cervical screening in the last 5.5 yrs (denominator incl. PCAs)" = "Women (50-64 yrs) receiving cervical cancer screen last 5.5yrs",
    "child_imms" = "% Children 5y received DTaP/IPV and 2 MMR",
    "Last BP reading of patients (<80 yrs, with hypertension), in the last 12 months is <= 140/90 mmHg (denominator incl. PCAs)" = "Last BP reading of hypertensive patients (<80 yrs) <= 140/90 mmHg",
    "IFCC-HbA1c <= 58 mmol/mol in patients with diabetes without frailty (denominator incl. PCAs)" = "Last HbA1c of diabetic patients < = 68 mmol/mol"
  )) +
  scale_y_continuous(
    labels = scales::percent
  ) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Average % achievement of QOF domains, 2023/24", " (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: NHS England Quality and Outcomes Framework, 2022/23-2023/24. © Health Equity Evidence Centre, 2024."
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with average percentage prevalence of qof domains for 2023/24 for England GP Practices. Split by 6 categories the highes percentage is for qof points achieved followed by percentage of children 5 years received DTaP/IPV and 2 MMR which also has a considerable deprivation gap.

Average QOF points achieved is 89.6% in the most deprived 20% of practices in England, versus 95% in the least deprived 20%.

13.8 Inequality in Patient Experience

Click to show code
df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(
    Year == 2024 & Indicator %in% c(
      "access_pct",
      "continuity_pct",
      "overall_pct",
      "trust_pct"
    ) |
      Year == 2023 & Indicator %in% c(
        "cqc_rating"
      )
  ) %>%
  mutate(Indicator = factor(Indicator, levels = c("access_pct", "continuity_pct", "trust_pct", "cqc_rating", "overall_pct")))

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "overall_pct" = "Overall satisfaction",
    "access_pct" = "Experience of contacting the surgery",
    "continuity_pct" = "Continuity of care",
    "trust_pct" = "Confidence and trust",
    "cqc_rating" = "'Good' or 'Outstanding' CQC rating"
  )) +
  scale_y_continuous(
    labels = scales::percent
  ) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Average patient experience, 2023/24 (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: Ipsos GP Patient Survey, 2024 & CQC, 2023. © Health Equity Evidence Centre, 2024."
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with average patient experience for 2023/24 for England GP Practices. Split by 5 categories of experience the lowest percentage is for continuity of care.

Average % of practices receiving ‘Good’ or ‘Outstanding’ CQC ratings is 94.4% in the most deprived 20% of practices in England, versus 94.8% in the least deprived 20%.

Average % of patients describing their experience as ‘Good’ is 72.9% in the most deprived 20% of practices in England, versus 80.9% in the least deprived 20%.

13.9 Inequality in Appointments

Click to show code
year <- 2024

df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == year) %>%
  filter(Indicator %in% c(
    "Telephone", "Face-to-Face", "DNA"
  ))

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Median appointments and DNAs per 10,000 weighted patients, March 2024 (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: NHS England Appointmets in General Practice, 2024. © Health Equity Evidence Centre, 2024."
  ) +
  theme(
    plot.title = element_text(
      size = 105
    ),
    plot.subtitle = element_text(
      size = 95
    )
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with median appointments and did not attends per 10,000 weighted patients for March 2024 for England GP Practices. Split by telephone, face to face and didn't attend the highest numbers are for face to face with lowest for DNA. Most deprived had the lowest number except for DNA which was almost the same as least deprived.

Average number of Face-to-Face appointments per 10,000 weighted patients is 1265.5 in the most deprived 20% of practices in England, versus 1371.7 in the least deprived 20%.

13.10 Inequality in Impact on Secondary Care

Click to show code
df_filter <- df %>%
  filter(ICB.NAME == "England") %>%
  filter(Year == 2021 & Indicator %in% c(
    "Emergency admissions (0 to 4 years) - CCG"
  ) |
    Year == 2022 & Indicator %in% c(
      "A&E attendances (0 to 4 years)"
    ) |
    Year == 2023 & Indicator %in% c(
      "Number of emergency admissions with cancer"
    ))

df_filter %>%
  ggplot(., aes(x = Indicator)) +
  geom_linerange(aes(x = Indicator, ymin = quin_5, ymax = quin_1)) +
  geom_point(aes(y = quin_1, color = "Least deprived")) +
  geom_point(aes(y = quin_5, color = "Most deprived")) +
  geom_point(aes(y = avg, color = "National average"), size = 15, shape = 18) +
  scale_x_discrete(labels = c(
    "Number of emergency admissions with cancer" = "Number of emergency admissions with cancer",
    "Emergency admissions (0 to 4 years) - CCG" = "Emergency admissions (0-4 yrs)",
    "A&E attendances (0 to 4 years)" = "A&E attendances (0-4 yrs)"
  )) +
  scale_color_manual(values = c("Least deprived" = "#009639", "Most deprived" = "#003087", "National average" = "#DA291C")) +
  labs(
    x = NULL, y = NULL,
    title = paste0("Median emergency admissions or A&E attendances, latest period (England)"),
    subtitle = "Practices in most and least deprived IMD quintiles",
    colour = "",
    caption = "Source: Office for Health Improvements and Disparities National GP Profiles, 2022/23. © Health Equity Evidence Centre, 2024."
  ) +
  theme(
    plot.title = element_text(
      size = 105
    ),
    plot.subtitle = element_text(
      size = 95
    )
  ) +
  coord_flip() +
  guides(colour = guide_legend(override.aes = list(size = 50, shape = c(16, 16, 18))))

Dumbbell chart with median emergency admissions or A&E attendances for the latest period in England GP Practices. Split by number of emergency admissions with cancer (from 2021), emergency admissions for ages 0-4 years (from 2022) and A&E attendances for 0-4 years (for 2023)the widest gap and highest numbers are for A&E attendances for children.

Average number of emergency admissions (0-4 years) is 155.7 in the most deprived 20% of practices in England, versus 120.7 in the least deprived 20%.

13.11 Acknowledgements

  • This work was contributed by the Health Equity Evidence Centre, and made possible through seed funding from NHS East of England team.