I am trying to implement a custom scale (derived from matplotlib.scale.ScaleBase
) and custom transform (derived from matplotlib.transforms.Transform
) for special plotting of time-series, so it is solely used for the x-axis and for some form of datetime data, ie a numpy datetime array or a pandas DateIndex
.
The transform needs to to know the actual times represented by the x values, but unfortunately floats are passed into the Transform.transform_non_affine
method and there it has no idea what the time units are (seconds? minutes? hours? days?).
from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms
import numpy as np
import pandas as pd
import datetime
class MyScale(mscale.ScaleBase):
name = 'myscale'
def __init__(self, axis, **kwargs):
super(MyScale, self).__init__()
def get_transform(self):
return MyTransform()
def set_default_locators_and_formatters(self, axis):
return
class MyTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
has_inverse = True
def __init__(self):
super(MyTransform, self).__init__()
def transform_non_affine(self, a):
# ideally some time-dependent transforms here instead...
print a, a.dtype
return np.asanyarray(a)
def inverted(self):
raise NotImplementedError('not implemented')
Then if I do this:
t = pd.date_range('20160420 07:42', '20160421 12:12', freq='30T')
ts = pd.Series(np.arange(t.size), index=t)
import matplotlib.pyplot as plt
plt.gca().set_xscale('myscale')
ts.plot()
I see the following print outs:
[ 24352302. 24354012.] float64
[ 24352302.] float64
[ 24353280.] float64
[ 24354012.] float64
[ 24352380.] float64
[ 24352560.] float64
[ 24352740.] float64
[ 24352920.] float64
[ 24353100.] float64
[ 24353280.] float64
[ 24353460.] float64
[ 24353640.] float64
[ 24353820.] float64
[ 24354000.] float64
[ 24352302. 24352302.] float64
[ 24352302. 24352302.] float64
[ 24352302.] float64
[ 24352302.] float64
[ 24352302.] float64
[ 24352302.] float64
[ 24353280. 24353280.] float64
[ 24353280. 24353280.] float64
[ 24353280.] float64
[ 24353280.] float64
[ 24353280.] float64
[ 24353280.] float64
[ 24353280.] float64
[ 24354012. 24354012.] float64
[ 24354012. 24354012.] float64
...
[ 13. 13.] float64
[ 0.] float64
[ 0.] float64
[ 0.] float64
[ 0.] float64
[ 0.] float64
[ 0.] float64
[ 0.] float64
[ 0. 1.] float64
[ 0. 1.] float64
...
[ 24352302. 24352332. 24352362. 24352392. 24352422. 24352452.
24352482. 24352512. 24352542. 24352572. 24352602. 24352632.
24352662. 24352692. 24352722. 24352752. 24352782. 24352812.
24352842. 24352872. 24352902. 24352932. 24352962. 24352992.
24353022. 24353052. 24353082. 24353112. 24353142. 24353172.
24353202. 24353232. 24353262. 24353292. 24353322. 24353352.
24353382. 24353412. 24353442. 24353472. 24353502. 24353532.
24353562. 24353592. 24353622. 24353652. 24353682. 24353712.
24353742. 24353772. 24353802. 24353832. 24353862. 24353892.
24353922. 24353952. 24353982. 24354012.] float64
...
That long array is the actual array of datetimes, looks like it was converted to float with units of minutes:
.>>> np.datetime64(24352302, 'm')
numpy.datetime64('2016-04-20T02:42-0500')
Any ideas how to get around this?