When I last visited I was given excellent advice about Gaussian and other
bell-shaped curves. Upon further reflection I realized that the Gaussian curves will not do; the curve does need to have y=0.0 at each end. I tried to apply a Beta distribution, but I cannot correlate the alpha and beta parameters with the curve characteristics that I have. What will work (I call it a pi curve) is a matched pair of sigmoid curves, the ascending curve on the left and the descending curve on the right. Using the Boltzmann function for these I can calculate and plot each individually, but I'm having difficulty in appending the x and y values across the entire range. This is where I would appreciate your assistance. The curve parameters passed to the function are the left end, right end, and midpoint. The inflection point (where y = 0.5) is half-way between the ends and the midpoint. What I have in the function is this: def piCurve(ll, lr, mid): flexL = (mid - ll)/2.0 flexR = (lr - mid)/2.0 tau = (mid - ll)/10.0 x = [] y = [] xL = nx.arange(ll,mid,0.1) for i in xL: x.append(xL[i]) yL = 1.0 / (1.0 + nx.exp(-(xL-flexL)/tau)) for j in yL: y.append(yL[j]) xR = nx.arange(mid,lr,0.1) for i in xR: x.append(xR[i]) yR = 1 - (1.0 / (1.0 + nx.exp(-(xR-flexR)/tau))) for j in yR: y.append(yR[j]) appData.plotX = x appData.plotY = y Python complains about adding to the list: yL = 1.0 / (1.0 + nx.exp(-(x-flexL)/tau)) TypeError: unsupported operand type(s) for -: 'list' and 'float' What is the appropriate way to generate two sigmoid curves so that the x values range from the left end to the right end and the y values rise from 0.0 to 1.0 at the midpoint, then lower to 0.0 again? Rich _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
2008/5/2 Rich Shepard <[hidden email]>:
> When I last visited I was given excellent advice about Gaussian and other > bell-shaped curves. Upon further reflection I realized that the Gaussian > curves will not do; the curve does need to have y=0.0 at each end. > > I tried to apply a Beta distribution, but I cannot correlate the alpha and > beta parameters with the curve characteristics that I have. > > What will work (I call it a pi curve) is a matched pair of sigmoid curves, > the ascending curve on the left and the descending curve on the right. Using > the Boltzmann function for these I can calculate and plot each individually, > but I'm having difficulty in appending the x and y values across the entire > range. This is where I would appreciate your assistance. > > The curve parameters passed to the function are the left end, right end, > and midpoint. The inflection point (where y = 0.5) is half-way between the > ends and the midpoint. > > What I have in the function is this: > > def piCurve(ll, lr, mid): > flexL = (mid - ll)/2.0 > flexR = (lr - mid)/2.0 > tau = (mid - ll)/10.0 > > x = [] > y = [] > > xL = nx.arange(ll,mid,0.1) > for i in xL: > x.append(xL[i]) > yL = 1.0 / (1.0 + nx.exp(-(xL-flexL)/tau)) > for j in yL: > y.append(yL[j]) > > xR = nx.arange(mid,lr,0.1) > for i in xR: > x.append(xR[i]) > yR = 1 - (1.0 / (1.0 + nx.exp(-(xR-flexR)/tau))) > for j in yR: > y.append(yR[j]) > > appData.plotX = x > appData.plotY = y > > Python complains about adding to the list: > > yL = 1.0 / (1.0 + nx.exp(-(x-flexL)/tau)) > TypeError: unsupported operand type(s) for -: 'list' and 'float' > > What is the appropriate way to generate two sigmoid curves so that the x > values range from the left end to the right end and the y values rise from > 0.0 to 1.0 at the midpoint, then lower to 0.0 again? How about multiplying two Boltzmann terms together, ala: f(x) = 1/(1+exp(-(x-flex1)/tau1)) * 1/(1+exp((x-flex2)/tau2)) You'll find if your two flexion points get too close together, the peak will drop below the maximum for each individual curve, but the transition will be continuous. Angus. -- AJC McMorland, PhD candidate Physiology, University of Auckland (Nearly) post-doctoral research fellow Neurobiology, University of Pittsburgh _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
On Fri, 2 May 2008, Angus McMorland wrote:
> How about multiplying two Boltzmann terms together, ala: > > f(x) = 1/(1+exp(-(x-flex1)/tau1)) * 1/(1+exp((x-flex2)/tau2)) > You'll find if your two flexion points get too close together, the peak > will drop below the maximum for each individual curve, but the transition > will be continuous. Angus, With an x range from 0.0-100.0 (and the flexion points at 25.0 and 75.0), the above formula provides a nice bell-shaped curve from x=0.0 to x=50.0, and a maximum y of only 0.25 rather than 2.0. Modifying the above so the second term is subtracted from 1 before the multiplication, or by negating the exponent in the second term, yields only the first half: the ascending 'S' curve from 0-50. Thanks, Rich -- Richard B. Shepard, Ph.D. | Integrity Credibility Applied Ecosystem Services, Inc. | Innovation <http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863 _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
In reply to this post by Rich Shepard
Rich,
this could use some serious vectorization/numpyification! Poke around the scipy Wiki and whatever other tutorials you can find -- you'll be glad you did. A hint: When you are writing a loop like: > for i in xL: > x.append(xL[i]) You should be doing array operations! Specifics: xL = nx.arange(ll,mid,0.1) # you've now created an array of your X values for i in xL: x.append(xL[i]) #this dumps them into a list - why not keep them an array? # if you really need an array (you don't here), then you can use: xL.tolist() That's why you get this error: TypeError: unsupported operand type(s) for -: 'list' and 'float' you're trying to do array operations on a list. Also, you probably want np.linspace, rather than arange, it's a better option for floats. Here is my version: import numpy as np ll, lr, mid = (-10, 10, 0) flexL = (mid - ll)/2.0 flexR = (lr - mid)/2.0 tau = (mid - ll)/10.0 xL = np.linspace(ll, mid, 5) yL = 1.0 / (1.0 + np.exp(-(xL-flexL)/tau)) print xL print yL xR = np.linspace(mid, lr, 5) yR = 1 - (1.0 / (1.0 + np.exp(-(xR-flexR)/tau))) print xR print yR # now put them together: x = np.hstack((xL, xR[1:])) # don't want to duplicate the midpoint y = np.hstack((yL, yR[1:])) print x print y Though it doesn't look like the numbers are right. Also, you don't need to create the separate left and right arraysand put them together, slicing gives a view, so you could do: numpoints = 11 # should be an odd number to get the midpoint x = linspace(ll,lr, numpoints) y = zeros_like(x) xL = x[:numpoints/2] yL = y[:numpoints/2] yL[:] = 1.0 / (1.0 + np.exp(-(xL-flexL)/tau)) you could also use something like: np.where(x<0, .....) left as an exercise for the reader... -Chris -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception [hidden email] _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
On Fri, 2 May 2008, Christopher Barker wrote:
> this could use some serious vectorization/numpyification! Poke around the > scipy Wiki and whatever other tutorials you can find -- you'll be glad you > did. A hint: > > When you are writing a loop like: >> for i in xL: >> x.append(xL[i]) > > You should be doing array operations! Chris, Good suggestions. I need to append the two numpy arrays so I return all x values in a single list and all y values in another list. I'll read the numpy book again. > That's why you get this error: > TypeError: unsupported operand type(s) for -: 'list' and 'float' > > you're trying to do array operations on a list. Ah, so! > Also, you probably want np.linspace, rather than arange, it's a better > option for floats. arange() has worked just fine in other functions. > # now put them together: > x = np.hstack((xL, xR[1:])) # don't want to duplicate the midpoint > y = np.hstack((yL, yR[1:])) > Also, you don't need to create the separate left and right arraysand put > them together, slicing gives a view, so you could do: I assume that there's a way to mate the two curves in a single equation. I've not been able to find what that is. Thanks, Rich _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
In reply to this post by Rich Shepard
2008/5/2 Rich Shepard <[hidden email]>:
> What will work (I call it a pi curve) is a matched pair of sigmoid curves, > the ascending curve on the left and the descending curve on the right. Using > the Boltzmann function for these I can calculate and plot each individually, > but I'm having difficulty in appending the x and y values across the entire > range. This is where I would appreciate your assistance. It's better not to work point-by-point, appending things, when working with numpy. Ideally you could find a formula which just produced the right curve, and then you'd apply it to the input vector and get the output vector all at once. For example for a Gaussian (which you're not using, I know), you'd write a function something like def f(x): return np.exp(-x**2) and then call it like: f(np.linspace(-1,1,1000)). This is efficient and clear. It does not seem to me that the logistic function, 1/(1+np.exp(x)) does quite what you want. How about using the cosine? def f(left, right, x): scaled_x = (x-(right+left)/2)/((right-left)/2) return (1+np.cos((np.pi/2) * scaled_x))/2 exactly zero at both endpoints, exactly one at the midpoint, inflection points midway between, where the value is 1/2. If you want to normalize it so that the area underneath is one, that's easy to do. More generally, the trick of producing a scaled_x as above lets you move any function anywhere you like. If you want the peak not to be at the midpoint of the interval, you will need to do something a litle more clever, perhaps choosing a different function, scaling the x values with a quadratic polynomial so that (left, middle, right) becomes (-1,0,1), or using a piecewise function. Good luck, Anne _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
On Fri, 2 May 2008, Anne Archibald wrote:
> It's better not to work point-by-point, appending things, when working > with numpy. Ideally you could find a formula which just produced the right > curve, and then you'd apply it to the input vector and get the output > vector all at once. Anne, That's been my goal. :-) > How about using the cosine? > > def f(left, right, x): > scaled_x = (x-(right+left)/2)/((right-left)/2) > return (1+np.cos((np.pi/2) * scaled_x))/2 > > exactly zero at both endpoints, exactly one at the midpoint, > inflection points midway between, where the value is 1/2. If you want > to normalize it so that the area underneath is one, that's easy to do. > More generally, the trick of producing a scaled_x as above lets you > move any function anywhere you like. This looks like a pragmatic solution. When I print scaled_x (using left = 0.0 and right = 100.0), the values range from -1.0 to +0.998. So, I need to figure out the scale_x that sets the end points at 0 and 100. Thanks very much, Rich _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
2008/5/2 Rich Shepard <[hidden email]>:
> On Fri, 2 May 2008, Anne Archibald wrote: > > > It's better not to work point-by-point, appending things, when working > > with numpy. Ideally you could find a formula which just produced the right > > curve, and then you'd apply it to the input vector and get the output > > vector all at once. > > Anne, > > That's been my goal. :-) > > > > How about using the cosine? > > > > def f(left, right, x): > > scaled_x = (x-(right+left)/2)/((right-left)/2) > > return (1+np.cos((np.pi/2) * scaled_x))/2 > > > > exactly zero at both endpoints, exactly one at the midpoint, > > inflection points midway between, where the value is 1/2. If you want > > to normalize it so that the area underneath is one, that's easy to do. > > > More generally, the trick of producing a scaled_x as above lets you > > move any function anywhere you like. > > This looks like a pragmatic solution. When I print scaled_x (using left = > 0.0 and right = 100.0), the values range from -1.0 to +0.998. So, I need to > figure out the scale_x that sets the end points at 0 and 100. No, no. You *want* scaled_x to range from -1 to 1. (The 0.998 is because you didn't include the endpoint, 100.) The function I gave, (1+np.cos((np.pi/2) * scaled_x))/2, takes [-1, 1] to a nice bump-shaped function. If you feed in numbers from 0 to 100 as x, they get transformed to scaled_x, and you feed them to the function, getting a result that goes from 0 at x=0 to 1 at x=50 to 0 at x=100. Anne _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
Anne Archibald wrote:
> 2008/5/2 Rich Shepard <[hidden email]>: > No, no. You *want* scaled_x to range from -1 to 1. Why not just scale to -pi to pi right there? (The 0.998 is because you didn't include the endpoint, 100.) Which is why you want linspace, rather than arange. Really, trust me on this! If the cosine curve isn't right for you, a little create use of np.where would let you do what you want in maybe another line or two of code. Something like: y = np.where( x < 0 , Leftfun(x), Rightfun(x) ) or just compute one side and then flip it to make the other side: y = np.zeros_like(x) y[center:] = fun(x[center:]) y[:center] = y[center+1:][::-1] # don't want the center point twice if it's symetric anyway. -Chris -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception [hidden email] _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
On Fri, 2 May 2008, Christopher Barker wrote:
> Why not just scale to -pi to pi right there? Dunno, Chris. As I wrote to Anne (including a couple of files and the resulting plot), it's been almost three decades since I dealt with the math underlying distribution functions. > Which is why you want linspace, rather than arange. Really, trust me on > this! I see the difference in the book. Didn't know about linspace(), but adding True brings the end point to 100.0 > If the cosine curve isn't right for you, a little create use of np.where > would let you do what you want in maybe another line or two of code. > Something like: > > y = np.where( x < 0 , Leftfun(x), Rightfun(x) ) > > or just compute one side and then flip it to make the other side: > > y = np.zeros_like(x) > y[center:] = fun(x[center:]) > y[:center] = y[center+1:][::-1] # don't want the center point twice > > if it's symetric anyway. I'll look into this over the weekend (after upgrading my machines to Slackware-12.1). I can get nice sigmoid curves with the Boltzmann function. This thread started when I asked how to combine the two into a single curve with one set of x,y points. Much appreciated, Rich -- Richard B. Shepard, Ph.D. | Integrity Credibility Applied Ecosystem Services, Inc. | Innovation <http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863 _______________________________________________ Numpy-discussion mailing list [hidden email] http://projects.scipy.org/mailman/listinfo/numpy-discussion |
Free forum by Nabble | Edit this page |