Hallo zusammen!
Ich möchte ein Produkt bilden über mehrere Zeilen im Datensatz hinweg.
Sobald eine Zeile mit einer neuen id auftritt, soll neu gerechnet werden.
Eine Summe schaffe ich zu berechnen, aber wenn ich das + gegen ein * tausche, erhalte ich eine Fehlermeldung.
Hier ein kurzes Beispielprogramm (die fehlerhafte Produktberechnung ist auskommentiert):
data test;
input id wert;
cards;
1 3
1 5
1 7
2 1
2 2
2 3
2 4
3 5
3 5
3 5
run;
Proc sort data=test;
by id wert;
run;
data test2;
set test;
by id;
if first.id then do;
summe = 0;
summe = summe + wert;
*produkt = 1;
*produkt = produkt * wert;
end;
else do;
summe + wert;
*produkt * wert;
end;
run;
Habt ihr eine Idee für mich, wie ich das hinkriege?
Vielen Dank vorab für Eure Hilfe!
Harald
Hallo Harald,
da du für die Multiplikation kein SUM Statement verwenden kannst, musst du die Variable produkt explizit per RETAIN Statement so deklarieren, dass ihr Wert über die Datensatziterationen erhalten bleibt.
Hier ein lauffähiges Beispiel:
data test2;
set test;
by id;
retain produkt 0;
if first.id then do;
summe = 0;
summe = summe + wert;
produkt = 1;
end;
else do;
summe + wert;
end;
produkt = produkt * wert;
run;
Gruß,
Andreas
Hallo Andreas,
vielen Dank für Deine schnelle Hilfe!
Gruß,
Harald
Hallo Harald,
summe+wert;
ist eine sogenannte SUM-Anweisung, die die Variable summe automatisch retained. Eine Entsprechende MAL-Anweisung gibt es leider nicht. Folgendermaßen funktioniert es:
data test2;
set test;
by id;
retain produkt 1;
if first.id then do;
produkt = 1;
end;
produkt=produkt * wert;
run;
Wichtig dabei ist es die Variable produkt auf retain zu setzen, das bedeutet, dass der errechnete Wert beibehalten wird, bis er bei einem Gruppenwechsel (first.id) neu initialisiert wird.
Viele GRüße
Wolfgang
Hallo Wolfgang,
vielen Dank für Deine schnelle Hilfe!
Gruß,
Harald
und zur Abwechslung mal etwas proc sql 😉
Marius
data test;
input id wert;
cards;
1 3
1 5
1 7
2 1
2 2
2 3
2 4
3 5
3 5
3 5
run;
proc sql;
SELECT
id,
round
(CASE
WHEN MinVal = 0 THEN 0
WHEN mod(Neg, 2) = 1 THEN -1 * EXP(ABSMult)
ELSE EXP(ABSMult)
END)
as Mult_Wert
from(
SELECT
id,
SUM(log(ABS(ifn(wert=0, 0,wert)))) AS ABSMult,
SUM(SIGN
(CASE
WHEN wert < 0 THEN 1
ELSE 0
END)
) AS Neg,
MIN(ABS(wert)) AS MinVal
FROM
test
GROUP BY
id
) test
;
quit;
Mit PROC SQL
DATA TEST;
INPUT ID WERT;
CARDS;
1 3
1 5
1 7
2 1
2 2
2 3
2 4
3 5
3 5
3 5
RUN;
PROC SQL;
SELECT ID,
EXP(SUM(LOG(WERT))) AS PRODUKT
FROM TEST
GROUP BY ID;
QUIT;
Auch wenn man nur positive Eingangswerte zulässt, sollte m. E. ein ROUND um das Formelergebnis nicht fehlen. Es könnte ja sein, dass mit der Variable PRODUKT weitergearbeitet wird (z. B. if produkt<125 then ...). Dann stellen die möglichen Rundungsfehler ein Risiko dar. Schon im vorliegenden Zahlenbeispiel weichen 2 der 3 Ergebnisse vom erwarteten ganzzahligen Produkt ab (jedenfalls auf meinem Rechner).
Werden prinzipiell auch nicht ganzzahlige Eingangswerte zugelassen, ist bei der Wahl der Rundungseinheit auf die Größenordnung der erwarteten Ergebnisse zu achten. In vielen Fällen ähnlich dem vorliegenden Beispiel dürfte sich 10^-8 als Rundungseinheit eignen, also
ROUND(EXP(SUM(LOG(WERT))), 1E-8) AS PRODUKT
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!