BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
NewSASPerson
Quartz | Level 8

 

I would like to get the total distinct values by month and year to date. The logic in my code does the sum and I can't think through how to get the distinct values. I have attached a worksheet with the sample data.

 

 

This is my code

data WORK.BOOK4;
  infile datalines dsd truncover;
  input ACTUAL:DOLLAR20.2 COUNTRY:$6. PRODTYPE:$9. PRODUCT:$20. YEAR:F12. MONTH:$3.;
datalines4;
$107.00,CANADA,FURNITURE,SOFA,1994,Jan
$354.00,CANADA,FURNITURE,SOFA,1994,Feb
$101.00,CANADA,FURNITURE,OTTOMAN,1994,Mar
$553.00,CANADA,FURNITURE,SOFA,1994,Apr
$877.00,CANADA,FURNITURE,SOFA,1994,May
$431.00,CANADA,FURNITURE,SOFA,1994,Jun
$511.00,CANADA,FURNITURE,CHAIR,1994,Jul
$157.00,CANADA,FURNITURE,STOOL,1994,Aug
$520.00,CANADA,FURNITURE,SOFA,1994,Sep
$114.00,CANADA,FURNITURE,SOFA,1994,Oct
$277.00,CANADA,FURNITURE,SOFA,1994,Nov
$561.00,CANADA,FURNITURE,CUPBOARD,1994,Dec
$267.00,CANADA,FURNITURE,BED,1994,Jan
$347.00,CANADA,FURNITURE,BED,1994,Feb
$991.00,CANADA,FURNITURE,BED,1994,Mar
$923.00,CANADA,FURNITURE,BED,1994,Apr
$437.00,CANADA,FURNITURE,DESK,1994,May
$737.00,CANADA,FURNITURE,BED,1994,Jun
$104.00,CANADA,FURNITURE,BED,1994,Jul
$840.00,CANADA,FURNITURE,BED,1994,Aug
$704.00,CANADA,FURNITURE,BED,1994,Sep
$889.00,CANADA,FURNITURE,BED,1994,Oct
$107.00,CANADA,FURNITURE,LOVESEAT,1994,Nov
$571.00,CANADA,FURNITURE,BED,1994,Dec
$355.00,CANADA,FURNITURE,TABLE,1994,Jan
$506.00,CANADA,FURNITURE,TABLE,1994,Feb
$585.00,CANADA,FURNITURE,DRESSER,1994,Mar
$634.00,CANADA,FURNITURE,TABLE,1994,Apr
$662.00,CANADA,FURNITURE,TABLE,1994,May
$783.00,CANADA,FURNITURE,TABLE,1994,Jun
$786.00,CANADA,FURNITURE,TABLE,1994,Jul
$710.00,CANADA,FURNITURE,CHAIR,1994,Aug
$950.00,CANADA,FURNITURE,TABLE,1994,Sep
$274.00,CANADA,FURNITURE,SOFA,1994,Oct
$406.00,CANADA,FURNITURE,TABLE,1994,Nov
$515.00,CANADA,FURNITURE,TABLE,1994,Dec
$877.00,CANADA,FURNITURE,BED,1994,Jan
$845.00,CANADA,FURNITURE,CHAIR,1994,Feb
$425.00,CANADA,FURNITURE,CHAIR,1994,Mar
$899.00,CANADA,FURNITURE,BENCH,1994,Apr
$987.00,CANADA,FURNITURE,DESK,1994,May
$641.00,CANADA,FURNITURE,BED,1994,Jun
$448.00,CANADA,FURNITURE,CHAIR,1994,Jul
$341.00,CANADA,FURNITURE,CHAIR,1994,Aug
$137.00,CANADA,FURNITURE,CHAIR,1994,Sep
$235.00,CANADA,FURNITURE,CHAIR,1994,Oct
$482.00,CANADA,FURNITURE,CHAIR,1994,Nov
$678.00,CANADA,FURNITURE,CHAIR,1994,Dec
$942.00,CANADA,FURNITURE,DESK,1994,Jan
$912.00,CANADA,FURNITURE,DESK,1994,Feb
$768.00,CANADA,FURNITURE,CHAIR,1994,Mar
$951.00,CANADA,FURNITURE,DESK,1994,Apr
$768.00,CANADA,FURNITURE,NIGHTSTAND,1994,May
$978.00,CANADA,FURNITURE,DESK,1994,Jun
$20.00,CANADA,FURNITURE,DESK,1994,Jul
$298.00,CANADA,FURNITURE,DESK,1994,Aug
$193.00,CANADA,FURNITURE,TABLE,1994,Sep
$336.00,CANADA,FURNITURE,DESK,1994,Oct
$617.00,CANADA,FURNITURE,DESK,1994,Nov
$709.00,CANADA,FURNITURE,SOFA,1994,Dec
;;;;

