LesezeichenAbonnierenRSS-Feed abonnieren
Katrin11
Calcite | Level 5

Hallo zusammen,

 

ich habe einen Datensatz vorliegen, der vereinfacht folgendermaßen aufgebaut ist:

 

Identnummer      Umsatz

           1                   2

           2                   5

           3                   1

           3                   3

           4                   7

 

Nun möchte ich die doppelten Datensätze (gleiche Identnummer) löschen, und zwar soll immer der Datensatz mit dem größeren Umsatz vorhanden bleiben.

Das Ergebnis sollte also folgendermaßen aussehen:

 

Identnummer      Umsatz

           1                   2

           2                   5

           3                   3

           4                   7

 

Vorab schon vielen Dank für Eure Mühen und eure Hilfe.

 

Viele Grüße

 

Katrin

11 ANTWORTEN 11
Kurt_Bremser
Super User

Simple SQL:

data have;
input id umsatz;
datalines;
1 2
2 5
3 1
3 3
4 7
;

proc sql;
create table want as
select id, max(umsatz) as umsatz
from have
group by id;
quit;

Wenn es noch mehr Spalten gibt (ist wohl anzunehmen), macht man das mit "having":

data have;
input id umsatz sonst $;
datalines;
1 2 a
2 5 b
3 1 c
3 3 d
4 7 e
;

proc sql;
create table want as
select *
from have
group by id
having umsatz = max(umsatz);
quit;
CKothenschulte
Obsidian | Level 7

Hallo @Katrin11 ,

 

hier bietet sich das Sortieren und ein anschließender Data-Step mit BY-Variable an:

/* Testdaten */
data DATEN;
input Identnummer Umsatz;

cards;
1 2
2 5
3 1
3 3
4 7
;

run;

/* Sortieren */
proc sort data=DATEN;
  by Identnummer descending Umsatz;
run;

/* Filtern */
data DATEN;
 set DATEN;
 by Identnummer;
 if first.Identnummer;
run;

Der Trick ist, nach Identnummer und Umsatz absteigend zu sortieren und dann den ersten Datensatz (also den mit höchstem Umsatz) zu behalten.

 

Viel Erfolg

Christian

PeterClemmensen
Tourmaline | Level 20
data have;
input Identnummer Umsatz;
datalines;
1 2
2 5
3 1
3 3
4 7
;

data want;
    do until(last.Identnummer);
        set have;
        by Identnummer;
        if Umsatz gt _iorc_ then _iorc_=Umsatz;
    end;
    Umsatz=_iorc_;
    _iorc_=.;
run;
CKothenschulte
Obsidian | Level 7

Interessante Lösung!

Aber auch dafür muss das Dataset nach Identnummer und absteigend nach Umsatz sortiert sein.

 

Spannend, wie immer mehrere Wege zum Ziel führen!

Kurt_Bremser
Super User

@CKothenschulte  schrieb:

Interessante Lösung!

Aber auch dafür muss das Dataset nach Identnummer und absteigend nach Umsatz sortiert sein.

 

Spannend, wie immer mehrere Wege zum Ziel führen!


Stimmt nicht wirklich. Denn der Umsatz muss hier gar nicht sortiert sein (Max ist Max, egal in welcher Reihenfolge).

Man kann diese Methode (DOW loop) sogar auf Bestände mit mehreren Variablen anwenden, indem man in jeder Iteration des Data Step ein Hash-Objekt füllt, und nach dem Abarbeiten einer Gruppe jenen Eintrag mit dem Maximum sucht und für die Ausgabe verwendet. Danach leert man das Objekt, so dass in der nächsten Iteration (= nächste Gruppe) wieder mit 0 begonnen wird. Ist das Dataset handlich genug, kann man sogar alles in ein Hash einlesen und daraus die Auswertung fahren.

CKothenschulte
Obsidian | Level 7

Okay!

 

