Passing multiple arguments to apply (Python) - python

I'm trying to clean up some code in Python to vectorize a set of features and I'm wondering if there's a good way to use apply to pass multiple arguments. Consider the following (current version):
def function_1(x):
if "string" in x:
return 1
else:
return 0
df['newFeature'] = df['oldFeature'].apply(function_1)
With the above I'm having to write a new function (function_1, function_2, etc) to test for each substring "string" that I want to find. In an ideal world I could combine all of these redundant functions and use something like this:
def function(x, string):
if string in x:
return 1
else:
return 0
df['newFeature'] = df['existingFeature'].apply(function("string"))
But trying that returns the error TypeError: function() takes exactly 2 arguments (1 given) Is there another way to accomplish the same thing?
Edit:
def function(string, x):
if string in x:
return 1
else:
return 0
df['newFeature'] = df['oldFeature'].apply(partial(function, 'string'))

I believe you want functools.partial. A demo:
>>> from functools import partial
>>> def mult(a, b):
... return a * b
...
>>> doubler = partial(mult, 2)
>>> doubler(4)
8
In your case you need to swap arguments in function (because of idea of partial), and then just
df['existingFeature'].apply(partial(function, "string"))

Related

Python function "sum" with syntax like: sum() -> 0 sum(1)(2)(3) -> 6

I am new to Python and I need to write function sum with similar syntax:
print(sum())
print(sum(1)(2)(3))
print(sum(1)(2)(3)(-4)(-5))
The output is:
0
6
-3
I tried to write decorator like this:
def sum(a=0):
res = a
def helper(b=0):
nonlocal res
res += b
return res
return helper
But it helps only with fixed quantity of (). So this code works only for: sum(1)(2)->3 and doesn't for sum()-><function sum.<locals>.helper at 0x7fca6de44160> or sum(1)()(3) -> TypeError: 'int' object is not callable
I think there should be a decorator with recursion, but i don't know how to realize it
That syntax choice looks very odd to me - should the result of sum(foo) be a number or a function? The built-in sum just takes an iterable and returns a number, which feels much less surprising.
However, assuming that you are certain you indeed want to create something that looks like an integer, walks like an integer, swims like an integer but is also callable, the language does let you create it:
class sum(int):
def __call__(self, x=0):
return sum(self + x)
The output is as you specified:
print(sum())
print(sum(1)(2)(3))
print(sum(1)(2)(3)(-4)(-5))
0
6
-3
Finaly, I found my way of solving this task (using only functions):
def sum(x=None):
value = 0
def helper(y=None):
nonlocal value
if y is None:
return value
value += y
return helper
return helper(x)

Parameters of parsy parser

Consider the following code, which parses and evaluates strings like 567 +223 in Python.
import parsy as pr
from parsy import generate
def lex(p):
return p << pr.regex('\s*')
numberP = lex(pr.regex('[0-9]+').map(int))
#generate
def sumP():
a = yield numberP
yield lex(pr.string('+'))
b = yield numberP
return a+b
exp = sumP.parse('567 + 323')
print(exp)
The #generate is a total mystery for me. Does anyone have more information on how that trick works? It does allow us to write in a similar style to Haskell's monadic do notation. Is code reflection needed to make your own #generate, or is there a clever way to interpret that code literally.
Now here comes my main problem, I want to generalize sumP to opP that also takes an operator symbol and a combinator function:
import parsy as pr
from parsy import generate
def lex(p):
return p << pr.regex('\s*')
numberP = lex(pr.regex('[0-9]+').map(int))
#generate
def opP(symbol, f):
a = yield numberP
yield lex(pr.string(symbol))
b = yield numberP
return f(a,b)
exp = opP('+', lambda x,y:x+y).parse('567 + 323')
print(exp)
This gives an error. It seems that the generated opP already has two arguments, which I do not know how to deal with.
The way that decorators work in Python is that they're functions that are called with the decorated method as an argument and then their return value is assigned to the method name. In other words this:
#foo
def bar():
bla
Is equivalent to this:
def bar():
bla
bar = foo(bar)
Here foo can do anything it wants with bar. It may wrap it in something, it may introspect its code, it may call it.
What #generate does is to wrap the given function in a parser object. The parser object, when parsing, will call the function without arguments, which is why you get an error about missing arguments when you apply #generate to a function that takes arguments.
To create parameterized rules, you can apply #generate to an inner 0-argument function and return that:
def opP(symbol, f):
#generate
def op():
a = yield numberP
yield lex(pr.string(symbol))
b = yield numberP
return f(a,b)
return op

Overload python ternary operator

Is it possible to overload the ternary operator in python? Basically what I want is something like:
class A(object):
def __ternary__(self, a, c):
return a + c
a = A()
print "asdf" if a else "fdsa" # prints "asdffdsa"
I'm trying to implement a symbolic package and basically want something that can do things like:
sym = Symbol("s")
result = 1 if sym < 3 else 10
print result.evaluate(sym=2) # prints 1
print result.evaluate(sym=4) # prints 10
Edit: Let me put out a bit more complex example to show how this could be layered upon.
sym = Symbol("s")
result = 1 if sym < 3 else 10
...
something_else = (result+1)*3.5
...
my_other_thing = sqrt(something_else)
print my_other_thing.evaluate(sym=2) # prints sqrt(7) or rather the decimal equivalent
The point is, I don't need to just be able to late evaluate the one ternary operator, I need to take the result and do other symbolic stuff with that before finally evaluating. Furthermore, my code can do partial evaluations where I give it a few bindings and it returns another symbolic expression if it can't evaluate the full expression.
My backup plan is just to directly use the ternary class taking 3 expressions objects that I would need to make anyway. I was just trying to hide the generation of this class with an operator overload. Basically:
a = TernaryOperator(a,b,c)
# vs
b = a if b else c
look at the sympy module; it already does this
for simple comparison, write A.__eq__ and A.__lt__ methods and use the total_ordering class decorator; this should be sufficient for comparing two As or an A and a constant
write it as a lambda,
result = lambda sym: 1 if sym < 3 else 10
print(result(2)) # => 1
print(result(4)) # => 10
Overload the comparison operators instead (something you probably needed to do anyway):
class A(object):
def __lt__(self, other):
return self.value() < other.value() # Replace with your own implementation of <
Then, use lambda functions to achieve the delayed evaluation you desire:
sym = Symbol("s")
result = lambda s: 1 if s < 3 else 10
sym.set(2)
print result(sym) # prints 1
sym.set(4)
print result(sym) # prints 10
(I don't think you can overload the assignment operator, as it doesn't actually perform an operation on any object, but rather on a variable.)

Return function with function

I would like to do something like the following:
def getFunction(params):
f= lambda x:
do stuff with params and x
return f
I get invalid syntax on this. What is the Pythonic/correct way to do it?
This way I can call f(x) without having to call f(x,params) which is a little more messy IMO.
A lambda expression is a very limited way of creating a function, you can't have multiple lines/expressions (per the tutorial, "They are syntactically restricted to a single expression"). However, you can nest standard function definitions:
def getFunction(params):
def to_return(x):
# do stuff with params and x
return to_return
Functions are first-class objects in Python, so once defined you can pass to_return around exactly as you can with a function created using lambda, and either way they get access to the "closure" variables (see e.g. Why aren't python nested functions called closures?).
It looks like what you're actually trying to do is partial function application, for which functools provides a solution. For example, if you have a function multiply():
def multiply(a, b):
return a * b
... then you can create a double() function1 with one of the arguments pre-filled like this:
from functools import partial
double = partial(multiply, 2)
... which works as expected:
>>> double(7)
14
1 Technically a partial object, not a function, but it behaves in the same way.
You can't have a multiline lambda expression in Python, but you can return a lambda or a full function:
def get_function1(x):
f = lambda y: x + y
return f
def get_function2(x):
def f(y):
return x + y
return f

Creating a new function as return in python function?

I was wondering if it is possible in python to do the following:
def func1(a,b):
return func2(c,d)
What I mean is that suppose I do something with a,b which leads to some coefficients that can define a new function, I want to create this function if the operations with a,b is indeed possible and be able to access this outside of func1.
An example would be a simple fourier series, F(x), of a given function f:
def fourier_series(f,N):
...... math here......
return F(x)
What I mean by this is I want to creat and store this new function for later use, maybe I want to derivate it, or integrate or plot or whatever I want to do, I do not want to send the point(s) x for evaluation in fourier_series (or func1(..)), I simply say that fourier_series creates a new function that takes a variable x, this function can be called later outside like y = F(3)... if I made myself clear enough?
You should be able to do this by defining a new function inline:
def fourier_series(f, N):
def F(x):
...
return F
You are not limited to the arguments you pass in to fourier_series:
def f(a):
def F(b):
return b + 5
return F
>>> fun = f(10)
>>> fun(3)
8
You could use a lambda (although I like the other solutions a bit more, I think :) ):
>>> def func2(c, d):
... return c, d
...
>>> def func1(a, b):
... c = a + 1
... d = b + 2
... return lambda: func2(c,d)
...
>>> result = func1(1, 2)
>>> print result
<function <lambda> at 0x7f3b80a3d848>
>>> print result()
(2, 4)
>>>
While I cannot give you an answer specific to what you plan to do. (Looks like math out of my league.)
I can tell you that Python does support first-class functions.
Python may return functions from functions, store functions in collections such as lists and generally treat them as you would any variable.
Cool things such as defining functions in other functions and returning functions are all possible.
>>> def func():
... def func2(x,y):
... return x*y
... return func2
>>> x = func()
>>> x(1,2)
2
Functions can be assigned to variables and stored in lists, they can be used as arguments for other functions and are as flexible as any other object.
If you define a function inside your outer function, you can use the parameters passed to the outer function in the definition of the inner function and return that inner function as the result of the outer function.
def outer_function(*args, **kwargs):
def some_function_based_on_args_and_kwargs(new_func_param, new_func_other_param):
# do stuff here
pass
return some_function_based_on_args_and_kwargs
I think what you want to do is:
def fourier_series(f,N):
#...... math here......
def F(x):
#... more math here ...
import math #blahblah, pseudo code
return math.pi #whatever you want to return from F
if f+N == 2: #pseudo, replace with condition where f,N turn out to be useful
return F
else:
return None
Outside, you can call this like:
F = fourier_series(a,b)
if F:
ans = F(x)
else:
print 'Fourier is not possible :('
The important thing from Python's point of view are:
Yes, you can write a function inside a function
Yes, you can return a function from a function. Just make sure to return it using return F (which returns the function object) as compared to return F(x) which calls the function and returns the value
I was scraping through some documentation and found this.
This is a Snippet Like your code:
def constant(a,b):
def pair(f):
return f(a,b)
return pair
a = constant(1,2) #If You Print variable-> a then it will display "<function constant.
#<locals>.pair at 0x02EC94B0>"
pair(lambda a, b: a) #This will return variable a.
Now, constant() function takes in both a and b and return a function called "Anonymous Function" which itself takes in f, and calls f with a and b.
This is called "closures". Closures is basically an Instance of a Function.
You can define functions inside functions and return these (I think these are technically closures):
def make_f(a, b):
def x(a, b):
return a+b
return x(a, b)

Categories