Element-wise addition on tuple or list python - python

I was wondering if anyone can teach me how to do element wise addition on a tuple or list without using zip, numpy arrays, or any of those modules?
For example if I have:
a = (1,0,0,1)
b = (2,1,0,1)
how can i get: (3,1,0,2) instead of (1,0,0,1,2,1,0,1) ?

You can do this using operator.add
from operator import add
>>>map(add, a, b)
[3, 1, 0, 2]
In python3
>>>list(map(add, a, b))

List comprehensions are really useful:
[a[i] + b[i] for i in range(len(a))]

You can use the map function, see here:
https://docs.python.org/2/tutorial/datastructures.html#functional-programming-tools
map(func, seq)
For example:
a,b=(1,0,0,1),(2,1,0,1)
c = map(lambda x,y: x+y,a,b)
print c

This will save you if the length of both lists are not the same:
result = [a[i] + b[i] for i in range(min(len(a), len(b))]

This can be done by simply iterating over the length of list(assuming both the lists have equal length) and adding up the values at that indices in both the lists.
a = (1,0,0,1)
b = (2,1,0,1)
c = (1,3,5,7)
#You can add more lists as well
n = len(a)
#if length of lists is not equal then we can use:
n = min(len(a), len(b), len(c))
#As this would not lead to IndexError
sums = []
for i in xrange(n):
sums.append(a[i] + b[i] + c[i])
print sums

Here is a solution that works well for deep as well as shallow nested lists or tuples
import operator
def list_recur(l1, l2, op = operator.add):
if not l1:
return type(l1)([])
elif isinstance(l1[0], type(l1)):
return type(l1)([list_recur(l1[0], l2[0], op)]) + \
list_recur(l1[1:],l2[1:], op)
else:
return type(l1)([op(l1[0], l2[0])]) + \
list_recur(l1[1:], l2[1:], op)
It (by default) performs element wise addition, but you can specify more complex functions and/or lambdas (provided they are binary)

Related

How does exact list comparison work?

I was making a code in which I needed to compare two lists for exact matches, and I found this code (I had to add print so it would output the result):
a = [1,2,3,4,5]
b = [9,8,7,6,5]
print [i for i, j in zip(a, b) if i == j]
This code outputs [5] because it prints the list, and if I change the code to
a = [1,2,3,5,4]
b = [9,8,7,6,5]
print [i for i, j in zip(a, b) if i == j]
It outputs [] because the list is empty.
This is all fine and good because it solves my list comparison problem, but I have almost no idea why or how it works. I would greatly appreciate either a detailed or partial explanation if you have one.
That's called a "list comprehension", let's break it down here:
[i for i, j in zip(a, b) if i == j] can be roughly understood as making a list of [i], where i (and j) come from zip(a, b) but only if i == j.
zip(a, b) takes two arrays, a and b, and combines them in such a way that the end result looks like [(1, 9), (2, 8), ...]
So effectively, you're processing the result of zip(a, b) and iterating over tuples i, j to return only if i == j is "truthy". Explaining "truthy" is a bit out of scope for this answer, but in this case, the expression i == j evaluates to True if i and j have the same value. I.e., 5 == 5 is True, where 5 == 4 is False.
This is a list comprehension, which is a python syntax feature. You can read about it in the python tutorial here.
It is being combined with the zip() built-in function. Documentation for that function is here.
A tl;dr is: the zip function makes pairs from both lists, and the list comprehension will filter out the pairs that don't compare equal, selecting only the pairs that match
To get the code working with the random indexing of the elements, you can try the following:
a = [1,2,3,5,4]
b = [9,8,7,6,5]
print [i for i in a for j in b if i == j]
This will output:
[5]

loop function 2 variables [duplicate]

This question already has answers here:
How do I iterate through two lists in parallel?
(8 answers)
Closed 2 years ago.
How can I include two variables in the same for loop?
t1 = [a list of integers, strings and lists]
t2 = [another list of integers, strings and lists]
def f(t): #a function that will read lists "t1" and "t2" and return all elements that are identical
for i in range(len(t1)) and for j in range(len(t2)):
...
If you want the effect of a nested for loop, use:
import itertools
for i, j in itertools.product(range(x), range(y)):
# Stuff...
If you just want to loop simultaneously, use:
for i, j in zip(range(x), range(y)):
# Stuff...
Note that if x and y are not the same length, zip will truncate to the shortest list. As #abarnert pointed out, if you don't want to truncate to the shortest list, you could use itertools.zip_longest.
UPDATE
Based on the request for "a function that will read lists "t1" and "t2" and return all elements that are identical", I don't think the OP wants zip or product. I think they want a set:
def equal_elements(t1, t2):
return list(set(t1).intersection(set(t2)))
# You could also do
# return list(set(t1) & set(t2))
The intersection method of a set will return all the elements common to it and another set (Note that if your lists contains other lists, you might want to convert the inner lists to tuples first so that they are hashable; otherwise the call to set will fail.). The list function then turns the set back into a list.
UPDATE 2
OR, the OP might want elements that are identical in the same position in the lists. In this case, zip would be most appropriate, and the fact that it truncates to the shortest list is what you would want (since it is impossible for there to be the same element at index 9 when one of the lists is only 5 elements long). If that is what you want, go with this:
def equal_elements(t1, t2):
return [x for x, y in zip(t1, t2) if x == y]
This will return a list containing only the elements that are the same and in the same position in the lists.
There's two possible questions here: how can you iterate over those variables simultaneously, or how can you loop over their combination.
Fortunately, there's simple answers to both. First case, you want to use zip.
x = [1, 2, 3]
y = [4, 5, 6]
for i, j in zip(x, y):
print(str(i) + " / " + str(j))
will output
1 / 4
2 / 5
3 / 6
Remember that you can put any iterable in zip, so you could just as easily write your exmple like:
for i, j in zip(range(x), range(y)):
# do work here.
Actually, just realised that won't work. It would only iterate until the smaller range ran out. In which case, it sounds like you want to iterate over the combination of loops.
In the other case, you just want a nested loop.
for i in x:
for j in y:
print(str(i) + " / " + str(j))
gives you
1 / 4
1 / 5
1 / 6
2 / 4
2 / 5
...
You can also do this as a list comprehension.
[str(i) + " / " + str(j) for i in range(x) for j in range(y)]
Any reason you can't use a nested for loop?
for i in range(x):
for j in range(y):
#code that uses i and j
for (i,j) in [(i,j) for i in range(x) for j in range(y)]
should do it.
If you really just have lock-step iteration over a range, you can do it one of several ways:
for i in range(x):
j = i
…
# or
for i, j in enumerate(range(x)):
…
# or
for i, j in ((i,i) for i in range(x)):
…
All of the above are equivalent to for i, j in zip(range(x), range(y)) if x <= y.
If you want a nested loop and you only have two iterables, just use a nested loop:
for i in range(x):
for i in range(y):
…
If you have more than two iterables, use itertools.product.
Finally, if you want lock-step iteration up to x and then to continue to y, you have to decide what the rest of the x values should be.
for i, j in itertools.zip_longest(range(x), range(y), fillvalue=float('nan')):
…
# or
for i in range(min(x,y)):
j = i
…
for i in range(min(x,y), max(x,y)):
j = float('nan')
…
"Python 3."
Add 2 vars with for loop using zip and range; Returning a list.
Note: Will only run till smallest range ends.
>>>a=[g+h for g,h in zip(range(10), range(10))]
>>>a
>>>[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
For your use case, it may be easier to utilize a while loop.
t1 = [137, 42]
t2 = ["Hello", "world"]
i = 0
j = 0
while i < len(t1) and j < len(t2):
print t1[i], t2[j]
i += 1
j += 1
# 137 Hello
# 42 world
As a caveat, this approach will truncate to the length of your shortest list.
I think you are looking for nested loops.
Example (based on your edit):
t1=[1,2,'Hello',(1,2),999,1.23]
t2=[1,'Hello',(1,2),999]
t3=[]
for it1, e1 in enumerate(t1):
for it2, e2 in enumerate(t2):
if e1==e2:
t3.append((it1,it2,e1))
# t3=[(0, 0, 1), (2, 1, 'Hello'), (3, 2, (1, 2)), (4, 3, 999)]
Which can be reduced to a single comprehension:
[(it1,it2,e1) for it1, e1 in enumerate(t1) for it2, e2 in enumerate(t2) if e1==e2]
But to find the common elements, you can just do:
print set(t1) & set(t2)
# set([(1, 2), 1, 'Hello', 999])
If your list contains non-hashable objects (like other lists, dicts) use a frozen set:
from collections import Iterable
s1=set(frozenset(e1) if isinstance(e1,Iterable) else e1 for e1 in t1)
s2=set(frozenset(e2) if isinstance(e2,Iterable) else e2 for e2 in t2)
print s1 & s2

Python: intersection of 2 lists keeping duplicates from both lists

I want to efficiently find the intersection of two lists , keeping duplicates from both, e.g. A=[1,1,2,3], B=[1,1,2,4] should return [1,1,1,1,2]
I know a similar question was asked previously (Python intersection of two lists keeping duplicates)
however this does not help me because only the duplicates from one list are retained.
The following works
def intersect(A,B):
C=[]
for a in A:
for b in B:
if a==b:
C.append(a)
return C
however it isn't efficient enough for what I'm doing! To speed things up I tried sorting the lists
def intersect(A,B):
A.sort()
B.sort()
C=[]
i=0
j=0
while i<len(A) and j<len(B):
if A[i]<=B[j]:
if A[i]==B[j]:
C.append(A[i])
i+=1
else:
j=j+1
return C
however this only keeps the duplicates from list B. Any suggestions?
Here is the answer to your question as asked:
import collections
for A,B,expected_output in (
([1,1,2,3], [1,1,2,4], [1,1,1,1,2]),
([1,1,2,3], [1,2,4], [1,1,2])):
cntA = collections.Counter(A)
cntB = collections.Counter(B)
output = [
x for x in sorted(set(A) & set(B)) for i in range(cntA[x]*cntB[x])]
assert output == expected_output
Here is the answer to the question as originally interpreted by myself and two others:
import collections
A=[1,1,2,3]
B=[1,1,2,4]
expected_output = [1,1,1,1,2,2]
cntA = collections.Counter(A)
cntB = collections.Counter(B)
cnt_sum = collections.Counter(A) + collections.Counter(B)
output = [x for x in sorted(set(A) & set(B)) for i in range(cnt_sum[x])]
assert output == expected_output
You can find the collections.Counter() documentation here. collections is a great module and I highly recommend giving the documentation on the whole module a read.
I realized you don't actually need to find the intersection of the sets, because the "count of a missing element is zero" according to the documentation:
import collections
for A,B,expected_output in (
([1,1,2,3], [1,1,2,4], [1,1,1,1,2]),
([1,1,2,3], [1,2,4], [1,1,2])):
cntA = collections.Counter(A)
cntB = collections.Counter(B)
output = [
x for x in sorted(set(A)) for i in range(cntA[x]*cntB[x])]
assert output == expected_output
How about this:
a_set = set(A)
b_set = set(B)
intersect = [i for i in A if i in b_set] + [j for j in B if j in a_set]
Two list comprehensions concatenated. A bit of extra time and memory is used to create sets of A and B, but that will be more than offset by the efficiency of checking membership of items in a set vs list.
You could also spruce it up a bit:
set_intersect = set(A) & set(B)
list_intersect = [ele for ele in A+B if ele in set_intersect]
Coerce both lists to sets, take their intersection, then use a list comprehension to add all elements from both lists A and B if they appear in the set intersection.
I'm having a hard time speeding your code since I don't know what are you running it on. It makes a lot of difference whether you run it on small or large lists and how many distinct elements are there. Anyway, here are some suggestions:
1.
def intersect(a, b):
count_a = Counter(a)
count_b = Counter(b)
count_mul = []
for i in count_a:
count_mul.extend([i] * (count_a[i] * count_b[i]))
return count_mul
2.
This returns an iterator, you can use list(iterator) to turn it into a list
def intersect(a, b):
count_a = Counter(a)
count_b = Counter(b)
count_mul = Counter()
for i in count_a:
count_mul[i] += count_a[i] * count_b[i]
return count_mul.elements()
3.
Very similar to your way but without changing the size of the list which takes time.
def intersect(A, B):
return [a for a in A for b in B if a == b]
I'm not sure this gives any improvement to your original way, it really depends on the inputs, but your way is O(n*m) and mine is O(n+m).
You can use the module timeit to check how fast it runs on your input:
from timeit import timeit
timeit('test.intersect(A, B)', 'import test; A = [1,1,2,3]; B = [1,1,2,4]')

divide list using recursion

I am trying to implement this function using recursion, the function takes a function parameter f where when passed a value it will return as true or false. It should check all values in the list and store all true values in a list and false values in another list returning them in a tuple.
def divL(f, l):
if not l:
return ([],[])
else:
a = list()
b = list()
for i in range(len(l)):
if f(l[i]):
a.append(l[i])
else:
b.append(l[i])
return (a, b)
Recursive version.
But I agree with others that it is better to do this without recursion.
def divL(f, l):
if not l:
return ([],[])
else:
a, b = divL(f,l[1:])
if f(l[0]):
a.insert(0, l[0])
else:
b.insert(0, l[0])
return (a, b)
#-- test ---
def f(x): return x > 0
divL(f,[1,-2,3,4,2,-5])
([1, 3, 4, 2], [-2, -5])
To solve this problem recursively, then you can start with:
def div_l(fn, elements):
if not elements:
return ([], [])
else:
trues, falses = div_l(fn, elements[1:])
if fn(elements[0]):
trues.append(elements[0])
else:
falses.append(elements[0])
return (trues, falses)
One thing to note about your code, is that, at the moment, there are no recursive calls. In the code above, our base case is when the list of elements is empty (if not elements). Then, we constantly check the first element of the list to see if it satisfies fn, appending it to trues or falses appropriately. Then we pass all the elements of the list besides the first one (elements[1:]) to the function again and repeat until the list is empty.
Having said that, the problem does not seem to be recursive in nature. Why not just use list comprehensions:
a = [element for element in l if f(element)]
b = [element for element in l if not f(element)]
Also, names like a, b, l and f are not great because they say nothing about what they actually mean.
Regarding the way you have written your code so far, the Pythonic way to iterate through the elements of a list is not the same as languages like C/C++/Java/etc; you should rarely need to get list items using their index. Instead you can re-write your for statement as follows:
for element in l:
if f(element):
a.append(element)
else:
b.append(element)
P.S. I haven't tested the code above yet, but it should be a reasonable starting point.
Recursive function that takes split lists as parameters:
def _R(f, l, ts, fs):
if not l:
return ts, fs
if f(l[0]):
return _R(f, l[1:], ts + l[:1], fs)
return _R(f, l[1:], ts, fs + l[:1])
def R(f, l):
return _R(f, l, [], [])
print R( lambda x: x > 10, range(20) )

Comparing two lists and only printing the differences? (XORing two lists)

I'm trying to create a function that takes in 2 lists and returns the list that only has the differences of the two lists.
Example:
a = [1,2,5,7,9]
b = [1,2,4,8,9]
The result should print [4,5,7,8]
The function so far:
def xor(list1, list2):
list3=list1+list2
for i in range(0, len(list3)):
x=list3[i]
y=i
while y>0 and x<list3[y-1]:
list3[y]=list3[y-1]
y=y-1
list3[y]=x
last=list3[-1]
for i in range(len(list3) -2, -1, -1):
if last==list3[i]:
del list3[i]
else:
last=list3[i]
return list3
print xor([1,2,5,7,8],[1,2,4,8,9])
The first for loop sorts it, second one removes the duplicates. Problem is the result is
[1,2,4,5,7,8,9] not [4,5,7,8], so it doesn't completely remove the duplicates? What can I add to do this.
I can't use any special modules, .sort, set or anything, just loops basically.
You basically want to add an element to your new list if it is present in one and not present in another. Here is a compact loop which can do it. For each element in the two lists (concatenate them with list1+list2), we add element if it is not present in one of them:
[a for a in list1+list2 if (a not in list1) or (a not in list2)]
You can easily transform it into a more unPythonic code with explicit looping through elements as you have now, but honestly I don't see a point (not that it matters):
def xor(list1, list2):
outputlist = []
list3 = list1 + list2
for i in range(0, len(list3)):
if ((list3[i] not in list1) or (list3[i] not in list2)) and (list3[i] not in outputlist):
outputlist[len(outputlist):] = [list3[i]]
return outputlist
Use set is better
>>> a = [1,2,5,7,9]
>>> b = [1,2,4,8,9]
>>> set(a).symmetric_difference(b)
{4, 5, 7, 8}
Thanks to #DSM, a better sentence is:
>>> set(a)^set(b)
These two statements are the same. But the latter is clearer.
Update: sorry, I did not see the last requirement: cannot use set. As far as I see, the solution provided by #sashkello is the best.
Note: This is really unpythonic and should only be used as a homework answer :)
After you have sorted both lists, you can find duplicates by doing the following:
1) Place iterators at the start of A and B
2) If Aitr is greater than Bitr, advance Bitr after placing Bitr's value in the return list
3) Else if Bitr is greater than Aitr, advance Aiter after placing Aitr's value in the return list
4) Else you have found a duplicate, advance Aitr and Bitr
This code works assuming you've got sorted lists. It works in linear time, rather than quadratic like many of the other solutions given.
def diff(sl0, sl1):
i0, i1 = 0, 0
while i0 < len(sl0) and i1 < len(sl1):
if sl0[i0] == sl1[i1]:
i0 += 1
i1 += 1
elif sl0[i0] < sl1[i1]:
yield sl0[i0]
i0 += 1
else:
yield sl1[i1]
i1 += 1
for i in xrange(i0, len(sl0)):
yield sl0[i]
for i in xrange(i1, len(sl1)):
yield sl1[i]
print list(diff([1,2,5,7,9], [1,2,4,8,9]))
Try this,
a = [1,2,5,7,9]
b = [1,2,4,8,9]
print set(a).symmetric_difference(set(b))
Simple, but not particularly efficient :)
>>> a = [1,2,5,7,9]
>>> b = [1,2,4,8,9]
>>> [i for i in a+b if (a+b).count(i)==1]
[5, 7, 4, 8]
Or with "just loops"
>>> res = []
>>> for i in a+b:
... c = 0
... for j in a+b:
... if i==j:
... c += 1
... if c == 1:
... res.append(i)
...
>>> res
[5, 7, 4, 8]

Categories