Set xticks relative to the plot coordinates - python

Is there a simple way to set two xticks at even distances from the xmin and xmax, for two plots with different ranges on the x-axis?
# Example:
from matplotlib import pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(6,4), constrained_layout=True)
gs = gridspec.GridSpec(ncols=2, nrows=1, figure=fig)
x1 = [1, 0.6, 0.4, 0.3, 0.25, 0.24, 0.23]
x2 = [0.1, 0.14, 0.15, 0.16, 0.166, 0.1666, 0.1666 ]
y = [1, 2, 3, 4, 5, 6, 7]
# xticks
number_of_xticks = 2
# Plot 1:
ax0 = fig.add_subplot(gs[0, 0])
ax0.plot(x1, y)
ax0.xaxis.set_major_locator(plt.MaxNLocator(number_of_xticks))
# Plot 2:
ax1 = fig.add_subplot(gs[0, 1])
ax1.plot(x2, y)
ax1.xaxis.set_major_locator(plt.MaxNLocator(number_of_xticks))
plt.show()
Example code does not work because xticks are at different distances from the xmin and xmax in the two plots:

You could try specifying the relative distance along the x-range:
# xticks
tick_fractions = [1/4, 3/4]
And then calculate the tick positions based on each x-range:
mini = min(x)
maxi = max(x)
dist = maxi - mini
ax.set_xticks([mini + f * dist for f in tick_fractions])
So full script would look like:
# Example:
from matplotlib import pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(6,4), constrained_layout=True)
gs = gridspec.GridSpec(ncols=2, nrows=1, figure=fig)
x1 = [1, 0.6, 0.4, 0.3, 0.25, 0.24, 0.23]
x2 = [0.1, 0.14, 0.15, 0.16, 0.166, 0.1666, 0.1666 ]
y = [1, 2, 3, 4, 5, 6, 7]
# xticks
tick_fractions = [1/4, 3/4]
# Plot 1:
ax0 = fig.add_subplot(gs[0, 0])
ax0.plot(x1, y)
mini = min(x1)
maxi = max(x1)
dist = maxi - mini
ax0.set_xticks([mini + f * dist for f in tick_fractions])
# Plot 2:
ax1 = fig.add_subplot(gs[0, 1])
ax1.plot(x2, y)
mini = min(x2)
maxi = max(x2)
dist = maxi - mini
ax1.set_xticks([mini + f * dist for f in tick_fractions])
plt.show()
You could add a call to round somewhere if you want to limit the decimals.

Related

How to arrange bins in stacked histogram, Python

I am working on a code of a stacked histogram and I need help arranging the bins in the order if this is possible.
0.01 - 0.1, 0.1 - 0.5, 0.5 - 1.0, 1.0 - 2.5, > 2.5
Right now, my histogram looks like this:
with the order of bins being:
0.01 - 0.1, 1.0 - 2.5, > 2.5, 0.1 - 0.5, 0.5 - 1.0
Code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data = [['0.01 - 0.1','A'],['0.1 - 0.5','B'],['0.5 - 1.0','B'],['0.01 - 0.1','C'],['> 2.5','A'],['1.0 - 2.5','A'],['> 2.5','A']]
df = pd.DataFrame(data, columns = ['Size','Index'])
### HISTOGRAM OF SIZE
df_new = df.sort_values(['Size'])
x_var = 'Size'
groupby_var = 'Index'
df_new_agg = df_new.loc[:, [x_var, groupby_var]].groupby(groupby_var)
vals = [df_new[x_var].values.tolist() for i, df_new in df_new_agg]
list_of_colors_element = ['lightcoral','palegreen','forestgreen']
# Draw
plt.figure(figsize=(16,10), dpi= 80)
colors = [plt.cm.Spectral(i/float(len(vals)-1)) for i in range(len(vals))]
n, bins, patches = plt.hist(vals, df_new[x_var].unique().__len__(), stacked=True, density=False, color=list_of_colors_element)
# Decorations
plt.legend({group:col for group, col in zip(np.unique(df_new[groupby_var]).tolist(), list_of_colors_element)}, prop={'size': 16})
plt.title("Stacked Histogram of Size colored by element of highest share", fontsize=22)
plt.xlabel(x_var, fontsize=22)
plt.ylabel("Frequency", fontsize=22)
plt.grid(color='black', linestyle='--', linewidth=0.4)
plt.xticks(range(5),fontsize=15)
plt.yticks(fontsize=15)
plt.show()
Any help is appreciated!
You can use:
piv = df_new.assign(dummy=1) \
.pivot_table('dummy', 'Size', 'Index', aggfunc='count', fill_value=0) \
.rename_axis(columns=None)
ax = piv.plot.bar(stacked=True, color=list_of_colors_element, rot=0, width=1)
plt.show()
I think I'd take a different route and represent the input data differently altogether to make the code easier to read.
import matplotlib.pyplot as plt
labels = ['0.01 - 0.1', '0.1 - 0.5', '0.5 - 1', '1.0 - 2.5', '> 2.5']
A = [1, 0, 0, 1, 2]
B = [0, 1, 1, 0, 0]
C = [1, 0, 0, 0, 0]
width = 1
fig, ax = plt.subplots()
ax.bar(labels, A, width, label='A', color='lightcoral')
ax.bar(labels, B, width, bottom=A, label='B', color='palegreen')
ax.bar(labels, C, width, bottom=A, label='C', color='forestgreen')
ax.set_ylabel('Frequency')
ax.set_xlabel('Size')
ax.set_title("Stacked Histogram of Size colored by element of highest share")
plt.show()

Creating a 2-D data plot with "vertical" marginal histograms

How can I create in python a bi-variate data plot with "vertical" marginal histograms like this?:
Say that the data is generated via:
from scipy.stats import multivariate_normal
import numpy as np
mean = np.array([0, 0])
cov = np.array([[1, 0.5], [0.5, 2]])
data = multivariate_normal(mean, cov).rvs(1000)
Here is sample code which shows how one can do this:
import math
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import rc, rcParams
from numpy.linalg import eigh
from scipy.stats import multivariate_normal, norm
from mpl_toolkits.mplot3d import Axes3D
rcParams['text.latex.preamble'] = r'\boldmath'
rc('text', usetex=True)
mean = np.array([0,0])
cov = np.array([[1, 0.3], [0.3, .5]])
np.random.seed(0)
mvn_rvs = multivariate_normal(mean, cov).rvs(800)
pdf_x = norm(mean[0], np.sqrt(cov[0,0])).pdf
pdf_y = norm(mean[1], np.sqrt(cov[1,1])).pdf
rv_x = mvn_rvs[:, 0]
rv_y = mvn_rvs[:, 1]
x = np.linspace(-3, 3, 101)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
fontsize = 30
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1.15, 1.15, 1, 1]))
ax.plot(y, pdf_y(y), zs=x.min(), zdir='x', linewidth=3, label="$\\mathsf{P_y(y)}$")
ax.plot(x, pdf_x(x), zs=y.max(), zdir='y', linewidth=3, label='$\\mathsf{P_x(x)}$')
leg = plt.legend(fontsize=fontsize, ncol=2, frameon=False, bbox_to_anchor=(-0.10, 1.1275),
loc='upper left', handlelength=0.7, handletextpad=0.5, columnspacing=2.4)
grid_linewidth = 1.15
ax.xaxis._axinfo["grid"]['linewidth'] = grid_linewidth
ax.yaxis._axinfo["grid"]['linewidth'] = grid_linewidth
ax.zaxis._axinfo["grid"]['linewidth'] = grid_linewidth
ax.xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.w_xaxis.line.set_color((1.0, 1.0, 1.0, 0.0))
ax.w_yaxis.line.set_color((1.0, 1.0, 1.0, 0.0))
ax.w_zaxis.line.set_color((1.0, 1.0, 1.0, 0.0))
labelpad = -5
ax.set_xlabel("$\\mathsf{x}$", fontsize=fontsize, labelpad=labelpad)
ax.set_ylabel("$\\mathsf{y}$", fontsize=fontsize, labelpad=labelpad)
labelsize = 10
ax.xaxis.set_rotate_label(False)
ax.yaxis.set_rotate_label(False)
ax.set_zlim(bottom=0)
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.xaxis.set_ticklabels([])
ax.xaxis.set_visible(False)
ax.yaxis.set_ticklabels([])
ax.zaxis.set_ticklabels([])
sx2 = cov[0, 0]
sy2 = cov[1, 1]
rho = cov[0, 1] / np.sqrt(sx2 * sy2)
Sigma = cov
target = 0.1
gamma = math.log(1 / (4*(np.pi**2)*(sx2**2)*(sy2**2)*(1 - rho**2)*(target**2)))
eigenvalues, P = eigh(np.linalg.inv(Sigma))
# Compute u and v as per link using thetas from 0 to 2pi
thetas = np.linspace(0, 2*np.pi, 10000)
uv = (np.sqrt(gamma) / np.sqrt(eigenvalues)) * np.hstack((np.cos(thetas).reshape(-1,1), np.sin(thetas).reshape(-1, 1)))
orig_coord=np.zeros((10000,2))
for i in range(len(uv)):
orig_coord[i,0]=np.matmul(np.linalg.inv(P), uv[i,:])[0]
orig_coord[i,1]=np.matmul(np.linalg.inv(P), uv[i,:])[1]
ax.plot(rv_x, rv_y, 0*rv_x, ' o', c='g', markersize=1.1) # "RdBu_r")
ax.plot(orig_coord[:, 0], orig_coord[:, 1],
0 * np.ones_like(orig_coord[:, 0]), c='r', linewidth=3)
ax.view_init(azim=-45, elev=20)

