Как сделать эквивалент макроса SAS с циклом do в R studio? - PullRequest
0 голосов
/ 26 декабря 2018

У меня есть набор наборов данных SAS с именем "haveyear", начиная с 2000-2018, то есть "have2000"-"have2018".Они хранятся в локальном каталоге на 'path_to_have_data'.Каждый набор данных содержит несколько переменных, например var1, var2 и т. Д.Я хочу загрузить эти наборы данных, а затем установить их в подмножестве 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, необходимая для получения требуемого результата из исходного сообщения.

Во-первых, для создания нескольких наборов данных SAS требуется шаг DATA.Мы будем хранить их в стандартной библиотеке 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
  • Запись выходных данных в набор данных SAS с именем want<year>

Наконец, код объединяет только что созданные наборы данных в один набор данных SAS, называемый appended.Мы также распечатаем полученный набор данных.

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

proc print data = appended;
run;

... и вывод:

enter image description here

Ответы [ 3 ]

0 голосов
/ 26 декабря 2018

Рассмотрите возможность извлечения всех необходимых , имеющих фреймов данных в глобальной среде, используя mget в список фреймов данных.Затем итеративно выполняйте операции с фреймами данных для каждого элемента, а затем связывайте строки со всеми элементами в конце.

Ниже используется mapply для поэлементного итерации между иметь фреймами данных и годами 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)
}
0 голосов
/ 26 декабря 2018

Спасибо @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)
0 голосов
/ 26 декабря 2018

Вот решение Base R для этой проблемы.ОП хочет повторить процесс макроса 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-объектам, имена которых могут быть сгенерированы путем объединения различных фрагментов информации в символьные объекты.

...