I am plotting my pandas data using matplotlib, My plot looks like this:
There are four classes in the dataset. I want to color the backgroud area for each class, something like this
My matplotlib code looks like this:
import pandas as pd
df = pd.read_csv('normalized.csv')
fig = plt.figure(figsize=(8,8))
plt.scatter(df['p1'], df['p2'], c= list(df['cs']), alpha=0.9)
plt.show()
I also tried sns for this:
import pandas as pd
df = pd.read_csv('normalized.csv')
sn.FacetGrid(df, hue="cs", size = 8).map(plt.scatter, "p1", "p2").add_legend()
plt.show()
How I can fill the backgroud area for four classes in any of module?
A filled contour could serve as background:
import numpy as np
import matplotlib.pyplot as plt
N = 100
M = 4
points = np.random.normal(np.tile(np.random.uniform(1, 10, 2 * M), N)).reshape(-1, 2)
group = np.tile(np.arange(M), N)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 5), sharey=True, sharex=True)
cmap = plt.cm.get_cmap('tab10', 4)
ax1.scatter(points[:, 0], points[:, 1], c=group, cmap=cmap)
ax2.scatter(points[:, 0], points[:, 1], c=group, cmap=cmap)
ax2.tricontourf(points[:, 0], points[:, 1], group, levels=np.arange(-0.5, 4), zorder=0, cmap=cmap, alpha=0.3)
plt.show()
Note that the contour plot also creates some narrow zones of inbetween values, because it only looks at numeric values and supposes that between a zone 0 and a zone 2 there must exist some small zone 1.
A bit more involved approach uses a nearest neighbor fit:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import neighbors
N = 100
M = 4
points = np.random.normal(np.tile(np.random.uniform(1, 10, 2 * M), N)).reshape(-1, 2)
groups = np.tile(np.arange(M), N)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 5), sharey=True, sharex=True)
cmap = ListedColormap(['orange', 'cyan', 'cornflowerblue', 'crimson'])
ax1.scatter(points[:, 0], points[:, 1], c=groups, cmap=cmap)
ax2.scatter(points[:, 0], points[:, 1], c=groups, cmap=cmap)
clf = neighbors.KNeighborsClassifier(10)
clf.fit(points, groups)
x_min, x_max = points[:, 0].min() - 1, points[:, 0].max() + 1
y_min, y_max = points[:, 1].min() - 1, points[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 50),
np.linspace(y_min, y_max, 50))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
ax2.imshow(Z, extent=[x_min, x_max, y_min, y_max], cmap=cmap, alpha=0.3, aspect='auto', origin='lower')
plt.show()
If you don't need to fill the space and do not bother about areas overlap (your data points show some overlap) then you can try to fill out the convex hull defined by each subset.
import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial import ConvexHull
N = 100
points = [np.random.normal(np.tile(np.random.uniform(1, 5, 2), N)).reshape(-1, 2) for i in range(4)]
colors = ['r', 'g', 'b', 'k']
for k in range(4):
hull = ConvexHull(points[k])
plt.plot(points[k][:,0], points[k][:,1], '.', color = colors[k])
plt.fill(points[k][hull.vertices,0], points[k][hull.vertices,1], color = colors[k], alpha=0.3)
stack.imgur.com/2562R.png
Related
How to plot this kind of thermal plot in Python? I tried to search for any sample plot like this but didn't find one.
This image I got from the internet. I want to plot something same like this:
FROM
TO
To represent this type of data the canonical solution is, of course, a heat map. Here it is the code to produce both the figures at the top of this post.
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 5, 501)
x = np.linspace(0, 1, 201)[:, None]
T = 50 + (30-6*t)*(4*x*(1-x)) + 4*t
fig, ax = plt.subplots(layout='constrained')
hm = ax.imshow(T, cmap='plasma',
aspect='auto', origin='lower', extent=(0, 5, 0, 1))
fig.colorbar(hm)
def heat_lines(x, t, T, n):
from matplotlib.cm import ScalarMappable
from matplotlib.collections import LineCollection
lx, lt = T.shape
ones = np.ones(lx)
norm = plt.Normalize(np.min(T), np.max(T))
plasma = plt.cm.plasma
fig, ax = plt.subplots(figsize=(1+1.2*n, 9), layout='constrained')
ax.set_xlim((-0.6, n-0.4))
ax.set_ylim((x[0], x[-1]))
ax.set_xticks(range(n))
ax.tick_params(right=False,top=False, bottom=False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.grid(axis='y')
fig.colorbar(ScalarMappable(cmap=plasma, norm=norm))
dt = round(lt/(n-1))
for pos, ix in enumerate(range(0, len(t)+dt//2, dt)):
points = np.array([ones*pos, x[:,0]]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, linewidth=72, ec=None,
color=plasma(norm(T[:,ix])))
lc.set_array(T[:,ix])
ax.add_collection(lc)
heat_lines(x, t, T, 6)
I'm trying to have the following plot to appear like the second plot.
Without the axises (vertical, horizontal) that have no meaning for this plot and range from 0 to 1.
This is the code I'm using to generate to plot:
import matplotlib.pyplot as plt
import numpy as np
x_lim = (0, 1)
y_lim = (0, 1)
z_lim = (0, 1)
list_points = [[0.3, 0.3, 0], [0.4, 0.4, 0], [0, 0, 0], [.1, .1, .5], [0.3, 0.3, .2]]
def plot_tracking_map():
"""
Visualize all grapes centers on a 3d map.
This function generates a plot that represents the TB in 3D.
"""
x_cors, y_cors, z_cors = [], [], []
for i in range(len(list_points)):
x_cor, y_cor, z_cor = list_points[i][0], list_points[i][1], list_points[i][2]
x_cors.append(x_cor)
y_cors.append(y_cor)
z_cors.append(z_cor)
fig, ax = plt.subplots(figsize=(12, 12))
ax = fig.add_subplot(projection='3d')
yy, zz = np.meshgrid(range(2), range(2))
xx = yy
s = ax.scatter(x_cors, y_cors, z_cors, s=400, marker='o') # x,y,z coordinates, size of each point, colors.
# controls the alpha channel. all points have the same value, ignoring their distance
s.set_edgecolors = s.set_facecolors = lambda *args: None
ax.title.set_text(f'Imgae number 1')
plt.show()
plot_tracking_map()
edit
I changed the lines
fig, ax = plt.subplots(figsize=(12, 12))
ax = fig.add_subplot(projection='3d')
to
fig, ax = plt.subplots(figsize=(12, 12), subplot_kw={'projection': '3d'})
And it solved the problem.
We are working on a project in which we would like to construct 3D plots. Python is our main language, and therefore naturally chose to use matplotlib as our plotting library. Various tutorials (here, here and here) have teached us how to perform 3D plotting using the mplot3d functionality of matplotlib. Consequently, various StackOverflow answers helped us to move the origin of each of the axes to different locations (here and here).
After searching for a couple of hours we have a hard time finding an answer to our next question, however. We would like to have a positive and negative side for our Z-axis (see the picture below, orange part). This would mean that data points with Z>0 are above origin, and with Z<0 are below origin. We tried several things, but our Z-axis origin always ends up at the most negative value of our dataset.
With great help of the community here, we've come to a minimal example showcasing what I want. The code I used is:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10, 10))
ax = fig.gca(projection='3d')
# Some settings
sn = 2 #limits in x,y,z
n = 50 #number of sample points
x1, x2 = 0, sn
y1, y2 = 0, sn
z1, z2 = -sn, sn
# Data for points
xs = (x2 - x1)*np.random.rand(n) + x1
ys = (y2 - y1)*np.random.rand(n) + y1
zs = (z2 - z1)*np.random.rand(n) + z1
# Points with z >= 0, plotted in green
ax.scatter(xs[zs>=0], ys[zs>=0], zs[zs>=0], color='green')
# Points with z < 0, plotted in red
ax.scatter(xs[zs<0], ys[zs<0], zs[zs<0], color='red')
# Data for plotting plane x|y|z=0 within the domain
tmp = np.linspace(0, sn, 8)
x, y = np.meshgrid(tmp, tmp)
z = 0*x
# Plot grid lines
ax.plot([0, sn], [0, 0], [0, 0], color='black')
ax.plot([0, 0], [0, sn], [0, 0], color='black')
ax.plot([0, 0], [0, 0], [-sn, sn], color='black')
# Maximum tick labels for X, Y, and Z (x3)
ax.plot([sn, sn], [0, 0], [-.05, .02], color='black')
ax.plot([0, 0], [sn, sn], [-.05, .02], color='black')
ax.plot([-.05, .02], [-.05, .02], [sn, sn], color='black')
ax.plot([-.05, .02], [-.05, .02], [-sn, -sn], color='black')
ax.plot([-.05, .02], [-.05, .02], [0, 0], color='black')
# Label texts
ax.text(sn/2, 0, -.2*sn, 'xlabel', 'x', ha='center')
ax.text(0, sn/2, -.2*sn, 'ylabel', 'y', ha='center')
ax.text(-.1*sn, 0, 0, 'zlabel', 'z', ha='center')
# Maximum limit text for X, Y and Z (x3)
ax.text(sn, 0, -.1*sn, f'{sn}', 'x', ha='center')
ax.text(0, sn, -.1*sn, f'{sn}', 'y', ha='center')
ax.text(-.05*sn, -.05*sn, 0, '0', 'x', ha='center')
ax.text(-.05*sn, -.05*sn, sn, f'{sn}', 'x', ha='right')
ax.text(-.05*sn, -.05*sn, -sn, f'{-sn}', 'x', ha='center')
# Set limits of the 3D display
ax.set_xlim3d([-sn, sn])
ax.set_ylim3d([-sn, sn])
ax.set_zlim3d([-sn, sn])
ax.set_axis_off()
plt.show()
This results in the graph below:
Although I am very happy with the outcome, this is still kind of 'hacky' solution with manually drawing the axis, ticks and labels. If anybody would have a solution in which we can re-design the axis from the mplot3d API that would be very helpful.
(Swatchai creates this as a community wiki):
Sometime, discussion without some runnable code to play/experiment with is not the best approach to get a solution. Here I propose this code to use for further discussion.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10, 10))
ax = fig.gca(projection='3d')
# Quivers for axes x,y,z from (0,0,0)
quiver1 = ax.quiver([0],[0],[0],[2],[0],[0], colors='r')
quiver2 = ax.quiver([0],[0],[0],[0],[2],[0], colors='g')
quiver3 = ax.quiver([0],[0],[0],[0],[0],[2], colors='b')
# Some settings
sn = 2 #limits in x,y,z
n = 50 #number of sample points
x1, x2 = -sn, sn
y1, y2 = -sn, sn
z1, z2 = -sn, sn
# Data for points
xs = (x2 - x1)*np.random.rand(n) + x1
ys = (y2 - y1)*np.random.rand(n) + y1
zs = (z2 - z1)*np.random.rand(n) + z1
# Points with z >= 0, plotted in green
ax.scatter(xs[zs>=0], ys[zs>=0], zs[zs>=0], color='green')
# Points with z < 0, plotted in red
ax.scatter(xs[zs<0], ys[zs<0], zs[zs<0], color='red')
# Data for plotting plane x|y|z=0 within the domain
tmp = np.linspace(0, sn, 8)
x,y = np.meshgrid(tmp,tmp)
z = 0*x
ax.plot_surface(z,x,y, alpha=0.15, color='red') # plot the plane x=0
ax.plot_surface(x,z,y, alpha=0.15, color='green') # plot the plane y=0
ax.plot_surface(x,y,z, alpha=0.15, color='blue') # plot the plane z=0
# Set limits of the 3D display
ax.set_xlim3d([-sn, sn])
ax.set_ylim3d([-sn, sn])
ax.set_zlim3d([-sn, sn])
# Set labels at the 3d box/frame
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
Output plot:
I am trying to combine two colourmap legends in one. Colour values are defined from third (z) data.
I am trying plot one legend colormap with two color scheme.
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.read_excel('C:\\Users\user1\\PycharmProjects\\untitled\\Python_test.xlsx')
x = df['Vp_dry']
y = df['Vs_dry']
q = df['Vp_wet']
w = df['Vs_wet']
fig, ax = plt.subplots()
popt, pcov = curve_fit(lambda fx, a, b: a * fx ** -b, x, y)
x_linspace = np.linspace(min(x - 100), max(x + 100), 100)
power_y = popt[0]*x_linspace ** -popt[1]
ax1 = plt.scatter(x, y, c=df['Porosity'], cmap=plt.cm.Greys, vmin=2, vmax=df['Porosity'].max(), edgecolors="#B6BBBD")
plt.plot(x_linspace, power_y, color='grey', label='Dry')
popt, pcov = curve_fit(lambda fx, a, b: a * fx ** -b, q, w)
q_linspace = np.linspace(min(q - 100), max(q + 100), 100)
power_w = popt[0]*q_linspace ** -popt[1]
ax2 = plt.scatter(q, w, c=df['Porosity'], cmap=plt.cm.Blues, vmin=2, vmax=df['Porosity'].max(), edgecolors="#3D83C1")
plt.plot(q_linspace, power_w, label='Wet')
cbar = fig.colorbar(ax2)
cbar = fig.colorbar(ax1)
cbar.set_label("Porosity (%)")
plt.xlabel('Vp (m/s)')
plt.ylabel('Vs (m/s)')
plt.grid()
plt.legend()
plt.show()
Desired result:
You seem to need a colorbar with two color maps combined, one of them reversed, and have the ticks changed to percentage values.
An approach is to manually create a second subplot, use two images and make it look like a colorbar:
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import numpy as np
# first create some dummy data to plot
N = 100
x = np.random.uniform(0, 10, N)
y = np.random.normal(15, 2, N)
q = np.random.uniform(0, 10, N)
w = np.random.normal(10, 2, N)
df_porosity = np.random.uniform(0, 5, N)
fig, (ax, ax2) = plt.subplots(ncols=2, figsize=(6, 4), gridspec_kw={"width_ratios": [1, 0.08]})
plot1 = ax.scatter(x, y, c=df_porosity, cmap=plt.cm.Greys, vmin=2, vmax=df_porosity.max(), edgecolors="#B6BBBD")
plot2 = ax.scatter(q, w, c=df_porosity, cmap=plt.cm.Blues, vmin=2, vmax=df_porosity.max(), edgecolors="#3D83C1")
img_cbar = np.linspace(0, 1, 256).reshape(256, 1)
ax2.imshow(img_cbar, cmap=plt.cm.Blues, extent=[0, 1, 1, 0]) # aspect='auto')
ax2.imshow(img_cbar, cmap=plt.cm.Greys, extent=[0, 1, -1, 0])
ax2.set_ylim(-1, 1)
ax2.set_aspect(10)
ax2.set_ylabel("Porosity (%)")
ax2.yaxis.set_label_position("right")
ax2.set_xticks([])
ax2.yaxis.tick_right()
# optionally show the ticks as percentage, where 1.0 corresponds to 100 %
ax2.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
plt.tight_layout()
plt.show()
I am scatter ploting data points with a very small marker (see screengrab below). When I use the very small marker ',' the legend is very hard to read (example code taken from here).
(Python 3, Jupyter lab)
How can I increase the size of the marker in the legend. The two versions shown on the above mentioned site do not work:
legend = ax.legend(frameon=True)
for legend_handle in legend.legendHandles:
legend_handle._legmarker.set_markersize(9)
and
ax.legend(markerscale=6)
The two solutions do however work when the marker is set to '.'.
How can I show bigger makers in the legend?
Sample Code from intoli.com:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, ',', label=f'Cluster {i + 1}')
ax.legend(markerscale=12)
fig.tight_layout()
plt.show()
You can get 1 pixel sized markers for a plot by setting the markersize to 1 pixel. This would look like
plt.plot(x, y, marker='s', markersize=72./fig.dpi, mec="None", ls="None")
What the above does is set the marker to a square, set the markersize to the ppi (points per inch) divided by dpi (dots per inch) == dots == pixels, and removes lines and edges.
Then the solution you tried using markerscale in the legend works nicely.
Complete example:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, marker='s', markersize=72./fig.dpi, mec="None", ls="None",
label=f'Cluster {i + 1}')
ax.legend(markerscale=12)
fig.tight_layout()
plt.show()
According to this discussion, the markersize has no effect when using pixels (,) as marker. How about generating a custom legend instead? For example, by adapting the first example in this tutorial, one can get a pretty decent legend:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, ',', label=f'Cluster {i + 1}')
##generating custom legend
handles, labels = ax.get_legend_handles_labels()
patches = []
for handle, label in zip(handles, labels):
patches.append(mpatches.Patch(color=handle.get_color(), label=label))
legend = ax.legend(handles=patches)
fig.tight_layout()
plt.show()
The output would look like this: