Inhalte unterschiedlicher Felder aufgrund des Formates ändern

Hallo

als Konsequenz aus meinem Datum=31.12.9999-Problem habe ich mich nun daran versucht, einen Dataset per Macro oder Datastep glattzuziehen... Leider bin ich gescheitert; ich bekomme nur Teillösungen hin.

IST:
Ich habe mehrere aus Excel importierte Tabellen, in denen Spalten an unterschiedlichen Positionen Datümer ausweisen. Die Spalten haben alle das Format DATETIME20. und manche Datümer lauten auf 02JAN****:00:00:00.

SOLL:
Im Prinzip sollen zwei Dinge geschehen:
1. DATETIME20. in DDMMYYp10. umwandeln
2. 02JAN**** soll zum 31.12.9999 verändert werden.
Wenn die Feldnamen immer bekannt wären, ginge dies einfach in dieser Form:

DATA TST2;
    
SET WORK.TST;
    
FORMAT GUELTIG_BIS DDMMYYP10.;
    
IF DATEPART(GUELTIG_BIS) - 2 = MDY(12,31,9999) THEN
        
DO;
            GUELTIG_BIS = DATEPART(GUELTIG_BIS) -
2;
        
END;
    
ELSE
        
DO;
            GUELTIG_BIS = DATEPART(GUELTIG_BIS);
        
END;
RUN;

