Helsingissä on toiminut kaupunkipyöräjärjestelmä vuodesta 2016 alkaen. Järjestelmä tarjoaa avoimen rajapinnan, josta voi hakea reaaliaikaisen vapaiden pyörien määrän kullakin telineellä. Olen kerännyt tätä dataa viiden minuutin välein kaudet 2017, 2018 ja 2019. Tältä sivulta voit ladata nuo datat vapaasti käyttöösi Nimeä 4.0 Kansainvälinen (CC BY 4.0)-lisenssin ehdoilla.

There has been a public bike sharing scheme in Helsinki since 2016. System offers a public API that provides real-time data on availability of bikes at each station. I have collected that data every five minutes since season 2017. You can download the files for any use from this site according to Attribution 4.0 International (CC BY 4.0)-license.

Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.

Tällä projektilla ei ole mitään yhteyksiä HKL;ään, HSL:ään tai CityGikeFinland:iin! This project is not connected with HKL, HSL or CityGikeFinland!

Ladattavat tiedostot / files to download

Data: Vapaat pyörät / available bikes

tiedosto / file koko / size rivejä / rows sarakkeita / columns
http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2017.csv.gz 32.8 Mt 7 795 206 16
http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2018.csv.gz 61.9 Mt 15 126 567 16
http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2019.csv.gz 124 Mt 25 966 001 16

Ensimmäiset rivit / First rows

  id    bikesAvailable spacesAvailable allowDropoff isFloatingBike state realTimeData time   week  yday
  <chr>          <dbl>           <dbl>        <dbl> <lgl>          <lgl>        <dbl> <chr> <dbl> <dbl>
1 070                4              10            1 NA             NA               1 2017…    19   128
2 071               16              12            1 NA             NA               1 2017…    19   128
3 072               14               5            1 NA             NA               1 2017…    19   128
4 073                8               6            1 NA             NA               1 2017…    19   128
5 074               15               5            1 NA             NA               1 2017…    19   128
6 075                3              17            1 NA             NA               1 2017…    19   128
# … with 6 more variables: day <dbl>, month <dbl>, hour <dbl>, minute <dbl>, yhour <dbl>, year <dbl>

Data: Tellingit / Stations

tiedosto / file koko / size rivejä / rows sarakkeita / columns
http://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2019.csv.gz 11Mt 171952 7

Ensimmäiset rivit / First rows

  id    name                        x     y time                 yday  year
  <chr> <chr>                   <dbl> <dbl> <chr>               <dbl> <dbl>
1 120   Mäkelänkatu              25.0  60.2 2017/05/09 12:00:03   129  2017
2 088   Kiskontie                24.9  60.2 2017/05/09 12:00:03   129  2017
3 121   Vilhonvuorenkatu         25.0  60.2 2017/05/09 12:00:03   129  2017
4 070   Sammonpuistikko          24.9  60.2 2017/05/09 12:00:03   129  2017
5 071   Hietaniemenkatu          24.9  60.2 2017/05/09 12:00:03   129  2017
6 072   Eteläinen Hesperiankatu  24.9  60.2 2017/05/09 12:00:03   129  2017

Metadatat / metadatas

Vapaat pyörät / available bikes

   varname         description                                                        
   <chr>           <chr>                                                              
 1 id              Station id (character)                                             
 2 bikesAvailable  Number of available bikes (integer)                                
 3 spacesAvailable Number of available spaces (integer)                               
 4 allowDropoff    Does the station allow dropoff of a bike: 1 = yes, 2 = no (integer)
 5 isFloatingBike  isFloatingBike (all values NA) (logical)                           
 6 state           state (all values NA) (logical)                                    
 7 realTimeData    realTimeData (integer)                                             
 8 time            Timestamp. Format 'yyyy/mm/dd hh:mm:ss' (character)                
 9 week            Week of the year (integer)                                         
10 yday            Day of the year (integer)                                          
11 day             Day of the month (integer)                                         
12 month           Month of the year (integer)                                        
13 hour            Hour of the day (integer)                                          
14 minute          Minute of the hour (integer)                                       
15 yhour           Hour of the year (integer)                                         
16 year            Year (integer)     

Tellingit / bike stations

 varname description                                        
  <chr>   <chr>                                              
1 id      Station id (character)                             
2 name    Station name  (character)                          
3 x       longitude (double)                                 
4 y       latitude (double)                                  
5 time    Timestamp. Format 'yyyy/mm/dd hh:mm:ss' (character)
6 yday    Day of the year (integer)                          
7 year    Year (integer)   

Sää / weather

   varname          description                                        
   <chr>            <chr>                                              
 1 ASEMA            Weather station name (character)                   
 2 AIKA             Time. Format 'mm:hh, d.m.yy'  (character)          
 3 LÄMPÖTILA_C      Temperature in Celsius (double)                    
 4 KOSTEUS          Relative humidity in percent (double)              
 5 KASTEPISTE_C     Dew point in Celsius (double)                      
 6 ILMANPAINE_HPA   Atmospheric pressure in hPA (double)               
 7 TUULI_MS         Wind speed, m/s. (double)                          
 8 PUUSKA_MS        Wind gust, m/s. (double)                           
 9 SUUNTA           Precipitation intensity, mm/h (double)             
