Chamar dinamicamente a macro da etapa de dados sas

Este código executa bem quando executar como um programa SAS:

%MyMacro(foo_val, bar_val, bat_val);

Eu criei uma tabela usando:

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

Eu quero executar MyMacro uma vez para cada linha da tabela de analyses .

O código a seguir aparece apenas para passar os valores de string title , weight e response (em vez dos valores de dados foo1 etc.) para minha macro (testado com chamadas para o comando %put ):

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

Como posso invocar a macro uma vez por registro da tabela de analyses enquanto passam valores de dados como argumentos para a macro? A intenção é realmente executar isso para um número muito grande de análises, de modo que a solução deve ser dimensionada adequadamente para muitos mais registros na tabela de analyses .

Isso depende em parte do que sua macro está fazendo. Se assumirmos que sua macro está fazendo algo que deve ser executado fora de uma etapa de dados (ou seja, não está apenas atribuindo uma variável de etapa de dados), então você tem várias opções.

  • CHAMAR EXECUTAR
  • SQL PROC: variável de macro SELECT INTO
  • Gravar chamadas de macro em um arquivo% INCLUDE
  • DOSUBL

CHAMAR EXECUTAR já foi explicado e é uma boa opção para alguns casos. Tem algumas desvantagens, no entanto, particularmente com o tempo de macro, que requer algum cuidado extra para proteger em alguns casos – especialmente quando você está criando variables ​​de macro dentro de sua macro. Quentin em seus comentários mostra uma maneira de contornar isso (adicionando %NRSTR à chamada), mas eu acho que prefiro usar o CALL EXECUTE quando há uma vantagem em fazer isso sobre os outros methods – particularmente, se eu quiser usar Técnicas de etapa de dados SAS (como FIRST ou LAST, por exemplo, ou alguma forma de loop) na criação de minhas chamadas de macro, ou quando tenho que fazer coisas em uma etapa de dados e evitar a sobrecarga de ler o arquivo outra vez. Se eu estou apenas escrevendo uma etapa de dados como a sua acima – dados algo, defina algo, chame execute, execute – eu não usaria.


PROC SQL SELECT INTO é tipicamente o que eu uso para o processamento de listas (que é basicamente o que é isso). Eu gosto da simplicidade do SQL um pouco melhor ao fazer coisas que não são muito complicadas; por exemplo, você pode obter apenas uma versão de cada chamada de macro facilmente com DISTINCT sem precisar escrever explicitamente um proc sort nodupkey ou usar o processamento anterior / final. Também tem a vantagem de depurar que você pode gravar todas as suas chamadas de macro na sua janela de resultados (se você não adicionar o noprint ), o que é um pouco mais fácil de ler do que o log para mim se eu estiver tentando entender por quê minhas chamadas não foram geradas corretamente (e não recebem instruções PUT extras).

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

Isso os executa de forma bastante simples e não tem problemas de tempo (como você está apenas escrevendo um monte de chamadas de macro).

A principal desvantagem deste método é que você tem no máximo 64k caracteres em uma variável de macro, então se você estiver escrevendo um grande número deles, você encontrará isso. Nesse caso, use os arquivos CALL EXECUTE ou %INCLUDE .


%INCLUDE arquivos %INCLUDE são amplamente úteis como substitutos de SELECT INTO quando a chamada está acima do limite de caracteres, ou se você achar útil ter um arquivo de texto para ver com suas chamadas (se você estiver executando isso no modo batch, por exemplo , isso poderia ser mais fácil de obter e / ou analisar do que registrar ou listar a saída). Você acabou de escrever suas chamadas para um arquivo e %INCLUDE esse arquivo.

 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; 

Eu realmente não uso muito mais isso, mas é uma técnica comum usada particularmente por programadores SAS mais antigos.


DOSUBL é um método relativamente novo e, até certo ponto, pode ser usado para replace o CALL EXECUTE já que seu comportamento padrão é tipicamente mais próximo do que você espera intuitivamente do que o CALL EXECUTE . A página doc tem realmente o melhor exemplo de como isso funciona de maneira diferente; Basicamente, ele corrige o problema de temporização permitindo que cada chamada separada pareça importar e exportar as variables ​​macro de / para o ambiente de chamada, o que significa que cada iteração do DOSUBL é executada em um horário distinto versus CALL EXECUTE onde tudo é executado em um grupo e O ambiente macro é ‘fixo’ (ou seja, qualquer referência a uma variável de macro é corrigida em tempo de execução, a menos que você escape de forma %NRSTR com %NRSTR ).


Mais uma coisa que vale a pena mencionar é RUN_MACRO , uma parte da linguagem FCMP . Isso permite que você execute completamente uma macro e importe seu conteúdo de volta para a etapa de dados , que é uma opção interessante em alguns casos (por exemplo, você pode envolver uma chamada em torno de uma SQL PROC que selecionou uma contagem de algo e depois importar para o dataset como uma variável, tudo em um datastep). É aplicável se você estiver fazendo isso com a finalidade de chamar uma macro para atribuir uma variável de etapa de dados, não para executar um processo que faz coisas que não precisam ser importadas para a etapa de dados, mas é algo que vale a pena considerar se você Deseja que todos os dados de volta no dataset que chamou o processo.

Você poderia usar CALL EXECUTE:

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

Você pode colocar os valores das variables ​​em macrovariables e depois chamar seu %MyMacro muitas vezes (o número de obs em seu dataset) com as macrovariables ​​como argumento:

Dados:

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

Código para executar 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; 
Intereting Posts