How to analyze quadratic plateau model in R Studio?

How to analyze quadratic plateau model in R Studio?

# If you copy and paste this code to your R console, you can obtain the same graph above.

if(!require(remotes)) install.packages("readr")
if(!require(ggplot2)) install.packages("ggplot2")
if(!require(minpack.lm)) install.packages("minpack.lm")
if(!require(nlraa)) install.packages("nlraa")
library (readr)
library(ggplot2)
library(minpack.lm)
library(nlraa)

github="https://raw.githubusercontent.com/agronomy4future/raw_data_practice/main/sulphur%20application.csv"
df=data.frame(read_csv(url(github),show_col_types = FALSE))

fit.lm= lm(yield ~ poly(sulphur,2, raw=TRUE),data=df)
a_parameter= fit.lm$coefficients[1]
b_parameter= fit.lm$coefficients[2]
c_parameter= fit.lm$coefficients[3]
x_mean= mean(df$sulphur)

qp= function(x, a, b, jp) {
  c= -0.5 * b / jp
  ifelse(x < jp,
          a + (b * x) + (c * x * x),
          a + (b * jp) + (c * jp * jp))
}

quadratic= lm(yield~poly(sulphur,2,raw= TRUE), data= df)
parameter= coef(quadratic)

model2= nls(formula=yield~qp(sulphur, a, b, jp),
            data=df,
            start=list(a=parameter[1],
                       b=parameter[2],
                       jp=mean(df$sulphur)))

df %>%
  ggplot(aes(sulphur, yield)) +
  geom_point(size=4, alpha = 0.5, color="grey15") +
  geom_line(stat="smooth",
            method="nls",
            formula=y ~ SSquadp3xs(x, a, b, jp),
            se=FALSE,
            color="darkred") +
          
  geom_vline(xintercept=25.0656, linetype="solid", color="grey") +
  annotate("text", label=paste("sulphur=","25.1","kg/ha"),
           x=25.2, y=1000, angle=90, hjust=0, vjust=1.5, size=5)+  
                            
  scale_x_continuous(breaks = seq(18,28,2), limits = c(18,28)) +
  scale_y_continuous(breaks = seq(1000,1500,100), limits = c(1000,1500)) +
  labs(x="Sulphur application (kg/ha)", y="Yield (kg/ha)") +
  theme_classic (base_size=18, base_family="serif")+
  theme(legend.position="none",
        axis.line=element_line(linewidth=0.5, colour="black"))
Previous post
How to analyze linear plateau model in R Studio?

In my previous post, I explained how to analyze the linear plateau model. I simulated yield data for five different crop varieties with varying sulphur applications and suggested that the optimum sulphur application would be 23.3 kg/ha based on the linear plateau model. In this post, I’ll explain how to analyze the quadratic plateau model using the same data in R Studio.

1) Data upload

If you run the below code, the data which I simulated will be uploaded to R.

if(!require(remotes)) install.packages("readr")
library (readr)

github="https://raw.githubusercontent.com/agronomy4future/raw_data_practice/main/sulphur%20application.csv"
df=data.frame(read_csv(url(github),show_col_types = FALSE))

print(head(df,3))
  variety sulphur yield
1     CV1   20.67  1286
2     CV1   19.90  1165
3     CV1   19.73  1176
.
.
.

Or, you can directly generate data like below.

variety= rep(c("CV1","CV2","CV3","CV4","CV5"), each=9)
sulphur= c(20.67, 19.9, 19.73,21.16, 21.61, 20.9, 22.36, 21.97, 21.9, 21.34, 21.28, 
           21.9, 20.7, 22.65, 22.16, 22.03, 20.4, 22.47, 22.65, 21.82, 24.41, 22.34, 
           24.03, 24.38, 23.6, 23.44, 23.51, 23.89, 24.7, 22.8, 23.08, 25.05, 24.09, 
           24.3, 25.23, 23.14, 24.06, 26.98, 25.71, 23.93, 25.16, 23.79, 26.34, 24.64, 
           24.77)
yield= c(1286, 1165, 1176, 1262, 1271, 1221, 1374, 1288, 1285, 1261, 1281, 1302, 1199, 
         1368, 1340, 1272, 1276, 1329, 1361, 1267, 1380, 1386, 1408, 1380, 1378, 1413, 
         1400, 1412, 1422, 1381, 1410, 1410, 1381, 1422, 1389, 1403, 1420, 1403, 1400, 
         1420, 1379, 1393, 1417, 1415, 1383)
df= data.frame(variety,sulphur,yield)

Let’s assume that this data is about yield data for five different crop varieties with different sulphur applications.

2) General quadratic regression model

First of all, I’ll analyze general quadratic regression model.

if(!require(remotes)) install.packages("readr")
library (ggplot2)

