import numpy as np
from scipy.linalg import block_diag
from scipy.sparse import csr_matrix
from scipy.special import psi

from sklearn.decomposition import LatentDirichletAllocation
from sklearn.decomposition._online_lda import (_dirichlet_expectation_1d,
                                               _dirichlet_expectation_2d)

from sklearn.utils.testing import assert_allclose
from sklearn.utils.testing import assert_true
from sklearn.utils.testing import assert_array_almost_equal
from sklearn.utils.testing import assert_almost_equal
from sklearn.utils.testing import assert_greater_equal
from sklearn.utils.testing import assert_raises_regexp
from sklearn.utils.testing import if_safe_multiprocessing_with_blas

from sklearn.utils.validation import NotFittedError
from sklearn.externals.six.moves import xrange

def _build_sparse_mtx():
    # Create 3 topics and each topic has 3 disticnt words.
    # (Each word only belongs to a single topic.)
    n_topics = 3
    block = n_topics * np.ones((3, 3))
    blocks = [block] * n_topics
    X = block_diag(*blocks)
    X = csr_matrix(X)
    return (n_topics, X)

def test_lda_default_prior_params():
    # default prior parameter should be `1 / topics`
    # and verbose params should not affect result
    n_topics, X = _build_sparse_mtx()
    prior = 1. / n_topics
    lda_1 = LatentDirichletAllocation(n_topics=n_topics, doc_topic_prior=prior,
                                      topic_word_prior=prior, random_state=0)
    lda_2 = LatentDirichletAllocation(n_topics=n_topics, random_state=0)

    topic_distr_1 = lda_1.fit_transform(X)
    topic_distr_2 = lda_2.fit_transform(X)
    assert_almost_equal(topic_distr_1, topic_distr_2)

def test_lda_fit_batch():
    # Test LDA batch learning_offset (`fit` method with 'batch' learning)
    rng = np.random.RandomState(0)
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, evaluate_every=1,
                                    learning_method='batch', random_state=rng)
    lda.fit(X)

    correct_idx_grps = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    for component in lda.components_:
        # Find top 3 words in each LDA component
        top_idx = set(component.argsort()[-3:][::-1])
        assert_true(tuple(sorted(top_idx)) in correct_idx_grps)

def test_lda_fit_online():
    # Test LDA online learning (`fit` method with 'online' learning)
    rng = np.random.RandomState(0)
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, learning_offset=10.,
                                    evaluate_every=1, learning_method='online',
                                    random_state=rng)
    lda.fit(X)

    correct_idx_grps = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    for component in lda.components_:
        # Find top 3 words in each LDA component
        top_idx = set(component.argsort()[-3:][::-1])
        assert_true(tuple(sorted(top_idx)) in correct_idx_grps)

def test_lda_partial_fit():
    # Test LDA online learning (`partial_fit` method)
    # (same as test_lda_batch)
    rng = np.random.RandomState(0)
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, learning_offset=10.,
                                    total_samples=100, random_state=rng)
    for i in xrange(3):
        lda.partial_fit(X)

    correct_idx_grps = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    for c in lda.components_:
        top_idx = set(c.argsort()[-3:][::-1])
        assert_true(tuple(sorted(top_idx)) in correct_idx_grps)

def test_lda_dense_input():
    # Test LDA with dense input.
    rng = np.random.RandomState(0)
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, learning_method='batch',
                                    random_state=rng)
    lda.fit(X.toarray())

    correct_idx_grps = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    for component in lda.components_:
        # Find top 3 words in each LDA component
        top_idx = set(component.argsort()[-3:][::-1])
        assert_true(tuple(sorted(top_idx)) in correct_idx_grps)

def test_lda_transform():
    # Test LDA transform.
    # Transform result cannot be negative
    rng = np.random.RandomState(0)
    X = rng.randint(5, size=(20, 10))
    n_topics = 3
    lda = LatentDirichletAllocation(n_topics=n_topics, random_state=rng)
    X_trans = lda.fit_transform(X)
    assert_true((X_trans > 0.0).any())

def test_lda_fit_transform():
    # Test LDA fit_transform & transform
    # fit_transform and transform result should be the same
    for method in ('online', 'batch'):
        rng = np.random.RandomState(0)
        X = rng.randint(10, size=(50, 20))
        lda = LatentDirichletAllocation(n_topics=5, learning_method=method,
                                        random_state=rng)
        X_fit = lda.fit_transform(X)
        X_trans = lda.transform(X)
        assert_array_almost_equal(X_fit, X_trans, 4)

def test_lda_partial_fit_dim_mismatch():
    # test `n_features` mismatch in `partial_fit`
    rng = np.random.RandomState(0)
    n_topics = rng.randint(3, 6)
    n_col = rng.randint(6, 10)
    X_1 = np.random.randint(4, size=(10, n_col))
    X_2 = np.random.randint(4, size=(10, n_col + 1))
    lda = LatentDirichletAllocation(n_topics=n_topics, learning_offset=5.,
                                    total_samples=20, random_state=rng)
    lda.partial_fit(X_1)
    assert_raises_regexp(ValueError, r"^The provided data has",
                         lda.partial_fit, X_2)

def test_invalid_params():
    # test `_check_params` method
    X = np.ones((5, 10))

    invalid_models = (
        ('n_topics', LatentDirichletAllocation(n_topics=0)),
        ('learning_method',
         LatentDirichletAllocation(learning_method='unknown')),
        ('total_samples', LatentDirichletAllocation(total_samples=0)),
        ('learning_offset', LatentDirichletAllocation(learning_offset=-1)),
    )
    for param, model in invalid_models:
        regex = r"^Invalid %r parameter" % param
        assert_raises_regexp(ValueError, regex, model.fit, X)

def test_lda_negative_input():
    # test pass dense matrix with sparse negative input.
    X = -np.ones((5, 10))
    lda = LatentDirichletAllocation()
    regex = r"^Negative values in data passed"
    assert_raises_regexp(ValueError, regex, lda.fit, X)

def test_lda_no_component_error():
    # test `transform` and `perplexity` before `fit`
    rng = np.random.RandomState(0)
    X = rng.randint(4, size=(20, 10))
    lda = LatentDirichletAllocation()
    regex = r"^no 'components_' attribute"
    assert_raises_regexp(NotFittedError, regex, lda.transform, X)
    assert_raises_regexp(NotFittedError, regex, lda.perplexity, X)

def test_lda_transform_mismatch():
    # test `n_features` mismatch in partial_fit and transform
    rng = np.random.RandomState(0)
    X = rng.randint(4, size=(20, 10))
    X_2 = rng.randint(4, size=(10, 8))

    n_topics = rng.randint(3, 6)
    lda = LatentDirichletAllocation(n_topics=n_topics, random_state=rng)
    lda.partial_fit(X)
    assert_raises_regexp(ValueError, r"^The provided data has",
                         lda.partial_fit, X_2)

@if_safe_multiprocessing_with_blas
def test_lda_multi_jobs():
    n_topics, X = _build_sparse_mtx()
    # Test LDA batch training with multi CPU
    for method in ('online', 'batch'):
        rng = np.random.RandomState(0)
        lda = LatentDirichletAllocation(n_topics=n_topics, n_jobs=2,
                                        learning_method=method,
                                        random_state=rng)
        lda.fit(X)

        correct_idx_grps = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
        for c in lda.components_:
            top_idx = set(c.argsort()[-3:][::-1])
            assert_true(tuple(sorted(top_idx)) in correct_idx_grps)

@if_safe_multiprocessing_with_blas
def test_lda_partial_fit_multi_jobs():
    # Test LDA online training with multi CPU
    rng = np.random.RandomState(0)
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, n_jobs=2,
                                    learning_offset=5., total_samples=30,
                                    random_state=rng)
    for i in range(2):
        lda.partial_fit(X)

    correct_idx_grps = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    for c in lda.components_:
        top_idx = set(c.argsort()[-3:][::-1])
        assert_true(tuple(sorted(top_idx)) in correct_idx_grps)

def test_lda_preplexity_mismatch():
    # test dimension mismatch in `perplexity` method
    rng = np.random.RandomState(0)
    n_topics = rng.randint(3, 6)
    n_samples = rng.randint(6, 10)
    X = np.random.randint(4, size=(n_samples, 10))
    lda = LatentDirichletAllocation(n_topics=n_topics, learning_offset=5.,
                                    total_samples=20, random_state=rng)
    lda.fit(X)
    # invalid samples
    invalid_n_samples = rng.randint(4, size=(n_samples + 1, n_topics))
    assert_raises_regexp(ValueError, r'Number of samples', lda.perplexity, X,
                         invalid_n_samples)
    # invalid topic number
    invalid_n_topics = rng.randint(4, size=(n_samples, n_topics + 1))
    assert_raises_regexp(ValueError, r'Number of topics', lda.perplexity, X,
                         invalid_n_topics)

def test_lda_perplexity():
    # Test LDA perplexity for batch training
    # perplexity should be lower after each iteration
    n_topics, X = _build_sparse_mtx()
    for method in ('online', 'batch'):
        lda_1 = LatentDirichletAllocation(n_topics=n_topics, max_iter=1,
                                          learning_method=method,
                                          total_samples=100, random_state=0)
        lda_2 = LatentDirichletAllocation(n_topics=n_topics, max_iter=10,
                                          learning_method=method,
                                          total_samples=100, random_state=0)
        distr_1 = lda_1.fit_transform(X)
        perp_1 = lda_1.perplexity(X, distr_1, sub_sampling=False)

        distr_2 = lda_2.fit_transform(X)
        perp_2 = lda_2.perplexity(X, distr_2, sub_sampling=False)
        assert_greater_equal(perp_1, perp_2)

        perp_1_subsampling = lda_1.perplexity(X, distr_1, sub_sampling=True)
        perp_2_subsampling = lda_2.perplexity(X, distr_2, sub_sampling=True)
        assert_greater_equal(perp_1_subsampling, perp_2_subsampling)

def test_lda_score():
    # Test LDA score for batch training
    # score should be higher after each iteration
    n_topics, X = _build_sparse_mtx()
    for method in ('online', 'batch'):
        lda_1 = LatentDirichletAllocation(n_topics=n_topics, max_iter=1,
                                          learning_method=method,
                                          total_samples=100, random_state=0)
        lda_2 = LatentDirichletAllocation(n_topics=n_topics, max_iter=10,
                                          learning_method=method,
                                          total_samples=100, random_state=0)
        lda_1.fit_transform(X)
        score_1 = lda_1.score(X)

        lda_2.fit_transform(X)
        score_2 = lda_2.score(X)
        assert_greater_equal(score_2, score_1)

def test_perplexity_input_format():
    # Test LDA perplexity for sparse and dense input
    # score should be the same for both dense and sparse input
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, max_iter=1,
                                    learning_method='batch',
                                    total_samples=100, random_state=0)
    distr = lda.fit_transform(X)
    perp_1 = lda.perplexity(X)
    perp_2 = lda.perplexity(X, distr)
    perp_3 = lda.perplexity(X.toarray(), distr)
    assert_almost_equal(perp_1, perp_2)
    assert_almost_equal(perp_1, perp_3)

def test_lda_score_perplexity():
    # Test the relationship between LDA score and perplexity
    n_topics, X = _build_sparse_mtx()
    lda = LatentDirichletAllocation(n_topics=n_topics, max_iter=10,
                                    random_state=0)
    distr = lda.fit_transform(X)
    perplexity_1 = lda.perplexity(X, distr, sub_sampling=False)

    score = lda.score(X)
    perplexity_2 = np.exp(-1. * (score / np.sum(X.data)))
    assert_almost_equal(perplexity_1, perplexity_2)

def test_lda_empty_docs():
    """Test LDA on empty document (all-zero rows)."""
    Z = np.zeros((5, 4))
    for X in [Z, csr_matrix(Z)]:
        lda = LatentDirichletAllocation(max_iter=750).fit(X)
        assert_almost_equal(lda.components_.sum(axis=0),
                            np.ones(lda.components_.shape[1]))

