💻 Reklassifisering av data#

Open In Colab

Reklassifisering av data basert på spesifikke kriterier er en vanlig oppgave når man utfører GIS analyse. Formålet med denne notebooken er å se hvordan vi kan reklassifisere verdier basert på noen kriterier. Man kunne for eksempel klassifisere informasjon basert på reisetider og boligpriser ved hjelp av disse kriteriene:

  1. Hvis reisetiden til jobben min er mindre enn 30 minutter, OG

  2. husleien for leiligheten er mindre enn 10000 kr per måned

Hvis begge kriteriene er oppfylt: Jeg går for å se leiligheten og prøver å leie den Hvis ikke: Jeg fortsetter å lete etter noe annet

I denne opplæringen vil vi:

  1. Bruk klassifiseringsskjemaer fra PySAL mapclassify bibliotek for å klassifisere befolkningstall til flere klasser.

  2. Opprett en egendefinert klassifisering for å klassifisere og benytte den

Inputdata#

Vi vil bruke befolkningsdata fra SSB for Oslo, som inneholder 2380 celler for Oslo kommune.

import pathlib 
NOTEBOOK_PATH = pathlib.Path().resolve()
DATA_MAPPE = NOTEBOOK_PATH / "data"
import geopandas

rutenett = geopandas.read_file(DATA_MAPPE / "ssb_rutenett" / "befolkning_250m_2023_oslo.shp")

