LesezeichenAbonnierenRSS-Feed abonnieren
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Dvdscot
Obsidian | Level 7

Hallo,

 

Ich hab jetzt einen Datensatz mit lauter Datumswerten die ich vergleichen möchte. Sowas ähnliches habe ich vor kurzem schon mit einem Array gelöst.

 

Auch jetzt würde ich sie in einen Array packen. Allerdings sind die Daten natürlich löchrig, gibt lauter Missings.

 

Dachte zuerst an Do i from 1 to Dim(Array)

if Spalte(i) < Spalte(i + 1) dann richtig sonst falsch

 

Aber die nächste Spalte kann ja missing sein und die danach auch, es soll nur verglichen werden wenn wirklich ein Datum enthalten ist.

 

Man könnte alles so komprimieren und in einen neuen Datensatz packen dass keine missings mehr vorhanden sind. Dann hat man in der einen Zeile 3 Datumswerte, danach nichts mehr, in der nächsten 5.

 

Besser fände ich es aber wenn man es so machen könnte:

 

DO UNTIL i+x hat ein Datum (NE .), dann vergleiche mit i, also der sucht nach Werten mit Inhalt und vergleicht dann den Wert davor.

 

So sieht es aus:

 

05Jan2017 . 12Jul2017 20Jan2018 . . . . .
. . . . . . 24Jun2019 13Dez2019 19Feb2020
. . . . . 29Dez2019 . . .
. . . . . . . . .
03Apr2017 28Jul2017 28Jan2018 23Jul2018 19Jan2019 . . . .
. . . . . . 10Jan2020 . .
06Dez2018 05Apr2019 18Okt2019 23Apr2020 . . . . .
12Jul2017 . . 05Jun2018 14Dez2018 . . . .
02Okt2019 . 27Apr2020 . . . . . .
02Apr2018 . . 22Jul2019 09Dez2019 . . . .
14Jun2020 09Feb2021 30Sep2020 22Jun2020 . 14Feb2017 24Jul2017 19Dez2017 17Jun2018
05Apr2019 06Jul2019 19Nov2019 27Apr2020 25Nov2020 13Nov2017 26Apr2018 27Jul2018 .
. . 25Okt2016 16Jul2017 16Jan2018 . . . .
11Mrz2016 . . . . . . . .
21Aug2017 . 26Apr2018 04Nov2018 20Feb2019 . . . .
15Aug2019 17Dez2019 06Jun2020 . 24Jan2021 . . . .
. . . . . . . . .
. . . . 09Aug2020 . . . .
06Jan2020 08Jul2020 . . . . . . .
11Dez2017 25Feb2018 10Apr2018 08Nov2018 . . . . .
18Sep2017 11Dez2017 17Jun2018 . 15Jan2019 . . . .
. . . . . . . . .
. 16Jun2019 . 14Jun2020 . . . . .

Danke

1 AKZEPTIERTE LÖSUNG

Akzeptierte Lösungen
FreelanceReinh
Jade | Level 19

Hallo @Dvdscot,

 

dieses Programm prüft, ob Datum1, ..., Datum9, abgesehen von fehlenden Werten, echt aufsteigend sind. Wenn ja, wird eine Variable CHRONO auf 1 gesetzt, sonst auf 0. Sollen auch gleiche Daten noch als "aufsteigend" akzeptiert werden, ist bloß das Gleichheitszeichen aus der IF-Bedingung zu löschen.

/* Einlesen der Beispieldaten */

data have;
array datum[9];
input datum[*] ?? :deudfde.;
format datum: deudfde9.;
cards;
05Jan2017 . 12Jul2017 20Jan2018 . . . . .
. . . . . . 24Jun2019 13Dez2019 19Feb2020
. . . . . 29Dez2019 . . .
. . . . . . . . .
03Apr2017 28Jul2017 28Jan2018 23Jul2018 19Jan2019 . . . .
. . . . . . 10Jan2020 . .
06Dez2018 05Apr2019 18Okt2019 23Apr2020 . . . . .
12Jul2017 . . 05Jun2018 14Dez2018 . . . .
02Okt2019 . 27Apr2020 . . . . . .
02Apr2018 . . 22Jul2019 09Dez2019 . . . .
14Jun2020 09Feb2021 30Sep2020 22Jun2020 . 14Feb2017 24Jul2017 19Dez2017 17Jun2018
05Apr2019 06Jul2019 19Nov2019 27Apr2020 25Nov2020 13Nov2017 26Apr2018 27Jul2018 .
. . 25Okt2016 16Jul2017 16Jan2018 . . . .
11Mrz2016 . . . . . . . .
21Aug2017 . 26Apr2018 04Nov2018 20Feb2019 . . . .
15Aug2019 17Dez2019 06Jun2020 . 24Jan2021 . . . .
. . . . . . . . .
. . . . 09Aug2020 . . . .
06Jan2020 08Jul2020 . . . . . . .
11Dez2017 25Feb2018 10Apr2018 08Nov2018 . . . . .
18Sep2017 11Dez2017 17Jun2018 . 15Jan2019 . . . .
. . . . . . . . .
. 16Jun2019 . 14Jun2020 . . . . .
;

