Reputation: 2012
I have a given function
def unnorm(x, alpha, beta):
return (1 + alpha * x + beta * x ** 2)
Which I then integrate to find a normalization constant for in a range, and turn it to a lambda function that takes the same parameters as unnorm
. Now, to create a fit-able object, I combine the functions like this:
def normalized(x, alpha, beta):
return unnorm(x, alpha, beta) * norm(x, alpha, beta)
Which is nice and all, but there's still repetition and pulling names from the global namespace.
How can I combine the two functions in a cleaner fashion, without having to re-write parameters? E.g
def normalized(func, normalizer):
return func * normalizer
Full code:
import sympy
import numpy as np
import inspect
def normalize_function(f, xmin, xmax):
"""
Normalizes function to PDF in the given range
"""
# Get function arguments
fx_args = inspect.getfullargspec(f).args
# Convert to symbolic notation
symbolic_args = sympy.symbols(fx_args)
# Find definite integral
fx_definite_integral = sympy.integrate(f(*symbolic_args), (symbolic_args[0], xmin, xmax))
# Convert to a normalization multiplication term, as a real function
N = sympy.lambdify(expr = 1 / fx_definite_integral, args = symbolic_args)
return N
def unnorm(x, alpha, beta):
return (1 + alpha * x + beta * x ** 2)
norm = normalize_function(unnorm, -1, 1)
# How do I condense this to a generic expression?
def normalized(x, alpha, beta):
return unnorm(x, alpha, beta) * norm(x, alpha, beta)
x = np.random.random(100)
print(normalized(x, alpha = 0.5, beta = 0.5))
Upvotes: 4
Views: 1634
Reputation: 164653
I don't see anything wrong with what you are doing now. But for aesthetic purposes, here are a couple of alternatives with some minimal functions.
def doubler(x, y, z):
return 2*(x + y + z)
def halver(x, y, z):
return 0.5*(x + y + z)
def doubler_halver_sumprod(*args):
return doubler(*args) * halver(*args)
dhs = lambda *args: doubler(*args) * halver(*args)
doubler_halver_sumprod(1, 2, 3) # 36
dhs(1, 2, 3) # 36
If you want a truly extendible, functional approach, extracting arguments once, this could work:
from operator import mul, methodcaller
from functools import reduce
def prod(iterable):
return reduce(mul, iterable, 1)
def doubler(x, y, z):
return 2*(x + y + z)
def halver(x, y, z):
return 0.5*(x + y + z)
def dhs2(*args):
return prod(map(methodcaller('__call__', *args), (doubler, halver)))
def dhs3(*args):
return prod(f(*args) for f in (doubler, halver))
dhs2(1, 2, 3) # 36
dhs3(1, 2, 3) # 36
Upvotes: 6
Reputation: 7186
Well, one way would be a decorator that implements *
and so on for functions:
class composable:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
def __mul__(self, other):
if callable(other):
def wrapper(*args, **kwargs):
return self(*args, **kwargs) * other(*args, **kwargs)
return self.__class__(wrapper)
return NotImplemented
@composable
def f(x):
return 2 * x
@composable
def g(x):
return x + 1
h = f * g # (2*x) * (x+1)
print(h(2))
# 12
You would need to add similar definitions for __add__
, __sub__
, __div__
and probably for the reverse methods __rmul__
and so on.
Upvotes: 2