10 SAT_RANKKUUS_MMH Wind direction, degrees (integer)                  
11 NÄKYVYYS_M       Visibility, meters (integer)                       
12 LUMI_CM          Snow depth, cm (integer)                           
13 PILVISYYS        Cloud cover, 0-8 (integer)                         
14 TIME             Timestamp. Format 'yyyy/mm/dd hh:mm:ss' (character)

Sääennuste / weather forecast

   varname            description                                                     
   <chr>              <chr>                                                           
 1 AJANKOHTA          Time for forecast, format 'dd.mm.yyyy hh.mm' (character)        
 2 OLOSUHDE           Condition (logical)                                             
 3 LÄMPÖTILA_C        Temperature in Celsius (double)                                 
 4 KOSTEUS            Relative humidity in percent (double)                           
 5 TUULEN_NOPEUS_MS   Wind speed, m/s. (double)                                       
 6 TUULEN_SUUNTA      Wind direction, degrees (integer)                               
 7 PUUSKA_MS          Wind gust, m/s. (double)                                        
 8 SADE_1H_MM         Precipitation intensity, mm/h (double)                          
 9 ILMANPAINE_HPA     Atmospheric pressure in hPA (double)                            
10 PILVISYYS          Cloud cover, 0-8 (integer)                                      
11 GLOBAALISÄTEILY_WM Global radiation,  W/m2. (double)                               
12 TIME               Timestamp for forecast. Format 'yyyy/mm/dd hh:mm:ss' (character)

Esimerkkejä R-kielellä / Examples using R-language

I Vapaiden pyörien määrät viikolla 25 vuosina 2018-2019 tellingeillä, jotka alkavat sanalla ‘Töölö’

library(tidyverse)
tmpdir <- tempdir()
tmpfly <- paste0(tmpdir,"/tmpdata.csv.gz")
download.file(url = "http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2017.csv.gz", 
              destfile = tmpfly)
dat17 <- read_csv2(tmpfly) %>% filter(week == 25)
download.file(url = "http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2018.csv.gz", 
              destfile = tmpfly)
dat18 <- read_csv2(tmpfly) %>% filter(week == 25)
download.file(url = "http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2019.csv.gz", 
              destfile = tmpfly)
dat19 <- read_csv2(tmpfly) %>% filter(week == 25)

dat <- bind_rows(
  dat17,
  dat18,
  dat19
)

stations <- read_csv2("http://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2019.csv.gz")

dat25 <- dat %>% 
  left_join(stations %>% 
              select(-time)) %>% 
  filter(grepl("Töölö", name)) %>% 
  mutate(time = as.POSIXct(time))

ggplot(dat25, aes(x = time, y = bikesAvailable, color = name, group = name)) +
  geom_line() +
  facet_wrap(~year, scales = "free_x", ncol = 1) +
  theme_minimal(base_family = "PT Sans") +
  labs(title = "Vapaiden pyörien määrä viikolla 25 vuosina 2017, 2018 ja 2019",
       subtitle = "Mukana `Töölö`-alkuiset telineet",
       y = "Vapaiden pyörien määrä", x = NULL, color = "tellinki")
## [1] "LC_CTYPE=fi_FI.UTF-8;LC_NUMERIC=C;LC_TIME=fi_FI.UTF-8;LC_COLLATE=fi_FI.UTF-8;LC_MONETARY=fi_FI.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=en_US.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US.UTF-8;LC_IDENTIFICATION=C"

II Tellinkien elinvuodet kartalla / Stations lifespan on map

library(tidyverse)
library(sf)
stations <- read_csv2("http://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2019.csv.gz")
stations %>% 
  distinct(id,name,year, .keep_all = TRUE) %>% 
  group_by(id,name) %>% 
  count(year) %>% 
  pivot_wider(names_from = year, values_from = n) %>% 
  mutate(type = case_when(
    `2017` == 1 & `2018` == 1 & `2019` == 1 ~ "2017-2019",
    `2017` == 1 & `2018` == 1 & is.na(`2019`) ~ "2017-2018",
    `2017` == 1 & is.na(`2018`) & is.na(`2019`) ~ "2017",
    is.na(`2017`) & `2018` == 1 & `2019` == 1 ~ "2018-2019",
    is.na(`2017`) & `2018` == 1 & is.na(`2019`) ~ "2018",
    is.na(`2017`) & is.na(`2018`) & `2019` == 1 ~ "2019",
    TRUE ~ "joku muu"
  )) %>% ungroup() %>% 
  select(id,type) -> alive

