Remove common elements between two list with repeated elements - python

I need your help because I'm a bit stuck with this issue.
I've got two list with repeated elements:
L = [1,2,5,2,4,1,9]
S =[4,2,9,1]
I want to get the "difference" between the two list, as if I do LS = L-S, and the result should be LS = [1,2,5].
I know that l2 will always be a subset of L, thus S will never have a value not present in L.
Note that only one 1 and one 2 has been removed from L, because there was only one 1 and one 2 in S. L can have elements repeated n times, not just 2 times.
Do you know if there's an "easy" way to do this? I wouldn't like to get an O(n^2) program.
Thank you so much!

Use collections.Counter (Python's version of a multi-set)
from collections import Counter
L = [1, 2, 5, 2, 4, 1, 9]
S = [4, 2, 9, 1]
LS = list((Counter(L) - Counter(S)).elements())
print(LS)
Output
[1, 2, 5]
The overall complexity of this approach is O(n).

You can use this:
l = [1, 2, 5, 2, 4, 1, 9]
s = [4, 2, 9, 1]
for i in range(len(l)-1,-1,-1):
if l[i] in s:
s.remove(l[i])
l.pop(i)
print(l)
But i have no idea about its complexity.

Related

improve efficiency of a nested loop

I need to find the number of pairs with consecutive numbers in a list. If elements in the list are repeated, they should be treated as members of a distinct pair. For instance, if the list were [1, 1, 1, 2, 2, 5, 8, 8], then there are three ways to choose 1 and two ways to choose 2, or a total of 3×2=63×2=6 ways to choose the pair (1, 2), so that the answer would, in this case, be 6.
My solution currently contains a nested loop as below. The code works but I want to optimize for a runtime of less than 2 seconds.
Can anyone give me some pointers on how to improve the runtime of this solution?
L = [1, 2, 5, 8]
count = 0
for i in range(0,len(L)-1):
for x in range(i+1, len(L)):
if L[x] == L[i] + 1 or L[x] == L[i] -1 :
count+=1
You could use the Counter class from collection to classify and count the available numbers, then sum up the product of counts for existing pairs of consecutive values:
from collections import Counter
L = [1, 1, 1, 2, 2, 5, 8, 8]
counts = Counter(L)
r = sum(c*counts[n+1] for n,c in counts.items())
print(r) # 6

Fill lists in list with zeros if their length less than

I have a list of lists with different sizes but I want to make them all the same length. For example, make them with length of 5 by padding with zeros if length less than 5 or cut the list if length is more than 5. For example, I have a list:
foo = [
[1, 2, 3],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5, 6, 7]]
result = [
[1, 2, 3, 0, 0],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
Do you have an idea of optimal and fast solution, if the list of lists is large?
List comprehension
Make a fill list and use slicing to get the appropriate lengths.
n = 5
fill = [0] * n
result = [sublist[:n] + fill[len(sublist):] for sublist in foo]
result = []
for sublist in foo:
size = len(sublist)
result.append(sublist[:5] + [0]*(5 - size))
To perform this optimization, i sliced additional elements beyond n = 5, and replaced with 0 those not reaching n = 5 by checking how many elements they miss.
def listOptimization(foo, n):
# slicing foo to have n elements on all child elements of foo
for i in range(len(foo)):
foo[i] = foo[i][:n]
# optimizing
for i in range(len(foo)):
# check if foo child element is less than n
# if true, append to the list with 0 depending on how many
# elements to reach n
if len(foo[i])<n:
temp = n-len(foo[i])
for x in range(temp):
foo[i].append(0)
return foo
result = [[bar[i] if i<len(bar)else 0 for i in range(5)] for bar in foo]
there are 5 elements in a row, so for i in range(5), the exceed 5 will be discard. then assign value directly,if the length of each row is less than i, assign 0
Actually, I found a pretty fast solution for me. If you have an idea how to solve it without a for loop please post.
for row in foo:
while len(row) < 5:
row.append(0)
else:
row[:5]

Python list comprehension mirroring values

I tried to figure out the following problem through list comprehension but I couldn't make it work. I will show you how I solved the problem using a loop and a list comprehension.
So, I have a list that can have 0 to 6 elements in a range(6) and when I apply my function on it I want to change the values, as shown here:
l = [0, 1, 2, 3, 4, 5]
mirror = [5, 4, 3, 2, 1, 0]
I don't want to just rotate the array by 180 degrees but I actually want to replace the values. For example, my list looks like this now:
l = [2, 5]
Then l_inverted list should look like this:
l_inverted = [3, 0]
I came up with a regular way to solve it but ever since I started learning Python I've preferred list comprehensions.
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
i = 0
for element in l:
l[i] = mirror[element]
i += 1
This actually inverts the l list. Here's my approach using a list comprehension:
l = [3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
This works fine.
Until:
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
So it will replace 5 with 0, 2 with 3 and both 5s (the new one too) become 0. Obviously, I don't want it like that.
Should I stick to the working solution or is there a smooth way to solve it with list comprehensions? I'm trying to practice list comprehensions at all times but it's not fully in my brain yet. Thanks a lot.
If you want it as a list comprehension:
>> l = [0, 3, 5]
>> mirror = [5, 4, 3, 2, 1, 0]
>> l_inverted = [mirror[x] for x in l]
>> l_inverted
[5, 2, 0]
You are drowning in a spoonful of water and trying to take us with you.
You are using bad naming conventions that make your simple problem complicated to comprehend.
orig = [0, 1, 2, 3, 4, 5]
orig_rev = l[::-1]
selector = [0, 3, 5]
result = [orig_rev[i] for i in selector]
print(result ) # [5, 2, 0]
Based on your first two examples, it seems you are looking for the complement of each list value according to some base, similar to ones' complement. You could either hard-code the base as 5 or whatever, or you could assume it's the maximum number in the list and calculate it. Here's a solution for the latter:
Concept
<this complement> = <max value in list> - <this value>
Code
values = [0, 3, 2, 4, 5]
max_value = max(values)
complements = [max_value - value for value in values]
print complements
Result
[5, 2, 3, 1, 0]

Get common elements majority of lists in python

Given 4 lists, I want to get elements that are common to 3 or more lists.
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
Hence, the output should be [1, 2, 3, 4].
My current code is as follows.
result1 = set(a) & set(b) & set(c)
result2 = set(b) & set(c) & set(d)
result3 = set(c) & set(d) & set(a)
result4 = set(d) & set(a) & set(b)
final_result = list(result1)+list(result2)+list(result3)+list(result4)
print(set(final_result))
It works fine, and give the desired output. However, I am interested in knowing if there is an easy way of doing this in Python, ie: are there any built in functions for this?
Using a Counter, you can do this like:
Code:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
from collections import Counter
counts = Counter(sum(([list(set(i)) for i in (a, b, c, d)]), []))
print(counts)
more_than_three = [i for i, c in counts.items() if c >= 3]
print(more_than_three)
Results:
Counter({1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 2, 7: 1})
[1, 2, 3, 4]
Iterate over the values in all lists to create a dict of {value: number_of_lists_the_value_appears_in}:
from collections import defaultdict
counts = defaultdict(int)
for list_ in (a, b, c, d):
for value in set(list_): # eliminate duplicate values with `set`
counts[value] += 1
Then in the second step remove all values with a count < 3:
result = [value for value, count in counts.items() if count >= 3]
print(result) # [1, 2, 3, 4]
The code below will solve the generalised problem (with n lists, and a requirement that a common element must be in at least k of them). It will work with non-hashable items, which is the main disadvantage of all the other answers:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 2, 3, 4, 4, 5, 6]
d = [1, 2, 6, 7]
lists = [a, b, c, d]
result = []
desired_quanity = 3
for i in range(len(lists) - desired_quanity + 1): #see point 1 below
sublist = lists.pop(0) #see point 2
for item in sublist:
counter = 1 #1 not 0, by virute of the fact it is in sublist
for comparisonlist in lists:
if item in comparisonlist:
counter += 1
comparisonlist.remove(item) #see point 3
if counter >= desired_quanity:
result.append(item)
This has the disadvantage that for each element in every list, we have to check in every other list to see if it is there, but we can make things more efficient in a few ways. Also look-ups are alot slower in lists than sets (which we can't use since the OP has non-hashable items in the lists), and so this may be slow for very large lists.
1) If we require an item to be in k lists, we don't need to check each item in the last k-1 lists, as we would have already picked it up whilst searching through the first k lists.
2) Once we have searched through a list, we can discard that list, since any items in the just-searched-list that might contribute to our final result, will again already have been dealt with. This means that with each iteration we have fewer lists to search through.
3) When we have checked if an item is in enough lists, we can remove that item from the list, which means not only is the number of lists getting shorter as we proceed, the lists themselves are getting shorter, meaning quicker lookups.
As an aftersort, if we the original lists happen to be sorted beforehand, this might also help this algorithm work efficiently.
create a dictionary of counts and filter out those with count less than 3

