2 - Urz
Transcription
2 - Urz
Tipps und Tricks für den leichteren Umgang mit der SAS Software Sabine Erbslöh und Christina Gelhorn Statistical Programming, Accovion GmbH 15. KSFE-Tagung, Heidelberg, 24. Februar 2011 Inhalt I Problem aus der Statistik: Konfidenzintervalle für 0% Feinheiten zu PROC Report • Erstellen von horizontalen Linien in RTF-Tabellen • Seitenumbruch nach einer Spalte Arbeiten mit Datumsfeldern • Auffüllen unvollständiger Datumsangaben • Umwandeln von Datum und Zeit in Datetime Achtung – Was geht nicht in SAS • LAG-Funktion in einer IF/ELSE-Anweisung • Vorsicht mit weiteren Anweisungen nach Merge • Ab SAS 9.2 Abfrage mit IN auch in der Makro-Sprache 2 Inhalt II Einfach bequem • Anzeigen von Variablennamen UND Variablenlabels • Angriff der SAS Kloneditoren unter Windows Kurz und elegant • Verschiedene DO Schleifen • Möglichkeiten des SELECT Statement • CALL MISSING Nützliche Funktionen • SMALLEST und LARGEST • STRIP, COMPBL, SUBSTRN 3 Konfidenzintervalle für 0% mit PROC FREQ Untersucht wird die Wahrscheinlichkeit des Auftretens einer allergischen Reaktion auf das Medikament Die Reaktion wurde bei keinem der Patienten beobachtet Beispieldatensatz: obs Patient 1=allergische Reaktion, 2=keine allergische Reaktion 1 1 2 2 2 2 3 3 2 4 4 2 5 5 2 6 6 2 7 7 2 8 8 2 9 9 2 10 10 2 4 Konfidenzintervalle für 0% mit PROC FREQ proc freq data = status; table ereignis / binomial; run; Binomial Proportion for EREIGNIS = 2 Proportion 1.0000 ASE 0.0000 95% Lower Conf Limit 1.0000 95% Upper Conf Limit 1.0000 Exact Conf Limits 95% Lower Conf Limit 0.6915 95% Upper Conf Limit 1.0000 5 Konfidenzintervalle für 0% mit PROC FREQ PROC FREQ liefert im Normalfall nur Schätzer für die Eintrittswahrscheinlichkeit des Ereignisses „keine allergische Reaktion“. Das Konfidenzintervall für die Eintrittswahrscheinlichkeit des Ereignisses „allergische Reaktion“ lässt sich mit PROC FREQ nur über Umwege berechnen 1. Definition eines Formats, das alle möglichen Ausprägungen der Variablen enthält: proc format; value statusf 1 = "allergische Reaktion" 2 = "keine allergische Reaktion"; run; 6 Konfidenzintervalle für 0% mit PROC FREQ 2. Auszählung der absoluten Häufigkeit aller so festgelegten Kategorien mit PROC TABULATE proc tabulate data = status out = anz; class ereignis / preloadfmt; table ereignis * n / printmiss; format ereignis statusf.; run; 1=allergische Reaktion, 2=keine allergische Reaktion allergische Reaktion keine allergische Reaktion N N 10 7 Konfidenzintervalle für 0% mit PROC FREQ 3. Missings werden durch Nullen ersetzt. data anz; set anz; if n eq . then n = 0; run; 4. Ergebnis wird PROC FREQ unter Verwendung des WEIGHT Statements übergeben. • Gewichte: Anzahl der Beobachtungen in den jeweiligen Kategorien • Damit dabei die Null berücksichtigt wird, muss zusätzlich die Option ZEROS angegeben werden 8 Konfidenzintervalle für 0% mit PROC FREQ proc freq data = anz; weight n / zeros ; table ereignis / binomial; run; Binomial Proportion for EREIGNIS = allergische Reaktion Proportion 0.0000 ASE 0.0000 95% Lower Conf Limit 0.0000 95% Upper Conf Limit 0.0000 Exact Conf Limits 95% Lower Conf Limit 0.0000 95% Upper Conf Limit 0.3085 9 Feinheiten zu PROC REPORT Erstellen von horizontalen Linien in RTF-Tabellen Seitenumbruch nach einer Spalte 10 PROC REPORT - horizontale Linien in RTF-Tabellen Vertikale Linien werden „automatisch“ erzeugt Um horizontale Linien zu erhalten, muss man mit styles, brdrt, brdrs und brdw arbeiten Beispieldatensatz: obs Pat.ID System Organ Class Adverse Event Term 1 123 Eye disorders Conjunctivitis 2 123 Eye disorders Glaucoma 3 123 Gastrointestinal disorders Severe Nausea 4 456 Eye disorders Blind 5 456 Eye disorders Conjunctivitis 6 456 Eye disorders Glaucoma 7 456 Gastrointestinal disorders Severe Nausea 8 789 Gastrointestinal disorders Severe Nausea 11 PROC REPORT - horizontale Linien in RTF-Tabellen 1. Erzeugen eines regulären lst-output PROC REPORT data=test nowindows spacing=3 headline headskip; column soc event subjid ; define soc / order width=30 flow; define event / order width=20 flow; define subjid / width=10; RUN; System Organ Class Adverse Event Term Pat.ID __________________________________________________________ Eye disorders Blind 456 Conjunctivitis 123 456 Glaucoma 123 456 Gastrointestinal disorders Severe Nausea 123 456 789 12 PROC REPORT - horizontale Linien in RTF-Tabellen 2. Erzeugen eines RTF output ohne weitere Angaben PROC REPORT data=test nowindows style={rules=cols cellspacing=0 cellpadding=0}; column soc event subjid ; define soc / order width=30 flow; define event / order width=20 flow; define subjid / width=10; RUN; System Organ Class Eye disorders Adverse Event Term Blind Conjunctivitis Glaucoma Gastrointestinal disorders Severe Nausea Pat.ID 456 123 456 123 456 123 456 789 13 PROC REPORT - horizontale Linien in RTF-Tabellen Definition von horizontalen Linien basiert auf RTF Spezifikationen Sie werden innerhalb eines compute statement als call define definiert Einstellungen für die Absatzränder: • brdrt: Border top • brdrs: Single-thickness border • brdwN: Border width N (in twips) N = Breite des Stiftes für den Rahmen 14 PROC REPORT - horizontale Linien in RTF-Tabellen 3. Erzeugen des RTF output mit horizontalen Linien PROC REPORT data=test nowindows spacing=3 headline headskip style={rules=cols cellspacing=0 cellpadding=0} style(header column)=[protectspecialchars=off ]; column soc event subjid ; define soc / order width=30 flow; define event / order width=20 flow; define subjid / width=10; compute subjid; if soc ne ' ' then call define ('_c1_','style','style=[pretext="\brdrt\brdrs\brdrw11"]'); if event ne ' ' then call define ('_c2_','style','style=[pretext="\brdrt\brdrs\brdrw11"]'); if subjid ne ' ' then call define ('_c3_','style','style=[pretext="\brdrt\brdrs\brdrw11"]'); endcomp; RUN; 15 PROC REPORT - horizontale Linien in RTF-Tabellen Achtung! In Word sieht man die Linien erst im Print Preview! System Organ Class Adverse Event Term Pat.ID Eye disorders Blind 456 Conjunctivitis 123 456 Glaucoma 123 456 Gastrointestinal disorders Severe Nausea 123 456 789 16 PROC REPORT - Seitenumbruch nach einer Spalte Seitenumbruch häufig gewünscht vor Beginn einer neuen Ausprägung der Gruppierungsvariablen -> Seitenumbruch nach einer Reihe Manchmal aber auch wünschenswert, nach einer bestimmten Spalte einen Seitenumbruch zu erzwingen PROC REPORT bietet auch die Möglichkeit, im define statement mit /page zu arbeiten Der Beispieldatensatz wurde um 3 Variablen erweitert: pt (preferred term), serious und related. 17 PROC REPORT - Seitenumbruch nach einer Spalte 1. Mit automatischem Spaltenumbruch PROC REPORT data=test3 nowindows spacing=3 headline; column subjid soc pt event serious related ; define subjid / id order width=10; define soc / order width=30 flow; define pt / order width=20 flow; define event / width=20 flow; define serious / width=7; define related / width=7; RUN; 18 PROC REPORT - Seitenumbruch nach einer Spalte Der erzeugte output benötigt 2 Seiten, der automatische Umbruch erfolgt NACH der Variablen Event Pat.ID System Organ Class Preferred Term Adverse Event Term _________________________________________________________________ 123 Eye disorders Conjunctivitis Conjunctivitis Glaucoma Glaucoma Gastrointestinal Nausea Severe Nausea disorders 456 Eye disorders Blindness Blind ... Serious Related Pat.ID Flag Flag _____________________________ 123 0 1 0 0 0 1 456 ... 1 0 19 PROC REPORT - Seitenumbruch nach einer Spalte 2. Mit definiertem Spaltenumbruch PROC REPORT data=test3 nowindows spacing=3 headline; column subjid soc pt event serious related ; define subjid / id order width=10; define soc / order width=20 flow; define pt / order width=20 flow; / width=20 flow page; define event define serious / width=7; define related / width=7; RUN; 20 PROC REPORT - Seitenumbruch nach einer Spalte Der erzeugte output benötigt ebenfalls 2 Seiten, der definierte Umbruch erfolgt VOR der Variablen Event Pat.ID System Organ Class Preferred Term ________________________________________________________ 123 Eye disorders Conjunctivitis Glaucoma Gastrointestinal Nausea disorders 456 Eye disorders Blindness ... Serious Related Pat.ID Adverse Event Term Flag Flag _____________________________________________________ 123 Conjunctivitis 0 1 Glaucoma 0 0 Severe Nausea 0 1 456 ... Blind 1 0 21 Arbeiten mit Datumsfeldern Auffüllen unvollständiger Datumsangaben Umwandeln von einzelnen Datums- und Zeitvariablen in eine Datetime Variable 22 Auffüllen unvollständiger Datumsangaben Auffüllen mit dem letzten Tag des Monats Folgende Werte sollen eingesetzt werden: Wert Monat 28 Februar ohne Schaltjahr 29 Februar mit Schaltjahr 30 April, Juni, September, November 31 Januar, März, Mai, Juli, August, Oktober, Dezember Statt umständlicher Zuweisungen kann man vom ersten Tag des Folgemonats 1 Tag abziehen -> Makro Voraussetzung: Datensatz mit numerischer Monatsund Jahres-Variable 23 Auffüllen unvollständiger Datumsangaben Makro zum Ersetzen des letzten Tages im Monat %macro get_lmdt(month=,year=,outdt=); *** if month is December ---> fill with 31 ***; if &month eq 12 then &outdt=input(compress("3112"||put(&year,z4.)),ddmmyy8.); *** else ---> 01 of next month - 1 day ***; else &outdt=input(compress("01"||put(&month+1,z2.) ||put(&year,z4.)),ddmmyy8.)-1; format &outdt date9.; %mend get_lmdt; 24 Umwandeln in Datetime Variable Statt Kombinationen von input und Konkatenierung: datetm= input(put(dat,date7.) || "," || put(tim,time5.),datetime.); oder datetm= (dat*24*60*60)+ tim; Funktion dhms – Syntax: dhms(date,hour,minute,second) datetm= dhms(dat,0,0,tim); Auch ideal um Zeitfenster abzufragen: timelow = dhms(end,-5,-30,endtim); timeupp = dhms(end,5,30,endtim); 25 Achtung – Was geht nicht in SAS LAG Funktion in einer IF/ELSE-Anweisung Vorsicht mit weiteren Anweisungen nach Merge (übernommen von Hr. Elmar Dunkl, RPS Research Germany GmbH, Nürnberg) Abfrage mit IN auch in der Makro-sprache (SAS 9.2) 26 Achtung – LAG Funktion in einer IF/ELSE Anweisung Beispiel: Mehrere Studienphasen, • Beginn der Phase notiert • Enddatum ist zu berechnen Obs Patient ID Phase Start Datum 1 1 1 06MAY2009 2 1 2 27MAY2009 3 2 1 08MAY2009 4 2 2 29MAY2009 5 2 3 19JUN2009 27 Achtung – LAG Funktion in einer IF/ELSE Anweisung data phase_end; attrib enddt format=date9. label = "End Datum"; set phase; by patient descending phase startdt; if first.patient then enddt = .; else enddt = lag(startdt); run; 28 Achtung – LAG Funktion in einer IF/ELSE Anweisung Ergebnis: Obs Patient ID Phase Start Datum End Datum 1 1 1 06MAY2009 2 1 2 27MAY2009 3 2 1 08MAY2009 29MAY2009 4 2 2 29MAY2009 06MAY2009 5 2 3 19JUN2009 29 Achtung – LAG Funktion in einer IF/ELSE Anweisung Obs Patient ID Start Datum Lag (normal) Phase 1 1 2 27MAY2009 2 1 1 06MAY2009 27MAY2009 3 2 3 19JUN2009 06MAY2009 4 2 2 29MAY2009 19JUN2009 06MAY2009 5 2 1 08MAY2009 29MAY2009 29MAY2009 Lag (if) Lag (else) 27MAY2009 30 Achtung – LAG Funktion in einer IF/ELSE Anweisung data phase_end; attrib enddt format=date9. label = "End Datum"; set phase; by patient descending phase startdt; lag_startdt = lag(startdt); if first.patient then enddt = .; else enddt = lag_startdt; run; 31 Achtung – LAG Funktion in einer IF/ELSE Anweisung Ergebnis: Obs Patient ID Phase Start Datum End Datum 1 1 1 06MAY2009 27MAY2009 2 1 2 27MAY2009 3 2 1 08MAY2009 29MAY2009 4 2 2 29MAY2009 19JUN2009 5 2 3 19JUN2009 32 Vorsicht mit weiteren Anweisungen nach Merge Beispiel: Zwei Datensätze mit einer gemeinsamen Variablen werden gemischt, dann wird zu einer nicht gemeinsamen Variablen eine 1 addiert. Zentrum Zentrum Nummer Berlin 29 Heidelberg 99 Zentrum Zentrum Nummer Patient ID Berlin 29 1 Zentrum Patient ID Berlin 29 2 Berlin 1 Berlin 29 3 Berlin 2 Heidelberg 99 4 Berlin 3 Heidelberg 99 5 Heidelberg 4 Heidelberg 5 33 Vorsicht mit weiteren Anweisungen nach Merge data zentpat; merge zentinfo patinfo; by zent; zentnum = zentnum + 1; run; 34 Vorsicht mit weiteren Anweisungen nach Merge Das Ergebnis hat vielleicht nicht jeder erwartet: Zentrum Zentrum Nummer Berlin 29 Heidelberg 99 Zentrum Zentrum Nummer Patient ID Berlin 30 1 Zentrum Patient ID Berlin 31 2 Berlin 1 Berlin 32 3 Berlin 2 Heidelberg 100 4 Berlin 3 Heidelberg 101 5 Heidelberg 4 Heidelberg 5 35 Vorsicht mit weiteren Anweisungen nach Merge Programmdatenvektor (PDV) Zentrum Patient ID Berlin 1 Zentrum Zentrum Nummer Berlin 2 Berlin 29 Berlin 3 Heidelberg 99 Heidelberg 4 Heidelberg 5 data zentpat; merge zentinfo patinfo; by zent; _N_=1 ZENT=Berlin PATIENT=1 ZENTNUM=29 FIRST.ZENT=1 LAST.ZENT=0 _ERROR_=0 _N_=2 _N_=3 ZENT=Berlin PATIENT=2 ZENTNUM=30 FIRST.ZENT=0 LAST.ZENT=0 _ERROR_=0 ZENT=Berlin PATIENT=3 ZENTNUM=31 FIRST.ZENT=0 LAST.ZENT=1 _ERROR_=0 zentnum = zentnum + 1; run; 36 Abfrage mit IN auch in der Makro-Sprache Ab SAS 9.2 Abfrage mit IN auch in der Makro-Sprache möglich. Im DATA step schon lange üblich DATA subset; SET sashelp.class; *** ausgeschrieben ***; IF name='Alice' or name='James' or name='John' or name='Judy'; *** abgekürzt mit IN ***; IF name in('Alice', 'James', 'John', 'Judy'); RUN; 37 Abfrage mit IN auch in der Makro-Sprache Innerhalb der Makrosprache war es vor SAS 9.2 nicht möglich, mit IN zu arbeiten. Folgender Code ergab eine Fehlermeldung: %MACRO usein(proc,ds); %if %upcase(&proc) IN (MEANS,PRINT) %then %do; proc &proc data=&ds; run; %end; %MEND usein; %usein(print,sashelp.class); ERROR: Required operator not found in expression: %upcase(&proc) IN (MEANS,PRINT) ERROR: The macro USEIN will stop executing. 38 Abfrage mit IN auch in der Makro-Sprache Auch unter SAS 9.2 gibt es eine Fehlermeldung, weil zunächst Optionen gesetzt werden müssen: • MINOPERATOR: Erlaubt es, den IN Operator zu benutzen • MINDELIMITER: zur Definition eines Trennzeichens Standard: Leerzeichen options minoperator mindelimiter=','; Im Beispiel wurde das Komma als Trennzeichen definiert, wie auch in der DATA step Version Danach funktioniert die Anwendung des IN Operator im Makro 39 Einfach bequem Anzeigen von Variablennamen UND Variablenlabels Angriff der SAS Kloneditoren unter Windows 40 Anzeigen von Variablennamen UND Variablenlabels PROC PRINT zeigt entweder die Variablennamen oder die –label Mit Hilfe von PROC SQL wird eine Makrovariable generiert, die beide Informationen enthält Dazu wird der Dictionary-Datensatz columns verwendet, hier am Beispiel von sashelp.class, dem vorher Label zugewiesen wurden Obs 1 2 3 4 5 name Name Sex Age Height Weight label Vorname Geschlecht Alter in Jahren Größe in Inch Gewicht in Pound 41 Anzeigen von Variablennamen UND Variablenlabels Alle ‚name‘ und ‚label‘ des ausgewählten Datensatzes werden in eine Makrovariable geschrieben, die später zum Ausdrucken verwendet wird %let lib= work; %let memname= class; PROC SQL noprint; select trim(name) !! "= '" !! trim(label) !! " (" !! trim(name) !! ")'" as newlabel into :newlabel separated by " " from dictionary.columns where libname eq upcase("&lib") and memname eq upcase("&memname."); quit; 42 Anzeigen von Variablennamen UND Variablenlabels Inhalt der generierten Macrovariablen &newlabel Name= 'Vorname (Name)' Sex= 'Geschlecht (Sex)' Age= 'Alter in Jahren (Age)' Height= 'Größe in Inch (Height)' Weight= 'Gewicht in Pound (Weight)' Verwendung im proc print PROC PRINT width= minimum data= &lib..&memname label; label &newlabel. ; run; Obs 1 2 ... Alter in Vorname Geschlecht Jahren (Name) (Sex) (Age) Alfred M 14 Alice F 13 Größe in Inch (Height) 69.0 56.5 Gewicht in Pound (Weight) 112.5 84.0 43 Angriff der SAS Kloneditoren unter Windows Bei langen Programmen muss man oft vor und zurück scrollen, um alle Informationen zusammenzutragen SAS unter Windows bietet die Möglichkeit, den Editor zu klonen Im Programmfenster kann man ein zweites Fenster mit dem Inhalt des aktuellen Fensters öffnen: Menü Window (Alt W) - New Window 44 Angriff der SAS Kloneditoren unter Windows Änderungen im Programmcode werden in beiden Fenstern gleichzeitig durchgeführt, wobei man an zwei verschiedenen Stellen im Programm sein kann Das zweite Fenster kann ohne Datenverlust einfach wieder geschlossen werden Erst beim Schließen des ersten Fensters erfolgt die Nachfrage, ob man speichern möchte 45 Kurz und elegant Verschiedene DO Schleifen Möglichkeiten des SELECT Statement CALL MISSING 46 Verschiedene DO Schleifen Klassische Anwendung: DO i=1 TO n Schleifenindex kann aber auch als Variable weiterverwendet werden. Trennung durch Komma und Sprünge sind möglich DO iday= 5, 7, 10, 14; output; END; DO proc= "Print", "Freq", "Report"; output; END; 47 Verschiedene DO Schleifen Anwendungsbeispiel: Erzeugen eines DummyDatensatzes zur Tabellierung aller möglichen Ausprägungen einer Variablen. Version mit einzelnen Zuweisungen: DATA dummy; catord=1; catord=2; catord=3; catord=4; catord=5; RUN; cattxt='Number of discontinuations'; OUTPUT; cattxt='Protocol violation'; OUTPUT; cattxt='Protocol entry criterion not met'; OUTPUT; cattxt='Adverse Event'; OUTPUT; cattxt='Lost to follow-up'; OUTPUT; 48 Verschiedene DO Schleifen Version mit Variablennamen in DO-Schleife und Format: PROC FORMAT; value txtf 1 = 'Number of discontinuations' 2 = 'Protocol violation' 3 = 'Protocol entry criterion not met' 4 = 'Adverse Event' 5 = 'Lost to follow-up‘; RUN; DATA dummy2; DO catord=1 TO 5; cattxt=put(catord, txtf.); OUTPUT; END; RUN; 49 Möglichkeiten des SELECT Statement Mehrere Elemente im SELECT Statement select(a); when (3,4,5) put ">= 3"; when (1,2) put "<= 2"; otherwise; end; Ohne Klammer hinter SELECT, komplette Bedingung direkt in WHEN Klammer select; when (a IN (3,4,5)) put ">= 3"; when (b IN (1,2)) put "<= 2"; otherwise; end; 50 CALL MISSING Neue Funktion zum Initialisieren (ab SAS 9.2) Weist einer Liste von Variablen missing values zu Die Variablen können numerisch oder alphanumerisch sein DATA dummy; LENGTH num1 num2 4 char1-char3 $10 ; CALL MISSING(num1, num2, char1, char2, char3); RUN; entspricht num1 =.; num2 =.; char1 = " "; char2 = " "; char3 = " "; 51 Nützliche Funktionen SMALLEST und LARGEST (ab SAS 9) STRIP (ab SAS 9) COMPBL (ab SAS 8) SUBSTRN (ab SAS 9) 52 SMALLEST und LARGEST (ab SAS9) Mit MIN und MAX wird der kleinste bzw. größte Wert einer Zahlenreihe ermittelt SMALLEST und LARGEST ermitteln den x-kleinsten bzw. x-größten Wert einer Zahlenreihe. Syntax: SMALLEST (k, value-1<,value-2 ...>) LARGEST (k, value-1<,value-2 ...>) k kann eine Zahl, eine Variable oder ein Ausdruck sein Missings werden ans Ende sortiert. Es werden immer erst die vorhandenen Werte genommen und erst wenn die Anzahl von k größer ist als die Anzahl der vorhandenen Werte wird missing ausgegeben. 53 SMALLEST und LARGEST (ab SAS9) Wenn erstes Argument von Largest/Smallest=1, dann gleiches Ergebnis wie max/min do k = 1 to 4; xklein = SMALLEST (k, 456, 789, ., 123); xgross = LARGEST (k, 456, 789, ., 123); output; end; k SMALLEST LARGEST 1 123 789 2 456 456 3 789 123 4 54 STRIP (ab SAS9) Führende und abschließende Leerzeichen werden entfernt Leerzeichen innerhalb des Arguments bleiben Verkürzung gegenüber SAS8: left(trim(text)) Syntax: strip(text) svar="*"||STRIP(" *Text1 Text1 Text2 ")||"*"; Text2* 55 COMPBL (ab SAS8) Syntax: compbl(text) Mehrfache Leerzeichen werden durch nur ein Leerzeichen ersetzt cvar="*"||COMPBL(" Text1 Text2 ")||"*"; * Text1 Text2 * 56 Vergleich STRIP und COMPBL Text * STRIP *Text1 Text1 Text2 * Text2* COMPBL * Text1 Text2 * Zur Lesbarkeit ist der Text hier jeweils umrahmt von * 57 SUBSTRN (ab SAS9) Verwandt mit SUBSTR Funktion, verhält sich anders bei ‘ungültigen’ Argumenten (z.B. negative Startposition, negative Textlänge) SUBSTRN ist robuster Syntax: SUBSTRN (Text, Position <, Länge>) • Text: Zeichenkette. Wenn numerisch Umwandlung (BEST32, Leerzeichen entfernt an Anfang und Ende) • Position: Startposition für Extraktion im text wenn <0 dann wird Ergebnis abgeschnitten, Anfang=erstes Zeichen im Text, Länge wird angepasst • Länge: Länge der zu extrahierenden Zeichenkette wenn nicht definiert: von Position bis Textende, wenn länger als Text, dann bis Textende Länge der Ausgabevariable: Wenn nicht vorher festgelegt, dann Länge des ersten Argumentes 58 Vergleich SUBSTRN und SUBSTR Wenn das erste Argument numerisch ist data substrn; vsubstr = "*" || SUBSTR(1234.5678,2,6) || "*"; vsubstrn = "*" || SUBSTRN(1234.5678,2,6) || "*"; run; Ergebnis: vsubstr =* 1234* vsubstrn=*234.56* Stellenübersicht 123456789012 1234.5678 1234.5678 59 Vielen Dank für Ihre Aufmerksamkeit! Sabine Erbslöh, Christina Gelhorn Accovion GmbH, Eschborn [email protected] [email protected] 60