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

Hello:

 

I have some questions from the program below.

 

1. The code for 'put(count(name),5.-L)',  what are 5. and L represent for?

2. I am trying to identify the length of all the columns and use them in the call symput statement,  I know I was NOT doing in the correct way.  However, I don't know how.  Please advice to fix it.  Thanks.

 

data work.ds1;

input a s d z $ x $ c $;

datalines;

1 . 3 qwe . yui

. . . . . hjk

3 . 3 zxc . .

;

 

%let lib=WORK;

%let mem=DS1;

proc sql noprint;

select name, put(count(name),5.-L) into :clist separated by ' ' , :charct

from dictionary.columns

where libname=upcase("&lib") and memname=upcase("&mem") and type='char';

quit;

%put &clist;

 

data truefalse;

array char(*) $ &clist;

array c_allmiss (&charct) $ (&charct*'true');

set &mem end=done;

len=length(name);

do i=1 to dim(c_allmiss);

if char(i) ne ' ' then c_allmiss(i)='false';

end;

if done then do;

cnt=0;

do i= 1 to dim(c_allmiss);

if c_allmiss(i) ='true' then do;

cnt+1;

call symput('var'||put(cnt, len-l),vname(char(i)));

                                                          ---

                                                          85

                                                          76

ERROR 85-322: Expecting a format name.

ERROR 76-322: Syntax error, statement will be ignored.

 

end;

end;

end;

run;

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

@ybz12003 wrote:

But I still don't understand what is the purpose for using " 5. - L " ?  Where is Name from? 


So in your original code you had this line.

select name, put(count(name),5.-L) into :clist separated by ' ' , :charct

The 5. is the format and the -L is format modifier that says to Left align the value. So when the number being PUT() is less than 100,000 the spaces are at the end instead of the beginning.  The reason it was used was to a create macro variable that did not have spaces in the beginning.  As I said before that is not really how you should do this.  You could instead use the TRIMMED keyword.

select name, count(name)
  into :clist separated by ' ' , :charct trimmed

Or even better just use the automatic macro variable SQLOBS which will have the count.

select name  into :clist separated by ' '  .... ;
%let charct=&sqlobs;

Later in the program there appears to be another attempt to perhaps use the -L format modifier:

call symput('var'||put(cnt, len-l),vname(char(i)));

But that use is syntacticly incorrect. The PUT function wants a format specification , not an expression. Even if you change to the PUTN() function which takes the format specification from a character variable or expression you still wouldn't want to use the varaible LEN as it has no meaning.  You could just hard code it to something that is long enough for any possible value of CNT, like the 5. in the SQL statement. 

call symput('var'||put(cnt, 5.-L),vname(char(i)));

But if you were trying to do this NOW you wouldn't use that syntax.  Use the CATS() function to append a numberic suffix onto a base name.  

 

call symputx(cats('var',cnt),vname(char(i)));

If you wanted a numeric suffix that was more than 12 digits long ( that is a number larger than 999,999,999,999) then you could use the PUT() or PUTN() function.  So since your prefix, VAR, is three characters long and a SAS name can only be 32 characters long so you could support a numeric suffix of up to 29 digits (if SAS could actually represent a number that large distinctly).

call symputx(cats('var',put(cnt,29.)),vname(char(i)));

View solution in original post

9 REPLIES 9
Reeza
Super User

Can you please comment your code so we understand what you think it's doing/should be doing?

ybz12003
Rhodochrosite | Level 12

Remove the Blank in all of the Charater columns

WarrenKuhfeld
Rhodochrosite | Level 12

5. is a format

-L means left justified.

 

call symput('var'||put(cnt, len-l),vname(char(i)));

 

causes an error, because you did not specify a format.  Using PUT and || in this kind of context (even if it were used correctly) strikes me as old fashioned.  This is an easier way to create a name from a string and an integer:

 

call symputx(cats('var',cnt),vname(char(i)));

ballardw
Super User

@ybz12003 wrote:

Hello:

 

I have some questions from the program below.

 

1. The code for 'put(count(name),5.-L)',  what are 5. and L represent for?

 

 


The 5. , and the period is important, is the Format used to put the numeric value, short hand for a F5,0 format meaning fixed 5 positions output with none in the decimal positions and the L says to justify the output left in the 5 characters.

If you use the code below:

data have;
  str= put (15, f5.0)  ;
  str2= put (15, f5.0 -L)  ;
  l1= length(str);
  l2= length(str2);
run;

you will note that the two STR variables have different lengths, L1 and L2. The first will have leading blanks as part of the value the the second not.

 

 

 

If I understand the approach you are needing you need a different array to hold the values of the lengths of each of the individual variables, the same number of elements as the names. BTW, since there is a Function named CHAR I would suggest not useing it as an array name as you might get unexpected results.

 

Also you need to use a different function to examine the value of a variable when you have the name in an array:

data truefalse;
   Set &lib..&mem ;
   array _c_(*) $ &clist;
   array len (&charct);
   retain Len: ;

   do i= 1 to dim(_c_);
      Len[i]= length(vvalue(_c_[i]));
   end;
run;

I left out the c_allmiss as I had no idea how you were meaning to use it. NOTE that you will need to be concerned with the "char" array that that NO variable in the list also has the name of the array. Array char(*) $ this char another var; will generate an error of recursive name. So if &clist has Char then you get the error.

 

Tom
Super User Tom
Super User

This SQL block in your code

proc sql noprint;
select name, put(count(name),5.-L) into :clist separated by ' ' , :charct
from dictionary.columns
where libname=upcase("&lib") and memname=upcase("&mem") and type='char';
quit;

