"""
Set up the plot figures, axes, and items to be done for each frame.

This module is imported by the plotting routines and then the
function setplot is called to set the plot parameters.

"""

import numpy as np

# Plot customization
import matplotlib

# Markers and line widths
matplotlib.rcParams['lines.linewidth'] = 2.0
matplotlib.rcParams['lines.markersize'] = 6
matplotlib.rcParams['lines.markersize'] = 8

# Font Sizes
matplotlib.rcParams['font.size'] = 16
matplotlib.rcParams['axes.labelsize'] = 15
matplotlib.rcParams['legend.fontsize'] = 12
matplotlib.rcParams['xtick.labelsize'] = 12
matplotlib.rcParams['ytick.labelsize'] = 12

# DPI of output images
matplotlib.rcParams['savefig.dpi'] = 300

# Need to do this after the above
import matplotlib.pyplot as mpl

from clawpack.pyclaw.solution import Solution

from multilayer.aux import bathy_index,kappa_index,wind_index
import multilayer.plot as plot

# matplotlib.rcParams['figure.figsize'] = [6.0,10.0]

def setplot(plotdata,eta=[0.0,-300.0],rho=[1025.0,1045.0],g=9.81,dry_tolerance=1e-3,bathy_ref_lines=[-30e3]):
    """
    Specify what is to be plotted at each frame.
    Input:  plotdata, an instance of pyclaw.plotters.data.ClawPlotData.
    Output: a modified version of plotdata.

    """

    # Fetch bathymetry once
    b = Solution(0,path=plotdata.outdir,read_aux=True).state.aux[bathy_index,:]

    # ========================================================================
    #  Plot variable functions
    def bathy(cd):
        return b

    def kappa(cd):
        return Solution(cd.frameno,path=plotdata.outdir,read_aux=True).state.aux[kappa_index,:]

    def wind(cd):
        return Solution(cd.frameno,path=plotdata.outdir,read_aux=True).state.aux[wind_index,:]

    def h_1(cd):
        return cd.q[0,:] / rho[0]

    def h_2(cd):
        return cd.q[2,:] / rho[1]

    def eta_2(cd):
        return h_2(cd) + bathy(cd)

    def eta_1(cd):
        return h_1(cd) + eta_2(cd)

    def u_1(cd):
        index = np.nonzero(h_1(cd) > dry_tolerance)
        u_1 = np.zeros(h_1(cd).shape)
        u_1[index] = cd.q[1,index] / cd.q[0,index]
        return u_1

    def u_2(cd):
        index = np.nonzero(h_2(cd) > dry_tolerance)
        u_2 = np.zeros(h_2(cd).shape)
        u_2[index] = cd.q[3,index] / cd.q[2,index]
        return u_2

    def hu_1(cd):
        index = np.nonzero(h_1(cd) > dry_tolerance)
        hu_1 = np.zeros(h_1(cd).shape)
        hu_1[index] = cd.q[1,index] / rho[0]
        return hu_1

    def hu_2(cd):
        index = np.nonzero(h_2(cd) > dry_tolerance)
        hu_2 = np.zeros(h_2(cd).shape)
        hu_2[index] = cd.q[3,index] / rho[1]
        return hu_2

    # ========================================================================
    #  Labels
    def add_bathy_dashes(current_data):
        for ref_line in bathy_ref_lines:
            mpl.plot([ref_line,ref_line],[-10,10],'k--')

    def add_horizontal_dashes(current_data):
        mpl.plot([-400e3,0.0],[0.0,0.0],'k--')

    def km_labels(current_data):
        r"""Flips xaxis and labels with km"""
        mpl.xlabel('km')
        locs,labels = mpl.xticks()
        labels = np.flipud(locs)/1.e3
        mpl.xticks(locs,labels)

    def time_labels(current_data):
        r"""Convert time to hours"""
        pass

    # ========================================================================
    # Limit Settings
    xlimits = [-400e3,0.0]
    ylimits_depth = [-4000.0,100.0]
    xlimits_zoomed = [-30e3-1e3,-30e3+1e3]
    ylimits_surface_zoomed = [eta[0] - 0.5,eta[0] + 0.5]
    ylimits_internal_zoomed = [eta[1] - 2.5,eta[1] + 2.5]
    ylimits_momentum = [-40,10]
    # ylimits_velocities = [-1.0,1.0]
    ylimits_velocities = [-0.04,0.04]
    ylimits_kappa = [0.0,1.2]

    # Create data object
    plotdata.clearfigures()  # clear any old figures,axes,items data

    # ========================================================================
    #  Function for doing depth drawing
    # ========================================================================
    def fill_items(plotaxes):
        # Top layer
        plotitem = plotaxes.new_plotitem(plot_type='1d_fill_between')
        plotitem.plot_var = eta_1
        plotitem.plot_var2 = eta_2
        plotitem.color = plot.top_color
        plotitem.plotstyle = plot.surface_linestyle
        plotitem.show = True

        # Bottom Layer
        plotitem = plotaxes.new_plotitem(plot_type='1d_fill_between')
        plotitem.plot_var = eta_2
        plotitem.plot_var2 = bathy
        plotitem.color = plot.bottom_color
        plotitem.plotstyle = plot.internal_linestyle
        plotitem.show = True

        # Plot bathy
        plotitem = plotaxes.new_plotitem(plot_type='1d_plot')
        plotitem.plot_var = bathy
        plotitem.plotstyle = plot.bathy_linestyle
        plotitem.show = True

        # Plot line in between layers
        plotitem = plotaxes.new_plotitem(plot_type='1d_plot')
        plotitem.plot_var = eta_2
        plotitem.color = 'k'
        plotitem.plotstyle = plot.internal_linestyle
        plotitem.show = True

        # Plot line on top layer
        plotitem = plotaxes.new_plotitem(plot_type='1d_plot')
        plotitem.plot_var = eta_1
        plotitem.color = 'k'
        plotitem.plotstyle = plot.surface_linestyle
        plotitem.show = True

    # ========================================================================
    #  Full Depths
    # ========================================================================
    plotfigure = plotdata.new_plotfigure(name='Full Depths',figno=102)
    plotfigure.show = True

    def bathy_axes(cd):
        km_labels(cd)
        mpl.xticks([-300e3,-200e3,-100e3,-30e3],[300,200,100,30],fontsize=15)
        mpl.xlabel('km')

    plotaxes = plotfigure.new_plotaxes()
    plotaxes.title = 'Full Depths'
    plotaxes.xlimits = xlimits
    plotaxes.ylimits = [-4100,100]
    plotaxes.afteraxes = bathy_axes

    fill_items(plotaxes)

    # ========================================================================
    #  Momentum
    # ========================================================================
    plotfigure = plotdata.new_plotfigure(name="momentum")
    plotfigure.show = True

    def momentum_axes(cd):
        km_labels(cd)
        mpl.xticks([-300e3,-200e3,-100e3,-30e3],[300,200,100,30],fontsize=15)
        mpl.xlabel('km')
        mpl.title("Layer Momenta at t = %4.1f s" % cd.t)

        mpl.legend(['Top Layer Momentum','Bottom Layer Momentum'],loc=4)

    def inset_momentum_axes(cd):

        # TODO: This plot does not refresh correctly, skip the inset
        fig = mpl.figure(cd.plotfigure.figno)
        axes = fig.add_subplot(111)

        # Plot main figure
        axes.plot(cd.x, hu_1(cd), 'b-')
        axes.plot(cd.x, hu_2(cd), 'k--')
        axes.set_xlim(xlimits)
        axes.set_ylim(ylimits_momentum)
        momentum_axes(cd)

        # Create inset plot
        from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
        from mpl_toolkits.axes_grid1.inset_locator import mark_inset
        inset_axes = zoomed_inset_axes(axes, 0.5, loc=3)
        inset_axes.plot(cd.x, hu_1(cd), 'b-')
        inset_axes.plot(cd.x, hu_2(cd), 'k--')
        inset_axes.set_xticklabels([])
        inset_axes.set_yticklabels([])
        x_zoom = [-120e3,-30e3]
        y_zoom = [-10,10]
        inset_axes.set_xlim(x_zoom)
        inset_axes.set_ylim(y_zoom)
        mark_inset(axes, inset_axes, loc1=2, loc2=4, fc='none', ec="0.5")
        # mpl.ion()
        mpl.draw()
        # mpl.show()

    plotaxes = plotfigure.new_plotaxes()
    plotaxes.title = "Momentum"
    plotaxes.xlimits = xlimits
    plotaxes.ylimits = ylimits_momentum
    # plotaxes.afteraxes = inset_momentum_axes

    # Top layer
    plotitem = plotaxes.new_plotitem(plot_type='1d')
    plotitem.plot_var = hu_1
    plotitem.plotstyle = 'b-'
    plotitem.show = True

    # Bottom layer
    plotitem = plotaxes.new_plotitem(plot_type='1d')
    plotitem.plot_var = hu_2
    plotitem.plotstyle = 'k--'
    plotitem.show = True

    # ========================================================================
    #  Velocities with Kappa
    # ========================================================================
    include_kappa = False
    if include_kappa:
        plotfigure = plotdata.new_plotfigure(name='Velocity and Kappa',figno=14)
    else:
        plotfigure = plotdata.new_plotfigure(name='Velocities',figno=14)
    plotfigure.show = True
    # plotfigure.kwargs = {'figsize':(7,6)}

    def twin_axes(cd):
        fig = mpl.gcf()
        fig.clf()

        # Get x coordinate values
        x = cd.patch.dimensions[0].centers

        # Draw velocity and kappa plot
        vel_axes = fig.add_subplot(111)     # the velocity scale
        # kappa_axes = vel_axes.twinx()              # the kappa scale

        # Bottom layer velocity
        bottom_layer = vel_axes.plot(x,u_2(cd),'k-',label="Bottom Layer Velocity")
        # Top Layer velocity
        top_layer = vel_axes.plot(x,u_1(cd),'b--',label="Top Layer velocity")

        if include_kappa:
            # Kappa
            kappa_line = kappa_axes.plot(x,kappa(cd),'r-.',label="Kappa")
            kappa_axes.plot(x,np.ones(x.shape),'r:')

        vel_axes.set_xlabel('km')
        mpl.xticks([-300e3,-200e3,-100e3,-30e3],[300,200,100,30],fontsize=15)

        for ref_line in bathy_ref_lines:
            vel_axes.plot([ref_line,ref_line],ylimits_velocities,'k:')
        if include_kappa:
            vel_axes.set_title("Layer Velocities and Kappa at t = %4.1f s" % cd.t)
        else:
            vel_axes.set_title("Layer Velocities at t = %4.1f s" % cd.t)
        vel_axes.set_ylabel('Velocities (m/s)')
        vel_axes.set_xlim(xlimits)
        vel_axes.set_ylim(ylimits_velocities)

        if include_kappa:
            plot.add_legend(vel_axes,'Kappa',location=3,color='r',linestyle='-.')
            kappa_axes.set_ylabel('Kappa')
            kappa_axes.set_ylim(ylimits_kappa)
        else:
            vel_axes.legend(loc=3)
        try:
            mpl.subplots_adjust(hspace=0.1)
        except:
            pass

    plotaxes = plotfigure.new_plotaxes()
    plotaxes.afteraxes = twin_axes

    # ========================================================================
    #  Combined Top and Internal Surface
    # ========================================================================
    plotfigure = plotdata.new_plotfigure(name='Zoomed Depths',figno=13)
    plotfigure.show = True
    plotfigure.kwargs = {'figsize':(6,6)}

    # Top surface
    plotaxes = plotfigure.new_plotaxes()
    plotaxes.axescmd = 'subplot(2,1,1)'
    plotaxes.title = 'Surfaces'
    plotaxes.xlimits = xlimits
    plotaxes.ylimits = ylimits_surface_zoomed
    def top_afteraxes(cd):
        mpl.xlabel('')
        locs,labels = mpl.xticks()
        # labels = np.flipud(locs)/1.e3
        labels = ['' for i in range(len(locs))]
        mpl.xticks(locs,labels)
        add_bathy_dashes(cd)
        mpl.ylabel('m')
        mpl.title("Surfaces t = %4.1f s" % cd.t)
    plotaxes.afteraxes = top_afteraxes
    plotaxes = fill_items(plotaxes)

    # Internal surface
    plotaxes = plotfigure.new_plotaxes()
    plotaxes.axescmd = 'subplot(2,1,2)'
    plotaxes.title = ''
    plotaxes.xlimits = xlimits
    plotaxes.ylimits = ylimits_internal_zoomed
    def internal_surf_afteraxes(cd):
        km_labels(cd)
        mpl.title('')
        mpl.ylabel('m')
        mpl.subplots_adjust(hspace=0.05)
        mpl.xticks([-300e3,-200e3,-100e3,-30e3],[300,200,100,30],fontsize=15)
        mpl.xlabel('km')
    plotaxes.afteraxes = internal_surf_afteraxes
    plotaxes = fill_items(plotaxes)

    # Parameters used only when creating html and/or latex hardcopy
    # e.g., via pyclaw.plotters.frametools.printframes:

    plotdata.printfigs = True                # print figures
    plotdata.print_format = 'png'            # file format
    # plotdata.print_framenos = 'all'          # list of frames to print
    plotdata.print_framenos = [0,30,100,200,300]
    plotdata.print_fignos = 'all'            # list of figures to print
    plotdata.html = True                     # create html files of plots?
    plotdata.html_homelink = '../README.html'   # pointer for top of index
    plotdata.latex = True                    # create latex file of plots?
    plotdata.latex_figsperline = 2           # layout of plots
    plotdata.latex_framesperline = 1         # layout of plots
    plotdata.latex_makepdf = False           # also run pdflatex?

    return plotdata


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

"""
***************************************************************************
    EquivalentNumField.py
    ---------------------
    Date                 : January 2013
    Copyright            : (C) 2013 by Victor Olaya
    Email                : volayaf at gmail dot com
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
"""

__author__ = 'Victor Olaya'
__date__ = 'January 2013'
__copyright__ = '(C) 2013, Victor Olaya'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'

import matplotlib.pyplot as plt
import matplotlib.pylab as lab
from PyQt4.QtCore import *
from qgis.core import *
from processing.parameters.ParameterVector import ParameterVector
from processing.parameters.ParameterTableField import ParameterTableField
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.outputs.OutputHTML import OutputHTML
from processing.tools import *
from processing.tools import dataobjects
from processing.parameters.ParameterNumber import ParameterNumber

class VectorLayerHistogram(GeoAlgorithm):

    INPUT = "INPUT"
    OUTPUT = "OUTPUT"
    FIELD = "FIELD"
    BINS = "BINS"

    def processAlgorithm(self, progress):
        uri = self.getParameterValue(self.INPUT)
        layer = dataobjects.getObjectFromUri(uri)
        fieldname = self.getParameterValue(self.FIELD)
        output = self.getOutputValue(self.OUTPUT)
        values = vector.getAttributeValues(layer, fieldname)
        plt.close()
        bins = self.getParameterValue(self.BINS)
        plt.hist(values[fieldname], bins)
        plotFilename = output +".png"
        lab.savefig(plotFilename)
        f = open(output, "w")
        f.write("<img src=\"" + plotFilename + "\"/>")
        f.close()

    def defineCharacteristics(self):
        self.name = "Vector layer histogram"
        self.group = "Graphics"
        self.addParameter(ParameterVector(self.INPUT, "Input layer", [ParameterVector.VECTOR_TYPE_ANY]))
        self.addParameter(ParameterTableField(self.FIELD, "Attribute", self.INPUT,ParameterTableField.DATA_TYPE_NUMBER))
        self.addParameter(ParameterNumber(self.BINS, "number of bins", 2, None, 10))
        self.addOutput(OutputHTML(self.OUTPUT, "Output"))