proc sql;
create table cur_month as
select
Country,
prodtype,
Month,
"Current Month" as Type,
sum(actual) as Actual,
count(distinct product) as Prod_Type
from book4
group by country,  prodtype, month
order  by country,  prodtype, month;
quit;

data ytd (rename=(ytd_Actual = Actual ytd_type = Prod_Type));
set cur_month;
by  country prodtype;

if first.prodtype then ytd_actual = 0;
ytd_actual+ actual;

if first.prodtype then ytd_type = 0;
ytd_type + prod_type;

Type = "YTD";

drop  actual prod_type;
run;

data combined;
	set cur_month ytd;

run;

This is the output I get, The issues is on the Prod_Type column

COUNTRY	PRODTYPE	MONTH	Type		Actual	Prod_Type
CANADA	FURNITURE	Jan	Current Month	2548	4
CANADA	FURNITURE	Feb	Current Month	2964	5
CANADA	FURNITURE	Mar	Current Month	2870	4
CANADA	FURNITURE	Apr	Current Month	3960	5
CANADA	FURNITURE	May	Current Month	3731	4
CANADA	FURNITURE	Jun	Current Month	3570	4
CANADA	FURNITURE	Jul	Current Month	1869	4
CANADA	FURNITURE	Aug	Current Month	2346	4
CANADA	FURNITURE	Sep	Current Month	2504	4
CANADA	FURNITURE	Oct	Current Month	1848	4
CANADA	FURNITURE	Nov	Current Month	1889	5
CANADA	FURNITURE	Dec	Current Month	3034	5
CANADA	FURNITURE	Jan	YTD		2548	4
CANADA	FURNITURE	Feb	YTD		5512	9
CANADA	FURNITURE	Mar	YTD		8382	13
CANADA	FURNITURE	Apr	YTD		12342	18
CANADA	FURNITURE	May	YTD		16073	22
CANADA	FURNITURE	Jun	YTD		19643	26
CANADA	FURNITURE	Jul	YTD		21512	30
CANADA	FURNITURE	Aug	YTD		23858	34
CANADA	FURNITURE	Sep	YTD		26362	38
CANADA	FURNITURE	Oct	YTD		28210	42
CANADA	FURNITURE	Nov	YTD		30099	47
CANADA	FURNITURE	Dec	YTD		33133	52

However this is my desired output.

 

COUNTRY PRODTYPE MONTH Type 	   	Actual Prod_Type 
CANADA FURNITURE Jan Current Month 	2548 	4 
CANADA FURNITURE Feb Current Month 	2964 	5 
CANADA FURNITURE Mar Current Month 	2870 	4 
CANADA FURNITURE Apr Current Month 	3960 	5 
CANADA FURNITURE May Current Month 	3731 	4 
CANADA FURNITURE Jun Current Month 	3570 	4 
CANADA FURNITURE Jul Current Month 	1869	4 
CANADA FURNITURE Aug Current Month 	2346 	4 
CANADA FURNITURE Sep Current Month 	2504 	4 
CANADA FURNITURE Oct Current Month 	1848 	4 
CANADA FURNITURE Nov Current Month	1889 	5 
CANADA FURNITURE Dec Current Month 	3034 	5 
CANADA FURNITURE Jan YTD 		2548 	4 sofa, bed table, desk
CANADA FURNITURE Feb YTD 		5512 	5 chair
CANADA FURNITURE Mar YTD	        8382 	7 dresser, ottoman
CANADA FURNITURE Apr YTD 		12342 	8 bench
CANADA FURNITURE May YTD 		16073 	9 nightstand
CANADA FURNITURE Jun YTD 		19643 	9 
CANADA FURNITURE Jul YTD 		21512 	9 
CANADA FURNITURE Aug YTD 		23858 	10 stool
CANADA FURNITURE Sep YTD 		26362 	10 
CANADA FURNITURE Oct YTD 		28210 	10 
CANADA FURNITURE Nov YTD 		30099 	11 loveseat
CANADA FURNITURE Dec YTD 		33133 	12 cupboard

