Statistics
| Branch: | Revision:

iof-tools / networkxMiCe / networkx-master / networkx / readwrite / pajek.py @ 5cef0f13

History | View | Annotate | Download (8.63 KB)

1
#    Copyright (C) 2008-2014 by
2
#    Aric Hagberg <hagberg@lanl.gov>
3
#    Dan Schult <dschult@colgate.edu>
4
#    Pieter Swart <swart@lanl.gov>
5
#    All rights reserved.
6
#    BSD license.
7
#
8
# Authors: Aric Hagberg (hagberg@lanl.gov)
9
"""
10
*****
11
Pajek
12
*****
13
Read graphs in Pajek format.
14

15
This implementation handles directed and undirected graphs including
16
those with self loops and parallel edges.
17

18
Format
19
------
20
See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
21
for format information.
22

23
"""
24

    
25
import warnings
26

    
27
import networkx as nx
28
from networkx.utils import is_string_like, open_file, make_str
29

    
30
__all__ = ['read_pajek', 'parse_pajek', 'generate_pajek', 'write_pajek']
31

    
32

    
33
def generate_pajek(G):
34
    """Generate lines in Pajek graph format.
35

36
    Parameters
37
    ----------
38
    G : graph
39
       A Networkx graph
40

41
    References
42
    ----------
43
    See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
44
    for format information.
45
    """
46
    if G.name == '':
47
        name = 'NetworkX'
48
    else:
49
        name = G.name
50
    # Apparently many Pajek format readers can't process this line
51
    # So we'll leave it out for now.
52
    # yield '*network %s'%name
53

    
54
    # write nodes with attributes
55
    yield '*vertices %s' % (G.order())
56
    nodes = list(G)
57
    # make dictionary mapping nodes to integers
58
    nodenumber = dict(zip(nodes, range(1, len(nodes) + 1)))
59
    for n in nodes:
60
        # copy node attributes and pop mandatory attributes
61
        # to avoid duplication.
62
        na = G.nodes.get(n, {}).copy()
63
        x = na.pop('x', 0.0)
64
        y = na.pop('y', 0.0)
65
        id = int(na.pop('id', nodenumber[n]))
66
        nodenumber[n] = id
67
        shape = na.pop('shape', 'ellipse')
68
        s = ' '.join(map(make_qstr, (id, n, x, y, shape)))
69
        # only optional attributes are left in na.
70
        for k, v in na.items():
71
            if is_string_like(v) and v.strip() != '':
72
                s += ' %s %s' % (make_qstr(k), make_qstr(v))
73
            else:
74
                warnings.warn('Node attribute %s is not processed. %s.' %
75
                              (k,
76
                               'Empty attribute' if is_string_like(v) else
77
                               'Non-string attribute'))
78
        yield s
79

    
80
    # write edges with attributes
81
    if G.is_directed():
82
        yield '*arcs'
83
    else:
84
        yield '*edges'
85
    for u, v, edgedata in G.edges(data=True):
86
        d = edgedata.copy()
87
        value = d.pop('weight', 1.0)  # use 1 as default edge value
88
        s = ' '.join(map(make_qstr, (nodenumber[u], nodenumber[v], value)))
89
        for k, v in d.items():
90
            if is_string_like(v) and v.strip() != '':
91
                s += ' %s %s' % (make_qstr(k), make_qstr(v))
92
            else:
93
                warnings.warn('Edge attribute %s is not processed. %s.' %
94
                              (k,
95
                               'Empty attribute' if is_string_like(v) else
96
                               'Non-string attribute'))
97
        yield s
98

    
99

    
100
@open_file(1, mode='wb')
101
def write_pajek(G, path, encoding='UTF-8'):
102
    """Write graph in Pajek format to path.
103

104
    Parameters
105
    ----------
106
    G : graph
107
       A Networkx graph
108
    path : file or string
109
       File or filename to write.
110
       Filenames ending in .gz or .bz2 will be compressed.
111

112
    Examples
113
    --------
114
    >>> G=nx.path_graph(4)
115
    >>> nx.write_pajek(G, "test.net")
116

117
    Warnings
118
    --------
119
    Optional node attributes and edge attributes must be non-empty strings.
120
    Otherwise it will not be written into the file. You will need to
121
    convert those attributes to strings if you want to keep them.
122

123
    References
124
    ----------
125
    See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
126
    for format information.
127
    """
128
    for line in generate_pajek(G):
129
        line += '\n'
130
        path.write(line.encode(encoding))
