Multiple Bar graphs in single figure matplotlib python - python

I want to plot two bar graphs in a single figure as you can see nrows=1, ncols=2, I have created 2 columns but unable to utilize it, however, the code works fine the only problem is, it prints two bar graphs separately and I want them to be printed in a single graph.
Looking forward to a friend in need is a friend indeed who can guide me. Thanks
Required_File
Desired_Output
Current Program Output
import pandas as pandas
import csv
import numpy as np
import matplotlib.pyplot as plt
width=0.2
fig,ax= plt.subplots(nrows=1, ncols=2, figsize=(14, 5), dpi=100)
colors = ['red', 'yellow', 'blue', 'green']
labels = ('Cyber incident', 'Theft of paperwork or data storagedevice', 'Rogue employee', 'Social engineering / impersonation')
identify=['Health service providers','Finance','Education','Legal,accounting & management services','Personal services']
df = pd.read_csv('Malicious_or_criminal_attacks_breakdown-Top_five_industry_sectors_July-Dec-2019.csv', index_col=0, engine='python') # opening the file
df = pd.DataFrame(df)
data = df.values.tolist()
# Set position of bar on X axis
# -----------------------------------
br = [np.arange(len(data[0]))]
for i in range(len(data)):
br.append([x + width for x in br[i-1]])
br.append([x + width for x in br[i-1]])
for i in range(len(br)):
if(i==4):
break
plt.bar(br[i], data[i], color =colors[i], width = width,
edgecolor ='grey', label =labels[i])
# Adding Xticks
plt.ylabel('Number Of Attacks', fontweight ='bold', fontsize = 15)
plt.title('Type of attack by top five industry sectors')
plt.xticks([r + width for r in range(len(data[i]))],
identify[:],rotation=89.5)
plt.legend()
plt.show()
# -----------------------------------------------
arr = np.array(data)
n_groups, N = arr.shape
ind=np.arange(N)
p=[]
for i in range(len(data)):
p.append(plt.bar(ind, data[i], width))
# for
plt.ylabel('Number Of Attacks', fontweight ='bold', fontsize = 15)
plt.title('Type of attack by top five industry sectors')
plt.xticks(ind+width/2, identify[:],rotation=89.5)
plt.yticks(np.arange(0, 80, 20))
for i in range (len(p)-1):
plt.legend(labels[:])
plt.show()

plt.bar() creates a new figure each iteration. You should reuse your subplot axes ax like ax[i].bar(), something like:
for i in range(len(br)):
if(i == 4):
break
ax[i].bar(br[i], data[i], color=colors[i], width=width,
edgecolor='grey', label=labels[i])
ax[i].set_ylabel('Number of Attacks', fontweight='bold', fontsize=15)
ax[i].set_title('Type of attack by top five industry sectors')
ax[i].set_xticks([r + width for r in range(len(data[i]))],
identify[:], rotation=89.5)
I'm not clear on why the loop tests for i==4. Note that your subplot grid is 1x2, so i can only be 0 or 1, otherwise this will break.

Related

Seaborn Align twinx and x Axis

I am trying to align X axis with its twin but I'm not finding a way to do it.
Here is my code
# Initialize the figure
plt.figure(figsize=(16, 10))
# Adding a title
plt.title(f'Client Retention Quarters: Monthly Cohorts', fontsize = 14)
# Creating the heatmap
sns.heatmap(retention, annot = True,vmin = 0, vmax =30,cmap="flare", fmt='g')
plt.ylabel('Cohort Quarter')
plt.xlabel('')
plt.yticks( rotation='360')
#Twinx
ax2 = plt.twiny()
ax2.set_xticks(range(0,len(x2)))
ax2.set_xticklabels(labels=x2)
ax2.spines['top'].set_position(('axes', -0.10))
plt.show()
And here is the output:
I want to align the percentages with the x ticks.
Is it possible?
You can use the below updated code. See if this works. Note that I have used random data for retention and x2. Basically, the main change it to get the xlim()s for both axes and then adjust it (see lambda f) so that the ticks align. Finally use set_major_locator() to fix the points. Hope this is what you are looking for...
retention = np.random.rand(10, 12) ##My random data
# Initialize the figure
plt.figure(figsize=(16, 10))
# Adding a title
plt.title(f'Client Retention Quarters: Monthly Cohorts', fontsize = 14)
# Creating the heatmap
ax=sns.heatmap(retention, annot = True,vmin = 0, vmax =30,cmap="flare", fmt='g') ## Note I am assigning to ax
plt.ylabel('Cohort Quarter')
plt.xlabel('')
plt.yticks( rotation='360')
x2 = np.around(np.linspace(1, 25, 12),2)
#Twinx
ax2 = ax.twiny()
#ax2.set_xticks(range(0,len(x2))) ## Commented as not required
#ax2.set_xticklabels(labels=x2) ## Commented as not required
## New code here ##
import matplotlib.ticker
l = ax.get_xlim()
l2 = ax2.get_xlim()
f = lambda y : l2[0]+(y-l[0])/(l[1]-l[0])*(l2[1]-l2[0]) ##Add delta to each tick
ticks = f(ax.get_xticks())
ax2.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(ticks)) ##Set the ticks
ax2.spines['top'].set_position(('axes', -0.10))
plt.show()