Any assistance I can get will be greatly appreciated.

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
mkeintz
PROC Star

YOu need more the $32767 to hold all the product values?  Then you can replace use of the CUM_TYPE_LIST variable with a hash object, which can be check for the presence of each product encountered, and can be dynamically grown by adding each each product's first encounter:

 

data WORK.BOOK4;
  infile datalines dsd truncover;
  input ACTUAL:DOLLAR20.2 COUNTRY:$6. PRODTYPE:$9. PRODUCT:$20. YEAR:F12. MONTH:$3.;
  monthnum=findw('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec',month,' ','e');
datalines4;
$107.00,CANADA,FURNITURE,SOFA,1994,Jan
$354.00,CANADA,FURNITURE,SOFA,1994,Feb
$101.00,CANADA,FURNITURE,OTTOMAN,1994,Mar
$553.00,CANADA,FURNITURE,SOFA,1994,Apr
$877.00,CANADA,FURNITURE,SOFA,1994,May
$431.00,CANADA,FURNITURE,SOFA,1994,Jun
$511.00,CANADA,FURNITURE,CHAIR,1994,Jul
$157.00,CANADA,FURNITURE,STOOL,1994,Aug
$520.00,CANADA,FURNITURE,SOFA,1994,Sep
$114.00,CANADA,FURNITURE,SOFA,1994,Oct
$277.00,CANADA,FURNITURE,SOFA,1994,Nov
$561.00,CANADA,FURNITURE,CUPBOARD,1994,Dec
$267.00,CANADA,FURNITURE,BED,1994,Jan
$347.00,CANADA,FURNITURE,BED,1994,Feb
$991.00,CANADA,FURNITURE,BED,1994,Mar
$923.00,CANADA,FURNITURE,BED,1994,Apr
$437.00,CANADA,FURNITURE,DESK,1994,May
$737.00,CANADA,FURNITURE,BED,1994,Jun
$104.00,CANADA,FURNITURE,BED,1994,Jul
$840.00,CANADA,FURNITURE,BED,1994,Aug
$704.00,CANADA,FURNITURE,BED,1994,Sep
$889.00,CANADA,FURNITURE,BED,1994,Oct
$107.00,CANADA,FURNITURE,LOVESEAT,1994,Nov
$571.00,CANADA,FURNITURE,BED,1994,Dec
$355.00,CANADA,FURNITURE,TABLE,1994,Jan
$506.00,CANADA,FURNITURE,TABLE,1994,Feb
$585.00,CANADA,FURNITURE,DRESSER,1994,Mar
$634.00,CANADA,FURNITURE,TABLE,1994,Apr
$662.00,CANADA,FURNITURE,TABLE,1994,May
$783.00,CANADA,FURNITURE,TABLE,1994,Jun
$786.00,CANADA,FURNITURE,TABLE,1994,Jul
$710.00,CANADA,FURNITURE,CHAIR,1994,Aug
$950.00,CANADA,FURNITURE,TABLE,1994,Sep
$274.00,CANADA,FURNITURE,SOFA,1994,Oct
$406.00,CANADA,FURNITURE,TABLE,1994,Nov
$515.00,CANADA,FURNITURE,TABLE,1994,Dec
$877.00,CANADA,FURNITURE,BED,1994,Jan
$845.00,CANADA,FURNITURE,CHAIR,1994,Feb
$425.00,CANADA,FURNITURE,CHAIR,1994,Mar
$899.00,CANADA,FURNITURE,BENCH,1994,Apr
$987.00,CANADA,FURNITURE,DESK,1994,May
$641.00,CANADA,FURNITURE,BED,1994,Jun
$448.00,CANADA,FURNITURE,CHAIR,1994,Jul
$341.00,CANADA,FURNITURE,CHAIR,1994,Aug
$137.00,CANADA,FURNITURE,CHAIR,1994,Sep
$235.00,CANADA,FURNITURE,CHAIR,1994,Oct
$482.00,CANADA,FURNITURE,CHAIR,1994,Nov
$678.00,CANADA,FURNITURE,CHAIR,1994,Dec
$942.00,CANADA,FURNITURE,DESK,1994,Jan
$912.00,CANADA,FURNITURE,DESK,1994,Feb
$768.00,CANADA,FURNITURE,CHAIR,1994,Mar
$951.00,CANADA,FURNITURE,DESK,1994,Apr
$768.00,CANADA,FURNITURE,NIGHTSTAND,1994,May
$978.00,CANADA,FURNITURE,DESK,1994,Jun
$20.00,CANADA,FURNITURE,DESK,1994,Jul
$298.00,CANADA,FURNITURE,DESK,1994,Aug
$193.00,CANADA,FURNITURE,TABLE,1994,Sep
$336.00,CANADA,FURNITURE,DESK,1994,Oct
$617.00,CANADA,FURNITURE,DESK,1994,Nov
$709.00,CANADA,FURNITURE,SOFA,1994,Dec
;;;;

