How to plot.bar base on other value than zero - python

Generally, the bar chart will show bottom on zero. which change the bottom, the bar move up or down.
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars: can also be len(x) sequence
p1 = plt.bar(ind, menMeans, width, bottom=0,color='#d62728')
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend('Men')
plt.savefig('bar.png')
plt.show()
while I want isn't moving up or down. the following code show the bottom of zero.
I want to show the chart based on value such as 25. if the data,such as 20, then it shows 5 below the 25 in the chart.

You can convert menMeans to numpy and then subtract the bottom. Subtracting 25, the example array would be [-5, 10, 5, 10, 2].
The x-axis can be moved to that height via ax.spines['bottom'].set_position(('data', bottom)). Similarly, the other spines can be made invisible (ax.spines[...].set_color('none')).
plt.tick_params() can remove the tick marks by setting their length to 0.
ax.text(x, y, text) can be used to set a text at a given position. Newlines can help to get an adequate padding independent of the y-axis.
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
menMeans = np.array(menMeans)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars: can also be len(x) sequence
bottom = 25
p1 = plt.bar(ind, menMeans-bottom, width, bottom=bottom, color='#d62728', label='Men')
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.tick_params(axis='both', length=0)
ax = plt.gca()
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', bottom))
ax.spines['top'].set_color('none')
for i, mean in zip(ind, menMeans):
ax.text(i, mean, f'{mean}\n' if mean >= bottom else f'\n{mean}', ha='center', va='center')
plt.legend()
plt.savefig('bar.png')
plt.show()

Related

How to set the location of bars in python matplotlib?

I use the following code to generate the following image.
import numpy as np
import matplotlib.pyplot as plt
labels = ['G1', 'G2']
men_means = [20, 35]
women_means = [25, 32]
men_std = [2, 3]
women_std = [3, 5]
width = 0.25 # the width of the bars: can also be len(x) sequence
fig, ax = plt.subplots()
ax.bar(labels, men_means, width, yerr=men_std, label='Men')
ax.bar(labels, women_means, width, yerr=women_std, bottom=men_means,
label='Women')
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.legend()
plt.show()
The two bars are too far from each other. How can I make them come closer but still be centered? Can I add some padding on the left and right?
You can manually set the positions of the bars in the x axis. You then have to add the tick labels manually:
import numpy as np
import matplotlib.pyplot as plt
men_means = [20, 35]
women_means = [25, 32]
men_std = [2, 3]
women_std = [3, 5]
width = 0.25 # the width of the bars: can also be len(x) sequence
fig, ax = plt.subplots()
# Define positions for each bar... just random here
# Change the 2nd argument to move bars around; play with bar widths also
positions = (0.5, 0.8)
# Here the first argument is the x position for the bars
ax.bar(positions, men_means, width, yerr=men_std, label='Men')
ax.bar(positions, women_means, width, yerr=women_std, bottom=men_means,
label='Women')
# Now set the ticks and the corresponding labels
labels = ('G1', 'G2')
plt.xticks(positions, labels)
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.legend()
plt.show()
Result:
You can play around with the bar widths and the 2nd argument in positions to get the distance you'd like.
Increase the width?
width = 0.6 # the width of the bars: can also be len(x) sequence
Output:

Matplotlib center/align ticks in imshow plot

I have been trying to center the x and y ticks of my imshow but without success.
The desired yticks should be: [ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] and xticks: [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] but aligned/centered. E.g. line 1 should have the 100 value exactly in the middle of the line space (middle of the yellow box/pixel).
import numpy as np
import matplotlib.pyplot as plt
X = np.random.rand(10,11)
plt.figure(dpi=130)
plt.imshow(X, cmap = 'jet', interpolation=None, extent=[5,55,1000,100], aspect='auto')
Here, the values 5 does not appear at all in the x axis.
I have also tried the following, x axis if fine but not the y axis
plt.figure(dpi=130)
X = np.random.rand(10,11)
plt.imshow(X, cmap = 'jet', interpolation=None, extent=[2.5,57.5,1000,100], aspect='auto')
ax = plt.gca()
xticks = cluster_space
yticks = space_segment
ax.set_xticks(xticks)
ax.set_yticks(yticks)
In general, to have the pixels centered, you need to set the extent to range from the lowest pixel coordinate minus half the pixel width to the highest pixel coordinate plus half the pixel width.
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
X = np.random.rand(10,11)
plt.figure()
centers = [5,55,1000,100]
dx, = np.diff(centers[:2])/(X.shape[1]-1)
dy, = -np.diff(centers[2:])/(X.shape[0]-1)
extent = [centers[0]-dx/2, centers[1]+dx/2, centers[2]+dy/2, centers[3]-dy/2]
plt.imshow(X, cmap = 'jet', interpolation=None, extent=extent, aspect='auto')
plt.xticks(np.arange(centers[0], centers[1]+dx, dx))
plt.yticks(np.arange(centers[3], centers[2]+dy, dy))
plt.show()

