【问题标题】:SAS: Looping Through Folders to Import and Export Multiple FilesSAS:循环文件夹以导入和导出多个文件
【发布时间】:2021-01-22 22:49:13
【问题描述】:

提前感谢您提出的任何建议。

我第一次在 SAS 工作以完成一项(理论上)简单的任务。我在 Windows 目录中有一个父文件夹,其中包含几个子文件夹。子文件夹没有系统地命名。例如,如果父文件夹名为“W:/Documents/ParentFolder/”,则子文件夹可能是“W:/Documents/ParentFolder/ABC1D26/”和“W:/Documents/ParentFolder/HG34A/”。

每个子文件夹包含多个 SAS 数据集。在任何特定的子文件夹中,一些 SAS 数据集具有 .sas7bdat 扩展名,而另一些具有 .sd2 扩展名。此外,没有两个子文件夹必须具有相同数量的数据集,并且数据集也没有系统地命名。

我想在 SAS 中编写一个程序,它查看每个子文件夹,加载它找到的任何 .sas7bdat 或 .sd2 数据集,并将数据集作为 .dta 文件导出到不同的文件夹中。

每个子文件夹中有太多 SAS 数据集,无法为每个数据集手动执行此任务,但没有太多子文件夹,我无法手动将子文件夹名称提供给 SAS。下面是我尝试完成此任务的程序的注释版本。不幸的是,我遇到了很多错误,这无疑是由于我对 SAS 缺乏经验。

例如,SAS 给出以下错误:“错误:无效的逻辑名称;” “错误:文件名语句中的错误;”和“错误:无效的 DO 循环控制信息;”等等。

谁能给点建议?

%macro sas_file_converter();

/* List the sub-folders containing SAS files in the parent folder */
%let folder1 = W:\Documents\ParentFolder\ABC1D26;
%let folder2 = W:\Documents\ParentFolder\HG34A;

/* Start loop over the sub-folders. In each sub-folder, identify all the files, extract the file names, import the files, and export the files. */
%do folder_iter = 1 %to 2;

    /* Define the sub-folder that is the focus of this iteration of the loop */
    filename workingFolder "&&folder&folder_iter..";
    
    /* Extract a list of datasets in this sub-folder */
    data datasetlist;
        length Line 8 dataset_name $300;
        List = dopen('workingFolder');
        do Line = 1 to dnum(List);
            dataset_name = tranwrd(tranwrd(lowcase(trim(dread(List,Line))),".sas7bdat",""),".sd2","");
            output;
        end;
        drop List Line;
    run;

    /* Get number of datasets in this sub-folder */
    proc sql nprint;
        select count(*)
        into :datasetCount
        from WORK.datasetlist;
    quit;

    /* Loop over datasets in the sub-folder. In each iteration of the loop, load the dataset and export the dataset. */
    %do dataset_iter = 1 %to &datasetCount.;

        /* Get the name of the dataset which is the focus of this iteration */
        data _NULL_;
            set WORK.DATASETLIST (firstobs=&dataset_iter. obs=&dataset_iter.);
            call symput("inMember",strip(dataset_name));
        end;

        /* Set the libname */
        LIBNAME library '&folder&folder_iter..';

        /* Load the dataset */
        data new;
            set library.&inMember.;
        run;

        /* Export the dataset */
        proc export data=library.&inMember.
            file = "W:\Documents\OutputFolder\&inMember..dta"
            dbms = stata replace;
        run;
    %end;
%end;
%mend;

【问题讨论】:

  • 您是否将 DTA 文件全部放入同一个输出文件夹中?如果在多个源文件夹中存在同一成员导致名称冲突怎么办?如果没有名称冲突,那么您只需要定义一个指向所有输入文件夹的 libref。
  • 就个人而言,我会以不同的方式分解它。首先,您有一个步骤可以轻松找到所有文件夹/文件。您浏览此文件并确定是否有重复项,查找名称。然后它是一个简单的导出,我可能会通过 CALL EXECUTE 调用,而不是通过循环宏 - 当您单独测试每个组件时,更容易完全调试和测试。
  • 这里的代码说明了这种方法。初始文件列表代码是从 SAS 文档 - 宏附录中窃取的。 github.com/statgeek/SAS-Tutorials/blob/master/…
  • 只是想插话:我真的很喜欢你的代码——非常干净、易于阅读且格式正确。

