|
| 1 | +## Commercial ESP indicators Script |
| 2 | +## translated from Stata code 2/24/2026 |
| 3 | +## Owner: Samantha Werner |
| 4 | + |
| 5 | +#overview-- This code creates six economic commercial fishing indicators used in ESP work. |
| 6 | +# The code below pulls data, deflates any monetary values and formats data to what is needed for the time series plots. |
| 7 | + |
| 8 | +#Indicators created: Commercial Landings (LBS): The total weight of the species landed (e.g., Commercial_LONGFINSQUID_Landings_LBS). |
| 9 | + |
| 10 | +#Number of Commercial Vessels: The count of unique permits landing that species (e.g., N_Commercial_Vessels_Landing_LONGFINSQUID). |
| 11 | +#Average Price per Pound: The average annual price, winsorized to handle outliers and adjusted for inflation (e.g., AVGPRICE_LONGFINSQUID_2024_DOLlb). |
| 12 | +#Total Annual Revenue: The total value of all landings for that species, adjusted for inflation (e.g., TOTALANNUALREV_LONGFINSQUID_2024Dols). |
| 13 | +#Average Revenue per Vessel: The average revenue earned per permit per year, adjusted for inflation (e.g., AVGVESREVperYr_LONGFINSQUID_2024_DOLlb). |
| 14 | +#Average Annual Diesel Price: The price of Ultra-Low-Sulfur No. 2 Diesel (from FRED), adjusted for inflation (e.g., AVGANNUAL_DIESEL_PRICE2024dols). |
| 15 | + |
| 16 | +## This uses CFDERS data but may need to be updated to CAMs |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | +#######################Running the code steps########################## |
| 21 | +#1. Change top of the code to update: |
| 22 | +# - Oracle log in information |
| 23 | +# - The time series you want |
| 24 | +# - The species (nespp3) and how you want your species to be named within the end file (sppname) |
| 25 | +# - the year you want to deflate to |
| 26 | + |
| 27 | +## before running!!!# |
| 28 | +# - make sure you are connected to VPN |
| 29 | +#-- ensure all packages are installed |
| 30 | +# -- you followed the steps above |
| 31 | +#-- you have a folder in your directory called "data" |
| 32 | + |
| 33 | + |
| 34 | +#install.packages("ROracle") |
| 35 | +#install.packages("DescTools") |
| 36 | + |
| 37 | +library(ROracle) |
| 38 | +library(DBI) |
| 39 | +library(fredr) |
| 40 | +library(tidyverse) |
| 41 | +library(DescTools) # For Winsorize |
| 42 | + |
| 43 | +###################################################################### |
| 44 | +############################update code below to match the years, species and user information############# |
| 45 | +##################################################################### |
| 46 | + |
| 47 | +# 1. Setup Credentials |
| 48 | +ora_id <- "INSERT USERNAME HERE" |
| 49 | +oraprod_pw <- "INSERT PASSWORD HERE" |
| 50 | + |
| 51 | +spp_name <- "LONGFINSQUID" # Replace with your actual species name |
| 52 | +nespp3_codes <- "('801')" # Note the single quotes inside the string for SQL |
| 53 | + |
| 54 | +START.YEAR <- 1996 |
| 55 | +END.YEAR <- 2025 |
| 56 | + |
| 57 | +## set year you would like to deflate to |
| 58 | +deflate_yr <- 2025 |
| 59 | + |
| 60 | +# This looks for a folder named 'data' then 'intermediate' inside your current directory |
| 61 | +data_intermediate <- file.path("data/intermediate") |
| 62 | + |
| 63 | +# Set your API key for FRED data (deflation) you may need to log into FRED API Keys and request a new one. https://fred.stlouisfed.org/docs/api/api_key.html |
| 64 | +fredr_set_key("a09e5d083681605146191f4996992c6e") |
| 65 | + |
| 66 | +################################################################# |
| 67 | +##no editiing should be needed past this point |
| 68 | +##################################################### |
| 69 | +# 2. Build the connection string |
| 70 | +shost <- "nefsc-prod-01-db.nmfs.noaa.gov" |
| 71 | +port <- 1521 |
| 72 | +ssid <- "NEFSC_DB_PROD.nefscproddbsn.nefscprodvcn.oraclevcn.com" |
| 73 | + |
| 74 | +# Using the name consistent with your loop |
| 75 | +nefscusers.connect.string <- paste0( |
| 76 | + "(DESCRIPTION=", |
| 77 | + "(ADDRESS=(PROTOCOL=tcp)(HOST=", shost, ")(PORT=", port, "))", |
| 78 | + "(CONNECT_DATA=(SERVICE_NAME=", ssid, ")))" |
| 79 | +) |
| 80 | + |
| 81 | +# 3. Connect ONCE outside the loop |
| 82 | +drv <- dbDriver("Oracle") |
| 83 | +conn <- dbConnect(drv, ora_id, password = oraprod_pw, dbname = nefscusers.connect.string) |
| 84 | + |
| 85 | +##### 4. Price deflator |
| 86 | + |
| 87 | +gdp_deflator <- fredr( |
| 88 | + series_id = "GDPDEF", |
| 89 | + observation_start = as.Date(paste0(START.YEAR, "-01-01")), |
| 90 | + observation_end = as.Date(paste0(END.YEAR, "-12-31")), |
| 91 | + frequency = "a" # Annual |
| 92 | +) %>% |
| 93 | + mutate(YEAR = as.numeric(format(date, "%Y"))) %>% |
| 94 | + select(YEAR, GDPDEF = value) |
| 95 | + |
| 96 | + |
| 97 | +# 1. Build the query string using your R variables |
| 98 | +# We use paste0 to insert the species name into the column alias |
| 99 | +# and the codes into the WHERE clause. |
| 100 | +query_landings <- paste0( |
| 101 | + "SELECT YEAR, SUM(SPPLNDLB) AS total_", spp_name, |
| 102 | + " FROM NEFSC_GARFO.CFDERS_ALL_YEARS ", |
| 103 | + " WHERE NESPP3 IN ", nespp3_codes, |
| 104 | + "AND YEAR BETWEEN ", START.YEAR, " AND ", END.YEAR, |
| 105 | + " GROUP BY YEAR ORDER BY YEAR" |
| 106 | +) |
| 107 | + |
| 108 | +# 2. Execute the query |
| 109 | +landings_data <- dbGetQuery(conn, query_landings) |
| 110 | + |
| 111 | +# 3. View the result |
| 112 | +print(head(landings_data)) |
| 113 | + |
| 114 | + |
| 115 | +# Standardize the data frame to match the required indicator format |
| 116 | +landings_final <- landings_data %>% |
| 117 | + mutate( |
| 118 | + CATEGORY = "Commercial", |
| 119 | + INDICATOR_NAME = paste0("Commercial_", spp_name, "_Landings_LBS"), |
| 120 | + SIGN = "N/A", |
| 121 | + INDICATOR_TYPE = "Socioeconomic" |
| 122 | + ) %>% |
| 123 | + # Rename the sum column to DATA_VALUE (Equivalent to Stata rename) |
| 124 | + rename(DATA_VALUE = !!paste0("TOTAL_", spp_name)) %>% |
| 125 | + select(YEAR, DATA_VALUE, CATEGORY, INDICATOR_NAME, SIGN, INDICATOR_TYPE) |
| 126 | + |
| 127 | +# |
| 128 | + |
| 129 | +######################### n vessels pull ############# |
| 130 | + |
| 131 | +# 1. Build the query string |
| 132 | +# I simplified the alias to N_VESSELS so the rename() below actually works. |
| 133 | +# I also added a space before 'FROM' to prevent syntax errors. |
| 134 | +query_Nvessels <- paste0( |
| 135 | + "SELECT YEAR, count(distinct PERMIT) AS N_VESSELS ", |
| 136 | + "FROM NEFSC_GARFO.CFDERS_ALL_YEARS ", |
| 137 | + "WHERE NESPP3 IN ", nespp3_codes, |
| 138 | + " AND YEAR BETWEEN ", START.YEAR, " AND ", END.YEAR, |
| 139 | + " GROUP BY YEAR ORDER BY YEAR" |
| 140 | +) |
| 141 | + |
| 142 | +# 2. Execute the query |
| 143 | +Nvessels_data <- dbGetQuery(conn, query_Nvessels) |
| 144 | + |
| 145 | +# 3. Format to match Stata indicators |
| 146 | +Nvessels_final <- Nvessels_data %>% |
| 147 | + mutate( |
| 148 | + CATEGORY = "Commercial", |
| 149 | + # This creates the long name you want in the final table |
| 150 | + INDICATOR_NAME = paste0("N_Commercial_Vessels_Landing_", spp_name), |
| 151 | + SIGN = "N/A", |
| 152 | + INDICATOR_TYPE = "Socioeconomic" |
| 153 | + ) %>% |
| 154 | + # Now this rename will work because the SQL alias matches 'N_VESSELS' |
| 155 | + # Note: Oracle often returns names in UPPERCASE, so we check for both. |
| 156 | + rename(DATA_VALUE = any_of(c("N_VESSELS", "N_vessels"))) %>% |
| 157 | + select(YEAR, DATA_VALUE, CATEGORY, INDICATOR_NAME, SIGN, INDICATOR_TYPE) |
| 158 | + |
| 159 | +# 4. View the result |
| 160 | +print(head(Nvessels_final)) |
| 161 | + |
| 162 | +##################average prices################## |
| 163 | + |
| 164 | +#1. Pull Price Data from Oracle --- |
| 165 | + # Uses the 'conn' object you already established |
| 166 | + query_price <- paste0( |
| 167 | + "SELECT SPPVALUE, SPPLNDLB, YEAR FROM NEFSC_GARFO.CFDERS_ALL_YEARS ", |
| 168 | + "WHERE NESPP3 IN ", nespp3_codes, |
| 169 | + " AND YEAR BETWEEN ", START.YEAR, " AND ", END.YEAR |
| 170 | + ) |
| 171 | + |
| 172 | +price_raw <- dbGetQuery(conn, query_price) |
| 173 | + |
| 174 | +#--- 2. Calculate Average Annual Prices (Manual Winsorize) --- |
| 175 | + price_annual <- price_raw %>% |
| 176 | + mutate(price_lb = SPPVALUE / SPPLNDLB) %>% |
| 177 | + # Remove Infinity or NA if pounds were 0 |
| 178 | + filter(is.finite(price_lb)) %>% |
| 179 | + group_by(YEAR) %>% |
| 180 | + mutate( |
| 181 | + # Calculate the 1st and 99th percentiles for this year |
| 182 | + p01 = quantile(price_lb, 0.01, na.rm = TRUE), |
| 183 | + p99 = quantile(price_lb, 0.99, na.rm = TRUE), |
| 184 | + # "Squish" values outside that range (This is Winsorizing!) |
| 185 | + price_lb_w = case_when( |
| 186 | + price_lb < p01 ~ p01, |
| 187 | + price_lb > p99 ~ p99, |
| 188 | + TRUE ~ price_lb |
| 189 | + ) |
| 190 | + ) %>% |
| 191 | + summarise(AVG_NOMINAL_PRICE = mean(price_lb_w, na.rm = TRUE)) %>% |
| 192 | + ungroup() |
| 193 | + |
| 194 | +# --- 3. Adjust for Inflation (Deflate) --- |
| 195 | +# (Keep this the same as before) |
| 196 | + |
| 197 | +base_index_val <- gdp_deflator$GDPDEF[gdp_deflator$YEAR == deflate_yr] |
| 198 | + |
| 199 | +price_final <- price_annual %>% |
| 200 | + left_join(gdp_deflator, by = "YEAR") %>% |
| 201 | + mutate( |
| 202 | + DATA_VALUE = (AVG_NOMINAL_PRICE / GDPDEF) * base_index_val, |
| 203 | + CATEGORY = "Commercial", |
| 204 | + INDICATOR_NAME = paste0("AVGPRICE_", spp_name, "_", deflate_yr, "_DOLlb"), |
| 205 | + SIGN = "N/A", |
| 206 | + INDICATOR_TYPE = "Socioeconomic" |
| 207 | + ) %>% |
| 208 | + select(YEAR, DATA_VALUE, CATEGORY, INDICATOR_NAME, SIGN, INDICATOR_TYPE) |
| 209 | + |
| 210 | +# --- 4. Cleanup --- |
| 211 | + |
| 212 | + |
| 213 | +print(price_final) |
| 214 | + |
| 215 | + |
| 216 | +##To ensure all indicators respect your START.YEAR and END.YEAR variables, I have integrated those filters into both the SQL queries (for database pulls) and the fredr calls (for external diesel data). |
| 217 | + |
| 218 | + |
| 219 | + |
| 220 | + |
| 221 | +################## Total Annual Revenues ################## |
| 222 | + |
| 223 | +# 1. Pull Revenue Data |
| 224 | +query_revs <- paste0( |
| 225 | + "SELECT YEAR, SUM(SPPVALUE) AS TOTAL_REV ", |
| 226 | + "FROM NEFSC_GARFO.CFDERS_ALL_YEARS ", |
| 227 | + "WHERE NESPP3 IN ", nespp3_codes, |
| 228 | + " AND YEAR BETWEEN ", START.YEAR, " AND ", END.YEAR, |
| 229 | + " GROUP BY YEAR ORDER BY YEAR" |
| 230 | +) |
| 231 | +revs_raw <- dbGetQuery(conn, query_revs) |
| 232 | + |
| 233 | +# 2. Deflate and Format |
| 234 | +revs_final <- revs_raw %>% |
| 235 | + left_join(gdp_deflator, by = "YEAR") %>% |
| 236 | + mutate( |
| 237 | + DATA_VALUE = (TOTAL_REV / GDPDEF) * base_index_val, |
| 238 | + CATEGORY = "Commercial", |
| 239 | + INDICATOR_NAME = paste0("TOTALANNUALREV_", spp_name, "_", deflate_yr, "Dols"), |
| 240 | + SIGN = "N/A", |
| 241 | + INDICATOR_TYPE = "Socioeconomic" |
| 242 | + ) %>% |
| 243 | + select(YEAR, DATA_VALUE, CATEGORY, INDICATOR_NAME, SIGN, INDICATOR_TYPE) |
| 244 | + |
| 245 | +################## Fuel Prices ################## |
| 246 | + |
| 247 | +# 1. Pull Diesel Price from FRED with Year Range |
| 248 | +fuel_raw <- fredr( |
| 249 | + series_id = "DDFUELNYH", |
| 250 | + observation_start = as.Date(paste0(START.YEAR, "-01-01")), |
| 251 | + observation_end = as.Date(paste0(END.YEAR, "-12-31")), |
| 252 | + frequency = "a" |
| 253 | +) %>% |
| 254 | + mutate(YEAR = as.numeric(format(date, "%Y"))) %>% |
| 255 | + # Stata 'drop if missing(DDFUELNYH)' equivalent: |
| 256 | + filter(!is.na(value)) %>% |
| 257 | + select(YEAR, DDFUELNYH = value) |
| 258 | + |
| 259 | +# 2. Deflate and Format |
| 260 | +fuel_final <- fuel_raw %>% |
| 261 | + left_join(gdp_deflator, by = "YEAR") %>% |
| 262 | + mutate( |
| 263 | + DATA_VALUE = (DDFUELNYH / GDPDEF) * base_index_val, |
| 264 | + CATEGORY = "Commercial", |
| 265 | + INDICATOR_NAME = paste0("AVGANNUAL_DIESEL_PRICE", deflate_yr, "dols"), |
| 266 | + SIGN = "N/A", |
| 267 | + INDICATOR_TYPE = "Socioeconomic" |
| 268 | + ) %>% |
| 269 | + select(YEAR, DATA_VALUE, CATEGORY, INDICATOR_NAME, SIGN, INDICATOR_TYPE) |
| 270 | + |
| 271 | +############ Average Revenue Per Vessel ################## |
| 272 | + |
| 273 | +# 1. Pull Revenue per Permit/Year with Year Range |
| 274 | +query_ves_rev <- paste0( |
| 275 | + "SELECT YEAR, PERMIT, SUM(SPPVALUE) AS VESSEL_TOTAL_REV ", |
| 276 | + "FROM NEFSC_GARFO.CFDERS_ALL_YEARS ", |
| 277 | + "WHERE NESPP3 IN ", nespp3_codes, |
| 278 | + " AND YEAR BETWEEN ", START.YEAR, " AND ", END.YEAR, |
| 279 | + " GROUP BY YEAR, PERMIT" |
| 280 | +) |
| 281 | +ves_rev_raw <- dbGetQuery(conn, query_ves_rev) |
| 282 | + |
| 283 | +# 2. Calculate Mean per Year and Deflate |
| 284 | +av_ves_rev_final <- ves_rev_raw %>% |
| 285 | + group_by(YEAR) %>% |
| 286 | + summarise(AVG_VESSEL_REV = mean(VESSEL_TOTAL_REV, na.rm = TRUE)) %>% |
| 287 | + left_join(gdp_deflator, by = "YEAR") %>% |
| 288 | + mutate( |
| 289 | + DATA_VALUE = (AVG_VESSEL_REV / GDPDEF) * base_index_val, |
| 290 | + CATEGORY = "Commercial", |
| 291 | + INDICATOR_NAME = paste0("AVGVESREVperYr_", spp_name, "_", deflate_yr, "_DOLlb"), |
| 292 | + SIGN = "N/A", |
| 293 | + INDICATOR_TYPE = "Socioeconomic" |
| 294 | + ) %>% |
| 295 | + select(YEAR, DATA_VALUE, CATEGORY, INDICATOR_NAME, SIGN, INDICATOR_TYPE) |
| 296 | + |
| 297 | +##disconnect from oracle |
| 298 | +dbDisconnect(conn) |
| 299 | +################## MASTER APPEND ################## |
| 300 | + |
| 301 | +# 1. Create a list of all your final data frames |
| 302 | +# This acts like a 'stack' of all the indicators you just created |
| 303 | +indicator_list <- list( |
| 304 | + landings_final, |
| 305 | + Nvessels_final, |
| 306 | + price_final, |
| 307 | + revs_final, |
| 308 | + fuel_final, |
| 309 | + av_ves_rev_final |
| 310 | +) |
| 311 | + |
| 312 | +# 2. Use bind_rows to stack them into one long file |
| 313 | +# This is identical to running 'append' multiple times in Stata |
| 314 | +final_master_file <- bind_rows(indicator_list) |
| 315 | + |
| 316 | +# 3. Final Quality Check (Filtering by your start/end years) |
| 317 | +final_master_file <- final_master_file %>% |
| 318 | + filter(YEAR >= START.YEAR & YEAR <= END.YEAR) %>% |
| 319 | + arrange(INDICATOR_NAME, YEAR) |
| 320 | + |
| 321 | +# 4. Save the file (Equivalent to Stata's 'save ..., replace') |
| 322 | +# Use file.path to make sure the folder and filename are joined correctly |
| 323 | +write.csv( |
| 324 | + final_master_file, |
| 325 | + file = file.path(data_intermediate, paste0(spp_name, "_Commercial_Indicators_Master.csv")), |
| 326 | + row.names = FALSE |
| 327 | +) |
| 328 | + |
| 329 | +# 5. View a summary of what you appended |
| 330 | +print(table(final_master_file$INDICATOR_NAME)) |
| 331 | + |
| 332 | + |
| 333 | + |
| 334 | + |
| 335 | + |
| 336 | + |
0 commit comments