Combining same elements into a list - python

I've been trying to convert my list
alist = [[1,[1,2]],[2,[3,4,5]],[3,[1,2]],[4,[3,4,5]],[5,[5,6,7]],[6,[1,2]]]
into this. Since the second item of those two sublists are same.
[[[1,3,6],[1,2]],[[2,4],[3,4,5]]]
This is my code
alist = [[1,[1,2]],[2,[3,4,5]],[3,[1,2]],[4,[3,4,5]],[5,[5,6,7]],[6,[1,2]]]
lst=[]
for i in range(len(alist)):
inner = []
inner1=[]
for j in range(i+1,len(alist)):
if i+1 < len(alist):
if alist[i][1] == alist[j][1]:
inner1.append(alist[i][0])
inner1.append(alist[j][0])
inner.append(inner1)
inner.append(alist[i][1])
lst.append(inner)
print(lst)
but it gives this instead
[[[1, 3, 1, 6], [1, 2], [1, 3, 1, 6], [1, 2]], [[1, 3, 1, 6], [1, 2], [1, 3, 1, 6], [1, 2]], [[2, 4], [3, 4, 5]], [[3, 6], [1, 2]]]
It works when there's only 2 elements that are the same but when there's 3 it doesn't work.
Example
[2,4],[3,4,5] #2 of the same elements from alist works
[1,3,1,6],[1,2] #3 of the same elements from alist doesn't work
Can anyone please offer a solution?

You can use a dict (an Ordered one since you have to maintain the order) to group "heads" by "tails":
alist = [[1,[1,2]],[2,[3,4,5]],[3,[1,2]],[4,[3,4,5]],[5,[5,6,7]],[6,[1,2]]]
from collections import OrderedDict
c = OrderedDict()
for head, tail in alist:
c.setdefault(tuple(tail), []).append(head)
res = [[heads, list(tail)] for tail, heads in c.items()]
print res
prints
[[[1, 3, 6], [1, 2]], [[2, 4], [3, 4, 5]], [[5], [5, 6, 7]]]
If you want to omit 5 (a group with a single "head"), add a condition to the res= line:
res = [[heads, list(tail)] for tail, heads in c.items() if len(heads) > 1]

Related

Find all the consecutive subsequences of alternating odd and even numbers

