# -*- coding: utf-8 -*-
r"""
    sphinx.ext.inheritance_diagram
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Defines a docutils directive for inserting inheritance diagrams.

    Provide the directive with one or more classes or modules (separated
    by whitespace).  For modules, all of the classes in that module will
    be used.

    Example::

       Given the following classes:

       class A: pass
       class B(A): pass
       class C(A): pass
       class D(B, C): pass
       class E(B): pass

       .. inheritance-diagram: D E

       Produces a graph like the following:

                   A
                  / \
                 B   C
                / \ /
               E   D

    The graph is inserted as a PNG+image map into HTML and a PDF in
    LaTeX.

    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
    :copyright: Copyright 2010-2011 by the PySide team.
    :license: BSD, see LICENSE for details.
"""

import os
import re
import sys
import inspect
try:
    from hashlib import md5
except ImportError:
    from md5 import md5

from docutils import nodes
from docutils.parsers.rst import directives

from sphinx.ext.graphviz import render_dot_html, render_dot_latex
from sphinx.util.compat import Directive

class_sig_re = re.compile(r'''^([\w.]*\.)?    # module names
                          (\w+)  \s* $        # class/final module name
                          ''', re.VERBOSE)

class InheritanceException(Exception):
    pass

class InheritanceGraph(object):
    """
    Given a list of classes, determines the set of classes that they inherit
    from all the way to the root "object", and then is able to generate a
    graphviz dot graph from them.
    """
    def __init__(self, class_names, currmodule, show_builtins=False, parts=0):
        """
        *class_names* is a list of child classes to show bases from.

        If *show_builtins* is True, then Python builtins will be shown
        in the graph.
        """
        self.class_names = class_names
        classes = self._import_classes(class_names, currmodule)
        self.class_info = self._class_info(classes, show_builtins, parts)
        if not self.class_info:
            raise InheritanceException('No classes found for '
                                       'inheritance diagram')

    def _import_class_or_module(self, name, currmodule):
        """
        Import a class using its fully-qualified *name*.
        """
        try:
            path, base = class_sig_re.match(name).groups()
        except (AttributeError, ValueError):
            raise InheritanceException('Invalid class or module %r specified '
                                       'for inheritance diagram' % name)

        fullname = (path or '') + base
        path = (path and path.rstrip('.') or '')

        # two possibilities: either it is a module, then import it
        try:
            __import__(fullname)
            todoc = sys.modules[fullname]
        except ImportError:
            # else it is a class, then import the module
            if not path:
                if currmodule:
                    # try the current module
                    path = currmodule
                else:
                    raise InheritanceException(
                        'Could not import class %r specified for '
                        'inheritance diagram' % base)
            try:
                __import__(path)
                todoc = getattr(sys.modules[path], base)
            except (ImportError, AttributeError):
                raise InheritanceException(
                    'Could not import class or module %r specified for '
                    'inheritance diagram' % (path + '.' + base))

        # If a class, just return it
        if inspect.isclass(todoc):
            return [todoc]
        elif inspect.ismodule(todoc):
            classes = []
            for cls in todoc.__dict__.values():
                if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
                    classes.append(cls)
            return classes
        raise InheritanceException('%r specified for inheritance diagram is '
                                   'not a class or module' % name)

    def _import_classes(self, class_names, currmodule):
        """Import a list of classes."""
        classes = []
        for name in class_names:
            classes.extend(self._import_class_or_module(name, currmodule))
        return classes

    def _class_info(self, classes, show_builtins, parts):
        """Return name and bases for all classes that are ancestors of
        *classes*.

        *parts* gives the number of dotted name parts that is removed from the
        displayed node names.
        """
        all_classes = {}
        builtins = __builtins__.values()

        def recurse(cls):
            if not show_builtins and cls in builtins:
                return

            nodename = self.class_name(cls, parts)
            fullname = self.class_name(cls, 0)

            baselist = []
            all_classes[cls] = (nodename, fullname, baselist)
            for base in cls.__bases__:
                if not show_builtins and base in builtins:
                    continue
                if  base.__name__ == "Object" and base.__module__ == "Shiboken":
                    continue
                baselist.append(self.class_name(base, parts))
                if base not in all_classes:
                    recurse(base)

        for cls in classes:
            recurse(cls)

        return all_classes.values()

    def class_name(self, cls, parts=0):
        """Given a class object, return a fully-qualified name.

        This works for things I've tested in matplotlib so far, but may not be
        completely general.
        """
        module = cls.__module__
        if module == '__builtin__':
            fullname = cls.__name__
        else:
            fullname = '%s.%s' % (module, cls.__name__)
        if parts == 0:
            return fullname
        name_parts = fullname.split('.')
        return '.'.join(name_parts[-parts:])

    def get_all_class_names(self):
        """
        Get all of the class names involved in the graph.
        """
        return [fullname for (_, fullname, _) in self.class_info]

    # These are the default attrs for graphviz
    default_graph_attrs = {
        'rankdir': 'LR',
        'size': '"8.0, 12.0"',
    }
    default_node_attrs = {
        'shape': 'box',
        'fontsize': 10,
        'height': 0.25,
        'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
                    'Arial, Helvetica, sans',
        'style': '"setlinewidth(0.5)"',
    }
    default_edge_attrs = {
        'arrowsize': 0.5,
        'style': '"setlinewidth(0.5)"',
    }

    def _format_node_attrs(self, attrs):
        return ','.join(['%s=%s' % x for x in attrs.items()])

    def _format_graph_attrs(self, attrs):
        return ''.join(['%s=%s;\n' % x for x in attrs.items()])

    def generate_dot(self, name, urls={}, env=None,
                     graph_attrs={}, node_attrs={}, edge_attrs={}):
        """
        Generate a graphviz dot graph from the classes that
        were passed in to __init__.

        *name* is the name of the graph.

        *urls* is a dictionary mapping class names to HTTP URLs.

        *graph_attrs*, *node_attrs*, *edge_attrs* are dictionaries containing
        key/value pairs to pass on as graphviz properties.
        """
        g_attrs = self.default_graph_attrs.copy()
        n_attrs = self.default_node_attrs.copy()
        e_attrs = self.default_edge_attrs.copy()
        g_attrs.update(graph_attrs)
        n_attrs.update(node_attrs)
        e_attrs.update(edge_attrs)
        if env:
            g_attrs.update(env.config.inheritance_graph_attrs)
            n_attrs.update(env.config.inheritance_node_attrs)
            e_attrs.update(env.config.inheritance_edge_attrs)

        res = []
        res.append('digraph %s {\n' % name)
        res.append(self._format_graph_attrs(g_attrs))

        for name, fullname, bases in self.class_info:
            # Write the node
            this_node_attrs = n_attrs.copy()
            url = urls.get(fullname)
            if url is not None:
                this_node_attrs['URL'] = '"%s"' % url
            res.append('  "%s" [%s];\n' %
                       (name, self._format_node_attrs(this_node_attrs)))

            # Write the edges
            for base_name in bases:
                res.append('  "%s" -> "%s" [%s];\n' %
                           (base_name, name,
                            self._format_node_attrs(e_attrs)))
        res.append('}\n')
        return ''.join(res)

class inheritance_diagram(nodes.General, nodes.Element):
    """
    A docutils node to use as a placeholder for the inheritance diagram.
    """
    pass

class InheritanceDiagram(Directive):
    """
    Run when the inheritance_diagram directive is first encountered.
    """
    has_content = False
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    option_spec = {
        'parts': directives.nonnegative_int,
    }

    def run(self):
        node = inheritance_diagram()
        node.document = self.state.document
        env = self.state.document.settings.env
        class_names = self.arguments[0].split()
        class_role = env.get_domain('py').role('class')
        # Store the original content for use as a hash
        node['parts'] = self.options.get('parts', 0)
        node['content'] = ', '.join(class_names)

        # Create a graph starting with the list of classes
        try:
            graph = InheritanceGraph(
                class_names, env.temp_data.get('py:module'),
                parts=node['parts'])
        except InheritanceException, err:
            return [node.document.reporter.warning(err.args[0],
                                                   line=self.lineno)]

        # Create xref nodes for each target of the graph's image map and
        # add them to the doc tree so that Sphinx can resolve the
        # references to real URLs later.  These nodes will eventually be
        # removed from the doctree after we're done with them.
        for name in graph.get_all_class_names():
            refnodes, x = class_role(
                'class', ':class:`%s`' % name, name, 0, self.state)
            node.extend(refnodes)
        # Store the graph object so we can use it to generate the
        # dot file later
        node['graph'] = graph
        return [node]

def get_graph_hash(node):
    return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]

def html_visit_inheritance_diagram(self, node):
    """
    Output the graph for HTML.  This will insert a PNG with clickable
    image map.
    """
    graph = node['graph']

    graph_hash = get_graph_hash(node)
    name = 'inheritance%s' % graph_hash

    # Create a mapping from fully-qualified class names to URLs.
    urls = {}
    for child in node:
        if child.get('refuri') is not None:
            urls[child['reftitle']] = child.get('refuri')
        elif child.get('refid') is not None:
            urls[child['reftitle']] = '#' + child.get('refid')

    dotcode = graph.generate_dot(name, urls, env=self.builder.env)
    render_dot_html(self, node, dotcode, [], 'inheritance', 'inheritance',
                    alt='Inheritance diagram of ' + node['content'])
    raise nodes.SkipNode

def latex_visit_inheritance_diagram(self, node):
    """
    Output the graph for LaTeX.  This will insert a PDF.
    """
    graph = node['graph']

    graph_hash = get_graph_hash(node)
    name = 'inheritance%s' % graph_hash

    dotcode = graph.generate_dot(name, env=self.builder.env,
                                 graph_attrs={'size': '"6.0,6.0"'})
    render_dot_latex(self, node, dotcode, [], 'inheritance')
    raise nodes.SkipNode

def skip(self, node):
    raise nodes.SkipNode

def setup(app):
    app.setup_extension('sphinx.ext.graphviz')
    app.add_node(
        inheritance_diagram,
        latex=(latex_visit_inheritance_diagram, None),
        html=(html_visit_inheritance_diagram, None),
        text=(skip, None),
        man=(skip, None))
    app.add_directive('inheritance-diagram', InheritanceDiagram)
    app.add_config_value('inheritance_graph_attrs', {}, False),
    app.add_config_value('inheritance_node_attrs', {}, False),
    app.add_config_value('inheritance_edge_attrs', {}, False),


"""
=================================================
SVM: Separating hyperplane for unbalanced classes
=================================================

Find the optimal separating hyperplane using an SVC for classes that
are unbalanced.

We first find the separating plane with a plain SVC and then plot
(dashed) the separating hyperplane with automatically correction for
unbalanced classes.

.. currentmodule:: sklearn.linear_model

.. note::

    This example will also work by replacing ``SVC(kernel="linear")``
    with ``SGDClassifier(loss="hinge")``. Setting the ``loss`` parameter
    of the :class:`SGDClassifier` equal to ``hinge`` will yield behaviour
    such as that of a SVC with a linear kernel.

    For example try instead of the ``SVC``::

        clf = SGDClassifier(n_iter=100, alpha=0.01)

"""
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
#from sklearn.linear_model import SGDClassifier

