Manually changing node position is networkx draw - matplotlib

I tried manually changing the positions of a graph in networkx before drawing, but it seems to have no effect.
Here's an mwe, with this I would expect the two figures to be different, more precisely, for the second be wider horizontally and thus have more space between nodes, but even the node labels seem to share the same relative size:
import matplotlib.pyplot as plt
import networkx as nx
import pydot
from networkx.drawing.nx_pydot import graphviz_layout
T = nx.balanced_tree(2, 5)
T = nx.relabel_nodes(T, lambda x: str(x))
pos = graphviz_layout(T, prog="dot")
plt.figure()
nx.draw(T, pos, with_labels=True)
plt.savefig("1.png")
pos = {key: (val[0] * pow(10, 5), val[1]) for key, val in pos.items()}
plt.figure()
nx.draw(T, pos, with_labels=True)
plt.savefig("2.png")
I guess this is mostly a matplotlib issue, but what is going on here?

The x positions are changing, it's just that since they are scaled with a factor of 10000 from their original positions, the difference is not noticeable unless you show the axis and the ticks.
See code below:
import matplotlib.pyplot as plt
import networkx as nx
import pydot
from networkx.drawing.nx_pydot import graphviz_layout
import numpy as np
fig=plt.figure(figsize=(15,15))
plt.subplot(211)
T = nx.balanced_tree(2, 5)
T = nx.relabel_nodes(T, lambda x: str(x))
pos = graphviz_layout(T, prog="dot")
nx.draw(T, pos, with_labels=True)
plt.axis('on')
plt.tick_params(left=True, bottom=True, labelleft=True, labelbottom=True)
plt.subplot(212)
pos = {key: (val[0]*pow(10,5), val[1]) for key, val in pos.items()}
nx.draw(T, pos, with_labels=True)
plt.axis('on')
plt.tick_params(left=True, bottom=True, labelleft=True, labelbottom=True)

Related

Drawing a community in networkx, anything I am doing incorrectly?

Trying to do something like this but I am not sure what I am doing incorrectly
import networkx as nx
import matplotlib.pyplot as plt
import networkx.algorithms.community as nxcom
G = nx.karate_club_graph()
greedy = nxcom.greedy_modularity_communities(G)
#returns a list with type frozen sets within the list
#[{set1},{set2},{set3}]
pos = nx.spring_layout(G) # compute graph layout
plt.axis('off')
nx.draw_networkx_nodes(G, pos, cmap=plt.cm.RdYlBu, node_color=list(greedy.values()))
plt.show(G)
It looks like your issue comes from the way you are mapping colors to your communities. Since the node_color argument from nx.draw_networkx_nodes is expected to be a list of color (see doc here), you will need to associate each one of your nodes with the color of its community. You can do that by using:
c=plt.cm.RdYlBu(np.linspace(0,1,len(greedy))) #create a list of colors, one for each community
colors={list(g)[j]:c[i] for i,g in enumerate(greedy) for j in range(len(list(g)))} #associate each node with the color of its community
colors_sort=dict(sorted(colors.items())) #sort the dictionary by keys such
You can then convert the values of your sorted dictionnary into a list and pass it to the nx.draw_networkx_nodes with nx.draw_networkx_nodes(G, pos,node_color=list(colors_sort.values())).
See full code below:
import networkx as nx
import matplotlib.pyplot as plt
import networkx.algorithms.community as nxcom
import numpy as np
G = nx.karate_club_graph()
greedy = nxcom.greedy_modularity_communities(G)
c=plt.cm.RdYlBu(np.linspace(0,1,len(greedy)))
colors={list(g)[j]:c[i] for i,g in enumerate(greedy) for j in range(len(list(g)))}
colors_sort=dict(sorted(colors.items()))
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos,node_color=list(colors_sort.values()))
nx.draw_networkx_edges(G, pos)
nx.draw_networkx_labels(G, pos,labels={n:str(n) for n in G.nodes()})
plt.axis('off')
plt.show(G)

How to fix the location of nodes in networkx?

