\n",
+ " \n",
+ "\n",
+ " \n",
+ "##### What works in Jupyter Book and locally but size not adjustable\n",
+ "\n",
+ "\n",
+ "!-alt-link construct.\n",
+ "\n",
+ "\n",
+ "![alt](../img/revelle.jpg)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3f8a9fdc-399a-427b-993e-3de931bc1b59",
+ "metadata": {},
+ "source": [
+ "#### Local animation file (mp4) playback"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "0ed45d5d-0d15-4894-bc0b-2629205c0cde",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from IPython.display import HTML, Video\n",
+ "Video('./../img/multisensor_animation.mp4', embed=True, width = 500, height = 500)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dae0986e-9d6e-4318-8a18-4af07baa9158",
+ "metadata": {},
+ "source": [
+ "#### Audio file (mp3) playback\n",
+ "\n",
+ "\n",
+ "```\n",
+ "from IPython.display import Audio\n",
+ "Audio(\".mp3\")\n",
+ "```\n",
+ "\n",
+ " \n",
+ "#### YouTube video playback\n",
+ "\n",
+ "\n",
+ "```\n",
+ "from IPython.display import YouTubeVideo\n",
+ "YouTubeVideo('sjfsUzECqK0')\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "id": "15435601-0c8b-4fdc-a4d3-1f26f3a10ab1",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/jpeg": "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\n",
+ "text/html": [
+ "\n",
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from IPython.display import YouTubeVideo\n",
+ "YouTubeVideo('sjfsUzECqK0')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0d259407-4353-41d6-98c1-6a5b82c30fca",
+ "metadata": {},
+ "source": [
+ "### Shell integration\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "### Working with NetCDF XArray and Pandas\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "#### Summary\n",
+ "\n",
+ "\n",
+ "For a general take on data manipulation, particularly with `pandas`: \n",
+ "See Jake VanDerplas' excellent book Python Data Science Handbook. \n",
+ "\n",
+ "\n",
+ "We have here multi-dimensional oceanography datasets in\n",
+ "NetCDF and CSV format. Corresponding Python libraries are `XArray` and `pandas`.\n",
+ "On import these libraries are abbreviated `xr` and `pd` respectively.\n",
+ "\n",
+ "\n",
+ "The XArray method `.open_dataset('somefile.nc')` returns an XArray Dataset:\n",
+ "A set of XArray DataArrays. The Dataset includes four (or more*) sections: `Dimensions`, \n",
+ "`Coordinates`, `Data Variables`, and `Attributes`. To examine a Dataset\n",
+ "called `A`: Run `A` (i.e. on a line by itself) to see these constituent sections. \n",
+ "\n",
+ "\n",
+ "* \"more than four\": Discovered while looking at seismic (DAS) data: Some XArray\n",
+ "data may include yet another internal organizing structure.\n",
+ "\n",
+ "\n",
+ "In pandas the data structure is a DataFrame. Here these are used to manage \n",
+ "shallow profiler ascent/descent/rest metadata.\n",
+ "\n",
+ "\n",
+ "Common reductive steps once data are read include removing extraneous components from\n",
+ "a dataset, downsampling, removing NaN values, changing the primary `dimension`\n",
+ "from `obs` (for 'observation') to `time`, combining multiple data files into \n",
+ "a single dataset, saving modified datasets to new files, and creating charts. \n",
+ "\n",
+ "\n",
+ "Datasets that reside within this [GitHub repository](https://github.com/robfatland/ocean)\n",
+ "have to stay pretty small. Larger datasets are downloaded to an external folder. \n",
+ "See for example the use of `wget` in the **`Global Ocean`** notebook.\n",
+ "The following code shows reduction of a global ocean temperature data file to just\n",
+ "the data of interest (temperature as a 3-D scalar field). \n",
+ "\n",
+ "\n",
+ "```\n",
+ "# Reduce volume of an XArray Dataset with extraneous Data Variables:\n",
+ "T=xr.open_dataset('glodap_oxygen.nc')\n",
+ "T.nbytes\n",
+ "T=T[['temperature', 'Depth']]\n",
+ "T.nbytes\n",
+ "T.to_netcdf('temperature.nc') \n",
+ "```\n",
+ "\n",
+ "Data can be down-sampled for example by averaging multiple samples. A tradeoff in down-sampling \n",
+ "Regional Cabled Array shallow profiler data however is this: Data collected during profiler \n",
+ "ascent spans 200 meters of water column depth in one hour, or about 6 centimeters per sec. \n",
+ "A 'thin layer' of signal variation might be washed out by combining samples. \n",
+ "\n",
+ "\n",
+ "This repository does include a number of examples of down-sampling and otherwise selecting out\n",
+ "data subsets. \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "#### XArray Datasets and DataArrays\n",
+ "\n",
+ "\n",
+ "##### Summary\n",
+ "\n",
+ "There are a million little details about working with XArray Datasets, DataArrays, numpy arrays, pandas DataFrames,\n",
+ "pandas arrays... let's begin! The main idea is that a **DataArray** is an object containing, in the spirit of \n",
+ "the game, one sort of data; and a **Dataset** is a collection of associated **DataArray**s. \n",
+ "\n",
+ "\n",
+ "##### XArray ***Dataset*** basics\n",
+ "\n",
+ "**Datasets** abbreviated `ds` have components { dimensions, coordinates, data variables, \n",
+ "attributes }.\n",
+ "\n",
+ "\n",
+ "A **DataArray** relates to a **`name`**; needs elaboration. \n",
+ "\n",
+ "\n",
+ "```\n",
+ "ds.variables\n",
+ "\n",
+ "ds.data_vars # 'dict-like object'\n",
+ "\n",
+ "for dv in ds.data_vars: print(dv)\n",
+ " \n",
+ "choice = 2\n",
+ "this_data_var = list(ds.data_vars)[choice]\n",
+ "print(this_data_var)\n",
+ "\n",
+ "ds.coords\n",
+ "ds.dims\n",
+ "ds.attrs\n",
+ "```\n",
+ "\n",
+ "\n",
+ "\n",
+ "##### Load via `open_mfdataset()` with dimension swap from `obs` to `time`\n",
+ "\n",
+ "\n",
+ "A single NetCDF (`.nc`) file can be opened as an XArray Dataset using `xr.open_dataset(fnm)`. \n",
+ "Multiple files can be opened as a single XArray Dataset via `xr.open_mfdataset(fnm*.nc)`. \n",
+ "`mf` stands for `multi-file`. Note \n",
+ "the wildcard `fnm*` is supported. \n",
+ "\n",
+ "```\n",
+ "def my_preprocessor(fds): return fds.swap_dims({'obs':'time'})\n",
+ "\n",
+ "ds = xr.open_mfdataset('files*.nc', \\\n",
+ " preprocess = my_preprocessor, \\\n",
+ " concat_dim='time', combine='by_coords')\n",
+ "```\n",
+ "\n",
+ "##### Obstacle: Getting information out of a Dataset\n",
+ "\n",
+ "\n",
+ "There is a sort of comprehension / approach that I have found hard to internalize.\n",
+ "With numpy ndarrays, XArray Datasets, etcetera there is this \"how do I get at it?\"\n",
+ "problem. As this documentation evolves I will try and articulate the most helpful\n",
+ "mindset. The starting point is that Datasets are built as collections of DataArrays; \n",
+ "and these have an indexing protocol the merges with a method protocol (`sel`, `merge`\n",
+ "and so on) where the end-result code that does what I want is inevitably very \n",
+ "elegant. So it is a process of learning that elegant sub-language...\n",
+ "\n",
+ "\n",
+ "##### Synthesizing along the dimension via `.concat`\n",
+ "\n",
+ "\n",
+ "```\n",
+ "ds_concat = xr.concat([ds.sel(time=\n",
+ "```\n",
+ "\n",
+ "\n",
+ "##### Recover a time value as `datetime64` from a Dataset by index\n",
+ "\n",
+ "\n",
+ "If `time` is a `dimension` it can be referenced via `ds.time[i]`. However\n",
+ "this will be a 1-Dimensional, 1-element DataArray. Adding `.data`\n",
+ "and casting the resulting ndarray (with one element) as a `dt64` works.\n",
+ "\n",
+ "```dt64(ds.time[i].data)```\n",
+ "\n",
+ "\n",
+ "##### Example: XArray transformation flow\n",
+ " \n",
+ " \n",
+ "As an example of the challenge of learning `XArray`: The reduction of this data to binned profiles\n",
+ "requires a non-trivial workflow. A naive approach can result in a calculation that should take \n",
+ "a seconds run for hours. (A key idea of this workflow -- the sortby() step -- is found on page 137 of **PDSH**.)\n",
+ " \n",
+ " \n",
+ "- `swap_dims()` to substitute `pressure` for `time` as the ordinate dimension\n",
+ "- `sortby()` to make the `pressure` dimension monotonic\n",
+ "- Create a pressure-bin array to guide the subsequent data reduction\n",
+ "- `groupby_bins()` together with `mean()` to reduce the data to a 0.25 meter quantized profile\n",
+ "- use `transpose()` to re-order wavelength and pressure, making the resulting `DataArray` simpler to plot\n",
+ "- accumulate these results by day as a list of `DataArrays`\n",
+ "- From this list create an `XArray Dataset`\n",
+ "- Write this to a new NetCDF file\n",
+ "\n",
+ "\n",
+ "##### needs sorting\n",
+ "\n",
+ "- Copy: `dsc = ds.copy()`\n",
+ "- Coordinate to data variable: `ds = ds.reset_coords('seawater_pressure')`\n",
+ "\n",
+ "##### Example: XArray Dataset subset and chart\n",
+ "\n",
+ "\n",
+ "Time dimension slice:\n",
+ "\n",
+ "```\n",
+ "ds = xr.open_dataset(\"file.nc\")\n",
+ "ds = ds.sel(time=slice(t0, t1))\n",
+ "ds\n",
+ "```\n",
+ "\n",
+ "This shows that the temperature Data Variable has a cumbersome name: \n",
+ "`sea_water_temperature_profiler_depth_enabled`. \n",
+ "\n",
+ "```\n",
+ "ds = ds.rename({'sea_water_temperature_profiler_depth_enabled':'temperature'})\n",
+ "```\n",
+ "\n",
+ "Plot this against the default dimension `time`:\n",
+ "\n",
+ "```\n",
+ "ds.temperature.plot()\n",
+ "```\n",
+ "\n",
+ "Temperature versus depth rather than time:\n",
+ "\n",
+ "```\n",
+ "fig, axs = plt.subplots(figsize=(12,4), tight_layout=True)\n",
+ "axs.plot(ds.temperature, -ds.z, marker='.', markersize=9., color='k', markerfacecolor='r')\n",
+ "axs.set(ylim = (200., 0.), title='temperature against depth')\n",
+ "```\n",
+ "\n",
+ "Here `ds.z` is negated to indicate depth below ocean surface.\n",
+ "\n",
+ "\n",
+ "### More cleanup of Datasets: rename() and drop()\n",
+ "\n",
+ "\n",
+ "* Use `ds = ds.rename(dictionary-of-from-to)` to rename data variables in a Dataset\n",
+ "* Use `ds = ds.drop(string-name-of-data-var)` to get rid of a data variable\n",
+ "* Use `ds = ds[[var1, var2]]` to eliminate all but those two variables\n",
+ "\n",
+ "\n",
+ "##### XArray ***DataArray*** name and length\n",
+ "\n",
+ "\n",
+ "```\n",
+ "sensor_t.name\n",
+ "\n",
+ "len(sensor_t)\n",
+ "len(sensor_t.time) # gives same result\n",
+ "```\n",
+ "\n",
+ "What is the name of the controlling dimension?\n",
+ "\n",
+ "```\n",
+ "if sensor_t.dims[0] == 'time': print('time is dimension zero')\n",
+ "```\n",
+ "\n",
+ "Equivalent; but the second version permits reference by \"discoverable\" string.\n",
+ "\n",
+ "\n",
+ "```\n",
+ "sensor_t = ds_CTD_time_slice.seawater_temperature\n",
+ "sensor_t = ds_CTD_time_slice['seawater_temperature']\n",
+ "```\n",
+ "\n",
+ "###### Plotting with scaling and offsetting\n",
+ "\n",
+ "Suppose I wish to shift some data left to contrast it with some other data (where they would clobber one another)...\n",
+ "\n",
+ "```\n",
+ "sensor_t + 0.4\n",
+ "```\n",
+ "\n",
+ "Suppose I wish to scale some data in a chart to make it easier to interpret given a fixed axis range\n",
+ "\n",
+ "```\n",
+ "sensor_t * 10. # this fails by trying to make ten copies of the array\n",
+ "\n",
+ "np.ones(71)*3.*smooth_t # this works by creating an inner product\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "797918c8-9b2c-4de9-aa5a-f1b5ad1b55bb",
+ "metadata": {},
+ "source": [
+ "### Time\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Missing data\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "### Resampling\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "#### Filtering with xrscipy\n",
+ "\n",
+ "\n",
+ "Some shallow profiler signals (particularly current) are noisy even at\n",
+ "1Min resolution. This suggests a low-pass filter. `xr-scipy` is a thin wrapper \n",
+ "of scipy for xarray eco-system. It includes digital filter machinery.\n",
+ "\n",
+ "\n",
+ "- [main site](https://xr-scipy.readthedocs.io/en/latest/index.html)\n",
+ "- [geo applications site](https://scrapbox.io/pycoaj/xr-scipy)\n",
+ "\n",
+ "\n",
+ "```\n",
+ "import xrscipy.other.signal as dsp\n",
+ "t = np.linspace(0, 1, 1000) # seconds\n",
+ "sig = xr.DataArray(np.sin(16*t) + np.random.normal(0, 0.1, t.size),\n",
+ " coords=[('time', t)], name='signal')\n",
+ "sig.plot(label='noisy')\n",
+ "low = dsp.lowpass(sig, 20, order=8) # cutoff at 20 Hz\n",
+ "low.plot(label='lowpass', linewidth=5)\n",
+ "plt.legend()\n",
+ "plt.show()\n",
+ "```\n",
+ "\n",
+ "(package not installed yet)\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Mapping\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "- cf PyGMT\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Visualization\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "#### Overview\n",
+ "\n",
+ "\n",
+ "There are two Python plotting libraries: **`matplotlib`** and **`plotly`**. \n",
+ "**`plotly`** is more advanced and interactive. \n",
+ "[This link provides more background on it](https://plotly.com/python/) \n",
+ "including a gallery of examples of what is possible.\n",
+ "\n",
+ "\n",
+ "Turning to Matplotlib: This library includes the **`.pyplot`** sub-library, \n",
+ "a MATLAB-parity API. It is the `pyplot` sub-library that is \n",
+ "most commonly put to use building charts; and to make matters more confusing it is \n",
+ "habitually imported as `plt`, hence the ubiquitous import line: \n",
+ "`import matplotlib.pyplot as plt`. With the API now abbreviated as `plt` we \n",
+ "proceed to generating data charts. \n",
+ "\n",
+ "\n",
+ "To make things further complicated: Herein we often generate a grid of charts\n",
+ "for comparison using the `subplots` API call. As an example: \n",
+ "\n",
+ "\n",
+ "```\n",
+ "fig,ax=plt.subplots(3,3,figsize=(12,12))\n",
+ "```\n",
+ "\n",
+ "\n",
+ "- What is `fig`? A figure (???)\n",
+ "- What is `ax`? An array of artists (???)\n",
+ "\n",
+ "\n",
+ "\n",
+ "The main agenda of this repository can be summarized as: \n",
+ "\n",
+ "\n",
+ "- reduce a dataset to just some data of interest\n",
+ "- obtain metadata (profile timestamps for example)\n",
+ "- produce charts to visualize this data by means of `.scatter` and `.plot` directives\n",
+ "- proceed to various forms of data analysis\n",
+ "\n",
+ "\n",
+ "> There is a utility `.plot()` method built into XArray Datasets for a quick view of \n",
+ "a particular data variable along the `dimension` axis.\n",
+ "\n",
+ "\n",
+ "\n",
+ "> Needed: Detail on how to do formatting, example arguments:\n",
+ "> `vmin=4.,vmax=22.,xincrease=False`\n",
+ "\n",
+ "\n",
+ "> PDSH recommends the Seaborn library as a chart-building alternative with cleaner graphics.\n",
+ "\n",
+ "\n",
+ "#### Matplotlib\n",
+ "\n",
+ "\n",
+ "Big topic: Building charts using the matplotlib library. Here's one to begin with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "id": "3c8bc626-1060-4b37-aa5c-5be6846e15c3",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAGGCAYAAABFWQz6AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABL3UlEQVR4nO3dd1gU59oG8HsLLEtVEBEEBRR7l2NBDFYEFbtLMFFBTTSKkWiiIcVu0KMxJLEk9uQgZVWM2LAExBK7gjV2BUUlFoqIS9nn+8MwHyugDLDMLry/65or2ZnZmWdw753ZKe8rIiICwzB6SSx0AQzDlB8LMMPoMRZghtFjLMAMo8dYgBlGj7EAM4weYwFmGD3GAswweowFmGH0GAtwDRYeHo7Q0NASp4lEIsydO1dj3J9//glXV1eYmJhAJBLhjz/+wKZNmyASiXD37l1e6547dy5EIlG56q7IewHA398fjo6O5XrvqlWrsGnTpnKvu7JJhS6AEU54eDguXbqEoKCgYtOOHz8Oe3t77jURQaFQoEmTJoiJiYGJiQmaNm2K/Px8HD9+HLa2trzWPWHCBHh5eVV0E6rcqlWrUKdOHfj7+wtdCgAW4Brp5cuXMDY2fus8Xbp00XidmpqKZ8+eYejQoejdu7fGNGtra9412Nvba3xBMOVTow6h//nnH3z88cdwcHCATCaDtbU1unXrhoMHD2rMt2HDBrRt2xZGRkawtLTE0KFDcfXqVY15/P39YWpqir///hv9+vWDiYkJbG1tsXjxYgDAiRMn4O7uDhMTEzRp0gS//fZbsXoePXqEiRMnwt7eHoaGhnBycsK8efOQn5//zm2JioqCp6cnbG1tIZfL0bx5c3z55ZfIzs4usc6LFy/C09MTZmZm6N27N3r06IHdu3fj3r17EIlE3FCo6CH03LlzubDNmjULIpGIOwQt7RA6NjYWvXv3hoWFBYyNjdG8eXOEhIRw00s6DC7rNvGxadMmNG3aFDKZDM2bN8fvv/9e4nzz5s1D586dYWlpCXNzc3To0AHr169H0Wd9HB0dcfnyZSQkJHB/r8K/w6tXrzBjxgy0a9cOFhYWsLS0RNeuXbFjx45y114WNWoPPHr0aJw7dw6LFi1CkyZNkJ6ejnPnzuHp06fcPCEhIfjqq6/g5+eHkJAQPH36FHPnzkXXrl1x+vRpuLi4cPPm5eVh2LBhmDRpEr744guEh4cjODgYmZmZ2LZtG2bNmgV7e3v8/PPP8Pf3R6tWrdCxY0cAr8PbqVMniMVizJ49G40aNcLx48excOFC3L17Fxs3bnzrtty4cQP9+/dHUFAQTExM8Pfff2PJkiU4deoU4uLiNObNzc3FoEGDMHHiRHz55ZfIz8+Hvb09Pv74Y9y6dQvbt29/67omTJiAtm3bYtiwYZg6dSpGjRoFmUxW6vzr16/HRx99BA8PD/zyyy+oW7curl+/jkuXLlXaNpXFpk2bEBAQgMGDB+P7779HRkYG5s6dC5VKBbFYc9919+5dTJw4EQ0aNADw+gt46tSpePDgAWbPng0A2L59O0aMGAELCwusWrUKALi/g0qlwrNnz/D555+jfv36yM3NxcGDBzFs2DBs3LgRY8aM4V1/mVANYmpqSkFBQaVOf/78Ocnlcurfv7/G+OTkZJLJZDRq1Chu3NixYwkAbdu2jRuXl5dH1tbWBIDOnTvHjX/69ClJJBKaPn06N27ixIlkampK9+7d01jXsmXLCABdvny5zNulVqspLy+PEhISCAAlJSUVq3PDhg3F3jdgwABq2LBhicsEQHPmzOFe37lzhwDQ0qVLNebbuHEjAaA7d+4QEVFWVhaZm5uTu7s7qdXqUmueM2cOve3j97Ztetd7iYgKCgrIzs6OOnTooFHH3bt3ycDAoNTtLnxvXl4ezZ8/n6ysrDTe37JlS/Lw8HjruomI8vPzKS8vj8aPH0/t27d/5/zlVaMOoTt16oRNmzZh4cKFOHHiBPLy8jSmHz9+HDk5OcVOUDg4OKBXr174888/NcaLRCL079+fey2VStG4cWPY2tqiffv23HhLS0vUrVsX9+7d48bt2rULPXv2hJ2dHfLz87nB29sbAJCQkPDWbbl9+zZGjRqFevXqQSKRwMDAAB4eHgBQ7HAfAIYPH/7W5VWWv/76C5mZmZg8eTLvM8V8t+ltrl27htTUVIwaNUqjjoYNG8LNza3Y/HFxcejTpw8sLCy4dc+ePRtPnz5FWlpamda5ZcsWdOvWDaamppBKpTAwMMD69et5185HjQpwVFQUxo4di3Xr1qFr166wtLTEmDFj8OjRIwDgDqVLOqNqZ2encagNAMbGxjAyMtIYZ2hoCEtLy2LvNzQ0xKtXr7jXjx8/xs6dO2FgYKAxtGzZEgDw5MmTUrfjxYsX6N69O06ePImFCxfi0KFDOH36NKKjowEAOTk5xeo0NzcvdXmV6Z9//gEA3ieo+G7TuxT+W9WrV6/YtDfHnTp1Cp6engCAtWvX4tixYzh9+jS+/vrrMq87OjoaCoUC9evXR1hYGI4fP47Tp09j3LhxGv/ula1G/QauU6cOQkNDERoaiuTkZMTExODLL79EWloaYmNjYWVlBQB4+PBhsfempqaiTp06lVpLmzZtsGjRohKn29nZlfreuLg4pKam4tChQ9weCgDS09NLnL8i10z5Kjwjff/+fV7v47tN71L4b1n45VzUm+MiIyNhYGCAXbt2aXwh//HHH2VeX1hYGJycnBAVFaXx91apVDwr56dG7YGLatCgAQIDA9G3b1+cO3cOANC1a1fI5XKEhYVpzHv//n3ExcUVu3xSEQMHDsSlS5fQqFEjuLq6FhveFuDCD8ibJ5J+/fVXXjXIZDLee7Z3cXNzg4WFBX755ReNM7jvUlnbVKhp06awtbVFRESERh337t3DX3/9VWzdUqkUEomEG5eTk4P//e9/xZZb2t9MJBLB0NBQI7yPHj3S+lnoGhPgjIwMdOjQAcuWLcOuXbuQkJCAZcuWITY2Fn379gUA1KpVC99++y1iYmIwZswY7N27F2FhYejZsyeMjIwwZ86cSqtn/vz5MDAwgJubG1avXo24uDjs2bMHq1atwsCBA9+6B3Nzc0Pt2rUxadIkbN++Hbt27YKfnx+SkpJ41dC6dWukpaVh9erVOHXqFM6cOVPRzYKpqSm+//57HD58GH369EFkZCTi4+Oxdu1aBAYGlvq+ytqmQmKxGAsWLMDZs2cxdOhQ7N69G5s3b0afPn2KHUIPGDAAL168wKhRo3DgwAFERkaie/fuJZ5pb926NZKSkhAVFYXTp0/j4sWLAF5/IV+7dg2TJ09GXFwcfvvtN7i7u/O+wYU3rZ0e0zGvXr2iSZMmUZs2bcjc3Jzkcjk1bdqU5syZQ9nZ2Rrzrlu3jtq0aUOGhoZkYWFBgwcPLnZWeOzYsWRiYlJsPR4eHtSyZcti4xs2bEgDBgzQGPfPP//Qp59+Sk5OTmRgYECWlpbUsWNH+vrrr+nFixdv3Z6//vqLunbtSsbGxmRtbU0TJkygc+fOEQDauHHjO+skInr27BmNGDGCatWqRSKRSOPMLsp5FrrQnj17yMPDg0xMTMjY2JhatGhBS5Ys4aaXdCa5rNtUlrPQhdatW0cuLi5kaGhITZo0oQ0bNtDYsWOLnYXesGEDNW3alGQyGTk7O1NISAitX7++2LbdvXuXPD09yczMjABoLGfx4sXk6OhIMpmMmjdvTmvXruVVa3mIiFirlAyjr2rMITTDVEcswAyjx1iAGUaPsQAzjB5jAWYYPcYCzDB6TK9vpVSr1UhNTYWZmVmV3i7IMNpERMjKyoKdnV2xxx7fpNcBTk1NhYODg9BlMIxWpKSkvPOhEL0OsJmZGYDXG1pVT9swjLZlZmbCwcGB+3y/jV4HuPCw2dzcnAWYqXbK8rOQncRiGD3GAswweowFmGH0GAsww+gxFmCG0WMswDpMrVYjOzsbarVa6FIYHcUCrIOSkpIQEBAAU2NjmJqawtTYGAEBAeVuXoapvgQNcH5+Pr755hs4OTlBLpfD2dkZ8+fPr9F7nIiICLh27Ij4sDAEq1TYDCBYpUJ8WBhcO3ZERESE0CUyukRrjfWUwcKFC8nKyop27dpFd+7coS1btpCpqSmFhoaW6f0ZGRkEgDIyMrRcadVITEwkqURCYwDKBYiKDLkAjQFIKpFQYmKi0KUyWsTncy3oHvj48eMYPHgwBgwYAEdHR4wYMQKenp6V0jqiPgoNDUV9kQjrABi8Mc0AwDoAdiIRfiylT1+m5hE0wO7u7vjzzz9x/fp1AK9/+x09elSju5KiVCoVMjMzNYbqQq1WIyoiAuPz84uFt5ABgAn5+Yh8o61jpuYS9F7oWbNmISMjA82aNYNEIkFBQQEWLVoEPz+/EucPCQnBvHnzqrjKqpGTk4MclQqN3jGfM4AclQo5OTnv7OOXqf4E3QNHRUUhLCwM4eHhOHfuHH777TcsW7asxL50ASA4OBgZGRnckJKSUsUVa49cLodcJsOtd8x3G4BcJoNcLq+Kshhdp/2f5KWzt7enFStWaIxbsGABNW3atEzvr24nscaMGUP2IlGxE1hFT2Q1kEopwN9f6FIZLdKbk1gvX74s1uKARCKpkZeRiAjPnj3DAyKMB5D3xvQ8AOMBpBJhWlBQldfH6CZBfwP7+Phg0aJFaNCgAVq2bInz589j+fLlGDdunJBlCeKbb77Brl27IBKJEC4SIUEsxoT8fDjj9WHzOqkUqUT4/X//Q9u2bYUul9EV2j8gKF1mZiZNmzaNGjRoQEZGRuTs7Exff/01qVSqMr2/uhxCr1y5kgAQAFq/fj0lJiZSgL8/yWUyAkBymYwC/P3Z9d8ags/nWq/7RsrMzISFhQUyMjL0tkWO7du3Y/jw4SAizJ8/H99++y03Ta1Wc2ebWaN9NQefz7VeN6mj744dO4ZRo0aBiPDxxx/jm2++0ZguFothYmIiUHWMPmAPMwjk6tWr8PHxwatXr+Dj44OVK1eyvSzDGwuwAB4+fAhvb288f/4cnTt3RmRkJKRSdjDE8McCXMUyMzPRv39/3Lt3Dy4uLti5cye7o4opNxbgKpSbm4vhw4cjMTERdevWRWxsLKytrYUui9FjLMBVRK1WY9y4cTh48CBMTEywZ88eODs7C10Wo+dYgKvIV199hc2bN0MqlWLr1q3o2LGj0CUx1QALcBX4+eefsWTJEgDAunXr4OXlJXBFTHXBAqxl27Ztw7Rp0wAAixYtwtixYwWuiKlOWIC16MiRI/jggw9ARPjkk08QHBwsdElMNcMCrCVXrlzBoEGDoFKpMGTIEPz888/sRg2m0rEAa8GDBw/g5eWF9PR0uLm5ITw8HBKJROiymGqIBbiSZWRkwNvbGykpKWjatCliYmJY6xmM1rAAVyKVSoWhQ4fi4sWLqFevHmJjY2FlZSV0WUw1xgJcSdRqNQICAhAfHw9TU1Ps2bMHjo6OQpfFVHMswJVk1qxZiIiIgFQqRXR0NNq3by90SUwNwAJcCUJDQ7Fs2TIAwIYNG9C3b1+BK2JqChbgClIqlZg+fToAYPHixRg9erTAFTE1CQtwBSQkJGD06NEgIgQGBmLmzJlCl8TUMCzA5XTp0iUMHjwYubm5GDZsGEJDQ9mNGkyVYwEuh5SUFHh5eSEjIwPu7u4ICwtjN2owgmAB5ik9PR3e3t548OABmjdvjh07drAbNRjBsADzUHhf8+XLl2FnZ4fY2FhYWloKXRZTg7EAl5FarcaYMWOQkJAAMzMz7NmzBw0aNBC6LKaGYwEuo88//xxKpRIGBgbYvn07696E0QkswGWwfPly/PDDDwCATZs2oXfv3gJXxDCvsQC/Q2RkJGbMmAEAWLp0KUaNGiVwRQzz/1iAi1Cr1cjOzua6N42Li8OYMWMAANOmTeOCzDC6ggUYQFJSEgICAmBqbAxTU1OYGhtj8ODBGDRoEPLy8jBy5EgsX76c3ajB6BxBA+zo6AiRSFRsmDJlSpXVEBERAdeOHREfFoZglQqbAQSrVDgbE4OX2dlo1qwZfv/992IdkTOMLhC0Q57Tp0+joKCAe33p0iX07dsXI0eOrJL1JyUlYczo0RhVUIB1AAyKTPsSwHgAETdu4Nq1a+ysM6OTBN2tWFtbo169etywa9cuNGrUCB4eHlWy/tDQUNQXiYqFF/++Xg/ATiTCj6GhVVIPw/ClM8eFubm5CAsLw7hx40r9ralSqZCZmakxlJdarUZURATG5+cXC28hAwAT8vMRGREBPe4HnanGdCbAf/zxB9LT0+Hv71/qPCEhIbCwsOAGBweHcq8vJycHOSoVGr1jPmcAOSoVcnJyyr0uhtEWnQnw+vXr4e3tDTs7u1LnCQ4ORkZGBjekpKSUe31yuRxymQy33jHfbQBymYw9sMDoJJ0I8L1793Dw4EFMmDDhrfPJZDKYm5trDOUlFovh6+eH9VIp8kqZJw/AOqkU7/v5sUtIjE7SiQBv3LgRdevWxYABA6p0vUFBQXhAhAlAsRDn4fVZ6FQiTAsKqtK6GKasBA+wWq3Gxo0bMXbsWEilVXtVq23btvj9f/9DuFgMRwALAGz+97+NpVJESCT4/X//Y5eQGN1FAtu3bx8BoGvXrvF+b0ZGBgGgjIyMCtUwZcoUAkBSsZgAkFwmowB/f0pMTKzQchmmPPh8rgW9kQMAPD09Bb9Ec/bsWQDAzytXYvTo0TA2Nma/eRm9IHiAhZaWloaTJ08CAHx8fGBiYiJwRQxTdoL/Bhba3r17QURo37496tevL3Q5DMNLjQ/wrl27AAADBw4UuBKG4a9GBzg3Nxf79u0DwALM6KcaHeAjR44gKysLdevWhaurq9DlMAxvNTrAhYfPAwYMYM/7MnqpRn9qd+/eDYAdPjP6q8YG+Pr167hx4wYMDAxYd6CM3qqxAS48fO7RowfMzMwEroZhyqfGB5gdPjP6rEYGOD09HUeOHAGAKn8CimEqU40M8P79+5Gfn4/mzZujUaN3tcnBMLqrRgaYHT4z1UWNC3BBQQH27NkDgAWY0X81LsAnT57E06dPUatWLbi5uQldDsNUSI0LcOHhs7e3d5W3AMIwla3GBpgdPjPVQY0K8L1793Dx4kWIxWJ4eXkJXQ7DVFiNCnDhvc9ubm6wtLQUuBqGqbgaFWB2+MxUNzUmwNnZ2YiLiwPAAsxUHzUmwH/++SdUKhUcHR3RokULocthmEpRYwJc9PCZNRnLVBe8ApyXl4eePXvi+vXr2qpHK4iIPbzPVEu8AmxgYIBLly7p3R4sMTERqampMDExqbLOwxmmKvA+hB4zZgzWr1+vjVq0pvDwuW/fvjAyMhK4GoapPLzvJczNzcW6detw4MABuLq6FuvJYPny5ZVWXGVhl4+Y6op3gC9duoQOHToAQLHfwrp2aK1Wq3H37l2cOnUKANC/f3+BK2KYysU7wPHx8ZVawIMHDzBr1izs3bsXOTk5aNKkCdavX4+OHTuWe5lJSUkIDQ1FVEQEclQqiAHUtrJCWloabG1tK694hhFYuS8j3bx5E/v27UNOTg4AlKuHwefPn6Nbt24wMDDA3r17ceXKFXz//feoVatWectCREQEXDt2RHxYGIJVKmwGMBeA8fPncO3YEREREeVeNsPoHL59lz558oR69epFIpGIxGIx3bp1i4iIxo0bR9OnT+e1rFmzZpG7uzvfEjhv9qOamJhIUomExgCUCxAVGXIBGgOQVCJh/f4yOo1P/8C898CfffYZDAwMkJycDGNjY268r68vYmNjeS0rJiYGrq6uGDlyJOrWrYv27dtj7dq1fEvihIaGor5IhHUADN6YZgBgHQA7kQg/hoaWex0Mo1P4fjvY2NhwezBTU1NuD3z79m0yMTHhtSyZTEYymYyCg4Pp3Llz9Msvv5CRkRH99ttvJc7/6tUrysjI4IaUlBTum6qgoIDkMhnNf2PP++YwHyC5TEZqtZrvpjNMldDqHjg7O1tjz1voyZMnkMlkvJalVqvRoUMHfPfdd2jfvj0mTpyIjz76CKtXry5x/pCQEFhYWHCDg4MDNy0nJwc5KhXe1cakM4AclQpHjhwp1+92htElvAP83nvv4ffff+dei0QiqNVqLF26FD179uS1LFtb22IPFjRv3hzJycklzh8cHIyMjAxuSElJ4abJ5XLIZTLcesc6b+P1Rnt4eMDZ2RmzZs3C2bNnWZgZ/cR393758mWytrYmLy8vMjQ0pBEjRlDz5s3JxsaGbt68yWtZfn5+xU5iBQUFUdeuXcv0/jcPNfz9/amhVFrsBFbRE1kOEgk5OTmRiYkJAeCGRo0aUXBwMJ0/f54dXjOC4nMIzTvAREQPHz6k2bNn04ABA8jb25u+/vprSk1N5b2cU6dOkVQqpUWLFtGNGzdo8+bNZGxsTGFhYWV6P9+z0KOLnIXOzs6mrVu30siRI0kul2uE2cXFhb7++mu6cOECCzNT5bQe4Mq0c+dOatWqFclkMmrWrBmtWbOmzO8taUPDw8NJKpFQA6mU5gMU9u+JqwZSKUklEgoPDy+2nBcvXlBUVBQNHz6cjIyMNMLcrFkzmj17Nl2+fLlStpdh3oVPgEVE/H/8PX/+HOvXr8fVq1chEonQvHlzBAQEVHk7U5mZmbCwsEBGRgbMzc258UlJSfgxNBSR/96JJZfJ8L6fH6YFBaFt27ZvXWZWVhZ27doFpVKJvXv3QqVScdNatmwJhUIBhUKBZs2aaW27mJqttM91SXgHOCEhAYMHD4a5uTlcXV0BAGfPnkV6ejpiYmKq9HG9d22oWq1GTk4OjI2Ny3WfdmZmJmJiYqBUKhEbG4u8vDxuWps2bbgwu7i4VGg7GKYorQa4VatWcHNzw+rVqyGRSAC87q5k8uTJOHbsGC5dulT+ynnis6EVlZ6ejh07dkCpVHKdoxVq164dfH19MXLkSNZZGlNhWg2wXC5HYmIimjZtqjH+2rVraNeuHXdvdFWoygAX9ezZM+zYsQNRUVE4ePAgCgoKuGkdO3bkwuzo6FhlNTHVB5/PNe/rwB06dMDVq1eLjb969SratWvHd3F6ydLSEgEBAYiNjcWjR4+wdu1a9OnTB2KxGGfPnsXMmTPh5OSEzp074/vvvy/1ujbDVFSZ9sAXLlzg/v/q1auYOXMmpk6dii5dugAATpw4gZUrV2Lx4sXw9fXVXrVvEGoPXJp//vkH0dHRUCqVOHToENRqNTeta9euUCgUGDFiBOzt7QWsktF1lX4ILRaLIRKJ3nm3kkgk0jic1DZdC3BRjx8/xrZt26BUKnH48GGNv123bt3g6+uL4cOHw87OTsAqGV1U6QG+d+9emVfesGHDMs9bUboc4KIePnyIrVu3QqlU4ujRo9x4kUiE7t27c2G2sbERsEpGV2j1JJYu0ZcAF3X//n1s27YNUVFROH78ODdeLBbDw8MDCoUCw4cPh7W1tYBVMkLSeoAfPHiAY8eOIS0tTeN3HgB8+umnfBdXbvoY4KKSk5O5PfPJkye58WKxGL169YJCocDQoUNRp04dAatkqppWA7xx40ZMmjQJhoaGsLKy0rhBQiQS4fbt2+Wruhz0PcBF3b17F1u2bIFSqcSZM2e48RKJBH369IFCocCQIUNYr4o1gFYD7ODggEmTJiE4OBhisbA9s1SnABd169YtLsznz5/nxkulUnh6ekKhUGDw4MEVajuM0V1aDbCVlRVOnTqlE3ccVdcAF3Xjxg0olUoolUqNy3kGBgbo168fF+bquv01kVYDPHPmTFhaWuLLL7+sUJGVoSYEuKi///4bW7ZsQVRUFC5fvsyNl8lk8PLygkKhgI+PD8zMzASskqkorQa4oKAAAwcORE5ODlq3bg0DA83m46qyZ4aaFuCiLl++zIX577//5sYbGRmhf//+UCgUGDhwYLGeMxjdp9UAL1iwAHPmzEHTpk1hY2NT7CRWYSfaVaEmB7gQEeHSpUtQKpWIiorCjRs3uGlyuRwDBw6EQqFA//79S2zLjNE9Wg1w7dq18cMPP8Df378iNVYKFmBNRISkpCQuzEWvCBgbG8PHxwe+vr7w8vKCXC4XsFLmbbQa4Hr16uHIkSM68QwsC3DpiAjnz59HVFQUlEol7t69y00zNTXFoEGD4OvrC09PT9Zjo47RaoBDQkLw8OFD/PTTTxUqsjKwAJcNEeHMmTPc2eyiT0eZm5tj8ODBUCgU8PT0hKGhoYCVMoCWAzx06FDExcXBysoKLVu2LHYSKzo6mn/F5cQCzB8R4eTJk1yYHzx4wE2zsLDA0KFDoVAo0Lt3bxZmgWg1wAEBAW+dvnHjRj6LqxAW4IpRq9U4fvw4lEoltmzZgocPH3LTateujWHDhkGhUKBnz57FvqgZ7WEPMzC8qdVqHD16FEqlElu3bsXjx4+5aVZWVhg2bBh8fX3h4eEBqZR3r7QMDyzATIUUFBTgyJEjiIqKwrZt2/DPP/9w06ytrTF8+HAoFAq89957XLtoTOXRaoCdnJze2sIje5ihesnPz0dCQgKioqIQHR2Np0+fctNsbGwwYsQIKBQKuLu7C35vfHWh1QD/+OOPGq/z8vJw/vx5xMbG4osvvqjSWyxZgKtWXl4e4uPjoVQqER0djefPn3PTbG1tMXLkSCgUCnTt2pWFuQIEOYReuXIlzpw5w05i1RC5ubn4888/oVQqsX37dmRkZHDT7O3tuTB37ty5XG1y12SCBPj27dto164dMjMzK2NxZcICrBtUKhUOHDgApVKJP/74A1lZWdy0Bg0aYOTIkfD19YWrqysLcxkIEuD//ve/WLVqlcYdP9rGAqx7Xr16hf379yMqKgoxMTF48eIFN83R0ZHrzaJDhw4szKXQaoDbt2+v8YcnIjx69Aj//PMPVq1ahY8//rh8VZcDC7Buy8nJQWxsLKKiorBz5068fPmSm9aoUSMuzG3btmVhLkKrAZ43b57Ga7FYDGtra/To0aPKO/xiAdYfL1++xJ49e6BUKrFr1y6NHjyaNGnChblVq1ZlCnNhv1dyubzanTDj9bl+Z/+FWjRnzhyNrjwBkI2NTZnfz6cbRkZ3ZGVlUWRkJA0bNox3d66JiYnk7+9PcpmMAJBcJiN/f39KTEys4q3QHq13L6pWq3Hz5s0SW6V87733yrycuXPnYuvWrTh48CA3TiKRlLlJVbYH1n9ZWVnYuXMn151rbm4uN62wO1dfX180bdoUERERGDN6NOqLRBifn49GAG4BWC+V4gERfv/f/+Dn5yfYtlQWre6Bjx8/Tk5OTiQWi0kkEmkMYrGY17LmzJlDbdu25VsCh+2Bq5f09HT6/fffaeDAgWRgYKCxZ3ZxcSGJSERjAMoFiIoMuQCNAUgqkVSLPTGfzzXvHw+TJk2Cq6srLl26hGfPnuH58+fc8OzZM97fNjdu3ICdnR2cnJzw/vvvV+mdXIxusbCwwOjRo7Fz506kpaVh06ZN8Pb2hlQqxY0bN1CPCOsAvPlYhQGAdQDsRCL8GBpa5XULifchtImJCZKSktC4ceMKr3zv3r14+fIlmjRpgsePH2PhwoX4+++/cfnyZVhZWRWbX6VSQaVSca8zMzPh4ODADqGruSdPnqB+vXr4pqAA375lvgUAQmQyZOfk6PVZba12L9q5c2fcvHmz3MUV5e3tjeHDh6N169bo06cPdu/eDQD47bffSpw/JCQEFhYW3ODg4FApdTC6KTk5GcuXL4eXlxdyCwrwroaMnQHkqFRV2ke10Hg/FzZ16lTMmDEDjx49KrFVyjZt2pS7GBMTE7Ru3VqjYbaigoODMX36dO514R6YqT7u37/PdTej0XcUXp+wepvbAOQyWY1q74t3gIcPHw4AGDduHDeusOvRinYvqlKpcPXqVXTv3r3E6TKZDDKZrNzLZ3RTWXpvPJyQgPXR0fgyP7/Yb2AAyAOwTirF+35+en34zBfvAN+5c6fSVv7555/Dx8cHDRo0QFpaGhYuXIjMzEyMHTu20tbB6Ca+/Sd369YNrtu2YQJQ7ERWHoDxAFKJMC0oqOo2Qhdo+Yz4W/n6+pKtrS0ZGBiQnZ0dDRs2rNQL+CVhl5H0S1paGv3yyy/Us2dPEovFGpeJunbtSj/88AOlpKSU+v7w8HCSSiTUQCql+QCFATQfoAZSKUklEgoPD6/CrdEePp9rQQNcUSzAuu/Jkye0Zs0a6tOnD0kkEo3QdurUiZYtW0Z3794t8/ISExMp4I07sQLYnVj6id2JpZueP3+O7du3Q6lU4uDBgxrnRTp27AiFQoGRI0fCycmp3OsovBfa2Ni42v3m5fO5Zq2TMZUiPT0dO3bsgFKpxIEDB5CXl8dNa9euHXx9fTFy5MhK69VSLBazfp/AAsxUQGZmJmJiYqBUKrFv3z6N+5jbtGnD7WmbNGkiYJXVW7kDnJubW+LDDA0aNKhwUYzuysrKwq5duxAVFYXY2FiNO+MKHz4YOXIkmjdvLmCVNQfvAN+4cQPjxo3DX3/9pTGeKuE6MKObsrOzsWvXLiiVSuzZswevXr3ipjVt2hS+vr5QKBRo2bKlgFXWTLwD7O/vD6lUil27dsHW1rbanUBgXnvbA/iNGzeGr68vfH19y/wAPqMdvAOcmJiIs2fPVnnrG4z2FTaBo1QqsXPnTmRnZ3PTnJ2duT0tawJHd/AOcIsWLfDkyRNt1MIIQKVSYd++fVAqldixYwdrhE7P8A7wkiVLMHPmTHz33XclPszArsfqvtzcXBw4cABRUVHYsWOHRlPADg4OXGj/85//sNDqON43chQ2IPbmP6wQJ7HYjRxll5eXh4MHD3JtN6enp3PT6tevr9EQe3VrJE7faPVGjvj4+HIXxlSt/Px8xMXFcb0nFG0xhXWFUj3wDrCHh4c26mAqSWFnZEqlEtu2bSu1M7Ju3bqxngWrgXLdyJGeno7169fj6tWrEIlEaNGiBcaNGwcLC4vKro8pg8LuQAtDm5aWxk1j3YFWb7x/A585cwb9+vWDXC5Hp06dQEQ4c+YMcnJysH//fnTo0EFbtRZTk38Dq9VqHDt2jOuQ+9GjR9y0wg65FQoFevTowTrk1jNa7Zmhe/fuaNy4MdauXct9MPLz8zFhwgTcvn0bhw8fLn/lPNW0AKvVahw/fpwLbWpqKjetdu3aGDp0KHx9fdGzZ89iVwcY/aHVAMvlcpw/f77YjRxXrlyBq6urRv832lYTAkxEOHnyJJRKJbZs2YL79+9z0ywsLDBkyBD4+vqid+/eMDQ0FLBSprJo9Sy0ubk5kpOTiwU4JSUFZmZmfBfHlKDwZ4lSqYRSqURycjI3zczMDEOGDIFCoUDfvn1ZG2E1HO8A+/r6Yvz48Vi2bBnc3NwgEolw9OhRfPHFF9WiWwuhEBHOnz/PhbZo22OmpqYYNGgQFAoF+vXrByMjIwErZXQJ7wAvW7YMIpEIY8aMQX5+PgDAwMAAn3zyCRYvXlzpBVZnRIQLFy4gKioKSqUSt279f8OpxsbG8PHxgUKhgLe3d41qKpUpu3I3qfPy5UvcunULRITGjRvD2Ni4smt7J338DUxEuHz5Mhfa69evc9PkcjkGDBgAhUKBAQMGCPI3ZYRXJU3qGBsbo3Xr1uV9e41z5coV7vD46tWr3HgjIyP079+fC62pqamAVTL6pkwBHjZsGDZt2gRzc3MMGzbsrfNGR0dXSmHVwbVr17jQXrp0iRtvaGgIb29vKBQK+Pj4sJN/TLmVKcAWFhbcwwvm5ubsCZW3uHnzJhfapKQkbryBgQH69esHhUKBQYMGsbvWmErBmpWtBLdv3+ZCe/78eW68VCpF3759oVAoMGTIENSqVUuwGhn9odXfwL169UJ0dHSxD2NmZiaGDBmCuLg4vovUS/fu3eNCe+bMGW68RCJB79694evriyFDhsDS0lLAKpnqjneADx06pNF8aKFXr17hyJEjlVKUrkpJScGWLVugVCpx8uRJbrxYLEbPnj3h6+uLoUOHok6dOgJWydQkZQ7whQsXuP+/cuWKxs3zBQUFiI2NRf369Su3Oh3w4MEDrue8oi1xikQi9OjRAwqFAsOGDUPdunUFrJKpqcoc4Hbt2kEkEkEkEqFXr17Fpsvlcvz888+VWlxFFXa/IZfLeT2w/vDhQ2zbtg1RUVEldnepUCgwfPhw1KtXTxtlM0yZlTnAd+7cARHB2dkZp06dgrW1NTfN0NAQdevW1ZlnTZOSkhAaGoqoiAjkqFSQy2Tw9fNDUFAQ2rZtW+J7Hj9+jOjoaERFRZXY3aVCocCIESO47i4ZRidUXp9qFfPdd98RAJo2bVqZ31NSL26FXVA2/LcLys3/dkHZsIQuKAu7u+zVq1ex7i67dOnyzu4uGUYb+PROWO47sa5cuYLk5ORiJ7QGDRrEe1mnT5/GmjVr0KZNm/KWA+D1nnfM6NEYVVBQrBPoL/PzMQHAmNGj8ffff+P48eOIi4vTaISvU6dO3J62YcOGFaqFYaoE32+HW7duUZs2bUgkEpFYLCaRSMT9v1gs5v1tk5WVRS4uLnTgwAHy8PCo0B7Y39+fGkqllAsQlTDkAmRXZC8LgDp27EhLliyh27dv866dYbSBzx6Yd1OE06ZNg5OTEx4/fgxjY2NcvnwZhw8fhqurKw4dOsT7C2TKlCkYMGAA+vTp8855VSoVMjMzNYZCarUaURERGJ+fj9LaojAAMAmARCTCokWLcPPmTZw5cwYzZ86sUF+1DCMU3ofQhYee1tbWEIvFEIvFcHd3R0hICD799FONO5HeJTIyEufOncPp06fLNH9ISAjmzZtX4rScnBzkqFR4V++zzgAKiBAUFMSe9mH0Hu89cEFBAffETJ06dbh2mRo2bIhr166VeTkpKSmYNm0awsLCyvyAenBwMDIyMrghJSWFmyaXyyGXyXDrLe8HgNsA5DIZe76WqRZ4B7hVq1bcTR2dO3fGf//7Xxw7dgzz58+Hs7NzmZdz9uxZpKWloWPHjpBKpZBKpUhISMBPP/0EqVRaYg8PMpkM5ubmGgO3IWIxfP38sF4qRV6xd76WB2DNv/OxBzKY6oB3gL/55huuU++FCxfi3r176N69O/bs2YOffvqpzMvp3bs3Ll68iMTERG5wdXXFBx98gMTExHJdUw4KCsIDIkwAioU4D8A4AA/Uajx8+FCju0yG0VeV8jTSs2fPULt27Qrv1Xr06IF27dohNDS0TPOX9NRGREQExoweDTuRCBPy8+GM14fN66RSPFCrQXh9wqtz5874448/2N1UjM7h8zQS7z3wb7/9ptFvLABYWlrqzCGpn58fzpw9i94ffogQmQwfAgiRydD7ww9x9tw5/Pnnn7C0tMTJkyfRqVMnjXu8GUbv8L1GVadOHTI2NiZfX1/auXMn5eXlleNKV+V41/WygoICevHiBanVao3x169fpyZNmhAAMjExoZiYmKool2HKRKvXgR8+fIioqChIJBK8//77sLW1xeTJkzWe1NEVYrEYJiYmxY4OXFxccOLECfTq1QvZ2dkYPHgwli9frnH/M8PohYp8U2RnZ1NYWBj179+fDA0NydnZuSKL443PN1VJcnNz6eOPP+buyvroo48oNze3kqtkGH60ugcuytjYGP369YO3tzdcXFxw9+7dSvhKqToGBgb45Zdf8MMPP0AsFmPt2rXw8vLS6EeXYXRZuQL88uVLbN68Gf3794ednR1++OEHDBkyRKPlRX0hEokQFBSEmJgYmJqaIi4uDl26dNFor5lhdBbf3fv7779PJiYmZG1tTZMnT6Zjx46V6zChMlT0EPpNFy5coIYNGxIAql27NsXFxVXKchmGD60eQotEIkRFRSE1NRUrV66Em5tbpX+pCKV169Y4efIkunTpgufPn8PT0xPr1q0TuiyGKRWvAOfl5eHhw4dwcXGptp1G29jYID4+Hn5+fsjPz8dHH32Ezz//vMRbOxlGaLwCbGBggEuXLunMTRvaYmRkhM2bN3NPPn3//fcYMmQIsrKyBK6MYTTxPoQeM2YM1q9fr41adIpIJMLs2bMRGRkJIyMj7Nq1C+7u7hp99TKM0HgfB+fm5mLdunU4cOAAXF1dYWJiojF9+fLllVacLvD19YWjoyMGDx6MCxcuoFOnTtixYwc6d+4sdGkMw/9hhp49e5a+MJGoSntmqMquVZKTk+Hj44MLFy5AJpPht99+g6+vr1bXydRMfD7XrG8kHrKysvDBBx9g586dAIB58+bh22+/rfbnBJiqpdWnkQrdvHkT+/bt456r1ePvgTIzMzPD9u3bMWPGDADAnDlz8MEHH+DVq1cCV8bUVLwD/PTpU/Tu3RtNmjRB//798fDhQwDAhAkTuA92dSaRSLBs2TKsXbsWUqkUERER6NmzJx4/fix0aUwNxDvAn332GQwMDJCcnKzRKJyvry9iY2MrtThdNmHCBOzfvx+1a9fGiRMn0KlTJ1y8eFHospgahneA9+/fjyVLlsDe3l5jvIuLC+7du1dphemDnj174sSJE3BxcUFycjLc3Nywe/duoctiahDeAc7Ozi6xOdYnT55AJpNVSlH6pEmTJjhx4gR69uyJFy9eYNCgQfjhhx9qxDkBRni8A/zee+/h999/516LRCKo1WosXbr0rZeYqjNLS0vs27cPH330EdRqNaZPn45JkyYhL6+09jEZppLwfVLi8uXLZG1tTV5eXmRoaEgjRoyg5s2bk42NDd28eZPv4iqksp9Gqii1Wk3Lly8nkUhEAKhXr1707Nkzocti9IxWn0Zq0aIFd0dS3759kZ2djWHDhuH8+fNo1Ohd/SJUbyKRCJ999hl27Nih8WzxjRs3hC6NqabYjRxacuHCBfj4+CA5ORm1a9dGdHQ0evToIXRZjB7Q6o0csbGxGr3Wr1y5Eu3atcOoUaPw/Plz/tVWU23atMHJkyfRuXNnPH/+HH379q0RD4EwVYt3gL/44guuV8CLFy9i+vTp6N+/P27fvo3p06dXeoH6rF69eoiPj4evry/y8/MxYcIEfPHFF+zZYqby8P2BbWJiQnfu3CEiojlz5tDw4cOJiOjs2bNkY2PDd3EVomsnsUqjVqtpzpw5XOuXgwYNoqysLKHLYnSUVk9iGRoa4uXLlwCAgwcPwtPTE8DrSylF++tl/p9IJMLcuXMRHh4OmUyGmJgYuLu7a/SuyDDlwTvA7u7umD59OhYsWIBTp05hwIABAIDr168XuzuL0eTn54dDhw6hbt26SEpKQqdOnXDq1Cmhy2L0GO8Ar1ixAlKpFFu3bsXq1atRv359AMDevXvh5eVV6QVWN126dMGpU6fQunVrPHr0CB4eHlAqlUKXxegpdhlJIFlZWfDz8+PunZ4/fz6++eYbjWeL1Wo1cnJyIJfLIRZXqA1+Ro9o/XnggoICbN26FQsWLMDChQuxdetW5Ofnl6vYmsrMzAw7duzgztzPnj0bH374IV69eoWkpCQEBATA1NgYpqamMDU2RkBAAJKSkgSumtE5fM+QXbx4kZycnMjY2Jjat29P7du3JxMTE3J0dKQLFy7wWtaqVauodevWZGZmRmZmZtSlSxfas2dPmd+vL2eh3+XXX38lqVRKAMjFxYWkEgk1lEppPkCbAZoPUEOplKQSCYWHhwtdLqNlfD7XvAPcuXNn8vHx0bjH99mzZzRo0CDq0qULr2XFxMTQ7t276dq1a3Tt2jX66quvyMDAgC5dulSm91eXABMRHTx4kMzMzEgE0GiAcgGiIkMuQGMAkkoklJiYKHS5jBZpNcBGRkYlBuzixYtkZGTEd3HF1K5dm9atW1emeatTgImIhg4dSvVLCG/REDeQSinA31/oUhkt0up14KZNm5bYfExaWhoaN25c7kP5goICREZGIjs7G127di1xHpVKhczMTI2hulCr1YjdswcTARiUMo8BgAn5+YiMiGDPGzOvlfUboXDYvXs3tWzZkrZs2UIpKSmUkpJCW7ZsodatW9Pu3bt5f9tcuHCBTExMSCKRkIWFxVuXUfRupqJDddgDv3jxgvDvb96S9r6FQ9i/25ydnS10yYyW8NkDl+kyklgs1ri8UfiWwnFFX/O9zzc3NxfJyclIT0/Htm3bsG7dOiQkJKBFixbF5lWpVFCpVNzrzMxMODg46OVlpDep1WqYGhsjWKXCt2+ZbwGAhVIpUh89gpWVVVWVx1ShSm8XOiEhocwr9/DwKPO8JenTpw8aNWqEX3/99Z3z6vN14JIEBAQgPiwMN/LzSzyMzgPgCCAVr/tvGjVqFAIDA9G+ffsqrZPRLl6fa60eC5RDr169aOzYsWWat7qdxEpMTCSpREJjSjkLPRogiVhMTZo00fgJ4ebmRuHh4aRSqYTeBKYSaPUsNBHR8+fPadmyZTR+/HiaMGECLV++nNLT03kvJzg4mA4fPkx37tyhCxcu0FdffUVisZj2799fpvdXtwATEYWHh5NUIqEG/14HDvv3OnCDIteB1Wo1HT16lN5//33u+jEAsrGxodmzZ9P9+/eF3gymArQa4NOnT5OlpSXVr1+fhg4dSkOGDCF7e3uysrKis2fP8lrWuHHjqGHDhmRoaEjW1tbUu3fvMoeXqHoGmOj1njjA35/kMhkBILlMRgH+/iVe/01NTaV58+aRra0tF2SJREIjR46khIQEUqvVAmwBUxFaDbC7uzv5+/tTXl4eNy4vL4/Gjh1L3bt357u4CqmuAS5UUFBAL168KFMIc3NzKSoqirp3765xeN26dWv69ddf6cWLF1VQMVMZtH4jx9WrV4uNv3z5Msnlcr6Lq5DqHuDySkxMpI8++ojkcjkXZAsLCwoKCqLr168LXR7zDlq9kcPc3LzETq5TUlJgZmbGd3GMFrRt2xZr1qzBgwcPsHz5cjRq1AgZGRkIDQ1FkyZN4O3tjd27d7OmfaoDvt8OU6dOJXt7e4qMjKTk5GRKSUmhiIgIsre3p2nTppXnC6fc2B64bAoKCmjPnj3Uv39/rs1qAOTs7ExLly6lp0+fCl0iU4RWD6FVKhV9+umnZGhoSGKxmMRiMclkMgoKCqJXr16Vq+DyYgHm7+bNmzRjxgyqVasWF2QjIyMaP348nTt3TujyGNLCnVglefnyJW7dugUiQuPGjUvsL0nbqtuNHFXp5cuXCA8Px4oVKzSeM+7WrRumTJmC4cOHw9DQUMAKa65KvxNLV7EAVxwR4dixY1ixYgW2bdvGNcxgY2ODiRMnYuLEibCzsxO4ypqFBZgpl4cPH2LNmjX45Zdf8OjRIwCAVCrFsGHDEBgYCHd3d4174hntYAFmKiQ3Nxfbt2/HihUrNHrhaNOmDaZMmYIPPvgAJiYmAlZYvbEAM5UmKSkJK1euRFhYGHJycgAAFhYWGDduHCZPnlyhZ8CZkrEAM5Xu+fPn2LhxI1auXInbt29z4729vTFlyhR4e3uzljMrCQswozVqtRqxsbFYsWIF9u7dy413dnbG5MmTERAQAEtLSwEr1H8swEyVuHnzJlavXo0NGzYgPT0dACCXy/HBBx9gypQpaNeunaD16SsWYKZKZWdnc9eUL1y4wI3v1q0bAgMDMWzYMHZNmQcWYEYQpV1TrlevHiZOnIiPP/6YXVMuAxZgRnCpqalYs2YNfv31V3ZNmScWYEZn5ObmIjo6GitXrix2TTkwMBCjRo1i15TfwALM6KTExESsXLkSmzdv5q4p16pVC+PGjcMnn3zCrin/iwWY0WnPnj3Dxo0bsWrVKu6askgkgpeXFwIDA+Hl5VWjrymzADN6obRryo0aNeKuKdeuXVvACoXBAszonZs3b2LVqlXYsGEDMjIyAPz/NeXAwEC0bdtW4AqrDgswo7eys7OxefNmrFixAhcvXuTGu7u7IzAwEEOHDq3215RZgBm9R0Q4evQoVqxYgejoaO6asq2tLXdN2dbWVuAqtYMFmKlWSrumPHz4cAQGBqJbt27V6poyCzBTLRVeU16xYgWOHTvGjW/bti13TVmIpp0qGwswU+2dP3+eu6b86tUrAP9/TXny5Mlo1KiRwBWWHwswU2MUXlNeuXIl7ty5A+D1NWVvb28EBgaiX79+endNmQWYqXEKCgq4a8qxsbHceH28pswCzNRoN27c4J5TLnpN+cMPP8SUKVPeeU1ZrVYjJycHcrlckL03n8+1fh1bMEwZuLi4YPny5Xjw4AF+/fVXtG7dGjk5OVi7di3atWuH9957D0qlEnl5eRrvS0pKQkBAAEyNjWFqagpTY2MEBARotJutc3g2Gl+pvvvuO3J1dSVTU1OytramwYMH099//13m97OeGZiyUKvVlJCQQAqFgiQSCdcjha2tLc2dO5dSU1O5fpkb/tsv8+Z/+2VuWKRf5qqi9Q6+K0u/fv1o48aNdOnSJUpMTKQBAwZQgwYNytwVJgsww9f9+/dp9uzZZGNjo9GfslgkojEA5QJERYZcgMYAJJVISuyfWRv0JsBvSktLIwCUkJBQpvlZgJnyUqlUFB4eTm5ubgSA6pcQ3qIhbiCVUoC/f5XUptXuRbWp8IRDaa0aqlQqZGZmagwMUx6Ghobw8/PDkSNHYGRoiIkADEqZ1wDAhPx8REZEgHTsnK/OBJiIMH36dLi7u6NVq1YlzhMSEgILCwtucHBwqOIqmeomJycHr3Jz8a7bPpwB5KhUXEMEukJnAhwYGIgLFy4gIiKi1HmCg4ORkZHBDSkpKVVYIVMdyeVyyGUy3HrHfLcByGUyyOXyqiirzHQiwFOnTkVMTAzi4+Nhb29f6nwymQzm5uYaA8NUhFgshq+fH9ZLpcgrZZ48AOukUrzv56dzD00IGmAiQmBgIKKjoxEXFwcnJychy2FqqKCgIDwgwgSgWIjzAIwDkEqEaUFBVV7bu0iFXPmUKVMQHh6OHTt2wMzMjHtUzMLCQucOVZjqq23btvj9f//DmNGjcUgkwoT8fDjj9WHzLwAeAvhl1SrdbBVE26fE3wb/Xod7c9i4cWOZ3s8uIzGVKTExkQL8/UkukxEAkstkVLtWLQJAn3zySZXVwedzze6FZpg3FN4LbWxsjCNHjsDDwwMikQhnzpxBhw4dtL5+di80w1SAWCyGiYkJRCIR3nvvPYwaNYo7X6NWq4UuTwMLMMO8w9KlS2Fqaorjx48jLCxM6HI0sAAzzDvY2dnh22+/BQDMnDmTu2NQF7AAM0wZBAUFoUmTJnj8+DHmzZsndDkcFmCGKQNDQ0P8/PPPAICffvoJly9fFrii11iAGaaMPD09MXToUBQUFGDq1Kk68WADCzDD8LB8+XIYGRkhPj4eW7ZsEbocFmCG4cPR0RHBwcEAgBkzZuDFixeC1sMCzDA8ffHFF3BycsL9+/fx3XffCVoLCzDD8CSXyxEaGgoAWLZsGa5fvy5YLSzADFMOPj4+8Pb2Rl5eHqZNmybYCS0WYIYpB5FIhB9//BGGhoaIjY3Fzp07BamDBZhhysnFxQUzZswA8PpGj8I+mqoSCzDDVMDXX38Ne3t73LlzB0uXLq3y9bMAM0wFmJiY4PvvvwcAfPfdd7h7926Vrp8FmGEqaOTIkejZsydevXrFHVJXFRZghqkgkUiEn3/+GRKJBNHR0di/f3+VrZsFmGEqQcuWLfHpp58CAD799FPk5uZWyXpZgBmmksyZMwc2Nja4du0afvzxxypZJwsww1QSCwsLLFmyBAAwf/58PHjwQOvrZAFmmEo0evRodO3aFS9evMDMmTO1vj4WYIapRGKxGCtWrIBIJEJ4eDgOHz6s3fVpdekMUwN16NABEydOBPC6z6/8/HytrYsFmGG0YOHChbC0tMTFixexevVqra2HBZhhtMDKyop7Vvjbb79FWlqaVtbDAswwWjJhwgR06NABGRkZXCselY0FmGG0RCKRYMWKFQCADRs24OTJk5W+DhZghtGirl27wt/fHwC4rlnUajWys7MrpZsWQQN8+PBh+Pj4wM7ODiKRCH/88YeQ5TCMVixevBjm5uY4c+YMunfvDlNjY5iamsLU2BgBAQFISkoq97IFDXB2djbatm3LHWYwTHVkY2ODwYMHQwTg3l9/IVilwmYAwSoV4sPC4NqxIyIiIsq1bEE7+Pb29oa3t7eQJTCM1iUlJSEiPBwfAlgPwKDItC/z8zEBwJjRo9GiRQvenYiz38AMo2WhoaGoLxIVCy/+fb0OgJ1IhB//bemSD70KsEqlQmZmpsbAMLpMrVYjKiIC4/Pzi4W3kAGACfn5iIyI4N26pV4FOCQkBBYWFtzg4OAgdEkM81Y5OTnIUanQ6B3zOQPIUamQk5PDa/l6FeDg4GBkZGRwQ0pKitAlMcxbyeVyyGUy3HrHfLcByGUyyOVyXsvXqwDLZDKYm5trDAyjy8RiMXz9/LBeKkVeKfPkAVgnleJ9Pz+IRCJ+y69whRXw4sULJCYmIjExEQBw584dJCYmIjk5WciyGKZSBQUF4QERJgDFQpwHYDyAVCJMCwriv3ASUHx8PAEoNowdO7ZM78/IyCAAlJGRod1CGaaCwsPDSSqRUAOplOYDFAbQfIAaSKUklUgoPDycm5fP51pEpAO9FJdTZmYmLCwskJGRwQ6nGZ2XlJSEH0NDERkRgRyVCnKZDO/7+WFaUJDG9V8+n2sWYIapYmq1Gjk5OTA2Ni7xNy+fz7Wgd2IxTE0kFothYmJSOcuqlKUwDCMIFmCG0WMswAyjx1iAGUaPsQAzjB7T67PQhVfA2FNJTHVS+HkuyxVevQ5wVlYWALCnkphqKSsrCxYWFm+dR69v5FCr1UhNTYWZmVmpF8QdHByQkpKilzd66HP9rPbyIyJkZWXBzs4OYvHbf+Xq9R5YLBbD3t7+nfPp+5NL+lw/q7183rXnLcROYjGMHmMBZhg9Vq0DLJPJMGfOHMhkMqFLKRd9rp/VXjX0+iQWw9R01XoPzDDVHQsww+gxFmCG0WPVOsCrVq2Ck5MTjIyM0LFjRxw5ckTokt4pJCQE//nPf2BmZoa6detiyJAhuHbtmtBllUtISAhEIhGCytNYm0AePHiADz/8EFZWVjA2Nka7du1w9uxZocsqVbUNcFRUFIKCgvD111/j/Pnz6N69O7y9vXW+xcuEhARMmTIFJ06cwIEDB5Cfnw9PT09kZ2cLXRovp0+fxpo1a9CmTRuhSymz58+fo1u3bjAwMMDevXtx5coVfP/996hVq5bQpZWu0pvf0xGdOnWiSZMmaYxr1qwZffnllwJVVD5paWkEgBISEoQupcyysrLIxcWFDhw4QB4eHjRt2jShSyqTWbNmkbu7u9Bl8FIt98C5ubk4e/YsPD09NcZ7enrir7/+Eqiq8snIyAAAWFpaClxJ2U2ZMgUDBgxAnz59hC6Fl5iYGLi6umLkyJGoW7cu2rdvj7Vr1wpd1ltVywA/efIEBQUFsLGx0RhvY2ODR48eCVQVf0SE6dOnw93dHa1atRK6nDKJjIzEuXPnEBISInQpvN2+fRurV6+Gi4sL9u3bh0mTJuHTTz/F77//LnRppdLrhxne5c0nlIiId9cVQgoMDMSFCxdw9OhRoUspk5SUFEybNg379++HkZGR0OXwplar4erqiu+++w4A0L59e1y+fBmrV6/GmDFjBK6uZNVyD1ynTh1IJJJie9u0tLRie2VdNXXqVMTExCA+Pr5MT1zpgrNnzyItLQ0dO3aEVCqFVCpFQkICfvrpJ0ilUhQUFAhd4lvZ2tqiRYsWGuOaN2+u0yc+q2WADQ0N0bFjRxw4cEBj/IEDB+Dm5iZQVWVDRAgMDER0dDTi4uLg5OQkdEll1rt3b1y8eJHr7yoxMRGurq744IMPkJiYCIlEInSJb9WtW7dil+yuX7+Ohg0bClRRGQh8Ek1rIiMjycDAgNavX09XrlyhoKAgMjExobt37wpd2lt98sknZGFhQYcOHaKHDx9yw8uXL4UurVz06Sz0qVOnSCqV0qJFi+jGjRu0efNmMjY2prCwMKFLK1W1DTAR0cqVK6lhw4ZkaGhIHTp00ItLMSihszcAtHHjRqFLKxd9CjAR0c6dO6lVq1Ykk8moWbNmtGbNGqFLeiv2NBLD6LFq+RuYYWoKFmCG0WMswAyjx1iAGUaPsQAzjB5jAWYYPcYCzDB6jAWYYfQYCzDD6DEWYKZamjt3Ltq1ayd0GVrHAszwRkTIz88XugwGLMBVrkePHpg6dSqCgoJQu3Zt2NjYYM2aNcjOzkZAQADMzMzQqFEj7N27l3vPlStX0L9/f5iamsLGxgajR4/GkydPuOmxsbFwd3dHrVq1YGVlhYEDB+LWrVvc9NzcXAQGBsLW1hZGRkZwdHTkWsy4e/cuRCIREhMTufnT09MhEolw6NAhAMChQ4cgEomwb98+uLq6QiaT4ciRIyAi/Pe//4WzszPkcjnatm2LrVu3cssp+r727dtDLpejV69eSEtLw969e9G8eXOYm5vDz88PL1++5N5X1uX++eefcHV1hbGxMdzc3LhHATdt2oR58+YhKSkJIpEIIpEImzZtqpR/P50j6KMUNZCHhweZmZnRggUL6Pr167RgwQISi8Xk7e1Na9asoevXr9Mnn3xCVlZWlJ2dTampqVSnTh0KDg6mq1ev0rlz56hv377Us2dPbplbt26lbdu20fXr1+n8+fPk4+NDrVu3poKCAiIiWrp0KTk4ONDhw4fp7t27dOTIEQoPDyciojt37hAAOn/+PLe858+fEwCKj48nIqL4+HgCQG3atKH9+/fTzZs36cmTJ/TVV19Rs2bNKDY2lm7dukUbN24kmUxGhw4d0nhfly5d6OjRo3Tu3Dlq3LgxeXh4kKenJ507d44OHz5MVlZWtHjxYm79ZV1u586d6dChQ3T58mXq3r07ubm5ERHRy5cvacaMGdSyZUu9fxzzXViAq5iHh4dGy4f5+flkYmJCo0eP5sY9fPiQANDx48fp22+/JU9PT41lpKSkEAC6du1aiesobMny4sWLREQ0depU6tWrF6nV6mLz8gnwH3/8wc3z4sULMjIyor/++ktjeePHjyc/Pz+N9x08eJCbHhISQgDo1q1b3LiJEydSv379KrTc3bt3EwDKyckhIqI5c+ZQ27ZtS/z7VCfVuk0sXVW0rWSJRAIrKyu0bt2aG1fY7E9aWhrOnj2L+Ph4mJqaFlvOrVu30KRJE9y6dQvffvstTpw4gSdPnkCtVgMAkpOT0apVK/j7+6Nv375o2rQpvLy8MHDgwGItdpaFq6sr9/9XrlzBq1ev0LdvX415cnNz0b59+1K318bGBsbGxnB2dtYYd+rUqQot19bWFsDrv1mDBg14b5u+YgEWgIGBgcZrkUikMa6w4T21Wg21Wg0fHx8sWbKk2HIKP7Q+Pj5wcHDA2rVrYWdnB7VajVatWiE3NxcA0KFDB9y5cwd79+7FwYMHoVAo0KdPH2zduhVi8evTIFTksfC8vLwS6zYxMeH+v/BLYvfu3ahfv77GfG92y/nmtpW0/YXLq8hyi76/pmAB1nEdOnTAtm3b4OjoCKm0+D/X06dPcfXqVfz666/o3r07AJTYiqW5uTl8fX3h6+uLESNGwMvLC8+ePYO1tTUA4OHDh9werugJrdK0aNECMpkMycnJ8PDwqMAWame5hoaGOt+IXmVgAdZxU6ZMwdq1a+Hn54cvvvgCderUwc2bNxEZGYm1a9eidu3asLKywpo1a2Bra4vk5GR8+eWXGsv44YcfYGtri3bt2kEsFmPLli2oV68eatWqBbFYjC5dumDx4sVwdHTEkydP8M0337yzLjMzM3z++ef47LPPoFar4e7ujszMTPz1118wNTXF2LFjy7W9lbVcR0dH3LlzB4mJibC3t4eZmZledNjNF7uMpOPs7Oxw7NgxFBQUoF+/fmjVqhWmTZsGCwsLiMViiMViREZG4uzZs2jVqhU+++wzLF26VGMZpqamWLJkCVxdXfGf//wHd+/exZ49e7jD5w0bNiAvLw+urq6YNm0aFi5cWKbaFixYgNmzZyMkJATNmzdHv379sHPnzgq3pFkZyx0+fDi8vLzQs2dPWFtbIyIiokI16SrWJhbD6DG2B2YYPcYCzDB6jAWYYfQYCzDD6DEWYIbRYyzADKPHWIAZRo+xADOMHmMBZhg9xgLMMHqMBZhh9BgLMMPosf8DJG/KbOO1biEAAAAASUVORK5CYII=",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from matplotlib import pyplot as plt\n",
+ "fig, axs = plt.subplots(figsize=(2.5,4), tight_layout=True)\n",
+ "axs.plot([7, 6, 2, 0, 5, 0, 0.5, 2], [1, 2, 3, 4, 5, 6, 7, 8], marker='.', markersize=14, color='black', markerfacecolor='red')\n",
+ "axs.set_title('some artificial data'); axs.set_ylabel('observation number'); axs.set_xlabel('measurement'); plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fc1281df-3f50-4f1a-bddf-2ffbfb15c587",
+ "metadata": {},
+ "source": [
+ "This chart is an abbreviated archetype of an initial shallow profiler chart:\n",
+ "The vertical axis corresponds to depth, horizontal is a physical measurement. Let's belabor\n",
+ "this for a moment by supposing color coding the markers 'from a separate measurement'."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "1dae7e34-d07d-4520-8445-c949d45dd46c",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from matplotlib import pyplot as plt\n",
+ "fig, axs = plt.subplots(figsize=(2.5,4), tight_layout=True)\n",
+ "axs.plot([7, 6, 2, 0, 5, 0, 0.5, 2], [1, 2, 3, 4, 5, 6, 7, 8], marker='.', markersize=14, color='black', markerfacecolor='none')\n",
+ "axs.scatter([7, 6, 2, 0, 5, 0, 0.5, 2], [1, 2, 3, 4, 5, 6, 7, 8], marker='.', s=120, c=['r', 'y', 'cyan', 'cyan', 'r', 'b', 'b', 'g'])\n",
+ "axs.set_title('some artificial data'); axs.set_ylabel('observation number'); axs.set_xlabel('measurement'); plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0e885ecc-11f7-4e84-b289-7ec0e00c153b",
+ "metadata": {},
+ "source": [
+ "The `axs.plot()` line is the same but for the color change to `'none'`. The result with the added scatter\n",
+ "plot color-codes each data marker. Building the `.scatter()` is not a trivial process, however, \n",
+ "(`markersize` changes to `s` and the value goes from `14` to `120`...) and the resulting chart is not clean."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a0aac9d9-1c5e-4017-981c-44e0bb78e3f4",
+ "metadata": {},
+ "source": [
+ "#### Widgets\n",
+ "\n",
+ "**Widgets** are an Interactive Python (IPython) library for building interactive visualizations. The idea is to set up \n",
+ "controls such as sliders or selectors that are *wired in* to charting code. Move the slider: Change\n",
+ "a parameter, redraw the chart.\n",
+ "\n",
+ "\n",
+ "### Containerization\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n",
+ "### Annotation\n",
+ "\n",
+ "\n",
+ "\n",
+ "[TOC](#1-Technical-Elements)\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f29f5af9-ab96-4f00-86f9-7f5350b7b6dc",
+ "metadata": {},
+ "source": [
+ "## Shallow Profilers\n",
+ "\n",
+ "\n",
+ "As a complete system a shallow profiler consists of two instrument-bearing structures: \n",
+ "The *platform* and the *profiler*. The platform includes a winch with a cable that holds\n",
+ "the profiler in place during rest intervals. The profiler is positively buoyant so that as\n",
+ "the cable is paid out the profiler rises through the water column to near the surface.\n",
+ "The platform is anchored to the sea bed by means of two cables. Consequently during\n",
+ "conditions of strong current (storms for example) the platform can be temporarily\n",
+ "displaced laterally and downward.\n",
+ "\n",
+ "\n",
+ "### Data\n",
+ "\n",
+ "\n",
+ "- Getting datasets from the portal: 1-minute-sample, full resolution (1 sample per second)\n",
+ "- See the `data.ipynb` notebook\n",
+ "\n",
+ "\n",
+ "### Profile timing metadata\n",
+ "\n",
+ "\n",
+ "- Nine daily profiles: 7 regular + 2 special: noon/midnight variants\n",
+ "- Basic charts: **Depth** vs { **A**, **B**, **C**, ... }\n",
+ "- Noon/Midnight charts: pCO2 (Descent), pH (Descent), Nitrate (Ascent), Spectral Irradiance (Ascent)\n",
+ "\n",
+ "\n",
+ "### Turbulence\n",
+ "\n",
+ "\n",
+ "### Comparatives\n",
+ "\n",
+ "- Ascent versus Descent\n",
+ "- At Rest versus Platform\n",
+ "- Profiler versus Discrete CTD cast data (VISIONS cruises)\n",
+ " \n",
+ " \n",
+ "### BioOptics\n",
+ "\n",
+ "- FDOM/CDOM and Chlorophyll\n",
+ "- Spectral Irradiance\n",
+ "- Spectrophotometer\n",
+ "\n",
+ "\n",
+ "### More on visualization in practice\n",
+ "\n",
+ "\n",
+ "- Interactivity: Sliders, Widgets\n",
+ "- Visualizing in 3D\n",
+ "- Saving charts as `.png` images\n",
+ "- Creating chart animations"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "746498ae-a9fe-4863-8627-cf6a4d033421",
+ "metadata": {},
+ "source": [
+ "\n",
+ " \n",
+ " \n",
+ "### Data manipulation\n",
+ "\n",
+ "#### XArray Dataset operations\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e7a5517b-4edf-453f-bf3e-94005d3fb05f",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "### Shallow profiler **`instrument; sensor(s)`** abbreviation table \n",
+ "\n",
+ "\n",
+ "| OOI abbrev | sensors |remark|simple abbrevs|\n",
+ "|--|--|--|--|\n",
+ "|ctdpf|3|CTD: includes a variety of sensors|ctd; salinity, temperature, pressure, density, conductivity\n",
+ "|pco2|1|carbonate chemistry, midnight and noon *descent* only|pco2; pco2\n",
+ "|phsen|1|pH, midnight and noon *descent* only|ph; ph\n",
+ "|nutnr|2|nitrate, dark samples; midnight and noon *ascent* only|nitrate; nitrate\n",
+ "|flort|3|Fluorometer triplet: chlorophyll-A, FDOM, backscatter|fluor; chlora, fdom, bb\n",
+ "|spkir|7|spectral irradiance: optical absorbance and beam attenuation|spkir; oa\\#, ba\\#\n",
+ "|parad|1|photosynthetically available radiation 'PAR'|par; par\n",
+ "|optaa|86|spectrophotometer: 86 bands, ascent noon/midnight|sp; sp\\#\\#"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "massive-rabbit",
+ "metadata": {},
+ "source": [
+ "### Slow resampling problem\n",
+ "\n",
+ "\n",
+ "The shallow profiler spectrophotometer has 86 channels. Each observation has \n",
+ "a corresponding depth and time, typically several thousand per profile.\n",
+ "The XArray Dataset has `time` swapped in for `obs` dimension but we are \n",
+ "interested in resampling into depth bins. This took hours; which was \n",
+ "puzzling. However page 137 of PDSH, on **Rearranging Multi-Indices**\n",
+ "and **Sorted and unsorted indices** provides this resolution: \n",
+ "\n",
+ "> ***Rearranging Multi-indices*** \n",
+ "One of the keys to working with multiply indexed data is knowing how to effectively \n",
+ "transform the data. There are a number of operations that will preserve all the \n",
+ "information in the dataset, but rearrange it for the purposes of various computations. \n",
+ "[...] There are many [ways] to finely control the rearrangement\n",
+ "of data between heirarchical indices and columns.\n",
+ " \n",
+ "> ***Sorted and unsorted indices*** \n",
+ "Earlier, we briefly mentioned a caveat, but we should emphasize it more here. \n",
+ "*Many of the `MultiIndex`slicing operations will fail if the index is not sorted.*"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "b63ab051-5eef-44ee-94eb-38aa0a1db505",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "SyntaxError",
+ "evalue": "unterminated string literal (detected at line 60) (1449922703.py, line 60)",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;36m Cell \u001b[0;32mIn[43], line 60\u001b[0;36m\u001b[0m\n\u001b[0;31m * qc points to total volume scattering and optical backscatter but I'll keep all three\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unterminated string literal (detected at line 60)\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Adrift content\n",
+ "\n",
+ "\n",
+ "\n",
+ "- merge() ...?... \n",
+ " - Order: `.merge()` then `.resample()` with `mean()`; or vice versa? (existing code is vice-versa)\n",
+ " - This approach does resampling prior to merge but was taking way too long\n",
+ "- resampling\n",
+ "\n",
+ "```\n",
+ "ds = ds.reset_coords('seawater_pressure') # converts the coordinate to a data variable\n",
+ "ds_mean = ds.resample(time='1Min').mean()\n",
+ "ds_std = ds.resample(time='1Min').std()\n",
+ "```\n",
+ "\n",
+ "- How to copy a dataset, how to move a coordinate to a data variable\n",
+ "\n",
+ " - ./images/misc/optaa_spectra_0_10_20_JAN_2019.png\n",
+ " - ./images/misc/nitrate_2019_JAN_1_to_10.png\n",
+ "- pH sensor fire once at the end of every profile; back in the platform***\n",
+ "- Manufacturer etc: [here](https://interactiveoceans.washington.edu/instruments/).\n",
+ "- ...but the DataArray can itself be invoked with `.attrs` to see additional attributes that are invisible\n",
+ "\n",
+ "```\n",
+ "ds.density.attrs\n",
+ "```\n",
+ "\n",
+ "- Optical absorption spectrophotometer\n",
+ " * Seabird Scientific (acquired WETLABS): 'AC-S' model (shallow profilers)\n",
+ " * 86 wavelengths per sample; in practice some nan values at both ends\n",
+ " * Interview Chris for more procedural / interpretive SME\n",
+ " * Operates only during shallow profiler ascents\n",
+ " * Only on the two \"nitrate\" ascents each day\n",
+ " * One sample per 0.27 seconds\n",
+ " * However it often does a \"skip\" with a sample interval about 0.5 seconds\n",
+ " * Spectral absorption: parameter `a`, values typically 20 - 45. \n",
+ " * Attenuation is `c` with values on 0 to 1.\n",
+ " * Coordinates we want are `time`, `int_ctd_pressure`, `wavelength`\n",
+ " * `time` and `wavelength` are also dimensions\n",
+ " * Data variables we want are `beam_attenuation` (this is `c`) and `optical_absorption` (`a`)\n",
+ " * Per year data is about 1.7 billion floating point numbers\n",
+ " * 86 wavelengths x 2 (c, a) x 2 (ascent / day) x 14,000 (sample / ascent) x 365\n",
+ "\n",
+ " \n",
+ "\n",
+ "- Photosynthetically Active Radiation (PAR)\n",
+ " * Devices mounted on the shallow profiler and the SP platform\n",
+ " * Seabird Scientific (from acquisition of Satlantic): PAR model\n",
+ " * Some ambiguity in desired result: `par`, `par_measured` and `par_counts_output` are all present in the data file\n",
+ " * Since `qc` values are associated with it I will simply use `par_counts_output`\n",
+ " \n",
+ " \n",
+ "- Fluorometer\n",
+ " * WETLABS (Seabird Scientific from acquisition) Triplet\n",
+ " * Chlorophyll emission is at 683 nm\n",
+ " * Measurement wavelengths in nm are 700.0 (scattering), 460.0 (cdom) and 695.0 (chlorophyll)\n",
+ " * Candidate Data variables\n",
+ " * Definites are `fluorometric_chlorophyll_a` and `fluorometric_cdom`\n",
+ " * Possibles are `total_volume_scattering_coefficient`, `seawater_scattering_coefficient`, `optical_backscatter`\n",
+ " * qc points to total volume scattering and optical backscatter but I'll keep all three\n",
+ " \n",
+ " \n",
+ "- Nitrate nutnr_a_sample and nutnr_a_dark_sample\n",
+ " * The nitrate run ascent is ~62 minutes (ascent only); ~3 meters per minute\n",
+ " * Ascent is about 14,000 samples; so 220 samples per minute\n",
+ " * That is 70 samples per meter depth over 20 seconds\n",
+ " * Per the User's Manual post-processing gets rather involved\n",
+ "\n",
+ "\n",
+ "- pCO2 water (two streams: pco2w_b_sami_data_record and pco2w_a_sami_data_record)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "weighted-retro",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "### One minute resampling\n",
+ "\n",
+ "\n",
+ "This is the practical implementation of index sorting described above and in *PDSH*.\n",
+ "\n",
+ "\n",
+ "`XArray Datasets` feature selection by time range: `ds.sel(time=slice(timeA, timeB))`\n",
+ "and resampling by time interval: `ds.resample(time='1Min').mean()`. \n",
+ "(Substitute `.std()` to expand into standard deviation signals.)\n",
+ "\n",
+ "\n",
+ "```\n",
+ "ds = xr.open_dataset(ctd_data_filename)\n",
+ "tJan1 = dt64('2019-01-01')\n",
+ "tFeb1 = dt64('2019-02-01')\n",
+ "ds = ds.sel(time=slice(tJan1, tFeb1))\n",
+ "ds1Min = ds.resample(time='1Min').mean()\n",
+ "```\n",
+ "\n",
+ "\n",
+ "\n",
+ "The problem however is that the resample() execution in the code above\n",
+ "can hang. The select operation `.sel()` is not understood by XArray as a monotonic\n",
+ "time dimension monotonic. It may be treated as a jumble even if it is not! \n",
+ "This can become even more catastrophic when other dimensions are present. \n",
+ "The following work-around uses `pandas Dataframes`. \n",
+ "\n",
+ "\n",
+ "\n",
+ "This code moves the \n",
+ "XArray Dataset contents into a pandas DataFrame.\n",
+ "Here they are resampled properly; and the resulting\n",
+ "columns are converted into a list of XArray DataArrays.\n",
+ "These are then combined to form a new Dataset with \n",
+ "the desired resampling completed quickly. \n",
+ "\n",
+ "\n",
+ "\n",
+ "```\n",
+ "df = ds.to_dataframe().resample(\"1Min\").mean()\n",
+ "vals = [xr.DataArray(data=df[c], \\\n",
+ " dims=['time'], \\\n",
+ " coords={'time':df.index}, \\\n",
+ " attrs=ds[c].attrs) \\\n",
+ " for c in df.columns]\n",
+ "ds = xr.Dataset(dict(zip(df.columns, vals)), attrs=ds.attrs)\n",
+ "ds.to_netcdf('new_data_file.nc')\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "above-boating",
+ "metadata": {},
+ "source": [
+ "Flourometry code redux: For OSB shallow profiler triplet, to 1Min samples, JAN 2019\n",
+ "\n",
+ "\n",
+ "```\n",
+ "ds_Fluorometer = xr.open_dataset('/data/rca/fluorescence/osb_sp_flort_2019.nc')\n",
+ "time_jan1, time_feb1 = dt64('2019-01-01'), dt64('2019-02-01')\n",
+ "ds_Fluor_jan2019 = ds_Fluorometer.sel(time=slice(time_jan1, time_feb1))\n",
+ "df = ds_Fluor_jan2019.to_dataframe().resample(\"1Min\").mean()\n",
+ "vals = [xr.DataArray(data=df[c], dims=['time'], coords={'time':df.index}, \\\n",
+ " attrs=ds_Fluor_jan2019[c].attrs) for c in df.columns]\n",
+ "xr.Dataset(dict(zip(df.columns, vals)), \\\n",
+ " attrs=ds_Fluor_jan2019.attrs).to_netcdf('./data/rca/fluorescence/osb_sp_fluor_jan2019.nc')\n",
+ "```\n",
+ "\n",
+ "Spectral irradiance stopgap version: Break out by spectrum (should be dimension of just one file).\n",
+ "\n",
+ "```\n",
+ "spectral_irradiance_source = '/data/rca/irradiance/'\n",
+ "spectral_irradiance_data = 'osb_sp_spkir_2019.nc'\n",
+ "ds_spectral_irradiance = xr.open_dataset(spectral_irradiance_source + spectral_irradiance_data)\n",
+ "ds_spectral_irradiance\n",
+ "time_jan1, time_feb1 = dt64('2019-01-01'), dt64('2019-02-01')\n",
+ "ds_Irr_jan2019 = ds_spectral_irradiance.sel(time=slice(time_jan1, time_feb1))\n",
+ "df = [ds_Irr_jan2019.sel(spectra=s).to_dataframe().resample(\"1Min\").mean() for s in ds_Irr_jan2019.spectra]\n",
+ "r = [xr.Dataset(dict(zip(q.columns, \n",
+ " [xr.DataArray(data=q[c], dims=['time'], coords={'time':q.index}, \\\n",
+ " attrs=ds_Irr_jan2019[c].attrs) for c in q.columns] \\\n",
+ " ) ), \n",
+ " attrs=ds_Irr_jan2019.attrs)\n",
+ " for q in df]\n",
+ "for i in range(7): r[i].to_netcdf('./data/rca/irradiance/osb_sp_irr_spec' + str(i) + '.nc')\n",
+ "```\n",
+ "\n",
+ "\n",
+ "Spectral irradiance related skeleton code showing use of `.isel(spectra=3)`: \n",
+ "\n",
+ "\n",
+ "```\n",
+ "ds = ds_spkir.sel(time=slice(time0, time1))\n",
+ "da_depth = ds.int_ctd_pressure.resample(time='1Min').mean()\n",
+ "dsbar = ds.resample(time='1Min').mean()\n",
+ "dsstd = ds.resample(time='1Min').std()\n",
+ "dsbar.spkir_downwelling_vector.isel(spectra=3).plot()\n",
+ "\n",
+ "\n",
+ "plot_base_dimension = 4\n",
+ "indices = [0, 1, 2, 3, 4, 5, 6]\n",
+ "n_indices = len(indices)\n",
+ "da_si, da_st = [], []\n",
+ "\n",
+ "\n",
+ "for idx in indices: \n",
+ " da_si.append(dsbar.spkir_downwelling_vector.isel(spectra=idx))\n",
+ " da_st.append(dsstd.spkir_downwelling_vector.isel(spectra=idx))\n",
+ "\n",
+ "\n",
+ "fig, axs = plt.subplots(n_indices, 2, figsize=(4*plot_base_dimension, plot_base_dimension*n_indices), /\n",
+ " sharey=True, tight_layout=True)\n",
+ "\n",
+ "\n",
+ "axs[0][0].scatter(da_si[0], da_depth, marker=',', s=1., color='k') \n",
+ "axs[0][0].set(ylim = (200., 0.), xlim = (-.03, .03), title='spectral irradiance averaged')\n",
+ "axs[0][1].scatter(da_st[0], da_depth, marker=',', s=1., color='r')\n",
+ "axs[0][1].set(ylim = (200., 0.), xlim = (0., .002), title='standard deviation')\n",
+ "\n",
+ "\n",
+ "for i in range(1, n_indices):\n",
+ " axs[i][0].scatter(da_si[i], da_depth, marker=',', s=1., color='k')\n",
+ " axs[i][0].set(ylim = (200., 0.), xlim = (-.03, .03))\n",
+ " axs[i][1].scatter(da_st[i], da_depth, marker=',', s=1., color='r')\n",
+ " axs[i][1].set(ylim = (200., 0.), xlim = (0., .002))\n",
+ "```\n",
+ "\n",
+ "Code for PAR\n",
+ "\n",
+ "```\n",
+ "par_source = '/data/rca/par/'\n",
+ "par_data = 'osb_sp_parad_2019.nc'\n",
+ "ds_par = xr.open_dataset(par_source + par_data)\n",
+ "time_jan1 = dt64('2019-01-01')\n",
+ "time_feb1 = dt64('2019-02-01')\n",
+ "ds_par_jan2019 = ds_par.sel(time=slice(time_jan1, time_feb1))\n",
+ "df = ds_par_jan2019.to_dataframe().resample(\"1Min\").mean()\n",
+ "vals = [xr.DataArray(data=df[c], dims=['time'], coords={'time':df.index}, attrs=ds_par_jan2019[c].attrs) for c in df.columns]\n",
+ "ds_par_jan2019_1Min = xr.Dataset(dict(zip(df.columns, vals)), attrs=ds_par_jan2019.attrs)\n",
+ "osb_par_nc_file = \"./data/rca/par/osb_sp_par_jan2019.nc\"\n",
+ "ds_par_jan2019_1Min.to_netcdf(osb_par_nc_file)\n",
+ "```\n",
+ "\n",
+ "PAR view: during shallow profiler rise/fall sequences\n",
+ "\n",
+ "```\n",
+ "t0, t1 = '2019-07-17T13', '2019-07-18T05'\n",
+ "t0, t1 = '2019-07-17T18:40', '2019-07-17T19:40'\n",
+ "t0, t1 = '2019-07-17T21', '2019-07-17T23:00' # These are the nitrate profiles\n",
+ "t0, t1 = '2019-07-18T21', '2019-07-18T23:00'\n",
+ "t0, t1 = '2019-07-19T21', '2019-07-19T23:00'\n",
+ "t0, t1 = '2019-07-17T18:40', '2019-07-17T19:40' # These are the profiles prior to nitrate\n",
+ "t0, t1 = '2019-07-18T18:40', '2019-07-18T19:40'\n",
+ "t0, t1 = '2019-07-19T18:40', '2019-07-19T19:40'\n",
+ "da = ds_parad.sel(time=slice(t0, t1)).par_counts_output\n",
+ "p=da.plot.line(marker='o', figsize = (14,8), markersize=1, yincrease = True)\n",
+ "```\n",
+ "\n",
+ "Staged 'nitrate' profile compared with 'normal' profile\n",
+ "\n",
+ "```\n",
+ "t0, t1 = '2019-07-19T20:30', '2019-07-19T23:50' # USE THIS!! This is a good nitrate profile time bracket\n",
+ "t0, t1 = '2019-07-19T18:40', '2019-07-19T19:40'\n",
+ "da = ds_parad.sel(time=slice(t0, t1)).int_ctd_pressure\n",
+ "p=da.plot.line(marker='o', figsize = (14,8), markersize=1, yincrease = False)\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "formed-moses",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Event handling and debugging\n",
+ "\n",
+ "\n",
+ "See the Annotate notebook on creating an event handler for interactivity. \n",
+ "\n",
+ "\n",
+ "> Key: Declare an event handler variable as `global`. It can now be examined in a \n",
+ "subsequent cell.\n",
+ "\n",
+ "\n",
+ "### Dual-purpose axis\n",
+ "\n",
+ "\n",
+ "Place two charts adjacent with the same y-axis (say depth). Now combine them, trading off\n",
+ "condensed information with clutter. This is done using the `.twiny()` or `.twinx()` methods. \n",
+ "See the BioOptics notebook for examples. \n",
+ "\n",
+ "\n",
+ "### Grid of charts\n",
+ "\n",
+ "\n",
+ "This is example code for time-series data. It sets up a 3 x 3 grid of charts. These are matched to a 2-D set of\n",
+ "axes (the 'a' variable) with both the scatter() and plot() constructs.\n",
+ "\n",
+ "```\n",
+ "rn = range(9); rsi = range(7)\n",
+ "\n",
+ "p,a=plt.subplots(3, 3, figsize=(14,14)) # first 3 is vertical count, second 3 is horizontal count\n",
+ "\n",
+ "plt.rcParams.update({'font.size': 10})\n",
+ "\n",
+ "a[0,0].plot(ctdF.time, ctdF.depth, color='r'); a[0,0].set(ylim=(200.,0.), title='Depth')\n",
+ "a[0,1].plot(ctdF.time, ctdF.salinity, color='k'); a[0,1].set(title='Salinity')\n",
+ "a[0,2].plot(ctdF.time, ctdF.temperature, color='b'); a[0,2].set(title='Temperature')\n",
+ "a[1,0].plot(ctdF.time, ctdF.dissolved_oxygen, color='b'); a[1,0].set(title='Dissolved Oxygen')\n",
+ "a[1,1].scatter(phF.time.values, phF.ph_seawater.values, color='r'); a[1,1].set(title='pH')\n",
+ "a[1,2].scatter(nitrateF.time.values, nitrateF.scn.values, color='k'); a[1,2].set(title='Nitrate')\n",
+ "a[2,0].plot(parF.time, parF.par_counts_output, color='k'); a[2,0].set(title='Photosynthetic Light')\n",
+ "a[2,1].plot(fluorF.time, fluorF.fluorometric_chlorophyll_a, color='b'); a[2,1].set(title='Chlorophyll')\n",
+ "a[2,2].plot(siF.time, siF.si0, color='r'); a[2,2].set(title='Spectral Irradiance')\n",
+ "\n",
+ "a[2,0].text(dt64('2017-08-21T07:30'), 155., 'local midnight', rotation=90, fontsize=15, color='blue', fontweight='bold')\n",
+ "a[2,2].text(dt64('2017-08-21T07:30'), 4.25, 'local midnight', rotation=90, fontsize=15, color='blue', fontweight='bold')\n",
+ "\n",
+ "tFmt = mdates.DateFormatter(\"%H\") # an extended format for strftime() is \"%d/%m/%y %H:%M\"\n",
+ "t0, t1 = ctdF.time[0].values, ctdF.time[-1].values # establish same time range for each chart\n",
+ "tticks = [dt64('2017-08-21T06:00'), dt64('2017-08-21T12:00'), dt64('2017-08-21T18:00')]\n",
+ "\n",
+ "for i in rn: j, k = i//3, i%3; a[j, k].set(xlim=(t0, t1),xticks=tticks); a[j, k].xaxis.set_major_formatter(tFmt)\n",
+ "print('')\n",
+ "```\n",
+ "\n",
+ "\n",
+ "Please note that Software Carpentry (Python) uses a post-facto approach to axes. \n",
+ "In what follows there is implicit use of numpy 'collapse data along a particular\n",
+ "dimension' using the `axis` keyword. So this is non-trivial code; but main point \n",
+ "it shows adding axes to the figure.\n",
+ "\n",
+ "```\n",
+ "fig = plt.figure(figsize=(10,3))\n",
+ "\n",
+ "axes1 = fig.add_subplot(1,3,1)\n",
+ "axes2 = fig.add_subplot(1,3,2)\n",
+ "axes3 = fig.add_subplot(1,3,3)\n",
+ "\n",
+ "avg_data = numpy.mean(data, axis=0)\n",
+ "min_data = numpy.min(data, axis=0)\n",
+ "max_data = numpy.max(data, axis=0)\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "simple-contributor",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "### Making animations\n",
+ "\n",
+ "[Top](#Introduction)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1bca32a7",
+ "metadata": {},
+ "source": [
+ "This section was lifted from the BioOptics.ipynb notebook and simplified. It illustrates **overloading** a chart to \n",
+ "show multiple sensor profiles evolving over time (frames). It also illustrates using markers along a line plot to\n",
+ "emphasize observation spacing."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "qualified-content",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# This code creates the animation; requires some time so it is commented out for now.\n",
+ "# anim = animation.FuncAnimation(fig, AnimateChart, init_func=AnimateInit, \\\n",
+ "# frames=nframes, interval=250, blit=True, repeat=False)\n",
+ "#\n",
+ "# Use 'HTML(anim.to_html5_video())'' for direct playback\n",
+ "# anim.save(this_dir + '/Images/animations/multisensor_animation.mp4')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "773fe8b2",
+ "metadata": {},
+ "source": [
+ "### 3D Charts\n",
+ "\n",
+ "First chart encodes data as shade of green.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ae9dd3bc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "from mpl_toolkits import mplot3d\n",
+ "import numpy as np\n",
+ "%matplotlib inline\n",
+ "\n",
+ "zline = np.linspace(0,15,1000)\n",
+ "xline = np.sin(zline)\n",
+ "yline = np.cos(zline)\n",
+ "\n",
+ "zdata = 15*np.random.random(100)\n",
+ "xdata = np.sin(zdata) + 0.1 * np.random.randn(100)\n",
+ "ydata = np.sin(zdata) + 0.1 * np.random.randn(100)\n",
+ "\n",
+ "ax=plt.axes(projection='3d')\n",
+ "ax.plot3D(xline, yline, zline, 'gray')\n",
+ "ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='Greens')\n",
+ "ax.view_init(20,35)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2676a379",
+ "metadata": {},
+ "source": [
+ "Second chart derives data from the busy beaver algorithm: An automaton moving about on \n",
+ "a 2D plane. The rules are encoded as changes to a velocity vector v; and cells have a \n",
+ "binary 'visit' status that toggles on each arrival."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "16534102",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nt, ngrid = 500000, 61\n",
+ "Z, W, x, y, v = np.zeros((ngrid,ngrid), dtype=int), np.zeros((ngrid,ngrid), dtype=int), ngrid//2, ngrid//2, (1, 0)\n",
+ "\n",
+ "def newv(b, v):\n",
+ " if b == 0: return (v[1], -v[0])\n",
+ " else: return (-v[1], v[0])\n",
+ "\n",
+ "for n in range(nt):\n",
+ " if x < 0 or y < 0 or x >= ngrid or y >= ngrid: break\n",
+ " v = newv(Z[x][y], v)\n",
+ " Z[x][y] = 1 - Z[x][y]\n",
+ " W[x][y] += 1\n",
+ " x += v[0]\n",
+ " y += v[1]\n",
+ "\n",
+ "fig = plt.figure(figsize=(8,8))\n",
+ "ax = plt.axes(projection='3d')\n",
+ "xg, yg = np.linspace(0, ngrid - 1, ngrid), np.linspace(0, ngrid - 1, ngrid)\n",
+ "X, Y = np.meshgrid(xg, yg)\n",
+ "ax.plot_wireframe(X,Y,W,color='red')\n",
+ "ax.view_init(40,-80)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "568aeb1f",
+ "metadata": {},
+ "source": [
+ "### To do\n",
+ "\n",
+ "\n",
+ "Print some 3D view with a rotating reference view. Write each view to an image file; \n",
+ "and use that in a flip book sense to create an animation. See the animation code \n",
+ "preceding this section, above, and the bio-optics notebook.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c230db14",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Binder-friendly playback\n",
+ "\n",
+ "\n",
+ "The cell above creates an animation file that is stored within this repository. \n",
+ "The cell below plays it back (for example in **binder**) to show multiple profile animations.\n",
+ "Nitrate is intermittent, appearing as a sky-blue line in 2 of every 9\n",
+ "frames. The remaining sensors are present in each frame.\n",
+ "\n",
+ "\n",
+ "There animation begins March 1 2021 and proceeds at a rate of nine frames (profiles) per day.\n",
+ "Change playback speed using the video settings control at lower right."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "raised-romantic",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c140be9c-5de3-4f74-bd73-096763176bbb",
+ "metadata": {},
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "id": "neural-series",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Pandas Series and DataFrames\n",
+ "\n",
+ "[Top](#Introduction)\n",
+ "\n",
+ "### Summary\n",
+ "\n",
+ "### DataFrames\n",
+ "\n",
+ "DataFrames:\n",
+ "\n",
+ "* constructor takes `data=` and both `index` and `columns` arguments... \n",
+ " * ...2 dimensions only: higher dimensions and they say 'use XArray'\n",
+ " * ...and switching required a `.T` transpose\n",
+ "* indexing by column and row header values, separated as in `[column_header][row_header]`\n",
+ " * as this reverses order from ndarrays: Better confirm... seems to be the case\n",
+ " * skip index/columns: defaults to integers.\n",
+ " \n",
+ "Below this section we go into n-dimensional arrays in Numpy, the *ndarray*. Here we take this \n",
+ "for granted and look at the relationship with DataFrames."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "70d19076-e075-4d46-b0a0-d0715ffb18e8",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "ndarray from a list of lists (notice no comma delimiter):\n",
+ "\n",
+ " [['l' 'i' 's' 't' '1']\n",
+ " ['s' 'c' 'n' 'd' '2']\n",
+ " ['t' 'h' 'r' 'd' '3']] \n",
+ "\n",
+ "and indexing comparison: first [0][2] then [2][0]: s t\n",
+ "\n",
+ "and tuplesque indexing [0, 2] or [2, 0] equivalently gives: s t\n",
+ "\n",
+ "So ndarrays index [slow][fast] equivalent to [row][column]\n",
+ "\n",
+ "\n",
+ "\n",
+ "Now let's create a 2D ndarray of zeros that is 3 rows by 5 columns\n",
+ "used np.zeros((3,5)) to set up 3 rows / 5 columns of zeros\n",
+ "set [0,1] to 3 and [1][0] to 4:\n",
+ "\n",
+ "[[0. 3. 0. 0. 0.]\n",
+ " [4. 0. 0. 0. 0.]\n",
+ " [0. 0. 0. 0. 0.]]\n",
+ "\n",
+ "\n",
+ "So that first index is row, second index is column\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "###################\n",
+ "#\n",
+ "# A micro study of ndarray to DataFrame translation\n",
+ "#\n",
+ "###################\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "\n",
+ "# Here is an ndarray construction from a built list of lists (not used in what follows): \n",
+ "# arr = np.array([range(i, i+5) for i in [2, 4, 6]]) \n",
+ "# ... where the range() runs across columns; 2 4 6 are rows\n",
+ "\n",
+ "# ndarray construction: Notice all list elements are of the same type (strings)\n",
+ "arr = np.array([['l','i','s','t','1'],['s','c','n','d','2'],['t','h','r','d', '3']])\n",
+ "\n",
+ "print('\\nndarray from a list of lists (notice no comma delimiter):\\n\\n', arr, \\\n",
+ " '\\n\\nand indexing comparison: first [0][2] then [2][0]:', arr[0][2], arr[2][0]) \n",
+ "print('\\nand tuplesque indexing [0, 2] or [2, 0] equivalently gives:', arr[0,2], arr[2,0])\n",
+ "print('\\nSo ndarrays index [slow][fast] equivalent to [row][column]\\n\\n\\n')\n",
+ "\n",
+ "print(\"Now let's create a 2D ndarray of zeros that is 3 rows by 5 columns\")\n",
+ "\n",
+ "z = np.zeros((3,5))\n",
+ "\n",
+ "print(\"used np.zeros((3,5)) to set up 3 rows / 5 columns of zeros\")\n",
+ "print(\"set [0,1] to 3 and [1][0] to 4:\\n\")\n",
+ "z[0,1]=3\n",
+ "z[1][0]=4\n",
+ "print(z)\n",
+ "print('\\n\\nSo that first index is row, second index is column')\n",
+ "print('\\n\\n\\n\\n')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "9f7268d6-98de-4611-b140-8a545aaf9e9a",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "ndarray from a list of lists (notice no comma delimiter):\n",
+ "\n",
+ " [['l' 'i' 's' 't' '1']\n",
+ " ['s' 'c' 'n' 'd' '2']\n",
+ " ['t' 'h' 'r' 'd' '3']] \n",
+ "\n",
+ "and indexing comparison: first [0][2] then [2][0]: s t\n",
+ "\n",
+ "and tuplesque indexing [0, 2] or [2, 0] equivalently gives: s t\n",
+ "\n",
+ "So ndarrays index [slow][fast] equivalent to [row][column]\n",
+ "\n",
+ "\n",
+ "Moving on to DataFrames:\n",
+ "\n",
+ "\n",
+ " col_a col_b col_c col_d col_e\n",
+ "2row l i s t 1\n",
+ "4row s c n d 2\n",
+ "6row t h r d 3 \n",
+ "\n",
+ "is a DataFrame from the ndarray; so now index [\"col_c\"][\"6row\"]: r\n",
+ "\n",
+ "Here is a Dataframe from a transpose of the ndarray\n",
+ "\n",
+ " 2row 4row 6row\n",
+ "col_a l s t\n",
+ "col_b i c h\n",
+ "col_c s n r\n",
+ "col_d t d d\n",
+ "col_e 1 2 3 \n",
+ "\n",
+ "indexing 2row then col_e: 1\n",
+ "\n",
+ "So the column of a DataFrame is indexed first, then the row: Reverses the sense of the 2D ndarray.\n",
+ "\n",
+ "Now skipping the \"index=\"\" argument so the row labels default to integers:\n",
+ "\n",
+ " col_a col_b col_c col_d col_e\n",
+ "0 l i s t 1\n",
+ "1 s c n d 2\n",
+ "2 t h r d 3 \n",
+ "\n",
+ "...so now indexing [\"col_d\"][0]: t \n",
+ "\n",
+ " 0 1 2 3 4\n",
+ "2row l i s t 1\n",
+ "4row s c n d 2\n",
+ "6row t h r d 3 \n",
+ "\n",
+ "having done it the other way: used index= but not columns=. Here is element [0][\"4row\"]: s\n",
+ "\n",
+ "\n",
+ "Starting from an XArray Dataset and using .to_dataframe() we arrive at a 2D structure.\n",
+ "\n",
+ "For example: df = ds_CTD.seawater_pressure.to_dataframe()\n",
+ " \n",
+ "The problem is that the resulting dataframe may not be indexed (row sense) using integers. A fix\n",
+ "is necessary to override the index and columns attributes of the dataframe, as in:\n",
+ " \n",
+ " df.index=range(len(df))\n",
+ " df.columns=range(1)\n",
+ " \n",
+ "results in a dataframe that one can index with integers [0] for column first then [n] for row.\n",
+ "This example came from the profile time series analysis to get ascent start times and so on.\n",
+ "The problem is it is a case of too much machinery. It is far simpler to use a pandas Series.\n"
+ ]
+ }
+ ],
+ "source": [
+ "print('Moving on to DataFrames:\\n\\n')\n",
+ "\n",
+ "rowlist=[\"2row\", \"4row\", \"6row\"]\n",
+ "columnlist = [\"col_a\", \"col_b\", \"col_c\", \"col_d\", \"col_e\"]\n",
+ "df = pd.DataFrame(data=arr, index=rowlist, columns=columnlist)\n",
+ "\n",
+ "print(df, '\\n\\nis a DataFrame from the ndarray; so now index [\"col_c\"][\"6row\"]:', df['col_c']['6row'])\n",
+ "\n",
+ "df = pd.DataFrame(data=arr.T, index=columnlist, columns=rowlist)\n",
+ "\n",
+ "print('\\nHere is a Dataframe from a transpose of the ndarray\\n\\n', df, \\\n",
+ " '\\n\\nindexing 2row then col_e:', df['2row']['col_e'])\n",
+ "print('\\nSo the column of a DataFrame is indexed first, then the row: Reverses the sense of the 2D ndarray.\\n')\n",
+ "print('Now skipping the \"index=\"\" argument so the row labels default to integers:\\n')\n",
+ "\n",
+ "df = pd.DataFrame(data=arr, columns=columnlist)\n",
+ "\n",
+ "print(df, '\\n\\n...so now indexing [\"col_d\"][0]:', df['col_d'][0], '\\n')\n",
+ "\n",
+ "df = pd.DataFrame(data=arr, index=rowlist)\n",
+ "\n",
+ "print(df, '\\n\\nhaving done it the other way: used index= but not columns=. Here is element [0][\"4row\"]:', \\\n",
+ " df[0]['4row'])\n",
+ "\n",
+ "\n",
+ "print('\\n\\nStarting from an XArray Dataset and using .to_dataframe() we arrive at a 2D structure.\\n')\n",
+ "print('For example: df = ds_CTD.seawater_pressure.to_dataframe()')\n",
+ "print(' ')\n",
+ "print('The problem is that the resulting dataframe may not be indexed (row sense) using integers. A fix')\n",
+ "print('is necessary to override the index and columns attributes of the dataframe, as in:')\n",
+ "print(' ')\n",
+ "print(' df.index=range(len(df))')\n",
+ "print(' df.columns=range(1)')\n",
+ "print(' ')\n",
+ "print('results in a dataframe that one can index with integers [0] for column first then [n] for row.')\n",
+ "print('This example came from the profile time series analysis to get ascent start times and so on.')\n",
+ "print('The problem is it is a case of too much machinery. It is far simpler to use a pandas Series.')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "99d6a6de",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "### Selecting based on a range\n",
+ "\n",
+ "\n",
+ "Suppose we have a DataFrame with a column of timestamps over a broad time range and we would like to focus on only a subset. \n",
+ "One approach would be to generate a smaller dataframe that meets the small time criterion and iterate over that.\n",
+ "\n",
+ "The following cell builds a pandas DataFrame with a date column; then creates a subset DataFrame where only rows in\n",
+ "a time range are preserved. This is done twice: First using conditional logic and then using the same with '.loc'. \n",
+ "('loc' and 'iloc' are location-based indexing, the first relying on labels and the second on integer position.)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a58cc95e",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[numpy.datetime64('2020-10-11') 7 13 6]\n",
+ " [numpy.datetime64('2020-10-12') 7 9 6]\n",
+ " [numpy.datetime64('2020-10-13') 7 8 6]\n",
+ " [numpy.datetime64('2020-10-14') 7 5 6]\n",
+ " [numpy.datetime64('2020-10-15') 7 11 6]]\n",
+ "\n",
+ "arr[0][2] then [2][0]: 13 2020-10-13\n",
+ "\n",
+ "and tuplesque indexing [0, 2] or [2, 0] equivalently gives: 13 2020-10-13\n",
+ "\n",
+ "using conditionals:\n",
+ "\n",
+ " date data1 data2 data3\n",
+ "day3 2020-10-13 7 8 6\n",
+ "day4 2020-10-14 7 5 6 \n",
+ "\n",
+ "\n",
+ "using loc:\n",
+ "\n",
+ " date data1 data2 data3\n",
+ "day3 2020-10-13 7 8 6\n",
+ "day4 2020-10-14 7 5 6\n",
+ "\n",
+ "notice the results are identical; so it is an open question \"Why use `loc`?\"\n"
+ ]
+ }
+ ],
+ "source": [
+ "from numpy import datetime64 as dt64, timedelta64 as td64\n",
+ "\n",
+ "t0=dt64('2020-10-11')\n",
+ "t1=dt64('2020-10-12')\n",
+ "t2=dt64('2020-10-13')\n",
+ "t3=dt64('2020-10-14')\n",
+ "t4=dt64('2020-10-15')\n",
+ "\n",
+ "r0 = dt64('2020-10-12')\n",
+ "r1 = dt64('2020-10-15')\n",
+ "\n",
+ "arr = np.array([[t0,7,13,6],[t1,7,9,6],[t2,7,8,6],[t3,7,5,6],[t4,7,11,6]])\n",
+ "\n",
+ "print(arr)\n",
+ "print('\\narr[0][2] then [2][0]:', arr[0][2], arr[2][0]) \n",
+ "print('\\nand tuplesque indexing [0, 2] or [2, 0] equivalently gives:', arr[0,2], arr[2,0])\n",
+ "\n",
+ "rowlist = [\"day1\", \"day2\",\"day3\",\"day4\",\"day5\"]\n",
+ "columnlist = [\"date\", \"data1\", \"data2\", \"data3\"]\n",
+ "df = pd.DataFrame(data=arr, index=rowlist, columns=columnlist)\n",
+ "\n",
+ "\n",
+ "df_conditional = df[(df['date'] > r0) & (df['date'] < r1)]\n",
+ "print('\\nusing conditionals:\\n\\n', df_conditional, '\\n')\n",
+ "\n",
+ "\n",
+ "df_loc = df.loc[(df['date'] > r0) & (df['date'] < r1)]\n",
+ "print('\\nusing loc:\\n\\n', df_loc)\n",
+ "\n",
+ "print('\\nnotice the results are identical; so it is an open question \"Why use `loc`?\"')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "celtic-following",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## Numpy ndarrays\n",
+ "\n",
+ "\n",
+ "* do not have row and column headers; whereas pandas DataFrames do have typed headers\n",
+ "* indexing has an equivalence of `[2][0]` to `[2,0]` \n",
+ " * The latter (with comma) is the presented way in PDSH\n",
+ " * This duality does not work for DataFrames\n",
+ "* has row-then-column index order...\n",
+ " * ....with three rows in `[['l','i','s','t','1'],['s','c','n','d','2'],['t','h','r','d','3']]` \n",
+ "* has slice by dimension as `start:stop:step` by default `0, len (this dimension), 1` \n",
+ " * ...exception: when `step` is negative `start` and `stop` are reversed\n",
+ " * ...multi-dimensional slices separated by commas\n",
+ " \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "statistical-trade",
+ "metadata": {},
+ "source": [
+ "#### [Top](#Introduction) and [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Time\n",
+ "\n",
+ "[Top](#Introduction)\n",
+ "\n",
+ "\n",
+ "### Summary\n",
+ "\n",
+ "There is time in association with data (when a sample was recorded) and time in association with\n",
+ "code development (how long did this cell take to run?) Let's look at both.\n",
+ "\n",
+ "\n",
+ "### Sample timing\n",
+ "\n",
+ "See PDSH-189. There are two time mechanisms in play: Python's built-in `datetime` and an improvement called\n",
+ "`datetime64` from **numpy** that enables *arrays* of dates, i.e. time series. \n",
+ "\n",
+ "\n",
+ "Consider these two ways of stipulating time slice arguments for `.sel()` applied to a DataSet.\n",
+ "First: Use a datetime64 with precision to minutes (or finer).\n",
+ "Second: Pass strings that are interpreted as days, inclusive. In pseudo-code: \n",
+ "\n",
+ "```\n",
+ "if do_precision: \n",
+ " t0 = dt64('2019-06-01T00:00')\n",
+ " t1 = dt64('2019-06-01T05:20')\n",
+ " dss = ds.sel(time=slice(t0, t1)) \n",
+ "else:\n",
+ " day1 = '24'\n",
+ " day2 = '27' # will be 'day 27 inclusive' giving four days of results\n",
+ " dss = ds.sel(time=slice('2019-06-' + day1, '2019-08-' + day2))\n",
+ "\n",
+ "len(dss.time)\n",
+ "```\n",
+ "\n",
+ "### Execution timing\n",
+ "\n",
+ "Time of execution in seconds: \n",
+ "\n",
+ "```\n",
+ "from time import time\n",
+ "\n",
+ "toc = time()\n",
+ "for i in range(12): j = i + 1\n",
+ "tic = time()\n",
+ "print(tic - toc)\n",
+ "\n",
+ "7.82012939453125e-05\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "native-sellers",
+ "metadata": {},
+ "source": [
+ "#### [Introduction](#Introduction), [Contents](#Contents)\n",
+ "\n",
+ "\n",
+ "## ipywidgets\n",
+ "\n",
+ "\n",
+ "### Summary"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "sticky-toolbox",
+ "metadata": {},
+ "source": [
+ "#### [Introduction](#Introduction) and [Contents](#Contents)\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Holoview\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "turkish-wesley",
+ "metadata": {},
+ "source": [
+ "## Instruments\n",
+ "\n",
+ "\n",
+ "### Specifically spectrophotometer (SP) and Nitrate\n",
+ "\n",
+ "\n",
+ "### Summary\n",
+ "\n",
+ "\n",
+ "The SP runs on ascent only, at about 3.7 samples per second. Compare nitrate that also runs \n",
+ "on ascent only at about 3 samples per minute. Nitrate data is fairly straightforward; SP \n",
+ "data is chaotic/messy. The objective is to reduce the SP to something interpretable.\n",
+ "\n",
+ "\n",
+ "### Deconstructing data: process pattern\n",
+ "\n",
+ "\n",
+ "- `ds = xr.open_dataset(fnm)` \n",
+ " - Data dispersed across files: Variant + wildcard: `ds = xr.open_mfdataset('data_with_*_.nc')`\n",
+ "- `obs` dimensional coordinate creates degeneracy over multiple files\n",
+ " - Use `.swap_dims` to swap time for `obs`\n",
+ "- `ds.time[0].values, ds.time[-1].values` gives a timespan but nothing about duty cycles\n",
+ " - 2019 spectrophotometer data at Oregon Slope Base: 86 channels, 7 million samples\n",
+ " - ...leading to...\n",
+ " - Only operates during midnight and noon ascent; at 3.7 samples per second\n",
+ " - Optical absorbance and beam attenuation are the two data types\n",
+ " - Data has frequent dropouts over calendar time\n",
+ " - Data has spikes that tend to register across all 86 channels\n",
+ " - Very poor documentation; even the SME report is cursory\n",
+ "\n",
+ "\n",
+ " \n",
+ "### Nitrate \n",
+ "\n",
+ " \n",
+ "This code follows suit the spectrophotometer. It is simpler because there is only a nitrate value \n",
+ "and no wavelength channel. \n",
+ "\n",
+ " \n",
+ "I kept the pressure bins the same even though the nitrate averates about 3 three samples or less per minute\n",
+ "during a 70 minute ascent. That's about three meters per minute so one sample per meter. Since the \n",
+ "spectrophotometer bin depth is 0.25 meters there are necessarily a lot of empty bins (bins with no data)\n",
+ "for the nitrate profile. \n",
+ "\n",
+ " \n",
+ "### Two open issues\n",
+ "\n",
+ "\n",
+ "A curious artifact of the situation is from a past bias: I had understood that the SCIP makes pauses \n",
+ "on descent to accommodate the nitrate sensor. I may be in error but now it looks like this sensor, \n",
+ "the nitrate sensor, is observing on ascent which is continuous. This leaves open the question of \n",
+ "why the pauses occur on the descent. If I have that right. \n",
+ "\n",
+ "\n",
+ "Finally there are two nitrate signals: 'samp' and 'dark'. This code addresses only 'samp' as 'dark'\n",
+ "is showing nothing of interest. So this is an open issue."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "super-pocket",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Animation in Python is one thing. Animation in a Jupyter notebook is another.\n",
+ "# Animation in binder is yet another. Rather than try and bootstrap a lesson here\n",
+ "# I present a sequence of annotated steps that create an animation, save it as \n",
+ "# an .mp4 file, load it and run it: In a Jupyter notebook of course. Then we\n",
+ "# will see how it does in binder.\n",
+ "\n",
+ "# At some point in working on this I did a conda install ffmpeg. I am not clear \n",
+ "# right now on whether this was necessary or not; I suspect not.\n",
+ "\n",
+ "%matplotlib inline\n",
+ "\n",
+ "# With [the inline] backend activated with this line magic matplotlib command, the output \n",
+ "# of plotting commands is displayed inline within frontends like the Jupyter notebook, \n",
+ "# directly below the code cell that produced it. The resulting plots will then also be stored \n",
+ "# in the notebook document.\n",
+ "\n",
+ "# de rigeur, commented out here as this runs at the top of the notebook\n",
+ "# import numpy as np\n",
+ "# import matplotlib.pyplot as plt\n",
+ "\n",
+ "from matplotlib import animation, rc # animation provides tools to build chart-based animations.\n",
+ " # Each time Matplotlib loads, it defines a runtime configuration (rc) \n",
+ " # containing the default styles for every plot element you create. \n",
+ " # This configuration can be adjusted at any time using \n",
+ " # the plt. ... matplotlibrc file, which you can read about \n",
+ " # in the Matplotlib documentation.\n",
+ "\n",
+ "\n",
+ "from IPython.display import HTML, Video # HTML is ...?...\n",
+ " # Video is used for load/playback\n",
+ "\n",
+ "# First set up the figure, the axis, and the plot element we want to animate\n",
+ "fig, ax = plt.subplots()\n",
+ "\n",
+ "ax.set_xlim(( 0, 2))\n",
+ "ax.set_ylim((-2, 2))\n",
+ "\n",
+ "line, = ax.plot([], [], lw=2)\n",
+ "\n",
+ "# initialization function: plot the background of each frame\n",
+ "def init():\n",
+ " line.set_data([], [])\n",
+ " return (line,)\n",
+ "\n",
+ "# animation function. This is called sequentially\n",
+ "def animate(i):\n",
+ " x = np.linspace(0, 2, 1000)\n",
+ " y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
+ " line.set_data(x, y)\n",
+ " return (line,)\n",
+ "\n",
+ "# call the animator. blit=True means only re-draw the parts that have changed.\n",
+ "anim = animation.FuncAnimation(fig, animate, init_func=init,\n",
+ " frames=100, interval=12, blit=True)\n",
+ "\n",
+ "HTML(anim.to_html5_video())\n",
+ "\n",
+ "# print(anim._repr_html_() is None) will be True\n",
+ "# anim"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "plain-functionality",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def update_line(num, data, line):\n",
+ " line.set_data(data[..., :num])\n",
+ " return line,\n",
+ "\n",
+ "fig1 = plt.figure()\n",
+ "\n",
+ "data = .05 + 0.9*np.random.rand(2, 200)\n",
+ "l, = plt.plot([], [], 'r-') # l, takes the 1-tuple returned by plt.plot() and grabs that first \n",
+ " # and only element; so it de-tuples it\n",
+ "\n",
+ "plt.xlim(0, 1); plt.ylim(0, 1); plt.xlabel('x'); plt.title('test')\n",
+ "\n",
+ "lines_anim = animation.FuncAnimation(fig1, update_line, 200, fargs=(data, l), interval=1, blit=True)\n",
+ "\n",
+ "# fargs are additional arguments to 'update_line()' in addition to the frame number: data and line\n",
+ "# interval is a time gap between frames (guess is milliseconds)\n",
+ "# blit is the idea of modifying only pixels that change from one frame to the next\n",
+ "\n",
+ "# For direct display use this: HTML(line_ani.to_html5_video())\n",
+ "lines_anim.save('./lines_tmp3.mp4') # save the animation to a file\n",
+ "Video(\"./lines_tmp3.mp4\") # One can add , embed=True"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "specific-plumbing",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "fig2 = plt.figure()\n",
+ "\n",
+ "x = np.arange(-9, 10)\n",
+ "y = np.arange(-9, 10).reshape(-1, 1)\n",
+ "base = np.hypot(x, y)\n",
+ "ims = []\n",
+ "for add in np.arange(15):\n",
+ " ims.append((plt.pcolor(x, y, base + add, norm=plt.Normalize(0, 30)),))\n",
+ "\n",
+ "im_ani = animation.ArtistAnimation(fig2, ims, interval=50, repeat_delay=3000,\n",
+ " blit=True)\n",
+ "# To save this second animation with some metadata, use the following command:\n",
+ "# im_ani.save('im.mp4', metadata={'artist':'Guido'})\n",
+ "\n",
+ "HTML(im_ani.to_html5_video())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "responsible-client",
+ "metadata": {},
+ "source": [
+ "## Binder\n",
+ "\n",
+ "[Top](#Introduction)\n",
+ "\n",
+ "* Create a binder badge in the home page `README.md` of the repository. \n",
+ "\n",
+ "```\n",
+ "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh///HEAD)\n",
+ "\n",
+ "```\n",
+ "\n",
+ "* In `/binder` create `environment.yml` to match the working environment\n",
+ " * For this repo as of 10/23/2021 `binder/environment.yml` was: \n",
+ "\n",
+ "\n",
+ "```\n",
+ "channels:\n",
+ " - conda-forge\n",
+ "dependencies:\n",
+ " - python=3\n",
+ " - numpy\n",
+ " - pandas\n",
+ " - matplotlib\n",
+ " - netcdf4\n",
+ " - xarray\n",
+ " - ffmpeg\n",
+ "```\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "89a502fd",
+ "metadata": {},
+ "source": [
+ "## Part 2 Sensors and Instruments\n",
+ "\n",
+ "\n",
+ "#### Code Archive\n",
+ "\n",
+ "\n",
+ "## [Contents](#Contents)\n",
+ "\n",
+ "\n",
+ "* [Nitrate](#Nitrate)\n",
+ "* [Mooring](#Mooring)\n",
+ "* [Spectral Irradiance](#Spectral-Irradiance)\n",
+ "* [Shallow Profiler](#Shallow-Profiler)\n",
+ "* [Coding Environment](#Coding-Environment)\n",
+ "* [OOI Data](#OOI-Data)\n",
+ "* [NetCDF](#NetCDF)\n",
+ "* [Depth And Time](#Depth-And-Time)\n",
+ "* [Data Features](#Data-Features)\n",
+ "* [wget](#wget)\n",
+ "* [Non OOI Data](#Non-OOI-Data)\n",
+ "* [Data Product Levels](#Data-Product-Levels)\n",
+ "* [OOI Terminology](#OOI-Terminology)\n",
+ "* [Diagnostics](#Diagnostics)\n",
+ "\n",
+ "\n",
+ "## Nitrate\n",
+ "\n",
+ "\n",
+ "```\n",
+ "ds_n03dark = xr.open_dataset(\"/data/rca/simpler/osb_sp_nutnr_a_dark_2019.nc\")\n",
+ "ds_n03samp = xr.open_dataset(\"/data/rca/simpler/osb_sp_nutnr_a_sample_2019.nc\")\n",
+ "\n",
+ "warnings.filterwarnings('ignore')\n",
+ "\n",
+ "include_charts = False\n",
+ "\n",
+ "m_strs = ['01', '02', '03', '04', '05', '06', '07', '08', '09'] # relevant 2019 months\n",
+ "m_days = [31, 28, 31, 30, 31, 30, 31, 31, 30] # days per month in 2019\n",
+ "\n",
+ "month_index = 0 # manage time via months and days; 0 is January\n",
+ "month_str = m_strs[month_index] \n",
+ "year_str = '2019'\n",
+ "\n",
+ "n_meters = 200\n",
+ "n_bins_per_meter = 4\n",
+ "halfbin = (1/2) * (1/n_bins_per_meter)\n",
+ "n_pressure_bins = n_meters * n_bins_per_meter\n",
+ "p_bounds = np.linspace(0., n_meters, n_pressure_bins + 1) # 801 bounds: 0., .25, ..., 200. \n",
+ "pressure = np.linspace(halfbin, n_meters - halfbin, n_pressure_bins) # 800 centers: 0.125, ..., 199.875 \n",
+ "nc_upper_bound = 40.\n",
+ "\n",
+ "ndays = m_days[month_index]\n",
+ "ndayplots, dayplotdays = 10, list(range(10))\n",
+ "\n",
+ "l_da_nc_midn, l_da_nc_noon = [], [] # these lists accumulate DataArrays by day\n",
+ "\n",
+ "if include_charts:\n",
+ " fig_height, fig_width, fig_n_across, fig_n_down = 4, 4, 2, ndayplots\n",
+ " fig, axs = plt.subplots(ndayplots, fig_n_across, figsize=(fig_width * fig_n_across, fig_height * fig_n_down), tight_layout=True)\n",
+ "\n",
+ "for day_index in range(ndays):\n",
+ " \n",
+ " day_str = day_of_month_to_string(day_index + 1); date_str = year_str + '-' + month_str + '-' + day_str\n",
+ " this_doy = doy(dt64(date_str))\n",
+ " clear_output(wait = True); print(\"on day\", day_str, 'i.e. doy', this_doy)\n",
+ " midn_start = date_str + 'T07:00:00'\n",
+ " midn_done = date_str + 'T10:00:00'\n",
+ " noon_start = date_str + 'T20:00:00'\n",
+ " noon_done = date_str + 'T23:00:00'\n",
+ "\n",
+ " # pull out OA and BA for both midnight and noon ascents; and swap in pressure for time\n",
+ " ds_midn = ds_n03samp.sel(time=slice(dt64(midn_start), dt64(midn_done))).swap_dims({'time':'int_ctd_pressure'})\n",
+ " ds_noon = ds_n03samp.sel(time=slice(dt64(noon_start), dt64(noon_done))).swap_dims({'time':'int_ctd_pressure'})\n",
+ " \n",
+ " # print('pressures:', ds_midn.int_ctd_pressure.size, ds_noon.int_ctd_pressure.size, '; times:', ds_midn.time.size, ds_noon.time.size) \n",
+ " midn = True if ds_midn.time.size > 0 else False\n",
+ " noon = True if ds_noon.time.size > 0 else False\n",
+ " \n",
+ " if midn:\n",
+ " da_nc_midn = ds_midn.nitrate_concentration.expand_dims({'doy':[this_doy]})\n",
+ " del da_nc_midn['time']\n",
+ " l_da_nc_midn.append(da_nc_midn.sortby('int_ctd_pressure').groupby_bins(\"int_ctd_pressure\", p_bounds, labels=pressure).mean().transpose('int_ctd_pressure_bins', 'doy'))\n",
+ " \n",
+ " if noon:\n",
+ " da_nc_noon = ds_noon.nitrate_concentration.expand_dims({'doy':[this_doy]})\n",
+ " del da_nc_noon['time']\n",
+ " l_da_nc_noon.append(da_nc_noon.sortby('int_ctd_pressure').groupby_bins(\"int_ctd_pressure\", p_bounds, labels=pressure).mean().transpose('int_ctd_pressure_bins', 'doy'))\n",
+ "\n",
+ " if include_charts and day_index in dayplotdays: # if this is a plotting day: Add to the chart repertoire\n",
+ "\n",
+ " dayplotindex = dayplotdays.index(day_index) \n",
+ "\n",
+ " if midn:\n",
+ " axs[dayplotindex][0].scatter(l_da_nc_midn[-1], pressure, marker=',', s=2., color='r') \n",
+ " axs[dayplotindex][0].set(xlim = (.0, nc_upper_bound), ylim = (200., 0.), title='NC midnight')\n",
+ " axs[dayplotindex][0].scatter(ds_midn.nitrate_concentration, ds_midn.int_ctd_pressure, marker=',', s=1., color='b'); \n",
+ " \n",
+ " if noon:\n",
+ " axs[dayplotindex][1].scatter(l_da_nc_noon[-1], pressure, marker=',', s=2., color='g')\n",
+ " axs[dayplotindex][1].set(xlim = (.0, nc_upper_bound), ylim = (200., 0.), title='NC noon')\n",
+ " axs[dayplotindex][1].scatter(ds_noon.nitrate_concentration, ds_noon.int_ctd_pressure, marker=',', s=1., color='k'); \n",
+ "\n",
+ "save_figure = False\n",
+ "if save_figure: fig.savefig('/home/ubuntu/chlorophyll/images/misc/nitrate_2019_JAN_1_to_10.png')\n",
+ "\n",
+ "save_nitrate_profiles = False\n",
+ "\n",
+ "if save_nitrate_profiles: \n",
+ " ds_nc_midn = xr.concat(l_da_nc_midn, dim=\"doy\").to_dataset(name='nitrate_concentration')\n",
+ " ds_nc_noon = xr.concat(l_da_nc_noon, dim=\"doy\").to_dataset(name='nitrate_concentration')\n",
+ "\n",
+ " ds_nc_midn.to_netcdf(\"/data1/nutnr/nc_midn_2019_01.nc\")\n",
+ " ds_nc_noon.to_netcdf(\"/data1/nutnr/nc_noon_2019_01.nc\")\n",
+ "```\n",
+ "\n",
+ "#### [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## Mooring\n",
+ "\n",
+ "```\n",
+ "\"\"\"\n",
+ "Stand-alone code to plot a user-specified mooring extraction.\n",
+ "\"\"\"\n",
+ "from pathlib import Path\n",
+ "moor_fn = Path('/Users/pm8/Documents/LO_output/extract/cas6_v3_lo8b/'\n",
+ " +'moor/ooi/CE02_2018.01.01_2018.12.31.nc')\n",
+ "\n",
+ "import xarray as xr\n",
+ "import matplotlib.pyplot as plt\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "\n",
+ "# load everything using xarray\n",
+ "ds = xr.load_dataset(moor_fn)\n",
+ "ot = ds.ocean_time.values\n",
+ "ot_dt = pd.to_datetime(ot)\n",
+ "t = (ot_dt - ot_dt[0]).total_seconds().to_numpy()\n",
+ "T = t/86400 # time in days from start\n",
+ "print('time step of mooring'.center(60,'-'))\n",
+ "print(t[1])\n",
+ "print('time limits'.center(60,'-'))\n",
+ "print('start ' + str(ot_dt[0]))\n",
+ "print('end ' + str(ot_dt[-1]))\n",
+ "print('info'.center(60,'-'))\n",
+ "VN_list = []\n",
+ "for vn in ds.data_vars:\n",
+ " print('%s %s' % (vn, ds[vn].shape))\n",
+ " VN_list.append(vn)\n",
+ " \n",
+ "# populate lists of variables to plot\n",
+ "vn2_list = ['zeta']\n",
+ "if 'shflux' in VN_list:\n",
+ " vn2_list += ['shflux', 'swrad']\n",
+ "vn3_list = []\n",
+ "if 'salt' in VN_list:\n",
+ " vn3_list += ['salt', 'temp']\n",
+ "if 'oxygen' in VN_list:\n",
+ " vn3_list += ['oxygen']\n",
+ "\n",
+ "# plot time series using a pandas DataFrame\n",
+ "df = pd.DataFrame(index=ot)\n",
+ "for vn in vn2_list:\n",
+ " df[vn] = ds[vn].values\n",
+ "for vn in vn3_list:\n",
+ " # the -1 means surface values\n",
+ " df[vn] = ds[vn][:, -1].values\n",
+ "\n",
+ "plt.close('all')\n",
+ "df.plot(subplots=True, figsize=(16,10))\n",
+ "plt.show()\n",
+ "```\n",
+ "\n",
+ "#### [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## Spectral Irradiance\n",
+ "\n",
+ "### Introduction\n",
+ "\n",
+ " \n",
+ "This notebook should run in **`binder`**. It uses small datasets stored within this repo.\n",
+ "\n",
+ "The notebook charts\n",
+ "CTD data, dissolved oxygen, nitrate, PAR, spectral irradiance, fluorescence and pH in relation\n",
+ "to pressure/depth. The focus is\n",
+ "shallow (photic zone) profilers from the Regional Cabled Array component of OOI.\n",
+ "Specifically the Oregon Slope Base site in 2019. Oregon Slope Base is an instrumentation\n",
+ "site off the continental shelf west of the state of Oregon.\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Photic Zone CTD and other low data rate sensors\n",
+ "\n",
+ "\n",
+ "The 'photic zone' is the upper layer of the ocean regularly illuminated by sunlight. This set of photic zone \n",
+ "notebooks concerns sensor data from the surface to about 200 meters depth. Data are acquired from two to nine\n",
+ "times per day by shallow profilers. This notebook covers CTD (salinity \n",
+ "and temperature), dissolved oxygen, nitrate, pH, spectral irradiance, fluorometry and photosynthetically \n",
+ "available radiation (PAR). \n",
+ "\n",
+ "\n",
+ "Data are first taken from the Regional Cabled Array shallow profilers and platforms. A word of explanation here: The\n",
+ "profilers rise and then fall over the course of about 80 minutes, nine times per day, from a depth of 200 meters\n",
+ "to within about 10 meters of the surface. As the ascend and descend they record data. The resting location in\n",
+ "between these excursions is a platform 200 meters below the surface that is anchored to the see floor. The platform\n",
+ "also carries sensors that measure basic ocean water properties.\n",
+ "\n",
+ "\n",
+ "Research ship Revelle in the southern ocean: 100 meters in length. \n",
+ "Note: Ninety percent of this iceberg is beneath the surface. \n",
+ "\n",
+ "\n",
+ "More on the Regional Cabled Array oceanography program [here](https://interactiveoceans.washington.edu).\n",
+ " \n",
+ " \n",
+ "### Study site locations\n",
+ " \n",
+ "\n",
+ "We begin with three sites in the northeast Pacific: \n",
+ " \n",
+ "\n",
+ "```\n",
+ "Site name Lat Lon\n",
+ "------------------ --- ---\n",
+ "Oregon Offshore 44.37415 -124.95648\n",
+ "Oregon Slope Base 44.52897 -125.38966 \n",
+ "Axial Base 45.83049 -129.75326\n",
+ "``` \n",
+ "\n",
+ "\n",
+ "* The data variable is `spkir_downwelling_vector` x 7 wavelengths per below\n",
+ "* 9 months continuous operation at about 4 samples per second gives 91 million samples\n",
+ "* DataSet includes `int_ctd_pressure` and `time` Coordinates; Dimensions are `spectra` (0--6) and `time`\n",
+ "* Oregon Slope Base `node : SF01A`, `id : RS01SBPS-SF01A-3D-SPKIRA101-streamed-spkir_data_record`\n",
+ "* Correct would be to plot these as a sequence of rainbow plots with depth, etc\n",
+ "\n",
+ "See [Interactive Oceans](https://interactiveoceans.washington.edu/instruments/spectral-irradiance-sensor/): \n",
+ "\n",
+ "\n",
+ "> The Spectral Irradiance sensor (Satlantic OCR-507 multispectral radiometer) measures the amount of \n",
+ "> downwelling radiation (light energy) per unit area that reaches a surface. Radiation is measured \n",
+ "> and reported separately for a series of seven wavelength bands (412, 443, 490, 510, 555, 620, \n",
+ "> and 683 nm), each between 10-20 nm wide. These measurements depend on the natural illumination \n",
+ "> conditions of sunlight and measure apparent optical properties. These measurements also are used \n",
+ "> as proxy measurements of important biogeochemical variables in the ocean.\n",
+ ">\n",
+ "> Spectral Irradiance sensors are installed on the Science Pods on the Shallow Profiler Moorings \n",
+ "> at Axial Base (SF01A), Slope Base (SF01A), and at the Endurance Array Offshore (SF01B) sites. \n",
+ "> Instruments on the Cabled Array are provided by Satlantic – OCR-507. \n",
+ "\n",
+ "\n",
+ "```\n",
+ "spectral_irradiance_source = './data/rca/irradiance/'\n",
+ "ds_irradiance = [xr.open_dataset(spectral_irradiance_source + 'osb_sp_irr_spec' + str(i) + '.nc') for i in range(7)]\n",
+ "\n",
+ "# Early attempt at using log crashed the kernel\n",
+ "\n",
+ "day_of_month_start = '25'\n",
+ "day_of_month_end = '27'\n",
+ "time0 = dt64('2019-01-' + day_of_month_start)\n",
+ "time1 = dt64('2019-01-' + day_of_month_end)\n",
+ "\n",
+ "spectral_irradiance_upper_bound = 10.\n",
+ "spectral_irradiance_lower_bound = 0.\n",
+ "ds_irr_time_slice = [ds_irradiance[i].sel(time = slice(time0, time1)) for i in range(7)]\n",
+ "\n",
+ "fig, axs = plt.subplots(figsize=(12,8), tight_layout=True)\n",
+ "colorwheel = ['k', 'r', 'y', 'g', 'c', 'b', 'm']\n",
+ "for i in range(7):\n",
+ " axs.plot(ds_irr_time_slice[i].spkir_downwelling_vector, \\\n",
+ " ds_irr_time_slice[i].int_ctd_pressure, marker='.', markersize = 4., color=colorwheel[i])\n",
+ " \n",
+ "axs.set(xlim = (spectral_irradiance_lower_bound, spectral_irradiance_upper_bound), \\\n",
+ " ylim = (60., 0.), title='multiple profiles: spectral irradiance')\n",
+ "\n",
+ "\n",
+ "plt.show()\n",
+ "```\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Shallow Profiler\n",
+ "\n",
+ "\n",
+ "### Working with shallow profiler ascent/descent/rest cycles\n",
+ "\n",
+ "\n",
+ "This topic is out of sequence intentionally. The topics that follow are in more of a logical order.\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "The issue at hand is that the shallow profiler ascends and descends and rests about nine times per\n",
+ "day; but the time of day when these events happen is not perfectly fixed. As a result we need \n",
+ "a means to identify the start and end times of (say) an ascent so that we can be confident that\n",
+ "the data were in fact acquired as the profiler ascended through the water column. This is also \n",
+ "useful for comparing ascent to descent data or comparing profiler-at-rest data to platform data\n",
+ "(since the profiler is at rest *on* the platform).\n",
+ "\n",
+ "\n",
+ "\n",
+ "To restate the task: From a conceptual { time window } we would like very specific { metadata }\n",
+ "for time windows when the profiler ascended while collecting data. \n",
+ "That is, we want accurate subsidiary time windows for successive profiles within our conceptual\n",
+ "time window; per site and year.\n",
+ "We can then use these specific { time window } boundaries to select data\n",
+ "subsets from corresponding profiling runs. \n",
+ "\n",
+ "\n",
+ "\n",
+ "The first step in this process is to get CTD data for the shallow profiler since it will have a\n",
+ "record of depth over time. This record is scanned in one-year chunks to identify the UTM start\n",
+ "times of each successive profile. Also determined: The start times of descents and the start times of rests. \n",
+ "\n",
+ "\n",
+ "\n",
+ "From these three sets of timestamps we can make the assumption that the end of an \n",
+ "ascent corresponds to the start of a descent. Likewise the end of a descent is the start of \n",
+ "a rest; and the start of an ascent is the end of the previous rest. Each ascent / descent / rest\n",
+ "interval is considered as one profile (in that order). The results are written to a CSV file\n",
+ "that has one row of timing metadata per profile. \n",
+ "\n",
+ "\n",
+ "\n",
+ "Now suppose the goal is to create a sequence of temperature plots for July 2019 for the Axial \n",
+ "Base shallow profiler. First we would identify the pre-existing CSV file for Axial Base for the\n",
+ "year 2019 and read that file into a pandas Dataframe. Let's suppose it is read into a profile\n",
+ "Dataframe called `p` and that we have labled the six columns that correspond to\n",
+ "ascent start/end, descent start/end and rest start/ned. Here is example code from `BioOpticsModule.py`.\n",
+ "\n",
+ "\n",
+ "```\n",
+ "p = pd.read_csv(metadata_filename, usecols=[\"1\", \"3\", \"5\", \"7\", \"9\", \"11\"])\n",
+ "p.columns = ['ascent_start', 'ascent_end', 'descent_start', 'descent_end', 'rest_start', 'rest_end']\n",
+ "p['ascent_start'] = pd.to_datetime(pDf['ascent_start'])\n",
+ "p['ascent_end'] = pd.to_datetime(pDf['ascent_end'])\n",
+ "p['descent_start'] = pd.to_datetime(pDf['descent_start'])\n",
+ "p['descent_end'] = pd.to_datetime(pDf['descent_end'])\n",
+ "p['rest_start'] = pd.to_datetime(pDf['rest_start'])\n",
+ "p['rest_end'] = pd.to_datetime(pDf['rest_end'])\n",
+ "```\n",
+ "\n",
+ "\n",
+ "\n",
+ "Let's examine two rows of this Dataframe:\n",
+ "\n",
+ "\n",
+ "\n",
+ "```\n",
+ "print(p['ascent_start'][0])\n",
+ "\n",
+ "2019-01-01 00:27:00\n",
+ "\n",
+ "print(p['ascent_start'][1600])\n",
+ "\n",
+ "2019-07-04 15:47:00\n",
+ "```\n",
+ "\n",
+ "\n",
+ "That is, row 0 corresponds to the start of 2019, January 1, and row 1600 occurs on July 4.\n",
+ "\n",
+ "\n",
+ "For a 365 day year with no\n",
+ "missed profiles (9 profiles per day) this file would contain 365 * 9 = 3285 profiles. In practice\n",
+ "there will be fewer owing to storms or other factors that interrupt data acquisition. \n",
+ "\n",
+ "\n",
+ "Each row of this dataframe corresponds to a profile run (ascent, descent, rest) of the shallow\n",
+ "profiler. Consequently we could use the time boundaries of one such row to select data that was\n",
+ "acquired *during the ascent period of that profile*. Suppose a temperature dataset for the month of July \n",
+ "is called `T`. `T` is constructed as an xarray Dataset with dimension `time`. \n",
+ "We can use the xarray *select* method `.sel`, as in `T.sel(time=slice(time0, time1))`, to\n",
+ "produce a Dataset with only times \n",
+ "that fall within a profile ascent window. \n",
+ "\n",
+ "\n",
+ "```\n",
+ "time0 = p['ascent_start'][1600]\n",
+ "time1 = p['ascent_end'][1600]\n",
+ "T_ascent = T.sel(time=slice(time0, time1))\n",
+ "```\n",
+ "\n",
+ "\n",
+ "Now `T_ascent` will contain about 60 minutes worth of data. \n",
+ "\n",
+ "\n",
+ "\n",
+ "This demonstrates loading time boundaries from the metadata `p`. \n",
+ "The metadata informs the small time box. Now we need the other direction \n",
+ "as well: Suppose the interval of interest is the first four days of July 2019.\n",
+ "We have no idea which rows of the metadata `p` this corresponds to. We need\n",
+ "a list of row indices for `p` in that time window. For this we \n",
+ "have a utility function.\n",
+ "\n",
+ "\n",
+ "```\n",
+ "def GenerateTimeWindowIndices(pDf, date0, date1, time0, time1):\n",
+ " '''\n",
+ " Given two day boundaries and a time window (UTC) within a day: Return a list\n",
+ " of indices of profiles that start within both the day and time bounds. This \n",
+ " works from the passed dataframe of profile times.\n",
+ " '''\n",
+ " nprofiles = len(pDf)\n",
+ " pIndices = []\n",
+ " for i in range(nprofiles):\n",
+ " a0 = pDf[\"ascent_start\"][i]\n",
+ " if a0 >= date0 and a0 <= date1 + td64(1, 'D'):\n",
+ " delta_t = a0 - dt64(a0.date())\n",
+ " if delta_t >= time0 and delta_t <= time1: pIndices.append(i)\n",
+ " return pIndices\n",
+ "```\n",
+ "\n",
+ "This function has both a date range and a time-of-day range. The resulting row index list corresponds\n",
+ "to profiles that satisfy both time window constraints: Date and time of day. \n",
+ "\n",
+ "\n",
+ "The end-result is this: We can go from a conceptual { time window } to a list of { metadata rows }, i.e. a\n",
+ "list of integer row numbers, using the above utility function. Within the metadata structure `p` we can \n",
+ "use these rows to look up ascent / descent / rest times for profiles.\n",
+ "At that point we have very specific { time window } boundaries for selecting data\n",
+ "from individual profiles. \n",
+ "\n",
+ "\n",
+ "* order data\n",
+ "* clean the data to regular 1Min samples\n",
+ "* scan the data for profiles; write these to CSV files\n",
+ "* load in a profile list for a particular site and year\n",
+ "\n",
+ "\n",
+ "Now we start charting this data. We'll begin with six signals, three each from the CTD and the fluorometer. \n",
+ "Always we have two possible axes: Depth and time. Most often we chart against depth using the y-axis and \n",
+ "measuring from a depth of 200 meters at the bottom to the surface at the top of the chart. \n",
+ "\n",
+ "\n",
+ "CTD signals\n",
+ "\n",
+ "\n",
+ "* Temperature\n",
+ "* Salinity\n",
+ "* Dissolved oxygen\n",
+ "\n",
+ "\n",
+ "Fluorometer signals\n",
+ "\n",
+ "\n",
+ "* CDOM: Color Dissolved Organic Matter)\n",
+ "* chlor-a: Chlorophyll pigment A\n",
+ "* scatt: Backscatter\n",
+ "\n",
+ "\n",
+ "The other sensor signals will be introduced subsequently. These include nitrate concentration,\n",
+ "pH, pCO2, PAR, spectral irradiance, local current and water density. \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "```\n",
+ "# Create a pandas DataFrame: Six columns of datetimes for a particular year and site\n",
+ "# The six columns are start/end for, in order: ascent, descent, rest: See column labels below.\n",
+ "def ReadProfiles(fnm):\n",
+ " \"\"\"\n",
+ " Profiles are saved by site and year as 12-tuples. Here we read only\n",
+ " the datetimes (not the indices) so there are only six values. These\n",
+ " are converted to Timestamps. They correspond to ascend start/end, \n",
+ " descend start/end and rest start/end.\n",
+ " \"\"\"\n",
+ " df = pd.read_csv(fnm, usecols=[\"1\", \"3\", \"5\", \"7\", \"9\", \"11\"])\n",
+ " df.columns=['ascent_start', 'ascent_end', 'descent_start', 'descent_end', 'rest_start', 'rest_end']\n",
+ " df['ascent_start'] = pd.to_datetime(df['ascent_start'])\n",
+ " df['ascent_end'] = pd.to_datetime(df['ascent_end'])\n",
+ " df['descent_start'] = pd.to_datetime(df['descent_start'])\n",
+ " df['descent_end'] = pd.to_datetime(df['descent_end'])\n",
+ " df['rest_start'] = pd.to_datetime(df['rest_start'])\n",
+ " df['rest_end'] = pd.to_datetime(df['rest_end'])\n",
+ " return df\n",
+ "\n",
+ "\n",
+ "# FilterSignal() operates on a time series DataArray passed in as 'v'. It is set up to point to multiple possible\n",
+ "# smoothing kernels but has just one at the moment, called 'hat'.\n",
+ "def FilterSignal(v, ftype='hat', control1=3):\n",
+ " \"\"\"Operate on an XArray data array (with some checks) to produce a filtered version\"\"\"\n",
+ " # pre-checks\n",
+ " if not v.dims[0] == 'time': return v\n",
+ "\n",
+ " if ftype == 'hat': \n",
+ " n_passes = control1 # should be a kwarg\n",
+ " len_v = len(v)\n",
+ " for n in range(n_passes):\n",
+ " source_data = np.copy(v) if n == 0 else np.copy(smooth_data)\n",
+ " smooth_data = [source_data[i] if i == 0 or i == len_v - 1 else \\\n",
+ " 0.5 * source_data[i] + 0.25 * (source_data[i-1] + source_data[i + 1]) \\\n",
+ " for i in range(len_v)]\n",
+ " return smooth_data\n",
+ " return v\n",
+ "\n",
+ "```\n",
+ "\n",
+ "\n",
+ "\n",
+ "#### [Table of Contents](#table-of-contents)\n",
+ "\n",
+ "\n",
+ "## Coding Environment\n",
+ "\n",
+ "\n",
+ "### bash, text editor, git, GitHub\n",
+ "\n",
+ "\n",
+ "### running a Jupyter notebook server (code and markdown)\n",
+ "\n",
+ "\n",
+ "- I learn the basic commands of the `bash` shell; including how to use a text editor like `nano` or `vim`\n",
+ "- I create an account at `github.com` and learn to use the basic `git` commands\n",
+ " - `git pull`, `git add`, `git commit`, `git push`, `git clone`, `git stash`\n",
+ " - I plan to spend a couple of hours learning `git`; I find good YouTube tutorials\n",
+ "- I create my own GitHub repository with a `README.md` file describing my research goals\n",
+ "- I set up a Jupyter notebook server on my local machine\n",
+ " - As I am using a PC I install WSL-2 (Windows Subsystem for Linux v2)...\n",
+ " - ...and install Miniconda plus some Python libraries\n",
+ "- I clone my \"empty\" repository from GitHub to my local Linux environment\n",
+ "- I start my Jupyter notebook server, navigate to my repo, and create a first notebook\n",
+ "- I save my notebook and use `git add, commit, push` to save it safely on GitHub\n",
+ "- On GitHub: Add and test a **`binder`** badge\n",
+ " - Once that works, be sure to `git pull` the modified GitHub repo back into the local copy\n",
+ "\n",
+ "#### [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## OOI Data\n",
+ "\n",
+ "\n",
+ "### Ordering, retrieving and cleaning datasets from OOI\n",
+ "\n",
+ "\n",
+ "At this point we do not have any data; so let's do that next. There are two important considerations. \n",
+ "First: If the data volume will exceed 100MB: That is too much to keep in a GitHub repository. The\n",
+ "data must be staged \"nearby\" in the local environment; outside the repository but accessible by\n",
+ "the repository code, as in:\n",
+ "\n",
+ "\n",
+ "```\n",
+ " ------------- /repo directory\n",
+ " /\n",
+ "/home --------\n",
+ " \\\n",
+ " -------------- /data directory\n",
+ "\n",
+ "```\n",
+ "\n",
+ "\n",
+ "Second: Suppose the repo *does* contain (smaller) datasets, to be read by the code. \n",
+ "If the intent is to use `binder` to make a sandbox version of the repo\n",
+ "available, all significant changes to this code should be tested: First locally\n",
+ "and then (after a `push` to GitHub) ***in `binder`***. This ensures that not too \n",
+ "many changes pile up, breaking binder in mysterious and hard-to-debug ways.\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "Now that we have a dataset let's open it up and examine it within a Notebook.\n",
+ "The data are presumed to be in NetCDF format; so we follow common practice of\n",
+ "reading the data into an `xarray Dataset` which is a composition of `xarray\n",
+ "DataArrays`. There is a certain amount of learning here, particularly as this\n",
+ "library shares some Python DNA with `pandas` and `numpy`. Deconstructing an\n",
+ "`xarray Dataset` can be very challenging; so a certain amount of ink is devoted\n",
+ "to that process in this repo.\n",
+ "\n",
+ "#### [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## NetCDF\n",
+ "\n",
+ "\n",
+ "### Open and subset a NetCDF data file via the `xarray Dataset` \n",
+ "\n",
+ "\n",
+ "Data provided by OOI tends to be \"not ready for use\". There are several steps needed; and\n",
+ "these are not automated. They require some interactive thought and refinement. \n",
+ "\n",
+ "\n",
+ "- Convert the principal dimension from `obs` or `row` to `time` \n",
+ " - `obs/row` are generic terms with values running 1, 2, 3... (hinders combining files into longer time series)\n",
+ "- Re-name certain data variables for easier use; and delete anything that is not of interest\n",
+ "- Identify the time range of interest\n",
+ "- Write a specific subset file\n",
+ " - For example: Subset files that are small can live within the repo\n",
+ "\n",
+ "\n",
+ "```\n",
+ "# This code runs 'one line at a time' (not as a block) to iteratively streamline the data\n",
+ "\n",
+ "# Suggestion: Pay particular attention to the construct ds = ds.some_operation(). This ensures \n",
+ "# that the results of some_operation() are retained in the new version of the Dataset. \n",
+ "\n",
+ "ds = xr.open_dataset(filename)\n",
+ "ds # notice the output will show dimension as \"row\" and \"time\" as a data variable\n",
+ "\n",
+ "\n",
+ "ds = ds.swap_dims({'row': 'time'}) # moves 'time' into the dimension slot\n",
+ "ds = ds.rename({'some_ridiculously_long_data_variable_name':'temperature'})\n",
+ "ds = ds.drop('some_data_variable_that_has_no_interest_at_this_point')\n",
+ "\n",
+ "\n",
+ "ds = ds.dropna('time') # if any data variable value == 'NaN' this entry is deleted: Includes all\n",
+ " # corresponding data variable values, corresponding coordinates and \n",
+ " # the corresponding dimension value. This enables plotting data such\n",
+ " # as pH that happens to be rife with NaNs. \n",
+ "\n",
+ "ds.z.plot() # this produces a simple chart showing gaps in the data record\n",
+ "ds.somedata.mean() # prints the mean of the given data variable\n",
+ "\n",
+ "ta0 = dt64_from_doy(2021, 60) # these time boundaries are set iteratively...\n",
+ "ta1 = dt64_from_doy(2021, 91) # ...to focus in on a particular time range with known data...\n",
+ "ds.sel(time=slice(ta0, ta1)).z.plot() # ...where this plot is the proof\n",
+ "\n",
+ "\n",
+ "ds.sel(time=slice(ta0, ta1)).to_netcdf(outputfile) # writes a time-bounded data subset to a new NetCDF file\n",
+ "```\n",
+ "\n",
+ "#### [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## Depth And Time\n",
+ "\n",
+ "\n",
+ "Datasets have a depth attribute `z` and a time dimension `time`. These are derived by the data \n",
+ "system and permit showing sensor values (like temperature) either in terms of depth below the \n",
+ "surface; or in time relative to some benchmark. \n",
+ "\n",
+ "#### [Table of Contents](#Table-of-Contents)\n",
+ "\n",
+ "\n",
+ "## Data Features\n",
+ "\n",
+ "\n",
+ "- Some signals may have dropouts: Missing data is usually flagged as `NaN`\n",
+ " - See the section above on using the xarray `.dropna(dimension)` feature to clean this up\n",
+ "- Nitrate data also features ***dark sample*** data\n",
+ "- Spectrophotometer instruments measure both ***optical absorption*** and ***beam attenuation***\n",
+ " - For both of these about 82 individual channel values are recorded\n",
+ " - Each channel is centered at a unique wavelength in the visible spectrum\n",
+ " - The wavelength channels are separated by about 4 nm\n",
+ " - The data are noisy\n",
+ " - Some channels contain no data\n",
+ " - Sampling frequency needed\n",
+ "- Spectral irradiance carries seven channels (wavelengths) of data\n",
+ "- Current measurements give three axis results: north, east, up\n",
+ " - ADCP details needed\n",
+ "\n",
+ "#### [Contents](#Contents)\n",
+ "\n",
+ "\n",
+ "## Non OOI Data: ROMS, ARGO, MODIS, GLODAP, MSLA\n",
+ "\n",
+ "\n",
+ "### ROMS\n",
+ "\n",
+ "\n",
+ "### ARGO\n",
+ "\n",
+ "\n",
+ "### MODIS\n",
+ "\n",
+ "\n",
+ "### GLODAP\n",
+ "\n",
+ "\n",
+ "### MSLA\n",
+ "\n",
+ "\n",
+ "\n",
+ "#### [Contents](#Contents)\n",
+ "\n",
+ "\n",
+ "## Data Product Levels\n",
+ "\n",
+ "\n",
+ "The \n",
+ "[OOI Data Catalog Documentation](https://dataexplorer.oceanobservatories.org/help/overview.html#data-products) \n",
+ "describes three levels of data product, summarized: \n",
+ "\n",
+ "\n",
+ "* Level 1 ***Instrument deployment***: Unprocessed, parsed data parameter that is in instrument/sensor \n",
+ "units and resolution. See note below defining a *deployment*. This is not data we are interested in using, as a rule.\n",
+ "\n",
+ "\n",
+ "* Level 1+ ***Full-instrument time series***: A join of recovered and telemetered \n",
+ "streams for non-cabled instrument deployments. For high-resolution cabled and recovered data, this product is \n",
+ "binned to 1-minute resolution to allow for efficient visualization and downloads for users that do not need \n",
+ "the full-resolution, gold copy (Level 2) time series. We'd like to hold out for 'gold standard'.\n",
+ "\n",
+ "\n",
+ "* Level 2 ***Full-resolution, gold standard time series***: The calibrated full-resolution dataset \n",
+ "(scientific units). L2 data have been processed, pre-built, and served \n",
+ "from the OOI system to the \n",
+ "[OOI Data Explorer](https://dataexplorer.oceanobservatories.org/)\n",
+ "and to Users. The mechanisms are THREDDS and ERDDAP; file format \n",
+ "NetCDF-CF. There is one file for every instrument, stream, and deployment. For more refer to this\n",
+ "[Data Download](https://dataexplorer.oceanobservatories.org/help/overview.html#download-data-map-overview) link.\n",
+ "\n",
+ "#### [Contents](#Contents)\n",
+ "\n",
+ "\n",
+ "## Computing Infrastructure\n",
+ "\n",
+ "\n",
+ "- bash\n",
+ "- editors\n",
+ "- git\n",
+ "- GitHub\n",
+ "- Python\n",
+ "- nbpuller\n",
+ "- binder\n",
+ "- wget\n",
+ "- pickle\n",
+ "- modules\n",
+ "- `conda` \n",
+ " - environments\n",
+ " - vocabulary\n",
+ " - generating replicators\n",
+ "\n",
+ "\n",
+ "#### wget\n",
+ "\n",
+ "\n",
+ "`wget` can be used recursively to copy files from the web, i.e. to make local copies.\n",
+ "`wget` used in the **Global Ocean** notebook to get 500MB of data from the \n",
+ "cloud that would otherwise make the repository too bulky for GitHub. \n",
+ "\n",
+ "\n",
+ "Example usage, typically run from the command line, run from a Jupyter notebook\n",
+ "cell, or placed in a `bash` script:\n",
+ "\n",
+ "\n",
+ "```\n",
+ "wget -q https://kilroybackup.s3.us-west-2.amazonaws.com/glodap/GLODAPv2.2016b.NO3.nc -O glodap/NO3.nc\n",
+ "```\n",
+ "\n",
+ "The `-q` flag suppresses output ('quiet') and `-O` specifies the local name of the data file.\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Jupyter servers \n",
+ "\n",
+ "- Littlest and so on\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "## flag move up to RCA OOI Terminology\n",
+ "\n",
+ "\n",
+ "\n",
+ "- **instrument**: A physical device with one or more sensors.\n",
+ "- **stream**: Sensor data.\n",
+ "- **deployment**: The act of putting infrastructure in the water, or the length of \n",
+ "time between a platform going in the water and being recovered and brought back to shore.There are \n",
+ "multiple deployment files per instrument. \n",
+ "\n",
+ "\n",
+ "\n",
+ "## Diagnostics\n",
+ "\n",
+ "\n",
+ "```\n",
+ "#########################\n",
+ "#\n",
+ "# Profiler diagnostic view\n",
+ "#\n",
+ "#########################\n",
+ "\n",
+ "# This is a diagnostic for a sequence of four profiles:\n",
+ "\n",
+ "for i in [503, 504, 505, 506]: print(i, 'profile start / end:', pDf21[\"ascent_start\"][i], \\\n",
+ " pDf21[\"descent_end\"][i], ' duration: ', pDf21[\"descent_end\"][i] - pDf21[\"ascent_start\"][i]) \n",
+ "\n",
+ "# Results, noting the fourth one is a midnight (slow descent) profile\n",
+ "\n",
+ "503 profile start / end: 2021-03-01 00:27:00 2021-03-01 02:05:00 duration: 0 days 01:38:00\n",
+ "504 profile start / end: 2021-03-01 02:42:00 2021-03-01 04:21:00 duration: 0 days 01:39:00\n",
+ "505 profile start / end: 2021-03-01 04:52:00 2021-03-01 06:31:00 duration: 0 days 01:39:00\n",
+ "506 profile start / end: 2021-03-01 07:22:00 2021-03-01 10:03:00 duration: 0 days 02:41:00\n",
+ "\n",
+ "# Profile 506 is an hour longer in duration than the three prior. The profiler pauses during descent\n",
+ "# to give the pH sensor time to equilibrate. The following chart shows depth with time over 24 hours\n",
+ "# including slowed descents for midnight and noon.\n",
+ "\n",
+ "#####################\n",
+ "#\n",
+ "# Saving a figure\n",
+ "#\n",
+ "#####################\n",
+ "\n",
+ "fig.savefig(os.getcwd() + \"/Images/charts/ABCOST_signals_vs_depth_and_time.png\")\n",
+ "\n",
+ "#####################\n",
+ "#\n",
+ "# Generate / Save / Play Back an animated chart\n",
+ "#\n",
+ "#####################\n",
+ "\n",
+ "# This code (animate / playback / save) takes time to run so commented out by default\n",
+ "# if False: \n",
+ " # anim = animation.FuncAnimation(fig, AnimateChart, init_func=AnimateInit, \\\n",
+ " # frames=nframes, interval=200, blit=True, repeat=False)\n",
+ " # play immediately: HTML(anim.to_html5_video())\n",
+ " # anim.save(this_dir + '/Images/animations/multisensor_animation.mp4')\n",
+ " \n",
+ " \n",
+ "#######################################\n",
+ "#\n",
+ "# Specific to BioOptics: Generate a five-signal animation\n",
+ "#\n",
+ "#######################################\n",
+ "\n",
+ "\n",
+ "# Animated time series\n",
+ "\n",
+ "site = 'osb'\n",
+ "year = '2021'\n",
+ "pDf21 = ReadProfileMetadata(os.getcwd() + \"/./Profiles/\" + site + year + \".csv\") \n",
+ "\n",
+ "firstframe = 506 # march 1 in 2021 at OSB\n",
+ "nframes = 279 # 279 max for one month\n",
+ "ncharts = 5\n",
+ "\n",
+ "fig, axs = plt.subplots(figsize=(12.5,14), tight_layout=True)\n",
+ "\n",
+ "# configuration lists with seven elements each, one for each sensor\n",
+ "clr = ['red', 'black', 'xkcd:bronze', 'green', 'magenta']\n",
+ "lows = [temp_lo, salinity_lo, do_lo, chlora_lo, cdom_lo]\n",
+ "highs = [temp_hi, salinity_hi, do_hi, chlora_hi, cdom_hi]\n",
+ "lbls = [\"Temperature\",\"Salinity\",\"Dissolved Oxygen\",\"Chlorophyll-A\",\"CDOM\"]\n",
+ "offs = [1.0, 1.065, 1.130, 1.195, 1.260]\n",
+ "mrkrs = ['o', 's', 'D', 'v', '^']\n",
+ "\n",
+ "axs.set_title('Temp, Salinity, DO, Chl-A, CDOM with Depth/Time')\n",
+ "axs.title.set_fontsize(22)\n",
+ "axs.yaxis.label.set_color('k')\n",
+ "axs.yaxis.label.set_fontsize(18)\n",
+ "axs.set_ylabel(\"Depth (m)\")\n",
+ "\n",
+ "axs.xaxis.label.set_fontsize(18)\n",
+ "\n",
+ "atw = [axs.twiny() for i in range(ncharts)] # twin y-axes supporting the multiple sensor types\n",
+ "\n",
+ "# Configures all of the twin axes per the above configuration lists\n",
+ "for i in range(ncharts): \n",
+ " atw[i].set(xlim = (lows[i], highs[i]), ylim = (-200., 0.))\n",
+ " atw[i].xaxis.label.set_fontsize(18)\n",
+ " atw[i].set_xlabel(lbls[i])\n",
+ " atw[i].xaxis.set_ticks_position('top')\n",
+ " atw[i].spines['top'].set_position(('axes', offs[i]))\n",
+ " atw[i].xaxis.label.set_color(clr[i])\n",
+ " atw[i].tick_params(axis='x', colors=clr[i], size=4, width=1.5)\n",
+ "\n",
+ "lines = [atw[i].plot([], [], lw=1, marker=mrkrs[i], ms = 6., c=clr[i], mfc=clr[i])[0] for i in range(ncharts)]\n",
+ "\n",
+ "def AnimateInit():\n",
+ " for i in range(ncharts): lines[i].set_data([], [])\n",
+ " return lines\n",
+ "\n",
+ "pIdcs = [i for i in range(firstframe, firstframe + nframes)]\n",
+ "\n",
+ "def AnimateChart(frame):\n",
+ " global pIdcs\n",
+ " \n",
+ " t0, t1 = pDf21['ascent_start'][pIdcs[frame]], pDf21['ascent_end'][pIdcs[frame]]\n",
+ "\n",
+ " Ts = dsT.sel(time=slice(t0, t1))\n",
+ " Ss = dsS.sel(time=slice(t0, t1))\n",
+ " Os = dsO.sel(time=slice(t0, t1))\n",
+ " As = dsA.sel(time=slice(t0, t1))\n",
+ " Cs = dsC.sel(time=slice(t0, t1))\n",
+ "\n",
+ " lines[0].set_data(Ts.temp, Ts.z)\n",
+ " lines[1].set_data(Ss.salinity, Ss.z)\n",
+ " lines[2].set_data(Os.doxygen, Os.z)\n",
+ " lines[3].set_data(As.chlora, As.z)\n",
+ " lines[4].set_data(Cs.cdom, Cs.z)\n",
+ "\n",
+ " clear_output(wait = True)\n",
+ " print(\"animating frame\", frame)\n",
+ " \n",
+ " return lines\n",
+ "\n",
+ "\n",
+ "##########################################################\n",
+ "#\n",
+ "# Organizational remarks across 16 datatypes (spectrophotometer not included\n",
+ "#\n",
+ "##########################################################\n",
+ "#\n",
+ "# Concerning the names of data variables\n",
+ "# Some engineering elements of OOI result in complex names. This commented-out code fragment demonstrates\n",
+ "# opening a NetCDF file as an XArray Dataset and renaming a data variable to something simpler.\n",
+ "#\n",
+ "# dsO = xr.open_dataset(\"../data/data_explorer_1Min/axb/profiler/axb_profiler_doxygen_1Min.nc\")\n",
+ "# dsO = dsO.rename_vars({\"moles_of_oxygen_per_unit_mass_in_sea_water_profiler_depth_enabled\":\"doxygen\"})\n",
+ "# dsO\n",
+ "#\n",
+ "# This cell formerly loaded selected datasets from the large (multi-year) data pool. This pool is \n",
+ "# external to the repository owing its large volume. This read cell is therefore now deprecated\n",
+ "# in favor of subsequent cells that load smaller datasets from within the repository.\n",
+ "#\n",
+ "# To keep code compact I use the following table of abbreviations for sensors.\n",
+ "# BioOptics includes Fluorometers, the main emphasis here. Fluorometers carry either two or\n",
+ "# three sensor types: Chlorophyll-A, Color Dissolved Organic Matter (CDOM), and particulate backscatter. \n",
+ "# The BioOptics ensemble also includes PAR and Spectral Irradiance. PAR measurements are individual\n",
+ "# values. Spectral irradiance is seven values per observation. Spectrophotometers are not considered\n",
+ "# in this notebook.\n",
+ "#\n",
+ "# Dictionary of single-letter sensor keys: The capitalized letter follows 'ds', an abbreviation for\n",
+ "# an XArray Dataset. We have therefore: dsA, dsB, dsC, etcetera\n",
+ "#\n",
+ "# Desig Data Renamed Instrument Runs during\n",
+ "# ----- ---- ------- ---------- -----------\n",
+ "# A Chlorophyll-A chlora fluorometer continuous\n",
+ "# B backscatter backscatter fluorometer continuous\n",
+ "# C CDOM cdom fluorometer continuous\n",
+ "# G pCO2 pco2 ? midnight/noon descent\n",
+ "# H pH ph pH midnight/noon descent\n",
+ "# I Spectral Irradiance ? spkir continuous\n",
+ "# M Reserved for Nitrate' ? nitrate midnight/noon ascent\n",
+ "# N Nitrate ? nitrate midnight/noon ascent\n",
+ "# P PAR par PAR continuous\n",
+ "# Q pressure pressure CTD continuous\n",
+ "# O dissolved oxygen doxygen CTD continuous\n",
+ "# S salinity salinity CTD continuous\n",
+ "# T temperature temp CTD continuous\n",
+ "# U velocity east veast xyz-current continuous\n",
+ "# V velocity north vnorth xyz-current continuous\n",
+ "# W velocity up vup xyz-current continuous\n",
+ "#\n",
+ "# \n",
+ "# Shallow profilers begin at rest at a depth of 200 meters. They ascend to within\n",
+ "# about 10 meters of the surface, then descend to create a double profile dataset;\n",
+ "# whereupon they return to the at-rest state. This cycle repeats nine times per\n",
+ "# day. What follows is a simple dictionary of interval designators: The capital letter \n",
+ "# follows the sensor key\n",
+ "#\n",
+ "# A Ascent\n",
+ "# D Descent\n",
+ "# R Rest\n",
+ "#\n",
+ "#\n",
+ "# There are three RCA shallow profiler sites with names abbreviated herein:\n",
+ "#\n",
+ "# osb Oregon Slope Base\n",
+ "# axb Axial Base\n",
+ "# oos Oregon Offshore (part of the Endurance array)\n",
+ "#\n",
+ "# For more on this see the README.md file and the Notebooks subdirectory.\n",
+ "#\n",
+ "################################################################################################\n",
+ "\n",
+ "####################\n",
+ "####################\n",
+ "####\n",
+ "#### IMPORTANT!!!!!!!!!!!\n",
+ "####\n",
+ "#### The code below loads data and ***renames*** the data variables to make them easier to work with\n",
+ "####\n",
+ "####################\n",
+ "####################\n",
+ "\n",
+ "\n",
+ "\n",
+ ".......................................\n",
+ "\n",
+ "\n",
+ "# This cell can be used to glance at data availability for each type of data. It uses a \n",
+ "# very simple plot call to show presence/absence over the history of the cabled array\n",
+ "# deployment. Both pCO2 and pH are 'no data' results; and upward velocity looks suspicious.\n",
+ "# The other datasets look to be present during the first half of 2021.\n",
+ "#\n",
+ "# To recap the relevant part of the single-letter-designator table...\n",
+ "#\n",
+ "# Desig Data Renamed Instrument\n",
+ "# ----- ---- ------- -----------\n",
+ "# G pCO2 pco2 ?\n",
+ "# H pH ph pH\n",
+ "# I Spectral Irradiance si412, si443, si490, spkir\n",
+ "# si510, si555, si620, si683 \n",
+ "# N Nitrate nitrate nitrate\n",
+ "# P PAR par PAR\n",
+ "# U velocity east veast ADCP?\n",
+ "# V velocity north vnorth ADCP?\n",
+ "# W velocity up vup ADCP?\n",
+ "\n",
+ "# un-comment the next line and one of the sensor lines that follow\n",
+ "# fig, ax = plt.subplots(figsize=(12, 8), tight_layout=True)\n",
+ "\n",
+ "# ax.plot(dsG.time, dsG.pco2, ms = 1., color='blue', mfc='blue') # no data\n",
+ "# ax.plot(dsH.time, dsH.ph, ms = 1., color='blue', mfc='blue') # no data\n",
+ "# ax.plot(dsI.time, dsI.si412, ms = 1., color='blue', mfc='blue') # good first half of 2021 (max 80)\n",
+ "# ax.plot(dsI.time, dsI.si443, ms = 1., color='blue', mfc='blue') # \" \n",
+ "# ax.plot(dsI.time, dsI.si490, ms = 1., color='blue', mfc='blue') # \" \n",
+ "# ax.plot(dsI.time, dsI.si510, ms = 1., color='blue', mfc='blue') # \" \n",
+ "# ax.plot(dsI.time, dsI.si555, ms = 1., color='blue', mfc='blue') # \" \n",
+ "# ax.plot(dsI.time, dsI.si620, ms = 1., color='blue', mfc='blue') # \" (max down around 15)\n",
+ "# ax.plot(dsI.time, dsI.si683, ms = 1., color='blue', mfc='blue') # \" (max down around 6)\n",
+ "# ax.plot(dsN.time, dsN.nitrate, ms = 1., color='blue', mfc='blue') # \" \n",
+ "# ax.plot(dsO.time, dsO.doxygen, ms = 1., color='blue', mfc='blue') # \" \n",
+ "# ax.plot(dsP.time, dsP.par, ms = 1., color='blue', mfc='blue') # \"\n",
+ "# ax.plot(dsS.time, dsS.salinity, ms = 1., color='blue', mfc='blue') # \"\n",
+ "# ax.plot(dsT.time, dsT.temp, ms = 1., color='blue', mfc='blue') # \"\n",
+ "# ax.plot(dsU.time, dsU.veast, ms = 1., color='blue', mfc='blue') # \"\n",
+ "# ax.plot(dsV.time, dsV.vnorth, ms = 1., color='blue', mfc='blue') # \"\n",
+ "# ax.plot(dsW.time, dsW.vup, ms = 1., color='blue', mfc='blue') # \" suspiciously high amplitude in 2021\n",
+ "\n",
+ "########################\n",
+ "#\n",
+ "# shear calculation code removed from BioOptics.ipynb\n",
+ "#\n",
+ "########################\n",
+ "\n",
+ "# get a list of ascent indices (for dataframe pDf21, OSB 2021) for March 1, 2021\n",
+ "t_midnight = td64(0, 'm')\n",
+ "t_almost_midnight = td64(24*60-1, 'm')\n",
+ "list_of_ascents = GenerateTimeWindowIndices(pDf21, dt64('2021-03-01'), dt64('2021-03-02'), noon0, noon1)\n",
+ "print(list_of_ascents)\n",
+ "\n",
+ "# attempt a shear calculation\n",
+ "def ShearProfile(v, offset):\n",
+ " \"\"\"Calculate shear from a Dataset dim=time, data vars = veast, vnorth, z\"\"\"\n",
+ " # verify the time dimension\n",
+ " if not v.dims['time']: return v\n",
+ " len_v = v.dims['time']\n",
+ " return [0. if i + offset >= len_v else \\\n",
+ " np.sqrt((vel['veast'][i]-vel['veast'][i + offset])**2 + \\\n",
+ " (vel['vnorth'][i]-vel['vnorth'][i + offset])**2) \\\n",
+ " for i in range(len_v)]\n",
+ "\n",
+ "i=0\n",
+ "offset=2\n",
+ "veast = dsU.sel(time=slice(pDf21['ascent_start'][list_of_ascents[i]], pDf21['ascent_end'][list_of_ascents[i]]))\n",
+ "vnorth = dsV.sel(time=slice(pDf21['ascent_start'][list_of_ascents[i]], pDf21['ascent_end'][list_of_ascents[i]]))\n",
+ "vel = xr.merge([veast, vnorth])\n",
+ "shear = ShearProfile(vel, offset)\n",
+ "\n",
+ "fig, axs = plt.subplots(figsize=(12,4), tight_layout=True)\n",
+ "axs.plot(shear, vel.z, marker='.', ms=9., color='k', mfc='r')\n",
+ "axs.set(ylim = (-200., 0.), title='--------------')\n",
+ "\n",
+ "# Some additional poking around code...\n",
+ "\n",
+ "# fig, axs = plt.subplots(figsize=(12,4), tight_layout=True)\n",
+ "# axs.plot(vel.time, vel.z, marker='.', ms=9., color='k', mfc='r')\n",
+ "# axs.set(ylim = (-200., 0.), title='Depth versus time: Ascent per velocity sensor')\n",
+ "\n",
+ "# vel.var\n",
+ "\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "04b8c6db",
+ "metadata": {},
+ "source": [
+ "## Part 3 Getting Data"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "518d45f8",
+ "metadata": {},
+ "source": [
+ "# Get Started With Data\n",
+ "\n",
+ "\n",
+ "This notebook introduces working with OOI data: From file to data structure to time series chart.\n",
+ "Visualization of data with depth (for example using profilers) is done in **`BioOptics.ipynb`**.\n",
+ "\n",
+ "\n",
+ "Starting assumption: A Dataset has been secured from OOI as a NetCDF file. \n",
+ "Within this directory we have `dataset.nc` to serve this role: Surface water\n",
+ "Chlorophyll-A fluorescence from Global Station Papa. \n",
+ "\n",
+ "\n",
+ "Using XArray Dataset methods we proceed:\n",
+ "\n",
+ "\n",
+ "- Read the file into memory as an XArray Dataset\n",
+ "- Print an overview of the Dataset: Dimensions, Coordinates, Data Variables and Attributes\n",
+ "- Modify the ordinal dimension `row` to `time`\n",
+ "- Along the time dimension: Drop all entries with value NAN (not a number)\n",
+ "- Rename the Data Variable for `chlorophyll` from a very long descriptive term to simply `chlora`\n",
+ "- Print an overview of the Dataset to note these changes\n",
+ "- Plot the chlorophyll data over the full time interval\n",
+ " - ...noting this runs from 2014 through 2020 with some gaps\n",
+ " - ...noting that August through October 2018 appears to be good data\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "692c4b46-acfd-4609-a07e-3501640e872f",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np, pandas as pd, xarray as xr\n",
+ "from numpy import datetime64 as dt64, timedelta64 as td64\n",
+ "import matplotlib.pyplot as plt\n",
+ "D = xr.open_dataset('./../dataset.nc')\n",
+ "D"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7778592b-d144-485a-b496-547dc409e919",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "D = D.swap_dims({'row':'time'}) # make 'time' the Dimension\n",
+ "D = D.dropna('time') # drop NaN data values\n",
+ "D=D.rename({'mass_concentration_of_chlorophyll_a_in_sea_water':'chlora'})\n",
+ "D "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3687a09e-ef52-447d-892c-82340ea081ad",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "print('Dataset starts:', D.time[0].time.data, '.....and ends:', D.time[-1].time.data)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "25c1b4c1-ca83-4213-aaca-1ded654b99a7",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "D.chlora.plot() "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bf9209b3-3766-4212-ab29-d2545ba235c8",
+ "metadata": {},
+ "source": [
+ "#### Interpretation so far...\n",
+ "\n",
+ "\n",
+ "The dataset D was read from a NetCDF file and manipulated in the above cells.\n",
+ "The last step was plotting the Chlorophyll A concentration as a time series.\n",
+ "Data is present in intermittent stretches along the time axis. The line jumps\n",
+ "indicate no data are available. \n",
+ "\n",
+ "\n",
+ "Next we focus on a time \n",
+ "range in 2018 where we appear to have good data: Aug 1 through Nov 1.\n",
+ "\n",
+ "\n",
+ "- Extract a time sub-range Aug 1 - Nov 1, 2018\n",
+ "- Drop NaN values\n",
+ "- Examine the resulting Dataset\n",
+ "- Examine a rough plot of the chlorophyll with time\n",
+ "- Resample the data from 15 minute intervals to day intervals\n",
+ "- Plot both day-interval and 15-minute-interval versions of chlorophyll with time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f01f8f6e-6302-46bf-b861-77cb7c42ed61",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "time0 = dt64('2018-08-01')\n",
+ "time1 = dt64('2018-11-01')\n",
+ "D = D.sel(time=slice(time0, time1)) # slice out data from the chosen time range\n",
+ "D = D.dropna('time') # (redundant) drop NaN values\n",
+ "D # inspect the dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "93b763e7-6bde-4449-b0c7-d3f937bfafe0",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "D.chlora.plot() # this plot shows a contiguous data record"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c70c5f68-d490-4555-b2f0-0c862456e1c9",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "D24=D.resample(time='24H').mean()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4d876841-16b9-4cd8-a23a-60467aa33846",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "fig, ax = plt.subplots(figsize=(8, 8), tight_layout=True)\n",
+ "ax.plot(D.time, D.chlora, color='red')\n",
+ "ax.plot(D24.time, D24.chlora, color='black')\n",
+ "ax.set(title = 'Chlorophyll (time): Global Station Papa 15 min (red) and daily (black)')\n",
+ "ax.set(ylim = (0., 3.))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5ed437db-6078-4bf1-ba9d-aa832edecbbd",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "#### Interpretation\n",
+ "\n",
+ "\n",
+ "The chlorophyll signal shows a four-fold increase through mid-October \n",
+ "and then drops sharply towards the end of the time series. \n",
+ "\n",
+ "\n",
+ "This fluorometer resides at a fixed depth on a mooring as part of the \n",
+ "Station Papa global array. \n",
+ "\n",
+ "\n",
+ "Profiling sensors add an additional level of \n",
+ "complexity."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e98dac7a-c1b3-48f5-9654-1f163a16f760",
+ "metadata": {},
+ "source": [
+ "# Here on: Residual notes\n",
+ "\n",
+ "\n",
+ "### To Do List\n",
+ "\n",
+ "\n",
+ "- interactive oceans data?\n",
+ "- reimagine ocean.py without charting; then depthchart.py\n",
+ "- maybe ./../data/experiment01 with a readme.txt and all the data in one place\n",
+ "- charts folder; then decorate the narrative with the charts\n",
+ "- make dim dropouts not plot diagonal lines\n",
+ "- revisit generating profiles from a depth time series\n",
+ "- programmatic data access through an API\n",
+ "- data streamlining\n",
+ " - Understand/annotate shallow profiler profile intervals (r,a,d)\n",
+ " - Sampling rates: Optional\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "### Profiler metadata: Calculate / Load timing data for rest - ascent - descent timestamps\n",
+ "\n",
+ "\n",
+ "- destination: subfolder `profiles`: `.csv` files corresponding to pandas dataframes\n",
+ " - row includes a profiler ascent start, descent start, rest start\n",
+ "- *axb*, *osb*, *oos*\n",
+ "- Annotate pH equil stops: both local midnight and noon\n",
+ " - Are things like T/C/DO stable during equil stops? \n",
+ "- Does chlorophyll versus backscatter show more zooplankton at depth? Diel?\n",
+ " - ArcticGRO 29\n",
+ " \n",
+ "\n",
+ "### Spectrophotometer \n",
+ "\n",
+ "Dimensions `obs` and `wavelength`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d5e97bfe-b423-44b4-a464-3f64d9ead3cb",
+ "metadata": {},
+ "source": [
+ "# This goes where?\n",
+ "\n",
+ "\n",
+ "### Selecting profile ascent data from the profiler time series\n",
+ "\n",
+ "* A separate set of notebooks (see Notebooks folder) produced tables of profile start/end times\n",
+ " * Results are saved in the Profiles folder of this repository\n",
+ " * Each saved CSV file is for a single year and a single location (from OSB, OOS, AXB)\n",
+ " * Starting from 2015 and running through 2021 this results in 3 x 7 = 21 files\n",
+ " * Owing to technical issues: Oregon Offshore is missing for 2021\n",
+ " * Here we use strictly one of these: For OSB and 2021: **`osb2021.csv`**.\n",
+ " \n",
+ "* **`ReadProfileMetadata(fnm)`** loads profile metadata into a (pandas) Dataframe **`pDf`**\n",
+ " * Each row of **`pDf`**: dt64 times: start/end for all three of: ascent, descent, rest\n",
+ " * We focus on **`ascent_start`** and **`ascent_end`**\n",
+ " * The reference is hence pDf['ascent_start'][integer_index]\n",
+ " * For OSB and 2021: integer_index = 0 will be the first (UTC) profile on January 1, 2021 at OSB\n",
+ " * At nine profiles per day there are a maximum of 365 * 9 profiles in a year\n",
+ " * We expect occasional drops and intervals of time when the profiler was not running\n",
+ " \n",
+ "* **`GenerateTimeWindowIndices(pDf, date0, date1, time0, time1)`** produces a list of table row-indices for **`pDf`**\n",
+ " * date0 and date1 define a range of days\n",
+ " * time0 and time1 define a time range applied to each day\n",
+ " * the list is extended for each **`ascent_start`** in **`pDf`** that falls within both\n",
+ " \n",
+ "* Suppose we want to see only nitrate profiles, i.e. the noon and midnight profiles from each day\n",
+ " * First generate a list using the midnight time range, say `list_midn`\n",
+ " * Second generate a list using the noon time range, say `list_noon`\n",
+ " * Combine the lists: These will be non-sequential indices for **`pDf`** rows\n",
+ " * Use the list sorting method `list.sort()` to order this combined list. Result will be time-sequential. \n",
+ " \n",
+ "* Suppose we want sensor vs depth charts for a set of profiles\n",
+ " * ...we have the corresponding list of profile indices (indexing into **`pDf`**) called **`pIdcs`**.\n",
+ " * ...we have a Dataset of data from sensor **X** called **`dsX`**\n",
+ " * Run an index **`i`** across **`pIdcs`**\n",
+ " * For index **`i`** set a time range: **`t0, t1 = pDf['ascent_start'][pIdcs[i]], pDf['ascent_end'][pIdcs[i]]`**\n",
+ " * Select data from the Dataset using this time range: **`Xs = dsX.sel(time=slice(t0, t1))`**\n",
+ " \n",
+ "* To streamline comparison-charting for two sensors A and B we have the function **`ChartAB()`**\n",
+ " * **`def ChartAB(pDf, xrng, pIdcs, A, Az, Albl, Acolor, B, Bz, Blbl, Bcolor)`**\n",
+ " * **`pDf`** is the profile metadata described above\n",
+ " * **`xrng`** is a list of low/high tuples for A and B\n",
+ " * **`pIdcs`** is a list of profile indices. \n",
+ " * The length of this list corresonds to the number of charts to be produced\n",
+ " * A and Az are data and z-value DataArrays, same for B and Bz\n",
+ " * From a Dataset **`As`** these would be passed as **`As.sensorname`** and **`As.z`**\n",
+ "\n",
+ "\n",
+ "- nitrate sequence to include midnight and noon both\n",
+ "- Place in this section a brief description of element-wise calculation (see shear, below)\n",
+ " - Include integrating shear as a new DataArray in a Dataset\n",
+ "- Consider: Filter velocity e.g. 30 meter box filter before calculating shear\n",
+ "\n",
+ "```\n",
+ "def streamline_data(source, output, keep_dims, keep_coords, keep_data_vars, keep_attrs):\n",
+ "\n",
+ " def timeswap_preprocessor(fds): # per-file datasets have dimension 'obs'\n",
+ " a = fds.swap_dims({'obs':'time'}) # ...so we pre-swap that for time\n",
+ " for key in a.dims: \n",
+ " if key not in keep_dims: a = a.drop_dims(key)\n",
+ " for key in a.coords: \n",
+ " if key not in keep_coords: a = a.drop(key)\n",
+ " for key in a.data_vars: \n",
+ " if key not in keep_data_vars: a = a.drop(key)\n",
+ " attrs_dict = a.attrs.copy() \n",
+ " for key in attrs_dict: \n",
+ " if key not in keep_attrs: a.attrs.pop(key)\n",
+ " return a\n",
+ " \n",
+ " ds = xr.open_mfdataset(source, preprocess = timeswap_preprocessor, concat_dim='time', combine='by_coords') \n",
+ " ds.to_netcdf(output)M\n",
+ " \n",
+ " return ds\n",
+ "\n",
+ "\n",
+ "# Example: pH sampling tracking the profiler\n",
+ "ds_phsen.sel(time=slice(dt64('2020-11-23T00:00:00'),dt64('2020-11-26T00:00:00'))).depth.plot()\n",
+ "\n",
+ "\n",
+ "def prepro(fds): \n",
+ " return fds.swap_dims({'obs':'time'})\n",
+ "\n",
+ "sample_file = 'deployment*.nc'\n",
+ "ds = xr.open_mfdataset(os.getenv(\"HOME\") + '/data_CTD_entire_time_range/' + sample_file, preprocess = prepro, compat='no_conflicts')\n",
+ "\n",
+ "\n",
+ "\n",
+ "# - 3-Wavelength fluorometer (flort: got it for OSB SP 2019)\n",
+ "# - CTD (ctdpf: got it for OSB SP 2019)\n",
+ "# - Photosynthetically Available Radiation (parad: got it for OSB SP 2019)\n",
+ "# - pH (phsen: got it for OSB SP 2019)\n",
+ "# - Spectral Irradiance (spkir: got)\n",
+ "# - Spectrophotometer (optaa: got)\n",
+ "# - NOT YET: Single Point Velocity Meter (velpt: )\n",
+ "# - Nitrate (nutnr: Got both nutnr_a_sample and nutnr_a_dark_sample)\n",
+ "# - pCO2 water (two streams: pco2w_a_sami_data_record and pco2w_b (no data past 2018; placed 2018 data\n",
+ "\n",
+ "# instrument 2014 2015 2016 2017 2018 2019 2020\n",
+ "#\n",
+ "# Oregon Slope Base\n",
+ "# SP flort 3-wavelength !\n",
+ "# SP ctdpf !\n",
+ "# SP parad !\n",
+ "# SP phsen !\n",
+ "# SP spkir !\n",
+ "# SP optaa !\n",
+ "# SP velpt !\n",
+ "# SP nutnr_a, nutnr_a_dark !\n",
+ "# SP pco2w_a_sami !\n",
+ "# SP pco2w_b_sami ! NA\n",
+ "# 200m ctdpf !\n",
+ "# 200m flort !\n",
+ "# 200m phsen !\n",
+ "# 200m do_stable !\n",
+ "# DP ctdpf wfp ! NA NA NA\n",
+ "# DP ctdpf inst !\n",
+ "# DP acm (VEL3D) inst !\n",
+ "# DP flcdrdt inst fluorometer !\n",
+ "#\n",
+ "# Axial Base\n",
+ "# SP flort !\n",
+ "# SP ctdpf !\n",
+ "# SP parad !\n",
+ "# SP phsen !\n",
+ "# SP spkir ?\n",
+ "# SP optaa !\n",
+ "# SP velpt ?\n",
+ "# SP nutnr_a, nutnr_a_dark ?\n",
+ "# SP pco2w_a_sami !\n",
+ "# SP pco2w_b_sami ?\n",
+ "# 200m ctdpf !\n",
+ "# 200m flort !\n",
+ "# 200m phsen !\n",
+ "# 200m do_stable !\n",
+ "# DP ctdpf wfp !\n",
+ "# DP ctdpf inst !\n",
+ "# DP acm (VEL3D) inst ?\n",
+ "# DP flcdrdt inst CDOM fluorometer !\n",
+ "# DP fl????? inst 2-Wav fluorometer ?\n",
+ "# DP dissolved oxygen !\n",
+ "# \n",
+ "# filename anatomy\n",
+ "# deployment0005 or 0006 etc\n",
+ "# _RS03AXPS site: AX is Axial, SB is slope base\n",
+ "# -SF03A platform: SF is shallow profiler, DP is deep profiler, PC is 200m platform \n",
+ "# -3B number + letter: unknown\n",
+ "# -OPTAAD301 6-letter instrument + 'A'/'D' + 30X/10X\n",
+ "# -streamed 'streamed' or 'recovered_inst' or 'recovered_wfp'\n",
+ "# -optaa_sample instrument designator, sometimes 'dpc_xxxxx_instrument_recovered'\n",
+ "# _20191004T073957.414490 datetime start\n",
+ "# -20191014T220233.907019 datetime end\n",
+ "# .nc NetCDF file\n",
+ "#\n",
+ "```\n",
+ "\n",
+ "\n",
+ "ds=xr.open_mfdataset(...filename description string including wildcard...)\n",
+ "ds = ds.swap_dims({'obs':'time'})\n",
+ "\n",
+ "\n",
+ "See use of a *preprocessor* function; or avoid this by putting the data into 'time' dimension first"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e2a7c2d8-815f-43c1-b7f1-65f6dd16f2a7",
+ "metadata": {},
+ "source": [
+ "## More un-sorted code resources\n",
+ "\n",
+ "```\n",
+ "\n",
+ "colorT = 'black'\n",
+ "colorS = 'xkcd:blood orange'\n",
+ "colorO = 'xkcd:blue'\n",
+ "colorA = 'xkcd:green'\n",
+ "colorB = 'xkcd:dark cyan'\n",
+ "colorC = 'red'\n",
+ "colorN = 'xkcd:gold'\n",
+ "colorP = 'magenta'\n",
+ "colorH = 'xkcd:purple blue'\n",
+ "\n",
+ "colorTd = 'grey'\n",
+ "colorSd = 'xkcd:yellow orange'\n",
+ "colorOd = 'xkcd:azure'\n",
+ "colorAd = 'xkcd:pale green'\n",
+ "colorBd = 'xkcd:light turquoise'\n",
+ "colorCd = 'xkcd:pinkish'\n",
+ "colorNd = 'xkcd:brownish yellow'\n",
+ "colorPd = 'xkcd:barbie pink'\n",
+ "colorHd = 'xkcd:pastel purple'\n",
+ "\n",
+ "\n",
+ "# time ranges for midnight and noon profiles, adjusted for UTC\n",
+ "# midn0 - midn1 is a time range for the midnight profile start\n",
+ "# noon0 - noon1 is a time range for the noon profile start\n",
+ "midn0 = td64( 7*60 + 10, 'm') # 7 hours 10 minutes\n",
+ "midn1 = td64( 7*60 + 34, 'm') # 7 hours 34 minutes\n",
+ "noon0 = td64(20*60 + 30, 'm') # 20 hours 30 minutes\n",
+ "noon1 = td64(20*60 + 54, 'm') # 20 hours 54 minutes \n",
+ "\n",
+ "\n",
+ "\n",
+ "def GetDiscreteSummaryCastSubset(dsDf, cast, columns):\n",
+ " '''\n",
+ " dsDf is a Discrete Summary Dataframe\n",
+ " cast is a string corresponding to the cast identifier, e.g. 'CTD-001'\n",
+ " columns is a list of column names to extract from the full Dataframe\n",
+ " Returns a Dataframe for 'just that cast' and 'just those parameters'\n",
+ " '''\n",
+ " return dsDf.loc[(dsDf['cast']==cast)][columns]\n",
+ "\n",
+ "\n",
+ "def CompareAscentDescent(p, T, S, O, A, B, C):\n",
+ " '''Get a sense of variability between ascent and subsequent descent'''\n",
+ " \n",
+ " pw = GenerateTimeWindowIndices(p, dt64_from_doy(2021, 65), dt64_from_doy(2021, 65), td64(0, 'h'), td64(24, 'h'))\n",
+ " ncharts = len(pw)\n",
+ "\n",
+ " fig, axs = plt.subplots(ncharts, 3, figsize=(15, 4*ncharts), tight_layout=True)\n",
+ "\n",
+ " axt0 = [axs[i][0].twiny() for i in range(ncharts)]\n",
+ " axt1 = [axs[i][1].twiny() for i in range(ncharts)]\n",
+ " axt2 = [axs[i][2].twiny() for i in range(ncharts)]\n",
+ "\n",
+ " for i in range(pw[0], pw[-1]+1):\n",
+ "\n",
+ " axi = i - pw[0]\n",
+ "\n",
+ " t0, t1, t2 = p[\"ascent_start\"][i], p[\"ascent_end\"][i], p[\"descent_end\"][i]\n",
+ "\n",
+ " Ta = T.sel(time=slice(t0, t1))\n",
+ " Td = T.sel(time=slice(t1, t2))\n",
+ " Sa = S.sel(time=slice(t0, t1))\n",
+ " Sd = S.sel(time=slice(t1, t2))\n",
+ " Oa = O.sel(time=slice(t0, t1))\n",
+ " Od = O.sel(time=slice(t1, t2))\n",
+ " Aa = A.sel(time=slice(t0, t1))\n",
+ " Ad = A.sel(time=slice(t1, t2))\n",
+ " Ba = B.sel(time=slice(t0, t1))\n",
+ " Bd = B.sel(time=slice(t1, t2))\n",
+ " Ca = C.sel(time=slice(t0, t1))\n",
+ " Cd = C.sel(time=slice(t1, t2))\n",
+ "\n",
+ " axs[axi][0].plot(Ta.temp, Ta.z, color=colorT, marker='s', ms=4., mfc=colorT)\n",
+ " axs[axi][0].plot(Td.temp, Td.z, color=colorTd, marker='v', ms=4., mfc=colorTd)\n",
+ " axt0[axi].plot(Sa.salinity, Sa.z, color=colorS, marker='o', ms=4., mfc=colorS)\n",
+ " axt0[axi].plot(Sd.salinity, Sd.z, color=colorSd, marker='^', ms=4., mfc=colorSd)\n",
+ "\n",
+ " axs[axi][1].plot(Oa.doxygen, Oa.z, color=colorO, marker='s', ms=4., mfc=colorO)\n",
+ " axs[axi][1].plot(Od.doxygen, Od.z, color=colorOd, marker='v', ms=4., mfc=colorOd)\n",
+ " axt1[axi].plot(Aa.chlora, Aa.z, color=colorA, marker='o', ms=4., mfc=colorA)\n",
+ " axt1[axi].plot(Ad.chlora, Ad.z, color=colorAd, marker='^', ms=4., mfc=colorAd)\n",
+ "\n",
+ " axs[axi][2].plot(Ba.backscatter, Ba.z, color=colorB, marker='s', ms=4., mfc=colorB)\n",
+ " axs[axi][2].plot(Bd.backscatter, Bd.z, color=colorBd, marker='v', ms=4., mfc=colorBd)\n",
+ " axt2[axi].plot(Ca.cdom, Ca.z, color=colorC, marker='o', ms=4., mfc=colorC)\n",
+ " axt2[axi].plot(Cd.cdom, Cd.z, color=colorCd, marker='^', ms=4., mfc=colorCd)\n",
+ "\n",
+ " axs[axi][0].set(ylim=(-200., 0.))\n",
+ " axs[axi][1].set(ylim=(-200., 0.))\n",
+ " axs[axi][2].set(ylim=(-200., 0.))\n",
+ "\n",
+ " axs[axi][0].set(xlim=(temp_lo, temp_hi))\n",
+ " axs[axi][1].set(xlim=(do_lo, do_hi))\n",
+ " axs[axi][2].set(xlim=(bb_lo, bb_hi))\n",
+ "\n",
+ "\n",
+ " axs[0][0].set(title='Temp (black) and Salinity (orange)')\n",
+ " axs[0][1].set(title='Oxygen (blue) and Chlorophyll (green)')\n",
+ " axs[0][2].set(title='CDOM (red) and Backscatter (cyan)')\n",
+ "\n",
+ " fig.show()\n",
+ "\n",
+ " # For additional labeling:\n",
+ " # axs[iC][0].text(7.4, -14, 'S')\n",
+ " # axs[iC][0].text(10.2, -14, 'T')\n",
+ " # axs[iC][1].text(170, -30, 'Chl-A')\n",
+ " # axs[iC][1].text(300, -150, 'DO')\n",
+ " # axs[iC][2].text(.0007, -20, 'CDOM')\n",
+ " # axs[iC][2].text(.0013, -75, 'SCATT') \n",
+ " \n",
+ " return\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "# GLODAP Data Loader\n",
+ "# Requires boto + target directory has write permission\n",
+ "if False: # disabled once the datasets are loaded into /data/glodap\n",
+ "\n",
+ " glodapTemperatureFnm = data_dir + '/glodap/glodap_temperature.nc'\n",
+ " glodapSalinityFnm = data_dir + '/glodap/glodap_salinity.nc'\n",
+ " glodapOxygenFnm = data_dir + '/glodap/glodap_oxygen.nc'\n",
+ "\n",
+ " import boto\n",
+ " from boto.s3.key import Key\n",
+ "\n",
+ " connection = boto.connect_s3(anon=True)\n",
+ " bucket = connection.get_bucket('fixthisshouldhavesecurebucketnamehere')\n",
+ "\n",
+ " for key in bucket.list(): \n",
+ " filename = key.name.encode('utf-8')\n",
+ " if b'glodap' in filename: \n",
+ " if b'salinity.nc' in filename: \n",
+ " print ('salinity file is', filename)\n",
+ " salinityfilename = filename\n",
+ " if b'temperature.nc' in filename: \n",
+ " print ('temperature file is', filename)\n",
+ " temperaturefilename = filename\n",
+ " if b'oxygen.nc' in filename: \n",
+ " print('oxygen file is', filename)\n",
+ " oxygenfilename = filename \n",
+ "\n",
+ " k = Key(bucket)\n",
+ " k.key = salinityfilename\n",
+ " k.get_contents_to_filename(glodapSalinityFnm)\n",
+ " k.key = temperaturefilename\n",
+ " k.get_contents_to_filename(glodapTemperatureFnm)\n",
+ " k.key = oxygenfilename\n",
+ " k.get_contents_to_filename(glodapOxygenFnm)\n",
+ "\n",
+ " print('\\ndata load complete for glodap')\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5794aee6-cc2d-4c49-9571-a3bb0d3f524a",
+ "metadata": {},
+ "source": [
+ "# Dashboards\n",
+ "\n",
+ "temporary notes stash; comments in ***bold italics***.\n",
+ "\n",
+ "***Bottom line: This is a great deal of inside baseball.***\n",
+ "\n",
+ "\n",
+ "* URL is [https://qaqc.ooi-rca.net/](https://qaqc.ooi-rca.net/)\n",
+ "* APL Status Dashboards\n",
+ " * Notice `ooirsn.uw.edu` is the base URL. `eng.` prefix not working today; Nereus works fine.\n",
+ " * [Nereus](https://nereus.ooirsn.uw.edu/) Works: Looks like the main operational status dashboard\n",
+ " * [Nereus: Suspect Instrument List](https://nereus.ooirsn.uw.edu/suspect-instruments) Works.\n",
+ " * [Nereus M2M Plots](https://nereus.ooirsn.uw.edu/m2m-data-plots) ('Machine 2 Machine' is a legacy term for an API interface I think)\n",
+ " * [APL Eng dashboard](http://eng.ooirsn.uw.edu/)\n",
+ " * [APL RealTime Dashboard](http://rtime.ooirsn.uw.edu/)\n",
+ " * [APL PP-Up](http://sea.ooirsn.uw.edu/power/graphs.html) Tried: Nope. Power graphs?\n",
+ " * [APL Camera Videos by Day](http://dstill.ooirsn.uw.edu/) Tried: Could not reach\n",
+ " * [OMS](https://io.ocean.washington.edu/oms_data/) Very down-in-the-weeds engineering data (see profiler summaries, for example)\n",
+ " * I do not see OrcaSound: Does that still exist?\n",
+ "* Data: by Site\n",
+ " * ***English site names would be pleasant***\n",
+ " * Axial Base is easy enough AXBS but... no profiles; just fixed depth\n",
+ " * ***Ok a User Manual would be good***\n",
+ " * AXPS is more the ticket for profiler data\n",
+ " * Fixed Depths works out of the box: One week of Density Oxygen Salinity Temperature (Density shows tides)\n",
+ " * ***Is this the shallow profiler platform maybe?***\n",
+ " * Depth binned\n",
+ " * ***Standard range ought to be the default for apples to apples slider experience***\n",
+ " * ***Slider left to right is surface to depth; depth ranges in title (fine print)***\n",
+ " * ***Multi-sensor (VEL, SPKIR) seems to be folded into the charts; very beta***\n",
+ " * Chlorophyll is a good example (AXPS) of using the slider to prospect in time with depth\n",
+ " * Profiles\n",
+ " * Another nice way to prospect with the slider\n",
+ " * Daily and Yearly are puzzling; Weekly and Monthly seem more functional\n",
+ " * ***In weekly mode: How many profiles do we see? Density does not look like 7 x 9 profiles***\n",
+ " * ***In weekly mode: Is the slider quantized at days to give a moving time window view?***\n",
+ "* Data: by Platform Type\n",
+ " * Shallow Profilers\n",
+ " * Deep Profilers: Anything to be found here???\n",
+ "* Data Stage 1:\n",
+ "* Data Stage 2:\n",
+ "* Data Stage 3:\n",
+ "* Data Stage 4: Not much here; maybe some HITL stuff?\n",
+ "\n",
+ " \n",
+ "\n",
+ "\n",
+ "# sklearn\n",
+ "\n",
+ "```\n",
+ "# NOTE: Using dir() on sklearn and sub-libraries can be confusing. Not everything\n",
+ "# we want shows up. Left as an exercise: Find out how to find out what sklearn\n",
+ "# can do. Here follow some puzzling fragments.\n",
+ "#\n",
+ "# import sklearn\n",
+ "# dir(sklearn)\n",
+ "# dir(sklearn.utils)\n",
+ "# dir(sklearn.cluster.KMeans)\n",
+ "#\n",
+ "# from sklearn.cluster import KMeans\n",
+ "# kmeans = KMeans() \n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8e825810-9842-49e8-9dd7-7714455462fc",
+ "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.11.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/book/chapters/rob/glodap.ipynb b/book/chapters/glodap.ipynb
similarity index 100%
rename from book/chapters/rob/glodap.ipynb
rename to book/chapters/glodap.ipynb
diff --git a/book/chapters/rob/issues.ipynb b/book/chapters/issues.ipynb
similarity index 100%
rename from book/chapters/rob/issues.ipynb
rename to book/chapters/issues.ipynb
diff --git a/book/chapters/rob/modis.ipynb b/book/chapters/modis.ipynb
similarity index 100%
rename from book/chapters/rob/modis.ipynb
rename to book/chapters/modis.ipynb
diff --git a/book/chapters/rob/modis.py b/book/chapters/modis.py
similarity index 100%
rename from book/chapters/rob/modis.py
rename to book/chapters/modis.py
diff --git a/book/chapters/rob/ocean_science.ipynb b/book/chapters/ocean_science.ipynb
similarity index 88%
rename from book/chapters/rob/ocean_science.ipynb
rename to book/chapters/ocean_science.ipynb
index e5a58ca..a209ec4 100644
--- a/book/chapters/rob/ocean_science.ipynb
+++ b/book/chapters/ocean_science.ipynb
@@ -10,106 +10,20 @@
"[Jupyter Book](https://geo-smart.github.io/oceanography/intro.html) and [GitHub repo](https://github.com/geo-smart/oceanography).\n",
"\n",
"\n",
- "This image-includer works locally for jpg, png and svg.\n",
"\n",
- "\n",
- " \n",
- "\n",
- "
\n",
- " \n",
- "\n",
- " \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- "\n",
- "
\n",
- " \n",
- "\n",
- "
\n",
- " \n",
- "\n",
- " \n",
- "\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "\n",
- "\n",
- "The *figure* and *image* directives work in the Jupyter Book but not locally (yet):\n",
- "\n",
- " \n",
- "
\n",
- "\n",
- "\n",
- "Here is the `![alt](link)` construct which works locally and in the Book.\n",
- "Down side: Renders are actual size.\n",
- "\n",
- "
\n",
"\n",
- "![Badge](../../img/use_case_badge.svg)\n",
+ "The main purpose of this chapter is to present the research ideas. A lot of this Jupyter Book \n",
+ "concerns reproducible methodology so it is a good idea to have some 'Why?' in mind before starting\n",
+ "in on the What? and the How?\n",
"\n",
- "
\n",
"\n",
"\n",
"## Themes\n",
@@ -239,9 +153,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Further background: Science and Technical\n",
- "\n",
- " \n",
+ "## From Data to Insight\n",
+ " \n",
"### Technical\n",
"\n",
"#### Visualization\n",
@@ -249,15 +162,19 @@
"##### Compressing time as a curtain plot\n",
" \n",
" \n",
- "When the profiler has run for several months we can squeeze the data horizontally to form a curtain. \n",
- "These data show variability in the photic zone over several months, for example. The example below\n",
+ "When the profiler has run for several months we can squeeze the data horizontally in time.\n",
+ "That is: One profile is a color-coded vertical line. Pressing these together as a time-ordered\n",
+ "sequence renders as a curtain, hence 'curtain plot'. \n",
+ "The data show variability in the photic zone over several months, for example. The example below\n",
"color-codes chlorophyll concentration.\n",
"\n",
"\n",
- "##### Using motion rendering for three-dimensional charts\n",
+ "##### Motion rendering for three-dimensional charts\n",
+ "\n",
"\n",
"#### Data file format\n",
"\n",
+ "\n",
"- NetCDF is the primary data file format\n",
" - Consists of a two-level heirarchy\n",
" - Top level: Groups (may or may not be present)\n",
@@ -266,9 +183,13 @@
" - XArray is the Python library used to parse and manipulate NetCDF data\n",
" - The central data structure in XArray is the DataArrays\n",
" - DataArrays are often bundled together to form Datasets\n",
- " - Both DataArrays and Datasets as objects include parsing and filtering methods\n",
- "\n",
- "\n",
+ " - Both DataArrays and Datasets as objects include parsing and filtering methods"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
"### Science\n",
"\n",
"\n",
@@ -317,7 +238,7 @@
"### Metabolic energy for an apex predator\n",
"\n",
"\n",
- "```{figure} ../../img/Sphyrna_mokarran.png.jpg\n",
+ "```{figure} ../../img/Sphyrna_mokarran.png\n",
"---\n",
"height: 500px\n",
"name: directive-fig\n",
@@ -353,7 +274,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 1,
"metadata": {},
"outputs": [
{
@@ -516,16 +437,13 @@
"\n",
"\n",
"\n",
- " \n",
- "\n",
- "
\n",
- " \n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
+ "```{figure} ../img/shallowprofilerinsitu.png\n",
+ "---\n",
+ "height: 300px\n",
+ "name: directive-fig\n",
+ "---\n",
+ "Research Vessel Revelle (Scripps)\n",
+ "```\n",
"\n",
"\n",
"Under normal circumstances\n",
@@ -558,6 +476,13 @@
"effectively treat profiles as \"instantaneous\" snapshots of upper water column. "
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
{
"cell_type": "code",
"execution_count": null,
diff --git a/book/chapters/rob/shallowprofiler.ipynb b/book/chapters/rob/shallowprofiler.ipynb
deleted file mode 100644
index 19a0597..0000000
--- a/book/chapters/rob/shallowprofiler.ipynb
+++ /dev/null
@@ -1,891 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "63f97121-dd06-44b0-9886-e9e996417339",
- "metadata": {},
- "source": [
- "# Shallow Profiler\n",
- "\n",
- "\n",
- "[Jupyter Book](https://geo-smart.github.io/oceanography/intro.html), [GitHub repo](https://github.com/geo-smart/oceanography).\n",
- "\n",
- "\n",
- "\n",
- "This notebook introduces physical and bio-optical data from the Ocean Observatories \n",
- "Initiative (***OOI***) Regional Cabled Array (***RCA***) *Shallow Profiler*, shown below. \n",
- "These platforms are anchored to the sea floor by cables. They are positively buoyant\n",
- "and are parked at a depth of 200 meters where they continuously record sensor data. \n",
- "By means of a cable/winch they regularly profile (sample) the upper 200 meters of the \n",
- "water column, hence the name shallow profiler.\n",
- "\n",
- "\n",
- "Some further detail on how data are collected: The bulbous pod in the photo below \n",
- "(called the Science Pod or SCIP) is attached to the moored rectangular platform \n",
- "by means of the yellow cable. The profiler ascends and then descends nine times per day.\n",
- "Multiple instruments are visible, bolted to the SCIP, where each instrument bears\n",
- "one or more sensors. Hence the sensors correspond to types of data: Temperature, \n",
- "chlorophyll fluorescence and so on. \n",
- "\n",
- "\n",
- "Try 1\n",
- "\n",
- " \n",
- " \n",
- "\n",
- "
\n",
- "\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "fcc8515b-51f1-4c29-a68b-329949cc525a",
- "metadata": {},
- "source": [
- "### Embedding content \n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "#### Inline images\n",
- "\n",
- "\n",
- "For this use HTML in a markdown cell. The path points to a relative `images/topic` subfolder.\n",
- "\n",
- "\n",
- " \n",
- "\n",
- "
\n",
- " \n",
- "\n",
- "\n",
- "#### Local animation file (mp4) playback\n",
- "\n",
- "\n",
- "```\n",
- "from IPython.display import HTML, Video\n",
- "Video('../images/animations/multisensor_animation.mp4', embed=True)\n",
- "``` \n",
- " \n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "f9db8cb9-4dd7-4014-96aa-40fe967ea1c1",
- "metadata": {
- "tags": []
- },
- "outputs": [
- {
- "data": {
- "text/html": [
- ""
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 1,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from IPython.display import HTML, Video\n",
- "Video('../images/animations/multisensor_animation.mp4', embed=True, width = 500, height = 500)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "dae0986e-9d6e-4318-8a18-4af07baa9158",
- "metadata": {},
- "source": [
- "#### Audio file (mp3) playback\n",
- "\n",
- "\n",
- "```\n",
- "from IPython.display import Audio\n",
- "Audio(\".mp3\")\n",
- "```\n",
- "\n",
- " \n",
- "#### YouTube video playback\n",
- "\n",
- "\n",
- "```\n",
- "from IPython.display import YouTubeVideo\n",
- "YouTubeVideo('sjfsUzECqK0')\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "id": "15435601-0c8b-4fdc-a4d3-1f26f3a10ab1",
- "metadata": {
- "tags": []
- },
- "outputs": [
- {
- "data": {
- "image/jpeg": "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\n",
- "text/html": [
- "\n",
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 40,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from IPython.display import YouTubeVideo\n",
- "YouTubeVideo('sjfsUzECqK0')"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0d259407-4353-41d6-98c1-6a5b82c30fca",
- "metadata": {},
- "source": [
- "### Shell integration\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "### Working with NetCDF XArray and Pandas\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "#### Summary\n",
- "\n",
- "\n",
- "For a general take on data manipulation, particularly with `pandas`: \n",
- "See Jake VanDerplas' excellent book Python Data Science Handbook. \n",
- "\n",
- "\n",
- "We have here multi-dimensional oceanography datasets in\n",
- "NetCDF and CSV format. Corresponding Python libraries are `XArray` and `pandas`.\n",
- "On import these libraries are abbreviated `xr` and `pd` respectively.\n",
- "\n",
- "\n",
- "The XArray method `.open_dataset('somefile.nc')` returns an XArray Dataset:\n",
- "A set of XArray DataArrays. The Dataset includes four (or more*) sections: `Dimensions`, \n",
- "`Coordinates`, `Data Variables`, and `Attributes`. To examine a Dataset\n",
- "called `A`: Run `A` (i.e. on a line by itself) to see these constituent sections. \n",
- "\n",
- "\n",
- "* \"more than four\": Discovered while looking at seismic (DAS) data: Some XArray\n",
- "data may include yet another internal organizing structure.\n",
- "\n",
- "\n",
- "In pandas the data structure is a DataFrame. Here these are used to manage \n",
- "shallow profiler ascent/descent/rest metadata.\n",
- "\n",
- "\n",
- "Common reductive steps once data are read include removing extraneous components from\n",
- "a dataset, downsampling, removing NaN values, changing the primary `dimension`\n",
- "from `obs` (for 'observation') to `time`, combining multiple data files into \n",
- "a single dataset, saving modified datasets to new files, and creating charts. \n",
- "\n",
- "\n",
- "Datasets that reside within this [GitHub repository](https://github.com/robfatland/ocean)\n",
- "have to stay pretty small. Larger datasets are downloaded to an external folder. \n",
- "See for example the use of `wget` in the **`Global Ocean`** notebook.\n",
- "The following code shows reduction of a global ocean temperature data file to just\n",
- "the data of interest (temperature as a 3-D scalar field). \n",
- "\n",
- "\n",
- "```\n",
- "# Reduce volume of an XArray Dataset with extraneous Data Variables:\n",
- "T=xr.open_dataset('glodap_oxygen.nc')\n",
- "T.nbytes\n",
- "T=T[['temperature', 'Depth']]\n",
- "T.nbytes\n",
- "T.to_netcdf('temperature.nc') \n",
- "```\n",
- "\n",
- "Data can be down-sampled for example by averaging multiple samples. A tradeoff in down-sampling \n",
- "Regional Cabled Array shallow profiler data however is this: Data collected during profiler \n",
- "ascent spans 200 meters of water column depth in one hour, or about 6 centimeters per sec. \n",
- "A 'thin layer' of signal variation might be washed out by combining samples. \n",
- "\n",
- "\n",
- "This repository does include a number of examples of down-sampling and otherwise selecting out\n",
- "data subsets. \n",
- "\n",
- "\n",
- "\n",
- "\n",
- "#### XArray Datasets and DataArrays\n",
- "\n",
- "\n",
- "##### Summary\n",
- "\n",
- "There are a million little details about working with XArray Datasets, DataArrays, numpy arrays, pandas DataFrames,\n",
- "pandas arrays... let's begin! The main idea is that a **DataArray** is an object containing, in the spirit of \n",
- "the game, one sort of data; and a **Dataset** is a collection of associated **DataArray**s. \n",
- "\n",
- "\n",
- "##### XArray ***Dataset*** basics\n",
- "\n",
- "**Datasets** abbreviated `ds` have components { dimensions, coordinates, data variables, \n",
- "attributes }.\n",
- "\n",
- "\n",
- "A **DataArray** relates to a **`name`**; needs elaboration. \n",
- "\n",
- "\n",
- "```\n",
- "ds.variables\n",
- "\n",
- "ds.data_vars # 'dict-like object'\n",
- "\n",
- "for dv in ds.data_vars: print(dv)\n",
- " \n",
- "choice = 2\n",
- "this_data_var = list(ds.data_vars)[choice]\n",
- "print(this_data_var)\n",
- "\n",
- "ds.coords\n",
- "ds.dims\n",
- "ds.attrs\n",
- "```\n",
- "\n",
- "\n",
- "\n",
- "##### Load via `open_mfdataset()` with dimension swap from `obs` to `time`\n",
- "\n",
- "\n",
- "A single NetCDF (`.nc`) file can be opened as an XArray Dataset using `xr.open_dataset(fnm)`. \n",
- "Multiple files can be opened as a single XArray Dataset via `xr.open_mfdataset(fnm*.nc)`. \n",
- "`mf` stands for `multi-file`. Note \n",
- "the wildcard `fnm*` is supported. \n",
- "\n",
- "```\n",
- "def my_preprocessor(fds): return fds.swap_dims({'obs':'time'})\n",
- "\n",
- "ds = xr.open_mfdataset('files*.nc', \\\n",
- " preprocess = my_preprocessor, \\\n",
- " concat_dim='time', combine='by_coords')\n",
- "```\n",
- "\n",
- "##### Obstacle: Getting information out of a Dataset\n",
- "\n",
- "\n",
- "There is a sort of comprehension / approach that I have found hard to internalize.\n",
- "With numpy ndarrays, XArray Datasets, etcetera there is this \"how do I get at it?\"\n",
- "problem. As this documentation evolves I will try and articulate the most helpful\n",
- "mindset. The starting point is that Datasets are built as collections of DataArrays; \n",
- "and these have an indexing protocol the merges with a method protocol (`sel`, `merge`\n",
- "and so on) where the end-result code that does what I want is inevitably very \n",
- "elegant. So it is a process of learning that elegant sub-language...\n",
- "\n",
- "\n",
- "##### Synthesizing along the dimension via `.concat`\n",
- "\n",
- "\n",
- "```\n",
- "ds_concat = xr.concat([ds.sel(time=\n",
- "```\n",
- "\n",
- "\n",
- "##### Recover a time value as `datetime64` from a Dataset by index\n",
- "\n",
- "\n",
- "If `time` is a `dimension` it can be referenced via `ds.time[i]`. However\n",
- "this will be a 1-Dimensional, 1-element DataArray. Adding `.data`\n",
- "and casting the resulting ndarray (with one element) as a `dt64` works.\n",
- "\n",
- "```dt64(ds.time[i].data)```\n",
- "\n",
- "\n",
- "##### Example: XArray transformation flow\n",
- " \n",
- " \n",
- "As an example of the challenge of learning `XArray`: The reduction of this data to binned profiles\n",
- "requires a non-trivial workflow. A naive approach can result in a calculation that should take \n",
- "a seconds run for hours. (A key idea of this workflow -- the sortby() step -- is found on page 137 of **PDSH**.)\n",
- " \n",
- " \n",
- "- `swap_dims()` to substitute `pressure` for `time` as the ordinate dimension\n",
- "- `sortby()` to make the `pressure` dimension monotonic\n",
- "- Create a pressure-bin array to guide the subsequent data reduction\n",
- "- `groupby_bins()` together with `mean()` to reduce the data to a 0.25 meter quantized profile\n",
- "- use `transpose()` to re-order wavelength and pressure, making the resulting `DataArray` simpler to plot\n",
- "- accumulate these results by day as a list of `DataArrays`\n",
- "- From this list create an `XArray Dataset`\n",
- "- Write this to a new NetCDF file\n",
- "\n",
- "\n",
- "##### needs sorting\n",
- "\n",
- "- Copy: `dsc = ds.copy()`\n",
- "- Coordinate to data variable: `ds = ds.reset_coords('seawater_pressure')`\n",
- "\n",
- "##### Example: XArray Dataset subset and chart\n",
- "\n",
- "\n",
- "Time dimension slice:\n",
- "\n",
- "```\n",
- "ds = xr.open_dataset(\"file.nc\")\n",
- "ds = ds.sel(time=slice(t0, t1))\n",
- "ds\n",
- "```\n",
- "\n",
- "This shows that the temperature Data Variable has a cumbersome name: \n",
- "`sea_water_temperature_profiler_depth_enabled`. \n",
- "\n",
- "```\n",
- "ds = ds.rename({'sea_water_temperature_profiler_depth_enabled':'temperature'})\n",
- "```\n",
- "\n",
- "Plot this against the default dimension `time`:\n",
- "\n",
- "```\n",
- "ds.temperature.plot()\n",
- "```\n",
- "\n",
- "Temperature versus depth rather than time:\n",
- "\n",
- "```\n",
- "fig, axs = plt.subplots(figsize=(12,4), tight_layout=True)\n",
- "axs.plot(ds.temperature, -ds.z, marker='.', markersize=9., color='k', markerfacecolor='r')\n",
- "axs.set(ylim = (200., 0.), title='temperature against depth')\n",
- "```\n",
- "\n",
- "Here `ds.z` is negated to indicate depth below ocean surface.\n",
- "\n",
- "\n",
- "### More cleanup of Datasets: rename() and drop()\n",
- "\n",
- "\n",
- "* Use `ds = ds.rename(dictionary-of-from-to)` to rename data variables in a Dataset\n",
- "* Use `ds = ds.drop(string-name-of-data-var)` to get rid of a data variable\n",
- "* Use `ds = ds[[var1, var2]]` to eliminate all but those two variables\n",
- "\n",
- "\n",
- "##### XArray ***DataArray*** name and length\n",
- "\n",
- "\n",
- "```\n",
- "sensor_t.name\n",
- "\n",
- "len(sensor_t)\n",
- "len(sensor_t.time) # gives same result\n",
- "```\n",
- "\n",
- "What is the name of the controlling dimension?\n",
- "\n",
- "```\n",
- "if sensor_t.dims[0] == 'time': print('time is dimension zero')\n",
- "```\n",
- "\n",
- "Equivalent; but the second version permits reference by \"discoverable\" string.\n",
- "\n",
- "\n",
- "```\n",
- "sensor_t = ds_CTD_time_slice.seawater_temperature\n",
- "sensor_t = ds_CTD_time_slice['seawater_temperature']\n",
- "```\n",
- "\n",
- "###### Plotting with scaling and offsetting\n",
- "\n",
- "Suppose I wish to shift some data left to contrast it with some other data (where they would clobber one another)...\n",
- "\n",
- "```\n",
- "sensor_t + 0.4\n",
- "```\n",
- "\n",
- "Suppose I wish to scale some data in a chart to make it easier to interpret given a fixed axis range\n",
- "\n",
- "```\n",
- "sensor_t * 10. # this fails by trying to make ten copies of the array\n",
- "\n",
- "np.ones(71)*3.*smooth_t # this works by creating an inner product\n",
- "```"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "797918c8-9b2c-4de9-aa5a-f1b5ad1b55bb",
- "metadata": {},
- "source": [
- "### Time\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "\n",
- "### Missing data\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "### Resampling\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "\n",
- "### Mapping\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "- cf PyGMT\n",
- "\n",
- "\n",
- "\n",
- "### Visualization\n",
- "\n",
- "\n",
- "[TOC](#1-Technical-Elements)\n",
- "\n",
- "\n",
- "#### Overview\n",
- "\n",
- "\n",
- "There are two Python plotting libraries: **`matplotlib`** and **`plotly`**. \n",
- "**`plotly`** is more advanced and interactive. \n",
- "[This link provides more background on it](https://plotly.com/python/) \n",
- "including a gallery of examples of what is possible.\n",
- "\n",
- "\n",
- "Turning to Matplotlib: This library includes the **`.pyplot`** sub-library, \n",
- "a MATLAB-parity API. It is the `pyplot` sub-library that is \n",
- "most commonly put to use building charts; and to make matters more confusing it is \n",
- "habitually imported as `plt`, hence the ubiquitous import line: \n",
- "`import matplotlib.pyplot as plt`. With the API now abbreviated as `plt` we \n",
- "proceed to generating data charts. \n",
- "\n",
- "\n",
- "To make things further complicated: Herein we often generate a grid of charts\n",
- "for comparison using the `subplots` API call. As an example: \n",
- "\n",
- "\n",
- "```\n",
- "fig,ax=plt.subplots(3,3,figsize=(12,12))\n",
- "```\n",
- "\n",
- "\n",
- "- What is `fig`? A figure (???)\n",
- "- What is `ax`? An array of artists (???)\n",
- "\n",
- "\n",
- "\n",
- "The main agenda of this repository can be summarized as: \n",
- "\n",
- "\n",
- "- reduce a dataset to just some data of interest\n",
- "- obtain metadata (profile timestamps for example)\n",
- "- produce charts to visualize this data by means of `.scatter` and `.plot` directives\n",
- "- proceed to various forms of data analysis\n",
- "\n",
- "\n",
- "> There is a utility `.plot()` method built into XArray Datasets for a quick view of \n",
- "a particular data variable along the `dimension` axis.\n",
- "\n",
- "\n",
- "\n",
- "> Needed: Detail on how to do formatting, example arguments:\n",
- "> `vmin=4.,vmax=22.,xincrease=False`\n",
- "\n",
- "\n",
- "> PDSH recommends the Seaborn library as a chart-building alternative with cleaner graphics.\n",
- "\n",
- "\n",
- "#### Matplotlib\n",
- "\n",
- "\n",
- "Big topic: Building charts using the matplotlib library. Here's one to begin with."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "id": "3c8bc626-1060-4b37-aa5c-5be6846e15c3",
- "metadata": {
- "tags": []
- },
- "outputs": [
- {
- "data": {
- "image/png": "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",
- "text/plain": [
- "