How to unstacked yticklabel in matplotlib.pcolor - python

The following code is the minimum amount of code needed for reproducing the my heat map problem (full code in comments):
import numpy as np
import string
from matplotlib import pylab as plt
def random_letter(chars=string.ascii_uppercase, size=2):
char_arr = np.array(list(chars))
if size > 27:
size = 27
np.random.shuffle(char_arr)
return char_arr[:size]
y_labels = [', '.join(x for x in random_letter()) for _ in range(174)]
fig.set_size_inches(11.7, 16.5)
fig, ax = plt.subplots()
data = np.random.poisson(1, (174, 40))
heatmap = ax.pcolor(data,
cmap=plt.cm.Blues,
vmin=data.min(),
vmax=data.max(),
edgecolors='white')
ax.set_xticks(np.arange(data.shape[1])+.5, minor=False);
ax.set_yticks(np.arange(data.shape[0])+.5, minor=False);
ax.set_xticklabels(np.arange(40),
minor=False,
rotation=90,
fontsize='x-small',
weight='bold');
ax.set_yticklabels(y_labels,
minor=False,
fontsize='x-small',
weight='bold');
cb = fig.colorbar(heatmap, shrink=0.33, aspect=10)
My question is: Is there a way of increase the distance between the y tick labels? When I print this in A3 (good size) it still almost unreadable because the letters are stacking in each other. I'd tried to fix the yticklabelpads without success.
Thank you all in advance

Related

Seaborn boxplot : set median color and set tick label colors to boxes color

I'm using this nice boxplot graph, answer from #Parfait.
I got an out of bound error on j and had to use range(i*5,i*5+5). Why?
I'd like to set the median to a particular color, let's say red. medianprops=dict(color="red") won't work. How to do it?
How to set the y-axis tick labels to the same color as the boxes?
Disclaimer: I don't know what I'm doing.
Here's the code using random data :
# import the required library
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import string
import matplotlib.colors as mc
import colorsys
# data
df = pd.DataFrame(np.random.normal(np.random.randint(5,15),np.random.randint(1,5),size=(100, 16)), columns=list(string.ascii_uppercase)[:16])
# Boxplot
fig, ax = plt.subplots(figsize=(9, 10))
medianprops=dict(color="red")
ax = sns.boxplot(data=df, orient="h", showfliers=False, palette = "husl")
ax = sns.stripplot(data=df, orient="h", jitter=True, size=7, alpha=0.5, palette = "husl") # show data points
ax.set_title("Title")
plt.xlabel("X label")
def lighten_color(color, amount=0.5):
# --------------------- SOURCE: #IanHincks ---------------------
try:
c = mc.cnames[color]
except:
c = color
c = colorsys.rgb_to_hls(*mc.to_rgb(c))
return colorsys.hls_to_rgb(c[0], 1 - amount * (1 - c[1]), c[2])
for i,artist in enumerate(ax.artists):
# Set the linecolor on the artist to the facecolor, and set the facecolor to None
col = lighten_color(artist.get_facecolor(), 1.2)
artist.set_edgecolor(col)
# Each box has 6 associated Line2D objects (to make the whiskers, fliers, etc.)
# Loop over them here, and use the same colour as above
for j in range(i*5,i*5+5):
line = ax.lines[j]
line.set_color(col)
line.set_mfc(col)
line.set_mec(col)
#line.set_linewidth(0.5)
To change the color of the median, you can use the medianprops in sns.boxplot(..., medianprops=...). If you also set a unique label, that label can be tested again when iterating through the lines.
To know how many lines belong to each boxplot, you can divide the number of lines by the number of artists (just after the boxplot has been created, before other elements have been added to the plot). Note that a line potentially has 3 colors: the line color, the marker face color and the marker edge color. Matplotlib creates the fliers as an invisible line with markers. The code below thus also changes these colors to make it more robust to different options and possible future changes.
Looping simultaneously through the boxes and the y tick labels allows copying the color. Making them a bit larger and darker helps for readability.
import matplotlib.pyplot as plt
from matplotlib.colors import rgb_to_hsv, hsv_to_rgb, to_rgb
import seaborn as sns
import pandas as pd
import numpy as np
def enlighten(color, factor=0.5):
h, s, v = rgb_to_hsv(to_rgb(color))
return hsv_to_rgb((h, s, 1 - factor * (1 - v)))
def endarken(color, factor=0.5):
h, s, v = rgb_to_hsv(to_rgb(color))
return hsv_to_rgb((h, s, factor * v))
df = pd.DataFrame(np.random.normal(1, 5, size=(100, 16)).cumsum(axis=0),
columns=['Hydrogen', 'Helium', 'Lithium', 'Beryllium', 'Boron', 'Carbon', 'Nitrogen', 'Oxygen',
'Fluorine', 'Neon', 'Sodium', 'Magnesium', 'Aluminum', 'Silicon', 'Phosphorus', 'Sulfur'])
sns.set_style('white')
fig, ax = plt.subplots(figsize=(9, 10))
colors = sns.color_palette("husl", len(df.columns))
sns.boxplot(data=df, orient="h", showfliers=False, palette='husl',
medianprops=dict(color="yellow", label='median'), ax=ax)
lines_per_boxplot = len(ax.lines) // len(ax.artists)
for i, (box, ytick) in enumerate(zip(ax.artists, ax.get_yticklabels())):
ytick.set_color(endarken(box.get_facecolor()))
ytick.set_fontsize(20)
color = enlighten(box.get_facecolor())
box.set_color(color)
for lin in ax.lines[i * lines_per_boxplot: (i + 1) * lines_per_boxplot]:
if lin.get_label() != 'median':
lin.set_color(color)
lin.set_markerfacecolor(color)
lin.set_markeredgecolor(color)
sns.stripplot(data=df, orient="h", jitter=True, size=7, alpha=0.5, palette='husl', ax=ax)
sns.despine(ax=ax)
ax.set_title("Title")
ax.set_xlabel("X label")
plt.tight_layout()
plt.show()
I just answer point 2. of my question.
After tinkering, I found this to work :
# Each box has 5 associated Line2D objects (the whiskers and median)
# Loop over them here, and use the same colour as above
n=5 # this was for tinkering
for j in range(i*n,i*n+n):
if j != i*n+4 : line = ax.lines[j] # not the median
line.set_color(col)
Again, I don't know what I'm doing. So someone more knowledgeable may provide a more valuable answer.
I removed the stripplot for better clarity.

