2D Density or Frquency Scatter Plot in python - python

How can I make a scatter plot colored by density in matplotlib?
When I plot a colorbar it shows density scale, I want counts/percentage instead. How to convert density estimation to frequency counts?
Expected result is Fig.3 on page 8 of this paper: https://www.atmos-meas-tech.net/9/3293/2016/amt-9-3293-2016.pdf
If anyone can guide me to draw a plot similar to one shown in paper, it will be really helpful. Thank you in advance.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
# Generate fake data
x = np.random.normal(size=1000)
y = x * 3 + np.random.normal(size=1000)
# Calculate the point density
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
fig, ax = plt.subplots()
cax=ax.scatter(x, y, c=z, s=10, cmap=plt.cm.jet)
cbar = fig.colorbar(cax)
plt.show()
Another Method Tried:
#libraries
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import kde
# create data
x = np.random.normal(size=500)
y = x * 3 + np.random.normal(size=500)
# Evaluate a gaussian kde on a regular grid of nbins x nbins over
nbins=50
k = kde.gaussian_kde([x,y])
xi, yi = np.mgrid[min(x):max(x):nbins*1j, min(y):max(y):nbins*1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))
# Add color bar
plt.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=plt.cm.jet)
plt.colorbar()
plt.show()

That's not a histogram, it's just sampling your kde on a grid...
Try this: plt.hist2d(x, y)
you can specify the bins using bins=whatever argument and many more options...

Related

Plotting surface and curve in 3D and a curve in xy-plane, all in the same plot

To illustrate an optimization problem, I want all of this in the same 3D plot:
A surface.
A curve in the xy-plane.
A curve/path on the surface which marks out the points on the surface that lies directly above the curve in the xy-plane.
This is my code so far:
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
from mpl_toolkits import mplot3d
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
X = np.linspace(-5,5,100)
Y = X
X, Y = np.meshgrid(X, Y)
Z = 50 - X**2 - Y**2
#Plotting curve on the surface
ax = plt.axes(projection='3d')
yline = np.linspace(-5,5,100)
xline = -np.sqrt(4/(2+yline**2)) #the x-values of the curve in the xy-plane
zline = 50 - xline**2 - yline**2
ax.plot3D(xline, yline, zline, "black")
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
ax.set_zlim(0, 50)
#Plotting curve in xy-plane
a = 5
g = 1 - 2*X - X*Y**2
plt.contour(X,Y,g, [a], offset=0)
plt.show()
Here is the plot from two different angles:
Some problems:
First of all, it seems like the axes have been numbered twice. Is that because I make a meshgrid, and later on use ax.plot3D? That I use two different ways of plotting something, and as a consequence make the 3D space twice?
The path on the surface appears weakly. Is there a way to make the path more visible?
From the picture in bird perspective, we see that the path does not lie directly above the curve in the xy-plane. What would be easier, was if Python had a built-in function who could project the curve in the xy-plane directly onto the surface. Am I missing something here? Does anyone know of such a function?
These questions might be dummy questions, but answers and tips are highly appreciated!
The code creates two axes objects (both assigned to the ax variable) in the same figure. This is not needed and results in double ticks marks.
To make the path on the surface more visible, plot it with a higher zorder.
The curve on the surface does not overlap with the curve on the xy plane because these are different curves. To plot the projection of the surface curve on the xy plane, set all z-coordinates of the curve to 0.
Below is the code with these changes.
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
from mpl_toolkits import mplot3d
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
X = np.linspace(-5, 5, 100)
Y = X
X, Y = np.meshgrid(X, Y)
Z = 50 - X**2 - Y**2
yline = np.linspace(-5, 5, 100)
xline = -np.sqrt(4 / (2 + yline**2))
zline = 50 - xline**2 - yline**2
ax.plot3D(xline, yline, zline, "b", zorder=10)
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, alpha=0.7)
ax.set_zlim(0, 50)
#Plotting curve in xy-plane
ax.plot3D(xline, yline, 0, "k")
plt.show()

Plot 3d points (x,y,z) in 2d plot with colorbar

I have computed a lot (~5000) of 3d points (x,y,z) in a quite complicated way so I have no function such that z = f(x,y). I can plot the 3d surface using
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
I would like to plot this also in 2d, with a colorbar indicating the z-value. I know there is a simple solution using ax.contour if my z is a matrix, but here I only have a vector.
Attaching the plot_trisurf result when rotated to xy-plane. This is what I what like to achieve without having to rotate a 3d plot. In this, my variable surface_points is an np.array with size 5024 x 3.
I had the same problems in one of my codes, I solved it this way:
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pylab as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
nx = 10*int(np.sqrt(N))
xg = np.linspace(X.min(), X.max(), nx)
yg = np.linspace(Y.min(), Y.max(), nx)
xgrid, ygrid = np.meshgrid(xg, yg)
ctr_f = griddata((X, Y), Z, (xgrid, ygrid), method='linear')
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.contourf(xgrid, ygrid, ctr_f, cmap=cm.coolwarm)
plt.show()
You could use a scatter plot to display a projection of your z color onto the x-y axis.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
# fig = plt.figure()
# ax = fig.add_subplot(projection='3d')
# surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
fig = plt.figure()
cmap = cm.get_cmap('coolwarm')
color = cmap(Z)[..., :3]
plt.scatter(X,Y,c=color)
plt.show()
Since you seem to have a 3D shape that is hollow, you could split the projection into two like if you cur the shape in two pieces.
fig = plt.figure()
plt.subplot(121)
plt.scatter(X[Z<0.5],Y[Z<0.5],c=color[Z<0.5])
plt.title('down part')
plt.subplot(122)
plt.scatter(X[Z>=0.5],Y[Z>=0.5],c=color[Z>+0.5])
plt.title('top part')
plt.show()

