2014-08-28 7 views
5

Questo codice viene eseguito bene quando eseguito come un programma SAS:dinamicamente chiamano macro dalla sas passo dati

%MyMacro(foo_val, bar_val, bat_val);

ho creato una tabella utilizzando:

DATA analyses; 
    input title : $32. weight : $32. response : $32.; 
    datalines; 
foo1 bar1 bat1 
foo2 bar2 bat2 
; 

voglio eseguire MyMacro una volta per ogni riga della tabella analyses.

Il seguente codice sembra passare solo i valori della stringa title, weight e response (piuttosto che i valori dei dati foo1 ecc) per la mia macro (testato con chiamate al comando di %put):

DATA _NULL_ ; 
    set analyses; 
    %MyMacro(title, weight, response); 

RUN; 

Come posso richiamare la macro una volta per record della tabella analyses passando i valori dei dati come argomenti alla macro? L'intenzione è di eseguirla effettivamente per un numero molto elevato di analisi, pertanto la soluzione deve essere adattata in modo appropriato a molti altri record nella tabella analyses.

risposta

9

Questo in parte dipende da cosa sta facendo la tua macro. Se supponiamo che la tua macro stia facendo qualcosa che è destinato ad essere eseguito al di fuori di un passo di dati (cioè, non si tratta solo di assegnare una variabile del passaggio dati), allora hai diverse opzioni.

chiamata execute è già stato spiegato, ed è una buona opzione per alcuni casi. Ha alcuni aspetti negativi, tuttavia, in particolare con la temporizzazione delle macro, che richiede in alcuni casi un'attenzione particolare da proteggere, in particolare quando si creano variabili macro all'interno della macro. Quentin nei suoi commenti mostra un modo per aggirare questo problema (aggiungendo %NRSTR alla chiamata), ma trovo che preferisco usare CALL EXECUTE solo quando c'è un vantaggio rispetto agli altri metodi - in particolare, se voglio usare SAS tecniche di step di dati (come FIRST o LAST, ad esempio, o qualche forma di looping) nella creazione delle mie macro chiamate, o quando devo fare comunque le cose in un passo di dati e posso evitare il sovraccarico di leggere il file un'altra volta. Se sto semplicemente scrivendo un passo di dati come il tuo qui sopra: dati qualcosa, imposta qualcosa, chiama execute, run - non lo userei.


PROC SQL SELECT INTO è tipicamente quello che uso per l'elaborazione lista (che è in gran parte di cosa si tratta). Mi piace la semplicità di SQL un po 'meglio quando faccio cose che non sono troppo complicate; ad esempio, è possibile ottenere facilmente una sola versione di ciascuna macro chiamata con DISTINCT senza dover scrivere esplicitamente un proc sort nodupkey o utilizzare la prima/ultima elaborazione. Ha anche il vantaggio di eseguire il debug che è possibile scrivere tutte le chiamate macro alla finestra dei risultati (se non si aggiunge noprint), che è un po 'più facile da leggere rispetto al registro per me se sto cercando di capire perché le mie chiamate non sono state generate correttamente (e non prende alcuna istruzione PUT in più).

proc sql; 
    select catx(',','%macro(',arg1,arg2,arg3)||')' 
    into :mvarlist separated by ' ' 
    from dataset; 
quit; 

&mvarlist. 

Che li corre molto semplicemente, e non ha problemi di temporizzazione (come si sta solo scrivendo un sacco di chiamate macro out).

Lo svantaggio principale di questo metodo è che hai un massimo di 64k caratteri in una variabile macro, quindi se stai scrivendo un numero enorme di questi ti imbatterai in quello. In tal caso, utilizzare i file CALL EXECUTE o %INCLUDE.


