{"id":490,"date":"2023-02-11T22:29:08","date_gmt":"2023-02-11T20:29:08","guid":{"rendered":"https:\/\/jvdl.me\/wordpress\/?p=490"},"modified":"2023-02-12T10:50:18","modified_gmt":"2023-02-12T08:50:18","slug":"creating-a-temporary-sas-array-of-dynamic-size","status":"publish","type":"post","link":"https:\/\/jvdl.me\/blog\/?p=490","title":{"rendered":"Creating a temporary SAS array of dynamic size"},"content":{"rendered":"\n<p>Arrays in SAS are incredibly useful things. For example, if you&#8217;re dealing with a concomitant medications dataset and want to check for certain medicines across multiple columns, you&#8217;d be hard-pressed to find a faster method than using arrays!<\/p>\n\n\n\n<p>Within the SDTM.CM domain, medication names are spread across several columns, usually: CMTRT (Reported Name of Drug, Med, or Therapy), CMMODIFY (Modified Reported Name), and CMDECOD (Standardized Medication Name).<\/p>\n\n\n\n<p>If we wanted to find and flag, for example, the following three medicines: Aspirin, Antacid, Potassium Chloride ; we could do it as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"sas\" class=\"language-sas line-numbers\">\ndata cm;\n    set sdtm.cm;\n\n    array all_meds {*} $ cmtrt cmmodify cmdecod;\n    array check_meds {3} $ _temporary_ (\"aspirin\", \"antacid\", \"potassium chloride\");\n\n    do i = 1 to dim(all_meds);\n        do j = 1 to dim(check_meds);\n            if upcase(all_meds[i]) = upcase(check_meds[j]) then occur = \"Y\";\n        end;\n    end;\n\n    if occur ^= \"Y\" then delete;\nrun;\n<\/code><\/pre>\n\n\n\n<p>The above method works well to quickly check for multiple conmeds across multiple columns. However, usually a conmeds list is not restricted to only 3 items and counting the number of unique items is a slow and tedious process. If an item needs to be added or removed, then the space to be reserved needs to be updated.<\/p>\n\n\n\n<p>Unfortunately specifying the list of conmeds in a temporary array prevents the use of the dynamic sizing, usually indicated by {*}. This is something that will hopefully be fixed in a newer version of SAS, but until such time I&#8217;ve taken it upon myself to create a macro which creates dynamically sized (sort of!) temporary arrays. It does this by counting the number of items and automatically reserving the space for it.<\/p>\n\n\n\n<p><strong>Note:<\/strong> if you are feeling lazy, you can still create a temporary array and simply oversize it, e.g., specify a size of 100 items even though you may only need half that. However, this will result in SAS posting a WARNING to the log about partial array initialization, which is not ideal in the pharmaceutical environment where we want clean logs! <\/p>\n\n\n\n<p>If you&#8217;d prefer to avoid another O(n) loop which checks for the maximum length needed for character variables, simply set a static length, say $20.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"sas\" class=\"language-sas line-numbers\">\n\/*\n    Macro makeTempArray\n    \n    Purpose: To create temporary arrays without knowing the\n             size of the array needed beforehand. This is a \n             limitation of the original SAS procedure for\n             creating temporary arrays.\n    \n    Parameters:\n        arrayname : an arbitrary name for your array\n        ischar    : pass either Y for a character array \n                    or N for numeric\n        items     : pass the list of items to be contained in\n                    the array, wrapped in %str() and separated\n                    by commas\n*\/\n\n%macro makeTempArray(arrayname=, ischar=, items=);\n    %let n=%sysfunc(countw(&amp;items., %str(,), )); \/*count the number of items to reserve space for*\/\n   \n    %if &amp;ischar.=Y %then %do; \/*if this is a character array, we need the length of the longest item*\/ \n        %let l = 1;\n        %do j = 1 %to &amp;n.;\n            %let l0 = %sysfunc(length(%sysfunc(scan(&amp;items., &amp;j., %str(,), r))));\n            %if  &amp;l0. &gt; &amp;l. %then %let l = &amp;l0.;\n        %end;\n    %end;\n    \n    array &amp;arrayname. {&amp;n.} %if &amp;ischar.=Y %then $&amp;l.; _temporary_ (\n        %do i = 1 %to &amp;n.;\n            %let item = %sysfunc(scan(&amp;items., &amp;i., %str(,), r));\n            %if &amp;ischar.=Y %then %str(\"&amp;item.\" ); %else &amp;item.;\n        %end;\n    );\n%mend makeTempArray;\n<\/code><\/pre>\n\n\n\n<p>With this macro, we can now modify our initial starting block of code as follows (items must be separated by commas):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"sas\" class=\"language-sas line-numbers\">\ndata cm;\n    set sdtm.cm;\n\n    array all_meds {*} $ cmtrt cmmodify cmdecod;\n    %makeTempArray(arrayname=%str(check_meds), ischar=%str(Y), items=%str(aspirin, antacid, potassium chloride));\n\n    do i = 1 to dim(all_meds);\n        do j = 1 to dim(check_meds);\n            if upcase(all_meds[i]) = upcase(check_meds[j]) then occur = \"Y\";\n        end;\n\n        if occur = \"Y\" then leave;\n    end;\n\n    if occur ^= \"Y\" then delete;\nrun;\n<\/code><\/pre>\n\n\n\n<p>Hope this helps you next time you need to cross-check multiple items across multiple columns! Happy hacking!<\/p>\n\n\n\n<p><strong>Updates:<\/strong> My colleague, Mazi Ntintelo has rightly pointed out that the commas within the macro&#8217;s scan functions should be wrapped as <em>%str(,)<\/em> and also that the <em>do<\/em> loop checking for conmeds can be optimised with a <em>leave<\/em> statement. Thanks, Mazi!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Arrays in SAS are incredibly useful things. For example, if you&#8217;re dealing with a concomitant medications dataset and want to check for certain medicines across multiple columns, you&#8217;d be hard-pressed to find a faster method than using arrays! Within the SDTM.CM domain, medication names are spread across several columns, usually: CMTRT (Reported Name of Drug, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,67,53,54],"tags":[126,128,130,131,129,55,127],"class_list":["post-490","post","type-post","status-publish","format-standard","hentry","category-computer-science","category-pharma","category-programming","category-sas","tag-arrays","tag-character","tag-dynamic-size","tag-macro","tag-numeric","tag-sas","tag-temporary"],"_links":{"self":[{"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=\/wp\/v2\/posts\/490","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=490"}],"version-history":[{"count":15,"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=\/wp\/v2\/posts\/490\/revisions"}],"predecessor-version":[{"id":505,"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=\/wp\/v2\/posts\/490\/revisions\/505"}],"wp:attachment":[{"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=490"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=490"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jvdl.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=490"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}