proc sql;
create table cur_month as
  select  Country,prodtype,Month,monthnum,
          "Current Month" as Type,
          sum(actual) as Actual
  from book4
  group  by country,  prodtype, monthnum
  order  by country,  prodtype, monthnum;
quit;

proc sort data=book4 out=book4s  equals;
  by country prodtype monthnum;
run;

data prod_type_counts (keep=country prodtype monthnum prod_type);
  if 0 then set book4s;
  if _n_=1 then do;
    declare hash cum_prod_types();
	  cum_prod_types.definekey('product');
	  cum_prod_types.definedone();
  end;
  cum_prod_types.clear();
  do until (last.prodtype);
    set book4s;
    by country prodtype monthnum;
	if cum_prod_types.check()^=0 then do;
      cum_prod_types.add();
	  prod_type=sum(prod_type,1);
    end;
    if last.monthnum=1 then output;
  end;
run;

data want (drop=cum_: monthnum);
  do until (last.prodtype);
    set cur_month ;
    by country prodtype;
    output;
  end;

  do until (last.prodtype);
    merge cur_month prod_type_counts;
    by country prodtype monthnum;
    cum_actual+actual;
    type='YTD';
    actual=cum_actual;
    output;
  end;
run;


Note that I clear the hash object before starting each prodtype. So none of the cumulative counts cross PRODTYPEs.

--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------

View solution in original post

19 REPLIES 19
PaigeMiller
Diamond | Level 26

Please explain how March YTD in the desired output is 7. How is this computed?

 

Also, where does the "dresser, ottoman" come from?

--
Paige Miller
NewSASPerson
Quartz | Level 8
I have updated the post with the data. Please advise if you can see it. Thank you.
PaigeMiller
Diamond | Level 26

@NewSASPerson wrote:
I have updated the post with the data. Please advise if you can see it. Thank you.

Many of us will not open Microsoft Office documents because they are a security threat. Please provide the data as described in the link from @ballardw 

 

Also please explain how, in the desired output, March YTD is equal to 7. What are the calculations?

--
Paige Miller
NewSASPerson
Quartz | Level 8
I have update the code. It should have the data step now. Thank you.
Reeza
Super User

You likely need to also include a bit of what your starting data looks like.

ballardw
Super User

Any attachment of data apparently didn't make it.

Better is to provide data step code of data and paste as text in a code box:

 

Instructions here: https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-a-data-step-version-of-your-dat... will show how to turn an existing SAS data set into data step code that can be pasted into a forum code box using the {i} icon or attached as text to show exactly what you have and that we can test code against.

NewSASPerson
Quartz | Level 8

I just added the data (book4.xlsx)

mkeintz
PROC Star

Instead of adding a spreadsheet, you'll probably get a lot faster response, and more responses, if you utilize the advice from @ballardw :

 

Better is to provide data step code of data and paste as text in a code box:

 

Instructions here: https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-a-data-step-version-of-your-dat... will show how to turn an existing SAS data set into data step code that can be pasted into a forum code box using the {i} icon or attached as text to show exactly what you have and that we can test code against.

 

I know that I am always more ready to provide actual code when the person asking for help creates a sas data step appropriate to her question.  Help us help you.

 

Regards,

Mark

--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------
NewSASPerson
Quartz | Level 8

I have update the code. It should have the data step now. Thank you.

ballardw
Super User

I don't duplicate your output.

The data from the first proc SQL creating WORK.CUR_MONTH has the output in Alphabetical Month Order: Apr, Aug, Dec etc.

 

If you are doing something such that MONTH is numeric 1 to 12 it is not shown in the code.

mkeintz
PROC Star

