matplotlib.finance独立出来成为mpl_finance,而mpl_finance中的candlestick_ochl和candlestick_ohlc一般用来画股票的K线图。我需要分析分时图,也就是一分钟的行情,这个时候就不能直接用candlestick_ochl函数,因为candlestick_ochl中x轴最小的单位是日期,不是分钟。
经过对mpl_finance的源代码进行分析,问题在于matplotlib的date2num将日期转换为浮点数,浮点数的整数部分表示日期,小数部分代表小时和分钟。比如下面4个时间段是连续的分钟。
时间
date2num之后
乘以1440
2018/09/17-21:34
736954.8986
1061215054
2018/09/17-21:35
736954.8993
1061215055
2018/09/17-21:36
736954.9000
1061215056
2018/09/17-21:37
736954.9007
1061215057
可以看出date2num函数计算之后,4个时间的整数部分都是736954,导致在X轴上这4个时间段都重叠在一起,无法区分了。要达到的效果是每一个分钟也能成为一个整数,这样就可以显示出来了。那么一天是24小时,每小时60分钟,那么一天就是1440分钟,将date2num计算的浮点数乘以1440就可以将每一分钟转为整数,那么就可以在x轴上。
最后还需要对x轴格式化,因为自己对x轴进行了处理(乘以1440),采用默认的格式化是乱码。需要自定义x轴的格式化函数。
完整例子如下:
import pandas as pd
from pandas import DataFrame
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import mpl_finance as mpf
from matplotlib.ticker import Formatter
import numpy as np
dfcvs = DataFrame([
["2018/09/17-21:34", 3646, 3650,3644,3650],
["2018/09/17-21:35", 3650, 3650,3648,3648],
["2018/09/17-21:36", 3650, 3650,3648,3650],
["2018/09/17-21:37", 3652, 3654,3648,3652]
])
dfcvs.columns = ['时间','开盘','最高','最低','收盘']
dfcvs['时间']=pd.to_datetime(dfcvs['时间'],format="%Y/%m/%d-%H:%M")
#matplotlib的date2num将日期转换为浮点数,整数部分区分日期,小数区分小时和分钟
#因为小数太小了,需要将小时和分钟变成整数,需要乘以24(小时)×60(分钟)=1440,这样小时和分钟也能成为整数
#这样就可以一分钟就占一个位置
dfcvs['时间']=dfcvs['时间'].apply(lambda x:dates.date2num(x)*1440)
data_mat=dfcvs.as_matrix()
fig,ax=plt.subplots(figsize=(1200/72,480/72))
fig.subplots_adjust(bottom=0.1)
mpf.candlestick_ohlc(ax,data_mat,colordown='#53c156', colorup='#ff1717',width=0.2,alpha=1)
#将x轴的浮点数格式化成日期小时分钟
#默认的x轴格式化是日期被dates.date2num之后的浮点数,因为在上面乘以了1440,所以默认是错误的
#只能自己将浮点数格式化为日期时间分钟
#参考https://matplotlib.org/examples/pylab_examples/date_index_formatter.html
class MyFormatter(Formatter):
def __init__(self, dates, fmt='%Y%m%d %H:%M'):
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))
#ind就是x轴的刻度数值,不是日期的下标
return dates.num2date( ind/1440).strftime(self.fmt)
formatter = MyFormatter(data_mat[:,0])
ax.xaxis.set_major_formatter(formatter)
for label in ax.get_xticklabels():
label.set_rotation(90)
label.set_horizontalalignment('right')
plt.show()