Hello
I recently saw that it’s now possible to style markers in Plotly:
https://plotly.com/python/marker-style/
Would it be possible to add a “tadpole” marker style to the markers listed in the above link?
@empet already suggested a workaround a few months ago here:
With the markers above I also found another workaround which now makes the tails of the markers appear as lines as wanted. I simply combine two traces with the marker symbols “circle” and “line-ew” (these are the tadpole tails) to get the desired tadpole plot.
In the workaround code below, the “line-ew” markers are placed with a small offset with respect to the “circle” markers because I want the lines to stick out only on one side of the “circle” markers. The corrective terms account for different figure sizes, x-/y-scale ranges and figure margins.
Even when zooming into the figure, the “line-ew” marks are always positioned at the right spot. It took me a while to figure out the corrective terms but it now works.
I used the function f1.layout.xaxis.on_change(update1, ‘range’) to update the figure automatically each time one zooms into the figure or out again.
If anyone has a more straighforward solution, please let me know!
code:
import numpy as np
import plotly.graph_objs as go
import plotly.express as px
from ipywidgets import interactive, HBox, VBox, widgets, interact, fixed,
interact_manual, Layout, Button, Box, FloatText, Textarea, Dropdown,
Label, IntSlider
from IPython.display import display, HTML
display(HTML(“.container { width:100% !important; }”))
“”" FUNCTION TO UPDATE FIGURE-WIDGET. “”"
def update1(XAxis, xAxisTuple):
# The following two function arguments are not used on the subsequent lines.
print('type(XAxis.range) =', type(XAxis.range))
print('type(xAxisTuple) =', type(xAxisTuple))
with f1.batch_update():
xAxMin = f1.layout.xaxis['range'][0]
print(xAxMin)
xAxMax = f1.layout.xaxis['range'][1]
print(xAxMax)
yAxMin = f1.layout.yaxis['range'][0]
print(yAxMin)
yAxMax = f1.layout.yaxis['range'][1]
print(yAxMax)
# Corrective term accounting for ratio of axes ranges.
fac1 = (xAxMax-xAxMin) / (yAxMax-yAxMin)
# Adjustment term for chosen value for variable l = 0.0042
fac4 = (yAxMax-yAxMin)/3
f1.data[1].x = df['sepal_width'] + l * np.cos(np.deg2rad(-ang))*fac1*fac2*fac3*fac4
f1.data[1].y = df['sepal_length'] + l * np.sin(np.deg2rad(-ang))*fac3*fac4
“”" LOADING DATA AND SETTING SOME FIGURE PARAMETERS. “”"
df = px.data.iris()
ang = 120
XaxRange = [2, 5]
YaxRange = [4, 9]
wid = 1000
heig = 800
bordL = 10
bordR = 10
bordT = 10
bordB = 10
l = 0.042
“”" TRACE 1, CIRCLE MARKERS. “”"
fig1 = px.scatter(df, x=‘sepal_width’, y=‘sepal_length’)
fig1.update_traces(marker=dict(size=8,
symbol=“circle”,
line=dict(width=2, color=“DarkSlateGrey”)),
selector=dict(mode=“markers”),
showlegend=False,
marker_color=‘LightSteelBlue’)
“”" TRACE 2, LINE MARKERS “”"
DEFINITION OF POSITIONS OF LINE MARKERS.
Corrective term accounting for ratio of axes ranges.
fac1 = (XaxRange[1]-XaxRange[0]) / (YaxRange[1]-YaxRange[0])
Corrective term accounting for window size and figure margins.
fac2 = (heig-bordT-bordB)/(wid-bordL-bordR)
fac3 = (1000-bordT-bordB)/(heig-bordT-bordB)
Adjustment term for chosen value for variable l = 0.042
fac4 = (YaxRange[1]-YaxRange[0])/3
df[‘x’] = df[‘sepal_width’] + l * np.cos(np.deg2rad(-ang)) * fac1 * fac2 * fac3 * fac4
df[‘y’] = df[‘sepal_length’] + l * np.sin(np.deg2rad(-ang)) * fac3 * fac4
fig2 = px.scatter(df, x=‘x’, y=‘y’)
fig2.update_traces(marker=dict(size=12,
symbol=“line-ew”,
angle=ang,
line=dict(width=2, color=“DarkSlateGrey”)),
selector=dict(mode=“markers”),
showlegend=False)
“”" FIGURE-WIDGET COMBINING TRACES 1 AND 2. “”"
fig3 = go.Figure(data=fig1.data + fig2.data,
layout_xaxis_range=XaxRange,
layout_yaxis_range=YaxRange)
fig3.update_layout(margin=dict(l=bordL, r=bordR, t=bordT, b=bordB),
paper_bgcolor=“LightSteelBlue”,
width=wid, height=heig)
f1 = go.FigureWidget(fig3)
display(f1)
f1.layout.xaxis.on_change(update1, ‘range’)