"""
Tests for DBSCAN clustering algorithm
"""

import pickle

import numpy as np
from numpy.testing import assert_raises

from scipy.spatial import distance

from sklearn.utils.testing import assert_equal
from sklearn.cluster.dbscan_ import DBSCAN, dbscan
from .common import generate_clustered_data
from sklearn.metrics.pairwise import pairwise_distances

n_clusters = 3
X = generate_clustered_data(n_clusters=n_clusters)

def test_dbscan_similarity():
    """Tests the DBSCAN algorithm with a similarity array."""
    # Parameters chosen specifically for this task.
    eps = 0.15
    min_samples = 10
    # Compute similarities
    D = distance.squareform(distance.pdist(X))
    D /= np.max(D)
    # Compute DBSCAN
    core_samples, labels = dbscan(D, metric="precomputed", eps=eps,
                                  min_samples=min_samples)
    # number of clusters, ignoring noise if present
    n_clusters_1 = len(set(labels)) - (1 if -1 in labels else 0)

    assert_equal(n_clusters_1, n_clusters)

    db = DBSCAN(metric="precomputed", eps=eps, min_samples=min_samples)
    labels = db.fit(D).labels_

    n_clusters_2 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_2, n_clusters)

def test_dbscan_feature():
    """Tests the DBSCAN algorithm with a feature vector array."""
    # Parameters chosen specifically for this task.
    # Different eps to other test, because distance is not normalised.
    eps = 0.8
    min_samples = 10
    metric = 'euclidean'
    # Compute DBSCAN
    # parameters chosen for task
    core_samples, labels = dbscan(X, metric=metric, eps=eps,
                                  min_samples=min_samples)

    # number of clusters, ignoring noise if present
    n_clusters_1 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_1, n_clusters)

    db = DBSCAN(metric=metric, eps=eps, min_samples=min_samples)
    labels = db.fit(X).labels_

    n_clusters_2 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_2, n_clusters)

def test_dbscan_callable():
    """Tests the DBSCAN algorithm with a callable metric."""
    # Parameters chosen specifically for this task.
    # Different eps to other test, because distance is not normalised.
    eps = 0.8
    min_samples = 10
    # metric is the function reference, not the string key.
    metric = distance.euclidean
    # Compute DBSCAN
    # parameters chosen for task
    core_samples, labels = dbscan(X, metric=metric, eps=eps,
                                  min_samples=min_samples,
                                  algorithm='ball_tree')

    # number of clusters, ignoring noise if present
    n_clusters_1 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_1, n_clusters)

    db = DBSCAN(metric=metric, eps=eps, min_samples=min_samples,
                algorithm='ball_tree')
    labels = db.fit(X).labels_

    n_clusters_2 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_2, n_clusters)

def test_dbscan_balltree():
    """Tests the DBSCAN algorithm with balltree for neighbor calculation."""
    eps = 0.8
    min_samples = 10

    D = pairwise_distances(X)
    core_samples, labels = dbscan(D, metric="precomputed", eps=eps,
                                  min_samples=min_samples)

    # number of clusters, ignoring noise if present
    n_clusters_1 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_1, n_clusters)

    db = DBSCAN(p=2.0, eps=eps, min_samples=min_samples, algorithm='ball_tree')
    labels = db.fit(X).labels_

    n_clusters_2 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_2, n_clusters)

    db = DBSCAN(p=2.0, eps=eps, min_samples=min_samples, algorithm='kd_tree')
    labels = db.fit(X).labels_

    n_clusters_3 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_3, n_clusters)

    db = DBSCAN(p=1.0, eps=eps, min_samples=min_samples, algorithm='ball_tree')
    labels = db.fit(X).labels_

    n_clusters_4 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_4, n_clusters)

    db = DBSCAN(leaf_size=20, eps=eps, min_samples=min_samples,
                algorithm='ball_tree')
    labels = db.fit(X).labels_

    n_clusters_5 = len(set(labels)) - int(-1 in labels)
    assert_equal(n_clusters_5, n_clusters)

def test_input_validation():
    """DBSCAN.fit should accept a list of lists."""
    X = [[1., 2.], [3., 4.]]
    DBSCAN().fit(X)             # must not raise exception

def test_dbscan_badargs():
    """Test bad argument values: these should all raise ValueErrors"""
    assert_raises(ValueError,
                  dbscan,
                  X, eps=-1.0)
    assert_raises(ValueError,
                  dbscan,
                  X, algorithm='blah')
    assert_raises(ValueError,
                  dbscan,
                  X, metric='blah')
    assert_raises(ValueError,
                  dbscan,
                  X, leaf_size=-1)
    assert_raises(ValueError,
                  dbscan,
                  X, p=-1)

def test_pickle():
    obj = DBSCAN()
    s = pickle.dumps(obj)
    assert_equal(type(pickle.loads(s)), obj.__class__)


# -*- coding: utf-8 -*-
# Copyright 2016 The Switch-Chile Authors. All rights reserved.
# Licensed under the Apache License, Version 2, which is in the LICENSE file.
# Operations, Control and Markets laboratory at Pontificia Universidad
# Católica de Chile.

import pandas, os, re, datetime, sys
from unidecode import unidecode

if sys.getdefaultencoding() != 'utf-8':
    # Character encoding may raise errors if set in ascii or other simple
    # encodings which do not support spanish characters.
    reload(sys)
    sys.setdefaultencoding('utf-8')

def limpiar(a):
    # Devuelvo un string limpio de carácteres anómalos, espacios y comas
    limpio = unidecode(a.replace(' ','_').replace('ó','o')).lower().replace(',','_')
    while limpio[0] == '_':
        limpio = limpio[1:]
    while limpio[-1] == '_':
        limpio = limpio[:-1]
    return limpio

def SepararLineaSIC(a):
    #Algunos nombres separan 220 KV en vez de 220KV, hacemos este cambio para que queden igual
    a = a.replace('k','K').replace('v','V').replace(' KV','KV')
    try:
        #Divido por guion y obtengo primer elemento
        se1 = limpiar(a.split('-')[0])
        #Obtengo 220kv al eliminar el ultimo termino con espacio y se lo quito al string, luego divido por guion
        se2 = limpiar(a.replace(a.split(' ')[-1],'').split('-')[1])
        return [se1,se2]
    except:
        print('No es posible separar',a)
        return [limpiar(a),limpiar(a)]

def SepararLineaSIC2(a):
    a = a.replace('k','K').replace('v','V').replace(' KV','KV')
    try:
        #Divido por guion y obtengo primer elemento
        se1 = limpiar(a.split('-')[0])
        #Obtengo 220kv al eliminar el ultimo termino con espacio y se lo quito al string, luego divido por guion
        se2 = limpiar(' '.join(a.split('-')[1].split('KV')[0].split(' ')[:-1]))
        return [se1,se2]
    except:
        print('No es posible separar',a)
        return [limpiar(a),limpiar(a)]

def SepararLineaSING(a):
    try:
        a = a.split('kV ')[1]
        #Divido por guion y obtengo primer elemento
        se1 = limpiar(a.split('-')[0])
        #Obtengo 220kv al eliminar el ultimo termino con espacio y se lo quito al string, luego divido por guion
        se2 = limpiar(a.split('-')[1])
        return [se1,se2]
    except:
        print('No es posible separar',a)
        return [limpiar(a),limpiar(a)]

###############################
# Obtenemos los datos del SIC #
###############################

#Archivo de conversion de unidades a abrir
transmision = pandas.read_excel('capacidad_instalada_de_transmision.xlsx', sheetname= 'SIC', parse_cols = 'E:K', skiprows=6)
transmision.columns = ['SE','Tramo','dsa','Tension (KV)', 'N','Longitud (km)','Capacidad (MVA)']
#Obtenemos las columnas
#for i,j in enumerate(transmision.columns.values):
#    print(limpiar(j),'=',i)

linea = 0
tramo = 1
tension = 3
numerocircuitos = 4
longitud = 5
capacidad = 6

#Construimos un data frame de dos columnas, de subestaciones por linea
SE = pandas.DataFrame({'SE1' : [],'SE2' : [], 'SEalt1' : [],'SEalt2' : []})

for i in transmision.index:
    #Mientras leamos
    if pandas.isnull(transmision.ix[i,linea]):
        break
    subs = SepararLineaSIC2(transmision.ix[i,tramo])
    subs2 = SepararLineaSIC(transmision.ix[i,linea])
    #print(subs,subs2)
    fila = pandas.DataFrame([[subs[0],subs[1], subs2[0], subs2[1]]], columns=['SE1','SE2','SEalt1','SEalt2'])
    SE = SE.append(fila, ignore_index = True)

#Hacemos la nueva matriz con las subestaciones, voltaje y
neotransmision = pandas.concat([pandas.Series(['sic' for i in range(i)], name = 'Sistema'),  SE.ix[:i,0], SE.ix[:i,1], SE.ix[:i,2], SE.ix[:i,3], transmision.ix[:i-1,3], transmision.iloc[:i,4], transmision.iloc[:i,5], transmision.iloc[:i,6]], names = None, axis = 1)

################################
# Obtenemos los datos del SING #
################################

#Leemos, eliminando las dos primeras lineas correspondientes al header (celdas agrupadas no se leen bien...)
transmision = pandas.read_excel('capacidad_instalada_de_transmision.xlsx', sheetname= 'SING', parse_cols = 'E:J', skiprows=6,header = None)
transmision = transmision[2:].reset_index(drop = True)
linea = 0
tension = 1
numerocircuitos = 2
longitud = 3
capacidad = 5

#Construimos un data frame de dos columnas, de subestaciones por linea
SE = pandas.DataFrame({'SE1' : [],'SE2' : [], 'SEalt1' : [],'SEalt2' : []})

for i in transmision.index:
    #Mientras leamos
    if pandas.isnull(transmision.ix[i,linea]):
        break

    subs = SepararLineaSING(transmision.ix[i,linea])
    fila = pandas.DataFrame([[subs[0],subs[1],subs[0],subs[1]]], columns=['SE1','SE2','SEalt1','SEalt2'])
    SE = SE.append(fila, ignore_index = True)

    #Si no tiene limite, le asignamos la capacidad
    if transmision.ix[i,capacidad] == 'N/I' or pandas.isnull(transmision.ix[i,capacidad]):
        transmision.ix[i,capacidad] = transmision.ix[i,4]

#Hacemos la nueva matriz con las subestaciones, voltaje y
neotransmision2 = pandas.concat([pandas.Series(['sing' for i in range(i)], name = 'Sistema'), SE.ix[:i,0], SE.ix[:i,1], SE.ix[:i,0], SE.ix[:i,1], transmision.ix[:i,tension], transmision.ix[:i,numerocircuitos], transmision.iloc[:i,longitud], transmision.iloc[:i,capacidad]], names = None, axis = 1)
neotransmision2 = neotransmision2[:-1]

#Renombramos columnas
neotransmision2.columns = ['Sistema','SE1','SE2','SEalt1','SEalt2','Tension (KV)', 'N','Longitud (km)','Capacidad (MVA)']

#Unimos ambas
transmisionfinal = pandas.concat([neotransmision, neotransmision2])

#Convertimos filas a int
transmisionfinal[['Tension (KV)', 'N']] = transmisionfinal[['Tension (KV)', 'N']].astype(int)

#Imprimimos datos
transmisionfinal.to_csv('transmision.csv', index = None , float_format = '%.2f')


import numpy
import random

import matplotlib.pyplot as plot
from sklearn.tree import DecisionTreeRegressor
from sklearn import tree

a = 1
b = 2

# Build a simple data set with y = x + random
nPoints = 1000

# x values for plotting
xPlot = [(float(i) / float(nPoints) - 0.5) for i in range(nPoints + 1)]

# x needs to be list of lists.
x = [[s] for s in xPlot]

# y (labels) has random noise added to x-value
# set seed
random.seed(1)
numpy.random.seed(1)
y = [a + b * s * s + numpy.random.normal(scale=0.1) for s in xPlot]

# take fixed test set 30% of sample
nSample = int(nPoints * 0.30)
idxTest = random.sample(range(nPoints), nSample)
idxTest.sort()
idxTrain = [idx for idx in range(nPoints) if not (idx in idxTest)]

# Define test and training attribute and label sets
xTrain = [x[r] for r in idxTrain]
xTest = [x[r] for r in idxTest]
yTrain = [y[r] for r in idxTrain]
yTest = [y[r] for r in idxTest]

