Rasterization of contours in filled contour plot [duplicate] - python
This question already has an answer here:
Reducing size of vectorized contourplot
(1 answer)
Closed 5 years ago.
I have a filled contour plot, which I wish to save as an .svg or .pdf file. The following is a simplified example. I want to rasterize the contour plot itself (the colorful part!), while keeping everything else (all axes, labels etc.) as vector graphics.
import numpy as np
import matplotlib.pylab as plt
x = np.linspace(0, 2*np.pi, 100)
y = np.linspace(0, 2*np.pi, 100)
xi, yi = np.meshgrid(x, y)
zi = np.cos(xi)**2 + np.sin(yi)**2
plt.figure()
plt.contourf(xi, yi, zi, rasterized=True)
plt.savefig('fig.svg', dpi=100)
However, when I inspect fig.svg or open it for editing in Inkscape (I am able to ungroup the filled contour into vector shapes) it is clear that rasterization has not worked!
That's fine for such a simple plot, but if my plot has a higher number of contour levels (below) the vector image will need many many curves and the filesize would be much bigger.
plt.close()
plt.figure()
plt.contourf(xi, yi, zi, 100, rasterized=True)
plt.savefig('fig.svg', dpi=100)
Can someone please suggest a solution and explain why this rasterized=True flag has not done what I require?
I just found this is a duplicate of this question.
Using rasterized=True as argument to contour or contourf should show a
UserWarning: The following kwargs were not used by contour: 'rasterized'
In order to rasterize a contour plot, you need to rasterize its individual parts, i.e.
cs = plt.contour(...)
for c in cs.collections:
c.set_rasterized(True)
The example from the question would hence look like
import numpy as np
import matplotlib.pylab as plt
x = np.linspace(0, 2*np.pi, 100)
y = np.linspace(0, 2*np.pi, 100)
xi, yi = np.meshgrid(x, y)
zi = np.cos(xi)**2 + np.sin(yi)**2
plt.figure()
cs = plt.contourf(xi, yi, zi)
for c in cs.collections:
c.set_rasterized(True)
plt.savefig('fig.svg', dpi=100)
Related
How to convert a matrix to heatmap image in torch [duplicate]
Using Matplotlib, I want to plot a 2D heat map. My data is an n-by-n Numpy array, each with a value between 0 and 1. So for the (i, j) element of this array, I want to plot a square at the (i, j) coordinate in my heat map, whose color is proportional to the element's value in the array. How can I do this?
The imshow() function with parameters interpolation='nearest' and cmap='hot' should do what you want. Please review the interpolation parameter details, and see Interpolations for imshow and Image antialiasing. import matplotlib.pyplot as plt import numpy as np a = np.random.random((16, 16)) plt.imshow(a, cmap='hot', interpolation='nearest') plt.show()
Seaborn is a high-level API for matplotlib, which takes care of a lot of the manual work. seaborn.heatmap automatically plots a gradient at the side of the chart etc. import numpy as np import seaborn as sns import matplotlib.pylab as plt uniform_data = np.random.rand(10, 12) ax = sns.heatmap(uniform_data, linewidth=0.5) plt.show() You can even plot upper / lower left / right triangles of square matrices. For example, a correlation matrix, which is square and is symmetric, so plotting all values would be redundant. corr = np.corrcoef(np.random.randn(10, 200)) mask = np.zeros_like(corr) mask[np.triu_indices_from(mask)] = True with sns.axes_style("white"): ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True, cmap="YlGnBu") plt.show()
I would use matplotlib's pcolor/pcolormesh function since it allows nonuniform spacing of the data. Example taken from matplotlib: import matplotlib.pyplot as plt import numpy as np # generate 2 2d grids for the x & y bounds y, x = np.meshgrid(np.linspace(-3, 3, 100), np.linspace(-3, 3, 100)) z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2) # x and y are bounds, so z should be the value *inside* those bounds. # Therefore, remove the last value from the z array. z = z[:-1, :-1] z_min, z_max = -np.abs(z).max(), np.abs(z).max() fig, ax = plt.subplots() c = ax.pcolormesh(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max) ax.set_title('pcolormesh') # set the limits of the plot to the limits of the data ax.axis([x.min(), x.max(), y.min(), y.max()]) fig.colorbar(c, ax=ax) plt.show()
For a 2d numpy array, simply use imshow() may help you: import matplotlib.pyplot as plt import numpy as np def heatmap2d(arr: np.ndarray): plt.imshow(arr, cmap='viridis') plt.colorbar() plt.show() test_array = np.arange(100 * 100).reshape(100, 100) heatmap2d(test_array) This code produces a continuous heatmap. You can choose another built-in colormap from here.
Here's how to do it from a csv: import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import griddata # Load data from CSV dat = np.genfromtxt('dat.xyz', delimiter=' ',skip_header=0) X_dat = dat[:,0] Y_dat = dat[:,1] Z_dat = dat[:,2] # Convert from pandas dataframes to numpy arrays X, Y, Z, = np.array([]), np.array([]), np.array([]) for i in range(len(X_dat)): X = np.append(X, X_dat[i]) Y = np.append(Y, Y_dat[i]) Z = np.append(Z, Z_dat[i]) # create x-y points to be used in heatmap xi = np.linspace(X.min(), X.max(), 1000) yi = np.linspace(Y.min(), Y.max(), 1000) # Interpolate for plotting zi = griddata((X, Y), Z, (xi[None,:], yi[:,None]), method='cubic') # I control the range of my colorbar by removing data # outside of my range of interest zmin = 3 zmax = 12 zi[(zi<zmin) | (zi>zmax)] = None # Create the contour plot CS = plt.contourf(xi, yi, zi, 15, cmap=plt.cm.rainbow, vmax=zmax, vmin=zmin) plt.colorbar() plt.show() where dat.xyz is in the form x1 y1 z1 x2 y2 z2 ...
Use matshow() which is a wrapper around imshow to set useful defaults for displaying a matrix. a = np.diag(range(15)) plt.matshow(a) https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.matshow.html This is just a convenience function wrapping imshow to set useful defaults for displaying a matrix. In particular: Set origin='upper'. Set interpolation='nearest'. Set aspect='equal'. Ticks are placed to the left and above. Ticks are formatted to show integer indices.
Here is a new python package to plot complex heatmaps with different kinds of row/columns annotations in Python: https://github.com/DingWB/PyComplexHeatmap
Scipy 2D interpolation skips first row
I build a Jupyter Notebookthat imports geoelectric VES point data and subsequently interpolates the point data over a uniform 2D Mesh. I added the relevant parts of the code below (the previous part only imports all data into a dataframe). x = df['Distance X [m]'].to_numpy() y = df['AB/2 [m]'].to_numpy() z = df['Resistivity [Ohmm]'].to_numpy() #plot cax = plt.scatter(x, y, c=z) cbar = plt.colorbar(cax, fraction=0.03) plt.title('Measured Resistivity') #invert y axis plt.gca().invert_yaxis() plt.savefig('datapoints.png',dpi=100) import numpy as np from scipy.interpolate import griddata from matplotlib.pyplot import figure # target grid to interpolate to xi = np.arange(0,6500,20) yi = np.arange(0,500,20) xi,yi = np.meshgrid(xi,yi) # interpolate zi = griddata((x,y),z,(xi,yi),method='cubic') # plot fig = plt.figure() figure(figsize=(12, 6), dpi=80) #ax = fig.add_subplot(111) plt.contourf(xi,yi,zi) plt.plot(x,y,'k.') plt.xlabel('xi',fontsize=16) plt.ylabel('yi',fontsize=16) plt.gca().invert_yaxis() plt.colorbar() plt.savefig('interpolated.png',dpi=100) #plt.close(fig) So far, I managed to import my dataset, plot it and interpolate over the grid. However, especially at higher grid spacings, it becomes obvious that for some reason, the cubic and linear do not interpolation does not include the first row of the mesh (in my context the first meters of the subsurface) which is actually supposed to have the best data coverage. Only the nearest neighbor method works fine. In the added image e.g., the first 20m are not resolved. Link to Interpolated Section
Plotting a heatmap with interpolation in Python using excel file
I need to plot a HEATMAP in python using x, y, z data from the excel file. All the values of z are 1 except at (x=5,y=5). The plot should be red at point (5,5) and blue elsewhere. But I am getting false alarms which need to be removed. The COLORMAP I have used is 'jet' X=[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9] Y=[0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9] Z=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] Code I have used is: import matplotlib.pyplot as plt import numpy as np from numpy import ravel from scipy.interpolate import interp2d import pandas as pd import matplotlib as mpl excel_data_df = pd.read_excel('test.xlsx') X= excel_data_df['x'].tolist() Y= excel_data_df['y'].tolist() Z= excel_data_df['z'].tolist() x_list = np.array(X) y_list = np.array(Y) z_list = np.array(Z) # f will be a function with two arguments (x and y coordinates), # but those can be array_like structures too, in which case the # result will be a matrix representing the values in the grid # specified by those arguments f = interp2d(x_list,y_list,z_list,kind="linear") x_coords = np.arange(min(x_list),max(x_list)) y_coords = np.arange(min(y_list),max(y_list)) z= f(x_coords,y_coords) fig = plt.imshow(z, extent=[min(x_list),max(x_list),min(y_list),max(y_list)], origin="lower", interpolation='bicubic', cmap= 'jet', aspect='auto') # Show the positions of the sample points, just to have some reference fig.axes.set_autoscale_on(False) #plt.scatter(x_list,y_list,400, facecolors='none') plt.xlabel('X Values', fontsize = 15, va="center") plt.ylabel('Y Values', fontsize = 15,va="center") plt.title('Heatmap', fontsize = 20) plt.tight_layout() plt.show() For your ease you can also use the X, Y, Z arrays instead of reading excel file. The result that I am getting is: Here you can see dark blue regions at (5,0) and (0,5). These are the FALSE ALARMS I am getting and I need to REMOVE these. I am probably doing some beginner's mistake. Grateful to anyone who points it out. Regards
There are at least three problems in your example: x_coords and y_coords are not properly resampled; the interpolation z does to fill in the whole grid leading to incorrect output; the output is then forced to be plotted on the original grid (extent) that add to the confusion. Leading to the following interpolated results: On what you have applied an extra smoothing with imshow. Let's create your artificial input: import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 11) y = np.arange(0, 11) X, Y = np.meshgrid(x, y) Z = np.ones(X.shape) Z[5,5] = 9 Depending on how you want to proceed, you can simply let imshow smooth your signal by interpolation: fig, axe = plt.subplots() axe.imshow(Z, origin="lower", cmap="jet", interpolation='bicubic') And you are done, simple and efficient! If you aim to do it by yourself, then choose the interpolant that suits you best and resample on a grid with a higher resolution: interpolant = interpolate.interp2d(x, y, Z.ravel(), kind="linear") xlin = np.linspace(0, 10, 101) ylin = np.linspace(0, 10, 101) zhat = interpolant(xlin, ylin) fig, axe = plt.subplots() axe.imshow(zhat, origin="lower", cmap="jet") Have a deeper look on scipy.interpolate module to pick up the best interpolant regarding your needs. Notice that all methods does not expose the same interface for imputing parameters. You may need to reshape your data to use another objects. MCVE Here is a complete example using the trial data generated above. Just bind it to your excel columns: # Flatten trial data to meet your requirement: x = X.ravel() y = Y.ravel() z = Z.ravel() # Resampling on as square grid with given resolution: resolution = 11 xlin = np.linspace(x.min(), x.max(), resolution) ylin = np.linspace(y.min(), y.max(), resolution) Xlin, Ylin = np.meshgrid(xlin, ylin) # Linear multi-dimensional interpolation: interpolant = interpolate.NearestNDInterpolator([r for r in zip(x, y)], z) Zhat = interpolant(Xlin.ravel(), Ylin.ravel()).reshape(Xlin.shape) # Render and interpolate again if necessary: fig, axe = plt.subplots() axe.imshow(Zhat, origin="lower", cmap="jet", interpolation='bicubic') Which renders as expected:
How to draw a circle with matplotlib.pyplot [duplicate]
This question already has answers here: Why is matplotlib plotting my circles as ovals? (3 answers) Closed 2 years ago. I'm trying to draw a circle using pyplot. However, in the plot is looks like an ellipse. The variable r can have any value. theta = np.linspace(0, 2*np.pi, 100) r= r/2 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y)
Use the pyplot.axis('equal') method (documentation). This will tell matplotlib to automatically keep the x and y axis scaling equal and will work even if you change the axis limits: import matplotlib.pyplot as plt import numpy as np theta = np.linspace(0, 2*np.pi, 100) r= 2 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) plt.axis('equal') plt.show() As you can see, this causes circles to be circles even if the x and y axis have different limits. It also allows for autoscaling if additional items are added to the plot and it will maintain the aspect ratio when the plot area is modified by adding axis labels or a plot title. There are additional examples of this in the Matplotlib documentation: Axis Equal Demo. Another option is to use pyplot.axis('scaled'), which changes the dimensions of the plot box to make the axis scaling equal. The difference is that this option will block further autoscaling after it has been set. For example, with 'scaled' axes, matplotlib will respect the values you specify for the x- and y-axis limits: import matplotlib.pyplot as plt import numpy as np theta = np.linspace(0, 2*np.pi, 100) r= 2 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) plt.axis('scaled') plt.ylim(-3, 3) plt.xlim(-3, 3) plt.show()
Your code is correct. Most likely the aspect ratio of your figure is not equal. Setting the figsize to equal arguments should fix that theta = np.linspace(0, 2*np.pi, 100) r= r/2 x = r*np.cos(theta) y = r*np.sin(theta) plt.figure(figsize=(10,10)) plt.plot(x,y)
Ellipsoid creation in Python
I have ran into a problem relating to the drawing of the Ellipsoid. The ellipsoid that I am drawing to draw is the following: x**2/16 + y**2/16 + z**2/16 = 1. So I saw a lot of references relating to calculating and plotting of an Ellipse void and in multiple questions a cartesian to spherical or vice versa calculation was mentioned. Ran into a website that had a calculator for it, but I had no idea on how to successfully perform this calculation. Also I am not sure as to what the linspaces should be set to. Have seen the ones that I have there as defaults, but as I got no previous experience with these libraries, I really don't know what to expect from it. from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np fig = plt.figure(figsize=plt.figaspect(1)) # Square figure ax = fig.add_subplot(111, projection='3d') multip = (1, 1, 1) # Radii corresponding to the coefficients: rx, ry, rz = 1/np.sqrt(multip) # Spherical Angles u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) # Cartesian coordinates #Lots of uncertainty. #x = #y = #z = # Plot: ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b') # Axis modifications max_radius = max(rx, ry, rz) for axis in 'xyz': getattr(ax, 'set_{}lim'.format(axis))((-max_radius, max_radius)) plt.show()
Your ellipsoid is not just an ellipsoid, it's a sphere. Notice that if you use the substitution formulas written below for x, y and z, you'll get an identity. It is in general easier to plot such a surface of revolution in a different coordinate system (spherical in this case), rather than attempting to solve an implicit equation (which in most plotting programs ends up jagged, unless you take some countermeasures). from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np phi = np.linspace(0,2*np.pi, 256).reshape(256, 1) # the angle of the projection in the xy-plane theta = np.linspace(0, np.pi, 256).reshape(-1, 256) # the angle from the polar axis, ie the polar angle radius = 4 # Transformation formulae for a spherical coordinate system. x = radius*np.sin(theta)*np.cos(phi) y = radius*np.sin(theta)*np.sin(phi) z = radius*np.cos(theta) fig = plt.figure(figsize=plt.figaspect(1)) # Square figure ax = fig.add_subplot(111, projection='3d') ax.plot_surface(x, y, z, color='b')