Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/.quarto/

**/*.quarto_ipynb
6 changes: 6 additions & 0 deletions Announcements.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,10 @@ For previous course announcements emails, see the links below.

- April 12, 2026 [Cytometry in R - Week # 09 - It's Raining Functions!](https://github.com/UMGCCCFCSR/CytometryInR/discussions/162)

- April 21, 2026 [No Class This Week](https://github.com/UMGCCCFCSR/CytometryInR/discussions/174)

- April 28, 2026 [Cytometry in R - Week # 10 - Downsampling and Concatenation](https://github.com/UMGCCCFCSR/CytometryInR/discussions/179)

- May 05, 2026 [Cytometry in R - Week # 11 - Data for Statistics](https://github.com/UMGCCCFCSR/CytometryInR/discussions/186)

![](images/YouTubeHex.png)
45 changes: 45 additions & 0 deletions Homeworks.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,49 @@ Click [here](https://github.com/UMGCCCFCSR/CytometryInR/pulls?q=label%3A%22Week+

Stay tuned for our [walk-through]() answers on Course section completion.

# Week 10 - Downsampling and Concatenation

![](/course/10_Downsampling/images/TakeAway.jpg)

Redirect to [Week 10](course/10_Downsampling/index.qmd) content.


:::{.callout-tip title="Problem 1"}
Load a dataset into R, gate it however you like, and then export out a population of interest as their own .fcs files. Open them in either Floreada.io or the commercial software of your choice, and take a screenshot of how they look by two markers of interest.
:::

:::{.callout-tip title="Problem 2"}
In the example for `Downsampling()` we only changed one keyword (GUID), after substituting in our desired addon right before the .fcs. Since keyword use might vary by manufacturer, create a couple additional arguments for `Downsampling()` that allow you to change out the values for some additional keywords.
:::

:::{.callout-tip title="Problem 3"}
Trickier - After concatenating out an .fcs file for a cell subset of your choice, reload it back into R, extract out both the exprs matrix, and the description list. Using the keywords that got added, figure out a way using dplyr to revert the numeric keys (denoted by "_key") in the exprs matrix back to their original character values as recorded in the keywords.
:::

Click [here](https://github.com/UMGCCCFCSR/CytometryInR/pulls?q=label%3A%22Week+10%22) to see previous community attempts at answering these optional take-home problems.

Stay tuned for our [walk-through]() answers on Course section completion.

# Week 11 - Data for Statistics

![](/course/11_DataForStats/images/TakeAway.jpg)

Redirect to [Week 11](course/11_DataForStats/index.qmd) content.

:::{.callout-tip title="Problem 1"}
Implement additional `openCyto` gates, and validate that they are being placed correctly using `Utility_GatingPlot()`. Update the gate_range arguments until satisfied. Then, add new gates, and run the resulting dataset through `CombinedFlow()`, screenshotting any values that return with p-values that upon visualizing the data also look reasonable (not driven by a single-outlier).
:::

:::{.callout-tip title="Problem 2"}
In our ggbeeswarm boxplot, currently we are seeing the proportion on the y-axis. Figure out how to modify the `StatPlotsForFlow()` function to instead display the axis as a percentage, and then using additional function arguments and conditions, set the function up to allow you to switch between as desired. Also, externalize size and cex to modify your beeswarm boxplots in an easier fashion!
:::

:::{.callout-tip title="Problem 3"}
Some "typical" immunology workflows run a "normality" test before implementing a corresponding downstream test based on the result (Your friendly-neighbourhood statistician may have some strong thoughts on this field practice). Regardless of your position on this approach, see if you can modify our `StatsFromFlow()` function, using additional arguments conditional statements, to incorporate in a shapiro-wilks or `fBasics` package Omnibus K2 test first, and based on the outputs proceed downstream to either the parametric or non-parametric options
:::

Click [here](https://github.com/UMGCCCFCSR/CytometryInR/pulls?q=label%3A%22Week+11%22) to see previous community attempts at answering these optional take-home problems.

Stay tuned for our [walk-through]() answers on Course section completion.


2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Cytometry in R is a free virtual mini-course being organized by the [Flow Cytome

We are excited that so many individuals worldwide have chosen to take part, and we look forward to helping you get started on your own learning journeys.

![](/images/WorldwideSignups.png){width=100%}
![](/images/WorldwideSignups.png){width="100%"}

Click here to go to our [Course Website](https://umgcccfcsr.github.io/CytometryInR)

Expand Down
6 changes: 3 additions & 3 deletions Schedule.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ No class week of March 30, 2026. If you are attending the [ABRF conference](http

![](https://images2.minutemediacdn.com/image/upload/c_fill,w_720,ar_16:9,f_auto,q_auto,g_auto/shape/cover/sport/raining-monkeys-e3fa8001e4eb47433b1cc58e2017d2b8.png){width=75%}

[**Week 9: April 13, 2026**]{.underline} In the course of this ninth session, we tackle one of the harder but most useful concepts to learn for a beginner, namely [functions](https://r4ds.had.co.nz/functions.html). We explore what they are, how their individual arguments work, how they differ from for-loops, and how to create our own to do useful work, reduce the number times code gets copied and pasted. Additionally, some functional programming best practices will be introduced, as well as provide introduction to how to use the walk and map functions from the [purrr](https://purrr.tidyverse.org/) package.
[**Week 9: April 13, 2026**]{.underline} In the course of this [ninth](/course/09_Functions/index.qmd) session, we tackle one of the harder but most useful concepts to learn for a beginner, namely [functions](https://r4ds.had.co.nz/functions.html). We explore what they are, how their individual arguments work, how they differ from for-loops, and how to create our own to do useful work, reduce the number times code gets copied and pasted. Additionally, some functional programming best practices will be introduced, as well as provide introduction to how to use the walk and map functions from the [purrr](https://purrr.tidyverse.org/) package.

<br>
<br>
Expand All @@ -126,7 +126,7 @@ No class week of March 30, 2026. If you are attending the [ABRF conference](http

![](https://www.ibm.com/content/dam/connectedassets-adobe-cms/worldwide-content/creative-assets/s-migr/ul/g/7d/10/downsampling-near-miss-v3.png){width=50%}

[**Week 10: April 20, 2026**]{.underline} Within this session, we will expand on our growing understanding of GatingSets, functions and fcs file internals to write a script to downsample your fcs files to a desired number (or percentage) of cells for a given cell population. We will additionally learn how to concatenate these downsampled files together, and save them to a new .fcs file in ways that the metadata can be read by commercial software without the scaling being widely thrown off.
[**Week 10: April 20, 2026**]{.underline} Within this [tenth](/course/10_Downsampling/index.qmd) session, we will expand on our growing understanding of GatingSets, functions and fcs file internals to write a function to downsample your fcs files to a desired number (or percentage) of cells for a given cell population. We will additionally learn how to concatenate these downsampled files together, and save them to a new .fcs file in ways that the metadata can be read by commercial software without the scaling being widely thrown off.

<br>
<br>
Expand All @@ -135,7 +135,7 @@ No class week of March 30, 2026. If you are attending the [ABRF conference](http

![](images/TidyData.png){width=33%}

[**Week 11: April 27, 2026**]{.underline} Leveraging the increased familiarity working with the various packages this far in the course, in this session we will retrieve summary statistics for the gates within our GatingSet, and programmatically derrive out tidy data.frames for use in statistical analyses typically used by many Immunologist. In the process, we add a couple additional plot types to our ggplot2 arsenal to hold in reserve should Prism prices go up again.
[**Week 11: April 27, 2026**]{.underline} Leveraging the increased familiarity working with the various packages this far in the course, in this [eleventh](/course/11_DataForStats/index.qmd) session we will retrieve summary statistics for the gates within our GatingSet, and programmatically derrive out tidy data.frames for use in statistical analyses typically used by many Immunologist. In the process, we add a couple additional plot types to our ggplot2 arsenal to hold in reserve should Prism prices go up again.

<br>
<br>
Expand Down
8 changes: 7 additions & 1 deletion _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ project:
- 07_*
- 08_*
- 09_*
- 10_*
- 11_*
website:
google-analytics: "G-LZ35J3XE4D"
announcement:
Expand Down Expand Up @@ -96,7 +98,11 @@ website:
- text: "09 - It's Raining Functions!"
href: course/09_Functions/index.qmd
- section: "Cytometry Core"
href: Schedule.qmd
contents:
- text: "10 - Downsampling and Concatenation"
href: course/10_Downsampling/index.qmd
- text: "11 - Data for Statistics"
href: course/11_DataForStats/index.qmd
- section: "Beyond the Sandbox"
href: Schedule.qmd
- section: "The World is your Oyster"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
---
title: "Homework week 09"
author: "Alexandra Gabriela Ferreira"
date: 2026/05/23
format: html
toc: true
toc-depth: 4
---


# Problem 1

"Modify one of the simpler functions (SecondFunction or similar), provide your own argument names, and modify the `message()`, `paste0` or `print()` functions to print a text style output. Generate a small vector of values, and iterate through your vector using one of the approaches we used."


### Solution

```{r}
#| eval: FALSE

best_instrument = function(best_instrument){

Message = paste0("The best instrument is a(n) ", best_instrument, "!")
Message
}
```

#### creating the vector

```{r}
#| eval: FALSE
instruments =
c("Flow Cytometer", "Fluorescence Microscope", "Automated Cell Counter", "pH meter", "Illumina miSeq")

instruments
```

Let's try this:

```{r}
#| eval: FALSE
output = map(.x=instruments, .f = best_instrument)
output
```

The result:

![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem1_output.PNG)



# Problem 2

"Using the initial framework for the CellConcentration function, retrieve several other keywords that are of interest, and incorporate them into the returned data.frame row."


### Solution

*Note* it is assumed that until this point I:

1. activated necessary libraries;
2. loaded necessary `.fcs` files:
i) got storage location path;
ii) got full path names for the files;
3. Added files to a GatingSet:
i) loaded files to a cytoset 1st;
ii) loaded cytoset into a gatingset;
4. Applied a transformation to the gatingset:
i) attributed the desired transformation to an object;
ii) got the parameters list from the gatingset (`colnames()`);
iii) used `transformerList()` to apply the transformation object to the parameters being transformed;
5. *optional for this exercise* created gates.


```{r}
#| eval: FALSE


#' this function will retrieve important keywords
#' for a given gated population in an .fcs file
#'
#' @param x A gatingset object (typically iterated in)

#' this function will figure out the cell concentration
#' for a given gated population in an .fcs file
#'
#' @param x A gatingset object (typically iterated in)
#' @param subset The gate from which to retrieve cell counts from
#' @param dilution_factor takes the initial dilution factor

cell_concentration = function(x, subset, dilution_factor){
Date = keyword(x)$`$DATE` # Day-Month-Year
InstrumentSerial = keyword(x)$`$CYTSN`
TotalFileEvents = keyword(x)$`$TOT`
Specimen = keyword(x)$GUID
StartTime = keyword(x)$`$BTIM`
EndTime = keyword(x)$`$ETIM`

treshold = keyword(x)$THRESHOLD

Volume = keyword(x)$`$VOL`
Volume = as.numeric(Volume)

EventsInTheGate = gs_pop_get_data(x, subset)
Cells = nrow(EventsInTheGate)[[1]] #[[1]] since the output is in a list

Concentration = (Cells*1000)/Volume
Concentration = Concentration*dilution_factor

Data = data.frame(
Date=Date,
InstrumentSerial=InstrumentSerial,
Specimen=Specimen,
StartTime=StartTime,
EndTime=EndTime,
Volume=Volume,
Cells=Cells,
Concentration=Concentration,
Treshold=treshold
)
return(Data)
}

dataset = map(.x=my_gatingset, .f=cell_concentration, subset ="CD19", dilution_factor = 100) |> bind_rows()

dataset
```

This is what the new data set looks like.


![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem2_output.PNG)

# Problem 3

"For CellConcentration, we retrieved both start and end time. Look up information on lubridate package, convert these times to a time-style format, and from acquired volume derive uL/min at which each .fcs file was acquired. Did this vary at all across days?"


### Solution

I will use the dataset from problem 2 to solve this problem.
I will first activate the lubridate library and check its documentation.

```{r}
#| eval: FALSE
library(lubridate)
?lubridate
```

From the documentation I see there's a function called: `hms()` that transforms a character or numeric vector into a period object with specified hours, minutes and seconds.

We can change StartTime and EndTime columns to be transformed into this kind of objects.

Let's see first what kind of objects are inside these columns:

```{r}
#| eval: FALSE
str(dataset)
```

![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem3_classobject.PNG)

we can see from the printscreen that EndTime and StartTime are character objects.

let's change these columns to an appropriate class of objects.
creating a new dataset, changing the columns, and checking if the changes are applied:

```{r}
#| eval: FALSE
dataset_test = dataset |> mutate(StartTime = as.duration(hms(StartTime)), EndTime = as.duration(hms(EndTime)))
dataset_test

str(dataset_test)
```

I changed the object type for start time and end time columns from characters to period objects followed by the actual time in seconds with `as.duration()`

![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem3_classobject_changed.PNG)´

![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem3_totaltime.PNG)

So now, I want to return how much time elapsed between start time and end time. Lubridate has 2 functions:
1. `interval()` which creates an interval object with the specified start and end dates/times..
2. `as.duration()` will provide the exact duration in the interval created with `interval()`
so taking the dataset_test we created we will add the duration in seconds for each run.

```{r}
#| eval: FALSE

dataset_test = dataset_test |> mutate(Time_elapsed = as.duration(interval(StartTime, EndTime)))

dataset_test
```

![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem3_time_elapsed.PNG)


To calculate uL/min I just create a new column with:

$$speed_acquisition = (Volume/Time_elapsed)*60$$


```{r}
#| eval: FALSE

dataset_test = dataset_test |> mutate(Speed_acquisition = (Volume/as.numeric(Time_elapsed)*60))

dataset_test
```


![](/course/09_Functions/homeworks/alexandragabrielaferreira/images/problem3_speed_acquisition.PNG)

I calculated the speed of acquisition in uL/min and we see that it was not the same everyday.
Loading