I have a dataframe with amounts grouped by categories and I want to generate a pie chart containing the percentages per category. I am using the Bokeh library in Python.
My problem is that some percetange labels are not displayed properly in the pie chart as shown in the following image.
Here is the code that I use to produce the pie chart:
df['angle'] = df['paymentAmount'] / df['paymentAmount'].sum() * 2 * pi if len(df.index) > 2: df["color"] = GnBu[len(df.index)] elif len(df.index) == 2: df["color"] = ["steelblue", "skyblue"] else: df["color"] = ["steelblue"] df["percentage"] = df["paymentAmount"] / df["paymentAmount"].sum() df['percentage'] = df['percentage'].astype(float).map(lambda n: '{:.2%}'.format(n)) df["percentage"] = df['percentage'].astype(str) df["percentage"] = df["percentage"].str.pad(35, side="left") source = ColumnDataSource(df) p = figure(width=figsize[0], height=figsize[1], toolbar_location=None, tooltips=[("percentage", "@percentage")], tools="hover", x_range=(-0.5, 1.0)) pie_chart = p.wedge(x=0, y=1, radius=0.4, start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'), line_color="white", fill_color='color', source=source) labels = LabelSet(x=0, y=1, text='percentage', angle=cumsum('angle', include_zero=True), source=source, render_mode='canvas') p.add_layout(labels) legend = Legend(items=[LegendItem(label=dict(field='category'), renderers=[pie_chart])], location=(0, figsize[1] - 100)) p.add_layout(legend, 'right') p.axis.axis_label = None p.axis.visible = False p.grid.grid_line_color = None save(p)
Some data to reproduce the the pie chart above (tab separated)
category paymentAmount Chemicals 52800.95001766206 Research 110878.70999269483 Construction 266189.2100121978
Any ideas how to solve this?
Thank you in advance!
Advertisement
Answer
I have found two useful StackOverflow posts to solve this:
- In LabelSet you have to set the angle as zero so as not to rotate the labels (Adding labels to bokeh pie chart wedge)
- You have to calculate the coordinates (x and y) of LabelSet for each item. For this I used this post: adding percentage label to Bokeh Pie chart
Therefore, I have modified your code as follows:
df['angle'] = df['paymentAmount'] / df['paymentAmount'].sum() * 2 * pi value = list(df["paymentAmount"]) df["cumulative_angle"] = [(sum(value[0:i + 1]) - (item / 2)) / sum(value) * 2 * pi for i, item in enumerate(value)] df['cos'] = np.cos(df['cumulative_angle']) * 0.3 df['sin'] = np.sin(df['cumulative_angle']) * 0.3 if len(df.index) > 2: df["color"] = GnBu[len(df.index)] elif len(df.index) == 2: df["color"] = ["steelblue", "skyblue"] else: df["color"] = ["steelblue"] df["percentage"] = df["paymentAmount"] / df["paymentAmount"].sum() df['percentage'] = df['percentage'].astype(float).map(lambda n: '{:.2%}'.format(n)) df["percentage"] = df['percentage'].astype(str) df["percentage"] = df["percentage"].str.pad(35, side="left") source = ColumnDataSource(df) p = figure(width=figsize[0], height=figsize[1], toolbar_location=None, x_range=(-1.0, 1.0), y_range=(-1.0, 1.0), tooltips=[("percentage", "@percentage")], tools="hover") pie_chart = p.wedge(x=0, y=0, radius=0.5, start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'), line_color="white", fill_color='color', source=source) labels = LabelSet(x="cos", y="sin", y_offset=0, text='percentage', text_align="center", angle=0, source=source, render_mode='canvas') p.add_layout(labels) legend = Legend(items=[LegendItem(label=dict(field="category"), renderers=[pie_chart])], location=(0, figsize[1] - 100)) p.add_layout(legend, 'right') p.axis.axis_label = None p.axis.visible = False p.grid.grid_line_color = None save(p)
The produced pie chart is: