Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 62 additions & 67 deletions instructors/01-practical-tutors.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
)
```

::: {.content-hidden when-format="html"}

Check warning on line 21 in instructors/01-practical-tutors.qmd

View workflow job for this annotation

GitHub Actions / Build markdown source files if valid

[unknown div] content-hidden

<!-- visible for instructors only -->
<!-- practical-week.md is generated from practical-week.qmd. Please edit that file -->
Expand All @@ -28,20 +28,19 @@

:::

::: {.content-hidden when-format="docx"}

Check warning on line 31 in instructors/01-practical-tutors.qmd

View workflow job for this annotation

GitHub Actions / Build markdown source files if valid

[unknown div] content-hidden

<!-- works for text on html and MD only -->

This practical is based in the following tutorial episodes:

- <https://epiverse-trace.github.io/tutorials-early/clean-data.html>
- <https://epiverse-trace.github.io/tutorials-early/validate.html>
- <https://epiverse-trace.github.io/tutorials-early/describe-cases.html>


:::

::: {.content-visible when-format="docx"}

Check warning on line 43 in instructors/01-practical-tutors.qmd

View workflow job for this annotation

GitHub Actions / Build markdown source files if valid

[unknown div] content-visible

{{< include _welcome.qmd >}}

Expand All @@ -55,47 +54,45 @@

**Goal:**

Get a clean and standardized data frame using the following available inputs:
Produce a clean and standardized data frame from the following input:

- Raw messy data frame in CSV format
- Raw, messy CSV file

**Steps:**

- Open file `01-practical-activity-1.R` and complete all the lines marked with `#<COMPLETE>`, following the detailed steps provided within the R file.
- First, complete the argument to read the data. Paste the link as a “string” in `read_csv()`.
- Second, complete the cleaning process. Add functions based on the data needs. Connect them using the pipe `%>%`:
- Standardize column names
- Standardize dates
- Check date sequence
- Convert to numeric
- Replace missing values
- Clean using dictionary
- Remove constants
- Remove duplicates
- Third, complete the cleanepi::timespan() arguments. Access the help manual running `?cleanepi::timespan()` in the console.
- Paste the outputs. Reply to questions.
1. Open `01-practical-activity-1.R` and complete every line marked with `#<COMPLETE>`, following the instructions in the file.
2. Complete the argument in `read_csv()` by pasting the data URL link as a string.
4. Complete the arguments of cleaning function. Remove functions based on the data needs.
5. Paste screenshots of the data frame before and after cleaning. Answer the questions below.

**Questions:**

Within your room, Write your answers to these questions:
1. **Diagnose the raw data.** What cleaning operations are needed? Which ones diagnose issues or perform cleaning actions?
2. **Time unit.** What time unit best describes the time span you need to calculate?
3. **Report.** Print the report. Which features are most useful for you as an analyst to understand or communicate the issues in the data frame?

- Diagnose the raw data. What data cleaning operations need to be performed on the dataset? Write all of them before writing the code.
- What time unit best describes the time span to calculate?
- Print the report: What features do you find useful to communicate with a decision-maker?
- Compare: What differences do you identify from other room outputs? (if available)
Discuss your answers with your group before sharing with the wider room.

### Inputs

| Room | Data | Link | Calculate time span | Categorize time span |
|---|---|---|---|---|
| 1 | Small linelist | <https://epiverse-trace.github.io/tutorials-early/data/linelist-date_of_birth.csv> | Age as of today | breaks = c(0, 20, 35, 60, 80) |
| 2 | Large linelist | <https://epiverse-trace.github.io/tutorials-early/data/covid_simulated_data.csv> | Delay from onset of symptoms to the time of death | breaks = c(0, 10, 15, 40) |
| 3 | Serology data [^1] | <https://epiverse-trace.github.io/tutorials-early/data/delta_full-messy.csv> | Time from last exposure to vaccine | breaks = c(0, 30, 100, 600) |
| Room | Data | Link | Calculate time span |
|---|---|---|---|
| 1 | Small linelist | <https://epiverse-trace.github.io/tutorials-early/data/linelist-date_of_birth.csv> | Age (as of today) |
| 2 | Large linelist | <https://epiverse-trace.github.io/tutorials-early/data/covid_simulated_data.csv> | Time from symptom onset to death |
| 3 | Serology data [^1] | <https://epiverse-trace.github.io/tutorials-early/data/delta_full-messy.csv> | Time from last vaccine dose to sample collection [^2] |

[^1]: **Context of Serological data**: Participants of a study are vaccinated against COVID-19 in multiple doses. Researchers collect serum samples some days after each vaccination dose and expose the serum to emerging SARS-CoV-2 variants. They measure the titre of this immunological response. The higher the titre, the higher the antigenic response. Ref: <https://www.thelancet.com/journals/laninf/article/PIIS1473-3099(24)00484-5/fulltext>

