"""
==========================
Plotting Validation Curves
==========================

In this plot you can see the training scores and validation scores of an SVM
for different values of the kernel parameter gamma. For very low values of
gamma, you can see that both the training score and the validation score are
low. This is called underfitting. Medium values of gamma will result in high
values for both scores, i.e. the classifier is performing fairly well. If gamma
is too high, the classifier will overfit, which means that the training score
is good but the validation score is poor.
"""
print(__doc__)

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_digits
from sklearn.svm import SVC
from sklearn.learning_curve import validation_curve

digits = load_digits()
X, y = digits.data, digits.target

param_range = np.logspace(-6, -1, 5)
train_scores, test_scores = validation_curve(
    SVC(), X, y, param_name="gamma", param_range=param_range,
    cv=10, scoring="accuracy", n_jobs=1)
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)

plt.title("Validation Curve with SVM")
plt.xlabel("$\gamma$")
plt.ylabel("Score")
plt.ylim(0.0, 1.1)
plt.semilogx(param_range, train_scores_mean, label="Training score", color="r")
plt.fill_between(param_range, train_scores_mean - train_scores_std,
                 train_scores_mean + train_scores_std, alpha=0.2, color="r")
plt.semilogx(param_range, test_scores_mean, label="Cross-validation score",
             color="g")
plt.fill_between(param_range, test_scores_mean - test_scores_std,
                 test_scores_mean + test_scores_std, alpha=0.2, color="g")
plt.legend(loc="best")
plt.show()


from SimpleCV import Image, Camera, Display, Color
import pygame as pg
import numpy as np
from pylab import *
from mpl_toolkits.mplot3d import axes3d
from matplotlib.backends.backend_agg import FigureCanvasAgg
import cv2

bins = 8
#precompute
idxs = []
colors = []
offset = bins/2
skip = 255/bins
for x in range(0,bins):
    for y in range(0,bins):
        for z in range(0,bins):
            b = ((x*skip)+offset)/255.0
            g = ((y*skip)+offset)/255.0
            r = ((z*skip)+offset)/255.0
            idxs.append((x,y,z,(r,g,b)))

# plot points in 3D
cam = Camera()
disp = Display((800,600))
fig = figure()
fig.set_size_inches( (10,7) )

canvas = FigureCanvasAgg(fig)
azim = 0
while disp.isNotDone():
    ax = fig.gca(projection='3d')
    ax.set_xlabel('BLUE', color=(0,0,1) )
    ax.set_ylabel('GREEN',color=(0,1,0))
    ax.set_zlabel('RED',color=(1,0,0))
    # Get the color histogram
    img = cam.getImage().scale(0.3)
    rgb = img.getNumpyCv2()
    hist = cv2.calcHist([rgb],[0,1,2],None,[bins,bins,bins],[0,256,0,256,0,256])
    hist = hist/np.max(hist)
    # render everything
    [ ax.plot([x],[y],[z],'.',markersize=max(hist[x,y,z]*100,6),color=color) for x,y,z,color in idxs if(hist[x][y][z]>0) ]
    #[ ax.plot([x],[y],[z],'.',color=color) for x,y,z,color in idxs if(hist[x][y][z]>0) ]
    ax.set_xlim3d(0, bins-1)
    ax.set_ylim3d(0, bins-1)
    ax.set_zlim3d(0, bins-1)
    azim = (azim+0.5)%360
    ax.view_init(elev=35, azim=azim)
    ########### convert matplotlib to  SimpleCV image
    canvas.draw()
    renderer = canvas.get_renderer()
    raw_data = renderer.tostring_rgb()
    size = canvas.get_width_height()
    surf = pg.image.fromstring(raw_data, size, "RGB")
    figure = Image(surf)
    ############ All done
    figure = figure.floodFill((0,0), tolerance=5,color=Color.WHITE)
    result = figure.blit(img, pos=(20,20))
    result.save(disp)
    fig.clf()


import pandas as pd
import os
from MPropulator import validations as vd

def readConfig(config):
    '''
    Reads in the config file as a dataframe
    and validates the inputs and outputs of
    this file.

    args:   config is the path to the config file csv
    output: pandas dataframe that is a parsed and prepped
            config file.
    '''

    vd.validateConfigPath(config)

    parseConfig = pd.read_csv(config)
    #parseConfig.fillna("", inplace=True)

    # users enter rows to skip as 1,2,3,4 and cols to skip as A,B,C
    # we need to parse these into lists
    split = str.split
    def splitFunc(val):
        arr = split(str(val).strip(" "),",")
        arr = [x for x in arr if x != 'nan']
        return arr
    #splitFunc = lambda x: split(str(x).strip(" "), ",")

    def assertList(x):
        assert(isinstance(x,list))

    parseConfig['skiprows'] = map(splitFunc, parseConfig['skiprows'])
    parseConfig['skipcols'] = map(splitFunc, parseConfig['skipcols'])

    map(assertList, parseConfig['skiprows'])
    map(assertList, parseConfig['skipcols'])

    # in addition, for the skiprows, we want these as ints, not strings
    def makeInt(array):
        intArray = [int(x) for x in array]
        return intArray

    try:
        parseConfig['skiprows'] = map(makeInt, parseConfig['skiprows'])
    except:
        raise ValueError("Cannot convert some values in skiprows to ints")

    parseConfig['ignore'].fillna(False, inplace=True)

    vd.validateConfigRead(parseConfig)
    return parseConfig


#!/usr/bin/env python

# Copyright (c) 2014, the GREAT3 executive committee (http://www.great3challenge.info/?q=contacts)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted
# provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions
# and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
# conditions and the following disclaimer in the documentation and/or other materials provided with
# the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to
# endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""@file plot_variable_submission.py

