From 37d389e1ced7b551d03eb5395fa14e2285b5f83d Mon Sep 17 00:00:00 2001 From: vhirtham Date: Wed, 3 Nov 2021 15:46:50 +0100 Subject: [PATCH] Add Second tutorial about time dependent data (#564) --- doc/conf.py | 1 + doc/tutorials.rst | 1 + tutorials/01_01_introduction.ipynb | 8 +- tutorials/01_02_time_dependent_data.ipynb | 438 ++++++++++++++++++++++ tutorials/quality_standards.ipynb | 2 +- 5 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 tutorials/01_02_time_dependent_data.ipynb diff --git a/doc/conf.py b/doc/conf.py index dc11d93d4..04f679561 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -63,6 +63,7 @@ def _prevent_sphinx_circular_imports_bug(): # todo: Remove shutil.copy("./../tutorials/single_pass_weld.wx", tutorials_dir) + # -- Project information ----------------------------------------------------- _now = datetime.datetime.now().year diff --git a/doc/tutorials.rst b/doc/tutorials.rst index 8ccc57828..4cd086bda 100644 --- a/doc/tutorials.rst +++ b/doc/tutorials.rst @@ -17,6 +17,7 @@ data processing and visualization. :caption: Welding examples tutorials/01_01_introduction + tutorials/01_02_time_dependent_data ****************** diff --git a/tutorials/01_01_introduction.ipynb b/tutorials/01_01_introduction.ipynb index 5ebc0af25..04827932e 100644 --- a/tutorials/01_01_introduction.ipynb +++ b/tutorials/01_01_introduction.ipynb @@ -6,6 +6,8 @@ "source": [ "# Introduction / Opening WelDX Files\n", "\n", + "[NEXT TUTORIAL >>](01_02_time_dependent_data.ipynb)\n", + "\n", "## Introduction\n", "\n", "This tutorial is the first one of a whole series that has the purpose to teach you how to work with WelDX files using the `weldx` Python package.\n", @@ -20,7 +22,7 @@ "\n", "All tutorials are written as jupyter notebooks so that you can always run and test the code on your local machine.\n", "You can find them in the `tutorials` subdirectory of [our GitHub repository](https://github.com/BAMWelDX/weldx).\n", - "To learn how to install the `weldx` package and all required dependencies to run the tutorials, visit the [installation guide] (https://weldx.readthedocs.io/en/latest/install.html) page of our [online documentation](https://weldx.readthedocs.io/en/latest/index.html).\n", + "To learn how to install the `weldx` package and all required dependencies to run the tutorials, visit the [installation guide](https://weldx.readthedocs.io/en/latest/install.html) page of our [online documentation](https://weldx.readthedocs.io/en/latest/index.html).\n", "\n", "All tutorials are also contained on the [\"Getting Started\"](https://weldx.readthedocs.io/en/latest/tutorials.html) page of the [online documentation](https://weldx.readthedocs.io/en/latest/index.html) so that you can read a nicely rendered version online.\n", "\n", @@ -148,7 +150,9 @@ "\n", "This concludes the first tutorial about opening and navigating through WelDX Files.\n", "You should now be able to read any given WelDX file and to inspect its structure.\n", - "In the next tutorials, we will learn how to access the actual data and what we can do with it." + "In the next tutorials, we will learn how to access the actual data and what we can do with it.\n", + "\n", + "[NEXT TUTORIAL >>](01_02_time_dependent_data.ipynb)" ] } ], diff --git a/tutorials/01_02_time_dependent_data.ipynb b/tutorials/01_02_time_dependent_data.ipynb new file mode 100644 index 000000000..bfb6e5a77 --- /dev/null +++ b/tutorials/01_02_time_dependent_data.ipynb @@ -0,0 +1,438 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Time dependent data / Welding current and voltage\n", + "\n", + "[<< PREVIOUS TUTORIAL](01_01_introduction.ipynb) | [NEXT TUTORIAL >>](01_01_introduction.ipynb)\n", + "\n", + "## Overview\n", + "\n", + "**This tutorial covers:**\n", + "\n", + "- Accessing and visualizing time dependent data stored inside a `TimeSeries` class\n", + "\n", + "**Requirements:**\n", + "\n", + "- Opening and navigating through WelDX files ([tutorial](01_01_introduction.ipynb))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Plotting data\n", + "\n", + "\n", + "Two important quantities in welding applications are the welding current and welding voltage.\n", + "Both are constantly monitored during an experiment.\n", + "The obtained data is a one dimensional array with time being its only dimension.\n", + "We will extract both from the file and see what we can do with it.\n", + "First we read the file and get an overview over its content as we learned in the previous tutorial:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from weldx import WeldxFile\n", + "\n", + "wxfile = WeldxFile(\"single_pass_weld.wx\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "wxfile.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see in the list, there are the top level items `welding_current` and `welding_voltage`.\n", + "Let's have a look at the welding current first:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current = wxfile[\"welding_current\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The object we extracted is of type `TimeSeries` as can be seen in the `info` output.\n", + "This is a special type provided by WelDX to deal with time dependent data.\n", + "\n", + "To see a plot of the `TimeSeries` simply call the built-in `.plot()` function.\n", + "If you are already familiar with `matplotlib` it is also easy to generate your own plots. We show how to access the underlying data later in this tutorial.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `plot` method is an easy and convenient way to quickly visualize a `TimeSeries`.\n", + "It labels the axes automatically and attaches the corresponding units to them.\n", + "\n", + "Depending on the Python environment you are using, there are some things to consider.\n", + "Since the `plot` method uses `matplotlib` internally, you have to call the `matplotlib.pyplot.show` function manually after using `plot` when running a regular Python script file.\n", + "Otherwise, the plot won't show up.\n", + "\n", + "This is not the case if you work with a jupyter notebook, where the plots are generated after each cell execution.\n", + "However, while a regular script will open an interactive window for each plot, the plots inside a notebook can't be modified without additional commands.\n", + "To enable interactive `matplotlib` plots inside a jupyter notebook, uncomment and run the following magic command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's plot the welding voltage. \n", + "Try to interact with the plot if you are running this tutorial as a jupyter notebook.\n", + "\n", + "> HINT: Remember that you can run the code of this tutorial by clicking on the binder link at the beginning" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "voltage = wxfile[\"welding_voltage\"]\n", + "voltage.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Accessing the data \n", + "\n", + "Now that we know how to get a quick overview over the data by visualizing it with the `plot` method, let's have a look on what else we can do with a `TimeSeries`.\n", + "While any number of dimensions in addition to time are supported, we expect the welding current to have no further dimensions.\n", + "We can easily check this using the `shape` property:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the returned value we see that the data indeed only consists of a single dimension.\n", + "Furthermore, we get the exact number of data points that are stored in the time series.\n", + "\n", + "We already saw in the axes labeling of the plots, that the `TimeSeries` also stores the units of the data \n", + "We can get it by calling the `units` property:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current.units" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As one might expect, the unit of the current is amperes, but it might also have been stored in milliamperes.\n", + "\n", + "Now, if we want to actually access the stored data, we can extract it from the `TimeSeries` by using its `data` property.\n", + "The returned object is a `pint.Quantity`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(current.data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `pint.Quantity` is a thin wrapper around a multi-dimensional array that attaches units to the contained data.\n", + "They can be used like NumPy arrays and support most of NumPy's functions.\n", + "For further information on how to work with quantities, consult [Pint's documentation](https://pint.readthedocs.io/en/stable/).\n", + "\n", + "An alternative to the `data` property is the `data_array` property.\n", + "This returns the data as `xarray.DataArray`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(current.data_array)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Xarray is a powerful tool that makes working with multi-dimensional arrays easier.\n", + "When performing operations on an `xarray.DataArray`, it automatically figures out which dimensions of the operands are compatible with each other and how they need to be combined.\n", + "Visit [the xarray documentation](http://xarray.pydata.org/en/stable/) to learn how to work with xarray classes.\n", + "\n", + "We recommend using either xarray or pint when working with the data, but if you feel more comfortable using NumPy, you can convert both types easily to a `numpy.ndarray`.\n", + "For pint use `.magnitude` or the short version `.m`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(current.data.magnitude)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For xarray use `.data` to get the data as `pint.Quantity` and then `.magnitude` or `.m` to receive a `numpy.ndarray`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(current.data_array.data.magnitude)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data processing examples\n", + "\n", + "Before we conclude this lesson, we will show you some simple examples on how to process the data obtained from a weldx file.\n", + "Pint and Xarray both offer you some basic functions to get the minimal and maximal values of a dataset or to calculate its mean value.\n", + "Here are the examples for Pint:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current.data.min()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current.data.max()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current.data.mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you exchange `.data` with `.data_array`, you will get the same results just as `xarray.DataArray`.\n", + "\n", + "To demonstrate you some features of Xarray and Pint, we will now do a more involved computation by calculating the electric power using the equation:\n", + "\n", + "$$ P = \\frac{1}{T} \\int_{t=0}^{T} u \\cdot i \\;\\mathrm{d} t$$\n", + "\n", + "$u$ is the time dependent current, $i$ the time dependent voltage, $t$ the time and $T$ the duration of the measurement.\n", + "We will do this step by step to keep it simple and to discuss all the things that need to be considered.\n", + "First we will calculate the integral with [Xarrays `.integrate` function](http://xarray.pydata.org/en/stable/generated/xarray.DataArray.integrate.html) which uses the trapezoidal rule." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "integral = (voltage.data_array * current.data_array).integrate(\n", + " \"time\", datetime_unit=\"s\"\n", + ")\n", + "integral" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The paremeter `datetime_unit` specifies the unit of the time coordinates during the integration. \n", + "Unfortunately, at the time of writing of this tutorial, the unit support of Xarray is still work in progress and not fully implemented.\n", + "The result's unit is just $\\mathrm{A \\cdot V}$ while it should have been $\\mathrm{A \\cdot V \\cdot s}$.\n", + "We correct this by turning the resulting `xarray.DataArray` into a `pint.Quantity` (see previous section) and simply multiplying the missing unit.\n", + "Therefore we need to import the unit object `U_` from weldx." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from weldx import U_\n", + "\n", + "q_integral = integral.data * U_(\"s\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What remains now is to divide the result by the process duration.\n", + "We can get the time data either from the voltage or the current data as they were both measured simultaneously.\n", + "It can be accessed with `.time` which returns a `weldx.Time` instance.\n", + "We will talk a bit more about this class in another tutorial.\n", + "For now it is sufficient to know, that it provides a `.duration` method to get the duration of the contained time data as a new `weldx.Time` instance and that we can turn it into a quantity using `.as_quantity`.\n", + "So let's finally calculate the electrical power:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "e_power = q_integral / voltage.time.duration.as_quantity()\n", + "e_power" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can further simplify this by convert the units manually to Watt with `.to` or `.ito`.\n", + "The first method creates a new quantity, while the latter one modifies the existing one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "e_power.ito(\"W\")\n", + "e_power" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you prefer compact numbers, you can use `.to_compact`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "e_power.to_compact()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This concludes the tutorial about accessing and plotting time dependent data.\n", + "We have learned about some basic functionalities of the `TimeSeries` class and know now, how to get the data for further processing and how to quickly visualize it, using the `plot` method.\n", + "\n", + "## Further readings\n", + "\n", + "- [API tutorial: TimeSeries](timeseries_01.ipynb)\n", + "\n", + "[<< PREVIOUS TUTORIAL](01_01_introduction.ipynb) | [NEXT TUTORIAL >>](01_01_introduction.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "weldx", + "language": "python", + "name": "weldx" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/quality_standards.ipynb b/tutorials/quality_standards.ipynb index e237f6d0a..b6f7a1c8a 100644 --- a/tutorials/quality_standards.ipynb +++ b/tutorials/quality_standards.ipynb @@ -649,7 +649,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.11" } }, "nbformat": 4,