import plotly.express as px
px.box(df, x=“people_num”)
I can hover the mouse to show the value.
However, I want annotation always on because I am going to export into PNG to put into my report. Median, q1, q3, fences, …
How can I show the value ?
import plotly.express as px
px.box(df, x=“people_num”)
I can hover the mouse to show the value.
However, I want annotation always on because I am going to export into PNG to put into my report. Median, q1, q3, fences, …
How can I show the value ?
Do it is possible with plotly express ? Or I have to manually re-calculate all the value Mean, Median, Q1, Q3, fences and use plotly.graph_objects to manually add annotation ?
I would guess that, yes, you will have to re-calculate them and then use something like fig.add_annotation
to add them manually. You may perhaps also have look at Statistical annotations in graphs to see if that helps? If you find an easier solution, please let me know. Thanks.
I had to recalculate the values to exactly match plotly method. Using numpy/scipy/pandas don’t give the exact value even though I used linear in both places. How I tackled the above case is mentioned in the code below. It also handle the case where we have facet plots in plotly express:
## Calculate quartiles as outlined in the plotly documentation
## (method #10 in paper https://jse.amstat.org/v14n3/langford.html)
def get_percentile(data, p):
data.sort()
n = len(data)
x = n*p + 0.5
# If integer, return
if x.is_integer():
return round(data[int(x-1)], 2) # account for zero-indexing
# If not an integer, get the interpolated value of the values of floor and ceiling indices
x1, x2 = math.floor(x), math.ceil(x)
y1, y2 = data[x1-1], data[x2-1] # account for zero-indexing
return round(np.interp(x=x, xp=[x1, x2], fp=[y1, y2]), 2)
thickness = generate_data()
minimum = round(np.min(thickness), 2)
maximum = round(np.max(thickness), 2)
std = round((np.std(thickness)), 2)
mean = round((np.mean(thickness)), 2)
## calculate all boxplot statistics
q1, median, q3 = get_percentile(thickness, 0.25), get_percentile(thickness, 0.50), get_percentile(thickness, 0.75)
iqr = q3 - q1
# Lower fence value is the minimum of y values that is more than the calculated lower limit
lower_limit = q1 - 1.5 * iqr
lower_fence = round(min([i for i in thickness.tolist() if i >= lower_limit]), 2)
# Upper fence value is the maximum of y values that is less than the calculated upper limit
upper_limit = q3 + 1.5 * iqr
upper_fence = round(max([i for i in thickness.tolist() if i <= upper_limit]), 2)
l_outliers = [i for i in thickness.tolist() if round(i, 2) < lower_fence]
u_outliers = [i for i in thickness.tolist() if round(i, 2) > upper_fence]
print(lower, q1, q3, upper, std, mean, median)
print(l_outliers, u_outliers)
thickness_cleaned = np.clip(thickness, a_min=lower_fence, a_max=upper_fence)
df = pd.DataFrame(dict(
Raw=thickness,
Cleaned=thickness_cleaned,
)).melt(var_name="stage")
fig = px.box(df, y='value', facet_col="stage", color="stage", boxmode="overlay", width=800, height=500, points='outliers', title="Thickness of the all points")
fig.update_traces(boxmean='sd')
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
# Annotate boxplot values
annots = [
dict(y=minimum, text=f'Min: {minimum}'),
dict(y=lower_fence, text=f'Lower: {lower_fence}'),
dict(y=q1, text=f'Q1: {q1}'),
dict(y=mean, text=f'Mean±σ: {mean}±{std}'),
dict(y=median, text=f'Median: {median}'),
dict(y=q3, text=f'Q3: {q3}'),
dict(y=upper_fence, text=f'Upper: {upper_fence}'),
dict(y=maximum, text=f'Max: {maximum}'),
]
common_annotation_params = dict(
font=dict(size=10, color="#ffffff"),
showarrow=False,
bgcolor=fig.data[0]['marker']['color'], # Get same colour as the facet plot
xref='x', # Specify which facet to put the annotation; Goes like x, x2, x3 ... xn.
x=0.4,
xanchor='left', # Align all the labels on x axis
)
for annot in annots:
annot = {**annot, **common_annotation_params}
# Change the alignment of the mean to avoid overlap with other values
# You can also do this for min and max, if it overlaps with the upper and lower
if (annot['y'] == mean):
annot['x'] = -0.4
annot['xanchor'] = 'right'
fig.add_annotation(annot)
fig.update_layout(showlegend=False, yaxis=dict(title_text="Thickness (mm)"))
fig.show()