[
  {
    "path": "DAE/DAE-example.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# How to use the Denoising Autoencoder with NILMTK\\n\",\n    \"This is an example on how to train and use the Denoising Autoencoder (DAE) disaggregator on the [REDD](http://redd.csail.mit.edu/) dataset using [NILMTK](https://github.com/nilmtk/NILMTK/).\\n\",\n    \"\\n\",\n    \"This network was described in the [Neural NILM](https://arxiv.org/pdf/1507.06594.pdf) paper.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"First of all, we need to train the DAEDisaggregator using the train data. For this example, both train and test data are consumption data of the fridge of the first REDD building.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"import warnings; warnings.filterwarnings('ignore')\\n\",\n    \"\\n\",\n    \"from nilmtk import DataSet\\n\",\n    \"train = DataSet('redd.h5')\\n\",\n    \"train.set_window(end=\\\"30-4-2011\\\") #Use data only until 4/30/2011\\n\",\n    \"train_elec = train.buildings[1].elec\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Next, we need to define the disaggregator model. For this example, the input window will have size of 200 samples.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Using TensorFlow backend.\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from daedisaggregator import DAEDisaggregator\\n\",\n    \"dae = DAEDisaggregator(256)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Then train the model. We need to input the train data as well as their sample period. Also, we need to pass the desired number of training epochs. Finally, save the model for later use.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {\n    \"scrolled\": true\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Epoch 1/25\\n\",\n      \"3919/3919 [==============================] - 17s - loss: 3.2031e-04    \\n\",\n      \"Epoch 2/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.4596e-04     \\n\",\n      \"Epoch 3/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.2974e-04     \\n\",\n      \"Epoch 4/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.3065e-04     \\n\",\n      \"Epoch 5/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.2060e-04     \\n\",\n      \"Epoch 6/25\\n\",\n      \"3919/3919 [==============================] - 2s - loss: 2.0973e-04     \\n\",\n      \"Epoch 7/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.2210e-04     \\n\",\n      \"Epoch 8/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.1770e-04     \\n\",\n      \"Epoch 9/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0082e-04     \\n\",\n      \"Epoch 10/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0247e-04     \\n\",\n      \"Epoch 11/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0241e-04     \\n\",\n      \"Epoch 12/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0058e-04     \\n\",\n      \"Epoch 13/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 1.9457e-04     \\n\",\n      \"Epoch 14/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0899e-04     \\n\",\n      \"Epoch 15/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0804e-04     \\n\",\n      \"Epoch 16/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0194e-04     \\n\",\n      \"Epoch 17/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 1.8804e-04     \\n\",\n      \"Epoch 18/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 1.9298e-04     \\n\",\n      \"Epoch 19/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.1932e-04     \\n\",\n      \"Epoch 20/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 1.9278e-04     \\n\",\n      \"Epoch 21/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 1.9146e-04     \\n\",\n      \"Epoch 22/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0114e-04     \\n\",\n      \"Epoch 23/25\\n\",\n      \"3919/3919 [==============================] - 2s - loss: 1.9523e-04     \\n\",\n      \"Epoch 24/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 2.0197e-04     \\n\",\n      \"Epoch 25/25\\n\",\n      \"3919/3919 [==============================] - 3s - loss: 1.9083e-04     \\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"train_mains = train_elec.mains().all_meters()[0] # The aggregated meter that provides the input\\n\",\n    \"train_meter = train_elec.submeters()['fridge'] # The microwave meter that is used as a training target\\n\",\n    \"\\n\",\n    \"dae.train(train_mains, train_meter, epochs=25, sample_period=1)\\n\",\n    \"dae.export_model(\\\"model-redd100.h5\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now that the model is trained, we can use it to disaggregate energy data. Let's test it on the rest of the data from building 1.\\n\",\n    \"\\n\",\n    \"First we use the model to predict the fridge consumption. The results are saved automatically in a .h5 datastore.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"New sensible chunk: 121482\\n\",\n      \"New sensible chunk: 112661\\n\",\n      \"New sensible chunk: 87770\\n\",\n      \"New sensible chunk: 54084\\n\",\n      \"New sensible chunk: 2660\\n\",\n      \"New sensible chunk: 33513\\n\",\n      \"New sensible chunk: 138535\\n\",\n      \"New sensible chunk: 32514\\n\",\n      \"New sensible chunk: 27255\\n\",\n      \"New sensible chunk: 34833\\n\",\n      \"New sensible chunk: 100831\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"test = DataSet('redd.h5')\\n\",\n    \"test.set_window(start=\\\"30-4-2011\\\")\\n\",\n    \"test_elec = test.buildings[1].elec\\n\",\n    \"test_mains = test_elec.mains().all_meters()[0]\\n\",\n    \"\\n\",\n    \"disag_filename = 'disag-out.h5' # The filename of the resulting datastore\\n\",\n    \"from nilmtk.datastore import HDFDataStore\\n\",\n    \"output = HDFDataStore(disag_filename, 'w')\\n\",\n    \"\\n\",\n    \"# test_mains: The aggregated signal meter\\n\",\n    \"# output: The output datastore\\n\",\n    \"# train_meter: This is used in order to copy the metadata of the train meter into the datastore\\n\",\n    \"dae.disaggregate(test_mains, output, train_meter, sample_period=1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Let's plot the results and compare them to the ground truth signal.\\n\",\n    \"\\n\",\n    \"**Note:** Calling plot this way, downsamples the signal to reduce computing time. To plot the entire signal call\\n\",\n    \"```\\n\",\n    \"predicted.power_series_all_data().plot()\\n\",\n    \"ground_truth.power_series_all_data().plot()\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\nMGAAnTt35qKLLqJnz548+uijnHPOOWzevJnzzz+/AncgGjU0lEyivc3skNrMoGrMNjzGGAYOHOja\\nJiK0tbUVvr/xxhu8+uqr7LvvvoVtzv02o0aNorm5mblz59KnTx8ATj75ZA455BCamtrFh3nz5jF1\\n6lQuueQSrr76agB++tOfMnToUBYtWuTK87LLLqO5uZkFCxawyy67APCTn/yE008/nSuuuIJzzz23\\nJnEiamgomUKHt2aHgoRRa49GBx8hkAZbdmxhydolVT/PAb0OoPtO3SuWn4gwceJEvvCFLwSmGThw\\noMvI8KO1tZXHH3+cM844o2BkABx00EEcd9xxzJo1q7Bt+vTpiAjDhw935TFixAjuvPPOwndjDFOn\\nTmXo0KG0tra6vC7HH3889913HwsWLKB///6xr7dU1NBQMol6NLKDrnVS/yxZu4R+t/Sr+nnmnzef\\nw3aPF7gZl/79+4cGg3pHpPixevVqWlpafA2S/fff32VovP3223Tq1Im99trLlc577KpVq9i0aRMT\\nJ05kwoQJRfmKCB988EFk2SqBGhpKptAYjeygMRodhwN6HcD88+bX5Dy1plu3bjU/J0AuZ42sGzZs\\nGGeddZZvmi9/+cs1KYsaGkqmKEgn2tvMDBqjUf9036l7xT0NHYnevXvTpUsX3njjjaJ9S5a4JaW9\\n9tqL1tZW3n77bZdXw3tsnz592Hnnncnlcnzzm9+sTsFjosNblUyi82hkB43RUOqdTp068a1vfYv7\\n77+f999/v7D95ZdfZubMma60gwYNwhjDxIkTXdtvuukmV91rbm7mlFNOYcqUKSxevLjonGvXrq3w\\nVQSjHg0lU6h0kh10Pgul2lSybo0ePZojjjiCI488kuHDh9PS0sL48eM5+OCDeeWVVwrpBgwYwEkn\\nncS1117LmjVr6N+/P0888QTLli0D3IbuNddcw5NPPsmAAQM499xzOfDAA/nwww95/vnnmTNnDqtW\\nrapY+cNQj4aSSbTxyQ661olSLaK8V2HrlHj3HXrooUyfPp2ePXty5ZVXcvvttzN27Fi++93vFh17\\n9913M3z4cB566CEuu+wy2trauOuuuzDG0LVr10K6Pn36MG/ePIYNG8b999/PiBEjGDduHBs3buT3\\nv/99iVedHPVoKJlCh7dmh9RWb9Vg0EwwbNgwhg0bFri/ubnZd76MsH3HHHMM8+bNK9o+ZswY1/du\\n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\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f8e90a6e690>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"result = DataSet(disag_filename)\\n\",\n    \"res_elec = result.buildings[1].elec\\n\",\n    \"predicted = res_elec['fridge']\\n\",\n    \"ground_truth = test_elec['fridge']\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"predicted.plot()\\n\",\n    \"ground_truth.plot()\\n\",\n    \"plt.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Finally let's see the metric results.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"============ Recall: 0.991529182705\\n\",\n      \"============ Precision: 0.80751980409\\n\",\n      \"============ Accuracy: 0.802475953548\\n\",\n      \"============ F1 Score: 0.890114118342\\n\",\n      \"============ Relative error in total energy: 0.755207186325\\n\",\n      \"============ Mean absolute error(in Watts): 51.2799840994\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import metrics\\n\",\n    \"rpaf = metrics.recall_precision_accuracy_f1(predicted, ground_truth)\\n\",\n    \"print(\\\"============ Recall: {}\\\".format(rpaf[0]))\\n\",\n    \"print(\\\"============ Precision: {}\\\".format(rpaf[1]))\\n\",\n    \"print(\\\"============ Accuracy: {}\\\".format(rpaf[2]))\\n\",\n    \"print(\\\"============ F1 Score: {}\\\".format(rpaf[3]))\\n\",\n    \"\\n\",\n    \"print(\\\"============ Relative error in total energy: {}\\\".format(metrics.relative_error_total_energy(predicted, ground_truth)))\\n\",\n    \"print(\\\"============ Mean absolute error(in Watts): {}\\\".format(metrics.mean_absolute_error(predicted, ground_truth)))\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"anaconda-cloud\": {},\n  \"kernelspec\": {\n   \"display_name\": \"Python [conda env:nilmtk-env]\",\n   \"language\": \"python\",\n   \"name\": \"conda-env-nilmtk-env-py\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.12\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 1\n}\n"
  },
  {
    "path": "DAE/README.md",
    "content": "# Denoising Autoencoder Energy Disaggregator\n\nAs described in:\n\n[Neural NILM: Deep Neural Networks Applied to Energy Disaggregation](https://arxiv.org/pdf/1507.06594.pdf) by Jack Kelly and William Knottenbelt\n\nSee example experiment [here](https://github.com/OdysseasKr/neural-disaggregator/blob/master/DAE/DAE-example.ipynb).\n"
  },
  {
    "path": "DAE/daedisaggregator.py",
    "content": "from __future__ import print_function, division\nimport random\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nimport pandas as pd\nimport numpy as np\nimport h5py\n\nfrom keras.models import load_model\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Flatten, Conv1D, Reshape, Dropout\nfrom keras.utils import plot_model\n\nfrom nilmtk.utils import find_nearest\nfrom nilmtk.feature_detectors import cluster\nfrom nilmtk.legacy.disaggregate import Disaggregator\nfrom nilmtk.datastore import HDFDataStore\n\nclass DAEDisaggregator(Disaggregator):\n    '''Denoising Autoencoder disaggregator from Neural NILM\n    https://arxiv.org/pdf/1507.06594.pdf\n\n    Attributes\n    ----------\n    model : keras Sequential model\n    sequence_length : the size of window to use on the aggregate data\n    mmax : the maximum value of the aggregate data\n\n    MIN_CHUNK_LENGTH : int\n       the minimum length of an acceptable chunk\n    '''\n\n    def __init__(self, sequence_length):\n        '''Initialize disaggregator\n\n        Parameters\n        ----------\n        sequence_length : the size of window to use on the aggregate data\n        meter : a nilmtk.ElecMeter meter of the appliance to be disaggregated\n        '''\n        self.MODEL_NAME = \"AUTOENCODER\"\n        self.mmax = None\n        self.sequence_length = sequence_length\n        self.MIN_CHUNK_LENGTH = sequence_length\n        self.model = self._create_model(self.sequence_length)\n\n    def train(self, mains, meter, epochs=1, batch_size=16, **load_kwargs):\n        '''Train\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter object for the aggregate data\n        meter : a nilmtk.ElecMeter object for the meter data\n        epochs : number of epochs to train\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        main_power_series = mains.power_series(**load_kwargs)\n        meter_power_series = meter.power_series(**load_kwargs)\n\n        # Train chunks\n        run = True\n        mainchunk = next(main_power_series)\n        meterchunk = next(meter_power_series)\n        if self.mmax == None:\n            self.mmax = mainchunk.max()\n\n        while(run):\n            mainchunk = self._normalize(mainchunk, self.mmax)\n            meterchunk = self._normalize(meterchunk, self.mmax)\n\n            self.train_on_chunk(mainchunk, meterchunk, epochs, batch_size)\n            try:\n                mainchunk = next(main_power_series)\n                meterchunk = next(meter_power_series)\n            except:\n                run = False\n\n    def train_on_chunk(self, mainchunk, meterchunk, epochs, batch_size):\n        '''Train using only one chunk\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        '''\n\n        s = self.sequence_length\n        #up_limit =  min(len(mainchunk), len(meterchunk))\n        #down_limit =  max(len(mainchunk), len(meterchunk))\n\n        # Replace NaNs with 0s\n        mainchunk.fillna(0, inplace=True)\n        meterchunk.fillna(0, inplace=True)\n        ix = mainchunk.index.intersection(meterchunk.index)\n        mainchunk = mainchunk[ix]\n        meterchunk = meterchunk[ix]\n\n        # Create array of batches\n        #additional = s - ((up_limit-down_limit) % s)\n        additional = s - (len(ix) % s)\n        X_batch = np.append(mainchunk, np.zeros(additional))\n        Y_batch = np.append(meterchunk, np.zeros(additional))\n\n        X_batch = np.reshape(X_batch, (int(len(X_batch) / s), s, 1))\n        Y_batch = np.reshape(Y_batch, (int(len(Y_batch) / s), s, 1))\n\n        self.model.fit(X_batch, Y_batch, batch_size=batch_size, epochs=epochs, shuffle=True)\n\n    def train_across_buildings(self, mainlist, meterlist, epochs=1, batch_size=128, **load_kwargs):\n        assert len(mainlist) == len(meterlist), \"Number of main and meter channels should be equal\"\n        num_meters = len(mainlist)\n\n        mainps = [None] * num_meters\n        meterps = [None] * num_meters\n        mainchunks = [None] * num_meters\n        meterchunks = [None] * num_meters\n\n        for i,m in enumerate(mainlist):\n            mainps[i] = m.power_series(**load_kwargs)\n\n        for i,m in enumerate(meterlist):\n            meterps[i] = m.power_series(**load_kwargs)\n\n        for i in range(num_meters):\n            mainchunks[i] = next(mainps[i])\n            meterchunks[i] = next(meterps[i])\n        if self.mmax == None:\n            self.mmax = max([m.max() for m in mainchunks])\n\n\n        run = True\n        while(run):\n            mainchunks = [self._normalize(m, self.mmax) for m in mainchunks]\n            meterchunks = [self._normalize(m, self.mmax) for m in meterchunks]\n\n            self.train_across_buildings_chunk(mainchunks, meterchunks, epochs, batch_size)\n            try:\n                for i in range(num_meters):\n                    mainchunks[i] = next(mainps[i])\n                    meterchunks[i] = next(meterps[i])\n            except:\n                run = False\n\n    def train_across_buildings_chunk(self, mainchunks, meterchunks, epochs, batch_size):\n        num_meters = len(mainchunks)\n        batch_size = int(batch_size/num_meters)\n        num_of_batches = [None] * num_meters\n        s = self.sequence_length\n        for i in range(num_meters):\n            mainchunks[i].fillna(0, inplace=True)\n            meterchunks[i].fillna(0, inplace=True)\n            ix = mainchunks[i].index.intersection(meterchunks[i].index)\n            m1 = mainchunks[i]\n            m2 = meterchunks[i]\n            mainchunks[i] = m1[ix]\n            meterchunks[i] = m2[ix]\n\n            num_of_batches[i] = int(len(ix)/(s*batch_size)) - 1\n\n        for e in range(epochs):\n            print(e)\n            batch_indexes = list(range(min(num_of_batches)))\n            random.shuffle(batch_indexes)\n\n            for bi, b in enumerate(batch_indexes):\n\n                print(\"Batch {} of {}\".format(bi,num_of_batches), end=\"\\r\")\n                sys.stdout.flush()\n                X_batch = np.empty((batch_size*num_meters, s, 1))\n                Y_batch = np.empty((batch_size*num_meters, s, 1))\n\n                for i in range(num_meters):\n                    mainpart = mainchunks[i]\n                    meterpart = meterchunks[i]\n                    mainpart = mainpart[b*batch_size*s:(b+1)*batch_size*s]\n                    meterpart = meterpart[b*batch_size*s:(b+1)*batch_size*s]\n                    X = np.reshape(mainpart, (batch_size, s, 1))\n                    Y = np.reshape(meterpart, (batch_size, s, 1))\n\n                    X_batch[i*batch_size:(i+1)*batch_size] = np.array(X)\n                    Y_batch[i*batch_size:(i+1)*batch_size] = np.array(Y)\n\n                p = np.random.permutation(len(X_batch))\n                X_batch, Y_batch = X_batch[p], Y_batch[p]\n\n                self.model.train_on_batch(X_batch, Y_batch)\n            print(\"\\n\")\n\n    def disaggregate(self, mains, output_datastore, meter_metadata, **load_kwargs):\n        '''Disaggregate mains according to the model learnt.\n\n        Parameters\n        ----------\n        mains : nilmtk.ElecMeter\n        output_datastore : instance of nilmtk.DataStore subclass\n            For storing power predictions from disaggregation algorithm.\n        meter_metadata : metadata for the produced output\n        **load_kwargs : key word arguments\n            Passed to `mains.power_series(**kwargs)`\n        '''\n\n        load_kwargs = self._pre_disaggregation_checks(load_kwargs)\n\n        load_kwargs.setdefault('sample_period', 60)\n        load_kwargs.setdefault('sections', mains.good_sections())\n\n        timeframes = []\n        building_path = '/building{}'.format(mains.building())\n        mains_data_location = building_path + '/elec/meter1'\n        data_is_available = False\n\n        for chunk in mains.power_series(**load_kwargs):\n            if len(chunk) < self.MIN_CHUNK_LENGTH:\n                continue\n            print(\"New sensible chunk: {}\".format(len(chunk)))\n\n            timeframes.append(chunk.timeframe)\n            measurement = chunk.name\n            chunk2 = self._normalize(chunk, self.mmax)\n\n            appliance_power = self.disaggregate_chunk(chunk2)\n            appliance_power[appliance_power < 0] = 0\n            appliance_power = self._denormalize(appliance_power, self.mmax)\n\n            # Append prediction to output\n            data_is_available = True\n            cols = pd.MultiIndex.from_tuples([chunk.name])\n            meter_instance = meter_metadata.instance()\n            df = pd.DataFrame(\n                appliance_power.values, index=appliance_power.index,\n                columns=cols, dtype=\"float32\")\n            key = '{}/elec/meter{}'.format(building_path, meter_instance)\n            output_datastore.append(key, df)\n\n            # Append aggregate data to output\n            mains_df = pd.DataFrame(chunk, columns=cols, dtype=\"float32\")\n            output_datastore.append(key=mains_data_location, value=mains_df)\n\n        # Save metadata to output\n        if data_is_available:\n            self._save_metadata_for_disaggregation(\n                output_datastore=output_datastore,\n                sample_period=load_kwargs['sample_period'],\n                measurement=measurement,\n                timeframes=timeframes,\n                building=mains.building(),\n                meters=[meter_metadata]\n            )\n\n    def disaggregate_chunk(self, mains):\n        '''In-memory disaggregation.\n\n        Parameters\n        ----------\n        mains : pd.Series to disaggregate\n        Returns\n        -------\n        appliance_powers : pd.DataFrame where each column represents a\n            disaggregated appliance.  Column names are the integer index\n            into `self.model` for the appliance in question.\n        '''\n        s = self.sequence_length\n        up_limit = len(mains)\n\n        mains.fillna(0, inplace=True)\n\n        additional = s - (up_limit % s)\n        X_batch = np.append(mains, np.zeros(additional))\n        X_batch = np.reshape(X_batch, (int(len(X_batch) / s), s ,1))\n\n        pred = self.model.predict(X_batch)\n        pred = np.reshape(pred, (up_limit + additional))[:up_limit]\n        column = pd.Series(pred, index=mains.index, name=0)\n\n        appliance_powers_dict = {}\n        appliance_powers_dict[0] = column\n        appliance_powers = pd.DataFrame(appliance_powers_dict)\n        return appliance_powers\n\n\n    def import_model(self, filename):\n        '''Loads keras model from h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n\n        Returns: Keras model\n        '''\n        self.model = load_model(filename)\n        with h5py.File(filename, 'a') as hf:\n            ds = hf.get('disaggregator-data').get('mmax')\n            self.mmax = np.array(ds)[0]\n\n    def export_model(self, filename):\n        '''Saves keras model to h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n        '''\n        self.model.save(filename)\n        with h5py.File(filename, 'a') as hf:\n            gr = hf.create_group('disaggregator-data')\n            gr.create_dataset('mmax', data = [self.mmax])\n\n    def _normalize(self, chunk, mmax):\n        '''Normalizes timeseries\n\n        Parameters\n        ----------\n        chunk : the timeseries to normalize\n        max : max value of the powerseries\n\n        Returns: Normalized timeseries\n        '''\n        tchunk = chunk / mmax\n        return tchunk\n\n    def _denormalize(self, chunk, mmax):\n        '''Deormalizes timeseries\n        Note: This is not entirely correct\n\n        Parameters\n        ----------\n        chunk : the timeseries to denormalize\n        max : max value used for normalization\n\n        Returns: Denormalized timeseries\n        '''\n        tchunk = chunk * mmax\n        return tchunk\n\n    def _create_model(self, sequence_len):\n        '''Creates the Auto encoder module described in the paper\n        '''\n        model = Sequential()\n\n        # 1D Conv\n        model.add(Conv1D(8, 4, activation=\"linear\", input_shape=(sequence_len, 1), padding=\"same\", strides=1))\n        model.add(Flatten())\n\n        # Fully Connected Layers\n        model.add(Dropout(0.2))\n        model.add(Dense((sequence_len-0)*8, activation='relu'))\n\n        model.add(Dropout(0.2))\n        model.add(Dense(128, activation='relu'))\n\n        model.add(Dropout(0.2))\n        model.add(Dense((sequence_len-0)*8, activation='relu'))\n\n        model.add(Dropout(0.2))\n\n        # 1D Conv\n        model.add(Reshape(((sequence_len-0), 8)))\n        model.add(Conv1D(1, 4, activation=\"linear\", padding=\"same\", strides=1))\n\n        model.compile(loss='mse', optimizer='adam')\n        plot_model(model, to_file='model.png', show_shapes=True)\n\n        return model\n"
  },
  {
    "path": "DAE/metrics.py",
    "content": "from nilmtk.electric import align_two_meters\nimport numpy as np\n\ndef tp_tn_fp_fn(states_pred, states_ground):\n    tp = np.sum(np.logical_and(states_pred == 1, states_ground == 1))\n    fp = np.sum(np.logical_and(states_pred == 1, states_ground == 0))\n    fn = np.sum(np.logical_and(states_pred == 0, states_ground == 1))\n    tn = np.sum(np.logical_and(states_pred == 0, states_ground == 0))\n    return tp, tn, fp, fn\n\ndef recall_precision_accuracy_f1(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    threshold = ground.on_power_threshold()\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        sum_samples += len(chunk)\n        pr = np.array([0 if (p)<threshold else 1 for p in chunk.iloc[:,0]])\n        gr = np.array([0 if p<threshold else 1 for p in chunk.iloc[:,1]])\n\n        tp, tn, fp, fn = tp_tn_fp_fn(pr,gr)\n        p = sum(pr)\n        n = len(pr) - p\n\n        chunk_results.append([tp,tn,fp,fn,p,n])\n\n    if sum_samples == 0:\n        return None\n    else:\n        [tp,tn,fp,fn,p,n] = np.sum(chunk_results, axis=0)\n\n        res_recall = recall(tp,fn)\n        res_precision = precision(tp,fp)\n        res_f1 = f1(res_precision,res_recall)\n        res_accuracy = accuracy(tp,tn,p,n)\n\n        return (res_recall,res_precision,res_accuracy,res_f1)\n\ndef relative_error_total_energy(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        E_pred = sum(chunk.iloc[:,0])\n        E_ground = sum(chunk.iloc[:,1])\n\n        chunk_results.append([\n                            E_pred,\n                            E_ground\n                            ])\n    if sum_samples == 0:\n        return None\n    else:\n        [E_pred, E_ground] = np.sum(chunk_results,axis=0)\n        return abs(E_pred - E_ground) / float(max(E_pred,E_ground))\n\ndef mean_absolute_error(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    total_sum = 0.0\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        total_sum += sum(abs((chunk.iloc[:,0]) - chunk.iloc[:,1]))\n    if sum_samples == 0:\n        return None\n    else:\n        return total_sum / sum_samples\n\n\ndef recall(tp,fn):\n    return tp/float(tp+fn)\n\ndef precision(tp,fp):\n    return tp/float(tp+fp)\n\ndef f1(prec,rec):\n    return 2 * (prec*rec) / float(prec+rec)\n\ndef accuracy(tp, tn, p, n):\n    return (tp + tn) / float(p + n)\n"
  },
  {
    "path": "DAE/redd-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom daedisaggregator import DAEDisaggregator\nimport metrics\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('redd.h5')\ntest = DataSet('redd.h5')\n\ntrain.set_window(end=\"30-4-2011\")\ntest.set_window(start=\"30-4-2011\")\n\ntest_building = 1\nmeter_key = 'microwave'\ntrain_elec = train.buildings[1].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains().all_meters()[0]\ntest_mains = test_elec.mains().all_meters()[0]\ndae = DAEDisaggregator(256)\n\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\ndae.train(train_mains, train_meter, epochs=5, sample_period=1)\ndae.export_model(\"model-redd100.h5\")\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = 'disag-out.h5'\noutput = HDFDataStore(disag_filename, 'w')\ndae.disaggregate(test_mains, output, train_meter, sample_period=1)\noutput.close()\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "DAE/ukdale-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom daedisaggregator import DAEDisaggregator\nimport metrics\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('ukdale.h5')\ntest = DataSet('ukdale.h5')\n\ntrain.set_window(start=\"13-4-2013\", end=\"1-1-2014\")\ntest.set_window(start=\"1-1-2014\", end=\"30-3-2014\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'microwave'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntest_meter = test_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains()\ntest_mains = test_elec.mains()\ndae = DAEDisaggregator(300)\n\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    print(\"CHECKPOINT {}\".format(epochs))\n    dae.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    dae.export_model(\"UKDALE-DAE-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = \"disag-out.h5\"\noutput = HDFDataStore(disag_filename, 'w')\ndae.disaggregate(test_mains, output, test_meter, sample_period=sample_period)\noutput.close()\n\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_meter)\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_meter)))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_meter)))\n"
  },
  {
    "path": "GRU/GRU-example.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# How to use the RNN Autoencoder with NILMTK\\n\",\n    \"\\n\",\n    \"This is an example on how to train and use the Recurrent Network (RNN) disaggregator on the [REDD](http://redd.csail.mit.edu/) dataset using [NILMTK](https://github.com/nilmtk/NILMTK/).\\n\",\n    \"\\n\",\n    \"This network was described in the [Neural NILM](https://arxiv.org/pdf/1507.06594.pdf) paper.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"First of all, we need to train the RNNDisaggregator using the train data. For this example, both train and test data are consumption data of the microwave of the first REDD building.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import warnings; warnings.simplefilter('ignore')\\n\",\n    \"\\n\",\n    \"from nilmtk import DataSet\\n\",\n    \"train = DataSet('redd.h5')\\n\",\n    \"train.set_window(end=\\\"30-4-2011\\\") #Use data only until 4/30/2011\\n\",\n    \"train_elec = train.buildings[1].elec\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Next, we need to define the disaggregator model.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Using TensorFlow backend.\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from grudisaggregator import GRUDisaggregator\\n\",\n    \"gru = GRUDisaggregator()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Then train the model. We need to input the train data as well as their sample period. Also, we need to pass the desired number of training epochs. Finally, save the model for later use.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Epoch 1/5\\n\",\n      \"1003066/1003066 [==============================] - 137s - loss: 7.2744e-04   \\n\",\n      \"Epoch 2/5\\n\",\n      \"1003066/1003066 [==============================] - 105s - loss: 6.6916e-04   \\n\",\n      \"Epoch 3/5\\n\",\n      \"1003066/1003066 [==============================] - 106s - loss: 6.5737e-04   \\n\",\n      \"Epoch 4/5\\n\",\n      \"1003066/1003066 [==============================] - 106s - loss: 6.5401e-04   \\n\",\n      \"Epoch 5/5\\n\",\n      \"1003066/1003066 [==============================] - 103s - loss: 6.5021e-04   \\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"train_mains = train_elec.mains().all_meters()[0] # The aggregated meter that provides the input\\n\",\n    \"train_meter = train_elec.submeters()['microwave'] # The microwave meter that is used as a training target\\n\",\n    \"\\n\",\n    \"gru.train(train_mains, train_meter, epochs=5, sample_period=1)\\n\",\n    \"gru.export_model(\\\"model-redd5.h5\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now that the model is trained, we can use it to disaggregate energy data. Let's test it on the rest of the data from building 1.\\n\",\n    \"\\n\",\n    \"First we use the model to predict the microwave consumption. The results are saved automatically in a .h5 datastore.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"New sensible chunk: 121482\\n\",\n      \"New sensible chunk: 112661\\n\",\n      \"New sensible chunk: 87770\\n\",\n      \"New sensible chunk: 54084\\n\",\n      \"New sensible chunk: 2660\\n\",\n      \"New sensible chunk: 33513\\n\",\n      \"New sensible chunk: 138535\\n\",\n      \"New sensible chunk: 32514\\n\",\n      \"New sensible chunk: 27255\\n\",\n      \"New sensible chunk: 34833\\n\",\n      \"New sensible chunk: 100831\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"test = DataSet('redd.h5')\\n\",\n    \"test.set_window(start=\\\"30-4-2011\\\")\\n\",\n    \"test_elec = test.buildings[1].elec\\n\",\n    \"test_mains = test_elec.mains().all_meters()[0]\\n\",\n    \"\\n\",\n    \"disag_filename = 'disag-out.h5' # The filename of the resulting datastore\\n\",\n    \"from nilmtk.datastore import HDFDataStore\\n\",\n    \"output = HDFDataStore(disag_filename, 'w')\\n\",\n    \"\\n\",\n    \"# test_mains: The aggregated signal meter\\n\",\n    \"# output: The output datastore\\n\",\n    \"# train_meter: This is used in order to copy the metadata of the train meter into the datastore\\n\",\n    \"gru.disaggregate(test_mains, output, train_meter, sample_period=1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Let's plot the results and compare them to the ground truth signal.\\n\",\n    \"\\n\",\n    \"**Note:** Calling plot this way, downsamples the signal to reduce computing time. To plot the entire signal call\\n\",\n    \"```\\n\",\n    \"predicted.power_series_all_data().plot()\\n\",\n    \"ground_truth.power_series_all_data().plot()\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\nkiWEQiF27drFmjVr4rZXVlZGf3/++efZv38/U6ZM4cknn2T8+PE88cQTXH/99dEyV111FWbG3Llz\\n27Xv+eefZ/z48ZSVlQHwxhtvMHbsWPbu3cuMGTP40Y9+RG1tLRdeeCF/+tOfDvv3ShdNlImIiK9M\\nnjyZ73znOzQ0NFBYWMisWbMYO3ZsuymajkybNo1PPvmEFStW8NnPfja6fcaMGe3KlpeX8/rrr8cl\\nwN5zzz00NTWxdOlSjj32WACuvfZaRowYwbe+9S3efPNNACorK3nhhReij6uurqayspK1a9dSXV3N\\nyJEjqamp4a9//Su33HJLtNzDDz9MYWFh9Pcbb7yR4cOH893vfpdNmzZxzDHHMHToUM4991zmzJnD\\n3XffHS37zjvv8Le//S1uJOjWW29l3Lhx/OEPf4huu+WWWzj11FO55557ePXVVw/7N0sHBSMih1F7\\noJaSXiXkh/RxkWCor4e1azP/OqecAsXF6X/eq666ijvvvJMFCxZw8cUXs2DBgrgpkc4455g/fz6X\\nXXZZXCDSETPjpptuigtEWlpaWLhwIVdccUU0EAEYOHAgkydP5plnnmHv3r306dOHyspKfvazn7F+\\n/XpOOukkqqurGT9+PP3796e6upqbb76Z6upqgLiRkdhApL6+nv379zN69GhaWlpYvXo1xxxzDACT\\nJk1i6tSpfPTRRxx//PEAzJkzh6KiouhqonfffZf169dz7733snPnzri/w7hx43juuecO+zdLF51d\\nRQ6j34/7cc3fX8OzVzyb7aqI9Ii1a6GiIvOvs3IlZOKefeXl5Vx00UXMmjWLffv20dLSwle+8pXD\\nPu7TTz+lrq6O0047LaHXOe6449o9vr6+npNPPrld2ZEjR9LS0sLGjRsZOXIklZWVOOeorq5myJAh\\nrF69mh/+8IeUl5fzyCOPAOHRktLSUk4//fTo82zcuJF7772Xl19+mZqamuh2M4vLA/nqV7/KXXfd\\nxZw5c6JTSPPmzWPChAn06dMHgPXr1wNw3XXXddi+UChEbW1tdEonkxSMiCTg92t/n+0qiPSYU04J\\nBwo98TqZMnnyZG666Sa2bt3KhAkT6Nu3b9pfo3fv3ik/dtCgQRx//PEsXrw4OooyevRoysvLufPO\\nO9m4cSNLliyJW6Lc0tLCRRddxO7du5k+fTojRoygpKSEzZs3c/3118etIBo0aBCVlZXMnTuXadOm\\n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\\nu3fKjx00aBDHH388ixcvjo6ijB49mvLycu688042btzIkiVL4pYot7S0cNFFF7F7926mT5/OiBEj\\nKCkpYfPmzVx//fVxK4gGDRpEZWUlc+fOZdq0aSxbtowNGzbwk5/8JO75IJzwGhvwxGoNXDJNwYiI\\nAOGhWQ2MCEBxQXFGRix60hVXXMEtt9zC8uXLmTNnTkKP6d+/P6WlpfzlL39J6TX79+9PcXEx69at\\na7dvzZo1hEKhuFU3lZWVVFdXc9xxx3HGGWdQUlLC6aefTllZGa+88gqrVq2Ky+94//33Wb9+Pc8+\\n+yxf+9rXotsXLVrUYX0mTZrEbbfdxvr165kzZw4lJSVccskl0f3Dhw8HoG/fvlx44YUptTldtJpG\\nRER8p6SkhJ///OfMmDGDSy+9NKHHmBmXX345L7/8MqtWrUr6NUOhEF/84heZP38+GzZsiG7fvn07\\ns2fPprKyMm6kobKyko8++oi5c+dG80LMjNGjR/Poo4/S1NQUly+Sl5cH0O4aKo899liHyedXXnkl\\noVCIWbNmMW/ePC655JK40ZyKigqGDx/OT3/6U/bt29fu8Tt27Ej6b5AqjYyICBDOGRHxsrbXELn2\\n2muTfo4HH3yQhQsXMmbMGG6++WZGjhzJli1bmDdvHkuXLqW0tLTD12r1gx/8gEWLFnHeeecxZcoU\\n8vLy+MUvfkFjYyMPP/xwXNnWQGPdunU8+OCD0e1jxozhlVdeoaioiLPOOiu6/ZRTTmH48OHcfffd\\nbNq0idLSUl544QV2797dYV369+/PBRdcwKOPPsrevXuZNGlS3H4z45lnnmHixImcdtpp3HDDDQwZ\\nMoTNmzfz5ptvUlZWxvz585P+G6ZCwYhIwLWeVDs7uYp4RSJL09vei6bt74MHD2b58uXce++9zJo1\\ni7q6OoYMGcLEiRMpLi6Oe1xHTj31VKqrq5k+fToPPfQQLS0tnHvuudErwMY6+eSTOfroo9mxYwef\\n//zno9srKysxM8455xwKCgqi2/Pz81mwYAHf/OY3eeihhygqKuLLX/4yt912W6c5H5MmTeL111+n\\ntLSUiRMntts/duxYli1bxgMPPMBTTz3F3r17GThwIOecc07ckuJMM52AOmdmo4CVK1euZNSo5OZP\\n7fvhN6q7T39fr7PvG3169WHP9D3ZrkpGHPnjI6k5UEPjPY0U5BUc/gHiSatWraKiooJUzmciHTnc\\n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\\nQq7akXgX3CwzWyEPh23AJOCI3JzvNXgArFW4VVyIYmeMnQ8SIxEwxs4H8TPGzpdUQ31t7eAn0Jfx\\nLrNO4B+B7cO27XELdXt83O9BYHLYNglYjS/PvANwLXAGHsRmSG7/2+BjgLMoaC547Iyx8yXGOBhj\\n52sHxtj5Uurm2DfpBJsM3APMo7Ka5biw7X7gLHw10p8ANxEC1AB3As/hi709BozJ7TPrwhtACcb+\\nYmeMnS8xxsEYO187MMbOl1LXqSnDNGZ2L/AUsCKcZIuA2ZIm4V1lg81scTjxdgE+GqqeBBwGTDGz\\nCeZzxrN9dobX1Wa2zmJwRSh2xtj5QjsSY4szxs4X2hE1Y+x8STXULKsHGI+fPDPD5ytxR6TVwDdD\\n3hbA93GLd7su9lF4+O92ZoydLzHGwRg7Xzswxs6X0vqpaRFYzexxfErW7pKOMbPzgJm4k9Hbkgaa\\n2TJgNvAjwtLwVfsoxSJTtRQ7Y+x8kBiJgDF2PoifMXa+pPWVLQrUnC+TRgJXA0OAT5jZC5J2MLOF\\nTWtEHyt2xtj5IDEW3LReUex8ED9j7HxJ66qpa9OY2Qv4/PBhwF+EvIUKypdVZbGjllLsjLHzQWKM\\ngTF2PoifMXa+pHVVxAGcDTwBnCJpD/CVEq2qi8YaXLm1pIqdMXY+SIxr1cKMsfNB/Iyx8yUF9W/2\\nF5rZ25JmAy/iXtLRKXbG2PkgMcag2PkgfsbY+ZIqaqrPSFJSUlJSUlJStQodZ2uHcb7YGWPng8QY\\ng2Lng/gZY+drd6WekaSkpKSkpKRClSzNpKSkpKSkpEKVjJGkpKSkpKSkQpWMkaSkpKSkpKRClYyR\\npKSkpKSkpEKVjJGkpKSkpKSkQpWMkaSkpKSkpKRClYyRpKSkpKSkpEKVjJGkpKSkpKSkQpWMkaSk\\npKSkpKRClYyRpKSkpKSkpEL1R3aeK+xT/zRhAAAAAElFTkSuQmCC\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f5c83d2c090>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"result = DataSet(disag_filename)\\n\",\n    \"res_elec = result.buildings[1].elec\\n\",\n    \"predicted = res_elec['microwave']\\n\",\n    \"ground_truth = test_elec['microwave']\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"predicted.plot()\\n\",\n    \"ground_truth.plot()\\n\",\n    \"plt.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Finally let's see the metric results.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"============ Recall: 0.0370807004875\\n\",\n      \"============ Precision: 0.813554622514\\n\",\n      \"============ Accuracy: 0.278783362107\\n\",\n      \"============ F1 Score: 0.0709285741419\\n\",\n      \"============ Relative error in total energy: 0.768754882991\\n\",\n      \"============ Mean absolute error(in Watts): 19.2988829522\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import metrics\\n\",\n    \"rpaf = metrics.recall_precision_accuracy_f1(predicted, ground_truth)\\n\",\n    \"print(\\\"============ Recall: {}\\\".format(rpaf[0]))\\n\",\n    \"print(\\\"============ Precision: {}\\\".format(rpaf[1]))\\n\",\n    \"print(\\\"============ Accuracy: {}\\\".format(rpaf[2]))\\n\",\n    \"print(\\\"============ F1 Score: {}\\\".format(rpaf[3]))\\n\",\n    \"\\n\",\n    \"print(\\\"============ Relative error in total energy: {}\\\".format(metrics.relative_error_total_energy(predicted, ground_truth)))\\n\",\n    \"print(\\\"============ Mean absolute error(in Watts): {}\\\".format(metrics.mean_absolute_error(predicted, ground_truth)))\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python [conda env:nilmtk-env]\",\n   \"language\": \"python\",\n   \"name\": \"conda-env-nilmtk-env-py\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.12\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "GRU/README.md",
    "content": "# Recurrent (GRU) Network Energy Disaggregator\n\nAn attempt to create a \"lightweight\" recurrent network for energy disaggregation. Moreover, this architecture supports predicting the energy consumptions of more than one meters.\n\nSee example experiment [here](https://github.com/OdysseasKr/neural-disaggregator/blob/master/GRU/GRU-example.ipynb).\n"
  },
  {
    "path": "GRU/grudisaggregator.py",
    "content": "from __future__ import print_function, division\nimport random\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nimport pandas as pd\nimport numpy as np\nimport h5py\n\nfrom keras.models import load_model\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Conv1D, GRU, Bidirectional, Dropout\nfrom keras.utils import plot_model\n\nfrom nilmtk.utils import find_nearest\nfrom nilmtk.feature_detectors import cluster\nfrom nilmtk.legacy.disaggregate import Disaggregator\nfrom nilmtk.datastore import HDFDataStore\n\nclass GRUDisaggregator(Disaggregator):\n    '''Attempt to create a RNN Disaggregator\n\n    Attributes\n    ----------\n    model : keras Sequential model\n    mmax : the maximum value of the aggregate data\n\n    MIN_CHUNK_LENGTH : int\n       the minimum length of an acceptable chunk\n    '''\n\n    def __init__(self):\n        '''Initialize disaggregator\n        '''\n        self.MODEL_NAME = \"GRU\"\n        self.mmax = None\n        self.MIN_CHUNK_LENGTH = 100\n        self.model = self._create_model()\n\n    def train(self, mains, meter, epochs=1, batch_size=128, **load_kwargs):\n        '''Train\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter object for the aggregate data\n        meter : a nilmtk.ElecMeter object for the meter data\n        epochs : number of epochs to train\n        batch_size : size of batch used for training\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        main_power_series = mains.power_series(**load_kwargs)\n        meter_power_series = meter.power_series(**load_kwargs)\n\n        # Train chunks\n        run = True\n        mainchunk = next(main_power_series)\n        meterchunk = next(meter_power_series)\n        if self.mmax == None:\n            self.mmax = mainchunk.max()\n\n        while(run):\n            mainchunk = self._normalize(mainchunk, self.mmax)\n            meterchunk = self._normalize(meterchunk, self.mmax)\n\n            self.train_on_chunk(mainchunk, meterchunk, epochs, batch_size)\n            try:\n                mainchunk = next(main_power_series)\n                meterchunk = next(meter_power_series)\n            except:\n                run = False\n\n    def train_on_chunk(self, mainchunk, meterchunk, epochs, batch_size):\n        '''Train using only one chunk\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n\n        # Replace NaNs with 0s\n        mainchunk.fillna(0, inplace=True)\n        meterchunk.fillna(0, inplace=True)\n        ix = mainchunk.index.intersection(meterchunk.index)\n        mainchunk = np.array(mainchunk[ix])\n        meterchunk = np.array(meterchunk[ix])\n\n        mainchunk = np.reshape(mainchunk, (mainchunk.shape[0],1,1))\n\n        self.model.fit(mainchunk, meterchunk, epochs=epochs, batch_size=batch_size, shuffle=True)\n\n    def train_across_buildings(self, mainlist, meterlist, epochs=1, batch_size=128, **load_kwargs):\n        '''Train using data from multiple buildings\n\n        Parameters\n        ----------\n        mainlist : a list of nilmtk.ElecMeter objects for the aggregate data of each building\n        meterlist : a list of nilmtk.ElecMeter objects for the meter data of each building\n        batch_size : size of batch used for training\n        epochs : number of epochs to train\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        assert len(mainlist) == len(meterlist), \"Number of main and meter channels should be equal\"\n        num_meters = len(mainlist)\n\n        mainps = [None] * num_meters\n        meterps = [None] * num_meters\n        mainchunks = [None] * num_meters\n        meterchunks = [None] * num_meters\n\n        # Get generators of timeseries\n        for i,m in enumerate(mainlist):\n            mainps[i] = m.power_series(**load_kwargs)\n\n        for i,m in enumerate(meterlist):\n            meterps[i] = m.power_series(**load_kwargs)\n\n        # Get a chunk of data\n        for i in range(num_meters):\n            mainchunks[i] = next(mainps[i])\n            meterchunks[i] = next(meterps[i])\n        if self.mmax == None:\n            self.mmax = max([m.max() for m in mainchunks])\n\n\n        run = True\n        while(run):\n            # Normalize and train\n            mainchunks = [self._normalize(m, self.mmax) for m in mainchunks]\n            meterchunks = [self._normalize(m, self.mmax) for m in meterchunks]\n\n            self.train_across_buildings_chunk(mainchunks, meterchunks, epochs, batch_size)\n\n            # If more chunks, repeat\n            try:\n                for i in range(num_meters):\n                    mainchunks[i] = next(mainps[i])\n                    meterchunks[i] = next(meterps[i])\n            except:\n                run = False\n\n    def train_across_buildings_chunk(self, mainchunks, meterchunks, epochs, batch_size):\n        '''Train using only one chunk of data. This chunk consists of data from\n        all buildings.\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n        num_meters = len(mainchunks)\n        batch_size = int(batch_size/num_meters)\n        num_of_batches = [None] * num_meters\n\n        # Find common parts of timeseries\n        for i in range(num_meters):\n            mainchunks[i].fillna(0, inplace=True)\n            meterchunks[i].fillna(0, inplace=True)\n            ix = mainchunks[i].index.intersection(meterchunks[i].index)\n            m1 = mainchunks[i]\n            m2 = meterchunks[i]\n            mainchunks[i] = m1[ix]\n            meterchunks[i] = m2[ix]\n\n            num_of_batches[i] = int(len(ix)/batch_size) - 1\n\n        for e in range(epochs): # Iterate for every epoch\n            print(e)\n            batch_indexes = list(range(min(num_of_batches)))\n            random.shuffle(batch_indexes)\n\n            for bi, b in enumerate(batch_indexes): # Iterate for every batch\n                print(\"Batch {} of {}\".format(bi,num_of_batches), end=\"\\r\")\n                sys.stdout.flush()\n                X_batch = np.empty((batch_size*num_meters, 1, 1))\n                Y_batch = np.empty((batch_size*num_meters, 1))\n\n                # Create a batch out of data from all buildings\n                for i in range(num_meters):\n                    mainpart = mainchunks[i]\n                    meterpart = meterchunks[i]\n                    mainpart = mainpart[b*batch_size:(b+1)*batch_size]\n                    meterpart = meterpart[b*batch_size:(b+1)*batch_size]\n                    X = np.reshape(mainpart, (batch_size, 1, 1))\n                    Y = np.reshape(meterpart, (batch_size, 1))\n\n                    X_batch[i*batch_size:(i+1)*batch_size] = np.array(X)\n                    Y_batch[i*batch_size:(i+1)*batch_size] = np.array(Y)\n\n                # Shuffle data\n                p = np.random.permutation(len(X_batch))\n                X_batch, Y_batch = X_batch[p], Y_batch[p]\n\n                self.model.train_on_batch(X_batch, Y_batch)\n            print(\"\\n\")\n\n    def disaggregate(self, mains, output_datastore, meter_metadata, **load_kwargs):\n        '''Disaggregate mains according to the model learnt previously.\n\n        Parameters\n        ----------\n        mains : nilmtk.ElecMeter\n        meter_metadata: a nilmtk.ElecMeter of the observed meter used for storing the metadata\n        output_datastore : instance of nilmtk.DataStore subclass\n            For storing power predictions from disaggregation algorithm.\n        **load_kwargs : key word arguments\n            Passed to `mains.power_series(**kwargs)`\n        '''\n\n        load_kwargs = self._pre_disaggregation_checks(load_kwargs)\n\n        load_kwargs.setdefault('sample_period', 60)\n        load_kwargs.setdefault('sections', mains.good_sections())\n\n        timeframes = []\n        building_path = '/building{}'.format(mains.building())\n        mains_data_location = building_path + '/elec/meter1'\n        data_is_available = False\n\n        for chunk in mains.power_series(**load_kwargs):\n            if len(chunk) < self.MIN_CHUNK_LENGTH:\n                continue\n            print(\"New sensible chunk: {}\".format(len(chunk)))\n\n            timeframes.append(chunk.timeframe)\n            measurement = chunk.name\n            chunk2 = self._normalize(chunk, self.mmax)\n\n            appliance_power = self.disaggregate_chunk(chunk2)\n            appliance_power[appliance_power < 0] = 0\n            appliance_power = self._denormalize(appliance_power, self.mmax)\n\n            # Append prediction to output\n            data_is_available = True\n            cols = pd.MultiIndex.from_tuples([chunk.name])\n            meter_instance = meter_metadata.instance()\n            df = pd.DataFrame(\n                appliance_power.values, index=appliance_power.index,\n                columns=cols, dtype=\"float32\")\n            key = '{}/elec/meter{}'.format(building_path, meter_instance)\n            output_datastore.append(key, df)\n\n            # Append aggregate data to output\n            mains_df = pd.DataFrame(chunk, columns=cols, dtype=\"float32\")\n            output_datastore.append(key=mains_data_location, value=mains_df)\n\n        # Save metadata to output\n        if data_is_available:\n            self._save_metadata_for_disaggregation(\n                output_datastore=output_datastore,\n                sample_period=load_kwargs['sample_period'],\n                measurement=measurement,\n                timeframes=timeframes,\n                building=mains.building(),\n                meters=[meter_metadata]\n            )\n\n    def disaggregate_chunk(self, mains):\n        '''In-memory disaggregation.\n\n        Parameters\n        ----------\n        mains : pd.Series\n        Returns\n        -------\n        appliance_powers : pd.DataFrame where each column represents a\n            disaggregated appliance.  Column names are the integer index\n            into `self.model` for the appliance in question.\n        '''\n        up_limit = len(mains)\n\n        mains.fillna(0, inplace=True)\n\n        X_batch = np.array(mains)\n        X_batch = np.reshape(X_batch, (X_batch.shape[0],1,1))\n\n        pred = self.model.predict(X_batch, batch_size=128)\n        pred = np.reshape(pred, (len(pred)))\n        column = pd.Series(pred, index=mains.index[:len(X_batch)], name=0)\n\n        appliance_powers_dict = {}\n        appliance_powers_dict[0] = column\n        appliance_powers = pd.DataFrame(appliance_powers_dict)\n        return appliance_powers\n\n    def import_model(self, filename):\n        '''Loads keras model from h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n\n        Returns: Keras model\n        '''\n        self.model = load_model(filename)\n        with h5py.File(filename, 'a') as hf:\n            ds = hf.get('disaggregator-data').get('mmax')\n            self.mmax = np.array(ds)[0]\n\n    def export_model(self, filename):\n        '''Saves keras model to h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n        '''\n        self.model.save(filename)\n        with h5py.File(filename, 'a') as hf:\n            gr = hf.create_group('disaggregator-data')\n            gr.create_dataset('mmax', data = [self.mmax])\n\n    def _normalize(self, chunk, mmax):\n        '''Normalizes timeseries\n\n        Parameters\n        ----------\n        chunk : the timeseries to normalize\n        max : max value of the powerseries\n\n        Returns: Normalized timeseries\n        '''\n        tchunk = chunk / mmax\n        return tchunk\n\n    def _denormalize(self, chunk, mmax):\n        '''Deormalizes timeseries\n        Note: This is not entirely correct\n\n        Parameters\n        ----------\n        chunk : the timeseries to denormalize\n        max : max value used for normalization\n\n        Returns: Denormalized timeseries\n        '''\n        tchunk = chunk * mmax\n        return tchunk\n\n    def _create_model(self):\n        '''Creates the ANN model\n        '''\n        model = Sequential()\n\n        # 1D Conv\n        model.add(Conv1D(16, 4, activation=\"relu\", padding=\"same\", strides=1, input_shape=(1,1)))\n        model.add(Conv1D(8, 4, activation=\"relu\", padding=\"same\", strides=1))\n\n        # Bi-directional LSTMs\n        model.add(Bidirectional(GRU(64, return_sequences=True, stateful=False), merge_mode='concat'))\n        model.add(Bidirectional(GRU(128, return_sequences=False, stateful=False), merge_mode='concat'))\n\n        # Fully Connected Layers\n        model.add(Dense(64, activation='relu'))\n        model.add(Dense(1, activation='linear'))\n\n        model.compile(loss='mse', optimizer='adam')\n        plot_model(model, to_file='model.png', show_shapes=True)\n        return model\n"
  },
  {
    "path": "GRU/metrics.py",
    "content": "from nilmtk.electric import align_two_meters\nimport numpy as np\n\ndef tp_tn_fp_fn(states_pred, states_ground):\n    tp = np.sum(np.logical_and(states_pred == 1, states_ground == 1))\n    fp = np.sum(np.logical_and(states_pred == 1, states_ground == 0))\n    fn = np.sum(np.logical_and(states_pred == 0, states_ground == 1))\n    tn = np.sum(np.logical_and(states_pred == 0, states_ground == 0))\n    return tp, tn, fp, fn\n\ndef recall_precision_accuracy_f1(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    threshold = ground.on_power_threshold()\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        sum_samples += len(chunk)\n        pr = np.array([0 if (p)<threshold else 1 for p in chunk.iloc[:,0]])\n        gr = np.array([0 if p<threshold else 1 for p in chunk.iloc[:,1]])\n\n        tp, tn, fp, fn = tp_tn_fp_fn(pr,gr)\n        p = sum(pr)\n        n = len(pr) - p\n\n        chunk_results.append([tp,tn,fp,fn,p,n])\n\n    if sum_samples == 0:\n        return None\n    else:\n        [tp,tn,fp,fn,p,n] = np.sum(chunk_results, axis=0)\n\n        res_recall = recall(tp,fn)\n        res_precision = precision(tp,fp)\n        res_f1 = f1(res_precision,res_recall)\n        res_accuracy = accuracy(tp,tn,p,n)\n\n        return (res_recall,res_precision,res_accuracy,res_f1)\n\ndef relative_error_total_energy(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        E_pred = sum(chunk.iloc[:,0])\n        E_ground = sum(chunk.iloc[:,1])\n\n        chunk_results.append([\n                            E_pred,\n                            E_ground\n                            ])\n    if sum_samples == 0:\n        return None\n    else:\n        [E_pred, E_ground] = np.sum(chunk_results,axis=0)\n        return abs(E_pred - E_ground) / float(max(E_pred,E_ground))\n\ndef mean_absolute_error(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    total_sum = 0.0\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        total_sum += sum(abs((chunk.iloc[:,0]) - chunk.iloc[:,1]))\n    if sum_samples == 0:\n        return None\n    else:\n        return total_sum / sum_samples\n\n\ndef recall(tp,fn):\n    return tp/float(tp+fn)\n\ndef precision(tp,fp):\n    return tp/float(tp+fp)\n\ndef f1(prec,rec):\n    return 2 * (prec*rec) / float(prec+rec)\n\ndef accuracy(tp, tn, p, n):\n    return (tp + tn) / float(p + n)\n"
  },
  {
    "path": "GRU/redd-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom nilmtk.elecmeter import ElecMeterID\nimport metrics\nfrom grudisaggregator import GRUDisaggregator\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('redd.h5')\ntrain.set_window(end=\"30-4-2011\")\ntest = DataSet('redd.h5')\ntest.set_window(start=\"30-4-2011\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'fridge'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains().all_meters()[0]\ntest_mains = test_elec.mains().all_meters()[0]\ngru = GRUDisaggregator()\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    gru.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    gru.export_model(\"REDD-GRU-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\n    print(\"CHECKPOINT {}\".format(epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = 'disag-out.h5'\noutput = HDFDataStore(disag_filename, 'w')\ngru.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "GRU/ukdale-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom grudisaggregator import GRUDisaggregator\nimport metrics\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('ukdale.h5')\ntest = DataSet('ukdale.h5')\n\ntrain.set_window(start=\"13-4-2013\", end=\"1-1-2014\")\ntest.set_window(start=\"1-1-2014\", end=\"30-3-2014\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'microwave'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains()\ntest_mains = test_elec.mains()\ngru = GRUDisaggregator()\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    gru.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    gru.export_model(\"UKDALE-GRU-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\n    print(\"CHECKPOINT {}\".format(epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = \"disag-out.h5\"\noutput = HDFDataStore(disag_filename, 'w')\ngru.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Odysseas (Ody) Krystalakos\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# neural-disaggregator\n\nImplementations of NILM disaggegregators using Neural Networks, using [NILMTK](https://github.com/NILMTK/NILMTK) and Keras.\n\nMost of the architectures are based on [Neural NILM: Deep Neural Networks Applied to Energy Disaggregation](https://arxiv.org/pdf/1507.06594.pdf) by Jack Kelly and William Knottenbelt.\n\nThe implemented models are:\n- Denoising autoencoder (DAE) as mentioned in [Neural NILM](https://arxiv.org/pdf/1507.06594.pdf) (see [example](https://github.com/OdysseasKr/neural-disaggregator/blob/master/DAE/DAE-example.ipynb))\n- Recurrent network with LSTM neurons as mentioned in [Neural NILM](https://arxiv.org/pdf/1507.06594.pdf) (see [example](https://github.com/OdysseasKr/neural-disaggregator/tree/master/RNN/RNN-example.ipynb))\n- Recurrent network with GRU. A variation of the LSTM network in order to compare the two types of RNNs (see [example](https://github.com/OdysseasKr/neural-disaggregator/blob/master/GRU/GRU-example.ipynb))\n- Window GRU. A variation of the GRU network in that uses a window of data as input. As described in [Sliding Window Approach for Online Energy Disaggregation Using Artificial Neural Networks](https://dl.acm.org/citation.cfm?id=3201011) by Krystalakos, Nalmpantis and Vrakas (see [example](https://github.com/OdysseasKr/neural-disaggregator/blob/master/WindowGRU/Window-GRU-example.ipynb))\n- Short Sequence to Point Network based on the architecture in [original paper](https://arxiv.org/abs/1612.09106) (see [example](https://github.com/OdysseasKr/neural-disaggregator/blob/master/ShortSeq2Point/ShortSeq2Point-example.ipynb))\n\n_Note: If you are looking for the LookbackGRU folder, it has been moved to http://github.com/OdysseasKr/online-nilm. I try to keep this repo clean by using the same train and test sets for all architectures._\n"
  },
  {
    "path": "RNN/README.md",
    "content": "# Recurrent(LSTM) Energy Disaggregator\n\nAs described in:\n\n[Neural NILM: Deep Neural Networks Applied to Energy Disaggregation](https://arxiv.org/pdf/1507.06594.pdf) by Jack Kelly and William Knottenbelt\n\nSee example experiment [here](https://github.com/OdysseasKr/neural-disaggregator/blob/master/RNN/RNN-example.ipynb).\n"
  },
  {
    "path": "RNN/RNN-example.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# How to use the RNN Disaggregator with NILMTK\\n\",\n    \"\\n\",\n    \"This is an example on how to train and use the Recurrent Network (RNN) disaggregator on the [REDD](http://redd.csail.mit.edu/) dataset using [NILMTK](https://github.com/nilmtk/NILMTK/).\\n\",\n    \"\\n\",\n    \"This network was described in the [Neural NILM](https://arxiv.org/pdf/1507.06594.pdf) paper.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"First of all, we need to train the RNNDisaggregator using the train data. For this example, both train and test data are consumption data of the microwave of the first REDD building.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import warnings; warnings.filterwarnings('ignore')\\n\",\n    \"\\n\",\n    \"from nilmtk import DataSet\\n\",\n    \"train = DataSet('redd.h5')\\n\",\n    \"train.set_window(end=\\\"30-4-2011\\\") #Use data only until 4/30/2011\\n\",\n    \"train_elec = train.buildings[1].elec\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Next, we need to define the disaggregator model.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Using TensorFlow backend.\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from rnndisaggregator import RNNDisaggregator\\n\",\n    \"rnn = RNNDisaggregator()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Then train the model. We need to input the train data as well as their sample period. Also, we need to pass the desired number of training epochs. Finally, save the model for later use.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Epoch 1/5\\n\",\n      \"1003066/1003066 [==============================] - 127s - loss: 6.1677e-04   \\n\",\n      \"Epoch 2/5\\n\",\n      \"1003066/1003066 [==============================] - 126s - loss: 6.1496e-04   \\n\",\n      \"Epoch 3/5\\n\",\n      \"1003066/1003066 [==============================] - 118s - loss: 6.1347e-04   \\n\",\n      \"Epoch 4/5\\n\",\n      \"1003066/1003066 [==============================] - 114s - loss: 6.1133e-04   \\n\",\n      \"Epoch 5/5\\n\",\n      \"1003066/1003066 [==============================] - 114s - loss: 6.1094e-04   \\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"train_mains = train_elec.mains().all_meters()[0] # The aggregated meter that provides the input\\n\",\n    \"train_meter = train_elec.submeters()['microwave'] # The microwave meter that is used as a training target\\n\",\n    \"\\n\",\n    \"rnn.train(train_mains, train_meter, epochs=5, sample_period=1)\\n\",\n    \"rnn.export_model(\\\"model-redd5.h5\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now that the model is trained, we can use it to disaggregate energy data. Let's test it on the rest of the data from building 1.\\n\",\n    \"\\n\",\n    \"First we use the model to predict the microwave consumption. The results are saved automatically in a .h5 datastore.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"New sensible chunk: 121482\\n\",\n      \"New sensible chunk: 112661\\n\",\n      \"New sensible chunk: 87770\\n\",\n      \"New sensible chunk: 54084\\n\",\n      \"New sensible chunk: 2660\\n\",\n      \"New sensible chunk: 33513\\n\",\n      \"New sensible chunk: 138535\\n\",\n      \"New sensible chunk: 32514\\n\",\n      \"New sensible chunk: 27255\\n\",\n      \"New sensible chunk: 34833\\n\",\n      \"New sensible chunk: 100831\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"test = DataSet('redd.h5')\\n\",\n    \"test.set_window(start=\\\"30-4-2011\\\")\\n\",\n    \"test_elec = test.buildings[1].elec\\n\",\n    \"test_mains = test_elec.mains().all_meters()[0]\\n\",\n    \"\\n\",\n    \"disag_filename = 'disag-out.h5' # The filename of the resulting datastore\\n\",\n    \"from nilmtk.datastore import HDFDataStore\\n\",\n    \"output = HDFDataStore(disag_filename, 'w')\\n\",\n    \"\\n\",\n    \"# test_mains: The aggregated signal meter\\n\",\n    \"# output: The output datastore\\n\",\n    \"# train_meter: This is used in order to copy the metadata of the train meter into the datastore\\n\",\n    \"rnn.disaggregate(test_mains, output, train_meter, sample_period=1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Let's plot the results and compare them to the ground truth signal.\\n\",\n    \"\\n\",\n    \"**Note:** Calling plot this way, downsamples the signal to reduce computing time. To plot the entire signal call\\n\",\n    \"```\\n\",\n    \"predicted.power_series_all_data().plot()\\n\",\n    \"ground_truth.power_series_all_data().plot()\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f68201d06d0>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"result = DataSet(disag_filename)\\n\",\n    \"res_elec = result.buildings[1].elec\\n\",\n    \"predicted = res_elec['microwave']\\n\",\n    \"ground_truth = test_elec['microwave']\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"predicted.plot()\\n\",\n    \"ground_truth.plot()\\n\",\n    \"plt.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Finally let's see the metric results.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"============ Recall: 0.997835349341\\n\",\n      \"============ Precision: 0.742378777703\\n\",\n      \"============ Accuracy: 0.741308963402\\n\",\n      \"============ F1 Score: 0.851357054837\\n\",\n      \"============ Relative error in total energy: 0.871686427835\\n\",\n      \"============ Mean absolute error(in Watts): 32.2338755931\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import metrics\\n\",\n    \"rpaf = metrics.recall_precision_accuracy_f1(predicted, ground_truth)\\n\",\n    \"print(\\\"============ Recall: {}\\\".format(rpaf[0]))\\n\",\n    \"print(\\\"============ Precision: {}\\\".format(rpaf[1]))\\n\",\n    \"print(\\\"============ Accuracy: {}\\\".format(rpaf[2]))\\n\",\n    \"print(\\\"============ F1 Score: {}\\\".format(rpaf[3]))\\n\",\n    \"\\n\",\n    \"print(\\\"============ Relative error in total energy: {}\\\".format(metrics.relative_error_total_energy(predicted, ground_truth)))\\n\",\n    \"print(\\\"============ Mean absolute error(in Watts): {}\\\".format(metrics.mean_absolute_error(predicted, ground_truth)))\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"anaconda-cloud\": {},\n  \"kernelspec\": {\n   \"display_name\": \"Python [conda env:nilmtk-env]\",\n   \"language\": \"python\",\n   \"name\": \"conda-env-nilmtk-env-py\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.12\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "RNN/metrics.py",
    "content": "from nilmtk.electric import align_two_meters\nimport numpy as np\n\ndef tp_tn_fp_fn(states_pred, states_ground):\n    tp = np.sum(np.logical_and(states_pred == 1, states_ground == 1))\n    fp = np.sum(np.logical_and(states_pred == 1, states_ground == 0))\n    fn = np.sum(np.logical_and(states_pred == 0, states_ground == 1))\n    tn = np.sum(np.logical_and(states_pred == 0, states_ground == 0))\n    return tp, tn, fp, fn\n\ndef recall_precision_accuracy_f1(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    threshold = ground.on_power_threshold()\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        sum_samples += len(chunk)\n        pr = np.array([0 if (p)<threshold else 1 for p in chunk.iloc[:,0]])\n        gr = np.array([0 if p<threshold else 1 for p in chunk.iloc[:,1]])\n\n        tp, tn, fp, fn = tp_tn_fp_fn(pr,gr)\n        p = sum(pr)\n        n = len(pr) - p\n\n        chunk_results.append([tp,tn,fp,fn,p,n])\n\n    if sum_samples == 0:\n        return None\n    else:\n        [tp,tn,fp,fn,p,n] = np.sum(chunk_results, axis=0)\n\n        res_recall = recall(tp,fn)\n        res_precision = precision(tp,fp)\n        res_f1 = f1(res_precision,res_recall)\n        res_accuracy = accuracy(tp,tn,p,n)\n\n        return (res_recall,res_precision,res_accuracy,res_f1)\n\ndef relative_error_total_energy(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        E_pred = sum(chunk.iloc[:,0])\n        E_ground = sum(chunk.iloc[:,1])\n\n        chunk_results.append([\n                            E_pred,\n                            E_ground\n                            ])\n    if sum_samples == 0:\n        return None\n    else:\n        [E_pred, E_ground] = np.sum(chunk_results,axis=0)\n        return abs(E_pred - E_ground) / float(max(E_pred,E_ground))\n\ndef mean_absolute_error(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    total_sum = 0.0\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        total_sum += sum(abs((chunk.iloc[:,0]) - chunk.iloc[:,1]))\n    if sum_samples == 0:\n        return None\n    else:\n        return total_sum / sum_samples\n\n\ndef recall(tp,fn):\n    return tp/float(tp+fn)\n\ndef precision(tp,fp):\n    return tp/float(tp+fp)\n\ndef f1(prec,rec):\n    return 2 * (prec*rec) / float(prec+rec)\n\ndef accuracy(tp, tn, p, n):\n    return (tp + tn) / float(p + n)\n"
  },
  {
    "path": "RNN/redd-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom nilmtk.elecmeter import ElecMeterID\nimport metrics\nfrom rnndisaggregator import RNNDisaggregator\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('redd.h5')\ntrain.set_window(end=\"30-4-2011\")\ntest = DataSet('redd.h5')\ntest.set_window(start=\"30-4-2011\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'fridge'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains().all_meters()[0]\ntest_mains = test_elec.mains().all_meters()[0]\nrnn = RNNDisaggregator()\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    rnn.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    rnn.export_model(\"REDD-RNN-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\n    print(\"CHECKPOINT {}\".format(epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = 'disag-out.h5'\noutput = HDFDataStore(disag_filename, 'w')\nrnn.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "RNN/rnndisaggregator.py",
    "content": "from __future__ import print_function, division\nimport random\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nimport pandas as pd\nimport numpy as np\nimport h5py\n\nfrom keras.models import load_model\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Conv1D, LSTM, Bidirectional, Dropout\nfrom keras.utils import plot_model\n\nfrom nilmtk.utils import find_nearest\nfrom nilmtk.feature_detectors import cluster\nfrom nilmtk.legacy.disaggregate import Disaggregator\nfrom nilmtk.datastore import HDFDataStore\n\nclass RNNDisaggregator(Disaggregator):\n    '''Attempt to create a RNN Disaggregator\n\n    Attributes\n    ----------\n    model : keras Sequential model\n    mmax : the maximum value of the aggregate data\n\n    MIN_CHUNK_LENGTH : int\n       the minimum length of an acceptable chunk\n    '''\n\n    def __init__(self):\n        '''Initialize disaggregator\n        '''\n        self.MODEL_NAME = \"LSTM\"\n        self.mmax = None\n        self.MIN_CHUNK_LENGTH = 100\n        self.model = self._create_model()\n\n    def train(self, mains, meter, epochs=1, batch_size=128, **load_kwargs):\n        '''Train\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter object for the aggregate data\n        meter : a nilmtk.ElecMeter object for the meter data\n        epochs : number of epochs to train\n        batch_size : size of batch used for training\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        main_power_series = mains.power_series(**load_kwargs)\n        meter_power_series = meter.power_series(**load_kwargs)\n\n        # Train chunks\n        run = True\n        mainchunk = next(main_power_series)\n        meterchunk = next(meter_power_series)\n        if self.mmax == None:\n            self.mmax = mainchunk.max()\n\n        while(run):\n            mainchunk = self._normalize(mainchunk, self.mmax)\n            meterchunk = self._normalize(meterchunk, self.mmax)\n\n            self.train_on_chunk(mainchunk, meterchunk, epochs, batch_size)\n            try:\n                mainchunk = next(main_power_series)\n                meterchunk = next(meter_power_series)\n            except:\n                run = False\n\n    def train_on_chunk(self, mainchunk, meterchunk, epochs, batch_size):\n        '''Train using only one chunk\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n\n        # Replace NaNs with 0s\n        mainchunk.fillna(0, inplace=True)\n        meterchunk.fillna(0, inplace=True)\n        ix = mainchunk.index.intersection(meterchunk.index)\n        mainchunk = np.array(mainchunk[ix])\n        meterchunk = np.array(meterchunk[ix])\n\n        mainchunk = np.reshape(mainchunk, (mainchunk.shape[0],1,1))\n\n        self.model.fit(mainchunk, meterchunk, epochs=epochs, batch_size=batch_size, shuffle=True)\n\n    def train_across_buildings(self, mainlist, meterlist, epochs=1, batch_size=128, **load_kwargs):\n        '''Train using data from multiple buildings\n\n        Parameters\n        ----------\n        mainlist : a list of nilmtk.ElecMeter objects for the aggregate data of each building\n        meterlist : a list of nilmtk.ElecMeter objects for the meter data of each building\n        batch_size : size of batch used for training\n        epochs : number of epochs to train\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        assert len(mainlist) == len(meterlist), \"Number of main and meter channels should be equal\"\n        num_meters = len(mainlist)\n\n        mainps = [None] * num_meters\n        meterps = [None] * num_meters\n        mainchunks = [None] * num_meters\n        meterchunks = [None] * num_meters\n\n        # Get generators of timeseries\n        for i,m in enumerate(mainlist):\n            mainps[i] = m.power_series(**load_kwargs)\n\n        for i,m in enumerate(meterlist):\n            meterps[i] = m.power_series(**load_kwargs)\n\n        # Get a chunk of data\n        for i in range(num_meters):\n            mainchunks[i] = next(mainps[i])\n            meterchunks[i] = next(meterps[i])\n        if self.mmax == None:\n            self.mmax = max([m.max() for m in mainchunks])\n\n\n        run = True\n        while(run):\n            # Normalize and train\n            mainchunks = [self._normalize(m, self.mmax) for m in mainchunks]\n            meterchunks = [self._normalize(m, self.mmax) for m in meterchunks]\n\n            self.train_across_buildings_chunk(mainchunks, meterchunks, epochs, batch_size)\n\n            # If more chunks, repeat\n            try:\n                for i in range(num_meters):\n                    mainchunks[i] = next(mainps[i])\n                    meterchunks[i] = next(meterps[i])\n            except:\n                run = False\n\n    def train_across_buildings_chunk(self, mainchunks, meterchunks, epochs, batch_size):\n        '''Train using only one chunk of data. This chunk consists of data from\n        all buildings.\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n        num_meters = len(mainchunks)\n        batch_size = int(batch_size/num_meters)\n        num_of_batches = [None] * num_meters\n\n        # Find common parts of timeseries\n        for i in range(num_meters):\n            mainchunks[i].fillna(0, inplace=True)\n            meterchunks[i].fillna(0, inplace=True)\n            ix = mainchunks[i].index.intersection(meterchunks[i].index)\n            m1 = mainchunks[i]\n            m2 = meterchunks[i]\n            mainchunks[i] = m1[ix]\n            meterchunks[i] = m2[ix]\n\n            num_of_batches[i] = int(len(ix)/batch_size) - 1\n\n        for e in range(epochs): # Iterate for every epoch\n            print(e)\n            batch_indexes = list(range(min(num_of_batches)))\n            random.shuffle(batch_indexes)\n\n            for bi, b in enumerate(batch_indexes): # Iterate for every batch\n                print(\"Batch {} of {}\".format(bi,num_of_batches), end=\"\\r\")\n                sys.stdout.flush()\n                X_batch = np.empty((batch_size*num_meters, 1, 1))\n                Y_batch = np.empty((batch_size*num_meters, 1))\n\n                # Create a batch out of data from all buildings\n                for i in range(num_meters):\n                    mainpart = mainchunks[i]\n                    meterpart = meterchunks[i]\n                    mainpart = mainpart[b*batch_size:(b+1)*batch_size]\n                    meterpart = meterpart[b*batch_size:(b+1)*batch_size]\n                    X = np.reshape(mainpart, (batch_size, 1, 1))\n                    Y = np.reshape(meterpart, (batch_size, 1))\n\n                    X_batch[i*batch_size:(i+1)*batch_size] = np.array(X)\n                    Y_batch[i*batch_size:(i+1)*batch_size] = np.array(Y)\n\n                # Shuffle data\n                p = np.random.permutation(len(X_batch))\n                X_batch, Y_batch = X_batch[p], Y_batch[p]\n\n                # Train model\n                self.model.train_on_batch(X_batch, Y_batch)\n            print(\"\\n\")\n\n    def disaggregate(self, mains, output_datastore, meter_metadata, **load_kwargs):\n        '''Disaggregate mains according to the model learnt previously.\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter of aggregate data\n        meter_metadata: a nilmtk.ElecMeter of the observed meter used for storing the metadata\n        output_datastore : instance of nilmtk.DataStore subclass\n            For storing power predictions from disaggregation algorithm.\n        **load_kwargs : key word arguments\n            Passed to `mains.power_series(**kwargs)`\n        '''\n\n        load_kwargs = self._pre_disaggregation_checks(load_kwargs)\n\n        load_kwargs.setdefault('sample_period', 60)\n        load_kwargs.setdefault('sections', mains.good_sections())\n\n        timeframes = []\n        building_path = '/building{}'.format(mains.building())\n        mains_data_location = building_path + '/elec/meter1'\n        data_is_available = False\n\n        for chunk in mains.power_series(**load_kwargs):\n            if len(chunk) < self.MIN_CHUNK_LENGTH:\n                continue\n            print(\"New sensible chunk: {}\".format(len(chunk)))\n\n            timeframes.append(chunk.timeframe)\n            measurement = chunk.name\n            chunk2 = self._normalize(chunk, self.mmax)\n\n            appliance_power = self.disaggregate_chunk(chunk2)\n            appliance_power[appliance_power < 0] = 0\n            appliance_power = self._denormalize(appliance_power, self.mmax)\n\n            # Append prediction to output\n            data_is_available = True\n            cols = pd.MultiIndex.from_tuples([chunk.name])\n            meter_instance = meter_metadata.instance()\n            df = pd.DataFrame(\n                appliance_power.values, index=appliance_power.index,\n                columns=cols, dtype=\"float32\")\n            key = '{}/elec/meter{}'.format(building_path, meter_instance)\n            output_datastore.append(key, df)\n\n            # Append aggregate data to output\n            mains_df = pd.DataFrame(chunk, columns=cols, dtype=\"float32\")\n            output_datastore.append(key=mains_data_location, value=mains_df)\n\n        # Save metadata to output\n        if data_is_available:\n            self._save_metadata_for_disaggregation(\n                output_datastore=output_datastore,\n                sample_period=load_kwargs['sample_period'],\n                measurement=measurement,\n                timeframes=timeframes,\n                building=mains.building(),\n                meters=[meter_metadata]\n            )\n\n    def disaggregate_chunk(self, mains):\n        '''In-memory disaggregation.\n\n        Parameters\n        ----------\n        mains : pd.Series of aggregate data\n        Returns\n        -------\n        appliance_powers : pd.DataFrame where each column represents a\n            disaggregated appliance.  Column names are the integer index\n            into `self.model` for the appliance in question.\n        '''\n        up_limit = len(mains)\n\n        mains.fillna(0, inplace=True)\n\n        X_batch = np.array(mains)\n        X_batch = np.reshape(X_batch, (X_batch.shape[0],1,1))\n\n        pred = self.model.predict(X_batch, batch_size=128)\n        pred = np.reshape(pred, (len(pred)))\n        column = pd.Series(pred, index=mains.index[:len(X_batch)], name=0)\n\n        appliance_powers_dict = {}\n        appliance_powers_dict[0] = column\n        appliance_powers = pd.DataFrame(appliance_powers_dict)\n        return appliance_powers\n\n    def import_model(self, filename):\n        '''Loads keras model from h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n\n        Returns: Keras model\n        '''\n        self.model = load_model(filename)\n        with h5py.File(filename, 'a') as hf:\n            ds = hf.get('disaggregator-data').get('mmax')\n            self.mmax = np.array(ds)[0]\n\n    def export_model(self, filename):\n        '''Saves keras model to h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n        '''\n        self.model.save(filename)\n        with h5py.File(filename, 'a') as hf:\n            gr = hf.create_group('disaggregator-data')\n            gr.create_dataset('mmax', data = [self.mmax])\n\n    def _normalize(self, chunk, mmax):\n        '''Normalizes timeseries\n\n        Parameters\n        ----------\n        chunk : the timeseries to normalize\n        max : max value of the powerseries\n\n        Returns: Normalized timeseries\n        '''\n        tchunk = chunk / mmax\n        return tchunk\n\n    def _denormalize(self, chunk, mmax):\n        '''Deormalizes timeseries\n        Note: This is not entirely correct\n\n        Parameters\n        ----------\n        chunk : the timeseries to denormalize\n        max : max value used for normalization\n\n        Returns: Denormalized timeseries\n        '''\n        tchunk = chunk * mmax\n        return tchunk\n\n    def _create_model(self):\n        '''Creates the RNN module described in the paper\n        '''\n        model = Sequential()\n\n        # 1D Conv\n        model.add(Conv1D(16, 4, activation=\"linear\", input_shape=(1,1), padding=\"same\", strides=1))\n\n        #Bi-directional LSTMs\n        model.add(Bidirectional(LSTM(128, return_sequences=True, stateful=False), merge_mode='concat'))\n        model.add(Bidirectional(LSTM(256, return_sequences=False, stateful=False), merge_mode='concat'))\n\n        # Fully Connected Layers\n        model.add(Dense(128, activation='tanh'))\n        model.add(Dense(1, activation='linear'))\n\n        model.compile(loss='mse', optimizer='adam')\n        plot_model(model, to_file='model.png', show_shapes=True)\n\n        return model\n"
  },
  {
    "path": "RNN/ukdale-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom rnndisaggregator import RNNDisaggregator\nimport metrics\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('ukdale.h5')\ntest = DataSet('ukdale.h5')\n\ntrain.set_window(start=\"13-4-2013\", end=\"1-1-2014\")\ntest.set_window(start=\"1-1-2014\", end=\"30-3-2014\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'kettle'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains()\ntest_mains = test_elec.mains()\nrnn = RNNDisaggregator()\n\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    print(\"CHECKPOINT {}\".format(epochs))\n    rnn.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    rnn.export_model(\"UKDALE-RNN-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = \"disag-out.h5\"\noutput = HDFDataStore(disag_filename, 'w')\nrnn.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "ShortSeq2Point/README.md",
    "content": "# Short Sequence to Point Energy Disaggregator\n\nBased on [Sequence-to-point learning with neural networks for nonintrusive load monitoring](https://arxiv.org/abs/1612.09106) by Zhang et al. Sequence-to-point learning with neural networks for nonintrusive load monitoring.\n\nThe main changes are the shorter window size as well as some dropout layers.\n\nSee example experiment [here](https://github.com/OdysseasKr/neural-disaggregator/blob/master/ShortSeq2Point/ShortSeq2Point-example.ipynb).\n"
  },
  {
    "path": "ShortSeq2Point/ShortSeq2Point-example.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# How to use the Short Sequence-to-Point with NILMTK\\n\",\n    \"\\n\",\n    \"This is an example on how to train and use the Short Sequence-to-Point disaggregator on the [REDD](http://redd.csail.mit.edu/) dataset using [NILMTK](https://github.com/nilmtk/NILMTK/).\\n\",\n    \"\\n\",\n    \"This network is a variation of the short sequence-to-point architecture described here:\\n\",\n    \"\\n\",\n    \"The paper for the Short version is\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"First of all, we need to train the ShortSeq2PointDisaggregator using the train data. For this example, both train and test data are consumption data of the microwave of the first REDD building.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import warnings; warnings.filterwarnings('ignore')\\n\",\n    \"\\n\",\n    \"from nilmtk import DataSet\\n\",\n    \"train = DataSet('redd.h5')\\n\",\n    \"train.set_window(end=\\\"30-4-2011\\\") #Use data only until 4/30/2011\\n\",\n    \"train_elec = train.buildings[1].elec\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Next, we need to define the disaggregator model.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Using TensorFlow backend.\\n\"\n     ]\n    },\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"_________________________________________________________________\\n\",\n      \"Layer (type)                 Output Shape              Param #   \\n\",\n      \"=================================================================\\n\",\n      \"conv1d_1 (Conv1D)            (None, 100, 30)           330       \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_1 (Dropout)          (None, 100, 30)           0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"conv1d_2 (Conv1D)            (None, 100, 30)           7230      \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_2 (Dropout)          (None, 100, 30)           0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"conv1d_3 (Conv1D)            (None, 100, 40)           7240      \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_3 (Dropout)          (None, 100, 40)           0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"conv1d_4 (Conv1D)            (None, 100, 50)           10050     \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_4 (Dropout)          (None, 100, 50)           0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"conv1d_5 (Conv1D)            (None, 100, 50)           12550     \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_5 (Dropout)          (None, 100, 50)           0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"flatten_1 (Flatten)          (None, 5000)              0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dense_1 (Dense)              (None, 1024)              5121024   \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_6 (Dropout)          (None, 1024)              0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dense_2 (Dense)              (None, 1)                 1025      \\n\",\n      \"=================================================================\\n\",\n      \"Total params: 5,159,449\\n\",\n      \"Trainable params: 5,159,449\\n\",\n      \"Non-trainable params: 0\\n\",\n      \"_________________________________________________________________\\n\",\n      \"None\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from shortseq2pointdisaggregator import ShortSeq2PointDisaggregator\\n\",\n    \"disaggregator = ShortSeq2PointDisaggregator(window_size=100)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Then train the model. We need to input the train data as well as their sample period. Also, we need to pass the desired number of training epochs. Finally, save the model for later use.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Epoch 1/5\\n\",\n      \"1002967/1002967 [==============================] - 228s - loss: 8.0571e-04   \\n\",\n      \"Epoch 2/5\\n\",\n      \"1002967/1002967 [==============================] - 213s - loss: 7.3260e-04   \\n\",\n      \"Epoch 3/5\\n\",\n      \"1002967/1002967 [==============================] - 210s - loss: 6.9686e-04   \\n\",\n      \"Epoch 4/5\\n\",\n      \"1002967/1002967 [==============================] - 210s - loss: 6.3350e-04   \\n\",\n      \"Epoch 5/5\\n\",\n      \"1002967/1002967 [==============================] - 209s - loss: 5.9427e-04   \\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"train_mains = train_elec.mains().all_meters()[0] # The aggregated meter that provides the input\\n\",\n    \"train_meter = train_elec.submeters()['microwave'] # The microwave meter that is used as a training target\\n\",\n    \"\\n\",\n    \"disaggregator.train(train_mains, train_meter, epochs=5, sample_period=1)\\n\",\n    \"disaggregator.export_model(\\\"model-redd5.h5\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now that the model is trained, we can use it to disaggregate energy data. Let's test it on the rest of the data from building 1.\\n\",\n    \"\\n\",\n    \"First we use the model to predict the microwave consumption. The results are saved automatically in a .h5 datastore.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"New sensible chunk: 121482\\n\",\n      \"New sensible chunk: 112661\\n\",\n      \"New sensible chunk: 87770\\n\",\n      \"New sensible chunk: 54084\\n\",\n      \"New sensible chunk: 2660\\n\",\n      \"New sensible chunk: 33513\\n\",\n      \"New sensible chunk: 138535\\n\",\n      \"New sensible chunk: 32514\\n\",\n      \"New sensible chunk: 27255\\n\",\n      \"New sensible chunk: 34833\\n\",\n      \"New sensible chunk: 100831\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"test = DataSet('redd.h5')\\n\",\n    \"test.set_window(start=\\\"30-4-2011\\\")\\n\",\n    \"test_elec = test.buildings[1].elec\\n\",\n    \"test_mains = test_elec.mains().all_meters()[0]\\n\",\n    \"\\n\",\n    \"disag_filename = 'disag-out.h5' # The filename of the resulting datastore\\n\",\n    \"from nilmtk.datastore import HDFDataStore\\n\",\n    \"output = HDFDataStore(disag_filename, 'w')\\n\",\n    \"\\n\",\n    \"# test_mains: The aggregated signal meter\\n\",\n    \"# output: The output datastore\\n\",\n    \"# train_meter: This is used in order to copy the metadata of the train meter into the datastore\\n\",\n    \"disaggregator.disaggregate(test_mains, output, train_meter, sample_period=1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Let's plot the results and compare them to the ground truth signal.\\n\",\n    \"\\n\",\n    \"**Note:** Calling plot this way, downsamples the signal to reduce computing time. To plot the entire signal call\\n\",\n    \"```\\n\",\n    \"predicted.power_series_all_data().plot()\\n\",\n    \"ground_truth.power_series_all_data().plot()\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\noQUAAAAASUVORK5CYII=\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f0df0492f10>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"result = DataSet(disag_filename)\\n\",\n    \"res_elec = result.buildings[1].elec\\n\",\n    \"predicted = res_elec['microwave']\\n\",\n    \"ground_truth = test_elec['microwave']\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"predicted.plot()\\n\",\n    \"ground_truth.plot()\\n\",\n    \"plt.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Finally let's see the metric results.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"============ Recall: 0.0456918965636\\n\",\n      \"============ Precision: 0.805732687157\\n\",\n      \"============ Accuracy: 0.28334183468\\n\",\n      \"============ F1 Score: 0.0864796608023\\n\",\n      \"============ Relative error in total energy: 0.840686257358\\n\",\n      \"============ Mean absolute error(in Watts): 25.0217610209\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import metrics\\n\",\n    \"rpaf = metrics.recall_precision_accuracy_f1(predicted, ground_truth)\\n\",\n    \"print(\\\"============ Recall: {}\\\".format(rpaf[0]))\\n\",\n    \"print(\\\"============ Precision: {}\\\".format(rpaf[1]))\\n\",\n    \"print(\\\"============ Accuracy: {}\\\".format(rpaf[2]))\\n\",\n    \"print(\\\"============ F1 Score: {}\\\".format(rpaf[3]))\\n\",\n    \"\\n\",\n    \"print(\\\"============ Relative error in total energy: {}\\\".format(metrics.relative_error_total_energy(predicted, ground_truth)))\\n\",\n    \"print(\\\"============ Mean absolute error(in Watts): {}\\\".format(metrics.mean_absolute_error(predicted, ground_truth)))\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"anaconda-cloud\": {},\n  \"kernelspec\": {\n   \"display_name\": \"Python [conda env:nilmtk-env]\",\n   \"language\": \"python\",\n   \"name\": \"conda-env-nilmtk-env-py\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.12\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "ShortSeq2Point/metrics.py",
    "content": "from nilmtk.electric import align_two_meters\nimport numpy as np\n\ndef tp_tn_fp_fn(states_pred, states_ground):\n    tp = np.sum(np.logical_and(states_pred == 1, states_ground == 1))\n    fp = np.sum(np.logical_and(states_pred == 1, states_ground == 0))\n    fn = np.sum(np.logical_and(states_pred == 0, states_ground == 1))\n    tn = np.sum(np.logical_and(states_pred == 0, states_ground == 0))\n    return tp, tn, fp, fn\n\ndef recall_precision_accuracy_f1(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    threshold = ground.on_power_threshold()\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        sum_samples += len(chunk)\n        pr = np.array([0 if (p)<threshold else 1 for p in chunk.iloc[:,0]])\n        gr = np.array([0 if p<threshold else 1 for p in chunk.iloc[:,1]])\n\n        tp, tn, fp, fn = tp_tn_fp_fn(pr,gr)\n        p = sum(pr)\n        n = len(pr) - p\n\n        chunk_results.append([tp,tn,fp,fn,p,n])\n\n    if sum_samples == 0:\n        return None\n    else:\n        [tp,tn,fp,fn,p,n] = np.sum(chunk_results, axis=0)\n\n        res_recall = recall(tp,fn)\n        res_precision = precision(tp,fp)\n        res_f1 = f1(res_precision,res_recall)\n        res_accuracy = accuracy(tp,tn,p,n)\n\n        return (res_recall,res_precision,res_accuracy,res_f1)\n\ndef relative_error_total_energy(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        E_pred = sum(chunk.iloc[:,0])\n        E_ground = sum(chunk.iloc[:,1])\n\n        chunk_results.append([\n                            E_pred,\n                            E_ground\n                            ])\n    if sum_samples == 0:\n        return None\n    else:\n        [E_pred, E_ground] = np.sum(chunk_results,axis=0)\n        return abs(E_pred - E_ground) / float(max(E_pred,E_ground))\n\ndef mean_absolute_error(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    total_sum = 0.0\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        total_sum += sum(abs((chunk.iloc[:,0]) - chunk.iloc[:,1]))\n    if sum_samples == 0:\n        return None\n    else:\n        return total_sum / sum_samples\n\n\ndef recall(tp,fn):\n    return tp/float(tp+fn)\n\ndef precision(tp,fp):\n    return tp/float(tp+fp)\n\ndef f1(prec,rec):\n    return 2 * (prec*rec) / float(prec+rec)\n\ndef accuracy(tp, tn, p, n):\n    return (tp + tn) / float(p + n)\n"
  },
  {
    "path": "ShortSeq2Point/redd-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom nilmtk.elecmeter import ElecMeterID\nimport metrics\nfrom shortseq2pointdisaggregator import ShortSeq2PointDisaggregator\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('redd.h5')\ntrain.set_window(end=\"30-4-2011\")\ntest = DataSet('redd.h5')\ntest.set_window(start=\"30-4-2011\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'fridge'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains().all_meters()[0]\ntest_mains = test_elec.mains().all_meters()[0]\ndisaggregator = WindowGRUDisaggregator(window_size=200)\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    disaggregator.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    disaggregator.export_model(\"REDD-RNN-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\n    print(\"CHECKPOINT {}\".format(epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = 'disag-out.h5'\noutput = HDFDataStore(disag_filename, 'w')\ndisaggregator.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "ShortSeq2Point/shortseq2pointdisaggregator.py",
    "content": "from __future__ import print_function, division\nimport random\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nimport pandas as pd\nimport numpy as np\nimport h5py\n\nfrom keras.models import load_model\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Conv1D, GRU, Bidirectional, Dropout, Flatten\nfrom keras.utils import plot_model\n\nfrom nilmtk.utils import find_nearest\nfrom nilmtk.feature_detectors import cluster\nfrom nilmtk.legacy.disaggregate import Disaggregator\nfrom nilmtk.datastore import HDFDataStore\n\nclass ShortSeq2PointDisaggregator(Disaggregator):\n    '''Attempt to create a RNN Disaggregator\n\n    Attributes\n    ----------\n    model : keras Sequential model\n    mmax : the maximum value of the aggregate data\n\n    MIN_CHUNK_LENGTH : int\n       the minimum length of an acceptable chunk\n    '''\n\n    def __init__(self, window_size=100):\n        '''Initialize disaggregator\n        '''\n        self.MODEL_NAME = \"WindowGRU\"\n        self.mmax = None\n        self.MIN_CHUNK_LENGTH = window_size\n        self.window_size = window_size\n        self.model = self._create_model()\n\n    def train(self, mains, meter, epochs=1, batch_size=128, **load_kwargs):\n        '''Train\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter object for the aggregate data\n        meter : a nilmtk.ElecMeter object for the meter data\n        epochs : number of epochs to train\n        batch_size : size of batch used for training\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        main_power_series = mains.power_series(**load_kwargs)\n        meter_power_series = meter.power_series(**load_kwargs)\n\n        # Train chunks\n        run = True\n        mainchunk = next(main_power_series)\n        meterchunk = next(meter_power_series)\n        if self.mmax == None:\n            self.mmax = mainchunk.max()\n\n        while(run):\n            mainchunk = self._normalize(mainchunk, self.mmax)\n            meterchunk = self._normalize(meterchunk, self.mmax)\n\n            self.train_on_chunk(mainchunk, meterchunk, epochs, batch_size)\n            try:\n                mainchunk = next(main_power_series)\n                meterchunk = next(meter_power_series)\n            except:\n                run = False\n\n    def train_on_chunk(self, mainchunk, meterchunk, epochs, batch_size):\n        '''Train using only one chunk\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n\n        # Replace NaNs with 0s\n        mainchunk.fillna(0, inplace=True)\n        meterchunk.fillna(0, inplace=True)\n        ix = mainchunk.index.intersection(meterchunk.index)\n        mainchunk = np.array(mainchunk[ix])\n        meterchunk = np.array(meterchunk[ix])\n\n        indexer = np.arange(self.window_size)[None, :] + np.arange(len(mainchunk)-self.window_size+1)[:, None]\n        mainchunk = mainchunk[indexer]\n        meterchunk = meterchunk[self.window_size-1:]\n        mainchunk = np.reshape(mainchunk, (mainchunk.shape[0], mainchunk.shape[1],1))\n\n        self.model.fit(mainchunk, meterchunk, epochs=epochs, batch_size=batch_size, shuffle=True)\n\n    def train_across_buildings(self, mainlist, meterlist, epochs=1, batch_size=128, **load_kwargs):\n        '''Train using data from multiple buildings\n\n        Parameters\n        ----------\n        mainlist : a list of nilmtk.ElecMeter objects for the aggregate data of each building\n        meterlist : a list of nilmtk.ElecMeter objects for the meter data of each building\n        batch_size : size of batch used for training\n        epochs : number of epochs to train\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        assert len(mainlist) == len(meterlist), \"Number of main and meter channels should be equal\"\n        num_meters = len(mainlist)\n\n        mainps = [None] * num_meters\n        meterps = [None] * num_meters\n        mainchunks = [None] * num_meters\n        meterchunks = [None] * num_meters\n\n        # Get generators of timeseries\n        for i,m in enumerate(mainlist):\n            mainps[i] = m.power_series(**load_kwargs)\n\n        for i,m in enumerate(meterlist):\n            meterps[i] = m.power_series(**load_kwargs)\n\n        # Get a chunk of data\n        for i in range(num_meters):\n            mainchunks[i] = next(mainps[i])\n            meterchunks[i] = next(meterps[i])\n        if self.mmax == None:\n            self.mmax = max([m.max() for m in mainchunks])\n\n\n        run = True\n        while(run):\n            # Normalize and train\n            mainchunks = [self._normalize(m, self.mmax) for m in mainchunks]\n            meterchunks = [self._normalize(m, self.mmax) for m in meterchunks]\n\n            self.train_across_buildings_chunk(mainchunks, meterchunks, epochs, batch_size)\n\n            # If more chunks, repeat\n            try:\n                for i in range(num_meters):\n                    mainchunks[i] = next(mainps[i])\n                    meterchunks[i] = next(meterps[i])\n            except:\n                run = False\n\n    def train_across_buildings_chunk(self, mainchunks, meterchunks, epochs, batch_size):\n        '''Train using only one chunk of data. This chunk consists of data from\n        all buildings.\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n        num_meters = len(mainchunks)\n        batch_size = int(batch_size/num_meters)\n        num_of_batches = [None] * num_meters\n\n        # Find common parts of timeseries\n        for i in range(num_meters):\n            mainchunks[i].fillna(0, inplace=True)\n            meterchunks[i].fillna(0, inplace=True)\n            ix = mainchunks[i].index.intersection(meterchunks[i].index)\n            m1 = mainchunks[i]\n            m2 = meterchunks[i]\n            mainchunks[i] = m1[ix]\n            meterchunks[i] = m2[ix]\n\n            indexer = np.arange(self.window_size)[None, :] + np.arange(len(mainchunks[i].values)-self.window_size+1)[:, None]\n            mainchunks[i] = mainchunks[i].values[indexer]\n            meterchunks[i] = meterchunks[i].values[self.window_size-1:]\n\n            num_of_batches[i] = int(len(ix)/batch_size) - 1\n\n        for e in range(epochs): # Iterate for every epoch\n            print(e)\n            batch_indexes = list(range(min(num_of_batches)))\n            random.shuffle(batch_indexes)\n\n            for bi, b in enumerate(batch_indexes): # Iterate for every batch\n                print(\"Batch {} of {}\".format(bi,min(num_of_batches)), end=\"\\r\")\n                sys.stdout.flush()\n                X_batch = np.empty((batch_size*num_meters, self.window_size, 1))\n                Y_batch = np.empty((batch_size*num_meters, 1))\n\n                # Create a batch out of data from all buildings\n                for i in range(num_meters):\n                    mainpart = mainchunks[i]\n                    meterpart = meterchunks[i]\n                    mainpart = mainpart[b*batch_size:(b+1)*batch_size]\n                    meterpart = meterpart[b*batch_size:(b+1)*batch_size]\n                    X = np.reshape(mainpart, (batch_size, self.window_size, 1))\n                    Y = np.reshape(meterpart, (batch_size, 1))\n\n                    X_batch[i*batch_size:(i+1)*batch_size] = np.array(X)\n                    Y_batch[i*batch_size:(i+1)*batch_size] = np.array(Y)\n\n                # Shuffle data\n                p = np.random.permutation(len(X_batch))\n                X_batch, Y_batch = X_batch[p], Y_batch[p]\n\n                # Train model\n                self.model.train_on_batch(X_batch, Y_batch)\n            print(\"\\n\")\n\n    def disaggregate(self, mains, output_datastore, meter_metadata, **load_kwargs):\n        '''Disaggregate mains according to the model learnt previously.\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter of aggregate data\n        meter_metadata: a nilmtk.ElecMeter of the observed meter used for storing the metadata\n        output_datastore : instance of nilmtk.DataStore subclass\n            For storing power predictions from disaggregation algorithm.\n        **load_kwargs : key word arguments\n            Passed to `mains.power_series(**kwargs)`\n        '''\n\n        load_kwargs = self._pre_disaggregation_checks(load_kwargs)\n\n        load_kwargs.setdefault('sample_period', 60)\n        load_kwargs.setdefault('sections', mains.good_sections())\n\n        timeframes = []\n        building_path = '/building{}'.format(mains.building())\n        mains_data_location = building_path + '/elec/meter1'\n        data_is_available = False\n\n        for chunk in mains.power_series(**load_kwargs):\n            if len(chunk) < self.MIN_CHUNK_LENGTH:\n                continue\n            print(\"New sensible chunk: {}\".format(len(chunk)))\n\n            timeframes.append(chunk.timeframe)\n            measurement = chunk.name\n            chunk2 = self._normalize(chunk, self.mmax)\n\n            appliance_power = self.disaggregate_chunk(chunk2)\n            appliance_power[appliance_power < 0] = 0\n            appliance_power = self._denormalize(appliance_power, self.mmax)\n\n            # Append prediction to output\n            data_is_available = True\n            cols = pd.MultiIndex.from_tuples([chunk.name])\n            meter_instance = meter_metadata.instance()\n            df = pd.DataFrame(\n                appliance_power.values, index=appliance_power.index,\n                columns=cols, dtype=\"float32\")\n            key = '{}/elec/meter{}'.format(building_path, meter_instance)\n            output_datastore.append(key, df)\n\n            # Append aggregate data to output\n            mains_df = pd.DataFrame(chunk, columns=cols, dtype=\"float32\")\n            output_datastore.append(key=mains_data_location, value=mains_df)\n\n        # Save metadata to output\n        if data_is_available:\n            self._save_metadata_for_disaggregation(\n                output_datastore=output_datastore,\n                sample_period=load_kwargs['sample_period'],\n                measurement=measurement,\n                timeframes=timeframes,\n                building=mains.building(),\n                meters=[meter_metadata]\n            )\n\n    def disaggregate_chunk(self, mains):\n        '''In-memory disaggregation.\n\n        Parameters\n        ----------\n        mains : pd.Series of aggregate data\n        Returns\n        -------\n        appliance_powers : pd.DataFrame where each column represents a\n            disaggregated appliance.  Column names are the integer index\n            into `self.model` for the appliance in question.\n        '''\n        up_limit = len(mains)\n\n        mains.fillna(0, inplace=True)\n\n        X_batch = np.array(mains)\n        Y_len = len(X_batch)\n        indexer = np.arange(self.window_size)[None, :] + np.arange(len(X_batch)-self.window_size+1)[:, None]\n        X_batch = X_batch[indexer]\n        X_batch = np.reshape(X_batch, (X_batch.shape[0],X_batch.shape[1],1))\n\n        pred = self.model.predict(X_batch, batch_size=128)\n        pred = np.reshape(pred, (len(pred)))\n        column = pd.Series(pred, index=mains.index[self.window_size-1:Y_len], name=0)\n\n        appliance_powers_dict = {}\n        appliance_powers_dict[0] = column\n        appliance_powers = pd.DataFrame(appliance_powers_dict)\n        return appliance_powers\n\n    def import_model(self, filename):\n        '''Loads keras model from h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n\n        Returns: Keras model\n        '''\n        self.model = load_model(filename)\n        with h5py.File(filename, 'a') as hf:\n            ds = hf.get('disaggregator-data').get('mmax')\n            self.mmax = np.array(ds)[0]\n\n    def export_model(self, filename):\n        '''Saves keras model to h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n        '''\n        self.model.save(filename)\n        with h5py.File(filename, 'a') as hf:\n            gr = hf.create_group('disaggregator-data')\n            gr.create_dataset('mmax', data = [self.mmax])\n\n    def _normalize(self, chunk, mmax):\n        '''Normalizes timeseries\n\n        Parameters\n        ----------\n        chunk : the timeseries to normalize\n        max : max value of the powerseries\n\n        Returns: Normalized timeseries\n        '''\n        tchunk = chunk / mmax\n        return tchunk\n\n    def _denormalize(self, chunk, mmax):\n        '''Deormalizes timeseries\n        Note: This is not entirely correct\n\n        Parameters\n        ----------\n        chunk : the timeseries to denormalize\n        max : max value used for normalization\n\n        Returns: Denormalized timeseries\n        '''\n        tchunk = chunk * mmax\n        return tchunk\n\n    def _create_model(self):\n        '''Creates and returns the ShortSeq2Point Network\n        Based on: https://arxiv.org/pdf/1612.09106v3.pdf\n        '''\n        model = Sequential()\n\n        # 1D Conv\n        model.add(Conv1D(30, 10, activation='relu', input_shape=(self.window_size,1), padding=\"same\", strides=1))\n        model.add(Dropout(0.5))\n        model.add(Conv1D(30, 8, activation='relu', padding=\"same\", strides=1))\n        model.add(Dropout(0.5))\n        model.add(Conv1D(40, 6, activation='relu', padding=\"same\", strides=1))\n        model.add(Dropout(0.5))\n        model.add(Conv1D(50, 5, activation='relu', padding=\"same\", strides=1))\n        model.add(Dropout(0.5))\n        model.add(Conv1D(50, 5, activation='relu', padding=\"same\", strides=1))\n        model.add(Dropout(0.5))\n        # Fully Connected Layers\n        model.add(Flatten())\n        model.add(Dense(1024, activation='relu'))\n        model.add(Dropout(0.5))\n        model.add(Dense(1, activation='linear'))\n\n        model.compile(loss='mse', optimizer='adam')\n        print(model.summary())\n        plot_model(model, to_file='model.png', show_shapes=True)\n\n        return model\n"
  },
  {
    "path": "ShortSeq2Point/ukdale-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom shortseq2pointdisaggregator import ShortSeq2PointDisaggregator\nimport metrics\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('ukdale.h5')\ntest = DataSet('ukdale.h5')\n\ntrain.set_window(start=\"13-4-2013\", end=\"1-1-2014\")\ntest.set_window(start=\"1-1-2014\", end=\"30-3-2014\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'kettle'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains()\ntest_mains = test_elec.mains()\ndisaggregator = ShortSeq2PointDisaggregator()\n\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    print(\"CHECKPOINT {}\".format(epochs))\n    disaggregator.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    disaggregator.export_model(\"UKDALE-RNN-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = \"disag-out.h5\"\noutput = HDFDataStore(disag_filename, 'w')\ndisaggregator.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "WindowGRU/README.md",
    "content": "# GRU with Sliding Window Disaggregator\n\nA Recurrent Neural Network disaggregator that uses a window of data as input instead of one sample of the timeseries.\n\nAs described in [Sliding Window Approach for Online Energy Disaggregation Using Artificial Neural Networks](https://dl.acm.org/citation.cfm?id=3201011) by Krystalakos, Nalmpantis and Vrakas.\n\nSee example experiment [here](https://github.com/OdysseasKr/neural-disaggregator/blob/master/WindowGRU/Window-GRU-example.ipynb).\n"
  },
  {
    "path": "WindowGRU/Window-GRU-example.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# How to use the Sliding Window GRU with NILMTK\\n\",\n    \"\\n\",\n    \"This is an example on how to train and use the Sliding Window GRU disaggregator on the [REDD](http://redd.csail.mit.edu/) dataset using [NILMTK](https://github.com/nilmtk/NILMTK/).\\n\",\n    \"\\n\",\n    \"This network was described in the []() paper.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"First of all, we need to train the WindowGRUDisaggregator using the train data. For this example, both train and test data are consumption data of the microwave of the first REDD building.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import warnings; warnings.filterwarnings('ignore')\\n\",\n    \"\\n\",\n    \"from nilmtk import DataSet\\n\",\n    \"train = DataSet('redd.h5')\\n\",\n    \"train.set_window(end=\\\"30-4-2011\\\") #Use data only until 4/30/2011\\n\",\n    \"train_elec = train.buildings[1].elec\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Next, we need to define the disaggregator model.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Using TensorFlow backend.\\n\"\n     ]\n    },\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"WARNING:tensorflow:From /home/odysseas/anaconda2/envs/nilmtk-env/lib/python2.7/site-packages/keras/backend/tensorflow_backend.py:1074: calling reduce_sum (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.\\n\",\n      \"Instructions for updating:\\n\",\n      \"keep_dims is deprecated, use keepdims instead\\n\",\n      \"WARNING:tensorflow:From /home/odysseas/anaconda2/envs/nilmtk-env/lib/python2.7/site-packages/keras/backend/tensorflow_backend.py:1181: calling reduce_mean (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.\\n\",\n      \"Instructions for updating:\\n\",\n      \"keep_dims is deprecated, use keepdims instead\\n\",\n      \"_________________________________________________________________\\n\",\n      \"Layer (type)                 Output Shape              Param #   \\n\",\n      \"=================================================================\\n\",\n      \"conv1d_1 (Conv1D)            (None, 50, 16)            80        \\n\",\n      \"_________________________________________________________________\\n\",\n      \"bidirectional_1 (Bidirection (None, 50, 128)           31104     \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_1 (Dropout)          (None, 50, 128)           0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"bidirectional_2 (Bidirection (None, 256)               197376    \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_2 (Dropout)          (None, 256)               0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dense_1 (Dense)              (None, 128)               32896     \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dropout_3 (Dropout)          (None, 128)               0         \\n\",\n      \"_________________________________________________________________\\n\",\n      \"dense_2 (Dense)              (None, 1)                 129       \\n\",\n      \"=================================================================\\n\",\n      \"Total params: 261,585\\n\",\n      \"Trainable params: 261,585\\n\",\n      \"Non-trainable params: 0\\n\",\n      \"_________________________________________________________________\\n\",\n      \"None\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from windowgrudisaggregator import WindowGRUDisaggregator\\n\",\n    \"disaggregator = WindowGRUDisaggregator(window_size=50)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Then train the model. We need to input the train data as well as their sample period. Also, we need to pass the desired number of training epochs. Finally, save the model for later use.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Epoch 1/2\\n\",\n      \"1003017/1003017 [==============================] - 1459s - loss: 7.4605e-04  \\n\",\n      \"Epoch 2/2\\n\",\n      \"1003017/1003017 [==============================] - 1443s - loss: 6.4579e-04  \\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"train_mains = train_elec.mains().all_meters()[0] # The aggregated meter that provides the input\\n\",\n    \"train_meter = train_elec.submeters()['microwave'] # The microwave meter that is used as a training target\\n\",\n    \"\\n\",\n    \"disaggregator.train(train_mains, train_meter, epochs=2, sample_period=1)\\n\",\n    \"disaggregator.export_model(\\\"model-redd5.h5\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now that the model is trained, we can use it to disaggregate energy data. Let's test it on the rest of the data from building 1.\\n\",\n    \"\\n\",\n    \"First we use the model to predict the microwave consumption. The results are saved automatically in a .h5 datastore.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"New sensible chunk: 121482\\n\",\n      \"New sensible chunk: 112661\\n\",\n      \"New sensible chunk: 87770\\n\",\n      \"New sensible chunk: 55\\n\",\n      \"New sensible chunk: 54084\\n\",\n      \"New sensible chunk: 2660\\n\",\n      \"New sensible chunk: 33513\\n\",\n      \"New sensible chunk: 138535\\n\",\n      \"New sensible chunk: 32514\\n\",\n      \"New sensible chunk: 27255\\n\",\n      \"New sensible chunk: 34833\\n\",\n      \"New sensible chunk: 100831\\n\",\n      \"New sensible chunk: 90\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"test = DataSet('redd.h5')\\n\",\n    \"test.set_window(start=\\\"30-4-2011\\\")\\n\",\n    \"test_elec = test.buildings[1].elec\\n\",\n    \"test_mains = test_elec.mains().all_meters()[0]\\n\",\n    \"\\n\",\n    \"disag_filename = 'disag-out.h5' # The filename of the resulting datastore\\n\",\n    \"from nilmtk.datastore import HDFDataStore\\n\",\n    \"output = HDFDataStore(disag_filename, 'w')\\n\",\n    \"\\n\",\n    \"# test_mains: The aggregated signal meter\\n\",\n    \"# output: The output datastore\\n\",\n    \"# train_meter: This is used in order to copy the metadata of the train meter into the datastore\\n\",\n    \"disaggregator.disaggregate(test_mains, output, train_meter, sample_period=1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Let's plot the results and compare them to the ground truth signal.\\n\",\n    \"\\n\",\n    \"**Note:** Calling plot this way, downsamples the signal to reduce computing time. To plot the entire signal call\\n\",\n    \"```\\n\",\n    \"predicted.power_series_all_data().plot()\\n\",\n    \"ground_truth.power_series_all_data().plot()\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\\n\",\n      \"text/plain\": [\n       \"<matplotlib.figure.Figure at 0x7f89fdbab6d0>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"result = DataSet(disag_filename)\\n\",\n    \"res_elec = result.buildings[1].elec\\n\",\n    \"predicted = res_elec['microwave']\\n\",\n    \"ground_truth = test_elec['microwave']\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"predicted.plot()\\n\",\n    \"ground_truth.plot()\\n\",\n    \"plt.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Finally let's see the metric results.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"============ Recall: 1.0\\n\",\n      \"============ Precision: 0.742390932564\\n\",\n      \"============ Accuracy: 0.742390932564\\n\",\n      \"============ F1 Score: 0.852151969675\\n\",\n      \"============ Relative error in total energy: 0.854315367543\\n\",\n      \"============ Mean absolute error(in Watts): 30.7409502125\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import metrics\\n\",\n    \"rpaf = metrics.recall_precision_accuracy_f1(predicted, ground_truth)\\n\",\n    \"print(\\\"============ Recall: {}\\\".format(rpaf[0]))\\n\",\n    \"print(\\\"============ Precision: {}\\\".format(rpaf[1]))\\n\",\n    \"print(\\\"============ Accuracy: {}\\\".format(rpaf[2]))\\n\",\n    \"print(\\\"============ F1 Score: {}\\\".format(rpaf[3]))\\n\",\n    \"\\n\",\n    \"print(\\\"============ Relative error in total energy: {}\\\".format(metrics.relative_error_total_energy(predicted, ground_truth)))\\n\",\n    \"print(\\\"============ Mean absolute error(in Watts): {}\\\".format(metrics.mean_absolute_error(predicted, ground_truth)))\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"anaconda-cloud\": {},\n  \"kernelspec\": {\n   \"display_name\": \"Python [conda env:nilmtk-env]\",\n   \"language\": \"python\",\n   \"name\": \"conda-env-nilmtk-env-py\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.12\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "WindowGRU/metrics.py",
    "content": "from nilmtk.electric import align_two_meters\nimport numpy as np\n\ndef tp_tn_fp_fn(states_pred, states_ground):\n    tp = np.sum(np.logical_and(states_pred == 1, states_ground == 1))\n    fp = np.sum(np.logical_and(states_pred == 1, states_ground == 0))\n    fn = np.sum(np.logical_and(states_pred == 0, states_ground == 1))\n    tn = np.sum(np.logical_and(states_pred == 0, states_ground == 0))\n    return tp, tn, fp, fn\n\ndef recall_precision_accuracy_f1(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    threshold = ground.on_power_threshold()\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        sum_samples += len(chunk)\n        pr = np.array([0 if (p)<threshold else 1 for p in chunk.iloc[:,0]])\n        gr = np.array([0 if p<threshold else 1 for p in chunk.iloc[:,1]])\n\n        tp, tn, fp, fn = tp_tn_fp_fn(pr,gr)\n        p = sum(pr)\n        n = len(pr) - p\n\n        chunk_results.append([tp,tn,fp,fn,p,n])\n\n    if sum_samples == 0:\n        return None\n    else:\n        [tp,tn,fp,fn,p,n] = np.sum(chunk_results, axis=0)\n\n        res_recall = recall(tp,fn)\n        res_precision = precision(tp,fp)\n        res_f1 = f1(res_precision,res_recall)\n        res_accuracy = accuracy(tp,tn,p,n)\n\n        return (res_recall,res_precision,res_accuracy,res_f1)\n\ndef relative_error_total_energy(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    chunk_results = []\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        E_pred = sum(chunk.iloc[:,0])\n        E_ground = sum(chunk.iloc[:,1])\n\n        chunk_results.append([\n                            E_pred,\n                            E_ground\n                            ])\n    if sum_samples == 0:\n        return None\n    else:\n        [E_pred, E_ground] = np.sum(chunk_results,axis=0)\n        return abs(E_pred - E_ground) / float(max(E_pred,E_ground))\n\ndef mean_absolute_error(pred, ground):\n    aligned_meters = align_two_meters(pred, ground)\n    total_sum = 0.0\n    sum_samples = 0.0\n    for chunk in aligned_meters:\n        chunk.fillna(0, inplace=True)\n        sum_samples += len(chunk)\n        total_sum += sum(abs((chunk.iloc[:,0]) - chunk.iloc[:,1]))\n    if sum_samples == 0:\n        return None\n    else:\n        return total_sum / sum_samples\n\n\ndef recall(tp,fn):\n    return tp/float(tp+fn)\n\ndef precision(tp,fp):\n    return tp/float(tp+fp)\n\ndef f1(prec,rec):\n    return 2 * (prec*rec) / float(prec+rec)\n\ndef accuracy(tp, tn, p, n):\n    return (tp + tn) / float(p + n)\n"
  },
  {
    "path": "WindowGRU/redd-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom nilmtk.elecmeter import ElecMeterID\nimport metrics\nfrom windowgrudisaggregator import WindowGRUDisaggregator\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('redd.h5')\ntrain.set_window(end=\"30-4-2011\")\ntest = DataSet('redd.h5')\ntest.set_window(start=\"30-4-2011\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'fridge'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains().all_meters()[0]\ntest_mains = test_elec.mains().all_meters()[0]\ndisaggregator = WindowGRUDisaggregator(window_size=200)\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(3):\n    disaggregator.train(train_mains, train_meter, epochs=5, sample_period=sample_period)\n    epochs += 5\n    disaggregator.export_model(\"REDD-RNN-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\n    print(\"CHECKPOINT {}\".format(epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = 'disag-out.h5'\noutput = HDFDataStore(disag_filename, 'w')\ndisaggregator.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "WindowGRU/ukdale-test.py",
    "content": "from __future__ import print_function, division\nimport time\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nfrom nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore\nfrom windowgrudisaggregator import WindowGRUDisaggregator\nimport metrics\n\nprint(\"========== OPEN DATASETS ============\")\ntrain = DataSet('ukdale.h5')\ntest = DataSet('ukdale.h5')\n\ntrain.set_window(start=\"13-4-2013\", end=\"1-1-2014\")\ntest.set_window(start=\"1-1-2014\", end=\"30-3-2014\")\n\ntrain_building = 1\ntest_building = 1\nsample_period = 6\nmeter_key = 'kettle'\ntrain_elec = train.buildings[train_building].elec\ntest_elec = test.buildings[test_building].elec\n\ntrain_meter = train_elec.submeters()[meter_key]\ntrain_mains = train_elec.mains()\ntest_mains = test_elec.mains()\ndisaggregator = WindowGRUDisaggregator()\n\n\nstart = time.time()\nprint(\"========== TRAIN ============\")\nepochs = 0\nfor i in range(1):\n    print(\"CHECKPOINT {}\".format(epochs))\n    disaggregator.train(train_mains, train_meter, epochs=1, sample_period=sample_period)\n    epochs += 5\n    disaggregator.export_model(\"UKDALE-RNN-h{}-{}-{}epochs.h5\".format(train_building,\n                                                        meter_key,\n                                                        epochs))\nend = time.time()\nprint(\"Train =\", end-start, \"seconds.\")\n\n\nprint(\"========== DISAGGREGATE ============\")\ndisag_filename = \"disag-out.h5\"\noutput = HDFDataStore(disag_filename, 'w')\ndisaggregator.disaggregate(test_mains, output, train_meter, sample_period=sample_period)\noutput.close()\n\nprint(\"========== RESULTS ============\")\nresult = DataSet(disag_filename)\nres_elec = result.buildings[test_building].elec\nrpaf = metrics.recall_precision_accuracy_f1(res_elec[meter_key], test_elec[meter_key])\nprint(\"============ Recall: {}\".format(rpaf[0]))\nprint(\"============ Precision: {}\".format(rpaf[1]))\nprint(\"============ Accuracy: {}\".format(rpaf[2]))\nprint(\"============ F1 Score: {}\".format(rpaf[2]))\n\nprint(\"============ Relative error in total energy: {}\".format(metrics.relative_error_total_energy(res_elec[meter_key], test_elec[meter_key])))\nprint(\"============ Mean absolute error(in Watts): {}\".format(metrics.mean_absolute_error(res_elec[meter_key], test_elec[meter_key])))\n"
  },
  {
    "path": "WindowGRU/windowgrudisaggregator.py",
    "content": "from __future__ import print_function, division\nimport random\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nimport pandas as pd\nimport numpy as np\nimport h5py\n\nfrom keras.models import load_model\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Conv1D, GRU, Bidirectional, Dropout\nfrom keras.utils import plot_model\n\nfrom nilmtk.utils import find_nearest\nfrom nilmtk.feature_detectors import cluster\nfrom nilmtk.legacy.disaggregate import Disaggregator\nfrom nilmtk.datastore import HDFDataStore\n\nclass WindowGRUDisaggregator(Disaggregator):\n    '''Attempt to create a RNN Disaggregator\n\n    Attributes\n    ----------\n    model : keras Sequential model\n    mmax : the maximum value of the aggregate data\n\n    MIN_CHUNK_LENGTH : int\n       the minimum length of an acceptable chunk\n    '''\n\n    def __init__(self, window_size=100):\n        '''Initialize disaggregator\n        '''\n        self.MODEL_NAME = \"WindowGRU\"\n        self.mmax = None\n        self.MIN_CHUNK_LENGTH = window_size\n        self.window_size = window_size\n        self.model = self._create_model()\n\n    def train(self, mains, meter, epochs=1, batch_size=128, **load_kwargs):\n        '''Train\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter object for the aggregate data\n        meter : a nilmtk.ElecMeter object for the meter data\n        epochs : number of epochs to train\n        batch_size : size of batch used for training\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        main_power_series = mains.power_series(**load_kwargs)\n        meter_power_series = meter.power_series(**load_kwargs)\n\n        # Train chunks\n        run = True\n        mainchunk = next(main_power_series)\n        meterchunk = next(meter_power_series)\n        if self.mmax == None:\n            self.mmax = mainchunk.max()\n\n        while(run):\n            mainchunk = self._normalize(mainchunk, self.mmax)\n            meterchunk = self._normalize(meterchunk, self.mmax)\n\n            self.train_on_chunk(mainchunk, meterchunk, epochs, batch_size)\n            try:\n                mainchunk = next(main_power_series)\n                meterchunk = next(meter_power_series)\n            except:\n                run = False\n\n    def train_on_chunk(self, mainchunk, meterchunk, epochs, batch_size):\n        '''Train using only one chunk\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n\n        # Replace NaNs with 0s\n        mainchunk.fillna(0, inplace=True)\n        meterchunk.fillna(0, inplace=True)\n        ix = mainchunk.index.intersection(meterchunk.index)\n        mainchunk = np.array(mainchunk[ix])\n        meterchunk = np.array(meterchunk[ix])\n\n        indexer = np.arange(self.window_size)[None, :] + np.arange(len(mainchunk)-self.window_size+1)[:, None]\n        mainchunk = mainchunk[indexer]\n        meterchunk = meterchunk[self.window_size-1:]\n        mainchunk = np.reshape(mainchunk, (mainchunk.shape[0], mainchunk.shape[1],1))\n\n        self.model.fit(mainchunk, meterchunk, epochs=epochs, batch_size=batch_size, shuffle=True)\n\n    def train_across_buildings(self, mainlist, meterlist, epochs=1, batch_size=128, **load_kwargs):\n        '''Train using data from multiple buildings\n\n        Parameters\n        ----------\n        mainlist : a list of nilmtk.ElecMeter objects for the aggregate data of each building\n        meterlist : a list of nilmtk.ElecMeter objects for the meter data of each building\n        batch_size : size of batch used for training\n        epochs : number of epochs to train\n        **load_kwargs : keyword arguments passed to `meter.power_series()`\n        '''\n\n        assert len(mainlist) == len(meterlist), \"Number of main and meter channels should be equal\"\n        num_meters = len(mainlist)\n\n        mainps = [None] * num_meters\n        meterps = [None] * num_meters\n        mainchunks = [None] * num_meters\n        meterchunks = [None] * num_meters\n\n        # Get generators of timeseries\n        for i,m in enumerate(mainlist):\n            mainps[i] = m.power_series(**load_kwargs)\n\n        for i,m in enumerate(meterlist):\n            meterps[i] = m.power_series(**load_kwargs)\n\n        # Get a chunk of data\n        for i in range(num_meters):\n            mainchunks[i] = next(mainps[i])\n            meterchunks[i] = next(meterps[i])\n        if self.mmax == None:\n            self.mmax = max([m.max() for m in mainchunks])\n\n\n        run = True\n        while(run):\n            # Normalize and train\n            mainchunks = [self._normalize(m, self.mmax) for m in mainchunks]\n            meterchunks = [self._normalize(m, self.mmax) for m in meterchunks]\n\n            self.train_across_buildings_chunk(mainchunks, meterchunks, epochs, batch_size)\n\n            # If more chunks, repeat\n            try:\n                for i in range(num_meters):\n                    mainchunks[i] = next(mainps[i])\n                    meterchunks[i] = next(meterps[i])\n            except:\n                run = False\n\n    def train_across_buildings_chunk(self, mainchunks, meterchunks, epochs, batch_size):\n        '''Train using only one chunk of data. This chunk consists of data from\n        all buildings.\n\n        Parameters\n        ----------\n        mainchunk : chunk of site meter\n        meterchunk : chunk of appliance\n        epochs : number of epochs for training\n        batch_size : size of batch used for training\n        '''\n        num_meters = len(mainchunks)\n        batch_size = int(batch_size/num_meters)\n        num_of_batches = [None] * num_meters\n\n        # Find common parts of timeseries\n        for i in range(num_meters):\n            mainchunks[i].fillna(0, inplace=True)\n            meterchunks[i].fillna(0, inplace=True)\n            ix = mainchunks[i].index.intersection(meterchunks[i].index)\n            m1 = mainchunks[i]\n            m2 = meterchunks[i]\n            mainchunks[i] = m1[ix]\n            meterchunks[i] = m2[ix]\n\n            indexer = np.arange(self.window_size)[None, :] + np.arange(len(mainchunks[i].values)-self.window_size+1)[:, None]\n            mainchunks[i] = mainchunks[i].values[indexer]\n            meterchunks[i] = meterchunks[i].values[self.window_size-1:]\n\n            num_of_batches[i] = int(len(ix)/batch_size) - 1\n\n        for e in range(epochs): # Iterate for every epoch\n            print(e)\n            batch_indexes = list(range(min(num_of_batches)))\n            random.shuffle(batch_indexes)\n\n            for bi, b in enumerate(batch_indexes): # Iterate for every batch\n                print(\"Batch {} of {}\".format(bi,min(num_of_batches)), end=\"\\r\")\n                sys.stdout.flush()\n                X_batch = np.empty((batch_size*num_meters, self.window_size, 1))\n                Y_batch = np.empty((batch_size*num_meters, 1))\n\n                # Create a batch out of data from all buildings\n                for i in range(num_meters):\n                    mainpart = mainchunks[i]\n                    meterpart = meterchunks[i]\n                    mainpart = mainpart[b*batch_size:(b+1)*batch_size]\n                    meterpart = meterpart[b*batch_size:(b+1)*batch_size]\n                    X = np.reshape(mainpart, (batch_size, self.window_size, 1))\n                    Y = np.reshape(meterpart, (batch_size, 1))\n\n                    X_batch[i*batch_size:(i+1)*batch_size] = np.array(X)\n                    Y_batch[i*batch_size:(i+1)*batch_size] = np.array(Y)\n\n                # Shuffle data\n                p = np.random.permutation(len(X_batch))\n                X_batch, Y_batch = X_batch[p], Y_batch[p]\n\n                # Train model\n                self.model.train_on_batch(X_batch, Y_batch)\n            print(\"\\n\")\n\n    def disaggregate(self, mains, output_datastore, meter_metadata, **load_kwargs):\n        '''Disaggregate mains according to the model learnt previously.\n\n        Parameters\n        ----------\n        mains : a nilmtk.ElecMeter of aggregate data\n        meter_metadata: a nilmtk.ElecMeter of the observed meter used for storing the metadata\n        output_datastore : instance of nilmtk.DataStore subclass\n            For storing power predictions from disaggregation algorithm.\n        **load_kwargs : key word arguments\n            Passed to `mains.power_series(**kwargs)`\n        '''\n\n        load_kwargs = self._pre_disaggregation_checks(load_kwargs)\n\n        load_kwargs.setdefault('sample_period', 60)\n        load_kwargs.setdefault('sections', mains.good_sections())\n\n        timeframes = []\n        building_path = '/building{}'.format(mains.building())\n        mains_data_location = building_path + '/elec/meter1'\n        data_is_available = False\n\n        for chunk in mains.power_series(**load_kwargs):\n            if len(chunk) < self.MIN_CHUNK_LENGTH:\n                continue\n            print(\"New sensible chunk: {}\".format(len(chunk)))\n\n            timeframes.append(chunk.timeframe)\n            measurement = chunk.name\n            chunk2 = self._normalize(chunk, self.mmax)\n\n            appliance_power = self.disaggregate_chunk(chunk2)\n            appliance_power[appliance_power < 0] = 0\n            appliance_power = self._denormalize(appliance_power, self.mmax)\n\n            # Append prediction to output\n            data_is_available = True\n            cols = pd.MultiIndex.from_tuples([chunk.name])\n            meter_instance = meter_metadata.instance()\n            df = pd.DataFrame(\n                appliance_power.values, index=appliance_power.index,\n                columns=cols, dtype=\"float32\")\n            key = '{}/elec/meter{}'.format(building_path, meter_instance)\n            output_datastore.append(key, df)\n\n            # Append aggregate data to output\n            mains_df = pd.DataFrame(chunk, columns=cols, dtype=\"float32\")\n            output_datastore.append(key=mains_data_location, value=mains_df)\n\n        # Save metadata to output\n        if data_is_available:\n            self._save_metadata_for_disaggregation(\n                output_datastore=output_datastore,\n                sample_period=load_kwargs['sample_period'],\n                measurement=measurement,\n                timeframes=timeframes,\n                building=mains.building(),\n                meters=[meter_metadata]\n            )\n\n    def disaggregate_chunk(self, mains):\n        '''In-memory disaggregation.\n\n        Parameters\n        ----------\n        mains : pd.Series of aggregate data\n        Returns\n        -------\n        appliance_powers : pd.DataFrame where each column represents a\n            disaggregated appliance.  Column names are the integer index\n            into `self.model` for the appliance in question.\n        '''\n        up_limit = len(mains)\n\n        mains.fillna(0, inplace=True)\n\n        X_batch = np.array(mains)\n        Y_len = len(X_batch)\n        indexer = np.arange(self.window_size)[None, :] + np.arange(len(X_batch)-self.window_size+1)[:, None]\n        X_batch = X_batch[indexer]\n        X_batch = np.reshape(X_batch, (X_batch.shape[0],X_batch.shape[1],1))\n\n        pred = self.model.predict(X_batch, batch_size=128)\n        pred = np.reshape(pred, (len(pred)))\n        column = pd.Series(pred, index=mains.index[self.window_size-1:Y_len], name=0)\n\n        appliance_powers_dict = {}\n        appliance_powers_dict[0] = column\n        appliance_powers = pd.DataFrame(appliance_powers_dict)\n        return appliance_powers\n\n    def import_model(self, filename):\n        '''Loads keras model from h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n\n        Returns: Keras model\n        '''\n        self.model = load_model(filename)\n        with h5py.File(filename, 'a') as hf:\n            ds = hf.get('disaggregator-data').get('mmax')\n            self.mmax = np.array(ds)[0]\n\n    def export_model(self, filename):\n        '''Saves keras model to h5\n\n        Parameters\n        ----------\n        filename : filename for .h5 file\n        '''\n        self.model.save(filename)\n        with h5py.File(filename, 'a') as hf:\n            gr = hf.create_group('disaggregator-data')\n            gr.create_dataset('mmax', data = [self.mmax])\n\n    def _normalize(self, chunk, mmax):\n        '''Normalizes timeseries\n\n        Parameters\n        ----------\n        chunk : the timeseries to normalize\n        max : max value of the powerseries\n\n        Returns: Normalized timeseries\n        '''\n        tchunk = chunk / mmax\n        return tchunk\n\n    def _denormalize(self, chunk, mmax):\n        '''Deormalizes timeseries\n        Note: This is not entirely correct\n\n        Parameters\n        ----------\n        chunk : the timeseries to denormalize\n        max : max value used for normalization\n\n        Returns: Denormalized timeseries\n        '''\n        tchunk = chunk * mmax\n        return tchunk\n\n    def _create_model(self):\n        '''Creates the GRU architecture described in the paper\n        '''\n        model = Sequential()\n\n        # 1D Conv\n        model.add(Conv1D(16, 4, activation='relu', input_shape=(self.window_size,1), padding=\"same\", strides=1))\n\n        #Bi-directional GRUs\n        model.add(Bidirectional(GRU(64, activation='relu', return_sequences=True), merge_mode='concat'))\n        model.add(Dropout(0.5))\n        model.add(Bidirectional(GRU(128, activation='relu', return_sequences=False), merge_mode='concat'))\n        model.add(Dropout(0.5))\n\n        # Fully Connected Layers\n        model.add(Dense(128, activation='relu'))\n        model.add(Dropout(0.5))\n        model.add(Dense(1, activation='linear'))\n\n        model.compile(loss='mse', optimizer='adam')\n        print(model.summary())\n        plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=False)\n\n        return model\n"
  },
  {
    "path": "requirements.txt",
    "content": "attrs==19.3.0\nbackcall==0.1.0\nbleach==3.1.0\nBottleneck==1.3.1\ncertifi==2019.11.28\ncoverage==4.5.4\ncycler==0.10.0\nCython==0.29.14\ndecorator==4.4.1\ndefusedxml==0.6.0\nentrypoints==0.3\nh5py==2.10.0\nhmmlearn==0.2.2\nimportlib-metadata==1.3.0\nipykernel==5.1.3\nipython==7.10.1\nipython-genutils==0.2.0\nipywidgets==7.5.1\njedi==0.15.1\nJinja2==2.10.3\njoblib==0.14.1\njsonschema==3.2.0\njupyter-client==5.3.3\njupyter-console==6.0.0\njupyter-core==4.6.1\nKeras==2.3.1\nKeras-Applications==1.0.8\nKeras-Preprocessing==1.1.0\nkiwisolver==1.1.0\nMako==1.1.0\nMarkdown==3.1.1\nMarkupSafe==1.1.1\nmatplotlib==3.1.2\nmistune==0.8.4\nmock==3.0.5\nmore-itertools==8.0.2\nnbconvert==5.6.1\nnbformat==4.4.0\nnetworkx==2.1\nnilm-metadata==0.2.4\nnilmtk===0.4.0.dev1-git.\nnose==1.3.7\nnotebook==6.0.1\nnumexpr==2.7.0\nnumpy==1.16.4\npandas==0.25.3\npandocfilters==1.4.2\nparso==0.5.1\npexpect==4.7.0\npickleshare==0.7.5\nprometheus-client==0.7.1\nprompt-toolkit==2.0.10\nprotobuf==3.11.1\npsycopg2==2.8.4\nptyprocess==0.6.0\npydot==1.4.1\nPygments==2.5.2\npygpu==0.7.6\npyparsing==2.4.5\nPyQt5==5.12.3\nPyQt5-sip==4.19.18\nPyQtWebEngine==5.12.1\npyrsistent==0.15.6\npython-dateutil==2.8.1\npytz==2019.3\nPyYAML==5.2\npyzmq==18.1.1\nqtconsole==4.6.0\nscikit-learn==0.22\nscipy==1.3.2\nSend2Trash==1.5.0\nsix==1.13.0\ntables==3.6.1\nterminado==0.8.3\ntestpath==0.4.4\nTheano==1.0.4\ntornado==6.0.3\ntraitlets==4.3.3\nwcwidth==0.1.7\nwebencodings==0.5.1\nwidgetsnbextension==3.5.1\nzipp==0.6.0\n"
  }
]