简体   繁体   中英

Find all possible paths in a graph with a time constraint

My data looks like the following :

      source  target
time                
0.5    96253   94861
1.0    96652   95091
1.5    94861   95091
2.5    95091   95409
3.5    95409   97221
4.5    97221   96781
5.5    96781   97707
6.5    97707   98191
7.5    98191   99096
8.5    99096  100016
8.5    99096  100013
9.5   100013   98663
9.5   100016   98658
10.5   98658   99573
10.5   98663   99589
11.5   99589  100506
11.5   99573  100490
  • Each integers inside source and target columns reference a spots.
  • Each row is a link between two spots.
  • The time index refers to the time point in which the link can be found.

A smart algorithm would be to found all the possible trajectories contained inside the dataset.

For example in the previous example, 4 trajectories exist :

[96253, 94861, 95091, 95409, 97221, 97221, 96781, 97707, 98191, 99096, 100016, 98658, 99573, 100490]
[96652, 95091, 95409, 97221, 97221, 96781, 97707, 98191, 99096, 100016, 98658, 99573, 100490]
[96253, 94861, 95091, 95409, 97221, 97221, 96781, 97707, 98191, 99096, 100013, 98663, 99589, 100506]
[96652, 95091, 95409, 97221, 97221, 96781, 97707, 98191, 99096, 100013, 98663, 99589, 100506]

This problem can be resumed as a graph theory problem. See the below graph which corresponds to the data showed at the beginning.

在此处输入图片说明

The idea would be to find all possible paths in this graph with a constraint respecting time logic : a trajectory is an ordered list of spots (nodes) which can only go from t to t+1 (can't go in past).


The algorithm will be implemented in Python. So any Python tricks are allowed :-)

Applying graph theory algorithm seems to be the smart way to resolve this. I used networkx python library.

print(spot_ids)

output :

      source  target
time                
0.5    96253   94861
1.0    96652   95091
1.5    94861   95091
2.5    95091   95409
3.5    95409   97221
4.5    97221   96781
5.5    96781   97707
6.5    97707   98191
7.5    98191   99096
8.5    99096  100016
8.5    99096  100013
9.5   100013   98663
9.5   100016   98658
10.5   98658   99573
10.5   98663   99589
11.5   99589  100506
11.5   99573  100490

The algorithm :

import itertools
import networkx as nx

# Build graph
graph = nx.Graph()
for t, spot in spot_ids.iterrows():
    graph.add_edge(int(spot['source']), int(spot['target']), attr_dict=dict(t=t))

# Find graph extremities by checking if number of neighbors is equal to 1
tracks_extremities = [node for node in graph.nodes() if len(graph.neighbors(node)) == 1]
tracks_extremities

paths = []
# Find all possible paths between extremities
for source, target in itertools.combinations(tracks_extremities, 2):

    # Find all path between two nodes
    for path in nx.all_simple_paths(graph, source=source, target=target):

        # Now we need to check wether this path respect the time logic contraint
        # edges can only go in one direction of the time

        # Build times vector according to path
        t = []
        for i, node_srce in enumerate(path[:-1]):
            node_trgt = path[i+1]
            t.append(graph.edge[node_srce][node_trgt]['t'])

        # Will be equal to 1 if going to one time direction
        if len(np.unique(np.sign(np.diff(t)))) == 1:
            paths.append(path)

for path in paths:
    print(path)

output :

[100490, 99573, 98658, 100016, 99096, 98191, 97707, 96781, 97221, 95409, 95091, 96652]
[100490, 99573, 98658, 100016, 99096, 98191, 97707, 96781, 97221, 95409, 95091, 94861, 96253]
[96652, 95091, 95409, 97221, 96781, 97707, 98191, 99096, 100013, 98663, 99589, 100506]
[100506, 99589, 98663, 100013, 99096, 98191, 97707, 96781, 97221, 95409, 95091, 94861, 96253]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM