UnivariateSpline equivalent for exponential functions? - python

I currently have a set of data that I wish to make a function out of that is defined for all values of possible values of temp_vp (my x data in this case)
temp_vp = [280.0,290.0,300.0,310.0,320.0,330.0,340.0,350.0,360.0,370.0,380.0,390.0,400.0,410.0,420.0,430.0,440.0,450.0,460.0,470.0,480.0,490.0,500.0]
vp_in = [3.88e-52,5.16e-50,4.95e-48,3.53e-46,1.93e-44,8.26e-43,2.83e-41,7.93e-40,1.85e-38,3.62e-37,6.07e-36,8.79e-35,1.11e-33,1.25e-32,1.24e-31,1.11e-30,9.03e-30,6.66e-29,4.51e-28,2.81e-27,1.62e-26,8.72e-26,4.38e-25]
I have used UnivariateSpline before in order to interpolate between data points and give a constant values outside of the given data range. However, when I try and use it this time for this data, I get this:
Does anyone know how I can get a desired interpolated function for this data, to the requirements described previously?
My current code is given below:
import numpy as np
from scipy.interpolate import interp1d
from scipy import interp
from scipy.interpolate import UnivariateSpline
import matplotlib.pyplot as plt
temp_vp = [280.0,290.0,300.0,310.0,320.0,330.0,340.0,350.0,360.0,370.0,380.0,390.0,400.0,410.0,420.0,430.0,440.0,450.0,460.0,470.0,480.0,490.0,500.0]
vp_in = [3.88e-52,5.16e-50,4.95e-48,3.53e-46,1.93e-44,8.26e-43,2.83e-41,7.93e-40,1.85e-38,3.62e-37,6.07e-36,8.79e-35,1.11e-33,1.25e-32,1.24e-31,1.11e-30,9.03e-30,6.66e-29,4.51e-28,2.81e-27,1.62e-26,8.72e-26,4.38e-25]
tempspace = np.linspace(200,10000,10000)
vp_f = UnivariateSpline(temp_vp, vp_in, k = 1, ext = 3)
fig=plt.figure(figsize=(4.5,3.6))
ax=fig.add_subplot(1,1,1)
ax.minorticks_on() # enable minor ticks
ax.set_axisbelow(True) # put grid behind curves
ax.grid(b=True, which='major', color='black', linestyle='-', zorder=1, linewidth=0.4, alpha = 0.12) # turn on major grid
ax.grid(b=True, which='minor', color='black', linestyle='-', zorder=1, linewidth=0.4, alpha = 0.12) # turn on minor grid
ax.scatter(temp_vp,vp_in, color = 'black', label = 'data', s= 5, zorder = 3)
ax.plot(tempspace, vp_f(tempspace), color = 'blue', label = 'Fit', zorder = 2)
ax.set_yscale('log')
ax.set_xscale('log')
ax.set_xlabel('Temperature [K]')
ax.set_ylabel('Vapor Pressure [Pa]')
ax.legend(labelspacing=0.25, fontsize = 8)
plt.xlim([250,600])
#plt.ylim([1e-10,1e5])
plt.savefig('Al_vpdata.pdf', bbox_inches='tight', format='pdf')
plt.savefig('Al_vpdata.png', dpi=300, bbox_inches='tight', format='png')

Are you really measuring pressures of 1e-50 Pa?
You are probably having underflow errors.
If you multiply vp_in by 1e52 you get:

Related

Contourf not showing full range of values

