【问题标题】:Matplotlib: Show all dates on custom formatted x axisMatplotlib:在自定义格式的 x 轴上显示所有日期
【发布时间】:2020-12-10 10:28:26
【问题描述】:

使用 matplotlib 我想创建一个图表,其中我的数据框 (df) 中的所有日期值都显示在 x 轴上。原则上,代码行plt.gca().xaxis.set_major_locator(matplotlib.dates.DayLocator(interval=1)) 应该可以完成这项工作,但它没有,可能是因为我使用的是自定义格式化程序!?这里需要自定义格式化程序,因为我会阻止 matplotlib 插入不属于我的数据框 (have a look at this question here) 的周末日期值。

我还想使用日期格式“%d.%m.%Y”。

虽然代码适用于 matplotlib 3.3.3/Python 3.8,但我的项目必须使用 matplotlib 3.2.2/Python 3.6,在这些情况下,代码不会返回所需的输出

代码如下:

import matplotlib
import matplotlib.pyplot as plt
from matplotlib.ticker import Formatter
import pandas as pd
import numpy as np


df = pd.DataFrame(data={"col1": [1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979,
                                 1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979],
                        "date": ['2018-01-08', '2018-01-09', '2018-01-10', '2018-01-11', '2018-01-12',
                                 '2018-01-15', '2018-01-16', '2018-01-17', '2018-01-18', '2018-01-19',
                                 '2018-01-22', '2018-01-23', '2018-01-24', '2018-01-25',]})
df["date"] = pd.to_datetime(df["date"])

class CustomFormatter(Formatter):

    def __init__(self, dates, fmt='%d.%m.%Y'):
        self.dates = dates
        self.fmt = fmt

    def __call__(self, x, pos=0):
        'Return the label for time x at position pos'
        ind = int(np.round(x))
        if ind >= len(self.dates) or ind < 0:
            return ''

        return self.dates[ind].strftime(self.fmt)

fig = plt.figure()
plt.gca().xaxis.set_major_formatter(CustomFormatter(df["date"]))
plt.plot(np.arange(df.shape[0]), df["col1"])
plt.gcf().autofmt_xdate()
#plt.gca().xaxis.set_major_locator(matplotlib.dates.DayLocator(interval=1)) # <-- this line should do the job, in theory!

使用 matplotlib 3.2.2 输出

预期输出(matplotlib 3.3.3)

感谢您的帮助!

