【问题标题】:Minimum obs from dataset to cover all required levels数据集中的最小 obs 以涵盖所有必需的级别
【发布时间】:2013-11-07 04:37:42
【问题描述】:

我被难住了。

我想将一个非常大的数据集减少为更少的观察,但包含原始数据集的所有级别(最多)一次。这样做是出于测试目的,因此提出具有最少 obs 数量的最终数据集符合我们的最大利益。

因此,如果我们以数据集 SASHELP.CLASS 为例。我想找出覆盖原始数据集每个单独级别的第一个 obs(或任何非特定的 obs,但我觉得第一个/最后一个 obs 可能更容易),不是 所需变量的所有可能(现有或理论)组合。

输出如下所示:(SASHELP.CLASS: all levels by AGE, SEX):

  • AGE 有 6 个不同的级别 (11-16)
  • SEX 有 2 个不同的级别(“F”、“M”)
  • 理论上,涵盖这些级别的最少 obs 数量为 6。

所以我们应该得到:

  • OBS #1:Affred,“M”,14
  • OBS #2:爱丽丝,“F”,13

不会输出芭芭拉、卡罗尔和亨利,因为 F-M 和 13-14 都被覆盖了

  • OBS #3:詹姆斯,“M”,12
  • OBS #4:珍妮特,“F”,15
  • OBS #5:乔伊斯,“F”,11
  • OBS #6:菲利普,“M”,16

输出结束

在这种情况下,我们按顺序检查数据,我们发现满足要求的最少 obs (6) 数量,但如果级别变得更长(或相互关联),并且数据分类(假设我们可以-sort),我们最终可能会得到一个接近最小值,但不是所需变量中最长级别的最小值。

我想这需要某种递归算法才能最有效地获得少量 obs 来覆盖这些值,但不知道从哪里开始。任何帮助将不胜感激!

【问题讨论】:

  • 您是否需要行中的类变量(此处为年龄和性别)之外的其他数据?如果是这样,其中有多少 - 另一个或两个或多个变量,有序的或离散的?

标签: algorithm sas minimum


【解决方案1】:

对数据集进行排序,在数据步骤中使用by语句只输出第一组。

请注意,这是从我之前的帖子中更新的。正如其他人指出的那样,这是不正确的。

proc sort data=SASHELP.CLASS out=class_temp;
by AGE descending sex;
run;

data class_temp;
set class_temp;
by AGE;
if first.age then output;
run;

proc sort data=SASHELP.CLASS out=class_temp2;
by sex descending AGE;
run;

data class_temp2;
set class_temp2;
by sex;
if first.sex then output;
run;

proc sort data=class_temp2;
by age;
run;

data combos;
merge class_temp class_temp2;
by age sex;
run;

【讨论】:

  • 如果每个年龄每个性别至少有一个观察结果,这将不会给出预期的结果。
  • 重新阅读第二段,“......涵盖了原始数据集的每个单独级别,并非所有可能的(现有或理论上的)所需变量的组合。”
  • 我同意 Laurent 的观点,我认为这不会给出预期的结果。它会给出 F 13 和 F 14,然后是 M 13 和 M 14,这是不希望的;他想要 F 13 F 14 M 13 而不是那组 4 中的 M 14)。
  • 你是对的。我正在更新帖子以修复。 -- 这基本上是 Laurent 的解决方案。
【解决方案2】:

你可以用一个散列对象写一些东西来大致做到这一点。这样做的缺点是它并不完美 - 我列出的示例提供了超出您想要的额外项目,因为与单元格交互的顺序很重要;并且数据集的初始顺序会影响输出的多样性,因此在下面的示例中,您有很多女性记录,只有两条男性记录,不能反映初始数据集的多样性,仅仅是因为(在我的 sashelp.class 中,至少)在大多数年龄段中,女性在男性之前恰好是女性。

data want;
if _n_ = 1 then do;
  declare hash recs();
  recs.defineKey('keytype');
  recs.defineKey('keylist');
  recs.defineData('keydata');
  recs.defineDone();
  format keylist $10. keytype $8. keydata $8.;
  call missing(of keylist keytype keydata);
end;
set sashelp.class;
  agechar=put(age,3.);
  rc_age = recs.check(key: 'age', key:agechar);  
  rc_sex = recs.check(key:'sex',key:sex);
  rc = rc_age+rc_sex;
  if rc=0 then delete;
  else do;
    if rc_age ne 0 then recs.add(key:'age',key:agechar,data:'sex');
    if rc_sex ne 0 then recs.add(key:'sex',key:sex,data:'sex');
    output;
  end;
run;

它分别检查每个键的散列,如果找到带有尚未找到的键的记录,它会使用这些键填充数据集和散列。它不会返回并稍后检查是否有更好的解决方案,尽管您可以在传入数据集上使用不同(随机)排序顺序运行几次并保留最小的数据集。

【讨论】:

    【解决方案3】:

    这不是一个非常有效的方法,但它给出了预期的结果

    /* sort by first key */
    proc sort data=sashelp.class out=minset;
     by age;
    run;
    
    /* set wantflag to 1 if first.key else wantflag is 0 */
    data minset;
     set minset;
     by age;
     if first.age then wantflag = 1;
     else wantflag = 0;
    run;
    
    /* repeat proc sort and next data step for each consecutive key */
    /* sort by second key and by descending wantflag */
    proc sort data=minset out=minset;
     by sex descending wantflag ;
    run;
    
    /* set wantflag to 1 if first.key, do NOT set to 0 if not key */
    data minset;
     set minset;
     by sex;
     if first.sex then wantflag = 1;
    run;
    
    /* finally keep smallest possible dataset */
    data minset (drop=wantflag );
     set minset;
     if wantflag eq 1 then output;
    run;
    

    【讨论】:

      【解决方案4】:

      我假设您想要一个相当容易实现并产生良好(但不一定是最好)结果的算法。

      我的方法是从最稀有的关卡开始。

      步骤 1. 根据级别频率对所有类别进行排序。

      在您的示例中,我们可能会得到:

      AGE
      14 117
      13 119
      11 140
      12 154
      15 165
      16 170
      
      SEX
      M 503
      F 524
      

      第 2 步。从最稀有的剩余级别开始。在我们的例子中 AGE=14。

      然后在(每个)剩余类别中取最稀有的剩余级别。 如果您得到匹配,则将其用作样本。如果没有,则将搜索增加到更频繁的级别,直到获得成功。勾选你找到的关卡。

      在 SQL 中,您可以使用 ORDER BY

      SELECT <primary key>, 
         case when category2 = <lowest remaining freq level for category2> then 1
              when category2 = <second lowest remaining freq level for category2> then 2
              ...
         end case AS category2,
         case when category3 = <lowest remaining freq level for category3> then 1
              when category3 = <second lowest remaining freq level for category3> then 2
              ...
         end case AS category3
      FROM table1
      WHERE category1 = <lowest remaining freq level for category1>
      ORDER BY category2, category3
      

      重复第 2 步,直到显示所有级别。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-07
        • 1970-01-01
        • 2018-11-23
        • 1970-01-01
        • 1970-01-01
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多