8000 add swarm plot to the scatter documentation by rl-utility-man · Pull Request #5149 · plotly/plotly.py · GitHub
[go: up one dir, main page]

Skip to content

add swarm plot to the scatter documentation #5149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 7, 2025
Merged
Prev Previous commit
Next Next commit
collision avoidance
  • Loading branch information
rl-utility-man authored Apr 21, 2025
commit 15b758055fbce230628e74120a0847cccc4f26d1
56 changes: 40 additions & 16 deletions doc/python/line-and-scatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,23 @@ import pandas as pd
import plotly.express as px
import collections

def negative_1_if_count_is_odd(count):
# if this is an odd numbered entry in its bin, make its y coordinate negative
# the y coordinate of the first entry is 0, so entries 3, 5, and 7 get negative y coordinates
if count%2 == 1:
return -1
else:
return 1




def swarm(
X_series,
point_size=16,
fig_width = 800,
gap_multiplier=1.2,
center_even_groups = False
):
#sorting will align columns in attractive arcs rather than having columns the vary unpredicatbly in the x-dimension
X_series=X_series.copy().sort_values()
Expand All @@ -309,7 +320,7 @@ def swarm(
# minimum X value to the maximum X value
min_x = min(X_series)
max_x = max(X_series)

list_of_rows = []
# we will count the number of points in each "bin" / vertical strip of the graph
# to be able to assign a y-coordinate that avoids overlapping
Expand All @@ -319,33 +330,43 @@ def swarm(
# assign this x_value to bin number
# each bin is a vertical strip wide enough for one marker
bin=(((fig_width*(x_val-min_x))/(max_x-min_x)) // point_size)

#update the count of dots in that strip
bin_counter.update([bin])

# if this is an odd numbered entry in its bin, make its y coordinate negative
# the y coordinate of the first entry is 0, so entries 3, 5, and 7 get negative y coordinates
if bin_counter[bin]%2 == 1:
negative_1_if_count_is_odd = -1
else:
negative_1_if_count_is_odd = 1


# the collision free y coordinate gives the items in a vertical bin
# coordinates: 0, 1, -1, 2, -2, 3, -3 ... and so on to evenly spread
# their locations above and below the y-axis (we'll make a correction below to deal with even numbers of entries)
# we then scale this by the point_size*gap_multiplier to get a y coordinate in px

collision_free_y_coordinate=(bin_counter[bin]//2)*negative_1_if_count_is_odd*point_size*gap_multiplier
list_of_rows.append({"x":x_val,"y":collision_free_y_coordinate,"bin":bin})
collision_free_y_coordinate=(bin_counter[bin]//2)*negative_1_if_count_is_odd(bin_counter[bin])*point_size*gap_multiplier
list_of_rows.append({"x":x_val,"y":collision_free_y_coordinate,"bin":bin, "adj":0})

# if the number of points is even,
# move y-coordinates down to put an equal number of entries above and below the axis
#this can sometimes break the collision avoidance routine, but makes small N outputs look better otherwise
if center_even_groups:
for row in list_of_rows:
if bin_counter[row["bin"]]%2==0:
row["y"]-=point_size*gap_multiplier/2
row["adj"]=-point_size*gap_multiplier/2


for row in list_of_rows:
if bin_counter[row["bin"]]%2==0:
row["y"]-=point_size*gap_multiplier/2
bin = row["bin"]
#see if we need to "look left" to avoid a possible collision
for other_row in list_of_rows:
if (other_row["bin"]==bin-1 ):
if (((other_row["y"]==row["y"]) or (other_row["y"]==row["y"]+row["adj"]))
and (((fig_width*(row["x"]-other_row["x"]))/(max_x-min_x) // point_size) < 1)):
bin_counter.update([bin])
row["y"]=(bin_counter[bin]//2)*negative_1_if_count_is_odd(bin_counter[bin])*point_size*gap_multiplier+row["adj"]



df = pd.DataFrame(list_of_rows)

fig = px.scatter(
df,
x="x",
Expand All @@ -370,9 +391,12 @@ def swarm(


df_iris = px.data.iris() # iris is a pandas DataFrame
fig = swarm(df_iris["sepal_length"])
x = df_iris["sepal_length"]
x2 = pd.Series([5.05])
x = pd.concat([x,x2], ignore_index=True)
fig = swarm(x)
#fig = swarm(pd.Series([1,1.5, 1.78, 1.79,2,2,12]))
fig.show()

```

## Scatter and line plots with go.Scatter
Expand Down
0