【问题标题】:How to show alternative calendar dates in mplfinance?如何在 mplfinance 中显示替代日历日期?
【发布时间】:2021-04-08 09:53:41
【问题描述】:

TL;DR - 问题

我有一个基于pandas 数据框的mplfinance 图,其中索引采用格鲁吉亚日历格式,我需要将它们显示为 Jalali 格式。

我的数据和代码

我的数据如下所示:

            open    high    low     close
date                                       
2021-03-15  67330.0 69200.0 66870.0 68720.0
2021-03-16  69190.0 71980.0 69000.0 71620.0
2021-03-17  72450.0 73170.0 71700.0 71820.0
2021-03-27  71970.0 73580.0 70000.0 73330.0
2021-03-28  73330.0 73570.0 71300.0 71850.0
...         ...     ...     ...     ...

第一列既是日期又是索引。这是mplfinance 正确绘制数据所必需的; 我可以用这样的东西来绘制:

import mplfinance as mpf
mpf.plot(chart_data.tail(7), figratio=(16,9), type="candle", style='yahoo', ylabel='', tight_layout=True, xrotation=90)

chart_data 是上面的数据,其余的几乎都是格式化的东西。

我现在拥有的

我的图表如下所示:

但是,我需要日期看起来像这样:1400-01-12。这是一个等价表,可以进一步证明我的情况。

2021-03-15  1399-12-25
2021-03-16  1399-12-26
2021-03-17  1399-12-27
2021-03-27  1400-01-07
2021-03-28  1400-01-08

我尝试过的

将 Jdates 设置为我的索引:

chart_data.index = history.jdate
mpf.plot(chart_data_j)

抛出此异常:

TypeError('Expect data.index as DatetimeIndex')

所以我尝试将 jdates 转换为 datetimes:

chart_data_j.index = pd.to_datetime(history.jdate)

抛出越界异常:

OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 1398-03-18 00:00:00

所以我可能会选择更改时区/语言环境,所以我尝试按照官方文档更改时区:

pd.to_datetime(history.date).tz_localize(tz='US/Eastern')

但我得到了这个例外:

raise TypeError(f"{ax_name} is not a valid DatetimeIndex or PeriodIndex")

最后我尝试使用诸如 PersianTools 和 pandas_jalali 之类的库无济于事。

【问题讨论】:

    标签: python-3.x pandas matplotlib jalali-calendar mplfinance


    【解决方案1】:

    您可以通过创建自己的自定义 DateFormatter 类并使用 mpf.plot() kwarg returnfig=True 访问 Axes 对象以安装您自己的自定义 DateFormatter 来实现此目的。

    我已经编写了一个自定义 DateFormatter(参见下面的代码)知道在show_nontrading=False 时 MPLfinance 处理 x 轴的特殊方式(即默认值)。

    import pandas as pd
    import mplfinance as mpf
    import jdatetime as jd
    import matplotlib.dates as mdates
    
    from matplotlib.ticker import Formatter
    class JalaliDateTimeFormatter(Formatter):
        """
        Formatter for JalaliDate in mplfinance.
        Handles both `show_nontrading=False` and `show_nontrading=True`.
        When show_nonntrading=False, then the x-axis is indexed by an
        integer representing the row number in the dataframe, thus:
        Formatter for axis that is indexed by integer, where the integers
        represent the index location of the datetime object that should be
        formatted at that lcoation.  This formatter is used typically when
        plotting datetime on an axis but the user does NOT want to see gaps
        where days (or times) are missing.  To use: plot the data against
        a range of integers equal in length to the array of datetimes that
        you would otherwise plot on that axis.  Construct this formatter
        by providing the arrange of datetimes (as matplotlib floats). When
        the formatter receives an integer in the range, it will look up the
        datetime and format it.
    
        """
        def __init__(self, dates=None, fmt='%b %d, %H:%M', show_nontrading=False):
            self.dates = dates
            self.len   = len(dates) if dates is not None else 0
            self.fmt   = fmt
            self.snt   = show_nontrading
    
        def __call__(self, x, pos=0):
            '''
            Return label for time x at position pos
            '''
            if self.snt:
                jdate = jd.date.fromgregorian(date=mdates.num2date(x))
                formatted_date = jdate.strftime(self.fmt)
                return formatted_date
    
            ix = int(round(x,0))
    
            if ix >= self.len or ix < 0:
                date = None
                formatted_date = ''
            else:
                date = self.dates[ix]
                jdate = jd.date.fromgregorian(date=mdates.num2date(date))
                formatted_date = jdate.strftime(self.fmt)
            return formatted_date
    
    #  ---------------------------------------------------
    
    df = pd.read_csv('so_67001540.csv',index_col=0,parse_dates=True)
    
    mpf.plot(df,figratio=(16,9),type="candle",style='yahoo',ylabel='',xrotation=90)
    
    dates     = [mdates.date2num(d) for d in df.index]
    formatter = JalaliDateTimeFormatter(dates=dates,fmt='%Y-%m-%d')
    
    fig, axlist = mpf.plot(df,figratio=(16,9), 
                           type="candle",style='yahoo',
                           ylabel='',xrotation=90,
                           returnfig=True)
    
    axlist[0].xaxis.set_major_formatter(formatter)
    mpf.show()
    
    • 文件'so_67001540.csv' 如下所示:
    date,open,high,low,close,alt_date
    2021-03-15,67330.0,69200.0,66870.0,68720.0,1399-12-25
    2021-03-16,69190.0,71980.0,69000.0,71620.0,1399-12-26
    2021-03-17,72450.0,73170.0,71700.0,71820.0,1399-12-27
    2021-03-27,71970.0,73580.0,70000.0,73330.0,1400-01-07
    2021-03-28,73330.0,73570.0,71300.0,71850.0,1400-01-08
    
    • 当您运行上述脚本时,您应该得到以下两个图:

    【讨论】:

    • 我渴望接近您的知识水平。这就像一个魅力!
    • @M.H.Tajaddini -- 很高兴我能帮上忙。这是一个非常有趣的谜题。
    【解决方案2】:

    您是否尝试过制作这些日期

    1399-12-25
    1399-12-26
    1399-12-27
    1400-01-07
    1400-01-08
    

    数据框的索引(也许这就是“交换索引”的意思?) set kwarg datetime_format='%Y-%m-%d' ?

    我认为应该可以。


    更新:

    在我看来问题在于

    • mplfinace 需要 Pandas.DatetimeIndex 作为数据框的索引,
    • Pandas.DatetimeIndex 由 Pandas.Timestamp 对象组成,
    • Pandas.Timestamp 有限制,排除年份小于 1677 的日期:
    In [1]: import pandas as pd
    
    In [2]: pd.Timestamp.max
    Out[2]: Timestamp('2262-04-11 23:47:16.854775807')
    
    In [3]: pd.Timestamp.min
    Out[3]: Timestamp('1677-09-21 00:12:43.145225')
    

    我会四处寻找,看看是否可以提出另一个解决方案。
    Matplotlib 内部日期可以到零年。

    【讨论】:

    • 我编辑了我的帖子,以包括我在使用这个想法时遇到的异常以及更多。
    • @M.H.Tajaddini 谢谢。我已更新我的答案,以包含有关可能导致此困难的原因的更多信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    • 2013-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    相关资源
    最近更新 更多