<?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>topic Re: how to add a row in SAS Programming</title>
    <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482574#M125024</link>
    <description>&lt;P&gt;My note was only about SAS/SQL syntax. I wouldn't advocate using SQL for such a problem, unless perhaps if the data resides in a distant DBMS, in the hope that SQL could get the server to perform the query.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;I would be interested in a timing comparison between your proposed hash- and array-based solutions.&lt;/P&gt;</description>
    <pubDate>Mon, 30 Jul 2018 19:05:00 GMT</pubDate>
    <dc:creator>PGStats</dc:creator>
    <dc:date>2018-07-30T19:05:00Z</dc:date>
    <item>
      <title>how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482330#M124906</link>
      <description>&lt;P&gt;Hello,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I want to add some rows in my dataset 'eq'. What I want is y should&amp;nbsp;include '.&amp;nbsp; 0 1 2'. If one of them is missing in eq, then add back it in 'want', and x will be 0.&amp;nbsp;&lt;/P&gt;&lt;P&gt;Actually, I have many IDs. Could anyone tell me how to do it?&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Thanks&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;data eq;
input ID y x ;
cards;
1 1 27 
1 0 . 
1 . 30 
1 2 38 
2 . 23 
2 0 32  
2 2 . 
3 0 33 
3 1 21 
3 2 13 
4 1 56 
4 0 67 
;
run;

want
1 1 27 
1 0 . 
1 . 30 
1 2 38 
2 . 23 
2 0 32 
2 1 0 
2 2 . 
3 0 33 
3 1 21 
3 . 0 
3 2 13  
4 1 56 
4 0 67   
4 . 0
4 2 0  &lt;/CODE&gt;&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 01:54:27 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482330#M124906</guid>
      <dc:creator>xiangpang</dc:creator>
      <dc:date>2018-07-30T01:54:27Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482332#M124908</link>
      <description>&lt;P&gt;I'm having a very difficult time trying to understand what your question really is... Can you elaborate a little more or may be rephrase the question?&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 02:28:59 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482332#M124908</guid>
      <dc:creator>mjabed600</dc:creator>
      <dc:date>2018-07-30T02:28:59Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482334#M124909</link>
      <description>&lt;P&gt;Sorry for that.&amp;nbsp;&lt;/P&gt;&lt;P&gt;Original data has some missing data. For each ID, they should have one of the 4 different y&amp;nbsp; (. or 0 or 1 or 2), which means 4 rows for each ID. so I need to add the missing row back. When the missing row added back, the value of x will be 0. I am not sure whether I explained it clear now.&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 02:43:44 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482334#M124909</guid>
      <dc:creator>xiangpang</dc:creator>
      <dc:date>2018-07-30T02:43:44Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482338#M124913</link>
      <description>&lt;P&gt;If you don't mind reordering the y's :&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;data eq;
input ID y x ;
cards;
1 1 27 
1 0 . 
1 . 30 
1 2 38 
2 . 23 
2 0 32  
2 2 . 
3 0 33 
3 1 21 
3 2 13 
4 1 56 
4 0 67 
;


data want;
array v{0:3} _temporary_;

do i = lbound(v) to hbound(v); v{i} = 0; end;

do until(last.id);
    set eq; by id;
    v{coalesce(y, 3)} = x;
    end;

do y = ., 0, 1, 2;
    x = v{coalesce(y, 3)};
    output;
    end;

drop i;
run;

proc print data=want noobs; run;&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Mon, 30 Jul 2018 03:16:26 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482338#M124913</guid>
      <dc:creator>PGStats</dc:creator>
      <dc:date>2018-07-30T03:16:26Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482341#M124916</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/194581"&gt;@xiangpang&lt;/a&gt;,&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;There're many ways to do what you want. I'll show primarily how a hash table can be used to keep track of what you have and what - not.&amp;nbsp;&lt;/P&gt;&lt;P&gt;First, if your data are actually sorted by ID (as in your sample):&lt;/P&gt;&lt;PRE&gt;data eq ;                          
  input ID y x ;                   
  cards ;                          
1 1 27                             
1 0  .                             
1 . 30                             
1 2 38                             
2 . 23                             
2 0 32                             
2 2  .                             
3 0 33                             
3 1 21                             
3 2 13                             
4 1 56                             
4 0 67                             
;                                  
run ;                              
                                   
