Plotting a heatmap in Python using matplotlib - python

I am a new Python user and need help with plotting a heatmap in Python using matplotlib
I have three vectors [x, y, z], each having 7700 elements. I got the code for plotting the heatmap from google search (see below), but ended up with an error
Some pointers
In array "x", all the items are different
In array "y", not all the values are different
In array "z", not all the values are different
x = mdf_merged.get('Signal_x').samples # define the x array
y = mdf_merged.get('Signal_y').samples # define the y array
z = mdf_merged.get('Signal_z').samples # define the z array
x=np.unique(x)
x = np.unique(x)
y1, yind = np.unique(y, return_index=True)
X,Y = np.meshgrid(x,y[sorted(yind)])
Z=z.reshape(len(y1),len(x), order='F')
plt.pcolormesh(X,Y,Z)
plt.colorbar()
plt.xlabel("X-values")
plt.ylabel("Y-values")
I end up with this error
Traceback (most recent call last):
File "<input>", line 1, in <module>
ValueError: cannot reshape array of size 7700 into shape (6447,7700)
So my questions are
a) What could be the cause and possible solution to this ?
b) Why can't it directly take x, y, and z. Why do I have to do meshgrip and reshape ?
I am a newbie to Python so it could be nice with a little more detailed reply

So, with the help from the community, I have been able to reach closer to the solution. The thing that I did was
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
x = mdf_merged.get('VariableX').samples
y = mdf_merged.get('VariableY').samples
z = mdf_merged.get('VariableZ').samples
###
xi = np.linspace(min(x),max(x),10)
yi = np.linspace(min(y),max(y),20)
zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='linear')
plt.pcolormesh(xi, yi, zi)

Related

Evaluating and plotting a function z = f(x,y) with different array size of x and y

Although it's a notebook question, but I am unable to get it correct, my problem is:
I have a function y ranging from 0 to 5.3 with 130 divisions
I have a function z ranging from 0 to 0.3 with 48 divisions
I wanted to plot a function v such that:
v = cos(2* \pi *z)*sin(\pi *y)
I tried to do with Python using the following code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
y = np.arange(0, 5.3, 0.007692)
z = np.arange(0,0.3,0.021)
v = np.cos(2.0*math.pi*z)*np.sin(math.pi*y)
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter( y, z, v,
linewidths=1, alpha=.7,
edgecolor='k',
s = 200,
c=v)
plt.show()
By looking at the problem or at the code itself it's clear that the array size of y and z are different and correspondingly the function "v" could not be evaluated correctly and thus I am getting the following error:
v = np.cos(2.0*math.pi*z)*np.sin(math.pi*y)
ValueError: operands could not be broadcast together with shapes (15,) (690,)
I am unable to get it fixed, also I tried to make different arrays for y and z and then using two different loops for y and z evaluated the value for function z, but again I could not do it correctly. Could any one please help.
Using useful comment by #tmdavison https://stackoverflow.com/users/588071/tmdavison I used the np.meshgrid I got the following contour, which is close to, what I was expecting, but is it possible to get the 3D plot of y,z,v ? The updated code is given as:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
y = np.arange(0, 5.3, 0.007692)
z = np.arange(0,0.3,0.021)
xx, yy = np.meshgrid(y, z, sparse=True)
v = np.cos(2.0*math.pi*xx)*np.sin(math.pi*yy)
h = plt.contourf(y,z,v)
plt.colorbar()
plt.show()
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter( y, z, v,
linewidths=1, alpha=.7,
edgecolor='k',
s = 200,
c=v)
plt.show()
But it is giving me error which says:
ValueError: shape mismatch: objects cannot be broadcast to a single shape

Python: How to create a surface-plot from a collection of 3D coordinates

