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

Hi Rhapsody,

 

Your desire:

 

Y=something(X)

 

can be achieved as a Function (something), only  if Y can take a SINGLE value where X can be an array. But you desire an array to be returned and so it can be achieved by a Subroutine only.

 

See the first part of my earlier solution. I reproduce that part:

data have;
input theta1-theta3;
datalines;
0.2  0.5  0
0.3  0.6  0.023
0.5  0.1  0.05
0.7  0.2  0.01 
;
run;

proc fcmp outlib = work.func.arr;
   subroutine add(t[*], tr[*]);
      outargs t, tr;
      tr[1] = t[1];
      do i = 2 to dim(t);
         tr[i] = t[i-1] + t[i];
      end;
   endsub;
quit;

options cmplib = work.func;

data want;
   array t[3] _temporary_;
   array trans[3] _temporary_;
   
   set have;
   array th theta1-theta3;
   do i = 1 to dim(t);
      t[i] = th[i];
   end;
   call add(t,trans);
   array tran trans1-trans3;
   do i = 1 to dim(t);
      tran[i] = trans[i];
   end;
   drop i th:;
run;

The Output:

Obs 	trans1 	trans2 	trans3
1 	0.2 	0.7 	0.500
2 	0.3 	0.9 	0.623
3 	0.5 	0.6 	0.150
4 	0.7 	0.9 	0.210

Since we want the Array, TRANS[ ], to be filled by FCMP program from the Array, T[ ], it is possible by a SUBROUTINE only. Hence, in Data Step we pass both the arrays by the subroutine (Subroutine ADD). This is done by passing one observation with theta1 to theta3 at a time where Array T[ ] holds them. Array TRANS[ ], while holding missing values on input, gets filled by the FCMP call routine:

 

call add(t,trans);

Note in FCMP parentheses ( ), are reserved for function arguments. Square brackets ([ ]) is a natural choice for Arrays.

 

 

 

Regards,

DATAsp

 

rhapsody
Calcite | Level 5

Oki.

 

The reason why I was looking into this option in the first place is to simplify the code. Instead of writing 30 lines of code you write just one function and everytime you wanna use that function you just write something like

 

data;

set input;

Y=something(X);

run;

 

And that gives you the output Array Y.

 

But since your solution implies about 30 lines of code I suppose that this kind of code simplification just isn't available in SAS?

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Just re-assess your data modelling.  Normalised (i.e. data going down the page) is far simpler to work with than transposed (across, which is what you have).  It would save you all the hassel of array processing and lots of coding.  Alternatively if you have a lot of matrix data, then IML should be something you look at, that is built for matrix processing.

Simply put, if you find something is difficult, or requires lots of coding, then you likely have something else wrong which is causing you work later on, be that data modelling or choice of processing.

KachiM
Rhodochrosite | Level 12

Hello,

 

FCMP functions and call routines are written once, compiled and saved in a Library with special Dataset Name. You don't write the function every time to run it. Then, give the LibraryName.Dataset name as option and use the function.

The second part of the solution, I gave previously, is one way to achieve your need.

I am not aware of any other software doing it in one or two statements. I will be thankful to you if you can describe such a software.

 

Regards,

DATAsp

rhapsody
Calcite | Level 5

In your example you have to write quite a lot of lines ta call the function again which makes it pointless to work with functions in the first place. I'll give you an example in Matlab where you can write a function and then call it using only one row of code.

 

I have the input Array X

 

X

0,67393,96833,7848,813,610,850,000,000,001,47
22,04167,802189,828,601,972,440,000,000,000,04
8,8339,581563,3442,6446,331,760,840,000,000,00
6,0081,531039,9512,2625,770,760,940,000,000,00
1,8887,12386,6111,5219,832,850,030,000,000,00
6,1261,22195,1547,000,140,920,000,000,000,00
0,38109,71649,6893,1713,050,000,700,000,000,00
2,3117,991416,3892,775,440,980,580,000,000,00
3,2955,231542,5135,9019,831,210,000,000,000,88
4,04130,801417,1363,7916,540,980,000,000,000,00
13,175,18963,5069,6034,621,310,980,000,000,00
3,34160,361341,45135,7331,930,000,040,000,000,00

 

And then I want a function which normalizes every row such that it sums to one. The function looks like

 

 

function Y = num2porcent(X)

 

[row col]=size(y);

sumY=sum(y,2);

Y = X./(sumY*ones(1,col));

 

end

 

Every time I wanna call it I just wirite

 

Y = num2porcent(X)

 

No more lines required.

 

 

 

The output is

Y

 

0,0010,3070,6500,0380,0030,0010,0000,0000,0000,001
0,0090,0700,9150,0040,0010,0010,0000,0000,0000,000
0,0050,0230,9180,0250,0270,0010,0000,0000,0000,000
0,0050,0700,8910,0110,0220,0010,0010,0000,0000,000
0,0040,1710,7580,0230,0390,0060,0000,0000,0000,000
0,0200,1970,6280,1510,0000,0030,0000,0000,0000,000
0,0000,1270,7500,1070,0150,0000,0010,0000,0000,000
0,0020,0120,9220,0600,0040,0010,0000,0000,0000,000
0,0020,0330,9300,0220,0120,0010,0000,0000,0000,001
0,0020,0800,8680,0390,0100,0010,0000,0000,0000,000
0,0120,0050,8850,0640,0320,0010,0010,0000,0000,000
0,0020,0960,8020,0810,0190,0000,0000,0000,0000,000

 

So this is a good example where I use an Array as input and get an Array as output and everytime I call it the only thing needed is one row of code.

rhapsody
Calcite | Level 5

I'm sorry, I made a small confusing error in the function. The corrected one is found below

 

function Y = num2porcent(X)

 

[row col]=size(X);

sumX=sum(X,2);

Y = X./(sumX*ones(1,col));

 

end

KachiM
Rhodochrosite | Level 12

Hi Rhapsody,

 

Thanks for showing the Matlab function. You simply use NUM2PORCENT() function to pass an input Array to get an output Array. The code of the function is hidden. Who knows how many statements it has.

 

In a similar way SAS provides hundreds of functions to use and we don't know its contents. For a specific need SAS provides Proc FCMP to build our own functions. Let us assume some programmer has written a function ARRAY2ARRAY() just to help you. You are supposed to use the compiled function saved into a Library  for infinite number of times. You just use one statement to pass your input Array as SAS Data Set to the function to get an output Array as a SAS Data Set. You write:

 

options cmplib = work.cmpar;                                     /* This is the programmer given Library */

 