I need to create a list containing all the sums of a list taken three at a time, ie, add first 3 elements then the next 3 [duplicate]

This question already has answers here:
How do I split a list into equally-sized chunks?
(66 answers)
Closed 6 years ago.
I need to add the first three elements of a list then add the next three elements of a list and so forth. This is the code I have got so far:
def get_triple_sums_list(a_list):
new_list = []
for numbers in range(0,len(a_list)):
numbers = sum(a_list[:3])
new_list.append(numbers)
return new_list
if a_list == []:
return []
For the list:
[1, 5, 3, 4, 5, 2]
This in turn gives me the result:
[9]
I need to get
[9, 11]
If the remaining numbers is less than 3, it gives me the remainder of the sum ie,
[1, 6, 2, 4, 3]
Gives me
[9, 7]
And
[1, 6, 2, 4]
Give me
[9, 4]
Let's analyze your code!
def get_triple_sums_list(a_list):
new_list = []
for numbers in range(0,len(a_list)):
numbers = sum(a_list[:3]) #You should be using the variable
#numbers here somehow.
#^^^^^^^ - You are overwriting the for-loop index.
new_list.append(numbers)
return new_list #Why are you returning here? You should be
#appending to `new_list`.
if a_list == []:
return []
Here is the fixed code:
def get_triple_sums_list(a_list):
new_list = []
for index in range(0,len(a_list), 3): #Range takes a 3rd param!
total = sum(a_list[index:index+3])#Get all the elements from the
#index to index+3
new_list.append(total)
return new_list
UPDATE: It seems there's a shortening contest going on -- and I do not want to be left behind. Here's an ugly version I'd like to add to the list.
>>> a = [1,2,3,4,5,6,7,8]
>>> a += [0]*(len(a)%3) #For people who are too lazy to import izip_longest
>>> map(sum,zip(a[::3], a[1::3], a[2::3]))
[6, 15, 15]
I like SuperSaiyan's approach of explaining things, I'll be the one who shortens it a bit. You can get the same result with a single comprehension:
l = [1, 5, 3, 4, 5, 2]
n = 3
r = [sum(l[i:i+n]) for i in range(0, len(l), n)]
print(r)
[9, 11]
l[i:i+n] splits the list in even chunks of length 3 and sum takes care of adding these together. Using the for i in range(0, len(l), n) we dictate that this operation is to happen for ceil(len(l) / 3) times.
Just cuz I like to be different.
l = [1, 5, 3, 4, 5, 3, 42]
g = lambda l,s: [sum(l[i:i+s]) for i in range(0,len(l),s)]
print g(l,3)
#>> [9,12,42]
The other answer mentions the fault with your code. However do note that it's always easier to use a list comprehension in these cases.
>>> l = [1, 5, 3, 4, 5, 2]
>>> [sum(l[i:i+3]) for i in range(0,len(l),3)]
[9, 11]
It also works for un-mod-3 lists
>>> l = [1, 5, 3, 4, 5]
>>> [sum(l[i:i+3]) for i in range(0,len(l),3)]
[9, 9]
See What does "list comprehension" mean? How does it work and how can I use it? for more details about a list comprehension.
Here is a slightly different way of doing it using zip_longest from itertools (izip_longest in python2), it splits the list in three lists then zip them to get packs of three elements and finally sums the packs:
from itertools import zip_longest
a=[1, 6, 2, 4, 3]
b=zip_longest(a[0::3],a[1::3],a[2::3],fillvalue=0)
result=[sum(x) for x in b]
>>>[9, 7]
Alternatively, you may achieve it by using map() with lambda function as:
>>> my_list = [1, 5, 3, 4, 5, 2]
>>> list(map(lambda x: sum(my_list[x:x+3]), range(0, len(my_list), 3)))
[9, 11]

Categories

Resources