I have two datasets that when compared result in a basically random distribution of values between -1 and 1. When I plot this using contourf, however, the figure shows almost all values > 0.5. When I plot every 10th point (thin the data), I get a graph that is more reasonable. But it is not clear why the contourf function is doing this.
I replicated this using a random number list of the same size as my data. The result is the same.
import numpy as np
import matplotlib.pyplot as plt
from netCDF4 import Dataset
from matplotlib.cm import get_cmap
import numpy as np
random = np.random.random((360,1600))*2.-1.
f, ax = plt.subplots(1,2,figsize=(15,5))
#heights = ax.contour(to_np(hgt),3,colors='k')
#ax.clabel(heights, fmt='%2.0f', colors='k', fontsize=8)
#cbar = f.colorbar(heights)
#heights.levels=[0,100,3000]
#plt.clabel(heights, heights.levels)
clevs = [-0.5,-0.1,0.1,0.5]
diffplot = ax[0].contourf(random[::10,::10],clevs,extend='both')
cbar = f.colorbar(diffplot,ax=ax[0])
clevs = [-0.5,-0.1,0.1,0.5]
diffplot2 = ax[1].contourf(random[:,:],clevs,extend='both')
cbar = f.colorbar(diffplot2,ax=ax[1])
Result of code
The full range is shown, but the issue is in the large number of points vs. resolution.
That means points drawn 'later' (aka the ones at the end of the color levels range = higher values) have a certain minimum size (you still need to see them) that then overlaps previous (aka smaller values) points, resulting in the image colors looking skewed.
Solution is to increase the dpi of the figure (matplotlib default is 100dpi):
Note how the reduced points from your example plotted on the left hand side looks the same but the right hand side only looks similar >=300 dpi.
Code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import get_cmap
import numpy as np
random = np.random.random((360,1600))*2.-1.
clevs = [-0.5,-0.1,0.1,0.5]
f1, ax = plt.subplots(1,2,figsize=(15,5)) # ,dpi=100
diffplot = ax[0].contourf(random[::10,::10],clevs,extend='both')
cbar = f1.colorbar(diffplot,ax=ax[0])
diffplot2 = ax[1].contourf(random[:,:],clevs,extend='both')
cbar = f1.colorbar(diffplot2,ax=ax[1])
f1.set_dpi(100)
f1.suptitle('dpi = 100', fontweight = 'bold', fontsize = 20)
f2, ax = plt.subplots(1,2,figsize=(15,5)) # ,dpi=150
diffplot = ax[0].contourf(random[::10,::10],clevs,extend='both')
cbar = f2.colorbar(diffplot,ax=ax[0])
diffplot2 = ax[1].contourf(random[:,:],clevs,extend='both')
cbar = f2.colorbar(diffplot2,ax=ax[1])
f2.set_dpi(150)
f2.suptitle('dpi = 150', fontweight = 'bold', fontsize = 20)
f3, ax = plt.subplots(1,2,figsize=(15,5)) # ,dpi=300
diffplot = ax[0].contourf(random[::10,::10],clevs,extend='both')
cbar = f3.colorbar(diffplot,ax=ax[0])
diffplot2 = ax[1].contourf(random[:,:],clevs,extend='both')
cbar = f3.colorbar(diffplot2,ax=ax[1])
f3.set_dpi(300)
f3.suptitle('dpi = 300', fontweight = 'bold', fontsize = 20)
f4, ax = plt.subplots(1,2,figsize=(15,5)) # ,dpi=600
diffplot = ax[0].contourf(random[::10,::10],clevs,extend='both')
cbar = f4.colorbar(diffplot,ax=ax[0])
diffplot2 = ax[1].contourf(random[:,:],clevs,extend='both')
cbar = f4.colorbar(diffplot2,ax=ax[1])
f4.set_dpi(600)
f4.suptitle('dpi = 600', fontweight = 'bold', fontsize = 20)
f5, ax = plt.subplots(1,2,figsize=(15,5)) # ,dpi=900
diffplot = ax[0].contourf(random[::10,::10],clevs,extend='both')
cbar = f5.colorbar(diffplot,ax=ax[0])
diffplot2 = ax[1].contourf(random[:,:],clevs,extend='both')
cbar = f5.colorbar(diffplot2,ax=ax[1])
f5.set_dpi(900)
f5.suptitle('dpi = 900', fontweight = 'bold', fontsize = 20)
plt.show()
Note the two options for setting the dpi:
fig, ax = plt.subplots(1,2,figsize=(15,5), dpi=600)
# or
fig.set_dpi(600)
An explanation from another angle - bare with me for some plots:
Note: The following plots are arranged with gridspec to be shown in a single figure.
That way the 'resolution' is depicted in a comparable way.
1) The effect you recognized depends on the plot size
See the code below, all 3 plots contain the same data ..., the only difference is their size.
Notice how with the increased size the colors distribution looks more and more as expected.
from matplotlib import gridspec
import numpy as np
import matplotlib.pyplot as plt
random = np.random.random((360,1600))*2.-1.
#random = np.random.random((100,100))*2.-1.
clevs = [-0.5,-0.1,0.1,0.5]
fig = plt.figure(figsize=(18,20), facecolor=(1, 1, 1))
gs = gridspec.GridSpec(3, 4, height_ratios=[1,1,4])
cmap='viridis' # Note: 'viridis' is the default cmap
ax1 = fig.add_subplot(gs[0,:1])
ax1.set_title('ax1')
diffplot1 = ax1.contourf(random[:,:],clevs,extend='both', cmap=cmap)
fig.colorbar(diffplot1, ax=ax1)
ax2 = fig.add_subplot(gs[0:2,2:])
ax2.set_title('ax2')
diffplot2 = ax2.contourf(random[:,:],clevs,extend='both', cmap=cmap)
fig.colorbar(diffplot2, ax=ax2)
ax3 = fig.add_subplot(gs[2,:])
ax3.set_title('ax3')
diffplot3 = ax3.contourf(random[:,:],clevs,extend='both', cmap=cmap)
fig.colorbar(diffplot3, ax=ax3)
fig.tight_layout()
# plt.savefig("Contourf_Colorbar.png")
plt.show()
2) The effect you recognized depends on number of points 'cramped' into a plot
Basically the same that you've already noticed in your question with plotting only every 10th value.
Notice how the colors distribution looks as expected kinda the same for the 3 plot sizes.
Activate random = np.random.random((100,100))*2.-1. in the code block above to get this plot.
3) Reversed color cmap as another way of showing the effect
Notice how this is like the plot from 1) but just with reversed colors.
from matplotlib import gridspec
import numpy as np
import matplotlib.pyplot as plt
random = np.random.random((360,1600))*2.-1.
clevs = [-0.5,-0.1,0.1,0.5]
fig = plt.figure(figsize=(18,20), facecolor=(1, 1, 1))
gs = gridspec.GridSpec(3, 4, height_ratios=[1,1,4])
# reverse cmap
cmap='viridis' # Note: 'viridis' is the default cmap
cmap=plt.cm.get_cmap(cmap)
cmap = cmap.reversed()
ax1 = fig.add_subplot(gs[0,:1])
ax1.set_title('ax1')
diffplot1 = ax1.contourf(random[:,:],clevs,extend='both', cmap=cmap)
fig.colorbar(diffplot1, ax=ax1)
ax2 = fig.add_subplot(gs[0:2,2:])
ax2.set_title('ax2')
diffplot2 = ax2.contourf(random[:,:],clevs,extend='both', cmap=cmap)
fig.colorbar(diffplot2, ax=ax2)
ax3 = fig.add_subplot(gs[2,:])
ax3.set_title('ax3')
diffplot3 = ax3.contourf(random[:,:],clevs,extend='both', cmap=cmap)
fig.colorbar(diffplot3, ax=ax3)
fig.tight_layout()
# plt.savefig("Contourf_Colorbar_reverse.png")
plt.show()
Finally for reference from matplotlib contourf docu:
algorithm{'mpl2005', 'mpl2014', 'serial', 'threaded'}, optional
Which contouring algorithm to use to calculate the contour lines and polygons. The algorithms are implemented in ContourPy, consult the
ContourPy documentation for further information.
The default is taken from rcParams["contour.algorithm"] (default: 'mpl2014').
I've tried some options (wasn't able to get algorithm going, but checked antialiased ... actually more in a try&error method) without an improvement.
But you could look into the referenced ContourPy to maybe find a way to reduce the 'size of the dots' that are drawn, but that's out of my league.

