test_scalar_compat.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. # -*- coding: utf-8 -*-
  2. """
  3. Tests for DatetimeIndex methods behaving like their Timestamp counterparts
  4. """
  5. from datetime import datetime
  6. import numpy as np
  7. import pytest
  8. import pandas as pd
  9. from pandas import DatetimeIndex, Timestamp, date_range
  10. import pandas.util.testing as tm
  11. from pandas.tseries.frequencies import to_offset
  12. class TestDatetimeIndexOps(object):
  13. def test_dti_time(self):
  14. rng = date_range('1/1/2000', freq='12min', periods=10)
  15. result = pd.Index(rng).time
  16. expected = [t.time() for t in rng]
  17. assert (result == expected).all()
  18. def test_dti_date(self):
  19. rng = date_range('1/1/2000', freq='12H', periods=10)
  20. result = pd.Index(rng).date
  21. expected = [t.date() for t in rng]
  22. assert (result == expected).all()
  23. def test_dti_date_out_of_range(self):
  24. # GH#1475
  25. pytest.raises(ValueError, DatetimeIndex, ['1400-01-01'])
  26. pytest.raises(ValueError, DatetimeIndex, [datetime(1400, 1, 1)])
  27. @pytest.mark.parametrize('field', [
  28. 'dayofweek', 'dayofyear', 'week', 'weekofyear', 'quarter',
  29. 'days_in_month', 'is_month_start', 'is_month_end',
  30. 'is_quarter_start', 'is_quarter_end', 'is_year_start',
  31. 'is_year_end', 'weekday_name'])
  32. def test_dti_timestamp_fields(self, field):
  33. # extra fields from DatetimeIndex like quarter and week
  34. idx = tm.makeDateIndex(100)
  35. expected = getattr(idx, field)[-1]
  36. if field == 'weekday_name':
  37. with tm.assert_produces_warning(FutureWarning,
  38. check_stacklevel=False):
  39. result = getattr(Timestamp(idx[-1]), field)
  40. else:
  41. result = getattr(Timestamp(idx[-1]), field)
  42. assert result == expected
  43. def test_dti_timestamp_freq_fields(self):
  44. # extra fields from DatetimeIndex like quarter and week
  45. idx = tm.makeDateIndex(100)
  46. assert idx.freq == Timestamp(idx[-1], idx.freq).freq
  47. assert idx.freqstr == Timestamp(idx[-1], idx.freq).freqstr
  48. # ----------------------------------------------------------------
  49. # DatetimeIndex.round
  50. def test_round_daily(self):
  51. dti = date_range('20130101 09:10:11', periods=5)
  52. result = dti.round('D')
  53. expected = date_range('20130101', periods=5)
  54. tm.assert_index_equal(result, expected)
  55. dti = dti.tz_localize('UTC').tz_convert('US/Eastern')
  56. result = dti.round('D')
  57. expected = date_range('20130101',
  58. periods=5).tz_localize('US/Eastern')
  59. tm.assert_index_equal(result, expected)
  60. result = dti.round('s')
  61. tm.assert_index_equal(result, dti)
  62. # invalid
  63. for freq in ['Y', 'M', 'foobar']:
  64. pytest.raises(ValueError, lambda: dti.round(freq))
  65. def test_round(self, tz_naive_fixture):
  66. tz = tz_naive_fixture
  67. rng = date_range(start='2016-01-01', periods=5,
  68. freq='30Min', tz=tz)
  69. elt = rng[1]
  70. expected_rng = DatetimeIndex([
  71. Timestamp('2016-01-01 00:00:00', tz=tz, freq='30T'),
  72. Timestamp('2016-01-01 00:00:00', tz=tz, freq='30T'),
  73. Timestamp('2016-01-01 01:00:00', tz=tz, freq='30T'),
  74. Timestamp('2016-01-01 02:00:00', tz=tz, freq='30T'),
  75. Timestamp('2016-01-01 02:00:00', tz=tz, freq='30T'),
  76. ])
  77. expected_elt = expected_rng[1]
  78. tm.assert_index_equal(rng.round(freq='H'), expected_rng)
  79. assert elt.round(freq='H') == expected_elt
  80. msg = pd._libs.tslibs.frequencies.INVALID_FREQ_ERR_MSG
  81. with pytest.raises(ValueError, match=msg):
  82. rng.round(freq='foo')
  83. with pytest.raises(ValueError, match=msg):
  84. elt.round(freq='foo')
  85. msg = "<MonthEnd> is a non-fixed frequency"
  86. with pytest.raises(ValueError, match=msg):
  87. rng.round(freq='M')
  88. with pytest.raises(ValueError, match=msg):
  89. elt.round(freq='M')
  90. # GH#14440 & GH#15578
  91. index = DatetimeIndex(['2016-10-17 12:00:00.0015'], tz=tz)
  92. result = index.round('ms')
  93. expected = DatetimeIndex(['2016-10-17 12:00:00.002000'], tz=tz)
  94. tm.assert_index_equal(result, expected)
  95. for freq in ['us', 'ns']:
  96. tm.assert_index_equal(index, index.round(freq))
  97. index = DatetimeIndex(['2016-10-17 12:00:00.00149'], tz=tz)
  98. result = index.round('ms')
  99. expected = DatetimeIndex(['2016-10-17 12:00:00.001000'], tz=tz)
  100. tm.assert_index_equal(result, expected)
  101. index = DatetimeIndex(['2016-10-17 12:00:00.001501031'])
  102. result = index.round('10ns')
  103. expected = DatetimeIndex(['2016-10-17 12:00:00.001501030'])
  104. tm.assert_index_equal(result, expected)
  105. with tm.assert_produces_warning(False):
  106. ts = '2016-10-17 12:00:00.001501031'
  107. DatetimeIndex([ts]).round('1010ns')
  108. def test_no_rounding_occurs(self, tz_naive_fixture):
  109. # GH 21262
  110. tz = tz_naive_fixture
  111. rng = date_range(start='2016-01-01', periods=5,
  112. freq='2Min', tz=tz)
  113. expected_rng = DatetimeIndex([
  114. Timestamp('2016-01-01 00:00:00', tz=tz, freq='2T'),
  115. Timestamp('2016-01-01 00:02:00', tz=tz, freq='2T'),
  116. Timestamp('2016-01-01 00:04:00', tz=tz, freq='2T'),
  117. Timestamp('2016-01-01 00:06:00', tz=tz, freq='2T'),
  118. Timestamp('2016-01-01 00:08:00', tz=tz, freq='2T'),
  119. ])
  120. tm.assert_index_equal(rng.round(freq='2T'), expected_rng)
  121. @pytest.mark.parametrize('test_input, rounder, freq, expected', [
  122. (['2117-01-01 00:00:45'], 'floor', '15s', ['2117-01-01 00:00:45']),
  123. (['2117-01-01 00:00:45'], 'ceil', '15s', ['2117-01-01 00:00:45']),
  124. (['2117-01-01 00:00:45.000000012'], 'floor', '10ns',
  125. ['2117-01-01 00:00:45.000000010']),
  126. (['1823-01-01 00:00:01.000000012'], 'ceil', '10ns',
  127. ['1823-01-01 00:00:01.000000020']),
  128. (['1823-01-01 00:00:01'], 'floor', '1s', ['1823-01-01 00:00:01']),
  129. (['1823-01-01 00:00:01'], 'ceil', '1s', ['1823-01-01 00:00:01']),
  130. (['2018-01-01 00:15:00'], 'ceil', '15T', ['2018-01-01 00:15:00']),
  131. (['2018-01-01 00:15:00'], 'floor', '15T', ['2018-01-01 00:15:00']),
  132. (['1823-01-01 03:00:00'], 'ceil', '3H', ['1823-01-01 03:00:00']),
  133. (['1823-01-01 03:00:00'], 'floor', '3H', ['1823-01-01 03:00:00']),
  134. (('NaT', '1823-01-01 00:00:01'), 'floor', '1s',
  135. ('NaT', '1823-01-01 00:00:01')),
  136. (('NaT', '1823-01-01 00:00:01'), 'ceil', '1s',
  137. ('NaT', '1823-01-01 00:00:01'))
  138. ])
  139. def test_ceil_floor_edge(self, test_input, rounder, freq, expected):
  140. dt = DatetimeIndex(list(test_input))
  141. func = getattr(dt, rounder)
  142. result = func(freq)
  143. expected = DatetimeIndex(list(expected))
  144. assert expected.equals(result)
  145. @pytest.mark.parametrize('start, index_freq, periods', [
  146. ('2018-01-01', '12H', 25),
  147. ('2018-01-01 0:0:0.124999', '1ns', 1000),
  148. ])
  149. @pytest.mark.parametrize('round_freq', [
  150. '2ns', '3ns', '4ns', '5ns', '6ns', '7ns',
  151. '250ns', '500ns', '750ns',
  152. '1us', '19us', '250us', '500us', '750us',
  153. '1s', '2s', '3s',
  154. '12H', '1D',
  155. ])
  156. def test_round_int64(self, start, index_freq, periods, round_freq):
  157. dt = date_range(start=start, freq=index_freq, periods=periods)
  158. unit = to_offset(round_freq).nanos
  159. # test floor
  160. result = dt.floor(round_freq)
  161. diff = dt.asi8 - result.asi8
  162. mod = result.asi8 % unit
  163. assert (mod == 0).all(), "floor not a {} multiple".format(round_freq)
  164. assert (0 <= diff).all() and (diff < unit).all(), "floor error"
  165. # test ceil
  166. result = dt.ceil(round_freq)
  167. diff = result.asi8 - dt.asi8
  168. mod = result.asi8 % unit
  169. assert (mod == 0).all(), "ceil not a {} multiple".format(round_freq)
  170. assert (0 <= diff).all() and (diff < unit).all(), "ceil error"
  171. # test round
  172. result = dt.round(round_freq)
  173. diff = abs(result.asi8 - dt.asi8)
  174. mod = result.asi8 % unit
  175. assert (mod == 0).all(), "round not a {} multiple".format(round_freq)
  176. assert (diff <= unit // 2).all(), "round error"
  177. if unit % 2 == 0:
  178. assert (
  179. result.asi8[diff == unit // 2] % 2 == 0
  180. ).all(), "round half to even error"
  181. # ----------------------------------------------------------------
  182. # DatetimeIndex.normalize
  183. def test_normalize(self):
  184. rng = date_range('1/1/2000 9:30', periods=10, freq='D')
  185. result = rng.normalize()
  186. expected = date_range('1/1/2000', periods=10, freq='D')
  187. tm.assert_index_equal(result, expected)
  188. arr_ns = np.array([1380585623454345752,
  189. 1380585612343234312]).astype("datetime64[ns]")
  190. rng_ns = DatetimeIndex(arr_ns)
  191. rng_ns_normalized = rng_ns.normalize()
  192. arr_ns = np.array([1380585600000000000,
  193. 1380585600000000000]).astype("datetime64[ns]")
  194. expected = DatetimeIndex(arr_ns)
  195. tm.assert_index_equal(rng_ns_normalized, expected)
  196. assert result.is_normalized
  197. assert not rng.is_normalized
  198. def test_normalize_nat(self):
  199. dti = DatetimeIndex([pd.NaT, Timestamp('2018-01-01 01:00:00')])
  200. result = dti.normalize()
  201. expected = DatetimeIndex([pd.NaT, Timestamp('2018-01-01')])
  202. tm.assert_index_equal(result, expected)
  203. class TestDateTimeIndexToJulianDate(object):
  204. def test_1700(self):
  205. dr = date_range(start=Timestamp('1710-10-01'), periods=5, freq='D')
  206. r1 = pd.Index([x.to_julian_date() for x in dr])
  207. r2 = dr.to_julian_date()
  208. assert isinstance(r2, pd.Float64Index)
  209. tm.assert_index_equal(r1, r2)
  210. def test_2000(self):
  211. dr = date_range(start=Timestamp('2000-02-27'), periods=5, freq='D')
  212. r1 = pd.Index([x.to_julian_date() for x in dr])
  213. r2 = dr.to_julian_date()
  214. assert isinstance(r2, pd.Float64Index)
  215. tm.assert_index_equal(r1, r2)
  216. def test_hour(self):
  217. dr = date_range(start=Timestamp('2000-02-27'), periods=5, freq='H')
  218. r1 = pd.Index([x.to_julian_date() for x in dr])
  219. r2 = dr.to_julian_date()
  220. assert isinstance(r2, pd.Float64Index)
  221. tm.assert_index_equal(r1, r2)
  222. def test_minute(self):
  223. dr = date_range(start=Timestamp('2000-02-27'), periods=5, freq='T')
  224. r1 = pd.Index([x.to_julian_date() for x in dr])
  225. r2 = dr.to_julian_date()
  226. assert isinstance(r2, pd.Float64Index)
  227. tm.assert_index_equal(r1, r2)
  228. def test_second(self):
  229. dr = date_range(start=Timestamp('2000-02-27'), periods=5, freq='S')
  230. r1 = pd.Index([x.to_julian_date() for x in dr])
  231. r2 = dr.to_julian_date()
  232. assert isinstance(r2, pd.Float64Index)
  233. tm.assert_index_equal(r1, r2)