Hi Folks:
I'm trying to present 1-year survival by unit of month in the Table below. However, my attempt to modify a macro deisgnated to calculate annual to monthly rate is not successful and I have the column "range" produced with overlapped intervals. Desired output is shown in red. The second patient died before 12-months.
The macro %lexis is attached to this post and can be found here too: http://www.pauldickman.com/survival/sas/lexis.sas.
Could you please help me produce the desired output?
I greatly appreciate your time!
Thanks in advance.
data have;
input id age yydx year8594 dx exit d;
cards;
1 78 1978 0 6854 9365 1
21 76 1976 0 6063 6078 1
;
%include 'C:\lexis.sas';
%lexis (data=have, out=have, breaks = %str( 0 to 1 by 0.083333), origin = dx, entry = dx, exit = exit,
fail = d, scale = 30.4, right = right, risk = y, lrisk = ln_y, lint = length, cint = w, nint = fu);
data have; set have;
range=put(left,4.1) || ' - ' || left(put(right,4.1));
run;
proc print; run;
%LEXIS
%macro Lexis ( data = , /* Data set with original data, */
/* defaults to _last_ */
out = , /* Where to put the result, */
/* defaults to &data. */
entry = entry, /* Variable holding the entry date */
exit = exit, /* Variable holding the exit date */
fail = fail, /* Variable holding the exit status */
/* If any of the entry, exit or fail */
/* variables are missing the person is */
/* discarded from the computations. */
breaks = , /* Specification of the cutpoints on */
/* the transformed scale. */
/* Syntax as for a do statement. */
/* The ONLY Mandatory argument. */
cens = 0, /* Code for censoring (may be a variable) */
scale = 1, /* Factor to transform from the scale */
/* of entry and exit to the scale */
/* where breaks and risk are given */
origin = 0, /* Origin of the transformed scale */
risk = risk, /* Variable recieving the risk time */
lrisk = lrisk, /* Variable recieving the log(risk time) */
left = left, /* Variable recieving left endpoint of int */
other = , /* Other dataset statements to be used such */
/* as: %str( format var ddmmyy10. ; ) */
/* or: %str( label risk ="P-years" ; ) */
disc = discrd, /* Dataset holding discarded observations */
/*-------------------------------------------------------------*/
/* Variables for making life-tables and other housekeeping: */
/* These will only appear in the output dataset if given here */
/* The existence of these arguments are tested in the macro so */
/* they cannot have names that are also logical operators such */
/* as: or, and, eq, ne, le, lt, gt. */
/*-------------------------------------------------------------*/
right = , /* Variable recieving right endpoint of int */
lint = , /* Variable recieving interval length */
os_left = , /* Variable recieving left endpoint of int */
os_right = , /* Variable recieving right endpoint of int */
os_lint = , /* Variable recieving interval length */
/* - the latter three on original scale */
cint = , /* Variable recieving censoring indicator */
/* for the current input record */
nint = /* Variable recieving index of follow-up */
/* interval; */
);
%if &breaks.= %then %put ERROR: breaks MUST be specified. ;
%if &data. = %then %let data = &syslast. ;
%if &out. = %then %do ;
%let out=&data. ;
%put
NOTE: Output dataset not specified, input dataset %upcase(&data.) will be overwritten. ;
%end ;
data &disc. &out. ;
set &data. ;
if ( nmiss ( &entry., &exit., &fail., &origin. ) gt 0 )
then do ; output &disc. ;
goto next ;
end ;
* Labelling of variables ;
label &entry. = 'Entry into interval' ;
label &exit. = 'Exit from interval' ;
label &fail. = 'Failure indicator for interval' ;
label &risk. = 'Risktime in interval' ;
label &lrisk. = 'Natural log of risktime in interval' ;
label &left. = 'Left endpoint of interval (transformed scale)' ;
%if &right.^= %then label &right. = 'Right endpoint of interval (transformed scale)' ; ;
%if &lint.^= %then label &lint. = 'Interval width (transformed scale)' ; ;
%if &os_left.^= %then label &os_left. = 'Left endpoint of interval (original scale)' ; ;
%if &os_right.^= %then label &os_right. = 'Right endpoint of interval (original scale)' ; ;
%if &os_lint.^= %then label &os_lint. = 'Interval width (original scale)' ; ;
%if &cint.^= %then label &cint. = 'Indicator for censoring during the interval' ; ;
%if &nint.^= %then label &nint. = 'Sequential index for follow-up interval' ; ;
&other. ;
drop _entry_ _exit_ _fail_
_origin_ _break_
_cur_r _cur_l _int_r _int_l
_first_ _cint_ _nint_;
/*
Temporary variables in this macro:
_entry_ holds entry date on the transformed timescale
_exit_ holds exit date on the transformed timescale
_fail_ holds exit status
_break_ current cut-point
_origin_ origin of the time scale
_cur_l left endpoint of current risk interval
_cur_r right endpoint of current risk interval
_int_l left endpoint of current break interval
_int_r right endpoint of current break interval
_first_ indicator for processing of the first break interval
_cint_ indicator for censoring during the interval
_nint_ sequential index of interval
If a variable with any of these names appear in the input dataset it will
not be present in the output dataset.
*/
_origin_ = &origin. ;
_entry_ = ( &entry. - _origin_ ) / &scale. ;
_exit_ = ( &exit. - _origin_ ) / &scale. ;
_fail_ = &fail. ;
_cur_l = _entry_ ;
_first_ = 1 ;
do _break_ = &breaks. ;
if _first_ then do ;
_nint_=-1;
_cur_l = max ( _break_, _entry_ ) ;
_int_l = _break_ ;
end ;
_nint_ + 1;
_first_ = 0 ;
_int_r = _break_ ;
_cur_r = min ( _exit_, _break_ ) ;
if _cur_r gt _cur_l then do ;
/*
Endpoints of risk interval are put back on original scale.
If any of left or right are specified the corresponding endpoint
of the break-interval are output.
*/
&entry. = _cur_l * &scale. + _origin_ ;
&exit. = _cur_r * &scale. + _origin_ ;
&risk. = _cur_r - _cur_l ;
&lrisk. = log ( &risk. ) ;
&fail. = _fail_ * ( _exit_ eq _cur_r ) +
&cens. * ( _exit_ gt _cur_r ) ;
_cint_ = not( _fail_ ) * ( _exit_ eq _cur_r ) ;
%if &left.^= %then &left. = _int_l ; ;
%if &right.^= %then &right. = _int_r ; ;
%if &lint.^= %then &lint. = _int_r - _int_l ; ;
%if &os_left.^= %then &os_left. = _int_l * &scale. + _origin_ ; ;
%if &os_right.^= %then &os_right. = _int_r * &scale. + _origin_ ; ;
%if &os_lint.^= %then &os_lint. = ( _int_r - _int_l ) * &scale. ; ;
%if &cint.^= %then &cint. = _cint_ ; ;
%if &nint.^= %then &nint. = _nint_ ; ;
output &out. ;
end ;
_cur_l = max ( _entry_, _break_ ) ;
_int_l = _break_ ;
end ;
next: ;
run ;
%mend Lexis ;
I think PaigeMiller and I just fixed the problem under the new forum. Please let me know if any comments.
Thanks Reeza.
Below is the original macro for an annual rate. They used 365.4 for annual and I used 30.4 for monthly rate and modifying steps in breaks by 1/12=0.083333 and expending 1-10 to 1-12. According to the author's STATA tutorial, 'breaks' specify the cutpoints for the lifetable intervals as an ascending numlist commencing at zero. The cutpoints need not be integer nor equidistant but the units must be years, e.g., specify breaks(0(0.0833)5) for monthly intervals up to 5 years.
It will be great to hear from you back.
SAS totorial on the same approach:
http://www.pauldickman.com/survival/sas/relative_survival_using_sas.pdf
Dickman's STATA tutorial on the same macro
https://pdfs.semanticscholar.org/71e1/9adf869a8587ee5d03e0af2e5a3bdebcc42a.pdf
%lexis (data=&individ.,out=&individ., breaks = %str( 0 to 10 by 1 ), origin = dx,entry = dx, exit = exit,
fail = d,scale = 365.4, right = right,risk = y,lrisk = ln_y, lint = length,cint = w,nint = fu);
As you pointed out, using complete data is important. That is, original %lexis above doesn't produce correct range on the two subject only data. Full data is colon.sas7bdat which can be found here:
http://www.pauldickman.com/survival/sas/survival.sas .
Full SAS code is here: http://www.pauldickman.com/survival/sas/survival.sas
Thanks again.
@Reeza the author wrote me back and basically said the same thing you mentioned. Scale and the length variables. Do I have to round 0.08333 to more decimal places?
I think PaigeMiller and I just fixed the problem under the new forum. Please let me know if any comments.
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.