Seaborn catplot (kind='count') change bar chart to pie chart

Viewed 28305

I have the following looking df for my paper on corona-tracking-apps (pd.melt was used on it):

    CTQ-tool    opinion
0   Information and awareness purposes  unacceptable
1   Information and awareness purposes  unacceptable
2   Information and awareness purposes  acceptable
3   Information and awareness purposes  acceptable
4   Information and awareness purposes  unacceptable
... ... ...
2827    Central/Local data storage  NaN
2828    Central/Local data storage  NaN
2829    Central/Local data storage  NaN
2830    Central/Local data storage  NaN
2831    Central/Local data storage  NaN
2832 rows × 2 columns

I am using Seaborn library to make the following catplot:

code:

g = sns.catplot("opinion", col="CTQ-tool", col_wrap=4, data=df_original_small, kind="count", height=6.5, aspect=.8)

enter image description here

However, instead of displaying these in bar charts I would like to present them as pie charts. The Seaborn.catplot does not allow for something kind='count-pie'. Does anyone know a work around?

EDIT after TiTo question:

this is basicly what I want to see happen to all 8 bar charts:

enter image description here

3 Answers

I ended up using matplotlib library to build it up from the bottem:

plt.style.use('seaborn')

IAP = df_original_small['Information and awareness purposes'].value_counts().to_frame().T
QE = df_original_small['Quarantine Enforcement'].value_counts().to_frame().T
CTCR = df_original_small['Contact Tracing and Cross-Referencing'].value_counts().to_frame().T
VPID = df_original_small['Voluntary provision of infection data'].value_counts().to_frame().T
QMA = df_original_small['Quarantine Monitoring App'].value_counts().to_frame().T
QRCode = df_original_small['QR code provided registration tracking'].value_counts().to_frame().T

total = pd.concat([IAP, QE, CTCR, VPID, QMA, QRCode])

fig, ax = plt.subplots(nrows=3, ncols=2)

labels = 'acceptable', 'unacceptable'
colors = ['#008fd5', '#fc4f30']
explode = (0, 0.1)
explode2 = (0.2, 0)

plt.title('Pie chart per CTQ-tool')
plt.tight_layout()

ax[0,0].pie(total.iloc[[0]], startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}, autopct='%1.f%%', explode=explode, shadow=True)
ax[0,0].set_title('Information and awareness purposes', fontweight='bold')
ax[0,1].pie(total.iloc[[1]],  startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}, autopct='%1.f%%', explode=explode, shadow=True)
ax[0,1].set_title('Quarantine Enforcement', fontweight='bold')
ax[1,0].pie(total.iloc[[2]],  startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}, autopct='%1.f%%', explode=explode2, shadow=True)
ax[1,0].set_title('Contact Tracing and Cross-Referencing', fontweight='bold')
ax[1,1].pie(total.iloc[[3]], startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}, autopct='%1.f%%', explode=explode, shadow=True)
ax[1,1].set_title('Voluntary provision of infection data', fontweight='bold')
ax[2,0].pie(total.iloc[[4]], startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}, autopct='%1.f%%', explode=explode2, shadow=True)
ax[2,0].set_title('Quarantine Monitoring App', fontweight='bold')
ax[2,1].pie(total.iloc[[5]], startangle=90, colors=colors, wedgeprops={'edgecolor': 'black'}, autopct='%1.f%%', explode=explode, shadow=True)
ax[2,1].set_title('QR code provided registration tracking', fontweight='bold')


fig.suptitle('Public Opinion on CTQ-measures', fontsize=20, y=1.07, fontweight='bold', x=0.37)
fig.set_figheight(10)
fig.set_figwidth(7)
fig.legend(loc='best', labels=labels, fontsize='medium')
fig.tight_layout()

fig.savefig('Opinions_ctq')

plt.show()

enter image description here

You can also try this if you want something quick:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame({'CTQ-tool':np.random.choice(['a','b','c','d'],50),
                  'opinion':np.random.choice(['acceptable','unacceptable'],50)})

fig, ax = plt.subplots(2,2)
ax = ax.flatten()
tab = pd.crosstab(df['CTQ-tool'],df['opinion'])
for i,cat in enumerate(tab.index):
    tab.loc[cat].plot.pie(ax=ax[i],startangle=90)
    ax[i].set_ylabel('')
    ax[i].set_title(cat, fontweight='bold')

enter image description here

The question is about creating pie charts with python so I think you can use another visualization library like Plotly, besides being a visualization library, Plotly is an interactive visualization library, so all your charts will be interactive!

Take a quick look at the pie chart documentation.

Now, for your question, I created a small dataset and created two pie charts to illustrate how the code would be.

  • First, import the needed libraries:
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from kaleido.scopes.plotly import PlotlyScope # this will be used to export the chart as static image
  • A toy dataset:
df = pd.DataFrame(
    {
        "CTQ-tool": [
            "Information and awareness purposes",
            "Information and awareness purposes",
            "Information and awareness purposes",
            "Information and awareness purposes",
            "Information and awareness purposes",
            "Information and awareness purposes",
            "Quarantine Enforcement",
            "Quarantine Enforcement",
            "Quarantine Enforcement",
            "Quarantine Enforcement",
            "Quarantine Enforcement",
            "Quarantine Enforcement",
        ],
        "opinion": [
            "unacceptable",
            "unacceptable",
            "unacceptable",
            "unacceptable",
            "acceptable",
            "unacceptable",
            "acceptable",
            "unacceptable",
            "acceptable",
            "unacceptable",
            "unacceptable",
            "unacceptable",
        ],
    }
)
  • Save the unique different tools:
tools = df["CTQ-tool"].unique()
  • Created aggregated data:

the following code, will group by the tool type and the by the opinion type, then create a new column counts storing the counts of each opinion type, for each tool.

df_agg = df.groupby(by=["CTQ-tool", "opinion"]).size().reset_index(name="counts")

the new data frame df_agg would be:

|      | CTQ-tool                           | opinion      | counts |
| ---: | :--------------------------------- | :----------- | -----: |
|    0 | Information and awareness purposes | acceptable   |      1 |
|    1 | Information and awareness purposes | unacceptable |      5 |
|    2 | Quarantine Enforcement             | acceptable   |      2 |
|    3 | Quarantine Enforcement             | unacceptable |      4 |
  • visualize the data (the fun part): Since this toy data has only two different tools, I created a sub-plot with onr row and two columns, but you can extend that for as many rows/columns you want.
fig = make_subplots(rows=1, cols=2, specs=[[{"type": "domain"}, {"type": "domain"}]])

then add each chart separately (you can do it with a for loop):

fig = make_subplots(rows=1, cols=2, specs=[[{"type": "domain"}, {"type": "domain"}]])

# Information and awareness purposes tool
fig.add_trace(
    go.Pie(
        values=df_agg[df_agg["CTQ-tool"] == tools[0]]["counts"],
        labels=df_agg[df_agg["CTQ-tool"] == tools[0]]["opinion"],
        pull=[0.2, 0.0],
        title=tools[0],
    ),
    1,
    1,
)

# Quarantine Enforcement tool
fig.add_trace(
    go.Pie(
        values=df_agg[df_agg["CTQ-tool"] == tools[1]]["counts"],
        labels=df_agg[df_agg["CTQ-tool"] == tools[1]]["opinion"],
        pull=[0.2, 0.0],
        title=tools[1],
    ),
    1,
    2,
)
  • update chart layout:
fig.update_layout(title_text="Public Opinion on CTQ-measures")

fig.show()
  • finally, export as static image:

now that you've prepared your data and visualized it, it's time to save it as an image. Plotly creators built a tool for this: Kaleido.

You can simply use it as the following:

scope = PlotlyScope()
fig_name = "Public-Opinion-on-CTQ-measures"
with open(f"{fig_name}.png", "wb") as f:
    f.write(scope.transform(fig, "png"))

and the figure would be:

enter image description here

Related