|
| 1 | +--- |
| 2 | +title: "Climate Data Processing" |
| 3 | +description: "Import and summarise hourly climate records; compute daily temperatures and growing degree-days (GDD) per sowing season." |
| 4 | +--- |
| 5 | + |
| 6 | +```{r setup, include=FALSE} |
| 7 | +knitr::opts_chunk$set( |
| 8 | + cache = FALSE, |
| 9 | + comment = "##", |
| 10 | + collapse = TRUE, |
| 11 | + warning = FALSE, |
| 12 | + message = FALSE |
| 13 | +) |
| 14 | +``` |
| 15 | + |
| 16 | +## Overview |
| 17 | + |
| 18 | +This section imports hourly weather station data, aggregates it to daily summaries, |
| 19 | +and derives the **accumulated growing degree-days (GDD)** used as the thermal-time |
| 20 | +variable for all subsequent growth models. |
| 21 | + |
| 22 | +::: {.callout-note} |
| 23 | +GDD is calculated as the mean daily temperature minus a base temperature of 5 °C, |
| 24 | +which is commonly adopted for flax (*Linum usitatissimum* L.). |
| 25 | +::: |
| 26 | + |
| 27 | +## Required packages |
| 28 | + |
| 29 | +```{r libraries} |
| 30 | +library(rio) # Flexible data import (import()) |
| 31 | +library(tidyverse) # Data manipulation and visualisation (dplyr, ggplot2, …) |
| 32 | +library(lubridate) # Date parsing helpers (dmy(), ymd()) |
| 33 | +library(ggridges) # Ridge/density plots (geom_density_ridges_gradient()) |
| 34 | +``` |
| 35 | + |
| 36 | +## Import and structure climate data |
| 37 | + |
| 38 | +```{r import-climate} |
| 39 | +# Import the raw CSV file. |
| 40 | +# 'dec = ","' handles the Brazilian decimal comma format. |
| 41 | +dft <- |
| 42 | + import("../clima.csv", dec = ",") |> |
| 43 | + # The 'hora' column stores date as "DD/MM/YYYY HH:MM" → split into parts |
| 44 | + separate(hora, into = c("dia", "mes", "ano")) |> |
| 45 | + # Rebuild a single date string and parse it with lubridate |
| 46 | + unite("data", dia, mes, ano, sep = "/") |> |
| 47 | + mutate(data = dmy(data)) |
| 48 | +``` |
| 49 | + |
| 50 | +## Daily temperature summary |
| 51 | + |
| 52 | +```{r daily-summary} |
| 53 | +# Aggregate hourly records to daily statistics. |
| 54 | +# Variables: |
| 55 | +# tmin / tmed / tmax : minimum, mean, maximum temperature (°C) |
| 56 | +# prec : daily rainfall (mm) |
| 57 | +# ur : mean relative humidity (%) |
| 58 | +# gd : growing degree-days via mean temperature (Tmean – 5) |
| 59 | +# gd2 : growing degree-days via Tmax+Tmin average ((Tmax+Tmin)/2 – 5) |
| 60 | +dftemp <- |
| 61 | + dft |> |
| 62 | + group_by(data) |> |
| 63 | + summarise( |
| 64 | + tmin = min(tmin), |
| 65 | + tmed = mean(tmed), |
| 66 | + tmax = max(tmax), |
| 67 | + prec = sum(prec), |
| 68 | + ur = mean(ur) |
| 69 | + ) |> |
| 70 | + mutate( |
| 71 | + gd = tmed - 5, # GDD method 1: mean temperature |
| 72 | + gd2 = ((tmax + tmin) / 2) - 5 # GDD method 2: (Tmax + Tmin) / 2 |
| 73 | + ) |> |
| 74 | + mutate(data = ymd(data)) |
| 75 | +``` |
| 76 | + |
| 77 | +## Split data by sowing season |
| 78 | + |
| 79 | +```{r sowing-seasons} |
| 80 | +# Season E1: rows 1–79 (first sowing date) |
| 81 | +dftempe1 <- |
| 82 | + dftemp |> |
| 83 | + slice(1:79) |> |
| 84 | + mutate(season = "E1") |
| 85 | +
|
| 86 | +# Season E2: rows 79–148 (second sowing date, one row overlap ensures continuity) |
| 87 | +dftempe2 <- |
| 88 | + dftemp |> |
| 89 | + slice(79:148) |> |
| 90 | + mutate(season = "E2") |
| 91 | +
|
| 92 | +# Combine seasons and compute cumulative GDD within each season |
| 93 | +dftemp2 <- |
| 94 | + bind_rows(dftempe1, dftempe2) |> |
| 95 | + relocate(season, .before = data) |> |
| 96 | + group_by(season) |> |
| 97 | + mutate( |
| 98 | + gda = cumsum(gd), # Cumulative GDD – method 1 |
| 99 | + gda2 = cumsum(gd2) # Cumulative GDD – method 2 |
| 100 | + ) |> |
| 101 | + # Reformat the date for display (DD/MM) |
| 102 | + separate(data, into = c("ano", "mes", "dia")) |> |
| 103 | + unite("data", dia, mes, sep = "/") |
| 104 | +
|
| 105 | +dftemp2 |> group_by(season) |> summarise(sum(prec)) |
| 106 | +``` |
| 107 | + |
| 108 | +## Ridge plot – temperature distribution by month |
| 109 | + |
| 110 | +```{r ridge-plot, fig.cap="Distribution of maximum daily temperatures by month. Higher temperatures in summer months (Dec–Feb) contrast with cooler autumn/winter records."} |
| 111 | +dft |> |
| 112 | + # Extract month from the date column for grouping on the y-axis |
| 113 | + separate(data, into = c("ano", "mes", "dia")) |> |
| 114 | + ggplot(aes(x = tmax, y = mes, fill = after_stat(x))) + |
| 115 | + geom_density_ridges_gradient() + |
| 116 | + scale_fill_viridis_c() + |
| 117 | + labs( |
| 118 | + x = "Maximum temperature (°C)", |
| 119 | + y = "Month of the year", |
| 120 | + fill = "Max. temperature\n(°C)" |
| 121 | + ) + |
| 122 | + theme_bw(base_size = 14) |
| 123 | +``` |
| 124 | + |
| 125 | +## Temperature and rainfall overview |
| 126 | + |
| 127 | +```{r temp-rain-plot, fig.cap="Daily maximum (red) and minimum (blue) temperatures with LOESS smoothing, and daily rainfall (sky-blue bars) across the study period.", out.width="100%"} |
| 128 | +ggplot() + |
| 129 | + # Rainfall bars (scaled to secondary axis: raw mm × 30/100) |
| 130 | + geom_bar( |
| 131 | + dftemp, |
| 132 | + mapping = aes(x = data, y = prec * 30 / 100), |
| 133 | + stat = "identity", |
| 134 | + fill = "skyblue" |
| 135 | + ) + |
| 136 | + # Raw daily Tmax and Tmin lines (transparent) |
| 137 | + geom_line(dftemp, mapping = aes(x = data, y = tmax, colour = "red"), linewidth = 1, alpha = 0.1) + |
| 138 | + geom_line(dftemp, mapping = aes(x = data, y = tmin, colour = "blue"), linewidth = 1, alpha = 0.1) + |
| 139 | + # LOESS smoothed trends for Tmax and Tmin |
| 140 | + geom_smooth(dftemp, mapping = aes(x = data, y = tmax, colour = "red"), linewidth = 1, se = FALSE) + |
| 141 | + geom_smooth(dftemp, mapping = aes(x = data, y = tmin, colour = "blue"), linewidth = 1, se = FALSE) + |
| 142 | + # X-axis: dates every 15 days |
| 143 | + scale_x_date( |
| 144 | + date_breaks = "15 days", |
| 145 | + date_labels = "%d/%m", |
| 146 | + expand = expansion(c(0, 0)) |
| 147 | + ) + |
| 148 | + # Primary Y-axis: temperature; secondary Y-axis: rainfall (rescaled back to mm) |
| 149 | + scale_y_continuous( |
| 150 | + name = expression("Temperature (" ~ degree ~ "C)"), |
| 151 | + sec.axis = sec_axis(~ . * 100 / 30, name = "Rainfall (mm)") |
| 152 | + ) + |
| 153 | + # Colour legend with descriptive labels |
| 154 | + scale_color_identity( |
| 155 | + breaks = c("red", "blue"), |
| 156 | + labels = c("Maximum temperature (°C)", "Minimum temperature (°C)"), |
| 157 | + guide = "legend" |
| 158 | + ) + |
| 159 | + labs( |
| 160 | + x = "Day of the year", |
| 161 | + color = "" |
| 162 | + ) + |
| 163 | + theme_bw(base_size = 16) + |
| 164 | + theme( |
| 165 | + panel.grid.major = element_blank(), |
| 166 | + legend.background = element_rect(fill = "transparent"), |
| 167 | + legend.position = "bottom", |
| 168 | + axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1) |
| 169 | + ) |
| 170 | +
|
| 171 | +ggsave("../figs/temperature.jpg", width = 12, height = 6) |
| 172 | +``` |
0 commit comments