This code analyzes data from male and female Endler guppies exposed to cues of risk (predator cue + alarm cue) in the presence or absence of social groups. Individuals assigned to the “Alone” treatment were placed in a tank next to an empty treatment tank. Individuals assigned to the “Social” treatment were placed in a tank next to a treatment tank with 3 unfamilar same-sex conspecifics. Individuals were recored for 10 minutes before risk cues were elicited, with minutes 5-10 being considered “baseline” and further recorded for an additional 35 minutes (encompassing Acute-Trial30 stages, 0-2100 seconds) after receipt of cues of risk (0.75 mL of fresh predator cue, 0.75 mL of alarm cue).

This code specifically analyzes cortisol release rate collected from water. Cortisol was collected two hours before pre receipt of risk cue before the experiment began and 35 minutes post receipt of risk cues.

##Clear enviroment

rm(list=ls())

#Load necessary libraries

#load necessary libraries
library(librarian)
shelf(tidyverse, magrittr, lmerTest, ggpubr, MuMIn, emmeans, rstatix, sjPlot, lsr,car,patchwork)
## 
##   The 'cran_repo' argument in shelf() was not set, so it will use
##   cran_repo = 'https://cran.r-project.org' by default.
## 
##   To avoid this message, set the 'cran_repo' argument to a CRAN
##   mirror URL (see https://cran.r-project.org/mirrors.html) or set
##   'quiet = TRUE'.

#Reading in data file

cortData <- read.csv("CortisolMasterSheetSB.csv")

#Checking structure and turning characters into factors as necessary for factor analysis

str(cortData)
## 'data.frame':    122 obs. of  12 variables:
##  $ FishID        : int  1 1 2 2 3 3 4 4 5 5 ...
##  $ SampleID      : int  63 132 60 125 102 114 19 133 43 123 ...
##  $ Sex           : chr  "F" "F" "F" "F" ...
##  $ Treatment     : chr  "Social" "Social" "Control" "Control" ...
##  $ Stage         : chr  "Post" "Pre" "Post" "Pre" ...
##  $ Dilution      : int  50 50 50 50 50 50 50 50 50 50 ...
##  $ GroupID       : int  1 1 1 1 1 1 1 1 10 10 ...
##  $ Cortisol_Elisa: num  190 747 146 943 341 ...
##  $ Plate         : int  19 6 11 10 12 15 19 13 18 12 ...
##  $ Weight        : num  0.29 0.23 0.13 0.17 0.14 0.15 0.17 0.21 0.09 0.07 ...
##  $ Length        : num  3.4 3.4 2.5 2.5 2.1 2.1 2.2 2.2 NA NA ...
##  $ Depth         : num  0.5 0.5 0.3 0.3 0.3 0.3 0.3 0.3 NA NA ...
#Converting all characters to factors

cortData %<>%
  mutate(across(where(is_character), as_factor)) -> cortData  ##looks for columns that are seen as characters and turning them into factors

str(cortData)
## 'data.frame':    122 obs. of  12 variables:
##  $ FishID        : int  1 1 2 2 3 3 4 4 5 5 ...
##  $ SampleID      : int  63 132 60 125 102 114 19 133 43 123 ...
##  $ Sex           : Factor w/ 2 levels "F","M": 1 1 1 1 1 1 1 1 2 2 ...
##  $ Treatment     : Factor w/ 2 levels "Social","Control": 1 1 2 2 2 2 1 1 2 2 ...
##  $ Stage         : Factor w/ 2 levels "Post","Pre": 1 2 1 2 1 2 1 2 1 2 ...
##  $ Dilution      : int  50 50 50 50 50 50 50 50 50 50 ...
##  $ GroupID       : int  1 1 1 1 1 1 1 1 10 10 ...
##  $ Cortisol_Elisa: num  190 747 146 943 341 ...
##  $ Plate         : int  19 6 11 10 12 15 19 13 18 12 ...
##  $ Weight        : num  0.29 0.23 0.13 0.17 0.14 0.15 0.17 0.21 0.09 0.07 ...
##  $ Length        : num  3.4 3.4 2.5 2.5 2.1 2.1 2.2 2.2 NA NA ...
##  $ Depth         : num  0.5 0.5 0.3 0.3 0.3 0.3 0.3 0.3 NA NA ...

#Turing specific numerical/integers into factors

#We also want FishID, GroupID, and Plate to be factors

cortData %>%
  mutate(FishID = as.factor(FishID)) -> cortData

cortData %>%
  mutate(GroupID = as.factor(GroupID)) -> cortData

cortData %>%
  mutate(Plate = as.factor(Plate)) -> cortData

str(cortData)
## 'data.frame':    122 obs. of  12 variables:
##  $ FishID        : Factor w/ 62 levels "1","2","3","4",..: 1 1 2 2 3 3 4 4 5 5 ...
##  $ SampleID      : int  63 132 60 125 102 114 19 133 43 123 ...
##  $ Sex           : Factor w/ 2 levels "F","M": 1 1 1 1 1 1 1 1 2 2 ...
##  $ Treatment     : Factor w/ 2 levels "Social","Control": 1 1 2 2 2 2 1 1 2 2 ...
##  $ Stage         : Factor w/ 2 levels "Post","Pre": 1 2 1 2 1 2 1 2 1 2 ...
##  $ Dilution      : int  50 50 50 50 50 50 50 50 50 50 ...
##  $ GroupID       : Factor w/ 18 levels "1","3","4","5",..: 1 1 1 1 1 1 1 1 9 9 ...
##  $ Cortisol_Elisa: num  190 747 146 943 341 ...
##  $ Plate         : Factor w/ 17 levels "2","3","4","5",..: 17 5 9 8 10 13 17 11 16 10 ...
##  $ Weight        : num  0.29 0.23 0.13 0.17 0.14 0.15 0.17 0.21 0.09 0.07 ...
##  $ Length        : num  3.4 3.4 2.5 2.5 2.1 2.1 2.2 2.2 NA NA ...
##  $ Depth         : num  0.5 0.5 0.3 0.3 0.3 0.3 0.3 0.3 NA NA ...

#Measurment Transformation

#Transforming the cortisol measurement from pg/mL to ng/dl

cortData %>%
  mutate(cortNG = (Cortisol_Elisa/1000)) -> cortData

#Creating the column to report cortisol by weight

cortData %>%
  mutate(cortWeight = (cortNG/Weight)) -> cortData

#Creating the column to report cortisol by weight by minute

cortData %>%
  mutate(cortFinal = (cortWeight/30)) -> cortData


#Putting all factors in the order we want and renaming if necessary
levels(cortData$Treatment) <- list(Alone = "Control", Social = "Social")

levels(cortData$Sex) <- list(female = "F", male = "M")

#levels(cortData$Stage) <- list(Pre = "Pre", Post = "Post")



str(cortData)
## 'data.frame':    122 obs. of  15 variables:
##  $ FishID        : Factor w/ 62 levels "1","2","3","4",..: 1 1 2 2 3 3 4 4 5 5 ...
##  $ SampleID      : int  63 132 60 125 102 114 19 133 43 123 ...
##  $ Sex           : Factor w/ 2 levels "female","male": 1 1 1 1 1 1 1 1 2 2 ...
##  $ Treatment     : Factor w/ 2 levels "Alone","Social": 2 2 1 1 1 1 2 2 1 1 ...
##  $ Stage         : Factor w/ 2 levels "Post","Pre": 1 2 1 2 1 2 1 2 1 2 ...
##  $ Dilution      : int  50 50 50 50 50 50 50 50 50 50 ...
##  $ GroupID       : Factor w/ 18 levels "1","3","4","5",..: 1 1 1 1 1 1 1 1 9 9 ...
##  $ Cortisol_Elisa: num  190 747 146 943 341 ...
##  $ Plate         : Factor w/ 17 levels "2","3","4","5",..: 17 5 9 8 10 13 17 11 16 10 ...
##  $ Weight        : num  0.29 0.23 0.13 0.17 0.14 0.15 0.17 0.21 0.09 0.07 ...
##  $ Length        : num  3.4 3.4 2.5 2.5 2.1 2.1 2.2 2.2 NA NA ...
##  $ Depth         : num  0.5 0.5 0.3 0.3 0.3 0.3 0.3 0.3 NA NA ...
##  $ cortNG        : num  0.19 0.747 0.146 0.943 0.341 ...
##  $ cortWeight    : num  0.655 3.248 1.127 5.549 2.433 ...
##  $ cortFinal     : num  0.0218 0.1083 0.0376 0.185 0.0811 ...