[^1]: Context of Serological data: Participants of a study are exposed to COVID-19 vaccines, then their serum samples are collected and challenged to emerging SARS-CoV-2 variants. They measure the titer of this immunological response. The higher the titre, the higher the antigenic response. Let's focus on describing the change in the frequency of vaccine categories (last vaccine exposure) through time (last date of exposure). Ref: https://www.thelancet.com/journals/laninf/article/PIIS1473-3099(24)00484-5/fulltext
[^2]: **How to read the repeated-measurements cohort dataset**:
Subject `pid = 1` was `Infection naive` (never exposed to infection).
Was exposed 2 times (`exp_num`) to the vaccine type BNT162b2 (`last_vax_type`).
The last vaccine exposure was on 2021-03-08 (`last_exp_date`).
One serum sample was obtained on 2021-03-10 (`date`, two days after last vaccine exposure).
The antibody titre against `Alpha` and `Delta` SARS-CoV-2 variants (`titre_type`) was 5 units (`value`).
The antibody titre against `Ancestral` SARS-CoV-2 variants was 176 units.


::: {.content-visible when-format="docx"}

Check warning on line 95 in instructors/01-practical-tutors.qmd

View workflow job for this annotation

GitHub Actions / Build markdown source files if valid

[unknown div] content-visible

### Your Answers

Expand Down Expand Up @@ -167,46 +164,41 @@



## Activity 2: Validate linelist and plot epicurve
## Activity 2: Plot delays and epicurves

**Goal:**

Get a validated linelist and incidence plot using the following available inputs:
Using a clean linelist data frame, produce:

- Clean data frame object
- A delay distribution plot showing time between two epidemiological events.
- An incidence plot (epicurve) showing case counts over time.

**Steps:**

