Repository: pandas-dev/pandas

Issue:
isin doesn't work for Timestamps
SO question: http://stackoverflow.com/q/19070194/564538



Discussion:
This just came up again: http://stackoverflow.com/questions/19551336/using-isin-with

Options: 

(1) Raise.  Irritating but no one will get the wrong answer. 

(2) Detect common cases like timestamps which don't place nicely with hashes and do list membership testing instead of set membership testing.  Slow for big collections but it'll give the right answer, and probably work well enough in the typical case of a small group.  

(2b) Instead of list membership testing, sort and then bisect if the to-be-tested collection is big enough.

(3) Make sure that standard time objects have compatible hashes.  


Resolving patch:
diff --git a/pandas/core/series.py b/pandas/core/series.py
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -2077,8 +2077,20 @@ def isin(self, values):
             raise TypeError("only list-like objects are allowed to be passed"
                             " to Series.isin(), you passed a "
                             "{0!r}".format(type(values).__name__))
+
+        # may need i8 conversion for proper membership testing
+        comps = _values_from_object(self)
+        if com.is_datetime64_dtype(self):
+            from pandas.tseries.tools import to_datetime
+            values = Series(to_datetime(values)).values.view('i8')
+            comps = comps.view('i8')
+        elif com.is_timedelta64_dtype(self):
+            from pandas.tseries.timedeltas import to_timedelta
+            values = Series(to_timedelta(values)).values.view('i8')
+            comps = comps.view('i8')
+
         value_set = set(values)
-        result = lib.ismember(_values_from_object(self), value_set)
+        result = lib.ismember(comps, value_set)
         return self._constructor(result, index=self.index).__finalize__(self)
 
     def between(self, left, right, inclusive=True):


Test patch:
diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py
--- a/pandas/tests/test_series.py
+++ b/pandas/tests/test_series.py
@@ -4840,7 +4840,7 @@ def test_isin(self):
         assert_series_equal(result, expected)
 
     def test_isin_with_string_scalar(self):
-        #GH4763
+        # GH4763
         s = Series(['A', 'B', 'C', 'a', 'B', 'B', 'A', 'C'])
         with tm.assertRaises(TypeError):
             s.isin('a')
@@ -4849,6 +4849,38 @@ def test_isin_with_string_scalar(self):
             s = Series(['aaa', 'b', 'c'])
             s.isin('aaa')
 
+    def test_isin_with_i8(self):
+        # GH 5021
+
+        expected = Series([True,True,False,False,False])
+        expected2 = Series([False,True,False,False,False])
+
+        # datetime64[ns]
+        s = Series(date_range('jan-01-2013','jan-05-2013'))
+
+        result = s.isin(s[0:2])
+        assert_series_equal(result, expected)
+
+        result = s.isin(s[0:2].values)
+        assert_series_equal(result, expected)
+
+        # fails on dtype conversion in the first place
+        if not _np_version_under1p7:
+            result = s.isin(s[0:2].values.astype('datetime64[D]'))
+            assert_series_equal(result, expected)
+
+        result = s.isin([s[1]])
+        assert_series_equal(result, expected2)
+
+        result = s.isin([np.datetime64(s[1])])
+        assert_series_equal(result, expected2)
+
+        # timedelta64[ns]
+        if not _np_version_under1p7:
+            s = Series(pd.to_timedelta(lrange(5),unit='d'))
+            result = s.isin(s[0:2])
+            assert_series_equal(result, expected)
+
 #------------------------------------------------------------------------------
 # TimeSeries-specific
     def test_cummethods_bool(self):