I got 2 problems regarding the SAS date using macros. To make it more complicated I am stuck with 2 specific macros that i need to use(its part of the puzzle that I try to solve).
The macro that I need to use are:
%let id=741852
%let month=January February March April May June July August September October November December
The output that I need to generate is the grade rsults for students in different dicipline. Only by changing the ID of the student the output has to be updated all by itself.
The information related to the date are only needed in the Title of my Output. my code at the moment is as follow:
Title1 "Grade for &firstname &lastname;
Tilte2 "Bithtdate : &bday;
Title3 "ID :&id"
title5 "As of &sysdate, the grades are:"
To create the bday variable I used the a function since i had the info in my data set:
CALL SYMPUTX('bday',Birth_date)
At the moment my output title 2 and 4 are as follow:
Birtdate:12556
As of 17NOV12, the grades are:
How can I use the macro &month to have both title read as follow: Birthdate: 10 Janurary 2012 and As of 15 November 2012, the grade are as follow:
(**The date may seems wrong but im working in french and days come before the month)
I tought of the %SCAN fonction but it wont udate the month if I cange the ID. plz help :)
It's not clear to me what exactly you are trying to accomplish, but here is an example of something similar. I set the locale to French to show how the date is formatted.
data a;
length firstname lastname $20;
input id firstname $ lastname $ grade birthday :date9. ;
datalines;
741852 Mary Jones 92.3 01Jan1980
654654 Chuck Berry 76.9 02Mar1983
823983 Michael Jordan 81.2 04Apr1965
;
run;
options locale=FR;
%macro printinfo(id, ds);
data _null_;
set &ds;
where id=&id;
put "-----------------------------------";
put " Grade for: " firstname lastname;
put " Birthday : " birthday nldate.;
put " ID : " id;
put " As of &sysdate., the grade is: " grade;
put "-----------------------------------";
put " ";
run;
%mend;
option nonotes;
%printinfo(741852,a);
%printinfo(654654,a);
option notes;
Here is the log output
-----------------------------------
Grade for: Mary Jones
Birthday : 01 janvier 1980
ID : 741852
As of 20NOV12, the grade is: 92.3
-----------------------------------
7299 %printinfo(654654,a);
-----------------------------------
Grade for: Chuck Berry
Birthday : 02 mars 1983
ID : 654654
As of 20NOV12, the grade is: 76.9
-----------------------------------
Without changing your other code, try these two title statements:
title2 "Birthdate: %qleft(%sysfunc(putn(&bday,worddatx.)))";
title5 "As of %qleft(%sysfunc(putn(%sysfunc(today()),worddatx.))) the grades are:";
Basically, your first macro variable bday needs to be formatted using the WORDDATX format. Also, you should use the system function TODAY() to get the current system date so you can format it as you want.
The %SYSFUNC macro function lets you execute other SAS functions, in this case PUTN and TODAY(). The %QLEFT macro function trims leading blanks.
Related
I'm formatting dates between day format YYYY.MM.DD and Monthly YYYY.MM and having some issues recasting data back and forth. Per Shock and Awe, I understand that the YYYY.MM format needs to be an integer however I'm having issues casting it correctly. Presently, I'm casting as a string to truncate the text and assign railing type indicator "m" but it returns an empty field.
I also have been looking at the Library as well .qdate.q_ but there doesn't seem to be a format for this.
I've read Shock and Awe here which makes complete sense Basic Data Types – Atoms
id seg scen prod SegmentStartDate iMonthCount fcast StartDate
-----------------------------------------------------------------------------------
AAA 1 PLAN XXX 2014.08.01 1 238.3821 2014.08m
AAA 1 PLAN XXX 2014.08.01 1 235.1754 2014.08m
AAA 1 PLAN XXX 2014.08.01 1 232.0119 2014.08m
...
tblMonthly: update StartDate: `$((-3_'string SegmentStartDate),'("m")) from tblMonthly; //formats as sym
tblMonthly: update StartDate: "I"$'string StartDate from tblMonthly; //fails to recast
Thanks in advance
You can cast directly to months like so:
tblMonthly: update StartDate:"m"$SegmentStartDate from tblMonthly
I have three columns with date formatted differently in SAS:
12 june 2017 00:15 - full date
2016 - only year
12 - only month
I Need to change the format of date and subtract after the dates to get results in the number of months.
for instance, "12 June 2017 00:15" - December 2016 = 7
how to do it?
As you have probably already found, there isn't a ready-made SAS date informat that will correctly handle your full date field, so you'll need to write a bit of custom logic to convert it before doing your calculation. date9. is the closest matching format I could find:
data example;
fulldate = '12 june 2017 00:15';
year = 2016;
month = 12;
/* Convert string to date9 format and input */
fulldate_num = input(
cats(
scan(fulldate,1),
substr(scan(fulldate,2,' '),1,3),
scan(fulldate,3)
), date9.
);
/* Calculate difference in months */
monthdiff = intck('month', mdy(month,1,year), fulldate_num);
run;
Convert the "full date" field to a SAS date value.
Convert the combo of year and month to a SAS date value, too.
Use the INTCK function to find the difference in months.
For example:
data dates ;
input dt $18. yy mm ;
mm_diff = intck ("mon", input (cats (yy, mm), yymmn6.), input (dt, anydtdte12.)) ;
put mm_diff= ;
cards ;
12 june 2017 00:15 2016 12
11 june 2018 00:15 2017 3
;
run ;
The log will print:
mm_diff=6
mm_diff=15
As a side note, the statement "there isn't a ready-made SAS date informat that will correctly handle your full date field" made elsewhere in this thread is incorrect. As the program snippet above shows, the ANYDTDTEw. informat handles it with aplomb. It's just incumbent upon the programmer to supply a sufficient informat width W. Above, it is selected as W=12. If you're reluctant to guess and/or count, just use ANYDTDTE32.
Regards,
Paul Dorfman
Assuming that you have three numeric variables and the first one contains valid SAS datetime values you should first convert both to valid SAS date values. You can then use the INTCK() function to count months.
nmonths = intck('month',datepart(VAR1),mdy(VAR3,1,VAR2));
I'm new to SAS and trying to do the next:
I have two tables (users & reviews) connected through user_id.
I've merged both via user_id.
There are two variables I want to use:
"date" (of the review. Saved as format MMDDYY10.)
"elite" (year a user was elite, saved as a character)
Elite has different forms (examples):
empty cell
2012,2015,2017
2012:2017
How do I check if a user was elite when he wrote the review?
Thanks in advance,
Peyo
This covers the case when a year is not present in the text of elite but is included in a range:
data test_cases;
input date yymmdd10. + 1 elite $32.;
format date yymmdd10.;
infile cards missover;
cards;
2012-01-01 2012,2015,2017
2012-01-01 2012:2017
2013-01-01 2012:2017
2012-01-01
2011-01-01 2012,2015,2017
2011-01-01 2012:2017
;
run;
data example;
set test_cases;
article_year = put(year(date),4.);
if index(elite,':') = 0 then elite_flag = index(elite,article_year);
else elite_flag = substr(elite,1,4) <= article_year <= substr(elite,6,9);
run;
You can use the YEAR function to extract the year from date, then use the PUT function to convert it to character and finally the INDEX function to search the elite list:
if index(elite,put(year(date),4.)) then ...
The condition will be true when the index function founds the year of date within the elite variable.
Here is my current formula:
if left ({Command.NextDueDate},7) = "2016-01" then "January'16"
else if left ({Command.NextDueDate},7) = "2016-02" then "February'16"
...
else if left ({Command.NextDueDate},7) = "2017-01" then "January'17"
I take date Strings from my database formatted like 2016-01-05, 2016-01-06...2017-01-01. I use these as columns.
I want to look for the year (16), then month (01), then for next column look for year (16) again and month (2). Then I'd like to interpret the month as shortened month names, like Jan or Feb. Finally I'd want to join the data back together. So in the end it will work like this:
16 + 01 becomes Jan16
16 + 02 becomes Feb16
17 + 01 becomes Jan17
How can I do this without manually entering an if-else clause for each month of each year?
No need to hard code it - Use this formula to get the month and year:
MonthName(Month(CDateTime({Command.NextDueDate}))) +
Right(Left({Command.NextDueDate},4),2);
Don't use if/else statements when you can get the information through clever use of formulas. There are a few ways you can make Ankur's formula even better:
To get the short Month name, use: MonthName(Month(CDateTime({Command.NextDueDate})),True) The True at the end shortens the name to "Jan", "Feb", "Mar" and so on.
To get the Year, use Year(CDateTime({Command.NextDueDate}))
Combine the two formulas and it does all the work for you:
MonthName(Month(CDateTime({Command.NextDueDate})),True) +
Year(CDateTime({Command.NextDueDate}))
I'd like to assign a person's name to a number based on a range rather than an explicit number. It's possible to do this using formats, but as I have the names in a dataset I'd prefer to avoid the manual process of writing the proc format.
data names;
input low high name $;
datalines;
1 10 John
11 20 Paul
21 30 George
31 40 Ringo
;
data numbers;
input number;
datalines;
33
21
17
5
;
The desired output is:
data output;
input number name $;
datalines;
33 Ringo
21 George
17 Paul
5 John
;
Thanks for any help.
You can do it like this using PROC SQL:
proc sql;
create table output as
select numbers.number, names.name
from numbers left join names
on numbers.number ge names.low
and numbers.number le names.high
;
quit;
One handy feature of proc format is the ability to use a data set to create the format, instead of typing it in by hand. Your scenario seems like a perfect scenario for this feature.
In the example you give, a few small changes to the "names" data set will put it in a form that can be read by proc format.
For example, if I modify the names data set like so..
data names;
retain fmtname "names" type "N";
input start end label $;
datalines;
1 10 John
11 20 Paul
21 30 George
31 40 Ringo
;
I can then issue this command to build the format based on it.
proc format cntlin=names;run;
Now I can use this format just like you would with any other format. For example, to create a new column that contains the desired "name" based on the number, you could do this:
data numbers;
input number;
number_formatted=put(number,names.);
datalines;
33
21
17
5
;
Here is what the output would look like:
number_
number formatted
33 Ringo
21 George
17 Paul
5 John
Update to address question:
There isn't much difference in coding needed to read from a text file. We just need to set it up so that the output data set has the particular variable names that proc format expects (fmtname, type, start, end , and label).
For example, if I have an external comma-seperated file called "names.csv" that looks like this:
1,10,John
11,20,Paul
21,30,George
31,40,Ringo
Then I simply can change the code that creates the "names" data set so that it looks like this:
data names;
retain fmtname "names" type "N";
infile "<path to file>/names.csv" dsd;
input start end label $;
run;
Now I can run proc format with the cntlin option like I did before:
proc format cntlin=names;run;
I think SQL is more succinct indeed, but if you aren't big fan of it and the numbers come in known increments, you may try something like:
data ranges;
set names;
do number = low to high; /* by ... */
output;
end;
proc sort;
by number;
run;
data output;
merge ranges
numbers ( in = innum )
;
by number;
keep number name;
if innum;
run;
Again, it requires numbers to come in predetermined increments, e.g. integers.