multiple values of a variable in same equation - python

A = [18.0,10.0]; B = [13.0,15.0]; C = [10.5,12.0];
these are the variables and think about function like
def hlf(A,B,C):
return A**(-1.0/2.0)-0.2*B-43+C
print "T:"
hlf(A,B,C)
Firstly, I want to use first values of the A B and C in the equation. After I want to use second values. How can I do this ?

map + list
Note map can take multiple iterable arguments:
res = map(hlf, A, B, C)
[-34.86429773960448, -33.68377223398316]
In Python 2.7, map returns a list. In Python 3.x map returns an iterator, so you can either iterate lazily or exhaust via list, i.e. list(map(hfl, A, B, C)).
Reference:
map(function, iterable, ...)
...If additional iterable arguments are passed, function must
take that many arguments and is applied to the items from all
iterables in parallel.
zip + list comprehension
You can use zip within a list comprehension. For clarity, you should avoid naming your arguments the same as your variables.
A = [18.0,10.0]; B = [13.0,15.0]; C = [10.5,12.0];
def hlf(x, y, z):
return x**(-1.0/2.0) - 0.2*y - 43 + z
res = [hlf(*vars) for vars in zip(A, B, C)]
[-34.86429773960448, -33.68377223398316]

Vectorize with Numpy. Best Performace
Normally its much better try to vectorize this kind of operations with numpy, because the best performance results. When you vectorize instead to use a loop, you are using all your cores, and its the fastest solution. You should vectorize the operation with numpy. Something like this:
import numpy as np
A = [18.0,10.0]; B = [13.0,15.0]; C = [10.5,12.0];
a = np.array(A)
b = np.array(B)
c = np.array(C)
And now your function with the new vectors like arguments:
def hlf(a_vector,b_vector,c_vector):
return a_vector**(-1.0/2.0)-0.2*b_vector-43+c_vector
And finally call your new function vectorized:
print (hlf(a_vector = a,b_vector = b,c_vector = c))
Output:
>>> array([-34.86429774, -33.68377223])

If you want to keep your function as is, you should call it N times with:
for i in range(N):
result = hlf(A[i], B[i], C[i])
print(result)
Another interesting method is to make a generator with your function:
A = [18.0,10.0]
B = [13.0,15.0]
C = [10.5,12.0];
def hlf(*args):
i=0
while i < len(args[0]):
yield args[0][i]**(-1.0/2.0) - 0.2*args[1][i] - 43 + args[2][i]
i += 1
results = hlf(A, B, C)
for r in results:
print(r)
Output:
-34.86429773960448
-33.68377223398316
Last one is rather edicational if you want to practice python generators.

Related

How to test all possible values ​for all variables to get the maximum result for the function

I have three variables called a, b and c, each of these can assume a different value defined in a range. I'd like to create a function that tests every possible variable value and gives me their best combination for the output 'f'.
a = list(range(1, 10, 2))
b = list(range(5, 8, 1))
c = list(range(1, 3, 1))
def all_combinations (a, b, c):
#something
f = a + (b * a) - (c*(a ^ b))
return BEST a, b, c for my f
it's possible to do it ? what is the best way to do it?
You can use itertools.product() to get all the possible combinations of a, b, and c.
Then calculate your formula for each unique combination of a b c, keep track of the result, and if the result is better than the previous best, save the current values of a b c.
import itertools
def all_combinations (alist, blist, clist):
best_a = 0
best_b = 0
best_c = 0
best_f = 0
for a,b,c in itertools.product(alist, blist, clist):
f = a + (b * a) - (c*(a ^ b))
if f > best_f: # use your own definition of "better"
best_a = a
best_b = b
best_c = c
best_f = f
return best_a, best_b, best_c
First of all, you said I have three variables called a, b and c, each of these can assume a different value defined in a range. Note that the variables in your code are actually equal to three lists of integers, not three integers.
The naive algorithm to test all possible combinations is 3 nested for loops. Here I assume that by "best" you mean "maximum value":
def all_combinations (list1, list2, list3):
best_f, best_a, best_b, best_c = None, None, None, None
for a in list1:
for b in list2:
for c in list3:
f = a + (b * a) - (c*(a ^ b))
# here you have to define what f being "better" than best_f means:
if not f or f > best_f:
best_f = f
best_a = a
best_b = b
best_c = c
return best_a, best_b, best_c
If you're sure those are the only values you want to test, then the following will work. Otherwise you might want to look into scipy.optimize.
from itertools import product
import numpy as np
parameters = list(product(a, b, c))
results = [my_fun(*x) for x in parameters]
print(parameters[np.argmax(results)])
obviously replace np.argmax with np.argmin if you want to minimize the function

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.

How to vectorize a class instantiation to allow NumPy arrays as input?

