跨数据集在 SAS 中为 id 分配一个一致的随机数

Assign a consistent random number to id in SAS across datasets

我有两个数据集 data1data2,其中有一个 id 列。我想为每个 id 分配一个随机 id,但是这个随机数需要在数据集中保持一致。 (rand_id for id=1 在两个数据集中必须相同)。 objective 是得到:

id rand_id
1 0.4212
2 0.5124
3 0.1231
id rand_id
1 0.4212
3 0.1231
2 0.5124
4 0.9102

请注意,Id 不需要排序,一些 Id 可能会出现在一个数据集中,但不会出现在另一个数据集中。我以为

DATA data1;
SET data1;
CALL STREAMINIT(id);
rand_id=RAND('uniform');
RUN;

data2 一样可以完成这项工作,但事实并非如此。它只是将第一个 id 作为种子并生成一系列随机数。 从 STREAMINIT 文档来看,似乎每个数据集只调用一次。我想在每一行都被称为它。这可能吗?

我们的想法是创建一个 table random_values,并为我们稍后加入两个 table 的每个 ID 关联一个随机 ID。

*assign random seed;
%let random_seed = 71514218;

*list of unique id;
proc sql;
create table unique_id as
select distinct id
from (
select id from have1
union all 
select id from have2
)
;
quit;

*add random values;
data random_values;
set unique_id;
call streaminit(&random_seed.);
rand = rand('uniform', 0, 1);
run;

*join back on have1;
proc sql;
create table have1 as
select t1.id, t2.rand as rand_id
from have1 t1 left join random_values t2
on t1.id = t2.id
;
quit;

*join back on have2;
proc sql;
create table have2 as
select t1.id, t2.rand as rand_id
from have2 t1 left join random_values t2
on t1.id = t2.id
;
quit;

已解决:

DATA data1;
SET data1;
seed = id;
CALL RANUNI(seed,rand_id);
DROP seed;
RUN;

生成所需的结果。

为什么不使用查找数据集。您可以 create/update 使用 HASH 对象。

首先制作一个空数据集:

data rand_id;
  set one(keep=id);
  rand_id=.;
  stop;
run;

然后处理第一个数据集。将新的 RAND_ID 变量添加到该数据集,并使用所有唯一 ID 值填充 RAND_ID 数据集。

data one_random;
  if _n_=1 then do;
    declare hash h(dataset:'rand_id');
    rc=h.definekey('id');
    rc=h.definedata('id','rand_id');
    rc=h.definedone();
  end;
  if eof then rc=h.output(dataset:'rand_id');
  set one end=eof;
  if h.find() then do;
    rand_id=rand('uniform');
    rc=h.add();
  end;
  drop rc;
run;

对共享相同 ID 变量的任何其他数据集重复上述步骤。

data two_random;
  if _n_=1 then do;
    declare hash h(dataset:'rand_id');
    rc=h.definekey('id');
    rc=h.definedata('id','rand_id');
    rc=h.definedone();
  end;
  if eof then rc=h.output(dataset:'rand_id');
  set two end=eof;
  if h.find() then do;
    rand_id=rand('uniform');
    rc=h.add();
  end;
  drop rc;
run;

在我看来,最简单的方法是创建格式数据集。 Tom 的哈希示例也很好,但如果您不了解哈希表,这可能更容易。

不要从 ID 本身播种随机数 - 这不再是随机的。

data forfmt;
  set data1;
  call streaminit(7);
  label = put(rand('Uniform'),12.9);
  start = id;
  fmtname = 'RANDIDF';
  output;
  if _n_ eq 1 then do;
    hlo='o';
    label='.';
    output;
  end;
run;

proc format cntlin=forfmt;
quit;

然后你可以使用put(id,randidf.)分配随机ID(并使用input而不是put并使其成为一个信息,如果你希望它是数字,那就处理了通过 type='i'; 并需要输入为字符或通过 put 转换为字符)。无需排序,大部分时间查找速度非常快。