Find Networkx path by a sequence of nodes - python

I want to find all the path that starts from a sequence of nodes, the network looks like this:
DG = nx.DiGraph()
attrs = {(1, 2), (2,3), (2,4), (4,5), (5, 6), (3,6), (6,7)}
DG.add_edges_from(attrs)
What I expect is a list of all path that starts from (1,2,3) and a list (1,2,4):
all_path = [[1, 2, 3], [1, 2, 3, 6], [1, 2, 3, 6, 7]] , [[1, 2, 4], [1, 2, 4, 5, 6], [1, 2, 4, 5, 6, 7], [1, 2, 4, 5]]
Any help would be appreciated!

There's some reading between the lines that you've left for us. For example, are you specifically looking for paths that end in node 7 in this case, or are you just looking for any path that has nowhere else to go (i.e. ends in a node of outdegree zero)? If the path contains a cycle, do we include paths that go around the cycle before ending?
My guess is that what you're really interested in is the set of all simple paths from a given node that end in a node with outdegree zero. With that in mind, here's some code you might find useful.
start = 1
ends = [n for n in DG.nodes if DG.out_degree[n] == 0]
paths = [p for e in ends
for p in nx.all_simple_paths(DG, source = start, target = e)]
print(paths)
Output: [[1, 2, 4, 5, 6, 7], [1, 2, 3, 6, 7]]
If you want paths that start from 1 and end in any other node, you could do the following.
start = 1
paths = [p for e in DG.nodes
for p in nx.all_simple_paths(DG, source = start, target = e)]
print(paths)
Output: [[1, 2], [1, 2, 4], [1, 2, 3], [1, 2, 4, 5, 6], [1, 2, 3, 6], [1, 2, 4, 5, 6, 7], [1, 2, 3, 6, 7], [1, 2, 4, 5]].
If you like, you can take this output and filter down to the paths that start with any 3 nodes. For example,
print([p for p in paths if p[:3] == [1,2,3]])
print([p for p in paths if p[:3] == [1,2,4]])
results in the outputs [[1, 2, 3], [1, 2, 3, 6], [1, 2, 3, 6, 7]] and [[1, 2, 4], [1, 2, 4, 5, 6], [1, 2, 4, 5, 6, 7], [1, 2, 4, 5]].
Alternatively, sorting paths naturally organizes the paths according to their first nodes. In particular, sorted(paths) is the list
[[1, 2],
[1, 2, 3],
[1, 2, 3, 6],
[1, 2, 3, 6, 7],
[1, 2, 4],
[1, 2, 4, 5],
[1, 2, 4, 5, 6],
[1, 2, 4, 5, 6, 7]]
which you could split into two pieces.

Related

Convert lists into higher dimension so that elements can be reached by following

