In [23]:
from __future__ import print_function, division
import numpy as np
from os.path import join, expanduser
import matplotlib.pyplot as plt
from matplotlib import ticker
import yaml  # for pretty-printing dict
from neuralnilm.metrics import run_metrics, across_all_appliances
import pandas as pd

# sklearn evokes warnings from numpy
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

In [196]:
TRAIN_HOUSES = {
    'microwave': (1, 2),
    'fridge': (1, 2, 4),
    'dish washer': (1, 2),
    'kettle': (1, 2, 4),
    'washing machine': (1, 5)
}

TEST_HOUSES = {
    'microwave': (5,),
    'fridge': (5,),
    'dish washer': (5,),
    'kettle': (5,),
    'washing machine': (2,)
}

APPLIANCES = TRAIN_HOUSES.keys()

ON_POWER_THRESHOLDS = {
    'microwave': 200,
    'fridge': 30,
    'dish washer': 10,
    'kettle': 2000,
    'washing machine': 20
}

HOUSES = [1, 2, 3, 4, 5]

METRICS = [
    'f1_score',
    'precision_score',
    'recall_score',
    'accuracy_score',
    'relative_error_in_total_energy',
    'total_energy_correctly_assigned',
    'mean_absolute_error'
]

ALGORITHMS = ['co', 'fhmm', 'ae', 'rectangles', 'rnn']

full_algorithm_names = [
    'Combinatorial Opt.', 'Factorial HMM', 'Autoencoder', 'Rectangles', 'LSTM']

DATA_PATH = expanduser("~/PhD/experiments/neural_nilm/data_for_BuildSys2015/")
ESTIMATES_PATH = join(DATA_PATH, "disag_estimates")
GROUND_TRUTH_PATH = join(DATA_PATH, "ground_truth_and_mains")

PLOT_PATH = expanduser("~/PhD/writing/papers/BuildSys_2015_Neural_NILM")

In [3]:
def load(architecture, building_i, appliance):
    # load estimates
    estimates_fname = "{}_building_{}_estimates_{}.csv".format(
        architecture, building_i, appliance)
    estimates_fname = join(ESTIMATES_PATH, estimates_fname)
    y_pred = np.loadtxt(estimates_fname, delimiter=',')

    # load ground truth
    y_true_fname = "building_{}_{}.csv".format(building_i, appliance.replace(' ', '_'))
    y_true_fname = join(GROUND_TRUTH_PATH, y_true_fname)
    y_true = np.loadtxt(y_true_fname, delimiter=',')

    # load mains
    mains_fname = "building_{}_mains.csv".format(building_i)
    mains_fname = join(GROUND_TRUTH_PATH, mains_fname)
    mains = np.loadtxt(mains_fname, delimiter=',')

    return y_true, y_pred, mains

In [4]:
def plot_all(y_true, y_pred, mains, title=None):
    fig, axes = plt.subplots(nrows=3, sharex=True)
    axes[0].plot(y_pred)
    axes[0].set_title('y_pred')
    axes[1].plot(y_true)
    axes[1].set_title('y_true')
    axes[2].plot(mains)
    axes[2].set_title('mains')
    if title:
        fig.set_title(title)
    plt.show()
    return fig, axes

In [5]:
# Run metrics
def calc_metrics(houses):
    scores = pd.Panel(
        np.NaN,
        items=APPLIANCES,
        major_axis=METRICS,
        minor_axis=ALGORITHMS
    )
    
    for appliance in APPLIANCES:
        houses_for_appliance = houses[appliance]
        on_power_threshold = ON_POWER_THRESHOLDS[appliance]
        for algo in ALGORITHMS:
            house_scores = pd.DataFrame(
                np.NaN, columns=METRICS, index=houses_for_appliance)
            for house_i in houses_for_appliance:
                y_true, y_pred, mains = load(algo, house_i, appliance)
                house_scores_dict = run_metrics(
                    y_true, y_pred, mains, on_power_threshold)
                house_scores_dict.pop('sum_abs_diff')
                house_scores.loc[house_i] = house_scores_dict
            scores[appliance, :, algo].update(house_scores.dropna().mean())
    
    scores['across all appliances'] = scores.mean(axis=0)
    return scores

