ValueError: need more than 1 value to unpack with matplotlib - python

I have a 3D array and would like to make a 2D plot using plt.pcolormesh but I keep getting the error
ValueError: need more than 1 value to unpack.
My x and y arrays are both of length 59 and the z array is length 59*59=3481 since x and y is now a matrix.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = [(54.5, 17.041667, 31.993), (54.5, 17.083333, 31.911), (54.458333, 17.041667, 31.945), (54.458333, 17.083333, 31.866)]
points = sorted(points) # order points by x, then by y
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = points
interp2d=[]
nums = np.linspace(x1, x2, num = 60, endpoint=True)
numms = np.linspace(y1, y2, num = 60, endpoint=True)
X, Y = np.meshgrid(nums[:-1], numms[:-1], indexing='xy')
for i in range(len(nums)-1):
for j in range(len(numms)-1):
x=nums[i]
y=numms[j]
interp = (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)
) / ((x2 - x1) * (y2 - y1) + 0.0)
interp2d.append(interp)
interp2d = np.asarray(interp2d)
fig, (ax1, ax2) = plt.subplots(1,2, sharex=True)
ax2.pcolormesh(X,Y,interp2d, cmap=plt.cm.BuPu_r)
cbar = plt.colorbar()
plt.show()

The values of interp2d are expected to be 2D array. So, you might wanna look into that. Rest of the code is irrelevant to the question you asked. So, good luck!

Related

Generating a random float below and above a line created by numpy arrays python

I would like to generate a random float point above and below a line created by numpy arrays.
For example I have these line equations:
x_values = np.linspace(-1, 1, 100)
y1 = 2 * x_values -5
y2= -3 * x_values +2
plt.plot(x_values,y1, '-k')
plt.plot(x_values,y2, '-g')
I have tried this method from Generate random points above and below a line in Python and it works if np.arrange is used like so:
lower, upper = -25, 25
num_points = 1
x1 = [random.randrange(start=1, stop=9) for i in range(num_points)]
x2 = [random.randrange(start=1, stop=9) for i in range(num_points)]
y1 = [random.randrange(start=lower, stop=(2 * x -5) )for x in x1]
y2 = [random.randrange(start=(2 * x -5), stop=upper) for x in x2]
plt.plot(np.arange(10), 2 * np.arange(10) -5)
plt.scatter(x1, y1, c='blue')
plt.scatter(x2, y2, c='red')
However, I wanted to find a way to generate a random point if np.linspace(-1, 1, 100) was used to create the line graph. The difference is involving/allowing float coordinates to be picked to. But unsure how.
Any ideas will be appreciated.
Here is an approach, using functions for the y-values. Random x positions are chosen uniformly over the x-range. For each random x, a value is randomly chosen between its y-ranges.
import numpy as np
import matplotlib.pyplot as plt
x_values = np.linspace(-1, 1, 100)
f1 = lambda x: 2 * x - 5
f2 = lambda x: -3 * x + 2
y1 = f1(x_values)
y2 = f2(x_values)
plt.plot(x_values, y1, '-k')
plt.plot(x_values, y2, '-g')
plt.fill_between (x_values, y1, y2, color='gold', alpha=0.2)
num_points = 20
xs = np.random.uniform(x_values[0], x_values[-1], num_points)
ys = np.random.uniform(f1(xs), f2(xs))
plt.scatter(xs, ys, color='crimson')
plt.show()
PS: Note that the simplicity of the approach chooses x uniform over its length. If you need an even distribution over the area of the trapezium, you need the x less probable at the right, and more at the left. You can visualize this with many more points and using transparency. With the simplistic approach, the right will look denser than the left.
The following code first generates x,y points in a parallelogram, and remaps the points on the wrong side back to its mirror position. The code looks like:
import numpy as np
import matplotlib.pyplot as plt
x0, x1 = -1, 1
x_values = np.linspace(x0, x1, 100)
f1 = lambda x: 2 * x - 5
f2 = lambda x: -3 * x + 2
y1 = f1(x_values)
y2 = f2(x_values)
plt.plot(x_values, y1, '-k')
plt.plot(x_values, y2, '-g')
plt.fill_between(x_values, y1, y2, color='gold', alpha=0.2)
num_points = 100_000
h0 = f2(x0) - f1(x0)
h1 = f2(x1) - f1(x1)
xs1 = np.random.uniform(x0, x1, num_points)
ys1 = np.random.uniform(0, h0 + h1, num_points) + f1(xs1)
xs = np.where(ys1 <= f2(xs1), xs1, x0 + x1 - xs1)
ys = np.where(ys1 <= f2(xs1), ys1, f1(xs) + h0 + h1 + f1(xs1) - ys1)
plt.scatter(xs, ys, color='crimson', alpha=0.2, ec='none', s=1)
plt.show()
Plot comparing the two approaches:
First of all, if you have 2 intersecting lines, there will most likely be a triangle in which you can pick random points. This is dangerously close to Bertrand's paradox, so make sure that your RNG suits its purpose.
If you don't really care about how "skewed" your randomness is, try this:
import numpy as np
left, right = -1, 1
# x_values = np.linspace(left, right, 100)
k1, k2 = 2, -3
b1, b2 = -5, 2
y1 = lambda x: k1*x + b1
y2 = lambda x: k2*x + b2
# If you need a point above the 1st equation, but below the second one.
# Check the limits where you can pick the points under this condition.
nosol = False
if k1==k2:
if b1>=b2:
inters = -100
nosol = True
else:
rand_x = np.random.uniform(left,right)
rand_y = np.random.uniform(y1(rand_x),y2(rand_x))
print(f'Random point is ({round(rand_x,2)}, {round(rand_y,2)})')
else:
inters = (b2-b1)/(k1-k2)
if inters<=left:
if k1>=k2:
nosol=True
elif inters>=right:
if k1<=k2:
nosol=True
if nosol:
print('No solution')
else:
if k1>k2:
right = inters
else:
left = inters
# Pick random X between "left" and "right"
# Pick whatever distribution you like or need
rand_x = np.random.uniform(left,right)
rand_y = np.random.uniform(y1(rand_x),y2(rand_x))
print(f'Random point is ({round(rand_x,2)}, {round(rand_y,2)})')
If your random X needs to belong to a specific number sequence, use some other np.random function: randint, choice...