I have a matrix that I want to convert to 3D so that I can be able to print the element of list[i][j][k]
a = [[[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 2, 3, 4,5], [3, 2, 3, 4, 3, 4], [4, 3, 4, 5, 4, 3], [5, 4, 5, 6, 5, 4]]]
print(a[5][5][0]) # I want to be able to print in 3D`
I get right output if I do a[5][5] but wrong when I add the [0]. Is there anyway of converting my matrix such that this will be solved?
I tried to just wrap the list up with brackets [list], but it did not work. I also did:
b = [[i] for i in a]
which gave me [[[0,1,2,3,4,5]],[[1,2,3,4,5,6]],...
and it still did not work!
NOTE: I want the i to be the row, j to be the column and k to be 0 or 1, so k = 0 (in which case the value is the row index of the cell is pointing to), or the k = 1 (the value is the column index).
Tried to reproduce your issue. To me, it works if you use the right index. Here, it perfectly works if you do for instance
print(a[0][0][5]) # I want to be able to print in 3D`
for list a = [[[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 2, 3, 4,5], [3, 2, 3, 4, 3, 4], [4, 3, 4, 5, 4, 3], [5, 4, 5, 6, 5, 4]]] you have just a[0][n][n]. You can try a[0][5][5]
You have index like below:
a = [
#0 element i
[
#0 element j
[0, 1, 2, 3, 4, 5],
#1 element j
[1, 2, 3, 4, 5, 6],
#2 element j
[2, 3, 2, 3, 4,5],
#3 element j
[3, 2, 3, 4, 3, 4],
#4 element j
[4, 3, 4, 5, 4, 3],
#5 element j
[5, 4, 5, 6, 5, 4]
]
]
print(a[0][5][5]) # a[i][j][k]

Using itertools.product repeat multiple times

I am trying to generate a list of unique lists each 5 elements long, the order is not important but there can't be any repeated elements. The first 3 elements needs to be from [1,2,3,4] and elements 4 and 5 from [5,6,7,8]. for example [1,2,3,7,8] is valid but [1,2,2,7,8] is not nor is [1,2,7,8,9]
The below code works but I am wondering is there a better way of incorporating the product function? something like d = product([L1, repeat=3][L4,repeat=2). From reading the docs the repeat keyword can only be used once, like this: d = product(L1,L4,repeat=2).
Any ideas how i could do this?
Thanks
from itertools import product
L1 = [1,2,3,4]
L2 = [1,2,3,4]
L3 = [1,2,3,4]
L4 = [5,6,7,8]
L5 = [5,6,7,8]
d = product(L1,L2,L3,L4,L5)
result=[]
for x in d:
if x.count(1)<2 and x.count(2)<2 and x.count(3)<2 and x.count(4)<2 and x.count(5)<2 and x.count(6)<2 and x.count(7)<2 and x.count(8)<2:
result.append(sorted(x))
result2 = []
for x in result:
if x not in result2:
result2.append(x)
print(result2)
result2
[[1, 2, 3, 5, 6],
[1, 2, 3, 5, 7],
[1, 2, 3, 5, 8],
[1, 2, 3, 6, 7],
[1, 2, 3, 6, 8],
[1, 2, 3, 7, 8],
[1, 2, 4, 5, 6],
[1, 2, 4, 5, 7],
[1, 2, 4, 5, 8],
[1, 2, 4, 6, 7],
[1, 2, 4, 6, 8],
[1, 2, 4, 7, 8],
[1, 3, 4, 5, 6],
[1, 3, 4, 5, 7],
[1, 3, 4, 5, 8],
[1, 3, 4, 6, 7],
[1, 3, 4, 6, 8],
[1, 3, 4, 7, 8],
[2, 3, 4, 5, 6],
[2, 3, 4, 5, 7],
[2, 3, 4, 5, 8],
[2, 3, 4, 6, 7],
[2, 3, 4, 6, 8],
[2, 3, 4, 7, 8]]
I would instead use itertools.combinations in combination with itertools.product:
from itertools import chain, combinations, product
result = list(
map(
list,
map(
chain.from_iterable,
product(
combinations([1, 2, 3, 4], 3),
combinations([5, 6, 7, 8], 2),
),
),
),
)
the repeat is going to repeat the result two times, in case anyone is wondering about it .
the product takes 1 parameter, the second is optional
for more details :
https://blog.teclado.com/python-itertools-part-1-product/

Form groups in a list based on condition

(Edited based on feedbacks)
I've got a list like this:
my_list = [1,2,3,1,2,4,1,3,5,1,4,6,1,4,7]
That I'm struggling to turn into that:
result = [[1,2,3,1,2,4],[1,3,5],[1,4,6,1,4,7]]
I want to group my_list elements in sublists of 3 elements unless my_list[i] = my_list[i+3] in this case I want to merge those in bigger sublists.
Here is what I've tried:
result = []
for i in range(1,len(my_list),3):
try:
print(my_list[i],my_list[i+3])
if my_list[i] == my_list[i+3]:
result.extend(my_list[i-1:i+5])
else:
result.append(my_list[i-1:i+2])
FWIW, the description of your logic isn't quite clear. However, if I understand your code correctly, I think this is at least something in the correct direction:
def stepper(my_list, step, bigger_step):
res = []
idx = 0
while idx <= len(my_list)-1:
if idx + step > len(my_list)-1:
# Remove this append if you don't want the "leftovers"
res.append(my_list[idx:])
break
if my_list[idx] != my_list[idx+step]:
res.append(my_list[idx:idx+step])
idx += step
else:
res.append(my_list[idx:idx+bigger_step])
idx += bigger_step
return res
my_list = [1,2,3,1,2,4,1,3,5,1,3,6,1,2,7]
print(stepper(my_list, step=3, bigger_step=6)) # Output: [[1, 2, 3, 1, 2, 4], [1, 3, 5, 1, 3, 6], [1, 2, 7]]
Note that the above output is different from your given example, because of your given logic that you've provided makes the second sub-list extended as well as the first.
Using the above code, we can check the results if we change bigger_step easily with a for-loop:
for big in range(4, 10):
print(f"Step: 3, Bigger_Step: {big}, Result:{stepper(my_list, step=3, bigger_step=big)}")
Output:
Step: 3, Bigger_Step: 4, Result:[[1, 2, 3, 1], [2, 4, 1], [3, 5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 5, Result:[[1, 2, 3, 1, 2], [4, 1, 3], [5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 6, Result:[[1, 2, 3, 1, 2, 4], [1, 3, 5, 1, 3, 6], [1, 2, 7]]
Step: 3, Bigger_Step: 7, Result:[[1, 2, 3, 1, 2, 4, 1], [3, 5, 1, 3, 6, 1, 2], [7]]
Step: 3, Bigger_Step: 8, Result:[[1, 2, 3, 1, 2, 4, 1, 3], [5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 9, Result:[[1, 2, 3, 1, 2, 4, 1, 3, 5], [1, 3, 6, 1, 2, 7]]

Removing every 4th, 5th and 6th element in a Python list

I'd like to remove every 4th to 6th elements from a list.
The list is of permutations of 4 numbers, it is as follows:
A = map(list, permutations([1, 2, 3, 4]))
These are the first 12 elements from the output of A[0:12], and the other list is the desired output, with the 4th-to-6th elements removed from this bit of the overall list.
[[1, 2, 3, 4],
[1, 2, 4, 3],
[1, 3, 2, 4],
[1, 3, 4, 2],
[1, 4, 2, 3],
[1, 4, 3, 2],
[2, 1, 3, 4],
[2, 1, 4, 3],
[2, 3, 1, 4],
[2, 3, 4, 1],
[2, 4, 1, 3],
[2, 4, 3, 1]],
After removal, the list should be:
[[1, 2, 3, 4],
[1, 2, 4, 3],
[1, 3, 2, 4],
[2, 1, 3, 4],
[2, 1, 4, 3],
[2, 3, 1, 4]],
But my code outputs an error.
B=A[:4]
B.extend(A[7:])
should do the trick.
TITLE CURRENT READS "removing every 3rd-6th element in a list - pythoN" yet question current reads "I'd like to remove every 4th-6th elements out of a list.", but iether way, this is done the same in every programming language, you have to lop through it, starting from the end, and remove it if the index % 3 == 0, for example, for pseudo-code
variable someList = []
for(variable index = someList.length - 1; index >= 0; index--)
if(index % 3 == 0) delete someList[index]
adapt it accordingly to any programming language, essentially

Permutations excluding inverses [duplicate]

This question already has answers here:
How to generate permutations of a list without "reverse duplicates" in Python using generators
(10 answers)
Closed 5 years ago.
I need to get permutations, excluding mirrored sequences, i.e. (1, 0, 4) should be included, but (4, 0, 1) should be excluded after.
I've came up with the following function but is wondering whether there is simpler solution.
The function skips inverses basing on the fact their last element is the same as the first element of the corresponding sequence, which is already processed given lexicographical order.
def permutations_no_inverse(iterable):
"""Return the half of permutations, treating mirrored sequences as the same,
e.g. (0, 1, 2) and (2, 1, 0) are the same.
Assume itertools.permutations returns tuples
"""
all_perm = it.permutations(iterable)
cur_start = None
starts_processed = set()
for perm in all_perm:
new_start = perm[0]
if new_start != cur_start:
if cur_start != None:
starts_processed.add(cur_start)
cur_start = new_start
if perm[-1] in starts_processed:
continue
else:
yield perm
Assuming the entries in iterable are unique and orderable, I would simply compare any two elements of the permutation (e.g. the first and last one), and only include those permutations where the first element is less or equal than the last element. This way, you don't need to store what you have already seen, and you don't care in what order itertools.permutations() returns the permutations.
Example code:
def permutations_no_inverse(iterable):
for p in itertools.permutations(iterable):
if p[0] <= p[-1]:
yield p
Your question has explicited stated ...wondering whether there is simpler solution, i think the below one is simple enough:
def permutations_no_inverse(iterable):
found_items = []
for p in it.permutations(iterable):
if p not in found_items:
found_items.extend([p, p[::-1]])
else:
yield p
Based on the fact that python delays the reversed tuple-orderings all for the latest columns, we could envisage the permutations set as a uniform matrix, then divide it diagonally, to get the unmirrored part of the matrix.
k=[1, 2, 3,4]
l=itertools.permutations(k)
a=list(l)
b=np.array(a).reshape((len(k),len(a)/len(k),len(k)))
neat_list=np.flipud(b)[np.tril_indices(len(k))]
This should be effective with all array lengths I guess ....
With k=[1,2,3,4,5,6]
This prints
array([
[6, 1, 2, 3, 4, 5],
[5, 1, 2, 3, 4, 6],
[5, 1, 2, 3, 6, 4],
[4, 1, 2, 3, 5, 6],
[4, 1, 2, 3, 6, 5],
[4, 1, 2, 5, 3, 6],
[3, 1, 2, 4, 5, 6],
[3, 1, 2, 4, 6, 5],
[3, 1, 2, 5, 4, 6],
[3, 1, 2, 5, 6, 4],
[2, 1, 3, 4, 5, 6],
[2, 1, 3, 4, 6, 5],
[2, 1, 3, 5, 4, 6],
[2, 1, 3, 5, 6, 4],
[2, 1, 3, 6, 4, 5],
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 6, 5],
[1, 2, 3, 5, 4, 6],
[1, 2, 3, 5, 6, 4],
[1, 2, 3, 6, 4, 5],
[1, 2, 3, 6, 5, 4]])
I tried :
test=[list(g) for g in neat_list]
any([(u[::-1] in test) for u in test])
And this outputs false which means no occurence of reversed arrays there.

Categories

Resources