I'm trying to write a pairwise multiplier function which takes two arguments, both being lists. pairwise_multiply should return a new list with each of the elements in the two input lists multiplied together in a pairwise fashion. e.g.
result = pairwise_multiply([1, 2], [3, 4])
print(result)
> [3, 8]
This is my current function but I keep getting syntax errors:
def pairwise_multiply([l1], [l2]):
i = 0
while 1 <= len(l1):
lst = int(l1[i] * l2[i])
i = i + 1
return lst
In your code here -
def pairwise_multiply([l1], [l2]):
You don't need square brackets to pass lists as arguments. Replace it with -
def pairwise_multiply(l1, l2):
Another implementation, more pythonic would be to use list comprehension with zip -
[i*j for i, j in zip(l1, l2)]
What zip does is (from official documentation)-
Make an iterator that aggregates elements from each of the iterables.
Returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples
There are some syntax and logic errors in this snippet.
def pairwise_multiply([l1], [l2]) As #FHTMitchell pointed out, you should cannot not use [...] when naming the arguments. This should be def pairwise_multiply(l1, l2)
while 1 <= len(l1) you mean i, not 1, right? Otherwise you will have an infinite loop. Also, since Python uses zero-based indexing, <= should become <.
You overwrite lst in every iteration. Your function will only return (if at all, see previous point) the result of the last multiplication.
Taking these into account, your code can be transformed to
def pairwise_multiply(l1, l2):
lst = []
i = 0
while i < len(l1):
lst.append(int(l1[i] * l2[i]))
i = i + 1
return lst
But it has many points of failure (for example, what if l1 and l2 are not the same length?), too long and not pythonic.
We can use zip and list comprehension like #ThatBird suggested in their answer.
Related
How can I rewrite this loop using lambda and list-comprehension?
n = 17
e = 0
for i in range(0, n):
e += 1 / factorial(i)
print(e)
So far I have tried this...but it's not working
lst = [item for item in map(lambda e: e + 1/factorial(i), range(0,n))])
myMap = map(lambda e: e + 1/factorial(i), range(0,n))
Since list comprehension and lambda do not fit well in this case, here some functional programming base approaches to, hope, make clear the difference (between a comprehension expression):
A reduce approach: a list is contracted to a "number" via a functional
from math import factorial
from functools import reduce
n = 10
reduce(lambda i, j: i + 1/factorial(j), range(n), 0)
#2.7182815255731922
here with map: consecutive 1-to-1 correspondences in which a function is applied termwise. Finally, the sum functional contracts the list into a "number".
Note: it should be read from right to left, apply the factorial, then the reciprocal and sum it.
sum(map(int(-1).__rpow__, map(factorial, range(n))))
#2.7182815255731922
I would use the sum function for this. I also don't understand why you are using the map function together with a list comprehension.
e = sum([1 / factorial(i) for i in range(n)])
As you can see, list comprehensions can also be used to modify the returned list. You don't need to do complex and not working things using map and lambda.
To be complete, the map function returns the sum of all values in a list. For example:
>>> l = [1, 2, 3, 4, 5, 6]
>>>
>>> sum(l)
21
When your n value gets larger and you need to start paying attention at efficiency, you can use the following code:
sum(1 / factorial(i) for i in range(n))
I removed the square brackets here. This means that python creates a generator instance instaed of a list instance. A generator does not have all its values precalculated, instead it calculates them only when they are needed. This is way more efficient then a list, which first calculates all the values and then iterates over them.
def total_sales(sales_table, product):
if product in sales_table[0]:
return sum(list(zip(*sales_table[1:]))[sales_table[0].index(product)])
return 'Product not found'
Hi could someone explain 1. what the * is doing after the zip() and 2. why there is no comma or break between the two elements in the sum()
many thanks
At first there the * is for unpacking the list next to it, you can check the python syntax for the * and learn how it's used, and the other question u had, There are no comma in sum() because of it have a list() inside it, And sum() takes a list at sums it's up and return the sum of it.
To iterate through nested lists, I usually use For loops.
Check this site for more info about ways to iterate through lists in python:
https://www.geeksforgeeks.org/iterate-over-a-list-in-python/
To get the index of a variable in a list (after iterating correctly through all your nested lists), use .index(element, start, end). This will return the index of the desired element you are searching for.
For more information about .index()
https://www.programiz.com/python-programming/methods/list/index
The zip() function returns a zip object, which is an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
If the passed iterators have different lengths, the iterator with the least items decides the length of the new iterator.
https://realpython.com/python-zip-function/#:~:text=Python's%20zip()%20function%20is,%2C%20sets%2C%20and%20so%20on.
The sum(iterable, start) function returns a number, the sum of all items in an iterable.
https://www.programiz.com/python-programming/methods/built-in/sum
Example:
numbers = [2.5, 3, 4, -5]
# start parameter is not provided
numbers_sum = sum(numbers)
print(numbers_sum)
# start = 10
numbers_sum = sum(numbers, 10)
print(numbers_sum)
# 4.5
# 14.5
The * can also be used for unpacking the containers. The easiest example is that we have data in the form of a list, tuple or dict, and a function take variable arguments:
from functools import reduce
primes = [2, 3, 5, 7, 11, 13]
def product(*numbers):
p = reduce(lambda x, y: x * y, numbers)
return p
product(*primes)
# 30030
product(primes)
# [2, 3, 5, 7, 11, 13]
Because the product() take the variable arguments, we need to unpack the our list data and pass it to that function. In this case, if we pass the primes as *primes, every elements of the primes list will be unpacked, then stored in list called numbers. If pass that list primes to the function without unpacking, the numbers will has only one primes list not all elements of primes.
I want to build a recursive function that finds all the subsets of size 'k' of a given list with length n>=k>=0 and returns a list of those subsets.
example:
if the input list is [1,2,3,4] and k = 2 then the function will return
[[4,3],[2,4],[2,3],[1,4],[1,3],[1,2]]
notice that different arrangments of list is considered to be the same list.
I think that this kind of recursion should work:
return [lst[0]] + choose_sets(lst[1:],k-1) ¬¬and¬¬ choose_sets(lst[1:],k)
where choose_sets(lst,k) is the function.
Meaning:
input : [1,2,3,4] , k=3
calls:
[1] + [2,3,4],k=2 and [2,3,4], k=3
and so on...
can anyone guide me as to how I should call those 2 recursive calls 'at the same time' ?
and what should my 'exiting term' be?
Thanks.
Suppose you have a list of size n and you need all subsets of size k.
This is basically the same as:
For each element of the list,
create a new list without the element,
in the new list, find all the subsets of size k-1 (this is the recursive call),
and add the remove element to all the lists.
Now... this solution will have repetitions, for example, in the example you gave, you'll get both [4, 1] and [1, 4]. But it can be changed a little so that it will not create duplicate results.
edit
to handle duplications
def choose_sets(l, k):
if k == 0:
return [[]]
if len(l) == 0:
return []
l2 = l[1:]
subsets = choose_sets(l2, k-1)
for s in subsets:
s.append(l[0])
return subsets+ choose_sets(l2, k)
b = []
def abc(a,k):
if len(a)==k:
b.append(a)
return b
b.extend([a[:k-1]+[i] for i in a[k-1:]])
return abc(a[1:],k)
print abc([1,2,3,4,5],2)
Context - developing algorithm to determine loop flows in a power flow network.
Issue:
I have a list of lists, each list represents a loop within the network determined via my algorithm. Unfortunately, the algorithm will also pick up the reversed duplicates.
i.e.
L1 = [a, b, c, -d, -a]
L2 = [a, d, c, -b, -a]
(Please note that c should not be negative, it is correct as written due to the structure of the network and defined flows)
Now these two loops are equivalent, simply following the reverse structure throughout the network.
I wish to retain L1, whilst discarding L2 from the list of lists.
Thus if I have a list of 6 loops, of which 3 are reversed duplicates I wish to retain all three.
Additionally, The loop does not have to follow the format specified above. It can be shorter, longer, and the sign structure (e.g. pos pos pos neg neg) will not occur in all instances.
I have been attempting to sort this by reversing the list and comparing the absolute values.
I am completely stumped and any assistance would be appreciated.
Based upon some of the code provided by mgibson I was able to create the following.
def Check_Dup(Loops):
Act = []
while Loops:
L = Loops.pop()
Act.append(L)
Loops = Popper(Loops, L)
return Act
def Popper(Loops, L):
for loop in Loops:
Rev = loop[::-1]
if all (abs(x) == abs(y) for x, y in zip(loop_check, Rev)):
Loops.remove(loop)
return Loops
This code should run until there are no loops left discarding the duplicates each time. I'm accepting mgibsons answers as it provided the necessary keys to create the solution
I'm not sure I get your question, but reversing a list is easy:
a = [1,2]
a_rev = a[::-1] #new list -- if you just want an iterator, reversed(a) also works.
To compare the absolute values of a and a_rev:
all( abs(x) == abs(y) for x,y in zip(a,a_rev) )
which can be simplified to:
all( abs(x) == abs(y) for x,y in zip(a,reversed(a)) )
Now, in order to make this as efficient as possible, I would first sort the arrays based on the absolute value:
your_list_of_lists.sort(key = lambda x : map(abs,x) )
Now you know that if two lists are going to be equal, they have to be adjacent in the list and you can just pull that out using enumerate:
def cmp_list(x,y):
return True if x == y else all( abs(a) == abs(b) for a,b in zip(a,b) )
duplicate_idx = [ idx for idx,val in enumerate(your_list_of_lists[1:])
if cmp_list(val,your_list_of_lists[idx]) ]
#now remove duplicates:
for idx in reversed(duplicate_idx):
_ = your_list_of_lists.pop(idx)
If your (sub) lists are either strictly increasing or strictly decreasing, this becomes MUCH simpler.
lists = list(set( tuple(sorted(x)) for x in your_list_of_lists ) )
I don't see how they can be equivalent if you have c in both directions - one of them must be -c
>>> a,b,c,d = range(1,5)
>>> L1 = [a, b, c, -d, -a]
>>> L2 = [a, d, -c, -b, -a]
>>> L1 == [-x for x in reversed(L2)]
True
now you can write a function to collapse those two loops into a single value
>>> def normalise(loop):
... return min(loop, [-x for x in reversed(L2)])
...
>>> normalise(L1)
[1, 2, 3, -4, -1]
>>> normalise(L2)
[1, 2, 3, -4, -1]
A good way to eliminate duplicates is to use a set, we just need to convert the lists to tuples
>>> L=[L1, L2]
>>> set(tuple(normalise(loop)) for loop in L)
set([(1, 2, 3, -4, -1)])
[pair[0] for pair in frozenset(sorted( (c,negReversed(c)) ) for c in cycles)]
Where:
def negReversed(list):
return tuple(-x for x in list[::-1])
and where cycles must be tuples.
This takes each cycle, computes its duplicate, and sorts them (putting them in a pair that are canonically equivalent). The set frozenset(...) uniquifies any duplicates. Then you extract the canonical element (in this case I arbitrarily chose it to be pair[0]).
Keep in mind that your algorithm might be returning cycles starting in arbitrary places. If this is the case (i.e. your algorithm might return either [1,2,-3] or [-3,1,2]), then you need to consider these as equivalent necklaces
There are many ways to canonicalize necklaces. The above way is less efficient because we don't care about canonicalizing the necklace directly: we just treat the entire equivalence class as the canonical element, by turning each cycle (a,b,c,d,e) into {(a,b,c,d,e), (e,a,b,c,d), (d,e,a,b,c), (c,d,e,a,b), (b,c,d,e,a)}. In your case since you consider negatives to be equivalent, you would turn each cycle into {(a,b,c,d,e), (e,a,b,c,d), (d,e,a,b,c), (c,d,e,a,b), (b,c,d,e,a), (-a,-b,-c,-d,-e), (-e,-a,-b,-c,-d), (-d,-e,-a,-b,-c), (-c,-d,-e,-a,-b), (-b,-c,-d,-e,-a)}. Make sure to use frozenset for performance, as set is not hashable:
eqClass.pop() for eqClass in {frozenset(eqClass(c)) for c in cycles}
where:
def eqClass(cycle):
for rotation in rotations(cycle):
yield rotation
yield (-x for x in rotation)
where rotation is something like Efficient way to shift a list in python but yields a tuple
A multidimensional list like l=[[1,2],[3,4]] could be converted to a 1D one by doing sum(l,[]). How does this happen?
(This doesn't work directly for higher multidimensional lists, but it can be repeated to handle those cases. For example if A is a 3D-list, then sum(sum(A),[]),[]) will flatten A to a 1D list.)
If your list nested is, as you say, "2D" (meaning that you only want to go one level down, and all 1-level-down items of nested are lists), a simple list comprehension:
flat = [x for sublist in nested for x in sublist]
is the approach I'd recommend -- much more efficient than summing would be (sum is intended for numbers -- it was just too much of a bother to somehow make it block all attempts to "sum" non-numbers... I was the original proposer and first implementer of sum in the Python standard library, so I guess I should know;-).
If you want to go down "as deep as it takes" (for deeply nested lists), recursion is the simplest way, although by eliminating the recursion you can get higher performance (at the price of higher complication).
This recipe suggests a recursive solution, a recursion elimination, and other approaches
(all instructive, though none as simple as the one-liner I suggested earlier in this answer).
sum adds a sequence together using the + operator. e.g sum([1,2,3]) == 6. The 2nd parameter is an optional start value which defaults to 0. e.g. sum([1,2,3], 10) == 16.
In your example it does [] + [1,2] + [3,4] where + on 2 lists concatenates them together. Therefore the result is [1,2,3,4]
The empty list is required as the 2nd paramter to sum because, as mentioned above, the default is for sum to add to 0 (i.e. 0 + [1,2] + [3,4]) which would result in unsupported operand type(s) for +: 'int' and 'list'
This is the relevant section of the help for sum:
sum(sequence[, start]) -> value
Returns the sum of a sequence of
numbers (NOT strings) plus the value
of parameter 'start' (which defaults
to 0).
Note
As wallacoloo comented this is not a general solution for flattening any multi dimensional list. It just works for a list of 1D lists due to the behavior described above.
Update
For a way to flatten 1 level of nesting see this recipe from the itertools page:
def flatten(listOfLists):
"Flatten one level of nesting"
return chain.from_iterable(listOfLists)
To flatten more deeply nested lists (including irregularly nested lists) see the accepted answer to this question (there are also some other questions linked to from that question itself.)
Note that the recipe returns an itertools.chain object (which is iterable) and the other question's answer returns a generator object so you need to wrap either of these in a call to list if you want the full list rather than iterating over it. e.g. list(flatten(my_list_of_lists)).
For any kind of multidiamentional array, this code will do flattening to one dimension :
def flatten(l):
try:
return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]
except IndexError:
return []
It looks to me more like you're looking for a final answer of:
[3, 7]
For that you're best off with a list comprehension
>>> l=[[1,2],[3,4]]
>>> [x+y for x,y in l]
[3, 7]
I wrote a program to do multi-dimensional flattening using recursion. If anyone has comments on making the program better, you can always see me smiling:
def flatten(l):
lf=[]
li=[]
ll=[]
p=0
for i in l:
if type(i).__name__=='list':
li.append(i)
else:
lf.append(i)
ll=[x for i in li for x in i]
lf.extend(ll)
for i in lf:
if type(i).__name__ =='list':
#not completely flattened
flatten(lf)
else:
p=p+1
continue
if p==len(lf):
print(lf)
I've written this function:
def make_array_single_dimension(l):
l2 = []
for x in l:
if type(x).__name__ == "list":
l2 += make_array_single_dimension(x)
else:
l2.append(x)
return l2
It works as well!
The + operator concatenates lists and the starting value is [] an empty list.