I have some data of the following type:
grid = np.array([posx, posy]) where posx and posy are the X/Y position some, stored in another array.
The (transposed) grid may look like:
grid = np.array([posx, posy])
print grid.T
[[ 2.47685286 2.51629155]
[ 2.47685286 8.51629155]
[ 2.47685286 14.51629155]
[ 8.47685286 5.51629155]
[ 8.47685286 11.51629155]
[ 14.47685286 2.51629155]
[ 14.47685286 8.51629155]
[ 14.47685286 14.51629155]]
Especially the y-Position is not identical in each "row" and the number of points differs, which I assume to be one of my problems.
Additionally, the corresponding data is stored in another (1D-)array like data = [2.3 4.7 -0.3 .....] having the same amount of entrys as I have points.
My aim is to plot this data in kind of a smooth heatmap displaying by colours indicating position of high / low values. So far I used:
import numpy as np
import matplotlib.pyplot as p
p.imshow(data, interpolation=None)
p.colorbar()
p.show()
Obviously my problem is that I need to adjust the positon of my points.
I searched some other posts but with this shape of data it never worked out.
Also I tried to adjust this by simply reshaping the data but this didn't work due to the irregular number of points
As I am new here I am also happy for comments on how to improve my post (more input needed etc.)
Thanks in advance!
There are several solutions to this problem.
If what you want is simply to have the points shown as markers of some size, with colors depending on the values in the z array, then a scatterplot will do nicely. If the space between the points should also be colored however, you should use interpolation and contouring. Fortunately those things have also been implemented in matplotlib for irregularly spaced data (data on an "unstructured grid"), which is what you have as the points cannot be easily mapped to a regular grid (although in the small example you've given, there does seem to be a tendency for equal-sized triangles).
Here are 3 examples that illustrate the functions you might want to look further into: plt.scatter, plt.tripcolor and plt.tricontourf. I've made the dataset to play with a bit larger, so that you can get a feeling of the function that is represented by z.
x,y = (2*np.random.rand(50)-1 for _ in range(2))
z = np.exp(-x*x - y*y) - np.cos(x) # turtle-surface around origin
f, ax = plt.subplots(1,3, sharex=True, sharey=True, num=2, subplot_kw={'xlim': (-1,1), 'ylim': (-1, 1)})
ax[0].scatter(x,y, s=500*(z-z.min()), c=z, cmap='hot') # scatterplot with variable-sized markers and colors
ax[1].tripcolor(x, y, z, cmap='hot') # creates a tesselation and colors the formed triangles based on the values in the 3 nodes
ax[2].tricontourf(x, y, z, cmap='hot') # estimates the underlying surface
for indx in (1,2):
ax[indx].triplot(x,y, 'ko ') # add the locations of the points
for axes in ax: # turn off the needless clutter
axes.tick_params(axis='both', which='both', bottom='off', left='off', labelbottom='off', labelleft='off')
ax[0].set_title('scatter')
ax[1].set_title('tripcolor')
ax[2].set_title('tricontourf')
I think your problem could be solved by creating a regular 2d-matrix and then using scipy.interpolate to interpolate the data between your data points. Example can be found at: http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html#id1
Related
I have trouble to produce four same sized plots.
I have four different plots, which are to be shown in a 2x2 matrix in a document. Two of the them have a second y-axis, and one of these have a slightly higher ax title (a greek letter). So, they come out in four differnt sizes of the plot, which does not look good. Additionally i Want to have them in single plots to give them an individual label.
Is there a way to directly set the length of the single axis in inch, so that they have exaclty the same size? And/or an option to define the origin ( in ccordinates) to prevent them from having a differnt adjustment?
Can I force them to be squred and equal using one plot? In this case, i will bite the bullet.
Thanks alot
Bad looking
This is how i plot each of the figures:
pre,ax = plt.subplots(figsize=(3,3))
ax2 = ax.twinx()
ax.plot([1,2],[3,4])
ax2.plot([3,4],[100,1000])
ax.set_box_aspect(1)
ax2.set_box_aspect(1)
plt.show()
To put an axes at exactly a given position in inches is relatively trivial. The following puts the axes exactly 0.5 inches from each side.
import matplotlib.pyplot as plt
w = 4
h = 3
margin = 0.5
fig =plt.figure(figsize=(w, h), facecolor='lightblue')
pos = [margin/w, margin/h, (w-2*margin)/ w, (h-2*margin)/h]
ax = fig.add_axes(pos)
plt.show()
This has been answered before, but many of the other solutions are pretty complex, whereas this is super straightforward.
So I am trying to create a 3D scatter plot of radar data, where each point is assigned an alpha value based on the amplitude of the corresponding pixel.
I have done this looping through all x,y,z points and building the scatter plot point by point assigning the alpha values on each iteration. But once the scatter plot is created it is very slow and unable to manipulate the graph without considerable time spent waiting for the plot to update.
Points is a normalised (0 to 1) array.
Here is a link to my data
Data
File preparation:
def normalise0to1(data):
normalised = (data - np.min(data)) / (np.max(data) - np.min(data))
return normalised
Data = np.loadtxt('filepath.txt')
points2D = normalise0to1(Data) #Is (101,400) size
points3D = np.reshape(points2D,(101,20,20)) #Is (101,20,20) size
And the first attempt at creating the 3D scatter plot:
def Scatter_Plot1(points3D):
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
for x in range(0,points3D[1]):
for y in range(0,points3D[2]):
for z in range(0,points3D[0]):
val = points3D[z,x,y]
ax.scatter(x, y, z, alpha=val,c='black',s=1)
plt.show()
This takes a long time to run and is very slow once created.
In 2D, I can do something like this. Bear in mind this is the same array as the 3D 'points' array, but the (20x20) has been flattened to 400. I have provided an image of the flattened array, you can see how it creates an image where intensity is scaled to the alpha value.
def Scatter_Plot2(points2D):
fig = plt.figure()
ax = fig.add_subplot()
x_=np.linspace(0,points2D.shape[1]-1,points2D.shape[1])
y_=np.linspace(0,points2D.shape[0]-1,points2D.shape[0])
x,y = np.meshgrid(x_,y_)
ax.scatter(x,y,alpha=points2D.flatten(),s=1,c='black')
plt.show()
This image is the flattened version of the 3D plot I want to create, where instead of 400 length, it would be (20x20) and overall the 3D shape is (101,20,20).
The problem comes when trying to assign the alpha to a 3D plot, in 2D it seems happy enough when I provide a flattened array to the alpha parameter.
I would like something like this, but whether that's possible is another question..
def Scatter_Plot3(points3D):
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
x_=np.linspace(0,points3D.shape[2]-1,points3D.shape[2])
y_=np.linspace(0,points3D.shape[1]-1,points3D.shape[1])
z_=np.linspace(0,points3D.shape[0]-1,points3D.shape[0])
x,y,z = np.meshgrid(x_,y_,z_)
ax.scatter(x,y,z,alpha=points3D,s=1,c='black')
plt.show()
Results in this image, which seems a bit random in terms of how the alpha is assigned. You would expect to see the dark horizontal lines as in the first picture. What I want may not be possible, I'm open to using another package, perhaps pyqtgraph or mayavi etc. But matplotlib would be preferable.
Thank you!
Edit:
I have achieved something similar to what I would like, though not exactly. I have used the c and cmap inputs. This isn't ideal as I am dealing with a 3D cube and viewing the centre is still difficult but it has correctly mapped a variation to the data. But it doesn't work when I use the alpha parameter.
Notice the 2 main horizontal bands and the dark bit in the centre which is hard to see.
What I need is the same mapping but rather opacity than colour.
c = (points2D.T.flatten())
ax.scatter(x,y,z,s=1,c=c,cmap='viridis_r')
I have a pair of lists of numbers representing points in a 2-D space, and I want to represent the y/x ratios for these points as a 1-dimensional heatmap, with a diverging color map centered around 1, or the logs of my ratios, with a diverging color map centered around 0.
How do I do that?
My current attempt (borrowing somewhat from Heatmap in matplotlib with pcolor?):
from matplotlib import numpy as np
import matplotlib.pyplot as plt
# There must be a better way to generate arrays of random values
x_values = [np.random.random() for _ in range(10)]
y_values = [np.random.random() for _ in range(10)]
labels = list("abcdefghij")
ratios = np.asarray(y_values) / np.asarray(x_values)
axis = plt.gca()
# I transpose the array to get the points arranged vertically
heatmap = axis.pcolor(np.log2([ratios]).T, cmap=plt.cm.PuOr)
# Put labels left of the colour cells
axis.set_yticks(np.arange(len(labels)) + 0.5, minor=False)
# (Not sure I get the label order correct...)
axis.set_yticklabels(labels)
# I don't want ticks on the x-axis: this has no meaning here
axis.set_xticks([])
plt.show()
Some points I'm not satisfied with:
The coloured cells I obtain are horizontally-elongated rectangles. I would like to control the width of these cells and obtain a column of cells.
I would like to add a legend for the color map. heatmap.colorbar = plt.colorbar() fails with RuntimeError: No mappable was found to use for colorbar creation. First define a mappable such as an image (with imshow) or a contour set (with contourf).
One important point:
matplotlib/pyplot always leaves me confused: there seems to be a lot of ways to do things and I get lost in the documentation. I never know what would be the "clean" way to do what I want: I welcome suggestions of reading material that would help me clarify my very approximative understanding of these things.
Just 2 more lines:
axis.set_aspect('equal') # X scale matches Y scale
plt.colorbar(mappable=heatmap) # Tells plt where it should find the color info.
Can't answer your final question very well. Part of it is due to we have two branches of doing things in matplotlib: the axis way (axis.do_something...) and the MATLAB clone way plt.some_plot_method. Unfortunately we can't change that, and it is a good feature for people to migrate into matplotlib. As far as the "Clean way" is concerned, I prefer to use whatever produces the shorter code. I guess that is inline with Python motto: Simple is better than complex and Readability counts.
I have 3 different parameters X,Y and Z over a range of values, and for each combination of these a certain value of V. To make it clearer, the data would look something like this.
X Y Z V
1 1 2 10
1 2 3 15
etc...
I'd like to visualize the data with a surface/contour plot, using V as a colour to see its value at that point, but I do not see how to add my custom colouring scheme into the mix using Python. Any idea on how to do this (or is this visualization outright silly)?
Thanks a lot!
Matplotlib allows one to pass the facecolors as an argument to e.g.
ax.plot_surface.
That would imply then that you would have to perform 2D interpolation on your
current array of colors, because you currently only have the colors in the
corners of the rectangular faces (you did mention that you have a rectilinear
grid).
You could use
scipy.interpolate.interp2d
for that, but as you see from the documentation, it is suggested to use
scipy.interpolate.RectBivariateSpline.
To give you a simple example:
import numpy as np
y,x = np.mgrid[1:10:10j, 1:10:10j] # returns 2D arrays
# You have 1D arrays that would make a rectangular grid if properly reshaped.
y,x = y.ravel(), x.ravel() # so let's convert to 1D arrays
z = x*(x-y)
colors = np.cos(x**2) - np.sin(y)**2
Now I have a similar dataset as you (one-dimensional arrays for x, y, z and
colors). Remark that the colors are defined for
each point (x,y). But when you want to plot with plot_surface, you'll
generate rectangular patches, of which the corners are given by those points.
So, on to interpolation then:
from scipy.interpolate import RectBivariateSpline
# from scipy.interpolate import interp2d # could 've used this too, but docs suggest the faster RectBivariateSpline
# Define the points at the centers of the faces:
y_coords, x_coords = np.unique(y), np.unique(x)
y_centers, x_centers = [ arr[:-1] + np.diff(arr)/2 for arr in (y_coords, x_coords)]
# Convert back to a 2D grid, required for plot_surface:
Y = y.reshape(y_coords.size, -1)
X = x.reshape(-1, x_coords.size)
Z = z.reshape(X.shape)
C = colors.reshape(X.shape)
#Normalize the colors to fit in the range 0-1, ready for using in the colormap:
C -= C.min()
C /= C.max()
interp_func = RectBivariateSpline(x_coords, y_coords, C.T, kx=1, ky=1) # the kx, ky define the order of interpolation. Keep it simple, use linear interpolation.
In this last step, you could also have used interp2d (with kind='linear'
replacing the kx=1, ky=1). But since the docs suggest to use the faster
RectBivariateSpline...
Now you're ready to plot it:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
r = ax.plot_surface(X,Y,Z,
facecolors=cm.hot(interp_func(x_centers, y_centers).T),
rstride=1, cstride=1) # only added because of this very limited dataset
As you can see, the colors on the faces have nothing to do anymore with the height of the dataset.
Note that you could have thought simply passing the 2D array C to facecolors would work, and matplotlib would not have complained. However, the result isn't accurate then, because matplotlib will use only a subset of C for the facecolors (it seems to ignore the last column and last row of C). It is equivalent to using only the color defined by one coordinate (e.g. the top-left) over the entire patch.
An easier method would have been to let matplotlib do the interpolation and obtain the facecolors and then pass those in to the real plot:
r = ax.plot_surface(X,Y,C, cmap='hot') # first plot the 2nd dataset, i.e. the colors
fc = r.get_facecolors()
ax.clear()
ax.plot_surface(X, Y, Z, facecolors=fc)
However, that won't work in releases <= 1.4.1 due to this recently submitted bug.
It really depends on how you plan on plotting this data. I like to plot graphs with gnuplot: it's easy, free and intuitive. To plot your example with gnuplot you'd have to print those line into a file (with only those four columns) and plot using a code like the following
reset
set terminal png
set output "out.png"
splot "file.txt" using 1:2:3:4 with lines palette
Assuming that you save your data into the file file.txt. splot stands for surface plot. Of course, this is a minimum example.
Alternatively you can use matplotlib, but that is not, in my opinion, as intuitive. Although it has the advantage of centering all the processing in python.
I would like to plot a 2D discretization rectangular mesh with non-regular
x y axes values, e.g. the typical discretization meshes used in CFD.
An example of the code may be:
fig = plt.figure(1,figsize=(12,8))
axes = fig.add_subplot(111)
matplotlib.rcParams.update({'font.size':17})
axes.set_xticks(self.xPoints)
axes.set_yticks(self.yPoints)
plt.grid(color='black', linestyle='-', linewidth=1)
myName = "2D.jpg"
fig.savefig(myName)
where self.xPoints and self.yPoints are 1D non-regular vectors.
This piece of code produce a good discretization mesh, the problem are the
xtics and ytics labels because they appear for all values of xPoints and yPoints (they overlap).
How can I easily redefine the printed values in the axes?
Let's say I only want to show the minimum and maximum value for x and y and not all values from the discretization mesh.
I cann't post a example-figure because it is the first time I ask something here (I can send it by mail if requested)
the problem is that you explicitly told matplotlib to label each point when you wrote:
axes.set_xticks(self.xPoints)
axes.set_yticks(self.yPoints)
comment out those lines and see what the result looks like.
Of course, if you only want the first and last point labelled, it becomes:
axes.set_xticks([self.xPoints[0], self.xPoints[-1]])
...
If the gridline was specified by axes.set_xticks(), I don't think it would be possible to show ticks without overlap in your case.
I may have a solution for you:
...
ax = plt.gca()
#Arr_y: y-direction data, 1D numpy array or list.
for j in range(len(Arr_y)):
plt.hline(y = Arr_y[j], xmin = Arr_x.min(), xmax = Arr_x.max(), color = 'black')
#Arr_x: x-direction data, 1D numpy array or list.
for i in range(len(Arr_x)):
plt.vline(x = Arr_x[i], ymin = Arr_y.min(), ymax = Arr_y.max(), color = 'black')
#Custom your ticks here, 1D numpy array or list.
ax.set_xticks(Arr_xticks)
ax.set_yticks(Arr_yticks)
plt.xlim(Arr_x.min(), Arr_x.max())
plt.ylim(Arr_y.min(), Arr_y.max())
plt.show()
...
hlines and vlines are horizontal and vertical lines, you can specify those lines with boundary data in both x and y directions.
I tried it with 60×182 non uniform mesh grid which cost me 1.2s, hope I can post a picture here.