Specify range of colors for density plot in Matplotlib

The following code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
# Generate fake data
x = np.random.normal(size=1000)
y = x * 3 + np.random.normal(size=1000)
# Calculate the point density
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
# Sort the points by density, so that the densest points are plotted last
idx = z.argsort()
x, y, z = x[idx], y[idx], z[idx]
fig, ax = plt.subplots()
ax.scatter(x, y, c=z, s=50, edgecolor='')
plt.show()
produces a graph like this:
How can I change the theme from red to, say, blue? Something like this:
import seaborn as sns
sns.palplot(sns.color_palette("Blues"))
You can assign any color map to scatter plot as follows. Here you will find all the existing colormaps in matplotlib.
The colormap you want is named Blues. You have to import matplotlib.cm to access the color maps and then pass the required color map as an argument to cmap in your scatter plot. Additionally, you can show the color bar for sake of interpretation of the colors. If you want to reverse a color map, just include _r at the end of that color map. For instance, Blues_r will reverse the map with the scale now going from blue (low) to white (high).
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
import matplotlib.cm as cm
# Generate fake data
x = np.random.normal(size=1000)
y = x * 3 + np.random.normal(size=1000)
# Calculate the point density
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
# Sort the points by density, so that the densest points are plotted last
idx = z.argsort()
x, y, z = x[idx], y[idx], z[idx]
fig, ax = plt.subplots()
ax_ = ax.scatter(x, y, c=z, cmap=cm.Blues, s=50, edgecolor='')
plt.colorbar(ax_)

Plot NumPy ndarray into a 3D surface

I have a numpy.ndarray of size 200x200. I want to plot it as a 3D surface where x and y are indexes of the array and z is the value of that array element. Is there any easy way to do it or do I have to transform my array into a long list?
For example using matplotlib:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
#your index
x = np.linspace(1, 200, 200);
y = np.linspace(1, 200, 200)
X, Y = np.meshgrid(x, y); #making a grid from it
fig = plt.figure()
ax = fig.gca(projection='3d')
R = np.sqrt(X**2 + Y**2) #make some calculations on the grid
Z = np.sin(R) #some more calculations
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-5, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
However, as your array is already quite large, you might want to consider a different plotting tool like mayavi. matplotlib usually puts a copy of your complete array into the plot. That's memory demanding when dealing with big data. But I'm not sure, whether mayavi does the same or not.
You can also use mayavi and plot your array as a plane with different colors representing the values. It would look like this:
import numpy
from mayavi import mlab
mlab.imshow(yourarray)
mlab.show()
Alternative you can create points with an elevation from the ground plane and get a fitting plane through the points. See here:http://docs.enthought.com/mayavi/mayavi/auto/example_surface_from_irregular_data.html#example-surface-from-irregular-data
What is best for you depends on the continuity of your data.
If what you want is to plot a 3D surface on top of a 2D grid what you could do is something similar to this:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
# create some fake data
array_distribution3d = np.ones((200, 200))
array_distribution3d[0:25, 0:25] = -1
# create the meshgrid to plot on
x = np.arange(0, array_distribution3d.shape[0])
y = np.arange(0, array_distribution3d.shape[1])
# here are the x,y and respective z values
X, Y = np.meshgrid(x, y)
z = []
for i in range(0, array_distribution3d.shape[0]):
z_y = []
for j in range(0, array_distribution3d.shape[1]):
z_y.append(array_distribution3d[i, j])
z.append(z_y)
Z = np.array(z)
# create the figure, add a 3d axis, set the viewing angle
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(111, projection='3d')
ax.view_init(45, 60)
# here we create the surface plot
ax.plot_surface(X, Y, Z)
However, to the best of my knowledge, this kind of data can be plotted as a colourmap.
This can be plotted as follows:
import numpy as np
import os.path
import matplotlib.pyplot as plt
array_distribution = np.ones((200, 200))
array_distribution[0:25, 0:25] = -1
fig = plt.imshow(array_distribution)
plt.colorbar(fraction=0.035, pad=0.035, ticks=[-1., 0., 1.])
axes = plt.gca()
axes.set_ylim([0, 200])
figure = plt.gcf()
file = os.path.join('demo1.png')
figure.savefig(file, dpi=250)
plt.close('all')
print('done')

Python plotting 2d data on to 3d axes

I've had a look at matplotlib's examples of 3d plots, but none of these give me what I want to plot, something like:
The plot shows a series of measurements on the y-axis (N) and each measurement has an intensity spectrum (p/2hk_L), i.e. N is fixed for each line you see in the graph. What is the easiest function to use to plot data like this?
Here is a try:
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.linspace(-50,50,100)
y = np.arange(25)
X,Y = np.meshgrid(x,y)
Z = np.zeros((len(y),len(x)))
for i in range(len(y)):
damp = (i/float(len(y)))**2
Z[i] = 5*damp*(1 - np.sqrt(np.abs(x/50)))
Z[i] += np.random.uniform(0,.1,len(Z[i]))
ax.plot_surface(X, Y, Z, rstride=1, cstride=1000, color='w', shade=False, lw=.5)
ax.set_zlim(0, 5)
ax.set_xlim(-51, 51)
ax.set_zlabel("Intensity")
ax.view_init(20,-120)
plt.show()

Categories