From 65ee23b1f9b6b247f249821cc699f8094c0bab0b Mon Sep 17 00:00:00 2001 From: maxhelskens Date: Mon, 14 May 2018 21:56:28 +0200 Subject: [PATCH 1/4] Issue23: Add threshold detection --- notebooks/Gas Toggle Demo.ipynb | 2 +- opengrid/library/analysis.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/notebooks/Gas Toggle Demo.ipynb b/notebooks/Gas Toggle Demo.ipynb index 7d8a5d5..2f5d0a6 100644 --- a/notebooks/Gas Toggle Demo.ipynb +++ b/notebooks/Gas Toggle Demo.ipynb @@ -116,7 +116,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.0" + "version": "3.6.3" } }, "nbformat": 4, diff --git a/opengrid/library/analysis.py b/opengrid/library/analysis.py index c67e520..d80779a 100644 --- a/opengrid/library/analysis.py +++ b/opengrid/library/analysis.py @@ -69,6 +69,35 @@ def do_analysis(self, agg, starttime=dt.time.min, endtime=dt.time.max): self.result = pd.DataFrame() +def threshold_detection(ts, threshold, direction='above', freq=None): + """ + Describe timeseries in regards to a specified threshold + + :param ts: Pandas Series + :param threshold: number + :param direction: string (above or below) + :param freq: string + :return: dict + """ + if freq is not None: + ts = ts.resample(freq).sum() + + if direction == 'above': + thresh_ts = ts > threshold + if direction == 'below': + thresh_ts = ts < threshold + + duration = len(ts[thresh_ts]) + amount = (ts[thresh_ts] - threshold).sum() + + on_toggles = thresh_ts + shifted = np.logical_not(on_toggles.shift(1)) + result = on_toggles & shifted + count = result.sum() + + return dict([('Total_amount', amount), ('Total_duration', duration), ('count', count)]) + + def standby(df, resolution='24h', time_window=None): """ Compute standby power From 84931983c478c8ed016d545eb94e7460948ad9e8 Mon Sep 17 00:00:00 2001 From: maxhelskens Date: Mon, 14 May 2018 21:57:26 +0200 Subject: [PATCH 2/4] Issue23: Add threshold detection --- notebooks/Threshold_detection.ipynb | 139 ++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 notebooks/Threshold_detection.ipynb diff --git a/notebooks/Threshold_detection.ipynb b/notebooks/Threshold_detection.ipynb new file mode 100644 index 0000000..5e4c14a --- /dev/null +++ b/notebooks/Threshold_detection.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Intro #\n", + "\n", + "This analysis counts the total amount of time - and total consumption when - a series goes above (or below) a certain threshold.\n", + "It also returns the count of these occurances." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports ##" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from opengrid import datasets\n", + "import numpy as np\n", + "\n", + "plt = og.plot_style()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prepare data ##" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# We have a dataset prepared that contains gas sensor data in W on a minutely resolution\n", + "df = datasets.get('gas_dec2016_min')\n", + "df = df['313b']\n", + "\n", + "ts = df.squeeze()\n", + "\n", + "threshold = 25000\n", + "freq = 'min'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot ##" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(ts)\n", + "plt.axhline(threshold, color='r')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def threshold_detection(ts, threshold, direction='above', freq = None):\n", + " \n", + " if freq is not None:\n", + " ts = ts.resample(freq).sum()\n", + " \n", + " if direction == 'above':\n", + " thresh_ts = ts > threshold\n", + " if direction == 'below':\n", + " thresh_ts = ts < threshold\n", + " \n", + " \n", + " duration = len(ts[thresh_ts])\n", + " amount = (ts[thresh_ts] - threshold).sum()\n", + " \n", + " on_toggles = thresh_ts\n", + " shifted = np.logical_not(on_toggles.shift(1))\n", + " result = on_toggles & shifted\n", + " count = result.sum()\n", + " \n", + " return dict([('Total_amount', amount), ('Total_duration', duration), ('count', count)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "threshold_detection(ts, threshold, freq=freq)" + ] + }, + { + "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.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From ec372ca8b25d55671546d8a6a738772803f9e04a Mon Sep 17 00:00:00 2001 From: maxhelskens Date: Tue, 15 May 2018 08:46:40 +0200 Subject: [PATCH 3/4] added unit test --- notebooks/Threshold_detection.ipynb | 24 +++--------------------- opengrid/tests/test_analyses.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/notebooks/Threshold_detection.ipynb b/notebooks/Threshold_detection.ipynb index 5e4c14a..7f064a1 100644 --- a/notebooks/Threshold_detection.ipynb +++ b/notebooks/Threshold_detection.ipynb @@ -24,6 +24,7 @@ "outputs": [], "source": [ "import pandas as pd\n", + "import opengrid as og\n", "from opengrid import datasets\n", "import numpy as np\n", "\n", @@ -76,26 +77,7 @@ "metadata": {}, "outputs": [], "source": [ - "def threshold_detection(ts, threshold, direction='above', freq = None):\n", - " \n", - " if freq is not None:\n", - " ts = ts.resample(freq).sum()\n", - " \n", - " if direction == 'above':\n", - " thresh_ts = ts > threshold\n", - " if direction == 'below':\n", - " thresh_ts = ts < threshold\n", - " \n", - " \n", - " duration = len(ts[thresh_ts])\n", - " amount = (ts[thresh_ts] - threshold).sum()\n", - " \n", - " on_toggles = thresh_ts\n", - " shifted = np.logical_not(on_toggles.shift(1))\n", - " result = on_toggles & shifted\n", - " count = result.sum()\n", - " \n", - " return dict([('Total_amount', amount), ('Total_duration', duration), ('count', count)])" + "result = og.analysis.threshold_detection(ts, threshold, freq=freq)" ] }, { @@ -104,7 +86,7 @@ "metadata": {}, "outputs": [], "source": [ - "threshold_detection(ts, threshold, freq=freq)" + "result" ] }, { diff --git a/opengrid/tests/test_analyses.py b/opengrid/tests/test_analyses.py index 796eb92..961d791 100644 --- a/opengrid/tests/test_analyses.py +++ b/opengrid/tests/test_analyses.py @@ -64,6 +64,20 @@ def test_load_factor(self): self.assertIsInstance(ts, pd.Series) self.assertAlmostEqual(175.0345212009457, (lf2 * 800).iloc[0]) + def test_threshold_detection(self): + df = datasets.get('gas_dec2016_min') + df = df['313b'] + + ts = df.squeeze() + + threshold = 25000 + freq = 'min' + + result = og.analysis.threshold_detection(ts, threshold, freq=freq) + + self.assertEqual(result['count'], 19) + self.assertEqual(result['Total_duration'], 22) + self.assertAlmostEqual(result['Total_amount'], 24335.314876027405) if __name__ == '__main__': unittest.main() From 50d3e56eb20e0ae3063d8acd69c8b252652ceeeb Mon Sep 17 00:00:00 2001 From: maxhelskens Date: Tue, 15 May 2018 09:01:49 +0200 Subject: [PATCH 4/4] Handle exceptions --- opengrid/library/analysis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opengrid/library/analysis.py b/opengrid/library/analysis.py index d80779a..626d93e 100644 --- a/opengrid/library/analysis.py +++ b/opengrid/library/analysis.py @@ -84,8 +84,10 @@ def threshold_detection(ts, threshold, direction='above', freq=None): if direction == 'above': thresh_ts = ts > threshold - if direction == 'below': + elif direction == 'below': thresh_ts = ts < threshold + else: + raise ValueError("direction can only be 'above' or 'below'.") duration = len(ts[thresh_ts]) amount = (ts[thresh_ts] - threshold).sum()