I have a dataset (patients) as such:
Pat_ID Hos Date
A 11 1/1/2012
B 12 2/3/2012
B 13 2/3/2012
C 11 4/1/2012
C 11 4/5/2012
How do I count using proc sql such that the outcome looks something like this:
Pat_ID Visits
A 1
B 1
C 2
Since B has two visits on the same date, they are considered as only 1 visit, whereas C has 2 visits because they are on different dates.
select Pat_ID, count(distinct VisitDate) as Visits
from patient
group by Pat_ID
order by Pat_ID asc
Related
My dataset and attempt
data mydata;
input Category $ Item $;
datalines;
A 1
A 1
A 2
B 3
B 1
;
proc sql;
create table mytable as
select *, count(Category) as Total_No_in_Category, count(Category)-count(item, "3") as No_of_not_3_in_the_same_category from mydata
group by Category;
run;
Result
Category No_of_not_3_in_the_same_category Total_No_in_Category
A 3 3
A 3 3
A 3 3
B 2 2
B 2 1
My expected result
Category No_of_not_3_in_the_same_ category Total_No_in_Category
A 2 3
B 1 2
I wonder how to achieve the expected result using only proc SQL. Thank you so much.
The two argument COUNT(item, "3") function call is not an summary function. That causes all rows from original table to be automatically remerged with the aggregate computation (those count()). The remerge is a proprietary feature of SAS Proc SQL and not part of the ANSI Standard for SQL.
You appear to want the number of unique non-3 item values, so you will need a
COUNT(DISTINCT ...expression...)
in the query. The ...expression... can be a case clause that transforms item="3" to a null value by not having an else part of the case clause.
Example:
create table want as
select
category
, count(*) as freq
, count(distinct case when item ne "3" then item end) as n_unq_item_not_3
from mydata
group by category
;
I have tables as below.
Table A(total of 3000 rows, end_date may have duplicates, ex, 123 and 223 may have the same end_date)
enroll_dt,end_date, acct_nbr
12/31/2016, 01/03/2017, 123
12/31/2016, 01/04/2017, 234
01/05/2017, 02/02/2017, 334
Table B(total of 30 unique values)
enroll_dt
12/31/2016
01/01/2017
01/02/2017
01/03/2017
01/04/2017
01/05/2017
...
Desired table:
Date number_of_records
12/31/2016 2
01/01/2017 2
01/02/2017 2
01/03/2017 2
01/04/2017 1
02/01/2017 1
What I want to do is for each value from Table B, I would sort all of rows from Table A, and return # of acct_nbr if
for total # of accounts get enrolled until dateA, how many accounts have
end_date>DateA.
Ex. for 01/01/2017 from Table B, number_of_records = 2 since we only have 2 accounts enrolled until 01/01/2017(acct_nbr=123 and 234)
and end_date'01/03/2017' and '01/04/2017' both greater than '01/01/2017'
Thanks a lot for your help
Assuming your dates are stored as actual dates:
select
b.datea,
count(distinct a.acct_nbr)
from
b
inner join a
on a.end_date >= b.datea
group by
1
I am struggling to join two table without creating duplicate rows using proc sql ( not sure if any other method is more efficient).
Inner join is on: datepart(table1.date)=datepart(table2.date) AND tag=tag AND ID=ID
I think the problem is date and different names in table 1. By just looking that the table its clear that table1's row 1 should be joined with table 2's row 1 because the transaction started at 00:04 in table one and finished at 00:06 in table 2. I issue I am having is I cant join on dates with the timestamp so I am removing timestamps and because of that its creating duplicates.
Table1:
id tag date amount name_x
1 23 01JUL2018:00:04 12 smith ltd
1 23 01JUL2018:00:09 12 anna smith
table 2:
id tag ref amount date
1 23 19 12 01JUL2018:00:06:00
1 23 20 12 01JUL2018:00:10:00
Desired output:
id tag date amount name_x ref
1 23 01JUL2018 12 smith ltd 19
1 23 01JUL2018 12 anna smith 20
Appreciate your help.
Thanks!
You need to set a boundary for that datetime join. You are correct in why you are getting duplicates. I would guess the lower bound is the previous datetime, if it exists and the upper bound is this record's datetime.
As an aside, this is poor database design on someone's part...
Let's first sort table2 by id, tag, and date
proc sort data=table2 out=temp;
by id tag date;
run;
Now write a data step to add the previous date for unique id/tag combinations.
data temp;
set temp;
format low_date datetime20.
by id tag;
retain p_date;
if first.tag then
p_date = 0;
low_date = p_date;
p_date = date;
run;
Now update your join to use the date range.
proc sql noprint;
create table want as
select a.id, a.tag, a.date, a.amount, a.name_x, b.ref
from table1 as a
inner join
temp as b
on a.id = b.id
and a.tag = b.tag
and b.low_date < a.date <= b.date;
quit;
If my understanding is correct, you want to merge by ID, tag and the closest two date, it means that 01JUL2018:00:04 in table1 is the closest with 01JUL2018:00:06:00 in talbe2, and 01JUL2018:00:09 is with 01JUL2018:00:10:00, you could try this:
data table1;
input id tag date:datetime21. amount name_x $15.;
format date datetime21.;
cards;
1 23 01JUL2018:00:04 12 smith ltd
1 23 01JUL2018:00:09 12 anna smith
;
data table2;
input id tag ref amount date: datetime21.;
format date datetime21.;
cards;
1 23 19 12 01JUL2018:00:06:00
1 23 20 12 01JUL2018:00:10:00
;
proc sql;
select a.*,b.ref from table1 a inner join table2 b
on a.id=b.id and a.tag=b.tag
group by a.id,a.tag,a.date
having abs(a.date-b.date)=min(abs(a.date-b.date));
quit;
The google search has been difficult for this. I have two categorical variables, age and months, with 7 levels each. for a few levels, say age =7 and month = 7 there is no value and when I use proc sql the intersections that do not have entries do not show, eg:
age month value
1 1 4
2 1 12
3 1 5
....
7 1 6
...
1 7 8
....
5 7 44
6 7 5
THIS LINE DOESNT SHOW
what i want
age month value
1 1 4
2 1 12
3 1 5
....
7 1 6
...
1 7 8
....
5 7 44
6 7 5
7 7 0
this happens a few times in the data, where tha last groups dont have value so they dont show, but I'd like them to for later purposes
You have a few options available, both seem to work on the premise of creating the master data and then merging it in.
Another is to use a PRELOADFMT and FORMATs or CLASSDATA option.
And the last - but possibly the easiest, if you have all months in the data set and all ages, then use the SPARSE option within PROC FREQ. It creates all possible combinations.
proc freq data=have;
table age*month /out = want SPARSE;
weight value;
run;
First some sample data:
data test;
do age=1 to 7;
do month=1 to 12;
value = ceil(10*ranuni(1));
if ranuni(1) < .9 then
output;
end;
end;
run;
This leaves a few holes, notably, (1,1).
I would use a series of SQL statements to get the levels, cross join those, and then left join the values on, doing a coalesce to put 0 when missing.
proc sql;
create table ages as
select distinct age from test;
create table months as
select distinct month from test;
create table want as
select a.age,
a.month,
coalesce(b.value,0) as value
from (
select age, month from ages, months
) as a
left join
test as b
on a.age = b.age
and a.month = b.month;
quit;
The group independent crossing of the classification variables requires a distinct selection of each level variable be crossed joined with the others -- this forms a hull that can be left joined to the original data. For the case of age*month having more than one item you need to determine if you want
rows with repeated age and month and original value
rows with distinct age and month with either
aggregate function to summarize the values, or
an indication of too many values
data have;
input age month value;
datalines;
1 1 4
2 1 12
3 1 5
7 1 6
1 7 8
5 7 44
6 7 5
8 8 1
8 8 11
run;
proc sql;
create table want1(label="Original class combos including duplicates and zeros for absent cross joins")
as
select
allAges.age
, allMonths.month
, coalesce(have.value,0) as value
from
(select distinct age from have) as allAges
cross join
(select distinct month from have) as allMonths
left join
have
on
have.age = allAges.age and have.month = allMonths.month
order by
allMonths.month, allAges.age
;
quit;
And a slight variation that marks duplicated class crossings
proc format;
value S_V_V .t = 'Too many source values'; /* single valued value */
quit;
proc sql;
create table want2(label="Distinct class combos allowing only one contributor to value, or defaulting to zero when none")
as
select distinct
allAges.age
, allMonths.month
, case
when count(*) = 1 then coalesce(have.value,0)
else .t
end as value format=S_V_V.
, count(*) as dup_check
from
(select distinct age from have) as allAges
cross join
(select distinct month from have) as allMonths
left join
have
on
have.age = allAges.age and have.month = allMonths.month
group by
allMonths.month, allAges.age
order by
allMonths.month, allAges.age
;
quit;
This type of processing can also be done in Proc TABULATE using the CLASSDATA= option.
Suppose I have a dataset A:
ID Geogkey
1 A
1 B
1 C
2 W
2 R
2 S
and another dataset B:
ID Temp Date
1 95 1
1 100 2
1 105 3
2 10 1
How do I merge these two datasets so I get three records each for geogkeys with id=1 and one record each for geogkeys where id =2?
Assuming you want the cartesian join, you are best off doing that in SQL, if it's not too big:
proc sql;
create table C as
select * from A,B
where A.ID=B.ID
;
quit;
The select * will generate a warning that the ID variables are overwriting; if that's a concern, explicitly spell out your select (select A.ID, A.Geogkey, B.Temp, B.date).