Randomized clinical trial evaluating the practical and biological limits of colostral IgG dosing in dairy calves
Author
Dini Hapukotuwa and Sam Rowe
Published
December 22, 2025
Authors and affiliations
Dini Hapukotuwa1, John House1, Katie Denholm2, and Sam Rowe1
1Sydney School of Veterinary Science, University of Sydney, Camden 2750, NSW, Australia
2School of Biodiversity, One Health and Veterinary Medicine, University of Glasgow, Glasgow, Scotland
Acknowledgements
We would like to thank the participating farm, Anna Waldron, and Jennie Mohler
Study objectives
Determine the effect of oral IgG dose (200, 250, 300, 350g IgG) on serum IgG
A secondary objective was to determine if the effect of oral IgG on serum IgG is modified by other variables.
Determine the effect of oral IgG dose (200, 250, 300, 350g IgG) on apparent efficiency of absorption
To identify the practically feasible dose of colostral IgG when considering IgG supply (i.e., IgG harvested and stored), demand (number of calves to be fed), and practical constraints on dose (colostral IgG concentration and volume limits per calf)
Identify if volume impacts IgG independently of total dose administered
Causal diagram (DAG)
Based on this causal diagram, colostrum IgG concentration and volume should be included as covariates as they could provide a backdoor path (confounding bias). Other variables do not need to be included.
The number of excluded animals is low and similar between each group. This indicates that there is less bias between groups.
There is a higher proportion of Angus in the 200g group compared to the other groups. This may create bias if Angus was different in their response to oral IgG. When results were adjusted for breed below, it was found that there was no significant difference between both, and therefore there is no bias in the numbers above.
Comparison of treatment groups after exclusions, losses to follow-up and calves changing treatment group (n = 6)
Show the code
data <- data %>%mutate(originally_assiged_to_other_dose =case_when( tx_group == tx_assigned ~0, T ~1 ) %>% as_factor)data <- data %>%filter(excluded==0, lost_to_follow_up ==0,!is.na(tx_group))
Objective 1 - Effect of oral IgG on serum IgG in Holstein calves
Plots
Box and whisker plot showing serum IgG for calves randomized to 4 doses of colostrum.
Show the code
figure <-ggplot(data %>%filter(breed =="Holstein"), aes(x = tx_group, y = igg)) +# Removed fill from aes()# Add shaded regionsgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10), fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18), fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25), fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf), fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Add boxplot with fixed fill colorgeom_boxplot(fill ="white", # Set all boxplots to light blueoutlier.shape =16, outlier.size =2, alpha =1) +# Labelslabs(title ="",x ="Oral IgG dose",y ="Serum IgG (g/L)",fill ="Treatment" ) +# Custom themetheme_minimal() +coord_cartesian(ylim =c(0, 60)) +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure
Show the code
ggsave(filename ="figure 1.tiff", # File nameplot = figure, # ggplot object to savedevice ="tiff", # Output formatwidth =4, # Width in inchesheight =4, # Height in inchesdpi =200# Resolution in dots per inch)
This box and whisker plot is comparing the IgG levels across treatment groups (250, 300 and 350g), relative to baseline (200g) for Holstein animals.
There appears to be a dose dependent effect of oral IgG on serum IgG in Holstein calves.
Show the code
prop_data <- data %>%filter(breed =="Holstein") %>%filter(!is.na(tx_group), !is.na(igg_category)) %>%group_by(tx_group, igg_category) %>%summarize(count =n(), .groups ="drop") %>%group_by(tx_group) %>%mutate(proportion = count /sum(count))figure <-ggplot(prop_data, aes(x = tx_group, y = proportion, fill = igg_category)) +geom_bar(stat ="identity", position ="stack") +# Add text labels for proportionsgeom_text(aes(label = scales::percent(proportion, accuracy =1)), # Format proportion as percentageposition =position_stack(vjust =0.5), # Place text in the middle of the stacked barssize =3, # Adjust text sizefamily ="Times New Roman"# Ensure consistent font ) +labs(title ="",x ="Oral IgG dose",y ="Proportion",fill ="IgG Levels" ) +scale_y_continuous(labels = scales::percent) +# Show percentagestheme_minimal() +scale_fill_manual(values =c('#A2D9CE', '#C3D69B', '#FFF2CC', "pink")) +theme(panel.border =element_rect(color ="black", fill =NA, linewidth =1),text =element_text(family ="Times New Roman") )figure
Show the code
ggsave(filename ="figure 2.tiff", # File nameplot = figure, # ggplot object to savedevice ="tiff", # Output formatwidth =6, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)
This graph shows the proportion of Holstein calves from each dose group that fall into the new proposed categories by Godden and colleagues (2019) for passive immunity in calves.
Lombard and collaborators (2020) proposed that >40% calves should be able to achieve an excellent level of passive immunity when given 300g if feeding once.
It is evident from this graph that this is also possible at a dose of 250g as 75% Holstein calves in the study were able to achieve a serum IgG >=25g/L IgG.
Statistical models
Main effects linear regression
Model includes concentration and volume as covariates (multi-colinearity. model rejected)
OK: No outliers detected.
- Based on the following method and threshold: cook (0.8).
- For variable: (Whole model)
Show the code
check_collinearity(lm)
Term
VIF
VIF_CI_low
VIF_CI_high
SE_factor
Tolerance
Tolerance_CI_low
Tolerance_CI_high
tx_group
82.54447
68.10437
100.09191
2.086642
0.0121147
0.0099908
0.0146833
concentration
37.91441
31.33392
45.92243
6.157468
0.0263752
0.0217759
0.0319143
vol
92.55015
76.34800
112.23627
9.620299
0.0108050
0.0089098
0.0130979
Decision - model is unstable due to multi-colinearity (volume and concentration). Will include volume only as more data support the link between volume and AEA.
Model includes volume as covariate
Show the code
lm <-lm(igg ~ tx_group + vol, data = data)summary(lm)
Call:
lm(formula = igg ~ tx_group + vol, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.1171 -4.6460 -0.0524 4.7329 19.6052
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 28.068 2.549 11.009 < 2e-16 ***
tx_group250g 5.553 1.359 4.086 5.34e-05 ***
tx_group300g 8.388 1.674 5.011 8.24e-07 ***
tx_group350g 13.178 1.884 6.993 1.19e-11 ***
vol -1.422 1.047 -1.358 0.175
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.058 on 387 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.1982, Adjusted R-squared: 0.1899
F-statistic: 23.92 on 4 and 387 DF, p-value: < 2.2e-16
There is a significant difference between the means of the 250g vs 350g and 300g and 350g groups. This shows that there is a linear effect of oral IgG dose and serum IgG. This is consistent with Godden’s principle of a dose-dependant effect of oral IgG and serum IgG.
Show the code
figure <-ggplot(data, aes(x = tx_group, y = igg)) +# Removed fill from aes()# Add shaded regionsgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10), fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18), fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25), fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf), fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Add boxplot with fixed fill colorgeom_boxplot(fill ="white", # Set all boxplots to light blueoutlier.shape =16, outlier.size =2, alpha =1) +# Labelslabs(title ="",x ="Oral IgG dose",y ="Serum IgG (g/L)",fill ="Treatment" ) +# Custom themetheme_minimal() +coord_cartesian(ylim =c(0, 60)) +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure
Show the code
ggsave(filename ="figure 1a.tiff", # File nameplot = figure, # ggplot object to savedevice ="tiff", # Output formatwidth =5, # Width in inchesheight =4, # Height in inchesdpi =200# Resolution in dots per inch)
Table Objective 1
Final model includes volume as a covariate
Show the code
lm_all_breed <-lm(igg ~ tx_group + vol, data = data %>%mutate(tx_group =fct_relevel(tx_group,c("250g","200g","300g","350g"))) )lm_all_breed_tab <- lm_all_breed %>% broom::tidy(conf.int=T) %>%mutate(difference =case_when( term =="(Intercept)"~"Ref", T ~paste0(round(estimate,1)," (",round(conf.low,1)," to ",round(conf.high,1),")" )),term =case_when( term =="(Intercept)"~"250g", term !="(Intercept)"~str_remove(term, "tx_group")) )%>%select(term,difference)emmean_all_breed_tab <-emmeans(lm_all_breed, ~tx_group) %>% as_tibble %>%mutate(term = tx_group,emmean =paste0(round(emmean,1)," (",round(lower.CL,1)," to ",round(upper.CL,1),")" )) %>%select(term,emmean)lm_holstein <-lm(igg ~tx_group + vol, data = data %>%filter(breed =="Holstein") %>%mutate(tx_group =fct_relevel(tx_group,c("250g","200g","300g","350g"))) )lm_holstein_tab <- lm_holstein %>% broom::tidy(conf.int=T) %>%mutate(difference =case_when( term =="(Intercept)"~"Ref", T ~paste0(round(estimate,1)," (",round(conf.low,1)," to ",round(conf.high,1),")" )),term =case_when( term =="(Intercept)"~"250g", term !="(Intercept)"~str_remove(term, "tx_group")) )%>%select(term,difference)emmean_holstein_tab <-emmeans(lm_holstein, ~tx_group) %>% as_tibble %>%mutate(term = tx_group,emmean =paste0(round(emmean,1)," (",round(lower.CL,1)," to ",round(upper.CL,1),")" )) %>%select(term,emmean)lm_angus <-lm(igg ~ tx_group + vol,data = data %>%filter(breed =="Angus x Holstein") %>%mutate(tx_group =fct_relevel(tx_group,c("250g","200g","300g","350g"))))lm_angus_tab <- lm_angus %>% broom::tidy(conf.int=T) %>%mutate(difference =case_when( term =="(Intercept)"~"Ref", T ~paste0(round(estimate,1)," (",round(conf.low,1)," to ",round(conf.high,1),")" )),term =case_when( term =="(Intercept)"~"250g", term !="(Intercept)"~str_remove(term, "tx_group")) )%>%select(term,difference)emmean_angus_tab <-emmeans(lm_angus, ~tx_group) %>% as_tibble %>%mutate(term = tx_group,emmean =paste0(round(emmean,1)," (",round(lower.CL,1)," to ",round(upper.CL,1),")" )) %>%select(term,emmean)emmean_all_breed_tab %>%left_join(lm_all_breed_tab, by ="term") %>%left_join(emmean_holstein_tab, by ="term",suffix =c("_overall","_holstein")) %>%left_join(lm_holstein_tab, by ="term", suffix =c("_overall","_holstein")) %>%left_join(emmean_angus_tab %>%rename(emmean_angus = emmean), by ="term") %>%left_join(lm_angus_tab %>%rename(difference_angus = difference), by ="term") %>%rename(tx = term) %>%arrange(tx)
These findings indicate that the linear relationship between dose and serum IgG is robust to potential clustering of calves within batches of colostrum.
Objective 1b - Investigating factors that may impact IgG absorption and therefore modify the effect of IgG dose on serum IgG.
Breed (Holstein vs Holstein x Angus)
Breed as an effect modifier
Box and whisker plot showing serum IgG for calves randomized to 4 doses of colostrum, stratified by breed
Show the code
figure <-ggplot(data %>%filter(!is.na(tx_group)), aes(x = tx_group, y = igg)) +# Removed fill from aes()# Add shaded regionsgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10), fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18), fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25), fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf), fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Add boxplot with fixed fill colorgeom_boxplot(fill ="white", # Set all boxplots to light blueoutlier.shape =16, outlier.size =2, alpha =1) +# Labelslabs(title ="",x ="Oral IgG dose",y ="Serum IgG (g/L)",fill ="Treatment" ) +# Custom themetheme_minimal() +facet_wrap(~ breed) +coord_cartesian(ylim =c(0, 60)) +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure
Show the code
ggsave(filename ="figure holstein and angus.tiff", # File nameplot = figure, # ggplot object to savedevice ="tiff", # Output formatwidth =5, # Width in inchesheight =3, # Height in inchesdpi =200# Resolution in dots per inch)
Show the code
prop_data <- data %>%filter(!is.na(tx_group), !is.na(igg_category)) %>%group_by(tx_group, igg_category, breed) %>%summarize(count =n(), .groups ="drop") %>%group_by(tx_group,breed) %>%mutate(proportion = count /sum(count))figure <-ggplot(prop_data, aes(x = tx_group, y = proportion, fill = igg_category)) +geom_bar(stat ="identity", position ="stack") +# Add text labels for proportionsgeom_text(aes(label = scales::percent(proportion, accuracy =1)), # Format proportion as percentageposition =position_stack(vjust =0.5), # Place text in the middle of the stacked barssize =3, # Adjust text sizefamily ="Times New Roman"# Ensure consistent font ) +labs(title ="",x ="Oral IgG dose",y ="Proportion",fill ="IgG Levels" ) +scale_y_continuous(labels = scales::percent) +# Show percentagestheme_minimal() +facet_wrap(~ breed) +scale_fill_manual(values =c('#A2D9CE', '#C3D69B', '#FFF2CC', "pink")) +theme(panel.border =element_rect(color ="black", fill =NA, linewidth =1),text =element_text(family ="Times New Roman") )figure
Show the code
ggsave(filename ="figure angus holstein groups.tiff", # File nameplot = figure, # ggplot object to savedevice ="tiff", # Output formatwidth =8, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)
Show the code
lm <-lm(igg ~ tx_group*breed + vol, data = data)summary(lm)
Call:
lm(formula = igg ~ tx_group * breed + vol, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.820 -4.827 0.034 4.775 19.631
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 27.90179 2.82934 9.862 < 2e-16 ***
tx_group250g 5.46452 2.01331 2.714 0.00694 **
tx_group300g 6.65560 2.24264 2.968 0.00319 **
tx_group350g 11.95730 2.52244 4.740 3.01e-06 ***
breedHolstein -0.23146 2.11271 -0.110 0.91282
vol -1.29083 1.05318 -1.226 0.22108
tx_group250g:breedHolstein 0.08221 2.54388 0.032 0.97424
tx_group300g:breedHolstein 2.41561 2.52290 0.957 0.33893
tx_group350g:breedHolstein 1.42700 2.67584 0.533 0.59414
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.067 on 383 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.2045, Adjusted R-squared: 0.1879
F-statistic: 12.31 on 8 and 383 DF, p-value: 1.026e-15
Type 2 P-values for assessing significance of an interaction term
Show the code
car::Anova(lm)
Sum Sq
Df
F value
Pr(>F)
tx_group
2536.59849
3
16.9311836
0.0000000
breed
65.55740
1
1.3127395
0.2526146
vol
75.02029
1
1.5022271
0.2210821
tx_group:breed
86.36277
3
0.5764507
0.6307912
Residuals
19126.78291
383
NA
NA
Estimated marginal means
Show the code
emm <-emmeans(lm, ~ tx_group | breed)summary(emm)
tx_group
breed
emmean
SE
df
lower.CL
upper.CL
200g
Angus x Holstein
23.92888
1.7702482
383
20.44825
27.40950
250g
Angus x Holstein
29.39340
1.2507369
383
26.93423
31.85257
300g
Angus x Holstein
30.58448
1.1710728
383
28.28194
32.88701
350g
Angus x Holstein
35.88617
1.5301584
383
32.87761
38.89474
200g
Holstein
23.69742
1.7204628
383
20.31468
27.08015
250g
Holstein
29.24415
0.8125525
383
27.64653
30.84177
300g
Holstein
32.76863
0.8139977
383
31.16817
34.36909
350g
Holstein
37.08172
0.9332789
383
35.24673
38.91671
Show the code
emm_df <-as.data.frame(emm)# Plot the resultsggplot(emm_df, aes(x = tx_group, y = emmean, ymin = lower.CL, ymax = upper.CL)) +geom_point() +# Points for the meansgeom_errorbar(width =0.2) +# Error bars for confidence intervalsfacet_wrap(~ breed) +labs(title ="Estimated Marginal Means of IgG by Treatment group and breed",x ="Treatment Group",y ="Estimated Mean igg (with 95% CI)" )
Breed as a main effect
Univariable model for effect of volume on IgG.
Show the code
lm_not_control_for_dose <-lm(igg ~ breed, data = data)summary(lm_not_control_for_dose)
Call:
lm(formula = igg ~ breed, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.2917 -5.7233 0.6448 4.9886 21.8532
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 30.2033 0.7154 42.219 <2e-16 ***
breedHolstein 1.8790 0.8572 2.192 0.029 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.804 on 390 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.01217, Adjusted R-squared: 0.009637
F-statistic: 4.805 on 1 and 390 DF, p-value: 0.02897
Show the code
broom::tidy(lm_not_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
30.20329
0.7153875
42.219482
0.000000
28.7967931
31.609790
breedHolstein
1.87905
0.8572413
2.191973
0.028972
0.1936574
3.564442
Model for effect of volume on IgG after controlling for IgG dose
Show the code
lm_control_for_dose <-lm(igg ~ tx_group + breed, data = data)summary(lm_control_for_dose)
Call:
lm(formula = igg ~ tx_group + breed, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.2853 -4.7318 0.1331 4.8803 19.1561
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 24.4182 1.1329 21.554 < 2e-16 ***
tx_group250g 4.5976 1.2386 3.712 0.000236 ***
tx_group300g 6.7252 1.2407 5.421 1.05e-07 ***
tx_group350g 11.0509 1.2721 8.687 < 2e-16 ***
breedHolstein 0.9325 0.7848 1.188 0.235480
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.062 on 387 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.1973, Adjusted R-squared: 0.189
F-statistic: 23.78 on 4 and 387 DF, p-value: < 2.2e-16
Show the code
broom::tidy(lm_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
24.4182497
1.1328875
21.553993
0.0000000
22.1908652
26.645634
tx_group250g
4.5975982
1.2385995
3.711933
0.0002359
2.1623719
7.032825
tx_group300g
6.7252044
1.2406529
5.420698
0.0000001
4.2859408
9.164468
tx_group350g
11.0508788
1.2720549
8.687423
0.0000000
8.5498753
13.551882
breedHolstein
0.9324941
0.7847895
1.188209
0.2354796
-0.6104905
2.475479
Time to feeding
Show the code
figure <-ggplot(data %>%filter(!is.na(tx_group)), aes(x = tx_group, y = igg)) +# Removed fill from aes()# Add shaded regionsgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10), fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18), fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25), fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf), fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Add boxplot with fixed fill colorgeom_boxplot(fill ="white", # Set all boxplots to light blueoutlier.shape =16, outlier.size =2, alpha =1) +# Labelslabs(title ="Stratifed by time to feeding quartile",x ="Oral IgG dose",y ="Serum IgG (g/L)",fill ="Treatment" ) +# Custom themetheme_minimal() +facet_wrap(~ time_to_feed_cat) +coord_cartesian(ylim =c(0, 60)) +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure
Time to feeding as an effect modifier
Linear model evaluating effect modification. Statistical interaction between tx and time to feeding
Show the code
lm <-lm(igg ~ tx_group*time_to_feed + vol, data = data)summary(lm)
Call:
lm(formula = igg ~ tx_group * time_to_feed + vol, data = data)
Residuals:
Min 1Q Median 3Q Max
-21.5379 -4.7311 0.1043 4.7294 19.1700
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 28.879079 3.332775 8.665 < 2e-16 ***
tx_group250g 5.857875 2.759071 2.123 0.0344 *
tx_group300g 8.733863 3.024292 2.888 0.0041 **
tx_group350g 15.103089 3.311455 4.561 6.87e-06 ***
time_to_feed -0.016053 0.037600 -0.427 0.6697
vol -1.376495 1.048032 -1.313 0.1898
tx_group250g:time_to_feed -0.003930 0.041877 -0.094 0.9253
tx_group300g:time_to_feed -0.006984 0.044137 -0.158 0.8744
tx_group350g:time_to_feed -0.031003 0.046819 -0.662 0.5082
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.047 on 383 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.2089, Adjusted R-squared: 0.1924
F-statistic: 12.65 on 8 and 383 DF, p-value: 3.757e-16
Show the code
car::Anova(lm)
Sum Sq
Df
F value
Pr(>F)
tx_group
2693.30237
3
18.077639
0.0000000
time_to_feed
220.42335
1
4.438492
0.0357871
vol
85.66860
1
1.725041
0.1898317
tx_group:time_to_feed
37.82364
3
0.253875
0.8585660
Residuals
19020.45609
383
NA
NA
Estimated marginal means
Show the code
data %>%group_by(time_to_feed_cat) %>%summarise(mean_time_to_feed =mean(time_to_feed,na.rm=T),median_time_to_feed =median(time_to_feed) )
emm_df <-as.data.frame(emm)# Plot the resultsggplot(emm_df, aes(x = tx_group, y = emmean, ymin = lower.CL, ymax = upper.CL)) +geom_point() +# Points for the meansgeom_errorbar(width =0.2) +# Error bars for confidence intervalsfacet_wrap(~ time_to_feed) +labs(title ="Estimated Marginal Means of IgG by Treatment group and time to feeding",x ="Treatment Group",y ="Estimated Mean igg (with 95% CI)" )
Time to feeding as a main effect
Show the code
figure_scatter <-ggplot(data %>%filter(!is.na(tx_group)),aes(x = time_to_feed, y = igg)) +# Background zonesgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10),fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18),fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25),fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf),fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Scatter pointsgeom_point(size =1, alpha =0.8, colour ="black") +# Optionally add a smoothed line (remove if not desired)geom_smooth(method ="lm", se = T, linewidth =0.5, linetype ="dashed",colour ="black") +# Labelslabs(title ="Relationship between time to feeding and Serum IgG",x ="Time to feeding (min)",y ="Serum IgG (g/L)" ) +# Facet by treatment group if relevantfacet_wrap(~ tx_group) +coord_cartesian(ylim =c(0, 60)) +theme_minimal() +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure_scatter
Show the code
ggsave(filename ="figure scatter time.tiff", # File nameplot = figure_scatter, # ggplot object to savedevice ="tiff", # Output formatwidth =6, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)
Univariable model for effect of volume on IgG.
Show the code
lm_not_control_for_dose <-lm(igg ~ time_to_feed, data = data)summary(lm_not_control_for_dose)
Call:
lm(formula = igg ~ time_to_feed, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.3501 -5.6577 0.4578 5.1479 21.9856
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 32.84105 0.89147 36.839 <2e-16 ***
time_to_feed -0.02228 0.01339 -1.663 0.0971 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.824 on 390 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.007044, Adjusted R-squared: 0.004498
F-statistic: 2.767 on 1 and 390 DF, p-value: 0.09706
Show the code
broom::tidy(lm_not_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
32.8410482
0.8914709
36.839172
0.0000000
31.0883582
34.5937382
time_to_feed
-0.0222772
0.0133934
-1.663298
0.0970562
-0.0486096
0.0040551
Model for effect of volume on IgG after controlling for IgG dose
Show the code
lm_control_for_dose <-lm(igg ~ tx_group + time_to_feed, data = data)summary(lm_control_for_dose)
Call:
lm(formula = igg ~ tx_group + time_to_feed, data = data)
Residuals:
Min 1Q Median 3Q Max
-21.4920 -4.6019 0.0523 4.8640 18.8429
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 26.38345 1.25273 21.061 < 2e-16 ***
tx_group250g 4.90516 1.22702 3.998 7.66e-05 ***
tx_group300g 6.85231 1.23084 5.567 4.85e-08 ***
tx_group350g 11.39734 1.25450 9.085 < 2e-16 ***
time_to_feed -0.02587 0.01208 -2.141 0.0329 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.033 on 387 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.2038, Adjusted R-squared: 0.1956
F-statistic: 24.77 on 4 and 387 DF, p-value: < 2.2e-16
Show the code
broom::tidy(lm_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
26.3834503
1.2527301
21.060762
0.0000000
23.9204417
28.8464589
tx_group250g
4.9051560
1.2270208
3.997615
0.0000766
2.4926948
7.3176172
tx_group300g
6.8523114
1.2308374
5.567195
0.0000000
4.4323462
9.2722766
tx_group350g
11.3973387
1.2544971
9.085185
0.0000000
8.9308559
13.8638215
time_to_feed
-0.0258732
0.0120848
-2.140965
0.0329011
-0.0496333
-0.0021131
Volume
Volume as an effect modifier.
Show the code
figure <-ggplot(data %>%filter(!is.na(tx_group)), aes(x = tx_group, y = igg)) +# Removed fill from aes()# Add shaded regionsgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10), fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18), fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25), fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf), fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Add boxplot with fixed fill colorgeom_boxplot(fill ="white", # Set all boxplots to light blueoutlier.shape =16, outlier.size =2, alpha =1) +# Labelslabs(title ="Stratifed by volume fed (L)",x ="Oral IgG dose",y ="Serum IgG (g/L)",fill ="Treatment" ) +# Custom themetheme_minimal() +facet_wrap(~ vol_cat) +coord_cartesian(ylim =c(0, 60)) +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure
Statistical interaction between tx and volume fed
Show the code
lm <-lm(igg ~ tx_group*vol_cat, data = data)summary(lm)
Call:
lm(formula = igg ~ tx_group * vol_cat, data = data)
Residuals:
Min 1Q Median 3Q Max
-22.0128 -4.5394 -0.0687 4.6713 19.4430
Coefficients: (4 not defined because of singularities)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 25.400 1.243 20.439 < 2e-16 ***
tx_group250g 4.428 2.036 2.175 0.03026 *
tx_group300g 5.193 3.269 1.588 0.11302
tx_group350g 9.561 3.209 2.980 0.00307 **
vol_cat2.5-2.999 -1.676 2.312 -0.725 0.46899
vol_cat3.0-3.499 2.475 3.332 0.743 0.45804
vol_cat3.5-4.0 1.008 2.845 0.354 0.72324
tx_group250g:vol_cat2.5-2.999 2.377 2.935 0.810 0.41843
tx_group300g:vol_cat2.5-2.999 5.260 4.079 1.290 0.19798
tx_group350g:vol_cat2.5-2.999 1.821 4.426 0.411 0.68097
tx_group250g:vol_cat3.0-3.499 -6.227 3.295 -1.890 0.05952 .
tx_group300g:vol_cat3.0-3.499 -2.265 2.252 -1.006 0.31529
tx_group350g:vol_cat3.0-3.499 NA NA NA NA
tx_group250g:vol_cat3.5-4.0 NA NA NA NA
tx_group300g:vol_cat3.5-4.0 NA NA NA NA
tx_group350g:vol_cat3.5-4.0 NA NA NA NA
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.03 on 380 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.219, Adjusted R-squared: 0.1964
F-statistic: 9.687 on 11 and 380 DF, p-value: 1.727e-15
emm_df <-as.data.frame(emm)# Plot the resultsggplot(emm_df, aes(x = tx_group, y = emmean, ymin = lower.CL, ymax = upper.CL)) +geom_point() +# Points for the meansgeom_errorbar(width =0.2) +# Error bars for confidence intervalsfacet_wrap(~ vol_cat) +# Separate panels for each value of vollabs(title ="Estimated Marginal Means of igg by tx_group and vol",x ="Treatment Group (tx_group)",y ="Estimated Mean igg (with 95% CI)" )
emm_df <-as.data.frame(emm)# Plot the resultsggplot(emm_df, aes(x = vol_cat, y = emmean, ymin = lower.CL, ymax = upper.CL)) +geom_point() +# Points for the meansgeom_errorbar(width =0.2) +# Error bars for confidence intervalsfacet_wrap(~ tx_group) +# Separate panels for each value of vollabs(title ="Estimated Marginal Means of igg by tx_group and vol",x ="Treatment Group (tx_group)",y ="Estimated Mean igg (with 95% CI)" )
ggsave(filename ="figure scatter vol.tiff", # File nameplot = figure_scatter, # ggplot object to savedevice ="tiff", # Output formatwidth =6, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)
Univariable model for effect of volume on IgG.
Show the code
lm_not_control_for_dose <-lm(igg ~ vol, data = data)summary(lm_not_control_for_dose)
Call:
lm(formula = igg ~ vol, data = data)
Residuals:
Min 1Q Median 3Q Max
-24.0073 -5.0283 0.3549 5.1163 20.6597
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 18.6529 2.1394 8.719 < 2e-16 ***
vol 4.1780 0.6841 6.107 2.45e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.501 on 390 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.08729, Adjusted R-squared: 0.08495
F-statistic: 37.3 on 1 and 390 DF, p-value: 2.453e-09
Show the code
broom::tidy(lm_not_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
18.652909
2.1393531
8.718948
0
14.446801
22.85902
vol
4.177978
0.6841031
6.107235
0
2.832987
5.52297
Model for effect of volume on IgG after controlling for IgG dose
Show the code
lm_control_for_dose <-lm(igg ~ tx_group + vol, data = data)summary(lm_control_for_dose)
Call:
lm(formula = igg ~ tx_group + vol, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.1171 -4.6460 -0.0524 4.7329 19.6052
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 28.068 2.549 11.009 < 2e-16 ***
tx_group250g 5.553 1.359 4.086 5.34e-05 ***
tx_group300g 8.388 1.674 5.011 8.24e-07 ***
tx_group350g 13.178 1.884 6.993 1.19e-11 ***
vol -1.422 1.047 -1.358 0.175
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.058 on 387 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.1982, Adjusted R-squared: 0.1899
F-statistic: 23.92 on 4 and 387 DF, p-value: < 2.2e-16
Show the code
broom::tidy(lm_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
28.068299
2.549476
11.009436
0.0000000
23.055741
33.0808572
tx_group250g
5.553479
1.359141
4.086021
0.0000534
2.881255
8.2257038
tx_group300g
8.388434
1.673861
5.011427
0.0000008
5.097434
11.6794340
tx_group350g
13.178252
1.884444
6.993178
0.0000000
9.473223
16.8832809
vol
-1.421567
1.047104
-1.357618
0.1753760
-3.480291
0.6371571
Colostrum concentration
Concentration as an effect modifier
Concentration as a main effect
Show the code
figure_scatter <-ggplot(data %>%filter(!is.na(tx_group)),aes(x = concentration, y = igg)) +# Background zonesgeom_rect(aes(xmin =-Inf, xmax =Inf, ymin =-5, ymax =10),fill ="pink", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =10, ymax =18),fill ="#FFF2CC", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =18, ymax =25),fill ="#C3D69B", alpha =0.5, inherit.aes =FALSE) +geom_rect(aes(xmin =-Inf, xmax =Inf, ymin =25, ymax =Inf),fill ="#A2D9CE", alpha =0.5, inherit.aes =FALSE) +# Scatter pointsgeom_point(size =1, alpha =0.8, colour ="black") +# Optionally add a smoothed line (remove if not desired)geom_smooth(method ="lm", se = T, linewidth =0.5, linetype ="dashed",colour ="black") +# Labelslabs(title ="Relationship between colostrum colostrum and Serum IgG",x ="Concentration (g/L)",y ="Serum IgG (g/L)" ) +# Facet by treatment group if relevantfacet_wrap(~ tx_group) +coord_cartesian(ylim =c(0, 60)) +theme_minimal() +theme(legend.position ="none",panel.border =element_rect(color ="black", fill =NA, linewidth =1 ),text =element_text(family ="Times New Roman") )figure_scatter
Show the code
ggsave(filename ="figure scatter conc.tiff", # File nameplot = figure_scatter, # ggplot object to savedevice ="tiff", # Output formatwidth =6, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)
Univariable model for effect of concentration on IgG.
Show the code
lm_not_control_for_dose <-lm(igg ~ concentration, data = data)summary(lm_not_control_for_dose)
Call:
lm(formula = igg ~ concentration, data = data)
Residuals:
Min 1Q Median 3Q Max
-22.709 -5.684 0.743 4.972 21.750
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.19922 3.22751 6.568 1.63e-10 ***
concentration 0.10987 0.03413 3.219 0.00139 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.75 on 390 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.02588, Adjusted R-squared: 0.02338
F-statistic: 10.36 on 1 and 390 DF, p-value: 0.001394
Show the code
broom::tidy(lm_not_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
21.199223
3.2275128
6.568285
0.0000000
14.8537223
27.5447244
concentration
0.109867
0.0341308
3.219004
0.0013941
0.0427637
0.1769703
Controlling for IgG dose
Show the code
lm_control_for_dose <-lm(igg ~ tx_group + concentration, data = data)summary(lm_control_for_dose)
Call:
lm(formula = igg ~ tx_group + concentration, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.0381 -4.5991 -0.0886 4.7512 19.6476
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.24441 3.13803 6.770 4.79e-11 ***
tx_group250g 4.74616 1.23024 3.858 0.000134 ***
tx_group300g 6.81478 1.23606 5.513 6.45e-08 ***
tx_group350g 10.95843 1.28328 8.539 3.12e-16 ***
concentration 0.04021 0.03238 1.242 0.215048
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.061 on 387 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.1976, Adjusted R-squared: 0.1893
F-statistic: 23.82 on 4 and 387 DF, p-value: < 2.2e-16
Show the code
broom::tidy(lm_control_for_dose, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
21.2444098
3.1380273
6.769989
0.0000000
15.0746942
27.4141254
tx_group250g
4.7461575
1.2302389
3.857915
0.0001339
2.3273691
7.1649458
tx_group300g
6.8147777
1.2360645
5.513287
0.0000001
4.3845356
9.2450199
tx_group350g
10.9584281
1.2832788
8.539398
0.0000000
8.4353573
13.4814989
concentration
0.0402088
0.0323785
1.241839
0.2150482
-0.0234509
0.1038686
Volume and Concentration in a single model
Show the code
lm_vol_concentration <-lm(igg ~ vol*concentration, data = data)summary(lm_vol_concentration)
Call:
lm(formula = igg ~ vol * concentration, data = data)
Residuals:
Min 1Q Median 3Q Max
-23.7497 -4.7729 -0.0067 4.7928 20.1867
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.57259 17.37787 1.241 0.2152
vol -4.43984 5.72600 -0.775 0.4386
concentration -0.09388 0.17920 -0.524 0.6007
vol:concentration 0.11325 0.06023 1.880 0.0608 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.069 on 388 degrees of freedom
(7 observations deleted due to missingness)
Multiple R-squared: 0.1936, Adjusted R-squared: 0.1873
F-statistic: 31.04 on 3 and 388 DF, p-value: < 2.2e-16
Show the code
broom::tidy(lm_vol_concentration, conf.int=T)
term
estimate
std.error
statistic
p.value
conf.low
conf.high
(Intercept)
21.5725941
17.3778657
1.2413834
0.2152141
-12.5939734
55.7391616
vol
-4.4398443
5.7259976
-0.7753835
0.4385854
-15.6977104
6.8180218
concentration
-0.0938767
0.1792024
-0.5238588
0.6006758
-0.4462059
0.2584525
vol:concentration
0.1132540
0.0602350
1.8802023
0.0608294
-0.0051739
0.2316818
Show the code
check_collinearity(lm_vol_concentration)
Term
VIF
VIF_CI_low
VIF_CI_high
SE_factor
Tolerance
Tolerance_CI_low
Tolerance_CI_high
vol
78.88442
65.02400
95.74546
8.881690
0.0126768
0.0104444
0.0153789
concentration
33.12883
27.36416
40.15397
5.755765
0.0301852
0.0249041
0.0365441
vol:concentration
68.65339
56.60318
83.31509
8.285734
0.0145659
0.0120026
0.0176669
Checking if this is equivalent to linear model where dose is on the continuous scale and is included instead of the vol*concentration interaction variable
Our objective is to determine the amount of IgG available to feed per calf from pooled and pasteurised batches of 40L, when there is a limiting effect of colostral IgG concentration and volume administered.
Show the code
day
date
born
igg_kg
concentration_limited_dose
igg_per_calf
2024-10-24
14
8.798840
317.9076
628.4886
2024-10-25
22
8.254016
347.6477
375.1826
2024-10-26
15
7.864941
372.9109
524.3294
2024-10-27
28
7.254011
372.6910
259.0718
2024-10-28
18
6.837712
323.6434
379.8729
2024-10-29
17
7.940289
321.0714
467.0758
2024-10-30
20
6.121256
370.2976
306.0628
2024-10-31
24
8.923116
348.0520
371.7965
2024-11-01
26
8.435949
328.6460
324.4596
2024-11-02
15
6.238196
283.2936
415.8797
2024-11-03
21
4.299691
338.4722
204.7472
2024-11-04
24
8.903345
359.0180
370.9727
2024-11-05
19
2.424971
373.0724
127.6300
2024-11-06
32
7.826972
304.1152
244.5929
2024-11-07
38
5.596646
365.3151
147.2802
2024-11-08
35
12.133704
294.0847
346.6773
Number born per day
Show the code
ggplot(data = day, aes(x = date, y = born)) +geom_col() +labs(title ="",x ="Date",y ="Number born" ) +scale_x_date(date_breaks ="1 day", date_labels ="%b %d") +# Custom date labelstheme_minimal() +theme(text =element_text(family ="Times New Roman"), # Set Times New Roman fontaxis.text.x =element_text(angle =90, hjust =0),panel.grid.major.x =element_blank(), # Remove major vertical grid linespanel.grid.minor.x =element_blank() # Remove minor vertical grid lines )
From this histogram, it is clear that the number of calves born per day have increased since November 6th and that the demand for colostrum is higher. Therefore colostrum recommendations must be made to account for times of peak demand.
IgG available per calf (due to limitation of total IgG harvested on that day - mass limited dose)
Show the code
ggplot(data = day, aes(x = date, y = igg_per_calf)) +geom_col() +labs(title ="Mass limited dose",x ="",y ="IgG (g) available per calf" ) +scale_x_date(date_breaks ="1 day", date_labels ="%b %d") +# Custom date labelstheme_minimal() +theme(text =element_text(family ="Times New Roman"), # Set Times New Roman fontaxis.text.x =element_text(angle =90, hjust =0),panel.grid.major.x =element_blank(), # Remove major vertical grid linespanel.grid.minor.x =element_blank() # Remove minor vertical grid lines )
Show the code
mass_day <-ggplot(data = day, aes(y = igg_per_calf, x =1)) +# Add a dummy x-axis (1) for a single boxgeom_boxplot(outlier.shape =NA, width =0.3) +# Box plot with adjusted width and no outliersgeom_jitter(width =0.01, alpha =0.6, size =1.5) +# Jitter with slight width, transparency, and sizelabs(y ="IgG (g) available per calf",x =NULL ) +scale_y_continuous(limits =c(100, 640), # Set y-axis rangebreaks =seq(100, 640, by =20) # Primary y-axis gridlines every 20 units ) +theme_minimal() +theme(text =element_text(family ="Times New Roman"), # Set Times New Roman fontpanel.grid.major.x =element_blank(), # Remove major vertical grid linespanel.grid.minor.x =element_blank(), # Remove minor vertical grid linespanel.grid.major.y =element_line(color ="grey80"), # Keep primary y-axis gridlinespanel.grid.minor.y =element_blank(), # Remove secondary gridlinesaxis.text.x =element_blank(), # Remove x-axis textaxis.title.x =element_blank() # Remove x-axis title )ggsave(filename ="figure mass day.tiff", # File nameplot = mass_day, # ggplot object to savedevice ="tiff", # Output formatwidth =3, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)mass_day
IgG available per calf each day (due to limitation of 4L per calf = concentration limited dose)
Show the code
ggplot(data = day, aes(x = date, y = concentration_limited_dose)) +geom_col() +labs(title ="Concentration limited dose",x ="",y ="IgG (g) available per calf" ) +scale_x_date(date_breaks ="1 day", date_labels ="%b %d") +# Custom date labelstheme_minimal() +theme(text =element_text(family ="Times New Roman"), # Set Times New Roman fontaxis.text.x =element_text(angle =90, hjust =0),panel.grid.major.x =element_blank(), # Remove major vertical grid linespanel.grid.minor.x =element_blank() # Remove minor vertical grid lines )
Concentration limited dose by batch
Show the code
ggplot(data = batch %>%arrange(dose_at_4l) %>%mutate(batch =1:nrow(batch)), aes(x = batch, y = dose_at_4l)) +geom_col() +labs(x ="Batches sorted by concentration", ) +theme_minimal() +scale_y_continuous(name ="IgG (g) in 4L feed",sec.axis =sec_axis(~ . /4, name ="IgG (g/L) in 1L") ) +theme(text =element_text(family ="Times New Roman"), # Set Times New Roman fontpanel.grid.major.x =element_blank(), # Remove major vertical grid linespanel.grid.minor.x =element_blank() # Remove minor vertical grid lines )
Show the code
concentration <-ggplot(data = batch, aes(y = dose_at_4l, x =1)) +# Add a dummy x-axis (1) for a single boxgeom_boxplot(outlier.shape =NA, width =0.3) +# Box plot with adjusted width and no outliersgeom_jitter(width =0.01, alpha =0.6, size =1.5) +# Jitter with slight width, transparency, and sizelabs(y ="IgG (g) in 4L (i.e., max dose)",x =NULL# Remove x-axis title ) +scale_y_continuous(limits =c(260, 500), # Set y-axis rangebreaks =seq(260, 500, by =20), # Primary y-axis gridlines every 20 unitssec.axis =sec_axis(~ . /4,name ="IgG (g) in 1L",breaks =seq(260, 500, by =20) /4# Align secondary axis breaks ) ) +theme_minimal() +theme(text =element_text(family ="Times New Roman"), # Set Times New Roman fontpanel.grid.major.x =element_blank(), # Remove major vertical grid linespanel.grid.minor.x =element_blank(), # Remove minor vertical grid linesaxis.text.x =element_blank(), # Remove x-axis textaxis.title.x =element_blank() # Remove x-axis title )ggsave(filename ="figure concentration.tiff", # File nameplot = concentration, # ggplot object to savedevice ="tiff", # Output formatwidth =3, # Width in inchesheight =5, # Height in inchesdpi =200# Resolution in dots per inch)concentration
tibble(date_range =paste0(min(day$date)," to ",max(day$date)),number_of_days =nrow(day),calves_born =sum(day$born),igg_harvested_kg =round(sum(day$igg_kg),1),igg_available_per_calf_g =round(igg_harvested_kg/calves_born *1000,0))
date_range
number_of_days
calves_born
igg_harvested_kg
igg_available_per_calf_g
2024-10-24 to 2024-11-08
16
368
117.9
320
Export data
Show the code
#data %>% write_rds('igg_trial.rds')
Secondary analyses
Comparison of randomisation periods
Show the code
weather_data_cleaned <- weather_data_2 %>% clean_names %>%mutate(randomisation_period =case_when( day <=as.Date("2024-10-30") ~"Days 1 - 7", T ~"Days 8 - 18" ) ) %>%filter(between(day,data$enrol_date %>% min %>% as.Date, data$enrol_date %>% max %>% as.Date))table1::table1(~ max_temp + rainfall | randomisation_period, data = weather_data_cleaned)
Days 1 - 7 (N=7)
Days 8 - 18 (N=11)
Overall (N=18)
max_temp
Mean (SD)
24.0 (4.10)
26.2 (4.64)
25.3 (4.45)
Median [Min, Max]
23.3 [18.9, 30.6]
24.5 [21.3, 36.3]
24.2 [18.9, 36.3]
rainfall
Mean (SD)
0 (0)
0 (0)
0 (0)
Median [Min, Max]
0 [0, 0]
0 [0, 0]
0 [0, 0]
Show the code
lm(max_temp ~ randomisation_period, data = weather_data_cleaned) %>% summary
Call:
lm(formula = max_temp ~ randomisation_period, data = weather_data_cleaned)
Residuals:
Min 1Q Median 3Q Max
-5.071 -3.464 -1.021 1.836 10.136
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.971 1.680 14.27 1.61e-10 ***
randomisation_periodDays 8 - 18 2.192 2.149 1.02 0.323
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 4.445 on 16 degrees of freedom
Multiple R-squared: 0.06107, Adjusted R-squared: 0.002382
F-statistic: 1.041 on 1 and 16 DF, p-value: 0.3229
Show the code
lm(rainfall ~ randomisation_period, data = weather_data_cleaned) %>% summary
Call:
lm(formula = rainfall ~ randomisation_period, data = weather_data_cleaned)
Residuals:
Min 1Q Median 3Q Max
0 0 0 0 0
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0 0 NaN NaN
randomisation_periodDays 8 - 18 0 0 NaN NaN
Residual standard error: 0 on 16 degrees of freedom
Multiple R-squared: NaN, Adjusted R-squared: NaN
F-statistic: NaN on 1 and 16 DF, p-value: NA
Show the code
data <- data %>%mutate(randomisation_period =case_when( enrol_date <=as.Date("2024-10-30") ~"Days 1 - 7", T ~"Days 8 - 18" ) )table1::table1(~ concentration + vol + sex + breed | randomisation_period, data = data)
Days 1 - 7 (N=131)
Days 8 - 18 (N=268)
Overall (N=399)
concentration
Mean (SD)
93.1 (11.0)
94.1 (11.7)
93.8 (11.5)
Median [Min, Max]
90.9 [76.9, 120]
92.6 [71.4, 121]
92.6 [71.4, 121]
vol
Mean (SD)
2.71 (0.519)
3.27 (0.471)
3.08 (0.552)
Median [Min, Max]
2.60 [1.80, 3.80]
3.40 [2.10, 3.90]
3.10 [1.80, 3.90]
sex
F
98 (74.8%)
220 (82.1%)
318 (79.7%)
M
33 (25.2%)
48 (17.9%)
81 (20.3%)
breed
Angus x Holstein
57 (43.5%)
69 (25.7%)
126 (31.6%)
Holstein
74 (56.5%)
199 (74.3%)
273 (68.4%)
Show the code
lm(concentration ~ randomisation_period, data = data) %>% summary
Call:
lm(formula = concentration ~ randomisation_period, data = data)
Residuals:
Min 1Q Median 3Q Max
-22.707 -7.929 -1.543 5.864 26.938
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 93.062 1.005 92.637 <2e-16 ***
randomisation_periodDays 8 - 18 1.074 1.226 0.876 0.381
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 11.5 on 397 degrees of freedom
Multiple R-squared: 0.001931, Adjusted R-squared: -0.0005833
F-statistic: 0.768 on 1 and 397 DF, p-value: 0.3814
Sample size calculations
Sample size calculations were conducted prior to the start of the trial. The goal was to demonstrate differences in proportions of calves with serum IgG > 10, 15 and 25g/L cut-off points.
The B line is probably most appropriate for what we can expect the relationship between oral IgG dose and serum IgG. y = 0.0804 * oral dose. The standard deviation is not reported in the paper, but back calculation from the SE estimates in table 1 (SD = SE * sqrt(n)) give SD values of 4, 7, and 6 for groups B, C, and D, respectively. I will calculate sample sizes for SD values of 5 and 6.
treat <-250; control <-200treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
140
70
70
0
0.8
300 vs 250
Show the code
treat <-300; control <-250treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
68
34
34
0
0.8
350 vs 300
Show the code
treat <-350; control <-300treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
60
30
30
0
0.8
Assuming SD = 6
250 vs 200
Show the code
treat <-250; control <-200treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
152
76
76
0
0.8
300 vs 250
Show the code
treat <-300; control <-250treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
94
47
47
0
0.8
350 vs 300
Show the code
treat <-350; control <-300treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
86
43
43
0
0.8
Sample size calculation for 10g/L
Assuming SD = 5
250 vs 200
Show the code
treat <-250; control <-200treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_10_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_10_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
180
90
90
0
0.8
300 vs 250
Show the code
treat <-300; control <-250treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_10_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_10_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
752
376
376
0
0.8
350 vs 300
Show the code
treat <-350; control <-300treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_10_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_10_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
5832
2916
2916
0
0.8
Assuming SD = 6
250 vs 200
Show the code
treat <-250; control <-200treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_10_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_10_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
182
91
91
0
0.8
300 vs 250
Show the code
treat <-300; control <-250treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_10_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_10_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
474
237
237
0
0.8
350 vs 300
Show the code
treat <-350; control <-300treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_10_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_10_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
1910
955
955
0
0.8
Sample size calculation for 15g/L
Assuming SD = 5
250 vs 200
Show the code
treat <-250; control <-200treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_15_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_15_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
68
34
34
0
0.8
300 vs 250
Show the code
treat <-300; control <-250treat <- data %>%filter(igg_oral == treat) %>%pull(igg_ftpi_15_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_ftpi_15_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
140
70
70
0
0.8
350 vs 300
Show the code
treat <-350; control <-300treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd5)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd5)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
60
30
30
0
0.8
Assuming SD = 6
250 vs 200
Show the code
treat <-250; control <-200treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
152
76
76
0
0.8
300 vs 250
Show the code
treat <-300; control <-250treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble
n.total
n.treat
n.control
delta
power
94
47
47
0
0.8
350 vs 300
Show the code
treat <-350; control <-300treat <- data %>%filter(igg_oral == treat) %>%pull(igg_excellent_sd6)control <- data %>%filter(igg_oral == control) %>%pull(igg_excellent_sd6)epi.sssupb(treat = treat, control = control,delta =0, power =0.8, alpha =0.05, n =NA) %>% as_tibble