DATA Step, Macro, Functions and more

Question on SAS allperm function

Reply
N/A
Posts: 0

Question on SAS allperm function

Hi I would like to know if SAS can generates all possible permutations of 2, 3, 4, 5 etc..instead of just all permutations of 6 like the code below.

data test;
drop i perms;
array x (6) $6 ('a' 'b' 'c' 'd' 'e' 'f');

/* Create a new variable of the concatenated results */
length new $6;
perms=fact(6);
do i=1 to perms;
call allperm(i, of x(*));
new=cats(of x(*));
output;
end;
run;

proc print;
run;

Many Thanks
Respected Advisor
Posts: 3,799

Re: Question on SAS allperm function

Posted in reply to deleted_user
My limited testing of your problem using ALLPERM indicated that ALLPERM must be called with the number of arguments equal to the number of things to be permuted. You need to use separate arrays and separate calls to ALLPERM.

If this question is related to your other question about "building" IN operator arguments I think you should explain the problem more completely because maybe you don't need all these permutations.

Here is an alternative method to generate permutations that may be of some interest.

[pre]
*ods trace on;
ods listing close;
proc plan ordered;
ods output plan=plan;
factors rep=%sysfunc(fact(1)) i=1 perm;
factors rep=%sysfunc(fact(2)) i=2 perm;
factors rep=%sysfunc(fact(3)) i=3 perm;
factors rep=%sysfunc(fact(4)) i=4 perm;
factors rep=%sysfunc(fact(5)) i=5 perm;
factors rep=%sysfunc(fact(6)) i=6 perm;
run;
ods listing;
ods trace off;

