"""3D visualisation inside the notebook.
"""
import warnings
import numpy as np
import pandas as pd
from matplotlib import cm
from ipywidgets import interact

from ..config.draw import sheet_spec
from ..utils.utils import spec_updater, get_sub_eptm

try:
    import ipyvolume as ipv
except ImportError:
    print(
        """
This module needs ipyvolume to work.
You can install it with:
$ conda install -c conda-forge ipyvolume
"""
    )

def browse_history(history, coords=["x", "y", "z"], **draw_specs_kw):

    times = history.time_stamps
    num_frames = times.size
    draw_specs = sheet_spec()
    spec_updater(draw_specs, draw_specs_kw)
    sheet = history.retrieve(0)
    ipv.clear()
    fig, meshes = sheet_view(sheet, coords, **draw_specs_kw)

    lim_inf = sheet.vert_df[sheet.coords].min().min()
    lim_sup = sheet.vert_df[sheet.coords].max().max()
    ipv.xyzlim(lim_inf, lim_sup)

    def set_frame(i=0):
        fig.animation = 0
        t = times[i]
        meshes = _get_meshes(history.retrieve(t), coords, draw_specs)
        update_view(fig, meshes)

    ipv.show()
    interact(set_frame, i=(0, num_frames - 1))

def update_view(fig, meshes):

    for old, new in zip(fig.meshes, meshes):
        old.x = new.x
        old.y = new.y
        old.z = new.z
        old.color = new.color
        old.triangles = new.triangles
        old.lines = new.lines

def sheet_view(sheet, coords=["x", "y", "z"], **draw_specs_kw):
    """
    Creates a javascript renderer of the edge lines to be displayed
    in Jupyter Notebooks

    Returns
    -------

    fig: a :class:`ipyvolume.widgets.Figure` widget
    mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget

    """

    # ipv.style.use(["dark", "minimal"])
    draw_specs = sheet_spec()
    spec_updater(draw_specs, draw_specs_kw)
    fig = ipv.gcf()
    fig.meshes = fig.meshes + _get_meshes(sheet, coords, draw_specs)
    box_size = max(*(np.ptp(sheet.vert_df[u]) for u in sheet.coords))
    border = 0.05 * box_size
    lim_inf = sheet.vert_df[sheet.coords].min().min() - border
    lim_sup = sheet.vert_df[sheet.coords].max().max() + border
    ipv.xyzlim(lim_inf, lim_sup)
    return fig, fig.meshes

def view_ipv(sheet, coords=["x", "y", "z"], **edge_specs):
    """
    Creates a javascript renderer of the edge lines to be displayed
    in Jupyter Notebooks

    Returns
    -------

    fig: a :class:`ipyvolume.widgets.Figure` widget
    mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget

    """
    warnings.warn("`view_ipv` is deprecated, use the more generic `sheet_view`")
    mesh = edge_mesh(sheet, coords, **edge_specs)
    fig = ipv.gcf()
    fig.meshes = fig.meshes + [mesh]
    box_size = max(*(np.ptp(sheet.vert_df[u]) for u in sheet.coords))
    border = 0.05 * box_size
    lim_inf = sheet.vert_df[sheet.coords].min().min() - border
    lim_sup = sheet.vert_df[sheet.coords].max().max() + border
    ipv.xyzlim(lim_inf, lim_sup)

    return fig, mesh

def edge_mesh(sheet, coords, **edge_specs):
    """
    Creates a ipyvolume Mesh of the edge lines to be displayed
    in Jupyter Notebooks

    Returns
    -------
    mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget

    """
    spec = sheet_spec()["edge"]
    spec.update(**edge_specs)
    if callable(spec["color"]):
        spec["color"] = spec["color"](sheet)

    if isinstance(spec["color"], str):
        color = spec["color"]
    elif hasattr(spec["color"], "__len__"):
        color = _wire_color_from_sequence(spec, sheet)[:, :3]

    u, v, w = coords
    mesh = ipv.Mesh(
        x=sheet.vert_df[u],
        y=sheet.vert_df[v],
        z=sheet.vert_df[w],
        lines=sheet.edge_df[["srce", "trgt"]].astype(dtype=np.uint32),
        color=color,
    )
    return mesh

def face_mesh(sheet, coords, **face_draw_specs):
    """
    Creates a ipyvolume Mesh of the face polygons
    """
    Ne, Nf = sheet.Ne, sheet.Nf
    if callable(face_draw_specs["color"]):
        face_draw_specs["color"] = face_draw_specs["color"](sheet)

    if isinstance(face_draw_specs["color"], str):
        color = face_draw_specs["color"]

    elif hasattr(face_draw_specs["color"], "__len__"):
        color = _face_color_from_sequence(face_draw_specs, sheet)[:, :3]

    if "visible" in sheet.face_df.columns:
        edges = sheet.edge_df[sheet.upcast_face(sheet.face_df["visible"])].index
        _sheet = get_sub_eptm(sheet, edges)
        if _sheet is not None:
            sheet = _sheet
            if isinstance(color, np.ndarray):
                faces = sheet.face_df["face_o"].values.astype(np.uint32)
                edges = edges.values.astype(np.uint32)
                indexer = np.concatenate([faces, edges + Nf, edges + Ne + Nf])
                color = color.take(indexer, axis=0)

    epsilon = face_draw_specs.get("epsilon", 0)
    up_srce = sheet.edge_df[["s" + c for c in coords]]
    up_trgt = sheet.edge_df[["t" + c for c in coords]]

    Ne, Nf = sheet.Ne, sheet.Nf

    if epsilon > 0:
        up_face = sheet.edge_df[["f" + c for c in coords]].values
        up_srce = (up_srce - up_face) * (1 - epsilon) + up_face
        up_trgt = (up_trgt - up_face) * (1 - epsilon) + up_face

    mesh_ = np.concatenate(
        [sheet.face_df[coords].values, up_srce.values, up_trgt.values]
    )

    triangles = np.vstack(
        [sheet.edge_df["face"], np.arange(Ne) + Nf, np.arange(Ne) + Ne + Nf]
    ).T.astype(dtype=np.uint32)

    mesh = ipv.Mesh(
        x=mesh_[:, 0], y=mesh_[:, 1], z=mesh_[:, 2], triangles=triangles, color=color
    )
    return mesh

def _wire_color_from_sequence(edge_spec, sheet):
    """
    """
    color_ = edge_spec["color"]
    cmap = cm.get_cmap(edge_spec.get("colormap", "viridis"))
    if color_.shape in [(sheet.Nv, 3), (sheet.Nv, 4)]:
        return np.asarray(color_)
    if color_.shape == (sheet.Nv,):
        if np.ptp(color_) < 1e-10:
            return np.ones((sheet.Nv, 3)) * 0.7
        return cmap((color_ - color_.min()) / np.ptp(color_))

    if color_.shape in [(sheet.Ne, 3), (sheet.Ne, 4)]:
        color_ = pd.DataFrame(color_, index=sheet.edge_df.index)
        color_["srce"] = sheet.edge_df["srce"]
        color_ = color_.groupby("srce").mean().values
        return color_
    if color_.shape == (sheet.Ne,):
        color_ = pd.DataFrame(color_, index=sheet.edge_df.index)
        color_["srce"] = sheet.edge_df["srce"]
        color_ = color_.groupby("srce").mean().values.ravel()
        if np.ptp(color_) < 1e-10:
            warnings.warn("Attempting to draw a colormap " "with a uniform value")
            return np.ones((sheet.Nv, 3)) * 0.7
        return cmap((color_ - color_.min()) / np.ptp(color_))
    else:
        raise ValueError("The 'color' value of the spec doesn't have a correct shape.")

def _face_color_from_sequence(face_spec, sheet):
    color_ = face_spec["color"]

    cmap = cm.get_cmap(face_spec.get("colormap", "viridis"))
    Nf, Ne = sheet.Nf, sheet.Ne
    color_min, color_max = face_spec.get("color_range", (color_.min(), color_.max()))

    face_mesh_shape = Nf + 2 * Ne

    if color_.shape in [(sheet.Nf, 3), (sheet.Nf, 4)]:
        return np.concatenate([color_, color_, color_])

    elif color_.shape == (sheet.Nf,):
        if np.ptp(color_) < 1e-10:
            # warnings.warn("Attempting to draw a colormap with a uniform value")
            return np.ones((face_mesh_shape, 3)) * 0.5

        normed = (color_ - color_min) / (color_max - color_min)
        up_color = sheet.upcast_face(normed).values
        return cmap(np.concatenate([normed, up_color, up_color]))

    else:
        raise ValueError(
            "shape of `face_spec['color']` must be either (Nf, 3), (Nf, 4) or (Nf,)"
        )

def _get_meshes(sheet, coords, draw_specs):

    meshes = []
    edge_spec = draw_specs["edge"]
    if edge_spec["visible"]:
        edges = edge_mesh(sheet, coords, **edge_spec)
        meshes.append(edges)
    else:
        edges = None

    face_spec = draw_specs["face"]
    if face_spec["visible"]:
        faces = face_mesh(sheet, coords, **face_spec)
        meshes.append(faces)
    else:
        faces = None
    return meshes


"""
Tests for DBSCAN clustering algorithm
"""

import pickle

import numpy as np

from scipy.spatial import distance
from scipy import sparse

