library(readxl)
library(lubridate)
library(tidyverse)
library(plotly)
library(stargazer)
library(Metrics)
## Warning: package 'Metrics' was built under R version 4.1.1
AIM : To compare HOBO (lux) and LiCOR (PAR) data. Found out conversion rate between LUX and PAR. Explore potential biaises of relationship with Depth or HOBO sensors in Marlborough Sounds - data from Dan Crossett. Compare linear and non linear regression following Long et al. (2012).
Document Note : The maps, plots and app within this document are interactive so make sure you give them a play like zooming in and out in the maps but also on the plots. Clicking on the legend allows to only select and display the time series needed.
hobo_licor <- read_excel('QCS_licor_hobo_calibration.xlsx')
## Plot Hobo vs PAR - No distinction of Depth, Time (hour), Site
p <- ggplot(hobo_licor,aes(x=Lux,y=PAR)) +
geom_point() +
geom_smooth(formula = y ~ x, se=T, method = 'lm') +
theme_light() +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=20))
ggplotly(p)
Figure 1 - PAR vs LUX and linear regression.
\(R^2\) of linear fit:
lm_fit <- lm(PAR ~ Lux, data = hobo_licor) #%>% tidy() %>% kable()
#summary(lm_fit)#$r.squared
#lm(Kd_MW ~ Kd_NN, data = nn_mw_idw_tb) %>% kable()
stargazer(lm_fit,title="Regression Results",type = "text", style = 'default')#"html"
##
## Regression Results
## ===============================================
## Dependent variable:
## ---------------------------
## PAR
## -----------------------------------------------
## Lux 0.023***
## (0.001)
##
## Constant 196.467***
## (15.920)
##
## -----------------------------------------------
## Observations 292
## R2 0.678
## Adjusted R2 0.677
## Residual Std. Error 202.093 (df = 290)
## F Statistic 610.844*** (df = 1; 290)
## ===============================================
## Note: *p<0.1; **p<0.05; ***p<0.01
\(R^2\) = .678, probably not too bad but definitely some variability. Due to what? Sensors? Depth?
\(PAR = 0.023*LUX + 196.467\)
p <- ggplot(hobo_licor,aes(x=Lux,y=PAR,col=Site,group=Site)) +
geom_point() +
geom_smooth(formula = y ~ x, se=T, method = 'lm') +
theme_light() +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=20))
ggplotly(p)
Figure 2 - PAR vs LUX and effect of sensor type on linear regression.
\(R^2\) of linear fit:
lm_fit_siteDC <- hobo_licor %>%
dplyr::filter(Site == 'DC') %>%
lm(PAR ~ Lux, data = .)
lm_fit_siteSB2 <- hobo_licor %>%
dplyr::filter(Site == 'SB 2') %>%
lm(PAR ~ Lux, data = .)
lm_fit_siteSC1 <- hobo_licor %>%
dplyr::filter(Site == 'SC 1') %>%
lm(PAR ~ Lux, data = .)
stargazer(lm_fit_siteDC,lm_fit_siteSB2,lm_fit_siteSC1, title="Regression Results for Sites DC (1), SB 2 (2) and SC 1 (3).",type = "text", style = 'default')#"html"
##
## Regression Results for Sites DC (1), SB 2 (2) and SC 1 (3).
## ===========================================================================================
## Dependent variable:
## -----------------------------------------------------------------------
## PAR
## (1) (2) (3)
## -------------------------------------------------------------------------------------------
## Lux 0.021*** 0.028*** 0.019***
## (0.002) (0.002) (0.003)
##
## Constant 278.212*** 58.328*** 273.388***
## (41.052) (16.578) (21.035)
##
## -------------------------------------------------------------------------------------------
## Observations 104 94 94
## R2 0.626 0.789 0.358
## Adjusted R2 0.622 0.787 0.351
## Residual Std. Error 280.790 (df = 102) 111.229 (df = 92) 109.306 (df = 92)
## F Statistic 170.419*** (df = 1; 102) 344.085*** (df = 1; 92) 51.338*** (df = 1; 92)
## ===========================================================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
Variability in \(R^2\): 0.789 for site SB 2 vs 0.358 for site SC 1.
p <- ggplot(hobo_licor,aes(x=Lux,y=PAR,col=as.character(HOBO),group=HOBO)) +
geom_point() +
geom_smooth(formula = y ~ x, se=T, method = 'lm') +
theme_light() +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=20))
ggplotly(p)
Figure 3 - PAR vs LUX and effect of sensor type on linear regression.
\(R^2\) of linear fit:
lm_fit_sensor40 <- hobo_licor %>%
dplyr::filter(HOBO == 40) %>%
lm(PAR ~ Lux, data = .)
lm_fit_sensor21 <- hobo_licor %>%
dplyr::filter(HOBO == 21) %>%
lm(PAR ~ Lux, data = .)
stargazer(lm_fit_sensor21,lm_fit_sensor40, title="Regression Results for Sensors 21 (1), 40 (2).",type = "text", style = 'default')#"html"
##
## Regression Results for Sensors 21 (1), 40 (2).
## ===========================================================
## Dependent variable:
## ----------------------------
## PAR
## (1) (2)
## -----------------------------------------------------------
## Lux 0.025*** 0.022***
## (0.001) (0.001)
##
## Constant 144.280*** 236.249***
## (23.301) (21.522)
##
## -----------------------------------------------------------
## Observations 146 146
## R2 0.708 0.667
## Adjusted R2 0.706 0.665
## Residual Std. Error (df = 144) 192.985 206.436
## F Statistic (df = 1; 144) 348.612*** 288.821***
## ===========================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
Variability in \(R^2\): .708 for sensor 21 vs .667 for sensor 40.
p <- ggplot(hobo_licor,aes(x=Lux,y=PAR)) +
facet_wrap(~Depth, scales = 'free') +
geom_point() +
geom_smooth(formula = y ~ x, se=T, method = 'lm') +
theme_light() +
theme(axis.text=element_text(size=8),
axis.title=element_text(size=12))
ggplotly(p)
Figure 3 - PAR vs LUX and effect of depth on linear regression.
\(R^2\) of linear fit:
lm_fit_depth1 <- hobo_licor %>%
dplyr::filter(Depth == '1') %>%
lm(PAR ~ Lux, data = .)
lm_fit_depth2 <- hobo_licor %>%
dplyr::filter(Depth == '2') %>%
lm(PAR ~ Lux, data = .)
lm_fit_depth3 <- hobo_licor %>%
dplyr::filter(Depth == '3') %>%
lm(PAR ~ Lux, data = .)
lm_fit_depth4 <- hobo_licor %>%
dplyr::filter(Depth == '4') %>%
lm(PAR ~ Lux, data = .)
lm_fit_depth5 <- hobo_licor %>%
dplyr::filter(Depth == '5') %>%
lm(PAR ~ Lux, data = .)
lm_fit_depthascent <- hobo_licor %>%
dplyr::filter(Depth == 'ascent') %>%
lm(PAR ~ Lux, data = .)
stargazer(lm_fit_depth1,lm_fit_depth2,lm_fit_depth3,lm_fit_depth4,lm_fit_depth5,lm_fit_depthascent, title="Regression Results for Depth 1m (1), 2m (2), 3m (3), 4m (4), 5m (5) and ascent (6).",type = "text", style = 'default')#"html"
##
## Regression Results for Depth 1m (1), 2m (2), 3m (3), 4m (4), 5m (5) and ascent (6).
## ==========================================================================================================================================================
## Dependent variable:
## --------------------------------------------------------------------------------------------------------------------------------------
## PAR
## (1) (2) (3) (4) (5) (6)
## ----------------------------------------------------------------------------------------------------------------------------------------------------------
## Lux 0.017*** 0.014*** 0.026*** 0.025*** 0.033*** 0.002
## (0.003) (0.002) (0.004) (0.004) (0.002) (0.021)
##
## Constant 486.388*** 437.161*** 113.277* 175.608*** 75.124*** 333.179**
## (89.256) (38.852) (58.028) (48.420) (13.128) (103.203)
##
## ----------------------------------------------------------------------------------------------------------------------------------------------------------
## Observations 40 48 26 36 136 6
## R2 0.492 0.644 0.647 0.593 0.644 0.002
## Adjusted R2 0.478 0.636 0.632 0.581 0.641 -0.247
## Residual Std. Error 345.402 (df = 38) 167.527 (df = 46) 205.991 (df = 24) 203.369 (df = 34) 55.120 (df = 134) 54.301 (df = 4)
## F Statistic 36.733*** (df = 1; 38) 83.089*** (df = 1; 46) 43.955*** (df = 1; 24) 49.544*** (df = 1; 34) 242.442*** (df = 1; 134) 0.010 (df = 1; 4)
## ==========================================================================================================================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
Highest \(R^2\) at depth 5m, lowest for ascent (makes sense). Presence of “clusters” indicative of 2 different time of the day (morning vs afternoon).
p <- ggplot(hobo_licor,aes(x=Lux,y=PAR,color=as.character(HOBO),group=HOBO)) +
facet_wrap(~Depth, scales = 'free') +
geom_point() +
geom_smooth(formula = y ~ x, se=T, method = 'lm') +
theme_light() +
theme(axis.text=element_text(size=8),
axis.title=element_text(size=12),
strip.text = element_text(size=12))
ggplotly(p)
Figure 4 - PAR vs LUX and effect of sensor type and depth on linear regression.
Limitation:
In Long et al. (2012),they assume a saturating behavior of PAR values (LiCOR) for high LUX values (from HOBO). They use the equation: \(PAR_{LiCOR} = A_1 \exp(-HOBO/t_1) + y_0\).
model_long <- nls(PAR ~ A1*exp(-Lux/t1) + y0, data=hobo_licor, start = list(A1 = -20000, t1 = 20000, y0 = 2000))
summary(model_long)
##
## Formula: PAR ~ A1 * exp(-Lux/t1) + y0
##
## Parameters:
## Estimate Std. Error t value Pr(>|t|)
## A1 -1725.8 145.6 -11.856 < 2e-16 ***
## t1 38386.4 5988.0 6.411 5.89e-10 ***
## y0 1791.5 157.0 11.411 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 180.8 on 289 degrees of freedom
##
## Number of iterations to convergence: 6
## Achieved convergence tolerance: 1.907e-06
Coefficients are compared to Long et al. (2012):
Quite lower than Long et al. (2012) except t1, not same range of LUX might be why + data quantity.
p <- ggplot() +
geom_point(data=hobo_licor,aes(x=Lux,y=PAR)) +
geom_line(data=hobo_licor,aes(x=Lux,y=predict(model_long)),col='red',size=1) +
geom_line(data=hobo_licor,aes(x=Lux,y=predict(lm_fit)),col='blue',size=1)
ggplotly(p)
Figure 5 - PAR vs LUX, linear regression (blue) and exponential regression following Long et al. (2012) (red).
Let’s compare the RMSE between models:
paste0('Long Expo Model RMSE =',rmse(hobo_licor$PAR,predict(model_long)))
## [1] "Long Expo Model RMSE =179.868979906591"
paste0('Linear Model RMSE =',rmse(hobo_licor$PAR,predict(lm_fit)))
## [1] "Linear Model RMSE =201.400055220429"
And let’s compare the AIC between models:
AIC(model_long,lm_fit)
## df AIC
## model_long 4 3868.922
## lm_fit 3 3932.951
The exponential model seems better. But we porbably need more data at higher LUX.