Search code examples
datetimematplotlibtimeunix-timestamp

"ValueError: year is out of range" when attempting to use matplotlib pyplot


I am attempting to get a matplotlib plotting function to be able to produce a graph with the x-axis set as a time axis. However, when I attempt to plot some values against UNIX times, I encounter the error ValueError: year is out of range. What is going wrong and how can it be addressed?

import os
import time

import matplotlib.dates
import matplotlib.pyplot
import shijian

def main():

    data = [
        [1484611200.0, 844.4333],
        [1484524800.0, 783.3373],
        [1484438400.0, 774.194 ],
        [1484352000.0, 769.2299]
    ]

    save_graph_matplotlib(
        values       = data,
        line         = True,
        line_width   = 0.5,
        title_axis_x = "time",
        title_axis_y = "value",
        #time_axis_x = True
    )

def save_graph_matplotlib(
    values              = None,
    title               = None,
    title_axis_x        = None,
    title_axis_y        = None,
    filename            = None,
    directory           = ".",
    overwrite           = True,
    color               = "black",
    LaTeX               = False,
    markers             = True,
    marker_size         = 1,
    aspect              = None,
    line                = False,
    line_style          = "-",
    line_width          = 0.2,
    font_size           = 20,
    scientific_notation = False,
    time_axis_x         = False
    ):

    # 1D or 2D data
    if isinstance(values[0], list):
        x = [element[0] for element in values]
        y = [element[1] for element in values]
    else:
        x = range(0, len(values))
        y = values

    matplotlib.pyplot.ioff()
    if LaTeX is True:
        matplotlib.pyplot.rc("text", usetex = True)
        matplotlib.pyplot.rc("font", family = "serif")
    if filename is None:
        if title is None:
            filename = "graph.png"
        else:
            filename = shijian.propose_filename(
                filename  = title + ".png",
                overwrite = overwrite
            )
    else:
        filename = shijian.propose_filename(
            filename  = filename,
            overwrite = overwrite
        )

    figure = matplotlib.pyplot.figure()

    if title is not None:
        figure.suptitle(
            title,
            fontsize = font_size
        )
    if markers is True:
        matplotlib.pyplot.scatter(
            x,
            y,
            s          = marker_size,
            c          = color,
            edgecolors = "none",
        )
    if line is True:
        matplotlib.pyplot.plot(
            x,
            y,
            line_style,
            c          = color,
            linewidth  = line_width
        )

    # Turn on or off axes scientific notation.
    if scientific_notation is False:
        matplotlib.pyplot.gca().get_xaxis().\
            get_major_formatter().set_scientific(False)
        matplotlib.pyplot.gca().get_yaxis().\
            get_major_formatter().set_scientific(False)
    # Set axes titles.
    if title_axis_x is not None:
        matplotlib.pyplot.xlabel(title_axis_x, fontsize = font_size)
    if title_axis_y is not None:
        matplotlib.pyplot.ylabel(title_axis_y, fontsize = font_size)
    # Set axes font size.
    matplotlib.pyplot.xticks(fontsize = font_size)
    matplotlib.pyplot.yticks(fontsize = font_size)
    # Set or do not set axis x as time.
    if time_axis_x:
        time_formatter = matplotlib.dates.DateFormatter("%Y-%m-%d")
        matplotlib.pyplot.axes().xaxis_date()
        matplotlib.pyplot.axes().xaxis.set_major_formatter(time_formatter)
        matplotlib.pyplot.xticks(rotation = -90)
    # Set the aspect ratio.
    if aspect is None:
        matplotlib.pyplot.axes().set_aspect(
            1 / matplotlib.pyplot.axes().get_data_ratio()
        )
    else:
        matplotlib.pyplot.axes().set_aspect(aspect)

    if not os.path.exists(directory):
        os.makedirs(directory)

    matplotlib.pyplot.savefig(
        directory + "/" + filename,
        dpi = 700
    )
    matplotlib.pyplot.close()

if __name__ == "__main__":
    main()

Solution

  • You need to convert your timestamp-like x data to a python datetime object, which can then be used in matplotlib and be understood by the matplotlib.dates.DateFormatter.

    This can be done using the datetime.datetime.fromtimestamp() method.

    import datetime
    import matplotlib.dates
    import matplotlib.pyplot as plt
    
    data = [
            [1484611200.0, 844.4333],
            [1484524800.0, 783.3373],
            [1484438400.0, 774.194 ],
            [1484352000.0, 769.2299]
        ]
    
    x = [datetime.datetime.fromtimestamp(element[0]) for element in data]
    y = [element[1] for element in data]
    
    plt.plot( x,  y,  ls="-",  c= "b",  linewidth  = 2 )
    plt.xlabel("Dates")
    
    time_formatter = matplotlib.dates.DateFormatter("%Y-%m-%d")
    plt.axes().xaxis.set_major_formatter(time_formatter)
    plt.axes().xaxis_date() # this is not actually necessary
    
    plt.show()