【问题标题】:How to make the equivalent of a SAS macro with do loop in R studio?如何在 R studio 中使用 do loop 制作相当于 SAS 宏的内容?
【发布时间】:2018-12-25 21:09:42
【问题描述】:

我有一堆名为“haveyear”的 SAS 数据集,范围从 2000 年到 2018 年,即"have2000"-"have2018"。这些存储在'path_to_have_data' 的本地目录中。每个数据集包含几个变量,即var1var2 等。我想加载这些数据集,然后根据var1 ne '0' 对它们进行子集化,同时保留原始数据集中的 var1 和 var2。此外,我想为每个子集添加一个新变量year,这样我就可以知道数据来自哪一年。最后,我想将所有新子集附加(堆叠)到一个名为appended 的数据集中。例如:

数据集Have2017 如下所示:

var1 var2 var3
 0    2    5
 3    7    9

数据集Have2018 如下所示:

var1 var2 var3
 0    2    5
 3    7    9

子集Want2017 如下所示:

var1 var2 year
 3    7   2017

子集Want2018 看起来像这样:

var1 var2 year
 3    7   2018

最终的数据集appended 如下所示:

var1 var2 year
 3    7   2017
 3    7   2018

以下 SAS 脚本可以解决问题:

libname raw 'path_to_have_data';

%macro a;

%do &year.=2000 %to 2018;

data want&year. (keep= var1 var2);
set raw.have&year.;
where var1 ne '0';
year=&year.;
run;

%end;
%mend;
%a;

data appended;
set want:;
run;

有人知道如何使用 R Studio 实现相同的结果吗?

编辑:问题的MCVE 版本

这是从原始帖子生成所需结果所需的 SAS 代码的工作版本。

首先,需要一个 DATA 步来创建一些 SAS 数据集。我们会将它们存储在默认的WORK 库中,而不是引用磁盘上的另一个库。

/* generate sample data */
 data have2000 have2001 have2002;
    input var1 var2 var3;
    cards;
    0 1 2
    1 3 5
    2 7 4
    0 9 9
    8 7 3
    ;
    run;

接下来,我们需要对 SAS 宏进行一些编辑以使其运行。

/* run macro from OP */
options mprint; /* shows SAS code generated by macro processor */
/* 
 * corrections / adjustments made to macro
 * 1. remove & in %do loop
 * 2. add year to keep list
 * 3. fix syntax error in where statement because var1 is numeric 
 * 4. use work library, and only process 3 years of data 
 */
%macro a;
   %do year = 2000 %to 2002;
      data want&year. (keep= var1 var2 year);
         set have&year.;
         where var1 ne 0;
         year=&year.;
      run;
   %end;
%mend;
/* run the macro */
%a;

SAS 选项mprint 导致 SAS 将宏生成的代码写入日志。当我们运行宏时,为单个数据集生成的代码子集如下所示。

 MPRINT(A):   data want2000 (keep= var1 var2 year);
 MPRINT(A):   set have2000;
 MPRINT(A):   where var1 ne 0;
 MPRINT(A):   year=2000;
 MPRINT(A):   run;
 MPRINT(A):   data want2001 (keep= var1 var2 year);
 MPRINT(A):   set have2001;
 MPRINT(A):   where var1 ne 0;
 MPRINT(A):   year=2001;
 MPRINT(A):   run;
 MPRINT(A):   data want2002 (keep= var1 var2 year);
 MPRINT(A):   set have2002;
 MPRINT(A):   where var1 ne 0;
 MPRINT(A):   year=2002;
 MPRINT(A):   run;

该宏生成三个 SAS 数据步骤,每年一个,包括以下更改。

  • 删除var3
  • 删除var1 = 0 所在的行
  • 将输出写入名为 want<year> 的 SAS 数据集

最后,代码将刚刚创建的数据集组合成一个名为 appended 的单个 SAS 数据集。我们还将打印结果数据集。

data appended;
set want:; /* references all SAS datasets that start with "want" */
run;

proc print data = appended;
run;

...和输出:

【问题讨论】:

  • 可以用文字描述您的脚本的作用 - 显然适用于非 SAS 用户。也很高兴看到示例输入和预期输出。
  • @markus - OP 有许多 SAS 数据集存储在一个目录中,have2000have2018。 SAS 宏读取每个数据集,对其进行子集化,添加一个year 变量,然后将它们组合成一个 SAS 数据集。例如输入和输出,请看下面我的回答。

标签: r loops macros sas


【解决方案1】:

这是该问题的 Base R 解决方案。 OP 希望复制 SAS 宏的过程,该宏将 SAS 数据集列表 raw.have2000 - raw.have2018 子集,保留两列,设置变量year 等于数据集名称中列出的年份,以及将它们连接成一个数据集。

# create some data

var1 <- 0:5
var2 <- 6:11
var3 <- 12:17 

