英文:
SAS iteration through table
问题
我有两个表格。第一个表格包含学生的列:ID,first_name,last_name,book。第二个表格包含书籍的列:title 和 availability。我需要从第二个表格中随机为第一个表格中的每个学生分配一本书的标题。书籍的数量有限,并在 "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;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论