How to remove marker from plot and make it smooth

I have been trying to plot a smooth graph, and here is my code
import matplotlib.pyplot as plt
#fig,axes= plt.subplots(nrows=6, ncols=1, squeeze=False)
x = df["DOY"]
y = df["By"]
z = df["Bz"]
a = df["Vsw"]
b = df["Nsw"]
c = df["magnetopause_distance"]
d = df["reconnection_rate"]
And after that, I used the following logic to plot the same
#create a figure
fig=plt.figure()
#define subplots and define their position
plt1=fig.add_subplot(611)
plt2=fig.add_subplot(612)
plt3=fig.add_subplot(613)
plt4=fig.add_subplot(614)
plt5=fig.add_subplot(615)
plt6=fig.add_subplot(616)
plt1.plot(x,y,'black',linewidth=0.5,marker=None)
plt1.set_ylabel("By")
plt1.set_title("3-6 July 2003")
plt2.plot(x,z,'black',linewidth=0.5)
plt2.set_ylabel("Bz")
plt3.plot(x,a,'black',linewidth=0.5)
plt3.set_ylabel("Vsw")
plt4.plot(x,b,'black',linewidth=0.5)
plt4.set_ylabel("Nsw")
plt5.plot(x,c,'black',linewidth=0.5)
plt5.set_ylabel("MD")
plt6.plot(x,d,'black',linewidth=0.5)
plt6.set_ylabel("MRR")
plt.subplots_adjust(hspace = 2,wspace = 2)
#saving plot in .jpg format
plt.savefig('myplot01.jpg', format='jpeg',dpi=500, bbox_inches='tight')
Finally, I am getting a plot like this:
What I want is something like this:
Sorry for the typos. Thanks for your time :)
Use:
from scipy.interpolate import UnivariateSpline
import numpy as np
list_x_new = np.linspace(min(x), max(x), 1000)
list_y_smooth = UnivariateSpline(x, y, list_x_new)
plt.plot(list_x_new, list_y_smooth)
plt.show()
This is for one of the graphs, you can substitute the values in list_y_smooth in place of y according to the values you want to plot.

Matplotlib : How to populate the below chart having all the x-axis labels and grid lines accordingly?

