{"id":2128,"date":"2012-02-23T02:22:41","date_gmt":"2012-02-23T07:22:41","guid":{"rendered":"http:\/\/www.thejuliagroup.com\/blog\/?p=2128"},"modified":"2012-02-23T02:29:07","modified_gmt":"2012-02-23T07:29:07","slug":"learning-advanced-sas-from-a-macro-part-2","status":"publish","type":"post","link":"https:\/\/www.thejuliagroup.com\/blog\/learning-advanced-sas-from-a-macro-part-2\/","title":{"rendered":"Learning Advanced SAS from a Macro: Part 2"},"content":{"rendered":"<p>Okay, <a href=\"http:\/\/www.thejuliagroup.com\/blog\/?p=2102\">where we left off on the propensity score macro<\/a> from <a href=\"http:\/\/www.lexjansen.com\/pharmasug\/2006\/publichealthresearch\/pr05.pdf\">Feng, Wu and Xu<\/a> and the nifty things you can learn from reading someone else&#8217;s code, in this case, their propensity score macro with calipers &#8230;.<\/p>\n<p>We previously dealt with the situation where you had no matches and exactly one match. If you find more than one match within the caliper, you would then go on to the next step. This is where it gets really interesting.<\/p>\n<p><strong>Nifty things eight and nine<\/strong><br \/>\n<code><br \/>\n%if \u00a0%eval(&amp;casen)=0 %then %do;<\/code><\/p>\n<p>This shows you how to set up a loop that does certain DATA and PROC steps repeatedly. Once you start using macro %DO loops you will wonder how you ever survived without them. In this first loop, it is going to do these things if there is no matching record for our case, delete the record if there is no matching id, and set the new id equal to i (the counter variable defined way above in the previous post). Notice that you need %IF , %THEN and %DO. I always leave off at least one of those % signs.<\/p>\n<p>This also uses the %EVAL function again, so it is a nice reminder if you are new to macros. This function evaluates (hence the name) the value in the parentheses as a number, not as text. That &amp;casen is the result of a previous PROC SQL step that put the count of the number of records into that macro variable.<\/p>\n<p>So &#8230; essentially, if we don&#8217;t find a match, we delete that record, re-set our newid value to be one more and loop back up to the top of the data set where we will get the next record and go again.<\/p>\n<p><code>data case_ctrl_together;<br \/>\nset case_ctrl_together match_temp(in=a keep=&amp;id);<br \/>\nif &amp;id=. then delete;<br \/>\nif a then newid=&amp;i;<br \/>\nrun;<br \/>\n%end;<\/code><\/p>\n<p><a href=\"http:\/\/www.thejuliagroup.com\/blog\/wp-content\/uploads\/2012\/02\/kale.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-2132\" title=\"kale\" src=\"http:\/\/www.thejuliagroup.com\/blog\/wp-content\/uploads\/2012\/02\/kale.jpg\" alt=\"kale\" width=\"251\" height=\"240\" \/><\/a><strong>Minor Nifty Thing Ten\u00a0 &#8230;. if you like SQL, which I don&#8217;t particularly, but it is probably good for you, like kale<\/strong><\/p>\n<p>So, here is the %ELSE, followed by the %IF,\u00a0 %THEN and %DO which does the exact same thing as an ELSE IF\u00a0 blah blah blah THEN DO ; in\u00a0 non-macro SAS. There is our %EVAL again (repetition is good when you are first learning).<br \/>\n<code>%else %if %eval(&amp;casen)=1 %then %do;<\/code><\/p>\n<p>So, yeah, what exactly do we do if we do have exactly one match? First, you temporarily create a data set of the controls and matches. Next you add the match you have found in to the case_ctrl_together data set you are building<\/p>\n<p>We use a PROC SQL step to update our temporary data set of cases that do not have matches.<\/p>\n<p>I have to say, this is not how I would have done this, but it is good in a way because it does show one way of solving this problem of updating the data set of matches you are building and updating the file to be matched using SQL if you are into that sort of thing. By the way, if you aren&#8217;t familiar with SQL, do notice that you need the QUIT ; statement at the end to exit out of it and go back to regular SAS syntax.<br \/>\n<code>data match_temp1;<br \/>\nset ctrl_temp match_temp;<br \/>\nnewid=&amp;i;<br \/>\nrun;<br \/>\ndata case_ctrl_together;<br \/>\nset case_ctrl_together match_temp1(keep=&amp;id newid);<br \/>\nif &amp;id=. then delete;<br \/>\nrun;<br \/>\nproc sql;<br \/>\ncreate table case_temp<br \/>\nas select a.*<br \/>\nfrom case_temp a, match_temp b<br \/>\nwhere a.&amp;id^=b.&amp;id;<br \/>\nquit;<br \/>\n%end;<\/code><\/p>\n<p><strong>Nifty thing eleven and I really do like this &#8230;<\/strong><\/p>\n<p>If there is more than one match, the macro calls ANOTHER macro which finds the optimal match of those available within the calipers. It does not need the &amp;&amp;macrovar notation to resolve nested macro variables because none of the parameters passed to it are macro variables. They are all &#8216;hard-coded&#8217; into the original macro. I thought this was rather clever.<\/p>\n<p>I know I have said before that I am not a fan of the unconditional ELSE statement , even under the best of circumstances (and this is the best of circumstances), but, oh well.<br \/>\n<code>%else %do;<br \/>\n***please specify the key variables (including propensity score as one key variable) which are used to calculate the mahalanobis distance ****;<br \/>\n%Mahalanobis(match_temp, prob, ctrl_temp);<br \/>\n%end;<br \/>\n%end;<\/code><\/p>\n<p>There are two %END statements because the first one ends the %ELSE %DO that will happen when you have more than one match and the second ends the whole loop that you are doing from 1 to the total number of observations in the control group data set.<\/p>\n<p>I really\u00a0 liked the %Mahalanobis macro, so tune in next time and see if you do as well.<\/p>\n<p>For now, having just met several deadlines in the past two weeks, I&#8217;m going to put my feet up, drink tea and read <a href=\"http:\/\/www.blogher.com\/\">random blogs on Blogher<\/a> just for the hell of it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Okay, where we left off on the propensity score macro from Feng, Wu and Xu and the nifty things you can learn from reading someone else&#8217;s code, in this case, their propensity score macro with calipers &#8230;. We previously dealt with the situation where you had no matches and exactly one match. If you find&#8230;<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[9,11,8],"tags":[],"class_list":["post-2128","post","type-post","status-publish","format-standard","hentry","category-software","category-statistics","category-technology"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/posts\/2128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/comments?post=2128"}],"version-history":[{"count":6,"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/posts\/2128\/revisions"}],"predecessor-version":[{"id":2137,"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/posts\/2128\/revisions\/2137"}],"wp:attachment":[{"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/media?parent=2128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/categories?post=2128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.thejuliagroup.com\/blog\/wp-json\/wp\/v2\/tags?post=2128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}