Milliseconds in SAS - sas

like current time/date, is it possible to get the current milliseconds in 6 digits?
I am using datetime and date to get the timestamp n date
call symput('currdatets',datetime());
call symput('currdate',date());

data _null_;
call symput('nowAsDateTime',put(datetime(), datetime26.6));
call symput('nowMs',1E6 * datetime()); * but this might generate overflow *;
call symput('nowMsOnly',1E6 * mod(datetime(), 1));
run;
%put _USER_;

data _null_;
dt = datetime() ;
ms = mod(dt,1) ; /* remainder when divided by 1 */
put dt= ms= ;
run ;

Related

How to subtract date/time values from different rows in the same column in sas

I got this values in my table and I need to know how can I subtract them creating another column with the results.
19FEB2018:14:24:43.00
23MAR2018:12:57:58.00
28MAR2018:15:37:37.00
29JUN2018:10:30:33.00
29JUN2018:13:43:07.00
What I need is:
1- 0h
2- (23MAR2018:12:57:58.00 - 19FEB2018:14:24:43.00)
3- (...)
Just use the DIF() function.
data have;
input dt datetime.;
format dt datetime22.2 ;
cards;
19FEB2018:14:24:43.00
23MAR2018:12:57:58.00
28MAR2018:15:37:37.00
29JUN2018:10:30:33.00
29JUN2018:13:43:07.00
;
data want;
set have ;
diff = dif(dt);
format diff hhmm12.2 ;
run;

Automate check for number of distinct values SAS

Looking to automate some checks and print some warnings to a log file. I think I've gotten the general idea but I'm having problems generalising the checks.
For example, I have two datasets my_data1 and my_data2. I wish to print a warning if nobs_my_data2 < nobs_my_data1. Additionally, I wish to print a warning if the number of distinct values of the variable n in my_data2 is less than 11.
Some dummy data and an attempt of the first check:
%LET N = 1000;
DATA my_data1(keep = i u x n);
a = -1;
b = 1;
max = 10;
do i = 1 to &N - 100;
u = rand("Uniform"); /* decimal values in (0,1) */
x = a + (b-a) * u; /* decimal values in (a,b) */
n = floor((1 + max) * u); /* integer values in 0..max */
OUTPUT;
END;
RUN;
DATA my_data2(keep = i u x n);
a = -1;
b = 1;
max = 10;
do i = 1 to &N;
u = rand("Uniform"); /* decimal values in (0,1) */
x = a + (b-a) * u; /* decimal values in (a,b) */
n = floor((1 + max) * u); /* integer values in 0..max */
OUTPUT;
END;
RUN;
DATA _NULL_;
FILE "\\filepath\log.txt" MOD;
SET my_data1 NOBS = NOBS1 my_data2 NOBS = NOBS2 END = END;
IF END = 1 THEN DO;
PUT "HERE'S A HEADER LINE";
END;
IF NOBS1 > NOBS2 AND END = 1 THEN DO;
PUT "WARNING!";
END;
IF END = 1 THEN DO;
PUT "HERE'S A FOOTER LINE";
END;
RUN;
How can I set up the check for the number of distinct values of n in my_data2?
A proc sql way to do it -
%macro nobsprint(tab1,tab2);
options nonotes; *suppresses all notes;
proc sql;
select count(*) into:nobs&tab1. from &tab1.;
select count(*) into:nobs&tab2. from &tab2.;
select count(distinct n) into:distn&tab2. from &tab2.;
quit;
%if &&nobs&tab2. < &&nobs&tab1. %then %put |WARNING! &tab2. has less recs than &tab1.|;
%if &&distn&tab2. < 11 %then %put |WARNING! distinct VAR n count in &tab2. less than 11|;
options notes; *overrides the previous option;
%mend nobsprint;
%nobsprint(my_data1,my_data2);
This would break if you have to specify libnames with the datasets due to the .. And, you can use proc printto log to print it to a file.
For your other part as to just print the %put use the above as a call -
filename mylog temp;
proc printto log=mylog; run;
options nomprint nomlogic;
%nobsprint(my_data1,my_data2);
proc printto; run;
This won't print any erroneous text to SAS log other than your custom warnings.
#samkart provided perhaps the most direct, easily understood way to compare the obs counts. Another consideration is performance. You can get them without reading the entire data set if your data set has millions of obs.
One method is to use nobs= option in the set statement like you did in your code, but you unnecessarily read the data sets. The following will get the counts and compare them without reading all of the observations.
62 data _null_;
63 if nobs1 ne nobs2 then putlog 'WARNING: Obs counts do not match.';
64 stop;
65 set sashelp.cars nobs=nobs1;
66 set sashelp.class nobs=nobs2;
67 run;
WARNING: Obs counts do not match.
Another option is to get the counts from sashelp.vtable or dictionary.tables. Note that you can only query dictionary.tables with proc sql.