data want ;                        
  if _n_ = 1 then do ;             
    dcl hash h (multidata:"y") ;   
    h.definekey ("y") ;            
    h.definedone () ;              
  end ;                            
  set eq ;                         
  by ID ;                          
  output ;                         
  h.add() ;                        
  if last.ID ;                     
  x = 0 ;                          
  do y = . , 0 to 2 ;              
    if h.check() ne 0 then output ;
  end ;                            
  h.clear() ;                      
run ;                              &lt;/PRE&gt;&lt;P&gt;If EQ is not initially ordered:&lt;/P&gt;&lt;PRE&gt;data _null_ ; &lt;BR /&gt; dcl hash h (multidata:"y", ordered:"a") ;&lt;BR /&gt; h.definekey ("id", "y") ; &lt;BR /&gt; h.definedata ("id", "y", "x") ; &lt;BR /&gt; h.definedone () ; &lt;BR /&gt; dcl hash a () ; &lt;BR /&gt; a.definekey ("id") ; &lt;BR /&gt; a.definedone () ; &lt;BR /&gt; dcl hiter i ("a") ; &lt;BR /&gt; do until (z) ; &lt;BR /&gt;   set eq end = z ; &lt;BR /&gt;   h.add() ; &lt;BR /&gt;   a.ref() ; &lt;BR /&gt; end ; &lt;BR /&gt; x = 0 ; &lt;BR /&gt; do while (i.next() = 0) ; &lt;BR /&gt;   do y = . , 0 to 2 ; &lt;BR /&gt;     h.ref() ; &lt;BR /&gt;   end ; &lt;BR /&gt; end ; &lt;BR /&gt; h.output (dataset:"want") ; &lt;BR /&gt;run ; &lt;BR /&gt;                 &lt;/PRE&gt;&lt;P&gt;A nice extra of this step is that your data will come out sorted by both ID and Y.&lt;/P&gt;&lt;P&gt;If you're averse to using the SAS hash object, the same as in step&amp;nbsp; #! can be done using olde goode arrays (minisclule since you only have 4 values to track). The following again assumes that EQ is sorted by ID:&lt;/P&gt;&lt;PRE&gt;data want (keep = ID x y) ;          
  array _f [-1:2] ;                  
  array _v [-1:2] (. 0 1 2) ;        
  do until (last.id) ;               
    set eq ;                         
    by ID ;                          
    output ;                         
    if nmiss (y)       then _f[-1] = 1 ;   
    else if y in (0:2) then _f[ y] = 1 ;   
  end ;                              
  x = 0 ;                            
  do j = lbound (_f) to hbound (_f) ;
    if _f[j] then continue ;         
    y = _v[j] ;                      
    output ;                         
  end ;                              
run ;                                &lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;HTH&lt;/P&gt;&lt;P&gt;Pail D.&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 04:23:53 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482341#M124916</guid>
      <dc:creator>hashman</dc:creator>
      <dc:date>2018-07-30T04:23:53Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482356#M124918</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/462"&gt;@PGStats&lt;/a&gt;,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Extremely ingenious. Kudos!&amp;nbsp;And I'm sure you realize that it's constrained by the assumptions that:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;(a) EQ has no records with Y not in (. 0 1 2)&amp;nbsp; &amp;nbsp;&lt;/P&gt;&lt;P&gt;(b) EQ has no variables but ID, X, and Y.&amp;nbsp; &amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;But within the limits of the sample data as presented, I can't think of anything more clever or concise.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Best&lt;/P&gt;&lt;P&gt;Paul D.&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 04:45:37 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482356#M124918</guid>
      <dc:creator>hashman</dc:creator>
      <dc:date>2018-07-30T04:45:37Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482367#M124919</link>
      <description>&lt;P&gt;Another way:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc sql; 
  create table WANT as
    select  a.ID
          , b.Y
          , coalesce(c.X, 0) as X
    from (select unique ID from EQ)  a
        full outer join
         (select unique Y  from EQ)  b
         on 1
        left join
        EQ                           c
        on  a.ID = c.ID
        and b.Y  = c.Y
    order by 1,2;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;DIV class="branch"&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure SQL: Query Results" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="r b header" scope="col"&gt;ID&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Y&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;X&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;.&lt;/TD&gt;
&lt;TD class="r data"&gt;30&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;27&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;38&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;.&lt;/TD&gt;
&lt;TD class="r data"&gt;23&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;TD class="r data"&gt;32&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;TD class="r data"&gt;.&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;TD class="r data"&gt;33&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;21&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;13&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;TD class="r data"&gt;.&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;TD class="r data"&gt;67&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;TD class="r data"&gt;56&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 05:35:43 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482367#M124919</guid>
      <dc:creator>ChrisNZ</dc:creator>
      <dc:date>2018-07-30T05:35:43Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482368#M124920</link>
      <description>&lt;P&gt;A variation:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc sql; 
    select  a.ID
          , b.Y
          , sum(X, 0) as X
    from (select unique ID from EQ) a
        inner join
         (select unique Y  from EQ) b
         on 1
        left join
         EQ                          c
         on  a.ID = c.ID
         and b.Y  = c.Y
    order by 1,2;
