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

I need to output some array values that contain side calculations from a given data step to a separate file. It works right now using @ but the numbers are left justified.  I can use macros to use the format I need but the character length of the numbers would have to be known before the data step is executed.  each program has code that is one big data step, so I have to find the solution that works without extra data steps or proc statements.

 

these are always integers so I want to output them in n. format where n is the max character length of the number in standard form.  I have an array with these values but I dont know of a way to use those values to get a format. 

 
/* psudo code to iterate through array and print values.  I have code for arbitrary dimensional dataset, I 
it just has a lot of extra mess and the idea is the same.  */


data example;
array saved_length {3}  (3,2,1);  /*contains max length for v/c/f for this given array */
%let vlen=3; %let clen=2; %let flen=1; *macro version of above;


file mprint;
do i =1 to 10;
put
"v= " &arrayname{i, 1} &vlen.. " " /* is there a way to use saved_length{i}  */
"c= " &arrayname{i, 2} &clen.. " " /* value of x to get x. here? */ 
"f= " &arrayname{i, 3} &flen..; /* so I can replacing having to use &.len? */
end;
file LOG;
run; 

 

 

I would like output of the form like this so the columns are aligned right

v=     1 c=   1 f =1  

v=   21 c= 21 f= 2

v= 321 c= 21 f= 3

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

So you want to do something like:

put
  @2 "d1= " d1 2. 
  @9 "d2= " d2 2. 
  @16 "d3= " d3 2. 
  @23 "V= " ExARRAY(d1,d2,d3,1) 4.
  @30 "C= " ExARRAY(d1,d2,d3,2) 3.
  @38 "F= " ExARRAY(d1,d2,d3,3) 2.
;

But with the 2,9,16,23,30,38 and 2,2,2,4,3,2 sets of numbers varying?

First question is why isn't it good enough to just figure out the max possible widths and always use those?

Looks like they are integers since you are not printing any decimal part. The maximum integer SAS can represent exactly (without having holes in the sequence) use 16 digits.  So if you use 16,16,16 instead of 4,3,2 then any values in the EXARRAY will fit.

 

Assuming there is a good reason not to do that then just build a string variable and put that.

You haven't said how you are calculating the width to use but lets assume have done so and stored them in another array of length 3 called W.

put  @2 "d1= " d1 2.   @9 "d2= " d2 2.   @16 "d3= " d3 2.  @;
length string $200 ;
string = cat(' V= ',putn(ExARRAY[d1,d2,d3,1],cats(w[1],'.'))
            ,' C= ',putn(ExARRAY[d1,d2,d3,2],cats(w[2],'.'))
            ,' F= ',putn(ExARRAY[d1,d2,d3,3],cats(w[3],'.')))
;
string_len = length(string);
put string $varying200. string_len;

View solution in original post

12 REPLIES 12
Tom
Super User Tom
Super User

Why is what you posted not good enough?

weg
Obsidian | Level 7 weg
Obsidian | Level 7
I might have 10 of these arrays in a given data step, so that means tweaking the length for each one manually, or specifying super wide lengths which look kinda bad . Since I can get the max lengths for each column in the array stored in another one during the run, I am tantalizingly close to just being able to automate this print step entirely and it seems like it could be doable
ballardw
Super User

The F format may be all that you need

 

data example;
   file print;
   input x;
   put "x=" x f5. +1 "y=" x F3. +1 "z=" x f8.;
datalines;
1
11
111
;

Be aware that your output destination can cause things to "miss-align" because of proportional fonts like this window uses. See the two lines below. Both 6 digits but the second like very likely appears longer. So your output may look shifted for anything that does not use a fixed space font.

111111

000000

 

You may need to use another approach if appearance is more important than actual numbers of spaces, such as a report procedure.

weg
Obsidian | Level 7 weg
Obsidian | Level 7

Hi Ballard

The font spacing doesn't matter to me, just the number of spaces needs to be consistent.

 

my problem is replacing the f5. f3. etc in your example with the max length if I can.  like if the max length in the array is 3, then I would like f3. and if its 8 i would do f8. for that section. 

 

I have the max values stored, but I dont know how to turn an array value of 7 -> f7. for max length in your example.

 

if it doesn't seem possible to do its not the end of the world, I just figure with everything else we can do it seems like there should be something like put_integer(x, *numeric value*) to pass an integer as a numeric format.

 

 

ballardw
Super User

@weg wrote:

Hi Ballard

The font spacing doesn't matter to me, just the number of spaces needs to be consistent.

 

my problem is replacing the f5. f3. etc in your example with the max length if I can.  like if the max length in the array is 3, then I would like f3. and if its 8 i would do f8. for that section. 

 

I have the max values stored, but I dont know how to turn an array value of 7 -> f7. for max length in your example.

 

if it doesn't seem possible to do its not the end of the world, I just figure with everything else we can do it seems like there should be something like put_integer(x, *numeric value*) to pass an integer as a numeric format.

 

 


Macro variables are text. If you have one you can use it in the F format.

 

%let l1=5;
%let l2=3;
%let l3=8;
data example;
   file print;
   input x;
   put "x=" x f&L1.. +1 "y=" x F&L2.. +1 "z=" x f&L3..;
datalines;
1
11
111
;

You need 2 dots because the dot is the macro processor concatenate and/or end of macro value indicator. Since format references require a dot to generate F3. you need the dot in the macro reference: F&L1..

 

Second, "length" and numeric values needs to be discussed carefully in SAS. The "length" of numeric variable ranging from 3 to 8 is an expression of the number of bytes used to store the value and is only very loosely related to display. If you mean "number of digits" that is a better way to phrase things.

weg
Obsidian | Level 7 weg
Obsidian | Level 7
Which is what I do right now. if I have 4 arrays example1 - example4 of various dimensions, I have a macro to write them out like this:

