Hash SAS哈希合并--较小的数据集作为哈希对象

Hash SAS哈希合并--较小的数据集作为哈希对象,hash,merge,sas,hashtable,Hash,Merge,Sas,Hashtable,我正在使用位于和以下示例数据集的%HASHMERGE宏: data working; length IID TYPE $12; input IID $ TYPE $; datalines; B 0 B 0 A 1 A 1 A 1 C 2 D 3 ; run; data master; length IID FIRST_NAME MIDDLE_NAME LAST_NAME

我正在使用位于和以下示例数据集的
%HASHMERGE
宏:

data working;
    length IID TYPE $12;
    input IID $ TYPE $;
    datalines;
    B   0
    B   0
    A   1
    A   1
    A   1
    C   2
    D   3
    ;
run;

data master;
    length IID FIRST_NAME MIDDLE_NAME LAST_NAME SUFFIX_NAME $12;
    input IID $ FIRST_NAME $ MIDDLE_NAME $ LAST_NAME $ SUFFIX_NAME;
    datalines;
    X   John James Smith Sr
    Z   Sarah Marie Jones .
    Y   Tim William Miller Jr
    C   Nancy Lynn  Brown .
    B   Carol Elizabeth Collins     .
    A   Wayne   Mark    Rooney .
    ;
run;
工作
数据集上,我试图使用此哈希合并从
数据集中附加
\u NAME
变量。输出看起来很好,是所需的输出。但是,在我的实际场景中,
主数据集太大,无法放入哈希对象中,宏一直将其作为哈希对象放置。我最终希望将这两个数据集翻转到
working
数据集是散列对象的位置,但翻转代码时无法获得所需的输出。下面是宏中产生所需输出并需要调整的部分,但我不确定如何设置:

data OUTPUT;
    if 0 then set MASTER (keep=IID FIRST_NAME MIDDLE_NAME LAST_NAME SUFFIX_NAME)
    WORKING (keep=IID);
    declare hash h_merge(dataset:"MASTER"); /* I want WORKING to be the hash object since it's smaller! */
    rc=h_merge.DefineKey("IID");
    rc=h_merge.DefineData("FIRST_NAME","MIDDLE_NAME","LAST_NAME","SUFFIX_NAME");
    rc=h_merge.DefineDone();
    do while(not eof);
        set WORKING (keep=IID) end=eof;
        call missing(FIRST_NAME,MIDDLE_NAME,LAST_NAME,SUFFIX_NAME);
        rc=h_merge.find();
        output;
        end;
    drop rc;
    stop;
run;
期望输出:

IID FIRST_NAME  MIDDLE_NAME  LAST_NAME  SUFFIX_NAME
---------------------------------------------------
B    Carol      Elizabeth    Collins    
B    Carol      Elizabeth    Collins    
A    Wayne      Mark         Rooney 
A    Wayne      Mark         Rooney 
A    Wayne      Mark         Rooney 
C    Nancy      Lynn         Brown  
D                   

虽然按照您所说的去做是可行的,但我怀疑您是否能从一个非专门构建的宏中得到这一点。那是因为这不是正常的方法;通常,您希望保持主数据集的形式,并将关系数据集放入哈希表中。当然,通常大小是相反的——关系表通常比主表小

就我个人而言,我不会在这个特殊情况下使用哈希。我会使用一种(或三种)格式。它的速度和散列一样快,大小问题也较少(因为它不需要放入内存),但最终会因为大小的原因而变慢(但不会中断!)

格式解决方案:

data working;
    length IID TYPE $12;
    input IID $ TYPE $;
    datalines;
    B   0
    B   0
    A   1
    A   1
    A   1
    C   2
    D   3
    ;
run;

data master;
    length IID FIRST_NAME MIDDLE_NAME LAST_NAME SUFFIX_NAME $12;
    input IID $ FIRST_NAME $ MIDDLE_NAME $ LAST_NAME $ SUFFIX_NAME;
    datalines;
    X   John James Smith Sr
    Z   Sarah Marie Jones .
    Y   Tim William Miller Jr
    C   Nancy Lynn  Brown .
    B   Carol Elizabeth Collins     .
    A   Wayne   Mark    Rooney .
    ;
run;

data for_fmt;
  set master;
  retain type 'char';
  length fmtname $32 
         label $255
         start $255
         ;
  start=iid;

  *first;
  label=first_name;
  fmtname='$FIRSTNAMEF';
  output;

  *last;
  label=last_name;
  fmtname='$LASTNAMEF';
  output;

  *middle;
  label=middle_name;
  fmtname='$MIDNAMEF';
  output;

  *suffix;
  label=suffix_name;
  fmtname='$SUFFNAMEF';
  output;

  if _n_=1 then do;
    start=' ';
    label=' ';
    hlo='o';
    fmtname='$FIRSTNAMEF';
    output;
    fmtname='$LASTNAMEF';
    output;
    fmtname='$MIDNAMEF';
    output;
    fmtname='$SUFFNAMEF';
    output;
  end;
run;

proc sort data=for_fmt;
  by fmtname start;
run;

proc format cntlin=for_fmt;
quit;

data want;
  set working;
  first_name = put(iid,$FIRSTNAMEF.);
  last_name  = put(iid,$LASTNAMEF.);
  middle_name = put(iid,$MIDNAMEF.);
  suffix_name = put(iid,$SUFFNAMEF.);

run;
也就是说

如果您确实希望在哈希表中执行此操作,那么您需要做的是,对于MASTER中的每一行,在工作表中执行查找,然后如果成功执行替换,则查找并替换下一行,直到失败