Date difference using sas macro

I have two macro variables value.
a=20150501
b=20160530
I want to create a new macro variable that will display the number of months between a and b.
Same logic as Allan's response but using a data step to simplify the code...
%let a = 20150501;
%let b = 20160530;
data _null_;
a = input(put("&a", 8.), yymmdd8.);
b = input(put("&b", 8.), yymmdd8.);
diff = intck("month", a, b);
call symputx("diff", put(diff, best.));
run;
%put &diff;
12
See documentation for intck for alternative ways to calculate the difference in months.
You can use the INTCK function for that - see documentation
%let a=20150501;
%let b=20160530;
/**
* first - convert to date values
* (DATE_A=01MAY2015 DATE_B=30MAY2016)
*/
%let date_a=%sysfunc(putn(%sysfunc(
INPUTN(%sysfunc(PUTN(&a, Z8.)), YYMMDD8.)
), date9.));
%let date_b=%sysfunc(putn(%sysfunc(
INPUTN(%sysfunc(PUTN(&b, Z8.)), YYMMDD8.)
), date9.));
%put &=date_a &=date_b;
/**
* Now calculate difference using INTCK
* (DIFF=12)
*/
%let diff=%sysfunc(intck(MONTH,"&date_a"d,"&date_b"d));
%put &=diff;
You can use the intck() function to get the number of months difference.
If you are performing a calculation such as age, or tenure, then be sure to use the 'continuous' parameter of intck().
If you simply need to know that there is 1 month difference between the 31-May and the 01-Jun, then use the 'discrete' (default) parameter.
%let a=20150531;
%let b=20150601;
* CONVERT TO SAS DATES;
%let date1 = %sysfunc(inputn(&a,yymmdd8.));
%let date2 = %sysfunc(inputn(&b,yymmdd8.));
* CALCULATE # DISCRETE MONTHS DIFFERENCE;
%let discrete_months = %sysfunc(intck(month, &date1, &date2, d));
%put &=discrete_months;
* CALCULATE # CONTINUOUS MONTHS DIFFERENCE;;
%let cont_months = %sysfunc(intck(month, &date1, &date2, c));
%put &=cont_months;
Output:
DISCRETE_MONTHS=1
CONT_MONTHS=0

Convert mm/dd/yy to MonthName DayNumber, YearNumber in SAS