# train a series of models on random subsets of the training data
# collect the models in a list and check error of composite as list grows
# maximum number of models to generate
numTreesMax = 30

# tree depth - typically at the high end
treeDepth = 5

# initialize a list to hold models
modelList = []
predList = []
eps = 0.3

# initialize residuals to be the labels y
residuals = list(yTrain)

for iTrees in range(numTreesMax):
    modelList.append(DecisionTreeRegressor(max_depth=treeDepth))
    modelList[-1].fit(xTrain, residuals)

    # make prediction with latest model and add to list of predictions
    latestInSamplePrediction = modelList[-1].predict(xTrain)

    # use new predictions to update residuals
    residuals = [residuals[i] - eps * latestInSamplePrediction[i] \
                 for i in range(len(residuals))]

    latestOutSamplePrediction = modelList[-1].predict(xTest)
    predList.append(list(latestOutSamplePrediction))

# build cumulative prediction from first "n" models
mse = []
allPredictions = []
for iModels in range(len(modelList)):
    # add the first "iModels" of the predictions and multiply by eps
    prediction = []
    for iPred in range(len(xTest)):
        prediction.append(
            sum([predList[i][iPred] for i in range(iModels + 1)]) * eps)

    allPredictions.append(prediction)
    errors = [(yTest[i] - prediction[i]) for i in range(len(yTest))]
    mse.append(sum([e * e for e in errors]) / len(yTest))

nModels = [i + 1 for i in range(len(modelList))]

# mse plot
plot.plot(nModels, mse)
plot.axis('tight')
plot.xlabel('Number of Models in Ensemble')
plot.ylabel('Mean Squared Error')
plot.ylim((0.0, max(mse)))
# plot.show()
plot.savefig("mseEx2.png")
plot.close()

print min(mse)

# predictions plot
plotList = [0, 14, 29]
lineType = [':', '-.', '--']
plot.figure()
for i in range(len(plotList)):
    iPlot = plotList[i]
    textLegend = 'Prediction with ' + str(iPlot) + ' Trees'
    plot.plot(xTest, allPredictions[iPlot],
              label=textLegend, linestyle=lineType[i])

plot.plot(xTest, yTest, label='True y Value', alpha=0.25)
plot.legend(bbox_to_anchor=(1, 0.3))
plot.axis('tight')
plot.xlabel('x value')
plot.ylabel('Predictions')
# plot.show()
plot.savefig("predictionsEx2.png")
plot.close()

# save first 2 tree
with open("tree1Ex2.dot", 'w') as f1:
    f1 = tree.export_graphviz(modelList[0], out_file=f1)

with open("tree2Ex2.dot", 'w') as f2:
    f2 = tree.export_graphviz(modelList[1], out_file=f2)


import copy
import json
import yaml
from sklearn.model_selection import ParameterGrid

def expand_config(base_file, hyper_file, output_file):
    """
    This is useful for taking the qanta.yaml config, a set of values to try for different hyper parameters, and
    generating a configuration representing each value in the parameter sweep
    """
    with open(base_file) as f:
        base_conf = yaml.load(f)

    with open(hyper_file) as f:
        hyper_conf = yaml.load(f)

    all_base_guessers = base_conf["guessers"]
    final_guessers = {}

    for guesser, params in hyper_conf["parameters"].items():
        base_guesser_conf = all_base_guessers[guesser]
        if len(base_guesser_conf) != 1:
            raise ValueError(
                "More than one configuration for parameter tuning base is invalid"
            )
        base_guesser_conf = base_guesser_conf[0]

        parameter_set = set(base_guesser_conf.keys()) | set(params.keys())
        param_grid = {}
        for p in parameter_set:
            if p in params:
                param_grid[p] = params[p]
            else:
                param_grid[p] = [base_guesser_conf[p]]

        parameter_list = list(ParameterGrid(param_grid))
        final_guessers[guesser] = parameter_list

    final_conf = copy.deepcopy(base_conf)
    for g in final_conf["guessers"]:
        if g in final_guessers:
            final_conf["guessers"][g] = copy.deepcopy(final_guessers[g])

    # There is a bug in yaml.dump that doesn't handle outputting nested dicts/arrays correctly. I didn't want to debug
    # So instead output to json then convert that to yaml
    with open("/tmp/qanta-tmp.json", "w") as f:
        json.dump(final_conf, f)

    with open("/tmp/qanta-tmp.json") as f:
        conf = json.load(f)

    with open(output_file, "w") as f:
        yaml.dump(conf, f)


'''
Created on 2 Sep 2014

@author: chris
'''
'''File format - data, length of data, containing unicode
               - target, length of data, contains int reference to target
               - target_names, type names relative to target
               - filenames, names of files storing data (probably target too)

               '''

def main():
    ''' taken from the tutorials, I'm having a look at how they store datasets'''
    from sklearn.datasets import fetch_20newsgroups
#     import numpy as np

    categories = ['alt.atheism', 'soc.religion.christian', 'comp.graphics', 'sci.med']

    twenty_train = fetch_20newsgroups(subset='train',
                                      categories=categories,
                                      shuffle=True,
                                      random_state=42)

    print dir(twenty_train)
    print twenty_train.keys()
#     print twenty_train.data[0]
    print twenty_train.target[0]
    print len(twenty_train.filenames)
    print twenty_train.filenames[0]
    print twenty_train.target_names

if __name__ == '__main__':
    main()


from __future__ import unicode_literals

import numpy as np

from sklearn.feature_extraction import FeatureHasher

from nose.tools import assert_raises, assert_true
from numpy.testing import assert_array_equal, assert_equal

def test_feature_hasher_dicts():
    h = FeatureHasher(n_features=16)
    assert_equal("dict", h.input_type)

    raw_X = [{"dada": 42, "tzara": 37}, {"gaga": 17}]
    X1 = FeatureHasher(n_features=16).transform(raw_X)
    gen = (iter(d.items()) for d in raw_X)
    X2 = FeatureHasher(n_features=16, input_type="pair").transform(gen)
    assert_array_equal(X1.toarray(), X2.toarray())