def test_dirichlet_expectation():
    """Test Cython version of Dirichlet expectation calculation."""
    x = np.logspace(-100, 10, 10000)
    expectation = np.empty_like(x)
    _dirichlet_expectation_1d(x, 0, expectation)
    assert_allclose(expectation, np.exp(psi(x) - psi(np.sum(x))),
                    atol=1e-19)

    x = x.reshape(100, 100)
    assert_allclose(_dirichlet_expectation_2d(x),
                    psi(x) - psi(np.sum(x, axis=1)[:, np.newaxis]),
                    rtol=1e-11, atol=3e-9)


"""
Sparse Logistic Regression module

This module has the same API as sklearn.linear_model.logistic, but is
designed to handle efficiently data in sparse matrix format.
"""

import numpy as np
import scipy.sparse as sp

from ...base import ClassifierMixin
from ...svm.sparse.base import SparseBaseLibLinear
from ...linear_model.sparse.base import CoefSelectTransformerMixin
from ...svm.liblinear import csr_predict_prob

class LogisticRegression(SparseBaseLibLinear, ClassifierMixin,
                         CoefSelectTransformerMixin):
    """
    Logistic Regression.

    Implements L1 and L2 regularized logistic regression.

    Parameters
    ----------

    penalty : string, 'l1' or 'l2'
        Used to specify the norm used in the penalization

    dual : boolean
        Dual or primal formulation. Dual formulation is only
        implemented for l2 penalty.

    C : float
        Specifies the strength of the regularization. The smaller it is
        the bigger in the regularization.

    fit_intercept : bool, default: True
        Specifies if a constant (a.k.a. bias or intercept) should be
        added the decision function

    intercept_scaling : float, default: 1
        when self.fit_intercept is True, instance vector x becomes
        [x, self.intercept_scaling],
        i.e. a "synthetic" feature with constant value equals to
        intercept_scaling is appended to the instance vector.
        The intercept becomes intercept_scaling * synthetic feature weight
        Note! the synthetic feature weight is subject to l1/l2 regularization
        as all other features.
        To lessen the effect of regularization on synthetic feature weight
        (and therefore on the intercept) intercept_scaling has to be increased

    tol: float, optional
         tolerance for stopping criteria

    Attributes
    ----------

    `coef_` : array, shape = [n_classes-1, n_features]
        Coefficient of the features in the decision function.

    `intercept_` : array, shape = [n_classes-1]
        intercept (a.k.a. bias) added to the decision function.
        It is available only when parameter intercept is set to True

    See also
    --------
    LinearSVC

    Notes
    -----
    The underlying C implementation uses a random number generator to
    select features when fitting the model. It is thus not uncommon,
    to have slightly different results for the same input data. If
    that happens, try with a smaller tol parameter.

    References
    ----------
    LIBLINEAR -- A Library for Large Linear Classification
    http://www.csie.ntu.edu.tw/~cjlin/liblinear/
    """

    def __init__(self, penalty='l2', dual=False, tol=1e-4, C=1.0,
                 fit_intercept=True, intercept_scaling=1):

        super(LogisticRegression, self).__init__ (penalty=penalty,
            dual=dual, loss='lr', tol=tol, C=C,
            fit_intercept=fit_intercept, intercept_scaling=intercept_scaling)

    def predict_proba(self, X):
        """
        Probability estimates.

        The returned estimates for all classes are ordered by the
        label of classes.
        """
        X = sp.csr_matrix(X)
        X.data = np.asanyarray(X.data, dtype=np.float64, order='C')
        probas = csr_predict_prob(X.shape[1], X.data, X.indices,
                                  X.indptr, self.raw_coef_,
                                  self._get_solver_type(),
                                  self.tol, self.C,
                                  self.class_weight_label,
                                  self.class_weight, self.label_,
                                  self._get_bias())
        return probas[:,np.argsort(self.label_)]

    def predict_log_proba(self, T):
        """
        Log of Probability estimates.

        The returned estimates for all classes are ordered by the
        label of classes.
        """
        return np.log(self.predict_proba(T))


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

import unittest
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
print(sys.path)
from pathlib import Path
import numpy as np
import pandas as pd
from cogstat import cogstat as cs

print(cs.__file__)
print(cs.__version__)
print(os.path.abspath(cs.__file__))

"""
- All statistical value should be tested at least once.
- All leafs of the decision tree should be tested once.
- Tests shouldn't give p<0.001 results, because exact values cannot be tested.
- No need to test the details of the statistical methods imported from other modules,
because that is the job of that specific module.
- All variables should be used with 3 digits decimal precision, to ensure that copying
the data for validation no additional rounding happens.
"""

#cs.output_type = 'do not format'

np.random.seed(555)
# https://docs.scipy.org/doc/numpy/reference/routines.random.html
# Make sure to use round function to have the same precision of the data when copied to other software
data_np = np.vstack((
    np.round(np.random.normal(loc=3, scale=3, size=30), 3),
    np.round(np.random.lognormal(mean=3, sigma=3, size=30), 3),
    np.random.randint(3, size=30),
    np.random.randint(3, size=30),
    np.round(np.random.normal(loc=3, scale=3, size=30), 3),
    np.round(np.random.lognormal(mean=1.4, sigma=0.6, size=30), 3),
    np.round(np.random.normal(loc=6, scale=3, size=30), 3),
    np.round(np.random.normal(loc=7, scale=6, size=30), 3),
    np.random.randint(2, size=30),
    np.random.randint(2, size=30),
    np.random.randint(2, size=30),
    np.concatenate((np.round(np.random.normal(loc=3, scale=3, size=15), 3),
                    np.round(np.random.normal(loc=4, scale=3, size=15), 3))),
    np.array([1]*15+[2]*15),
    np.array([1]+[2]*29),
    np.concatenate((np.round(np.random.normal(loc=3, scale=3, size=15), 3),
                    np.round(np.random.lognormal(mean=1.5, sigma=2.0, size=15), 3))),
    np.concatenate((np.round(np.random.normal(loc=3, scale=3, size=15), 3),
                    np.round(np.random.normal(loc=3, scale=7, size=15), 3))),
    np.array([1]*10+[2]*8+[3]*12),
    np.concatenate((np.round(np.random.normal(loc=3, scale=3, size=10), 3),
                    np.round(np.random.normal(loc=3, scale=3, size=8), 3),
                    np.round(np.random.normal(loc=6, scale=3, size=12), 3)))
    ))
data_pd = pd.DataFrame(data_np.T, columns=
                       ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r'])
data = cs.CogStatData(data=data_pd, measurement_levels=['int', 'int', 'nom', 'nom', 'int', 'int', 'int', 'int', 'nom',
                                                       'nom', 'nom', 'int', 'nom', 'nom', 'int', 'int', 'int', 'int'])

#pd.set_option('display.expand_frame_repr', False)
#print (data_pd)

class CogStatTestCase(unittest.TestCase):
    """Unit tests for CogStat."""

    def test_explore_variables(self):
        """Test explore variables"""

        # Int variable
        result = data.explore_variable('a', 1, 2.0)
        #for i, res in enumerate(result): print(i, res)
        self.assertTrue('N of valid cases: 30' in result[2])
        self.assertTrue('N of missing cases: 0' in result[2])
        self.assertTrue('Mean	      3.1438	' in result[4])
        self.assertTrue('Standard deviation	      3.2152	' in result[4])
        self.assertTrue('Skewness	      0.3586	' in result[4])
        self.assertTrue('Kurtosis	      0.0446	' in result[4])
        self.assertTrue('Range	      12.7840	' in result[4])
        self.assertTrue('Maximum	      9.9810	' in result[4])
        self.assertTrue('Upper quartile	      4.3875	' in result[4])
        self.assertTrue('Median	      2.8545	' in result[4])
        self.assertTrue('Lower quartile	      1.4190	' in result[4])
        self.assertTrue('Minimum	      -2.8030	' in result[4])
        # Shapiro–Wilk normality
        self.assertTrue('W = 0.96' in result[6])  # W = 0.959
        self.assertTrue('p = .287' in result[6])

        # Population estimation and one sample t-test
        self.assertTrue('Mean	      3.1438	      1.9227	      4.3649	' in result[9])
        self.assertTrue('Standard deviation	      3.2702	      2.6044	      4.3961	' in result[9])
        # Sensitivity power analysis
            # G*Power 3.1.9.6: 0.6811825
            # jamovi v1.2.19.0, jpower 0.1.2: 0.681
        self.assertTrue('(effect size is in d): 0.68' in result[11])
        self.assertTrue('t(29) = 1.92' in result[11])
        self.assertTrue('p = .065' in result[11])

        # Wilcoxon signed-rank test for non-normal interval variable
        result = data.explore_variable('b', 0, 20.0)
        self.assertTrue('T = 203' in result[11])
        self.assertTrue('p = .551' in result[11])

        # Ord variable
        data.data_measlevs['a'] = 'ord'
        result = data.explore_variable('a', 1, 2.0)
        self.assertTrue('N of valid cases: 30' in result[2])
        self.assertTrue('N of missing cases: 0' in result[2])
        self.assertTrue('Maximum	      9.9810	' in result[4])
        self.assertTrue('Upper quartile	      4.3875	' in result[4])
        self.assertTrue('Median	      2.8545	' in result[4])
        self.assertTrue('Lower quartile	      1.4190	' in result[4])
        self.assertTrue('Minimum	      -2.8030	' in result[4])
        # TODO median CI
        # Wilcoxon signed-rank test
        self.assertTrue('T = 145' in result[9])
        self.assertTrue('p = .074' in result[9])
        data.data_measlevs['a'] = 'int'

        # Nominal variable
        #result = data.explore_variable('c')
        # TODO variation ratio
        # TODO multinomial proportion CI

    def test_explore_variable_pairs(self):
        """Test explore variable pairs"""

        # Int variables
        result = data.explore_variable_pair('a', 'b')
        self.assertTrue('N of valid pairs: 30' in result[1])
        self.assertTrue('N of missing pairs: 0' in result[1])
        self.assertTrue('-0.141' in result[4])
        self.assertTrue('[-0.477, 0.231]' in result[6])
        self.assertTrue("Pearson's correlation: r(28) = -0.14, p = .456" in result[7])  # r(28) = -0.141
        self.assertTrue('y = -21.811x + 300.505' in result[3])
        self.assertTrue('-0.363' in result[4])
        self.assertTrue('[-0.640, -0.003]' in result[6])
        self.assertTrue("Spearman's rank-order correlation: rs(28) = -0.36, p = .048" in result[7])  # rs(28) = -0.363

        # Ord variables
        data.data_measlevs['a'] = 'ord'
        data.data_measlevs['b'] = 'ord'
        result = data.explore_variable_pair('a', 'b')
        self.assertTrue('-0.363' in result[4])
        self.assertTrue('[-0.640, -0.003]' in result[5])
        self.assertTrue("Spearman's rank-order correlation: rs(28) = -0.36, p = .048" in result[6])  # rs(28) = -0.363
        data.data_measlevs['a'] = 'int'
        data.data_measlevs['b'] = 'int'

        # Nom variables
        result = data.explore_variable_pair('c', 'd')
        self.assertTrue('N of valid pairs: 30' in result[1])
        self.assertTrue('N of missing pairs: 0' in result[1])
        # Cramer's V
        self.assertTrue('c = 0.372' in result[4])
        # Sensitivity power analysis
            # G*Power 3.1.9.6, Goodness of fit test, df=4: Contingency tables: 0.7868005
            #  TODO GPower gives 0.8707028 with df of 8; Seems like statsmodels GofChisquarePower calculates power
            #  with df=8; should we use 4 or 8 df? https://github.com/cogstat/cogstat/issues/134
        self.assertTrue('(effect size is in w): 0.87' in result[6])
        # Chi-squared
            # jamovi v1.2.19.0: X2, df, p, N: 8.31, 4, 0.081, 30
        self.assertTrue('(4, N = 30) = 8.31' in result[6])  # (4, N = 30) = 8.312
        self.assertTrue('p = .081' in result[6])

    def test_diffusion(self):
        """Test diffusion analysis"""
        data_diffusion = cs.CogStatData(data=str(Path('data/diffusion.csv')))
        result = data_diffusion.diffusion(error_name=['Error'], RT_name=['RT_sec'], participant_name=['Name'], condition_names=['Num1', 'Num2'])
        # Drift rate
        self.assertTrue('zsiraf	      0.190	      0.276	      0.197	      0.235	      0.213	' in result[1])
        # Threshold
        self.assertTrue('zsiraf	      0.178	      0.096	      0.171	      0.112	      0.088	' in result[1])
        # Nondecision time
        self.assertTrue('zsiraf	      0.481	      0.590	      0.483	      0.561	      0.522	' in result[1])

    def test_compare_variables(self):
        """Test compare variables"""

        # 2 Int variables
        result = data.compare_variables(['a', 'e'])
        self.assertTrue('N of valid cases: 30' in result[1])
        self.assertTrue('N of missing cases: 0' in result[1])
        # Cohen's d
            # CS formula: https://pingouin-stats.org/generated/pingouin.compute_effsize.html
            # Based on the formula, calculated in LO Calc 6.4: 0.030004573510063
            # jamovi v1.2.19.0: 0.0202; formula: https://github.com/jamovi/jmv/blob/master/R/ttestps.b.R#L54-L66
        self.assertTrue("Cohen's d	      0.030	" in result[3])
        # eta-squared
            # CS formula: https://pingouin-stats.org/generated/pingouin.convert_effsize.html
            # Based on the formula, calculated in LO Calc 6.4: 0.0002250179634
            # jamovi v1.2.19.0: 0.000
        self.assertTrue('Eta-squared	      0.000	' in result[3])
        # Sample means
        self.assertTrue('3.1438	      3.0502	' in result[3])
        # Hedges'g (with CI)
            # CS formula: https://pingouin-stats.org/generated/pingouin.compute_effsize.html
            # https://pingouin-stats.org/generated/pingouin.compute_esci.html
            # Note that the latter (CI) method has changed in v0.3.5 https://pingouin-stats.org/changelog.html
            # Based on the formula, calculated in LO Calc 7.0: 0.029614903724218, -0.34445335392457, 0.403683161373007
            # Note that the last value is 0.404 in LO, not .403 as in pingouin
        self.assertTrue("Hedges' g	      0.030	      -0.344	      0.403	" in result[5])
        self.assertTrue('W = 0.95, p = .215' in result[7])  # W = 0.954
        # Sensitivity power analysis
            # G*Power 3.1.9.6: 0.6811825
            # jamovi v1.2.19.0, jpower 0.1.2: 0.681
        self.assertTrue('(effect size is in d): 0.68' in result[7])
        # Paired samples t-test
            # jamovi v1.2.19.0: t, df, p: 0.110, 29.0, 0.913
        self.assertTrue('t(29) = 0.11, p = .913' in result[7])

        # 2 Int variables - non-normal
        result = data.compare_variables(['e', 'f'])
        self.assertTrue('W = 0.91, p = .019' in result[7])  # W = 0.915
        self.assertTrue('T = 110.00, p = .012' in result[7])

        # 3 Int variables
        result = data.compare_variables(['a', 'e', 'g'])
        self.assertTrue('3.1438	      3.0502	      5.7295	' in result[3])
        self.assertTrue('a: W = 0.96, p = .287' in result[7])  # W = 0.959
        self.assertTrue('e: W = 0.97, p = .435' in result[7])  # W = 0.966
        self.assertTrue('g: W = 0.95, p = .133' in result[7])  #x W = 0.946
        self.assertTrue('sphericity: W = 0.98, p = .703' in result[7])  # W = 0.975
        self.assertTrue('F(2, 58) = 6.17, p = .004' in result[7])
        self.assertTrue('0.11, p = .913' in result[7])  # TODO keep the order of the variables, and have a fixed sign
        self.assertTrue('3.17, p = .011' in result[7])
        self.assertTrue('2.88, p = .015' in result[7])

        # 3 Int variables, sphericity violated
        result = data.compare_variables(['a', 'e', 'h'])
        self.assertTrue('3.1438	      3.0502	      6.5786	' in result[3])
        self.assertTrue('a: W = 0.96, p = .287' in result[7])  # W = 0.959
        self.assertTrue('e: W = 0.97, p = .435' in result[7])  # W = 0.966
        self.assertTrue('h: W = 0.98, p = .824' in result[7])
        self.assertTrue('sphericity: W = 0.79, p = .039' in result[7])  # W = 0.793
        self.assertTrue('F(1.66, 48) = 6.16, p = .007' in result[7])
        self.assertTrue('0.11, p = .913' in result[7])  # TODO keep the order of the variables, and have a fixed sign
        self.assertTrue('2.68, p = .024' in result[7])
        self.assertTrue('2.81, p = .026' in result[7])

        # 3 Int variables, non-normal
        result = data.compare_variables(['a', 'e', 'f'])
        self.assertTrue('3.1438	      3.0502	      5.3681	' in result[3])
        self.assertTrue('a: W = 0.96, p = .287' in result[7])  # W = 0.959
        self.assertTrue('e: W = 0.97, p = .435' in result[7])  # W = 0.966
        self.assertTrue('f: W = 0.82, p < .001' in result[7])  # W = 0.818
        self.assertTrue('χ2(2, N = 30) = 6.47, p = .039' in result[7])

        # 2 × 2 Int variables
        result = data.compare_variables(['a', 'b', 'e', 'f'], factors=[['first', 2], ['second', 2]])
        self.assertTrue('Main effect of first: F(1, 29) = 6.06, p = .020' in result[7])
        self.assertTrue('Main effect of second: F(1, 29) = 6.29, p = .018' in result[7])
        self.assertTrue('Interaction of factors first, second: F(1, 29) = 6.04, p = .020' in result[7])

        # 2 Ord variables
        data.data_measlevs['a'] = 'ord'
        data.data_measlevs['e'] = 'ord'
        data.data_measlevs['f'] = 'ord'
        result = data.compare_variables(['e', 'f'])
        self.assertTrue('2.3895	      4.2275	' in result[3])
        self.assertTrue('T = 110.00, p = .012' in result[6])

        # 3 Ord variables
        result = data.compare_variables(['a', 'e', 'f'])
        self.assertTrue('2.8545	      2.3895	      4.2275	' in result[3])
        self.assertTrue('χ2(2, N = 30) = 6.47, p = .039' in result[6])
        data.data_measlevs['a'] = 'int'
        data.data_measlevs['e'] = 'int'
        data.data_measlevs['f'] = 'int'

        # 2 Nom variables
        result = data.compare_variables(['i', 'j'])
        # TODO on Linux the row labels are 0.0 and 1.0 instead of 0 and 1
        self.assertTrue('0.0	      4	      9	      13

      1.0	      9	' in result[3])
        self.assertTrue('χ2(1, N = 30) = 0.06, p = .814' in result[5])  # χ2(1, N = 30) = 0.0556

        # 3 Nom variables
        result = data.compare_variables(['i', 'j', 'k'])
        self.assertTrue('Q(2, N = 30) = 0.78, p = .676' in result[7])  # Q(2, N = 30) = 0.783

    def test_compare_groups(self):
        """Test compare groups"""

        # 2 Int groups
        result = data.compare_groups('l', ['m'])
        self.assertTrue('2.5316	      4.5759	' in result[3])
        # Cohen's d
            # CS formula: https://pingouin-stats.org/generated/pingouin.compute_effsize.html
            # Based on the formula, calculated in LO Calc 6.4: -0.704171924382848
            # jamovi v1.2.19.0: 0.0704
        self.assertTrue("Cohen's d	      -0.704	" in result[3])
        # eta-squared
            # CS formula: https://pingouin-stats.org/generated/pingouin.convert_effsize.html
            # Based on the formula, calculated in LO Calc 6.4: 0.110292204104377
            # jamovi v1.2.19.0: 0.117 # TODO why the difference?
        self.assertTrue('Eta-squared	      0.110	' in result[3])
        # Hedges'g (with CI)
            # CS formula: https://pingouin-stats.org/generated/pingouin.compute_effsize.html
            # https://pingouin-stats.org/generated/pingouin.compute_esci.html
            # Note that the latter (CI) method has changed in v0.3.5 https://pingouin-stats.org/changelog.html
            # Based on the formula, calculated in LO Calc 7.0: -0.685140250750879, -1.45474443187683, 0.084463930375068
        self.assertTrue('Difference between the two groups:	      -2.0443	      -4.2157	      0.1272	' in result[5])
        self.assertTrue("Hedges' g	      -0.685	      -1.455	      0.084	" in result[6])
        self.assertTrue('(m: 1.0): W = 0.96, p = .683' in result[8])  # W = 0.959
        self.assertTrue('(m: 2.0): W = 0.98, p = .991' in result[8])  # W = 0.984
        self.assertTrue('W = 0.30, p = .585' in result[8])  # W = 0.305
        # Sensitivity power analysis
            # G*Power 3.1.9.6: 1.3641059
            # jamovi v1.2.19.0, jpower 0.1.2: 1.36
        self.assertTrue('(effect size is in d): 1.36' in result[8])
        # independent samples t-test
            # jamovi v1.2.19.0: t, df, p: -1.93, 28.0, 0.064
        self.assertTrue('t(28) = -1.93, p = .064' in result[8])

        # Non-normal group
        result = data.compare_groups('o', ['m'])
        self.assertTrue('(m: 2.0): W = 0.81, p = .005' in result[8])  # W = 0.808
        self.assertTrue('U = 51.00, p = .011' in result[8])

        # Heteroscedastic groups
        result = data.compare_groups('p', ['m'])
        self.assertTrue('t(25.3) = 0.12, p = .907' in result[8])  # t(25.3) = 0.119

        # TODO single case vs. group

        # 3 Int groups
        result = data.compare_groups('r', ['q'])
        self.assertTrue('3.2869	      5.0400	      7.2412	' in result[3])
        self.assertTrue('W = 0.68, p = .517' in result[8])  # TODO this might be incorrect  # W = 0.675
        # Sensitivity power analysis
            # G*Power 3.1.9.6: 0.7597473
        self.assertTrue('(effect size is in f): 0.76' in result[8])
        self.assertTrue('F(2, 27) = 4.00, p = .030' in result[8])
        self.assertTrue('ω2 = 0.167' in result[6])
        # TODO post-hoc

        # 3 Int groups with assumption violation
        result = data.compare_groups('o', ['q'])
        self.assertTrue('χ2(2, N = 30) = 8.37, p = .015' in result[8])

        # 2 Ord groups
        data.data_measlevs['o'] = 'ord'
        result = data.compare_groups('o', ['m'])
        self.assertTrue('U = 51.00, p = .011' in result[6])

        # 3 Ord groups
        data.data_measlevs['o'] = 'ord'
        result = data.compare_groups('o', ['q'])
        self.assertTrue('χ2(2, N = 30) = 8.37, p = .015' in result[6])
        data.data_measlevs['o'] = 'int'

        # 2 Nom groups
        result = data.compare_groups('i', ['j'])
        self.assertTrue('φc = 0.154' in result[3])  # TODO validate
        self.assertTrue('χ2(1, N = 30) = 0.71, p = .399' in result[5])  # TODO validate  # χ2(1, N = 30) = 0.710

        # 3 Nom groups
        result = data.compare_groups('i', ['c'])
        self.assertTrue('φc = 0.009' in result[3])  # TODO validate
        self.assertTrue('χ2(2, N = 30) = 0.00, p = .999' in result[5])  # TODO validate  # χ2(2, N = 30) = 0.002

        # 3 × 3 Int groups
        result = data.compare_groups('a', ['c', 'd'])
        self.assertTrue('Mean	      1.0695	      1.8439	      2.3693	' in result[3])
        self.assertTrue('Standard deviation	      2.7005	      2.0891	      4.2610	' in result[3])
        self.assertTrue('Maximum	      4.4130	      4.7890	      9.1600	' in result[3])
        self.assertTrue('Upper quartile	      3.0000	      3.0213	      4.4028	' in result[3])
        self.assertTrue('Median	      1.3340	      2.4590	      0.9015	' in result[3])
        self.assertTrue('Lower quartile	      -0.5965	      0.8870	      -1.1320	' in result[3])
        self.assertTrue('Minimum	      -2.8030	      -2.2890	      -1.4860	' in result[3])
        # TODO the two main effects differ from the SPSS result, see issue #91
        self.assertTrue('F(2, 21) = 2.35, p = .120' in result[7])
        self.assertTrue('F(2, 21) = 0.19, p = .832' in result[7])  # F(2, 21) = 0.185
        self.assertTrue('F(4, 21) = 1.15, p = .363' in result[7])

    def test_single_case(self):

        # Test for the slope stat
        data = cs.CogStatData(data='''group	slope	slope_SE
Patient	0.247	0.069
Control	0.492	0.106
Control	0.559	0.108
Control	0.63	0.116
Control	0.627	0.065
Control	0.674	0.105
Control	0.538	0.107''')
        result = data.compare_groups('slope', ['group'], 'slope_SE', 25)
        self.assertTrue('Test d.2: t(42.1) = -4.21, p < .001' in result[8])
        result = data.compare_groups('slope', ['group'])
        self.assertTrue('t(5) = -5.05, p = .004' in result[8])

if __name__ == '__main__':
    unittest.main()


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

import numpy as np
import pandas as pd
from sklearn.datasets.base import Bunch
from os.path import join

PATH = "d:\\data"

# class Bunch(dict):
#     """Container object for datasets
#     Dictionary-like object that exposes its keys as attributes.
#
#     See: sklearn.datasets.base.py Bunch
#     """
#
#     def __init__(self, **kwargs):
#         super(Bunch, self).__init__(kwargs)
#
#     def __setattr__(self, key, value):
#         self[key] = value
#
#     def __dir__(self):
#         return self.keys()
#
#     def __getattr__(self, key):
#         try:
#             return self[key]
#         except KeyError:
#             raise AttributeError(key)
#
#     def __setstate__(self, state):
#         # Bunch pickles generated with scikit-learn 0.16.* have an non
#         # empty __dict__. This causes a surprising behaviour when
#         # loading these pickles scikit-learn 0.17: reading bunch.key
#         # uses __dict__ but assigning to bunch.key use __setattr__ and
#         # only changes bunch['key']. More details can be found at:
#         # https://github.com/scikit-learn/scikit-learn/issues/6196.
#         # Overriding __setstate__ to be a noop has the effect of
#         # ignoring the pickled __dict__
#         pass

def parse_date(x):
    return pd.datetime.strptime(x, '%Y-%m-%d')

def load_pcs_data():
    # column: date,pcs,f1,f2,...
    #  sep='\001',
    df = pd.read_csv(join(PATH, 'spu_pcs_20170721.csv'), sep='\001', parse_dates=['date'], date_parser=parse_date)
    df.sort_values(by='date')
    columns = np.array(df.columns.values)
    feature_name = columns[2:]
    tmp_data = np.array(df)
    inx_data = tmp_data[:, 0]
    target = tmp_data[:, 1]
    data = tmp_data[:, 2:]

    # print shape
    print data.shape
    print feature_name

    return Bunch(data=data, target=target, feature_names=feature_name, inx=inx_data)

if __name__ == '__main__':
    load_pcs_data()


# coding: utf-8

""" Test cases for Series.plot """

import itertools
import pytest

from datetime import datetime

import pandas as pd
from pandas import Series, DataFrame, date_range
from pandas.compat import range, lrange
import pandas.util.testing as tm

import numpy as np
from numpy.random import randn

import pandas.plotting as plotting
from pandas.tests.plotting.common import (TestPlotBase, _check_plot_works,
                                          _skip_if_no_scipy_gaussian_kde,
                                          _ok_for_gaussian_kde)

tm._skip_if_no_mpl()

class TestSeriesPlots(TestPlotBase):

    def setup_method(self, method):
        TestPlotBase.setup_method(self, method)
        import matplotlib as mpl
        mpl.rcdefaults()

        self.ts = tm.makeTimeSeries()
        self.ts.name = 'ts'

        self.series = tm.makeStringSeries()
        self.series.name = 'series'

        self.iseries = tm.makePeriodSeries()
        self.iseries.name = 'iseries'

    @pytest.mark.slow
    def test_plot(self):
        _check_plot_works(self.ts.plot, label='foo')
        _check_plot_works(self.ts.plot, use_index=False)
        axes = _check_plot_works(self.ts.plot, rot=0)
        self._check_ticks_props(axes, xrot=0)

        ax = _check_plot_works(self.ts.plot, style='.', logy=True)
        self._check_ax_scales(ax, yaxis='log')

        ax = _check_plot_works(self.ts.plot, style='.', logx=True)
        self._check_ax_scales(ax, xaxis='log')

        ax = _check_plot_works(self.ts.plot, style='.', loglog=True)
        self._check_ax_scales(ax, xaxis='log', yaxis='log')

        _check_plot_works(self.ts[:10].plot.bar)
        _check_plot_works(self.ts.plot.area, stacked=False)
        _check_plot_works(self.iseries.plot)

        for kind in ['line', 'bar', 'barh', 'kde', 'hist', 'box']:
            if not _ok_for_gaussian_kde(kind):
                continue
            _check_plot_works(self.series[:5].plot, kind=kind)

        _check_plot_works(self.series[:10].plot.barh)
        ax = _check_plot_works(Series(randn(10)).plot.bar, color='black')
        self._check_colors([ax.patches[0]], facecolors=['black'])

        # GH 6951
        ax = _check_plot_works(self.ts.plot, subplots=True)
        self._check_axes_shape(ax, axes_num=1, layout=(1, 1))

        ax = _check_plot_works(self.ts.plot, subplots=True, layout=(-1, 1))
        self._check_axes_shape(ax, axes_num=1, layout=(1, 1))
        ax = _check_plot_works(self.ts.plot, subplots=True, layout=(1, -1))
        self._check_axes_shape(ax, axes_num=1, layout=(1, 1))

    @pytest.mark.slow
    def test_plot_figsize_and_title(self):
        # figsize and title
        _, ax = self.plt.subplots()
        ax = self.series.plot(title='Test', figsize=(16, 8), ax=ax)
        self._check_text_labels(ax.title, 'Test')
        self._check_axes_shape(ax, axes_num=1, layout=(1, 1), figsize=(16, 8))

    def test_dont_modify_rcParams(self):
        # GH 8242
        if self.mpl_ge_1_5_0:
            key = 'axes.prop_cycle'
        else:
            key = 'axes.color_cycle'
        colors = self.plt.rcParams[key]
        _, ax = self.plt.subplots()
        Series([1, 2, 3]).plot(ax=ax)
        assert colors == self.plt.rcParams[key]

    def test_ts_line_lim(self):
        fig, ax = self.plt.subplots()
        ax = self.ts.plot(ax=ax)
        xmin, xmax = ax.get_xlim()
        lines = ax.get_lines()
        assert xmin == lines[0].get_data(orig=False)[0][0]
        assert xmax == lines[0].get_data(orig=False)[0][-1]
        tm.close()

        ax = self.ts.plot(secondary_y=True, ax=ax)
        xmin, xmax = ax.get_xlim()
        lines = ax.get_lines()
        assert xmin == lines[0].get_data(orig=False)[0][0]
        assert xmax == lines[0].get_data(orig=False)[0][-1]

    def test_ts_area_lim(self):
        _, ax = self.plt.subplots()
        ax = self.ts.plot.area(stacked=False, ax=ax)
        xmin, xmax = ax.get_xlim()
        line = ax.get_lines()[0].get_data(orig=False)[0]
        assert xmin == line[0]
        assert xmax == line[-1]
        tm.close()

        # GH 7471
        _, ax = self.plt.subplots()
        ax = self.ts.plot.area(stacked=False, x_compat=True, ax=ax)
        xmin, xmax = ax.get_xlim()
        line = ax.get_lines()[0].get_data(orig=False)[0]
        assert xmin == line[0]
        assert xmax == line[-1]
        tm.close()

        tz_ts = self.ts.copy()
        tz_ts.index = tz_ts.tz_localize('GMT').tz_convert('CET')
        _, ax = self.plt.subplots()
        ax = tz_ts.plot.area(stacked=False, x_compat=True, ax=ax)
        xmin, xmax = ax.get_xlim()
        line = ax.get_lines()[0].get_data(orig=False)[0]
        assert xmin == line[0]
        assert xmax == line[-1]
        tm.close()

        _, ax = self.plt.subplots()
        ax = tz_ts.plot.area(stacked=False, secondary_y=True, ax=ax)
        xmin, xmax = ax.get_xlim()
        line = ax.get_lines()[0].get_data(orig=False)[0]
        assert xmin == line[0]
        assert xmax == line[-1]

    def test_label(self):
        s = Series([1, 2])
        _, ax = self.plt.subplots()
        ax = s.plot(label='LABEL', legend=True, ax=ax)
        self._check_legend_labels(ax, labels=['LABEL'])
        self.plt.close()
        _, ax = self.plt.subplots()
        ax = s.plot(legend=True, ax=ax)
        self._check_legend_labels(ax, labels=['None'])
        self.plt.close()
        # get name from index
        s.name = 'NAME'
        _, ax = self.plt.subplots()
        ax = s.plot(legend=True, ax=ax)
        self._check_legend_labels(ax, labels=['NAME'])
        self.plt.close()
        # override the default
        _, ax = self.plt.subplots()
        ax = s.plot(legend=True, label='LABEL', ax=ax)
        self._check_legend_labels(ax, labels=['LABEL'])
        self.plt.close()
        # Add lebel info, but don't draw
        _, ax = self.plt.subplots()
        ax = s.plot(legend=False, label='LABEL', ax=ax)
        assert ax.get_legend() is None  # Hasn't been drawn
        ax.legend()  # draw it
        self._check_legend_labels(ax, labels=['LABEL'])

    def test_line_area_nan_series(self):
        values = [1, 2, np.nan, 3]
        s = Series(values)
        ts = Series(values, index=tm.makeDateIndex(k=4))

        for d in [s, ts]:
            ax = _check_plot_works(d.plot)
            masked = ax.lines[0].get_ydata()
            # remove nan for comparison purpose
            exp = np.array([1, 2, 3], dtype=np.float64)
            tm.assert_numpy_array_equal(np.delete(masked.data, 2), exp)
            tm.assert_numpy_array_equal(
                masked.mask, np.array([False, False, True, False]))

            expected = np.array([1, 2, 0, 3], dtype=np.float64)
            ax = _check_plot_works(d.plot, stacked=True)
            tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected)
            ax = _check_plot_works(d.plot.area)
            tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected)
            ax = _check_plot_works(d.plot.area, stacked=False)
            tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected)

    def test_line_use_index_false(self):
        s = Series([1, 2, 3], index=['a', 'b', 'c'])
        s.index.name = 'The Index'
        _, ax = self.plt.subplots()
        ax = s.plot(use_index=False, ax=ax)
        label = ax.get_xlabel()
        assert label == ''
        _, ax = self.plt.subplots()
        ax2 = s.plot.bar(use_index=False, ax=ax)
        label2 = ax2.get_xlabel()
        assert label2 == ''

    @pytest.mark.slow
    def test_bar_log(self):
        expected = np.array([1., 10., 100., 1000.])

        if not self.mpl_le_1_2_1:
            expected = np.hstack((.1, expected, 1e4))

        _, ax = self.plt.subplots()
        ax = Series([200, 500]).plot.bar(log=True, ax=ax)
        tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), expected)
        tm.close()

        _, ax = self.plt.subplots()
        ax = Series([200, 500]).plot.barh(log=True, ax=ax)
        tm.assert_numpy_array_equal(ax.xaxis.get_ticklocs(), expected)
        tm.close()

        # GH 9905
        expected = np.array([1.0e-03, 1.0e-02, 1.0e-01, 1.0e+00])

        if not self.mpl_le_1_2_1:
            expected = np.hstack((1.0e-04, expected, 1.0e+01))
        if self.mpl_ge_2_0_0:
            expected = np.hstack((1.0e-05, expected))

        _, ax = self.plt.subplots()
        ax = Series([0.1, 0.01, 0.001]).plot(log=True, kind='bar', ax=ax)
        ymin = 0.0007943282347242822 if self.mpl_ge_2_0_0 else 0.001
        ymax = 0.12589254117941673 if self.mpl_ge_2_0_0 else .10000000000000001
        res = ax.get_ylim()
        tm.assert_almost_equal(res[0], ymin)
        tm.assert_almost_equal(res[1], ymax)
        tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), expected)
        tm.close()

        _, ax = self.plt.subplots()
        ax = Series([0.1, 0.01, 0.001]).plot(log=True, kind='barh', ax=ax)
        res = ax.get_xlim()
        tm.assert_almost_equal(res[0], ymin)
        tm.assert_almost_equal(res[1], ymax)
        tm.assert_numpy_array_equal(ax.xaxis.get_ticklocs(), expected)

    @pytest.mark.slow
    def test_bar_ignore_index(self):
        df = Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
        _, ax = self.plt.subplots()
        ax = df.plot.bar(use_index=False, ax=ax)
        self._check_text_labels(ax.get_xticklabels(), ['0', '1', '2', '3'])

    def test_rotation(self):
        df = DataFrame(randn(5, 5))
        # Default rot 0
        _, ax = self.plt.subplots()
        axes = df.plot(ax=ax)
        self._check_ticks_props(axes, xrot=0)

        _, ax = self.plt.subplots()
        axes = df.plot(rot=30, ax=ax)
        self._check_ticks_props(axes, xrot=30)

    def test_irregular_datetime(self):
        rng = date_range('1/1/2000', '3/1/2000')
        rng = rng[[0, 1, 2, 3, 5, 9, 10, 11, 12]]
        ser = Series(randn(len(rng)), rng)
        _, ax = self.plt.subplots()
        ax = ser.plot(ax=ax)
        xp = datetime(1999, 1, 1).toordinal()
        ax.set_xlim('1/1/1999', '1/1/2001')
        assert xp == ax.get_xlim()[0]

    @pytest.mark.slow
    def test_pie_series(self):
        # if sum of values is less than 1.0, pie handle them as rate and draw
        # semicircle.
        series = Series(np.random.randint(1, 5),
                        index=['a', 'b', 'c', 'd', 'e'], name='YLABEL')
        ax = _check_plot_works(series.plot.pie)
        self._check_text_labels(ax.texts, series.index)
        assert ax.get_ylabel() == 'YLABEL'

        # without wedge labels
        ax = _check_plot_works(series.plot.pie, labels=None)
        self._check_text_labels(ax.texts, [''] * 5)

        # with less colors than elements
        color_args = ['r', 'g', 'b']
        ax = _check_plot_works(series.plot.pie, colors=color_args)

        color_expected = ['r', 'g', 'b', 'r', 'g']
        self._check_colors(ax.patches, facecolors=color_expected)

        # with labels and colors
        labels = ['A', 'B', 'C', 'D', 'E']
        color_args = ['r', 'g', 'b', 'c', 'm']
        ax = _check_plot_works(series.plot.pie, labels=labels,
                               colors=color_args)
        self._check_text_labels(ax.texts, labels)
        self._check_colors(ax.patches, facecolors=color_args)

        # with autopct and fontsize
        ax = _check_plot_works(series.plot.pie, colors=color_args,
                               autopct='%.2f', fontsize=7)
        pcts = ['{0:.2f}'.format(s * 100)
                for s in series.values / float(series.sum())]
        iters = [iter(series.index), iter(pcts)]
        expected_texts = list(next(it) for it in itertools.cycle(iters))
        self._check_text_labels(ax.texts, expected_texts)
        for t in ax.texts:
            assert t.get_fontsize() == 7

        # includes negative value
        with pytest.raises(ValueError):
            series = Series([1, 2, 0, 4, -1], index=['a', 'b', 'c', 'd', 'e'])
            series.plot.pie()

        # includes nan
        series = Series([1, 2, np.nan, 4], index=['a', 'b', 'c', 'd'],
                        name='YLABEL')
        ax = _check_plot_works(series.plot.pie)
        self._check_text_labels(ax.texts, ['a', 'b', '', 'd'])

    def test_pie_nan(self):
        s = Series([1, np.nan, 1, 1])
        _, ax = self.plt.subplots()
        ax = s.plot.pie(legend=True, ax=ax)
        expected = ['0', '', '2', '3']
        result = [x.get_text() for x in ax.texts]
        assert result == expected

    @pytest.mark.slow
    def test_hist_df_kwargs(self):
        df = DataFrame(np.random.randn(10, 2))
        _, ax = self.plt.subplots()
        ax = df.plot.hist(bins=5, ax=ax)
        assert len(ax.patches) == 10

    @pytest.mark.slow
    def test_hist_df_with_nonnumerics(self):
        # GH 9853
        with tm.RNGContext(1):
            df = DataFrame(
                np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
        df['E'] = ['x', 'y'] * 5
        _, ax = self.plt.subplots()
        ax = df.plot.hist(bins=5, ax=ax)
        assert len(ax.patches) == 20

        _, ax = self.plt.subplots()
        ax = df.plot.hist(ax=ax)  # bins=10
        assert len(ax.patches) == 40

    @pytest.mark.slow
    def test_hist_legacy(self):
        _check_plot_works(self.ts.hist)
        _check_plot_works(self.ts.hist, grid=False)
        _check_plot_works(self.ts.hist, figsize=(8, 10))
        # _check_plot_works adds an ax so catch warning. see GH #13188
        with tm.assert_produces_warning(UserWarning):
            _check_plot_works(self.ts.hist,
                              by=self.ts.index.month)
        with tm.assert_produces_warning(UserWarning):
            _check_plot_works(self.ts.hist,
                              by=self.ts.index.month, bins=5)

        fig, ax = self.plt.subplots(1, 1)
        _check_plot_works(self.ts.hist, ax=ax)
        _check_plot_works(self.ts.hist, ax=ax, figure=fig)
        _check_plot_works(self.ts.hist, figure=fig)
        tm.close()

        fig, (ax1, ax2) = self.plt.subplots(1, 2)
        _check_plot_works(self.ts.hist, figure=fig, ax=ax1)
        _check_plot_works(self.ts.hist, figure=fig, ax=ax2)

        with pytest.raises(ValueError):
            self.ts.hist(by=self.ts.index, figure=fig)

    @pytest.mark.slow
    def test_hist_bins_legacy(self):
        df = DataFrame(np.random.randn(10, 2))
        ax = df.hist(bins=2)[0][0]
        assert len(ax.patches) == 2

    @pytest.mark.slow
    def test_hist_layout(self):
        df = self.hist_df
        with pytest.raises(ValueError):
            df.height.hist(layout=(1, 1))

        with pytest.raises(ValueError):
            df.height.hist(layout=[1, 1])

    @pytest.mark.slow
    def test_hist_layout_with_by(self):
        df = self.hist_df

        # _check_plot_works adds an ax so catch warning. see GH #13188
        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.gender, layout=(2, 1))
        self._check_axes_shape(axes, axes_num=2, layout=(2, 1))

        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.gender, layout=(3, -1))
        self._check_axes_shape(axes, axes_num=2, layout=(3, 1))

        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.category, layout=(4, 1))
        self._check_axes_shape(axes, axes_num=4, layout=(4, 1))

        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.category, layout=(2, -1))
        self._check_axes_shape(axes, axes_num=4, layout=(2, 2))

        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.category, layout=(3, -1))
        self._check_axes_shape(axes, axes_num=4, layout=(3, 2))

        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.category, layout=(-1, 4))
        self._check_axes_shape(axes, axes_num=4, layout=(1, 4))

        with tm.assert_produces_warning(UserWarning):
            axes = _check_plot_works(df.height.hist,
                                     by=df.classroom, layout=(2, 2))
        self._check_axes_shape(axes, axes_num=3, layout=(2, 2))

        axes = df.height.hist(by=df.category, layout=(4, 2), figsize=(12, 7))
        self._check_axes_shape(axes, axes_num=4, layout=(4, 2),
                               figsize=(12, 7))

    @pytest.mark.slow
    def test_hist_no_overlap(self):
        from matplotlib.pyplot import subplot, gcf
        x = Series(randn(2))
        y = Series(randn(2))
        subplot(121)
        x.hist()
        subplot(122)
        y.hist()
        fig = gcf()
        axes = fig.axes if self.mpl_ge_1_5_0 else fig.get_axes()
        assert len(axes) == 2

    @pytest.mark.slow
    def test_hist_secondary_legend(self):
        # GH 9610
        df = DataFrame(np.random.randn(30, 4), columns=list('abcd'))

        # primary -> secondary
        _, ax = self.plt.subplots()
        ax = df['a'].plot.hist(legend=True, ax=ax)
        df['b'].plot.hist(ax=ax, legend=True, secondary_y=True)
        # both legends are dran on left ax
        # left and right axis must be visible
        self._check_legend_labels(ax, labels=['a', 'b (right)'])
        assert ax.get_yaxis().get_visible()
        assert ax.right_ax.get_yaxis().get_visible()
        tm.close()

        # secondary -> secondary
        _, ax = self.plt.subplots()
        ax = df['a'].plot.hist(legend=True, secondary_y=True, ax=ax)
        df['b'].plot.hist(ax=ax, legend=True, secondary_y=True)
        # both legends are draw on left ax
        # left axis must be invisible, right axis must be visible
        self._check_legend_labels(ax.left_ax,
                                  labels=['a (right)', 'b (right)'])
        assert not ax.left_ax.get_yaxis().get_visible()
        assert ax.get_yaxis().get_visible()
        tm.close()

        # secondary -> primary
        _, ax = self.plt.subplots()
        ax = df['a'].plot.hist(legend=True, secondary_y=True, ax=ax)
        # right axes is returned
        df['b'].plot.hist(ax=ax, legend=True)
        # both legends are draw on left ax
        # left and right axis must be visible
        self._check_legend_labels(ax.left_ax, labels=['a (right)', 'b'])
        assert ax.left_ax.get_yaxis().get_visible()
        assert ax.get_yaxis().get_visible()
        tm.close()

    @pytest.mark.slow
    def test_df_series_secondary_legend(self):
        # GH 9779
        df = DataFrame(np.random.randn(30, 3), columns=list('abc'))
        s = Series(np.random.randn(30), name='x')

        # primary -> secondary (without passing ax)
        _, ax = self.plt.subplots()
        ax = df.plot(ax=ax)
        s.plot(legend=True, secondary_y=True, ax=ax)
        # both legends are dran on left ax
        # left and right axis must be visible
        self._check_legend_labels(ax, labels=['a', 'b', 'c', 'x (right)'])
        assert ax.get_yaxis().get_visible()
        assert ax.right_ax.get_yaxis().get_visible()
        tm.close()

        # primary -> secondary (with passing ax)
        _, ax = self.plt.subplots()
        ax = df.plot(ax=ax)
        s.plot(ax=ax, legend=True, secondary_y=True)
        # both legends are dran on left ax
        # left and right axis must be visible
        self._check_legend_labels(ax, labels=['a', 'b', 'c', 'x (right)'])
        assert ax.get_yaxis().get_visible()
        assert ax.right_ax.get_yaxis().get_visible()
        tm.close()

        # seconcary -> secondary (without passing ax)
        _, ax = self.plt.subplots()
        ax = df.plot(secondary_y=True, ax=ax)
        s.plot(legend=True, secondary_y=True, ax=ax)
        # both legends are dran on left ax
        # left axis must be invisible and right axis must be visible
        expected = ['a (right)', 'b (right)', 'c (right)', 'x (right)']
        self._check_legend_labels(ax.left_ax, labels=expected)
        assert not ax.left_ax.get_yaxis().get_visible()
        assert ax.get_yaxis().get_visible()
        tm.close()

        # secondary -> secondary (with passing ax)
        _, ax = self.plt.subplots()
        ax = df.plot(secondary_y=True, ax=ax)
        s.plot(ax=ax, legend=True, secondary_y=True)
        # both legends are dran on left ax
        # left axis must be invisible and right axis must be visible
        expected = ['a (right)', 'b (right)', 'c (right)', 'x (right)']
        self._check_legend_labels(ax.left_ax, expected)
        assert not ax.left_ax.get_yaxis().get_visible()
        assert ax.get_yaxis().get_visible()
        tm.close()

        # secondary -> secondary (with passing ax)
        _, ax = self.plt.subplots()
        ax = df.plot(secondary_y=True, mark_right=False, ax=ax)
        s.plot(ax=ax, legend=True, secondary_y=True)
        # both legends are dran on left ax
        # left axis must be invisible and right axis must be visible
        expected = ['a', 'b', 'c', 'x (right)']
        self._check_legend_labels(ax.left_ax, expected)
        assert not ax.left_ax.get_yaxis().get_visible()
        assert ax.get_yaxis().get_visible()
        tm.close()

    @pytest.mark.slow
    def test_plot_fails_with_dupe_color_and_style(self):
        x = Series(randn(2))
        with pytest.raises(ValueError):
            _, ax = self.plt.subplots()
            x.plot(style='k--', color='k', ax=ax)

    @pytest.mark.slow
    def test_hist_kde(self):
        _, ax = self.plt.subplots()
        ax = self.ts.plot.hist(logy=True, ax=ax)
        self._check_ax_scales(ax, yaxis='log')
        xlabels = ax.get_xticklabels()
        # ticks are values, thus ticklabels are blank
        self._check_text_labels(xlabels, [''] * len(xlabels))
        ylabels = ax.get_yticklabels()
        self._check_text_labels(ylabels, [''] * len(ylabels))

        tm._skip_if_no_scipy()
        _skip_if_no_scipy_gaussian_kde()
        _check_plot_works(self.ts.plot.kde)
        _check_plot_works(self.ts.plot.density)
        _, ax = self.plt.subplots()
        ax = self.ts.plot.kde(logy=True, ax=ax)
        self._check_ax_scales(ax, yaxis='log')
        xlabels = ax.get_xticklabels()
        self._check_text_labels(xlabels, [''] * len(xlabels))
        ylabels = ax.get_yticklabels()
        self._check_text_labels(ylabels, [''] * len(ylabels))

    @pytest.mark.slow
    def test_kde_kwargs(self):
        tm._skip_if_no_scipy()
        _skip_if_no_scipy_gaussian_kde()
        from numpy import linspace
        _check_plot_works(self.ts.plot.kde, bw_method=.5,
                          ind=linspace(-100, 100, 20))
        _check_plot_works(self.ts.plot.density, bw_method=.5,
                          ind=linspace(-100, 100, 20))
        _, ax = self.plt.subplots()
        ax = self.ts.plot.kde(logy=True, bw_method=.5,
                              ind=linspace(-100, 100, 20), ax=ax)
        self._check_ax_scales(ax, yaxis='log')
        self._check_text_labels(ax.yaxis.get_label(), 'Density')

    @pytest.mark.slow
    def test_kde_missing_vals(self):
        tm._skip_if_no_scipy()
        _skip_if_no_scipy_gaussian_kde()
        s = Series(np.random.uniform(size=50))
        s[0] = np.nan
        axes = _check_plot_works(s.plot.kde)

        # gh-14821: check if the values have any missing values
        assert any(~np.isnan(axes.lines[0].get_xdata()))

    @pytest.mark.slow
    def test_hist_kwargs(self):
        _, ax = self.plt.subplots()
        ax = self.ts.plot.hist(bins=5, ax=ax)
        assert len(ax.patches) == 5
        self._check_text_labels(ax.yaxis.get_label(), 'Frequency')
        tm.close()

        if self.mpl_ge_1_3_1:
            _, ax = self.plt.subplots()
            ax = self.ts.plot.hist(orientation='horizontal', ax=ax)
            self._check_text_labels(ax.xaxis.get_label(), 'Frequency')
            tm.close()

            _, ax = self.plt.subplots()
            ax = self.ts.plot.hist(align='left', stacked=True, ax=ax)
            tm.close()

    @pytest.mark.slow
    def test_hist_kde_color(self):
        _, ax = self.plt.subplots()
        ax = self.ts.plot.hist(logy=True, bins=10, color='b', ax=ax)
        self._check_ax_scales(ax, yaxis='log')
        assert len(ax.patches) == 10
        self._check_colors(ax.patches, facecolors=['b'] * 10)

        tm._skip_if_no_scipy()
        _skip_if_no_scipy_gaussian_kde()
        _, ax = self.plt.subplots()
        ax = self.ts.plot.kde(logy=True, color='r', ax=ax)
        self._check_ax_scales(ax, yaxis='log')
        lines = ax.get_lines()
        assert len(lines) == 1
        self._check_colors(lines, ['r'])

    @pytest.mark.slow
    def test_boxplot_series(self):
        _, ax = self.plt.subplots()
        ax = self.ts.plot.box(logy=True, ax=ax)
        self._check_ax_scales(ax, yaxis='log')
        xlabels = ax.get_xticklabels()
        self._check_text_labels(xlabels, [self.ts.name])
        ylabels = ax.get_yticklabels()
        self._check_text_labels(ylabels, [''] * len(ylabels))

    @pytest.mark.slow
    def test_kind_both_ways(self):
        s = Series(range(3))
        kinds = (plotting._core._common_kinds +
                 plotting._core._series_kinds)
        _, ax = self.plt.subplots()
        for kind in kinds:
            if not _ok_for_gaussian_kde(kind):
                continue
            s.plot(kind=kind, ax=ax)
            getattr(s.plot, kind)()

    @pytest.mark.slow
    def test_invalid_plot_data(self):
        s = Series(list('abcd'))
        _, ax = self.plt.subplots()
        for kind in plotting._core._common_kinds:
            if not _ok_for_gaussian_kde(kind):
                continue
            with pytest.raises(TypeError):
                s.plot(kind=kind, ax=ax)

    @pytest.mark.slow
    def test_valid_object_plot(self):
        s = Series(lrange(10), dtype=object)
        for kind in plotting._core._common_kinds:
            if not _ok_for_gaussian_kde(kind):
                continue
            _check_plot_works(s.plot, kind=kind)

    def test_partially_invalid_plot_data(self):
        s = Series(['a', 'b', 1.0, 2])
        _, ax = self.plt.subplots()
        for kind in plotting._core._common_kinds:
            if not _ok_for_gaussian_kde(kind):
                continue
            with pytest.raises(TypeError):
                s.plot(kind=kind, ax=ax)

    def test_invalid_kind(self):
        s = Series([1, 2])
        with pytest.raises(ValueError):
            s.plot(kind='aasdf')

    @pytest.mark.slow
    def test_dup_datetime_index_plot(self):
        dr1 = date_range('1/1/2009', periods=4)
        dr2 = date_range('1/2/2009', periods=4)
        index = dr1.append(dr2)
        values = randn(index.size)
        s = Series(values, index=index)
        _check_plot_works(s.plot)

    @pytest.mark.slow
    def test_errorbar_plot(self):

        s = Series(np.arange(10), name='x')
        s_err = np.random.randn(10)
        d_err = DataFrame(randn(10, 2), index=s.index, columns=['x', 'y'])
        # test line and bar plots
        kinds = ['line', 'bar']
        for kind in kinds:
            ax = _check_plot_works(s.plot, yerr=Series(s_err), kind=kind)
            self._check_has_errorbars(ax, xerr=0, yerr=1)
            ax = _check_plot_works(s.plot, yerr=s_err, kind=kind)
            self._check_has_errorbars(ax, xerr=0, yerr=1)
            ax = _check_plot_works(s.plot, yerr=s_err.tolist(), kind=kind)
            self._check_has_errorbars(ax, xerr=0, yerr=1)
            ax = _check_plot_works(s.plot, yerr=d_err, kind=kind)
            self._check_has_errorbars(ax, xerr=0, yerr=1)
            ax = _check_plot_works(s.plot, xerr=0.2, yerr=0.2, kind=kind)
            self._check_has_errorbars(ax, xerr=1, yerr=1)

        ax = _check_plot_works(s.plot, xerr=s_err)
        self._check_has_errorbars(ax, xerr=1, yerr=0)

        # test time series plotting
        ix = date_range('1/1/2000', '1/1/2001', freq='M')
        ts = Series(np.arange(12), index=ix, name='x')
        ts_err = Series(np.random.randn(12), index=ix)
        td_err = DataFrame(randn(12, 2), index=ix, columns=['x', 'y'])

        ax = _check_plot_works(ts.plot, yerr=ts_err)
        self._check_has_errorbars(ax, xerr=0, yerr=1)
        ax = _check_plot_works(ts.plot, yerr=td_err)
        self._check_has_errorbars(ax, xerr=0, yerr=1)

        # check incorrect lengths and types
        with pytest.raises(ValueError):
            s.plot(yerr=np.arange(11))

        s_err = ['zzz'] * 10
        # in mpl 1.5+ this is a TypeError
        with pytest.raises((ValueError, TypeError)):
            s.plot(yerr=s_err)

    def test_table(self):
        _check_plot_works(self.series.plot, table=True)
        _check_plot_works(self.series.plot, table=self.series)

    @pytest.mark.slow
    def test_series_grid_settings(self):
        # Make sure plot defaults to rcParams['axes.grid'] setting, GH 9792
        self._check_grid_settings(Series([1, 2, 3]),
                                  plotting._core._series_kinds +
                                  plotting._core._common_kinds)

    @pytest.mark.slow
    def test_standard_colors(self):
        from pandas.plotting._style import _get_standard_colors

        for c in ['r', 'red', 'green', '#FF0000']:
            result = _get_standard_colors(1, color=c)
            assert result == [c]

            result = _get_standard_colors(1, color=[c])
            assert result == [c]

            result = _get_standard_colors(3, color=c)
            assert result == [c] * 3

            result = _get_standard_colors(3, color=[c])
            assert result == [c] * 3

    @pytest.mark.slow
    def test_standard_colors_all(self):
        import matplotlib.colors as colors
        from pandas.plotting._style import _get_standard_colors

        # multiple colors like mediumaquamarine
        for c in colors.cnames:
            result = _get_standard_colors(num_colors=1, color=c)
            assert result == [c]

            result = _get_standard_colors(num_colors=1, color=[c])
            assert result == [c]

            result = _get_standard_colors(num_colors=3, color=c)
            assert result == [c] * 3

            result = _get_standard_colors(num_colors=3, color=[c])
            assert result == [c] * 3

        # single letter colors like k
        for c in colors.ColorConverter.colors:
            result = _get_standard_colors(num_colors=1, color=c)
            assert result == [c]

            result = _get_standard_colors(num_colors=1, color=[c])
            assert result == [c]

            result = _get_standard_colors(num_colors=3, color=c)
            assert result == [c] * 3

            result = _get_standard_colors(num_colors=3, color=[c])
            assert result == [c] * 3

    def test_series_plot_color_kwargs(self):
        # GH1890
        _, ax = self.plt.subplots()
        ax = Series(np.arange(12) + 1).plot(color='green', ax=ax)
        self._check_colors(ax.get_lines(), linecolors=['green'])

    def test_time_series_plot_color_kwargs(self):
        # #1890
        _, ax = self.plt.subplots()
        ax = Series(np.arange(12) + 1, index=date_range(
            '1/1/2000', periods=12)).plot(color='green', ax=ax)
        self._check_colors(ax.get_lines(), linecolors=['green'])

    def test_time_series_plot_color_with_empty_kwargs(self):
        import matplotlib as mpl

        if self.mpl_ge_1_5_0:
            def_colors = self._maybe_unpack_cycler(mpl.rcParams)
        else:
            def_colors = mpl.rcParams['axes.color_cycle']
        index = date_range('1/1/2000', periods=12)
        s = Series(np.arange(1, 13), index=index)

        ncolors = 3

        _, ax = self.plt.subplots()
        for i in range(ncolors):
            ax = s.plot(ax=ax)
        self._check_colors(ax.get_lines(), linecolors=def_colors[:ncolors])

    def test_xticklabels(self):
        # GH11529
        s = Series(np.arange(10), index=['P%02d' % i for i in range(10)])
        _, ax = self.plt.subplots()
        ax = s.plot(xticks=[0, 3, 5, 9], ax=ax)
        exp = ['P%02d' % i for i in [0, 3, 5, 9]]
        self._check_text_labels(ax.get_xticklabels(), exp)

    def test_custom_business_day_freq(self):
        # GH7222
        from pandas.tseries.offsets import CustomBusinessDay
        s = Series(range(100, 121), index=pd.bdate_range(
            start='2014-05-01', end='2014-06-01',
            freq=CustomBusinessDay(holidays=['2014-05-26'])))

        _check_plot_works(s.plot)


