Changing the grid properties of insets in matplotlib - python-3.x

This is a follow up to my question posted here. A network diagram is added as an inset in matplotlib figure.
import networkx as nx
import matplotlib.pyplot as plt
G = nx.gnm_random_graph(n=10, m=15, seed=1)
nxpos = nx.spring_layout(G, dim=3, seed=1)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
nx_lines = [(nxpts[i], nxpts[j]) for i, j in G.edges()]
# node values
values = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[30, 80, 10, 79, 70, 60, 75, 78, 65, 10],
[1, .30, .10, .79, .70, .60, .75, .78, .65, .90]]
time = [0.0, 0.1, 0.2] # in seconds
fig, ax = plt.subplots()
ax.plot(
[1, 2, 3], [1, 2, 3],
'go-',
label='line 1',
linewidth=2
)
from mpl_toolkits.mplot3d import (Axes3D)
from matplotlib.transforms import Bbox
rect = [.6, 0, .5, .5]
bbox = Bbox.from_bounds(*rect)
inax = fig.add_axes(bbox, projection = '3d')
# inax.axis('off')
# set angle
angle = 25
inax.view_init(10, angle)
# hide axes, make transparent
# inax.set_facecolor('none')
inax.grid('off')
import numpy as np
# plot 3d
seen = set()
for i, j in G.edges():
x = np.stack((nxpos[i], nxpos[j]))
inax.plot(*x.T, color = 'k')
if i not in seen:
inax.scatter(*x[0], color = 'skyblue')
seen.add(i)
if j not in seen:
inax.scatter(*x[1], color = "skyblue")
seen.add(j)
fig.show()
I would like to change the grid properties i.e set the grid color to red and change line width. I tried inax.grid('on', color='r') but this doesn't change the color. Suggestions on how to change the settings will be really helpful.

You can do it like this:
inax.w_xaxis._axinfo.update({'grid' : {'color': 'red', 'linewidth': 0.8, 'linestyle': '-'}})
inax.w_yaxis._axinfo.update({'grid' : {'color': 'red', 'linewidth': 0.8, 'linestyle': '-'}})
inax.w_zaxis._axinfo.update({'grid' : {'color': 'red', 'linewidth': 0.8, 'linestyle': '-'}})
Output:

Related

Is there some way can add label in legend in plot by one step?

My legend now shows,
I want to add my label in legend, from 0 to 7, but I don't want to add a for-loop in my code and correct each label step by step, my code like that,
fig, ax = plt.subplots()
ax.set_title('Clusters by OPTICS in 2D space after PCA')
ax.set_xlabel('First Component')
ax.set_ylabel('Second Component')
points = ax.scatter(
pca_2_spec[:,0],
pca_2_spec[:,1],
s = 7,
marker='o',
c = pred_pca_2_spec,
cmap= 'rainbow')
ax.legend(*points.legend_elements(), title = 'cluster')
plt.show()
Assuming pred_pca_2_spec is some np.array with values [0, 5, 10, 15, 20, 30, 35] to change the values of these to be in the range 0-7, simply divide (each element) by 5.
Sample Data:
import numpy as np
from matplotlib import pyplot as plt
np.random.seed(54)
pca_2_spec = np.random.randint(-100, 300, (100, 2))
pred_pca_2_spec = np.random.choice([0, 5, 10, 15, 20, 25, 30, 35], 100)
Plotting Code:
fig, ax = plt.subplots()
ax.set_title('Clusters by OPTICS in 2D space after PCA')
ax.set_xlabel('First Component')
ax.set_ylabel('Second Component')
points = ax.scatter(
pca_2_spec[:, 0],
pca_2_spec[:, 1],
s=7,
marker='o',
c=pred_pca_2_spec / 5, # Divide By 5
cmap='rainbow')
ax.legend(*points.legend_elements(), title='cluster')
plt.show()

Adding image generated from another library as inset in matplotlib