/* Prüfen, ob DATUM1-DATUM9, abgesehen von Missings, echt aufsteigend sortiert sind (CHRONO=1) */

data want(drop=i v);
set have;
array d[*] datum:;
do i=1 to dim(d)-1;
  v=coalesce(d[i],v);
  if v>=d[i+1]>.z then leave;
end;
chrono=(i=dim(d));
run;

Lösung in ursprünglichem Beitrag anzeigen

7 ANTWORTEN 7
Christine2
Fluorite | Level 6
Hallo,
habe ich es richtig verstanden, dass ein Eintrag leer der nächste aber
wieder gefüllt sein kann?
Falls nur die gefüllten Werte verglichen werden sollen, würde ich mit einer
doppelten Schleife arbeiten.
Grob skizziert:
i = 1;
Do while i < dim(array);
j = i +1;
Do while missing(array[j]) and j< dim(array);
j = j + 1;
End;
if array[i] < array[j] then richtig else falsch.
i = j;
End;

Dvdscot
Obsidian | Level 7

Ja, es kann mit einem Datum oder ein oder mehreren Missings anfangen, dann kommt entweder gleich sofort wieder ein Datum oder ein oder mehrere Missings und dann erst wieder ein Datum usw.

 

Danke, allerdings bekomme ich Fehler:

 

93         i = 1;
94         Do while i < dim(kl);
                    _
                    22
                    76
