Dette dokumentet viser hvordan vi har kommet oss fra den store filen med data (originale data fra SSV), og til en mindre og mer håndterbar fil som kun består av informasjonen vi er interesserte i for oppgave.
# importere pakker og funksjoner vi trenger
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Første steg er å hente inn hele den store filen:
# laste inn dataene
df_vei = pd.read_csv('https://www.math.ntnu.no/emner/IST100x/ISTx1003/h2022/segments_full.csv', sep = ";", decimal = ",")
# printe første og siste radene
df_vei
Unnamed: 0 | WKT | X | X.MID | VEGSYSTEMREFERANSE | year | Section | SPORDYBDE | SPORBREDDE | TVERRFALL | ... | VEGSYSTEMREFERANSE.3 | VEGSYSTEMREFERANSE.4 | ditch_depth | FRA.METER.3 | TIL.METER.3 | rut | rut.diff.lag1 | rut.diff | rut.diff.response | rut.depth.before | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | POINT (10.933685 63.46724) | 1047.9 | 1057.9 | EV14 S1D1 m1006.8-1026.7 | 2010 | 1 | 23.6 | 163.5 | 2.0 | ... | EV14 S1D1 m1408-1479 | EV14 S1D1 m1005-1214 | NaN | 1004.827 | 1214.322 | 23.6 | 0.0 | 0.0 | 0.0 | NaN |
1 | 2 | POINT (10.9336992 63.4672014) | 1047.9 | 1057.9 | EV14 S1D1 m1006.8-1026.7 | 2011 | 1 | 2.7 | 120.0 | 3.2 | ... | EV14 S1D1 m1408-1479 | EV14 S1D1 m1005-1214 | NaN | 1004.827 | 1214.322 | 2.7 | -20.9 | -20.9 | NaN | 23.6 |
2 | 3 | POINT (10.9335982 63.4671892) | 1047.9 | 1057.9 | EV14 S1D1 m1006.8-1026.7 | 2012 | 1 | 3.5 | 141.1 | 2.5 | ... | EV14 S1D1 m1408-1479 | EV14 S1D1 m1005-1214 | NaN | 1004.827 | 1214.322 | 3.5 | 0.8 | -20.1 | 0.8 | 2.7 |
3 | 4 | POINT (10.9337204 63.4672228) | 1047.9 | 1057.9 | EV14 S1D1 m1006.8-1026.7 | 2013 | 1 | 6.1 | 150.7 | 3.6 | ... | EV14 S1D1 m1408-1479 | EV14 S1D1 m1005-1214 | NaN | 1004.827 | 1214.322 | 6.1 | 2.6 | -17.5 | 2.6 | 3.5 |
4 | 5 | POINT (10.9336885 63.4672195) | 1047.9 | 1057.9 | EV14 S1D1 m1006.8-1026.7 | 2014 | 1 | NaN | NaN | NaN | ... | EV14 S1D1 m1408-1479 | EV14 S1D1 m1005-1214 | NaN | 1004.827 | 1214.322 | NaN | NaN | NaN | NaN | 6.1 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
36900 | 36901 | POINT (11.9743193 63.3607558) | 61506.5 | 61516.5 | EV14 S7D1 m9994-10014 | 2016 | 5 | 8.9 | 175.5 | -3.5 | ... | EV14 S6D1 m11768-11800 | EV14 S7D1 m9355-9995 | NaN | 9355.459 | 9994.633 | 8.9 | 1.0 | -12.4 | 1.0 | 7.9 |
36901 | 36902 | POINT (11.9744796 63.3607046) | 61506.5 | 61516.5 | EV14 S7D1 m9994-10014 | 2017 | 5 | 10.1 | 171.6 | -3.3 | ... | EV14 S6D1 m11768-11800 | EV14 S7D1 m9355-9995 | NaN | 9355.459 | 9994.633 | 10.1 | 1.2 | -11.2 | 1.2 | 8.9 |
36902 | 36903 | POINT (11.9742775 63.3607685) | 61506.5 | 61516.5 | EV14 S7D1 m9994-10014 | 2018 | 5 | 13.4 | 173.3 | -3.5 | ... | EV14 S6D1 m11768-11800 | EV14 S7D1 m9355-9995 | NaN | 9355.459 | 9994.633 | 13.4 | 3.3 | -7.9 | 3.3 | 10.1 |
36903 | 36904 | POINT (11.9743084 63.3607589) | 61506.5 | 61516.5 | EV14 S7D1 m9994-10014 | 2019 | 5 | 14.1 | 169.5 | -3.3 | ... | EV14 S6D1 m11768-11800 | EV14 S7D1 m9355-9995 | NaN | 9355.459 | 9994.633 | 14.1 | 0.7 | -7.2 | 0.7 | 13.4 |
36904 | 36905 | POINT (11.9744066 63.360729) | 61506.5 | 61516.5 | EV14 S7D1 m9994-10014 | 2020 | 5 | 15.8 | 174.8 | -3.5 | ... | EV14 S6D1 m11768-11800 | EV14 S7D1 m9355-9995 | NaN | 9355.459 | 9994.633 | 15.8 | 1.7 | -5.5 | 1.7 | 14.1 |
36905 rows × 46 columns
Vi skal nå hente det vi trenger for å lære mer om endringen i spordybde på E14. For hver $20$ meter lange del av veien vil vi derfor ha:
Vi har snakket med noen som kan mye om disse dataene, og har kommet frem til at kolonnene vi vil hente ut er: year
, SPORDYBDE
, AADT
, MASSETYPE
, width
og X
(X
skal ikke brukes i analysen, men er nyttig for å plotte dataene, og forteller oss hvor på veien hver måling er gjort).
# henter ut kolonnene vi ville se på:
df_vei2 = df_vei[['year', 'SPORDYBDE', 'AADT', 'MASSETYPE', 'width', 'X']]
# printe første og siste radene
df_vei2
year | SPORDYBDE | AADT | MASSETYPE | width | X | |
---|---|---|---|---|---|---|
0 | 2010 | 23.6 | 15000 | Skjelettasfalt | 3.3 | 1047.9 |
1 | 2011 | 2.7 | 15000 | Skjelettasfalt | 3.3 | 1047.9 |
2 | 2012 | 3.5 | 15000 | Skjelettasfalt | 3.3 | 1047.9 |
3 | 2013 | 6.1 | 15000 | Skjelettasfalt | 3.3 | 1047.9 |
4 | 2014 | NaN | 15000 | Skjelettasfalt | 3.3 | 1047.9 |
... | ... | ... | ... | ... | ... | ... |
36900 | 2016 | 8.9 | 1920 | Asfaltgrusbetong | 2.9 | 61506.5 |
36901 | 2017 | 10.1 | 1920 | Asfaltgrusbetong | 2.9 | 61506.5 |
36902 | 2018 | 13.4 | 1920 | Asfaltgrusbetong | 2.9 | 61506.5 |
36903 | 2019 | 14.1 | 1920 | Asfaltgrusbetong | 2.9 | 61506.5 |
36904 | 2020 | 15.8 | 1920 | Asfaltgrusbetong | 2.9 | 61506.5 |
36905 rows × 6 columns
Nå har vi et datasett som inneholder kun den informasjonen vi trenger. Neste steg er å hente ut endringen i spordybde fra 2019 til 2020, og den finner vi ganske enkelt ved å trekke spordybden i 2020 fra spordybden i 2019 for hver enkelt veistrekning. Samtidig vil vi ta vare på spordybden i 2019, trafikkintensiteten for hver veistrekning, asfalttypen for hver veistrekning, veibredden for hver veistrekning, og posisjonen til hver veistrekning (vi bruker avstand fra der målingene starter (Stjørdal) til dette). Vi gir også dataene nye navn. I tillegg lager vi en variabel som gir oss sporing per ti tusen biler.
# vi lager oss først variabler som inneholder hver enkelt-kolonne med informasjon vi trenger:
spordybde19 = df_vei2[df_vei['year'] == 2019]['SPORDYBDE'].to_numpy()
spordybde20 = df_vei2[df_vei['year'] == 2020]['SPORDYBDE'].to_numpy()
sporing = spordybde20 - spordybde19
AADT = df_vei2[df_vei['year'] == 2020]['AADT'].to_numpy()
posisjon = df_vei2[df_vei['year'] == 2020]['X'].to_numpy()
asfalt = df_vei2[df_vei['year'] == 2020]['MASSETYPE'].to_numpy()
veibredde = df_vei2[df_vei['year'] == 2020]['width'].to_numpy()
# deretter lager vi oss en variabel som inneholder sporing per ti tusen biler
sporing_trafikk = sporing/(AADT/10000)
# så forenkler vi asfalt-typene
asfalt2 = asfalt
asfalt2[asfalt2 == "Skjelettasfalt"] = "A1"
asfalt2[asfalt2 == "Asfaltbetong"] = "A2"
asfalt2[asfalt2 == "Asfaltgrusbetong"] = "A3"
# og så setter vi alt sammen til et nytt datasett
df_vei3 = pd.DataFrame({
'sporing_trafikk': sporing_trafikk,
'sporing': sporing,
'spordybde': spordybde19,
'asfalt': asfalt2,
'veibredde': veibredde,
'posisjon': posisjon
})
# merk: variablene trenger ikke ha samme navn som vi gir i lista (ref asfalt/asfalt2)
# vi sorterer så dataene etter avstand for ordens skyld
df_vei3 = df_vei3.sort_values(by = 'posisjon')
# og så gir vi nye radnummere
df_vei3.index = range(len(df_vei3))
# skriver ut første og siste radene i datasettet
df_vei3
sporing_trafikk | sporing | spordybde | asfalt | veibredde | posisjon | |
---|---|---|---|---|---|---|
0 | 2.204301 | 4.1 | 1.2 | A1 | 6.50 | 0.0 |
1 | 0.752688 | 1.4 | 7.4 | A1 | 6.50 | 19.7 |
2 | 0.860215 | 1.6 | 6.1 | A1 | 6.50 | 39.4 |
3 | 0.483871 | 0.9 | 4.2 | A1 | 6.50 | 59.2 |
4 | 0.967742 | 1.8 | 4.5 | A1 | 6.50 | 79.0 |
... | ... | ... | ... | ... | ... | ... |
3350 | 30.208333 | 5.8 | 9.8 | A3 | 3.05 | 66594.3 |
3351 | 21.875000 | 4.2 | 12.6 | A3 | 3.00 | 66614.0 |
3352 | 13.541667 | 2.6 | 15.5 | A3 | 3.00 | 66633.8 |
3353 | 6.770833 | 1.3 | 13.9 | A3 | 3.00 | 66653.8 |
3354 | 8.854167 | 1.7 | 12.3 | A3 | 3.00 | 66673.6 |
3355 rows × 6 columns
Det siste vi nå vil sjekke er om vi mangler noen av dataene.
# denne koden gir oss antall manglende datapunkter
df_vei3.isnull().any(axis=1).sum()
17
Vi mangler visst 17 datapunkter, så da vil vi sjekke hvilke.
# denne koden viser delene av dataene vi mangler
df_vei3.loc[df_vei3.isnull().any(axis = 1)]
sporing_trafikk | sporing | spordybde | asfalt | veibredde | posisjon | |
---|---|---|---|---|---|---|
29 | 1.466667 | 2.2 | 4.5 | A1 | NaN | 631.7 |
30 | 1.400000 | 2.1 | 4.1 | A1 | NaN | 651.4 |
31 | 1.466667 | 2.2 | 3.9 | A1 | NaN | 671.3 |
32 | 1.400000 | 2.1 | 4.0 | A1 | NaN | 691.1 |
33 | 1.400000 | 2.1 | 5.1 | A1 | NaN | 711.0 |
34 | 1.333333 | 2.0 | 4.8 | A1 | NaN | 730.7 |
35 | 1.733333 | 2.6 | 4.3 | A1 | NaN | 750.5 |
36 | 1.933333 | 2.9 | 3.6 | A1 | NaN | 770.3 |
37 | 1.600000 | 2.4 | 4.0 | A1 | NaN | 790.1 |
38 | 1.666667 | 2.5 | 2.8 | A1 | NaN | 810.0 |
39 | 0.933333 | 1.4 | 4.1 | A1 | NaN | 829.7 |
40 | 1.600000 | 2.4 | 3.5 | A1 | NaN | 849.6 |
41 | 1.666667 | 2.5 | 4.4 | A1 | NaN | 869.4 |
42 | 1.733333 | 2.6 | 4.1 | A1 | NaN | 889.1 |
43 | 1.600000 | 2.4 | 3.2 | A1 | NaN | 909.1 |
44 | 1.866667 | 2.8 | 3.1 | A1 | NaN | 928.8 |
45 | 1.600000 | 2.4 | 3.4 | A1 | NaN | 948.7 |
Vi mangler altså veibredden for 17 datapunkter (som betyr 17 20-meters veistrekninger). Dette kan vi enten ignorere, da vil modellene vi senere skal tilpasse ikke kunne bruke veibredde
for akkurat disse strekningene, og det er ganske vanlig å tilpasse modeller med manglende data. Vi har sjelden fullstendige datasett i den virkelige verden.
Men, vi har lyst på et fullstendig datasett, og vil derfor se om vi kan få tak i informasjon om veibredden der den mangler i datasettet.
Vi starter med å plotte veibredden som funksjon av avstand
. Først hele strekningen, og deretter kun områdene rundt der vi mangler data.
figure, axis = plt.subplots(1, 2, figsize = (12, 6))
axis[0].scatter(df_vei3['posisjon'], df_vei3['veibredde'])
axis[0].set_xlabel("Posisjon"); axis[0].set_ylabel("Veibredde")
axis[1].scatter(df_vei3[20:55]['posisjon'], df_vei3[20:55]['veibredde'])
axis[1].set_xlabel("Posisjon"); axis[1].set_ylabel("Veibredde")
Text(0, 0.5, 'Veibredde')
Vi ser at veisegmentene der vi mangler data er etter hverandre. Det får oss til å tenke at det ikke er helt tilfeldig at det er her dataene mangler. Vi ser også at veien blir smalere i det området vi mangler data.
Vi snakker igjen med noen som kan mye om dataene og situasjonen, som heldigvis har en løsning for oss i akkurat dette tilfellet (flaks!). Det viser seg at det er en feil med selve filen vi har som gjør at dataene mangler, og problemet er ikke at de ikke har målinger. Vi får beskjed om at de første 200 meterne av veistrekket med manglende data har bredde 5.4 meter (som er bredden før vi mangler data), og 3.3 meter de siste 140 meterne (som er bredden på veien etter strekket med manglende data).
Så da putter vi denne informasjonen inn i datasettet vårt:
# de første 200 meterne som mangler, altså 10 segmenter, skal få bredden 5.4 meter
df_vei3['veibredde'] = df_vei3['veibredde'].fillna(5.4, limit = 10) # fyller de første 10 manglende verdiene med 5.4
# av tabellen litt over ser vi at det er rad nummer 39 til og med 45
df_vei3['veibredde'] = df_vei3['veibredde'].fillna(3.3) # fyller de resterende manglende verdiene med 3.3
# sjekker igjen om vi mangler verdier i datasettet
df_vei3.isnull().any(axis = 1).sum()
0
Nå mangler vi ikke lenger noen verdier i datasettet vårt, og vi er fornøyde.
Merk at dette absolutt ikke alltid vil være tilfellet. Det er mye data vi kan mangle der ingen har intuisjon eller fasitsvar til oss om hvordan data vi mangler skal se ut, og vi må være veldig forsiktige med å erstatte manglende data.
Før vi sier oss helt ferdige med datasettet må vi lagre det, det gjør vi på følgende måte:
df_vei3.to_csv('sporing_data.csv', index = False)
Og da er vi endelig klare for å se gjøre analyse av dataene! Tilbake til oppgavesettet!