使用 plotly 在一个图中绘制多条 3d 线

Posted

技术标签:

【中文标题】使用 plotly 在一个图中绘制多条 3d 线【英文标题】:Plotting multiple 3d lines in one figure using plotly 【发布时间】:2020-05-25 22:41:43 【问题描述】:

我有许多可变长度的二维序列,即列表列表,其中每个子列表都是一个序列。我想在 3d 可视化中投影这些序列/行/子列表,添加时间步长作为另一个维度。到目前为止,我未能使用 plotly.express 绘制所有 3d 线。

import plotly.express as px

t = [[ii+1 for ii in range(len(features[i]))] for i in range(len(labels))]
x0 = [[x[0] for x in features[i]] for i in range(len(labels))]
x1 = [[x[1] for x in features[i]] for i in range(len(labels))]

df = pd.DataFrame(dict(
    X=[tii for ti in t for tii in ti],
    Y=[xii for xi in x0 for xii in xi],
    Z=[xii for xi in x1 for xii in xi],
    color=[aa for a in labels for aa in a]
))
fig = px.line_3d(df, x="X", y="Y", z="Z", color="color")
fig.show

这就是我得到的,这并不是我真正想要的。它将所有带有公共标签的案例/子列表视为一个序列,因此我们在每一行的末尾看到它回到它开始的地方。我已经查看了如何在 for 循环中迭代地绘制它(就像 matplotlib 一样)(基本上在每次迭代时创建一个新的 pandas 数据框并绘制它),但是没有成功。请问有人有这方面的经验吗?非常感谢!

一个mcve如下:

import plotly.express as px
import numpy as np
import pandas as pd

features = [np.random.rand(4,2).tolist(), 
            np.random.rand(5,2).tolist(), 
            np.random.rand(6,2).tolist(), 
            np.random.rand(5,2).tolist(), 
            np.random.rand(9,2).tolist()]
labels = [[1, 1, 1, 1], [1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2],
         [2, 2, 2, 2, 2], [0, 0, 0, 0, 0, 0, 0, 0, 0]]

t = [[ii+1 for ii in range(len(features[i]))] for i in range(len(labels))]
x0 = [[x[0] for x in features[i]] for i in range(len(labels))]
x1 = [[x[1] for x in features[i]] for i in range(len(labels))]

df2 = pd.DataFrame(dict(
    X=[tii for ti in t for tii in ti],
    Y=[xii for xi in x0 for xii in xi],
    Z=[xii for xi in x1 for xii in xi],
    color=[aa for a in labels for aa in a]
))
fig1 = px.line_3d(df2, x="X", y="Y", z="Z", color="color")
fig1.show()

你看到的基本上是 3 行而不是 5 行。

【问题讨论】:

在我看来,features 没有定义。你介意生成一个mcve吗? 谢谢。我在问题中提供了一个示例。 你看到三行因为px 第一组标签[1,1,...] 与第二组相同。 【参考方案1】:

您的问题是您对不同的跟踪使用相同的标签。这里有一个循环的解决方法

import numpy as np
import plotly.graph_objs as go

features = [np.random.rand(4,2).tolist(), 
            np.random.rand(5,2).tolist(), 
            np.random.rand(6,2).tolist(), 
            np.random.rand(5,2).tolist(), 
            np.random.rand(9,2).tolist()]
labels = [[1, 1, 1, 1],
          [1, 1, 1, 1, 1],
          [2, 2, 2, 2, 2, 2],
          [2, 2, 2, 2, 2],
          [0, 0, 0, 0, 0, 0, 0, 0, 0]]

fig = go.Figure()
for i, feat in enumerate(features):
    feat = np.array(feat)
    fig.add_trace(
        go.Scatter3d(
            x=np.arange(len(feat)),
            y=feat[:,0],
            z=feat[:,1],
            mode='lines',
            hovertext=labels[i]
        )
    )
fig.show()

您可能需要使用跟踪名称。

更新

虽然不太复杂,但应该尽可能通用


import numpy as np
import plotly.graph_objs as go
from itertools import cycle

def plotly_color_map(names):
    # From https://***.com/a/44727682
    plotly_colors = cycle(['#1f77b4',  # muted blue
                           '#ff7f0e',  # safety orange
                           '#2ca02c',  # cooked asparagus green
                           '#d62728',  # brick red
                           '#9467bd',  # muted purple
                           '#8c564b',  # chestnut brown
                           '#e377c2',  # raspberry yogurt pink
                           '#7f7f7f',  # middle gray
                           '#bcbd22',  # curry yellow-green
                           '#17becf'  # blue-teal
                           ])

    return dict(zip(names, plotly_colors))


features = [np.random.rand(4,2).tolist(), 
            np.random.rand(5,2).tolist(), 
            np.random.rand(6,2).tolist(), 
            np.random.rand(5,2).tolist(), 
            np.random.rand(9,2).tolist()]

labels = [[1, 1, 1, 1],
          [1, 1, 1, 1, 1],
          [2, 2, 2, 2, 2, 2],
          [2, 2, 2, 2, 2],
          [0, 0, 0, 0, 0, 0, 0, 0, 0]]

legend_groups = [l[0] for l in labels]

traces = [False if (len(legend_groups[:i])>0 and l in legend_groups[:i]) 
          else True for i, l in enumerate(legend_groups)]

cm = plotly_color_map(set(legend_groups))

fig = go.Figure()
for i, feat in enumerate(features):
    feat = np.array(feat)
    fig.add_trace(
        go.Scatter3d(
            x=np.arange(len(feat)),
            y=feat[:,0],
            z=feat[:,1],
            mode='lines',
            line="color":cm[legend_groups[i]],
            legendgroup=legend_groups[i],
            hovertext=labels[i],
            showlegend=traces[i],
            name="label_".format(legend_groups[i])
        )
    )
fig.show()

【讨论】:

非常感谢@rpanai!请问我是否可以根据每行的标签(即'0'或'1'或'2')为其分配颜色,而不是为每行分配不同的颜色?因此,图例显示了哪种颜色表示哪个标签。 如果你能@rpanai 那就太好了!非常感激!谢谢!!

以上是关于使用 plotly 在一个图中绘制多条 3d 线的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 plotly.graph_objects 绘制 3D 线?

如何在 R 的 3D 图中从分类算法中绘制分区平面

如何使用 plotly express(XYXY 格式数据)在同一图表上绘制多条线?

R语言使用scatterplot3d包的scatterplot3d函数可视化3D散点图(3D scatter plots)在3D散点图中添加垂直线和数据点描影3D图中添加回归平面

使用 Plotly 绘制动画 3D 曲面图

如何在 Plotly 3D 散点图中设置点标记的样式/格式?