2015-11-20 34 views
6

Tôi muốn trả về một giá trị từ một macro SAS mà tôi đã tạo nhưng tôi không chắc chắn như thế nào. Macro tính toán số lượng quan sát trong tập dữ liệu. Tôi muốn số lượng các quan sát được trả về.SAS - Cách trả về một giá trị từ một macro SAS?

%macro nobs(library_name, table_name); 
    proc sql noprint; 
    select nlobs into :nobs 
    from dictionary.tables 
    where libname = UPCASE(&library_name) 
     and memname = UPCASE(&table_name); 
    quit; 

    *return nobs macro variable; 
    &nobs 
%mend; 

%let num_of_observations = %nobs('work', 'patients'); 

Ngoài ra, tôi muốn biến số vĩ mô &nobs được sử dụng trong macro là cục bộ cho macro đó chứ không phải toàn cục. Làm thế nào tôi có thể làm điều đó?

+0

Tôi thích chức năng macro của Jack Hamilton để đếm số lần truy cập: http://www2.sas.com/proceedings/sugi26/p095-26.pdf – Quentin

+0

Cảm ơn bạn đã liên kết. Mối quan tâm chính của tôi ở đây là làm thế nào để trả về một giá trị từ một macro. Tôi cũng thực sự muốn biết làm thế nào để làm cho "vào" biến (trong một sql proc) địa phương và không toàn cầu. Đây là hai điều tôi cần phải làm liên tục với các macro khác. – bambi

+0

Bạn có chắc bạn cần biết bao nhiêu? Thông thường tôi chỉ muốn biết không ai hay không. Bạn sẽ làm gì với NOBS? –

Trả lời

2

Bạn không thể 'trả về' một giá trị từ macro kiểu hàm trừ khi bạn đã viết nó bằng cách sử dụng chỉ các câu lệnh macro. Liên kết của Quentin cung cấp một ví dụ về cách thực hiện điều này. Ví dụ: bạn không thể sử dụng macro của mình như vậy, vì proc sql không thể thực hiện ở giữa câu lệnh %put (điều này có thể với các cách giải quyết phức tạp hơn, ví dụ: dosubl, nhưng không phải cách bạn đã viết) .

%put %nobs(mylib,mydata); 

Điều tốt nhất bạn có thể thực hiện mà không thay đổi đáng kể là tạo biến macro chung và sử dụng trong các câu sau.

Để tạo biến macro cục bộ cho macro ban đầu, trước tiên bạn phải khai báo biến đó qua tuyên bố %local trong định nghĩa macro.

2

Một macro SAS chèn mã. Nó không bao giờ có thể trả về một giá trị, mặc dù trong một số trường hợp, bạn có thể bắt chước chức năng, thường bạn cần một công việc xung quanh như

%nobs(work, patients, toReturn=num_of_observations) 

** Để giúp bạn hiểu điều gì xảy ra, tôi khuyên in mã chèn bởi macro trong nhật ký của bạn:;

options mprint; 

Chúng tôi vượt qua tên của biến vĩ mô để điền vào vĩ mô, tôi thấy nó thực tế nhất để

  • không yêu cầu người dùng của vĩ mô của tôi để đặt dấu ngoặc kép quanh thư viện và tên thành viên
  • đặt tên biến là biến macro được đặt tên, vì vậy chúng tôi có thể đặt biến đó làm mặc định;

    % quý tộc vĩ mô (library_name, table_name, toReturn = nobs);

Hãy chắc chắn rằng các biến trở lại tồn tại

  • Nếu nó tồn tại nó được biết đến bên ngoài của macro này.
  • Otherwisse nếu chúng ta tạo nó ở đây, theo mặc định nó sẽ là địa phương và bị mất khi chúng ta rời khỏi macro;

    %if not %symexist(&toReturn.) %then %global &toReturn.; 
    

Trong SQL, tôi

  • sử dụng SASHELP.VTABLE, một cái nhìn được cung cấp bởi SAS trên dữ liệu meta của nó
  • thêm dấu ngoặc kép tôi bỏ qua trong các cuộc gọi vĩ mô ("", không phải '': biến macro không được thay thế bằng các dấu nháy đơn)
  • sử dụng hàm tăng giá% macro thay vì hàm upcase SAS, như đôi khi nó cải thiện hiệu suất;

    proc sql noprint; 
        select nobs 
        into :&toReturn. 
        from sashelp.vtable 
        where libname = %UPCASE("&library_name.") 
        and memname = %UPCASE("&table_name."); 
    quit; 
    

    % sửa đổi;

Chú ý nếu bạn gọi một macro trong macro, Chạy đoạn mã này và đọc các bản ghi để hiểu tại sao;

%macro test_nobs(); 
    %nobs(sashelp, class); ** will return the results in nobs **; 

    %nobs(sashelp, shoes, toReturn=num_of_shoes); 

    %let num_of_cars = ; 
    %nobs(sashelp, cars, toReturn=num_of_cars); 

    %put NOTE: inside test_nobs, the following macro variables are known; 
    %put _user_; 
%mend; 

%test_nobs; 
%put NOTE: outside test_nobs, the following macro variables are known; 
%put _user_; 
+0

Có thể ai đã bỏ phiếu cho tôi xuống vui lòng giải thích. Nó có hiệu quả với bạn không? –

+0

'Xin lỗi tôi và tôi xin lỗi vì thiếu bình luận. Bản sửa đổi bổ sung thêm nội dung hữu ích. Nhưng hai câu đầu tiên là không chính xác. Một macro có thể trả về một giá trị, do đó,% let num_of_observations =% nobs ('work', 'patient'); có thể làm việc. Đây là lợi ích của các macro kiểu hàm như được trình bày trong bài báo của Jack Hamilton có một macro kiểu hàm để đếm các quan sát. Do đó với tôi câu trả lời vẫn là sai. – Quentin

+0

Cảm ơn bạn đã chỉ cho tôi ví dụ của Hamilton, Quentin. Tuy nhiên, nó vẫn chỉ chèn các ký tự trong mã được biên dịch trước. Nó giải quyết câu hỏi bambi đưa ra làm ví dụ, nhưng không phải là câu hỏi cơ bản. –

3

Sự cố khi viết macro giống như hàm là gì?

tức là macro mà bạn có thể sử dụng như %let myVar = %myMacro(myArgument)

  • Bạn có thể sử dụng người dùng viết macro của bạn như thể nó là một chức năng nếu tất cả các bạn làm là
    • kêu gọi một số %doSomething(withSometing) như chức năng vĩ mô
    • chỉ định giá trị cho biến macro với câu hỏi %let someVar =
    • "trả lại" kết quả của bạn, thường bằng cách viết.210 trên dòng cuối cùng trước khi bạn %mend
  • Ngay sau khi bạn bao gồm một proc hoặc data bước trong macro của bạn, điều này không làm việc nữa
  • May mắn thay,% sysFunc() đến để giải thoát, vì vậy chúng tôi có thể sử dụng bất kỳ chức năng bước dữ liệu
  • này bao gồm các chức năng ở mức độ thấp như open, fetchclose mà thậm chí có thể truy cập vào dữ liệu của bạn
  • người Nerdy có thể làm khá nhiều với nó, nhưng ngay cả khi bạn Nerdy, sếp của bạn sẽ hiếm khi cho bạn thời gian để làm như vậy.

Làm cách nào để chúng tôi giải quyết vấn đề này?, tức là tôi sử dụng khối xây dựng nào để giải quyết vấn đề này?

  • proc fcmp cho phép đóng gói một số báo cáo bước dữ liệu trong một chương trình con hoặc chức năng
  • Chức năng này, có nghĩa là để sử dụng trong một bước dữ liệu, có thể được sử dụng trong vòng %sysfunc()
  • Trong chức năng này, bạn có thể gọi run_macro để thực hiện bất kỳ vĩ mô trong nền nGAY

Bây giờ chúng ta đã sẵn sàng cho các giải pháp thực tiễn

