Repository: British-Oceanographic-Data-Centre/COAsT (language: python)

Issue:
version bump 3.1.1 --> 3.1.2
update version numbering for new release


Discussion / hints:

Fix patch:
diff --git a/CITATION.cff b/CITATION.cff
index c93c2dbe..18b99bb7 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -37,6 +37,6 @@ authors:
  - family-names: "Gorman"
    given-names: "Luke"
    orcid: "https://orcid.org/0000-0002-2414-4311"
-title: "British-Oceanographic-Data-Centre/COAsT: v3.1.0"
-version: v3.1.1
-date-released: 2022-09-22
+title: "British-Oceanographic-Data-Centre/COAsT: v3.1.2"
+version: v3.1.2
+date-released: 2022-10-20
diff --git a/coast/data/tidegauge.py b/coast/data/tidegauge.py
index 9c40f295..14846f2c 100644
--- a/coast/data/tidegauge.py
+++ b/coast/data/tidegauge.py
@@ -971,7 +971,7 @@ def _read_bodc_data(fn_bodc, date_start=None, date_end=None, header_length: int
     ###                ~            Plotting             ~                     ###
     ##############################################################################
 
-    def plot_timeseries(self, id, var_list=["ssh"], date_start=None, date_end=None, plot_line=False):
+    def plot_timeseries(self, var_list=["ssh"], date_start=None, date_end=None, plot_line=False):
         """
         Quick plot of time series stored within object's dataset
         Parameters
@@ -992,9 +992,10 @@ def plot_timeseries(self, id, var_list=["ssh"], date_start=None, date_end=None,
             var_list = [var_list]
 
         for var_str in var_list:
-            dim_str = self.dataset[var_str].dims[0]
-            x = np.array(self.dataset[dim_str])
-            y = np.array(self.dataset[var_str])
+            # dim_str = self.dataset[var_str].dims[0]  # commented out: could pull wrong dimensio
+            # x = np.array(self.dataset[dim_str])  # return indices, not coordinate values
+            x = np.array(self.dataset["time"])
+            y = np.array(self.dataset[var_str].squeeze())
 
             # Use only values between stated dates
             start_index = 0
@@ -1019,7 +1020,20 @@ def plot_timeseries(self, id, var_list=["ssh"], date_start=None, date_end=None,
         plt.legend(var_list)
         # Title and axes
         plt.xlabel("Date")
-        plt.title("Site: " + self.dataset.site_name)
+
+        try:
+            self.dataset.id_name
+        except AttributeError:
+            try:
+                self.dataset.site_name
+            except AttributeError:
+                title_str = ""
+            else:
+                title_str = f"Site: {self.dataset.site_name}"
+        else:
+            title_str = f"Site: {self.dataset.id_name}"
+
+        plt.title(title_str)
 
         return fig, ax
 
@@ -1057,19 +1071,22 @@ def plot_on_map_multiple(cls, tidegauge_list, color_var_str=None):
         Y = []
         C = []
         for tg in tidegauge_list:
-            X.append(tg.dataset.longitude)
-            Y.append(tg.dataset.latitude)
+            X.append(tg.dataset.longitude.values)
+            Y.append(tg.dataset.latitude.values)
             if color_var_str is not None:
                 C.append(tg.dataset[color_var_str].values)
 
+        X = np.hstack(X)
+        Y = np.hstack(Y)
         title = ""
         if color_var_str is None:
             fig, ax = plot_util.geo_scatter(X, Y, title=title)
         else:
+            C = np.hstack(C)
             fig, ax = plot_util.geo_scatter(X, Y, title=title, c=C)
 
-        ax.set_xlim((min(X) - 10, max(X) + 10))
-        ax.set_ylim((min(Y) - 10, max(Y) + 10))
+        ax.set_xlim((np.min(X) - 10, np.max(X) + 10))
+        ax.set_ylim((np.min(Y) - 10, np.max(Y) + 10))
         return fig, ax
 
     ##############################################################################
diff --git a/coast/diagnostics/profile_analysis.py b/coast/diagnostics/profile_analysis.py
index f794b403..8ad50522 100644
--- a/coast/diagnostics/profile_analysis.py
+++ b/coast/diagnostics/profile_analysis.py
@@ -385,9 +385,15 @@ def interpolate_vertical(cls, profile, new_depth, interp_method="linear", print_
                 # Get arrays to interpolate
                 interpx = profile.depth.values
                 interpy = profile[vv].values
+                if interpx.shape != interpy.shape:
+                    continue
 
                 # Remove NaNs
                 xnans = np.isnan(interpx)
+                try:
+                    ynans = np.isnan(interpy)
+                except:
+                    debug(f"Issue with vv: {vv}")
                 ynans = np.isnan(interpy)
                 xynans = np.logical_or(xnans, ynans)
                 interpx = interpx[~xynans]
diff --git a/coast/diagnostics/tidegauge_analysis.py b/coast/diagnostics/tidegauge_analysis.py
index 9a253169..2367bafb 100644
--- a/coast/diagnostics/tidegauge_analysis.py
+++ b/coast/diagnostics/tidegauge_analysis.py
@@ -257,8 +257,8 @@ def threshold_statistics(cls, dataset, thresholds=np.arange(-0.4, 2, 0.1), peak_
                     # NTR: Threshold Frequency (Peaks)
                     ds_thresh["peak_count_" + vv][pp, nn] = np.sum(pk_values >= threshn)
 
-                    # NTR: Threshold integral (Time over threshold)
-                    ds_thresh["time_over_threshold_" + vv][pp, nn] = np.sum(data_pp >= threshn)
+                    # NTR: Threshold integral (Timestamps over threshold)
+                    ds_thresh["time_over_threshold_" + vv][pp, nn] = np.sum(data_pp >= threshn)  # sum of booleans
 
                     # NTR: Number of daily maxima over threshold
                     ds_thresh["dailymax_count_" + vv][pp, nn] = np.sum(ds_daily_max.values >= threshn)
diff --git a/environment.yml b/environment.yml
index a908e1c0..5a502731 100644
--- a/environment.yml
+++ b/environment.yml
@@ -6,8 +6,8 @@ channels:
 dependencies:
   - python==3.8.10
   - pip>=22.2.2
-  - cartopy>=0.20.2
-  - gsw>=3.4.0
+  - cartopy>=0.21.0
+  - gsw==3.4.0
   - pip:
     - numpy==1.22.3
     - dask==2022.3.0
@@ -16,7 +16,7 @@ dependencies:
     - matplotlib>=3.5.3
     - netCDF4==1.5.8
     - scipy>=1.8.0
-    - gsw>=3.4.0
+    - gsw==3.4.0
     - utide>=0.3.0
     - scikit-learn>=1.0.2
     - scikit-image>=0.19.2
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/0._profile_introduction.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/0._profile_introduction.ipynb
new file mode 100644
index 00000000..2dd2e4c7
--- /dev/null
+++ b/example_scripts/notebooks/runnable_notebooks/profile/0._profile_introduction.ipynb
@@ -0,0 +1,1106 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "fce85142",
+   "metadata": {},
+   "source": [
+    "\n",
+    "##  Example useage of Profile object.\n",
+    "\n",
+    "### Overview\n",
+    "\n",
+    "INDEXED type class for storing data from a CTD Profile (or similar\n",
+    "down and up observations). The structure of the class is based around having\n",
+    "discrete profile locations with independent depth dimensions and coords. \n",
+    "The class dataset should contain two dimensions:\n",
+    "\n",
+    "    > id_dim      :: The profiles dimension. Each element of this dimension\n",
+    "                 contains data (e.g. cast) for an individual location.\n",
+    "    > z_dim   :: The dimension for depth levels. A profile object does not\n",
+    "                 need to have shared depths, so NaNs might be used to\n",
+    "                 pad any depth array.\n",
+    "\n",
+    "Alongside these dimensions, the following minimal coordinates should also\n",
+    "be available:\n",
+    "\n",
+    "    > longitude (id_dim)   :: 1D array of longitudes, one for each id_dim\n",
+    "    > latitude  (id_dim)   :: 1D array of latitudes, one for each id_dim\n",
+    "    > time      (id_dim)   :: 1D array of times, one for each id_dim\n",
+    "    > depth     (id_dim, z_dim)  :: 2D array of depths, with different depth\n",
+    "                                levels being provided for each profile.\n",
+    "                                Note that these depth levels need to be\n",
+    "                                stored in a 2D array, so NaNs can be used\n",
+    "                                to pad out profiles with shallower depths.\n",
+    "    > id_name   (id_dim)   :: [Optional] Name of id_dim/case or id_dim number.\n",
+    "\n",
+    "## Introduction to Profile and ProfileAnalysis\n",
+    "\n",
+    "Below is a description of the available example scripts for this class as well\n",
+    "as an overview of validation using `Profile` and `ProfileAnalysis`.\n",
+    "\n",
+    "### Example Scripts\n",
+    "\n",
+    "Please see `COAsT/example_scripts/notesbooks/runnable_notebooks/profile_validation/*.ipynb` and `COAsT/example_scripts/profile_validation/*.py` for some notebooks and equivalent scripts which\n",
+    "demonstrate how to use the Profile and ProfileAnalysis classes for model\n",
+    "validation. \n",
+    "\n",
+    "1. `analysis_preprocess_en4.py` : If you're using EN4 data, this kind of script\n",
+    "might be your first step for analysis.\n",
+    "\n",
+    "2. `analysis_extract_and_compare.py`: This script shows you how to extract\n",
+    "the nearest model profiles, compare them with EN4 observations and get errors\n",
+    "throughout the vertical dimension and averaged in surface and bottom zones\n",
+    "\n",
+    "3. `analysis_extract_and_compare_single_process.py`: This script does the same\n",
+    "as number 2. However, it is modified slightly to take a command line argument\n",
+    "which helps it figure out which dates to analyse. This means that this script\n",
+    "can act as a template for `jug` type parallel processing on, e.g. JASMIN.\n",
+    "\n",
+    "4. `analysis_mask_means.py`: This script demonstrates how to use boolean masks\n",
+    "to obtain regional averages of profiles and errors.\n",
+    "\n",
+    "5. `analysis_average_into_grid_boxes.py`: This script demonstrates how to \n",
+    "average the data inside a `Profile` object into regular grid boxes and \n",
+    "seasonal climatologies.\n",
+    "\n",
+    "## Load and preprocess profile and model data\n",
+    "\n",
+    "Start by loading python packages\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "773ad868",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "import coast\n",
+    "from os import path\n",
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a7360869",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "We can create a new Profile object easily:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "30eaeb5c",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "profile = coast.Profile()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bd96e4ef",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Currently, this object is empty, and contains no dataset. There are some\n",
+    "reading routines currently available in Profile for reading EN4 or WOD data\n",
+    "files. These can be used to easily read data into your new profile object:\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e5abc701",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Read WOD data into profile object\n",
+    "fn_prof = path.join(\"example_files\",\"WOD_example_ragged_standard_level.nc\")\n",
+    "profile.read_wod( fn_prof )\n",
+    "\n",
+    "# Read EN4 data into profile object (OVERWRITES DATASET)\n",
+    "fn_prof = path.join(\"example_files\", \"coast_example_en4_201008.nc\")\n",
+    "fn_cfg_prof = path.join(\"config\",\"example_en4_profiles.json\")\n",
+    "profile = coast.Profile(config=fn_cfg_prof)\n",
+    "profile.read_en4( fn_prof )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "93799184",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Alternatively, you can pass an `xarray.dataset` straight to Profile:\n",
+    "```\n",
+    "profile = coast.Profile( dataset = your_dataset, config = config_file [opt] )\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5fe9b182",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "If you are using EN4 data, you can use the `process_en4()` routine to apply\n",
+    "quality control flags to the data (replacing with NaNs):"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8b5c8d53",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "processed_profile = profile.process_en4()\n",
+    "profile = processed_profile"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bf26b82e",
+   "metadata": {},
+   "source": [
+    "We can do some simple spatial and temporal manipulations of this data:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8f2cdc8c",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Cut out a geographical box\n",
+    "profile = profile.subset_indices_lonlat_box(lonbounds = [-15, 15],\n",
+    "                                            latbounds = [45, 65])\n",
+    "\n",
+    "# Cut out a time window\n",
+    "profile = profile.time_slice( date0 = np.datetime64('2010-01-01'), date1 = np.datetime64(\"2010-01-20\"))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "18db7eba",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Inspect profile locations\n",
+    "Have a look inside the `profile.py` class to see what it can do"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9ca87048",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "profile.plot_map()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8958c1c0",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Direct Model comparison using obs_operator() method\n",
+    "\n",
+    "There are a number of routines available for interpolating in the horizontal,\n",
+    "vertical and in time to do direct comparisons of model and profile data.\n",
+    "`Profile.obs_operator` will do a nearest neighbour spatial interpolation of\n",
+    "the data in a `Gridded` object to profile latitudes/longitudes. It will also\n",
+    "do a custom time interpolation.\n",
+    "\n",
+    "#### First load some model data:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "bba61dbf",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "root = \"./\"\n",
+    "# And by defining some file paths\n",
+    "dn_files = root + \"./example_files/\"\n",
+    "fn_nemo_dat = path.join(dn_files, \"coast_example_nemo_data.nc\")\n",
+    "fn_nemo_dom = path.join(dn_files, \"coast_example_nemo_domain.nc\")\n",
+    "fn_nemo_config = path.join(root, \"./config/example_nemo_grid_t.json\")\n",
+    "\n",
+    "# Create gridded object:\n",
+    "nemo = coast.Gridded(fn_nemo_dat, fn_nemo_dom, multiple=True, config=fn_nemo_config)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6438363a",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "#### Create a landmask array in Gridded\n",
+    "In this example we add a `landmask` variable to the `Gridded` dataset.\n",
+    "When this is present, the `obs_operator` will use this to interpolation to the\n",
+    "nearest *wet* point. If not present, it will just take the nearest grid point (not implemented).\n",
+    "\n",
+    "We also rename the depth at initial time coordinate `depth_0` to `depth` as this is expected by Profile()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "13204fcc",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "nemo.dataset[\"landmask\"] = nemo.dataset.bottom_level == 0\n",
+    "nemo.dataset = nemo.dataset.rename({\"depth_0\": \"depth\"})  # profile methods will expect a `depth` coordinate"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d004c0a5",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Interpolate model to horizontal observation locations using obs_operator() method"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fa6650d9",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Use obs operator for horizontal remapping of Gridded onto Profile.\n",
+    "model_profiles = profile.obs_operator(nemo)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "af86b273",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Now that we have interpolated the model onto Profiles, we have a new Profile\n",
+    "object called `model_profiles`. This can be used to do some comparisons with\n",
+    "our original `processed_profile` object, which we created above.\n",
+    "\n",
+    "### Discard profiles where the interpolation distance is too large\n",
+    "\n",
+    "However maybe we first want to restrict the set of model profiles to those that\n",
+    " were close to the observations; perhaps, for example, the observational profiles are beyond the model domain.\n",
+    "The model resolution would be an appropriate scale to pick"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c2fdeaeb",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "too_far = 7  # distance km\n",
+    "keep_indices = model_profiles.dataset.interp_dist <= too_far\n",
+    "model_profiles = model_profiles.isel(id_dim=keep_indices)\n",
+    "\n",
+    "# Also drop the unwanted observational profiles\n",
+    "profile = profile.isel(id_dim=keep_indices)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5790cd22",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "## Profile analysis\n",
+    "\n",
+    "### Create an object for Profile analysis\n",
+    "\n",
+    "Let's make our ProfileAnalysis object:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2272ee1d",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "analysis = coast.ProfileAnalysis()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e3db5398",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "We can use `ProfileAnalysis.interpolate_vertical` to interpolate all variables\n",
+    "within a Profile object. This can be done onto a set of reference depths or,\n",
+    "matching another object's depth coordinates by passing another profile object.\n",
+    "Let's interpolate our model profiles onto observations depths, then interpolate\n",
+    "both onto a set of reference depths:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8a5b8fa3",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "### Set depth averaging settings\n",
+    "ref_depth = np.concatenate((np.arange(1, 100, 2), np.arange(100, 300, 5), np.arange(300, 1000, 50)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e7b718e7",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Interpolate model profiles onto observation depths\n",
+    "model_profiles_interp = analysis.interpolate_vertical(model_profiles, profile, interp_method=\"linear\")\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c8322758",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Vertical interpolation of model profiles to reference depths\n",
+    "model_profiles_interp_ref = analysis.interpolate_vertical(model_profiles_interp, ref_depth)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "03cd3eba",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "```\n",
+    "# Interpolation of obs profiles to reference depths\n",
+    "profile_interp_ref = analysis.interpolate_vertical(profile, ref_depth)\n",
+    "```\n",
+    "However, there is a problem here as the `interpolate_vertical()` method tries to map the whole contents of `profile` to the `ref_depth` and the `profile` object contains some binary data from the original qc flags. The data from the qc flags was mapped using `process_en4()` so the original qc entries can be removed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "80fc1714",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "## Strip out old QC variables\n",
+    "profile.dataset = profile.dataset.drop_vars(['qc_potential_temperature','qc_practical_salinity',\n",
+    "                                 'qc_depth','qc_time',\n",
+    "                                 'qc_flags_profiles','qc_flags_levels'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "87801c48",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Interpolation of obs profiles to reference depths\n",
+    "profile_interp_ref = analysis.interpolate_vertical(profile, ref_depth)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1f62c76a",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Differencing\n",
+    "\n",
+    "Now that we have two Profile objects that are horizontally and vertically\n",
+    "comparable, we can use `difference()` to get some basic errors:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3e43265e",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "differences = analysis.difference(profile_interp_ref, model_profiles_interp_ref)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "728e13c7",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "This will return a new `Profile` object that contains the variable difference,\n",
+    "absolute differences and square differences at all depths and means for each\n",
+    "profile.\n",
+    "\n",
+    "Type\n",
+    "```\n",
+    "differences.dataset\n",
+    "```\n",
+    "to see what it returns"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c63dc470",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# E.g. plot the differences on ind_dim vs z_dim axes\n",
+    "differences.dataset.diff_temperature.plot()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "164e7e50",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# or a bit prettier on labelled axes\n",
+    "cmap=plt.get_cmap('seismic')\n",
+    "fig = plt.figure(figsize=(8, 3))\n",
+    "\n",
+    "plt.pcolormesh( differences.dataset.time, ref_depth, differences.dataset.diff_temperature.T,\n",
+    "                    label='abs_diff', cmap=cmap,\n",
+    "                    vmin=-5, vmax=5)\n",
+    "plt.ylim([0,200])\n",
+    "plt.gca().invert_yaxis()\n",
+    "plt.ylabel('depth')\n",
+    "plt.colorbar( label='temperature diff (obs-model)')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "cd62f0b5",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Layer Averaging\n",
+    "\n",
+    "We can use the `Profile` object to get mean values between specific depth levels\n",
+    "or for some layer above the bathymetric depth. The former can be done using\n",
+    "`ProfileAnalysis.depth_means()`, for example the following will return a new\n",
+    "Profile object containing the means of all variables between 0m and 5m:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "34599281",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "profile_surface = analysis.depth_means(profile, [0, 5])  # 0 - 5 metres"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5822ef7c",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "But since this can work on any Profile object it would be more interesting to apply it to the differences\n",
+    "between the interpolated observations and model points"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d9de28c2",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "surface_def = 10  # in metres\n",
+    "\n",
+    "model_profiles_surface = analysis.depth_means(model_profiles_interp_ref, [0, surface_def])\n",
+    "obs_profiles_surface   = analysis.depth_means(profile_interp_ref, [0, surface_def])\n",
+    "surface_errors = analysis.difference(obs_profiles_surface, model_profiles_surface)\n",
+    "\n",
+    "# Plot (observation - model) upper 10m averaged temperatures\n",
+    "surface_errors.plot_map(var_str=\"diff_temperature\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bbd1e9be",
+   "metadata": {},
+   "source": [
+    "This can be done for any arbitrary depth layer defined by two depths.\n",
+    "\n",
+    "However, in some cases it may be that one of the depth levels is not defined by a constant,\n",
+    " e.g. when calculating bottom means. In this case you may want to calculate averages over a\n",
+    "  height from the bottom that is conditional on the bottom depth. This can be done using\n",
+    "   `ProfileAnalysis.bottom_means()`. For example:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "16252ee7",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "bottom_height = [10, 50, 100]  # Average over bottom heights of 10m, 30m and 100m for...\n",
+    "bottom_thresh = [100, 500, np.inf]  # ...bathymetry depths less than 100m, 100-500m and 500-infinite\n",
+    "model_profiles_bottom = analysis.bottom_means(model_profiles_interp_ref, bottom_height, bottom_thresh)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f93894de",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "similarly compute the same for the observations... though first we have to patch in a `bathymetry` variable\n",
+    "that will be expected by the method. Grab it from the model dataset."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8aaff406",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "profile_interp_ref.dataset[\"bathymetry\"] = ([\"id_dim\"], model_profiles_interp_ref.dataset[\"bathymetry\"].values)\n",
+    "obs_profiles_bottom = analysis.bottom_means(profile_interp_ref, bottom_height, bottom_thresh)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d3ab0e15",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Now the difference can be calculated"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f836489b",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "bottom_errors = analysis.difference( obs_profiles_bottom, model_profiles_bottom)\n",
+    "\n",
+    "# Plot (observation - model) upper 10m averaged temperatures\n",
+    "bottom_errors.plot_map(var_str=\"diff_temperature\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2bc1dba8",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "**NOTE1**: The bathymetry variable does not actually need to contain bathymetric depths, it can also be used to calculate means above any non-constant surface. For example, it could be mixed layer depth.\n",
+    "\n",
+    "**NOTE2**: This can be done for any Profile object. So, you could use this workflow to also average a Profile derived from the `difference()` routine."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9c02b3c9",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "# Since they are indexed by 'id_dim' they can be plotted against time\n",
+    "fig = plt.figure(figsize=(8, 3))\n",
+    "\n",
+    "plt.plot( surface_errors.dataset.time, surface_errors.dataset.diff_temperature, '.', label='surf T' )\n",
+    "plt.plot( bottom_errors.dataset.time, bottom_errors.dataset.diff_temperature, '.', label='bed T' )\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('temperature errors')\n",
+    "plt.legend()\n",
+    "plt.title(\"Temperature diff (obs-model)\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d5555bb9",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Regional (Mask) Averaging\n",
+    "\n",
+    "We can use `Profile` in combination with `MaskMaker` to calculate averages over\n",
+    "regions defined by masks. For example, to get the mean errors in the North Sea.\n",
+    "Start by creating a list of boolean masks we would like to use:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "eba4492b",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "mm = coast.MaskMaker()\n",
+    "\n",
+    "# Define Regional Masks\n",
+    "regional_masks = []\n",
+    "\n",
+    "# Define convenient aliases based on nemo data\n",
+    "lon = nemo.dataset.longitude.values\n",
+    "lat = nemo.dataset.latitude.values\n",
+    "bathy = nemo.dataset.bathymetry.values\n",
+    "\n",
+    "# Add regional mask for whole domain\n",
+    "regional_masks.append(np.ones(lon.shape))\n",
+    "\n",
+    "# Add regional mask for English Channel\n",
+    "regional_masks.append(mm.region_def_nws_english_channel(lon, lat, bathy))\n",
+    "region_names = [\"whole_domain\",\"english_channel\",]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "92c16e1f",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Next, we must make these masks into datasets using `MaskMaker.make_mask_dataset`.\n",
+    "Masks should be 2D datasets defined by booleans. In our example here we have used\n",
+    "the latitude/longitude array from the nemo object, however it can be defined\n",
+    "however you like."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2281b970",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "mask_list = mm.make_mask_dataset(lon, lat, regional_masks)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5f4f223b",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Then we use `ProfileAnalysis.determine_mask_indices` to figure out which\n",
+    "profiles in a `Profile` object lie within each regional mask:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "00597ac4",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "mask_indices = analysis.determine_mask_indices(profile, mask_list)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "20cac4be",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "This returns an object called `mask_indices`, which is required to pass to\n",
+    "`ProfileAnalysis.mask_means()`. This routine will return a new xarray dataset\n",
+    "containing averaged data for each region:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b73f43db",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "mask_means = analysis.mask_means(profile, mask_indices)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "18d042fe",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "which can be visualised or further processed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d45aac07",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "for count_region in range(len(region_names)):\n",
+    "    plt.plot( mask_means.profile_mean_temperature.isel(dim_mask=count_region),\n",
+    "          mask_means.profile_mean_depth.isel(dim_mask=count_region),\n",
+    "          label=region_names[count_region],\n",
+    "          marker=\".\", linestyle='none')\n",
+    "\n",
+    "plt.ylim([10,1000])\n",
+    "plt.yscale(\"log\")\n",
+    "plt.gca().invert_yaxis()\n",
+    "plt.xlabel('temperature'); plt.ylabel('depth')\n",
+    "plt.legend()\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a7051b22",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "### Gridding Profile Data\n",
+    "\n",
+    "If you have large amount of profile data you may want to average it into\n",
+    "grid boxes to get, for example, mean error maps or climatologies. This can be\n",
+    "done using `ProfileAnalysis.average_into_grid_boxes()`.\n",
+    "\n",
+    "We can create a gridded dataset shape `(y_dim, x_dim)` from all the data using:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b84802ac",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "grid_lon = np.arange(-15, 15, 0.5)\n",
+    "grid_lat = np.arange(45, 65, 0.5)\n",
+    "prof_gridded = analysis.average_into_grid_boxes(profile, grid_lon, grid_lat)\n",
+    "# NB this method does not separately treat `z_dim`, see docstr"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a3cae42d",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "lat = prof_gridded.dataset.latitude\n",
+    "lon = prof_gridded.dataset.longitude\n",
+    "temperature = prof_gridded.dataset.temperature\n",
+    "\n",
+    "plt.pcolormesh( lon, lat, temperature)\n",
+    "plt.title('gridded mean temperature')\n",
+    "plt.colorbar()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "61c2a438",
+   "metadata": {
+    "pycharm": {
+     "name": "#%% md\n"
+    }
+   },
+   "source": [
+    "Alternatively, we can calculate averages for each season:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "828632e0",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "prof_gridded_DJF = analysis.average_into_grid_boxes(profile, grid_lon, grid_lat, season=\"DJF\", var_modifier=\"_DJF\")\n",
+    "prof_gridded_MAM = analysis.average_into_grid_boxes(profile, grid_lon, grid_lat, season=\"MAM\", var_modifier=\"_MAM\")\n",
+    "prof_gridded_JJA = analysis.average_into_grid_boxes(profile, grid_lon, grid_lat, season=\"JJA\", var_modifier=\"_JJA\")\n",
+    "prof_gridded_SON = analysis.average_into_grid_boxes(profile, grid_lon, grid_lat, season=\"SON\", var_modifier=\"_SON\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "07acdf68",
+   "metadata": {},
+   "source": [
+    "Here, `season` specifies which season to average over and `var_modifier` is added to the end of\n",
+    "all variable names in the object's dataset.\n",
+    "\n",
+    "NB with the example data only DJF has any data.\n",
+    "\n",
+    "This function returns a new Gridded object. It also contains a new variable\n",
+    "called `grid_N`, which stores how many profiles were averaged into each grid box.\n",
+    "You may want to use this when using or extending the analysis. E.g. use it with plot symbol size"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "43c0f600",
+   "metadata": {
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "temperature = prof_gridded_DJF.dataset.temperature_DJF\n",
+    "N = prof_gridded_DJF.dataset.grid_N_DJF\n",
+    "plt.scatter( lon, lat, c=temperature, s=N)\n",
+    "plt.title('DJF gridded mean temperature')\n",
+    "plt.colorbar()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/1._analysis_preprocess_en4.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/1._analysis_preprocess_en4.ipynb
new file mode 100644
index 00000000..6428a702
--- /dev/null
+++ b/example_scripts/notebooks/runnable_notebooks/profile/1._analysis_preprocess_en4.ipynb
@@ -0,0 +1,275 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "5bd63eda-5c51-4ab1-bdf0-6ec8eaf869ed",
+   "metadata": {},
+   "source": [
+    "Script for processing raw EN4 data prior to analysis.\n",
+    "See docstring of `Profile.process_en4()` for more specifics on what it does.\n",
+    "\n",
+    "This script will just load modules, read in raw EN4 data, cut out a geographical box, call the processing routine and write the processed data to file.\n",
+    "\n",
+    "You don't have to do this for each EN4 file individually if you don't want, you can read in multiple using `multiple = True` on the creation of the profile object. However, if analysing model data in parallel chunks, you may want to split up the processing into smaller files to make the analysis faster."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "39fa949e-1ba4-4f29-a5fa-906786f0b7b3",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Modules loaded\n"
+     ]
+    }
+   ],
+   "source": [
+    "import sys\n",
+    "\n",
+    "# IF USING A DEVELOPMENT BRANCH OF COAST, ADD THE REPOSITORY TO PATH:\n",
+    "# sys.path.append('<PATH_TO_COAST_REPO')\n",
+    "import coast\n",
+    "import pandas as pd\n",
+    "from datetime import datetime\n",
+    "from os import path\n",
+    "\n",
+    "print(\"Modules loaded\")\n",
+    "\n",
+    "# File paths - input en4, output processed file and read config file\n",
+    "\"\"\"\n",
+    "fn_prof = \"<PATH_TO_RAW_EN4_DATA_FILE(S)>\"\n",
+    "fn_out = \"<PATH_TO_OUTPUT_LOCATION_FOR_PROCESSED_PROFILES>\"\n",
+    "fn_cfg_prof = \"<PATH_TO_COAST_PROFILE_CONFIG_FILE>\"\n",
+    "\"\"\"\n",
+    "\n",
+    "fn_out = \"./output.nc\"\n",
+    "fn_prof = path.join('./example_files', \"coast_example_en4_201008.nc\")\n",
+    "fn_prof = path.join('/Users/jeff/Downloads/EN.4.2.2.profiles.g10.2022', \"EN.4.2.2.f.profiles.g10.2022*.nc\")\n",
+    "fn_cfg_prof = path.join('/Users/jeff/gitHub/COAsT/config', \"example_en4_profiles.json\")\n",
+    "\n",
+    "\n",
+    "# Some important settings, easier to get at here\n",
+    "longitude_bounds = [-15, 15]  # Geo box to cut out from data (match to model)\n",
+    "latitude_bounds = [40, 65]\n",
+    "multiple = True  # Reading multple files?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "outputs": [],
+   "source": [
+    "fn_prof = \"/Users/jeff/GitHub/COAsT/example_files/coast_example_en4_201008.nc\""
+   ],
+   "metadata": {
+    "collapsed": false,
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   }
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "outputs": [],
+   "source": [
+    "# Some important settings, easier to get at here\n",
+    "longitude_bounds = [0, 360]  # Geo box to cut out from data (match to model)\n",
+    "latitude_bounds = [-15, 15]\n",
+    "multiple = True  # Reading multple files?"
+   ],
+   "metadata": {
+    "collapsed": false,
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   }
+  },
+  {
+   "cell_type": "markdown",
+   "id": "633dd796-fdbd-4522-82f1-5f98fe835e31",
+   "metadata": {},
+   "source": [
+    "Create profile object containing data"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "id": "3d9f6d11-60aa-4799-8d82-91862fb6c353",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "/Users/jeff/gitHub/COAsT/config/example_en4_profiles.json\n"
+     ]
+    }
+   ],
+   "source": [
+    "profile = coast.Profile(config=fn_cfg_prof)\n",
+    "profile.read_en4(fn_prof, multiple=multiple)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "outputs": [
+    {
+     "data": {
+      "text/plain": "<xarray.Dataset>\nDimensions:                   (id_dim: 1257, z_dim: 400)\nCoordinates:\n    latitude                  (id_dim) float64 dask.array<chunksize=(1257,), meta=np.ndarray>\n    longitude                 (id_dim) float64 dask.array<chunksize=(1257,), meta=np.ndarray>\n    time                      (id_dim) datetime64[ns] dask.array<chunksize=(1257,), meta=np.ndarray>\nDimensions without coordinates: id_dim, z_dim\nData variables:\n    depth                     (id_dim, z_dim) float32 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    potential_temperature     (id_dim, z_dim) float32 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    temperature               (id_dim, z_dim) float32 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    practical_salinity        (id_dim, z_dim) float32 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    qc_potential_temperature  (id_dim, z_dim) |S1 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    qc_practical_salinity     (id_dim, z_dim) |S1 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    qc_depth                  (id_dim, z_dim) |S1 dask.array<chunksize=(1257, 400), meta=np.ndarray>\n    qc_time                   (id_dim) |S1 dask.array<chunksize=(1257,), meta=np.ndarray>\n    qc_flags_profiles         (id_dim) int32 dask.array<chunksize=(1257,), meta=np.ndarray>\n    qc_flags_levels           (id_dim, z_dim) int32 dask.array<chunksize=(1257, 400), meta=np.ndarray>\nAttributes:\n    history:  Fri Apr 21 21:10:34 2017: /project/ukmo/rhel6/nco/bin/ncks -x -...\n    NCO:      4.3.2",
+      "text/html": "<div><svg style=\"position: absolute; width: 0; height: 0; overflow: hidden\">\n<defs>\n<symbol id=\"icon-database\" viewBox=\"0 0 32 32\">\n<path d=\"M16 0c-8.837 0-16 2.239-16 5v4c0 2.761 7.163 5 16 5s16-2.239 16-5v-4c0-2.761-7.163-5-16-5z\"></path>\n<path d=\"M16 17c-8.837 0-16-2.239-16-5v6c0 2.761 7.163 5 16 5s16-2.239 16-5v-6c0 2.761-7.163 5-16 5z\"></path>\n<path d=\"M16 26c-8.837 0-16-2.239-16-5v6c0 2.761 7.163 5 16 5s16-2.239 16-5v-6c0 2.761-7.163 5-16 5z\"></path>\n</symbol>\n<symbol id=\"icon-file-text2\" viewBox=\"0 0 32 32\">\n<path d=\"M28.681 7.159c-0.694-0.947-1.662-2.053-2.724-3.116s-2.169-2.030-3.116-2.724c-1.612-1.182-2.393-1.319-2.841-1.319h-15.5c-1.378 0-2.5 1.121-2.5 2.5v27c0 1.378 1.122 2.5 2.5 2.5h23c1.378 0 2.5-1.122 2.5-2.5v-19.5c0-0.448-0.137-1.23-1.319-2.841zM24.543 5.457c0.959 0.959 1.712 1.825 2.268 2.543h-4.811v-4.811c0.718 0.556 1.584 1.309 2.543 2.268zM28 29.5c0 0.271-0.229 0.5-0.5 0.5h-23c-0.271 0-0.5-0.229-0.5-0.5v-27c0-0.271 0.229-0.5 0.5-0.5 0 0 15.499-0 15.5 0v7c0 0.552 0.448 1 1 1h7v19.5z\"></path>\n<path d=\"M23 26h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z\"></path>\n<path d=\"M23 22h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z\"></path>\n<path d=\"M23 18h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z\"></path>\n</symbol>\n</defs>\n</svg>\n<style>/* CSS stylesheet for displaying xarray objects in jupyterlab.\n *\n */\n\n:root {\n  --xr-font-color0: var(--jp-content-font-color0, rgba(0, 0, 0, 1));\n  --xr-font-color2: var(--jp-content-font-color2, rgba(0, 0, 0, 0.54));\n  --xr-font-color3: var(--jp-content-font-color3, rgba(0, 0, 0, 0.38));\n  --xr-border-color: var(--jp-border-color2, #e0e0e0);\n  --xr-disabled-color: var(--jp-layout-color3, #bdbdbd);\n  --xr-background-color: var(--jp-layout-color0, white);\n  --xr-background-color-row-even: var(--jp-layout-color1, white);\n  --xr-background-color-row-odd: var(--jp-layout-color2, #eeeeee);\n}\n\nhtml[theme=dark],\nbody.vscode-dark {\n  --xr-font-color0: rgba(255, 255, 255, 1);\n  --xr-font-color2: rgba(255, 255, 255, 0.54);\n  --xr-font-color3: rgba(255, 255, 255, 0.38);\n  --xr-border-color: #1F1F1F;\n  --xr-disabled-color: #515151;\n  --xr-background-color: #111111;\n  --xr-background-color-row-even: #111111;\n  --xr-background-color-row-odd: #313131;\n}\n\n.xr-wrap {\n  display: block !important;\n  min-width: 300px;\n  max-width: 700px;\n}\n\n.xr-text-repr-fallback {\n  /* fallback to plain text repr when CSS is not injected (untrusted notebook) */\n  display: none;\n}\n\n.xr-header {\n  padding-top: 6px;\n  padding-bottom: 6px;\n  margin-bottom: 4px;\n  border-bottom: solid 1px var(--xr-border-color);\n}\n\n.xr-header > div,\n.xr-header > ul {\n  display: inline;\n  margin-top: 0;\n  margin-bottom: 0;\n}\n\n.xr-obj-type,\n.xr-array-name {\n  margin-left: 2px;\n  margin-right: 10px;\n}\n\n.xr-obj-type {\n  color: var(--xr-font-color2);\n}\n\n.xr-sections {\n  padding-left: 0 !important;\n  display: grid;\n  grid-template-columns: 150px auto auto 1fr 20px 20px;\n}\n\n.xr-section-item {\n  display: contents;\n}\n\n.xr-section-item input {\n  display: none;\n}\n\n.xr-section-item input + label {\n  color: var(--xr-disabled-color);\n}\n\n.xr-section-item input:enabled + label {\n  cursor: pointer;\n  color: var(--xr-font-color2);\n}\n\n.xr-section-item input:enabled + label:hover {\n  color: var(--xr-font-color0);\n}\n\n.xr-section-summary {\n  grid-column: 1;\n  color: var(--xr-font-color2);\n  font-weight: 500;\n}\n\n.xr-section-summary > span {\n  display: inline-block;\n  padding-left: 0.5em;\n}\n\n.xr-section-summary-in:disabled + label {\n  color: var(--xr-font-color2);\n}\n\n.xr-section-summary-in + label:before {\n  display: inline-block;\n  content: '►';\n  font-size: 11px;\n  width: 15px;\n  text-align: center;\n}\n\n.xr-section-summary-in:disabled + label:before {\n  color: var(--xr-disabled-color);\n}\n\n.xr-section-summary-in:checked + label:before {\n  content: '▼';\n}\n\n.xr-section-summary-in:checked + label > span {\n  display: none;\n}\n\n.xr-section-summary,\n.xr-section-inline-details {\n  padding-top: 4px;\n  padding-bottom: 4px;\n}\n\n.xr-section-inline-details {\n  grid-column: 2 / -1;\n}\n\n.xr-section-details {\n  display: none;\n  grid-column: 1 / -1;\n  margin-bottom: 5px;\n}\n\n.xr-section-summary-in:checked ~ .xr-section-details {\n  display: contents;\n}\n\n.xr-array-wrap {\n  grid-column: 1 / -1;\n  display: grid;\n  grid-template-columns: 20px auto;\n}\n\n.xr-array-wrap > label {\n  grid-column: 1;\n  vertical-align: top;\n}\n\n.xr-preview {\n  color: var(--xr-font-color3);\n}\n\n.xr-array-preview,\n.xr-array-data {\n  padding: 0 5px !important;\n  grid-column: 2;\n}\n\n.xr-array-data,\n.xr-array-in:checked ~ .xr-array-preview {\n  display: none;\n}\n\n.xr-array-in:checked ~ .xr-array-data,\n.xr-array-preview {\n  display: inline-block;\n}\n\n.xr-dim-list {\n  display: inline-block !important;\n  list-style: none;\n  padding: 0 !important;\n  margin: 0;\n}\n\n.xr-dim-list li {\n  display: inline-block;\n  padding: 0;\n  margin: 0;\n}\n\n.xr-dim-list:before {\n  content: '(';\n}\n\n.xr-dim-list:after {\n  content: ')';\n}\n\n.xr-dim-list li:not(:last-child):after {\n  content: ',';\n  padding-right: 5px;\n}\n\n.xr-has-index {\n  font-weight: bold;\n}\n\n.xr-var-list,\n.xr-var-item {\n  display: contents;\n}\n\n.xr-var-item > div,\n.xr-var-item label,\n.xr-var-item > .xr-var-name span {\n  background-color: var(--xr-background-color-row-even);\n  margin-bottom: 0;\n}\n\n.xr-var-item > .xr-var-name:hover span {\n  padding-right: 5px;\n}\n\n.xr-var-list > li:nth-child(odd) > div,\n.xr-var-list > li:nth-child(odd) > label,\n.xr-var-list > li:nth-child(odd) > .xr-var-name span {\n  background-color: var(--xr-background-color-row-odd);\n}\n\n.xr-var-name {\n  grid-column: 1;\n}\n\n.xr-var-dims {\n  grid-column: 2;\n}\n\n.xr-var-dtype {\n  grid-column: 3;\n  text-align: right;\n  color: var(--xr-font-color2);\n}\n\n.xr-var-preview {\n  grid-column: 4;\n}\n\n.xr-var-name,\n.xr-var-dims,\n.xr-var-dtype,\n.xr-preview,\n.xr-attrs dt {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  padding-right: 10px;\n}\n\n.xr-var-name:hover,\n.xr-var-dims:hover,\n.xr-var-dtype:hover,\n.xr-attrs dt:hover {\n  overflow: visible;\n  width: auto;\n  z-index: 1;\n}\n\n.xr-var-attrs,\n.xr-var-data {\n  display: none;\n  background-color: var(--xr-background-color) !important;\n  padding-bottom: 5px !important;\n}\n\n.xr-var-attrs-in:checked ~ .xr-var-attrs,\n.xr-var-data-in:checked ~ .xr-var-data {\n  display: block;\n}\n\n.xr-var-data > table {\n  float: right;\n}\n\n.xr-var-name span,\n.xr-var-data,\n.xr-attrs {\n  padding-left: 25px !important;\n}\n\n.xr-attrs,\n.xr-var-attrs,\n.xr-var-data {\n  grid-column: 1 / -1;\n}\n\ndl.xr-attrs {\n  padding: 0;\n  margin: 0;\n  display: grid;\n  grid-template-columns: 125px auto;\n}\n\n.xr-attrs dt,\n.xr-attrs dd {\n  padding: 0;\n  margin: 0;\n  float: left;\n  padding-right: 10px;\n  width: auto;\n}\n\n.xr-attrs dt {\n  font-weight: normal;\n  grid-column: 1;\n}\n\n.xr-attrs dt:hover span {\n  display: inline-block;\n  background: var(--xr-background-color);\n  padding-right: 10px;\n}\n\n.xr-attrs dd {\n  grid-column: 2;\n  white-space: pre-wrap;\n  word-break: break-all;\n}\n\n.xr-icon-database,\n.xr-icon-file-text2 {\n  display: inline-block;\n  vertical-align: middle;\n  width: 1em;\n  height: 1.5em !important;\n  stroke-width: 0;\n  stroke: currentColor;\n  fill: currentColor;\n}\n</style><pre class='xr-text-repr-fallback'>&lt;xarray.Dataset&gt;\nDimensions:                   (id_dim: 1257, z_dim: 400)\nCoordinates:\n    latitude                  (id_dim) float64 dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;\n    longitude                 (id_dim) float64 dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;\n    time                      (id_dim) datetime64[ns] dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;\nDimensions without coordinates: id_dim, z_dim\nData variables:\n    depth                     (id_dim, z_dim) float32 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    potential_temperature     (id_dim, z_dim) float32 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    temperature               (id_dim, z_dim) float32 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    practical_salinity        (id_dim, z_dim) float32 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    qc_potential_temperature  (id_dim, z_dim) |S1 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    qc_practical_salinity     (id_dim, z_dim) |S1 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    qc_depth                  (id_dim, z_dim) |S1 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\n    qc_time                   (id_dim) |S1 dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;\n    qc_flags_profiles         (id_dim) int32 dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;\n    qc_flags_levels           (id_dim, z_dim) int32 dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;\nAttributes:\n    history:  Fri Apr 21 21:10:34 2017: /project/ukmo/rhel6/nco/bin/ncks -x -...\n    NCO:      4.3.2</pre><div class='xr-wrap' style='display:none'><div class='xr-header'><div class='xr-obj-type'>xarray.Dataset</div></div><ul class='xr-sections'><li class='xr-section-item'><input id='section-f6d18958-d10e-449d-9c52-27eb62256200' class='xr-section-summary-in' type='checkbox' disabled ><label for='section-f6d18958-d10e-449d-9c52-27eb62256200' class='xr-section-summary'  title='Expand/collapse section'>Dimensions:</label><div class='xr-section-inline-details'><ul class='xr-dim-list'><li><span>id_dim</span>: 1257</li><li><span>z_dim</span>: 400</li></ul></div><div class='xr-section-details'></div></li><li class='xr-section-item'><input id='section-f23bb151-d0e9-496d-9270-e6eb91ea3cde' class='xr-section-summary-in' type='checkbox'  checked><label for='section-f23bb151-d0e9-496d-9270-e6eb91ea3cde' class='xr-section-summary' >Coordinates: <span>(3)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>latitude</span></div><div class='xr-var-dims'>(id_dim)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;</div><input id='attrs-f90feaa0-f26a-4746-b4d5-1d9b31f189b1' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-f90feaa0-f26a-4746-b4d5-1d9b31f189b1' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-0ae7445c-8c08-4af2-8744-2d1fc610da52' class='xr-var-data-in' type='checkbox'><label for='data-0ae7445c-8c08-4af2-8744-2d1fc610da52' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>latitude of the station, best estimated value</dd><dt><span>units :</span></dt><dd>degree_north</dd><dt><span>_fillvalue :</span></dt><dd>99999.0</dd><dt><span>valid_min :</span></dt><dd>-90.0</dd><dt><span>valid_max :</span></dt><dd>90.0</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 9.82 kiB </td>\n                        <td> 9.82 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257,) </td>\n                        <td> (1257,) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> float64 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"170\" height=\"75\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"120\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"25\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"25\" style=\"stroke-width:2\" />\n  <line x1=\"120\" y1=\"0\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 120.0,0.0 120.0,25.412616514582485 0.0,25.412616514582485\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"60.000000\" y=\"45.412617\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1257</text>\n  <text x=\"140.000000\" y=\"12.706308\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(0,140.000000,12.706308)\">1</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>longitude</span></div><div class='xr-var-dims'>(id_dim)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;</div><input id='attrs-b16f97dc-e551-40fa-887d-f2dcaf10209e' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-b16f97dc-e551-40fa-887d-f2dcaf10209e' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-db05a668-c22e-4961-ab96-fd7a560f3036' class='xr-var-data-in' type='checkbox'><label for='data-db05a668-c22e-4961-ab96-fd7a560f3036' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>longitude of the station, best estimated value</dd><dt><span>units :</span></dt><dd>degree_east</dd><dt><span>_fillvalue :</span></dt><dd>99999.0</dd><dt><span>valid_min :</span></dt><dd>-180.0</dd><dt><span>valid_max :</span></dt><dd>180.0</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 9.82 kiB </td>\n                        <td> 9.82 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257,) </td>\n                        <td> (1257,) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> float64 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"170\" height=\"75\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"120\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"25\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"25\" style=\"stroke-width:2\" />\n  <line x1=\"120\" y1=\"0\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 120.0,0.0 120.0,25.412616514582485 0.0,25.412616514582485\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"60.000000\" y=\"45.412617\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1257</text>\n  <text x=\"140.000000\" y=\"12.706308\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(0,140.000000,12.706308)\">1</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>time</span></div><div class='xr-var-dims'>(id_dim)</div><div class='xr-var-dtype'>datetime64[ns]</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;</div><input id='attrs-0803f28a-6cfc-4dea-8ff3-a8219bce95a2' class='xr-var-attrs-in' type='checkbox' disabled><label for='attrs-0803f28a-6cfc-4dea-8ff3-a8219bce95a2' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-5510821f-5371-4e1e-88df-297117d9a872' class='xr-var-data-in' type='checkbox'><label for='data-5510821f-5371-4e1e-88df-297117d9a872' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 9.82 kiB </td>\n                        <td> 9.82 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257,) </td>\n                        <td> (1257,) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> datetime64[ns] </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"170\" height=\"75\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"120\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"25\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"25\" style=\"stroke-width:2\" />\n  <line x1=\"120\" y1=\"0\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 120.0,0.0 120.0,25.412616514582485 0.0,25.412616514582485\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"60.000000\" y=\"45.412617\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1257</text>\n  <text x=\"140.000000\" y=\"12.706308\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(0,140.000000,12.706308)\">1</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-10efc0d9-d2af-4252-ab3e-b77332845884' class='xr-section-summary-in' type='checkbox'  checked><label for='section-10efc0d9-d2af-4252-ab3e-b77332845884' class='xr-section-summary' >Data variables: <span>(10)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>depth</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-c7e08921-80d9-4e6b-af3c-cd2ba5449a04' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-c7e08921-80d9-4e6b-af3c-cd2ba5449a04' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-3c1bb5c0-8a2d-42c3-99d0-6d0aaa0cd032' class='xr-var-data-in' type='checkbox'><label for='data-3c1bb5c0-8a2d-42c3-99d0-6d0aaa0cd032' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>corrected depth</dd><dt><span>_fillvalue :</span></dt><dd>99999.0</dd><dt><span>units :</span></dt><dd>metre</dd><dt><span>valid_min :</span></dt><dd>0.0</dd><dt><span>valid_max :</span></dt><dd>15000.0</dd><dt><span>c_format :</span></dt><dd>%7.1f</dd><dt><span>fortran_format :</span></dt><dd>f7.1</dd><dt><span>resolution :</span></dt><dd>0.1</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 1.92 MiB </td>\n                        <td> 1.92 MiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> float32 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>potential_temperature</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-0ed32740-e890-4a3d-abf2-cb8595ee5353' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-0ed32740-e890-4a3d-abf2-cb8595ee5353' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-c9e29c66-dd6b-4064-ba20-179ff0b1f8f7' class='xr-var-data-in' type='checkbox'><label for='data-c9e29c66-dd6b-4064-ba20-179ff0b1f8f7' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>corrected pot. temp</dd><dt><span>_fillvalue :</span></dt><dd>99999.0</dd><dt><span>units :</span></dt><dd>degree_celsius</dd><dt><span>valid_min :</span></dt><dd>-3.0</dd><dt><span>valid_max :</span></dt><dd>40.0</dd><dt><span>comment :</span></dt><dd>corrected value</dd><dt><span>c_format :</span></dt><dd>%9.3f</dd><dt><span>fortran_format :</span></dt><dd>f9.3</dd><dt><span>resolution :</span></dt><dd>0.001</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 1.92 MiB </td>\n                        <td> 1.92 MiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> float32 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>temperature</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-f8392acd-c369-4d31-82b0-44ae40c9cef5' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-f8392acd-c369-4d31-82b0-44ae40c9cef5' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-16ac59fd-ca86-47d7-88f0-947d90c4865b' class='xr-var-data-in' type='checkbox'><label for='data-16ac59fd-ca86-47d7-88f0-947d90c4865b' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>Temperature in situ t90 scale</dd><dt><span>_fillvalue :</span></dt><dd>99999.0</dd><dt><span>units :</span></dt><dd>degree_celsius</dd><dt><span>valid_min :</span></dt><dd>-3.0</dd><dt><span>valid_max :</span></dt><dd>40.0</dd><dt><span>comment :</span></dt><dd>in situ measurement</dd><dt><span>c_format :</span></dt><dd>%9.3f</dd><dt><span>fortran_format :</span></dt><dd>f9.3</dd><dt><span>resolution :</span></dt><dd>0.001</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 1.92 MiB </td>\n                        <td> 1.92 MiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> float32 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>practical_salinity</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-37a82b8d-e089-42a8-818c-1de73f8f950e' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-37a82b8d-e089-42a8-818c-1de73f8f950e' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-0758e028-456d-43d0-9650-d675aa0259ff' class='xr-var-data-in' type='checkbox'><label for='data-0758e028-456d-43d0-9650-d675aa0259ff' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>corrected practical salinity</dd><dt><span>_fillvalue :</span></dt><dd>99999.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>valid_min :</span></dt><dd>0.0</dd><dt><span>valid_max :</span></dt><dd>60.0</dd><dt><span>comment :</span></dt><dd>corrected value</dd><dt><span>c_format :</span></dt><dd>%9.3f</dd><dt><span>fortran_format :</span></dt><dd>f9.3</dd><dt><span>resolution :</span></dt><dd>0.001</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 1.92 MiB </td>\n                        <td> 1.92 MiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> float32 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>qc_potential_temperature</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>|S1</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-5dda3c08-fa8b-4335-a03a-f1d166123797' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-5dda3c08-fa8b-4335-a03a-f1d166123797' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-5a951bdd-3f87-47b9-8a16-1f112c59ba5e' class='xr-var-data-in' type='checkbox'><label for='data-5a951bdd-3f87-47b9-8a16-1f112c59ba5e' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>quality on pot. temperature</dd><dt><span>conventions :</span></dt><dd>q where q =[0,9]</dd><dt><span>_fillvalue :</span></dt><dd>0</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 491.02 kiB </td>\n                        <td> 491.02 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> |S1 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>qc_practical_salinity</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>|S1</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-5966c235-3b8d-4645-89f6-9e7120ad5e3c' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-5966c235-3b8d-4645-89f6-9e7120ad5e3c' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-fb9be041-a529-423f-8319-152c1d404d03' class='xr-var-data-in' type='checkbox'><label for='data-fb9be041-a529-423f-8319-152c1d404d03' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>quality on practical salinity</dd><dt><span>conventions :</span></dt><dd>q where q =[0,9]</dd><dt><span>_fillvalue :</span></dt><dd>0</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 491.02 kiB </td>\n                        <td> 491.02 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> |S1 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>qc_depth</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>|S1</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-360a0876-6889-4086-bfc9-9cc7f2a9624b' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-360a0876-6889-4086-bfc9-9cc7f2a9624b' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-35c0aa57-c88e-41f7-815b-1b4f17aa83f9' class='xr-var-data-in' type='checkbox'><label for='data-35c0aa57-c88e-41f7-815b-1b4f17aa83f9' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>quality on depth</dd><dt><span>conventions :</span></dt><dd>q where q =[0,9]</dd><dt><span>_fillvalue :</span></dt><dd>0</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 491.02 kiB </td>\n                        <td> 491.02 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> |S1 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>qc_time</span></div><div class='xr-var-dims'>(id_dim)</div><div class='xr-var-dtype'>|S1</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;</div><input id='attrs-66e22990-3cb4-4ec6-bdd1-93660bf2a397' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-66e22990-3cb4-4ec6-bdd1-93660bf2a397' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-15c1ddb3-7b17-42e3-b0bf-93221567fb4a' class='xr-var-data-in' type='checkbox'><label for='data-15c1ddb3-7b17-42e3-b0bf-93221567fb4a' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>quality on date and time</dd><dt><span>conventions :</span></dt><dd>q where q=[0-9]</dd><dt><span>_fillvalue :</span></dt><dd>0</dd></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 1.23 kiB </td>\n                        <td> 1.23 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257,) </td>\n                        <td> (1257,) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> |S1 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"170\" height=\"75\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"120\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"25\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"25\" style=\"stroke-width:2\" />\n  <line x1=\"120\" y1=\"0\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 120.0,0.0 120.0,25.412616514582485 0.0,25.412616514582485\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"60.000000\" y=\"45.412617\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1257</text>\n  <text x=\"140.000000\" y=\"12.706308\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(0,140.000000,12.706308)\">1</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>qc_flags_profiles</span></div><div class='xr-var-dims'>(id_dim)</div><div class='xr-var-dtype'>int32</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257,), meta=np.ndarray&gt;</div><input id='attrs-a845e8f9-ec91-4508-a86c-41ed046b71cf' class='xr-var-attrs-in' type='checkbox' disabled><label for='attrs-a845e8f9-ec91-4508-a86c-41ed046b71cf' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-074cae49-a112-4260-8573-3ec5b0e832b8' class='xr-var-data-in' type='checkbox'><label for='data-074cae49-a112-4260-8573-3ec5b0e832b8' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 4.91 kiB </td>\n                        <td> 4.91 kiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257,) </td>\n                        <td> (1257,) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> int32 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"170\" height=\"75\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"120\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"25\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"25\" style=\"stroke-width:2\" />\n  <line x1=\"120\" y1=\"0\" x2=\"120\" y2=\"25\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 120.0,0.0 120.0,25.412616514582485 0.0,25.412616514582485\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"60.000000\" y=\"45.412617\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1257</text>\n  <text x=\"140.000000\" y=\"12.706308\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(0,140.000000,12.706308)\">1</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>qc_flags_levels</span></div><div class='xr-var-dims'>(id_dim, z_dim)</div><div class='xr-var-dtype'>int32</div><div class='xr-var-preview xr-preview'>dask.array&lt;chunksize=(1257, 400), meta=np.ndarray&gt;</div><input id='attrs-1a630925-cab4-4ead-a3a5-de626e8a5ee3' class='xr-var-attrs-in' type='checkbox' disabled><label for='attrs-1a630925-cab4-4ead-a3a5-de626e8a5ee3' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-1223c032-e565-47ac-80cf-0518f31f06f9' class='xr-var-data-in' type='checkbox'><label for='data-1223c032-e565-47ac-80cf-0518f31f06f9' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'></dl></div><div class='xr-var-data'><table>\n    <tr>\n        <td>\n            <table>\n                <thead>\n                    <tr>\n                        <td> </td>\n                        <th> Array </th>\n                        <th> Chunk </th>\n                    </tr>\n                </thead>\n                <tbody>\n                    \n                    <tr>\n                        <th> Bytes </th>\n                        <td> 1.92 MiB </td>\n                        <td> 1.92 MiB </td>\n                    </tr>\n                    \n                    <tr>\n                        <th> Shape </th>\n                        <td> (1257, 400) </td>\n                        <td> (1257, 400) </td>\n                    </tr>\n                    <tr>\n                        <th> Count </th>\n                        <td> 2 Tasks </td>\n                        <td> 1 Chunks </td>\n                    </tr>\n                    <tr>\n                    <th> Type </th>\n                    <td> int32 </td>\n                    <td> numpy.ndarray </td>\n                    </tr>\n                </tbody>\n            </table>\n        </td>\n        <td>\n        <svg width=\"93\" height=\"170\" style=\"stroke:rgb(0,0,0);stroke-width:1\" >\n\n  <!-- Horizontal lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"43\" y2=\"0\" style=\"stroke-width:2\" />\n  <line x1=\"0\" y1=\"120\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Vertical lines -->\n  <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"120\" style=\"stroke-width:2\" />\n  <line x1=\"43\" y1=\"0\" x2=\"43\" y2=\"120\" style=\"stroke-width:2\" />\n\n  <!-- Colored Rectangle -->\n  <polygon points=\"0.0,0.0 43.73850167156636,0.0 43.73850167156636,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n\n  <!-- Text -->\n  <text x=\"21.869251\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >400</text>\n  <text x=\"63.738502\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,63.738502,60.000000)\">1257</text>\n</svg>\n        </td>\n    </tr>\n</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-d63355cf-ca43-4b31-8efc-6a406866d037' class='xr-section-summary-in' type='checkbox'  checked><label for='section-d63355cf-ca43-4b31-8efc-6a406866d037' class='xr-section-summary' >Attributes: <span>(2)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><dl class='xr-attrs'><dt><span>history :</span></dt><dd>Fri Apr 21 21:10:34 2017: /project/ukmo/rhel6/nco/bin/ncks -x -v TEMP_UNIQUE_ID,PSAL_UNIQUE_ID /project/marine_database/EN.4.2.1/WDIR/g10/EN.4.2.1.f.profiles.g10.201001.nc /project/marine_database/EN.4.2.1/WDIR/g10/EN.4.2.1.f.profiles.g10.201001.ncdel.nc\nFri Apr 21 21:10:31 2017: /project/ukmo/rhel6/nco/bin/ncks -x -v BKPT,BKPS /project/marine_database/EN.4.2.1/WDIR/g10/EN.4.2.1.f.profiles.g10.201001.nc /project/marine_database/EN.4.2.1/WDIR/g10/EN.4.2.1.f.profiles.g10.201001.ncdel.nc\nFri Apr 21 21:06:37 2017: /project/ukmo/rhel6/nco/bin/ncatted -a _FillValue,BKPS,c,f,99999.0 /project/marine_database/EN.4.2.1/WDIR/profiles.nc\nFri Apr 21 21:06:36 2017: /project/ukmo/rhel6/nco/bin/ncatted -a _FillValue,BKPT,c,f,99999.0 /project/marine_database/EN.4.2.1/WDIR/profiles.nc\nFri Apr 21 21:06:34 2017: /project/ukmo/rhel6/nco/bin/ncatted -a _FillValue,POTM_CORRECTED,c,f,99999.0 /project/marine_database/EN.4.2.1/WDIR/profiles.nc\nFri Apr 21 21:06:33 2017: /project/ukmo/rhel6/nco/bin/ncatted -a _FillValue,TEMP,c,f,99999.0 /project/marine_database/EN.4.2.1/WDIR/profiles.nc\nFri Apr 21 21:06:31 2017: /project/ukmo/rhel6/nco/bin/ncatted -a _FillValue,PSAL_CORRECTED,c,f,99999.0 /project/marine_database/EN.4.2.1/WDIR/profiles.nc\nFri Apr 21 21:06:21 2017: /project/ukmo/rhel6/nco/bin/ncatted -a _FillValue,DEPH_CORRECTED,c,f,99999.0 /project/marine_database/EN.4.2.1/WDIR/profiles.nc</dd><dt><span>NCO :</span></dt><dd>4.3.2</dd></dl></div></li></ul></div></div>"
+     },
+     "execution_count": 19,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "profile.dataset"
+   ],
+   "metadata": {
+    "collapsed": false,
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   }
+  },
+  {
+   "cell_type": "markdown",
+   "id": "33077579-0f3f-4e89-a1f2-51a709835f28",
+   "metadata": {},
+   "source": [
+    "Get geographical indices to extract."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "id": "98344d6a-5d69-405a-9d0a-752bb160037f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "profile = profile.subset_indices_lonlat_box(longitude_bounds, latitude_bounds)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "outputs": [
+    {
+     "ename": "AttributeError",
+     "evalue": "'Profile' object has no attribute 'quick_plot'",
+     "output_type": "error",
+     "traceback": [
+      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
+      "\u001B[0;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
+      "\u001B[0;32m/var/folders/m8/d31wn5y90935821nhqkkwtq80000gr/T/ipykernel_30745/1488730455.py\u001B[0m in \u001B[0;36m<cell line: 1>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0mprofile\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mquick_plot\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m",
+      "\u001B[0;31mAttributeError\u001B[0m: 'Profile' object has no attribute 'quick_plot'"
+     ]
+    }
+   ],
+   "source": [
+    "profile.quick_plot()"
+   ],
+   "metadata": {
+    "collapsed": false,
+    "pycharm": {
+     "name": "#%%\n"
+    }
+   }
+  },
+  {
+   "cell_type": "markdown",
+   "source": [
+    "Cut out a time slice of the data."
+   ],
+   "metadata": {
+    "collapsed": false
+   }
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1faf2cbd-eb2b-49bb-8612-554c7a8a387e",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "profile = profile.time_slice(date0=datetime(2010, 1, 1), date1=datetime(2010, 1, 20))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "29c26c1f-c117-4107-ba61-15c507e1aa5f",
+   "metadata": {},
+   "source": [
+    "Process the extracted data into new processed profile."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a5ab1eee-0ba4-4974-aae8-752f66f20031",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "processed_profile = profile.process_en4()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e2a748b5-1975-4f3b-a23d-3197297ec4c7",
+   "metadata": {},
+   "source": [
+    "Sometimes the following line is needed to avoid an error::  \n",
+    "`processed_profile.dataset[\"time\"] = (\"id_dim\", pd.to_datetime(processed_profile.dataset.time.values))`\n",
+    "\n",
+    "Write processed profiles to file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f9f5345a-db15-4f3f-8e4a-6d2d0af11f74",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "processed_profile.dataset.to_netcdf(fn_out)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5618f997",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
\ No newline at end of file
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/analysis_extract_and_compare.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/2._analysis_extract_and_compare.ipynb
similarity index 100%
rename from example_scripts/notebooks/runnable_notebooks/profile/analysis_extract_and_compare.ipynb
rename to example_scripts/notebooks/runnable_notebooks/profile/2._analysis_extract_and_compare.ipynb
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/analysis_extract_and_compare_single_process_tutorial.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/3._analysis_extract_and_compare_single_process_tutorial.ipynb
similarity index 100%
rename from example_scripts/notebooks/runnable_notebooks/profile/analysis_extract_and_compare_single_process_tutorial.ipynb
rename to example_scripts/notebooks/runnable_notebooks/profile/3._analysis_extract_and_compare_single_process_tutorial.ipynb
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/analysis_mask_means_tutorial.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/4._analysis_mask_means_tutorial.ipynb
similarity index 100%
rename from example_scripts/notebooks/runnable_notebooks/profile/analysis_mask_means_tutorial.ipynb
rename to example_scripts/notebooks/runnable_notebooks/profile/4._analysis_mask_means_tutorial.ipynb
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/analysis_average_into_grid_boxes.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/5._analysis_average_into_grid_boxes.ipynb
similarity index 100%
rename from example_scripts/notebooks/runnable_notebooks/profile/analysis_average_into_grid_boxes.ipynb
rename to example_scripts/notebooks/runnable_notebooks/profile/5._analysis_average_into_grid_boxes.ipynb
diff --git a/example_scripts/notebooks/runnable_notebooks/profile/analysis_preprocess_en4.ipynb b/example_scripts/notebooks/runnable_notebooks/profile/analysis_preprocess_en4.ipynb
deleted file mode 100644
index 0e01e4e2..00000000
--- a/example_scripts/notebooks/runnable_notebooks/profile/analysis_preprocess_en4.ipynb
+++ /dev/null
@@ -1,176 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "5bd63eda-5c51-4ab1-bdf0-6ec8eaf869ed",
-   "metadata": {},
-   "source": [
-    "Script for processing raw EN4 data prior to analysis.\n",
-    "See docstring of `Profile.process_en4()` for more specifics on what it does.\n",
-    "\n",
-    "This script will just load modules, read in raw EN4 data, cut out a geographical box, call the processing routine and write the processed data to file.\n",
-    "\n",
-    "You don't have to do this for each EN4 file individually if you don't want, you can read in multiple using `multiple = True` on the creation of the profile object. However, if analysing model data in parallel chunks, you may want to split up the processing into smaller files to make the analysis faster."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "39fa949e-1ba4-4f29-a5fa-906786f0b7b3",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import sys\n",
-    "\n",
-    "# IF USING A DEVELOPMENT BRANCH OF COAST, ADD THE REPOSITORY TO PATH:\n",
-    "# sys.path.append('<PATH_TO_COAST_REPO')\n",
-    "import coast\n",
-    "import pandas as pd\n",
-    "from datetime import datetime\n",
-    "from os import path\n",
-    "\n",
-    "print(\"Modules loaded\")\n",
-    "\n",
-    "# File paths - input en4, output processed file and read config file\n",
-    "\"\"\"\n",
-    "fn_prof = \"<PATH_TO_RAW_EN4_DATA_FILE(S)>\"\n",
-    "fn_out = \"<PATH_TO_OUTPUT_LOCATION_FOR_PROCESSED_PROFILES>\"\n",
-    "fn_cfg_prof = \"<PATH_TO_COAST_PROFILE_CONFIG_FILE>\"\n",
-    "\"\"\"\n",
-    "\n",
-    "fn_out = \"./output.nc\"\n",
-    "fn_prof = path.join('./example_files', \"coast_example_en4_201008.nc\")\n",
-    "fn_cfg_prof = path.join('./config', \"example_en4_profiles.json\")\n",
-    "\n",
-    "\n",
-    "# Some important settings, easier to get at here\n",
-    "longitude_bounds = [-15, 15]  # Geo box to cut out from data (match to model)\n",
-    "latitude_bounds = [40, 65]\n",
-    "multiple = True  # Reading multple files?"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "633dd796-fdbd-4522-82f1-5f98fe835e31",
-   "metadata": {},
-   "source": [
-    "Create profile object containing data"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "3d9f6d11-60aa-4799-8d82-91862fb6c353",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "profile = coast.Profile(config=fn_cfg_prof)\n",
-    "profile.read_en4(fn_prof, multiple=multiple)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "33077579-0f3f-4e89-a1f2-51a709835f28",
-   "metadata": {},
-   "source": [
-    "Get geographical indices to extract."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "98344d6a-5d69-405a-9d0a-752bb160037f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "profile = profile.subset_indices_lonlat_box(longitude_bounds, latitude_bounds)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "23221aba-a10f-45fb-90aa-4c6dbdaed080",
-   "metadata": {},
-   "source": [
-    "Cut out a time slice of the data."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "1faf2cbd-eb2b-49bb-8612-554c7a8a387e",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "profile = profile.time_slice(date0=datetime(2010, 1, 1), date1=datetime(2010, 1, 20))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "29c26c1f-c117-4107-ba61-15c507e1aa5f",
-   "metadata": {},
-   "source": [
-    "Process the extracted data into new processed profile."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "a5ab1eee-0ba4-4974-aae8-752f66f20031",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "processed_profile = profile.process_en4()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e2a748b5-1975-4f3b-a23d-3197297ec4c7",
-   "metadata": {},
-   "source": [
-    "Sometimes the following line is needed to avoid an error::  \n",
-    "`processed_profile.dataset[\"time\"] = (\"id_dim\", pd.to_datetime(processed_profile.dataset.time.values))`\n",
-    "\n",
-    "Write processed profiles to file."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "f9f5345a-db15-4f3f-8e4a-6d2d0af11f74",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "processed_profile.dataset.to_netcdf(fn_out)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "5618f997",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3 (ipykernel)",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.13"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
\ No newline at end of file
diff --git a/example_scripts/notebooks/runnable_notebooks/tidegauge/tidegauge_validation_tutorial.ipynb b/example_scripts/notebooks/runnable_notebooks/tidegauge/tidegauge_validation_tutorial.ipynb
index 27a32401..210034a1 100644
--- a/example_scripts/notebooks/runnable_notebooks/tidegauge/tidegauge_validation_tutorial.ipynb
+++ b/example_scripts/notebooks/runnable_notebooks/tidegauge/tidegauge_validation_tutorial.ipynb
@@ -6,7 +6,20 @@
    "metadata": {},
    "source": [
     "This tutorial gives an overview of some of validation tools available when\n",
-    "using the Tidegauge objects in COAsT."
+    "using the Tidegauge objects in COAsT.\n",
+    "\n",
+    "This includes:\n",
+    "- creating tidegauge objects\n",
+    "    - reading in tidegauge data\n",
+    "    - creating tidegauge object from gridded simulation data\n",
+    "- basic plotting\n",
+    "    - on maps and timeseries\n",
+    "- analysis\n",
+    "    - harmonic analysis and calculation of non-tidal residual\n",
+    "    - doodsonX0 tidal filtering\n",
+    "    - threshold statistics\n",
+    "    - error calculation: mean errors, mean absolute error (MAE), mean square error (MSE)\n",
+    "    "
    ]
   },
   {
@@ -67,7 +80,7 @@
    "id": "827c084b",
    "metadata": {},
    "source": [
-    "### Reading and manipulation\n",
+    "### Reading data\n",
     "\n",
     "We can create our empty tidegauge object:"
    ]
@@ -142,6 +155,17 @@
     "on the same time dimension. It will be read in from a simple netCDF file:"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "35f6d6b2",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "tt = xr.open_dataset(fn_tg)\n",
+    "obs = coast.Tidegauge(dataset=tt)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -160,6 +184,8 @@
    "id": "0727e04a",
    "metadata": {},
    "source": [
+    "## Quick plotting to visualise Tidegauge data\n",
+    "\n",
     "Tidegauge has ready made quick plotting routines for viewing time series and tide gauge location. To look at the tide gauge location:"
    ]
   },
@@ -173,6 +199,33 @@
     "fig, ax = obs.plot_on_map()"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "24e0c86e",
+   "metadata": {},
+   "source": [
+    "There is also a slightly expanded version where you can plot multiple tidegauge objects, included as a list, and also the colour (if it is a dataarray with one value per location)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "59d2e821",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# plot a list tidegauge datasets (here repeating obs for the point of demonstration) and colour\n",
+    "fig, ax = obs.plot_on_map_multiple([obs,obs], color_var_str=\"latitude\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ac21b4a0",
+   "metadata": {},
+   "source": [
+    "Time series can be plotted using `matplotlib.pyplot` methods. However `xarray` has its own plotting wrappers that can be used, which offers some conveniences with labelling"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -180,43 +233,66 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "id=1\n",
-    "obs.dataset.ssh[id].rename({'t_dim':'time'}).plot()  # rename time dimension to enable automatic x-axis labelling\n",
+    "stn_id=1  # pick a gauge station\n",
+    "\n",
+    "plt.subplot(2,1,1)\n",
+    "obs.dataset.ssh[stn_id].plot()  # rename time dimension to enable automatic x-axis labelling\n",
+    "\n",
+    "plt.subplot(2,1,2)\n",
+    "obs.dataset.ssh[stn_id].rename({'t_dim':'time'}).plot()  # rename time dimension to enable automatic x-axis labelling\n",
+    "plt.tight_layout()\n",
     "plt.show()"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "68a18c52",
+   "id": "48797df0",
    "metadata": {},
    "source": [
-    "Note that start and end dates can also be specified for the `Tidegauge.plot_timeseries()` method.\n",
-    "\n",
-    "We can do some simple spatial and temporal manipulations of this data:\n"
+    "Or you can use the `Tidegauge.plot_timeseries()` method, in which start and end dates can also be specified."
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "6b1a5413",
+   "id": "1b99331b",
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Cut out a geographical box\n",
-    "obs_cut = obs.subset_indices_lonlat_box(lonbounds = [-5, 0],\n",
-    "                                            latbounds = [50, 55])\n",
-    "fig, ax = obs_cut.plot_on_map()"
+    "obs.isel(id_dim=stn_id).plot_timeseries(date_start=np.datetime64('2007-01-01'),\n",
+    "                                        date_end = np.datetime64('2007-01-31') )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "68a18c52",
+   "metadata": {},
+   "source": [
+    "## Basic manipulation: subsetting + plotting\n",
+    "\n",
+    "\n",
+    "\n",
+    "We can do some simple spatial and temporal manipulations of this data:\n"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "c6b57274",
+   "id": "6b1a5413",
    "metadata": {},
    "outputs": [],
    "source": [
     "# Cut out a time window\n",
-    "obs_cut = obs.time_slice( date0 = datetime.datetime(2007, 1, 1), date1 = datetime.datetime(2007,1,31))"
+    "obs_cut = obs.time_slice( date0 = datetime.datetime(2007, 1, 1), date1 = datetime.datetime(2007,1,31))\n",
+    "\n",
+    "# Cut out geographical boxes\n",
+    "obs_upper = obs_cut.subset_indices_lonlat_box(lonbounds = [0, 3],\n",
+    "                                            latbounds = [50, 55])\n",
+    "obs_lower = obs_cut.subset_indices_lonlat_box(lonbounds = [-9, -3],\n",
+    "                                            latbounds = [55.5, 59])\n",
+    "\n",
+    "#fig, ax = obs_cut.plot_on_map()\n",
+    "fig, ax = obs_upper.plot_on_map_multiple([obs_upper, obs_lower], color_var_str=\"latitude\")"
    ]
   },
   {
@@ -304,9 +380,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Shift the timestamp so it overlaps with the tidegauge data - Not ideal but this is only a demo\n",
-    "#obs.dataset.coords[\"time\"] = obs.dataset.coords[\"time\"] + 1000000000 * 3600 * 24 * 365 * 3\n",
-    "\n",
     "# Cut down data to be only in 2007 to match model data.\n",
     "start_date = datetime.datetime(2007, 1, 1)\n",
     "end_date = datetime.datetime(2007, 1, 31)\n",
@@ -330,8 +403,16 @@
    "source": [
     "model_timeseries = obs.obs_operator(nemo)\n",
     "\n",
-    "# In this case, transpose the interpolated dataset\n",
-    "model_timeseries.dataset = model_timeseries.dataset.transpose()"
+    "# Take a look\n",
+    "model_timeseries.dataset"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "88736e30",
+   "metadata": {},
+   "source": [
+    "We can see that the structure for the new dataset `model_timeseries`, generated from the gridded model simulation, is that of a `tidegauge` object. NB in the example simulation data the ssh variable is output as 5-day means. So it is not particulatly useful for high frequency validation but serves as a demonstration of the workflow."
    ]
   },
   {
@@ -339,11 +420,23 @@
    "id": "5ba915ea",
    "metadata": {},
    "source": [
+    "## Tidegauge analysis methods\n",
+    "\n",
     "For a good comparison, we would like to make sure that both the observed and\n",
     "modelled `Tidegauge` objects contain the same missing values. `TidegaugeAnalysis`\n",
     "contains a routine for ensuring this. First create our analysis object:"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6a364fdd",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "tganalysis = coast.TidegaugeAnalysis()"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -351,9 +444,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "# In this case, transpose the interpolated dataset\n",
+    "model_timeseries.dataset = model_timeseries.dataset.transpose()\n",
+    "\n",
     "# This routine searches for missing values in each dataset and applies them\n",
     "# equally to each corresponding dataset\n",
-    "tganalysis = coast.TidegaugeAnalysis()\n",
     "obs_new, model_new = tganalysis.match_missing_values(obs.dataset.ssh, model_timeseries.dataset.ssh)"
    ]
   },
@@ -379,14 +474,6 @@
     "First we can use the `TidegaugeAnalysis` class to do a harmonic analysis. Suppose\n",
     "we have two `Tidegauge` objects called `obs` and `model_timeseries` generated from tidegauge observations and model simulations respectively.\n",
     "\n",
-    "We can subtract means from all time series:"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bf8c89e4",
-   "metadata": {},
-   "source": [
     "Then subtract means from all the time series"
    ]
   },
@@ -405,12 +492,38 @@
     "# easily compared."
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "663a5270",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2d7aa25a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.figure()\n",
+    "plt.plot( model_new.dataset.time, model_new.dataset.ssh.isel(id_dim=stn_id),\n",
+    "         label='model - demeaned', color='orange' )\n",
+    "plt.plot( obs_new.dataset.time, obs_new.dataset.ssh.isel(id_dim=stn_id) ,\n",
+    "         label='obs - demeaned', color='blue' )\n",
+    "plt.title(f'model and observed timeseries : {obs_new.dataset.site_name.isel(id_dim=stn_id).coords}')\n",
+    "plt.legend()\n",
+    "plt.xticks(rotation=45)\n",
+    "plt.show()"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "36cb5707",
    "metadata": {},
    "source": [
-    "Then we can apply the harmonic analysis (though the example data is too short for this example to be that meaningful):"
+    "Then we can apply the harmonic analysis (though the example data is too short for this example to be that meaningful and the model data is only saved as 5-day means! Nevertheless we proceed):"
    ]
   },
   {
@@ -482,7 +595,10 @@
    "outputs": [],
    "source": [
     "ntr_mod = tganalysis.calculate_non_tidal_residuals(model_new.dataset.ssh, tide_mod.dataset.reconstructed)\n",
-    "ntr_obs = tganalysis.calculate_non_tidal_residuals(obs_new.dataset.ssh, tide_obs.dataset.reconstructed)\n"
+    "ntr_obs = tganalysis.calculate_non_tidal_residuals(obs_new.dataset.ssh, tide_obs.dataset.reconstructed)\n",
+    "\n",
+    "# Take a look\n",
+    "ntr_mod.dataset"
    ]
   },
   {
@@ -490,11 +606,12 @@
    "id": "ac23e4ee",
    "metadata": {},
    "source": [
+    "The dataset structure is preserved and has created a new variable called `ntr` - non-tidal residual.\n",
     "\n",
     "By default, this routines will apply `scipy.signal.savgol_filter` to the non-tidal residuals\n",
     "to remove some noise. This can be switched off using `apply_filter = False`.\n",
     "\n",
-    "The Doodson X0 filter is an alternative way of estimating non-tidal residuals:"
+    "The Doodson X0 filter is an alternative way of estimating non-tidal residuals. This will return a new Tidegauge() object containing filtered ssh data:"
    ]
   },
   {
@@ -504,84 +621,189 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dx0 = tganalysis.doodson_x0_filter(obs.dataset, \"ssh\")"
+    "dx0 = tganalysis.doodson_x0_filter(obs_new.dataset, \"ssh\")\n",
+    "\n",
+    "# take a look\n",
+    "dx0.dataset\n"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "fe89aa16",
+   "id": "ca8b8ae6",
+   "metadata": {},
+   "source": [
+    "We can compare these analyses e.g. the observed timeseries against the harmonic reconstruction. Noting we can in principle extend the harmoninc reconstruction beyond the observation time window."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b23719a6",
    "metadata": {},
+   "outputs": [],
    "source": [
-    "This will return a new Tidegauge() object containing filtered ssh data."
+    "plt.figure()\n",
+    "plt.plot( tide_obs.dataset.time, tide_obs.dataset.reconstructed.isel(id_dim=stn_id), label='reconstructed from harmonics', color='orange' )\n",
+    "plt.plot( obs_new.dataset.time, obs_new.dataset.ssh.isel(id_dim=stn_id) , label='observed', color='blue' )\n",
+    "plt.legend()\n",
+    "plt.xticks(rotation=45)\n",
+    "plt.show()"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "101abcf1",
+   "id": "7b3c4076",
    "metadata": {},
    "source": [
+    "We can also look closer at the difference between the observed timeseries and the harmonic reconstruction, that is the non-tidal residual. And we can compare the observed and modelled non-harmonic residual and contrast the different methods of removing the tides. Here we contrast using a harmonic analysis (and creating a \"non-tidal residual\") with the DoodsonX0 filter. Note that the timeseries is far too short for a sensible analysis and that this is really a demonstration of concept."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7a3abf06",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.figure()\n",
+    "plt.subplot(2,1,1)\n",
+    "plt.plot( dx0.dataset.time, dx0.isel(id_dim=stn_id).dataset.ssh, label='obs: doodsonX0 filtered', color='orange' )\n",
+    "plt.plot( ntr_obs.dataset.time, ntr_obs.isel(id_dim=stn_id).dataset.ntr, label='obs: non-tidal residual', color='blue' )\n",
+    "plt.title('analysis comparison: non-tidal residual vs doodsonX0')\n",
+    "plt.legend()\n",
+    "plt.xticks(rotation=45)\n",
     "\n",
-    "### Other TidegaugeAnalysis methods \n",
+    "plt.subplot(2,1,2)\n",
+    "plt.plot( ntr_mod.dataset.time, ntr_mod.isel(id_dim=stn_id).dataset.ntr, label='model: non-tidal residual', color='g' )\n",
+    "plt.plot( ntr_obs.dataset.time, ntr_obs.isel(id_dim=stn_id).dataset.ntr, label='obs: non-tidal residual', color='blue' )\n",
+    "plt.title('model vs observation: non-tidal residual')\n",
+    "plt.legend()\n",
+    "plt.xticks(rotation=45)\n",
     "\n",
-    "#### Calculate errors\n",
+    "plt.tight_layout()\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b9e45f07-043d-4c46-9d82-27127e66ddde",
+   "metadata": {},
+   "source": [
+    "### Threshold Statistics for non-tidal residuals\n",
     "\n",
-    "The difference() routine will calculate differences, absolute_differences and squared differenced for all variables:"
+    "This is a simple extreme value analysis of whatever data you use. It will count the number of peaks and the total time spent over each threshold provided. It will also count the numbers of daily and monthly maxima over each threshold. To this, a Tidegauge object and an array of thresholds (in metres) should be passed. The method return `peak_count_*`, `time_over_threshold_*`, `dailymax_count_*`, `monthlymax_count_*`:\n"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "c4231f71",
+   "id": "6dba7711",
    "metadata": {},
    "outputs": [],
    "source": [
-    "ntr_diff = tganalysis.difference(ntr_obs.dataset, ntr_mod.dataset)\n",
-    "ssh_diff = tganalysis.difference(obs_new.dataset, model_new.dataset)\n"
+    "thresh_mod = tganalysis.threshold_statistics(ntr_mod.dataset, thresholds=np.arange(-2, 2, 0.1))\n",
+    "thresh_obs = tganalysis.threshold_statistics(ntr_obs.dataset, thresholds=np.arange(-2, 2, 0.1))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d8e4b81a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Have a look\n",
+    "thresh_obs"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "34868071",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot( thresh_obs.threshold, thresh_obs.peak_count_ntr.mean(dim='id_dim') )\n",
+    "#plt.ylim([0,15])\n",
+    "plt.title(f\"Observed peaks over threshold\")\n",
+    "plt.xlabel('Analysis threshold (m)')\n",
+    "plt.ylabel('event count')"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "bc57ec3d",
+   "id": "1c344448",
    "metadata": {},
    "source": [
-    "We can then easily get mean errors, MAE and MSE\n"
+    "Note that the non-tidal residual is a noisy timeseries (computed as a difference between two timeseries) so peaks do not necessarily correspond to peaks in total water level. For this reason `time_over_threshold_*` can be useful. Below we see that about 6% (y-axis) of the observations of non-tidal residual exceed 20cm (x-axis). Again, threshold statistics are more useful with a longer record."
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "9e7597b4",
+   "id": "607364e5",
    "metadata": {},
    "outputs": [],
    "source": [
-    "mean_stats = ntr_diff.dataset.mean(dim=\"t_dim\", skipna=True)"
+    "normalised_event_count = 100 * thresh_obs.time_over_threshold_ntr.isel(id_dim=stn_id) / thresh_obs.time_over_threshold_ntr.isel(id_dim=stn_id).max()\n",
+    "\n",
+    "plt.plot( thresh_obs.threshold, normalised_event_count )\n",
+    "plt.ylim([0,20])\n",
+    "plt.xlim([0.1,0.4])\n",
+    "plt.title(f\"time over threshold\")\n",
+    "plt.xlabel('Analysis threshold (m)')\n",
+    "plt.ylabel('normalised event count (%)')\n",
+    "plt.plot(thresh_obs.threshold[22], normalised_event_count[22], 'r+', markersize=20)"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "b9e45f07-043d-4c46-9d82-27127e66ddde",
+   "id": "101abcf1",
    "metadata": {},
    "source": [
-    "#### Threshold Statistics for non-tidal residuals\n",
     "\n",
-    "This is a simple extreme value analysis of whatever data you use. It will count the number of peaks and the total time spent over each threshold provided. It will also count the numbers of daily and monthly maxima over each threshold. To this, a Tidegauge object and an array of thresholds (in metres) should be passed:\n"
+    "## Other TidegaugeAnalysis methods \n",
+    "\n",
+    "### Calculate errors\n",
+    "\n",
+    "The difference() routine will calculate differences, absolute_differences and squared differenced for all variables. Corresponding new variables are created with names `diff_*`, `abs_diff_*` and `square_diff_*`"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "6dba7711",
+   "id": "c4231f71",
    "metadata": {},
    "outputs": [],
    "source": [
-    "thresh_mod = tganalysis.threshold_statistics(ntr_mod.dataset, thresholds=np.arange(0, 2, 0.2))\n",
-    "thresh_obs = tganalysis.threshold_statistics(ntr_obs.dataset, thresholds=np.arange(0, 2, 0.2))"
+    "ntr_diff = tganalysis.difference(ntr_obs.dataset, ntr_mod.dataset)\n",
+    "ssh_diff = tganalysis.difference(obs_new.dataset, model_new.dataset)\n",
+    "\n",
+    "# Take a look\n",
+    "ntr_diff.dataset"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bc57ec3d",
+   "metadata": {},
+   "source": [
+    "We can then easily get mean errors, MAE and MSE\n"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "d8e4b81a",
+   "id": "9e7597b4",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mean_stats = ntr_diff.dataset.mean(dim=\"t_dim\", skipna=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "400086e9",
    "metadata": {},
    "outputs": [],
    "source": []
@@ -603,9 +825,9 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.13"
+   "version": "3.8.10"
   }
  },
  "nbformat": 4,
  "nbformat_minor": 5
-}
+}
\ No newline at end of file
diff --git a/example_scripts/profile_validation/plot_validation_gridded_data_tutorial.ipynb b/example_scripts/profile_validation/plot_validation_gridded_data_tutorial.ipynb
index e0b3d7a2..09b18625 100644
--- a/example_scripts/profile_validation/plot_validation_gridded_data_tutorial.ipynb
+++ b/example_scripts/profile_validation/plot_validation_gridded_data_tutorial.ipynb
@@ -231,4 +231,4 @@
  },
  "nbformat": 4,
  "nbformat_minor": 5
-}
+}
\ No newline at end of file
diff --git a/example_scripts/profile_validation/profile_intro.ipynb b/example_scripts/profile_validation/profile_intro.ipynb
deleted file mode 100644
index de8444c7..00000000
--- a/example_scripts/profile_validation/profile_intro.ipynb
+++ /dev/null
@@ -1,310 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "fce85142",
-   "metadata": {},
-   "source": [
-    "\n",
-    "##  Example useage of Profile object.\n",
-    "\n",
-    "### Overview\n",
-    "\n",
-    "INDEXED type class for storing data from a CTD Profile (or similar\n",
-    "down and up observations). The structure of the class is based around having\n",
-    "discrete profile locations with independent depth dimensions and coords. \n",
-    "The class dataset should contain two dimensions:\n",
-    "\n",
-    "    > id_dim      :: The profiles dimension. Each element of this dimension\n",
-    "                 contains data (e.g. cast) for an individual location.\n",
-    "    > z_dim   :: The dimension for depth levels. A profile object does not\n",
-    "                 need to have shared depths, so NaNs might be used to\n",
-    "                 pad any depth array.\n",
-    "\n",
-    "Alongside these dimensions, the following minimal coordinates should also\n",
-    "be available:\n",
-    "\n",
-    "    > longitude (id_dim)   :: 1D array of longitudes, one for each id_dim\n",
-    "    > latitude  (id_dim)   :: 1D array of latitudes, one for each id_dim\n",
-    "    > time      (id_dim)   :: 1D array of times, one for each id_dim\n",
-    "    > depth     (id_dim, z_dim)  :: 2D array of depths, with different depth\n",
-    "                                levels being provided for each profile.\n",
-    "                                Note that these depth levels need to be\n",
-    "                                stored in a 2D array, so NaNs can be used\n",
-    "                                to pad out profiles with shallower depths.\n",
-    "    > id_name   (id_dim)   :: [Optional] Name of id_dim/case or id_dim number.\n",
-    "\n",
-    "## Example Useage\n",
-    "\n",
-    "Below is a description of the available example scripts for this class as well\n",
-    "as an overview of validation using` Profile` and `ProfileAnalysis`.\n",
-    "\n",
-    "### Example Scripts\n",
-    "\n",
-    "Please see COAsT/example_scripts/profile_validation for some scripts which\n",
-    "demonstrate how to use the Profile and ProfileAnalysis classes for model\n",
-    "validation. \n",
-    "\n",
-    "1. `analysis_preprocess_en4.py` : If you're using EN4 data, this kind of script\n",
-    "might be your first step for analysis.\n",
-    "\n",
-    "2. `analysis_extract_and_compare.py`: This script shows you how to extract\n",
-    "the nearest model profiles, compare them with EN4 observations and get errors\n",
-    "throughout the vertical dimension and averaged in surface and bottom zones\n",
-    "\n",
-    "3. `analysis_extract_and_compare_single_process.py`: This script does the same\n",
-    "as number 2. However, it is modified slightly to take a command line argument\n",
-    "which helps it figure out which dates to analyse. This means that this script\n",
-    "can act as a template for `jug` type parallel processing on, e.g. JASMIN.\n",
-    "\n",
-    "4. `analysis_mask_means.py`: This script demonstrates how to use boolean masks\n",
-    "to obtain regional averages of profiles and errors.\n",
-    "\n",
-    "5. `analysis_average_into_grid_boxes.py`: This script demonstrates how to \n",
-    "average the data inside a `Profile` object into regular grid boxes and \n",
-    "seasonal climatologies.\n",
-    "\n",
-    "### Basic useage\n",
-    "\n",
-    "We can create a new Profile object easily:\n",
-    "\n",
-    "```\n",
-    "profile = coast.Profile()\n",
-    "```\n",
-    "\n",
-    "Currently, this object is empty, and contains no dataset. There are some\n",
-    "reading routines currently available in Profile for reading EN4 or WOD data\n",
-    "files. These can be used to easily read data into your new profile object:\n",
-    "\n",
-    "```\n",
-    "# Read WOD data into profile object (OVERWRITES DATASET)\n",
-    "profile.read_wod( filename )\n",
-    "\n",
-    "# Read EN4 data into profile object\n",
-    "profile.read_en4( filename )\n",
-    "```\n",
-    "\n",
-    "Alternatively, you can pass an `xarray.dataset` straight to Profile:\n",
-    "```\n",
-    "profile = coast.Profile( dataset = your_dataset, config = config_file [opt] )\n",
-    "```\n",
-    "\n",
-    "We can do some simple spatial and temporal manipulations of this data:\n",
-    "\n",
-    "```\n",
-    "# Cut out a geographical box\n",
-    "profile = profile.subset_indices_lonlat_box(lonbounds = [-15, 15], \n",
-    "                                            latbounds = [45, 65])\n",
-    "\n",
-    "# Cut out a time window\n",
-    "profile = profile.time_slice( date0 = datetime(2004, 1, 1), date1 = datetime(2005,1,1))\n",
-    "```\n",
-    "\n",
-    "If you are using EN4 data, you can use the `process_en4()` routine to apply \n",
-    "quality control flags to the data (replacing with NaNs):\n",
-    "\n",
-    "```\n",
-    "processed_profile = profile.process_en4()\n",
-    "```\n",
-    "\n",
-    "### Direct Model Comparison\n",
-    "\n",
-    "There are a number of routines available for interpolating in the horizontal,\n",
-    "vertical and in time to do direct comparisons of model and profile data.\n",
-    "`Profile.obs_operator` will do a nearest neighbour spatial interpolation of\n",
-    "the data in a `Gridded` object to profile latitudes/longitudes. It will also\n",
-    "do a custom time interpolation. For example:\n",
-    "\n",
-    "```\n",
-    "# Create gridded object:\n",
-    "nemo = coast.Gridded(fn_dat, fn_dom, multiple=True, config=fn_cfg_nemo)\n",
-    "\n",
-    "# Create a landmask array in Gridded\n",
-    "nemo.dataset[\"landmask\"] = nemo.dataset.bottom_level == 0\n",
-    "nemo.dataset = nemo.dataset.rename({\"depth_0\": \"depth\"})\n",
-    "\n",
-    "# Use obs operator for horizontal remapping of Gridded onto Profile.\n",
-    "model_profiles = profile.obs_operator(nemo)\n",
-    "```\n",
-    "\n",
-    "In the above example we added a `landmask` variable to the `Gridded` dataset.\n",
-    "When this is present, the `obs_operator` will use this to interpolation to the\n",
-    "nearest *wet* point. If not present, it will just take the nearest grid point.\n",
-    "\n",
-    "Now that we have interpolated the model onto Profiles, we have a new Profile\n",
-    "object called `model_profiles`. This can be used to do some comparisons with\n",
-    "our original `processed_profile` object, which we created above. First lets\n",
-    "make our ProfileAnalysis object:\n",
-    "\n",
-    "```\n",
-    "analysis = coast.ProfileAnalysis()\n",
-    "```\n",
-    "\n",
-    "We can use `ProfileAnalysis.interpolate_vertical` to interpolate all variables\n",
-    "within a Profile object. This can be done onto a set of reference depths or,\n",
-    "matching another object's depth coordinates by passing another profile object.\n",
-    "Let's interpolate our model profiles onto observations depths, then interpolate\n",
-    "both onto a set of reference depths:\n",
-    "\n",
-    "```\n",
-    "# Interpolate model profiles onto observation depths\n",
-    "model_profiles_interp = analysis.interpolate_vertical(model_profiles, profile, interp_method=\"linear\")\n",
-    "\n",
-    "# Vertical interpolation of model profiles to reference depths\n",
-    "model_profiles_interp = panalysis.interpolate_vertical(model_profiles_interp, ref_depth)\n",
-    "\n",
-    "# Interpolation of obs profiles to reference depths\n",
-    "profile_interp = analysis.interpolate_vertical(profile, ref_depth)\n",
-    "```\n",
-    "\n",
-    "Now that we have two Profile objects that are horizontally and vertically \n",
-    "comparable, we can use `difference()` to get some basic errors:\n",
-    "\n",
-    "```\n",
-    "differences = analysis.difference(profile_interp, model_profiles_interp)\n",
-    "``` \n",
-    "\n",
-    "This will return a new `Profile` object that contains the variable difference,\n",
-    "absolute differences and square differences at all depths and means for each\n",
-    "profile.\n",
-    "\n",
-    "### Layer Averaging\n",
-    "\n",
-    "We can use the `Profile` object to get mean values between specific depth levels\n",
-    "or for some layer above the bathymetric depth. The former can be done using\n",
-    "`ProfileAnalysis.depth_means()`, for example the following will return a new\n",
-    "Profile object containing the means of all variables between 0m and 5m:\n",
-    "\n",
-    "```\n",
-    "profile_surface = analysis.depth_means(profile, [0, 5])\n",
-    "```\n",
-    "\n",
-    "This can be done for any arbitrary depth layer defined by two depths. In some\n",
-    "cases it may be that one of the depth levels is not defined by a constant, e.g.\n",
-    "when calculating bottom means. In this case you may want to calculate averages\n",
-    "in some layer above the seabed. This can be done using \n",
-    "`ProfileAnalysis.bottom_means()`. For example:\n",
-    "\n",
-    "```\n",
-    "bottom_height = [10, 30, 100]  # Use bottom heights of 10m, 30m and 100m for...\n",
-    "bottom_thresh = [100, 500, np.inf]  # ...depths less than 100m, 500m and infinite\n",
-    "profile_bottom = analysis.bottom_means(profile, bottom_height, bottom_thresh)\n",
-    "```\n",
-    "\n",
-    "This will calculate bottom means differently depending upon the actualy depth.\n",
-    "For depths less than 100m, it will take the average of the bottom 10m. For less\n",
-    "than 500 m (and greater than 100m), it will take the average of the bottom 30m.\n",
-    "And so on. This routine will look for a variable in the input Profile called\n",
-    "`bathymetry`. If this is not present you will need to insert it yourself, e.g:\n",
-    "\n",
-    "```\n",
-    "profile.dataset[\"bathymetry\"] = ([\"id_dim\"], obs_bathymetry_array)\n",
-    "```\n",
-    "\n",
-    "**NOTE1: The `bathymetry` variable does not actually need to contain bathymetric\n",
-    "depths, it can also be used to calculate means above any non-constant surface.\n",
-    "For example, it could be mixed layer depth.\n",
-    "\n",
-    "**NOTE2: This can be done for any Profile object. So, you could use this \n",
-    "workflow to also average a Profile derived from the `difference()` routine.\n",
-    "\n",
-    "### Regional (Mask) Averaging\n",
-    "\n",
-    "We can use `Profile` in combination with `MaskMaker` to calculate averages over\n",
-    "regions defined by masks. For example, to get the mean errors in the North Sea.\n",
-    "Start by creating a list of boolean masks we would like to use:\n",
-    "\n",
-    "```\n",
-    "mm = coast.MaskMaker()\n",
-    "\n",
-    "# Define Regional Masks\n",
-    "regional_masks = []\n",
-    "bath = nemo.dataset.bathymetry.values\n",
-    "\n",
-    "# Add regional mask for whole domain\n",
-    "regional_masks.append(np.ones(lon.shape))\n",
-    "\n",
-    "# Add regional mask for North Sea\n",
-    "regional_masks.append(mm.region_def_nws_north_sea(lon, lat, bath))\n",
-    "\n",
-    "region_names = [\"whole_domain\",\"north_sea\",]\n",
-    "```\n",
-    "\n",
-    "Next, we must make these masks into datasets using `MaskMaker.make_mask_dataset`.\n",
-    "Masks should be 2D datasets defined by booleans. In our example here we have used\n",
-    "the latitude/longitude array from the nemo object, however it can be defined\n",
-    "however you like.\n",
-    "\n",
-    "```\n",
-    "mask_list = mm.make_mask_dataset(lon, lat, regional_masks)\n",
-    "```\n",
-    "\n",
-    "Then we use `ProfileAnalysis.determine_mask_indices` to figure out which\n",
-    "profiles in a `Profile` object lie within each regional mask:\n",
-    "\n",
-    "```\n",
-    "mask_indices = analysis.determine_mask_indices(profile, mask_list)\n",
-    "```\n",
-    "\n",
-    "This returns an object called `mask_indices`, which is required to pass to\n",
-    "`ProfileAnalysis.mask_means()`. This routine will return a new xarray dataset\n",
-    "containing averaged data for each region:\n",
-    "\n",
-    "```\n",
-    "mask_means = analysis.mask_means(profile, mask_indices)\n",
-    "```\n",
-    "\n",
-    "### Gridding Profile Data\n",
-    "\n",
-    "If you have large amount of profile data you may want to average it into\n",
-    "grid boxes to get, for example, mean error maps or climatologies. This can be\n",
-    "done using `ProfileAnalysis.average_into_grid_boxes()`.\n",
-    "\n",
-    "We can create a gridded dataset of all the data using:\n",
-    "\n",
-    "```\n",
-    "grid_lon = np.arange(-15, 15, 0.5)\n",
-    "grid_lat = np.arange(45, 65, 0.5)\n",
-    "prof_gridded = analysis.average_into_grid_boxes(grid_lon, grid_lat)\n",
-    "```\n",
-    "\n",
-    "Alternatively, we can calculate averages for each season:\n",
-    "\n",
-    "```\n",
-    "prof_gridded_DJF = profile_analysis.average_into_grid_boxes(grid_lon, grid_lat, season=\"DJF\", var_modifier=\"_DJF\")\n",
-    "prof_gridded_MAM = profile_analysis.average_into_grid_boxes(grid_lon, grid_lat, season=\"MAM\", var_modifier=\"_MAM\")\n",
-    "prof_gridded_JJA = profile_analysis.average_into_grid_boxes(grid_lon, grid_lat, season=\"JJA\", var_modifier=\"_JJA\")\n",
-    "prof_gridded_SON = profile_analysis.average_into_grid_boxes(grid_lon, grid_lat, season=\"SON\", var_modifier=\"_SON\")\n",
-    "```\n",
-    "\n",
-    "Here, `season` specifies which season to average over and `var_modifier` is added to the end of \n",
-    "all variable names in the object's dataset. \n",
-    "\n",
-    "This function returns a new Gridded object. It also contains a new variable\n",
-    "called `grid_N`, which stores how many profiles were averaged into each grid box.\n",
-    "You may want to use this when using the analysis.\n"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3 (ipykernel)",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.13"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/requirements.txt b/requirements.txt
index c96531c3..2028effc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@ xarray>=2022.3.0
 matplotlib>=3.5.3
 netCDF4>=1.5.8
 scipy>=1.8.0
-gsw>=3.4.0
+gsw==3.4.0
 utide>=0.3.0
 scikit-learn>=1.0.2
 scikit-image>=0.19.2
diff --git a/setup.py b/setup.py
index 84f87179..cf06a4bd 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
 PACKAGE = SimpleNamespace(
     **{
         "name": "COAsT",
-        "version": "3.1.1",
+        "version": "3.1.2",
         "description": "This is the Coast Ocean Assessment Tool",
         "url": "https://www.bodc.ac.uk",
         "download_url": "https://github.com/British-Oceanographic-Data-Centre/COAsT/",
@@ -36,7 +36,7 @@
             "matplotlib>=3.5.3",
             "netCDF4>=1.5.8",
             "scipy>=1.8.0",
-            "gsw>=3.4.0",
+            "gsw==3.4.0",
             "utide>=0.3.0",
             "scikit-learn>=1.0.2",
             "scikit-image>=0.19.2",


Test patch:
diff --git a/unit_testing/test_index_classes_load_datasets.py b/unit_testing/test_index_classes_load_datasets.py
index cf425927..1ebf5f97 100644
--- a/unit_testing/test_index_classes_load_datasets.py
+++ b/unit_testing/test_index_classes_load_datasets.py
@@ -6,10 +6,16 @@
         pytest -s tests/test_index_classes_load_datasets.py
 
 This file was in directory tests/ and executed under Git Actions. However, it
-calls external data that are too large for accessible hosting. Nevertheless,
-the structure proposed here "one file per type" could lend itself well to a
-restructure unit_testing work flow. So I move it there as a potential template
-for when the unit testing is restructured.
+calls external data that are too large for accessible hosting.
+
+Nevertheless, it includes tests for undeveloped objects in the indexed class:
+* EGO Gliders
+* ARGO floats
+* OceanParcels data
+
+There is also some elegance in having all the indexed class load methods together.
+As opposed to having one file per object, with all its methods too. Better filenaming
+could help point out the parent object.
 """
 from coast import Altimetry, Profile, Glider, Argos, Oceanparcels, Tidegauge
 import datetime
@@ -19,7 +25,7 @@
 fn_altimetry_config = "config/example_altimetry.json"
 
 # En4 profile data (NetCDF)
-fn_profile = "./example_files/EN.4.2.1.f.profiles.l09.201501.nc"
+fn_profile = "./example_files/coast_example_en4_201008.nc"
 fn_profile_config = "config/example_en4_profiles.json"
 
 # EGO glider (NetCDF). More examples see https://www.bodc.ac.uk/data/bodc_database/gliders/
@@ -63,7 +69,8 @@ def test_load_profile_no_config():
 
 
 def test_load_profile_config():
-    profile = Profile(file_path=fn_profile, config=fn_profile_config)
+    profile = Profile(config=fn_profile_config)
+    profile.read_en4(fn_profile)
     assert profile is not None
     # assert that we have the coordinate and data variable names as specified in the json config file
     assert list(profile.dataset.coords) == ["latitude", "longitude", "time"]