Für das FORMAT-Statement dachte ich an ein Macro, welches mir die Felder ausgibt, welche das Format DATETIME20. haben. Eine Liste aller Feldnamen bekomme ich dank dieses Forums (Danke für %VARLIST) ja schon hin, aber diese nun einzuschränken auf die Felder mit dem gesuchten Format gelingt mir nicht. Dafür wird es wohl die Funktion VFORMAT geben, aber die bekomme ich nicht ans fliegen :-(

Im Prinzip müsste mir ein Makro den obigen DATASET dynamisch (je nach dem welche und wie viele Spalten das Format DATETIME20. haben) erzeugen. Geht das überhaupt oder denke ich inzwischen zu sehr um die Ecke ?!?

Gruß aus Hamburg
Marco Schmidt

Formate

Hallo Marco,

ich habe das Macro VarListX ein wenig für Deinen Anspruch modifiziert.
Anstelle des Suchmusters Pattern habe ich einfach eine andere Bedingung für Formate eingesetzt. So müsste es gehen:

%macro VarListY( dset  
                , sep=
%str( )
                , fmt=
                );
  
/*----------------------------------------------------------------------
   *   Macro: VarListY as a modification of the macro Varlist

   *   Macro:      VarList  
   *   Programmer: Pete Lund  / Hans Kneilmann  / Wolfgang Hornung
   *   Date:       Sept. 2000 / Nov. 2006       / Juni 2007
   *   Purpose:    Create a macro variable that contains all the  
   *      variable names in a dataset where the format
   *       matches, separated by any character(s) specified.
   *       Can be used in KEEPs, DROPs, SQL SELECTs, etc.  
   *  
   *   Parameters:  
   *      DSET - the name of the dataset (libref.member)  
   *      SEP  - character(s) to separate the variable names  
   *               (default is a space)  
   *      FMT  - the formatname
   *  
   *--------------------------------------------------------------------*/
  
%local ii dsid varlist;  
  
%global rc;  
  
%if &fmt. eq %then %let fmt=absoluter_blödsinn;
  
%let fmt=%upcase(&fmt.);
  
/* make sure that the dataset exists */  
  
%if %sysfunc(exist(&dset.)) EQ 0  
  
%then %do;  
    
%put %substr(###WARNING,4): &DSET. does not exist;  
    
%let rc=1;                  /* 1 if 'Fehler'  */  
    
%let varlist=;  
    
%goto Quit;  
  
%end;  
  
/*****  
        If it does exist, open the dataset. We'r going to create  
        a variable (varlist) which contains the list of variables  
        in the dataset, separated by the specified separator char-  
        acter(s). Start VarList off with the name of the first  
        variable in the dataset.  
  *****/  
  
%let varlist=;
  
%let dsid   =%sysfunc( open(&dset.) );  
  
%let feld=%upcase(%sysfunc( varname( &dsid., 1 ) ));  
  
%if %sysfunc(varfmt(&dsid.,1)) ne &fmt. %then %do;
    
%put INFO: Überspringe Feld &feld. (fmt=&fmt.);
  
%end;
  
%else %do;
    
%let varlist=&varlist.&feld.;  
  
%end;
  
  
/*****  
        Look through the rest of the variables in the dataset and  
        add them to the VarList value (with the sparator char in  
        between values).  
  *****/  
  
%do ii=2 %to %sysfunc(attrn(&dsid., nvars));  
    
%let feld=%upcase(%sysfunc( varname( &dsid., &ii. ) ));  
  
%if %sysfunc(varfmt(&dsid.,&ii.)) ne &fmt. %then %do;

      
%put INFO: Überspringe Feld &feld. (fmt=&fmt.);
    
%end;
    
%else %do;
      
%if &varlist. EQ %then %do;
        
%let varlist=&feld.;  
      
%end;
      
%else %do;
        
%let varlist=&varlist.&sep.&feld.;  /* falls für ii=1 galt: überspringe Feld */
      
%end;
    
%end;
  
%end;  
  
  
/*****  
        Close the dataset.  
  *****/  
  
%let rc =%sysfunc( close(&dsid.) );  
  
  %Quit:  
  
  
/*****  
        Write out the variable name list.  
  *****/  
  
&varlist.  


%mend VarListY;
options mlogic symbolgen mprint;
data test;
set sashelp.prdsal2;
format %VarListY( sashelp.prdsal2, sep=%str( )  ,fmt=MONYY.) ddmmyy10.;
run;

In dem Beitrag von Herrn Kneilmann hier ist meines Erachtens ein kleiner Fehler. In folgendem Teil muss etwas vertauscht werden.


%if &varlist. EQ %then %do;
        
%let varlist=&varlist.&sep.&feld.;  
      
%end;
      
%else %do;
        
%let varlist=&feld.;  /* falls für ii=1 galt: überspringe Feld */
/* Ich meine die LET-Anweisungen müssen vertauscht werden Hornung 29.06.2007 */

LET nicht tauschen

ich habe es eben im anderen Zusammenhang getestet. Die LET-Anweisungen dürfen nicht getaucht werden, denn

Wenn &varlist = leer, dann "%if &varlist. EQ" = true

Gruß aus Hamburg
Marco Schmidt

let tauschen

ist erledigt in meinem Blog und 'zu Hause' in der Vorlage. Danke für die Info!
Hallo Macro Schmidt,
die Let-Anweisung muß getauscht werden ... (Nachtrag: siehe unten, ich war zu spät).
Gruß Hans Kneilmann, Schäfer Shop GmbH (SSI)

LET tauschen

Hallo Marco,

in meinem Beispiel ist die LET-Anweisung in der richtigen Reihenfolge, wahlweise hätte man auch das EQ durch ein NE ersetzen können (s.a. Beitrag von Herrn Kneilmann hier).

Begründung:

Wenn &varlist gleich leer, dann soll varlist den Inhalt von feld bekommen, ansonsten soll der neue Inhalt von feld mit dem Separator &sep getrennt angehängt werden.

Im ursprünglichen Fall war es so, dass im Prinzip überschrieben wurde, d.h. bei mehreren Übereinstimmungen wurde die Letzte genommen.

Gruß
Wolfgang Hornung

Danke

Hallo Wolfgang,

varfmt ist die Function der Wahl :-) Die hatte ich in der Hilfe einfach überlesen.. Vielen Dank für den Tipp!

Ich werde aber die wirklich schlanke Lösung Herrn Mangold als Basis/Lösung weiterverwenden.

Gruß aus Hamburg
Marco

Format mit PROC CONTENTS ermitteln

Das Format kann man mit PROC CONTENTS ermitteln und die betreffenden Variabeln mit PROC SQL in Makrovariablen einlesen. Dann ist es recht einfach, im Data-Schritt mit einem Array alle betroffenen Variablen umzuwandeln.

data datuemer;
   
format gueltig_von gueltig_bis datetime20.;
   gueltig_von =
'01JAN2005:00:00:00'dt;   
   gueltig_bis =
'31DEC9999:00:00:00'dt + 2*24*3600;
   value =
12345;
   name =
'Text';
run;

%macro convert_date(data=, out=);
proc contents noprint data=&data out=contents;
run;

proc sql noprint;
   select name into :vars separated by
' '
      
from contents
      where format=
'DATETIME';
quit;

data &out;
   set &data;
   array _in &vars;
   do over _in;
      _in = datepart(_in);
      if abs(
'31dec9999'd - _in) < 5 then _in = '31dec9999'd;
   end;
   format &vars
ddmmyyp10.;
run;

proc sql;
   drop table contents;
quit;

%mend convert_date;

%convert_date(data=datuemer, out=bessere_datuemer)

Klasse

Hallo Herr Mangold,

die Lösung ist schlank, schnell und sogar schon vollständig! Vielen vielen Dank dafür. Das hilft wirklich weiter.

Gruß aus Hamburg
Marco Schmidt