# -*- coding: utf-8 -*-
import json

import pandas as pd
import requests
from py2cytoscape.data.edge_view import EdgeView
from py2cytoscape.data.node_view import NodeView

from . import BASE_URL, HEADERS
from py2cytoscape.data.util_network import NetworkUtil

BASE_URL_NETWORK = BASE_URL + 'networks'

class CyNetworkView(object):

    def __init__(self, network=None, suid=None):
        if network is None:
            raise ValueError('Network is required.')

        # Validate required argument
        if pd.isnull(suid):
            raise ValueError("View SUID is missing.")
        else:
            self.__network = network
            self.__id = suid

        self.__url = BASE_URL_NETWORK + '/' \
                     + str(self.__network.get_id()) + '/views/' + str(self.__id)

    def get_id(self):
        """
        Get session-unique ID of this network view

        :return: SUID as integer
        """
        return self.__id

    def get_model_id(self):
        """
        Get network model SUID

        :return: model SUID as integer
        """
        return self.__network.get_id()

    def get_node_views(self):
        return self.__get_views('nodes')

    def get_edge_views(self):
        return self.__get_views('edges')

    def get_node_views_as_dict(self):
        return self.__get_views('nodes', format='dict')

    def get_edge_views_as_dict(self):
        return self.__get_views('edges', format='dict')

    def get_network_view_as_dict(self):
        return self.__get_views('network', format='dict')

    def get_node_views_as_dataframe(self):
        return self.__get_views('nodes', format='df')

    def get_edge_views_as_dataframe(self):
        return self.__get_views('edges', format='df')

    def __get_views(self, obj_type=None, format='view'):
        url = self.__url + '/' + obj_type
        views = requests.get(url).json()
        if format is 'dict':
            if obj_type is 'network':
                return self.__get_network_view_dict(views)
            else:
                return self.__get_view_dict(views)

        elif format is 'view':
            return self.__get_view_objects(views, obj_type)

        else:
            raise ValueError('Format not supported: ' + format)

    def __get_view_objects(self, views, obj_type):
        view_list = []
        if obj_type is 'nodes':
            for view in views:
                view = NodeView(self, view['SUID'], obj_type)
                view_list.append(view)

        elif obj_type is 'edges':
            for view in views:
                view = EdgeView(self, view['SUID'], obj_type)
                view_list.append(view)

        else:
            raise ValueError('No such object type: ' + obj_type)

        return view_list

    def __get_view_dict(self, views):
        # reformat return value to simple dict
        view_dict = {}
        for view in views:
            key = view['SUID']
            values = view['view']
            # Flatten the JSON
            key_val_pair = {}
            for entry in values:
                vp = entry['visualProperty']
                value = entry['value']
                key_val_pair[vp] = value
            view_dict[key] = key_val_pair

        return view_dict

    def __get_view_df(self, views):
        # reformat return value to simple dict
        view_dict = {}

        for view in views:
            key = view['SUID']
            values = view['view']
            # Flatten the JSON
            key_val_pair = {}
            for entry in values:
                vp = entry['visualProperty']
                value = entry['value']
                key_val_pair[vp] = value
            view_dict[key] = key_val_pair

        return view_dict

    def __get_network_view_dict(self, values):
        # reformat return value to simple dict
        view_dict = {}
        # Flatten the JSON
        for entry in values:
            vp = entry['visualProperty']
            value = entry['value']
            view_dict[vp] = value

        return view_dict

    def update_node_views(self, visual_property=None, values=None, key_type='suid'):
        self.__update_views(visual_property, values, 'nodes', key_type)

    def batch_update_node_views(self, value_dataframe=None):
        self.__batch_update(value_dataframe, 'nodes')

    def batch_update_edge_views(self, value_dataframe=None):
        self.__batch_update(value_dataframe, 'edges')

    def update_edge_views(self, visual_property=None, values=None, key_type='suid'):
        self.__update_views(visual_property, values, 'edges', key_type)

    def update_network_view(self, visual_property=None, value=None):
        """
        Updates single value for Network-related VP.

        :param visual_property:
        :param value:
        :return:
        """

        new_value = [
            {
                "visualProperty": visual_property,
                "value": value
            }
        ]
        requests.put(self.__url + '/network', data=json.dumps(new_value),
                     headers=HEADERS)

    def __update_views(self, visual_property, values,
                       object_type=None, key_type='suid'):
        if key_type is 'name':
            name2suid = NetworkUtil.name2suid(self.__network)

        body = []
        for key in values.keys():
            if key_type is 'name':
                suid = name2suid[key]
                if suid is None:
                    continue
            else:
                suid = key

            new_value = self.__create_new_value(suid, visual_property,
                                                values[key])
            body.append(new_value)
        requests.put(self.__url + '/' + object_type, data=json.dumps(body), headers=HEADERS)

    def __create_new_value(self, suid, visual_property, value):
        return {
            "SUID": suid,
            "view": [
                {
                    "visualProperty": visual_property,
                    "value": value
                }
            ]
        }

    def __batch_update(self, df, object_type=None):
        body = []

        columns = df.columns
        for index, row in df.iterrows():
            entry = {
                'SUID': int(index),
                'view': self.__create_new_values_from_row(columns, row)
            }
            body.append(entry)

        requests.put(self.__url + '/' + object_type, data=json.dumps(body), headers=HEADERS)

    def __create_new_values_from_row(self, columns, row):
        views = []
        for column in columns:
            view = {
                "visualProperty": column,
                "value": row[column]
            }
            views.append(view)

        return views