I've generated a network figure using vedo library and I'm trying to add this as an inset to a figure generated in matplotlib
import networkx as nx
import matplotlib.pyplot as plt
from vedo import *
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
G = nx.gnm_random_graph(n=10, m=15, seed=1)
nxpos = nx.spring_layout(G, dim=3, seed=1)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
nx_lines = [(nxpts[i], nxpts[j]) for i, j in G.edges()]
pts = Points(nxpts, r=12)
edg = Lines(nx_lines).lw(2)
# node values
values = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[30, 80, 10, 79, 70, 60, 75, 78, 65, 10],
[1, .30, .10, .79, .70, .60, .75, .78, .65, .90]]
time = [0.0, 0.1, 0.2] # in seconds
vplt = Plotter(N=1)
pts1 = pts.cmap('Blues', values[0])
vplt.show(
pts1, edg,
axes=False,
bg='white',
at=0,
interactive=False,
zoom=1.5
).screenshot("network.png")
ax = plt.subplot(111)
ax.plot(
[1, 2, 3], [1, 2, 3],
'go-',
label='line 1',
linewidth=2
)
arr_img = vplt.screenshot(returnNumpy=True, scale=1)
im = OffsetImage(arr_img, zoom=0.25)
ab = AnnotationBbox(im, (1, 0), xycoords='axes fraction', box_alignment=(1.1, -0.1), frameon=False)
ax.add_artist(ab)
plt.show()
ax.figure.savefig(
"output.svg",
transparent=True,
dpi=600,
bbox_inches="tight"
)
There resolution of the image in the inset is too low. Suggestions on how to add the inset without loss of resolution will be really helpful.
EDIT:
The answer posted below works for adding a 2D network, but I am still looking for ways that will be useful for adding a 3D network in the inset.
I am not familiar with vedo but the general procedure would be to create an inset_axis and plot the image with imshow. However, your code is using networkx which has matplotlib bindings and you can directly do this without vedo
EDIT: code edited for 3d plotting
import networkx as nx
import matplotlib.pyplot as plt
G = nx.gnm_random_graph(n=10, m=15, seed=1)
nxpos = nx.spring_layout(G, dim=3, seed=1)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
nx_lines = [(nxpts[i], nxpts[j]) for i, j in G.edges()]
# node values
values = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[30, 80, 10, 79, 70, 60, 75, 78, 65, 10],
[1, .30, .10, .79, .70, .60, .75, .78, .65, .90]]
time = [0.0, 0.1, 0.2] # in seconds
fig, ax = plt.subplots()
ax.plot(
[1, 2, 3], [1, 2, 3],
'go-',
label='line 1',
linewidth=2
)
from mpl_toolkits.mplot3d import (Axes3D)
from matplotlib.transforms import Bbox
rect = [.6, 0, .5, .5]
bbox = Bbox.from_bounds(*rect)
inax = fig.add_axes(bbox, projection = '3d')
# inax = add_inset_axes(,
# ax_target = ax,
# fig = fig, projection = '3d')
# inax.axis('off')
# set angle
angle = 25
inax.view_init(10, angle)
# hide axes, make transparent
# inax.set_facecolor('none')
# inax.grid('off')
import numpy as np
# plot 3d
seen = set()
for i, j in G.edges():
x = np.stack((nxpos[i], nxpos[j]))
inax.plot(*x.T, color = 'k')
if i not in seen:
inax.scatter(*x[0], color = 'skyblue')
seen.add(i)
if j not in seen:
inax.scatter(*x[1], color = "skyblue")
seen.add(j)
fig.show()

How to improve cone - make the bases more like a circle?

I have a code for plotting a cone with some text and arrows like axes. How to change this code to get the bases of the cone looking like circles? Is the problem in the settings of margins? Or is it necessary to define circles in another way?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
FancyArrowPatch.draw(self, renderer)
nn = 400 # number of points along circle's perimeter
theta = np.linspace(0, 2*np.pi, nn)
rho = np.ones(nn)
# (x,y) represents points on circle's perimeter
x = np.ravel(rho*np.cos(theta))
y = np.ravel(rho*np.sin(theta))
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [5, 5]
figsize = (5, 5)
ax = plt.axes(projection='3d') # set the axes for 3D plot
ax.azim = -88 # y rotation (default=270)
ax.elev = 13 # x rotation (default=0)
# Low, high values of z for plotting 2 circles at different elevation
loz, hiz = -15, 15
# Plot two circles
ax.plot(x, y, hiz)
ax.plot(x, y, loz)
# Set some indices to get proper (x,y) for line plotting
lo1,hi1 = 15, 15+nn//2
lo2,hi2 = lo1+nn//2-27, hi1-nn//2-27
# Plot 3d lines using coordinates of selected points
ax.plot([x[lo1], x[hi1]], [y[lo1], y[hi1]], [loz, hiz])
ax.plot([x[lo2], x[hi2]], [y[lo2], y[hi2]], [loz, hiz])
eps = 0.005
ax.plot([0, 0], [0, 0], [0, 20]) # extend in z direction
ax.plot([0-eps, 0], [0-eps, -5], [0-eps, 0]) # extend in y direction
ax.plot([0, 1.3], [0, 0], [0, 0]) # extend in x direction
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, 16]) # v vector
ax.plot([0.63, 0.63], [-4, -4], [16, -0.005]) # vertical projection
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, -0.005]) # to the beginning
ax.scatter(0, 0, 20, marker=(3, 0, 0), s=100, clip_on=False)
ax.scatter(0, -5, 0, marker=(3, 0, 43), s=100, clip_on=False)
ax.scatter(1.3, 0, 0, marker=(3, 0, 30), s=100, clip_on=False)
ax.scatter(0.6, -4, 16, marker=(3, 0, 80), s=100, clip_on=False)
ax.scatter(0.6, -4, -0.005, marker=(3, 0, 0), s=100, clip_on=False)
a2 = Arrow3D([0.14, -0.515], [-5.581, 1.358], [14.73, 4.983], mutation_scale=20, arrowstyle="-|>", color="k", connectionstyle="arc3,rad=0.3")
ax.add_artist(a2)
ax.text3D(0.23, -5.23, 23.33, r'$A$')
ax.text3D(1.41, 1.29, -2.7, r'$B$')
ax.text3D(-0.31, 1.46, -12.6, r'$C$')
ax.text3D(0.4, -5.48, 17, r'$D$')
ax.text3D(0.64, 1.57, -9.95, r'$E$')
ax.text3D(-0.2, -5.5, 15.73, r'$F$')
# Hide axes
ax._axis3don = False
# Save the figure (.pdf)
margins = { # vvv margin in inches
"left" : 1 / figsize[0],
"bottom" : -2.45 / figsize[1],
"right" : 1 - 0.5 / figsize[0],
"top" : 1 + 1.8 / figsize[1]
}
fig.subplots_adjust(**margins)
plt.savefig('output.pdf')
plt.show()
From this code I got the following output:
The desired output is bases looking like a circle.
This is the view from above:
It is not a circle but an ellipse.
Desired shape of the cone:

