Set individual wedge hatching for pandas pie chart - python

I am trying to make pie charts where some of the wedges have hatching and some of them don't, based on their content. The data consists of questions and yes/no/in progress answers, as shown below in the MWE.
import pandas as pd
import matplotlib.pyplot as plt
raw_data = {'Q1': ['IP', 'IP', 'Y/IP', 'Y', 'IP'],
'Q2': ['Y', 'Y', 'Y', 'Y', 'N/IP'],
'Q3': ['N/A', 'IP', 'Y/IP', 'N', 'N']}
df = pd.DataFrame(raw_data, columns = ['Q1', 'Q2', 'Q3'])
df= df.astype('string')
colors={'Y':'green',
'Y/IP':'greenyellow',
'IP':'orange',
'N/IP':'gold',
'N':'red',
'N/A':'grey'
}
for i in df.columns:
pie = df[i].value_counts().plot.pie(colors=[colors[v] for v in df[i].value_counts().keys()])
fig = pie.get_figure()
fig.savefig("D:/windows/"+i+"test.png")
fig.clf()
However, instead of greenyellow and gold I am trying to make the wedges green with yellow hatching, and yellow with red hatching, like so (note the below image does not match the data from the MWE):
I had a look online and am aware I will likely have to split the pie(s) into individual wedges but can't seem to get that to work alongside the pandas value counts. Any help would be massively appreciated. Thanks!

This snippet shows how to add hatching in custom colors to a pie chart. You can extract the Pandas valuecount - this will be a Series - then use it with the snippet I have provided.
I have added the hatch color parameter as a second parameter in the color dictionary:
import matplotlib.pyplot as plt
colors={'Y' :['green', 'lime'],
'IP': ['orange', 'red'],
'N' : ['red', 'cyan']}
labels=['Y', 'N', 'IP']
wedges, _ = plt.pie(x=[1, 2, 3], labels=labels)
for pie_wedge in wedges:
pie_wedge.set_edgecolor(colors[pie_wedge.get_label()][1])
pie_wedge.set_facecolor(colors[pie_wedge.get_label()][0])
pie_wedge.set_hatch('/')
plt.legend(wedges, labels, loc="best")
plt.show()
The result looks like so:

Related

Bar with different color

I am working with matplotlib and below you can see my data and my plot.
data = {
'type_sale': ['g_1','g_2','g_3','g_4','g_5','g_6','g_7','g_8','g_9','g_10'],
'open':[70,20,24,150,80,90,60,90,20,20],
}
df = pd.DataFrame(data, columns = ['type_sale',
'open',
])
df.plot(x='type_sale', kind='bar', title='Bar Graph')
So now I want to put a different color (color = 'red') on the fourth bar. I tryed but I colored all instead only one.
So can anybody help me how to solve this ?
The ax.bar() method returns a list of bars that you can then manipulate, in your case with .set_color():
import matplotlib.pyplot as plt
f=plt.figure()
ax=f.add_subplot(1,1,1)
## bar() will return a list of bars
barlist = ax.bar([1,2,3,4], [1,2,3,4])
barlist[3].set_color('r')
plt.show()
You can try this solution
# libraries
import numpy as np
import matplotlib.pyplot as plt
# create a dataset
height = [3, 12, 5, 18, 45]
bars = ('A', 'B', 'C', 'D', 'E')
x_pos = np.arange(len(bars))
# Create bars with different colors
plt.bar(x_pos, height, color=['black', 'red', 'green', 'blue', 'cyan'])
# Create names on the x-axis
plt.xticks(x_pos, bars)
# Show graph
plt.show()
Here is the documentation link
Link

How to add x-axis tick labels in python bar chart

I am trying to plot a python bar chart. Here is my code and an image of my bar chart. The problems I am facing are:
I want to write name of each category of bar chart on the x-axis as CAT1, CAT2, CAT3, CAT4. Right now it's printing 0, 1, 2 on the x-axis.
I want to change the purple color of the bar chart.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame([['CAT1',9,3,24,46,76], ['CAT2', 48,90,42,56,68], ['CAT3', 31,24,28,11,90],
['CAT4', 76,85,16,65,91]],
columns=['metric', 'A', 'B', 'C', 'D', 'E'])
df.plot(
kind='bar',
stacked=False
)
plt.legend(labels=['A', 'B', 'C', 'D', 'E'], ncol=4, loc='center', fontsize=15, bbox_to_anchor=(0.5, 1.06))
plt.show()
By default, matplotlib recognizes the index of your dataframe as x-labels.
I suggest you to add the following to make the column metric as the index, which allows matplotlib to automatically add label for you.
df = df.set_index('metric')

