I use the following to fill in null values for numeric variables with 0, but this does it for date variables as well. How can I fill in null values only for non-date numeric variables?
data mydataset;
set mydataset;
array myarray _numeric_;
do over myarray;
if myarray=. then myarray=0;
end;
run;
There is an excellent post on SAS Communities about determining if a variable is a date or not. It's available here. Your question isn't trivial, as you shouldn't forget about user-defined formats which can also behave like date formats (proc format). Let's suppose that all your date variables are of one format.
data test;
format x y z datetime8.;
x = .;
y = .;
z = .;
run;
%macro get_vars_format(lib, tab, fmt);
proc sql noprint;
select name into :names separated by ' '
from sashelp.vcolumn
where libname = "&lib." and
memname = "&tab." and
format eq "&fmt.";
quit;
%put Names: &names.;
data work.test;
set &lib..&tab.;
%let i = 1;
%let name = %scan(&names., &i., %str( ));
%do %while(&name. ne );
if &name. eq . then &name = 0;
output;
%let i = %eval(&i + 1);
%let name = %scan(&names., &i., %str( ));
%end;
run;
%mend get_vars_format;
%get_vars_format(lib=WORK, tab=TEST, fmt=DATETIME8.);
This macro takes three arguments:'
library name,
data set name,
and desired format name.
I am saving all the variables names into a macro-variable (they are separated by a space sign) and in a next step, I am iterating this in a loop (note i and name macro-variables). For every row, if a value of any variable with a given format is equal to . then replace it with 0. Note that if a variable has a date format, 0 will be treated as a Jan 01, 1960, as that's the first day in SAS date value convention.
Related
I have a bunch of datetime formatted values in my dateset that started in the format 01JAN2020:00:00:00. I've managed to condense this down using dtdate9. but that changes the format to 01JAN20. I'm looking to change the format to 01/01/2020, any ideas?
As far as I know, there is no off the shelf SAS format that maps a datetime value to construct mm/dd/yyyy.
You can create a user defined function with FCMP that computes the transform and specify that function in a custom format.
Example:
proc fcmp outlib=work.functions.formats;
function dtmmddyy(datetime) $10;
return (put(datepart(datetime), mmddyy10.));
endsub;
run;
proc format;
value dtmmddyy
low-high = [dtmmddyy()]
;
run;
options cmplib=(work.functions);
data _null_;
dt = input ('01JAN2020:00:00:00', datetime20.);
put
dt= dtdate9. /
dt= dtmmddyy. /
;
run;
Log
dt=01JAN2020
dt=01/01/2020
You can write a custom datetime type format like this
proc format;
picture dtdmy (default=10)
low - high = '%D/%0m/%Y' (datatype=datetime)
;
run;
data _null_;
dt = '01JAN2020:00:00:00'dt;
put dt = dtdate9.;
put dt = dtdmy.;
run;
You can also control it via the length on a date time format, especially if it's only for display, at least if it's MDY. Not sure about DMY formats.
data demo;
have = dhms('01Jan2020'd, 4, 3, 0);
x = have;
format x mdyampm10. have datetime22.;
run;
1 01JAN2020:04:03:00 1/1/20
I read a number for example 42000 that I need to convert into date in the format DD/MM/YY in a macro, not a Load statement.
In the macro the number is stored in vInput and the new value will be stored in vDate. Neither
vDatet = Date(vInput, 'DD/MM/YYYY')
or
vDate = date(vInput#(Date,'DD/MM/YYYY'))
works. Any ideas?
You can use the script below to get the content of vInput variable, evaluate the Qlik expression and save the result in the vDate variable.
Using the script below if vInput is equal to 42000 the vDate will be equal to 27/12/2014
sub ChangeDate
set vInputVar = ActiveDocument.Variables("vInput")
vInput = vInputVar.GetContent.String
changedDate = ActiveDocument.Evaluate("=Date(" & vInput & ",'DD/MM/YYYY')")
set vDateVar = ActiveDocument.Variables("vDate")
vDateVar.SetContent changedDate , true
end sub
I would like to count the number of integers in my variable, the same way that %countw counts the number of words in a variable.
Example:
%let test = 'aaa' 'bbb';
%let ntest = %sysfunc(countw(&test.));
ntest = 2..
My question is how to do this for integers?
Now I have:
%let test2 = 12, 13, 14;
How to get ntest = 3?
How can I get the number of items in &test2.?
I apologize if this is ridiculously simple and I just missed the documentation.
Because there are commas as delimiters in your macro variable value, you can use the %superq function to prevent these commas to be interpreted as parameter separators in the macro call. And since your values are separated by both commas and spaces, you can specify both in a %str function, for the same reason as previously.
%let test2= 12, 13, 14;
%let ntest=%sysfunc(countw(%superq(test2),%str(, )));
Hi I am trying to call a macro for each row in the data set using the code below
proc sql;
select cats('%run_procreg(name=',name,',month=',month,')') into :macrocalllist
separated by ' ' from dataset_a;
quit;
¯ocalllist;
I am getting the 'variable maximum length' error:
SAS length of the value of the macro variable MACROCALLLIST (65540)
exceeds the maximum length (65534). The value has been
truncated to 65534 characters.
because of the number of rows in the data set. Could you suggest a work-around?
Thank you,
CALL EXECUTE is one option. It allows you to generate a series of macro calls using data from a dataset, without storing the macro invocations in a macro variable.
For example:
%macro testprint(data=,obs=);
proc print data=&data (obs=&obs);
run;
%mend testprint;
data _null_;
input datasetname $13. obs;
call execute('%nrstr(%testprint(data='||datasetname
||',obs='||put(obs,1.)
||'))'
);
cards;
sashelp.shoes 3
sashelp.class 5
;
And the log will show:
NOTE: CALL EXECUTE generated line.
131 ;
1 + %testprint(data=sashelp.shoes,obs=3)
NOTE: There were 3 observations read from the data set SASHELP.SHOES.
2 + %testprint(data=sashelp.class,obs=5)
NOTE: There were 5 observations read from the data set SASHELP.CLASS.
I have only 1 line without line feed (CRLF CRLF), the linefeed is a string of 4 characters, in this example is "#A$3" I don't need dlm for now, and I need to import it from a external file (/files/Example.txt)
JOSH 30JUL1984 1011 SPANISH#A$3RACHEL 29OCT1986 1013 MATH#A$3JOHNATHAN 05JAN1985 1015 chemistry
I need this line into 3 lines:
JOSH 30JUL1984 1011 SPANISH
RACHEL 29OCT1986 1013 MATH
JOHNATHAN 05JAN1985 1015 chemistry
How I can do that in SAS?
*Added: Your solutions are working with this example, but i have a issue, a line that contains more than the maximum length allowed for the line(32,767 bytes),
For example this line in the above exercise contains 5,000 records.
Is it possible?
Use the DLMSTR= option on the infile statement -- this will specify "#A$3" as the delimiter. Then use ## on the input statement to tell SAS to look for more records on the same line.
data test;
infile "/files/Example.txt" dsd dlmstr='#A$3';
informat var $255.;
input var $ ##;
run;
With your example, you will get a data set with 3 records with 1 variable containing the strings you are looking for.
Adjust the length of var as needed.
You could do something like this:
First import the file as a single row (be sure to adjust the length):
DATA WORK.IMPORTED_DATA;
INFILE "/files/Example.txt" TRUNCOVER;
LENGTH Column1 $ 255;
INPUT #1 Column1 $255.;
RUN;
Then parse imported data into variables using a data step:
data result (keep=var1-var4);
set WORK.IMPORTED_DATA;
delim = '#A$3';
end = 1;
begin = 1;
do while (end > 0);
end = find(Column1, delim, begin);
row = substr(Column1, begin, end - begin);
var1 = scan(row, 1);
var2 = scan(row, 2);
var3 = scan(row, 3);
var4 = scan(row, 4);
begin = end + length(delim);
output;
end;
run;
Try this in data step by viewing #A$3 as a multi-character delimiter:
data want (keep=subject);
infile 'C:\sasdata\test.txt';
input;
length line $4500 subject $80;
line=tranwrd(_infile_,"#A$3",'!');
do i=1 by 1 while (scan(line,i,'!') ^= ' ');
subject=scan(line,i,'!');
output;
end;
run;
_infile_ gives the current row that is being read in the data step. I converted the multi-character delimiter #A$2 into a single-character delimiter. tranwrd() can replace a sub-string inside a string. And then use the delimiter inside the scan() function.
Also, if you want to break the values up into separate variables, just scan some more. E.g. put something like B = scan(subject,2); into do loop and data want (keep= A B C D);. Cheers.