Plotly绘制金融图表——时间系列和日期坐标轴
本文翻译自Plotly官方文档 Time Series and Date Axes in Python
使用日期类型坐标轴的时间系列
Plotly中的一些金融表格默认使用日期坐标轴,比如:蜡烛图和OHLC图。
如果对应数据是ISO格式的日期字符串、pandas的日期列或者NumPy的日期数组,Plotly会自动将坐标轴设置为日期类型。
可以使用plotly.express或者plotly.graph_objects方法来绘制图表。
# 使用plotly.express
import plotly.express as px
df = px.data.stocks() # plotly自带的示例数据
fig = px.line(df, x='date', y="GOOG")
fig.show()

# 使用 graph_objects
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('...')
fig = go.Figure([go.Scatter(x=df['Date'], y=df['AAPL.High'])])
fig.show()

绘制出来的图表的横坐标为英语格式的日期,例如"Apr 2025"。
日期坐标轴上的不同类型的图表
任何绘制在笛卡尔坐标系上的图表都可以放在日期坐标轴上,比如柱状图:
import plotly.express as px
df = px.data.stocks(indexed=True)-1
fig = px.bar(df, x=df.index, y="GOOG")
fig.show()

或者分面图:
import plotly.express as px
df = px.data.stocks(indexed=True)-1
fig = px.area(df, facet_col="company", facet_col_wrap=2)
fig.show()

配置刻度标签
在默认设置下,刻度标签是和网格线一起的。刻度标签代表一个时间点。刻度标签可以使用tickformat属性(接受d3时间格式)来进行格式化,从而只显示月和年,但是它们还是默认代表一个时间点,所以在下面的图表中,"Feb 2018"的刻度标签覆盖了一部分1月份和一部分2月份的日期区间。dtick属性控制网格线之间的间距,M1表示1个月。这个属性也接受毫秒时间戳,可以通过将某天的日期转化为毫秒时间戳。
在日期轴的刻度标签中,tickformat属性中的第一个换行符'\n'后面的部分都会出现在第二行,而且只会显示一次,就像下面的例子中一样。如果想要下图中的年份在每个标签中都出现,那就要用'<br>'代替'\n'。
注意在默认情况下,悬停标签中的X和Y值是和对应坐标轴上的标签坐标保持一致的。所以,当刻度坐标被设置得非常宽泛,比如以月为周期,通常需要将悬停标签设置回实际的日期周期,比如天,就像下面一样:
import plotly.express as px
df = px.data.stocks()
fig = px.line(df, x="date", y=df.columns,
hover_data={"date": "|%B %d, %Y"},
title='custom tick labels')
fig.update_xaxes(
dtick="M1",
tickformat="%b\n%Y")
fig.show()