from sklearn.utils.testing import assert_equal
from sklearn.utils.testing import assert_array_equal
from sklearn.utils.testing import assert_raises
from sklearn.utils.testing import assert_in
from sklearn.utils.testing import assert_not_in
from sklearn.cluster.dbscan_ import DBSCAN
from sklearn.cluster.dbscan_ import dbscan
from sklearn.cluster.tests.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_sparse():
    core_sparse, labels_sparse = dbscan(sparse.lil_matrix(X), eps=.8,
                                        min_samples=10)
    core_dense, labels_dense = dbscan(X, eps=.8, min_samples=10)
    assert_array_equal(core_dense, core_sparse)
    assert_array_equal(labels_dense, labels_sparse)

def test_dbscan_no_core_samples():
    rng = np.random.RandomState(0)
    X = rng.rand(40, 10)
    X[X < .8] = 0

    for X_ in [X, sparse.csr_matrix(X)]:
        db = DBSCAN(min_samples=6).fit(X_)
        assert_array_equal(db.components_, np.empty((0, X_.shape[1])))
        assert_array_equal(db.labels_, -1)
        assert_equal(db.core_sample_indices_.shape, (0,))

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__)

def test_boundaries():
    # ensure min_samples is inclusive of core point
    core, _ = dbscan([[0], [1]], eps=2, min_samples=2)
    assert_in(0, core)
    # ensure eps is inclusive of circumference
    core, _ = dbscan([[0], [1], [1]], eps=1, min_samples=2)
    assert_in(0, core)
    core, _ = dbscan([[0], [1], [1]], eps=.99, min_samples=2)
    assert_not_in(0, core)

def test_weighted_dbscan():
    # ensure sample_weight is validated
    assert_raises(ValueError, dbscan, [[0], [1]], sample_weight=[2])
    assert_raises(ValueError, dbscan, [[0], [1]], sample_weight=[2, 3, 4])

    # ensure sample_weight has an effect
    assert_array_equal([], dbscan([[0], [1]], sample_weight=None,
                                  min_samples=6)[0])
    assert_array_equal([], dbscan([[0], [1]], sample_weight=[5, 5],
                                  min_samples=6)[0])
    assert_array_equal([0], dbscan([[0], [1]], sample_weight=[6, 5],
                                   min_samples=6)[0])
    assert_array_equal([0, 1], dbscan([[0], [1]], sample_weight=[6, 6],
                                      min_samples=6)[0])

    # points within eps of each other:
    assert_array_equal([0, 1], dbscan([[0], [1]], eps=1.5,
                                      sample_weight=[5, 1], min_samples=6)[0])
    # and effect of non-positive and non-integer sample_weight:
    assert_array_equal([], dbscan([[0], [1]], sample_weight=[5, 0],
                                  eps=1.5, min_samples=6)[0])
    assert_array_equal([0, 1], dbscan([[0], [1]], sample_weight=[5.9, 0.1],
                                      eps=1.5, min_samples=6)[0])
    assert_array_equal([0, 1], dbscan([[0], [1]], sample_weight=[6, 0],
                                      eps=1.5, min_samples=6)[0])
    assert_array_equal([], dbscan([[0], [1]], sample_weight=[6, -1],
                                  eps=1.5, min_samples=6)[0])

    # for non-negative sample_weight, cores should be identical to repetition
    rng = np.random.RandomState(42)
    sample_weight = rng.randint(0, 5, X.shape[0])
    core1, label1 = dbscan(X, sample_weight=sample_weight)
    assert_equal(len(label1), len(X))

    X_repeated = np.repeat(X, sample_weight, axis=0)
    core_repeated, label_repeated = dbscan(X_repeated)
    core_repeated_mask = np.zeros(X_repeated.shape[0], dtype=bool)
    core_repeated_mask[core_repeated] = True
    core_mask = np.zeros(X.shape[0], dtype=bool)
    core_mask[core1] = True
    assert_array_equal(np.repeat(core_mask, sample_weight), core_repeated_mask)

    # sample_weight should work with precomputed distance matrix
    D = pairwise_distances(X)
    core3, label3 = dbscan(D, sample_weight=sample_weight,
                           metric='precomputed')
    assert_array_equal(core1, core3)
    assert_array_equal(label1, label3)

    # sample_weight should work with estimator
    est = DBSCAN().fit(X, sample_weight=sample_weight)
    core4 = est.core_sample_indices_
    label4 = est.labels_
    assert_array_equal(core1, core4)
    assert_array_equal(label1, label4)

    est = DBSCAN()
    label5 = est.fit_predict(X, sample_weight=sample_weight)
    core5 = est.core_sample_indices_
    assert_array_equal(core1, core5)
    assert_array_equal(label1, label5)
    assert_array_equal(label1, est.labels_)

def test_dbscan_core_samples_toy():
    X = [[0], [2], [3], [4], [6], [8], [10]]
    n_samples = len(X)

    for algorithm in ['brute', 'kd_tree', 'ball_tree']:
        # Degenerate case: every sample is a core sample, either with its own
        # cluster or including other close core samples.
        core_samples, labels = dbscan(X, algorithm=algorithm, eps=1,
                                      min_samples=1)
        assert_array_equal(core_samples, np.arange(n_samples))
        assert_array_equal(labels, [0, 1, 1, 1, 2, 3, 4])

        # With eps=1 and min_samples=2 only the 3 samples from the denser area
        # are core samples. All other points are isolated and considered noise.
        core_samples, labels = dbscan(X, algorithm=algorithm, eps=1,
                                      min_samples=2)
        assert_array_equal(core_samples, [1, 2, 3])
        assert_array_equal(labels, [-1, 0, 0, 0, -1, -1, -1])

        # Only the sample in the middle of the dense area is core. Its two
        # neighbors are edge samples. Remaining samples are noise.
        core_samples, labels = dbscan(X, algorithm=algorithm, eps=1,
                                      min_samples=3)
        assert_array_equal(core_samples, [2])
        assert_array_equal(labels, [-1, 0, 0, 0, -1, -1, -1])

        # It's no longer possible to extract core samples with eps=1:
        # everything is noise.
        core_samples, labels = dbscan(X, algorithm=algorithm, eps=1,
                                      min_samples=4)
        assert_array_equal(core_samples, [])
        assert_array_equal(labels, -np.ones(n_samples))

def test_dbscan_precomputed_metric_with_degenerate_input_arrays():
    # see https://github.com/scikit-learn/scikit-learn/issues/4641 for
    # more details
    X = np.ones((10, 2))
    labels = DBSCAN(eps=0.5, metric='precomputed').fit(X).labels_
    assert_equal(len(set(labels)), 1)

    X = np.zeros((10, 2))
    labels = DBSCAN(eps=0.5, metric='precomputed').fit(X).labels_
    assert_equal(len(set(labels)), 1)


"""
============================
Faces dataset decompositions
============================

This example applies to :ref:`olivetti_faces` different unsupervised
matrix decomposition (dimension reduction) methods from the module
:py:mod:`sklearn.decomposition` (see the documentation chapter
:ref:`decompositions`) .

"""
print(__doc__)

# Authors: Vlad Niculae, Alexandre Gramfort
# License: BSD 3 clause

import logging
from time import time

from numpy.random import RandomState
import matplotlib.pyplot as plt

from sklearn.datasets import fetch_olivetti_faces
from sklearn.cluster import MiniBatchKMeans
from sklearn import decomposition

# Display progress logs on stdout
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(message)s')
n_row, n_col = 2, 3
n_components = n_row * n_col
image_shape = (64, 64)
rng = RandomState(0)

###############################################################################
# Load faces data
dataset = fetch_olivetti_faces(shuffle=True, random_state=rng)
faces = dataset.data

n_samples, n_features = faces.shape

# global centering
faces_centered = faces - faces.mean(axis=0)

# local centering
faces_centered -= faces_centered.mean(axis=1).reshape(n_samples, -1)

print("Dataset consists of %d faces" % n_samples)

###############################################################################
def plot_gallery(title, images, n_col=n_col, n_row=n_row):
    plt.figure(figsize=(2. * n_col, 2.26 * n_row))
    plt.suptitle(title, size=16)
    for i, comp in enumerate(images):
        plt.subplot(n_row, n_col, i + 1)
        vmax = max(comp.max(), -comp.min())
        plt.imshow(comp.reshape(image_shape), cmap=plt.cm.gray,
                   interpolation='nearest',
                   vmin=-vmax, vmax=vmax)
        plt.xticks(())
        plt.yticks(())
    plt.subplots_adjust(0.01, 0.05, 0.99, 0.93, 0.04, 0.)

###############################################################################
# List of the different estimators, whether to center and transpose the
# problem, and whether the transformer uses the clustering API.
estimators = [
    ('Eigenfaces - RandomizedPCA',
     decomposition.RandomizedPCA(n_components=n_components, whiten=True),
     True),

    ('Non-negative components - NMF',
     decomposition.NMF(n_components=n_components, init='nndsvda', beta=5.0,
                       tol=5e-3, sparseness='components'),
     False),

    ('Independent components - FastICA',
     decomposition.FastICA(n_components=n_components, whiten=True),
     True),

    ('Sparse comp. - MiniBatchSparsePCA',
     decomposition.MiniBatchSparsePCA(n_components=n_components, alpha=0.8,
                                      n_iter=100, batch_size=3,
                                      random_state=rng),
     True),

    ('MiniBatchDictionaryLearning',
        decomposition.MiniBatchDictionaryLearning(n_components=15, alpha=0.1,
                                                  n_iter=50, batch_size=3,
                                                  random_state=rng),
     True),

    ('Cluster centers - MiniBatchKMeans',
        MiniBatchKMeans(n_clusters=n_components, tol=1e-3, batch_size=20,
                        max_iter=50, random_state=rng),
     True),

    ('Factor Analysis components - FA',
     decomposition.FactorAnalysis(n_components=n_components, max_iter=2),
     True),
]