%INCLUDE file sono in gran parte utile sia come ricambio per SELECT INTO quando la chiamata è sopra il limite di caratteri, o se lo trovate utile avere un file di testo da guardare con le chiamate (se si sta eseguendo questo per esempio, in modalità batch, questo potrebbe essere più facile da raggiungere e/o analizzare rispetto all'output di registro o elenco). Basta scrivere le chiamate su un file, e quindi il file %INCLUDE.

filename myfile temp; *or a real file if you want to look at it.; 
data _null_; 
set dataset; 
file myfile; 
put @1 catx(',','%macro(',arg1,arg2,arg3)||')'; 
run; 

%include myfile; 

non ho davvero usare questo molto più, ma è una tecnica comune usata soprattutto dai più anziani programmatori SAS così bello sapere.


DOSUBL è un metodo relativamente nuovo, e in qualche misura può essere utilizzato per sostituire CALL EXECUTE come il suo comportamento predefinito è in genere più vicino a quello che ci si aspetta in modo intuitivo di CALL EXECUTE 's. La pagina doc ha davvero il miglior esempio di come funzioni in modo diverso; fondamentalmente, risolve il problema di temporizzazione lasciando che ogni chiamata separata sembri importare ed esportare le variabili macro da/verso l'ambiente chiamante, il che significa che ogni iterazione di DOSUBL viene eseguita in un momento distinto rispetto a CALL EXECUTE dove tutto viene eseguito in un mazzo e la macro l'ambiente è 'fisso' (cioè, qualsiasi riferimento a una variabile macro viene corretto in fase di esecuzione, a meno che non lo si passi in modo disordinato con %NRSTR).


più Una cosa degno di nota è RUN_MACRO, una parte del linguaggio FCMP. Ciò consente di eseguire completamente una macro e di importarne il contenuto al passaggio dati, che in alcuni casi è un'opzione interessante (ad esempio, è possibile racchiudere una chiamata in un PROC SQL che ha selezionato un conteggio di qualcosa, quindi importalo nel dataset come variabile, tutto in un unico datastep). È applicabile se stai facendo questo allo scopo di chiamare una macro per assegnare una variabile di passaggio dati, non per eseguire un processo che fa cose che non devono essere importate nel passo dati, ma è qualcosa che vale la pena considerare se vuoi che quei dati tornino tutti nel set di dati che ha chiamato il processo.

+1

Riassunto molto bello. Sto ancora sperando che più documentazione arriverà da SAS re DOSUBL e RUN_MACRO. Mi sembra che entrambi questi codici eseguano in qualche modo una sessione mini-SAS separata. E quando ci ho giocato un paio di anni fa, le regole di scoping non erano molto chiare. Ad esempio, le opzioni di sistema impostate in una macro richiamata con run_macro potrebbero o potrebbero non modificare le opzioni della sessione principale. Ma sono certamente strumenti potenti, vale la pena imparare. – Quentin

+0

Bella risposta. Vale anche la pena considerare che la macro può essere riscritta come una funzione FCMP? –

+0

Non vedo l'ora che le funzioni FCMP siano più numerose delle macro ... anche se in questo caso sembra un'analisi completa, che probabilmente è meglio non essere scritto in questo modo per ora (mentre RUN_MACRO consente di scrivere in questo modo , non sono convinto che sia davvero una buona idea se non ci sono altri grandi vantaggi ad esso). – Joe

0

È possibile inserire le variabili di valori in macrovariabili e poi chiamare i tuoi %MyMacro molte volte (il numero di OB nel set di dati) con la macrovariabili come argomento:

dati:

DATA analyses; 
    input title : $32. weight : $32. response : $32.; 
    datalines; 
foo1 bar1 bat1 
foo2 bar2 bat2 
; 
run; 

Il codice per eseguire macro:

data _NULL_; 
    set analyses end=fine; 
    call symput("ARGUMENT"||compress(_N_),catx(",",title,weight,response)); 
    if fine then call symput("NLOOPS",compress(_N_)); 
run; 
%*PUT &ARGUMENT1; 
%*PUT &ARGUMENT2; 

%MACRO MAIN; 
%DO L=1 %TO &NLOOPS; 
    %MyMacro(&&ARGUMENT&L); 
%END; 
%MEND; 
%MAIN; 
+0

Sembra molto hacky e non capisco perché non riesca a chiamare la macro dall'interno di un passo 'DATA' - potresti spiegare perché questo approccio è necessario? – JustinJDavies

+0

Cambiato secondo '% DO' a'% TO' – JustinJDavies

+1

L'approccio non è riuscito a causa di problemi di temporizzazione.Le istruzioni macro vengono eseguite prima dell'esecuzione del codice di passaggio dei dati. Questo perché il lavoro del linguaggio macro è quello di generare il codice SAS. Nel tuo esempio, la macro vede il titolo come argomento di una stringa di testo. Non sa che esiste una variabile del set di dati chiamata titolo che ha un valore. L'approccio di esecuzione delle chiamate è più simile a quello che ti aspettavi. Si tratta di un'istruzione di passo dati che, quando viene eseguita, può richiamare una macro e può passare argomenti dai valori del set di dati. Vi sono anche interessanti problemi di temporizzazione. (vedi il mio commento lì). – Quentin

4

È possibile utilizzare CALL EXECUTE:

data _null_; 
    set analyses; 
    call execute('%nrstr(%MyMacro('||title||','||weight||','||response||'))'); 
run; 
+1

Mi piace chiamare approccio di esecuzione. Si noti che la macro verrà eseguita (tutti i simboli macro risolti) mentre il passaggio dei dati è in esecuzione. Ciò significa che se% MyMacro genera variabili macro dal codice del passaggio dei dati, non funzionerà. Di solito è utile aggiungere% nrstr(), che chiamerà execute generare la chiamata alla macro, ma la macro non verrà eseguita fino a quando il passaggio dei dati non sarà completato, ad esempio: call execute ('% nrstr (% MyMacro (' || titolo || ' '|| peso ||', '|| risposta ||'))'); vedere http://support.sas.com/kb/23/134.html – Quentin

+2

vedere anche DOSUBL come alternativa a CALL EXECUTE, a cui si fa riferimento nella stessa pagina di supporto, apparentemente gestisce il problema di temporizzazione senza la necessità di racchiudere la chiamata macro in% NRSTR() – Quentin

+0

Ho aggiornato la mia risposta per utilizzare% nrstr, come indicato da @Quentin – DavB

Problemi correlati