Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Candidat au Club
    Selection couples d'individus puis suppression individus déjà pris
    Bonjour,

    Ma problématique consiste à associer les individus deux à deux tels qu'ils soient les plus proches c'est à dire que ma variable "cout" soit minimale sachant qu'un individus ne peut être sélectionné 2 fois.
    exemple :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    data have;
      input ind1 $ ind2 $ cout;
    datalines;
    1 A 2
    1 B 5 
    1 C 3
    2 A 1
    2 B 10 
    2 C 5
    3 A 5
    3 B 4 
    3 C 55
    ;


    Je calcule un cout minimum par ind1:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    proc sort data=have; by ind1 cout;run;
    proc summary data=have;var cout;by ind1;output out=min_ind1(drop=_FREQ_ _TYPE_) min=min_cout;run;
     
    data have;merge have min_ind1;by ind1;run;
     
    proc sort data=have; by descending min_cout;run;
     
    /*ensuite je calcule un compteur par ind1 et cout min*/
    data have; 
    set have;
    retain cpt;
    if _N_=1 then cpt=0;
    if ind1 ne lag(ind1) then  cpt=cpt+1;
    run ;

    Et je voudrais avoir en sortie une table qui contient:
    1 A 2
    3 B 4
    2 C 5

    Je ne vois pas comment faire à part faire de multiples data set qui balaye la table sachant que ma "vraie" table contient des milliers d'individus:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    %macro test();
    data reste;set have;run;
     
    data out;set _null_;run;
     
    %do i=1 %to 3; /*nombre d'individus 1*/
     
    proc sort data=reste; by cpt;run;
     
    data reste out_1; 	
    set reste;
    by cpt;
    if first.cpt and cpt=&i. then output out_1;
    if cpt=&i. then delete;output reste;
    run;
     
    data out; set out out_1;run;
     
    proc sort data=reste; by  ind2;run; 
    proc sort data=out; by  ind2;run;
     
    data reste; 
    merge reste(in=a) out(in=b);
    by  ind2;
    if a and not b;
    run;
    %end;
     
    %mend;
    %test;

    Avez vous une idée pour optimiser ce programme?
    Cordialement
    Mathilde

  2. #2
    Membre expérimenté
    Bonjour, l'optimisation est aisée, mais que se passe t il lorsque deux ind2 ont un même coût , lequel choisi t on ?

  3. #3
    Candidat au Club
    Bonjour,
    si deux individus ont le même coût minimum on choisit selon l'ordre des individus, donc je commence par l'individus 1 qui a le coût minimum(=coût minimum ind2) puis j'efface l'ind 1 et l'individu choisi (ex: A) puis je passe à l'individus 2...

    Mathilde

  4. #4
    Membre confirmé
    Bonjour,

    Un bout de code qui pourrait t'aider à résoudre ton problème :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    data have;
      input ind1 $ ind2 $ cout;
    datalines;
    1 A 2
    1 B 5 
    1 C 3
    2 A 1
    2 B 10 
    2 C 5
    3 A 5
    3 B 4 
    3 C 55
    ;
    Run;
     
    Proc sort data = have out = test;
    By ind1 cout;
    Run;
     
     
    Data test2 (drop = liste);
    Set test;
    By ind1;
    Length Liste $ 1000;
    Retain Liste;
    If Find(Liste,strip(ind1)) = 0 and Find(Liste,strip(ind2)) = 0 then do;
    	Liste = Catx(" ",Liste,Ind1, Ind2);
    	Output;
    End;
    Run;


    Je vois que tu utilises pas mal de "By" dans la proc summary par exemple, je te conseille d'utiliser "Class" à la place qui te permet de ne pas trier ta table en amont.
    Autre point, tu utilises la fonction Lag dans un instruction conditionnelle ce qui est à éviter car cette fonction peu être piégeuse si on ne maitrise pas son fonctionnement. Pour ce cas tu peux plutôt utiliser les marqueurs Fisrt et Last qui feront parfaitement le job.

    Bon courage

  5. #5
    Candidat au Club
    Bonjour
    merci beaucoup pour la réponse.
    Cela fonctionne bien sauf que j'ai des identifiants en chiffre et donc le find ne fonctionne pas quand j'ai une personne avec un numéro 13 par exemple et une autre en 130.

    Cordialement
    Mathilde

  6. #6
    Membre expérimenté
    Bonjour,

    normalement avec une configuration normale, tu dois pouvoir traiter un million d'individus sans trop de problème (tout dépend de ta longueur d'identifiant ind2 en fait).

    La recherche se fait en O(1) contrairement à la proposition de flo qui est en O(n) et est rapidement limitée en terme de stockage de clé déjà retenues.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    data have;  input ind1 $ ind2 $ cout;
    datalines;
    1 A 2
    1 B 5 
    1 C 3
    2 A 1
    2 B 10 
    2 C 5
    3 A 5
    3 B 4 
    3 C 55
    ;
     
     
    run;
     
     
    data to_Have_and_Have_not;
    set have;
    if _n_=1 then     do;
                declare hash YesWeCan();
                YesWeCan.definekey('ind2');
                YesWeCan.definedone();
     
                declare hash Data(ordered:'a');
                Data.definekey('ordre');
                Data.definedata('ordre','ind1','ind2','cout');
                Data.definedone();
                declare hiter HData('Data');
                    end;
     
     
    retain min_cout num_ordre;
    by ind1;
    ordre=_n_ ;
     
     
    if first.ind1     then do;    rcData=Data.clear();
                                min_cout=.;
                                num_ordre=.;
                         end;
    rcYesWeCan=YesWeCan.check();                
     
     
    if rcYesWeCan^=0 then     do;
                    if min_cout=. then     do;
                                    num_ordre=ordre;
                                    min_cout=cout;
                                        end;
                                else if cout<min_cout then     do;
                                                                num_ordre=ordre;
                                                                min_cout=cout;
                                                            end;
                    rcData=Data.add();
                            end;
     
     
     
    if last.ind1    then    do;
        if min_cout=.     then     do;
                                ind2="";cout=.;output;
                                end;
     
                        else    do;
                                      rcHData=HData.first();
                                      do while (rcHData=0);
                              if num_ordre=ordre then     do;
                                                          output;
                                                          RCYesWeCan=YesWeCan.add();
                                                          end;
                                      rcHData=HData.next();
                                      end;
                                  end;
                              end;
    keep ind1 ind2 cout;
    run;
     
     
    proc print data=to_Have_and_Have_not;run;



    [TH="class: r header, bgcolor: #EDF2F9, align: right"]Obs.[/TH]
    [TH="class: header, bgcolor: #EDF2F9, align: left"]ind1[/TH]
    [TH="class: header, bgcolor: #EDF2F9, align: left"]ind2[/TH]
    [TH="class: r header, bgcolor: #EDF2F9, align: right"]cout[/TH]
    [TH="class: r rowheader, bgcolor: #EDF2F9, align: right"]1[/TH]
    [TD="class: data, bgcolor: #FFFFFF, align: left"]1
    [TD="class: data, bgcolor: #FFFFFF, align: left"]A
    [TD="class: r data, bgcolor: #FFFFFF, align: right"]2
    [TH="class: r rowheader, bgcolor: #EDF2F9, align: right"]2[/TH]
    [TD="class: data, bgcolor: #FFFFFF, align: left"]2
    [TD="class: data, bgcolor: #FFFFFF, align: left"]C
    [TD="class: r data, bgcolor: #FFFFFF, align: right"]5
    [TH="class: r rowheader, bgcolor: #EDF2F9, align: right"]3[/TH]
    [TD="class: data, bgcolor: #FFFFFF, align: left"]3
    [TD="class: data, bgcolor: #FFFFFF, align: left"]B
    [TD="class: r data, bgcolor: #FFFFFF, align: right"]4

  7. #7
    Candidat au Club
    Merci.
    Je vois que je n'avait pas clôturé le sujet.
    Grâce à ton aide Jérôme, j'ai pu résoudre mon problème à l'époque.

###raw>template_hook.ano_emploi###