What is wrong with my multiple line graph plotting?

I am attempting to plot multiple line graphs in a graph table itself. However, I run into an error that mentioned:
No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
Not only this happened but my legend tables of the 3 lines don't merge together and my X-axis does not show the months but random numbers from my dataframe. Here is my code and graph result to look through.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_excel (r'C:\Users\admin\Desktop\Question Folder\Sales of top 30 customers.xlsx')
#Refine and adjust the dataframe for suitable manipulation
df = df.drop('Unnamed: 0', axis = 1)
df = df.iloc[2: , :]
row_detail = df.head(1).values.tolist()
row_detail = row_detail[0]
a = df.iloc[-3:, :].values.tolist()
a = a[0]
df.columns = row_detail
df = df.iloc[1:, :]
print(df) # This is for checking purpose
# This creates a dataframe needed for the practice
df1 = df.iloc[:3]
# This is to plot a line graph from df1
df_chosen = df1
a = 0
# Turning data row of a customer into a list
data_row_1 = df_chosen.iloc[a].values.tolist()
data_row_2 = df_chosen.iloc[a + 1].values.tolist()
data_row_3 = df_chosen.iloc[a + 2].values.tolist()
date = data_row_1[1:]
cus_1 = data_row_1[0]
cus_2 = data_row_2[0]
cus_3 = data_row_3[0]
y1 = data_row_1[1:]
y2 = data_row_2[1:]
y3 = data_row_3[1:]
x = np.arange(len(date)) # the label locations
width = 0.60 # the width of the bars
fig, ax = plt.subplots()
# Increase size of plot in jupyter
plt.rcParams["figure.figsize"] = (20,15)
plt.rcParams.update({'font.size':25})
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_xlabel('Months', fontsize=30)
ax.set_ylabel('Sales', fontsize=30)
ax.set_title('Monthly Sales from ' + cus_1 +", " + cus_2+ " and " + cus_3, fontsize=30)
ax.set_xticks(x, date)
ax.set_ylim(bottom = 0, top = 1000)
legend1 = plt.legend(())
ax.legend(loc='best', fontsize=30)
plt.grid(True)
# set up the 1st line graph
ax.plot(x, y1, "r", label = cus_1, marker='x')
#ax.set_yticks(
ax.grid(True) # turn on grid #1
ax.set_ylim(bottom = 0, top = 1000)
ax.legend(loc='upper left', fontsize=25)
ax2 = ax.twinx()
ax2.plot(x, y2, "b", label= cus_2, marker='x')
ax2.set_yticks([])
ax2.grid(False) # turn off grid #2
ax2.set_ylim(bottom = 0, top = 10000)
ax2.legend(loc='upper left', fontsize=25)
ax3 = ax2.twinx()
ax3.plot(x, y3, "g", label= cus_3, marker='x')
ax3.set_yticks([])
ax3.grid(False) # turn off grid #2
ax3.set_ylim(bottom = 0, top = 10000)
ax3.legend(loc='upper left', fontsize=25)
I just need to understand and know the solutions for the following:
Why is the X-axis not showing the months' names?
Why is the 3 separate legend tables not connected together?
How do I avoid the 'No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.' error warning?
Hope to receive a favorable reply soon. :)
Edit notice: Here is the dataframe used for this problem:

