Python interpolation and extracting value of z for x and y? - python-3.x

Would you please help
I have this data where z is a function for specific x and y
xs = [0.15, 0.35, 0.5, 0.67, 0.8]
ys = [0.01,0.01, 0.01, 0.01, 0.01]
z = [0.75, 0.83, 1.00, 0.92, 0.91]
I arranged the values in this shape
How can I do interpolation for the points so I can call z value later different than the the one I have?

A simple search would have helped already.
Your problem is basically the example of the
scipy.interpolate.interp2d documentation.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy import interpolate
import numpy as np
xs = [ 0.15, 0.35, 0.5, 0.67, 0.8 ]
ys = [ 0.01, 0.05, 0.1, 0.2, 0.3 ]
zz = np.array( [
0.75, 0.83, 1.00, 0.92, 0.91,
0.75, 0.82, 0.87, 0.88, 0.88,
0.74, 0.81, 0.84, 0.83, 0.83,
0.72, 0.76, 0.77, 0.76, 0.76,
0.72, 0.72, 0.72, 0.72, 0.72
] ).reshape( ( 5, 5 ) )
xx, yy = np.meshgrid( xs, ys )
f = interpolate.interp2d( xx, yy, zz, kind='cubic' )
fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1, projection='3d' )
ax.plot_surface( xx, yy, zz)
x2 = np.linspace( .15,.8,50 )
y2 = np.linspace( .01,.3,50 )
xx2, yy2 = np.meshgrid( x2, y2 )
zz2 = f( x2, y2 )
fig2 = plt.figure()
bx = fig2.add_subplot( 1, 1, 1, projection='3d' )
bx.plot_surface( xx2, yy2, zz2 )
plt.show()
providing the original data
and the cubic interpolation on a 50 by 50 grid

tck = interpolate.bisplrep(x, y, z, s=0)
def givemeZ(x,y):
return interpolate.bisplev(x,y,tck)
Now by running the code, it will give z for specific x and y.
This can be used without plot. just put it under the values and make sure that the values are arranged in the same way

Related

Matplotlib plot is not displaying all xticks and yticks

I am creating subplots in matplotlib but not all xticks and yticks are being displayed. I have tried everything from setting xlim and ylim, chainging figure size etc. The thing is this is a handson on hackerrnak and they are evaluating my output against their expected output. The 0.0 in xaxis and 1.0 on yaxis are simply not matching up. What am I doing wrong here.
Here is the code,
import matplotlib.pyplot as plt
import numpy as np
def test_generate_figure2():
np.random.seed(1000)
x = np.random.rand(10)
y = np.random.rand(10)
z = np.sqrt(x**2 + y**2)
fig = plt.figure(figsize=(8,6))
axes1 = plt.subplot(2, 2, 1, title="Scatter plot with Upper Triangle Markers")
axes1.set_xticks([0.0, 0.4, 0.8, 1.2])
axes1.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes1.set_ylim(-0.2,1.0) #Doing this still doesnot get the expected output
axes1.set_xlim(0.0,1.2)
print(axes1.get_yticks())
axes1.scatter(x, y, marker="^", s=80, c=z)
axes2 = plt.subplot(2, 2, 2, title="Scatter plot with Plus Markers")
axes2.set_xticks([0.0, 0.4, 0.8, 1.2])
axes2.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes2.scatter(x, y, marker="+", s=80, c=z)
axes3 = plt.subplot(2, 2, 3, title="Scatter plot with Circle Markers")
axes3.set_xticks([0.0, 0.4, 0.8, 1.2])
axes3.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes3.scatter(x, y, marker="o", s=80, c=z)
axes4 = plt.subplot(2, 2, 4, title="Scatter plot with Diamond Markers")
axes4.set_xticks([0.0, 0.4, 0.8, 1.2])
axes4.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes4.scatter(x, y, marker="d", s=80,c=z)
plt.tight_layout()
plt.show()
test_generate_figure2()
My Output,
Expected Output,
Your set_xlim & set_ylim approach works. You just need to set it for every subplot:
https://akuiper.com/console/5vaLIq0ZC_KO
import matplotlib.pyplot as plt
import numpy as np
def test_generate_figure2():
np.random.seed(1000)
x = np.random.rand(10)
y = np.random.rand(10)
z = np.sqrt(x**2 + y**2)
fig = plt.figure(figsize=(8,6))
axes1 = plt.subplot(2, 2, 1, title="Scatter plot with Upper Triangle Markers")
axes1.set_xticks([0.0, 0.4, 0.8, 1.2])
axes1.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes1.set_ylim(-0.2,1.0) #Doing this still doesnot get the expected output
axes1.set_xlim(0.0,1.2)
print(axes1.get_yticks())
axes1.scatter(x, y, marker="^", s=80, c=z)
axes2 = plt.subplot(2, 2, 2, title="Scatter plot with Plus Markers")
axes2.set_xticks([0.0, 0.4, 0.8, 1.2])
axes2.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes2.set_ylim(-0.2,1.0) #Doing this still doesnot get the expected output
axes2.set_xlim(0.0,1.2)
axes2.scatter(x, y, marker="+", s=80, c=z)
axes3 = plt.subplot(2, 2, 3, title="Scatter plot with Circle Markers")
axes3.set_xticks([0.0, 0.4, 0.8, 1.2])
axes3.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes3.set_ylim(-0.2,1.0) #Doing this still doesnot get the expected output
axes3.set_xlim(0.0,1.2)
axes3.scatter(x, y, marker="o", s=80, c=z)
axes4 = plt.subplot(2, 2, 4, title="Scatter plot with Diamond Markers")
axes4.set_xticks([0.0, 0.4, 0.8, 1.2])
axes4.set_yticks([-0.2, 0.2, 0.6, 1.0])
axes4.set_ylim(-0.2,1.0) #Doing this still doesnot get the expected output
axes4.set_xlim(0.0,1.2)
axes4.scatter(x, y, marker="d", s=80,c=z)
plt.tight_layout()
plt.show()
test_generate_figure2()

