Построение нескольких 3d линий на одной фигуре с использованием графика - PullRequest
2 голосов
/ 10 февраля 2020

У меня есть много двумерных последовательностей с переменной длиной, то есть списки списков, где каждый подсписок является последовательностью. Я хочу проецировать эти последовательности / строки / подсписки в трехмерной визуализации, добавляя шаг по времени в качестве другого измерения. Пока что мне не удается построить все 3d линии, используя plotly.express.

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-l oop (точно так же, как matplotlib) (в основном создавая новый pandas фрейм данных на каждой итерации и создавая его), однако безуспешно. У кого-нибудь есть опыт по этому вопросу, пожалуйста? Очень ценится!

screenshot

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.

1 Ответ

3 голосов
/ 10 февраля 2020

Ваши проблемы в том, что вы используете один и тот же ярлык для разных трасс. Вот обходной путь с al oop

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()

Возможно, вам придется поиграть с именами трасс.

Обновление

К счастью, это не слишком сложно, но это должно быть в общем c насколько возможно


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

def plotly_color_map(names):
    # From https://stackoverflow.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()
...