In der Version zum Zeitpunkt meines Beitrages (siehe unten, hatte ich noch im EG) kam ein falsches Ergebnis heraus, wenn ich in den Testdaten die Datensätze:

3 1

33

vertauscht habe:

3 3

3 1

data have;
input Identnummer Umsatz;
datalines;
1 2
2 5
3 3
3 1
4 7
;

data want;
    do until(last.Identnummer);
        set have;
        by Identnummer;
        if Identnummer gt _iorc_ then _iorc_=Identnummer;
    end;
    _iorc_=.;
run;

Mittlerweile sieht der Code ja anders aus. Das funktioniert dann unabhängig von der Reihenfolge in den Eingabedaten.

PeterClemmensen
Tourmaline | Level 20

Yes, I corrected the code since there was an obvious error. Sorry, I am doing this in English. I understand German, though not fluently speaking 🙂

mfab
Quartz | Level 8

Die Sortierung ist trotzdem notwendig (nicht nach Umsatz, aber immer noch nach Identnummer).

Wenn ein BY-Statement mit "Identnummer" vorkommt, braucht es eine entsprechende Sortierung (im Beispiel ist das zufälligerweise gegeben. Allgemein gültig ist der code so nicht).

 

Beispiel:

data have;
input Identnummer Umsatz;
datalines;
1 2
3 3
2 5
3 1
4 7
;
run;
Kurt_Bremser
Super User

Hier Beispielcode für die Verwendung des Hash:

data have;
input id umsatz sonst $;
datalines;
1 2 a
2 5 b
3 3 c
3 1 d
4 7 e
;

data want;
if _n_ = 0 then set have;
if _n_ = 1
then do;
  declare hash h (dataset:"have (obs=0)"); /* nur die Struktur anlegen */
  h.definedata("sonst");
  h.definekey("umsatz");
  h.definedone();
end;
do until (last.id);
  set have;
  by id;
  ums = max(ums,umsatz);
  rc = h.add();
end;
/* 2 Statements zur Kontrolle */
rc = h.num_items;
put rc=; /* Groesse des Hash */
umsatz = ums; /* Maximalwert setzen */
rc = h.find(); /* dazu passendes Item extrahieren */
rc = h.clear();
drop ums rc;
/* automatische Ausgabe des Maximum-Objekts per Gruppe */
run;

Wenn zB der Eingangsbestand nach ID und Datum sortiert ist, und man nicht umsortieren will. Da immer nur eine Gruppe im Memory gehalten wird, ist es äusserst unwahrscheinlich, dass einem der Speicher ausgeht.

Kurt_Bremser
Super User

Und hier jetzt mit komplett unsortierter Quelle, da muss dann allerdings der ganze Datenbestand ins Memory passen:

data have;
input id umsatz sonst $;
datalines;
3 3 c
2 5 b
3 5 x
4 7 e
3 1 d
1 2 a
;

data want (
  keep=_id _umsatz _sonst
  rename=(_id=id _umsatz=umsatz _sonst=sonst)
);
if _n_ = 0 then set have;
length
  _id 8
  _umsatz 8
  _sonst $8
;
retain _id;
if _n_ = 1
then do;
  declare hash h(dataset:'have',ordered:'a',multidata:'Y');
  h.definekey('id','umsatz');
  h.definedata('id','umsatz','sonst');
  h.definedone();
  declare hiter hi('h');
  rc = hi.first();
  _id = id;
end;
rc = 0;
do until (rc);
  do until(_id ne id or rc);
    _umsatz = umsatz;
    _sonst = sonst;
    rc = hi.next();
  end;
  output;
  _id = id;
end;
stop;
run;

proc print data=want noobs;
run;

Ergebnis:

id    umsatz    sons

 1       2        a 
 2       5        b 
 3       5        x 
 4       7        e 

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 16. Read more here about why you should contribute and what is in it for you!

Submit your idea!

Diskussionsstatistiken
  • 11 Antworten
  • 3788 Aufrufe
  • 13 Kudos
  • 5 in Unterhaltung