Python matplotlib "No handles with labels found to put in legend" Error when doing subplots

I am facing the issue No handles with labels found to put in legend it's due to subplot.legend()
this line of code.
In this code I am making two bar graphs simultaneously the desire output and current program output is given below.
Desired_Output
Cureent_Output
I strongly believe I am missing a small thing.
import numpy as np
import matplotlib.pyplot as plt
#reading data
data = pd.read_csv('Malicious_or_criminal_attacks_breakdown-Top_five_industry_sectors_July-Dec-2019.csv',index_col=0,engine='python')
df = pd.DataFrame(data)
#df list for data
df.values.tolist()
#construction of group bar chart
labels = ('Cyber incident', 'Theft of paperwork or data storagedevice', 'Rogue employee', 'Social engineering / impersonation')
colors = ['red', 'yellow', 'blue', 'green']
identify=['Health service providers','Finance','Education','Legal,accounting & management services','Personal services']
data = df.values.tolist()
arr = np.array(data)
n_groups, n_colors = arr.shape
width = 0.2
x_pos = np.arange(n_colors)
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(14, 5), dpi=100)
for i in range(n_groups):
plt.bar(x_pos + i*width, arr[i, :], width, align='center', label=labels[i], color=colors[i])
for subplot in ax:
subplot.set_xlabel("the top five industry sectors")
subplot.set_ylabel("Number of attack")
subplot.set_title("Type of attack by top five industry sectors")
subplot.set_xticks(x_pos+width/2)
subplot.set_xticklabels(identify,rotation=89.5)
subplot.legend()
If you want to have a legend for each of the multiple subplots, you can set up the axs, create each graph in an array, and then set up a legend for it.
fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(14, 5), dpi=100)
for i in range(n_groups):
axs[0].bar(x_pos + i*width, arr[i, :], width, align='center', label=labels[i], color=colors[i])
axs[1].bar(x_pos + i*width, arr[i, :], width, align='center', label=labels[i], color=colors[i])
axs[0].legend()
axs[1].legend()
for subplot in ax:
subplot.set_xlabel("the top five industry sectors")
subplot.set_ylabel("Number of attack")
subplot.set_title("Type of attack by top five industry sectors")
subplot.set_xticks(x_pos[:4]+width/2)
subplot.set_xticklabels(labels, rotation=89.5)
# subplot.legend()

Plotting a Bar Chart on matplotlib

How can I plot a horizontal bar chart with the values at the end of the bar, Something similar to this
I tried this
plt.barh(inc.index,inc)
plt.yticks(inc.index)
plt.xticks(inc);
plt.xlabel("Order Count")
plt.ylabel("Date")
Bar chart
The answer can be found here:
How to display the value of the bar on each bar with pyplot.barh()?
Just add the for loop as cphlewis said:
for i, v in enumerate(inc):
ax.text(v + 3, i + .25, str(v), color='blue', fontweight='bold')
plt.show()
Here is the code that I tried for your situation:
import matplotlib.pyplot as plt
import numpy as np
inc = [12, 25, 50, 65, 40, 45]
index = ["2019-10-31", "2019-10-30", "2019-10-29", "2019-10-28", "2019-10-27", "2019-10-26"]
fig, ax = plt.subplots()
ax.barh(index,inc, color='black')
plt.yticks(index)
plt.xticks(inc);
plt.xlabel("Order Count")
plt.ylabel("Date")
# Set xticks
plt.xticks(np.arange(0, max(inc)+15, step=10))
# Loop for showing inc numbers in the end of bar
for i, v in enumerate(inc):
ax.text(v + 1, i, str(v), color='black', fontweight='bold')
plt.show()
Plot looks like this:
To generate a plot with values superimposed, run:
ax = inc.plot.barh(xticks=inc, xlim=(0, 40));
ax.set_xlabel('Order Count')
ax.set_ylabel('Date')
for p in ax.patches:
w = p.get_width()
ax.annotate(f' {w}', (w + 0.1, p.get_y() + 0.1))
Note that I set xlim with upper limit slightly above the
maximum Order Count, to provide the space for annotations.
For a subset of your data I got:
And one more impovement:
As I see, your data is a Series with a DatetimeIndex.
So if you want to have y label values as dates only (without
00:00:00 for hours), convert the index to string:
inc.index = inc.index.strftime('%Y-%m-%d')
like I did, generating my plot.