#
# CDR-Stats License
# http://www.cdr-stats.org
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (C) 2011-2015 Star2Billing S.L.
#
# The Initial Developer of the Original Code is
# Arezqui Belaid <info@star2billing.com>
#
from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from voip_billing.models import VoIPRetailRate
from voip_billing.forms import PrefixRetailRateForm, SimulatorForm, BillingReportForm
from voip_billing.function_def import prefix_allowed_to_call
from voip_billing.rate_engine import rate_engine
from voip_billing.constants import RATE_COLUMN_NAME
from aggregator.pandas_cdr import get_report_cdr_per_switch
from aggregator.aggregate_cdr import custom_sql_aggr_top_country
from cdr.decorators import check_user_detail
from cdr.functions_def import get_switch_ip_addr, calculate_act_acd
from cdr.constants import Export_choice
from common.helpers import trunc_date_start, trunc_date_end
from django_lets_go.common_functions import getvar, get_pagination_vars
from datetime import datetime
import logging
import requests
import ast
import tablib
from apirest.view_voip_rate import find_rates

def rest_api_call(request, api_url):
    final_rate_list = []
    response = False
    try:
        response = requests.get(api_url, auth=(request.user, request.user), timeout=1.0)
        logging.debug('API requests.get response: ' + response)
    except requests.exceptions.Timeout:
        # Todo: we may want to deal with error nicely
        logging.debug('API Timeout Error : ' + api_url)
    except:
        logging.debug('API Error : ' + api_url)

    if response and response.status_code == 200:
        # due to string response of API, we need to convert response in to array
        rate_list = response.content.replace('[', '').replace(']', '').replace('}, {', '}|{').split('|')
        for i in rate_list:
            if i:
                # convert string into dict
                final_rate_list.append(ast.literal_eval(i))
    return final_rate_list