Matplotlib scatter plot of unfilled squares

I would like to make a scatter plot with unfilled squares. markerfacecolor is not an option recognized by scatter. I made a MarkerStyle but the fill style seems to be ignored by the scatter plot. Is there a way to make unfilled markers in the scatterplot?
import matplotlib.markers as markers
import matplotlib.pyplot as plt
import numpy as np
def main():
size = [595, 842] # in pixels
dpi = 72. # dots per inch
figsize = [i / dpi for i in size]
fig = plt.figure(figsize=figsize)
ax = fig.add_axes([0,0,1,1])
x_max = 52
y_max = 90
ax.set_xlim([0, x_max+1])
ax.set_ylim([0, y_max + 1])
x = np.arange(1, x_max+1)
y = [np.arange(1, y_max+1) for i in range(x_max)]
marker = markers.MarkerStyle(marker='s', fillstyle='none')
for temp in zip(*y):
plt.scatter(x, temp, color='green', marker=marker)
plt.show()
main()
It would appear that if you want to use plt.scatter() then you have to use facecolors = 'none' instead of setting fillstyle = 'none' in construction of the MarkerStyle, e.g.
marker = markers.MarkerStyle(marker='s')
for temp in zip(*y):
plt.scatter(x, temp, color='green', marker=marker, facecolors='none')
plt.show()
or, use plt.plot() with fillstyle = 'none' and linestyle = 'none' but since the marker keyword in plt.plot does not support MarkerStyle objects you have to specify the style inline, i.e.
for temp in zip(*y):
plt.plot(x, temp, color='green', marker='s', fillstyle='none')
plt.show()
either of which will give you something that looks like this
Refer to: How to do a scatter plot with empty circles in Python?
Try adding facecolors='none' to your plt.scatter
plt.scatter(x, temp, color='green', marker=marker, facecolors='none')