###############################################################################
# Plot a sample of the input data

plot_gallery("First centered Olivetti faces", faces_centered[:n_components])

###############################################################################
# Do the estimation and plot it

for name, estimator, center in estimators:
    print("Extracting the top %d %s..." % (n_components, name))
    t0 = time()
    data = faces
    if center:
        data = faces_centered
    estimator.fit(data)
    train_time = (time() - t0)
    print("done in %0.3fs" % train_time)
    if hasattr(estimator, 'cluster_centers_'):
        components_ = estimator.cluster_centers_
    else:
        components_ = estimator.components_
    if hasattr(estimator, 'noise_variance_'):
        plot_gallery("Pixelwise variance",
                     estimator.noise_variance_.reshape(1, -1), n_col=1,
                     n_row=1)
    plot_gallery('%s - Train time %.1fs' % (name, train_time),
                 components_[:n_components])

plt.show()


"""
==========================================
IsolationForest example
==========================================

An example using IsolationForest for anomaly detection.

The IsolationForest 'isolates' observations by randomly selecting a feature
and then randomly selecting a split value between the maximum and minimum
values of the selected feature.

Since recursive partitioning can be represented by a tree structure, the
number of splittings required to isolate a sample is equivalent to the path
length from the root node to the terminating node.

This path length, averaged over a forest of such random trees, is a measure
of normality and our decision function.

Random partitioning produces noticeable shorter paths for anomalies.
Hence, when a forest of random trees collectively produce shorter path lengths
for particular samples, they are highly likely to be anomalies.

.. [1] Liu, Fei Tony, Ting, Kai Ming and Zhou, Zhi-Hua. "Isolation forest."
    Data Mining, 2008. ICDM'08. Eighth IEEE International Conference on.

"""
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import IsolationForest

rng = np.random.RandomState(42)

# Generate train data
X = 0.3 * rng.randn(100, 2)
X_train = np.r_[X + 2, X - 2]
# Generate some regular novel observations
X = 0.3 * rng.randn(20, 2)
X_test = np.r_[X + 2, X - 2]
# Generate some abnormal novel observations
X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))

# fit the model
clf = IsolationForest(max_samples=100, random_state=rng)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)

# plot the line, the samples, and the nearest vectors to the plane
xx, yy = np.meshgrid(np.linspace(-5, 5, 50), np.linspace(-5, 5, 50))
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

plt.title("IsolationForest")
plt.contourf(xx, yy, Z, cmap=plt.cm.Blues_r)

b1 = plt.scatter(X_train[:, 0], X_train[:, 1], c='white')
b2 = plt.scatter(X_test[:, 0], X_test[:, 1], c='green')
c = plt.scatter(X_outliers[:, 0], X_outliers[:, 1], c='red')
plt.axis('tight')
plt.xlim((-5, 5))
plt.ylim((-5, 5))
plt.legend([b1, b2, c],
           ["training observations",
            "new regular observations", "new abnormal observations"],
           loc="upper left")
plt.show()


__author__ = "Christian Kongsgaard"
__license__ = 'MIT'

# -------------------------------------------------------------------------------------------------------------------- #
# IMPORTS

# Modules
import pandas as pd
import matplotlib.pyplot as plt

# RiBuild Modules

# -------------------------------------------------------------------------------------------------------------------- #
# RIBuild

out_folder = r'C:\Users\ocni\PycharmProjects\delphin_6_automation\data_process\2d_1d\processed_data'
graphic_folder = r'U:\RIBuild\2D_1D\Processed Results\4A'
hdf_file = out_folder + '/relative_moisture_content.h5'

# Open HDF
# Uninsulated
dresdenzp_highratio_uninsulated_4a = pd.read_hdf(hdf_file, 'dresden_zp_high_ratio_uninsulated_4a')
dresdenzd_highratio_uninsulated_4a = pd.read_hdf(hdf_file, 'dresden_zd_high_ratio_uninsulated_4a')
postdam_highratio_uninsulated_4a = pd.read_hdf(hdf_file, 'potsdam_high_ratio_uninsulated_4a')
dresdenzp_lowratio_uninsulated_4a = pd.read_hdf(hdf_file, 'dresden_zp_low_ratio_uninsulated_4a')
dresdenzd_lowratio_uninsulated_4a = pd.read_hdf(hdf_file, 'dresden_zd_low_ratio_uninsulated_4a')
postdam_lowratio_uninsulated_4a = pd.read_hdf(hdf_file, 'potsdam_low_ratio_uninsulated_4a')

total_uninsulated_4a = pd.concat([dresdenzp_highratio_uninsulated_4a, dresdenzd_highratio_uninsulated_4a,
                                  postdam_highratio_uninsulated_4a, dresdenzp_lowratio_uninsulated_4a,
                                  dresdenzd_lowratio_uninsulated_4a, postdam_lowratio_uninsulated_4a])

# Insulated
dresdenzp_highratio_insulated_4a = pd.read_hdf(hdf_file, 'dresden_zp_high_ratio_insulated_4a')
dresdenzd_highratio_insulated_4a = pd.read_hdf(hdf_file, 'dresden_zd_high_ratio_insulated_4a')
postdam_highratio_insulated_4a = pd.read_hdf(hdf_file, 'potsdam_high_ratio_insulated_4a')
dresdenzp_lowratio_insulated_4a = pd.read_hdf(hdf_file, 'dresden_zp_low_ratio_insulated_4a')
dresdenzd_lowratio_insulated_4a = pd.read_hdf(hdf_file, 'dresden_zd_low_ratio_insulated_4a')
postdam_lowratio_insulated_4a = pd.read_hdf(hdf_file, 'potsdam_low_ratio_insulated_4a')

total_insulated_4a = pd.concat([dresdenzp_highratio_insulated_4a, dresdenzd_highratio_insulated_4a,
                                postdam_highratio_insulated_4a, dresdenzp_lowratio_insulated_4a,
                                dresdenzd_lowratio_insulated_4a, postdam_lowratio_insulated_4a])