将刻度标签移到区间的中间
4.10版本中的新功能
通过将ticklabelmode属性设置为"period"(默认为"instant"),我们可以将刻度标签移到它所代表的区间的中间。网格线会留在每个月的开头(因为dtick="M1"属性),但是标签现在覆盖它代表的整个月。
import plotly.express as px
df = px.data.stocks()
fig = px.line(df, x="date", y=df.columns,
hover_data={"data": "|%B %d, %Y},
title='custom tick labels with ticklabelmode="period"')
fig.update_xaxes(
dtick="M1",
tickformat=%b\n%Y",
ticklabelmode="period")
fig.show()

添加次刻度
5.8版本中的新功能
你可以通过minor给坐标轴加入次刻度。这个属性接收一个dict来将特征应用于次刻度。
在下面的例子中,我们在x轴里面添加了次刻度,并开启了次网格线。
import pandas as pd
import plotly.express as px
df = px.data.stocks()
fig = px.line(df, x='date', y="GOOG")
fig.update_xaxes(minor=dict(ticks="inside", showgrid=True))
fig.show()

带周度次刻度的月度标签
5.8版本中的新功能
可以设置minor中的dtick来控制次刻度和网格线的间距。在下面的例子中,通过设置dtick=7*24*60*1000(一周的毫秒数)以及tick0="2016-07-03"(数据中的第一个周日),图中每周的开头就会有一个次刻度和网格线。放大图片的时候,我们就可以清楚地看到每个月和每周的开始和结束。
import pandas as pd
import plotly.express as px
df = pd.read_csv('...')
df = df.loc[(df["Date"] >= "2016-07-01") & (df["Date"] <= "2016-12-01")]
fig = px.line(df, x='Date', y='AAPL.High')
fig.update_xaxes(ticks="outside",
ticklabelmode="period",
tickcolor="black",
ticklen=10,
minor=dict(
ticklen=4,
dtick=7*24*60*60*1000,
tick0="2016-07-03",
griddash='dot',
gridcolor='white'))
fig.show()

用直方图总结时间系列数据
Plotly的直方图是非常有用的数据聚合工具,它在日期坐标轴中也可以使用。在下面的图片中,我们通过设置histfunc="avg"和xbins_size="M1",将传入的日度数据显示为月度平均值:
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('...')
fig = px.histogram(df, x="Date", y="APPL.Close", histfunc="avg", title="Histogram on Date Axes")
fig.update_traces(xbins_size="M1")
fig.update_xaxes(showgrid=True, ticklabelmode="period", dtick="M1", tickformat="%b\n%Y")
fid.update_layout(bargap=0.1)
fig.add_trace(go.Scatter(mode="markers", x=df["Date"], y=df["AAPL.Close"], name="daily"))
fig.show()

展示周期数据
4.11版本中的新功能
如果你的数据对应的日期是“1月1日”或者“1月31日”,但实际上它代表的是整个1月份的数据,你可以通过设置xperiod和xperiodalignment属性来将数据点显示在该月的开头、结尾或中间。在下面的例子中,原始数据被标上的都是每个月的10日的日期,但是通过xperiod=M1被绘制成月度的柱状图,并且分别在月份的开头、中间和结尾显示。
import plotly.graph_objects as go
import pandas as pd
df = pd.DataFrame(dict(
date=["2020-01-10", "2020-02-10", "2020-03-10", "2020-04-10", "2020-05-10", "2020-06-10"],
value=[1, 2, 3, 1, 2, 3]))
fig = go.Figure()
fig.add_trace(go.Scatter(
name="Raw Data",
mode="markers+lines", x=df["date"], y=df["value"],
marker_symbol="star"))
fig.add_trace(go.Scatter(
name="Start-aligned",
mode="markers+lines", x=df["date"], y=df["value"],
xperiod="M1",
xperiodalignment="start"))
fig.add_trace(go.Scatter(
name="Middle-aligned",
mode="markers+lines", x=df["date"], y=df["value"],
xperiod="M1",
xperiodalignment="middle"))
fig.add_trace(go.Scatter(
name="End-aligned",
mode="markers+lines", x=df["date"], y=df["value"],
xperiod="M1",
xperiodalignment="end"))
fig.add_trace(go.Bar(
name="Middle-aligned",
x=df["date"], y=df["value"],
xperiod="M1",
xperiodalignment="middle"))
fig.update_xaex(showgrid=True, ticklabelmode="period")
fig.show()

混合周期数据的悬停模板
5.0版本中的新功能
当用x或x unified悬停模式以及使用hovertemplate展示混合周期的周期数据时,可以使用xhoverformat属性来控制每个周期的X值是如何展示的。%{xother}悬停模板特殊指令可以用来控制那些不与被悬停的数据点共享X轴的数据点的X值。当被悬停的数据点的X值与X轴一致时,%{xother}会返回一个空字符串;否则就会返回(%{x})。特殊指令变体%{_xother}、%{xother_}以及%{_xother_}分别会在括号的前面、后面或两边显示空格。
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Bar(
x=["2020-01-01", "2020-04-01", "2020-07-01"],
y=[1000, 1500, 1700],
xperiod="M3",
xperiodalignment="middle",
xhoverformat="Q%q".
hovertemplate="%{y}%{_xother}"))
fig.add_trace(go.Scatter(
x=["2020-01-01", "2020-02-01", "2020-03-01",
"2020-04-01", "2020-05-01", "2020-06-01",
"2020-07-01", "2020-08-01", "2020-09-01"],
y=[1100, 1050, 1200, 1300, 1400, 1700, 1500, 1400, 1600],
xperiod="M1",
xperiodalignment="middle",
hovertemplate="%{y}%{_xother}"))
fig.update_layout(hovermode="x unified")
fig.show()

自定义日期区间的日期系列图
日期区间可以通过手动设置datetime.datetime对象或者日期字符串来控制。
# 使用 plotly.express
import plotly.express as px
import pandas as pd
df = pd.read_csv('...')
fig = px.line(df, x='Date', y='AAPL.High', range_x=['2016-07-01','2016-12-31'])
fig.show()

# 使用graph_objects
import plotly.graph_objects as go
import datetime
x = [datetime.datetime(year=2013, month=10, day=4),
datetime.datetime(year=2013, month=11, day=5),
datetime.datetime(year=2013, month=12, day=6)]
fig = go.Figure(data=[go.Scatter(x=x, y=[1, 3, 6])])
# 使用datetime对象来设置日期区间
fig.update_layout(xaxis_range=[datetime.datetime(2013, 10, 17),
datetime.datetime(2013, 11, 20)])
fig.show()

import plotly.express as px
import pandas as pd
df = pd.read_csv('...')
fig = px.line(df, x='Date', y='AAPL.High')
# Use date string to set xaxis range
fig.update_layout(xaxis_range=['2016-07-01','2016-12-31'],
title_text="Manually Set Date Range")
fig.show()

时间系列的区间滑块
区间滑块是图表下方类似于小子图的区域,允许用户缩放X轴的范围,同时还能保留图表的完整预览图。
import plotly.express as px
import pandas as pd
df = pd.read_csv('...')
fig = px.line(df, x='Date', y='AAPL.High', title='Time Series with Rangeslider')
fig.update_xaxes(rangeslider_visible=True)
fig.show()

时间系列的区间选择按钮
区间选择按钮是能够和时间系列图表以及区间滑块很好地协同的特殊控件,允许用户轻松地设置X轴的范围。
import plotly.express as px
import pandas as pd
df = pd.read_csv('...')
fig = px.line(df, x='Date', y='AAPL.High', title='Time Series with Range Slider and Selectors')
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label="1m", step="month", stepmode="backward"),
dict(count=6, label="6m", step="month", stepmode="backward"),
dict(count=1, label="YTD", step="year", stepmode="todate"),
dict(count=1, label="1y", step="year", stepmode="backward"),
dict(step="all")
])
)
)
fig.show()

通过缩放等级自定义刻度标签格式
tickformatstops属性可以用来自定义自适应缩放等级的刻度标签的格式。随着X轴的缩放,刻度标签会自动调整格式,比如从以天为单位变成以小时为单位。
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('...')
fig = go.Figure(go.Scatter(
x = df['Date'],
y = df['mavg']
))
fig.update_xaxes(
rangeslider_visible=True,
tickformatstops = [
dict(dtickrange=[None, 1000], value="%H:%M:%S.%L ms"),
dict(dtickrange=[1000, 60000], value="%H:%M:%S s"),
dict(dtickrange=[60000, 3600000], value="%H:%M m"),
dict(dtickrange=[3600000, 86400000], value="%H:%M h"),
dict(dtickrange=[86400000, 604800000], value="%e. %b d"),
dict(dtickrange=[604800000, "M1"], value="%e. %b w"),
dict(dtickrange=["M1", "M12"], value="%b '%y M"),
dict(dtickrange=["M12", None], value="%Y Y")
]
)
fig.show()

隐藏周末和假期
rangebreaks属性可以用在date类型的坐标轴上,来隐藏特定的时间。下面的例子中,我们展示了2个图表:一个以默认的模式来展示数据中的间距;另一个隐藏了周末和假期,从而展现了连续不断的交易历史。注意1月4日和12月21日之间的网格线的间距,这段时间的假期被移除了。
注意:这个特征的一个显著的限制就是它不支持
scattergl系列。当将这个特征适用于超过几百个数据点的px.scatter或px.line或px.area时,你可能需要传入render_mode="svg"来保证底层的系列类型是scatter,而不是scattergl。
import plotly.express as px
import pandas as pd
df = pd.read_csv('...')
fig = px.scatter(df, x='Date', y='AAPL.High', range_x=['2015-12-01', '2016-01-15'],
title="Default Display with Gaps")
fig.show()

import plotly.express as px
import pandas as pd
df = pd.read_csv('...')
fig = px.scatter(df, x='Date', y='AAPL.High', range_x=['2015-12-01', '2016-01-15'],
title="Hide Weekend and Holiday Gaps with rangebreaks")
fig.update_xaxes(
rangebreaks=[
dict(bounds=["sat", "mon"]), # 隐藏周末
dict(values=["2015-12-25", "2016-01-01"]) # 隐藏圣诞节和元旦节
]
)
fig.show()

隐藏非交易时间
上面描述的rangebreaks特征也可以用来隐藏以小时为单位的周期。
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(1)
work_week_40h = pd.date_range(start='2020-03-01', end='2020-03-07', freq="BH")
df = pd.DataFrame(dict(
date = work_week_40h,
value = np.cumsum(np.random.rand(40)-0.5)
))
fig = px.scatter(df, x="date", y="value",
title="Default Display with Gaps")
fig.show()

import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(1)
work_week_40h = pd.date_range(start='2020-03-01', end='2020-03-07', freq="BH")
df = pd.DataFrame(dict(
date = work_week_40h,
value = np.cumsum(np.random.rand(40)-0.5)
))
fig = px.scatter(df, x="date", y="value",
title="Hide Non-Business Hour Gaps with rangebreaks")
fig.update_xaxes(
rangebreaks=[
dict(bounds=[17, 9], pattern="hour"), # 隐藏9am-5pm之外的时间
]
)
fig.show()

- 点赞
- 收藏
- 关注作者
评论(0)