Histogram bars overlapping matplotlib - python

I am able to build the histogram I need. However, the bars overlap over one another.
As you can see I changed the width of the bars to 0.2 but it still overlaps. What is the mistake I am doing?
from matplotlib import pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
from random import randrange
color = ['r', 'b', 'g','c','m','y','k','darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred']
label = ['2','6','10','14','18','22','26','30','34','38','42','46']
file_names = ['a','b','c']
diff = [[randrange(10) for a in range(0, len(label))] for a in range(0, len(file_names))]
print diff
x = diff
name = file_names
y = zip(*x)
pos = np.arange(len(x))
width = 1. / (1 + len(x))
fig, ax = plt.subplots()
for idx, (serie, color,label) in enumerate(zip(y, color,label)):
ax.bar(pos + idx * width, serie, width, color=color, label=label)
ax.set_xticks(pos + width)
plt.xlabel('foo')
plt.ylabel('bar')
ax.set_xticklabels(name)
ax.legend()
plt.savefig("final" + '.eps', bbox_inches='tight', pad_inches=0.5,dpi=100,format="eps")
plt.clf()
Here is the graph:

As you can see in the below example, you can easily get non-overlapping bars using a heavily simplified version of your plotting code. I'd suggest you to have a closer look at whether x and y really are what you expect them to be. (And that you try to simplify your code as much as possible when you are looking for an error in the code.)
Also have a look at the computation of the width of the bars. You appear to use the number of subjects for this, while it should be the number of bars per subject instead.
Have a look at this example:
import numpy as np
import matplotlib.pyplot as plt
subjects = ('Tom', 'Dick', 'Harry', 'Sally', 'Sue')
# number of bars per subject
n = 5
# y-data per subject
y = np.random.rand(n, len(subjects))
# x-positions for the bars
x = np.arange(len(subjects))
# plot bars
width = 1./(1+n) # <-- n.b., use number of bars, not number of subjects
for i, yi in enumerate(y):
plt.bar(x+i*width, yi, width)
# add labels
plt.xticks(x+n/2.*width, subjects)
plt.show()
This is the result image:
For reference:
http://matplotlib.org/examples/api/barchart_demo.html
http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.bar

The problem is that the width of your bars is calculated from the three subjects, not the twelve bars per subject. That means you're placing multiple bars at each x-position. Try swapping in these lines where appropriate to fix that:
n = len(x[0]) # New variable with the right length to calculate bar width
width = 1. / (1 + n)
ax.set_xticks(pos + n/2. * width)

Related

Creating subplots with equal axis scale, Python, matplotlib

I am plotting seismological data and am creating a figure featuring 16 subplots of different depth slices. Each subplot displays the lat/lon of the epicenter and the color is scaled to its magnitude. I am trying to do two things:
Adjust the scale of all plots to equal the x and y min and max for the area selected. This will allow easy comparison across the plots. (so all plots would range from xmin to xmax etc)
adjust the magnitude colors so they also represent the scale (ie colors represent all available points not just the points on that specific sub plot)
I have seen this accomplished a number of ways but am struggling to apply them to the loop in my code. The data I am using is here: Data.
I posted my code and what the current output looks like below.
import matplotlib.pyplot as plt
import pandas as pd
eq_df = pd.read_csv(eq_csv)
eq_data = eq_df[['LON', 'LAT', 'DEPTH', 'MAG']]
nbound = max(eq_data.LAT)
sbound = min(eq_data.LAT)
ebound = max(eq_data.LON)
wbound = min(eq_data.LON)
xlimit = (wbound, ebound)
ylimit = (sbound, nbound)
magmin = min(eq_data.MAG)
magmax = max(eq_data.MAG)
for n in list(range(1,17)):
km = eq_data[(eq_data.DEPTH > n - 1) & (eq_data.DEPTH <= n)]
plt.subplot(4, 4, n)
plt.scatter(km["LON"], km['LAT'], s = 10, c = km['MAG'], vmin = magmin, vmax = magmax) #added vmin/vmax to scale my magnitude data
plt.ylim(sbound, nbound) # set y limits of plot
plt.xlim(wbound, ebound) # set x limits of plot
plt.tick_params(axis='both', which='major', labelsize= 6)
plt.subplots_adjust(hspace = 1)
plt.gca().set_title('Depth = ' + str(n - 1) +'km to ' + str(n) + 'km', size = 8) #set title of subplots
plt.suptitle('Magnitude of Events at Different Depth Slices, 1950 to Today')
plt.show()
ETA: new code to resolve my issue
In response to this comment on the other answer, here is a demonstration of the use of sharex=True and sharey=True for this use case:
import matplotlib.pyplot as plt
import numpy as np
# Supply the limits since random data will be plotted
wbound = -0.1
ebound = 1.1
sbound = -0.1
nbound = 1.1
fig, axs = plt.subplots(nrows=4, ncols=4, figsize=(16,12), sharex=True, sharey=True)
plt.xlim(wbound, ebound)
plt.ylim(sbound, nbound)
for n, ax in enumerate(axs.flatten()):
ax.scatter(np.random.random(20), np.random.random(20),
c = np.random.random(20), marker = '.')
ticks = [n % 4 == 0, n > 12]
ax.tick_params(left=ticks[0], bottom=ticks[1])
ax.set_title('Depth = ' + str(n - 1) +'km to ' + str(n) + 'km', size = 12)
plt.suptitle('Magnitude of Events at Different Depth Slices, 1950 to Today', y = 0.95)
plt.subplots_adjust(wspace=0.05)
plt.show()
Explanation of a couple things:
I have reduced the horizontal spacing between subplots with subplots_adjust(wspace=0.05)
plt.suptitle does not need to be (and should not be) in the loop.
ticks = [n % 4 == 0, n > 12] creates a pair of bools for each axis which is then used to control which tick marks are drawn.
Left and bottom tick marks are controlled for each axis with ax.tick_params(left=ticks[0], bottom=ticks[1])
plt.xlim() and plt.ylim() need only be called once, before the loop
Finally got it thanks to some help above and some extended googling.
I have updated my code above with notes indicating where code was added.
To adjust the limits of my plot axes I used:
plt.ylim(sbound, nbound)
plt.xlim(wbound, ebound)
To scale my magnitude data across all plots I added vmin, vmax to the following line:
plt.scatter(km["LON"], km['LAT'], s = 10, c = km['MAG'], vmin = magmin, vmax = magmax)
And here is the resulting figure:

