# -*- coding: utf-8 -*-
# pylint: disable=W0612,E1101

from datetime import datetime

import operator
import nose
from functools import wraps

import numpy as np
import pandas as pd

from pandas import Series, DataFrame, Index, isnull, notnull, pivot, MultiIndex
from pandas.core.datetools import bday
from pandas.core.nanops import nanall, nanany
from pandas.core.panel import Panel
from pandas.core.series import remove_na
import pandas.core.common as com
from pandas.formats.printing import pprint_thing
from pandas import compat
from pandas.compat import range, lrange, StringIO, OrderedDict, signature
from pandas import SparsePanel

from pandas.util.testing import (assert_panel_equal, assert_frame_equal,
                                 assert_series_equal, assert_almost_equal,
                                 assert_produces_warning, ensure_clean,
                                 assertRaisesRegexp, makeCustomDataframe as
                                 mkdf, makeMixedDataFrame)
import pandas.core.panel as panelm
import pandas.util.testing as tm


def ignore_sparse_panel_future_warning(func):
    """
    decorator to ignore FutureWarning if we have a SparsePanel

    can be removed when SparsePanel is fully removed
    """

    @wraps(func)
    def wrapper(self, *args, **kwargs):

        if isinstance(self.panel, SparsePanel):
            with assert_produces_warning(FutureWarning,
                                         check_stacklevel=False):
                return func(self, *args, **kwargs)
        else:
            return func(self, *args, **kwargs)

    return wrapper


class PanelTests(object):
    panel = None

    def test_pickle(self):
        unpickled = self.round_trip_pickle(self.panel)
        assert_frame_equal(unpickled['ItemA'], self.panel['ItemA'])

    def test_rank(self):
        self.assertRaises(NotImplementedError, lambda: self.panel.rank())

    def test_cumsum(self):
        cumsum = self.panel.cumsum()
        assert_frame_equal(cumsum['ItemA'], self.panel['ItemA'].cumsum())

    def not_hashable(self):
        c_empty = Panel()
        c = Panel(Panel([[[1]]]))
        self.assertRaises(TypeError, hash, c_empty)
        self.assertRaises(TypeError, hash, c)


class SafeForLongAndSparse(object):
    _multiprocess_can_split_ = True

    def test_repr(self):
        repr(self.panel)

    @ignore_sparse_panel_future_warning
    def test_copy_names(self):
        for attr in ('major_axis', 'minor_axis'):
            getattr(self.panel, attr).name = None
            cp = self.panel.copy()
            getattr(cp, attr).name = 'foo'
            self.assertIsNone(getattr(self.panel, attr).name)

    def test_iter(self):
        tm.equalContents(list(self.panel), self.panel.items)

    def test_count(self):
        f = lambda s: notnull(s).sum()
        self._check_stat_op('count', f, obj=self.panel, has_skipna=False)

    def test_sum(self):
        self._check_stat_op('sum', np.sum)

    def test_mean(self):
        self._check_stat_op('mean', np.mean)

    def test_prod(self):
        self._check_stat_op('prod', np.prod)

    def test_median(self):
        def wrapper(x):
            if isnull(x).any():
                return np.nan
            return np.median(x)

        self._check_stat_op('median', wrapper)

    def test_min(self):
        self._check_stat_op('min', np.min)

    def test_max(self):
        self._check_stat_op('max', np.max)

    def test_skew(self):
        try:
            from scipy.stats import skew
        except ImportError:
            raise nose.SkipTest("no scipy.stats.skew")

        def this_skew(x):
            if len(x) < 3:
                return np.nan
            return skew(x, bias=False)

        self._check_stat_op('skew', this_skew)

    # def test_mad(self):
    #     f = lambda x: np.abs(x - x.mean()).mean()
    #     self._check_stat_op('mad', f)

    def test_var(self):
        def alt(x):
            if len(x) < 2:
                return np.nan
            return np.var(x, ddof=1)

        self._check_stat_op('var', alt)

    def test_std(self):
        def alt(x):
            if len(x) < 2:
                return np.nan
            return np.std(x, ddof=1)

        self._check_stat_op('std', alt)

    def test_sem(self):
        def alt(x):
            if len(x) < 2:
                return np.nan
            return np.std(x, ddof=1) / np.sqrt(len(x))

        self._check_stat_op('sem', alt)

    # def test_skew(self):
    #     from scipy.stats import skew

    #     def alt(x):
    #         if len(x) < 3:
    #             return np.nan
    #         return skew(x, bias=False)

    #     self._check_stat_op('skew', alt)

    def _check_stat_op(self, name, alternative, obj=None, has_skipna=True):
        if obj is None:
            obj = self.panel

            # # set some NAs
            # obj.ix[5:10] = np.nan
            # obj.ix[15:20, -2:] = np.nan

        f = getattr(obj, name)

        if has_skipna:

            def skipna_wrapper(x):
                nona = remove_na(x)
                if len(nona) == 0:
                    return np.nan
                return alternative(nona)

            def wrapper(x):
                return alternative(np.asarray(x))

            for i in range(obj.ndim):
                result = f(axis=i, skipna=False)
                assert_frame_equal(result, obj.apply(wrapper, axis=i))
        else:
            skipna_wrapper = alternative
            wrapper = alternative

        for i in range(obj.ndim):
            result = f(axis=i)
            if not tm._incompat_bottleneck_version(name):
                assert_frame_equal(result, obj.apply(skipna_wrapper, axis=i))

        self.assertRaises(Exception, f, axis=obj.ndim)

        # Unimplemented numeric_only parameter.
        if 'numeric_only' in signature(f).args:
            self.assertRaisesRegexp(NotImplementedError, name, f,
                                    numeric_only=True)


class SafeForSparse(object):
    _multiprocess_can_split_ = True

    @classmethod
    def assert_panel_equal(cls, x, y):
        assert_panel_equal(x, y)

    def test_get_axis(self):
        assert (self.panel._get_axis(0) is self.panel.items)
        assert (self.panel._get_axis(1) is self.panel.major_axis)
        assert (self.panel._get_axis(2) is self.panel.minor_axis)

    def test_set_axis(self):
        new_items = Index(np.arange(len(self.panel.items)))
        new_major = Index(np.arange(len(self.panel.major_axis)))
        new_minor = Index(np.arange(len(self.panel.minor_axis)))

        # ensure propagate to potentially prior-cached items too
        item = self.panel['ItemA']
        self.panel.items = new_items

        if hasattr(self.panel, '_item_cache'):
            self.assertNotIn('ItemA', self.panel._item_cache)
        self.assertIs(self.panel.items, new_items)

        # TODO: unused?
        item = self.panel[0]  # noqa

        self.panel.major_axis = new_major
        self.assertIs(self.panel[0].index, new_major)
        self.assertIs(self.panel.major_axis, new_major)

        # TODO: unused?
        item = self.panel[0]  # noqa

        self.panel.minor_axis = new_minor
        self.assertIs(self.panel[0].columns, new_minor)
        self.assertIs(self.panel.minor_axis, new_minor)

    def test_get_axis_number(self):
        self.assertEqual(self.panel._get_axis_number('items'), 0)
        self.assertEqual(self.panel._get_axis_number('major'), 1)
        self.assertEqual(self.panel._get_axis_number('minor'), 2)

        with tm.assertRaisesRegexp(ValueError, "No axis named foo"):
            self.panel._get_axis_number('foo')

        with tm.assertRaisesRegexp(ValueError, "No axis named foo"):
            self.panel.__ge__(self.panel, axis='foo')

    def test_get_axis_name(self):
        self.assertEqual(self.panel._get_axis_name(0), 'items')
        self.assertEqual(self.panel._get_axis_name(1), 'major_axis')
        self.assertEqual(self.panel._get_axis_name(2), 'minor_axis')

    def test_get_plane_axes(self):
        # what to do here?

        index, columns = self.panel._get_plane_axes('items')
        index, columns = self.panel._get_plane_axes('major_axis')
        index, columns = self.panel._get_plane_axes('minor_axis')
        index, columns = self.panel._get_plane_axes(0)

    @ignore_sparse_panel_future_warning
    def test_truncate(self):
        dates = self.panel.major_axis
        start, end = dates[1], dates[5]

        trunced = self.panel.truncate(start, end, axis='major')
        expected = self.panel['ItemA'].truncate(start, end)

        assert_frame_equal(trunced['ItemA'], expected)

        trunced = self.panel.truncate(before=start, axis='major')
        expected = self.panel['ItemA'].truncate(before=start)

        assert_frame_equal(trunced['ItemA'], expected)

        trunced = self.panel.truncate(after=end, axis='major')
        expected = self.panel['ItemA'].truncate(after=end)

        assert_frame_equal(trunced['ItemA'], expected)

        # XXX test other axes

    def test_arith(self):
        self._test_op(self.panel, operator.add)
        self._test_op(self.panel, operator.sub)
        self._test_op(self.panel, operator.mul)
        self._test_op(self.panel, operator.truediv)
        self._test_op(self.panel, operator.floordiv)
        self._test_op(self.panel, operator.pow)

        self._test_op(self.panel, lambda x, y: y + x)
        self._test_op(self.panel, lambda x, y: y - x)
        self._test_op(self.panel, lambda x, y: y * x)
        self._test_op(self.panel, lambda x, y: y / x)
        self._test_op(self.panel, lambda x, y: y ** x)

        self._test_op(self.panel, lambda x, y: x + y)  # panel + 1
        self._test_op(self.panel, lambda x, y: x - y)  # panel - 1
        self._test_op(self.panel, lambda x, y: x * y)  # panel * 1
        self._test_op(self.panel, lambda x, y: x / y)  # panel / 1
        self._test_op(self.panel, lambda x, y: x ** y)  # panel ** 1

        self.assertRaises(Exception, self.panel.__add__, self.panel['ItemA'])

    @staticmethod
    def _test_op(panel, op):
        result = op(panel, 1)
        assert_frame_equal(result['ItemA'], op(panel['ItemA'], 1))

    def test_keys(self):
        tm.equalContents(list(self.panel.keys()), self.panel.items)

    def test_iteritems(self):
        # Test panel.iteritems(), aka panel.iteritems()
        # just test that it works
        for k, v in self.panel.iteritems():
            pass

        self.assertEqual(len(list(self.panel.iteritems())),
                         len(self.panel.items))

    @ignore_sparse_panel_future_warning
    def test_combineFrame(self):
        def check_op(op, name):
            # items
            df = self.panel['ItemA']

            func = getattr(self.panel, name)

            result = func(df, axis='items')

            assert_frame_equal(result['ItemB'], op(self.panel['ItemB'], df))

            # major
            xs = self.panel.major_xs(self.panel.major_axis[0])
            result = func(xs, axis='major')

            idx = self.panel.major_axis[1]

            assert_frame_equal(result.major_xs(idx),
                               op(self.panel.major_xs(idx), xs))

            # minor
            xs = self.panel.minor_xs(self.panel.minor_axis[0])
            result = func(xs, axis='minor')

            idx = self.panel.minor_axis[1]

            assert_frame_equal(result.minor_xs(idx),
                               op(self.panel.minor_xs(idx), xs))

        ops = ['add', 'sub', 'mul', 'truediv', 'floordiv']
        if not compat.PY3:
            ops.append('div')
        # pow, mod not supported for SparsePanel as flex ops (for now)
        if not isinstance(self.panel, SparsePanel):
            ops.extend(['pow', 'mod'])
        else:
            idx = self.panel.minor_axis[1]
            with assertRaisesRegexp(ValueError, "Simple arithmetic.*scalar"):
                self.panel.pow(self.panel.minor_xs(idx), axis='minor')
            with assertRaisesRegexp(ValueError, "Simple arithmetic.*scalar"):
                self.panel.mod(self.panel.minor_xs(idx), axis='minor')

        for op in ops:
            try:
                check_op(getattr(operator, op), op)
            except:
                pprint_thing("Failing operation: %r" % op)
                raise
        if compat.PY3:
            try:
                check_op(operator.truediv, 'div')
            except:
                pprint_thing("Failing operation: %r" % 'div')
                raise

    @ignore_sparse_panel_future_warning
    def test_combinePanel(self):
        result = self.panel.add(self.panel)
        self.assert_panel_equal(result, self.panel * 2)

    @ignore_sparse_panel_future_warning
    def test_neg(self):
        self.assert_panel_equal(-self.panel, self.panel * -1)

    # issue 7692
    def test_raise_when_not_implemented(self):
        p = Panel(np.arange(3 * 4 * 5).reshape(3, 4, 5),
                  items=['ItemA', 'ItemB', 'ItemC'],
                  major_axis=pd.date_range('20130101', periods=4),
                  minor_axis=list('ABCDE'))
        d = p.sum(axis=1).ix[0]
        ops = ['add', 'sub', 'mul', 'truediv', 'floordiv', 'div', 'mod', 'pow']
        for op in ops:
            with self.assertRaises(NotImplementedError):
                getattr(p, op)(d, axis=0)

    @ignore_sparse_panel_future_warning
    def test_select(self):
        p = self.panel

        # select items
        result = p.select(lambda x: x in ('ItemA', 'ItemC'), axis='items')
        expected = p.reindex(items=['ItemA', 'ItemC'])
        self.assert_panel_equal(result, expected)

        # select major_axis
        result = p.select(lambda x: x >= datetime(2000, 1, 15), axis='major')
        new_major = p.major_axis[p.major_axis >= datetime(2000, 1, 15)]
        expected = p.reindex(major=new_major)
        self.assert_panel_equal(result, expected)

        # select minor_axis
        result = p.select(lambda x: x in ('D', 'A'), axis=2)
        expected = p.reindex(minor=['A', 'D'])
        self.assert_panel_equal(result, expected)

        # corner case, empty thing
        result = p.select(lambda x: x in ('foo', ), axis='items')
        self.assert_panel_equal(result, p.reindex(items=[]))

    def test_get_value(self):
        for item in self.panel.items:
            for mjr in self.panel.major_axis[::2]:
                for mnr in self.panel.minor_axis:
                    result = self.panel.get_value(item, mjr, mnr)
                    expected = self.panel[item][mnr][mjr]
                    assert_almost_equal(result, expected)

    @ignore_sparse_panel_future_warning
    def test_abs(self):

        result = self.panel.abs()
        result2 = abs(self.panel)
        expected = np.abs(self.panel)
        self.assert_panel_equal(result, expected)
        self.assert_panel_equal(result2, expected)

        df = self.panel['ItemA']
        result = df.abs()
        result2 = abs(df)
        expected = np.abs(df)
        assert_frame_equal(result, expected)
        assert_frame_equal(result2, expected)

        s = df['A']
        result = s.abs()
        result2 = abs(s)
        expected = np.abs(s)
        assert_series_equal(result, expected)
        assert_series_equal(result2, expected)
        self.assertEqual(result.name, 'A')
        self.assertEqual(result2.name, 'A')


class CheckIndexing(object):

    _multiprocess_can_split_ = True

    def test_getitem(self):
        self.assertRaises(Exception, self.panel.__getitem__, 'ItemQ')

    def test_delitem_and_pop(self):
        expected = self.panel['ItemA']
        result = self.panel.pop('ItemA')
        assert_frame_equal(expected, result)
        self.assertNotIn('ItemA', self.panel.items)

        del self.panel['ItemB']
        self.assertNotIn('ItemB', self.panel.items)
        self.assertRaises(Exception, self.panel.__delitem__, 'ItemB')

        values = np.empty((3, 3, 3))
        values[0] = 0
        values[1] = 1
        values[2] = 2

        panel = Panel(values, lrange(3), lrange(3), lrange(3))

        # did we delete the right row?

        panelc = panel.copy()
        del panelc[0]
        assert_frame_equal(panelc[1], panel[1])
        assert_frame_equal(panelc[2], panel[2])

        panelc = panel.copy()
        del panelc[1]
        assert_frame_equal(panelc[0], panel[0])
        assert_frame_equal(panelc[2], panel[2])

        panelc = panel.copy()
        del panelc[2]
        assert_frame_equal(panelc[1], panel[1])
        assert_frame_equal(panelc[0], panel[0])

    def test_setitem(self):
        # LongPanel with one item
        lp = self.panel.filter(['ItemA', 'ItemB']).to_frame()
        with tm.assertRaises(ValueError):
            self.panel['ItemE'] = lp

        # DataFrame
        df = self.panel['ItemA'][2:].filter(items=['A', 'B'])
        self.panel['ItemF'] = df
        self.panel['ItemE'] = df

        df2 = self.panel['ItemF']

        assert_frame_equal(df, df2.reindex(index=df.index, columns=df.columns))

        # scalar
        self.panel['ItemG'] = 1
        self.panel['ItemE'] = True
        self.assertEqual(self.panel['ItemG'].values.dtype, np.int64)
        self.assertEqual(self.panel['ItemE'].values.dtype, np.bool_)

        # object dtype
        self.panel['ItemQ'] = 'foo'
        self.assertEqual(self.panel['ItemQ'].values.dtype, np.object_)

        # boolean dtype
        self.panel['ItemP'] = self.panel['ItemA'] > 0
        self.assertEqual(self.panel['ItemP'].values.dtype, np.bool_)

        self.assertRaises(TypeError, self.panel.__setitem__, 'foo',
                          self.panel.ix[['ItemP']])

        # bad shape
        p = Panel(np.random.randn(4, 3, 2))
        with tm.assertRaisesRegexp(ValueError,
                                   "shape of value must be \(3, 2\), "
                                   "shape of given object was \(4, 2\)"):
            p[0] = np.random.randn(4, 2)

    def test_setitem_ndarray(self):
        from pandas import date_range, datetools

        timeidx = date_range(start=datetime(2009, 1, 1),
                             end=datetime(2009, 12, 31),
                             freq=datetools.MonthEnd())
        lons_coarse = np.linspace(-177.5, 177.5, 72)
        lats_coarse = np.linspace(-87.5, 87.5, 36)
        P = Panel(items=timeidx, major_axis=lons_coarse,
                  minor_axis=lats_coarse)
        data = np.random.randn(72 * 36).reshape((72, 36))
        key = datetime(2009, 2, 28)
        P[key] = data

        assert_almost_equal(P[key].values, data)

    def test_set_minor_major(self):
        # GH 11014
        df1 = DataFrame(['a', 'a', 'a', np.nan, 'a', np.nan])
        df2 = DataFrame([1.0, np.nan, 1.0, np.nan, 1.0, 1.0])
        panel = Panel({'Item1': df1, 'Item2': df2})

        newminor = notnull(panel.iloc[:, :, 0])
        panel.loc[:, :, 'NewMinor'] = newminor
        assert_frame_equal(panel.loc[:, :, 'NewMinor'],
                           newminor.astype(object))

        newmajor = notnull(panel.iloc[:, 0, :])
        panel.loc[:, 'NewMajor', :] = newmajor
        assert_frame_equal(panel.loc[:, 'NewMajor', :],
                           newmajor.astype(object))

    def test_major_xs(self):
        ref = self.panel['ItemA']

        idx = self.panel.major_axis[5]
        xs = self.panel.major_xs(idx)

        result = xs['ItemA']
        assert_series_equal(result, ref.xs(idx), check_names=False)
        self.assertEqual(result.name, 'ItemA')

        # not contained
        idx = self.panel.major_axis[0] - bday
        self.assertRaises(Exception, self.panel.major_xs, idx)

    def test_major_xs_mixed(self):
        self.panel['ItemD'] = 'foo'
        xs = self.panel.major_xs(self.panel.major_axis[0])
        self.assertEqual(xs['ItemA'].dtype, np.float64)
        self.assertEqual(xs['ItemD'].dtype, np.object_)

    def test_minor_xs(self):
        ref = self.panel['ItemA']

        idx = self.panel.minor_axis[1]
        xs = self.panel.minor_xs(idx)

        assert_series_equal(xs['ItemA'], ref[idx], check_names=False)

        # not contained
        self.assertRaises(Exception, self.panel.minor_xs, 'E')

    def test_minor_xs_mixed(self):
        self.panel['ItemD'] = 'foo'

        xs = self.panel.minor_xs('D')
        self.assertEqual(xs['ItemA'].dtype, np.float64)
        self.assertEqual(xs['ItemD'].dtype, np.object_)

    def test_xs(self):
        itemA = self.panel.xs('ItemA', axis=0)
        expected = self.panel['ItemA']
        assert_frame_equal(itemA, expected)

        # get a view by default
        itemA_view = self.panel.xs('ItemA', axis=0)
        itemA_view.values[:] = np.nan
        self.assertTrue(np.isnan(self.panel['ItemA'].values).all())

        # mixed-type yields a copy
        self.panel['strings'] = 'foo'
        result = self.panel.xs('D', axis=2)
        self.assertIsNotNone(result.is_copy)

    def test_getitem_fancy_labels(self):
        p = self.panel

        items = p.items[[1, 0]]
        dates = p.major_axis[::2]
        cols = ['D', 'C', 'F']

        # all 3 specified
        assert_panel_equal(p.ix[items, dates, cols],
                           p.reindex(items=items, major=dates, minor=cols))

        # 2 specified
        assert_panel_equal(p.ix[:, dates, cols],
                           p.reindex(major=dates, minor=cols))

        assert_panel_equal(p.ix[items, :, cols],
                           p.reindex(items=items, minor=cols))

        assert_panel_equal(p.ix[items, dates, :],
                           p.reindex(items=items, major=dates))

        # only 1
        assert_panel_equal(p.ix[items, :, :], p.reindex(items=items))

        assert_panel_equal(p.ix[:, dates, :], p.reindex(major=dates))

        assert_panel_equal(p.ix[:, :, cols], p.reindex(minor=cols))

    def test_getitem_fancy_slice(self):
        pass

    def test_getitem_fancy_ints(self):
        p = self.panel

        # #1603
        result = p.ix[:, -1, :]
        expected = p.ix[:, p.major_axis[-1], :]
        assert_frame_equal(result, expected)

    def test_getitem_fancy_xs(self):
        p = self.panel
        item = 'ItemB'

        date = p.major_axis[5]
        col = 'C'

        # get DataFrame
        # item
        assert_frame_equal(p.ix[item], p[item])
        assert_frame_equal(p.ix[item, :], p[item])
        assert_frame_equal(p.ix[item, :, :], p[item])

        # major axis, axis=1
        assert_frame_equal(p.ix[:, date], p.major_xs(date))
        assert_frame_equal(p.ix[:, date, :], p.major_xs(date))

        # minor axis, axis=2
        assert_frame_equal(p.ix[:, :, 'C'], p.minor_xs('C'))

        # get Series
        assert_series_equal(p.ix[item, date], p[item].ix[date])
        assert_series_equal(p.ix[item, date, :], p[item].ix[date])
        assert_series_equal(p.ix[item, :, col], p[item][col])
        assert_series_equal(p.ix[:, date, col], p.major_xs(date).ix[col])

    def test_getitem_fancy_xs_check_view(self):
        item = 'ItemB'
        date = self.panel.major_axis[5]

        # make sure it's always a view
        NS = slice(None, None)

        # DataFrames
        comp = assert_frame_equal
        self._check_view(item, comp)
        self._check_view((item, NS), comp)
        self._check_view((item, NS, NS), comp)
        self._check_view((NS, date), comp)
        self._check_view((NS, date, NS), comp)
        self._check_view((NS, NS, 'C'), comp)

        # Series
        comp = assert_series_equal
        self._check_view((item, date), comp)
        self._check_view((item, date, NS), comp)
        self._check_view((item, NS, 'C'), comp)
        self._check_view((NS, date, 'C'), comp)

    def test_getitem_callable(self):
        p = self.panel
        # GH 12533

        assert_frame_equal(p[lambda x: 'ItemB'], p.loc['ItemB'])
        assert_panel_equal(p[lambda x: ['ItemB', 'ItemC']],
                           p.loc[['ItemB', 'ItemC']])

    def test_ix_setitem_slice_dataframe(self):
        a = Panel(items=[1, 2, 3], major_axis=[11, 22, 33],
                  minor_axis=[111, 222, 333])
        b = DataFrame(np.random.randn(2, 3), index=[111, 333],
                      columns=[1, 2, 3])

        a.ix[:, 22, [111, 333]] = b

        assert_frame_equal(a.ix[:, 22, [111, 333]], b)

    def test_ix_align(self):
        from pandas import Series
        b = Series(np.random.randn(10), name=0)
        b.sort()
        df_orig = Panel(np.random.randn(3, 10, 2))
        df = df_orig.copy()

        df.ix[0, :, 0] = b
        assert_series_equal(df.ix[0, :, 0].reindex(b.index), b)

        df = df_orig.swapaxes(0, 1)
        df.ix[:, 0, 0] = b
        assert_series_equal(df.ix[:, 0, 0].reindex(b.index), b)

        df = df_orig.swapaxes(1, 2)
        df.ix[0, 0, :] = b
        assert_series_equal(df.ix[0, 0, :].reindex(b.index), b)

    def test_ix_frame_align(self):
        p_orig = tm.makePanel()
        df = p_orig.ix[0].copy()
        assert_frame_equal(p_orig['ItemA'], df)

        p = p_orig.copy()
        p.ix[0, :, :] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p.ix[0] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p.iloc[0, :, :] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p.iloc[0] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p.loc['ItemA'] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p.loc['ItemA', :, :] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p['ItemA'] = df
        assert_panel_equal(p, p_orig)

        p = p_orig.copy()
        p.ix[0, [0, 1, 3, 5], -2:] = df
        out = p.ix[0, [0, 1, 3, 5], -2:]
        assert_frame_equal(out, df.iloc[[0, 1, 3, 5], [2, 3]])

        # GH3830, panel assignent by values/frame
        for dtype in ['float64', 'int64']:

            panel = Panel(np.arange(40).reshape((2, 4, 5)),
                          items=['a1', 'a2'], dtype=dtype)
            df1 = panel.iloc[0]
            df2 = panel.iloc[1]

            tm.assert_frame_equal(panel.loc['a1'], df1)
            tm.assert_frame_equal(panel.loc['a2'], df2)

            # Assignment by Value Passes for 'a2'
            panel.loc['a2'] = df1.values
            tm.assert_frame_equal(panel.loc['a1'], df1)
            tm.assert_frame_equal(panel.loc['a2'], df1)

            # Assignment by DataFrame Ok w/o loc 'a2'
            panel['a2'] = df2
            tm.assert_frame_equal(panel.loc['a1'], df1)
            tm.assert_frame_equal(panel.loc['a2'], df2)

            # Assignment by DataFrame Fails for 'a2'
            panel.loc['a2'] = df2
            tm.assert_frame_equal(panel.loc['a1'], df1)
            tm.assert_frame_equal(panel.loc['a2'], df2)

    def _check_view(self, indexer, comp):
        cp = self.panel.copy()
        obj = cp.ix[indexer]
        obj.values[:] = 0
        self.assertTrue((obj.values == 0).all())
        comp(cp.ix[indexer].reindex_like(obj), obj)

    def test_logical_with_nas(self):
        d = Panel({'ItemA': {'a': [np.nan, False]},
                   'ItemB': {'a': [True, True]}})

        result = d['ItemA'] | d['ItemB']
        expected = DataFrame({'a': [np.nan, True]})
        assert_frame_equal(result, expected)

        # this is autodowncasted here
        result = d['ItemA'].fillna(False) | d['ItemB']
        expected = DataFrame({'a': [True, True]})
        assert_frame_equal(result, expected)

    def test_neg(self):
        # what to do?
        assert_panel_equal(-self.panel, -1 * self.panel)

    def test_invert(self):
        assert_panel_equal(-(self.panel < 0), ~(self.panel < 0))

    def test_comparisons(self):
        p1 = tm.makePanel()
        p2 = tm.makePanel()

        tp = p1.reindex(items=p1.items + ['foo'])
        df = p1[p1.items[0]]

        def test_comp(func):

            # versus same index
            result = func(p1, p2)
            self.assert_numpy_array_equal(result.values,
                                          func(p1.values, p2.values))

            # versus non-indexed same objs
            self.assertRaises(Exception, func, p1, tp)

            # versus different objs
            self.assertRaises(Exception, func, p1, df)

            # versus scalar
            result3 = func(self.panel, 0)
            self.assert_numpy_array_equal(result3.values,
                                          func(self.panel.values, 0))

        test_comp(operator.eq)
        test_comp(operator.ne)
        test_comp(operator.lt)
        test_comp(operator.gt)
        test_comp(operator.ge)
        test_comp(operator.le)

    def test_get_value(self):
        for item in self.panel.items:
            for mjr in self.panel.major_axis[::2]:
                for mnr in self.panel.minor_axis:
                    result = self.panel.get_value(item, mjr, mnr)
                    expected = self.panel[item][mnr][mjr]
                    assert_almost_equal(result, expected)
        with tm.assertRaisesRegexp(TypeError,
                                   "There must be an argument for each axis"):
            self.panel.get_value('a')

    def test_set_value(self):
        for item in self.panel.items:
            for mjr in self.panel.major_axis[::2]:
                for mnr in self.panel.minor_axis:
                    self.panel.set_value(item, mjr, mnr, 1.)
                    assert_almost_equal(self.panel[item][mnr][mjr], 1.)

        # resize
        res = self.panel.set_value('ItemE', 'foo', 'bar', 1.5)
        tm.assertIsInstance(res, Panel)
        self.assertIsNot(res, self.panel)
        self.assertEqual(res.get_value('ItemE', 'foo', 'bar'), 1.5)

        res3 = self.panel.set_value('ItemE', 'foobar', 'baz', 5)
        self.assertTrue(com.is_float_dtype(res3['ItemE'].values))
        with tm.assertRaisesRegexp(TypeError,
                                   "There must be an argument for each axis"
                                   " plus the value provided"):
            self.panel.set_value('a')


_panel = tm.makePanel()
tm.add_nans(_panel)


class TestPanel(tm.TestCase, PanelTests, CheckIndexing, SafeForLongAndSparse,
                SafeForSparse):
    _multiprocess_can_split_ = True

    @classmethod
    def assert_panel_equal(cls, x, y):
        assert_panel_equal(x, y)

    def setUp(self):
        self.panel = _panel.copy()
        self.panel.major_axis.name = None
        self.panel.minor_axis.name = None
        self.panel.items.name = None

    def test_panel_warnings(self):
        with tm.assert_produces_warning(FutureWarning):
            shifted1 = self.panel.shift(lags=1)

        with tm.assert_produces_warning(False):
            shifted2 = self.panel.shift(periods=1)

        tm.assert_panel_equal(shifted1, shifted2)

        with tm.assert_produces_warning(False):
            shifted3 = self.panel.shift()

        tm.assert_panel_equal(shifted1, shifted3)

    def test_constructor(self):
        # with BlockManager
        wp = Panel(self.panel._data)
        self.assertIs(wp._data, self.panel._data)

        wp = Panel(self.panel._data, copy=True)
        self.assertIsNot(wp._data, self.panel._data)
        assert_panel_equal(wp, self.panel)

        # strings handled prop
        wp = Panel([[['foo', 'foo', 'foo', ], ['foo', 'foo', 'foo']]])
        self.assertEqual(wp.values.dtype, np.object_)

        vals = self.panel.values

        # no copy
        wp = Panel(vals)
        self.assertIs(wp.values, vals)

        # copy
        wp = Panel(vals, copy=True)
        self.assertIsNot(wp.values, vals)

        # GH #8285, test when scalar data is used to construct a Panel
        # if dtype is not passed, it should be inferred
        value_and_dtype = [(1, 'int64'), (3.14, 'float64'),
                           ('foo', np.object_)]
        for (val, dtype) in value_and_dtype:
            wp = Panel(val, items=range(2), major_axis=range(3),
                       minor_axis=range(4))
            vals = np.empty((2, 3, 4), dtype=dtype)
            vals.fill(val)
            assert_panel_equal(wp, Panel(vals, dtype=dtype))

        # test the case when dtype is passed
        wp = Panel(1, items=range(2), major_axis=range(3), minor_axis=range(4),
                   dtype='float32')
        vals = np.empty((2, 3, 4), dtype='float32')
        vals.fill(1)
        assert_panel_equal(wp, Panel(vals, dtype='float32'))

    def test_constructor_cast(self):
        zero_filled = self.panel.fillna(0)

        casted = Panel(zero_filled._data, dtype=int)
        casted2 = Panel(zero_filled.values, dtype=int)

        exp_values = zero_filled.values.astype(int)
        assert_almost_equal(casted.values, exp_values)
        assert_almost_equal(casted2.values, exp_values)

        casted = Panel(zero_filled._data, dtype=np.int32)
        casted2 = Panel(zero_filled.values, dtype=np.int32)

        exp_values = zero_filled.values.astype(np.int32)
        assert_almost_equal(casted.values, exp_values)
        assert_almost_equal(casted2.values, exp_values)

        # can't cast
        data = [[['foo', 'bar', 'baz']]]
        self.assertRaises(ValueError, Panel, data, dtype=float)

    def test_constructor_empty_panel(self):
        empty = Panel()
        self.assertEqual(len(empty.items), 0)
        self.assertEqual(len(empty.major_axis), 0)
        self.assertEqual(len(empty.minor_axis), 0)

    def test_constructor_observe_dtype(self):
        # GH #411
        panel = Panel(items=lrange(3), major_axis=lrange(3),
                      minor_axis=lrange(3), dtype='O')
        self.assertEqual(panel.values.dtype, np.object_)

    def test_constructor_dtypes(self):
        # GH #797

        def _check_dtype(panel, dtype):
            for i in panel.items:
                self.assertEqual(panel[i].values.dtype.name, dtype)

        # only nan holding types allowed here
        for dtype in ['float64', 'float32', 'object']:
            panel = Panel(items=lrange(2), major_axis=lrange(10),
                          minor_axis=lrange(5), dtype=dtype)
            _check_dtype(panel, dtype)

        for dtype in ['float64', 'float32', 'int64', 'int32', 'object']:
            panel = Panel(np.array(np.random.randn(2, 10, 5), dtype=dtype),
                          items=lrange(2),
                          major_axis=lrange(10),
                          minor_axis=lrange(5), dtype=dtype)
            _check_dtype(panel, dtype)

        for dtype in ['float64', 'float32', 'int64', 'int32', 'object']:
            panel = Panel(np.array(np.random.randn(2, 10, 5), dtype='O'),
                          items=lrange(2),
                          major_axis=lrange(10),
                          minor_axis=lrange(5), dtype=dtype)
            _check_dtype(panel, dtype)

        for dtype in ['float64', 'float32', 'int64', 'int32', 'object']:
            panel = Panel(np.random.randn(2, 10, 5), items=lrange(
                2), major_axis=lrange(10), minor_axis=lrange(5), dtype=dtype)
            _check_dtype(panel, dtype)

        for dtype in ['float64', 'float32', 'int64', 'int32', 'object']:
            df1 = DataFrame(np.random.randn(2, 5),
                            index=lrange(2), columns=lrange(5))
            df2 = DataFrame(np.random.randn(2, 5),
                            index=lrange(2), columns=lrange(5))
            panel = Panel.from_dict({'a': df1, 'b': df2}, dtype=dtype)
            _check_dtype(panel, dtype)

    def test_constructor_fails_with_not_3d_input(self):
        with tm.assertRaisesRegexp(ValueError,
                                   "The number of dimensions required is 3"):
            Panel(np.random.randn(10, 2))

    def test_consolidate(self):
        self.assertTrue(self.panel._data.is_consolidated())

        self.panel['foo'] = 1.
        self.assertFalse(self.panel._data.is_consolidated())

        panel = self.panel.consolidate()
        self.assertTrue(panel._data.is_consolidated())

    def test_ctor_dict(self):
        itema = self.panel['ItemA']
        itemb = self.panel['ItemB']

        d = {'A': itema, 'B': itemb[5:]}
        d2 = {'A': itema._series, 'B': itemb[5:]._series}
        d3 = {'A': None,
              'B': DataFrame(itemb[5:]._series),
              'C': DataFrame(itema._series)}

        wp = Panel.from_dict(d)
        wp2 = Panel.from_dict(d2)  # nested Dict

        # TODO: unused?
        wp3 = Panel.from_dict(d3)  # noqa

        self.assertTrue(wp.major_axis.equals(self.panel.major_axis))
        assert_panel_equal(wp, wp2)

        # intersect
        wp = Panel.from_dict(d, intersect=True)
        self.assertTrue(wp.major_axis.equals(itemb.index[5:]))

        # use constructor
        assert_panel_equal(Panel(d), Panel.from_dict(d))
        assert_panel_equal(Panel(d2), Panel.from_dict(d2))
        assert_panel_equal(Panel(d3), Panel.from_dict(d3))

        # a pathological case
        d4 = {'A': None, 'B': None}

        # TODO: unused?
        wp4 = Panel.from_dict(d4)  # noqa

        assert_panel_equal(Panel(d4), Panel(items=['A', 'B']))

        # cast
        dcasted = dict((k, v.reindex(wp.major_axis).fillna(0))
                       for k, v in compat.iteritems(d))
        result = Panel(dcasted, dtype=int)
        expected = Panel(dict((k, v.astype(int))
                              for k, v in compat.iteritems(dcasted)))
        assert_panel_equal(result, expected)

        result = Panel(dcasted, dtype=np.int32)
        expected = Panel(dict((k, v.astype(np.int32))
                              for k, v in compat.iteritems(dcasted)))
        assert_panel_equal(result, expected)

    def test_constructor_dict_mixed(self):
        data = dict((k, v.values) for k, v in self.panel.iteritems())
        result = Panel(data)
        exp_major = Index(np.arange(len(self.panel.major_axis)))
        self.assertTrue(result.major_axis.equals(exp_major))

        result = Panel(data, items=self.panel.items,
                       major_axis=self.panel.major_axis,
                       minor_axis=self.panel.minor_axis)
        assert_panel_equal(result, self.panel)

        data['ItemC'] = self.panel['ItemC']
        result = Panel(data)
        assert_panel_equal(result, self.panel)

        # corner, blow up
        data['ItemB'] = data['ItemB'][:-1]
        self.assertRaises(Exception, Panel, data)

        data['ItemB'] = self.panel['ItemB'].values[:, :-1]
        self.assertRaises(Exception, Panel, data)

    def test_ctor_orderedDict(self):
        keys = list(set(np.random.randint(0, 5000, 100)))[
            :50]  # unique random int  keys
        d = OrderedDict([(k, mkdf(10, 5)) for k in keys])
        p = Panel(d)
        self.assertTrue(list(p.items) == keys)

        p = Panel.from_dict(d)
        self.assertTrue(list(p.items) == keys)

    def test_constructor_resize(self):
        data = self.panel._data
        items = self.panel.items[:-1]
        major = self.panel.major_axis[:-1]
        minor = self.panel.minor_axis[:-1]

        result = Panel(data, items=items, major_axis=major, minor_axis=minor)
        expected = self.panel.reindex(items=items, major=major, minor=minor)
        assert_panel_equal(result, expected)

        result = Panel(data, items=items, major_axis=major)
        expected = self.panel.reindex(items=items, major=major)
        assert_panel_equal(result, expected)

        result = Panel(data, items=items)
        expected = self.panel.reindex(items=items)
        assert_panel_equal(result, expected)

        result = Panel(data, minor_axis=minor)
        expected = self.panel.reindex(minor=minor)
        assert_panel_equal(result, expected)

    def test_from_dict_mixed_orient(self):
        df = tm.makeDataFrame()
        df['foo'] = 'bar'

        data = {'k1': df, 'k2': df}

        panel = Panel.from_dict(data, orient='minor')

        self.assertEqual(panel['foo'].values.dtype, np.object_)
        self.assertEqual(panel['A'].values.dtype, np.float64)

    def test_constructor_error_msgs(self):
        def testit():
            Panel(np.random.randn(3, 4, 5), lrange(4), lrange(5), lrange(5))

        assertRaisesRegexp(ValueError,
                           "Shape of passed values is \(3, 4, 5\), "
                           "indices imply \(4, 5, 5\)",
                           testit)

        def testit():
            Panel(np.random.randn(3, 4, 5), lrange(5), lrange(4), lrange(5))

        assertRaisesRegexp(ValueError,
                           "Shape of passed values is \(3, 4, 5\), "
                           "indices imply \(5, 4, 5\)",
                           testit)

        def testit():
            Panel(np.random.randn(3, 4, 5), lrange(5), lrange(5), lrange(4))

        assertRaisesRegexp(ValueError,
                           "Shape of passed values is \(3, 4, 5\), "
                           "indices imply \(5, 5, 4\)",
                           testit)

    def test_conform(self):
        df = self.panel['ItemA'][:-5].filter(items=['A', 'B'])
        conformed = self.panel.conform(df)

        assert (conformed.index.equals(self.panel.major_axis))
        assert (conformed.columns.equals(self.panel.minor_axis))

    def test_convert_objects(self):

        # GH 4937
        p = Panel(dict(A=dict(a=['1', '1.0'])))
        expected = Panel(dict(A=dict(a=[1, 1.0])))
        result = p._convert(numeric=True, coerce=True)
        assert_panel_equal(result, expected)

    def test_dtypes(self):

        result = self.panel.dtypes
        expected = Series(np.dtype('float64'), index=self.panel.items)
        assert_series_equal(result, expected)

    def test_apply(self):
        # GH1148

        # ufunc
        applied = self.panel.apply(np.sqrt)
        self.assertTrue(assert_almost_equal(applied.values, np.sqrt(
            self.panel.values)))

        # ufunc same shape
        result = self.panel.apply(lambda x: x * 2, axis='items')
        expected = self.panel * 2
        assert_panel_equal(result, expected)
        result = self.panel.apply(lambda x: x * 2, axis='major_axis')
        expected = self.panel * 2
        assert_panel_equal(result, expected)
        result = self.panel.apply(lambda x: x * 2, axis='minor_axis')
        expected = self.panel * 2
        assert_panel_equal(result, expected)

        # reduction to DataFrame
        result = self.panel.apply(lambda x: x.dtype, axis='items')
        expected = DataFrame(np.dtype('float64'), index=self.panel.major_axis,
                             columns=self.panel.minor_axis)
        assert_frame_equal(result, expected)
        result = self.panel.apply(lambda x: x.dtype, axis='major_axis')
        expected = DataFrame(np.dtype('float64'), index=self.panel.minor_axis,
                             columns=self.panel.items)
        assert_frame_equal(result, expected)
        result = self.panel.apply(lambda x: x.dtype, axis='minor_axis')
        expected = DataFrame(np.dtype('float64'), index=self.panel.major_axis,
                             columns=self.panel.items)
        assert_frame_equal(result, expected)

        # reductions via other dims
        expected = self.panel.sum(0)
        result = self.panel.apply(lambda x: x.sum(), axis='items')
        assert_frame_equal(result, expected)
        expected = self.panel.sum(1)
        result = self.panel.apply(lambda x: x.sum(), axis='major_axis')
        assert_frame_equal(result, expected)
        expected = self.panel.sum(2)
        result = self.panel.apply(lambda x: x.sum(), axis='minor_axis')
        assert_frame_equal(result, expected)

        # pass kwargs
        result = self.panel.apply(lambda x, y: x.sum() + y, axis='items', y=5)
        expected = self.panel.sum(0) + 5
        assert_frame_equal(result, expected)

    def test_apply_slabs(self):

        # same shape as original
        result = self.panel.apply(lambda x: x * 2,
                                  axis=['items', 'major_axis'])
        expected = (self.panel * 2).transpose('minor_axis', 'major_axis',
                                              'items')
        assert_panel_equal(result, expected)
        result = self.panel.apply(lambda x: x * 2,
                                  axis=['major_axis', 'items'])
        assert_panel_equal(result, expected)

        result = self.panel.apply(lambda x: x * 2,
                                  axis=['items', 'minor_axis'])
        expected = (self.panel * 2).transpose('major_axis', 'minor_axis',
                                              'items')
        assert_panel_equal(result, expected)
        result = self.panel.apply(lambda x: x * 2,
                                  axis=['minor_axis', 'items'])
        assert_panel_equal(result, expected)

        result = self.panel.apply(lambda x: x * 2,
                                  axis=['major_axis', 'minor_axis'])
        expected = self.panel * 2
        assert_panel_equal(result, expected)
        result = self.panel.apply(lambda x: x * 2,
                                  axis=['minor_axis', 'major_axis'])
        assert_panel_equal(result, expected)

        # reductions
        result = self.panel.apply(lambda x: x.sum(0), axis=[
            'items', 'major_axis'
        ])
        expected = self.panel.sum(1).T
        assert_frame_equal(result, expected)

        result = self.panel.apply(lambda x: x.sum(1), axis=[
            'items', 'major_axis'
        ])
        expected = self.panel.sum(0)
        assert_frame_equal(result, expected)

        # transforms
        f = lambda x: ((x.T - x.mean(1)) / x.std(1)).T

        # make sure that we don't trigger any warnings
        with tm.assert_produces_warning(False):
            result = self.panel.apply(f, axis=['items', 'major_axis'])
            expected = Panel(dict([(ax, f(self.panel.loc[:, :, ax]))
                                   for ax in self.panel.minor_axis]))
            assert_panel_equal(result, expected)

        result = self.panel.apply(f, axis=['major_axis', 'minor_axis'])
        expected = Panel(dict([(ax, f(self.panel.loc[ax]))
                               for ax in self.panel.items]))
        assert_panel_equal(result, expected)

        result = self.panel.apply(f, axis=['minor_axis', 'items'])
        expected = Panel(dict([(ax, f(self.panel.loc[:, ax]))
                               for ax in self.panel.major_axis]))
        assert_panel_equal(result, expected)

        # with multi-indexes
        # GH7469
        index = MultiIndex.from_tuples([('one', 'a'), ('one', 'b'), (
            'two', 'a'), ('two', 'b')])
        dfa = DataFrame(np.array(np.arange(12, dtype='int64')).reshape(
            4, 3), columns=list("ABC"), index=index)
        dfb = DataFrame(np.array(np.arange(10, 22, dtype='int64')).reshape(
            4, 3), columns=list("ABC"), index=index)
        p = Panel({'f': dfa, 'g': dfb})
        result = p.apply(lambda x: x.sum(), axis=0)

        # on windows this will be in32
        result = result.astype('int64')
        expected = p.sum(0)
        assert_frame_equal(result, expected)

    def test_apply_no_or_zero_ndim(self):
        # GH10332
        self.panel = Panel(np.random.rand(5, 5, 5))

        result_int = self.panel.apply(lambda df: 0, axis=[1, 2])
        result_float = self.panel.apply(lambda df: 0.0, axis=[1, 2])
        result_int64 = self.panel.apply(lambda df: np.int64(0), axis=[1, 2])
        result_float64 = self.panel.apply(lambda df: np.float64(0.0),
                                          axis=[1, 2])

        expected_int = expected_int64 = Series([0] * 5)
        expected_float = expected_float64 = Series([0.0] * 5)

        assert_series_equal(result_int, expected_int)
        assert_series_equal(result_int64, expected_int64)
        assert_series_equal(result_float, expected_float)
        assert_series_equal(result_float64, expected_float64)

    def test_reindex(self):
        ref = self.panel['ItemB']

        # items
        result = self.panel.reindex(items=['ItemA', 'ItemB'])
        assert_frame_equal(result['ItemB'], ref)

        # major
        new_major = list(self.panel.major_axis[:10])
        result = self.panel.reindex(major=new_major)
        assert_frame_equal(result['ItemB'], ref.reindex(index=new_major))

        # raise exception put both major and major_axis
        self.assertRaises(Exception, self.panel.reindex, major_axis=new_major,
                          major=new_major)

        # minor
        new_minor = list(self.panel.minor_axis[:2])
        result = self.panel.reindex(minor=new_minor)
        assert_frame_equal(result['ItemB'], ref.reindex(columns=new_minor))

        # this ok
        result = self.panel.reindex()
        assert_panel_equal(result, self.panel)
        self.assertFalse(result is self.panel)

        # with filling
        smaller_major = self.panel.major_axis[::5]
        smaller = self.panel.reindex(major=smaller_major)

        larger = smaller.reindex(major=self.panel.major_axis, method='pad')

        assert_frame_equal(larger.major_xs(self.panel.major_axis[1]),
                           smaller.major_xs(smaller_major[0]))

        # don't necessarily copy
        result = self.panel.reindex(major=self.panel.major_axis, copy=False)
        assert_panel_equal(result, self.panel)
        self.assertTrue(result is self.panel)

    def test_reindex_multi(self):

        # with and without copy full reindexing
        result = self.panel.reindex(items=self.panel.items,
                                    major=self.panel.major_axis,
                                    minor=self.panel.minor_axis, copy=False)

        self.assertIs(result.items, self.panel.items)
        self.assertIs(result.major_axis, self.panel.major_axis)
        self.assertIs(result.minor_axis, self.panel.minor_axis)

        result = self.panel.reindex(items=self.panel.items,
                                    major=self.panel.major_axis,
                                    minor=self.panel.minor_axis, copy=False)
        assert_panel_equal(result, self.panel)

        # multi-axis indexing consistency
        # GH 5900
        df = DataFrame(np.random.randn(4, 3))
        p = Panel({'Item1': df})
        expected = Panel({'Item1': df})
        expected['Item2'] = np.nan

        items = ['Item1', 'Item2']
        major_axis = np.arange(4)
        minor_axis = np.arange(3)

        results = []
        results.append(p.reindex(items=items, major_axis=major_axis,
                                 copy=True))
        results.append(p.reindex(items=items, major_axis=major_axis,
                                 copy=False))
        results.append(p.reindex(items=items, minor_axis=minor_axis,
                                 copy=True))
        results.append(p.reindex(items=items, minor_axis=minor_axis,
                                 copy=False))
        results.append(p.reindex(items=items, major_axis=major_axis,
                                 minor_axis=minor_axis, copy=True))
        results.append(p.reindex(items=items, major_axis=major_axis,
                                 minor_axis=minor_axis, copy=False))

        for i, r in enumerate(results):
            assert_panel_equal(expected, r)

    def test_reindex_like(self):
        # reindex_like
        smaller = self.panel.reindex(items=self.panel.items[:-1],
                                     major=self.panel.major_axis[:-1],
                                     minor=self.panel.minor_axis[:-1])
        smaller_like = self.panel.reindex_like(smaller)
        assert_panel_equal(smaller, smaller_like)

    def test_take(self):
        # axis == 0
        result = self.panel.take([2, 0, 1], axis=0)
        expected = self.panel.reindex(items=['ItemC', 'ItemA', 'ItemB'])
        assert_panel_equal(result, expected)

        # axis >= 1
        result = self.panel.take([3, 0, 1, 2], axis=2)
        expected = self.panel.reindex(minor=['D', 'A', 'B', 'C'])
        assert_panel_equal(result, expected)

        # neg indicies ok
        expected = self.panel.reindex(minor=['D', 'D', 'B', 'C'])
        result = self.panel.take([3, -1, 1, 2], axis=2)
        assert_panel_equal(result, expected)

        self.assertRaises(Exception, self.panel.take, [4, 0, 1, 2], axis=2)

    def test_sort_index(self):
        import random

        ritems = list(self.panel.items)
        rmajor = list(self.panel.major_axis)
        rminor = list(self.panel.minor_axis)
        random.shuffle(ritems)
        random.shuffle(rmajor)
        random.shuffle(rminor)

        random_order = self.panel.reindex(items=ritems)
        sorted_panel = random_order.sort_index(axis=0)
        assert_panel_equal(sorted_panel, self.panel)

        # descending
        random_order = self.panel.reindex(items=ritems)
        sorted_panel = random_order.sort_index(axis=0, ascending=False)
        assert_panel_equal(sorted_panel,
                           self.panel.reindex(items=self.panel.items[::-1]))

        random_order = self.panel.reindex(major=rmajor)
        sorted_panel = random_order.sort_index(axis=1)
        assert_panel_equal(sorted_panel, self.panel)

        random_order = self.panel.reindex(minor=rminor)
        sorted_panel = random_order.sort_index(axis=2)
        assert_panel_equal(sorted_panel, self.panel)

    def test_fillna(self):
        filled = self.panel.fillna(0)
        self.assertTrue(np.isfinite(filled.values).all())

        filled = self.panel.fillna(method='backfill')
        assert_frame_equal(filled['ItemA'],
                           self.panel['ItemA'].fillna(method='backfill'))

        panel = self.panel.copy()
        panel['str'] = 'foo'

        filled = panel.fillna(method='backfill')
        assert_frame_equal(filled['ItemA'],
                           panel['ItemA'].fillna(method='backfill'))

        empty = self.panel.reindex(items=[])
        filled = empty.fillna(0)
        assert_panel_equal(filled, empty)

        self.assertRaises(ValueError, self.panel.fillna)
        self.assertRaises(ValueError, self.panel.fillna, 5, method='ffill')

        self.assertRaises(TypeError, self.panel.fillna, [1, 2])
        self.assertRaises(TypeError, self.panel.fillna, (1, 2))

        # limit not implemented when only value is specified
        p = Panel(np.random.randn(3, 4, 5))
        p.iloc[0:2, 0:2, 0:2] = np.nan
        self.assertRaises(NotImplementedError, lambda: p.fillna(999, limit=1))

        # Test in place fillNA
        # Expected result
        expected = Panel([[[0, 1], [2, 1]], [[10, 11], [12, 11]]],
                         items=['a', 'b'], minor_axis=['x', 'y'],
                         dtype=np.float64)
        # method='ffill'
        p1 = Panel([[[0, 1], [2, np.nan]], [[10, 11], [12, np.nan]]],
                   items=['a', 'b'], minor_axis=['x', 'y'],
                   dtype=np.float64)
        p1.fillna(method='ffill', inplace=True)
        assert_panel_equal(p1, expected)

        # method='bfill'
        p2 = Panel([[[0, np.nan], [2, 1]], [[10, np.nan], [12, 11]]],
                   items=['a', 'b'], minor_axis=['x', 'y'], dtype=np.float64)
        p2.fillna(method='bfill', inplace=True)
        assert_panel_equal(p2, expected)

    def test_ffill_bfill(self):
        assert_panel_equal(self.panel.ffill(),
                           self.panel.fillna(method='ffill'))
        assert_panel_equal(self.panel.bfill(),
                           self.panel.fillna(method='bfill'))

    def test_truncate_fillna_bug(self):
        # #1823
        result = self.panel.truncate(before=None, after=None, axis='items')

        # it works!
        result.fillna(value=0.0)

    def test_swapaxes(self):
        result = self.panel.swapaxes('items', 'minor')
        self.assertIs(result.items, self.panel.minor_axis)

        result = self.panel.swapaxes('items', 'major')
        self.assertIs(result.items, self.panel.major_axis)

        result = self.panel.swapaxes('major', 'minor')
        self.assertIs(result.major_axis, self.panel.minor_axis)

        panel = self.panel.copy()
        result = panel.swapaxes('major', 'minor')
        panel.values[0, 0, 1] = np.nan
        expected = panel.swapaxes('major', 'minor')
        assert_panel_equal(result, expected)

        # this should also work
        result = self.panel.swapaxes(0, 1)
        self.assertIs(result.items, self.panel.major_axis)

        # this works, but return a copy
        result = self.panel.swapaxes('items', 'items')
        assert_panel_equal(self.panel, result)
        self.assertNotEqual(id(self.panel), id(result))

    def test_transpose(self):
        result = self.panel.transpose('minor', 'major', 'items')
        expected = self.panel.swapaxes('items', 'minor')
        assert_panel_equal(result, expected)

        # test kwargs
        result = self.panel.transpose(items='minor', major='major',
                                      minor='items')
        expected = self.panel.swapaxes('items', 'minor')
        assert_panel_equal(result, expected)

        # text mixture of args
        result = self.panel.transpose('minor', major='major', minor='items')
        expected = self.panel.swapaxes('items', 'minor')
        assert_panel_equal(result, expected)

        result = self.panel.transpose('minor', 'major', minor='items')
        expected = self.panel.swapaxes('items', 'minor')
        assert_panel_equal(result, expected)

        # duplicate axes
        with tm.assertRaisesRegexp(TypeError,
                                   'not enough/duplicate arguments'):
            self.panel.transpose('minor', maj='major', minor='items')

        with tm.assertRaisesRegexp(ValueError, 'repeated axis in transpose'):
            self.panel.transpose('minor', 'major', major='minor',
                                 minor='items')

        result = self.panel.transpose(2, 1, 0)
        assert_panel_equal(result, expected)

        result = self.panel.transpose('minor', 'items', 'major')
        expected = self.panel.swapaxes('items', 'minor')
        expected = expected.swapaxes('major', 'minor')
        assert_panel_equal(result, expected)

        result = self.panel.transpose(2, 0, 1)
        assert_panel_equal(result, expected)

        self.assertRaises(ValueError, self.panel.transpose, 0, 0, 1)

    def test_transpose_copy(self):
        panel = self.panel.copy()
        result = panel.transpose(2, 0, 1, copy=True)
        expected = panel.swapaxes('items', 'minor')
        expected = expected.swapaxes('major', 'minor')
        assert_panel_equal(result, expected)

        panel.values[0, 1, 1] = np.nan
        self.assertTrue(notnull(result.values[1, 0, 1]))

    @ignore_sparse_panel_future_warning
    def test_to_frame(self):
        # filtered
        filtered = self.panel.to_frame()
        expected = self.panel.to_frame().dropna(how='any')
        assert_frame_equal(filtered, expected)

        # unfiltered
        unfiltered = self.panel.to_frame(filter_observations=False)
        assert_panel_equal(unfiltered.to_panel(), self.panel)

        # names
        self.assertEqual(unfiltered.index.names, ('major', 'minor'))

        # unsorted, round trip
        df = self.panel.to_frame(filter_observations=False)
        unsorted = df.take(np.random.permutation(len(df)))
        pan = unsorted.to_panel()
        assert_panel_equal(pan, self.panel)

        # preserve original index names
        df = DataFrame(np.random.randn(6, 2),
                       index=[['a', 'a', 'b', 'b', 'c', 'c'],
                              [0, 1, 0, 1, 0, 1]],
                       columns=['one', 'two'])
        df.index.names = ['foo', 'bar']
        df.columns.name = 'baz'

        rdf = df.to_panel().to_frame()
        self.assertEqual(rdf.index.names, df.index.names)
        self.assertEqual(rdf.columns.names, df.columns.names)

    def test_to_frame_mixed(self):
        panel = self.panel.fillna(0)
        panel['str'] = 'foo'
        panel['bool'] = panel['ItemA'] > 0

        lp = panel.to_frame()
        wp = lp.to_panel()
        self.assertEqual(wp['bool'].values.dtype, np.bool_)
        # Previously, this was mutating the underlying index and changing its
        # name
        assert_frame_equal(wp['bool'], panel['bool'], check_names=False)

        # GH 8704
        # with categorical
        df = panel.to_frame()
        df['category'] = df['str'].astype('category')

        # to_panel
        # TODO: this converts back to object
        p = df.to_panel()
        expected = panel.copy()
        expected['category'] = 'foo'
        assert_panel_equal(p, expected)

    def test_to_frame_multi_major(self):
        idx = MultiIndex.from_tuples([(1, 'one'), (1, 'two'), (2, 'one'), (
            2, 'two')])
        df = DataFrame([[1, 'a', 1], [2, 'b', 1], [3, 'c', 1], [4, 'd', 1]],
                       columns=['A', 'B', 'C'], index=idx)
        wp = Panel({'i1': df, 'i2': df})
        expected_idx = MultiIndex.from_tuples(
            [
                (1, 'one', 'A'), (1, 'one', 'B'),
                (1, 'one', 'C'), (1, 'two', 'A'),
                (1, 'two', 'B'), (1, 'two', 'C'),
                (2, 'one', 'A'), (2, 'one', 'B'),
                (2, 'one', 'C'), (2, 'two', 'A'),
                (2, 'two', 'B'), (2, 'two', 'C')
            ],
            names=[None, None, 'minor'])
        expected = DataFrame({'i1': [1, 'a', 1, 2, 'b', 1, 3,
                                     'c', 1, 4, 'd', 1],
                              'i2': [1, 'a', 1, 2, 'b',
                                     1, 3, 'c', 1, 4, 'd', 1]},
                             index=expected_idx)
        result = wp.to_frame()
        assert_frame_equal(result, expected)

        wp.iloc[0, 0].iloc[0] = np.nan  # BUG on setting. GH #5773
        result = wp.to_frame()
        assert_frame_equal(result, expected[1:])

        idx = MultiIndex.from_tuples([(1, 'two'), (1, 'one'), (2, 'one'), (
            np.nan, 'two')])
        df = DataFrame([[1, 'a', 1], [2, 'b', 1], [3, 'c', 1], [4, 'd', 1]],
                       columns=['A', 'B', 'C'], index=idx)
        wp = Panel({'i1': df, 'i2': df})
        ex_idx = MultiIndex.from_tuples([(1, 'two', 'A'), (1, 'two', 'B'),
                                         (1, 'two', 'C'),
                                         (1, 'one', 'A'),
                                         (1, 'one', 'B'),
                                         (1, 'one', 'C'),
                                         (2, 'one', 'A'),
                                         (2, 'one', 'B'),
                                         (2, 'one', 'C'),
                                         (np.nan, 'two', 'A'),
                                         (np.nan, 'two', 'B'),
                                         (np.nan, 'two', 'C')],
                                        names=[None, None, 'minor'])
        expected.index = ex_idx
        result = wp.to_frame()
        assert_frame_equal(result, expected)

    def test_to_frame_multi_major_minor(self):
        cols = MultiIndex(levels=[['C_A', 'C_B'], ['C_1', 'C_2']],
                          labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
        idx = MultiIndex.from_tuples([(1, 'one'), (1, 'two'), (2, 'one'), (
            2, 'two'), (3, 'three'), (4, 'four')])
        df = DataFrame([[1, 2, 11, 12], [3, 4, 13, 14],
                        ['a', 'b', 'w', 'x'],
                        ['c', 'd', 'y', 'z'], [-1, -2, -3, -4],
                        [-5, -6, -7, -8]], columns=cols, index=idx)
        wp = Panel({'i1': df, 'i2': df})

        exp_idx = MultiIndex.from_tuples(
            [(1, 'one', 'C_A', 'C_1'), (1, 'one', 'C_A', 'C_2'),
             (1, 'one', 'C_B', 'C_1'), (1, 'one', 'C_B', 'C_2'),
             (1, 'two', 'C_A', 'C_1'), (1, 'two', 'C_A', 'C_2'),
             (1, 'two', 'C_B', 'C_1'), (1, 'two', 'C_B', 'C_2'),
             (2, 'one', 'C_A', 'C_1'), (2, 'one', 'C_A', 'C_2'),
             (2, 'one', 'C_B', 'C_1'), (2, 'one', 'C_B', 'C_2'),
             (2, 'two', 'C_A', 'C_1'), (2, 'two', 'C_A', 'C_2'),
             (2, 'two', 'C_B', 'C_1'), (2, 'two', 'C_B', 'C_2'),
             (3, 'three', 'C_A', 'C_1'), (3, 'three', 'C_A', 'C_2'),
             (3, 'three', 'C_B', 'C_1'), (3, 'three', 'C_B', 'C_2'),
             (4, 'four', 'C_A', 'C_1'), (4, 'four', 'C_A', 'C_2'),
             (4, 'four', 'C_B', 'C_1'), (4, 'four', 'C_B', 'C_2')],
            names=[None, None, None, None])
        exp_val = [[1, 1], [2, 2], [11, 11], [12, 12], [3, 3], [4, 4],
                   [13, 13], [14, 14], ['a', 'a'], ['b', 'b'], ['w', 'w'],
                   ['x', 'x'], ['c', 'c'], ['d', 'd'], ['y', 'y'], ['z', 'z'],
                   [-1, -1], [-2, -2], [-3, -3], [-4, -4], [-5, -5], [-6, -6],
                   [-7, -7], [-8, -8]]
        result = wp.to_frame()
        expected = DataFrame(exp_val, columns=['i1', 'i2'], index=exp_idx)
        assert_frame_equal(result, expected)

    def test_to_frame_multi_drop_level(self):
        idx = MultiIndex.from_tuples([(1, 'one'), (2, 'one'), (2, 'two')])
        df = DataFrame({'A': [np.nan, 1, 2]}, index=idx)
        wp = Panel({'i1': df, 'i2': df})
        result = wp.to_frame()
        exp_idx = MultiIndex.from_tuples([(2, 'one', 'A'), (2, 'two', 'A')],
                                         names=[None, None, 'minor'])
        expected = DataFrame({'i1': [1., 2], 'i2': [1., 2]}, index=exp_idx)
        assert_frame_equal(result, expected)

    def test_to_panel_na_handling(self):
        df = DataFrame(np.random.randint(0, 10, size=20).reshape((10, 2)),
                       index=[[0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
                              [0, 1, 2, 3, 4, 5, 2, 3, 4, 5]])

        panel = df.to_panel()
        self.assertTrue(isnull(panel[0].ix[1, [0, 1]]).all())

    def test_to_panel_duplicates(self):
        # #2441
        df = DataFrame({'a': [0, 0, 1], 'b': [1, 1, 1], 'c': [1, 2, 3]})
        idf = df.set_index(['a', 'b'])
        assertRaisesRegexp(ValueError, 'non-uniquely indexed', idf.to_panel)

    def test_panel_dups(self):

        # GH 4960
        # duplicates in an index

        # items
        data = np.random.randn(5, 100, 5)
        no_dup_panel = Panel(data, items=list("ABCDE"))
        panel = Panel(data, items=list("AACDE"))

        expected = no_dup_panel['A']
        result = panel.iloc[0]
        assert_frame_equal(result, expected)

        expected = no_dup_panel['E']
        result = panel.loc['E']
        assert_frame_equal(result, expected)

        expected = no_dup_panel.loc[['A', 'B']]
        expected.items = ['A', 'A']
        result = panel.loc['A']
        assert_panel_equal(result, expected)

        # major
        data = np.random.randn(5, 5, 5)
        no_dup_panel = Panel(data, major_axis=list("ABCDE"))
        panel = Panel(data, major_axis=list("AACDE"))

        expected = no_dup_panel.loc[:, 'A']
        result = panel.iloc[:, 0]
        assert_frame_equal(result, expected)

        expected = no_dup_panel.loc[:, 'E']
        result = panel.loc[:, 'E']
        assert_frame_equal(result, expected)

        expected = no_dup_panel.loc[:, ['A', 'B']]
        expected.major_axis = ['A', 'A']
        result = panel.loc[:, 'A']
        assert_panel_equal(result, expected)

        # minor
        data = np.random.randn(5, 100, 5)
        no_dup_panel = Panel(data, minor_axis=list("ABCDE"))
        panel = Panel(data, minor_axis=list("AACDE"))

        expected = no_dup_panel.loc[:, :, 'A']
        result = panel.iloc[:, :, 0]
        assert_frame_equal(result, expected)

        expected = no_dup_panel.loc[:, :, 'E']
        result = panel.loc[:, :, 'E']
        assert_frame_equal(result, expected)

        expected = no_dup_panel.loc[:, :, ['A', 'B']]
        expected.minor_axis = ['A', 'A']
        result = panel.loc[:, :, 'A']
        assert_panel_equal(result, expected)

    def test_filter(self):
        pass

    def test_compound(self):
        compounded = self.panel.compound()

        assert_series_equal(compounded['ItemA'],
                            (1 + self.panel['ItemA']).product(0) - 1,
                            check_names=False)

    def test_shift(self):
        # major
        idx = self.panel.major_axis[0]
        idx_lag = self.panel.major_axis[1]
        shifted = self.panel.shift(1)
        assert_frame_equal(self.panel.major_xs(idx), shifted.major_xs(idx_lag))

        # minor
        idx = self.panel.minor_axis[0]
        idx_lag = self.panel.minor_axis[1]
        shifted = self.panel.shift(1, axis='minor')
        assert_frame_equal(self.panel.minor_xs(idx), shifted.minor_xs(idx_lag))

        # items
        idx = self.panel.items[0]
        idx_lag = self.panel.items[1]
        shifted = self.panel.shift(1, axis='items')
        assert_frame_equal(self.panel[idx], shifted[idx_lag])

        # negative numbers, #2164
        result = self.panel.shift(-1)
        expected = Panel(dict((i, f.shift(-1)[:-1])
                              for i, f in self.panel.iteritems()))
        assert_panel_equal(result, expected)

        # mixed dtypes #6959
        data = [('item ' + ch, makeMixedDataFrame()) for ch in list('abcde')]
        data = dict(data)
        mixed_panel = Panel.from_dict(data, orient='minor')
        shifted = mixed_panel.shift(1)
        assert_series_equal(mixed_panel.dtypes, shifted.dtypes)

    def test_tshift(self):
        # PeriodIndex
        ps = tm.makePeriodPanel()
        shifted = ps.tshift(1)
        unshifted = shifted.tshift(-1)

        assert_panel_equal(unshifted, ps)

        shifted2 = ps.tshift(freq='B')
        assert_panel_equal(shifted, shifted2)

        shifted3 = ps.tshift(freq=bday)
        assert_panel_equal(shifted, shifted3)

        assertRaisesRegexp(ValueError, 'does not match', ps.tshift, freq='M')

        # DatetimeIndex
        panel = _panel
        shifted = panel.tshift(1)
        unshifted = shifted.tshift(-1)

        assert_panel_equal(panel, unshifted)

        shifted2 = panel.tshift(freq=panel.major_axis.freq)
        assert_panel_equal(shifted, shifted2)

        inferred_ts = Panel(panel.values, items=panel.items,
                            major_axis=Index(np.asarray(panel.major_axis)),
                            minor_axis=panel.minor_axis)
        shifted = inferred_ts.tshift(1)
        unshifted = shifted.tshift(-1)
        assert_panel_equal(shifted, panel.tshift(1))
        assert_panel_equal(unshifted, inferred_ts)

        no_freq = panel.ix[:, [0, 5, 7], :]
        self.assertRaises(ValueError, no_freq.tshift)

    def test_pct_change(self):
        df1 = DataFrame({'c1': [1, 2, 5], 'c2': [3, 4, 6]})
        df2 = df1 + 1
        df3 = DataFrame({'c1': [3, 4, 7], 'c2': [5, 6, 8]})
        wp = Panel({'i1': df1, 'i2': df2, 'i3': df3})
        # major, 1
        result = wp.pct_change()  # axis='major'
        expected = Panel({'i1': df1.pct_change(),
                          'i2': df2.pct_change(),
                          'i3': df3.pct_change()})
        assert_panel_equal(result, expected)
        result = wp.pct_change(axis=1)
        assert_panel_equal(result, expected)
        # major, 2
        result = wp.pct_change(periods=2)
        expected = Panel({'i1': df1.pct_change(2),
                          'i2': df2.pct_change(2),
                          'i3': df3.pct_change(2)})
        assert_panel_equal(result, expected)
        # minor, 1
        result = wp.pct_change(axis='minor')
        expected = Panel({'i1': df1.pct_change(axis=1),
                          'i2': df2.pct_change(axis=1),
                          'i3': df3.pct_change(axis=1)})
        assert_panel_equal(result, expected)
        result = wp.pct_change(axis=2)
        assert_panel_equal(result, expected)
        # minor, 2
        result = wp.pct_change(periods=2, axis='minor')
        expected = Panel({'i1': df1.pct_change(periods=2, axis=1),
                          'i2': df2.pct_change(periods=2, axis=1),
                          'i3': df3.pct_change(periods=2, axis=1)})
        assert_panel_equal(result, expected)
        # items, 1
        result = wp.pct_change(axis='items')
        expected = Panel({'i1': DataFrame({'c1': [np.nan, np.nan, np.nan],
                                           'c2': [np.nan, np.nan, np.nan]}),
                          'i2': DataFrame({'c1': [1, 0.5, .2],
                                           'c2': [1. / 3, 0.25, 1. / 6]}),
                          'i3': DataFrame({'c1': [.5, 1. / 3, 1. / 6],
                                           'c2': [.25, .2, 1. / 7]})})
        assert_panel_equal(result, expected)
        result = wp.pct_change(axis=0)
        assert_panel_equal(result, expected)
        # items, 2
        result = wp.pct_change(periods=2, axis='items')
        expected = Panel({'i1': DataFrame({'c1': [np.nan, np.nan, np.nan],
                                           'c2': [np.nan, np.nan, np.nan]}),
                          'i2': DataFrame({'c1': [np.nan, np.nan, np.nan],
                                           'c2': [np.nan, np.nan, np.nan]}),
                          'i3': DataFrame({'c1': [2, 1, .4],
                                           'c2': [2. / 3, .5, 1. / 3]})})
        assert_panel_equal(result, expected)

    def test_round(self):
        values = [[[-3.2, 2.2], [0, -4.8213], [3.123, 123.12],
                   [-1566.213, 88.88], [-12, 94.5]],
                  [[-5.82, 3.5], [6.21, -73.272], [-9.087, 23.12],
                   [272.212, -99.99], [23, -76.5]]]
        evalues = [[[float(np.around(i)) for i in j] for j in k]
                   for k in values]
        p = Panel(values, items=['Item1', 'Item2'],
                  major_axis=pd.date_range('1/1/2000', periods=5),
                  minor_axis=['A', 'B'])
        expected = Panel(evalues, items=['Item1', 'Item2'],
                         major_axis=pd.date_range('1/1/2000', periods=5),
                         minor_axis=['A', 'B'])
        result = p.round()
        self.assert_panel_equal(expected, result)

    def test_numpy_round(self):
        values = [[[-3.2, 2.2], [0, -4.8213], [3.123, 123.12],
                   [-1566.213, 88.88], [-12, 94.5]],
                  [[-5.82, 3.5], [6.21, -73.272], [-9.087, 23.12],
                   [272.212, -99.99], [23, -76.5]]]
        evalues = [[[float(np.around(i)) for i in j] for j in k]
                   for k in values]
        p = Panel(values, items=['Item1', 'Item2'],
                  major_axis=pd.date_range('1/1/2000', periods=5),
                  minor_axis=['A', 'B'])
        expected = Panel(evalues, items=['Item1', 'Item2'],
                         major_axis=pd.date_range('1/1/2000', periods=5),
                         minor_axis=['A', 'B'])
        result = np.round(p)
        self.assert_panel_equal(expected, result)

        msg = "the 'out' parameter is not supported"
        tm.assertRaisesRegexp(ValueError, msg, np.round, p, out=p)

    def test_multiindex_get(self):
        ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b', 2)],
                                     names=['first', 'second'])
        wp = Panel(np.random.random((4, 5, 5)),
                   items=ind,
                   major_axis=np.arange(5),
                   minor_axis=np.arange(5))
        f1 = wp['a']
        f2 = wp.ix['a']
        assert_panel_equal(f1, f2)

        self.assertTrue((f1.items == [1, 2]).all())
        self.assertTrue((f2.items == [1, 2]).all())

        ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)],
                                     names=['first', 'second'])

    def test_multiindex_blocks(self):
        ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)],
                                     names=['first', 'second'])
        wp = Panel(self.panel._data)
        wp.items = ind
        f1 = wp['a']
        self.assertTrue((f1.items == [1, 2]).all())

        f1 = wp[('b', 1)]
        self.assertTrue((f1.columns == ['A', 'B', 'C', 'D']).all())

    def test_repr_empty(self):
        empty = Panel()
        repr(empty)

    def test_rename(self):
        mapper = {'ItemA': 'foo', 'ItemB': 'bar', 'ItemC': 'baz'}

        renamed = self.panel.rename_axis(mapper, axis=0)
        exp = Index(['foo', 'bar', 'baz'])
        self.assertTrue(renamed.items.equals(exp))

        renamed = self.panel.rename_axis(str.lower, axis=2)
        exp = Index(['a', 'b', 'c', 'd'])
        self.assertTrue(renamed.minor_axis.equals(exp))

        # don't copy
        renamed_nocopy = self.panel.rename_axis(mapper, axis=0, copy=False)
        renamed_nocopy['foo'] = 3.
        self.assertTrue((self.panel['ItemA'].values == 3).all())

    def test_get_attr(self):
        assert_frame_equal(self.panel['ItemA'], self.panel.ItemA)

        # specific cases from #3440
        self.panel['a'] = self.panel['ItemA']
        assert_frame_equal(self.panel['a'], self.panel.a)
        self.panel['i'] = self.panel['ItemA']
        assert_frame_equal(self.panel['i'], self.panel.i)

    def test_from_frame_level1_unsorted(self):
        tuples = [('MSFT', 3), ('MSFT', 2), ('AAPL', 2), ('AAPL', 1),
                  ('MSFT', 1)]
        midx = MultiIndex.from_tuples(tuples)
        df = DataFrame(np.random.rand(5, 4), index=midx)
        p = df.to_panel()
        assert_frame_equal(p.minor_xs(2), df.xs(2, level=1).sort_index())

    def test_to_excel(self):
        try:
            import xlwt  # noqa
            import xlrd  # noqa
            import openpyxl  # noqa
            from pandas.io.excel import ExcelFile
        except ImportError:
            raise nose.SkipTest("need xlwt xlrd openpyxl")

        for ext in ['xls', 'xlsx']:
            with ensure_clean('__tmp__.' + ext) as path:
                self.panel.to_excel(path)
                try:
                    reader = ExcelFile(path)
                except ImportError:
                    raise nose.SkipTest("need xlwt xlrd openpyxl")

                for item, df in self.panel.iteritems():
                    recdf = reader.parse(str(item), index_col=0)
                    assert_frame_equal(df, recdf)

    def test_to_excel_xlsxwriter(self):
        try:
            import xlrd  # noqa
            import xlsxwriter  # noqa
            from pandas.io.excel import ExcelFile
        except ImportError:
            raise nose.SkipTest("Requires xlrd and xlsxwriter. Skipping test.")

        with ensure_clean('__tmp__.xlsx') as path:
            self.panel.to_excel(path, engine='xlsxwriter')
            try:
                reader = ExcelFile(path)
            except ImportError as e:
                raise nose.SkipTest("cannot write excel file: %s" % e)

            for item, df in self.panel.iteritems():
                recdf = reader.parse(str(item), index_col=0)
                assert_frame_equal(df, recdf)

    def test_dropna(self):
        p = Panel(np.random.randn(4, 5, 6), major_axis=list('abcde'))
        p.ix[:, ['b', 'd'], 0] = np.nan

        result = p.dropna(axis=1)
        exp = p.ix[:, ['a', 'c', 'e'], :]
        assert_panel_equal(result, exp)
        inp = p.copy()
        inp.dropna(axis=1, inplace=True)
        assert_panel_equal(inp, exp)

        result = p.dropna(axis=1, how='all')
        assert_panel_equal(result, p)

        p.ix[:, ['b', 'd'], :] = np.nan
        result = p.dropna(axis=1, how='all')
        exp = p.ix[:, ['a', 'c', 'e'], :]
        assert_panel_equal(result, exp)

        p = Panel(np.random.randn(4, 5, 6), items=list('abcd'))
        p.ix[['b'], :, 0] = np.nan

        result = p.dropna()
        exp = p.ix[['a', 'c', 'd']]
        assert_panel_equal(result, exp)

        result = p.dropna(how='all')
        assert_panel_equal(result, p)

        p.ix['b'] = np.nan
        result = p.dropna(how='all')
        exp = p.ix[['a', 'c', 'd']]
        assert_panel_equal(result, exp)

    def test_drop(self):
        df = DataFrame({"A": [1, 2], "B": [3, 4]})
        panel = Panel({"One": df, "Two": df})

        def check_drop(drop_val, axis_number, aliases, expected):
            try:
                actual = panel.drop(drop_val, axis=axis_number)
                assert_panel_equal(actual, expected)
                for alias in aliases:
                    actual = panel.drop(drop_val, axis=alias)
                    assert_panel_equal(actual, expected)
            except AssertionError:
                pprint_thing("Failed with axis_number %d and aliases: %s" %
                             (axis_number, aliases))
                raise
        # Items
        expected = Panel({"One": df})
        check_drop('Two', 0, ['items'], expected)

        self.assertRaises(ValueError, panel.drop, 'Three')

        # errors = 'ignore'
        dropped = panel.drop('Three', errors='ignore')
        assert_panel_equal(dropped, panel)
        dropped = panel.drop(['Two', 'Three'], errors='ignore')
        expected = Panel({"One": df})
        assert_panel_equal(dropped, expected)

        # Major
        exp_df = DataFrame({"A": [2], "B": [4]}, index=[1])
        expected = Panel({"One": exp_df, "Two": exp_df})
        check_drop(0, 1, ['major_axis', 'major'], expected)

        exp_df = DataFrame({"A": [1], "B": [3]}, index=[0])
        expected = Panel({"One": exp_df, "Two": exp_df})
        check_drop([1], 1, ['major_axis', 'major'], expected)

        # Minor
        exp_df = df[['B']]
        expected = Panel({"One": exp_df, "Two": exp_df})
        check_drop(["A"], 2, ['minor_axis', 'minor'], expected)

        exp_df = df[['A']]
        expected = Panel({"One": exp_df, "Two": exp_df})
        check_drop("B", 2, ['minor_axis', 'minor'], expected)

    def test_update(self):
        pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]],
                     [[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]]])

        other = Panel([[[3.6, 2., np.nan], [np.nan, np.nan, 7]]], items=[1])

        pan.update(other)

        expected = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.],
                           [1.5, np.nan, 3.], [1.5, np.nan, 3.]],
                          [[3.6, 2., 3], [1.5, np.nan, 7], [1.5, np.nan, 3.],
                           [1.5, np.nan, 3.]]])

        assert_panel_equal(pan, expected)

    def test_update_from_dict(self):
        pan = Panel({'one': DataFrame([[1.5, np.nan, 3], [1.5, np.nan, 3],
                                       [1.5, np.nan, 3.], [1.5, np.nan, 3.]]),
                     'two': DataFrame([[1.5, np.nan, 3.], [1.5, np.nan, 3.],
                                       [1.5, np.nan, 3.], [1.5, np.nan, 3.]])})

        other = {'two': DataFrame([[3.6, 2., np.nan], [np.nan, np.nan, 7]])}

        pan.update(other)

        expected = Panel(
            {'two': DataFrame([[3.6, 2., 3], [1.5, np.nan, 7],
                               [1.5, np.nan, 3.], [1.5, np.nan, 3.]]),
             'one': DataFrame([[1.5, np.nan, 3.], [1.5, np.nan, 3.],
                               [1.5, np.nan, 3.], [1.5, np.nan, 3.]])})

        assert_panel_equal(pan, expected)

    def test_update_nooverwrite(self):
        pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]],
                     [[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]]])

        other = Panel([[[3.6, 2., np.nan], [np.nan, np.nan, 7]]], items=[1])

        pan.update(other, overwrite=False)

        expected = Panel([[[1.5, np.nan, 3], [1.5, np.nan, 3],
                           [1.5, np.nan, 3.], [1.5, np.nan, 3.]],
                          [[1.5, 2., 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                           [1.5, np.nan, 3.]]])

        assert_panel_equal(pan, expected)

    def test_update_filtered(self):
        pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]],
                     [[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]]])

        other = Panel([[[3.6, 2., np.nan], [np.nan, np.nan, 7]]], items=[1])

        pan.update(other, filter_func=lambda x: x > 2)

        expected = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.],
                           [1.5, np.nan, 3.], [1.5, np.nan, 3.]],
                          [[1.5, np.nan, 3], [1.5, np.nan, 7],
                           [1.5, np.nan, 3.], [1.5, np.nan, 3.]]])

        assert_panel_equal(pan, expected)

    def test_update_raise(self):
        pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]],
                     [[1.5, np.nan, 3.], [1.5, np.nan, 3.], [1.5, np.nan, 3.],
                      [1.5, np.nan, 3.]]])

        np.testing.assert_raises(Exception, pan.update, *(pan, ),
                                 **{'raise_conflict': True})

    def test_all_any(self):
        self.assertTrue((self.panel.all(axis=0).values == nanall(
            self.panel, axis=0)).all())
        self.assertTrue((self.panel.all(axis=1).values == nanall(
            self.panel, axis=1).T).all())
        self.assertTrue((self.panel.all(axis=2).values == nanall(
            self.panel, axis=2).T).all())
        self.assertTrue((self.panel.any(axis=0).values == nanany(
            self.panel, axis=0)).all())
        self.assertTrue((self.panel.any(axis=1).values == nanany(
            self.panel, axis=1).T).all())
        self.assertTrue((self.panel.any(axis=2).values == nanany(
            self.panel, axis=2).T).all())

    def test_all_any_unhandled(self):
        self.assertRaises(NotImplementedError, self.panel.all, bool_only=True)
        self.assertRaises(NotImplementedError, self.panel.any, bool_only=True)


class TestLongPanel(tm.TestCase):
    """
    LongPanel no longer exists, but...
    """
    _multiprocess_can_split_ = True

    def setUp(self):
        import warnings
        warnings.filterwarnings(action='ignore', category=FutureWarning)

        panel = tm.makePanel()
        tm.add_nans(panel)

        self.panel = panel.to_frame()
        self.unfiltered_panel = panel.to_frame(filter_observations=False)

    def test_ops_differently_indexed(self):
        # trying to set non-identically indexed panel
        wp = self.panel.to_panel()
        wp2 = wp.reindex(major=wp.major_axis[:-1])
        lp2 = wp2.to_frame()

        result = self.panel + lp2
        assert_frame_equal(result.reindex(lp2.index), lp2 * 2)

        # careful, mutation
        self.panel['foo'] = lp2['ItemA']
        assert_series_equal(self.panel['foo'].reindex(lp2.index), lp2['ItemA'],
                            check_names=False)

    def test_ops_scalar(self):
        result = self.panel.mul(2)
        expected = DataFrame.__mul__(self.panel, 2)
        assert_frame_equal(result, expected)

    def test_combineFrame(self):
        wp = self.panel.to_panel()
        result = self.panel.add(wp['ItemA'].stack(), axis=0)
        assert_frame_equal(result.to_panel()['ItemA'], wp['ItemA'] * 2)

    def test_combinePanel(self):
        wp = self.panel.to_panel()
        result = self.panel.add(self.panel)
        wide_result = result.to_panel()
        assert_frame_equal(wp['ItemA'] * 2, wide_result['ItemA'])

        # one item
        result = self.panel.add(self.panel.filter(['ItemA']))

    def test_combine_scalar(self):
        result = self.panel.mul(2)
        expected = DataFrame(self.panel._data) * 2
        assert_frame_equal(result, expected)

    def test_combine_series(self):
        s = self.panel['ItemA'][:10]
        result = self.panel.add(s, axis=0)
        expected = DataFrame.add(self.panel, s, axis=0)
        assert_frame_equal(result, expected)

        s = self.panel.ix[5]
        result = self.panel + s
        expected = DataFrame.add(self.panel, s, axis=1)
        assert_frame_equal(result, expected)

    def test_operators(self):
        wp = self.panel.to_panel()
        result = (self.panel + 1).to_panel()
        assert_frame_equal(wp['ItemA'] + 1, result['ItemA'])

    def test_arith_flex_panel(self):
        ops = ['add', 'sub', 'mul', 'div', 'truediv', 'pow', 'floordiv', 'mod']
        if not compat.PY3:
            aliases = {}
        else:
            aliases = {'div': 'truediv'}
        self.panel = self.panel.to_panel()

        for n in [np.random.randint(-50, -1), np.random.randint(1, 50), 0]:
            for op in ops:
                alias = aliases.get(op, op)
                f = getattr(operator, alias)
                exp = f(self.panel, n)
                result = getattr(self.panel, op)(n)
                assert_panel_equal(result, exp, check_panel_type=True)

                # rops
                r_f = lambda x, y: f(y, x)
                exp = r_f(self.panel, n)
                result = getattr(self.panel, 'r' + op)(n)
                assert_panel_equal(result, exp)

    def test_sort(self):
        def is_sorted(arr):
            return (arr[1:] > arr[:-1]).any()

        sorted_minor = self.panel.sortlevel(level=1)
        self.assertTrue(is_sorted(sorted_minor.index.labels[1]))

        sorted_major = sorted_minor.sortlevel(level=0)
        self.assertTrue(is_sorted(sorted_major.index.labels[0]))

    def test_to_string(self):
        buf = StringIO()
        self.panel.to_string(buf)

    @ignore_sparse_panel_future_warning
    def test_truncate(self):
        dates = self.panel.index.levels[0]
        start, end = dates[1], dates[5]

        trunced = self.panel.truncate(start, end).to_panel()
        expected = self.panel.to_panel()['ItemA'].truncate(start, end)

        # TODO trucate drops index.names
        assert_frame_equal(trunced['ItemA'], expected, check_names=False)

        trunced = self.panel.truncate(before=start).to_panel()
        expected = self.panel.to_panel()['ItemA'].truncate(before=start)

        # TODO trucate drops index.names
        assert_frame_equal(trunced['ItemA'], expected, check_names=False)

        trunced = self.panel.truncate(after=end).to_panel()
        expected = self.panel.to_panel()['ItemA'].truncate(after=end)

        # TODO trucate drops index.names
        assert_frame_equal(trunced['ItemA'], expected, check_names=False)

        # truncate on dates that aren't in there
        wp = self.panel.to_panel()
        new_index = wp.major_axis[::5]

        wp2 = wp.reindex(major=new_index)

        lp2 = wp2.to_frame()
        lp_trunc = lp2.truncate(wp.major_axis[2], wp.major_axis[-2])

        wp_trunc = wp2.truncate(wp.major_axis[2], wp.major_axis[-2])

        assert_panel_equal(wp_trunc, lp_trunc.to_panel())

        # throw proper exception
        self.assertRaises(Exception, lp2.truncate, wp.major_axis[-2],
                          wp.major_axis[2])

    def test_axis_dummies(self):
        from pandas.core.reshape import make_axis_dummies

        minor_dummies = make_axis_dummies(self.panel, 'minor')
        self.assertEqual(len(minor_dummies.columns),
                         len(self.panel.index.levels[1]))

        major_dummies = make_axis_dummies(self.panel, 'major')
        self.assertEqual(len(major_dummies.columns),
                         len(self.panel.index.levels[0]))

        mapping = {'A': 'one', 'B': 'one', 'C': 'two', 'D': 'two'}

        transformed = make_axis_dummies(self.panel, 'minor',
                                        transform=mapping.get)
        self.assertEqual(len(transformed.columns), 2)
        self.assert_numpy_array_equal(transformed.columns, ['one', 'two'])

        # TODO: test correctness

    def test_get_dummies(self):
        from pandas.core.reshape import get_dummies, make_axis_dummies

        self.panel['Label'] = self.panel.index.labels[1]
        minor_dummies = make_axis_dummies(self.panel, 'minor')
        dummies = get_dummies(self.panel['Label'])
        self.assert_numpy_array_equal(dummies.values, minor_dummies.values)

    def test_mean(self):
        means = self.panel.mean(level='minor')

        # test versus Panel version
        wide_means = self.panel.to_panel().mean('major')
        assert_frame_equal(means, wide_means)

    def test_sum(self):
        sums = self.panel.sum(level='minor')

        # test versus Panel version
        wide_sums = self.panel.to_panel().sum('major')
        assert_frame_equal(sums, wide_sums)

    def test_count(self):
        index = self.panel.index

        major_count = self.panel.count(level=0)['ItemA']
        labels = index.labels[0]
        for i, idx in enumerate(index.levels[0]):
            self.assertEqual(major_count[i], (labels == i).sum())

        minor_count = self.panel.count(level=1)['ItemA']
        labels = index.labels[1]
        for i, idx in enumerate(index.levels[1]):
            self.assertEqual(minor_count[i], (labels == i).sum())

    def test_join(self):
        lp1 = self.panel.filter(['ItemA', 'ItemB'])
        lp2 = self.panel.filter(['ItemC'])

        joined = lp1.join(lp2)

        self.assertEqual(len(joined.columns), 3)

        self.assertRaises(Exception, lp1.join,
                          self.panel.filter(['ItemB', 'ItemC']))

    def test_pivot(self):
        from pandas.core.reshape import _slow_pivot

        one, two, three = (np.array([1, 2, 3, 4, 5]),
                           np.array(['a', 'b', 'c', 'd', 'e']),
                           np.array([1, 2, 3, 5, 4.]))
        df = pivot(one, two, three)
        self.assertEqual(df['a'][1], 1)
        self.assertEqual(df['b'][2], 2)
        self.assertEqual(df['c'][3], 3)
        self.assertEqual(df['d'][4], 5)
        self.assertEqual(df['e'][5], 4)
        assert_frame_equal(df, _slow_pivot(one, two, three))

        # weird overlap, TODO: test?
        a, b, c = (np.array([1, 2, 3, 4, 4]),
                   np.array(['a', 'a', 'a', 'a', 'a']),
                   np.array([1., 2., 3., 4., 5.]))
        self.assertRaises(Exception, pivot, a, b, c)

        # corner case, empty
        df = pivot(np.array([]), np.array([]), np.array([]))


def test_monotonic():
    pos = np.array([1, 2, 3, 5])

    def _monotonic(arr):
        return not (arr[1:] < arr[:-1]).any()

    assert _monotonic(pos)

    neg = np.array([1, 2, 3, 4, 3])

    assert not _monotonic(neg)

    neg2 = np.array([5, 1, 2, 3, 4, 5])

    assert not _monotonic(neg2)


def test_panel_index():
    index = panelm.panel_index([1, 2, 3, 4], [1, 2, 3])
    expected = MultiIndex.from_arrays([np.tile(
        [1, 2, 3, 4], 3), np.repeat(
            [1, 2, 3], 4)])
    assert (index.equals(expected))


def test_import_warnings():
    # GH8152
    panel = Panel(np.random.rand(3, 3, 3))
    with assert_produces_warning():
        panel.major_xs(1, copy=False)


if __name__ == '__main__':
    nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
                   exit=False)