Given an integer array, find all the consecutive subsequences of alternating odd and even numbers.
Also print the total number of such subsequences.
All the subsequences should be unique.
The numbers in the list may or may not be unique.
Example:
array = [1,2,5]
output1: [[1], [1,2], [2], [2,5], [5], [1,2,5]]
output2: 6
My Code:
res = []
lst = [1,2,5]
for i in range(len(lst)-1):
res.append([lst[i]])
if abs(lst[i] - lst[i+1]) % 2 == 1:
res.append([lst[i], lst[i+1]])
print(res)
Output: [[1], [1, 2], [2], [2, 5]]
How can I get the remaining subsequences?
You care about duplicates:
def alternating_sublists(xs: list[int]) -> list[list[int]]:
results = []
for i in range(len(xs)):
if [xs[i]] not in results:
results.append([xs[i]])
for j in range(i+1, len(xs)):
if (xs[j] - xs[j-1]) % 2 != 0:
if xs[i:j+1] not in results:
results.append(xs[i:j+1])
else:
break
return results
print(list(alternating_sublists([1, 2, 5])))
print(list(alternating_sublists([1, 2, 2, 2, 1])))
print(list(alternating_sublists([1, 2, 3, 2, 3, 2, 1])))
Output:
[[1], [1, 2], [1, 2, 5], [2], [2, 5], [5]]
[[1], [1, 2], [2], [2, 1]]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 2], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3, 2], [1, 2, 3, 2, 3, 2, 1], [2], [2, 3], [2, 3, 2], [2, 3, 2, 3], [2, 3, 2, 3, 2], [2, 3, 2, 3, 2, 1], [3], [3, 2], [3, 2, 3], [3, 2, 3, 2], [3, 2, 3, 2, 1], [2, 3, 2, 1], [3, 2, 1], [2, 1]]
It's not extremely efficient (there's many lookups of lists already in the result). Depending on the application you may want a more complex data structure to save expensive 'list in large list' tests.
The basic logic is this:
each sequence has to start at some index, so try sequences starting at all possible indices for i in range(len(xs)):
the sequence with length 1 always meets your rule, so add it if it wasn't there yet
the other sequences start at index i and end at index i+1 or greater for j in range(i+1, len(xs)):
break from the loop whenever the modulo is 0 for the last two items in list you're about to add, since this sequence doesn't meet the rule, and longer ones wouldn't either.
Slightly faster and shorter, using tuples internally, but essentially the same:
def alternating_sublists2(xs: list[int]) -> list[list[int]]:
results = set()
for i in range(len(xs)):
results.add((xs[i],))
for j in range(i+1, len(xs)):
if (xs[j] - xs[j-1]) % 2 != 0:
results.add(tuple(xs[i:j+1]))
else:
break
return [list(t) for t in results]
shorter as the previous if statements are now internal to set.add()
faster because looking up tuples is faster than looking up strings, and testing membership of a set is faster than testing membership of a list
not quite as fast as you might like, since it then has to convert the result back to a list of lists, to get the result you required.
However, no guarantees on the order of the sublists in the result, so this is no good if you need the sublists in the order they are first found.
Here's a recursive solution to the problem. It iterates the elements of the list, adding the results from recursing the balance of the list when there is a change from odd to even between the current element and the next:
def odd_even(list, start=None):
result = []
for i, val in enumerate(list):
if start is None or i == 0:
if [val] not in result:
result.append([val])
if len(list) > i+1 and (list[i+1] - val) % 2 == 1:
for res in odd_even(list[i+1:], val):
if [val] + res not in result:
result = result + [[val] + res]
return result
print(odd_even([1, 2, 5]))
print(odd_even([1, 2, 2, 2, 1]))
print(odd_even([1, 2, 3, 2, 3, 2, 1]))
Output:
[[1], [1, 2], [1, 2, 5], [2], [2, 5], [5]]
[[1], [1, 2], [2], [2, 1]]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 2], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3, 2], [1, 2, 3, 2, 3, 2, 1], [2], [2, 3], [2, 3, 2], [2, 3, 2, 3], [2, 3, 2, 3, 2], [2, 3, 2, 3, 2, 1], [3], [3, 2], [3, 2, 3], [3, 2, 3, 2], [3, 2, 3, 2, 1], [2, 3, 2, 1], [3, 2, 1], [2, 1]]
Your accepted output is silly, because it's obvious that every subsequence of a "good" sequence is also "good" and there's no need to enumerate them all. Let's concentrate on finding longest alternating sequences:
def split(a):
buf = [a[0]]
for i in range(1, len(a)):
if a[i] % 2 != a[i - 1] % 2:
buf.append(a[i])
else:
yield buf
buf = [a[i]]
if buf:
yield buf
test = [1, 2, 5, 7, 3, 8, 9, 9, 10, 11]
result = list(split(test))
# [[1, 2, 5], [7], [3, 8, 9], [9, 10, 11]]
To get your expected answer, take each list from the result and generate all sublists of it. This is another, much simpler task.
This looks like a gray code sequence with additional twist:
https://en.wikipedia.org/wiki/Gray_code
Code:
import math
def powerOf2(k):
if k == 0:
return 1
else:
return 2*powerOf2(k-1)
def gray_encode(n):
return n ^ n >> 1
def count_required_sequence(lst):
n = len(lst)
sequence_nr = powerOf2(n)
results = []
results_sequence = -1
for i in range(sequence_nr):
gray = gray_encode(i)
gray_r = list("{:>010b}".format(gray))[::-1]
#print(gray_r)
count = sum(el == "1" for el in gray_r)
if count > 1:
results_sequence += 1
results.append(list())
for k in range(len(gray_r)):
if k < len(gray_r)-1:
if gray_r[k] == "1" and gray_r[k+1] == "1":
if abs(lst[k] - lst[k+1]) % 2 == 1:
results[results_sequence].append(lst[k])
results[results_sequence].append(lst[k+1])
is_there_count1 = results.count(list(set(results[results_sequence])))
results[results_sequence] = list(set(results[results_sequence]))
is_there_count = results.count(results[results_sequence])
if is_there_count > 1 or is_there_count1 > 1:
index = results.index(list(set(results[results_sequence])))
results.pop(results_sequence)
results_sequence -= 1
elif count == 1 :
results_sequence += 1
results.append(list())
pos = [index for index,value in enumerate(gray_r) if value == "1" ]
results[results_sequence].append(lst[pos[0]])
results = (list(filter(lambda a: a != [], results)))
print("results: {}".format(results))
# Driver code
if __name__ == "__main__" :
# lst = [ 1, 2, 5, 6, 7];
lst = [ 1, 2, 5 ];
count_required_sequence(lst);
Output:
results: [[1], [1, 2], [2], [2, 5], [1, 2, 5], [5]]
Change 010b to a bigger number is len(lst) is bigger then 10
gray_r = list("{:>010b}".format(gray))[::-1]