131

    
132

    
133
@open_file(0, mode='rb')
134
def read_pajek(path, encoding='UTF-8'):
135
    """Read graph in Pajek format from path.
136

137
    Parameters
138
    ----------
139
    path : file or string
140
       File or filename to write.
141
       Filenames ending in .gz or .bz2 will be uncompressed.
142

143
    Returns
144
    -------
145
    G : NetworkX MultiGraph or MultiDiGraph.
146

147
    Examples
148
    --------
149
    >>> G=nx.path_graph(4)
150
    >>> nx.write_pajek(G, "test.net")
151
    >>> G=nx.read_pajek("test.net")
152

153
    To create a Graph instead of a MultiGraph use
154

155
    >>> G1=nx.Graph(G)
156

157
    References
158
    ----------
159
    See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
160
    for format information.
161
    """
162
    lines = (line.decode(encoding) for line in path)
163
    return parse_pajek(lines)
164

    
165

    
166
def parse_pajek(lines):
167
    """Parse Pajek format graph from string or iterable.
168

169
    Parameters
170
    ----------
171
    lines : string or iterable
172
       Data in Pajek format.
173

174
    Returns
175
    -------
176
    G : NetworkX graph
177

178
    See Also
179
    --------
180
    read_pajek()
181

182
    """
183
    import shlex
184
    # multigraph=False
185
    if is_string_like(lines):
186
        lines = iter(lines.split('\n'))
187
    lines = iter([line.rstrip('\n') for line in lines])
188
    G = nx.MultiDiGraph()  # are multiedges allowed in Pajek? assume yes
189
    labels = []  # in the order of the file, needed for matrix
190
    while lines:
191
        try:
192
            l = next(lines)
193
        except:  # EOF
194
            break
195
        if l.lower().startswith("*network"):
196
            try:
197
                label, name = l.split(None, 1)
198
            except ValueError:
199
                # Line was not of the form:  *network NAME
200
                pass
201
            else:
202
                G.graph['name'] = name
203
        elif l.lower().startswith("*vertices"):
204
            nodelabels = {}
205
            l, nnodes = l.split()
206
            for i in range(int(nnodes)):
207
                l = next(lines)
208
                try:
209
                    splitline = [x.decode('utf-8') for x in
210
                                 shlex.split(make_str(l).encode('utf-8'))]
211
                except AttributeError:
212
                    splitline = shlex.split(str(l))
213
                id, label = splitline[0:2]
214
                labels.append(label)
215
                G.add_node(label)
216
                nodelabels[id] = label
217
                G.nodes[label]['id'] = id
218
                try:
219
                    x, y, shape = splitline[2:5]
220
                    G.nodes[label].update({'x': float(x),
221
                                           'y': float(y),
222
                                           'shape': shape})
223
                except:
224
                    pass
225
                extra_attr = zip(splitline[5::2], splitline[6::2])
226
                G.nodes[label].update(extra_attr)
227
        elif l.lower().startswith("*edges") or l.lower().startswith("*arcs"):
228
            if l.lower().startswith("*edge"):
229
                # switch from multidigraph to multigraph
230
                G = nx.MultiGraph(G)
231
            if l.lower().startswith("*arcs"):
232
                # switch to directed with multiple arcs for each existing edge
233
                G = G.to_directed()
234
            for l in lines:
235
                try:
236
                    splitline = [x.decode('utf-8') for x in
237
                                 shlex.split(make_str(l).encode('utf-8'))]
238
                except AttributeError:
239
                    splitline = shlex.split(str(l))
240

    
241
                if len(splitline) < 2:
242
                    continue
243
                ui, vi = splitline[0:2]
244
                u = nodelabels.get(ui, ui)
245
                v = nodelabels.get(vi, vi)
246
                # parse the data attached to this edge and put in a dictionary
247
                edge_data = {}
248
                try:
249
                    # there should always be a single value on the edge?
250
                    w = splitline[2:3]
251
                    edge_data.update({'weight': float(w[0])})
252
                except:
253
                    pass
254
                    # if there isn't, just assign a 1
255
#                    edge_data.update({'value':1})
256
                extra_attr = zip(splitline[3::2], splitline[4::2])
257
                edge_data.update(extra_attr)
258
                # if G.has_edge(u,v):
259
                #     multigraph=True
260
                G.add_edge(u, v, **edge_data)
261
        elif l.lower().startswith("*matrix"):
262
            G = nx.DiGraph(G)
263
            adj_list = ((labels[row], labels[col], {'weight': int(data)})
264
                        for (row, line) in enumerate(lines)
265
                        for (col, data) in enumerate(line.split())
266
                        if int(data) != 0)
267
            G.add_edges_from(adj_list)
268

    
269
    return G
270

    
271

    
272
def make_qstr(t):
273
    """Returns the string representation of t.
274
    Add outer double-quotes if the string has a space.
275
    """
276
    if not is_string_like(t):
277
        t = str(t)
278
    if " " in t:
279
        t = r'"%s"' % t
280
    return t
281

    
282

    
283
# fixture for nose tests
284
def teardown_module(module):
285
    import os
286
    os.unlink('test.net')