I would like to know if there is a built in function in python for the equivalent Haskell scanl, as reduce is the equivalent of foldl.
Something that does this:
Prelude> scanl (+) 0 [1 ..10]
[0,1,3,6,10,15,21,28,36,45,55]
The question is not about how to implement it, I already have 2 implementations, shown below (however, if you have a more elegant one please feel free to show it here).
First implementation:
# Inefficient, uses reduce multiple times
def scanl(f, base, l):
ls = [l[0:i] for i in range(1, len(l) + 1)]
return [base] + [reduce(f, x, base) for x in ls]
print scanl(operator.add, 0, range(1, 11))
Gives:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
Second implementation:
# Efficient, using an accumulator
def scanl2(f, base, l):
res = [base]
acc = base
for x in l:
acc = f(acc, x)
res += [acc]
return res
print scanl2(operator.add, 0, range(1, 11))
Gives:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
Thank you :)
You can use this, if its more elegant:
def scanl(f, base, l):
for x in l:
base = f(base, x)
yield base
Use it like:
import operator
list(scanl(operator.add, 0, range(1,11)))
Python 3.x has itertools.accumulate(iterable, func= operator.add). It is implemented as below. The implementation might give you ideas:
def accumulate(iterable, func=operator.add):
'Return running totals'
# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
# accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
it = iter(iterable)
total = next(it)
yield total
for element in it:
total = func(total, element)
yield total
Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), which gives the possibility to name the result of an expression, we can use a list comprehension to replicate a scan left operation:
acc = 0
scanned = [acc := acc + x for x in [1, 2, 3, 4, 5]]
# scanned = [1, 3, 6, 10, 15]
Or in a generic way, given a list, a reducing function and an initialized accumulator:
items = [1, 2, 3, 4, 5]
f = lambda acc, x: acc + x
accumulator = 0
we can scan items from the left and reduce them with f:
scanned = [accumulator := f(accumulator, x) for x in items]
# scanned = [1, 3, 6, 10, 15]
I had a similar need. This version uses the python list comprehension
def scanl(data):
'''
returns list of successive reduced values from the list (see haskell foldl)
'''
return [0] + [sum(data[:(k+1)]) for (k,v) in enumerate(data)]
>>> scanl(range(1,11))
gives:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
As usual, the Python ecosystem is also overflowing with solutions:
Toolz has an accumulate capable of taking a user-supplied function as an argument. I tested it with lambda expressions.
https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py
https://pypi.python.org/pypi/toolz
as does more_itertools
http://more-itertools.readthedocs.io/en/stable/api.html
I did not test the version from more-itertools, but it also can take a user-supplied function.
Related
Why does this code print 2?
I'm having a hard time wrapping my head around the order of the calls and what happens when the code runs. I'd like to know what's going on here, thanks!
The code:
mia = lambda dan: lambda john: list(john)[dan]
john = lambda mia: lambda dan: filter(dan, map(lambda x: x % 2 + 1, mia))
ta = mia(-1)(john([3, 6, 9, 12, 15])(lambda f: f % 3))
print(ta)
This is some kind of obfuscated way to perform a simple task:
check if numbers in a list are odd or even and map 1, 2 accordingly
filter the previous output if this is not a multiple of 3 (which is always True)
take the last item of the previous output
In summary, this output 2 if the last number of the input is odd and 1 otherwise.
This could be simplified to:
list(map(lambda x: x % 2 + 1, [3, 6, 9, 12, 15]))[-1]
or, keeping the useless filter:
list(filter(lambda f: f % 3, map(lambda x: x % 2 + 1, [3, 6, 9, 12, 15])))[-1]
This is using a functional approach in which the functions return functions instead of values. In addition the local variables have names designed to be confusing (mia in john has nothing to do with the mia function)
Interestingly, mia is equivalent to operator.itemgetter
It does a lot less than it looks like.
To simplify, turn the lambda expressions into def statements.
def mia(dan):
def inner(john):
lst = list(john)
return lst[dan]
return inner
def john(mia):
def inner(dan):
mp = map(lambda x: x % 2 + 1, mia)
return filter(dan, mp)
return inner
To simplify further, separate your function calls.
# just return the inner function
john_inner_func = john([3, 6, 9, 12, 15])
# first map element % 2 + 1 to each element of the given array
# this results in [2, 1, 2, 1, 2]
# next it filters all truthy (non-zero) values of the result of element % 3.
# Since all are positive and less than 3 the result is the same
# [2, 1, 2, 1, 2]
john_filter_result = john_inner_func(lambda f: f % 3)
# just return the inner function
mia_inner_func = mia(-1)
# return the -1 index of the filter result as a list
# this gives the last element, or 2
ta = mia_inner_func(john_filter_result)
print(ta)
I am trying to figure out how to iterate over fn to modify the list again with only even numbers, AFTER appending. The desire result is
[2,4,6,8,10,12,14,16,18,20]
is this possible? I do not want to add fn + sn or lst + lst2. I want to continue over this specific function.
lst = [1,2,3,4,5,6,7,8,9,10]
lst2 =[11,12,13,14,15,16,17,18,19,20]
def even(fn,sn):
for i in sn:
if i %2 ==0:
fn.append(i)
even(lst,lst2)
print(lst) # output [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20]
I know there are more efficient and pythonic ways like this example:
example 1:
def even2(fn,sn):
fn[:] = [x for x in fn + sn if x % 2 == 0]
example 2:
def even(fn, sn):
fn.extend(sn)
fn[:] = [x for x in fn if x % 2 == 0]
def even(fn,sn):
for i in sn:
if i %2 ==0:
fn.append(i)
fn = list(filter(lambda x: x %2 == 0, fn))
the last code block is what i tried and failed with
You don't need to rebuild the entire list; just extend it with the desired extra elements.
>>> lst = [1,2,3,4,5,6,7,8,9,10]
>>> lst2 =[11,12,13,14,15,16,17,18,19,20]
>>> def even(fn, sn):
... fn.extend(i for i in sn if i % 2 == 0)
...
>>> even(lst, lst2)
>>> lst
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20]
In the above expression i for i... is a generator expression that allows extend to iterate over all of the i elements without storing them in a separate list first.
You could do it in two steps:
remove odd numbers from fn
extend fn with the even numbers in sn
This function does it without creating any temporary copies or sublists:
lst = [1,2,3,4,5,6,7,8,9,10]
lst2 =[11,12,13,14,15,16,17,18,19,20]
def even(fn,sn):
for i,n in enumerate(reversed(fn),1-len(fn)): # delete odds backwards
if n%2: del fn[-i] # so indexes stay stable
fn.extend(n for n in sn if n%2==0) # extend with sn's evens
even(lst,lst2)
print(lst)
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Note that you could extend fn first and then remove the odd numbers, but that would be inefficient:
def even(fn,sn):
fn.extend(sn) # add all sn values
for i,n in enumerate(reversed(fn),1-len(fn)): # delete odds backwards
if n%2: del fn[-i] # so indexes stay stable
A more efficient way would be to iterate through the two lists and assign the result to fn[:]. Doing this starting with fn will ensure that there is no conflict between the iterators and the assignment process:
def even(fn,sn):
fn[:] = (n for L in (fn,sn) for n in L if n%2==0)
The nested comprehension loops allow iterating through the two lists without actually concatenating them so there is no temporary copies or sublists in that solution either.
Does this meet your requirements? Pulling lst into the even() func as a global.
lst = [1,2,3,4,5,6,7,8,9,10]
lst2 =[11,12,13,14,15,16,17,18,19,20]
def even(fn, sn):
global lst
lst = [x for x in (fn + sn) if x % 2 == 0]
even(lst,lst2)
print(lst)
#!/usr/bin/python2
s=5 # input any int
l1=[5,8,2,1,17] # input some int numbers
l2=[] # will be out
for i in l1:
s+=i
l2.append(s)
print l1,l2
[5, 8, 2, 1, 17]
[10, 18, 20, 21, 38]
I would like to replace the above for cycle with listcomprehension.
I generally use list comprehensions but I don't know how could I do with the above for cycle.
For a list comprehension without using libraries or the overhead of sum(), you could use an internal list to store the accumulator.
l1 = [5,8,2,1,17]
s = 5
R = [a.append(a.pop(0)+n) or a[0] for a in [[s]] for n in l1]
print(R)
[10, 18, 20, 21, 38]
If you're using Python 3.8+ you can use the walrus operator to manage the accumulation:
R = [a := n + (a if i else s) for i,n in enumerate(l1)]
Here you can see use of sum(). However I would not prefer it if the list is too long for the repetitive calculations by sum()
>>> s=5
>>>
>>> l1=[5,8,2,1,17]
>>> [s+sum(l1[:i+1]) for i,x in enumerate(l1)]
[10, 18, 20, 21, 38]
>>>
List comprehensions are much better suited to situations where you generate an output item independently for each input item. Here, having to sum the items makes it rather inconvenient.
You could use itertools.accumulate, though, which is designed to do that by default:
from itertools import accumulate
l1 = [5, 8, 2, 1, 17]
s = 5
l2 = list(accumulate(l1, initial=s))[1:]
print(l2)
# [10, 18, 20, 21, 38]
(Note that we have to exclude the first value, which would be the initial s, as you don't want it in your output.)
You have the list-comprehension option
res = [s + sum(l1[:i+1]) for i,x in enumerate(l1)]
Or using some libs along with itertools.accumulate
from operator import add
from itertools import accumulate
import numpy as np
res = np.array(list(accumulate(l1, add))) + s
I've designed a simple function that looks at an inputted list of numbers, identifies the minimum and maximum values, then substitutes both of them for the midpoint value between them, the function is here:
def mainfunction(list_of_numbers):
smallest_number = list_of_numbers[0]
for a in list_of_numbers:
if a < smallest_number:
smallest_number = a
largest_number = list_of_numbers[0]
for b in list_of_numbers:
if b > largest_number:
largest_number = b
midpoint = (smallest_number + largest_number)/2
final_list = [x if (x != largest_number and x != smallest_number) else midpoint for x in list_of_numbers]
return final_list
print(mainfunction([10, 7, 14, 3, -200, 8, 1, -12, 250]))
Unfortunately, I can't get the function to work on TABLES of numbers, is there an easy way to convert a table of numbers into a list? Any info would be appreciated, cheers!
You can use itertools.chain
from itertools import chain
a = [[3,4], [15,16], [19,20]]
res = list(chain.from_iterable(a))
print(res)
Output:
[3, 4, 15, 16, 19, 20]
with list comprehension
res = [x for lst in a for x in lst]
I want to multiply an element of a list with all other elements.
For example:
def product(a,b,c):
return (a*b, a*c, a*b*c)
I have done this
def product(*args):
list = []
for index,element in enumerate(args):
for i in args:
if (args[index]*i) not in list:
list.append(args[index]*i)
return list
but this gives me [a*a, a*b,a*c, b*b] etc. I don't want the a*a, b*b, c*c bit in there.
you could check for equality
if (args[index]*i) not in list and args[index] != i:
itertools is your friend here:
from itertools import combinations
from functools import reduce, partial
from operator import mul
# Make a sum-like function for multiplication; I'd call it product,
# but that overlaps a name in itertools and our own function
multiplyall = partial(reduce, mul)
def product(*args):
# Loop so you get all two elements combinations, then all three element, etc.
for n in range(2, len(args) + 1):
# Get the combinations for the current combo count
for comb in combinations(args, n):
# Compute product and yield it
# yielding comb as well just for illustration
yield comb, multiplyall(comb)
I made it a generator function, because frankly, almost any function that's just slowly building a list element by element and returning it should really be a generator function (if the caller wants a list, they just do mylist = list(generatorfunc(...))), making it easier to use iteratively without blowing main memory when many arguments are passed.
Example usage:
>>> for pieces, prod in product(2, 3, 4):
print ' * '.join(map(str, pieces)), '=', prod
Which outputs:
2 * 3 = 6
2 * 4 = 8
3 * 4 = 12
2 * 3 * 4 = 24
So if the values are 2, 3, 4, 5 you want all and only these products:
2*3=6, 2*4=8, 2*5=10, 2*3*4=24, 2*3*5=30, 2*4*5=40, 2*3*4*5=120
This means taking all combinations of 3, 4, 5 and then multiplying them togther with 2. The itertools module has a combinations function, and reduce can be used in conjunction with operator.mul to do the calculation:
def product(first, *other):
for n in range(1, len(other) + 1):
for m in combinations(other, n):
yield reduce(mul, m, first)
list(product(2, 3, 4, 5))
Output:
[6, 8, 10, 24, 30, 40, 120]
Does your list have duplicate elements, like [2, 3, 4, 2]?
If it does not, here is a one liner:
First, with tags to illustrate the pattern:
a = ['a1','a2','a3']
lsta = [[x+y for y in [z for z in a if z != x]] for x in a]
lsta
[['a1a2', 'a1a3'], ['a2a1', 'a2a3'], ['a3a1', 'a3a2']]
And here, with numbers:
a =[2,3,4,5]
print [[x*y for y in [z for z in a if z != x]] for x in a]
[[6, 8, 10], [6, 12, 15], [8, 12, 20], [10, 15, 20]]
or the sum of the products, if you wish:
a =[2,3,4,5]
print [sum([x*y for y in [z for z in a if z != x]]) for x in a]
[24, 33, 40, 45]
If the list has duplicates, it gets more complicated. Do you want the first occurrence and the second occurrence of 2 in [2,3,4,2] to be separately calculated (you might need that for some purposes even though you will get the same value for both)?