Plot of a discrete distribution


#1

The image below contains plots of the binomial distribution produced with Mathematica:

image

Source: https://reference.wolfram.com/language/ref/DiscretePlot.html

How do I create similar plots, i.e. of a discrete distribution, in Python?
I can plot the dots, but, I want the distribution to appear as shown in the image.


#2

Hi @ursus,

If you’ve already calculated the point locations then you could draw the vertical bars using bar traces (https://plot.ly/python/bar-charts/) underneath the scatter trace holding the points.

To match the aesthetic of your example, set the layout.bargap to something like 0.8 (0 is no gap between and 1 is a bar width of zero), and set the bar.marker.color to the same color as the scatter markers but set the bar.marker.opacity to something like 0.6.

Hope that helps!
-Jon


#3

Thank you!
For posterity, here is the code:

fig = go.FigureWidget()
xs = np.arange(51)
for el in zip([0.3, 0.5, 0.8], ['#1f77b4', '#2ca02c', '#9467bd']):
    ys = stats.binom.pmf(xs, n=50, p=el[0])
    fig.add_scatter(x=xs, y=ys, mode='markers')
    fig.add_bar(x=xs, y=ys, marker = dict(color=el[1], opacity=0.5))
fig.layout = dict(showlegend = False, bargap=0.75, barmode='overlay', width = 800)
fig

The plot:

Is it possible instead of color=el[1] to have something like color=scatter.marker.color (scatter would be the name of the scatter plot)?


#4

Yes, if I understand you correctly. You could do something like

scatter = fig.add_scatter(x=xs, y=ys, mode='markers')
fig.add_bar(x=xs, y=ys, marker = dict(color=scatter.marker.color, opacity=0.5))

-Jon


#5

That is allowed, but somehow the bars do not inherit the color of scatter.
So, color=scatter.marker.color does not appear to have any effect:

import numpy as np
from scipy import stats
import plotly.graph_objs as go

fig = go.FigureWidget()
xs = np.arange(51)
ys = stats.binom.pmf(xs, n=50, p=0.3)
scatter = fig.add_scatter(x=xs, y=ys, mode='markers')
fig.add_bar(x=xs, y=ys, marker = dict(color=scatter.marker.color, opacity=0.3))
fig.layout = dict(showlegend = False, bargap=0.75, barmode='overlay', width = 800)
fig

will produce:

If I then change the name to dots but keep scatter.marker.color:

fig = go.FigureWidget()
xs = np.arange(51)
ys = stats.binom.pmf(xs, n=50, p=0.3)
dots = fig.add_scatter(x=xs, y=ys, mode='markers')
fig.add_bar(x=xs, y=ys, marker = dict(color=scatter.marker.color, opacity=0.3))
fig.layout = dict(showlegend = False, bargap=0.75, barmode='overlay', width = 800)
fig

I get what I want:

So, I think scatter gets initialized when fig is called… or something like that. In my case it remembered the scatter attributes from the previous call. Indeed when I place fig.add_bar(x=xs, y=ys, marker = dict(color=scatter.marker.color, opacity=0.3)) in a different cell (after I’ve run fig) I get the right output. It is not very elegant though to have bits of code pertaining to the same plot in different cells, and I don’t know how (aside from using dictionaries) this can be done in a loop (as shown in the first piece of code above).

Maybe I’m missing something.


#6

Hi @ursus,

I didn’t realize that you weren’t initializing the scatter trace with the color that you want. The reason this isn’t quite working is that the default value of scatter.marker.color that is chosen by plotly.js is synchronized back to the Python model asynchronously, so the color property is not available yet when you make the add_bar call.

So your choices are to either specify the scatter.marker.color property manually, or wait for the color to be updated before calling add_bar. One way to wait for this update to occur is to use an on_edits_completed callback. This lets you register a function to be called after all of the default properties have been synchronized back to the Python model. For example:

import numpy as np
from scipy import stats
import plotly.graph_objs as go

fig = go.FigureWidget()
xs = np.arange(51)
ys = stats.binom.pmf(xs, n=50, p=0.3)
scatter = fig.add_scatter(x=xs, y=ys, mode='markers')
fig.on_edits_completed(
    lambda: fig.add_bar(
        x=xs, y=ys,marker = dict(color=scatter.marker.color, opacity=0.3)))
fig.layout = dict(showlegend = False, bargap=0.75, barmode='overlay', width = 800)
fig

Hope that helps,
-Jon