我有一个像下面这样的数据集。我想根据每个人前一天的表现给他排名。如果name
的sales
在第t天排名前10%(20%),则name
在第t+1天的排名应为1(2)。同样,如果name
的sales
在第t天排名后10%,则name
在第t+1天的排名应为10。
name date sales rank
a day1 11
b day1 20
c day1 15
d day1 8
a day2 12
b day2 21
c day2 16
d day2 9
a day3 7
b day3 14
c day3 12
d day3 10
到目前为止我所做的是:步骤1。按date
sales
排序数据步骤2。创建new_variable i
和N
。I是名称I在每个date
上的确切排名。N
测量每个date
上的观测总数。如果I
/N
小于。1,则Rank_previous
= 10,以此类推。这个新变量Rank_previous
将是name
在第t天的排名。
然而,我不知道如何将今天的排名Rank_previous
分配给第二天的Rank
。因为我的数据集非常大,如果你有更有效的方法来解决这个问题,那就太好了。
data data;
set data;
retain I;by date sales;
if first.date or first.sales then I=1; else I=I+1;
run;
proc sql;
create table data
as select a.*, max(I) as N
from data as a
group by date sales
order by date sales;
quit;
data data (drop=I N);
set data;
Rank_temp = I/N;
if Rank_temp <= .1 then rank = 10;
run;
考虑到SAS的命名法,计算排名最简单的方法是PROC RANK
。因此,数据必须先按by date
排序。
data have;
input name $ date $ sales;
datalines;
a day1 11
b day1 20
c day1 15
d day1 8
a day2 12
b day2 21
c day2 16
d day2 9
a day3 7
b day3 14
c day3 12
d day3 10
;;;;
run;
proc rank data=have out=ranks percent;
by date;
var sales;
ranks rank;
run;
percent
参数要求百分位数而不是数字排名。另一个可能会给你想要的排名的选项是groups=10
,它会将所有值分配给十个组中的一个,尽管有时使用原始百分位数并自己分配更容易(为了更好地处理平局等)。你也可以用descending
来反向赋值,因为我不清楚你想要的顺序。
当然,当您想要下一个天的排名时,您将需要一个短的数据步来将日期增加1并合并,或者SQL连接,或者您喜欢的任何方法。
**首先让我们加载比示例中更多的数据:**;
data have;
input name $ date $ sales;
datalines;
a day1 11
b day1 20
c day1 15
d day1 8
a day2 12
b day2 21
c day2 16
d day2 9
e day2 1
f day2 90
g day2 99
h day2 2
i day2 70
j day2 39
k day2 1
l day2 16
m day2 90
a day3 7
b day3 14
c day3 12
d day3 10
e day4 1
f day4 90
g day4 99
h day4 2
i day4 70
j day4 39
k day4 1
l day4 16
m day4 90
;
run;
**然后,按照Joe教我们的,让我们应用proc rank。我们需要的是排名,而不是销售额,所以让我们删掉它**;
proc rank groups=10
data=have
out=ranks (drop=sales);
by date;
var sales;
ranks rank_previous;
run;
**现在要合并前一天的排名和今天的销售,我们需要一点记忆**;
data have_memory;
set have;
by date;
retain date_previous;
if first.date then date_previous = lag1(date);
run;
**现在一个经典的合并工作**;
data have_ranked (drop=date_previous);
merge have_memory (in=has)
ranks (rename=(date=date_previous));
by date_previous name;
if not has then date = 'next';
run;
使用两组临时数组:一组用于保存前一天的排名,另一组用于创建当天的排名。
当你点击最后。日期,可以输出当天的所有信息,包括前几天的排行。然后,将今天的排名复制到前一天的数组中。
这样,您只需要在按日期和销售额排序后浏览一次数据。
首先让我们加载比示例中更多的数据:
data have;
input name $ date $ sales;
datalines;
a day1 11
b day1 20
c day1 15
d day1 8
a day2 12
b day2 21
c day2 16
d day2 9
e day2 1
f day2 90
g day2 99
h day2 2
i day2 70
j day2 39
k day2 1
l day2 16
m day2 90
a day3 7
b day3 14
c day3 12
d day3 10
;
run;
考虑到性能问题,我会选择PROC MEAN(它是PROC sort的扩展版本,可以保存小计并且非常高效)。class子句对应于进程sort)
中的by子句。proc means data=have noprint;
class date sales name;
output out=haveMean (where=(_type_ in (4,7)));
小计和详细信息由类型区分。省略where子句以查找哪个类型具有什么数据。现在合并类型 4:其中频率为我们提供了当天活动的销售人员数量和类型 7:详细信息
data salesSum;
merge haveMean (where=(_type_ eq 4) rename=(_freq_=numberPerDay) drop=name sales)
haveMean (where=(_type_ eq 7));
by date;
跟踪一天内的订单数量,并将其除以活动销售人员的数量
retain orderInDay rank;
if first.date then orderInDay = 1; else orderInDay = orderInDay+ 1;
只计算具有一定销售量的第一个观察值的排名,给执行相同的排名
if first.date or lag1(sales) NE sales then rank = ceil(10 * orderInDay / numberPerDay);
打印相关内容
proc print data=salesSum;
by date;
var name sales rank;
run;
该解决方案仍然缺少将排名移植到下一个工作日的方法。为此,我考虑使用散列表。