是否可以使用目标行初始化Spark UDAF?

时间:2018-10-10 00:11:31

标签: python scala pandas apache-spark user-defined-functions

我有一个问题,我正在尝试通过模仿herehere给出的建议来定义自己的UDAF,从而在Spark中解决。我最终的目标是对给定窗口内的整数序列应用一系列复杂的位移位和按位布尔操作。

我遇到了问题,因为用例位于相当大的数据集(约1亿行,为此,我需要对2-7个元素长的组执行6种此类按位操作),因此我正在尝试在scala中实现。问题是我是scala的新手(我的主要语言是python),虽然scala本身似乎并不那么困难,但将新语言与UDAF类本身的详细信息结合在一起应用于{{1} } s让我有些困惑。

在python / pandas中通过示例解释逻辑

要使问题更具体,请考虑 window pandas

DataFrame

哪个会产生:

keep = list(range(30))
for num in (3, 5, 11, 16, 22, 24):
    keep.pop(num)
np.random.seed(100)
df = pd.DataFrame({
    'id': 'A',
    'date': pd.date_range('2018-06-01', '2018-06-30')[keep],
    'num': np.random.randint(low=1, high=100, size=30)[keep]
})

我想做的是,相对于当前行,找到天数,然后根据该值执行一些按位操作。为了演示,留在熊猫里(我必须做一个完整的外部联接然后过滤来演示):

   id       date  num
0   A 2018-06-01    9
1   A 2018-06-02   25
2   A 2018-06-03   68
3   A 2018-06-05   80
4   A 2018-06-06   49
5   A 2018-06-08   95
6   A 2018-06-09   53
7   A 2018-06-10   99
8   A 2018-06-11   54
9   A 2018-06-12   67
10  A 2018-06-13   99
11  A 2018-06-15   35
12  A 2018-06-16   25
13  A 2018-06-17   16
14  A 2018-06-18   61
15  A 2018-06-19   59
16  A 2018-06-21   10
17  A 2018-06-22   94
18  A 2018-06-23   87
19  A 2018-06-24    3
20  A 2018-06-25   28
21  A 2018-06-26    5
22  A 2018-06-28    2
23  A 2018-06-29   14

现在我执行按位移位和其他逻辑:

exp_df = df[['id', 'date']].merge(df, on='id') \ # full outer join on 'id'
                           .assign(days_diff = lambda df: (df['date_y'] - df['date_x']).dt.days) \ # number of days since my date of interest
                           .mask(lambda df: (df['days_diff'] > 3) | (df['days_diff'] < 0)) \ # nulls rows where days_diff isn't between 0 and 3
                           .dropna() \ # then filters the rows
                           .drop('date_y', axis='columns') \
                           .rename({'date_x': 'date', 'num': 'nums'}, axis='columns') \
                           .reset_index(drop=True)
exp_df[['nums', 'days_diff']] = exp_df[['nums', 'days_diff']].astype('int')

所有这些之后,# Extra values to add after bit-wise shifting (1 for shift of 1, 3 for shift of 2 ...) additions = {val: sum(2**power for power in range(val)) for val in exp_df['days_diff'].unique()} exp_df['shifted'] = np.left_shift(exp_df['nums'].values, exp_df['days_diff'].values) \ + exp_df['days_diff'].apply(lambda val: additions[val]) 如下所示(前10行):

exp_df

现在我可以汇总:

  id       date  nums  days_diff  shifted
0  A 2018-06-01     9          0        9
1  A 2018-06-01    25          1       51
2  A 2018-06-01    68          2      275
3  A 2018-06-02    25          0       25
4  A 2018-06-02    68          1      137
5  A 2018-06-02    80          3      647
6  A 2018-06-03    68          0       68
7  A 2018-06-03    80          2      323
8  A 2018-06-03    49          3      399
9  A 2018-06-05    80          0       80

最终结果如下所示(如果我重新加入原始的exp_df.groupby('date')['shifted'].agg(lambda group_vals: np.bitwise_and.reduce(group_vals.values)

DataFrame

回到问题

好吧,既然我已经展示了我的逻辑,我意识到我基本上可以在Spark中做同样的事情-对DataFrame本身进行完全外部联接,然后进行过滤和聚合。

我想知道的是,我是否可以避免执行完全连接,而是使用目标行作为输入,创建自己的UDAF以通过窗口函数执行此聚合。基本上,我需要创建与 id date num shifted 0 A 2018-06-01 9 1 1 A 2018-06-02 25 1 2 A 2018-06-03 68 0 3 A 2018-06-05 80 64 4 A 2018-06-06 49 33 5 A 2018-06-08 95 3 6 A 2018-06-09 53 1 7 A 2018-06-10 99 1 8 A 2018-06-11 54 6 9 A 2018-06-12 67 3 10 A 2018-06-13 99 3 11 A 2018-06-15 35 3 12 A 2018-06-16 25 1 13 A 2018-06-17 16 0 14 A 2018-06-18 61 21 15 A 2018-06-19 59 35 16 A 2018-06-21 10 8 17 A 2018-06-22 94 6 18 A 2018-06-23 87 3 19 A 2018-06-24 3 1 20 A 2018-06-25 28 0 21 A 2018-06-26 5 1 22 A 2018-06-28 2 0 23 A 2018-06-29 14 14 列等效的内容以执行所需的逻辑,这意味着将目标日期与指定窗口中的其他每个日期进行比较。这有可能吗?

此外,我是否有理由担心使用自连接?我知道spark会延迟执行所有处理,因此很有可能我不必担心。如果我使用自连接进行所有这些操作,而不是将我想象的UDAF应用于窗口,那么我是否应该期望性能类似?使用join-filter-aggregate方法的逻辑更加连续,更易于遵循,这是一个明显的优势。

要知道的一件事是,我将在多个窗口上执行此逻辑。原则上,我可以在联接后"days_diff" {em>最大版本的过滤后的DataFrame,然后将其用于后续计算。

0 个答案:

没有答案