I used the sashelp library data sets as an example
proc datasets library=sashelp;
contents
data=company
out=work.var_names;
quit;
run;
Now i using proc sql into to store the variable names
proc sql noprint;
select name into: varname separated by ' '
from var_names;
quit;
%put &varname;
Now i would like to print the variables name on the log
data newdata (drop=i);
array temp{*} &varname;
do i=1 to dim(temp);
put temp{i};
end;
run;
While it print . instead of the variable names.
Thanks
log:
337 %put &varname;
DEPTHEAD JOB1 LEVEL1 LEVEL2 LEVEL3 LEVEL4 LEVEL5 N
While i want
DEPTHEAD
JOB1
LEVEL1
LEVEL2
LEVEL3
LEVEL4
LEVEL5
N
Suppose the variables in the company are name, salary, the varname macro variable is "name salary". When the last segments were run, it is acually like this:
array temp{*} name salary;
The name and salary are set to default numeric variables with missing value '.'.
Why not just put the variables in the var_names:
data _null_;
set var_names;
put name;
run;
I don't really understand the premise of this question, as you're doing something waaaaay more complicated than you need to.
First of all, you don't need proc contents.
proc sql noprint;
select name into :varname separated by ' '
from dictionary.columns
where libname='SASHELP' and memname='COMPANY';
quit;
Second, this is a much easier way to get the variable names, even assuming you don't want them as %put.
data _null_;
set sashelp.company;
array _names &varname.;
do _i = 1 to dim(_names);
_v = vname(_names[_i]);
put _v=;
end;
run;
I don't see what value a _temporary_ array has here at all. vname will happily give you the names of the variables in the array, if that's what you're after. Honestly this seems like a contrived example though as Reese and others pointed out.
Not sure why your method is not working, but for now you can use scan function to extract the elements, code is pretty much self explanatory, let me know if you don't get it
data _null_;
array temp{*} &varname;
do i=1 to dim(temp);
temp_1 = scan("&varname.",i);
put temp_1;
end;
second_element=scan("&varname.",2);
put second_element=;
run;
EDIT:
Your method
array temp{*} &varname
which eventually translates to
array temp{*} DEPTHEAD JOB1 LEVEL1 LEVEL2 LEVEL3 LEVEL4 LEVEL5 N;
is not working because by doing that array variable names are defined and not the values are initialized
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000201956.htm
Look for this statement
array simple{3} red green yellow;
If you want to do it your way then you have to do something like this
data _null_;
array tempar{8} $ tempar1-tempar8 ("DEPTHEAD" "JOB1" "LEVEL1" "LEVEL2" "LEVEL3" "LEVEL4" "LEVEL5" "N");
do i=1 to dim(tempar);
put tempar(i)=;
end;
run;
Notice first I have defined the individual elements of the array - tempar1-tempar8 and have defined it char using $ and then I have intialized the values that too in quotes.
EDIT2:
Tested
If you absolutely can't deviate from your approach, below is the code(which is less efficient than what I have suggested above)
proc datasets library=sashelp;
contents
data=company
out=work.var_names;
quit;
run;
proc sql noprint;
select quote(compress(name)) into: varname separated by ' '
from var_names;
select count(name) into: Numofelements from var_names;
quit;
%put %quote(&varname.);
%put &Numofelements;
data _null_;
array tempar{%sysfunc(compress(&Numofelements.))} $ tempar1-tempar%sysfunc(compress(&Numofelements.)) (%quote(&varname.));
do i=1 to dim(tempar);
put tempar(i)=;
end;
run;
This seems like a long way to do something that may be accomplished more easily, if you explained why you wanted to do it :).
Here's a solution that uses a stores the values using quotes and then puts them into a temporary array.
proc sql noprint;
select quote(trim(name)) into: varname separated by ' '
from var_names;
quit;
%put &varname;
data new data;
array var_names{&sqlobs} $ _temporary_ (&varname);
put var_names(2);
do i=1 to dim(var_names);
put var_names(i);
end;
run;
Here's a second solution that uses your dataset, since I find it a good practice to avoid macros wherever possible.
data newdata;
set var_names;
if _n_=2 then put name;
run;
And a note that you can use the
_all_, _numeric_ or _character_
shortcut to reference variable lists. An array can only have one type, so I assume that all variables are numeric or character. As well as the fact that SASHELP.VCOLUMN contains various metadata about all SAS library and tables.
Related
I have a dataset with X number of categorical variables for a given record. I would like to somehow turn this dataset into a new dataset with dummy variables, but I want to have one command / macro that will take the dataset and make the dummy variables for all variables in the dataset.
I also dont want to specify the name of each variable, because I could have a dataset with 50 variables so it would be too cumbersome to have to specify each variable name.
Lets say I have a table like this, and I want the resulting table, with the above conditions that I want a single command or single macro without specifying each individual variable:
You can use PROC GLMSELECT to generate the design matrix, which is what you are asking for.
data test;
input id v1 $ v2 $ v3 $ ;
datalines;
1 A A A
2 B B B
3 C C C
4 A B C
5 B A A
6 C B A
;
proc glmselect data=test outdesign(fullmodel)=test_design noprint ;
class v1 -- v3;
model id = v1 -- v3 /selection=none noint;
run;
You can use the -- to specify all variables between the first and last. Notice I don't have to type v2. So if you know first and the last, you can get want you want easily.
I prefer GLMMOD myself. One note, if you can, CLASS variables are usually a better way to go, but not supported by all PROCS.
/*Run model within PROC GLMMOD for it to create design matrix
Include all variables that might be in the model*/
proc glmmod data=sashelp.class outdesign=want outparm=p;
class sex age;
model weight=sex age height;
run;
/*Create rename statement automatically
THIS WILL NOT WORK IF YOUR VARIABLE NAMES WILL END UP OVER 32 CHARS*/
data p;
set p;
if _n_=1 and effname='Intercept' then
var='Col1=Intercept';
else
var=catt("Col", _colnum_, "=", catx("_", effname, vvaluex(effname)));
run;
proc sql ;
select var into :rename_list separated by " " from p;
quit;
/*Rename variables*/
proc datasets library=work nodetails nolist;
modify want;
rename &rename_list;
run;
quit;
proc print data=want;
run;
Originally from here and the post has links to several other methods.
https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-dummy-variables-Categorical-Variables/ta-p/308484
Here is a worked example using your simple three observation dataset and a modified version of the PROC GLMMOD method posted by #Reeza
First let's make a sample dataset with a long character ID variable. We will introduce a numeric ROW variable that we can later use to merge the design matrix back with the input data.
data have;
input id :$21. education_lvl $ income_lvl $ ;
row+1;
datalines;
1 A A
2 B B
3 C C
;
You could set the list of variables into a macro variable since we will need to use it in multiple places.
%let varlist=education_lvl income_lvl;
Use PROC GLMMOD to generate the design matrix and the parameter list that we will later use to generate user friendly variable names.
proc glmmod data=have outdesign=design outparm=parm noprint;
class &varlist;
model row=&varlist / noint ;
run;
Now let's use the parameter list to generate rename statement to a temporary text file.
filename code temp;
data _null_;
set parm end=eof;
length rename $65 ;
rename = catx('=',cats('col',_colnum_),catx('_',effname,of &varlist));
file code ;
if _n_=1 then put 'rename ' ;
put #3 rename ;
if eof then put ';' ;
run;
Now let's merge back with the input data and rename the variables in the design matrix.
data want;
merge have design;
by row ;
%inc code / source2;
run;
I'm working on code that will change the coding of several hundred variables stored as 1/0 or Y/N in numeric 1 or 0. Because this will need to be in a flexible process, I am writing a macro to do so. The only issue that I am have with the macro is I am unable to pass the SAS column names to the macro to work. Thoughts?
%Macro Test(S,E);
%Array(A,&S.-&E.);
%MEnd;
data subset;
set dataset);
%Test(v1,v20)
run;
SAS supports variable lists. Macro parameters are just text strings. So as long as you use the macro variable value in a place where SAS supports variable lists there is no problem passing a variable list to a macro. For example here a simplistic macro to make an array statement.
%macro array(name,varlist);
array &name &varlist ;
%mend;
Which you could then use in the middle of a data step like this.
data want;
set have ;
%array(binary,var1-var20 a--d male education);
do over binary; binary=binary in ('Y','1','T'); end;
run;
The difficult part is if you want to convert variables from character to numeric then you will need to rename them. This will make it difficult to use variable lists (x1-x5 or vara -- vard). You can solve that problem with a little extra logic to convert the variable lists into a list of individual names. For example you can use PROC TRANSPOSE to create a dataset with the variable names that match your list.
proc transpose data=&inds(obs=0) out=_names ;
var &varlist;
run;
You could then use this dataset to generate code or generate a list of the individual variable names.
proc sql noprint ;
select name into :varlist2 separated by ' ' from _names;
quit;
A list of all variable names is stored in the dictionary.columns dataset. You can access it and store the names as a list that you can then loop through:
proc sql noprint;
select name into: list_of_names
separated by " "
from dictionary.columns where memname = upcase("your_dataset");
quit;
%put &list_of_names.;
I need to add a prefix to certain column names in a table. The names at the moment range from _15 to _49 and I would simply like to add the prefix N to give N_15,...,N_49 etc.
I tried the following:
proc sql noprint;
select cats(name,'=','N',name)
into :prefixlist
separated by ' '
from dictionary.columns
where libname = 'WORK' and memname = 'Freq_nais_2006_2010';
quit;
However this does nothing as I just get the message no rows were selected in the log output. What must I change?
Your particular issue is that the WHERE clause is not being fulfilled by any rows, likely because of this: and memname = 'Freq_nais_2006_2010'. Member names are typically capitalized internally in SAS, even if they're not capitalized in your code.
Otherwise your code looks fine, and you should be able to use that &prefixlist. in a PROC DATASETS or data step rename statement. I generally suggest the PROC SQL method as it's easier to customize to specify the variables you want to rename, but of course if you're renaming all of the variables in the dataset the macro works as well.
You're very close:
proc sql noprint;
select cats(name,'=','N',name)
into :prefixlist
separated by ' '
from dictionary.columns
where libname = 'WORK' and memname = 'FREQ_NAIS_2006_2010'
/* and substr(NAME,1,1) = '_' - can add condition on column name pattern */;
quit;
proc datasets lib=WORK nolist nodetails;
modify FREQ_NAIS_2006_2010;
rename
&prefixlist
;
quit;
Changed separator to space for use in PROC DATASETS; MODIFY ... RENAME ... statement.
Side note: the datastep variant answers rewrite the dataset completly, which is ineffective and dangerous for real world usage (big tables), also much less clear on what you're doing.
I managed to find the following code from the sas website (http://support.sas.com/kb/37/433.html):
%macro vars(dsn,chr,out);
%let dsid=%sysfunc(open(&dsn));
%let n=%sysfunc(attrn(&dsid,nvars));
data &out;
set &dsn(rename=(
%do i = 2 %to &n;
%let var=%sysfunc(varname(&dsid,&i));
&var=&chr&var
%end;));
%let rc=%sysfunc(close(&dsid));
run;
%mend vars;
%vars(Freq_nais_2006_2010,N,Freq_nais_2006_2010);
You can just list them in the rename statement in your code, no need for macros or anything else. Though the best idea is avoid it in the first place if you can. See the rename statement below.
data test;
array test(20) _1-_20;
do i=1 to 20;
test(i)=rand('normal', 20);
end;
run;
data test2;
set test;
rename _1-_20 = n_1-n_20;
run;
I need to run a macro that does a transpose for many variables (and creates a table for each one), orders the columns names, which are numeric, but also adds as a prefix the variable's name (which is a string).
I have a macro in SAS to perform a transpose with different variables as var in the transpose. The code is:
%macro transponer(var);
proc transpose data=labo2.A_svm_200711_200806
out=labo2.D_tr_&var.0;
var &var;
id mes;
by cid;
run;
/*......more code.....*/
select cats(name, '=', &var, name)
into :prefijolista
separated by ' '
from dictionary.columns
where libname='LABO2' and memname= cats('D_TR_',upcase(&var))
and name like '_20%';
quit;
%put &prefijolista;
%mend;
Since mes is numeric I wanted to order the variable, that's why I didn't introduce the "prefix &var" in the proc transpose but instead I did it after the retain (that was useful to order the columns).
The problem starts when I try to introduce the prefix (after the ordering).
Since one of the variables' name is for example "monto", I get the following error (because it is the var variable in the transpose and it's not a column name in the transposed table):
The following columns were not found in the contributing tables:
monto.
My next step would be:
proc datasets library=labo2;
modify D_tr_&var.0;
rename &prefijolista;
quit;
But I cant do it untill I get the previous one done.
So I don't know how to order the columns after the transpose and also add the prefix.
How can I solve this?
Thanks!
You need to rename the columns using something like PROC DATASETS.
proc datasets lib=work nolist;
modify myDataSet;
rename old_col_name = new_col_name;
run;
quit;
A documentation example is available in the Base SAS guide under the doc for PROC DATASETS. It is available online at: http://support.sas.com/documentation/cdl/en/proc/67327/HTML/default/viewer.htm#n0mfav25learpan1lerk79jsp30n.htm
The problem was that &var inside the cats function inside a macro hast to use
" "
Also you could use
sysfunc(cats(D_TR, &a)
So finally the code will remain like:
%let a = %upcase(&var);
%put &a;
%let b=%sysfunc(cats(D_TR_,&a));
%put &b;
proc sql;
select cats(name, '=', "&var" , name)
into :prefijolista
separated by ' '
from dictionary.columns
where libname='LABO2' and memname= "&b"
and name like '_20%';
quit;
%put &prefijolista;
%put "&b";
PROC datasets library=LABO2;
modify &b;
rename &prefijolista;
quit;
%put "ult" &b;
Not very straightforward, but worked. :)
I have a SAS dataset which has 20 character variables, all of which are names (e.g. Adam, Bob, Cathy etc..)
I would like a dynamic code to create variables called Adam_ref, Bob_ref etc.. which will work even if there a different dataset with different names (i.e. don't want to manually define each variable).
So far my approach has been to use proc contents to get all variable names and then use a macro to create macro variables Adam_ref, Bob_ref etc..
How do I create actual variables within the dataset from here? Do I need a different approach?
proc contents data=work.names
out=contents noprint;
run;
proc sort data = contents; by varnum; run;
data contents1;
set contents;
Name_Ref = compress(Name||"_Ref");
call symput (NAME, NAME_Ref);
%put _user_;
run;
If you want to create an empty dataset that has variables named like some values you have in a macro variables you could do something like this.
Save the values into macro variables that are named by some pattern, like v1, v2 ...
proc sql;
select compress(Name||"_Ref") into :v1-:v20 from contents;
quit;
If you don't know how many values there are, you have to count them first, I assumed there are only 20 of them.
Then, if all your variables are character variables of length 100, you create a dataset like this:
%macro create_dataset;
data want;
length %do i=1 %to 20; &&v&i $100 %end;
;
stop;
run;
%mend;
%create_dataset; run;
This is how you can do it if you have the values in macro variable, there is probably a better way to do it in general.
If you don't want to create an empty dataset but only change the variable names, you can do it like this:
proc sql;
select name into :v1-:v20 from contents;
quit;
%macro rename_dataset;
data new_names;
set have(rename=(%do i=1 %to 20; &&v&i = &&v&i.._ref %end;));
run;
%mend;
%rename_dataset; run;
You can use PROC TRANSPOSE with an ID statement.
This step creates an example dataset:
data names;
harry="sally";
dick="gordon";
joe="schmoe";
run;
This step is essentially a copy of your step above that produces a dataset of column names. I will reuse the dataset namerefs throughout.
proc contents data=names out=namerefs noprint;
run;
This step adds the "_Refs" to the names defined before and drops everything else. The variable "name" comes from the column attributes of the dataset output by PROC CONTENTS.
data namerefs;
set namerefs (keep=name);
name=compress(name||"_Ref");
run;
This step produces an empty dataset with the desired columns. The variable "name" is again obtained by looking at column attributes. You might get a harmless warning in the GUI if you try to view the dataset, but you can otherwise use it as you wish and you can confirm that it has the desired output.
proc transpose out=namerefs(drop=_name_) data=namerefs;
id name;
run;
Here is another approach which requires less coding. It does not require running proc contents, does not require knowing the number of variables, nor creating a macro function. It also can be extended to do some additional things.
Step 1 is to use built-in dictionary views to get the desired variable names. The appropriate view for this is dictionary.columns, which has alias of sashelp.vcolumn. The dictionary libref can be used only in proc sql, while th sashelp alias can be used anywhere. I tend to use sashelp alias since I work in windows with DMS and can always interactively view the sashelp library.
proc sql;
select compress(Name||"_Ref") into :name_list
separated by ' '
from sashelp.vcolumn
where libname = 'WORK'
and memname = 'NAMES';
quit;
This produces a space delimited macro vaiable with the desired names.
Step 2 To build the empty data set then this code will work:
Data New ;
length &name_list ;
run ;
You can avoid assuming lengths or create populated dataset with new variable names by using a slightly more complicated select statement.
For example
select compress(Name)||"_Ref $")||compress(put(length,best.))
into :name_list
separated by ' '
will generate a macro variable which retains the previous length for each variable. This will work with no changes to step 2 above.
To create populated data set for use with rename dataset option, replace the select statement as follows:
select compress(Name)||"= "||compress(_Ref")
into :name_list
separated by ' '
Then replace the Step 2 code with the following:
Data New ;
set names (rename = ( &name_list)) ;
run ;