ggplot(data=df, aes(x=sulphur, y=yield)) +
  geom_point(aes(shape=variety, fill=variety),col="black", size=5, stroke = 0.5) +
  geom_smooth(method=lm, level=0.95, se=FALSE, linetype=1, size=0.5, color="Red", 
              formula=y~poly(x,2, raw=TRUE)) +
  scale_shape_manual(values=rep(c(21),5)) +
  scale_fill_manual(values=rep(c("grey55"),5))+
  scale_x_continuous(breaks=seq(15,30,5), limits=c(15,30)) +
  scale_y_continuous(breaks=seq(500,1800,300), limits=c(500,1800)) +
  labs(x="sulphur application (kg/ha)", y="Yield (kg/ha)") +
  theme_classic(base_size=15, base_family="serif")+
  theme(legend.position="none",
        axis.line=element_line(linewidth=0.5, colour="black"))

I obtained the graph like above. Now I’ll do statistical analysis for the quadratic regression model.

regression= lm(yield ~ poly(sulphur,2, raw=TRUE), data=df)
summary(regression)

Call:
lm(formula = yield ~ poly(sulphur, 2, raw = TRUE), data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-51.536 -23.671   1.901  17.072  60.441 

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)    
(Intercept)                   -3707.735    643.594  -5.761 8.76e-07 ***
poly(sulphur, 2, raw = TRUE)1   403.477     55.829   7.227 6.90e-09 ***
poly(sulphur, 2, raw = TRUE)2    -7.948      1.206  -6.588 5.67e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 27.53 on 42 degrees of freedom
Multiple R-squared:  0.8637,	Adjusted R-squared:  0.8572 
F-statistic: 133.1 on 2 and 42 DF,  p-value: < 2.2e-16

It shows the quadratic term is significant, and also regression variance is significant. Therefore, I can suggest the model equation; y= -3707.735 + 403.4772x - 7.948x2.

Now, I’d like to find the optimum sulphur amount to bring about the greatest yield. Simply we can ‘predict’ according to the model equation, but I’d like to know the critical value as a break point (the x-value above which there is no further increase in y), indicating the ‘plateau value’ (the statistically highest value that y reaches) as quadratic regression model. This is called ‘Quadratic plateau model.’

3) Quadratic plateau model

Now, I’ll introduce how to analyze quadratic plateau model in R step by step.

3.1) to find reasonable initial values for parameters

fit.lm= lm(yield ~ poly(sulphur,2, raw=TRUE),data=df)
a_parameter= fit.lm$coefficients[1]
b_parameter= fit.lm$coefficients[2]
c_parameter= fit.lm$coefficients[3]
x_mean= mean(dataA$sulphur)

Now, the general values for quadratic regression were calculated again.

a_parameter = intercept, 
b_parameter = slope x,
c_parameter = slope x2
x_mean = average of sulphur.

These values are the same as model equation, y= -3707.735 + 403.4772x - 7.948x2

3.2) to define quadratic plateau function

Quadratic plateau model is defined as below.

# a = intercept
# b = slope
# c = quadratic term (curvy bit)
# jp = join point = break point

qp= function(x, a, b, jp) {
  c= -0.5 * b / jp
  ifelse(x < jp,
          a + (b * x) + (c * x * x),
          a + (b * jp) + (c * jp * jp))
}

FYI, this is the definition of linear plateau model.

#a = intercept
#b = slope
#jp = join point or break point 

linplat= function(x, a, b, jp){
  ifelse(x<jp, a+b*x,
         a+b*jp)
}

3.3) to find the best fit parameters

if(!require(dplyr)) install.packages("dplyr")
library(dplyr)

model= nls(formula=yield ~ qp(sulphur, a, b, jp),
           data=df,
           start=list(a=a_parameter, b=b_parameter, jp=x_mean))

summary(model)
Formula: yield ~ qp(sulphur, a, b, jp)

Parameters:
     Estimate Std. Error t value Pr(>|t|)    
a  -4174.2608   923.9016  -4.518 5.01e-05 ***
b    445.3539    81.5181   5.463 2.34e-06 ***
jp    25.0656     0.4925  50.897  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 27.33 on 42 degrees of freedom

Number of iterations to convergence: 10 
Achieved convergence tolerance: 5.685e-06

I obtained the quadratic plateau model equation.

y≈ -4174.26 + 445.35x + __x2.

This code does not provide quadratic parameter, but simply we can calculate by hand.

c= -0.5 * b / jp

c= -0.5 * 445.3539 / 25.0656 ≈ -8.88

and critical value is 25.0656. Then, quadratic plateau model would be written as

y≈ -4174.26 + 445.35x -8.88x2 (if x < 25.1)

This is another code to find the best fit parameters

quadratic= lm(yield~poly(sulphur,2,raw= TRUE), data= df)
parameter= coef(quadratic)

model= nls(formula=yield~qp(sulphur, a, b, jp),
            data=df,
            start=list(a=parameter[1],
                       b=parameter[2],
                       jp=mean(df$sulphur)))

summary(model)

----------- or ----------- 

if(!require(minpack.lm)) install.packages("minpack.lm")
library(minpack.lm)

model= nlsLM(formula=yield~qp(sulphur, a, b, jp),
             data=df,
             start=list(a=a_parameter,
                         b=b_parameter,
                         jp=x_mean))

summary(model)

If you use nlraa() package, you can obtain the model equation at once without extra coding. However, it would be better to follow what I suggested step by step to understand the concept of quadratic plateau model.

if(!require(nlraa)) install.packages("nlraa")
library(nlraa)

model= nlsLM(formula=yield~SSquadp3xs(sulphur,a,b,jp),
       data=df)

summary(model)
Formula: yield ~ SSquadp3xs(sulphur, a, b, jp)

Parameters:
     Estimate Std. Error t value Pr(>|t|)    
a  -4174.1127   923.8811  -4.518 5.01e-05 ***
b    445.3407    81.5162   5.463 2.34e-06 ***
jp    25.0657     0.4925  50.896  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 27.33 on 42 degrees of freedom

Number of iterations to convergence: 7 
Achieved convergence tolerance: 1.49e-08

3.4) Visualization

if(!require(ggplot2)) install.packages("ggplot2")
if(!require(minpack.lm)) install.packages("minpack.lm")
if(!require(nlraa)) install.packages("nlraa")
library(ggplot2)
library(minpack.lm)
library(nlraa)

df %>%
  ggplot(aes(sulphur, yield)) +
  geom_point(size=4, alpha = 0.5, color="grey15") +
  geom_line(stat="smooth",
            method="nls",
            formula=y ~ SSquadp3xs(x, a, b, jp),
            se=FALSE,
            color="darkred") +
  scale_x_continuous(breaks = seq(18,28,2), limits = c(18,28)) +
  scale_y_continuous(breaks = seq(1000,1500,100), limits = c(1000,1500)) +
  labs(x="Sulphur application (kg/ha)", y="Yield (kg/ha)") +
  theme_classic (base_size=18, base_family="serif")+
  theme(legend.position="none",
        axis.line=element_line(linewidth=0.5, colour="black"))

The breaking point is 25.1 and at this point, yield would be 1,410 according to the model equation, y≈ -4174.26 + 445.35x -8.88x2 (if x < 25.1).

To visualize more clearly, more code will be added.

if(!require(ggplot2)) install.packages("ggplot2")
if(!require(minpack.lm)) install.packages("minpack.lm")
if(!require(nlraa)) install.packages("nlraa")
library(ggplot2)
library(minpack.lm)
library(nlraa)

df %>%
  ggplot(aes(sulphur, yield)) +
  geom_point(size=4, alpha = 0.5, color="grey15") +
  geom_line(stat="smooth",
            method="nls",
            formula=y ~ SSquadp3xs(x, a, b, jp),
            se=FALSE,
            color="darkred") +
          
  geom_vline(xintercept=25.0656, linetype="solid", color="grey") +
  annotate("text", label=paste("sulphur=","25.1","kg/ha"),
           x=25.2, y=1000, angle=90, hjust=0, vjust=1.5, size=5)+    
             
  scale_x_continuous(breaks = seq(18,28,2), limits = c(18,28)) +
  scale_y_continuous(breaks = seq(1000,1500,100), limits = c(1000,1500)) +
  labs(x="Sulphur application (kg/ha)", y="Yield (kg/ha)") +
  theme_classic (base_size=18, base_family="serif")+
  theme(legend.position="none",
        axis.line=element_line(linewidth=0.5, colour="black"))

3.5) How to interpret quadratic plateau model?

y≈ -4174.26 + 445.35x -8.88x2 (if x < 25.1)

In the model equation; y≈ -4174.26 + 445.35x -8.88x2 (if x < 25.1), the important thing is critical value, 25.1. Statistically, at this point, yield does not increase although sulphur application increases. Therefore, critical value is regarded as the highest value that yield reaches.

and when sulphur application is 25.1, yield would be

y≈ -4174.26 + 445.35*25.0656 – 8.88*25.0656^2 ≈ 1409.5

That is, when 25.1 S kg/ha was applied, yield would be 1409.5 kg/ha and no more yield increases, indicating the optimum sulphur application would be 25.1 kg/ha.

4) p-value and pseudo R-squared

nullfunct= function(x, m){m}
m.ini= mean(dataA$yield)
null= nls(yield~nullfunct(sulphur, m),
         data=df,
         start=list(m=m.ini),
         trace=FALSE,
         nls.control(maxiter= 1000))

library(rcompanion)
nagelkerke(model, null)
■ Code summary: https://github.com/agronomy4future/r_code/blob/main/How_to_analyze_quadratic_plateau_model_in_R_Studio.ipynb

Reference
https://gradcylinder.org/post/linear-plateau/
https://rcompanion.org/handbook/I_11.html


We aim to develop open-source code for agronomy ([email protected])

© 2022 – 2025 https://agronomy4future.com – All Rights Reserved.

Last Updated: 07/Nov/2025

Comments are closed.