I want to create a table head for a table, replacing the orignal table head.
The table head contains two rows:
head1 = !!!SS!FAS!PPS!
head2 = !Index!!(N=30)!(N=30)!(N=30)!
Here the ! meaning column delimiter. The head1 is the first row of table head .In head1, the first and second column are blank and the content of third, fourth and fifith are SS, FAS and PPS.
The head2 is the second row of table head, the content of the first column is index; the second is blank; the third to fifth are (N-30).
I want to form the following table head:
enter image description here
You can use ods inline styling to place a newline in a columns label (which is used for the header in output). Inline styling is introduced using a special escapechar of your choosing. Also, during output, a blank label is replaced with the column name, so use a hard-space character (A0) to force a blank header.
data have;
index = 1;
blank = ' ';
ss = 1;
fas = 2;
pps = 3;
run;
ods escapechar='^';
proc print noobs data=have label;
var index blank ss fas pps blank;
label
index = '^nIndex'
blank = '0A'x
ss = 'SS^n(N=30)'
fas = 'FAS^n(N=30)'
pps = 'PPS^n(N=30)'
;
run;
Related
I am trying to create a summary row above each group in my data. I have 2 questions:
How do I merge the first 2 cells horizontally (the ones in red below) in the summary rows.
How do I remove the duplicated F and M in the Sex column (at the moment I can work around this by changing only those cell's text colours to white, but hopefully there's a better way)
The output is an RTF file, and I'm using SAS 9.4 - the desktop version.
Is this possible using proc report?
Code:
options missing=' ';
proc report data=sashelp.class nowd;
columns sex name age weight;
define sex / order;
break before sex / summarize;
run;
I don't think you can merge cells in the summarize line.
Some trickery with compute blocks and call define can alter the cell values and appearances.
For example (Just J names for smaller image):
proc report data=sashelp.class nowd;
where name =: 'J';
columns sex name age weight;
define sex / order;
define age / sum;
define weight / sum;
break before sex / summarize style=[verticalalign=bottom];
compute name;
* the specification of / order for sex sets up conditions in the name value
* that can be leveraged in the compute block;
if name = ' ' then do;
* a blank name means the current row the compute is acting on
* is the summarization row;
* uncomment if stat is not obvious or stated in title;
* name = 'SUM';
* 'hide' border for appearance of merged cell;
call define (1, 'style', 'style=[fontsize=18pt borderrightcolor=white]');
end;
else do;
* a non-blank name means one of the detail rows is being processed;
* blank out the value in the sex column of the detail rows;
* the value assignment can only be applied to current column or those
* to the left;
sex = ' ';
end;
endcomp;
compute after sex;
* if you want more visual separation add a blank line;
* line ' ';
endcomp;
run;
referring to below code, after I transpose a data-set (output qc2), I tried to create a percentage column (most_recent_wk_percent_change) but the result of the column is 12.5% with two new columns - &week3. and &week2. created. The expected result is to calculate based on the values in week2 and week3 columns. I know the problem could be the referencing of the two columns in the percentage calculation (==> ( &week3. - &week2.)/&week2.;) , but I couldn't put my head to the correction. pls advise :)
%let week1 = 7;
%let week2 = 8;
%let week3 = 9;
proc sql;
create table qc as
select t_week, prod_cat, sum(sales) as sales
from master_table
where t_week in (&week1.,&week2.,&week3.)
group by 1,2
order by 2;
quit;
proc transpose data= qc out=qc2;
format
by prod_cat ;
id t_week;
run;
data qc2;
set qc2;
format most_recent_wk_percent_change PERCENT7.1;
most_recent_wk_percent_change = ( &week3. - &week2.)/&week2.;
run;
qc:
t_week|prod_cat|sales
7|cat|100
8|cat|200
9|cat|300
7|dog|150
8|dog|400
9|dog|300
7|rat|200
8|rat|600
9|rat|300
qc2: (TRANSPOSED TABLE --> note the column name of 7,8,9. (which is expected)
prod_cat|7|8|9
cat|100|200|300
dog|150|400|300
rat|200|600|300
qc2: (i wanted to get the change in % )
prod_cat|7|8|9|most_recent_wk_percent_change|&week2.|&week3.
cat|100|200|300|12.5%|.|.| ==> 12.5% is wrong. should be 50% (300-200)/(200)
dog|150|400|300|12.5%|.|.| ==> 12.5% is wrong. should be -25%
rat|200|600|300|12.5%|.|.| ==> 12.5% is wrong. should be -50%
I have no idea what you are doing or why, but if you have set VALIDVARNAME=any and the actual name of your variable is 7 and you try to use it in SAS code like this:
ratio = 7/8 ;
Then SAS will assume you mean the numeric value 7.
You need to use a name literal instead.
ratio = '7'n / '8'n ;
So you want
most_recent_wk_percent_change = ("&week3"n-"&week2"n)/"&week2"n;
If instead the actual name of the variable is _7 then you need to code this way.
most_recent_wk_percent_change = (_&week3.-_&week2.)/_&week2.;
Try adding a keep statement to your last data step, this will only keeps the columns you want in the output.
data qc2 (keep= most_recent_wk_percent_change prod_cat);
set qc2;
format most_recent_wk_percent_change PERCENT7.1;
most_recent_wk_percent_change = ( &week3. - &tweek2.)/&week2.;
run;
I have a series of values in a column -- the first value is a category description, the different categories are separated by a blank row. In the example below the first category is called A, second category is called T, and third category is called R.
What I want to do is to retain the first instance of the category name and create a new field name prefaced by the category. See the have/want below. Any ideas?
For example:
data example;
input have $1. want $4.;
datalines;
A
T A_T
G A_G
T
R T_R
E T_E
W T_W
R
H R_H
R R_R
;
you should consider using retain statement in SAS to carry the values over and lag statement to determine when you need to reset your retained value.
data have;
input category $1.;
datalines;
A
T
G
T
R
E
W
R
H
R
;
data want (drop=category_retained);
set have;
length subcategory $3.;
retain category_retained "";
if lag(category) = "" then
do;
subcategory = "";
category_retained = category;
END;
if lag(category) ne "" and category ne "" then
do;
subcategory = CATX("_",category_retained,category);
END;
RUN;
Got the following example
I'm trying to know if any part of string in the column nomvar in table tata does exist in col1 in table toto and if yes, give me the definition using col2.
For I2010,RT,IS-IPI,F_CC11_X_CCXBA, I would have in the column intitule "yes,toto,tata,well"
I thought about using a proc sql with an insert and a select but I have two tables and I would need to do a join.
In the same time, I thought to have everything in one table but I'm unsure if it is a good idea.
Any suggestions are welcomed as I'm deeply stuck.
The SAS data step hash object is a nice way to do this. It allows you to read the Toto table into memory and it becomes a lookup table for you. Then you just walk the string from the Tata table using the scan function, tokenize, and lookup the col2 value. Here is the code.
By the way, turning table Tata into a structure like Toto and performing join is a perfectly rational way to do this, too.
/*Create sample data*/
data toto;
length col1 col2 $ 100;
col1='I2010';
col2='yes';
output;
col1='RT';
col2='toto';
output;
col1='IS-IPI';
col2='tata';
output;
col1='F_CC11_X_CCXBA';
col2='well';
output;
run;
data tata;
length nomvar intitule $ 100;
nomvar='I2010,RT,IS-IPI,F_CC11_X_CCXBA';
run;
/*Now for the solution*/
/*You can do this lookup easily with a data step hash object*/
data tata;
set tata;
length col1 col2 token $ 100;
drop col1 col2 token i sepchar rc;
/*slurp the data in from the Toto data set into the hash*/
if (_n_ = 1) then do;
declare hash toto_hash(dataset: 'work.toto');
rc = toto_hash.definekey('col1');
rc = toto_hash.definedata('col2');
toto_hash.definedone();
end;
/*now walk the tokens in data set tata and perform the lookup to get each value*/
i = 1;
sepchar = ''; /*this will be a comma after the first iteration of the loop*/
intitule = '';
do until (token = '');
/*grab nth item in the comma-separated list*/
token = scan(nomvar, i, ',');
/*lookup the col2 value from the toto data set*/
rc = toto_hash.find(key:token);
if (rc = 0) then do;
/*lookup successful so tack the value on*/
intitule = strip(intitule) || sepchar || col2;
sepchar = ',';
end;
i = i + 1;
end;
run;
Assuming your data is all structured like this (you're looking at the different strings in between . characters) I would think the easiest way is to normalize TATA (splitting by .) and then doing a straight join, then (if you need to) transposing back. (It might be better to leave it vertical - very likely you would find this more useful structure for analysis.)
data tata_v;
set tata;
call scan(nomvar,1,position,length,'.');
do _i = 1 by 1 while position le 0);
nomvar_out = substr(nomvar,position,length);
output;
call scan(nomvar,_i+1,position,length,'.');
end;
run;
Now you can join on nomvar_out and then (if needed) recombine things.
I have a column in my sas file as age and another column as finalage. I want to substitute the values in age column by values in agefinal column for just one ID (that is 5)
The code that I used was:
Data temp;
set temp;
if ID = 5;
then age = agefinal;
run;
I could not substitute the values. The values in age column did not change. I tried to run this code to check the character length of values since character type is numeric for both the columns.
Code:
Proc contents data = temp;
tables age agefinal;
run;
The output that I got was:
age : character length 3.
agefinal: character length $3
I would appreciate your suggestions.
Try removing the semicolon at the end of the if statement. Right now what you're doing is deleting all records where the id isn't equal to five.
Try setting the formats to be the same
data temp;
modify temp;
format age agefinal $3.;
run;
and then see if it will let you do the substitution.
The code you provided runs with an ERROR, remove the additional semicolon and that may fix your issue:
/* ORIGINAL */
Data temp;
set temp;
if ID = 5;
then age = agefinal;
run;
/* CORRECTED */
Data temp;
set temp;
if ID = 5 /* REMOVED SEMICOLON */
then age = agefinal;
run;
Cheers
Rob