Code
# load SF tide gauge data
# read in data and get annual maxima
function load_data(fname)
date_format = DateFormat("yyyy-mm-dd HH:MM:SS")
# This uses the DataFramesMeta.jl package, which makes it easy to string together commands to load and process data
df = @chain fname begin
CSV.read(DataFrame; header=false)
rename("Column1" => "year", "Column2" => "month", "Column3" => "day", "Column4" => "hour", "Column5" => "gauge")
# need to reformat the decimal date in the data file
@transform :datetime = DateTime.(:year, :month, :day, :hour)
# replace -99999 with missing
@transform :gauge = ifelse.(abs.(:gauge) .>= 9999, missing, :gauge)
select(:datetime, :gauge)
end
return df
end
dat = load_data("data/surge/h551.csv")
# detrend the data to remove the effects of sea-level rise and seasonal dynamics
ma_length = 366
ma_offset = Int(floor(ma_length/2))
moving_average(series,n) = [mean(@view series[i-n:i+n]) for i in n+1:length(series)-n]
dat_ma = DataFrame(datetime=dat.datetime[ma_offset+1:end-ma_offset], residual=dat.gauge[ma_offset+1:end-ma_offset] .- moving_average(dat.gauge, ma_offset))
# group data by year and compute the annual maxima
dat_ma = dropmissing(dat_ma) # drop missing data
dat_annmax = combine(dat_ma -> dat_ma[argmax(dat_ma.residual), :], groupby(transform(dat_ma, :datetime => x->year.(x)), :datetime_function))
delete!(dat_annmax, nrow(dat_annmax)) # delete 2023; haven't seen much of that year yet
rename!(dat_annmax, :datetime_function => :Year)
select!(dat_annmax, [:Year, :residual])
dat_annmax.residual = dat_annmax.residual / 1000 # convert to m
# make plots
p1 = plot(
dat_annmax.Year,
dat_annmax.residual;
xlabel="Year",
ylabel="Annual Max Tide Level (m)",
label=false,
marker=:circle,
markersize=5,
tickfontsize=16,
guidefontsize=18,
left_margin=5mm,
bottom_margin=5mm
)
n = nrow(dat_annmax)
linfit = lm(@formula(residual ~ Year), dat_annmax)
pred = coef(linfit)[1] .+ coef(linfit)[2] * dat_annmax.Year
plot!(p1, dat_annmax.Year, pred, linewidth=3, label="Linear Trend")