def test_feature_hasher_strings():
    # mix byte and Unicode strings; note that "foo" is a duplicate in row 0
    raw_X = [["foo", "bar", "baz", "foo".encode("ascii")],
             ["bar".encode("ascii"), "baz", "quux"]]

    for lg_n_features in (7, 9, 11, 16, 22):
        n_features = 2 ** lg_n_features

        it = (x for x in raw_X)                 # iterable

        h = FeatureHasher(n_features, non_negative=True, input_type="string")
        X = h.transform(it)

        assert_equal(X.shape[0], len(raw_X))
        assert_equal(X.shape[1], n_features)

        assert_true(np.all(X.data > 0))
        assert_equal(X[0].sum(), 4)
        assert_equal(X[1].sum(), 3)

        assert_equal(X.nnz, 6)

def test_feature_hasher_pairs():
    raw_X = (iter(d.items()) for d in [{"foo": 1, "bar": 2},
                                       {"baz": 3, "quux": 4, "foo": -1}])
    h = FeatureHasher(n_features=16, input_type="pair")
    x1, x2 = h.transform(raw_X).toarray()
    x1_nz = sorted(np.abs(x1[x1 != 0]))
    x2_nz = sorted(np.abs(x2[x2 != 0]))
    assert_equal([1, 2], x1_nz)
    assert_equal([1, 3, 4], x2_nz)

def test_hash_empty_input():
    n_features = 16
    raw_X = [[], (), iter(range(0))]

    h = FeatureHasher(n_features=n_features, input_type="string")
    X = h.transform(raw_X)

    assert_array_equal(X.A, np.zeros((len(raw_X), n_features)))

def test_hasher_invalid_input():
    assert_raises(ValueError, FeatureHasher, input_type="gobbledygook")
    assert_raises(ValueError, FeatureHasher, n_features=-1)
    assert_raises(ValueError, FeatureHasher, n_features=0)
    assert_raises(TypeError, FeatureHasher, n_features='ham')

    h = FeatureHasher(n_features=np.uint16(2 ** 6))
    assert_raises(ValueError, h.transform, [])
    assert_raises(Exception, h.transform, [[5.5]])
    assert_raises(Exception, h.transform, [[None]])

def test_hasher_set_params():
    # Test delayed input validation in fit (useful for grid search).
    hasher = FeatureHasher()
    hasher.set_params(n_features=np.inf)
    assert_raises(TypeError, hasher.fit)

def test_hasher_zeros():
    # Assert that no zeros are materialized in the output.
    X = FeatureHasher().transform([{'foo': 0}])
    assert_equal(X.data.shape, (0,))


"""Classes to represent heatmaps, i.e. float arrays of ``[0.0, 1.0]``."""
from __future__ import print_function, division, absolute_import

import numpy as np
import six.moves as sm

from .. import imgaug as ia
from .base import IAugmentable

