Skip to content

Commit d2a905f

Browse files
Add water bills projections (#1224)
* Add validate function to dataset * Add water bills uprating * Add docs page
1 parent 1815c56 commit d2a905f

8 files changed

Lines changed: 191 additions & 0 deletions

File tree

changelog_entry.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- bump: minor
2+
changes:
3+
added:
4+
- Water bills projections.

docs/book/_toc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ parts:
77
- caption: Economic assumptions
88
chapters:
99
- file: assumptions/growthfactors
10+
- file: assumptions/water-bills
1011
- caption: Validation
1112
chapters:
1213
- file: validation/hbai
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Water bills projection and uprating
2+
3+
This documentation describes the water bills projection and uprating methodology used in PolicyEngine UK.
4+
5+
## Overview
6+
7+
We implement water bills projections through a combination of historical data analysis and regulatory projection data. The system uses:
8+
9+
1. **Historical data** (2021-2025) from Ofwat average bills
10+
2. **Regulatory projection data** from Ofwat company-specific increases (2025-2030)
11+
3. **Economic uprating** using Consumer Price Index (CPIH) growth rates
12+
13+
## Implementation
14+
15+
### Data sources
16+
17+
#### Historical data
18+
The projection uses historical real water bills data from Ofwat:
19+
- **2021**: £486 (real terms)
20+
- **2022**: £470 (real terms)
21+
- **2023**: £486 (real terms)
22+
- **2024**: £492 (real terms)
23+
- **2025**: £503 (real terms)
24+
25+
**Data source**: [Ofwat average bills data for England and Wales](https://www.ofwat.gov.uk/average-bills-press-statement-2024-25/)
26+
27+
#### Regulatory projections
28+
We source company-specific water bill increases from the Consumer Council for Water (CCW) based on Ofwat's price review data for 2025-2030. The data includes projected bills for 16 water companies covering both water and sewerage services.
29+
30+
**Data source**: [CCW Water Company Bill Increases 2025-30](https://www.ccw.org.uk/our-work/price-review/how-much-will-my-water-and-sewerage-bills-increase-by-2030/breakdown-of-water-companies-bill-increases-2025-30/)
31+
32+
### Projection methodology
33+
34+
#### Historical period (2021-2025)
35+
1. We convert real bills to nominal terms using CPIH values
36+
2. We calculate year-on-year nominal growth rates
37+
3. We normalize to 2021 baseline (index = 100)
38+
39+
#### Future period (2025-2030)
40+
1. We calculate average of company-specific projected increases
41+
2. We apply Consumer Price Index (CPIH) uprating to account for inflation
42+
3. We compound inflation adjustments year-over-year
43+
44+
The methodology ensures that:
45+
- We preserve real increases from regulatory decisions
46+
- We apply additional inflation adjustments using CPIH forecasts
47+
- Future projections account for both policy changes and economic conditions
48+
49+
### Code structure
50+
51+
#### Core implementation
52+
- **Location**: `policyengine_uk/utils/water/forecast_water_bills.py`
53+
- **Function**: `project_water_bills()`
54+
- **Data**: `policyengine_uk/utils/water/ofwat_increases.csv`
55+
56+
#### Parameters integration
57+
We store water bills year-on-year growth rates in:
58+
- **Location**: `policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml`
59+
- **Parameter**: `ofwat.water_bills`
60+
61+
### Growth rates
62+
63+
The model uses these resulting year-on-year growth rates:
64+
65+
| Year | Growth rate |
66+
|------|-------------|
67+
| 2022 | 5.2% |
68+
| 2023 | 9.2% |
69+
| 2024 | 4.4% |
70+
| 2025 | 6.1% |
71+
| 2026 | 6.1% |
72+
| 2027 | 5.1% |
73+
| 2028 | 3.8% |
74+
| 2029 | 4.3% |

policyengine_uk/data/dataset_schema.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ def copy(self):
8686
household=self.household.copy(),
8787
)
8888

89+
def validate(self):
90+
# Check for NaNs in the tables
91+
for df in self.tables:
92+
for col in df.columns:
93+
if df[col].isna().any():
94+
raise ValueError(f"Column '{col}' contains NaN values.")
95+
8996
@staticmethod
9097
def from_simulation(
9198
simulation: "Microsimulation", fiscal_year: int = 2025

policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,21 @@ ons:
263263
reference:
264264
- title: ONS Population Projections
265265
href: https://www.ons.gov.uk/
266+
ofwat:
267+
water_bills:
268+
description: Water and sewerage bills year-on-year growth.
269+
values:
270+
2022-01-01: 0.052
271+
2023-01-01: 0.092
272+
2024-01-01: 0.044
273+
2025-01-01: 0.061
274+
2026-01-01: 0.061
275+
2027-01-01: 0.051
276+
2028-01-01: 0.038
277+
2029-01-01: 0.043
278+
metadata:
279+
unit: /1
280+
label: water bills growth
281+
reference:
282+
- title: Ofwat (and custom projections)
283+
href: https://www.ofwat.gov.uk/price-review/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Water bills projections
2+
3+
In this folder, we have:
4+
5+
* `forecast_water_bills.py` - A script that projects water bills based on historical data and proposed increases.
6+
* `ofwat_increases.csv` A CSV with the data from [here](https://www.ccw.org.uk/our-work/price-review/how-much-will-my-water-and-sewerage-bills-increase-by-2030/breakdown-of-water-companies-bill-increases-2025-30/) with proposed pre-inflation increases for each water company.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import pandas as pd
2+
from pathlib import Path
3+
4+
5+
def project_water_bills():
6+
df_pre_2025 = pd.DataFrame(
7+
{
8+
"Year": [2021, 2022, 2023, 2024, 2025],
9+
"Oftwat avg bills (real)": [486, 470, 486, 492, 503],
10+
"CPIH": [113.1, 123.0, 129.9, 134.0, 139.0],
11+
}
12+
)
13+
14+
df_pre_2025["Oftwat avg bills (nominal)"] = (
15+
df_pre_2025["Oftwat avg bills (real)"] * df_pre_2025["CPIH"] / 100
16+
)
17+
df_pre_2025["Oftwat avg bills (nominal)"] = (
18+
df_pre_2025["Oftwat avg bills (nominal)"]
19+
/ df_pre_2025["Oftwat avg bills (nominal)"].iloc[0]
20+
* 100
21+
).round(1)
22+
df_pre_2025["Nominal YoY change"] = (
23+
df_pre_2025["Oftwat avg bills (nominal)"].pct_change() * 100
24+
).round(1)
25+
26+
proposed_increases = pd.read_csv(
27+
Path(__file__).parent / "ofwat_increases.csv"
28+
)
29+
avg_bills_2025_onwards = (
30+
proposed_increases[proposed_increases.columns[1:]].mean()[1:].values
31+
)
32+
33+
df_post_2025 = pd.DataFrame(
34+
{
35+
"Year": [2025, 2026, 2027, 2028, 2029],
36+
"CPIH": [139.0, 142.2, 145.2, 148.2, 151.3],
37+
"Pre-inflation avg bills (nominal)": avg_bills_2025_onwards,
38+
}
39+
)
40+
41+
# Add CPIH to each year's change
42+
43+
df_post_2025["Avg bills (nominal)"] = df_post_2025[
44+
"Pre-inflation avg bills (nominal)"
45+
].values
46+
df_post_2025["CPIH change"] = df_post_2025["CPIH"].pct_change() * 100
47+
48+
for year in range(2026, 2030):
49+
row = df_post_2025[df_post_2025["Year"] == year].iloc[0]
50+
cpi_change = (
51+
row["CPIH"]
52+
/ df_post_2025[df_post_2025["Year"] == year - 1]["CPIH"].values[0]
53+
- 1
54+
) * 100
55+
# Increase the nominal bills by the CPIH change
56+
addition = df_post_2025.loc[
57+
df_post_2025["Year"] == year - 1, "Avg bills (nominal)"
58+
].values[0] * (cpi_change / 100)
59+
# Add addition to this and future years
60+
df_post_2025.loc[
61+
df_post_2025["Year"] >= year, "Avg bills (nominal)"
62+
] += addition
63+
64+
df_post_2025["Relative change"] = (
65+
df_post_2025["Avg bills (nominal)"].pct_change() * 100
66+
).round(1)
67+
68+
df_post_2025
69+
70+
combined_water_forecast = pd.DataFrame(
71+
{
72+
"Year": list(range(2022, 2030)),
73+
"Average nominal bills YoY change": df_pre_2025[
74+
"Nominal YoY change"
75+
].tolist()[1:]
76+
+ df_post_2025["Relative change"].tolist()[1:],
77+
}
78+
)
79+
80+
print(combined_water_forecast.to_markdown(index=False))

policyengine_uk/variables/input/consumption/property/water_and_sewerage_charges.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ class water_and_sewerage_charges(Variable):
77
label = "water and sewerage charges"
88
documentation = "Total amount spent on water and sewerage charges"
99
definition_period = YEAR
10+
uprating = "gov.economic_assumptions.indices.ofwat.water_bills"

0 commit comments

Comments
 (0)