quit;&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Mon, 30 Jul 2018 05:39:26 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482368#M124920</guid>
      <dc:creator>ChrisNZ</dc:creator>
      <dc:date>2018-07-30T05:39:26Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482549#M125020</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/16961"&gt;@ChrisNZ&lt;/a&gt;, Note, to request a cross product, you can replace &lt;STRONG&gt;(...) inner join (...) on 1&lt;/STRONG&gt; by &lt;STRONG&gt;(...) cross join (...)&lt;/STRONG&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 17:32:15 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482549#M125020</guid>
      <dc:creator>PGStats</dc:creator>
      <dc:date>2018-07-30T17:32:15Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482570#M125022</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/462"&gt;@PGStats&lt;/a&gt;,&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Sure; but it still a Cartesian product. A thought in a different direction would be: Is it possible, in this case, to reformulate the query to make the optimizer avoid it. One could speculate that it could take advantage of the sorted input if prompted with SortedBy=ID, yet it doesn't and, judging from the _method messaging, still sorts behind-the-scenes. That said, even with the Cartesian product, @ChrisNZ 's query performs tolerably well against a data set with a couple of million IDs and 10 numeric variables added as ballast, about 10 secs flat on my laptop. But since DATA step by-processing gets there in 1/4 of the time, I guess the optimizer may not be smart enough to opt for a better optimized path (or maybe there's just no provision for it).&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Paul D.&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 18:39:56 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482570#M125022</guid>
      <dc:creator>hashman</dc:creator>
      <dc:date>2018-07-30T18:39:56Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482574#M125024</link>
      <description>&lt;P&gt;My note was only about SAS/SQL syntax. I wouldn't advocate using SQL for such a problem, unless perhaps if the data resides in a distant DBMS, in the hope that SQL could get the server to perform the query.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;I would be interested in a timing comparison between your proposed hash- and array-based solutions.&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 19:05:00 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482574#M125024</guid>
      <dc:creator>PGStats</dc:creator>
      <dc:date>2018-07-30T19:05:00Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482618#M125037</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/462"&gt;@PGStats&lt;/a&gt;&amp;nbsp;Thanks. I never used &lt;FONT face="courier new,courier"&gt;cross join&lt;/FONT&gt; before. One more in the toolbox... &lt;span class="lia-unicode-emoji" title=":slightly_smiling_face:"&gt;🙂&lt;/span&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 21:27:45 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482618#M125037</guid>
      <dc:creator>ChrisNZ</dc:creator>
      <dc:date>2018-07-30T21:27:45Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482619#M125038</link>
      <description>&lt;P&gt;It's a small Cartesian product (one column in each table, unique values only) and it's very legible.&lt;/P&gt;
&lt;P&gt;Unless performance is actually an issue, I'd go for legible (and I care a lot about performance!).&lt;/P&gt;
&lt;P&gt;A data step performs a single sequential read, so will be faster, but the guy who&amp;nbsp;comes after you will curse you for spending so much time understanding the logic rather than reading a very simple join.&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 21:33:53 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482619#M125038</guid>
      <dc:creator>ChrisNZ</dc:creator>
      <dc:date>2018-07-30T21:33:53Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482631#M125043</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/462"&gt;@PGStats&lt;/a&gt;,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Sure, eager to oblige:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;data eq (keep = ID X Y) ;               
  retain r "integer" ;                  
  call streaminit (7) ;                 
  array yy [4] _temporary_ (. 0 1 2) ;  
  do ID = 1 to 5E6 ;                    
    do _n_ = 1 to rand (r, dim (yy)) ;  
      Y = yy (rand (r, dim (yy))) ;     
      X = rand (r, 99) ;                
      output ;                          
    end ;                               
  end ;                                 
run ;                                   
                                        
data kxarr (keep = ID x y) ;            
  array _f [-1:2] ;                     
  array _v [-1:2] (. 0 1 2) ;           
  do until (last.id) ;                  
    set eq ;                            
    by ID ;                             
    output ;                            
    if nmiss (y)       then _f[-1] = 1 ;
    else if y in (0:2) then _f[ y] = 1 ;
  end ;                                 
  x = 0 ;                               
  do j = lbound (_f) to hbound (_f) ;   
    if _f[j] then continue ;            
    y = _v[j] ;                         
    output ;                            
  end ;                                 