class HeatmapsOnImage(IAugmentable):
    """Object representing heatmaps on a single image.

    Parameters
    ----------
    arr : (H,W) ndarray or (H,W,C) ndarray
        Array representing the heatmap(s) on a single image.
        Multiple heatmaps may be provided, in which case ``C`` is expected to
        denote the heatmap index.
        The array must be of dtype ``float32``.

    shape : tuple of int
        Shape of the image on which the heatmap(s) is/are placed.
        **Not** the shape of the heatmap(s) array, unless it is identical
        to the image shape (note the likely difference between the arrays
        in the number of channels).
        This is expected to be ``(H, W)`` or ``(H, W, C)`` with ``C`` usually
        being ``3``.
        If there is no corresponding image, use ``(H_arr, W_arr)`` instead,
        where ``H_arr`` is the height of the heatmap(s) array
        (analogous ``W_arr``).

    min_value : float, optional
        Minimum value for the heatmaps that `arr` represents. This will
        usually be ``0.0``.

    max_value : float, optional
        Maximum value for the heatmaps that `arr` represents. This will
        usually be ``1.0``.

    """

    def __init__(self, arr, shape, min_value=0.0, max_value=1.0):
        """Construct a new HeatmapsOnImage object."""
        assert ia.is_np_array(arr), (
            "Expected numpy array as heatmap input array, "
            "got type %s" % (type(arr),))
        # TODO maybe allow 0-sized heatmaps? in that case the min() and max()
        #      must be adjusted
        assert arr.shape[0] > 0 and arr.shape[1] > 0, (
            "Expected numpy array as heatmap with height and width greater "
            "than 0, got shape %s." % (arr.shape,))
        assert arr.dtype.name in ["float32"], (
            "Heatmap input array expected to be of dtype float32, "
            "got dtype %s." % (arr.dtype,))
        assert arr.ndim in [2, 3], (
            "Heatmap input array must be 2d or 3d, got shape %s." % (
                arr.shape,))
        assert len(shape) in [2, 3], (
            "Argument 'shape' in HeatmapsOnImage expected to be 2d or 3d, "
            "got shape %s." % (shape,))
        assert min_value < max_value, (
            "Expected min_value to be lower than max_value, "
            "got %.4f and %.4f" % (min_value, max_value))

        eps = np.finfo(arr.dtype).eps
        components = arr.flat[0:50]
        beyond_min = np.min(components) < min_value - eps
        beyond_max = np.max(components) > max_value + eps
        if beyond_min or beyond_max:
            ia.warn(
                "Value range of heatmap was chosen to be (%.8f, %.8f), but "
                "found actual min/max of (%.8f, %.8f). Array will be "
                "clipped to chosen value range." % (
                    min_value, max_value, np.min(arr), np.max(arr)))
            arr = np.clip(arr, min_value, max_value)

        if arr.ndim == 2:
            arr = arr[..., np.newaxis]
            self.arr_was_2d = True
        else:
            self.arr_was_2d = False

        min_is_zero = 0.0 - eps < min_value < 0.0 + eps
        max_is_one = 1.0 - eps < max_value < 1.0 + eps
        if min_is_zero and max_is_one:
            self.arr_0to1 = arr
        else:
            self.arr_0to1 = (arr - min_value) / (max_value - min_value)

        self.shape = shape

        self.min_value = min_value
        self.max_value = max_value

    def get_arr(self):
        """Get the heatmap's array in value range provided to ``__init__()``.

        The :class:`HeatmapsOnImage` object saves heatmaps internally in the
        value range ``[0.0, 1.0]``. This function converts the internal
        representation to ``[min, max]``, where ``min`` and ``max`` are
        provided to :func:`HeatmapsOnImage.__init__` upon instantiation of
        the object.

        Returns
        -------
        (H,W) ndarray or (H,W,C) ndarray
            Heatmap array of dtype ``float32``.

        """
        if self.arr_was_2d and self.arr_0to1.shape[2] == 1:
            arr = self.arr_0to1[:, :, 0]
        else:
            arr = self.arr_0to1

        eps = np.finfo(np.float32).eps
        min_is_zero = 0.0 - eps < self.min_value < 0.0 + eps
        max_is_one = 1.0 - eps < self.max_value < 1.0 + eps
        if min_is_zero and max_is_one:
            return np.copy(arr)

        diff = self.max_value - self.min_value
        return self.min_value + diff * arr

    # TODO
    # def find_global_maxima(self):
    #    raise NotImplementedError()

    def draw(self, size=None, cmap="jet"):
        """Render the heatmaps as RGB images.

        Parameters
        ----------
        size : None or float or iterable of int or iterable of float, optional
            Size of the rendered RGB image as ``(height, width)``.
            See :func:`~imgaug.imgaug.imresize_single_image` for details.
            If set to ``None``, no resizing is performed and the size of the
            heatmaps array is used.

        cmap : str or None, optional
            Name of the ``matplotlib`` color map to use when convert the
            heatmaps to RGB images.
            If set to ``None``, no color map will be used and the heatmaps
            will be converted to simple intensity maps.

        Returns
        -------
        list of (H,W,3) ndarray
            Rendered heatmaps as ``uint8`` arrays.
            Always a **list** containing one RGB image per heatmap array
            channel.

        """
        heatmaps_uint8 = self.to_uint8()
        heatmaps_drawn = []

        for c in sm.xrange(heatmaps_uint8.shape[2]):
            # We use c:c+1 here to get a (H,W,1) array. Otherwise imresize
            # would have to re-attach an axis.
            heatmap_c = heatmaps_uint8[..., c:c+1]

            if size is not None:
                heatmap_c_rs = ia.imresize_single_image(
                    heatmap_c, size, interpolation="nearest")
            else:
                heatmap_c_rs = heatmap_c
            heatmap_c_rs = np.squeeze(heatmap_c_rs).astype(np.float32) / 255.0

            if cmap is not None:
                # import only when necessary (faster startup; optional
                # dependency; less fragile -- see issue #225)
                import matplotlib.pyplot as plt

                cmap_func = plt.get_cmap(cmap)
                heatmap_cmapped = cmap_func(heatmap_c_rs)
                heatmap_cmapped = np.delete(heatmap_cmapped, 3, 2)
            else:
                heatmap_cmapped = np.tile(
                    heatmap_c_rs[..., np.newaxis], (1, 1, 3))

            heatmap_cmapped = np.clip(
                heatmap_cmapped * 255, 0, 255).astype(np.uint8)

            heatmaps_drawn.append(heatmap_cmapped)
        return heatmaps_drawn

    def draw_on_image(self, image, alpha=0.75, cmap="jet", resize="heatmaps"):
        """Draw the heatmaps as overlays over an image.

        Parameters
        ----------
        image : (H,W,3) ndarray
            Image onto which to draw the heatmaps.
            Expected to be of dtype ``uint8``.

        alpha : float, optional
            Alpha/opacity value to use for the mixing of image and heatmaps.
            Larger values mean that the heatmaps will be more visible and the
            image less visible.

        cmap : str or None, optional
            Name of the ``matplotlib`` color map to use.
            See :func:`HeatmapsOnImage.draw` for details.

        resize : {'heatmaps', 'image'}, optional
            In case of size differences between the image and heatmaps,
            either the image or the heatmaps can be resized. This parameter
            controls which of the two will be resized to the other's size.

        Returns
        -------
        list of (H,W,3) ndarray
            Rendered overlays as ``uint8`` arrays.
            Always a **list** containing one RGB image per heatmap array
            channel.

        """
        # assert RGB image
        assert image.ndim == 3, (
            "Expected to draw on three-dimensional image, "
            "got %d dimensions with shape %s instead." % (
                image.ndim, image.shape))
        assert image.shape[2] == 3, (
            "Expected RGB image, got %d channels instead." % (image.shape[2],))
        assert image.dtype.name == "uint8", (
            "Expected uint8 image, got dtype %s." % (image.dtype.name,))

        assert 0 - 1e-8 <= alpha <= 1.0 + 1e-8, (
            "Expected 'alpha' to be in the interval [0.0, 1.0], got %.4f" % (
                alpha))
        assert resize in ["heatmaps", "image"], (
            "Expected resize to be \"heatmaps\" or \"image\", "
            "got %s instead." % (resize,))

        if resize == "image":
            image = ia.imresize_single_image(
                image, self.arr_0to1.shape[0:2], interpolation="cubic")

        heatmaps_drawn = self.draw(
            size=image.shape[0:2] if resize == "heatmaps" else None,
            cmap=cmap)

        # TODO use blend_alpha here
        mix = [
            np.clip(
                (1-alpha) * image + alpha * heatmap_i,
                0, 255
            ).astype(np.uint8)
            for heatmap_i
            in heatmaps_drawn]

        return mix

    def invert(self):
        """Invert each component in the heatmap.

        This shifts low values towards high values and vice versa.

        This changes each value to::

            v' = max - (v - min)

        where ``v`` is the value at a spatial location, ``min`` is the
        minimum value in the heatmap and ``max`` is the maximum value.
        As the heatmap uses internally a ``0.0`` to ``1.0`` representation,
        this simply becomes ``v' = 1.0 - v``.

        This function can be useful e.g. when working with depth maps, where
        algorithms might have an easier time representing the furthest away
        points with zeros, requiring an inverted depth map.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Inverted heatmap.

        """
        arr_inv = HeatmapsOnImage.from_0to1(
            1 - self.arr_0to1,
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)
        arr_inv.arr_was_2d = self.arr_was_2d
        return arr_inv

    def pad(self, top=0, right=0, bottom=0, left=0, mode="constant", cval=0.0):
        """Pad the heatmaps at their top/right/bottom/left side.

        Parameters
        ----------
        top : int, optional
            Amount of pixels to add at the top side of the heatmaps.
            Must be ``0`` or greater.

        right : int, optional
            Amount of pixels to add at the right side of the heatmaps.
            Must be ``0`` or greater.

        bottom : int, optional
            Amount of pixels to add at the bottom side of the heatmaps.
            Must be ``0`` or greater.

        left : int, optional
            Amount of pixels to add at the left side of the heatmaps.
            Must be ``0`` or greater.

        mode : string, optional
            Padding mode to use. See :func:`~imgaug.imgaug.pad` for details.

        cval : number, optional
            Value to use for padding `mode` is ``constant``.
            See :func:`~imgaug.imgaug.pad` for details.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Padded heatmaps of height ``H'=H+top+bottom`` and
            width ``W'=W+left+right``.

        """
        from ..augmenters import size as iasize
        arr_0to1_padded = iasize.pad(
            self.arr_0to1,
            top=top,
            right=right,
            bottom=bottom,
            left=left,
            mode=mode,
            cval=cval)
        # TODO change to deepcopy()
        return HeatmapsOnImage.from_0to1(
            arr_0to1_padded,
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)

    def pad_to_aspect_ratio(self, aspect_ratio, mode="constant", cval=0.0,
                            return_pad_amounts=False):
        """Pad the heatmaps until they match a target aspect ratio.

        Depending on which dimension is smaller (height or width), only the
        corresponding sides (left/right or top/bottom) will be padded. In
        each case, both of the sides will be padded equally.

        Parameters
        ----------
        aspect_ratio : float
            Target aspect ratio, given as width/height. E.g. ``2.0`` denotes
            the image having twice as much width as height.

        mode : str, optional
            Padding mode to use.
            See :func:`~imgaug.imgaug.pad` for details.

        cval : number, optional
            Value to use for padding if `mode` is ``constant``.
            See :func:`~imgaug.imgaug.pad` for details.

        return_pad_amounts : bool, optional
            If ``False``, then only the padded instance will be returned.
            If ``True``, a tuple with two entries will be returned, where
            the first entry is the padded instance and the second entry are
            the amounts by which each array side was padded. These amounts are
            again a tuple of the form ``(top, right, bottom, left)``, with
            each value being an integer.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Padded heatmaps as :class:`HeatmapsOnImage` instance.

        tuple of int
            Amounts by which the instance's array was padded on each side,
            given as a tuple ``(top, right, bottom, left)``.
            This tuple is only returned if `return_pad_amounts` was set to
            ``True``.

        """
        from ..augmenters import size as iasize
        arr_0to1_padded, pad_amounts = iasize.pad_to_aspect_ratio(
            self.arr_0to1,
            aspect_ratio=aspect_ratio,
            mode=mode,
            cval=cval,
            return_pad_amounts=True)
        # TODO change to deepcopy()
        heatmaps = HeatmapsOnImage.from_0to1(
            arr_0to1_padded,
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)
        if return_pad_amounts:
            return heatmaps, pad_amounts
        return heatmaps

    def avg_pool(self, block_size):
        """Average-pool the heatmap(s) array using a given block/kernel size.

        Parameters
        ----------
        block_size : int or tuple of int
            Size of each block of values to pool, aka kernel size.
            See :func:`~imgaug.imgaug.pool` for details.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Heatmaps after average pooling.

        """
        arr_0to1_reduced = ia.avg_pool(self.arr_0to1, block_size, pad_cval=0.0)
        return HeatmapsOnImage.from_0to1(
            arr_0to1_reduced,
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)

    def max_pool(self, block_size):
        """Max-pool the heatmap(s) array using a given block/kernel size.

        Parameters
        ----------
        block_size : int or tuple of int
            Size of each block of values to pool, aka kernel size.
            See :func:`~imgaug.imgaug.pool` for details.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Heatmaps after max-pooling.

        """
        arr_0to1_reduced = ia.max_pool(self.arr_0to1, block_size)
        return HeatmapsOnImage.from_0to1(
            arr_0to1_reduced,
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)

    @ia.deprecated(alt_func="HeatmapsOnImage.resize()",
                   comment="resize() has the exactly same interface.")
    def scale(self, *args, **kwargs):
        """Resize the heatmap(s) array given a target size and interpolation."""
        return self.resize(*args, **kwargs)

    def resize(self, sizes, interpolation="cubic"):
        """Resize the heatmap(s) array given a target size and interpolation.

        Parameters
        ----------
        sizes : float or iterable of int or iterable of float
            New size of the array in ``(height, width)``.
            See :func:`~imgaug.imgaug.imresize_single_image` for details.

        interpolation : None or str or int, optional
            The interpolation to use during resize.
            See :func:`~imgaug.imgaug.imresize_single_image` for details.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Resized heatmaps object.

        """
        arr_0to1_resized = ia.imresize_single_image(
            self.arr_0to1, sizes, interpolation=interpolation)

        # cubic interpolation can lead to values outside of [0.0, 1.0],
        # see https://github.com/opencv/opencv/issues/7195
        # TODO area interpolation too?
        arr_0to1_resized = np.clip(arr_0to1_resized, 0.0, 1.0)

        return HeatmapsOnImage.from_0to1(
            arr_0to1_resized,
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)

    def to_uint8(self):
        """Convert this heatmaps object to an ``uint8`` array.

        Returns
        -------
        (H,W,C) ndarray
            Heatmap as an ``uint8`` array, i.e. with the discrete value
            range ``[0, 255]``.

        """
        # TODO this always returns (H,W,C), even if input ndarray was
        #      originally (H,W). Does it make sense here to also return
        #      (H,W) if self.arr_was_2d?
        arr_0to255 = np.clip(np.round(self.arr_0to1 * 255), 0, 255)
        arr_uint8 = arr_0to255.astype(np.uint8)
        return arr_uint8

    @staticmethod
    def from_uint8(arr_uint8, shape, min_value=0.0, max_value=1.0):
        """Create a ``float``-based heatmaps object from an ``uint8`` array.

        Parameters
        ----------
        arr_uint8 : (H,W) ndarray or (H,W,C) ndarray
            Heatmap(s) array, where ``H`` is height, ``W`` is width
            and ``C`` is the number of heatmap channels.
            Expected dtype is ``uint8``.

        shape : tuple of int
            Shape of the image on which the heatmap(s) is/are placed.
            **Not** the shape of the heatmap(s) array, unless it is identical
            to the image shape (note the likely difference between the arrays
            in the number of channels).
            If there is not a corresponding image, use the shape of the
            heatmaps array.

        min_value : float, optional
            Minimum value of the float heatmaps that the input array
            represents. This will usually be 0.0. In most other cases it will
            be close to the interval ``[0.0, 1.0]``.
            Calling :func:`~imgaug.HeatmapsOnImage.get_arr`, will automatically
            convert the interval ``[0.0, 1.0]`` float array to this
            ``[min, max]`` interval.

        max_value : float, optional
            Minimum value of the float heatmaps that the input array
            represents. This will usually be 1.0.
            See parameter `min_value` for details.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Heatmaps object.

        """
        arr_0to1 = arr_uint8.astype(np.float32) / 255.0
        return HeatmapsOnImage.from_0to1(
            arr_0to1, shape,
            min_value=min_value,
            max_value=max_value)

    @staticmethod
    def from_0to1(arr_0to1, shape, min_value=0.0, max_value=1.0):
        """Create a heatmaps object from a ``[0.0, 1.0]`` float array.

        Parameters
        ----------
        arr_0to1 : (H,W) or (H,W,C) ndarray
            Heatmap(s) array, where ``H`` is the height, ``W`` is the width
            and ``C`` is the number of heatmap channels.
            Expected dtype is ``float32``.

        shape : tuple of ints
            Shape of the image on which the heatmap(s) is/are placed.
            **Not** the shape of the heatmap(s) array, unless it is identical
            to the image shape (note the likely difference between the arrays
            in the number of channels).
            If there is not a corresponding image, use the shape of the
            heatmaps array.

        min_value : float, optional
            Minimum value of the float heatmaps that the input array
            represents. This will usually be 0.0. In most other cases it will
            be close to the interval ``[0.0, 1.0]``.
            Calling :func:`~imgaug.HeatmapsOnImage.get_arr`, will automatically
            convert the interval ``[0.0, 1.0]`` float array to this
            ``[min, max]`` interval.

        max_value : float, optional
            Minimum value of the float heatmaps that the input array
            represents. This will usually be 1.0.
            See parameter `min_value` for details.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Heatmaps object.

        """
        heatmaps = HeatmapsOnImage(arr_0to1, shape,
                                   min_value=0.0, max_value=1.0)
        heatmaps.min_value = min_value
        heatmaps.max_value = max_value
        return heatmaps

    # TODO change name to change_value_range()?
    @classmethod
    def change_normalization(cls, arr, source, target):
        """Change the value range of a heatmap array.

        E.g. the value range may be changed from the interval ``[0.0, 1.0]``
        to ``[-1.0, 1.0]``.

        Parameters
        ----------
        arr : ndarray
            Heatmap array to modify.

        source : tuple of float
            Current value range of the input array, given as a
            tuple ``(min, max)``, where both are ``float`` values.

        target : tuple of float
            Desired output value range of the array, given as a
            tuple ``(min, max)``, where both are ``float`` values.

        Returns
        -------
        ndarray
            Input array, with value range projected to the desired target
            value range.

        """
        assert ia.is_np_array(arr), (
            "Expected 'arr' to be an ndarray, got type %s." % (type(arr),))

        def _validate_tuple(arg_name, arg_value):
            assert isinstance(arg_value, tuple), (
                "'%s' was not a HeatmapsOnImage instance, "
                "expected type tuple then. Got type %s." % (
                    arg_name, type(arg_value),))
            assert len(arg_value) == 2, (
                "Expected tuple '%s' to contain exactly two entries, "
                "got %d." % (arg_name, len(arg_value),))
            assert arg_value[0] < arg_value[1], (
                "Expected tuple '%s' to have two entries with "
                "entry 1 < entry 2, got values %.4f and %.4f." % (
                    arg_name, arg_value[0], arg_value[1]))

        if isinstance(source, HeatmapsOnImage):
            source = (source.min_value, source.max_value)
        else:
            _validate_tuple("source", source)

        if isinstance(target, HeatmapsOnImage):
            target = (target.min_value, target.max_value)
        else:
            _validate_tuple("target", target)

        # Check if source and target are the same (with a tiny bit of
        # tolerance) if so, evade compuation and just copy the array instead.
        # This is reasonable, as source and target will often both
        # be (0.0, 1.0).
        eps = np.finfo(arr.dtype).eps
        mins_same = source[0] - 10*eps < target[0] < source[0] + 10*eps
        maxs_same = source[1] - 10*eps < target[1] < source[1] + 10*eps
        if mins_same and maxs_same:
            return np.copy(arr)

        min_source, max_source = source
        min_target, max_target = target

        diff_source = max_source - min_source
        diff_target = max_target - min_target

        arr_0to1 = (arr - min_source) / diff_source
        arr_target = min_target + arr_0to1 * diff_target

        return arr_target

    # TODO make this a proper shallow-copy
    def copy(self):
        """Create a shallow copy of the heatmaps object.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Shallow copy.

        """
        return self.deepcopy()

    def deepcopy(self):
        """Create a deep copy of the heatmaps object.

        Returns
        -------
        imgaug.augmentables.heatmaps.HeatmapsOnImage
            Deep copy.

        """
        return HeatmapsOnImage(
            self.get_arr(),
            shape=self.shape,
            min_value=self.min_value,
            max_value=self.max_value)


