The data presented are from manual cell counts using a haemocytometer. Primary muscle satellite cells were isolated from Ruffs (Philomachus pugnax), Sanderlings (Calidris alba), Western Sandpipers (Calidris mauri) Swainson’s Thrushes (Catharus ustulatus), and Yellow-rumped Warblers (Setophaga coronata). Initially 20,000 cells were plated into 35 mm wells and counted at the timepoints shown below.
## Load packages
library(ggplot2)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(growthrates)
## Loading required package: lattice
## Loading required package: deSolve
## Standard error function
se_calc = function(x){
sqrt(var(x)/length(x))
}
#### Import and Organize Data ####
##################################################################.
counts <- read.csv("all_species_counts.csv") %>%
## Calculate cells/mL and cell total
mutate(cellsml = count*(Dilution/squares_counted)*10000 ) %>%
mutate(celltotal = cellsml*volume_ml)
## Summarise technical replicates and remove unused columns
counts <- counts %>%
suppressMessages() %>%
group_by(t, id, bFGF, sp) %>%
summarise(total = mean(celltotal) ) %>%
## Add cells/cm^2 ---> 35mm dish == 9cm^2
mutate(cell_area = total/9,
log_total = log10(total),
log_cell_area = log10(total/9)) %>%
as.data.frame()
## `summarise()` regrouping output by 't', 'id', 'bFGF' (override with `.groups` argument)
## Summarise data by species
counts_sum <- counts %>%
suppressMessages() %>%
group_by(t, sp, bFGF) %>%
summarise(mean_total = mean(total),
sd_total = sd(total),
se_total = se_calc(total),
mean_area = mean(cell_area),
sd_area = sd(cell_area),
se_area = se_calc(cell_area),
mean_log_total = mean(log_total),
sd_log_total = sd(log_total),
se_log_total = se_calc(log_total),
mean_log_area = mean(log_cell_area),
sd_log_area = sd(log_cell_area),
se_log_area = se_calc(log_cell_area)) %>%
as.data.frame()
## `summarise()` regrouping output by 't', 'sp' (override with `.groups` argument)
Calculate doubling times
## Create a column to combine id, bfgf and sp identifiers
counts <- counts %>%
mutate(name = paste(id, bFGF, sp, sep = "_"))
## generate a list with each item containing a unique identifier
counts_list <- replicate(length(unique(counts$name)), counts, simplify = FALSE)
## Assign the unique identifier to each list name
id_bfgf_sp <- unique(counts$name)
names(counts_list) <- id_bfgf_sp
## Filter each item in list to contain only data matched by the named identifier
for(i in 1:length(counts_list)){
counts_list[[i]] = counts_list[[i]] %>%
filter(name == id_bfgf_sp[i])
}
## Calculate splines for each
counts_spline <- counts_list
counts_spline_dt <- counts_list
for(i in 1:length(counts_list)){
counts_spline[[i]] <- fit_easylinear(time = counts_list[[i]]$t,
y = counts_list[[i]]$total,
h = 4)
counts_spline_dt[[i]] <- log(2)/coef(counts_spline[[i]])[[3]]
}
## collapse doubling times together
spline_dt <- bind_rows(counts_spline_dt, .id = "names") %>%
t()
spline_dt <- cbind(spline_dt, rownames(spline_dt)) %>%
as.data.frame()
## Change column names
colnames(spline_dt) <- c("dt", "id_bfgf_sp")
## Convert dt column to a numeric and create empty columns to fill
spline_dt <- spline_dt %>%
mutate(dt = as.numeric(dt),
id = "",
bfgf = "",
sp = "")
## Replace each identifier column with real values from id_bfgf_sp
for(i in 1:length(spline_dt[,1])){
spline_dt$id[i] <- strsplit(spline_dt$id_bfgf_sp[i], "_")[[1]][1]
spline_dt$bfgf[i] <- strsplit(spline_dt$id_bfgf_sp[i], "_")[[1]][2]
spline_dt$sp[i] <- strsplit(spline_dt$id_bfgf_sp[i], "_")[[1]][3]
}
mumax is the maximum growth rate -> convert this to doubling time for comparison
growth rate k is reciprocal to time (usually hr^-1) k = ln(N2/N1)/(t2-t1) … where N is the concentration of cells, and t is time
\[ dt = T*ln(2)/ln(Xe/Xb) \] – dt also referred to as g Where Xb = the first timepoint, Xe = the second timepoint, and T = the total time between the two timepoints.
dt = ln2/k
## Summary table
spline_dt_sum <- spline_dt %>%
group_by(sp, bfgf) %>%
summarise(mean_dt = mean(dt), sd_dt = sd(dt), se_dt = se_calc(dt))
## `summarise()` regrouping output by 'sp' (override with `.groups` argument)
## Initial plot of doubling times by species and bFGF treatment
ggplot(spline_dt, aes(x = sp, y = dt, colour = bfgf)) + geom_boxplot()
## Models
lm1.1 <- aov(dt ~ sp*bfgf, data = spline_dt)
anova(lm1.1)
## Analysis of Variance Table
##
## Response: dt
## Df Sum Sq Mean Sq F value Pr(>F)
## sp 4 645715 161429 41.836 7.342e-12 ***
## bfgf 1 76653 76653 19.865 0.0001074 ***
## sp:bfgf 4 614562 153641 39.818 1.364e-11 ***
## Residuals 30 115758 3859
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Check model assumptions
spline_dt_res1.1 <- spline_dt %>%
mutate(stres = rstandard(lm1.1) )
## Mean residuals approximate 0
ggplot(spline_dt_res1.1, aes(x = sp, y = stres, fill = bfgf), xlab = "Species", ylab = "Standardized Residuals") + geom_boxplot() + theme_classic() + geom_hline(yintercept = 0)
## qqplot to check normality of residuals
qplot(data = spline_dt_res1.1, sample = stres, geom = "qq", xlab = "Theoretical Quantile", ylab = "Sample Quantile") + geom_abline(intercept = 0, slope = 1)
Log-transform doubling times to fix model fit.
## Log transform doubling time
spline_dt <- spline_dt %>%
mutate(log_dt = log(dt) )
## Models
lm1.2 <- aov(dt ~ sp*bfgf, data = spline_dt)
anova(lm1.2)
## Analysis of Variance Table
##
## Response: dt
## Df Sum Sq Mean Sq F value Pr(>F)
## sp 4 645715 161429 41.836 7.342e-12 ***
## bfgf 1 76653 76653 19.865 0.0001074 ***
## sp:bfgf 4 614562 153641 39.818 1.364e-11 ***
## Residuals 30 115758 3859
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Check model assumptions
spline_dt_res1.2 <- spline_dt %>%
mutate(stres = rstandard(lm1.2) )
## Mean residuals approximate 0
ggplot(spline_dt_res1.2, aes(x = sp, y = stres, fill = bfgf), xlab = "Species", ylab = "Standardized Residuals") + geom_boxplot() + theme_classic() + geom_hline(yintercept = 0)
## qqplot to check normality of residuals
qplot(data = spline_dt_res1.2, sample = stres, geom = "qq", xlab = "Theoretical Quantile", ylab = "Sample Quantile") + geom_abline(intercept = 0, slope = 1)
This model isn’t valid because of an extreme outlier. SWTH grown without bFGF did not reach exponential growth.
Remove outlier and reproduce anova.
no_swth_spline_dt <- spline_dt %>%
filter(id_bfgf_sp != "A_N_SWTH", id_bfgf_sp != "B_N_SWTH")
## No swth
lm2.1 <- aov(dt ~ sp*bfgf, data = no_swth_spline_dt)
summary(lm2.1)
## Df Sum Sq Mean Sq F value Pr(>F)
## sp 4 2548.6 637.1 20.634 3.86e-08 ***
## bfgf 1 218.4 218.4 7.072 0.0126 *
## sp:bfgf 3 133.6 44.5 1.442 0.2508
## Residuals 29 895.5 30.9
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Check model assumptions
spline_dt_res2.1 <- no_swth_spline_dt %>%
mutate(stres = rstandard(lm2.1) )
## Mean residuals approximate 0
ggplot(spline_dt_res2.1, aes(x = sp, y = stres, fill = bfgf)) + geom_boxplot() +
theme_classic() + geom_hline(yintercept = 0)
## qqplot to check normality of residuals
qplot(data = spline_dt_res2.1, sample = stres, geom = "qq", xlab = "Theoretical Quantile", ylab = "Sample Quantile") + geom_abline(intercept = 0, slope = 1)
Interaction is not significant here, remove it from anova
lm2.2 <- aov(dt ~ sp + bfgf, data = no_swth_spline_dt)
summary(lm2.2)
## Df Sum Sq Mean Sq F value Pr(>F)
## sp 4 2548.6 637.1 19.813 2.72e-08 ***
## bfgf 1 218.4 218.4 6.791 0.0138 *
## Residuals 32 1029.1 32.2
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Check model assumptions
spline_dt_res2.2 <- no_swth_spline_dt %>%
mutate(stres = rstandard(lm2.2) )
## Mean residuals approximate 0
ggplot(spline_dt_res2.2, aes(x = sp, y = stres, fill = bfgf)) + geom_boxplot() +
theme_classic() + geom_hline(yintercept = 0)
## qqplot to check normality of residuals
qplot(data = spline_dt_res2.2, sample = stres, geom = "qq", xlab = "Theoretical Quantile", ylab = "Sample Quantile") + geom_abline(intercept = 0, slope = 1)
Using Tukey’s honest significant differences for multiple comparisons
lm2.2_tukey <- TukeyHSD(lm2.2)
lm2.2_tukey
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = dt ~ sp + bfgf, data = no_swth_spline_dt)
##
## $sp
## diff lwr upr p adj
## SAND-RUFF 1.420447 -7.040850 9.881744 0.9882029
## SWTH-RUFF 16.741527 4.049581 29.433473 0.0050122
## WESA-RUFF 15.082128 7.754429 22.409826 0.0000120
## YRWA-RUFF 18.704958 11.377260 26.032657 0.0000002
## SWTH-SAND 15.321080 1.942595 28.699566 0.0183520
## WESA-SAND 13.661681 5.200384 22.122978 0.0004738
## YRWA-SAND 17.284512 8.823214 25.745809 0.0000136
## WESA-SWTH -1.659399 -14.351345 11.032547 0.9954484
## YRWA-SWTH 1.963431 -10.728514 14.655377 0.9913371
## YRWA-WESA 3.622831 -3.704868 10.950529 0.6144218
##
## $bfgf
## diff lwr upr p adj
## Y-N -4.679661 -8.432527 -0.9267952 0.0161412
spline_dt_sum
## # A tibble: 10 x 5
## # Groups: sp [5]
## sp bfgf mean_dt sd_dt se_dt
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 RUFF N 9.27 1.31 0.585
## 2 RUFF Y 7.88 1.18 0.528
## 3 SAND N 11.4 2.03 1.17
## 4 SAND Y 8.59 0.902 0.521
## 5 SWTH N 856. 339. 240.
## 6 SWTH Y 25.3 2.29 1.62
## 7 WESA N 29.1 8.90 3.98
## 8 WESA Y 18.2 2.79 1.25
## 9 YRWA N 29.1 9.94 4.45
## 10 YRWA Y 25.4 5.59 2.50
Produce final plot of doubling times
## Generate label list for showing similarities/differences among species in boxplots
## Grouping label including only one label per species to conform to the plot - this produces missing values warning in the final plot but successfully produces the differences labels.
label_list <- c(
c("A", rep(NA, 9)), # RUFF
c("B", rep(NA, 7)), # WESA
c("A", rep(NA, 7)), # SAND
c("B", rep(NA, 7)), # YRWA
c("B", rep(NA, 3)) # SWTH
)
## Position for label
label_pos_list <- c(
c(46, rep(NA, 9)), # RUFF
c(46, rep(NA, 7)), # WESA
c(46, rep(NA, 7)), # SAND
c(46, rep(NA, 7)), # YRWA
c(46, rep(NA, 3)) # SWTH
)
## Add labels to dataframe
spline_plot_dt <- no_swth_spline_dt %>%
cbind(label_list, label_pos_list)
## Colours to include in plot
bwcolors = c("gray50", "gray15")
noswth_plot <- ggplot(spline_plot_dt, aes(x = sp, y = dt, fill = bfgf)) +
geom_boxplot() + guides(fill = FALSE) +
theme_classic() +
theme(text = element_text(size = 20)) +
labs(x = "Species", y = "Doubling Time (hrs)") +
scale_fill_manual(values = bwcolors) +
scale_y_continuous(breaks = seq(5,45,5), limits = c(5,46)) +
geom_text(data = spline_plot_dt, aes(x = sp, y = label_pos_list, label = label_list), size = 7)
noswth_plot
## Warning: Removed 33 rows containing missing values (geom_text).
## Save plot
#ggsave("Doubling_Time.png", plot = noswth_plot, device = "png", width = 12, height = 9.3, dpi = 300)