Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structured labelled arrays #4217

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ after_success:
codecov;
fi
- if [[ "${DEPLOY_DOCS}" == 1 && $TRAVIS_PULL_REQUEST == false && $TRAVIS_BRANCH == "master" ]]; then
pip install ${PIP_FLAGS} -r requirements/doc.txt;
pip install ${PIP_FLAGS} -r requirements/example.txt;
source tools/travis/build_docs.sh;
source tools/travis/deploy_docs.sh;
Expand Down
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"../examples/graph",
"../examples/algorithms",
"../examples/advanced",
"../examples/convert",
"../examples/3d_drawing",
"../examples/pygraphviz",
"../examples/javascript",
Expand Down
2 changes: 2 additions & 0 deletions examples/convert/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Convert
--------
115 changes: 115 additions & 0 deletions examples/convert/plot_graph_tensors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""
=========================================================================
Generating Node Feature Matrices and Diffusion Matrix Tensors from Graphs
=========================================================================

In this example, we show how two things:

1. How to obtain a node dataframe from NetworkX graphs
using custom user-defined functions,
2. How to obtain an adjacency matrix/tensor from NetworkX graphs,
using custom user-defined functions.

A tensor is a generalization of vectors & matrices
to arbitrary numbers of dimensions.
By convention, vectors are 1D, matrices are 2D,
and tensors are ND.
In the Python world, we refer to all of these as nD-arrays
(i.e. n-dimensional arrays).

This allows you to generate structured dataframes and xarray dataarrays
in a highly customized fashion.
The resulting data containers are highly compatible
with the rest of the PyData ecosystem,
and can easily be converted into NumPy arrays/tensors
to be fed into deep learning models.

An overview of the steps involved looks like this:

1. Create your graph with data attributes
1. Define functions to extract data.
- For node DataFrames functions have signature:
f(node, datadict) -> pd.Series.
- For adjacency DataArray functions have signature: f(G) -> xr.DataArray.
Use ``format_adjacency()`` to ease creation of the DataArray.
1. Call the relevant generate function
(``generate_node_dataframe`` or ``generate_adjacency_xarray``)

"""
import networkx as nx
import string
from random import choice
import pandas as pd
import numpy as np
from functools import partial
import xarray as xr


def make_graph():
# Generate a graph
G = nx.erdos_renyi_graph(n=20, p=0.1)

# Add metadata to it:
# - some arbitrary letter chosen from the English alphabet
# - a random number chosen from the range(0,100)
for node in G.nodes():
G.nodes[node]["letter"] = choice(string.ascii_lowercase)
G.nodes[node]["number"] = choice(range(100))
return G


# %%
# Now, generate a node dataframe using a custom function.


def node_metadata(n, d):
return pd.Series(d, name=n)


# %%
# We'll define a function that also transforms number by multiplying it by 10


def ten_times_number(n, d):
return pd.Series({"10x_number": d["number"] * 10}, name=n)


# %%
# Now, generate node dataframe

G = make_graph()
funcs = [
node_metadata,
ten_times_number,
]
dataframe = nx.generate_node_dataframe(G, funcs)
assert set(dataframe.columns) == set(["letter", "number", "10x_number"])
assert set(dataframe.index) == set(G.nodes())

# %%
# We are now going to generate a graph's adjacency tensor.


def adjacency_power(G, n):
A = np.asarray(nx.adjacency_matrix(G).todense())
A = np.linalg.matrix_power(A, n)
return nx.format_adjacency(G, A, name=f"adjacency_{n}")


def laplacian(G):
A = np.asarray(nx.laplacian_matrix(G).todense())
return nx.format_adjacency(G, A, name="laplacian_matrix")


G = make_graph()
adj_funcs = [
partial(adjacency_power, n=1),
partial(adjacency_power, n=2),
partial(adjacency_power, n=3),
laplacian,
]


adjacency_tensor = nx.generate_adjacency_xarray(G, adj_funcs)
assert isinstance(adjacency_tensor, xr.DataArray)
assert set(adjacency_tensor.dims) == set(["n1", "n2", "name"])
2 changes: 1 addition & 1 deletion networkx/algorithms/tests/test_simple_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ def test_bidirectional_dijksta_restricted():
"s",
"v",
11,
*_bidirectional_dijkstra(XG, "s", "v", ignore_edges=[("s", "x")])
*_bidirectional_dijkstra(XG, "s", "v", ignore_edges=[("s", "x")]),
)
pytest.raises(
nx.NetworkXNoPath,
Expand Down
11 changes: 10 additions & 1 deletion networkx/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ def add_nx(doctest_namespace):
has_ogr = False


try:
import xarray

has_xarray = True
except ImportError:
has_xarray = False


# List of files that pytest should ignore

collect_ignore = []
Expand Down Expand Up @@ -178,11 +186,12 @@ def add_nx(doctest_namespace):
"utils/rcm.py",
]
needs_matplotlib = ["drawing/nx_pylab.py"]
needs_pandas = ["convert_matrix.py"]
needs_pandas = ["convert_matrix.py", "convert.py"]
needs_yaml = ["readwrite/nx_yaml.py"]
needs_pygraphviz = ["drawing/nx_agraph.py"]
needs_pydot = ["drawing/nx_pydot.py"]
needs_ogr = ["readwrite/nx_shp.py"]
needs_xarray = ["convert.py"]

if not has_numpy:
collect_ignore += needs_numpy
Expand Down
Loading