[Matplotlib-users] Writing a custom Scale class
Paul Hobson
pmhobson at gmail.com
Thu Mar 21 18:21:12 EDT 2019
When the axes limits go beyond the bounds of your dataframe, how should the
ticks be labeled?
On Thu, Mar 21, 2019 at 2:03 PM Konstantin Miller <
konstantin.miller at gmail.com> wrote:
> Hi,
>
> 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.
>
> 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).
>
> Or, another way to see it is, that I would like to plot ValueY vs.
> DateTime, but scale the x-axis as ValueX.
>
> 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.
>
> 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>).
>
> I appreciate any help! :) It seems that the documentation for such
> low-level functionality is sometimes a bit scarce :)
>
> Here is a minimum working example:
>
> import matplotlib.scale
> import matplotlib.transforms
> import matplotlib.pyplot as plt
> from matplotlib.ticker import AutoLocator, FixedLocator, FuncFormatter,
> MaxNLocator, ScalarFormatter
> import numpy as np
> from numpy import ma
> import pandas as pd
>
> class Scaler(matplotlib.scale.ScaleBase):
>
> name = 'scaler'
>
> def __init__(self, axis, df, **kwargs):
> matplotlib.scale.ScaleBase.__init__(self)
> self.df = df
>
> def get_transform(self):
> return self.Transform(self.df)
>
> def limit_range_for_scale(self, vmin, vmax, minpos):
> min_ = max(vmin, self.df.index.min())
> max_ = min(vmax, self.df.index.max())
> return min_, max_
>
> def set_default_locators_and_formatters(self, axis):
> axis.set_major_locator(AutoLocator())
> axis.set_major_formatter(ScalarFormatter())
>
> class Transform(matplotlib.transforms.Transform):
> input_dims = 1
> output_dims = 1
> is_separable = True
> has_inverse = True
>
> def __init__(self, df):
> matplotlib.transforms.Transform.__init__(self)
> self.df = df
>
> def transform_non_affine(self, x):
> if x.ndim > 1:
> assert x.ndim == 2 and x.shape[1] == 1
> y = ma.masked_array(np.zeros_like(x), mask=[False for _ in x])
> for i in range(x.shape[0]):
> if x.ndim == 1:
> if (int(x[i]) != x[i]) or (x[i] not in df.index):
> y.mask[i] = True
> else:
> y[i] = self.df.at[int(x[i]), 'x']
> else:
> if (int(x[i, 0]) != x[i, 0]) or (x[i, 0] not in
> df.index):
> y.mask[i] = True
> else:
> y[i, 0] = self.df.at[int(x[i, 0]), 'x']
> return y
>
> def inverted(self):
> return Scaler.InvertedTransform(self.df)
>
> class InvertedTransform(matplotlib.transforms.Transform):
> input_dims = 1
> output_dims = 1
> is_separable = True
> has_inverse = True
>
> def __init__(self, df):
> matplotlib.transforms.Transform.__init__(self)
> self.df = df
>
> def transform_non_affine(self, x):
> if x.ndim > 1:
> assert x.ndim == 2 and x.shape[1] == 1
> y = ma.masked_array(np.zeros_like(x), mask=[False for _ in x])
> for i in range(x.shape[0]):
> if x.ndim == 1:
> if x[i] not in df['x']:
> y.mask[i] = True
> else:
> y[i] = self.df.loc[self.df['x'] == x[i],
> :].index[0]
> else:
> if x[i, 0] not in df['x']:
> y.mask[i] = True
> else:
> y[i, 0] = self.df.loc[self.df['x'] == x[i, 0],
> :].index[0]
> return y
>
> def inverted(self):
> return Scaler.Transform(self.df)
>
> matplotlib.scale.register_scale(Scaler)
>
> 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)})
> fig = plt.figure()
> ax = fig.add_subplot(1, 1, 1)
> ax.plot(df.index, df['y'])
> ax.set_xlim([df.index[0], df.index[-1]])
> ax.set_xscale('scaler', df=df)
>
> Cheers
> Konstantin
>
> --
> To send me an encrypted email, download my public key from pgp.mit.edu
> _______________________________________________
> Matplotlib-users mailing list
> Matplotlib-users at python.org
> https://mail.python.org/mailman/listinfo/matplotlib-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20190321/7f6ed6ee/attachment.html>
More information about the Matplotlib-users
mailing list