Related
I wan to two plots side by side instead of this vertically, right now it's showing one by one
def scatter_plot(surrogate, building, actual, pred,index):
#calculating max and min x axis range
min_range=pred.min()-10
max_range=pred.max()+10
min_domain=actual.min()-10
max_domain=actual.max()+10
#scaling and creating scatter plot
plt.axes([0, 0, 2, 2])
plt.scatter(x=actual,y=pred, marker="o") #(y = predicted)
#plt.gca().set_aspect('equal', adjustable='box')
plt.grid()
plt.xlabel('Actual Values', fontsize = 20)
plt.ylabel('Predicted Values', fontsize = 20)
plt.title(f'{building.idf}_{building.epw}_{variable} Scatter Plot of NN vs E+', fontsize= 25)
#adding regression line
plt.plot([min_domain, max_domain], [min_range, max_range], color='g', linestyle='-', linewidth=1,label='regression')
#adding line passing minimum and maximum actual points
plt.plot([min_domain, max_domain],[min_domain, max_domain],color='r',linestyle='-',linewidth=1,label='actual point line')
#adding legend
plt.legend(loc='lower right')
#calculating error metrics
location = building.metadata['building_attributes']['Location']
building_type = building.idf
df = csv.loc[(csv['id'] == surrogate.surrogate_id) &
(csv['Location'] == location) & (csv['Building Type'] == building_type)]
rmse = df[f'{variable} RMSE'].values[0]
r2 = df[f'{variable} R2'].values[0]
#Adding Error metric annotations
textstr = '\n'.join((r'Total Error Metrics', r'$RMSE=%.2f$' % (rmse, ),r'$R2=%.2f$' % (r2, )))
props = dict(boxstyle='round', facecolor='ivory', alpha=0.5)
plt.text(max_range, max_domain, textstr, fontsize=20, verticalalignment='top', bbox=props)
#calculating x and y range
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
#Coordinates of interested area
percentile = 10
nth_percentile = np.percentile(actual,percentile)
bottom, left, width, height = 0, 0, nth_percentile,nth_percentile
try:
x_hist = x_min +(x_max - x_min)/9 #may have to change value 9
#calculating lines for selected area
x1, y1 = [left, x_hist], [bottom+height, (y_max + y_min)/2]
x2, y2 = [left + width, x_hist], [bottom + height, (y_max + y_min)/2]
L_act = []
L_pred = []
for x, y in zip(actual, pred):
if left <= x <= width+left:
if bottom<= y <= height + bottom:
L_act.append(x)
L_pred.append(y)
#adding rectangle for selected area
rect=mpatches.Rectangle((left, bottom),width, height, fill = False, color = "black",linewidth = 2)
plt.gca().add_patch(rect)
#calculating error metrics for selected area
rmse = RMSE(L_act, L_pred)
r2 = R2(L_act, L_pred)
#adding lines to indicated the selected area
plt.plot(x1, y1, x2, y2, color = 'black', linewidth = 2)
#adding histogram
plt.axes([0.2, 1, .6, .6], facecolor='w')
plt.hist(L_act, 30)
plt.xticks([])
plt.yticks([])
textstr = '\n'.join((r'Selected Section Error Metrics', r'$RMSE=%.2f$' % (rmse, ),r'$R2=%.2f$' % (r2, )))
props = dict(boxstyle='round', facecolor='ivory', alpha=0.8)
#adding error metrics annotations for selected area
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
plt.text(x_min + x_min/10, y_max - y_max/30, textstr, fontsize=10, verticalalignment='top', bbox=props)
except ValueError:
print("Selected section doesn't contain any data points")
plt.show()
I tried using a subplot but that didn't work
def s_plot(surrogate,building):
figure, axis = plt.subplots(1, 2)
actual, pred = np.array(surrogate.test_samples[variable].values[:]), np.array(surrogate.training_samples[variable].values[:])
actual_train, pred_train = np.array(surrogate.train_actual[variable].values[:]), np.array(surrogate.train_pred[variable].values[:])
data =[[actual,pred],[actual_train, pred_train]
for ax,i in zip(axes.flatten(),data):
scatter_plot(surrogate,building,i[0],i[1],ax)
Here I am using axes instead of plt but there are so many parameters that axes doesn't have such as gca, scaling using axes, etc. and I am not able to plot histogram by subplot use
Is there any way to plot this side by side
here is a simple example of using histogram with subplots:
def func(ax):
# example data
mu = 100 # mean of distribution
sigma = 15 # standard deviation of distribution
x = mu + sigma * np.random.randn(437)
num_bins = 50
# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, density=True)
# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')
fig, (ax1, ax2) = plt.subplots(1, 2)
func(ax1)
func(ax2)
I find the code example for drawing scatter pie chat
In this example, the size of each pie slices is identical across all three scatters. I would like to know if it is possible to make each pie chart unique (different number of slices and different pie proportions)
Yes, it's totally possible. Here's a function that plot a pie chart at given position with a given size:
def draw_pie(dist,
xpos,
ypos,
size,
ax=None):
if ax is None:
fig, ax = plt.subplots(figsize=(10,8))
# for incremental pie slices
cumsum = np.cumsum(dist)
cumsum = cumsum/ cumsum[-1]
pie = [0] + cumsum.tolist()
for r1, r2 in zip(pie[:-1], pie[1:]):
angles = np.linspace(2 * np.pi * r1, 2 * np.pi * r2)
x = [0] + np.cos(angles).tolist()
y = [0] + np.sin(angles).tolist()
xy = np.column_stack([x, y])
ax.scatter([xpos], [ypos], marker=xy, s=size)
return ax
Using that function, we can draw, say three pie charts:
fig, ax = plt.subplots(figsize=(10,8))
draw_pie([1,2,1],1,1,10000,ax=ax)
draw_pie([2,2,2,2], 2, 1, 20000, ax=ax)
draw_pie([1,1,1,1,1], 1.5,1.5, 30000, ax=ax)
plt.xlim(0.6,2.5)
plt.ylim(0.8, 1.8)
plt.show()
gives:
you could implement it like this:
import numpy as np
import matplotlib.pyplot as plt
def drawPieMarker(xs, ys, ratios, sizes, colors):
assert sum(ratios) <= 1, 'sum of ratios needs to be < 1'
markers = []
previous = 0
# calculate the points of the pie pieces
for color, ratio in zip(colors, ratios):
this = 2 * np.pi * ratio + previous
x = [0] + np.cos(np.linspace(previous, this, 10)).tolist() + [0]
y = [0] + np.sin(np.linspace(previous, this, 10)).tolist() + [0]
xy = np.column_stack([x, y])
previous = this
markers.append({'marker':xy, 's':np.abs(xy).max()**2*np.array(sizes), 'facecolor':color})
# scatter each of the pie pieces to create pies
for marker in markers:
ax.scatter(xs, ys, **marker)
fig, ax = plt.subplots()
drawPieMarker(xs=np.random.rand(3),
ys=np.random.rand(3),
ratios=[.3, .2, .5],
sizes=[80, 60, 100],
colors=['cyan', 'orange', 'teal'])
drawPieMarker(xs=np.random.rand(2),
ys=np.random.rand(2),
ratios=[.33, .66],
sizes=[100, 120],
colors=['blue', 'yellow'])
drawPieMarker(xs=np.random.rand(2),
ys=np.random.rand(2),
ratios=[.33, .25],
sizes=[50, 75],
colors=['maroon', 'brown'])
plt.show()
I have been playing around with Python for the last couple of days and found a lot of good resources about labelling, but I am failing to make it properly display negative values. Because the autolabel() function takes the height of the bar, which seems to always be a positive value, the labels are displayed way up in the graph and are of course not displayed as negative values. Can I somehow get the values that make up these bars or how do I get these labels down where they belong and show them as negative?
import pandas as pd
import matplotlib.pyplot as plt
from builtins import list
import matplotlib
matplotlib.style.use('ggplot')
import numpy as np
n_groups = 2
# create plot
fig, ax = plt.subplots()
fig.canvas.set_window_title('Mindestlohn Bundesweit')
index = np.arange(n_groups)
bar_width = 0.20
opacity = 0.8
list_reallohn_week_vollzeit = [-8.159698443426123, 11.395025597733763]
list_reallohn_week_teilzeit = [-1.048913873322391, 28.99318154295449]
list_reallohn_week_mini = [-7.552596893170488, 7.959096278017519]
rects1 = plt.bar(index + 0.00, list_reallohn_week_vollzeit, bar_width,
alpha=opacity,
color='b',
label='Vollzeit')
rects2 = plt.bar(index + bar_width, list_reallohn_week_teilzeit, bar_width,
alpha=opacity,
color='g',
label='Teilzeit')
rects3 = plt.bar(index + bar_width * 2,list_reallohn_week_mini, bar_width,
alpha = opacity,
color='c',
label='Mini Job')
label_week_lists = ('2014 vor MdL', '2015 Nicht MdL berechtigt', '2015 mit MdL')
plt.ylabel('EUR')
plt.title('Reallöhne pro Woche')
plt.xticks(index + bar_width, label_week_lists)
plt.legend(bbox_to_anchor=(1, 1),
bbox_transform=plt.gcf().transFigure)
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = rect.get_height()
# Fraction of axis height taken up by this rectangle
p_height = (height / y_height)
# If we can fit the label above the column, do that;
# otherwise, put it inside the column.
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05)
else:
label_position = height + (y_height * 0.01)
ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1, ax)
autolabel(rects2, ax)
autolabel(rects3, ax)
plt.show()
mathplotlib is not very well documented in that department. Try using the dir() function to reveal the available options you have on the container you're working on. i found there is a .get_y() function which retuns negative numbers in that case
try this code instead
import pandas as pd
import matplotlib.pyplot as plt
from builtins import list
import matplotlib
matplotlib.style.use('ggplot')
import numpy as np
n_groups = 2
# create plot
fig, ax = plt.subplots()
fig.canvas.set_window_title('Mindestlohn Bundesweit')
index = np.arange(n_groups)
bar_width = 0.20
opacity = 0.8
list_reallohn_week_vollzeit = [-8.159698443426123, 11.395025597733763]
list_reallohn_week_teilzeit = [-1.048913873322391, 28.99318154295449]
list_reallohn_week_mini = [-7.552596893170488, 7.959096278017519]
rects1 = plt.bar(index + 0.00, list_reallohn_week_vollzeit, bar_width,
alpha=opacity,
color='b',
label='Vollzeit')
rects2 = plt.bar(index + bar_width, list_reallohn_week_teilzeit, bar_width,
alpha=opacity,
color='g',
label='Teilzeit')
rects3 = plt.bar(index + bar_width * 2,list_reallohn_week_mini, bar_width,
alpha = opacity,
color='c',
label='Mini Job')
label_week_lists = ('2015 Nicht MdL berechtigt', '2015 mit MdL')
plt.ylabel('EUR')
plt.title('Reallöhne pro Woche')
plt.xticks(index + bar_width, label_week_lists)
plt.legend(bbox_to_anchor=(1, 1),
bbox_transform=plt.gcf().transFigure)
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
# print(dir(rect))
height = 0
if rect.get_y() < 0:
height = rect.get_y()
else:
height = rect.get_height()
print(rect.get_height())
print( str(rect.get_y()) )
# Fraction of axis height taken up by this rectangle
p_height = (height / y_height)
# If we can fit the label above the column, do that;
# otherwise, put it inside the column.
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05)
else:
label_position = height + (y_height * 0.01)
ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1, ax)
autolabel(rects2, ax)
autolabel(rects3, ax)
plt.show()
If you print rect.get_height() values you get something like:
-8.159698443426123
11.395025597733763
-1.048913873322391
28.99318154295449
-7.552596893170488
7.959096278017519
Therefore height of a bar maybe a negative.
To mark negative bars modify if statement in auto label function as follow:
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05) if (height > 0) else height + (y_height * 0.05)
else:
label_position = height + (y_height * 0.01) if (height > 0) else height - (y_height * 0.05)
You have to ajust coefficients in else branch (0.05) manually because position of a below label depends on a font size of a label. (Position of a label for positive bar does not affected by font size because a label is above the bar).
Finale for my font settings:
I use matplotlib to generate an image in the following way:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 0.5)
patches = []
for x1,y1,r in zip(x, y, radii):
circle = Circle((x1,y1), r)
patches.append(circle)
p = PatchCollection(patches, cmap='cool', alpha=1.0)
p.set_array(c)
ax.add_collection(p)
plt.colorbar(p)
plt.savefig(fig_name)
What I want to have is a polygon (given by its border) and colored circles on the top of this polygon. However, I get the polygon on the top of the circles.
This is strange because I plot the polygon first and then I add circles to the plot.
Does anybody know why it happens and how this problem can be resolved?
ADDED
As requested, here is fully working example:
import pandas
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Circle, Polygon
import numpy as np
def plot_xyc(df, x_col, y_col, c_col, radius, fig_name, title, zrange):
resolution = 50
x = df[x_col]
y = df[y_col]
c = df[c_col]
x0 = (max(x) + min(x))/2.0
y0 = (max(y) + min(y))/2.0
dx = (max(x) - min(x))
dy = (max(y) - min(y))
delta = max(dx, dy)
radii = [delta*radius for i in range(len(x))]
fig = plt.figure()
plt.title(title)
ax = fig.add_subplot(111)
border = ([-3, 3, 3, -3], [-3, -3, 3, 3])
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 1.0)
patches = []
for x1,y1,r in zip(x, y, radii):
circle = Circle((x1,y1), r)
patches.append(circle)
patches.append(Circle((-100,-100), r))
patches.append(Circle((-100,-100), r))
p = PatchCollection(patches, cmap='cool', alpha=1.0)
p.set_array(c)
max_ind = max(c.index)
c.set_value(max_ind + 1, min(zrange))
c.set_value(max_ind + 2, max(zrange))
plt.xlim([x0 - delta/2.0 - 0.05*delta, x0 + delta/2.0 + 0.05*delta])
plt.ylim([y0 - delta/2.0 - 0.05*delta, y0 + delta/2.0 + 0.05*delta])
ax.add_collection(p)
plt.colorbar(p)
plt.savefig(fig_name)
if __name__ == '__main__':
df = pandas.DataFrame({'x':[1,2,3,4], 'y':[4,3,2,1], 'z':[1,1,2,2]})
plot_xyc(df, 'x', 'y', 'z', 0.1, 'test2.png', 'My Titlle', (0.0, 3.0))
You're looking for zorder.
In matplotlib, all additional arguments are just passed up the class heirarchy. zorder is a kwarg of the Artist class, so you just need to make sure that at some point it gets zorder.
You can do it two ways in your example;
either add it in here:
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 1.0, zorder=1)
or here:
p = PatchCollection(patches, cmap='cool', alpha=1.0, zorder=2)
or if you want, both. Objects with a higher zorder sit on top of those with lower values.
I've got a pie chart (example) with following fracs = [10, 20, 50, 30]. Drawing this with matplotlib is no problem. How do I get a breakdown of the first wedge (10) into 6 and 4? Ideally, I want a second wedge for the 20, to breakdown into 10, 3, 7. This would be displayed as a barchart near the specific wedge or a pie chart (which would make it a pie of pie chart similar to the ones in Excel).
Here is one way of doing it (possibly not the best...). I've adapted some of the code found here, on the matplotlib site to make a little_pie function, that will draw small pie charts at arbitrary positions.
from pylab import *
import math
import numpy as np
def little_pie(breakdown,location,size):
breakdown = [0] + list(np.cumsum(breakdown)* 1.0 / sum(breakdown))
for i in xrange(len(breakdown)-1):
x = [0] + np.cos(np.linspace(2 * math.pi * breakdown[i], 2 * math.pi *
breakdown[i+1], 20)).tolist()
y = [0] + np.sin(np.linspace(2 * math.pi * breakdown[i], 2 * math.pi *
breakdown[i+1], 20)).tolist()
xy = zip(x,y)
scatter( location[0], location[1], marker=(xy,0), s=size, facecolor=
['gold','yellow', 'orange', 'red','purple','indigo','violet'][i%7])
figure(1, figsize=(6,6))
little_pie([10,3,7],(1,1),600)
little_pie([10,27,4,8,4,5,6,17,33],(-1,1),800)
fracs = [10, 8, 7, 10]
explode=(0, 0, 0.1, 0)
pie(fracs, explode=explode, autopct='%1.1f%%')
show()
I couldn't find a solution for this, so I hacked my own. I used the ConnectionPatch object in the matplotlib.patches module. This allows you to draw lines between different axes in the same figure. The following creates a pie chart on the left and a stacked bar on the right:
import matplotlib.pyplot as plt
from matplotlib.patches import ConnectionPatch
import numpy as np
import math
# style choice
plt.style.use('fivethirtyeight')
# make figure and assign axis objects
fig = plt.figure(figsize=(15,7.5))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
# pie chart parameters
ratios = [.4, .56, .04]
labels = ['Approve', 'Disapprove', 'Undecided']
explode=[0.1,0,0]
# rotate so that first wedge is split by the x-axis
angle = -180*ratios[0]
ax1.pie(ratios, autopct='%1.1f%%', startangle=angle,
labels=labels,explode=explode )
# bar chart parameters
xpos = 0
bottom = 0
ratios = [.33, .54, .07, .06]
width = .2
colors = ['y','m','#99ff99','#ffcc99']
for j in range(len(ratios)):
height = ratios[j]
ax2.bar(xpos, height, width, bottom=bottom, color=colors[j])
ypos = bottom + ax2.patches[j].get_height()/2
bottom += height
ax2.text(xpos,ypos, "%d%%" %
(ax2.patches[j].get_height()*100), ha='center')
plt.title('Gender of approvers')
plt.legend(('Women', 'Men', 'Gender Neutral', 'Alien'))
plt.axis('off')
plt.xlim(-2.5*width, 2.5*width)
Then I add two lines connected the first wedge of the pie chart with the top and bottom, respectively, of the stacked bar plot:
# use ConnectionPatch to draw lines between the two plots
# get the wedge data for the first group
theta1, theta2 = ax1.patches[0].theta1, ax1.patches[0].theta2
center, r = ax1.patches[0].center, ax1.patches[0].r
bar_height = sum([item.get_height() for item in ax2.patches])
x = r*np.cos(math.pi/180*theta2)+center[0]
y = np.sin(math.pi/180*theta2)+center[1]
con = ConnectionPatch(xyA=(-width/2,bar_height), xyB=(x,y),
coordsA="data", coordsB="data", axesA=ax2, axesB=ax1)
con.set_color([0,0,0])
con.set_linewidth(4)
ax2.add_artist(con)
x = r*np.cos(math.pi/180*theta1)+center[0]
y = np.sin(math.pi/180*theta1)+center[1]
con = ConnectionPatch(xyA=(-width/2,0), xyB=(x,y),
coordsA="data", coordsB="data", axesA=ax2, axesB=ax1)
con.set_color([0,0,0])
ax2.add_artist(con)
con.set_linewidth(4)
plt.show()
Here is the plot:
I haven't used it yet, but you could try: PyGal
In particular: http://pygal.org/en/stable/documentation/types/pie.html#multi-series-pie