# we create 40 separable points
rng = np.random.RandomState(0)
n_samples_1 = 1000
n_samples_2 = 100
X = np.r_[1.5 * rng.randn(n_samples_1, 2),
          0.5 * rng.randn(n_samples_2, 2) + [2, 2]]
y = [0] * (n_samples_1) + [1] * (n_samples_2)

# fit the model and get the separating hyperplane
clf = svm.SVC(kernel='linear', C=1.0)
clf.fit(X, y)

w = clf.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - clf.intercept_[0] / w[1]

# get the separating hyperplane using weighted classes
wclf = svm.SVC(kernel='linear', class_weight={1: 10})
wclf.fit(X, y)

ww = wclf.coef_[0]
wa = -ww[0] / ww[1]
wyy = wa * xx - wclf.intercept_[0] / ww[1]

# plot separating hyperplanes and samples
h0 = plt.plot(xx, yy, 'k-', label='no weights')
h1 = plt.plot(xx, wyy, 'k--', label='with weights')
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
plt.legend()

plt.axis('tight')
plt.show()


# -*- coding: utf-8 -*-

import sys, os, re

# Check Sphinx version
import sphinx
if sphinx.__version__ < "1.1":
    raise RuntimeError("Sphinx 1.1 or newer required")

needs_sphinx = '1.1'

# -----------------------------------------------------------------------------
# General configuration
# -----------------------------------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.

sys.path.insert(0, os.path.abspath('../sphinxext'))
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))

extensions = ['sphinx.ext.autodoc', 'sphinx.ext.mathjax', 'numpydoc',
              'sphinx.ext.intersphinx', 'sphinx.ext.coverage',
              'sphinx.ext.autosummary', 'scipyoptdoc']

# Determine if the matplotlib has a recent enough version of the
# plot_directive.
try:
    from matplotlib.sphinxext import plot_directive
except ImportError:
    use_matplotlib_plot_directive = False
else:
    try:
        use_matplotlib_plot_directive = (plot_directive.__version__ >= 2)
    except AttributeError:
        use_matplotlib_plot_directive = False

if use_matplotlib_plot_directive:
    extensions.append('matplotlib.sphinxext.plot_directive')
else:
    raise RuntimeError("You need a recent enough version of matplotlib")

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General substitutions.
project = 'SciPy'
copyright = '2008-2014, The Scipy community'

# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
import scipy
version = re.sub(r'\.dev-.*$', r'.dev', scipy.__version__)
release = scipy.__version__

print "Scipy (VERSION %s)" % (version,)

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# The reST default role (used for this markup: `text`) to use for all documents.
default_role = "autolink"

# List of directories, relative to source directories, that shouldn't be searched
# for source files.
exclude_dirs = []

# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = False

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# -----------------------------------------------------------------------------
# HTML output
# -----------------------------------------------------------------------------

themedir = os.path.join(os.pardir, 'scipy-sphinx-theme', '_theme')
if os.path.isdir(themedir):
    html_theme = 'scipy'
    html_theme_path = [themedir]

    if 'scipyorg' in tags:
        # Build for the scipy.org website
        html_theme_options = {
            "edit_link": True,
            "sidebar": "right",
            "scipy_org_logo": True,
            "rootlinks": [("http://scipy.org/", "Scipy.org"),
                          ("http://docs.scipy.org/", "Docs")]
        }
    else:
        # Default build
        html_theme_options = {
            "edit_link": False,
            "sidebar": "left",
            "scipy_org_logo": False,
            "rootlinks": []
        }
        html_logo = '_static/scipyshiny_small.png'
        html_sidebars = {'index': 'indexsidebar.html'}
else:
    # Build without scipy.org sphinx theme present
    if 'scipyorg' in tags:
        raise RuntimeError("Get the scipy-sphinx-theme first, "
                           "via git submodule init & update")
    else:
        html_style = 'scipy_fallback.css'
        html_logo = '_static/scipyshiny_small.png'
        html_sidebars = {'index': 'indexsidebar.html'}

html_title = "%s v%s Reference Guide" % (project, version)
html_static_path = ['_static']
html_last_updated_fmt = '%b %d, %Y'

html_additional_pages = {}
html_use_modindex = True
html_copy_source = False
html_file_suffix = '.html'

htmlhelp_basename = 'scipy'

pngmath_use_preview = True
pngmath_dvipng_args = ['-gamma', '1.5', '-D', '96', '-bg', 'Transparent']

# -----------------------------------------------------------------------------
# LaTeX output
# -----------------------------------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
_stdauthor = 'Written by the SciPy community'
latex_documents = [
  ('index', 'scipy-ref.tex', 'SciPy Reference Guide', _stdauthor, 'manual'),
#  ('user/index', 'scipy-user.tex', 'SciPy User Guide',
#   _stdauthor, 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
latex_preamble = r'''
\usepackage{amsmath}

\DeclareUnicodeCharacter{00A0}{\nobreakspace}

% In the parameters etc. sections, align uniformly, and adjust label emphasis
\usepackage{expdlist}
\let\latexdescription=\description
\let\endlatexdescription=\enddescription
\renewenvironment{description}%
{\begin{latexdescription}[\setleftmargin{60pt}\breaklabel\setlabelstyle{\bfseries\itshape}]}%
{\end{latexdescription}}

% Make Examples/etc section headers smaller and more compact
\makeatletter
\titleformat{\paragraph}{\normalsize\normalfont\bfseries\itshape}%
            {\py@NormalColor}{0em}{\py@NormalColor}{\py@NormalColor}
\titlespacing*{\paragraph}{0pt}{1ex}{0pt}
\makeatother

% Save vertical space in parameter lists and elsewhere
\makeatletter
\renewenvironment{quote}%
               {\list{}{\topsep=0pt%
                        \parsep \z@ \@plus\p@}%
                \item\relax}%
               {\endlist}
\makeatother

% Fix footer/header
\renewcommand{\chaptermark}[1]{\markboth{\MakeUppercase{\thechapter.\ #1}}{}}
\renewcommand{\sectionmark}[1]{\markright{\MakeUppercase{\thesection.\ #1}}}
'''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
latex_use_modindex = False

# -----------------------------------------------------------------------------
# Intersphinx configuration
# -----------------------------------------------------------------------------
intersphinx_mapping = {
        'http://docs.python.org/dev': None,
        'http://docs.scipy.org/doc/numpy': None,
}

# -----------------------------------------------------------------------------
# Numpy extensions
# -----------------------------------------------------------------------------

# If we want to do a phantom import from an XML file for all autodocs
phantom_import_file = 'dump.xml'

# Generate plots for example sections
numpydoc_use_plots = True

# -----------------------------------------------------------------------------
# Autosummary
# -----------------------------------------------------------------------------

if sphinx.__version__ >= "0.7":
    import glob
    autosummary_generate = glob.glob("*.rst")

# -----------------------------------------------------------------------------
# Coverage checker
# -----------------------------------------------------------------------------
coverage_ignore_modules = r"""
    """.split()
coverage_ignore_functions = r"""
    test($|_) (some|all)true bitwise_not cumproduct pkgload
    generic\.
    """.split()
coverage_ignore_classes = r"""
    """.split()

coverage_c_path = []
coverage_c_regexes = {}
coverage_ignore_c_items = {}

#------------------------------------------------------------------------------
# Plot
#------------------------------------------------------------------------------
plot_pre_code = """
import numpy as np
np.random.seed(123)
"""
plot_include_source = True
plot_formats = [('png', 96), 'pdf']
plot_html_show_formats = False

import math
phi = (math.sqrt(5) + 1)/2

font_size = 13*72/96.0  # 13 px

plot_rcparams = {
    'font.size': font_size,
    'axes.titlesize': font_size,
    'axes.labelsize': font_size,
    'xtick.labelsize': font_size,
    'ytick.labelsize': font_size,
    'legend.fontsize': font_size,
    'figure.figsize': (3*phi, 3),
    'figure.subplot.bottom': 0.2,
    'figure.subplot.left': 0.2,
    'figure.subplot.right': 0.9,
    'figure.subplot.top': 0.85,
    'figure.subplot.wspace': 0.4,
    'text.usetex': False,
}

if not use_matplotlib_plot_directive:
    import matplotlib
    matplotlib.rcParams.update(plot_rcparams)

# -----------------------------------------------------------------------------
# Source code links
# -----------------------------------------------------------------------------

import inspect
from os.path import relpath, dirname

for name in ['sphinx.ext.linkcode', 'linkcode', 'numpydoc.linkcode']:
    try:
        __import__(name)
        extensions.append(name)
        break
    except ImportError:
        pass
else:
    print "NOTE: linkcode extension not found -- no links to source generated"

def linkcode_resolve(domain, info):
    """
    Determine the URL corresponding to Python object
    """
    if domain != 'py':
        return None

    modname = info['module']
    fullname = info['fullname']

    submod = sys.modules.get(modname)
    if submod is None:
        return None

    obj = submod
    for part in fullname.split('.'):
        try:
            obj = getattr(obj, part)
        except:
            return None

    try:
        fn = inspect.getsourcefile(obj)
    except:
        fn = None
    if not fn:
        try:
            fn = inspect.getsourcefile(sys.modules[obj.__module__])
        except:
            fn = None
    if not fn:
        return None

    try:
        source, lineno = inspect.getsourcelines(obj)
    except:
        lineno = None

    if lineno:
        linespec = "#L%d-L%d" % (lineno, lineno + len(source) - 1)
    else:
        linespec = ""

    fn = relpath(fn, start=dirname(scipy.__file__))

    if 'dev' in scipy.__version__:
        return "http://github.com/scipy/scipy/blob/master/scipy/%s%s" % (
           fn, linespec)
    else:
        return "http://github.com/scipy/scipy/blob/v%s/scipy/%s%s" % (
           scipy.__version__, fn, linespec)


"""
The :mod:`sklearn.feature_extraction.image` submodule gathers utilities to
extract features from images.
"""

# Authors: Emmanuelle Gouillart <emmanuelle.gouillart@normalesup.org>
#          Gael Varoquaux <gael.varoquaux@normalesup.org>
#          Olivier Grisel
#          Vlad Niculae
# License: BSD 3 clause

from itertools import product
import numbers
import numpy as np
from scipy import sparse
from numpy.lib.stride_tricks import as_strided

from ..utils import check_array, check_random_state
from ..utils.fixes import astype
from ..base import BaseEstimator

__all__ = ['PatchExtractor',
           'extract_patches_2d',
           'grid_to_graph',
           'img_to_graph',
           'reconstruct_from_patches_2d']

###############################################################################
# From an image to a graph

def _make_edges_3d(n_x, n_y, n_z=1):
    """Returns a list of edges for a 3D image.

    Parameters
    ===========
    n_x: integer
        The size of the grid in the x direction.
    n_y: integer
        The size of the grid in the y direction.
    n_z: integer, optional
        The size of the grid in the z direction, defaults to 1
    """
    vertices = np.arange(n_x * n_y * n_z).reshape((n_x, n_y, n_z))
    edges_deep = np.vstack((vertices[:, :, :-1].ravel(),
                            vertices[:, :, 1:].ravel()))
    edges_right = np.vstack((vertices[:, :-1].ravel(),
                             vertices[:, 1:].ravel()))
    edges_down = np.vstack((vertices[:-1].ravel(), vertices[1:].ravel()))
    edges = np.hstack((edges_deep, edges_right, edges_down))
    return edges

def _compute_gradient_3d(edges, img):
    n_x, n_y, n_z = img.shape
    gradient = np.abs(img[edges[0] // (n_y * n_z),
                      (edges[0] % (n_y * n_z)) // n_z,
                      (edges[0] % (n_y * n_z)) % n_z] -
                      img[edges[1] // (n_y * n_z),
                      (edges[1] % (n_y * n_z)) // n_z,
                      (edges[1] % (n_y * n_z)) % n_z])
    return gradient

# XXX: Why mask the image after computing the weights?

def _mask_edges_weights(mask, edges, weights=None):
    """Apply a mask to edges (weighted or not)"""
    inds = np.arange(mask.size)
    inds = inds[mask.ravel()]
    ind_mask = np.logical_and(np.in1d(edges[0], inds),
                              np.in1d(edges[1], inds))
    edges = edges[:, ind_mask]
    if weights is not None:
        weights = weights[ind_mask]
    if len(edges.ravel()):
        maxval = edges.max()
    else:
        maxval = 0
    order = np.searchsorted(np.unique(edges.ravel()), np.arange(maxval + 1))
    edges = order[edges]
    if weights is None:
        return edges
    else:
        return edges, weights

def _to_graph(n_x, n_y, n_z, mask=None, img=None,
              return_as=sparse.coo_matrix, dtype=None):
    """Auxiliary function for img_to_graph and grid_to_graph
    """
    edges = _make_edges_3d(n_x, n_y, n_z)

    if dtype is None:
        if img is None:
            dtype = np.int
        else:
            dtype = img.dtype

    if img is not None:
        img = np.atleast_3d(img)
        weights = _compute_gradient_3d(edges, img)
        if mask is not None:
            edges, weights = _mask_edges_weights(mask, edges, weights)
            diag = img.squeeze()[mask]
        else:
            diag = img.ravel()
        n_voxels = diag.size
    else:
        if mask is not None:
            mask = astype(mask, dtype=np.bool, copy=False)
            mask = np.asarray(mask, dtype=np.bool)
            edges = _mask_edges_weights(mask, edges)
            n_voxels = np.sum(mask)
        else:
            n_voxels = n_x * n_y * n_z
        weights = np.ones(edges.shape[1], dtype=dtype)
        diag = np.ones(n_voxels, dtype=dtype)

    diag_idx = np.arange(n_voxels)
    i_idx = np.hstack((edges[0], edges[1]))
    j_idx = np.hstack((edges[1], edges[0]))
    graph = sparse.coo_matrix((np.hstack((weights, weights, diag)),
                              (np.hstack((i_idx, diag_idx)),
                               np.hstack((j_idx, diag_idx)))),
                              (n_voxels, n_voxels),
                              dtype=dtype)
    if return_as is np.ndarray:
        return graph.toarray()
    return return_as(graph)

def img_to_graph(img, mask=None, return_as=sparse.coo_matrix, dtype=None):
    """Graph of the pixel-to-pixel gradient connections

    Edges are weighted with the gradient values.

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

    Parameters
    ----------
    img : ndarray, 2D or 3D
        2D or 3D image
    mask : ndarray of booleans, optional
        An optional mask of the image, to consider only part of the
        pixels.
    return_as : np.ndarray or a sparse matrix class, optional
        The class to use to build the returned adjacency matrix.
    dtype : None or dtype, optional
        The data of the returned sparse matrix. By default it is the
        dtype of img

    Notes
    -----
    For sklearn versions 0.14.1 and prior, return_as=np.ndarray was handled
    by returning a dense np.matrix instance.  Going forward, np.ndarray
    returns an np.ndarray, as expected.

    For compatibility, user code relying on this method should wrap its
    calls in ``np.asarray`` to avoid type issues.
    """
    img = np.atleast_3d(img)
    n_x, n_y, n_z = img.shape
    return _to_graph(n_x, n_y, n_z, mask, img, return_as, dtype)

def grid_to_graph(n_x, n_y, n_z=1, mask=None, return_as=sparse.coo_matrix,
                  dtype=np.int):
    """Graph of the pixel-to-pixel connections

    Edges exist if 2 voxels are connected.

    Parameters
    ----------
    n_x : int
        Dimension in x axis
    n_y : int
        Dimension in y axis
    n_z : int, optional, default 1
        Dimension in z axis
    mask : ndarray of booleans, optional
        An optional mask of the image, to consider only part of the
        pixels.
    return_as : np.ndarray or a sparse matrix class, optional
        The class to use to build the returned adjacency matrix.
    dtype : dtype, optional, default int
        The data of the returned sparse matrix. By default it is int

    Notes
    -----
    For sklearn versions 0.14.1 and prior, return_as=np.ndarray was handled
    by returning a dense np.matrix instance.  Going forward, np.ndarray
    returns an np.ndarray, as expected.

    For compatibility, user code relying on this method should wrap its
    calls in ``np.asarray`` to avoid type issues.
    """
    return _to_graph(n_x, n_y, n_z, mask=mask, return_as=return_as,
                     dtype=dtype)

###############################################################################
# From an image to a set of small image patches

def _compute_n_patches(i_h, i_w, p_h, p_w, max_patches=None):
    """Compute the number of patches that will be extracted in an image.

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

    Parameters
    ----------
    i_h : int
        The image height
    i_w : int
        The image with
    p_h : int
        The height of a patch
    p_w : int
        The width of a patch
    max_patches : integer or float, optional default is None
        The maximum number of patches to extract. If max_patches is a float
        between 0 and 1, it is taken to be a proportion of the total number
        of patches.
    """
    n_h = i_h - p_h + 1
    n_w = i_w - p_w + 1
    all_patches = n_h * n_w

    if max_patches:
        if (isinstance(max_patches, (numbers.Integral))
                and max_patches < all_patches):
            return max_patches
        elif (isinstance(max_patches, (numbers.Real))
                and 0 < max_patches < 1):
            return int(max_patches * all_patches)
        else:
            raise ValueError("Invalid value for max_patches: %r" % max_patches)
    else:
        return all_patches

def extract_patches(arr, patch_shape=8, extraction_step=1):
    """Extracts patches of any n-dimensional array in place using strides.

    Given an n-dimensional array it will return a 2n-dimensional array with
    the first n dimensions indexing patch position and the last n indexing
    the patch content. This operation is immediate (O(1)). A reshape
    performed on the first n dimensions will cause numpy to copy data, leading
    to a list of extracted patches.

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

    Parameters
    ----------
    arr : ndarray
        n-dimensional array of which patches are to be extracted

    patch_shape : integer or tuple of length arr.ndim
        Indicates the shape of the patches to be extracted. If an
        integer is given, the shape will be a hypercube of
        sidelength given by its value.

    extraction_step : integer or tuple of length arr.ndim
        Indicates step size at which extraction shall be performed.
        If integer is given, then the step is uniform in all dimensions.

    Returns
    -------
    patches : strided ndarray
        2n-dimensional array indexing patches on first n dimensions and
        containing patches on the last n dimensions. These dimensions
        are fake, but this way no data is copied. A simple reshape invokes
        a copying operation to obtain a list of patches:
        result.reshape([-1] + list(patch_shape))
    """

    arr_ndim = arr.ndim

    if isinstance(patch_shape, numbers.Number):
        patch_shape = tuple([patch_shape] * arr_ndim)
    if isinstance(extraction_step, numbers.Number):
        extraction_step = tuple([extraction_step] * arr_ndim)

    patch_strides = arr.strides

    slices = [slice(None, None, st) for st in extraction_step]
    indexing_strides = arr[slices].strides

    patch_indices_shape = ((np.array(arr.shape) - np.array(patch_shape)) //
                           np.array(extraction_step)) + 1

    shape = tuple(list(patch_indices_shape) + list(patch_shape))
    strides = tuple(list(indexing_strides) + list(patch_strides))

    patches = as_strided(arr, shape=shape, strides=strides)
    return patches

def extract_patches_2d(image, patch_size, max_patches=None, random_state=None):
    """Reshape a 2D image into a collection of patches

    The resulting patches are allocated in a dedicated array.

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

    Parameters
    ----------
    image : array, shape = (image_height, image_width) or
        (image_height, image_width, n_channels)
        The original image data. For color images, the last dimension specifies
        the channel: a RGB image would have `n_channels=3`.

    patch_size : tuple of ints (patch_height, patch_width)
        the dimensions of one patch

    max_patches : integer or float, optional default is None
        The maximum number of patches to extract. If max_patches is a float
        between 0 and 1, it is taken to be a proportion of the total number
        of patches.

    random_state : int or RandomState
        Pseudo number generator state used for random sampling to use if
        `max_patches` is not None.

    Returns
    -------
    patches : array, shape = (n_patches, patch_height, patch_width) or
         (n_patches, patch_height, patch_width, n_channels)
         The collection of patches extracted from the image, where `n_patches`
         is either `max_patches` or the total number of patches that can be
         extracted.

    Examples
    --------

    >>> from sklearn.feature_extraction import image
    >>> one_image = np.arange(16).reshape((4, 4))
    >>> one_image
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11],
           [12, 13, 14, 15]])
    >>> patches = image.extract_patches_2d(one_image, (2, 2))
    >>> print(patches.shape)
    (9, 2, 2)
    >>> patches[0]
    array([[0, 1],
           [4, 5]])
    >>> patches[1]
    array([[1, 2],
           [5, 6]])
    >>> patches[8]
    array([[10, 11],
           [14, 15]])
    """
    i_h, i_w = image.shape[:2]
    p_h, p_w = patch_size

    if p_h > i_h:
        raise ValueError("Height of the patch should be less than the height"
                         " of the image.")

    if p_w > i_w:
        raise ValueError("Width of the patch should be less than the width"
                         " of the image.")

    image = check_array(image, allow_nd=True)
    image = image.reshape((i_h, i_w, -1))
    n_colors = image.shape[-1]

    extracted_patches = extract_patches(image,
                                        patch_shape=(p_h, p_w, n_colors),
                                        extraction_step=1)

    n_patches = _compute_n_patches(i_h, i_w, p_h, p_w, max_patches)
    if max_patches:
        rng = check_random_state(random_state)
        i_s = rng.randint(i_h - p_h + 1, size=n_patches)
        j_s = rng.randint(i_w - p_w + 1, size=n_patches)
        patches = extracted_patches[i_s, j_s, 0]
    else:
        patches = extracted_patches

    patches = patches.reshape(-1, p_h, p_w, n_colors)
    # remove the color dimension if useless
    if patches.shape[-1] == 1:
        return patches.reshape((n_patches, p_h, p_w))
    else:
        return patches

def reconstruct_from_patches_2d(patches, image_size):
    """Reconstruct the image from all of its patches.

    Patches are assumed to overlap and the image is constructed by filling in
    the patches from left to right, top to bottom, averaging the overlapping
    regions.

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

    Parameters
    ----------
    patches : array, shape = (n_patches, patch_height, patch_width) or
        (n_patches, patch_height, patch_width, n_channels)
        The complete set of patches. If the patches contain colour information,
        channels are indexed along the last dimension: RGB patches would
        have `n_channels=3`.

    image_size : tuple of ints (image_height, image_width) or
        (image_height, image_width, n_channels)
        the size of the image that will be reconstructed

    Returns
    -------
    image : array, shape = image_size
        the reconstructed image

    """
    i_h, i_w = image_size[:2]
    p_h, p_w = patches.shape[1:3]
    img = np.zeros(image_size)
    # compute the dimensions of the patches array
    n_h = i_h - p_h + 1
    n_w = i_w - p_w + 1
    for p, (i, j) in zip(patches, product(range(n_h), range(n_w))):
        img[i:i + p_h, j:j + p_w] += p

    for i in range(i_h):
        for j in range(i_w):
            # divide by the amount of overlap
            # XXX: is this the most efficient way? memory-wise yes, cpu wise?
            img[i, j] /= float(min(i + 1, p_h, i_h - i) *
                               min(j + 1, p_w, i_w - j))
    return img

class PatchExtractor(BaseEstimator):
    """Extracts patches from a collection of images

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

    Parameters
    ----------
    patch_size : tuple of ints (patch_height, patch_width)
        the dimensions of one patch

    max_patches : integer or float, optional default is None
        The maximum number of patches per image to extract. If max_patches is a
        float in (0, 1), it is taken to mean a proportion of the total number
        of patches.

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

    """
    def __init__(self, patch_size=None, max_patches=None, random_state=None):
        self.patch_size = patch_size
        self.max_patches = max_patches
        self.random_state = random_state

    def fit(self, X, y=None):
        """Do nothing and return the estimator unchanged

        This method is just there to implement the usual API and hence
        work in pipelines.
        """
        return self

    def transform(self, X):
        """Transforms the image samples in X into a matrix of patch data.

        Parameters
        ----------
        X : array, shape = (n_samples, image_height, image_width) or
            (n_samples, image_height, image_width, n_channels)
            Array of images from which to extract patches. For color images,
            the last dimension specifies the channel: a RGB image would have
            `n_channels=3`.

        Returns
        -------
        patches: array, shape = (n_patches, patch_height, patch_width) or
             (n_patches, patch_height, patch_width, n_channels)
             The collection of patches extracted from the images, where
             `n_patches` is either `n_samples * max_patches` or the total
             number of patches that can be extracted.

        """
        self.random_state = check_random_state(self.random_state)
        n_images, i_h, i_w = X.shape[:3]
        X = np.reshape(X, (n_images, i_h, i_w, -1))
        n_channels = X.shape[-1]
        if self.patch_size is None:
            patch_size = i_h // 10, i_w // 10
        else:
            patch_size = self.patch_size

        # compute the dimensions of the patches array
        p_h, p_w = patch_size
        n_patches = _compute_n_patches(i_h, i_w, p_h, p_w, self.max_patches)
        patches_shape = (n_images * n_patches,) + patch_size
        if n_channels > 1:
            patches_shape += (n_channels,)

        # extract the patches
        patches = np.empty(patches_shape)
        for ii, image in enumerate(X):
            patches[ii * n_patches:(ii + 1) * n_patches] = extract_patches_2d(
                image, patch_size, self.max_patches, self.random_state)
        return patches


#
# Copyright 2014 Quantopian, Inc.
#
# 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.
import datetime
import hashlib
import os

import numpy as np
import pandas as pd
import pytz
import xlrd
import requests

from six.moves import map

def col_letter_to_index(col_letter):
    # Only supports single letter,
    # but answer key doesn't need multi-letter, yet.
    index = 0
    for i, char in enumerate(reversed(col_letter)):
        index += ((ord(char) - 65) + 1) * pow(26, i)
    return index

DIR = os.path.dirname(os.path.realpath(__file__))

ANSWER_KEY_CHECKSUMS_PATH = os.path.join(DIR, 'risk-answer-key-checksums')
ANSWER_KEY_CHECKSUMS = open(ANSWER_KEY_CHECKSUMS_PATH, 'r').read().splitlines()

ANSWER_KEY_FILENAME = 'risk-answer-key.xlsx'

ANSWER_KEY_PATH = os.path.join(DIR, ANSWER_KEY_FILENAME)

ANSWER_KEY_BUCKET_NAME = 'zipline-test_data'

ANSWER_KEY_DL_TEMPLATE = """
https://s3.amazonaws.com/zipline-test-data/risk/{md5}/risk-answer-key.xlsx
""".strip()

LATEST_ANSWER_KEY_URL = ANSWER_KEY_DL_TEMPLATE.format(
    md5=ANSWER_KEY_CHECKSUMS[-1])

def answer_key_signature():
    with open(ANSWER_KEY_PATH, 'rb') as f:
        md5 = hashlib.md5()
        buf = f.read(1024)
        md5.update(buf)
        while buf != b"":
            buf = f.read(1024)
            md5.update(buf)
    return md5.hexdigest()

def ensure_latest_answer_key():
    """
    Get the latest answer key from a publically available location.

    Logic for determining what and when to download is as such:

    - If there is no local spreadsheet file, then get the lastest answer key,
    as defined by the last row in the checksum file.
    - If there is a local spreadsheet file:
    -- If the spreadsheet's checksum is in the checksum file:
    --- If the spreadsheet's checksum does not match the latest, then grab the
    the latest checksum and replace the local checksum file.
    --- If the spreadsheet's checksum matches the latest, then skip download,
    and use the local spreadsheet as a cached copy.
    -- If the spreadsheet's checksum is not in the checksum file, then leave
    the local file alone, assuming that the local xls's md5 is not in the list
    due to local modifications during development.

    It is possible that md5's could collide, if that is ever case, we should
    then find an alternative naming scheme.

    The spreadsheet answer sheet is not kept in SCM, as every edit would
    increase the repo size by the file size, since it is treated as a binary.
    """

    answer_key_dl_checksum = None

    local_answer_key_exists = os.path.exists(ANSWER_KEY_PATH)
    if local_answer_key_exists:
        local_hash = answer_key_signature()

        if local_hash in ANSWER_KEY_CHECKSUMS:
            # Assume previously downloaded version.
            # Check for latest.
            if local_hash != ANSWER_KEY_CHECKSUMS[-1]:
                # More recent checksum, download
                answer_key_dl_checksum = ANSWER_KEY_CHECKSUMS[-1]
            else:
                # Assume local copy that is being developed on
                answer_key_dl_checksum = None
    else:
        answer_key_dl_checksum = ANSWER_KEY_CHECKSUMS[-1]

    if answer_key_dl_checksum:
        res = requests.get(
            ANSWER_KEY_DL_TEMPLATE.format(md5=answer_key_dl_checksum))
        with open(ANSWER_KEY_PATH, 'wb') as f:
            f.write(res.content)

# Get latest answer key on load.
ensure_latest_answer_key()

class DataIndex(object):
    """
    Coordinates for the spreadsheet, using the values as seen in the notebook.
    The python-excel libraries use 0 index, while the spreadsheet in a GUI
    uses a 1 index.
    """
    def __init__(self, sheet_name, col, row_start, row_end,
                 value_type='float'):
        self.sheet_name = sheet_name
        self.col = col
        self.row_start = row_start
        self.row_end = row_end
        self.value_type = value_type

    @property
    def col_index(self):
        return col_letter_to_index(self.col) - 1

    @property
    def row_start_index(self):
        return self.row_start - 1

    @property
    def row_end_index(self):
        return self.row_end - 1

    def __str__(self):
        return "'{sheet_name}'!{col}{row_start}:{col}{row_end}".format(
            sheet_name=self.sheet_name,
            col=self.col,
            row_start=self.row_start,
            row_end=self.row_end
        )

class AnswerKey(object):

    INDEXES = {
        'RETURNS': DataIndex('Sim Period', 'D', 4, 255),

        'BENCHMARK': {
            'Dates': DataIndex('s_p', 'A', 4, 254, value_type='date'),
            'Returns': DataIndex('s_p', 'H', 4, 254)
        },

        # Below matches the inconsistent capitalization in spreadsheet
        'BENCHMARK_PERIOD_RETURNS': {
            'Monthly': DataIndex('s_p', 'R', 8, 19),
            '3-Month': DataIndex('s_p', 'S', 10, 19),
            '6-month': DataIndex('s_p', 'T', 13, 19),
            'year': DataIndex('s_p', 'U', 19, 19),
        },

        'BENCHMARK_PERIOD_VOLATILITY': {
            'Monthly': DataIndex('s_p', 'V', 8, 19),
            '3-Month': DataIndex('s_p', 'W', 10, 19),
            '6-month': DataIndex('s_p', 'X', 13, 19),
            'year': DataIndex('s_p', 'Y', 19, 19),
        },

        'ALGORITHM_PERIOD_RETURNS': {
            'Monthly': DataIndex('Sim Period', 'Z', 23, 34),
            '3-Month': DataIndex('Sim Period', 'AA', 25, 34),
            '6-month': DataIndex('Sim Period', 'AB', 28, 34),
            'year': DataIndex('Sim Period', 'AC', 34, 34),
        },

        'ALGORITHM_PERIOD_VOLATILITY': {
            'Monthly': DataIndex('Sim Period', 'AH', 23, 34),
            '3-Month': DataIndex('Sim Period', 'AI', 25, 34),
            '6-month': DataIndex('Sim Period', 'AJ', 28, 34),
            'year': DataIndex('Sim Period', 'AK', 34, 34),
        },

        'ALGORITHM_PERIOD_SHARPE': {
            'Monthly': DataIndex('Sim Period', 'AL', 23, 34),
            '3-Month': DataIndex('Sim Period', 'AM', 25, 34),
            '6-month': DataIndex('Sim Period', 'AN', 28, 34),
            'year': DataIndex('Sim Period', 'AO', 34, 34),
        },

        'ALGORITHM_PERIOD_BETA': {
            'Monthly': DataIndex('Sim Period', 'AP', 23, 34),
            '3-Month': DataIndex('Sim Period', 'AQ', 25, 34),
            '6-month': DataIndex('Sim Period', 'AR', 28, 34),
            'year': DataIndex('Sim Period', 'AS', 34, 34),
        },

        'ALGORITHM_PERIOD_ALPHA': {
            'Monthly': DataIndex('Sim Period', 'AT', 23, 34),
            '3-Month': DataIndex('Sim Period', 'AU', 25, 34),
            '6-month': DataIndex('Sim Period', 'AV', 28, 34),
            'year': DataIndex('Sim Period', 'AW', 34, 34),
        },

        'ALGORITHM_PERIOD_BENCHMARK_VARIANCE': {
            'Monthly': DataIndex('Sim Period', 'BJ', 23, 34),
            '3-Month': DataIndex('Sim Period', 'BK', 25, 34),
            '6-month': DataIndex('Sim Period', 'BL', 28, 34),
            'year': DataIndex('Sim Period', 'BM', 34, 34),
        },

        'ALGORITHM_PERIOD_COVARIANCE': {
            'Monthly': DataIndex('Sim Period', 'BF', 23, 34),
            '3-Month': DataIndex('Sim Period', 'BG', 25, 34),
            '6-month': DataIndex('Sim Period', 'BH', 28, 34),
            'year': DataIndex('Sim Period', 'BI', 34, 34),
        },

        'ALGORITHM_PERIOD_DOWNSIDE_RISK': {
            'Monthly': DataIndex('Sim Period', 'BN', 23, 34),
            '3-Month': DataIndex('Sim Period', 'BO', 25, 34),
            '6-month': DataIndex('Sim Period', 'BP', 28, 34),
            'year': DataIndex('Sim Period', 'BQ', 34, 34),
        },

        'ALGORITHM_PERIOD_SORTINO': {
            'Monthly': DataIndex('Sim Period', 'BR', 23, 34),
            '3-Month': DataIndex('Sim Period', 'BS', 25, 34),
            '6-month': DataIndex('Sim Period', 'BT', 28, 34),
            'year': DataIndex('Sim Period', 'BU', 34, 34),
        },

        'ALGORITHM_RETURN_VALUES': DataIndex(
            'Sim Cumulative', 'D', 4, 254),

        'ALGORITHM_CUMULATIVE_VOLATILITY': DataIndex(
            'Sim Cumulative', 'P', 4, 254),

        'ALGORITHM_CUMULATIVE_SHARPE': DataIndex(
            'Sim Cumulative', 'R', 4, 254),

        'CUMULATIVE_DOWNSIDE_RISK': DataIndex(
            'Sim Cumulative', 'U', 4, 254),

        'CUMULATIVE_SORTINO': DataIndex(
            'Sim Cumulative', 'V', 4, 254),

        'CUMULATIVE_INFORMATION': DataIndex(
            'Sim Cumulative', 'AA', 4, 254),

        'CUMULATIVE_BETA': DataIndex(
            'Sim Cumulative', 'AD', 4, 254),

        'CUMULATIVE_ALPHA': DataIndex(
            'Sim Cumulative', 'AE', 4, 254),

        'CUMULATIVE_MAX_DRAWDOWN': DataIndex(
            'Sim Cumulative', 'AH', 4, 254),

    }

    def __init__(self):
        self.workbook = xlrd.open_workbook(ANSWER_KEY_PATH)

        self.sheets = {}
        self.sheets['Sim Period'] = self.workbook.sheet_by_name('Sim Period')
        self.sheets['Sim Cumulative'] = self.workbook.sheet_by_name(
            'Sim Cumulative')
        self.sheets['s_p'] = self.workbook.sheet_by_name('s_p')

        for name, index in self.INDEXES.items():
            if isinstance(index, dict):
                subvalues = {}
                for subkey, subindex in index.items():
                    subvalues[subkey] = self.get_values(subindex)
                setattr(self, name, subvalues)
            else:
                setattr(self, name, self.get_values(index))

    def parse_date_value(self, value):
        return xlrd.xldate_as_tuple(value, 0)

    def parse_float_value(self, value):
        return value if value != '' else np.nan

    def get_raw_values(self, data_index):
        return self.sheets[data_index.sheet_name].col_values(
            data_index.col_index,
            data_index.row_start_index,
            data_index.row_end_index + 1)

    @property
    def value_type_to_value_func(self):
        return {
            'float': self.parse_float_value,
            'date': self.parse_date_value,
        }

    def get_values(self, data_index):
        value_parser = self.value_type_to_value_func[data_index.value_type]
        return [value for value in
                map(value_parser, self.get_raw_values(data_index))]

ANSWER_KEY = AnswerKey()

BENCHMARK_DATES = ANSWER_KEY.BENCHMARK['Dates']
BENCHMARK_RETURNS = ANSWER_KEY.BENCHMARK['Returns']
DATES = [datetime.datetime(*x, tzinfo=pytz.UTC) for x in BENCHMARK_DATES]
BENCHMARK = pd.Series(dict(zip(DATES, BENCHMARK_RETURNS)))
ALGORITHM_RETURNS = pd.Series(
    dict(zip(DATES, ANSWER_KEY.ALGORITHM_RETURN_VALUES)))
RETURNS_DATA = pd.DataFrame({'Benchmark Returns': BENCHMARK,
                             'Algorithm Returns': ALGORITHM_RETURNS})
RISK_CUMULATIVE = pd.DataFrame({
    'volatility': pd.Series(dict(zip(
        DATES, ANSWER_KEY.ALGORITHM_CUMULATIVE_VOLATILITY))),
    'sharpe': pd.Series(dict(zip(
        DATES, ANSWER_KEY.ALGORITHM_CUMULATIVE_SHARPE))),
    'downside_risk': pd.Series(dict(zip(
        DATES, ANSWER_KEY.CUMULATIVE_DOWNSIDE_RISK))),
    'sortino': pd.Series(dict(zip(
        DATES, ANSWER_KEY.CUMULATIVE_SORTINO))),
    'information': pd.Series(dict(zip(
        DATES, ANSWER_KEY.CUMULATIVE_INFORMATION))),
    'alpha': pd.Series(dict(zip(
        DATES, ANSWER_KEY.CUMULATIVE_ALPHA))),
    'beta': pd.Series(dict(zip(
        DATES, ANSWER_KEY.CUMULATIVE_BETA))),
    'max_drawdown': pd.Series(dict(zip(
        DATES, ANSWER_KEY.CUMULATIVE_MAX_DRAWDOWN))),
})


import oga_api.ml.cmac as cmac
import numpy as np
import matplotlib.pyplot as plt
import oga_api.physics.cinematic as c

class BasicCMAC(cmac.CMAC):
    def __init__(self, trajectories, pos_angles, time_frame, markers, angles, activations, output, num_iterations):
        self._num_iterations = num_iterations
        confs = []
        conf = None
        data_set = None
        for marker in markers:
            if 'xCheckedForInput' in marker and marker['xCheckedForInput'] and 'qx'in marker:
                data = c.get_vectorial_velocities(trajectories[marker['index'], 0, :], time_frame)
                conf = cmac.SignalConfiguration(data.min(), data.max(), marker['qx'],  marker['description'])
                if conf != None: confs.append(conf)
                if data_set == None: data_set = np.reshape(data, (len(data), 1))
                else: data_set = np.concatenate((data_set,  np.reshape(data, (len(data), 1))), axis=1)
            if 'yCheckedForInput' in marker and marker['yCheckedForInput'] and 'qy'in marker:
                data = c.get_vectorial_velocities(trajectories[marker['index'], 1, :], time_frame)
                conf = cmac.SignalConfiguration(data.min(), data.max(), marker['qy'],  marker['description'])
                if conf != None: confs.append(conf)
                if data_set == None: data_set = np.reshape(data, (len(data), 1))
                else: data_set = np.concatenate((data_set,  np.reshape(data, (len(data), 1))), axis=1)
            if 'zCheckedForInput' in marker and marker['zCheckedForInput'] and 'qz'in marker:
                data = c.get_vectorial_velocities(trajectories[marker['index'], 2, :], time_frame)
                conf = cmac.SignalConfiguration(data.min(), data.max(), marker['qz'],  marker['description'])
                if conf != None: confs.append(conf)
                if data_set == None: data_set = np.reshape(data, (len(data), 1))
                else: data_set = np.concatenate((data_set,  np.reshape(data, (len(data), 1))), axis=1)

        super(BasicCMAC, self).__init__(confs, activations)
        if data_set == None:
            raise ParameterInvalid('No data do process')
        if len(confs) == 0:
            raise ParameterInvalid('No input valid input sginal')

        self._data_set = data_set
        self._get_output_data(output, trajectories, pos_angles, time_frame)
        self._generate_data_for_training_and_test()

    @property
    def data_in(self):
        return self._data_in

    @property
    def data_in_test(self):
        return self._data_in_test

    @property
    def data_set(self):
        return self._data_set

    @property
    def out_data(self):
        return self._out_data

    @property
    def data_out(self):
        return self._data_out

    @property
    def data_out_test(self):
        return self._data_out_test

    def _get_output_data(self, output, trajectories, pos_angles, time_frame):
        if output['type'] == 0: #Marker
            component = 0
            if output['component'] =='x':
                component = 0
            elif output['component'] == 'y':
                component = 1
            else:
                component == 2 # component == z
            self._out_data = trajectories[output['_id'], component, :]
        else: #1 Angle
            #import pdb; pdb.set_trace()
            angle = pos_angles[int(output['_id'])]
            origin = trajectories[int(angle['origin']), 0:3, :]
            component_a = trajectories[int(angle['component_a']), 0:3, :]
            component_b = trajectories[int(angle['component_b']), 0:3, :]
            if output['component'] == 'a': # angle
                self._out_data = c.get_angles(origin.T, component_a.T, component_b.T)
            else: # v - angular velocities
                self._out_data = c.calc_angular_velocities(origin.T, component_a.T, component_b.T, time_frame)
            #import pdb; pdb.set_trace()

    def _generate_data_for_training_and_test(self):
        data_in = None
        data_in_test = None
        data_out = np.array([])
        data_out_test = np.array([])

        for i in np.arange(self._data_set.shape[0]):
            if i % 2 == 0:
                if data_in == None:
                    data_in = np.reshape(self._data_set[i,:], (1, self._data_set.shape[1]))
                else:
                    data_in = np.concatenate((data_in, np.reshape(self._data_set[i,:], (1, self._data_set.shape[1]))))
                data_out = np.append(data_out, np.array([self._out_data[i]]))
            else:
                if data_in_test == None:
                    data_in_test = np.reshape(self._data_set[i,:], (1, self._data_set.shape[1]))
                else:
                    data_in_test = np.concatenate((data_in_test, np.reshape(self._data_set[i,:], (1, self._data_set.shape[1]))))
                data_out_test = np.append(data_out_test, np.array([self._out_data[i]]))
        self._data_in = data_in
        self._data_in_test = data_in_test
        self._data_out = data_out
        self._data_out_test = data_out_test

    def train(self):
        if self._num_iterations < 1:
            raise ParameterInvalid('Number of iterations must be greater than 1')
        t = cmac.Train(self, self._data_in, self._data_out, 1, self._num_iterations)
        t.train()
        self.t = t

    def fire_all(self, inputs):
        result = []
        for data in inputs:
            result.append(self.fire(data))
        return np.array(result)

    def fire_test(self):
        return self.fire_all(self._data_in_test)

"""
    def plot_aproximation(self, time = None):
	real = self._data_test
	aproximations = self.fire_test)
        if time == None:
            t = arange(0, real.shape[0]) * (1./315.)
        else:
            t = time
        plt.figure()
        plt.plot(self.t.E)

        plt.figure()
        plt.hold(True)
        p1 = plt.plot(t.tolist(), real, 'b', linewidth=4)
        p2 = plt.plot(t.tolist(), aproximation, 'r', linewidth=2)
        plt.xlabel('t (sec.)', fontsize=15)
        plt.ylabel('Angular Velocities (rads/sec.)', fontsize=15)
        plt.legend(['Human Knee', 'CMAC Prediction'])
        plt.show()

"""
class ParameterInvalid(BaseException):
    def __init__(self, description):
        self._description = description

    @property
    def description(self):
        return self._description


"""
Module for visualizing skill embeddings

@author Siddharth Reddy <sgr45@cornell.edu>
"""

import logging

import matplotlib
from matplotlib import pyplot as plt
import numpy as np

from . import models

_logger = logging.getLogger(__name__)

def plot_embedding(
    model,
    timestep=-1,
    show_students=True,
    show_assessments=True,
    show_lessons=None,
    show_prereqs=None,
    show_concepts=None,
    show_student_ids=False,
    show_assessment_ids=False,
    show_lesson_ids=False,
    show_concept_ids=False,
    id_padding_x=0.01,
    id_padding_y=0.01,
    alpha=0.5,
    size=20,
    title='',
    show_legend=True,
    force_invariant_axis_limits=True,
    axis_limit_padding=0.1,
    show_pass_rates=False,
    x_axis_limits=None,
    y_axis_limits=None):
    """
    Plot students, assessments, lessons, and prereqs
    in a two-dimensional skill embedding

    Students, assessments, prereqs = points
    Lessons = vectors

    See nb/toy_examples.ipynb for example invocations

    :param EmbeddingModel model: A skill embedding model
    :param int timestep: A timestep. By default, timestep=-1 => latest snapshot
    :param float id_padding_x: Padding between object and id along x-axis
    :param float id_padding_y: Padding between object and id along y-axis
    :param float alpha: Alpha level for scatterplot points' color
    :param int size: Size of scatterplot points
    :param str|None title: Title of plot
    :param bool show_legend:
        True => show legend in upper left corner
        False => do not show legend

    :param bool force_invariant_axis_limits:
        True => plot will have same axes limits regardless of timestep,
        False => plot may have different axes limits depending on timestep

    :param float axis_limit_padding:
        Padding for axis limits (to prevent points from being stuck
        at the edges of the plot)

    :param bool show_pass_rates:
        True => color assessments by pass rate,
        False => don't color assessments

    :param list[int,int]|None x_axis_limits: [x_min, x_max]
    :param list[int,int]|None y_axis_limits: [y_min, y_max]
    """
    if model.embedding_dimension != 2:
        raise ValueError('Invalid embedding dimension!')
    if timestep<-1 or timestep>=model.history.duration():
        raise ValueError('Invalid timestep!')
    if size<=0:
        raise ValueError('Invalid scatterplot point size!')
    if axis_limit_padding<0:
        raise ValueError('Invalid axis limit padding!')
    if show_lessons is None:
        show_lessons = model.using_lessons
    if show_prereqs is None:
        show_prereqs = model.using_prereqs
    if show_lessons and not model.using_lessons:
        raise ValueError(
            'Cannot show lessons because model does not use lessons!')
    if show_prereqs and not model.using_prereqs:
        raise ValueError(
            'Cannot show prereqs because model does not use prereqs!')
    if show_concepts and not model.using_graph_prior:
        raise ValueError(
            'Cannot show concepts because model does not use a graph prior!')
    if show_student_ids and not show_students:
        raise ValueError('Cannot show student_ids without students!')
    if show_assessment_ids and not show_assessments:
        raise ValueError('Cannot show assessment_ids without assessments!')
    if show_lesson_ids and not show_lessons and not show_prereqs:
        raise ValueError('Cannot show lesson_ids without lessons and/or prereqs!')
    if show_pass_rates and not show_assessments:
        raise ValueError('Cannot show pass rates without assessments!')
    if show_concept_ids and not show_concepts:
        raise ValueError('Cannot show concept_ids without concepts!')

    if show_pass_rates and model.history.num_students() > 1:
        _logger.warning('Showing pass rates for more than one student!')

    _, ax = plt.subplots()

    if show_students:
        student_embeddings_x = model.student_embeddings[:, 0, timestep]
        student_embeddings_y = model.student_embeddings[:, 1, timestep]
        ax.scatter(
            student_embeddings_x, student_embeddings_y,
            alpha=alpha, marker='o', s=size, label='student')

        if show_student_ids:
            for student_id in model.history.iter_students():
                student_idx = model.history.idx_of_student_id(student_id)
                student_x = student_embeddings_x[student_idx]
                student_y = student_embeddings_y[student_idx]
                student_id_x = student_x + id_padding_x
                student_id_y = student_y + id_padding_y
                ax.annotate(student_id, xy=(
                    student_x, student_y), xytext=(
                    student_id_x, student_id_y))

    if show_assessments:
        assessment_embeddings_x = model.assessment_embeddings[:, 0]
        assessment_embeddings_y = model.assessment_embeddings[:, 1]
        if show_pass_rates:
            num_assessments = model.history.num_assessments()
            pass_rates = [model.history.assessment_pass_rate(
                model.history.id_of_assessment_idx(
                    i), timestep if timestep!=-1 else None) for i in xrange(
                num_assessments)]
            ax.scatter(
                assessment_embeddings_x,
                assessment_embeddings_y,
                c=pass_rates,
                alpha=alpha,
                marker='s',
                s=size,
                label='assessment',
                cmap=matplotlib.cm.cool)
        else:
            ax.scatter(
                assessment_embeddings_x,
                assessment_embeddings_y,
                alpha=alpha,
                marker='s',
                s=size,
                label='assessment')

        if show_assessment_ids:
            for assessment_id in model.history.iter_assessments():
                assessment_idx = model.history.idx_of_assessment_id(assessment_id)
                assessment_x = assessment_embeddings_x[assessment_idx]
                assessment_y = assessment_embeddings_y[assessment_idx]
                assessment_id_x = assessment_x + id_padding_x
                assessment_id_y = assessment_y + id_padding_y
                ax.annotate(assessment_id, xy=(
                    assessment_x, assessment_y), xytext=(
                    assessment_id_x, assessment_id_y))

    if show_concepts:
        concept_embeddings_x = model.concept_embeddings[:, 0]
        concept_embeddings_y = model.concept_embeddings[:, 1]
        ax.scatter(
            concept_embeddings_x,
            concept_embeddings_y,
            alpha=alpha,
            marker='^',
            s=size,
            label='concept')

        if show_concept_ids:
            for concept_id, concept_idx in model.graph.idx_of_concept_id.iteritems():
                concept_x = concept_embeddings_x[concept_idx]
                concept_y = concept_embeddings_y[concept_idx]
                concept_id_x = concept_x + id_padding_x
                concept_id_y = concept_y + id_padding_y
                ax.annotate(concept_id, xy=(
                    concept_x, concept_y), xytext=(
                    concept_id_x, concept_id_y))

    if show_lessons:
        if model.using_prereqs and show_prereqs:
            prereq_embeddings_x = model.prereq_embeddings[:, 0]
            prereq_embeddings_y = model.prereq_embeddings[:, 1]
        else:
            prereq_embeddings_x = prereq_embeddings_y = [0] * (
                model.history.num_lessons())
        lesson_embeddings_x = model.lesson_embeddings[:, 0]
        lesson_embeddings_y = model.lesson_embeddings[:, 1]
        ax.quiver(
            prereq_embeddings_x, prereq_embeddings_y,
            lesson_embeddings_x, lesson_embeddings_y, pivot='tail')

        if show_lesson_ids:
            for lesson_id in model.history.iter_lessons():
                lesson_idx = model.history.idx_of_lesson_id(lesson_id)
                lesson_x = prereq_embeddings_x[lesson_idx] if model.using_prereqs else 0
                lesson_y = prereq_embeddings_y[lesson_idx] if model.using_prereqs else 0
                lesson_id_x = lesson_x + id_padding_x
                lesson_id_y = lesson_y + id_padding_y
                ax.annotate(lesson_id, xy=(
                    lesson_x, lesson_y), xytext=(
                    lesson_id_x, lesson_id_y))

    if show_legend:
        ax.legend(loc='upper left')

    if force_invariant_axis_limits:
        x = []
        y = []
        if show_students:
            x += np.unique(model.student_embeddings[:, 0, :]).tolist()
            y += np.unique(model.student_embeddings[:, 1, :]).tolist()
        if show_assessments:
            x += np.unique(model.assessment_embeddings[:, 0]).tolist()
            y += np.unique(model.assessment_embeddings[:, 1]).tolist()
        if show_lessons:
            x += np.unique(model.lesson_embeddings[:, 0] + (
                model.prereq_embeddings[:, 0] if show_prereqs else 0)).tolist()
            y += np.unique(model.lesson_embeddings[:, 1] + (
                model.prereq_embeddings[:, 1] if show_prereqs else 0)).tolist()
        if show_concepts:
            x += np.unique(model.concept_embeddings[:, 0]).tolist()
            y += np.unique(model.concept_embeddings[:, 1]).tolist()
        ax.set_xlim([min(x)-axis_limit_padding, max(x)+axis_limit_padding])
        ax.set_ylim([min(y)-axis_limit_padding, max(y)+axis_limit_padding])

    if x_axis_limits is not None:
        ax.set_xlim(x_axis_limits)
    if y_axis_limits is not None:
        ax.set_ylim(y_axis_limits)

    if title is None:
        title = 'Latent Skill Space'
    ax.set_title(title)

    ax.set_xlabel('Skill 1')
    ax.set_ylabel('Skill 2')

    plt.show()


# -*- coding: utf-8 -*-

"""
The :mod:`sklearn.naive_bayes` module implements Naive Bayes algorithms. These
are supervised learning methods based on applying Bayes' theorem with strong
(naive) feature independence assumptions.
"""

# Author: Vincent Michel <vincent.michel@inria.fr>
#         Minor fixes by Fabian Pedregosa
#         Amit Aides <amitibo@tx.technion.ac.il>
#         Yehuda Finkelstein <yehudaf@tx.technion.ac.il>
#         Lars Buitinck <L.J.Buitinck@uva.nl>
#         Jan Hendrik Metzen <jhm@informatik.uni-bremen.de>
#         (parts based on earlier work by Mathieu Blondel)
#
# License: BSD 3 clause

from abc import ABCMeta, abstractmethod

import numpy as np
from scipy.sparse import issparse

from .base import BaseEstimator, ClassifierMixin
from .preprocessing import binarize
from .preprocessing import LabelBinarizer
from .preprocessing import label_binarize
from .utils import check_X_y, check_array
from .utils.extmath import safe_sparse_dot, logsumexp
from .utils.multiclass import _check_partial_fit_first_call
from .utils.fixes import in1d
from .utils.validation import check_is_fitted
from .externals import six

__all__ = ['BernoulliNB', 'GaussianNB', 'MultinomialNB']

class BaseNB(six.with_metaclass(ABCMeta, BaseEstimator, ClassifierMixin)):
    """Abstract base class for naive Bayes estimators"""

    @abstractmethod
    def _joint_log_likelihood(self, X):
        """Compute the unnormalized posterior log probability of X

        I.e. ``log P(c) + log P(x|c)`` for all rows x of X, as an array-like of
        shape [n_classes, n_samples].

        Input is passed to _joint_log_likelihood as-is by predict,
        predict_proba and predict_log_proba.
        """

    def predict(self, X):
        """
        Perform classification on an array of test vectors X.

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

        Returns
        -------
        C : array, shape = [n_samples]
            Predicted target values for X
        """
        jll = self._joint_log_likelihood(X)
        return self.classes_[np.argmax(jll, axis=1)]

    def predict_log_proba(self, X):
        """
        Return log-probability estimates for the test vector X.

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

        Returns
        -------
        C : array-like, shape = [n_samples, n_classes]
            Returns the log-probability of the samples for each class in
            the model. The columns correspond to the classes in sorted
            order, as they appear in the attribute `classes_`.
        """
        jll = self._joint_log_likelihood(X)
        # normalize by P(x) = P(f_1, ..., f_n)
        log_prob_x = logsumexp(jll, axis=1)
        return jll - np.atleast_2d(log_prob_x).T

    def predict_proba(self, X):
        """
        Return probability estimates for the test vector X.

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

        Returns
        -------
        C : array-like, shape = [n_samples, n_classes]
            Returns the probability of the samples for each class in
            the model. The columns correspond to the classes in sorted
            order, as they appear in the attribute `classes_`.
        """
        return np.exp(self.predict_log_proba(X))

class GaussianNB(BaseNB):
    """
    Gaussian Naive Bayes (GaussianNB)

    Can perform online updates to model parameters via `partial_fit` method.
    For details on algorithm used to update feature means and variance online,
    see Stanford CS tech report STAN-CS-79-773 by Chan, Golub, and LeVeque:

        http://i.stanford.edu/pub/cstr/reports/cs/tr/79/773/CS-TR-79-773.pdf

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

    Attributes
    ----------
    class_prior_ : array, shape (n_classes,)
        probability of each class.

    class_count_ : array, shape (n_classes,)
        number of training samples observed in each class.

    theta_ : array, shape (n_classes, n_features)
        mean of each feature per class

    sigma_ : array, shape (n_classes, n_features)
        variance of each feature per class

    Examples
    --------
    >>> import numpy as np
    >>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
    >>> Y = np.array([1, 1, 1, 2, 2, 2])
    >>> from sklearn.naive_bayes import GaussianNB
    >>> clf = GaussianNB()
    >>> clf.fit(X, Y)
    GaussianNB()
    >>> print(clf.predict([[-0.8, -1]]))
    [1]
    >>> clf_pf = GaussianNB()
    >>> clf_pf.partial_fit(X, Y, np.unique(Y))
    GaussianNB()
    >>> print(clf_pf.predict([[-0.8, -1]]))
    [1]
    """

    def fit(self, X, y, sample_weight=None):
        """Fit Gaussian Naive Bayes according to X, y

        Parameters
        ----------
        X : array-like, shape (n_samples, n_features)
            Training vectors, where n_samples is the number of samples
            and n_features is the number of features.

        y : array-like, shape (n_samples,)
            Target values.

        sample_weight : array-like, shape (n_samples,), optional
            Weights applied to individual samples (1. for unweighted).

        Returns
        -------
        self : object
            Returns self.
        """
        X, y = check_X_y(X, y)
        return self._partial_fit(X, y, np.unique(y), _refit=True,
                                 sample_weight=sample_weight)

    @staticmethod
    def _update_mean_variance(n_past, mu, var, X, sample_weight=None):
        """Compute online update of Gaussian mean and variance.

        Given starting sample count, mean, and variance, a new set of
        points X, and optionally sample weights, return the updated mean and
        variance. (NB - each dimension (column) in X is treated as independent
        -- you get variance, not covariance).

        Can take scalar mean and variance, or vector mean and variance to
        simultaneously update a number of independent Gaussians.

        See Stanford CS tech report STAN-CS-79-773 by Chan, Golub, and LeVeque:

        http://i.stanford.edu/pub/cstr/reports/cs/tr/79/773/CS-TR-79-773.pdf

        Parameters
        ----------
        n_past : int
            Number of samples represented in old mean and variance. If sample
            weights were given, this should contain the sum of sample
            weights represented in old mean and variance.

        mu : array-like, shape (number of Gaussians,)
            Means for Gaussians in original set.

        var : array-like, shape (number of Gaussians,)
            Variances for Gaussians in original set.

        sample_weight : array-like, shape (n_samples,), optional
            Weights applied to individual samples (1. for unweighted).

        Returns
        -------
        total_mu : array-like, shape (number of Gaussians,)
            Updated mean for each Gaussian over the combined set.

        total_var : array-like, shape (number of Gaussians,)
            Updated variance for each Gaussian over the combined set.
        """
        if X.shape[0] == 0:
            return mu, var

        # Compute (potentially weighted) mean and variance of new datapoints
        if sample_weight is not None:
            n_new = float(sample_weight.sum())
            new_mu = np.average(X, axis=0, weights=sample_weight / n_new)
            new_var = np.average((X - new_mu) ** 2, axis=0,
                                 weights=sample_weight / n_new)
        else:
            n_new = X.shape[0]
            new_var = np.var(X, axis=0)
            new_mu = np.mean(X, axis=0)

        if n_past == 0:
            return new_mu, new_var

        n_total = float(n_past + n_new)

        # Combine mean of old and new data, taking into consideration
        # (weighted) number of observations
        total_mu = (n_new * new_mu + n_past * mu) / n_total

        # Combine variance of old and new data, taking into consideration
        # (weighted) number of observations. This is achieved by combining
        # the sum-of-squared-differences (ssd)
        old_ssd = n_past * var
        new_ssd = n_new * new_var
        total_ssd = (old_ssd + new_ssd +
                     (n_past / float(n_new * n_total)) *
                     (n_new * mu - n_new * new_mu) ** 2)
        total_var = total_ssd / n_total

        return total_mu, total_var

    def partial_fit(self, X, y, classes=None, sample_weight=None):
        """Incremental fit on a batch of samples.

        This method is expected to be called several times consecutively
        on different chunks of a dataset so as to implement out-of-core
        or online learning.

        This is especially useful when the whole dataset is too big to fit in
        memory at once.

        This method has some performance and numerical stability overhead,
        hence it is better to call partial_fit on chunks of data that are
        as large as possible (as long as fitting in the memory budget) to
        hide the overhead.

        Parameters
        ----------
        X : array-like, shape (n_samples, n_features)
            Training vectors, where n_samples is the number of samples and
            n_features is the number of features.

        y : array-like, shape (n_samples,)
            Target values.

        classes : array-like, shape (n_classes,)
            List of all the classes that can possibly appear in the y vector.

            Must be provided at the first call to partial_fit, can be omitted
            in subsequent calls.

        sample_weight : array-like, shape (n_samples,), optional
            Weights applied to individual samples (1. for unweighted).

        Returns
        -------
        self : object
            Returns self.
        """
        return self._partial_fit(X, y, classes, _refit=False,
                                 sample_weight=sample_weight)

    def _partial_fit(self, X, y, classes=None, _refit=False,
                     sample_weight=None):
        """Actual implementation of Gaussian NB fitting.

        Parameters
        ----------
        X : array-like, shape (n_samples, n_features)
            Training vectors, where n_samples is the number of samples and
            n_features is the number of features.

        y : array-like, shape (n_samples,)
            Target values.

        classes : array-like, shape (n_classes,)
            List of all the classes that can possibly appear in the y vector.

            Must be provided at the first call to partial_fit, can be omitted
            in subsequent calls.

        _refit: bool
            If true, act as though this were the first time we called
            _partial_fit (ie, throw away any past fitting and start over).

        sample_weight : array-like, shape (n_samples,), optional
            Weights applied to individual samples (1. for unweighted).

        Returns
        -------
        self : object
            Returns self.
        """

        X, y = check_X_y(X, y)
        epsilon = 1e-9

        if _refit:
            self.classes_ = None

        if _check_partial_fit_first_call(self, classes):
            # This is the first call to partial_fit:
            # initialize various cumulative counters
            n_features = X.shape[1]
            n_classes = len(self.classes_)
            self.theta_ = np.zeros((n_classes, n_features))
            self.sigma_ = np.zeros((n_classes, n_features))
            self.class_prior_ = np.zeros(n_classes)
            self.class_count_ = np.zeros(n_classes)
        else:
            if X.shape[1] != self.theta_.shape[1]:
                msg = "Number of features %d does not match previous data %d."
                raise ValueError(msg % (X.shape[1], self.theta_.shape[1]))
            # Put epsilon back in each time
            self.sigma_[:, :] -= epsilon

        classes = self.classes_

        unique_y = np.unique(y)
        unique_y_in_classes = in1d(unique_y, classes)

        if not np.all(unique_y_in_classes):
            raise ValueError("The target label(s) %s in y do not exist in the "
                             "initial classes %s" %
                             (y[~unique_y_in_classes], classes))

        for y_i in unique_y:
            i = classes.searchsorted(y_i)
            X_i = X[y == y_i, :]

            if sample_weight is not None:
                sw_i = sample_weight[y == y_i]
                N_i = sw_i.sum()
            else:
                sw_i = None
                N_i = X_i.shape[0]

            new_theta, new_sigma = self._update_mean_variance(
                self.class_count_[i], self.theta_[i, :], self.sigma_[i, :],
                X_i, sw_i)

            self.theta_[i, :] = new_theta
            self.sigma_[i, :] = new_sigma
            self.class_count_[i] += N_i

        self.sigma_[:, :] += epsilon
        self.class_prior_[:] = self.class_count_ / np.sum(self.class_count_)
        return self

    def _joint_log_likelihood(self, X):
        check_is_fitted(self, "classes_")

        X = check_array(X)
        joint_log_likelihood = []
        for i in range(np.size(self.classes_)):
            jointi = np.log(self.class_prior_[i])
            n_ij = - 0.5 * np.sum(np.log(2. * np.pi * self.sigma_[i, :]))
            n_ij -= 0.5 * np.sum(((X - self.theta_[i, :]) ** 2) /
                                 (self.sigma_[i, :]), 1)
            joint_log_likelihood.append(jointi + n_ij)

        joint_log_likelihood = np.array(joint_log_likelihood).T
        return joint_log_likelihood

class BaseDiscreteNB(BaseNB):
    """Abstract base class for naive Bayes on discrete/categorical data

    Any estimator based on this class should provide:

    __init__
    _joint_log_likelihood(X) as per BaseNB
    """

    def _update_class_log_prior(self, class_prior=None):
        n_classes = len(self.classes_)
        if class_prior is not None:
            if len(class_prior) != n_classes:
                raise ValueError("Number of priors must match number of"
                                 " classes.")
            self.class_log_prior_ = np.log(class_prior)
        elif self.fit_prior:
            # empirical prior, with sample_weight taken into account
            self.class_log_prior_ = (np.log(self.class_count_)
                                     - np.log(self.class_count_.sum()))
        else:
            self.class_log_prior_ = np.zeros(n_classes) - np.log(n_classes)

    def partial_fit(self, X, y, classes=None, sample_weight=None):
        """Incremental fit on a batch of samples.

        This method is expected to be called several times consecutively
        on different chunks of a dataset so as to implement out-of-core
        or online learning.

        This is especially useful when the whole dataset is too big to fit in
        memory at once.

        This method has some performance overhead hence it is better to call
        partial_fit on chunks of data that are as large as possible
        (as long as fitting in the memory budget) to hide the overhead.

        Parameters
        ----------
        X : {array-like, sparse matrix}, shape = [n_samples, n_features]
            Training vectors, where n_samples is the number of samples and
            n_features is the number of features.

        y : array-like, shape = [n_samples]
            Target values.

        classes : array-like, shape = [n_classes]
            List of all the classes that can possibly appear in the y vector.

            Must be provided at the first call to partial_fit, can be omitted
            in subsequent calls.

        sample_weight : array-like, shape = [n_samples], optional
            Weights applied to individual samples (1. for unweighted).

        Returns
        -------
        self : object
            Returns self.
        """
        X = check_array(X, accept_sparse='csr', dtype=np.float64)
        _, n_features = X.shape

        if _check_partial_fit_first_call(self, classes):
            # This is the first call to partial_fit:
            # initialize various cumulative counters
            n_effective_classes = len(classes) if len(classes) > 1 else 2
            self.class_count_ = np.zeros(n_effective_classes, dtype=np.float64)
            self.feature_count_ = np.zeros((n_effective_classes, n_features),
                                           dtype=np.float64)
        elif n_features != self.coef_.shape[1]:
            msg = "Number of features %d does not match previous data %d."
            raise ValueError(msg % (n_features, self.coef_.shape[-1]))

        Y = label_binarize(y, classes=self.classes_)
        if Y.shape[1] == 1:
            Y = np.concatenate((1 - Y, Y), axis=1)

        n_samples, n_classes = Y.shape

        if X.shape[0] != Y.shape[0]:
            msg = "X.shape[0]=%d and y.shape[0]=%d are incompatible."
            raise ValueError(msg % (X.shape[0], y.shape[0]))

        # label_binarize() returns arrays with dtype=np.int64.
        # We convert it to np.float64 to support sample_weight consistently
        Y = Y.astype(np.float64)
        if sample_weight is not None:
            Y *= check_array(sample_weight).T

        class_prior = self.class_prior

        # Count raw events from data before updating the class log prior
        # and feature log probas
        self._count(X, Y)

        # XXX: OPTIM: we could introduce a public finalization method to
        # be called by the user explicitly just once after several consecutive
        # calls to partial_fit and prior any call to predict[_[log_]proba]
        # to avoid computing the smooth log probas at each call to partial fit
        self._update_feature_log_prob()
        self._update_class_log_prior(class_prior=class_prior)
        return self

    def fit(self, X, y, sample_weight=None):
        """Fit Naive Bayes classifier according to X, y

        Parameters
        ----------
        X : {array-like, sparse matrix}, shape = [n_samples, n_features]
            Training vectors, where n_samples is the number of samples and
            n_features is the number of features.

        y : array-like, shape = [n_samples]
            Target values.

        sample_weight : array-like, shape = [n_samples], optional
            Weights applied to individual samples (1. for unweighted).

        Returns
        -------
        self : object
            Returns self.
        """
        X, y = check_X_y(X, y, 'csr')
        _, n_features = X.shape

        labelbin = LabelBinarizer()
        Y = labelbin.fit_transform(y)
        self.classes_ = labelbin.classes_
        if Y.shape[1] == 1:
            Y = np.concatenate((1 - Y, Y), axis=1)

        # LabelBinarizer().fit_transform() returns arrays with dtype=np.int64.
        # We convert it to np.float64 to support sample_weight consistently;
        # this means we also don't have to cast X to floating point
        Y = Y.astype(np.float64)
        if sample_weight is not None:
            Y *= check_array(sample_weight).T

        class_prior = self.class_prior

        # Count raw events from data before updating the class log prior
        # and feature log probas
        n_effective_classes = Y.shape[1]
        self.class_count_ = np.zeros(n_effective_classes, dtype=np.float64)
        self.feature_count_ = np.zeros((n_effective_classes, n_features),
                                       dtype=np.float64)
        self._count(X, Y)
        self._update_feature_log_prob()
        self._update_class_log_prior(class_prior=class_prior)
        return self

    # XXX The following is a stopgap measure; we need to set the dimensions
    # of class_log_prior_ and feature_log_prob_ correctly.
    def _get_coef(self):
        return (self.feature_log_prob_[1:]
                if len(self.classes_) == 2 else self.feature_log_prob_)

    def _get_intercept(self):
        return (self.class_log_prior_[1:]
                if len(self.classes_) == 2 else self.class_log_prior_)

    coef_ = property(_get_coef)
    intercept_ = property(_get_intercept)

class MultinomialNB(BaseDiscreteNB):
    """
    Naive Bayes classifier for multinomial models

    The multinomial Naive Bayes classifier is suitable for classification with
    discrete features (e.g., word counts for text classification). The
    multinomial distribution normally requires integer feature counts. However,
    in practice, fractional counts such as tf-idf may also work.

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

    Parameters
    ----------
    alpha : float, optional (default=1.0)
        Additive (Laplace/Lidstone) smoothing parameter
        (0 for no smoothing).

    fit_prior : boolean
        Whether to learn class prior probabilities or not.
        If false, a uniform prior will be used.

    class_prior : array-like, size (n_classes,)
        Prior probabilities of the classes. If specified the priors are not
        adjusted according to the data.

    Attributes
    ----------
    class_log_prior_ : array, shape (n_classes, )
        Smoothed empirical log probability for each class.

    intercept_ : property
        Mirrors ``class_log_prior_`` for interpreting MultinomialNB
        as a linear model.

    feature_log_prob_ : array, shape (n_classes, n_features)
        Empirical log probability of features
        given a class, ``P(x_i|y)``.

    coef_ : property
        Mirrors ``feature_log_prob_`` for interpreting MultinomialNB
        as a linear model.

    class_count_ : array, shape (n_classes,)
        Number of samples encountered for each class during fitting. This
        value is weighted by the sample weight when provided.

    feature_count_ : array, shape (n_classes, n_features)
        Number of samples encountered for each (class, feature)
        during fitting. This value is weighted by the sample weight when
        provided.

    Examples
    --------
    >>> import numpy as np
    >>> X = np.random.randint(5, size=(6, 100))
    >>> y = np.array([1, 2, 3, 4, 5, 6])
    >>> from sklearn.naive_bayes import MultinomialNB
    >>> clf = MultinomialNB()
    >>> clf.fit(X, y)
    MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)
    >>> print(clf.predict(X[2]))
    [3]

    Notes
    -----
    For the rationale behind the names `coef_` and `intercept_`, i.e.
    naive Bayes as a linear classifier, see J. Rennie et al. (2003),
    Tackling the poor assumptions of naive Bayes text classifiers, ICML.

    References
    ----------
    C.D. Manning, P. Raghavan and H. Schuetze (2008). Introduction to
    Information Retrieval. Cambridge University Press, pp. 234-265.
    http://nlp.stanford.edu/IR-book/html/htmledition/naive-bayes-text-classification-1.html
    """

    def __init__(self, alpha=1.0, fit_prior=True, class_prior=None):
        self.alpha = alpha
        self.fit_prior = fit_prior
        self.class_prior = class_prior

    def _count(self, X, Y):
        """Count and smooth feature occurrences."""
        if np.any((X.data if issparse(X) else X) < 0):
            raise ValueError("Input X must be non-negative")
        self.feature_count_ += safe_sparse_dot(Y.T, X)
        self.class_count_ += Y.sum(axis=0)

    def _update_feature_log_prob(self):
        """Apply smoothing to raw counts and recompute log probabilities"""
        smoothed_fc = self.feature_count_ + self.alpha
        smoothed_cc = smoothed_fc.sum(axis=1)

        self.feature_log_prob_ = (np.log(smoothed_fc)
                                  - np.log(smoothed_cc.reshape(-1, 1)))

    def _joint_log_likelihood(self, X):
        """Calculate the posterior log probability of the samples X"""
        check_is_fitted(self, "classes_")

        X = check_array(X, accept_sparse='csr')
        return (safe_sparse_dot(X, self.feature_log_prob_.T)
                + self.class_log_prior_)

class BernoulliNB(BaseDiscreteNB):
    """Naive Bayes classifier for multivariate Bernoulli models.

    Like MultinomialNB, this classifier is suitable for discrete data. The
    difference is that while MultinomialNB works with occurrence counts,
    BernoulliNB is designed for binary/boolean features.

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

    Parameters
    ----------
    alpha : float, optional (default=1.0)
        Additive (Laplace/Lidstone) smoothing parameter
        (0 for no smoothing).

    binarize : float or None, optional
        Threshold for binarizing (mapping to booleans) of sample features.
        If None, input is presumed to already consist of binary vectors.

    fit_prior : boolean
        Whether to learn class prior probabilities or not.
        If false, a uniform prior will be used.

    class_prior : array-like, size=[n_classes,]
        Prior probabilities of the classes. If specified the priors are not
        adjusted according to the data.

    Attributes
    ----------
    class_log_prior_ : array, shape = [n_classes]
        Log probability of each class (smoothed).

    feature_log_prob_ : array, shape = [n_classes, n_features]
        Empirical log probability of features given a class, P(x_i|y).

    class_count_ : array, shape = [n_classes]
        Number of samples encountered for each class during fitting. This
        value is weighted by the sample weight when provided.

    feature_count_ : array, shape = [n_classes, n_features]
        Number of samples encountered for each (class, feature)
        during fitting. This value is weighted by the sample weight when
        provided.

    Examples
    --------
    >>> import numpy as np
    >>> X = np.random.randint(2, size=(6, 100))
    >>> Y = np.array([1, 2, 3, 4, 4, 5])
    >>> from sklearn.naive_bayes import BernoulliNB
    >>> clf = BernoulliNB()
    >>> clf.fit(X, Y)
    BernoulliNB(alpha=1.0, binarize=0.0, class_prior=None, fit_prior=True)
    >>> print(clf.predict(X[2]))
    [3]

    References
    ----------

    C.D. Manning, P. Raghavan and H. Schuetze (2008). Introduction to
    Information Retrieval. Cambridge University Press, pp. 234-265.
    http://nlp.stanford.edu/IR-book/html/htmledition/the-bernoulli-model-1.html

    A. McCallum and K. Nigam (1998). A comparison of event models for naive
    Bayes text classification. Proc. AAAI/ICML-98 Workshop on Learning for
    Text Categorization, pp. 41-48.

    V. Metsis, I. Androutsopoulos and G. Paliouras (2006). Spam filtering with
    naive Bayes -- Which naive Bayes? 3rd Conf. on Email and Anti-Spam (CEAS).
    """

    def __init__(self, alpha=1.0, binarize=.0, fit_prior=True,
                 class_prior=None):
        self.alpha = alpha
        self.binarize = binarize
        self.fit_prior = fit_prior
        self.class_prior = class_prior

    def _count(self, X, Y):
        """Count and smooth feature occurrences."""
        if self.binarize is not None:
            X = binarize(X, threshold=self.binarize)
        self.feature_count_ += safe_sparse_dot(Y.T, X)
        self.class_count_ += Y.sum(axis=0)

    def _update_feature_log_prob(self):
        """Apply smoothing to raw counts and recompute log probabilities"""
        smoothed_fc = self.feature_count_ + self.alpha
        smoothed_cc = self.class_count_ + self.alpha * 2

        self.feature_log_prob_ = (np.log(smoothed_fc)
                                  - np.log(smoothed_cc.reshape(-1, 1)))

    def _joint_log_likelihood(self, X):
        """Calculate the posterior log probability of the samples X"""
        check_is_fitted(self, "classes_")

        X = check_array(X, accept_sparse='csr')

        if self.binarize is not None:
            X = binarize(X, threshold=self.binarize)

        n_classes, n_features = self.feature_log_prob_.shape
        n_samples, n_features_X = X.shape

        if n_features_X != n_features:
            raise ValueError("Expected input with %d features, got %d instead"
                             % (n_features, n_features_X))

        neg_prob = np.log(1 - np.exp(self.feature_log_prob_))
        # Compute  neg_prob · (1 - X).T  as  ∑neg_prob - X · neg_prob
        jll = safe_sparse_dot(X, (self.feature_log_prob_ - neg_prob).T)
        jll += self.class_log_prior_ + neg_prob.sum(axis=1)

        return jll