Altair: Setting Raw Color Values Messes Up SortField

I'm trying to create an Altair barplot with the bars sorted by 'count' and then apply raw color values to the bars.
import pandas as pd
import altair as alt
# Dummy data
df = pd.DataFrame({'fruit': ['apple', 'orange', 'blueberry', 'pear', 'grape', 'kiwi', 'strawberry', 'lychee'],
'count': [2, 4, 1, 7, 9, 12, 16, 35],
'label': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
'colors': ['#4c78a8', '#f58518', '#e45756', '#72b7b2', '#54a24b', '#eeca3b', '#b279a2',
'#9d755d']})
def make_alt_chart(df):
g = alt.Chart(df).encode(
x=alt.X('count'),
y=alt.Y('fruit', sort=alt.SortField(field='count', order='descending'))
).properties(
width=700,
height=650,
)
bars = g.mark_bar(
size=60,
).encode(
color=alt.Color('colors', sort=alt.SortField('count', order='descending'),
scale=None, legend=None)
).properties(height=alt.Step(75))
text = g.mark_text(
align='center',
baseline='middle',
dx=28
).encode(
text='label'
).interactive()
return (bars + text)
fruits = make_alt_chart(df)
fruits
Adding sort=alt.SortField(field='count', order='descending') to y= gets the chart sorted how I want, but when I add color=alt.Color('colors', sort=alt.SortField('count', order='descending'), scale=None, legend=None) to bars, the order on the y axis is no longer sorted by 'count'.
This is what the fruits chart looks like after running the above code:
This is what my desired output would look like, but with the custom colors applied:
If there's an easier way to set custom colors in Altair please let me know.
Note: The color hex values are the tableau10 scheme but dropping the pink shade.
I've reviewed these resources but haven't been able to figure it out:
altair.Color Documentation
Altair Customizing Visualizations Docs
Vega Color Schemes Docs
Altair issues: sort not working on alt.Y
SO: Setting constant label color for bar chart
SO: Sorting based on alt.Color
If you look in the Javascript console, you see that the renderer is outputting this warning:
WARN Domains that should be unioned has conflicting sort properties. Sort will be set to true.
The relevant Vega-Lite issue suggests the workaround; replace
y=alt.Y('fruit', sort=alt.SortField(field='count', order='descending'))
with
y=alt.Y('fruit', sort=alt.EncodingSortField(field='count', order='descending', op='sum'))
This is the result:

Pandas bar chart with unequal groups

I have data with a hierarchical structure and want to create a plot with groups of bars.
import pandas as pd
data = [
['alpha', 'x', 1],
['alpha', 'y', 2],
['alpha', 'z', 2],
['beta', 'x', 3],
['beta', 'z', 4]]
df = pd.DataFrame(data, columns=['P','Q','R'])
df.pivot('P','Q','R').plot.bar(rot=0)
This code produces:
How could I:
Eliminate the space for the missing bar, i.e. accommodate groups with different numbers of bars?
Make all the alphas blue and the betas orange, i.e. cycle the colors by group rather than within groups?
What if you create the plot "manually"? You can use loc to filter. Then plot on the same figure.
the magic for the space happens by using the index values. notice in beta I add +1 to the index to create that extra space. I then combine both indexes in xticks and then simply use df['Q'] as the labels.
plt.bar(data=df.loc[df['P']=='alpha'], x=df.loc[df['P']=='alpha'].index, height='R', label='alpha')
plt.bar(data=df.loc[df['P']=='beta'], x=df.loc[df['P']=='beta'].index+1, height='R', label='beta')
plt.xticks(df.loc[df['P']=='alpha'].index.tolist() + list(df.loc[df['P']=='beta'].index+1),df['Q'].tolist())
plt.legend()
I am not sure to get rid of empty cells but you can use a stacked parameter to get the output and also yes you can pass the color array to bar method which will display color accordingly.
import pandas as pd
data = [
['alpha', 'x', 1],
['alpha', 'y', 2],
['alpha', 'z', 2],
['beta', 'x', 3],
['beta', 'z', 4]]
df = pd.DataFrame(data, columns=['P','Q','R'])
df.pivot(index='P',columns='Q',values='R').plot.bar(rot=0, stacked=True,color = ['blue', 'green', 'red'])
I hope it helps.
This is inspired by #MattR's answer, which showed me that plotting bars from scratch is not rocket science. Pandas groupby() seems to be a good tool for this.
In the end I prefer it without extra space between groups.
labels = []
for g, grp in df.groupby('P'):
plt.bar(grp.index, grp.R, label=g)
labels.extend(grp.Q)
plt.xticks(df.index, labels)
plt.legend()