I programmed class which looks something like this:
import numpy as np
class blank():
def __init__(self,a,b,c):
self.a=a
self.b=b
self.c=c
n=5
c=a/b*8
if (a>b):
y=c+a*b
else:
y=c-a*b
p = np.empty([1,1])
k = np.empty([1,1])
l = np.empty([1,1])
p[0]=b
k[0]=b*(c-1)
l[0]=p+k
for i in range(1, n, 1):
p=np.append(p,l[i-1])
k=np.append(k,(p[i]*(c+1)))
l=np.append(l,p[i]+k[i])
komp = np.zeros(shape=(n, 1))
for i in range(0, n):
pl_avg = (p[i] + l[i]) / 2
h=pl_avg*3
komp[i]=pl_avg*h/4
self.tot=komp+l
And when I call it like this:
from ex1 import blank
import numpy as np
res=blank(1,2,3)
print(res.tot)
everything works well.
BUT I want to call it like this:
res = blank(np.array([1,2,3]), np.array([3,4,5]), 3)
Is there an easy way to call it for each i element of this two arrays without editing class code?
You won't be able to instantiate a class with NumPy arrays as inputs without changing the class code. #PabloAlvarez and #NagaKiran already provided alternative: iterate with zip over arrays and instantiate class for each pair of elements. While this is pretty simple solution, it defeats the purpose of using NumPy with its efficient vectorized operations.
Here is how I suggest you to rewrite the code:
from typing import Union
import numpy as np
def total(a: Union[float, np.ndarray],
b: Union[float, np.ndarray],
n: int = 5) -> np.array:
"""Calculates what your self.tot was"""
bc = 8 * a
c = bc / b
vectorized_geometric_progression = np.vectorize(geometric_progression,
otypes=[np.ndarray])
l = np.stack(vectorized_geometric_progression(bc, c, n))
l = np.atleast_2d(l)
p = np.insert(l[:, :-1], 0, b, axis=1)
l = np.squeeze(l)
p = np.squeeze(p)
pl_avg = (p + l) / 2
komp = np.array([0.75 * pl_avg ** 2]).T
return komp + l
def geometric_progression(bc, c, n):
"""Calculates array l"""
return bc * np.logspace(start=0,
stop=n - 1,
num=n,
base=c + 2)
And you can call it both for sole numbers and NumPy arrays like that:
>>> print(total(1, 2))
[[2.6750000e+01 6.6750000e+01 3.0675000e+02 1.7467500e+03 1.0386750e+04]
[5.9600000e+02 6.3600000e+02 8.7600000e+02 2.3160000e+03 1.0956000e+04]
[2.1176000e+04 2.1216000e+04 2.1456000e+04 2.2896000e+04 3.1536000e+04]
[7.6205600e+05 7.6209600e+05 7.6233600e+05 7.6377600e+05 7.7241600e+05]
[2.7433736e+07 2.7433776e+07 2.7434016e+07 2.7435456e+07 2.7444096e+07]]
>>> print(total(3, 4))
[[1.71000000e+02 3.39000000e+02 1.68300000e+03 1.24350000e+04 9.84510000e+04]
[8.77200000e+03 8.94000000e+03 1.02840000e+04 2.10360000e+04 1.07052000e+05]
[5.59896000e+05 5.60064000e+05 5.61408000e+05 5.72160000e+05 6.58176000e+05]
[3.58318320e+07 3.58320000e+07 3.58333440e+07 3.58440960e+07 3.59301120e+07]
[2.29323574e+09 2.29323590e+09 2.29323725e+09 2.29324800e+09 2.29333402e+09]]
>>> print(total(np.array([1, 3]), np.array([2, 4])))
[[[2.67500000e+01 6.67500000e+01 3.06750000e+02 1.74675000e+03 1.03867500e+04]
[1.71000000e+02 3.39000000e+02 1.68300000e+03 1.24350000e+04 9.84510000e+04]]
[[5.96000000e+02 6.36000000e+02 8.76000000e+02 2.31600000e+03 1.09560000e+04]
[8.77200000e+03 8.94000000e+03 1.02840000e+04 2.10360000e+04 1.07052000e+05]]
[[2.11760000e+04 2.12160000e+04 2.14560000e+04 2.28960000e+04 3.15360000e+04]
[5.59896000e+05 5.60064000e+05 5.61408000e+05 5.72160000e+05 6.58176000e+05]]
[[7.62056000e+05 7.62096000e+05 7.62336000e+05 7.63776000e+05 7.72416000e+05]
[3.58318320e+07 3.58320000e+07 3.58333440e+07 3.58440960e+07 3.59301120e+07]]
[[2.74337360e+07 2.74337760e+07 2.74340160e+07 2.74354560e+07 2.74440960e+07]
[2.29323574e+09 2.29323590e+09 2.29323725e+09 2.29324800e+09 2.29333402e+09]]]
You can see that results are in compliance.
Explanation:
First of all I'd like to note that your calculation of p, k, and l doesn't have to be in the loop. Moreover, calculating k is unnecessary. If you see carefully, how elements of p and l are calculated, they are just geometric progressions (except the 1st element of p):
p = [b, b*c, b*c*(c+2), b*c*(c+2)**2, b*c*(c+2)**3, b*c*(c+2)**4, ...]
l = [b*c, b*c*(c+2), b*c*(c+2)**2, b*c*(c+2)**3, b*c*(c+2)**4, b*c*(c+2)**5, ...]
So, instead of that loop, you can use np.logspace. Unfortunately, np.logspace doesn't support base parameter as an array, so we have no other choice but to use np.vectorize which is just a loop under the hood...
Calculating of komp though is easily vectorized. You can see it in my example. No need for loops there.
Also, as I already noted in a comment, your class doesn't have to be a class, so I took a liberty of changing it to a function.
Next, note that input parameter c is overwritten, so I got rid of it. Variable y is never used. (Also, you could calculate it just as y = c + a * b * np.sign(a - b))
And finally, I'd like to remark that creating NumPy arrays with np.append is very inefficient (as it was pointed out by #kabanus), so you should always try to create them at once - no loops, no appending.
P.S.: I used np.atleast_2d and np.squeeze in my code and it could be unclear why I did it. They are necessary to avoid if-else clauses where we would check dimensions of array l. You can print intermediate results to see what is really going on there. Nothing difficult.
if it is just calling class with two different list elements, loop can satisfies well
res = [blank(i,j,3) for i,j in zip(np.array([1,2,3]),np.array([3,4,5]))]
You can see list of values for res variable
The only way I can think of iterating lists of arrays is by using a function on the main program for iteration and then do the operations you need to do inside the loop.
This solution works for each element of both arrays (note to use zip function for making the iteration in both lists if they have a small size as listed in this answer here):
for n,x in zip(np.array([1,2,3]),np.array([3,4,5])):
res=blank(n,x,3)
print(res.tot)
Hope it is what you need!

python futures and tuple unpacking

What is an elagant/idiomatic way to achieve something like tuple unpacking with futures?
I have code like
a, b, c = f(x)
y = g(a, b)
z = h(y, c)
and I would like to convert it to use futures.
Ideally I would like to write something like
a, b, c = ex.submit(f, x)
y = ex.submit(g, a, b)
z = ex.submit(h, y, c)
The first line of that throws
TypeError: 'Future' object is not iterable
though.
How can I get a,b,c without having to make 3 additional ex.submit calls? ie. I would like to avoid having to write this as:
import operator as op
fut = ex.submit(f, x)
a = client.submit(op.getitem, fut, 0)
b = client.submit(op.getitem, fut, i)
c = client.submit(op.getitem, fut, 2)
y = ex.submit(g, a, b)
z = ex.submit(h, y, c)
I guess a potential solution is to write an unpack function like below,
import operator as op
def unpack(fut, n):
return [client.submit(op.getitem, fut, i) for i in range(n)]
a, b, c = unpack(ex.submit(f, x), 3)
y = ex.submit(g, a, b)
z = ex.submit(h, y, c)
which works: for example if you first define:
def f(x):
return range(x, x+3)
x = 5
g = op.add
h = op.mul
then you get
z.result() #===> 77
I thought something like this might already exist.
The above only works with dask.distributed.Future. It does not work for plain concurrent.futures.Future.
A quick glance at:
https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future
suggests that you'll have to do something like
afuture = ex.submit(f, x)
a,b,c = afuture.result()
...
submit returns a Future object, not the result of running f(x).
This SO answer indicates that chaining futures is not trivial:
How to chain futures in a non-blocking manner? That is, how to use one future as an input in another future without blocking?

Using two theano functions together

If I had something like:
import theano.tensor as T
from theano import function
a = T.dscalar('a')
b = T.dscalar('b')
first_func = a * b
second_func = a - b
first = function([a, b], first_func)
second = function([a, b], second_func)
and I wanted to create a third function that was first_func(1,2) + second_func(3,4), is there a way to do this and create a function that is passed these two smaller functions as input?
I want to do something like:
third_func = first(a, b) + second(a,b)
third = function([a, b], third_func)
but this does not work. What is the correct way to break my functions into smaller functions?
I guess the only way to decompose function is in-terms of tensor variables, rather than function calls. This should work:
import theano.tensor as T
from theano import function
a = T.dscalar('a')
b = T.dscalar('b')
first_func = a * b
second_func = a - b
first = function([a, b], first_func)
second = function([a, b], second_func)
third_func = first_func + second_func
third = function([a, b], third_func)
third_func = first(a, b) + second(a,b) does not work because function call need real values whereas a and b are tensor/symbolic variables. Basically one should define mathematical operations with tensors and then use function to evaluate values of these tensors.

Categories

Resources