"""Build a language detector model

The goal of this exercise is to train a linear classifier on text features
that represent sequences of up to 3 consecutive characters so as to be
recognize natural languages by using the frequencies of short character
sequences as 'fingerprints'.

"""
# Author: Olivier Grisel <olivier.grisel@ensta.org>
# License: Simplified BSD

import sys

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import Perceptron
from sklearn.pipeline import Pipeline
from sklearn.datasets import load_files
from sklearn.cross_validation import train_test_split
from sklearn import metrics

# The training data folder must be passed as first argument
languages_data_folder = sys.argv[1]
dataset = load_files(languages_data_folder)

# Split the dataset in training and test set:
docs_train, docs_test, y_train, y_test = train_test_split(
    dataset.data, dataset.target, test_size=0.5)

# TASK: Build a an vectorizer that splits strings into sequence of 1 to 3
# characters instead of word tokens
vectorizer = TfidfVectorizer(ngram_range=(1, 3), analyzer='char',
                             use_idf=False)

# TASK: Build a vectorizer / classifier pipeline using the previous analyzer
# the pipeline instance should stored in a variable named clf
clf = Pipeline([
    ('vec', vectorizer),
    ('clf', Perceptron()),
])

# TASK: Fit the pipeline on the training set
clf.fit(docs_train, y_train)

# TASK: Predict the outcome on the testing set in a variable named y_predicted
y_predicted = clf.predict(docs_test)

# Print the classification report
print(metrics.classification_report(y_test, y_predicted,
                                    target_names=dataset.target_names))

# Plot the confusion matrix
cm = metrics.confusion_matrix(y_test, y_predicted)
print(cm)

#import pylab as pl
#pl.matshow(cm, cmap=pl.cm.jet)
#pl.show()

# Predict the result on some short new sentences:
sentences = [
    u'This is a language detection test.',
    u'Ceci est un test de d\xe9tection de la langue.',
    u'Dies ist ein Test, um die Sprache zu erkennen.',
]
predicted = clf.predict(sentences)

for s, p in zip(sentences, predicted):
    print(u'The language of "%s" is "%s"' % (s, dataset.target_names[p]))


#!/usr/bin/env python
# encoding: utf-8
"""
Example of creating a block model using the blockmodel function in NX.  Data used is the Hartford, CT drug users network:

@article{,
	title = {Social Networks of Drug Users in {High-Risk} Sites: Finding the Connections},
	volume = {6},
	shorttitle = {Social Networks of Drug Users in {High-Risk} Sites},
	url = {http://dx.doi.org/10.1023/A:1015457400897},
	doi = {10.1023/A:1015457400897},
	number = {2},
	journal = {{AIDS} and Behavior},
	author = {Margaret R. Weeks and Scott Clair and Stephen P. Borgatti and Kim Radda and Jean J. Schensul},
	month = jun,
	year = {2002},
	pages = {193--206}
}

"""

__author__ = """\n""".join(['Drew Conway <drew.conway@nyu.edu>',
                            'Aric Hagberg <hagberg@lanl.gov>'])

from collections import defaultdict
import networkx as nx
import numpy
from scipy.cluster import hierarchy
from scipy.spatial import distance
import matplotlib.pyplot as plt

def create_hc(G):
    """Creates hierarchical cluster of graph G from distance matrix"""
    path_length=nx.all_pairs_shortest_path_length(G)
    distances=numpy.zeros((len(G),len(G)))
    for u,p in path_length.items():
        for v,d in p.items():
            distances[u][v]=d
    # Create hierarchical cluster
    Y=distance.squareform(distances)
    Z=hierarchy.complete(Y)  # Creates HC using farthest point linkage
    # This partition selection is arbitrary, for illustrive purposes
    membership=list(hierarchy.fcluster(Z,t=1.15))
    # Create collection of lists for blockmodel
    partition=defaultdict(list)
    for n,p in zip(list(range(len(G))),membership):
        partition[p].append(n)
    return list(partition.values())

if __name__ == '__main__':
    G=nx.read_edgelist("hartford_drug.edgelist")

    # Extract largest connected component into graph H
    H=nx.connected_component_subgraphs(G)[0]
    # Makes life easier to have consecutively labeled integer nodes
    H=nx.convert_node_labels_to_integers(H)
    # Create parititions with hierarchical clustering
    partitions=create_hc(H)
    # Build blockmodel graph
    BM=nx.blockmodel(H,partitions)

    # Draw original graph
    pos=nx.spring_layout(H,iterations=100)
    fig=plt.figure(1,figsize=(6,10))
    ax=fig.add_subplot(211)
    nx.draw(H,pos,with_labels=False,node_size=10)
    plt.xlim(0,1)
    plt.ylim(0,1)

    # Draw block model with weighted edges and nodes sized by number of internal nodes
    node_size=[BM.node[x]['nnodes']*10 for x in BM.nodes()]
    edge_width=[(2*d['weight']) for (u,v,d) in BM.edges(data=True)]
    # Set positions to mean of positions of internal nodes from original graph
    posBM={}
    for n in BM:
        xy=numpy.array([pos[u] for u in BM.node[n]['graph']])
        posBM[n]=xy.mean(axis=0)
    ax=fig.add_subplot(212)
    nx.draw(BM,posBM,node_size=node_size,width=edge_width,with_labels=False)
    plt.xlim(0,1)
    plt.ylim(0,1)
    plt.axis('off')
    plt.savefig('hartford_drug_block_model.png')


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

'''
@author: Gabriele Girelli
@contact: gigi.ga90@gmail.com
@description: contains Series wrapper, which in turn contains Nucleus.
'''

# DEPENDENCIES =================================================================

import math
import os

import matplotlib.pyplot as plt
import numpy as np
from skimage.measure import label

from pygpseq import const

from pygpseq.tools.binarize import Binarize
from pygpseq.tools import io as iot
from pygpseq.tools import image as imt
from pygpseq.tools import plot
from pygpseq.tools import stat as stt
from pygpseq.tools import string as st
from pygpseq.tools import vector as vt

from pygpseq.anim.nucleus import Nucleus

# CLASSES ======================================================================

