Element-wise addition of 2 misaligned lists? - python

I have 2 differently sized lists (a and b) and starting index for a (a_start). I would like to add the lists together, accounting for the fact they are not aligned.
Example inputs and expected output:
a = [1,2]
b = [1,2,3,4]
a_start = 1 # 1st element of a needs to be added to 2nd element of b, etc.
expected result: [1,3,5,4]
Another example:
a = [1,2,3,4]
b = [1,2,3,4]
a_start = 2 # 1st element of a needs to be added to 3rd element of b, etc.
expected result: [1, 2, 4, 6, 3, 4]
My current solution:
[i+j for i,j in zip([0]*a_start + a + [0]*(len(b) - len(a) - a_start), b+[0]*max(0,len(a) + a_start - len(b)))]
What is a more elegant/Pythonic way to do this for any arbitrarily sized lists?

It would probably be best to use zip_longest:
a_padded = a_start * [0] + a
result = [x + y for x, y in itertools.zip_longest(a_padded, b, fillvalue=0)]
Also, if space is an issue, you may want to avoid creating a copy of a. In that case:
from itertools import zip_longest, chain, repeat
a_padded = chain(repeat(0, a_start), a)
result = [x + y for x, y in zip_longest(a_padded, b, fillvalue=0)]

Since zip_longest is already taken, here's one like yours, but I compute the length difference only once (and as a separate line, for readability) and take advantage of Python treating negative list multipliers like zero (your max(0, ...) is unnecessary):
d = a_start + len(a) - len(b)
result = [*map(sum, zip([0]*a_start + a + [0]*-d, b + [0]*d))]

Related

NumPy slicing: All except one array entry

What is the best way to exclude exact one NumPy array entry from an operation?
I have an array x containing n values and want to exclude the i-th entry when I call numpy.prod(x). I know about MaskedArray, but is there another/better way?
I think the simplest would be
np.prod(x[:i]) * np.prod(x[i+1:])
This should be fast and also works when you don't want to or can't modify x.
And in case x is multidimensional and i is a tuple:
x_f = x.ravel()
i_f = np.ravel_multi_index(i, x.shape)
np.prod(x_f[:i_f]) * np.prod(x_f[i_f+1:])
You could use np.delete whch removes an element from a one-dimensional array:
import numpy as np
x = np.arange(1, 5)
i = 2
y = np.prod(np.delete(x, i)) # gives 8
I don't think there is any better way, honestly. Even without knowing the NumPy functions, I would do it like:
#I assume x is array of len n
temp = x[i] #where i is the index of the value you don't want to change
x = x * 5
#...do whatever with the array...
x[i] = temp
If I understand correctly, your problem is one dimensional? Even if not, you can do this the same way.
EDIT:
I checked the prod function and in this case I think you can just replace the value u don't want to use with 1 (using temp approach I've given you above) and later just put in the right value. It is just a in-place change, so it's kinda efficient. The second way you can do this is just to divide the result by the x[i] value (assuming it's not 0, as commenters said).
As np.prod is taking the product of all the elements in an array, if we want to exclude one element from the solution, we can set that element to 1 first in order to ignore it (as p * 1 = p).
So:
>>> n = 10
>>> x = np.arange(10)
>>> i = 0
>>> x[i] = 1
>>> np.prod(x)
362880
which, we can see, works:
>>> 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9
362880
You could use a list comprehension to index all the points but 1:
i = 2
np.prod(x[[val for val in range(len(x)) if val != i]])
or use a set difference:
np.prod(x[list(set(range(len(x)) - {i})])

How can i iteratively apply elements from the 2 lists into a new function? [duplicate]

This question already has answers here:
How do I iterate through two lists in parallel?
(8 answers)
Closed 6 years ago.
Suppose I have 2 lists, for which the i-th element of the first lists corresponds to the i-th element of the second list. How can i iteratively apply elements from the 2 lists into a different function?
def GetLists(n):
List1 = []
List2 = []
n = int(input("how many terms: "))
for i in range(1,n):
val1 = float(input("what is val1: "))
val2 = float(input("what is the corresponding val2: "))
List1.append(val1)
List2.append(val2)
return List1, List2
def newfunc(ListA, ListB, var):
# take i-th elements from lists A and B where (a,b) are the i-th elements of A and B
# want output = sum(a * var ** b) for index in len(A) if len(A) == len(B)
What is the most pythonic way to do this? If possible, I would like to do so without importing external modules.
Edit: I checked other solutions. The "duplicate" answer requires importing modules; I was trying to do without. Also, I am trying to do an operation that returns an output rather than print values, which complicates the use of zip beyond the level shown in the duplicate answer.
take i-th elements from lists A and B where (a,b) are the i-th elements of A and B
want output = sum(a * var ** b) for index in len(A) if len(A) == len(B)
Is this what you're looking for? It will zip two lists of the same length together, calculating f(a, b, i) = a[i] * var ** b[i] for some constant var and for each i where 0 <= i < len(a). It then returns the sum.
def process(list_a, list_b, var):
if len(list_a) != len(list_b):
raise ValueError('Lists are not equal in length')
def combine():
for a, b in zip(list_a, list_b):
yield a * var ** b
return sum(combine())
print(process([5, 2, 3], [2, 2, 3], 10))
Output
3300
This output is the result of (1 * 10 ** 2) + (2 * 10 ** 2) + (3 * 10 ** 3).
Edit
The approach above decouples the combination logic - which is the focus of your question - from the summation logic. An alternative approach which is arguably more Pythonic (as per your requirements) and a lot shorter would be to use a generator expression, as suggested in the comments for this answer:
def process(list_a, list_b, var):
if len(list_a) != len(list_b):
raise ValueError('Lists are not equal in length')
return sum(a * var ** b for a, b in zip(list_a, list_b))
print(process([1, 2, 3], [2, 2, 3], 10))
Essentially, the expression within sum acts as an anonymous replacement for the generator function combine that I defined in the previous approach.

Getting difference from all possible pairs from a list Python

I have an int list with unspecified number. I would like to find the difference between two integers in the list that match a certain value.
#Example of a list
intList = [3, 6, 2, 7, 1]
#This is what I have done so far
diffList = []
i = 0
while (i < len(intList)):
x = intList[i]
j = i +1
while (j < len(intList)):
y = intList[j]
diff = abs(x-y)
diffList.append(diff)
j += 1
i +=1
#Find all pairs that has a difference of 2
diff = diffList.count(2)
print diff
Is there a better way to do this?
EDIT: Made changes to the codes. This is what I was trying to do. What I want to know is what else can I use besides the loop.
seems like a job for itertools.combinations
from itertools import combinations
for a, b in combinations(intList, 2):
print abs(a - b)
You could even turn this one into a list comprehension if you wanted to :)
[abs(a -b) for a, b in combinations(intList, 2)]
int_list = [3, 6, 2, 7, 1]
for x in int_list:
for y in int_list:
print abs(x - y)