Handy command line executable script for plotting up a GREAT3 variable shear submission.
"""

# Constants
NFIELDS = 10
NBINS_THETA = 15
YLIM_EMODE = 2.e-5
YLIM_BMODE = 2.e-5

def plot(submission_filename, output_filename, nfields=NFIELDS, nbins_theta=NBINS_THETA,
         ylim_emode=YLIM_EMODE, ylim_bmode=YLIM_BMODE):
    """Plot a submission.
    """
    import numpy as np
    import matplotlib.pyplot as plt

    # Load the data from the input submission
    data = np.loadtxt(submission_filename)
    field, theta, map_E, map_B, maperr = (
        data[:, 0].astype(int), data[:, 1], data[:, 2], data[:, 3], data[:, 4])

    # Then plot (largely borrowed from the code in server/great3/evaluate.py)
    plt.figure(figsize=(10, 8))
    plt.subplot(211)
    for ifield in range(nfields):
        plt.semilogx(
            theta[ifield * nbins_theta: (ifield + 1) * nbins_theta],
            map_E[ifield * nbins_theta: (ifield + 1) * nbins_theta], label="Field "+str(ifield))

    plt.ylim(-ylim_emode, ylim_emode)
    plt.title(submission_filename+" E-mode")
    plt.ylabel("Ap. Mass Dispersion")
    plt.axhline(ls="--", color="k")
    plt.legend()
    plt.subplot(212)
    for ifield in range(nfields):
        plt.semilogx(
            theta[ifield * nbins_theta: (ifield + 1) * nbins_theta],
            map_B[ifield * nbins_theta: (ifield + 1) * nbins_theta], label="Field "+str(ifield))

    plt.ylim(-ylim_bmode, ylim_bmode)
    plt.title(submission_filename+" B-mode")
    plt.xlabel("Theta [degrees]")
    plt.ylabel("Ap. Mass Dispersion")
    plt.axhline(ls="--", color="k")
    plt.legend()
    plt.savefig(output_filename)
    return

if __name__ == "__main__":

    import sys
    # Get the input and output filenames from the command line
    if len(sys.argv) != 3:
        print "plot_variable_submission.py"
        print "usage: ./plot_variable_submission.py input_submission output_filename"
        sys.exit(1)
    submission_filename = sys.argv[1]
    output_filename = sys.argv[2]
    plot(submission_filename, output_filename)


"""Command line arguments to the oggm_benchmark command

Type `$ oggm_benchmark -h` for help

