使用plotly graph objs在3d散点图中的两个特定点之间绘制箭头

Sus*_*lik 3 python plotly scatter3d

我有一串球。每个球上都有一个箭头。

在此输入图像描述

我想用绘图来表示上面的图形,这样我就可以旋转它,但我完全迷失了。如何将箭头定位在球上?

我开始制作前 4 个箭头,如下所示:

import plotly.graph_objs as go
import plotly
plotly.offline.init_notebook_mode()


x = [10.1219, 10.42579, 15.21396, 15.42468, 20.29639,20.46268, 25.36298, 25.49156]
y = [5.0545,  5.180104, 5.0545,   5.20337,  5.0545,  5.194271, 5.0545,   5.231627]
z = [5.2713,  5.231409, 5.2713,   5.231409, 5.2713 ,  5.235852,  5.2713, 5.231627]



pairs = [(0,1), (2,3),(4,5), (6,7)]

trace1 = go.Scatter3d(
    x=x,
    y=y,
    z=z,
    mode='markers',
    name='markers'
)

x_lines = list()
y_lines = list()
z_lines = list()


for p in pairs:
    for i in range(2):
        x_lines.append(x[p[i]])
        y_lines.append(y[p[i]])
        z_lines.append(z[p[i]])
    x_lines.append(None)
    y_lines.append(None)
    z_lines.append(None)

trace2 = go.Scatter3d(
    x=x_lines,
    y=y_lines,
    z=z_lines,
    line = dict(width = 2, color = 'rgb(255, 0,0)'))

)

fig = go.Figure(data=[trace1, trace2])
plotly.offline.iplot(fig, filename='simple-3d-scatter')
Run Code Online (Sandbox Code Playgroud)

但我想要的不是连接点对的线,而是箭头。

在此输入图像描述

Der*_*k O 12

我的第一个想法是,您可以使用注释来绘制箭头作为解决方法,但注释只能在 x 和 y 维度上绘制箭头,如类似问题的答案中所述。Plotly 尚未实现可以指定所有三个维度的注释箭头。

然而,@Abdul Saboor 提出了一个巧妙的技巧,即使用基本圆锥图在线段的末端绘制一个圆锥。这些参数x, y, z为您提供圆锥体底部的起始位置,并且u, v, w为您提供圆锥体指向方向的矢量场。

因此,对于每一对点,我们可以将圆锥体底部的起点设置为起始球和结束球之间的 95%,并且圆锥体的方向将是从起始点指向的向量到终点,大小可以是任何你喜欢的值,比如点之间距离的 10%,这样圆锥体就很小而且看起来很漂亮。您可以使用我设置的任意值arrow_tip_ratio来调整这些值。arrow_starting_ratio

编辑:要仅绘制起点而不绘制终点,您可以trace1通过使用列表理解仅访问每个起始球和结束球的第一个 x、y、z 坐标来进行修改。然后,您应该指定 的参数mode='lines',以便trace2仅在每个起点和终点的第一个点上绘制线条。

import plotly.graph_objs as go
# plotly.offline.init_notebook_mode()

x = [10.1219, 10.42579, 15.21396, 15.42468, 20.29639,20.46268, 25.36298, 25.49156]
y = [5.0545,  5.180104, 5.0545,   5.20337,  5.0545,  5.194271, 5.0545,   5.231627]
z = [5.2713,  5.231409, 5.2713,   5.231409, 5.2713 ,  5.235852,  5.2713, 5.231627]

pairs = [(0,1), (2,3),(4,5), (6,7)]

## plot ONLY the first ball in each pair of balls
trace1 = go.Scatter3d(
    x=[x[p[0]] for p in pairs],
    y=[y[p[0]] for p in pairs],
    z=[z[p[0]] for p in pairs],
    mode='markers',
    name='markers',
    line=dict(color='red')
)

x_lines = list()
y_lines = list()
z_lines = list()

for p in pairs:
    for i in range(2):
        x_lines.append(x[p[i]])
        y_lines.append(y[p[i]])
        z_lines.append(z[p[i]])
    x_lines.append(None)
    y_lines.append(None)
    z_lines.append(None)

## set the mode to lines to plot only the lines and not the balls/markers
trace2 = go.Scatter3d(
    x=x_lines,
    y=y_lines,
    z=z_lines,
    mode='lines',
    line = dict(width = 2, color = 'rgb(255, 0,0)')
)

fig = go.Figure(data=[trace1, trace2])

arrow_tip_ratio = 0.1
arrow_starting_ratio = 0.98

## the cone will point in the direction of vector field u, v, w 
## so we take this to be the difference between each pair 

## then hack the colorscale to force it to display the same color
## by setting the starting and ending colors to be the same

for p in pairs:
    fig.add_trace(go.Cone(
        x=[x[p[0]] + arrow_starting_ratio*(x[p[1]] - x[p[0]])],
        y=[y[p[0]] + arrow_starting_ratio*(y[p[1]] - y[p[0]])],
        z=[z[p[0]] + arrow_starting_ratio*(z[p[1]] - z[p[0]])],
        u=[arrow_tip_ratio*(x[p[1]] - x[p[0]])],
        v=[arrow_tip_ratio*(y[p[1]] - y[p[0]])],
        w=[arrow_tip_ratio*(z[p[1]] - z[p[0]])],
        showlegend=False,
        showscale=False,
        colorscale=[[0, 'rgb(255,0,0)'], [1, 'rgb(255,0,0)']]
        ))

fig.show()

# plotly.offline.iplot(fig, filename='simple-3d-scatter')
Run Code Online (Sandbox Code Playgroud)

![在此输入图像描述