【问题标题】:Fuzzy join without proc SQL没有 proc SQL 的模糊连接
【发布时间】:2018-02-05 15:27:13
【问题描述】:

早安,

我希望将两个日期合并到下一个最接近的日期

数据集从 500Mb 到 1G 都是巨大的,所以proc sql 是不可能的。

我有两个数据集。第一个(舰队)有观察结果,第二个有日期和用于进一步处理的代号。像这样:

data Fleet
    CreatedPortalDate 
    2013/2/19 
    2013/8/22 
    2013/8/25 
    2013/10/01 
    2013/10/07 

data gennum_list
    date 
    01/12/2014 
    08/12/2014 
    15/12/2014 
    22/12/2014 
    29/12/2014 
    ...

我想要的是这样的链接表:

data link_table
    CreatedPortalDate date 
    14-12-03  01/12/2014 
    14-12-06  01/12/2014 
    14-12-09  08/12/2014 
    14-12-11  08/12/2014 
    14-12-14  08/12/2014 

有逻辑的

Date < CreatedPortalDate and (CreatedPortalDate - date) = min(CreatedPortalDate - date)

我想出的东西有点笨拙,我正在寻找一种有效/更好的方法来实现这一点。

data all_comb; 
  set devFleet(keep=createdportaldate);
  do i=1 to n;
    set gennum_list(keep=date) point=i nobs=n;
    if createdportaldate > date 
        and createdportaldate - 15 < date then do;/*Assumption, the generations are created weekly.*/
           distance= createdportaldate - date; 
          output; 
        end;
  end;
run;

proc sort data=all_comb; by createdportaldate distance; run;

data link_table; 
    set _all_comb(drop=distance); 
    by createdportaldate; 
    if first.createdportaldate; 
run;
  • 任何想法如何改进或解决这个问题?
  • 无知的想法:我可以创建存储distance 的哈希表吗?
  • 可能是数组?以某种方式。

编辑:

  • 通用格式
    • 完成
  • 十亿行从何而来?
    • 是的,还涉及其他数据,但日期是唯一的链接变量。
  • 排序了吗?
    • 是的,数据已排序,可以再次排序。
  • gen num 日期总是相隔 7 天吗?
    • 没有。这是棘手的部分。否则我可以使用weekyear(或其他分箱)作为唯一标识符。

【问题讨论】:

  • 请编辑,以便显示的日期都是通用格式,例如 yyyy-mm-dd。您仅在示例数据中列出唯一的日期值。 100 年只有约 36,500 天。十亿行从何而来?是否涉及其他列,例如帐户 ID?显示的样本数据已排序。真实数据是否排序? gen num 日期总是相隔 7 天吗?
  • @Richard 更新了问题。

标签: merge sas fuzzyjoin


【解决方案1】:

巨大是一个相对术语,今天的巨大就是明天的斑点。

关键数据特征表明直接寻址查找方案是可能的

  • 日期值是整数。
  • 日期值范围有限。
  • 日期值或未来 14 天中的任何一天都将用作查找验证器
  • key为日期值,可作为数组索引。

按如下方式加载一次 Gennum 查找

array gennum_of ( %sysfunc(today()) ) _temporary_;
if last_date then
  do index = last_date to date-1;
    gennum_of(index) = prev_date;
  end;

last_date = date;

然后获取一个 gennum 为

if portaldate > last_date
  then portal_gennum = last_date;
  else portal_gennum = gennum_of ( portaldate );

如果由于按帐户 ID 分组而有很多行,则必须清除并加载每个组的 gennum 数组。

【讨论】:

    【解决方案2】:

    这是 sas by 语句的典型应用。

    data 步骤中的by 语句旨在一次读取两个或多个按公共变量排序的数据集。

    通用变量是日期,但在两个数据集上的名称不同。在sql 中,您通过要求一个变量与另一个Fleet.CreatedPortalDate = gennum_list.date 相等来解决这个问题,但是by 语句不允许这样的构造,因此我们必须在读取数据集时(至少)重命名其中一个.这就是我们在gennum_list 选项中的rename 子句中所做的事情

    data all_comb;
        merge gennum_list (in = in_gennum rename = (date = CreatedPortalDate))
              Fleet (in = in_fleet);
        by CreatedPortalDate;
    

    我选择将by 语句与merge 语句结合起来,虽然set 也可以完成这项工作,但是两个输入数据集的顺序会有所不同。

    还请注意,我要求 sas 创建指示变量 in_gennumin_fleet,它们指示值存在于哪个输入数据集中。知道这种类型的变量 id 没有写入结果数据集是很方便的。

    当然,我们必须从CreatedPortalDate 中恢复date

        if in_gennum then date = CreatedPortalDate;
    

    如果您是 sas 新手,您会惊讶于上述语句不起作用除非您明确指示 sas retain 从一次观察到嵌套的日期值。 (观察是行的 sas 行话。)

        retain date;
    

    在这里,我们为从Fleet 数据集中读取的每个观察写入一个观察。

        if in_fleet then output;
    run;
    

    这种方法的优点是

    • 您需要更少的逻辑来正确组合来自两个输入数据集的观察结果(这就是发明 data 步骤的目的)
    • 您不必在内存中保留一组值,因此不会出现溢出问题
    • 此解决方案在数据集的大小(除了排序之外)中是 1 阶 (O1),因此我们预先知道将数据量加倍只会将时间加倍。

      免责声明:此答案正在建设中。

    它将在本周晚些时候进行测试

    【讨论】:

    • 感谢您迄今为止的意见。期待更多。但是,我不确定当两个日期(createddate)不匹配时这是否有效。还是我错过了什么?
    • 您可能缺少retain 语句的功能。
    猜你喜欢
    • 2017-01-08
    • 1970-01-01
    • 2012-12-30
    • 2016-10-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多