---
title: "Mikä latausasema kannattaisi pitää pystyssä hinnalla millä hyvänsä?"
subtitle: "Latausverkko jakaumana, osa 7: kestävyys, riskikartta ja Lapin eristäminen"
description: |
Latausverkon toimivuus näkyy vasta kun jotain hajoaa. Poistamme asemia yksi
kerrallaan ja mittaamme, kuinka paljon koko maan saavutettavuus heikkenee.
Tuloksena on kartta, joka kertoo mitkä asemat pitävät verkkoa kasassa.
date: 2026-10-13
categories: [avoin data, verkostoanalyysi, latausverkko, riskianalyysi, kestävyys]
---
```{r}
#| label: setup
#| include: false
#| cache: false
library(here)
library(tidyverse)
library(sf)
library(dodgr)
library(igraph)
library(ggrepel)
library(ggdist)
library(patchwork)
library(qs2)
library(scales)
select <- dplyr::select
filter <- dplyr::filter
count <- dplyr::count
source(here("R", "theme_kristian.R"))
knitr::opts_chunk$set(
echo = FALSE, warning = FALSE, message = FALSE,
fig.width = 9, fig.height = 5.5, dpi = 150
)
theme_set(theme_kristian())
pal <- c(punainen = "#e63946", turkoosi = "#2a9d8f", oranssi = "#f4a261",
navy = "#1d3557", sininen = "#457b9d")
data_dir <- here("data", "latausverkko")
set.seed(42)
asemat <- qs2::qd_read(file.path(data_dir, "asemat.qs"))
liitokset <- qs2::qd_read(file.path(data_dir, "liitokset.qs"))
kunnat_sf <- qs2::qs_read(file.path(data_dir, "kunnat_lau.qs"))
pno <- qs2::qs_read(file.path(data_dir, "paavo.qs"))
tieverkko <- qs2::qs_read(file.path(data_dir, "tieverkko.qs"))
tieverkko <- tieverkko[tieverkko$component == 1, ]
# Koordinaattijärjestelmä pinnataan uudelleen: eri PROJ-versiot tuottavat
# samasta EPSG-koodista hieman eri merkkijonon, ja sf vertaa merkkijonoja.
st_crs(kunnat_sf) <- 3067
st_crs(pno) <- 3067
# Kotilatausmahdollisuus arvioidaan pientaloasumisen osuudesta, kuten
# tämän sarjan aiemmassa osassa. Kerrostaloasuja on julkisen verkon
# varassa arjessa; pientaloasuja ei ole.
stopifnot(
"Paavo-aineistosta puuttuu rakennuskantatieto" =
all(c("pientaloasunnot", "kerrostaloasunnot") %in% names(pno))
)
pno <- pno |>
mutate(
asuntoja = pientaloasunnot + kerrostaloasunnot,
pientalo_osuus = if_else(asuntoja > 0, pientaloasunnot / asuntoja,
NA_real_),
# Arjessa julkisesta verkosta riippuvaiset asukkaat
arkiriippuvaiset = asukkaat * (1 - coalesce(pientalo_osuus, 0.5))
)
stopifnot(
"Pientalo-osuus jakauman ulkopuolella" =
all(between(pno$pientalo_osuus, 0, 1), na.rm = TRUE),
"Arkiriippuvaisia enemmän kuin asukkaita" =
all(pno$arkiriippuvaiset <= pno$asukkaat + 1e-9)
)
```
## Verkko näyttää hyvältä siihen asti kun se ei näytä
Latausverkon tilaa arvioidaan sen mukaan, miten se toimii normaalisti. Kuinka
monta asemaa on, kuinka kaukana ne ovat, kuinka moni käyttää niitä.
Nämä mittarit kuvaavat verkkoa hyvässä säässä. Infrastruktuurin arvo mitataan
kuitenkin siinä, mitä tapahtuu kun jokin pettää — ja se ei näy normaalitilan
mittareista lainkaan.
Asema voi olla epäkunnossa, sähköt voivat olla poikki, maksujärjestelmä voi
kaatua, tai kaikki latauspaikat voivat olla varattuja juhannusaattona.
Autoilijalle nämä tarkoittavat samaa asiaa: asema on siinä mutta sitä ei voi
käyttää.
Tässä osassa poistan asemia yksi kerrallaan ja mittaan, kuinka paljon koko maan
saavutettavuus heikkenee. Lopputulos on kartta, jossa jokainen asema on
väritetty sen mukaan, kuinka paljon sen katoaminen haittaisi.
Se on eri kartta kuin lista suurimmista tai vilkkaimmista asemista.
## Menetelmä: poista yksi, mittaa kaikki
Idea on yksinkertainen. Lasketaan ensin, kuinka kaukana lähin suurtehulaturi on
jokaisesta postinumeroalueesta. Sitten poistetaan yksi asema ja lasketaan sama
uudelleen. Erotus kertoo, kuinka paljon tuo asema oli tuomassa
saavutettavuutta.
Kun tämä toistetaan jokaiselle asemalle, saadaan jokaiselle asemalle luku:
**paljonko koko maan saavutettavuus heikkenisi, jos tämä asema katoaisi
huomenna.**
Laskenta on kevyempi kuin miltä kuulostaa. Kun etäisyydet jokaisesta alueesta
jokaiseen asemaan on kerran laskettu, aseman poistaminen tarkoittaa vain yhden
sarakkeen pudottamista taulukosta ja rivikohtaisen pienimmän arvon etsimistä
uudelleen. Reititystä ei tarvitse tehdä uudelleen.
```{r}
#| label: hpc-ja-matriisi
#| echo: true
hpc <- liitokset |>
filter(teho_kw >= 150) |>
group_by(avain) |>
summarise(max_kw = max(teho_kw, na.rm = TRUE), pistokkeita = n(),
.groups = "drop") |>
left_join(asemat, by = "avain") |>
filter(!is.na(lat), !is.na(lon))
stopifnot(
"Suurtehulatureita ei löytynyt" = nrow(hpc) > 50,
"Koordinaatit Suomen ulkopuolella" =
all(between(hpc$lat, 59.4, 70.2) & between(hpc$lon, 18.9, 31.7))
)
matriisi_path <- file.path(data_dir, "saavutettavuusmatriisi.qs")
if (!file.exists(matriisi_path)) {
from <- pno |>
st_transform(4326) |> st_coordinates() |>
as_tibble() |> setNames(c("x", "y")) |> as.matrix()
ajat <- dodgr_times(
graph = tieverkko, from = from,
to = as.matrix(select(hpc, x = lon, y = lat))
) / 60
dimnames(ajat) <- list(pno$pno, hpc$avain)
qs2::qs_save(ajat, matriisi_path)
} else {
ajat <- qs2::qs_read(matriisi_path)
}
stopifnot(
"Matriisin koko ei täsmää" =
nrow(ajat) == nrow(pno) && ncol(ajat) == nrow(hpc),
"Matriisissa on negatiivisia aikoja" = all(ajat >= 0, na.rm = TRUE)
)
# Alueet, joista ei pääse yhdellekään asemalle, jätetään pois: ne eivät
# reagoi minkään aseman poistamiseen eivätkä siis kerro kriittisyydestä.
nyky_min <- apply(ajat, 1, \(r) min(r, na.rm = TRUE))
kelpo <- is.finite(nyky_min) & pno$asukkaat > 0
ajat_k <- ajat[kelpo, , drop = FALSE]
vakiluku <- pno$asukkaat[kelpo]
arkiriippuvaiset <- pno$arkiriippuvaiset[kelpo]
nyky <- nyky_min[kelpo]
stopifnot(
"Yli 10 % alueista jäi ulkopuolelle" = mean(kelpo) > 0.90,
"Väkilukuja puuttuu" = all(!is.na(vakiluku)),
"Arkiriippuvaisten painoja puuttuu" = all(!is.na(arkiriippuvaiset))
)
nyky_keskiaika <- weighted.mean(nyky, vakiluku)
arkiriippuvaisia_yht <- sum(arkiriippuvaiset)
arkiosuus <- arkiriippuvaisia_yht / sum(vakiluku)
```
```{r}
#| label: poistosimulaatio
#| echo: true
# Jokaisen aseman poisto vuorollaan. Poisto = sarakkeen pudotus ja
# rivikohtaisen minimin laskenta uudelleen.
#
# Vaikutusta EI mitata koko maan keskiarvona. Kriittisinkin asema koskettaa
# vain murto-osaa väestöstä, joten koko maan yli jaettuna sen katoaminen
# näyttää muutaman sekunnin asialta. Kolme lukua kertoo sen, minkä yksi
# keskiarvo peittää: keitä koskee, kuinka paljon heitä koskee, ja kuinka
# moni ylittää kipurajan.
kipuraja_min <- 30
poistovaikutus <- map_dfr(seq_len(ncol(ajat_k)), function(j) {
uusi_min <- apply(ajat_k[, -j, drop = FALSE], 1, \(r) min(r, na.rm = TRUE))
# Osa alueista voi menettää yhteyden kokonaan
katkeaa <- !is.finite(uusi_min) & is.finite(nyky)
muutos <- ifelse(katkeaa, NA_real_, uusi_min - nyky)
koskee <- !is.na(muutos) & muutos > 0.5
tibble(
avain = colnames(ajat_k)[j],
# Keitä koskee
asukkaita_koskee = sum(vakiluku[koskee]),
# Keitä koskee arjessa: vain ne, joilla ei ole kotilatausta
arkiriippuvaisia_koskee = sum(arkiriippuvaiset[koskee]),
# Kuinka paljon koskee niitä, joita se koskee
kasvu_koskeville = if (any(koskee))
weighted.mean(muutos[koskee], vakiluku[koskee]) else 0,
# Kokonaisvaikutus: menetettyjä henkilöminuutteja latauskertaa kohti
henkilominuutteja = sum(muutos[koskee] * vakiluku[koskee]),
# Sama arkikäytön näkökulmasta: kotilataus vähentää tarvetta
arki_henkilominuutteja = sum(muutos[koskee] * arkiriippuvaiset[koskee]),
# Kynnysmittari: kuinka moni siirtyy kipurajan yli
yli_kipurajan = sum(vakiluku[!is.na(muutos) &
nyky <= kipuraja_min &
uusi_min > kipuraja_min]),
# Kuinka moni menettää yhteyden kokonaan
katkeaa_asukkaita = sum(vakiluku[katkeaa]),
# Pahin yksittäinen alue
pahin_alue_min = suppressWarnings(max(muutos, na.rm = TRUE)),
# Koko maan keskiarvo kuljetaan mukana vain vertailua varten
keskikasvu_min = weighted.mean(muutos, vakiluku, na.rm = TRUE)
)
}) |>
mutate(pahin_alue_min = if_else(is.finite(pahin_alue_min),
pahin_alue_min, NA_real_)) |>
left_join(select(hpc, avain, nimi, kunta, op_nimi, max_kw, pistokkeita,
lat, lon), by = "avain")
stopifnot(
"Poistolaskenta tuotti negatiivisia muutoksia" =
all(poistovaikutus$kasvu_koskeville >= -1e-9, na.rm = TRUE),
"Poistolaskenta epäonnistui" = nrow(poistovaikutus) == nrow(hpc),
"Kynnysmittari on nolla kaikilla – valitse toinen kipuraja" =
sum(poistovaikutus$yli_kipurajan) > 0
)
# Kuinka paljon laimennus muuttaa kuvaa? Tämä luku menee tekstiin.
laimennuskerroin <- poistovaikutus |>
slice_max(henkilominuutteja, n = 1) |>
transmute(kerroin = kasvu_koskeville / pmax(keskikasvu_min, 1e-9)) |>
pull(kerroin)
jakauman_muoto <- poistovaikutus |>
summarise(
mediaani = median(kasvu_koskeville, na.rm = TRUE),
maksimi = max(kasvu_koskeville, na.rm = TRUE),
ei_ketaan = mean(asukkaita_koskee == 0),
top10_osuus = sum(sort(henkilominuutteja, decreasing = TRUE)[1:10],
na.rm = TRUE) / sum(henkilominuutteja, na.rm = TRUE)
)
```
### Miksi koko maan keskiarvo ei kelpaa tähän
Ilmeisin tapa mitata aseman kriittisyyttä olisi laskea, kuinka paljon
suomalaisten keskimääräinen matka lähimmälle laturille pitenisi. Kokeilin sitä
ensin, ja tulos oli käyttökelvoton.
Syy on aritmetiikassa. Maan kriittisinkin asema koskettaa muutamaa tuhatta
ihmistä. Kun heidän kokemansa kymmenien minuuttien lisäys jaetaan viiden ja
puolen miljoonan asukkaan kesken — joista valtaosa ei huomaisi mitään —
jäljelle jää muutama sekunti.
Kriittisimmän aseman kohdalla ero on
`r format(round(laimennuskerroin), big.mark = " ")`-kertainen: se, mitä se
tarkoittaa asianosaisille, on tuhatkertainen siihen nähden, miltä se näyttää
kansallisessa keskiarvossa.
Luku ei olisi ollut väärin. Se olisi ollut tosi ja merkityksetön — ja juuri
siksi vaarallinen, koska se olisi antanut ymmärtää ettei yhdelläkään asemalla
ole väliä.
Mittaan siksi kolmea asiaa erikseen: **keitä** aseman katoaminen koskee,
**kuinka paljon** se koskee juuri heitä, ja **kuinka moni** siirtyy sen myötä
kohtuullisen matkan päästä kohtuuttoman taakse.
```{r}
#| label: kuva-jakauma
#| fig-cap: "Asemien kriittisyyden jakauma kahdella eri mittarilla."
#| fig-height: 6.5
p_vaara <- poistovaikutus |>
filter(!is.na(keskikasvu_min)) |>
ggplot(aes(keskikasvu_min * 60)) +
geom_histogram(bins = 50, fill = "grey35", colour = NA) +
scale_y_continuous(expand = expansion(mult = c(0, 0.08))) +
labs(x = "Koko maan keskimääräisen ajoajan kasvu (sekuntia)", y = "Asemia",
title = "Väärä mittari: koko maan keskiarvo",
subtitle = "Kaikki asemat näyttävät merkityksettömiltä, koska luku on jaettu koko väestölle")
p_oikea <- poistovaikutus |>
filter(asukkaita_koskee > 0) |>
ggplot(aes(kasvu_koskeville)) +
geom_histogram(bins = 50, fill = pal["turkoosi"], colour = NA) +
scale_y_continuous(expand = expansion(mult = c(0, 0.08))) +
labs(x = "Ajoajan kasvu niille, joita asema koskee (min)", y = "Asemia",
title = "Oikea mittari: vaikutus asianosaisille",
subtitle = "Sama laskenta, sama data – vain nimittäjä on eri")
p_vaara / p_oikea
```
**Miten kuvaa luetaan.** Molemmat paneelit kuvaavat täsmälleen samaa laskentaa.
Ainoa ero on se, kenen kesken vaikutus jaetaan.
Ylempi harmaa paneeli jakaa sen koko Suomen väestölle, ja silloin akselin
yksikkönä on sekunti. Alempi jakaa sen vain niille, joiden matka oikeasti
pitenee, ja silloin yksikkönä on minuutti.
Alemmassa paneelissa valtaosa asemista on edelleen vasemmassa laidassa. Se ei
tarkoita että ne olisivat tarpeettomia — se tarkoittaa, että niiden vieressä on
toinen asema. Jos sellainen asema katoaa, käyttäjä ajaa naapuriin eikä kukaan
huomaa mitään.
Oikeassa hännässä on asemia, joiden katoaminen tarkoittaisi asianosaisille
kymmenien minuuttien lisämatkaa. Ne ovat tämän osan kohde.
Kymmenen kriittisintä asemaa vastaa
`r percent(jakauman_muoto$top10_osuus, accuracy = 1)` kaikkien asemien
yhteenlasketusta kokonaisvaikutuksesta.
## Riskikartta
```{r}
#| label: kuva-riskikartta
#| fig-cap: "Latausasemien kriittisyys: kuinka paljon koko maan saavutettavuus heikkenisi, jos asema katoaisi."
#| fig-height: 9.5
#| fig-width: 7.5
riski_sf <- poistovaikutus |>
filter(asukkaita_koskee > 0) |>
st_as_sf(coords = c("lon", "lat"), crs = 4326) |>
st_transform(3067)
ggplot() +
geom_sf(data = st_transform(kunnat_sf, 3067),
fill = "grey11", colour = "grey18", linewidth = 0.05) +
geom_sf(data = riski_sf |> arrange(kasvu_koskeville),
aes(colour = kasvu_koskeville, size = asukkaita_koskee),
alpha = 0.85) +
scale_colour_gradientn(
colours = c(unname(pal["sininen"]), unname(pal["turkoosi"]),
unname(pal["oranssi"]), unname(pal["punainen"])),
values = scales::rescale(c(0, 5, 20, max(riski_sf$kasvu_koskeville))),
breaks = c(0, 15, 30, 45, 60),
labels = c("0", "15 min", "30 min", "45 min", "1 h"),
name = "Lisämatka niille, joita asema koskee",
guide = guide_colourbar(barwidth = unit(9, "cm"),
barheight = unit(0.4, "cm"),
title.position = "top",
label.theme = element_text(angle = 45, hjust = 1,
size = 8,
colour = "grey85"))
) +
scale_size_continuous(range = c(0.6, 7), guide = "none") +
labs(title = "Suomen latausverkon riskikartta",
subtitle = "Väri: kuinka paljon matka pitenee asianosaisille. Koko: kuinka moni heitä on.",
caption = "Aineistot: OpenStreetMap, Fintraffic AFIR, Tilastokeskus") +
theme(axis.text = element_blank(), axis.ticks = element_blank(),
panel.grid = element_blank(), legend.position = "bottom")
```
**Miten karttaa luetaan.** Jokainen piste on suurtehulatausasema.
Pisteen **väri** kertoo, kuinka paljon matka lähimmälle laturille pitenisi
niillä ihmisillä, joita aseman katoaminen koskee. Sininen tarkoittaa muutamaa
minuuttia, punainen puolta tuntia tai enemmän.
Pisteen **koko** kertoo, kuinka moni ihminen on asianosainen.
Nämä kaksi eivät kulje käsi kädessä, ja siinä on kartan koko viesti. Pieni
punainen piste on asema, jonka katoaminen olisi harvoille katastrofi. Suuri
sininen piste on asema, jonka katoaminen olisi monille pieni harmi. Kumpaa
pidetään pahempana, on arvovalinta — mutta se on valinta, joka pitää tehdä
tietoisesti eikä mittarin sivutuotteena.
Kartta ei kerro, mitkä asemat ovat vilkkaimpia tai suurimpia. Se kertoo, mitkä
ovat korvaamattomimpia — ja nämä kaksi ovat eri asioita. Vilkkain asema on
siellä missä on paljon ihmisiä, ja siellä on yleensä myös vaihtoehtoja.
```{r}
#| label: kuva-karki
#| fig-cap: "Kriittisimmät latausasemat."
poistovaikutus |>
slice_max(henkilominuutteja, n = 15) |>
mutate(label = paste0(str_trunc(nimi, 30), "\n", kunta, " · ", op_nimi),
label = fct_reorder(label, henkilominuutteja)) |>
select(label,
`Lisämatka asianosaisille (min)` = kasvu_koskeville,
`Asianosaisia (henkeä)` = asukkaita_koskee) |>
pivot_longer(-label) |>
ggplot(aes(value, label)) +
geom_col(fill = pal["punainen"], width = 0.68) +
facet_wrap(~name, scales = "free_x") +
scale_x_continuous(expand = expansion(mult = c(0, 0.10)),
labels = label_number(big.mark = " ")) +
labs(x = NULL, y = NULL,
title = "Asemat, joita ilman verkko toimii huonoimmin",
subtitle = "Järjestetty kokonaisvaikutuksen mukaan: lisämatka kerrottuna asianosaisten määrällä") +
theme(axis.text.y = element_text(size = 7))
```
**Miten kuvaa luetaan.** Vasen paneeli kertoo, kuinka paljon matka pitenisi
niillä ihmisillä, joita aseman katoaminen koskee. Oikea kertoo, kuinka monta
heitä on.
Asemat on järjestetty näiden tulon mukaan, eli kokonaisvaikutuksen. Paneelien
järjestys ei ole sama, ja se on tarkoituksellista: osa asemista nousee listalle
siksi että vaikutus on suuri, osa siksi että asianosaisia on paljon.
## Sama asema ei ole yhtä tärkeä kaikille
Tähän asti olen mitannut yhtä asiaa: kuinka paljon matka lähimmälle
suurtehulaturille pitenee, jos asema katoaa. Mittari olettaa hiljaisesti, että
kaikki tarvitsevat sitä asemaa samalla tavalla.
He eivät tarvitse. Suurtehulaturia käytetään kahteen täysin eri tarkoitukseen,
ja niiden kriittisyys määräytyy eri tavalla.
**Arkilataus.** Asukas, jolla ei ole omaa latauspaikkaa, käy lataamassa autonsa
lähimmällä julkisella laturilla samaan tapaan kuin ennen käytiin bensa-asemalla.
Hänelle lähin asema on arkinen välttämättömyys. Omakotitalossa asuva, joka
lataa autonsa yöllä kotona, ei tätä asemaa tarvitse juuri koskaan.
**Matkalataus.** Pitkällä matkalla auto on ladattava jossain matkan varrella.
Tämä ei riipu lainkaan siitä, missä matkalainen asuu tai onko hänellä kotona
latauspistettä. Helsingistä Rovaniemelle ajava tarvitsee aseman kilometrillä
kolmesataa myös silloin, kun hän on lähtenyt liikkeelle täydellä akulla
omakotitalon autotallista.
Näistä ensimmäinen on herkkä asumismuodolle, jälkimmäinen ei ole sille lainkaan
herkkä. Siksi ne on laskettava erikseen.
### Arjen kriittisyys: ketä lähin asema oikeasti palvelee
Tämän sarjan aiemmassa osassa arvioitiin kotilatausmahdollisuutta
pientaloasumisen osuudesta. Käytän samaa arviota tässä: alueen asukkaista
julkisesta verkosta arjessa riippuvaisiksi lasketaan kerrostaloasumisen osuutta
vastaava osa.
Koko maassa tämä tarkoittaa noin
`r percent(arkiosuus, accuracy = 1)` väestöstä. Loput lataavat pääosin kotona.
```{r}
#| label: arki-vs-kaikki
#| echo: true
arkivertailu <- poistovaikutus |>
mutate(
arjen_paino = if_else(henkilominuutteja > 0,
arki_henkilominuutteja / henkilominuutteja,
NA_real_)
)
# Järjestyksen muutos: kuinka moni kärkiasemista vaihtuu, kun
# kotilataus otetaan huomioon?
karki_kaikki <- arkivertailu |> slice_max(henkilominuutteja, n = 20) |> pull(avain)
karki_arki <- arkivertailu |> slice_max(arki_henkilominuutteja, n = 20) |> pull(avain)
paallekkaisyys <- length(intersect(karki_kaikki, karki_arki))
kor_arki <- cor(arkivertailu$henkilominuutteja,
arkivertailu$arki_henkilominuutteja,
method = "spearman", use = "complete.obs")
```
Kahdenkymmenen kärjestä `r paallekkaisyys` on sama molemmilla mittareilla, ja
järjestyskorrelaatio on
`r format(round(kor_arki, 2), decimal.mark = ",")`.
Kotilatauksen huomioiminen ei siis mullista järjestystä, ja syy on
ymmärrettävä: asemat ovat siellä missä on asutusta, ja asutuksen rakenne
vaihtelee vähemmän kuin asemien harvuus. Mutta se siirtää painopistettä
taajamiin päin, koska siellä kerrostaloasumisen osuus on suurin.
```{r}
#| label: kuva-arkipaino
#| fig-cap: "Kuinka suuri osa aseman vaikutuksesta kohdistuu arjessa julkisesta verkosta riippuvaisiin."
arkivertailu |>
filter(!is.na(arjen_paino), asukkaita_koskee > 0) |>
ggplot(aes(kasvu_koskeville, arjen_paino)) +
geom_hline(yintercept = arkiosuus, colour = pal["punainen"], linetype = 2) +
geom_point(aes(size = asukkaita_koskee), colour = pal["turkoosi"],
alpha = 0.5) +
geom_text_repel(
data = \(d) d |> slice_max(kasvu_koskeville, n = 6),
aes(label = kunta), colour = "grey80", size = 3,
max.overlaps = 12, seed = 42
) +
scale_y_continuous(labels = percent) +
scale_size_continuous(range = c(1.2, 7), guide = "none") +
labs(x = "Lisämatka asianosaisille, jos asema katoaa (min)",
y = "Osuus vaikutuksesta, joka kohdistuu kotilatausta vailla oleviin",
title = "Kaukana sijaitsevan aseman käyttäjä lataa todennäköisemmin kotona",
subtitle = "Punainen katkoviiva: koko maan taso")
```
**Miten kuvaa luetaan.** Vaaka-akselilla on aseman kriittisyys asianosaisille,
pystyakselilla se, kuinka suuri osa asianosaisista on vailla kotilatausta.
Punainen katkoviiva on koko maan taso.
Viivan alapuolella olevat asemat palvelevat keskimääräistä enemmän
omakotitaloasujia, jotka lataavat pääosin kotona. Viivan yläpuolella olevat
palvelevat kerrostaloasujia, joille julkinen verkko on ainoa vaihtoehto.
Kuvassa näkyy selvä kaltevuus: mitä kriittisempi asema on etäisyysmielessä,
sitä todennäköisemmin sen lähialueella asutaan pientaloissa. Se on looginen
seuraus siitä, että harvaan asutuilla alueilla sekä asemia että kerrostaloja on
vähän.
**Tästä seuraa epämiellyttävä johtopäätös.** Jos aseman tärkeyttä mitattaisiin
pelkästään arkikäytöllä, syrjäseutujen asemat putoaisivat listan hännille — ei
siksi että niitä ei tarvittaisi, vaan siksi että niiden lähialueella asutaan
omakotitaloissa.
Arkimittari yksinään siis siirtäisi tuen kaupunkeihin ja jättäisi harvaan
asutut alueet ilman. Sitä varten tarvitaan toinen mittari.
### Matkan kriittisyys: milloin asema on ketjun lenkki
Arkimittari ei tavoita sitä tilannetta, jonka vuoksi syrjäinen asema on usein
korvaamaton. Asema voi olla lähes tyhjä ja silti välttämätön, jos se on ainoa
latausmahdollisuus kahden kaukana toisistaan olevan aseman välissä.
Tällöin kyse ei ole siitä, kuka asuu lähellä. Kyse on siitä, että ilman tuota
asemaa jokin väli kasvaa pidemmäksi kuin akku kestää — ja se koskee jokaista
ohi ajavaa riippumatta siitä, missä hän asuu.
Mittaan tämän kysymällä: **jos asema poistetaan, kuinka pitkäksi kasvaa se
väli, joka sen tilalle jää?**
```{r}
#| label: matkan-kriittisyys
#| echo: true
matriisi_asemat_path <- file.path(data_dir, "matkamatriisi.qs")
stopifnot("Asemien matkamatriisi puuttuu – aja edellinen osa ensin" =
file.exists(matriisi_asemat_path))
mm <- qs2::qs_read(matriisi_asemat_path)
D_kaikki <- mm$matkat
yhteiset <- intersect(rownames(D_kaikki), hpc$avain)
D <- D_kaikki[yhteiset, yhteiset]
stopifnot(
"Matkamatriisi ei kata asemia" = length(yhteiset) > 50,
"Matriisi ei ole neliö" = nrow(D) == ncol(D)
)
# Talvinen pienen akun kantama: väli, jota pidetään liian pitkänä
kantamaraja_km <- 150
# Jokaiselle asemalle: kuinka pitkäksi lähimmän naapurin matka kasvaa
# muilla asemilla, jos tämä asema poistetaan. Jos jonkin aseman lähin
# naapuri hyppää kantamarajan yli, poistettu asema oli ketjun lenkki.
naapurietaisyys <- function(Dm) {
diag(Dm) <- Inf
apply(Dm, 1, \(r) min(r, na.rm = TRUE))
}
nyky_naapuri <- naapurietaisyys(D)
ketjulenkki <- map_dfr(seq_len(nrow(D)), function(j) {
D2 <- D[-j, -j, drop = FALSE]
uusi <- naapurietaisyys(D2)
vanha <- nyky_naapuri[-j]
kasvu <- uusi - vanha
ylittaa <- is.finite(uusi) & vanha <= kantamaraja_km & uusi > kantamaraja_km
tibble(
avain = rownames(D)[j],
pisin_vali_km = suppressWarnings(max(uusi[is.finite(uusi)], na.rm = TRUE)),
vali_kasvu_km = suppressWarnings(max(kasvu[is.finite(kasvu)], na.rm = TRUE)),
rikkoo_ketjun = any(ylittaa)
)
}) |>
mutate(across(c(pisin_vali_km, vali_kasvu_km),
\(x) if_else(is.finite(x), x, NA_real_)))
stopifnot(
"Ketjulaskenta epäonnistui" = nrow(ketjulenkki) == nrow(D),
"Välin kasvu on negatiivinen" =
all(ketjulenkki$vali_kasvu_km >= -1e-9, na.rm = TRUE)
)
n_ketjulenkkeja <- sum(ketjulenkki$rikkoo_ketjun, na.rm = TRUE)
kriittisyys <- poistovaikutus |>
left_join(ketjulenkki, by = "avain")
```
Asemia, joiden poistuminen kasvattaa jonkin välin yli
`r kantamaraja_km` kilometrin, on **`r n_ketjulenkkeja`**.
Nämä ovat matkalatauksen kannalta kriittisiä riippumatta siitä, kuka niiden
lähellä asuu. Raja `r kantamaraja_km` kilometriä vastaa talvista pienen akun
kantamaa; kesällä suurella akulla sama väli olisi ongelmaton.
```{r}
#| label: kuva-kaksi-kriittisyytta
#| fig-cap: "Arjen kriittisyys ja matkan kriittisyys samassa kuvassa."
kriittisyys |>
filter(asukkaita_koskee > 0, !is.na(vali_kasvu_km)) |>
mutate(ryhma = if_else(rikkoo_ketjun,
"Rikkoo ketjun: väli ylittää talvikantaman",
"Ei riko ketjua")) |>
ggplot(aes(arki_henkilominuutteja + 1, vali_kasvu_km)) +
geom_hline(yintercept = kantamaraja_km, colour = "grey55", linetype = 2) +
geom_point(aes(colour = ryhma), size = 2.4, alpha = 0.7) +
geom_text_repel(
data = \(d) d |> filter(rikkoo_ketjun) |> slice_max(vali_kasvu_km, n = 8),
aes(label = kunta), colour = "grey80", size = 3,
max.overlaps = 14, seed = 42
) +
scale_x_log10(labels = label_number(big.mark = " ")) +
scale_colour_manual(values = c(
"Rikkoo ketjun: väli ylittää talvikantaman" = unname(pal["punainen"]),
"Ei riko ketjua" = unname(pal["turkoosi"]))) +
labs(x = "Arjen kriittisyys: henkilöminuutteja kotilatausta vailla oleville (log)",
y = "Suurimman syntyvän välin kasvu (km)",
colour = NULL,
title = "Kaksi eri tapaa olla korvaamaton",
subtitle = "Vasen yläkulma: harvojen arki, mutta kaikkien matka") +
theme(legend.position = "bottom")
```
**Miten kuvaa luetaan.** Vaaka-akselilla on arjen kriittisyys: kuinka paljon
aseman katoaminen haittaisi niitä lähialueen asukkaita, joilla ei ole
kotilatausmahdollisuutta. Akseli on logaritminen, koska arvot vaihtelevat
tuhansiin.
Pystyakselilla on matkan kriittisyys: kuinka paljon pisin väli kasvaa, jos
asema katoaa. Punaiset pisteet ovat asemia, joiden poistuminen kasvattaa välin
yli talvisen kantaman.
Kuvan neljä lohkoa tarkoittavat eri asioita.
*Oikea alakulma* on kaupunkiasema: paljon kotilatausta vailla olevia käyttäjiä,
mutta naapurissa on toinen asema. Jos se katoaa, moni kokee pienen harmin.
*Vasen yläkulma* on juuri se tapaus, jota arkimittari ei näe. Lähellä ei asu
juuri ketään ilman kotilatausta — mutta asema on ketjun ainoa lenkki, ja sen
katoaminen katkaisee matkalatausreitin kaikilta ohi ajavilta.
*Oikea yläkulma* olisi pahin mahdollinen: paljon arkikäyttäjiä ja ketjun lenkki.
Niitä on vähän, mikä on hyvä uutinen.
*Vasen alakulma* on asema, jonka katoamista ei huomaisi juuri kukaan.
**Tämä on osan tärkein korjaus.** Yksi kriittisyysluku ei riitä, koska
latausasemalla on kaksi eri tehtävää eivätkä ne mene samoihin paikkoihin.
Kaupunkiasema on tärkeä siksi että sen käyttäjillä ei ole vaihtoehtoa kotona;
syrjäinen asema on tärkeä siksi että sen käyttäjillä ei ole vaihtoehtoa
matkalla. Tuki, joka mittaa vain toista, kohdistuu väärin.
## Kriittisyys ei ole sama kuin koko
```{r}
#| label: koko-vs-kriittisyys
#| echo: true
kor_koko <- cor.test(poistovaikutus$henkilominuutteja,
poistovaikutus$pistokkeita,
method = "spearman", exact = FALSE)
kor_teho <- cor.test(poistovaikutus$henkilominuutteja,
poistovaikutus$max_kw,
method = "spearman", exact = FALSE)
# Operaattorivertailu
isot_op <- poistovaikutus |> count(op_nimi) |> filter(n >= 8) |> pull(op_nimi)
op_data <- poistovaikutus |> filter(op_nimi %in% isot_op)
kw_testi <- kruskal.test(henkilominuutteja ~ op_nimi, data = op_data)
n_tot <- nrow(op_data)
epsilon2 <- unname(kw_testi$statistic) / ((n_tot^2 - 1) / (n_tot + 1))
```
Kriittisyyden ja pistokemäärän järjestyskorrelaatio on
`r format(round(unname(kor_koko$estimate), 3), decimal.mark = ",")`, ja
kriittisyyden ja tehon
`r format(round(unname(kor_teho$estimate), 3), decimal.mark = ",")`.
**Miksi järjestyskorrelaatio.** Kriittisyysjakauma on erittäin vino: valtaosa
asemista on nollan tuntumassa ja muutama kaukana ylhäällä. Tavallinen
korrelaatiokerroin olettaa suunnilleen symmetrisen jakauman ja reagoi
voimakkaasti yksittäisiin ääriarvoihin, joita tässä on paljon.
Järjestyskorrelaatio käyttää vain sijalukuja eikä oleta jakauman muodosta
mitään.
```{r}
#| label: kuva-koko
#| fig-cap: "Aseman koko ja sen kriittisyys."
poistovaikutus |>
filter(asukkaita_koskee > 0) |>
ggplot(aes(pistokkeita, kasvu_koskeville)) +
geom_point(aes(size = asukkaita_koskee), colour = pal["turkoosi"],
alpha = 0.5) +
geom_text_repel(
data = \(d) d |> slice_max(kasvu_koskeville, n = 8),
aes(label = kunta), colour = "grey80", size = 3,
max.overlaps = 12, seed = 42
) +
scale_size_continuous(range = c(1.2, 7), guide = "none") +
labs(x = "Latauspistokkeita asemalla",
y = "Lisämatka asianosaisille, jos asema katoaa (min)",
title = "Iso asema ei ole sama kuin tärkeä asema",
subtitle = "Kriittisimmät asemat ovat usein pieniä")
```
**Miten kuvaa luetaan.** Vaaka-akselilla on aseman koko pistokemäärällä
mitattuna, pystyakselilla sen kriittisyys verkoston kannalta. Jos nämä
kulkisivat käsi kädessä, pisteet asettuisivat nousevalle suoralle.
Ne eivät asetu. Kriittisimmät asemat löytyvät kuvan vasemmasta laidasta eli
pienten asemien joukosta. Syy on maantieteellinen: pieni asema on pieni koska
sen ympärillä ei ole paljon ihmisiä, ja juuri siksi sen ympärillä ei ole
myöskään muita asemia.
Tällä on suora seuraus julkiselle tuelle. Jos tukea kohdennetaan aseman koon,
käyttöasteen tai investoinnin suuruuden perusteella, se ohjautuu asemille,
joiden katoamista kukaan ei huomaisi. Verkoston kestävyyden kannalta olennaiset
asemat ovat pieniä, syrjäisiä ja liiketaloudellisesti heikoimpia.
::: {.callout-note}
## Faktalaatikko: miksi verkot kestävät satunnaisvahinkoa mutta eivät kohdennettua
Verkostoteoriassa on tunnettu tulos, joka koskee lähes kaikkia
todellisia verkkoja: sähköverkkoja, internetiä, lentoliikennettä,
ekosysteemejä.
Näissä verkoissa solmujen tärkeys on erittäin epätasaisesti jakautunut.
Valtaosa solmuista on merkityksettömiä ja muutama pitää verkkoa kasassa. Sama
rakenne näkyy tämän osan ensimmäisessä kuvassa.
Rakenteesta seuraa kaksi vastakkaista ominaisuutta. Verkko kestää
poikkeuksellisen hyvin satunnaisia vaurioita: jos solmuja katoaa umpimähkään,
osumat kohdistuvat lähes aina merkityksettömiin solmuihin eikä mitään tapahdu.
Mutta verkko on poikkeuksellisen haavoittuvainen kohdennetulle vahingolle: jos
solmuja poistetaan tärkeysjärjestyksessä, verkko hajoaa nopeasti.
Ilmiö tunnetaan nimellä *robust yet fragile*, ja se on yksi
verkostotutkimuksen keskeisimmistä havainnoista.
**Mitä tästä seuraa latausverkolle.** Yksittäisen aseman satunnainen
rikkoutuminen ei ole ongelma, ja siksi arki näyttää sujuvan hyvin. Se ei kerro
mitään siitä, miten verkko kestäisi häiriön, joka ei ole satunnainen — kuten
yhden operaattorin taustajärjestelmän kaatumista, sähkökatkoa laajalla alueella
tai poikkeuksellista kysyntähuippua samaan aikaan samoilla teillä.
Tätä eroa testaan seuraavassa luvussa numeerisesti.
:::
## Vaihtoehtoiset skenaariot: satunnainen vastaan kohdennettu
```{r}
#| label: counterfactual
#| echo: true
# Poistetaan asemia yksi kerrallaan kahdessa järjestyksessä ja
# seurataan, miten saavutettavuus heikkenee.
poista_ketju <- function(jarjestys, askeleet = 40) {
jaljella <- seq_len(ncol(ajat_k))
tulos <- vector("list", askeleet)
for (s in seq_len(askeleet)) {
poistettava <- jarjestys[s]
jaljella <- setdiff(jaljella, poistettava)
m <- apply(ajat_k[, jaljella, drop = FALSE], 1, \(r) min(r, na.rm = TRUE))
kelpo_r <- is.finite(m)
tulos[[s]] <- tibble(
poistettu = s,
keskiaika = weighted.mean(m[kelpo_r], vakiluku[kelpo_r]),
ilman_yhteytta = sum(vakiluku[!kelpo_r]) / sum(vakiluku)
)
}
bind_rows(tulos)
}
# Kohdennettu: kriittisimmät ensin
jarjestys_kohdennettu <- order(poistovaikutus$henkilominuutteja,
decreasing = TRUE, na.last = TRUE)
# Satunnainen: keskiarvo useasta vedosta, jotta yksittäinen
# onnekas tai epäonnekas järjestys ei ohjaa tulosta
satunnaiset <- map_dfr(1:20, function(k) {
poista_ketju(sample(ncol(ajat_k))) |> mutate(veto = k)
}) |>
group_by(poistettu) |>
summarise(keskiaika = mean(keskiaika),
ilman_yhteytta = mean(ilman_yhteytta),
.groups = "drop") |>
mutate(tapa = "Satunnainen häiriö")
kohdennettu <- poista_ketju(jarjestys_kohdennettu) |>
mutate(tapa = "Kohdennettu häiriö")
vertailu <- bind_rows(satunnaiset, kohdennettu)
stopifnot(
"Ketjulaskenta epäonnistui" = nrow(vertailu) > 0,
"Keskiaika laskee poistojen myötä" =
all(diff(kohdennettu$keskiaika) >= -1e-6)
)
ero_40 <- vertailu |>
filter(poistettu == max(poistettu)) |>
select(tapa, keskiaika)
# Onko kokonaisuus enemmän kuin osiensa summa? Verrataan yksittäispoistojen
# summaa siihen, mitä samojen asemien yhtäaikainen poisto tekee.
n_askelta <- max(vertailu$poistettu)
summa_erikseen <- poistovaikutus |>
slice_max(henkilominuutteja, n = n_askelta) |>
summarise(s = sum(keskikasvu_min, na.rm = TRUE)) |>
pull(s)
yhdessa <- kohdennettu |>
filter(poistettu == n_askelta) |>
pull(keskiaika) - nyky_keskiaika
superadditiivisuus <- yhdessa / summa_erikseen
stopifnot(
"Yhtäaikainen poisto on pienempi kuin erillisten summa – tarkista järjestys" =
superadditiivisuus >= 0.95
)
```
```{r}
#| label: kuva-kestavyys
#| fig-cap: "Saavutettavuuden heikkeneminen, kun asemia poistetaan kahdessa eri järjestyksessä."
vertailu |>
ggplot(aes(poistettu, keskiaika, colour = tapa)) +
geom_hline(yintercept = nyky_keskiaika, colour = "grey55", linetype = 2) +
geom_line(linewidth = 1) +
scale_colour_manual(values = c("Satunnainen häiriö" = unname(pal["turkoosi"]),
"Kohdennettu häiriö" = unname(pal["punainen"]))) +
labs(x = "Poistettuja asemia", y = "Keskimääräinen ajoaika laturille (min)",
colour = NULL,
title = "Sama määrä asemia pois, hyvin eri lopputulos",
subtitle = "Katkoviiva: nykytila")
```
**Miten kuvaa luetaan.** Vaaka-akselilla on poistettujen asemien määrä,
pystyakselilla suomalaisten keskimääräinen matka lähimmälle
suurtehulaturille.
Turkoosi käyrä kuvaa satunnaista häiriötä: asemia katoaa umpimähkään.
Punainen kuvaa kohdennettua: kriittisimmät poistuvat ensin.
Tässä kuvassa pystyakseli on koko maan keskiarvo, vaikka edellä juuri totesin
sen huonoksi mittariksi. Ero on siinä, mihin lukua käytetään. Yksittäisen
aseman arvon kuvaamiseen se ei kelpaa, koska laimennus peittää vaikutuksen.
Kahden skenaarion vertailuun se kelpaa hyvin, koska molemmat lasketaan samalla
nimittäjällä ja kiinnostavaa on käyrien välinen ero, ei niiden korkeus.
### Kokonaisuus on enemmän kuin osiensa summa
Kuvassa on yksityiskohta, joka on helppo ohittaa mutta joka on tämän osan
kiinnostavin yksittäinen tulos.
Jos lasken yhteen `r n_askelta` kriittisimmän aseman yksittäiset vaikutukset —
siis sen, mitä kukin niistä tekee erikseen poistettuna — saan selvästi
pienemmän luvun kuin se, mitä niiden yhtäaikainen poistuminen tekee. Suhde on
noin `r format(round(superadditiivisuus, 2), decimal.mark = ",")`-kertainen.
Syy on ymmärrettävä kun sen sanoo ääneen. Kun ensimmäinen asema poistetaan, sen
käyttäjät siirtyvät naapuriasemalle. Kun myös naapuri poistetaan, he siirtyvät
jo kauemmas — ja tuon toisen aseman vaikutus on silloin suurempi kuin mitä se
oli yksin mitattuna, koska pakotie oli jo tukittu.
Ilmiötä kutsutaan superadditiivisuudeksi, ja sillä on suora käytännön seuraus.
**Riskilista, joka on laadittu poistamalla yksi kohde kerrallaan, aliarvioi
systemaattisesti sen, mitä useampi samanaikainen häiriö tekisi.** Juuri niin
riskilistat kuitenkin lähes aina laaditaan — myös tämän osan riskikartta on
sellainen.
Kerron tämän siksi, että tiedän oman menetelmäni rajan. Kartta kuvaa
oikein sitä, mikä yksittäinen asema on korvaamattomin. Se ei kuvaa oikein sitä,
mitä tapahtuu päivänä jona useampi asia menee rikki yhtä aikaa — ja se on
poikkeustilanteen määritelmä.
Ero on suuri, ja se on tämän osan tärkein tulos. **Verkko kestää satunnaista
vahinkoa hyvin ja kohdennettua huonosti.** Se ei ole latausverkon erityispiirre
vaan yleinen ominaisuus, mutta sen suuruus on tässä mitattu.
Käytännön seuraus on kaksijakoinen. Hyvä uutinen: yksittäiset viat eivät ole
ongelma, ja arki sujuu. Huono uutinen: mittarit, jotka seuraavat arkea, eivät
kerro mitään verkon kestävyydestä poikkeustilanteessa.
## Kuinka monta asemaa erottaa Lapin?
Kestävyyttä voi kysyä myös toisin. Sen sijaan että poistettaisiin asemia
tärkeysjärjestyksessä, voidaan kysyä: kuinka monta asemaa pitäisi vähintään
poistaa, jotta jokin alue irtoaisi muusta verkosta?
Tämä on graafiteorian minimileikkaus. Se etsii pienimmän joukon solmuja, joiden
poistaminen katkaisee yhteyden kahden pisteen välillä.
```{r}
#| label: minimileikkaus
#| echo: true
# Asemien välinen matkamatriisi D on luettu jo edellä matkan
# kriittisyyttä varten; käytetään samaa.
hpc_v <- hpc |> filter(avain %in% yhteiset) |>
arrange(match(avain, yhteiset))
stopifnot("Matriisin ja asemataulun järjestys ei täsmää" =
all(rownames(D) == hpc_v$avain))
# Kaari kahden aseman välille, jos ne ovat toistensa naapureita eikä
# välissä ole kolmatta asemaa.
rng_matriisi <- function(Dm, raja) {
n <- nrow(Dm)
A <- (Dm <= raja) & is.finite(Dm)
diag(A) <- FALSE
for (k in seq_len(n)) {
M <- pmax(matrix(Dm[, k], n, n), matrix(Dm[k, ], n, n, byrow = TRUE))
A[is.finite(M) & M < Dm] <- FALSE
}
diag(A) <- FALSE
A
}
g <- graph_from_adjacency_matrix(rng_matriisi(D, 300), mode = "undirected")
V(g)$avain <- hpc_v$avain
V(g)$lat <- hpc_v$lat
V(g)$kunta <- hpc_v$kunta
# Eteläisin ja pohjoisin asema: näiden välisen yhteyden katkaiseminen
# irrottaisi pohjoisen etelästä.
etela <- which.min(V(g)$lat)
pohjoinen <- which.max(V(g)$lat)
leikkaus <- min_cut(g, source = etela, target = pohjoinen,
value.only = FALSE)
leikkaus_koko <- leikkaus$value
stopifnot(
"Minimileikkaus epäonnistui" = is.finite(leikkaus_koko),
"Leikkaus on epäuskottavan suuri" = leikkaus_koko < 20
)
```
Etelän ja pohjoisen välinen yhteys katkeaisi, jos **`r leikkaus_koko`**
yhteyttä poistuisi verkosta.
Luku on pienempi kuin useimmat arvaisivat. Se ei tarkoita, että pohjoiseen ei
pääsisi ajamaan — se tarkoittaa, ettei sinne pääsisi suurtehulatureiden
ketjua pitkin ilman että jokin väli ylittää kolmesataa kilometriä.
Tulos on rakenteellinen ja seuraa maantieteestä: Suomi on kapea maa, ja
pohjoiseen johtaa käytännössä muutama päätie. Latausverkko noudattaa teitä,
joten sen rakenne perii tieverkon rakenteen.
::: {.callout-important}
## Mitä tämä analyysi ei ota huomioon
**Asemat eivät katoa vaan menevät epäkuntoon.** Poistosimulaatio olettaa aseman
katoavan kokonaan. Todellisuudessa yleisin häiriö on osittainen: yksi laturi
kolmesta ei toimi, jono on pitkä, tai maksupääte kaatuu. Näiden vaikutus
riippuu käyttöasteesta, jota ei ole julkisesti saatavilla.
**Hitaammat laturit puuttuvat.** Tarkastelu koskee vain suurtehulatureita.
Häiriötilanteessa autoilija voi käyttää hitaampaa laturia, jos aikaa on. Kartta
kuvaa siis pikalatausverkon kestävyyttä, ei liikkumisen mahdottomuutta.
**Ajoajat ovat systemaattisesti alakanttiin, kaupungeissa pahiten.** Ajoaika
lasketaan nopeusrajoituksista, eikä laskenta tunne liikennevaloja,
risteysviiveitä eikä ruuhkia. Maantiellä virhe on pieni, mutta kaupungissa se
on suuri — siellä matka-ajasta merkittävä osa kuluu juuri niihin asioihin,
joita malli ei tunne.
Tämä näkyy suoraan siinä, että nykytilan keskimääräinen matka lähimmälle
suurtehulaturille on `r round(nyky_keskiaika, 1)` minuuttia. Luku on
epäuskottavan pieni jokaiselle, joka on joskus ajanut kaupungissa. Se on
oikein laskettu mutta kuvaa esteetöntä ajoa, ei todellista.
Vertailuihin tämä ei vaikuta, koska virhe on samansuuntainen kaikkialla.
Yksittäistä lukua ei kuitenkaan pidä lukea todellisena matka-aikana.
**Rekisteri on epätäydellinen.** Sarjan ensimmäisessä osassa arvioitiin, että
osa asemista puuttuu rekisteristä kokonaan. Jos puuttuvat asemat ovat siellä
missä muutkin, riskiarvio on hieman yläkanttiin. Jos ne ovat syrjässä, arvio on
liian matala. En tiedä kumpi pätee.
**Kriittisyys on nykyhetken ominaisuus.** Uusi asema kriittisen aseman viereen
poistaa kriittisyyden kokonaan. Kartta kuvaa siis tilannetta, ei kohtaloa —
ja juuri siksi se on hyödyllinen.
:::
## Mitä jää käteen
**Useimpien asemien katoamista ei huomaisi kukaan.** Se ei tee niistä
tarpeettomia; se tarkoittaa että niillä on naapuri.
**Nimittäjä ratkaisee, näyttääkö mikään miltään.** Koko maan keskiarvona
kriittisimmänkin aseman katoaminen on muutaman sekunnin asia. Asianosaisille
se on kymmeniä minuutteja. Sama laskenta, eri nimittäjä, tuhatkertainen ero.
**Latausasemalla on kaksi eri tehtävää, ja ne eivät ole samoissa paikoissa.**
Kaupunkiasema on korvaamaton siksi, että sen käyttäjillä ei ole
latausmahdollisuutta kotona. Syrjäinen asema on korvaamaton siksi, että sen
käyttäjillä ei ole vaihtoehtoa matkan varrella. Mittari, joka tunnistaa vain
toisen, kohdentaa tuen väärin.
**Riskilistat aliarvioivat yhtäaikaiset häiriöt.** Yksi kerrallaan poistaen
mitattu kriittisyys ei summaudu: yhdessä poistetut asemat tekevät enemmän
vahinkoa kuin erikseen mitattuna.
**Kriittisyys ei seuraa kokoa.** Kriittisimmät asemat ovat pieniä ja syrjäisiä
— eli juuri niitä, joita markkinaehtoinen rakentaminen tuottaa vähiten ja
joiden kannattavuus on heikoin.
**Verkko kestää satunnaista vahinkoa ja hajoaa kohdennetusta.** Arjen mittarit
kertovat ensimmäisestä eivätkä mitään toisesta.
**Pohjoisen yhteys nojaa harvoihin väleihin.** Se on seurausta maantieteestä,
ei suunnitteluvirheestä — mutta se on tiedettävä.
Seuraavassa osassa siirryn rakenteesta ajankohtaan. Verkko, joka riittää
keskiviikkona marraskuussa, ei välttämättä riitä juhannusaattona — ja
kesämökkien sijainti kertoo, missä se ruuhkautuu.
---
## Mitä tästä on hyötyä yrityksellesi
**Aja poistotesti omalle infrastruktuurillesi.** Tämän osan koko menetelmä on
yksi silmukka: poista yksi solmu, mittaa lopputulos, palauta, siirry seuraavaan.
Sitä voi soveltaa mihin tahansa, mikä on mallinnettavissa verkkona —
toimittajiin, järjestelmiin, avainhenkilöihin, toimipisteisiin. Vastaus
kysymykseen "mitä tapahtuu jos tämä katoaa" on laskettavissa, ei arvattavissa.
**Älä priorisoi kokoa, priorisoi korvaamattomuutta.** Suurin toimittaja ei ole
kriittisin. Kriittisin on se, jolle ei ole vaihtoehtoa. Nämä ovat lähes aina eri
toimijoita, ja ero näkyy vasta kun sen laskee.
**Muista, että arki ei ennusta poikkeustilaa.** Verkko, joka kestää
satunnaisvahinkoa erinomaisesti, voi romahtaa kohdennetusta. Jos
varautumissuunnitelma nojaa siihen, että viime vuonna ei sattunut mitään, se
mittaa satunnaista häiriötä eikä kerro mitään järjestelmällisestä.
Tämä on suoraan DORA:n ja NIS2:n logiikka: valvoja ei kysy, onko teillä
sattunut häiriöitä, vaan onko teillä tunnistettu mitkä toiminnot kaatuvat
ensin ja mitä siitä seuraa.
**Kysy, onko sama resurssi kahdessa eri käytössä.** Latausasema osoittautui
kahdeksi eri asiaksi: arjen peruspalveluksi niille joilla ei ole vaihtoehtoa
kotona, ja matkaketjun lenkiksi kaikille ohi ajaville. Yksi kriittisyysluku ei
voinut kuvata molempia.
Sama pätee yllättävän moneen asiaan organisaatiossa. Järjestelmä, jota
käytetään sekä päivittäiseen rutiiniin että kerran kuussa tapahtuvaan
tilinpäätökseen, on kriittinen kahdesta eri syystä ja eri aikoina.
Avainhenkilö, joka hoitaa arjen työt ja on lisäksi ainoa joka osaa yhden
harvinaisen prosessin, on korvattavissa ensimmäisessä roolissa ja korvaamaton
toisessa. Jos kriittisyys lasketaan yhtenä lukuna, toinen rooli katoaa
näkyvistä — ja yleensä se joka katoaa on se harvinainen, koska se on pienempi
volyymiltaan.
**Tarkista riskiarviosi nimittäjä.** Jos riskin suuruus lasketaan jakamalla
vaikutus koko organisaatiolle, koko asiakaskunnalle tai koko liikevaihdolle,
jokainen yksittäinen riski näyttää pieneltä. Se ei ole laskuvirhe vaan
näkökulmavirhe, ja se johtaa järjestelmällisesti siihen ettei mitään
priorisoida. Kysy aina erikseen: keitä tämä koskee, kuinka paljon se koskee
juuri heitä, ja kuinka moni ylittää sietokynnyksen.
**Älä oleta riskien summautuvan.** Yksi kerrallaan arvioidut riskit ovat
yhdessä pahempia kuin erikseen, koska varajärjestelyt ovat usein samoja.
Jos palvelin A kaatuu, kuorma siirtyy palvelimelle B; jos molemmat kaatuvat,
vaikutus on enemmän kuin kaksinkertainen. Sama koskee avainhenkilöitä,
toimittajia ja toimipisteitä. Riskirekisteri, jossa jokainen rivi on arvioitu
yksinään, kuvaa normaalipäivää eikä sitä päivää jota varten se on tehty.
**Erota "ei ole tapahtunut" ja "ei voi tapahtua".** Riskikartta on
todennäköisyyksistä riippumaton: se kertoo mitä seuraisi, ei kuinka
todennäköistä se on. Molempia tarvitaan, mutta ne on laskettava erikseen.
Seurausten laskeminen on usein helpompaa kuin todennäköisyyden arviointi — ja
silti se jätetään useammin tekemättä.
**Ja tee kartta, älä listaa.** Tämän osan tulos olisi voitu esittää
taulukkona viidestätoista tärkeimmästä asemasta. Kartta kertoo saman ja
lisäksi sen, missä ne ovat ja miksi. Kun analyysin pitää muuttaa jonkun
mieltä, esitystapa ratkaisee yhtä paljon kuin sisältö.
Jos organisaatiossasi ei ole laskettu, mikä hajoaa ensin ja mitä siitä seuraa,
tai jos kriittisyys on määritelty kokouksessa eikä mitattu, siinä on työtä
odottamassa. Teen tätä vuokrattavana Head of Datana ja projektikohtaisesti —
hinnasto ja yhteystiedot löytyvät
[sivustolta](https://kristianvepsalainen.com).
---
*Aineistot: OpenStreetMap (© OpenStreetMap-osallistujat, ODbL 1.0), Fintraffic
AFIR / Digitraffic, Tilastokeskuksen Paavo-aineisto, Eurostat GISCO. Koodi on
kokonaisuudessaan tässä postauksessa. Maailma on jakauma.*