带有两个Set语句的SAS中的嵌入式隐式循环 [英] Embedded Implicit Loops in SAS with two Set Statements

查看:65
本文介绍了带有两个Set语句的SAS中的嵌入式隐式循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码来跟踪特定制造商优惠券的发行.每个问题的兑换,过期和剩余字段中都可以包含美元.我有一个ISSUE表,该表提供了所有发行和金额,而REDEMPTIONS表则是对已兑现或过期的金额的说明.目的是通过在每个类别中放置适当的金额来跟踪每期的所有优惠券美元.

I have the below code to track specific manufacturer coupon issuings. Each issue can have dollars in the redeemed, expired and remaining fields. I have an ISSUE table that gives all of the issuings and amounts and the REDEMPTIONS table that is an accounting of amounts redeemed or expired. The objective is to track all coupon dollars for each issue by putting the appropriate amounts in each category.

该代码应该遍历ISSUE表,并将REDEMPTIONS表中的匹配记录绑定到ISSUE表中的每个记录.

This code is supposed to loop through the ISSUE table and tie matching records from the REDEMPTIONS table to each record in the ISSUE table.

/*create sample tables*/
    data ISSUE; input
    Coupon_NBR $  AMOUNT  REDEEMED    EXPIRED    REMAINING; datalines;
    A           500     0           0           500
    A           500     0           0           500
    B           500     0           0           500
    B           500     0           0           500
    B           500     0           0           500
    B           1250    0           0           1250
    B           750     0           0           750
    C           500     0           0           500
    C           500     0           0           500
    C           500     0           0           500
    C           500     0           0           500
    C           500     0           0           500
    run;

data REDEMPTIONS; input
Redemp_coupon_NBR $ TRANS_AMOUNT     TYPE $16.; datalines;
A                      -150       REDEMPTION
A                      -350       REDEMPTION
A                      -200       EXPIRATION
B                      -300       REDEMPTION
B                      -200       EXPIRATION
B                     -1000       REDEMPTION
C                     -1500       REDEMPTION
C                      -500       EXPIRATION
run;

/*begin looping code*/
data Tracking;

    if _n_ = 1 then Link get_redemptions;
    set issue;
        if (remaining > 0) and (Coupon_NBR = Redemp_coupon_NBR) then do; 
            if trans_amount = 0 then                
               link get_redemptions;


            if trans_amount + remaining >=0 then do; 
               remaining = remaining + trans_amount;
               if type = 'EXPIRATION' then;         
                  expired = expired - trans_amount;

               if type = 'REDEMPTION' then;         
                  redeemed = redeemed - trans_amount;

               link get_redemptions;        
            end;                                    

            else do;                                 
               remaining = 0;
               if type = 'EXPIRATION' then do;         
                  expired = expired - trans_amount;
               end;                   
               else do;                            
                  redeemed = redeemed - trans_amount;      
                  trans_amount = trans_amount + remaining;
                  remaining = 0;
               end;                                 
            end; 
        end; 

        else do;
            link get_redemptions;
        end;                                        

    return;


    get_redemptions:
    set redemptions;
    return;

run;     

这是我得到的输出:

Coupon_NBR  AMOUNT  REDEEMED    EXPIRED REMAINING   redemp_coupon_nbr   trans_amount    type
A              500    150         150     350               A               -350    REDEMPTION
A              500    350         350     150               A               -200    EXPIRATION
B              500      0           0     500               B               -300    REDEMPTION
B              500    300         300     200               B               -200    EXPIRATION
B              500    200         200     300               B              -1000    REDEMPTION
B             1250   1000        1000     250               C              -1500    REDEMPTION
B              750      0           0     750               C               -500    EXPIRATION

在此示例中,正确的输出是:

In this example, the correct output is:

redemp_coupon_nbr   AMOUNT  REDEEMED    EXPIRED REMAINING
A                      500       500          0         0
A                      500         0        200       300
B                      500       300        200         0
B                      500       500          0         0
B                      500       500          0         0
B                     1250         0          0      1250
B                      750         0          0       750
C                      500       500          0         0
C                      500       500          0         0
C                      500       500          0         0
C                      500         0        500         0
C                      500         0          0       500

很明显,我的结果与我想要的结果相去甚远.但是,我主要关心的是,当我希望它跟踪每个优惠券发行时,输出仅七行,这意味着我需要它具有12行(ISSUE表中的每一行一个).我的循环存在某种问题,我认为特别是在 Get Redemptions 定义中.我已经调试了一段时间,没有成功.

Obviously my result is far from where I want it to be. My main concern, however, is that the output only has seven rows, when I want it track every coupon issue, which means I need it to have 12 rows (one for each row in the ISSUE table). There is some kind of a problem with my loop I think specifically in the Get Redemptions definition. I've been debugging for a while without success.

推荐答案

Jarom:

一个强大的解决方案需要一种事务分类帐方法,以便正确处理以下选择:每张优惠券提取多个赎回和跟踪超额,以及在提取额外的赎回之前将超额应用于优惠券.

A robust solution requires a transactional ledgering approach in order to properly deal with the alternatives of fetching multiple redemptions per coupon and tracking overages, and applying overages to a coupon before fetching additional redemptions.

以下示例代码具有大量put语句,因此您可以在日志中观察算法决策点. balance 的目标是在每次交易对帐时(从上或下)接近零,并跟踪超出目标的任何部分.

The following sample code has numerous put statements so you can observe the algorithm decision points in the log. The balance goal is to approach zero (from above or below) at each transaction reconciliation, and track any portions that go beyond the goal.