#Standard Error Formual Input and obtain sample sizes

#Standard error formula:

se <- function(x) sd(x, na.rm=T)/sqrt(length(x))

#Sample sizes

tapply(cortData$FishID, list(cortData$Treatment, cortData$Stage, cortData$Sex), length)
## , , female
## 
##        Post Pre
## Alone    15  16
## Social   16  16
## 
## , , male
## 
##        Post Pre
## Alone    15  15
## Social   15  14

#Filter out single high cort data male

dim(cortData)
## [1] 122  15
cortData %>%
  filter(cortFinal < 1) -> cortData

dim(cortData)
## [1] 121  15

Analysis of the stages seperately

the sexes

#First check for outliers

#Create column with difference of after cue - before cue to control for individual starting cortisol

cortData %>% 
  pivot_wider(id_cols=c(Treatment, Sex, FishID, GroupID, Plate),
        names_from = Stage,
        values_from = cortFinal) -> cortDatNew



cortData %>% 
  pivot_wider(id_cols=c(Treatment, Sex, FishID, GroupID),
        names_from = Stage,
        values_from = cortFinal) %>%
  mutate(difference =(Post - Pre)) -> cortDatDiff


#cortData %>%
 # mutate(cortFinal = (cortWeight/30)) -> cortData
#before cue
#after cue
#cortFinal 
list_quantiles <- tapply(cortDatDiff$difference, cortDatDiff$Sex, quantile, na.rm = TRUE)

dim(cortDatDiff)
## [1] 62  7
Q1s <- sapply(1:2, function(i) list_quantiles[[i]][2])
Q3s <- sapply(1:2, function(i) list_quantiles[[i]][4])

IQRs <- tapply(cortDatDiff$difference, cortDatDiff$Sex, IQR, na.rm = TRUE)
 
Lowers <- Q1s - 1.5*IQRs
Uppers <- Q3s + 1.5*IQRs
 
datas <- split(cortDatDiff, cortDatDiff$Sex)
 
data_no_outlierCort <- NULL
for (i in 1:2){
out <- subset(datas[[i]], datas[[i]]$difference > Lowers[i] & datas[[i]]$difference < Uppers[i])
data_no_outlierCort <- rbind(data_no_outlierCort, out)
}
 
dim(data_no_outlierCort) #six outliers removed
## [1] 56  7

Analysis of sex differences in pre-cue and in post-cue cortisol release rate, regardless of treatment

cort.lmm <- lmer(Pre~Sex*Treatment + 
                     + (1|GroupID) + (1|Plate), data = cortDatNew)
## boundary (singular) fit: see help('isSingular')
#Check residuals for normality

resid(cort.lmm) -> cortResids
hist(cortResids)

ggdensity(cortResids,
          main="Density plot of residuals",
          xlab="Residuals")

ggqqplot(cortResids)

shapiro.test(cortResids)
## 
##  Shapiro-Wilk normality test
## 
## data:  cortResids
## W = 0.9274, p-value = 0.001392
#Fits our criteria, let's check effects

anova(cort.lmm) 
## Type III Analysis of Variance Table with Satterthwaite's method
##                 Sum Sq  Mean Sq NumDF DenDF F value   Pr(>F)   
## Sex           0.144001 0.144001     1    57 10.3829 0.002104 **
## Treatment     0.005283 0.005283     1    57  0.3809 0.539575   
## Sex:Treatment 0.011494 0.011494     1    57  0.8288 0.366463   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
ranova(cort.lmm)
## boundary (singular) fit: see help('isSingular')
## boundary (singular) fit: see help('isSingular')
## ANOVA-like table for random-effects: Single term deletions
## 
## Model:
## Pre ~ Sex + Treatment + (1 | GroupID) + (1 | Plate) + Sex:Treatment
##               npar logLik   AIC LRT Df Pr(>Chisq)
## <none>           7   35.6 -57.2                  
## (1 | GroupID)    6   35.6 -59.2   0  1          1
## (1 | Plate)      6   35.6 -59.2   0  1          1
#Pulling out R2

r.squaredGLMM(cort.lmm)
##            R2m       R2c
## [1,] 0.1593917 0.1593917
## Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