"""

# External modules
import os
import sys
import argparse
import time
import logging
import pandas as pd
import geopandas as gpd

# Locals
import oggm.cfg as cfg
from oggm import utils, workflow, tasks
from oggm.exceptions import InvalidParamsError

def _add_time_to_df(df, index, t):
    df.loc[index, 't'] = t
    m, s = divmod(t, 60)
    h, m = divmod(m, 60)
    df.loc[index, 'H'] = h
    df.loc[index, 'M'] = m
    df.loc[index, 'S'] = s

def run_benchmark(rgi_version=None, rgi_reg=None, border=None,
                  output_folder='', working_dir='', is_test=False,
                  test_rgidf=None, test_intersects_file=None,
                  test_topofile=None):
    """Does the actual job.

    Parameters
    ----------
    rgi_version : str
        the RGI version to use (defaults to cfg.PARAMS)
    rgi_reg : str
        the RGI region to process
    border : int
        the number of pixels at the maps border
    output_folder : str
        path to the output folder (where to put the preprocessed tar files)
    working_dir : str
        path to the OGGM working directory
    is_test : bool
        to test on a couple of glaciers only!
    test_rgidf : shapefile
        for testing purposes only
    test_intersects_file : shapefile
        for testing purposes only
    test_topofile : str
        for testing purposes only
    """

    # Module logger
    log = logging.getLogger(__name__)

    # Params
    params = {}

    # Local paths
    utils.mkdir(working_dir)
    params['working_dir'] = working_dir

    # Initialize OGGM and set up the run parameters
    cfg.initialize(logging_level='WORKFLOW', params=params, future=True)

    # Use multiprocessing?
    cfg.PARAMS['use_multiprocessing'] = True

    # How many grid points around the glacier?
    # Make it large if you expect your glaciers to grow large
    cfg.PARAMS['border'] = border

    # Set to True for operational runs
    cfg.PARAMS['continue_on_error'] = True

    # For statistics
    odf = pd.DataFrame()

    if rgi_version is None:
        rgi_version = cfg.PARAMS['rgi_version']
    base_dir = os.path.join(output_folder)

    # Add a package version file
    utils.mkdir(base_dir)
    opath = os.path.join(base_dir, 'package_versions.txt')
    with open(opath, 'w') as vfile:
        vfile.write(utils.show_versions(logger=log))

    # Read RGI
    start = time.time()
    if test_rgidf is None:
        # Get the RGI file
        rgidf = gpd.read_file(utils.get_rgi_region_file(rgi_reg,
                                                        version=rgi_version))
        # We use intersects
        rgif = utils.get_rgi_intersects_region_file(rgi_reg,
                                                    version=rgi_version)
        cfg.set_intersects_db(rgif)
    else:
        rgidf = test_rgidf
        cfg.set_intersects_db(test_intersects_file)

    if is_test:
        # Just for fun
        rgidf = rgidf.sample(2)
    _add_time_to_df(odf, 'Read RGI', time.time()-start)

    # Sort for more efficient parallel computing
    rgidf = rgidf.sort_values('Area', ascending=False)

    log.workflow('Starting prepro run for RGI reg: {} '
                 'and border: {}'.format(rgi_reg, border))
    log.workflow('Number of glaciers: {}'.format(len(rgidf)))

    # Input
    if test_topofile:
        cfg.PATHS['dem_file'] = test_topofile
    utils.apply_test_ref_tstars()

    # Initialize working directories
    start = time.time()
    gdirs = workflow.init_glacier_directories(rgidf, reset=True, force=True)
    _add_time_to_df(odf, 'init_glacier_directories', time.time()-start)

    # Tasks
    task_list = [
        tasks.define_glacier_region,
        tasks.process_cru_data,
        tasks.glacier_masks,
        tasks.compute_centerlines,
        tasks.initialize_flowlines,
        tasks.compute_downstream_line,
        tasks.compute_downstream_bedshape,
        tasks.catchment_area,
        tasks.catchment_intersections,
        tasks.catchment_width_geom,
        tasks.catchment_width_correction,
        tasks.local_t_star,
        tasks.mu_star_calibration,
        tasks.prepare_for_inversion,
        tasks.mass_conservation_inversion,
        tasks.filter_inversion_output,
        tasks.init_present_time_glacier,
    ]
    for task in task_list:
        start = time.time()
        workflow.execute_entity_task(task, gdirs)
        _add_time_to_df(odf, task.__name__, time.time()-start)

    # Runs
    start = time.time()
    workflow.execute_entity_task(tasks.run_random_climate, gdirs,
                                 nyears=250, bias=0, seed=0,
                                 output_filesuffix='_tstar')
    _add_time_to_df(odf, 'run_random_climate_tstar_250', time.time()-start)

    start = time.time()
    workflow.execute_entity_task(tasks.run_random_climate, gdirs,
                                 nyears=250, y0=1995, seed=0,
                                 output_filesuffix='_commit')
    _add_time_to_df(odf, 'run_random_climate_commit_250', time.time()-start)

    # Compile results
    start = time.time()
    utils.compile_glacier_statistics(gdirs)
    _add_time_to_df(odf, 'compile_glacier_statistics', time.time()-start)

    start = time.time()
    utils.compile_climate_statistics(gdirs,
                                     add_climate_period=[1920, 1960, 2000])
    _add_time_to_df(odf, 'compile_climate_statistics', time.time()-start)

    start = time.time()
    utils.compile_run_output(gdirs, input_filesuffix='_tstar')
    _add_time_to_df(odf, 'compile_run_output_tstar', time.time()-start)

    start = time.time()
    utils.compile_run_output(gdirs, input_filesuffix='_commit')
    _add_time_to_df(odf, 'compile_run_output_commit', time.time()-start)

    # Log
    opath = os.path.join(base_dir, 'benchmarks_b{:03d}.csv'.format(border))
    odf.index.name = 'Task'
    odf.to_csv(opath)
    log.workflow('OGGM benchmarks is done!')

def parse_args(args):
    """Check input arguments and env variables"""

    # CLI args
    description = ('Run an OGGM benchmark on a selected RGI Region. '
                   'This writes a benchmark_{border}.txt file where '
                   'the results are summarized')
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument('--map-border', type=int,
                        help='the size of the map border. Is required if '
                             '$OGGM_MAP_BORDER is not set.')
    parser.add_argument('--rgi-reg', type=str,
                        help='the rgi region to process. Is required if '
                             '$OGGM_RGI_REG is not set.')
    parser.add_argument('--rgi-version', type=str,
                        help='the RGI version to use. Defaults to the OGGM '
                             'default.')
    parser.add_argument('--working-dir', type=str,
                        help='path to the directory where to write the '
                             'output. Defaults to current directory or '
                             '$OGGM_WORKDIR.')
    parser.add_argument('--output', type=str,
                        help='path to the directory where to write the '
                             'output. Defaults to current directory or'
                             '$OGGM_OUTDIR.')
    parser.add_argument('--test', nargs='?', const=True, default=False,
                        help='if you want to do a test on a couple of '
                             'glaciers first.')
    args = parser.parse_args(args)

    # Check input
    rgi_reg = args.rgi_reg
    if not rgi_reg:
        rgi_reg = os.environ.get('OGGM_RGI_REG', None)
        if rgi_reg is None:
            raise InvalidParamsError('--rgi-reg is required!')
    rgi_reg = '{:02}'.format(int(rgi_reg))

    rgi_version = args.rgi_version

    border = args.map_border
    if not border:
        border = os.environ.get('OGGM_MAP_BORDER', None)
        if border is None:
            raise InvalidParamsError('--map-border is required!')

    working_dir = args.working_dir
    if not working_dir:
        working_dir = os.environ.get('OGGM_WORKDIR', '')

    output_folder = args.output
    if not output_folder:
        output_folder = os.environ.get('OGGM_OUTDIR', '')

    border = int(border)
    output_folder = os.path.abspath(output_folder)
    working_dir = os.path.abspath(working_dir)

    # All good
    return dict(rgi_version=rgi_version, rgi_reg=rgi_reg,
                border=border, output_folder=output_folder,
                working_dir=working_dir, is_test=args.test)

def main():
    """Script entry point"""

    run_benchmark(**parse_args(sys.argv[1:]))


from matplotlib import pyplot as plt
import matplotlib.ticker as plticker
import seaborn as sns
import pandas as pd
import numpy as np
import math
import warnings
from collections import Counter
import nfldatatools as nfltools

rs_pbp = nfltools.gather_data(playoffs=False)
po_pbp = nfltools.gather_data(playoffs=True)

sns.set_style("whitegrid")

#Set general plot properties
sns.set_style("white", {"axes.grid": True})
sns.set_context({"figure.figsize": (10, 7)})

################################################
# Figure 1 - HeatMap
def fig1():
    filters=[
        ['Season', 2009, '>=']
    ]
    yard_grouping = 10
    fig,(ax1, ax2) = plt.subplots(1,2,figsize=(15,10))
    nfltools.plotPassingHeatMap(rs_pbp, filters=filters, ax=ax1, yard_grouping=yard_grouping)
    nfltools.plotPassingHeatMap(po_pbp, filters=filters, ax=ax2, yard_grouping=yard_grouping)
    return fig
figure_1 = fig1()

###############################################################
# Figure 1 - HeatMap
def match(playtype):
    valid_play_types = [
        'Field Goal',
        'Pass',
        'Run',
        'QB Kneel',
        'Punt',
        'Extra Point',
        'Sack',
        'Spike',
        'Timeout'
    ]
    return playtype in valid_play_types

def condense_pbp_data(df):
    new_df = df[['qtr', 'down', 'TimeUnder','TimeSecs', 'yrdline100', 'ScoreDiff', 'PlayType','Season']]
    new_df = new_df[new_df.PlayType.map(match)]
    new_df = new_df[new_df['down'].isnull()==False]
    return new_df

playoffs = condense_pbp_data(po_pbp)
regular = condense_pbp_data(rs_pbp)

def makeDF(season=2009):
    rdf = regular#[regular.Season==season]
    rdf = rdf.groupby('PlayType').agg({'qtr':len}).reset_index()
    rdf.columns = ['PlayType', 'Count']
    rdf['Percent Total'] = rdf.Count/rdf.Count.sum()*100
    rdf['ID'] = 'Regular'

    pdf = playoffs[playoffs.Season==season]
    pdf = pdf.groupby('PlayType').agg({'qtr':len}).reset_index()
    pdf.columns = ['PlayType', 'Count']
    pdf['Percent Total'] = pdf.Count/pdf.Count.sum()*100
    pdf['ID'] = 'Playoffs'

    x = rdf.append(pdf, ignore_index=True)
    fig, ax1 = plt.subplots(1,1,figsize=(12,10))
    sns.barplot(ax=ax1, data=x, y='PlayType', x='Percent Total',hue='ID', order=['Pass', 'Run', 'Punt', 'Field Goal', 'QB Kneel'])
    ax1.set_xlim(0,60)
    return fig

figure_2 = makeDF()

###############################################################
# Figure 1 - HeatMap
def fig3():
    sns.set_style('whitegrid')
    sns.set_palette(['blue', 'green','red'])

    fig, axes = plt.subplots(2, 1, figsize=(15,15))
    shade = True
    bw = '2'

    sns.kdeplot(ax=axes[0],data=rs_pbp[rs_pbp.PlayType == 'Pass'].ScoreDiff.dropna(),label='Pass',shade=shade,bw=bw)
    sns.kdeplot(ax=axes[0],data=rs_pbp[rs_pbp.PlayType == 'Run'].ScoreDiff.dropna(),label='Run',shade=shade,bw=bw)
    sns.kdeplot(ax=axes[0],data=rs_pbp[rs_pbp.PlayType == 'Extra Point'].ScoreDiff.dropna(),label='Extra Point',shade=shade,bw=bw)

    axes[0].set_xlim(-40,40)
    axes[0].set_ylim(0,0.09)

    sns.kdeplot(ax=axes[1],data=po_pbp[po_pbp.PlayType == 'Pass'].ScoreDiff.dropna(),label='Pass',shade=shade,bw=bw)
    sns.kdeplot(ax=axes[1],data=po_pbp[po_pbp.PlayType == 'Run'].ScoreDiff.dropna(),label='Run',shade=shade,bw=bw)
    sns.kdeplot(ax=axes[1],data=po_pbp[po_pbp.PlayType == 'Extra Point'].ScoreDiff.dropna(),label='Extra Point',shade=shade,bw=bw)

    axes[1].set_xlim(-40,40)
    axes[1].set_ylim(0,0.09)
    #SMOOTH IT OUT!
    return fig

figure_3 = fig3()

###############################################################
# Figure 1 - HeatMap
def plot_PlayType(df,stat,playtypelist=['Pass','Run','Field Goal','QB Kneel','Punt'],percent_total=False):
    g = df.groupby([stat,'PlayType']).count().reset_index()
    g = g[g.columns[0:3]]
    last_col_name = g.columns[-1]
    g1 = g.groupby([stat, 'PlayType']).agg({last_col_name: 'sum'})
    if percent_total:
        g1 = g1.groupby(level=1).apply(lambda x: 100 * x / float(x.sum()))
    g1 = g1.reset_index()
    g1 = g1[g1.PlayType.apply(lambda x: x in playtypelist)]
    return sns.barplot(x=stat, y=last_col_name, hue="PlayType", data=g1)

def fig4():
    fig = plt.figure(figsize=(16,32))

    ax3 = fig.add_subplot(513)
    ax3 = plot_PlayType(regular,'qtr',['Run','Pass'],False)

    ax4 = fig.add_subplot(514)
    ax4 = plot_PlayType(regular,'yrdline100',['Run','Pass'],False)
    ax4.xaxis.set_ticks(range(4, 99, 5))
    ax4.xaxis.set_ticklabels(range(5,100,5))
    ax4.grid(True,'major','both')
    return fig

figure_4 = fig4()

###############################################################
# Figure 1 - HeatMap
def fig5():
    fig, axes = plt.subplots(2,1,figsize=(14,7))
    sns.kdeplot(ax=axes[0],data=regular[regular.PlayType == 'Pass'].TimeSecs,bw=20,label='Pass')
    sns.kdeplot(ax=axes[0],data=regular[regular.PlayType == 'Run'].TimeSecs,bw=20,label='Run')
    loc = plticker.MultipleLocator(base=120.0) # this locator puts ticks at regular intervals
    axes[0].xaxis.set_major_locator(loc)
    axes[0].set_xlim(0,3600)
    axes[0].set_ylim(0,0.00085)
    axes[0].vlines([x*60 for x in [15,30,45]],0,0.0009,colors='black')
    axes[0].grid(True,'major','y')
    axes[0].grid(False,'major','x')

    sns.kdeplot(ax=axes[1],data=playoffs[playoffs.PlayType == 'Pass'].TimeSecs,bw=20,label='Pass')
    sns.kdeplot(ax=axes[1],data=playoffs[playoffs.PlayType == 'Run'].TimeSecs,bw=20,label='Run')
    loc = plticker.MultipleLocator(base=120.0) # this locator puts ticks at regular intervals
    axes[1].xaxis.set_major_locator(loc)
    axes[1].set_xlim(0,3600)
    axes[1].set_ylim(0,0.00085)
    axes[1].vlines([x*60 for x in [15,30,45]],0,0.0009,colors='black')
    axes[1].grid(True,'major','y')
    axes[1].grid(False,'major','x')
    return fig

figure_5 = fig5()

#################################################################
# Figure 1 - HeatMap
def fig6():
    rs_fg = rs_pbp[rs_pbp.PlayType =='Field Goal'].groupby('FieldGoalResult').agg({'Date':len}).reset_index()
    rs_fg.columns=['FieldGoalResult', 'Count']
    rs_fg['Percent Total'] = rs_fg.Count.apply(lambda x: 100 * x / float(rs_fg.Count.sum()))

    po_fg = po_pbp[po_pbp.PlayType =='Field Goal'].groupby('FieldGoalResult').agg({'Date':len}).reset_index()
    po_fg.columns=['FieldGoalResult', 'Count']
    po_fg['Percent Total'] = po_fg.Count.apply(lambda x: 100 * x / float(po_fg.Count.sum()))

    sns.set_palette(['green', 'orange', 'red'])

    fig, axes = plt.subplots(2, 2,sharey=True,figsize=(14,7))
    order = ['Good','Blocked','No Good']

    sns.violinplot(ax=axes[0][0], data=rs_pbp[rs_pbp.PlayType=='Field Goal'], x='FieldGoalDistance', y='FieldGoalResult',order=order, scale='width', bw=0.05)
    sns.violinplot(ax=axes[1][0], data=po_pbp[po_pbp.PlayType=='Field Goal'], x='FieldGoalDistance', y='FieldGoalResult',order=order, scale='width', bw=0.05)
    axes[0][0].set_xlim(0,100)
    axes[1][0].set_xlim(0,100)

    sns.barplot(ax=axes[0][1], data=rs_fg,y='FieldGoalResult', x='Percent Total',order=order)
    sns.barplot(ax=axes[1][1], data=po_fg,y='FieldGoalResult', x='Percent Total',order=order)
    axes[0][1].set_xlim(0,100)
    axes[1][1].set_xlim(0,100)

    axes[0][1].set_xticklabels(['0%','20%','40%','60%','80%','100%'])
    axes[1][1].set_xticklabels(['0%','20%','40%','60%','80%','100%'])

    axes[0][0].set_title('Field Goal Results by Distance')
    axes[0][0].set_xlabel('')
    axes[0][0].set_ylabel('Regular Season')

    axes[0][1].set_title('Field Goal Results Distribution')
    axes[0][1].set_xlabel('')
    axes[0][1].set_ylabel('')

    axes[1][0].set_ylabel('Playoffs')
    axes[1][0].set_xlabel('Field Goal Distance (yds)')
    axes[1][0].figure

    axes[1][1].set_ylabel('')
    axes[1][1].set_xlabel('Percent Total')
    return fig

figure_6 = fig6()
#####################################################################
# Figure 1 - HeatMap
teams = [['ARI', 'Arizona', 'Cardinals', 'Arizona Cardinals'],
 ['ATL', 'Atlanta', 'Falcons', 'Atlanta Falcons'],
 ['BAL', 'Baltimore', 'Ravens', 'Baltimore Ravens'],
 ['BUF', 'Buffalo', 'Bills', 'Buffalo Bills'],
 ['CAR', 'Carolina', 'Panthers', 'Carolina Panthers'],
 ['CHI', 'Chicago', 'Bears', 'Chicago Bears'],
 ['CIN', 'Cincinnati', 'Bengals', 'Cincinnati Bengals'],
 ['CLE', 'Cleveland', 'Browns', 'Cleveland Browns'],
 ['DAL', 'Dallas', 'Cowboys', 'Dallas Cowboys'],
 ['DEN', 'Denver', 'Broncos', 'Denver Broncos'],
 ['DET', 'Detroit', 'Lions', 'Detroit Lions'],
 ['GB', 'Green Bay', 'Packers', 'Green Bay Packers', 'G.B.', 'GNB'],
 ['HOU', 'Houston', 'Texans', 'Houston Texans'],
 ['IND', 'Indianapolis', 'Colts', 'Indianapolis Colts'],
 ['JAC', 'Jacksonville', 'Jaguars', 'Jacksonville Jaguars', 'JAX'],
 ['KC', 'Kansas City', 'Chiefs', 'Kansas City Chiefs', 'K.C.', 'KAN'],
 ['LA', 'Los Angeles', 'Rams', 'Los Angeles Rams', 'L.A.'],
 ['MIA', 'Miami', 'Dolphins', 'Miami Dolphins'],
 ['MIN', 'Minnesota', 'Vikings', 'Minnesota Vikings'],
 ['NE', 'New England', 'Patriots', 'New England Patriots', 'N.E.', 'NWE'],
 ['NO', 'New Orleans', 'Saints', 'New Orleans Saints', 'N.O.', 'NOR'],
 ['NYG', 'Giants', 'New York Giants', 'N.Y.G.'],
 ['NYJ', 'Jets', 'New York Jets', 'N.Y.J.'],
 ['OAK', 'Oakland', 'Raiders', 'Oakland Raiders'],
 ['PHI', 'Philadelphia', 'Eagles', 'Philadelphia Eagles'],
 ['PIT', 'Pittsburgh', 'Steelers', 'Pittsburgh Steelers'],
 ['SD', 'San Diego', 'Chargers', 'San Diego Chargers', 'S.D.', 'SDG'],
 ['SEA', 'Seattle', 'Seahawks', 'Seattle Seahawks'],
 ['SF', 'San Francisco', '49ers', 'San Francisco 49ers', 'S.F.', 'SFO'],
 ['STL', 'St. Louis', 'Rams', 'St. Louis Rams', 'S.T.L.'],
 ['TB', 'Tampa Bay', 'Buccaneers', 'Tampa Bay Buccaneers', 'T.B.', 'TAM'],
 ['TEN', 'Tennessee', 'Titans', 'Tennessee Titans'],
 ['WAS', 'Washington', 'Redskins', 'Washington Redskins', 'WSH']]

teams_dict = {x[3]:x[0] for x in teams}
 # Jacksonville Data Fix
rs_pbp.posteam = rs_pbp.posteam.replace('JAX', 'JAC')
rs_pbp.HomeTeam = rs_pbp.HomeTeam.replace('JAX', 'JAC')
rs_pbp.AwayTeam = rs_pbp.AwayTeam.replace('JAX', 'JAC')

pass_rush_attempts_by_team = rs_pbp.groupby(['posteam','Season']).agg(sum)[['PassAttempt','RushAttempt']]
pass_rush_attempts_by_team['PassRushRatio'] = pass_rush_attempts_by_team.apply(lambda x: (x.PassAttempt * 1.0) / x.RushAttempt, axis=1)

sns.set_palette('muted')
plot_df = pass_rush_attempts_by_team
plot_teams = teams_dict

def plotPassRushByTeam(team_focus_1, team_focus_2):
    fig,ax = plt.subplots(1,1,figsize=(15,8))
    for team in plot_teams:
        if (plot_teams[team] != team_focus_1) or (plot_teams[team] != team_focus_1):
            plt.plot(plot_df.loc[plot_teams[team]]['PassRushRatio'], color='0.91')
    plt.plot(plot_df.loc[team_focus_1]['PassRushRatio'], color='Blue', axes=ax)
    plt.plot(plot_df.loc[team_focus_2]['PassRushRatio'], color='Red', axes=ax)
    return fig

def fig7():
    sns.set_style('white')
    return plotPassRushByTeam(team_focus_1 = 'NYG', team_focus_2 = 'NYJ')

figure_7 = fig7()

##########################################################
# Figure 1 - HeatMap

playoff_teams = {year:po_pbp.mask('Season',year).posteam.dropna().unique().tolist() for year in np.arange(2009,2017,1)}

def madeit(row):
    team, season = row.name
    return int(team in playoff_teams[season])

next_df = pass_rush_attempts_by_team.copy()

next_df['PO'] = next_df.apply(madeit, axis=1)

next_df.reset_index().groupby(['posteam','PO']).agg({'PassRushRatio':np.mean}).reset_index().pivot('posteam','PO','PassRushRatio')

def fig8():
    sns.set_context('talk')
    #sns.heatmap(data = pass_rush_attempts_by_team.reset_index().pivot('posteam','PO','PassRushRatio'),
    #            vmin=0,vmax=1,square=False,cmap='rainbow', annot=False)
    fig,ax = plt.subplots(1,1)
    new_df = next_df.reset_index().groupby(['posteam','PO']).agg({'PassRushRatio':np.mean}).reset_index().pivot('posteam','PO','PassRushRatio')
    sns.heatmap(data = new_df, square=False, annot=False, cmap='Greens')
    return fig

figure_8 = fig8()
############################################################

def fig9():
    fig,ax = plt.subplots(1,1)
    pass_rush_attempts_by_team.loc['DEN']['PassRushRatio'].plot()
    return fig

figure_9 = fig9()

#############################################################

def fig10():
    fig, ax = plt.subplots(1,1,figsize=(3,5))
    sns.boxplot(data=next_df.reset_index(),x='PO', y='PassRushRatio', ax=ax)
    return fig

figure_10 = fig10()
#############################################################

avg_prr_by_team = pass_rush_attempts_by_team.reset_index().groupby('posteam').agg({'PassRushRatio':np.mean}).sort_values('PassRushRatio')
avg_prr_by_season = pass_rush_attempts_by_team.reset_index().groupby('Season').agg({'PassRushRatio':np.mean}).sort_values('PassRushRatio')

def fig11():
    with sns.axes_style('ticks'):
        fig,ax = plt.subplots(1,1,figsize=(20,7))
        sns.boxplot(data=next_df.reset_index(),x='posteam', y='PassRushRatio', ax=ax, order=avg_prr_by_team.index.tolist(),hue='PO')
    return fig

figure_11 = fig11()


"""
StanhopeFramers Tables
"""
import io
import subprocess

import pandas
from stanhope import utils

pandas.set_option('display.max_rows', 999)
pandas.set_option('display.width', 999)
pandas.set_option('display.max_colwidth', 999)

class Table(object):
    def __init__(self, *tables):
        self.tables = tables or (type(self).__name__,)
        self.frame = None

    @staticmethod
    def read(table):
        cmd = ['mdb-export', '/data/StanhopeFramers.mdb', table]
        out = subprocess.check_output(cmd)
        return io.BytesIO(out)

    def load(self):
        frame = pandas.DataFrame()
        for table in self.tables:
            page = pandas.read_csv(self.read(table), **self.READ_CSV)
            # page['Table'] = table
            frame = frame.append(page)
        self.frame = frame
        return frame

class Customers(Table):
    READ_CSV = {
        'converters': {
            'Customer Number': utils.upper,
            'Credit': utils.boolean,
            'Tax Exempt': utils.boolean,
            'Deceased': utils.boolean},
        'parse_dates': [
            'Date',
            'Last Order',
            'Last Update']}

    def accounts(self):
        frame = self.frame.copy()

        # Add Legacy ID
        frame['Legacy ID'] = \
            frame[['Customer Number']].apply(utils.legacy_id, axis=1)

        # Copy legacy record
        frame['Legacy Record'] = frame.drop('Legacy ID', axis=1)\
                                      .apply(utils.legacy_record, axis=1)

        # Drop unused columns
        frame.drop(['Address',
                    'City',
                    'Date',
                    'Deceased',
                    'Email',
                    'Last Order',
                    'Last Update',
                    'State',
                    'Telephone',
                    'Zip'],
                   axis=1,
                   inplace=True)

        # Rename columns
        frame.rename(inplace=True,
                     columns={'Customer Number': 'Legacy Customer Number',
                              'Name': 'Account',
                              'Comment': 'Comments'})

        # Set account name
        frame.loc[:, 'Account'] = \
            frame['Account'].apply(utils.replace_newline)\
                            .combine_first(frame['Legacy Customer Number'])
        frame['Primary Contact'] = frame['Account']

        # Massage fields
        frame.loc[~frame['Category'].isnull(), 'Category'] = \
            frame.loc[~frame['Category'].isnull(), 'Category'].apply(
                utils.account_category)
        frame.loc[~frame['Source'].isnull(), 'Source'] = \
            frame.loc[~frame['Source'].isnull(), 'Source'].apply(utils.source)
        frame.loc[:, 'Comments'] = \
            frame['Comments'].apply(lambda x: utils.replace_newline(x, '\n'))

        # Return
        return frame

    def contacts(self):
        frame = self.frame.copy()

        # Add Legacy ID
        frame['Legacy ID'] = \
            frame[['Customer Number']].apply(utils.legacy_id, axis=1)

        # Drop unused columns
        frame.drop(['Category',
                    'Comment',
                    'Credit',
                    'Date',
                    'Last Order',
                    'Last Update',
                    'Source',
                    'Tax Exempt'],
                   axis=1,
                   inplace=True)

        # Rename columns
        frame.rename(inplace=True,
                     columns={'Customer Number': 'Account Link',
                              'Name': 'Contact',
                              'Deceased': 'Removed'})

        # Massage fields
        frame.loc[:, 'Contact'] = \
            frame['Contact'].apply(utils.replace_newline)\
                            .combine_first(frame['Account Link'])
        frame.loc[:, 'Contact'] = frame['Contact'].apply(utils.replace_newline)
        frame.loc[:, 'Address'] = frame['Address'].apply(utils.replace_newline)
        frame.loc[:, 'City'] = frame['City'].apply(utils.replace_newline)
        frame.loc[:, 'State'] = frame['State'].apply(utils.replace_newline)
        frame.loc[:, 'Zip'] = frame['Zip'].apply(utils.replace_newline)
        frame.loc[:, 'Telephone'] = \
            frame['Telephone'].apply(utils.replace_newline)

        # Return
        return frame

class FrameOrders(Table):
    READ_CSV = {
        'converters': {'CustomerNo': utils.upper, 'OrderNo': utils.upper},
        'parse_dates': ['DateCompleted', 'DueDate', 'OrderDate']}

    def orders(self):
        frame = self.frame.copy()

        # Add Legacy ID
        frame['Legacy ID'] = \
            frame[['CustomerNo', 'OrderNo', 'OrderDate']]\
            .apply(utils.legacy_id, axis=1)

        # Copy legacy record
        frame['Legacy Record'] = frame.drop('Legacy ID', axis=1)\
                                      .apply(utils.legacy_record, axis=1)

        # Set status
        frame.loc[frame['SalesType'] == 'VOID', 'Status'] = 'V'

        # Drop unused columns
        frame.drop(['Artist',
                    'BinNo',
                    'Comments',
                    'DateCompleted',
                    'Fitting',
                    'Frame Height',
                    'Frame Width',
                    'FrameMfg',
                    'FrameNo',
                    'Glazing',
                    'Joining',
                    'Mat',
                    'MatColor',
                    'MatMfg',
                    'Matting',
                    'MattingSize',
                    'ProductionComments',
                    'Qty',
                    'SalesCatgy',
                    'SalesType',
                    'TotalSale'],
                   axis=1,
                   inplace=True)

        # Rename columns
        frame.rename(inplace=True,
                     columns={'OrderNo': 'Order Number',
                              'OrderDate': 'Order Date',
                              'DueDate': 'Due Date',
                              'CustomerNo': 'Account Link',
                              'Status': 'Order Status',
                              'Location': 'Order Location',
                              'SalesPers': 'Salesperson Link',
                              'Delivery': 'Delivery Location',
                              'Cust-Client': 'Client'})

        # Massage fields
        frame.loc[:, 'Delivery Location'] = \
            frame['Delivery Location'].apply(utils.delivery_location)
        frame.loc[:, 'Discount'] = frame['Discount'].apply(utils.discount)
        frame.loc[:, 'Order Location'] = \
            frame['Order Location'].apply(utils.order_location)
        frame.loc[:, 'Order Status'] = \
            frame['Order Status'].apply(utils.status)
        frame.loc[:, 'Salesperson Link'] = \
            frame['Salesperson Link'].apply(utils.salesperson)
        frame.loc[:, 'Delivery Location'] = \
            frame['Delivery Location'].combine_first(frame['Order Location'])

        # Return
        return frame

    def treatments(self):
        frame = self.frame.copy()

        # Add Legacy ID
        frame['Legacy ID'] = \
            frame[['CustomerNo', 'OrderNo', 'OrderDate']]\
            .apply(utils.legacy_id, axis=1)

        # Add Legacy Order ID
        frame['Order Link'] = frame['Legacy ID']

        # Drop unused columns
        frame.drop(['Cust-Client',
                    'CustomerNo',
                    'DateCompleted',
                    'Delivery',
                    'Discount',
                    'DueDate',
                    'Fitting',
                    'Location',
                    'Matting',
                    'OrderDate',
                    'OrderNo',
                    'SalesCatgy',
                    'SalesPers',
                    'Status'],
                   axis=1,
                   inplace=True)

        # Rename fields
        frame.rename(inplace=True,
                     columns={'BinNo': 'Bin Number',
                              'Comments': 'Description',
                              'FrameNo': 'Frame Style',
                              'FrameMfg': 'Frame Manufacturer',
                              'Joining': 'Frame Join',
                              'Mat': 'Matting / Mounting',
                              'MatColor': 'Mat Color',
                              'MatMfg': 'Mat Manufacturer',
                              'MattingSize': 'Mat Size',
                              'ProductionComments': 'Production Comments',
                              'Qty': 'Quantity',
                              'SalesType': 'Treatment',
                              'TotalSale': 'Price'})

        # Massage fields
        frame.loc[:, 'Frame Join'] = frame['Frame Join'].apply(utils.join)
        frame.loc[:, 'Mat Manufacturer'] = \
            frame['Mat Manufacturer'].apply(utils.matmfg)
        frame.loc[:, 'Frame Manufacturer'] = \
            frame['Frame Manufacturer'].apply(utils.framemfg)
        frame.loc[:, 'Matting / Mounting'] = \
            frame['Matting / Mounting'].apply(utils.mat)
        frame.loc[:, 'Glazing'] = \
            frame['Glazing'].apply(utils.glazing)
        frame.loc[:, 'Treatment'] = frame['Treatment'].apply(utils.sales_type)

        # Add dimensions
        frame['Frame Width Inches'] = \
            frame['Frame Width'].apply(utils.inches)
        frame['Frame Width Fraction'] = \
            frame['Frame Width'].apply(utils.fraction)
        frame['Frame Height Inches'] = \
            frame['Frame Height'].apply(utils.inches)
        frame['Frame Height Fraction'] = \
            frame['Frame Height'].apply(utils.fraction)
        frame.drop(['Frame Width', 'Frame Height'], axis=1, inplace=True)

        # Return
        return frame


# -*- coding: utf-8 -*-
"""
Created on Fri May  2 13:49:11 2014