I want to be able to convert an entire column of dates this way. For example, 01/01/2017 to January 1, 2017. I realize there is a convoluted way of doing this but I am not entirely sure how i'd approach that logically. Also, does there happen to be a SAS format that does this? Thanks.
There does happen to be a format you can use. Here is a worked example using test data:
data test;
input datestring $;
datalines;
01/01/2017
;
run;
Using input converts the string value into a SAS date, and then the put function is used to create a character variable holding the representation you are looking for:
data test2;
set test;
date_as_date = input(datestring,ddmmyy10.);
date_formatted = put(date_as_date,worddate20.);
run;
The number 20 is used to describe a length that is long enough to hold the full value, using a lower number may result in truncation, e.g.
date_formatted = put(date_as_date,worddate3.);
put date_formatted=;
dateformatted=Jan
In some cases, the desired date format may NOT exist (in this case, it does 'worddate20.'), but as an example...
You could either write a function-style macro to convert a SAS date to "monname + day, year" format, e.g.
%MACRO FULLMDY(DT) ;
catx(', ',catx(' ',put(&DT,monname.),put(&DT,day.)),put(&DT,year4.))
%MEND ;
data example1 ;
dt = '26jul2017'd ;
fulldate = %FULLMDY(dt) ;
run ;
Or, you could build a custom format, covering all the dates which may exist in your data, e.g.
data alldates ;
retain fmtname 'FULLMDY' type 'N' ;
do dt = '01jan1900'd to '01jan2100'd ;
mdy = catx(', ',catx(' ',put(dt,monname.),put(dt,day.)),put(dt,year4.)) ;
output ;
end ;
rename dt = start
mdy = label ;
run ;
proc format cntlin=alldates ; run ;
data example2 ;
dt = '26jul2017'd ;
format dt fullmdy. ;
run ;

Macro variables : Apparent symbolic reference not resolved