class Series(iot.IOinterface):
    """Series (Field of View, i.e., two-channel image) wrapper.

    Attributes:
      __version__ (string): package version.
      n (int): series id (1-indexed).
      name (string): series name.
      nuclei (list[pygpseq.wraps.Nuclei]): nuclei list.
      basedir (string): series folder path.
      dna_bg (float): estimated dna channel background.
      sig_bg (float): estimated signal channel background.
      flist (list): series file info.
    """

    __version__ = const.VERSION
    n = 0
    name = ''
    nuclei = []
    basedir = '.'
    dna_bg = None
    sig_bg = None
    filist = []

    def __init__(self, ds, condition = None, **kwargs):
        """Run IOinterface __init__ method.

        Args:
          ds (dict): series information list.
          condition (pyGPSeq.wraps.Condition): condition wrapper (opt).
        """

        # If required, inherit from `condition` wrap
        if None != condition:
            logpath = condition.logpath
            super(Series, self).__init__(path = logpath, append = True)
            self.basedir = condition.path
        else:
            super(Series, self).__init__()

        # Save input parameters
        self.name = ds[0]
        self.filist = ds[1]
        self.n = ds[2]

    def __getitem__(self, key):
        """ Allow get item. """
        if key in dir(self):
            return(getattr(self, key))
        else:
            return(None)

    def __setitem__(self, key, value):
        """ Allow set item. """
        if key in dir(self):
            self.__setattr__(key, value)

    def adjust_options(self, read_only_dna = None, log = None, **kwargs):
        """Adjust options to be passed to the Nucleus class.

        Args:
          dna_names (tuple[string]): dna channel names.
          sig_names (tuple[string]): signal channel names.
          an_type (pyGPSeq.const): analysis type.

        Returns:
          dict: adds the following kwargs:
            series_name (string): series wrap name.
            basedir (string): series wrap base directory.
            dna_ch (numpy.array): image (dimensionality based on an_type).
            sig_ch (numpy.array): image (dimensionality based on an_type).
        """

        # Start log
        if None == log: log = ''

        # Only work on dna channel
        if None == read_only_dna:
            read_only_dna = False

        # Add necessary options
        kwargs['series_name'] = self.name
        kwargs['basedir'] = self.basedir

        # Read DNA channel
        kwargs['dna_ch'], log = self.get_channel(kwargs['dna_names'],
            log, **kwargs)
        if not read_only_dna:
            kwargs['sig_ch'], log = self.get_channel(kwargs['sig_names'],
                log, **kwargs)

        # Output
        return((kwargs, log))

    def export_nuclei(self, **kwargs):
        """Export current series nuclei. """

        # Set output suffix
        if not 'suffix' in kwargs.keys():
            suffix = ''
        else:
            suffix = st.add_leading_dot(kwargs['suffix'])

        # Add necessary options
        self.printout('Current series: "' + self.name + '"...', 1)
        kwargs, log = self.adjust_options(**kwargs)

        # Export nuclei
        [n.export(**kwargs) for n in self.nuclei]

        # Produce log
        log = np.zeros(len(self.nuclei), dtype = const.DTYPE_NUCLEAR_SUMMARY)
        for l in [n.get_summary(**kwargs) for n in self.nuclei]:
            # Append nuclear data to the series log
            summary = [self.n]
            summary.extend(l)
            log[i, :] = summary

        # Export series log
        np.savetxt(kwargs['out_dir'] + self.name + '.summary' + suffix + '.csv',
            log, delimiter = ',', comments = '',
            header = ",".join([h for h in log.dtype.names]))

        return(log)

    def find_channel(self, channel_names):
        """Return the first channel to correspond to channel_names. """

        # Fix the param type
        if type(str()) == type(channel_names):
            channel_names = [channel_names]

        # Cycle through the available channels
        for cname in channel_names:

            # Identify the requested channel
            idx = self.find_channel_id(cname)

            # Return the channel
            if -1 != idx:
                return([i for i in self.filist.items()][idx])

        # Return empty dictionary if no matching channel is found
        return({})

    def find_channel_id(self, channel_name):
        """Return the id of the channel file with the specified name. """

        # Retrieve available channel names
        names = self.get_channel_names()

        if 0 != names.count(channel_name):
            # Return matching channel id
            return(names.index(channel_name))
        else:
            # Return -1 if no matching channel is found
            return(-1)

    def find_nuclei(self, **kwargs):
        """Segment current series.

        Args:
          **kwargs
            dna_names (tuple[string]): dna channel names.
            cond_name (string): condition wrapper name.
            seg_type (pyGPSeq.const): segmentation type.
            rm_z_tips (bool): remove nuclei touching the tips of the stack.
            radius_interval (tuple[float]): allowed nuclear radius interval.
            offset (tuple[int]): dimensions box/square offset.
            aspect (tuple[float]): pixel/voxel dimension proportion.

        Returns:
          tuple: series current instance and log string.
        """

        # Set output suffix
        if not 'suffix' in kwargs.keys():
            suffix = ''
        else:
            suffix = st.add_leading_dot(kwargs['suffix'])

        # Check plotting
        if not 'plotting' in kwargs.keys():
            kwargs['plotting'] = True

        log = ""
        log += self.printout('Current series: "' + self.name + '"...', 1)

        # Read images
        kwargs, alog = self.adjust_options(read_only_dna = False, **kwargs)
        log += alog

        # Extract from kwargs
        seg_type = kwargs['seg_type']
        dna_ch = kwargs['dna_ch']
        sig_ch = kwargs['sig_ch']

        # Make new channel copy
        i = dna_ch.copy()

        # Produce a mask
        bi = Binarize(path = kwargs['logpath'], append = True, **kwargs)
        bi.verbose = self.verbose
        mask, thr, tmp_log = bi.run(i)
        log += tmp_log

        # Estimate background
        if None == self.dna_bg:
            self.dna_bg = imt.estimate_background(dna_ch, mask, seg_type)
        kwargs['dna_bg'] = self.dna_bg
        if None == self.sig_bg:
            self.sig_bg = imt.estimate_background(sig_ch, mask, seg_type)
        kwargs['sig_bg'] = self.sig_bg
        log += self.printout('Estimating background:', 2)
        log += self.printout('DNA channel: ' + str(kwargs['dna_bg']), 3)
        log += self.printout('Signal channel: ' + str(kwargs['sig_bg']), 3)

        # Filter object size
        mask, tmp_log = bi.filter_obj_XY_size(mask)
        log += tmp_log
        mask, tmp_log = bi.filter_obj_Z_size(mask)
        log += tmp_log

        # Save mask
        log += self.printout('Saving series object mask...', 2)
        L = label(mask)

        # Plot
        fig = plt.figure()
        if 3 == len(mask.shape):
            plt.imshow(L.max(0).astype('u4'))
        else:
            plt.imshow(L.astype('u4'))
        plt.gca().get_xaxis().set_visible(False)
        plt.gca().get_yaxis().set_visible(False)
        plot.set_font_size(kwargs['font_size'])

        title = 'Nuclei in "' + kwargs['cond_name'] + '", ' + str(self.name)
        title += ' [' + str(L.max()) + ' objects]'
        plt.title(title)

        # Export as png
        fname = kwargs['out_dir'] + const.OUTDIR_MASK + kwargs['cond_name']
        fname += '.' + self.name + '.mask' + suffix + '.png'
        if kwargs['plotting']: plot.export(fname, 'png')

        # Close plot figure
        plt.close(fig)

        # Initialize nuclei
        log += self.printout('Bounding ' + str(L.max()) + ' nuclei...', 2)
        kwargs['logpath'] = self.logpath
        kwargs['i'] = i
        kwargs['thr'] = thr
        kwargs['series_id'] = self.n
        seq = range(1, L.max() + 1)
        self.nuclei = [Nucleus(n = n, mask = L == n, **kwargs) for n in seq]

        return((self, log))

    def get_c(self):
        """Return number of channels in the series. """
        return(len(self.filist))

    def get_channel(self, ch_name, log = None, **kwargs):
        """Read the series specified channel.

        Args:
          ch_name (string): channel name.
          log (string): log string.
          **kwargs

        Returns:
          tuple: channel image and log string.
        """

        # Start log (used when verbosity is off)
        if None == log: log = ""
        log += self.printout('Reading channel "' + str(ch_name) + '"...', 2)

        # Read channel
        f = self.find_channel(ch_name)
        imch = imt.read_tiff(os.path.join(self.basedir, f[0]))
        imch = imt.slice_k_d_img(imch, 3)

        # Deconvolved images correction
        if 'rescale_deconvolved' in kwargs.keys():
            if kwargs['rescale_deconvolved']:
                # Get DNA scaling factor and rescale
                sf = imt.get_rescaling_factor(f, **kwargs)
                imch = (imch / sf).astype('float')
                msg = 'Rescaling "' + f[0] + '" [' + str(sf) + ']...'
                log += self.printout(msg, 3)

        # Make Z-projection
        if kwargs['an_type'] in [const.AN_SUM_PROJ, const.AN_MAX_PROJ]:
            msg = 'Generating Z-projection [' + str(kwargs['an_type']) + ']...'
            log += self.printout(msg, 3)
            if 2 != len(imch.shape):
                imch = imt.mk_z_projection(imch, kwargs['an_type'])

        # Prepare output
        return((imch, log))

    def get_channel_names(self, channel_field = None):
        """Return the names of the channels in the series. """
        if None == channel_field:
            channel_field = const.REG_CHANNEL_NAME
        return([c[channel_field] for c in self.filist.values()])

    def get_nuclei_data(self, nuclei_ids, **kwargs):
        """Retrieve a single nucleus from the current series. """

        # Read channel images
        kwargs, log = self.adjust_options(**kwargs)

        # Re-build mask
        bi = Binarize(path = self.logpath, append = True, **kwargs)
        bi.verbose = self.verbose
        mask, thr, tmp_log = bi.run(kwargs['dna_ch'].copy())
        log += tmp_log

        # Empty nuclear data array
        data = []
        for nucleus_id in nuclei_ids:
            # Select nucleus
            n = self.nuclei[nucleus_id -1]

            # Setup nucleus instance verbosity
            if not self.verbose:
                n.verbose = False

            # Retrieve nuclear data
            ndata, nlog = n.get_data(mask = mask, **kwargs)

            # Update log and save nuclear data
            log += nlog
            data.append(ndata)

        return((data, log))

    def propagate_attr(self, key):
        """Propagate attribute current value to every nucleus. """
        for i in range(len(self.nuclei)):
            self.nuclei[i][key] = self[key]

