Algorithm 在以下条件下消除成对观测,即观测可以有多个可能的伙伴观测

Algorithm 在以下条件下消除成对观测,即观测可以有多个可能的伙伴观测,algorithm,sas,matching,Algorithm,Sas,Matching,在我当前的项目中,我们有几次不得不根据不同的条件实施匹配。首先对问题进行更详细的描述 我们有一个表格测试: 键值 110 1-10 110 120 1-10 110 210 2-10 现在我们想应用一个规则,这样在一个组中(由key的值定义)和为0的对应该被消除 预期结果是: 键值 110 120 排序顺序不相关 下面的代码是我们解决方案的一个示例。 我们希望消除my_id为2和7的观察值,以及数量为10的3个观察值中的另外2个 data test; input my_id alias $ am

在我当前的项目中,我们有几次不得不根据不同的条件实施匹配。首先对问题进行更详细的描述

我们有一个表格测试:
键值
110
1-10
110
120
1-10
110
210
2-10

现在我们想应用一个规则,这样在一个组中(由key的值定义)和为0的对应该被消除

预期结果是:
键值
110
120

排序顺序不相关

下面的代码是我们解决方案的一个示例。 我们希望消除my_id为2和7的观察值,以及数量为10的3个观察值中的另外2个

data test;
input my_id alias $ amount;
datalines4;
1 aaa 10
2 aaa -10
3 aaa 8000
4 aaa -16000
5 aaa 700
6 aaa 10
7 aaa -10
8 aaa 10
;;;;
run;

/* get all possible matches represented by pairs of my_id */
proc sql noprint;
  create table zwischen_erg as
  select a.my_id as a_id,
         b.my_id as b_id
  from test as a inner join
       test as b on (a.alias=b.alias) 
  where a.amount=-b.amount;
quit;

/* select ids of matches to eliminate */
proc sort data=zwischen_erg ;
  by a_id b_id;
run;

data zwischen_erg1;
  set zwischen_erg;
  by a_id;

  if first.a_id then tmp_id1 = 0;
  tmp_id1 +1;
run;


proc sort data=zwischen_erg;
  by b_id a_id;
run;

data zwischen_erg2;
  set zwischen_erg;
  by b_id;

  if first.b_id then tmp_id2 = 0;
  tmp_id2 +1;
run;

proc sql;
  create table delete_ids as 
  select zwischen_erg1.a_id as my_id
  from zwischen_erg1 as erg1 left join 
       zwischen_erg2 as erg2 on 
                   (erg1.a_id = erg2.a_id and 
                    erg1.b_id = erg2.b_id)
  where tmp_id1 = tmp_id2
;
quit;

/* use delete_ids as filter */
proc sql noprint;
  create table erg as
  select a.*
  from test as a left join
       delete_ids as b on (a.my_id = b.my_id) 
  where b.my_id=.;
quit;
该算法似乎有效,至少没有人找到导致错误的输入数据。 但是没有人能向我解释它为什么工作,我也不明白它是如何工作的

