123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- from datetime import (
- date,
- datetime,
- )
- import subprocess
- import sys
- import numpy as np
- import pytest
- import pandas._config.config as cf
- from pandas import (
- Index,
- Period,
- PeriodIndex,
- Series,
- Timestamp,
- arrays,
- date_range,
- )
- import pandas._testing as tm
- from pandas.plotting import (
- deregister_matplotlib_converters,
- register_matplotlib_converters,
- )
- from pandas.tseries.offsets import (
- Day,
- Micro,
- Milli,
- Second,
- )
- try:
- from pandas.plotting._matplotlib import converter
- except ImportError:
- # try / except, rather than skip, to avoid internal refactoring
- # causing an improper skip
- pass
- pytest.importorskip("matplotlib.pyplot")
- dates = pytest.importorskip("matplotlib.dates")
- def test_registry_mpl_resets():
- # Check that Matplotlib converters are properly reset (see issue #27481)
- code = (
- "import matplotlib.units as units; "
- "import matplotlib.dates as mdates; "
- "n_conv = len(units.registry); "
- "import pandas as pd; "
- "pd.plotting.register_matplotlib_converters(); "
- "pd.plotting.deregister_matplotlib_converters(); "
- "assert len(units.registry) == n_conv"
- )
- call = [sys.executable, "-c", code]
- subprocess.check_output(call)
- def test_timtetonum_accepts_unicode():
- assert converter.time2num("00:01") == converter.time2num("00:01")
- class TestRegistration:
- def test_dont_register_by_default(self):
- # Run in subprocess to ensure a clean state
- code = (
- "import matplotlib.units; "
- "import pandas as pd; "
- "units = dict(matplotlib.units.registry); "
- "assert pd.Timestamp not in units"
- )
- call = [sys.executable, "-c", code]
- assert subprocess.check_call(call) == 0
- def test_registering_no_warning(self):
- plt = pytest.importorskip("matplotlib.pyplot")
- s = Series(range(12), index=date_range("2017", periods=12))
- _, ax = plt.subplots()
- # Set to the "warn" state, in case this isn't the first test run
- register_matplotlib_converters()
- ax.plot(s.index, s.values)
- plt.close()
- def test_pandas_plots_register(self):
- plt = pytest.importorskip("matplotlib.pyplot")
- s = Series(range(12), index=date_range("2017", periods=12))
- # Set to the "warn" state, in case this isn't the first test run
- with tm.assert_produces_warning(None) as w:
- s.plot()
- try:
- assert len(w) == 0
- finally:
- plt.close()
- def test_matplotlib_formatters(self):
- units = pytest.importorskip("matplotlib.units")
- # Can't make any assertion about the start state.
- # We we check that toggling converters off removes it, and toggling it
- # on restores it.
- with cf.option_context("plotting.matplotlib.register_converters", True):
- with cf.option_context("plotting.matplotlib.register_converters", False):
- assert Timestamp not in units.registry
- assert Timestamp in units.registry
- def test_option_no_warning(self):
- pytest.importorskip("matplotlib.pyplot")
- ctx = cf.option_context("plotting.matplotlib.register_converters", False)
- plt = pytest.importorskip("matplotlib.pyplot")
- s = Series(range(12), index=date_range("2017", periods=12))
- _, ax = plt.subplots()
- # Test without registering first, no warning
- with ctx:
- ax.plot(s.index, s.values)
- # Now test with registering
- register_matplotlib_converters()
- with ctx:
- ax.plot(s.index, s.values)
- plt.close()
- def test_registry_resets(self):
- units = pytest.importorskip("matplotlib.units")
- dates = pytest.importorskip("matplotlib.dates")
- # make a copy, to reset to
- original = dict(units.registry)
- try:
- # get to a known state
- units.registry.clear()
- date_converter = dates.DateConverter()
- units.registry[datetime] = date_converter
- units.registry[date] = date_converter
- register_matplotlib_converters()
- assert units.registry[date] is not date_converter
- deregister_matplotlib_converters()
- assert units.registry[date] is date_converter
- finally:
- # restore original stater
- units.registry.clear()
- for k, v in original.items():
- units.registry[k] = v
- class TestDateTimeConverter:
- @pytest.fixture
- def dtc(self):
- return converter.DatetimeConverter()
- def test_convert_accepts_unicode(self, dtc):
- r1 = dtc.convert("2000-01-01 12:22", None, None)
- r2 = dtc.convert("2000-01-01 12:22", None, None)
- assert r1 == r2, "DatetimeConverter.convert should accept unicode"
- def test_conversion(self, dtc):
- rs = dtc.convert(["2012-1-1"], None, None)[0]
- xp = dates.date2num(datetime(2012, 1, 1))
- assert rs == xp
- rs = dtc.convert("2012-1-1", None, None)
- assert rs == xp
- rs = dtc.convert(date(2012, 1, 1), None, None)
- assert rs == xp
- rs = dtc.convert("2012-1-1", None, None)
- assert rs == xp
- rs = dtc.convert(Timestamp("2012-1-1"), None, None)
- assert rs == xp
- # also testing datetime64 dtype (GH8614)
- rs = dtc.convert("2012-01-01", None, None)
- assert rs == xp
- rs = dtc.convert("2012-01-01 00:00:00+0000", None, None)
- assert rs == xp
- rs = dtc.convert(
- np.array(["2012-01-01 00:00:00+0000", "2012-01-02 00:00:00+0000"]),
- None,
- None,
- )
- assert rs[0] == xp
- # we have a tz-aware date (constructed to that when we turn to utc it
- # is the same as our sample)
- ts = Timestamp("2012-01-01").tz_localize("UTC").tz_convert("US/Eastern")
- rs = dtc.convert(ts, None, None)
- assert rs == xp
- rs = dtc.convert(ts.to_pydatetime(), None, None)
- assert rs == xp
- rs = dtc.convert(Index([ts - Day(1), ts]), None, None)
- assert rs[1] == xp
- rs = dtc.convert(Index([ts - Day(1), ts]).to_pydatetime(), None, None)
- assert rs[1] == xp
- def test_conversion_float(self, dtc):
- rtol = 0.5 * 10**-9
- rs = dtc.convert(Timestamp("2012-1-1 01:02:03", tz="UTC"), None, None)
- xp = converter.mdates.date2num(Timestamp("2012-1-1 01:02:03", tz="UTC"))
- tm.assert_almost_equal(rs, xp, rtol=rtol)
- rs = dtc.convert(
- Timestamp("2012-1-1 09:02:03", tz="Asia/Hong_Kong"), None, None
- )
- tm.assert_almost_equal(rs, xp, rtol=rtol)
- rs = dtc.convert(datetime(2012, 1, 1, 1, 2, 3), None, None)
- tm.assert_almost_equal(rs, xp, rtol=rtol)
- def test_conversion_outofbounds_datetime(self, dtc):
- # 2579
- values = [date(1677, 1, 1), date(1677, 1, 2)]
- rs = dtc.convert(values, None, None)
- xp = converter.mdates.date2num(values)
- tm.assert_numpy_array_equal(rs, xp)
- rs = dtc.convert(values[0], None, None)
- xp = converter.mdates.date2num(values[0])
- assert rs == xp
- values = [datetime(1677, 1, 1, 12), datetime(1677, 1, 2, 12)]
- rs = dtc.convert(values, None, None)
- xp = converter.mdates.date2num(values)
- tm.assert_numpy_array_equal(rs, xp)
- rs = dtc.convert(values[0], None, None)
- xp = converter.mdates.date2num(values[0])
- assert rs == xp
- @pytest.mark.parametrize(
- "time,format_expected",
- [
- (0, "00:00"), # time2num(datetime.time.min)
- (86399.999999, "23:59:59.999999"), # time2num(datetime.time.max)
- (90000, "01:00"),
- (3723, "01:02:03"),
- (39723.2, "11:02:03.200"),
- ],
- )
- def test_time_formatter(self, time, format_expected):
- # issue 18478
- result = converter.TimeFormatter(None)(time)
- assert result == format_expected
- @pytest.mark.parametrize("freq", ("B", "L", "S"))
- def test_dateindex_conversion(self, freq, dtc):
- rtol = 10**-9
- dateindex = tm.makeDateIndex(k=10, freq=freq)
- rs = dtc.convert(dateindex, None, None)
- xp = converter.mdates.date2num(dateindex._mpl_repr())
- tm.assert_almost_equal(rs, xp, rtol=rtol)
- @pytest.mark.parametrize("offset", [Second(), Milli(), Micro(50)])
- def test_resolution(self, offset, dtc):
- # Matplotlib's time representation using floats cannot distinguish
- # intervals smaller than ~10 microsecond in the common range of years.
- ts1 = Timestamp("2012-1-1")
- ts2 = ts1 + offset
- val1 = dtc.convert(ts1, None, None)
- val2 = dtc.convert(ts2, None, None)
- if not val1 < val2:
- raise AssertionError(f"{val1} is not less than {val2}.")
- def test_convert_nested(self, dtc):
- inner = [Timestamp("2017-01-01"), Timestamp("2017-01-02")]
- data = [inner, inner]
- result = dtc.convert(data, None, None)
- expected = [dtc.convert(x, None, None) for x in data]
- assert (np.array(result) == expected).all()
- class TestPeriodConverter:
- @pytest.fixture
- def pc(self):
- return converter.PeriodConverter()
- @pytest.fixture
- def axis(self):
- class Axis:
- pass
- axis = Axis()
- axis.freq = "D"
- return axis
- def test_convert_accepts_unicode(self, pc, axis):
- r1 = pc.convert("2012-1-1", None, axis)
- r2 = pc.convert("2012-1-1", None, axis)
- assert r1 == r2
- def test_conversion(self, pc, axis):
- rs = pc.convert(["2012-1-1"], None, axis)[0]
- xp = Period("2012-1-1").ordinal
- assert rs == xp
- rs = pc.convert("2012-1-1", None, axis)
- assert rs == xp
- rs = pc.convert([date(2012, 1, 1)], None, axis)[0]
- assert rs == xp
- rs = pc.convert(date(2012, 1, 1), None, axis)
- assert rs == xp
- rs = pc.convert([Timestamp("2012-1-1")], None, axis)[0]
- assert rs == xp
- rs = pc.convert(Timestamp("2012-1-1"), None, axis)
- assert rs == xp
- rs = pc.convert("2012-01-01", None, axis)
- assert rs == xp
- rs = pc.convert("2012-01-01 00:00:00+0000", None, axis)
- assert rs == xp
- rs = pc.convert(
- np.array(
- ["2012-01-01 00:00:00", "2012-01-02 00:00:00"],
- dtype="datetime64[ns]",
- ),
- None,
- axis,
- )
- assert rs[0] == xp
- def test_integer_passthrough(self, pc, axis):
- # GH9012
- rs = pc.convert([0, 1], None, axis)
- xp = [0, 1]
- assert rs == xp
- def test_convert_nested(self, pc, axis):
- data = ["2012-1-1", "2012-1-2"]
- r1 = pc.convert([data, data], None, axis)
- r2 = [pc.convert(data, None, axis) for _ in range(2)]
- assert r1 == r2
- class TestTimeDeltaConverter:
- """Test timedelta converter"""
- @pytest.mark.parametrize(
- "x, decimal, format_expected",
- [
- (0.0, 0, "00:00:00"),
- (3972320000000, 1, "01:06:12.3"),
- (713233432000000, 2, "8 days 06:07:13.43"),
- (32423432000000, 4, "09:00:23.4320"),
- ],
- )
- def test_format_timedelta_ticks(self, x, decimal, format_expected):
- tdc = converter.TimeSeries_TimedeltaFormatter
- result = tdc.format_timedelta_ticks(x, pos=None, n_decimals=decimal)
- assert result == format_expected
- @pytest.mark.parametrize("view_interval", [(1, 2), (2, 1)])
- def test_call_w_different_view_intervals(self, view_interval, monkeypatch):
- # previously broke on reversed xlmits; see GH37454
- class mock_axis:
- def get_view_interval(self):
- return view_interval
- tdc = converter.TimeSeries_TimedeltaFormatter()
- monkeypatch.setattr(tdc, "axis", mock_axis())
- tdc(0.0, 0)
- @pytest.mark.parametrize("year_span", [11.25, 30, 80, 150, 400, 800, 1500, 2500, 3500])
- # The range is limited to 11.25 at the bottom by if statements in
- # the _quarterly_finder() function
- def test_quarterly_finder(year_span):
- vmin = -1000
- vmax = vmin + year_span * 4
- span = vmax - vmin + 1
- if span < 45: # the quarterly finder is only invoked if the span is >= 45
- return
- nyears = span / 4
- (min_anndef, maj_anndef) = converter._get_default_annual_spacing(nyears)
- result = converter._quarterly_finder(vmin, vmax, "Q")
- quarters = PeriodIndex(
- arrays.PeriodArray(np.array([x[0] for x in result]), freq="Q")
- )
- majors = np.array([x[1] for x in result])
- minors = np.array([x[2] for x in result])
- major_quarters = quarters[majors]
- minor_quarters = quarters[minors]
- check_major_years = major_quarters.year % maj_anndef == 0
- check_minor_years = minor_quarters.year % min_anndef == 0
- check_major_quarters = major_quarters.quarter == 1
- check_minor_quarters = minor_quarters.quarter == 1
- assert np.all(check_major_years)
- assert np.all(check_minor_years)
- assert np.all(check_major_quarters)
- assert np.all(check_minor_quarters)
|