ERROR 22-322: Syntaxfehler, erwartet wird eines der folgenden: (, =.  

ERROR 76-322: Syntax error, statement will be ignored.

95         j = i + 1;
96         Do while missing(kl(j)) and j < dim(kl);
                    _______
                    22
                    76
ERROR 22-322: Syntaxfehler, erwartet wird eines der folgenden: (, =.  

ERROR 76-322: Syntax error, statement will be ignored.

97         j = j + 1;
98         End;
99         if kl(i) < kl(j) then richtig = 1; else richtig = 0;
100        i = j;
101        End;

Habe hier zwei Vorschläge auf englisch gefunden die aber auch nicht funktionieren, der erste ist zu kompliziert wegen Date und Time (ich hab nur Date) und der zweite geht nicht.

 

https://communities.sas.com/t5/SAS-Programming/Check-chronology-of-multiple-event-dates-within-a-row...

https://communities.sas.com/t5/SAS-Programming/How-do-I-check-chronological-order-of-event-datetimes...

 

FreelanceReinh
Jade | Level 19

Hallo @Dvdscot,

 

dieses Programm prüft, ob Datum1, ..., Datum9, abgesehen von fehlenden Werten, echt aufsteigend sind. Wenn ja, wird eine Variable CHRONO auf 1 gesetzt, sonst auf 0. Sollen auch gleiche Daten noch als "aufsteigend" akzeptiert werden, ist bloß das Gleichheitszeichen aus der IF-Bedingung zu löschen.

/* Einlesen der Beispieldaten */

data have;
array datum[9];
input datum[*] ?? :deudfde.;
format datum: deudfde9.;
cards;
05Jan2017 . 12Jul2017 20Jan2018 . . . . .
. . . . . . 24Jun2019 13Dez2019 19Feb2020
. . . . . 29Dez2019 . . .
. . . . . . . . .
03Apr2017 28Jul2017 28Jan2018 23Jul2018 19Jan2019 . . . .
. . . . . . 10Jan2020 . .
06Dez2018 05Apr2019 18Okt2019 23Apr2020 . . . . .
12Jul2017 . . 05Jun2018 14Dez2018 . . . .
02Okt2019 . 27Apr2020 . . . . . .
02Apr2018 . . 22Jul2019 09Dez2019 . . . .
14Jun2020 09Feb2021 30Sep2020 22Jun2020 . 14Feb2017 24Jul2017 19Dez2017 17Jun2018
05Apr2019 06Jul2019 19Nov2019 27Apr2020 25Nov2020 13Nov2017 26Apr2018 27Jul2018 .
. . 25Okt2016 16Jul2017 16Jan2018 . . . .
11Mrz2016 . . . . . . . .
21Aug2017 . 26Apr2018 04Nov2018 20Feb2019 . . . .
15Aug2019 17Dez2019 06Jun2020 . 24Jan2021 . . . .
. . . . . . . . .
. . . . 09Aug2020 . . . .
06Jan2020 08Jul2020 . . . . . . .
11Dez2017 25Feb2018 10Apr2018 08Nov2018 . . . . .
18Sep2017 11Dez2017 17Jun2018 . 15Jan2019 . . . .
. . . . . . . . .
. 16Jun2019 . 14Jun2020 . . . . .
;

/* Prüfen, ob DATUM1-DATUM9, abgesehen von Missings, echt aufsteigend sortiert sind (CHRONO=1) */

data want(drop=i v);
set have;
array d[*] datum:;
do i=1 to dim(d)-1;
  v=coalesce(d[i],v);
  if v>=d[i+1]>.z then leave;
end;
chrono=(i=dim(d));
run;
Dvdscot
Obsidian | Level 7

Vielen Dank, das funktioniert. Magste die einzelnen Schritte erklären?

 

Ich versteh nicht warum du zwei Arrays hintereinander machst, reicht das erste nicht?

 

Ich überlege ob ich das ausführen kann mit mehreren solcher Arrays in einem Datensatz und auch noch anderen Variablen, dann darf die Schleife wohl nicht bei eins beginnen, oder ob ich für jedes Array einen extra Einzeldatensatz ohne alles andere erstellen muss.

Dvdscot
Obsidian | Level 7
Ich sehe gerade dass der auch 0 angibt wenn zwei identische Datumswerte aufeinander folgen. Ich muss mal rausfinden ob das so in Ordnung ist.
FreelanceReinh
Jade | Level 19

Ein Array existiert nur innerhalb eines Datasteps, seine Struktur wird nicht im erzeugten Dataset gespeichert. Deswegen muss im zweiten Datastep zwingend ein neues ARRAY-Statement verwendet werden, um das Array als solches wieder zur Verfügung zu haben. Das ARRAY-Statement im ersten Datastep hat freilich die Variablen datum1, ..., datum9 erzeugt, die der zweite Datastep im Dataset HAVE vorfindet und auf die sich das ARRAY-Statement dort bezieht.

 

Den neuen Namen d für das Array habe ich nur der Kürze halber gewählt, weil der Name ja danach mehrfach verwendet wird. Anders als durch das ARRAY-Statement im ersten Step werden hier keine neuen Variablen erzeugt, sondern nur vereinbart, dass auf die vorhandenen Variablen datum1, ..., datum9 mit Array-Referenzen d[1], ..., d[9] zugegriffen werden kann.

 

Genau genommen, wird vereinbart, dass die bis dahin vom Compiler "gesehenen" Variablen, deren Namen mit "datum" (oder "Datum", "DATUM" usw.) beginnen, als d[1], d[2], ... referenziert werden können, und zwar in der Reihenfolge, in der sie im "Program Data Vector" (PDV) stehen. Insofern wäre die Kurzform datum: mit dem Doppelpunkt etwas riskant, wenn nicht aus dem vorigen Step bekannt wäre, dass es in HAVE

a) keine weiteren Variablen mit Namen wie DatumDatumswert, Datumsdifferenz o. ä. gibt und

b) die neun Datumsvariablen nicht etwa in anderer Reihenfolge (etwa datum8, datum3, ...) im PDV stehen.

 

Sicherer und bei unbekannten Input-Daten dringend zu empfehlen ist eine explizite Variablenliste:

array d[*] datum1-datum9;

Alternativ könnte man hier

array datum[9];

verwenden, müsste dann aber im weiteren Verlauf jeweils "datum" statt "d" schreiben.

 

Wenn die Variablen in Wirklichkeit ganz anders heißen (etwa Screening, Enrollment, ...), muss man die Namen einzeln aufführen, die Liste automatisiert erstellen oder eine Liste nach Variablenreihenfolge (z. B. Screening--LastVisit) verwenden.

 

Allgemein können in einem Datastep beliebig viele Arrays definiert werden und die betreffenden Listen von Variablen dürfen sich auch überschneiden. Die Variablen zu einem Array müssen aber jeweils vom selben Typ sein: entweder numerisch oder alphanumerisch ("character").

 

In unserem Datastep durchläuft die DO-Schleife die Array-Variablen von der ersten bis zur vorletzten (weil danach immer mit der nächsten verglichen wird). In der Hilfsvariable v wird jeweils der bis dahin letzte vorhandene (also "non-missing") Datumswert gespeichert, falls es überhaupt einen gab (sonst ist auch v missing). Die COALESCE-Funktion (die den ersten non-missing Wert ihrer -- hier: zwei -- [numerischen] Argumente liefert) vereinfacht dabei den Code: Ohne ein IF-THEN-Konstrukt zu benötigen, wird v immer "aktualisiert", sobald man bei einem non-missing Datum "vorbeikommt". Da v erst in dem Datastep erzeugt (und nicht RETAINed) wird, startet es in jedem Satz immer mit einem Missing (auf der rechten Seite des Zuweisungsstatements v=...) und kann nicht etwa noch ein Datum aus dem vorigen Satz enthalten. 

 