标签: loops directory sas


【解决方案1】:

非常感谢您的有用建议。我使用以下程序来执行此任务。它主要基于理查德的例子。我把它贴在这里是为了未来的读者; Richard 的示例包含额外的代码,可以帮助您理解该程序的功能。

可以通过将其他文件/文件夹添加到“%let 文件夹”行来容纳它们。 (我在这里写了很多文件/文件夹名称。)

请注意,我用三个破折号(“---”)分隔子文件夹,因为某些文件和子子文件夹的名称中有空格。另请注意,对于 .sd2 文件,我可以简单地将“sas7bdat”的实例替换为“sd2”并且程序运行良好。

再次感谢。

%let inputfolder = W:\Documents\ParentFolder;
%let folders = ABC1D26---HG34A---Sub Folder\ZH323;
%let exportfolder = W:\Documents\ExportFolder;

data _null_;
    do findex = 1 to countw("&folders.","---");
        folder = scan("&folders", findex, "---");

        path = catx("/", "&dataroot.", folder);
        call execute ('libname user ' || quote(trim(path)) || ';');

        length fileref $8;
        call missing(fileref);
        rc = filename(fileref, path);

        did = dopen(fileref);
        do dindex = 1 to dnum(did);
            filename = lowcase(dread(did,dindex));
            if scan(filename,-1) ne 'sas7bdat' then continue;

            xptfilename = tranwrd(filename, '.sas7bdat', '.dta')
            xptfilepath = catx("\", "&exportpath", folder, xptfilename);
            
            datasetname = tranwrd(filename, '.sas7bdat', '');
       
            sascode = 'PROC EXPORT data=' || trim(datasetname) || " replace file=" || quote(trim(xptfilepath)) || " dbms=stata; run;";

            call execute (trim(sascode));
        end;
        did = dclose(did);
      
        call execute ('libname user clear;');

        rc = filename(fileref);
    end;
run;

【讨论】:

    【解决方案2】:

    您可以在 DATA 步中执行所有代码生成并通过CALL EXECUTE 提交。程序中唯一与宏相关的部分是指定 sas 数据根文件夹、要搜索的子文件夹的名称和导出路径。

    该程序可以是非常相似的宏编码,但可能更难调试,并且需要在函数调用周围使用%sysfunc 包装器。

    例子:

    /* Create some sample data in some example folders */
    
    %let workpath = %sysfunc(pathname(WORK));
    
    %let name = %sysfunc(dcreate(ABC, &workpath));
    %let name = %sysfunc(dcreate(DEF, &workpath));
    
    libname user "&workpath./ABC";
    data one two three four five;
      set sashelp.class;
    run;
    
    libname user "&workpath./DEF";
    data six seven eight nine ten;
      set sashelp.class;
    run;
    
    libname user clear;
    
    /* export all data sets in folders to liked named export files */
    
    %let dataroot = &workpath;
    %let folders = ABC DEF;
    %let exportpath = c:\temp;
    
    data _null_;
      do findex = 1 to countw("&folders");
        folder = scan("&folders", findex);
    
        path = catx("/", "&dataroot.", folder);
        call execute ('libname user ' || quote(trim(path)) || ';');
    
        length fileref $8;
        call missing(fileref);
        rc = filename(fileref, path);
    
        did = dopen(fileref);
        do dindex = 1 to dnum(did);
          filename = dread(did,dindex);
          if scan(filename,-1) ne 'sas7bdat' then continue;
    
          xptfilename = tranwrd(filename, '.sas7bdat', '.dta');
          xptfilepath = catx('/', "&exportpath", xptfilename);
    
          datasetname = tranwrd(filename, '.sas7bdat', '');
    
          sascode = 'PROC EXPORT data=' || trim(datasetname)
          || " replace file=" || quote(trim(xptfilepath))
          || " dbms=stata;"
          ;
    
          call execute (trim(sascode));      
        end;
        did = dclose(did);
    
        call execute ('run; libname user clear;');
    
        rc = filename(fileref);
      end;
    run;
    

    【讨论】:

    • 在此处颠倒函数调用的顺序 trim(quote(xptfilepath)) 否则尾随空格在引号内。
    • 很好的捕获 @Tom quote(trim( 使得代码生成更加紧凑
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-09
    • 1970-01-01
    • 2019-08-25
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多