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

Hi everyone,

 

I'd like to create a function where I assign a value depending on the parameters of the function as such:

proc fcmp outlib=mydata.functions.func;
function Lx(Age,Sex);

Lx(1,1)=100;
Lx(2,1)=99;
Lx(3,1)=97;

/*
.
.
.
*/
Lx(1,2)=99;
Lx(2,2)=95;
Lx(3,2)=82;

/*
.
.
.
*/
return(Lx);
endsub;

 Unfortunately it doesn't work at all 😁.

I can use "If then Statement " to achieve what I want like shown below, but I would like to learn a different way to get it.

 

proc fcmp outlib=mydata.functions.func;
function Lx(Age,Sex);

if sex=1 then
do;
if age = 1 then
Lx=100;
else if age=2 then
Lx=99;
else if age=3 then
Lx=97;

/*
.
.
.
*/
end;

else if sex=2 then
do;
if age = 1 then
Lx=99;
else if age=2 then
Lx=95;
else if age=3 then
Lx=82;

/*
.
.
.
*/
end;

return(Lx);
endsub;

If someone has any clue on how to achieve this , it'd be awesome to share the solution.

 

Thank u all

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Rick_SAS
SAS Super FREQ

So, there is no formula for the return value in terms of Age and Sex?  You have a table of values for

Sex=1, 2

and for 

Age=1,2,3?

 

In that case, define an array in the PROC FCMP function and look up the value you want:

proc fcmp outlib=work.funcs.TestFuncs;
function Lx(Age,Sex);
array f[3,2] (100 99 87
               99 95 82);
if (0 < Age <= 3) and (0 < Sex <= 2) then 
   return( f[Age, Sex] );
else
   return( . );
endsub;
quit;

options cmplib=work.funcs;  /* define location of function */
data Want;
do Age = 1 to 3;
   do Sex=1 to 2;
      y = Lx(Age, Sex);
      output;
   end;
end;
run;

proc print; run;

 

View solution in original post

13 REPLIES 13
Rick_SAS
SAS Super FREQ

So, there is no formula for the return value in terms of Age and Sex?  You have a table of values for

Sex=1, 2

and for 

Age=1,2,3?

 

In that case, define an array in the PROC FCMP function and look up the value you want:

proc fcmp outlib=work.funcs.TestFuncs;
function Lx(Age,Sex);
array f[3,2] (100 99 87
               99 95 82);
if (0 < Age <= 3) and (0 < Sex <= 2) then 
   return( f[Age, Sex] );
else
   return( . );
endsub;
quit;

options cmplib=work.funcs;  /* define location of function */
data Want;
do Age = 1 to 3;
   do Sex=1 to 2;
      y = Lx(Age, Sex);
      output;
   end;
end;
run;

proc print; run;

 

adil256
Quartz | Level 8

Thank u very much for the quick solution.

This is exactly what I need. It does the job perfectly.

 

I have a dataset with the age and the sex for each person. Sex can only have 2 values, either 1=Man or 2=Women.

The Lx correspond to the values of a mortality table from age 1 to age 120.

 

Do u have any support to recommend for array. I didn't know it was possible to have multiple argument when using array.

 

I just have a general question regarding this kind of array. Is it possible to replace parameter 3 by an asterisk in case I don't want count the number of output?

proc fcmp outlib=work.funcs.TestFuncs;
function Lx(Age,Sex);
array f[*,2] (100 99 87
               99 95 82);
if (0 < Age <= 3) and (0 < Sex <= 2) then 
   return( f[Age, Sex] );
else
   return( . );
endsub;
quit;
FreelanceReinh
Jade | Level 19

@adil256 wrote:

I just have a general question regarding this kind of array. Is it possible to replace parameter 3 by an asterisk in case I don't want count the number of output?

array f[*,2] (100 99 87
               99 95 82);

No, the asterisk is not allowed in the definition of two-dimensional arrays.

 


@adil256 wrote:

I just don't understand why the parameters are reversed in your array [2,3] instead of [3,2] from Rick_Sas.

Maybe the order is not important in my case.


Note that the order of initial values depends on your "style choice" between a 2×3 array (sex,age) and a 3×2 array (age,sex). You must use "row-major order" (see documentation).

With the example values from your initial post your choices are:

array a[2,3] (100 99 97 99 95 82);
array b[3,2] (100 99 99 95 97 82);

 You may write either of these in tabular form (ideally reflecting the array dimensions) for better readability, ...

array a[2,3] (100 99 97
               99 95 82);

array b[3,2] (100 99
               99 95
               97 82);

 ... but the compiler ignores these fancy arrangements and only demands row-major order.

Tom
Super User Tom
Super User

And since the SAS compiler does not care if you use space or commas or both in delimiting the list you can use:

array a[2,3] (100 99 97,99 95 82);
array b[3,2] (100 99,99 95,97 82);

to make is clearer to humans what is happening.

Tom
Super User Tom
Super User

Your boundary checks can be done easier and more precisely by using IN operator.  

if age in (1:3) and sex in (1:2) then 

Now non-integer values of AGE like 2.5 will be trapped.

ballardw
Super User

Your first FCMP doesn't work because you aren't assigning values to LX unless you expect to pass an array back as a value (or maybe an element of an array).

 

This may be what you are looking for. I used a different function name and library so there isn't confusion with yours while testing.

proc fcmp outlib=sasuser.funcs.trial;;
function lukup (age,sex);
   /* this array ONLY uses sex=1 and 2
      with ages= 1, 2 and 3
      the two rows of values are for easy alignment
      age=4 would be the 4th value in each row
      age=5 would be the 5th value in each row
      repeat as needed
   */
   array s_a[2,3] (100 99 97 
                    99 95 87
                  );
   lx = s_a[sex,age];
   return (lx);
endsub;
run;

options cmplib=sasuser.funcs;

data example;
   do sex =1,2;
      do age= 1 to 3;
         result=lukup(age,sex);
         output;
      end;
   end;
run;

If you have more values for sex or age then the ARRAY definition would need to have the numbers in the (2,3) updated to match the number of sex and age values. Additional sex values would require more rows. IF you have gaps in the numbers of values, such as you need an age=7 but don't have an age=6 value then place a . (missing) in the corresponding place in the row.

adil256
Quartz | Level 8

Thank u too for ur solution. It's very similar to Rick_Sas's solution.

I just don't understand why the parameters are reversed in your array [2,3] instead of [3,2] from Rick_Sas.

Maybe the order is not important in my case.

 

Yeah basically I need to lookup the value but I also need to perform calculations with iteration depending on the Age. That's why the merge is not possible for what I want to do I think.

Reeza
Super User
Are you planning to manually enter these values or have them entered from a lookup table somehow? Your FCMP code is manual entry by comparison where a temporary array from a data step can load from a data set or your merge can be dynamic. SAS data step loops automatically so I'm fairly certain a merge will work fine.
adil256
Quartz | Level 8

I intended to do it manually but if there is a way to do it automatically. I'll change my mind :).

 

Let's say I have a dataset with 1 person with age=20 and a mortality table with value from age 20 to 22 for each sex.

I want to calculate the probability of death for each age. To do so,i need to compute the following formula : qx=(Lx[i,Sex]-Lx[i+1,Sex])/Lx[i,Sex];

Data have;
ID=1;
Age=20;
Sex=1;

run;

Data T_Mortality;
retain Age 20;
do Lx=100 to 97 by-1;
Sex=1;
output;
Age=Age+1;
end;
Age=20;
do Lx=99 to 96 by-1;

Sex=2;
output;
Age=Age+1;
end;
run;

Data want;
set have;

Do i=Age to 22;

qx=(Lx[i,Sex]-Lx[i+1,Sex])/Lx[i,Sex];
output;
end;

run;

With the following information, how can I use a merge to perform the calculation? 

Normally, with your FCMP function, I can perform the calculation. I'll just need to write the Lx value manually which is kind of annoying but it'll work.

Thx for ur help.

Reeza
Super User
Data have;
ID=1;
Age=20;
Sex=1;
do age = age to 22;
output;
end;
run;

Data T_Mortality;
retain Age 20;
do Lx=100 to 97 by-1;
Sex=1;
output;
Age=Age+1;
end;
Age=20;
do Lx=99 to 96 by-1;

Sex=2;
output;
Age=Age+1;
end;
run;

proc sql;
create table want as
select t1.ID, t1.Age, t1.Sex, mort1.lx as lx_age, mort2.lx as lx_age_1, (mort1.lx - mort2.lx)/mort1.lx as qx
from have as t1
left join t_mortality as mort1
on mort1.age=t1.age
and mort1.sex=t1.sex
left join t_mortality as mort2
on mort2.age = t1.age+1
and mort2.sex = t1.sex;
quit;

@adil256 wrote:

I intended to do it manually but if there is a way to do it automatically. I'll change my mind :).

 

Let's say I have a dataset with 1 person with age=20 and a mortality table with value from age 20 to 22 for each sex.

I want to calculate the probability of death for each age. To do so,i need to compute the following formula : qx=(Lx[i,Sex]-Lx[i+1,Sex])/Lx[i,Sex];

Data have;
ID=1;
Age=20;
Sex=1;

run;

Data T_Mortality;
retain Age 20;
do Lx=100 to 97 by-1;
Sex=1;
output;
Age=Age+1;
end;
Age=20;
do Lx=99 to 96 by-1;

Sex=2;
output;
Age=Age+1;
end;
run;

Data want;
set have;

Do i=Age to 22;

qx=(Lx[i,Sex]-Lx[i+1,Sex])/Lx[i,Sex];
output;
end;

run;

With the following information, how can I use a merge to perform the calculation? 

Normally, with your FCMP function, I can perform the calculation. I'll just need to write the Lx value manually which is kind of annoying but it'll work.

Thx for ur help.


 

adil256
Quartz | Level 8
Thank u to all of u guys.
I learned a lot thanks to u.
ballardw
Super User

@adil256 wrote:

Thank u too for ur solution. It's very similar to Rick_Sas's solution.

I just don't understand why the parameters are reversed in your array [2,3] instead of [3,2] from Rick_Sas.

Maybe the order is not important in my case.

 

Yeah basically I need to lookup the value but I also need to perform calculations with iteration depending on the Age. That's why the merge is not possible for what I want to do I think.


Style choice for which variable is the "row" and which is the "column" position. No technical or practical difference.

Reeza
Super User
That looks like a lookup table that should be dealt with via a MERGE unless this is something you're using in multiple locations in your programs.

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 13 replies
  • 1107 views
  • 12 likes
  • 6 in conversation