data = {'tenor': ['1w','1m','3m','6m','12m','1y','2y','3y','4y','5y','6y','7y','10y','15y','20y','25y','30y','40y','50y'],'rate_s': [0.02514, 0.026285, 0.0273, 0.0279, 0.029616, 0.026526, 0.026028, 0.024, 0.025958,0.0261375, 0.026355, 0.026, 0.026898, 0.0271745, 0.02741, 0.027, 0.0275, 0.0289,0.0284],'rate_t':[ 0.02314, 0.024285, 0.0253,0.0279, 0.028616, 0.026526,0.027028, 0.024, 0.025958,0.0271375, 0.02355, 0.026, 0.024898, 0.0271745, 0.02641,0.027, 0.0255, 0.0289,0.0284]}
I want to produce the chart in blue with the same format like below. I tried this piece of code but results are not satisfactory (chart in white). It also not showing all x-axis labels. Please suggest.
ax = plt.gca()
df.plot(kind='line',x='tenor',y='rate_s',marker='o',color='green',ax=ax)
df.plot(kind='line',x='tenor',y='rate_y',marker='o', color='red', ax=ax)
ax.minorticks_on()
ax.grid(which='major',linestyle='-', linewidth='0.5', color='blue')
ax.grid(which='minor', linestyle=':', linewidth='0.5', color='black')
plt.show()
This is following the discussions in the comments.
There are a couple parts, the full example is at the bottom.
Style
One of your questions was how to change the style of the plot. This can be done with the following code:
import matplotlib.pyplot as plt
plt.style.use('seaborn-darkgrid')
there are many possible styles, and you can even create your own style if you wish. To see all possible styles see: the documentation. To list all styles use plt.style.available
Custom Ticker
For the custom tickers: you can use FixedLocator or if you know it is log or symlog, then matplotlib has a built-in locator. See the matplotlib doc for scales
You can use FixedLocator to set up the axis, to be separated. i.e. the following code will give you what you want.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
X = np.arange(0, 2000)
Y = np.arange(0, 2000)
def convert(date):
if 'w' in date:
return 7*int(date[:-1])
if 'm' in date:
return 30*int(date[:-1])
if 'y' in date:
return 30*int(date[:-1]) + 360
ticks = [convertdate(d) for d in tenor]
plt.style.use('seaborn-darkgrid')
ax = plt.axes()
t = ticker.FixedLocator(locs=ticks)
ax.xaxis.set_ticklabels(tenor)
ax.xaxis.set_major_locator(t)
# ax.xaxis.set_minor_locator(ticker.MultipleLocator(3))
plt.plot(X, Y, c = 'k')
plt.show()
Which produces:
Specific Case
For your specific case, you probably want the custom tickers to be on a specific interval (i.e. smallest of rate_t, biggest of rate_t).
Thus you would need to change the convert function to be as following:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = data['rate_t']
y = data['rate_s']
def get_indices(date):
if 'w' in date:
return 7*int(date[:-1])
if 'm' in date:
return 30*int(date[:-1])
if 'y' in date:
return 30*int(date[:-1]) + 360
def convert(indices):
x = np.linspace(min(data['rate_t']), max(data['rate_t']), indices[-1] + 1)
return x[indices]
indices = [get_indices(d) for d in tenor]
ticks = convert(indices)
plt.style.use('seaborn-darkgrid')
ax = plt.axes()
t = ticker.FixedLocator(locs=ticks)
ax.xaxis.set_ticklabels(tenor)
ax.xaxis.set_major_locator(t)
# ax.xaxis.set_minor_locator(ticker.MultipleLocator(3))
plt.plot(x, y, c = 'k')
plt.show()
(assuming the data['rate_s'] and data['rate_t'] are as is and without processing)
Which would produce this:
Let me know if you have any questions.

python scatter plot with errorbars and colors mapping a physical quantity

