Swap two values in a list of lists (python) - python

I want to swap every occurrence of two elements in a list of lists. I have seen previous answers but they are different from what I'm trying to do. Also, I am looking for a clean and concise way (preferably list-comprehension) for this purpose.
Input
my_list = [[1,2], [1,3], [1,4], [3,2]]
Output
my_list = [[2,1], [2,3], [2,4], [3,1]]
I am trying to do something like this but no success:
[1 if i==2 else i, 2 if i==1 else i for i in my_list]

Here's a simple solution using list comprehension:
my_list = [[1,2], [1,3], [1,4], [3,2]]
a = 1
b = 2
my_list = [[a if i==b else b if i==a else i for i in j] for j in my_list]
print(my_list) # [[2, 1], [2, 3], [2, 4], [3, 1]]
If you want to add more elements to replace you can use a dictionary:
swap = {
1: 2,
2: 1
}
my_list = [[swap.get(i, i) for i in j] for j in my_list]

Someone else will probably answer with something better, but this would work.
def check(num):
if num == 1:
return 2
elif num == 2:
return 1
else:
return num
out = [[check(j) for j in i] for i in my_list]

If you need to swap 1's with 2's, this one-liner will work:
>>> my_list = [[1,2], [1,3], [1,4], [3,2]]
>>> print([[(lambda k: (1 if val == 2 else 2) if val in [1, 2] else val)(val) for val in sub_list] for sub_list in my_list])
[[2, 1], [2, 3], [2, 4], [3, 1]]
read the second line from right to left... in chunks!

Related

How to merge smaller sub-elements into larger parent-elements in a list?

I have a list of lists, but some lists are "sublists" of other lists. What I want to do is remove the sublists from the larger list so that we only have the largest unique sublists.
For example:
>>> some_list = [[1], [1, 2], [1, 2, 3], [1, 4]]
>>> ideal_list = [[1, 2, 3], [1, 4]]
The code that I've written right now is:
new_list = []
for i in range(some_list)):
for j in range(i + 1, len(some_list)):
count = 0
for k in some_list[i]:
if k in some_list[j]:
count += 1
if count == len(some_list[i]):
new_list.append(some_list[j])
The basic algorithm that I had in mind is that we'd check if a list's elements were in the following sublists, and if so then we use the other larger sublist. It doesn't give the desired output (it actually gives [[1, 2], [1, 2, 3], [1, 4], [1, 2, 3]]) and I'm wondering what I could do to achieve what I want.
I don't want to use sets because duplicate elements matter.
Same idea as set, but using Counter instead. It should be a lot more efficient in sublist check part than brute force
from collections import Counter
new_list = []
counters = []
for arr in sorted(some_list, key=len, reverse=True):
arr_counter = Counter(arr)
if any((c & arr_counter) == arr_counter for c in counters):
continue # it is a sublist of something else
new_list.append(arr)
counters.append(arr_counter)
With some inspiration from #mkrieger1's comment, one possible solution would be:
def merge_sublists(some_list):
new_list = []
for i in range(len(some_list)):
true_or_false = []
for j in range(len(some_list)):
if some_list[j] == some_list[i]:
continue
true_or_false.append(all([x in some_list[j] for x in some_list[i]]))
if not any(true_or_false):
new_list.append(some_list[i])
return new_list
As is stated in the comment, a brute-force solution would be to loop through each element and check if it's a sublist of any other sublist. If it's not, then append it to the new list.
Test cases:
>>> merge_sublists([[1], [1, 2], [1, 2, 3], [1, 4]])
[[1, 2, 3], [1, 4]]
>>> merge_sublists([[1, 2, 3], [4, 5], [3, 4]])
[[1, 2, 3], [4, 5], [3, 4]]
Input:
l = [[1], [1, 2], [1, 2, 3], [1, 4]]
One way here:
l1 = l.copy()
for i in l:
for j in l:
if set(i).issubset(set(j)) and i!=j:
l1.remove(i)
break
This prints:
print(l1)
[[1, 2, 3], [1, 4]]
EDIT: (Taking care of duplicates as well)
l1 = [list(tupl) for tupl in {tuple(item) for item in l }]
l2 = l1.copy()
for i in l1:
for j in l1:
if set(i).issubset(set(j)) and i!=j:
l2.remove(i)
break

Formatting a string from a list of numbers