alive %>% 
  left_join(distinct(stations, id,name,x,y)) %>% 
  # poistetaan testit / remove tests
  filter(x <= 30, y >= 55) %>% 
  sf::st_as_sf(coords = c(4,5)) -> spatdat

library(leaflet)
pal <- colorFactor(
    palette = "Set2",domain = unique(alive$type))
  
leaflet(spatdat) %>% 
  leaflet::addProviderTiles(providers$CartoDB.Positron) %>% 
  addCircleMarkers(color = ~pal(type), 
                   label = ~paste(name,"\n", type), 
                   labelOptions = labelOptions(noHide = F, direction = "auto",
                                               style = list(
                                                 "color" = "black",
                                                 "box-shadow" = "3px 3px rgba(0,0,0,0.25)",
                                                 "font-size" = "10px",
                                                 "border-color" = "rgba(0,0,0,0.5)",
                                                 "background" = "rgba(235, 235, 235, 0.61)"
                                               ))) %>% 
    addLegend(pal = pal, 
            values = ~type, 
            opacity = 0.7, 
            title = "Tellinki olemassa",
            position = "bottomright")

III Säätila ja aseman vilkkaus syyskuun aamuina Meilahden sairaalan ja Unioninkadun tellingeillä

library(tidyverse)
tmpdir <- tempdir()
tmpfly <- paste0(tmpdir,"/tmpdata.csv.gz")
download.file(url = "http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2017.csv.gz", 
              destfile = tmpfly)
dat17 <- read_csv2(tmpfly) %>% filter(month == 9,hour %in% 6:10)
download.file(url = "http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2018.csv.gz", 
              destfile = tmpfly)
dat18 <- read_csv2(tmpfly) %>% filter(month == 9,hour %in% 6:10)
download.file(url = "http://data.markuskainu.fi/opendata/kaupunkipyorat/data_2019.csv.gz", 
              destfile = tmpfly)
dat19 <- read_csv2(tmpfly) %>% filter(month == 9,hour %in% 6:10)

dat <- bind_rows(
  dat17,
  dat18,
  dat19
)

stations <- read_csv2("http://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2019.csv.gz")

mo <- dat %>% 
  left_join(stations %>% 
              select(-time)) %>% 
  filter(grepl("Meilahden|Unionin", name)) %>% 
  mutate(time = as.POSIXct(time)) %>% 
  group_by(id,name) %>% 
  mutate(freq = abs(bikesAvailable-lag(bikesAvailable))) %>% 
  ungroup()

mohour <- 
  mo %>% 
  group_by(id,name,year,week,month,yday,day,yhour,week,hour) %>% 
  summarise(freqs = sum(freq, na.rm = TRUE)) %>% 
  ungroup() %>% 
  arrange(yhour)

# Data from FMI
## Install fmi2 with remotes::install_github("ropengov/fmi2")
library(fmi2)
dat_date <- mo %>% 
  distinct(year,yday, .keep_all = TRUE) %>% 
  mutate(date = as.Date(time)) %>% 
  select(year,date)

library(glue)
dates <- dat_date$date
fmi_tmp <- list()
for (i in 1:length(dates)){  
  pvm <- dates[i]
  fmi_tmp[[pvm]] <- fmi2::obs_weather_hourly(starttime = glue("{pvm}T06:00:00Z"), 
                                             endtime = glue("{pvm}T10:00:00Z"), 
                                             fmisid = "100971") %>% 
    filter(variable %in% c("TA_PT1H_AVG","PRI_PT1H_MAX","WS_PT1H_AVG","RH_PT1H_AVG")) %>% 
    sf::st_set_geometry(value = NULL) %>% 
    as_tibble() %>% 
    mutate(var = case_when(
      variable == "TA_PT1H_AVG" ~ "Ilman lämpötila / Air temperature (C)",
      variable == "PRI_PT1H_MAX" ~ "Sateen intensiteetti / Maximum precipitation intensity (mm/h)",
      variable == "WS_PT1H_AVG" ~ "Tuulen nopeus / Wind speed (m/s)",
      variable == "RH_PT1H_AVG" ~ "Ilmankosteus / Relative humidity (%)"),
      station = "Kaisaniemi") %>%
    select(-variable)
}
fmi <- do.call(bind_rows, fmi_tmp) %>% 
  mutate(year = lubridate::year(time),
         yday = lubridate::yday(time),
         hour = lubridate::hour(time),
         yhour = (yday - 93) * 24 + hour) %>% 
  select(-hour,-yday)

df <- left_join(mohour, fmi) %>% 
  filter(!is.na(var))
ggplot(df, aes(x = value, y = freqs, color = factor(year), group = factor(year))) +
  geom_point(shape = 21, alpha = .6) +
  facet_grid(name~sub("/", "\n", var), scales = "free") +
  geom_smooth(method = "lm", se = FALSE) +
  theme_minimal(base_family = "PT Sans") +
  labs(y = "Lainauksia tai palautuksia tunnissa / Rentals or return per hour",
       x = NULL, color = NULL)