Piecewise functions.
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
Hi all, this is probably an already discussed problem, but I've not been able to find a solution even after googling a lot. I've a piecewise defined function: / | f1(x) if x <= a f(x) = | | f2(x) if x > a \ where f1 and f2 are not defined outside the above range. How can I define such a function in Python in order to apply (map) it to an array ranging from values smaller to values bigger than a? Thanks, Andrea.
![](https://secure.gravatar.com/avatar/39916bae984cb93b797efd2b175f59c0.jpg?s=120&d=mm&r=g)
On Thu, 22 Sep 2005, Andrea Riciputi apparently wrote:
this is probably an already discussed problem, but I've not been able to find a solution even after googling a lot.
I've a piecewise defined function:
I suspect I do not understand your question. But perhaps you want this: def f(x): return x<=a and f1(x) or f2(x) fwiw, Alan
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
I've already tried something like this, but it doesn't work since f1 and f2 return not valid values outside the range over they are defined. Perhaps an example could clarify; suppose that f1(x) = 1./ sqrt(1 - x**2) for x <= 1, and f2(x) = 1./sqrt(x**2 - 1) for x > 1. Your suggestion, as the other I've tried, fails with a "OverflowError: math range error". Any helps? Andrea. On Sep 22, 2005, at 9:33 PM, Alan G Isaac wrote:
![](https://secure.gravatar.com/avatar/39916bae984cb93b797efd2b175f59c0.jpg?s=120&d=mm&r=g)
On Thu, 22 Sep 2005, Andrea Riciputi apparently wrote:
Your suggestion, as the other I've tried, fails with a "OverflowError: math range error".
If you do it as I suggested, they should not I believe be evaluated outside of their range. So your function must be generating an overflow error within this range.
Works fine. Cheers, Alan Isaac
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Andrea Riciputi wrote:
I wish people would stop suggesting the 'a and b or c' trick, because it DOESN'T WORK except in special circumstances (i.e. when you can be sure that b is never false). What you want is: def f(x): if x <= a: return f1(x) else: return f2(x) -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg.ewing@canterbury.ac.nz +--------------------------------------+
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
On Sep 23, 2005, at 5:23 AM, Greg Ewing wrote:
It doesn't work either. As I've already explained x is an array containing values both above and below a! What I really need is a way to prevent f1 and f2 from acting on those values of the 'x' array for which the functions are not defined. Any other hints? Andrea.
![](https://secure.gravatar.com/avatar/39916bae984cb93b797efd2b175f59c0.jpg?s=120&d=mm&r=g)
On Fri, 23 Sep 2005, Andrea Riciputi apparently wrote:
The example I posted works with an array, which I called d. If you must feed the array to the function, just move the list comprehension inside of f. Of course, you may find list comprehension too slow for your application. Alan Isaac
![](https://secure.gravatar.com/avatar/8185be81060345a47ca63640f135529c.jpg?s=120&d=mm&r=g)
On Thu, 22 Sep 2005 17:44:59 +0200 Andrea Riciputi <ariciputi@pito.com> wrote:
This does maybe what you want: from scipy import * def f(x): """Approximative implementation of: -1/(x-pi) for x < pi 1/(x-pi) for x > pi """ result = zeros(len(x), x.typecode()) i = argmin(abs(x-pi)) # you may have to tweak i here, because it may be off by 1 result[:i+1] = -1/(x[:i+1]-pi) result[i+1:] = 1/(x[i+1:]-pi) return result x = arange(0, 10, 1, Float) print f(x) print abs(1/(x-pi)) Gerard
![](https://secure.gravatar.com/avatar/37c1dd52b545c64455f1de5977d16dff.jpg?s=120&d=mm&r=g)
Here is a slightly different solution, that is easier to my eyes and that can handle arguments in arbitrary order. It requires numarray (as it uses array indexing). If I understand well it should work woth the new scipy-core (Numeric3), but I haven't it compiled here. Obs: Probably f2 implementation is faster than f1. Best, Paulo ---- from numarray import * def f1(x): """Implementation of: -1/(x-pi) for x < pi 1/(x-pi) for x > pi """ result = zeros(len(x), x.typecode()) # First solution, probably slower, but clear. result[x < pi] = -1/(x[x < pi]-pi) result[x > pi] = 1/(x[x > pi]-pi) return result def f2(x): """Second Iplementation of: -1/(x-pi) for x < pi 1/(x-pi) for x > pi """ result = zeros(len(x), x.typecode()) # Second solution, probably faster as where returns only # the correct indexes. small = where(x < pi) big = where(x > pi) result[small] = -1/(x[small]-pi) result[big] = 1/(x[big]-pi) return result x = arange(0, 10, 1, Float) print f1(x) print f2(x) print abs(1/(x-pi)) # It uses the default value for pi. api = array([pi]) print f1(api) print f2(api)
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
On Sep 23, 2005, at 1:24 PM, Paulo J. S. Silva wrote:
[snip]
Thanks, it is exactly what I'm looking for. Unfortunately, I'm using Numeric, and I won't switch to numarray only for these feature; small arrays are too frequent in my applications. Anyway the method proposed by Gerard Vermeulen works too (thanks a lot Gerard!), and I'll stay with it, at least until Numeric3 won't be ready for the root-mean-square users. ;-) Cheers, Andrea.
![](https://secure.gravatar.com/avatar/4d021a1d1319f36ad861ebef0eb5ba44.jpg?s=120&d=mm&r=g)
Andrea Riciputi wrote:
This is not a trivial problem in current versions of Numeric. What are you using, Numeric, numarray? In new scipy core (which replaces Numeric) and, I think, in numarray, you could say gta = x>a lea = x<=a y = x.copy() y[gta] = f2(x[gta]) y[lea] = f1(x[lea]) I've also just written a piecewise function for the new scipy core so you could write y = piecewise(x, x<=a, [f1,f2]) -Travis Oliphant
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
On Sep 26, 2005, at 7:43 PM, Travis Oliphant wrote:
Yes I'm using Numeric, and in the short to middle term I'll stay with it. Anyway I'm aware of the effort you are spending in putting together a Numeric replacement, and I'll look forward for a stable release of it. In the meanwhile I'll use the tricks already suggested here. Thanks again for your effort, Andrea.
![](https://secure.gravatar.com/avatar/39916bae984cb93b797efd2b175f59c0.jpg?s=120&d=mm&r=g)
On Thu, 22 Sep 2005, Andrea Riciputi apparently wrote:
this is probably an already discussed problem, but I've not been able to find a solution even after googling a lot.
I've a piecewise defined function:
I suspect I do not understand your question. But perhaps you want this: def f(x): return x<=a and f1(x) or f2(x) fwiw, Alan
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
I've already tried something like this, but it doesn't work since f1 and f2 return not valid values outside the range over they are defined. Perhaps an example could clarify; suppose that f1(x) = 1./ sqrt(1 - x**2) for x <= 1, and f2(x) = 1./sqrt(x**2 - 1) for x > 1. Your suggestion, as the other I've tried, fails with a "OverflowError: math range error". Any helps? Andrea. On Sep 22, 2005, at 9:33 PM, Alan G Isaac wrote:
![](https://secure.gravatar.com/avatar/39916bae984cb93b797efd2b175f59c0.jpg?s=120&d=mm&r=g)
On Thu, 22 Sep 2005, Andrea Riciputi apparently wrote:
Your suggestion, as the other I've tried, fails with a "OverflowError: math range error".
If you do it as I suggested, they should not I believe be evaluated outside of their range. So your function must be generating an overflow error within this range.
Works fine. Cheers, Alan Isaac
![](https://secure.gravatar.com/avatar/72ee673975357d43d79069ac1cd6abda.jpg?s=120&d=mm&r=g)
Andrea Riciputi wrote:
I wish people would stop suggesting the 'a and b or c' trick, because it DOESN'T WORK except in special circumstances (i.e. when you can be sure that b is never false). What you want is: def f(x): if x <= a: return f1(x) else: return f2(x) -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg.ewing@canterbury.ac.nz +--------------------------------------+
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
On Sep 23, 2005, at 5:23 AM, Greg Ewing wrote:
It doesn't work either. As I've already explained x is an array containing values both above and below a! What I really need is a way to prevent f1 and f2 from acting on those values of the 'x' array for which the functions are not defined. Any other hints? Andrea.
![](https://secure.gravatar.com/avatar/39916bae984cb93b797efd2b175f59c0.jpg?s=120&d=mm&r=g)
On Fri, 23 Sep 2005, Andrea Riciputi apparently wrote:
The example I posted works with an array, which I called d. If you must feed the array to the function, just move the list comprehension inside of f. Of course, you may find list comprehension too slow for your application. Alan Isaac
![](https://secure.gravatar.com/avatar/8185be81060345a47ca63640f135529c.jpg?s=120&d=mm&r=g)
On Thu, 22 Sep 2005 17:44:59 +0200 Andrea Riciputi <ariciputi@pito.com> wrote:
This does maybe what you want: from scipy import * def f(x): """Approximative implementation of: -1/(x-pi) for x < pi 1/(x-pi) for x > pi """ result = zeros(len(x), x.typecode()) i = argmin(abs(x-pi)) # you may have to tweak i here, because it may be off by 1 result[:i+1] = -1/(x[:i+1]-pi) result[i+1:] = 1/(x[i+1:]-pi) return result x = arange(0, 10, 1, Float) print f(x) print abs(1/(x-pi)) Gerard
![](https://secure.gravatar.com/avatar/37c1dd52b545c64455f1de5977d16dff.jpg?s=120&d=mm&r=g)
Here is a slightly different solution, that is easier to my eyes and that can handle arguments in arbitrary order. It requires numarray (as it uses array indexing). If I understand well it should work woth the new scipy-core (Numeric3), but I haven't it compiled here. Obs: Probably f2 implementation is faster than f1. Best, Paulo ---- from numarray import * def f1(x): """Implementation of: -1/(x-pi) for x < pi 1/(x-pi) for x > pi """ result = zeros(len(x), x.typecode()) # First solution, probably slower, but clear. result[x < pi] = -1/(x[x < pi]-pi) result[x > pi] = 1/(x[x > pi]-pi) return result def f2(x): """Second Iplementation of: -1/(x-pi) for x < pi 1/(x-pi) for x > pi """ result = zeros(len(x), x.typecode()) # Second solution, probably faster as where returns only # the correct indexes. small = where(x < pi) big = where(x > pi) result[small] = -1/(x[small]-pi) result[big] = 1/(x[big]-pi) return result x = arange(0, 10, 1, Float) print f1(x) print f2(x) print abs(1/(x-pi)) # It uses the default value for pi. api = array([pi]) print f1(api) print f2(api)
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
On Sep 23, 2005, at 1:24 PM, Paulo J. S. Silva wrote:
[snip]
Thanks, it is exactly what I'm looking for. Unfortunately, I'm using Numeric, and I won't switch to numarray only for these feature; small arrays are too frequent in my applications. Anyway the method proposed by Gerard Vermeulen works too (thanks a lot Gerard!), and I'll stay with it, at least until Numeric3 won't be ready for the root-mean-square users. ;-) Cheers, Andrea.
![](https://secure.gravatar.com/avatar/4d021a1d1319f36ad861ebef0eb5ba44.jpg?s=120&d=mm&r=g)
Andrea Riciputi wrote:
This is not a trivial problem in current versions of Numeric. What are you using, Numeric, numarray? In new scipy core (which replaces Numeric) and, I think, in numarray, you could say gta = x>a lea = x<=a y = x.copy() y[gta] = f2(x[gta]) y[lea] = f1(x[lea]) I've also just written a piecewise function for the new scipy core so you could write y = piecewise(x, x<=a, [f1,f2]) -Travis Oliphant
![](https://secure.gravatar.com/avatar/09764ad794a843b7c030ae5ace320f18.jpg?s=120&d=mm&r=g)
On Sep 26, 2005, at 7:43 PM, Travis Oliphant wrote:
Yes I'm using Numeric, and in the short to middle term I'll stay with it. Anyway I'm aware of the effort you are spending in putting together a Numeric replacement, and I'll look forward for a stable release of it. In the meanwhile I'll use the tricks already suggested here. Thanks again for your effort, Andrea.
participants (6)
-
Alan G Isaac
-
Andrea Riciputi
-
Gerard Vermeulen
-
Greg Ewing
-
Paulo J. S. Silva
-
Travis Oliphant