Making a Matplot piechart in Plotly?

I want to have the percentage values shown ontop of the pie wedge and have the labels next to the wedge. In Matplot this is pretty easy

I can’t get the same thing in plotly though, despite my efforts, any suggestions?

import plotly.graph_objects as go
import numpy as np

Sample data

labels = [‘Apple’, ‘Banana’, ‘Cherry’, ‘Date’]
values = np.array([4500, 2500, 1050, 1500]) # Convert values to a NumPy array

Create the pie chart

fig = go.Figure(data=[go.Pie(labels=labels, values=values, textinfo=‘label’, insidetextorientation=‘horizontal’)])

Customize the trace for displaying labels outside

fig.update_traces(
textposition=‘outside’, # Display labels outside the pie
marker=dict(line=dict(color=‘#000000’, width=2)),
hoverinfo=‘label+percent’
)

Calculate midpoints for each slice for proper percentage placement

angle_sum = np.cumsum(values) - values / 2
total_sum = sum(values)
mid_angles = angle_sum / total_sum * 2 * np.pi # convert to radians

Add annotations for percentages directly on the slices

for angle, value in zip(mid_angles, values):
x = 0.5 + 0.5 * np.cos(angle) # X position
y = 0.5 + 0.5 * np.sin(angle) # Y position
percentage = f"{(value / total_sum * 100):.1f}%"

fig.add_annotation(
    x=x, y=y,
    xref="paper", yref="paper",
    text=percentage,  # The percentage value
    showarrow=False,
    font=dict(size=12, color="white"),  # Ensure text is visible on slices
    align='center'
)

Update layout to ensure everything fits

fig.update_layout(
title_text=‘Fruit Consumption’,
showlegend=False # Disable the legend
)

Show the figure

fig.show()

Hi @tdl ,

it seems pretty hard to separate the value and label into different position.
Annotation maybe the best approach to add percentage inside the pie chart.

unfortunately I have not found the best calculation to get location to text annotation.
so I use exactly hard coded coordinate to place annotation, and maybe this is useful for you.

Here is the output.

import plotly.graph_objects as go
import numpy as np

# Sample data
labels = ["Apple", "Banana", "Cherry", "Date"]
values = np.array([4500, 2500, 1050, 1500]) # Convert values to a NumPy array

# Create the pie chart
fig = go.Figure(data=[go.Pie(labels=labels, values=values, textinfo="label", insidetextorientation="horizontal")])

# Customize the trace for displaying labels outside
fig.update_traces(
	textposition="outside", # Display labels outside the pie
	marker=dict(line=dict(color="#000000", width=2)),
	hoverinfo="label+percent"
)

# Calculate midpoints for each slice for proper percentage placement
angle_sum = np.cumsum(values) - values / 2
total_sum = sum(values)
mid_angles = angle_sum / total_sum * 2 * np.pi # convert to radians

percentages = []
# Add annotations for percentages directly on the slices
for angle, value in zip(mid_angles, values):
	# x = 0.5 + 0.5 * np.cos(angle) # X position
	# y = 0.5 + 0.5 * np.sin(angle) # Y position
	percentage = f"{(value / total_sum * 100):.1f}%"
	percentages.append(percentage)

# list of the annottaion location
coordinate_text_list=[(0.65,0.5),(0.35,0.35),(0.45,0.15),(0.35, 0.65)]

for coord, percentage in zip(coordinate_text_list,percentages):
	x,y = coord
	fig.add_annotation(
	    x=x, y=y,
	    xref="x domain", yref="y domain", # using x and y domain to make esier get center of pie chart
	    text=percentage,  # The percentage value
	    showarrow=False,
	    font=dict(size=12, color="white"),  # Ensure text is visible on slices
	    align='center'
	)

# Update layout to ensure everything fits
fig.update_layout(
	title_text="Fruit Consumption",
	showlegend=False # Disable the legend
)

# Show the figure
fig.show()
2 Likes

Thanks for taking a shot! Hardcoded probably isn’t ideal so I’ll keep hunting. I appreciate the effort though @farispriadi !

2 Likes

Maybe overlaying the exact same trace again but tweaking the labels and making the markers invisible? Also skip the hoverinfo.

Just thinking aloud…

1 Like

Yup, good thinking, that does it

import plotly.graph_objects as go
import numpy as np

# Sample data
labels = ['Apple', 'Banana', 'Cherry', 'Date']
values = np.array([4500, 2500, 1050, 1500])  # Convert values to a NumPy array

# Create the initial pie chart
fig = go.Figure(data=[go.Pie(
    labels=labels,
    values=values,
    textinfo='label',
    insidetextorientation='horizontal',
    textposition='outside',
    marker=dict(line=dict(color='#000000', width=2)),
    sort=False  # Ensure that the slices are in the same order for both traces
)])

# Overlay the same pie chart but with labels inside and invisible markers
fig.add_trace(go.Pie(
    labels=labels,
    values=values,
    textinfo='percent',
    insidetextorientation='horizontal',
    textposition='inside',
    marker=dict(line=dict(color='#000000', width=0)),  # Make the line invisible
    sort=False  # Ensure that the slices are in the same order
))

# Show the figure
fig.show()
2 Likes