data perm;
set plan;
array x[6] $6 ('a' 'b' 'c' 'd' 'e' 'f');
array i
  • i:;
    n = n(of i
  • );
    length perm $6;
    do _n_ = 1 to n;
    perm = cats(perm,x[i[_n_]]);
    end;
    run;
    proc print;
    run;
    proc sql noprint;
    select quote(strip(perm)) into Smiley Tongueerm1 separated by ' ' from perm where n eq 1;
    select quote(strip(perm)) into Smiley Tongueerm2 separated by ' ' from perm where n eq 2;
    select quote(strip(perm)) into Smiley Tongueerm3 separated by ' ' from perm where n eq 3;
    select quote(strip(perm)) into Smiley Tongueerm4 separated by ' ' from perm where n eq 4;
    select quote(strip(perm)) into Smiley Tongueerm5 separated by ' ' from perm where n eq 5;
    select quote(strip(perm)) into Smiley Tongueerm6 separated by ' ' from perm where n eq 6;
    quit;
    run;
    %put _user_;
    [/pre]
  • N/A
    Posts: 0

    Re: Question on SAS allperm function

    Posted in reply to data_null__
    Merci beaucoup data _null_;

    This question is related to an earlier question

    I do need all permutations to complete this task

    data test;
    set test;
    if new_var in ("aa bb","bb aa") then number='01';
    if new_var in ("aa bb cc","aa cc bb","bb aa cc","bb cc aa","cc aa bb","cc bb aa") then number='02';
    and so on.

    and currently I doing it with hundreds of if then...if then....with all combinations for 2, and 3 already but the scope is just too much when I move on to 4, 5 and 6. I hoping someone could show me how to do this without resort to thousands of if then...if then statement.

    Have a great day Smiley Happy
    Frequent Contributor
    Posts: 102

    Re: Question on SAS allperm function

    Posted in reply to deleted_user
    This is thinking a little outside of the box, and I don't completely understand your problem, but the following would work in your example:
    -----------------------------------------
    data test;
    set test;
    number = put(countw(new_var) - 1,Z2.);
    run;
    ------------------------------------------------------
    Are you really just getting a count of words in the string?
    N/A
    Posts: 0

    Re: Question on SAS allperm function

    Posted in reply to CurtisMack
    Hi CurtisMack, what I intend is to capture all possible variations of aa bb cc dd ee ff in groups of 2, 3, 4, 5 and 6 which exists in my data to assign a group number. So far the hurdles are

    1) Generates all permutations of these 6 words in a dataset like

    Col1 Col2 Col3 Col4 ... Perm1 Perm2 Perm3 Perm4 Perm5 Perm6 ....
    aa bb aa,bb bb,aa
    aa bb cc aa,bb,cc| aa,cc,bb|bb,aa,cc|bb,cc,aa|cc,aa,bb|cc,bb,aa
    .
    .
    .
    aa bb cc dd

    and concatenate all the Perm into one string which will be use in the IN("STRING")

    data test;
    set test;
    if new_var in ("aa bb","bb aa") then number='01';
    if new_var in ("aa bb cc","aa cc bb","bb aa cc","bb cc aa","cc aa bb","cc bb aa") then number='02';
    and so on.

    Hope this will clear up some confusion.

    Thank you
    Frequent Contributor
    Posts: 102

    Re: Question on SAS allperm function

    Posted in reply to deleted_user
    Unfortunately it still looks like you are trying to determine how many values were included in a particluar permutation. If that is the case, the code I posted will give you the results you want and you can skip all of the other steps.

    Although I still don't understand the purpose, here is something I whipped up that will create a dataset like the one you described.
    -------------------------------------------
    data test;
    Col1 = "aa";
    Col2 = "bb";
    Col3 = "cc";
    Col4 = "dd";
    Col5 = "ee";
    Col6 = "ff";
    output;
    run;

    proc means data=test chartype;
    class Col1 Col2 Col3 Col4 Col5 Col6;
    output out = premu(where = (length(compress(_type_,"0")) > 1));
    run;


    data premu2;
    set premu;
    array Col [6];
    array Prem [%sysfunc(fact(6))] $ 400;
    n=length(compress(_type_,"0"));
    nfact=fact(n);
    do i=1 to nfact;
    rc = LEXPERM(i, of Col
  • );
    Prem(i) = catx(' ',of Col
  • );
    end;

    run;
  • N/A
    Posts: 0

    Re: Question on SAS allperm function

    Posted in reply to CurtisMack
    Brilliant!! That is exactly what I needed, is there a way to code up the 57 lines without resorting to if then...if then....57 times CurtisMack?

    Ta
    Frequent Contributor
    Posts: 102

    Re: Question on SAS allperm function

    Posted in reply to deleted_user
    Although we are still just returning the number of words in the string, I wouldn't code it out the way you describe anyways. I would create a format from the permutations and use that to assign the groups. That way you wouldn't even need to create the new "number" variable at all. You would just use the format in any of the output or proc calls. Here is how you could replicate that last step. There are no changes to the proc means call so I did not repeat it.
    -----------------------------------------------------------
    data premu2;
    set premu;
    retain fmtName 'Prem' type 'C';
    array Col [6];
    length Start $ 400;
    n=length(compress(_type_,"0"));
    Label = put(n,z2.);
    nfact=fact(n);
    do i=1 to nfact;
    rc = LEXPERM(i, of Col
  • );
    Start = catx(' ',of Col
  • );
    output;
    end;
    run;

    proc format cntlin=premu2;
    run;

    data test;
    length new_var $ 200;
    new_var = "aa bb";output;
    new_var = "bb aa";output;
    new_var = "aa bb cc";output;
    new_var = "aa cc bb";output;
    new_var = "bb aa cc";output;
    new_var = "bb cc aa";output;
    new_var = "cc aa bb";output;
    new_var = "cc bb aa";output;
    run;
    data test;
    set test;
    number = put(new_var,Prem.);
    run;
  • Frequent Contributor
    Posts: 102

    Re: Question on SAS allperm function

    Posted in reply to CurtisMack
    If you really do need those if statements written out, this is a way to do it using Call execute. It could be done a number of other ways such as pseudo macro arrays, or using the SCL OPEN statement in SYSFUNC calls, but I think this is the most straight forward. Call Execute generates SAS code that is executed once the dataset is complete.
    --------------------------------------------

    data _null_;
    set premu end = finished;
    if _n_ = 1 then do;
    call execute ('data test; set test;');
    end;
    array Col [6];
    length IfStr $ 32767;
    array Prem [%sysfunc(fact(6))] $ 400;
    n=length(compress(_type_,"0"));
    nfact=fact(n);
    do i=1 to nfact;
    rc = LEXPERM(i, of Col
  • );
    Prem(i) = catx(' ',of Col
  • );
    end;
    number = put(n,Z2.);
    IfStr = 'if new_var in ("'||catx('","',of Prem
  • )||'") then number = "' || number || '";';
    call execute(IfStr);
    if finished then do;
    call execute ('run;');
    end;
    run;
  • N/A
    Posts: 0

    Re: Question on SAS allperm function

    Posted in reply to CurtisMack
    This is way beyond what I expected, thank you so much CurtisMack, I have learned a great deal from you today, the way I envisage when you gave me the code earlier is something along the line using proc sql and arrays.

    Cheers
    N/A
    Posts: 0

    Re: Question on SAS allperm function

    Posted in reply to CurtisMack
    Just out of curiosity, I have never used SCL before what would be an example if I were to do this in SCL?

    Ta
    Frequent Contributor
    Posts: 102

    Re: Question on SAS allperm function

    Posted in reply to deleted_user
    SCL is a another of the many sub-languages in SAS. It was originally created for use in the AF/Frame world, but it can be very useful in base SAS programming. I find it particularly usefull when you need to read a small SAS data set, but you are either already in a Data Step, or are in the MACRO environment. This isn't the place to give you a lesson on how to use it, but here is the same solution implimented using it. I find the code much easier to read than the alternatives:
    ---------------------------------------------------------------------------
    data premu2(keep = IfStr number) ;
    set premu;
    array Col [6];
    length IfStr $ 32767;
    array Prem [%sysfunc(fact(6))] $ 400;
    n=length(compress(_type_,"0"));
    nfact=fact(n);
    do i=1 to nfact;
    rc = LEXPERM(i, of Col
  • );
    Prem(i) = catx(' ',of Col
  • );
    end;
    IfStr = '"'||catx('","',of Prem
  • )||'"';
    number = put(n,Z2.);
    run;

    options mprint;
    %macro PermTest;
    data test;
    set test;
    %let Id = %sysfunc(open(premu2,is));
    %syscall set(Id);
    %do %while(%sysfunc(fetch(&Id)) = 0);
    if new_var in(&IfStr) then number = "&number";
    else
    %end;
    %let rc = %sysfunc(close(&Id));
    put "Unexpected value of :" new_var;
    run;
    %mend;
    %PermTest;
    -------------------------------------------------------------
    Glad I could be of help. I am just trying to pass along the benefits I have recieved from many others in other forums.
    Good Luck!
    Curtis
  • Ask a Question
    Discussion stats
    • 11 replies
    • 288 views
    • 0 likes
    • 3 in conversation