1

TL;DR - 问题

我有一个mplfinance基于数据框的图pandas,其中索引采用格鲁吉亚日历格式,我需要将它们显示为 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 等库无济于事。

4

2 回答 2

1

您可以通过创建自己的自定义 DateFormatter 类并使用mpf.plot()kwargreturnfig=True访问 Axes 对象来安装您自己的自定义 DateFormatter 来使其工作。

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

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
  • 当你运行上面的脚本时,你应该得到以下两个图:

在此处输入图像描述

在此处输入图像描述

于 2021-04-08T17:00:52.190 回答
1

你试过做这些日期吗

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

数据框的索引(也许这就是“交换索引”的意思?)设置 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 日期可以到零年。

于 2021-04-08T10:18:52.190 回答