comparison env/lib/python3.7/site-packages/networkx/utils/misc.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 """
2 Miscellaneous Helpers for NetworkX.
3
4 These are not imported into the base networkx namespace but
5 can be accessed, for example, as
6
7 >>> import networkx
8 >>> networkx.utils.is_string_like('spam')
9 True
10 """
11 # Authors: Aric Hagberg (hagberg@lanl.gov),
12 # Dan Schult(dschult@colgate.edu),
13 # Ben Edwards(bedwards@cs.unm.edu)
14
15 # Copyright (C) 2004-2019 by
16 # Aric Hagberg <hagberg@lanl.gov>
17 # Dan Schult <dschult@colgate.edu>
18 # Pieter Swart <swart@lanl.gov>
19 # All rights reserved.
20 # BSD license.
21 from collections import defaultdict
22 from collections import deque
23 import warnings
24 import sys
25 import uuid
26 from itertools import tee, chain
27 import networkx as nx
28
29 # itertools.accumulate is only available on Python 3.2 or later.
30 #
31 # Once support for Python versions less than 3.2 is dropped, this code should
32 # be removed.
33 try:
34 from itertools import accumulate
35 except ImportError:
36 import operator
37
38 # The code for this function is from the Python 3.5 documentation,
39 # distributed under the PSF license:
40 # <https://docs.python.org/3.5/library/itertools.html#itertools.accumulate>
41 def accumulate(iterable, func=operator.add):
42 it = iter(iterable)
43 try:
44 total = next(it)
45 except StopIteration:
46 return
47 yield total
48 for element in it:
49 total = func(total, element)
50 yield total
51
52 # 2.x/3.x compatibility
53 try:
54 basestring
55 except NameError:
56 basestring = str
57 unicode = str
58
59 # some cookbook stuff
60 # used in deciding whether something is a bunch of nodes, edges, etc.
61 # see G.add_nodes and others in Graph Class in networkx/base.py
62
63
64 def is_string_like(obj): # from John Hunter, types-free version
65 """Check if obj is string."""
66 return isinstance(obj, basestring)
67
68
69 def iterable(obj):
70 """ Return True if obj is iterable with a well-defined len()."""
71 if hasattr(obj, "__iter__"):
72 return True
73 try:
74 len(obj)
75 except:
76 return False
77 return True
78
79
80 def flatten(obj, result=None):
81 """ Return flattened version of (possibly nested) iterable object. """
82 if not iterable(obj) or is_string_like(obj):
83 return obj
84 if result is None:
85 result = []
86 for item in obj:
87 if not iterable(item) or is_string_like(item):
88 result.append(item)
89 else:
90 flatten(item, result)
91 return obj.__class__(result)
92
93
94 def make_list_of_ints(sequence):
95 """Return list of ints from sequence of integral numbers.
96
97 All elements of the sequence must satisfy int(element) == element
98 or a ValueError is raised. Sequence is iterated through once.
99
100 If sequence is a list, the non-int values are replaced with ints.
101 So, no new list is created
102 """
103 msg = 'sequence is not all integers: %s'
104 if not isinstance(sequence, list):
105 result = []
106 for i in sequence:
107 try:
108 ii = int(i)
109 except ValueError:
110 raise nx.NetworkXError(msg % i) from None
111 if ii != i:
112 raise nx.NetworkXError(msg % i)
113 result.append(ii)
114 return result
115 # original sequence is a list... in-place conversion to ints
116 for indx, i in enumerate(sequence):
117 if isinstance(i, int):
118 continue
119 try:
120 ii = int(i)
121 except ValueError:
122 raise nx.NetworkXError(msg % i) from None
123 if ii != i:
124 raise nx.NetworkXError(msg % i)
125 sequence[indx] = ii
126 return sequence
127
128
129 def is_list_of_ints(intlist):
130 """ Return True if list is a list of ints. """
131 if not isinstance(intlist, list):
132 return False
133 for i in intlist:
134 if not isinstance(i, int):
135 return False
136 return True
137
138
139 PY2 = sys.version_info[0] == 2
140 if PY2:
141 def make_str(x):
142 """Returns the string representation of t."""
143 if isinstance(x, unicode):
144 return x
145 else:
146 # Note, this will not work unless x is ascii-encoded.
147 # That is good, since we should be working with unicode anyway.
148 # Essentially, unless we are reading a file, we demand that users
149 # convert any encoded strings to unicode before using the library.
150 #
151 # Also, the str() is necessary to convert integers, etc.
152 # unicode(3) works, but unicode(3, 'unicode-escape') wants a buffer
153 #
154 return unicode(str(x), 'unicode-escape')
155 else:
156 def make_str(x):
157 """Returns the string representation of t."""
158 return str(x)
159
160
161 def generate_unique_node():
162 """ Generate a unique node label."""
163 return str(uuid.uuid1())
164
165
166 def default_opener(filename):
167 """Opens `filename` using system's default program.
168
169 Parameters
170 ----------
171 filename : str
172 The path of the file to be opened.
173
174 """
175 from subprocess import call
176
177 cmds = {'darwin': ['open'],
178 'linux': ['xdg-open'],
179 'linux2': ['xdg-open'],
180 'win32': ['cmd.exe', '/C', 'start', '']}
181 cmd = cmds[sys.platform] + [filename]
182 call(cmd)
183
184
185 def dict_to_numpy_array(d, mapping=None):
186 """Convert a dictionary of dictionaries to a numpy array
187 with optional mapping."""
188 try:
189 return dict_to_numpy_array2(d, mapping)
190 except (AttributeError, TypeError):
191 # AttributeError is when no mapping was provided and v.keys() fails.
192 # TypeError is when a mapping was provided and d[k1][k2] fails.
193 return dict_to_numpy_array1(d, mapping)
194
195
196 def dict_to_numpy_array2(d, mapping=None):
197 """Convert a dictionary of dictionaries to a 2d numpy array
198 with optional mapping.
199
200 """
201 import numpy
202 if mapping is None:
203 s = set(d.keys())
204 for k, v in d.items():
205 s.update(v.keys())
206 mapping = dict(zip(s, range(len(s))))
207 n = len(mapping)
208 a = numpy.zeros((n, n))
209 for k1, i in mapping.items():
210 for k2, j in mapping.items():
211 try:
212 a[i, j] = d[k1][k2]
213 except KeyError:
214 pass
215 return a
216
217
218 def dict_to_numpy_array1(d, mapping=None):
219 """Convert a dictionary of numbers to a 1d numpy array
220 with optional mapping.
221
222 """
223 import numpy
224 if mapping is None:
225 s = set(d.keys())
226 mapping = dict(zip(s, range(len(s))))
227 n = len(mapping)
228 a = numpy.zeros(n)
229 for k1, i in mapping.items():
230 i = mapping[k1]
231 a[i] = d[k1]
232 return a
233
234
235 def is_iterator(obj):
236 """Returns True if and only if the given object is an iterator
237 object.
238
239 """
240 has_next_attr = hasattr(obj, '__next__') or hasattr(obj, 'next')
241 return iter(obj) is obj and has_next_attr
242
243
244 def arbitrary_element(iterable):
245 """Returns an arbitrary element of `iterable` without removing it.
246
247 This is most useful for "peeking" at an arbitrary element of a set,
248 but can be used for any list, dictionary, etc., as well::
249
250 >>> arbitrary_element({3, 2, 1})
251 1
252 >>> arbitrary_element('hello')
253 'h'
254
255 This function raises a :exc:`ValueError` if `iterable` is an
256 iterator (because the current implementation of this function would
257 consume an element from the iterator)::
258
259 >>> iterator = iter([1, 2, 3])
260 >>> arbitrary_element(iterator)
261 Traceback (most recent call last):
262 ...
263 ValueError: cannot return an arbitrary item from an iterator
264
265 """
266 if is_iterator(iterable):
267 raise ValueError('cannot return an arbitrary item from an iterator')
268 # Another possible implementation is ``for x in iterable: return x``.
269 return next(iter(iterable))
270
271
272 # Recipe from the itertools documentation.
273 def consume(iterator):
274 "Consume the iterator entirely."
275 # Feed the entire iterator into a zero-length deque.
276 deque(iterator, maxlen=0)
277
278
279 # Recipe from the itertools documentation.
280 def pairwise(iterable, cyclic=False):
281 "s -> (s0, s1), (s1, s2), (s2, s3), ..."
282 a, b = tee(iterable)
283 first = next(b, None)
284 if cyclic is True:
285 return zip(a, chain(b, (first,)))
286 return zip(a, b)
287
288
289 def groups(many_to_one):
290 """Converts a many-to-one mapping into a one-to-many mapping.
291
292 `many_to_one` must be a dictionary whose keys and values are all
293 :term:`hashable`.
294
295 The return value is a dictionary mapping values from `many_to_one`
296 to sets of keys from `many_to_one` that have that value.
297
298 For example::
299
300 >>> from networkx.utils import groups
301 >>> many_to_one = {'a': 1, 'b': 1, 'c': 2, 'd': 3, 'e': 3}
302 >>> groups(many_to_one) # doctest: +SKIP
303 {1: {'a', 'b'}, 2: {'c'}, 3: {'d', 'e'}}
304
305 """
306 one_to_many = defaultdict(set)
307 for v, k in many_to_one.items():
308 one_to_many[k].add(v)
309 return dict(one_to_many)
310
311
312 def to_tuple(x):
313 """Converts lists to tuples.
314
315 For example::
316
317 >>> from networkx.utils import to_tuple
318 >>> a_list = [1, 2, [1, 4]]
319 >>> to_tuple(a_list)
320 (1, 2, (1, 4))
321
322 """
323 if not isinstance(x, (tuple, list)):
324 return x
325 return tuple(map(to_tuple, x))
326
327
328 def create_random_state(random_state=None):
329 """Returns a numpy.random.RandomState instance depending on input.
330
331 Parameters
332 ----------
333 random_state : int or RandomState instance or None optional (default=None)
334 If int, return a numpy.random.RandomState instance set with seed=int.
335 if numpy.random.RandomState instance, return it.
336 if None or numpy.random, return the global random number generator used
337 by numpy.random.
338 """
339 import numpy as np
340
341 if random_state is None or random_state is np.random:
342 return np.random.mtrand._rand
343 if isinstance(random_state, np.random.RandomState):
344 return random_state
345 if isinstance(random_state, int):
346 return np.random.RandomState(random_state)
347 msg = '%r cannot be used to generate a numpy.random.RandomState instance'
348 raise ValueError(msg % random_state)
349
350
351 class PythonRandomInterface(object):
352 try:
353 def __init__(self, rng=None):
354 import numpy
355 if rng is None:
356 self._rng = numpy.random.mtrand._rand
357 self._rng = rng
358 except ImportError:
359 msg = 'numpy not found, only random.random available.'
360 warnings.warn(msg, ImportWarning)
361
362 def random(self):
363 return self._rng.random_sample()
364
365 def uniform(self, a, b):
366 return a + (b - a) * self._rng.random_sample()
367
368 def randrange(self, a, b=None):
369 return self._rng.randint(a, b)
370
371 def choice(self, seq):
372 return seq[self._rng.randint(0, len(seq))]
373
374 def gauss(self, mu, sigma):
375 return self._rng.normal(mu, sigma)
376
377 def shuffle(self, seq):
378 return self._rng.shuffle(seq)
379
380 # Some methods don't match API for numpy RandomState.
381 # Commented out versions are not used by NetworkX
382
383 def sample(self, seq, k):
384 return self._rng.choice(list(seq), size=(k,), replace=False)
385
386 def randint(self, a, b):
387 return self._rng.randint(a, b + 1)
388
389 # exponential as expovariate with 1/argument,
390 def expovariate(self, scale):
391 return self._rng.exponential(1/scale)
392
393 # pareto as paretovariate with 1/argument,
394 def paretovariate(self, shape):
395 return self._rng.pareto(shape)
396
397 # weibull as weibullvariate multiplied by beta,
398 # def weibullvariate(self, alpha, beta):
399 # return self._rng.weibull(alpha) * beta
400 #
401 # def triangular(self, low, high, mode):
402 # return self._rng.triangular(low, mode, high)
403 #
404 # def choices(self, seq, weights=None, cum_weights=None, k=1):
405 # return self._rng.choice(seq
406
407
408 def create_py_random_state(random_state=None):
409 """Returns a random.Random instance depending on input.
410
411 Parameters
412 ----------
413 random_state : int or random number generator or None (default=None)
414 If int, return a random.Random instance set with seed=int.
415 if random.Random instance, return it.
416 if None or the `random` package, return the global random number
417 generator used by `random`.
418 if np.random package, return the global numpy random number
419 generator wrapped in a PythonRandomInterface class.
420 if np.random.RandomState instance, return it wrapped in
421 PythonRandomInterface
422 if a PythonRandomInterface instance, return it
423 """
424 import random
425 try:
426 import numpy as np
427 if random_state is np.random:
428 return PythonRandomInterface(np.random.mtrand._rand)
429 if isinstance(random_state, np.random.RandomState):
430 return PythonRandomInterface(random_state)
431 if isinstance(random_state, PythonRandomInterface):
432 return random_state
433 has_numpy = True
434 except ImportError:
435 has_numpy = False
436
437 if random_state is None or random_state is random:
438 return random._inst
439 if isinstance(random_state, random.Random):
440 return random_state
441 if isinstance(random_state, int):
442 return random.Random(random_state)
443 msg = '%r cannot be used to generate a random.Random instance'
444 raise ValueError(msg % random_state)
445
446
447 # fixture for pytest
448 def setup_module(module):
449 import pytest
450 numpy = pytest.importorskip('numpy')