~tfardet: 1 Doc+Plot: updated doc and gallery plots 18 files changed, 195 insertions(+), 168 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~tfardet/nngt-developers/patches/25710/mbox | git am -3Learn more about email & git
From: Tanguy Fardet <tanguyfardet@protonmail.com> * Improve documentation plots and colors * Improve marker size and shape support * Correct igraph documentation links * Correct geospatial map initialization * Switch NaturalEarth download to new location * Include all doc examples into tests --- .build.yml | 8 +-- doc/conf.py | 8 +-- .../graph_properties/plot_attributes.py | 2 +- .../graph_properties/plot_betweenness.py | 13 ++-- doc/examples/graph_properties/plot_degrees.py | 3 +- .../graph_structure/plot_hive_panel.py | 18 +++--- doc/examples/graph_structure/plot_layouts.py | 30 ++++----- doc/examples/graph_structure/plot_map.py | 7 ++- doc/examples/groups_and_metagroups.py | 33 +++++----- doc/nngt_theme/static/nngt_theme.css | 61 +++++++++++-------- nngt/analysis/clustering.py | 26 +++++--- nngt/geospatial/_cartopy_ne.py | 33 +++------- nngt/geospatial/countries.py | 6 +- nngt/plot/plt_networks.py | 12 ++-- nngt/plot/plt_properties.py | 37 +++++------ setup.py | 9 ++- testing/__init__.py | 9 +-- testing/test_examples.py | 48 +++++++++++---- 18 files changed, 195 insertions(+), 168 deletions(-) diff --git a/.build.yml b/.build.yml index f557b27..f631aaf 100644 --- a/.build.yml +++ b/.build.yml @@ -15,12 +15,12 @@ tasks: sudo apt-key adv --keyserver keys.openpgp.org --recv-key 612DEFB798507F25 sudo add-apt-repository -y ppa:nest-simulator/nest sudo apt-get update -qq - sudo apt install -y gcc libcairo2-dev pkg-config build-essential autoconf automake python3-dev libblas-dev + sudo apt install -y gcc libcairo2-dev pkg-config build-essential autoconf automake python3-dev libblas-dev libgeos-dev proj-bin libproj-dev sudo apt install -y nest liblapack-dev libatlas-base-dev gfortran libxml2-dev openmpi-bin libopenmpi-dev libgmp-dev - sudo apt install -y python3-pip python3-tk libigraph0v5 libigraph0-dev python3-graph-tool + sudo apt install -y python3-pip python3-tk libigraph0v5 libigraph0-dev python3-graph-tool python3-cairo python3-cairocffi pip3 install numpy scipy cython mpi4py pip3 install networkx python-igraph - pip3 install pycairo matplotlib seaborn shapely svg.path dxfgrabber + pip3 install matplotlib seaborn shapely svg.path dxfgrabber 'cartopy<0.20' geopandas descartes pip3 install pytest pytest-mpi cov-core coverage coveralls[yaml] cd NNGT python3 setup.py install --user @@ -32,7 +32,7 @@ tasks: GL=nx coverage run -p -m pytest testing GL=ig coverage run -p -m pytest testing GL=nngt coverage run -p -m pytest testing - GL=gt OMP=2 coverage run -p -m pytest testing + GL=gt OMP=2 coverage run -p -m pytest -s testing GL=gt OMP=0 MPI=1 mpirun -n 2 coverage run -p -m pytest --with-mpi testing coverage combine GIT_BRANCH=$(git show -s --pretty=%D HEAD | tr -s ', /' '\n' | grep -v HEAD | sed -n 2p) diff --git a/doc/conf.py b/doc/conf.py index d7f6b38..a2e1edf 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -558,8 +558,8 @@ intersphinx_mapping = { 'gt': ('https://graph-tool.skewed.de/static/doc/', None), 'ipython': ('https://ipython.org/ipython-doc/stable/', None), 'matplotlib': ('https://matplotlib.org/', None), - 'networkx': ('https://networkx.github.io/documentation/stable/', None), - 'numpy': ('https://docs.scipy.org/doc/numpy', None), + 'networkx': ('https://networkx.org/documentation/stable/', None), + 'numpy': ('https://numpy.org/doc/stable/', None), 'python': ('https://docs.python.org/3/', None), 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), 'shapely': ('https://shapely.readthedocs.io/en/latest/', None), @@ -569,8 +569,8 @@ extlinks_fancy = { 'doi': (['https://dx.doi.org/{0}'], ['DOI: {0}']), 'arxiv': (['https://arxiv.org/abs/{0}'], ['arXiv: {0}']), 'gtdoc': (['https://graph-tool.skewed.de/static/doc/{0}.html#graph_tool.{1}'], ['graph-tool - {0}']), - 'igdoc': (['https://igraph.org/python/doc/igraph.GraphBase-class.html#{0}'], ['igraph - {0}']), - 'nxdoc': (['https://networkx.github.io/documentation/stable/reference/{0}generated/networkx.{1}.html'], ['networkx - {0}']) + 'igdoc': (['https://igraph.org/python/api/latest/igraph._igraph.GraphBase.html#{0}'], ['igraph - {0}']), + 'nxdoc': (['https://networkx.org/documentation/stable/reference/{0}generated/networkx.{1}.html'], ['networkx - {0}']) } # sphinx gallery parameters diff --git a/doc/examples/graph_properties/plot_attributes.py b/doc/examples/graph_properties/plot_attributes.py index d20c69b..f0851bb 100644 --- a/doc/examples/graph_properties/plot_attributes.py +++ b/doc/examples/graph_properties/plot_attributes.py @@ -31,7 +31,7 @@ import matplotlib.pyplot as plt plt.rcParams.update({ 'axes.edgecolor': 'grey', 'xtick.color': 'grey', 'ytick.color': 'grey', "figure.facecolor": (0, 0, 0, 0), "axes.facecolor": (0, 0, 0, 0), - "axes.labelcolor": "grey", "axes.titlecolor": "grey", "text.color": "grey" + "axes.labelcolor": "grey", "text.color": "grey" }) diff --git a/doc/examples/graph_properties/plot_betweenness.py b/doc/examples/graph_properties/plot_betweenness.py index 42822fa..a4a4c70 100644 --- a/doc/examples/graph_properties/plot_betweenness.py +++ b/doc/examples/graph_properties/plot_betweenness.py @@ -31,8 +31,7 @@ import matplotlib.pyplot as plt plt.rcParams.update({ 'axes.edgecolor': 'grey', 'xtick.color': 'grey', 'ytick.color': 'grey', "figure.facecolor": (0, 0, 0, 0), "axes.facecolor": (0, 0, 0, 0), - "axes.labelcolor": "grey", "axes.titlecolor": "grey", "text.color": "grey", - "legend.facecolor": "none" + "axes.labelcolor": "grey", "text.color": "grey", "legend.facecolor": "none" }) @@ -50,18 +49,18 @@ g = nngt.generation.distance_rule(5, shape=shape, nodes=1000, avg_deg=3) # %% # then we can plot the betweenness -nplt.betweenness_distribution(g, logx=True, show=True, - legend_location='left') +nplt.betweenness_distribution(g, logx=True, show=True, legend_location='left') # %% # we can of course change various parameters and plot only the nodes nplt.betweenness_distribution(g, logx=False, show=True) -nplt.betweenness_distribution(g, btype="node", num_nbins="auto", - alpha=0.5, show=True) +nplt.betweenness_distribution(g, btype="node", num_nbins="auto", alpha=0.5, + show=True) # %% # By the way, this is the graph we're looking at -nplt.draw_network(g, max_nsize=1, show_environment=False, show=True) +nplt.draw_network(g, max_nsize=5, max_esize=4, ecolor="grey", eborder_color="w", + curved_edges=True, show_environment=False, show=True) diff --git a/doc/examples/graph_properties/plot_degrees.py b/doc/examples/graph_properties/plot_degrees.py index c2ee531..7f48e8b 100644 --- a/doc/examples/graph_properties/plot_degrees.py +++ b/doc/examples/graph_properties/plot_degrees.py @@ -30,8 +30,7 @@ import matplotlib.pyplot as plt plt.rcParams.update({ 'axes.edgecolor': 'grey', 'xtick.color': 'grey', 'ytick.color': 'grey', "figure.facecolor": (0, 0, 0, 0), "axes.facecolor": (0, 0, 0, 0), - "axes.labelcolor": "grey", "axes.titlecolor": "grey", "text.color": "grey", - "legend.facecolor": "none" + "axes.labelcolor": "grey", "text.color": "grey", "legend.facecolor": "none" }) diff --git a/doc/examples/graph_structure/plot_hive_panel.py b/doc/examples/graph_structure/plot_hive_panel.py index d62a11c..25a94b9 100644 --- a/doc/examples/graph_structure/plot_hive_panel.py +++ b/doc/examples/graph_structure/plot_hive_panel.py @@ -22,7 +22,8 @@ Hive plot panel =============== """ -import os +import inspect +from os.path import abspath, dirname import matplotlib.pyplot as plt @@ -30,13 +31,12 @@ import nngt plt.rcParams.update({ - "figure.facecolor": (0, 0, 0, 0), - "axes.titlecolor": "grey", "text.color": "grey" + "figure.facecolor": (0, 0, 0, 0), "text.color": "grey" }) -dirpath = os.path.abspath(os.getcwd()) -rootpath = os.path.abspath(dirpath + "/../../..") +dirpath = dirname(inspect.getframeinfo(inspect.currentframe()).filename) +rootpath = abspath(dirpath + "/../../..") # load graph @@ -82,10 +82,7 @@ for i in range(len(todo)): ax = axes[i, j] if i == 0: - ax.set_title(ax_name) - - if j == 0: - ax.set_ylabel(radial) + ax.set_title(ax_name + " (groups)") size = todo[list(set([0, 1, 2]).difference([i, j]))[0]] @@ -94,7 +91,8 @@ for i in range(len(todo)): axes_bins=ax_bins, axes_units="native", axis=ax, show_names=False) for i in range(len(todo)): - fig.text(0.03, 0.8 - i*0.33, todo[i], rotation=90, fontsize="large") + fig.text(0.03, 0.83 - i*0.33, todo[i] + " (radius)", rotation=90, + fontsize="large", va="center") plt.tight_layout() diff --git a/doc/examples/graph_structure/plot_layouts.py b/doc/examples/graph_structure/plot_layouts.py index 0d84d02..07cbb45 100644 --- a/doc/examples/graph_structure/plot_layouts.py +++ b/doc/examples/graph_structure/plot_layouts.py @@ -32,7 +32,7 @@ import nngt plt.rcParams.update({ "figure.facecolor": (0, 0, 0, 0), - "axes.labelcolor": "grey", "axes.titlecolor": "grey", "text.color": "grey" + "axes.labelcolor": "grey", "text.color": "grey" }) @@ -43,7 +43,6 @@ nngt.seed(0) mpl_backend = mpl.get_backend() if nngt.get_config("backend") in ("graph-tool", "igraph"): - if mpl_backend.startswith("Qt4"): if mpl_backend != "Qt4Cairo": plt.switch_backend("Qt4Cairo") @@ -53,13 +52,18 @@ if nngt.get_config("backend") in ("graph-tool", "igraph"): elif mpl_backend.startswith("GTK"): if mpl_backend != "GTK3Cairo": plt.switch_backend("GTK3Cairo") - elif mpl_backend != "cairo": + else: plt.switch_backend("cairo") # prepare figure and parameters -_, axes = plt.subplots(2, 2, figsize=(10, 8)) +fig = plt.figure(figsize=(10, 8), constrained_layout=False) + +gs = fig.add_gridspec(nrows=2, ncols=2, left=0, right=1, bottom=0, top=0.97, + wspace=0, hspace=0.05) + +axes = [fig.add_subplot(gs[i, j]) for i in (0, 1) for j in (0, 1)] num_nodes = 50 @@ -84,10 +88,10 @@ nngt.generation.connect_groups(g, (room1, room2), struct, "erdos_renyi", nngt.generation.connect_groups(g, room3, room1, "erdos_renyi", avg_deg=5) -nngt.plot.library_draw(g, tight=False, axis=axes[0, 0], ecolor="grey", +nngt.plot.library_draw(g, tight=False, axis=axes[0], ecolor="grey", show=False) -axes[0, 0].set_title("Spring-block layout") +axes[0].set_title("Spring-block layout") # random layout @@ -96,19 +100,19 @@ sw = nngt.generation.watts_strogatz(4, 0.3, nodes=num_nodes) betw = nngt.analysis.betweenness(sw, "node") -nngt.plot.draw_network(sw, nsize=betw, ncolor="out-degree", axis=axes[0, 1], +nngt.plot.draw_network(sw, nsize=betw, ncolor="out-degree", axis=axes[1], ecolor="lightgrey", tight=False, show=False) -axes[0, 1].set_title("Random layout") +axes[1].set_title("Random layout") # circular layout for small-world networks nngt.plot.draw_network(sw, nsize=betw, ncolor="out-degree", layout="circular", - ecolor="lightgrey", axis=axes[1, 0], + ecolor="lightgrey", axis=axes[2], show=False, tight=False) -axes[1, 0].set_title("Circular layout") +axes[2].set_title("Circular layout") # spatial layout @@ -126,13 +130,11 @@ g = nngt.generation.distance_rule(10, shape=shape, nodes=num_nodes, avg_deg=5, cc = nngt.analysis.local_clustering(g) -nngt.plot.draw_network(g, ncolor=cc, axis=axes[1, 1], ecolor="grey", show=False, +nngt.plot.draw_network(g, ncolor=cc, axis=axes[3], ecolor="grey", show=False, eborder_width=0.5, eborder_color="w", esize=10, max_nsize=max_nsize, tight=False) -axes[1, 1].set_title("Spatial layout") - -plt.tight_layout() +axes[3].set_title("Spatial layout") # save figure diff --git a/doc/examples/graph_structure/plot_map.py b/doc/examples/graph_structure/plot_map.py index f5a2ac2..6b4da61 100644 --- a/doc/examples/graph_structure/plot_map.py +++ b/doc/examples/graph_structure/plot_map.py @@ -35,7 +35,7 @@ import nngt.geospatial as ng plt.rcParams.update({ 'axes.edgecolor': 'grey', 'xtick.color': 'grey', 'ytick.color': 'grey', "figure.facecolor": (0, 0, 0, 0), "axes.facecolor": (0, 0, 0, 0), - "axes.labelcolor": "grey", "axes.titlecolor": "grey", "text.color": "grey" + "axes.labelcolor": "grey", "text.color": "grey" }) @@ -62,5 +62,6 @@ g.set_weights(nngt._rng.exponential(2, g.edge_nb())) ng.draw_map(g, "code", ncolor="in-degree", esize="weight", threshold=0, ecolor="grey", proj=ccrs.EqualEarth(), max_nsize=20, show=False) -plt.tight_layout() -plt.show() +if nngt.get_config("with_plot"): + plt.tight_layout() + plt.show() diff --git a/doc/examples/groups_and_metagroups.py b/doc/examples/groups_and_metagroups.py index cc6e251..59ad0e4 100644 --- a/doc/examples/groups_and_metagroups.py +++ b/doc/examples/groups_and_metagroups.py @@ -90,30 +90,33 @@ print(pop["left"]) Plot the graph ''' -if nngt.get_config("with_plot"): - import matplotlib.pyplot as plt +plt = None - # we plot the graph, setting the node shape from the left and right groups - # and the color from the neuronal type (exc. and inhib.) +# we plot the graph, setting the node shape from the left and right groups +# and the color from the neuronal type (exc. and inhib.) - nngt.plot.draw_network(net, nshape=[left, right], show_environment=False) +nngt.plot.draw_network(net, nshape=[left, right], nsize=20, + show_environment=False) +if nngt.get_config("with_plot"): + import matplotlib.pyplot as plt plt.show() - # further tests to make sure every configuration works +# further tests to make sure every configuration works - nngt.plot.draw_network(net, nshape=[left, right], show_environment=False, - simple_nodes=True) +nngt.plot.draw_network(net, nshape=[left, right], show_environment=False, + max_nsize=20, simple_nodes=True) - nngt.plot.draw_network(net, nshape=["o" for _ in range(net.node_nb())], - show_environment=False, simple_nodes=True) +nngt.plot.draw_network(net, nshape=["o" for _ in range(net.node_nb())], + show_environment=False, simple_nodes=True) - nngt.plot.draw_network(net, nshape=["o" for _ in range(net.node_nb())], - show_environment=False) +nngt.plot.draw_network(net, nshape=["o" for _ in range(net.node_nb())], + show_environment=False) - nngt.plot.draw_network(net, nshape="s", show_environment=False, - simple_nodes=True) +nngt.plot.draw_network(net, nshape="s", show_environment=False, + simple_nodes=True) - nngt.plot.draw_network(net, nshape="s", show_environment=False) +nngt.plot.draw_network(net, nshape="s", show_environment=False) +if nngt.get_config("with_plot"): plt.show() diff --git a/doc/nngt_theme/static/nngt_theme.css b/doc/nngt_theme/static/nngt_theme.css index 771cc8b..60ce9f4 100755 --- a/doc/nngt_theme/static/nngt_theme.css +++ b/doc/nngt_theme/static/nngt_theme.css @@ -7,10 +7,10 @@ --hv-color: #eeeeee; --sb-color: #f7f5fa; --dt-color: #f3f3fb; - --img-invert: 0; --alert-info: #5bc0de; --admonition-a: #555555; --a-color: #008cba; + --highlighted: #fbe54e; } @media (prefers-color-scheme: dark) { @@ -21,10 +21,37 @@ --hv-color: #333333; --sb-color: #3f3f3f; --dt-color: #111111; - --img-invert: 1; --alert-info: #1e7bb3; - --admonition-a: #cccccc; + --admonition-a: #bbbbbb; --a-color: #2ec5f6; + --highlighted: #80731e; + } + + /* math */ + + img.math { + -webkit-filter: invert(100%); + filter: invert(100%); + } + + div.math img { + -webkit-filter: invert(100%); + filter: invert(100%); + } + + div.highlight { + -webkit-filter: invert(100%) hue-rotate(180deg); + filter: invert(100%) hue-rotate(180deg); + } + + code { + -webkit-filter: invert(100%) hue-rotate(180deg); + filter: invert(100%) hue-rotate(180deg); + } + + code span.pre { + -webkit-filter: hue-rotate(180deg); + filter: hue-rotate(180deg); } } @@ -36,7 +63,6 @@ --hv-color: #eeeeee; --sb-color: #f7f5fa; --dt-color: #f3f3fb; - --img-invert: 0; --alert-info: #5bc0de; --admonition-a: #555555; --a-color: #008cba; @@ -227,18 +253,12 @@ a.fn-backref { color: var(--fg-color); } -.alert-info { - background-color: var(--alert-info) !important; -} - -div.highlight { - -webkit-filter: invert(var(--img-invert)); - filter: invert(var(--img-invert)); +dt:target, span.highlighted { + background-color: var(--highlighted); } -code { - -webkit-filter: invert(var(--img-invert)); - filter: invert(var(--img-invert)); +.alert-info { + background-color: var(--alert-info) !important; } .form-control { @@ -280,16 +300,3 @@ div.sphx-glr-download a { div.sphx-glr-download a:hover { background-image: linear-gradient(to top, var(--sb-color), var(--sb-color)) !important; } - - -/* math */ - -img.math { - -webkit-filter: invert(var(--img-invert)); - filter: invert(var(--img-invert)); -} - -div.math img { - -webkit-filter: invert(var(--img-invert)); - filter: invert(var(--img-invert)); -} diff --git a/nngt/analysis/clustering.py b/nngt/analysis/clustering.py index 65e6925..72d242d 100644 --- a/nngt/analysis/clustering.py +++ b/nngt/analysis/clustering.py @@ -82,8 +82,8 @@ def global_clustering(g, directed=True, weights=None, method="continuous", otherwise uses any valid edge attribute required. method : str, optional (default: 'continuous') Method used to compute the weighted clustering, either 'barrat' - [Barrat2004]_, 'continuous', 'onnela' [Onnela2005]_, or 'zhang' - [Zhang2005]_. + [Barrat2004]_, 'continuous' [Fardet2021]_, 'onnela' [Onnela2005]_, or + 'zhang' [Zhang2005]_. mode : str, optional (default: "total") Type of clustering to use for directed graphs, among "total", "fan-in", "fan-out", "middleman", and "cycle" [Fagiolo2007]_. @@ -119,6 +119,9 @@ def global_clustering(g, directed=True, weights=None, method="continuous", and Molecular Biology 2005, 4 (1). :doi:`10.2202/1544-6115.1128`, `PDF <https://dibernardo.tigem.it/files/papers/2008/ zhangbin-statappsgeneticsmolbio.pdf>`_. + .. [Fardet2021] Fardet, Levina. Weighted directed clustering: + interpretations and requirements for heterogeneous, inferred, and + measured networks. 2021. :arxiv:`2105.06318`. See also -------- @@ -183,6 +186,8 @@ def local_closure(g, directed=True, weights=None, method=None, * "fan-in" is given by the pattern [(k, j), (j, i), (k, i)], * "fan-out" is given by the pattern [(i, j), (j, k), (i, k)]. + See [Fardet2021]_ for more details. + Parameters ---------- g : :class:`~nngt.Graph` @@ -219,6 +224,9 @@ def local_closure(g, directed=True, weights=None, method=None, International Conference on Web Search and Data Mining 2019, 303-311. :doi:`10.1145/3289600.3290991`, `PDF <https://www.cs.cornell.edu/~arb/ papers/closure-coefficients-WSDM-2019.pdf>`_. + .. [Fardet2021] Fardet, Levina. Weighted directed clustering: + interpretations and requirements for heterogeneous, inferred, and + measured networks. 2021. :arxiv:`2105.06318`. ''' directed *= g.is_directed() weighted = weights not in (False, None) @@ -345,8 +353,8 @@ def local_clustering(g, nodes=None, directed=True, weights=None, normalized to dimensionless values between 0 and 1 through a division by the highest weight. - The default `method` for weighted networks is based on a modification of - the proposal in [Zhang2005]_ with: + The default `method` for weighted networks is the continuous definition + [Fardet2021]_ and is defined as: .. math:: @@ -384,7 +392,8 @@ def local_clustering(g, nodes=None, directed=True, weights=None, * equivalence between no-edge and zero-weight edge cases, * normalized (always between zero and 1). - Using either 'continuous' or 'zhang' is recommended for weighted graphs. + Using either 'continuous' or 'zhang' is usually recommended for weighted + graphs, see the discussion in [Fardet2021]_ for details. Parameters ---------- @@ -400,8 +409,8 @@ def local_clustering(g, nodes=None, directed=True, weights=None, otherwise uses any valid edge attribute required. method : str, optional (default: 'continuous') Method used to compute the weighted clustering, either 'barrat' - [Barrat2004]_/[Clemente2018]_, 'continuous', 'onnela' [Onnela2005]_/ - [Fagiolo2007]_, or 'zhang' [Zhang2005]_. + [Barrat2004]_/[Clemente2018]_, 'continuous' [Fardet2021]_, 'onnela' + [Onnela2005]_/[Fagiolo2007]_, or 'zhang' [Zhang2005]_. mode : str, optional (default: "total") Type of clustering to use for directed graphs, among "total", "fan-in", "fan-out", "middleman", and "cycle" [Fagiolo2007]_. @@ -445,6 +454,9 @@ def local_clustering(g, nodes=None, directed=True, weights=None, and Molecular Biology 2005, 4 (1). :doi:`10.2202/1544-6115.1128`, `PDF <https://dibernardo.tigem.it/files/papers/2008/ zhangbin-statappsgeneticsmolbio.pdf>`_. + .. [Fardet2021] Fardet, Levina. Weighted directed clustering: + interpretations and requirements for heterogeneous, inferred, and + measured networks. 2021. :arxiv:`2105.06318`. See also -------- diff --git a/nngt/geospatial/_cartopy_ne.py b/nngt/geospatial/_cartopy_ne.py index 983db03..228f483 100644 --- a/nngt/geospatial/_cartopy_ne.py +++ b/nngt/geospatial/_cartopy_ne.py @@ -50,10 +50,7 @@ class NEShpDownloader(Downloader): # Define the NaturalEarth URL template. The natural earth website # returns a 302 status if accessing directly, so we use the naciscdn # URL directly. - _NE_URL_TEMPLATE = ('https://naciscdn.org/naturalearth/{resolution}' - '/{category}/ne_{resolution}_{name}.zip') - - _NE_URL_BACKUP = ('https://naturalearth.s3.amazonaws.com/{resolution}' + _NE_URL_TEMPLATE = ('https://naturalearth.s3.amazonaws.com/{resolution}' '_{category}/ne_{resolution}_{name}.zip') def __init__(self, url_template=_NE_URL_TEMPLATE, @@ -102,8 +99,8 @@ class NEShpDownloader(Downloader): return target_path - @classmethod - def default_downloader(cls, backup=False): + @staticmethod + def default_downloader(): ''' Return a generic, standard, NEShpDownloader instance. @@ -118,20 +115,14 @@ ne_{resolution}_{name}.shp ''' default_spec = ('shapefiles', 'natural_earth', '{category}', 'ne_{resolution}_{name}.shp') - ne_path_template = os.path.join('{config[data_dir]}', *default_spec) - pre_path_template = os.path.join('{config[pre_existing_data_dir]}', *default_spec) - - url_template = cls._NE_URL_BACKUP if backup else cls._NE_URL_TEMPLATE - - return NEShpDownloader(url_template=url_template, - target_path_template=ne_path_template, + return NEShpDownloader(target_path_template=ne_path_template, pre_downloaded_path_template=pre_path_template) - -# add a generic Natural Earth shapefile downloader to the cartopy config + +# add a generic Natural Earth shapefile downloader to the cartopy config # dictionary's 'downloaders' section. _ne_key = ('shapefiles', 'natural_earth') cartopy.config['downloaders'].setdefault(_ne_key, @@ -161,15 +152,7 @@ def natural_earth(resolution='110m', category='physical', name='coastline'): # get hold of the Downloader (typically a NEShpDownloader instance) # which we can then simply call its path method to get the appropriate # shapefile (it will download if necessary) + ne_downloader = NEShpDownloader.default_downloader() format_dict = {'config': cartopy.config, 'category': category, - 'name': name, 'resolution': resolution} - - try: - ne_downloader = NEShpDownloader.default_downloader() - return ne_downloader.path(format_dict) - except: - ne_downloader = NEShpDownloader.default_downloader(backup=True) - + 'name': name, 'resolution': resolution} return ne_downloader.path(format_dict) - - diff --git a/nngt/geospatial/countries.py b/nngt/geospatial/countries.py index 6d85ddb..13ff5f4 100644 --- a/nngt/geospatial/countries.py +++ b/nngt/geospatial/countries.py @@ -286,8 +286,7 @@ for _, v in world.iterrows(): continue elif len(idx) > 1: pop = cities.iloc[idx].pop_max - idx = idx[np.argmax(pop)] - points.append(cities.iloc[idx].geometry) + points.append(cities.iloc[pop.idxmax()].geometry) continue else: idx = np.where((cities.sov_a3 == v.SU_A3)*cities.adm0cap)[0] @@ -297,8 +296,7 @@ for _, v in world.iterrows(): continue elif len(idx) > 1: pop = cities.iloc[idx].pop_max - idx = idx[np.argmax(pop)] - points.append(cities.iloc[idx].geometry) + points.append(cities.iloc[pop.idxmax()].geometry) continue points.append(v.geometry.representative_point()) diff --git a/nngt/plot/plt_networks.py b/nngt/plot/plt_networks.py index 4adefed..9b8915e 100755 --- a/nngt/plot/plt_networks.py +++ b/nngt/plot/plt_networks.py @@ -204,6 +204,8 @@ def draw_network(network, nsize="total-degree", ncolor=None, nshape="o", else: fig = axis.get_figure() + fig.patch.set_visible(False) + # projections for geographic plots proj = kwargs.get("proj", None) @@ -476,9 +478,9 @@ def draw_network(network, nsize="total-degree", ncolor=None, nshape="o", for i in ids: scatter.append(axis.scatter( - pos[i, 0], pos[i, 1], color=c[i], ms=0.5*nsize[i], - marker=nshape[i], zorder=2, mec=nborder_color[i], - mew=nborder_width, alpha=nalpha)) + pos[i, 0], pos[i, 1], color=c[i], s=0.5*nsize[i], + marker=nshape[i], zorder=2, edgecolors=nborder_color[i], + linewidths=nborder_width, alpha=nalpha)) else: scatter.append(axis.scatter( pos[:, 0], pos[:, 1], color=c, s=0.5*np.array(nsize), @@ -1642,8 +1644,8 @@ def _node_edge_shape_size(network, nshape, nsize, max_nsize, min_nsize, esize, shapes[ids] = m markers = list(shapes) - if len(nshape) == network.node_nb() and restrict_nodes is not None: - nshape = nshape[list(restrict_nodes)] + elif len(nshape) == network.node_nb() and restrict_nodes is not None: + markers = nshape[list(restrict_nodes)] elif len(nshape) != n: raise ValueError("When passing an array of markers to " "`nshape`, one entry per node in the " diff --git a/nngt/plot/plt_properties.py b/nngt/plot/plt_properties.py index 775658f..c294fcc 100755 --- a/nngt/plot/plt_properties.py +++ b/nngt/plot/plt_properties.py @@ -317,18 +317,18 @@ def betweenness_distribution( if axes is None: fig, ax1 = plt.subplots() fig.patch.set_visible(False) - ax1.grid(False, axis='y') - axes = [ax1.twinx()] + axes = [ax1] if num_axes == 2: ax2 = ax1.twiny() axes.append(ax2) - ax1.grid(False, axis='x') - ax1.yaxis.tick_right() - ax1.yaxis.set_label_position("right") + ax2.grid(False) + ax2.yaxis.set_visible(True) + ax2.yaxis.set_ticks_position("right") + ax2.yaxis.set_label_position("right") else: - ax1.set_yticks([]) + ax2 = ax1 else: ax1 = axes[0] @@ -374,19 +374,21 @@ def betweenness_distribution( _set_scale(ax1, nbins, np.min(ncounts[ncounts>0]), ncounts.max(), logx, logy) + if btype in ("edge", "both"): ax2.bar( ebins[:-1], ecounts, np.diff(ebins), color=colors[-1], align='edge', **kwargs) ax2.set_xlim([ebins.min(), ebins.max()]) - ax1.set_ylim([0, 1.1*ecounts.max()]) + ax2.set_ylim([0, 1.1*ecounts.max()]) ax2.set_xlabel("Edge betweenness") - ax1.set_ylabel("Edge count") + ax2.set_ylabel("Edge count") ax2.ticklabel_format(axis='x', style='sci', scilimits=(-3, 2)) _set_scale(ax2, ebins, np.min(ecounts[ecounts>0]), ecounts.max(), logx, logy) + if btype == "both": ax2.legend( ["Edge betweenness"], bbox_to_anchor=[x, 0.88], @@ -1007,41 +1009,42 @@ def _set_new_plot(fignum=None, num_new_plots=1, names=None, sharex=None): import matplotlib.pyplot as plt # get the figure and compute the new number of rows and cols fig = plt.figure(num=fignum) + num_axes = len(fig.axes) + num_new_plots + if names is not None: num_axes = len(fig.axes) + len(names) num_new_plots = len(names) + num_cols = max(int(np.ceil(np.sqrt(num_axes))), 1) ratio = num_axes/float(num_cols) num_rows = int(ratio) + if int(ratio) != int(np.ceil(ratio)): num_rows += 1 + # change the geometry gs = fig.add_gridspec(num_rows, num_cols) + for i in range(num_axes - num_new_plots): y = i // num_cols x = i - num_cols*y fig.axes[i].set_subplotspec(gs[y, x]) + lst_new_axes = [] n_old = num_axes-num_new_plots+1 + for i in range(num_new_plots): if fig.axes: lst_new_axes.append( fig.add_subplot(num_rows, num_cols, n_old+i, sharex=sharex)) else: lst_new_axes.append(fig.add_subplot(num_rows, num_cols, n_old+i)) + if names is not None: lst_new_axes[-1].name = names[i] - return fig, lst_new_axes - -def _log_format(y, pos): - ''' - Needed to move log values by one, so first increment, then decrement - ''' - # rounding err for 4 so add 0.4 to avoid it - #~ return '{}'.format(int(np.e*np.e**(y-1) + 0.4)) if y > -1 else 0 - return y + return fig, lst_new_axes def _set_scale(ax1, xbins, mincounts, maxcounts, logx, logy): diff --git a/setup.py b/setup.py index b4dcf50..73ffd17 100755 --- a/setup.py +++ b/setup.py @@ -159,7 +159,7 @@ setup_params = dict( ]}, # Requirements - install_requires = ['numpy>=1.17', 'scipy>=0.11'], + install_requires = ['numpy>=1.17', 'scipy>=0.11', 'cython'], python_requires = '>=3.5, <4', extras_require = { 'matplotlib': 'matplotlib', @@ -167,9 +167,8 @@ setup_params = dict( 'ig': ['python-igraph'], 'geometry': ['matplotlib', 'shapely', 'dxfgrabber', 'svg.path'], 'geospatial': ['matplotlib', 'geopandas', 'descartes', 'cartopy'], - 'full': ['cython', 'networkx>=2.4', 'shapely', 'dxfgrabber', - 'svg.path', 'matplotlib', 'geopandas', 'descartes', 'cartopy', - 'lxml'] + 'full': ['networkx>=2.4', 'shapely', 'dxfgrabber', 'svg.path', + 'matplotlib', 'geopandas', 'descartes', 'cartopy', 'lxml'] }, # Cython module @@ -181,7 +180,7 @@ setup_params = dict( author_email = 'tanguy.fardet@tuebingen.mpg.de', license = 'GPL3', keywords = 'network graph structure simulation neuron NEST DeNSE topology ' - 'growth', + 'growth igraph graph-tool networkx geospatial', long_description = long_descr, classifiers = [ 'Development Status :: 5 - Production/Stable', diff --git a/testing/__init__.py b/testing/__init__.py index f35ff9d..fd249d6 100644 --- a/testing/__init__.py +++ b/testing/__init__.py @@ -26,15 +26,12 @@ Testing module ============== This module tests the various functionalities of NNGT to make sure that all -implementations remain compatible with the graph libraries and versions 2.7 and -3.x of python. +implementations remain compatible with the graph libraries and versions 3.X of +python. note :: When adding new tests, filename should be of the form `test_xxx.py` and the - code should contain: - * a ``TestXXX`` class, - * a ``suite = unittest.TestLoader().loadTestsFromTestCase(TestXXX)`` - declaration. + code should contain a list of functions called `test_yyy` """ # std imports diff --git a/testing/test_examples.py b/testing/test_examples.py index b609299..fc497cb 100644 --- a/testing/test_examples.py +++ b/testing/test_examples.py @@ -12,7 +12,7 @@ Check that the examples work. import os from os import environ -from os.path import dirname, abspath, isfile, join +from os.path import dirname, abspath, isfile, isdir, join import unittest import pytest @@ -20,15 +20,32 @@ from scipy.special import lambertw import nngt + +''' Set state, paths, and global variables ''' + +with_plot, with_nest = None, None + + +def setup_module(): + ''' setup any state specific to the execution of the current module.''' + with_plot = nngt.get_config("with_plot") + with_nest = nngt.get_config("with_nest") + + nngt.set_config("with_plot", False) + nngt.set_config("with_nest", False) + + +def teardown_module(): + ''' teardown any state that was previously setup with setup_module. ''' + nngt.set_config("with_plot", with_plot) + nngt.set_config("with_nest", with_nest) + + # set example dir current_dir = dirname(abspath(__file__)) idx_testing = current_dir.find('testing') example_dir = current_dir[:idx_testing] + 'doc/examples/' -# remove plotting and NEST -nngt.set_config("with_plot", False) -nngt.set_config("with_nest", False) - # set globals glob = {"lambertw": lambertw} @@ -39,15 +56,22 @@ glob = {"lambertw": lambertw} @pytest.mark.mpi_skip class TestExamples(unittest.TestCase): - + ''' Class testing saving and loading functions. ''' - - example_files = [ - example_dir + f for f in os.listdir(example_dir) - if isfile(join(example_dir, f)) - ] + + example_files = [] + + for f in os.listdir(example_dir): + joint = join(example_dir, f) + if joint.endswith(".py"): + example_files.append(joint) + elif isdir(joint): + for f in os.listdir(joint): + newjoint = join(joint, f) + if newjoint.endswith(".py"): + example_files.append(newjoint) @classmethod def tearDownClass(cls): @@ -55,7 +79,7 @@ class TestExamples(unittest.TestCase): os.remove("sp_graph.el") except: pass - + @property def test_name(self): return "test_examples" -- 2.32.0
builds.sr.ht <builds@sr.ht>NNGT/patches/.build.yml: FAILED in 25m53s [Doc+Plot: updated doc and gallery plots][0] from [~tfardet][1] [0]: https://lists.sr.ht/~tfardet/nngt-developers/patches/25710 [1]: mailto:tanguyfardet@protonmail.com ✗ #608173 FAILED NNGT/patches/.build.yml https://builds.sr.ht/~tfardet/job/608173