GeezerVison is showing github.com — [Bug]: Plotting of Pandas DataFrame data with nanosecond timestamps · Issue #23493 · matplotlib/matplotlib
[Bug]: Plotting of Pandas DataFrame data with nanosecond timestamps #23493DescriptionBug summary
When plotting time-series data from a high-frequency data source, the plot looks distorted when the values are plotted against a nanosecond timestamp.
I am working in PyCharm 2021.2.2 Community Edition, without Jupiter notebook
Code for reproduction
import pandas as pd
from pandas import DataFrame
from matplotlib import pyplot as plt
if __name__ == '__main__':
data = DataFrame(pd.read_hdf('YRT1DT2F_rawdata_series_2022-05-10_0h-6h.h5', 'YRT1DT2F'))
data = data[pd.to_datetime('2022-05-10 04:28:32.573995', utc=False, unit='ns'):pd.to_datetime('2022-05-10 04:28:32.574', utc=False, unit='ns')]
print(data)
plt.plot(data['value']) # produces bad output
plt.show()
plt.plot(data['value'].values) # produces good output
plt.show()
Actual outcome
Plot result of data['values'].values

Plot result of data['values']

Expected outcome
I would expect the plot result to lookalike "good plot", but with time-scaled x-Axis
Additional information
No response
Operating system
MacOS 12.4 (Monterey)
Matplotlib Version
3.5.2
Matplotlib Backend
MacOSX
Python version
3.9.13
Jupyter version
No response
Installation
pip Activitywolfig commented on Jul 26, 2022 As it seems, matplotlib cannot deal with nanosecond timestamps jklymak commented on Jul 26, 2022 Can you provide a self contained example that doesn't involve pandas, otherwise please open an issue with them. Thanks. jklymak commented on Jul 26, 2022 However, I agree that just using Matplotlib does not give optimal results either:
from matplotlib.colors import Normalize
import matplotlib.pyplot as plt
import numpy as np
times = (np.array([5, 7, 15, 23, 68], dtype='timedelta64[ns]') +
np.datetime64('2022-07-26T04:50:20', 'ns'))
fig, ax = plt.subplots()
ax.plot(times, np.array([5, 7, 15, 23, 68]))
plt.show()
I think there have been attempts to fix this in the past, but it usually bumps up against the fact that nano-seconds are probably best plotted as deltas from a starting time. github-actions commented on Jan 5, 2026 This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help! timhoffm commented on Jan 12, 2026 @jklymak's plot gives:
The issue here is the limited resolution of our internal date representation as float seconds since the epoch:
In [10]: import matplotlib.dates as mdates
In [11]: mdates.date2num(times)
Out[11]:
array([19199.20162037, 19199.20162037, 19199.20162037, 19199.20162037,
19199.20162037])
IMHO, this is not reasonably fixable. While you could adjust the epoch to the day (maybe even hour) of your data, this is cumbersome. Additionally, the formatted dates are not trivial too. A naive full 2022-05-10 04:28:32.573995 is typically not a good label. It's very large and you need to rotate to get a reasonable amout of non-overlapping labels. Shortening would be possible, YMMV - do you strip the date, or possibly also hours, minutes, ...)?
Overall I think it's not worth the effort. We probably should add a documentation note to AutoDateLocator on the precision limits. Maybe we can also warn if AutoDateLocator is used on a very small data range - or rather in date2num if there's an array of inputs with a very small time range. Semi-related: #22829 (comment)
For anybody, who wants to look into this. The code is:
|
def _dt64_to_ordinalf(d): |
|
""" |
|
Convert `numpy.datetime64` or an `numpy.ndarray` of those types to |
|
Gregorian date as UTC float relative to the epoch (see `.get_epoch`). |
|
Roundoff is float64 precision. Practically: microseconds for dates |
|
between 290301 BC, 294241 AD, milliseconds for larger dates |
|
(see `numpy.datetime64`). |
|
""" |
|
|
|
# the "extra" ensures that we at least allow the dynamic range out to |
|
# seconds. That should get out to +/-2e11 years. |
|
dseconds = d.astype('datetime64[s]') |
|
extra = (d - dseconds).astype('timedelta64[ns]') |
|
t0 = np.datetime64(get_epoch(), 's') |
|
dt = (dseconds - t0).astype(np.float64) |
|
dt += extra.astype(np.float64) / 1.0e9 |
|
dt = dt / SEC_PER_DAY |
|
|
|
NaT_int = np.datetime64('NaT').astype(np.int64) |
|
d_int = d.astype(np.int64) |
|
dt[d_int == NaT_int] = np.nan |
|
return dt |
vineoy commented on Nov 2, 2026 I've opened PR #32566 for this. It adds a note to the AutoDateLocator docstring on the precision limits, with pointers to the elapsed timedelta64 and set_epoch workarounds discussed here.
Happy to adjust the wording or placement if needed.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|