I cannot add a colorbar to my 3D scatter plot that is coloured in range of min and max according to the value of bifurWidth. I've tried various attempts shown on stackoverflow, none have had any success. Any help would really be appreciated, as I am at a major loss with this.
My most recent attempt is hashed out of the code shown below.
My code:
from glob import glob
from pylab import *
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
def myScatter(x0,y0,power_array,c,lw,s,vmin,vmax,cmap,label,ax):
ax.scatter(x0,y0,power_array,c=c,lw=lw,s=s,vmin=min,vmax=max,cmap=cmhot,label=label)
fig = figure()
ax = fig.add_subplot(111, projection='3d')
cmhot = get_cmap("jet")
fig.tight_layout()
fig.set_size_inches(25,15)
min = 3 #colorbar range
max = 10
lw = 0 #linewidth
s = 10 #scatter size
for idx, p in enumerate(dataSorted[:,1]):
powerLoop = dataSorted[idx,0]
powerLoop = powerLoop.astype(np.float)
bifurWidthLoop = dataSorted[idx,2]
bifurWidthLoop = bifurWidthLoop.astype(np.float)
y0 = genfromtxt(p, unpack=True, usecols=[0], skiprows=19, skip_footer=1)
length = len(x0)
power_array = [powerLoop] * length
bifurWidth_array = [bifurWidthLoop] * length
label = str(bifurWidth)
a = myScatter(x0,power_array,y0,bifurWidth_array,lw,s,min,max,cmhot,label,ax)
#cax = ax.imshow(y0, interpolation='nearest', vmin=min, vmax=max)
#fig.colorbar(cax)
fig.savefig('test.png',dpi=300)
Example of an attempt and its error:
If I use fig.colorbar(a) inside or outside of the plotting for loop, I return NoneType oject has no attribute autoscale_None.
Your code doesn't run (x0,dataSorted,y0,etc missing) so can't get it to work (also note x0,power_array,y0 are wrong order in fn call). You need to return the handle to the scatter plot in order to plot a colorbar. If you change your myScatter function to return the handle,
def myScatter(x0,y0,power_array,c,lw,s,vmin,vmax,cmap,label,ax):
return ax.scatter(x0,y0,power_array,c=c,lw=lw,s=s,vmin=min,vmax=max,cmap=cmhot,label=label)
and then call plt.colorbar(a). A minimal(ish) example would be,
from glob import glob
from pylab import *
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
def myScatter(x0,y0,power_array,c,lw,s,vmin,vmax,cmap,label,ax):
return ax.scatter(x0,y0,power_array,c=c,lw=lw,s=s,vmin=min,vmax=max,cmap=cmhot,label=label)
fig = figure()
ax = fig.add_subplot(111, projection='3d')
cmhot = get_cmap("jet")
fig.tight_layout()
fig.set_size_inches(25,15)
min = 3 #colorbar range
max = 10
lw = 0 #linewidth
s = 10 #scatter size
label = 'test'
power_array = np.random.random((100,10))
bifurWidth_array = np.random.random((100,10))*(max-min)+min
x0 = np.random.random((100,10))
y0 = np.random.random((100,10))
a = myScatter(x0,power_array,y0,bifurWidth_array,lw,s,min,max,cmhot,label,ax)
plt.colorbar(a)
plt.show()
Related
I've been attempting to limit the range on the colorbar function in matplotlib. For whatever reason, I cannot use the clim function. Ideally I would like 80 and 20 to be the max values of the colorbar, and all values above or below those values to be a single dark blue/red, and the entire colorbar to be fit within the range of 20 and 80.
import requests
from bs4 import BeautifulSoup
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import matplotlib as mpl
import numpy as np
Gpercent=40
xGpercent = 60
SCFpercent = 55
CFpercent = 45
Analytics = ['GF%','xGF%','SCF%','CF%']
AnalyticsValues = [Gpercent,xGpercent,SCFpercent,CFpercent]
AnalyticsValues = [float(val) for val in AnalyticsValues]
data_height_normalized = [x / 100 for x in AnalyticsValues]
fig, ax = plt.subplots(figsize=(15, 4))
#my_cmap = plt.cm.get_cmap('RdBu')
my_cmap = plt.cm.get_cmap('coolwarm_r')
colors = my_cmap(data_height_normalized)
rects = ax.bar(Analytics, AnalyticsValues, color=colors)
sm = ScalarMappable(cmap=my_cmap, norm=plt.Normalize(0,100))
plt.ylim(0, 100)
cbar = plt.colorbar(sm)
plt.yticks(np.arange(0, 100.8, 10))
plt.title('bob' + (" On Ice 5v5 Impact"))
plt.xlabel('Analytical Metric')
plt.ylabel('%')
fig.patch.set_facecolor('xkcd:white')
plt.show()
The plot comes out as follows. I'd like the colorbar to be more defined in a shorter range, while still showing the % from 0-100
The intent of your question is to add an upper and lower limit to the color bar only. I would like to set the lower limit to 20 and the upper limit to 80. I will answer with the understanding that
The gist of the code is to create a new colormap from the defined colormap using LinearSegmentedColormap with the upper and lower color range.
My answer was modified from this excellent answer to fit your assignment.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
from matplotlib.colors import LinearSegmentedColormap # add
import matplotlib as mpl
import numpy as np
Gpercent=40
xGpercent = 60
SCFpercent = 55
CFpercent = 45
Analytics = ['GF%','xGF%','SCF%','CF%']
AnalyticsValues = [Gpercent,xGpercent,SCFpercent,CFpercent]
AnalyticsValues = [float(val) for val in AnalyticsValues]
data_height_normalized = [x / 100 for x in AnalyticsValues]
fig, ax = plt.subplots(figsize=(15, 4))
#my_cmap = plt.cm.get_cmap('RdBu')
my_cmap = plt.cm.get_cmap('coolwarm_r')
colors = my_cmap(data_height_normalized)
rects = ax.bar(Analytics, AnalyticsValues, color=colors)
# update
vmin,vmax = 20,80
colors2 = my_cmap(np.linspace(1.-(vmax-vmin)/float(vmax), 1, my_cmap.N))
color_map = LinearSegmentedColormap.from_list('cut_coolwarm', colors2)
sm = ScalarMappable(cmap=color_map, norm=plt.Normalize(vmin, vmax))
plt.ylim(0, 100)
cbar = plt.colorbar(sm)
plt.yticks(np.arange(0, 100.8, 10))
plt.title('bob' + (" On Ice 5v5 Impact"))
plt.xlabel('Analytical Metric')
plt.ylabel('%')
fig.patch.set_facecolor('xkcd:white')
plt.show()
I am trying to create a 3D colored bar chart using ideas from: this stackoverflow post.
First I create a 3D bar chart with the following code:
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data )
plt.show()
The following is the output:
Now I try to color the bars using code verbatim from: this stackoverflow post. Here is the code:
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
dz = F
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
colors = cm.jet(norm(fracs))
# colors = np.random.rand(91,91,4)
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data,color=colors )
plt.show()
However I get: ValueError: Invalid RGBA argument:
Now I am unable to debug the Invalid RGBA argument because I don't understand what is causing the error. I even tried to use random colors instead with colors = np.random.rand(91,91,4) and still the error persists.
I have checked stackoverflow posts regarding Invalid RGBA argument (for example this,this,this and this) and none of that seems to answer my problem.
I want to know what could be causing this error. I am using the standard Anaconda distribution for python on Ubuntu Mate 16.
Could it be that due to recent updates in python, the solution as in the original stackoverflow post becomes obsolete?
The error message is misleading. You're getting a ValueError because the shape of colors is wrong, not because an RGBA value is invalid.
When coloring each bar a single color, color should be an array of length N, where N is the number of bars. Since there are 8281 bars,
In [121]: x_data.shape
Out[121]: (8281,)
colors should have shape (8281, 4). But instead, the posted code generates an array of shape (91, 91, 4):
In [123]: colors.shape
Out[123]: (91, 91, 4)
So to fix the problem, use color=colors.reshape(-1,4).
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
dz = F
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
colors = cm.jet(norm(fracs))
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data,color=colors.reshape(-1,4) )
plt.show()
The color argument expects a 1D array, similar to all other arguments of bar3d.
Hence, you need to replace the line offset = dz + np.abs(dz.min())
by
offset = z_data + np.abs(z_data.min())
for your case. dz is not useful here (maybe it was in the linked example).
Note that color=np.random.rand(len(z_data),4) would equally work.
Then the result will be
The problem
I need the animation to go fluid, but it is ploting frame by frame. The code is running in Jupyter Notebook.
Here are the libraries
import numpy as np
from matplotlib import pyplot as plt
from scipy import signal as sp
Creating the functions to convolve
t_ini=0
t_final = 11
dt=0.1
t = np.arange(t_ini,t_final,dt)
expo = np.exp(-t)*np.piecewise(t,t>=0,[1,0])
t1 = np.arange(0,10,0.1)
s = np.sin(t1)
conv_=sp.convolve(s,expo,'full')
n_conv=np.arange(min(t1)+min(t),max(t1)+max(t)+0.1,0.1)
y = [0] * len(conv_)
t2 = [0] * len(n_conv)
Here is the plotting
i = 0
for x in n_conv:
y[i] = conv_[i]
plt.cla()
t2[i] = n_conv[i]
plt.plot(t2,y)
plt.show()
plt.pause(0.5)
i = i+1
matplotlib provides for instance ArtistAnimation that allows a seamless animation of precalculated graphs. I just added a couple of lines to your code. Only thing I changed was to use enumerate to improve your code
import numpy as np
from matplotlib import pyplot as plt
from scipy import signal as sp
import matplotlib.animation as anim
t_ini=0
t_final = 11
dt=0.1
t = np.arange(t_ini,t_final,dt)
expo = np.exp(-t)*np.piecewise(t,t>=0,[1,0])
t1 = np.arange(0,10,0.1)
s = np.sin(t1)
conv_=sp.convolve(s,expo,'full')
n_conv=np.arange(min(t1)+min(t),max(t1)+max(t)+0.1,0.1)
y = [0] * len(conv_)
t2 = [0] * len(n_conv)
#prepare figure for display
fig = plt.figure()
ax = plt.axes()
#create list to collect graphs for animation
img = []
for i, x in enumerate(n_conv):
y[i] = conv_[i]
t2[i] = n_conv[i]
#append new graphs to list
newpic, = ax.plot(t2, y, c= "blue")
img.append([newpic])
#animate the list of precalculated graphs
ani = anim.ArtistAnimation(fig, img, interval = 50)
plt.show()
Output:
I would like to add a fourth dimension to the scatter plot by defining the ellipticity of the markers depending on a variable. Is that possible somehow ?
EDIT:
I would like to avoid a 3D-plot. In my opinion these plots are usually not very informative.
You can place Ellipse patches directly onto your axes, as demonstrated in this matplotlib example. To adapt it to use eccentricity as your "third dimension") keeping the marker area constant:
from pylab import figure, show, rand
from matplotlib.patches import Ellipse
import numpy as np
import matplotlib.pyplot as plt
N = 25
# ellipse centers
xy = np.random.rand(N, 2)*10
# ellipse eccentrities
eccs = np.random.rand(N) * 0.8 + 0.1
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
A = 0.1
for pos, e in zip(xy, eccs):
# semi-minor, semi-major axes, b and a:
b = np.sqrt(A/np.pi * np.sqrt(1-e**2))
a = A / np.pi / b
ellipse = Ellipse(xy=pos, width=2*a, height=2*b)
ax.add_artist(ellipse)
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
show()
Of course, you need to scale your marker area to your x-, y- values in this case.
You can use colorbar as the 4th dimension to your 3D plot. One example is as shown below:
import matplotlib.cm as cmx
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
def scatter3d(x,y,z, cs, colorsMap='jet'):
cm = plt.get_cmap(colorsMap)
cNorm = matplotlib.colors.Normalize(vmin=min(cs), vmax=max(cs))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x, y, z, c=scalarMap.to_rgba(cs))
scalarMap.set_array(cs)
fig.colorbar(scalarMap,label='Test')
plt.show()
x = np.random.uniform(0,1,50)
y = np.random.uniform(0,1,50)
z = np.random.uniform(0,1,50)
so scatter3D(x,y,z,x+y) produces:
with x+y being the 4th dimension shown in color. You can add your calculated ellipticity depending on your specific variable instead of x+y to get what you want.
To change the ellipticity of the markers you will have to create them manually as such a feature is not implemented yet. However, I believe you can show 4 dimensions with a 2D scatter plot by using color and size as additional dimensions. You will have to take care of the scaling from data to marker size yourself. I added a simple function to handle that in the example below:
import matplotlib.pyplot as plt
import numpy as np
data = np.random.rand(60,4)
def scale_size(data, data_min=None, data_max=None, size_min=10, size_max=60):
# if the data limits are set to None we will just infer them from the data
if data_min is None:
data_min = data.min()
if data_max is None:
data_max = data.max()
size_range = size_max - size_min
data_range = data_max - data_min
return ((data - data_min) * size_range / data_range) + size_min
plt.scatter(data[:,0], data[:,1], c=data[:,2], s=scale_size(data[:,3]))
plt.colorbar()
plt.show()
Result:
I have been using a piece of code (based on the solution to another's problem given here) to create plots of spectroscopic data with two x-axis. The first (bottom) is in frequency units, the second (top) is just transformed to wavelength units (wavelength = 3E8/frequency). This was working well until I upgraded MPL to 1.4.2 after which the values on the upper axis are just the same as those on the lower axis (see example).
A MWE (an exact copy from the MPL mailing list) is:
from matplotlib.transforms import Transform, BlendedGenericTransform, IdentityTransform
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid.parasite_axes import SubplotHost
import numpy as np
c = 3.e2
class Freq2WavelengthTransform(Transform):
input_dims = 1
output_dims = 1
is_separable = False
has_inverse = True
def transform(self, tr):
return c/tr
def inverted(self):
return Wavelength2FreqTransform()
class Wavelength2FreqTransform(Freq2WavelengthTransform):
def inverted(self):
return Freq2WavelengthTransform()
aux_trans = BlendedGenericTransform(Freq2WavelengthTransform(),
IdentityTransform())
fig = plt.figure(2)
ax_GHz = SubplotHost(fig, 1,1,1)
fig.add_subplot(ax_GHz)
ax_GHz.set_xlabel("Frequency (GHz)")
xvals = np.arange(199.9, 999.9, 0.1)
#make some test data
data = np.sin(0.03*xvals)
ax_mm = ax_GHz.twin(aux_trans)
ax_mm.set_xlabel('Wavelength (mm)')
ax_mm.set_viewlim_mode("transform")
ax_mm.axis["right"].toggle(ticklabels=False)
ax_GHz.plot(xvals, data)
ax_GHz.set_xlim(200, 1000)
plt.draw()
plt.show()
This produces
Can any one advise me how to address this in MPL 1.4.2?
Using a combination of Adobe's answer from the thread linked to in wwii's comment, and your own code.
import numpy as np
import matplotlib.pyplot as plt
c=3.e2
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
xvals = np.arange(199.9, 999.9, 0.1)
data = np.sin(0.03*xvals)
ax1.plot(xvals, data)
ax1Ticks = ax1.get_xticks()
ax2Ticks = ax1Ticks
def tick_function(X):
V = c/X
return ["%.3f" % z for z in V]
ax2.set_xticks(ax2Ticks)
ax2.set_xbound(ax1.get_xbound())
ax2.set_xticklabels(tick_function(ax2Ticks))
ax1.set_xlabel("Frequency (GHz)")
ax2.set_xlabel('Wavelength (mm)')
ax1.grid(True)
plt.ylim(ymin=-1.1,ymax=1.1)
plt.show()
This produces;
I hope this helps!