# END ==========================================================================

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


"""This file contains code for use with "Think Stats",
by Allen B. Downey, available from greenteapress.com

Copyright 2014 Allen B. Downey
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
"""

from __future__ import print_function

import math
import matplotlib
import matplotlib.pyplot as pyplot
import numpy as np
import pandas

import warnings

# customize some matplotlib attributes
#matplotlib.rc('figure', figsize=(4, 3))

#matplotlib.rc('font', size=14.0)
#matplotlib.rc('axes', labelsize=22.0, titlesize=22.0)
#matplotlib.rc('legend', fontsize=20.0)

#matplotlib.rc('xtick.major', size=6.0)
#matplotlib.rc('xtick.minor', size=3.0)

#matplotlib.rc('ytick.major', size=6.0)
#matplotlib.rc('ytick.minor', size=3.0)

class _Brewer(object):
    """Encapsulates a nice sequence of colors.

    Shades of blue that look good in color and can be distinguished
    in grayscale (up to a point).

    Borrowed from http://colorbrewer2.org/
    """
    color_iter = None

    colors = ['#081D58',
              '#253494',
              '#225EA8',
              '#1D91C0',
              '#41B6C4',
              '#7FCDBB',
              '#C7E9B4',
              '#EDF8B1',
              '#FFFFD9']

    # lists that indicate which colors to use depending on how many are used
    which_colors = [[],
                    [1],
                    [1, 3],
                    [0, 2, 4],
                    [0, 2, 4, 6],
                    [0, 2, 3, 5, 6],
                    [0, 2, 3, 4, 5, 6],
                    [0, 1, 2, 3, 4, 5, 6],
                    ]

    @classmethod
    def Colors(cls):
        """Returns the list of colors.
        """
        return cls.colors

    @classmethod
    def ColorGenerator(cls, n):
        """Returns an iterator of color strings.

        n: how many colors will be used
        """
        for i in cls.which_colors[n]:
            yield cls.colors[i]
        raise StopIteration('Ran out of colors in _Brewer.ColorGenerator')

    @classmethod
    def InitializeIter(cls, num):
        """Initializes the color iterator with the given number of colors."""
        cls.color_iter = cls.ColorGenerator(num)

    @classmethod
    def ClearIter(cls):
        """Sets the color iterator to None."""
        cls.color_iter = None

    @classmethod
    def GetIter(cls):
        """Gets the color iterator."""
        if cls.color_iter is None:
            cls.InitializeIter(7)

        return cls.color_iter

def PrePlot(num=None, rows=None, cols=None):
    """Takes hints about what's coming.

    num: number of lines that will be plotted
    rows: number of rows of subplots
    cols: number of columns of subplots
    """
    if num:
        _Brewer.InitializeIter(num)

    if rows is None and cols is None:
        return

    if rows is not None and cols is None:
        cols = 1

    if cols is not None and rows is None:
        rows = 1

    # resize the image, depending on the number of rows and cols
    size_map = {(1, 1): (8, 6),
                (1, 2): (14, 6),
                (1, 3): (14, 6),
                (2, 2): (10, 10),
                (2, 3): (16, 10),
                (3, 1): (8, 10),
                }

    if (rows, cols) in size_map:
        fig = pyplot.gcf()
        fig.set_size_inches(*size_map[rows, cols])

    # create the first subplot
    if rows > 1 or cols > 1:
        pyplot.subplot(rows, cols, 1)
        global SUBPLOT_ROWS, SUBPLOT_COLS
        SUBPLOT_ROWS = rows
        SUBPLOT_COLS = cols

def SubPlot(plot_number, rows=None, cols=None):
    """Configures the number of subplots and changes the current plot.

    rows: int
    cols: int
    plot_number: int
    """
    rows = rows or SUBPLOT_ROWS
    cols = cols or SUBPLOT_COLS
    pyplot.subplot(rows, cols, plot_number)

def _Underride(d, **options):
    """Add key-value pairs to d only if key is not in d.

    If d is None, create a new dictionary.

    d: dictionary
    options: keyword args to add to d
    """
    if d is None:
        d = {}

    for key, val in options.items():
        d.setdefault(key, val)

    return d