Seaborn scatterplot legend showing true values and normalized continuous color

I have a dataframe that I'd like to use to build a scatterplot where different points have different colors:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
dat=pd.DataFrame(np.random.rand(20, 2), columns=['x','y'])
dat['c']=np.random.randint(0,100,20)
dat['c_norm']=(dat['c']-dat['c'].min())/(dat['c'].max()-dat['c'].min())
dat['group']=np.append(np.repeat('high',10), np.repeat('low',10))
As you can see, the column c_norm shows the c column has been normalized between 0 and 1. I would like to show a continuous legend whose color range reflect the normalized values, but labeled using the original c values as label. Say, the minimum (1), the maximum (86), and the median (49). I also want to have differing markers depending on group.
So far I was able to do this:
fig = plt.figure(figsize = (8,8))
ax = fig.add_subplot(1,1,1)
for row in dat.index:
if(dat.loc[row,'group']=='low'):
i_marker='.'
else:
i_marker='x'
ax.scatter(
x=dat.loc[row,'x'],
y=dat.loc[row,'y'],
s=50, alpha=0.5,
marker=i_marker
)
ax.legend(dat['c_norm'], loc='center right', bbox_to_anchor=(1.5, 0.5), ncol=1)
Questions:
- How to generate a continuous legend based on the values?
- How to adapt its ticks to show the original ticks in c, or at least a min, max, and mean or median?
Thanks in advance
Partial answer. Do you actually need to determine your marker colors based on the normed values? See the output of the snippet below.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
dat = pd.DataFrame(np.random.rand(20, 2), columns=['x', 'y'])
dat['c'] = np.random.randint(0, 100, 20)
dat['c_norm'] = (dat['c'] - dat['c'].min()) / (dat['c'].max() - dat['c'].min())
dat['group'] = np.append(np.repeat('high', 10), np.repeat('low', 10))
fig, (ax, bx) = plt.subplots(nrows=1, ncols=2, num=0, figsize=(16, 8))
mask = dat['group'] == 'low'
scat = ax.scatter(dat['x'][mask], dat['y'][mask], s=50, c=dat['c'][mask],
marker='s', vmin=np.amin(dat['c']), vmax=np.amax(dat['c']),
cmap='plasma')
ax.scatter(dat['x'][~mask], dat['y'][~mask], s=50, c=dat['c'][~mask],
marker='X', vmin=np.amin(dat['c']), vmax=np.amax(dat['c']),
cmap='plasma')
cbar = fig.colorbar(scat, ax=ax)
scat = bx.scatter(dat['x'][mask], dat['y'][mask], s=50, c=dat['c_norm'][mask],
marker='s', vmin=np.amin(dat['c_norm']),
vmax=np.amax(dat['c_norm']), cmap='plasma')
bx.scatter(dat['x'][~mask], dat['y'][~mask], s=50, c=dat['c_norm'][~mask],
marker='X', vmin=np.amin(dat['c_norm']),
vmax=np.amax(dat['c_norm']), cmap='plasma')
cbar2 = fig.colorbar(scat, ax=bx)
plt.show()
You could definitely modify the second colorbar so that it matches the first one, but is that necessary?