@permission_required('user_profile.call_rate', login_url='/')
@login_required
@check_user_detail('voipplan')
def voip_rates(request):
    """List voip call rates according to country prefix

    **Attributes**:

        * ``template`` - voip_billing/rates.html
        * ``form`` - PrefixRetailRateForm

    **Logic Description**:

        get all call rates from voip rate API and list them in template
        with pagination & sorting column
    """
    form = PrefixRetailRateForm(request.POST or None)
    final_rate_list = []
    # Get pagination data

    sort_col_field_list = ['prefix', 'retail_rate', 'destination']
    page_data = get_pagination_vars(request, sort_col_field_list, default_sort_field='prefix')

    sort_order = page_data['sort_order']
    order = 'ASC'
    if "-" in sort_order:
        order = 'DESC'
        sort_order = sort_order[1:]

    dialcode = ''
    if form.is_valid():
        dialcode = request.POST.get('prefix')
        request.session['dialcode'] = dialcode
    else:
        # pagination with prefix code
        if (request.session.get('dialcode') and (request.GET.get('page') or request.GET.get('sort_by'))):
            dialcode = request.session.get('dialcode')
            form = PrefixRetailRateForm(initial={'prefix': dialcode})
        else:
            # Reset variables
            request.session['dialcode'] = ''
            dialcode = ''
    if hasattr(request.user, 'userprofile'):
        voipplan_id = request.user.userprofile.voipplan_id
        if dialcode:
            final_rate_list = find_rates(voipplan_id, dialcode=dialcode, sort_field=sort_order, order=order)
        else:
            final_rate_list = find_rates(voipplan_id, dialcode=None, sort_field=sort_order, order=order)
    else:
        final_rate_list = []

    variables = {
        'form': form,
        'rate_list': final_rate_list,
        'rate_list_count': len(final_rate_list),
        'col_name_with_order': page_data['col_name_with_order'],
        'RATE_COLUMN_NAME': RATE_COLUMN_NAME,
        'sort_order': sort_order,
        'up_icon': '',
        'down_icon': ''
    }
    return render_to_response('voip_billing/rates.html',
                              variables,
                              context_instance=RequestContext(request))

