How to define a function that has a condition as input? - python

I need a function that takes a rule/condition as an input. for example given an array of integers detect all the numbers that are greater than two, and all the numbers greater than four. I know this can be achieved easily without a function, but I need this to be inside a function. The function I would like to have is like
def _select(x,rule):
outp = rule(x)
return outp
L = np.round(np.random.normal(2,4,50),decimals=2)
y = _select(x=L,rule=(>2))
y1 = _select(x=L,rule=(>4))
How should I code a function like this?

Functions are first class objects meaning you can treat them as any other variable.
import numpy as np
def _select(x,rule):
outp = rule(x)
return outp
def rule_2(val):
return val > 2
def rule_4(val):
return val > 4
L = np.round(np.random.normal(2,4,50),decimals=2)
y = _select(x=L,rule=rule_2)
print(y)
y1 = _select(x=L,rule=rule_4)
print(y1)
In your example, the condition you want to use can be expressed as a simple expression. The python lambda keyword lets you define expressions as anonymous functions in other statements and expressions. So, you could replace the explicit def of the functions
import numpy as np
def _select(x,rule):
outp = rule(x)
return outp
L = np.round(np.random.normal(2,4,50),decimals=2)
y = _select(x=L,rule=lambda val: val > 2)
print(y)
y1 = _select(x=L,rule=lambda val: val > 4)
print(y1)

Related

float() argument must be a string or a number, not 'function'. What is wrong with this code?

I am trying to make a function call from a choice of two functions based on the variable 't' value. But "float() argument must be a string or a number, not 'function'" comes up. Please help.
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt
n = 9
t = np.linspace(0,10,n)
u = np.linspace(0.0,n)
v = np.linspace(0.0,n)
a = np.linspace(0.0,n)
def T_td(t,utd,vtd):
t = sym.symbols('t')
y = utd*sym.sin(5*t) + vtd*sym.cos(5*t)
yp = y.diff(t)
ypp = yp.diff(t)
j = sym.lambdify(t,y)
k = sym.lambdify(t,yp)
l = sym.lambdify(t,ypp)
return j,k,l
def td_T(t):
t = sym.symbols('t')
y = sym.sin(5*t) + sym.cos(5*t)
yp = y.diff(t)
ypp = yp.diff(t)
j = sym.lambdify(t,y)
k = sym.lambdify(t,yp)
l = sym.lambdify(t,ypp)
return j,k,l
def func(t,utd,vtd):
if t < 5:
u,v,a = td_T(t)
utd = 0
vtd = 0
elif t == 5:
u,v,a = td_T(t)
utd = u
vtd = v
else:
u,v,a = T_td(t,utd,vtd)
return u,v,a,utd,vtd
#print(t)
for i in range(0,n,1):
u[i],v[i],a[i],u_td,v_td = func(t[i],0,0)
The first 3 values in the tuple returned by func() are of the type <function _lambdifygenerated at 0x1282cd090>
The target is a numpy array of floats.
Hence the error
Well, there are many probable errors in this code. The linspace arguments. The useless "t" arguments to td_T and T_td (since the first thing you do in it, is overwrite t with a symbolic value), the apparently useless u_dt and v_td in the main loop.
But the one causing your error, is the fact that u, v and a are numpy arrays of floats. And you are trying to force feed them with functions.
3 1st values returned by func are the values returned by td_T and T_td functions. Which are all the result of sym.lambdify. And as its name suggest, sym.lambdify returns a function, not a float. You are supposed to call those functions with some parameters. And since I've no idea what you are trying to do, I've no idea neither about which would be those parameters. But there have to be some.
Otherwise, it is like you where trying to do
u[i]=sin
v[i]=cos
a[i]=len
sin, cos or len are functions. sin(0), cos(0) and len([]) are numbers.
Likewise, the j, k, l your td_T and T_td functions returns are functions. j(0), k(1), l(2) would be numbers suited to be stored in u[i] and its kind.

Details about how a=b=c works

From this answer: How do chained assignments work?, I understand that chained assignement in Python :
x = y = z # (1)
is equivalent to:
temp = z
x = temp
y = temp
But is (1) also equivalent to:
x = z
y = x
?
Or is there a slight difference (for example when z = some_function())? If so, which difference?
In the very example you give, yes, the effects of the two approaches are practically identical because both involve simply assigning the same reference to a number of names.
Be aware, however, that if the expressions in the assignment targets involve more complex evaluations, the two approaches could be different.
For example, consider the following chain expression, where x is initialized as a dict and expensive_func is a time-consuming function that returns a key:
x[expensive_func()] = y = some_function()
While it would be indeed equivalent to the following:
temp = some_function()
x[expensive_func()] = temp
y = temp
it would not be be equivalent to the second approach:
x[expensive_func()] = some_function()
y = x[expensive_func()]
since expensive_func would then have to be called twice, doubling the time taken, and triggering the side effect of the function twice, if it has any.
Also, consider the following code:
obj = []
x = []
x[:] = y = obj
print(id(obj), id(x), id(y))
where the output would show that y gets assigned the same reference as obj, while x is different.
That code is then indeed equivalent to:
obj = []
x = []
temp = obj
x[:] = temp
y = temp
print(id(obj), id(x), id(y))
But not equivalent to:
obj = []
x = []
x[:] = obj
y = x[:]
print(id(obj), id(x), id(y))
The latter of which would show y getting a different reference from both obj and x.
I always find using examples to be the best way to understand things (in general).
Let's say we have a func:
def function_sample ():
print(20)
If you print it:
print(function_sample)
<function function_sample at 0x7f8f840a01f0>
returns the function object.
When assigning to a variable a function without parentheses (without calling/running it).
x = function_sample
print(x)
you will get the same message: <function function_sample at 0x7f8f840a01f0>
However, if you run it (with parentheses).
print(x())
You will see :
20
None
Why None? It's because Python functions have a default return value, which is None if no return expression is given, or return is given on its own.
Another sample:
def another_sample(some):
print(some)
y = another_sample
print(y)
As you probably have guessed it : <function another_sample at 0x7f8f7e747700>
If you try to print y() you will get an error because the some argument is missing.
But if we add one:
print(y(5))
5
None
One last example:
def third_sample ():
return 20
aa = third_sample # without running the func
bb = third_sample() # calling/running the func
print(aa) # function object
print(bb) # 20
The 2 approaches you have shown are both functional and legit in terms of going about chaining and using previous variables. No difference at all
When assigning variables to the same number of variables, instead of doing the typical:
x = 0
y = 0
OR using tuple unpacking approach:
(x,y) = 0,0
You could just do like what you have (chained assignment):
x = y = 0
This could be used with any object (being called on) for the RHS, and that:
x = y = some_object()
is the same as:
tmp = some_object()
x = tmp
y = tmp
and when you del tmp, the xand y become useless or nothing.

Realizing a matrix of equations: Using a loop to define functions (Python)

For context, I am essentially using a numerical integrator that takes in a set of differential equations defined as functions. A large set of these functions follow a regular pattern and I would like to define them in a loop (or whatever is the most suitable way). For example;
#system coordinates
s = [y1,y2]
#system equations
def e1(s):
x1 = s[1]**2 + 1
return x1
def e2(s):
x1 = s[2]**2 + 2
return x1
#equations of motion
eom = [e1,e2]
Not all of the functions will follow the exact pattern, for those that do though ideally I need something like,
def en(s)
x1 = s[n]**2 + n
return x1
where it is possible to iterate over a range of 'n' values. Thanks for any advice.
Why not simply use a second parameter in your function like so:
def en(s, n)
x1 = s[n]**2 + n
return x1
result = []
for i in range(100): # 100 is just for illustration purposes..
result[0] = en(s, i) # you do not have to store them in a list. just an example
I would use partial, wich bind values to functions arguments:
import functools
def e1(s, n, v1, v2):
x1 = s[n]**v1 + v2
return x1
[functools.partial(e1, n=i, v1=2, v2=1) for i in range(10)] # this was your first example
#your second example
[functools.partial(e1, n=n, v1=2, v2=n) for n in range(10)]

theano and lambda functions

I am having some strange behaviour when I have a list of lambda functions which evaluate theano expressions. The code is below:
# Equivalent functions (or at least I assume so)
def tilted_loss(q,y,f):
e = (y-f)
return (q*tt.sum(e)-tt.sum(e[(e<0).nonzero()]))/e.shape[0]
def tilted_loss2(y,f):
q = 0.05
e = (y-f)
return (q*tt.sum(e)-tt.sum(e[(e<0).nonzero()]))/e.shape[0]
def tilted_loss_np(q,y,f):
e = (y-f)
return (q*sum(e)-sum(e[e<0]))/e.shape[0]
# lambda functions which uses above functions
qs = np.arange(0.05,1,0.05)
q_loss_f = [lambda y,f: tilted_loss(q,y,f) for q in qs]
q_loss_f2 = lambda y,f:tilted_loss(0.05,y,f)
q_loss_f3 = lambda y,f:tilted_loss(qs[0],y,f)
# Test the functions
np.random.seed(1)
a = np.random.randn(1000,1)
b = np.random.randn(1000,1)
print(q_loss_f[0](a,b).eval())
print(q_loss_f2(a,b).eval())
print(q_loss_f3(a,b).eval())
print(tilted_loss2(a,b).eval())
print(tilted_loss_np(qs[0],a,b)[0])
This gives the output:
0.571973847658054
0.5616355181780912
0.5616355181695327
0.5616355181780912
0.56163551817
I must be doing something wrong with the way that the list of functions q_loss_f is defined.
Is the way that q is defined ok? i.e. its a numpy variable that I'm sending in, but this seems to be fine in q_loss_f3.
Any thoughts?
Is a common error, the q value in the lambda expresion will just take the last value from the comprehension loop, you better use partial:
q_loss_f = [partial(tilted_loss, q=q) for q in qs]

Looping through a function in python but redefining a variable when a function from an array of functions gets called

Say I have the following example, in python:
import numpy as np, PROGRAMS as prg
testlist = []
x = 0
n=0
y=[1,2,3,4,5]
x_fn = np.array=([prg.test1(x),prg.test2(x),prg.test3(x)])
for i in range(0,len(x_fn)):
for j in range(0, len(y)):
x = y[j]*2
z=x_fn[i]
testlist.append(z)
j = j+1
i = i+1
print testlist
#####PROGRAMS
def test1(x):
x=x**2
return x
def test2(x):
x=x**3
return x
def test3(x):
x=x+10
return x
If x isn't defined before x_fn then an error occurs but if I define it as zero then that is what is used in the calculations. I basically want this code to produce a list with the the defined value of x in the 2nd loop :
x = y[j]*2
for all values of y. I know there would be a way around this mathematically - but I would like to solve it by running the same function and not changing any of the values of y or any of the functions in PROGRAMS.
Basically, is it a good idea to put these functions in a array and run through it element by element or is there a better way to do it?
Thanks in advance for your replies,
Sven D.
Could this be what you want ?
def test1(x):
x=x**2
return x
def test2(x):
x=x**3
return x
def test3(x):
x=x+10
return x
testlist = []
n=0
y_vals=[1,2,3,4,5]
x_fn = [test1, test2, test3]
for fun in x_fn:
for y in y_vals:
x = y*2
z=fun(x)
testlist.append(z)
print testlist
Functions are objects that can be stored in containers and recalled for use later just like any other object in Python.
You don't even need to use numpy arrays. Just use a list (functions) and put the test functions in it. Note, that I have removed the argument. The elements of the functions array are references to the functions, so you can use them in your loop.
import PROGRAMS as prg
testlist = []
y=[1,2,3,4,5]
functions = [prg.test1, prg.test2, prg.test3]
for func in functions:
for j in y:
x = j*2
z = func(x)
testlist.append(z)
print testlist
#####PROGRAMS
def test1(x):
x=x**2
return x
def test2(x):
x=x**3
return x
def test3(x):
x=x+10
return x

Categories