Adding colorbars to clustered heatmaps

I am trying to replicate this type of plot (heatmap with colorbars as leaves)
This is what I've done so far
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage
import scipy.cluster.hierarchy as sch
import scipy.spatial.distance as ssd
#read data
fid_df = pd.read_csv(fid_file, index_col=[0])
# scale data
def scale(x):
return np.math.log2(x+1)
fid_df = fid_df.applymap(scale)
# clustering colums
data_1D_X = ssd.pdist(fid_df.T, 'euclidean')
X = sch.linkage(data_1D_X, method='ward')
# clustering rows
data_1D_Y = ssd.pdist(fid_df, 'cityblock')
Y = linkage(data_1D_Y, method='ward')
#plot first dendrogram
fig = plt.figure(figsize=(8, 8))
ax1 = fig.add_axes([0.09, 0.1, 0.2, 0.6])
Z1 = sch.dendrogram(Y, orientation='left')
ax1.set_xticks([])
ax1.set_yticks([])
# second dendrogram.
ax2 = fig.add_axes([0.3, 0.71, 0.6, 0.2])
Z2 = sch.dendrogram(X)
ax2.set_xticks([])
ax2.set_yticks([])
# plot matrix
axmatrix = fig.add_axes([0.3, 0.1, 0.6, 0.6])
# sorts based of clustering
idx1 = Z1['leaves']
idx2 = Z2['leaves']
D = fid_df.values[idx1, :]
D = D[:, idx2]
im = axmatrix.matshow(D, aspect='auto', origin='lower', cmap=plt.cm.YlGnBu)
axmatrix.set_xticks([])
axmatrix.set_yticks([])
Example:
However, I need to add colorbars that would show the initial groups of rows and columns. Any idea how to do this?
Something like this?
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_axes((0, 0, 1, 0.9))
ax2 = fig.add_axes((0, 0.9, 1, 0.1))
gridY, gridX = np.mgrid[0:10:11 * 1j, 0:10:11 * 1j]
ax1.pcolormesh(gridX, gridY, np.sqrt(gridX ** 2 + gridY ** 2))
randCol = ['red', 'blue']
for value in np.linspace(0, 10, 1001):
ax2.axvline(value, color=randCol[np.random.default_rng().integers(2)])
ax2.set_xlim((0, 10))
ax2.tick_params(labelbottom=False, bottom=False, labelleft=False, left=False)
fig.savefig('so.png', bbox_inches='tight')

Polar chart with limit and anomalous points

Consider the following data frame,
d = {'Score': [0.25, 0.52, 0.26, 0.22, 0.31, 2.45, 3.68, 41.3, 87, 91],
'Thr1': 16.5,
'Thr2': 45.5,
'Anomaly':[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]}
df = pd.DataFrame(data = d)
What I m trying to do is to plot a polar chart, with a dotted line for threshold or multiple dotted lines for multiple thresholds and different color for the anomalies. What I ve got so far is,
r = df['Score']
theta = df.index.values
fig = plt.figure()
ax = fig.add_subplot(111, projection = 'polar')
c = ax.scatter(theta, r)
I cannot get the threshold though and change the color of the anomalous points. Any ideas?
You need to draw a dashed line at the threshold level, to indicate where the threshold is. (a line will appear as a circle on a polar plot).
Then you need to segregate the values to plot on the scatter plot, based whether or not they are below, between, or above the thresholds, and color the points accordingly.
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
dataset = {'Score': [0.25, 0.52, 0.26, 0.22, 0.31, 2.45, 3.68, 41.3, 87, 91],
'Thr1': 16.5,
'Thr2': 45.5,
'Anomaly':[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]}
df = pd.DataFrame(data=dataset)
scores = df['Score']
theta, thr_1, thr_2 = df.index.values, dataset['Thr1'], dataset['Thr2']
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
# assigns a color to each point based on their relative value to the thresholds
colors = ['b' if val < thr_1 else 'y' if val < thr_2 else 'r' for val in scores]
point_cloud = ax.scatter(theta, scores, color=colors, marker='o')
# Drawing the threshold dash lines (with alpha value 1/2)
theta_xs, thr_y1, thr_y2 = np.linspace(0, 2*np.pi, 20), [thr_1] * 20, [thr_2] * 20
thr_line_1 = ax.plot(theta_xs, thr_y1, color='blue', linestyle='--', alpha=0.5)
thr_line_2 = ax.plot(theta_xs, thr_y2, color='green', linestyle='--', alpha=0.5)
plt.show()
Well, i'm not exactly sure that it is what you want, because i never used Anomaly part of your dataset, and just take color info from Score array
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as c
d = {'Score': [0.25, 0.52, 0.26, 0.22, 0.31, 2.45, 3.68, 41.3, 87, 91],
'Thr1': 16.5,
'Thr2': 45.5,
'Anomaly': [0, 0, 0, 0, 0, 0, 0, 1, 1, 1]}
df = pd.DataFrame(data = d)
r = df['Score']
theta = df.index.values
fig = plt.figure()
ax = fig.add_subplot(111, projection = 'polar')
#Add thresholds
ax.plot(np.linspace(0, 2*np.pi, 100), np.ones(100)*d['Thr1'], c='g', ls='--')
ax.plot(np.linspace(0, 2*np.pi, 100), np.ones(100)*d['Thr2'], c='r', ls='--')
#Add colors
colors = ['g' if v < d['Thr1'] else 'y' if v < d['Thr2'] else "r" for v in r]
sc = ax.scatter(theta, r, c=colors)
plt.show()