@permission_required('user_profile.export_call_rate', login_url='/')
@login_required
def export_rate(request):
    """
    **Logic Description**:

        get the prifix rates  from voip rate API
        according to search parameters & store into csv file
    """
    format_type = request.GET['format']
    response = HttpResponse(content_type='text/%s' % format_type)
    response['Content-Disposition'] = 'attachment;filename=call_rate.%s' % format_type

    headers = ('prefix', 'destination', 'retail_rate')

    final_result = []
    if request.session.get('session_api_url'):
        api_url = request.session['session_api_url']
        final_result = rest_api_call(request, api_url)

    list_val = []
    for row in final_result:
        list_val.append((row['prefix'], row['prefix__destination'], row['retail_rate']))

    data = tablib.Dataset(*list_val, headers=headers)

    if format_type == Export_choice.XLS:
        response.write(data.xls)
    elif format_type == Export_choice.CSV:
        response.write(data.csv)
    elif format_type == Export_choice.JSON:
        response.write(data.json)

    return response

@permission_required('user_profile.simulator', login_url='/')
@check_user_detail('voipplan')
@login_required
def simulator(request):
    """Client Simulator
    To view rate according to VoIP Plan & Destination No.

    **Attributes**:

        * ``template`` - voip_billing/simulator.html
        * ``form`` - SimulatorForm

    **Logic Description**:

        get min call rates for destination from rate_engine and display them in template
    """
    data = []
    form = SimulatorForm(request.user, request.POST or None)
    # Get Voip Plan ID according to USER

    if form.is_valid():
        # IS recipient_phone_no/destination no is valid prefix
        # (Not banned Prefix) ?
        destination_no = request.POST.get("destination_no")
        if hasattr(request.user, 'userprofile'):
            voipplan_id = request.user.userprofile.voipplan_id
            allowed = prefix_allowed_to_call(destination_no, voipplan_id)
            if allowed:
                rates = rate_engine(voipplan_id=voipplan_id, dest_number=destination_no)
                for rate in rates:
                    r_r_plan = VoIPRetailRate.objects.get(id=rate.rrid)
                    data.append((voipplan_id,
                                 r_r_plan.voip_retail_plan_id.name,
                                 rate.retail_rate))
    data = {
        'form': form,
        'data': data,
    }
    return render_to_response('voip_billing/simulator.html',
                              data,
                              context_instance=RequestContext(request))

@permission_required('user_profile.billing_report', login_url='/')
@check_user_detail('accountcode,voipplan')
@login_required
def billing_report(request):
    """CDR billing graph by daily basis

    **Attributes**:

        * ``template`` - voip_billing/billing_report.html
        * ``form`` - BillingReportForm

    **Logic Description**:

        Retrieve call records from PostgreSQL and build the
        daily billing analytics for given date range
    """
    switch_id = 0
    tday = datetime.today()
    total_data = []
    charttype = "lineWithFocusChart"
    hourly_chartdata = {"x": []}

    form = BillingReportForm(request.POST or None,
                             initial={'from_date': tday.strftime('%Y-%m-%d 00:00'),
                                    'to_date': tday.strftime('%Y-%m-%d 23:55'),
                                    'switch_id': switch_id})
    start_date = trunc_date_start(tday)
    end_date = trunc_date_end(tday)

    if form.is_valid():
        from_date = getvar(request, 'from_date')
        to_date = getvar(request, 'to_date')
        start_date = trunc_date_start(from_date)
        end_date = trunc_date_end(to_date)
        switch_id = getvar(request, 'switch_id')

    metrics = ['buy_cost', 'sell_cost']

    hourly_data = get_report_cdr_per_switch(request.user, 'hour', start_date, end_date, switch_id)

    hourly_chartdata['x'] = hourly_data["nbcalls"]["x_timestamp"]

    i = 0
    for metric in metrics:
        extra_serie = {
            "tooltip": {"y_start": "", "y_end": " " + metric},
            "date_format": "%d %b %y %H:%M%p"
        }
        for switch in hourly_data[metric]["columns"]:
            i = i + 1
            hourly_chartdata['name' + str(i)] = get_switch_ip_addr(switch) + "_" + metric
            hourly_chartdata['y' + str(i)] = hourly_data[metric]["values"][str(switch)]
            hourly_chartdata['extra' + str(i)] = extra_serie

    total_calls = hourly_data["nbcalls"]["total"]
    total_duration = hourly_data["duration"]["total"]
    total_billsec = hourly_data["billsec"]["total"]
    total_buy_cost = hourly_data["buy_cost"]["total"]
    total_sell_cost = hourly_data["sell_cost"]["total"]

    # Calculate the Average Time of Call
    metric_aggr = calculate_act_acd(total_calls, total_duration)

    # Get top 10 of country calls
    country_data = custom_sql_aggr_top_country(request.user, switch_id, 10, start_date, end_date)

    data = {
        'form': form,
        'total_data': total_data,
        'start_date': start_date,
        'end_date': end_date,
        'charttype': charttype,
        'chartdata': hourly_chartdata,
        'chartcontainer': 'chart_container',
        'extra': {
            'x_is_date': True,
            'x_axis_format': '%d %b %Y',
            'tag_script_js': True,
            'jquery_on_ready': True,
        },
        'total_calls': total_calls,
        'total_duration': total_duration,
        'total_billsec': total_billsec,
        'total_buy_cost': total_buy_cost,
        'total_sell_cost': total_sell_cost,
        'metric_aggr': metric_aggr,
        'country_data': country_data,
    }
    return render_to_response('voip_billing/billing_report.html',
                              data,
                              context_instance=RequestContext(request))


# This code is supporting material for the book
# Building Machine Learning Systems with Python
# by Willi Richert and Luis Pedro Coelho
# published by PACKT Publishing
#
# It is made available under the MIT License

import numpy as np
from collections import defaultdict

from sklearn.metrics import precision_recall_curve, roc_curve
from sklearn.metrics import auc
from sklearn.cross_validation import ShuffleSplit

from sklearn.metrics import confusion_matrix

from utils import plot_pr, plot_roc, plot_confusion_matrix, GENRE_LIST

from fft import read_fft

genre_list = GENRE_LIST

