# INBO CODING CLUB
27 June 2024
Welcome!
## Share your code snippet
If you want to share your code snippet, copy paste your snippet within a section of three backticks (```):
As an **example**:
```
library(tidyverse)
```
(*you can copy paste this example and add your code further down*)
## Yellow sticky notes
No yellow sticky notes online. Put your name + " | " and add a "*" each time you solve a challenge (see below).
## Participants
Name | Challenges
--- | ---
Damiano Oldoni | ***
Pieter Huybrechts | ***
Nele Mullens | **
Ward Standaert | **
Rhea Maesele | *
Dirk Maes |
Dirk Festraets |
Jo-Hannes Nowé |**
Heleen Deroo |
Sanne Govaert | **
Siebe Indestege | **
Anja Leyman | *
Soria Delva | *
Tina Tuerlings | ***
Katrijn Alaerts | **
Lotte Pohl | *
Lucia Manzanares | *
## Challenge 1
### Damiano's solution (example)
Copy paste this section to show your solutions.
```r
# dummy code
a <- 1
print("This is how to insert text which looks like code.")
```
### Heleen's solution
````
map(swiss, mean)
map(iris, class)
list_with_dfs <- list(swiss = swiss, iris = iris)
map(list_with_dfs, nrow)
map_dbl(swiss, mean)
map_chr(iris, class)
map_dbl(list_with_dfs, nrow)
map_df(swiss, mean)
map_df(iris, class)
map_df(list_with_dfs, nrow)
````
### Nele's solution
```
#1.1 Compute the mean of every column in the swiss data.frame.
#swiss is a basic package in R base, just like iris
map(swiss, mean)
#1.2 Determine the type of each column in the iris data.frame.
map(iris, class)
#1.3 After creating the variable list_with_dfs as a named list with swiss and
# iris (code provided), compute the number of rows of each data.frame.
list_with_dfs <- list(iris = iris, swiss = swiss) #list with dataframes
map(list_with_dfs, nrow)
#1.4 map() ALWAYS returns a list, ALWAYS. Apply the right map_*() variants to
# the previous exercises to return a numeric vector (exercises 1 and 3), a
# character vector (exercise 2) or a data.frame (exercises 1, 2 and 3). Hint:
# use cheat sheet or the sections map-atomic and the-map-functions.
map_vec(swiss, mean)
map_vec(list_with_dfs, nrow)
map_chr(iris, class)
map_df(swiss, mean)
map_df(iris, class)
map_df(list_with_dfs, nrow)
```
### Jo-Hannes' solution
```
## CHALLENGE 1
#Write map() statements to:
#1.
map(swiss, mean)
#2.
map(iris,typeof)
#3.
list_with_dfs <- list(swiss = swiss, iris = iris)
map(list_with_dfs,nrow)
#4.
map_dbl(swiss,mean)
map_dbl(list_with_dfs,nrow)
map_chr(iris,typeof)
map_df(swiss,mean)
map_df(iris,typeof)
map_df(list_with_dfs,nrow)
```
### Siebe's Solution
```
# 1.1
map(swiss, mean)
# 1.2
map(iris, class)
# 1.3
list_with_dfs <- list(swiss, iris)
names(list_with_dfs) <- c("swiss","iris")
map(list_with_dfs, nrow)
# 1.4
map_dbl(swiss, mean)
map_dbl(list_with_dfs, nrow)
map_chr(iris,class)
map_df(swiss, mean)
map_df(iris, class)
map_df(list_with_dfs, nrow)
```
## Challenge 2
### Sanne's solution
```r
# 2.1
map(sensors, read_sensor_data)
# 2.2
sensor_dfs <- map(
sensors,
read_sensor_data,
path_pattern = path_pattern_datafiles,
extension = file_extension
)
# 2.3
map2(means, st_dev, rnorm, n = 10) %>% str()
# 2.4
datetimes <- function(sensor_df) {
summarize(
sensor_df,
min_datetime = min(datetime),
max_datetime = max(datetime)
)
}
map_df(sensor_dfs, datetimes)
```
### Ward's solution
```r
# 2.1
# Define the list with the sensor IDs
sensors <- list("A1", "G7", "H4")
names(sensors) <- sensors
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' read_sensor_data(sensor = "H4")
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0("./data/20240627/20240627_sensor_", sensor, ".txt")
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1")
sensor_list <- map(sensors, \(x) read_sensor_data(x))
# 2.2
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @param path_pattern character. The path common to all sensor files.
#' @param extension character. The file extension starting with `.`.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
#' file_extension <- ".txt"
#' read_sensor_data(sensor = "H4", path_pattern_datafiles, file_extension)
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0(path_pattern, sensor, extension)
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# If data files are where they should be, here the path_pattern and
# extension to use. Just copied from the @example above the function.
path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
file_extension <- ".txt"
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1",
path_pattern = path_pattern_datafiles,
extension = file_extension
)
map(sensors, read_sensor_data,
path_pattern = path_pattern_datafiles,
extension = file_extension)
# 2.3
# Define means and standard deviations
means <- c(-10, 0, 10, 20)
st_dev <- c(1, 3, 2, 1.5)
map2(means, st_dev, rnorm, n = 10)
# 2.4
max_min_fun <- \(x) summarize(x, min_datetime = min(datetime), max_datetime = max(datetime))
map_df(sensor_list, max_min_fun)
```
### Jo-Hannes' solution
```r
# 2.1
# Define the list with the sensor IDs
sensors <- list("A1", "G7", "H4")
names(sensors) <- sensors
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' read_sensor_data(sensor = "H4")
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0("./data/20240627/20240627_sensor_", sensor, ".txt")
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1")
sensor_data <- map(sensors,read_sensor_data)
# 2.2 Now, you want to generalize your script
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @param path_pattern character. The path common to all sensor files.
#' @param extension character. The file extension starting with `.`.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
#' file_extension <- ".txt"
#' read_sensor_data(sensor = "H4", path_pattern_datafiles, file_extension)
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0(path_pattern, sensor, extension)
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# If data files are where they should be, here the path_pattern and
# extension to use. Just copied from the @example above the function.
path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
file_extension <- ".txt"
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1",
path_pattern = path_pattern_datafiles,
extension = file_extension
)
sensor_data <- map(sensors, \(x) read_sensor_data(x,path_pattern= path_pattern_datafiles,
extension=file_extension))
# 2.3
# Define means and standard deviations
means <- c(-10, 0, 10, 20)
st_dev <- c(1, 3, 2, 1.5)
#for one example
rnorm(n=10, mean = -10, sd=1)
#map to all elements
map2(means,st_dev, \(x,y) rnorm(n=10,mean=x,sd=y))
# 2.4
#sensor_data is a list of the sensor data.frames
#Try it for one example
summarize(sensor_data[[1]], min_datetime = min(datetime), max_datetime = max(datetime))
#map it to all elements of the list (so each dataframe)
map(sensor_data, \(x) summarize(x, min_datetime = min(datetime), max_datetime = max(datetime)))
# 2.3
# Define means and standard deviations
means <- c(-10, 0, 10, 20)
st_dev <- c(1, 3, 2, 1.5)
#for one example
rnorm(n=10, mean = -10, sd=1)
#map to all elements
map2(means,st_dev, \(x,y) rnorm(n=10,mean=x,sd=y))
# 2.4
#sensor_data is a list of the sensor data.frames
#Try it for one example
summarize(sensor_data[[1]], min_datetime = min(datetime), max_datetime = max(datetime))
#map it to all elements of the list (so each dataframe)
map(sensor_data, \(x) summarize(x, min_datetime = min(datetime), max_datetime = max(datetime)))
```
### Nele's solution
```r
# 2.1
# Define the list with the sensor IDs
sensors <- list("A1", "G7", "H4")
names(sensors) <- sensors
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' read_sensor_data(sensor = "H4")
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0("./data/20240627/20240627_sensor_", sensor, ".txt")
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1")
all_sensors <- map(sensors, read_sensor_data)
# 2.2
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @param path_pattern character. The path common to all sensor files.
#' @param extension character. The file extension starting with `.`.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
#' file_extension <- ".txt"
#' read_sensor_data(sensor = "H4", path_pattern_datafiles, file_extension)
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0(path_pattern, sensor, extension)
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# If data files are where they should be, here the path_pattern and
# extension to use. Just copied from the @example above the function.
path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
file_extension <- ".txt"
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1",
path_pattern = path_pattern_datafiles,
extension = file_extension
)
map(sensors, read_sensor_data, path_pattern_datafiles, file_extension)
# er zijn drie argumenten in de functie, de eerste vult ze vanzelf in, de tweede
# en derde niet, deze moeten na de functie (hier read_sensor_data) zelf nog in-
# gevuld worden
# 2.3 Calculate 10 random numbers from a normal distribution (rnorm(n = 10)) for
# each pair of the 4 provided means and standard deviations and return it as a list of 4
# Define means and standard deviations
means <- c(-10, 0, 10, 20)
st_dev <- c(1, 3, 2, 1.5)
map2(means, st_dev, function(x, y) rnorm(n = 10, mean = x, sd = y))
# 2.4
# get sensor data ready
sensor_data <- map(sensors, read_sensor_data, path_pattern_datafiles, file_extension)
# we need to get a function to do what we want
function(x) summarize(x, min_datetime = min(datetime), max_datetime = max(datetime))
# apply the function, output a dataframe
min_max_time <- map_df(sensor_data, function(x) summarize(x, min_datetime = min(datetime), max_datetime = max(datetime)))
# but I want to see what dates are from what sensor. Add names:
min_max_time <- map2_df(sensor_data, sensors, function(x, sensors) summarize(x, sensor = sensors, min_datetime = min(datetime), max_datetime = max(datetime)))
```
### Pieter's solution
```r
# 2.1
# Define the list with the sensor IDs
sensors <- list("A1", "G7", "H4")
names(sensors) <- sensors
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' read_sensor_data(sensor = "H4")
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0("./data/20240627/20240627_sensor_", sensor, ".txt")
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1")
### 2.1
map(sensors, read_sensor_data)
# 2.2
#' Function to read a comma separated sensor data file.
#'
#' @param sensor character. A sensor ID.
#' @param path_pattern character. The path common to all sensor files.
#' @param extension character. The file extension starting with `.`.
#' @return A tibble data.frame as returned by `readr::read_csv()`.
#' @examples
#' # example code
#' library(readr)
#' path_pattern_datafiles <- "./data/20240627/20240627_sensor_"
#' file_extension <- ".txt"
#' read_sensor_data(sensor = "H4", path_pattern_datafiles, file_extension)
read_sensor_data <- function(
sensor,
path_pattern,
extension) {
path <- paste0(path_pattern, sensor, extension)
readr::read_csv(path,
na = "NA",
col_types = cols(
datetime = col_datetime(format = "%d/%m/%Y %H:%M:%S")
)
)
}
# If data files are where they should be, here the path_pattern and
# extension to use. Just copied from the @example above the function.
path_pattern_datafiles <- "./data/20200630/20200630_sensor_"
file_extension <- ".txt"
# Apply function to one sensor ID as check.
sensor_df <- read_sensor_data(sensor = "A1",
path_pattern = path_pattern_datafiles,
extension = file_extension
)
all_sensor_dfs <- map(
sensors,
~ read_sensor_data(.x,
path_pattern = "./data/20240627/20240627_sensor_",
extension = ".txt"
)
)
map(
sensors,
read_sensor_data,
path_pattern = "./data/20240627/20240627_sensor_",
extension = ".txt"
)
# 2.3
# Define means and standard deviations
means <- c(-10, 0, 10, 20)
st_dev <- c(1, 3, 2, 1.5)
map2(means,
st_dev,
\(mean_to_calc, sd_to_calc) rnorm(n = 10,
mean = mean_to_calc,
sd = sd_to_calc)
)
# 2.4
get_min_max_dtime <- function(df) {
summarize(df, min_datetime = min(datetime), max_datetime = max(datetime)) |>
ungroup()
}
all_sensor_dfs |>
map(get_min_max_dtime)
```
## Challenge 3
### Sanne's solution
```r
# 3.1
lepidoptera_in_prot_areas <- readRDS("./data/20240627/20240627_lepidoptera_in_prot_areas.RData")
map(lepidoptera_in_prot_areas, length) %>% str()
# 3.2
obs <- readr::read_tsv("./data/20240627/20240627_lepidoptera_2024.tsv", na = "")
slice(obs, unlist(lepidoptera_in_prot_areas))
3.3
# Species data.frames
species_1 <- tibble(
species = c("Balanus tintinnabulum",
"Callinectes sapidus",
"Mnemiopsis leidyi"),
is_marine = c(TRUE, TRUE, TRUE)
)
species_2 <- tibble(
species = c("Leuciscus aspius",
"Rhithropanopeus harrisii",
"Mnemiopsis leidyi"),
genus = c("Leuciscus",
"Rhithropanopeus",
"Mnemiopsis leidyi")
)
species_3 <- tibble(
species = c("Rhithropanopeus harrisii","Potamopyrgus antipodarum"),
informal_group = c("mollusca", "mollusca")
)
reduce(list(species_1, species_2, species_3), full_join, by = "species")
```
### Tina's solution
```r
#### CHALLENGE 3A####
# Load lepidoptera_in_prot_areas
lepidoptera_in_prot_areas <- readRDS("./data/20240627/20240627_lepidoptera_in_prot_areas.RData")
lepidoptera_in_prot_areas
#list with the (row numbers of the) lepidoptera observations taken in protected areas.
# It's a kind of "special" list, but still a list! So, you can apply all purrr
# functions to it.
class(lepidoptera_in_prot_areas)
# 3.1 how many observations for each protected area?
map_int(lepidoptera_in_prot_areas, length)
# 3.2 Given obs, the data.frame with the GBIF observations
#(see the provided code), how to get a data.frame with only the observations
#taken in protected areas?
obs <- readr::read_tsv("./data/20240627/20240627_lepidoptera_2024.tsv", na = "")
str(obs)
protected <- map_df(lepidoptera_in_prot_areas, ~slice(obs,.x))
slice(iris,c(1,3,6))
# 3.3 purrr is more than the map family. To join the data.frames species_1,
#species_2 and species_3 (code provided) you could apply full_join() twice:
# Species data.frames
species_1 <- tibble(
species = c("Balanus tintinnabulum",
"Callinectes sapidus",
"Mnemiopsis leidyi"),
is_marine = c(TRUE, TRUE, TRUE)
)
species_2 <- tibble(
species = c("Leuciscus aspius",
"Rhithropanopeus harrisii",
"Mnemiopsis leidyi"),
genus = c("Leuciscus",
"Rhithropanopeus",
"Mnemiopsis leidyi")
)
species_3 <- tibble(
species = c("Rhithropanopeus harrisii","Potamopyrgus antipodarum"),
informal_group = c("mollusca", "mollusca")
)
full_join(species_1, species_2, by = "species") %>%
full_join(species_3, by = "species")
reduce(list(species_1,species_2,species_3),full_join)
#### Challenge 3B####
# Height of 5 giraffes
height_giraffe <- runif(n = 6, min = 4.3, max = 5.7)
# Weight of 5 giraffes
weight_giraffe <- rnorm(n = 6, mean = 1192, sd = 300)
# GPS tracker ID
gps_tracker_giraffe <- c("A31", "E4T", "RT4", "YU7", "3G1", "ON9")
# Data.frame with weight, height and GPS tracker IDs.
df <- tibble::tibble(
"weight" = weight_giraffe,
"height" = height_giraffe,
"gpsID" = gps_tracker_giraffe
)
# 3B.1 FOR EACH column OF df and class numeric, APPLY FUNCTION floor().
#Modify the columns.
df <- df %>%
mutate(across(where(is.numeric), ~ map_dbl(., ~ floor(.))))
# 3B.2 FOR EACH column OF df ending with ight (height, weight),
#APPLY FUNCTION floor(). Modify the columns. Same result of exercise 1 expected.
df <- df %>%
mutate(across(ends_with("ight"), ~ floor(.)))
# 3B.3 Instead of modifying the columns, how to add new ones?
df <- df %>%
mutate(across(ends_with("ight"), list(floored=~ floor(.))))
```
### Pieter's solution
```r
# 3.1
map_vec(lepidoptera_in_prot_areas, length)
# 3.2
obs <- readr::read_tsv("./data/20240627/20240627_lepidoptera_2024.tsv", na = "")
str(obs)
glimpse(obs)
map(lepidoptera_in_prot_areas, ~ slice(obs, .x))
# 3.3
# Species data.frames
species_1 <- tibble(
species = c("Balanus tintinnabulum",
"Callinectes sapidus",
"Mnemiopsis leidyi"),
is_marine = c(TRUE, TRUE, TRUE)
)
species_2 <- tibble(
species = c("Leuciscus aspius",
"Rhithropanopeus harrisii",
"Mnemiopsis leidyi"),
genus = c("Leuciscus",
"Rhithropanopeus",
"Mnemiopsis leidyi")
)
species_3 <- tibble(
species = c("Rhithropanopeus harrisii","Potamopyrgus antipodarum"),
informal_group = c("mollusca", "mollusca")
)
# Not DRY solution (hardcoded)
full_join(species_1, species_2, by = "species") %>%
full_join(species_3, by = "species")
reduce(
list(
species_1,
species_2,
species_3
),
~ full_join(.x , .y, by = join_by(species))
)
## Challenge 3B
# Height of 5 giraffes
height_giraffe <- runif(n = 6, min = 4.3, max = 5.7)
# Weight of 5 giraffes
weight_giraffe <- rnorm(n = 6, mean = 1192, sd = 300)
# GPS tracker ID
gps_tracker_giraffe <- c("A31", "E4T", "RT4", "YU7", "3G1", "ON9")
# Data.frame with weight, height and GPS tracker IDs.
df <- tibble::tibble(
"weight" = weight_giraffe,
"height" = height_giraffe,
"gpsID" = gps_tracker_giraffe
)
# 3B.1
mutate(df, across(where(is.numeric), floor))
# 3B.2
mutate(df, across(ends_with('ight'), floor))
# 3B.3
summarise(df, .by = where(is.numeric), across(everything()))
```
## Bonus Challange
### Pieter's solution
```r
# BC.1
penguin_plot_tbl <-
penguins %>%
group_by(species) %>%
nest() %>%
mutate(plot = map(data, ~ ggplot(.x) +
geom_point(aes(
x = bill_length_mm,
y = bill_depth_mm,
colour = sex
)
) + ggtitle(species)
)
)
# BC.2
paths <- paste0(penguin_plot_tbl$species, ".png")
plots <- pull(penguin_plot_tbl, plot)
pwalk(list(paths, plots), ggsave, path = "./data/20240627")
`
```