SAS遍历表格

huangapple go评论60阅读模式
英文:

SAS iteration through table

问题

我有两个表格。第一个表格包含学生的列:IDfirst_namelast_namebook。第二个表格包含书籍的列:titleavailability。我需要从第二个表格中随机为第一个表格中的每个学生分配一本书的标题。书籍的数量有限,并在 "availability" 列中指定。

我尝试了一些方法:

%let title;
data student_book;
set first_table;
if missing(book) then do;
call execute(proc sql; select title into :title from second_table where availability > 0 order by rand("uniform"); quit;);
book = "&title";
end;
run;

但它没有工作,可能是因为 CALL EXECUTE 晚评估 :(
英文:

I have two tables. The first table contains columns for student: ID, first_name, last_name, book. The second table contains columns for book: title and availability. I need to randomly assign a book title from the second table to each student in the first table. The number of books is limited and specified in the "availability" column.

I tried something like:

%let title;
data student_book;
set first_table;
if missing(book) then do;
call execute(proc sql; select title into :title from second_table where availability > 0 order by rand("uniform"); quit;');
book = "&title";

end;
run;

but it's not working, probably, CALL EXECUTE is evaluated late :(

答案1

得分: 3

我假设您希望每本可用的书籍复本具有相同的被选中概率。

您有一份记录每本书标题的数据,并且您知道标题的可用复本数量:

data booksummary ;
  input title $1 availability ;
  cards ;
A 10
B 10
C 5
D 0
;

一种处理方法是创建一个新的数据集,其中每个可用的书籍复本都有一条记录。所以,不是上面的四条记录,而是有25条记录。您可以为每个记录创建一个随机数,然后可以使用该随机数对记录进行排序,以获得随机顺序:

data books (keep=title rand);
  set booksummary ;
  call streaminit(123);
  do i=1 to availability ;
    rand=rand("Uniform");
    output ;
  end ;
run ;

然后,当您有了以随机顺序排列的可用书籍时,您可以将书籍标题列与学生姓名列合并在一起。要“合并”数据,您可以使用没有BY子句的MERGE语句。

options mergenoby=nowarn ; *合并,不需要BY子句 ;
data want ;
  merge sashelp.class (keep=name in=a)
        books (keep=title)
  ;
  *没有BY子句,1:1合并 ;
  if a ;
run ;
options mergenoby=error ;
英文:

I'm assuming you want each book copy that is available to have the same probability of being selected.

You have data with one record per book title, and you know the number of copies of the title that are available:

data booksummary ;
  input title $1 availability ;
  cards ;
A 10
B 10
C 5
D 0
;

One way to approach this would be to make a new dataset that has one record per book copy available. So instead of the above four records, you would have 25 records. And you can create a random number for each record, which you can use to sort the records into a random order:

data books (keep=title rand);
  set booksummary ;
  call streaminit(123);
  do i=1 to availability ;
    rand=rand("Uniform");
    output ;
  end ;
run ;

proc sort data=books ;
  by rand ;
run ;

Then when you have the available books in random order, you can just smush the column of book titles onto your column of student names. To 'smush' data you can use the MERGE statement with no BY statement.

options mergenoby=nowarn ; *smush coming ;
data want ;
  merge sashelp.class (keep=name in=a)
        books (keep=title)
  ;
  *no by statement, 1:1 smush ;
  if a ;
run ;
options mergenoby=error ;

答案2

得分: 0

call execute 在数据步骤之后执行。你不能在数据步骤内部使用 call execute 的结果。如果你想要从 second_table 中获取数据、减少其可用性,然后随机从可用性大于 0 的表中选择一个新的标题,一种方法是使用一个宏循环来迭代并更新这两个表。

示例数据:

data student_book;
    set sashelp.class;

    select(_N_);
        when(1) book = 'book1';
        when(2) book = 'book2';
        when(3) book = ' ';
        when(4) book = ' ';
        when(5) book = 'book3';
        when(6) book = 'book4';
        when(7) book = ' ';
        when(8) book = ' ';
        otherwise;
    end;

    if(_N_ LE 8);

    keep name book;
run;

data second_table;
    input title$ availability;
    datalines;
book5 1
book6 2
book7 3
book8 4
book9 5
;
run;

代码:

%macro replace_missing_books;

    /* 计算缺失的书籍数量 */
    %let dsid     = %sysfunc(open(student_book(where=(missing(book))));
    %let nmissing = %sysfunc(attrn(&dsid, nlobsf));
    %let rc       = %sysfunc(close(&dsid));

    %do %until(&nmissing = 0);

        /* 随机选择一本书 */
        proc sql noprint outobs=1;
            select title
            into :title trimmed
            from second_table
            where availability > 0
            order by rand('uniform')
            ;
        quit;

        /* 更新第一个缺失的书籍 */
        data student_book;
            modify student_book;
            where missing(book);

            book = "&title";
            replace;
            stop;
        run;

        /* 更新书籍可用性 */
        data second_table;
            modify second_table;
            where title = "&title";
            availability = availability-1;
        run;

        /* 检查还有多少缺失的书籍 */
        %let dsid     = %sysfunc(open(student_book(where=(missing(book))));
        %let nmissing = %sysfunc(attrn(&dsid, nlobsf));
        %let rc       = %sysfunc(close(&dsid));
    %end;
%mend;
%replace_missing_books;

student_book:

Name    book
Alfred  book1
Alice   book2
Barbara book9
Carol   book9
Henry   book3
James   book4
Jane    book8
Janet   book5

second_table:

title   availability
book5   0
book6   2
book7   3
book8   3
book9   3
英文:

call execute happens after the data step. You cannot use the results of call execute within a data step. If you want to pull from second_table, subtract its availability, then randomly pull a new title from the table where availability > 0, one option is to use a macro loop that iterates and updates both tables.

Sample data:

data student_book;
    set sashelp.class;

    select(_N_);
        when(1) book = 'book1';
        when(2) book = 'book2';
        when(3) book = ' ';
        when(4) book = ' ';
        when(5) book = 'book3';
        when(6) book = 'book4';
        when(7) book = ' ';
        when(8) book = ' ';
        otherwise;
    end;

    if(_N_ LE 8);

    keep name book;
run;

data second_table;
    input title$ availability;
    datalines;
book5 1
book6 2
book7 3
book8 4
book9 5
;
run;

Code:

%macro replace_missing_books;
    
    /* Count missing books */
    %let dsid     = %sysfunc(open(student_book(where=(missing(book)))));
    %let nmissing = %sysfunc(attrn(&dsid, nlobsf));
    %let rc       = %sysfunc(close(&dsid));

    %do %until(&nmissing = 0);
       
        /* Pull a random book */
        proc sql noprint outobs=1;
            select title
            into :title trimmed
            from second_table
            where availability > 0
            order by rand('uniform')
            ;
        quit;

        /* Update the first instance of a missing book */
        data student_book;
            modify student_book;
            where missing(book);

            book = "&title";
            replace;
            stop;
        run;

        /* Update book availability */
        data second_table;
            modify second_table;
            where title = "&title";
            availability = availability-1;
        run;

        /* Check how many missing books are left */
        %let dsid     = %sysfunc(open(student_book(where=(missing(book)))));
        %let nmissing = %sysfunc(attrn(&dsid, nlobsf));
        %let rc       = %sysfunc(close(&dsid));
    %end;
%mend;
%replace_missing_books;

student_book

Name	book
Alfred	book1
Alice	book2
Barbara	book9
Carol	book9
Henry	book3
James	book4
Jane	book8
Janet	book5

second_table

title	availability
book5	0
book6	2
book7	3
book8	3
book9	3

答案3

得分: 0

以下是翻译好的部分:

通常建议使用SET语句与POINT=<var>来选择一个随机标题分配给学生。但由于交易性质,每次分配一本书时可用性应该减少1。

示例:

为每本可用的书分配一个随机数(抽取顺序),然后在1:1合并中将其分配给学生。

data books;
  call streaminit(20230410);

  do bookid = 1 to 30;
    avail = rand('integer',0,10);
    total + avail;
    output;
  end;

  put 'NOTE: total avail: ' total;
  drop total;
run;

data bookrandom;
  set books;
  do _n_ = 1 to avail;
    draw = rand('uniform');
    output;
  end;
run;

proc sort data=bookrandom;
  by draw;
run;

data students;
  do studentid = 1 to 100;
    output;
  end;
run;

data 
  studentbooks(keep=studentid bookid) 
  bookout(keep=bookid where=(not missing(bookid)))
;
  * 1-1 merge to assign an available book as previously random ordered;
  merge students bookrandom(drop=draw);

  if missing(studentid) then stop;
run;

data books;
  modify books bookout; 
  by bookid;
  avail = avail-1;  
run;

%let syslast=books;

注意:由于代码中包含特殊符号和格式,可能需要根据你的需求对代码进行调整。

英文:

Normally a suggestion would be to use a SET statement with the POINT=<var> to select a random title to assign a student. However, due to the transactional nature, the availability should drop by 1 each time a book is assigned.

Example:

Assign each book available a random number (draw order) and use that in a 1:1 merge to assign to a student.

data books;
  call streaminit(20230410);

  do bookid = 1 to 30;
    avail = rand('integer',0,10);
    total + avail;
    output;
  end;

  put 'NOTE: total avail: ' total;
  drop total;
run;

data bookrandom;
  set books;
  do _n_ = 1 to avail;
    draw = rand('uniform');
    output;
  end;
run;

proc sort data=bookrandom;
  by draw;
run;

data students;
  do studentid = 1 to 100;
    output;
  end;
run;

data 
  studentbooks(keep=studentid bookid) 
  bookout(keep=bookid where=(not missing(bookid)))
;
  * 1-1 merge to assign an available book as previously random ordered;
  merge students bookrandom(drop=draw);

  if missing(studentid) then stop;
run;

data books;
  modify books bookout; 
  by bookid;
  avail = avail-1;  
run;

%let syslast=books;

huangapple
  • 本文由 发表于 2023年4月11日 00:46:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75978957.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定