Log-log density-colour plot in matplotlib

I am trying to create a density plot with a given data and using log scales in the two axes x,y, using the version of Matplotlib 2.0.0. I have made the following code, the problem is that for the log plot case don't give the correct functional behaviour.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
init = 0.0
points = 500
final_value = 100
steep = (final_value-init)/points
list_values_x = np.arange(init,final_value,steep)
list_values_y = np.arange(init,final_value,steep)
#WE CREATE OUT DATA FILE
f1 = open("data.txt", "w")
for i in list_values_x:
for j in list_values_y:
f1.write( str(i) +" "+str(j)+" "+str(0.0001*(i**2+j**2)) +"\n")
f1.close()
#NOW WE OPEN THE FILE WITH THE DATA AND MAKE THE PLOT
x,y,temp = np.loadtxt('data.txt').T #Transposed for easier unpacking
nrows, ncols = points, points
grid = temp.reshape((nrows, ncols))
# LINEAR PLOT
fig1 = plt.imshow(grid, extent=(x.min(), x.max(), y.max(), y.min()),
interpolation='nearest', cmap=cm.gist_rainbow)
plt.axis([x.min(), x.max(),y.min(), y.max()])
plt.colorbar()
plt.suptitle('Example', fontsize=15)
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.show()
# LOG-LOG PLOT
fig, (ax1) = plt.subplots(ncols=1, figsize=(8, 4))
ax1.imshow(grid, aspect="auto", extent=(1, 1e2, 1, 1e2), interpolation='nearest')
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_title('Example with log scale')
plt.show()
The data that I am using in order to make the plot is irrelevant, it's just an example. So that, the first plot is given with a linear scale. The second plot is given with a log-log scale, but is clear that it's incorrect, the behaviour beetwen the two plots is absolutely different and I am using the same data. Moreover, I don't know how put a colorbar in the log-log plot
Any idea why this happens? Thanks for your attention.
PD: In order to build the log-log plot, I have used part of the code that apears in "Non-linear scales on image plots" given in (http://matplotlib.org/devdocs/users/whats_new.html#non-linear-scales-on-image-plots)
Using the extent keyword and it with extent=(xmin, xmax, ymin, ymax) makes more sense when additionally using origin="lower" in imshow. You might also want to set the limits for the axes, since the automatic feature does not work too well for log scales.
Here is the complete example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.axes_grid1 import make_axes_locatable
points = 500
init = 0.0
final_value = 100
steep = (final_value-init)/points
x = np.arange(init,final_value,steep)
y = np.arange(init,final_value,steep)
X,Y = np.meshgrid(x,y)
Z = 0.0001*(X**2+Y**2)
fig, (ax, ax1) = plt.subplots(ncols=2, figsize=(8, 4))
# LINEAR PLOT
im = ax.imshow(Z, extent=(x.min(), x.max(), y.min(), y.max() ),
interpolation='nearest', cmap=cm.gist_rainbow, origin="lower")
ax.set_title('lin scale')
#make colorbar
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="5%", pad=0.05)
fig.add_axes(ax_cb)
fig.colorbar(im, cax = ax_cb, ax=ax)
# LOG-LOG PLOT
im1 = ax1.imshow(Z, extent=(1, 1e2, 1, 1e2),
interpolation='nearest',cmap=cm.gist_rainbow, origin="lower")
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_xlim([1, x.max()])
ax1.set_ylim([1, y.max()])
ax1.set_title('log scale')
#make colorbar
divider1 = make_axes_locatable(ax1)
ax_cb1 = divider1.new_horizontal(size="5%", pad=0.05)
fig.add_axes(ax_cb1)
fig.colorbar(im1, cax = ax_cb1, ax=ax1)
plt.tight_layout()
plt.show()

Matplotlib Half color axis

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()

Categories

Resources