%let rc = %sysfunc(Array2Array(have, want);            /* This is how the function is to be used */

 

passing the Data Set, HAVE to get an Output Data Set Want.

 

Now you can visualize the similarity between Matlab and FCMP Functions.

 

The programmer made his function as below. You assume it is hidden for the User like you.

 

proc fcmp outlib = work.cmpar.lib;
   function array2array(inds $, outds $);
   file log;
   array theta[1] / nosymbols; 
   array trans[1] / nosymbols; 

   rc = read_array(inds, theta); 
   
   rows = dim1(theta); cols = dim2(theta); 
   call dynamic_array(trans, rows, cols);    
   do i = 1 to rows;
      do j = 1 to cols - 1;
         trans[i,1] = theta[i,1]; 
         trans[i,j+1] = theta[i, j+1] + theta[i, j]; 
      end;
   end;
   rc = write_array(outds, trans); 
   put 'INFO: Created Data Set' outds  'with' rows 'Rows and' cols 'Cols.';
   return(1); 
   endsub;
quit;

Your Data Set (HAVE) is:

data have;
input theta1-theta3;
datalines;
0.2  0.5  0
0.3  0.6  0.023
0.5  0.1  0.05
0.7  0.2  0.01 
;
run;

You enter the following 2 statements:

 

options cmplib = work.cmpar;

 

%let rc = %sysfunc(array2array(have, want));

 

The output Data Set is:

Obs 	trans1 	trans2 	trans3
1 	0.2 	0.7 	0.500
2 	0.3 	0.9 	0.623
3 	0.5 	0.6 	0.150
4 	0.7 	0.9 	0.210

You use the first statement at the beginning of the program. The function can be used any number of times within the current SAS Session. The programmer can be requested to save to a permanent Library to use the function infinite number of times.

 

Kind regards,

DATAsp

 

rhapsody
Calcite | Level 5

I suppose the last lines are not complete?

 

options cmplib = work.cmpar;

 

%let rc = array2array(have, want);

 

They should be written in a datastep or something? All you do now is defining a macro variable using the %let statement

 

 

Patrick
Opal | Level 21

@rhapsody 

I like your rational for using FCMP to create a function library in a DIS context.

I believe though that using FCMP is not the ideal approach for the problem at hand. The main reason is: You need to create a set of new variables and SAS variables need to be declared during compilation time. So I don't believe that's gonna work here.

In a DIS context: You could implement something dynamic via a SAS macro within a custom transformation. That would also allow for the re-usability and central code management you're after.

 

KachiM
Rhodochrosite | Level 12

Hi Rhapsody

 

It is my bad. I did not use %sysfunc(). It must be:

 

%let rc = %sysfunc(array2array(have, want));

 

This is equivalent to your Y = f(X). Here I used rc = f(X,Y).

 

 

rhapsody
Calcite | Level 5

When I run your code I get the error

 

ERROR: Unable to open data set "WORK.have" in function READ_ARRAY.

ERROR: Second parameter passed to DYNAMIC_ARRAY must be a positive number.

ERROR: Error reported in function 'READ_ARRAY' in statement number 3 at line 8 column 1.

The statement was:

0 (8:1) rc = READ_ARRAY( inds="have", theta=(ndim=1, dim1=1 [.,...]) )

ERROR: An illegal argument is used in the function call in statement number 5 at line 10 column

1.

The statement was:

0 (10:1) cols = DIM2( theta=(ndim=1, dim1=1 [.,...]) )

ERROR: A TO value is missing in the DO statement in statement number 8 at line 13 column 1.

The statement was:

0 (13:1) do j = 1 to cols - 1;

INFO: Created Data Set want with 1 Rows and . Cols.

WARNING: Argument 2 to function PUTN referenced by the %SYSFUNC or %QSYSFUNC macro function is

out of range.

KachiM
Rhodochrosite | Level 12

ERROR: Unable to open data set "WORK.have" in function READ_ARRAY.

 

This ERROR may be due to the absence of HAVE in WORK Library. Please check it. All other Errors may be the result of the above Error.

KachiM
Rhodochrosite | Level 12

Rhapsody,

 

On Thursday you came with an ERROR message for not finding the WORK.HAVE Data Set. I replied you to check the presence of HAVE Data Set in WORK Library. What did you find? 

 

Please share your experience. 

 

DATAsp

rhapsody
Calcite | Level 5

I just ran your code again. I've attached an exact copy of that code to show you. It's this one

 

data have;

input theta1-theta3;

datalines;

 

0.2 0.5 0

0.3 0.6 0.023

0.5 0.1 0.05

0.7 0.2 0.01

;

run;

options cmplib = work.cmpar;

%let rc = %sysfunc(array2array(have, want));

 

 

 

Then I get the foloowing message in the log

 

1 data have;

2 input theta1-theta3;

3 datalines;

NOTE: The data set WORK.HAVE has 4 observations and 3 variables.

NOTE: Compressing data set WORK.HAVE increased size by 100.00 percent.

Compressed is 2 pages; un-compressed would require 1 pages.

NOTE: DATA statement used (Total process time):

real time 0.17 seconds

cpu time 0.00 seconds

 

8 ;

9 run;

10

11 options cmplib = work.cmpar;

12

13 %let rc = %sysfunc(array2array(have, want));

WARNING: No CMP or C functions found in library work.cmpar.

ERROR: The ARRAY2ARRAY function referenced in the %SYSFUNC or %QSYSFUNC macro function is not

found.

WARNING: Argument 2 to function PUTN referenced by the %SYSFUNC or %QSYSFUNC macro function is

out of range.

 

rhapsody
Calcite | Level 5

Hello! Sorry. It works now. and I just accepted your solution. Thank you very much 🙂

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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
  • 31 replies
  • 3740 views
  • 4 likes
  • 6 in conversation