Stack Bar Plot with Numbers

I'm trying to graph some datas with stack bar using matplotlib.
I wrote a code and it works perfectly without numbers;
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = [20, 35, 30, 35, 27]
womenMeans = [25, 32, 34, 20, 25]
ind = np.arange(N)
width = 0.35
p1 = plt.bar(ind, menMeans, width, color='#d62728')
p2 = plt.bar(ind, womenMeans, width, bottom=menMeans)
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('Men', 'Women'))
plt.show()
And that is the picture of the graph
But I want to show every bar's numbers in the middle of them like this:
I tried to edit my code like this;
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = [20, 35, 30, 35, 27]
womenMeans = [25, 32, 34, 20, 25]
ind = np.arange(N)
width = 0.35
p1 = plt.bar(ind, menMeans, width, color='#d62728')
p2 = plt.bar(ind, womenMeans, width, bottom=menMeans)
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('Men', 'Women'))
for index, data in enumerate(menMeans):
plt.text(x=index, y=data + 1, s=f"{data}", fontdict=dict(fontsize=20))
for index, data in enumerate(womenMeans):
plt.text(x=index, y=data + 1, s=f"{data}", fontdict=dict(fontsize=20))
plt.show()
But it shows like this
Where is my fault? Can you fix it ?
You need to set the horizontalalignment='center' and verticalalignment='center' and then use the correct values for the y-offset. This is one way of doing it. You can also use short forms as ha and va
for index, data in enumerate(menMeans):
plt.text(x=index, y=data/2, s=f"{data}", ha='center',
va='center', fontsize=20)
plt.text(x=index, y=data + (womenMeans[index]/2), s=f"{womenMeans[index]}", ha='center',
va='center',fontsize=20)
plt.show()
EDIT: Answering your second question, add the following line which will give you the below figure
plt.text(x=index, y=data + womenMeans[index]+1, s=f"{data+womenMeans[index]}",
ha='center',fontsize=20)

Barplot python yticks in equal range despite value (pseudo logarithmic)

I'm trying to manipulate my y axis in a barplot. Say I have this code
import numpy as np
import matplotlib.pyplot as plt
N = 11
men_means = (-500,0,1,2,5,10,20,50,100,200,500)
men_std = (-500,0,1,2,5,10,20,50,100,200,500)
ind = np.arange(N) # the x locations for the groups
width = 0.25 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men_means, width, color='r', yerr=men_std)
women_means = (-500,0,1,2,5,10,20,50,100,200,500)
women_std = (-500,0,1,2,5,10,20,50,100,200,500)
rects2 = ax.bar(ind + width, women_means, width, color='y', yerr=women_std)
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width)
ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7'))
ax.legend((rects1[0], rects2[0]), ('Men', 'Women'))
plt.show()
It will give me a y axis that is linear. Of course I could use 'symlog' to make the y-axis logarithmic (for my original data there are negative values as well) but what I really want is to have a 'pseudo logarithmic y axis', where the ticks are set to -500, -200, -100, -50, [..],0, 1, 2 ,5, 10, 20, [...], 500, but are equally distributed along the yaxis.
Can anyone help? :-)

How to plot bar graphs with same X coordinates side by side ('dodged')