【问题讨论】:

    标签: python-3.x pandas matplotlib


    【解决方案1】:

    我认为您应该只使用日期工具,但请确保您实际上是在绘制日期,而不是 np.arange(df.shape[0])

    import matplotlib
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    
    
    df = pd.DataFrame(data={"col1": [1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979,
                                     1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979],
                            "date": ['2018-01-08', '2018-01-09', '2018-01-10', '2018-01-11', '2018-01-12',
                                     '2018-01-15', '2018-01-16', '2018-01-17', '2018-01-18', '2018-01-19',
                                     '2018-01-22', '2018-01-23', '2018-01-24', '2018-01-25',]})
    df["date"] = pd.to_datetime(df["date"])
    
    fig, ax = plt.subplots()
    ax.xaxis.set_major_locator(matplotlib.dates.DayLocator(interval=1))
    ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%d.%m.%Y'))
    
    ax.plot(df["date"], df["col1"])
    fig.autofmt_xdate()
    plt.show()
    

    【讨论】:

    • 嘿乔迪,如果情节没有插入周末日期,这将是最简单的答案。
    • 啊,我明白了。如果我这样做,我会制作一个跳过周末的新音阶,但这有点复杂。
    【解决方案2】:

    由于您不是针对日期而是针对索引进行绘图,因此DateLocator 可能是这里的错误选择:

    import matplotlib
    import matplotlib.pyplot as plt
    from matplotlib.ticker import Formatter, FixedLocator
    import pandas as pd
    import numpy as np
    
    
    df = pd.DataFrame(data={"col1": [1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979,
                                     1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979],
                            "date": ['2018-01-08', '2018-01-09', '2018-01-10', '2018-01-11', '2018-01-12',
                                     '2018-01-15', '2018-01-16', '2018-01-17', '2018-01-18', '2018-01-19',
                                     '2018-01-22', '2018-01-23', '2018-01-24', '2018-01-25',]})
    
    df["date"] = pd.to_datetime(df["date"])
    
    
    class CustomFormatter(Formatter):
    
        def __init__(self, dates, fmt='%d.%m.%Y'):
            self.dates = dates
            self.fmt = fmt
    
        def __call__(self, x, pos=0):
            'Return the label for time x at position pos'
            ind = int(np.round(x))
            if ind >= len(self.dates) or ind < 0:
                return ''
    
            return self.dates[ind].strftime(self.fmt)
    
    fig = plt.figure()
    plt.plot(np.arange(df.shape[0]), df["col1"])
    plt.gca().xaxis.set_major_locator(FixedLocator(np.arange(df.shape[0])))
    plt.gca().xaxis.set_major_formatter(CustomFormatter(df["date"]))
    plt.gcf().autofmt_xdate()
    
    #print(matplotlib.__version__)
    plt.show()
    

    输出:

    但是由于无论如何您都针对索引进行绘图,因此您不需要类定义:

    import matplotlib
    import matplotlib.pyplot as plt
    from matplotlib.ticker import FixedLocator
    import pandas as pd
    import numpy as np
    
    
    df = pd.DataFrame(data={"col1": [1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979,
                                     1.000325, 1.000807, 1.001207, 1.000355, 1.001512, 1.003237, 1.000979],
                            "date": ['2018-01-08', '2018-01-09', '2018-01-10', '2018-01-11', '2018-01-12',
                                     '2018-01-15', '2018-01-16', '2018-01-17', '2018-01-18', '2018-01-19',
                                     '2018-01-22', '2018-01-23', '2018-01-24', '2018-01-25',]})
    
    df["date"] = pd.to_datetime(df["date"])
    
    
    fig = plt.figure()
    plt.plot(np.arange(df.shape[0]), df["col1"])
    plt.gca().xaxis.set_major_locator(FixedLocator(np.arange(df.shape[0])))
    plt.xticks(np.arange(df.shape[0]), df["date"].dt.strftime("%d.%m.%Y"))
    plt.gcf().autofmt_xdate()
    
    #print(matplotlib.__version__)
    plt.show()
    

    输出:见上文

    【讨论】:

    • 非常感谢您的精彩回答!很高兴看到我不必使用愚蠢的 CustomFormatter。是的,我知道这是一个非常专业的问题。但我必须使用 matplotlib 3.2.2,因为它是 fbs 包支持的版本(使用 pyinstaller)。
    • 评论不是给你的——当我最初运行你的代码时,我想知道你的问题是什么。所以,如果其他人读到它,我想确保这被理解为版本问题。我也会将此包含在您的问题中以强调这个问题。
    【解决方案3】:

    不涉及类的问题的可能解决方案是这样做(obs!所有导入可能都不是必需的):

    import matplotlib.pyplot as plt
    import matplotlib.colors as colors
    import matplotlib.ticker as ticker
    import seaborn as sns
    from IPython.display import display
    from mpl_toolkits.axes_grid1 import make_axes_locatable
    from matplotlib.colors import ListedColormap, LinearSegmentedColormap
    from matplotlib.lines import Line2D
    %matplotlib inline
    import matplotlib.pyplot as plt
    import matplotlib.gridspec as gridspec
    
    
    fig = plt.figure(figsize=[30,30])
    gs  = gridspec.GridSpec(100,100)
    ax21 = fig.add_subplot(gs[0:100,0:100])
    
    a = df['col1']
    ax21 = sns.lineplot(x=df['date'], y=df['col1'].values, palette="Reds", ax=ax21)
    ax21.set_xlabel('whatever', c='w', fontsize=16)
    ax21.set_ylabel('Other ever', c='w', fontsize=16)
    ax21.set_title('This is it', c='w', fontsize=20, weight = 'bold')
    
    plt.show()
    

    【讨论】:

    • 感谢您的回答!不幸的是,我不能在这个项目中使用 seaborn。我需要一个完全基于 matplotlib 的解决方案。
    猜你喜欢
    • 2019-09-29
    • 2017-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 2020-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多