Custom art in NetworkX graph - plot

Is anyone aware of a way to put in an image (vector or raster) in place of a text label for a node or edge in a NetworkX graph visualization?
I prefer a solution that uses the matplotlib plot engine rather than the graphviz, but will take either solution.

In principle, the below should work. I transform the points into pixel coordinates, and then use figimage to put the image at that point.
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.image as image
im = image.imread('Lower_case_a.png')
G=nx.fast_gnp_random_graph(2,1)
pos = nx.spring_layout(G)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.patch.set_alpha(0)
ax.axis(xmin=-1,xmax=2,ymin=-1,ymax=2)
for node in G.nodes():
x,y = pos[node]
trans_x, trans_y = ax.transData.transform((x,y))
fig.figimage(im,trans_x,trans_y) #could use custom image for each node
nx.draw_networkx_edges(G,pos)
plt.savefig('tmp.png')
It almost works for me. I get:
I think that's because of some weird issue on my computer. When I run the code provided at matplotlib.org to explain transData.transform, it should give me, but instead I get
So I feel like the offset I'm seeing may be a problem on my machine and I'd like to try another computer. At any rate, let me know if this works for you and hopefully it at least points you in the right direction.

Related

Jupiter notebook is printing words before plotting my images (not correct order)

In my code, I have it so that first images are plotted and then the user is asked to choose an image.
But when I run the cell, the user is first asked to choose an image and then the images are plotted. Not sure why.
Code in this picture:
Jupyter will render the plot after everthing else if it is running in 'inline' mode. I can't reproduce what you have exactly but take the following example:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10,10)
y = 5*x +2
plt.plot(x,y)
print('Text is here first even though the plot call comes last')
The output of this looks like:
Plot example without inline
So the plot is rendered last. Instead if we add '%matplotlib notebook' to the start. The plot will be rendered interactively and before the text. If you want to switch back just change it to '%matplotlib inline'. There are a number of other backend plotting options that are worth exploring.

How to change light settings for a 3d plot in sage?

I try to plot a gyroid surface in sage which is defined implicitly. However, I get reflections of the light source on my surface which do not look very good. How can I change light settings for the 3d plots?
I tried to set different texture properties for my surface, but the changes showed no result. I also tried to set the light source but also without any resulting changes.
x, y, z = var('x,y,z')
def phi_g(x,y,z):
X=x+pi/2
Y=y+pi/2
Z=z+pi/2
return sin(X)*cos(Y)+sin(Y)*cos(Z)+sin(Z)*cos(X)
G1=implicit_plot3d(phi_g(x,y,z)==0.02, (x,0,2*pi), (y,0,2*pi), (z,0,2*pi),color='blue', frame=False, plot_points=80)
G2=implicit_plot3d(phi_g(x,y,z)==-0.02, (x,0,2*pi), (y,0,2*pi), (z,0,2*pi),color='red', frame=False, plot_points=80)
C=cube(center=(pi, pi, pi), size=2*pi, color='grey', opacity=0.1)
G=G1+G2+C
plot_G=G.plot()
plot_G.save('g.png',figsize=20,zoom=1.27)
I would like to have the output without any reflections of the light source on the surface.
I don't really use it much, but I think the Tachyon ray-tracer in Sage can do a lot of what you are looking for. I don't know if the implicit_plot3d fully supports these things in the default viewer.

Networkx; move edges in nx.MultiDiGraph plot

The question is very simple. However, neither official documentation or searching has helped much. I am looking to create something that (in a simple case) looks like this:
I am looking to plot a very simple directed graph, with one node, and two self-loops. This is what I got so far.
import networkx as nx
from nxpd import draw
G = nx.MultiDiGraph()
G.graph['dpi'] = 120
{'state': 'Low'}
G.add_nodes_from(range(1,2))
G.add_edges_from([(1,1),(1,1)])
pos=nx.get_node_attributes(G,'pos')
draw(G, show='ipynb')
Which produces
But, I would very much like that the other edge was on the left, and of equal size to the first one.
You are using Graphviz to draw the graph with the draw() command there. The attribute settings for Graphviz are documented at http://www.graphviz.org/content/attrs
In your case you want to use "headport" and "tailport" to set where the edges meet the node.
Try
import networkx as nx
G = nx.MultiDiGraph()
G.add_edge(1,1,headport='sw',tailport='nw')
G.add_edge(1,1,headport='se',tailport='ne')
nx.write_dot(G,'sl.dot')
# run dot -Tpng sl.dot >sl.png
The documentation

How to scale figure down to a particular size in inches in matplotlib

Suppose I prepare a PDF figure in matplotlib and let us say I have specified the original dimensions of the figure to be 10x10 inches. Would it be possible to produce essentially the same figure, but scaled down to 7x7in (so that all the fonts/point sizes, etc, would scale down appropriately)?
I do understand that I can open my 10x10 file in a vector graphics editor and perform the rescaling, but I was interested whether there is some simple switch that would do this directly from matplotlib.
Use set_size_inches, like so:
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)
fig.set_size_inches([10,10])
ax.plot([1,3,2],[2,2,2],'ro-')
plt.savefig('10x10.png')
fig.set_size_inches([4,4])
plt.savefig('4x4.png')

How can I hide the axes in matplotlib 3d?

How can I make a 3D plot without showing the axes?
When plotting a 3d plot, Matplotlib not only draws the x, y, and z axes, it draws light gray grids on the x-y, y-z, and x-z planes. I would like to draw a "free-floating" 3D plot, with none of these elements.
Stuff I've tried:
# Doesn't work; this hides the plot, not the axes
my_3d_axes.set_visible(False)
# Doesn't do anything. Also, there's no get_zaxis() function.
my_3d_axes.get_xaxis().set_visible(False)
my_3d_axes.get_yaxis().set_visible(False)
Ben Root provided a patch that fixes this for 1.0.1. It can be found as an attachment to the last email of this thread. To quote Ben:
Ok, looks like the hiding of the 3d axes was a feature added after the v1.0 release (but before I started working on mplot3d). This patch should enable the basic feature without interfering with existing functions. To hide the axes, you would have to set the private member "_axis3don" to False, like so:
ax = plt.gca(projection='3d')
ax._axis3don = False
If you do it this way, then you will get what you want now, and your code will still be compatible with mplot3d when you upgrade (although the preferred method would be to call set_axis_on() or set_axis_off()).
I hope that helps!
Ben Root
ax.set_axis_off()
Just to provide a concrete and direct example of what was mentioned at https://stackoverflow.com/a/7363931/895245
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import mpl_toolkits.mplot3d.art3d as art3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_axis_off()
# Draw a circle on the x=0 'wall'
p = Circle((0, 0), 1, fill=False)
ax.add_patch(p)
art3d.pathpatch_2d_to_3d(p, zdir="x")
p = Circle((0, 0), 1, fill=False)
ax.add_patch(p)
art3d.pathpatch_2d_to_3d(p, zdir="z")
ax.set_xlim(-1.2, 1.2)
ax.set_ylim(-1.2, 1.2)
ax.set_zlim(-1.2, 1.2)
plt.savefig('main.png', format='png', bbox_inches='tight')
Output:
Without ax.set_axis_off() it would look like:
You will notice however that this produces an excessively large whitespace margin around the figure as it simply hides the axes but does not change the viewbox. I tried bbox_inches='tight' and it did not help as it does in 2D. How to solve that at: Remove white spaces in Axes3d (matplotlib)
Tested on matplotlib==3.2.2.

Resources