当前位置: 首页 > 面试题库 >

将Python序列(时间序列/数组)拆分为具有重叠的子序列

施鸿
2023-03-14
问题内容

我需要提取给定窗口的时间序列/数组的所有子序列。例如:

>>> ts = pd.Series([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> window = 3
>>> subsequences(ts, window)
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6],
       [5, 6, 7],
       [5, 7, 8],
       [6, 8, 9]])

迭代序列的简单方法当然很昂贵,例如:

def subsequences(ts, window):
    res = []
    for i in range(ts.size - window + 1):
        subts = ts[i:i+window]
        subts.reset_index(drop=True, inplace=True)
        subts.name = None
        res.append(subts)
    return pd.DataFrame(res)

我发现了一种更好的方法,即复制序列,将其移动一个不同的值直到覆盖窗口,然后使用分割不同的序列reshape性能大约提高了100倍,因为for循环遍历窗口大小而不是序列大小:

def subsequences(ts, window):
    res = []
    for i in range(window):
        subts = ts.shift(-i)[:-(ts.size%window)].reshape((ts.size // window, window))
        res.append(subts)
    return pd.DataFrame(np.concatenate(res, axis=0))

我已经看到pandas在pandas.stats.moment模块中包含多个滚动功能,我想它们的作用在某种程度上类似于子序列问题。该模块中是否有其他地方,或者熊猫中是否有其他地方可以提高效率?

谢谢!

更新(解决方案):

基于@elyase答案,对于这种特定情况,有一个稍微简单的实现,让我在这里写下来,并解释它的作用:

def subsequences(ts, window):
    shape = (ts.size - window + 1, window)
    strides = ts.strides * 2
    return np.lib.stride_tricks.as_strided(ts, shape=shape, strides=strides)

给定一维numpy数组,我们首先计算所得数组的形状。除了数组中的最后几个元素外,我们将从数组的每个位置开始一行,在数组的最后几个元素旁边没有足够的元素来完成窗口。

请参见本说明中的第一个示例,我们从的最后一个数字如何为6,因为从7开始,我们无法创建包含三个元素的窗口。因此,行数是大小减去窗口再加上一。列数就是窗口。

接下来,棘手的部分是告诉我们如何使用刚刚定义的形状填充结果数组。

为此,我们认为第一个元素将是第一个。然后,我们需要指定两个值(两个整数的元组作为参数的参数strides)。这些值指定我们需要在原始数组(一维数组)中填充第二个(二维数组)的步骤。

考虑一个不同的示例,np.reshape从9个元素的1维数组到3x3数组,我们要实现该功能。第一个元素填充第一个位置,然后其右边的一个元素将是1-D数组中的下一个元素,因此我们移动
1 step 。然后,棘手的部分要填充第二行的第一个元素,我们应该执行3步,从0到4,请参见:

>>> original = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> new = array([[0, 1, 2],
                 [3, 4, 5],
                 [6, 7, 8])]

因此,reshape对于这两个维度,我们的步骤将是(1, 3)。对于我们来说,它存在重叠,实际上更简单。当我们向右移动以填充结果数组时,我们从1-D数组中的下一个位置开始,而当我们向右移动时,我们再次获取1-D数组中的下一个元素,即1步。因此,步骤将是(1, 1)

最后只有一件事要注意。该strides参数不接受我们使用的“步骤”,而是接受内存中的字节。要了解它们,我们可以使用stridesnumpy数组的方法。它返回一个带有跨步(以字节为单位的步幅)的元组,每个维都有一个元素。在我们的例子中,我们得到一个1元素元组,并且我们希望它两次,所以我们有了* 2

np.lib.stride_tricks.as_strided函数使用描述的方法执行填充, 而无需 复制数据,这使其效率很高。

最后,请注意,此处发布的函数假定使用一维输入数组(与具有1个元素作为行或列的二维数组不同)。参见输入数组的shape方法,您应该得到类似(N, )和not的信息(N, 1)。这种方法在后者上将失败。请注意,@ elyase发布的方法处理二维输入数组(这就是为什么此版本稍微简单一些的原因)。


问题答案:

这比您的机器上的快速版本快34倍:

def rolling_window(a, window):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

>>> rolling_window(ts.values, 3)
array([[0, 1, 2],
      [1, 2, 3],
      [2, 3, 4],
      [3, 4, 5],
      [4, 5, 6],
      [5, 6, 7],
      [6, 7, 8],
      [7, 8, 9]])

幸得埃里克Rigtorp。



 类似资料:
  • 问题内容: 这个问题类似于将 列表切成子列表的列表 ,但是在我的情况下,我想包括每个先前子列表的最后一个元素,作为下一个子列表的第一个元素。并且必须考虑到最后一个元素必须始终至少包含两个元素。 例如: 大小为3的子列表的结果: 问题答案: 通过简单地缩短传递到范围的“ step”参数,可以轻松地将链接的答案中的列表理解用于支持重叠的块: 这个问题的其他访问者可能没有足够的精力来处理输入 列表 (可

  • 主要内容:创建时间戳,创建时间范围,更改时间频率,转化为时间戳,频率和周期转换,时间周期计算,创建时间周期,时间序列转换,创建日期范围,更改日频率,工作日时间顾名思义,时间序列(time series),就是由时间构成的序列,它指的是在一定时间内按照时间顺序测量的某个变量的取值序列,比如一天内的温度会随时间而发生变化,或者股票的价格会随着时间不断的波动,这里用到的一系列时间,就可以看做时间序列。时间序列包含三种应用场景,分别是: 特定的时刻(timestamp),也就是时间戳; 固定的日期(pe

  • 我正在处理一个大的时间序列,其中一列包含四个不同的传感器,一列包含测量值。我需要为属于同一时间的测量分配一个 id。问题是,每个设备的测量时间略有不同,因此我不能简单地按时间戳对它们进行分组。在按时间排序的数据框中,应分组的测量值可以通过唯一设备 ID 序列进行识别。这里的问题是,一次 4 台设备记录一个值,另一次 3 台设备记录一个值。我的数据如下所示。 您可以通过以下方式复制: 我需要为连续的

  • 给定一个数组大小,请检查是否可以将序列拆分为两个序列-和,以便第一个序列严格减少,第二个序列严格增加。 输入格式 第一行包含单个整数表示输入的大小。 下一个行包含单个整数,每个行表示数组的元素。 约束

  • 问题内容: 此Python代码: 引发此错误信息: 谁能告诉我该怎么办才能解决上面破碎的代码中的问题,以便停止抛出错误消息? 编辑: 我做了一个打印命令来获取矩阵的内容,这就是它打印出来的内容: 对我来说,这看起来像是5行乘13列的矩阵,但是当通过脚本运行不同的数据时,行数是可变的。使用我要添加的相同数据。 编辑2:但是,脚本抛出错误。因此,我认为您的想法不能解释此处正在发生的问题。谢谢你 还有其

  • 问题内容: 我有: 在MyClass2中是无法序列化的属性。如何序列化(和反序列化)此对象? 更正:MyClass2当然不是接口,而是类。 问题答案: 正如其他人指出的那样,Josh Bloch的Effective Java的 第11章是有关Java序列化的必不可少的资源。 该章中与您的问题有关的几点: 假设您要序列化MyClass2中不可序列化字段的状态,则MyClass必须可以直接访问该字段,