对于样本,这些变量名的替换是针对您的数据进行的.

For the sample these variable names substitutions were done with respect to your data.

  • coupon_nbr-> G
  • redemp_coupon_nbr-> XG
  • trans_amount-> XAMOUNT

您正确地需要一个 LINK 来提取兑现.
通过添加 BY XG END = ,可以进一步简化按组处理.

You were correct in needing a LINK to fetch the redemptions.
The group wise processing is further facilitated by adding BY XG and END=.

对赎回end =变量进行的测试可防止数据步骤过早停止(如果在读取数据集的最后一条记录后到达随后的无条件集合,则会发生这种情况).

Tests for the redemptions end= variable prevent a premature halting of the data step (which would occur if a subsequent unconditional set is reached after a data sets last record is read).

data reconciliation (keep=G AMOUNT REDEEMED EXPIRED REMAINING EXCESS APP_COUNT _redem_bal _expir_bal fetch_sum)
  ; * / debug;

  set issue;
  by G;

  REDEEMED = 0;
  EXPIRED = 0;
  REMAINING = 0;
  EXCESS = 0;

  if 0 then set redemptions; %* prep pdv;

  retain _balance   0;
  retain _redem_sum 0;
  retain _expir_sum 0;
  retain _redem_bal 0;
  retain _expir_bal 0;

if first.g then put / '----------- ' G= '-------------';

put '@set ' _N_=;
put 'balance: ' _balance _redem_bal= _expir_bal=; 
put 'coupon : ' amount first.g= /;

  if first.G then do;
    put @3 '@first in group';
    _balance = amount;
    _redem_sum = 0;
    _redem_bal = 0;
    _expir_sum = 0;
    _expir_bal = 0;

    put @3 'balance: ' _balance _redem_bal= _expir_bal= G= XG=;

    * spin to first matching redemption or first redemption in a higher by-group;
    if (XG ne G) then 
      do while (not EOT);
        link fetch;
        if XG >= G then leave;
      end;

    if (G = XG) then
      link apply_redemp; 

    put @6 'spin: ' G= XG=;
  end;
  else do; * additional couponage;
    put @3 '@next in group';

    if (G = XG) then
      _balance + amount;
    else
      _balance = amount;

    put @3 'balance: ' _balance _redem_bal= _expir_bal=;

    link apply_excess_to_balance;

    put @3 'balance: ' _balance _redem_bal= _expir_bal= xg= last.xg=;

    if (_balance > 0 and G = XG and not last.XG) then
      link fetch_apply;
  end;

  if (G = XG) then
    do while (not EOT and not last.XG and _balance > 0);
      link fetch_apply;
    end;

  redeemed  = _redem_sum;
  expired   = _expir_sum;
  remaining = min (_balance, amount);
  excess = sum (_redem_bal, _expir_bal, max (0, _balance - amount));

  output;

  put @4 'output: ' amount= redeemed= expired= remaining= excess= /;

  _redem_sum = 0;
  _expir_sum = 0;

  return;

apply_excess_to_balance:
  if (_redem_bal > 0 and _balance > 0) then do;
    apply = min ( _balance, _redem_bal );
    _redem_sum + apply;
    _redem_bal + -apply;
    _balance + -apply;
    app_count = sum(app_count,1);

    put @4 'excess: ' apply= _redem_bal= _redem_sum= _balance= 'reduced amount by excess redemption';
  end;

  if (_expir_bal > 0 and _balance > 0) then do;
    apply = min ( _balance, _expir_bal );
    _expir_sum + apply;
    _expir_bal + -apply;
    _balance + -apply;
    app_count = sum(app_count,1);

    put @4 'excess: ' apply= _expir_bal= _expir_sum= _balance= 'reduced amount by excess expiration';
  end;
return;

fetch:
  set redemptions end=EOT;
  by XG;

  put @5 'fetch: ' xg= xamount= type= first.xg= last.xg= EOT=;
return;

fetch_apply:
  link fetch;

  if (G = XG) then
    link apply_redemp; 

return;

apply_redemp:
  if type in: ('RED' 'EXP') then do;

    apply = min (_balance, -XAMOUNT);
    excess = max (0, -XAMOUNT - _balance);

    if type =: 'RED' then do; 
      _redem_sum + apply;
      _redem_bal + excess;
    end;
    else
    if type =: 'EXP' then do; 
      _expir_sum + apply;
      _expir_bal + excess;
    end;

    _balance + -apply;

    app_count = sum(app_count,1);
    fetch_sum = sum(fetch_sum, -xamount);

    put @5 'apply: ' apply= _balance= _redem_sum= _expir_sum= _redem_bal= _expir_bal=;
  end;
  return;
run;

还有

  • 不匹配的兑换
  • B组的第一次赎回是很大的一笔,跨越了两个优惠券.
  • 没有兑换的优惠券组

棘手的数据

data ISSUE; input
G $  AMOUNT;
A           500   
A           500    
B           500    
B           500    
B           500    
B           1250   
B           750    
B2          100
B2          200
C           500    
C           500    
C           500    
C           500    
C           500    
run;

data REDEMPTIONS; input
XG $ XAMOUNT     TYPE $16.; datalines;
!   -1000       REDEMPTION
A    -150       REDEMPTION
A    -350       REDEMPTION
A    -200       EXPIRATION
B   -1100       REDEMPTION   was -300
B    -200       EXPIRATION
B   -1000       REDEMPTION
C   -1500       REDEMPTION
C    -500       EXPIRATION
run;

这篇关于带有两个Set语句的SAS中的嵌入式隐式循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