How to annotate completely outside of plot with AdjustText

I've been trying to make my annotations avoid the plot area and get drawn completely outside, using AdjustText so they don't overlap. I haven't found any solution or hidden parameter in the matplotlib or AdjustText docs so far to do this. What am I missing? All I found were ways to clip the annotations
import matplotlib.pyplot as plt
from matplotlib.widgets import CheckButtons
from adjustText import adjust_text
x_axis1 = [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 5.5, 6.0, 10.5, 15.0, 15.5]
y_axis1 = [60.0, 80.0, 70.0, 60.0, 70.0, 50.0, 80.0, 100.0, 80.0, 60.0, 50.0]
x_axis2 = [0.0, 0.3, 0.6, 0.9]
y_axis2_labels = ['First Station', 'Second Station', 'Third Station', 'Last station']
max_y = max(y_axis1)
fig, ax = plt.subplots()
ax.set_xlabel("Distance [km]")
ax.set_ylabel("Speed [km/h]")
l0, = ax.step(x_axis1, y_axis1, label="Speed", where="post")
ax2 = ax.twiny()
ax2.set_xlim(ax.get_xlim())
ax2.set_label('Stations')
plt.xticks([])
ax2.tick_params(
axis="x",
which='major',
direction="in",
width=1.5,
length=7,
labelsize=10,
color="red",
)
for x in x_axis2:
ax2.axvline(x, color='red', ls=':', lw=1.5)
# -------------------
y_axis2 = [max(ax.get_ylim()) for i in range(len(x_axis2))]
texts = [
plt.text(
x_axis2[i],
y_axis2[i],
y_axis2_labels[i],
ha='center',
va='center',
# annotation_clip=False,
rotation=20,
clip_on=False
) for i in range(len(x_axis2))
]
adjust_text(
texts,
[ax.get_xlim()],
[ax.get_ylim()],
arrowprops=dict(
arrowstyle='->',
connectionstyle="arc,angleA=-90,angleB=0,armA=30,armB=30,rad=5",
color='red'
),
)
# -------------------
lines = [l0, ax2]
rax = plt.axes([0, 0, 0.12, 0.1])
labels = [str(line.get_label()) for line in lines]
visibility = [line.get_visible() for line in lines]
check = CheckButtons(rax, labels, visibility)
def func(label):
index = labels.index(label)
lines[index].set_visible(not lines[index].get_visible())
plt.draw()
check.on_clicked(func)
fig.tight_layout()
plt.show()
This is the current output I get:
Unfortunately AdjustText moves everything inside the limits by default. I ended up adjusting the code in Matplotlib overlapping annotations / text and messing with some obscure matplotlib parameters:
import matplotlib.pyplot as plt
import numpy as np
def get_text_positions(x_data, y_data, txt_width, txt_height):
"""Get plot tick labels to check for collision."""
a = list(zip(y_data, x_data))
text_positions = y_data.copy()
for index, (y, x) in reversed(list(enumerate(a))):
local_text_positions = [
i for i in a
if i[0] > (y - txt_height) and (abs(i[1] - x) < txt_width * 2) and i != (y, x)
]
if local_text_positions:
sorted_ltp = sorted(local_text_positions)
if abs(sorted_ltp[0][0] - y) < txt_height: #True == collision
differ = np.diff(sorted_ltp, axis=0)
a[index] = (sorted_ltp[-1][0] + txt_height, a[index][1])
text_positions[index] = sorted_ltp[-1][0] + txt_height * 2
for k, (j, m) in enumerate(differ):
#j is the vertical distance between words
if j > txt_height * 2: #if True then room to fit a word in
a[index] = (sorted_ltp[k][0] + txt_height, a[index][1])
text_positions[index] = sorted_ltp[k][0] + txt_height
break
return text_positions
def text_plotter(x_data, y_data, y_heigth, text_positions, axis, txt_width, txt_height):
"""Changes label text location and adds arrow if there's a collision."""
for x, y, h, t in list(zip(x_data, y_data, y_heigth, text_positions)):
axis.text(
x - txt_width,
1.02 * t,
str(y),
rotation=70,
color='blue',
clip_on=False,
)
if h != t:
axis.arrow(
x,
t,
0,
h - t,
color='black',
alpha=0.2,
width=txt_width * 0.1,
head_width=txt_width / 2,
head_length=txt_height * 0.3,
zorder=0,
length_includes_head=True,
clip_on=False,
)
x_axis1 = [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 5.5, 6.0, 10.5, 15.0, 15.5]
y_axis1 = [60.0, 80.0, 70.0, 60.0, 70.0, 50.0, 80.0, 100.0, 80.0, 60.0, 50.0]
x_axis2 = [0, 0.2, 0.3, 1.2, 1.5, 1.8, 2, 3, 4, 5, 5.5, 6, 7, 7.5, 8, 9, 10, 13, 15]
y_axis2 = [
'Station 1', 'Station 2', 'Station 3', 'Station 4', 'Station 5', 'Station 6', 'Station 7',
'Station 8', 'Station 9', 'Station 10', 'Station 11', 'Station 12', 'Station 13', 'Station 14',
'Station 15', 'Station 16', 'Station 17', 'Station 18', 'Station 19'
]
fig, ax = plt.subplots()
ax.set_xlabel("Distance [km]")
ax.set_ylabel("Speed [km/h]")
l0, = ax.step(x_axis1, y_axis1, label="Speed", where="post")
ax2 = ax.twiny()
ax2.set_xlim(ax.get_xlim())
ax2.set_label('Stations')
txt_height = 0.35 * (plt.ylim()[1] - plt.ylim()[0])
txt_width = 0.01 * (plt.xlim()[1] - plt.xlim()[0])
y_height = [max(ax.get_ylim())] * len(x_axis2) # labels on spines
x_data = [i / max(x_axis1) for i in x_axis1]
text_positions = get_text_positions(x_axis2, y_height, txt_width, txt_height)
text_plotter(x_axis2, y_axis2, y_height, text_positions, ax, txt_width, txt_height)
plt.ylim(0, max(y_height)) #+ 2 * txt_height
plt.xticks([])
plt.subplots_adjust(top=0.5) # manual adjustment
plt.show()
which outputs:

