Plotly绘制金融图表——时间系列和日期坐标轴

举报
发表于 2026/01/13 11:07:45 2026/01/13
【摘要】 本文翻译自Plotly官方文档 Time Series and Date Axes in Python 使用日期类型坐标轴的时间系列Plotly中的一些金融表格默认使用日期坐标轴,比如:蜡烛图和OHLC图。如果对应数据是ISO格式的日期字符串、pandas的日期列或者NumPy的日期数组,Plotly会自动将坐标轴设置为日期类型。可以使用plotly.express或者plotly.grap...

本文翻译自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个月。这个属性也接受毫秒时间戳,可以通过246060100024 * 60 * 60 * 1000将某天的日期转化为毫秒时间戳。

在日期轴的刻度标签中,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月份的数据,你可以通过设置xperiodxperiodalignment属性来将数据点显示在该月的开头、结尾或中间。在下面的例子中,原始数据被标上的都是每个月的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版本中的新功能

当用xx 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.scatterpx.linepx.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()

【版权声明】本文为华为云社区用户翻译文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容, 举报邮箱:cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。