%print_array(ArrayName=example1, dimnames= name1 name2 name3,vlen=5, clen=2, flen=1)
%print_array(ArrayName=example2, dimnames= name4 name5 ,vlen=7, clen=2, flen=1)
%print_array(ArrayName=example3, dimnames= name6,vlen=3, clen=2, flen=2)
%print_array(ArrayName=example4, dimnames= name7 name8 name8 name9 name10,vlen=,11 clen=4, flen=1)

but then I have to manually specify the print lengths for vlen, clen or flen each time so that it aligns right. I cant save the values from the data step to the macro,
because the arrays I'm printing are being created in that data step. If I can get it automated so I can get the formatting from looking at the array itself, I wont have to have those 3 extra arguments for format lengths.

Tom
Super User Tom
Super User

I still don't get what you are doing and how ARRAY's get into it.

If you need to find a width to use for a variable you probably just want to calculate the maximum width needed for any value.  For integers that is going to be the width needed for the maximum value (or the minimum if your numbers include negative values).

data have;
  input x @@ ;
cards;
1 1234 -34567 
;
proc sql noprint;
  select max(length(strip(put(x,32.)))) into :xlen trimmed
  from have 
  ;
quit;
data _null_;
  set have;
  put 'X=' x &xlen.. ;
run;

Results:

11    data _null_;
12      set have;
13      put 'X=' x &xlen.. ;
14    run;

X=     1
X=  1234
X=-34567

If you need to make the format width vary then perhaps you want to use PUTN() function to generate a string and then use $VARYING format to print it.

data have;
  input x w @@ ;
cards;
1 3 1234 5 -34567 7  
;
data _null_;
  set have;
  str = putn(x,cats(w,'.'));
  put 'X=' str $varying200. w ;
run;

Results:

30    data _null_;
31      set have;
32      str = putn(x,cats(w,'.'));
33      put 'X=' str $varying200. w ;
34    run;

X=  1
X= 1234
X= -34567

Note: The F format is just another name for the regular numeric format used in my code above.

weg
Obsidian | Level 7 weg
Obsidian | Level 7
@Tom wrote:

I still don't get what you are doing and how ARRAY's get into it.

I am writing the arrays to a file which are snapshots of key variables in the data process used for logging.   these are side calculations, IE I  alrady am outputting multiple other datasets in the data statement   I might need to output the same array multiple times at different stages and the lengths would be different so it is a lot easier to use mprint and put to output than to save them as a ton of datasets and output in another step, which is why I am trying to get a solution that works for the given data step.

 

here is another example of an array I would need to print that has multiple dimensions.  I got to print like 10-20 of these in a single data step, and they are not the main output which is why I really cant use multiple data or proc steps

 

file mprint;
put "ExARRAY(EX1,EX2,EX3,EX4)"; put "**************************"; do d1=1 to 2; do d2 =1 to 2; do d3 =1 to 7; put @2 "d1= " d1 2. @9 "d2= " d2 2. @16 "d3= " d3 2. @23 "V= " ExARRAY(d1,d2,d3,1) 4. @30 "C= " ExARRAY(d1,d2,d3,2) 3. @38 "F= " ExARRAY(d1,d2,d3,3) 2.; end; end; end;
file LOG;

I already can take the array name and variables as input: %print_array(array=Exarray, dimnames= ex1 ex2 ex3 ex4) and recreate this output code, including the @ placement.  What I cannot recreate is the formatting.  I have the lengths (for this example 2. 2. 2. 2. 4. 3. 2.) for each array as an integer in another array like in my example, but there is no way I know of in sas to make a format from that aside from macros, which doesnt work since they resolve before I can calculate the needed length for each array, which means I'm stuck doing what I currently do an manually specify the format lengths.

 

Tom
Super User Tom
Super User

So you want to do something like:

put
  @2 "d1= " d1 2. 
  @9 "d2= " d2 2. 
  @16 "d3= " d3 2. 
  @23 "V= " ExARRAY(d1,d2,d3,1) 4.
  @30 "C= " ExARRAY(d1,d2,d3,2) 3.
  @38 "F= " ExARRAY(d1,d2,d3,3) 2.
;

But with the 2,9,16,23,30,38 and 2,2,2,4,3,2 sets of numbers varying?

First question is why isn't it good enough to just figure out the max possible widths and always use those?

Looks like they are integers since you are not printing any decimal part. The maximum integer SAS can represent exactly (without having holes in the sequence) use 16 digits.  So if you use 16,16,16 instead of 4,3,2 then any values in the EXARRAY will fit.

 

Assuming there is a good reason not to do that then just build a string variable and put that.

You haven't said how you are calculating the width to use but lets assume have done so and stored them in another array of length 3 called W.

put  @2 "d1= " d1 2.   @9 "d2= " d2 2.   @16 "d3= " d3 2.  @;
length string $200 ;
string = cat(' V= ',putn(ExARRAY[d1,d2,d3,1],cats(w[1],'.'))
            ,' C= ',putn(ExARRAY[d1,d2,d3,2],cats(w[2],'.'))
            ,' F= ',putn(ExARRAY[d1,d2,d3,3],cats(w[3],'.')))
;
string_len = length(string);
put string $varying200. string_len;
weg
Obsidian | Level 7 weg
Obsidian | Level 7
This is perfect, thanks!
ballardw
Super User

What's wrong with a report procedure? You can specify alignment in most.

weg
Obsidian | Level 7 weg
Obsidian | Level 7
I have too many of these to create as another step, and they might need to be printed multiple times and there is still the main data processing going on.

I guess worst case I can do it manually or just do a super wide format and deal with the ugly whitespace.

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

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
  • 12 replies
  • 1786 views
  • 1 like
  • 3 in conversation