How to improve cone - make the bases more like a circle?

I have a code for plotting a cone with some text and arrows like axes. How to change this code to get the bases of the cone looking like circles? Is the problem in the settings of margins? Or is it necessary to define circles in another way?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
FancyArrowPatch.draw(self, renderer)
nn = 400 # number of points along circle's perimeter
theta = np.linspace(0, 2*np.pi, nn)
rho = np.ones(nn)
# (x,y) represents points on circle's perimeter
x = np.ravel(rho*np.cos(theta))
y = np.ravel(rho*np.sin(theta))
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [5, 5]
figsize = (5, 5)
ax = plt.axes(projection='3d') # set the axes for 3D plot
ax.azim = -88 # y rotation (default=270)
ax.elev = 13 # x rotation (default=0)
# Low, high values of z for plotting 2 circles at different elevation
loz, hiz = -15, 15
# Plot two circles
ax.plot(x, y, hiz)
ax.plot(x, y, loz)
# Set some indices to get proper (x,y) for line plotting
lo1,hi1 = 15, 15+nn//2
lo2,hi2 = lo1+nn//2-27, hi1-nn//2-27
# Plot 3d lines using coordinates of selected points
ax.plot([x[lo1], x[hi1]], [y[lo1], y[hi1]], [loz, hiz])
ax.plot([x[lo2], x[hi2]], [y[lo2], y[hi2]], [loz, hiz])
eps = 0.005
ax.plot([0, 0], [0, 0], [0, 20]) # extend in z direction
ax.plot([0-eps, 0], [0-eps, -5], [0-eps, 0]) # extend in y direction
ax.plot([0, 1.3], [0, 0], [0, 0]) # extend in x direction
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, 16]) # v vector
ax.plot([0.63, 0.63], [-4, -4], [16, -0.005]) # vertical projection
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, -0.005]) # to the beginning
ax.scatter(0, 0, 20, marker=(3, 0, 0), s=100, clip_on=False)
ax.scatter(0, -5, 0, marker=(3, 0, 43), s=100, clip_on=False)
ax.scatter(1.3, 0, 0, marker=(3, 0, 30), s=100, clip_on=False)
ax.scatter(0.6, -4, 16, marker=(3, 0, 80), s=100, clip_on=False)
ax.scatter(0.6, -4, -0.005, marker=(3, 0, 0), s=100, clip_on=False)
a2 = Arrow3D([0.14, -0.515], [-5.581, 1.358], [14.73, 4.983], mutation_scale=20, arrowstyle="-|>", color="k", connectionstyle="arc3,rad=0.3")
ax.add_artist(a2)
ax.text3D(0.23, -5.23, 23.33, r'$A$')
ax.text3D(1.41, 1.29, -2.7, r'$B$')
ax.text3D(-0.31, 1.46, -12.6, r'$C$')
ax.text3D(0.4, -5.48, 17, r'$D$')
ax.text3D(0.64, 1.57, -9.95, r'$E$')
ax.text3D(-0.2, -5.5, 15.73, r'$F$')
# Hide axes
ax._axis3don = False
# Save the figure (.pdf)
margins = { # vvv margin in inches
"left" : 1 / figsize[0],
"bottom" : -2.45 / figsize[1],
"right" : 1 - 0.5 / figsize[0],
"top" : 1 + 1.8 / figsize[1]
}
fig.subplots_adjust(**margins)
plt.savefig('output.pdf')
plt.show()
From this code I got the following output:
The desired output is bases looking like a circle.
This is the view from above:
It is not a circle but an ellipse.
Desired shape of the cone:

