Erlinska
Erlinska

Reputation: 433

Issue with matplotlib formatting using pandas

I'm looking for help on an issue I have with my graphs.

I'm trying to plot financial data contained inside a dataframe with dates and hours as index, however, matplotlib standard dates formatting doesn't do the trick here as I don't want evenly spread ticks on the x axis, as it shows big horizontal lines between business hours.

The solution I came up with is simply to plot this graph using np.arange on the x axis, and to use the index as the label on the x axis, thus not using matplotlib date formatting, but still displaying the dates on my graph.

My code is the following:

L = np.arange(len(DAX_M15.index))

def format_date(x, pos=None):
    return DAX_M15.index[x].strftime("%Y-%m-%d %H-%M-%S")

fig, ax = plt.subplots()
ax.plot(L, DAX_M15["Open"])
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
ax.set_title("Custom tick formatter")
fig.autofmt_xdate()

However, I get the following error when using this:

 File "D:\Anaconda\lib\site-packages\matplotlib\backends\backend_qt5agg.py", line 197, in __draw_idle_agg
    FigureCanvasAgg.draw(self)
  File "D:\Anaconda\lib\site-packages\matplotlib\backends\backend_agg.py", line 464, in draw
    self.figure.draw(self.renderer)
  File "D:\Anaconda\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "D:\Anaconda\lib\site-packages\matplotlib\figure.py", line 1144, in draw
    renderer, self, dsu, self.suppressComposite)
  File "D:\Anaconda\lib\site-packages\matplotlib\image.py", line 139, in _draw_list_compositing_images
    a.draw(renderer)
  File "D:\Anaconda\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "D:\Anaconda\lib\site-packages\matplotlib\axes\_base.py", line 2426, in draw
    mimage._draw_list_compositing_images(renderer, self, dsu)
  File "D:\Anaconda\lib\site-packages\matplotlib\image.py", line 139, in _draw_list_compositing_images
    a.draw(renderer)
  File "D:\Anaconda\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "D:\Anaconda\lib\site-packages\matplotlib\axis.py", line 1136, in draw
    ticks_to_draw = self._update_ticks(renderer)
  File "D:\Anaconda\lib\site-packages\matplotlib\axis.py", line 969, in _update_ticks
    tick_tups = [t for t in self.iter_ticks()]
  File "D:\Anaconda\lib\site-packages\matplotlib\axis.py", line 969, in <listcomp>
    tick_tups = [t for t in self.iter_ticks()]
  File "D:\Anaconda\lib\site-packages\matplotlib\axis.py", line 916, in iter_ticks
    for i, val in enumerate(majorLocs)]
  File "D:\Anaconda\lib\site-packages\matplotlib\axis.py", line 916, in <listcomp>
    for i, val in enumerate(majorLocs)]
  File "D:\Anaconda\lib\site-packages\matplotlib\ticker.py", line 386, in __call__
    return self.func(x, pos)
  File "D:/Finance python/test_data_formatter.py", line 40, in format_date
    return DAX_M15.index[x].strftime("%Y-%m-%d %H-%M-%S")
  File "D:\Anaconda\lib\site-packages\pandas\tseries\base.py", line 247, in __getitem__
    raise ValueError
ValueError

Would anyone have an idea on the issue here and how to solve it?

Thanks.

Upvotes: 2

Views: 480

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339340

As said in the comments, the problem is that the index of a list or series needs to be an integer between 0 and the length of the list. Something like DAX_M15.index[4.5] will not work.

In order to ensure that only those locations that have a datapoint associated with them are ticked, you may use a matplotlib.ticker.IndexLocator. E.g. if you want to label every fifth point of the list,

ax.xaxis.set_major_locator(ticker.IndexLocator(5,0))
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))

Inside the formatter you would also need to make sure the index is an integer and within the range of allowed values.

def format_date(x, pos=None):
    if int(x) >= len(df.index) or int(x) < 0: return ""
    return df.index[int(x)].strftime("%Y-%m-%d %H-%M-%S")

A complete example:

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
import pandas as pd

inx = pd.DatetimeIndex(start="2017-05-05", freq="7H", periods=45)
df = pd.DataFrame({"open" : np.random.rand(len(inx))}, index=inx)


L = np.arange(len(df.index))

def format_date(x, pos=None):
    return df.index[int(x)].strftime("%Y-%m-%d %H-%M-%S")

fig, ax = plt.subplots()
ax.plot(L, df["open"])
ax.xaxis.set_major_locator(ticker.IndexLocator(5,0))
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
ax.set_title("Custom tick formatter")
fig.autofmt_xdate()

plt.show()

enter image description here

Upvotes: 1

Related Questions