<div dir="ltr"><div dir="ltr"><div>Hi,</div><div><br></div><div>I have a time series with two values x(t) and y(t), stored in a Pandas data frame df with columns DateTime, ValueX, ValueY. I would like to plot ValueY vs. ValueX. In addition, I would like to see for each data point on the graph the date when it was measured.<br></div><div><br></div><div>My idea was to plot(ValueX, ValueY) and then somehow set the labels to DateTime. But not only the visible tick labels. Rather, when I move my mouse over the plot, I would like to see  (DateTime, ValueY) for each point, rather then (ValueX, ValueY).</div><div><br></div><div>Or, another way to see it is, that I would like to plot ValueY vs. DateTime, but scale the x-axis as ValueX.</div><div><br></div><div>My take was to plt.plot(df.index, df.ValueX), and to write a custom Scale module that receives the data frame upon construction and that scales the x-axis as ValueX and formats the labels as DateTime.</div><div><br></div><div>I managed to have the right scaling of my x-axis but I can't see any ticks nor tick labels. And when I hover the mouse over the plot, I see (x=nan, y=<correct value>). <br></div><div><br></div><div>I appreciate any help! :) It seems that the documentation for such low-level functionality is sometimes a bit scarce :)<br></div><div><br></div><div>Here is a minimum working example:<br></div><div><br></div><div>import matplotlib.scale<br>import matplotlib.transforms<br>import matplotlib.pyplot as plt<br>from matplotlib.ticker import AutoLocator, FixedLocator, FuncFormatter, MaxNLocator, ScalarFormatter<br>import numpy as np<br>from numpy import ma<br>import pandas as pd<br><br>class Scaler(matplotlib.scale.ScaleBase):<br>    <br>    name = 'scaler'<br>    <br>    def __init__(self, axis, df, **kwargs):<br>        matplotlib.scale.ScaleBase.__init__(self)<br>        self.df = df<br><br>    def get_transform(self):<br>        return self.Transform(self.df)<br>    <br>    def limit_range_for_scale(self, vmin, vmax, minpos):<br>        min_ = max(vmin, self.df.index.min())<br>        max_ = min(vmax, self.df.index.max())<br>        return min_, max_<br>    <br>    def set_default_locators_and_formatters(self, axis):<br>        axis.set_major_locator(AutoLocator())<br>        axis.set_major_formatter(ScalarFormatter())<br><br>    class Transform(matplotlib.transforms.Transform):<br>        input_dims = 1<br>        output_dims = 1<br>        is_separable = True<br>        has_inverse = True<br>        <br>        def __init__(self, df):<br>            matplotlib.transforms.Transform.__init__(self)<br>            self.df = df<br><br>        def transform_non_affine(self, x):<br>            if x.ndim > 1:<br>                assert x.ndim == 2 and x.shape[1] == 1<br>            y = ma.masked_array(np.zeros_like(x), mask=[False for _ in x])<br>            for i in range(x.shape[0]):<br>                if x.ndim == 1:<br>                    if (int(x[i]) != x[i]) or (x[i] not in df.index):<br>                        y.mask[i] = True<br>                    else:<br>                        y[i] = <a href="http://self.df.at">self.df.at</a>[int(x[i]), 'x']<br>                else:<br>                    if (int(x[i, 0]) != x[i, 0]) or (x[i, 0] not in df.index):<br>                        y.mask[i] = True<br>                    else:<br>                        y[i, 0] = <a href="http://self.df.at">self.df.at</a>[int(x[i, 0]), 'x']<br>            return y<br>            <br>        def inverted(self):<br>            return Scaler.InvertedTransform(self.df)<br><br>    class InvertedTransform(matplotlib.transforms.Transform):<br>        input_dims = 1<br>        output_dims = 1<br>        is_separable = True<br>        has_inverse = True<br>        <br>        def __init__(self, df):<br>            matplotlib.transforms.Transform.__init__(self)<br>            self.df = df<br><br>        def transform_non_affine(self, x):<br>            if x.ndim > 1:<br>                assert x.ndim == 2 and x.shape[1] == 1<br>            y = ma.masked_array(np.zeros_like(x), mask=[False for _ in x])<br>            for i in range(x.shape[0]):<br>                if x.ndim == 1:<br>                    if x[i] not in df['x']:<br>                        y.mask[i] = True<br>                    else:<br>                        y[i] = self.df.loc[self.df['x'] == x[i], :].index[0]<br>                else:<br>                    if x[i, 0] not in df['x']:<br>                        y.mask[i] = True<br>                    else:<br>                        y[i, 0] = self.df.loc[self.df['x'] == x[i, 0], :].index[0]<br>            return y<br><br>        def inverted(self):<br>            return Scaler.Transform(self.df)<br><br>matplotlib.scale.register_scale(Scaler)<br><br>df = pd.DataFrame(index=range(1, 11), data={'x': [1, 1.5, 3, 3.5, 5, 5.5, 7, 7.5, 9, 9.5], 'y': range(1, 11)})<br>fig = plt.figure()<br>ax = fig.add_subplot(1, 1, 1)<br>ax.plot(df.index, df['y'])<br>ax.set_xlim([df.index[0], df.index[-1]])<br>ax.set_xscale('scaler', df=df)<br></div><div><br></div><div>Cheers</div><div>Konstantin<br></div><div><br></div><div><div dir="ltr" class="gmail_signature"><div dir="ltr">--<br>To send me an encrypted email, download my public key from <a href="http://pgp.mit.edu" target="_blank">pgp.mit.edu</a><br></div></div></div></div></div>