How to randomly replace elements between lists in list of lists

I have a list of lists like that:
l_of_l = [[1,2,3], [4,5],[6]]
and I want to replace two elements randomly cross-list, for example:
perm(l_of_l) = [[1, 4, 3], [2, 5], [6]] # 2 and 4 replaced
perm(l_of_l) = [[6, 2, 3], [4, 5], [1]] # 6 and 1 replaced
#etc.
The length of the list should be saved and replacement on the same list is declined.
perm(l_of_l) = [[1, 2], [4, 5], [3, 6]] # illegal permutation - the lenght of lists changed
perm(l_of_l) = [[2, 1, 3], [4, 5], [6]] # illegal permutation - 1 and 2 are from the same list
I have been tried to use itertools.permutaion but it doesn't work:
# permutations using library function
from itertools import permutations
# Get all permutations of lists
perm = permutations([[1, 2, 3], [4, 5], [6]])
# Print the obtained permutations
for i in list(perm):
print (i)
#output:
#([1, 2, 3], [4, 5], [6])
#([1, 2, 3], [6], [4, 5])
#([4, 5], [1, 2, 3], [6])
#([4, 5], [6], [1, 2, 3])
#([6], [1, 2, 3], [4, 5])
#([6], [4, 5], [1, 2, 3])
What do you suggest for me?
Thanks in advance!
Here's a naive solution, broken up into several lines for clarity:
l_of_l = [[1,2,3], [4,5],[6]]
num_lists = len(l_of_l)
l1_inx, l2_inx = random.sample(range(num_lists), 2)
len1 = len(l_of_l[l1_inx])
len2 = len(l_of_l[l2_inx])
elem1 = random.randint(0, len1-1)
elem2 = random.randint(0, len2-1)
temp = l_of_l[l1_inx][elem1]
l_of_l[l1_inx][elem1] = l_of_l[l2_inx][elem2]
l_of_l[l2_inx][elem2] = temp

How do I account for repetition within list elements

The objective of the parent program is to generate permutations and find out with what probability they are generated by the generating process. I would like help in sorting identical permutations through the list.
So each element in the list is of the form [[[a,b,c],[x]],[[d,e,f],[y]],....] where the x represents the probability with which [a,b,c] is generated in one loop. I have one of my outputs below.
[[[1, 2, 3], [0.125]],[[1, 2, 3], [0.125]],[[2, 3, 1], [0.125]],[[2, 1, 3], [0.125]],[[3, 1, 2], [0.125]],[[1, 2, 3], [0.125]],[[1, 3, 2], [0.125]],[[1, 2, 3], [0.125]]]
When my final list contains [[a,b,c],[x]] and [[a,b,c],[y]], I will need it to become [[a,b,c],[x+y]]. How do I do this?
I tried the following code. The output list is called master_list.
for i in range(0,len(master_list)):
count = []
for j in range(i+1,len(master_list)):
if master_list[i][0] == master_list[j][0]:
count.append(j)
master_list[i][1][0] += master_list[j][1][0]
else:
continue
print('count for element',master_list[i][0], 'at number', i, 'is', count)
for k in count:
print(k)
for c in range(0,len(count)):
master_list.remove(master_list[k])
print('master_list now is',master_list)
count[c] += -1
The print statements were only for me to check. However, the problem I faced was that after every .remove() operation, the indices of the list(s) changed so the loop broke down.
Instead of iterating the original list removing element from there (list.remove() is O(n) [ref]), a more efficient way is to pass through an intermediary dictionary for then creating again a list.
from collections import defaultdict
res = [[[1, 2, 3], [0.125]], [[1, 2, 3], [0.125]], [[2, 3, 1], [0.125]], [[2, 1, 3], [0.125]], [[3, 1, 2], [0.125]],
[[1, 2, 3], [0.125]], [[1, 3, 2], [0.125]], [[1, 2, 3], [0.125]]]
grouped_res = defaultdict(float)
for perm, prob in res:
grouped_res[tuple(perm)] += prob[0]
new_res = [[list(k), [v]] for k, v in grouped_res.items()]
# [[[1, 2, 3], [0.5]], [[2, 3, 1], [0.125]], [[2, 1, 3], [0.125]], [[3, 1, 2], [0.125]], [[1, 3, 2], [0.125]]]