For loop to create multiple histogram png files

I am not sure as to why this happens. Maybe it is just a simple mistake that I cannot see, but by using this code:
for filename in glob.glob('/Users/jacob/Desktop/MERS/new/NOT COAL/gensets/statistics_per_lgu/per_lgu_files/*.csv'):
base = os.path.basename(filename)
name = os.path.splitext(base)[0]
df = pd.read_csv(filename)
# Show 4 different binwidths
for i, binwidth in enumerate([10, 20, 30, 40]):
# Set up the plot
ax = plt.subplot(2, 2, i + 1)
plt.subplots_adjust( wspace=0.5, hspace=0.5)
# Draw the plot
ax.hist(df['New Capacity based on 0.8 PF'], bins=binwidth,
color='red', edgecolor='black',alpha=0.5)
# Title and labels
ax.set_title('Histogram with Binwidth = %d' % binwidth, size=10)
ax.set_xlabel('Capacity', size=11)
ax.set_ylabel('Frequency count', size=11)
ax.axvline(x=df['New Capacity based on 0.8 PF'].median(), linestyle='dashed', alpha=0.3, color='blue')
min_ylim, max_ylim = plt.ylim()
ax.text(x=df['New Capacity based on 0.8 PF'].median(),y= max_ylim*0.9, s='Median', alpha=0.7, color='blue',fontsize = 12)
ax.axvline(x=df['New Capacity based on 0.8 PF'].mean(), linestyle='dashed', alpha=0.9, color='green')
min_ylim, max_ylim = plt.ylim()
ax.text(x=df['New Capacity based on 0.8 PF'].mean(),y= max_ylim*0.5, s='Mean', alpha=0.9, color='green',fontsize = 12)
plt.tight_layout()
plt.grid(True)
plt.savefig('/Users/jacob/Documents/Gensets_gis/historgrams/per_lgu_files/{}.png'.format(name))
I get all files created like this attached photo here.
Any ideas as to what I've done wrong?
Thanks in advance.
attached photo of one histogram output
My desired result would be something like this.
Desired output
It doesn't create new subplots but it use previous ones and then it draw new plots on old plots so you have to use clear subplot before you draw new histogram.
ax = plt.subplot(2, 2, i + 1)
ax.clear()
Example code. It gives desired output but if you remove `ax.clear() then first image will be OK but you get new plot with old plots on second and third image.
import os
import pandas as pd
import matplotlib.pyplot as plt
import random
for n in range(3):
filename = f'example_data_{n}.csv'
base = os.path.basename(filename)
name = os.path.splitext(base)[0]
df = pd.DataFrame({'New Capacity based on 0.8 PF': random.choices(list(range(1000)), k=100)})
data = df['New Capacity based on 0.8 PF']
median = data.median()
mean = data.mean()
# Show 4 different binwidths
for i, binwidth in enumerate([10, 20, 30, 40]):
# Set up the plot
ax = plt.subplot(2,2,i+1)
ax.clear() # <--- it removes previous histogram
plt.subplots_adjust( wspace=0.5, hspace=0.5)
# Draw the plot
ax.hist(data , bins=binwidth, color='red', edgecolor='black',alpha=0.5)
# Title and labels
ax.set_title('Histogram with Binwidth = %d' % binwidth, size=10)
ax.set_xlabel('Capacity', size=11)
ax.set_ylabel('Frequency count', size=11)
min_ylim, max_ylim = plt.ylim()
ax.axvline(x=median, linestyle='dashed', alpha=0.3, color='blue')
ax.text(x=median, y= max_ylim*0.9, s='Median', alpha=0.7, color='blue',fontsize = 12)
ax.axvline(x=mean, linestyle='dashed', alpha=0.9, color='green')
ax.text(x=mean, y= max_ylim*0.5, s='Mean', alpha=0.9, color='green',fontsize = 12)
plt.tight_layout()
plt.grid(True)
plt.savefig('{}.png'.format(name))

Categories