11"""Time dependent algorithms."""
22
3- from datetime import datetime , timedelta
4-
53import networkx as nx
64from networkx .utils import not_implemented_for
75
119@not_implemented_for ("undirected" )
1210@not_implemented_for ("multigraph" )
1311@nx ._dispatch (node_attrs = {"time" : None , "weight" : 1 })
14- def cd_index (G , node , * , time_delta = timedelta ( days = 5 * 365 ) , time = "time" , weight = None ):
12+ def cd_index (G , node , time_delta , * , time = "time" , weight = None ):
1513 r"""Compute the CD index for `node` within the graph `G`.
1614
1715 Calculates the CD index for the given node of the graph,
@@ -26,12 +24,15 @@ def cd_index(G, node, *, time_delta=timedelta(days=5 * 365), time="time", weight
2624 `weight` attributes (if a weight is not given, it is considered 1).
2725 node : node
2826 The node for which the CD index is calculated.
29- time_delta : timedelta, integer or float (Optional, default is timedelta(days=5*365))
30- Amount of time after the `time` attribute of the `node`.
27+ time_delta : numeric or timedelta
28+ Amount of time after the `time` attribute of the `node`. The value of
29+ `time_delta` must support comparison with the `time` node attribute. For
30+ example, if the `time` attribute of the nodes are `datetime.datetime`
31+ objects, then `time_delta` should be a `datetime.timedelta` object.
3132 time : string (Optional, default is "time")
3233 The name of the node attribute that will be used for the calculations.
3334 weight : string (Optional, default is None)
34- the name of the node attribute used as weight.
35+ The name of the node attribute used as weight.
3536
3637 Returns
3738 -------
@@ -50,6 +51,7 @@ def cd_index(G, node, *, time_delta=timedelta(days=5 * 365), time="time", weight
5051
5152 Examples
5253 --------
54+ >>> from datetime import datetime, timedelta
5355 >>> G = nx.DiGraph()
5456 >>> nodes = {
5557 ... 1: {"time": datetime(2015, 1, 1)},
@@ -61,7 +63,19 @@ def cd_index(G, node, *, time_delta=timedelta(days=5 * 365), time="time", weight
6163 >>> G.add_nodes_from([(n, nodes[n]) for n in nodes])
6264 >>> edges = [(1, 3), (1, 4), (2, 3), (3, 4), (3, 5)]
6365 >>> G.add_edges_from(edges)
64- >>> cd = nx.cd_index(G, 3, time="time", weight="weight")
66+ >>> delta = timedelta(days=5 * 365)
67+ >>> nx.cd_index(G, 3, time_delta=delta, time="time")
68+ 0.5
69+ >>> nx.cd_index(G, 3, time_delta=delta, time="time", weight="weight")
70+ 0.12
71+
72+ Integers can also be used for the time values:
73+ >>> node_times = {1: 2015, 2: 2012, 3: 2010, 4: 2008, 5: 2014}
74+ >>> nx.set_node_attributes(G, node_times, "new_time")
75+ >>> nx.cd_index(G, 3, time_delta=4, time="new_time")
76+ 0.5
77+ >>> nx.cd_index(G, 3, time_delta=4, time="new_time", weight="weight")
78+ 0.12
6579
6680 Notes
6781 -----
@@ -80,6 +94,14 @@ def cd_index(G, node, *, time_delta=timedelta(days=5 * 365), time="time", weight
8094 of forward citations in `i` and `w_{it}` is a matrix of weight for patent `i`
8195 at time `t`.
8296
97+ The `datetime.timedelta` package can lead to off-by-one issues when converting
98+ from years to days. In the example above `timedelta(days=5 * 365)` looks like
99+ 5 years, but it isn't because of leap year days. So it gives the same result
100+ as `timedelta(days=4 * 365)`. But using `timedelta(days=5 * 365 + 1)` gives
101+ a 5 year delta **for this choice of years** but may not if the 5 year gap has
102+ more than 1 leap year. To avoid these issues, use integers to represent years,
103+ or be very careful when you convert units of time.
104+
83105 References
84106 ----------
85107 .. [1] Funk, Russell J., and Jason Owen-Smith.
@@ -99,7 +121,7 @@ def cd_index(G, node, *, time_delta=timedelta(days=5 * 365), time="time", weight
99121 except :
100122 raise nx .NetworkXError (
101123 "Addition and comparison are not supported between 'time_delta' "
102- "and 'time' types, default time_delta = datetime.timedelta ."
124+ "and 'time' types."
103125 )
104126
105127 # -1 if any edge between node's predecessors and node's successors, else 1
0 commit comments