forcing two matplotlib 3d plots to be in one figure

How can I make these to be overlapping or be together in one figure (not side by side):
fig = plt.figure()
ax = Axes3D(fig)
ax.set_xlim(-6, 6)
ax.set_ylim(-6, 6)
ax.set_zlim(-6, 6)
xx_floor, yy_floor, zz_floor = plot_plane(plane1['a'],
plane1['b'],
plane1['c'],
plane1['d'],
xwin=[-2, 2],
ywin=[-2, 2])
ax.plot_surface(xx_floor, yy_floor, zz_floor, color=(0, 1, 1, 0.3))
plane2_points = plane2[:3]
p0, p1, p2 = plane2_points
x0, y0, z0 = p0
x1, y1, z1 = p1
x2, y2, z2 = p2
ux, uy, uz = u = [x1 - x0, y1 - y0, z1 - z0]
vx, vy, vz = v = [x2 - x0, y2 - y0, z2 - z0]
u_cross_v = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx]
plane2_point = np.array(p0)
plane2_normal = np.array(u_cross_v)
pointdot = -plane2_point.dot(plane2_normal)
xx, yy = np.meshgrid(range(6), range(6))
z = (-plane2_normal[0] * xx - plane2_normal[1] * yy - pointdot) * 1. / plane2_normal[2]
plt3d = plt.figure().gca(projection='3d')
plt3d.plot_surface(xx, yy, z)
plt.show()
plt.close(fig=fig)
Currently, I see two figures that I should close the first one to see the second one.
fig = plt.figure()
ax = Axes3D(fig)
ax.set_xlim(-6, 6)
ax.set_ylim(-6, 6)
ax.set_zlim(-6, 6)
xx_floor, yy_floor, zz_floor = plot_plane(plane1['a'],
plane1['b'],
plane1['c'],
plane1['d'],
xwin=[-2, 2],
ywin=[-2, 2])
ax.plot_surface(xx_floor, yy_floor, zz_floor, color=(0, 1, 1, 0.3))
plane2_points = plane2[:3]
p0, p1, p2 = plane2_points
x0, y0, z0 = p0
x1, y1, z1 = p1
x2, y2, z2 = p2
ux, uy, uz = u = [x1 - x0, y1 - y0, z1 - z0]
vx, vy, vz = v = [x2 - x0, y2 - y0, z2 - z0]
u_cross_v = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx]
plane2_point = np.array(p0)
plane2_normal = np.array(u_cross_v)
pointdot = -plane2_point.dot(plane2_normal)
xx, yy = np.meshgrid(range(6), range(6))
z = (-plane2_normal[0] * xx - plane2_normal[1] * yy - pointdot) * 1. / plane2_normal[2]
#plt3d = plt.figure().gca(projection='3d')
#plt3d.plot_surface(xx, yy, z)
ax.plot_surface(xx, yy, z)
plt.show()
plt.close(fig=fig)
Followed the instructions by swatchai

The truth value of an array with more than one element is ambiguous. Use a.any() or a.all(). WHILE plotting 3d graph

import numpy as np
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
y = [4, 2]
def objectivedraw(a, b, y):
return -1 * (y[0] + 0.75 * max((1 - b) * y[0] - (y[1] + a), 0) - 0.5 * max((y[1] + a) - (1 - b) * y[0], 0) \
+ y[1] + 0.75 * max((1 - b) * y[1] - (y[0] + a), 0) - 0.5 * max((y[0] + b) - (1 - b) * y[1], 0))
a = np.arange(0, 3.0, 0.1)
b = np.arange(0, 1, 0.1)
A, B = np.meshgrid(a, b) # grid of point
Z = objectivedraw(A, B,y) # evaluation of the function on the grid
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap=cm.RdBu, linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
This is the code that I am using. The part related to plotting, i.e., after defining Z was coped from https://dzone.com/articles/how-plot-function-two (second block of code on the website). I am getting an error message:
File "C:/Users/rohan/PycharmProjects/untitled/plot utility.py", line 12, in objectivedraw
+ y[1] + 0.75 * max((1 - b) * y[1] - (y[0] + a), 0) - 0.5 * max((y[0] + b) - (1 - b) * y[1], 0))
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I cannot figure out how to fix it. I think its because of my function.
EDIT: I am trying to make a Fehr-Schmidt utility function, so that's what the function is.
For Z = objectivedraw(A, B, y) to work with A and B being (2D) numpy arrays, and Z is to be expected also a numpy array, function objectivedraw should be compatible with numpy. In numpy, functions on an array are executed element by element, e.g. np.sin(A) would have same number of elements and the same number of dimensions as A but with all elements replaced by their sine.
The #np.vectorize decorator can make a function vectorized. To be compatible with numpy, the max operators should be replaced by np.maximum. Also, the vectorization doesn't know how to cope with the list y. In this case the elements of y can be passed one by one.
import numpy as np
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
#np.vectorize
def objectivedraw(a, b, y0, y1):
return -1 * (y0 + 0.75 * np.maximum((1 - b) * y0 - (y1 + a), 0) - 0.5 * np.maximum((y1 + a) - (1 - b) * y0, 0)
+ y1 + 0.75 * np.maximum((1 - b) * y1 - (y0 + a), 0) - 0.5 * np.maximum((y0 + b) - (1 - b) * y1, 0))
y = [4, 2]
a = np.arange(0, 3.0, 0.1)
b = np.arange(0, 1, 0.1)
A, B = np.meshgrid(a, b) # grid of point
Z = objectivedraw(A, B, y[0], y[1]) # evaluation of the function on the grid
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(A, B, Z, rstride=1, cstride=1,
cmap=cm.RdBu, linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()

Creating non symmetrical axes in matplotlib

I'm trying to create the graph present in the image bellow. I'm new on matplotlib so I don't know how to create the axis without insert some data. For example, if a have x and y and plot(x,y), the graph will be create and the axis too, but the axis will be correlated with the data. In the graph attach, the curves are isolines, so the correlation of the data doesn't depend exactly of the axis (depend by a function that I have described in the code).
How can I proceed?
You can use plt.xlim and plt.ylim to set the limits on the axis. With plt.grid you can control how to show the gridlines. set_major_locator can set the position for the ticks and corresponding gridlines.
The position for the text could be calculated by first finding the y on the line that corresponds to x=40. If that y would be too low, we can calculate an x value on the line corresponding to y=-1500. For the rotation of the text, we can take the tangent of the line, which needs to be corrected by the aspect ratio of the plot. To have the rotation still valid when the size of the plot is changed, a fixed aspect ratio can be set.
Here is some demonstration code to get you started:
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import numpy as np
fig, ax = plt.subplots()
aspect = 1 / 300
ax.set_aspect(aspect) # a fixed aspect ratio is needed so the text rotation stays equal to the line rotation
for iso in range(-5000, 16001, 1000):
x1 = 25 + iso / 100
x2 = -50
y1 = -1000
y2 = 7000 + iso
plt.plot([x1, x2], [y1, y2], c='crimson', lw=2 if iso % 5000 == 0 else 0.5)
if iso % 5000 == 0:
# xt,yt: x,y value for text; first try y position when x = 40; if too small find x position for y = -1500
xt = 40
yt = (y1 * (xt - x2) + y2 * (x1 - xt)) / (x1 - x2)
if yt < y1:
yt = -1500
xt = (x1 * (yt - y2) + x2 * (y1 - yt)) / (y1 - y2)
ax.text(xt, yt, iso if iso != 0 else "zero",
{'ha': 'center', 'va': 'center', 'bbox': {'fc': 'lightgoldenrodyellow', 'pad': 2, 'alpha': 0.7}},
rotation=np.arctan(aspect * (y1 - y2) / (x1 - x2)) / np.pi * 180)
ax.xaxis.set_major_locator(MultipleLocator(10))
ax.xaxis.set_minor_locator(MultipleLocator(5))
ax.xaxis.set_major_formatter(FormatStrFormatter('%d°C'))
ax.yaxis.set_major_locator(MultipleLocator(5000))
ax.yaxis.set_minor_locator(MultipleLocator(1000))
plt.grid(True, which='major', color='blue', linestyle='--', lw=0.5)
plt.grid(True, which='minor', color='blue', linestyle='--', lw=0.2)
plt.xlim(-50, 50)
plt.ylim(-5000, 20000)
plt.show()

matplotlib, how to plot 3d 2 variable function under given conditions

Plotting the function:
(x1 - 3)^2 + (x2 - 2)^2
With constraints:
x1^2 - x2 - 3 <= 0
x2 - 1 <= 0
-x1 <= 0
The equation can also be found here.
I am trying to solve this graphically using matplotlib
but ended up with the above graph using the code below (the question i found that helped me with the code) which is missing the first condition.
import matplotlib.pyplot as plt
from numpy import arange
from pylab import meshgrid
# function to be plotted
def z_func(a, b):
return (a - 3) * (a - 3) + (b - 2) * (b - 2)
x1 = arange(15.0, 0, -0.1) # x1 >= 0 according to given conditions
x2 = arange(-15.0, 1, 0.1) # x2 <= 1 according to given conditions
X1,X2 = meshgrid(x1, x2)
Z = z_func(X1, X2)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X1, X2, Z, rstride=1, cstride=1, cmap=cm.RdBu,linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')
ax.view_init(elev=25, azim=-120)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
in which way shall the above code be altered in order to also take the first condition into account?
thanks
You can filter the array to plot and set all values outside the condition to nan:
Z[X1**2 - X2 - 3 > 0] = np.nan
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
import numpy as np
from pylab import meshgrid
# function to be plotted
def z_func(a, b):
return (a - 3) * (a - 3) + (b - 2) * (b - 2)
x1 = np.arange(15.0, 0, -0.1) # x1 >= 0 according to given conditions
x2 = np.arange(-15.0, 1, 0.1) # x2 <= 1 according to given conditions
X1,X2 = meshgrid(x1, x2)
Z = z_func(X1, X2)
# set all values outside condition to nan
Z[X1**2 - X2 - 3 > 0] = np.nan
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X1, X2, Z, rstride=1, cstride=1,vmin=0, vmax=np.nanmax(Z),
cmap=plt.cm.RdBu,linewidth=0, antialiased=False)
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')
ax.view_init(elev=25, azim=-120)
ax.set_ylim(0,4)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()

Categories