import pytest

from pandas.util._move import BadMove, move_into_mutable_buffer, stolenbuf

def test_cannot_create_instance_of_stolen_buffer():
    # Stolen buffers need to be created through the smart constructor
    # "move_into_mutable_buffer," which has a bunch of checks in it.

    msg = "cannot create 'pandas.util._move.stolenbuf' instances"
    with pytest.raises(TypeError, match=msg):
        stolenbuf()

def test_more_than_one_ref():
    # Test case for when we try to use "move_into_mutable_buffer"
    # when the object being moved has other references.

    b = b"testing"

    with pytest.raises(BadMove, match="testing") as e:

        def handle_success(type_, value, tb):
            assert value.args[0] is b
            return type(e).handle_success(e, type_, value, tb)  # super

        e.handle_success = handle_success
        move_into_mutable_buffer(b)

def test_exactly_one_ref():
    # Test case for when the object being moved has exactly one reference.

    b = b"testing"

    # We need to pass an expression on the stack to ensure that there are
    # not extra references hanging around. We cannot rewrite this test as
    #   buf = b[:-3]
    #   as_stolen_buf = move_into_mutable_buffer(buf)
    # because then we would have more than one reference to buf.
    as_stolen_buf = move_into_mutable_buffer(b[:-3])

    # Materialize as byte-array to show that it is mutable.
    assert bytearray(as_stolen_buf) == b"test"