def Clf():
    """Clears the figure and any hints that have been set."""
    global LOC
    LOC = None
    _Brewer.ClearIter()
    pyplot.clf()
    fig = pyplot.gcf()
    fig.set_size_inches(8, 6)

def Figure(**options):
    """Sets options for the current figure."""
    _Underride(options, figsize=(6, 8))
    pyplot.figure(**options)

def _UnderrideColor(options):
    if 'color' in options:
        return options

    color_iter = _Brewer.GetIter()

    if color_iter:
        try:
            options['color'] = next(color_iter)
        except StopIteration:
            # TODO: reconsider whether this should warn
            # warnings.warn('Warning: Brewer ran out of colors.')
            _Brewer.ClearIter()
    return options

def Plot(obj, ys=None, style='', **options):
    """Plots a line.

    Args:
      obj: sequence of x values, or Series, or anything with Render()
      ys: sequence of y values
      style: style string passed along to pyplot.plot
      options: keyword args passed to pyplot.plot
    """
    options = _UnderrideColor(options)
    label = getattr(obj, 'label', '_nolegend_')
    options = _Underride(options, linewidth=3, alpha=0.8, label=label)

    xs = obj
    if ys is None:
        if hasattr(obj, 'Render'):
            xs, ys = obj.Render()
        if isinstance(obj, pandas.Series):
            ys = obj.values
            xs = obj.index

    if ys is None:
        pyplot.plot(xs, style, **options)
    else:
        pyplot.plot(xs, ys, style, **options)

def FillBetween(xs, y1, y2=None, where=None, **options):
    """Plots a line.

    Args:
      xs: sequence of x values
      y1: sequence of y values
      y2: sequence of y values
      where: sequence of boolean
      options: keyword args passed to pyplot.fill_between
    """
    options = _UnderrideColor(options)
    options = _Underride(options, linewidth=0, alpha=0.5)
    pyplot.fill_between(xs, y1, y2, where, **options)

def Bar(xs, ys, **options):
    """Plots a line.

    Args:
      xs: sequence of x values
      ys: sequence of y values
      options: keyword args passed to pyplot.bar
    """
    options = _UnderrideColor(options)
    options = _Underride(options, linewidth=0, alpha=0.6)
    pyplot.bar(xs, ys, **options)

def Scatter(xs, ys=None, **options):
    """Makes a scatter plot.

    xs: x values
    ys: y values
    options: options passed to pyplot.scatter
    """
    options = _Underride(options, color='blue', alpha=0.2,
                        s=30, edgecolors='none')

    if ys is None and isinstance(xs, pandas.Series):
        ys = xs.values
        xs = xs.index

    pyplot.scatter(xs, ys, **options)

def HexBin(xs, ys, **options):
    """Makes a scatter plot.

    xs: x values
    ys: y values
    options: options passed to pyplot.scatter
    """
    options = _Underride(options, cmap=matplotlib.cm.Blues)
    pyplot.hexbin(xs, ys, **options)

def Pdf(pdf, **options):
    """Plots a Pdf, Pmf, or Hist as a line.

    Args:
      pdf: Pdf, Pmf, or Hist object
      options: keyword args passed to pyplot.plot
    """
    low, high = options.pop('low', None), options.pop('high', None)
    n = options.pop('n', 101)
    xs, ps = pdf.Render(low=low, high=high, n=n)
    options = _Underride(options, label=pdf.label)
    Plot(xs, ps, **options)

def Pdfs(pdfs, **options):
    """Plots a sequence of PDFs.

    Options are passed along for all PDFs.  If you want different
    options for each pdf, make multiple calls to Pdf.

    Args:
      pdfs: sequence of PDF objects
      options: keyword args passed to pyplot.plot
    """
    for pdf in pdfs:
        Pdf(pdf, **options)

def Hist(hist, **options):
    """Plots a Pmf or Hist with a bar plot.

    The default width of the bars is based on the minimum difference
    between values in the Hist.  If that's too small, you can override
    it by providing a width keyword argument, in the same units
    as the values.

    Args:
      hist: Hist or Pmf object
      options: keyword args passed to pyplot.bar
    """
    # find the minimum distance between adjacent values
    xs, ys = hist.Render()

    if 'width' not in options:
        try:
            options['width'] = 0.9 * np.diff(xs).min()
        except TypeError:
            warnings.warn("Hist: Can't compute bar width automatically."
                            "Check for non-numeric types in Hist."
                            "Or try providing width option."
                            )

    options = _Underride(options, label=hist.label)
    options = _Underride(options, align='center')
    if options['align'] == 'left':
        options['align'] = 'edge'
    elif options['align'] == 'right':
        options['align'] = 'edge'
        options['width'] *= -1

    Bar(xs, ys, **options)

def Hists(hists, **options):
    """Plots two histograms as interleaved bar plots.

    Options are passed along for all PMFs.  If you want different
    options for each pmf, make multiple calls to Pmf.

    Args:
      hists: list of two Hist or Pmf objects
      options: keyword args passed to pyplot.plot
    """
    for hist in hists:
        Hist(hist, **options)

def Pmf(pmf, **options):
    """Plots a Pmf or Hist as a line.

    Args:
      pmf: Hist or Pmf object
      options: keyword args passed to pyplot.plot
    """
    xs, ys = pmf.Render()
    low, high = min(xs), max(xs)

    width = options.pop('width', None)
    if width is None:
        try:
            width = np.diff(xs).min()
        except TypeError:
            warnings.warn("Pmf: Can't compute bar width automatically."
                          "Check for non-numeric types in Pmf."
                          "Or try providing width option.")
    points = []

    lastx = np.nan
    lasty = 0
    for x, y in zip(xs, ys):
        if (x - lastx) > 1e-5:
            points.append((lastx, 0))
            points.append((x, 0))

        points.append((x, lasty))
        points.append((x, y))
        points.append((x+width, y))

        lastx = x + width
        lasty = y
    points.append((lastx, 0))
    pxs, pys = zip(*points)

    align = options.pop('align', 'center')
    if align == 'center':
        pxs = np.array(pxs) - width/2.0
    if align == 'right':
        pxs = np.array(pxs) - width

    options = _Underride(options, label=pmf.label)
    Plot(pxs, pys, **options)

def Pmfs(pmfs, **options):
    """Plots a sequence of PMFs.

    Options are passed along for all PMFs.  If you want different
    options for each pmf, make multiple calls to Pmf.

    Args:
      pmfs: sequence of PMF objects
      options: keyword args passed to pyplot.plot
    """
    for pmf in pmfs:
        Pmf(pmf, **options)

def Diff(t):
    """Compute the differences between adjacent elements in a sequence.

    Args:
        t: sequence of number

    Returns:
        sequence of differences (length one less than t)
    """
    diffs = [t[i+1] - t[i] for i in range(len(t)-1)]
    return diffs

