<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>GeoffreyBrent Tracker</title>
    <link>https://communities.sas.com/kntur85557/tracker</link>
    <description>GeoffreyBrent Tracker</description>
    <pubDate>Wed, 20 May 2026 21:08:12 GMT</pubDate>
    <dc:date>2026-05-20T21:08:12Z</dc:date>
    <item>
      <title>Re: The correct average please help</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/The-correct-average-please-help/m-p/166503#M43129</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Yeah, renaming as a data set option is usually a more intuitive way to go, I just got lazy in my demo code &lt;span class="lia-unicode-emoji" title=":slightly_smiling_face:"&gt;🙂&lt;/span&gt;&lt;/P&gt;&lt;P&gt;(When I first worked in SAS, nobody mentioned to me that data step commands aren't all executed in order of appearance, which made the learning process... interesting.)&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Mon, 07 Apr 2014 23:37:09 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/The-correct-average-please-help/m-p/166503#M43129</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2014-04-07T23:37:09Z</dc:date>
    </item>
    <item>
      <title>Re: The correct average please help</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/The-correct-average-please-help/m-p/166500#M43126</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;I agree that it's best to convert missing values to zeroes where that's what they represent, but that code won't give the result you're expecting.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;RENAME statements are executed &lt;EM&gt;after &lt;/EM&gt;the body of the DATA step is run. So your code will create a new variable named "flu_cases" (which will be zero for cases which are missing from flu_reports_by_date, and missing for all others) before attempting to rename _freq_ to flu_cases. This will generate a warning ("Variable flu_cases already exists") and as far as I can tell, you end up with the same values that were in _freq_, complete with missingness.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Probably safer to do the renaming up in the MERGE statement so you can use the new name through the rest of the data step - I just couldn't remember the exact syntax and didn't have time to look it up.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Fri, 04 Apr 2014 06:14:46 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/The-correct-average-please-help/m-p/166500#M43126</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2014-04-04T06:14:46Z</dc:date>
    </item>
    <item>
      <title>Re: The correct average please help</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/The-correct-average-please-help/m-p/166497#M43123</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;This is really more of a stats problem than a programming question, but then I'm more of a statistician than a programmer...&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Without context, there's no universal answer to this question of how to calculate the average. There are scenarios in which missing values should be excluded from calculation, others where they should be treated as zeroes, and yet others where missingness makes it impossible to get a sound answer. You need to think about what missing data means in &lt;EM&gt;your&lt;/EM&gt; context, and how the average will be used.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;One situation where I commonly encounter missing-as-zero is when merging counts created by PROC SUMMARY/MERGE. For instance, I might have a list of 'flu cases reported, each with a date attached. I can get day-by-day counts:&lt;/P&gt;&lt;P&gt;proc summary data=flu_reports nway;&lt;/P&gt;&lt;P&gt;class day_reported;&lt;/P&gt;&lt;P&gt;output out=flu_reports_by_date;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I can then merge onto a list of other illnesses reported (assumed to cover all days of the year):&lt;/P&gt;&lt;P&gt;data all_illnesses_by_date;&lt;/P&gt;&lt;P&gt;merge illnesses_except_flu flu_reports_by_date;&lt;/P&gt;&lt;P&gt;by day_reported;&lt;/P&gt;&lt;P&gt;rename _freq_=flu_cases;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;If there were no flu cases reported on April 1, then flu_reports_by_date will have no entry for April 1. When I merge it to create all_illnesses_by_date it will have a missing value for flu - but that should be treated as zero. Ideally it'd be changed to zero before calculating averages, but if not, then the missing entry needs to be included in the count when calculating an average.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Thu, 03 Apr 2014 23:25:49 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/The-correct-average-please-help/m-p/166497#M43123</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2014-04-03T23:25:49Z</dc:date>
    </item>
    <item>
      <title>Re: Log window output</title>
      <link>https://communities.sas.com/t5/Developers/Log-window-output/m-p/158720#M4179</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;In addition to Tom's advice, it might be worth trying to figure out &lt;EM&gt;why &lt;/EM&gt;the log is so large.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;When I get "log full" type problems, it usually turns out to be because something in my program is generating a very large number of notes/warnings. For instance, if I'm doing something that generates a note at every observation of a 2-million-observation file, that will choke up the log very quickly. One way to identify this is to run the program on a smaller data set so that I can look at the log and see what's taking up the space. If I can identify a step that's spamming the log, I can then fix that problem without needing to shut off logging altogether.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Thu, 28 Nov 2013 22:56:01 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Developers/Log-window-output/m-p/158720#M4179</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2013-11-28T22:56:01Z</dc:date>
    </item>
    <item>
      <title>Re: This problem can be solved by pre-school children in 5-10 minutes</title>
      <link>https://communities.sas.com/t5/Statistical-Procedures/This-problem-can-be-solved-by-pre-school-children-in-5-10/m-p/100808#M5245</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;I'm with PG on this one. The SAS "solution" posted can only be written by somebody who's already guessed that f(x) might be a sum of individual digit scores. But at that point, they've already done 99% of the heavy lifting. Somebody who has that idea in mind doesn't even need SAS; it's easy enough to figure out the digit values and verify the results by eye.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Thu, 06 Sep 2012 01:20:43 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Statistical-Procedures/This-problem-can-be-solved-by-pre-school-children-in-5-10/m-p/100808#M5245</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-09-06T01:20:43Z</dc:date>
    </item>
    <item>
      <title>Re: How to get rid of these warnings in concatenation!</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/How-to-get-rid-of-these-warnings-in-concatenation/m-p/111302#M30840</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;If you have the data set open, you can also check the length of a character variable by mousing over the column header. After a little while a tooltip will pop up with variable name, length, and other properties.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Tue, 04 Sep 2012 00:42:52 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/How-to-get-rid-of-these-warnings-in-concatenation/m-p/111302#M30840</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-09-04T00:42:52Z</dc:date>
    </item>
    <item>
      <title>Re: how to remove duplicates in SAS 4.3 EG</title>
      <link>https://communities.sas.com/t5/SAS-Enterprise-Guide/how-to-remove-duplicates-in-SAS-4-3-EG/m-p/109604#M9380</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Patrick is correct, I was aware of this rather important detail but completely neglected to mention it in my response &lt;span class="lia-unicode-emoji" title=":disappointed_face:"&gt;😞&lt;/span&gt; In hindsight, my PROC SORT example should have used "by _all_".&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Sun, 02 Sep 2012 23:59:02 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Enterprise-Guide/how-to-remove-duplicates-in-SAS-4-3-EG/m-p/109604#M9380</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-09-02T23:59:02Z</dc:date>
    </item>
    <item>
      <title>Re: how to delete one macro variable for another macro variable?</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-delete-one-macro-variable-for-another-macro-variable/m-p/109810#M22805</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;I see you already have solutions, but you might (or might not) find that existing SAS regression options can save you the need to write your own macro.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;For instance, PROC REG gives you options for stepwise selection (add variables with best explanatory power/remove variables with worst) or by using something like SELECTION=MAXR STOP=&amp;amp;MAXEXPVARS you can force SAS to choose the best n variables.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Sun, 02 Sep 2012 23:56:36 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-delete-one-macro-variable-for-another-macro-variable/m-p/109810#M22805</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-09-02T23:56:36Z</dc:date>
    </item>
    <item>
      <title>Re: how to delete one macro variable for another macro variable?</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-delete-one-macro-variable-for-another-macro-variable/m-p/109801#M22796</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;You can't delete it from x, because it's not in x to begin with.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;What you've done here is define &amp;amp;x as the literal string "a1-a10". I assuming you're thinking of this as a list of variables a1, a2, ..., a10, but when you define "%let x=a1-a10" SAS doesn't perform that interpretation. SAS will substitute the characters "a1-a10" in the code where "&amp;amp;x" appears and only then will it interpret it.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Demonstration:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;%let x=a1-a3;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data test;&lt;/P&gt;&lt;P&gt;a1=5;&lt;/P&gt;&lt;P&gt;a2=6;&lt;/P&gt;&lt;P&gt;a3=7;&lt;/P&gt;&lt;P&gt;diff=&amp;amp;x;&lt;/P&gt;&lt;P&gt;sum=sum(of &amp;amp;x);&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;When you run this code, the first thing SAS does is to substitute "a1-a3" where "&amp;amp;x" appears:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data test;&lt;/P&gt;&lt;P&gt;a1=5;&lt;/P&gt;&lt;P&gt;a2=6;&lt;/P&gt;&lt;P&gt;a3=7;&lt;/P&gt;&lt;P&gt;diff=a1-a3;&lt;/P&gt;&lt;P&gt;sum=sum(of a1-a3);&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;It then interprets based on context. In the first case, it interprets "a1-a3" as a1 minus a3 (= 5-7 = -2); in the second case it interprets it as "a1, a2, a3" (= 5+6+7 = 18).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;If you can give some more context on what you're looking to do, people might be able to suggest some other solution.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Fri, 31 Aug 2012 06:07:50 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-delete-one-macro-variable-for-another-macro-variable/m-p/109801#M22796</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-31T06:07:50Z</dc:date>
    </item>
    <item>
      <title>Re: how to remove duplicates in SAS 4.3 EG</title>
      <link>https://communities.sas.com/t5/SAS-Enterprise-Guide/how-to-remove-duplicates-in-SAS-4-3-EG/m-p/109600#M9376</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Assuming you want to remove duplicate observations with the same values?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data have;&lt;/P&gt;&lt;P&gt;input x y;&lt;/P&gt;&lt;P&gt;cards;&lt;/P&gt;&lt;P&gt;1 2&lt;/P&gt;&lt;P&gt;1 4&lt;/P&gt;&lt;P&gt;1 8&lt;/P&gt;&lt;P&gt;2 3&lt;/P&gt;&lt;P&gt;2 4&lt;/P&gt;&lt;P&gt;2 4&lt;/P&gt;&lt;P&gt;2 5&lt;/P&gt;&lt;P&gt;1 7&lt;/P&gt;&lt;P&gt;1 8&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;/* PROC SORT method */&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sort data=have nodup out=want1;&lt;/P&gt;&lt;P&gt;by x y;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;/* PROC SQL method */&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql;&lt;/P&gt;&lt;P&gt;create table want2 as select distinct * from have;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;If you want to delete duplicates that are not 100% identical (e.g. all units with the same ID variable regardless of whether they differ in other values) look at the NODUPKEY option in PROC SORT.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Fri, 31 Aug 2012 02:20:51 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Enterprise-Guide/how-to-remove-duplicates-in-SAS-4-3-EG/m-p/109600#M9376</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-31T02:20:51Z</dc:date>
    </item>
    <item>
      <title>Re: PROC PANEL: What values SAS uses for the cross-sectional dummies in fixed-effects model?</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/PROC-PANEL-What-values-SAS-uses-for-the-cross-sectional-dummies/m-p/100697#M28317</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;I haven't experimented with PROC PANEL (and on a quick glance, the support documentation doesn't help me) but you may want to check whether it's using "effect coding". I remember being very confused by some other modelling PROCs until I found out about effect coding, which I'd never heard of before. Google should be able to find you a good explanation of the concept.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Thu, 23 Aug 2012 01:49:21 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/PROC-PANEL-What-values-SAS-uses-for-the-cross-sectional-dummies/m-p/100697#M28317</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-23T01:49:21Z</dc:date>
    </item>
    <item>
      <title>Re: Selecting the right record...</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Selecting-the-right-record/m-p/101508#M21256</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Assumptions:&lt;/P&gt;&lt;P&gt;(1) no two records for the same key have overlapping dates (i.e. if birth_da for record 1 &amp;lt; birth_da for record 2 on the same key, then ret_date for record 1 &amp;lt; birth_da for record 2)&lt;/P&gt;&lt;P&gt;(2) birth_da is nonmissing&lt;/P&gt;&lt;P&gt;(3) missing value for ret_date indicates not yet retired (so can only occur in the last record)&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;So for each key, you want to return the latest birth_da that is not greater than the date?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I think the easiest way to do this in simple SAS is to reverse-sort and use LAG to find the next date:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;%let date='15Jul2001'd;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data have;&lt;/P&gt;&lt;P&gt;input recordid h_postal $ birth_da ret_date;&lt;/P&gt;&lt;P&gt;informat birth_da ret_date ANYDTDTE20.;&lt;/P&gt;&lt;P&gt;format birth_da ret_date DATE9.;&lt;/P&gt;&lt;P&gt;cards;&lt;/P&gt;&lt;P&gt;1 A 01Jan2000 01Jun2001&lt;/P&gt;&lt;P&gt;2 A 01Jan2002 12Feb2003&lt;/P&gt;&lt;P&gt;3 B 01Jan2000 01Jun2001&lt;/P&gt;&lt;P&gt;4 B 01Jul2001 12Feb2003&lt;/P&gt;&lt;P&gt;5 C 01Jan1999 31Dec1999&lt;/P&gt;&lt;P&gt;6 C 01Jan2001 01Feb2001&lt;/P&gt;&lt;P&gt;7 C 01Jan2003 12feb2003&lt;/P&gt;&lt;P&gt;8 D 01Jan2005 01Jan2007&lt;/P&gt;&lt;P&gt;9 D 01Jan2009 .&lt;/P&gt;&lt;P&gt;10 E 01Jan2001 .&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sort data=have out=have_reverse;&lt;/P&gt;&lt;P&gt;by h_postal descending birth_da;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;set have_reverse;&lt;/P&gt;&lt;P&gt;by h_postal;&lt;/P&gt;&lt;P&gt;next_birth_date=lag(birth_da);&lt;/P&gt;&lt;P&gt;if first.h_postal then next_birth_date=.;&lt;/P&gt;&lt;P&gt;/* because we have sorted in reverse date order, first.h_postal indicates that this is the most recent entry for this unit&lt;/P&gt;&lt;P&gt;(highest value of birth_da) */&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;if (next_birth_date&amp;lt;=ret_date OR missing(ret_date)) AND not missing(next_birth_date) then put "ERROR: dates overlap";&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;if (birth_da&amp;lt;=&amp;amp;date) AND ((next_birth_date&amp;gt;&amp;amp;date) OR missing(next_birth_date)) then output;&lt;/P&gt;&lt;P&gt;drop next_birth_date;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Edit: before using this you'll need to convert to SAS date variables (which you should be using anyway for this sort of work) and as per Tom's comment, if a date of "190000001" indicates "not yet retired" this should probably be interpreted as missing.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Message was edited by: Geoffrey Brent&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Wed, 22 Aug 2012 07:22:36 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Selecting-the-right-record/m-p/101508#M21256</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-22T07:22:36Z</dc:date>
    </item>
    <item>
      <title>Re: DOES SAS 9.3 Supports Excel2010</title>
      <link>https://communities.sas.com/t5/Graphics-Programming/DOES-SAS-9-3-Supports-Excel2010/m-p/96627#M3612</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;I encountered a similar problem when migrating from SAS 9.1 to 9.3. In our case, I think the cause was that our SAS 9.3 server had mistakenly been set to use the 32-bit version of Microsoft Access; once it was reconfigured to the 64-bit version, things worked fine. (Can't give you any more detail on how to do that, our tech support guys handled the detail.)&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Tue, 21 Aug 2012 03:42:16 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Graphics-Programming/DOES-SAS-9-3-Supports-Excel2010/m-p/96627#M3612</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-21T03:42:16Z</dc:date>
    </item>
    <item>
      <title>Re: Do “subsetting if” and “where” clauses do the same thing?</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/Do-subsetting-if-and-where-clauses-do-the-same-thing/m-p/92360#M26297</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;The two code examples you provide should give identical results, though WHERE may be faster to run.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;However there are many cases where WHERE and subsetting IF do not give identical output. Compare:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data test;&lt;/P&gt;&lt;P&gt;length x $5;&lt;/P&gt;&lt;P&gt;input x;&lt;/P&gt;&lt;P&gt;cards;&lt;/P&gt;&lt;P&gt;One&lt;/P&gt;&lt;P&gt;Two&lt;/P&gt;&lt;P&gt;Three&lt;/P&gt;&lt;P&gt;Four&lt;/P&gt;&lt;P&gt;Five&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data subset_if;&lt;/P&gt;&lt;P&gt;set test;&lt;/P&gt;&lt;P&gt;sequence_number=_n_;&lt;/P&gt;&lt;P&gt;previous_x=lag(x);&lt;/P&gt;&lt;P&gt;if not(x="Three");&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data subset_where;&lt;/P&gt;&lt;P&gt;set test;&lt;/P&gt;&lt;P&gt;sequence_number=_n_;&lt;/P&gt;&lt;P&gt;previous_x=lag(x);&lt;/P&gt;&lt;P&gt;where not(x="Three");&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In creating SUBSET_IF, we read in and process all lines from TEST before dropping the third observation. Even though the third observation isn't output directly, it still affects what is output: sequence_number goes 1,2,4,5 and previous_x goes " ", "One", "Three", "Four".&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In creating SUBSET_WHERE, the observation with x="Three" is deleted at an earlier stage, and there's no sign in the output that it ever existed.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Wed, 15 Aug 2012 01:04:33 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/Do-subsetting-if-and-where-clauses-do-the-same-thing/m-p/92360#M26297</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-15T01:04:33Z</dc:date>
    </item>
    <item>
      <title>Re: sum and ratio, by year and industry</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/sum-and-ratio-by-year-and-industry/m-p/89676#M25574</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;I'm a bit confused by "squared revenue" - not obvious why this would be of interest. But I'm assuming TOTREV is supposed to be the sum of squared revenue, not the sum of individual revenue, otherwise the ratio doesn't make much sense either. (You would get different answers depending on whether you worked in dollars or cents.)&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Under those assumptions:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc summary data=have nway;&lt;/P&gt;&lt;P&gt;class herfsic fyear;&lt;/P&gt;&lt;P&gt;var revt_squared;&lt;/P&gt;&lt;P&gt;output out=total_squared_revs (drop=_type_ _freq_) sum(revt_squared)=totrev;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql;&lt;/P&gt;&lt;P&gt;create table want as select&lt;/P&gt;&lt;P&gt;a.*,&lt;/P&gt;&lt;P&gt;b.totrev,&lt;/P&gt;&lt;P&gt;a.revt_squared/b.totrev as conratio&lt;/P&gt;&lt;P&gt;from have as a join total_squared_revs as b&lt;/P&gt;&lt;P&gt;on (a.herfsic=b.herfsic AND a.fyear=b.fyear);&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Fri, 10 Aug 2012 06:52:30 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/sum-and-ratio-by-year-and-industry/m-p/89676#M25574</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-10T06:52:30Z</dc:date>
    </item>
    <item>
      <title>Re: check if a character is an alphabet</title>
      <link>https://communities.sas.com/t5/SAS-Programming/check-if-a-character-is-an-alphabet/m-p/87574#M18738</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Toby, have you tested that PrxMatch code? When I use that one I'm getting zeroes where they shouldn't be.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Fri, 10 Aug 2012 01:50:08 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/check-if-a-character-is-an-alphabet/m-p/87574#M18738</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-10T01:50:08Z</dc:date>
    </item>
    <item>
      <title>Re: Finding non-exact matches in one dataset</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/Finding-non-exact-matches-in-one-dataset/m-p/89012#M25354</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;If you want a Rolls-Royce solution, there are some good commercial packages out there. But you can get a long way with a little bit of SQL and some knowledge of SOUNDEX and edit distance functions (COMPLEV, COMPGED).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;SOUNDEX converts a character string to an expression that gives a rough idea of what it sounds like: vowels are omitted and similar-sounding consonants are lumped together. COMPLEV tells you how many single-character edits it takes to convert one string into another.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;For instance, SOUNDEX("John") = J5. SOUNDEX("Johann") also equals J5, and SOUNDEX("Susan") = S25. Using COMPLEV to compare the SOUNDEX values tells us that "John" and "Johann" are very similar (score 0), but "John" and "Susan" are less similar (score 2).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;COMPGED is a more sophisticated version of COMPLEV that accounts for the sorts of errors that are most commonly made: e.g. "Simon"-&amp;gt;"Simmon" is more likely than "Simon"-&amp;gt;"Simkon".&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;You can use these functions (and others of your choice) to generate a score for possible matches. Each pair of observations ends up with a score, and you use a cutoff to determine which ones should be considered as possible matches. Here's an example:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;%let soundweight=20;&lt;/P&gt;&lt;P&gt;%let gedisweight=0.1;&lt;/P&gt;&lt;P&gt;%let cutoff=75;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data have;&lt;/P&gt;&lt;P&gt;id=_n_;&lt;/P&gt;&lt;P&gt;input firstname $ lastname $;&lt;/P&gt;&lt;P&gt;firstname=upcase(firstname); /* COMPGED is case-sensitive */&lt;/P&gt;&lt;P&gt;lastname=upcase(lastname);&lt;/P&gt;&lt;P&gt;firstnamesound=soundex(firstname);&lt;/P&gt;&lt;P&gt;lastnamesound=soundex(lastname);&lt;/P&gt;&lt;P&gt;cards;&lt;/P&gt;&lt;P&gt;Susan Smith&lt;/P&gt;&lt;P&gt;John Smith&lt;/P&gt;&lt;P&gt;Sue Smith&lt;/P&gt;&lt;P&gt;Johann Schmidt&lt;/P&gt;&lt;P&gt;Sue Jones&lt;/P&gt;&lt;P&gt;Sam Snell&lt;/P&gt;&lt;P&gt;Joe Johnson&lt;/P&gt;&lt;P&gt;Jae Johnston&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql;&lt;/P&gt;&lt;P&gt;create table want as select&lt;/P&gt;&lt;P&gt;a.id as id1, b.id as id2,&lt;/P&gt;&lt;P&gt;a.firstname as firstname1, b.firstname as firstname2,&lt;/P&gt;&lt;P&gt;a.lastname as lastname1, b.lastname as lastname2,&lt;/P&gt;&lt;P&gt;a.firstnamesound as firstnamesound1, b.firstnamesound as firstnamesound2,&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;/* The next few variables don't need to be here - they are recalculated separately in the join condition below.&lt;/P&gt;&lt;P&gt;I've included them here so you can see what these intermediate functions look like before they're combined to&lt;/P&gt;&lt;P&gt;generate the overall match score. */&lt;/P&gt;&lt;P&gt;complev(a.firstnamesound,b.firstnamesound) as firstnamesoundscore,&lt;/P&gt;&lt;P&gt;complev(a.lastnamesound,b.lastnamesound) as lastnamesoundscore,&lt;/P&gt;&lt;P&gt;compged(a.firstname,b.firstname) as firstnameeditscore,&lt;/P&gt;&lt;P&gt;compged(a.lastname,b.lastname) as lastnameeditscore,&lt;/P&gt;&lt;P&gt;&amp;amp;soundweight*(calculated firstnamesoundscore + calculated lastnamesoundscore)&lt;/P&gt;&lt;P&gt;+&amp;amp;gedisweight*(calculated firstnameeditscore + calculated lastnameeditscore) as matchscore&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;from have as a inner join have as b on (a.id &amp;lt; b.id /* prevents duplicates and self-matches */ AND&lt;/P&gt;&lt;P&gt;(&lt;/P&gt;&lt;P&gt;&amp;amp;soundweight*(complev(a.firstnamesound,b.firstnamesound)+complev(a.lastnamesound,b.lastnamesound))&lt;/P&gt;&lt;P&gt;+&amp;amp;gedisweight*(compged(a.firstname,b.firstname)+compged(a.lastname,b.lastname))&lt;/P&gt;&lt;P&gt;)&lt;/P&gt;&lt;P&gt;&amp;lt;&amp;amp;cutoff);&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;You may want to play around with the weights and the scoring function, especially if you have other data that could be used to enhance the match. Raising the cutoff will increase the likelihood of accepting a match, so you'll get more false positives but fewer false negatives. Lowering it has the reverse effect.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Fri, 10 Aug 2012 01:42:57 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/Finding-non-exact-matches-in-one-dataset/m-p/89012#M25354</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-10T01:42:57Z</dc:date>
    </item>
    <item>
      <title>Re: check if a character is an alphabet</title>
      <link>https://communities.sas.com/t5/SAS-Programming/check-if-a-character-is-an-alphabet/m-p/87571#M18735</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Another option is using regular expressions:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data t2;&lt;/P&gt;&lt;P&gt;set t1;&lt;/P&gt;&lt;P&gt;one_alpha_rx=prxparse("/[a-zA-Z]/");&lt;/P&gt;&lt;P&gt;ind=prxmatch(one_alpha_rx,x);&lt;/P&gt;&lt;P&gt;drop one_alpha_rx;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;For the problem you describe, you'd be better off using Art and Linlin's solutions - they're simpler and probably faster. But if you have to check more complex patterns some time, it's worth learning about regexp matching (this webform won't let me copy and paste, but there's a good paper on this in the SUGI29 archives).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;As an example of where regexp comes in handy, I had an application where ID variables were expected to be twelve digits followed by an alpha character and then four more digits. To check whether inputs fit this rule, I used:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;legal_pattern=prxparse("/\d{12}[a-zA-Z]\d{4}/");&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Note that regexp matching doesn't check whether the variable EXACTLY matches the pattern defined, only whether it appears somewhere in there. But this isn't a problem if the length of the regexp exactly matches the length of the variable.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Thu, 09 Aug 2012 00:55:37 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/check-if-a-character-is-an-alphabet/m-p/87571#M18735</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-09T00:55:37Z</dc:date>
    </item>
    <item>
      <title>Re: proc summary procedure giving difference in sum when summarized by different classes</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/proc-summary-procedure-giving-difference-in-sum-when-summarized/m-p/83371#M23934</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Yeah, looks like roundoff errors to me. A length-8 SAS numeric variable is stored with 52-56 bits in the mantissa (depending on your system), which translates to around 15-17 significant figures in base-10. So when you store a number of magnitude ~ 1E21, you can expect rounding errors of magnitude ~ 100,000.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Some demo code:&lt;/P&gt;&lt;P&gt;data test;&lt;/P&gt;&lt;P&gt;input x;&lt;/P&gt;&lt;P&gt;cards;&lt;/P&gt;&lt;P&gt;1E4&lt;/P&gt;&lt;P&gt;1E21&lt;/P&gt;&lt;P&gt;-1E21&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc summary data=test nway;&lt;/P&gt;&lt;P&gt;var x;&lt;/P&gt;&lt;P&gt;output out=test2 sum=;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;On my system, this returns zero: when we add 1E4 to 1E21, it gets rounded away to zero. However, if I reorder the data so that the "1E4" appears last in the data step, the two big values cancel out exactly, and we get the correct sum.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Some options:&lt;/P&gt;&lt;P&gt;- accept the rounding error&lt;/P&gt;&lt;P&gt;- find a way to implement calculations in higher precision (see 2008 SGF paper "Ludicrously Large Numbers" for some ideas)&lt;/P&gt;&lt;P&gt;- fine-tune the SAS calculation to minimise rounding error.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;If the values you are adding are mostly positive, I'd go with the first strategy suggested by mkeintz: pick up the small values early so they can add up to something that won't be wiped out by roundoff error.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;However, looking at the values you've listed above, it seems you have several very large positives and negatives that almost exactly cancel one another out. For instance, your two largest negatives almost exactly cancel the largest positive; the fourth- and fifth-largest negatives cancel the second-largest positive; and the third-largest on both lists almost cancel out.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Is there some reason why this might be the case? And if so, is there some way of matching up values that are likely to cancel out?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Take the following example:&lt;/P&gt;&lt;P&gt;data test;&lt;/P&gt;&lt;P&gt;length group $1;&lt;/P&gt;&lt;P&gt;input group x;&lt;/P&gt;&lt;P&gt;cards;&lt;/P&gt;&lt;P&gt;A 1E4&lt;/P&gt;&lt;P&gt;B 1E21&lt;/P&gt;&lt;P&gt;B 1E21&lt;/P&gt;&lt;P&gt;B -2E21&lt;/P&gt;&lt;P&gt;C 1E10&lt;/P&gt;&lt;P&gt;C 1E10&lt;/P&gt;&lt;P&gt;C -2E10&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc summary data=test;&lt;/P&gt;&lt;P&gt;var x;&lt;/P&gt;&lt;P&gt;output out=test2 sum=;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This returns zero, because the original 1E4 is wiped out by rounding error. But add "class group;" to the PROC SUMMARY and you get the right answer: it forces large values to cancel before combining them with small ones.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Tue, 07 Aug 2012 01:22:41 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/proc-summary-procedure-giving-difference-in-sum-when-summarized/m-p/83371#M23934</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-08-07T01:22:41Z</dc:date>
    </item>
    <item>
      <title>Re: Number of Decimal Places</title>
      <link>https://communities.sas.com/t5/SAS-Procedures/Number-of-Decimal-Places/m-p/77940#M22505</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Adding to Arthur's advice: even if you don't want to count trailing zeroes, the methods above should ONLY be used on character inputs.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;On my computer, all three of the code examples above give the wrong answer for an input of 0.000001 or 10000000.1234. The reason for this is that their inputs are defined as numeric by default. When you perform a string operation (STRIP, INDEX, SUBSTR, CAT) on a numeric variable you force SAS to do an implicit conversion from num to char, and the format it selects may not be the one you're expecting. Watch out for that "NOTE: Character values have been converted to numeric values" in the log.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In this case, a numeric value of 0.000001 gets converted to "1E-6" not "0.000001" and 10000000.1234 gets converted to "1000000.123", both of which lead to the wrong answer. Two of these examples assume that there will be a decimal point, and so they give the wrong answer for an integer input.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Possibly worth adding a check in the program to return a warning/error message if this is supplied with numeric input, since it would be a VERY easy mistake to make.&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Tue, 31 Jul 2012 06:37:43 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Procedures/Number-of-Decimal-Places/m-p/77940#M22505</guid>
      <dc:creator>GeoffreyBrent</dc:creator>
      <dc:date>2012-07-31T06:37:43Z</dc:date>
    </item>
  </channel>
</rss>