How to draw simple 3d axis in python3?

I would like to have names of axes as in the figure.
This could be a good starter. Try experiment with it.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig = plt.figure(figsize=[8,8])
ax = fig.gca(projection = '3d')
# some settings
vleng = 4
aleng = vleng/3.
p = np.array([vleng, 0, 0])
q = np.array([0, vleng, 0])
r = np.array([0, 0, vleng])
ax.plot(*np.vstack([[0,0,0], p]).T, color='b')
ax.plot(*np.vstack([[0,0,0], q]).T, color='g')
ax.plot(*np.vstack([[0,0,0], r]).T, color='r')
# plotting arrow at terminal of the lines
ax.quiver(vleng, 0, 0, aleng, 0, 0, \
length=0.5, arrow_length_ratio=0.5, color='r')
ax.quiver(0, vleng, 0, 0, aleng, 0, \
length=0.5, arrow_length_ratio=0.5, color='m')
ax.quiver(0, 0, vleng, 0, 0, aleng, \
length=0.5, arrow_length_ratio=0.5, color='k')
ax.text3D(vleng+1.5, 0, 0, 'X')
ax.text3D(0, vleng+1.0, 0, 'y')
ax.text3D(0, 0, vleng+1.0, 'z')
ax.azim = 35 # y rotation (default=270)
ax.elev = 20 # x rotation (default=0)
ax.dist = 15 # zoom (define perspective)
ax.set_axis_off( ) # hide all grid
ax.set_aspect('equal')
# plot poly1
ax.plot3D( [3.5, 0.25, 2, 3.5], [1, 0.25, 2.5, 1], [1.9, 3.2, 3.8, 1.9], label = 'one line', color='pink' )
# projection of poly1 on xy-plane
ax.plot3D( [3.5, 0.25, 2, 3.5], [1, 0.25, 2.5, 1], [0, 0, 0, 0], label = 'one line', color='gray' )
#ax.legend()
plt.show()

How to draw a triangle using matplotlib.pyplot based on 3 dots (x,y) in 2D?

I would like to draw a triangle using python3 module matplotlib.
import numpy as np
import matplotlib.pyplot as plt
X_train = np.array([[1,1], [2,2.5], [3, 1], [8, 7.5], [7, 9], [9, 9]])
Y_train = ['red', 'red', 'red', 'blue', 'blue', 'blue']
plt.figure()
plt.scatter(X_train[:, 0], X_train[:, 1], s = 170, color = Y_train[:])
plt.show()
It displays 6 dots but they are grouped separately in 2 places. (color helps to see it clearly)
There are 2 sets of 3 dots. I want each set(3 dots) be united in the triangle.
How is it possible to implement this? How to build a triangle based on 3 dots using matplotlib?
Any suggestions are appreciated ;)
A triangle is a polygon. You may use plt.Polygon to draw a polygon.
import numpy as np
import matplotlib.pyplot as plt
X = np.array([[1,1], [2,2.5], [3, 1], [8, 7.5], [7, 9], [9, 9]])
Y = ['red', 'red', 'red', 'blue', 'blue', 'blue']
plt.figure()
plt.scatter(X[:, 0], X[:, 1], s = 170, color = Y[:])
t1 = plt.Polygon(X[:3,:], color=Y[0])
plt.gca().add_patch(t1)
t2 = plt.Polygon(X[3:6,:], color=Y[3])
plt.gca().add_patch(t2)
plt.show()

Resources