4.4 Daily temperatures

Last updated

July 6, 2026

4.4 Daily temperatures

On this page

This page reformats raw temperature data from three monitoring programs (TCRMP, VINPS, CSUN) into one comparable daily record. We read each program’s raw logger files, collapse the sub-hourly readings to daily average, minimum, and maximum temperatures (which shrinks the dataset), and merge the three programs into a single table. The page produces one derived product, temperatureDaily (the s4pt4 daily-temperature table), and its metadata. Downstream page 4.5 reads this table to compute monthly means, annual means, and degree-heating weeks.

TCRMP and VINPS sites carry raw logger temperatures at 15 to 60 minute intervals. CSUN sites lack their own loggers, so we assign VINPS Yawzi temperatures to the CSUN sites White Point, Europa, West Little Lameshur Bay, Yawzi, and Neptune, and VINPS Tektite temperatures to the CSUN sites East Tektite, Tektite, and Cabritte Horn.1 This page holds no figures.

Data sources

This page reads raw logger temperatures from the TCRMP and VINPS monitoring programs, together with the site master table for program, site codes, and year added. CSUN sites reuse VINPS Yawzi and Tektite series. This page’s own derived daily-temperature table is available in the Downloads section below.

Site data

The site master table lists every monitoring site with its program, site code, and year added.

Show code
sitedat <-
  read.csv(
    "../../../RRSdata/00_RRS_dataCatalogStatus/00_RRS_siteMaster_allSites_data.csv"
  )

TCRMP

We import the raw TCRMP temperature data downloaded from the TCRMP website, average it to daily values, and filter it to the sites we keep.

We first point to the raw TCRMP temperature directory.

Show code
# tcrmp_raw_tempdir <-
#     "../../../RRSdata/data_TCRMP/TCRMP_temperature_processed_raw"
# tcrmp_raw_tempdir <-
#   "/Users/laurenkay/UVI Dropbox/SMITH LAB TEAM FOLDER/TCRMP/TCRMP_temperature/TCRMP_temperature_processed_raw" #absolute path added 1 April 2025
# tcrmp_raw_tempdir <- "/Users/laurenkay/Library/CloudStorage/Dropbox-UVI/Lauren Olinger/misc/tcrmp_temp_2025july/TCRMP_temperature_processed_raw" # new absolute path added 24 july
tcrmp_raw_tempdir <-
  "../../../RRSdata/data_TCRMP/TCRMP_temperature_database_combined" # local RRSdata copy (recent, through 2025; embargo trims to 2024). Replaces the dead Dropbox path.

We then list the per-site temperature files in that directory, keeping only the raw TCRMP_temp_ files and dropping the combined daily-DHW file.

Show code
tcrmptempdir <- dir(tcrmp_raw_tempdir)
tcrmptempdir <- tcrmptempdir[str_detect(tcrmptempdir,"TCRMP_temp_")]
tcrmptempdir <- tcrmptempdir[tcrmptempdir != "TCRMP_temp_Daily_Temp_DHW.csv"]  # skip the combined DHW file; keep only per-site raw files

Validation note: the directory holds 43 raw TCRMP site files. The first few are TCRMP_temp_Black_Point_raw.csv, TCRMP_temp_Botany_Bay_raw.csv, TCRMP_temp_Brewers_Bay_raw.csv, TCRMP_temp_Buck_Island_STT_raw.csv, TCRMP_temp_Buck_Island_STX_Deep_raw.csv, TCRMP_temp_Buck_Island_STX_raw.csv.

We build sitekp, the set of TCRMP sites to keep, from the site master table.

Show code
sitekp <-
  sitedat |>
  filter(program=="TCRMP") |>
  # filter(yearadded <= maxyearadded) |>
  select(site)

We match each kept site to its file position in the directory.

Show code
sitekp$ind <- 0
for (i in 1:length(sitekp$site)) {
  sitekp$ind[i] <- grep(paste(sitekp$site[i], "raw"),
                        gsub("_", " ", tcrmptempdir))
}

We then read each site file and reduce it to daily statistics. tcrmp_tempDat starts as an empty frame with the twelve target columns.

Show code
tcrmp_tempDat <- data.frame(matrix(ncol = 12, nrow = 0))
colnames(tcrmp_tempDat) <-
  c("year",
    "month",
    "day",
    "site",
    "meantemp",
    "sdtemp",
    "maxtemp",
    "mintemp",
    "nmsmts",
    "topdecilemean",
    "bottomdecilemean",
    "decilemeandifference")

What happens here: the loop reads each TCRMP site file, filters to the embargo window (1992 to 2023), and summarizes each day to a mean, standard deviation, maximum, minimum, count, and the top and bottom decile means. It appends every site to tcrmp_tempDat.

Show code
#i=1

for (i in 1:length(sitekp$ind)) {
  tempdat <-
    read.csv(paste(tcrmp_raw_tempdir,
                   tcrmptempdir[sitekp$ind[i]],
                   sep = "/"))
  tempdat <- tempdat %>% distinct() 
  
  tempdat$site <- sitekp$site[i]
  # tempdat$year <- lubridate::year(ymd(tempdat$Date))
  # tempdat$month <- lubridate::month(ymd(tempdat$Date))
  # tempdat$day <- lubridate::day(ymd(tempdat$Date))
  tempdat$year <- lubridate::year(ymd(tempdat$Date))
  tempdat$month <- lubridate::month(ymd(tempdat$Date))
  tempdat$day <- lubridate::day(ymd(tempdat$Date))
  
  tempdat <- tempdat |>
    filter(year >= minyear &
             year <= maxyear) |>
    dplyr::group_by(year, month, day, site) |>
    mutate(topdecilemean = mean(Temperature[order(-Temperature)][1:floor(0.10 * length(Temperature))])) |>
    mutate(bottomdecilemean = mean(Temperature[order(Temperature)][1:floor(0.10 * length(Temperature))])) |>
    dplyr::summarise(
      meantemp = mean(Temperature),
      sdtemp = sd(Temperature),
      maxtemp = max(Temperature),
      mintemp = min(Temperature),
      nmsmts = length(Temperature),
      topdecilemean = mean(topdecilemean),
      bottomdecilemean = mean(bottomdecilemean),
      decilemeandifference = topdecilemean - bottomdecilemean
    )
  
  tcrmp_tempDat <- rbind(tcrmp_tempDat, tempdat)
}

rm(i, tempdat, sitekp, tcrmptempdir, tcrmp_raw_tempdir)

tcrmp_tempDat <-
  cbind(program = rep("TCRMP", nrow(tcrmp_tempDat)), tcrmp_tempDat
  )

Validation note: the modal daily reading count is 96, which corresponds to a reading every 15 minutes at most TCRMP sites.

VINPS

We read the raw VINPS logger file.

Show code
vinps_tempDat <-
  read.csv("../../../RRSdata/data_VINPS/VINPS_temperature_allSites_2005_2018_data.csv")

Validation note: the raw VINPS file holds 980,029 records across 10 columns, spanning 2005 to 2018.

Good values carry a quality flag of 31, so we keep only those rows.

Show code
vinps_tempDat <- vinps_tempDat |>
  filter(Quality==31)

We build the VINPS sitekp set from the site master table, parse each record’s file name to recover its site code, and join the readable site names onto the temperature records.

Show code
sitekp <-
  sitedat |> filter(program == "VINPS" &
                      yearadded <= 2005) |> select(site, site_code)

vinps_file <-
  t(matrix(unlist(strsplit(
    vinps_tempDat$filename, "_"
  )), nrow = 4))
vinps_file <- paste(vinps_file[, 1], vinps_file[, 2], sep = "_")
vinps_tempDat$site <-
  sitekp$site[match(vinps_file, sitekp$site_code)]
vinps_tempDat <- vinps_tempDat[-which(is.na(vinps_tempDat$site)), ]

We filter to the embargo window (1992 to 2023).

Show code
vinps_tempDat <-
  vinps_tempDat |>
  filter(year >= minyear &
           year <= maxyear)

We drop records with a missing temperature.

Show code
vinps_tempDat <- vinps_tempDat[-which(is.na(vinps_tempDat$Temp_C)), ]

We summarize each day to the same daily statistics used for TCRMP, then tag the program.

Show code
vinps_tempDat <- vinps_tempDat |>
  dplyr::group_by(year, month, site, day) |>

  mutate(topdecilemean = mean(Temp_C[order(-Temp_C)][1:floor(0.10 * length(Temp_C))])) |>
  mutate(bottomdecilemean = mean(Temp_C[order(Temp_C)][1:floor(0.10 * length(Temp_C))])) |>
  dplyr::summarise(
    meantemp = mean(Temp_C),
    sdtemp = sd(Temp_C),
    maxtemp = max(Temp_C),
    mintemp = min(Temp_C),
    nmsmts = length(Temp_C),
    topdecilemean = mean(topdecilemean),
    bottomdecilemean = mean(bottomdecilemean),
    decilemeandifference = topdecilemean - bottomdecilemean
  )

vinps_modal_nmsmts <- as.integer(names(sort(table(vinps_tempDat$nmsmts), decreasing = TRUE))[1])
rm(vinps_file, sitekp)

vinps_tempDat <- cbind(program = rep("VINPS", nrow(vinps_tempDat)),
                       vinps_tempDat)

Validation note: the modal daily reading count is 24, consistent with hourly logging at VINPS sites.

CSUN

CSUN sites lack their own loggers, so we borrow VINPS measurements from Yawzi and Tektite. We pull those two VINPS series and relabel them as CSUN.

Show code
YZdat <- vinps_tempDat |>
  filter(site == "VIIS-Yawzi")

TKdat <- vinps_tempDat |>
  filter(site == "VIIS-Tektite")

YZdat$program <- "CSUN"
TKdat$program <- "CSUN"

We build the CSUN sitekp set from the site master table.

Show code
sitekp <-
  sitedat |>
  filter(program == "CSUN" &
           yearadded <= maxyearadded) |>
  select(site, site_code)

We split the CSUN sites into those assigned VINPS Yawzi temperatures (sitekp_YZ) and those assigned VINPS Tektite temperatures (sitekp_TK).

Show code
sitekp_YZ <- sitekp |>
  filter(
    site %in% c(
      "White Point",
      "Europa Bay",
      "West Little Lameshur",
      "Yawzi",
      "Neptunes Table"
    )
  )

sitekp_TK <- sitekp |>
  filter(site %in% c("East Tektite",
                     "Tektite"))

We assemble csun_tempDat the same way as tcrmp_tempDat and vinps_tempDat, copying the borrowed donor series onto each CSUN site.

Show code
csun_tempDat <- data.frame(matrix(ncol = 10, nrow = 0))

colnames(csun_tempDat) <-
  c(
    "program",
    "year",
    "month",
    "day",
    "site",
    "meantemp",
    "sdtemp",
    "maxtemp",
    "mintemp",
    "nmsmts"
  )

for (i in 1:nrow(sitekp_YZ)) {
  temp <- YZdat
  temp$site <- sitekp_YZ$site[i]
  csun_tempDat <- rbind(csun_tempDat, temp)
}

for (i in 1:nrow(sitekp_TK)) {
  temp <- TKdat
  temp$site <- sitekp_TK$site[i]
  csun_tempDat <- rbind(csun_tempDat, temp)
}

rm(sitekp, sitekp_TK, sitekp_YZ, temp, i, YZdat, TKdat)

Merge daily temperatures into tempDat_daily

We stack the three program tables into one daily record.

Show code
tempDat_daily <- rbind(tcrmp_tempDat, vinps_tempDat, csun_tempDat)

We drop VIIS-Tektite for year-added consistency, and drop the CSUN Tektite and Yawzi sites because they lack matching benthic data.

Show code
tempDat_daily <- tempDat_daily |>
  filter(site != "VIIS-Tektite")
tempDat_daily <- tempDat_daily |>
  filter(site != "Tektite")
tempDat_daily <- tempDat_daily |>
  filter(site != "Yawzi")

Validation note: the merged daily record holds 240,815 site-day rows across 45 sites and the three programs (CSUN, TCRMP, VINPS), spanning 2002 to 2023.

Downloads

The links below serve this page’s derived daily-temperature product and its metadata.


version 1.0.0 • in-review • data ≤ 2023-12-31

Footnotes

  1. Edmunds has previously used data from VINPS Yawzi Point (9m) as there is little variation among these sites that are separated by < 2 km. Where sensors fail, Edmunds has also augmented Yawzi with data from Tektite 14 m and have shown no meaningful difference (personal correspondence).↩︎