How to make all combinations of many functions in python? - python

So i have
x = 3
def first(x):
return x+2
def second(x):
return x/2
def third(x):
return x*4
I would like to make a pipe of functions like :
first -> second -> third
but all combinations of functions :
like first -> second , first -> third
and get each time the value of x for each combination.
And i don't need not only to multiply them but to be able to make multiple combination of various length.
Here it's just fixed number of combination :
How to multiply functions in python?
regards and thanks

First the combinations part:
>>> functions = [first, second, third]
>>> from itertools import combinations, permutations
>>> for n in range(len(functions)):
... for comb in combinations(functions, n + 1):
... for perm in permutations(comb, len(comb)):
... print('_'.join(f.__name__ for f in perm))
...
first
second
third
first_second
second_first
first_third
third_first
second_third
third_second
first_second_third
first_third_second
second_first_third
second_third_first
third_first_second
third_second_first
Next the composing part, steal the #Composable decorator from the question How to multiply functions in python? and use it to compose functions from each permutation.
from operator import mul
from functools import reduce
for n in range(len(functions)):
for comb in combinations(functions, n + 1):
for perm in permutations(comb, len(comb)):
func_name = '_'.join(f.__name__ for f in perm)
func = reduce(mul, [Composable(f) for f in perm])
d[func_name] = func
Now you have a namespace of functions (actually callable classes), demo:
>>> f = d['third_first_second']
>>> f(123)
254.0
>>> third(first(second(123)))
254.0
>>> ((123 / 2) + 2) * 4
254.0

Related

Using sympy to sum over a range of symbols

Consider two sets i,j which both have m elements. Say we have an expression which describes a sum of terms. Each term can be described as a product of an element of i and j. Now, I would like to sum over each element of j, where each element has the range [i1,i2,...,im].
In the context of python & sympy, this is difficult since sympy's Sum describes the summation variable with (symbol,start,stop), which assume integer steps.
To demonstrate what I mean, consider the following code:
>>> from sympy import *
>>> i = symbols('i1,i2,i3,i4') # for the case m = 4
>>> j = symbols('j1,j2,j3,j4')
Here I use permutations to setup the expression:
>>> from itertools import permutations as perm
>>> c = list(perm(range(4),2))
>>> a,b = c[0]
>>> expr = i[a]*j[b]
>>> for a,b in c[1:]:
>>> expr += i[a]*j[b]
>>> print(expr)
i1*j2 + i1*j3 + i1*j4 + i2*j1 + i2*j3 + i2*j4 + i3*j1 + i3*j2 + i3*j4 + i4*j1 + i4*j2 + i4*j3
Now, using Sum over each j with range of i. It would be ideal if I could write one of the following:
>>> s = Sum(expr,(j,i))
>>> s = Sum(expr,(j1,i),(j2,i),...,(jm,i))
But that's not canonical with the sympy documentation. Are there any other methods which can be used to solve this problem?
Edit:
In this post, I tried to isolate the problem by only using elements i,j in expr. The full context problem is where expr is a sum of Kronecker Delta functions of i,j and using a sum over index set j, where each element of j has range i. For example:
>>> from sympy import KroneckerDelta as KD
>>> expr = KD(i[0],j[1]) # Only doing j[1] to reduce clutter
>>> print(expr)
KroneckerDelta(i1,j2)
>>> s = Sum(expr,(j[1],i)).doit()
>>> print(s)
# Desired output to look like:
1 + KroneckerDelta(i1,i2) + KroneckerDelta(i1,i3) + KroneckerDelta(i1,i4)
This is the reason for which I phrased my question as: summing over each element of j with range i.
IndexedBase can act as a symbolic, integer-indexed array:
>>> from sympy import *
>>> from sympy.abc import k,l
>>> i,j = map(IndexedBase,'ij')
>>> Sum(i[k]*j[l],(k,1,2),(l,1,2)).doit().expand()
i[1]*j[1] + i[1]*j[2] + i[2]*j[1] + i[2]*j[2]
>>> Sum(Piecewise((i[k]*j[l],Ne(k,l)),(0,True)),(k,1,2),(l,1,2)).doit()
i[1]*j[2] + i[2]*j[1]
It's not clear whether you want the cross terms when the indicices are the same, so both versions are shown.

Multipy mutli parameters in Python

I am a begineer of python.
I want multipy multi arguements like this:
def multiply(*args):
for nums in args
return (args*args)
print(multiply(3, 4, 5))
60
EDIT:
Actualy i want to make one again for substraction
python
minus(3, 4, 7)
6
def multiply(*args):
n = 1
for nums in args:
n *= nums
return n
print(multiply(3, 4, 5))
60
You can do this in a single line using functools.reduce and operator.mul, but we'll leave that for a later lesson.
You can use functools.reduce
from functools import reduce
def multiply(*args):
return reduce(lambda a, b: a*b, args, 1)
edit:
It turns out there is something even simpler starting in python 3.8:
from math import prod
def multiply(*args):
return prod(args)
There you go
def multiply(*args):
x = 1
for e in args:
x = x*e
return x

Evaluating a function over a list in Python - without using loops

There is a problem in Python which involves the evaluation of a function over a list of numbers which are provided as inputs to the following function:
f(y) = sin(3y + pi/3) + cos(4y - pi/7)
I don't think MathJax tools are available on StackOverflow so the above is the best I can do.
There are four outputs to the function: An array or list containing the values obtained by the function for each element of the input list, the minimum and maximum values in the output array / list, and an array or list of the differences between successive values obtained by the function.
Here is the code so far. We assume that only sensible inputs are passed to the function.
import sympy
def minMaxDiffValues(lst):
y = sympy.symbols('y')
f = sympy.sin(3*y + sympy.pi/3) + sympy.cos(4*y - sympy.pi/7)
values = []
for n in lst:
values.append(f.subs(y,n))
differences = []
for i in range(len(values) - 1):
differences.append(values[i + 1] - values[i])
print values
print min(values)
print max(values)
print differences
As far as I know, the above code gets the job done; I've opted to work with lists, even though I am familiar with numpy. I'll replace the print statements with a single return statement; for now I'm printing the outputs to make sure that they are correct.
The only issue is that the problem prevents the use of loops; thus I am uncertain as to how to approach such a problem for the first and last function outputs.
Is it possible to write the above function without using any loops?
You could use list comprehensions:
import sympy
def minMaxDiffValues(lst):
y = sympy.symbols('y')
f = sympy.sin(3*y + sympy.pi/3) + sympy.cos(4*y - sympy.pi/7)
values = [f.subs(y,n) for n in lst]
differences = [values[i+1] - values[i] for i in range(len(values)-1)]
print(values)
print(min(values))
print(max(values))
print(differences)
If you wanted to, you could also use the pairwise recipe from the itertools module docs:
import itertools
import sympy
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)
def minMaxDiffValues(lst):
y = sympy.symbols('y')
f = sympy.sin(3*y + sympy.pi/3) + sympy.cos(4*y - sympy.pi/7)
values = [f.subs(y,n) for n in lst]
differences = [y - x for (x, y) in pairwise(values)]
print(values)
print(min(values))
print(max(values))
print(differences)
Using map is a way to apply a function to a list of values in a compact fashion:
>>> from sympy import y, pi
>>> f = lambda y: sin(3*y + pi/3) + cos(4*y - pi/7)
>>> vals = list(map(f, lst))
>>> d = lambda i: vals[i] - vals[i-1]
>>> difs = list(map(d, range(1, len(vals))))
And there is no visible 'for'. But as #hpaulj notes, there's one under the hood somewhere.

Python3 reduce() function alternative using loop instead of using from functools import reduce

I have a python2 code that I used before but I would like replace reduce by a loop. How can I rewrite this part prod = reduce(lambda a, b: a * b, n) function below?
def chineeseRemainder(n, a):
sum = 0
prod = reduce(lambda a, b: a * b, n)
for n_i, a_i in zip(n, a):
p = prod // n_i
sum += a_i * Get_Multi_Inv(p, n_i) * p
return sum % prod
In general for a reduce() without a starting value, you can use iter and next to convert it to a for loop. This combination lets you use the first element of the iterable as the starting accumulator value, and loop over the rest.
iterator = iter(n)
prod = next(iterator)
for x in iterator:
prod *= x
But in the specific case of multiplication, we know the identity element is 1, so we can use that as the starting value and multiply the whole iterable.
prod = 1
for x in n:
prod *= x
Not all functions will have an identity like this, but many do, like 0 for + and math.inf for min(), etc.
you can also add import
from functools import reduce
I have it working.
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
value = next(it)
else:
value = initializer
for element in it:
value = function(value, element)
return value
and I called it the function above with
prod = reduce(function1, n)

Return at least X results from split

split has a maxsplit parameter, which is useful when you want at most X results. If there something similar to return at least X results and populate the rest with Nones. I'd like to be able to write
a, b, c = 'foo,bar'.magic_split(',', 3)
and have a=foo, b=bar and c=None.
Any ideas how to write such a function?
Upd. I ended up with a solution which is a combination of this and this answers:
>>> def just(n, iterable, fill=None):
... return (list(iterable) + [fill] * n)[:n]
...
>>> just(3, 'foo,bar'.split(','))
['foo', 'bar', None]
One way would be:
from itertools import chain
from itertools import repeat
from itertools import islice
def magic_split(seq, sep, n, def_value=None):
return list(islice(chain(seq.split(sep), repeat(def_value)), n))
You could just return the return value of islice if you don't need the list.
If you don't want the values to be cut off when n is less than number of split elements in seq, the modification is trivial:
def magic_split(seq, sep, n, def_value=None):
elems = seq.split(sep)
if len(elems) >= n:
return elems
return list(islice(chain(elems, repeat(def_value)), n))
There is no such parameter to str.split(). A hack to achieve this would be
a, b, c = ('foo,bar'.split(',', 2) + [None] * 3)[:3]
Not sure if I recommend this code, though.
I would use a more general function for that:
def fill(iterable, n):
tmp = tuple(iterable)
return tmp + (None,)*(n - len(tmp))
Then:
a, b, c = fill('foo,bar'.split(','), 3)
Since you ask for a string method, you can start by deriving from str:
>>> class magicstr(str):
def magic_split(self, sep=None, mlen=0):
parts = self.split(sep)
return parts + [None]* (mlen - len(parts))
>>> test = magicstr("hello there, ok?")
>>> test.magic_split(",", 3)
['hello there', ' ok?', None]

Categories

Resources