An open API service indexing awesome lists of open source software.

https://github.com/flrd/standardlastprofile

R Data Package for BDEW Standard Load Profiles in Electricity
https://github.com/flrd/standardlastprofile

data electricity germany r

Last synced: 3 months ago
JSON representation

R Data Package for BDEW Standard Load Profiles in Electricity

Awesome Lists containing this project

README

          

---
output: github_document
---

```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "95%",
fig.align = "center"
)
```

# standardlastprofile

[![R-CMD-check](https://github.com/flrd/standardlastprofile/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/flrd/standardlastprofile/actions/workflows/R-CMD-check.yaml)
[![codecov](https://codecov.io/gh/flrd/standardlastprofile/branch/main/graph/badge.svg)](https://app.codecov.io/gh/flrd/standardlastprofile)
[![CRAN version](https://www.r-pkg.org/badges/version/standardlastprofile)](https://cran.r-project.org/package=standardlastprofile)
[![CRAN downloads](http://cranlogs.r-pkg.org/badges/grand-total/standardlastprofile)](https://cran.r-project.org/package=standardlastprofile)

This package provides data on standard load profiles for electricity, published by the German Association of Energy and Water Industries (BDEW Bundesverband der Energie- und Wasserwirtschaft e.V.).

``` {r, message=FALSE, include = FALSE}
library(standardlastprofile)
library(ggplot2)
```

```{r bdew-1999-small_multiples, echo = FALSE, fig.asp = 2.64, fig.retina=2}
#| fig.alt = "Small multiple line chart of 11 standard load profiles
#| published by the German Association of Energy and Water Industries (BDEW
#| Bundesverband der Energie- und Wasserwirtschaft e.V.). The lines compare
#| the consumption for three different periods over a year, and
#| also compare the consumption between different days of a week."

standardlastprofile:::slp_plot_1999()
```

## Installation

You can install standardlastprofile from CRAN with:

```{r eval=FALSE}
install.packages("standardlastprofile")
```

To install the development version from [GitHub](https://github.com/) use:

``` {r eval=FALSE}
# install.packages("pak")
pak::pkg_install("flrd/standardlastprofile")
```

## Included Features

- `slp` — a dataset containing BDEW standard load profiles for electricity.
- `slp_generate()` — generate a standard load profile for a user-defined time period.
- `slp_info()` — retrieve details of standard load profiles.

## About the Data

The dataset `slp` contains 26,784 observations across 5 variables:

- `profile_id`: identifier of a standard load profile
- `period`: one of `"summer"`, `"winter"`, `"transition"` for the 1999 profiles; a lowercase month name (`"january"` … `"december"`) for the 2025 profiles
- `day`: one of `"workday"`, `"saturday"`, `"sunday"`
- `timestamp`: format `"%H:%M"`
- `watts`: electric power

``` {r, message=FALSE}
library(standardlastprofile)
str(slp)
```

In the context of the German electricity market, the term *Standard Load Profile* denotes a representative pattern of electricity consumption over a specific period. These profiles portray anticipated electricity consumption for diverse customer groups, like households or businesses. While not an exact match for an individual customer's consumption profile, they serve as a valid approximation for larger groups of similar customers.

For each unique combination of `profile_id`, `period` and `day` there are 96 x 1/4 hour measurements in watts. The dataset covers two generations of profiles:

**1999 profiles**[^bdew-2] — based on an analysis of 1,209 load profiles of low-voltage electricity consumers in Germany:

[^bdew-2]: More information on the data and methodology can be found [here](https://www.bdew.de/media/documents/1999_Repraesentative-VDEW-Lastprofile.pdf).

- `H0`: households (German: "Haushalte")
- `G0` to `G6`: commerce ("Gewerbe")
- `L0` to `L2`: agriculture ("Landwirtschaft")

**2025 profiles** — an updated set published by BDEW reflecting changes in consumption patterns:

- `H25`, `G25`, `L25`: updated household, commerce, and agriculture profiles
- `P25`: households with a photovoltaic (PV) system
- `S25`: households with a PV system and battery storage

For more details, call the `slp_info()` function.

``` {r, message=FALSE, echo=TRUE}
slp_info(profile_id = "H0", language = "DE")
```

## 2025 Profiles

In 2025, BDEW published an updated set of standard load profiles reflecting
changes in electricity consumption patterns since the original 1999 study. Five
new profiles are included:

- `H25`: households — updated version of `H0`
- `G25`: commerce (general) — updated version of `G0`
- `L25`: agriculture — updated version of `L0`
- `P25`: combination profile for households with a photovoltaic (PV) system
- `S25`: combination profile for households with a PV system and battery storage

Unlike the 1999 profiles, which group days into three seasonal periods
(`winter`, `summer`, `transition`), the 2025 profiles provide a separate set
of values for each calendar month. `P25` and `S25` are entirely new profile
types with no 1999 equivalent, capturing the growing role of distributed
generation and storage in residential electricity consumption.

```{r bdew-2025-small_multiples, echo = FALSE, fig.asp = 1.2, fig.retina = 2}
#| fig.alt = "Small multiple line chart of five standard load profiles published
#| by the German Association of Energy and Water Industries (BDEW) in 2025.
#| Lines are coloured by calendar month and faceted by profile and day type."

standardlastprofile:::slp_plot_2025()
```

The chart below places the 2025 household profiles side by side against
`H0` as a reference, showing how cumulative energy consumption diverges over
the course of a year.

```{r H0_vs_2025, echo=FALSE, message=FALSE, fig.asp=0.78, fig.retina=2}
#| fig.alt = "Faceted line plot with three panels for profiles H25, P25, and
#| S25. Each panel shows cumulative energy consumption in kWh over 2026.
#| A grey reference line shows H0 in every panel. H25 tracks H0 closely,
#| while P25 and S25 diverge due to photovoltaic generation and battery
#| storage respectively."
dat <- slp_generate(
profile_id = c("H0", "H25", "P25", "S25"),
start_date = "2026-01-01",
end_date = "2026-12-31"
)

dat$kWh <- tapply(dat, dat$profile_id, \(x) {
(cumsum(x$watts) - x$watts[1]) / 4 / 1000
}) |>
unlist()

ggplot(transform(dat, profile_facet = profile_id), aes(start_time, kWh)) +
geom_line(
data = dat[, c("start_time", "kWh", "profile_id")],
aes(group = profile_id),
colour = "#CCCCCC"
) +
geom_line(aes(colour = profile_id), linewidth = 1) +
facet_wrap(vars(profile_facet), ncol = 2) +
scale_x_datetime(NULL, date_labels = "%b") +
scale_y_continuous(breaks = c(0, 500, 1000)) +
scale_colour_manual(
values = c(
"H25" = "#961BFA",
"P25" = "#FA9529",
"S25" = "#0CC792"
)
) +
labs(
title = "H0 vs. H25, S25, and P25",
subtitle = "Cumulative energy consumption over the course of one year",
caption = "data: www.bdew.de",
y = "kWh"
) +
theme_minimal() +
theme(legend.position = "none") +
theme(strip.text.y.right = element_text(angle = 0)) +
theme(panel.grid.minor.x = element_blank()) +
theme(panel.grid.minor.y = element_blank()) +
theme(
panel.grid = element_line(
linetype = "12",
lineend = "round",
colour = "#FAF6F4"
)
)
```

`H25` tracks `H0` almost exactly, confirming that the updated household profile
represents a similar consumption pattern to its 1999 predecessor. `P25` and
`S25`, on the other hand, show a flattening of the cumulative curve from spring
through summer: households with a photovoltaic system — and even more so those
with additional battery storage — draw less energy from the grid during the
months of high solar yield.

### Generate a Standard Load Profile

To create a standard load profile for a specified time period, call the `slp_generate()` function:

``` {r, G5_data_readme, message=FALSE, echo=TRUE}
G5 <- slp_generate(
profile_id = "G5",
start_date = "2023-12-22",
end_date = "2023-12-27"
)

head(G5)
```

``` {r G5_plot_readme, message=FALSE, echo=FALSE, fig.retina=2, fig.asp=0.5}
#| fig.alt = "Line plot of the standard load profile 'G5' (i.e. Bakery
#| with a bakehouse) based on data from the German Association of Energy
#| and Water Industries (BDEW Bundesverband der Energie- und Wasserwirtschaft
#| e.V.) from December 22nd to December 27th 2023; values
#| are normalised to an annual consumption of 1,000 kWh."
ggplot(G5, aes(start_time, watts)) +
geom_line(color = "#0CC792") +
scale_x_datetime(
date_breaks = "1 day",
date_labels = "%b %d"
) +
scale_y_continuous(NULL, labels = \(x) paste(x, "W")) +
labs(
title = "'G5': bakery with bakehouse",
subtitle = "1/4h measurements, based on consumption of 1,000 kWh/a",
caption = "data: www.bdew.de",
x = NULL
) +
theme_minimal() +
theme(
panel.grid.minor.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid = element_line(
linetype = "12",
lineend = "round",
colour = "#FAF6F4"
)
) +
NULL
```

### Public Holidays

By default, `slp_generate()` treats the nine public holidays observed
nationwide in Germany as Sundays:

* New Year's (Jan 1)
* Good Friday
* Easter Monday
* Labour Day (May 1)
* Ascension Day
* Whit Monday
* German Unity Day (Oct 3)
* Christmas Day (Dec 25)
* Boxing Day (Dec 26)

State-level holidays are _not_ included because they vary by state and can
change over time. Use the `holidays` argument to supply your own dates — the
built-in data are then ignored entirely.

The example below fetches all 2027 public holidays for Germany and the state of
Berlin from the [nager.Date API](https://date.nager.at), and passes them to
`slp_generate()`. Berlin observes **International Women's Day** (8 March) as
an additional public holiday not shared by any other state.

``` {r holidays-berlin, eval=FALSE}
library(httr2)

resp <- request("https://date.nager.at") |>
req_url_path_append("api", "v3", "PublicHolidays", "2027", "DE") |>
req_perform() |>
resp_body_json()

# global == TRUE → nationwide holiday (counties is NULL)
# global == FALSE → counties lists the states that observe it
is_berlin <- \(x) isTRUE(x$global) || "DE-BE" %in% unlist(x$counties)

holidays_berlin_2027 <- as.Date(
vapply(Filter(is_berlin, resp), \(x) x$date, character(1))
)

H0_berlin_2027 <- slp_generate(
"H0", "2027-01-01", "2027-12-31",
holidays = holidays_berlin_2027
)
```

To generate a standard load profile including holidays for each of the
16 German states, repeat the same pattern in a loop — one API call suffices,
re-filter per state:

``` {r holidays-all-states, eval=FALSE}
states <- c(
"DE-BB", "DE-BE", "DE-BW", "DE-BY", "DE-HB", "DE-HE",
"DE-HH", "DE-MV", "DE-NI", "DE-NW", "DE-RP", "DE-SH",
"DE-SL", "DE-SN", "DE-ST", "DE-TH"
)

results <- vector("list", length(states))
names(results) <- states

for (state in states) {
is_state <- \(x) isTRUE(x$global) || state %in% unlist(x$counties)
holidays_state <- as.Date(
vapply(Filter(is_state, resp), \(x) x$date, character(1))
)
results[[state]] <- slp_generate(
profile_id = "H0",
start_date = "2027-01-01",
end_date = "2027-12-31",
holidays = holidays_state
)
}
```

For more information, details about the data, and an explanation of the algorithm,
see the [vignette](https://flrd.github.io/standardlastprofile/articles/standardlastprofile.html)
or run `vignette("standardlastprofile", package = "standardlastprofile")` locally.

## Source
You can access the studies and data on standard load profiles for electricity on the website of the BDEW: https://www.bdew.de/energie/standardlastprofile-strom/

## Code of Conduct
Please note that this project is released with a [Contributor Code of Conduct](https://github.com/flrd/standardlastprofile/blob/main/CODE_OF_CONDUCT.md).
By participating in this project you agree to abide by its terms.