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

3
我有许多长度不同的2D序列,即列表的列表,其中每个子列表都是一个序列。我想在三维可视化中将这些序列/行/子列表投影出来,并添加时间步作为另一维。到目前为止,我未能使用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数据帧并绘制它),但没有成功。请问有人有相关经验吗?非常感谢!

screenshot

一份简历如下:
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行。

1
我觉得features并没有被定义。你介意提供一个mcve吗? - undefined
1
谢谢。我在问题中提供了一个例子。 - undefined
1
你看到三行cos对于px,第一组标签[1,1,...]和第二组是一样的。 - undefined
1个回答

8
你的问题是你在不同的追踪中使用了相同的标签。这里提供一种循环的解决方案。
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://dev59.com/gFkR5IYBdhLWcg3w6RED#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')为每行分配一种颜色,而不是为每行分配不同的颜色?这样图例就可以显示哪种颜色表示哪个标签。 - undefined
如果你能做到,那就太好了,@rpanai!非常感谢!谢谢!! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接