用匹配平面重复填充3D numpy阵列

dfddblmv  于 2023-03-18  发布在  其他
关注(0)|答案(2)|浏览(104)

我有两个大尺寸(~1.2b elements, e.g. 80k x 500 x 30)的3D numpy数组。这两个数组在2维上具有相同的大小,但在与时间戳相关的第三维上不同。我还有两个数组,其中包含与沿着大小不同轴的平面对应的时间戳值。我想拉伸/广播并使用差距之前自身包含值的最后一个平面填充较短的数组,以便达到与较大数组相同的大小,这样我就可以将它们相加。“间隙”和“最后平面”由时间戳数组与数据数组的匹配方式决定。
以下是所示逻辑(黑色字体,深色模式版本见下文):

数组之间的时间戳关系

沿着数组大小不同的轴,时间戳数组中的对应值是日期。但是,这些时间戳的粒度可以不同。例如,与较大数组相关的时间戳可以是一系列连续的天,而与较短数组相关的时间戳可以是同一时段内的每个星期五,但结束时间短或开始时间晚。即,当与较大数组的开始和结束比较时,错过前5个星期五或最后10个星期五。
当有这样的“间隙”时,我希望(临时地说)三维阵列的最后一个可用平面在该间隙上伸展,直到下一个可用平面有值。
注意,Fridays的例子只是一种情况,也可以是较大的数组对应于某个时间段内的每个星期一,而较短的数组是该时间段内每个月的第一天,但结束时间较短或开始时间较晚。
另一个重要的注意事项可能是,与时间戳相关的3D数组的轴在所有轴中具有最小的大小。这意味着,例如,数组1可能是(80 k,500,180),数组2可能是(80 k,500,50)。因此,在该轴上循环,然后重复大小为(80 k,500,1)的平面可能有效。但我不知道如何操作。
此外,较短的数组完全包含在较大的数组中。

代码示例

假设有两个数组A和B

m, k, n, v = 3, 4, 10, 4

A = np.random.randint(10, size=(m, n, k))
B = np.random.randint(10, size=(m, v, k))

A_timestamps = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
B_timestamps = [2, 4, 7, 8]

然而,尽管B_timestamps被完全包含,但其最低时间戳大于A中的最低时间戳。换句话说,B开始得晚。
然后用这种方法重新索引

equals = np.equal.outer(A_timestamps, B_timestamps)
filled = np.maximum.accumulate(equals, axis=0)
reindex = len(B_timestamps) - np.argmax(filled[:, ::-1], axis=1) - 1

导致

# array([4, 1, 1, 2, 2, 2, 3, 4, 4, 4], dtype=int64)

从4开始。
理想情况下,当重新索引数组B时,我希望前面差距(由于B开始晚)用零填充。
我想我可以先检查第一个匹配的时间戳,从那里开始只重新索引B,然后可能使用np.pad在B的前面添加零,以弥补A中被排除的时间戳。
对于暗模式用户,使用白色字体重复图像:

8wigbo56

8wigbo561#

这种方法似乎比@Chrysophylaxs answer性能更高,对于更大的输入可能是2倍。
首先找出B_timestamps适合A_timestamps的索引,这些索引之间的间隔表示B的每个元素应该被使用的次数:

repeats = np.diff(np.searchsorted(A_timestamps, B_timestamps), append=A_timestamps[-1])
B_expanded = np.repeat(B, repeats, axis=1)

对于使用0的左填充:

left_pad_width = A.shape[1] - B_expanded.shape[1]
B_expanded = np.pad(
    B_expanded,
    pad_width=((0, 0), (left_pad_width, 0), (0, 0)),
    constant_values=0,
)
vnzz0bqm

vnzz0bqm2#

我认为这在很大程度上取决于时间戳的外观,主要的技巧是为第二个数组生成一个索引数组,该数组的长度与第一个数组中可变大小轴的长度相同,为了说明这一点,我们假设您有数组AB,如下所示:

m, k, n, v = 3, 4, 10, 5

A = np.random.randint(10, size=(m, n, k))
B = np.random.randint(10, size=(m, v, k))

A_timestamps = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
B_timestamps = [1, 2, 4, 7, 8]

您想要为B重新建立索引的数组为:

reindex = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4] # <<< Index taken from B
#          0, 1, 2, 3, 4, 5, 6, 7, 8, 9    <<< Corresponding "aligned" index in A

这样您就可以简单地编写:

out = A + B[:, reindex, :]

那么创建重建索引数组的最佳方法是什么呢?我不知道...下面是我的想法,假设时间戳是唯一的并且经过排序:

equals = np.equal.outer(A_timestamps, B_timestamps)
filled = np.maximum.accumulate(equals, axis=0)
reindex = len(B_timestamps) - np.argmax(filled[:, ::-1], axis=1) - 1
# array([0, 1, 1, 2, 2, 2, 3, 4, 4, 4], dtype=int64)

相关问题