Custom Colorbar-like plot with matplotlib

I'm looking to make a colorbar like plot, like so:
but with a controllable color, for example I have the following x and y arrays:
x = [0,1,2,4,7,8]
y = [1,2,1,3,4,5]
Then I would have a colorbar like the above picture, but when y=1, it would color red, y=2: green, y=3: blue, y=4:black, etc.
Here is the python code that I modified from matplotlib's gallery:
from matplotlib import pyplot
import matplotlib as mpl
fig = pyplot.figure(figsize=(8,1))
ax2 = fig.add_axes([0.05, 0.25, 0.9, 0.5])
cmap = mpl.cm.Accent
norm = mpl.colors.Normalize(vmin=5, vmax=10)
bounds = [1, 2, 4, 7, 8]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap=cmap,
norm=norm,
boundaries=[0]+bounds+[13],
ticks=bounds, # optional
spacing='proportional',
orientation='horizontal')
After adapting your code I managed to obtain something like you described.
In this case the colormap is generated using ListedColormap and I added the yellow color for y=5.
It is important to notice that while calculating the BoundaryNorm I am using the intervals that contain the values you described for y.
from matplotlib import pyplot,colors
import matplotlib as mpl
fig = pyplot.figure(figsize=(8,1))
ax2 = fig.add_axes([0.05, 0.25, 0.9, 0.5])
cmap = colors.ListedColormap(['r', 'g', 'b', 'k','y'])
bounds = [0, 1, 2, 4, 7, 8, 13]
yVals = [ 1, 2, 1, 3, 4, 5]
cBounds = [i+0.5 for i in range(6)]
norm = mpl.colors.BoundaryNorm(cBounds, cmap.N)
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap=cmap,
norm=norm,
values=yVals,
boundaries=bounds,
ticks=bounds[1:-1], # optional
spacing='proportional',
orientation='horizontal')
-- Edited 14 of Jan (mrcl) --
Alternatively, you can use pcolormesh to plot your colormap and have a colorbar as your legend, such as in the example below.
from pylab import *
from matplotlib import pyplot,colors
import matplotlib as mpl
fig = pyplot.figure(figsize=(8,1.5))
ax1 = fig.add_axes([0.05, 0.25, 0.82, 0.5])
cmap = colors.ListedColormap(['r', 'g', 'b', 'k','y'])
xBounds = array([0, 1, 2, 4, 7, 8, 13])
yBounds = array([0, 1])
Vals = array([[ 1, 2, 1, 3, 4, 5]])
cBounds = [i+0.5 for i in arange(amax(Vals)+1)]
norm = mpl.colors.BoundaryNorm(cBounds, cmap.N)
c = ax1.pcolormesh(xBounds,yBounds,Vals,cmap=cmap,norm=norm)
ax1.set_xticks(xBounds[1:-1])
ax1.set_yticks([])
ax1.set_xlim(xBounds[0],xBounds[-1])
ax1.set_ylim(yBounds[0],yBounds[-1])
ax2 = fig.add_axes([0.9, 0.25, 0.05, 0.5])
colorbar(c,cax=ax2,ticks=arange(amax(Vals))+1)
Hope it helps.
Cheers
Well, I sort of tinkering with other ways:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cmx
import matplotlib.colors as colors
close('all')
def ColorPlot(x,y):
figure()
jet = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=min(y), vmax=max(y))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
if len(x) == len(y):
x.insert(0,0)
for kk in range(len(x)-1):
colorVal = scalarMap.to_rgba(y[kk])
plt.axvspan(x[kk], x[kk+1], facecolor=colorVal,
alpha=0.5,label=colorVal)
plt.yticks([])
plt.xticks(x)
xlim([x[0],x[-1]])
plt.show()
x = [1,3,5,6,10,12]
y = [1,3,4,1,4,3]
ColorPlot(x,y)

Categories

Resources