Our article on lactation curves has just appeared in Preventive Veterinary Medicine:
We propose a semi-parametric model for lactation curves that, along with stage of lactation, accounts for day of the year at milk recording and stage of gestation. Lactation is described as having 3 different phases defined by 2 change points of which the second is a function of gestation stage. Season of milk recording is modelled using cosine and sine functions. As an application, the model is used to estimate the association between intramammary infections (IMI) dynamics as measured by somatic cell count (SCC) over the dry period and the shape of the lactation curve. Milk recording data collected in 2128 herds from England and Wales between 2004 and 2007 were used in the analysis. From a random sample of 1000 of these herds, smoothed milk production was used to test the behaviour of the model and estimate model parameters. The first change point was set at 60 days in milk. The second change point was set at 100 days of gestation or 200 days in milk when the latter was not available. Using data from the 1128 remaining herds, multilevel models were then used to model individual test-day milk production within lactations within herds. Average milk production at 60 days in milk for cows of parities 1, 2, 3 and greater than 3 were 26.9kg, 31.6kg, 34.4kg and 34.7kg respectively and, after this stage, decreases in milk production per 100 days milk of lactation were 3.1kg, 5.1kg, 6.3kg and 6.7kg respectively. Compared to cows that had an SCC below 200,000cells/mL on both the last milk recording in a lactation and the first milk recording in the following lactation, cows that had an SCC greater than 200,000cells/mL on their first milk recording after calving had an estimated loss of milk production of between 216 and 518kg depending on parity. These estimates demonstrate the impact of the dynamics of SCC during the dry period on milk production during the following lactation.
The basic idea was presented here. On this page you will find the R code that goes with the paper, apart from the association with stage of gestation which is accounted for in some of the models presented in the paper but not here.
Milk production, stage of lactation and time of the year
Below is an R function that generates lactation curves using the model presented in the paper.
###################################################################
### Function that generates predicted milk productions
### for a grid days in milk x day of the year
###################################################################
## The function takes the model's coefficients as arguments
## The 'plot' argument needs to be set to TRUE if a 3D surface
## is wanted - FALSE otherwise
PredMilk <- function(betas, plot){
tau1 <- 60
tau2 <- 200
ntau2 <- (tau2 - tau1) / 100
Dim <- 5:305
Doy <- 1:365
## Day of year mapped onto 2 period
X <- expand.grid(
Dim = Dim,
Doy = Doy)
X$Toy <- X$Doy / 365 * 2 * pi
X$Int <- rep(1)
X$nDim <- (X$Dim - tau1) / 100
X$I1nDim2 <- ifelse(X$Dim < tau1, X$nDim^2, 0)
X$I1nDim3 <- ifelse(X$Dim < tau1, X$nDim^3, 0)
X$I3fDim <- ifelse(X$Dim >= tau2,
ntau2^2 - 2 * ntau2 * X$nDim + X$nDim^2, 0)
X$cosT <- cos(X$Toy)
X$sinT <- sin(X$Toy)
X$I1.nDim.cosT <- ifelse(Dim < tau1, X$nDim * X$cosT, 0)
X$I1.nDim.sinT <- ifelse(Dim < tau1, X$nDim * X$sinT, 0)
X$I23.nDim.cosT <- ifelse(Dim > tau1, X$nDim * X$cosT, 0)
X$I23.nDim.sinT <- ifelse(Dim > tau1, X$nDim * X$sinT, 0)
X$Milk <- as.matrix(X[,-(1:3)]) %*% betas
Z <- X[,c("Dim", "Doy", "Milk")]
Z <- reshape(Z, direction = "wide", idvar = "Dim", timevar = "Doy")
Z <- Z[,-1]
if(isTRUE(plot)){
x <- seq(5, 305, 5)
y <- seq(5, 305, 5)
par(mfrow = c(1, 2), mar = c(2, 2, 0, 2))
persp(x = x,
y = y,
z = as.matrix(Z[match(x, Dim), match(y, Doy)]),
theta = -60,
ticktype = "detailed",
xlab = "Day in milk",
ylab = "Day of year at recording",
zlab = "Milk (kg)")
persp(x = x,
y = y,
z = as.matrix(Z[match(x, Dim), match(y, Doy)]),
theta = 60,
ticktype = "detailed",
xlab = "Day in milk",
ylab = "Day of year at recording",
zlab = "Milk (kg)")
}
Milk <- list(Dim = Dim,
Doy = Doy,
Milk = Z)
}
Parity 1
Lactation curve from primiparous cows:
betas <- c(26.82, -3.081, 16.041, 77.301, -2.092,
-.272, .992, -.755, 1.058, -.34, .059)
par(oma = c(0, 0, 2, 0))
PredMilk(betas = betas, plot = TRUE)
title("Parity 1", outer = TRUE)
Other parities
## Predicted milk production - Parity 2
betas <- c(31.561, -5.094, 16.041, 77.301, -3.148,
-.633, 1.208, -.755, 1.058, -.34, .059)
par(oma = c(0, 0, 2, 0))
PredMilk(betas = betas, plot = TRUE)
title("Parity 2", outer = TRUE)
## Predicted milk production - Parity 3
betas <- c(34.347, -6.282, 16.041, 77.301, -3.148,
-.633, 1.208, -.755, 1.058, -.34, .059)
par(oma = c(0, 0, 2, 0))
PredMilk(betas = betas, plot = TRUE)
title("Parity 3", outer = TRUE)
## Predicted milk production - Parity > 3
betas <- c(34.715, -6.708, 16.041, 77.301, -3.148,
-.633, 1.208, -.755, 1.058, -.34, .059)
par(oma = c(0, 0, 2, 0))
PredMilk(betas = betas, plot = TRUE)
title("Parity > 3", outer = TRUE)
Milk production and somatic cell count categories
Here is the code to generate the lactation curves for the different SCC categories:
###################################################################
### Function that plots milk production per day in milk
### Uses the predictions generated by the above function
###################################################################
plotMilk <- function(Betas, Thresh, Parit){
nmfig <- paste("T", Thresh, "_Par", Parit, ".jpg", sep ="")
nmcsv <- paste("T", Thresh, "_Par", Parit, ".csv", sep ="")
CumulProd <- data.frame(
catg = c("LL", "LH", "HL", "HH"),
cp = rep(NA, 4))
colr <- c("darkolivegreen", "lightblue", "orange", "red")
lty <- c(1, 2, 1, 2)
for(i in 1:4){
Y <- PredMilk(betas = Betas[, i + 1], plot = FALSE)
plot(x = 5:305,
y = Y$Milk[,1],
type = "l", col = colr[i],
lty = lty[i], lwd = 2,
ylim = c(15, 40), ylab = "Milk (kg)",
xlab = "Day in milk")
par(new = TRUE)
CumulProd$cp[i] <- sum(Y$Milk[,1])
}
legend(200, 40,
c("Low-Low", "Low-High", "High-Low", "High-High"),
col = colr, bty = "n", lty = lty, lwd = 2)
title(paste("Threshold = ", Thresh," cells/mL - Parity = ", Parit,
sep = ""))
print(CumulProd)
}
Threshold = 200,000 cells/mL; parity 2
Betas <- data.frame(
coF = c("Int", "Dim", "I1Dim2", "I1Dim3", "I3fDim",
"cosT",
"sinT", "I1Dim.cosT", "I1Dim.sinT", "I23Dim.cosT",
"I23Dim.sinT"),
LL = c(32.74, -6.091, 6.478, 57.712, -.172, -.691, 1.605,
-1.328, .55, -.019, -.119),
LH = c(-.918, .292, -4.132, rep(0, 8)),
HL = c(.421, -.304, -1.882, rep(0, 8)),
HH = c(-.929, .186, -3.522, rep(0, 8)))
Betas$LH <- Betas$LL + Betas$LH
Betas$HL <- Betas$LL + Betas$HL
Betas$HH <- Betas$LL + Betas$HH
plotMilk(Betas = Betas, Thresh = 200, Parit = 2)
Threshold = 200,000 cells/mL; parity 3
Betas <- data.frame(
coF = c("Int", "Dim", "I1Dim2", "I1Dim3", "I3fDim",
"cosT",
"sinT", "I1Dim.cosT", "I1Dim.sinT", "I23Dim.cosT",
"I23Dim.sinT"),
LL = c(34.827, -6.869, 1.494, 54.95, -.187, -.603, 1.482,
-1.831, 1.217, -.252, -.056),
LH = c(-.892, .24, -3.473, rep(0, 8)),
HL = c(.752, -.496, -2.455, rep(0, 8)),
HH = c(-.693, -.143, -4.167, rep(0, 8)))
Betas$LH <- Betas$LL + Betas$LH
Betas$HL <- Betas$LL + Betas$HL
Betas$HH <- Betas$LL + Betas$HH
plotMilk(Betas = Betas, Thresh = 200, Parit = 3)
Threshold = 200,000 cells/mL; parity > 3
Betas <- data.frame(
coF = c("Int", "Dim", "I1Dim2", "I1Dim3", "I3fDim",
"cosT",
"sinT", "I1Dim.cosT", "I1Dim.sinT", "I23Dim.cosT",
"I23Dim.sinT"),
LL = c(35.17, -7.247, 3.741, 60.025, -.12, -.802, 1.304,
-1.972, 1.044, -.23, .061),
LH = c(-1.414, .396, -3.178, rep(0, 8)),
HL = c(.292, -.5471, -3.518, rep(0, 8)),
HH = c(-1.552, -.09, -4.37, rep(0, 8)))
Betas$LH <- Betas$LL + Betas$LH
Betas$HL <- Betas$LL + Betas$HL
Betas$HH <- Betas$LL + Betas$HH
plotMilk(Betas = Betas, Thresh = 200, Parit = 4)
Threshold = 100,000 cells/mL; parity 2
Betas <- data.frame(
coF = c("Int", "Dim", "I1Dim2", "I1Dim3", "I3fDim",
"cosT",
"sinT", "I1Dim.cosT", "I1Dim.sinT", "I23Dim.cosT",
"I23Dim.sinT"),
LL = c(32.593, -6.003, 7.824, 59.528, -.15, -.664, 1.624,
-1.287, .908, -.038, -.118),
LH = c(-.622, .218, -4.076, rep(0, 8)),
HL = c(.519, -.348, -1.054, rep(0, 8)),
HH = c(-.694, .049, -2.713, rep(0, 8)))
Betas$LH <- Betas$LL + Betas$LH
Betas$HL <- Betas$LL + Betas$HL
Betas$HH <- Betas$LL + Betas$HH
plotMilk(Betas = Betas, Thresh = 100, Parit = 2)
Threshold = 100,000 cells/mL; parity 3
Betas <- data.frame(
coF = c("Int", "Dim", "I1Dim2", "I1Dim3", "I3fDim",
"cosT",
"sinT", "I1Dim.cosT", "I1Dim.sinT", "I23Dim.cosT",
"I23Dim.sinT"),
LL = c(34.592, -6.651, 6.093, 61.814, -.178, -.7, 1.405,
-1.397, .966, -.187, -.025),
LH = c(-.768, .193, -2.988, rep(0, 8)),
HL = c(0.867, -.682, -2.761, rep(0, 8)),
HH = c(-.325, -.247, -4.451, rep(0, 8)))
Betas$LH <- Betas$LL + Betas$LH
Betas$HL <- Betas$LL + Betas$HL
Betas$HH <- Betas$LL + Betas$HH
plotMilk(Betas = Betas, Thresh = 100, Parit = 3)
Threshold = 100,000 cells/mL; parity > 3
Betas <- data.frame(
coF = c("Int", "Dim", "I1Dim2", "I1Dim3", "I3fDim",
"cosT",
"sinT", "I1Dim.cosT", "I1Dim.sinT", "I23Dim.cosT",
"I23Dim.sinT"),
LL = c(35.26, -7.145, 6.444, 64.402, -.082, -.745, 1.276,
-1.808, 1.017, -.294, .017),
LH = c(-1.145, .343, -3.289, rep(0, 8)),
HL = c(.435, -.572, -3.989, rep(0, 8)),
HH = c(-1.291, .007, -4.097, rep(0, 8)))
Betas$LH <- Betas$LL + Betas$LH
Betas$HL <- Betas$LL + Betas$HL
Betas$HH <- Betas$LL + Betas$HH
plotMilk(Betas = Betas, Thresh = 100, Parit = 4)