BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
PamG
Quartz | Level 8

I have a data set with y and x.  Is there a way to find the 2 roots of a quadratic equation for each obs? 

The equation is y - 2*x*R  + 3*x*R**2 =0.  

Sample data is

data have;
input y x;
datalines;
1 2
2 4
5 6
8 9
;
run;

1 ACCEPTED SOLUTION

Accepted Solutions
Rick_SAS
SAS Super FREQ

Initially you said you wanted to solve

(3*x)*R**2 - (2*x)*R + y =0, which has polynomial coefficients {3*x, -2*x, y}

But later you use 

1*R**2 + 1*R + 2*e1 = 0 

Which is it?

 

In any case, to get the roots of a low-degree polynomial, use the POLYROOT function in SAS IML. Create a SAS data set that has the polynomial coefficients ordered from highest degree to lowest (the constant term).  Read the coefficients into IML and loop over the coefficients.

 

A polynomial of degree d always has d complex roots. If you are interested only in the real roots, then find the roots for which the imaginary part is 0 (or very tiny).

 

The following code assumes that you want to solve the equation in your original post. It then creates a SAS data set that has ONLY the polynomial coefficients. The IML program extracts the REAL roots.

 

data have;
input y x;
datalines;
-1 2
-2 3
-5 6
-8 9
;

/* root of
   (3*x)*R**2 - 2*x*R  + y = 0 
   Let a = 3*x, b= -2*x, and c=y and use the quadratic equation
*/
data Coef;
keep c2 c1 c0;
set have;
c2 = 3*x;
c1 = -2*x;
c0 = y;
run;

proc iml;
delta = 1E-14;   /* small number to use as a custoff value */
use Coef;
read all var _num_ into coeff;  /* coefficients of R^2, R, constant */
close;

numPolys = nrow(coeff);
deg = ncol(coeff)-1;
roots = j(numPolys, deg, .);  /* poly of degree d has d complex roots */
do i = 1 to numPolys;
   r = polyroot( coeff[i,] );   /* returns all complex roots */
   print i r;
   /* store only the real roots for which r[,2]=0 */
   realIdx = loc( abs(r[,2]) < delta );
   numRealRts = ncol(realIdx);
   if numRealRts > 0 then 
      roots[i, 1:numRealRts] = rowvec( r[realIdx, 1] );  /* get the real parts */
end;
PolyNumber = T(1:numPolys);
RootNumber = 1:deg;
print PolyNumber roots[c=RootNumber];

You can use the same IML program to find roots of higher-degree polynomials. For example, if you want to find cubic roots, use the following DATA step to create the coefficients and use the same IML code:

data Coef;
input c3 c2 c1 c0;
datalines;
1 -1 2 1
1 -1 -4 4
1 1 -3 -3
;

 

View solution in original post

12 REPLIES 12
Tom
Super User Tom
Super User

The quadratic equation from Wikipedia:

Tom_3-1736092154963.png

So first convert your equation to the standard form.

Tom_4-1736092209193.png

Then just calculate the various parts. -b , 2a and sqrt(b**2-4ac). 

Make sure to check if 4ac > b**2 because those cases have imaginary roots.

If it has real roots then build that by combining the parts twice, once using addition and once using subtraction.

PamG
Quartz | Level 8

Thanks for your response.  I was looking for an easier way to find the roots without having to write the equations in data step.  For another project I will be having cubic polynomials.  In that case how do I save the 3 roots in 3 variables?  I saw some examples online who are using  PROC MODEL or PROC IML.  But they use some sort of starting numbers to find the roots.

PaigeMiller
Diamond | Level 26

I was looking for an easier way to find the roots without having to write the equations in data step.

 

Hint: there are a gazillion different articles out on the internet regarding how to perform specific mathematical and statistical calculations, many of these articles written by @Rick_SAS . Searching for these are always a good place to start.

 

See Rick's 2011 article: https://blogs.sas.com/content/iml/2011/08/03/finding-the-root-of-a-univariate-function.html

 

Hint: in your second message, you said "I was looking for an easier way to find the roots without having to write the equations in data step. For another project I will be having cubic polynomials." Please tell us all of your needs in your first message from now on.

--
Paige Miller
sbxkoenk
SAS Super FREQ

Ciao, Koen

PamG
Quartz | Level 8

Thanks for the link. I looked up and got some code.  However I am getting only one root.

**Example 3: Solve for a: P=2*e1 + a + a*a;
data test;
input p e1;
datalines;
12 1
24 2
36 3
;
run;

/* FCMP routine to find function roots */
proc fcmp outlib=work.fcmp.test;
function fn(e1, a);
    return (2*e1 + a + a*a );
endsub;
function findZero(p, e1,init);
    array solvopts[1] initial (0);
    initial = init;
    return (solve("fn", solvopts, p, e1, .)); 
endsub;
run;

options cmplib=work.fcmp.test;
         
data roots;
set test;
format root 5.2;
root = findZero(p, e1, 0.1);
run;
sbxkoenk
SAS Super FREQ

Sorry for still another link ... but I would do it this way :

https://communities.sas.com/t5/SAS-IML-Software-and-Matrix/find-roots-of-function/td-p/794659

 

A quadratic function (equation) may have one, two, or zero roots.

  • If the discriminant is less than 0, then the quadratic has no real roots. (but the quadratic equation still has two complex-valued roots)
  • If the discriminant is equal to zero then the quadratic has equal roots. (two roots with the same value, called a double root)
  • If the discriminant is more than zero then it has 2 distinct roots.

Ciao,

Koen

PaigeMiller
Diamond | Level 26

The link I gave does not have a solution that uses PROC FCMP. So, I don't know what code you are using or what link you got it from.

--
Paige Miller
Ksharp
Super User
Your object funtion
P=2*e1 + a + a*a;
is monotonic , so you can't expect to get two roots.
Ksharp
Super User

Yes. Check  @Rick_SAS  blogs. Risk has written many blog about this topic.

For your this special topic, you could use PROC NLIN ,SAS/IML .SAS/OR to get root of function.

 

https://blogs.sas.com/content/iml/2022/05/04/bootstrap-nonlinear-regression.html

https://blogs.sas.com/content/iml/2015/06/08/fit-circle.html

https://blogs.sas.com/content/iml/2018/08/15/optimization-nonlinear-constraints.html

 

Here is some example:

First is from Rick

data have;
input y x;
datalines;
12 1
24 2
36 3
;
run;

 
proc nlin data=have plots(stats=none)=(fitplot); 
   parameters R=1;                
   model y = 2*x + R + R**2;
run;

Ksharp_0-1736131930234.png

 

 

Second is from Rick


proc iml;
start ObjF(R)  global(x, y);    /* x and y are fixed vectors of data */
   F = ssq(2*x + R + R**2 - y);            /* minimize F = SSQ differences */
   return(F);
finish;
use have;  read all var {x y};  close ;  /* read observed data */
R = {10};                           /* initial guess for (x0, y0) */
opt = {0 1};                           /* options=(min, print level) */
call nlptr(rc,result,"ObjF", r, opt);  /* find optimum (x0, y0) */
print result;
quit;

Ksharp_1-1736132003054.png

 

 

Third is from @RobPratt 


proc optmodel;
   set OBS;
   num x {OBS};
   num y {OBS};
   read data have into OBS=[_N_] x y;
   var R >= 0;
   min F = sum {i in OBS} (2*x[i]+R+R^2  - y[i])^2;
   solve;
   print R;
quit;

Ksharp_2-1736132067248.png

 

Ksharp
Super User

If R was a vector:


data have;
input y x;
datalines;
12 1
24 2
36 3
;
run;



proc optmodel;
   set OBS;
   num x {OBS};
   num y {OBS};
   read data have into OBS=[_N_] x y;
   var R{OBS} >= 0;
   min F = sum {i in OBS} (2*x[i]+R[i]+R[i]^2  - y[i])^2;
   solve ;

   print F R;
quit;

Ksharp_0-1736133079047.png

 

Rick_SAS
SAS Super FREQ

Initially you said you wanted to solve

(3*x)*R**2 - (2*x)*R + y =0, which has polynomial coefficients {3*x, -2*x, y}

But later you use 

1*R**2 + 1*R + 2*e1 = 0 

Which is it?

 

In any case, to get the roots of a low-degree polynomial, use the POLYROOT function in SAS IML. Create a SAS data set that has the polynomial coefficients ordered from highest degree to lowest (the constant term).  Read the coefficients into IML and loop over the coefficients.

 

A polynomial of degree d always has d complex roots. If you are interested only in the real roots, then find the roots for which the imaginary part is 0 (or very tiny).

 

The following code assumes that you want to solve the equation in your original post. It then creates a SAS data set that has ONLY the polynomial coefficients. The IML program extracts the REAL roots.

 

data have;
input y x;
datalines;
-1 2
-2 3
-5 6
-8 9
;

/* root of
   (3*x)*R**2 - 2*x*R  + y = 0 
   Let a = 3*x, b= -2*x, and c=y and use the quadratic equation
*/
data Coef;
keep c2 c1 c0;
set have;
c2 = 3*x;
c1 = -2*x;
c0 = y;
run;

proc iml;
delta = 1E-14;   /* small number to use as a custoff value */
use Coef;
read all var _num_ into coeff;  /* coefficients of R^2, R, constant */
close;

numPolys = nrow(coeff);
deg = ncol(coeff)-1;
roots = j(numPolys, deg, .);  /* poly of degree d has d complex roots */
do i = 1 to numPolys;
   r = polyroot( coeff[i,] );   /* returns all complex roots */
   print i r;
   /* store only the real roots for which r[,2]=0 */
   realIdx = loc( abs(r[,2]) < delta );
   numRealRts = ncol(realIdx);
   if numRealRts > 0 then 
      roots[i, 1:numRealRts] = rowvec( r[realIdx, 1] );  /* get the real parts */
end;
PolyNumber = T(1:numPolys);
RootNumber = 1:deg;
print PolyNumber roots[c=RootNumber];

You can use the same IML program to find roots of higher-degree polynomials. For example, if you want to find cubic roots, use the following DATA step to create the coefficients and use the same IML code:

data Coef;
input c3 c2 c1 c0;
datalines;
1 -1 2 1
1 -1 -4 4
1 1 -3 -3
;

 

PamG
Quartz | Level 8

Thank you so much Rick!!!  This works perfectly.  

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 12 replies
  • 3081 views
  • 7 likes
  • 6 in conversation