time_series.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. """
  2. Time Series Graphs
  3. """
  4. import itertools
  5. import networkx as nx
  6. __all__ = ["visibility_graph"]
  7. @nx._dispatchable(graphs=None, returns_graph=True)
  8. def visibility_graph(series):
  9. """
  10. Return a Visibility Graph of an input Time Series.
  11. A visibility graph converts a time series into a graph. The constructed graph
  12. uses integer nodes to indicate which event in the series the node represents.
  13. Edges are formed as follows: consider a bar plot of the series and view that
  14. as a side view of a landscape with a node at the top of each bar. An edge
  15. means that the nodes can be connected by a straight "line-of-sight" without
  16. being obscured by any bars between the nodes.
  17. The resulting graph inherits several properties of the series in its structure.
  18. Thereby, periodic series convert into regular graphs, random series convert
  19. into random graphs, and fractal series convert into scale-free networks [1]_.
  20. Parameters
  21. ----------
  22. series : Sequence[Number]
  23. A Time Series sequence (iterable and sliceable) of numeric values
  24. representing times.
  25. Returns
  26. -------
  27. NetworkX Graph
  28. The Visibility Graph of the input series
  29. Examples
  30. --------
  31. >>> series_list = [range(10), [2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3]]
  32. >>> for s in series_list:
  33. ... g = nx.visibility_graph(s)
  34. ... print(g)
  35. Graph with 10 nodes and 9 edges
  36. Graph with 12 nodes and 18 edges
  37. References
  38. ----------
  39. .. [1] Lacasa, Lucas, Bartolo Luque, Fernando Ballesteros, Jordi Luque, and Juan Carlos Nuno.
  40. "From time series to complex networks: The visibility graph." Proceedings of the
  41. National Academy of Sciences 105, no. 13 (2008): 4972-4975.
  42. https://www.pnas.org/doi/10.1073/pnas.0709247105
  43. """
  44. # Sequential values are always connected
  45. G = nx.path_graph(len(series))
  46. nx.set_node_attributes(G, dict(enumerate(series)), "value")
  47. # Check all combinations of nodes n series
  48. for (n1, t1), (n2, t2) in itertools.combinations(enumerate(series), 2):
  49. # check if any value between obstructs line of sight
  50. slope = (t2 - t1) / (n2 - n1)
  51. offset = t2 - slope * n2
  52. obstructed = any(
  53. t >= slope * n + offset
  54. for n, t in enumerate(series[n1 + 1 : n2], start=n1 + 1)
  55. )
  56. if not obstructed:
  57. G.add_edge(n1, n2)
  58. return G