/* Richard A. DeVenezia * www.devenezia.com * 2/6/04 * * This material was originally posted to SAS-L on June 4, 2003 * "One pass to find max and maxcnt using data step?" * * Ya Huang asked > For the above data, I need to find, for each id, the max of dos, > and the number of dos at the max level, so the expected result > is as below: > > id max_dos maxcnt > 1 0.3 3 > 2 0.6 6 > > It is very easy to get the max_dos, I just wonder if it is > possible to get the count of max_dos in the same step? > */ /* * Yas data was id 1 and 2, I added id 3 * Note in id=2, dos=0.6 occurs in non-consecutive row */ data xx; input id dos; cards; 1 0.1 1 0.1 1 0.01 1 0.01 1 0.01 1 0.01 1 0.3 1 0.3 1 0.3 2 0.2 2 0.2 2 0.6 2 0.05 2 0.05 2 0.05 2 0.05 2 0.6 2 0.6 2 0.6 2 0.6 2 0.6 3 0.05 3 0.6 3 0.6 3 0.8 ; /* * Make things tougher, * remove all suppositions of orderedness of data */ proc sql; reset undo_policy=none; create table xx as select * from xx order by ranuni(654321); quit; /* * This sample uses DATA Step hashes to demonstrate the * more general problem of doing groupwise frequency counting * and ordered lookups into data */ /* * Hash and HIter are production in release 9.1 * Find more at http://support.sas.com/91doc/docMainpage.jsp * * HIter methods do not set the key var value, * thus a hashes data needs to include the key if you * want to know the key value when iterating the hash */ data onepass; length id dos maxdos count 8; * instantiate two hash objects * things in parenthesis are passed to the objects constructor method; * this hash keeps track of the count of every id/dos combination; declare Hash IdDosCount(ordered:1); IdDosCount.defineKey("id", "dos"); * two value key; IdDosCount.defineData ("count"); IdDosCount.defineDone(); * this hash keeps track of the maximum dos observed in each id group; declare Hash IdMaxdos(ordered:1); IdMaxdos.defineKey("id"); * one value key; IdMaxdos.defineData("maxdos", "id"); IdMaxdos.defineDone(); * read the data and perform counting operations; * (if populating only one hash, consider using dataset:'' argument in hash constructor ; do while (not endOfData); set xx end = endOfData; * read values of variables id and dos from next row in data set; keyNotPresent = IdMaxdos.find (); put keyNotPresent= id= maxdos=; * if id is found in the hash, the variables maxdos and id are updated * to the corresponding values stored in the hash object; * add() and replace() - style 1, rely on dataset variables; if keyNotPresent then IdMaxdos.add (); else if dos > maxdos then do; maxdos = dos; IdMaxdos.replace (); end; keyNotPresent = IdDosCount.find (); put keyNotPresent= id= dos= count=; * if id/dos combination is found in the hash, the count variable is updated * to the corresponding value stored in the hash object; * add() and replace() - style 2, directly specify key and data expressions; if keyNotPresent then IdDosCount.add (key:id, key:dos, data:1); else IdDosCount.replace (key:id, key:dos, data:(count+1)); end; declare HIter iter ('IdMaxdos'); rc = iter.first(); do while (rc = 0); rc2 = IdDosCount.find (key:id, key:maxdos); OUTPUT; rc = iter.next(); end; stop; keep id maxdos count; run;