raw.have2000 <- data.frame(var1,var2,var3)
raw.have2001 <- data.frame(var1,var2,var3)
raw.have2002 <- data.frame(var1,var2,var3)

years <- 2000:2002
dataList <- lapply(years,function(x){
     # create name of data set as a character object
     dsname <- paste0("raw.have",x)
     # use dsname with get() to extract data and subset first 2 variables
     ds <- subset(get(dsname),var1 !=0,select=c(var1,var2))
     ds$year <- x
     # print to have data frame returned in
     # output list 
     ds 
})
# combine data frames 
appended <- do.call(rbind,dataList)

...和输出,注意到 var1 = 0 的行已被删除,var3 已被删除,year 变量已被添加:

> appended
   var1 var2 year
2     1    7 2000
3     2    8 2000
4     3    9 2000
5     4   10 2000
6     5   11 2000
21    1    7 2001
31    2    8 2001
41    3    9 2001
51    4   10 2001
61    5   11 2001
22    1    7 2002
32    2    8 2002
42    3    9 2002
52    4   10 2002
62    5   11 2002
> 

说明

SAS 和 R 之间的主要区别之一是经验丰富的 SAS 程序员使用 SAS 宏语言来自动执行重复性任务。宏语言生成由 SAS 系统处理的 SAS 代码。

R 没有宏语言/代码生成器。但是,可以使用get() 函数访问R 对象,这些对象的名称可以通过将各种信息组合成字符对象来生成。

【讨论】:

  • @ThomasChristensen - 如果您是一位经验丰富的 SAS 程序员,并且是 R 新手,那么您需要付出一些努力才能忘记 SAS 的做事方式。然而,努力是值得的。正如我在A SAS Version of Pollutantmean 中解释的那样,可以用很少的代码在 R 中做很多事情。
  • 我在 SAS 数据科学方面比较有经验,但我几天前才开始用 R 编程。正如您在链接中指出的那样,SAS 中一些简单的事情在 R 中非常复杂,反之亦然。但是,我已经确信 R 比 SAS 有很多优势,所以我很想学习它,尽管启动困难:)
【解决方案2】:

考虑使用mget 将全局环境中所有需要的拥有 数据帧检索到数据帧列表中。然后,在每个项目上迭代运行数据框操作,然后在最后对所有项目进行行绑定。

下面使用 mapplyhave 数据框和 2000-2018 年之间逐元素迭代:

haves_dfs <- mget(ls(pattern="have"))

# SUBSET ROWS AND COLUMNS
want_dfs <- mapply(function(df, yr) transform(subset(df, var1 != '0')[c("var1", "var2")],
                                              year = yr), 
                   have_dfs, c(2000:2018), SIMPLIFY = FALSE)

final_df <- do.call(rbind, want_dfs)

或者使用lapply 迭代使用get

want_dfs <- lapply(c(2000:2018), function(yr) 
               # SUBSET ROWS AND COLUMNS
               transform(subset(get(paste0("have", yr)), var1 != '0')[c("var1", "var2")],
                         year = yr)
            )

final_df <- do.call(rbind, want_dfs)

上面,可能看起来很密集,但这嵌套了一行

transform(subset(df, var1 != '0')[c("var1", "var2")], year = yr)

等于多步:

df_process <- function(df, yr) {
    # SUBSET ROWS
    df <- df[df$var1 != '0',]
    # SUBSET COLUMNS
    df <- df[c("var1", "var2")]
    # ADD NEW COLUMN
    df$year <- yr

    # RETURN FINAL
    return(df)
}

【讨论】:

    【解决方案3】:

    感谢@Parfait 还写了一个很好的答案!但是,在您编写的第一行代码中:

    haves_dfs <- mget(ls(pattern="have"))
    

    后来你提到了:

    have_dfs
    

    因此,您的第一行代码应该是:

    have_dfs <- mget(ls(pattern="have"))
    

    我已经调整了您的答案,并将其与@Len 给出的答案的数据集生成部分相结合。这是该解决方案的完整工作示例:

    var1 <- 0:5
    var2 <- 6:11
    var3 <- 12:17
    
    have2000 <- data.frame(var1,var2,var3)
    have2001 <- data.frame(var1,var2,var3)
    have2002 <- data.frame(var1,var2,var3)
    
    have_dfs <- mget(ls(pattern="have"))
    
    want_dfs <- mapply(function(df, yr) transform(subset(df, var1 != '0')[c("var1", "var2")],
                                                  year = yr), 
                       have_dfs, c(2000:2002), SIMPLIFY = FALSE)
    
    final_df <- do.call(rbind, want_dfs)
    

    或者与lapply一起使用get()

    want_dfs <- lapply(c(2000:2002), function(yr) 
    transform(subset(get(paste0("have", yr)), var1 != '0')[c("var1", "var2")],
                year = yr) )
    
    final_df <- do.call(rbind, want_dfs)
    

    【讨论】:

      猜你喜欢
      • 2018-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多