Skip to content

Grouped Summaries

The most common question asked of a dataset is “what is the average per group?” Mean expression per gene, counts per condition, a standard deviation per batch. Both dplyr and Polars answer it the same way: split the rows into groups, then compute one or more summaries per group. dplyr calls the second step summarise; Polars calls it agg.

Every code block runs in a container in the companion repository, on the same shared counts table used across this section. The two languages agree on every number. They print the groups in a different order, dplyr sorted by the grouping key and Polars in the order the groups first appear, which is expected and harmless.

Mean counts per gene.

library(dplyr)
expr |>
group_by(gene) |>
summarise(mean_counts = mean(counts), .groups = "drop")
# A tibble: 6 × 2
gene mean_counts
<chr> <dbl>
1 ACTB 3881.
2 BRCA1 182.
3 EGFR 513.
4 GAPDH 4028.
5 MYC 502.
6 TP53 504.

The tibble rounds the means for display; the values are the same. Polars keeps the groups in first-seen order, so TP53 leads.

Mean, standard deviation, and group size together. In dplyr each is a named argument to summarise; in Polars each is an expression in agg.

expr |>
group_by(gene) |>
summarise(mean_counts = mean(counts),
sd_counts = sd(counts),
n = n(),
.groups = "drop")
# A tibble: 6 × 4
gene mean_counts sd_counts n
<chr> <dbl> <dbl> <int>
1 ACTB 3881. 476. 8
2 BRCA1 182. 20.6 8
3 EGFR 513. 241. 8
4 GAPDH 4028. 262. 8
5 MYC 502. 291. 8
6 TP53 504. 101. 8

Both use the sample standard deviation (divides by n - 1), so sd_counts matches to full precision.

Grouping by gene and condition gives the treated versus control mean for each gene, which is the shape of a differential-expression summary.

expr |>
group_by(gene, condition) |>
summarise(mean_counts = mean(counts), .groups = "drop") |>
arrange(gene, condition)
# A tibble: 12 × 3
gene condition mean_counts
<chr> <chr> <dbl>
1 ACTB control 3968.
2 ACTB treated 3794.
3 BRCA1 control 171.
4 BRCA1 treated 192
5 EGFR control 296
6 EGFR treated 730.
7 GAPDH control 4005.
8 GAPDH treated 4052.
9 MYC control 246
10 MYC treated 757.
11 TP53 control 411.
12 TP53 treated 598.

EGFR jumps from a control mean near 296 to a treated mean near 730, and MYC from 246 to 757. Those are the growth genes the fixture was built to move.

A grouped count needs no value column, just the group sizes.

count(expr, condition)
condition n
1 control 24
2 treated 24

Twenty-four measurements per condition: four samples times six genes. Next, Joins attach gene metadata to these summaries.