繁体   English   中英

在SAS中汇总实际年份

[英]Aggregating Over Actual Year in SAS

假设我们有下表(“购买”):

Date                 Units_Sold             Brand       Year
18/03/2010                5                   A         2010
12/04/2010                2                   A         2010
22/05/2010                1                   A         2010
25/05/2010                7                   A         2010
11/08/2011                5                   A         2011
12/07/2010                2                   B         2010
22/10/2010                1                   B         2010
05/05/2011                7                   B         2011

对于不同品牌,同样的逻辑一直持续到2014年底。

我想做的是计算每年每个品牌的Units_Sold数量。 但是,我不想在日历年中执行此操作,而是在实际年份中执行此操作。

所以我想要的一个例子:

proc sql;
create table Dont_Want as
select Year, Brand, sum(Units_Sold) as Unit_per_Year
from Purchases
group by Year, Brand;
quit;

如果我们知道例如品牌“ A”在整个2010年都存在,则上述逻辑是可以的。但是,如果品牌“ A”第一次出现在18/03/2010并一直存在到现在,则可以比较2010年和2011年将不够好,因为2010年我们将“短缺” 3个月。

所以我想做的是计算:

对于A:从18/03/2010到17/03/2011,然后从18/03/2011到17/03/2012的总和,依此类推。

B:从2010年12月7日至2011年7月11日的总和,依此类推。

以此类推。

有这样做的聪明方法吗?

第1步:确保按品牌和日期对数据集进行排序或编制索引

proc sort data=want;
     by brand date;
run;

步骤2:计算每种产品的开始/结束日期

下面的代码背后的想法:

  1. 我们知道品牌在分类数据集中的首次出现是品牌被引入的日期。 我们将其称为Product_Year_Start

  2. intnx函数可用于将该日期增加365天,然后从中减去1。 我们将此日期称为Product_Year_End

  3. 由于我们现在知道产品的年末日期,因此我们知道,如果任何给定行上的日期都超过产品的年末日期,那么我们就开始下一个产品年。 我们只计算该品牌的Product_Year_EndProduct_Year_Start ,并将它们提高一年。

所有这些都是通过按组处理和retain语句来实现的。

data Comparison_Dates;
    set have;
    by brand date;

    retain Product_Year_Start Product_Year_End;

    if(first.brand) then do;
        Product_Year_Start = date;
        Product_Year_End = intnx('year', date, 1, 'S') - 1;
    end;

    if(Date > Product_Year_End) then do;
        Product_Year_Start = intnx('year', Product_Year_Start, 1, 'S');
        Product_Year_End = intnx('year', Product_Year_End, 1, 'S');
    end;

    format Product_Year_Start Product_Year_End date9.;
run;

步骤3:使用原始的SQL代码,按新产品的开始/结束日期分组

proc sql;
    create table want as
    select catt(year(Product_Year_Start), '-', year(Product_Year_End) ) as Product_Year
         , Brand
         , sum(Units_Sold) as Unit_per_Year
    from Comparison_Dates
    group by Brand, calculated Product_Year
    order by Brand, calculated Product_Year;
quit;

下面的代码按照字面意义进行操作,对于每个“品牌”的最早“日期”,它开始聚合“单位销售”,当达到365天标记时,它将重置计数,并开始另一个周期。

data have;
    informat date ddmmyy10.;
    input date units_sold brand $ year;
    format date date9.;
    cards;
18/03/2010                5                   A         2010
12/04/2010                2                   A         2010
22/05/2010                1                   A         2010
25/05/2010                7                   A         2010
11/08/2011                5                   A         2011
12/07/2010                2                   B         2010
22/10/2010                1                   B         2010
05/05/2011                7                   B         2011
;

proc sort data=have;
    by brand date;
run;

data want;
    do until (last.brand);
        set have;
        by brand date;

        if first.brand then
            do;
                Sales_Over_365=0;
                _end=intnx('day',date,365);
            end;

        if date <= _end then
            Sales_Over_365+units_sold;
        else
            do;
                output;
                Sales_Over_365=units_sold;
                _end=intnx('day',date,365);
            end;
    end;

    output;
    drop _end;
run;

您需要每个品牌的开始日期。 目前,我们可以使用第一个销售日期,但这可能不是您想要的。 然后,您可以将每个销售日期分类为该品牌的年份。

让我们从样本数据创建数据集开始。 不需要YEAR变量。

data have ;
  input Date Units_Sold Brand $ Year ;
  informat date ddmmyy10.;
  format date yymmdd10.;
cards;
18/03/2010 5 A 2010
12/04/2010 2 A 2010
22/05/2010 1 A 2010
25/05/2010 7 A 2010
11/08/2011 5 A 2011
12/07/2010 2 B 2010
22/10/2010 1 B 2010
05/05/2011 7 B 2011
;;;;

现在,我们可以通过SQL查询获得所需的答案。

proc sql ;
  create table want as
   select brand
        , start_date
        , 1+floor((date - start_date)/365) as sales_year
        , intnx('year',start_date,calculated sales_year -1,'same')
            as start_sales_year format=yymmdd10.
        , sum(units_sold) as total_units_sold
  from
  ( select brand
        , min(date) as start_date format=yymmdd10.
        , date
        , units_sold
    from have
    group by 1
   )
  group by 1,2,3,4
  ;
quit;

这将产生以下结果:

                                               total_
                       sales_      start_      units_
Brand    start_date     year     sales_year     sold
  A      2010-03-18       1      2010-03-18      15
  A      2010-03-18       2      2011-03-18       5
  B      2010-07-12       1      2010-07-12      10

没有直接的方法可以做到这一点。 你可以做这样的事情。

为了测试代码,我将您的表保存到一个文本文件中。

然后,我创建了一个名为Sale的类。

public class Sale
{
    public DateTime Date { get; set; }
    public int UnitsSold { get; set; }
    public string Brand { get; set; }
    public int Year { get; set; }
}

然后,使用保存的文本文件填充List<Sale>

var lines = File.ReadAllLines(@"C:\Users\kosala\Documents\data.text");
var validLines = lines.Where(l => !l.Contains("Date")).ToList();//remove the first line.

List<Sale> sales = validLines.Select(l => new Sale()
        {
            Date = DateTime.Parse(l.Substring(0,10)),
            UnitsSold = int.Parse(l.Substring(26,5)),
            Brand = l.Substring(46,1),
            Year = int.Parse(l.Substring(56,4)),
        }).ToList();

//All the above code is for testing purposes. The actual code starts from here.
var totalUnitsSold = sales.OrderBy(s => s.Date).GroupBy(s => s.Brand);

        foreach (var soldUnit in totalUnitsSold)
        {
            DateTime? minDate = null;
            DateTime? maxDate = null;
            int total = 0;
            string brand = "";

            foreach (var sale in soldUnit)
            {
                brand = sale.Brand;
                if (minDate == null)
                {
                    minDate = sale.Date;
                }
                if ((sale.Date - minDate).Value.Days <= 365)
                {
                    maxDate = sale.Date;
                    total += sale.UnitsSold;
                }
                else
                {
                    break;
                }
            }
            Console.WriteLine("Brand : {0} UnitsSold Between {1} - {2} is {3}",brand, minDate.Value, maxDate.Value, total);
   }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM