Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

agriconnect / pandas   python

Repository URL to install this package:

/ plotting / _converter.py

import datetime as pydt
from datetime import datetime, timedelta
import warnings

from dateutil.relativedelta import relativedelta
import matplotlib.dates as dates
from matplotlib.ticker import AutoLocator, Formatter, Locator
from matplotlib.transforms import nonsingular
import matplotlib.units as units
import numpy as np

from pandas._libs import lib, tslibs
from pandas._libs.tslibs import resolution
from pandas._libs.tslibs.frequencies import FreqGroup, get_freq
import pandas.compat as compat
from pandas.compat import lrange

from pandas.core.dtypes.common import (
    is_datetime64_ns_dtype, is_float, is_float_dtype, is_integer,
    is_integer_dtype, is_nested_list_like)
from pandas.core.dtypes.generic import ABCSeries

import pandas.core.common as com
from pandas.core.index import Index
from pandas.core.indexes.datetimes import date_range
from pandas.core.indexes.period import Period, PeriodIndex, period_range
import pandas.core.tools.datetimes as tools

# constants
HOURS_PER_DAY = 24.
MIN_PER_HOUR = 60.
SEC_PER_MIN = 60.

SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR
SEC_PER_DAY = SEC_PER_HOUR * HOURS_PER_DAY

MUSEC_PER_DAY = 1e6 * SEC_PER_DAY

_WARN = True  # Global for whether pandas has registered the units explicitly
_mpl_units = {}  # Cache for units overwritten by us


def get_pairs():
    pairs = [
        (tslibs.Timestamp, DatetimeConverter),
        (Period, PeriodConverter),
        (pydt.datetime, DatetimeConverter),
        (pydt.date, DatetimeConverter),
        (pydt.time, TimeConverter),
        (np.datetime64, DatetimeConverter),
    ]
    return pairs


def register(explicit=True):
    """
    Register Pandas Formatters and Converters with matplotlib

    This function modifies the global ``matplotlib.units.registry``
    dictionary. Pandas adds custom converters for

    * pd.Timestamp
    * pd.Period
    * np.datetime64
    * datetime.datetime
    * datetime.date
    * datetime.time

    See Also
    --------
    deregister_matplotlib_converter
    """
    # Renamed in pandas.plotting.__init__
    global _WARN

    if explicit:
        _WARN = False

    pairs = get_pairs()
    for type_, cls in pairs:
        converter = cls()
        if type_ in units.registry:
            previous = units.registry[type_]
            _mpl_units[type_] = previous
        units.registry[type_] = converter


def deregister():
    """
    Remove pandas' formatters and converters

    Removes the custom converters added by :func:`register`. This
    attempts to set the state of the registry back to the state before
    pandas registered its own units. Converters for pandas' own types like
    Timestamp and Period are removed completely. Converters for types
    pandas overwrites, like ``datetime.datetime``, are restored to their
    original value.

    See Also
    --------
    deregister_matplotlib_converters
    """
    # Renamed in pandas.plotting.__init__
    for type_, cls in get_pairs():
        # We use type to catch our classes directly, no inheritance
        if type(units.registry.get(type_)) is cls:
            units.registry.pop(type_)

    # restore the old keys
    for unit, formatter in _mpl_units.items():
        if type(formatter) not in {DatetimeConverter, PeriodConverter,
                                   TimeConverter}:
            # make it idempotent by excluding ours.
            units.registry[unit] = formatter


def _check_implicitly_registered():
    global _WARN

    if _WARN:
        msg = ("Using an implicitly registered datetime converter for a "
               "matplotlib plotting method. The converter was registered "
               "by pandas on import. Future versions of pandas will require "
               "you to explicitly register matplotlib converters.\n\n"
               "To register the converters:\n\t"
               ">>> from pandas.plotting import register_matplotlib_converters"
               "\n\t"
               ">>> register_matplotlib_converters()")
        warnings.warn(msg, FutureWarning)
        _WARN = False


def _to_ordinalf(tm):
    tot_sec = (tm.hour * 3600 + tm.minute * 60 + tm.second +
               float(tm.microsecond / 1e6))
    return tot_sec


def time2num(d):
    if isinstance(d, compat.string_types):
        parsed = tools.to_datetime(d)
        if not isinstance(parsed, datetime):
            raise ValueError('Could not parse time {d}'.format(d=d))
        return _to_ordinalf(parsed.time())
    if isinstance(d, pydt.time):
        return _to_ordinalf(d)
    return d


class TimeConverter(units.ConversionInterface):

    @staticmethod
    def convert(value, unit, axis):
        valid_types = (str, pydt.time)
        if (isinstance(value, valid_types) or is_integer(value) or
                is_float(value)):
            return time2num(value)
        if isinstance(value, Index):
            return value.map(time2num)
        if isinstance(value, (list, tuple, np.ndarray, Index)):
            return [time2num(x) for x in value]
        return value

    @staticmethod
    def axisinfo(unit, axis):
        if unit != 'time':
            return None

        majloc = AutoLocator()
        majfmt = TimeFormatter(majloc)
        return units.AxisInfo(majloc=majloc, majfmt=majfmt, label='time')

    @staticmethod
    def default_units(x, axis):
        return 'time'


# time formatter
class TimeFormatter(Formatter):

    def __init__(self, locs):
        self.locs = locs

    def __call__(self, x, pos=0):
        """
        Return the time of day as a formatted string.

        Parameters
        ----------
        x : float
            The time of day specified as seconds since 00:00 (midnight),
            with up to microsecond precision.
        pos
            Unused

        Returns
        -------
        str
            A string in HH:MM:SS.mmmuuu format. Microseconds,
            milliseconds and seconds are only displayed if non-zero.
        """
        fmt = '%H:%M:%S.%f'
        s = int(x)
        msus = int(round((x - s) * 1e6))
        ms = msus // 1000
        us = msus % 1000
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)
        _, h = divmod(h, 24)
        if us != 0:
            return pydt.time(h, m, s, msus).strftime(fmt)
        elif ms != 0:
            return pydt.time(h, m, s, msus).strftime(fmt)[:-3]
        elif s != 0:
            return pydt.time(h, m, s).strftime('%H:%M:%S')

        return pydt.time(h, m).strftime('%H:%M')


# Period Conversion


class PeriodConverter(dates.DateConverter):

    @staticmethod
    def convert(values, units, axis):
        if is_nested_list_like(values):
            values = [PeriodConverter._convert_1d(v, units, axis)
                      for v in values]
        else:
            values = PeriodConverter._convert_1d(values, units, axis)
        return values

    @staticmethod
    def _convert_1d(values, units, axis):
        if not hasattr(axis, 'freq'):
            raise TypeError('Axis must have `freq` set to convert to Periods')
        valid_types = (compat.string_types, datetime,
                       Period, pydt.date, pydt.time, np.datetime64)
        if (isinstance(values, valid_types) or is_integer(values) or
                is_float(values)):
            return get_datevalue(values, axis.freq)
        elif isinstance(values, PeriodIndex):
            return values.asfreq(axis.freq)._ndarray_values
        elif isinstance(values, Index):
            return values.map(lambda x: get_datevalue(x, axis.freq))
        elif lib.infer_dtype(values, skipna=False) == 'period':
            # https://github.com/pandas-dev/pandas/issues/24304
            # convert ndarray[period] -> PeriodIndex
            return PeriodIndex(values, freq=axis.freq)._ndarray_values
        elif isinstance(values, (list, tuple, np.ndarray, Index)):
            return [get_datevalue(x, axis.freq) for x in values]
        return values


def get_datevalue(date, freq):
    if isinstance(date, Period):
        return date.asfreq(freq).ordinal
    elif isinstance(date, (compat.string_types, datetime,
                           pydt.date, pydt.time, np.datetime64)):
        return Period(date, freq).ordinal
    elif (is_integer(date) or is_float(date) or
          (isinstance(date, (np.ndarray, Index)) and (date.size == 1))):
        return date
    elif date is None:
        return None
    raise ValueError("Unrecognizable date '{date}'".format(date=date))


def _dt_to_float_ordinal(dt):
    """
    Convert :mod:`datetime` to the Gregorian date as UTC float days,
    preserving hours, minutes, seconds and microseconds.  Return value
    is a :func:`float`.
    """
    if (isinstance(dt, (np.ndarray, Index, ABCSeries)
                   ) and is_datetime64_ns_dtype(dt)):
        base = dates.epoch2num(dt.asi8 / 1.0E9)
    else:
        base = dates.date2num(dt)
    return base


# Datetime Conversion
class DatetimeConverter(dates.DateConverter):

    @staticmethod
    def convert(values, unit, axis):
        # values might be a 1-d array, or a list-like of arrays.
        _check_implicitly_registered()
        if is_nested_list_like(values):
            values = [DatetimeConverter._convert_1d(v, unit, axis)
                      for v in values]
        else:
            values = DatetimeConverter._convert_1d(values, unit, axis)
        return values

    @staticmethod
    def _convert_1d(values, unit, axis):
        def try_parse(values):
            try:
                return _dt_to_float_ordinal(tools.to_datetime(values))
            except Exception:
                return values

        if isinstance(values, (datetime, pydt.date)):
            return _dt_to_float_ordinal(values)
        elif isinstance(values, np.datetime64):
            return _dt_to_float_ordinal(tslibs.Timestamp(values))
        elif isinstance(values, pydt.time):
            return dates.date2num(values)
        elif (is_integer(values) or is_float(values)):
            return values
        elif isinstance(values, compat.string_types):
            return try_parse(values)
        elif isinstance(values, (list, tuple, np.ndarray, Index, ABCSeries)):
            if isinstance(values, ABCSeries):
                # https://github.com/matplotlib/matplotlib/issues/11391
                # Series was skipped. Convert to DatetimeIndex to get asi8
                values = Index(values)
            if isinstance(values, Index):
                values = values.values
            if not isinstance(values, np.ndarray):
                values = com.asarray_tuplesafe(values)

            if is_integer_dtype(values) or is_float_dtype(values):
                return values

            try:
                values = tools.to_datetime(values)
                if isinstance(values, Index):
                    values = _dt_to_float_ordinal(values)
                else:
                    values = [_dt_to_float_ordinal(x) for x in values]
            except Exception:
                values = _dt_to_float_ordinal(values)

        return values

    @staticmethod
    def axisinfo(unit, axis):
        """
        Return the :class:`~matplotlib.units.AxisInfo` for *unit*.

        *unit* is a tzinfo instance or None.
Loading ...