Thank you for setting up the DATA step for this problem.  As @ballardw said, your program orders the months alphabetically instead of in calendar order.  So I modified your data step to add a MONTHNUM variable, which is then added to the group by and sort by clauses.

 

data WORK.BOOK4;
  infile datalines dsd truncover;
  input ACTUAL:DOLLAR20.2 COUNTRY:$6. PRODTYPE:$9. PRODUCT:$20. YEAR:F12. MONTH:$3.;
  monthnum=findw('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec',month,' ','e');
datalines4;
$107.00,CANADA,FURNITURE,SOFA,1994,Jan
$354.00,CANADA,FURNITURE,SOFA,1994,Feb
$101.00,CANADA,FURNITURE,OTTOMAN,1994,Mar
$553.00,CANADA,FURNITURE,SOFA,1994,Apr
$877.00,CANADA,FURNITURE,SOFA,1994,May
$431.00,CANADA,FURNITURE,SOFA,1994,Jun
$511.00,CANADA,FURNITURE,CHAIR,1994,Jul
$157.00,CANADA,FURNITURE,STOOL,1994,Aug
$520.00,CANADA,FURNITURE,SOFA,1994,Sep
$114.00,CANADA,FURNITURE,SOFA,1994,Oct
$277.00,CANADA,FURNITURE,SOFA,1994,Nov
$561.00,CANADA,FURNITURE,CUPBOARD,1994,Dec
$267.00,CANADA,FURNITURE,BED,1994,Jan
$347.00,CANADA,FURNITURE,BED,1994,Feb
$991.00,CANADA,FURNITURE,BED,1994,Mar
$923.00,CANADA,FURNITURE,BED,1994,Apr
$437.00,CANADA,FURNITURE,DESK,1994,May
$737.00,CANADA,FURNITURE,BED,1994,Jun
$104.00,CANADA,FURNITURE,BED,1994,Jul
$840.00,CANADA,FURNITURE,BED,1994,Aug
$704.00,CANADA,FURNITURE,BED,1994,Sep
$889.00,CANADA,FURNITURE,BED,1994,Oct
$107.00,CANADA,FURNITURE,LOVESEAT,1994,Nov
$571.00,CANADA,FURNITURE,BED,1994,Dec
$355.00,CANADA,FURNITURE,TABLE,1994,Jan
$506.00,CANADA,FURNITURE,TABLE,1994,Feb
$585.00,CANADA,FURNITURE,DRESSER,1994,Mar
$634.00,CANADA,FURNITURE,TABLE,1994,Apr
$662.00,CANADA,FURNITURE,TABLE,1994,May
$783.00,CANADA,FURNITURE,TABLE,1994,Jun
$786.00,CANADA,FURNITURE,TABLE,1994,Jul
$710.00,CANADA,FURNITURE,CHAIR,1994,Aug
$950.00,CANADA,FURNITURE,TABLE,1994,Sep
$274.00,CANADA,FURNITURE,SOFA,1994,Oct
$406.00,CANADA,FURNITURE,TABLE,1994,Nov
$515.00,CANADA,FURNITURE,TABLE,1994,Dec
$877.00,CANADA,FURNITURE,BED,1994,Jan
$845.00,CANADA,FURNITURE,CHAIR,1994,Feb
$425.00,CANADA,FURNITURE,CHAIR,1994,Mar
$899.00,CANADA,FURNITURE,BENCH,1994,Apr
$987.00,CANADA,FURNITURE,DESK,1994,May
$641.00,CANADA,FURNITURE,BED,1994,Jun
$448.00,CANADA,FURNITURE,CHAIR,1994,Jul
$341.00,CANADA,FURNITURE,CHAIR,1994,Aug
$137.00,CANADA,FURNITURE,CHAIR,1994,Sep
$235.00,CANADA,FURNITURE,CHAIR,1994,Oct
$482.00,CANADA,FURNITURE,CHAIR,1994,Nov
$678.00,CANADA,FURNITURE,CHAIR,1994,Dec
$942.00,CANADA,FURNITURE,DESK,1994,Jan
$912.00,CANADA,FURNITURE,DESK,1994,Feb
$768.00,CANADA,FURNITURE,CHAIR,1994,Mar
$951.00,CANADA,FURNITURE,DESK,1994,Apr
$768.00,CANADA,FURNITURE,NIGHTSTAND,1994,May
$978.00,CANADA,FURNITURE,DESK,1994,Jun
$20.00,CANADA,FURNITURE,DESK,1994,Jul
$298.00,CANADA,FURNITURE,DESK,1994,Aug
$193.00,CANADA,FURNITURE,TABLE,1994,Sep
$336.00,CANADA,FURNITURE,DESK,1994,Oct
$617.00,CANADA,FURNITURE,DESK,1994,Nov
$709.00,CANADA,FURNITURE,SOFA,1994,Dec
;;;;