Is probably trying to do what this code does more clearly and accurately.  Using the %UPCASE() macro funciton instead of the UPCASE() function will allow SAS to use the indexes for the LIBNAME and MEMNAME fields to avoid searching the metadata for all open libraries.  Using the automatic macro varaible SQLOBS to store the count will both eliminate any leading/trailing spaces and also accurately count when there are no character variables in the dataset.

proc sql noprint;
select name
  into :clist separated by ' '
  from dictionary.columns
  where libname=%upcase("&lib")
    and memname=%upcase("&mem")
    and type='char'
;
%let charct=&sqlobs;
quit;

It is not at all clear what the data step is trying to do.  It looks like you want to make a series of macro variables that contain the names of all of the character variables that are totally empty.  Why would you want that in a series of macro varibles instead of just in one macro variable, like you did in the first step?

 

And if the whole purpose is to get that list then you can skip the first step completely and do the whole job in the single data step.

Use the _CHARACTER_ variable list in the ARRAY definition to find all of the character variables. You will need to create a character variable that will be used to hold the generated list at the end so define it first, before the SET statement, and that way your array will always have at least one entry.  Make your flag variables numeric and temporary and then you can just used a fixed size for the array that is larger than anything you would ever see in a real dataset.

data _null_;
  length _namelist_ $32767 ;
  set &mem end=done;
  array _char_ _character_ ;
  array _flag_ (8000) _temporary_ ;
  do i=2 to dim(_char_);
    if not missing(_char_(i))' then _flag_(i)=1;
  end;
  if done then do;
    do i=2 to dim(_char_);
      if _flag_(i) then _namelist_ = catx(' ',_namelist_,vname(_char_(i)));
    end;
    call symputx('emptyvars',_namelist_);
    call symputx('nempty',countw(_namelist_));
  end;
run;
Astounding
PROC Star

You've already gotten an explanation of the easy parts.

 

The missing piece ... this should probably work but you will have to test it because I'm not sure about some of the details ... is to swtich from PUT to PUTN.  The PUT function requires that you hard-code a format.  The PUTN function expects that the format will be an expression such as the variable named LEN.

 

While that may eliminate syntax errors, the end result is still somewhat far-fetched.  Why should the name of a macro variable depend on the length of a character variable in the data set?  What if you have two character variables with the same length?  You can't use a single macro variable to hold the names of both.  In short, eliminating syntax errors will not eliminate all the problems.  Start with planning what you would like the end result to be.  Then we can talk about how to program it.

 

EDITED:

 

Perhaps what you are looking to do is the opposite.  Store a macro variable where the name of the macro variable matches the name of the DATA step variable, and the value of the macro variable is the length of the DATA step variable.  That can be done:

 

call symputx(vname(char{i}), length(vname(char{i})));

 

By switching to SYMPUTX instead of SYMPUT, the value of the macro variable has all leading and trailing blanks removed automatically.

 

However, it is much simpler to do that without so much macro language.  Create a data set (not a set of macro variables) from dictionary.columns.  For each observation in that data set, simply use:

 

call symputx(name, length);

 

I'm pretty sure those are the right variable names from DICTIONARY.COLUMNS, but I would need to verify that.

ybz12003
Rhodochrosite | Level 12

But I still don't understand what is the purpose for using " 5. - L " ?  Where is Name from? 

Tom
Super User Tom
Super User

@ybz12003 wrote:

But I still don't understand what is the purpose for using " 5. - L " ?  Where is Name from? 


So in your original code you had this line.

select name, put(count(name),5.-L) into :clist separated by ' ' , :charct

The 5. is the format and the -L is format modifier that says to Left align the value. So when the number being PUT() is less than 100,000 the spaces are at the end instead of the beginning.  The reason it was used was to a create macro variable that did not have spaces in the beginning.  As I said before that is not really how you should do this.  You could instead use the TRIMMED keyword.

select name, count(name)
  into :clist separated by ' ' , :charct trimmed

Or even better just use the automatic macro variable SQLOBS which will have the count.

select name  into :clist separated by ' '  .... ;
%let charct=&sqlobs;

Later in the program there appears to be another attempt to perhaps use the -L format modifier:

call symput('var'||put(cnt, len-l),vname(char(i)));

But that use is syntacticly incorrect. The PUT function wants a format specification , not an expression. Even if you change to the PUTN() function which takes the format specification from a character variable or expression you still wouldn't want to use the varaible LEN as it has no meaning.  You could just hard code it to something that is long enough for any possible value of CNT, like the 5. in the SQL statement. 

call symput('var'||put(cnt, 5.-L),vname(char(i)));

But if you were trying to do this NOW you wouldn't use that syntax.  Use the CATS() function to append a numberic suffix onto a base name.  

 

call symputx(cats('var',cnt),vname(char(i)));

If you wanted a numeric suffix that was more than 12 digits long ( that is a number larger than 999,999,999,999) then you could use the PUT() or PUTN() function.  So since your prefix, VAR, is three characters long and a SAS name can only be 32 characters long so you could support a numeric suffix of up to 29 digits (if SAS could actually represent a number that large distinctly).

call symputx(cats('var',put(cnt,29.)),vname(char(i)));
ybz12003
Rhodochrosite | Level 12

Thank you so much for everyone's comprehensive and thorough explanation, sharing your expert opinions.   I am grateful for all of your heart-melted kindness help! 🙂

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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
  • 9 replies
  • 2730 views
  • 5 likes
  • 6 in conversation