In [6]:
test_houses_scores = calc_metrics(TEST_HOUSES)
train_houses_scores = calc_metrics(TRAIN_HOUSES)

In [11]:
scores_hdf_filename = join(DATA_PATH, 'all_scores.hdf')
test_houses_scores.to_hdf(scores_hdf_filename, key='test')
train_houses_scores.to_hdf(scores_hdf_filename, key='train')

In [12]:
APPLIANCE = 'washing machine'
test_houses_scores[APPLIANCE]


Out[12]:
co fhmm ae rectangles rnn
f1_score 0.101690 0.077404 0.132470 0.265773 0.025179
precision_score 0.056811 0.041181 0.070952 0.293043 0.012812
recall_score 0.484127 0.642857 0.996392 0.243146 0.725108
accuracy_score 0.882384 0.789273 0.820545 0.981527 0.227960
relative_error_in_total_energy 0.734942 0.859433 0.478495 -0.738145 0.909220
total_energy_correctly_assigned 0.930384 0.881886 0.957229 0.980871 0.808618
mean_absolute_error 39.467577 66.962825 24.248299 10.844981 108.500605

In [13]:
train_houses_scores[APPLIANCE]


Out[13]:
co fhmm ae rectangles rnn
f1_score 0.128903 0.111168 0.251175 0.493818 0.087758
precision_score 0.075282 0.061310 0.148191 0.724677 0.048637
recall_score 0.564659 0.869108 0.992013 0.377078 0.617400
accuracy_score 0.687574 0.385451 0.763328 0.966354 0.311202
relative_error_in_total_energy 0.648831 0.759248 0.179166 -0.651331 0.732140
total_energy_correctly_assigned 0.920369 0.877248 0.959632 0.973316 0.882958
mean_absolute_error 87.919616 137.529573 44.060847 28.470840 132.590392

In [14]:
train_houses_scores[APPLIANCE]


Out[14]:
co fhmm ae rectangles rnn
f1_score 0.128903 0.111168 0.251175 0.493818 0.087758
precision_score 0.075282 0.061310 0.148191 0.724677 0.048637
recall_score 0.564659 0.869108 0.992013 0.377078 0.617400
accuracy_score 0.687574 0.385451 0.763328 0.966354 0.311202
relative_error_in_total_energy 0.648831 0.759248 0.179166 -0.651331 0.732140
total_energy_correctly_assigned 0.920369 0.877248 0.959632 0.973316 0.882958
mean_absolute_error 87.919616 137.529573 44.060847 28.470840 132.590392

In [15]:
y_true, y_pred, mains = load('rectangles', 5, APPLIANCE)
plot_all(y_true, y_pred, mains)


Out[15]:
(<matplotlib.figure.Figure at 0x7fec7dc80b90>,
 array([<matplotlib.axes._subplots.AxesSubplot object at 0x7fec7b74e550>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fec700aac10>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fec6a7f2590>], dtype=object))

In [261]:
# plot
import seaborn as sns
sns.reset_orig()
# deep, muted, bright, pastel, dark, colorblind
#FIGSIZE = (8.1, 8.7)
FIGSIZE = (3.33, 3.8)
#COLOR = sns.palettes.color_palette('deep', n_colors=5)

COLOR = ['#5F7343', '#99A63C', '#FEC06A', '#F25430', '#E61924']
#FONTSIZE = 10
FONTSIZE = 6
NUMBERS_ON_PLOT_FONTSIZE = 4.5