import matplotlib.pyplot as plt
gridnumber = range(1,4)
b1 = plt.bar(gridnumber, [0.2, 0.3, 0.1], width=0.4,
label="Bar 1", align="center")
b2 = plt.bar(gridnumber, [0.3, 0.2, 0.2], color="red", width=0.4,
label="Bar 2", align="center")
plt.ylim([0,0.5])
plt.xlim([0,4])
plt.xticks(gridnumber)
plt.legend()
plt.show()
Currently b1 and b2 overlap each other. How do I plot them separately like so:
There is an example in the matplotlib site. Basically, you just shift the x values by width. Here is the relevant bit:
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
menStd = (2, 3, 4, 1, 2)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig = plt.figure()
ax = fig.add_subplot(111)
rects1 = ax.bar(ind, menMeans, width, color='royalblue', yerr=menStd)
womenMeans = (25, 32, 34, 20, 25)
womenStd = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind+width, womenMeans, width, color='seagreen', yerr=womenStd)
# add some
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') )
ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') )
plt.show()
Below answer will explain each and every line of code in the simplest manner possible:
# Numbers of pairs of bars you want
N = 3
# Data on X-axis
# Specify the values of blue bars (height)
blue_bar = (23, 25, 17)
# Specify the values of orange bars (height)
orange_bar = (19, 18, 14)
# Position of bars on x-axis
ind = np.arange(N)
# Figure size
plt.figure(figsize=(10,5))
# Width of a bar
width = 0.3
# Plotting
plt.bar(ind, blue_bar , width, label='Blue bar label')
plt.bar(ind + width, orange_bar, width, label='Orange bar label')
plt.xlabel('Here goes x-axis label')
plt.ylabel('Here goes y-axis label')
plt.title('Here goes title of the plot')
# xticks()
# First argument - A list of positions at which ticks should be placed
# Second argument - A list of labels to place at the given locations
plt.xticks(ind + width / 2, ('Xtick1', 'Xtick3', 'Xtick3'))
# Finding the best position for legends and putting it
plt.legend(loc='best')
plt.show()
Sometimes could be tricky to find the right bar width. I usually use this np.diff to find the right dimension.
import numpy as np
import matplotlib.pyplot as plt
#The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = [5.5,6,7,8.5,8.9]
#Calculate optimal width
width = np.min(np.diff(indices))/3
fig = plt.figure()
ax = fig.add_subplot(111)
# matplotlib 3.0 you have to use align
ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin',align='edge')
ax.bar(indices,menMeans,width,color='r',label='Ymax',align='edge')
ax.set_xlabel('Test histogram')
plt.show()
# matplotlib 2.0 (you could avoid using align)
# ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin')
# ax.bar(indices,menMeans,width,color='r',label='Ymax')
This is the result:
What if my indices on my x axis are nominal values like names:
#
import numpy as np
import matplotlib.pyplot as plt
# The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = range(len(womenMeans))
names = ['Asian','European','North Amercian','African','Austrailian','Martian']
# Calculate optimal width
width = np.min(np.diff(indices))/3.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(indices-width/2.,womenMeans,width,color='b',label='-Ymin')
ax.bar(indices+width/2.,menMeans,width,color='r',label='Ymax')
#tiks = ax.get_xticks().tolist()
ax.axes.set_xticklabels(names)
ax.set_xlabel('Test histogram')
plt.show()
Here are two examples of creating a side-by-side bar chart when you have more than two "categories" in a group.
Manual Method
Manually set the position and width of each bar.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = np.array([.01, .05, .10, .25])
# Coin values times *n* coins
# This controls how many bars we get in each group
values = [worth*i for i in range(1,6)]
n = len(values) # Number of bars to plot
w = .15 # With of each column
x = np.arange(0, len(coins)) # Center position of group on x axis
for i, value in enumerate(values):
position = x + (w*(1-n)/2) + i*w
plt.bar(position, value, width=w, label=f'{i+1}x')
plt.xticks(x, coins);
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.legend()
Pandas Method
If you put the data into a pandas DataFrame, pandas will do the hard stuff for you.
import pandas as pd
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = [0.01, 0.05, 0.10, 0.25]
df = pd.DataFrame(worth, columns=['1x'], index=coins)
df['2x'] = df['1x'] * 2
df['3x'] = df['1x'] * 3
df['4x'] = df['1x'] * 4
df['5x'] = df['1x'] * 5
from matplotlib import ticker
import matplotlib.pyplot as plt
df.plot(kind='bar')
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.gca().xaxis.set_tick_params(rotation=0)
Pandas creates a similar figure...

Categories