Bước 1: viết một helper vĩ mô

  • không có tham số,
  • sử dụng một số các biến vĩ mô toàn cầu
  • "trở về" kết quả của nó trong một biến vĩ mô toàn cầu

Tôi biết đó là thói quen viết mã xấu, nhưng để giảm thiểu rủi ro, chúng tôi đủ điều kiện cho các biến đó với tiền tố. Áp dụng cho ví dụ trong câu hỏi

** macro nobsHelper retrieves the number of observations in a dataset 
    Uses global macro variables: 
     nobsHelper_lib: the library in which the dataset resides, enclosed in quotes 
     nobsHelper_mem: the name of the dataset, enclosed in quotes 
    Writes global macro variable: 
     nobsHelper_obs: the number of observations in the dataset 

    Take care nobsHelper exists before calling this macro, or it will be ost 
**; 
%macro nobsHelper(); 
    ** Make sure nobsHelper_obs is a global macro variable**; 
    %global nobsHelper_obs; 

    proc sql noprint; 
     select nobs 
     into :nobsHelper_obs 
     from sashelp.vtable 
     where libname = %UPCASE(&nobsHelper_lib) 
      and memname = %UPCASE(&nobsHelper_mem); 
    quit; 
    %* uncomment these put statements to debug **; 
    %*put NOTE: inside nobsHelper, the following macro variables are known; 
    %*put _user_; 
%mend; 

Bước 2: viết một hàm helper;

**Functions need to be stored in a compilation library; 
options cmplib=sasuser.funcs; 

** function nobsHelper, retrieves the number of observations in a dataset 
    Writes global macro variables: 
     nobsHelper_lib: the library in which the dataset resides, enclosed in quotes 
     nobsHelper_mem: the name of the dataset, enclosed in quotes 
    Calls the macro nobsHelper 

    Uses macro variable: 
     nobsHelper_obs: the number of observations in the dataset 
**; 
proc fcmp outlib=sasuser.funcs.trial; 
    ** Define the function and specity it should be called with two character vriables **; 
    function nobsHelper(nobsHelper_lib $, nobsHelper_mem $); 
     ** Call the macro and pass the variables as global macro variables 
     ** The macro variables will be magically qouted **; 
     rc = run_macro('nobsHelper', nobsHelper_lib, nobsHelper_mem); 
     if rc then put 'ERROR: calling nobsHelper gave ' rc=; 

     ** Retreive the result and pass it on **; 
     return (symget('nobsHelper_obs')); 
    endsub; 
quit; 

Bước 3: viết một macro thuận tiện để sử dụng người giúp;

** macro nobs retrieves the number of observations in a dataset 
    Parameters: 
     library_name: the library in which the dataset resides 
     member_name: the name of the dataset 
    Inserts in your code: 
     the number of observations in the dataset 
    Use as a function 
**; 
%macro nobs(library_name, member_name); 
    %sysfunc(nobsHelper(&library_name, &member_name)); 

    %* Uncomment this to debug **; 
    %*put _user_; 
%mend; 

Cuối cùng sử dụng nó;

%let num_carrs = %nobs(sasHelp, cars); 
%put There are &num_carrs cars in sasHelp.Cars; 

Data aboutClass; 
    libname = 'SASHELP'; 
    memname = 'CLASS'; 
    numerOfStudents = %nobs(sasHelp, class); 
run; 

Tôi biết điều này là phức tạp nhưng ít nhất tất cả các công việc Nerdy được thực hiện. Bạn có thể sao chép, dán và sửa đổi điều này trong một thời gian mà sếp của bạn sẽ chấp nhận. ;

+1

Ah, giải pháp FCMP/RUN_MACRO - sẽ viết một cách riêng biệt, tốt để xem bạn đã thực hiện nó. Rất mong nhận xét. – Joe

+1

Vâng, đây là một cách tiếp cận tốt đẹp, những gì Mike Rhoads gọi là "Macro Function Sandwich" http://support.sas.com/resources/papers/proceedings12/004-2012.pdf. Vì vậy, bây giờ có lẽ ai đó sẽ thêm giải pháp DOSUBL tương tự, ví dụ: https://support.sas.com/resources/papers/proceedings13/032-2013.pdf. Tôi đã có đặt phòng về DOSUBL do khả năng va chạm phạm vi, nhưng lòng tốt của tôi nó trông thực sự tốt đẹp. Vì vậy, hy vọng nó sẽ được tinh chế trong các phiên bản tương lai. – Quentin

+0

Cảm ơn bạn đã thử thách, Bambie! Joe, Quentin và tôi rất thích nó! –

7

Tôi sẽ trả lời câu hỏi cốt lõi Bambi hỏi trong ý kiến:

mối quan tâm chính của tôi ở đây là làm thế nào để trả về một giá trị từ một macro.

Tôi sẽ phân biệt với Dirk ở đây một cách quan trọng. Anh ấy nói:

Một macro chèn mã. Nó không bao giờ có thể trả về một giá trị, mặc dù trong một số trường hợp, bạn có thể bắt chước các hàm

Tôi không đồng ý. Một macro SAS trả về văn bản được chèn vào luồng xử lý. Trả về hoàn toàn là một thuật ngữ thích hợp cho điều đó. Và khi văn bản xảy ra là một số duy nhất, thì tốt hơn là nói rằng nó trả về một giá trị.

Tuy nhiên, macro chỉ có thể trả về một giá trị nếu macro chỉ có câu lệnh macro ngoài giá trị đó. Có nghĩa là, mỗi dòng phải bắt đầu bằng một số %. Bất cứ điều gì không bắt đầu với % sẽ được trả lại (và một số thứ bắt đầu với % cũng có thể được trả lại).

Vì vậy, câu hỏi quan trọng là, Làm cách nào để trả lại chỉ giá trị từ macro.


Trong một số trường hợp, như thế này, nó hoàn toàn có thể xảy ra với chỉ mã vĩ mô. Trong thực tế, trong nhiều trường hợp, điều này là có thể về mặt kỹ thuật - mặc dù trong nhiều trường hợp nó hoạt động nhiều hơn bạn nên làm.

Kết nối của Jack Hamilton paper bao gồm ví dụ phù hợp tại đây. Ông bác bỏ ví dụ này, nhưng đó là phần lớn vì bài báo của ông là về việc đếm các quan sát trong trường hợp NOBS sai - hoặc với mệnh đề WHERE hoặc trong một số trường hợp khác mà bộ dữ liệu đã được sửa đổi mà không cần cập nhật siêu dữ liệu NOBS.

Trong trường hợp của bạn, bạn có vẻ hoàn toàn hạnh phúc khi tin tưởng NOBS - vì vậy ví dụ này sẽ thực hiện.

Một vĩ mô mà trả về một giá trị phải có chính xác một tuyên bố rằng một trong hai không phải là một tuyên bố cú pháp vĩ mô, hoặc một tuyên bố cú pháp vĩ mô mà trả về một giá trị vào trong dòng xử lý. %sysfunc là một ví dụ về một tuyên bố làm như vậy. Những thứ như %let, %put, %if, v.v. là các câu lệnh cú pháp không trả lại bất kỳ thứ gì (tự mình); vì vậy bạn có thể có nhiều thứ như bạn muốn.

Bạn cũng để có một tuyên bố đặt giá trị trong luồng xử lý: nếu không bạn sẽ không nhận được bất kỳ thứ gì ngoài macro của mình.

Đây là một phiên bản rút gọn vĩ mô của Jack vào cuối trang 3, đơn giản để loại bỏ các nlobsf rằng anh ta đang thấy là sai:

%macro check; 

    %let dsid = %sysfunc(open(sashelp.class, IS)); 
    %if &DSID = 0 %then 
    %put %sysfunc(sysmsg()); 

    %let nlobs = %sysfunc(attrn(&dsid, NLOBS)); 

    %put &nlobs; 

    %let rc = %sysfunc(close(&dsid)); 