def train_model(clf_factory, X, Y, name, plot=False):
    labels = np.unique(Y)

    cv = ShuffleSplit(
        n=len(X), n_iter=1, test_size=0.3, indices=True, random_state=0)

    train_errors = []
    test_errors = []

    scores = []
    pr_scores = defaultdict(list)
    precisions, recalls, thresholds = defaultdict(
        list), defaultdict(list), defaultdict(list)

    roc_scores = defaultdict(list)
    tprs = defaultdict(list)
    fprs = defaultdict(list)

    clfs = []  # just to later get the median

    cms = []

    for train, test in cv:
        X_train, y_train = X[train], Y[train]
        X_test, y_test = X[test], Y[test]

        clf = clf_factory()
        clf.fit(X_train, y_train)
        clfs.append(clf)

        train_score = clf.score(X_train, y_train)
        test_score = clf.score(X_test, y_test)
        scores.append(test_score)

        train_errors.append(1 - train_score)
        test_errors.append(1 - test_score)

        y_pred = clf.predict(X_test)
        cm = confusion_matrix(y_test, y_pred)
        cms.append(cm)

        for label in labels:
            y_label_test = np.asarray(y_test == label, dtype=int)
            proba = clf.predict_proba(X_test)
            proba_label = proba[:, label]

            precision, recall, pr_thresholds = precision_recall_curve(
                y_label_test, proba_label)
            pr_scores[label].append(auc(recall, precision))
            precisions[label].append(precision)
            recalls[label].append(recall)
            thresholds[label].append(pr_thresholds)

            fpr, tpr, roc_thresholds = roc_curve(y_label_test, proba_label)
            roc_scores[label].append(auc(fpr, tpr))
            tprs[label].append(tpr)
            fprs[label].append(fpr)

    if plot:
        for label in labels:
            print("Plotting %s" % genre_list[label])
            scores_to_sort = roc_scores[label]
            median = np.argsort(scores_to_sort)[len(scores_to_sort) / 2]

            desc = "%s %s" % (name, genre_list[label])
            plot_pr(pr_scores[label][median], desc, precisions[label][median],
                    recalls[label][median], label='%s vs rest' % genre_list[label])
            plot_roc(roc_scores[label][median], desc, tprs[label][median],
                     fprs[label][median], label='%s vs rest' % genre_list[label])

    all_pr_scores = np.asarray(pr_scores.values()).flatten()
    summary = (np.mean(scores), np.std(scores),
               np.mean(all_pr_scores), np.std(all_pr_scores))
    print("%.3f\t%.3f\t%.3f\t%.3f\t" % summary)

    return np.mean(train_errors), np.mean(test_errors), np.asarray(cms)

def create_model():
    from sklearn.linear_model.logistic import LogisticRegression
    clf = LogisticRegression()

    return clf

if __name__ == "__main__":
    X, y = read_fft(genre_list)

    train_avg, test_avg, cms = train_model(
        create_model, X, y, "Log Reg FFT", plot=True)

    cm_avg = np.mean(cms, axis=0)
    cm_norm = cm_avg / np.sum(cm_avg, axis=0)

    plot_confusion_matrix(cm_norm, genre_list, "fft",
                          "Confusion matrix of an FFT based classifier")


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

# TODO(ptucker,ipolosukhin): Improve descriptions.
"""High level API for learning with TensorFlow.

## Estimators

Train and evaluate TensorFlow models.

@@BaseEstimator
@@Estimator
@@ModeKeys
@@TensorFlowClassifier
@@DNNClassifier
@@DNNRegressor
@@TensorFlowDNNClassifier
@@TensorFlowDNNRegressor
@@TensorFlowEstimator
@@LinearClassifier
@@LinearRegressor
@@TensorFlowLinearClassifier
@@TensorFlowLinearRegressor
@@TensorFlowRNNClassifier
@@TensorFlowRNNRegressor
@@TensorFlowRegressor

## Graph actions

Perform various training, evaluation, and inference actions on a graph.

@@NanLossDuringTrainingError
@@RunConfig
@@evaluate
@@infer
@@run_feeds
@@run_n
@@train

## Input processing

Queue and read batched input data.

@@extract_dask_data
@@extract_dask_labels
@@extract_pandas_data
@@extract_pandas_labels
@@extract_pandas_matrix
@@read_batch_examples
@@read_batch_features
@@read_batch_record_features

"""

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

# pylint: disable=wildcard-import
from tensorflow.contrib.learn.python.learn import *
from tensorflow.python.util.all_util import make_all

__all__ = make_all(__name__)
__all__.append('datasets')


import itertools
import pickle

import numpy as np
from numpy.testing import assert_array_almost_equal

import scipy
from scipy.spatial.distance import cdist
from sklearn.neighbors.dist_metrics import DistanceMetric
from sklearn.neighbors import BallTree
from sklearn.utils.testing import SkipTest, assert_raises_regex

def dist_func(x1, x2, p):
    return np.sum((x1 - x2) ** p) ** (1. / p)

def cmp_version(version1, version2):
    version1 = tuple(map(int, version1.split('.')[:2]))
    version2 = tuple(map(int, version2.split('.')[:2]))

    if version1 < version2:
        return -1
    elif version1 > version2:
        return 1
    else:
        return 0

class TestMetrics:
    def __init__(self, n1=20, n2=25, d=4, zero_frac=0.5,
                 rseed=0, dtype=np.float64):
        np.random.seed(rseed)
        self.X1 = np.random.random((n1, d)).astype(dtype)
        self.X2 = np.random.random((n2, d)).astype(dtype)

        # make boolean arrays: ones and zeros
        self.X1_bool = self.X1.round(0)
        self.X2_bool = self.X2.round(0)

        V = np.random.random((d, d))
        VI = np.dot(V, V.T)

        self.metrics = {'euclidean': {},
                        'cityblock': {},
                        'minkowski': dict(p=(1, 1.5, 2, 3)),
                        'chebyshev': {},
                        'seuclidean': dict(V=(np.random.random(d),)),
                        'wminkowski': dict(p=(1, 1.5, 3),
                                           w=(np.random.random(d),)),
                        'mahalanobis': dict(VI=(VI,)),
                        'hamming': {},
                        'canberra': {},
                        'braycurtis': {}}

        self.bool_metrics = ['matching', 'jaccard', 'dice',
                             'kulsinski', 'rogerstanimoto', 'russellrao',
                             'sokalmichener', 'sokalsneath']

    def test_cdist(self):
        for metric, argdict in self.metrics.items():
            keys = argdict.keys()
            for vals in itertools.product(*argdict.values()):
                kwargs = dict(zip(keys, vals))
                D_true = cdist(self.X1, self.X2, metric, **kwargs)
                yield self.check_cdist, metric, kwargs, D_true

        for metric in self.bool_metrics:
            D_true = cdist(self.X1_bool, self.X2_bool, metric)
            yield self.check_cdist_bool, metric, D_true

    def check_cdist(self, metric, kwargs, D_true):
        if metric == 'canberra' and cmp_version(scipy.__version__, '0.9') <= 0:
            raise SkipTest("Canberra distance incorrect in scipy < 0.9")
        dm = DistanceMetric.get_metric(metric, **kwargs)
        D12 = dm.pairwise(self.X1, self.X2)
        assert_array_almost_equal(D12, D_true)

    def check_cdist_bool(self, metric, D_true):
        dm = DistanceMetric.get_metric(metric)
        D12 = dm.pairwise(self.X1_bool, self.X2_bool)
        assert_array_almost_equal(D12, D_true)

    def test_pdist(self):
        for metric, argdict in self.metrics.items():
            keys = argdict.keys()
            for vals in itertools.product(*argdict.values()):
                kwargs = dict(zip(keys, vals))
                D_true = cdist(self.X1, self.X1, metric, **kwargs)
                yield self.check_pdist, metric, kwargs, D_true

        for metric in self.bool_metrics:
            D_true = cdist(self.X1_bool, self.X1_bool, metric)
            yield self.check_pdist_bool, metric, D_true

    def check_pdist(self, metric, kwargs, D_true):
        if metric == 'canberra' and cmp_version(scipy.__version__, '0.9') <= 0:
            raise SkipTest("Canberra distance incorrect in scipy < 0.9")
        dm = DistanceMetric.get_metric(metric, **kwargs)
        D12 = dm.pairwise(self.X1)
        assert_array_almost_equal(D12, D_true)

    def check_pdist_bool(self, metric, D_true):
        dm = DistanceMetric.get_metric(metric)
        D12 = dm.pairwise(self.X1_bool)
        assert_array_almost_equal(D12, D_true)

    def test_pickle(self):
        for metric, argdict in self.metrics.items():
            keys = argdict.keys()
            for vals in itertools.product(*argdict.values()):
                kwargs = dict(zip(keys, vals))
                yield self.check_pickle, metric, kwargs

        for metric in self.bool_metrics:
            yield self.check_pickle_bool, metric

    def check_pickle_bool(self, metric):
        dm = DistanceMetric.get_metric(metric)
        D1 = dm.pairwise(self.X1_bool)
        dm2 = pickle.loads(pickle.dumps(dm))
        D2 = dm2.pairwise(self.X1_bool)
        assert_array_almost_equal(D1, D2)

    def check_pickle(self, metric, kwargs):
        dm = DistanceMetric.get_metric(metric, **kwargs)
        D1 = dm.pairwise(self.X1)
        dm2 = pickle.loads(pickle.dumps(dm))
        D2 = dm2.pairwise(self.X1)
        assert_array_almost_equal(D1, D2)

def test_haversine_metric():
    def haversine_slow(x1, x2):
        return 2 * np.arcsin(np.sqrt(np.sin(0.5 * (x1[0] - x2[0])) ** 2
                                     + np.cos(x1[0]) * np.cos(x2[0]) *
                                     np.sin(0.5 * (x1[1] - x2[1])) ** 2))

    X = np.random.random((10, 2))

    haversine = DistanceMetric.get_metric("haversine")

    D1 = haversine.pairwise(X)
    D2 = np.zeros_like(D1)
    for i, x1 in enumerate(X):
        for j, x2 in enumerate(X):
            D2[i, j] = haversine_slow(x1, x2)

    assert_array_almost_equal(D1, D2)
    assert_array_almost_equal(haversine.dist_to_rdist(D1),
                              np.sin(0.5 * D2) ** 2)

def test_pyfunc_metric():
    X = np.random.random((10, 3))

    euclidean = DistanceMetric.get_metric("euclidean")
    pyfunc = DistanceMetric.get_metric("pyfunc", func=dist_func, p=2)

    # Check if both callable metric and predefined metric initialized
    # DistanceMetric object is picklable
    euclidean_pkl = pickle.loads(pickle.dumps(euclidean))
    pyfunc_pkl = pickle.loads(pickle.dumps(pyfunc))

    D1 = euclidean.pairwise(X)
    D2 = pyfunc.pairwise(X)

    D1_pkl = euclidean_pkl.pairwise(X)
    D2_pkl = pyfunc_pkl.pairwise(X)

    assert_array_almost_equal(D1, D2)
    assert_array_almost_equal(D1_pkl, D2_pkl)

def test_bad_pyfunc_metric():
    def wrong_distance(x, y):
        return "1"

    X = np.ones((5, 2))
    assert_raises_regex(TypeError,
                        "Custom distance function must accept two vectors",
                        BallTree, X, metric=wrong_distance)

def test_input_data_size():
    # Regression test for #6288
    # Previoulsly, a metric requiring a particular input dimension would fail
    def custom_metric(x, y):
        assert x.shape[0] == 3
        return np.sum((x - y) ** 2)

    rng = np.random.RandomState(0)
    X = rng.rand(10, 3)

    pyfunc = DistanceMetric.get_metric("pyfunc", func=dist_func, p=2)
    eucl = DistanceMetric.get_metric("euclidean")
    assert_array_almost_equal(pyfunc.pairwise(X), eucl.pairwise(X))