Päivitetty 2021-11-01: datat julkaistaan kerran viikossa maanantaiaamuisin

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, 2019, 2020 ja 2021. 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 CityBikeFinland:iin! This project is not connected with HKL, HSL or CityBikeFinland!

Ladattavat tiedostot / files to download

Data: Vapaat pyörät / available bikes

tiedosto / file koko / size rivejä / rows sarakkeita / columns
https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2017.csv.gz 32.8 Mt 7 795 206 16
https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2018.csv.gz 61.9 Mt 15 126 567 16
https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2019.csv.gz 124 Mt 25 966 001 16
https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2020.csv.gz 136 Mt 16
https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2021.csv.gz xxx yyy 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
https://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2021.csv.gz xxMt xxxxx 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)   

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 = "https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2017.csv.gz", 
              destfile = tmpfly)
dat17 <- read_csv2(tmpfly) %>% filter(week == 24)
download.file(url = "https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2018.csv.gz", 
              destfile = tmpfly)
dat18 <- read_csv2(tmpfly) %>% filter(week == 24)
download.file(url = "https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2019.csv.gz", 
              destfile = tmpfly)
dat19 <- read_csv2(tmpfly) %>% filter(week == 24)
download.file(url = "https://data.markuskainu.fi/opendata/kaupunkipyorat/data_2020.csv.gz", 
              destfile = tmpfly)
dat20 <- read_csv2(tmpfly) %>% filter(week == 24)


dat <- bind_rows(
  dat17,
  dat18,
  dat19,
  dat20
)

stations <- read_csv2("https://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2020.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 24 vuosina 2017, 2018, 2019 ja 2020",
       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=fi_FI.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=fi_FI.UTF-8;LC_IDENTIFICATION=C"

II Tellinkien elinvuodet kartalla / Stations lifespan on map

library(tidyverse)
library(sf)
stations <- read_csv2("https://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2021.csv.gz")
stations2 <- stations %>% 
  distinct(id,name,year, .keep_all = TRUE) %>%
  filter(x <= 30, y >= 55)
spatdat <- stations2 %>%  
  sf::st_as_sf(coords = c(3,4)) %>% 
  left_join(stations2 %>% 
              group_by(id) %>% 
              summarise(years = list(unique(year))) %>% 
              ungroup()
            ) %>% 
  group_by(id,name,year) %>% 
  mutate(vuodet =  glue::glue_collapse(sort(unlist(years)), sep = ", ", last = " ja ")) %>% 
  ungroup()


  
library(leaflet)
pal <- colorFactor(
    palette = "Set2",domain = unique(spatdat$year))
  
leaflet(spatdat) %>% 
  leaflet::addProviderTiles(providers$CartoDB.Positron) %>% 
  addCircleMarkers(data = spatdat %>% filter(year == 2017),
                   color = ~pal(year), 
                   label = ~paste(name, vuodet), 
                   group = "2017"
                   ) %>% 
    addCircleMarkers(data = spatdat %>% filter(year == 2018),
                   color = ~pal(year), 
                   label = ~paste(name, vuodet), 
                   group = "2018"
                   ) %>% 
      addCircleMarkers(data = spatdat %>% filter(year == 2019),
                   color = ~pal(year), 
                   label = ~paste(name, vuodet), 
                   group = "2019"
                   ) %>% 
        addCircleMarkers(data = spatdat %>% filter(year == 2020),
                   color = ~pal(year), 
                   label = ~paste(name, vuodet), 
                   group = "2020"
                   ) %>% 
          addCircleMarkers(data = spatdat %>% filter(year == 2021),
                   color = ~pal(year), 
                   label = ~paste(name, vuodet), 
                   group = "2021"
                   ) %>% 
    addLayersControl(
    baseGroups = c("2021", "2020", "2019", "2018", "2017"),
    options = layersControlOptions(collapsed = FALSE)
  )

III Säätila ja aseman vilkkaus touko-kesäkuun taitteen aamuina Meilahden sairaalan ja Unioninkadun tellingeillä

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



dat <- bind_rows(
  dat17,
  dat18,
  dat19,
  dat20
)

stations <- read_csv2("https://data.markuskainu.fi/opendata/kaupunkipyorat/tellingit_2017-2020.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)