"Exploding" wedges of pie chart when plotting them on a map (Python, matplotlib)

So I've successfully plotted pie charts on a map as markers using ax.scatter, but I'm having trouble with some of the wedges "exploding" out of the pie chart. I can't seem to find the reason for this in my code, and have been unable to find an explanation anywhere online. This code is based on the example here , which a colleague has also used and resulted in normal, uniform pie charts. Between us we can't find the issue, and no errors occur.
The code:
import numpy as np
import math
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap, cm
australia_data = np.zeros((24,12))
colors = ['red','yellow','blue','mediumorchid']
#pie chart locations
xlon=[146.7,166,101.6,137.4,145.1,113.6,169.7,113.3,176.0,139.6,148.9,124.2,132.4,142.0,129.6,148.0,116.5,142.8,141.7,128.0,113.6,120.7,128.3,148.6]
ylat=[-42.2,-19.2,-0.5,-3.5,-34.4,-8.7,-45.1,-1.0,-38.6,-26.7,-29.1,-20.0,-14.4,-18.9,-31.3,-6.6,-23.8,-3.4,-7.5,-25.6,3.8,-3.1,-1.9,-23.2]
#function to draw pie charts on map
def draw_pie(ax,X=0, Y=0, size = 1500):
xy = []
start = 0.17
ratios=[1/12.]*12
for ratio in ratios:
x = [0] + np.cos(np.linspace(2*math.pi*start,2*math.pi*(start+ratio))).tolist() #30
y = [0] + np.sin(np.linspace(2*math.pi*start,2*math.pi*(start+ratio))).tolist() #30
xy1=(zip(x,y))
xy.append(xy1)
start -= ratio
piecolors = []
for lt in range(12):
c = australia_data[b,lt]-1
c=int(c)
piecolors.append(colors[c])
for i, xyi in enumerate(xy):
ax.scatter([X],[Y] , marker=(xyi,0), s=size, facecolor=piecolors[i],linewidth=0.5,alpha=.7)
australia_data[:,11] = 1
australia_data[:,4] = 3
australia_data[:,1] = 2
fig = plt.figure()
ax = fig.add_axes([.05,.01,.79,.95])
x1 = 90 #left
x2 = 180 #right
y1 = -50 #bottom
y2 = 10 #top
#Create the map
m = Basemap(resolution='l',projection='merc', llcrnrlat=y1,urcrnrlat=y2,llcrnrlon=x1,urcrnrlon=x2,lat_ts=0) #,lat_ts=(x1+x2)/2
m.drawcoastlines()
#plots pie charts:
for b in range(24):
X,Y=m(xlon[b],ylat[b])
draw_pie(ax,X, Y,size=400)
plt.savefig('australia_pies.png',dpi=400)
Any ideas as to why this is happening (and how to fix it!) would be greatly appreciated!
Edit: it seems to be an issue with the number of wedges in the pie chart - reducing this to 6 results in uniform pies, but 7+ causes some wedges to "explode".
Looking at the scatter piecharts example, you forgot to adjust the size of the pie wedges according to the maximum distance from 0 to the the arc of the wedge. This is necessary because markers normalize the path given before drawing it, hence different wedges need different sizes in order to appear with the same size in the final plot.
import numpy as np
import matplotlib.pyplot as plt
#function to draw pie charts on map
def draw_pie(ax,X=0, Y=0, size = 1500):
xy = []; s=[]
start = 0.0
ratios=[1/12.]*12
for ratio in ratios:
x = [0] + np.cos(np.linspace(2*np.pi*start,2*np.pi*(start+ratio))).tolist() #30
y = [0] + np.sin(np.linspace(2*np.pi*start,2*np.pi*(start+ratio))).tolist() #30
xy1 = np.column_stack([x, y])
s1 = np.abs(xy1).max()
xy.append(xy1)
s.append(s1)
start -= ratio
for xyi, si in zip(xy,s):
ax.scatter([X],[Y] , marker=(xyi,0), s=size*si**2, edgecolor="k")
fig, ax = plt.subplots()
X,Y=166,50
draw_pie(ax,X, Y,size=3000)
plt.show()