How to filter out equivalent lists of values in python?

When given a list of lists like: [[5, 2] [2, 5]]
I want to reduce this list to this: [[5, 2]] or [[2, 5]]
I've already tried the following: list(map(list, set(map(frozenset, parts_to_add))))
But the problem with this method is that it turns my lists in sets and while this works - it filters out all the equivalent lists - it also eats all of my duplicate values; When given [[3, 3]] it returns [[3]], due to the nature of sets in python. Is there another way of doing this?
Any help would be grately appreciated.
You could do :
data = [[5, 2], [2, 5], [3, 3]]
result = set(tuple(e) for e in map(sorted, data))
print([list(e) for e in result])
Output
[[2, 5], [3, 3]]
As #soon mentioned you could use a frozen Counter, for example:
from collections import Counter
data = [[5, 2], [2, 5], [3, 3]]
result = [e for e in {frozenset(Counter(e)): e for e in data}.values()]
print(result)
Output
[[2, 5], [3, 3]]
The above approach also uses a dict for removing duplicates instead of a set.
A more straightforward approach would be,
data = [[2, 5], [5, 2], [3, 6], [2, 5], [6, 3]]
result = []
for i in range(len(data)):
data[i].sort()
if data[i] not in result:
result.append(data[i])
In [49]: result
Out[49]: [[2, 5], [3, 6]]

How to do Math Functions on Lists within a List

I'm very new to python (using python3) and I'm trying to add numbers from one list to another list. The only problem is that the second list is a list of lists. For example:
[[1, 2, 3], [4, 5, 6]]
What I want is to, say, add 1 to each item in the first list and 2 to each item in the second, returning something like this:
[[2, 3, 4], [6, 7, 8]]
I tried this:
original_lst = [[1, 2, 3], [4, 5, 6]]
trasposition_lst = [1, 2]
new_lst = [x+y for x,y in zip(original_lst, transposition_ls)]
print(new_lst)
When I do this, I get an error
can only concatenate list (not "int") to list
This leads me to believe that I can't operate in this way on the lists as long as they are nested within another list. I want to do this operation without flattening the nested list. Is there a solution?
One approach using enumerate
Demo:
l = [[1, 2, 3], [4, 5, 6]]
print( [[j+i for j in v] for i,v in enumerate(l, 1)] )
Output:
[[2, 3, 4], [6, 7, 8]]
You can use enumerate:
l = [[1, 2, 3], [4, 5, 6]]
new_l = [[c+i for c in a] for i, a in enumerate(l, 1)]
Output:
[[2, 3, 4], [6, 7, 8]]
Why don't use numpy instead?
import numpy as np
mat = np.array([[1, 2, 3], [4, 5, 6]])
mul = np.array([1,2])
m = np.ones(mat.shape)
res = (m.T *mul).T + mat
You were very close with you original method. Just fell one step short.
Small addition
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
new_lst = [[xx + y for xx in x] for x, y in zip(original_lst, transposition_lst)]
print(new_lst)
Output
[[2, 3, 4], [6, 7, 8]]
Reasoning
If you print your original zip it is easy to see the issue. Your original zip yielded this:
In:
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
for x,y in zip(original_lst, transposition_lst):
print(x, y)
Output
[1, 2, 3] 1
[4, 5, 6] 2
Now it is easy to see that you are trying to add an integer to a list (hence the error). Which python doesn't understand. if they were both integers it would add them or if they were both lists it would combine them.
To fix this you need to do one extra step with your code to add the integer to each value in the list. Hence the addition of the extra list comprehension in the solution above.
A different approach than numpy that could work even for lists of different lengths is
lst = [[1, 2, 3], [4, 5, 6, 7]]
c = [1, 2]
res = [[l + c[i] for l in lst[i]] for i in range(len(c))]

Categories

Resources