30
30
import pandas .util .testing as tm
31
31
32
32
33
+ def assert_invalid_comparison (left , right , box ):
34
+ """
35
+ Assert that comparison operations with mismatched types behave correctly.
36
+
37
+ Parameters
38
+ ----------
39
+ left : np.ndarray, ExtensionArray, Index, or Series
40
+ right : object
41
+ box : {pd.DataFrame, pd.Series, pd.Index, tm.to_array}
42
+ """
43
+ # Not for tznaive-tzaware comparison
44
+
45
+ # Note: not quite the same as how we do this for tm.box_expected
46
+ xbox = box if box is not pd .Index else np .array
47
+
48
+ result = left == right
49
+ expected = xbox (np .zeros (result .shape , dtype = np .bool_ ))
50
+
51
+ tm .assert_equal (result , expected )
52
+
53
+ result = right == left
54
+ tm .assert_equal (result , expected )
55
+
56
+ result = left != right
57
+ tm .assert_equal (result , ~ expected )
58
+
59
+ result = right != left
60
+ tm .assert_equal (result , ~ expected )
61
+
62
+ msg = "Invalid comparison between"
63
+ with pytest .raises (TypeError , match = msg ):
64
+ left < right
65
+ with pytest .raises (TypeError , match = msg ):
66
+ left <= right
67
+ with pytest .raises (TypeError , match = msg ):
68
+ left > right
69
+ with pytest .raises (TypeError , match = msg ):
70
+ left >= right
71
+ with pytest .raises (TypeError , match = msg ):
72
+ right < left
73
+ with pytest .raises (TypeError , match = msg ):
74
+ right <= left
75
+ with pytest .raises (TypeError , match = msg ):
76
+ right > left
77
+ with pytest .raises (TypeError , match = msg ):
78
+ right >= left
79
+
80
+
33
81
def assert_all (obj ):
34
82
"""
35
83
Test helper to call call obj.all() the appropriate number of times on
@@ -47,7 +95,7 @@ def assert_all(obj):
47
95
48
96
class TestDatetime64ArrayLikeComparisons :
49
97
# Comparison tests for datetime64 vectors fully parametrized over
50
- # DataFrame/Series/DatetimeIndex/DateteimeArray . Ideally all comparison
98
+ # DataFrame/Series/DatetimeIndex/DatetimeArray . Ideally all comparison
51
99
# tests will eventually end up here.
52
100
53
101
def test_compare_zerodim (self , tz_naive_fixture , box_with_array ):
@@ -59,36 +107,61 @@ def test_compare_zerodim(self, tz_naive_fixture, box_with_array):
59
107
60
108
other = np .array (dti .to_numpy ()[0 ])
61
109
62
- # FIXME: ValueError with transpose on tzaware
63
- dtarr = tm .box_expected (dti , box , transpose = False )
110
+ dtarr = tm .box_expected (dti , box )
64
111
result = dtarr <= other
65
112
expected = np .array ([True , False , False ])
66
- expected = tm .box_expected (expected , xbox , transpose = False )
113
+ expected = tm .box_expected (expected , xbox )
67
114
tm .assert_equal (result , expected )
68
115
116
+ def test_dt64arr_cmp_date_invalid (self , tz_naive_fixture , box_with_array ):
117
+ # GH#19800, GH#19301 datetime.date comparison raises to
118
+ # match DatetimeIndex/Timestamp. This also matches the behavior
119
+ # of stdlib datetime.datetime
120
+ tz = tz_naive_fixture
69
121
70
- class TestDatetime64DataFrameComparison :
71
- @pytest .mark .parametrize (
72
- "timestamps" ,
73
- [
74
- [pd .Timestamp ("2012-01-01 13:00:00+00:00" )] * 2 ,
75
- [pd .Timestamp ("2012-01-01 13:00:00" )] * 2 ,
76
- ],
77
- )
78
- def test_tz_aware_scalar_comparison (self , timestamps ):
79
- # GH#15966
80
- df = pd .DataFrame ({"test" : timestamps })
81
- expected = pd .DataFrame ({"test" : [False , False ]})
82
- tm .assert_frame_equal (df == - 1 , expected )
122
+ dti = pd .date_range ("20010101" , periods = 10 , tz = tz )
123
+ date = dti [0 ].to_pydatetime ().date ()
124
+
125
+ dtarr = tm .box_expected (dti , box_with_array )
126
+ assert_invalid_comparison (dtarr , date , box_with_array )
83
127
84
- def test_dt64_nat_comparison (self ):
128
+ @pytest .mark .parametrize ("other" , ["foo" , - 1 , 99 , 4.0 , object (), timedelta (days = 2 )])
129
+ def test_dt64arr_cmp_scalar_invalid (self , other , tz_naive_fixture , box_with_array ):
130
+ # GH#22074, GH#15966
131
+ tz = tz_naive_fixture
132
+
133
+ rng = date_range ("1/1/2000" , periods = 10 , tz = tz )
134
+ dtarr = tm .box_expected (rng , box_with_array )
135
+ assert_invalid_comparison (dtarr , other , box_with_array )
136
+
137
+ @pytest .mark .parametrize ("other" , [None , np .nan ])
138
+ def test_dt64arr_cmp_na_scalar_invalid (
139
+ self , other , tz_naive_fixture , box_with_array
140
+ ):
141
+ # GH#19301
142
+ tz = tz_naive_fixture
143
+ dti = pd .date_range ("2016-01-01" , periods = 2 , tz = tz )
144
+ dtarr = tm .box_expected (dti , box_with_array )
145
+ assert_invalid_comparison (dtarr , other , box_with_array )
146
+
147
+ def test_dt64arr_nat_comparison (self , tz_naive_fixture , box_with_array ):
85
148
# GH#22242, GH#22163 DataFrame considered NaT == ts incorrectly
86
- ts = pd .Timestamp .now ()
87
- df = pd .DataFrame ([ts , pd .NaT ])
88
- expected = pd .DataFrame ([True , False ])
149
+ tz = tz_naive_fixture
150
+ box = box_with_array
151
+ xbox = box if box is not pd .Index else np .ndarray
152
+
153
+ ts = pd .Timestamp .now (tz )
154
+ ser = pd .Series ([ts , pd .NaT ])
155
+
156
+ # FIXME: Can't transpose because that loses the tz dtype on
157
+ # the NaT column
158
+ obj = tm .box_expected (ser , box , transpose = False )
89
159
90
- result = df == ts
91
- tm .assert_frame_equal (result , expected )
160
+ expected = pd .Series ([True , False ], dtype = np .bool_ )
161
+ expected = tm .box_expected (expected , xbox , transpose = False )
162
+
163
+ result = obj == ts
164
+ tm .assert_equal (result , expected )
92
165
93
166
94
167
class TestDatetime64SeriesComparison :
@@ -142,35 +215,17 @@ def test_nat_comparisons(self, dtype, box, reverse, pair):
142
215
expected = Series ([False , False , True ])
143
216
tm .assert_series_equal (left <= right , expected )
144
217
145
- def test_comparison_invalid (self , box_with_array ):
218
+ def test_comparison_invalid (self , tz_naive_fixture , box_with_array ):
146
219
# GH#4968
147
220
# invalid date/int comparisons
148
- xbox = box_with_array if box_with_array is not pd .Index else np .ndarray
149
-
221
+ tz = tz_naive_fixture
150
222
ser = Series (range (5 ))
151
- ser2 = Series (pd .date_range ("20010101" , periods = 5 ))
223
+ ser2 = Series (pd .date_range ("20010101" , periods = 5 , tz = tz ))
152
224
153
225
ser = tm .box_expected (ser , box_with_array )
154
226
ser2 = tm .box_expected (ser2 , box_with_array )
155
227
156
- for (x , y ) in [(ser , ser2 ), (ser2 , ser )]:
157
-
158
- result = x == y
159
- expected = tm .box_expected ([False ] * 5 , xbox )
160
- tm .assert_equal (result , expected )
161
-
162
- result = x != y
163
- expected = tm .box_expected ([True ] * 5 , xbox )
164
- tm .assert_equal (result , expected )
165
- msg = "Invalid comparison between"
166
- with pytest .raises (TypeError , match = msg ):
167
- x >= y
168
- with pytest .raises (TypeError , match = msg ):
169
- x > y
170
- with pytest .raises (TypeError , match = msg ):
171
- x < y
172
- with pytest .raises (TypeError , match = msg ):
173
- x <= y
228
+ assert_invalid_comparison (ser , ser2 , box_with_array )
174
229
175
230
@pytest .mark .parametrize (
176
231
"data" ,
@@ -227,26 +282,6 @@ def test_series_comparison_scalars(self):
227
282
expected = Series ([x > val for x in series ])
228
283
tm .assert_series_equal (result , expected )
229
284
230
- def test_dt64ser_cmp_date_invalid (self , box_with_array ):
231
- # GH#19800 datetime.date comparison raises to
232
- # match DatetimeIndex/Timestamp. This also matches the behavior
233
- # of stdlib datetime.datetime
234
-
235
- ser = pd .date_range ("20010101" , periods = 10 )
236
- date = ser [0 ].to_pydatetime ().date ()
237
-
238
- ser = tm .box_expected (ser , box_with_array )
239
- assert_all (~ (ser == date ))
240
- assert_all (ser != date )
241
- with pytest .raises (TypeError ):
242
- ser > date
243
- with pytest .raises (TypeError ):
244
- ser < date
245
- with pytest .raises (TypeError ):
246
- ser >= date
247
- with pytest .raises (TypeError ):
248
- ser <= date
249
-
250
285
@pytest .mark .parametrize (
251
286
"left,right" , [("lt" , "gt" ), ("le" , "ge" ), ("eq" , "eq" ), ("ne" , "ne" )]
252
287
)
@@ -388,57 +423,6 @@ def test_dti_cmp_datetimelike(self, other, tz_naive_fixture):
388
423
expected = np .array ([True , False ])
389
424
tm .assert_numpy_array_equal (result , expected )
390
425
391
- def dt64arr_cmp_non_datetime (self , tz_naive_fixture , box_with_array ):
392
- # GH#19301 by convention datetime.date is not considered comparable
393
- # to Timestamp or DatetimeIndex. This may change in the future.
394
- tz = tz_naive_fixture
395
- dti = pd .date_range ("2016-01-01" , periods = 2 , tz = tz )
396
- dtarr = tm .box_expected (dti , box_with_array )
397
-
398
- other = datetime (2016 , 1 , 1 ).date ()
399
- assert not (dtarr == other ).any ()
400
- assert (dtarr != other ).all ()
401
- with pytest .raises (TypeError ):
402
- dtarr < other
403
- with pytest .raises (TypeError ):
404
- dtarr <= other
405
- with pytest .raises (TypeError ):
406
- dtarr > other
407
- with pytest .raises (TypeError ):
408
- dtarr >= other
409
-
410
- @pytest .mark .parametrize ("other" , [None , np .nan , pd .NaT ])
411
- def test_dti_eq_null_scalar (self , other , tz_naive_fixture ):
412
- # GH#19301
413
- tz = tz_naive_fixture
414
- dti = pd .date_range ("2016-01-01" , periods = 2 , tz = tz )
415
- assert not (dti == other ).any ()
416
-
417
- @pytest .mark .parametrize ("other" , [None , np .nan , pd .NaT ])
418
- def test_dti_ne_null_scalar (self , other , tz_naive_fixture ):
419
- # GH#19301
420
- tz = tz_naive_fixture
421
- dti = pd .date_range ("2016-01-01" , periods = 2 , tz = tz )
422
- assert (dti != other ).all ()
423
-
424
- @pytest .mark .parametrize ("other" , [None , np .nan ])
425
- def test_dti_cmp_null_scalar_inequality (
426
- self , tz_naive_fixture , other , box_with_array
427
- ):
428
- # GH#19301
429
- tz = tz_naive_fixture
430
- dti = pd .date_range ("2016-01-01" , periods = 2 , tz = tz )
431
- dtarr = tm .box_expected (dti , box_with_array )
432
- msg = "Invalid comparison between"
433
- with pytest .raises (TypeError , match = msg ):
434
- dtarr < other
435
- with pytest .raises (TypeError , match = msg ):
436
- dtarr <= other
437
- with pytest .raises (TypeError , match = msg ):
438
- dtarr > other
439
- with pytest .raises (TypeError , match = msg ):
440
- dtarr >= other
441
-
442
426
@pytest .mark .parametrize ("dtype" , [None , object ])
443
427
def test_dti_cmp_nat (self , dtype , box_with_array ):
444
428
if box_with_array is tm .to_array and dtype is object :
@@ -728,34 +712,6 @@ def test_dti_cmp_str(self, tz_naive_fixture):
728
712
expected = np .array ([True ] * 10 )
729
713
tm .assert_numpy_array_equal (result , expected )
730
714
731
- @pytest .mark .parametrize ("other" , ["foo" , 99 , 4.0 , object (), timedelta (days = 2 )])
732
- def test_dt64arr_cmp_scalar_invalid (self , other , tz_naive_fixture , box_with_array ):
733
- # GH#22074
734
- tz = tz_naive_fixture
735
- xbox = box_with_array if box_with_array is not pd .Index else np .ndarray
736
-
737
- rng = date_range ("1/1/2000" , periods = 10 , tz = tz )
738
- rng = tm .box_expected (rng , box_with_array )
739
-
740
- result = rng == other
741
- expected = np .array ([False ] * 10 )
742
- expected = tm .box_expected (expected , xbox )
743
- tm .assert_equal (result , expected )
744
-
745
- result = rng != other
746
- expected = np .array ([True ] * 10 )
747
- expected = tm .box_expected (expected , xbox )
748
- tm .assert_equal (result , expected )
749
- msg = "Invalid comparison between"
750
- with pytest .raises (TypeError , match = msg ):
751
- rng < other
752
- with pytest .raises (TypeError , match = msg ):
753
- rng <= other
754
- with pytest .raises (TypeError , match = msg ):
755
- rng > other
756
- with pytest .raises (TypeError , match = msg ):
757
- rng >= other
758
-
759
715
def test_dti_cmp_list (self ):
760
716
rng = date_range ("1/1/2000" , periods = 10 )
761
717
@@ -2576,24 +2532,3 @@ def test_shift_months(years, months):
2576
2532
raw = [x + pd .offsets .DateOffset (years = years , months = months ) for x in dti ]
2577
2533
expected = DatetimeIndex (raw )
2578
2534
tm .assert_index_equal (actual , expected )
2579
-
2580
-
2581
- # FIXME: this belongs in scalar tests
2582
- class SubDatetime (datetime ):
2583
- pass
2584
-
2585
-
2586
- @pytest .mark .parametrize (
2587
- "lh,rh" ,
2588
- [
2589
- (SubDatetime (2000 , 1 , 1 ), Timedelta (hours = 1 )),
2590
- (Timedelta (hours = 1 ), SubDatetime (2000 , 1 , 1 )),
2591
- ],
2592
- )
2593
- def test_dt_subclass_add_timedelta (lh , rh ):
2594
- # GH 25851
2595
- # ensure that subclassed datetime works for
2596
- # Timedelta operations
2597
- result = lh + rh
2598
- expected = SubDatetime (2000 , 1 , 1 , 1 )
2599
- assert result == expected
0 commit comments