How to set random_state in networkx graph with holoviews/bokeh? - python

I would like to generate reproducible plots. With networkx is possible to pass the random state to the layout. That is to ensure the plot is the same. When doing the same with holoviews I am getting an error.
%pylab inline
import pandas as pd
import networkx as nx
import holoviews as hv
# generating the graph
G = nx.Graph()
ndxs = [1, 2, 3, 4]
G.add_nodes_from(ndxs)
G.add_weighted_edges_from([(1,2,0), (1,3,1), (1,4,-1),
(2,4,1), (2,3,-1), (3,4,10)])
# drawing with networkx
nx.draw(G, nx.spring_layout(G, random_state=100))
# drawing with holoviews/bokeh
hv.extension('bokeh')
%opts Graph [width=400 height=400]
layout = nx.layout.spring_layout(G, random_state=100)
hv.Graph.from_networkx(G, layout)
>>> TypeError: 'dict' object is not callable

The first issue is that the Graph.from_networkx method accepts the layout function not the dictionary that is output by that function. If you want to pass arguments to the function you can do so as keyword argument, e.g.:
hv.Graph.from_networkx(G, nx.layout.spring_layout, random_state=42)
In my version of networkx random_state is not an accepted argument to the layout functions in which case you can set the seed directly with NumPy:
np.random.seed(42)
hv.Graph.from_networkx(G, nx.layout.spring_layout)

Related

Unable to write waxman graph using graphml in Networkx

I am trying to generate the Waxman random graph using the built-in function which works fine and generates the Waxman graph.
Later in my code, I am writing the above-generated graph using graphml as:
nx.write_graphml(my_graph_waxman, "waxman_Iteration.graphml")
This writing function gives the error:
networkx.exception.NetworkXError: GraphML writer does not support <class 'tuple'> as data values.
My complete code is here:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
my_graph_waxman=nx.waxman_graph(6, beta=0.8, alpha=0.6, L=None, domain=(0, 0, 1, 1), metric=None, seed=None)
nx.draw(my_graph_waxman, with_labels=True, font_weight='normal')
plt.axis('on')
plt.show()
nx.write_graphml(my_graph_waxman, "waxman_Iteration.graphml")

How can i set custom background colors of seaborn heatmap fields? (matplotlib collections.QuadMesh facecolors, Python)

I want to set the background color of some fields of a seaborn heatmap AFTER it has been created (customising some colours based on their position).
I am able to access the facecolor values via the matplotlib collections.QuadMesh object. When I modify the values (I have tried different ways) the changes get reflected, but the facecolors shown when displaying or exporting the plot stay unchanged.
What am I missing here?
This is a (non-)working example:
import numpy as np
import numpy.testing
import pandas as pd
import seaborn as sns
from matplotlib.collections import QuadMesh
import matplotlib.pyplot as plt
CUSTOM_BACKGROUND = np.array([0.35, 0.8, 0.55, 1.0])
df = pd.DataFrame(data={'col1': [1, 1], 'col2': [2,2]})
matrix_size = df.shape[0]
ax = sns.heatmap(df)
quadmesh = ax.findobj(QuadMesh)[0]
facecolors_old = quadmesh.get_facecolors()
facecolors_new = facecolors_old.copy()
facecolors_new[1] = CUSTOM_BACKGROUND
# Not working:
quadmesh.set_facecolors(facecolors_new)
# quadmesh._original_facecolor = facecolors_new
# facecolors_old[1] = CUSTOM_BACKGROUND
# quadmesh._facecolors[1] = CUSTOM_BACKGROUND
# works:
# quadmesh.set_facecolors('none')
# tests pass:
facecolors_actual = quadmesh.get_facecolors()
numpy.testing.assert_array_equal(facecolors_new, facecolors_actual)
facecolors_actual = quadmesh._facecolors
numpy.testing.assert_array_equal(facecolors_new, facecolors_actual)
# NOTE: inspecting `quadmesh._facecolors` however shows the old values!
# shows old colors
plt.show()
Dependency versions:
[tool.poetry.dependencies]
python = "^3.8"
pandas = "^1.2"
seaborn = "^0.11.2"
Remark:
This approach used to work for me before updating the dependencies. I have adapted some code from here for drawing custom confusion matrixes using the same approach: pretty-print-confusion-matrix

Plot distribution of node attributes networkx

