diff options
author | Jarrod Millman <jarrod.millman@gmail.com> | 2017-07-27 13:58:16 -0700 |
---|---|---|
committer | Jarrod Millman <jarrod.millman@gmail.com> | 2017-08-12 22:47:57 -0700 |
commit | e9d8c6b9d64d4b6bbab6b77a7ac285be07146930 (patch) | |
tree | 2d666dad6adb82740408e92461e7f2a91d75da5b | |
parent | fbbfe49a5906ce9ecce4f0d002fc58e89769fd7a (diff) | |
download | networkx-e9d8c6b9d64d4b6bbab6b77a7ac285be07146930.tar.gz |
Create separate functions for df as edge-lists and adjacency matrices
Fixes #2464.
Some related commits:
52eb3d3: from_pandas_dataframe and to_pandas_dataframe were added to work with adjacency matrix.
f0c22dd: from_pandas_dataframe was changed to work with edge-lists, but to_pandas_dataframe was not changed.
-rw-r--r-- | doc/reference/convert.rst | 6 | ||||
-rw-r--r-- | doc/release/release_dev.rst | 8 | ||||
-rw-r--r-- | networkx/convert.py | 17 | ||||
-rw-r--r-- | networkx/convert_matrix.py | 97 | ||||
-rw-r--r-- | networkx/tests/test_convert_pandas.py | 22 |
5 files changed, 126 insertions, 24 deletions
diff --git a/doc/reference/convert.rst b/doc/reference/convert.rst index ab72083d..78645b69 100644 --- a/doc/reference/convert.rst +++ b/doc/reference/convert.rst @@ -54,5 +54,7 @@ Pandas .. autosummary:: :toctree: generated/ - to_pandas_dataframe - from_pandas_dataframe + to_pandas_adjacency + from_pandas_adjacency + to_pandas_edgelist + from_pandas_edgelist diff --git a/doc/release/release_dev.rst b/doc/release/release_dev.rst index 1fe15399..627cf38d 100644 --- a/doc/release/release_dev.rst +++ b/doc/release/release_dev.rst @@ -192,6 +192,14 @@ API Changes Deprecations ------------ +The following deprecated functions will be removed in 2.1. + +- The function ``bellman_ford`` has been deprecated in favor of + ``bellman_ford_predecessor_and_distance``. + +- The functions ``to_pandas_dataframe`` and ``from_pandas_dataframe`` have been + deprecated in favor of ``to_pandas_adjacency``, ``from_pandas_adjacency``, + ``to_pandas_edgelist``, and ``from_pandas_edgelist``. Contributors to this release ---------------------------- diff --git a/networkx/convert.py b/networkx/convert.py index e3a0b502..da639886 100644 --- a/networkx/convert.py +++ b/networkx/convert.py @@ -134,11 +134,18 @@ def to_networkx_graph(data, create_using=None, multigraph_input=False): try: import pandas as pd if isinstance(data, pd.DataFrame): - try: - return nx.from_pandas_dataframe(data, edge_attr=True, create_using=create_using) - except: - msg = "Input is not a correct Pandas DataFrame." - raise nx.NetworkXError(msg) + if data.shape[0] == data.shape[1]: + try: + return nx.from_pandas_adjacency(data, create_using=create_using) + except: + msg = "Input is not a correct Pandas DataFrame adjacency matrix." + raise nx.NetworkXError(msg) + else: + try: + return nx.from_pandas_edgelist(data, edge_attr=True, create_using=create_using) + except: + msg = "Input is not a correct Pandas DataFrame edge-list." + raise nx.NetworkXError(msg) except ImportError: msg = 'pandas not found, skipping conversion test.' warnings.warn(msg, ImportWarning) diff --git a/networkx/convert_matrix.py b/networkx/convert_matrix.py index 69cbfba3..b0490f09 100644 --- a/networkx/convert_matrix.py +++ b/networkx/convert_matrix.py @@ -26,7 +26,7 @@ nx_agraph, nx_pydot # Pieter Swart <swart@lanl.gov> # All rights reserved. # BSD license. -import warnings +import warnings as _warnings import itertools import networkx as nx from networkx.convert import _prep_create_using @@ -36,6 +36,8 @@ __author__ = """\n""".join(['Aric Hagberg <aric.hagberg@gmail.com>', 'Dan Schult(dschult@colgate.edu)']) __all__ = ['from_numpy_matrix', 'to_numpy_matrix', 'from_pandas_dataframe', 'to_pandas_dataframe', + 'from_pandas_adjacency', 'to_pandas_adjacency', + 'from_pandas_edgelist', 'to_pandas_edgelist', 'to_numpy_recarray', 'from_scipy_sparse_matrix', 'to_scipy_sparse_matrix', 'from_numpy_array', 'to_numpy_array'] @@ -43,6 +45,15 @@ __all__ = ['from_numpy_matrix', 'to_numpy_matrix', def to_pandas_dataframe(G, nodelist=None, dtype=None, order=None, multigraph_weight=sum, weight='weight', nonedge=0.0): + """DEPRECATED: Replaced by ``to_pandas_adjacency``.""" + msg = "to_pandas_dataframe is deprecated and will be removed" \ + "in 2.1, use to_pandas_adjacency instead." + _warnings.warn(msg, DeprecationWarning) + return to_pandas_adjacency(G, nodelist, dtype, order, multigraph_weight, weight, nonedge) + + +def to_pandas_adjacency(G, nodelist=None, dtype=None, order=None, + multigraph_weight=sum, weight='weight', nonedge=0.0): """Return the graph adjacency matrix as a Pandas DataFrame. Parameters @@ -94,7 +105,7 @@ def to_pandas_dataframe(G, nodelist=None, dtype=None, order=None, >>> import pandas as pd >>> import numpy as np >>> G = nx.Graph([(1,1)]) - >>> df = nx.to_pandas_dataframe(G, dtype=int) + >>> df = nx.to_pandas_adjacency(G, dtype=int) >>> df 1 1 1 @@ -114,7 +125,7 @@ def to_pandas_dataframe(G, nodelist=None, dtype=None, order=None, 0 >>> G.add_edge(2,2) 1 - >>> nx.to_pandas_dataframe(G, nodelist=[0,1,2], dtype=int) + >>> nx.to_pandas_adjacency(G, nodelist=[0,1,2], dtype=int) 0 1 2 0 0 2 0 1 1 0 0 @@ -129,8 +140,82 @@ def to_pandas_dataframe(G, nodelist=None, dtype=None, order=None, return pd.DataFrame(data=M, index=nodelist, columns=nodelist) +def from_pandas_adjacency(df, create_using=None): + """Return a graph from Pandas DataFrame. + + The Pandas DataFrame is interpreted as an adjacency matrix for the graph. + + Parameters + ---------- + df : Pandas DataFrame + An adjacency matrix representation of a graph + + create_using : NetworkX graph + Use specified graph for result. The default is Graph() + + Notes + ----- + If the numpy matrix has a single data type for each matrix entry it + will be converted to an appropriate Python data type. + + If the numpy matrix has a user-specified compound data type the names + of the data fields will be used as attribute keys in the resulting + NetworkX graph. + + See Also + -------- + to_pandas_adjacency + + Examples + -------- + Simple integer weights on edges: + + >>> import numpy as np + >>> import pandas as pd + >>> df = pd.DataFrame([[1, 1], [2, 1]]) + >>> df + 0 1 + 0 1 1 + 1 2 1 + >>> G = nx.from_pandas_adjacency(df) + >>> print(nx.info(G)) + Name: + Type: Graph + Number of nodes: 2 + Number of edges: 3 + Average degree: 3.0000 + """ + + A = df.values + G = from_numpy_matrix(A, create_using) + try: + df = df[df.index] + except: + raise nx.NetworkXError("Columns must match Indices.", + "%s not in columns"%list(set(df.index).difference(set(df.columns)))) + + nx.relabel.relabel_nodes(G, dict(enumerate(df.columns)), copy=False) + return G + + +def to_pandas_edgelist(G, nodelist=None, dtype=None, order=None, + multigraph_weight=sum, weight='weight', nonedge=0.0): + """Placeholder + """ + return NotImplemented + + def from_pandas_dataframe(df, source='source', target='target', edge_attr=None, create_using=None): + """DEPRECATED: Replaced by ``from_pandas_edgelist``.""" + msg = "from_pandas_dataframe is deprecated and will be removed" \ + "in 2.1, use from_pandas_edgelist instead." + _warnings.warn(msg, DeprecationWarning) + return from_pandas_edgelist(df, source, target, edge_attr, create_using) + + +def from_pandas_edgelist(df, source='source', target='target', edge_attr=None, + create_using=None): """Return a graph from Pandas DataFrame containing an edge list. The Pandas DataFrame should contain at least two columns of node names and @@ -166,7 +251,7 @@ def from_pandas_dataframe(df, source='source', target='target', edge_attr=None, See Also -------- - to_pandas_dataframe + to_pandas_edgelist Examples -------- @@ -186,7 +271,7 @@ def from_pandas_dataframe(df, source='source', target='target', edge_attr=None, 0 4 7 A D 1 7 1 B A 2 10 9 C E - >>> G=nx.from_pandas_dataframe(df, 0, 'b', ['weight', 'cost']) + >>> G=nx.from_pandas_edgelist(df, 0, 'b', ['weight', 'cost']) >>> G['E']['C']['weight'] 10 >>> G['E']['C']['cost'] @@ -195,7 +280,7 @@ def from_pandas_dataframe(df, source='source', target='target', edge_attr=None, ... 'target': [2, 2, 3], ... 'weight': [3, 4, 5], ... 'color': ['red', 'blue', 'blue']}) - >>> G = nx.from_pandas_dataframe(edges, edge_attr=True) + >>> G = nx.from_pandas_edgelist(edges, edge_attr=True) >>> G[0][2]['color'] 'red' """ diff --git a/networkx/tests/test_convert_pandas.py b/networkx/tests/test_convert_pandas.py index 97a206d1..ed41c77d 100644 --- a/networkx/tests/test_convert_pandas.py +++ b/networkx/tests/test_convert_pandas.py @@ -34,40 +34,40 @@ class TestConvertPandas(object): def assert_equal(self, G1, G2): assert_true(nx.is_isomorphic(G1, G2, edge_match=lambda x, y: x == y)) - def test_from_dataframe_all_attr(self, ): + def test_from_edgelist_all_attr(self, ): Gtrue = nx.Graph([('E', 'C', {'cost': 9, 'weight': 10}), ('B', 'A', {'cost': 1, 'weight': 7}), ('A', 'D', {'cost': 7, 'weight': 4})]) - G = nx.from_pandas_dataframe(self.df, 0, 'b', True) + G = nx.from_pandas_edgelist(self.df, 0, 'b', True) self.assert_equal(G, Gtrue) # MultiGraph MGtrue = nx.MultiGraph(Gtrue) MGtrue.add_edge('A', 'D', cost=16, weight=4) - MG = nx.from_pandas_dataframe(self.mdf, 0, 'b', True, nx.MultiGraph()) + MG = nx.from_pandas_edgelist(self.mdf, 0, 'b', True, nx.MultiGraph()) self.assert_equal(MG, MGtrue) - def test_from_dataframe_multi_attr(self, ): + def test_from_edgelist_multi_attr(self, ): Gtrue = nx.Graph([('E', 'C', {'cost': 9, 'weight': 10}), ('B', 'A', {'cost': 1, 'weight': 7}), ('A', 'D', {'cost': 7, 'weight': 4})]) - G = nx.from_pandas_dataframe(self.df, 0, 'b', ['weight', 'cost']) + G = nx.from_pandas_edgelist(self.df, 0, 'b', ['weight', 'cost']) self.assert_equal(G, Gtrue) - def test_from_dataframe_one_attr(self, ): + def test_from_edgelist_one_attr(self, ): Gtrue = nx.Graph([('E', 'C', {'weight': 10}), ('B', 'A', {'weight': 7}), ('A', 'D', {'weight': 4})]) - G = nx.from_pandas_dataframe(self.df, 0, 'b', 'weight') + G = nx.from_pandas_edgelist(self.df, 0, 'b', 'weight') self.assert_equal(G, Gtrue) - def test_from_dataframe_no_attr(self, ): + def test_from_edgelist_no_attr(self, ): Gtrue = nx.Graph([('E', 'C', {}), ('B', 'A', {}), ('A', 'D', {})]) - G = nx.from_pandas_dataframe(self.df, 0, 'b',) + G = nx.from_pandas_edgelist(self.df, 0, 'b',) self.assert_equal(G, Gtrue) - def test_from_datafram(self, ): + def test_from_edgelist(self, ): # Pandas DataFrame g = nx.cycle_graph(10) G = nx.Graph() @@ -81,7 +81,7 @@ class TestConvertPandas(object): edges = pd.DataFrame({'source': source, 'target': target, 'weight': weight}) - GG = nx.from_pandas_dataframe(edges, edge_attr='weight') + GG = nx.from_pandas_edgelist(edges, edge_attr='weight') assert_nodes_equal(sorted(G.nodes()), sorted(GG.nodes())) assert_edges_equal(sorted(G.edges()), sorted(GG.edges())) GW = nx.to_networkx_graph(edges, create_using=nx.Graph()) |