I'm trying to do a quite simple scatter plot with error bars and semilogy scale. What is a little bit different from tutorials I have found is that the color of the scatterplot should trace a different quantity. On one hand, I was able to do a scatterplot with the errorbars with my data, but just with one color. On the other hand, I realized a scatterplot with the right colors, but without the errorbars.
I'm not able to combine the two different things.
Here an example using fake data:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
n=100
Lx_gas = 1e40*np.random.random(n) + 1e37
Tx_gas = np.random.random(n) + 0.5
Lx_plus_error = Lx_gas
Tx_plus_error = Tx_gas/2.
Tx_minus_error = Tx_gas/4.
#actually positive numbers, this is the quantity that should be traced by the
#color, in this example I use random numbers
Lambda = np.random.random(n)
#this is actually different from zero, but I want to be sure that this simple
#code works with the log axis
Lx_minus_error = np.zeros_like(Lx_gas)
#normalize the color, to be between 0 and 1
colors = np.asarray(Lambda)
colors -= colors.min()
colors *= (1./colors.max())
#build the error arrays
Lx_error = [Lx_minus_error, Lx_plus_error]
Tx_error = [Tx_minus_error, Tx_plus_error]
##--------------
##important part of the script
##this works, but all the dots are of the same color
#plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error,fmt='o')
##this is what is should be in terms of colors, but it is without the error bars
#plt.scatter(Tx_gas, Lx_gas, marker='s', c=colors)
##what I tried (and failed)
plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error,\
color=colors, fmt='o')
ax = plt.gca()
ax.set_yscale('log')
plt.show()
I even tried to plot the scatterplot after the errorbar, but for some reason everything plotted on the same window is put in background with respect to the errorplot.
Any ideas?
Thanks!
You can set the color to the LineCollection object returned by the errorbar as described here.
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
n=100
Lx_gas = 1e40*np.random.random(n) + 1e37
Tx_gas = np.random.random(n) + 0.5
Lx_plus_error = Lx_gas
Tx_plus_error = Tx_gas/2.
Tx_minus_error = Tx_gas/4.
#actually positive numbers, this is the quantity that should be traced by the
#color, in this example I use random numbers
Lambda = np.random.random(n)
#this is actually different from zero, but I want to be sure that this simple
#code works with the log axis
Lx_minus_error = np.zeros_like(Lx_gas)
#normalize the color, to be between 0 and 1
colors = np.asarray(Lambda)
colors -= colors.min()
colors *= (1./colors.max())
#build the error arrays
Lx_error = [Lx_minus_error, Lx_plus_error]
Tx_error = [Tx_minus_error, Tx_plus_error]
sct = plt.scatter(Tx_gas, Lx_gas, marker='s', c=colors)
cb = plt.colorbar(sct)
_, __ , errorlinecollection = plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error, marker = '', ls = '', zorder = 0)
error_color = sct.to_rgba(colors)
errorlinecollection[0].set_color(error_color)
errorlinecollection[1].set_color(error_color)
ax = plt.gca()
ax.set_yscale('log')
plt.show()

How to remove axis in pyplot.bar?

Is there any possibility to do a bar plot without y-(x-)axis? In presentations all redundant informations have to be erased, so I would like to begin to delete the axis. I did not see helpful informations in the matplotlib documentation. Maybe you have better solutions than pyplot..?
Edit: I would like to have lines around the bars except the axis at the bottom. Is this possible
#!/usr/bin/env python
import matplotlib.pyplot as plt
ind = (1,2,3)
width = 0.8
fig = plt.figure(1)
p1 = plt.bar(ind,ind)
# plt.show()
fig.savefig("test.svg")
Edit: I did not see using plt.show()
that there is still the yaxis without ticks.
To make the axes not visible, try something like
import matplotlib.pyplot as plt
ind = (1,2,3)
width = 0.8
fig,a = plt.subplots()
p1 = a.bar(ind,ind)
a.xaxis.set_visible(False)
a.yaxis.set_visible(False)
plt.show()
Is this what you meant?
Here is the code I used at the end. It is not minimal anymore. Maybe it helps.
import matplotlib.pyplot as plt
import numpy as np
def adjust_spines(ax,spines):
for loc, spine in ax.spines.items():
if loc in spines:
spine.set_smart_bounds(True)
else:
spine.set_color('none') # don't draw spine
# turn off ticks where there is no spine
if 'left' in spines:
ax.yaxis.set_ticks_position('left')
else:
# no yaxis ticks
ax.yaxis.set_ticks([])
def nbar(samples, data, err, bWidth=0.4, bSafe=True, svgName='out'):
fig,a = plt.subplots(frameon=False)
if len(data)!=len(samples):
print("length(data) must be equal to length(samples)!")
return
ticks = np.arange(len(data))
p1 = plt.bar(ticks, data, bWidth, yerr=err)
plt.xticks(ticks+bWidth/2., samples )
adjust_spines(a,['bottom'])
a.xaxis.tick_bottom()
if bSafe:
fig.savefig(svgName+".svg")
samples = ('Sample1', 'Sample2','Sample3')
qyss = (91, 44, 59)
qysserr = (1,5,4)
nbar(samples,qyss,qysserr,svgName="test")
Thx to all contributors.

Categories