def Cdf(cdf, complement=False, transform=None, **options):
    """Plots a CDF as a line.

    Args:
      cdf: Cdf object
      complement: boolean, whether to plot the complementary CDF
      transform: string, one of 'exponential', 'pareto', 'weibull', 'gumbel'
      options: keyword args passed to pyplot.plot

    Returns:
      dictionary with the scale options that should be passed to
      Config, Show or Save.
    """
    xs, ps = cdf.Render()
    xs = np.asarray(xs)
    ps = np.asarray(ps)

    scale = dict(xscale='linear', yscale='linear')

    for s in ['xscale', 'yscale']:
        if s in options:
            scale[s] = options.pop(s)

    if transform == 'exponential':
        complement = True
        scale['yscale'] = 'log'

    if transform == 'pareto':
        complement = True
        scale['yscale'] = 'log'
        scale['xscale'] = 'log'

    if complement:
        ps = [1.0-p for p in ps]

    if transform == 'weibull':
        xs = np.delete(xs, -1)
        ps = np.delete(ps, -1)
        ps = [-math.log(1.0-p) for p in ps]
        scale['xscale'] = 'log'
        scale['yscale'] = 'log'

    if transform == 'gumbel':
        xs = xp.delete(xs, 0)
        ps = np.delete(ps, 0)
        ps = [-math.log(p) for p in ps]
        scale['yscale'] = 'log'

    options = _Underride(options, label=cdf.label)
    Plot(xs, ps, **options)
    return scale

def Cdfs(cdfs, complement=False, transform=None, **options):
    """Plots a sequence of CDFs.

    cdfs: sequence of CDF objects
    complement: boolean, whether to plot the complementary CDF
    transform: string, one of 'exponential', 'pareto', 'weibull', 'gumbel'
    options: keyword args passed to pyplot.plot
    """
    for cdf in cdfs:
        Cdf(cdf, complement, transform, **options)

def Contour(obj, pcolor=False, contour=True, imshow=False, **options):
    """Makes a contour plot.

    d: map from (x, y) to z, or object that provides GetDict
    pcolor: boolean, whether to make a pseudocolor plot
    contour: boolean, whether to make a contour plot
    imshow: boolean, whether to use pyplot.imshow
    options: keyword args passed to pyplot.pcolor and/or pyplot.contour
    """
    try:
        d = obj.GetDict()
    except AttributeError:
        d = obj

    _Underride(options, linewidth=3, cmap=matplotlib.cm.Blues)

    xs, ys = zip(*d.keys())
    xs = sorted(set(xs))
    ys = sorted(set(ys))

    X, Y = np.meshgrid(xs, ys)
    func = lambda x, y: d.get((x, y), 0)
    func = np.vectorize(func)
    Z = func(X, Y)

    x_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
    axes = pyplot.gca()
    axes.xaxis.set_major_formatter(x_formatter)

    if pcolor:
        pyplot.pcolormesh(X, Y, Z, **options)
    if contour:
        cs = pyplot.contour(X, Y, Z, **options)
        pyplot.clabel(cs, inline=1, fontsize=10)
    if imshow:
        extent = xs[0], xs[-1], ys[0], ys[-1]
        pyplot.imshow(Z, extent=extent, **options)

def Pcolor(xs, ys, zs, pcolor=True, contour=False, **options):
    """Makes a pseudocolor plot.

    xs:
    ys:
    zs:
    pcolor: boolean, whether to make a pseudocolor plot
    contour: boolean, whether to make a contour plot
    options: keyword args passed to pyplot.pcolor and/or pyplot.contour
    """
    _Underride(options, linewidth=3, cmap=matplotlib.cm.Blues)

    X, Y = np.meshgrid(xs, ys)
    Z = zs

    x_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
    axes = pyplot.gca()
    axes.xaxis.set_major_formatter(x_formatter)

    if pcolor:
        pyplot.pcolormesh(X, Y, Z, **options)

    if contour:
        cs = pyplot.contour(X, Y, Z, **options)
        pyplot.clabel(cs, inline=1, fontsize=10)

def Text(x, y, s, **options):
    """Puts text in a figure.

    x: number
    y: number
    s: string
    options: keyword args passed to pyplot.text
    """
    options = _Underride(options,
                         fontsize=16,
                         verticalalignment='top',
                         horizontalalignment='left')
    pyplot.text(x, y, s, **options)

LEGEND = True
LOC = None

def Config(**options):
    """Configures the plot.

    Pulls options out of the option dictionary and passes them to
    the corresponding pyplot functions.
    """
    names = ['title', 'xlabel', 'ylabel', 'xscale', 'yscale',
             'xticks', 'yticks', 'axis', 'xlim', 'ylim']

    for name in names:
        if name in options:
            getattr(pyplot, name)(options[name])

    # looks like this is not necessary: matplotlib understands text loc specs
    loc_dict = {'upper right': 1,
                'upper left': 2,
                'lower left': 3,
                'lower right': 4,
                'right': 5,
                'center left': 6,
                'center right': 7,
                'lower center': 8,
                'upper center': 9,
                'center': 10,
                }

    global LEGEND
    LEGEND = options.get('legend', LEGEND)

    if LEGEND:
        global LOC
        LOC = options.get('loc', LOC)
        pyplot.legend(loc=LOC)

def Show(**options):
    """Shows the plot.

    For options, see Config.

    options: keyword args used to invoke various pyplot functions
    """
    clf = options.pop('clf', True)
    Config(**options)
    pyplot.show()
    if clf:
        Clf()

def Plotly(**options):
    """Shows the plot.

    For options, see Config.

    options: keyword args used to invoke various pyplot functions
    """
    clf = options.pop('clf', True)
    Config(**options)
    import plotly.plotly as plotly
    url = plotly.plot_mpl(pyplot.gcf())
    if clf:
        Clf()
    return url

def Save(root=None, formats=None, **options):
    """Saves the plot in the given formats and clears the figure.

    For options, see Config.

    Args:
      root: string filename root
      formats: list of string formats
      options: keyword args used to invoke various pyplot functions
    """
    clf = options.pop('clf', True)
    Config(**options)

    if formats is None:
        formats = ['pdf', 'eps']

    try:
        formats.remove('plotly')
        Plotly(clf=False)
    except ValueError:
        pass

    if root:
        for fmt in formats:
            SaveFormat(root, fmt)
    if clf:
        Clf()

def SaveFormat(root, fmt='eps'):
    """Writes the current figure to a file in the given format.

    Args:
      root: string filename root
      fmt: string format
    """
    filename = '%s.%s' % (root, fmt)
    print('Writing', filename)
    pyplot.savefig(filename, format=fmt, dpi=300)

# provide aliases for calling functons with lower-case names
preplot = PrePlot
subplot = SubPlot
clf = Clf
figure = Figure
plot = Plot
text = Text
scatter = Scatter
pmf = Pmf
pmfs = Pmfs
hist = Hist
hists = Hists
diff = Diff
cdf = Cdf
cdfs = Cdfs
contour = Contour
pcolor = Pcolor
config = Config
show = Show
save = Save

def main():
    color_iter = _Brewer.ColorGenerator(7)
    for color in color_iter:
        print(color)

if __name__ == '__main__':
    main()