Make plt.colorbar extend to the steps immediately before and after vmin/vmax

I want to do something with plt.hist2d and plt.colorbar and I'm having real trouble working out how to do it. To explain, I've written the following example:
import numpy as np
from matplotlib import pyplot as plt
x = np.random.random(1e6)
y = np.random.random(1e6)
plt.hist2d(x, y)
plt.colorbar()
plt.show()
This code generates a plot that looks something like the image below.
If I generate a histogram, ideally I would like the colour bar to extend beyond the maximum and minimum range of the data to the next step beyond the maximum and minimum. In the example in this question, this would set the colour bar extent from 9660 to 10260 in increments of 60.
How can I force either plt.hist2d or plt.colorbar to set the colour bar such that ticks are assigned to the start and end of the plotted colour bar?
I think this is what you're looking for:
h = plt.hist2d(x, y)
mn, mx = h[-1].get_clim()
mn = 60 * np.floor(mn / 60.)
mx = 60 * np.ceil(mx / 60.)
h[-1].set_clim(mn, mx)
cbar = plt.colorbar(h[-1], ticks=np.arange(mn, mx + 1, 60), )
This gives something like,
It's also often convenient to use tickers from the matplotlib.ticker, and use the tick_values method of tickers, but for this purpose I think the above is most convenient.
Good luck!
With huge thanks to farenorth, who got me thinking about this in the right way, I came up with a function, get_colour_bar_ticks:
def get_colour_bar_ticks(colourbar):
import numpy as np
# Get the limits and the extent of the colour bar.
limits = colourbar.get_clim()
extent = limits[1] - limits[0]
# Get the yticks of the colour bar as values (ax.get_yticks() returns them as fractions).
fractions = colourbar.ax.get_yticks()
yticks = (fractions * extent) + limits[0]
increment = yticks[1] - yticks[0]
# Generate the expanded ticks.
if (fractions[0] == 0) & (fractions[-1] == 1):
return yticks
else:
start = yticks[0] - increment
end = yticks[-1] + increment
if fractions[0] == 0:
newticks = np.concatenate((yticks, [end]))
elif fractions[1] == 1:
newticks = np.concatenate(([start], yticks))
else:
newticks = np.concatenate(([start], yticks, [end]))
return newticks
With this function I can then do this:
from matplotlib import pyplot as plt
x = np.random.random(1e6)
y = np.random.random(1e6)
h = plt.hist2d(x, y)
cbar = plt.colorbar()
ticks = get_colour_bar_ticks(cbar)
h[3].set_clim(ticks[0], ticks[-1])
cbar.set_clim(ticks[0], ticks[-1])
cbar.set_ticks(ticks)
plt.show()
Which results in this, which is what I really wanted:

Python Matplotlib polar Labeling