The nodes in a directed graph has Name, Age and Height as attributes. I want to plot the distribution of the three attributes, is that possible?
I know that it is possible to get attributes this way:
name = nx.get_node_attributes(G, "Name")
age = nx.get_node_attributes(G, "Age")
height = nx.get_node_attributes(G, "Height")
But I don't really get how I can use those instead of G in function below?
import networkx as nx
def plot_degree_dist(G):
degrees = [G.degree(n) for n in G.nodes()]
plt.hist(degrees)
plt.show()
plot_degree_dist(nx.gnp_random_graph(100, 0.5, directed=True))
Or is there some better way to do plot the distribution of node attributes?
Seems like a perfectly reasonable way to me. I'm not aware of any more convenient method. To be more generalizable, add an argument to your function that takes the name of the attribute you'd like to plot.
Just know that nx.get_node_attributes() returns a dictionary keyed to the nodes. Since we're just plotting the distribution, we're only interested in the values and not the keys.
Here's a self-contained example following your lead:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
def plot_attribute_dist(G, attribute):
attribute = nx.get_node_attributes(G, attribute).values()
plt.hist(attribute)
plt.show()
attribute_name = 'Name'
G = nx.gnp_random_graph(100, 0.5, directed=True)
rng = np.random.default_rng(seed=42)
for node, data in G.nodes(data=True):
data[attribute_name] = rng.normal()
plot_attribute_dist(G, attribute_name)
which outputs

Changing colormap for categorical data in Holoviews/Datashader

I'm trying to visualize categorical spatial data using Datashader and Holoviews, similarly to https://anaconda.org/jbednar/census-hv-dask/notebook. However, when I try to assign different colors to categories, I always end up with same (presumably default) colors (An example of the output image.)
Here is the code I'm running in Jupyter notebook. Could anyone advise me on how to make the custom color map work? Or at least run the code to see if you end up with colors matching the legend or not. Thank you!
from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot
import pandas as pd
import holoviews as hv
import geoviews as gv
import datashader as ds
from cartopy import crs
from matplotlib.cm import get_cmap
from holoviews.operation.datashader import datashade, aggregate
hv.notebook_extension('bokeh', width=95)
# Generating blob data:
X, y = make_blobs(n_samples=5000000, centers=5, n_features=2)
df = pd.DataFrame(dict(x=X[:,0], y=X[:,1], label=y))
# Plotting the blobs using datashader and holoviews:
%opts Overlay [width=800 height=455 xaxis=None yaxis=None show_grid=False]
%opts Shape (fill_color=None line_width=1.5) [apply_ranges=False]
%opts Points [apply_ranges=False] WMTS (alpha=0.5) NdOverlay [tools=['tap']]
color_key = {0:'red', 1:'blue', 2:'green', 3:'yellow', 4:'black'}
labels = {0:'red', 1:'blue', 2:'green', 3:'yellow', 4:'black'}
color_points = hv.NdOverlay({labels[k]: gv.Points([0,0], crs=crs.PlateCarree(),
label=labels[k])(style=dict(color=v))
for k, v in color_key.items()})
dataset = gv.Dataset(df, kdims=['x', 'y'], vdims=['label'])
shaded = datashade(hv.Points(dataset), cmap=color_key, aggregator=ds.count_cat('label'))
shaded * color_points
That code doesn't seem to be runnable (races is not defined, and gv is not imported), but in any case, categorical colors are determined by the color_key argument, not cmap, so you'd need to change cmap=color_key to color_key=color_key.

Simplify networkx node labels

%matplotlib inline
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
G.add_node('abc#gmail.com')
nx.draw(G, with_labels=True)
plt.show()
The output figure is
What I want is
I have thousands of email records from person#email.com to another#email.com in a csv file, I use G.add_node(email_address) and G.add_edge(from, to) to build G. I want keep the whole email address in Graph G but display it in a simplified string.
networkx has a method called relabel_nodes that takes a graph (G), a mapping (the relabeling rules) and returns a new graph (new_G) with the nodes relabeled.
That said, in your case:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
G.add_node('abc#gmail.com')
mapping = {
'abc#gmail.com': 'abc'
}
relabeled_G = nx.relabel_nodes(G,mapping)
nx.draw(relabeled_G, with_labels=True)
plt.show()
That way you keep G intact and haves simplified labels.
You can optionally modify the labels in place, without having a new copy, in which case you'd simply call G = nx.relabel_nodes(G, mapping, copy=False)
If you don't know the email addresses beforehand, you can pass relabel_nodes a function, like so:
G = nx.relabel_nodes(G, lambda email: email.split("#")[0], copy=False)

Categories

Resources