A Appendix: Data Review and Uplift
A.1 Introduction
Prior to analysis and interpretation of water quality data, we will ensure that all data that meets QA/QC standards outlined in the current project Quality Assurance Project Plan (QAPP) is accessible in the appropriate repository.
Water quality data from this project is ultimately destined for the EPA Water Quality Exchange (EPA WQX), formerly EPA STORET. The process of transferring these data to the higher-level EPA repository is referred to as data “uplift.”
Section B10 of the 2020 QAPP describes data management details and responsible parties for each step of the data pipeline from observation to repository. The 2021 data preparation and review process is published here as an appendix as an example of the process applied annually to each year’s data.
A.1.1 2021 Water Quality Data
In this appendix we will collate laboratory data from several sources into a single spreadsheet document with a consistent format. The format consists of a spreadsheet template provided by the Alaska Department of Environmental Conservation (ADEC), referred to here as the AQWMS Template:
Download the AQWMS Water Quality Data Spreadsheet Template
AWQMS is an acronym for “Ambient Water Quality Management System,” the service used by ADEC to manage and prepare data for potential uplift to the EPA WQX.
A.1.1.1 2021 Water Quality Data AQWMS Formatting
The code scripts below assemble water quality data from the three analytical laboratories that partnered with Kenai Watershed Forum for this project in 2021:
SGS Laboratories (Anchorage, AK)
Soldotna Wastewater Treatment Plant (Soldotna, AK)
Taurianen Engineering and Testing (Soldotna, AK)
A.1.1.1.1 Metals/Nutrients Lab Results (SGS Labs)
Download Original Spring 2021 Metals/Nutrients Lab Results from SGS
Download Original Summer 2021 Metals/Nutrients Lab Results from SGS
*Note: the chain of custody documents for SGS Laboratories are integrated into the above downloadable PDF files.
Show/Hide Code used to Prepare 2021 Metals/Nutrients Results
# clear environment
rm(list=ls())
# load packages
library(tidyverse)
library(readxl)
library(openxlsx)
library(data.table)
library(stringr)
library(magrittr)
library(janitor)
library(hms)
library(lubridate)
library(anytime)
::pkg_load2(c("htmltools", "mime"))
xfun
# Assign 2021 Field Sample Dates
# Spring 2021 sampling date
<- "5/11/2021"
spring21_sample_date
# Summer 2021 Sampling Date
<-"7/27/2021" summer21_sample_date
################################################################################################################
######################################### Read in and Clean SGS/ALS Data ######################################
################################################################################################################
############################ Part A: SGS Data Read In #############################
## Reformat SGS data downloaded from their server client (SGS Engage, full EDD files) to match AQWMS template
# read in
<- read.csv("other/input/2021_wqx_data/spring_2021_wqx_data/SGS/spring_2021_sgs_batch_info.csv")
spring_batch_sgs21 <- read.csv("other/input/2021_wqx_data/summer_2021_wqx_data/SGS/summer_2021_sgs_batch_info.csv")
summer_batch_sgs21
# clean up and retain only useful columns
<- bind_rows(spring_batch_sgs21,summer_batch_sgs21) %>%
sgs21 clean_names() %>%
remove_empty() %>%
# remove unneeded columns
select(-project_id)%>%
rename(sample = sample_id,
lab_sample = lab_sample_id,
detection_limit = dl) %>%
transform(lab_sample = as.character(lab_sample),
sample_rpd = as.character(sample_rpd)) %>%
# add lab name
mutate(lab_name = "SGS North America, Anchorage, Alaska",
matrix = "Water") %>%
# split a.) lab sample run & b.) collect time and date in prep for future join with ALS data
##### NOTE: SGS data has date and time, ALS has date only.
transform(collect_date_time = mdy_hm(collect_date),
rec_date_time = mdy_hm(rec_date),
run_date_time = mdy_hm(run_date_time),
extracted_date_time = mdy_hm(extracted_date)) %>%
mutate(collect_time = as_hms(collect_date_time),
collect_date = date(collect_date_time),
rec_date = date(rec_date_time),
rec_time = as_hms(rec_date_time),
run_time = as_hms(run_date_time),
run_date = date(run_date_time),
extracted_time = as_hms(extracted_date_time),
extracted_date = date(extracted_date_time)) %>%
select(-collect_date_time,-rec_date_time,-run_date_time) %>%
rename(sample = sample)
## value for "which" not specified, defaulting to c("rows", "cols")
rm(spring_batch_sgs21,summer_batch_sgs21)
###################### Part B: ALS Data Read In #############################
## SGS subcontracted analyses of Ca, Fe, and Mg to ALS laboratories (Kelso, WA). These results are not included in the spreadsheet download from SGS engage and were entered manually in to seperate spring and summer "ALS" named spreadsheets
#### read in spring 2021 results from ALS
<- read.csv("other/input/2021_wqx_data/spring_2021_wqx_data/SGS/spring_2021_als_batch_info.csv") %>%
spring_als21 clean_names()
<- read.csv("other/input/2021_wqx_data/summer_2021_wqx_data/SGS/summer_2021_als_batch_info.csv") %>%
summer_als21 clean_names()
# bind spring and summer
<- bind_rows(spring_als21,summer_als21) %>%
als21 remove_empty() %>%
# proceed left to right of existing ALS dataframe to make its naming structure match the sgs21 dataframe. Add, remove, modify column names as needed
select(-client,
-project,
-service_request) %>%
rename(lab_sample = lab_code) %>%
rename(
collect_date = date_collected,
collect_time = time_collected,
rec_date = date_received,
rec_time = time_received,
# sample_type ::: not sure where to match with sgs data yet or where to put in aqwms, but is important for qa/qc
extracted_date = date_extracted,
extracted_time = time_extracted,
extraction_code = extraction_method,
run_date = date_analyzed,
run_time = time_analyzed,
analytical_method = method,
#units = units,
analyte = component,
resultflag = result_notes,
amount_spiked = spike_concentration,
percent_recovered = percent_recovery,
allowable_limit = acceptance_limits,
sample_rpd = rpd,
# change report/detection limit terminology See SGS document, "SGS DL, LOD, LOQ Interpretation"
loq = reporting_limit) %>%
mutate(lab_name = "ALS Environmental, Kelso, Washington"
#,
#run_time = ""
%>%
) # prep column classes to bind with sgs dataframe
transform(analytical_method = as.character(analytical_method),
run_date = mdy(run_date),
run_time = as_hms(as.POSIXct(run_time, format = "%H:%M")),
#run_time = as_hms(run_time),
collect_date = mdy(collect_date),
rec_date = mdy(rec_date),
rec_time = as_hms(as.POSIXct(rec_time, format = "%H:%M")),
extracted_date = mdy(extracted_date),
extracted_time = as_hms(as.POSIXct(extracted_time, format = "%H:%M")),
result = as.double(result),
collect_time = as_hms(as.POSIXct(collect_time, format = "%H:%M")))
## value for "which" not specified, defaulting to c("rows", "cols")
## Warning in eval(substitute(list(...)), `_data`, parent.frame()): NAs introduced by coercion
# join SGS data with ALS data
<- bind_rows(sgs21,als21)
dat
rm(als21,sgs21,spring_als21,summer_als21)
# export table of sample types
## assign sample type acronyms just like with sgs21 samples. see excel file for full definitions
# --> make sure doesn't conflict with other sample_type designations in rest of document. use same acronyms
### export table of sample types, then manually translate their abbreviations
<- dat %>%
sample_types select(sample_type,lab_name) %>%
distinct()
# remove old version and write new one
unlink("other/input/AQWMS/sample_type_abbreviations.xlsx")
write.xlsx(sample_types, "other/input/AQWMS/sample_type_abbreviations.xlsx")
# manually created a translation of all the acronyms in an accompanying file. removed inconsistencies in sample type abbreviations into one consistent schema between SGS and ALS labs
############### Part C: Address spelling/format issues and inconsistent sample/site names ######################
# Upon visual inspection of site names, we can see that the location names in the AQWMS template differ slightly from the place names in the SGS report (spelling and name inconsistencies).
# 3/28/2022 - A note on addressing "Duplicate" designations. In QA/QC data review in March 2022 the following was clarified through trial and error: we must make a careful distinction between "Field Duplicates" and "Lab duplicates" when preparing this data. The sample names contain info about whether a result is from a "Field Duplicate," e.g., two field collections made at the same location/day/time. However the ALS lab also created "Lab Duplicates," which are not from the same sites as field duplicates, and designates these as "DUP1" in the "sample_type" column.
# See AQWMS Activity Type column assign distinctions
# Decision - we will designate the field duplicates simply as a Field Duplicate
# move info about duplicate sample and/or sample blank status into separate column, "sample_condition"
%<>%
dat mutate(sample_condition = case_when(
grepl("Method Blank",sample) ~ "Method Blank",
grepl("Trip Blank",sample) ~ "Trip Blank",
grepl("DUP",sample) ~ "Field Duplicate",
grepl("Dup",sample) ~ "Field Duplicate")) %>%
# remove "DUP" designation from sample column
mutate(sample = str_replace(sample, "DUP|Dup", ""))
# remove from "sample" names the text containing the suffixes Diss/Dis (Dissolved metals sample) since we only want location info in this column. (Solution for this step was found at https://stackoverflow.com/questions/29271549/replace-all-occurrences-of-a-string-in-a-data-frame)
%<>%
dat mutate(sample = (str_replace(sample, "Diss|Dis|DUP|Dup",""))) %>%
# remove "Diss" suffix and "EP" prefix from "analytical_method" column
mutate(analytical_method = str_replace(analytical_method, "Diss", "")) %>%
# note trailing space after "EP200.8 "
mutate(analytical_method = str_replace(analytical_method,"EP200.8 ","200.8")) %>%
# address the one stubborn site name still containing "Diss"
mutate(sample = case_when(
== "RM0-No Name Creek Diss" ~ "RM0-No Name Creek",
sample TRUE ~ sample))
# z <- dat %>%
# select(sample,sample_type,sample_condition)
# We need to remove white spaces, apostrophes, and dashes; because join functions such as "left_join" are often uncooperative with these types of string characters. We will need to use joins with site names in next steps.
%<>%
dat # remove excess white spaces
mutate(sample = str_trim(sample,"both")) %>%
mutate(sample = str_squish(sample)) %>%
# make remaining white spaces underscores
mutate(sample = gsub("\\s+","_",sample)) %>%
# remove apostrophes
mutate(sample = gsub("\\'","",sample)) %>%
# replace dashes with underscores
mutate(sample = gsub("\\-","_",sample)) %>%
# replace multiple underscores with single
mutate(sample = gsub("\\__","_",sample)) %>%
mutate(sample = gsub("\\___","_",sample)) %>%
# again replace multiple underscores with single
mutate(sample = gsub("\\__","_",sample))
# apply note regarding trip blanks (for BTEX organics)
# assigned in sequence as encountered on chain of custody
%<>%
dat mutate(note = case_when(
grepl("Trip_Blank_1", sample) ~ "KWF Crew, RM1.5_Kenai_City_Dock",
grepl("Trip_Blank_2", sample) ~ "USFWS Crew, RM6.5_Cunningham_Park",
grepl("Trip_Blank_3", sample) ~ "DEC Crew, RM40_Bings_Landing",
grepl("Trip_Blank_4", sample) ~ "DEC Crew, RM43_Upstream_of_Dow_Island"))
# seperate result qualifiers (U, J, B) in to a new column
#sgs21 %<>%
# account for fact that als data already has qualifier column and has characters in results column
# mutate(qualifier = case_when(
# result == "ND" ~ qualifier,
# result != "ND" ~ str_extract(result,"[aA-zZ]+"))) %>%
# mutate(result = str_remove(result,"[aA-zZ]+"))
############## Part D: Prepare SGS/ALS Location/Site Names ##########################
# NOTE: The SGS and ALS 2021 results have a variety of misspelling and typos. For 2022, we should provide labs with a csv file of site names that they can use
# In preparation for a join to AQWMS table, we will manually generate a match table csv file that we can use
## generate list of unique site names from 2021 SGS data
<- data.table(unique(dat$sample)) %>%
sgs21_sitenames arrange(V1)
# generate list of unique site names from 2021 AQWMS template. these are the names we want in the final product
<- read_excel("other/input/AQWMS/AWQMS_KWF_Baseline_2021.xlsx", sheet = "Monitoring Locations") %>%
aqwms21_sitenames select("Monitoring Location Name", "Monitoring Location ID") %>%
distinct()
# write 2021 sgs site names to an excel file
<- "other/input/AQWMS/sgs_site_names_matching_table.xlsx"
site_match_table_path write.xlsx(sgs21_sitenames, site_match_table_path)
# create an excel file with two sheets: a.) SGS site names, and b.) AQWMS site names
<- loadWorkbook(site_match_table_path)
wb addWorksheet(wb,"Sheet2")
writeData(wb,"Sheet2",aqwms21_sitenames)
saveWorkbook(wb,site_match_table_path,overwrite = TRUE)
# Using these two tables, we will manually create a new file titled "sgs_site_names_matching_table_manual_edit.xlsx" and manually match up the two disparate naming systems.
# Performed manually by B Meyer March 18, 2022.
# append "Monitoring Location Name" and "Monitoring Location ID" info from WQX to spring 2021 SGS data
## read in site names join table
<- read_excel("other/input/AQWMS/sgs_site_names_matching_table_manual_edit.xlsx") %>%
sitenames21_match select(`Monitoring Location Name`,`Monitoring Location ID`,sgs_sitenames) %>%
rename(sample = sgs_sitenames) %>%
filter(!is.na(`Monitoring Location ID`))
## New names:
## * `` -> `...4`
## * `` -> `...5`
## * `` -> `...6`
## * `` -> `...7`
# append monitoring location names
%<>%
dat left_join(sitenames21_match, by = "sample") %>%
clean_names()
# remove dfs
rm(sgs21_sitenames,aqwms21_sitenames,sitenames21_match)
######################## Part E: "Result Analytical Method Context" name rectification ######################
# In the AQWMS template, the EPA names that will go in the column "Result Analytical Method ID" do not exactly match the names provided by the laboratory (SGS). After communicating with SGS and DEC on 2/8/2022, we are able to cross-walk between the two naming systems. These matches are documented in the excel file "analysis_code_matching_table.xlsx."
# assign "Result Analytical Method ID" and "Result Analytical Method Context" to dataset using matching table
# read in matching table
<- read_excel("other/input/AQWMS/analysis_code_matching_table.xlsx") %>%
analysis_code_matching_table select(-Comments,-`EPA Name`) %>%
clean_names() %>%
rename(analytical_method = sgs_analysis_code) %>%
# remove "EP" prefix from method "EP200.8"
mutate(analytical_method = str_replace(analytical_method,"EP200.8","200.8"))
# read in AQWMS Analytical Methods list
<- read_excel("other/input/AQWMS/AWQMS_KWF_Baseline_2021.xlsx", sheet = "Analytical Methods") %>%
aqwms_analytical_methods select("ID","Context Code") %>%
clean_names() %>%
rename(epa_analysis_id = id) %>%
distinct()
## Warning: Expecting logical in F1126 / R1126C6: got '4500-NO2'
## Warning: Expecting logical in F1130 / R1130C6: got '4500-NO3'
# join two tables above
<- inner_join(aqwms_analytical_methods,analysis_code_matching_table, by = "epa_analysis_id") %>%
epa_analysis_codes filter(!context_code %in% c("USEPA Rev 5.4",
"APHA (1997)",
"APHA (1999)"))
# join EPA analysis IDs and context codes to overall dataset
%<>%
dat mutate(analytical_method = str_replace(analytical_method,"EP200.8","200.8")) %>%
left_join(epa_analysis_codes, by = "analytical_method")
# remove unneeded dfs
rm(analysis_code_matching_table,aqwms_analytical_methods,epa_analysis_codes)
########################## Miscellaneous Steps #########################################
########### Address Non-Detect values #########################
# Non-detect values should be left blank. A non-detect does not necessarily mean there was a zero observation of an analyte in a sample, it could be just be present at a level lower than the method detection level (lower than what the lab equipment can detect). Instead of 0, we’ll leave it blank. The Practical Quantitation Limit (or Limit of Quantitation) is presented alongside the result. When DEC evaluates a waterbody, they’ll use ½ the PQL as a stand-in for a non-detect. See explanatory document at other/documents/references/SGS DL, LOD, LOQ Interpretation.pdf for more details.
# modify non-detect values from "0" to "NA" if resultflag = U or ND
%<>%
dat mutate(result1 = na_if(result,0)) %>%
select(-result) %>%
rename(result = result1)
###### Segregate laboratory QA/QC data from field data ########
# These data will be evaluated at a later step. See excel file "other/input/AQWMS/sample_type_abbreviations_manual_edit.xlsx" for sample_type naming schema
<- dat %>%
sgs21_als21_qaqc_dat # retain only results not from field sampling program (project samples and trip blanks)
# also filter out hydrocarbon surrogate results ("surr"). Surrogate standards are compounds spiked into all samples, blanks, Laboratory Control Samples, and matrix spikes to monitor the efficacy of sample extraction, chromatographic, and calibration systems. They do not represent environmental observations.
filter(!sample_type %in% c("PS","SMPL","TB") |
grepl("(surr)",analyte))
write.csv(sgs21_als21_qaqc_dat, "other/output/lab_qaqc_data/2021_lab_qaqc_data/sgs21_als21_qaqc_dat.csv")
# retain only non-field sample results for AQWMS export
%<>%
dat filter(sample_type %in% c("PS","SMPL","TB")) %>%
filter(!grepl("(surr)",analyte))
rm(sgs21_als21_qaqc_dat)
A.1.1.1.2 Fecal Coliform Lab Results (Soldotna Wastewater Treatment Plant (SWWTP)/Taurianen Engineering)
Download Original Spring 2021 Fecal Coliform Lab Results from SWWTP
Download Original Summer 2021 Fecal Coliform Lab Results from Taurianen
Download Spring 2021 Fecal Coliform Chain of Custody
Download Summer 2021 Fecal Coliform Chain of Custody
Show/Hide Code used to Prepare 2021 Fecal Coliform Results
############################################################################################################
##################################### Read in and Clean SWWTP / Taurianen FC Data #########################
############################################################################################################
########################### Part A: SWWTP FC Data Read In ##################################################
<- read_excel("other/input/2021_wqx_data/spring_2021_wqx_data/SWWTP/KRWF Fecal 05-11-21.xls", skip = 11) %>%
swwtp_spring21 clean_names() %>%
## fix site naming and terminology
# move info about duplicate sample and/or sample blank status into separate column
# sample type abbreviations
mutate(sample_type = case_when(
grepl("BLANK",sample_location_rm) ~ "MB", # method blank
grepl("POSITIVE",sample_location_rm) ~ "LCS")) %>% # laboratory control sample
# assign all other samples as "PS" (project sample)
mutate_at(vars(sample_type),~replace_na(.,"PS")) %>%
# field dup designation
mutate(sample_condition = case_when(
grepl("DUP",sample_location_rm) ~ "Field Duplicate")) %>%
# remove "BLANK", and "POSITIVE designation from sample_location column
mutate(sample_location_rm = (str_replace(sample_location_rm, "BLANK|POSITIVE", "")))
# remove "DUP" from site name column and trim white spaces in site name column
%<>%
swwtp_spring21 mutate(sample_location_rm = str_remove(sample_location_rm,"DUP")) %>%
mutate(sample_location_rm = str_trim(sample_location_rm,"right"))
# address different site naming systems
# use manually generated matching table
# read in matching table and match
<- read_excel("other/input/AQWMS/swwtp_site_names_matching_table_manual_edit.xlsx")
swwtp_spring21_site_matching
# join
%<>%
swwtp_spring21 full_join(swwtp_spring21_site_matching) %>%
select(-sample_location_rm)
## Joining, by = "sample_location_rm"
rm(swwtp_spring21_site_matching)
## fix lab analysis times and dates
%<>%
swwtp_spring21 # lab processing time/date
mutate(analysis_time_in = as_hms(time_in),
analysis_date_in = mdy(spring21_sample_date),
analysis_time_out = as_hms(time_out),
# see file "other/input/2021_wqx_data/spring_2021_wqx_data/SWWTP/KRWF Fecal 05-11-21.xls for out analysis date
analysis_date_out = ymd("2021-05-12")) %>%
select(-time_in,-time_out) %>%
transform(time_sampled = as_hms(time_sampled)) %>%
# field sample date and time
mutate(time_sampled = as_hms(time_sampled),
sample_date = mdy(spring21_sample_date))
## assign time/date received at lab. info from chain of custody
<- "13:31:00"
swwtp_spring21_rec_time <- "2021-05-11"
swwtp_spring21_rec_date %<>%
swwtp_spring21 mutate(rec_date = ymd(swwtp_spring21_rec_date),
rec_time = as_hms(swwtp_spring21_rec_time))
## rename existing column names and create new ones to match sgs21 data format at end of prior code chunk
%<>%
swwtp_spring21 rename(lab_sample = dish_number,
result = colony_count_100m_l,
collect_time = time_sampled,
run_time = analysis_time_in,
run_date = analysis_date_in,
# = analysis_time_out,
# = analysis_date_out,
collect_date = sample_date) %>%
mutate(note = paste0("Lab analysis volume = ",ml," mL"),
matrix = "Water (Surface, Eff., Ground)",
analytical_method = "9222 D ~ Membrane filtration test for fecal coliforms",
analyte = "Fecal Coliform",
units = "cfu/100ml",
# reporting limit ("loq") value from 2019 QAPP, pg 17
loq = 1.0,
lab_name = "Soldotna Wastewater Treatment Plant, Soldotna, Alaska",
units = "cfu/100ml",
epa_analysis_id = "9222D",
context_code = "APHA",
analyst = "AW") %>%
clean_names() %>%
select(-ml,-colony_count) %>%
# transform to prep for bind with sgs21
transform(lab_sample = as.character(lab_sample),
result = as.double(result)) %>%
# apply correction to the one "TNTC" result (Too Numerous To Count), since we can't have characters and integers in same column
mutate(note = case_when(
== "30" ~ paste("Lab analysis volume = 0.5 mL, result = TNTC"),
lab_sample TRUE ~ note))
## Warning in eval(substitute(list(...)), `_data`, parent.frame()): NAs introduced by coercion
########### Address Non-Detect values #########################
# Non-detect values should be left blank. A non-detect does not necessarily mean there was a zero observation of an analyte in a sample, it could be just be present at a level lower than the method detection level (lower than what the lab equipment can detect). Instead of 0, we’ll leave it blank. The Practical Quantitation Limit (or Limit of Quantitation) is presented alongside the result. When DEC evaluates a waterbody, they’ll use ½ the PQL as a stand-in for a non-detect. See explanatory document at other/documents/references/SGS DL, LOD, LOQ Interpretation.pdf for more details.
# Assign resultflag column. Use "=" if result > 1 and "U" if result < 1. See pg 17 of 2020 QAPP at "other/documents/QAPP/QAPP-2020-KenaiRiverWatershed_ZnCu.pdf"
%<>%
swwtp_spring21 mutate(resultflag = case_when(
< 1 ~ "U",
result TRUE ~ "="))
# modify non-detect values from "0" to "NA" if resultflag = U or ND
%<>%
swwtp_spring21 mutate(result1 = na_if(result,0)) %>%
select(-result) %>%
rename(result = result1)
# segregate lab results from field results, and write lab qa/qc results to external csv
<- swwtp_spring21 %>%
swwtp_spring21_qaqc_dat filter(sample_type %in% c("MB","LCS"))
write.csv(swwtp_spring21_qaqc_dat, "other/output/lab_qaqc_data/2021_lab_qaqc_data/swwtp_spring21_fc_qaqc_dat.csv", row.names = F)
%<>%
swwtp_spring21 filter(!sample_type %in% c("MB","LCS"))
## join SGS 2021 data to Spring 2021 Fecal Coliform data from SWWTP
<- bind_rows(dat,swwtp_spring21) %>%
dat select(-location)
rm(swwtp_spring21)
########################### Part B: Taurianen FC Data Read In (Summer 2021) ##############################################
<- "2021-07-27"
taur_summer21_rec_date <- "13:37:00"
taur_summer21_rec_time
## read in taurianen summer 2021 results
<- read_excel("other/input/2021_wqx_data/summer_2021_wqx_data/Taurianen/Fecal_Coliform_Results_Spreadsheet.xlsx", skip = 3) %>%
taur_summer21 clean_names() %>%
select(-qc1,-data_entry,-qc2) %>%
## move info about duplicate sample and/or sample blank status into separate column
mutate(sample_condition = case_when(
grepl("DUP",sample_location) ~ "Field Duplicate")) %>%
# remove "DUP" designation from sample_location column
mutate(sample_location = (str_replace(sample_location, "_DUP", ""))) %>%
# trim white spaces in site name column
mutate(sample_location = str_trim(sample_location,"right")) %>%
## add known info about times/dates, correct formats and column names
mutate(collect_date = mdy(summer21_sample_date),
run_date = mdy(summer21_sample_date),
run_time = as_hms(time_relinquished),
analysis_date_out = mdy("7/28/2021"),
analysis_time_out = as_hms(time_tested),
# time/date received at lab from chain of custody
rec_date = ymd(taur_summer21_rec_date),
rec_time = as_hms(taur_summer21_rec_time),
# drop old columns
.keep = "unused") %>%
select(-date_of_testing,-neg_pos) %>%
transform(time_sampled = as_hms(time_sampled)) %>%
## add lab name
mutate(lab_name = "Taurianen Engineering and Testing, Soldotna, Alaska") %>%
# rename columns
rename(sample = sample_location,
collect_time = time_sampled)
# NOTE: for Taurianan QA/QC practices, see email from from Taurianen at "other/documents/references/Taurianen QA Technique (Email march 2022).docx" (folder in this project repo)
## fix site naming and terminology
# generate spreadsheet of unique site names from taurianen dataset
<- data.frame(unique(taur_summer21$sample)) %>%
taur_summer21_sites rename(sample = unique.taur_summer21.sample.)
# export site names list to spreadsheet
write.xlsx(taur_summer21_sites, "other/input/AQWMS/taurianen_site_names_matching_table.xlsx")
# manually edit a new spreadsheet such that taurianen site names are paired iwth AWQMS site names
# read in manually edited site names sheet
<- read_excel("other/input/AQWMS/taurianen_site_names_matching_table_manual_edit.xlsx")
taur_summer21_sites
# join AWQMS site names to taurianen data
<- left_join(taur_summer21,taur_summer21_sites,by = "sample")
taur_summer21
## add and/or rename other columns to match SWWTP dataframe structure
%<>%
taur_summer21 clean_names() %>%
select(-direct_count) %>%
rename(result = number_of_colonies) %>%
mutate(note = "",
matrix = "Water",
analytical_method = "9222 D ~ Membrane filtration test for fecal coliforms",
analyte = "Fecal Coliform",
units = "cfu/100ml",
# loq = reporting limit
loq = 1,
epa_analysis_id = "9222D",
context_code = "APHA") %>%
transform(result = as.double(result))
# assign "sample_type"
%<>% mutate(sample_type = case_when(
taur_summer21 == "Lab Blank" ~ "MB", # method blank
sample_condition == "Positive Control" ~ "LCS", # laboratory control sample
sample_condition TRUE ~ "PS"
))
########### Address Non-Detect values #########################
# Non-detect values should be left blank. A non-detect does not necessarily mean there was a zero observation of an analyte in a sample, it could be just be present at a level lower than the method detection level (lower than what the lab equipment can detect). Instead of 0, we’ll leave it blank. The Practical Quantitation Limit (or Limit of Quantitation) is presented alongside the result. When DEC evaluates a waterbody, they’ll use ½ the PQL as a stand-in for a non-detect. See explanatory document at other/documents/references/SGS DL, LOD, LOQ Interpretation.pdf for more details.
# Assign resultflag column. Use "=" if result > 1 and "U" if result < 1. See pg 17 of 2020 QAPP at "other/documents/QAPP/QAPP-2020-KenaiRiverWatershed_ZnCu.pdf"
%<>%
taur_summer21 mutate(resultflag = case_when(
< 1 ~ "U",
result TRUE ~ "="))
# modify non-detect values from "0" to "NA" if resultflag = U or ND
%<>%
taur_summer21 mutate(result1 = na_if(result,0)) %>%
select(-result) %>%
rename(result = result1)
# segregate lab results from field results, and write lab qa/qc results to external csv
<- taur_summer21 %>%
taur_summer21_qaqc_dat filter(sample_type %in% c("MB","LCS"))
write.csv(taur_summer21_qaqc_dat, "other/output/lab_qaqc_data/2021_lab_qaqc_data/taur_summer21_qaqc_dat.csv", row.names = F)
# join 2021 Taurianen Fecal Coliform data into overall dataframe so far
<- bind_rows(dat,taur_summer21)
dat
rm(taur_summer21,taur_summer21_sites,taur_summer21_qaqc_dat,swwtp_spring21_qaqc_dat)
A.1.1.1.3 Total Dissolved Solids Lab Results (Soldotna Wastewater Treatment Plant (SWWTP))
Download Original Spring 2021 Total Suspended Solids Results from SWWTP.xlsx
Download Original Summer 2021 Total Suspended Solids Results from SWWTP.xlsx
Download Spring 2021 Total Suspended Solids Chain of Custody
Download Summer 2021 Total Suspended Solids Chain of Custody
Show/Hide Code used to Prepare 2021 Total Dissolved Solids Results
# SWWTP Spring 2021 TSS data
## Reformat TSS data to match AQWMS template
# read in
<- read_excel('other/input/2021_wqx_data/spring_2021_wqx_data/SWWTP/KRWF TSS MONITORING 05-11-21.xlsx', skip = 1, sheet = "Updated_Formatting") %>%
swwtp_tss_spring21 clean_names() %>%
transform(date_of_analysis = anydate(date_of_analysis)) %>%
# add info from lab COC
mutate(rec_date = ymd_hms("2021-05-11 14:00:00"))
## New names:
## * `` -> `...8`
<- read_excel('other/input/2021_wqx_data/summer_2021_wqx_data/SWWTP/KRWF TSS MONITORING 07-28-21.xlsx', skip = 1, sheet = "Updated_Formatting") %>%
swwtp_tss_summer21 clean_names() %>%
transform(sample_time = anytime(sample_time)) %>%
# add info from lab COC
mutate(rec_date = ymd_hms("2021-07-27 14:00:00"))
## New names:
## * `` -> `...8`
# combine spring & summer
<- bind_rows(swwtp_tss_spring21,swwtp_tss_summer21) %>%
swwtp_tss21 remove_empty()
## value for "which" not specified, defaulting to c("rows", "cols")
rm(swwtp_tss_spring21,swwtp_tss_summer21)
# prepare and format to match larger dataset
# miscellaneous
%<>%
swwtp_tss21 select(-qc1,-data_entry,-x8) %>%
rename(analysis_time = time) %>%
transform(sample_time = as_hms(sample_time),
analysis_time = as_hms(analysis_time)) %>%
# move info about duplicate sample and/or sample blank status into separate column
mutate(sample_condition = case_when(
grepl("DUP",sample_location) ~ "Field Duplicate")) %>%
# remove "DUP" designation from locations column
mutate(sample_location = str_replace(sample_location, "_DUP", "")) %>%
# replace "O" with zeros in location column
mutate(sample_location = str_replace(sample_location, "RM_O", "RM_0")) %>%
# add units of suspended solids
mutate(units = "mg/l") %>%
rename(result = s_s_mg_l) %>%
transform(result = as.numeric(result)) %>%
# add info about EPA analysis type from AWQMS template
mutate(epa_analysis_id = "2540-D",
analytical_method = "SM21-2540-+D",
context_code = "APHA",
note = "") %>%
# remove tare and paper weight values
select(-dried_wt,-paper_wt,-tare_wt_kg, -ml) %>%
# modify date/time formats
mutate(collect_date = as.character(paste(field_sample_date,sample_time)),
run_date_time = as.character(paste(date_of_analysis,analysis_time)), .keep = "unused") %>%
mutate(collect_time = as_hms(as.POSIXct(collect_date))) %>%
mutate(collect_date = date(as.POSIXct(collect_date)),
run_time = as_hms(ymd_hms(run_date_time)),
run_date = date(ymd_hms(run_date_time)),.keep = "unused") %>%
# rename
rename(analyst = signature) %>%
# miscellaneous
mutate(lab_sample = "",
matrix = "Water",
analyte = "Total suspended solids",
# loq = reporting limit
loq = 1.0,
# lod = sensitivity, or method detection limit
lod = 0.31,
lab_name = "Soldotna Wastewater Treatment Plant, Soldotna, Alaska")
# assign "sample_type"
%<>%
swwtp_tss21 mutate(sample_type = case_when(
== "Lab Blank" ~ "MB", # method blank
sample_condition == "Positive Control" ~ "LCS", # laboratory control sample
sample_condition TRUE ~ "PS"
))
# get site names consistent with AWQMS format
<- data.frame(unique(swwtp_tss21$sample_location))
swwtp_tss_sitenames
# delete existing csv if present
unlink("other/input/AQWMS/swwtp_tss_sitenames.csv")
# export csv of swwtp_tss site names
write.csv(swwtp_tss_sitenames,"other/input/AQWMS/swwtp_tss_sitenames.csv",row.names = F)
# use this list to create manually edited file, matched to AWQMS template names
# read in manually edited file
<- read_excel("other/input/AQWMS/swwtp_tss_site_names_matching_table_manual_edit.xlsx")
swwtp_tss_sitenames
# join correct site names to overall 2021 TSS dataset
<- left_join(swwtp_tss21,swwtp_tss_sitenames) %>%
swwtp_tss21 clean_names() %>%
rename(sample = sample_location)
## Joining, by = "sample_location"
########### Address Non-Detect values #########################
# Non-detect values should be left blank. A non-detect does not necessarily mean there was a zero observation of an analyte in a sample, it could be just be present at a level lower than the method detection level (lower than what the lab equipment can detect). Instead of 0, we’ll leave it blank. The Practical Quantitation Limit (or Limit of Quantitation) is presented alongside the result. When DEC evaluates a waterbody, they’ll use ½ the PQL as a stand-in for a non-detect. See explanatory document at other/documents/references/SGS DL, LOD, LOQ Interpretation.pdf for more details.
# Assign resultflag column. Use "=" if result > 1 and "U" if result < 1. See pg 17 of 2020 QAPP at "other/documents/QAPP/QAPP-2020-KenaiRiverWatershed_ZnCu.pdf"
%<>%
swwtp_tss21 mutate(resultflag = case_when(
< 1 & result > 0.31 ~ "J",
result < 0.31 ~ "U",
result TRUE ~ "="))
# modify non-detect values from "0" to "NA" if resultflag = U or ND
%<>%
swwtp_tss21 mutate(result1 = na_if(result,0)) %>%
select(-result) %>%
rename(result = result1)
# in future scripts need to ensure that non-detect "0" results (resultflag = "U") are shown as "NA" rather than zero.
######## 3/21/2022 --> no qa/qc results from SWWTP TSS on record yet. contact by email to see whats available. looking for lab duplicate and/or external qc check standard (pg 34 of QAPP)
# no response as of 3/29/22. need to call to ensure practice/records are in place by spring 2022
# segregate lab results from field results, and write lab qa/qc results to external csv
#swwtp_tss21_qaqc_dat <- swwtp_tss21 %>%
# filter(sample_type %in% c("MB","LCS"))
#write.csv(swwtp_tss21_qaqc_dat, "other/output/lab_qaqc_data/2021_lab_qaqc_data/swwtp_tss21_qaqc_dat.csv", row.names = F)
#rm(swwtp_tss21_qaqc_dat)
# join TSS data with overall dataset
<- bind_rows(dat,swwtp_tss21)
dat
rm(swwtp_spring21,swwtp_tss_sitenames,swwtp_tss21)
## Warning in rm(swwtp_spring21, swwtp_tss_sitenames, swwtp_tss21): object 'swwtp_spring21' not found
Additional Miscellaneous Code Used to Prepare Data For AQWMS Template
############### Miscellaneous Steps for Overall Field Results Dataframe #################
############# a.) filter out lab blanks and positive lab controls ##################
#dat %<>%
# filter(!sample_condition %in% c("Lab Blank","Positive Control")) # should already be gone now 3/4/22
############ b.) match latitude and longitude coordinates to sites ##################
## read in coordinates
<- read_excel("other/input/AQWMS/AQWMS_template_matching_table.xlsx", sheet = "site_coordinates") %>%
site_coords remove_empty()
## value for "which" not specified, defaulting to c("rows", "cols")
## New names:
## * `` -> `...5`
## * `` -> `...6`
## * `` -> `...7`
## * `` -> `...8`
## * `` -> `...9`
## * `` -> `...10`
## join coords to overall df
<- left_join(dat,site_coords) dat
## Joining, by = c("monitoring_location_name", "monitoring_location_id")
############ c.) assign "result sample fraction" (e.g. filtered, dissolved, etc.) ############
## read in manually organized table that pairs "result sample fraction" with "analytical method"
<- read_excel("other/input/AQWMS/AQWMS_template_matching_table.xlsx", sheet = "result_sample_fraction") %>%
result_sample_fraction filter(!is.na(analytical_method)) %>%
select(-description)
## join to table
<- left_join(dat,result_sample_fraction) dat
## Joining, by = "analytical_method"
################ d.) assign "result detection condition"##############
## read in manually assigned join table
<- read_excel("other/input/AQWMS/AQWMS_template_matching_table.xlsx", sheet = "result_detection_condition") %>%
result_detection_condition clean_names() %>%
filter(!is.na(resultflag))
## join to table
## z <- dat %>% left_join(dat,result_detection_condition, by = "resultflag")
## the attempt at left_join above is exhibiting nonsensical result for unclear reason. For now, define programmatically:
%<>%
dat mutate(result_detection_condition = case_when(
== "U" | resultflag == "ND" ~ "Not Detected",
resultflag == "J" ~ "Present Below Quantification Limit"))
resultflag
############### e.) assign chemical preservative type ################
## read in table
<- read_excel("other/input/AQWMS/AQWMS_template_matching_table.xlsx", sheet = "chemical_preservative") %>%
chemical_preservative filter(!is.na(preservative)) %>%
select(-description)
## join to overall dataframe
<- left_join(dat,chemical_preservative) dat
## Joining, by = "analytical_method"
############# f.) assign bottle type and color ######################
## read in table
<- read_excel("other/input/AQWMS/AQWMS_template_matching_table.xlsx", sheet = "sample_container_type_color") %>%
bottle_type_color select(-description) %>%
filter(!is.na(sample_container_type))
## join to overall dataframe
<- left_join(dat,bottle_type_color) dat
## Joining, by = "analytical_method"
rm(site_coords, result_sample_fraction, result_detection_condition, chemical_preservative, bottle_type_color)
############ g.) assign "Statistical Base Code" column ##############
# this value is not applicable to most results here, usually left blank. will assign "count": to fecal coliform
%<>%
dat mutate(stat_base_code = case_when(
== "Fecal Coliform" ~ "Count"
analyte
))
########### f.) "Activity ID" code shortening ######################
# The Activity ID column consists of concatenating the columns (`Monitoring Location ID`,"-",collect_date,"-",analyte,"-", sample_condition). This field is permitted to be no longer than 55 characters. With full names of analytes included, the entries sometimes exceed 55 characters.
# To address this, we will do two things:
## a.) use analyte abbreviations: for single elements, we will use their periodic table abbreviation. TBD for other analytes.
## b.) use abbreviations for sample_condition (designations of field blank or field dup)
## a.) analyte abbreviations
# export list of unique analytes from 2021 data
write.csv(data.frame(unique(dat$analyte)),"other/input/AQWMS/analytes_list.csv", row.names = F)
# manually assign abbreviations for each analyte in the sister file "analytes_list_manual_edit.csv"
# re-import edited list of analyte abbreviation names, then append to dataframe. Use in later step when creating Activity IDs.
<- read.csv("other/input/AQWMS/analytes_list_manual_edit.csv")
analyte_abbrev colnames(analyte_abbrev) <- c("analyte","analyte_abbreviation")
%<>%
dat left_join(analyte_abbrev)
## Joining, by = "analyte"
## b.) sample_condition abbreviations
%<>%
dat mutate(sample_condition_abbrv = case_when(
== "Field Duplicate" ~ "DUP",
sample_condition == "Trip Blank" ~ "Blank"))
sample_condition
########################## TO DO
# note 5/18/2022 -- some samples from 5/11/2021 no name creek have "PS" fro sample type but have "Field Duplicate" for sample_condition. need to fix.
############ g.) prepare final format ###############################
# create column structure from example in AQWMS template. Use existing input from SGS results if applicable, specify value from "Permitted Values" tab if SGS input not applicable or not yet specified
%<>%
dat # Proceeding left to right across columns of AWQMS template
# Mutate new column or rename existing column as needed
mutate(
`Monitoring Location ID` = monitoring_location_id,
`Activity Media Name` = "Water",
`Activity Media Subdivision Name` = "Surface Water",
# create activity ID name conditionally if condition(s) present
`Activity ID` = case_when(
is.na(sample_condition) ~ paste0(`Monitoring Location ID`,"-",collect_date,"-",analyte_abbreviation),
!is.na(sample_condition) ~ paste0(`Monitoring Location ID`,"-",collect_date,"-",analyte_abbreviation,"-",sample_condition_abbrv)),
`Activity Start Date` = collect_date,
`Activity Start Time` = collect_time,
`Activity End Date` = "",
`Activity End Time` = "",
`Activity Latitude` = latitude,
`Activity Longitude` = longitude,
`Activity Source Map Scale` = "",
`Activity Type` = case_when(
== "Field Duplicate" ~ "Quality Control Field Replicate Msr/Obs",
sample_condition == "Blank" ~ "Quality Control Sample-Trip Blank",
sample_condition == "TB" ~ "Quality Control Sample-Trip Blank",
sample_type TRUE ~ "Field Msr/Obs"),
# All samples are surface grab samples. Depths are assigned across the board here as 6 inches (~15 cm)
`Activity Depth/Height Measure` = 15,
`Activity Depth/Height Unit` = "cm",
# Next three columns not applicable for surface grab samples
`Activity Top Depth/Height Measure` = "",
`Activity Top Depth/Height Unit` = "",
`Activity Bottom Depth/Height Measure` = "",
`Activity Bottom Depth/Height Unit` = "",
`Activity Relative Depth Name` = "",
`Activity Comment` = note,
`Characteristic Name` = analyte,
`Result Analytical Method ID` = epa_analysis_id,
`Result Analytical Method Context` = context_code,
`Method Speciation` = "",
`Result Value` = result,
`Result Unit` = units,
`Result Qualifier` = resultflag,
`Result Weight Basis` = "Sampled",
`Statistical Base Code` = stat_base_code,
`Result Sample Fraction` = result_sample_fraction,
`Result Value Type` = "Actual",
`Result Comment` = "",
`Sample Collection Method ID` = "",
`Equipment ID` = "Water Bottle",
`Result Detection Condition` = result_detection_condition,
`Result Detection Limit Type 1` = "Limit of Quantitation",
`Result Detection Limit Value 1` = loq,
`Result Detection Limit Unit 1` = units,
# note: lod = "limit of detection"; equivalent to "method detection level"
`Result Detection Limit Type 2` = "Method Detection Level",
`Result Detection Limit Value 2` = lod,
`Result Detection Limit Unit 2` = units,
`Laboratory Accreditation Indicator` = "",
`Laboratory Name` = lab_name,
`Laboratory Sample ID` = lab_sample,
`Analysis Start Date` = run_date,
`Analysis Start Time` = run_time,
`Biological Intent` = "",
`Subject Taxonomic Name` = "",
`Thermal Preservative` = "Cold packs",
`Sample Container Type` = sample_container_type,
`Sample Container Color` = sample_container_color,
`Chemical Preservative` = preservative
# remove columns that were mutated to a new name
.keep = "unused")
,
# Save a copy of the whole dataframe with all results and parameters prior to reducing it to just the column subset. We will use this later in QA/QC analyses.
<- dat
all_dat
# next, for our AQWMS export, we want to retain just those columns listed in the AQWMS template
# found solution to this problem here: https://gist.github.com/djhocking/62c76e63543ba9e94ebe
# get all column names from AQWMS template
<- read_excel("other/input/AQWMS/AWQMS_KWF_Baseline_2021.xlsx", sheet = "KWF Baseline AWQMS Template") %>%
aqwms_colnames colnames()
# select from subset of column in aqwms template
%<>%
dat select(one_of(aqwms_colnames))
# export final formatted AQWMS results to external csv
write.csv(dat,"other/output/aqwms_formatted_results/2021_kwf_baseline_results_aqwms.csv",row.names = F)
A.1.3 Data Evaluation
Prior to submission to ADEC, all water quality data must be checked against a series of standard questions in order to evaluate how quality assurance / quality check (QA/QC) processes are instituted. The draft Data Evaluation Checklist Template (available for download below) outlines these questions:
Download Draft ADEC Draft Data Evaluation Checklist Template
A.1.3.1 Pre-Database
A.1.3.1.1 Overall Project Success
Show/Hide Code used to Evaluate Overall Project Success
## Warning in rm(spring21_planned, summer21_planned): object 'spring21_planned' not found
## Warning in rm(spring21_planned, summer21_planned): object 'summer21_planned' not found
## [1] TRUE
- Did the project follow the QAPP? Yes. Were there any deviations from the sampling plan? Yes. Refer to Individual Answers Below.
- Was the data collected representative of environmental conditions? Yes. Notes: Study design is intended to create two single-day snapshots, one in spring and another in summer, across a wide geographical area of the Kenai River watershed.
- Are site names, dates, and times correct and as expected? Yes, after post-season correction documented in this report. Notes: In 2021 Kenai Watershed Forum used pre-printed labels on all sample bottles, reducing opportunity for field and lab transcription errors. Remaining site name transcription errors were corrected in post-season data review.
- Is the dataset complete and did you receive the expected number of results? TBD. Notes: we acquired the expected number of results in 2021 sampling events. All sites described in the QAPP were visited twice in 2021, and all water sample collections were successfully conducted. However most intrinsic water quality parameters mentioned in the current QAPP were not collected in 2021.
Data evaluation is currently in progress. The values in the downloadable table below are current as of 2022-06-08. The numerator values of “Samples Submitted to ADEC” and “Usable Samples” are not yet finalized and will depend on feedback received from DEC as part of this pre-database review process.
- Completeness Measure A = (Results Collected) / (Results Submitted to ADEC) \*100%
- Project goal: ***85%***
- Calculated project completeness Measure A: Varies by parameter, see downloadable file below.
- Completeness Measure B = (Planned Number of Results) / (Useable Number of Results) \* 100%
- Project goal: ***60%***
- Calculated project completeness Measure B: Varies by parameter, see downloadable file below. *These values will be updated as revisions from DEC dictate the number of usable (publishable) samples.*
Download Summary Table of Planned vs. Actual Lab Analysis Results for 2021
- Were field duplicates, blanks, and/or other QC samples collected as planned? Yes, with one exception, see below
Download Summary Table of Planned vs. Actual Trip Blank and Field Duplicate Samples for 2021
From the above downloadable table, “Planned vs. Actual Trip Blank and Field Duplicate Samples for 2021,” we observe that data from one field duplicate sample is missing. The missing sample is for the 200.7 (total metals) analyses on 2021-05-11 at RM 31 (Morgan’s Landing). This error occurred due to a transcription error on the chain of custody form, and the lab did not process a total metals analysis for this site/event.
A.1.3.1.1.1 2021 Field QA/QC Sample Collection Summary
Field duplicates:
- # required: 22
- # collected 21
Trip blanks:
- # required: 4
- # collected: 4
Other:
- # required: NA
- # collected: NA
- Are the duplicate sample(s) RPD within range described in QAPP? Varies by Parameter, see downloadable summary table below
RPD (Relative Percent Difference) = ((A - B)*100) / ((A + B) / 2)
RPD goal from QAPP(%): Varies by Parameter, See Current QAPP
View duplicate RPD calculations and a summary table in supporting excel file; including parameters, site names, dates, results, and RPD values: Download Table Below
Note: we applied standards from ADEC to determine which observations may be evaluated in RPD calculations. From ADEC 2022, “Estimated values (detected at levels lower than the practical quantitation limit (PQL)) were treated as non-detects for the purpose of this analysis. A set of paired samples was evaluated for RPD only if:
one or both of the samples were above the PQL;
one or both of the samples were at least two times the PQL.”
The above table indicates that two RPD values were outside of acceptable QA/QC ranges (>20% RPD). An inquiry has been emailed to ADEC on 2022-06-08 on how best to apply this criteria, and how it will affect the extent of the finalized dataset.
A.1.3.1.2 In-situ Field Data and Instruments
Were there any issues with instrument calibration? No. Did the instrument perform as expected? Yes. Notes: The sole in-situ field measurement type recorded in 2021 was for water temperature.
Was instrument calibration performed according to the QAPP and instrument recommendations? Yes. Were calibration logs or records kept? Yes. Notes: Instrument calibration records are available upon request at hydrology@kenaiwatershed.org.
Was instrument verification during the field season performed according to the QAPP and instrument recommendations? NA . Were verification logs or records kept? No logger instruments were deployed, thus no verification checks were performed..
Do the instrument data files site IDs, time stamps and file names match? No logger instruments were deployed, thus no instrument data files were generated..
Is any field data rejected and why? Yes. As of this raw data review last updated on 2022-06-08, the RPD value for Total Phosphorus on 5/11/2021 from the site “KR RM 31” is 77.03%, well above the project precision goal for this parameter of 20%. Additionally, the RPD value for fecal coliform on 5/11/2021 at the site “KR RM 0 NNC” is 51.4%, however this parameter doe snot have overall project accuracy or precision goals according to the current QAPP..
A.1.3.1.3 Analytical Laboratory Reports and Results
Do the laboratory reports provide results for all sites and parameters? Yes. The laboratory reports provide results for all sites and parameters specified in the Chains of Custody. However in one case, a transcription error on the chain of custody form for SGS labs in Spring 2021 resulted in missing result values for the method 200.7 analyses (Total Metals) from 2021-05-11 at RM 31 (Morgan’s Landing).
Were the appropriate analytical methods used for all parameters? Yes.
Do the laboratory reports match the COC and requested methods? Yes. Are same methods used throughout? Yes.
Are the number of samples on the laboratory reports the same as on the COC? Yes.
Is a copy of the COC included with the laboratory reports? Yes.
Were preservation, hold time and temperature requirements met? Yes. Summer and Spring 2021 holding time requirements were met for all samples. See downloadable files below. Laboratory result documents indicated no compromises of preservation and temperature requirements.
Download Table of Maximum Holding Times for Each Sample Type
Download Holding Time Calculations for Spring and Summer 2021 Field Samples
Are there any project specific concerns (e.g. total v. dissolved, MST, etc)? Yes. Communication with the ADEC office in Winter 2022 includes the recommendation to modify field practices with relation to dissolved metals sampling. These practices include a.) filtering samples in lab rather than in the field, and b.) incorporating a field blank for dissolved metals. These practices are integrated into the project as of summer 2022 and will be reflected in the QAPP, currently in the process of being updated.
Was all supporting info provided in the laboratory report, such as reporting limits for all analyses and definitions? Yes, see raw results in previous section.
Were there any laboratory discrepancies, errors, data qualifiers, or QC failures (review laboratory duplicates, matrix spikes and blanks)? Yes. These laboratory-identified discrepancies are outlined in detail below. As of 2022-06-08 we would like to consult with ADEC to determine if these laboratory-identified discrepancies are grounds for rejecting field observations.
Show/Hide Code used to Evaluate Lab Duplicates, Matrix Spikes, and Blanks
# calculate qa/qc discrepancies as detailed in PDF reports check qapp for guidance, or check w/ qa officer...
<- read.csv("other/output/lab_qaqc_data/2021_lab_qaqc_data/sgs21_als21_qaqc_dat.csv") %>%
matrix_spike_limits select(lab_name,sample,collect_date,extracted_date,sample_type,result,analyte,dissolved,analytical_method,
resultflag,percent_recovered,rec_limit_low,rec_limit_high,sample_rpd,rpd_limit_low,rpd_limit_high,%>%
loq,lod,detection_limit,sample_condition)
# Matrix Spike Recovery
# calculate if matrix spike recovery limits pass requirements
mutate(rec_limit_pass = case_when(
is.na(rec_limit_low) ~ "",
> rec_limit_high |
percent_recovered < rec_limit_low ~ "N",
percent_recovered TRUE ~ "Y"
))
# calculate total number of matrix spike cases where recovery exceeded limits
<- matrix_spike_limits %>%
matrix_spike_recovery_fails filter(rec_limit_pass == "N") %>%
nrow() %>%
as.numeric()
# generate table of specific failed samples
<- matrix_spike_limits %>%
matrix_spike_recovery_fails_tbl filter(rec_limit_pass == "N")
write.csv(matrix_spike_recovery_fails_tbl,"other/output/lab_qaqc_data/matrix_spike_recovery_fails.csv")
The following discrepancies were identified in the results from SGS laboratories:
A.1.3.1.3.1 Matrix Spike Recoveries
A total of 6 matrix spike or matrix spike duplicate samples are outside of QC criteria. The limit of recovery range for the analyte “Total Nitrate/Nitrite-N” is 90% - 110%. For these matrix spike measurements outside of the QC criteria, recovery levels range from 112% - 118%.
Download Matrix Spike Recovery Values Exceeding Threshold for Spring and Summer 2021 Field Samples
No additional lab QA/QC anomalies for any other parameters were noted by any laboratories contracted in 2021, including lab duplicates and lab blanks. Additional details are available upon request at hydrology@kenaiwatershed.org.
A.1.3.1.3.2 Total vs Dissolved Metals
We will verify that the quantity of total metals is less than that of dissolved metals… [in progress]
## Warning in `Result Analytical Method ID` == c("200.7", "200.8"): longer object length is not a multiple of shorter object
## length
## `summarise()` has grouped output by 'Monitoring Location ID', 'Activity Start Date', 'Result Unit'. You can override
## using the `.groups` argument.
Is any laboratory data rejected and why? Yes, see question 6 on Relative Percent Difference values.
Was the QA Officer consulted for any data concerns? We are communicating with the Soldotna ADEC office for this data submission. We will schedule a meeting with the QA office with their coordination when appropriate.
Subsequent steps in the “Database Prep” and “Database Import” phases are conducted by ADEC staff. See the above Data Evaluation Checklist Template for details.