4 votes

Ne conservez l'identification des doublons que s'il n'y a pas de nouvelles informations.

Je veux conserver tous les identifiants en double, sauf s'il n'y a pas de nouvelles informations en raison de valeurs manquantes. Par exemple,

data test;
input id var1 var2 var3
    datalines;
    1 2 3 4
    1 4 . 4
    1 6 5 4
    1 . 3 .
    1 2 4 4
    1 6 . 4
    1 . 8 4
    ;
run;

Je veux que le résultat soit

1 2 3 4
1 4 . 4
1 6 5 4
1 2 4 4
1 . 8 4    

La rangée 4 est supprimée car la rangée 1 a le même id, var2 et var3. La rangée 6 est supprimée parce que la rangée 3 a le même id, var1, var3. Je souhaite également une solution robuste, car je veux que la solution fonctionne pour n'importe quel nombre de variables dans l'ensemble de données (id serait toujours la clé unique).

Des idées ? Je pensais à sort nodupkey mais cela ne fonctionne pas s'il y a plus d'une valeur manquante dans une ligne.

3voto

Sanek Zhitnik Points 353

Vous pouvez extraire les lignes, qui ont un ou plusieurs manquants. Dans un deuxième temps, vous devez générer des RegExp, qui vous aideront à identifier les lignes similaires.

Le code peut être amélioré par vos suggestions.

data test;
input id var1 var2 var3;
    datalines;
    1 2 3 4
    1 4 . 4
    1 6 5 4
    1 . 3 .
    1 2 4 4
    1 6 . 4
    1 . 8 4
    ;
run;

data test2 missing;
/*incrase this strings if you have big values*/
length res $ 200 addedEl $ 10;
    set test;
    array num _NUMERIC_;

    /*add flag  to determine is there missin in row*/
    flag=0;
    do i=1 to dim(num);
        addedEl=compress(put(num(i),8.));
        if num(i)=. then
            do;
                flag=1;
                /*template for number. If you have comma separated vars then replace on \d+\.\d*        */
                addedEl="\d+";
            end;
        /*add delimeter to row parse, if you have more than one digits in vars =)*/
        res=catx("_",res,addedEl);
    end;

    if flag=0 then  output test2;
    else    do;
        res=catt("/",res,"/");
        output missing;
    end;

    drop i flag addedEl;
run;

/*determine rows that dublicates*/
proc sql noprint;
create table matched as
  select  B.* 
          ,prxparse(B.res) as prxm 
          ,A.*
  from  test2 as A
        ,missing as B
  where prxmatch(calculated prxm,A.res)
  order by B.res;
quit;
run;

/*pre-merge sort*/
proc sort data=missing;
    by res;
run;

/*delete rows that are in second dataset*/
data miss_correctred;
    merge missing(in=mss)
        matched(in=mtch)
    ;
    by res;

    if mss=1 and mtch=0;
run;

data test_res(drop=prxm res);
    set test2 miss_correctred;
run;

résultat :

+----+------+------+------+
| id | var1 | var2 | var3 |
+----+------+------+------+
|  1 |    2 |    3 |    4 |
|  1 |    6 |    5 |    4 |
|  1 |    2 |    4 |    4 |
|  1 |    4 |    . |    4 |
|  1 |    . |    8 |    4 |
+----+------+------+------+

2voto

user667489 Points 671

Voici le schéma d'une approche de hachage à une étape de données + double DOW :

  • Pour chaque id :
    • Créer un objet de hachage + itérateur avec toutes les variables comme clés
    • Tente de charger toutes les lignes pour cet id dans le hachage. Ceci est équivalent à une passe initiale avec proc sort nodupkey.
    • Effectuez un deuxième passage par toutes les lignes pour le même id (double DOW), en ignorant celles qui ne contiennent que des valeurs non manquantes.
      • Compter chaque ligne comme un doublon par défaut
      • Pour chaque paire de variables ayant au moins une valeur non manquante dans la ligne actuelle :
        • Vérifiez si cette paire de valeurs est présente dans tout élément précédent du hachage.
      • Marquer la ligne comme non dupliquée si nous trouvons au moins une paire de valeurs qui ne correspondent à aucun endroit dans le hachage. Traiter les valeurs manquantes dans la ligne actuelle comme des correspondances. Dès qu'une ligne est marquée comme non-dupliquée, nous pouvons passer à la ligne suivante.

Je pense que dans le pire des cas, c'est O(n^4), mais s'il y a une forte proportion de doublons, cela devrait faire mieux.

Mise à jour :

Voici un exemple de mise en œuvre - c'était en effet assez désordonné :

proc sql noprint;
  select 
    quote(trim(name)), 
    name,
    count(name) 
  into 
    :varlist separated by ',', 
    :arraylist separated by ' ',
    :varcount
  from dictionary.columns 
  where 
    libname = 'WORK' 
    and memname = 'TEST' 
    and type = 'num'
    and name ne 'id'
  ; 
quit;

data want;
  /*Set up arrays*/
  if 0 then set test;
  array vars[*] &arraylist;
  array temp[&varcount] _temporary_;
  length sub_id 8;
  keep id &arraylist;

  /*Set up hash + iterator*/
  if _n_ = 1 then do;
    declare hash h(ordered:'a');
    rc = h.definekey('sub_id', &varlist);
    rc = h.definedata('sub_id', &varlist);
    rc = h.definedone();
    declare hiter hi('h');
  end;

  /*DOW #1 - load hash and output definite non-duplicates*/
  do _n_ = 1 by 1 until(last.id);
    set test;
    by id;
    /*We need a way to keep track of rows within each id so that we don't count rows as duplicates when they match themselves in DOW #2*/
    sub_id = _n_;
    rc = h.add();
    if rc = 0 and nmiss(of vars[*]) = 0 then output;
  end;

  /*DOW #2 - check for any previously unseen pairs of values*/
  do _n_ = 1 to _n_;
    set test;
    /*Make a copy of the current row to retrieve after looping through the hash iterator*/
    do i = 1 to dim(vars);
      temp[i] = vars[i];
    end;
    if nmiss(of vars[*]) > 0 then do;
      dup_flag = 1;
      /*Work through successive pairs of values*/
      do i = 1 to dim(vars) while(dup_flag = 1);
        do j = 1 to i - 1 while(dup_flag = 1);
          __v_i = temp[i];
          __v_j = temp[j];
          match_flag = 0;
          /*For each pair, loop through the iterator until we find a 'match'*/
          rc = hi.first();
          do while(rc = 0 and match_flag = 0 and sub_id < _n_);            
            if    (missing(__v_i) or __v_i = vars[i])
              and (missing(__v_j) or __v_j = vars[j])
              then match_flag = 1;
            rc = hi.next();
          end;
          /*If we didn't find a match, we have a new combination and the row is not a duplicate*/
          if match_flag = 0 then dup_flag = 0;
        end;
      end;
      if dup_flag = 0 then do;
        do i = 1 to dim(vars);
          vars[i] = temp[i];
        end;
        output;
      end;
    end;
  end;
  rc = h.clear();
run;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X