I am given three numpy-arrays, which contain the x, y, and z- coordinates of multiple points, respectively. In fact, there are 100 points, which are arranged in a grid:
So, although there are 100 points, there are only 10 different x- and 10 different y coordinates. There are, however, 100 different z coordinates.
I thought I could create a surface plot using the following code:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
def plot_surface():
x = np.arange(10)
y = np.arange(10)
z = z_coords.reshape(10,10)
X, Y = np.meshgrid(x, y)
fig = plt.figure()
ax = Axes3D(fig)
ax.plot_surface(X, Y, Z)
I am aware that - since I cannot describe the z-coordinate through a function of x and y - the z-coordinate e.g. at x=1 and between y=1 and y=2 will be constant. I am fine with this though.
Anyways, the code doesn't work. Maybe my thinking is wrong. Running this, I get the error:
ValueError: shape mismatch: objects cannot be broadcast to a single shape
Are you sure if your z array can be reshaped into (10,10)? I quickly ran the following as I didn't know specifics of your z array, it seems the plotting works as you wanted?
import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
def plot_surface():
x = np.arange(10)
y = np.arange(10)
z = np.zeros((len(x),len(x)))+10
X, Y = np.meshgrid(x, y)
fig = plt.figure()
ax = Axes3D(fig)
ax.plot_surface(X, Y, z)
plot_surface()
plt.show(True)

mplot3d (python) why plotting a line in 3d the coordinates need the metod flatten

I'm starting to learn python and the related graphical library.
After some experience in 2D I started to use 3D.
What I would like to do is plotting a circle in 3D.
I report a minimal example
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=plt.figaspect(1)) # only solution to define axis aspect equal
ax = fig.add_subplot((111), projection='3d')
t = np.linspace(0, np.pi * 2, 360, endpoint=True)
x = np.cos(t)
y = np.sin(t)
z = zeros((1, len(x)))
ax.plot(x.flatten(), y.flatten(), z.flatten(), color='red')
plt.show()
The question is: why if I use only x, y, z (without flatten) I obtain an error like:
input operand has more dimensions than allowed by the axis remapping?
Thank you
Your problem is the shape of z. You've defined it as (1,N), when it should be (N,).
Use z = np.zeros(shape=t.shape) and you won't need to flatten your array anymore

3D surface graph with matplotlib using dataframe columns to input the data

I have a spreadsheet file that I would like to input to create a 3D surface graph using Matplotlib in Python.
I used plot_trisurf and it worked, but I need the projections of the contour profiles onto the graph that I can get with the surface function, like this example.
I'm struggling to arrange my Z data in a 2D array that I can use to input in the plot_surface method. I tried a lot of things, but none seems to work.
Here it is what I have working, using plot_trisurf
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
df=pd.read_excel ("/Users/carolethais/Desktop/Dissertação Carol/Códigos/Resultados/res_02_0.5.xlsx")
fig = plt.figure()
ax = fig.gca(projection='3d')
# I got the graph using trisurf
graf=ax.plot_trisurf(df["Diametro"],df["Comprimento"], df["temp_out"], cmap=matplotlib.cm.coolwarm)
ax.set_xlim(0, 0.5)
ax.set_ylim(0, 100)
ax.set_zlim(25,40)
fig.colorbar(graf, shrink=0.5, aspect=15)
ax.set_xlabel('Diâmetro (m)')
ax.set_ylabel('Comprimento (m)')
ax.set_zlabel('Temperatura de Saída (ºC)')
plt.show()
This is a part of my df, dataframe:
Diametro Comprimento temp_out
0 0.334294 0.787092 34.801994
1 0.334294 8.187065 32.465551
2 0.334294 26.155976 29.206090
3 0.334294 43.648591 27.792126
4 0.334294 60.768219 27.163233
... ... ... ...
59995 0.437266 14.113660 31.947302
59996 0.437266 25.208851 30.317583
59997 0.437266 33.823035 29.405461
59998 0.437266 57.724209 27.891616
59999 0.437266 62.455890 27.709298
I tried this approach to use the imported data with plot_surface, but what I got was indeed a graph but it didn't work, here it's the way the graph looked with this approach:
Thank you so much
A different approach, based on re-gridding the data, that doesn't require that the original data is specified on a regular grid [deeply inspired by this example;-].
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as tri
from mpl_toolkits.mplot3d import Axes3D
np.random.seed(19880808)
# compute the sombrero over a cloud of random points
npts = 10000
x, y = np.random.uniform(-5, 5, npts), np.random.uniform(-5, 5, npts)
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.33*(x*x+y*y))
# prepare the interpolator
triang = tri.Triangulation(x, y)
interpolator = tri.LinearTriInterpolator(triang, z)
# do the interpolation
xi = yi = np.linspace(-5, 5, 101)
Xi, Yi = np.meshgrid(xi, yi)
Zi = interpolator(Xi, Yi)
# plotting
fig = plt.figure()
ax = fig.gca(projection='3d')
norm = plt.Normalize(-1,1)
ax.plot_surface(Xi, Yi, Zi,
cmap='inferno',
norm=plt.Normalize(-1,1))
plt.show()
plot_trisurf expects x, y, z as 1D arrays while plot_surface expects X, Y, Z as 2D arrays or as x, y, Z with x, y being 1D array and Z a 2D array.
Your data consists of 3 1D arrays, so plotting them with plot_trisurf is immediate but you need to use plot_surface to be able to project the isolines on the coordinate planes... You need to reshape your data.
It seems that you have 60000 data points, in the following I assume that you have a regular grid 300 points in the x direction and 200 points in y — but what is important is the idea of regular grid.
The code below shows
the use of plot_trisurf (with a coarser mesh), similar to your code;
the correct use of reshaping and its application in plot_surface;
note that the number of rows in reshaping corresponds to the number
of points in y and the number of columns to the number of points in x;
and 4. incorrect use of reshaping, the resulting subplots are somehow
similar to the plot you showed, maybe you just need to fix the number
of row and columns.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
x, y = np.arange(30)/3.-5, np.arange(20)/2.-5
x, y = (arr.flatten() for arr in np.meshgrid(x, y))
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.1*(x*x+y*y))
fig, axes = plt.subplots(2, 2, subplot_kw={"projection" : "3d"})
axes = iter(axes.flatten())
ax = next(axes)
ax.plot_trisurf(x,y,z, cmap='Reds')
ax.set_title('Trisurf')
X, Y, Z = (arr.reshape(20,30) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 20×30')
X, Y, Z = (arr.reshape(30,20) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 30×20')
X, Y, Z = (arr.reshape(40,15) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 40×15')
plt.tight_layout()
plt.show()

How to fix "not enough values to unpack (expected 2, got 1)"?

I have zero background in programming. I am trying to code something for my class and it is giving me an error "not enough values to unpack (expected 2, got 1)". What might be wrong with my codes?
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import shapefile as shp
#Read x, y, z file
data = pd.read_excel('RegionV.xlsx', header = None)
# Get X, Y, Z values from file
data = np.array(data)
data = data.astype(np.float)
x = data[:,0]
y = data[:,1]
z = data[:,2]
#Mask negative values of z (bathemetry)
zz = np.ma.masked_where(z <= 0, z)
#Create a map using basemap
fig = plt.figure(figsize=(10,10))
map = Basemap(projection='mill',llcrnrlat=11,urcrnrlat=15,\
llcrnrlon=122,urcrnrlon=125,resolution='h')
lon = np.linspace(min(x), max(x))
lat = np.linspace(min(y), max(y))
xs, ys = np.meshgrid(lon, lat)
x, y = map(xs, ys)
map.drawparallels(np.arange(12.,14.,0.5), labels=[0,0,0,1])
map.drawmeridians(np.arange(123.,126.,0.5), labels=[1,0,0,0])
#Plot
cmap = plt.cm.jet
cmap.set_bad(color='white')
m.pcolormesh(x, y, zz, cmap=cmap, vmax=1300, vmin=0)
m.colorbar(location='right', label='meters')
map.drawmapboundary()
map.drawcoastlines()
the first part of the error message says:
ValueError Traceback (most recent call last)
<ipython-input-50-3d7531011dc2> in <module>
44 cmap = plt.cm.jet
45 cmap.set_bad(color='white')
---> 46 m.pcolormesh(xs, ys, zz, cmap=cmap, vmax=1300, vmin=0)
47 m.colorbar(location='right', label='meters')
then, at the end,
ValueError: not enough values to unpack (expected 2, got 1)
Stackoverflow is not allowing me to post the entire error message. Hope everyone who see my post understands what I mean.
I think that the problem is with line x, y = map(xs, ys). Look at this page for the documentation of the Basemap() function and the example usage (search for ".basemap" keyword). It says that the example usage is:
# create Basemap instance for Robinson projection.
m = Basemap(projection='robin',lon_0=0.5*(lons[0]+lons[-1]))
# compute map projection coordinates for lat/lon grid.
x, y = m(*np.meshgrid(lons,lats))
You try to get x,y from Basemap() instance, which is only one object. That's why it says that it expected 2 values (x, y), but got only one (Basemap()).

Categories

Resources