rutenett.head()
fid ru250m pop_tot geometry
0 1.0 2.263751e+13 14 POLYGON ((264000 6643000, 263750 6643000, 2637...
1 2.0 2.264001e+13 177 POLYGON ((264250 6643000, 264000 6643000, 2640...
2 3.0 2.264251e+13 169 POLYGON ((264500 6643000, 264250 6643000, 2642...
3 4.0 2.264501e+13 261 POLYGON ((264750 6643000, 264500 6643000, 2645...
4 5.0 2.264751e+13 106 POLYGON ((265000 6643000, 264750 6643000, 2647...

Vanlige klassifiseringer#

Klassifiseringsskjemaer for tematiske kart#

PySAL -modulen er et omfattende Python-bibliotek for romlig analyse. Det inkluderer også alle de vanligste dataklassifiseringene som er brukt vanlig f.eks. når man visualiserer data. Tilgjengelige kartklassifiseringer i pysal’s mapclassify -modul:

  • Box Plot

  • Equal Interval

  • Fisher Jenks

  • Fisher Jenks Sampled

  • HeadTail Breaks

  • Jenks Caspall

  • Jenks Caspall Forced

  • Jenks Caspall Sampled

  • Max P Classifier

  • Maximum Breaks

  • Natural Breaks

  • Quantiles

  • Percentiles

  • Std Mean

  • User Defined

NoData-verdiene presenteres med verdi -1. Derfor må vi først fjerne No Data-verdiene.

# Inkluder bare data som er over eller lik 0
rutenett = rutenett.loc[rutenett["pop_tot"] >=0]

La oss plotte dataene og se hvordan det ser ut

  • cmap parameter definerer fargekartet. Les mer om valg av fargekart i matplotlib

  • scheme alternativ skalerer fargene i henhold til et klassifiseringsskjema (krever at mapclassify modulen er installert):

# Plott ved hjelp av 9 klasser og klassifiser verdiene ved hjelp av "Natural Breaks" klassifisering
rutenett.plot(column="pop_tot", scheme="Natural_Breaks", k=9, cmap="RdYlBu", linewidth=0, legend=True, aspect=1)
<Axes: >
../../_images/01b393bf09e50e37e53e8945e49fa1f3e81a69401df700c8a639083d2f58747b.png

Som vi kan se fra dette kartet, er befolkningen større i sentrum, men det er også noen områder med få beboere i noen andre områder (hvor fargen er rød).

Bruk av klassifiseringer på data#

Som nevnt, definerer scheme alternativet klassifiseringsskjemaet ved hjelp av pysal/mapclassify. La oss se nærmere på hvordan disse klassifiseringene fungerer.

import mapclassify

Natural Breaks#

mapclassify.NaturalBreaks(y=rutenett["pop_tot"], k=9)
NaturalBreaks

     Interval        Count
--------------------------
[   1.00,   91.00] |   701
(  91.00,  216.00] |   521
( 216.00,  344.00] |   511
( 344.00,  515.00] |   264
( 515.00,  727.00] |   163
( 727.00,  962.00] |    79
( 962.00, 1243.00] |    61
(1243.00, 1618.00] |    55
(1618.00, 2351.00] |    25

Quantiles (standard er 5 klasser):#

mapclassify.Quantiles(y=rutenett["pop_tot"])
Quantiles

     Interval        Count
--------------------------
[   1.00,   31.00] |   480
(  31.00,  160.00] |   476
( 160.00,  262.00] |   476
( 262.00,  433.20] |   472
( 433.20, 2351.00] |   476

Trekk ut terskelverdiene#

Det er mulig å trekke ut terskelverdiene i en matrise:

klassifiserer = mapclassify.NaturalBreaks(y=rutenett["pop_tot"], k=9)
klassifiserer.bins
array([  83.,  198.,  315.,  462.,  658.,  896., 1212., 1618., 2351.])

La oss bruke en av Pysal klassifiseringene på dataene våre og klassifisere befolkningen til 9 klasser Klassifiseringen må først initialiseres med make() funksjonen som tar antall ønskede klasser som inngangsparameter

# Lag en Natural Breaks klassifisering
klassifiserer = mapclassify.NaturalBreaks.make(k=9)
  • Nå kan vi bruke klassifiseringen på dataene våre ved å bruke apply -funksjonen

# Klassifiser dataene
klassifiseringer = rutenett[["pop_tot"]].apply(klassifiserer)

# La oss se hva vi har
klassifiseringer.head()
pop_tot
0 0
1 1
2 1
3 2
4 1
type(klassifiseringer)
pandas.core.frame.DataFrame

Ok, så nå har vi en DataFrame der inngangskolonnen vår ble klassifisert til 9 forskjellige klasser (tallene 1-9) basert på Natural Breaks klassifisering.

Vi kan også legge til klassifiseringsverdiene direkte i en ny kolonne i vår dataframe:

# Gi nytt navn til kolonnen så vi vet at den ble klassifisert med naturlige brudd
rutenett["nb_pop_tot"] = rutenett[["pop_tot"]].apply(klassifiserer)

# Sjekk de opprinnelige verdiene og klassifiseringen
rutenett[["pop_tot", "nb_pop_tot"]].head()
pop_tot nb_pop_tot
0 14 0
1 177 1
2 169 1
3 261 2
4 106 1

Flott, nå har vi disse verdiene i GeoDataFramen. La oss visualisere resultatene og se hvordan de ser ut.

# Plott
rutenett.plot(column="nb_pop_tot", linewidth=0, legend=True)
<Axes: >
../../_images/d1bfc826873c34129ab16f2bca170e45d734766088c3715bb36f472957b46cf0.png

Og her går vi, nå har vi et kart der vi har brukt en av de vanlige klassifiseringene for å klassifisere dataene våre i 9 klasser.

Plott et histogram#

Et histogram er en grafisk representasjon av datafordelingen. Når man klassifiserer data, er det alltid bra å vurdere hvordan dataene er fordelt, og hvordan klassifiseringsskjemaet deler verdier i forskjellige områder.

  • plott histogrammet ved hjelp av pandas.DataFrame.plot.hist

  • Antall histogrambinner (grupper av data) kan kontrolleres ved hjelp av parameteren bins:

# Histogram for befolkningsrutenettet
rutenett["pop_tot"].plot.hist(bins=50)
<Axes: ylabel='Frequency'>
../../_images/f47e10a52858d4b17dd7dbe632701d9cbf0fcfd5c8b7f1004b2f72aeb3abdb26.png

La oss også legge til terskelverdier på toppen av histogrammet som vertikale linjer.

  • Natural Breaks:

import matplotlib.pyplot as plt

# Definer klassifiserer
klassifiserer = mapclassify.NaturalBreaks(y=rutenett["pop_tot"], k=9)

# Plott histogram for befolkningsrutenettet
rutenett["pop_tot"].plot.hist(bins=50)

# Legg til vertikale linjer for klassebrudd
for break_point in klassifiserer.bins:
    plt.axvline(break_point, color="k", linestyle="dashed", linewidth=1)
../../_images/82964ebdf68af03d76cb0a9aa02072b0f68b1582e97383a1c14d55fbbc724f0b.png
  • Kvartiler:

# Definer klassifiserer
klassifiserer = mapclassify.Quantiles(y=rutenett['pop_tot'])

# Plott histogram for befolkningsrutenettet
rutenett["pop_tot"].plot.hist(bins=50)

for break_point in klassifiserer.bins:
    plt.axvline(break_point, color="k", linestyle="dashed", linewidth=1)
../../_images/64ae6cfc63a19c9792d9b7d7d625c851ae091089ae4b13e86abce2952a7fa212.png

Bruke en egendefinert klassifisering#

Multikriterium dataklassifisering#

La oss klassifisere geometriene i to klasser basert på en gitt terskel -parameter. Hvis området til en polygon er lavere enn terskelverdien, vil utgangskolonnen få en verdi 0, hvis den er større, vil den få en verdi 1. Denne typen klassifisering kalles ofte en binær klassifisering.

For å klassifisere hver rad i vår GeoDataFrame kan vi iterere over hver rad, eller vi kan bruke en funksjon for hver rad. I vårt tilfelle vil vi bruke en lambda funksjon for hver rad i vår GeoDataFrame, som returnerer en verdi basert på de vilkårene vi gir.

La oss klassifisere basert på to kriterier: og finne ut rutenettceller

  1. Rutenettceller der befolkningen er mindre eller lik 50

  2. og Rutenettceller der befolkningen er større eller lik 15

La oss først se hvordan vi klassifiserer en enkelt rad:

rutenett.iloc[0]["pop_tot"] <= 50 and rutenett.iloc[0]["pop_tot"] > 15
False
int(rutenett.iloc[2000]["pop_tot"] < 50 and rutenett.iloc[2000]["pop_tot"] > 15)
0

La oss nå bruke dette på vår GeoDataFrame og lagre det i en kolonne kalt "egnet":

rutenett["egnet"] = rutenett.apply(lambda row: int(row["pop_tot"] < 50 and row["pop_tot"] > 15), axis=1)
rutenett.head()
fid ru250m pop_tot geometry nb_pop_tot egnet
0 1.0 2.263751e+13 14 POLYGON ((264000 6643000, 263750 6643000, 2637... 0 0
1 2.0 2.264001e+13 177 POLYGON ((264250 6643000, 264000 6643000, 2640... 1 0
2 3.0 2.264251e+13 169 POLYGON ((264500 6643000, 264250 6643000, 2642... 1 0
3 4.0 2.264501e+13 261 POLYGON ((264750 6643000, 264500 6643000, 2645... 2 0
4 5.0 2.264751e+13 106 POLYGON ((265000 6643000, 264750 6643000, 2647... 1 0

Ok, vi har nye verdier i egnet -kolonnen.

  • Hvor mange polygoner passer for oss? La oss finne ut ved hjelp av en Pandas funksjon kalt value_counts() som returnerer antall forskjellige verdier i vår kolonne.

# Få antall verdier
rutenett["egnet"].value_counts()
egnet
0    2216
1     164
Name: count, dtype: int64

Ok, så det ser ut til å være 164 passende steder.

  • La oss se hvor de er lokalisert:

# Plott
rutenett.plot(column="egnet", linewidth=0)
<Axes: >
../../_images/21996667abf24d6b831adafa42dc1bebfaf5b7f546999fba6a2e61bce94e4f35.png