How can I fix the location of nodes in networkx? Each frame the cells move because the edges added were different. I'd like to keep all the cells in the same location.
(If you run the code snippet below on https://colab.research.google.com/ the resulting mp4 file shows how the nodes are moving around. This makes its hard to track the changes over each frame.)
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
import seaborn.apionly as sns
import matplotlib.animation
import random
G = nx.grid_2d_graph(9, 9, )
pos = {f"{x},{y}":(x,y) for x,y in G.nodes()}
ndxs = [f"{x},{y}" for x,y in G.nodes()]
G=nx.DiGraph(directed=True)
G.add_nodes_from(ndxs)
edges = [
[(f"{random.randint(0, 8)},{random.randint(0, 8)}", f"{random.randint(0, 8)},{random.randint(0, 8)}") for _ in range(10)]
for _ in range(20)
]
# Build plot
fig, ax = plt.subplots(figsize=(8,8))
def update(i):
ax.clear()
edgelist = edges[i]
nx.draw(G, pos, node_color="lightgrey", ax=ax)
nx.draw_networkx_edges(
G, pos=pos, edgelist=edgelist,
arrowstyle="->", connectionstyle=f"arc3,rad=0.5", ax=ax)
ax.set_title(f"frame {i}")
ax.set_xticks([])
ax.set_yticks([])
ani = matplotlib.animation.FuncAnimation(fig, update, frames=20, interval=250, repeat=True)
ani.save("tmp.mp4")
plt.show()

In Matplotlib, adding `trantsform` breaks rectangles [duplicate]

I wanted to rotate a Rectangle in matplotlib but when I apply the transformation, the rectangle doesn't show anymore:
rect = mpl.patches.Rectangle((0.0120,0),0.1,1000)
t = mpl.transforms.Affine2D().rotate_deg(45)
rect.set_transform(t)
is this a known bug or do I make a mistake?
The patch in the provided code makes it hard to tell what's going on, so I've made a clear demonstration that I worked out from a matplotlib example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
fig = plt.figure()
ax = fig.add_subplot(111)
r1 = patches.Rectangle((0,0), 20, 40, color="blue", alpha=0.50)
r2 = patches.Rectangle((0,0), 20, 40, color="red", alpha=0.50)
t2 = mpl.transforms.Affine2D().rotate_deg(-45) + ax.transData
r2.set_transform(t2)
ax.add_patch(r1)
ax.add_patch(r2)
plt.xlim(-20, 60)
plt.ylim(-20, 60)
plt.grid(True)
plt.show()
Apparently the transforms on patches are composites of several transforms for dealing with scaling and the bounding box. Adding the transform to the existing plot transform seems to give something more like what you'd expect. Though it looks like there's still an offset to work out.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
fig = plt.figure()
ax = fig.add_subplot(111)
rect = patches.Rectangle((0.0120,0),0.1,1000)
t_start = ax.transData
t = mpl.transforms.Affine2D().rotate_deg(-45)
t_end = t_start + t
rect.set_transform(t_end)
print repr(t_start)
print repr(t_end)
ax.add_patch(rect)
plt.show()

Plotting a rasterio raster on a Cartopy GeoAxes

I've seen a few other questions on this topic, but the library has changed enough that the answers to those no longer seem to apply.
Rasterio used to include an example for plotting a rasterio raster on a Cartopy GeoAxes. The example went roughly like this:
import matplotlib.pyplot as plt
import rasterio
from rasterio import plot
import cartopy
import cartopy.crs as ccrs
world = rasterio.open(r"../tests/data/world.rgb.tif")
fig = plt.figure(figsize=(20, 12))
ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
ax.set_global()
plot.show(world, origin='upper', transform=ccrs.PlateCarree(), interpolation=None, ax=ax)
ax.coastlines()
ax.add_feature(cartopy.feature.BORDERS)
However, this code no longer draws the raster. Instead, I get something like this:
It should look like this:
When I asked about this in the rasterio issues tracker, they told me the example was deprecated (and deleted the example). Still, I wonder if there's some way to do what I'm trying to do. Can anyone point me in the right direction?
I think you may want to read the data to a numpy.ndarray and plot it using ax.imshow, where ax is your cartopy.GeoAxes (as you have it already). I offer an example of what I mean, below.
I clipped a small chunk of Landsat surface temperature and some agricultural fields for this example. Get them on this drive link.
Note fields are in WGS 84 (epsg 4326), Landsat image is in UTM Zone 12 (epsg 32612), and I want my map in Lambert Conformal Conic. Cartopy makes this easy.
import numpy as np
import cartopy.crs as ccrs
from cartopy.io.shapereader import Reader
from cartopy.feature import ShapelyFeature
import rasterio
import matplotlib.pyplot as plt
def cartopy_example(raster, shapefile):
with rasterio.open(raster, 'r') as src:
raster_crs = src.crs
left, bottom, right, top = src.bounds
landsat = src.read()[0, :, :]
landsat = np.ma.masked_where(landsat <= 0,
landsat,
copy=True)
landsat = (landsat - np.min(landsat)) / (np.max(landsat) - np.min(landsat))
proj = ccrs.LambertConformal(central_latitude=40,
central_longitude=-110)
fig = plt.figure(figsize=(20, 16))
ax = plt.axes(projection=proj)
ax.set_extent([-110.8, -110.4, 45.3, 45.6], crs=ccrs.PlateCarree())
shape_feature = ShapelyFeature(Reader(shapefile).geometries(),
ccrs.PlateCarree(), edgecolor='blue')
ax.add_feature(shape_feature, facecolor='none')
ax.imshow(landsat, transform=ccrs.UTM(raster_crs['zone']),
cmap='inferno',
extent=(left, right, bottom, top))
plt.savefig('surface_temp.png')
feature_source = 'fields.shp'
raster_source = 'surface_temperature_32612.tif'
cartopy_example(raster_source, feature_source)
The trick with Cartopy is to remember to use the projection keyword for your axes object, as this renders the map in a nice projection of your choice (LCC in my case). Use transform keyword to indicate what projection system your data is in, so Cartopy knows how to render it.
No need of rasterio. Get a bluemarble image, then plot it.
Here is the working code:
import cartopy
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig = plt.figure(figsize=(10, 5))
ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
# source of the image:
# https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73909/world.topo.bathy.200412.3x5400x2700.jpg
fname = "./world.topo.bathy.200412.3x5400x2700.jpg"
img_origin = 'lower'
img = plt.imread(fname)
img = img[::-1]
ax.imshow(img, origin=img_origin, transform=ccrs.PlateCarree(), extent=[-180, 180, -90, 90])
ax.coastlines()
ax.add_feature(cartopy.feature.BORDERS)
ax.set_global()
plt.show()
The output plot:

Using perceptually uniform colormaps in Mayavi volumetric visualization

AFAIK Mayavi does not come with any perceptually uniform colormaps. I tried naively to just pass it one of Matplotlib's colormaps but it failed:
from mayavi import mlab
import multiprocessing
import matplotlib.pyplot as plt
plasma = plt.get_cmap('plasma')
...
mlab.pipeline.volume(..., colormap=plasma)
TraitError: Cannot set the undefined 'colormap' attribute of a 'VolumeFactory' object.
Edit: I found a guide to convert Matplotlib colormaps to Mayavi colormaps. However, it unfortunately doesn't work since I am trying to use a volume using a perceptually uniform colormap.
from matplotlib.cm import get_cmap
import numpy as np
from mayavi import mlab
values = np.linspace(0., 1., 256)
lut_dict = {}
lut_dict['plasma'] = get_cmap('plasma')(values.copy())
x, y, z = np.ogrid[-10:10:20j, -10:10:20j, -10:10:20j]
s = np.sin(x*y*z)/(x*y*z)
mlab.pipeline.volume(mlab.pipeline.scalar_field(s), vmin=0, vmax=0.8, colormap=lut_dict['plasma']) # still getting the same error
mlab.axes()
mlab.show()
...
Instead of setting it as the colormap argument, if you set it as the ColorTransferFunction of the volume, it works as expected.
import numpy as np
from mayavi import mlab
from tvtk.util import ctf
from matplotlib.pyplot import cm
values = np.linspace(0., 1., 256)
x, y, z = np.ogrid[-10:10:20j, -10:10:20j, -10:10:20j]
s = np.sin(x*y*z)/(x*y*z)
volume = mlab.pipeline.volume(mlab.pipeline.scalar_field(s), vmin=0, vmax=0.8)
# save the existing colormap
c = ctf.save_ctfs(volume._volume_property)
# change it with the colors of the new colormap
# in this case 'plasma'
c['rgb']=cm.get_cmap('plasma')(values.copy())
# load the color transfer function to the volume
ctf.load_ctfs(c, volume._volume_property)
# signal for update
volume.update_ctf = True
mlab.show()
While the previous answer by like444 helped me partially with a similar problem, it leads to incorrect translation between colormaps. This is because the format in which matplotlib and tvtk store color information is slightly different: Matplotlib uses RGBA, while ColorTransferFunction uses VRGB, where V is the value in the shown data that this part of the colormap is assigned to. So by doing a 1-to-1 copy, green becomes red, blue becomes green and alpha becomes blue. The following code snippet fixes that:
def cmap_to_ctf(cmap_name):
values = list(np.linspace(0, 1, 256))
cmap = cm.get_cmap(cmap_name)(values)
transfer_function = ctf.ColorTransferFunction()
for i, v in enumerate(values):
transfer_function.add_rgb_point(v, cmap[i, 0], cmap[i, 1], cmap[i, 2])
return transfer_function