所以我有几个问题

  • 对于所有可能的输入数据组合,该算法是否以正确的方式消除对
  • 如果它工作正确,那么算法的详细工作原理是什么?尤其是零件
    其中,tmp_id1=tmp_id2
  • 是否有更好的算法来消除对应的对
  • 提前感谢,编码愉快

    Michael作为对第三个问题的回答。以下方法对我来说似乎更简单。 而且可能表现得更出色。(因为我没有加入)




    如果您只需要纯SAS(因此不需要proc sql),那么可以按如下方式操作。请注意,它背后的想法保持不变

    data V_INTERMEDIATE_VIEW /view=V_INTERMEDIATE_VIEW;
        set INPUT_DATA;
        value_abs = abs(value);
    run;
    proc sort data=V_INTERMEDIATE_VIEW out=INTERMEDIATE_DATA;
        by key value_abs; *we will encounter the negatives of each value and then the positives;
    run;
    
    data OUTPUT_DATA (keep=key value);
        set INTERMEDIATE_DATA;
        by key value_abs;
    
        retain balance 0;
        balance = sum(balance,sign(value));
    
        if last.value_abs then do;
            value = sign(balance)*value_abs; *set sign depending on what we have in excess;            
            do i=1 to abs(balance) by 1;
                output;
            end;
    
            balance=0; *reset balance for next value_abs;
        end;
    run;
    

    注意:感谢Joe提供了一些有用的性能建议。

    作为对第三个问题的回答。以下方法对我来说似乎更简单。 而且可能表现得更出色。(因为我没有加入)




    如果您只需要纯SAS(因此不需要proc sql),那么可以按如下方式操作。请注意,它背后的想法保持不变

    data V_INTERMEDIATE_VIEW /view=V_INTERMEDIATE_VIEW;
        set INPUT_DATA;
        value_abs = abs(value);
    run;
    proc sort data=V_INTERMEDIATE_VIEW out=INTERMEDIATE_DATA;
        by key value_abs; *we will encounter the negatives of each value and then the positives;
    run;
    
    data OUTPUT_DATA (keep=key value);
        set INTERMEDIATE_DATA;
        by key value_abs;
    
        retain balance 0;
        balance = sum(balance,sign(value));
    
        if last.value_abs then do;
            value = sign(balance)*value_abs; *set sign depending on what we have in excess;            
            do i=1 to abs(balance) by 1;
                output;
            end;
    
            balance=0; *reset balance for next value_abs;
        end;
    run;
    

    注意:感谢Joe提供了一些有用的性能建议。

    快速阅读后,我没有看到任何bug。但是“zwischen_erg”可能会有很多不必要的多对多匹配,这将是低效的

    这似乎有效(但不能保证),而且可能更有效。也比较短,所以可能更容易看到发生了什么

    data test;
    input my_id alias $ amount;
    datalines4;
    1 aaa 10
    2 aaa -10
    3 aaa 8000
    4 aaa -16000
    5 aaa 700
    6 aaa 10
    7 aaa -10
    8 aaa 10
    ;;;;
    run;
    
    proc sort data=test;
        by alias amount;
    run;
    
    data zwischen_erg;
        set test;
        by alias amount;
        if first.amount then occurrence = 0;
        occurrence+1;
    run;
    
    proc sql;
        create table zwischen as
        select
            a.my_id,
            a.alias,
            a.amount
        from zwischen_erg as a
        left join zwischen_erg as b
        on a.amount = (-1)*b.amount and a.occurrence = b.occurrence
        where b.my_id is missing;
    quit;
    

    快速阅读后,我没有看到任何错误。但是“zwischen_erg”可能会有很多不必要的多对多匹配,这将是低效的

    这似乎有效(但不能保证),而且可能更有效。也比较短,所以可能更容易看到发生了什么

    data test;
    input my_id alias $ amount;
    datalines4;
    1 aaa 10
    2 aaa -10
    3 aaa 8000
    4 aaa -16000
    5 aaa 700
    6 aaa 10
    7 aaa -10
    8 aaa 10
    ;;;;
    run;
    
    proc sort data=test;
        by alias amount;
    run;
    
    data zwischen_erg;
        set test;
        by alias amount;
        if first.amount then occurrence = 0;
        occurrence+1;
    run;
    
    proc sql;
        create table zwischen as
        select
            a.my_id,
            a.alias,
            a.amount
        from zwischen_erg as a
        left join zwischen_erg as b
        on a.amount = (-1)*b.amount and a.occurrence = b.occurrence
        where b.my_id is missing;
    quit;
    

    哇!美丽的代码。这正是我想要的。它具有更好的性能。你以非常好的方式回答了问题2和3。现在我了解了原始版本的工作原理,并得到了更好的解决方案。非常感谢。哇。。。美丽的代码。这正是我想要的。它具有更好的性能。你以非常好的方式回答了问题2和3。现在我了解了原始版本的工作原理,并得到了更好的解决方案。非常感谢。谢谢你的解决方案,它消除了对。但在productiv环境中,我们得到了更多的列,也需要选择它们。很抱歉没有更详细地定义我的问题。但是你的方法也很好。比你做错了什么更好我建立了一个小的测试集来测试大多数可能的场景,它成功了。然后,我使用您的代码创建了数据集测试(将变量名别名替换为key,将变量名amount替换为value),并对其进行了测试。对于这两种解决方案。而且有效。:)在阅读了你的评论之后,我在你文章的顶部(代码块之外)用你的例子进行了测试,这也行得通。是的,我做了。我使用我的_id作为密钥,而不是别名。为我感到羞耻xD先三思而后行。经验教训:)感谢您的解决方案,它消除了成对。但在productiv环境中,我们得到了更多的列,也需要选择它们。很抱歉没有更详细地定义我的问题。但是你的方法也很好。比你做错了什么更好我建立了一个小的测试集来测试大多数可能的场景,它成功了。然后,我使用您的代码创建了数据集测试(将变量名别名替换为key,将变量名amount替换为value),并对其进行了测试。对于这两种解决方案。而且有效。:)在阅读了你的评论之后,我在你文章的顶部(代码块之外)用你的例子进行了测试,这也行得通。是的,我做了。我使用我的_id作为密钥,而不是别名。为我感到羞耻xD先三思而后行。经验教训:)