run ;  
                                 
data hash ;                       
  if _n_ = 1 then do ;             
    dcl hash h () ;                
    h.definekey ("y") ;            
    h.definedone () ;              
  end ;                            
  do until (last.id) ;             
    set eq ;                       
    by ID ;                        
    output ;                       
    h.ref() ;                      
  end ;                            
  x = 0 ;                          
  do y = . , 0 to 2 ;              
    if h.check() ne 0 then output ;
  end ;                            
  h.clear() ;                      
run ;      &lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Results (in seconds):&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;1. Key-indexed array: 3.96&lt;/P&gt;&lt;P&gt;2. Hash object: 12.66&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;A pretty stark difference, isn't it? Sure, but it stands to reason: Key-indexing, within its range limitations, is the fastest search algorithm there is because:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;- There's no hash function overhead.&lt;/P&gt;&lt;P&gt;- A value is simply assigned to the array cell whose index is equal to the key-value.&lt;/P&gt;&lt;P&gt;- There's no need to search before "inserting" the value - it merely overwrites what's there.&lt;/P&gt;&lt;P&gt;- There's no cost of run-time memory allocation, as it is allocated at compile time.&lt;/P&gt;&lt;P&gt;- There's practically no cost cleaning it up after each BY group.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;As opposed to that, the hash table needs to:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;- Compute the hash function.&lt;/P&gt;&lt;P&gt;- Search the table to see if the value is already in the table and make a decision based on this finding and the specs.&lt;/P&gt;&lt;P&gt;- Traverse an AVL tree before inserting the value. Though pretty fast, it's not nearly as fast as just sticking it into an array.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;- Allocate memory at run time for each new key-value being added.&lt;/P&gt;&lt;P&gt;- Wipe the contents of the table out after each BY group. Again, it costs much more than setting the array values to missing.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Can the hash table be made perform better? Sure it can. Here the heaviest burden comes from the excessive clean-up since under this particular arrangement the BY-groups are very numerous and the CLEAR method gets called as many times as there are distinct IDs. However, the groups are small and the hash memory footprint isn't an issue, so to improve performance, we can add ID to the key portion and clean the table after each Nth BY group. The value of N needs to be chosen optimally: If we set it too high, we'll increase the time needed for memory allocation, as a fuller table requires more time for that, plus letting the table get too big between the successive Ns means more cleaning effort. It's sort of like striking a balance between cleaning a house way too often and way too rarely: If we clean every minute, we will do nothing but clean; and if we do it every few months ... you get the picture. Having tinkered a little before posting, I found the optimal value to be N~50 and also zeroed in on hashexp:9 (instead of the default 8). Thus, the code needs only minor changes (in red bold):&lt;/P&gt;&lt;PRE&gt;data hash ;                            
  if _n_ = 1 then do ;                 
    dcl hash h (&lt;FONT color="#FF0000"&gt;&lt;STRONG&gt;hashexp:9&lt;/STRONG&gt;&lt;/FONT&gt;) ;                    
    h.definekey (&lt;FONT color="#FF0000"&gt;&lt;STRONG&gt;"ID"&lt;/STRONG&gt;&lt;/FONT&gt;, "y") ;          
    h.definedone () ;                  
  end ;                                
  do until (last.id) ;                 
    set eq ;                           
    by ID ;                            
    output ;                           
    h.ref() ;                          
  end ;                                
  x = 0 ;                              
  do y = . , 0 to 2 ;                  
    if h.check() ne 0 then output ;    
  end ;                                
  &lt;FONT color="#FF0000"&gt;&lt;STRONG&gt;if not mod (_n_, 50) then&lt;/STRONG&gt;&lt;/FONT&gt; h.clear() ;