Sobald die IF-Bedingung erfüllt ist, also festgestellt wird, dass der "aktuelle" Datumswert in v nicht kleiner als, sondern größer oder gleich dem nächsten Datumswert im Array ist und letzterer non-missing ist (nämlich größer als der größte "special missing value" .z -- im konkreten Fall könnte man auch einfach >. schreiben), liegt eine Abweichung von der nach Annahme erwünschten echt aufsteigenden Reihenfolge vor. In diesem Fall (und nur dann) wird die Schleife vorzeitig verlassen (LEAVE-Anweisung).

 

Der Wert der Laufvariable i verrät, ob die Schleife vorzeitig verlassen wurde: i ist dann nämlich höchstens gleich dem im DO-Statement nach dem Schlüsselwort "TO" vorgesehenen Endwert (hier: dim(d)-1). Wurde die Schleife dagegen komplett durchlaufen, also ohne das LEAVE-Statement auszuführen, ist der finale Wert von i gleich dem vorgesehenen Endwert plus 1, hier also gleich dim(d), nämlich 9. Damit ist der Wahrheitswert (i=dim(d)) -- also 1, wenn diese Gleichung gilt, sonst 0 -- genau der Wert, den wir in der "Flag"-Variable chrono haben wollen: 1, wenn die echt aufsteigende Reihenfolge erfüllt ist (was auch in den trivialen Fällen "alle neun Daten missing" oder "nur ein Datum vorhanden, alle anderen missing" als erfüllt gewertet wird) und 0, wenn an irgendeiner Stelle der 9-elementigen Wertefolge ein absteigender oder nur gleicher Datumswert beobachtet wurde. Die Trivialfälle könnten bei Bedarf mit Hilfe der N-Funktion erkannt und anders behandelt werden.

 

Wie schon erwähnt, kann der Fall "Gleichheit benachbarter Daten" aus dem Kriterium herausgenommen werden, indem man schreibt:

if v>d[i+1]>.z then leave;

Nach diesem modifizierten Kriterium würde z. B. die (aus zwei vorhandenen Werten und sieben Missings bestehende) Folge

. . 01Jan2000 . . . 01Jan2000 . .

als aufsteigend anerkannt (chrono=1).

Kurt_Bremser
Super User

Sowas ist sehr einfach zu lösen, wenn man ein vertikales statt horizontales Layout hat:

data have;
array datum[9];
input datum[*] ?? :deudfde.;
format datum: deudfde9.;
id = _n_; /* vurtuelle Gruppe bauen */
cards;
05Jan2017 . 12Jul2017 20Jan2018 . . . . .
. . . . . . 24Jun2019 13Dez2019 19Feb2020
. . . . . 29Dez2019 . . .
. . . . . . . . .
03Apr2017 28Jul2017 28Jan2018 23Jul2018 19Jan2019 . . . .
. . . . . . 10Jan2020 . .
06Dez2018 05Apr2019 18Okt2019 23Apr2020 . . . . .
12Jul2017 . . 05Jun2018 14Dez2018 . . . .
02Okt2019 . 27Apr2020 . . . . . .
02Apr2018 . . 22Jul2019 09Dez2019 . . . .
14Jun2020 09Feb2021 30Sep2020 22Jun2020 . 14Feb2017 24Jul2017 19Dez2017 17Jun2018
05Apr2019 06Jul2019 19Nov2019 27Apr2020 25Nov2020 13Nov2017 26Apr2018 27Jul2018 .
. . 25Okt2016 16Jul2017 16Jan2018 . . . .
11Mrz2016 . . . . . . . .
21Aug2017 . 26Apr2018 04Nov2018 20Feb2019 . . . .
15Aug2019 17Dez2019 06Jun2020 . 24Jan2021 . . . .
. . . . . . . . .
. . . . 09Aug2020 . . . .
06Jan2020 08Jul2020 . . . . . . .
11Dez2017 25Feb2018 10Apr2018 08Nov2018 . . . . .
18Sep2017 11Dez2017 17Jun2018 . 15Jan2019 . . . .
. . . . . . . . .
. 16Jun2019 . 14Jun2020 . . . . .
;

proc transpose data=have out=long (where=(col1 ne .));
by id;
var datum:;
run;

Es wird kein Platz für missing values verschwendet, und der Code ist jetzt recht einfach:

data want;
set long;
by id;
if not (first.id) and lag(col1) > col1
then steigend = "No ";
else steigend = "Yes";
run;

Braucht man nur eine Zeile, um das Kennzeichen zu erhalten, sieht es so aus:

data want2;
steigend = "Yes";
do until (last.id);
  set long;
  by id;
  if col1 < l_col1 then steigend = "No";
  l_col1 = col1;
end;
keep id steigend;
run;