Issue:
I'm looking to reverse the order of all columns in a sas dataset. Should I achieve this by first transposing and then using a loop to reverse the order of the columns? This is my logic...
Step One:
data pre_transpose;
set sashelp.class;
*set &&dataset&i.. ;
_row_ + 1; * Unique identifier ;
length _charvar_ $20; * Create 1 character variable ;
run;
Step One Output:
Step Two: Do I Reverse Columns Here?
proc transpose data = pre_transpose out = middle (where = (lowcase(_name_) ne '_row_'));
by _row_;
var _all_;
quit;
Step Two Output:
EDIT:
I have attempted this:
/* use proc sql to create a macro variable for column names */
proc sql noprint;
select varnum, nliteral(name)
into :varlist, :varlist separated by ' '
from dictionary.columns
where libname = 'WORK' and memname = 'all_character'
order by varnum desc;
quit;
/* Use retain to maintain format */
data reverse_columns;
retain &varlist.;
set all_character;
run;
But I did not achieve the results I was looking for - the column order is not reversed.
You just need to get the list of variable names. One way is to use the metadata. Do if your dataset is member HAVE in libref WORK then you could use this to get the list of variable names into a single macro variable.
proc sql noprint;
select varnum , nliteral(name)
into :varlist, :varlist separated by ' '
from dictionary.columns
where libname='WORK' and memname='HAVE'
order by varnum desc
;
quit;
You could then use the macro variable in a data step like this.
data want ;
retain &varlist ;
set have ;
run;
Note that the value of libname and memname in DICTIONARY.COLUMNS is in uppercase only.
Related
Suppose that I have the following list
proc sql;
select name into: list separated by ' ' from dataset;
quit;
and I want to keep the elements of this list in my dataset2. The datastep
data dataset2;
set dataset2;
if name in &list.;
run;
does not work. How should I modify the first proc sql so that the datastep makes sense? Thanks in advance!
This assumes you're looking to filter values within a column, not the number of columns.
If the value is numeric this is what you need:
data dataset3;
set dataset2;
if name not in (&list.);
run;
If the value is character then this is what you need:
proc sql;
select quote(name) into: list separated by ' ' from dataset;
quit;
data dataset3;
set dataset2;
if name not in (&list.);
run;
I'm kinda new to SAS.
I have 2 datasets: set1 and set2.
I'd like to get a list of variables that's in set2 but not in set1.
I know I can easily see them by doing proc compare and then listvar,
however, i wish to copy&paste the whole list of different variables instead of copying one by one from the report generated.
i want either a macro variable containing a list of all different variables separated by space, or printing out all variables in plain texts that I can easily copy everything.
proc contents data=set1 out=cols1;
proc contents data=set2 out=cols2;
data common;
merge cols1 (in=a) cols2 (in=b);
by name;
if not a and b;
keep name;
run;
proc sql;
select name into :commoncols separated by ','
from work.common;
quit;
Get the list of variable names and then compare the lists.
Conceptually the simplest way see what is in a dataset is to use proc contents.
proc contents data=set1 noprint out=content1 ; run;
proc contents data=set2 noprint out=content2 ; run;
Now you just need to find the names that are in one and not the other.
An easy way is with PROC SQL set operations.
proc sql ;
create table in1_not_in2 as
select name from content1
where upcase(name) not in (select upcase(name) from content2)
;
create table in2_not_in1 as
select name from content2
where upcase(name) not in (select upcase(name) from content1)
;
quit;
You could also push the lists into macro variables instead of datasets.
proc sql noprint ;
select name from content1
into :in1_not_in2 separated by ' '
where upcase(name) not in (select upcase(name) from content2)
;
select name from content2
into :in2_not_in1 separated by ' '
where upcase(name) not in (select upcase(name) from content1)
;
quit;
Then you could use the macro variables to generate other code.
data both;
set set1(drop=&in1_not_in2) set2(drop=&in2_not_in1) ;
run;
What is the simplest way to put all column names of a given dataset into a macro variable?
Another question, how should I put names of all "numeric" columns into one macro variable and names of all character columns into another macro variable?
Depending on what you're doing you can also reference all character variables with _character_ and numerical variables with _numeric_.
*doesn't do anything but illustrates how to reference all variables of a specific type;
data want;
set sashelp.class;
array _c1(*) _character_;
array _n1(*) _numeric_;
run;
If you need to do this for a number of tables then you could try defining a macro for that purpose, e.g.:
%macro get_cols(lib,mem,mvar,type);
%global &mvar;
proc sql noprint;
select
name
into
:&mvar separated by ' '
from
dictionary.columns
where
libname eq upcase("&lib")
and memname eq upcase("&mem")
%if %upcase(&type) ne ALL %then
and upcase(type) eq upcase("&type");
;
quit;
%put &mvar = &&&mvar;
%mend get_cols;
Then you could call it, as required:
/* character variables */
%get_cols(sashelp,class,cvars,char);
/* numeric variables */
%get_cols(sashelp,class,nvars,num);
/* all variables */
%get_cols(sashelp,class,vars,all);
using proc sql into clause with dictionary.columns is one of easiest way to make macro variables for your purpose. Below query uses sashelp.class, you can use your table name and libname instead
/* for all variables*/
Proc sql noprint;
select name into :macvar1 separated by ',' from
dictionary.columns
where upcase(memname) = 'CLASS'
and upcase(libname) = 'SASHELP';
/* for numeric variables*/
Proc sql noprint;
select name into :macvar2 separated by ',' from
dictionary.columns
where upcase(memname) = 'CLASS'
and upcase(libname) = 'SASHELP'
and upcase(type) = 'NUM';
/* for character variables*/
Proc sql noprint;
select name into :macvar3 separated by ',' from
dictionary.columns
where upcase(memname) = 'CLASS'
and upcase(libname) = 'SASHELP'
and upcase(type) = 'CHAR';
%put value of all variables is &macvar1;
%put value of numeric variables is &macvar2;
%put value of character variables is &macvar3;
I am trying to write a PROC SQL query in SAS to determine maximum of many columns starting with a particular letter (say RF*). The existing proc means statement which i have goes like this.
proc means data = input_table nway noprint missing;
var age x y z RF: ST: ;
class a b c;
output out = output_table (drop = _type_ _freq_) max=;
run;
Where the columns RF: refers to all columns starting with RF and likewise for ST. I was wondering if there is something similar in PROC SQL, which i can use?
Thanks!
Dynamic SQL is indeed the way to go with this, if you must use SQL. The good news is that you can do it all in one proc sql call using only one macro variable, e.g.:
proc sql noprint;
select catx(' ','max(',name,') as',name) into :MAX_LIST separated by ','
from dictionary.columns
where libname = 'SASHELP'
and memname = 'CLASS'
and type = 'num'
/*eq: is not available in proc sql in my version of SAS, but we can use substr to match partial variable names*/
and upcase(substr(name,1,1)) in ('A','W') /*Match all numeric vars that have names starting with A or W*/
;
create table want as select SEX, &MAX_LIST
from sashelp.class
group by SEX;
quit;
I have like 500 columns of dataset, and I want to rearrange all the variables in an alphabetical order. How can I do that in any other way than using retain statement before set statement?
You can generate the list of variable names dynamically, and create a new dataset using PROC SQL.
proc sql ;
select name into :VARLIST separated by ', '
from dictionary.columns
where libname = 'SASHELP'
and memname = 'CLASS'
order by name ;
quit ;
proc sql ;
create table ordered as
select &VARLIST
from sashelp.class ;
quit ;