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

from datetime import datetime, timedelta

from numpy import nan
import numpy as np
import pandas as pd

from pandas import Index, Series, DataFrame, isnull, date_range
from pandas.core.index import MultiIndex
from pandas.core.indexing import IndexingError
from pandas.tseries.index import Timestamp
from pandas.tseries.tdi import Timedelta
import pandas.core.common as com

import pandas.core.datetools as datetools
import pandas.lib as lib

from pandas.compat import lrange, range
from pandas import compat
from pandas.util.testing import assert_series_equal, assert_almost_equal
import pandas.util.testing as tm

from .common import TestData

JOIN_TYPES = ['inner', 'outer', 'left', 'right']


class TestSeriesIndexing(TestData, tm.TestCase):

    _multiprocess_can_split_ = True

    def test_get(self):

        # GH 6383
        s = Series(np.array([43, 48, 60, 48, 50, 51, 50, 45, 57, 48, 56, 45,
                             51, 39, 55, 43, 54, 52, 51, 54]))

        result = s.get(25, 0)
        expected = 0
        self.assertEqual(result, expected)

        s = Series(np.array([43, 48, 60, 48, 50, 51, 50, 45, 57, 48, 56,
                             45, 51, 39, 55, 43, 54, 52, 51, 54]),
                   index=pd.Float64Index(
                       [25.0, 36.0, 49.0, 64.0, 81.0, 100.0,
                        121.0, 144.0, 169.0, 196.0, 1225.0,
                        1296.0, 1369.0, 1444.0, 1521.0, 1600.0,
                        1681.0, 1764.0, 1849.0, 1936.0],
                       dtype='object'))

        result = s.get(25, 0)
        expected = 43
        self.assertEqual(result, expected)

        # GH 7407
        # with a boolean accessor
        df = pd.DataFrame({'i': [0] * 3, 'b': [False] * 3})
        vc = df.i.value_counts()
        result = vc.get(99, default='Missing')
        self.assertEqual(result, 'Missing')

        vc = df.b.value_counts()
        result = vc.get(False, default='Missing')
        self.assertEqual(result, 3)

        result = vc.get(True, default='Missing')
        self.assertEqual(result, 'Missing')

    def test_delitem(self):

        # GH 5542
        # should delete the item inplace
        s = Series(lrange(5))
        del s[0]

        expected = Series(lrange(1, 5), index=lrange(1, 5))
        assert_series_equal(s, expected)

        del s[1]
        expected = Series(lrange(2, 5), index=lrange(2, 5))
        assert_series_equal(s, expected)

        # empty
        s = Series()

        def f():
            del s[0]

        self.assertRaises(KeyError, f)

        # only 1 left, del, add, del
        s = Series(1)
        del s[0]
        assert_series_equal(s, Series(dtype='int64', index=Index(
            [], dtype='int64')))
        s[0] = 1
        assert_series_equal(s, Series(1))
        del s[0]
        assert_series_equal(s, Series(dtype='int64', index=Index(
            [], dtype='int64')))

        # Index(dtype=object)
        s = Series(1, index=['a'])
        del s['a']
        assert_series_equal(s, Series(dtype='int64', index=Index(
            [], dtype='object')))
        s['a'] = 1
        assert_series_equal(s, Series(1, index=['a']))
        del s['a']
        assert_series_equal(s, Series(dtype='int64', index=Index(
            [], dtype='object')))

    def test_getitem_setitem_ellipsis(self):
        s = Series(np.random.randn(10))

        np.fix(s)

        result = s[...]
        assert_series_equal(result, s)

        s[...] = 5
        self.assertTrue((result == 5).all())

    def test_getitem_negative_out_of_bounds(self):
        s = Series(tm.rands_array(5, 10), index=tm.rands_array(10, 10))

        self.assertRaises(IndexError, s.__getitem__, -11)
        self.assertRaises(IndexError, s.__setitem__, -11, 'foo')

    def test_pop(self):
        # GH 6600
        df = DataFrame({'A': 0, 'B': np.arange(5, dtype='int64'), 'C': 0, })
        k = df.iloc[4]

        result = k.pop('B')
        self.assertEqual(result, 4)

        expected = Series([0, 0], index=['A', 'C'], name=4)
        assert_series_equal(k, expected)

    def test_getitem_get(self):
        idx1 = self.series.index[5]
        idx2 = self.objSeries.index[5]

        self.assertEqual(self.series[idx1], self.series.get(idx1))
        self.assertEqual(self.objSeries[idx2], self.objSeries.get(idx2))

        self.assertEqual(self.series[idx1], self.series[5])
        self.assertEqual(self.objSeries[idx2], self.objSeries[5])

        self.assertEqual(
            self.series.get(-1), self.series.get(self.series.index[-1]))
        self.assertEqual(self.series[5], self.series.get(self.series.index[5]))

        # missing
        d = self.ts.index[0] - datetools.bday
        self.assertRaises(KeyError, self.ts.__getitem__, d)

        # None
        # GH 5652
        for s in [Series(), Series(index=list('abc'))]:
            result = s.get(None)
            self.assertIsNone(result)

    def test_iget(self):

        s = Series(np.random.randn(10), index=lrange(0, 20, 2))

        # 10711, deprecated
        with tm.assert_produces_warning(FutureWarning):
            s.iget(1)

        # 10711, deprecated
        with tm.assert_produces_warning(FutureWarning):
            s.irow(1)

        # 10711, deprecated
        with tm.assert_produces_warning(FutureWarning):
            s.iget_value(1)

        for i in range(len(s)):
            result = s.iloc[i]
            exp = s[s.index[i]]
            assert_almost_equal(result, exp)

        # pass a slice
        result = s.iloc[slice(1, 3)]
        expected = s.ix[2:4]
        assert_series_equal(result, expected)

        # test slice is a view
        result[:] = 0
        self.assertTrue((s[1:3] == 0).all())

        # list of integers
        result = s.iloc[[0, 2, 3, 4, 5]]
        expected = s.reindex(s.index[[0, 2, 3, 4, 5]])
        assert_series_equal(result, expected)

    def test_iget_nonunique(self):
        s = Series([0, 1, 2], index=[0, 1, 0])
        self.assertEqual(s.iloc[2], 2)

    def test_getitem_regression(self):
        s = Series(lrange(5), index=lrange(5))
        result = s[lrange(5)]
        assert_series_equal(result, s)

    def test_getitem_setitem_slice_bug(self):
        s = Series(lrange(10), lrange(10))
        result = s[-12:]
        assert_series_equal(result, s)

        result = s[-7:]
        assert_series_equal(result, s[3:])

        result = s[:-12]
        assert_series_equal(result, s[:0])

        s = Series(lrange(10), lrange(10))
        s[-12:] = 0
        self.assertTrue((s == 0).all())

        s[:-12] = 5
        self.assertTrue((s == 0).all())

    def test_getitem_int64(self):
        idx = np.int64(5)
        self.assertEqual(self.ts[idx], self.ts[5])

    def test_getitem_fancy(self):
        slice1 = self.series[[1, 2, 3]]
        slice2 = self.objSeries[[1, 2, 3]]
        self.assertEqual(self.series.index[2], slice1.index[1])
        self.assertEqual(self.objSeries.index[2], slice2.index[1])
        self.assertEqual(self.series[2], slice1[1])
        self.assertEqual(self.objSeries[2], slice2[1])

    def test_getitem_boolean(self):
        s = self.series
        mask = s > s.median()

        # passing list is OK
        result = s[list(mask)]
        expected = s[mask]
        assert_series_equal(result, expected)
        self.assert_numpy_array_equal(result.index, s.index[mask])

    def test_getitem_boolean_empty(self):
        s = Series([], dtype=np.int64)
        s.index.name = 'index_name'
        s = s[s.isnull()]
        self.assertEqual(s.index.name, 'index_name')
        self.assertEqual(s.dtype, np.int64)

        # GH5877
        # indexing with empty series
        s = Series(['A', 'B'])
        expected = Series(np.nan, index=['C'], dtype=object)
        result = s[Series(['C'], dtype=object)]
        assert_series_equal(result, expected)

        s = Series(['A', 'B'])
        expected = Series(dtype=object, index=Index([], dtype='int64'))
        result = s[Series([], dtype=object)]
        assert_series_equal(result, expected)

        # invalid because of the boolean indexer
        # that's empty or not-aligned
        def f():
            s[Series([], dtype=bool)]

        self.assertRaises(IndexingError, f)

        def f():
            s[Series([True], dtype=bool)]

        self.assertRaises(IndexingError, f)

    def test_getitem_generator(self):
        gen = (x > 0 for x in self.series)
        result = self.series[gen]
        result2 = self.series[iter(self.series > 0)]
        expected = self.series[self.series > 0]
        assert_series_equal(result, expected)
        assert_series_equal(result2, expected)

    def test_getitem_boolean_object(self):
        # using column from DataFrame

        s = self.series
        mask = s > s.median()
        omask = mask.astype(object)

        # getitem
        result = s[omask]
        expected = s[mask]
        assert_series_equal(result, expected)

        # setitem
        s2 = s.copy()
        cop = s.copy()
        cop[omask] = 5
        s2[mask] = 5
        assert_series_equal(cop, s2)

        # nans raise exception
        omask[5:10] = np.nan
        self.assertRaises(Exception, s.__getitem__, omask)
        self.assertRaises(Exception, s.__setitem__, omask, 5)

    def test_getitem_setitem_boolean_corner(self):
        ts = self.ts
        mask_shifted = ts.shift(1, freq=datetools.bday) > ts.median()

        # these used to raise...??

        self.assertRaises(Exception, ts.__getitem__, mask_shifted)
        self.assertRaises(Exception, ts.__setitem__, mask_shifted, 1)
        # ts[mask_shifted]
        # ts[mask_shifted] = 1

        self.assertRaises(Exception, ts.ix.__getitem__, mask_shifted)
        self.assertRaises(Exception, ts.ix.__setitem__, mask_shifted, 1)
        # ts.ix[mask_shifted]
        # ts.ix[mask_shifted] = 2

    def test_getitem_setitem_slice_integers(self):
        s = Series(np.random.randn(8), index=[2, 4, 6, 8, 10, 12, 14, 16])

        result = s[:4]
        expected = s.reindex([2, 4, 6, 8])
        assert_series_equal(result, expected)

        s[:4] = 0
        self.assertTrue((s[:4] == 0).all())
        self.assertTrue(not (s[4:] == 0).any())

    def test_getitem_out_of_bounds(self):
        # don't segfault, GH #495
        self.assertRaises(IndexError, self.ts.__getitem__, len(self.ts))

        # GH #917
        s = Series([])
        self.assertRaises(IndexError, s.__getitem__, -1)

    def test_getitem_setitem_integers(self):
        # caused bug without test
        s = Series([1, 2, 3], ['a', 'b', 'c'])

        self.assertEqual(s.ix[0], s['a'])
        s.ix[0] = 5
        self.assertAlmostEqual(s['a'], 5)

    def test_getitem_box_float64(self):
        value = self.ts[5]
        tm.assertIsInstance(value, np.float64)

    def test_getitem_ambiguous_keyerror(self):
        s = Series(lrange(10), index=lrange(0, 20, 2))
        self.assertRaises(KeyError, s.__getitem__, 1)
        self.assertRaises(KeyError, s.ix.__getitem__, 1)

    def test_getitem_unordered_dup(self):
        obj = Series(lrange(5), index=['c', 'a', 'a', 'b', 'b'])
        self.assertTrue(lib.isscalar(obj['c']))
        self.assertEqual(obj['c'], 0)

    def test_getitem_dups_with_missing(self):

        # breaks reindex, so need to use .ix internally
        # GH 4246
        s = Series([1, 2, 3, 4], ['foo', 'bar', 'foo', 'bah'])
        expected = s.ix[['foo', 'bar', 'bah', 'bam']]
        result = s[['foo', 'bar', 'bah', 'bam']]
        assert_series_equal(result, expected)

    def test_getitem_dups(self):
        s = Series(range(5), index=['A', 'A', 'B', 'C', 'C'], dtype=np.int64)
        expected = Series([3, 4], index=['C', 'C'], dtype=np.int64)
        result = s['C']
        assert_series_equal(result, expected)

    def test_getitem_dataframe(self):
        rng = list(range(10))
        s = pd.Series(10, index=rng)
        df = pd.DataFrame(rng, index=rng)
        self.assertRaises(TypeError, s.__getitem__, df > 5)

    def test_getitem_callable(self):
        # GH 12533
        s = pd.Series(4, index=list('ABCD'))
        result = s[lambda x: 'A']
        self.assertEqual(result, s.loc['A'])

        result = s[lambda x: ['A', 'B']]
        tm.assert_series_equal(result, s.loc[['A', 'B']])

        result = s[lambda x: [True, False, True, True]]
        tm.assert_series_equal(result, s.iloc[[0, 2, 3]])

    def test_setitem_ambiguous_keyerror(self):
        s = Series(lrange(10), index=lrange(0, 20, 2))

        # equivalent of an append
        s2 = s.copy()
        s2[1] = 5
        expected = s.append(Series([5], index=[1]))
        assert_series_equal(s2, expected)

        s2 = s.copy()
        s2.ix[1] = 5
        expected = s.append(Series([5], index=[1]))
        assert_series_equal(s2, expected)

    def test_setitem_float_labels(self):
        # note labels are floats
        s = Series(['a', 'b', 'c'], index=[0, 0.5, 1])
        tmp = s.copy()

        s.ix[1] = 'zoo'
        tmp.iloc[2] = 'zoo'

        assert_series_equal(s, tmp)

    def test_setitem_callable(self):
        # GH 12533
        s = pd.Series([1, 2, 3, 4], index=list('ABCD'))
        s[lambda x: 'A'] = -1
        tm.assert_series_equal(s, pd.Series([-1, 2, 3, 4], index=list('ABCD')))

    def test_slice(self):
        numSlice = self.series[10:20]
        numSliceEnd = self.series[-10:]
        objSlice = self.objSeries[10:20]

        self.assertNotIn(self.series.index[9], numSlice.index)
        self.assertNotIn(self.objSeries.index[9], objSlice.index)

        self.assertEqual(len(numSlice), len(numSlice.index))
        self.assertEqual(self.series[numSlice.index[0]],
                         numSlice[numSlice.index[0]])

        self.assertEqual(numSlice.index[1], self.series.index[11])

        self.assertTrue(tm.equalContents(numSliceEnd, np.array(self.series)[
            -10:]))

        # test return view
        sl = self.series[10:20]
        sl[:] = 0
        self.assertTrue((self.series[10:20] == 0).all())

    def test_slice_can_reorder_not_uniquely_indexed(self):
        s = Series(1, index=['a', 'a', 'b', 'b', 'c'])
        s[::-1]  # it works!

    def test_slice_float_get_set(self):

        self.assertRaises(TypeError, lambda: self.ts[4.0:10.0])

        def f():
            self.ts[4.0:10.0] = 0

        self.assertRaises(TypeError, f)

        self.assertRaises(TypeError, self.ts.__getitem__, slice(4.5, 10.0))
        self.assertRaises(TypeError, self.ts.__setitem__, slice(4.5, 10.0), 0)

    def test_slice_floats2(self):
        s = Series(np.random.rand(10), index=np.arange(10, 20, dtype=float))

        self.assertEqual(len(s.ix[12.0:]), 8)
        self.assertEqual(len(s.ix[12.5:]), 7)

        i = np.arange(10, 20, dtype=float)
        i[2] = 12.2
        s.index = i
        self.assertEqual(len(s.ix[12.0:]), 8)
        self.assertEqual(len(s.ix[12.5:]), 7)

    def test_slice_float64(self):

        values = np.arange(10., 50., 2)
        index = Index(values)

        start, end = values[[5, 15]]

        s = Series(np.random.randn(20), index=index)

        result = s[start:end]
        expected = s.iloc[5:16]
        assert_series_equal(result, expected)

        result = s.loc[start:end]
        assert_series_equal(result, expected)

        df = DataFrame(np.random.randn(20, 3), index=index)

        result = df[start:end]
        expected = df.iloc[5:16]
        tm.assert_frame_equal(result, expected)

        result = df.loc[start:end]
        tm.assert_frame_equal(result, expected)

    def test_setitem(self):
        self.ts[self.ts.index[5]] = np.NaN
        self.ts[[1, 2, 17]] = np.NaN
        self.ts[6] = np.NaN
        self.assertTrue(np.isnan(self.ts[6]))
        self.assertTrue(np.isnan(self.ts[2]))
        self.ts[np.isnan(self.ts)] = 5
        self.assertFalse(np.isnan(self.ts[2]))

        # caught this bug when writing tests
        series = Series(tm.makeIntIndex(20).astype(float),
                        index=tm.makeIntIndex(20))

        series[::2] = 0
        self.assertTrue((series[::2] == 0).all())

        # set item that's not contained
        s = self.series.copy()
        s['foobar'] = 1

        app = Series([1], index=['foobar'], name='series')
        expected = self.series.append(app)
        assert_series_equal(s, expected)

        # Test for issue #10193
        key = pd.Timestamp('2012-01-01')
        series = pd.Series()
        series[key] = 47
        expected = pd.Series(47, [key])
        assert_series_equal(series, expected)

        series = pd.Series([], pd.DatetimeIndex([], freq='D'))
        series[key] = 47
        expected = pd.Series(47, pd.DatetimeIndex([key], freq='D'))
        assert_series_equal(series, expected)

    def test_setitem_dtypes(self):

        # change dtypes
        # GH 4463
        expected = Series([np.nan, 2, 3])

        s = Series([1, 2, 3])
        s.iloc[0] = np.nan
        assert_series_equal(s, expected)

        s = Series([1, 2, 3])
        s.loc[0] = np.nan
        assert_series_equal(s, expected)

        s = Series([1, 2, 3])
        s[0] = np.nan
        assert_series_equal(s, expected)

        s = Series([False])
        s.loc[0] = np.nan
        assert_series_equal(s, Series([np.nan]))

        s = Series([False, True])
        s.loc[0] = np.nan
        assert_series_equal(s, Series([np.nan, 1.0]))

    def test_set_value(self):
        idx = self.ts.index[10]
        res = self.ts.set_value(idx, 0)
        self.assertIs(res, self.ts)
        self.assertEqual(self.ts[idx], 0)

        # equiv
        s = self.series.copy()
        res = s.set_value('foobar', 0)
        self.assertIs(res, s)
        self.assertEqual(res.index[-1], 'foobar')
        self.assertEqual(res['foobar'], 0)

        s = self.series.copy()
        s.loc['foobar'] = 0
        self.assertEqual(s.index[-1], 'foobar')
        self.assertEqual(s['foobar'], 0)

    def test_setslice(self):
        sl = self.ts[5:20]
        self.assertEqual(len(sl), len(sl.index))
        self.assertTrue(sl.index.is_unique)

    def test_basic_getitem_setitem_corner(self):
        # invalid tuples, e.g. self.ts[:, None] vs. self.ts[:, 2]
        with tm.assertRaisesRegexp(ValueError, 'tuple-index'):
            self.ts[:, 2]
        with tm.assertRaisesRegexp(ValueError, 'tuple-index'):
            self.ts[:, 2] = 2

        # weird lists. [slice(0, 5)] will work but not two slices
        result = self.ts[[slice(None, 5)]]
        expected = self.ts[:5]
        assert_series_equal(result, expected)

        # OK
        self.assertRaises(Exception, self.ts.__getitem__,
                          [5, slice(None, None)])
        self.assertRaises(Exception, self.ts.__setitem__,
                          [5, slice(None, None)], 2)

    def test_basic_getitem_with_labels(self):
        indices = self.ts.index[[5, 10, 15]]

        result = self.ts[indices]
        expected = self.ts.reindex(indices)
        assert_series_equal(result, expected)

        result = self.ts[indices[0]:indices[2]]
        expected = self.ts.ix[indices[0]:indices[2]]
        assert_series_equal(result, expected)

        # integer indexes, be careful
        s = Series(np.random.randn(10), index=lrange(0, 20, 2))
        inds = [0, 2, 5, 7, 8]
        arr_inds = np.array([0, 2, 5, 7, 8])
        result = s[inds]
        expected = s.reindex(inds)
        assert_series_equal(result, expected)

        result = s[arr_inds]
        expected = s.reindex(arr_inds)
        assert_series_equal(result, expected)

        # GH12089
        # with tz for values
        s = Series(pd.date_range("2011-01-01", periods=3, tz="US/Eastern"),
                   index=['a', 'b', 'c'])
        expected = Timestamp('2011-01-01', tz='US/Eastern')
        result = s.loc['a']
        self.assertEqual(result, expected)
        result = s.iloc[0]
        self.assertEqual(result, expected)
        result = s['a']
        self.assertEqual(result, expected)

    def test_basic_setitem_with_labels(self):
        indices = self.ts.index[[5, 10, 15]]

        cp = self.ts.copy()
        exp = self.ts.copy()
        cp[indices] = 0
        exp.ix[indices] = 0
        assert_series_equal(cp, exp)

        cp = self.ts.copy()
        exp = self.ts.copy()
        cp[indices[0]:indices[2]] = 0
        exp.ix[indices[0]:indices[2]] = 0
        assert_series_equal(cp, exp)

        # integer indexes, be careful
        s = Series(np.random.randn(10), index=lrange(0, 20, 2))
        inds = [0, 4, 6]
        arr_inds = np.array([0, 4, 6])

        cp = s.copy()
        exp = s.copy()
        s[inds] = 0
        s.ix[inds] = 0
        assert_series_equal(cp, exp)

        cp = s.copy()
        exp = s.copy()
        s[arr_inds] = 0
        s.ix[arr_inds] = 0
        assert_series_equal(cp, exp)

        inds_notfound = [0, 4, 5, 6]
        arr_inds_notfound = np.array([0, 4, 5, 6])
        self.assertRaises(Exception, s.__setitem__, inds_notfound, 0)
        self.assertRaises(Exception, s.__setitem__, arr_inds_notfound, 0)

        # GH12089
        # with tz for values
        s = Series(pd.date_range("2011-01-01", periods=3, tz="US/Eastern"),
                   index=['a', 'b', 'c'])
        s2 = s.copy()
        expected = Timestamp('2011-01-03', tz='US/Eastern')
        s2.loc['a'] = expected
        result = s2.loc['a']
        self.assertEqual(result, expected)

        s2 = s.copy()
        s2.iloc[0] = expected
        result = s2.iloc[0]
        self.assertEqual(result, expected)

        s2 = s.copy()
        s2['a'] = expected
        result = s2['a']
        self.assertEqual(result, expected)

    def test_ix_getitem(self):
        inds = self.series.index[[3, 4, 7]]
        assert_series_equal(self.series.ix[inds], self.series.reindex(inds))
        assert_series_equal(self.series.ix[5::2], self.series[5::2])

        # slice with indices
        d1, d2 = self.ts.index[[5, 15]]
        result = self.ts.ix[d1:d2]
        expected = self.ts.truncate(d1, d2)
        assert_series_equal(result, expected)

        # boolean
        mask = self.series > self.series.median()
        assert_series_equal(self.series.ix[mask], self.series[mask])

        # ask for index value
        self.assertEqual(self.ts.ix[d1], self.ts[d1])
        self.assertEqual(self.ts.ix[d2], self.ts[d2])

    def test_ix_getitem_not_monotonic(self):
        d1, d2 = self.ts.index[[5, 15]]

        ts2 = self.ts[::2][[1, 2, 0]]

        self.assertRaises(KeyError, ts2.ix.__getitem__, slice(d1, d2))
        self.assertRaises(KeyError, ts2.ix.__setitem__, slice(d1, d2), 0)

    def test_ix_getitem_setitem_integer_slice_keyerrors(self):
        s = Series(np.random.randn(10), index=lrange(0, 20, 2))

        # this is OK
        cp = s.copy()
        cp.ix[4:10] = 0
        self.assertTrue((cp.ix[4:10] == 0).all())

        # so is this
        cp = s.copy()
        cp.ix[3:11] = 0
        self.assertTrue((cp.ix[3:11] == 0).values.all())

        result = s.ix[4:10]
        result2 = s.ix[3:11]
        expected = s.reindex([4, 6, 8, 10])

        assert_series_equal(result, expected)
        assert_series_equal(result2, expected)

        # non-monotonic, raise KeyError
        s2 = s.iloc[lrange(5) + lrange(5, 10)[::-1]]
        self.assertRaises(KeyError, s2.ix.__getitem__, slice(3, 11))
        self.assertRaises(KeyError, s2.ix.__setitem__, slice(3, 11), 0)

    def test_ix_getitem_iterator(self):
        idx = iter(self.series.index[:10])
        result = self.series.ix[idx]
        assert_series_equal(result, self.series[:10])

    def test_where(self):
        s = Series(np.random.randn(5))
        cond = s > 0

        rs = s.where(cond).dropna()
        rs2 = s[cond]
        assert_series_equal(rs, rs2)

        rs = s.where(cond, -s)
        assert_series_equal(rs, s.abs())

        rs = s.where(cond)
        assert (s.shape == rs.shape)
        assert (rs is not s)

        # test alignment
        cond = Series([True, False, False, True, False], index=s.index)
        s2 = -(s.abs())

        expected = s2[cond].reindex(s2.index[:3]).reindex(s2.index)
        rs = s2.where(cond[:3])
        assert_series_equal(rs, expected)

        expected = s2.abs()
        expected.ix[0] = s2[0]
        rs = s2.where(cond[:3], -s2)
        assert_series_equal(rs, expected)

        self.assertRaises(ValueError, s.where, 1)
        self.assertRaises(ValueError, s.where, cond[:3].values, -s)

        # GH 2745
        s = Series([1, 2])
        s[[True, False]] = [0, 1]
        expected = Series([0, 2])
        assert_series_equal(s, expected)

        # failures
        self.assertRaises(ValueError, s.__setitem__, tuple([[[True, False]]]),
                          [0, 2, 3])
        self.assertRaises(ValueError, s.__setitem__, tuple([[[True, False]]]),
                          [])

        # unsafe dtype changes
        for dtype in [np.int8, np.int16, np.int32, np.int64, np.float16,
                      np.float32, np.float64]:
            s = Series(np.arange(10), dtype=dtype)
            mask = s < 5
            s[mask] = lrange(2, 7)
            expected = Series(lrange(2, 7) + lrange(5, 10), dtype=dtype)
            assert_series_equal(s, expected)
            self.assertEqual(s.dtype, expected.dtype)

        # these are allowed operations, but are upcasted
        for dtype in [np.int64, np.float64]:
            s = Series(np.arange(10), dtype=dtype)
            mask = s < 5
            values = [2.5, 3.5, 4.5, 5.5, 6.5]
            s[mask] = values
            expected = Series(values + lrange(5, 10), dtype='float64')
            assert_series_equal(s, expected)
            self.assertEqual(s.dtype, expected.dtype)

        # GH 9731
        s = Series(np.arange(10), dtype='int64')
        mask = s > 5
        values = [2.5, 3.5, 4.5, 5.5]
        s[mask] = values
        expected = Series(lrange(6) + values, dtype='float64')
        assert_series_equal(s, expected)

        # can't do these as we are forced to change the itemsize of the input
        # to something we cannot
        for dtype in [np.int8, np.int16, np.int32, np.float16, np.float32]:
            s = Series(np.arange(10), dtype=dtype)
            mask = s < 5
            values = [2.5, 3.5, 4.5, 5.5, 6.5]
            self.assertRaises(Exception, s.__setitem__, tuple(mask), values)

        # GH3235
        s = Series(np.arange(10), dtype='int64')
        mask = s < 5
        s[mask] = lrange(2, 7)
        expected = Series(lrange(2, 7) + lrange(5, 10), dtype='int64')
        assert_series_equal(s, expected)
        self.assertEqual(s.dtype, expected.dtype)

        s = Series(np.arange(10), dtype='int64')
        mask = s > 5
        s[mask] = [0] * 4
        expected = Series([0, 1, 2, 3, 4, 5] + [0] * 4, dtype='int64')
        assert_series_equal(s, expected)

        s = Series(np.arange(10))
        mask = s > 5

        def f():
            s[mask] = [5, 4, 3, 2, 1]

        self.assertRaises(ValueError, f)

        def f():
            s[mask] = [0] * 5

        self.assertRaises(ValueError, f)

        # dtype changes
        s = Series([1, 2, 3, 4])
        result = s.where(s > 2, np.nan)
        expected = Series([np.nan, np.nan, 3, 4])
        assert_series_equal(result, expected)

        # GH 4667
        # setting with None changes dtype
        s = Series(range(10)).astype(float)
        s[8] = None
        result = s[8]
        self.assertTrue(isnull(result))

        s = Series(range(10)).astype(float)
        s[s > 8] = None
        result = s[isnull(s)]
        expected = Series(np.nan, index=[9])
        assert_series_equal(result, expected)

    def test_where_setitem_invalid(self):

        # GH 2702
        # make sure correct exceptions are raised on invalid list assignment

        # slice
        s = Series(list('abc'))

        def f():
            s[0:3] = list(range(27))

        self.assertRaises(ValueError, f)

        s[0:3] = list(range(3))
        expected = Series([0, 1, 2])
        assert_series_equal(s.astype(np.int64), expected, )

        # slice with step
        s = Series(list('abcdef'))

        def f():
            s[0:4:2] = list(range(27))

        self.assertRaises(ValueError, f)

        s = Series(list('abcdef'))
        s[0:4:2] = list(range(2))
        expected = Series([0, 'b', 1, 'd', 'e', 'f'])
        assert_series_equal(s, expected)

        # neg slices
        s = Series(list('abcdef'))

        def f():
            s[:-1] = list(range(27))

        self.assertRaises(ValueError, f)

        s[-3:-1] = list(range(2))
        expected = Series(['a', 'b', 'c', 0, 1, 'f'])
        assert_series_equal(s, expected)

        # list
        s = Series(list('abc'))

        def f():
            s[[0, 1, 2]] = list(range(27))

        self.assertRaises(ValueError, f)

        s = Series(list('abc'))

        def f():
            s[[0, 1, 2]] = list(range(2))

        self.assertRaises(ValueError, f)

        # scalar
        s = Series(list('abc'))
        s[0] = list(range(10))
        expected = Series([list(range(10)), 'b', 'c'])
        assert_series_equal(s, expected)

    def test_where_broadcast(self):
        # Test a variety of differently sized series
        for size in range(2, 6):
            # Test a variety of boolean indices
            for selection in [
                    # First element should be set
                    np.resize([True, False, False, False, False], size),
                    # Set alternating elements]
                    np.resize([True, False], size),
                    # No element should be set
                    np.resize([False], size)]:

                # Test a variety of different numbers as content
                for item in [2.0, np.nan, np.finfo(np.float).max,
                             np.finfo(np.float).min]:
                    # Test numpy arrays, lists and tuples as the input to be
                    # broadcast
                    for arr in [np.array([item]), [item], (item, )]:
                        data = np.arange(size, dtype=float)
                        s = Series(data)
                        s[selection] = arr
                        # Construct the expected series by taking the source
                        # data or item based on the selection
                        expected = Series([item if use_item else data[
                            i] for i, use_item in enumerate(selection)])
                        assert_series_equal(s, expected)

                        s = Series(data)
                        result = s.where(~selection, arr)
                        assert_series_equal(result, expected)

    def test_where_inplace(self):
        s = Series(np.random.randn(5))
        cond = s > 0

        rs = s.copy()

        rs.where(cond, inplace=True)
        assert_series_equal(rs.dropna(), s[cond])
        assert_series_equal(rs, s.where(cond))

        rs = s.copy()
        rs.where(cond, -s, inplace=True)
        assert_series_equal(rs, s.where(cond, -s))

    def test_where_dups(self):
        # GH 4550
        # where crashes with dups in index
        s1 = Series(list(range(3)))
        s2 = Series(list(range(3)))
        comb = pd.concat([s1, s2])
        result = comb.where(comb < 2)
        expected = Series([0, 1, np.nan, 0, 1, np.nan],
                          index=[0, 1, 2, 0, 1, 2])
        assert_series_equal(result, expected)

        # GH 4548
        # inplace updating not working with dups
        comb[comb < 1] = 5
        expected = Series([5, 1, 2, 5, 1, 2], index=[0, 1, 2, 0, 1, 2])
        assert_series_equal(comb, expected)

        comb[comb < 2] += 10
        expected = Series([5, 11, 2, 5, 11, 2], index=[0, 1, 2, 0, 1, 2])
        assert_series_equal(comb, expected)

    def test_where_datetime(self):
        s = Series(date_range('20130102', periods=2))
        expected = Series([10, 10], dtype='datetime64[ns]')
        mask = np.array([False, False])

        rs = s.where(mask, [10, 10])
        assert_series_equal(rs, expected)

        rs = s.where(mask, 10)
        assert_series_equal(rs, expected)

        rs = s.where(mask, 10.0)
        assert_series_equal(rs, expected)

        rs = s.where(mask, [10.0, 10.0])
        assert_series_equal(rs, expected)

        rs = s.where(mask, [10.0, np.nan])
        expected = Series([10, None], dtype='datetime64[ns]')
        assert_series_equal(rs, expected)

    def test_where_timedelta(self):
        s = Series([1, 2], dtype='timedelta64[ns]')
        expected = Series([10, 10], dtype='timedelta64[ns]')
        mask = np.array([False, False])

        rs = s.where(mask, [10, 10])
        assert_series_equal(rs, expected)

        rs = s.where(mask, 10)
        assert_series_equal(rs, expected)

        rs = s.where(mask, 10.0)
        assert_series_equal(rs, expected)

        rs = s.where(mask, [10.0, 10.0])
        assert_series_equal(rs, expected)

        rs = s.where(mask, [10.0, np.nan])
        expected = Series([10, None], dtype='timedelta64[ns]')
        assert_series_equal(rs, expected)

    def test_mask(self):
        # compare with tested results in test_where
        s = Series(np.random.randn(5))
        cond = s > 0

        rs = s.where(~cond, np.nan)
        assert_series_equal(rs, s.mask(cond))

        rs = s.where(~cond)
        rs2 = s.mask(cond)
        assert_series_equal(rs, rs2)

        rs = s.where(~cond, -s)
        rs2 = s.mask(cond, -s)
        assert_series_equal(rs, rs2)

        cond = Series([True, False, False, True, False], index=s.index)
        s2 = -(s.abs())
        rs = s2.where(~cond[:3])
        rs2 = s2.mask(cond[:3])
        assert_series_equal(rs, rs2)

        rs = s2.where(~cond[:3], -s2)
        rs2 = s2.mask(cond[:3], -s2)
        assert_series_equal(rs, rs2)

        self.assertRaises(ValueError, s.mask, 1)
        self.assertRaises(ValueError, s.mask, cond[:3].values, -s)

        # dtype changes
        s = Series([1, 2, 3, 4])
        result = s.mask(s > 2, np.nan)
        expected = Series([1, 2, np.nan, np.nan])
        assert_series_equal(result, expected)

    def test_mask_broadcast(self):
        # GH 8801
        # copied from test_where_broadcast
        for size in range(2, 6):
            for selection in [
                    # First element should be set
                    np.resize([True, False, False, False, False], size),
                    # Set alternating elements]
                    np.resize([True, False], size),
                    # No element should be set
                    np.resize([False], size)]:
                for item in [2.0, np.nan, np.finfo(np.float).max,
                             np.finfo(np.float).min]:
                    for arr in [np.array([item]), [item], (item, )]:
                        data = np.arange(size, dtype=float)
                        s = Series(data)
                        result = s.mask(selection, arr)
                        expected = Series([item if use_item else data[
                            i] for i, use_item in enumerate(selection)])
                        assert_series_equal(result, expected)

    def test_mask_inplace(self):
        s = Series(np.random.randn(5))
        cond = s > 0

        rs = s.copy()
        rs.mask(cond, inplace=True)
        assert_series_equal(rs.dropna(), s[~cond])
        assert_series_equal(rs, s.mask(cond))

        rs = s.copy()
        rs.mask(cond, -s, inplace=True)
        assert_series_equal(rs, s.mask(cond, -s))

    def test_ix_setitem(self):
        inds = self.series.index[[3, 4, 7]]

        result = self.series.copy()
        result.ix[inds] = 5

        expected = self.series.copy()
        expected[[3, 4, 7]] = 5
        assert_series_equal(result, expected)

        result.ix[5:10] = 10
        expected[5:10] = 10
        assert_series_equal(result, expected)

        # set slice with indices
        d1, d2 = self.series.index[[5, 15]]
        result.ix[d1:d2] = 6
        expected[5:16] = 6  # because it's inclusive
        assert_series_equal(result, expected)

        # set index value
        self.series.ix[d1] = 4
        self.series.ix[d2] = 6
        self.assertEqual(self.series[d1], 4)
        self.assertEqual(self.series[d2], 6)

    def test_where_numeric_with_string(self):
        # GH 9280
        s = pd.Series([1, 2, 3])
        w = s.where(s > 1, 'X')

        self.assertFalse(com.is_integer(w[0]))
        self.assertTrue(com.is_integer(w[1]))
        self.assertTrue(com.is_integer(w[2]))
        self.assertTrue(isinstance(w[0], str))
        self.assertTrue(w.dtype == 'object')

        w = s.where(s > 1, ['X', 'Y', 'Z'])
        self.assertFalse(com.is_integer(w[0]))
        self.assertTrue(com.is_integer(w[1]))
        self.assertTrue(com.is_integer(w[2]))
        self.assertTrue(isinstance(w[0], str))
        self.assertTrue(w.dtype == 'object')

        w = s.where(s > 1, np.array(['X', 'Y', 'Z']))
        self.assertFalse(com.is_integer(w[0]))
        self.assertTrue(com.is_integer(w[1]))
        self.assertTrue(com.is_integer(w[2]))
        self.assertTrue(isinstance(w[0], str))
        self.assertTrue(w.dtype == 'object')

    def test_setitem_boolean(self):
        mask = self.series > self.series.median()

        # similiar indexed series
        result = self.series.copy()
        result[mask] = self.series * 2
        expected = self.series * 2
        assert_series_equal(result[mask], expected[mask])

        # needs alignment
        result = self.series.copy()
        result[mask] = (self.series * 2)[0:5]
        expected = (self.series * 2)[0:5].reindex_like(self.series)
        expected[-mask] = self.series[mask]
        assert_series_equal(result[mask], expected[mask])

    def test_ix_setitem_boolean(self):
        mask = self.series > self.series.median()

        result = self.series.copy()
        result.ix[mask] = 0
        expected = self.series
        expected[mask] = 0
        assert_series_equal(result, expected)

    def test_ix_setitem_corner(self):
        inds = list(self.series.index[[5, 8, 12]])
        self.series.ix[inds] = 5
        self.assertRaises(Exception, self.series.ix.__setitem__,
                          inds + ['foo'], 5)

    def test_get_set_boolean_different_order(self):
        ordered = self.series.sort_values()

        # setting
        copy = self.series.copy()
        copy[ordered > 0] = 0

        expected = self.series.copy()
        expected[expected > 0] = 0

        assert_series_equal(copy, expected)

        # getting
        sel = self.series[ordered > 0]
        exp = self.series[self.series > 0]
        assert_series_equal(sel, exp)

    def test_setitem_na(self):
        # these induce dtype changes
        expected = Series([np.nan, 3, np.nan, 5, np.nan, 7, np.nan, 9, np.nan])
        s = Series([2, 3, 4, 5, 6, 7, 8, 9, 10])
        s[::2] = np.nan
        assert_series_equal(s, expected)

        # get's coerced to float, right?
        expected = Series([np.nan, 1, np.nan, 0])
        s = Series([True, True, False, False])
        s[::2] = np.nan
        assert_series_equal(s, expected)

        expected = Series([np.nan, np.nan, np.nan, np.nan, np.nan, 5, 6, 7, 8,
                           9])
        s = Series(np.arange(10))
        s[:5] = np.nan
        assert_series_equal(s, expected)

    def test_basic_indexing(self):
        s = Series(np.random.randn(5), index=['a', 'b', 'a', 'a', 'b'])

        self.assertRaises(IndexError, s.__getitem__, 5)
        self.assertRaises(IndexError, s.__setitem__, 5, 0)

        self.assertRaises(KeyError, s.__getitem__, 'c')

        s = s.sort_index()

        self.assertRaises(IndexError, s.__getitem__, 5)
        self.assertRaises(IndexError, s.__setitem__, 5, 0)

    def test_int_indexing(self):
        s = Series(np.random.randn(6), index=[0, 0, 1, 1, 2, 2])

        self.assertRaises(KeyError, s.__getitem__, 5)

        self.assertRaises(KeyError, s.__getitem__, 'c')

        # not monotonic
        s = Series(np.random.randn(6), index=[2, 2, 0, 0, 1, 1])

        self.assertRaises(KeyError, s.__getitem__, 5)

        self.assertRaises(KeyError, s.__getitem__, 'c')

    def test_datetime_indexing(self):
        from pandas import date_range

        index = date_range('1/1/2000', '1/7/2000')
        index = index.repeat(3)

        s = Series(len(index), index=index)
        stamp = Timestamp('1/8/2000')

        self.assertRaises(KeyError, s.__getitem__, stamp)
        s[stamp] = 0
        self.assertEqual(s[stamp], 0)

        # not monotonic
        s = Series(len(index), index=index)
        s = s[::-1]

        self.assertRaises(KeyError, s.__getitem__, stamp)
        s[stamp] = 0
        self.assertEqual(s[stamp], 0)

    def test_timedelta_assignment(self):
        # GH 8209
        s = Series([])
        s.loc['B'] = timedelta(1)
        tm.assert_series_equal(s, Series(Timedelta('1 days'), index=['B']))

        s = s.reindex(s.index.insert(0, 'A'))
        tm.assert_series_equal(s, Series(
            [np.nan, Timedelta('1 days')], index=['A', 'B']))

        result = s.fillna(timedelta(1))
        expected = Series(Timedelta('1 days'), index=['A', 'B'])
        tm.assert_series_equal(result, expected)

        s.loc['A'] = timedelta(1)
        tm.assert_series_equal(s, expected)

    def test_underlying_data_conversion(self):

        # GH 4080
        df = DataFrame(dict((c, [1, 2, 3]) for c in ['a', 'b', 'c']))
        df.set_index(['a', 'b', 'c'], inplace=True)
        s = Series([1], index=[(2, 2, 2)])
        df['val'] = 0
        df
        df['val'].update(s)

        expected = DataFrame(
            dict(a=[1, 2, 3], b=[1, 2, 3], c=[1, 2, 3], val=[0, 1, 0]))
        expected.set_index(['a', 'b', 'c'], inplace=True)
        tm.assert_frame_equal(df, expected)

        # GH 3970
        # these are chained assignments as well
        pd.set_option('chained_assignment', None)
        df = DataFrame({"aa": range(5), "bb": [2.2] * 5})
        df["cc"] = 0.0

        ck = [True] * len(df)

        df["bb"].iloc[0] = .13

        # TODO: unused
        df_tmp = df.iloc[ck]  # noqa

        df["bb"].iloc[0] = .15
        self.assertEqual(df['bb'].iloc[0], 0.15)
        pd.set_option('chained_assignment', 'raise')

        # GH 3217
        df = DataFrame(dict(a=[1, 3], b=[np.nan, 2]))
        df['c'] = np.nan
        df['c'].update(pd.Series(['foo'], index=[0]))

        expected = DataFrame(dict(a=[1, 3], b=[np.nan, 2], c=['foo', np.nan]))
        tm.assert_frame_equal(df, expected)

    def test_preserveRefs(self):
        seq = self.ts[[5, 10, 15]]
        seq[1] = np.NaN
        self.assertFalse(np.isnan(self.ts[10]))

    def test_drop(self):

        # unique
        s = Series([1, 2], index=['one', 'two'])
        expected = Series([1], index=['one'])
        result = s.drop(['two'])
        assert_series_equal(result, expected)
        result = s.drop('two', axis='rows')
        assert_series_equal(result, expected)

        # non-unique
        # GH 5248
        s = Series([1, 1, 2], index=['one', 'two', 'one'])
        expected = Series([1, 2], index=['one', 'one'])
        result = s.drop(['two'], axis=0)
        assert_series_equal(result, expected)
        result = s.drop('two')
        assert_series_equal(result, expected)

        expected = Series([1], index=['two'])
        result = s.drop(['one'])
        assert_series_equal(result, expected)
        result = s.drop('one')
        assert_series_equal(result, expected)

        # single string/tuple-like
        s = Series(range(3), index=list('abc'))
        self.assertRaises(ValueError, s.drop, 'bc')
        self.assertRaises(ValueError, s.drop, ('a', ))

        # errors='ignore'
        s = Series(range(3), index=list('abc'))
        result = s.drop('bc', errors='ignore')
        assert_series_equal(result, s)
        result = s.drop(['a', 'd'], errors='ignore')
        expected = s.ix[1:]
        assert_series_equal(result, expected)

        # bad axis
        self.assertRaises(ValueError, s.drop, 'one', axis='columns')

        # GH 8522
        s = Series([2, 3], index=[True, False])
        self.assertTrue(s.index.is_object())
        result = s.drop(True)
        expected = Series([3], index=[False])
        assert_series_equal(result, expected)

    def test_align(self):
        def _check_align(a, b, how='left', fill=None):
            aa, ab = a.align(b, join=how, fill_value=fill)

            join_index = a.index.join(b.index, how=how)
            if fill is not None:
                diff_a = aa.index.difference(join_index)
                diff_b = ab.index.difference(join_index)
                if len(diff_a) > 0:
                    self.assertTrue((aa.reindex(diff_a) == fill).all())
                if len(diff_b) > 0:
                    self.assertTrue((ab.reindex(diff_b) == fill).all())

            ea = a.reindex(join_index)
            eb = b.reindex(join_index)

            if fill is not None:
                ea = ea.fillna(fill)
                eb = eb.fillna(fill)

            assert_series_equal(aa, ea)
            assert_series_equal(ab, eb)
            self.assertEqual(aa.name, 'ts')
            self.assertEqual(ea.name, 'ts')
            self.assertEqual(ab.name, 'ts')
            self.assertEqual(eb.name, 'ts')

        for kind in JOIN_TYPES:
            _check_align(self.ts[2:], self.ts[:-5], how=kind)
            _check_align(self.ts[2:], self.ts[:-5], how=kind, fill=-1)

            # empty left
            _check_align(self.ts[:0], self.ts[:-5], how=kind)
            _check_align(self.ts[:0], self.ts[:-5], how=kind, fill=-1)

            # empty right
            _check_align(self.ts[:-5], self.ts[:0], how=kind)
            _check_align(self.ts[:-5], self.ts[:0], how=kind, fill=-1)

            # both empty
            _check_align(self.ts[:0], self.ts[:0], how=kind)
            _check_align(self.ts[:0], self.ts[:0], how=kind, fill=-1)

    def test_align_fill_method(self):
        def _check_align(a, b, how='left', method='pad', limit=None):
            aa, ab = a.align(b, join=how, method=method, limit=limit)

            join_index = a.index.join(b.index, how=how)
            ea = a.reindex(join_index)
            eb = b.reindex(join_index)

            ea = ea.fillna(method=method, limit=limit)
            eb = eb.fillna(method=method, limit=limit)

            assert_series_equal(aa, ea)
            assert_series_equal(ab, eb)

        for kind in JOIN_TYPES:
            for meth in ['pad', 'bfill']:
                _check_align(self.ts[2:], self.ts[:-5], how=kind, method=meth)
                _check_align(self.ts[2:], self.ts[:-5], how=kind, method=meth,
                             limit=1)

                # empty left
                _check_align(self.ts[:0], self.ts[:-5], how=kind, method=meth)
                _check_align(self.ts[:0], self.ts[:-5], how=kind, method=meth,
                             limit=1)

                # empty right
                _check_align(self.ts[:-5], self.ts[:0], how=kind, method=meth)
                _check_align(self.ts[:-5], self.ts[:0], how=kind, method=meth,
                             limit=1)

                # both empty
                _check_align(self.ts[:0], self.ts[:0], how=kind, method=meth)
                _check_align(self.ts[:0], self.ts[:0], how=kind, method=meth,
                             limit=1)

    def test_align_nocopy(self):
        b = self.ts[:5].copy()

        # do copy
        a = self.ts.copy()
        ra, _ = a.align(b, join='left')
        ra[:5] = 5
        self.assertFalse((a[:5] == 5).any())

        # do not copy
        a = self.ts.copy()
        ra, _ = a.align(b, join='left', copy=False)
        ra[:5] = 5
        self.assertTrue((a[:5] == 5).all())

        # do copy
        a = self.ts.copy()
        b = self.ts[:5].copy()
        _, rb = a.align(b, join='right')
        rb[:3] = 5
        self.assertFalse((b[:3] == 5).any())

        # do not copy
        a = self.ts.copy()
        b = self.ts[:5].copy()
        _, rb = a.align(b, join='right', copy=False)
        rb[:2] = 5
        self.assertTrue((b[:2] == 5).all())

    def test_align_sameindex(self):
        a, b = self.ts.align(self.ts, copy=False)
        self.assertIs(a.index, self.ts.index)
        self.assertIs(b.index, self.ts.index)

        # a, b = self.ts.align(self.ts, copy=True)
        # self.assertIsNot(a.index, self.ts.index)
        # self.assertIsNot(b.index, self.ts.index)

    def test_align_multiindex(self):
        # GH 10665

        midx = pd.MultiIndex.from_product([range(2), range(3), range(2)],
                                          names=('a', 'b', 'c'))
        idx = pd.Index(range(2), name='b')
        s1 = pd.Series(np.arange(12, dtype='int64'), index=midx)
        s2 = pd.Series(np.arange(2, dtype='int64'), index=idx)

        # these must be the same results (but flipped)
        res1l, res1r = s1.align(s2, join='left')
        res2l, res2r = s2.align(s1, join='right')

        expl = s1
        tm.assert_series_equal(expl, res1l)
        tm.assert_series_equal(expl, res2r)
        expr = pd.Series([0, 0, 1, 1, np.nan, np.nan] * 2, index=midx)
        tm.assert_series_equal(expr, res1r)
        tm.assert_series_equal(expr, res2l)

        res1l, res1r = s1.align(s2, join='right')
        res2l, res2r = s2.align(s1, join='left')

        exp_idx = pd.MultiIndex.from_product([range(2), range(2), range(2)],
                                             names=('a', 'b', 'c'))
        expl = pd.Series([0, 1, 2, 3, 6, 7, 8, 9], index=exp_idx)
        tm.assert_series_equal(expl, res1l)
        tm.assert_series_equal(expl, res2r)
        expr = pd.Series([0, 0, 1, 1] * 2, index=exp_idx)
        tm.assert_series_equal(expr, res1r)
        tm.assert_series_equal(expr, res2l)

    def test_reindex(self):

        identity = self.series.reindex(self.series.index)

        # __array_interface__ is not defined for older numpies
        # and on some pythons
        try:
            self.assertTrue(np.may_share_memory(self.series.index,
                                                identity.index))
        except (AttributeError):
            pass

        self.assertTrue(identity.index.is_(self.series.index))
        self.assertTrue(identity.index.identical(self.series.index))

        subIndex = self.series.index[10:20]
        subSeries = self.series.reindex(subIndex)

        for idx, val in compat.iteritems(subSeries):
            self.assertEqual(val, self.series[idx])

        subIndex2 = self.ts.index[10:20]
        subTS = self.ts.reindex(subIndex2)

        for idx, val in compat.iteritems(subTS):
            self.assertEqual(val, self.ts[idx])
        stuffSeries = self.ts.reindex(subIndex)

        self.assertTrue(np.isnan(stuffSeries).all())

        # This is extremely important for the Cython code to not screw up
        nonContigIndex = self.ts.index[::2]
        subNonContig = self.ts.reindex(nonContigIndex)
        for idx, val in compat.iteritems(subNonContig):
            self.assertEqual(val, self.ts[idx])

        # return a copy the same index here
        result = self.ts.reindex()
        self.assertFalse((result is self.ts))

    def test_reindex_nan(self):
        ts = Series([2, 3, 5, 7], index=[1, 4, nan, 8])

        i, j = [nan, 1, nan, 8, 4, nan], [2, 0, 2, 3, 1, 2]
        assert_series_equal(ts.reindex(i), ts.iloc[j])

        ts.index = ts.index.astype('object')

        # reindex coerces index.dtype to float, loc/iloc doesn't
        assert_series_equal(ts.reindex(i), ts.iloc[j], check_index_type=False)

    def test_reindex_corner(self):
        # (don't forget to fix this) I think it's fixed
        self.empty.reindex(self.ts.index, method='pad')  # it works

        # corner case: pad empty series
        reindexed = self.empty.reindex(self.ts.index, method='pad')

        # pass non-Index
        reindexed = self.ts.reindex(list(self.ts.index))
        assert_series_equal(self.ts, reindexed)

        # bad fill method
        ts = self.ts[::2]
        self.assertRaises(Exception, ts.reindex, self.ts.index, method='foo')

    def test_reindex_pad(self):

        s = Series(np.arange(10), dtype='int64')
        s2 = s[::2]

        reindexed = s2.reindex(s.index, method='pad')
        reindexed2 = s2.reindex(s.index, method='ffill')
        assert_series_equal(reindexed, reindexed2)

        expected = Series([0, 0, 2, 2, 4, 4, 6, 6, 8, 8], index=np.arange(10))
        assert_series_equal(reindexed, expected)

        # GH4604
        s = Series([1, 2, 3, 4, 5], index=['a', 'b', 'c', 'd', 'e'])
        new_index = ['a', 'g', 'c', 'f']
        expected = Series([1, 1, 3, 3], index=new_index)

        # this changes dtype because the ffill happens after
        result = s.reindex(new_index).ffill()
        assert_series_equal(result, expected.astype('float64'))

        result = s.reindex(new_index).ffill(downcast='infer')
        assert_series_equal(result, expected)

        expected = Series([1, 5, 3, 5], index=new_index)
        result = s.reindex(new_index, method='ffill')
        assert_series_equal(result, expected)

        # inferrence of new dtype
        s = Series([True, False, False, True], index=list('abcd'))
        new_index = 'agc'
        result = s.reindex(list(new_index)).ffill()
        expected = Series([True, True, False], index=list(new_index))
        assert_series_equal(result, expected)

        # GH4618 shifted series downcasting
        s = Series(False, index=lrange(0, 5))
        result = s.shift(1).fillna(method='bfill')
        expected = Series(False, index=lrange(0, 5))
        assert_series_equal(result, expected)

    def test_reindex_nearest(self):
        s = Series(np.arange(10, dtype='int64'))
        target = [0.1, 0.9, 1.5, 2.0]
        actual = s.reindex(target, method='nearest')
        expected = Series(np.around(target).astype('int64'), target)
        assert_series_equal(expected, actual)

        actual = s.reindex_like(actual, method='nearest')
        assert_series_equal(expected, actual)

        actual = s.reindex_like(actual, method='nearest', tolerance=1)
        assert_series_equal(expected, actual)

        actual = s.reindex(target, method='nearest', tolerance=0.2)
        expected = Series([0, 1, np.nan, 2], target)
        assert_series_equal(expected, actual)

    def test_reindex_backfill(self):
        pass

    def test_reindex_int(self):
        ts = self.ts[::2]
        int_ts = Series(np.zeros(len(ts), dtype=int), index=ts.index)

        # this should work fine
        reindexed_int = int_ts.reindex(self.ts.index)

        # if NaNs introduced
        self.assertEqual(reindexed_int.dtype, np.float_)

        # NO NaNs introduced
        reindexed_int = int_ts.reindex(int_ts.index[::2])
        self.assertEqual(reindexed_int.dtype, np.int_)

    def test_reindex_bool(self):

        # A series other than float, int, string, or object
        ts = self.ts[::2]
        bool_ts = Series(np.zeros(len(ts), dtype=bool), index=ts.index)

        # this should work fine
        reindexed_bool = bool_ts.reindex(self.ts.index)

        # if NaNs introduced
        self.assertEqual(reindexed_bool.dtype, np.object_)

        # NO NaNs introduced
        reindexed_bool = bool_ts.reindex(bool_ts.index[::2])
        self.assertEqual(reindexed_bool.dtype, np.bool_)

    def test_reindex_bool_pad(self):
        # fail
        ts = self.ts[5:]
        bool_ts = Series(np.zeros(len(ts), dtype=bool), index=ts.index)
        filled_bool = bool_ts.reindex(self.ts.index, method='pad')
        self.assertTrue(isnull(filled_bool[:5]).all())

    def test_reindex_like(self):
        other = self.ts[::2]
        assert_series_equal(self.ts.reindex(other.index),
                            self.ts.reindex_like(other))

        # GH 7179
        day1 = datetime(2013, 3, 5)
        day2 = datetime(2013, 5, 5)
        day3 = datetime(2014, 3, 5)

        series1 = Series([5, None, None], [day1, day2, day3])
        series2 = Series([None, None], [day1, day3])

        result = series1.reindex_like(series2, method='pad')
        expected = Series([5, np.nan], index=[day1, day3])
        assert_series_equal(result, expected)

    def test_reindex_fill_value(self):
        # -----------------------------------------------------------
        # floats
        floats = Series([1., 2., 3.])
        result = floats.reindex([1, 2, 3])
        expected = Series([2., 3., np.nan], index=[1, 2, 3])
        assert_series_equal(result, expected)

        result = floats.reindex([1, 2, 3], fill_value=0)
        expected = Series([2., 3., 0], index=[1, 2, 3])
        assert_series_equal(result, expected)

        # -----------------------------------------------------------
        # ints
        ints = Series([1, 2, 3])

        result = ints.reindex([1, 2, 3])
        expected = Series([2., 3., np.nan], index=[1, 2, 3])
        assert_series_equal(result, expected)

        # don't upcast
        result = ints.reindex([1, 2, 3], fill_value=0)
        expected = Series([2, 3, 0], index=[1, 2, 3])
        self.assertTrue(issubclass(result.dtype.type, np.integer))
        assert_series_equal(result, expected)

        # -----------------------------------------------------------
        # objects
        objects = Series([1, 2, 3], dtype=object)

        result = objects.reindex([1, 2, 3])
        expected = Series([2, 3, np.nan], index=[1, 2, 3], dtype=object)
        assert_series_equal(result, expected)

        result = objects.reindex([1, 2, 3], fill_value='foo')
        expected = Series([2, 3, 'foo'], index=[1, 2, 3], dtype=object)
        assert_series_equal(result, expected)

        # ------------------------------------------------------------
        # bools
        bools = Series([True, False, True])

        result = bools.reindex([1, 2, 3])
        expected = Series([False, True, np.nan], index=[1, 2, 3], dtype=object)
        assert_series_equal(result, expected)

        result = bools.reindex([1, 2, 3], fill_value=False)
        expected = Series([False, True, False], index=[1, 2, 3])
        assert_series_equal(result, expected)

    def test_select(self):
        n = len(self.ts)
        result = self.ts.select(lambda x: x >= self.ts.index[n // 2])
        expected = self.ts.reindex(self.ts.index[n // 2:])
        assert_series_equal(result, expected)

        result = self.ts.select(lambda x: x.weekday() == 2)
        expected = self.ts[self.ts.index.weekday == 2]
        assert_series_equal(result, expected)

    def test_cast_on_putmask(self):

        # GH 2746

        # need to upcast
        s = Series([1, 2], index=[1, 2], dtype='int64')
        s[[True, False]] = Series([0], index=[1], dtype='int64')
        expected = Series([0, 2], index=[1, 2], dtype='int64')

        assert_series_equal(s, expected)

    def test_type_promote_putmask(self):

        # GH8387: test that changing types does not break alignment
        ts = Series(np.random.randn(100), index=np.arange(100, 0, -1)).round(5)
        left, mask = ts.copy(), ts > 0
        right = ts[mask].copy().map(str)
        left[mask] = right
        assert_series_equal(left, ts.map(lambda t: str(t) if t > 0 else t))

        s = Series([0, 1, 2, 0])
        mask = s > 0
        s2 = s[mask].map(str)
        s[mask] = s2
        assert_series_equal(s, Series([0, '1', '2', 0]))

        s = Series([0, 'foo', 'bar', 0])
        mask = Series([False, True, True, False])
        s2 = s[mask]
        s[mask] = s2
        assert_series_equal(s, Series([0, 'foo', 'bar', 0]))

    def test_head_tail(self):
        assert_series_equal(self.series.head(), self.series[:5])
        assert_series_equal(self.series.head(0), self.series[0:0])
        assert_series_equal(self.series.tail(), self.series[-5:])
        assert_series_equal(self.series.tail(0), self.series[0:0])

    def test_multilevel_preserve_name(self):
        index = MultiIndex(levels=[['foo', 'bar', 'baz', 'qux'], ['one', 'two',
                                                                  'three']],
                           labels=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3],
                                   [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]],
                           names=['first', 'second'])
        s = Series(np.random.randn(len(index)), index=index, name='sth')

        result = s['foo']
        result2 = s.ix['foo']
        self.assertEqual(result.name, s.name)
        self.assertEqual(result2.name, s.name)