def plot_scores(scores):
    appliances = list(scores.items)
    metrics = list(scores.major_axis)
    algorithms = list(scores.minor_axis)
    
    nrows = len(metrics)
    ncols = len(appliances)
    n_algorithms = len(algorithms)
    x = range(n_algorithms)
    
    fig, axes = plt.subplots(nrows=nrows, ncols=ncols, sharey='row', figsize=FIGSIZE)
    fig.patch.set_facecolor('white')
    for row_i, metric in enumerate(metrics):
        for col_i, appliance in enumerate(appliances):
            ax = axes[row_i, col_i]
            scores_for_algorithms = scores[appliance, metric]
            rects = ax.bar(
                x, scores_for_algorithms, color=COLOR, edgecolor=COLOR, zorder=3)

            # Numbers on the plot
            if row_i == 6:  # mean absolute error (watts)
                text_y = 90
                text_format = '{:3.0f}'
            elif row_i == 4:  # relative error in total energy
                text_y = 0
            else:
                text_y = 0.5
                text_format = '{:.2f}'

            # Draw text
            for i, rect in enumerate(rects):
                ax.text(
                    rect.get_x() + (1/12.),
                    text_y,
                    text_format.format(scores_for_algorithms[i]),
                    va='center', rotation=90, fontsize=NUMBERS_ON_PLOT_FONTSIZE)

            # Formatting
            ax.set_xticks([])
            ax.tick_params(direction='out')
            #ax.yaxis.grid(
            #    b=True, which='major', color='white', linestyle='-', zorder=0)
            ax.patch.set_facecolor((0.9, 0.9, 0.9))

            if row_i in [0, 1, 2, 3, 5]:
                ax.set_ylim((0, 1))                
            elif row_i == 4:  # relative error in total energy
                ax.set_ylim((-1, 1))
            elif row_i == 6:  # relative error in total energy
                ax.set_ylim((0, 200))

            for spine in ['top', 'right', 'left', 'bottom']:
                ax.spines[spine].set_visible(False)

            if row_i == 0:
                if appliance == 'across all appliances':
                    label = 'Across all\nappliances'
                else:
                    label = appliance.replace(' ', '\n')
                    label = label[0].capitalize() + label[1:]
                ax.set_title(label, fontsize=FONTSIZE)
            if col_i == 0:
                label = metric.replace('_', '\n')
                if label == 'mean\nabsolute\nerror':
                    label = label + '\n(watts)'
                elif label == 'total\nenergy\ncorrectly\nassigned':
                    label = 'prop. of\n' + label
                elif label == 'relative\nerror\nin\ntotal\nenergy':
                    label = 'relative\nerror in\ntotal\nenergy'
                label = label[0].capitalize() + label[1:]
                ylabel = ax.set_ylabel(label, fontsize=FONTSIZE)
                ylabel.set_rotation('horizontal')
                ylabel.set_verticalalignment('center')
                ylabel.set_horizontalalignment('center')
                ax.yaxis.labelpad = 17
                ax.tick_params(axis='y', left='on', right='off')
                ax.yaxis.set_major_locator(ticker.MaxNLocator(2))
                ax.tick_params(axis='both', which='major', labelsize=4.5, length=4, 
                               width=0.05)#, color=(0.5, 0.5, 0.5))
                # ax.xaxis.set_tick_params(width=5)
            else:
                ax.tick_params(axis='y', left='off', right='off')

    plt.subplots_adjust(hspace=0.3, top=0.94, bottom=0.07, left=0.20, right=0.9955)
    plt.legend(rects, full_algorithm_names,#["CO", "FHMM", "Autoencoder", "Rectangles", "LSTM"], 
               ncol=5, loc=(-7.7, -0.7),
               frameon=False, fontsize=5, handlelength=1)
    return fig, axes

In [262]:
import nilmtk
nilmtk.plots.latexify()

In [263]:
fig, axes = plot_scores(test_houses_scores)
#fig.suptitle('Unseen houses', fontsize=16)
plt.savefig(join(PLOT_PATH, 'unseen_houses.pdf'))
#plt.show()
plt.close()

In [264]:
fig, axes = plot_scores(train_houses_scores)
#fig.suptitle('Train houses', fontsize=16)
plt.savefig(join(PLOT_PATH, 'train_houses.pdf'))
#plt.show()

In [133]:
plt.legend?

In [ ]: