library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.1 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
library(plotly)
##
## Attaching package: 'plotly'
##
## The following object is masked from 'package:ggplot2':
##
## last_plot
##
## The following object is masked from 'package:stats':
##
## filter
##
## The following object is masked from 'package:graphics':
##
## layout
# Define function simulate plant growth based on
#soil moisture content
simulate_plant_growth <- function(n = 500,
alpha = 2,
beta1 = 20,
rse = 8,
moisture_range = c(0.1, 5.0)) {
# Generates randomized soil moisture values within range
soil_moisture <- runif(n, min = moisture_range[1], max = moisture_range[2])
# Generates random error values using normal distribution
error <- rnorm(n, mean = 8, sd = rse)
# Growth rate linear regression equation
growth_rate <- alpha + beta1 * soil_moisture + error
# Add soil moisture and growth rate to same data frame
data.frame(
soil_moisture = soil_moisture,
growth_rate = growth_rate
)
}
# Simulate plant growth data w/baseline parameters
data_single <- simulate_plant_growth()
head(data_single)
## soil_moisture growth_rate
## 1 2.2074530 46.70344
## 2 0.1299629 14.43521
## 3 1.4455090 45.87660
## 4 3.7324001 84.94240
## 5 1.0463439 33.10439
## 6 1.9395232 50.88101
fit_single <- lm(growth_rate ~ soil_moisture, data = data_single)
# Extract fitted values and residuals
data_single <- data_single %>%
mutate(
fitted = predict(fit_single),
resid_model = growth_rate - fitted, # Residuals from line (SSE)
resid_mean = growth_rate - mean(growth_rate)
) # Deviations from Y_bar (SST)
# Summary table
summary(fit_single)
##
## Call:
## lm(formula = growth_rate ~ soil_moisture, data = data_single)
##
## Residuals:
## Min 1Q Median 3Q Max
## -28.8926 -5.2028 -0.2195 5.4681 22.9886
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.4114 0.7187 13.09 <2e-16 ***
## soil_moisture 20.3529 0.2466 82.53 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.809 on 498 degrees of freedom
## Multiple R-squared: 0.9319, Adjusted R-squared: 0.9317
## F-statistic: 6811 on 1 and 498 DF, p-value: < 2.2e-16
#ANOVA table
anova(fit_single)
## Analysis of Variance Table
##
## Response: growth_rate
## Df Sum Sq Mean Sq F value Pr(>F)
## soil_moisture 1 415261 415261 6810.5 < 2.2e-16 ***
## Residuals 498 30365 61
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
\[ Y_i = \beta_0 + \beta_1X_i + \varepsilon_i \] ## Question 3. ### Display Regression Coefficients
coef(fit_single)
## (Intercept) soil_moisture
## 9.411372 20.352868
\[ \hat{Y}_i = b_0 + b_1X_i = 9.411372 + 20.352868X_i \]
#Total Variance
SST <- sum(data_single$resid_mean^2)
#Residual Variance
SSE <- sum(data_single$resid_model^2)
#Sum Of Squares For Regression
SSR <- SST - SSE
#Mean Square Error
MSE <- SSE / df.residual(fit_single)
#F Statistic
F_Stat <- (SSR / 1) / MSE
#Coefficient Of Determination
R2 <- SSR / SST
##Or
R2 <- 1 - (SSE / SST)
ggplot(data_single, aes(x = soil_moisture, y = growth_rate)) +
geom_point(alpha = 0.4) +
geom_smooth( #Linear Regression line, and Confidence Band
method = "lm",
formula = y ~ x,
color = "red",
se = TRUE,
fill = "blue", alpha = 0.7, linewidth = 0.5
) +
geom_segment( #Residual diff between observed/actual growth rate
aes(xend = soil_moisture, yend = predict(fit_single)),
alpha = 0.4,
color = "green"
) +
theme_classic() +
labs(
title = "Model Residuals (Vertical Distance to Regression Line)",
x = "Soil Moisture Content",
y = "Growth Rate"
)