run ;                                  &lt;/PRE&gt;&lt;P&gt;Simply reducing the clean-up frequency in this manner reduces the hash run time to 7 seconds flat. While still a far cry from the key-index's 3.96 seconds (for the reasons we can't control), it's a huge improvement against 12.66.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The advantage of the hash object lies in is its much wider applicability. If instead of tracking Y ranging from -1 to 2 we had to track, say, Y1-Y10 with a integer value range exceeding ~1E8 (let alone if they were character variable longer than $3), we wouldn't be able to key-index within reasonable memory constraints, while the hash table would still work - we would only have to augment it accordingly.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Best&lt;/P&gt;&lt;P&gt;Paul D.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 22:26:04 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482631#M125043</guid>
      <dc:creator>hashman</dc:creator>
      <dc:date>2018-07-30T22:26:04Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482650#M125053</link>
      <description>&lt;P&gt;In general, I agree with you in terms of legibility vs insignificant differences in performance (though I care a lot about performance, too). In this case, however, it's very simple DATA step code with rather straightforward standard BY processing logic:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;For each BY group:&lt;/P&gt;&lt;P&gt;- output every record as is.&amp;nbsp;&lt;/P&gt;&lt;P&gt;- store Y in a table (hash or otherwise)&lt;/P&gt;&lt;P&gt;After each BY group:&lt;/P&gt;&lt;P&gt;- Output a record with X=0 for each value of (. 0 1 2) not found in the table&lt;/P&gt;&lt;P&gt;- Clean the table&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;This kind of logic is&amp;nbsp;bread and butter of any SAS programmer. If one understands the task, this logic is obvious, and, conversely, the nature of the task can be easily understood from this kind of code. Besides, "the guy who comes after" me would read my comments explaining both before starting to read the code. As to whether SQL is more legible than an equivalent DATA step, it depends on the nature of the task and the background of whomever is trying to comprehend it. With mine, if I were to reconstruct this particular task from code alone, I'd do it far more readily from the DATA step in question than from your query; for other people (such as those who had learned SQL before procedural programming), it may be just the opposite. Suum cuique.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Best&lt;/P&gt;&lt;P&gt;Paul D.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 23:51:29 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482650#M125053</guid>
      <dc:creator>hashman</dc:creator>
      <dc:date>2018-07-30T23:51:29Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482652#M125055</link>
      <description>&lt;P&gt;&lt;SPAN&gt;&lt;EM&gt;Suum cuique&lt;/EM&gt; indeed &lt;span class="lia-unicode-emoji" title=":slightly_smiling_face:"&gt;🙂&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 30 Jul 2018 23:55:33 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482652#M125055</guid>
      <dc:creator>ChrisNZ</dc:creator>
      <dc:date>2018-07-30T23:55:33Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482658#M125059</link>
      <description>&lt;P&gt;Thanks everyone. My case is more complex than the sample. But I am pleased to learn the basic idea from you guys. I did not know hash before. Is there any book recommended for a beginner?&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Thanks again.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 31 Jul 2018 00:18:02 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482658#M125059</guid>
      <dc:creator>xiangpang</dc:creator>
      <dc:date>2018-07-31T00:18:02Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482674#M125067</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/21262"&gt;@hashman&lt;/a&gt;, Wow, that's a very nice demonstration, and some very instructive explanations. Please continue to impress us with hash based solutions. Speaking for myself, this is one area where I don't feel so confident. Thank you.&lt;/P&gt;</description>
      <pubDate>Tue, 31 Jul 2018 02:40:58 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482674#M125067</guid>
      <dc:creator>PGStats</dc:creator>
      <dc:date>2018-07-31T02:40:58Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482676#M125069</link>
      <description>&lt;P&gt;Hope you don't mind if I recommend the book coauthored by myself. I can honestly aver that I've read it from cover to cover :).&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;A href="https://www.sas.com/store/books/categories/examples/data-management-solutions-using-sas-hash-table-operations-a-business-intelligence-case-study/prodBK_69153_en.html" target="_blank"&gt;https://www.sas.com/store/books/categories/examples/data-management-solutions-using-sas-hash-table-operations-a-business-intelligence-case-study/prodBK_69153_en.html&lt;/A&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;As to the level, it only assumes that the reader is fairly competent in DATA step programming. As far as the hash object is concerned, it starts ab ovo, and immersion grows as you progress through. We've also endeavored to present the subject in a systematic way from the standpoint of general table operations (such as CRUD) and avoid aping SAS documentation like plague.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Paul D.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 31 Jul 2018 02:41:52 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482676#M125069</guid>
      <dc:creator>hashman</dc:creator>
      <dc:date>2018-07-31T02:41:52Z</dc:date>
    </item>
    <item>
      <title>Re: how to add a row</title>
      <link>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482680#M125070</link>
      <description>&lt;P&gt;Thanks for your patience and help&lt;/P&gt;</description>
      <pubDate>Tue, 31 Jul 2018 03:02:20 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/how-to-add-a-row/m-p/482680#M125070</guid>
      <dc:creator>xiangpang</dc:creator>
      <dc:date>2018-07-31T03:02:20Z</dc:date>
    </item>
  </channel>
</rss>

