diff --git a/.github/workflows/run-tox.yml b/.github/workflows/run-tox.yml index 30445c3..afa0306 100644 --- a/.github/workflows/run-tox.yml +++ b/.github/workflows/run-tox.yml @@ -24,6 +24,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | + sudo apt-get install -y poppler-utils imagemagick python -m pip install --upgrade pip setuptools pip install tox-gh-actions pybind11 - name: Run tox diff --git a/.gitignore b/.gitignore index abcb24c..74eb1cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ -*.pkl -*.gpickle -*.svg -*.png -*.pdf -*.csv -*.html -examples/communities - # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/examples/run_simple_example.sh b/examples/run_simple_example.sh index cecd36d..dd6a2b8 100755 --- a/examples/run_simple_example.sh +++ b/examples/run_simple_example.sh @@ -11,10 +11,9 @@ pygenstability run \ --n-louvain 100 \ --n-workers 40 \ edges.csv -# sbm_graph.pkl pygenstability plot_scan --help pygenstability plot_scan results.pkl pygenstability plot_communities --help -pygenstability plot_communities sbm_graph.gpickle results.pkl +pygenstability plot_communities edges.csv results.pkl diff --git a/setup.py b/setup.py index 0eb7be4..135397d 100755 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ "pytest", "pytest-cov", "pytest-html", + "diff-pdf-visually", ] setup( diff --git a/src/pygenstability/app.py b/src/pygenstability/app.py index a931732..7a4f135 100644 --- a/src/pygenstability/app.py +++ b/src/pygenstability/app.py @@ -3,6 +3,7 @@ from pathlib import Path import click +import networkx as nx import numpy as np import pandas as pd from scipy import sparse as sp @@ -13,6 +14,26 @@ from pygenstability.plotting import plot_scan as _plot_scan +def _load_graph(graph_file): + try: + # load pickle file + if Path(graph_file).suffix == ".pkl": + with open(graph_file, "rb") as pickle_file: # pragma: no cover + graph = pickle.load(pickle_file) + else: + # load text file with edge list + edges = pd.read_csv(graph_file) + n_nodes = len(np.unique(edges[edges.columns[:2]].to_numpy().flatten())) + # pylint: disable=unsubscriptable-object,no-member + graph = sp.csr_matrix( + (edges[edges.columns[2]], tuple(edges[edges.columns[:2]].to_numpy().T)), + shape=(n_nodes, n_nodes), + ) + except Exception as exc: # pragma: no cover + raise Exception("Could not load the graph file.") from exc + return graph + + @click.group() def cli(): """App initialisation.""" @@ -27,23 +48,23 @@ def cli(): help="Name of the quality constructor.", ) @click.option( - "--min-time", + "--min-scale", default=-2.0, show_default=True, - help="Minimum Markov time.", + help="Minimum scale.", ) @click.option( - "--max-time", + "--max-scale", default=0.5, show_default=True, - help="Maximum Markov time.", + help="Maximum scale.", ) -@click.option("--n-time", default=20, show_default=True, help="Number of time steps.") +@click.option("--n-scale", default=20, show_default=True, help="Number of scale steps.") @click.option( - "--log-time", + "--log-scale", default=True, show_default=True, - help="Use linear or log scales for times.", + help="Use linear or log scales.", ) @click.option( "--n-louvain", @@ -52,16 +73,16 @@ def cli(): help="Number of Louvain evaluations.", ) @click.option( - "--VI/--no-VI", + "--NVI/--no-NVI", default=True, show_default=True, - help="Compute the variation of information between Louvain runs.", + help="Compute the normalized variation of information between Louvain runs.", ) @click.option( - "--n-louvain-VI", + "--n-louvain-NVI", default=20, show_default=True, - help="Number of randomly chosen Louvain run to estimate the VI.", + help="Number of randomly chosen Louvain run to estimate the NVI.", ) @click.option( "--postprocessing/--no-postprocessing", @@ -79,7 +100,7 @@ def cli(): "--spectral-gap/--no-spectral-gap", default=True, show_default=True, - help="Normalize time by spectral gap.", + help="Normalize scale by spectral gap.", ) @click.option( "--result-file", @@ -97,13 +118,13 @@ def cli(): def run( graph_file, constructor, - min_time, - max_time, - n_time, - log_time, + min_scale, + max_scale, + n_scale, + log_scale, n_louvain, - vi, - n_louvain_vi, + nvi, + n_louvain_nvi, postprocessing, ttprime, spectral_gap, @@ -120,33 +141,17 @@ def run( See https://barahona-research-group.github.io/PyGenStability/ for more information. """ - try: - # load pickle file - if Path(graph_file).suffix == ".pkl": - with open(graph_file, "rb") as pickle_file: - graph = pickle.load(pickle_file) - else: - # load text file with edge list - edges = pd.read_csv(graph_file) - n_nodes = len(np.unique(edges[edges.columns[:2]].to_numpy().flatten())) - # pylint: disable=unsubscriptable-object,no-member - graph = sp.csr_matrix( - (edges[edges.columns[2]], tuple(edges[edges.columns[:2]].to_numpy().T)), - shape=(n_nodes, n_nodes), - ) - except Exception as exc: - raise Exception("Could not load the graph file.") from exc - + graph = _load_graph(graph_file) _run( graph, constructor=constructor, - min_time=min_time, - max_time=max_time, - n_time=n_time, - log_time=log_time, + min_scale=min_scale, + max_scale=max_scale, + n_scale=n_scale, + log_scale=log_scale, n_louvain=n_louvain, - with_VI=vi, - n_louvain_VI=n_louvain_vi, + with_NVI=nvi, + n_louvain_NVI=n_louvain_nvi, with_postprocessing=postprocessing, with_ttprime=ttprime, with_spectral_gap=spectral_gap, @@ -166,11 +171,9 @@ def plot_scan(results_file): @cli.command("plot_communities") @click.argument("graph_file", type=click.Path(exists=True)) @click.argument("results_file", type=click.Path(exists=True)) -def plot_communities(results_file, graph_file): - """Plot communities on networkx graph. - - Argument graph_file has to be a .gpickle compatible with network. - """ - with open(graph_file, "rb") as pickle_file: - graph = pickle.load(pickle_file) +def plot_communities(graph_file, results_file): + """Plot communities on networkx graph.""" + graph = _load_graph(graph_file) + if not isinstance(graph, nx.Graph): + graph = nx.from_scipy_sparse_array(graph) _plot_communities(graph, load_results(results_file)) diff --git a/src/pygenstability/contrib/sankey.py b/src/pygenstability/contrib/sankey.py index 31251e9..58a56c0 100644 --- a/src/pygenstability/contrib/sankey.py +++ b/src/pygenstability/contrib/sankey.py @@ -1,7 +1,7 @@ """Sankey diagram plots.""" -import numpy as np -import plotly.graph_objects as go -from plotly.offline import plot +import numpy as np # pragma: no cover +import plotly.graph_objects as go # pragma: no cover +from plotly.offline import plot # pragma: no cover def plot_sankey( @@ -10,7 +10,7 @@ def plot_sankey( live=False, filename="communities_sankey.html", time_index=None, -): +): # pragma: no cover """Plot Sankey diagram of communities accros time (plotly only). Args: diff --git a/src/pygenstability/io.py b/src/pygenstability/io.py index f955a69..34af04e 100644 --- a/src/pygenstability/io.py +++ b/src/pygenstability/io.py @@ -8,7 +8,7 @@ def save_results(all_results, filename="results.pkl"): pickle.dump(all_results, results_file) -def load_results(filename="results.pkl"): +def load_results(filename="results.pkl"): # pragma: no cover """Load results from a pickle.""" with open(filename, "rb") as results_file: return pickle.load(results_file) diff --git a/src/pygenstability/plotting.py b/src/pygenstability/plotting.py index 0101ecc..0d2ff8b 100644 --- a/src/pygenstability/plotting.py +++ b/src/pygenstability/plotting.py @@ -14,7 +14,7 @@ try: import plotly.graph_objects as go from plotly.offline import plot as _plot -except ImportError: +except ImportError: # pragma: no cover pass @@ -41,7 +41,7 @@ def plot_scan( live (bool): for plotly backend, open browser with pot plotly_filename (str): filename of .html figure from plotly """ - if len(all_results["scales"]) == 1: + if len(all_results["scales"]) == 1: # pragma: no cover L.info("Cannot plot the results if only one scale point, we display the result instead:") L.info(all_results) return None @@ -66,7 +66,7 @@ def plot_scan_plotly( # pylint: disable=too-many-branches,too-many-statements,t nvi_opacity = 1.0 nvi_title = "Variation of information" nvi_ticks = True - else: + else: # pragma: no cover nvi_data = np.zeros(len(scales)) nvi_opacity = 0.0 nvi_title = None @@ -98,7 +98,7 @@ def plot_scan_plotly( # pylint: disable=too-many-branches,too-many-statements,t z = all_results["ttprime"] showscale = True tprime_title = "log10(scale)" - else: + else: # pragma: no cover z = np.nan + np.zeros([len(scales), len(scales)]) showscale = False tprime_title = None @@ -182,7 +182,7 @@ def plot_scan_plotly( # pylint: disable=too-many-branches,too-many-statements,t if filename is not None: _plot(fig, filename=filename) - if live: + if live: # pragma: no cover fig.show() return fig, layout @@ -202,6 +202,11 @@ def plot_single_partition( node_size (float): size of nodes ext (str): extension of figures files """ + if any("pos" not in graph.nodes[u] for u in graph): + pos = nx.spring_layout(graph) + for u in graph: + graph.nodes[u]["pos"] = pos[u] + pos = {u: graph.nodes[u]["pos"] for u in graph} node_color = all_results["community_id"][scale_id] @@ -225,7 +230,9 @@ def plot_single_partition( ) -def plot_optimal_partitions(graph, all_results, edge_color="0.5", edge_width=0.5): +def plot_optimal_partitions( + graph, all_results, edge_color="0.5", edge_width=0.5, folder="optimal_partitions", ext=".pdf" +): """Plot the community structures at each optimal scale. Args: @@ -233,20 +240,26 @@ def plot_optimal_partitions(graph, all_results, edge_color="0.5", edge_width=0.5 all_results (dict): results of pygenstability scan edge_color (str): color of edges edge_width (float): width of edgs + folder (str): folder to save figures + ext (str): extension of figures files """ - if "selected_partitions" not in all_results: + if not os.path.isdir(folder): + os.mkdir(folder) + + if "selected_partitions" not in all_results: # pragma: no cover identify_optimal_scales(all_results) selected_scales = all_results["selected_partitions"] n_selected_scales = len(selected_scales) - if n_selected_scales == 0: + if n_selected_scales == 0: # pragma: no cover return for optimal_scale_id in selected_scales: plot_single_partition( graph, all_results, optimal_scale_id, edge_color=edge_color, edge_width=edge_width ) + plt.savefig(f"{folder}/scale_{optimal_scale_id}{ext}", bbox_inches="tight") def plot_communities( @@ -279,11 +292,11 @@ def plot_communities( def get_scales(all_results, scale_axis=True): """Get the scale vector.""" - if not scale_axis: + if not scale_axis: # pragma: no cover return np.arange(len(all_results["scales"])) if all_results["run_params"]["log_scale"]: return np.log10(all_results["scales"]) - return all_results["scales"] + return all_results["scales"] # pragma: no cover def _plot_number_comm(all_results, ax, scales): @@ -373,7 +386,7 @@ def plot_scan_plt(all_results, scale_axis=True, figure_name="scan_results.svg"): ax0 = plt.subplot(gs[1, 0]) _plot_ttprime(all_results, ax=ax0, scales=scales) ax1 = ax0.twinx() - else: + else: # pragma: no cover ax1 = plt.subplot(gs[1, 0]) axes.append(ax1) @@ -438,7 +451,7 @@ def plot_clustered_adjacency( adjacency[adjacency == 0] = np.nan plt.figure(figsize=figsize) - plt.imshow(adjacency, aspect="auto", origin="auto", cmap=cmap) + plt.imshow(adjacency, aspect="auto", cmap=cmap) ax = plt.gca() @@ -458,7 +471,7 @@ def plot_clustered_adjacency( ax.set_xticks(np.arange(len(adjacency))) ax.set_yticks(np.arange(len(adjacency))) - if labels is not None: + if labels is not None: # pragma: no cover labels_plot = [labels[i] for i in node_ids] ax.set_xticklabels(labels_plot) ax.set_yticklabels(labels_plot) @@ -474,100 +487,3 @@ def plot_clustered_adjacency( ) plt.savefig(figure_name, bbox_inches="tight") - - -def plot_optimal_scales( - results, - scale_axis=True, - figure_name="scan_results.pdf", - use_plotly=False, - live=True, - plotly_filename="scan_results.html", -): - """Plot scan results with optimal scales.""" - if len(results["scales"]) == 1: - L.info("Cannot plot the results if only one scalae point, we display the result instead:") - L.info(results) - return - - if use_plotly: - try: - plot_optimal_scales_plotly(results, live=live, filename=plotly_filename) - except ImportError: - L.warning( - "Plotly is not installed, please install package with \ - pip install pygenstabiliy[plotly], using matplotlib instead." - ) - - else: - plot_optimal_scales_plt(results, scale_axis=scale_axis, figure_name=figure_name) - - -def plot_optimal_scales_plotly(results, live=False, filename="scan_results.pdf"): - """Plot optimal scales on plotly.""" - fig, _ = plot_scan_plotly(results, live=False, filename=None) - - scales = get_scales(results, scale_axis=True) - - fig.add_scatter( - x=scales, - y=results["optimal_scale_criterion"], - mode="lines+markers", - name="Optimal Scale Criterion", - yaxis="y5", - xaxis="x", - marker_color="orange", - ) - - fig.add_scatter( - x=scales[results["selected_partitions"]], - y=results["optimal_scale_criterion"][results["selected_partitions"]], - mode="markers", - name="Optimal Scale", - yaxis="y5", - xaxis="x", - marker_color="red", - ) - - fig.update_layout( - yaxis5=dict( - titlefont=dict(color="orange"), - tickfont=dict(color="orange"), - domain=[0.0, 0.28], - overlaying="y", - ) - ) - fig.update_layout(yaxis=dict(title="Stability, Optimal Scale Criterion")) - if filename is not None: - _plot(fig, filename=filename) - - if live: - fig.show() - - -def plot_optimal_scales_plt(results, scale_axis=True, figure_name="scan_results.pdf"): - """Plot scan results with optimal scales with matplotlib.""" - ax2 = plot_scan_plt(results, scale_axis=scale_axis, figure_name=None)[2] - scales = get_scales(results, scale_axis=scale_axis) - - ax2.plot( - scales, - results["optimal_scale_criterion"], - "-", - lw=2.0, - c="C4", - label="optimal scale criterion", - ) - ax2.plot( - scales[results["selected_partitions"]], - results["optimal_scale_criterion"][results["selected_partitions"]], - "o", - lw=2.0, - c="C4", - label="optimal scales", - ) - - ax2.set_ylabel(r"Stability, Optimal scales", color="k") - ax2.legend() - if figure_name is not None: - plt.savefig(figure_name, bbox_inches="tight") diff --git a/tests/conftest.py b/tests/conftest.py index 35cb039..ff9428b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,31 +2,45 @@ import networkx as nx import pytest +from pygenstability.constructors import load_constructor +from pygenstability import pygenstability as pgs + + +@pytest.fixture() +def graph_nx(): + """Create barbell graph.""" + return nx.barbell_graph(10, 2) + @pytest.fixture() -def graph(): +def graph(graph_nx): """Create barbell graph.""" - return nx.to_scipy_sparse_matrix(nx.barbell_graph(10, 2), dtype=float) + return nx.to_scipy_sparse_matrix(graph_nx, dtype=float) @pytest.fixture() def graph_non_connected(): """Create barbell graph.""" - graph = nx.barbell_graph(10, 2) - graph.add_node(len(graph)) - return nx.to_scipy_sparse_matrix(graph, dtype=float) + graph_nx = nx.barbell_graph(10, 2) + graph_nx.add_node(len(graph_nx)) + return nx.to_scipy_sparse_matrix(graph_nx, dtype=float) + @pytest.fixture() def graph_directed(): """Create barbell graph.""" return nx.to_scipy_sparse_matrix(nx.DiGraph([(0, 1), (1, 2), (2, 3), (3, 0)]), dtype=float) + @pytest.fixture() def graph_signed(): """Create barbell graph.""" - graph = nx.barbell_graph(10, 2) - graph[0][1]['weight'] = -1 - return nx.to_scipy_sparse_matrix(graph, dtype=float) - + graph_nx = nx.barbell_graph(10, 2) + graph_nx[0][1]["weight"] = -1 + return nx.to_scipy_sparse_matrix(graph_nx, dtype=float) +@pytest.fixture() +def results(graph): + constructor = load_constructor("continuous_combinatorial", graph) + return pgs.run(graph, constructor=constructor) diff --git a/tests/data/clustered_adjacency.pdf b/tests/data/clustered_adjacency.pdf new file mode 100644 index 0000000..9e02d41 Binary files /dev/null and b/tests/data/clustered_adjacency.pdf differ diff --git a/tests/data/edges.csv b/tests/data/edges.csv new file mode 100644 index 0000000..76f6a8f --- /dev/null +++ b/tests/data/edges.csv @@ -0,0 +1,1753 @@ +i,j,weight +0,3,1 +0,4,1 +0,5,1 +0,6,1 +0,8,1 +0,9,1 +0,10,1 +0,12,1 +0,13,1 +0,17,1 +0,31,1 +0,34,1 +0,56,1 +1,2,1 +1,3,1 +1,8,1 +1,11,1 +1,12,1 +1,13,1 +1,14,1 +1,27,1 +1,62,1 +2,3,1 +2,6,1 +2,8,1 +2,10,1 +2,11,1 +2,13,1 +2,16,1 +2,21,1 +2,59,1 +2,60,1 +3,4,1 +3,5,1 +3,6,1 +3,8,1 +3,9,1 +3,11,1 +3,12,1 +3,13,1 +3,24,1 +3,31,1 +3,33,1 +3,41,1 +3,46,1 +3,51,1 +3,61,1 +3,64,1 +3,66,1 +3,67,1 +4,6,1 +4,7,1 +4,8,1 +4,9,1 +4,11,1 +4,12,1 +4,14,1 +4,59,1 +4,63,1 +4,64,1 +5,7,1 +5,9,1 +5,10,1 +5,11,1 +5,12,1 +5,13,1 +5,14,1 +5,20,1 +5,26,1 +5,33,1 +5,40,1 +5,67,1 +5,71,1 +5,73,1 +6,7,1 +6,8,1 +6,9,1 +6,10,1 +6,11,1 +6,12,1 +6,13,1 +6,17,1 +6,29,1 +6,30,1 +6,59,1 +6,73,1 +7,13,1 +7,14,1 +7,35,1 +7,53,1 +7,61,1 +8,10,1 +8,14,1 +8,46,1 +8,47,1 +8,53,1 +8,61,1 +8,70,1 +9,11,1 +9,12,1 +9,13,1 +9,33,1 +9,51,1 +9,58,1 +9,70,1 +10,13,1 +10,14,1 +10,24,1 +10,43,1 +10,46,1 +10,47,1 +10,55,1 +10,57,1 +10,71,1 +11,12,1 +11,13,1 +11,52,1 +12,13,1 +12,23,1 +12,34,1 +12,69,1 +12,72,1 +13,14,1 +13,27,1 +13,44,1 +13,58,1 +14,28,1 +14,34,1 +14,47,1 +14,63,1 +15,16,1 +15,18,1 +15,19,1 +15,20,1 +15,22,1 +15,23,1 +15,24,1 +15,25,1 +15,26,1 +15,27,1 +15,28,1 +15,29,1 +15,30,1 +15,31,1 +15,32,1 +15,33,1 +15,35,1 +15,36,1 +15,37,1 +15,38,1 +15,39,1 +15,40,1 +15,41,1 +15,42,1 +15,43,1 +15,44,1 +15,45,1 +15,46,1 +15,47,1 +15,48,1 +16,17,1 +16,18,1 +16,19,1 +16,23,1 +16,25,1 +16,26,1 +16,27,1 +16,29,1 +16,30,1 +16,31,1 +16,32,1 +16,33,1 +16,34,1 +16,36,1 +16,37,1 +16,38,1 +16,39,1 +16,41,1 +16,43,1 +16,45,1 +16,46,1 +16,47,1 +16,48,1 +16,49,1 +16,67,1 +17,19,1 +17,20,1 +17,21,1 +17,22,1 +17,24,1 +17,25,1 +17,26,1 +17,27,1 +17,28,1 +17,32,1 +17,33,1 +17,34,1 +17,35,1 +17,36,1 +17,38,1 +17,39,1 +17,40,1 +17,41,1 +17,42,1 +17,43,1 +17,44,1 +17,45,1 +17,46,1 +17,47,1 +17,48,1 +17,49,1 +17,56,1 +17,66,1 +18,19,1 +18,20,1 +18,22,1 +18,23,1 +18,24,1 +18,25,1 +18,26,1 +18,27,1 +18,28,1 +18,29,1 +18,30,1 +18,31,1 +18,32,1 +18,33,1 +18,34,1 +18,35,1 +18,37,1 +18,40,1 +18,41,1 +18,42,1 +18,43,1 +18,44,1 +18,45,1 +18,48,1 +18,49,1 +19,21,1 +19,22,1 +19,23,1 +19,24,1 +19,25,1 +19,26,1 +19,27,1 +19,28,1 +19,29,1 +19,30,1 +19,31,1 +19,32,1 +19,33,1 +19,34,1 +19,36,1 +19,37,1 +19,38,1 +19,39,1 +19,40,1 +19,42,1 +19,44,1 +19,45,1 +19,46,1 +19,47,1 +19,48,1 +19,49,1 +20,22,1 +20,23,1 +20,24,1 +20,25,1 +20,26,1 +20,27,1 +20,30,1 +20,31,1 +20,32,1 +20,34,1 +20,36,1 +20,37,1 +20,38,1 +20,39,1 +20,40,1 +20,41,1 +20,42,1 +20,43,1 +20,47,1 +20,48,1 +20,49,1 +21,22,1 +21,24,1 +21,25,1 +21,27,1 +21,28,1 +21,30,1 +21,31,1 +21,32,1 +21,33,1 +21,35,1 +21,36,1 +21,37,1 +21,38,1 +21,40,1 +21,42,1 +21,43,1 +21,44,1 +21,46,1 +21,48,1 +22,23,1 +22,24,1 +22,25,1 +22,27,1 +22,28,1 +22,29,1 +22,30,1 +22,32,1 +22,33,1 +22,34,1 +22,35,1 +22,36,1 +22,37,1 +22,38,1 +22,41,1 +22,42,1 +22,44,1 +22,45,1 +22,46,1 +22,47,1 +22,49,1 +23,24,1 +23,25,1 +23,26,1 +23,28,1 +23,29,1 +23,30,1 +23,32,1 +23,33,1 +23,34,1 +23,35,1 +23,36,1 +23,37,1 +23,38,1 +23,39,1 +23,40,1 +23,41,1 +23,42,1 +23,43,1 +23,44,1 +23,45,1 +23,46,1 +23,48,1 +23,49,1 +24,25,1 +24,26,1 +24,27,1 +24,28,1 +24,29,1 +24,30,1 +24,31,1 +24,33,1 +24,36,1 +24,38,1 +24,39,1 +24,40,1 +24,41,1 +24,42,1 +24,43,1 +24,44,1 +24,45,1 +24,46,1 +24,47,1 +24,49,1 +24,52,1 +25,28,1 +25,29,1 +25,30,1 +25,31,1 +25,34,1 +25,35,1 +25,36,1 +25,37,1 +25,38,1 +25,39,1 +25,40,1 +25,43,1 +25,44,1 +25,45,1 +25,46,1 +25,47,1 +25,48,1 +25,49,1 +26,27,1 +26,30,1 +26,31,1 +26,32,1 +26,33,1 +26,34,1 +26,35,1 +26,36,1 +26,39,1 +26,41,1 +26,42,1 +26,43,1 +26,45,1 +26,46,1 +26,47,1 +26,48,1 +26,49,1 +27,29,1 +27,30,1 +27,31,1 +27,33,1 +27,35,1 +27,36,1 +27,37,1 +27,40,1 +27,41,1 +27,43,1 +27,44,1 +27,45,1 +27,49,1 +27,62,1 +28,29,1 +28,30,1 +28,31,1 +28,32,1 +28,33,1 +28,34,1 +28,35,1 +28,36,1 +28,37,1 +28,38,1 +28,39,1 +28,40,1 +28,41,1 +28,42,1 +28,43,1 +28,44,1 +28,45,1 +28,46,1 +28,48,1 +28,49,1 +28,73,1 +29,30,1 +29,31,1 +29,32,1 +29,33,1 +29,34,1 +29,35,1 +29,37,1 +29,38,1 +29,39,1 +29,40,1 +29,41,1 +29,42,1 +29,43,1 +29,44,1 +29,45,1 +29,46,1 +29,47,1 +29,49,1 +30,31,1 +30,32,1 +30,33,1 +30,34,1 +30,35,1 +30,36,1 +30,37,1 +30,38,1 +30,39,1 +30,40,1 +30,41,1 +30,42,1 +30,43,1 +30,44,1 +30,45,1 +30,46,1 +30,47,1 +30,48,1 +30,49,1 +31,32,1 +31,34,1 +31,36,1 +31,37,1 +31,38,1 +31,39,1 +31,40,1 +31,41,1 +31,42,1 +31,43,1 +31,44,1 +31,46,1 +31,47,1 +31,48,1 +31,49,1 +32,33,1 +32,34,1 +32,35,1 +32,37,1 +32,38,1 +32,39,1 +32,40,1 +32,41,1 +32,42,1 +32,43,1 +32,44,1 +32,45,1 +32,46,1 +32,47,1 +32,48,1 +32,49,1 +33,34,1 +33,35,1 +33,36,1 +33,37,1 +33,38,1 +33,39,1 +33,40,1 +33,41,1 +33,42,1 +33,43,1 +33,44,1 +33,45,1 +33,46,1 +33,47,1 +33,48,1 +33,49,1 +33,50,1 +33,57,1 +33,61,1 +34,35,1 +34,36,1 +34,37,1 +34,38,1 +34,39,1 +34,40,1 +34,42,1 +34,43,1 +34,44,1 +34,46,1 +34,47,1 +34,48,1 +34,49,1 +35,36,1 +35,37,1 +35,40,1 +35,41,1 +35,42,1 +35,43,1 +35,44,1 +35,45,1 +35,46,1 +35,47,1 +35,48,1 +35,49,1 +35,68,1 +36,38,1 +36,39,1 +36,40,1 +36,41,1 +36,42,1 +36,44,1 +36,45,1 +36,46,1 +36,48,1 +36,49,1 +37,38,1 +37,40,1 +37,41,1 +37,42,1 +37,43,1 +37,44,1 +37,45,1 +37,46,1 +37,47,1 +37,48,1 +37,49,1 +37,60,1 +38,39,1 +38,40,1 +38,41,1 +38,42,1 +38,44,1 +38,45,1 +38,46,1 +38,47,1 +38,64,1 +39,42,1 +39,43,1 +39,44,1 +39,45,1 +39,46,1 +39,47,1 +39,48,1 +39,49,1 +40,41,1 +40,42,1 +40,43,1 +40,44,1 +40,46,1 +40,48,1 +40,49,1 +41,42,1 +41,44,1 +41,45,1 +41,46,1 +41,47,1 +41,48,1 +41,49,1 +41,52,1 +42,43,1 +42,44,1 +42,45,1 +42,46,1 +42,47,1 +42,48,1 +42,49,1 +42,58,1 +43,44,1 +43,46,1 +43,47,1 +43,48,1 +43,49,1 +44,46,1 +44,47,1 +44,48,1 +44,49,1 +45,48,1 +45,49,1 +45,63,1 +46,47,1 +46,48,1 +46,49,1 +47,48,1 +47,74,1 +48,49,1 +49,65,1 +50,51,1 +50,52,1 +50,53,1 +50,54,1 +50,55,1 +50,56,1 +50,57,1 +50,58,1 +50,59,1 +50,60,1 +50,61,1 +50,62,1 +50,63,1 +50,65,1 +50,66,1 +50,67,1 +50,68,1 +50,69,1 +50,70,1 +50,71,1 +50,72,1 +50,73,1 +51,52,1 +51,53,1 +51,54,1 +51,55,1 +51,56,1 +51,57,1 +51,58,1 +51,60,1 +51,61,1 +51,62,1 +51,63,1 +51,64,1 +51,65,1 +51,67,1 +51,68,1 +51,69,1 +51,70,1 +51,71,1 +51,72,1 +51,73,1 +51,74,1 +52,53,1 +52,55,1 +52,56,1 +52,57,1 +52,58,1 +52,60,1 +52,62,1 +52,63,1 +52,64,1 +52,65,1 +52,66,1 +52,67,1 +52,68,1 +52,69,1 +52,71,1 +52,72,1 +52,74,1 +53,54,1 +53,55,1 +53,57,1 +53,58,1 +53,59,1 +53,60,1 +53,61,1 +53,63,1 +53,64,1 +53,65,1 +53,66,1 +53,67,1 +53,68,1 +53,69,1 +53,70,1 +53,71,1 +53,72,1 +53,73,1 +53,74,1 +54,55,1 +54,56,1 +54,57,1 +54,58,1 +54,59,1 +54,60,1 +54,61,1 +54,63,1 +54,65,1 +54,66,1 +54,67,1 +54,68,1 +54,69,1 +54,71,1 +54,72,1 +54,73,1 +54,74,1 +55,56,1 +55,57,1 +55,58,1 +55,59,1 +55,60,1 +55,62,1 +55,63,1 +55,64,1 +55,65,1 +55,67,1 +55,68,1 +55,69,1 +55,70,1 +55,73,1 +55,74,1 +56,58,1 +56,59,1 +56,60,1 +56,61,1 +56,62,1 +56,64,1 +56,65,1 +56,66,1 +56,67,1 +56,68,1 +56,69,1 +56,71,1 +56,72,1 +56,74,1 +57,58,1 +57,59,1 +57,60,1 +57,61,1 +57,62,1 +57,63,1 +57,64,1 +57,65,1 +57,66,1 +57,67,1 +57,68,1 +57,70,1 +57,72,1 +57,73,1 +57,74,1 +58,59,1 +58,60,1 +58,61,1 +58,62,1 +58,63,1 +58,64,1 +58,65,1 +58,66,1 +58,67,1 +58,68,1 +58,69,1 +58,70,1 +58,71,1 +58,72,1 +58,73,1 +58,74,1 +59,62,1 +59,63,1 +59,64,1 +59,67,1 +59,68,1 +59,69,1 +59,70,1 +59,73,1 +59,74,1 +60,61,1 +60,63,1 +60,65,1 +60,66,1 +60,67,1 +60,68,1 +60,69,1 +60,71,1 +60,72,1 +60,73,1 +60,74,1 +61,64,1 +61,67,1 +61,68,1 +61,69,1 +61,70,1 +61,71,1 +61,73,1 +61,74,1 +62,63,1 +62,64,1 +62,65,1 +62,66,1 +62,67,1 +62,68,1 +62,69,1 +62,70,1 +62,71,1 +62,72,1 +62,74,1 +63,65,1 +63,66,1 +63,68,1 +63,69,1 +63,70,1 +63,72,1 +63,73,1 +63,74,1 +64,66,1 +64,67,1 +64,69,1 +64,70,1 +64,71,1 +64,73,1 +64,74,1 +65,66,1 +65,68,1 +65,69,1 +65,70,1 +65,71,1 +65,72,1 +65,73,1 +66,67,1 +66,70,1 +66,71,1 +66,72,1 +66,73,1 +66,74,1 +67,69,1 +67,71,1 +67,74,1 +68,70,1 +68,72,1 +68,73,1 +68,74,1 +69,70,1 +69,71,1 +69,72,1 +69,73,1 +69,74,1 +70,71,1 +70,72,1 +70,73,1 +71,72,1 +71,73,1 +72,73,1 +72,74,1 +73,74,1 +3,0,1 +4,0,1 +5,0,1 +6,0,1 +8,0,1 +9,0,1 +10,0,1 +12,0,1 +13,0,1 +17,0,1 +31,0,1 +34,0,1 +56,0,1 +2,1,1 +3,1,1 +8,1,1 +11,1,1 +12,1,1 +13,1,1 +14,1,1 +27,1,1 +62,1,1 +3,2,1 +6,2,1 +8,2,1 +10,2,1 +11,2,1 +13,2,1 +16,2,1 +21,2,1 +59,2,1 +60,2,1 +4,3,1 +5,3,1 +6,3,1 +8,3,1 +9,3,1 +11,3,1 +12,3,1 +13,3,1 +24,3,1 +31,3,1 +33,3,1 +41,3,1 +46,3,1 +51,3,1 +61,3,1 +64,3,1 +66,3,1 +67,3,1 +6,4,1 +7,4,1 +8,4,1 +9,4,1 +11,4,1 +12,4,1 +14,4,1 +59,4,1 +63,4,1 +64,4,1 +7,5,1 +9,5,1 +10,5,1 +11,5,1 +12,5,1 +13,5,1 +14,5,1 +20,5,1 +26,5,1 +33,5,1 +40,5,1 +67,5,1 +71,5,1 +73,5,1 +7,6,1 +8,6,1 +9,6,1 +10,6,1 +11,6,1 +12,6,1 +13,6,1 +17,6,1 +29,6,1 +30,6,1 +59,6,1 +73,6,1 +13,7,1 +14,7,1 +35,7,1 +53,7,1 +61,7,1 +10,8,1 +14,8,1 +46,8,1 +47,8,1 +53,8,1 +61,8,1 +70,8,1 +11,9,1 +12,9,1 +13,9,1 +33,9,1 +51,9,1 +58,9,1 +70,9,1 +13,10,1 +14,10,1 +24,10,1 +43,10,1 +46,10,1 +47,10,1 +55,10,1 +57,10,1 +71,10,1 +12,11,1 +13,11,1 +52,11,1 +13,12,1 +23,12,1 +34,12,1 +69,12,1 +72,12,1 +14,13,1 +27,13,1 +44,13,1 +58,13,1 +28,14,1 +34,14,1 +47,14,1 +63,14,1 +16,15,1 +18,15,1 +19,15,1 +20,15,1 +22,15,1 +23,15,1 +24,15,1 +25,15,1 +26,15,1 +27,15,1 +28,15,1 +29,15,1 +30,15,1 +31,15,1 +32,15,1 +33,15,1 +35,15,1 +36,15,1 +37,15,1 +38,15,1 +39,15,1 +40,15,1 +41,15,1 +42,15,1 +43,15,1 +44,15,1 +45,15,1 +46,15,1 +47,15,1 +48,15,1 +17,16,1 +18,16,1 +19,16,1 +23,16,1 +25,16,1 +26,16,1 +27,16,1 +29,16,1 +30,16,1 +31,16,1 +32,16,1 +33,16,1 +34,16,1 +36,16,1 +37,16,1 +38,16,1 +39,16,1 +41,16,1 +43,16,1 +45,16,1 +46,16,1 +47,16,1 +48,16,1 +49,16,1 +67,16,1 +19,17,1 +20,17,1 +21,17,1 +22,17,1 +24,17,1 +25,17,1 +26,17,1 +27,17,1 +28,17,1 +32,17,1 +33,17,1 +34,17,1 +35,17,1 +36,17,1 +38,17,1 +39,17,1 +40,17,1 +41,17,1 +42,17,1 +43,17,1 +44,17,1 +45,17,1 +46,17,1 +47,17,1 +48,17,1 +49,17,1 +56,17,1 +66,17,1 +19,18,1 +20,18,1 +22,18,1 +23,18,1 +24,18,1 +25,18,1 +26,18,1 +27,18,1 +28,18,1 +29,18,1 +30,18,1 +31,18,1 +32,18,1 +33,18,1 +34,18,1 +35,18,1 +37,18,1 +40,18,1 +41,18,1 +42,18,1 +43,18,1 +44,18,1 +45,18,1 +48,18,1 +49,18,1 +21,19,1 +22,19,1 +23,19,1 +24,19,1 +25,19,1 +26,19,1 +27,19,1 +28,19,1 +29,19,1 +30,19,1 +31,19,1 +32,19,1 +33,19,1 +34,19,1 +36,19,1 +37,19,1 +38,19,1 +39,19,1 +40,19,1 +42,19,1 +44,19,1 +45,19,1 +46,19,1 +47,19,1 +48,19,1 +49,19,1 +22,20,1 +23,20,1 +24,20,1 +25,20,1 +26,20,1 +27,20,1 +30,20,1 +31,20,1 +32,20,1 +34,20,1 +36,20,1 +37,20,1 +38,20,1 +39,20,1 +40,20,1 +41,20,1 +42,20,1 +43,20,1 +47,20,1 +48,20,1 +49,20,1 +22,21,1 +24,21,1 +25,21,1 +27,21,1 +28,21,1 +30,21,1 +31,21,1 +32,21,1 +33,21,1 +35,21,1 +36,21,1 +37,21,1 +38,21,1 +40,21,1 +42,21,1 +43,21,1 +44,21,1 +46,21,1 +48,21,1 +23,22,1 +24,22,1 +25,22,1 +27,22,1 +28,22,1 +29,22,1 +30,22,1 +32,22,1 +33,22,1 +34,22,1 +35,22,1 +36,22,1 +37,22,1 +38,22,1 +41,22,1 +42,22,1 +44,22,1 +45,22,1 +46,22,1 +47,22,1 +49,22,1 +24,23,1 +25,23,1 +26,23,1 +28,23,1 +29,23,1 +30,23,1 +32,23,1 +33,23,1 +34,23,1 +35,23,1 +36,23,1 +37,23,1 +38,23,1 +39,23,1 +40,23,1 +41,23,1 +42,23,1 +43,23,1 +44,23,1 +45,23,1 +46,23,1 +48,23,1 +49,23,1 +25,24,1 +26,24,1 +27,24,1 +28,24,1 +29,24,1 +30,24,1 +31,24,1 +33,24,1 +36,24,1 +38,24,1 +39,24,1 +40,24,1 +41,24,1 +42,24,1 +43,24,1 +44,24,1 +45,24,1 +46,24,1 +47,24,1 +49,24,1 +52,24,1 +28,25,1 +29,25,1 +30,25,1 +31,25,1 +34,25,1 +35,25,1 +36,25,1 +37,25,1 +38,25,1 +39,25,1 +40,25,1 +43,25,1 +44,25,1 +45,25,1 +46,25,1 +47,25,1 +48,25,1 +49,25,1 +27,26,1 +30,26,1 +31,26,1 +32,26,1 +33,26,1 +34,26,1 +35,26,1 +36,26,1 +39,26,1 +41,26,1 +42,26,1 +43,26,1 +45,26,1 +46,26,1 +47,26,1 +48,26,1 +49,26,1 +29,27,1 +30,27,1 +31,27,1 +33,27,1 +35,27,1 +36,27,1 +37,27,1 +40,27,1 +41,27,1 +43,27,1 +44,27,1 +45,27,1 +49,27,1 +62,27,1 +29,28,1 +30,28,1 +31,28,1 +32,28,1 +33,28,1 +34,28,1 +35,28,1 +36,28,1 +37,28,1 +38,28,1 +39,28,1 +40,28,1 +41,28,1 +42,28,1 +43,28,1 +44,28,1 +45,28,1 +46,28,1 +48,28,1 +49,28,1 +73,28,1 +30,29,1 +31,29,1 +32,29,1 +33,29,1 +34,29,1 +35,29,1 +37,29,1 +38,29,1 +39,29,1 +40,29,1 +41,29,1 +42,29,1 +43,29,1 +44,29,1 +45,29,1 +46,29,1 +47,29,1 +49,29,1 +31,30,1 +32,30,1 +33,30,1 +34,30,1 +35,30,1 +36,30,1 +37,30,1 +38,30,1 +39,30,1 +40,30,1 +41,30,1 +42,30,1 +43,30,1 +44,30,1 +45,30,1 +46,30,1 +47,30,1 +48,30,1 +49,30,1 +32,31,1 +34,31,1 +36,31,1 +37,31,1 +38,31,1 +39,31,1 +40,31,1 +41,31,1 +42,31,1 +43,31,1 +44,31,1 +46,31,1 +47,31,1 +48,31,1 +49,31,1 +33,32,1 +34,32,1 +35,32,1 +37,32,1 +38,32,1 +39,32,1 +40,32,1 +41,32,1 +42,32,1 +43,32,1 +44,32,1 +45,32,1 +46,32,1 +47,32,1 +48,32,1 +49,32,1 +34,33,1 +35,33,1 +36,33,1 +37,33,1 +38,33,1 +39,33,1 +40,33,1 +41,33,1 +42,33,1 +43,33,1 +44,33,1 +45,33,1 +46,33,1 +47,33,1 +48,33,1 +49,33,1 +50,33,1 +57,33,1 +61,33,1 +35,34,1 +36,34,1 +37,34,1 +38,34,1 +39,34,1 +40,34,1 +42,34,1 +43,34,1 +44,34,1 +46,34,1 +47,34,1 +48,34,1 +49,34,1 +36,35,1 +37,35,1 +40,35,1 +41,35,1 +42,35,1 +43,35,1 +44,35,1 +45,35,1 +46,35,1 +47,35,1 +48,35,1 +49,35,1 +68,35,1 +38,36,1 +39,36,1 +40,36,1 +41,36,1 +42,36,1 +44,36,1 +45,36,1 +46,36,1 +48,36,1 +49,36,1 +38,37,1 +40,37,1 +41,37,1 +42,37,1 +43,37,1 +44,37,1 +45,37,1 +46,37,1 +47,37,1 +48,37,1 +49,37,1 +60,37,1 +39,38,1 +40,38,1 +41,38,1 +42,38,1 +44,38,1 +45,38,1 +46,38,1 +47,38,1 +64,38,1 +42,39,1 +43,39,1 +44,39,1 +45,39,1 +46,39,1 +47,39,1 +48,39,1 +49,39,1 +41,40,1 +42,40,1 +43,40,1 +44,40,1 +46,40,1 +48,40,1 +49,40,1 +42,41,1 +44,41,1 +45,41,1 +46,41,1 +47,41,1 +48,41,1 +49,41,1 +52,41,1 +43,42,1 +44,42,1 +45,42,1 +46,42,1 +47,42,1 +48,42,1 +49,42,1 +58,42,1 +44,43,1 +46,43,1 +47,43,1 +48,43,1 +49,43,1 +46,44,1 +47,44,1 +48,44,1 +49,44,1 +48,45,1 +49,45,1 +63,45,1 +47,46,1 +48,46,1 +49,46,1 +48,47,1 +74,47,1 +49,48,1 +65,49,1 +51,50,1 +52,50,1 +53,50,1 +54,50,1 +55,50,1 +56,50,1 +57,50,1 +58,50,1 +59,50,1 +60,50,1 +61,50,1 +62,50,1 +63,50,1 +65,50,1 +66,50,1 +67,50,1 +68,50,1 +69,50,1 +70,50,1 +71,50,1 +72,50,1 +73,50,1 +52,51,1 +53,51,1 +54,51,1 +55,51,1 +56,51,1 +57,51,1 +58,51,1 +60,51,1 +61,51,1 +62,51,1 +63,51,1 +64,51,1 +65,51,1 +67,51,1 +68,51,1 +69,51,1 +70,51,1 +71,51,1 +72,51,1 +73,51,1 +74,51,1 +53,52,1 +55,52,1 +56,52,1 +57,52,1 +58,52,1 +60,52,1 +62,52,1 +63,52,1 +64,52,1 +65,52,1 +66,52,1 +67,52,1 +68,52,1 +69,52,1 +71,52,1 +72,52,1 +74,52,1 +54,53,1 +55,53,1 +57,53,1 +58,53,1 +59,53,1 +60,53,1 +61,53,1 +63,53,1 +64,53,1 +65,53,1 +66,53,1 +67,53,1 +68,53,1 +69,53,1 +70,53,1 +71,53,1 +72,53,1 +73,53,1 +74,53,1 +55,54,1 +56,54,1 +57,54,1 +58,54,1 +59,54,1 +60,54,1 +61,54,1 +63,54,1 +65,54,1 +66,54,1 +67,54,1 +68,54,1 +69,54,1 +71,54,1 +72,54,1 +73,54,1 +74,54,1 +56,55,1 +57,55,1 +58,55,1 +59,55,1 +60,55,1 +62,55,1 +63,55,1 +64,55,1 +65,55,1 +67,55,1 +68,55,1 +69,55,1 +70,55,1 +73,55,1 +74,55,1 +58,56,1 +59,56,1 +60,56,1 +61,56,1 +62,56,1 +64,56,1 +65,56,1 +66,56,1 +67,56,1 +68,56,1 +69,56,1 +71,56,1 +72,56,1 +74,56,1 +58,57,1 +59,57,1 +60,57,1 +61,57,1 +62,57,1 +63,57,1 +64,57,1 +65,57,1 +66,57,1 +67,57,1 +68,57,1 +70,57,1 +72,57,1 +73,57,1 +74,57,1 +59,58,1 +60,58,1 +61,58,1 +62,58,1 +63,58,1 +64,58,1 +65,58,1 +66,58,1 +67,58,1 +68,58,1 +69,58,1 +70,58,1 +71,58,1 +72,58,1 +73,58,1 +74,58,1 +62,59,1 +63,59,1 +64,59,1 +67,59,1 +68,59,1 +69,59,1 +70,59,1 +73,59,1 +74,59,1 +61,60,1 +63,60,1 +65,60,1 +66,60,1 +67,60,1 +68,60,1 +69,60,1 +71,60,1 +72,60,1 +73,60,1 +74,60,1 +64,61,1 +67,61,1 +68,61,1 +69,61,1 +70,61,1 +71,61,1 +73,61,1 +74,61,1 +63,62,1 +64,62,1 +65,62,1 +66,62,1 +67,62,1 +68,62,1 +69,62,1 +70,62,1 +71,62,1 +72,62,1 +74,62,1 +65,63,1 +66,63,1 +68,63,1 +69,63,1 +70,63,1 +72,63,1 +73,63,1 +74,63,1 +66,64,1 +67,64,1 +69,64,1 +70,64,1 +71,64,1 +73,64,1 +74,64,1 +66,65,1 +68,65,1 +69,65,1 +70,65,1 +71,65,1 +72,65,1 +73,65,1 +67,66,1 +70,66,1 +71,66,1 +72,66,1 +73,66,1 +74,66,1 +69,67,1 +71,67,1 +74,67,1 +70,68,1 +72,68,1 +73,68,1 +74,68,1 +70,69,1 +71,69,1 +72,69,1 +73,69,1 +74,69,1 +71,70,1 +72,70,1 +73,70,1 +72,71,1 +73,71,1 +73,72,1 +74,72,1 +74,73,1 diff --git a/tests/data/scale_0.pdf b/tests/data/scale_0.pdf new file mode 100644 index 0000000..1fb5865 Binary files /dev/null and b/tests/data/scale_0.pdf differ diff --git a/tests/data/scale_1.pdf b/tests/data/scale_1.pdf new file mode 100644 index 0000000..e8032b4 Binary files /dev/null and b/tests/data/scale_1.pdf differ diff --git a/tests/data/scan.html b/tests/data/scan.html new file mode 100644 index 0000000..21d3b3b --- /dev/null +++ b/tests/data/scan.html @@ -0,0 +1,71 @@ + + + +
+
+ + \ No newline at end of file diff --git a/tests/data/scan.pdf b/tests/data/scan.pdf new file mode 100644 index 0000000..804c3ab Binary files /dev/null and b/tests/data/scan.pdf differ diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..a6b16ac --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,29 @@ +"""Test cli.""" +from pathlib import Path +from click.testing import CliRunner + +from pygenstability import app + +DATA = Path(__file__).absolute().parent / "data" + + +def test_cli(tmp_path): + runner = CliRunner() + res = runner.invoke( + app.cli, + ["run", str(DATA / "edges.csv"), "--result-file", str(tmp_path / "results.pkl")], + catch_exceptions=False, + ) + assert res.exit_code == 0 + + res = runner.invoke( + app.cli, ["plot_scan", str(tmp_path / "results.pkl")], catch_exceptions=False + ) + assert res.exit_code == 0 + + res = runner.invoke( + app.cli, + ["plot_communities", str(DATA / "edges.csv"), str(tmp_path / "results.pkl")], + catch_exceptions=False, + ) + assert res.exit_code == 0 diff --git a/tests/test_plotting.py b/tests/test_plotting.py new file mode 100644 index 0000000..c6f9110 --- /dev/null +++ b/tests/test_plotting.py @@ -0,0 +1,36 @@ +"""Test plotting module.""" +from pathlib import Path +from diff_pdf_visually import pdfdiff +import filecmp + +from pygenstability import plotting + +DATA = Path(__file__).absolute().parent / "data" + + +def test_plot_scan(results, tmp_path): + plotting.plot_scan(results, figure_name=tmp_path / "scan.pdf") + pdfdiff(DATA / "scan.pdf", tmp_path / "scan.pdf") + + plotting.plot_scan( + results, use_plotly=True, live=False, plotly_filename=str(tmp_path / "scan.html") + ) + filecmp.cmp(DATA / "scan.html", tmp_path / "scan.html") + + +def test_plot_clustered_adjacency(graph, results, tmp_path): + plotting.plot_clustered_adjacency( + graph.toarray(), results, 0, figure_name=tmp_path / "clustered_adjacency.pdf" + ) + pdfdiff(DATA / "clustered_adjacency.pdf", tmp_path / "clustered_adjacency.pdf") + + +def test_plot_communities(graph_nx, results, tmp_path): + plotting.plot_communities(graph_nx, results, tmp_path / "communities") + pdfdiff(DATA / "scale_0.pdf", tmp_path / "communities/scale_0.pdf") + + +def test_plot_optimal_partitions(graph_nx, results, tmp_path): + results["selected_partitions"] = [1] + plotting.plot_optimal_partitions(graph_nx, results, folder=tmp_path / "partitions") + pdfdiff(DATA / "scale_0.pdf", tmp_path / "partitions/scale_1.pdf") diff --git a/tests/test_pygenstability.py b/tests/test_pygenstability.py index 3e3450f..b1b34a3 100644 --- a/tests/test_pygenstability.py +++ b/tests/test_pygenstability.py @@ -75,3 +75,24 @@ def test__get_scales(): def test_evaluate_NVI(): """Test evaluate_NVI.""" assert pgs.evaluate_NVI([0, 1], [[1, 1, 1, 1], [1, 1, 1, 1]]) == 0.0 + assert pgs.evaluate_NVI([0, 1], [[0, 0, 1, 1], [1, 1, 1, 1]]) == 1.0 + + +def test_evaluate_louvain(graph): + constructor = load_constructor("continuous_combinatorial", graph) + data = constructor.get_data(1) + quality_indices, quality_values = pgs._to_indices(data["quality"]) + stability, community_id = pgs.evaluate_louvain( + 0, quality_indices, quality_values, data["null_model"], 0 + ) + assert_almost_equal(stability, 0.5590341906608186) + assert community_id == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + + +def test_evaluate_quality(graph): + constructor = load_constructor("continuous_combinatorial", graph) + data = constructor.get_data(1) + community_id = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + qualities_index = pgs._to_indices(data["quality"]) + quality = pgs.evaluate_quality(community_id, qualities_index, data["null_model"], 0) + assert_almost_equal(quality, 0.5590341906608186)