在 sas 中编写 vba 代码
Writing vba code in sas
我想将我的 VBA 代码实现到我的 SAS 代码中,这样我就可以用一个 运行. 我的 SAS 代码完成整个过程读取一个大的 SAS table,进行一些转换,最后导出到一个 Excel 文件(代码如下)。我还在 Excel 文件中写了一些 VBA 代码(例如一些变量的自动过滤,你可以看到下面的代码)。
table 看起来像这样:
A B C Var1 Var2 Var3
--------------------
1 1 1 10 15 20
1 1 2 15 20 30
1 2 1 20 30 40
1 2 2 30 40 50
2 1 1 40 50 60
2 1 2 50 60 70
2 2 1 60 70 80
..............
..............
但是,我想将我的 VBA 代码实现到我的 SAS 代码中,这样我就可以用一个 运行. 我完成整个过程知道如何在 SAS 中打开和 运行 一个 Excel 文件(代码在下面),但我不知道如何在我的 SAS 中实现 VBA 代码。
如果你想知道为什么我要在我的 SAS 中实现我的宏代码,我以后会多次使用类似的 SAS-tables,所以保留整个代码会更实用-代码在一处。
我刚刚意识到我无法在 SAS 中以启用宏的 Excel 格式 XLSM 导出 table。我想这也是一个挑战。此外,从 Excel 文件中保存宏代码也不太实用,因为它必须保存在加载项菜单中。所以最好在一个地方处理整个过程,比如在 SAS 编辑器中。
SAS 中将最终 table 导出到 Excel 文件的代码:
PROC EXPORT DATA=File1
OUTFILE= "&server\&env\test1.xlsx"
DBMS=EXCEL REPLACE;
SHEET="sheet1";
RUN;
Excel 文件中的 VBA 代码示例,用于为 Excel 文件中的变量创建自动筛选:
Sub Macro1()
Dim N As Long, r As Range
With Sheets("sheet1")
N = .Cells(Rows.Count, "B").End(xlUp).Row
ReDim ary(1 To N)
For i = 1 To N
ary(i) = .Cells(i, 1)
Next i
End With
Range("A1:F20").AutoFilter
ActiveSheet.Range("$A:$F").AutoFilter Field:=1, Criteria1:=ary, Operator:=xlFilterValues
End Sub
要启动的 SAS 代码和 运行 SAS 中的 Excel 文件:
OPTIONS NOXWAIT NOXSYNC;
DATA _NULL_;
RC=SYSTEM('START EXCEL');
RC=SLEEP(0.5);
RUN;
FILENAME CMDS DDE 'EXCEL|SYSTEM';
DATA _NULL_;
FILE CMDS;
PUT "[OPEN(""&server\&env\test1.XLS"")]";
PUT '[RUN("Macro1")]';
PUT '[SAVE.AS("&server\&env\FORMATTED_FILE.XLSM")';
PUT "[QUIT()]";
RUN;
QUIT;
执行此操作的常用方法是使用模板文件。您已保存模板,其中保存了 excel 宏(也许还对其进行了一些格式化;毕竟使用 DDE,您不必从空白工作表开始)。
您可以使用 DDE 填充模板 workbook/worksheet,然后 "Save As" 另一个文件,或者您可以使用 DDE 创建新的工作簿和工作表,打开模板工作簿,运行宏,关闭模板。您执行哪种操作可能取决于您是否要将宏与结果一起分发。
这允许您 运行 一切都无需以任何方式与之交互 - 您不必向它添加新的宏或任何东西,因为模板宏已经存在。一切都可以这样 运行 一次完成。
这在论文 Step-by-Step in Using SAS® DDE to Create an Excel Graph Based on N 中有展示
来自 SAS 数据集的观察,以及关于该主题的其他几篇论文。
使用 DDE,您可以在 SAS 中设置过滤器:
data _null_;
FILE CMDS;
/* select your worksheet */
put '[workbook.select("your_sheet")]';
/* select the column range you want to set the filter */
put '[select("r1c2:r1c5")]';
/* set filter */
put '[filter]';
run;
我一直觉得 DDE 有点笨拙,如果你在 运行 的时候触摸 excel 工作簿,它会感到不安。此示例将 VBscript 写入 sas 工作区,然后执行 iut。基本上,您可以获得 put 语句来为您希望程序执行的任何操作编写代码,并且它可以由 SAS 数据集中的内容驱动。此示例将页眉和页脚添加到现有 excel 电子表格...
%macro xlHeadFoot(WorkBookPath=,Header=FileName,Footer=SheetName,onlySheet=);
%local _shortpath WorkBook;
data _null_;
length header footer 0;
header=ifc(lowcase("&header.")='filename' or lowcase("&header.")='sheetname',tranwrd(tranwrd(lowcase("&header."),'filename','&F'),'sheetname','&A'),"&header.");
footer=ifc(lowcase("&footer.")='filename' or lowcase("&footer.")='sheetname',tranwrd(tranwrd(lowcase("&footer."),'filename','&F'),'sheetname','&A'),"&footer.");
call symput("header",trim(header));
call symput("footer",trim(footer));
run;
%let WorkBook=%scan(&WorkBookPath.,%sysfunc(countw(&WorkBookPath.,\)),\);
%* ***********************************************;
%* get the short DOS name for the workspace folder;
data _null_;
rc=filename("inpipe",catx('"','for %I in (',"%sysfunc(pathname(work))",') do echo %~sI'),"pipe");
run;
data _null_;
infile inpipe truncover end=last;
input @1 data 6. ;
rc=filename("inpipe","");
if last then call symput('_shortpath',trim(data));
run;
%* *******************************;
%* tidy up any previous executions;
data _null_;
if fileexist("&_shortpath\testx.vbs") then do;
rc=filename("dump","&_shortpath\testx.vbs");
rc=fdelete('dump');
msg=sysmsg();
if msg ne '' then put msg=;
end;
if fileexist("&_shortpath\xmlFile.xml") then do;
rc=filename("dump","");
rc=filename("dump","&_shortpath\xmlFile.xml");
rc=fdelete('dump');
msg=sysmsg();
if msg ne '' then put msg=;
rc=filename("dump","");
end;
run;
%if %sysfunc(fileexist("&WorkBookPath."))=1 %then %do;
data null;
file "%sysfunc(pathname(work))\testx.vbs";
put @1 'Set objExcel = CreateObject("Excel.Application")';
put @1 'objExcel.Application.Visible = True';
put @1 'objExcel.Workbooks.open "' "&WorkBookPath." '"' ;
%if %str(&onlySheet.) ne %str() %then %do;
put @1 'onlySheetExisits=False';
put @1 'for each sheet in objExcel.Workbooks("' "&WorkBook." '").sheets';
put @1 'if strcomp(sheet.name,"' "&onlySheet." '",1)=0 then onlySheetExisits=True';
put @1 'if strcomp(sheet.name,"' "&onlySheet." '",1)=0 then sheet.PageSetup.CenterHeader = "' "%str(&Header.)" '"';
put @1 'if strcomp(sheet.name,"' "&onlySheet." '",1)=0 then sheet.PageSetup.CenterFooter = "' "%str(&Footer.)" '"';
put @1 'Next';
%end;
%else %do;
put @1 'for each sheet in objExcel.Workbooks("' "&WorkBook." '").sheets';
put @1 'sheet.PageSetup.CenterHeader = "' "%str(&Header.)" '"';
put @1 'sheet.PageSetup.CenterFooter = "' "%str(&Footer.)" '"';
put @1 'Next';
%end;
put @1 'objExcel.Workbooks("' "&WorkBook." '").save';
put @1 'objExcel.Workbooks("' "&WorkBook." '").Close';
put @1 'objExcel.Application.Quit';
%if %str(&onlySheet.) ne %str() %then %do;
put @1 'if onlySheetExisits=False then msgbox "Error! Could not find Worksheet: '"&onlySheet."' in Workbook: "' ' & vbcr & ' '" '"&WorkBookPath." '",16,"SAS: '"&SYSMACRONAME"'"';
%end;
run;
x %sysfunc(quote("&_shortpath.\testx.vbs"));
%end;
%else %put %str(ERR)OR: [&SYSMACRONAME.] Unable to open: &WorkBookPath. - check it exists!;
%mend;
我想将我的 VBA 代码实现到我的 SAS 代码中,这样我就可以用一个 运行. 我的 SAS 代码完成整个过程读取一个大的 SAS table,进行一些转换,最后导出到一个 Excel 文件(代码如下)。我还在 Excel 文件中写了一些 VBA 代码(例如一些变量的自动过滤,你可以看到下面的代码)。
table 看起来像这样:
A B C Var1 Var2 Var3
--------------------
1 1 1 10 15 20
1 1 2 15 20 30
1 2 1 20 30 40
1 2 2 30 40 50
2 1 1 40 50 60
2 1 2 50 60 70
2 2 1 60 70 80
..............
..............
但是,我想将我的 VBA 代码实现到我的 SAS 代码中,这样我就可以用一个 运行. 我完成整个过程知道如何在 SAS 中打开和 运行 一个 Excel 文件(代码在下面),但我不知道如何在我的 SAS 中实现 VBA 代码。
如果你想知道为什么我要在我的 SAS 中实现我的宏代码,我以后会多次使用类似的 SAS-tables,所以保留整个代码会更实用-代码在一处。
我刚刚意识到我无法在 SAS 中以启用宏的 Excel 格式 XLSM 导出 table。我想这也是一个挑战。此外,从 Excel 文件中保存宏代码也不太实用,因为它必须保存在加载项菜单中。所以最好在一个地方处理整个过程,比如在 SAS 编辑器中。
SAS 中将最终 table 导出到 Excel 文件的代码:
PROC EXPORT DATA=File1
OUTFILE= "&server\&env\test1.xlsx"
DBMS=EXCEL REPLACE;
SHEET="sheet1";
RUN;
Excel 文件中的 VBA 代码示例,用于为 Excel 文件中的变量创建自动筛选:
Sub Macro1()
Dim N As Long, r As Range
With Sheets("sheet1")
N = .Cells(Rows.Count, "B").End(xlUp).Row
ReDim ary(1 To N)
For i = 1 To N
ary(i) = .Cells(i, 1)
Next i
End With
Range("A1:F20").AutoFilter
ActiveSheet.Range("$A:$F").AutoFilter Field:=1, Criteria1:=ary, Operator:=xlFilterValues
End Sub
要启动的 SAS 代码和 运行 SAS 中的 Excel 文件:
OPTIONS NOXWAIT NOXSYNC;
DATA _NULL_;
RC=SYSTEM('START EXCEL');
RC=SLEEP(0.5);
RUN;
FILENAME CMDS DDE 'EXCEL|SYSTEM';
DATA _NULL_;
FILE CMDS;
PUT "[OPEN(""&server\&env\test1.XLS"")]";
PUT '[RUN("Macro1")]';
PUT '[SAVE.AS("&server\&env\FORMATTED_FILE.XLSM")';
PUT "[QUIT()]";
RUN;
QUIT;
执行此操作的常用方法是使用模板文件。您已保存模板,其中保存了 excel 宏(也许还对其进行了一些格式化;毕竟使用 DDE,您不必从空白工作表开始)。
您可以使用 DDE 填充模板 workbook/worksheet,然后 "Save As" 另一个文件,或者您可以使用 DDE 创建新的工作簿和工作表,打开模板工作簿,运行宏,关闭模板。您执行哪种操作可能取决于您是否要将宏与结果一起分发。
这允许您 运行 一切都无需以任何方式与之交互 - 您不必向它添加新的宏或任何东西,因为模板宏已经存在。一切都可以这样 运行 一次完成。
这在论文 Step-by-Step in Using SAS® DDE to Create an Excel Graph Based on N 中有展示 来自 SAS 数据集的观察,以及关于该主题的其他几篇论文。
使用 DDE,您可以在 SAS 中设置过滤器:
data _null_;
FILE CMDS;
/* select your worksheet */
put '[workbook.select("your_sheet")]';
/* select the column range you want to set the filter */
put '[select("r1c2:r1c5")]';
/* set filter */
put '[filter]';
run;
我一直觉得 DDE 有点笨拙,如果你在 运行 的时候触摸 excel 工作簿,它会感到不安。此示例将 VBscript 写入 sas 工作区,然后执行 iut。基本上,您可以获得 put 语句来为您希望程序执行的任何操作编写代码,并且它可以由 SAS 数据集中的内容驱动。此示例将页眉和页脚添加到现有 excel 电子表格...
%macro xlHeadFoot(WorkBookPath=,Header=FileName,Footer=SheetName,onlySheet=);
%local _shortpath WorkBook;
data _null_;
length header footer 0;
header=ifc(lowcase("&header.")='filename' or lowcase("&header.")='sheetname',tranwrd(tranwrd(lowcase("&header."),'filename','&F'),'sheetname','&A'),"&header.");
footer=ifc(lowcase("&footer.")='filename' or lowcase("&footer.")='sheetname',tranwrd(tranwrd(lowcase("&footer."),'filename','&F'),'sheetname','&A'),"&footer.");
call symput("header",trim(header));
call symput("footer",trim(footer));
run;
%let WorkBook=%scan(&WorkBookPath.,%sysfunc(countw(&WorkBookPath.,\)),\);
%* ***********************************************;
%* get the short DOS name for the workspace folder;
data _null_;
rc=filename("inpipe",catx('"','for %I in (',"%sysfunc(pathname(work))",') do echo %~sI'),"pipe");
run;
data _null_;
infile inpipe truncover end=last;
input @1 data 6. ;
rc=filename("inpipe","");
if last then call symput('_shortpath',trim(data));
run;
%* *******************************;
%* tidy up any previous executions;
data _null_;
if fileexist("&_shortpath\testx.vbs") then do;
rc=filename("dump","&_shortpath\testx.vbs");
rc=fdelete('dump');
msg=sysmsg();
if msg ne '' then put msg=;
end;
if fileexist("&_shortpath\xmlFile.xml") then do;
rc=filename("dump","");
rc=filename("dump","&_shortpath\xmlFile.xml");
rc=fdelete('dump');
msg=sysmsg();
if msg ne '' then put msg=;
rc=filename("dump","");
end;
run;
%if %sysfunc(fileexist("&WorkBookPath."))=1 %then %do;
data null;
file "%sysfunc(pathname(work))\testx.vbs";
put @1 'Set objExcel = CreateObject("Excel.Application")';
put @1 'objExcel.Application.Visible = True';
put @1 'objExcel.Workbooks.open "' "&WorkBookPath." '"' ;
%if %str(&onlySheet.) ne %str() %then %do;
put @1 'onlySheetExisits=False';
put @1 'for each sheet in objExcel.Workbooks("' "&WorkBook." '").sheets';
put @1 'if strcomp(sheet.name,"' "&onlySheet." '",1)=0 then onlySheetExisits=True';
put @1 'if strcomp(sheet.name,"' "&onlySheet." '",1)=0 then sheet.PageSetup.CenterHeader = "' "%str(&Header.)" '"';
put @1 'if strcomp(sheet.name,"' "&onlySheet." '",1)=0 then sheet.PageSetup.CenterFooter = "' "%str(&Footer.)" '"';
put @1 'Next';
%end;
%else %do;
put @1 'for each sheet in objExcel.Workbooks("' "&WorkBook." '").sheets';
put @1 'sheet.PageSetup.CenterHeader = "' "%str(&Header.)" '"';
put @1 'sheet.PageSetup.CenterFooter = "' "%str(&Footer.)" '"';
put @1 'Next';
%end;
put @1 'objExcel.Workbooks("' "&WorkBook." '").save';
put @1 'objExcel.Workbooks("' "&WorkBook." '").Close';
put @1 'objExcel.Application.Quit';
%if %str(&onlySheet.) ne %str() %then %do;
put @1 'if onlySheetExisits=False then msgbox "Error! Could not find Worksheet: '"&onlySheet."' in Workbook: "' ' & vbcr & ' '" '"&WorkBookPath." '",16,"SAS: '"&SYSMACRONAME"'"';
%end;
run;
x %sysfunc(quote("&_shortpath.\testx.vbs"));
%end;
%else %put %str(ERR)OR: [&SYSMACRONAME.] Unable to open: &WorkBookPath. - check it exists!;
%mend;