[英]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:計算每種產品的開始/結束日期
下面的代碼背后的想法:
我們知道品牌在分類數據集中的首次出現是品牌被引入的日期。 我們將其稱為Product_Year_Start
。
intnx
函數可用於將該日期增加365天,然后從中減去1。 我們將此日期稱為Product_Year_End
。
由於我們現在知道產品的年末日期,因此我們知道,如果任何給定行上的日期都超過產品的年末日期,那么我們就開始下一個產品年。 我們只計算該品牌的Product_Year_End
和Product_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.