Tidal analysis in python can be quite difficult. During my MatlabTM times I used the t_tide a lot and I end up comparing other packages to t_tide.
The only python alternative one that comes close to t_tide is tappy.
Tappy seems to be a good one, but it was build to be a command line interface, therefore it lacks an interactive mode. Also, it is missing several of t_tide's features, like analyzing a complex vector ($u+iv$).
Because of these shortcomings I still rely on t_tide for my analyses. This post is to show how you can call t_tide toolbox from python using octave.
We will use elevation data from the GLOSS Station 194, loading it in a pandas DataFrame to make our job easier.
from datetime import datetime
from pandas import read_table, DataFrame
def date_parser(year, day, month, hour):
year, day, month, hour = map(int, (year, day, month, hour))
return datetime(year, day, month, hour)
names = ['year', 'month', 'day', 'hour', 'elev', 'flag']
obs = read_table('./data/can1998.dtf', names=names, skipinitialspace=True, delim_whitespace=True,
index_col='datetime', usecols=range(1, 7), na_fvalues='9.990',
parse_dates={'datetime': ['year', 'month', 'day','hour']}, date_parser=date_parser)
obs.head(6)
The metadata explains that there are two flag values, one for bad data and another one for corrected data. A quick plot can help us visualize the flags.
import numpy as np
import matplotlib.pyplot as plt
bad = obs['flag'] == 2
corrected = obs['flag'] == 1
fig, ax = plt.subplots(figsize=(8, 3))
ax.plot(obs.index, obs['elev'], alpha=0.5)
ax.plot(obs.index[bad], obs['elev'][bad], 'ro', label='bad')
ax.plot(obs.index[corrected], obs['elev'][corrected], 'go', label='corrected')
ax.legend(numpoints=1, loc='upper right')
# Remove the mean, and interpolate the bad data points.
obs[bad] = np.NaN
obs['anomaly'] = obs['elev'] - obs['elev'].mean()
obs['anomaly'] = obs['anomaly'].interpolate() # Interpolate gaps.
ax = obs['anomaly'].plot(figsize=(8, 3), alpha=0.5)
ax.set_xlabel('')
_ = ax.set_ylabel(u'Height [m]')
obs['elev'].describe()
The data seems to be OK. Now we can go to the next step.
To call octave we will use IPython's octave extension. The extension relies on the oct2py module to create a two-way connection with octave, so you will need to install octpy.
t_tide needs pwelch
, so we need to install octave's
signal toolbox and its dependencies as well.
For that, just type the lines below inside an octave session.
pkg install -forge specfun
pkg install -forge control
pkg install -forge general
pkg install -forge signal
We are almost there, the last two steps are a just a few changes in the t_tide toolbox to make it more "octave friendly."
In
t_tide.m
we need to change pwelch's overlap argument fromceil(nx/2)
to something between 0, 0.95. I ended up leaving it blank[]
(the default).The last case block in
t_getconsts.m
conflicts with octave internalctime
. Since it is not usually used I just commented it out.
The line below is just to pass the elevation data as a numpy array to oct2py.
elev = obs['anomaly'].values
%load_ext oct2py.ipython
%%octave -i elev -o tidestruc -o pout
pkg load all
addpath(genpath('./t_tide_v1.3beta/'))
[tidestruc, pout] = t_tide(elev, 'interval', 1, 'latitude', -25, 'starttime', [1998, 1, 1, 0]);
fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, sharey=True, sharex=True, figsize=(10, 4))
ax0.plot(obs.index, obs['anomaly'], label=u'Observations')
ax0.legend(numpoints=1, loc='lower right')
ax1.plot(obs.index, pout.squeeze(), alpha=0.5, label=u'Prediction')
ax1.legend(numpoints=1, loc='lower right')
ax2.plot(obs.index, obs['anomaly']-pout.squeeze(), alpha=0.5, label=u'Residue')
_ = ax2.legend(numpoints=1, loc='lower right')
No MatlabTM code was written to produce this post. Our Open Source soul can be at ease ;)
More details regarding t_tide can be found here:
R. Pawlowicz, B. Beardsley, and S. Lentz, "Classical tidal harmonic analysis including error estimates in MATLAB using T_TIDE", Computers and Geosciences 28 (2002), 929-937.
HTML(html)