def plots(plot, save=False):
    """
    Creates box plots from all the wall scenarios
    """

    if plot == 'uninsulated' or plot == 'all':
        plt.figure('dresdenzp_highratio_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzp_highratio_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZP - Mortar: High Cement Ratio - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzp_highratio_uninsulated_4a_moisture")

        plt.figure('dresdenzd_highratio_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzd_highratio_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZD - Mortar: High Cement Ratio - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzd_highratio_uninsulated_4a_moisture")

        plt.figure('postdam_highratio_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        postdam_highratio_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Potsdam - Mortar: High Cement Ratio - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/postdam_highratio_uninsulated_4a_moisture")

        plt.figure('dresdenzp_lowratio_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzp_lowratio_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZP - Mortar: Low Cement Ratio - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzp_lowratio_uninsulated_4a_moisture")

        plt.figure('dresdenzd_lowratio_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzd_lowratio_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZD - Mortar: Low Cement Ratio - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzd_lowratio_uninsulated_4a_moisture")

        plt.figure('postdam_lowratio_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        postdam_lowratio_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Potsdam - Mortar: Low Cement Ratio - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/postdam_lowratio_uninsulated_4a_moisture")

        plt.figure('total_uninsulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        total_uninsulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 1100)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: All - Mortar: All - Insulation: None')
        if save:
            plt.savefig(f"{graphic_folder}/total_uninsulated_4a_moisture")

    if plot == 'insulated' or plot == 'all':
        plt.figure('dresdenzp_highratio_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzp_highratio_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZP - Mortar: High Cement Ratio - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzp_highratio_insulated_4a_moisture")

        plt.figure('dresdenzd_highratio_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzd_highratio_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZD - Mortar: High Cement Ratio - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzd_highratio_insulated_4a_moisture")

        plt.figure('postdam_highratio_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        postdam_highratio_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Potsdam - Mortar: High Cement Ratio - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/postdam_highratio_insulated_4a_moisture")

        plt.figure('dresdenzp_lowratio_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzp_lowratio_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZP - Mortar: Low Cement Ratio - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzp_lowratio_insulated_4a_moisture")

        plt.figure('dresdenzd_lowratio_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        dresdenzd_lowratio_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Dresden ZD - Mortar: Low Cement Ratio - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/dresdenzd_lowratio_insulated_4a_moisture")

        plt.figure('postdam_lowratio_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        postdam_lowratio_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: Potsdam - Mortar: Low Cement Ratio - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/postdam_lowratio_insulated_4a_moisture")

        plt.figure('total_insulated_4a_moisture', figsize=(16, 8), tight_layout=True)
        total_insulated_4a.boxplot(showfliers=False)
        plt.ylim(-5, 2000)
        plt.ylabel('Relative Difference in %')
        plt.title('Weighted Relative Difference between 1D and 2D\n'
                  'Moisture Content\n'
                  'Brick: All - Mortar: All - Insulation: Calcium Silicate')
        if save:
            plt.savefig(f"{graphic_folder}/total_insulated_4a_moisture")

    plt.show()

plots('all', False)

def std3_ratio(print_=False, excel=False):
    """Computes ratio of outliers in the data sets. Outliers is here defined as data points deviating with more
    the 3 standard deviations from the mean."""

    std3_uninsulated_ratio_ = uninsulated()
    std3_insulated_ratio_ = insulated()

    if print_:
        print('Uninsulated')
        print(std3_uninsulated_ratio_)
        print('')
        print('Insulated')
        print(std3_insulated_ratio_)

    if excel:
        writer = pd.ExcelWriter(f'{out_folder}/moisture_std_ratios.xlsx')
        std3_uninsulated_ratio_.to_excel(writer, 'Uninsulated')
        std3_insulated_ratio_.to_excel(writer, 'Insulated')
        writer.save()

def uninsulated():
    """Computes the outliers for the uninsulated cases"""

    outliers_total_uninsulated = (total_uninsulated_4a.shape[0] -
                                  total_uninsulated_4a.sub(total_uninsulated_4a.mean())
                                  .div(total_uninsulated_4a.std()).abs().lt(3).sum()) / total_uninsulated_4a.shape[0]

    outliers_zd_high_uninsulated = (dresdenzd_highratio_uninsulated_4a.shape[0] -
                                    dresdenzd_highratio_uninsulated_4a.sub(dresdenzd_highratio_uninsulated_4a.mean())
                                    .div(dresdenzd_highratio_uninsulated_4a.std()).abs().lt(3).sum()) \
                                   / dresdenzd_highratio_uninsulated_4a.shape[0]

    outliers_zp_high_uninsulated = (dresdenzp_highratio_uninsulated_4a.shape[0] -
                                    dresdenzp_highratio_uninsulated_4a.sub(dresdenzp_highratio_uninsulated_4a.mean())
                                    .div(dresdenzp_highratio_uninsulated_4a.std()).abs().lt(3).sum()) \
                                   / dresdenzp_highratio_uninsulated_4a.shape[0]

    outliers_pd_high_uninsulated = (postdam_highratio_uninsulated_4a.shape[0] -
                                    postdam_highratio_uninsulated_4a.sub(postdam_highratio_uninsulated_4a.mean())
                                    .div(postdam_highratio_uninsulated_4a.std()).abs().lt(3).sum()) \
                                   / postdam_highratio_uninsulated_4a.shape[0]

    outliers_zd_low_uninsulated = (dresdenzd_lowratio_uninsulated_4a.shape[0] -
                                   dresdenzd_lowratio_uninsulated_4a.sub(dresdenzd_lowratio_uninsulated_4a.mean())
                                   .div(dresdenzd_lowratio_uninsulated_4a.std()).abs().lt(3).sum()) \
                                  / dresdenzd_lowratio_uninsulated_4a.shape[0]

    outliers_zp_low_uninsulated = (dresdenzp_lowratio_uninsulated_4a.shape[0] -
                                   dresdenzp_lowratio_uninsulated_4a.sub(dresdenzp_lowratio_uninsulated_4a.mean())
                                   .div(dresdenzp_lowratio_uninsulated_4a.std()).abs().lt(3).sum()) \
                                  / dresdenzp_lowratio_uninsulated_4a.shape[0]

    outliers_pd_low_uninsulated = (postdam_lowratio_uninsulated_4a.shape[0] -
                                   postdam_lowratio_uninsulated_4a.sub(postdam_lowratio_uninsulated_4a.mean())
                                   .div(postdam_lowratio_uninsulated_4a.std()).abs().lt(3).sum()) \
                                  / postdam_lowratio_uninsulated_4a.shape[0]

    outliers_uninsulated_ratio_ = pd.concat([outliers_total_uninsulated, outliers_zd_high_uninsulated,
                                             outliers_zp_high_uninsulated, outliers_pd_high_uninsulated,
                                             outliers_zd_low_uninsulated, outliers_zp_low_uninsulated,
                                             outliers_pd_low_uninsulated], axis=1)

    outliers_uninsulated_ratio_.columns = ["Brick: All - Mortar: All - Insulation: None",
                                           "Brick: Dresden ZD - Mortar: High Cement Ratio - Insulation: None",
                                           "Brick: Dresden ZP - Mortar: High Cement Ratio - Insulation: None",
                                           "Brick: Potsdam - Mortar: High Cement Ratio - Insulation: None",
                                           "Brick: Dresden ZD - Mortar: Low Cement Ratio - Insulation: None",
                                           "Brick: Dresden ZP - Mortar: Low Cement Ratio - Insulation: None",
                                           "Brick: Potsdam - Mortar: Low Cement Ratio - Insulation: None"]
    return outliers_uninsulated_ratio_

def insulated():
    """Computes the outliers for the insulated cases"""

    outliers_total_insulated = (total_insulated_4a.shape[0] - total_insulated_4a.sub(total_insulated_4a.mean())
                                .div(total_insulated_4a.std()).abs().lt(3).sum()) / total_insulated_4a.shape[0]

    outliers_zd_high_insulated = (dresdenzd_highratio_insulated_4a.shape[0] -
                                  dresdenzd_highratio_insulated_4a.sub(dresdenzd_highratio_insulated_4a.mean())
                                  .div(dresdenzd_highratio_insulated_4a.std()).abs().lt(3).sum()) \
                                 / dresdenzd_highratio_insulated_4a.shape[0]

    outliers_zp_high_insulated = (dresdenzp_highratio_insulated_4a.shape[0] -
                                  dresdenzp_highratio_insulated_4a.sub(dresdenzp_highratio_insulated_4a.mean())
                                  .div(dresdenzp_highratio_insulated_4a.std()).abs().lt(3).sum()) \
                                 / dresdenzp_highratio_insulated_4a.shape[0]

    outliers_pd_high_insulated = (postdam_highratio_insulated_4a.shape[0] -
                                  postdam_highratio_insulated_4a.sub(postdam_highratio_insulated_4a.mean())
                                  .div(postdam_highratio_insulated_4a.std()).abs().lt(3).sum()) \
                                 / postdam_highratio_insulated_4a.shape[0]

    outliers_zd_low_insulated = (dresdenzd_lowratio_insulated_4a.shape[0] -
                                 dresdenzd_lowratio_insulated_4a.sub(dresdenzd_lowratio_insulated_4a.mean())
                                 .div(dresdenzd_lowratio_insulated_4a.std()).abs().lt(3).sum()) \
                                / dresdenzd_lowratio_insulated_4a.shape[0]

    outliers_zp_low_insulated = (dresdenzp_lowratio_insulated_4a.shape[0] -
                                 dresdenzp_lowratio_insulated_4a.sub(dresdenzp_lowratio_insulated_4a.mean())
                                 .div(dresdenzp_lowratio_insulated_4a.std()).abs().lt(3).sum()) \
                                / dresdenzp_lowratio_insulated_4a.shape[0]

    outliers_pd_low_insulated = (postdam_lowratio_insulated_4a.shape[0] -
                                 postdam_lowratio_insulated_4a.sub(postdam_lowratio_insulated_4a.mean())
                                 .div(postdam_lowratio_insulated_4a.std()).abs().lt(3).sum()) \
                                / postdam_lowratio_insulated_4a.shape[0]

    std2_insulated_ratio_ = pd.concat([outliers_total_insulated, outliers_zd_high_insulated,
                                       outliers_zp_high_insulated, outliers_pd_high_insulated,
                                       outliers_zd_low_insulated, outliers_zp_low_insulated,
                                       outliers_pd_low_insulated], axis=1)

    std2_insulated_ratio_.columns = ["Brick: All - Mortar: All - Insulation: None",
                                     "Brick: Dresden ZD - Mortar: High Cement Ratio - Insulation: Calcium Silicate",
                                     "Brick: Dresden ZP - Mortar: High Cement Ratio - Insulation: Calcium Silicate",
                                     "Brick: Potsdam - Mortar: High Cement Ratio - Insulation: Calcium Silicate",
                                     "Brick: Dresden ZD - Mortar: Low Cement Ratio - Insulation: Calcium Silicate",
                                     "Brick: Dresden ZP - Mortar: Low Cement Ratio - Insulation: Calcium Silicate",
                                     "Brick: Potsdam - Mortar: Low Cement Ratio - Insulation: Calcium Silicate"]
    return std2_insulated_ratio_

#std3_ratio(False, True)


import csv
import os
import numpy as np
import matplotlib.pyplot as plt
import time
plt.switch_backend('agg')

TODO = {
    ('tox21', 'random'): [
        'weave', 'graphconv', 'tf', 'tf_robust', 'irv', 'xgb', 'logreg',
        'textcnn'
    ],
    ('clintox', 'random'): [
        'weave', 'graphconv', 'tf', 'tf_robust', 'irv', 'xgb', 'logreg',
        'textcnn'
    ],
    ('sider', 'random'): [
        'weave', 'graphconv', 'tf', 'tf_robust', 'irv', 'xgb', 'logreg',
        'textcnn'
    ],
    ('bbbp', 'scaffold'):
    ['weave', 'graphconv', 'tf', 'irv', 'xgb', 'logreg', 'textcnn'],
    ('bace_c', 'scaffold'):
    ['weave', 'graphconv', 'tf', 'irv', 'xgb', 'logreg', 'textcnn'],
    ('hiv', 'scaffold'):
    ['weave', 'graphconv', 'tf', 'irv', 'xgb', 'logreg', 'textcnn'],
    ('muv', 'random'): ['graphconv', 'tf', 'tf_robust', 'irv', 'xgb', 'logreg'],
    ('delaney', 'random'): [
        'weave_regression', 'graphconvreg', 'tf_regression', 'xgb_regression',
        'krr', 'textcnn_regression', 'dag_regression', 'mpnn'
    ],
    ('sampl', 'random'): [
        'weave_regression', 'graphconvreg', 'tf_regression', 'xgb_regression',
        'krr', 'textcnn_regression', 'dag_regression', 'mpnn'
    ],
    ('lipo', 'random'): [
        'weave_regression', 'graphconvreg', 'tf_regression', 'xgb_regression',
        'krr', 'textcnn_regression', 'dag_regression', 'mpnn'
    ],
    ('qm7', 'stratified'): [
        'dtnn', 'graphconvreg', 'tf_regression_ft', 'krr_ft'
    ],
    ('qm8', 'random'): [
        'dtnn', 'graphconvreg', 'weave_regression', 'textcnn_regression',
        'mpnn', 'tf_regression', 'tf_regression_ft'
    ],
}

ORDER = [
    'logreg', 'rf', 'rf_regression', 'xgb', 'xgb_regression', 'kernelsvm',
    'krr', 'krr_ft', 'tf', 'tf_regression', 'tf_regression_ft', 'tf_robust',
    'irv', 'textcnn', 'textcnn_regression', 'graphconv', 'graphconvreg', 'dag',
    'dag_regression', 'ani', 'weave', 'weave_regression', 'dtnn', 'mpnn'
]

COLOR = {
    'logreg': '#3F3F3F',
    'rf': '#67AD4F',
    'rf_regression': '#67AD4F',
    'xgb': '#0E766C',
    'xgb_regression': '#0E766C',
    'kernelsvm': '#FC926B',
    'krr': '#FC926B',
    'krr_ft': '#5A372A',
    'tf': '#2B6596',
    'tf_regression': '#2B6596',
    'tf_regression_ft': '#162939',
    'tf_robust': '#775183',
    'irv': '#D9D9D9',
    'graphconv': '#A4D192',
    'graphconvreg': '#A4D192',
    'dag': '#D06329',
    'dag_regression': '#D06329',
    'ani': '#D9D9D9',
    'weave': '#8196AE',
    'weave_regression': '#8196AE',
    'textcnn': '#811B18',
    'textcnn_regression': '#811B18',
    'dtnn': '#D06329',
    'mpnn': '#7B0A48'
}

TODO_list = set()
for key in TODO.keys():
  for val in TODO[key]:
    TODO_list.add((key[0], key[1], val))

def read_results(path):
  Results = set()
  with open(path, 'r') as f:
    reader = csv.reader(f)
    for line in reader:
      Results.add((line[0], line[1], line[3]))
  return Results

def run_benchmark(path, deepchem_dir):
  finished = read_results(path)
  os.chdir(deepchem_dir)
  os.chdir('./examples')
  while len(TODO_list - finished) > 0:
    todo = TODO_list - finished
    for p in todo:
      os.system('python benchmark.py --seed 123 -d ' + p[0] + ' -s ' + p[1] +
                ' -m ' + p[2])

def plot(dataset, split, path, out_path):
  if dataset in [
      'bace_c', 'bbbp', 'clintox', 'hiv', 'muv', 'pcba', 'pcba_146',
      'pcba_2475', 'sider', 'tox21', 'toxcast'
  ]:
    mode = 'classification'
  else:
    mode = 'regression'
  data = {}
  with open(path, 'r') as f:
    reader = csv.reader(f)
    for line in reader:
      if line[0] == dataset and line[1] == split:
        data[line[3]] = line[8]
  labels = []
  values = []
  colors = []
  for model in ORDER:
    if model in data.keys():
      labels.append(model)
      colors.append(COLOR[model])
      values.append(float(data[model]))
  y_pos = np.arange(len(labels))
  plt.rcdefaults()
  fig, ax = plt.subplots()

  ax.barh(y_pos, values, align='center', color='green')
  ax.set_yticks(y_pos)
  ax.set_yticklabels(labels)
  ax.invert_yaxis()
  if mode == 'regression':
    ax.set_xlabel('R square')
    ax.set_xlim(left=0., right=1.)
  else:
    ax.set_xlabel('ROC-AUC')
    ax.set_xlim(left=0.4, right=1.)
  t = time.localtime(time.time())
  ax.set_title("Performance on %s (%s split), %i-%i-%i" %
               (dataset, split, t.tm_year, t.tm_mon, t.tm_mday))
  plt.tight_layout()
  for i in range(len(colors)):
    ax.get_children()[i].set_color(colors[i])
    ax.text(
        values[i] - 0.1, y_pos[i] + 0.1, str("%.3f" % values[i]), color='white')
  fig.savefig(os.path.join(out_path, dataset + '_' + split + '.png'))
  #plt.show()

if __name__ == '__main__':
  current_dir = os.path.dirname(os.path.realpath(__file__))
  DEEPCHEM_DIR = os.path.split(os.path.split(current_dir)[0])[0]
  FILE = os.path.join(os.path.join(DEEPCHEM_DIR, 'examples'), 'results.csv')
  #run_benchmark(FILE, DEEPCHEM_DIR)
  save_dir = os.path.join(DEEPCHEM_DIR, 'datasets/MolNet_pic')
  if not os.path.exists(save_dir):
    os.mkdir(save_dir)
  for pair in TODO.keys():
    plot(pair[0], pair[1], FILE, save_dir)
  os.system('aws s3 sync ' + save_dir +
            ' s3://deepchem.io/trained_models/MolNet_pic')


import matplotlib.pyplot as plt
import numpy as np
import sklearn
import sklearn.datasets
import sklearn.linear_model

def plot_decision_boundary(model, X, Y):
    # Set min and max values and give it some padding
    x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
    y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
    h = 0.01
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole grid
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.ylabel('x2')
    plt.xlabel('x1')
    plt.scatter(X[0, :], X[1, :], c=Y.reshape(X[0,:].shape), cmap=plt.cm.Spectral)

def sigmoid(x):
    """
    Compute the sigmoid of x

    Arguments:
    x -- A scalar or numpy array of any size.

    Return:
    s -- sigmoid(x)
    """
    s = 1/(1+np.exp(-x))
    return s

def load_planar_dataset():
    np.random.seed(1)
    m = 400 # number of examples
    N = int(m/2) # number of points per class
    D = 2 # dimensionality
    X = np.zeros((m,D)) # data matrix where each row is a single example
    Y = np.zeros((m,1), dtype='uint8') # labels vector (0 for red, 1 for blue)
    a = 4 # maximum ray of the flower

    for j in range(2):
        ix = range(N*j,N*(j+1))
        t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2 # theta
        r = a*np.sin(4*t) + np.random.randn(N)*0.2 # radius
        X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
        Y[ix] = j

    X = X.T
    Y = Y.T

    return X, Y

def load_extra_datasets():
    N = 200
    noisy_circles = sklearn.datasets.make_circles(n_samples=N, factor=.5, noise=.3)
    noisy_moons = sklearn.datasets.make_moons(n_samples=N, noise=.2)
    blobs = sklearn.datasets.make_blobs(n_samples=N, random_state=5, n_features=2, centers=6)
    gaussian_quantiles = sklearn.datasets.make_gaussian_quantiles(mean=None, cov=0.5, n_samples=N, n_features=2, n_classes=2, shuffle=True, random_state=None)
    no_structure = np.random.rand(N, 2), np.random.rand(N, 2)

    return noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure


'''
Copyleft Oct 14, 2016 Arya Iranmehr, PhD Student, Bafna Lab, UC San Diego,  Email: airanmehr@gmail.com
'''
import numpy as np;

np.set_printoptions(linewidth=200, precision=5, suppress=True)
import pandas as pd;

pd.options.display.max_rows = 20;
pd.options.display.expand_frame_repr = False
import seaborn as sns
import matplotlib as mpl
import os;

home = os.path.expanduser('~') + '/'
import Utils.Util as utl
import Scripts.TimeSeriesPaper.RealData.Utils as rutl

a = rutl.loadAllScores().groupby(level='h', axis=1).apply(rutl.HstatisticAll)
df = pd.read_pickle(utl.outpath + 'real/scores.df')
i = df.lrd.sort_values().index[-1]
df.loc[i]

cd = pd.read_pickle(utl.outpath + 'real/CD.F59.df')

import Utils.Plots as pplt
import pylab as plt

names = rutl.loadSNPIDs()
sns.set_style("white", {"grid.color": "0.9", 'axes.linewidth': .5, "grid.linewidth": "9.99"})
mpl.rc('font', **{'family': 'serif', 'serif': ['Computer Modern']});
mpl.rc('text', usetex=True)
reload(pplt)
f, ax = plt.subplots(1, 2, sharey=True, dpi=300, figsize=(4, 2))
i = a[0.5].sort_values().index[-1]
sns.set_context("notebook", font_scale=1, rc={"lines.linewidth": 1.2})

pplt.plotSiteReal(cd.loc[i], ax=ax[0], legend=True)
ax[0].set_title('{}:{:.0f} ({})'.format(i[0], i[1], names.loc[i]), fontsize=8)

i = df.lrdiff.sort_values().index[-1]
pplt.plotSiteReal(cd.loc[i], ax=ax[1])
sns.set_context("notebook", font_scale=1, rc={"lines.linewidth": 1.2})

ax[1].set_title('{}:{:.0f} ({})'.format(i[0], i[1], names.loc[i]), fontsize=8)
plt.gcf().subplots_adjust(bottom=0.2)
pplt.savefig('topSNPs', 300)
plt.show()


#! /usr/bin/env python
# -*- coding: utf-8

'''
Name: dating.py(KNN algorithm)
Training and test dataset: dating.txt

Created on Feb 8, 2015

@author: Tao He
'''

__author__ = 'Tao He'

from numpy import array as nmarray
from matplotlib import pyplot as plt

LABEL_MAP = {
    'didntLike': 1,
    'smallDoses': 2,
    'largeDoses': 3,
}

ATTR_MAP = {
    1: 'Number of frequent flyer miles earned per year',
    2: 'Percentage of time spent playing video games',
    3: 'Liters of ice cream consumed per week',
}

def create_dataset(filename=None):
    ''' Return data group and labels.
    Get the data from file.
    If the filename is not specialed, return None.

    dataformat: flyerMiles, gameTime, icecream, label.
    '''

    def normalize_data(data=None):
        ''' Normalized dataset.
        Normalize all data to range 0-1.
        '''
        if data is None:
            return None
        for column in range(data[0].__len__()):
            max_val, min_val = max(data[:, column]), min(data[:, column])
            for row in range(data.__len__()):
                data[row][column] = (data[row][column]-min_val)/(max_val-min_val)
        return data

    if filename == None:
        return (None, None)
    group = []
    labels = []
    with open(filename, mode='r') as fp_data:
        for line in fp_data:
            group.append([float(num) for num in line[:-1].split('\t')[0:3]])
            labels.append(LABEL_MAP[line[:-1].split('\t')[3]])
    return normalize_data(nmarray(group)), labels

def draw_pic(group=None, labels=None, x=0, y=0):
    ''' Draw a subplot from data group.
    '''
    if group is None or labels is None:
        return None
    name = 'knn-dating'
    figure = plt.figure(num=name, dpi=100)
    ax_main = figure.add_subplot(1, 1, 1, xlabel=ATTR_MAP[x+1], ylabel=ATTR_MAP[y+1], title=name)
    ax_main.scatter(group[:, x], group[:, y],
                    s=15*nmarray(labels),
                    c=[[i/LABEL_MAP.__len__()] for i in labels])
    plt.show()
    ## plt.savefig('%s.png'%name, format='png', dpi=100)

def knn_classify(group, labels, attrs, ratio=0.5, item=0, k=3):
    ''' Return the type of item.
    knn classify function.
    '''

    def get_dist(i, j):
        ''' Return the distence of group[i] and group[j].
        '''
        dist = 0.0
        for attr in attrs:
            dist += (group[i][attr]-group[j][attr])*(group[i][attr]-group[j][attr])
        return dist

    length = group.__len__()
    distence = []
    for i in range(int(length*ratio), length):
        distence.append((i, get_dist(item, i)))
    cnt = {}
    distence.sort(key=lambda item: item[1])
    for i in range(k):
        label = labels[distence[i][0]]
        if label in cnt:
            cnt[label] += 1
        else:
            cnt[label] = 1
    return sorted(cnt.items(), key=lambda item: item[1], reverse=True)[0][0]

def knn():
    ''' KNN classify algorithm.
    '''
    data, labels = create_dataset('dating.txt')
    ratio, attr = 0.5, [0, 1, 2]
    cnt, cnt_correct = 0, 0
    length = data.__len__()
    for i in range(0, int(length*ratio)):
        cnt += 1
        knn_type = knn_classify(data, labels, attr, ratio, i, 3)
        # print('case[%d]: real: %d, knn: %d'%(i, labels[i], knn_type))
        if knn_type == labels[i]:
            cnt_correct += 1
    print('total: %d, correct: %d, correct ratio: %f'%(cnt, cnt_correct, cnt_correct/cnt))

if __name__ == '__main__':
    knn()

# vim: set sw=4, ts=4, fileencoding=utf-8


"""Mean shift clustering algorithm.

Mean shift clustering aims to discover *blobs* in a smooth density of
samples. It is a centroid based algorithm, which works by updating candidates
for centroids to be the mean of the points within a given region. These
candidates are then filtered in a post-processing stage to eliminate
near-duplicates to form the final set of centroids.

Seeding is performed using a binning technique for scalability.
"""

# Authors: Conrad Lee <conradlee@gmail.com>
#          Alexandre Gramfort <alexandre.gramfort@inria.fr>
#          Gael Varoquaux <gael.varoquaux@normalesup.org>
#          Martino Sorbaro <martino.sorbaro@ed.ac.uk>

import numpy as np
import warnings

from collections import defaultdict
from ..externals import six
from ..utils.validation import check_is_fitted
from ..utils import extmath, check_random_state, gen_batches, check_array
from ..base import BaseEstimator, ClusterMixin
from ..neighbors import NearestNeighbors
from ..metrics.pairwise import pairwise_distances_argmin
from ..externals.joblib import Parallel
from ..externals.joblib import delayed

def estimate_bandwidth(X, quantile=0.3, n_samples=None, random_state=0):
    """Estimate the bandwidth to use with the mean-shift algorithm.

    That this function takes time at least quadratic in n_samples. For large
    datasets, it's wise to set that parameter to a small value.

    Parameters
    ----------
    X : array-like, shape=[n_samples, n_features]
        Input points.

    quantile : float, default 0.3
        should be between [0, 1]
        0.5 means that the median of all pairwise distances is used.

    n_samples : int, optional
        The number of samples to use. If not given, all samples are used.

    random_state : int or RandomState
        Pseudo-random number generator state used for random sampling.

    Returns
    -------
    bandwidth : float
        The bandwidth parameter.
    """
    random_state = check_random_state(random_state)
    if n_samples is not None:
        idx = random_state.permutation(X.shape[0])[:n_samples]
        X = X[idx]
    nbrs = NearestNeighbors(n_neighbors=int(X.shape[0] * quantile))
    nbrs.fit(X)

    bandwidth = 0.
    for batch in gen_batches(len(X), 500):
        d, _ = nbrs.kneighbors(X[batch, :], return_distance=True)
        bandwidth += np.max(d, axis=1).sum()

    return bandwidth / X.shape[0]

# separate function for each seed's iterative loop
def _mean_shift_single_seed(my_mean, X, nbrs, max_iter):
    # For each seed, climb gradient until convergence or max_iter
    bandwidth = nbrs.get_params()['radius']
    stop_thresh = 1e-3 * bandwidth  # when mean has converged
    completed_iterations = 0
    while True:
        # Find mean of points within bandwidth
        i_nbrs = nbrs.radius_neighbors([my_mean], bandwidth,
                                       return_distance=False)[0]
        points_within = X[i_nbrs]
        if len(points_within) == 0:
            break  # Depending on seeding strategy this condition may occur
        my_old_mean = my_mean  # save the old mean
        my_mean = np.mean(points_within, axis=0)
        # If converged or at max_iter, adds the cluster
        if (extmath.norm(my_mean - my_old_mean) < stop_thresh or
                completed_iterations == max_iter):
            return tuple(my_mean), len(points_within)
        completed_iterations += 1

def mean_shift(X, bandwidth=None, seeds=None, bin_seeding=False,
               min_bin_freq=1, cluster_all=True, max_iter=300,
               max_iterations=None, n_jobs=1):
    """Perform mean shift clustering of data using a flat kernel.

    Read more in the :ref:`User Guide <mean_shift>`.

    Parameters
    ----------

    X : array-like, shape=[n_samples, n_features]
        Input data.

    bandwidth : float, optional
        Kernel bandwidth.

        If bandwidth is not given, it is determined using a heuristic based on
        the median of all pairwise distances. This will take quadratic time in
        the number of samples. The sklearn.cluster.estimate_bandwidth function
        can be used to do this more efficiently.

    seeds : array-like, shape=[n_seeds, n_features] or None
        Point used as initial kernel locations. If None and bin_seeding=False,
        each data point is used as a seed. If None and bin_seeding=True,
        see bin_seeding.

    bin_seeding : boolean, default=False
        If true, initial kernel locations are not locations of all
        points, but rather the location of the discretized version of
        points, where points are binned onto a grid whose coarseness
        corresponds to the bandwidth. Setting this option to True will speed
        up the algorithm because fewer seeds will be initialized.
        Ignored if seeds argument is not None.

    min_bin_freq : int, default=1
       To speed up the algorithm, accept only those bins with at least
       min_bin_freq points as seeds.

    cluster_all : boolean, default True
        If true, then all points are clustered, even those orphans that are
        not within any kernel. Orphans are assigned to the nearest kernel.
        If false, then orphans are given cluster label -1.

    max_iter : int, default 300
        Maximum number of iterations, per seed point before the clustering
        operation terminates (for that seed point), if has not converged yet.

    n_jobs : int
        The number of jobs to use for the computation. This works by computing
        each of the n_init runs in parallel.

        If -1 all CPUs are used. If 1 is given, no parallel computing code is
        used at all, which is useful for debugging. For n_jobs below -1,
        (n_cpus + 1 + n_jobs) are used. Thus for n_jobs = -2, all CPUs but one
        are used.

    Returns
    -------

    cluster_centers : array, shape=[n_clusters, n_features]
        Coordinates of cluster centers.

    labels : array, shape=[n_samples]
        Cluster labels for each point.

    Notes
    -----
    See examples/cluster/plot_meanshift.py for an example.

    """
    # FIXME To be removed in 0.18
    if max_iterations is not None:
        warnings.warn("The `max_iterations` parameter has been renamed to "
                      "`max_iter` from version 0.16. The `max_iterations` "
                      "parameter will be removed in 0.18", DeprecationWarning)
        max_iter = max_iterations

    if bandwidth is None:
        bandwidth = estimate_bandwidth(X)
    elif bandwidth <= 0:
        raise ValueError("bandwidth needs to be greater than zero or None,\
            got %f" % bandwidth)
    if seeds is None:
        if bin_seeding:
            seeds = get_bin_seeds(X, bandwidth, min_bin_freq)
        else:
            seeds = X
    n_samples, n_features = X.shape
    center_intensity_dict = {}
    nbrs = NearestNeighbors(radius=bandwidth).fit(X)

    # execute iterations on all seeds in parallel
    all_res = Parallel(n_jobs=n_jobs)(
        delayed(_mean_shift_single_seed)
        (seed, X, nbrs, max_iter) for seed in seeds)
    # copy results in a dictionary
    for i in range(len(seeds)):
        if all_res[i] is not None:
            center_intensity_dict[all_res[i][0]] = all_res[i][1]

    if not center_intensity_dict:
        # nothing near seeds
        raise ValueError("No point was within bandwidth=%f of any seed."
                         " Try a different seeding strategy \
                         or increase the bandwidth."
                         % bandwidth)

    # POST PROCESSING: remove near duplicate points
    # If the distance between two kernels is less than the bandwidth,
    # then we have to remove one because it is a duplicate. Remove the
    # one with fewer points.
    sorted_by_intensity = sorted(center_intensity_dict.items(),
                                 key=lambda tup: tup[1], reverse=True)
    sorted_centers = np.array([tup[0] for tup in sorted_by_intensity])
    unique = np.ones(len(sorted_centers), dtype=np.bool)
    nbrs = NearestNeighbors(radius=bandwidth).fit(sorted_centers)
    for i, center in enumerate(sorted_centers):
        if unique[i]:
            neighbor_idxs = nbrs.radius_neighbors([center],
                                                  return_distance=False)[0]
            unique[neighbor_idxs] = 0
            unique[i] = 1  # leave the current point as unique
    cluster_centers = sorted_centers[unique]

    # ASSIGN LABELS: a point belongs to the cluster that it is closest to
    nbrs = NearestNeighbors(n_neighbors=1).fit(cluster_centers)
    labels = np.zeros(n_samples, dtype=np.int)
    distances, idxs = nbrs.kneighbors(X)
    if cluster_all:
        labels = idxs.flatten()
    else:
        labels.fill(-1)
        bool_selector = distances.flatten() <= bandwidth
        labels[bool_selector] = idxs.flatten()[bool_selector]
    return cluster_centers, labels

def get_bin_seeds(X, bin_size, min_bin_freq=1):
    """Finds seeds for mean_shift.

    Finds seeds by first binning data onto a grid whose lines are
    spaced bin_size apart, and then choosing those bins with at least
    min_bin_freq points.

    Parameters
    ----------

    X : array-like, shape=[n_samples, n_features]
        Input points, the same points that will be used in mean_shift.

    bin_size : float
        Controls the coarseness of the binning. Smaller values lead
        to more seeding (which is computationally more expensive). If you're
        not sure how to set this, set it to the value of the bandwidth used
        in clustering.mean_shift.

    min_bin_freq : integer, optional
        Only bins with at least min_bin_freq will be selected as seeds.
        Raising this value decreases the number of seeds found, which
        makes mean_shift computationally cheaper.

    Returns
    -------
    bin_seeds : array-like, shape=[n_samples, n_features]
        Points used as initial kernel positions in clustering.mean_shift.
    """

    # Bin points
    bin_sizes = defaultdict(int)
    for point in X:
        binned_point = np.round(point / bin_size)
        bin_sizes[tuple(binned_point)] += 1

    # Select only those bins as seeds which have enough members
    bin_seeds = np.array([point for point, freq in six.iteritems(bin_sizes) if
                          freq >= min_bin_freq], dtype=np.float32)
    if len(bin_seeds) == len(X):
        warnings.warn("Binning data failed with provided bin_size=%f,"
                      " using data points as seeds." % bin_size)
        return X
    bin_seeds = bin_seeds * bin_size
    return bin_seeds

class MeanShift(BaseEstimator, ClusterMixin):
    """Mean shift clustering using a flat kernel.

    Mean shift clustering aims to discover "blobs" in a smooth density of
    samples. It is a centroid-based algorithm, which works by updating
    candidates for centroids to be the mean of the points within a given
    region. These candidates are then filtered in a post-processing stage to
    eliminate near-duplicates to form the final set of centroids.

    Seeding is performed using a binning technique for scalability.

    Read more in the :ref:`User Guide <mean_shift>`.

    Parameters
    ----------
    bandwidth : float, optional
        Bandwidth used in the RBF kernel.

        If not given, the bandwidth is estimated using
        sklearn.cluster.estimate_bandwidth; see the documentation for that
        function for hints on scalability (see also the Notes, below).

    seeds : array, shape=[n_samples, n_features], optional
        Seeds used to initialize kernels. If not set,
        the seeds are calculated by clustering.get_bin_seeds
        with bandwidth as the grid size and default values for
        other parameters.

    bin_seeding : boolean, optional
        If true, initial kernel locations are not locations of all
        points, but rather the location of the discretized version of
        points, where points are binned onto a grid whose coarseness
        corresponds to the bandwidth. Setting this option to True will speed
        up the algorithm because fewer seeds will be initialized.
        default value: False
        Ignored if seeds argument is not None.

    min_bin_freq : int, optional
       To speed up the algorithm, accept only those bins with at least
       min_bin_freq points as seeds. If not defined, set to 1.

    cluster_all : boolean, default True
        If true, then all points are clustered, even those orphans that are
        not within any kernel. Orphans are assigned to the nearest kernel.
        If false, then orphans are given cluster label -1.

    n_jobs : int
        The number of jobs to use for the computation. This works by computing
        each of the n_init runs in parallel.

        If -1 all CPUs are used. If 1 is given, no parallel computing code is
        used at all, which is useful for debugging. For n_jobs below -1,
        (n_cpus + 1 + n_jobs) are used. Thus for n_jobs = -2, all CPUs but one
        are used.

    Attributes
    ----------
    cluster_centers_ : array, [n_clusters, n_features]
        Coordinates of cluster centers.

    labels_ :
        Labels of each point.

    Notes
    -----

    Scalability:

    Because this implementation uses a flat kernel and
    a Ball Tree to look up members of each kernel, the complexity will is
    to O(T*n*log(n)) in lower dimensions, with n the number of samples
    and T the number of points. In higher dimensions the complexity will
    tend towards O(T*n^2).

    Scalability can be boosted by using fewer seeds, for example by using
    a higher value of min_bin_freq in the get_bin_seeds function.

    Note that the estimate_bandwidth function is much less scalable than the
    mean shift algorithm and will be the bottleneck if it is used.

    References
    ----------

    Dorin Comaniciu and Peter Meer, "Mean Shift: A robust approach toward
    feature space analysis". IEEE Transactions on Pattern Analysis and
    Machine Intelligence. 2002. pp. 603-619.

    """
    def __init__(self, bandwidth=None, seeds=None, bin_seeding=False,
                 min_bin_freq=1, cluster_all=True, n_jobs=1):
        self.bandwidth = bandwidth
        self.seeds = seeds
        self.bin_seeding = bin_seeding
        self.cluster_all = cluster_all
        self.min_bin_freq = min_bin_freq
        self.n_jobs = n_jobs

    def fit(self, X, y=None):
        """Perform clustering.

        Parameters
        -----------
        X : array-like, shape=[n_samples, n_features]
            Samples to cluster.
        """
        X = check_array(X)
        self.cluster_centers_, self.labels_ = \
            mean_shift(X, bandwidth=self.bandwidth, seeds=self.seeds,
                       min_bin_freq=self.min_bin_freq,
                       bin_seeding=self.bin_seeding,
                       cluster_all=self.cluster_all, n_jobs=self.n_jobs)
        return self

    def predict(self, X):
        """Predict the closest cluster each sample in X belongs to.

        Parameters
        ----------
        X : {array-like, sparse matrix}, shape=[n_samples, n_features]
            New data to predict.

        Returns
        -------
        labels : array, shape [n_samples,]
            Index of the cluster each sample belongs to.
        """
        check_is_fitted(self, "cluster_centers_")

        return pairwise_distances_argmin(X, self.cluster_centers_)


"""
Benchmark for approximate nearest neighbor search using
locality sensitive hashing forest.

There are two types of benchmarks.

First, accuracy of LSHForest queries are measured for various
hyper-parameters and index sizes.

Second, speed up of LSHForest queries compared to brute force
method in exact nearest neighbors is measures for the
aforementioned settings. In general, speed up is increasing as
the index size grows.
"""

from __future__ import division

import numpy as np
from tempfile import gettempdir
from time import time

from sklearn.neighbors import NearestNeighbors
from sklearn.neighbors.approximate import LSHForest
from sklearn.datasets import make_blobs
from sklearn.externals.joblib import Memory

m = Memory(cachedir=gettempdir())

@m.cache()
def make_data(n_samples, n_features, n_queries, random_state=0):
    """Create index and query data."""
    print('Generating random blob-ish data')
    X, _ = make_blobs(n_samples=n_samples + n_queries,
                      n_features=n_features, centers=100,
                      shuffle=True, random_state=random_state)

    # Keep the last samples as held out query vectors: note since we used
    # shuffle=True we have ensured that index and query vectors are
    # samples from the same distribution (a mixture of 100 gaussians in this
    # case)
    return X[:n_samples], X[n_samples:]

def calc_exact_neighbors(X, queries, n_queries, n_neighbors):
    """Measures average times for exact neighbor queries."""
    print ('Building NearestNeighbors for %d samples in %d dimensions' %
           (X.shape[0], X.shape[1]))
    nbrs = NearestNeighbors(algorithm='brute', metric='cosine').fit(X)
    average_time = 0

    t0 = time()
    neighbors = nbrs.kneighbors(queries, n_neighbors=n_neighbors,
                                return_distance=False)
    average_time = (time() - t0) / n_queries
    return neighbors, average_time

def calc_accuracy(X, queries, n_queries, n_neighbors, exact_neighbors,
                  average_time_exact, **lshf_params):
    """Calculates accuracy and the speed up of LSHForest."""
    print('Building LSHForest for %d samples in %d dimensions' %
          (X.shape[0], X.shape[1]))
    lshf = LSHForest(**lshf_params)
    t0 = time()
    lshf.fit(X)
    lshf_build_time = time() - t0
    print('Done in %0.3fs' % lshf_build_time)

    accuracy = 0

    t0 = time()
    approx_neighbors = lshf.kneighbors(queries, n_neighbors=n_neighbors,
                                       return_distance=False)
    average_time_approx = (time() - t0) / n_queries

    for i in range(len(queries)):
        accuracy += np.in1d(approx_neighbors[i], exact_neighbors[i]).mean()

    accuracy /= n_queries
    speed_up = average_time_exact / average_time_approx

    print('Average time for lshf neighbor queries: %0.3fs' %
          average_time_approx)
    print ('Average time for exact neighbor queries: %0.3fs' %
           average_time_exact)
    print ('Average Accuracy : %0.2f' % accuracy)
    print ('Speed up: %0.1fx' % speed_up)

    return speed_up, accuracy

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    # Initialize index sizes
    n_samples = [int(1e3), int(1e4), int(1e5), int(1e6)]
    n_features = int(1e2)
    n_queries = 100
    n_neighbors = 10

    X_index, X_query = make_data(np.max(n_samples), n_features, n_queries,
                                 random_state=0)

    params_list = [{'n_estimators': 3, 'n_candidates': 50},
                   {'n_estimators': 5, 'n_candidates': 70},
                   {'n_estimators': 10, 'n_candidates': 100}]

    accuracies = np.zeros((len(n_samples), len(params_list)), dtype=float)
    speed_ups = np.zeros((len(n_samples), len(params_list)), dtype=float)

    for i, sample_size in enumerate(n_samples):
        print ('==========================================================')
        print ('Sample size: %i' % sample_size)
        print ('------------------------')
        exact_neighbors, average_time_exact = calc_exact_neighbors(
            X_index[:sample_size], X_query, n_queries, n_neighbors)
        for j, params in enumerate(params_list):
            print ('LSHF parameters: n_estimators = %i, n_candidates = %i' %
                   (params['n_estimators'], params['n_candidates']))
            speed_ups[i, j], accuracies[i, j] = calc_accuracy(
                X_index[:sample_size], X_query, n_queries, n_neighbors,
                exact_neighbors, average_time_exact, random_state=0, **params)
            print ('')
        print ('==========================================================')

    # Set labels for LSHForest parameters
    colors = ['c', 'm', 'y']
    p1 = plt.Rectangle((0, 0), 0.1, 0.1, fc=colors[0])
    p2 = plt.Rectangle((0, 0), 0.1, 0.1, fc=colors[1])
    p3 = plt.Rectangle((0, 0), 0.1, 0.1, fc=colors[2])

    labels = ['n_estimators=' + str(params_list[0]['n_estimators']) +
              ', n_candidates=' + str(params_list[0]['n_candidates']),
              'n_estimators=' + str(params_list[1]['n_estimators']) +
              ', n_candidates=' + str(params_list[1]['n_candidates']),
              'n_estimators=' + str(params_list[2]['n_estimators']) +
              ', n_candidates=' + str(params_list[2]['n_candidates'])]

    # Plot precision
    plt.figure()
    plt.legend((p1, p2, p3), (labels[0], labels[1], labels[2]),
               loc='upper left')

    for i in range(len(params_list)):
        plt.scatter(n_samples, accuracies[:, i], c=colors[i])
        plt.plot(n_samples, accuracies[:, i], c=colors[i])
    plt.ylim([0, 1.3])
    plt.xlim(np.min(n_samples), np.max(n_samples))
    plt.semilogx()
    plt.ylabel("Precision@10")
    plt.xlabel("Index size")
    plt.grid(which='both')
    plt.title("Precision of first 10 neighbors with index size")

    # Plot speed up
    plt.figure()
    plt.legend((p1, p2, p3), (labels[0], labels[1], labels[2]),
               loc='upper left')

    for i in range(len(params_list)):
        plt.scatter(n_samples, speed_ups[:, i], c=colors[i])
        plt.plot(n_samples, speed_ups[:, i], c=colors[i])
    plt.ylim(0, np.max(speed_ups))
    plt.xlim(np.min(n_samples), np.max(n_samples))
    plt.semilogx()
    plt.ylabel("Speed up")
    plt.xlabel("Index size")
    plt.grid(which='both')
    plt.title("Relationship between Speed up and index size")

    plt.show()


#  Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
"""This is an example of using recurrent neural networks over characters for DBpedia dataset to predict class from description of an entity.

This model is similar to one described in this paper:
   "Character-level Convolutional Networks for Text Classification"
   http://arxiv.org/abs/1509.01626

and is somewhat alternative to the Lua code from here:
   https://github.com/zhangxiangxiao/Crepe
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import sys

import numpy as np
import pandas
from sklearn import metrics
import tensorflow as tf

learn = tf.contrib.learn

FLAGS = None

MAX_DOCUMENT_LENGTH = 100
HIDDEN_SIZE = 20

def char_rnn_model(features, target):
  """Character level recurrent neural network model to predict classes."""
  target = tf.one_hot(target, 15, 1, 0)
  byte_list = tf.one_hot(features, 256, 1, 0)
  byte_list = tf.unstack(byte_list, axis=1)

  cell = tf.contrib.rnn.GRUCell(HIDDEN_SIZE)
  _, encoding = tf.contrib.rnn.static_rnn(cell, byte_list, dtype=tf.float32)

  logits = tf.contrib.layers.fully_connected(encoding, 15, activation_fn=None)
  loss = tf.contrib.losses.softmax_cross_entropy(logits, target)

  train_op = tf.contrib.layers.optimize_loss(
      loss,
      tf.contrib.framework.get_global_step(),
      optimizer='Adam',
      learning_rate=0.01)

  return ({
      'class': tf.argmax(logits, 1),
      'prob': tf.nn.softmax(logits)
  }, loss, train_op)

def main(unused_argv):
  # Prepare training and testing data
  dbpedia = learn.datasets.load_dataset(
      'dbpedia', test_with_fake_data=FLAGS.test_with_fake_data)
  x_train = pandas.DataFrame(dbpedia.train.data)[1]
  y_train = pandas.Series(dbpedia.train.target)
  x_test = pandas.DataFrame(dbpedia.test.data)[1]
  y_test = pandas.Series(dbpedia.test.target)

  # Process vocabulary
  char_processor = learn.preprocessing.ByteProcessor(MAX_DOCUMENT_LENGTH)
  x_train = np.array(list(char_processor.fit_transform(x_train)))
  x_test = np.array(list(char_processor.transform(x_test)))

  # Build model
  classifier = learn.Estimator(model_fn=char_rnn_model)

  # Train and predict
  classifier.fit(x_train, y_train, steps=100)
  y_predicted = [
      p['class'] for p in classifier.predict(
          x_test, as_iterable=True)
  ]
  score = metrics.accuracy_score(y_test, y_predicted)
  print('Accuracy: {0:f}'.format(score))

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--test_with_fake_data',
      default=False,
      help='Test the example code with fake data.',
      action='store_true')
  FLAGS, unparsed = parser.parse_known_args()
  tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)