BookmarkSubscribeRSS Feed
deleted_user
Not applicable
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
11 REPLIES 11
data_null__
Jade | Level 19
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 :perm1 separated by ' ' from perm where n eq 1;
    select quote(strip(perm)) into :perm2 separated by ' ' from perm where n eq 2;
    select quote(strip(perm)) into :perm3 separated by ' ' from perm where n eq 3;
    select quote(strip(perm)) into :perm4 separated by ' ' from perm where n eq 4;
    select quote(strip(perm)) into :perm5 separated by ' ' from perm where n eq 5;
    select quote(strip(perm)) into :perm6 separated by ' ' from perm where n eq 6;
    quit;
    run;
    %put _user_;
    [/pre]
  • deleted_user
    Not applicable
    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 🙂
    CurtisMack
    Fluorite | Level 6
    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?
    deleted_user
    Not applicable
    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
    CurtisMack
    Fluorite | Level 6
    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;
  • deleted_user
    Not applicable
    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
    CurtisMack
    Fluorite | Level 6
    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;
  • CurtisMack
    Fluorite | Level 6
    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;
  • deleted_user
    Not applicable
    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
    deleted_user
    Not applicable
    Just out of curiosity, I have never used SCL before what would be an example if I were to do this in SCL?

    Ta
    CurtisMack
    Fluorite | Level 6
    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
  • sas-innovate-2024.png

    Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

    Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

     

    Register now!

    How to Concatenate Values

    Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

    Find more tutorials on the SAS Users YouTube channel.

    Click image to register for webinarClick image to register for webinar

    Classroom Training Available!

    Select SAS Training centers are offering in-person courses. View upcoming courses for:

    View all other training opportunities.

    Discussion stats
    • 11 replies
    • 1220 views
    • 0 likes
    • 3 in conversation