2

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.

2
  • Let's say matplotlib wants to place a tick at a value of 4.5 on the axes. Your code would then try to index the index as DAX_M15.index[4.5]. This throws an error, as floating point indexing is not defined. Similar, if a tick is located at a negative value, or a value above the length of the list. You may try to exclude those cases in the format_date function; but I'm not sure if this is what you want. Commented Jul 28, 2017 at 11:05
  • Thank you for the answer, I understand the issue now. I've tried to use the closest integer in the function, however it throws an out of bounds issue. Is there any way to force matplotlib to use integer index within the limits of my data without modifying my function? Commented Jul 28, 2017 at 11:30

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, it works perfectly, and most importantly I think I understand how matplotlib tickers work now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.