##Analysis of post-cue cortisol release rate between sexes regardless of treatment with group ID as a random effect

cort.lmm <- lmer(Post~Sex*Treatment + (1|GroupID) + (1|Plate), data = cortDatNew)
#Check residuals for normality

resid(cort.lmm) -> cortResids
hist(cortResids)

ggdensity(cortResids,
          main="Density plot of residuals",
          xlab="Residuals")

ggqqplot(cortResids)

shapiro.test(cortResids)
## 
##  Shapiro-Wilk normality test
## 
## data:  cortResids
## W = 0.93512, p-value = 0.003296
#Fits our criteria, let's check effects

anova(cort.lmm) 
## Type III Analysis of Variance Table with Satterthwaite's method
##                 Sum Sq  Mean Sq NumDF  DenDF F value  Pr(>F)  
## Sex           0.093862 0.093862     1 10.203  8.2752 0.01616 *
## Treatment     0.001137 0.001137     1 27.755  0.1002 0.75394  
## Sex:Treatment 0.033163 0.033163     1 25.174  2.9238 0.09958 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#Means and se

tapply(cortData$cortFinal, list(cortData$Sex, cortData$Stage), mean)
##        before cue after cue
## female  0.1108598 0.1040713
## male    0.2073859 0.1852387
tapply(cortData$cortFinal, list(cortData$Sex, cortData$Stage), se)
##        before cue  after cue
## female 0.01497993 0.02175837
## male   0.02681746 0.02623432
ranova(cort.lmm)
## boundary (singular) fit: see help('isSingular')
## ANOVA-like table for random-effects: Single term deletions
## 
## Model:
## Post ~ Sex + Treatment + (1 | GroupID) + (1 | Plate) + Sex:Treatment
##               npar logLik     AIC     LRT Df Pr(>Chisq)
## <none>           7 29.416 -44.831                      
## (1 | GroupID)    6 29.412 -46.825 0.00610  1     0.9377
## (1 | Plate)      6 29.114 -46.228 0.60247  1     0.4376
#Pulling out R2

r.squaredGLMM(cort.lmm)
##             R2m       R2c
## [1,] 0.09818171 0.5569277

##Panel graphs

# panel <- (cortgraph2|cortgraph3) 
# #  plot_annotation(tag_levels = 'A') #add figure labels
#   panel

Analysis of difference of cortisol release rate (after cue - pre cue) between sex

cortNO.lmm <- lmer(difference~Treatment*Sex + (1|GroupID), data =cortDatDiff)
## boundary (singular) fit: see help('isSingular')
#Check residuals for normality

resid(cortNO.lmm) -> cortResidsNO
hist(cortResidsNO)

ggdensity(cortResidsNO,
          main="Density plot of residuals",
          xlab="Residuals")

ggqqplot(cortResidsNO)

shapiro.test(cortResidsNO)
## 
##  Shapiro-Wilk normality test
## 
## data:  cortResidsNO
## W = 0.94252, p-value = 0.007665

#Fits criteria

anova(cortNO.lmm)
## Type III Analysis of Variance Table with Satterthwaite's method
##                 Sum Sq  Mean Sq NumDF DenDF F value  Pr(>F)  
## Treatment     0.042809 0.042809     1    55  1.9290 0.17047  
## Sex           0.003065 0.003065     1    55  0.1381 0.71158  
## Treatment:Sex 0.071569 0.071569     1    55  3.2249 0.07802 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
ranova(cortNO.lmm)
## ANOVA-like table for random-effects: Single term deletions
## 
## Model:
## difference ~ Treatment + Sex + (1 | GroupID) + Treatment:Sex
##               npar logLik     AIC        LRT Df Pr(>Chisq)
## <none>           6 21.302 -30.604                         
## (1 | GroupID)    5 21.302 -32.604 7.1054e-15  1          1
r.squaredGLMM(cortNO.lmm)
##             R2m        R2c
## [1,] 0.07898577 0.07898577

#Boxplot graphing Difference in cortisol release rate after receipt of risk cues by Treatment and Sex #Fulldataset

#Boxplot graphing Difference in cortisol release rate after receipt of risk cues by Treatment and Sex with outliers taken out

## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 3 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).

## Warning: Removed 3 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).