%mend; 

Đó vĩ mô là không macro chức năng phong cách. Nó không trả lại bất kỳ thứ gì cho luồng xử lý! Nó rất hữu ích cho việc xem nhật ký, nhưng không hữu ích cho bạn một giá trị mà bạn có thể lập trình. Tuy nhiên, đó là một khởi đầu tốt cho một macro kiểu hàm, bởi vì những gì bạn thực sự muốn là &nlobs, phải không?

%macro check; 

    %let dsid = %sysfunc(open(sashelp.class, IS)); 
    %if &DSID = 0 %then 
    %put %sysfunc(sysmsg()); 

    %let nlobs = %sysfunc(attrn(&dsid, NLOBS)); 

    &nlobs 

    %let rc = %sysfunc(close(&dsid)); 

%mend; 

Bây giờ đây là một macro chức năng phong cách: nó có một tuyên bố đó không phải là một tuyên bố cú pháp vĩ mô, &nlobs. trên một dòng đơn giản tất cả của chính nó.

Nó thực sự nhiều hơn bạn cần bởi một tuyên bố; nhớ cách tôi nói rằng %sysfunc trả về một giá trị cho luồng xử lý?Bạn có thể loại bỏ các %let phần của tuyên bố rằng, để lại cho bạn

%sysfunc(attrn(&dsid, NLOBS)) 

Và sau đó giá trị sẽ được đặt ngay trong luồng xử lý riêng của mình - cho phép bạn sử dụng nó trực tiếp. Tất nhiên, nó không phải là dễ dàng để gỡ lỗi nếu có điều gì sai, nhưng tôi chắc chắn bạn có thể làm việc xung quanh đó nếu bạn cần. Cũng lưu ý sự vắng mặt của dấu chấm phẩy ở cuối câu lệnh - điều này là do dấu chấm phẩy không bắt buộc đối với các hàm macro để thực thi và chúng tôi không muốn trả về bất kỳ dấu chấm phẩy không liên quan nào.

Hãy cũng được cư xử và thêm một vài %local s để có được điều này tốt đẹp và an toàn, và làm cho tên của bộ dữ liệu một tham số, bởi vì bản chất abhors một vĩ mô không có tham số:

%macro check(dsetname=); 

    %local dsid nlobs rc; 

    %let dsid = %sysfunc(open(&dsetname., IS)); 
    %if &DSID = 0 %then 
    %put %sysfunc(sysmsg()); 

    %let nlobs = %sysfunc(attrn(&dsid, NLOBS)); 

    &nlobs 

    %let rc = %sysfunc(close(&dsid)); 

%mend; 

%let classobs= %check(dsetname=sashelp.class); 

%put &=classobs; 

Ở đó bạn có nó : macro kiểu hàm sử dụng hàm nlobs để tìm hiểu số lượng hàng trong bất kỳ tập dữ liệu cụ thể nào.

+3

Gosh, @Joe, tôi đến để viết một câu trả lời và thấy rằng bạn đã viết nó, vì vậy tôi sẽ phải chờ trong thời gian tới. Điều đó nói rằng, tôi sẽ loại bỏ dấu chấm phẩy mà macro của bạn đang trả về: '&nlobs;'. Thông thường một hàm macro chỉ trả về giá trị, không có dấu chấm phẩy, để nó có thể được gọi ở giữa một câu lệnh. Tôi là một fan hâm mộ lớn của tiện ích macro đơn giản của Peter Crawford% NOW, và giấy thiết kế chức năng vĩ mô của ông mô tả nó: http://www2.sas.com/proceedings/sugi31/038-31.pdf. Việc hiểu các hàm macro đã thay đổi sự hiểu biết của tôi về ngôn ngữ macro. – Quentin

+0

@Quentin Điểm tốt, mà không nên có một ';' trong nó - cố định. – Joe

+0

OK, Joe, nếu bạn thấy nó theo cách đó, TẤT CẢ các macro là các hàm. Đó là tất cả hay không. –

Các vấn đề liên quan