2

I'm having this problem with matplotlib where I'm unable to hide automatically generated, undesired xlabels that seemingly come out of nowhere.

Here is my code (reduced actual code in this minimal example which reproduces the problem):

import datetime as dt
import matplotlib.pyplot as plt
import pandas as pd

data = pd.read_csv("TestData.csv")

# Convert string to pd.Timestamp
data["datetime"] = data["datetime"].transform(lambda d:
                              dt.datetime.strptime(d, '%Y-%m-%d %H:%M:%S'))
data.set_index("datetime")

# start and end dates of data in CSV
max_date = data["datetime"].max()
min_date = data["datetime"].min()

# make figure
fig = plt.figure(figsize=[24, 12], dpi=300)

ax = data.plot.line(x="datetime",
                    y="totalAIAD",
                    color="xkcd:aquamarine")

delta = max_date - min_date  # total span of time for the report

time_unit = pd.to_timedelta(5, unit='m')  # 5 minute ticks
floor_unit = 'T'

# floor for cleaner ticks
date = min_date.floor(floor_unit)
xticks = []

while date + time_unit <= max_date:
    date = date + time_unit  # create date ticks for x axis

    xticks.append(date)

ax.axes.set_xticks(xticks)

str_xticks = []
for xtick in xticks:
    str_xticks.append(xtick.strftime('%H:%M:%S'))

ax.axes.set_xticklabels(str_xticks, rotation=45, ha='right')

#plt.suptitle("Stream Processor and Anomaly Detector Execution times")
#plt.title("From " + min_date.strftime('%Y-%m-%d %H:%M:%S') + " to "
#          + max_date.strftime('%Y-%m-%d %H:%M:%S'))

plt.xlabel(
    "Time",
    fontdict={
        'family': 'monospace',
        'color': 'xkcd:black',
        'weight': 'normal',
        'size': 14,
    },
    labelpad=6
)
plt.ylabel(
    "Delays (ms)",
    fontdict={
        'family': 'monospace',
        'color': 'xkcd:black',
        'weight': 'normal',
        'size': 14,
    },
    labelpad=6
)

[t.set_color('red') for t in ax.xaxis.get_ticklines()]
[t.set_color('red') for t in ax.xaxis.get_ticklabels()]

# vertical range for values (0 to configured MAX_Y)
plt.ylim((0, 50))

plt.grid(True, axis='y', alpha=0.5)

plt.axhline(y=10, color='r', linestyle='-')

plt.show()
plt.close(fig)

This requires a TestData.csv: https://www.toptal.com/developers/hastebin/uqepujujav.apache

And it produces the following output:

enter image description here

As you can see only the xtick labels that I'm setting in my code are red. The undesired ones (11:30, 11:45 and 12:15) are unaffected by color. They are also unaffected by all attempts I had of removing them. (I tried ax.set_xticks([]), ax.set_xticklabels([]), plt.xticks([]), ax.xaxis.set_major_locator(ticker.NullLocator())).

Any idea? I'm losing hope I'll ever get rid of those labels.

Winter
  • 3,894
  • 7
  • 24
  • 56

1 Answers1

1

By using pandas plotting with dates as x-axis, automatically the AutoDateLocator and AutoDateFormatter are set. These set their own ticks, independent from set_xticks (and also calculate new ticks when you're zooming). See also matplotlib's date tick tutorial.

As indicated in e.g. Matplotlib DateFormatter for axis label not working, pandas plotting doesn't work easy with customized date formatting. You could directly plot via matplotlib. Here is a possible adaption for the code:

import matplotlib.pyplot as plt
from matplotlib.dates import MinuteLocator, ConciseDateFormatter
import pandas as pd
import datetime as dt

data = pd.read_csv('TestData.csv', parse_dates=True)
data["datetime"] = data["datetime"].transform(lambda d: dt.datetime.strptime(d, '%Y-%m-%d %H:%M:%S'))
data.set_index("datetime")

fig, ax = plt.subplots(figsize=[24, 12], dpi=300)
ax.plot("datetime", "totalAIAD", data=data, color="xkcd:aquamarine")

locator = MinuteLocator(interval=5)
formatter = ConciseDateFormatter(locator)
formatter.formats = ['%y',  # ticks are mostly years
                     '%b',  # ticks are mostly months
                     '%d',  # ticks are mostly days
                     '%H:%M',  # hrs
                     '%H:%M:%S',  # min
                     '%S.%f', ]  # secs
formatter.zero_formats = [''] * len(formatter.formats)
formatter.offset_formats = [''] * len(formatter.formats) # this would write out the current date at the lower right
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)

ax.set_xlabel("Time", fontdict={'family': 'monospace', 'color': 'xkcd:black', 'weight': 'normal', 'size': 14},
              labelpad=6)
ax.set_ylabel("Delays (ms)", fontdict={'family': 'monospace', 'color': 'xkcd:black', 'weight': 'normal', 'size': 14},
              labelpad=6)

[t.set_color('red') for t in ax.xaxis.get_ticklines()]
[t.set_color('red') for t in ax.xaxis.get_ticklabels()]

ax.set_ylim((0, 50))
ax.margins(x=0)
ax.grid(True, axis='y', alpha=0.5)
ax.axhline(y=10, color='r', linestyle='-')
fig.autofmt_xdate() # automatically makes the x-labels rotate
plt.tight_layout()
plt.show()

timestamps formatted per 5 minutes

JohanC
  • 71,591
  • 8
  • 33
  • 66