proc sql;
create table cur_month as
select
Country,
prodtype,
Month,
"Current Month" as Type,
sum(actual) as Actual,
count(distinct product) as Prod_Type
from book4
group by country,  prodtype, monthnum, month
order  by country,  prodtype, monthnum, month;
quit;

Then, to follow the twelve months of current values, by the same twelve months year with YTD values, you can read the CUR_MONTH data set twice, as in:

 


data want (drop=cum_:);
  do until (last.prodtype);
    set cur_month ;
	by country prodtype;
	output;
  end;
  do until (last.prodtype);
    set cur_month ;
	by country prodtype;
	cum_actual+actual;
	cum_pt+prod_type;
	type='YTD';
	actual=cum_actual;
	prod_type=cum_pt;
	output;
  end;
run;

Now, if you have multiple years, and your want to do this for each year, you need to add YEAR to the group by and sort by clauses, between PRODTYPE and MONTHNUM.  And you would have to change the data step to use

 

do until (last.year);

and

by country prodtype year;

 

  

--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------
NewSASPerson
Quartz | Level 8
Thank you for your help in fixing the data step. However, this does not give me the desire result. It gives the same result I already have.
mkeintz
PROC Star

Ah, it's the list of newly-encountered products you want included in the YTD records.  To do this in the data step, you can use a sorted version of your original dataset (by country prodtype month).

 

Such a dataset can be interleaved with the results of your sql procedure  (by country prodtype monthnum), such that for each month, all the book4 products (sorted below) will be read, followed by the single record from the sql data set.  This program should do:

 

data WORK.BOOK4;
  infile datalines dsd truncover;
  input ACTUAL:DOLLAR20.2 COUNTRY:$6. PRODTYPE:$9. PRODUCT:$20. YEAR:F12. MONTH:$3.;
  monthnum=findw('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec',month,' ','e');