matplotlib shift pcolormesh plot to symmetrized coordinates

I have some 2D data with x and y coordinates both within [0,1], plotted using pcolormesh.
Now I want to symmetrize the plot to [-0.5, 0.5] for both x and y coordinates. In Matlab I was able to achieve this by changing x and y from e.g. [0, 0.2, 0.4, 0.6, 0.8] to [0, 0.2, 0.4, -0.4, -0.2], without rearranging the data. However, with pcolormesh I cannot get the desired result.
A minimum example is shown below, with data represented simply by x+y:
import matplotlib.pyplot as plt
import numpy as np
x,y = np.mgrid[0:1:5j,0:1:5j]
fig,(ax1,ax2,ax3) = plt.subplots(1,3,figsize=(9,3.3),constrained_layout=1)
# original plot spanning [0,1]
img1 = ax1.pcolormesh(x,y,x+y,shading='auto')
# shift x and y from [0,1] to [-0.5,0.5]
x = x*(x<0.5)+(x-1)*(x>0.5)
y = y*(y<0.5)+(y-1)*(y>0.5)
img2 = ax2.pcolormesh(x,y,x+y,shading='auto') # similar code works in Matlab
# for this specific case, the following is close to the desired result, I can just rename x and y tick labels
# to [-0.5,0.5], but in general data is not simply x+y
img3 = ax3.pcolormesh(x+y,shading='auto')
fig.colorbar(img1,ax=[ax1,ax2,ax3],orientation='horizontal')
The corresponding figure is below, any suggestion on what is missed would be appreciated!
Let's look at what you want to achieve in a 1D example.
You have x values between 0 and 1 and a dummy function f(x) = 20*x to produce some values.
# x = [0, .2, .4, .6, .8] -> [0, .2, .4, -.4, -.2] -> [-.4, .2, .0, .2, .4])
# fx = [0, 4, 8, 12, 16] -> [0, 4, 8, 12, 16] -> [ 12, 16, 0, 4, 8]
# ^ only flip and shift x not fx ^
You could use np.roll() to achieve the last operation.
I used n=14 to make the result better visible and show that this approach works for arbitrary n.
import numpy as np
import matplotlib.pyplot as plt
n = 14
x, y = np.meshgrid(np.linspace(0, 1, n, endpoint=False),
np.linspace(0, 1, n, endpoint=False))
z = x + y
x_sym = x*(x <= .5)+(x-1)*(x > .5)
# array([[ 0. , 0.2, 0.4, -0.4, -0.2], ...
x_sym = np.roll(x_sym, n//2, axis=(0, 1))
# array([[-0.4, -0.2, 0. , 0.2, 0.4], ...
y_sym = y*(y <= .5)+(y-1)*(y > .5)
y_sym = np.roll(y_sym, n//2, axis=(0, 1))
z_sym = np.roll(z, n//2, axis=(0, 1))
# array([[1.2, 1.4, 0.6, 0.8, 1. ],
# [1.4, 1.6, 0.8, 1. , 1.2],
# [0.6, 0.8, 0. , 0.2, 0.4],
# [0.8, 1. , 0.2, 0.4, 0.6],
# [1. , 1.2, 0.4, 0.6, 0.8]])
fig, (ax1, ax2) = plt.subplots(1, 2)
img1 = ax1.imshow(z, origin='lower', extent=(.0, 1., .0, 1.))
img2 = ax2.imshow(z_sym, origin='lower', extent=(-.5, .5, -.5, .5))

How to plot the xlabel in two lines of different colors?

I have two different parameters (lines y1 and y2), of different units which I want to plot in the same figure because their individual values are of similar magnitude. I therefore want to put their respective units (Unit y1 and Unit y2) in the xlabel in one row each and color each row after the color of the line. How can I do this?
import numpy as np
import matplotlib as plt
x1 = np.arange(0, 10, 1)
y1 = np.arange(10, 0, -1)
x2 = np.arange(11, 21, 1)
y2 = np.arange(0, 10, 1)
plt.figure()
plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'red')
plt.xlabel('Unit y1\n''Unit y2')
plt.show()
One way is to use plt.text to put the labels. While it is unclear how you want the labels to be positioned, I will answer both possible ways
Way 1
import matplotlib.pyplot as plt
# Rest of the code
fig, ax = plt.subplots()
plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'red')
plt.text(0.2, -0.15, 'Unit y1', color='blue', transform=ax.transAxes)
plt.text(0.7, -0.15, 'Unit y2', color='red', transform=ax.transAxes)
plt.show()
Way 2
fig, ax = plt.subplots()
plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'red')
plt.text(0.45, -0.15, 'Unit y1', color='blue', transform=ax.transAxes)
plt.text(0.45, -0.2, 'Unit y2', color='red', transform=ax.transAxes)
plt.show()

Resources