MBasith MBasith - 3 years ago 338
Python Question

Matplotlib Pandas DateTime Frequency

I am attempting to plot some data using matplotlib and would like to reduce the number of DateTime x-axis ticks displayed. I was able to use plt.locator to reduce the number of bins by half but the datetime does not align with the bars.
Is there a way I can remedy this? I would like to have only 5 of the 10 ticks displayed. Where for example only the following datetimes are displayed under their respective plotted bars (2017-09-29 02:00, 2017-09-29 04:00, 2017-09-29 06:00, 2017-09-29 08:00, 2017-09-29 10:00).

Below is my reproducible code and the plotted output.

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame([{'DATETIME': '2017-09-29 01:00,', 'Population': 1000},
{'DATETIME': '2017-09-29 02:00,', 'Population': 3000},
{'DATETIME': '2017-09-29 03:00,', 'Population': 4000},
{'DATETIME': '2017-09-29 04:00,', 'Population': 5000},
{'DATETIME': '2017-09-29 05:00,', 'Population': 7000},
{'DATETIME': '2017-09-29 06:00,', 'Population': 6000},
{'DATETIME': '2017-09-29 07:00,', 'Population': 5000},
{'DATETIME': '2017-09-29 08:00,', 'Population': 4000},
{'DATETIME': '2017-09-29 09:00,', 'Population': 4000},
{'DATETIME': '2017-09-29 10:00,', 'Population': 4000}])

df.index = df['DATETIME']
df.index = (pd.to_datetime(df.index)).strftime("%m/%d %H:00")
df.Population.plot.bar()
plt.tick_params(axis='both', which='both', labelsize=7)
plt.locator_params(nbins=5)
plt.tight_layout()
plt.show()


enter image description here

Answer Source

Let me first explain why the method you chose setting nbins on the locator params fails, although in principle this method might be expected to do exactly what you want.
The bars in the plot are positionned at integer numeric locations 0,1,2,...N-1. Their labels are set as a fixed list of texts. Hence if you chose to only have 5 ticks, those ticks are correctly positionned, but the labels to those are taken to be the first 5 elements from the fixed label list. Therefore, the third bar has the label of the second, the fifth bar has the label of the third and so on.

Knowing this, we can play the trick of setting new ticks and ticklabels just at every second bar by slicing the current labels. You get the current tick positions and labels via plt.xticks(), which returns a tuple of ticks and labels. Slicing a list is easy via the :: operator,
e.g. [0,1,2,3,4,5][1::2] returns [1,3,5]. Hence:

plt.xticks(plt.xticks()[0][1::2], 
           plt.xticks()[1][1::2])

Complete example:

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame([{'DATETIME': '2017-09-29 01:00,', 'Population': 1000},
                   {'DATETIME': '2017-09-29 02:00,', 'Population': 3000},
                   {'DATETIME': '2017-09-29 03:00,', 'Population': 4000},
                   {'DATETIME': '2017-09-29 04:00,', 'Population': 5000},
                   {'DATETIME': '2017-09-29 05:00,', 'Population': 7000},
                   {'DATETIME': '2017-09-29 06:00,', 'Population': 6000},
                   {'DATETIME': '2017-09-29 07:00,', 'Population': 5000},
                   {'DATETIME': '2017-09-29 08:00,', 'Population': 4000},
                   {'DATETIME': '2017-09-29 09:00,', 'Population': 4000},
                   {'DATETIME': '2017-09-29 10:00,', 'Population': 4000}])

df.index = df['DATETIME']
df.index = (pd.to_datetime(df.index)).strftime("%m/%d %H:00")
df.Population.plot.bar()
plt.tick_params(axis='both', which='both', labelsize=7)

plt.xticks(plt.xticks()[0][1::2], 
           plt.xticks()[1][1::2])

plt.tight_layout()
plt.show()

enter image description here

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download