Add hash pattern to a seaborn bar plot [duplicate]

This question already has answers here:
Is it possible to add hatches to each individual bar in seaborn.barplot?
(2 answers)
Closed 5 months ago.
I have a bar plot created using seaborn. For example, the plot can be created as follows:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
data1 = pd.DataFrame(np.random.rand(17,3), columns=['A','B','C']).assign(Location=1)
data2 = pd.DataFrame(np.random.rand(17,3)+0.2, columns=['A','B','C']).assign(Location=2)
data3 = pd.DataFrame(np.random.rand(17,3)+0.4, columns=['A','B','C']).assign(Location=3)
cdf = pd.concat([data1, data2, data3])
mdf = pd.melt(cdf, id_vars=['Location'], var_name=['Letter'])
ax = sns.barplot(x="Location", y="value", hue="Letter", data=mdf, errwidth=0)
ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.2), ncol=3, fancybox=True, shadow=True)
plt.show()
This gives the following plot
I would like to do customize the chart as follows:
Remove the face color (set it to a white color)
Add a hash pattern to the image to distinguish the groups
How can this be achieved?
Removing the face color is easy, just do ax.set_facecolor('w'), although this will make the grid lines invisible. I'd recommend using sns.set_style('whitegrid') before plotting instead, you'll get a white background with only horizontal grid lines in grey.
As for the different has patterns, this is a little trickier with seaborn, but it can be done. You can pass the hatch keyword argument to barplot, but it'll be applied to each bar, which doesn't really help you distinguish them. Unfortunately, passing a dictionary here doesn't work. Instead, you can iterate over the bars after they're constructed to apply a hatch. You'll have to calculate the number of locations, but this is pretty straightforward with pandas. It turns out that seaborn actually plots each hue before moving on to the next hue, so in your example it would plot all blue bars, then all green bars, then all red bars, so the logic is pretty straightforward:
num_locations = len(mdf.Location.unique())
hatches = itertools.cycle(['/', '//', '+', '-', 'x', '\\', '*', 'o', 'O', '.'])
for i, bar in enumerate(ax.patches):
if i % num_locations == 0:
hatch = next(hatches)
bar.set_hatch(hatch)
So the full script is
import itertools
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
data1 = pd.DataFrame(np.random.rand(17, 3), columns=['A', 'B', 'C']).assign(Location=1)
data2 = pd.DataFrame(np.random.rand(17, 3) + 0.2, columns=['A', 'B', 'C']).assign(Location=2)
data3 = pd.DataFrame(np.random.rand(17, 3) + 0.4, columns=['A', 'B', 'C']).assign(Location=3)
cdf = pd.concat([data1, data2, data3])
mdf = pd.melt(cdf, id_vars=['Location'], var_name=['Letter'])
ax = sns.barplot(x="Location", y="value", hue="Letter", data=mdf, errwidth=0)
num_locations = len(mdf.Location.unique())
hatches = itertools.cycle(['/', '//', '+', '-', 'x', '\\', '*', 'o', 'O', '.'])
for i, bar in enumerate(ax.patches):
if i % num_locations == 0:
hatch = next(hatches)
bar.set_hatch(hatch)
ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1), ncol=3, fancybox=True, shadow=True)
plt.show()
And I get the output
Reference for setting hatches and the different hatches available: http://matplotlib.org/examples/pylab_examples/hatch_demo.html
Note: I adjusted your bbox_to_anchor for the legend because it was partially outside of the figure on my computer.

Categories