问题出在哪里?每个主行至少有一个查找,您自己注意到它非常大。如果
WORKING
为100k,而
MASTER
为100M,则每个匹配项将执行1000次查找。这非常昂贵,可能意味着您最好使用其他解决方案。

虽然按照您所说的做是可行的,但我怀疑您能否从非专用构建的宏中获得这一点。那是因为这不是正常的方法;通常,您希望保持主数据集的形式,并将关系数据集放入哈希表中。当然,通常大小是相反的——关系表通常比主表小

就我个人而言,我不会在这个特殊情况下使用哈希。我会使用一种(或三种)格式。它的速度和散列一样快,大小问题也较少(因为它不需要放入内存),但最终会因为大小的原因而变慢(但不会中断!)

格式解决方案:

data working;
    length IID TYPE $12;
    input IID $ TYPE $;
    datalines;
    B   0
    B   0
    A   1
    A   1
    A   1
    C   2
    D   3
    ;
run;

data master;
    length IID FIRST_NAME MIDDLE_NAME LAST_NAME SUFFIX_NAME $12;
    input IID $ FIRST_NAME $ MIDDLE_NAME $ LAST_NAME $ SUFFIX_NAME;
    datalines;
    X   John James Smith Sr
    Z   Sarah Marie Jones .
    Y   Tim William Miller Jr
    C   Nancy Lynn  Brown .
    B   Carol Elizabeth Collins     .
    A   Wayne   Mark    Rooney .
    ;
run;

data for_fmt;
  set master;
  retain type 'char';
  length fmtname $32 
         label $255
         start $255
         ;
  start=iid;

  *first;
  label=first_name;
  fmtname='$FIRSTNAMEF';
  output;

  *last;
  label=last_name;
  fmtname='$LASTNAMEF';
  output;

  *middle;
  label=middle_name;
  fmtname='$MIDNAMEF';
  output;

  *suffix;
  label=suffix_name;
  fmtname='$SUFFNAMEF';
  output;

  if _n_=1 then do;
    start=' ';
    label=' ';
    hlo='o';
    fmtname='$FIRSTNAMEF';
    output;
    fmtname='$LASTNAMEF';
    output;
    fmtname='$MIDNAMEF';
    output;
    fmtname='$SUFFNAMEF';
    output;
  end;
run;

proc sort data=for_fmt;
  by fmtname start;
run;

proc format cntlin=for_fmt;
quit;

data want;
  set working;
  first_name = put(iid,$FIRSTNAMEF.);
  last_name  = put(iid,$LASTNAMEF.);
  middle_name = put(iid,$MIDNAMEF.);
  suffix_name = put(iid,$SUFFNAMEF.);

run;
也就是说

如果您确实希望在哈希表中执行此操作,那么您需要做的是,对于MASTER中的每一行,在工作表中执行查找,然后如果成功执行替换,则查找并替换下一行,直到失败


问题出在哪里?每个主行至少有一个查找,您自己注意到它非常大。如果
WORKING
为100k,而
MASTER
为100M,则每个匹配项将执行1000次查找。这非常昂贵,而且可能意味着你最好使用其他解决方案。

一如既往地感谢你的建议,@Joe。这很有意义,因为没有太多关于我使用哈希表的场景的文档。我将尝试一下格式化方法<代码>工作
有22毫米的行,
MASTER
有350毫米左右的行——这是一件讨厌的事情,但必须完成。我绝对不会用散列来完成它——找到另一个解决方案。也许把MASTER分解成几组,然后把它们散列。一份杂烩可以装多大尺寸?你有多少记性?获取更多内存或更小的数据集-例如,如果你可以在一个哈希中放入100毫米,那么四个哈希可能就足够了,特别是如果你可以智能地拆分它(例如索引),这样你只能从工作中加载5毫米的行,而不是每次从MASTER中加载22/100的正确的100毫米行)。这很有意义,因为没有太多关于我使用哈希表的场景的文档。我将尝试一下格式化方法<代码>工作有22毫米的行,
MASTER
有350毫米左右的行——这是一件讨厌的事情,但必须完成。我绝对不会用散列来完成它——找到另一个解决方案。也许把MASTER分解成几组,然后把它们散列。一份杂烩可以装多大尺寸?你有多少记性?获取更多内存或更小的数据集-例如,如果可以在一个散列中容纳100MM,则四个散列可能就足够了,尤其是如果可以智能地拆分它(例如索引),因此只能从工作中加载5MM行,从主数据集加载正确的100MM行,您可以使用工作数据集的IID过滤主数据集,然后将过滤后的主数据集与工作数据集合并,这样两个数据集都很小,很容易处理。我尝试了
SQL左连接
filter,它比读取主数据集、排序和合并
工作(in=a)花费的时间要长得多如果是
,则使用控制;创建表格New_master as select*from master(keep=keep=IID FIRST_NAME MIDDLE_NAME LAST_NAME SUFFIX_NAME),IID在其中(选择与工作不同的IID(keep=IID));退出您可以使用工作数据集的IID过滤主数据集,然后将过滤后的主数据集与工作数据集合并,这样两个数据集都很小,很容易处理。我尝试了
SQL左连接
filter,它比读取主数据集、排序和合并
工作(in=a)花费的时间要长得多如果是
,则使用控制;创建表格New_master as select*from master(keep=keep=IID FIRST_NAME MIDDLE_NAME LAST_NAME SUFFIX_NAME),IID在其中(选择与工作不同的IID(keep=IID));退出