Extract longest common path between two lists in python - python-3.x

Lets say there are two lists
L1=[['A', ['C', ['B', ['D', 0]]]],
[['A', ['D', ['K', ['C', ['E', 0]]]]],
[['A', ['C', ['B', ['M', 0]]]]]
and
L2=[['A', ['C', ['B', ['K', 0]]]],
[['A', ['C', ['B', ['B', ['E', 0]]]]],
[['A', ['D', ['K', ['F', 0]]]]]
Then the output should return all the sub-paths with longest common path. For example:
Since 'A', 'C', 'B' is common L1 and L2; output should be:
[['A', ['C', ['B', ['D', 0]]]],
[['A', ['C', ['B', ['M', 0]]]],
[['A', ['C', ['B', ['K', 0]]]],
[['A', ['C', ['B', ['B', ['E', 0]]]]]
. Also, 'A', 'D', 'K' is also common for one time in L1 and L2; the output whould be:
[['A', ['D', ['K', ['C', ['E', 0]]]]],
[['A', ['D', ['K', ['F', 0]]]]]
I tried :
[i for i in L1 if i in L2]
but it will give the output of all the common paths till the leaf (end).

take something from the great and marvelous c!
you can simply use a while in another and check letter by letter, if one is different exit the first while and put the character to 0
like
while (var[i][n] && var2[i][n])
while (var[i][n] == var2[i][n])
n = n + 1
var[i][n] = 0
or something like that.
but it is not really optimised.

Related

Sorting a list by another list with duplicates

I have two lists [1, 2, 3, 1, 2, 1] and [a, b, c, d, e, f]. I want to reorder elements in the second list according to the permutations that sort the first list. Sorting the first list gives [1, 1, 1, 2, 2, 3] but there are many possible permutations for the second list to be sorted by the first i.e. [a, d, f, b, e, c], [d, f, a, e, b, c], etc..
How can I generate all of these permutations in an efficient manner in python?
If I just wanted one permutation I could get one by something like this:
sorted_numbers, sorted_letters = list(zip(*[(x, y) for x, y in sorted(zip(numbers, letters))]))
If the size of the lists is not too large you could just use a list comprehension to filter all the permutations with a helper function:
from itertools import permutations
def is_valid_ordering(perm: str, ch_to_order: dict) -> bool:
if not perm or len(perm) <= 1:
return True
for ch1, ch2 in zip(perm[:-1], perm[1:]):
if ch_to_order[ch1] > ch_to_order[ch2]:
return False
return True
lst_1 = [1, 2, 3, 1, 2, 1]
lst_2 = ['a', 'b', 'c', 'd', 'e', 'f']
ch_to_order = {ch: o for ch, o in zip(lst_2, lst_1)}
valid_permutations = [
list(p) for p in permutations(lst_2)
if is_valid_ordering(p, ch_to_order)
]
for valid_perm in valid_permutations:
print(valid_perm)
Output:
['a', 'd', 'f', 'b', 'e', 'c']
['a', 'd', 'f', 'e', 'b', 'c']
['a', 'f', 'd', 'b', 'e', 'c']
['a', 'f', 'd', 'e', 'b', 'c']
['d', 'a', 'f', 'b', 'e', 'c']
['d', 'a', 'f', 'e', 'b', 'c']
['d', 'f', 'a', 'b', 'e', 'c']
['d', 'f', 'a', 'e', 'b', 'c']
['f', 'a', 'd', 'b', 'e', 'c']
['f', 'a', 'd', 'e', 'b', 'c']
['f', 'd', 'a', 'b', 'e', 'c']
['f', 'd', 'a', 'e', 'b', 'c']
Alternatively if the lists are large and therefore efficiency is important, you could construct only the valid orderings (see Stef's answer for an even better approach than below):
from collections import defaultdict
from itertools import permutations, product
from iteration_utilities import flatten
lst_1 = [1, 2, 3, 1, 2, 1]
lst_2 = ['a', 'b', 'c', 'd', 'e', 'f']
equivalent_chars = defaultdict(list)
for o, ch in zip(lst_1, lst_2):
equivalent_chars[o].append(ch)
equivalent_char_groups = [g for o, g in sorted(equivalent_chars.items())]
all_group_permutations = [[list(p) for p in permutations(group)]
for group in equivalent_char_groups]
valid_permutations = [
list(flatten(p)) for p in product(*all_group_permutations)
]
for valid_perm in valid_permutations:
print(valid_perm)
Using itertools to build the Cartesian product of the permutations for each duplicated key:
Code
from itertools import chain, permutations, groupby, product
from operator import itemgetter
def all_sorts(numbers, letters):
return [list(map(itemgetter(1), chain.from_iterable(p))) for p in product(*(permutations(g) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))))]
print( all_sorts([1,2,3,1,2,1], 'abcdef') )
# [['a', 'd', 'f', 'b', 'e', 'c'], ['a', 'd', 'f', 'e', 'b', 'c'], ['a', 'f', 'd', 'b', 'e', 'c'], ['a', 'f', 'd', 'e', 'b', 'c'], ['d', 'a', 'f', 'b', 'e', 'c'], ['d', 'a', 'f', 'e', 'b', 'c'], ['d', 'f', 'a', 'b', 'e', 'c'], ['d', 'f', 'a', 'e', 'b', 'c'], ['f', 'a', 'd', 'b', 'e', 'c'], ['f', 'a', 'd', 'e', 'b', 'c'], ['f', 'd', 'a', 'b', 'e', 'c'], ['f', 'd', 'a', 'e', 'b', 'c']]
This approach is optimal in the sense that it generates the solutions directly, rather that filtering them from a huge list of candidates. With the given example list of size 6, it generates only 12 solutions, rather than filtering through all 720 permutations of a list of size 6.
How it works:
First we sort and group by key, using sorted and itertools.groupby. Note operator.itemgetter(0) is the same as lambda t: t[0].
>>> [list(g) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))]
[[(1, 'a'), (1, 'd'), (1, 'f')],
[(2, 'b'), (2, 'e')],
[(3, 'c')]]
Then we generate the possible permutations of every key, using itertools.permutation on every group.
>>> [list(permutations(g)) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))]
[[((1, 'a'), (1, 'd'), (1, 'f')), ((1, 'a'), (1, 'f'), (1, 'd')), ((1, 'd'), (1, 'a'), (1, 'f')), ((1, 'd'), (1, 'f'), (1, 'a')), ((1, 'f'), (1, 'a'), (1, 'd')), ((1, 'f'), (1, 'd'), (1, 'a'))],
[((2, 'b'), (2, 'e')), ((2, 'e'), (2, 'b'))],
[((3, 'c'),)]]
Then we build the Cartesian product of these lists of permutations, using itertools.product; and we rebuild a list from each tuple in the Cartesian product, using itertools.chain to concatenate. Fially we "undecorate", discarding the keys and keeping only the letters, which I did with map(itemgetter(1), ...) but could have equivalently done with a list comprehension [t[1] for t in ...].
>>> [list(map(itemgetter(1), chain.from_iterable(p))) for p in product(*(permutations(g) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))))]
[['a', 'd', 'f', 'b', 'e', 'c'], ['a', 'd', 'f', 'e', 'b', 'c'], ['a', 'f', 'd', 'b', 'e', 'c'], ['a', 'f', 'd', 'e', 'b', 'c'], ['d', 'a', 'f', 'b', 'e', 'c'], ['d', 'a', 'f', 'e', 'b', 'c'], ['d', 'f', 'a', 'b', 'e', 'c'], ['d', 'f', 'a', 'e', 'b', 'c'], ['f', 'a', 'd', 'b', 'e', 'c'], ['f', 'a', 'd', 'e', 'b', 'c'], ['f', 'd', 'a', 'b', 'e', 'c'], ['f', 'd', 'a', 'e', 'b', 'c']]
Another implementation without filtering:
from itertools import product, permutations, chain
numbers = [1, 2, 3, 1, 2, 1]
letters = ['a', 'b', 'c', 'd', 'e', 'f']
grouper = {}
for number, letter in zip(numbers, letters):
grouper.setdefault(number, []).append(letter)
groups = [grouper[number] for number in sorted(grouper)]
for prod in product(*map(permutations, groups)):
print(list(chain.from_iterable(prod)))
Output:
['a', 'd', 'f', 'b', 'e', 'c']
['a', 'd', 'f', 'e', 'b', 'c']
['a', 'f', 'd', 'b', 'e', 'c']
['a', 'f', 'd', 'e', 'b', 'c']
['d', 'a', 'f', 'b', 'e', 'c']
['d', 'a', 'f', 'e', 'b', 'c']
['d', 'f', 'a', 'b', 'e', 'c']
['d', 'f', 'a', 'e', 'b', 'c']
['f', 'a', 'd', 'b', 'e', 'c']
['f', 'a', 'd', 'e', 'b', 'c']
['f', 'd', 'a', 'b', 'e', 'c']
['f', 'd', 'a', 'e', 'b', 'c']
It first groups the letters by their numbers, using a dict:
grouper = {1: ['a', 'd', 'f'], 2: ['b', 'e'], 3: ['c']}
Then it sorts the numbers and extracts their letter groups:
groups = [['a', 'd', 'f'], ['b', 'e'], ['c']]
Then just permute each group and build and chain the products.

numpy filter for not unique elements in 2d array

import numpy as np
data = np.array(
[
['a' 'a'],
['a' 'b'],
['d' 'c'],
['a' 'b'],
['d' 'c'],
['a' 'a'],
['b' 'a'],
['c' nan]
]
)
How can i filter for the most frequent subarrays?
Expected results: [['a' 'a'], ['d' 'c']]
I don't quite understand the question, but I think np.unqiue might be useful.
data = np.array(
[
['a', 'a'],
['a', 'b'],
['d', 'c'],
['a', 'b'],
['d', 'c'],
['a', 'a'],
['b', 'a'],
['c', np.nan]
]
)
unique, idx, counts = np.unique(data[:,0], return_counts=True, return_index=True)
threshold = 1
data[idx[counts > threshold]]
Output:
array([['a', 'a'],
['d', 'c']], dtype='<U32')

How to find same elements of a list in 2d list python

I have the following 2D list:
test_list = [['A', 'B', 'C'], ['I', 'L', 'A', 'C', 'K', 'B'], ['J', 'I', 'A', 'B', 'C']]
I want to compare the 1st list elements of the 2D array test_list[0] with all other lists. If the elements ['A', 'B', 'C'] are present in all other lists then it should print any message such as "All elements are similar".
I have tried this piece of code but it is not working as I expected:
test_list = [['A', 'B', 'C'], ['I', 'L', 'A', 'C', 'K', 'B'], ['J', 'I', 'A', 'B', 'C']]
for idx,ele in enumerate(p):
result = set(test_list [0]).intersection(test_list [(idx + 1) % len(temp_d)])
print(result)
Expected Output:
The elements of the list ['A', 'B', 'C'] are present in all other lists.
You can use the all(...) function - or remove all elements from the bigger list from your smaller one converted to set. If the set.difference() is Falsy (i.e. all elements were removed) they were all contained in it:
test_list = [['A', 'B', 'C'], ['I', 'L', 'A', 'C', 'K', 'B'], ['J', 'I', 'A', 'B', 'C']]
s = test_list[0]
for e in test_list[1:]:
if all(v in e for v in s):
print(e, "contains all elements of ", s)
s = set(s)
for e in test_list[1:]:
# if all elements of s are in e the difference will be an empty set == Falsy
if not s.difference(e):
print(e, "contains all elements of ", s)
Output:
['I', 'L', 'A', 'C', 'K', 'B'] contains all elements of ['A', 'B', 'C']
['J', 'I', 'A', 'B', 'C'] contains all elements of ['A', 'B', 'C']
['I', 'L', 'A', 'C', 'K', 'B'] contains all elements of {'A', 'B', 'C'}
['J', 'I', 'A', 'B', 'C'] contains all elements of {'A', 'B', 'C'}
For each letter in the first list see if they are in the second and third list and return the boolean.
Then see if the set of the new list equals True
test_list = [['A', 'B', 'C'], ['I', 'L', 'A', 'C', 'K', 'B'], ['J', 'I', 'A', 'B', 'C']]
bool = ([x in test_list[1]+test_list[2] for x in test_list[0]])
if list(set(bool))[0] == True:
print('All elements are similar')
>>> All elements are similar

How do you rearrange a list of lists where the first element holds multiple values and creates a new list of lists while repeating the first element?

Given a list of lists:
list_format = [['a', 'c', 'f', 'b'], ['j', 'l', 'o', 'c'], ['q', 's', 'v', 'e']]
'c', 'f', 'b' must be mapped to 'a'
'l', 'o', 'c' must be mapped to 'j'
's', 'v', 'e' must be mapped to 'q'
The output should look like this:
[['a','c'],['a','f'],['a','b'],['j','l'],['j','o'],['j','c'],['q','s'],['q','v'],['q','e']]
I've tried so far:
list_dict = {element[0]:element[1:] for element in list_format}
newer_lst = []
for key, value in list_dict.items():
newer_lst.append((key, value))
newer_lst
Gives me the output of tuples:
[('a', ['c', 'f', 'b']), ('j', ['l', 'o', 'c']), ('q', ['s', 'v', 'e'])]
I'm newer at this and trying to rearrange, any advice would be awesome, been stuck for days with trial and error(searched google countless times and constantly googling. I feel I'm getting close but can't seem to put it together.
Here is a one-liner, using slicing:
[[i[0],j] for i in list_format for j in i[1:]]
gives:
[['a', 'c'], ['a', 'f'], ['a', 'b'], ['j', 'l'], ['j', 'o'], ['j', 'c'], ['q', 's'], ['q', 'v'], ['q', 'e']]
Also, if you iterate through your value variable, you get your result:
list_dict = {element[0]:element[1:] for element in list_format}
newer_lst = []
for key, value in list_dict.items():
for i in value:
newer_lst.append((key, i))
print(newer_lst)
You don't need to create a loop, just loop on sub array and append new sub array to main output array on the fly, like new_list.append([lst[0], item])
new_list = []
for lst in list_format:
for item in lst[1:]:
new_list.append([lst[0], item])
print(new_list)
#output
#[['a', 'c'], ['a', 'f'], ['a', 'b'], ['j', 'l'], ['j', 'o'], ['j', 'c'], ['q', 's'], ['q', 'v'], ['q', 'e']]

Count the elements in a list of a list(python)

[['s', 'a', 'b', 't'], ['s', 'c', 'd', 't'], ['s', 'c', 'e', 't','g']]
I want to count how many elements are in each list of the list.
so,[4,4,5].How can i do that in python?
Just use len for each element on the given list:
l = [['s', 'a', 'b', 't'], ['s', 'c', 'd', 't'], ['s', 'c', 'e', 't','g']]
[len(x) for x in l] # [4, 4, 5]
you can utilize the len() function. It takes a list as a parameter and returns the elements count.
python len function

Resources