@author: mireland

A script for testing...  Change this to try out your own analysis.
"""

import astropy.io.fits as pyfits
import numpy as np
import matplotlib.pyplot as plt
from azimuthalAverage import *
# This includes an AO Instrument called "aoinst"
import pypoise
import nirc2
import glob
import pdb

#Create a pypoise instance with a nirc2 AO instrument
pp = pypoise.PYPOISE(nirc2.NIRC2())
plt.ion()

#Reduction Directory - Lp full pupil
pp.aoinst.rdir = '/Users/mireland/tel/nirc2/redux/generic2015/'
pp.aoinst.cdir = '/Users/mireland/tel/nirc2/redux/TauPAH15/'
#Data directory
pp.aoinst.ddir =  '/Users/mireland/data/nirc2/151128/'

pp.aoinst.read_summary_csv()

if(False):
  pp.process_block(fstart='n1251.fits',fend='n1293.fits', dither=True)

if(False):
  pp.process_block(fstart='n1493.fits',fend='n1517.fits', dither=True)

targname = 'AB Aur'
targname = 'SU Aur'
targname = 'RY Tau'

if(True):
 #The argument "target_file" is just there to determine which object is the target.
 summary_files = pp.poise_process(target=targname, use_powerspect=False)
 print(summary_files)

if(True):
# summary_files = glob.glob('*LkCa*poise_cube*.fits')
 implane_file = pp.aoinst.cdir + targname + '_implane.fits'
 pxscale = 5.0

#pdb.set_trace()

if (True):
 kp_implane = pp.kp_to_implane(summary_files=summary_files,
 	out_file=implane_file, sz=141, pxscale=pxscale, use_powerspect=False)

if (True):
 #Automatic from here...
 pgrid, crat, crat_sig, chi2, best_rchi2 = pp.implane_fit_binary(implane_file, maxrad=250)
 print "Grid Fit: ", pgrid
 pgrid = np.array(pgrid)
 if (pgrid[2] > 0.5):
    print "Contrast too high to use kerphase for fitting (i.e. near-equal binary)."
 else:
    p,errs,cov = pp.kp_binary_fit(summary_files,pgrid)
    fitfile = open(targname + '_binaryfit.txt','w')
    fitfile.write('Separation (mas) & Position angle (degs) & Contrast \\\\\n')
    fitfile.write('{0:5.2f} $\pm$ {1:5.2f} & {2:5.2f} $\pm$ {3:5.2f} & {4:6.4f} $\pm$ {5:6.4f} \\\\ \n'.format(\
            p[0],errs[0], p[1],errs[1], p[2],errs[2]))
    fitfile.write('Contrast (mags) & Separation (mas) & Position angle (degs) \\\\\n')
    fit_crat = -2.5*np.log10(p[2])
    fit_crat_sig = 2.5/np.log(10)*errs[2]/p[2]
    fitfile.write('{0:5.2f} $\pm$ {1:5.2f} & {2:5.2f} $\pm$ {3:5.2f} & {4:5.3f} $\pm$ {5:5.3f} \\\\ \n'.format(\
            fit_crat, fit_crat_sig, p[0],errs[0], p[1],errs[1] ))
    fitfile.close()
 a = azimuthalAverage(crat_sig*np.sqrt(best_rchi2), returnradii=True,binsize=1)
 sep_null = a[0]*pxscale
 contrast_null = -2.5*np.log10(5*a[1])
 plt.clf()
 plt.plot(sep_null, contrast_null)
 plt.title(targname)
 plt.xlabel("Separation (milli-arcsec)")
 plt.ylabel("5-sigma contrast (mags)")
 sep_out = np.arange(20,301,10)
 contrast_out = np.interp(sep_out, sep_null, contrast_null)
 for i in range(len(sep_out)):
  print '{0:4d} {1:5.1f}'.format(int(sep_out[i]),contrast_out[i])
 plt.axis((0,300,2,7))
 plt.savefig(pp.aoinst.cdir + targname + '_contrast_curve.png')


# -*- coding: utf-8 -*-
"""
Nearest Centroid Classification
"""

# Author: Robert Layton <robertlayton@gmail.com>
#         Olivier Grisel <olivier.grisel@ensta.org>
#
# License: BSD 3 clause

import warnings
import numpy as np
from scipy import sparse as sp

from ..base import BaseEstimator, ClassifierMixin
from ..metrics.pairwise import pairwise_distances
from ..preprocessing import LabelEncoder
from ..utils.validation import check_array, check_X_y, check_is_fitted
from ..utils.sparsefuncs import csc_median_axis_0

class NearestCentroid(BaseEstimator, ClassifierMixin):
    """Nearest centroid classifier.

    Each class is represented by its centroid, with test samples classified to
    the class with the nearest centroid.

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

    Parameters
    ----------
    metric: string, or callable
        The metric to use when calculating distance between instances in a
        feature array. If metric is a string or callable, it must be one of
        the options allowed by metrics.pairwise.pairwise_distances for its
        metric parameter.
        The centroids for the samples corresponding to each class is the point
        from which the sum of the distances (according to the metric) of all
        samples that belong to that particular class are minimized.
        If the "manhattan" metric is provided, this centroid is the median and
        for all other metrics, the centroid is now set to be the mean.

    shrink_threshold : float, optional (default = None)
        Threshold for shrinking centroids to remove features.

    Attributes
    ----------
    centroids_ : array-like, shape = [n_classes, n_features]
        Centroid of each class

    Examples
    --------
    >>> from sklearn.neighbors.nearest_centroid import NearestCentroid
    >>> 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])
    >>> clf = NearestCentroid()
    >>> clf.fit(X, y)
    NearestCentroid(metric='euclidean', shrink_threshold=None)
    >>> print(clf.predict([[-0.8, -1]]))
    [1]

    See also
    --------
    sklearn.neighbors.KNeighborsClassifier: nearest neighbors classifier

    Notes
    -----
    When used for text classification with tf-idf vectors, this classifier is
    also known as the Rocchio classifier.

    References
    ----------
    Tibshirani, R., Hastie, T., Narasimhan, B., & Chu, G. (2002). Diagnosis of
    multiple cancer types by shrunken centroids of gene expression. Proceedings
    of the National Academy of Sciences of the United States of America,
    99(10), 6567-6572. The National Academy of Sciences.

    """

    def __init__(self, metric='euclidean', shrink_threshold=None):
        self.metric = metric
        self.shrink_threshold = shrink_threshold

    def fit(self, X, y):
        """
        Fit the NearestCentroid model according to the given training data.

        Parameters
        ----------
        X : {array-like, sparse matrix}, shape = [n_samples, n_features]
            Training vector, where n_samples in the number of samples and
            n_features is the number of features.
            Note that centroid shrinking cannot be used with sparse matrices.
        y : array, shape = [n_samples]
            Target values (integers)
        """
        # If X is sparse and the metric is "manhattan", store it in a csc
        # format is easier to calculate the median.
        if self.metric == 'manhattan':
            X, y = check_X_y(X, y, ['csc'])
        else:
            X, y = check_X_y(X, y, ['csr', 'csc'])
        is_X_sparse = sp.issparse(X)
        if is_X_sparse and self.shrink_threshold:
            raise ValueError("threshold shrinking not supported"
                             " for sparse input")

        n_samples, n_features = X.shape
        le = LabelEncoder()
        y_ind = le.fit_transform(y)
        self.classes_ = classes = le.classes_
        n_classes = classes.size
        if n_classes < 2:
            raise ValueError('y has less than 2 classes')

        # Mask mapping each class to it's members.
        self.centroids_ = np.empty((n_classes, n_features), dtype=np.float64)
        # Number of clusters in each class.
        nk = np.zeros(n_classes)

        for cur_class in range(n_classes):
            center_mask = y_ind == cur_class
            nk[cur_class] = np.sum(center_mask)
            if is_X_sparse:
                center_mask = np.where(center_mask)[0]

            # XXX: Update other averaging methods according to the metrics.
            if self.metric == "manhattan":
                # NumPy does not calculate median of sparse matrices.
                if not is_X_sparse:
                    self.centroids_[cur_class] = np.median(X[center_mask], axis=0)
                else:
                    self.centroids_[cur_class] = csc_median_axis_0(X[center_mask])
            else:
                if self.metric != 'euclidean':
                    warnings.warn("Averaging for metrics other than "
                                  "euclidean and manhattan not supported. "
                                  "The average is set to be the mean."
                                  )
                self.centroids_[cur_class] = X[center_mask].mean(axis=0)

        if self.shrink_threshold:
            dataset_centroid_ = np.mean(X, axis=0)

            # m parameter for determining deviation
            m = np.sqrt((1. / nk) + (1. / n_samples))
            # Calculate deviation using the standard deviation of centroids.
            variance = (X - self.centroids_[y_ind]) ** 2
            variance = variance.sum(axis=0)
            s = np.sqrt(variance / (n_samples - n_classes))
            s += np.median(s)  # To deter outliers from affecting the results.
            mm = m.reshape(len(m), 1)  # Reshape to allow broadcasting.
            ms = mm * s
            deviation = ((self.centroids_ - dataset_centroid_) / ms)
            # Soft thresholding: if the deviation crosses 0 during shrinking,
            # it becomes zero.
            signs = np.sign(deviation)
            deviation = (np.abs(deviation) - self.shrink_threshold)
            deviation[deviation < 0] = 0
            deviation *= signs
            # Now adjust the centroids using the deviation
            msd = ms * deviation
            self.centroids_ = dataset_centroid_[np.newaxis, :] + msd
        return self

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

        The predicted class C for each sample in X is returned.

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

        Returns
        -------
        C : array, shape = [n_samples]

        Notes
        -----
        If the metric constructor parameter is "precomputed", X is assumed to
        be the distance matrix between the data to be predicted and
        ``self.centroids_``.
        """
        check_is_fitted(self, 'centroids_')

        X = check_array(X, accept_sparse='csr')
        return self.classes_[pairwise_distances(
            X, self.centroids_, metric=self.metric).argmin(axis=1)]