I created a scatter plot using matplotlib but I am somehow unable to get the labels to center into the boxes within the colorbar..
This is the code I have so far:
cMap = ListedColormap(['Orange', 'Purple', 'Blue','Red','Green'])
fig, ax = plt.subplots()
plt.figure(figsize=(12,12),dpi = 80)
#data
dist = np.random.rand(1900,1900)
#legend
cbar = plt.colorbar(scatter)
cbar.ax.get_yaxis().set_ticks([])
for j, lab in enumerate(['$Training$','$None$','$GS$','$ML$','$Both$']):
cbar.ax.text( .5, j - .985, lab, ha='left', va='center', rotation = 270)
cbar.ax.get_yaxis().labelpad = 15
cbar.ax.set_ylabel('Outliers', rotation=270)
indices = np.where(outlier_label != -2)[0]
plt.scatter(dist[indices, 0], dist[indices, 1], c=outlier_label[indices], cmap=cMap, s=20)
plt.gca().set_aspect('equal', 'datalim')
plt.title('Projection of the data', fontsize=24)
Thanks!
In line cbar.ax.text( .5, j - .985, lab, ha='left', va='center', rotation = 270) you have to work and change with '.985' with try and error to get better results.
You can extract the y limits of the colorbar to know its top and bottom. Dividing that area into 11 equally spaced positions, will have the 5 centers at the odd positions of that list. Similarly, you can extract the x limits to find the horizontal center.
Some remarks:
If you already called plt.subplots(), then plt.figure() will create a new figure, leaving the first plot empty. You can set the figsize directly via plt.subplots(figsize=...)
You are mixing matplotlib's "object-oriented interface" with the pyplot interface. This can lead to a lot of confusion. It is best to stick to one or the other. (The object-oriented interface is preferred, especially when you are creating non-trivial plots.)
You set dist = np.random.rand(1900,1900) of dimensions 1900x1900 while you are only using dimensions 1900x2.
The code nor the text give an indication of the values inside outlier_label. The code below assumes they are 5 equally-spaced numbers, and that both the lowest and the highest value are present in the data.
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import numpy as np
colors = ['Orange', 'Purple', 'Blue', 'Red', 'Green']
cmap = ListedColormap(colors)
fig, ax = plt.subplots(figsize=(12, 12), dpi=80)
# data
dist = np.random.randn(1900, 2).cumsum(axis=0)
outlier_label = np.repeat(np.arange(5), 1900 // 5)
indices = outlier_label != -2
scatter = ax.scatter(dist[indices, 0], dist[indices, 1], c=outlier_label[indices], cmap=cmap, s=20)
# legend
cbar = plt.colorbar(scatter, ax=ax)
cbar.ax.get_yaxis().set_ticks([])
cb_xmin, cb_xmax = cbar.ax.get_xlim()
cb_ymin, cb_ymax = cbar.ax.get_ylim()
num_colors = len(colors)
for j, lab in zip(np.linspace(cb_ymin, cb_ymax, 2 * num_colors + 1)[1::2],
['$Training$', '$None$', '$GS$', '$ML$', '$Both$']):
cbar.ax.text((cb_xmin + cb_xmax) / 2, j, lab, ha='center', va='center', rotation=270, color='white', fontsize=16)
cbar.ax.get_yaxis().labelpad = 25
cbar.ax.set_ylabel('Outliers', rotation=270, fontsize=18)
ax.set_aspect('equal', 'datalim')
ax.set_title('Projection of the data', fontsize=24)
plt.show()
Related
I have the following issue.
I have a graph of which has colored segments. The problem is in relating those segments to the color bar (which also contains text), so that each color segment is aligned with the color bar.
The code is the following:
from matplotlib.colorbar import colorbar_factory
x_v = datosg["Hour"]+div
y_v = datosg["UV Index"]
fig, ax= plt.subplots(figsize = (7,7))
ax.plot(x_v, y_v, color = "green")
ax.set_xlim(7, 19)
ax.grid()
ax.axhspan(0, 2.5, facecolor='green', alpha=0.8)
ax.axhspan(2.5, 5.5, facecolor='blue', alpha=0.7)
ax.axhspan(5.5, 7.5, facecolor='red', alpha=0.7)
ax.axhspan(7.5, 10.5, facecolor='yellow', alpha=0.7)
ax.axhspan(10.5, 16, facecolor='pink', alpha=0.7)
ax.margins(0)
from matplotlib.colors import ListedColormap
#discrete color scheme
cMap = ListedColormap(['green', 'blue','red', 'yellow', 'pink'])
#data
np.random.seed(42)
data = np.random.rand(5, 5)
heatmap = ax.pcolor(data, cmap=cMap)
#legend
cbar_ay = fig.add_axes([0.93, 0.125, 0.2, 0.755])
cbar = plt.colorbar(heatmap, cax=cbar_ay, orientation="vertical")
cbar.ax.get_yaxis().set_ticks([])
for j, lab in enumerate(['$Bajo$','$Medio$','$Alto$','$Muy Alto$','$Extremo$']):
cbar.ax.text(.5, (2 * j + 1) / 10.0, lab, ha='center', va='center')
plt.show()
The graph that results from this code is as follows:
Result_code
I have tried everything, the result I expect is very similar to this graph:
resulting image
But I can't change the range of the colors in the color bar.
Also note that I created random values in order to create the colorbar, I couldn't think of any other way, however so far it has worked. I only have to modify the range, so that it is similar to the last graph.
Any help would be appreciated.
I guess it's much easier to just draw a second Axes and fill it with axhspans the same way you did it with the main Axes, but if you want to use a colorbar, you can do it as follows:
import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
colors = ['green', 'blue','red', 'yellow', 'pink']
labels = ['$Bajo$','$Medio$','$Alto$','$Muy Alto$','$Extremo$']
bounds = np.array([0, 2.5, 5.5, 7.5, 10.5, 16 ])
fig, ax= plt.subplots()
for span, color in zip(itertools.pairwise(bounds), colors):
ax.axhspan(*span, facecolor=color, alpha=0.8)
ax.margins(0)
cmap = mpl.colors.ListedColormap(colors)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
ax_pos = ax.get_position().bounds
cbar_ay = fig.add_axes([0.93, ax_pos[1], 0.2, ax_pos[3]])
cbar = plt.colorbar(mpl.cm.ScalarMappable(cmap=cmap, norm=norm), cax=cbar_ay, orientation="vertical", spacing='proportional')
cbar.ax.set_axis_off()
for y, lab in zip(bounds[:-1] + np.diff(bounds) / 2, labels):
cbar.ax.text(.5, y, lab, ha='center', va='center')
I am trying to create an axis plot. I was trying to loop over it as I am plotting the same variable for two different categories. Currently, I have written code two times but I am looking for a smarter way with looping, if possible. Any other suggestion will also be helpful.
zone = ['AB','CD']
plt.style.use('default')
fig,(ax0,ax1) = plt.subplots(2,1, figsize = (18,18), sharex = False)
i = 0
while i < len(zone):
if zone[i] == zone[0]:
ax0.plot(df0['datetime'], df0['pnl1'], color='k', linewidth=1, label ='PnL1')
ax0.plot(df0['datetime'], df0['pnl2'], color='m', linewidth=1, label ='PnL2')
ax00 = ax0.twinx()
ax00.bar(df0['datetime'], df0['qty'], width = 1/96, color='g', align = 'edge', alpha = 0.5, label ='Qty')
elif zone[i] == zone[1]:
ax1.plot(df0['datetime'], df0['pnl1'], color='k', linewidth=1, label ='PnL1')
ax1.plot(df0['datetime'], df0['pnl2'], color='m', linewidth=1, label ='PnL2')
ax01 = ax1.twinx()
ax01.bar(df0['datetime'], df0['hedge'], width = 1/96, color='g', align = 'edge', alpha = 0.5, label ='Qty')
i = i + 1
I want to check if something like below can be done with axis plots or not.
zone = ['AB','CD']
plt.style.use('default')
fig,(ax0,ax1) = plt.subplots(2,1, figsize = (18,18), sharex = False)
i = 0
while i < len(zone):
ax{''}.format(i).plot(df0['datetime'], df0['pnl1'], color='k', linewidth=1, label ='PnL1')
ax{''}.format(i).plot(df0['datetime'], df0['pnl2'], color='m', linewidth=1, label ='PnL2')
ax0{''}.format(i) = ax{''}.format(i).twinx()
ax0{''}.format(i).bar(df0['datetime'], df0['qty'], width = 1/96, color='g', align = 'edge', alpha = 0.5, label ='Qty')
It did not work for me. Any leads to execute axis plot with loop will be helpful.
Here are some ways:
Simply loop over the list of axes
import matplotlib.pyplot as plt
import numpy as np
fig,axes = plt.subplots(2,1)
x = np.linspace(0,5,21)
for ax in axes:
ax.plot(x,np.sin(x))
plt.show()
Works also with index:
for i in range(len(axes)):
axes[i].plot(x,np.sin(x))
For a grid of plot you can use a similar approach:
import matplotlib.pyplot as plt
import numpy as np
fig,axes = plt.subplots(2,2)
x = np.linspace(0,5,21)
for i in range(len(axes)):
for j in range(len(axes[0])):
axes[i][j].plot(x,np.sin(x))
plt.show()
If you don't like double-loops, you can flatten the array with np.ravel()
fig,axes = plt.subplots(2,2)
x = np.linspace(0,5,21)
for ax in np.ravel(axes):
ax.plot(x,np.sin(x))
plt.show()
I have a function that inputs a string (the name of the dataframe we're visualizing) and returns two histograms that visualize that data. The first plot (on the left) is the raw data, the one on the right is it after being normalized (same, just plotted using the matplotlib parameter density=True). But as you can see, this leads to transparency issues when the plots overlap. This is my code for this particular plot:
plt.rcParams["figure.figsize"] = [12, 8]
plt.rcParams["figure.autolayout"] = True
ax0_1 = plt.subplot(121)
_,bins,_ = ax0_1.hist(filtered_0,alpha=1,color='b',bins=15,label='All apples')
ax0_1.hist(filtered_1,alpha=0.9,color='gold',bins=bins,label='Less than two apples')
ax0_1.set_title('Condition 0 vs Condition 1: '+'{}'.format(apple_data),fontsize=14)
ax0_1.set_xlabel('{}'.format(apple_data),fontsize=13)
ax0_1.set_ylabel('Frequency',fontsize=13)
ax0_1.grid(axis='y',linewidth=0.4)
ax0_1.tick_params(axis='x',labelsize=13)
ax0_1.tick_params(axis='y',labelsize=13)
ax0_1_norm = plt.subplot(122)
_,bins,_ = ax0_1_norm.hist(filtered_0,alpha=1,color='b',bins=15,label='All apples',density=True)
ax0_1_norm.hist(filtered_1,alpha=0.9,color='gold',bins=bins,label='Less than two apples',density=True)
ax0_1_norm.set_title('Condition 0 vs Condition 1: '+'{} - Normalized'.format(apple_data),fontsize=14)
ax0_1_norm.set_xlabel('{}'.format(apple_data),fontsize=13)
ax0_1_norm.set_ylabel('Frequency',fontsize=13)
ax0_1_norm.legend(bbox_to_anchor=(2, 0.95))
ax0_1_norm.grid(axis='y',linewidth=0.4)
ax0_1_norm.tick_params(axis='x',labelsize=13)
ax0_1_norm.tick_params(axis='y',labelsize=13)
plt.tight_layout(pad=0.5)
plt.show()
What my current plot looks like
Any ideas on how to make the colors blend a bit better would be helpful. Alternatively, if there are any other combinations you know of that would work instead, feel free to share. I'm not picky about the color choice. Thanks!
I think it is better to emphasize such a histogram by distinguishing it by the shape of the histogram or by the difference in transparency rather than visualizing it by color. I have coded an example from the official reference with additional overlap.
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(20211021)
N_points = 100000
n_bins = 20
x = np.random.randn(N_points)
y = .4 * x + np.random.randn(100000) + 2
fig, axs = plt.subplots(2, 2, sharey=True, tight_layout=True)
# We can set the number of bins with the `bins` kwarg
axs[0,0].hist(x, color='b', alpha=0.9, bins=n_bins, ec='b', fc='None')
axs[0,0].hist(y, color='gold', alpha=0.6, bins=21)
axs[0,0].set_title('edgecolor and facecolor None')
axs[0,1].hist(x, color='b', alpha=0.9, bins=n_bins)
axs[0,1].hist(y, color='gold', alpha=0.6, bins=21, ec='b')
axs[0,1].set_title('edgecolor and facecolor')
axs[1,0].hist(x, alpha=0.9, bins=n_bins, histtype='step', facecolor='b')
axs[1,0].hist(y, color='gold', alpha=0.6, bins=21)
axs[1,0].set_title('step')
axs[1,1].hist(x, color='b', alpha=0.9, bins=n_bins, histtype='bar', rwidth=0.8)
axs[1,1].hist(y, color='gold', alpha=0.6, bins=21, ec='b')
axs[1,1].set_title('bar')
plt.show()
Currently, I have the first y axis (probability) of my subplots aligned. However, I am attempting to get the secondary y axis (sample size) of the subplots aligned. I've tried to simply set the y-axis limit, but this solution isn't very generalizable.
Here is my code:
attacks = 5
crit_rate = .5
idealdata = fullMatrix(attacks, crit_rate)
crit_rate = ("crit_%.0f" % (crit_rate*100))
actualdata = trueDataM(attacks, crit_rate)
[enter image description here][1]
fig, axs = plt.subplots(attacks+1, sharex=True, sharey=True)
axs2 = [ax.twinx() for ax in axs]
fig.text(0.5, 0.04, 'State', ha='center')
fig.text(0.04, 0.5, 'Probability', va='center', rotation='vertical')
fig.text(.95, .5, 'Sample Size', va='center', rotation='vertical')
fig.text(.45, .9, 'Ideal vs. Actual Critical Strike Rate', va='center')
cmap = plt.get_cmap('rainbow')
samplesize = datasample(attacks, 'crit_50')
fig.set_size_inches(18.5, 10.5)
for i in range(attacks+1):
axs[i].plot(idealdata[i], color=cmap(i/attacks), marker='o', lw=3)
axs[i].plot(actualdata[i], 'gray', marker='o', lw=3, ls='--')
axs2[i].bar(range(len(samplesize[i])), samplesize[i], width=.1, color=cmap(i/attacks), alpha = .6)
plt.show()
https://i.stack.imgur.com/HKJlE.png
Without data to confirm my assumptions it's hard to tell if this will be correct.
You are not making any attempt to scale the left y-axes so that data must all have the same range. To ensure the right y-axes all have the same scale/limits you need to determine the range (max and min) of the (all) data being plotted on those axes then apply that to all of them.
It isn't clear whether samplesize is a Numpy ndarray or a lists of lists, I'm also assuming that it is a 2-d structure with range(attacks+1) rows. Since you are making bar charts on the second y-axes you only need to find the largest height in all the data.
# for a list of lists
biggest = max(max(row) for row in samplesize)
# or
biggest = max(map(max,samplesize))
# for an ndarray
biggest = samplesize.max()
Then apply that scale to all the right y-axes before they are shown
for ax in axs2:
ax.set_ylim(top=biggest)
If you determine biggest prior to the plot loop you can just add a line to that loop:
for i in range(attacks+1):
...
axs2[i].set_ylim(top=biggest)
You'll find plenty of related SO Q&A'a searching with the terms: matplotlib subplots same y scale, matplotlib subplots y axis limits or something similar.
Here is a toy example:
from matplotlib import pyplot as plt
import numpy as np
lines = np.random.randint(0,200,(5,10))
bars = [np.random.randint(0,np.random.randint(0,10000),10) for _ in (0,0,0,0,0,)]
fig, axs = plt.subplots(lines.shape[0], sharex=True, sharey=True)
axs2 = [ax.twinx() for ax in axs]
#xs = np.arange(lines.shape[1])
xs = np.arange(1,11)
biggest = max(map(max,bars))
for ax,ax2,line,row in zip(axs,axs2,lines,bars):
bars = ax2.bar(xs,row)
ax.plot(line)
ax2.set_ylim(top=biggest)
plt.show()
plt.close()
I am using matplotlib to make some plots and I have run into a few difficulties that I need help with.
problem 1) In order to keep a consistent colorscheme I need to only use half of the color axis. There are only positive values, so I want the zero values to be green, the mid values to be yellow and the highest values to be red. The color scheme that most closely matches this is gist_rainbow_r, but I only want the top half of it.
problem 2) I can't seem to figure out how to get the colorbar on the right hand side of the plot to show up or how to get it to let me label the axes.
If it helps, I am using the latest version of Anaconda wth the latext version of matplotlib
cmap = plt.get_cmap('gist_rainbow_r')
edosfig2 = plt.figure(2)
edossub2 = edosfig.add_subplot(1,1,1)
edossub2 = plt.contourf(eVec,kints,smallEDOS,cmap=cmap)
edosfig2.show()
If you have a specific set of colors that you want to use for you colormap, you can build it based on those. For example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list('name', ['green', 'yellow', 'red'])
# Generate some data similar to yours
y, x = np.mgrid[-200:1900, -300:2000]
z = np.cos(np.hypot(x, y) / 100) + 1
fig, ax = plt.subplots()
cax = ax.contourf(x, y, z, cmap=cmap)
cbar = fig.colorbar(cax)
cbar.set_label('Z-Values')
plt.show()
However, if you did just want the top half of some particularly complex colormap, you can copy a portion of it by evaluating the colormap over the range you're interested in. For example, if you wanted the "top" half, you'd evaluate it from 0.5 to 1:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
# Evaluate an existing colormap from 0.5 (midpoint) to 1 (upper end)
cmap = plt.get_cmap('gist_earth')
colors = cmap(np.linspace(0.5, 1, cmap.N // 2))
# Create a new colormap from those colors
cmap2 = LinearSegmentedColormap.from_list('Upper Half', colors)
y, x = np.mgrid[-200:1900, -300:2000]
z = np.cos(np.hypot(x, y) / 100) + 1
fig, axes = plt.subplots(ncols=2)
for ax, cmap in zip(axes.flat, [cmap, cmap2]):
cax = ax.imshow(z, cmap=cmap, origin='lower',
extent=[x.min(), x.max(), y.min(), y.max()])
cbar = fig.colorbar(cax, ax=ax, orientation='horizontal')
cbar.set_label(cmap.name)
plt.show()