datalines4;
$107.00,CANADA,FURNITURE,SOFA,1994,Jan
$354.00,CANADA,FURNITURE,SOFA,1994,Feb
$101.00,CANADA,FURNITURE,OTTOMAN,1994,Mar
$553.00,CANADA,FURNITURE,SOFA,1994,Apr
$877.00,CANADA,FURNITURE,SOFA,1994,May
$431.00,CANADA,FURNITURE,SOFA,1994,Jun
$511.00,CANADA,FURNITURE,CHAIR,1994,Jul
$157.00,CANADA,FURNITURE,STOOL,1994,Aug
$520.00,CANADA,FURNITURE,SOFA,1994,Sep
$114.00,CANADA,FURNITURE,SOFA,1994,Oct
$277.00,CANADA,FURNITURE,SOFA,1994,Nov
$561.00,CANADA,FURNITURE,CUPBOARD,1994,Dec
$267.00,CANADA,FURNITURE,BED,1994,Jan
$347.00,CANADA,FURNITURE,BED,1994,Feb
$991.00,CANADA,FURNITURE,BED,1994,Mar
$923.00,CANADA,FURNITURE,BED,1994,Apr
$437.00,CANADA,FURNITURE,DESK,1994,May
$737.00,CANADA,FURNITURE,BED,1994,Jun
$104.00,CANADA,FURNITURE,BED,1994,Jul
$840.00,CANADA,FURNITURE,BED,1994,Aug
$704.00,CANADA,FURNITURE,BED,1994,Sep
$889.00,CANADA,FURNITURE,BED,1994,Oct
$107.00,CANADA,FURNITURE,LOVESEAT,1994,Nov
$571.00,CANADA,FURNITURE,BED,1994,Dec
$355.00,CANADA,FURNITURE,TABLE,1994,Jan
$506.00,CANADA,FURNITURE,TABLE,1994,Feb
$585.00,CANADA,FURNITURE,DRESSER,1994,Mar
$634.00,CANADA,FURNITURE,TABLE,1994,Apr
$662.00,CANADA,FURNITURE,TABLE,1994,May
$783.00,CANADA,FURNITURE,TABLE,1994,Jun
$786.00,CANADA,FURNITURE,TABLE,1994,Jul
$710.00,CANADA,FURNITURE,CHAIR,1994,Aug
$950.00,CANADA,FURNITURE,TABLE,1994,Sep
$274.00,CANADA,FURNITURE,SOFA,1994,Oct
$406.00,CANADA,FURNITURE,TABLE,1994,Nov
$515.00,CANADA,FURNITURE,TABLE,1994,Dec
$877.00,CANADA,FURNITURE,BED,1994,Jan
$845.00,CANADA,FURNITURE,CHAIR,1994,Feb
$425.00,CANADA,FURNITURE,CHAIR,1994,Mar
$899.00,CANADA,FURNITURE,BENCH,1994,Apr
$987.00,CANADA,FURNITURE,DESK,1994,May
$641.00,CANADA,FURNITURE,BED,1994,Jun
$448.00,CANADA,FURNITURE,CHAIR,1994,Jul
$341.00,CANADA,FURNITURE,CHAIR,1994,Aug
$137.00,CANADA,FURNITURE,CHAIR,1994,Sep
$235.00,CANADA,FURNITURE,CHAIR,1994,Oct
$482.00,CANADA,FURNITURE,CHAIR,1994,Nov
$678.00,CANADA,FURNITURE,CHAIR,1994,Dec
$942.00,CANADA,FURNITURE,DESK,1994,Jan
$912.00,CANADA,FURNITURE,DESK,1994,Feb
$768.00,CANADA,FURNITURE,CHAIR,1994,Mar
$951.00,CANADA,FURNITURE,DESK,1994,Apr
$768.00,CANADA,FURNITURE,NIGHTSTAND,1994,May
$978.00,CANADA,FURNITURE,DESK,1994,Jun
$20.00,CANADA,FURNITURE,DESK,1994,Jul
$298.00,CANADA,FURNITURE,DESK,1994,Aug
$193.00,CANADA,FURNITURE,TABLE,1994,Sep
$336.00,CANADA,FURNITURE,DESK,1994,Oct
$617.00,CANADA,FURNITURE,DESK,1994,Nov
$709.00,CANADA,FURNITURE,SOFA,1994,Dec
;;;;

proc sql;
  create table cur_month as
  select  Country,prodtype,Month,monthnum,"Current Month" as Type,
    sum(actual) as Actual,
    count(distinct product) as Prod_Type
  from book4
  group by country,  prodtype, monthnum,  month
  order  by country,  prodtype, monthnum, month;
quit;

proc sort data=book4 out=book4s;
  by country prodtype year monthnum;
run;

data want (drop=cum_: product);
  do until (last.prodtype);
    set cur_month ;
    by country prodtype;
    output;
  end;

  length cum_list $300 prod_list $50;
  do until (last.prodtype);
    set book4s (keep=country prodtype monthnum product in=inprod) 
        cur_month ;
    by country prodtype monthnum;
	if first.monthnum then call missing(prod_list);
    if inprod then do;
      if findw(cum_list,trim(product),' ','E')=0 then do;
        prod_list=catx(' ',prod_list,product);
        cum_list=catx(' ',cum_list,product);
      end;
      continue;  /* Go back to top of loop */
    end;
    cum_actual+actual;
    cum_pt+prod_type;
    type='YTD';
    actual=cum_actual;
    prod_type=cum_pt;
    output;
  end;
run;

How this works:

  1. In the DATA step, the first do loop writes out the results taken from the sql procedure.
  2. In the second do loop,
    1. the SET statement reads, for each country/prodtype/monthnum  all records from BOOK4S, followed by the single corresponding record from CUR_MONTH.
    2. The "if inprod" tests for whether the incoming record is from BOOK4S.  For such records the product tests whether the incoming PRODUCT has already been encountered and has been stored in CUM_LIST.  If it is not then it is added to CUM_LIST and also to PROD_LIST - the variable that will be output in the YTD record.
    3. The CONTINUE statement tell SAS to back to the beginning of the DO loop, which is what is needed for incoming BOOK4S records.
    4. Otherwise, if the incoming record is not from BOOK4S (inprod not equal to 1) it is from the sql data set, where ytd values are calculated and output.
--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 19 replies
  • 3407 views
  • 7 likes
  • 6 in conversation