- Open the file `01-practical-activity-2.R` and complete all the lines marked with `#<COMPLETE>`, following the detailed steps provided within the R file.
- First, complete linelist::make_linelist() arguments.
- Second, complete the {linelist} function that can validate a linelist.
- Third, complete the arguments of the incidence2::incidence()
- Fourth, keep, drop, or change argument values in function plot()
- Paste the outputs. Reply to questions.
1. Open `01-practical-activity-2.R` and complete every line marked with `#<COMPLETE>`, following the instructions in the file.
2. Describe the pre-configured epidemiological delay.
3. Complete the arguments of `incidence2::incidence()` to generate an incidence object.
4. Adjust the arguments of `plot()` to get the most informative epicurve. Read the [`plot()` reference manual](https://www.reconverse.org/incidence2/manual.html#sec:man-plot.incidence2) to find available arguments.
5. Paste your plots and reply to the questions below.

**Questions:**

Within your room, Write your answers to these questions:
- Which combination of time unit and case categories best captures the outbreak pattern and why?
- What does the shape of your epicurve suggest about how this outbreak spread?
- Which is larger in your delay distribution: the mean or the median, and what does that tell you about its shape?
- How might delays in the data collection process affect your interpretation of the most recent cases?

- In the validation step, Do you need to allow for extra variable names and types for the `Date` and `Categorical` variable?
- _[Read this GitHub issue as a hint](https://github.com/epiverse-trace/linelist/issues/176) to allow for extra variables._
- What is the most apprioriate time unit to aggregate the incidence plot, based on visual inspection?
- Does keeping or dropping arguments like `fill`, `show_cases`, `angle`, `n_breaks` improve the incidence plot?
- _[Read `plot()` reference manual](https://www.reconverse.org/incidence2/manual.html#sec:man-plot.incidence2) to find its arguments._
- Interpret: How would you communicate these results to a decision-maker?
- Compare: What differences do you identify from other room outputs? (if available)
Discuss your answers with your group before sharing with the wider room.

### Inputs

Use outputs from activity 1.

| Room | Date | Categorical variable |
| Room | Disease | Link |
|---|---|---|
| 1 | Date reporting | Age category |
| 2 | Date onset | Outcome |
| 3 | Last exposure date | Last vaccine type |
| 1 | COVID | <https://epiverse-trace.github.io/tutorials-early/data/covid_simulist.rds> |
| 2 | Ebola | <https://epiverse-trace.github.io/tutorials-early/data/ebola_simulist.rds> |
| 3 | Unknown | <https://epiverse-trace.github.io/tutorials-early/data/unknown_simulist.rds> |

::: {.content-visible when-format="docx"}

Check warning on line 201 in instructors/01-practical-tutors.qmd

View workflow job for this annotation

GitHub Actions / Build markdown source files if valid

[unknown div] content-visible

### Your Answers

Expand All @@ -214,7 +206,7 @@

| Data | Paste screenshot below |
|---|---|
| validated | |
| epidemiological delay | |
| incidence plot | |

Write your answers to the questions above:
Expand All @@ -236,7 +228,7 @@

| Data | Paste screenshot below |
|---|---|
| validated | |
| epidemiological delay | |
| incidence plot | |

Write your answers to the questions above:
Expand All @@ -258,7 +250,7 @@

| Data | Paste screenshot below |
|---|---|
| validated | |
| epidemiological delay | |
| incidence plot | |

Write your answers to the questions above:
Expand All @@ -278,7 +270,7 @@



::: {.content-visible unless-format="docx"}

Check warning on line 273 in instructors/01-practical-tutors.qmd

View workflow job for this annotation

GitHub Actions / Build markdown source files if valid

[unknown div] content-visible

### Solution

Expand Down Expand Up @@ -340,17 +332,17 @@

##### activity 2

| Room | Output |
|---|---|
| 1 | ![image](https://hackmd.io/_uploads/ry5d6xnA1e.png) |
| 2 | ![image](https://hackmd.io/_uploads/SJ2f0e2Cyx.png) |
| 3 | ![image](https://hackmd.io/_uploads/H1-PRlhA1g.png) |
| Group | Delay | Incidence |
| --- | --- | --- |
| 1 | ![image](https://hackmd.io/_uploads/SkIzvCrMMe.png) | ![image](https://hackmd.io/_uploads/rkIuVRBGMe.png) |
| 2 | ![image](https://hackmd.io/_uploads/rys4wCHGfl.png) | ![image](https://hackmd.io/_uploads/H1F-FAHGfe.png) |
| 3 | ![image](https://hackmd.io/_uploads/BkoSPCBMfg.png) | ![image](https://hackmd.io/_uploads/SJpIICSGzl.png) |

| Room | validation | time unit | incidence plot |
|---|---|---|---|
| 1 | The timespan_category (age categories) must be added as an extra variable. Declare age as “factor”. | An appropriate time unit to aggregated is using interval by month. | The argument `show_cases` can improve the visibility of `fill` categorical variables |
| 2 | It is not required to add an extra variable. | An appropriate time unit to aggregate is using interval by day. | Keeping angle and `n_breaks` works. Dropping `fill` keeps the facets making "died" are move visible. |
| 3 | last_exp_date and last_vax_type must be added as extra variables. Declare last_vax_type as “character”, and last_vax_date as “Date”. | An appropriate time unit to aggregated is using interval by month. | Keeping `fill` by last_vax_type works. |
| Room | Incidence arguments | Plot arguments |
|---|---|---|
| 1 | Date by onset and outcome help identify the low proportion of deaths among cases in time. | Keeping `fill` helps visualizing the delay and peak from cases to deaths in the whole population. |
| 2 | Date by onset and outcome help identify the high proportion of deaths among cases in time. | Dropping `fill` helps identify a higher proportion of deaths in older age groups. |
| 3 | Interval by month help identify a continuous growth and decay pattern. | Using `fill` with age category helps compare the higher proportion of cases in male compared to female. |

#### Interpretation

Expand All @@ -359,13 +351,16 @@
- In small data frames, we can diagnose cleaning operations easier than large data frames.
- For example, in the large data frame, before cleaning the sex variable with a data dictionary, we need to remove unconsistent missing values. We can use `dplyr::count()` to find this issue.

Validation
Epicurve

- Using the `linelist::tags_df()` output can keep stable downstream analysis. Jointly with `linelist::lost_tags_action(action = "error")` we can improve the capacity to diagnose changes in the input data. This can prevent getting misleading outputs from automatic daily code runs or dashboards updates.
- COVID: Onset cases peak sharply around 2023-W03, dominated by the under-20 age group. Outcome dates are sparse and spread later, reflecting the delay between onset and death.
- Ebola: Across all age groups, symptom onset dates peaks almost as high as outcome dates (2023-W03 to W05). Possibly with a shorter delay from onset to death than the COVID.
- Unknown: Males had substantially higher case counts than females across all age groups, with both sexes peaking around mid-2023. Older age groups (35-60 and 60-100) make up a larger share in males, while females show a more even age distribution throughout.

Epicurve
Delays

- The argument `show_cases` can improve the visibility of `fill` categorical variables when the amount of observed cases is small.
- If the mean exceeds the median, the delay distribution is right-skewed, meaning most samples are collected relatively soon after exposure, but a small number of cases have unusually long delays that pull the average upward.
- Recent cases are likely undercounted. If there is a lag between an event occurring and it being recorded, the most recent time periods will appear artificially low simply because many cases have not been logged yet. This can make it look like there is a recent decline when it is really just a reporting delay (also known as a right-censoring effect).

# Continue your learning path

Expand Down
2 changes: 0 additions & 2 deletions instructors/02-practical-tutors.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ knitr::opts_chunk$set(

This practical is based in the following tutorial episodes:

- <https://epiverse-trace.github.io/tutorials-middle/delays-access.html>
- <https://epiverse-trace.github.io/tutorials-middle/quantify-transmissibility.html>
- <https://epiverse-trace.github.io/tutorials-middle/delays-functions.html>
- <https://epiverse-trace.github.io/tutorials-middle/severity-static.html>

:::
Expand Down
11 changes: 10 additions & 1 deletion instructors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ The goal of the `##-practical-tutors.qmd` files in this folder is to host comple

## Usage

One Quarto QMD file generates:
### Inputs:

- `data/`: practical scaffold scripts
- `fig/`: practical solutions
- `-challenge` for tutorial challenge without solution
- `-miscellanea` for extended sessions

### Outputs:

In `files/`, one Quarto QMD file generates:

- Tutors MD file: for Tutors to use during practical or propose edits. Saved as `files/##-practical-tutors.md`.
- Guide DOCX file: for Learners to complete during the practical. Saved as `files/##-practical-guide.docx`.
Expand Down
86 changes: 51 additions & 35 deletions instructors/data/01-practical-activity-1.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,30 @@ room_number <- #<COMPLETE> replace with 1/2/3/4

# Load packages ----------------------------------------------------------
library(cleanepi)
library(linelist)
library(incidence2)
library(tidyverse)


# Adapt the data dictionary ----------------------------------------------

# Replace 'variable_name' when you have the information
# Replace '#<COMPLETE>' when you have the information
dat_dictionary <- tibble::tribble(
~options, ~values, ~grp, ~orders,
"1", "male", "variable_name", 1L,
"2", "female", "variable_name", 2L,
"M", "male", "variable_name", 3L,
"F", "female", "variable_name", 4L,
"m", "male", "variable_name", 5L,
"f", "female", "variable_name", 6L
~options, ~values, ~grp, ~orders,
"1", "male", "#<COMPLETE>", 1L,
"2", "female", "#<COMPLETE>", 2L,
"M", "male", "#<COMPLETE>", 3L,
"F", "female", "#<COMPLETE>", 4L,
"m", "male", "#<COMPLETE>", 5L,
"f", "female", "#<COMPLETE>", 6L
)

dat_dictionary


# Read raw data ----------------------------------------------------------

# Replace the string with the URL provided (or location to local file)
dat_raw <- readr::read_csv(
#<COMPLETE>
"paste/complete/URL/#<COMPLETE>" #<COMPLETE>
)

dat_raw
Expand All @@ -39,35 +39,51 @@ dat_raw
# Clean and standardize data ---------------------------------------------

# How many cleanepi functions did you use to get clean data?
dat_clean <- dat_raw %>%
cleanepi::#<COMPLETE>

dat_clean
# Remove those functions not needed in the cleaning workflow


# Create time span variable ----------------------------------------------

# What time span unit best describes the 'delay' from 'onset' to 'death'?
dat_timespan <- dat_clean %>%
cleanepi::timespan(
#<COMPLETE>
#<COMPLETE>
#<COMPLETE>
span_column_name = "timespan_variable",
span_remainder_unit = NULL
dat_clean <- dat_raw %>%
cleanepi::standardize_column_names() %>%
cleanepi::standardize_dates(
target_columns = c(
#<COMPLETE>,
#<COMPLETE>,
#<COMPLETE>
)
) %>%
cleanepi::check_date_sequence(
target_columns = c(
#<COMPLETE>,
#<COMPLETE>,
#<COMPLETE>
)
) %>%
cleanepi::convert_to_numeric(
target_columns = #<COMPLETE>
) %>%
# using data_dictionary requires valid missing entries
cleanepi::replace_missing_values(
target_columns = #<COMPLETE>,
na_strings = #<COMPLETE>
) %>%
# skimr::skim(timespan_variable)
# Categorize the delay numerical variable
dplyr::mutate(
timespan_category = base::cut(
x = timespan_variable,
breaks = #<COMPLETE>,
include.lowest = TRUE,
right = FALSE
cleanepi::clean_using_dictionary(
dictionary = #<COMPLETE>
) %>%
cleanepi::remove_constants() %>%
cleanepi::remove_duplicates(
target_columns = c(
#<COMPLETE>,
#<COMPLETE>
)
) %>%
cleanepi::timespan(
target_column = #<COMPLETE>,
end_date = #<COMPLETE>,
span_unit = #<COMPLETE>,
span_column_name = #<COMPLETE>,
span_remainder_unit = #<COMPLETE>
)

dat_timespan
dat_clean


# nolint end
Loading
Loading