{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "cd5ea363-0f9b-480a-a6e4-d28338c442bb", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import pandas as pd" ] }, { "cell_type": "code", "execution_count": null, "id": "b5d08168-f04f-4769-93f7-c5e3c25e5c0f", "metadata": {}, "outputs": [], "source": [ "from marine_qc import (\n", " do_aground_check,\n", " do_speed_check,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "3fa6ca48-7f38-4127-93e8-8a8f4119d5e7", "metadata": {}, "outputs": [], "source": [ "from data import get_buoy_data" ] }, { "cell_type": "markdown", "id": "56c5d833-c51f-4b59-8c75-9166cbcbf530", "metadata": {}, "source": [ "# How to use quality control checks with sequential reports from buoys" ] }, { "cell_type": "markdown", "id": "5cddbfc6-f401-4c8f-bfea-b08e09ed38ee", "metadata": {}, "source": [ "The ``marine_qc`` toolbox provides some quality control (QC) checks that work on sequential marine buoy reports. Creating some test dataset we can show how to use them on \"real\" data. There are three checks that are shown here:\n", "\n", "* a speed check that tests that speeds inferred from a sequence of reports are not implausible for a drifting buoy\n", "* a aground check that tests whether reports from a drifting buoy suggest it has run aground and stopped moving\n", "\n", "For more information of all available QC checks on sequential buoy reports see [the overview of QC functions for additional buoy reports](https://marine-qc.readthedocs.io/en/stable/overview_buoy_tracking.html)." ] }, { "cell_type": "markdown", "id": "33ad2f54-14eb-42b8-bcc9-3c2dd2f57a10", "metadata": {}, "source": [ "The test dataset we provide is a ``pandas.DataFrame`` including sequential marine reports representing the track of one drifting buoy. The QC functions return a QC flag for each individual report. The flags are:\n", "\n", "* `0`: the check has passed\n", "* `1`: the check has failed\n", "* `2`: the check is untestable" ] }, { "cell_type": "code", "execution_count": null, "id": "b65d2a1a-4113-4d16-b416-5548a90424de", "metadata": {}, "outputs": [], "source": [ "data = get_buoy_data()\n", "data" ] }, { "cell_type": "markdown", "id": "8f7fc855-67eb-4a61-b63d-cbff3b4c022f", "metadata": {}, "source": [ "The drifting buoy data include four different parameters (`lat`, `lon`, `date`, and `sst`)." ] }, { "cell_type": "code", "execution_count": null, "id": "c3c6802c-830b-4f95-af10-faff6fe69e28", "metadata": {}, "outputs": [], "source": [ "fig, axs = plt.subplots(1, 2, figsize=(13, 5))\n", "\n", "axs[0].plot(data.lon, data.lat, marker=\"o\", linestyle=\"-\")\n", "axs[0].set_title(\"Buoy drifter – latitude/longitude track\")\n", "axs[0].set_xlabel(\"Longitude (°)\")\n", "axs[0].set_ylabel(\"Latitude (°)\")\n", "axs[0].grid(True)\n", "\n", "point_counts = {}\n", "for lon, lat in zip(data.lon, data.lat, strict=False):\n", " point = (lon, lat)\n", " if point in point_counts:\n", " point_counts[point] += 1\n", " else:\n", " point_counts[point] = 1\n", "\n", "# Add labels with occurrence counts\n", "for point, count in point_counts.items():\n", " lon, lat = point\n", " axs[0].annotate(f\"{count} time steps\", (lon, lat), textcoords=\"offset points\", xytext=(10, 0), ha=\"left\")\n", "\n", "axs[1].plot(data.sst, marker=\"o\", linestyle=\"-\")\n", "axs[1].set_title(\"Buoy drifter – sea surface temperature\")\n", "axs[1].set_xlabel(\"Time\")\n", "axs[1].set_ylabel(\"Temperature (°C)\")\n", "axs[1].grid(True)\n", "\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "705e3a66-b080-42b8-9f9c-ebdbf6ff884c", "metadata": {}, "source": [ "We see a buoy that is drifting northwards with a constant speed expect one time step. The last position is kept for four time steps. The buoy does not move anymore. The sea-surface temperature has an offset in the middle of the track." ] }, { "cell_type": "markdown", "id": "d39a0ce4-f569-479b-ae3f-c7053889b39f", "metadata": {}, "source": [ "Firstly, a speed check is performed. Beside `lat`, `lon`, and `date`, this check needs some extra parameters:\n", "\n", "* speed_limit: maximum allowable speed for an in situ drifting buoy in meters per second (`2.5`)\n", "* min_win_period: minimum period of time in days over which position is assessed for speed estimates (`1`)\n", "* max_win_period: maximum period of time in days over which position is assessed for speed estimates (`1`)\n", "\n", "The speed is calculated from `lat`, `lon` and `date` within a period of `min_win_period` and `max_win_period`. If the speed within this period is greater than `speed_limit`, the QC flags for the entire period are set to `1` (failed)." ] }, { "cell_type": "code", "execution_count": null, "id": "b824a093-bf75-4d75-8d03-cf04b5bdc6da", "metadata": {}, "outputs": [], "source": [ "qc_speed = do_speed_check(\n", " lat=data.lat,\n", " lon=data.lon,\n", " date=data.date,\n", " speed_limit=2.5,\n", " min_win_period=1,\n", " max_win_period=1,\n", ")\n", "pd.DataFrame({\"date\": data.date, \"lat\": data.lat, \"lon\": data.lon, \"qc_speed\": qc_speed})" ] }, { "cell_type": "markdown", "id": "88144ea2-6f7a-4007-993d-35f88b7e8f68", "metadata": {}, "source": [ "As expected the speed check fails for three time steps where the speed increases." ] }, { "cell_type": "markdown", "id": "14996220-5f9b-4e2f-b253-039a6a67ce7f", "metadata": {}, "source": [ "Next, a aground check is performed. Beside `lat`, `lon`, and `date`, this check needs some extra parameters:\n", "\n", "* smooth_win: length of window (odd number) in datapoints used for smoothing lon/lat (`3`)\n", "* min_win_period: minimum period of time in days over which position is assessed for speed estimates (`1`)\n", "* max_win_period: maximum period of time in days over which position is assessed for speed estimates (`1`)" ] }, { "cell_type": "code", "execution_count": null, "id": "b5a83d98-f81c-4bf9-bc06-77c7406e6454", "metadata": {}, "outputs": [], "source": [ "qc_aground = do_aground_check(\n", " lat=data.lat,\n", " lon=data.lon,\n", " date=data.date,\n", " smooth_win=3,\n", " min_win_period=1,\n", " max_win_period=1,\n", ")\n", "pd.DataFrame({\"date\": data.date, \"lat\": data.lat, \"lon\": data.lon, \"qc_aground\": qc_aground})" ] }, { "cell_type": "markdown", "id": "1626c64b-833b-49e9-aca0-ea64e1347c56", "metadata": {}, "source": [ "For the last four time steps the buoy is not moving anymore. Hence, the aground check fails as expected." ] }, { "cell_type": "code", "execution_count": null, "id": "b41b69e7-3636-416b-8e1e-5e026e625841", "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.13.2" } }, "nbformat": 4, "nbformat_minor": 5 }