# pylint: disable-msg=E1101,W0612

import nose  # noqa
import numpy as np
import pandas as pd
import pandas.util.testing as tm

class TestSparseSeriesIndexing(tm.TestCase):

    _multiprocess_can_split_ = True

    def setUp(self):
        self.orig = pd.Series([1, np.nan, np.nan, 3, np.nan])
        self.sparse = self.orig.to_sparse()

    def test_getitem(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse[0], 1)
        self.assertTrue(np.isnan(sparse[1]))
        self.assertEqual(sparse[3], 3)

        result = sparse[[1, 3, 4]]
        exp = orig[[1, 3, 4]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # dense array
        result = sparse[orig % 2 == 1]
        exp = orig[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse[sparse % 2 == 1]
        exp = orig[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array
        result = sparse[pd.SparseArray(sparse % 2 == 1, dtype=bool)]
        tm.assert_sp_series_equal(result, exp)

    def test_getitem_slice(self):
        orig = self.orig
        sparse = self.sparse

        tm.assert_sp_series_equal(sparse[:2], orig[:2].to_sparse())
        tm.assert_sp_series_equal(sparse[4:2], orig[4:2].to_sparse())
        tm.assert_sp_series_equal(sparse[::2], orig[::2].to_sparse())
        tm.assert_sp_series_equal(sparse[-5:], orig[-5:].to_sparse())

    def test_getitem_int_dtype(self):
        # GH 8292
        s = pd.SparseSeries([0, 1, 2, 3, 4, 5, 6], name='xxx')
        res = s[::2]
        exp = pd.SparseSeries([0, 2, 4, 6], index=[0, 2, 4, 6], name='xxx')
        tm.assert_sp_series_equal(res, exp)
        self.assertEqual(res.dtype, np.int64)

        s = pd.SparseSeries([0, 1, 2, 3, 4, 5, 6], fill_value=0, name='xxx')
        res = s[::2]
        exp = pd.SparseSeries([0, 2, 4, 6], index=[0, 2, 4, 6],
                              fill_value=0, name='xxx')
        tm.assert_sp_series_equal(res, exp)
        self.assertEqual(res.dtype, np.int64)

    def test_getitem_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0])
        sparse = orig.to_sparse(fill_value=0)

        self.assertEqual(sparse[0], 1)
        self.assertTrue(np.isnan(sparse[1]))
        self.assertEqual(sparse[2], 0)
        self.assertEqual(sparse[3], 3)

        result = sparse[[1, 3, 4]]
        exp = orig[[1, 3, 4]].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

        # dense array
        result = sparse[orig % 2 == 1]
        exp = orig[orig % 2 == 1].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse[sparse % 2 == 1]
        exp = orig[orig % 2 == 1].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

        # sparse array
        result = sparse[pd.SparseArray(sparse % 2 == 1, dtype=bool)]
        tm.assert_sp_series_equal(result, exp)

    def test_getitem_ellipsis(self):
        # GH 9467
        s = pd.SparseSeries([1, np.nan, 2, 0, np.nan])
        tm.assert_sp_series_equal(s[...], s)

        s = pd.SparseSeries([1, np.nan, 2, 0, np.nan], fill_value=0)
        tm.assert_sp_series_equal(s[...], s)

    def test_getitem_slice_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0])
        sparse = orig.to_sparse(fill_value=0)
        tm.assert_sp_series_equal(sparse[:2],
                                  orig[:2].to_sparse(fill_value=0))
        tm.assert_sp_series_equal(sparse[4:2],
                                  orig[4:2].to_sparse(fill_value=0))
        tm.assert_sp_series_equal(sparse[::2],
                                  orig[::2].to_sparse(fill_value=0))
        tm.assert_sp_series_equal(sparse[-5:],
                                  orig[-5:].to_sparse(fill_value=0))

    def test_loc(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse.loc[0], 1)
        self.assertTrue(np.isnan(sparse.loc[1]))

        result = sparse.loc[[1, 3, 4]]
        exp = orig.loc[[1, 3, 4]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # exceeds the bounds
        result = sparse.loc[[1, 3, 4, 5]]
        exp = orig.loc[[1, 3, 4, 5]].to_sparse()
        tm.assert_sp_series_equal(result, exp)
        # padded with NaN
        self.assertTrue(np.isnan(result[-1]))

        # dense array
        result = sparse.loc[orig % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse.loc[sparse % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array
        result = sparse.loc[pd.SparseArray(sparse % 2 == 1, dtype=bool)]
        tm.assert_sp_series_equal(result, exp)

    def test_loc_index(self):
        orig = pd.Series([1, np.nan, np.nan, 3, np.nan], index=list('ABCDE'))
        sparse = orig.to_sparse()

        self.assertEqual(sparse.loc['A'], 1)
        self.assertTrue(np.isnan(sparse.loc['B']))

        result = sparse.loc[['A', 'C', 'D']]
        exp = orig.loc[['A', 'C', 'D']].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # dense array
        result = sparse.loc[orig % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse.loc[sparse % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array
        result = sparse[pd.SparseArray(sparse % 2 == 1, dtype=bool)]
        tm.assert_sp_series_equal(result, exp)

    def test_loc_index_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0], index=list('ABCDE'))
        sparse = orig.to_sparse(fill_value=0)

        self.assertEqual(sparse.loc['A'], 1)
        self.assertTrue(np.isnan(sparse.loc['B']))

        result = sparse.loc[['A', 'C', 'D']]
        exp = orig.loc[['A', 'C', 'D']].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

        # dense array
        result = sparse.loc[orig % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse.loc[sparse % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

    def test_loc_slice(self):
        orig = self.orig
        sparse = self.sparse
        tm.assert_sp_series_equal(sparse.loc[2:], orig.loc[2:].to_sparse())

    def test_loc_slice_index_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0], index=list('ABCDE'))
        sparse = orig.to_sparse(fill_value=0)

        tm.assert_sp_series_equal(sparse.loc['C':],
                                  orig.loc['C':].to_sparse(fill_value=0))

    def test_loc_slice_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0])
        sparse = orig.to_sparse(fill_value=0)
        tm.assert_sp_series_equal(sparse.loc[2:],
                                  orig.loc[2:].to_sparse(fill_value=0))

    def test_iloc(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse.iloc[3], 3)
        self.assertTrue(np.isnan(sparse.iloc[2]))

        result = sparse.iloc[[1, 3, 4]]
        exp = orig.iloc[[1, 3, 4]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        result = sparse.iloc[[1, -2, -4]]
        exp = orig.iloc[[1, -2, -4]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        with tm.assertRaises(IndexError):
            sparse.iloc[[1, 3, 5]]

    def test_iloc_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0])
        sparse = orig.to_sparse(fill_value=0)

        self.assertEqual(sparse.iloc[3], 3)
        self.assertTrue(np.isnan(sparse.iloc[1]))
        self.assertEqual(sparse.iloc[4], 0)

        result = sparse.iloc[[1, 3, 4]]
        exp = orig.iloc[[1, 3, 4]].to_sparse(fill_value=0)
        tm.assert_sp_series_equal(result, exp)

    def test_iloc_slice(self):
        orig = pd.Series([1, np.nan, np.nan, 3, np.nan])
        sparse = orig.to_sparse()
        tm.assert_sp_series_equal(sparse.iloc[2:], orig.iloc[2:].to_sparse())

    def test_iloc_slice_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0])
        sparse = orig.to_sparse(fill_value=0)
        tm.assert_sp_series_equal(sparse.iloc[2:],
                                  orig.iloc[2:].to_sparse(fill_value=0))

    def test_at(self):
        orig = pd.Series([1, np.nan, np.nan, 3, np.nan])
        sparse = orig.to_sparse()
        self.assertEqual(sparse.at[0], orig.at[0])
        self.assertTrue(np.isnan(sparse.at[1]))
        self.assertTrue(np.isnan(sparse.at[2]))
        self.assertEqual(sparse.at[3], orig.at[3])
        self.assertTrue(np.isnan(sparse.at[4]))

        orig = pd.Series([1, np.nan, np.nan, 3, np.nan],
                         index=list('abcde'))
        sparse = orig.to_sparse()
        self.assertEqual(sparse.at['a'], orig.at['a'])
        self.assertTrue(np.isnan(sparse.at['b']))
        self.assertTrue(np.isnan(sparse.at['c']))
        self.assertEqual(sparse.at['d'], orig.at['d'])
        self.assertTrue(np.isnan(sparse.at['e']))

    def test_at_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0],
                         index=list('abcde'))
        sparse = orig.to_sparse(fill_value=0)
        self.assertEqual(sparse.at['a'], orig.at['a'])
        self.assertTrue(np.isnan(sparse.at['b']))
        self.assertEqual(sparse.at['c'], orig.at['c'])
        self.assertEqual(sparse.at['d'], orig.at['d'])
        self.assertEqual(sparse.at['e'], orig.at['e'])

    def test_iat(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse.iat[0], orig.iat[0])
        self.assertTrue(np.isnan(sparse.iat[1]))
        self.assertTrue(np.isnan(sparse.iat[2]))
        self.assertEqual(sparse.iat[3], orig.iat[3])
        self.assertTrue(np.isnan(sparse.iat[4]))

        self.assertTrue(np.isnan(sparse.iat[-1]))
        self.assertEqual(sparse.iat[-5], orig.iat[-5])

    def test_iat_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0])
        sparse = orig.to_sparse()
        self.assertEqual(sparse.iat[0], orig.iat[0])
        self.assertTrue(np.isnan(sparse.iat[1]))
        self.assertEqual(sparse.iat[2], orig.iat[2])
        self.assertEqual(sparse.iat[3], orig.iat[3])
        self.assertEqual(sparse.iat[4], orig.iat[4])

        self.assertEqual(sparse.iat[-1], orig.iat[-1])
        self.assertEqual(sparse.iat[-5], orig.iat[-5])

    def test_get(self):
        s = pd.SparseSeries([1, np.nan, np.nan, 3, np.nan])
        self.assertEqual(s.get(0), 1)
        self.assertTrue(np.isnan(s.get(1)))
        self.assertIsNone(s.get(5))

        s = pd.SparseSeries([1, np.nan, 0, 3, 0], index=list('ABCDE'))
        self.assertEqual(s.get('A'), 1)
        self.assertTrue(np.isnan(s.get('B')))
        self.assertEqual(s.get('C'), 0)
        self.assertIsNone(s.get('XX'))

        s = pd.SparseSeries([1, np.nan, 0, 3, 0], index=list('ABCDE'),
                            fill_value=0)
        self.assertEqual(s.get('A'), 1)
        self.assertTrue(np.isnan(s.get('B')))
        self.assertEqual(s.get('C'), 0)
        self.assertIsNone(s.get('XX'))

    def test_take(self):
        orig = pd.Series([1, np.nan, np.nan, 3, np.nan],
                         index=list('ABCDE'))
        sparse = orig.to_sparse()

        tm.assert_sp_series_equal(sparse.take([0]),
                                  orig.take([0]).to_sparse())
        tm.assert_sp_series_equal(sparse.take([0, 1, 3]),
                                  orig.take([0, 1, 3]).to_sparse())
        tm.assert_sp_series_equal(sparse.take([-1, -2]),
                                  orig.take([-1, -2]).to_sparse())

    def test_take_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0],
                         index=list('ABCDE'))
        sparse = orig.to_sparse(fill_value=0)

        tm.assert_sp_series_equal(sparse.take([0]),
                                  orig.take([0]).to_sparse(fill_value=0))

        exp = orig.take([0, 1, 3]).to_sparse(fill_value=0)
        tm.assert_sp_series_equal(sparse.take([0, 1, 3]), exp)

        exp = orig.take([-1, -2]).to_sparse(fill_value=0)
        tm.assert_sp_series_equal(sparse.take([-1, -2]), exp)

    def test_reindex(self):
        orig = pd.Series([1, np.nan, np.nan, 3, np.nan],
                         index=list('ABCDE'))
        sparse = orig.to_sparse()

        res = sparse.reindex(['A', 'E', 'C', 'D'])
        exp = orig.reindex(['A', 'E', 'C', 'D']).to_sparse()
        tm.assert_sp_series_equal(res, exp)

        # all missing & fill_value
        res = sparse.reindex(['B', 'E', 'C'])
        exp = orig.reindex(['B', 'E', 'C']).to_sparse()
        tm.assert_sp_series_equal(res, exp)

        orig = pd.Series([np.nan, np.nan, np.nan, np.nan, np.nan],
                         index=list('ABCDE'))
        sparse = orig.to_sparse()

        res = sparse.reindex(['A', 'E', 'C', 'D'])
        exp = orig.reindex(['A', 'E', 'C', 'D']).to_sparse()
        tm.assert_sp_series_equal(res, exp)

    def test_reindex_fill_value(self):
        orig = pd.Series([1, np.nan, 0, 3, 0], index=list('ABCDE'))
        sparse = orig.to_sparse(fill_value=0)

        res = sparse.reindex(['A', 'E', 'C', 'D'])
        exp = orig.reindex(['A', 'E', 'C', 'D']).to_sparse(fill_value=0)
        tm.assert_sp_series_equal(res, exp)

        # includes missing and fill_value
        res = sparse.reindex(['A', 'B', 'C'])
        exp = orig.reindex(['A', 'B', 'C']).to_sparse(fill_value=0)
        tm.assert_sp_series_equal(res, exp)

        # all missing
        orig = pd.Series([np.nan, np.nan, np.nan, np.nan, np.nan],
                         index=list('ABCDE'))
        sparse = orig.to_sparse(fill_value=0)

        res = sparse.reindex(['A', 'E', 'C', 'D'])
        exp = orig.reindex(['A', 'E', 'C', 'D']).to_sparse(fill_value=0)
        tm.assert_sp_series_equal(res, exp)

        # all fill_value
        orig = pd.Series([0., 0., 0., 0., 0.],
                         index=list('ABCDE'))
        sparse = orig.to_sparse(fill_value=0)

        res = sparse.reindex(['A', 'E', 'C', 'D'])
        exp = orig.reindex(['A', 'E', 'C', 'D']).to_sparse(fill_value=0)
        tm.assert_sp_series_equal(res, exp)

    def tests_indexing_with_sparse(self):
        # GH 13985

        for kind in ['integer', 'block']:
            for fill in [True, False, np.nan]:
                arr = pd.SparseArray([1, 2, 3], kind=kind)
                indexer = pd.SparseArray([True, False, True], fill_value=fill,
                                         dtype=bool)

                tm.assert_sp_array_equal(pd.SparseArray([1, 3], kind=kind),
                                         arr[indexer])

                s = pd.SparseSeries(arr, index=['a', 'b', 'c'],
                                    dtype=np.float64)
                exp = pd.SparseSeries([1, 3], index=['a', 'c'],
                                      dtype=np.float64, kind=kind)
                tm.assert_sp_series_equal(s[indexer], exp)
                tm.assert_sp_series_equal(s.loc[indexer], exp)
                tm.assert_sp_series_equal(s.iloc[indexer], exp)

                indexer = pd.SparseSeries(indexer, index=['a', 'b', 'c'])
                tm.assert_sp_series_equal(s[indexer], exp)
                tm.assert_sp_series_equal(s.loc[indexer], exp)

                msg = ("iLocation based boolean indexing cannot use an "
                       "indexable as a mask")
                with tm.assertRaisesRegexp(ValueError, msg):
                    s.iloc[indexer]

class TestSparseSeriesMultiIndexing(TestSparseSeriesIndexing):

    _multiprocess_can_split_ = True

    def setUp(self):
        # Mi with duplicated values
        idx = pd.MultiIndex.from_tuples([('A', 0), ('A', 1), ('B', 0),
                                         ('C', 0), ('C', 1)])
        self.orig = pd.Series([1, np.nan, np.nan, 3, np.nan], index=idx)
        self.sparse = self.orig.to_sparse()

    def test_getitem_multi(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse[0], orig[0])
        self.assertTrue(np.isnan(sparse[1]))
        self.assertEqual(sparse[3], orig[3])

        tm.assert_sp_series_equal(sparse['A'], orig['A'].to_sparse())
        tm.assert_sp_series_equal(sparse['B'], orig['B'].to_sparse())

        result = sparse[[1, 3, 4]]
        exp = orig[[1, 3, 4]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # dense array
        result = sparse[orig % 2 == 1]
        exp = orig[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse[sparse % 2 == 1]
        exp = orig[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array
        result = sparse[pd.SparseArray(sparse % 2 == 1, dtype=bool)]
        tm.assert_sp_series_equal(result, exp)

    def test_getitem_multi_tuple(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse['C', 0], orig['C', 0])
        self.assertTrue(np.isnan(sparse['A', 1]))
        self.assertTrue(np.isnan(sparse['B', 0]))

    def test_getitems_slice_multi(self):
        orig = self.orig
        sparse = self.sparse

        tm.assert_sp_series_equal(sparse[2:], orig[2:].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['B':], orig.loc['B':].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['C':], orig.loc['C':].to_sparse())

        tm.assert_sp_series_equal(sparse.loc['A':'B'],
                                  orig.loc['A':'B'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[:'B'], orig.loc[:'B'].to_sparse())

    def test_loc(self):
        # need to be override to use different label
        orig = self.orig
        sparse = self.sparse

        tm.assert_sp_series_equal(sparse.loc['A'],
                                  orig.loc['A'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['B'],
                                  orig.loc['B'].to_sparse())

        result = sparse.loc[[1, 3, 4]]
        exp = orig.loc[[1, 3, 4]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # exceeds the bounds
        result = sparse.loc[[1, 3, 4, 5]]
        exp = orig.loc[[1, 3, 4, 5]].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # dense array
        result = sparse.loc[orig % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse.loc[sparse % 2 == 1]
        exp = orig.loc[orig % 2 == 1].to_sparse()
        tm.assert_sp_series_equal(result, exp)

        # sparse array
        result = sparse.loc[pd.SparseArray(sparse % 2 == 1, dtype=bool)]
        tm.assert_sp_series_equal(result, exp)

    def test_loc_multi_tuple(self):
        orig = self.orig
        sparse = self.sparse

        self.assertEqual(sparse.loc['C', 0], orig.loc['C', 0])
        self.assertTrue(np.isnan(sparse.loc['A', 1]))
        self.assertTrue(np.isnan(sparse.loc['B', 0]))

    def test_loc_slice(self):
        orig = self.orig
        sparse = self.sparse
        tm.assert_sp_series_equal(sparse.loc['A':], orig.loc['A':].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['B':], orig.loc['B':].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['C':], orig.loc['C':].to_sparse())

        tm.assert_sp_series_equal(sparse.loc['A':'B'],
                                  orig.loc['A':'B'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[:'B'], orig.loc[:'B'].to_sparse())

class TestSparseDataFrameIndexing(tm.TestCase):

    _multiprocess_can_split_ = True

    def test_getitem(self):
        orig = pd.DataFrame([[1, np.nan, np.nan],
                             [2, 3, np.nan],
                             [np.nan, np.nan, 4],
                             [0, np.nan, 5]],
                            columns=list('xyz'))
        sparse = orig.to_sparse()

        tm.assert_sp_series_equal(sparse['x'], orig['x'].to_sparse())
        tm.assert_sp_frame_equal(sparse[['x']], orig[['x']].to_sparse())
        tm.assert_sp_frame_equal(sparse[['z', 'x']],
                                 orig[['z', 'x']].to_sparse())

        tm.assert_sp_frame_equal(sparse[[True, False, True, True]],
                                 orig[[True, False, True, True]].to_sparse())

        tm.assert_sp_frame_equal(sparse[[1, 2]],
                                 orig[[1, 2]].to_sparse())

    def test_getitem_fill_value(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)

        tm.assert_sp_series_equal(sparse['y'],
                                  orig['y'].to_sparse(fill_value=0))

        exp = orig[['x']].to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse[['x']], exp)

        exp = orig[['z', 'x']].to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse[['z', 'x']], exp)

        indexer = [True, False, True, True]
        exp = orig[indexer].to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse[indexer], exp)

        exp = orig[[1, 2]].to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse[[1, 2]], exp)

    def test_loc(self):
        orig = pd.DataFrame([[1, np.nan, np.nan],
                             [2, 3, np.nan],
                             [np.nan, np.nan, 4]],
                            columns=list('xyz'))
        sparse = orig.to_sparse()

        self.assertEqual(sparse.loc[0, 'x'], 1)
        self.assertTrue(np.isnan(sparse.loc[1, 'z']))
        self.assertEqual(sparse.loc[2, 'z'], 4)

        tm.assert_sp_series_equal(sparse.loc[0], orig.loc[0].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[1], orig.loc[1].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[2, :],
                                  orig.loc[2, :].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[2, :],
                                  orig.loc[2, :].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[:, 'y'],
                                  orig.loc[:, 'y'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[:, 'y'],
                                  orig.loc[:, 'y'].to_sparse())

        result = sparse.loc[[1, 2]]
        exp = orig.loc[[1, 2]].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.loc[[1, 2], :]
        exp = orig.loc[[1, 2], :].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.loc[:, ['x', 'z']]
        exp = orig.loc[:, ['x', 'z']].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.loc[[0, 2], ['x', 'z']]
        exp = orig.loc[[0, 2], ['x', 'z']].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # exceeds the bounds
        result = sparse.loc[[1, 3, 4, 5]]
        exp = orig.loc[[1, 3, 4, 5]].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # dense array
        result = sparse.loc[orig.x % 2 == 1]
        exp = orig.loc[orig.x % 2 == 1].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse.loc[sparse.x % 2 == 1]
        exp = orig.loc[orig.x % 2 == 1].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # sparse array
        result = sparse.loc[pd.SparseArray(sparse.x % 2 == 1, dtype=bool)]
        tm.assert_sp_frame_equal(result, exp)

    def test_loc_index(self):
        orig = pd.DataFrame([[1, np.nan, np.nan],
                             [2, 3, np.nan],
                             [np.nan, np.nan, 4]],
                            index=list('abc'), columns=list('xyz'))
        sparse = orig.to_sparse()

        self.assertEqual(sparse.loc['a', 'x'], 1)
        self.assertTrue(np.isnan(sparse.loc['b', 'z']))
        self.assertEqual(sparse.loc['c', 'z'], 4)

        tm.assert_sp_series_equal(sparse.loc['a'], orig.loc['a'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['b'], orig.loc['b'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['b', :],
                                  orig.loc['b', :].to_sparse())
        tm.assert_sp_series_equal(sparse.loc['b', :],
                                  orig.loc['b', :].to_sparse())

        tm.assert_sp_series_equal(sparse.loc[:, 'z'],
                                  orig.loc[:, 'z'].to_sparse())
        tm.assert_sp_series_equal(sparse.loc[:, 'z'],
                                  orig.loc[:, 'z'].to_sparse())

        result = sparse.loc[['a', 'b']]
        exp = orig.loc[['a', 'b']].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.loc[['a', 'b'], :]
        exp = orig.loc[['a', 'b'], :].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.loc[:, ['x', 'z']]
        exp = orig.loc[:, ['x', 'z']].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.loc[['c', 'a'], ['x', 'z']]
        exp = orig.loc[['c', 'a'], ['x', 'z']].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # dense array
        result = sparse.loc[orig.x % 2 == 1]
        exp = orig.loc[orig.x % 2 == 1].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # sparse array (actuary it coerces to normal Series)
        result = sparse.loc[sparse.x % 2 == 1]
        exp = orig.loc[orig.x % 2 == 1].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        # sparse array
        result = sparse.loc[pd.SparseArray(sparse.x % 2 == 1, dtype=bool)]
        tm.assert_sp_frame_equal(result, exp)

    def test_loc_slice(self):
        orig = pd.DataFrame([[1, np.nan, np.nan],
                             [2, 3, np.nan],
                             [np.nan, np.nan, 4]],
                            columns=list('xyz'))
        sparse = orig.to_sparse()
        tm.assert_sp_frame_equal(sparse.loc[2:], orig.loc[2:].to_sparse())

    def test_iloc(self):
        orig = pd.DataFrame([[1, np.nan, np.nan],
                             [2, 3, np.nan],
                             [np.nan, np.nan, 4]])
        sparse = orig.to_sparse()

        self.assertEqual(sparse.iloc[1, 1], 3)
        self.assertTrue(np.isnan(sparse.iloc[2, 0]))

        tm.assert_sp_series_equal(sparse.iloc[0], orig.loc[0].to_sparse())
        tm.assert_sp_series_equal(sparse.iloc[1], orig.loc[1].to_sparse())
        tm.assert_sp_series_equal(sparse.iloc[2, :],
                                  orig.iloc[2, :].to_sparse())
        tm.assert_sp_series_equal(sparse.iloc[2, :],
                                  orig.iloc[2, :].to_sparse())
        tm.assert_sp_series_equal(sparse.iloc[:, 1],
                                  orig.iloc[:, 1].to_sparse())
        tm.assert_sp_series_equal(sparse.iloc[:, 1],
                                  orig.iloc[:, 1].to_sparse())

        result = sparse.iloc[[1, 2]]
        exp = orig.iloc[[1, 2]].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.iloc[[1, 2], :]
        exp = orig.iloc[[1, 2], :].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.iloc[:, [1, 0]]
        exp = orig.iloc[:, [1, 0]].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        result = sparse.iloc[[2], [1, 0]]
        exp = orig.iloc[[2], [1, 0]].to_sparse()
        tm.assert_sp_frame_equal(result, exp)

        with tm.assertRaises(IndexError):
            sparse.iloc[[1, 3, 5]]

    def test_iloc_slice(self):
        orig = pd.DataFrame([[1, np.nan, np.nan],
                             [2, 3, np.nan],
                             [np.nan, np.nan, 4]],
                            columns=list('xyz'))
        sparse = orig.to_sparse()
        tm.assert_sp_frame_equal(sparse.iloc[2:], orig.iloc[2:].to_sparse())

    def test_at(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse()
        self.assertEqual(sparse.at['A', 'x'], orig.at['A', 'x'])
        self.assertTrue(np.isnan(sparse.at['B', 'z']))
        self.assertTrue(np.isnan(sparse.at['C', 'y']))
        self.assertEqual(sparse.at['D', 'x'], orig.at['D', 'x'])

    def test_at_fill_value(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)
        self.assertEqual(sparse.at['A', 'x'], orig.at['A', 'x'])
        self.assertTrue(np.isnan(sparse.at['B', 'z']))
        self.assertTrue(np.isnan(sparse.at['C', 'y']))
        self.assertEqual(sparse.at['D', 'x'], orig.at['D', 'x'])

    def test_iat(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse()
        self.assertEqual(sparse.iat[0, 0], orig.iat[0, 0])
        self.assertTrue(np.isnan(sparse.iat[1, 2]))
        self.assertTrue(np.isnan(sparse.iat[2, 1]))
        self.assertEqual(sparse.iat[2, 0], orig.iat[2, 0])

        self.assertTrue(np.isnan(sparse.iat[-1, -2]))
        self.assertEqual(sparse.iat[-1, -1], orig.iat[-1, -1])

    def test_iat_fill_value(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)
        self.assertEqual(sparse.iat[0, 0], orig.iat[0, 0])
        self.assertTrue(np.isnan(sparse.iat[1, 2]))
        self.assertTrue(np.isnan(sparse.iat[2, 1]))
        self.assertEqual(sparse.iat[2, 0], orig.iat[2, 0])

        self.assertTrue(np.isnan(sparse.iat[-1, -2]))
        self.assertEqual(sparse.iat[-1, -1], orig.iat[-1, -1])

    def test_take(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            columns=list('xyz'))
        sparse = orig.to_sparse()

        tm.assert_sp_frame_equal(sparse.take([0]),
                                 orig.take([0]).to_sparse())
        tm.assert_sp_frame_equal(sparse.take([0, 1]),
                                 orig.take([0, 1]).to_sparse())
        tm.assert_sp_frame_equal(sparse.take([-1, -2]),
                                 orig.take([-1, -2]).to_sparse())

    def test_take_fill_value(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)

        exp = orig.take([0]).to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse.take([0]), exp)

        exp = orig.take([0, 1]).to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse.take([0, 1]), exp)

        exp = orig.take([-1, -2]).to_sparse(fill_value=0)
        exp._default_fill_value = np.nan
        tm.assert_sp_frame_equal(sparse.take([-1, -2]), exp)

    def test_reindex(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse()

        res = sparse.reindex(['A', 'C', 'B'])
        exp = orig.reindex(['A', 'C', 'B']).to_sparse()
        tm.assert_sp_frame_equal(res, exp)

        orig = pd.DataFrame([[np.nan, np.nan, np.nan],
                             [np.nan, np.nan, np.nan],
                             [np.nan, np.nan, np.nan],
                             [np.nan, np.nan, np.nan]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse()

        res = sparse.reindex(['A', 'C', 'B'])
        exp = orig.reindex(['A', 'C', 'B']).to_sparse()
        tm.assert_sp_frame_equal(res, exp)

    def test_reindex_fill_value(self):
        orig = pd.DataFrame([[1, np.nan, 0],
                             [2, 3, np.nan],
                             [0, np.nan, 4],
                             [0, np.nan, 5]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)

        res = sparse.reindex(['A', 'C', 'B'])
        exp = orig.reindex(['A', 'C', 'B']).to_sparse(fill_value=0)
        tm.assert_sp_frame_equal(res, exp)

        # all missing
        orig = pd.DataFrame([[np.nan, np.nan, np.nan],
                             [np.nan, np.nan, np.nan],
                             [np.nan, np.nan, np.nan],
                             [np.nan, np.nan, np.nan]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)

        res = sparse.reindex(['A', 'C', 'B'])
        exp = orig.reindex(['A', 'C', 'B']).to_sparse(fill_value=0)
        tm.assert_sp_frame_equal(res, exp)

        # all fill_value
        orig = pd.DataFrame([[0, 0, 0],
                             [0, 0, 0],
                             [0, 0, 0],
                             [0, 0, 0]],
                            index=list('ABCD'), columns=list('xyz'))
        sparse = orig.to_sparse(fill_value=0)

        res = sparse.reindex(['A', 'C', 'B'])
        exp = orig.reindex(['A', 'C', 'B']).to_sparse(fill_value=0)
        tm.assert_sp_frame_equal(res, exp)

class TestMultitype(tm.TestCase):
    def setUp(self):
        self.cols = ['string', 'int', 'float', 'object']

        self.string_series = pd.SparseSeries(['a', 'b', 'c'])
        self.int_series = pd.SparseSeries([1, 2, 3])
        self.float_series = pd.SparseSeries([1.1, 1.2, 1.3])
        self.object_series = pd.SparseSeries([[], {}, set()])
        self.sdf = pd.SparseDataFrame({
            'string': self.string_series,
            'int': self.int_series,
            'float': self.float_series,
            'object': self.object_series,
        })
        self.sdf = self.sdf[self.cols]
        self.ss = pd.SparseSeries(['a', 1, 1.1, []], index=self.cols)

    def test_frame_basic_dtypes(self):
        for _, row in self.sdf.iterrows():
            self.assertEqual(row.dtype, object)
        tm.assert_sp_series_equal(self.sdf['string'], self.string_series,
                                  check_names=False)
        tm.assert_sp_series_equal(self.sdf['int'], self.int_series,
                                  check_names=False)
        tm.assert_sp_series_equal(self.sdf['float'], self.float_series,
                                  check_names=False)
        tm.assert_sp_series_equal(self.sdf['object'], self.object_series,
                                  check_names=False)

    def test_frame_indexing_single(self):
        tm.assert_sp_series_equal(self.sdf.iloc[0],
                                  pd.SparseSeries(['a', 1, 1.1, []],
                                                  index=self.cols),
                                  check_names=False)
        tm.assert_sp_series_equal(self.sdf.iloc[1],
                                  pd.SparseSeries(['b', 2, 1.2, {}],
                                                  index=self.cols),
                                  check_names=False)
        tm.assert_sp_series_equal(self.sdf.iloc[2],
                                  pd.SparseSeries(['c', 3, 1.3, set()],
                                                  index=self.cols),
                                  check_names=False)

    def test_frame_indexing_multiple(self):
        tm.assert_sp_frame_equal(self.sdf, self.sdf[:])
        tm.assert_sp_frame_equal(self.sdf, self.sdf.loc[:])
        tm.assert_sp_frame_equal(self.sdf.iloc[[1, 2]],
                                 pd.SparseDataFrame({
                                     'string': self.string_series.iloc[[1, 2]],
                                     'int': self.int_series.iloc[[1, 2]],
                                     'float': self.float_series.iloc[[1, 2]],
                                     'object': self.object_series.iloc[[1, 2]]
                                 }, index=[1, 2])[self.cols])
        tm.assert_sp_frame_equal(self.sdf[['int', 'string']],
                                 pd.SparseDataFrame({
                                     'int': self.int_series,
                                     'string': self.string_series,
                                 }))

    def test_series_indexing_single(self):
        for i, idx in enumerate(self.cols):
            self.assertEqual(self.ss.iloc[i], self.ss[idx])
            self.assertEqual(type(self.ss.iloc[i]),
                             type(self.ss[idx]))
        self.assertEqual(self.ss['string'], 'a')
        self.assertEqual(self.ss['int'], 1)
        self.assertEqual(self.ss['float'], 1.1)
        self.assertEqual(self.ss['object'], [])

    def test_series_indexing_multiple(self):
        tm.assert_sp_series_equal(self.ss.loc[['string', 'int']],
                                  pd.SparseSeries(['a', 1],
                                                  index=['string', 'int']))
        tm.assert_sp_series_equal(self.ss.loc[['string', 'object']],
                                  pd.SparseSeries(['a', []],
                                                  index=['string', 'object']))


#!/usr/bin/env python

"""
Functions to do automatic visualization of activation-like maps.

For 2D-only visualization, only matplotlib is required.
For 3D visualization, Mayavi, version 3.0 or greater, is required.
"""

# Author: Gael Varoquaux <gael dot varoquaux at normalesup dot org>
# License: BSD

# Standard library imports
import os
import sys

# Standard scientific libraries imports (more specific imports are
# delayed, so that the part module can be used without them).
import numpy as np
import matplotlib as mp
import pylab as pl

# Local imports
from nipy.neurospin.utils.mask import compute_mask
from nipy.io.imageformats import load

from anat_cache import mni_sform, mni_sform_inv, _AnatCache
from coord_tools import coord_transform, find_activation, \
        find_cut_coords

class SformError(Exception):
    pass

class NiftiIndexError(IndexError):
    pass

################################################################################
# Colormaps

def _rotate_cmap(cmap, name=None, swap_order=('green', 'red', 'blue')):
    """ Utility function to swap the colors of a colormap.
    """
    orig_cdict = cmap._segmentdata.copy()

    cdict = dict()
    cdict['green'] = [(p, c1, c2)
                        for (p, c1, c2) in orig_cdict[swap_order[0]]]
    cdict['blue'] = [(p, c1, c2)
                        for (p, c1, c2) in orig_cdict[swap_order[1]]]
    cdict['red'] = [(p, c1, c2)
                        for (p, c1, c2) in orig_cdict[swap_order[2]]]

    if name is None:
        name = '%s_rotated' % cmap.name
    return mp.colors.LinearSegmentedColormap(name, cdict, 512)

def _pigtailed_cmap(cmap, name=None,
                    swap_order=('green', 'red', 'blue')):
    """ Utility function to make a new colormap by concatenating a
        colormap with its reverse.
    """
    orig_cdict = cmap._segmentdata.copy()

    cdict = dict()
    cdict['green'] = [(0.5*(1-p), c1, c2)
                        for (p, c1, c2) in reversed(orig_cdict[swap_order[0]])]
    cdict['blue'] = [(0.5*(1-p), c1, c2)
                        for (p, c1, c2) in reversed(orig_cdict[swap_order[1]])]
    cdict['red'] = [(0.5*(1-p), c1, c2)
                        for (p, c1, c2) in reversed(orig_cdict[swap_order[2]])]

    for color in ('red', 'green', 'blue'):
        cdict[color].extend([(0.5*(1+p), c1, c2)
                                    for (p, c1, c2) in orig_cdict[color]])

    if name is None:
        name = '%s_reversed' % cmap.name
    return mp.colors.LinearSegmentedColormap(name, cdict, 512)

# Using a dict as a namespace, to micmic matplotlib's cm

_cm = dict(
    cold_hot     = _pigtailed_cmap(pl.cm.hot,      name='cold_hot'),
    brown_blue   = _pigtailed_cmap(pl.cm.bone,     name='brown_blue'),
    cyan_copper  = _pigtailed_cmap(pl.cm.copper,   name='cyan_copper'),
    cyan_orange  = _pigtailed_cmap(pl.cm.YlOrBr_r, name='cyan_orange'),
    blue_red     = _pigtailed_cmap(pl.cm.Reds_r,   name='blue_red'),
    brown_cyan   = _pigtailed_cmap(pl.cm.Blues_r,  name='brown_cyan'),
    purple_green = _pigtailed_cmap(pl.cm.Greens_r, name='purple_green',
                    swap_order=('red', 'blue', 'green')),
    purple_blue  = _pigtailed_cmap(pl.cm.Blues_r, name='purple_blue',
                    swap_order=('red', 'blue', 'green')),
    blue_orange  = _pigtailed_cmap(pl.cm.Oranges_r, name='blue_orange',
                    swap_order=('green', 'red', 'blue')),
    black_blue   = _rotate_cmap(pl.cm.hot, name='black_blue'),
    black_purple = _rotate_cmap(pl.cm.hot, name='black_purple',
                                    swap_order=('blue', 'red', 'green')),
    black_pink   = _rotate_cmap(pl.cm.hot, name='black_pink',
                            swap_order=('blue', 'green', 'red')),
    black_green  = _rotate_cmap(pl.cm.hot, name='black_green',
                            swap_order=('red', 'blue', 'green')),
    black_red    = pl.cm.hot,
    )

_cm.update(pl.cm.datad)

class _CM(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__.update(self)

cm = _CM(**_cm)

################################################################################
# 2D plotting of activation maps
################################################################################

def plot_map_2d(map, sform, cut_coords, anat=None, anat_sform=None,
                    vmin=None, figure_num=None, axes=None, title='',
                    mask=None, **kwargs):
    """ Plot three cuts of a given activation map (Frontal, Axial, and Lateral)

        Parameters
        ----------
        map : 3D ndarray
            The activation map, as a 3D image.
        sform : 4x4 ndarray
            The affine matrix going from image voxel space to MNI space.
        cut_coords: 3-tuple of floats
            The MNI coordinates of the point where the cut is performed, in
            MNI coordinates and order.
        anat : 3D ndarray, optional or False
            The anatomical image to be used as a background. If None, the
            MNI152 T1 1mm template is used. If False, no anat is displayed.
        anat_sform : 4x4 ndarray, optional
            The affine matrix going from the anatomical image voxel space to
            MNI space. This parameter is not used when the default
            anatomical is used, but it is compulsory when using an
            explicite anatomical image.
        vmin : float, optional
            The lower threshold of the positive activation. This
            parameter is used to threshold the activation map.
        figure_num : integer, optional
            The number of the matplotlib figure used. If None is given, a
            new figure is created.
        axes : 4 tuple of float: (xmin, xmax, ymin, ymin), optional
            The coordinates, in matplotlib figure space, of the axes
            used to display the plot. If None, the complete figure is
            used.
        title : string, optional
            The title dispayed on the figure.
        mask : 3D ndarray, boolean, optional
            The brain mask. If None, the mask is computed from the map.*
        kwargs: extra keyword arguments, optional
            Extra keyword arguments passed to pylab.imshow

        Notes
        -----
        All the 3D arrays are in numpy convention: (x, y, z)

        Cut coordinates are in Talairach coordinates. Warning: Talairach
        coordinates are (y, x, z), if (x, y, z) are in voxel-ordering
        convention.
    """
    if anat is None:
        anat, anat_sform, vmax_anat = _AnatCache.get_anat()
    elif anat is not False:
        vmax_anat = anat.max()

    if mask is not None and (
                    np.all(mask) or np.all(np.logical_not(mask))):
        mask = None

    vmin_map  = map.min()
    vmax_map  = map.max()
    if vmin is not None and np.isfinite(vmin):
        map = np.ma.masked_less(map, vmin)
    elif mask is not None and not isinstance(map, np.ma.masked_array):
        map = np.ma.masked_array(map, np.logical_not(mask))
        vmin_map  = map.min()
        vmax_map  = map.max()

    if isinstance(map, np.ma.core.MaskedArray):
        use_mask = False
        if map._mask is False or np.all(np.logical_not(map._mask)):
            map = np.asarray(map)
        elif map._mask is True or np.all(map._mask):
            map = np.asarray(map)
        if use_mask and mask is not None:
            map = np.ma.masked_array(map, np.logical_not(mask))

    # Calculate the bounds
    if anat is not False:
        anat_bounds = np.zeros((4, 6))
        anat_bounds[:3, -3:] = np.identity(3)*anat.shape
        anat_bounds[-1, :] = 1
        anat_bounds = np.dot(anat_sform, anat_bounds)

    map_bounds = np.zeros((4, 6))
    map_bounds[:3, -3:] = np.identity(3)*map.shape
    map_bounds[-1, :] = 1
    map_bounds = np.dot(sform, map_bounds)

    # The coordinates of the center of the cut in different spaces.
    y, x, z = cut_coords
    x_map, y_map, z_map = [int(round(c)) for c in
                            coord_transform(x, y, z,
                                    np.linalg.inv(sform))]
    if anat is not False:
        x_anat, y_anat, z_anat = [int(round(c)) for c in
                            coord_transform(x, y, z,
                                    np.linalg.inv(anat_sform))]

    fig = pl.figure(figure_num, figsize=(6.6, 2.6))
    if axes is None:
        axes = (0., 1., 0., 1.)
        pl.clf()
    ax_xmin, ax_xmax, ax_ymin, ax_ymax = axes
    ax_width = ax_xmax - ax_xmin
    ax_height = ax_ymax - ax_ymin

    # Calculate the axes ratio size in a 'clever' way
    if anat is not False:
        shapes = np.array(anat.shape, 'f')
    else:
        shapes = np.array(map.shape, 'f')
    shapes *= ax_width/shapes.sum()

    ###########################################################################
    # Frontal
    pl.axes([ax_xmin, ax_ymin, shapes[0], ax_height])
    if anat is not False:
        if y_anat < anat.shape[1]:
            pl.imshow(np.rot90(anat[:, y_anat, :]),
                                    cmap=pl.cm.gray,
                                    vmin=-.5*vmax_anat,
                                    vmax=vmax_anat,
                                    extent=(anat_bounds[0, 3],
                                            anat_bounds[0, 0],
                                            anat_bounds[2, 0],
                                            anat_bounds[2, 5]))
    if y_map < map.shape[1]:
        pl.imshow(np.rot90(map[:, y_map, :]),
                                vmin=vmin_map,
                                vmax=vmax_map,
                                extent=(map_bounds[0, 3],
                                        map_bounds[0, 0],
                                        map_bounds[2, 0],
                                        map_bounds[2, 5]),
                                **kwargs)
    pl.text(ax_xmin +shapes[0] + shapes[1] - 0.01, ax_ymin + 0.07, '%i' % x,
             horizontalalignment='right',
             verticalalignment='bottom',
             transform=fig.transFigure)

    xmin, xmax = pl.xlim()
    ymin, ymax = pl.ylim()
    pl.hlines(z, xmin, xmax, color=(.5, .5, .5))
    pl.vlines(-x, ymin, ymax, color=(.5, .5, .5))
    pl.axis('off')

    ###########################################################################
    # Lateral
    pl.axes([ax_xmin + shapes[0], ax_ymin, shapes[1], ax_height])
    if anat is not False:
        if x_anat < anat.shape[0]:
            pl.imshow(np.rot90(anat[x_anat, ...]), cmap=pl.cm.gray,
                                    vmin=-.5*vmax_anat,
                                    vmax=vmax_anat,
                                    extent=(anat_bounds[1, 0],
                                            anat_bounds[1, 4],
                                            anat_bounds[2, 0],
                                            anat_bounds[2, 5]))
    if x_map < map.shape[0]:
        pl.imshow(np.rot90(map[x_map, ...]),
                                vmin=vmin_map,
                                vmax=vmax_map,
                                extent=(map_bounds[1, 0],
                                        map_bounds[1, 4],
                                        map_bounds[2, 0],
                                        map_bounds[2, 5]),
                                **kwargs)
    pl.text(ax_xmin + shapes[-1] - 0.01, ax_ymin + 0.07, '%i' % y,
             horizontalalignment='right',
             verticalalignment='bottom',
             transform=fig.transFigure)

    xmin, xmax = pl.xlim()
    ymin, ymax = pl.ylim()
    pl.hlines(z, xmin, xmax, color=(.5, .5, .5))
    pl.vlines(y, ymin, ymax, color=(.5, .5, .5))
    pl.axis('off')

    ###########################################################################
    # Axial
    pl.axes([ax_xmin + shapes[0] + shapes[1], ax_ymin, shapes[-1],
                ax_height])
    if anat is not False:
        if z_anat < anat.shape[2]:
            pl.imshow(np.rot90(anat[..., z_anat]),
                                    cmap=pl.cm.gray,
                                    vmin=-.5*vmax_anat,
                                    vmax=vmax_anat,
                                    extent=(anat_bounds[0, 0],
                                            anat_bounds[0, 3],
                                            anat_bounds[1, 0],
                                            anat_bounds[1, 4]))
    if z_map < map.shape[2]:
        pl.imshow(np.rot90(map[..., z_map]),
                                vmin=vmin_map,
                                vmax=vmax_map,
                                extent=(map_bounds[0, 0],
                                        map_bounds[0, 3],
                                        map_bounds[1, 0],
                                        map_bounds[1, 4]),
                                **kwargs)
    pl.text(ax_xmax - 0.01, ax_ymin + 0.07, '%i' % z,
             horizontalalignment='right',
             verticalalignment='bottom',
             transform=fig.transFigure)

    xmin, xmax = pl.xlim()
    ymin, ymax = pl.ylim()
    pl.hlines(y,  xmin, xmax, color=(.5, .5, .5))
    pl.vlines(x, ymin, ymax, color=(.5, .5, .5))
    pl.axis('off')

    pl.text(ax_xmin + 0.01, ax_ymax - 0.01, title,
             horizontalalignment='left',
             verticalalignment='top',
             transform=fig.transFigure)

    pl.axis('off')

def demo_plot_map_2d():
    map = np.zeros((182, 218, 182))
    # Color a asymetric rectangle around Broadman area 26:
    x, y, z = -6, -53, 9
    x_map, y_map, z_map = coord_transform(x, y, z, mni_sform_inv)
    map[x_map-30:x_map+30, y_map-3:y_map+3, z_map-10:z_map+10] = 1
    map = np.ma.masked_less(map, 0.5)
    plot_map_2d(map, mni_sform, cut_coords=(x, y, z),
                                figure_num=512)

def plot_map(map, sform, cut_coords, anat=None, anat_sform=None,
    vmin=None, figure_num=None, title='', mask=None):
    """ Plot a together a 3D volume rendering view of the activation, with an
        outline of the brain, and 2D cuts. If Mayavi is not installed,
        falls back to 2D views only.

        Parameters
        ----------
        map : 3D ndarray
            The activation map, as a 3D image.
        sform : 4x4 ndarray
            The affine matrix going from image voxel space to MNI space.
        cut_coords: 3-tuple of floats, optional
            The MNI coordinates of the cut to perform, in MNI coordinates
            and order. If None is given, the cut_coords are automaticaly
            estimated.
        anat : 3D ndarray, optional
            The anatomical image to be used as a background. If None, the
            MNI152 T1 1mm template is used.
        anat_sform : 4x4 ndarray, optional
            The affine matrix going from the anatomical image voxel space to
            MNI space. This parameter is not used when the default
            anatomical is used, but it is compulsory when using an
            explicite anatomical image.
        vmin : float, optional
            The lower threshold of the positive activation. This
            parameter is used to threshold the activation map.
        figure_num : integer, optional
            The number of the matplotlib and Mayavi figures used. If None is
            given, a new figure is created.
        title : string, optional
            The title dispayed on the figure.
        mask : 3D ndarray, boolean, optional
            The brain mask. If None, the mask is computed from the map.

        Notes
        -----
        All the 3D arrays are in numpy convention: (x, y, z)

        Cut coordinates are in Talairach coordinates. Warning: Talairach
        coordinates are (y, x, z), if (x, y, z) are in voxel-ordering
        convention.
    """
    try:
        from enthought.mayavi import version
        if not int(version.version[0]) > 2:
            raise ImportError
    except ImportError:
        print >> sys.stderr, 'Mayavi > 3.x not installed, plotting only 2D'
        return plot_map_2d(map, sform, cut_coords=cut_coords, anat=anat,
                                anat_sform=anat_sform, vmin=vmin,
                                title=title,
                                figure_num=figure_num, mask=mask)

    from .maps_3d import plot_map_3d, m2screenshot
    plot_map_3d(map, sform, cut_coords=cut_coords, anat=anat,
                anat_sform=anat_sform, vmin=vmin,
                figure_num=figure_num, mask=mask)

    fig = pl.figure(figure_num, figsize=(10.6, 2.6))
    ax = pl.axes((-0.01, 0, 0.3, 1))
    m2screenshot(mpl_axes=ax)

    plot_map_2d(map, sform, cut_coords=cut_coords, anat=anat,
                anat_sform=anat_sform, vmin=vmin, mask=mask,
                figure_num=fig.number, axes=(0.28, 1, 0, 1.), title=title)

def demo_plot_map():
    map = np.zeros((182, 218, 182))
    # Color a asymetric rectangle around Broadman area 26:
    x, y, z = -6, -53, 9
    x_map, y_map, z_map = coord_transform(x, y, z, mni_sform_inv)
    map[x_map-30:x_map+30, y_map-3:y_map+3, z_map-10:z_map+10] = 1
    plot_map(map, mni_sform, cut_coords=(x, y, z), vmin=0.5,
                                figure_num=512)

def auto_plot_map(map, sform, vmin=None, cut_coords=None, do3d=False,
                    anat=None, anat_sform=None, title='',
                    figure_num=None, mask=None, auto_sign=True):
    """ Automatic plotting of an activation map.

        Plot a together a 3D volume rendering view of the activation, with an
        outline of the brain, and 2D cuts. If Mayavi is not installed,
        falls back to 2D views only.

        Parameters
        ----------
        map : 3D ndarray
            The activation map, as a 3D image.
        sform : 4x4 ndarray
            The affine matrix going from image voxel space to MNI space.
        vmin : float, optional
            The lower threshold of the positive activation. This
            parameter is used to threshold the activation map.
        cut_coords: 3-tuple of floats, optional
            The MNI coordinates of the point where the cut is performed, in
            MNI coordinates and order. If None is given, the cut_coords are
            automaticaly estimated.
        do3d : boolean, optional
            If do3d is True, a 3D plot is created if Mayavi is installed.
        anat : 3D ndarray, optional
            The anatomical image to be used as a background. If None, the
            MNI152 T1 1mm template is used.
        anat_sform : 4x4 ndarray, optional
            The affine matrix going from the anatomical image voxel space to
            MNI space. This parameter is not used when the default
            anatomical is used, but it is compulsory when using an
            explicite anatomical image.
        title : string, optional
            The title dispayed on the figure.
        figure_num : integer, optional
            The number of the matplotlib and Mayavi figures used. If None is
            given, a new figure is created.
        mask : 3D ndarray, boolean, optional
            The brain mask. If None, the mask is computed from the map.
        auto_sign : boolean, optional
            If auto_sign is True, the sign of the activation is
            automaticaly computed: negative activation can thus be
            plotted.

        Returns
        -------
        vmin : float
            The lower threshold of the activation used.
        cut_coords : 3-tuple of floats
            The Talairach coordinates of the cut performed for the 2D
            view.

        Notes
        -----
        All the 3D arrays are in numpy convention: (x, y, z)

        Cut coordinates are in Talairach coordinates. Warning: Talairach
        coordinates are (y, x, z), if (x, y, z) are in voxel-ordering
        convention.
    """
    if do3d:
        if do3d == 'offscreen':
            try:
                from enthought.mayavi import mlab
                mlab.options.offscreen = True
            except:
                pass
        plotter = plot_map
    else:
        plotter = plot_map_2d
    if mask is None:
        mask = compute_mask(map)
    if vmin is None:
        vmin = np.inf
        pvalue = 0.04
        while not np.isfinite(vmin):
            pvalue *= 1.25
            vmax, vmin = find_activation(map, mask=mask, pvalue=pvalue)
            if not np.isfinite(vmin) and auto_sign:
                if np.isfinite(vmax):
                    vmin = -vmax
                    if mask is not None:
                        map[mask] *= -1
                    else:
                        map *= -1
    if cut_coords is None:
        x, y, z = find_cut_coords(map, activation_threshold=vmin)
        # XXX: Careful with Voxel/MNI ordering
        y, x, z = coord_transform(x, y, z, sform)
        cut_coords = (x, y, z)
    plotter(map, sform, vmin=vmin, cut_coords=cut_coords,
                anat=anat, anat_sform=anat_sform, title=title,
                figure_num=figure_num, mask=mask)
    return vmin, cut_coords

def plot_niftifile(filename, outputname=None, do3d=False, vmin=None,
            cut_coords=None, anat_filename=None, figure_num=None,
            mask_filename=None, auto_sign=True):
    """ Given a nifti filename, plot a view of it to a file (png by
        default).

        Parameters
        ----------
        filename : string
            The name of the Nifti file of the map to be plotted
        outputname : string, optional
            The file name of the output file created. By default
            the name of the input file with a png extension is used.
        do3d : boolean, optional
            If do3d is True, a 3D plot is created if Mayavi is installed.
        vmin : float, optional
            The lower threshold of the positive activation. This
            parameter is used to threshold the activation map.
        cut_coords: 3-tuple of floats, optional
            The MNI coordinates of the point where the cut is performed, in
            MNI coordinates and order. If None is given, the cut_coords are
            automaticaly estimated.
        anat : string, optional
            Name of the Nifti image file to be used as a background. If None,
            the MNI152 T1 1mm template is used.
        title : string, optional
            The title dispayed on the figure.
        figure_num : integer, optional
            The number of the matplotlib and Mayavi figures used. If None is
            given, a new figure is created.
        mask_filename : string, optional
            Name of the Nifti file to be used as brain mask. If None, the
            mask is computed from the map.
        auto_sign : boolean, optional
            If auto_sign is True, the sign of the activation is
            automaticaly computed: negative activation can thus be
            plotted.

        Notes
        -----

        Cut coordinates are in Talairach coordinates. Warning: Talairach
        coordinates are (y, x, z), if (x, y, z) are in voxel-ordering
        convention.
    """

    if outputname is None:
        outputname = os.path.splitext(filename)[0] + '.png'
    if not os.path.exists(filename):
        raise OSError, 'File %s does not exist' % filename

    nim = load(filename)
    sform = nim.get_affine()
    if any(np.linalg.eigvals(sform)==0):
        raise SformError, "sform affine is not inversible"
    if anat_filename is not None:
        anat_im = load(anat_filename)
        anat = anat_im.data
        anat_sform = anat_im.get_affine()
    else:
        anat = None
        anat_sform = None

    if mask_filename is not None:
        mask_im = load(mask_filename)
        mask = mask_im.data.astype(np.bool)
        if not np.allclose(mask_im.get_affine(), sform):
            raise SformError, 'Mask does not have same sform as image'
        if not np.allclose(mask.shape, nim.data.shape[:3]):
            raise NiftiIndexError, 'Mask does not have same shape as image'
    else:
        mask = None

    output_files = list()

    if nim.data.ndim == 3:
        map = nim.data.T
        auto_plot_map(map, sform, vmin=vmin, cut_coords=cut_coords,
                do3d=do3d, anat=anat, anat_sform=anat_sform, mask=mask,
                title=os.path.basename(filename), figure_num=figure_num,
                auto_sign=auto_sign)
        pl.savefig(outputname)
        output_files.append(outputname)
    elif nim.data.ndim == 4:
        outputname, outputext = os.path.splitext(outputname)
        if len(nim.data) < 10:
            fmt = '%s_%i%s'
        elif len(nim.data) < 100:
            fmt = '%s_%02i%s'
        elif len(nim.data) < 1000:
            fmt = '%s_%03i%s'
        else:
            fmt = '%s_%04i%s'
        if mask is None:
            mask = compute_mask(nim.data.mean(axis=0)).T
        for index, data in enumerate(nim.data):
            map = data.T
            auto_plot_map(map, sform, vmin=vmin, cut_coords=cut_coords,
                    do3d=do3d, anat=anat, anat_sform=anat_sform,
                    title='%s, %i' % (os.path.basename(filename), index),
                    figure_num=figure_num, mask=mask, auto_sign=auto_sign)
            this_outputname = fmt % (outputname, index, outputext)
            pl.savefig(this_outputname)
            pl.clf()
            output_files.append(this_outputname)
    else:
        raise NiftiIndexError, 'File %s: incorrect number of dimensions'
    return output_files


# -----------------------------------------------------------------------------
# Copyright (c) 2014--, The Qiita Development Team.
#
# Distributed under the terms of the BSD 3-clause License.
#
# The full license is in the file LICENSE, distributed with this software.
# -----------------------------------------------------------------------------
from tornado.escape import json_encode, json_decode
import pandas as pd

from qiita_db.handlers.oauth2 import authenticate_oauth
from .rest_handler import RESTHandler

class StudySamplesHandler(RESTHandler):

    @authenticate_oauth
    def get(self, study_id):
        study = self.safe_get_study(study_id)
        if study is None:
            return

        if study.sample_template is None:
            samples = []
        else:
            samples = list(study.sample_template.keys())

        self.write(json_encode(samples))
        self.finish()

    @authenticate_oauth
    def patch(self, study_id):
        study = self.safe_get_study(study_id)
        if study is None:
            return

        if study.sample_template is None:
            self.fail('No sample information found', 404)
            return
        else:
            sample_info = study.sample_template.to_dataframe()

        data = pd.DataFrame.from_dict(json_decode(self.request.body),
                                      orient='index')

        if len(data.index) == 0:
            self.fail('No samples provided', 400)
            return

        categories = set(study.sample_template.categories())

        if set(data.columns) != categories:
            if set(data.columns).issubset(categories):
                self.fail('Not all sample information categories provided',
                          400)
            else:
                unknown = set(data.columns) - categories
                self.fail("Some categories do not exist in the sample "
                          "information", 400,
                          categories_not_found=sorted(unknown))
            return

        existing_samples = set(sample_info.index)
        overlapping_ids = set(data.index).intersection(existing_samples)
        new_ids = set(data.index) - existing_samples
        status = 500

        # warnings generated are not currently caught
        # see https://github.com/biocore/qiita/issues/2096
        if overlapping_ids:
            to_update = data.loc[overlapping_ids]
            study.sample_template.update(to_update)
            status = 200

        if new_ids:
            to_extend = data.loc[new_ids]
            study.sample_template.extend(to_extend)
            status = 201

        self.set_status(status)
        self.finish()

class StudySamplesCategoriesHandler(RESTHandler):

    @authenticate_oauth
    def get(self, study_id, categories):
        if not categories:
            self.fail('No categories specified', 405)
            return

        study = self.safe_get_study(study_id)
        if study is None:
            return

        categories = categories.split(',')

        if study.sample_template is None:
            self.fail('Study does not have sample information', 404)
            return

        available_categories = set(study.sample_template.categories())
        not_found = set(categories) - available_categories
        if not_found:
            self.fail('Category not found', 404,
                      categories_not_found=sorted(not_found))
            return

        blob = {'header': categories,
                'samples': {}}
        df = study.sample_template.to_dataframe()
        for idx, row in df[categories].iterrows():
            blob['samples'][idx] = list(row)

        self.write(json_encode(blob))
        self.finish()

class StudySamplesInfoHandler(RESTHandler):

    @authenticate_oauth
    def get(self, study_id):
        study = self.safe_get_study(study_id)
        if study is None:
            return

        st = study.sample_template
        if st is None:
            info = {'number-of-samples': 0,
                    'categories': []}
        else:
            info = {'number-of-samples': len(st),
                    'categories': st.categories()}

        self.write(json_encode(info))
        self.finish()


#!/usr/bin/env python
#coding=utf-8

##====================================================
## $Author$
## $Date$
## $Revision$
##====================================================

from pylab import *
from configobj import ConfigObj
import matplotlib.pyplot as plt

def display2Dpointset(A):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    #ax.grid(True)
    ax.plot(A[:,0],A[:,1],'yo',markersize=8,mew=1)
    labels = plt.getp(plt.gca(), 'xticklabels')
    plt.setp(labels, color='k', fontweight='bold')
    labels = plt.getp(plt.gca(), 'yticklabels')
    plt.setp(labels, color='k', fontweight='bold')
    for i,x in enumerate(A):
        ax.annotate('%d'%(i+1), xy = x, xytext = x + 0)
    ax.set_axis_off()
    #fig.show()

def display2Dpointsets(A, B, ax = None):
    """ display a pair of 2D point sets """
    if not ax:
        fig = plt.figure()
        ax = fig.add_subplot(111)

    ax.plot(A[:,0],A[:,1],'yo',markersize=8,mew=1)
    ax.plot(B[:,0],B[:,1],'b+',markersize=8,mew=1)
    #pylab.setp(pylab.gca(), 'xlim', [-0.15,0.6])
    labels = plt.getp(plt.gca(), 'xticklabels')
    plt.setp(labels, color='k', fontweight='bold')
    labels = plt.getp(plt.gca(), 'yticklabels')
    plt.setp(labels, color='k', fontweight='bold')

def display3Dpointsets(A,B,ax):
    #ax.plot3d(A[:,0],A[:,1],A[:,2],'yo',markersize=10,mew=1)
    #ax.plot3d(B[:,0],B[:,1],B[:,2],'b+',markersize=10,mew=1)
    ax.scatter(A[:,0],A[:,1],A[:,2], c = 'y', marker = 'o')
    ax.scatter(B[:,0],B[:,1],B[:,2], c = 'b', marker = '+')
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')

from mpl_toolkits.mplot3d import Axes3D

def displayABC(A,B,C):
    fig = plt.figure()
    dim = A.shape[1]
    if dim==2:
        ax = plt.subplot(121)
        display2Dpointsets(A, B, ax)
        ax = plt.subplot(122)
        display2Dpointsets(C, B, ax)
    if dim==3:
        plot1 = plt.subplot(1,2,1)
        ax = Axes3D(fig, rect = plot1.get_position())
        display3Dpointsets(A,B,ax)
        plot2 = plt.subplot(1,2,2)
        ax = Axes3D(fig, rect = plot2.get_position())
        display3Dpointsets(C,B,ax)
    plt.show()

def display_pts(f_config):
    config = ConfigObj(f_config)

    file_section = config['FILES']
    mf = file_section['model']
    sf = file_section['scene']
    tf = file_section['transformed_model']

    m = np.loadtxt(mf)
    s = np.loadtxt(sf)
    t = np.loadtxt(tf)
    displayABC(m,s,t)


import warnings
import itertools
from contextlib import contextmanager

import numpy as np
from matplotlib import transforms

from .. import utils
from .. import _py3k_compat as py3k

class Renderer(object):
    @staticmethod
    def ax_zoomable(ax):
        return bool(ax and ax.get_navigate())

    @staticmethod
    def ax_has_xgrid(ax):
        return bool(ax and ax.xaxis._gridOnMajor and ax.yaxis.get_gridlines())

    @staticmethod
    def ax_has_ygrid(ax):
        return bool(ax and ax.yaxis._gridOnMajor and ax.yaxis.get_gridlines())

    @property
    def current_ax_zoomable(self):
        return self.ax_zoomable(self._current_ax)

    @property
    def current_ax_has_xgrid(self):
        return self.ax_has_xgrid(self._current_ax)

    @property
    def current_ax_has_ygrid(self):
        return self.ax_has_ygrid(self._current_ax)

    @contextmanager
    def draw_figure(self, fig, props):
        if hasattr(self, "_current_fig") and self._current_fig is not None:
            warnings.warn("figure embedded in figure: something is wrong")
        self._current_fig = fig
        self._fig_props = props
        self.open_figure(fig=fig, props=props)
        yield
        self.close_figure(fig=fig)
        self._current_fig = None
        self._fig_props = {}

    @contextmanager
    def draw_axes(self, ax, props):
        if hasattr(self, "_current_ax") and self._current_ax is not None:
            warnings.warn("axes embedded in axes: something is wrong")
        self._current_ax = ax
        self._ax_props = props
        self.open_axes(ax=ax, props=props)
        yield
        self.close_axes(ax=ax)
        self._current_ax = None
        self._ax_props = {}

    @contextmanager
    def draw_legend(self, legend, props):
        self._current_legend = legend
        self._legend_props = props
        self.open_legend(legend=legend, props=props)
        yield
        self.close_legend(legend=legend)
        self._current_legend = None
        self._legend_props = {}

    # Following are the functions which should be overloaded in subclasses

    def open_figure(self, fig, props):
        """
        Begin commands for a particular figure.

        Parameters
        ----------
        fig : matplotlib.Figure
            The Figure which will contain the ensuing axes and elements
        props : dictionary
            The dictionary of figure properties
        """
        pass

    def close_figure(self, fig):
        """
        Finish commands for a particular figure.

        Parameters
        ----------
        fig : matplotlib.Figure
            The figure which is finished being drawn.
        """
        pass

    def open_axes(self, ax, props):
        """
        Begin commands for a particular axes.

        Parameters
        ----------
        ax : matplotlib.Axes
            The Axes which will contain the ensuing axes and elements
        props : dictionary
            The dictionary of axes properties
        """
        pass

    def close_axes(self, ax):
        """
        Finish commands for a particular axes.

        Parameters
        ----------
        ax : matplotlib.Axes
            The Axes which is finished being drawn.
        """
        pass

    def open_legend(self, legend, props):
        """
        Beging commands for a particular legend.

        Parameters
        ----------
        legend : matplotlib.legend.Legend
                The Legend that will contain the ensuing elements
        props : dictionary
                The dictionary of legend properties
        """
        pass

    def close_legend(self, legend):
        """
        Finish commands for a particular legend.

        Parameters
        ----------
        legend : matplotlib.legend.Legend
                The Legend which is finished being drawn
        """
        pass

    def draw_marked_line(self, data, coordinates, linestyle, markerstyle,
                         label, mplobj=None):
        """Draw a line that also has markers.

        If this isn't reimplemented by a renderer object, by default, it will
        make a call to BOTH draw_line and draw_markers when both markerstyle
        and linestyle are not None in the same Line2D object.

        """
        if linestyle is not None:
            self.draw_line(data, coordinates, linestyle, label, mplobj)
        if markerstyle is not None:
            self.draw_markers(data, coordinates, markerstyle, label, mplobj)

    def draw_line(self, data, coordinates, style, label, mplobj=None):
        """
        Draw a line. By default, draw the line via the draw_path() command.
        Some renderers might wish to override this and provide more
        fine-grained behavior.

        In matplotlib, lines are generally created via the plt.plot() command,
        though this command also can create marker collections.

        Parameters
        ----------
        data : array_like
            A shape (N, 2) array of datapoints.
        coordinates : string
            A string code, which should be either 'data' for data coordinates,
            or 'figure' for figure (pixel) coordinates.
        style : dictionary
            a dictionary specifying the appearance of the line.
        mplobj : matplotlib object
            the matplotlib plot element which generated this line
        """
        pathcodes = ['M'] + (data.shape[0] - 1) * ['L']
        pathstyle = dict(facecolor='none', **style)
        pathstyle['edgecolor'] = pathstyle.pop('color')
        pathstyle['edgewidth'] = pathstyle.pop('linewidth')
        self.draw_path(data=data, coordinates=coordinates,
                       pathcodes=pathcodes, style=pathstyle, mplobj=mplobj)

    @staticmethod
    def _iter_path_collection(paths, path_transforms, offsets, styles):
        """Build an iterator over the elements of the path collection"""
        N = max(len(paths), len(offsets))

        if not path_transforms:
            path_transforms = [np.eye(3)]

        edgecolor = styles['edgecolor']
        if np.size(edgecolor) == 0:
            edgecolor = ['none']
        facecolor = styles['facecolor']
        if np.size(facecolor) == 0:
            facecolor = ['none']

        elements = [paths, path_transforms, offsets,
                    edgecolor, styles['linewidth'], facecolor]

        it = itertools
        return it.islice(py3k.zip(*py3k.map(it.cycle, elements)), N)

    def draw_path_collection(self, paths, path_coordinates, path_transforms,
                             offsets, offset_coordinates, offset_order,
                             styles, mplobj=None):
        """
        Draw a collection of paths. The paths, offsets, and styles are all
        iterables, and the number of paths is max(len(paths), len(offsets)).

        By default, this is implemented via multiple calls to the draw_path()
        function. For efficiency, Renderers may choose to customize this
        implementation.

        Examples of path collections created by matplotlib are scatter plots,
        histograms, contour plots, and many others.

        Parameters
        ----------
        paths : list
            list of tuples, where each tuple has two elements:
            (data, pathcodes).  See draw_path() for a description of these.
        path_coordinates: string
            the coordinates code for the paths, which should be either
            'data' for data coordinates, or 'figure' for figure (pixel)
            coordinates.
        path_transforms: array_like
            an array of shape (*, 3, 3), giving a series of 2D Affine
            transforms for the paths. These encode translations, rotations,
            and scalings in the standard way.
        offsets: array_like
            An array of offsets of shape (N, 2)
        offset_coordinates : string
            the coordinates code for the offsets, which should be either
            'data' for data coordinates, or 'figure' for figure (pixel)
            coordinates.
        offset_order : string
            either "before" or "after". This specifies whether the offset
            is applied before the path transform, or after.  The matplotlib
            backend equivalent is "before"->"data", "after"->"screen".
        styles: dictionary
            A dictionary in which each value is a list of length N, containing
            the style(s) for the paths.
        mplobj : matplotlib object
            the matplotlib plot element which generated this collection
        """
        if offset_order == "before":
            raise NotImplementedError("offset before transform")

        for tup in self._iter_path_collection(paths, path_transforms,
                                              offsets, styles):
            (path, path_transform, offset, ec, lw, fc) = tup
            vertices, pathcodes = path
            path_transform = transforms.Affine2D(path_transform)
            vertices = path_transform.transform(vertices)
            # This is a hack:
            if path_coordinates == "figure":
                path_coordinates = "points"
            style = {"edgecolor": utils.color_to_hex(ec),
                     "facecolor": utils.color_to_hex(fc),
                     "edgewidth": lw,
                     "dasharray": "10,0",
                     "alpha": styles['alpha'],
                     "zorder": styles['zorder']}
            self.draw_path(data=vertices, coordinates=path_coordinates,
                           pathcodes=pathcodes, style=style, offset=offset,
                           offset_coordinates=offset_coordinates,
                           mplobj=mplobj)

    def draw_markers(self, data, coordinates, style, label, mplobj=None):
        """
        Draw a set of markers. By default, this is done by repeatedly
        calling draw_path(), but renderers should generally overload
        this method to provide a more efficient implementation.

        In matplotlib, markers are created using the plt.plot() command.

        Parameters
        ----------
        data : array_like
            A shape (N, 2) array of datapoints.
        coordinates : string
            A string code, which should be either 'data' for data coordinates,
            or 'figure' for figure (pixel) coordinates.
        style : dictionary
            a dictionary specifying the appearance of the markers.
        mplobj : matplotlib object
            the matplotlib plot element which generated this marker collection
        """
        vertices, pathcodes = style['markerpath']
        pathstyle = dict((key, style[key]) for key in ['alpha', 'edgecolor',
                                                       'facecolor', 'zorder',
                                                       'edgewidth'])
        pathstyle['dasharray'] = "10,0"
        for vertex in data:
            self.draw_path(data=vertices, coordinates="points",
                           pathcodes=pathcodes, style=pathstyle,
                           offset=vertex, offset_coordinates=coordinates,
                           mplobj=mplobj)

    def draw_text(self, text, position, coordinates, style,
                  text_type=None, mplobj=None):
        """
        Draw text on the image.

        Parameters
        ----------
        text : string
            The text to draw
        position : tuple
            The (x, y) position of the text
        coordinates : string
            A string code, which should be either 'data' for data coordinates,
            or 'figure' for figure (pixel) coordinates.
        style : dictionary
            a dictionary specifying the appearance of the text.
        text_type : string or None
            if specified, a type of text such as "xlabel", "ylabel", "title"
        mplobj : matplotlib object
            the matplotlib plot element which generated this text
        """
        raise NotImplementedError()

    def draw_path(self, data, coordinates, pathcodes, style,
                  offset=None, offset_coordinates="data", mplobj=None):
        """
        Draw a path.

        In matplotlib, paths are created by filled regions, histograms,
        contour plots, patches, etc.

        Parameters
        ----------
        data : array_like
            A shape (N, 2) array of datapoints.
        coordinates : string
            A string code, which should be either 'data' for data coordinates,
            'figure' for figure (pixel) coordinates, or "points" for raw
            point coordinates (useful in conjunction with offsets, below).
        pathcodes : list
            A list of single-character SVG pathcodes associated with the data.
            Path codes are one of ['M', 'm', 'L', 'l', 'Q', 'q', 'T', 't',
                                   'S', 's', 'C', 'c', 'Z', 'z']
            See the SVG specification for details.  Note that some path codes
            consume more than one datapoint (while 'Z' consumes none), so
            in general, the length of the pathcodes list will not be the same
            as that of the data array.
        style : dictionary
            a dictionary specifying the appearance of the line.
        offset : list (optional)
            the (x, y) offset of the path. If not given, no offset will
            be used.
        offset_coordinates : string (optional)
            A string code, which should be either 'data' for data coordinates,
            or 'figure' for figure (pixel) coordinates.
        mplobj : matplotlib object
            the matplotlib plot element which generated this path
        """
        raise NotImplementedError()

    def draw_image(self, imdata, extent, coordinates, style, mplobj=None):
        """
        Draw an image.

        Parameters
        ----------
        imdata : string
            base64 encoded png representation of the image
        extent : list
            the axes extent of the image: [xmin, xmax, ymin, ymax]
        coordinates: string
            A string code, which should be either 'data' for data coordinates,
            or 'figure' for figure (pixel) coordinates.
        style : dictionary
            a dictionary specifying the appearance of the image
        mplobj : matplotlib object
            the matplotlib plot object which generated this image
        """
        raise NotImplementedError()