SAS层次结构总和
SAS hierarchical structure sum
我有一个带有分层编码表变量的数据集。
层级逻辑由LEVEL变量和CODE字符变量的前缀结构决定。
有6个(码长从1到6)"aggregate"级和终端级(码长10个字符)
我需要更新节点变量(终端节点的计数 - 聚合级别不计入 "higher" 聚合,仅计入终端节点) - 例如,一个级别的计数总和每个 5 级的总计数与每个 6 级的总计数相同。
我需要计算(总结)"higher" 级节点的权重。
注意:我偏移了输出 table 的 NODES 和 WEIGHT 变量,以便您可以更好地理解我在说什么(只需将每个偏移量中的数字相加即可得到相同的值)。
EDIT1:同一代码可以有多个观察结果。一个独特的观察结果是 3 个变量 code + var1 + var2 的组合。
输入table:
ID level code var1 var2 nodes weight myIndex
1 1 1 . . 999 999 999
2 2 11 . . 999 999 999
3 3 111 . . 999 999 999
4 4 1111 . . 999 999 999
5 5 11111 . . 999 999 999
6 6 111111 . . 999 999 999
7 10 1111119999 01 1 1 0.1 105,5
8 10 1111119999 01 2 1 0.1 109,1
9 6 111112 . . 999 999 999
10 10 1111120000 01 1 1 0.5 95,0
11 5 11119 . . 999 999 999
12 6 111190 . . 999 999 999
13 10 1111901000 01 1 1 0.1 80,7
14 10 1111901000 02 1 1 0.2 105,5
期望的输出table:
ID level code var1 var2 nodes weight myIndex
1 1 1 . . 5 1.0 98,1
2 2 11 . . 5 1.0 98,1
3 3 111 . . 5 1.0 98,1
4 4 1111 . . 5 1.0 98,1
5 5 11111 . . 3 0.7 98,5
6 6 111111 . . 2 0.2 107,3
7 10 1111119999 01 1 1 0.1 105,5
8 10 1111119999 01 2 1 0.1 109,1
9 6 111112 . . 1 0.5 95,0
10 10 1111120000 01 1 1 0.5 95,0
11 5 11119 . . 2 0.3 97,2
12 6 111190 . . 2 0.3 97,2
13 10 1111901000 01 1 1 0.1 80,7
14 10 1111901000 02 1 1 0.2 105,5
这是我想出的代码。它就像我想要的那样工作,但是伙计,它真的很慢。我需要更快的方法,因为这是网络服务的一部分,必须根据要求 运行 "instantly" 。
欢迎任何关于加速代码或任何其他解决方案的建议。
%macro doit;
data temporary;
set have;
run;
%do i=6 %to 2 %by -1;
%if &i = 6 %then %let x = 10;
%else %let x = (&i+1);
proc sql noprint;
select count(code)
into :cc trimmed
from have
where level = &i;
select code
into :id1 - :id&cc
from have
where level = &i;
quit;
%do j=1 %to &cc.;
%let idd = &&id&j;
proc sql;
update have t1
set nodes = (
select sum(nodes)
from temporary t2
where t2.level = &x and t2.code like ("&idd" || "%")),
set weight = (
select sum(weight)
from temporary t2
where t2.level = &x and t2.code like ("&idd" || "%"))
where (t1.level = &i and t1.code like "&idd");
quit;
%end;
%end;
%mend doit;
基于@Quentin 解决方案的当前代码:
data have;
input ID level code : . nodes weight myIndex;
cards;
1 1 1 . . .
2 2 11 . . .
3 3 111 . . .
4 4 1111 . . .
5 5 11111 . . .
6 6 111111 . . .
7 10 1111110000 1 0.1 105.5
8 10 1111119999 1 0.1 109.1
9 6 111112 . . .
10 10 1111129999 1 0.5 95.0
11 5 11119 . . .
12 6 111190 . . .
13 10 1111900000 1 0.1 80.7
14 10 1111901000 1 0.2 105.5
;
data want (drop=_:);
*hash table of terminal nodes;
if (_n_ = 1) then do;
if (0) then set have (rename=(code=_code weight=_weight));
declare hash h(dataset:'have(where=(level=10) rename=(code=_code weight=_weight myIndex=_myIndex))');
declare hiter iter('h');
h.definekey('ID');
h.definedata('_code','_weight','_myIndex');
h.definedone();
end;
set have;
*for each non-terminal node, iterate through;
*hash table of all terminal nodes, looking for children;
if level ne 10 then do;
call missing(weight, nodes, myIndex);
do _n_ = iter.first() by 0 while (_n_ = 0);
if trim(code) =: _code then do;
weight=sum(weight,_weight);
nodes=sum(nodes,1);
myIndex=sum(myIndex,_myIndex*_weight);
end;
_n_ = iter.next();
end;
myIndex=round(myIndex/weight,.1);
end;
output;
run;
一种方法(我认为)是制作笛卡尔积,并找到与每个节点 "match" 相关的所有终端节点,然后对权重求和。
类似于:
data have;
input ID level code : . nodes weight ;
cards;
1 1 1 . .
2 2 11 . .
3 3 111 . .
4 4 1111 . .
5 5 11111 . .
6 6 111111 . .
7 10 1111110000 1 0.1
8 10 1111119999 1 0.1
9 6 111112 . .
10 10 1111129999 1 0.5
11 5 11119 . .
12 6 111190 . .
13 10 1111900000 1 0.1
14 10 1111901000 1 0.2
;
proc sql;
select min(id) as id
, min(level) as level
, a.code
, count(b.weight) as nodes /*count of terminal nodes*/
, sum(b.weight) as weight /*sum of weights of terminal nodes*/
from
have as a
,(select code , weight
from have
where level=10 /*selects terminal nodes*/
) as b
where a.code eqt b.code /*EQT is equivalent to =: */
group by a.code
;
quit;
我不确定这是否正确,但它给出了示例数据所需的结果。
下面是一种蛮力哈希方法,用于执行与 SQL 中类似的笛卡尔积。加载终端节点的哈希 table。然后读取节点数据集,对于每个不是终端节点的节点,遍历散列table,识别所有子终端节点。
我认为@joop 描述的方法可能更有效,因为这种方法没有利用树结构。所以有很多重新计算。对于 5000 条记录和 3000 个终端节点,这将进行 2000*3000 次比较。但可能不会那么慢,因为哈希 table 在内存中,所以你不会有过多的 I/O ....
data want (drop=_:);
*hash table of terminal nodes;
if (_n_ = 1) then do;
if (0) then set have (rename=(code=_code weight=_weight));
declare hash h(dataset:'have(where=(level=10) rename=(code=_code weight=_weight))');
declare hiter iter('h');
h.definekey('ID');
h.definedata('_code','_weight');
h.definedone();
end;
set have;
*for each non-terminal node, iterate through;
*hash table of all terminal nodes, looking for children;
if level ne 10 then do;
call missing(weight, nodes);
do _n_ = iter.first() by 0 while (_n_ = 0);
if trim(code) =: _code then do;
weight=sum(weight,_weight);
nodes=sum(nodes,1);
end;
_n_ = iter.next();
end;
end;
output;
run;
这是估计每条记录的 parent 条记录所需的 SQL。它只使用字符串函数(位置和长度),所以它应该适应 table 到 SQL 的任何方言,甚至可能是 SAS。 (CTE 可能需要重写为子查询或视图)想法是:
- 向数据集添加一个 parent_id 字段
- 找出代码子串最长的记录
- 并使用它的 id 作为我们 parent_id
的值
- (在那之后)从 直接 children 的 sum(nodes),sum(weight) 更新记录(那些 child.parent_id = this.id )
顺便说一句:我本可以使用 LEVEL
而不是 LENGTH(code)
;这方面的数据有点冗余。
WITH sub AS (
SELECT id, length(code) AS len
, code
FROM tree)
UPDATE tree t
SET parent_id = s.id
FROM sub s
WHERE length(t.code) > s.len AND POSITION (s.code IN t.code) = 1
AND NOT EXISTS (
SELECT *
FROM sub nx
WHERE nx.len > s.len AND POSITION (nx.code IN t.code ) = 1
AND nx.len < length(t.code) AND POSITION (nx.code IN t.code ) = 1
)
;
SELECT * FROM tree
ORDER BY parent_id DESC NULLS LAST
, id
;
找到 parent 之后,整个 table 应该从自身更新(重复)
喜欢:
-- PREPARE omg( integer) AS
UPDATE tree t
SET nodes = s.nodes , weight = s.weight
FROM ( SELECT parent_id , SUM(nodes) AS nodes , SUM(weight) AS weight
FROM tree GROUP BY parent_id) s
WHERE s.parent_id = t.id
;
在 SAS 中,这可能通过对 {0-parent_id, id} 进行排序并执行一些保留+求和魔术来完成。 (我的SAS在这方面有点生疏)
更新:如果只有叶节点有 non-NULL (non-missing) {nodes, weight} 的值,聚合可以在整个树的一次扫描中完成,而无需首先计算parent_id秒:
UPDATE tree t
SET nodes = s.nodes , weight = s.weight
FROM ( SELECT p.id , SUM(c.nodes) AS nodes , SUM(c.weight) AS weight
FROM tree p
JOIN tree c ON c.lev > p.lev AND POSITION (p.code IN c.code ) = 1
GROUP BY p.id
) s
WHERE s.id = t.id
;
{lev,code}
上的索引可能会加快速度。 (假设 id 上有一个索引)
看起来很简单。只需加入自己和 count/sum.
proc sql ;
create table want as
select a.id, a.level, a.code , a.var1, a.var2
, count(b.id) as nodes
, sum(b.weight) as weight
from have a
left join have b
on a.code eqt b.code
and b.level=10
group by 1,2,3,4,5
order by 1
;
quit;
如果您不想使用 EQT
运算符,则可以改用 SUBSTR() 函数。
on a.code = substr(b.code,1,a.level)
and b.level=10
既然您使用的是 SAS,那么使用 proc summary
来完成这里的繁重工作怎么样?不需要笛卡尔连接!
与其他一些选项相比,此选项的一个优点是,如果您想为多个变量计算大量更复杂的统计数据,则它更容易概括。
data have;
input ID level code : . nodes weight myIndex;
format myIndex 5.1;
cards;
1 1 1 . . .
2 2 11 . . .
3 3 111 . . .
4 4 1111 . . .
5 5 11111 . . .
6 6 111111 . . .
7 10 1111110000 1 0.1 105.5
8 10 1111119999 1 0.1 109.1
9 6 111112 . . .
10 10 1111129999 1 0.5 95.0
11 5 11119 . . .
12 6 111190 . . .
13 10 1111900000 1 0.1 80.7
14 10 1111901000 1 0.2 105.5
;
run;
data v_have /view = v_have;
set have(where = (level = 10));
array lvl[6] ;
do i = 1 to 6;
lvl[i]=substr(code,1,i);
end;
drop i;
run;
proc summary data = v_have;
class lvl1-lvl6;
var nodes weight;
var myIndex /weight = weight;
ways 1;
output out = summary(drop = _:) sum(nodes weight)= mean(myIndex)=;
run;
data v_summary /view = v_summary;
set summary;
length code ;
code = cats(of lvl:);
drop lvl:;
run;
data have;
modify have v_summary;
by code;
replace;
run;
理论上,哈希的哈希也可能是一种合适的数据结构,但为了获得相对较小的收益,这将是极其复杂的。无论如何我可能会去作为一个学习练习......
这是另一种哈希方法。
这不是使用散列对象进行笛卡尔连接,而是将节点和权重从每个级别 10 节点添加到 6 个适用的父节点中的每一个。这可能比 Quentin 的方法稍微快一点,因为没有冗余的哈希查找。
在构造散列对象时,它比 Quentin 的方法花费的时间长一点,并且使用更多的内存,因为每个终端节点使用不同的键添加 6 次,并且通常必须更新现有条目,但之后每个父节点节点只需要查找自己的个人统计数据,而不是循环遍历所有终端节点,这是一个很大的节省。
加权统计也是可能的,但你必须更新两个循环,而不仅仅是第二个循环。
data want;
if 0 then set have;
dcl hash h();
h.definekey('code');
h.definedata('nodes','weight','myIndex');
h.definedone();
length t_code ;
do until(eof);
set have(where = (level = 10)) end = eof;
t_nodes = nodes;
t_weight = weight;
t_myindex = weight * myIndex;
do _n_ = 1 to 6;
t_code = substr(code,1,_n_);
if h.find(key:t_code) ne 0 then h.add(key:t_code,data:t_nodes,data:t_weight,data:t_myIndex);
else do;
nodes + t_nodes;
weight + t_weight;
myIndex + t_myIndex;
h.replace(key:t_code,data:nodes,data:weight,data:MyIndex);
end;
end;
end;
do until(eof2);
set have end = eof2;
if level ne 10 then do;
h.find();
myIndex = round(MyIndex / Weight,0.1);
end;
output;
end;
drop t_:;
run;
我有一个带有分层编码表变量的数据集。 层级逻辑由LEVEL变量和CODE字符变量的前缀结构决定。 有6个(码长从1到6)"aggregate"级和终端级(码长10个字符)
我需要更新节点变量(终端节点的计数 - 聚合级别不计入 "higher" 聚合,仅计入终端节点) - 例如,一个级别的计数总和每个 5 级的总计数与每个 6 级的总计数相同。 我需要计算(总结)"higher" 级节点的权重。
注意:我偏移了输出 table 的 NODES 和 WEIGHT 变量,以便您可以更好地理解我在说什么(只需将每个偏移量中的数字相加即可得到相同的值)。
EDIT1:同一代码可以有多个观察结果。一个独特的观察结果是 3 个变量 code + var1 + var2 的组合。
输入table:
ID level code var1 var2 nodes weight myIndex
1 1 1 . . 999 999 999
2 2 11 . . 999 999 999
3 3 111 . . 999 999 999
4 4 1111 . . 999 999 999
5 5 11111 . . 999 999 999
6 6 111111 . . 999 999 999
7 10 1111119999 01 1 1 0.1 105,5
8 10 1111119999 01 2 1 0.1 109,1
9 6 111112 . . 999 999 999
10 10 1111120000 01 1 1 0.5 95,0
11 5 11119 . . 999 999 999
12 6 111190 . . 999 999 999
13 10 1111901000 01 1 1 0.1 80,7
14 10 1111901000 02 1 1 0.2 105,5
期望的输出table:
ID level code var1 var2 nodes weight myIndex
1 1 1 . . 5 1.0 98,1
2 2 11 . . 5 1.0 98,1
3 3 111 . . 5 1.0 98,1
4 4 1111 . . 5 1.0 98,1
5 5 11111 . . 3 0.7 98,5
6 6 111111 . . 2 0.2 107,3
7 10 1111119999 01 1 1 0.1 105,5
8 10 1111119999 01 2 1 0.1 109,1
9 6 111112 . . 1 0.5 95,0
10 10 1111120000 01 1 1 0.5 95,0
11 5 11119 . . 2 0.3 97,2
12 6 111190 . . 2 0.3 97,2
13 10 1111901000 01 1 1 0.1 80,7
14 10 1111901000 02 1 1 0.2 105,5
这是我想出的代码。它就像我想要的那样工作,但是伙计,它真的很慢。我需要更快的方法,因为这是网络服务的一部分,必须根据要求 运行 "instantly" 。 欢迎任何关于加速代码或任何其他解决方案的建议。
%macro doit;
data temporary;
set have;
run;
%do i=6 %to 2 %by -1;
%if &i = 6 %then %let x = 10;
%else %let x = (&i+1);
proc sql noprint;
select count(code)
into :cc trimmed
from have
where level = &i;
select code
into :id1 - :id&cc
from have
where level = &i;
quit;
%do j=1 %to &cc.;
%let idd = &&id&j;
proc sql;
update have t1
set nodes = (
select sum(nodes)
from temporary t2
where t2.level = &x and t2.code like ("&idd" || "%")),
set weight = (
select sum(weight)
from temporary t2
where t2.level = &x and t2.code like ("&idd" || "%"))
where (t1.level = &i and t1.code like "&idd");
quit;
%end;
%end;
%mend doit;
基于@Quentin 解决方案的当前代码:
data have;
input ID level code : . nodes weight myIndex;
cards;
1 1 1 . . .
2 2 11 . . .
3 3 111 . . .
4 4 1111 . . .
5 5 11111 . . .
6 6 111111 . . .
7 10 1111110000 1 0.1 105.5
8 10 1111119999 1 0.1 109.1
9 6 111112 . . .
10 10 1111129999 1 0.5 95.0
11 5 11119 . . .
12 6 111190 . . .
13 10 1111900000 1 0.1 80.7
14 10 1111901000 1 0.2 105.5
;
data want (drop=_:);
*hash table of terminal nodes;
if (_n_ = 1) then do;
if (0) then set have (rename=(code=_code weight=_weight));
declare hash h(dataset:'have(where=(level=10) rename=(code=_code weight=_weight myIndex=_myIndex))');
declare hiter iter('h');
h.definekey('ID');
h.definedata('_code','_weight','_myIndex');
h.definedone();
end;
set have;
*for each non-terminal node, iterate through;
*hash table of all terminal nodes, looking for children;
if level ne 10 then do;
call missing(weight, nodes, myIndex);
do _n_ = iter.first() by 0 while (_n_ = 0);
if trim(code) =: _code then do;
weight=sum(weight,_weight);
nodes=sum(nodes,1);
myIndex=sum(myIndex,_myIndex*_weight);
end;
_n_ = iter.next();
end;
myIndex=round(myIndex/weight,.1);
end;
output;
run;
一种方法(我认为)是制作笛卡尔积,并找到与每个节点 "match" 相关的所有终端节点,然后对权重求和。
类似于:
data have;
input ID level code : . nodes weight ;
cards;
1 1 1 . .
2 2 11 . .
3 3 111 . .
4 4 1111 . .
5 5 11111 . .
6 6 111111 . .
7 10 1111110000 1 0.1
8 10 1111119999 1 0.1
9 6 111112 . .
10 10 1111129999 1 0.5
11 5 11119 . .
12 6 111190 . .
13 10 1111900000 1 0.1
14 10 1111901000 1 0.2
;
proc sql;
select min(id) as id
, min(level) as level
, a.code
, count(b.weight) as nodes /*count of terminal nodes*/
, sum(b.weight) as weight /*sum of weights of terminal nodes*/
from
have as a
,(select code , weight
from have
where level=10 /*selects terminal nodes*/
) as b
where a.code eqt b.code /*EQT is equivalent to =: */
group by a.code
;
quit;
我不确定这是否正确,但它给出了示例数据所需的结果。
下面是一种蛮力哈希方法,用于执行与 SQL 中类似的笛卡尔积。加载终端节点的哈希 table。然后读取节点数据集,对于每个不是终端节点的节点,遍历散列table,识别所有子终端节点。
我认为@joop 描述的方法可能更有效,因为这种方法没有利用树结构。所以有很多重新计算。对于 5000 条记录和 3000 个终端节点,这将进行 2000*3000 次比较。但可能不会那么慢,因为哈希 table 在内存中,所以你不会有过多的 I/O ....
data want (drop=_:);
*hash table of terminal nodes;
if (_n_ = 1) then do;
if (0) then set have (rename=(code=_code weight=_weight));
declare hash h(dataset:'have(where=(level=10) rename=(code=_code weight=_weight))');
declare hiter iter('h');
h.definekey('ID');
h.definedata('_code','_weight');
h.definedone();
end;
set have;
*for each non-terminal node, iterate through;
*hash table of all terminal nodes, looking for children;
if level ne 10 then do;
call missing(weight, nodes);
do _n_ = iter.first() by 0 while (_n_ = 0);
if trim(code) =: _code then do;
weight=sum(weight,_weight);
nodes=sum(nodes,1);
end;
_n_ = iter.next();
end;
end;
output;
run;
这是估计每条记录的 parent 条记录所需的 SQL。它只使用字符串函数(位置和长度),所以它应该适应 table 到 SQL 的任何方言,甚至可能是 SAS。 (CTE 可能需要重写为子查询或视图)想法是:
- 向数据集添加一个 parent_id 字段
- 找出代码子串最长的记录
- 并使用它的 id 作为我们 parent_id 的值
- (在那之后)从 直接 children 的 sum(nodes),sum(weight) 更新记录(那些 child.parent_id = this.id )
顺便说一句:我本可以使用 LEVEL
而不是 LENGTH(code)
;这方面的数据有点冗余。
WITH sub AS (
SELECT id, length(code) AS len
, code
FROM tree)
UPDATE tree t
SET parent_id = s.id
FROM sub s
WHERE length(t.code) > s.len AND POSITION (s.code IN t.code) = 1
AND NOT EXISTS (
SELECT *
FROM sub nx
WHERE nx.len > s.len AND POSITION (nx.code IN t.code ) = 1
AND nx.len < length(t.code) AND POSITION (nx.code IN t.code ) = 1
)
;
SELECT * FROM tree
ORDER BY parent_id DESC NULLS LAST
, id
;
找到 parent 之后,整个 table 应该从自身更新(重复) 喜欢:
-- PREPARE omg( integer) AS
UPDATE tree t
SET nodes = s.nodes , weight = s.weight
FROM ( SELECT parent_id , SUM(nodes) AS nodes , SUM(weight) AS weight
FROM tree GROUP BY parent_id) s
WHERE s.parent_id = t.id
;
在 SAS 中,这可能通过对 {0-parent_id, id} 进行排序并执行一些保留+求和魔术来完成。 (我的SAS在这方面有点生疏)
更新:如果只有叶节点有 non-NULL (non-missing) {nodes, weight} 的值,聚合可以在整个树的一次扫描中完成,而无需首先计算parent_id秒:
UPDATE tree t
SET nodes = s.nodes , weight = s.weight
FROM ( SELECT p.id , SUM(c.nodes) AS nodes , SUM(c.weight) AS weight
FROM tree p
JOIN tree c ON c.lev > p.lev AND POSITION (p.code IN c.code ) = 1
GROUP BY p.id
) s
WHERE s.id = t.id
;
{lev,code}
上的索引可能会加快速度。 (假设 id 上有一个索引)
看起来很简单。只需加入自己和 count/sum.
proc sql ;
create table want as
select a.id, a.level, a.code , a.var1, a.var2
, count(b.id) as nodes
, sum(b.weight) as weight
from have a
left join have b
on a.code eqt b.code
and b.level=10
group by 1,2,3,4,5
order by 1
;
quit;
如果您不想使用 EQT
运算符,则可以改用 SUBSTR() 函数。
on a.code = substr(b.code,1,a.level)
and b.level=10
既然您使用的是 SAS,那么使用 proc summary
来完成这里的繁重工作怎么样?不需要笛卡尔连接!
与其他一些选项相比,此选项的一个优点是,如果您想为多个变量计算大量更复杂的统计数据,则它更容易概括。
data have;
input ID level code : . nodes weight myIndex;
format myIndex 5.1;
cards;
1 1 1 . . .
2 2 11 . . .
3 3 111 . . .
4 4 1111 . . .
5 5 11111 . . .
6 6 111111 . . .
7 10 1111110000 1 0.1 105.5
8 10 1111119999 1 0.1 109.1
9 6 111112 . . .
10 10 1111129999 1 0.5 95.0
11 5 11119 . . .
12 6 111190 . . .
13 10 1111900000 1 0.1 80.7
14 10 1111901000 1 0.2 105.5
;
run;
data v_have /view = v_have;
set have(where = (level = 10));
array lvl[6] ;
do i = 1 to 6;
lvl[i]=substr(code,1,i);
end;
drop i;
run;
proc summary data = v_have;
class lvl1-lvl6;
var nodes weight;
var myIndex /weight = weight;
ways 1;
output out = summary(drop = _:) sum(nodes weight)= mean(myIndex)=;
run;
data v_summary /view = v_summary;
set summary;
length code ;
code = cats(of lvl:);
drop lvl:;
run;
data have;
modify have v_summary;
by code;
replace;
run;
理论上,哈希的哈希也可能是一种合适的数据结构,但为了获得相对较小的收益,这将是极其复杂的。无论如何我可能会去作为一个学习练习......
这是另一种哈希方法。
这不是使用散列对象进行笛卡尔连接,而是将节点和权重从每个级别 10 节点添加到 6 个适用的父节点中的每一个。这可能比 Quentin 的方法稍微快一点,因为没有冗余的哈希查找。
在构造散列对象时,它比 Quentin 的方法花费的时间长一点,并且使用更多的内存,因为每个终端节点使用不同的键添加 6 次,并且通常必须更新现有条目,但之后每个父节点节点只需要查找自己的个人统计数据,而不是循环遍历所有终端节点,这是一个很大的节省。
加权统计也是可能的,但你必须更新两个循环,而不仅仅是第二个循环。
data want;
if 0 then set have;
dcl hash h();
h.definekey('code');
h.definedata('nodes','weight','myIndex');
h.definedone();
length t_code ;
do until(eof);
set have(where = (level = 10)) end = eof;
t_nodes = nodes;
t_weight = weight;
t_myindex = weight * myIndex;
do _n_ = 1 to 6;
t_code = substr(code,1,_n_);
if h.find(key:t_code) ne 0 then h.add(key:t_code,data:t_nodes,data:t_weight,data:t_myIndex);
else do;
nodes + t_nodes;
weight + t_weight;
myIndex + t_myIndex;
h.replace(key:t_code,data:nodes,data:weight,data:MyIndex);
end;
end;
end;
do until(eof2);
set have end = eof2;
if level ne 10 then do;
h.find();
myIndex = round(MyIndex / Weight,0.1);
end;
output;
end;
drop t_:;
run;