gpu-jupyter/extra/Getting_Started/ElasticsearchConnection.ipynb

1244 lines
80 KiB
Plaintext
Executable File

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Elasticsearch Data Analytics\n",
"\n",
"This notebook provides sample code to fetch Elasticsearch Data into and analyze it."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<img src=\"https://www.antaresnet.com/wp-content/uploads/2018/07/Elasticsearch-Logo-Color-V.png\"/>"
],
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import Image\n",
"from IPython.core.display import HTML \n",
"Image(url= \"https://www.antaresnet.com/wp-content/uploads/2018/07/Elasticsearch-Logo-Color-V.png\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Loading modules and connect to the Elastic Stack"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"pycharm": {
"is_executing": false
}
},
"outputs": [],
"source": [
"import sys\n",
"import json\n",
"import requests\n",
"import numpy as np\n",
"import pandas as pd\n",
"import seaborn as sns\n",
"import pytz\n",
"from datetime import datetime, timedelta\n",
"from dateutil import tz\n",
"\n",
"import matplotlib.pyplot as plt\n",
"sns.set(style=\"darkgrid\")\n",
"plt.rcParams[\"figure.figsize\"] = (18,10)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"pycharm": {
"is_executing": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Last run: 2019-12-20 08:12:58.766840 UTC, status: 7.0604683677036 %\n"
]
},
{
"data": {
"text/plain": [
"{'name': 'c185f3ed577c',\n",
" 'cluster_name': 'il.es.cluster',\n",
" 'cluster_uuid': 'sBgbgyRXTvKta2cEJCczKQ',\n",
" 'version': {'number': '6.2.2',\n",
" 'build_hash': '10b1edd',\n",
" 'build_date': '2018-02-16T19:01:30.685723Z',\n",
" 'build_snapshot': False,\n",
" 'lucene_version': '7.2.1',\n",
" 'minimum_wire_compatibility_version': '5.6.0',\n",
" 'minimum_index_compatibility_version': '5.0.0'},\n",
" 'tagline': 'You Know, for Search'}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# connect to our cluster\n",
"from elasticsearch import Elasticsearch\n",
"es = Elasticsearch([{'host': 'elasticsearch', 'port': 9200}])\n",
"print('Last run: {} UTC, status: {} %'.format(\n",
" datetime.utcnow(),\n",
" es.cluster.health()['active_shards_percent_as_number']))\n",
"es.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Display our indices and document types saved in elasticsearch.\n",
"\n",
"Update the elasticsearch package."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: elasticsearch<8.0.0,>=7.0.0 in /opt/conda/lib/python3.7/site-packages (7.1.0)\n",
"Requirement already satisfied: urllib3>=1.21.1 in /opt/conda/lib/python3.7/site-packages (from elasticsearch<8.0.0,>=7.0.0) (1.25.7)\n"
]
}
],
"source": [
"!sudo pip install \"elasticsearch>=7.0.0,<8.0.0\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Defining useful functions"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# %load scroller.py\n",
"def scroller(index, quantity, timerange=timedelta(days=0), startdt=\"\", enddt=\"\"):\n",
" print(\"Starting to scroll\", end='')\n",
" # Retrieve the datetimes, note that timerange has a higher priority\n",
" if timerange.total_seconds() > 0:\n",
" now = datetime.utcnow().replace(tzinfo=pytz.UTC)\n",
" startdt = (now - timerange).isoformat()\n",
" enddt = now.isoformat()\n",
" \n",
" # search the first page and write the result to data\n",
" response = es.search(\n",
" index=index,\n",
" body={\n",
" \"query\": {\n",
" \"bool\": {\n",
" \"must\": [\n",
" {\"range\" : {\n",
" \"phenomenonTime\" : {\n",
" #\"gte\": \"2018-02-20T09:08:34.230693+00:00\", \n",
" \"gte\": startdt,\n",
" \"lte\": enddt, \n",
" \"time_zone\": \"+01:00\"\n",
" }\n",
" }},\n",
" {\n",
" \"match_phrase\": {\n",
" \"Datastream.name.keyword\": quantity\n",
" }\n",
" }\n",
" ]\n",
" }\n",
" }\n",
" },\n",
" scroll='10m'\n",
" )\n",
" data = [[row[\"_source\"][\"phenomenonTime\"], row[\"_source\"][\"result\"]] for row in response['hits']['hits']]\n",
"\n",
" # Append new pages until there aren't any left\n",
" while len(response['hits']['hits']):\n",
" print(\".\", end='')\n",
" # process results\n",
" # print([item[\"_id\"] for item in response[\"hits\"][\"hits\"]])\n",
" response = es.scroll(scroll_id=response['_scroll_id'], scroll='10m')\n",
" data += [[row[\"_source\"][\"phenomenonTime\"], row[\"_source\"][\"result\"]] for row in response['hits']['hits']]\n",
" \n",
" # Convert data to a DataFrame and return it\n",
" df = pd.DataFrame(data, columns=[\"phenomenonTime\", quantity])\n",
" # df.index = pd.to_datetime(df[\"phenomenonTime\"].map(lambda t: t.split(\".\")[0]), utc=True)\n",
" df.index = pd.to_datetime(df[\"phenomenonTime\"].map(lambda t: roundto(t, 1)), utc=True)\n",
" df = df.drop([\"phenomenonTime\"], axis=1)\n",
" print(\"\\nFetched {} tuples.\".format(df.shape[0]))\n",
" return df\n",
"\n",
"def roundto(string, n):\n",
" base = string.split(\".\")[0]\n",
" if n > 0:\n",
" base += \".\" + string.split(\".\")[1][:n]\n",
" return base\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Gather data from Elasticsearch\n",
"\n",
"It is supposed that in the Elasticsearch instance, there is data with the Datastream.name \"at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature\" within the index name \"at.srfg.iot-iot4cps-wp5.infraprov.internal-*\""
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting to scroll\n",
"Fetched 0 tuples.\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature</th>\n",
" </tr>\n",
" <tr>\n",
" <th>phenomenonTime</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
"Empty DataFrame\n",
"Columns: [at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature]\n",
"Index: []"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Get data for an index and a quantity between two static timestamps\n",
"startdt=\"2019-08-07T08:58:34+00:00\"\n",
"enddt=\"2019-08-07T11:58:34+00:00\"\n",
"df = scroller(\"at.srfg.iot-iot4cps-wp5.infraprov.internal-*\",\n",
" \"at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature\",\n",
" startdt=startdt, enddt=enddt)\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting to scroll\n",
"Fetched 0 tuples.\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature</th>\n",
" </tr>\n",
" <tr>\n",
" <th>phenomenonTime</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
"Empty DataFrame\n",
"Columns: [at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature]\n",
"Index: []"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Get data for an index and a quantity of the latest timerange\n",
"df = scroller(\"at.srfg.iot-iot4cps-wp5.infraprov.internal-*\",\n",
" \"at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature\",\n",
" timerange=timedelta(days=10))\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABBoAAAI5CAYAAAAPNFn9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3Xd8VFXCxvFnSnojJAQCIQQSSoBAqIIUwYZSBAQBUSyoqyIiirtrW9vaO4KgoggCIr1XcREUUOmdhNAhAZIQSC+TmfcPJC9IDwl3kvl9/wKSOfeZOZN8uM/cc4/J4XA4BAAAAAAAUALMRgcAAAAAAADlB0UDAAAAAAAoMRQNAAAAAACgxFA0AAAAAACAEkPRAAAAAAAASgxFAwAAAAAAKDEUDQAAAAAAoMRQNAAAAAAAgBJD0QAAAAAAAEoMRQMAAAAAACgxFA0AAAAAAKDEUDQAAAAAAIASYzU6wBlpaVmy2x1Gx8BFBAX5KjU10+gYMAjz79qYf/AecG3Mv2tj/l0b8+/agoJ8lZaWpcBAn6t+rNMUDXa7g6LByTE/ro35d23MP3gPuDbm37Ux/66N+XdtxZ1/lk4AAAAAAIASQ9EAAAAAAABKDEUDAAAAAAAoMU5zjwYAAADg7woLbUpLS5bNlm90FJd0/LhZdrvd6BgwCPPvWqxWdwUGVpLFcu01AUUDAAAAnFZaWrI8Pb3l41NFJpPJ6Dgux2o1y2bjRNNVMf+uw+FwKCsrXWlpyQoODr3m8Vg6AQAAAKdls+XLx8efkgEASpHJZJKPj3+JXT1WYkXDvn371LdvX3Xq1El9+/bV/v37S2poAAAAuDBKBgAofSX5u7bEiobXXntN/fv315IlS9S/f3+9+uqrJTU0AAAAAAAoI0qkaEhNTdWOHTvUtWtXSVLXrl21Y8cOnThxoiSGBwAAAAAAZUSJ3AwyKSlJlStXlsVikSRZLBaFhIQoKSlJFStWLIlDAAAAAE4rKSlRf/75u7p3v/uax5o1a7qmTZssDw8PffHFGHl7+1zR495777+6886uaty4ySW/b+rUH3TbbXcoMPDC/0+fPXu68vLy1LfvfZccZ+XKXxQcHKz69Rue8+8Oh0NDhw5SQkK8Fiz4+YqyG2nDhnX65z+fUfXqNSRJbm5uGjNm/AW/12azady4b7Rs2VJZrVY5HHa1atVGTz75tKzWqzu16t27m9zd3eXu7iFJatq0mYYMGaa3335d9epFq1evvsV+TgsXzlPDho0UHn76OSUnH9ebb/5H8fG7FBYWrm+/nVDssa/FyJGfacWK/ykpKVHff/+jatWKuuT3/+c/L2jjxvWaPXtR0eubkpKsN954RSNGfHXZ47344vNKSkqUJCUkxCsyMkomk1kVK1bUJ5+MvPYndJ0cOXJYGzasU7duPYyOcsWcZteJoCBfoyPgMipV8jM6AgzE/Ls25h+8B1ybkfN//LhZVqvz37/8+PGjmjdvlnr16l3sMWw2m6xWq6ZP/1Gvv/6W6tdvcFWPf+WV167o+6ZOnawbbmilSpWCL/j13r37nPP3i73+v/22QtHR0WrUqNHfxv9RVatWVULC7jIxdxaLWTVr1tK4cZMu+73//e8bysvL0/jxk+Tj4yObrUDz58+V3W6T1ep+Rcc7M8+S9O67Hyoy8tyTbZPJJLPZdE2v3aJF81WxYqBq1aopSfLz89E//vGEsrKy9M03X13V2Feb4+zn93cdOnTUvff21+OPPyKL5dI/26dOndL69X8qPLyG1qz5VR073iJJqlKlskaPHnNFx/7ww0+K/tyqVVONGTNO3t7eV/V8rodLvWaSdOxYkubPn62ePa++yLzc2H9nNpvP+Z1f3PP0EikaQkNDdezYMRUWFspisaiwsFDHjx9XaOiVb4uRmpopu91REnFQCipV8lNycobRMWAQ5t+1Mf/gPeDajJ5/u91etL3eqq1J+m1LUqkcp22jULWJufz/Xd944xUdPHhABQX5qlatul588VX5+/vrww/fU1LSEd1/fz+FhYXprbc+OOdxv/76i8aMGS2z2aLCQpueffZfatq0uQYP/odiYhprx45tcnd3l5eXt44cOazXX39FdetG67XX3tKMGVM0bdqP8vX1U+vWbTRz5tQLXikwePA/dO+9A9SmTTudOJGqDz98V4mJh+VwOHTvvQN0551dNX78t0pJSdaLL/5T7u4eeu21t1SzZq1zxvn226+Uk5OjwYOHymRyaMSI4frjj9WSpBtuuFFPPvm01q37U7/+ukJr1/6hOXNmq2/f/rrzzq46dOigfvppsV566XWtXPnLOVsjzp8/R9Om/Sjp9FUDH3zwqfLy8vToowN0553dtHnzBuXl5WnYsBfUuHETpaWd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RQMAAAAAAPAaigYAAAAAAOA1FA0AAAAAAMBrKBoAAAAAAIDXUDQAAAAAAACvoWgAAAAAAABeQ9EAAAAAAAC8hqIBAAAAAAB4DUUDAAAAAADwGooGAAAAAADgNRQNAAAAAADAaygaAAAAAACA11iNDvANs9lkdAScBWPk3xh//8b4g+uAf2P8/Rvj798Yf/92oeNv8ng8Hi9nAQAAAAAAfopDJwAAAAAAgNdQNAAAAAAAAK+haAAAAAAAAF5D0QAAAAAAALyGogEAAAAAAHgNRQMAAAAAAPAaigYAAAAAAOA1FA0AAAAAAMBrKBoAAAAAAIDXUDQAAAAAAACvoWjAKcePH1d9fb0kye12G5wGzW379u1yuVxGx4BBVq1apYqKCqNjwCBTp07V3r17jY4Bg7z88stavHixJMnj8RicBs1t586dp+7/GX//U1ZWdmrcef7vf0pKSk6Nu7fH3+rVS0OrtHDhQk2ZMkXJycmqqKjQP//5T5nNdFD+wuVyadKkSdqxY4deeOEFdevWzehIaEZffPGFXnnlFV100UXKzMw0Og6a2VdffaVnn31WycnJ6tu3r9Fx0My++OILvfXWW9q8ebOGDBmiSy65RCaTyehYaCYLFy7UK6+8oqioKJWWluq9995TQECA0bHQTD7//HNNmTJF7du3l9Vq1bPPPsvzfz+ycOFC/eUvf1F6errMZrP++te/en38KRr8WG1trf785z9r586devDBBzVw4ED17dtXn332ma644gqj46GZeDwelZWVKSoqSqtWrVJiYqLCwsLk8Xh4wunDampq9Nvf/lY7duzQo48+qoEDBxodCc2srq5OM2fO1P3336+hQ4caHQfNxOPxqLy8XPfdd5+sVqvuuece5efna/v27XI6nbJardz3+4F169bptdde00MPPaSBAwdq6NChWrhwoUaOHGl0NDSDjRs36p///KceeeQR5eTkaMKECfrNb36jX/3qV4qIiDA6HprYwYMHNWXKFP3v//6vLrroIt1555165plnNHHiRLVp08Zrf4fayo8FBgZq2LBhevfddzVw4EAVFxerZ8+eSkxMNDoamonT6ZTH41FOTo5uu+02LVy4ULt375Yknmj6OIfDobq6Og0dOlQDBw5UfX29vvzySx0/fvzUFEqm0Pq2goIC1dfXa8iQISovL9ebb76pRYsWqbS01Oho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AGw2m/skhwDBwcFUVVVx/fp1qqurmTBhgvs+l8vlPuzgfo8tLS2ltrYWu91eb5tXrlxxf962bVv3x0FBQQC0a9fOfVuLFi2orKx85Ov4320GBwcDUFVVde+P76Hcu/57P787wCgsLGTXrl1s2bLFfX9tba27iYiIiDyYBg0iIiJeVlRU5P64sLCQ9u3b3/frQ0NDCQoKYt++fXTo0OFfbSs0NBSr1UphYSHdu3d3b//ffp/HaR2e1LFjR2bOnMmsWbO8ug4RERFfpkMnREREvGzbtm1cvnyZGzduuE8MeT/+/v4kJyezdOlSrl27BsCVK1c4duzYA7dlsVhISEhg1apVVFRUcOnSJb744osGXQ7ycVmHJyUnJ/Pll19y4sQJXC4XVVVV5ObmUlFR4dV1iYiI+BINGkRERLxs7NixTJ8+nfj4eMLCwh7q3fT58+fTtWtXJk2axIABA3jppZdwOBwPtb3FixcTHBxMfHw8L774ImPHjiUpKalBa39c1uEpffv2JT09nbS0NCIjIxk5ciTZ2dleXZOIiIiv8XM19PTNIiIi0mhxcXFkZGQQHR3t7aWIiIiIeIT2aBARERERERERj9GgQUREREREREQ8RodOiIiIiIiIiIjHaI8GEREREREREfEYDRpERERERERExGM0aBARERERERERj9GgQUREREREREQ8RoMGEREREREREfGY/wBn3ii98n45MwAAAABJRU5ErkJggg==\n",
"text/plain": [
"<Figure size 1296x720 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Plot the extracted data using pandas and seaborn\n",
"df.plot()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [],
"source": [
"# Get multiple quantities and (outer) join them to a single DataFrame.\n",
"# There can be a lot of missing values\n",
"used_quantities = [\"at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature\", \n",
" \"at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature\"]"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting to scroll.....\n",
"Fetched 48 tuples.\n",
"at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature\n",
"Starting to scroll\n",
"Fetched 0 tuples.\n"
]
}
],
"source": [
"df = scroller(\"at.srfg.iot-iot4cps-wp5.infraprov.internal-*\",\n",
" used_quantities[0],\n",
" timerange=timedelta(days=10))\n",
"for q in used_quantities[1:]:\n",
" print(q)\n",
" df = df.join(scroller(\"at.srfg.iot-iot4cps-wp5.infraprov.internal-*\", q,\n",
" timerange=timedelta(days=10)),\n",
" how=\"outer\")"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature</th>\n",
" <th>at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature</th>\n",
" </tr>\n",
" <tr>\n",
" <th>phenomenonTime</th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2019-08-07 09:32:50.600000+00:00</th>\n",
" <td>-1.396915</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:33:40.700000+00:00</th>\n",
" <td>-2.559881</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:34:10.700000+00:00</th>\n",
" <td>-3.360251</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:32:10.600000+00:00</th>\n",
" <td>-0.112741</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:32:40.600000+00:00</th>\n",
" <td>-0.956904</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature \\\n",
"phenomenonTime \n",
"2019-08-07 09:32:50.600000+00:00 -1.396915 \n",
"2019-08-07 09:33:40.700000+00:00 -2.559881 \n",
"2019-08-07 09:34:10.700000+00:00 -3.360251 \n",
"2019-08-07 09:32:10.600000+00:00 -0.112741 \n",
"2019-08-07 09:32:40.600000+00:00 -0.956904 \n",
"\n",
" at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature \n",
"phenomenonTime \n",
"2019-08-07 09:32:50.600000+00:00 NaN \n",
"2019-08-07 09:33:40.700000+00:00 NaN \n",
"2019-08-07 09:34:10.700000+00:00 NaN \n",
"2019-08-07 09:32:10.600000+00:00 NaN \n",
"2019-08-07 09:32:40.600000+00:00 NaN "
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Store and retrieve the DataFrame in a csv"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [],
"source": [
"df.to_csv(\"elasticsearchdata.csv\")"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature</th>\n",
" <th>at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature</th>\n",
" </tr>\n",
" <tr>\n",
" <th>phenomenonTime</th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2019-08-07 09:39:01.100000+00:00</th>\n",
" <td>-3.039692</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:39:51.100000+00:00</th>\n",
" <td>-1.599475</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:39:21.100000+00:00</th>\n",
" <td>-2.488179</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:39:31.100000+00:00</th>\n",
" <td>-2.259640</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:40:01.200000+00:00</th>\n",
" <td>-1.246612</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature \\\n",
"phenomenonTime \n",
"2019-08-07 09:39:01.100000+00:00 -3.039692 \n",
"2019-08-07 09:39:51.100000+00:00 -1.599475 \n",
"2019-08-07 09:39:21.100000+00:00 -2.488179 \n",
"2019-08-07 09:39:31.100000+00:00 -2.259640 \n",
"2019-08-07 09:40:01.200000+00:00 -1.246612 \n",
"\n",
" at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature \n",
"phenomenonTime \n",
"2019-08-07 09:39:01.100000+00:00 NaN \n",
"2019-08-07 09:39:51.100000+00:00 NaN \n",
"2019-08-07 09:39:21.100000+00:00 NaN \n",
"2019-08-07 09:39:31.100000+00:00 NaN \n",
"2019-08-07 09:40:01.200000+00:00 NaN "
]
},
"execution_count": 56,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.read_csv(\"elasticsearchdata.csv\", parse_dates=True, index_col='phenomenonTime')\n",
"df.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Pre-processing"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reduce size and interpolate"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_csv(\"elasticsearchdata.csv\", parse_dates=True, index_col='phenomenonTime')"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [],
"source": [
"df.index.names = [\"time\"]"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [],
"source": [
"col_mapping = {\"at.srfg.iot-iot4cps-wp5.CarFleet1.car_1.Air Temperature\": \"car1_temp\", \n",
" \"at.srfg.iot-iot4cps-wp5.CarFleet2.car_2.Air Temperature\": \"car2_temp\"}\n",
"df = df.rename(index=str, \n",
" columns=col_mapping)"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>car1_temp</th>\n",
" <th>car2_temp</th>\n",
" </tr>\n",
" <tr>\n",
" <th>time</th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2019-08-07 09:32:50.600000+00:00</th>\n",
" <td>-1.396915</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:33:40.700000+00:00</th>\n",
" <td>-2.559881</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:34:10.700000+00:00</th>\n",
" <td>-3.360251</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:32:10.600000+00:00</th>\n",
" <td>-0.112741</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2019-08-07 09:32:40.600000+00:00</th>\n",
" <td>-0.956904</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" car1_temp car2_temp\n",
"time \n",
"2019-08-07 09:32:50.600000+00:00 -1.396915 NaN\n",
"2019-08-07 09:33:40.700000+00:00 -2.559881 NaN\n",
"2019-08-07 09:34:10.700000+00:00 -3.360251 NaN\n",
"2019-08-07 09:32:10.600000+00:00 -0.112741 NaN\n",
"2019-08-07 09:32:40.600000+00:00 -0.956904 NaN"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [],
"source": [
"# Interpolate forwards and backwaonly up to \n",
"df = df.interpolate(method ='linear', limit_direction ='both', limit=10)\n",
"df = df.interpolate(method ='linear', limit_direction ='both', limit=10)"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [],
"source": [
"# Keep only the rows with at least 2 non-NA values.\n",
"df = df.dropna(thresh=2)"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [],
"source": [
"# make Timestamp unique\n",
"df = df.reset_index()\n",
"df = df.groupby(\"time\").agg({q: \"mean\" for q in col_mapping.values()})"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [],
"source": [
"# Interpolate again to close gaps, use the smalles value \n",
"df = df.interpolate(method ='zero', limit_direction ='forward')\n",
"df = df.interpolate(method ='zero', limit_direction ='forward')"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"nan"
]
},
"execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.index.min()"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0, 2)"
]
},
"execution_count": 66,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.shape"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"car1_temp 0\n",
"car2_temp 0\n",
"dtype: int64"
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.isna().sum()"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>car1_temp</th>\n",
" <th>car2_temp</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" car1_temp car2_temp\n",
"count 0.0 0.0\n",
"mean NaN NaN\n",
"std NaN NaN\n",
"min NaN NaN\n",
"25% NaN NaN\n",
"50% NaN NaN\n",
"75% NaN NaN\n",
"max NaN NaN"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.describe()"
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {},
"outputs": [],
"source": [
"# Keep only rows with all filled rows\n",
"df = df.dropna()"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {},
"outputs": [],
"source": [
"df.to_csv(\"elasticsearchdata.csv\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Basic Data Analysis"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>car1_temp</th>\n",
" <th>car2_temp</th>\n",
" </tr>\n",
" <tr>\n",
" <th>time</th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
"Empty DataFrame\n",
"Columns: [car1_temp, car2_temp]\n",
"Index: []"
]
},
"execution_count": 71,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.read_csv(\"elasticsearchdata.csv\", parse_dates=True, index_col='time')\n",
"df.tail()"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {},
"outputs": [],
"source": [
"# df.hist()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {},
"outputs": [],
"source": [
"# pd.plotting.scatter_matrix(df, alpha=0.2)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 74,
"metadata": {},
"outputs": [],
"source": [
"# corr = df.corr() \n",
"cm = sns.light_palette(\"orange\", as_cmap=True) \n",
"cm = sns.diverging_palette(220, 20, sep=20, as_cmap=True) \n",
"# corr.style.background_gradient(cmap=cm).set_precision(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Feature Engineering\n",
"\n",
"This task is very domain-specific and must be done by an expert."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Data Analytics"
]
},
{
"cell_type": "code",
"execution_count": 77,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "75cfeeea6204489e945d894895b2bc30",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(IntSlider(value=45, description='pitch', max=90), IntSlider(value=45, description='yaw',…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from IPython.html.widgets import *\n",
"from mpl_toolkits.mplot3d import Axes3D\n",
"\n",
"plt.rcParams[\"figure.figsize\"] = (18,10)\n",
"sns.set(style=\"darkgrid\")\n",
"\n",
"def plot3D(pitch, yaw):\n",
" fig = plt.figure()\n",
" ax = fig.add_subplot(111, projection='3d')\n",
" plot = ax.scatter(df['car1_temp'], df['car1_temp'], df['car2_temp'], c=df[\"car1_temp\"], s=60)\n",
" fig.colorbar(plot)\n",
" ax.view_init(pitch, yaw)\n",
" ax.legend(['Vibration for each 3D position'])\n",
" ax.set_xlabel(\"x-Position\")\n",
" ax.set_ylabel(\"y-Position\")\n",
" ax.set_zlabel(\"z-Position\")\n",
"interact(plot3D, pitch=(0,90,1), yaw=(0,90,1))\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 78,
"metadata": {},
"outputs": [],
"source": [
"# df[col_mapping.values()].hist()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 79,
"metadata": {},
"outputs": [],
"source": [
"# pd.plotting.scatter_matrix(df[[\"vib\", \"distance\", \"projection\", \"v-radial\", \"v-tang\"]], alpha=0.5)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 80,
"metadata": {},
"outputs": [],
"source": [
"# bins = np.linspace(0, df['v-radial'].max(), 10)\n",
"# df[\"binned-v-radial\"] = pd.cut(df['v-radial'], bins)\n",
"# df.groupby(\"binned-v-radial\").agg({\"vib\": {\"min\", \"median\", \"mean\", \"max\", \"count\"}})"
]
},
{
"cell_type": "code",
"execution_count": 81,
"metadata": {},
"outputs": [],
"source": [
"# corr = df[df.columns.sort_values()].corr()[[\"vib\", \"vib-x\", \"vib-y\"]]\n",
"# cm = sns.light_palette(\"orange\", as_cmap=True) \n",
"# cm = sns.diverging_palette(220, 20, sep=20, as_cmap=True) \n",
"# corr.style.background_gradient(cmap=cm).set_precision(2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"## Inline Description:\n",
"\n",
"It is very nice to describe results using Markdown with dynamic values like: **3.141592653589793**.\n",
"\n"
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"execution_count": 82,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import Markdown \n",
"Markdown(\"\"\"\n",
"## Inline Description:\n",
"\n",
"It is very nice to describe results using Markdown with dynamic values like: **{pi}**.\n",
"\n",
"\"\"\".format(pi=np.pi))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Machine Learning\n",
"\n",
"**Be careful when handling with Artificial Intelligence:**\n",
"\n",
"![Be careful when handling with Artificial Intelligence](https://imgs.xkcd.com/comics/twitter_bot.png)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}