Matplotlib TwoSlopeNorm use base2 in colorbar - matplotlib

is there a way to get TwoSlopeNorm in combination with base 2 ticks on the colorbar?
An example is something like this where you have normal linear scaling:
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
x = np.arange(-50,100,1)
y = x.copy()
c = x.copy()
scatter_plot = plt.scatter(x, y, c=c, cmap='bwr', norm=matplotlib.colors.TwoSlopeNorm(vmin=-50, vcenter=0, vmax=100))
cbar = plt.colorbar(scatter_plot)
plt.show()
I know based on a previous question of mine that SymLogNorm supports base2, but it looks like this is not the case for TwoSlopeNorm. Does anyone have a suggestion on how to do it?

Related

Cannot set ylim for sklearn partial dependence plot

I am following this example on sklearn documentation
I want to change the limits of y axis so I can visually compare results from different models.
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.tree import DecisionTreeRegressor
from sklearn.inspection import PartialDependenceDisplay
diabetes = load_diabetes()
X = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
y = diabetes.target
tree = DecisionTreeRegressor()
tree.fit(X, y)
fig, ax = plt.subplots(figsize=(12, 6))
ax.set_ylim(50,300)
tree_disp = PartialDependenceDisplay.from_estimator(tree, X, ["age", "bmi"], ax=ax)
However, it seems that ax.set_ylim get ignored no matter what I specify. On the other hand, ax.set_title given in example works fine.
PartialDependenceDisplay have an axes_ attribute that represents both matplotlib's axes of the figure.
You can modify them as follow:
tree_disp = PartialDependenceDisplay.from_estimator(tree, X, ["age", "bmi"], ax=ax)
tree_disp.axes_[0][0].set_ylim(50,300)
tree_disp.axes_[0][1].set_ylim(50,300)
This will output the following plot:

Making sure 0 gets white in a RdBu colorbar

I create a heatmap with the following snippet:
import numpy as np
import matplotlib.pyplot as plt
d = np.random.normal(.4,2,(10,10))
plt.imshow(d,cmap=plt.cm.RdBu)
plt.colorbar()
plt.show()
The result is plot below:
Now, since the middle point of the data is not 0, the cells in which the colormap has value 0 are not white, but rather a little reddish.
How do I force the colormap so that max=blue, min=red and 0=white?
Use a DivergingNorm.
Note: From matplotlib 3.2 onwards DivergingNorm is renamed to TwoSlopeNorm.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
d = np.random.normal(.4,2,(10,10))
norm = mcolors.DivergingNorm(vmin=d.min(), vmax = d.max(), vcenter=0)
plt.imshow(d, cmap=plt.cm.RdBu, norm=norm)
plt.colorbar()
plt.show()
A previous SO post (Change colorbar gradient in matplotlib) wanted a solution for a more complicated situation, but one of the answers talked about the MidpointNormalize subclass in the matplotlib documentation. With that, the solution becomes:
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
class MidpointNormalize(mpl.colors.Normalize):
## class from the mpl docs:
# https://matplotlib.org/users/colormapnorms.html
def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
self.midpoint = midpoint
super().__init__(vmin, vmax, clip)
def __call__(self, value, clip=None):
# I'm ignoring masked values and all kinds of edge cases to make a
# simple example...
x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
return np.ma.masked_array(np.interp(value, x, y))
d = np.random.normal(.4,2,(10,10))
plt.imshow(d,cmap=plt.cm.RdBu,norm=MidpointNormalize(midpoint=0))
plt.colorbar()
plt.show()
Kudos to Joe Kington for writing the subclass, and to Rutger Kassies for pointing out the answer.

Visualize 1-dimensional data in a sequential colormap

I have a pandas series containing numbers ranging between 0 and 100. I want to visualise it in a horizontal bar consisting of 3 main colours.
I have tried using seaborn but all I can get is a heatmap matrix. I have also tried the below code, which is producing what I need but not in the way I need it.
x = my_column.values
y = x
t = x
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.scatter(x, y, c=t, cmap='brg')
ax2.scatter(x, y, c=t, cmap='brg')
plt.show()
What I'm looking for is something similar to the below figure, how can I achieve that using matplotlib or seaborn?
The purpose of this is not quite clear, however, the following would produce an image like the one shown in the question:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
x = np.linspace(100,0,101)
fig, ax = plt.subplots(figsize=(6,1), constrained_layout=True)
cmap = LinearSegmentedColormap.from_list("", ["limegreen", "gold", "crimson"])
ax.imshow([x], cmap=cmap, aspect="auto",
extent=[x[0]-np.diff(x)[0]/2, x[-1]+np.diff(x)[0]/2,0,1])
ax.tick_params(axis="y", left=False, labelleft=False)
plt.show()

Matplotlib double legend

With my code I get 2 equations in the legend that are the same. I don't how why it is so. I just want to correct this by making it only one equation. How can I do that? This equation is the line fit result of some of the data below.
Thanks in advance!
import matplotlib.pyplot as plt
import numpy as np
import plotly.plotly as py
import plotly.tools as tls
from sympy import S, symbols
import sympy
y = [2.7,2.3,1.9,1.5,1.3,1.0,0.8,0.6,0.5,0.4,0.2,0.1,0.0,0.0,-0.20,-0.2]
y = [i*10**(-16) for i in y]
x = [0,0.05,0.10,0.15,0.20,0.25,0.30,0.40,0.45,0.50,0.55,0.60,0.65,0.70,0.75,0.80]
e_y = [10**(-17)]* 16
e_x = [0.001] * 16
fig= plt.figure()
ax = fig.add_subplot(111)
ax.errorbar(x,y, yerr=e_y,xerr=0.001,fmt='-o')
ax.set_title('Current vs. Potential')
ax.set_xlabel('Retarding Potential')
ax.set_ylabel('Photocell Current')
x=x[:7]
y=y[:7]
e_y=e_y[:7]
e_x=e_x[:7]
#line fit:
fit=np.polyfit(x,y,1)
fit_fn = np.poly1d(fit)
a=symbols("x")
line = sum(S(format(v))*a**i for i, v in enumerate(fit[::-1]))
eq_latex = sympy.printing.latex(line)
plt.plot(x,y,x,fit_fn(x),label="${}$".format(eq_latex))
plt.legend(fontsize='small')
plt.show()
I solved this using the following:
#import matplotlib.patches as mpatches
plt.plot(x,y,x,fit_fn(x))
eqn = mpatches.Patch(color='green',label="${}$".format(eq_latex))
plt.legend(handles=[eqn])
instead of
plt.plot(x,y,x,fit_fn(x),label="${}$".format(eq_latex))
plt.legend(fontsize='small')

Working with ipywidgets and images [duplicate]

I am trying to generate an interactive plot that depends on widgets.
The problem I have is that when I change parameters using the slider, a new plot is done after the previous one, instead I would expect only one plot changing according to the parameters.
Example:
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
def plot_func(freq):
x = np.linspace(0, 2*np.pi)
y = np.sin(x * freq)
plt.plot(x, y)
interact(plot_func, freq = widgets.FloatSlider(value=7.5,
min=1,
max=5.0,
step=0.5))
After moving the slider to 4.0, I have:
while I just want one figure to change as I move the slider.
How can I achieve this?
(I am using Python 2.7, matplotlib 2.0 and I have just updated notebook and jupyter to the latest version. let me know if further info is needed.)
As you want to change the figure, instead of creating a new one, may I suggest the following way:
Use an interactive backend; %matplotlib notebook
Update the line in the plot, instead of drawing new ones.
So the code could look something like this:
%matplotlib notebook
from ipywidgets import *
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
line, = ax.plot(x, np.sin(x))
def update(w = 1.0):
line.set_ydata(np.sin(w * x))
fig.canvas.draw_idle()
interact(update);
Alternatively you may use plt.show() as in this answer.
This is an issue (?) introduced in the last version of jupyter and/or ipywidgets. One workaround I found was to add the line plt.show() at the end of plot_func.
For completion, here is an answer that makes use of more than one slider bar and sets the default parameters as well as the interval lengths.
%matplotlib notebook
from ipywidgets import *
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10, 10,100)
def f(x, A, B, C):
return A*x**2 + B*x + C
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
line, = ax.plot(x, f(x, A=1, B=1, C=1))
def update(A = 1, B = 0, C = 0):
line.set_ydata(f(x,A,B,C))
fig.canvas.draw_idle()
interact(update, A = (-4,4,0.1), B = (-4,4,0.1), C = (-4,4,0.1));