I've got a list of integers l = [1,2,3,4,5,6] and I want to print them in a formatted way as such:
[1,2] [3,4] [5,6]
How can I get this result in an easy way? I've tried using:
print("[%d, %d] [%d, %d] [%d, %d]" % l)
But it didn't work since l is not a number. Any pointers on how this effect can be achieved would be appreciated.
I know I can use
print("[", l[0], "," ,1[1] ...
But that is way to ugly and ineffective to write code, and was hoping for a better way.
If the number of elements is fixed, we can try this:
lst = [1, 2, 3, 4, 5, 6]
print('[{}, {}] [{}, {}] [{}, {}]'.format(*lst))
=> '[1, 2] [3, 4] [5, 6]'
You can group the list items in pairs by creating an iterator from the list and zipping the iterator with itself:
i = iter(l)
print(*map(list, zip(i, i)))
This outputs:
[1, 2] [3, 4] [5, 6]
l = [1,2,3,4,5,6]
print(*map(list, zip(l[::2], l[1::2])))
Prints:
[1, 2] [3, 4] [5, 6]
How about this?
l = [1,2,3,4,5,6]
evens = [i for i in l if i % 2 == 0]
odds = [i for i in l if i % 2 != 0]
zip(evens, odds)
or this
l = [1,2,3,4,5,6,7,8]
print( *[ (l[2*i], l[2*i+1] ) for i in range(0,len(l)//2)])
produces
[1, 2] [3, 4] [5, 6]

how to remove all subset from a list of list

what is the efficient way to clean sublist in list . cause I only want to got the biggest set in list. just like.
b = [[1,2,3], [1,2], [3,5], [2,3,4], [2,3,4], [3,4,5], [1,2,4,6,7]]
and I want the output as follow.
result = [[1,2,3], [2,3,4], [3,4,5], [1,2,4,6,7]]
Cause [1,2] is subset of [1,2,3] and [1,2,4,6,7], [3,5] is subset of [3,4,5], and also [2,3,4] appear 2 times, only want calculate 1 time in final result. I want to based on the subset logical to filter data.
I only think out 2 loops solution to solve this problem, but if there is other efficient way to solve this problem.
what I tried like that: (after I optimising this one more effect, add break and add 1 part not calculate 2 times)
b = [[1,2,3], [1,2], [3,5], [2,3,4], [2,3,4], [3,4,5], [1,2,4,6,7]]
i = 0
record = []
subset_status = False
for index, re in enumerate(b):
while i <= (len(b)-1):
if i != index:
if i not in record:
if set(re).issubset(b[i]):
subset_status = True
break
i += 1
i = 0
if subset_status:
record.append(index)
subset_status = False
print(record)
>>[1, 2, 3]
So I got the index in [1,2,3] is the dirty data.
Thanks.
filter your list on condition:
b = [[1,2,3], [1,2], [3,5], [2,3,4],[3,4,5]]
print(list(filter(lambda x: len(x) == 3, b)))
# [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
A conditional list comprehension is a pythonic, flexible and performant approach. It is usually faster and less error prone to assemble the clean list from scratch than to repeatedly remove elements:
b = [[1, 2, 3], [1, 2], [3, 5], [2, 3, 4],[3, 4, 5]]
cleaned = [x for x in b if clean(x)] # where clean is your condition
# e.g.
cleaned = [x for x in b if len(x) == 3]
# [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
If you need to mutate the original list object, use slice assignment:
b[:] = [x for x in b if clean(x)]
One way to do this is to process the lists in b in order of length, from longest to shortest.
b = [[1,2,3], [1,2], [3,5], [2,3,4], [2,3,4], [3,4,5], [1,2,4,6,7]]
result = []
for u in sorted(map(set, b), key=len, reverse=True):
if not any(u <= v for v in result):
result.append(u)
print(result)
output
[{1, 2, 4, 6, 7}, {1, 2, 3}, {2, 3, 4}, {3, 4, 5}]
If you need to keep the inner lists as actual lists, and you also need to preserve the order, then we can do that with an additional pass over the data. But instead of using a list for result I'll use a set to make the tests more efficient. And that means turning the sublists into frozensets: plain sets won't work because only hashable objects can be put into a set.
b = [[1,2,3], [1,2], [3,5], [2,3,4], [2,3,4], [3,4,5], [1,2,4,6,7]]
temp = set()
for u in sorted(map(frozenset, b), key=len, reverse=True):
if not any(u <= v for v in temp):
temp.add(u)
newb = []
for u in b:
if set(u) in temp and u not in newb:
newb.append(u)
print(newb)
output
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [1, 2, 4, 6, 7]]
This is not very good, but it works:
result = []
for i in b:
for j in result:
if all(c in j for c in i):
break
else:
new_list.append(i)
for i in result:
for j in result:
if all(c in j for c in i) and result.index(i) != result.index(j):
del(result[result.index(i)])
break
You can use tuples and product to detect if item is a sublist, then construct a new list excluding those sublist
list comprehension
from itertools import product
b = [[1,2,3], [1,2], [3,5], [2,3,4], [3,4,5], [1,2,4,6,7]]
dirty = [i for i in b for j in b if i != j if tuple(i) in product(j, repeat = len(i))]
clean = [i for i in b if i not in dirty]
Expanded explanation:
dirty = []
for i in b:
for j in b:
if i != j:
if tuple(i) in product(j, repeat = len(i)):
dirty.append(i)
clean = [i for i in b if i not in dirty]
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [1, 2, 4, 6, 7]]

Python find indices in an array based on conditions

I have a list/array(can make them arrays, doesn't matter):
array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
I would like to create a new_array FROM array which looks like this:
new_array = [[1,2],[1],[1,2,3],[1,2],[1,2,3],[1,2,3,4,5]]
The logic I came up with was to start with a=0,b=1 and loop through array, when the value of array[a] < array[b], then a+=1,b+=1, but if the value of array[a]>=array[b], then append the value of b:
Here is what I tried:
index_1 = [] # This will be the list which will have the FIRST index
index_2 = [] # This will be the list which will have the SECOND index
a = 0
b = 1
for i in array:
if i[a] < i[b]:
a = a+1
b = b+1
elif i[a] >= i[b]:
index_2.append(b)
index_1.append(a)
So index_1 will have the first index and index_2 will have the second index and then, we can create the new_array by:
new_array = [(array[start:end]) for start,end in zip(index_1,index_2)]
Unfortunately, even though my idea is correct, the loop stops in if i[a]<i[b] because of IndexError: invalid index to scalar variable..
We can go through the array directly by the elements (more precisely by a pair of neighboring elements) simultaneously forming the result.
We initialize our result with a sublist of the 1st element of the array and then:
if the next element is greater than the previous one, we add it to the last sublist
otherwise - we add a new sublist with this element to the result.
array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
new_array = [[array[0]]] # assume the array is not empty
for a,b in zip(array, array[1:]):
if a<b:
new_array[-1].append(b)
else:
new_array.append([b])
print(new_array)
# [[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
UPD.
Alternatively, we can implement it with indices.
(based on this answer by #fortran)
First, prepare indices to split the array:
idxs_to_split = [idx for idx in range(1, len(array)) if not array[idx-1] < array[idx]]
pairs = zip([0] + idxs_to_split, idxs_to_split + [None])
Second, implement the splitting itself:
new_array = [array[i:j] for i,j in pairs]
print(new_array)
# [[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
Here's a simple approach, just keep track of the previous, and check if it is smaller. If it is, append to sub. If it isnt, append sub to the new list, then create a new sub. Always append sub at the end.
>>> new_array = []
>>> array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
>>> sub = []
>>> prev = float('-inf')
>>> for x in array:
... if prev < x:
... sub.append(x)
... else:
... new_array.append(sub)
... sub = [x]
... prev = x
...
>>> new_array.append(sub)
>>> new_array
[[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
def g(l):
acc = []
for i in l:
if not acc:
acc.append(i)
else:
if acc[-1] < i:
acc.append(i)
else:
yield acc
acc = [i]
yield acc
Here's a generator version that should work for all iterables. Usage would be something like
list(g(array))
And gives the desired
[[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
I simply checked for two conditions, 1. If next element == 1 and 2. If j is at last element of the list.
array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
newList = []
j = 0
k = 0
while j < len(array)-1:
if array[j+1] ==1:
newList.append(array[k:j+1])
k = j+1
if j+2==len(array):
newList.append(array[k:j+2])
k = j + 1
j +=1
print newList

get all the partitions of the set python with itertools

How to get all partitions of a set?
For example, I have array [1, 2, 3]. I need to get [[1], [2], [3]], [[1], [2, 3]], [[2], [1,3]], [[3], [1, 2]], [[1, 2, 3]].
Now, I wrote this code:
def neclusters(S, K):
for splits in itertools.combinations(range(len(S)), K):
yield np.split(S, 1 + np.array(splits))
But that code don't return [[2],[1,3]].
I could take all permutations of the original set and run this code on them. But can this be made easier?
I wrote this one for fun:
def partition(a_list):
yield [[x] for x in a_list]
for i in range(1, len(a_list) + 1):
_l = a_list[:]
yield [_l.pop(i-1), _l]
yield a_list
my_list = [1, 2, 3]
print list(partition(my_list))
#or
for p in partition(my_list):
print p
a = [1, 2, 3]
b = list()
for l in range(len(a)+1): b.append([c for c in combinations(a, l)])
print(b)
check this

Categories

Resources