# coding: utf-8

import numpy as np

from .block import Block
from .._global import OptionalModule

try:
  import matplotlib.pyplot as plt
  from matplotlib.widgets import Button
except (ModuleNotFoundError, ImportError):
  plt = OptionalModule("matplotlib")
  Button = OptionalModule("matplotlib")

class Grapher(Block):
  """The grapher receive data from a block (via a :ref:`Link`) and plots it."""

  def __init__(self,
               *labels,
               length=0,
               freq=2,
               maxpt=20000,
               window_size=(8, 8),
               window_pos=None,
               interp=True,
               backend="TkAgg"):
    """Sets the args and initializes the parent class.

    Args:
      *labels (:obj:`tuple`): Tuples of the columns labels of input data for
        plotting. You can add as much as you want, depending on your
        performances. The first value is the `x` label, the second is the `y`
        label.
      length (:obj:`int`, optional): If `0` the graph is static and displays
        all data from the start of the assay. Else only displays the last
        ``length`` received chunks, and drops the previous ones.
      freq (:obj:`float`, optional): The refresh rate of the graph. May cause
        high memory consumption if set too high.
      maxpt (:obj:`int`, optional): The maximum number of points displayed on
        the graph. When reaching this limit, the block deletes one point out of
        two but this is almost invisible to the user.
      window_size (:obj:`tuple`, optional): The size of the graph, in inches.
      window_pos (:obj:`tuple`, optional): The position of the graph in pixels.
        The first value is for the `x` direction, the second for the `y`
        direction. The origin is the top left corner. Works with multiple
        screens.
      interp (:obj:`bool`, optional): If :obj:`True`, the points of data will
        be linked to the following by straight lines. Else, each value wil be
        displayed as constant until the next update.
      backend (:obj:`int`, optional): The :mod:`matplotlib` backend to use.

    Example:
      ::

        graph = Grapher(('t(s)', 'F(N)'), ('t(s)', 'def(%)'))

      will plot a dynamic graph with two lines plot (`F=f(t)` and `def=f(t)`).
      ::

        graph = Grapher(('def(%)', 'F(N)'), length=0)

      will plot a static graph.
      ::

        graph = Grapher(('t(s)', 'F(N)'), length=30)

      will plot a dynamic graph displaying the last 30 chunks of data.
    """

    Block.__init__(self)
    self.niceness = 10
    self.length = length
    self.freq = freq
    self.maxpt = maxpt
    self.window_size = window_size
    self.window_pos = window_pos
    self.interp = interp
    self.backend = backend

    self.labels = labels

  def prepare(self):
    if self.backend:
      plt.switch_backend(self.backend)
    self.f = plt.figure(figsize=self.window_size)
    self.ax = self.f.add_subplot(111)
    self.lines = []
    for _ in self.labels:
      if self.interp:
        self.lines.append(self.ax.plot([], [])[0])
      else:
        self.lines.append(self.ax.step([], [])[0])
    # Keep only 1/factor points on each line
    self.factor = [1 for _ in self.labels]
    # Count to drop exactly 1/factor points, no more and no less
    self.counter = [0 for _ in self.labels]
    legend = [y for x, y in self.labels]
    plt.legend(legend, bbox_to_anchor=(-0.03, 1.02, 1.06, .102), loc=3,
               ncol=len(legend), mode="expand", borderaxespad=1)
    plt.xlabel(self.labels[0][0])
    plt.ylabel(self.labels[0][1])
    plt.grid()
    self.axclear = plt.axes([.8, .02, .15, .05])
    self.bclear = Button(self.axclear, 'Clear')
    self.bclear.on_clicked(self.clear)

    if self.window_pos:
      mng = plt.get_current_fig_manager()
      mng.window.wm_geometry("+%s+%s" % self.window_pos)
    plt.draw()
    plt.pause(.001)

  def clear(self, event=None):
    for line in self.lines:
      line.set_xdata([])
      line.set_ydata([])
    self.factor = [1 for _ in self.labels]
    self.counter = [0 for _ in self.labels]

  def loop(self):
    # We need to recv data from all the links, but keep
    # ALL of the data, even with the same label (so not get_all_last)
    data = self.recv_all_delay()
    for i, (lx, ly) in enumerate(self.labels):
      x, y = 0, 0  # So that if we don't find it, we do nothing
      for d in data:
        if lx in d and ly in d:  # Find the first input with both labels
          dx = d[lx][self.factor[i]-self.counter[i]-1::self.factor[i]]
          dy = d[ly][self.factor[i]-self.counter[i]-1::self.factor[i]]
          self.counter[i] = (self.counter[i]+len(d[lx])) % self.factor[i]
          x = np.hstack((self.lines[i].get_xdata(), dx))
          y = np.hstack((self.lines[i].get_ydata(), dy))
          break
      if isinstance(x, int):
        break
      if self.length and len(x) >= self.length:
        # Remove the beginning if the graph is dynamic
        x = x[-self.length:]
        y = y[-self.length:]
      elif len(x) > self.maxpt:
        # Reduce the number of points if we have to many to display
        print("[Grapher] Too many points on the graph {} ({}>{})".format(
          i, len(x), self.maxpt))
        x, y = x[::2], y[::2]
        self.factor[i] *= 2
        print("[Grapher] Resampling factor is now {}".format(self.factor[i]))
      self.lines[i].set_xdata(x)
      self.lines[i].set_ydata(y)
    self.ax.relim()  # Update the window
    self.ax.autoscale_view(True, True, True)
    self.f.canvas.draw()  # Update the graph
    self.f.canvas.flush_events()

  def finish(self):
    plt.close("all")