I have an issue with an unresolved macro variable in the following (part of a) macro:
DATA _NULL_;
SET TempVarFormat END=Last;
LENGTH FormatValues $10000;
RETAIN FormatValues;
IF &OnlyNumeric = 1 THEN
FormatValues = CATX(" ",FormatValues,STRIP(LookUpValue)||
" = "||CATQ("A",TRIM(LookupDescription)));
ELSE
FormatValues = CATX(" ",FormatValues,CATQ("A"
,LookUpValue)||" = "||CATQ("A"
,TRIM(LookupDescription)));
Test = STRIP(FormatValues);
PUT Test /* To test buildup of variable */;
IF Last THEN CALL SYMPUT('FormatValuesM',STRIP(FormatValues));
IF Last THEN CALL SYMPUT('DataCollectionFK',DataCollectionFK);
RUN;
/* Make format with PROC FORMAT*/
%IF &OnlyNumeric = 1 %THEN %DO;
PROC FORMAT LIB=WORK;
VALUE DC&DataCollectionFK.A&AttributeFK.Format &FormatValuesM;
RUN;
%END;
%ELSE %IF &OnlyNumeric = 0 %THEN %DO;
PROC FORMAT LIB=WORK;
VALUE $DC&DataCollectionFK.A&AttributeFK.Format &FormatValuesM;
RUN;
%END;
I get the following warning
Apparent symbolic reference FORMATVALUESM not resolved.
And if I look in the log &DataCollectionFK is resolved but &FormatValues is not.
PROC FORMAT LIB=WORK; VALUE DC170A570Format &FormatValuesM;
Could someone advice? It is driving me nuts.
I tested it also without the STRIP() function and replacing the CALL SYMPUT with PUT to see if the variable is assigned a value. This all works fine.
Log copy (as requested in comment)
4 +
DATA _NULL_; SET TempVarFormat END=Last; LENGTH
5 + FormatValues $10000; RETAIN FormatValues; IF 1 = 1 THEN FormatValues = CATX("
",FormatValues,STRIP(LookUpValue)|| " = "||CATQ("A",TRIM(LookupDescription))); ELSE
FormatValues = CATX(" ",FormatValues,CATQ("A" ,LookUpValue)||" = "||CATQ("A" ,TRIM
6 +(LookupDescription))); Test = STRIP(FormatValues); PUT Test ; IF Last THEN CALL
SYMPUT('DataCollectionFK',DataCollectionFK); IF Last THEN CALL SYMPUT('FormatValuesM',Test);
RUN;
NOTE: Numeric values have been converted to character values at the places given by:
(Line):(Column).
6:107
1 = "Ja"
1 = "Ja" 0 = "Nee"
NOTE: There were 2 observations read from the data set WORK.TEMPVARFORMAT.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
6 +
PROC FORMAT LIB=WORK; VALUE DC170A1483Format &FormatValuesM; RUN;;
NOTE: Format DC170A1483FORMAT is already on the library.
NOTE: Format DC170A1483FORMAT has been output.
NOTE: PROCEDURE FORMAT used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
MPRINT LOG
MPRINT(CONSTRUCTVARIABLEFORMAT): DATA TestDataSetFormat;
MPRINT(CONSTRUCTVARIABLEFORMAT): SET TempVarFormat END=Last;
MPRINT(CONSTRUCTVARIABLEFORMAT): LENGTH FormatValues $10000;
MPRINT(CONSTRUCTVARIABLEFORMAT): RETAIN FormatValues;
MPRINT(CONSTRUCTVARIABLEFORMAT): IF 1 = 1 THEN FormatValues = CATX("
",FormatValues,STRIP(LookUpValue)|| " = "||CATQ("A",TRIM(LookupDescription)));
MPRINT(CONSTRUCTVARIABLEFORMAT): ELSE FormatValues = CATX(" ",FormatValues,CATQ("A"
,LookUpValue)||" = "||CATQ("A" ,TRIM(LookupDescription)));
MPRINT(CONSTRUCTVARIABLEFORMAT): Test = STRIP(FormatValues);
MPRINT(CONSTRUCTVARIABLEFORMAT): PUT Test ;
MPRINT(CONSTRUCTVARIABLEFORMAT): IF Last THEN CALL
SYMPUT('DataCollectionFK',DataCollectionFK);
MPRINT(CONSTRUCTVARIABLEFORMAT): IF Last THEN CALL SYMPUT('FormatValuesM',Test);
MPRINT(CONSTRUCTVARIABLEFORMAT): RUN;
MPRINT(CONSTRUCTVARIABLEFORMAT): PROC FORMAT LIB=WORK;
WARNING: Apparent symbolic reference FORMATVALUESM not resolved.
MPRINT(CONSTRUCTVARIABLEFORMAT): VALUE DC170A1483Format &FormatValuesM;
MPRINT(CONSTRUCTVARIABLEFORMAT): RUN;
EDIT with some more attemps:
The problem lies in that the macro variable is not getting a value during the datastep, for some reason. Loading the macrovariable with an empty value before I run the macro, makes
that the script does not give an error. But the variable is resolved as an empty variable.
removing the IF Last THEN parts, also does not alter the outcome.
Surely it'll be easier/simpler to use the cntlin= option of PROC FORMAT to pass in a dataset containing the relevant format name, start, end, label values...
A simple example...
/* Create dummy format data */
data formats ;
fmtname = 'MYCHARFMT' ;
type = 'C' ;
do n1 = 'A','B','C','D','E' ;
start = n1 ;
label = repeat(n1,5) ;
output ;
end ;
fmtname = 'MYNUMFMT' ;
type = 'N' ;
do n2 = 1 to 5 ;
start = n2 ;
label = repeat(strip(n2),5) ;
output ;
end ;
drop n1 n2 ;
run ;
/* dummy data looks like this... */
fmtname type start label
MYCHARFMT C A AAAAAA
MYCHARFMT C B BBBBBB
MYCHARFMT C C CCCCCC
MYCHARFMT C D DDDDDD
MYCHARFMT C E EEEEEE
MYNUMFMT N 1 111111
MYNUMFMT N 2 222222
MYNUMFMT N 3 333333
MYNUMFMT N 4 444444
MYNUMFMT N 5 555555
/* Build formats from dataset */
proc format cntlin=formats library=work ; run ;
There are several other fields which can be defined in your format dataset to cater for low/high/missing values, ranges, etc.
See the SAS documentation > http://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#a002473464.htm