Hi Im currently wishing to label my polar bar chart in the form whereby the labels are all rotating by differing amounts so they can be read easily much like a clock. I know there is a rotation in plt.xlabel however this will only rotate it by one amount I have many values and thus would like to not have them all crossing my graph.
This is figuratively what my graph is like with all the orientations in the same way, however I would like something akin to this; I really need this just using matplotlib and pandas if possible. Thanks in advance for the help!
Some example names might be farming, generalists, food and drink if these are not correctly rotated they will overlap the graph and be difficult to read.
from pandas import DataFrame,Series
import pandas as pd
import matplotlib.pylab as plt
from pylab import *
import numpy as np
data = pd.read_csv('/.../data.csv')
data=DataFrame(data)
N = len(data)
data1=DataFrame(data,columns=['X'])
data1=data1.get_values()
plt.figure(figsize=(8,8))
ax = plt.subplot(projection='polar')
plt.xlabel("AAs",fontsize=24)
ax.set_theta_zero_location("N")
bars = ax.bar(theta, data1,width=width, bottom=0.0,color=colours)
I would then like to label the bars according to their names which I can obtain in a list, However there are a number of values and i would like to be able to read the data names.
The very meager beginnings of an answer for you (I was doing something similar, so I just threw a quick hack to go in the right direction):
# The number of labels you'd like
In [521]: N = 5
# Where on the circle it will show up
In [522]: theta = numpy.linspace(0., 2 * numpy.pi, N + 1, endpoint = True)
In [523]: theta = theta[1:]
# Create the figure
In [524]: fig = plt.figure(figsize = (6,6), facecolor = 'white', edgecolor = None)
# Create the axis, notice polar = True
In [525]: ax = plt.subplot2grid((1, 1), (0,0), polar = True)
# Create white bars so you're really just focusing on the labels
In [526]: ax.bar(theta, numpy.ones_like(theta), align = 'center',
...: color = 'white', edgecolor = 'white')
# Create the text you're looking to add, here I just use numbers from counter = 1 to N
In [527]: counter = 1
In [528]: for t, o in zip(theta, numpy.ones_like(theta)):
...: ax.text(t, 1 - .1, counter, horizontalalignment = 'center', verticalalignment = 'center', rotation = t * 100)
...: counter += 1
In [529]: ax.set_yticklabels([])
In [530]: ax.set_xticklabels([])
In [531]: ax.grid(False)
In [531]: plt.show()

increasing width of figure in matplotlib according to number of x values

I was trying to draw a barchart using matplotlib.The number of items to be plotted can vary . I cannot set the figure.set_size_inches(w,h) or set_figwidth(w) with constants(like 6.,8. or 8.,12. etc),since I cannot tell in advance what the value of w or h should be.I want the width of figure to increase as the number of items to be plotted increases.Can someone tell me how I can do this?
import pylab
def create_barchart(map):
xvaluenames = map.keys()
xvaluenames.sort()
yvalues = map.values()
max_yvalue = get_max_yvalue(yvalues)
xdata = range(len(xvaluenames))
ydata = [map[x] for x in xvaluenames]
splitxdata = [x.split('-',1) for x in xvaluenames]
xlabels = [x[0] for x in splitxdata]
figure = pylab.figure()
ax = figure.add_subplot(1,1,1)
figsize = figure.get_size_inches()
print 'figure size1=',figsize,'width=',figsize[0],'height=',figsize[1]
barwidth = .25
ystep = max_yvalue/5
pylab.grid(True)
if xdata and ydata:
ax.bar(xdata, ydata, width=barwidth,align='center',color='orange')
ax.set_xlabel('xvalues',color='green')
ax.set_ylabel('yvalues',color='green')
ax.set_xticks(xdata)
ax.set_xlim([min(xdata) - 0.5, max(xdata) + 0.5])
ax.set_xticklabels(xlabels)
ax.set_yticks(range(0,max_yvalue+ystep,ystep))
ax.set_ylim(0,max(ydata)+ystep)
figure.autofmt_xdate(rotation=30)
figure.savefig('mybarplot',format="png")
print 'figure size2=',figure.get_size_inches()
pylab.show()
def get_max_yvalue(yvals):
return max(yvals) if yvals else 0
if I try with a small set of items,I get
if __name__=='__main__':
datamap = dict(mark=39,jim=40, simon=20,dan=33)
print datamap
create_barchart(datamap)
but if I use a larger set
datamap = dict(mark=39,jim=40, simon=20,dan=33)
additional_values= dict(jon=34,ray=23,bert=45,kevin=35,ned=31,bran=11,tywin=56,tyrion=30,jaime=36,griffin=25,viserys=25)
datamap.update(additional_values)
create_barchart(datamap)
This looks awful,
I am wondering if there is a way to increase the width of figure,according to the number of items to be plotted,keeping the width of bars in both cases same
You can set the width when you initialize the figure:
# default scale is 1 in your original case, scales with other cases:
widthscale = len(yvalues)/4
figsize = (8*widthscale,6) # fig size in inches (width,height)
figure = pylab.figure(figsize = figsize) # set the figsize
Replace the figure = pylab.figure() line with the above three lines and you get what your asking for.

Categories

Resources