{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "ce97q1ZcBiwj" }, "source": [ "# Introduction to NumPy and Pandas\n", "\n", "## Introduction\n", "\n", "This notebook and accompanying webinar was developed and released by the [enDAQ team](https://endaq.com/). This is the second \"chapter\" of our series on *Python for Mechanical Engineers*:\n", "\n", "1. [Get Started with Python](https://colab.research.google.com/drive/1_pcGtgJleapV9tz5WfuRuqfWryjqhPHy#scrollTo=ikUJITDDIp19)\n", " * Blog: [Get Started with Python: Why and How Mechanical Engineers Should Make the Switch](https://blog.endaq.com/get-started-with-python-why-how-mechanical-engineers-should-make-the-switch)\n", "2. **Introduction to NumPy & Pandas**\n", " * [Watch Recording of This](https://info.endaq.com/pandas-and-numpy-for-data-analysis-webinar)\n", "3. [Introduction to Plotly](https://colab.research.google.com/drive/1pag2pKQQW5amWgRykAH8uMAPqHA2yUfU?usp=sharing)\n", "4. [Introduction of the enDAQ Library](https://colab.research.google.com/drive/1WAtQ8JJC_ny0fki7eUABACMA-isZzKB6)\n", "\n", "To sign up for future webinars and watch previous ones, [visit our webinars page](https://endaq.com/pages/shock-vibration-webinars). " ] }, { "cell_type": "markdown", "metadata": { "id": "lZPDu150sQbm" }, "source": [ "### Recap of Python Introduction\n", "\n", "1. Python is popular for good reason\n", "2. There are many ways to interact with Python\n", " * Here we are in Google Colab based on Jupyter Notebooks\n", "3. There are many open source libraries to use\n", " * Today we are covering two of the most popular:\n", " * Numpy\n", " * Pandas\n", " * And teasing a bit of:\n", " * Plotly\n", " * enDAQ\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": { "id": "OTdahAjBEIVj" }, "source": [ "### SO MANY Other Resources\n", "Python is incredibly popular and so there are a lot of resources you can tap into online. You can either:\n", "\n", "* Just start coding and google specific questions when you get stuck (my preference for learning)\n", "* Follow a few online tutorials (like this first)\n", "* Do both!\n", "\n", "**Here are a few resources:**\n", "\n", "* [Jake VanderPlas Python Data Science Handbook](https://colab.research.google.com/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/Index.ipynb)\n", " * [Buy the book](https://www.amazon.com/Python-Data-Science-Handbook-Essential-ebook-dp-B01N2JT3ST/dp/B01N2JT3ST/ref=mt_other?_encoding=UTF8&me=&qid=) for $35\n", " * Go through a series of [well documented Colab Notebooks](https://colab.research.google.com/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/Index.ipynb)\n", " * **Highly recommended resource!**\n", "* [Code Academy: Analyze Data with Python](https://www.codecademy.com/learn/paths/analyze-data-with-python)\n", "* [Udemy Academy: Data Analysis with Pandas & NumPy in Python for Beginner](https://www.udemy.com/course/python-data-analysis-2020/)\n", "* [Datacamp: Introduction to Python](https://www.datacamp.com/courses/intro-to-python-for-data-science)\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "WOyh91ABBlc3" }, "source": [ "## NumPy\n", "\n", "NumPy provides an in depth overview of [what exactly NumPy is](https://numpy.org/doc/stable/user/whatisnumpy.html). Quoting them:\n", ">*At the core of the NumPy package, is the ndarray object. This encapsulates n-dimensional arrays of homogeneous data types, with many operations being performed in compiled code for performance.*\n", "\n", "Let's get started by importing it, typically shortened to `np` because of how frequently it's called. This will come standard in most Python distributions such as Anaconda. If you need to install it simply:\n", "\n", "```\n", "!pip install numpy\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "VLP6WeFY8UWo" }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": { "id": "WrQ0eAG8CBqG" }, "source": [ "### Creating Arrays" ] }, { "cell_type": "markdown", "metadata": { "id": "_S1VR75hIAUM" }, "source": [ "#### Manually or from Lists\n", "Manually create a list and demonstrate that operations on that list are difficult." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "RFL-_fg5IDeg", "outputId": "373c2282-92f1-4dab-b75f-55fcf5764575" }, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 0, 1, 2, 3]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lst = [0, 1, 2, 3]\n", "lst * 2" ] }, { "cell_type": "markdown", "metadata": { "id": "nXhiXAjgIbdc" }, "source": [ "That isn't what we expected! For lists, we need to loop through all elements to apply a function to them which can be incredibly time consuming. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "zMQ03IPWIS7S", "outputId": "f1ba686c-6261-408a-aae7-ea49e83e9141" }, "outputs": [ { "data": { "text/plain": [ "[0, 2, 4, 6]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[i * 2 for i in lst]" ] }, { "cell_type": "markdown", "metadata": { "id": "B-kxWUb6Ipf4" }, "source": [ "Now lets make a numpy array and once in an array, let's show how intuitive operations are now that they are performed element by element." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "qkFeoqrRIn-r", "outputId": "c62abf15-989d-4a01-cdb3-8b8af8c8fd5e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1 2 3]\n", "[0 2 4 6]\n", "[2 3 4 5]\n" ] } ], "source": [ "array = np.array(lst)\n", "print(array)\n", "print(array * 2)\n", "print(array + 2)" ] }, { "cell_type": "markdown", "metadata": { "id": "iL3RZxhbIf4I" }, "source": [ "Let's create a 2D matrix of 32 bit floats." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "DLQY2HN_JdcB", "outputId": "d4a0c081-676a-43a9-b08d-84f33a613170" }, "outputs": [ { "data": { "text/plain": [ "array([[1., 0., 0.],\n", " [0., 1., 0.],\n", " [0., 0., 1.]], dtype=float32)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.array([[1, 0, 0],\n", " [0, 1, 0],\n", " [0, 0, 1]], dtype=np.float32)" ] }, { "cell_type": "markdown", "metadata": { "id": "-Nuet-s2Jd3q" }, "source": [ "#### Using Functions\n", "\n", "We'll go through a few here, but for more in depth examples and options see NumPy's [Array Creation Routines](https://numpy.org/doc/stable/reference/routines.array-creation.html). These first few examples are for very basic arrays/matrices." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "CQyecbfVL3F9", "outputId": "fb2c7665-6da1-4299-c57d-2cd26348b699" }, "outputs": [ { "data": { "text/plain": [ "array([0., 0., 0., 0., 0.])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.zeros(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "1TP42y0nL5iq", "outputId": "49751d7a-31ca-4205-e2ca-1e4abd980a7a" }, "outputs": [ { "data": { "text/plain": [ "array([[0., 0., 0.],\n", " [0., 0., 0.]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.zeros([2, 3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "22P2Xf2aL-T8", "outputId": "797a529d-9e08-48e5-cddb-ada72817ed66" }, "outputs": [ { "data": { "text/plain": [ "array([[1., 0., 0.],\n", " [0., 1., 0.],\n", " [0., 0., 1.]])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.eye(3)" ] }, { "cell_type": "markdown", "metadata": { "id": "Dhn3g-dkxZY6" }, "source": [ "Now let's start making sequences." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vVFX3uuD-E0G", "outputId": "37e76848-9dee-4296-f453-e531243fd469" }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "-ZkTkdK5EtRg", "outputId": "dc486237-4d77-4be8-f2b8-89fad6726b5b" }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(0, #start\n", " 10) #stop (not included in array)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "a5OIfwPN-O48", "outputId": "b4768962-0028-469d-c194-af02dd98f1a7" }, "outputs": [ { "data": { "text/plain": [ "array([0, 2, 4, 6, 8])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(0, #start\n", " 10, #stop (not included in array)\n", " 2) #step size" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "yAO2opdCKMVE", "outputId": "911b0441-5f17-4886-dfeb-f30150a133ac" }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 2., 4., 6., 8., 10.])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linspace(0, #start\n", " 10, #stop (default to be included, can pass in endpoint=False)\n", " 6) #number of data points evenly spaced" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "x0xigPrOK4Y1", "outputId": "f2f023ee-94f7-44da-a7b1-7b7bc89dc776" }, "outputs": [ { "data": { "text/plain": [ "array([ 1., 10., 100., 1000.])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.logspace(0, #output starts being raised to this value\n", " 3, #ending raised value\n", " 4) #number of data points" ] }, { "cell_type": "markdown", "metadata": { "id": "cv0nw1SnLb5x" }, "source": [ "Logspace is the equivalent of raising a base by a linspaced array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "J3txdCX2LTjy", "outputId": "de2c4c21-ea5d-4bb3-9b5f-ebc1b138357f" }, "outputs": [ { "data": { "text/plain": [ "array([ 1., 10., 100., 1000.])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "10 ** np.linspace(0,3,4)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Ew2EuHuELa-A", "outputId": "30ca9d00-eabb-4826-e6d0-e3d41f262c0d" }, "outputs": [ { "data": { "text/plain": [ "array([ 1., 2., 4., 8., 16.])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.logspace(0, 4, 5, base=2)" ] }, { "cell_type": "markdown", "metadata": { "id": "wkVckjCtxl_A" }, "source": [ "If you don't want to have to do the mental math to know what exponent to raise the values to, you can use geomspace but this only helps for base of 10." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ytK6IH_HLnXF", "outputId": "bf3d4c46-b54a-4703-e32b-55c3f3d1d4ed" }, "outputs": [ { "data": { "text/plain": [ "array([ 1., 10., 100., 1000.])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.geomspace(1, 1000, 4)" ] }, { "cell_type": "markdown", "metadata": { "id": "DQT4Vdrax8IE" }, "source": [ "Random numbers!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "CNipvh8wMi5t", "outputId": "28a12c88-6e3b-4d1f-d571-495db1fcc42b" }, "outputs": [ { "data": { "text/plain": [ "array([0.05057259, 0.19699801, 0.46727495, 0.67357217, 0.60387782])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.rand(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "YtcgKwEhMosI", "outputId": "e632e6a6-5b74-4b64-a0b8-00c4e94fd72b" }, "outputs": [ { "data": { "text/plain": [ "array([[0.76299702, 0.06301467, 0.92031266],\n", " [0.42219446, 0.73721929, 0.39450975]])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.rand(2, 3)" ] }, { "cell_type": "markdown", "metadata": { "id": "NzxTMPNaCW86" }, "source": [ "### Indexing\n", "\n", "Let's first create a simple array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "qkWur-4ARuBa", "outputId": "b2d0a3a6-2c5a-4e24-e849-5ca49ba53aff" }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array = np.arange(1, 11, 1)\n", "array" ] }, { "cell_type": "markdown", "metadata": { "id": "FFs20jVcjF2L" }, "source": [ "Now index the first item." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "OFCBTk5qTvLp", "outputId": "a7f71372-c785-46f5-ea94-3c11437fe9b5" }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[0]" ] }, { "cell_type": "markdown", "metadata": { "id": "gJFlP9V2jH_A" }, "source": [ "Index the last item." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "7Ct2CXINT_HH", "outputId": "5571a5f0-e08c-4a1f-cacf-a5158d5691d7" }, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[-1]" ] }, { "cell_type": "markdown", "metadata": { "id": "an9Cc7rejJd-" }, "source": [ "Grab every 2nd item." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Ec6mWL-5UAok", "outputId": "5177ffee-2c8d-4b76-b54a-8abdeebca2d3" }, "outputs": [ { "data": { "text/plain": [ "array([1, 3, 5, 7, 9])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[::2]" ] }, { "cell_type": "markdown", "metadata": { "id": "H-KRmA9hjMLu" }, "source": [ "Grab every second item starting at index of 1 (the second value)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Kpwn_NT3UCdR", "outputId": "1b19b929-d194-491d-c2c9-15f281b86683" }, "outputs": [ { "data": { "text/plain": [ "array([ 2, 4, 6, 8, 10])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[1::2]" ] }, { "cell_type": "markdown", "metadata": { "id": "HgCo96qfjQJM" }, "source": [ "Start from the second item, going to the 7th but skipping every other. (Our first time using the full `array[start (inclusive): stop (exclusive): step]` array 'slicing' notation)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "8q-r1nFgUF5Z", "outputId": "cc8fd7bf-887b-4674-88ad-d2e5acde32b5" }, "outputs": [ { "data": { "text/plain": [ "array([2, 4, 6])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[1:6:2]" ] }, { "cell_type": "markdown", "metadata": { "id": "O5NAi55AjpE-" }, "source": [ "Reverse the order." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "HLv2rpV5jna1", "outputId": "9781f215-afc2-4d28-c1bc-83f106176a34" }, "outputs": [ { "data": { "text/plain": [ "array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[::-1]" ] }, { "cell_type": "markdown", "metadata": { "id": "IKIl4jfbk6LZ" }, "source": [ "Boolean operations to index the array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vAbOU4ayUqDD", "outputId": "ceece14d-1c76-4610-8dc4-4d652503e9c2" }, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[array < 5]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "kSxUAD5Vk8p9", "outputId": "cd032815-3669-4a27-fee3-80f6bd5baf66" }, "outputs": [ { "data": { "text/plain": [ "array([1, 2])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[array * 2 < 5]" ] }, { "cell_type": "markdown", "metadata": { "id": "MmlhYRGMhvZg" }, "source": [ "Integer list can also index arrays." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "1uUNfzc7huzw", "outputId": "0a0aa077-08d3-4f75-a720-67d2073182d5" }, "outputs": [ { "data": { "text/plain": [ "array([2, 4, 6])" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array[[1,3,5]]" ] }, { "cell_type": "markdown", "metadata": { "id": "DlqWdsqqdJT0" }, "source": [ "Now let's create a slightly more complicated, 2 dimensional array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "4Vz5G3PNc2cZ", "outputId": "16662c03-c015-4806-8ec3-6be75ea72c6c" }, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 2, 3, 4],\n", " [5, 6, 7, 8, 9]])" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array_2d = np.arange(10).reshape((2, 5))\n", "array_2d" ] }, { "cell_type": "markdown", "metadata": { "id": "k688SAR_iAjf" }, "source": [ "Indexing the second dimension." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "WbYDyf-KiJzr", "outputId": "139e6975-5656-49e4-9adf-3a17ca561e5d" }, "outputs": [ { "data": { "text/plain": [ "array([1, 6])" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array_2d[:, 1]" ] }, { "cell_type": "markdown", "metadata": { "id": "euR2mj3RiRcl" }, "source": [ "Any combination of these indexing methods works as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "SW9G--6JiQfp", "outputId": "001a5226-7cd0-43bd-a3a3-d39effd809f4" }, "outputs": [ { "data": { "text/plain": [ "array([[1, 3]])" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array_2d[[True, False], 1::2]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "aJqko5CBimsB", "outputId": "cb1faf48-1bb1-4220-db95-df40e6972fa3" }, "outputs": [ { "data": { "text/plain": [ "array([5, 6, 9])" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array_2d[1, [0,1,4]]" ] }, { "cell_type": "markdown", "metadata": { "id": "n2mS2VIsCzp8" }, "source": [ "### Operations" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "_1Rs1BpJltMH", "outputId": "a4a0f90f-df5a-4c50-f184-610bcd66bdb3" }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "oxc2cNL4lyKw", "outputId": "b1e1b39a-64e6-438c-f35b-bbb076fae6c1" }, "outputs": [ { "data": { "text/plain": [ "array([101, 102, 103, 104, 105, 106, 107, 108, 109, 110])" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array + 100" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "-N2RQG6Ilo2X", "outputId": "1bea148b-72c4-413c-bf8c-5f2cc17d7a37" }, "outputs": [ { "data": { "text/plain": [ "array([ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20])" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array * 2" ] }, { "cell_type": "markdown", "metadata": { "id": "EpuHnkodzQmv" }, "source": [ "To raise all the elements in an array to an exponent we have to use the notation `**` not `^`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "H0pp6Rt-ls7K", "outputId": "912abeb5-d74f-4e31-c151-fd449f85feaa" }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100])" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array ** 2" ] }, { "cell_type": "markdown", "metadata": { "id": "ipJ0Ss5Rm2f7" }, "source": [ "Use that shape to create a new array matching it to do operations with." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "wX4Rudonl1Xb", "outputId": "00f7b08f-7dfe-4748-dafb-0e7ede76d066" }, "outputs": [ { "data": { "text/plain": [ "array([ 0, 5, 10, 15, 20, 25, 30, 35, 40, 45])" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array2 = np.arange(array.shape[0]) * 5\n", "array2" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "IRvsl8qEl9V3", "outputId": "d238cdb2-3630-4fb0-e651-712eb9a36c63" }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 7, 13, 19, 25, 31, 37, 43, 49, 55])" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array2 + array" ] }, { "cell_type": "markdown", "metadata": { "id": "Qex1vdxJnFAC" }, "source": [ "### Stats of an Array" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "m6KX660In-f2", "outputId": "43020216-077c-4503-fec8-239dff49cff6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1 2 3 4 5 6 7 8 9 10]\n", "[[0 1 2 3 4]\n", " [5 6 7 8 9]]\n" ] } ], "source": [ "print(array)\n", "print(array_2d)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9NkWPeaknIj1", "outputId": "c51538dd-7c08-4926-8445-0b65e729ed30" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(10,)\n", "(2, 5)\n" ] } ], "source": [ "print(array.shape)\n", "print(array_2d.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "nPrYLzs_nxgh", "outputId": "65d85450-b74a-4d90-8ac4-25bd7e9b71e7" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "2\n" ] } ], "source": [ "print(len(array))\n", "print(len(array_2d))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "z8bhAHwFnO_D", "outputId": "99546b5c-3606-4713-e29b-7b2a6447e169" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "9\n" ] } ], "source": [ "print(array.max())\n", "print(array_2d.max())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "n6SSOUXtnaXc", "outputId": "89eb2d31-8edb-45b6-fd94-13ddc0944b20" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "0\n" ] } ], "source": [ "print(array.min())\n", "print(array_2d.min())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "zZSRKnF6nbas", "outputId": "9cf0ecd3-7abe-4d94-8a64-36c623ff51da" }, "outputs": [ { "data": { "text/plain": [ "2.8722813232690143" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array.std()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "jdMsJndyndOQ", "outputId": "6f3d707e-2963-4388-e8ce-f2297df4b6fb" }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55])" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array.cumsum()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "VRpZYL4YnfUb", "outputId": "dfdb683c-ed16-4f16-f9f8-05b173349f08" }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 2, 6, 24, 120, 720, 5040,\n", " 40320, 362880, 3628800])" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array.cumprod()" ] }, { "cell_type": "markdown", "metadata": { "id": "0nDm77jUm7St" }, "source": [ "### Constants" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "aMGMdwaqoXkn", "outputId": "68025722-d17c-4c87-e958-40988b6f79ee" }, "outputs": [ { "data": { "text/plain": [ "3.141592653589793" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.pi" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "jLhkYK-zoYo6", "outputId": "21e3820a-bb77-41bf-e52e-d648cbc8ff3c" }, "outputs": [ { "data": { "text/plain": [ "2.718281828459045" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.e" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "0H-XpiGNoZgG", "outputId": "88623e5a-fcee-4076-81da-b9efda2f3234" }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.inf" ] }, { "cell_type": "markdown", "metadata": { "id": "jX-rL5P0ouD6" }, "source": [ "### Functions" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "eG1GKzVuovNY", "outputId": "eecdf4ad-89b7-447a-e4a8-4a2758af972c" }, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sin(np.pi / 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "tWQ3xUExo7Bn", "outputId": "ffabcf03-ea44-4f68-dfdc-d3dc16c7ca4b" }, "outputs": [ { "data": { "text/plain": [ "-1.0" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.cos(np.pi)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "gk6bP-m-o9gC", "outputId": "c1b67bd1-1cb5-436c-92db-1ca0263a734f" }, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.log(np.e)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "WHZQnDRzpENN", "outputId": "0ca8e0ff-f6aa-46ab-c019-057e555ab342" }, "outputs": [ { "data": { "text/plain": [ "2.0" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.log10(100)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "hAxm918epGJj", "outputId": "d2a60bf9-123d-44ca-b875-a0ddd0a25013" }, "outputs": [ { "data": { "text/plain": [ "6.0" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.log2(64)" ] }, { "cell_type": "markdown", "metadata": { "id": "p8IvjWHkzvlm" }, "source": [ "To demonstrate rounding, let's first make a new array with decimals. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "VSJgi2IQpYXa", "outputId": "e437c7a9-e4fe-4499-b7ee-b0db39da7eeb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0. 0.33333333 0.66666667 1. ]\n" ] }, { "data": { "text/plain": [ "array([0. , 0.33, 0.67, 1. ])" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "array = np.arange(4) / 3\n", "print(array)\n", "np.around(array, 2)" ] }, { "cell_type": "markdown", "metadata": { "id": "88VwFdWBCFkC" }, "source": [ "### Looping vs Vectorization\n", "\n", "As mentioned in the beginning, NumPy uses machine code with their ndarray objects which is what leads to the performance improvements. Let's demonstrate this by constructing a simple sine wave." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "mX9awPKn-ZlU", "outputId": "29c7994e-1b18-4885-ceea-4f5e363c2c3c" }, "outputs": [ { "data": { "text/plain": [ "(1001,)" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fs = 500 #sampling rate in Hz\n", "d_t = 1 / fs #time steps in seconds\n", "n_step = 1000 #number of steps (there will be n_step+1 data points)\n", "\n", "amp = 1 #amplitude of sine wave\n", "f = 2 #frequency of sine wave\n", "\n", "time = np.linspace(0, #start time\n", " n_step * d_t, #end time\n", " num=n_step + 1) #number of data points, one more than the steps to include an endpoint\n", "time.shape" ] }, { "cell_type": "markdown", "metadata": { "id": "CIKO7e0e0QvP" }, "source": [ "First we make a function to loop through each element and calculate the amplitude." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wqVZJKtwTB8l" }, "outputs": [], "source": [ "def sine_wave_with_loop(time, amp, f, phase=0):\n", " length = time.shape[0]\n", " wave = np.zeros(length)\n", "\n", " for i in range(length-1):\n", " wave[i] = np.sin(2 * np.pi * f * time[i] + phase * np.pi / 180)*amp\n", " return wave" ] }, { "cell_type": "markdown", "metadata": { "id": "8nkt7EDc0c3H" }, "source": [ "Now let's time how quickly that executes for our time array of 1,001 data points." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "CMwHGKrITpN4", "outputId": "950cebd8-3bb5-4062-cbde-15b4b4727eb3" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 7.52 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100 loops, best of 5: 2.55 ms per loop\n" ] } ], "source": [ "%timeit sine_wave_with_loop(time, amp, f)" ] }, { "cell_type": "markdown", "metadata": { "id": "g4s7-D_90nNH" }, "source": [ "Now let's do the same using NumPy's sine function and vectorization." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "89xWQJpzAdal" }, "outputs": [], "source": [ "def sine_wave_with_numpy(time, amp, f, phase=0):\n", " \"\"\"Takes in a time array and sine wave parameters, returns an array of the sine wave amplitude.\"\"\" \n", " return np.sin(2 * np.pi * f * time + phase * np.pi / 180) * amp" ] }, { "cell_type": "markdown", "metadata": { "id": "6rNJady-0sJp" }, "source": [ "Notice my docstrings!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "AsVkSRvtV3HI", "outputId": "c2da90a6-e914-4964-c53b-2f1b2924a1e8" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function sine_wave_with_numpy in module __main__:\n", "\n", "sine_wave_with_numpy(time, amp, f, phase=0)\n", " Takes in a time array and sine wave parameters, returns an array of the sine wave amplitude.\n", "\n" ] } ], "source": [ "help(sine_wave_with_numpy)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Q5S5fTrw--Z3", "outputId": "cb83ee47-de30-4215-9624-9c55571f30ce" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 4.34 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "10000 loops, best of 5: 27.9 µs per loop\n" ] } ], "source": [ "%timeit sine_wave_with_numpy(time, amp, f)" ] }, { "cell_type": "markdown", "metadata": { "id": "jtS0wmIyppOW" }, "source": [ "Using vectorization is about **100x faster!** And this increases the longer the loops are." ] }, { "cell_type": "markdown", "metadata": { "id": "KQIddnfwp7t5" }, "source": [ "#### Why Vectorization Works So Much Faster\n", "The above example highlights that NumPy is much faster, but why? Because it is using compiled machine code under the hood for it's operations. \n", "\n", "Python has the [Numba](http://numba.pydata.org/) package which can be used to do this compilation which we will do to highlight just why NumPy is faster (and recommended!)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "cX8PSvkRoJR3" }, "outputs": [], "source": [ "from numba import njit\n", "\n", "numba_sine_wave_with_loop = njit(sine_wave_with_loop)\n", "numba_sine_wave_with_numpy = njit(sine_wave_with_numpy)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "T_ckZKtlo1IJ", "outputId": "da56b3e6-205c-4da7-8b89-b622903a94e1" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 14426.53 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "1 loop, best of 5: 22.8 µs per loop\n", "The slowest run took 11783.25 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "1 loop, best of 5: 22.1 µs per loop\n" ] } ], "source": [ "%timeit numba_sine_wave_with_loop(time, amp, f)\n", "%timeit numba_sine_wave_with_numpy(time, amp, f)" ] }, { "cell_type": "markdown", "metadata": { "id": "Gk0i33QcstPA" }, "source": [ "Let's combine this into a DataFrame we'll discuss next in more detail, but here's a preview." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 175 }, "id": "U2y5ggCUpEHh", "outputId": "b87bf24b-7621-4553-e384-5a0b14bd3035" }, "outputs": [ { "data": { "text/html": [ "
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Time (us)MethodNumba?
0277.0Loopw/o
127.5NumPyw/o
223.1Loopw
322.6NumPyw
\n", "
" ], "text/plain": [ " Time (us) Method Numba?\n", "0 277.0 Loop w/o\n", "1 27.5 NumPy w/o\n", "2 23.1 Loop w\n", "3 22.6 NumPy w" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "time_data = pd.DataFrame({'Time (us)':[2.77*100, 27.5, 23.1, 22.6],\n", " 'Method':['Loop','NumPy','Loop','NumPy'],\n", " 'Numba?':['w/o','w/o','w','w']}\n", ")\n", "time_data" ] }, { "cell_type": "markdown", "metadata": { "id": "Y4xT1NK3sqnQ" }, "source": [ "Now let's plot it and preview Plotly!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 559 }, "id": "qxZGaIywpPBr", "outputId": "a0fda4c9-0df4-4412-abe0-9e440f820206" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[K |████████████████████████████████| 23.9 MB 1.5 MB/s \n", "\u001b[?25h" ] }, { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "!pip install --upgrade -q plotly\n", "import plotly.express as px\n", "import plotly.io as pio; pio.renderers.default = \"iframe\"\n", "\n", "fig = px.bar(time_data, \n", " x=\"Method\", \n", " y=\"Time (us)\", \n", " color=\"Numba?\", \n", " title=\"Compute Time of a Sine Wave for 1000 Elements\",\n", " barmode='group')\n", "fig.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "q220YoGquB1h" }, "source": [ "## Pandas\n", "Pandas is built *on top of* NumPy meaning that the data is stored still as NumPy ndarray objects under the hood. But it exposes a much more intuitive labeling/indexing architecture and allows you to link arrays of different types (strings, floats, integers etc.) to one another.\n", "\n", "To quote Jake VanderPlas:\n", ">*At the very basic level, Pandas objects can be thought of as enhanced versions of NumPy structured arrays in which the rows and columns are identified with labels rather than simple integer indices.*\n", "\n", "To start, import pandas as `pd`, again this will come standard in virtually all Python distributions such as Anaconda. But to install is simply:\n", "\n", "```\n", "!pip install pandas\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "zigCIKBhNkNl" }, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": { "id": "OtlqVHIwT6wx" }, "source": [ "There are three types of Pandas objects, we'll only focus on the first two:\n", "\n", "1. **Series** – 1D labeled homogeneous array, size-immutable\n", "2. **Data Frames** – 2D labeled, size-mutable tabular structure with heterogenic columns\n", "3. **Panel** – 3D labeled size mutable array." ] }, { "cell_type": "markdown", "metadata": { "id": "ReBZJAjGuFTx" }, "source": [ "### Creating a Series\n", "First let's create a few numpy arrays." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Slfe4gYJuKZL", "outputId": "b14099a3-7fd2-4c23-b9fb-b5eccf322338" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0. 0.002 0.004 ... 1.996 1.998 2. ]\n", "[ 1.22464680e-16 -2.51300954e-02 -5.02443182e-02 ... 5.02443182e-02\n", " 2.51300954e-02 1.10218212e-15]\n" ] } ], "source": [ "amplitude = sine_wave_with_numpy(time, amp, f, 180)\n", "print(time)\n", "print(amplitude)" ] }, { "cell_type": "markdown", "metadata": { "id": "yH8v1NpUBr0X" }, "source": [ "Now let's see what a series looks like made from one of the arrays." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Ke_Vt_SmBrl0", "outputId": "4e323c4a-ef2d-40a0-c44d-0511336ba3e5" }, "outputs": [ { "data": { "text/plain": [ "0 1.224647e-16\n", "1 -2.513010e-02\n", "2 -5.024432e-02\n", "3 -7.532681e-02\n", "4 -1.003617e-01\n", " ... \n", "996 1.003617e-01\n", "997 7.532681e-02\n", "998 5.024432e-02\n", "999 2.513010e-02\n", "1000 1.102182e-15\n", "Length: 1001, dtype: float64" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.Series(amplitude)" ] }, { "cell_type": "markdown", "metadata": { "id": "jt0xL0cuB2Ai" }, "source": [ "This type of series has some value, but you really start to see it when you add in an index." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "OQ11y3klvvLj", "outputId": "5dfe9f12-e4a2-4cc4-d234-dd048046d477" }, "outputs": [ { "data": { "text/plain": [ "0.000 1.224647e-16\n", "0.002 -2.513010e-02\n", "0.004 -5.024432e-02\n", "0.006 -7.532681e-02\n", "0.008 -1.003617e-01\n", " ... \n", "1.992 1.003617e-01\n", "1.994 7.532681e-02\n", "1.996 5.024432e-02\n", "1.998 2.513010e-02\n", "2.000 1.102182e-15\n", "Name: Amplitude, Length: 1001, dtype: float64" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "series = pd.Series(data=amplitude,\n", " index=time,\n", " name='Amplitude')\n", "series" ] }, { "cell_type": "markdown", "metadata": { "id": "YxGmalSBl2_3" }, "source": [ "Here's where Pandas shines - indexing is much more intuitive (and inclusive) to specify based on labels, not those confusing integer locations. We'll come back to this when we have the dataframe next too." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "VTqpSZl9lyIl", "outputId": "3a92856e-0677-4c80-f52f-7d0e2fd9e80a" }, "outputs": [ { "data": { "text/plain": [ "0.000 1.224647e-16\n", "0.002 -2.513010e-02\n", "0.004 -5.024432e-02\n", "0.006 -7.532681e-02\n", "0.008 -1.003617e-01\n", "0.010 -1.253332e-01\n", "Name: Amplitude, dtype: float64" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "series[0: 0.01]" ] }, { "cell_type": "markdown", "metadata": { "id": "s38flxoeB8n0" }, "source": [ "Being able to plot quickly is also a plus!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 283 }, "id": "YbluH1OXu-0f", "outputId": "f1a137b5-b116-4ef8-e9de-4458e1510bcf" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "series.plot()" ] }, { "cell_type": "markdown", "metadata": { "id": "H8I5XnmU4Dp7" }, "source": [ "Remember we never left the NumPy array, it is still here and can be accessed with the following." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "xtmkmd2l5U4o", "outputId": "278f9301-9f68-42b1-d0dd-11657f48e364" }, "outputs": [ { "data": { "text/plain": [ "array([ 1.22464680e-16, -2.51300954e-02, -5.02443182e-02, ...,\n", " 5.02443182e-02, 2.51300954e-02, 1.10218212e-15])" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "series.values" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "-jvWYyW44JUH", "outputId": "7ec67c4d-b14b-4dd3-ceda-5d42655636e4" }, "outputs": [ { "data": { "text/plain": [ "array([ 1.22464680e-16, -2.51300954e-02, -5.02443182e-02, ...,\n", " 5.02443182e-02, 2.51300954e-02, 1.10218212e-15])" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "series.to_numpy()" ] }, { "cell_type": "markdown", "metadata": { "id": "wzVCQDfNuIOP" }, "source": [ "### Creating a DataFrame\n", "\n", "A DataFrame is basically a sequence of aligned series objects, and by aligned I mean they share a common index or label. This let's us mix and match types easily among other benefits.\n", "\n", "First we'll start creating dataframes using what is called a \"dictionary\" with keys and values." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 423 }, "id": "Bty17IsDU6B3", "outputId": "5434f563-2cdf-4618-ee07-a0056152f58f" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Phase 0Phase 90Phase 180Phase 270
0.0000.000000e+001.0000001.224647e-16-1.000000
0.0022.513010e-020.999684-2.513010e-02-0.999684
0.0045.024432e-020.998737-5.024432e-02-0.998737
0.0067.532681e-020.997159-7.532681e-02-0.997159
0.0081.003617e-010.994951-1.003617e-01-0.994951
...............
1.992-1.003617e-010.9949511.003617e-01-0.994951
1.994-7.532681e-020.9971597.532681e-02-0.997159
1.996-5.024432e-020.9987375.024432e-02-0.998737
1.998-2.513010e-020.9996842.513010e-02-0.999684
2.000-9.797174e-161.0000001.102182e-15-1.000000
\n", "

1001 rows × 4 columns

\n", "
" ], "text/plain": [ " Phase 0 Phase 90 Phase 180 Phase 270\n", "0.000 0.000000e+00 1.000000 1.224647e-16 -1.000000\n", "0.002 2.513010e-02 0.999684 -2.513010e-02 -0.999684\n", "0.004 5.024432e-02 0.998737 -5.024432e-02 -0.998737\n", "0.006 7.532681e-02 0.997159 -7.532681e-02 -0.997159\n", "0.008 1.003617e-01 0.994951 -1.003617e-01 -0.994951\n", "... ... ... ... ...\n", "1.992 -1.003617e-01 0.994951 1.003617e-01 -0.994951\n", "1.994 -7.532681e-02 0.997159 7.532681e-02 -0.997159\n", "1.996 -5.024432e-02 0.998737 5.024432e-02 -0.998737\n", "1.998 -2.513010e-02 0.999684 2.513010e-02 -0.999684\n", "2.000 -9.797174e-16 1.000000 1.102182e-15 -1.000000\n", "\n", "[1001 rows x 4 columns]" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.DataFrame({\"Phase 0\": sine_wave_with_numpy(time, amp, f, 00),\n", " \"Phase 90\": sine_wave_with_numpy(time, amp, f, 90),\n", " \"Phase 180\": sine_wave_with_numpy(time, amp, f, 180),\n", " \"Phase 270\": sine_wave_with_numpy(time, amp, f, 270)},\n", " index=time)\n", "df" ] }, { "cell_type": "markdown", "metadata": { "id": "dGDtiYG-cU9V" }, "source": [ "### Plotting (Preview)\n", "\n", "Dataframes also wrap around Matplotlib to allow for plotting directly from the dataframe object itself. This can also be done from the Pandas Series object too like we showed earlier." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 283 }, "id": "ZkC62XnKVd6X", "outputId": "6e92b6eb-a66d-4582-9ea7-7fe80c6cf0f2" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "df.plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 423 }, "id": "goaaXKMXVjUq", "outputId": "e3c5e802-dd8e-4fb3-8598-0dd8be598f66" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", " \n", " \n", " \n", "
Phase 0Phase 90Phase 180Phase 270MaxMin
0.0000.000000e+001.0000001.224647e-16-1.0000001.000000-1.000000
0.0022.513010e-020.999684-2.513010e-02-0.9996840.999684-0.999684
0.0045.024432e-020.998737-5.024432e-02-0.9987370.998737-0.998737
0.0067.532681e-020.997159-7.532681e-02-0.9971590.997159-0.997159
0.0081.003617e-010.994951-1.003617e-01-0.9949510.994951-0.994951
.....................
1.992-1.003617e-010.9949511.003617e-01-0.9949510.994951-0.994951
1.994-7.532681e-020.9971597.532681e-02-0.9971590.997159-0.997159
1.996-5.024432e-020.9987375.024432e-02-0.9987370.998737-0.998737
1.998-2.513010e-020.9996842.513010e-02-0.9996840.999684-0.999684
2.000-9.797174e-161.0000001.102182e-15-1.0000001.000000-1.000000
\n", "

1001 rows × 6 columns

\n", "
" ], "text/plain": [ " Phase 0 Phase 90 Phase 180 Phase 270 Max Min\n", "0.000 0.000000e+00 1.000000 1.224647e-16 -1.000000 1.000000 -1.000000\n", "0.002 2.513010e-02 0.999684 -2.513010e-02 -0.999684 0.999684 -0.999684\n", "0.004 5.024432e-02 0.998737 -5.024432e-02 -0.998737 0.998737 -0.998737\n", "0.006 7.532681e-02 0.997159 -7.532681e-02 -0.997159 0.997159 -0.997159\n", "0.008 1.003617e-01 0.994951 -1.003617e-01 -0.994951 0.994951 -0.994951\n", "... ... ... ... ... ... ...\n", "1.992 -1.003617e-01 0.994951 1.003617e-01 -0.994951 0.994951 -0.994951\n", "1.994 -7.532681e-02 0.997159 7.532681e-02 -0.997159 0.997159 -0.997159\n", "1.996 -5.024432e-02 0.998737 5.024432e-02 -0.998737 0.998737 -0.998737\n", "1.998 -2.513010e-02 0.999684 2.513010e-02 -0.999684 0.999684 -0.999684\n", "2.000 -9.797174e-16 1.000000 1.102182e-15 -1.000000 1.000000 -1.000000\n", "\n", "[1001 rows x 6 columns]" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Max'] = df.max(axis=1)\n", "df['Min'] = df.min(axis=1)\n", "df" ] }, { "cell_type": "markdown", "metadata": { "id": "I7_Phn2UF7Yp" }, "source": [ "This will be the topic of the next webinar, plotting with Plotly! \n", "\n", "Note that I need to install an upgraded version of Plotly in Colab because the default Plotly Express version doesn't work in Colab (but their more advanced graph objects does)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Ilzy8VvZDgt0" }, "outputs": [], "source": [ "!pip install --upgrade -q plotly\n", "import plotly.express as px" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "7XF4YcNIE1Hn", "outputId": "6c694dac-a715-472b-a2cb-37b29b113858" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "px.line(df).show()" ] }, { "cell_type": "markdown", "metadata": { "id": "0_45NbIUDvGI" }, "source": [ "### Load from CSV\n", "\n", "This dataset was discussed in a blog on [vibration metrics and used bearing data as an example](https://blog.endaq.com/top-vibration-metrics-to-monitor-how-to-calculate-them).\n", "\n", "Note you don't *have* to use a CSV. They have a lot of other file formats natively supported ([see full list](https://pandas.pydata.org/docs/user_guide/io.html)):\n", "\n", "* hdf\n", "* feather\n", "* pickle\n", "\n", "But I know everyone likes CSVs!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "8Yiph7IODh4E", "outputId": "c87e5403-aeb5-4a33-a402-8971ecb52c85" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Fault_021Fault_014Fault_007Normal
Time
0.000000-0.105351-0.0743950.0531160.046104
0.0000830.1328880.0563650.116628-0.037134
0.000167-0.0565350.2012570.083654-0.089496
0.000250-0.193178-0.024528-0.026477-0.084906
0.0003330.064879-0.0722840.045319-0.038594
...............
9.9996670.0957540.145055-0.0989230.064254
9.999750-0.1230830.092263-0.0675730.070721
9.999833-0.036508-0.1681200.0056850.103265
9.9999170.097006-0.0358980.0934000.124335
10.000000-0.0087620.1658460.1309230.114947
\n", "

120000 rows × 4 columns

\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal\n", "Time \n", "0.000000 -0.105351 -0.074395 0.053116 0.046104\n", "0.000083 0.132888 0.056365 0.116628 -0.037134\n", "0.000167 -0.056535 0.201257 0.083654 -0.089496\n", "0.000250 -0.193178 -0.024528 -0.026477 -0.084906\n", "0.000333 0.064879 -0.072284 0.045319 -0.038594\n", "... ... ... ... ...\n", "9.999667 0.095754 0.145055 -0.098923 0.064254\n", "9.999750 -0.123083 0.092263 -0.067573 0.070721\n", "9.999833 -0.036508 -0.168120 0.005685 0.103265\n", "9.999917 0.097006 -0.035898 0.093400 0.124335\n", "10.000000 -0.008762 0.165846 0.130923 0.114947\n", "\n", "[120000 rows x 4 columns]" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.read_csv('https://info.endaq.com/hubfs/Plots/bearing_data.csv', index_col=0)\n", "df" ] }, { "cell_type": "markdown", "metadata": { "id": "SuYE7enn5-0T" }, "source": [ "### Save CSV\n", "\n", "Like reading data, there are a host of native formats we can save data from a dataframe. [See documentation](https://pandas.pydata.org/docs/reference/io.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rB5uIDRt6CJe" }, "outputs": [], "source": [ "df.to_csv('bearing-data.csv')" ] }, { "cell_type": "markdown", "metadata": { "id": "JrAWfnkOJZsk" }, "source": [ "### Simple Analysis" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 300 }, "id": "TVD8WFpvHqs-", "outputId": "17bbd709-f79a-4d6e-f0e1-bb42f01d3242" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Fault_021Fault_014Fault_007Normal
count120000.000000120000.000000120000.000000120000.000000
mean0.0122510.0027290.0029530.010755
std0.1983830.1577610.1212720.065060
min-1.037862-1.338628-0.650390-0.269114
25%-0.107020-0.096649-0.072284-0.032544
50%0.0116820.0012990.0045480.013351
75%0.1320540.1008720.0800810.056535
max0.9179081.1243760.5940250.251382
\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal\n", "count 120000.000000 120000.000000 120000.000000 120000.000000\n", "mean 0.012251 0.002729 0.002953 0.010755\n", "std 0.198383 0.157761 0.121272 0.065060\n", "min -1.037862 -1.338628 -0.650390 -0.269114\n", "25% -0.107020 -0.096649 -0.072284 -0.032544\n", "50% 0.011682 0.001299 0.004548 0.013351\n", "75% 0.132054 0.100872 0.080081 0.056535\n", "max 0.917908 1.124376 0.594025 0.251382" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.describe()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "6fiVPnBNJb32", "outputId": "df0b8b39-f9df-44bf-c236-7695d375cae4" }, "outputs": [ { "data": { "text/plain": [ "Fault_021 0.198383\n", "Fault_014 0.157761\n", "Fault_007 0.121272\n", "Normal 0.065060\n", "dtype: float64" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.std()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "oQgXuQ9VJhTd", "outputId": "537e189f-46b5-4945-808d-705f1932e4f0" }, "outputs": [ { "data": { "text/plain": [ "Fault_021 0.917908\n", "Fault_014 1.124376\n", "Fault_007 0.594025\n", "Normal 0.251382\n", "dtype: float64" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.max()" ] }, { "cell_type": "markdown", "metadata": { "id": "KuwaUNJPth1r" }, "source": [ "Note that these built in Pandas functions are using NumPy to process and are the equivalent of doing the following." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TUsz_I4ptfo3", "outputId": "bd9f9729-ecf7-4899-8113-ca5a5cc4ffe0" }, "outputs": [ { "data": { "text/plain": [ "Fault_021 0.917908\n", "Fault_014 1.124376\n", "Fault_007 0.594025\n", "Normal 0.251382\n", "dtype: float64" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.max(df)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "cEG3SeRAJkyW", "outputId": "33ef1359-42c7-41f7-d908-5a753168ac5f" }, "outputs": [ { "data": { "text/plain": [ "Fault_021 -0.107020\n", "Fault_014 -0.096649\n", "Fault_007 -0.072284\n", "Normal -0.032544\n", "Name: 0.25, dtype: float64" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.quantile(0.25)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "Shx-sdauJllV", "outputId": "8f8d4f7b-5071-4df5-c5d2-4c7ef7ae0a49" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
Fault_021Fault_014Fault_007Normalabs(max)
Time
0.000000-0.105351-0.0743950.0531160.0461040.105351
0.0000830.1328880.0563650.116628-0.0371340.132888
0.000167-0.0565350.2012570.083654-0.0894960.201257
0.000250-0.193178-0.024528-0.026477-0.0849060.193178
0.0003330.064879-0.0722840.045319-0.0385940.072284
..................
9.9996670.0957540.145055-0.0989230.0642540.145055
9.999750-0.1230830.092263-0.0675730.0707210.123083
9.999833-0.036508-0.1681200.0056850.1032650.168120
9.9999170.097006-0.0358980.0934000.1243350.124335
10.000000-0.0087620.1658460.1309230.1149470.165846
\n", "

120000 rows × 5 columns

\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal abs(max)\n", "Time \n", "0.000000 -0.105351 -0.074395 0.053116 0.046104 0.105351\n", "0.000083 0.132888 0.056365 0.116628 -0.037134 0.132888\n", "0.000167 -0.056535 0.201257 0.083654 -0.089496 0.201257\n", "0.000250 -0.193178 -0.024528 -0.026477 -0.084906 0.193178\n", "0.000333 0.064879 -0.072284 0.045319 -0.038594 0.072284\n", "... ... ... ... ... ...\n", "9.999667 0.095754 0.145055 -0.098923 0.064254 0.145055\n", "9.999750 -0.123083 0.092263 -0.067573 0.070721 0.123083\n", "9.999833 -0.036508 -0.168120 0.005685 0.103265 0.168120\n", "9.999917 0.097006 -0.035898 0.093400 0.124335 0.124335\n", "10.000000 -0.008762 0.165846 0.130923 0.114947 0.165846\n", "\n", "[120000 rows x 5 columns]" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['abs(max)'] = df.abs().max(axis=1)\n", "df " ] }, { "cell_type": "markdown", "metadata": { "id": "oilY0XL3MSA_" }, "source": [ "### Indexing\n", "Here is where indexing in Python gets a whole lot more intuitive! A dataframe with an index let's use index values (time in this case) to slice the dataframe, not rely on the nth element in the arrays." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "-u2YH6_WMxdX", "outputId": "6b4dd652-7842-4961-ee70-2c8bb4970ca9" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
Fault_021Fault_014Fault_007Normalabs(max)
Time
0.000000-0.105351-0.0743950.0531160.0461040.105351
0.0000830.1328880.0563650.116628-0.0371340.132888
0.000167-0.0565350.2012570.083654-0.0894960.201257
0.000250-0.193178-0.024528-0.026477-0.0849060.193178
0.0003330.064879-0.0722840.045319-0.0385940.072284
..................
0.0495840.1310100.129136-0.0146190.0214870.131010
0.0496670.437675-0.221399-0.0253400.0210700.437675
0.0497500.095754-0.1206890.0331370.0352560.120689
0.049834-0.1372690.2759770.0237160.0442260.275977
0.0499170.1502030.019167-0.0446700.0054240.150203
\n", "

600 rows × 5 columns

\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal abs(max)\n", "Time \n", "0.000000 -0.105351 -0.074395 0.053116 0.046104 0.105351\n", "0.000083 0.132888 0.056365 0.116628 -0.037134 0.132888\n", "0.000167 -0.056535 0.201257 0.083654 -0.089496 0.201257\n", "0.000250 -0.193178 -0.024528 -0.026477 -0.084906 0.193178\n", "0.000333 0.064879 -0.072284 0.045319 -0.038594 0.072284\n", "... ... ... ... ... ...\n", "0.049584 0.131010 0.129136 -0.014619 0.021487 0.131010\n", "0.049667 0.437675 -0.221399 -0.025340 0.021070 0.437675\n", "0.049750 0.095754 -0.120689 0.033137 0.035256 0.120689\n", "0.049834 -0.137269 0.275977 0.023716 0.044226 0.275977\n", "0.049917 0.150203 0.019167 -0.044670 0.005424 0.150203\n", "\n", "[600 rows x 5 columns]" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[0: 0.05]" ] }, { "cell_type": "markdown", "metadata": { "id": "xTQkEsfJuDAR" }, "source": [ "We can also use the same convention as before by adding in a step definition, in this case we'll grab every 100th point." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 269 }, "id": "aH5m-nHdM6MV", "outputId": "97f1fc33-f5aa-42ca-83f5-d1e8b2919da6" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Fault_021Fault_014Fault_007Normalabs(max)
Time
0.000000-0.105351-0.0743950.0531160.0461040.105351
0.008333-0.481067-0.137745-0.010558-0.0129340.481067
0.016667-0.265985-0.073746-0.1406690.0273290.265985
0.025000-0.1923430.203856-0.168120-0.0298320.203856
0.0333340.0189840.154476-0.0729330.0763530.154476
0.041667-0.289975-0.2274090.0882020.0168980.289975
\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal abs(max)\n", "Time \n", "0.000000 -0.105351 -0.074395 0.053116 0.046104 0.105351\n", "0.008333 -0.481067 -0.137745 -0.010558 -0.012934 0.481067\n", "0.016667 -0.265985 -0.073746 -0.140669 0.027329 0.265985\n", "0.025000 -0.192343 0.203856 -0.168120 -0.029832 0.203856\n", "0.033334 0.018984 0.154476 -0.072933 0.076353 0.154476\n", "0.041667 -0.289975 -0.227409 0.088202 0.016898 0.289975" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[0: 0.05: 100]" ] }, { "cell_type": "markdown", "metadata": { "id": "0xzown4qLWGb" }, "source": [ "There are ways to use the integer based indexing if you so desire." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 394 }, "id": "VgZB15g6LQTY", "outputId": "23123f3d-7038-4e4c-86b7-b8bba57e093e" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Fault_021Fault_014Fault_007Normalabs(max)
Time
0.000000-0.105351-0.0743950.0531160.0461040.105351
0.0000830.1328880.0563650.116628-0.0371340.132888
0.000167-0.0565350.2012570.083654-0.0894960.201257
0.000250-0.193178-0.024528-0.026477-0.0849060.193178
0.0003330.064879-0.0722840.045319-0.0385940.072284
0.0004170.2148740.0347610.0607510.0254510.214874
0.000500-0.0763530.094212-0.1741300.0406800.174130
0.000583-0.065922-0.070010-0.2295210.0425580.229521
0.0006670.206529-0.0794310.0454820.0381770.206529
0.0007500.0214870.0924260.0274520.0440180.092426
\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal abs(max)\n", "Time \n", "0.000000 -0.105351 -0.074395 0.053116 0.046104 0.105351\n", "0.000083 0.132888 0.056365 0.116628 -0.037134 0.132888\n", "0.000167 -0.056535 0.201257 0.083654 -0.089496 0.201257\n", "0.000250 -0.193178 -0.024528 -0.026477 -0.084906 0.193178\n", "0.000333 0.064879 -0.072284 0.045319 -0.038594 0.072284\n", "0.000417 0.214874 0.034761 0.060751 0.025451 0.214874\n", "0.000500 -0.076353 0.094212 -0.174130 0.040680 0.174130\n", "0.000583 -0.065922 -0.070010 -0.229521 0.042558 0.229521\n", "0.000667 0.206529 -0.079431 0.045482 0.038177 0.206529\n", "0.000750 0.021487 0.092426 0.027452 0.044018 0.092426" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.iloc[0:10]" ] }, { "cell_type": "markdown", "metadata": { "id": "XxIMZ9-gMSfC" }, "source": [ "### Rolling\n", "I love the [rolling method](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rolling.html) which allows for easy rolling window calculations, something you'll do frequently with time series data." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "QeIgZF4WNo7K", "outputId": "59361aae-3320-48b6-8716-e68971845511" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
Fault_021Fault_014Fault_007Normalabs(max)
Time
0.000000NaNNaNNaNNaNNaN
0.000083NaNNaNNaNNaNNaN
0.000167NaNNaNNaNNaNNaN
0.000250NaNNaNNaNNaNNaN
0.000333NaNNaNNaNNaNNaN
..................
9.9996670.7487210.7683180.4083620.185250.933137
9.9997500.7487210.7683180.4083620.185250.933137
9.9998330.7487210.7683180.4083620.185250.933137
9.9999170.7487210.7683180.4083620.185250.933137
10.0000000.7487210.7683180.4083620.185250.933137
\n", "

120000 rows × 5 columns

\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal abs(max)\n", "Time \n", "0.000000 NaN NaN NaN NaN NaN\n", "0.000083 NaN NaN NaN NaN NaN\n", "0.000167 NaN NaN NaN NaN NaN\n", "0.000250 NaN NaN NaN NaN NaN\n", "0.000333 NaN NaN NaN NaN NaN\n", "... ... ... ... ... ...\n", "9.999667 0.748721 0.768318 0.408362 0.18525 0.933137\n", "9.999750 0.748721 0.768318 0.408362 0.18525 0.933137\n", "9.999833 0.748721 0.768318 0.408362 0.18525 0.933137\n", "9.999917 0.748721 0.768318 0.408362 0.18525 0.933137\n", "10.000000 0.748721 0.768318 0.408362 0.18525 0.933137\n", "\n", "[120000 rows x 5 columns]" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = int(df.shape[0] / 100)\n", "df.rolling(n).max()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "eke1fPqkN_NS", "outputId": "e238d753-2fcb-4119-90b4-b8e34c98b900" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
Fault_021Fault_014Fault_007Normalabs(max)
Time
0.000000NaNNaNNaNNaNNaN
0.1000010.7261900.7101660.4434480.1844160.813809
0.2000020.6961500.6411310.4117730.2044430.740376
0.3000030.4912890.8466120.4921780.1725250.846612
0.4000030.5786990.6084820.5248280.2048600.608482
..................
9.5000790.7318230.5009500.4747980.2061120.760820
9.6000800.6978180.7132530.4704120.1904660.713253
9.7000810.6777910.5472440.4159960.2290600.677791
9.8000820.6252200.5694980.3914690.1764890.653801
9.9000830.7495550.9492710.3323420.1764890.949271
\n", "

100 rows × 5 columns

\n", "
" ], "text/plain": [ " Fault_021 Fault_014 Fault_007 Normal abs(max)\n", "Time \n", "0.000000 NaN NaN NaN NaN NaN\n", "0.100001 0.726190 0.710166 0.443448 0.184416 0.813809\n", "0.200002 0.696150 0.641131 0.411773 0.204443 0.740376\n", "0.300003 0.491289 0.846612 0.492178 0.172525 0.846612\n", "0.400003 0.578699 0.608482 0.524828 0.204860 0.608482\n", "... ... ... ... ... ...\n", "9.500079 0.731823 0.500950 0.474798 0.206112 0.760820\n", "9.600080 0.697818 0.713253 0.470412 0.190466 0.713253\n", "9.700081 0.677791 0.547244 0.415996 0.229060 0.677791\n", "9.800082 0.625220 0.569498 0.391469 0.176489 0.653801\n", "9.900083 0.749555 0.949271 0.332342 0.176489 0.949271\n", "\n", "[100 rows x 5 columns]" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.rolling(n).max()[::n]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "ZX7LWaYlL-eH", "outputId": "dcf2ba3c-08ed-46a2-e1b6-8ab978787f0a" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "px.line(df.rolling(n).max()[::n]).show()" ] }, { "cell_type": "markdown", "metadata": { "id": "fEldJh0jO-N8" }, "source": [ "### Datetime Data (Yay Finance!)\n", "\n", "Let's use Yahoo Finance and stock data as a relatable example of data with datetimes." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "jSQFF4_PMFyX", "outputId": "e290bbf0-00e8-46ab-b733-d8652f8a0c3f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[K |████████████████████████████████| 6.3 MB 56.8 MB/s \n", "\u001b[?25h Building wheel for yfinance (setup.py) ... \u001b[?25l\u001b[?25hdone\n" ] } ], "source": [ "!pip install -q yfinance\n", "import yfinance as yf" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 697 }, "id": "NBd9jY74MOs_", "outputId": "34453976-3b01-4b41-ce61-3b936aa5ba08" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[*********************100%***********************] 5 of 5 completed\n" ] }, { "data": { "text/html": [ "
\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", "
Adj CloseCloseHighLowOpenVolume
AAPLAMZNGOOGLMSFTSPYAAPLAMZNGOOGLMSFTSPYAAPLAMZNGOOGLMSFTSPYAAPLAMZNGOOGLMSFTSPYAAPLAMZNGOOGLMSFTSPYAAPLAMZNGOOGLMSFTSPY
Date
2019-01-0238.3822291539.1300051054.68005497.961319238.69455039.4800001539.1300051054.680054101.120003250.17999339.7125021553.3599851060.790039101.750000251.21000738.5574991460.9300541025.28002998.940002245.94999738.7225001465.1999511027.19995199.550003245.9799961481588007983100159340035329300126925200
2019-01-0334.5590781500.2800291025.46997194.357529232.99862735.5475011500.2800291025.46997197.400002244.21000736.4300001538.0000001066.260010100.190002248.57000735.5000001497.1099851022.36999597.199997243.66999835.9949991520.0100101050.670044100.099998248.2299963652488006975600209800042579100144140700
2019-01-0436.0343701575.3900151078.06994698.746010240.80307037.0649991575.3900151078.069946101.930000252.38999937.1375011594.0000001080.000000102.510002253.11000135.9500011518.3100591036.85998598.930000247.16999836.1325001530.0000001042.56005999.720001247.5899962344284009182600230110044060600142628800
2019-01-0735.9541701629.5100101075.92004498.871956242.70170636.9824981629.5100101075.920044102.059998254.38000537.2075001634.5600591082.699951103.269997255.94999736.4749981589.1899411062.640015100.980003251.69000237.1749991602.3100591080.969971101.639999252.6900022191112007993200237230035656100103139100
2019-01-0836.6395651656.5799561085.36999599.588829244.98199537.6875001656.5799561085.369995102.800003256.76998937.9550021676.6099851093.349976103.970001257.30999837.1300011616.6099851068.349976101.709999254.00000037.3899991664.6899411086.000000103.040001256.8200071641012008881400177070031514400102512600
.............................................................................................
2021-09-17146.0599983462.5200202816.000000299.869995441.399994146.0599983462.5200202816.000000299.869995441.399994148.8200073497.4099122869.000000304.500000445.369995145.7599953452.1298832809.399902299.529999441.019989148.8200073488.4099122860.610107304.170013444.9200131297287004614100266680041309300118220200
2021-09-20142.9400023355.7299802774.389893294.299988434.040009142.9400023355.7299802774.389893294.299988434.040009144.8399963419.0000002780.000000298.720001436.559998141.2700043305.0100102726.439941289.519989428.859985143.8000033396.0000002763.229980296.329987434.8800051234789004669100232590038278700166445500
2021-09-21143.4299933343.6298832780.659912294.799988433.630005143.4299933343.6298832780.659912294.799988433.630005144.6000063379.6999512800.229980297.540009437.910004142.7799993332.3898932765.560059294.070007433.070007143.9299933375.0000002794.989990295.690002436.52999975834000278090012666002236410092526100
2021-09-22145.8500063380.0500492805.669922298.579987437.859985145.8500063380.0500492805.669922298.579987437.859985146.4299933389.0000002817.739990300.220001440.029999143.6999973341.0500492771.030029294.510010433.750000144.4499973351.0000002786.080078296.730011436.049988764043002411400125280026626300102350100
2021-09-23146.8300023416.0000002824.320068299.559998443.179993146.8300023416.0000002824.320068299.559998443.179993147.0800023428.9599612833.929932300.899994444.890015145.6399993380.0500492808.030029297.529999439.600006146.6499943380.0500492819.750000298.850006439.85000664838200237940010476001860460076396000
\n", "

688 rows × 30 columns

\n", "
" ], "text/plain": [ " Adj Close ... Volume \n", " AAPL AMZN GOOGL ... GOOGL MSFT SPY\n", "Date ... \n", "2019-01-02 38.382229 1539.130005 1054.680054 ... 1593400 35329300 126925200\n", "2019-01-03 34.559078 1500.280029 1025.469971 ... 2098000 42579100 144140700\n", "2019-01-04 36.034370 1575.390015 1078.069946 ... 2301100 44060600 142628800\n", "2019-01-07 35.954170 1629.510010 1075.920044 ... 2372300 35656100 103139100\n", "2019-01-08 36.639565 1656.579956 1085.369995 ... 1770700 31514400 102512600\n", "... ... ... ... ... ... ... ...\n", "2021-09-17 146.059998 3462.520020 2816.000000 ... 2666800 41309300 118220200\n", "2021-09-20 142.940002 3355.729980 2774.389893 ... 2325900 38278700 166445500\n", "2021-09-21 143.429993 3343.629883 2780.659912 ... 1266600 22364100 92526100\n", "2021-09-22 145.850006 3380.050049 2805.669922 ... 1252800 26626300 102350100\n", "2021-09-23 146.830002 3416.000000 2824.320068 ... 1047600 18604600 76396000\n", "\n", "[688 rows x 30 columns]" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = yf.download([\"SPY\", \"AAPL\", \"MSFT\", \"AMZN\", \"GOOGL\"],\n", " start='2019-01-01',\n", " end='2021-09-24') \n", "df" ] }, { "cell_type": "markdown", "metadata": { "id": "DrzB4cQh7zvI" }, "source": [ "Let's compare not the price, but the relative performance." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "KA6yCs-IPqpD", "outputId": "5309f784-3917-4125-f1a9-f39b56fb3372" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
AAPLAMZNGOOGLMSFTSPY
Date
2019-01-021.0000001.0000001.0000001.0000001.000000
2019-01-030.9003930.9747580.9723040.9632120.976137
2019-01-040.9388301.0235591.0221771.0080101.008834
2019-01-070.9367401.0587211.0201391.0092961.016788
2019-01-080.9545971.0763091.0290991.0166141.026341
..................
2021-09-173.8054072.2496612.6700043.0611061.849225
2021-09-203.7241192.1802772.6305513.0042471.818391
2021-09-213.7368852.1724162.6364963.0093511.816673
2021-09-223.7999362.1960782.6602103.0479381.834395
2021-09-233.8254682.2194362.6778933.0579421.856682
\n", "

688 rows × 5 columns

\n", "
" ], "text/plain": [ " AAPL AMZN GOOGL MSFT SPY\n", "Date \n", "2019-01-02 1.000000 1.000000 1.000000 1.000000 1.000000\n", "2019-01-03 0.900393 0.974758 0.972304 0.963212 0.976137\n", "2019-01-04 0.938830 1.023559 1.022177 1.008010 1.008834\n", "2019-01-07 0.936740 1.058721 1.020139 1.009296 1.016788\n", "2019-01-08 0.954597 1.076309 1.029099 1.016614 1.026341\n", "... ... ... ... ... ...\n", "2021-09-17 3.805407 2.249661 2.670004 3.061106 1.849225\n", "2021-09-20 3.724119 2.180277 2.630551 3.004247 1.818391\n", "2021-09-21 3.736885 2.172416 2.636496 3.009351 1.816673\n", "2021-09-22 3.799936 2.196078 2.660210 3.047938 1.834395\n", "2021-09-23 3.825468 2.219436 2.677893 3.057942 1.856682\n", "\n", "[688 rows x 5 columns]" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = df['Adj Close']\n", "df = df / df.iloc[0]\n", "df" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "GwRWHNPUQj1I", "outputId": "4bf45621-d8e6-4720-d389-3f17ebcd1383" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "px.line(df).show()" ] }, { "cell_type": "markdown", "metadata": { "id": "0AN97x-7RGF9" }, "source": [ "The [rolling function](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rolling.html) will play very nicely with datetime data as shown here when I get the moving average over a 40 day period. And this can handle unevenly sampled date easily." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "FL9eZOutQzso", "outputId": "36661409-e636-4055-e6b7-7bd68c046329" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "px.line(df.rolling('40d').mean()).show()" ] }, { "cell_type": "markdown", "metadata": { "id": "3XzrLgmRR02j" }, "source": [ "Indexing with datetime data though will require a slightly extra step, but then it is easy." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZQpQ7EJIRAJP" }, "outputs": [], "source": [ "from datetime import date\n", "start = date(2021, 4, 1)\n", "end = date(2021, 4, 30)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 739 }, "id": "1YU6TiEcRzPa", "outputId": "96ee05aa-e1a9-4e69-cf17-2d62f5e1c9b9" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
AAPLAMZNGOOGLMSFTSPY
Date
2021-04-013.1943882.0537582.0193612.4635201.667522
2021-04-053.2697032.0964642.1039182.5318301.691456
2021-04-063.2777542.0945732.0947212.5195301.690457
2021-04-073.3216442.1306782.1229472.5402671.692414
2021-04-083.3855322.1436142.1337562.5743201.700447
2021-04-093.4540942.1909782.1529472.6007491.712810
2021-04-123.4083862.1956492.1282472.6013591.713434
2021-04-133.4912322.2090402.1375492.6275851.718512
2021-04-143.4289032.1655092.1256782.5981061.712644
2021-04-153.4930502.1954552.1667712.6378521.731041
2021-04-163.4842202.2086762.1644002.6504571.736827
2021-04-193.5018802.1908552.1710472.6301271.728294
2021-04-203.4569512.1666072.1608542.6252471.715640
2021-04-213.4670792.1843642.1602292.6488301.731874
2021-04-223.4265662.1499422.1357382.6141671.716057
2021-04-233.4883762.1706292.1806902.6546241.734663
2021-04-263.4987642.2148882.1901712.6586901.738284
2021-04-273.4901932.2203652.1722042.6629601.737910
2021-04-283.4691572.2470492.2367352.5876361.737410
2021-04-293.4665602.2553722.2687072.5667981.748482
2021-04-303.4141002.2528442.2314822.5634431.736994
\n", "
" ], "text/plain": [ " AAPL AMZN GOOGL MSFT SPY\n", "Date \n", "2021-04-01 3.194388 2.053758 2.019361 2.463520 1.667522\n", "2021-04-05 3.269703 2.096464 2.103918 2.531830 1.691456\n", "2021-04-06 3.277754 2.094573 2.094721 2.519530 1.690457\n", "2021-04-07 3.321644 2.130678 2.122947 2.540267 1.692414\n", "2021-04-08 3.385532 2.143614 2.133756 2.574320 1.700447\n", "2021-04-09 3.454094 2.190978 2.152947 2.600749 1.712810\n", "2021-04-12 3.408386 2.195649 2.128247 2.601359 1.713434\n", "2021-04-13 3.491232 2.209040 2.137549 2.627585 1.718512\n", "2021-04-14 3.428903 2.165509 2.125678 2.598106 1.712644\n", "2021-04-15 3.493050 2.195455 2.166771 2.637852 1.731041\n", "2021-04-16 3.484220 2.208676 2.164400 2.650457 1.736827\n", "2021-04-19 3.501880 2.190855 2.171047 2.630127 1.728294\n", "2021-04-20 3.456951 2.166607 2.160854 2.625247 1.715640\n", "2021-04-21 3.467079 2.184364 2.160229 2.648830 1.731874\n", "2021-04-22 3.426566 2.149942 2.135738 2.614167 1.716057\n", "2021-04-23 3.488376 2.170629 2.180690 2.654624 1.734663\n", "2021-04-26 3.498764 2.214888 2.190171 2.658690 1.738284\n", "2021-04-27 3.490193 2.220365 2.172204 2.662960 1.737910\n", "2021-04-28 3.469157 2.247049 2.236735 2.587636 1.737410\n", "2021-04-29 3.466560 2.255372 2.268707 2.566798 1.748482\n", "2021-04-30 3.414100 2.252844 2.231482 2.563443 1.736994" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[start:end]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "aJWvXHmlcGtV", "outputId": "7564cf85-46c9-482b-e30c-895d888a17c3" }, "outputs": [ { "data": { "text/plain": [ "DatetimeIndex(['2019-01-31', '2019-02-28', '2019-03-31', '2019-04-30',\n", " '2019-05-31', '2019-06-30', '2019-07-31', '2019-08-31',\n", " '2019-09-30', '2019-10-31', '2019-11-30', '2019-12-31',\n", " '2020-01-31', '2020-02-29', '2020-03-31', '2020-04-30',\n", " '2020-05-31', '2020-06-30', '2020-07-31', '2020-08-31',\n", " '2020-09-30', '2020-10-31', '2020-11-30', '2020-12-31',\n", " '2021-01-31', '2021-02-28', '2021-03-31', '2021-04-30',\n", " '2021-05-31', '2021-06-30', '2021-07-31', '2021-08-31'],\n", " dtype='datetime64[ns]', freq='M')" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.date_range(start='1/1/2019', end='08/31/2021', freq='M')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 425 }, "id": "Q1KZmgV4cVGe", "outputId": "6c89eb55-ca9f-4df1-ffce-dfb308d16ff4" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
AAPLAMZNGOOGLMSFTSPY
Date
2019-03-311.2406711.1820051.1720431.1939621.143113
2019-06-301.3466191.2750451.2289981.3734241.187797
2019-09-301.4352501.3130731.1813441.4109021.218385
2019-12-311.8874241.2148421.2918331.5952371.315273
2020-03-312.1080571.4100301.4458131.8936921.377995
2020-06-302.3678411.7960861.3887622.0535991.324073
2020-09-303.4735472.2944461.6283522.3432081.471860
2020-12-313.5446292.2373871.7303542.2814941.551179
2021-03-313.7124092.1960462.0087802.4846341.649707
2021-06-303.5629802.2775462.3236622.7651871.787611
2021-09-304.0823582.4243632.7537363.1157201.892556
\n", "
" ], "text/plain": [ " AAPL AMZN GOOGL MSFT SPY\n", "Date \n", "2019-03-31 1.240671 1.182005 1.172043 1.193962 1.143113\n", "2019-06-30 1.346619 1.275045 1.228998 1.373424 1.187797\n", "2019-09-30 1.435250 1.313073 1.181344 1.410902 1.218385\n", "2019-12-31 1.887424 1.214842 1.291833 1.595237 1.315273\n", "2020-03-31 2.108057 1.410030 1.445813 1.893692 1.377995\n", "2020-06-30 2.367841 1.796086 1.388762 2.053599 1.324073\n", "2020-09-30 3.473547 2.294446 1.628352 2.343208 1.471860\n", "2020-12-31 3.544629 2.237387 1.730354 2.281494 1.551179\n", "2021-03-31 3.712409 2.196046 2.008780 2.484634 1.649707\n", "2021-06-30 3.562980 2.277546 2.323662 2.765187 1.787611\n", "2021-09-30 4.082358 2.424363 2.753736 3.115720 1.892556" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.resample(rule='Q').max()" ] }, { "cell_type": "markdown", "metadata": { "id": "50HsfFt6TFSE" }, "source": [ "### Sorting & Filtering on Tabular Data\n", "To highlight filtering in DataFrames, we'll use a dataset with a bunch of different columns/series of different types. This data was pulled directly from the enDAQ cloud API off some example recording files." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "id": "PI9EYy7JSgeo", "outputId": "6936608c-279b-44e3-e0ad-f6faeff55be9" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Unnamed: 0tagsidserial_number_idfile_namefile_sizerecording_lengthrecording_tscreated_tsmodified_tsdevicegpsLocationFullvelocityRMSFullgpsSpeedFullpressureMeanFullsamplePeakStartTimedisplacementRMSFullpsuedoVelocityPeakFullaccelerationPeakFullpsdResultantOctavesamplePeakWindowtemperatureMeanFullaccelerometerSampleRateFullpsdPeakOctavesmicrophonoeRMSFullgyroscopeRMSFullpvssResultantOctaveaccelerationRMSFullpsdResultant1Hz
00['Vibration Severity: High', 'Shock Severity: ...6cd65b8f-1187-3bf9-a431-35304a7f84b79695train-passing-1632515146.ide1049260273.612335158818443616325151461632515146NaNnan,nan6.969NaN104.6201941.1250.061419.9447.513[0. 0. 0.001 0.024 0.032 0.008 0.008 0.0...[1.241 1.682 1.944 ... 1.852 1.372 1.473]23.43219999.0[0. 0. 0.001 0.131 0.229 0.045 0.05 0.0...NaN0.625[ 10.333 25.952 30.28 52.579 172.174 275.8...0.372[0. 0. 0. ... 0. 0. 0.]
11['Vibration Severity: Low', 'Acceleration Seve...342aacc3-cd91-3124-8cf4-0c7fece6dcab9316Seat-Top_09-1632515145.ide10491986172.704559157580007116325151461632515146NaNnan,nan1.535NaN98.7331265.9640.04086.5951.105[0. 0. 0. 0. 0. 0. 0. 0. ...[0.035 0.024 0.007 ... 0.067 0.031 0.022]20.1333996.0[0. 0. 0.001 0. 0.001 0. 0.001 0.0...NaN0.945[53.648 76.9 43.779 44.217 39.351 11.396 9....0.082[ 0. 0. 0. ... nan nan nan]
22['Shock Severity: Very Low', 'Acceleration Sev...5c5592b4-2dbc-3124-be8f-c7179eecda4910118Bolted-1632515144.ide614922929.396118161904144716325151441632515144NaNnan,nan14.101NaN99.65217.4450.154148.27615.343[0.001 0.003 0.004 0.064 0.106 0.102 0.103 0.1...[5.283 5.944 5.19 ... 0.356 1.079 1.099]23.17220000.0[0.001 0.004 0.004 0.128 0.125 0.119 0.14 0.1...25.507NaN[ 26.338 27.636 64.097 102.733 107.863 124.6...2.398[0. 0.001 0.002 ... 0.002 0.001 0.001]
33['Shock Severity: Very Low', 'Acceleration Sev...ef832e45-50fa-38b4-8f2c-91769f7cef6e10309RMI-2000-1632515143.ide590963260.2508552416325151431632515143NaNnan,nan1.247NaN100.46740.3830.00517.2870.332[0. 0. 0. 0. 0.001 0.002 0.002 0.0...[0.13 0.118 0.1 ... 0.105 0.1 0.066]21.8064012.0[0. 0. 0. 0. 0.008 0.01 0.019 0.0...1.7541.557[ 0.854 1.159 1.662 1.815 3.022 6.139 10....0.079[ 0. 0. 0. ... nan nan nan]
44['Shock Severity: Very Low', 'Acceleration Sev...0396df6a-56b0-3e43-8e46-e1d0d7485ff59295Seat-Base_21-1632515142.ide524883683.092255157580021016325151431632515143NaNnan,nan7.318NaN98.9301588.2990.190251.0091.085[0.002 0.007 0.008 0.005 0.002 0.005 0.002 0.0...[0.084 0.137 0.178 ... 0.347 0.324 0.286]17.8204046.0[0.002 0.014 0.016 0.013 0.003 0.031 0.003 0.0...NaN2.666[ 74.73 79.453 101.006 151.429 73.92 53.4...0.130[0.001 0.002 0.002 ... nan nan nan]
55['Drive-Test', 'Vibration Severity: Very Low',...5e293d65-c9e0-3aa1-9098-c9762e8fbc8611046Drive-Home_01-1632515142.ide363279961.755371161617895516325151421632515142NaNnan,nan1.081NaN100.28422.5430.02340.1970.479[0. 0. 0. 0. 0.001 0. 0. 0. ...[0.001 0.003 0.002 ... 0.011 0.008 0.006]29.0614013.0[0. 0. 0. 0. 0.007 0. 0. 0. ...16.2430.363[ 2.336 7.82 19.078 10.384 16.975 38.326 18....0.021[ 0. 0. 0. ... nan nan nan]
66['Acceleration Severity: High', 'Shock Severit...cd81c850-9e22-3b20-b570-7411e7a144cc0HiTest-Shock-1632515141.ide265589420.331848154393697416325151411632515141NaNnan,nan167.835NaN101.126327.3924.0556058.093619.178[0.304 0.486 0.399 0.454 0.347 0.178 0.185 0.1...[ 3.088 3.019 2.893 ... 73.794 40.005 24.303]9.53819997.0[ 0.304 0.537 0.479 0.811 0.624 0.554 0....NaNNaN[1378.444 2470.941 4368.78 5033.327 5814.49 ...11.645[0.157 0.304 0.42 ... 0.001 0.01 0.013]
77['Vibration Severity: High', 'Acceleration Sev...0931a23d-3515-3d11-bf97-bb9b2c7863be11162Calibration-Shake-1632515140.IDE221813027.882690162127897016325151401632515140NaNnan,nan46.346NaN102.2518.4600.6171142.2828.783[2.100e-02 7.400e-02 7.500e-02 4.900e-02 5.500...[7.486 7.496 7.137 ... 7.997 8.294 7.806]24.5455000.0[2.10000e-02 9.00000e-02 8.60000e-02 6.30000e-...NaN0.166[ 90.703 198.651 288.078 183.492 126.417 108.4...2.712[0.007 0.021 0.055 ... nan nan nan]
88['Shock Severity: Very Low', 'Acceleration Sev...c1571d10-2329-3aea-aa5d-223733f6336b10916FUSE_HSTAB_000005-1632515139.ide53756218.491791161910800416325151401632515140NaNnan,nan1.504NaN90.70612.5190.03653.3750.202[ 0. 0. 0. 0. 0. 0. 0. nan nan nan nan n...[0.001 0.001 0.001 ... 0.002 0.006 0.006]18.874504.0[0. 0. 0.001 0.001 0. 0. 0. n...NaN0.749[ 2.617 6.761 17.326 34.067 28.721 9.469 15....0.011[ 0. 0. 0. ... nan nan nan]
90['Shock Severity: Medium', 'Acceleration Sever...8ce137ff-904e-314b-81ff-ff2cea279db29874Coffee_002-1631722736.IDE60959516769.29989695211374416317227361631722736NaNnan,nan5.606NaN100.339NaN0.1041338.3962.698[][]24.5405000.0[]NaN0.082[]0.059[]
101['Vibration Severity: High', 'Shock Severity: ...9d7383fb-40d6-34c1-9d0c-37f11c29bff511456100_Joules_900_lbs-1629315313.ide159671420.200623162732731516293153131629315313NaNnan,nan53.875NaN98.7515.6431.0532961.256218.634[0.071 0.201 0.293 0.686 1.565 1.028 0.856 0.9...[0.304 0.274 0.296 ... 1.513 1.313 0.652]24.1805000.0[0.071 0.236 0.311 1.148 2.036 1.791 1.498 2.7...NaN24.471[ 194.741 361.14 563.488 855.34 1712.229 ...2.877[0.02 0.071 0.138 ... nan nan nan]
112['Acceleration Severity: High', 'Shock Severit...5f35ed7f-ff55-36a3-896d-276f06a0e3401145650_Joules_900_lbs-1629315312.ide159775020.201752162732939916293153121629315312NaNnan,nan54.507NaN98.7459.8751.0662907.650231.212[0.074 0.196 0.327 0.874 1.521 0.917 0.478 0.7...[0.304 0.21 0.098 ... 0.693 1.418 0.932]24.1755000.0[0.074 0.232 0.392 1.258 2.045 2.049 1.143 2.3...NaN15.575[ 242.141 388.517 736.981 1070.284 1843.722 ...2.423[0.021 0.074 0.137 ... nan nan nan]
123['Shock Severity: Very Low', 'Vibration Severi...f7240576-47c6-34b8-bdce-3fe11bb5f1c611046Drive-Home_07-1626805222.ide36225758634.732056161618255716268052221626805222NaN42.4679965,-71.129260199999996.11741.425101.988NaN0.135356.12823.805[][]28.8324012.0[]16.2774.185[]0.097[]
134['Big-Mining']c2c234cc-8055-3fba-ad8b-446c4b6f85dc5120Mining-SSX28803_06-1626457584.IDE4029206863238.119202153695330416264575851626457585NaNNaNNaNNaNNaNNaNNaNNaNNaN[][]NaNNaN[]NaNNaN[]NaN[]
145['Shock Severity: Medium', 'Acceleration Sever...69fd99f8-3d85-38ec-9f5d-67e9d7b7e86810030200922_Moto_Max_Run5_Control_Larry-1626297441.ide478089399.325134160081845516262974421626297442NaNnan,nan55.569NaNNaN62.5731.0601280.34929.864[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][ 3.34 13.586 10.13 ... 7.737 8.978 8.672]NaN4014.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaNNaN[165.983 278.352 717.331 950.513 697.421 358.6...3.528[0.006 0.016 0.04 ... nan nan nan]
156['Ford']f38361de-30a7-3dda-aec4-e87a67d1ecbb9695ford_f150-1626296561.ide960970591207.678344158414250816262965611626296561NaNNaNNaNNaNNaNNaNNaNNaNNaN[][]NaNNaN[]NaNNaN[]NaN[]
167['Shock Severity: High', 'Vibration Severity: ...40718e01-f422-3926-a22d-acf6daf1182b7530Motorcycle-Car-Crash-1626277852.ide10489262151.069336156217337216262778521626277852NaNnan,nan143.437NaN100.3631137.3423.98812831.590480.737[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][3.371 3.641 3.649 ... 6.168 7.463 7.04 ]26.98910001.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN19.810[ 2723.153 5977.212 11406.291 12031.397 7337...1.732[2.250e-01 8.390e-01 2.025e+00 ... 1.000e-03 1...
178['Shock Severity: Very Low', 'Surgical', 'Vibr...ee6dc613-d422-347c-8119-f122a55ac81a11071surgical-instrument-1625829182.ide5419946.951172161911039016258291831625829183NaNnan,nan24.418NaN99.8794.0200.242387.3125.739[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0.667 0.549 1.05 ... 1.109 2.86 3.99 ]21.8895000.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN4.647[ 88.953 171.73 104.303 71.184 58.883 72.4...1.568[0.001 0.001 0.003 ... nan nan nan]
189['Acceleration Severity: High', 'Shock Severit...f642d81c-7fe7-37fd-ab75-d493fc0556db9680LOC__3__DAQ41551_11_01_02-1625170795.IDE234329228.456818161664517916251707951625170795NaNnan,nan372.049NaN105.6822045.3709.5808907.949622.040[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][ 32.679 24.925 30.95 ... 106.511 27.715 ...33.45220010.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN286.217[2333.993 5629.021 6417.48 5802.895 3692.838 ...94.197[ 2.09 8.631 18.196 ... 69.565 59.225 60.801]
1910['Vibration Severity: High', 'Shock Severity: ...0c0e92ae-214a-32bd-a5bb-5e1801da06b29680LOC__4__DAQ41551_15_05-1625170794.IDE692795864.486054161664613016251707941625170794NaNnan,nan148.591NaN105.7503004.3572.6152153.020585.863[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][14.223 14.903 24.949 ... 11.482 21.319 32.766]32.20219992.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN277.654[ 378.188 748.336 1054.232 1115.369 756.905 ...46.528[ 0.072 0.302 0.816 ... 52.226 45.804 52.81 ]
2011['Vibration Severity: High', 'Mining-Hammer', ...99ac47a8-63c2-38a4-8d92-508698eb37529680LOC__6__DAQ41551_25_01-1625170793.IDE866423863.878937161664800716251707931625170793NaNnan,nan145.223NaN102.8754867.9413.0882357.599564.966[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][ 66.83 54.723 63.536 ... 109.259 85.341 ...26.03119992.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN245.929[ 780.577 1168.663 1851.417 1094.945 793.122 ...54.408[ 0.162 0.587 1.495 ... 45.639 43.86 40.177]
2112['Mining-Hammer', 'Vibration Severity: High', ...ebf31fb2-9d36-37de-999c-3edb01f17fb29680LOC__2__DAQ38060_06_03_05-1625170793.IDE151917227.057647161664086216251707931625170793NaNnan,nan323.287NaN104.4731227.9753.1445845.241995.670[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][ 33.294 29.392 19.99 ... 175.123 162.043 ...25.61619998.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN266.815[1073.684 940.873 1063.151 957.568 975.826 ...131.087[ 0.371 0.409 0.787 ... 27.577 24.778 37.822]
2213['Shock Severity: Very Low', 'Vibration Severi...e39fe506-f39a-3301-866c-9da6a30f95779695Tilt_000000-1625156721.IDE71940323.355163162515646116251567221625156722NaNnan,nan11.042NaN99.51022.9660.345330.9460.378[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0.033 0.029 0.03 ... nan nan nan]26.4105000.0[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\\n [0...NaN11.933[142.046 273.02 216.774 129.288 54.011 24.0...0.044[0.008 0.02 0.03 ... nan nan nan]
\n", "
" ], "text/plain": [ " Unnamed: 0 ... psdResultant1Hz\n", "0 0 ... [0. 0. 0. ... 0. 0. 0.]\n", "1 1 ... [ 0. 0. 0. ... nan nan nan]\n", "2 2 ... [0. 0.001 0.002 ... 0.002 0.001 0.001]\n", "3 3 ... [ 0. 0. 0. ... nan nan nan]\n", "4 4 ... [0.001 0.002 0.002 ... nan nan nan]\n", "5 5 ... [ 0. 0. 0. ... nan nan nan]\n", "6 6 ... [0.157 0.304 0.42 ... 0.001 0.01 0.013]\n", "7 7 ... [0.007 0.021 0.055 ... nan nan nan]\n", "8 8 ... [ 0. 0. 0. ... nan nan nan]\n", "9 0 ... []\n", "10 1 ... [0.02 0.071 0.138 ... nan nan nan]\n", "11 2 ... [0.021 0.074 0.137 ... nan nan nan]\n", "12 3 ... []\n", "13 4 ... []\n", "14 5 ... [0.006 0.016 0.04 ... nan nan nan]\n", "15 6 ... []\n", "16 7 ... [2.250e-01 8.390e-01 2.025e+00 ... 1.000e-03 1...\n", "17 8 ... [0.001 0.001 0.003 ... nan nan nan]\n", "18 9 ... [ 2.09 8.631 18.196 ... 69.565 59.225 60.801]\n", "19 10 ... [ 0.072 0.302 0.816 ... 52.226 45.804 52.81 ]\n", "20 11 ... [ 0.162 0.587 1.495 ... 45.639 43.86 40.177]\n", "21 12 ... [ 0.371 0.409 0.787 ... 27.577 24.778 37.822]\n", "22 13 ... [0.008 0.02 0.03 ... nan nan nan]\n", "\n", "[23 rows x 29 columns]" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.read_csv('https://info.endaq.com/hubfs/data/endaq-cloud-table.csv')\n", "df" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TrjLYbATgLDi", "outputId": "7154d956-2df0-4a0b-a04c-b70ac0be95fb" }, "outputs": [ { "data": { "text/plain": [ "Index(['Unnamed: 0', 'tags', 'id', 'serial_number_id', 'file_name',\n", " 'file_size', 'recording_length', 'recording_ts', 'created_ts',\n", " 'modified_ts', 'device', 'gpsLocationFull', 'velocityRMSFull',\n", " 'gpsSpeedFull', 'pressureMeanFull', 'samplePeakStartTime',\n", " 'displacementRMSFull', 'psuedoVelocityPeakFull', 'accelerationPeakFull',\n", " 'psdResultantOctave', 'samplePeakWindow', 'temperatureMeanFull',\n", " 'accelerometerSampleRateFull', 'psdPeakOctaves', 'microphonoeRMSFull',\n", " 'gyroscopeRMSFull', 'pvssResultantOctave', 'accelerationRMSFull',\n", " 'psdResultant1Hz'],\n", " dtype='object')" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.columns" ] }, { "cell_type": "markdown", "metadata": { "id": "8y82HS2h8c0G" }, "source": [ "There's a lot of data here! So we'll focus on just a handful of columns and convert the time in seconds to a datetime object." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "id": "6BUQuL9kgQ6z", "outputId": "72f73913-1a57-4804-f69d-c92882d80b0b" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", "
serial_number_idfile_namefile_sizerecording_lengthrecording_tsaccelerationPeakFullpsuedoVelocityPeakFullaccelerationRMSFullvelocityRMSFulldisplacementRMSFullpressureMeanFulltemperatureMeanFull
111145650_Joules_900_lbs-1629315312.ide159775020.2017522021-07-26 19:56:39231.2122907.6502.42354.5071.06698.74524.175
1011456100_Joules_900_lbs-1629315313.ide159671420.2006232021-07-26 19:21:55218.6342961.2562.87753.8751.05398.75124.180
229695Tilt_000000-1625156721.IDE71940323.3551632021-07-01 16:21:010.378330.9460.04411.0420.34599.51026.410
711162Calibration-Shake-1632515140.IDE221813027.8826902021-05-17 19:16:108.7831142.2822.71246.3460.617102.25124.545
1711071surgical-instrument-1625829182.ide5419946.9511722021-04-22 16:53:105.739387.3121.56824.4180.24299.87921.889
810916FUSE_HSTAB_000005-1632515139.ide53756218.4917912021-04-22 16:13:240.20253.3750.0111.5040.03690.70618.874
210118Bolted-1632515144.ide614922929.3961182021-04-21 21:44:0715.343148.2762.39814.1010.15499.65223.172
209680LOC__6__DAQ41551_25_01-1625170793.IDE866423863.8789372021-03-25 04:53:27564.9662357.59954.408145.2233.088102.87526.031
199680LOC__4__DAQ41551_15_05-1625170794.IDE692795864.4860542021-03-25 04:22:10585.8632153.02046.528148.5912.615105.75032.202
189680LOC__3__DAQ41551_11_01_02-1625170795.IDE234329228.4568182021-03-25 04:06:19622.0408907.94994.197372.0499.580105.68233.452
219680LOC__2__DAQ38060_06_03_05-1625170793.IDE151917227.0576472021-03-25 02:54:22995.6705845.241131.087323.2873.144104.47325.616
1211046Drive-Home_07-1626805222.ide36225758634.7320562021-03-19 19:35:5723.805356.1280.0976.1170.135101.98828.832
511046Drive-Home_01-1632515142.ide363279961.7553712021-03-19 18:35:550.47940.1970.0211.0810.023100.28429.061
1410030200922_Moto_Max_Run5_Control_Larry-1626297441.ide478089399.3251342020-09-22 23:47:3529.8641280.3493.52855.5691.060NaNNaN
09695train-passing-1632515146.ide1049260273.6123352020-04-29 18:20:367.513419.9440.3726.9690.061104.62023.432
159695ford_f150-1626296561.ide960970591207.6783442020-03-13 23:35:08NaNNaNNaNNaNNaNNaNNaN
49295Seat-Base_21-1632515142.ide524883683.0922552019-12-08 10:16:501.085251.0090.1307.3180.19098.93017.820
19316Seat-Top_09-1632515145.ide10491986172.7045592019-12-08 10:14:311.10586.5950.0821.5350.04098.73320.133
167530Motorcycle-Car-Crash-1626277852.ide10489262151.0693362019-07-03 17:02:52480.73712831.5901.732143.4373.988100.36326.989
60HiTest-Shock-1632515141.ide265589420.3318482018-12-04 15:22:54619.1786058.09311.645167.8354.055101.1269.538
135120Mining-SSX28803_06-1626457584.IDE4029206863238.1192022018-09-14 19:28:24NaNNaNNaNNaNNaNNaNNaN
99874Coffee_002-1631722736.IDE60959516769.2998962000-03-03 20:02:242.6981338.3960.0595.6060.104100.33924.540
310309RMI-2000-1632515143.ide590963260.2508551970-01-01 00:00:240.33217.2870.0791.2470.005100.46721.806
\n", "
" ], "text/plain": [ " serial_number_id ... temperatureMeanFull\n", "11 11456 ... 24.175\n", "10 11456 ... 24.180\n", "22 9695 ... 26.410\n", "7 11162 ... 24.545\n", "17 11071 ... 21.889\n", "8 10916 ... 18.874\n", "2 10118 ... 23.172\n", "20 9680 ... 26.031\n", "19 9680 ... 32.202\n", "18 9680 ... 33.452\n", "21 9680 ... 25.616\n", "12 11046 ... 28.832\n", "5 11046 ... 29.061\n", "14 10030 ... NaN\n", "0 9695 ... 23.432\n", "15 9695 ... NaN\n", "4 9295 ... 17.820\n", "1 9316 ... 20.133\n", "16 7530 ... 26.989\n", "6 0 ... 9.538\n", "13 5120 ... NaN\n", "9 9874 ... 24.540\n", "3 10309 ... 21.806\n", "\n", "[23 rows x 12 columns]" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = df[['serial_number_id', 'file_name', 'file_size', 'recording_length', 'recording_ts',\n", " 'accelerationPeakFull', 'psuedoVelocityPeakFull', 'accelerationRMSFull',\n", " 'velocityRMSFull', 'displacementRMSFull', 'pressureMeanFull', 'temperatureMeanFull']].copy()\n", "\n", "df['recording_ts'] = pd.to_datetime(df['recording_ts'], unit='s') \n", "df = df.sort_values(by=['recording_ts'], ascending=False) \n", "df " ] }, { "cell_type": "markdown", "metadata": { "id": "5ZLSvcMZ8t6-" }, "source": [ "Filtering is made simple with boolean expressions that can be combined. There is also a method to sort_values by columns/series." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 702 }, "id": "3oe0EgX0gyfz", "outputId": "7008ef1a-9396-4183-c7c8-23f08a4b050f" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
serial_number_idfile_namefile_sizerecording_lengthrecording_tsaccelerationPeakFullpsuedoVelocityPeakFullaccelerationRMSFullvelocityRMSFulldisplacementRMSFullpressureMeanFulltemperatureMeanFull
111145650_Joules_900_lbs-1629315312.ide159775020.2017522021-07-26 19:56:39231.2122907.6502.42354.5071.06698.74524.175
1011456100_Joules_900_lbs-1629315313.ide159671420.2006232021-07-26 19:21:55218.6342961.2562.87753.8751.05398.75124.180
711162Calibration-Shake-1632515140.IDE221813027.8826902021-05-17 19:16:108.7831142.2822.71246.3460.617102.25124.545
1711071surgical-instrument-1625829182.ide5419946.9511722021-04-22 16:53:105.739387.3121.56824.4180.24299.87921.889
1211046Drive-Home_07-1626805222.ide36225758634.7320562021-03-19 19:35:5723.805356.1280.0976.1170.135101.98828.832
511046Drive-Home_01-1632515142.ide363279961.7553712021-03-19 18:35:550.47940.1970.0211.0810.023100.28429.061
810916FUSE_HSTAB_000005-1632515139.ide53756218.4917912021-04-22 16:13:240.20253.3750.0111.5040.03690.70618.874
210118Bolted-1632515144.ide614922929.3961182021-04-21 21:44:0715.343148.2762.39814.1010.15499.65223.172
229695Tilt_000000-1625156721.IDE71940323.3551632021-07-01 16:21:010.378330.9460.04411.0420.34599.51026.410
209680LOC__6__DAQ41551_25_01-1625170793.IDE866423863.8789372021-03-25 04:53:27564.9662357.59954.408145.2233.088102.87526.031
199680LOC__4__DAQ41551_15_05-1625170794.IDE692795864.4860542021-03-25 04:22:10585.8632153.02046.528148.5912.615105.75032.202
189680LOC__3__DAQ41551_11_01_02-1625170795.IDE234329228.4568182021-03-25 04:06:19622.0408907.94994.197372.0499.580105.68233.452
219680LOC__2__DAQ38060_06_03_05-1625170793.IDE151917227.0576472021-03-25 02:54:22995.6705845.241131.087323.2873.144104.47325.616
\n", "
" ], "text/plain": [ " serial_number_id ... temperatureMeanFull\n", "11 11456 ... 24.175\n", "10 11456 ... 24.180\n", "7 11162 ... 24.545\n", "17 11071 ... 21.889\n", "12 11046 ... 28.832\n", "5 11046 ... 29.061\n", "8 10916 ... 18.874\n", "2 10118 ... 23.172\n", "22 9695 ... 26.410\n", "20 9680 ... 26.031\n", "19 9680 ... 32.202\n", "18 9680 ... 33.452\n", "21 9680 ... 25.616\n", "\n", "[13 rows x 12 columns]" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = df.recording_ts > pd.to_datetime('2021-01-01')\n", "df[mask].sort_values(by=['serial_number_id'], ascending=False) " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 361 }, "id": "ug9gKCKQhW7K", "outputId": "f8b91607-0276-4304-a49c-add75954c180" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \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", "
serial_number_idfile_namefile_sizerecording_lengthrecording_tsaccelerationPeakFullpsuedoVelocityPeakFullaccelerationRMSFullvelocityRMSFulldisplacementRMSFullpressureMeanFulltemperatureMeanFull
219680LOC__2__DAQ38060_06_03_05-1625170793.IDE151917227.0576472021-03-25 02:54:22995.6705845.241131.087323.2873.144104.47325.616
189680LOC__3__DAQ41551_11_01_02-1625170795.IDE234329228.4568182021-03-25 04:06:19622.0408907.94994.197372.0499.580105.68233.452
199680LOC__4__DAQ41551_15_05-1625170794.IDE692795864.4860542021-03-25 04:22:10585.8632153.02046.528148.5912.615105.75032.202
209680LOC__6__DAQ41551_25_01-1625170793.IDE866423863.8789372021-03-25 04:53:27564.9662357.59954.408145.2233.088102.87526.031
111145650_Joules_900_lbs-1629315312.ide159775020.2017522021-07-26 19:56:39231.2122907.6502.42354.5071.06698.74524.175
1011456100_Joules_900_lbs-1629315313.ide159671420.2006232021-07-26 19:21:55218.6342961.2562.87753.8751.05398.75124.180
\n", "
" ], "text/plain": [ " serial_number_id ... temperatureMeanFull\n", "21 9680 ... 25.616\n", "18 9680 ... 33.452\n", "19 9680 ... 32.202\n", "20 9680 ... 26.031\n", "11 11456 ... 24.175\n", "10 11456 ... 24.180\n", "\n", "[6 rows x 12 columns]" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = (df.recording_ts > pd.to_datetime('2021-01-01')) & (df.accelerationPeakFull > 100)\n", "df[mask].sort_values(by=['accelerationPeakFull'], ascending=False) " ] }, { "cell_type": "markdown", "metadata": { "id": "oi-4q68J9DsC" }, "source": [ "Another preview to plotly, but visualizing dataframes is made easy, even with mixed types. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "thTJcMe0kDWT", "outputId": "4458ed52-eda1-4d5b-a27d-84d38c0b77b6" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "px.scatter(df, \n", " x=\"recording_ts\", \n", " y=\"accelerationRMSFull\",\n", " size=\"recording_length\", \n", " color=\"serial_number_id\",\n", " hover_name=\"file_name\", \n", " log_y=True, \n", " size_max=60).show()" ] }, { "cell_type": "markdown", "metadata": { "id": "LUM38k4F9O3T" }, "source": [ "Plotly automatically made my colors a colorbar because I specified it based on a numeric value. If instead I change the type to string and replot, we'll see discrete series for each device." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "HmeGuksDd8PK", "outputId": "b895aea4-a574-4f33-e8fd-4ddd0056b7b7" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "df['device'] = df[\"serial_number_id\"].astype(str)\n", "\n", "px.scatter(df, \n", " x=\"recording_ts\", \n", " y=\"accelerationRMSFull\",\n", " size=\"recording_length\", \n", " color=\"device\",\n", " hover_name=\"file_name\", \n", " log_y=True, \n", " size_max=60).show()\n" ] }, { "cell_type": "markdown", "metadata": { "id": "8xY4Pn-Hcty-" }, "source": [ "## Preview of enDAQ Library" ] }, { "cell_type": "markdown", "metadata": { "id": "siJuja8UvDKF" }, "source": [ "### Installation\n", "\n", "The code is live on [GitHub](https://github.com/MideTechnology/endaq-python), [PyPI](https://pypi.org/project/endaq/), and cleaner documentation lives at [docs.endaq.com](https://docs.endaq.com/en/latest/).\n", "\n", "It can easily be installed with pip." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "qVKyNUT9cvKA", "outputId": "887fddf9-bab4-4953-adb9-1974705ca840" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", " Preparing wheel metadata ... \u001b[?25l\u001b[?25hdone\n", " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", " Preparing wheel metadata ... \u001b[?25l\u001b[?25hdone\n", " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", " Preparing wheel metadata ... \u001b[?25l\u001b[?25hdone\n", " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", " Preparing wheel metadata ... \u001b[?25l\u001b[?25hdone\n", "\u001b[K |████████████████████████████████| 92 kB 1.1 MB/s \n", "\u001b[K |████████████████████████████████| 81 kB 11.1 MB/s \n", "\u001b[?25h Building wheel for endaq (PEP 517) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for endaq-calc (PEP 517) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for endaq-cloud (PEP 517) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for endaq-ide (PEP 517) ... \u001b[?25l\u001b[?25hdone\n" ] } ], "source": [ "!pip install -q endaq" ] }, { "cell_type": "markdown", "metadata": { "id": "AOf2kh9nu6hY" }, "source": [ "We are using newer versions of some of these libraries so we need to update them then reset the runtime. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "l-GXnXDQun1O", "outputId": "19f6b56c-2e65-4ce6-ac10-dd353834be5e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[K |████████████████████████████████| 15.7 MB 417 kB/s \n", "\u001b[K |████████████████████████████████| 28.5 MB 1.3 MB/s \n", "\u001b[K |████████████████████████████████| 11.3 MB 28.8 MB/s \n", "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", "tensorflow 2.6.0 requires numpy~=1.19.2, but you have numpy 1.21.2 which is incompatible.\n", "google-colab 1.0.0 requires pandas~=1.1.0; python_version >= \"3.0\", but you have pandas 1.3.3 which is incompatible.\n", "datascience 0.10.6 requires folium==0.2.1, but you have folium 0.8.3 which is incompatible.\n", "albumentations 0.1.12 requires imgaug<0.2.7,>=0.2.5, but you have imgaug 0.2.9 which is incompatible.\u001b[0m\n", "\u001b[?25h" ] } ], "source": [ "! python -m pip install -U -q numpy scipy plotly pandas\n", "exit()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "xjTc9n3Ju5W1" }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import scipy\n", "\n", "import plotly.express as px\n", "import plotly.graph_objects as go\n", "\n", "import endaq\n" ] }, { "cell_type": "markdown", "metadata": { "id": "9MPMD_K3vsI5" }, "source": [ "### Access Data from IDE" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "2VB-DQoGv6dF" }, "outputs": [], "source": [ "def get_doc_and_table(file_location, display_table=True, display_recorder_info=True):\n", " \"\"\"Given an IDE file location, return the object and table summary\"\"\"\n", " dataset = endaq.ide.get_doc(file_location, quiet=True) \n", " table = endaq.ide.get_channel_table(dataset)\n", "\n", " if display_recorder_info:\n", " display(pd.Series(dataset.recorderInfo))\n", " if display_table:\n", " display(table)\n", "\n", " return dataset, table.data" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 557 }, "id": "fUxT9tPOwgSc", "outputId": "3376b560-7e5d-47fd-ba09-a6b9daf637b3" }, "outputs": [ { "data": { "text/plain": [ "CalibrationDate 1583263964\n", "CalibrationExpiry 1614799964\n", "CalibrationSerialNumber 1999\n", "RecorderTypeUID 1\n", "RecorderSerial 10118\n", "HwRev 1\n", "FwRev 5\n", "FwRevStr 2.0.36\n", "ProductName W8-E100D40\n", "PartNumber W8-E100D40\n", "DateOfManufacture 1588954110\n", "UniqueChipID 3782319969872279\n", "McuType EFM32GG11B820F2048GL120\n", "RecorderName Steve's Microphone\n", "dtype: object" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
 channelnametypeunitsstartenddurationsamplesrate
08.0X (100g)Accelerationg00:00.038100:29.066700:29.028658572819999.64 Hz
18.1Y (100g)Accelerationg00:00.038100:29.066700:29.028658572819999.64 Hz
28.2Z (100g)Accelerationg00:00.038100:29.066700:29.028658572819999.64 Hz
38.3MicAudioA00:00.038100:29.066700:29.028658572819999.64 Hz
480.0X (40g)Accelerationg00:00.038100:29.066900:29.02871177604020.81 Hz
580.1Y (40g)Accelerationg00:00.038100:29.066900:29.02871177604020.81 Hz
680.2Z (40g)Accelerationg00:00.038100:29.066900:29.02871177604020.81 Hz
736.0Pressure/Temperature:00PressurePa00:00.037500:29.077100:29.0396311.05 Hz
836.1Pressure/Temperature:01Temperature°C00:00.037500:29.077100:29.0396311.05 Hz
959.0Control Pad PressurePressurePa00:00.041400:29.048900:29.007529110.01 Hz
1059.1Control Pad TemperatureTemperature°C00:00.041400:29.048900:29.007529110.01 Hz
1159.2Relative HumidityRelative HumidityRH00:00.041400:29.048900:29.007529110.01 Hz
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[doc,table] = get_doc_and_table('https://info.endaq.com/hubfs/data/Bolted.ide')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "0FOIW0ri3Sqd" }, "outputs": [], "source": [ "def get_primary_accel(dataset, table, preferred_ch=None, time_mode='seconds'):\n", " \"\"\"Given an IDE object and summary table, return a dataframe of the accelerometer with the most samples unless a channel number is explicitely defined.\"\"\"\n", " if preferred_ch:\n", " df = endaq.ide.to_pandas(doc.channels[preferred_ch],time_mode=time_mode)\n", " else:\n", " table = table.sort_values('samples',ascending=False)\n", " channel = table.loc[table.type=='Acceleration','channel'].iloc[0].parent \n", " df = endaq.ide.to_pandas(channel,time_mode=time_mode)\n", "\n", " return df.drop([\"Mic\"], axis=1, errors=\"ignore\") " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "XuU2PbWuwjlH", "outputId": "280b68bf-bcaf-449e-d5db-f0f9e86c0f99" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \n", "
X (100g)Y (100g)Z (100g)
timestamp
0.381011-1.268025-2.824445-2.262913
0.381061-1.292439-1.902800-4.362554
0.381111-1.438926-1.164263-4.740978
0.381161-1.274128-0.914015-3.483635
0.381211-0.730907-1.115435-1.927214
............
29.667738-1.036088-1.817350-2.207980
29.667788-1.383993-1.585412-2.092012
29.667838-1.438926-1.933318-1.512169
29.667888-1.219196-2.738994-0.682078
29.667938-0.865187-3.5263600.111391
\n", "

585728 rows × 3 columns

\n", "
" ], "text/plain": [ " X (100g) Y (100g) Z (100g)\n", "timestamp \n", "0.381011 -1.268025 -2.824445 -2.262913\n", "0.381061 -1.292439 -1.902800 -4.362554\n", "0.381111 -1.438926 -1.164263 -4.740978\n", "0.381161 -1.274128 -0.914015 -3.483635\n", "0.381211 -0.730907 -1.115435 -1.927214\n", "... ... ... ...\n", "29.667738 -1.036088 -1.817350 -2.207980\n", "29.667788 -1.383993 -1.585412 -2.092012\n", "29.667838 -1.438926 -1.933318 -1.512169\n", "29.667888 -1.219196 -2.738994 -0.682078\n", "29.667938 -0.865187 -3.526360 0.111391\n", "\n", "[585728 rows x 3 columns]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = get_primary_accel(doc, table)\n", "df" ] }, { "cell_type": "markdown", "metadata": { "id": "f8vaIGA03g8C" }, "source": [ "### PSD\n", "We created a wrapper to [SciPy's Welch's method](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html) to make the interface rely on dataframes." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "v8WTbmq5xGpC", "outputId": "6f29f012-a331-48c3-8eb7-2555506d89cb" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "df_psd = endaq.calc.psd.welch(df, bin_width=10)\n", "\n", "fig = px.line(df_psd)\n", "\n", "fig.update_layout(\n", " title='Power Spectral Density',\n", " xaxis_title=\"Frequency (Hz)\",\n", " yaxis_title=\"Acceleration (g^2/Hz)\",\n", " xaxis_type=\"log\",\n", " yaxis_type=\"log\",\n", " legend=dict(\n", " orientation=\"h\",\n", " yanchor=\"top\",\n", " y=-.2,\n", " xanchor=\"right\",\n", " x=1\n", " )\n", " )\n", "\n", "fig.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "thYYU5yLAXfI" }, "source": [ "We also provide a function to convert a linear spaced PSD to an octave spaced one." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "97aXuVQPxiqZ", "outputId": "240ada0e-881a-4b80-8667-eb7c04d1ad06" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "df_psd_oct = endaq.calc.psd.to_octave(df_psd, fstart=4, octave_bins=3)\n", "\n", "fig = px.line(df_psd_oct)\n", "\n", "fig.update_layout(\n", " title='Octave Spaced Power Spectral Density',\n", " xaxis_title=\"Frequency (Hz)\",\n", " yaxis_title=\"Acceleration (g^2/Hz)\",\n", " xaxis_type=\"log\",\n", " yaxis_type=\"log\",\n", " legend=dict(\n", " orientation=\"h\",\n", " yanchor=\"top\",\n", " y=-.2,\n", " xanchor=\"right\",\n", " x=1\n", " )\n", " )\n", "\n", "fig.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "Cfk4pIK95Lbe" }, "source": [ "### Shock\n", "\n", "For some shock data, we'll use the motorcycle crash test data discussed in our [blog post on pseudo velocity](https://blog.endaq.com/shock-analysis-response-spectrum-srs-pseudo-velocity-severity)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 455 }, "id": "0z3-ayAh4ytz", "outputId": "514155ec-8fe9-41d6-c996-c2f08cad9ab4" }, "outputs": [ { "data": { "text/html": [ "
\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", " \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", " \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", " \n", "
X (500g)Y (500g)Z (500g)
timestamp
1024.0886530.563998-5.1425590.575260
1024.0887530.409059-5.0457410.847571
1024.0888530.389691-5.1425591.275489
1024.0889530.234751-5.0070140.905924
1024.0890530.196017-5.1812870.633613
............
1152.0982320.796408-5.4523790.497457
1152.0983320.835142-5.6072880.205695
1152.0984321.048184-5.5491970.205695
1152.0985321.145022-5.9364710.478006
1152.0986321.280594-5.8590160.419654
\n", "

1280087 rows × 3 columns

\n", "
" ], "text/plain": [ " X (500g) Y (500g) Z (500g)\n", "timestamp \n", "1024.088653 0.563998 -5.142559 0.575260\n", "1024.088753 0.409059 -5.045741 0.847571\n", "1024.088853 0.389691 -5.142559 1.275489\n", "1024.088953 0.234751 -5.007014 0.905924\n", "1024.089053 0.196017 -5.181287 0.633613\n", "... ... ... ...\n", "1152.098232 0.796408 -5.452379 0.497457\n", "1152.098332 0.835142 -5.607288 0.205695\n", "1152.098432 1.048184 -5.549197 0.205695\n", "1152.098532 1.145022 -5.936471 0.478006\n", "1152.098632 1.280594 -5.859016 0.419654\n", "\n", "[1280087 rows x 3 columns]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[doc,table] = get_doc_and_table('https://info.endaq.com/hubfs/data/Motorcycle-Car-Crash.ide',\n", " display_recorder_info=False,\n", " display_table=False)\n", "df = get_primary_accel(doc, table)\n", "df" ] }, { "cell_type": "markdown", "metadata": { "id": "32e_tLm65_Ge" }, "source": [ "#### Get Peak Time & Plot" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nKt1eQtW5hyI" }, "outputs": [], "source": [ "def get_peak_time(df,num):\n", " \"\"\"Get a subset of a dataframe around the peak value\"\"\"\n", " peak_time = df.abs().max(axis=1).idxmax() #get the time at which the peak value occurs\n", " d_t = (df.index[-1]-df.index[0])/(len(df.index)-1) #find the average time step\n", " fs = 1/d_t #find the sampling rate\n", "\n", " num = num / 2 #total number of datapoints to plot (divide by 2 because it will be two sided)\n", " df_peak = df[peak_time - num / fs : peak_time + num / fs ] #segment the dataframe to be around that peak value\n", " \n", " return df_peak" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "DryEV6a96bCI", "outputId": "264ce923-11d8-4003-d074-2a3806e1320b" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "df_peak = get_peak_time(df,5000)\n", "\n", "fig = px.line(df_peak)\n", "fig.update_layout(\n", " title=\"Time History around Peak\",\n", " xaxis_title=\"Time (s)\",\n", " yaxis_title=\"Acceleration (g)\",\n", " template=\"plotly_white\",\n", " legend=dict(\n", " orientation=\"h\",\n", " yanchor=\"top\",\n", " y=-.2,\n", " xanchor=\"right\",\n", " x=1\n", " )\n", ")\n", "fig.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "TTkXtcr868k5" }, "source": [ "#### Filter & Plot" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "TtSWPMFX6ihW", "outputId": "e7a56508-5a1f-4af4-de12-27798d030491" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "df_filtered = df - df.median() #For shock events it's sometimes best to avoid high pass filters because they can introduce weird artifacts\n", "df_filtered = endaq.calc.filters.butterworth(df_filtered, low_cutoff=None, high_cutoff=150, half_order=3)\n", "df_peak_filtered = get_peak_time(df_filtered,5000)\n", "\n", "fig = px.line(df_peak_filtered)\n", "fig.update_layout(\n", " title=\"Filtered Time History around Peak\",\n", " xaxis_title=\"Time (s)\",\n", " yaxis_title=\"Acceleration (g)\",\n", " template=\"plotly_white\",\n", " legend=dict(\n", " orientation=\"h\",\n", " yanchor=\"top\",\n", " y=-.2,\n", " xanchor=\"right\",\n", " x=1\n", " )\n", ")\n", "fig.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "A7eyZ2sx803f" }, "source": [ "#### Integrate to Velocity & Displacement" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "tAVITAR07FJy" }, "outputs": [], "source": [ "[df_accel, df_vel, df_disp] = endaq.calc.integrate.integrals(df_peak_filtered, n=2, highpass_cutoff=None, tukey_percent=0)\n", "\n", "df_vel = df_vel-df_vel.iloc[0] #forced the starting velocity to 0\n", "df_vel = df_vel*9.81*39.37 #convert to in/s" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "6NQrZXmJ87TE", "outputId": "630f45ed-723c-446a-fdf9-ce949e54766e" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig = px.line(df_vel)\n", "fig.update_layout(\n", " title=\"Integrated Velocity Time History\",\n", " xaxis_title=\"Time (s)\",\n", " yaxis_title=\"Velocity (in/s)\",\n", " template=\"plotly_dark\",\n", " legend=dict(\n", " orientation=\"h\",\n", " yanchor=\"top\",\n", " y=-.2,\n", " xanchor=\"right\",\n", " x=1\n", " )\n", ")\n", "fig.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "cPeJwY65nZUr" }, "source": [ "#### Shock Response Spectrum" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "TaZLW-jS9bsg" }, "outputs": [], "source": [ "freqs=2 ** np.arange(0, 12, 1/12)\n", "\n", "pvss = endaq.calc.shock.pseudo_velocity(df_peak-df_peak.median(), freqs=freqs, damp=0.05)\n", "pvss = pvss*9.81*39.37 #convert to in/s" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "N_Ggrhq_nKFc", "outputId": "c2623b81-0edb-4576-9ca5-65645109cc3a" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig = px.line(pvss)\n", "fig.update_layout(\n", " title='Psuedo Velocity Shock Spectrum (PVSS)',\n", " xaxis_title=\"Natural Frequency (Hz)\",\n", " yaxis_title=\"Psuedo Velocity (in/s)\",\n", " template=\"plotly_dark\",\n", " xaxis_type=\"log\",\n", " yaxis_type=\"log\",\n", " legend=dict(\n", " orientation=\"h\",\n", " yanchor=\"top\",\n", " y=-.2,\n", " xanchor=\"right\",\n", " x=1\n", " )\n", " )" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "Webinar-Introduction-NumPy-and-Pandas.ipynb", "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" } }, "nbformat": 4, "nbformat_minor": 4 }