我正在创建一个仪表板,使用 Python 中的 Plotly 和 Dash 包来可视化数据。
我使用
prepare_plot_data
和 prepare_data_trend
函数来准备用于绘图的数据。
我正在尝试使用
make_subplots
设置图形布局在一张图中创建两个子图。然而,两个子图在同一位置相互重叠。
据我了解,
make_subplots
允许使用行索引和列索引定义子图的位置。在我的例子中,我将行设置为 2,将列设置为 1。这应该在图中创建两个位置:一个位于 (1, 1),另一个位于 (2, 1)。
当我绘制第一个热图时,我将行设置为 1,列设置为 1,期望将其放置在 (1, 1) 位置。对于第二个图,我将行设置为 2,将列设置为 1,期望将其放置在 (2, 1) 处。然而,根据我想象的结果,结果与我的预期不符。
有谁知道如何将子图分成不同的位置?或者我是否误解了 Plotly 的
make_subplots
的工作原理?
from datetime import date, datetime
from functools import reduce
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from dash import Dash, Input, Output, dcc, html
from loguru import logger
from plotly.subplots import make_subplots
from scipy import stats
class OverView:
def __init__(self):
self.catologs = ["1","2","3",]
def prepare_plot_data(self, start_date, end_date):
data = [
{"date":"2020-01-01", "1":0.1},
{"date":"2020-01-01", "2":-0.1},
{"date":"2020-01-01", "3":0.9},
{"date":"2020-02-01", "1":0.1},
{"date":"2020-02-01", "2":0.1},
{"date":"2020-02-01", "3":0.1},
{"date":"2020-03-01", "1":0.1},
{"date":"2020-03-01", "2":0.1},
{"date":"2020-03-01", "3":0.1},
]
return pd.DataFrame(data)
def prepare_data_trend(self, start_date, end_date):
data = [
{"year-month":"2020-01", "id":"1", "normal_slope":0.1},
{"year-month":"2020-01", "id":"2", "normal_slope":-0.1},
{"year-month":"2020-01", "id":"3", "normal_slope":0.9},
{"year-month":"2020-02", "id":"1", "normal_slope":0.12},
{"year-month":"2020-02", "id":"2", "normal_slope":0.15},
{"year-month":"2020-02", "id":"3", "normal_slope":-0.15},
{"year-month":"2020-03", "id":"1", "normal_slope":0.5},
{"year-month":"2020-03", "id":"2", "normal_slope":0.3},
{"year-month":"2020-03", "id":"3", "normal_slope":0.2},
]
return pd.DataFrame(data)
def plot(self, start_date=None, end_date=None):
df = self.prepare_plot_data(start_date, end_date)
df_trend = self.prepare_data_trend(start_date, end_date)
fig = make_subplots(
rows=2, cols=1, row_heights=[0.6, 0.4],
specs=[
[{"type": "xy", "colspan": 1, "rowspan": 1}],
[{"type": "xy", "colspan": 1, "rowspan": 1, "secondary_y": True}],
],
horizontal_spacing=0,
vertical_spacing=0.01,
)
fig1 = go.Heatmap(
x=df_trend['year-month'].tolist(),
y=df_trend['id'].tolist(),
z=df_trend['normal_slope'].tolist(),
colorbar=dict(title='heat'),
)
fig.add_trace(fig1, row=1, col=1,)
fig.update_traces(colorbar_orientation='h', selector=dict(type='heatmap'))
for catolog in self.catologs:
fig.add_trace(
go.Scatter(
x=df["date"].values,
y=df[catolog].values,
name=catolog,
),
secondary_y=True,
row=2, col=1,
)
return fig
def run_dash(self):
app = Dash(__name__)
app.layout = html.Div(
[
html.H1("tmp Overview"),
dcc.DatePickerRange(
id="my-date-picker-range",
min_date_allowed=date(1990, 1, 1),
max_date_allowed=date(2100, 12, 31),
initial_visible_month=date(2021, 1, 1),
start_date=date(2018, 1, 1),
end_date=date(2023, 12, 31),
),
dcc.Graph(id="graph"),
],
)
@app.callback(
Output("graph", "figure"),
[
Input("my-date-picker-range", "start_date"),
Input("my-date-picker-range", "end_date"),
],
)
def update_output(start_date, end_date):
return self.plot(start_date, end_date)
app.run_server(debug=True)
OverView().run_dash()
您对
make_subplots
函数及其轴索引的理解是正确的!问题似乎出在您对水平和垂直放置方面所做的自定义调整。通过简化的 make_subplots
声明来解决这个问题似乎可以解决问题:
from datetime import date, datetime
from functools import reduce
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from dash import Dash, Input, Output, dcc, html
from loguru import logger
from plotly.subplots import make_subplots
from scipy import stats
class OverView:
def __init__(self):
self.catologs = [
"1",
"2",
"3",
]
def prepare_plot_data(self, start_date, end_date):
data = [
{"date": "2020-01-01", "1": 0.1},
{"date": "2020-01-01", "2": -0.1},
{"date": "2020-01-01", "3": 0.9},
{"date": "2020-02-01", "1": 0.1},
{"date": "2020-02-01", "2": 0.1},
{"date": "2020-02-01", "3": 0.1},
{"date": "2020-03-01", "1": 0.1},
{"date": "2020-03-01", "2": 0.1},
{"date": "2020-03-01", "3": 0.1},
]
return pd.DataFrame(data)
def prepare_data_trend(self, start_date, end_date):
data = [
{"year-month": "2020-01", "id": "1", "normal_slope": 0.1},
{"year-month": "2020-01", "id": "2", "normal_slope": -0.1},
{"year-month": "2020-01", "id": "3", "normal_slope": 0.9},
{"year-month": "2020-02", "id": "1", "normal_slope": 0.12},
{"year-month": "2020-02", "id": "2", "normal_slope": 0.15},
{"year-month": "2020-02", "id": "3", "normal_slope": -0.15},
{"year-month": "2020-03", "id": "1", "normal_slope": 0.5},
{"year-month": "2020-03", "id": "2", "normal_slope": 0.3},
{"year-month": "2020-03", "id": "3", "normal_slope": 0.2},
]
return pd.DataFrame(data)
def plot(self, start_date=None, end_date=None):
df = self.prepare_plot_data(start_date, end_date)
df_trend = self.prepare_data_trend(start_date, end_date)
fig = make_subplots(rows=2)
fig1 = go.Heatmap(
x=df_trend["year-month"].tolist(),
y=df_trend["id"].tolist(),
z=df_trend["normal_slope"].tolist(),
colorbar=dict(title="heat"),
)
fig.add_trace(
fig1, row=1, col=1,
)
fig.update_traces(
colorbar_orientation="h", selector=dict(type="heatmap")
)
for catolog in self.catologs:
fig.add_trace(
go.Scatter(
x=df["date"].values, y=df[catolog].values, name=catolog,
),
row=2,
col=1,
)
return fig
def run_dash(self):
app = Dash(__name__)
app.layout = html.Div(
[
html.H1("tmp Overview"),
dcc.DatePickerRange(
id="my-date-picker-range",
min_date_allowed=date(1990, 1, 1),
max_date_allowed=date(2100, 12, 31),
initial_visible_month=date(2021, 1, 1),
start_date=date(2018, 1, 1),
end_date=date(2023, 12, 31),
),
dcc.Graph(id="graph"),
],
)
@app.callback(
Output("graph", "figure"),
[
Input("my-date-picker-range", "start_date"),
Input("my-date-picker-range", "end_date"),
],
)
def update_output(start_date, end_date):
return self.plot(start_date, end_date)
app.run_server(debug=True)
OverView().run_dash()