"for loop" with two 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

Aesthetic way of appending to a list in Python?

When appending longer statements to a list, I feel append becomes awkward to read. I would like a method that would work for dynamic list creation (i.e. don't need to initialize with zeros first, etc.), but I cannot seem to come up with another way of doing what I want.
Example:
import math
mylist = list()
phi = [1,2,3,4] # lets pretend this is of unknown/varying lengths
i, num, radius = 0, 4, 6
while i < num:
mylist.append(2*math.pi*radius*math.cos(phi[i]))
i = i + 1
Though append works just fine, I feel it is less clear than:
mylist[i] = 2*math.pi*radius*math.cos(phi[i])
But this does not work, as that element does not exist in the list yet, yielding:
IndexError: list assignment index out of range
I could just assign the resulting value to temporary variable, and append that, but that seems ugly and inefficient.
You don;t need an existing list and append to it later. Just use list comprehension
List comprehension,
is fast,
easy to comprehend,
and can easily be ported as a generator expression
>>> import math
>>> phi = [1,2,3,4]
>>> i, num, radius = 0, 4, 6
>>> circum = 2*math.pi*radius
>>> mylist = [circum * math.cos(p) for p in phi]
Reviewing your code, here are some generic suggestions
Do not compute a known constant in an iteration
while i < num:
mylist.append(2*math.pi*radius*math.cos(phi[i]))
i = i + 1
should be written as
circum = 2*math.pi
while i < num:
mylist.append(circum*math.cos(phi[i]))
i = i + 1
Instead of while use for-each construct
for p in phi:
mylist.append(circum*math.cos(p))
If an expression is not readable, break it into multiple statements, after all readability counts in Python.
In this particular case you could use a list comprehension:
mylist = [2*math.pi*radius*math.cos(phi[i]) for i in range(num)]
Or, if you're doing this sort of computations a lot, you could move away from using lists and use NumPy instead:
In [78]: import numpy as np
In [79]: phi = np.array([1, 2, 3, 4])
In [80]: radius = 6
In [81]: 2 * np.pi * radius * np.cos(phi)
Out[81]: array([ 20.36891706, -15.68836613, -37.32183785, -24.64178397])
I find this last version to be the most aesthetically pleasing of all. For longer phi it will also be more performant than using lists.
mylist += [2*math.pi*radius*math.cos(phi[i])]
you can use list concatenation, but append is twice as fast according to this:
import math
mylist = list()
phi = [1,2,3,4] # lets pretend this is of unknown/varying lengths
i, num, radius = 0, 4, 6
while i < num:
mylist += [(2*math.pi*radius*math.cos(phi[i]))]
i = i + 1

Categories

Resources