【问题标题】:Detrending a time-series of a multi-dimensional array without the for loops在没有 for 循环的情况下去除多维数组的时间序列
【发布时间】:2011-12-02 10:51:44
【问题描述】:

我有一个 3D 阵列,其中包含地球表面每个网格点的海气碳通量时间序列(模型输出)。我想删除时间序列中的趋势(线性)。我遇到了这段代码:

from matplotlib import mlab

for x in xrange(40):
    for y in xrange(182):
        cflux_detrended[:, x, y] = mlab.detrend_linear(cflux[:, x, y])

我可以不使用 for 循环来加快速度吗?

【问题讨论】:

  • 您真的想像在双循环中那样删除每个网格点的单独趋势,还是要删除共同趋势?两者都可以作为一个大型线性模型拟合和预测,通过堆叠或重塑来完成。
  • @user333700 我想我想看看这两个结果。我发现删除数据的趋势会比删除每个网格点的趋势更有用。

标签: python numpy signal-processing scipy


【解决方案1】:

Scipy 有很多signal processing 工具。 使用scipy.signal.detrend() 将删除沿数据轴的线性趋势。从文档看来,整个数据集的线性趋势将从每个网格点的时间序列中减去。

import scipy.signal
cflux_detrended = scipy.signal.detrend(cflux, axis=0)

使用scipy.signal 将获得与使用原始帖子中的方法相同的结果。使用 Josef 的 detrend_separate() 函数也会返回相同的结果。

【讨论】:

  • 很高兴知道。我不知道也不记得这个。我查看了源代码,它看起来与我的单独趋势版本相似,仅限于线性趋势(或常数),但更一般。但是,我的数字与 signal.detrend 不匹配。 ???
  • @user333700 答案非常吻合。
  • @nicholaschris - scipy 是否能让您去除二维数据的趋势?
【解决方案2】:

这里有两个使用 numpy.linalg.lstsq 的版本。此版本使用 np.vander 创建任何多项式趋势。

警告:除示例外未测试。

我认为这样的内容将被添加到 scikits.statsmodels 中,它还没有用于去趋势的多变量版本。对于常见的趋势情况,我们可以使用 scikits.statsmodels OLS,我们也会得到所有的结果统计信息来进行估计。

# -*- coding: utf-8 -*-
"""Detrending multivariate array

Created on Fri Dec 02 15:08:42 2011

Author: Josef Perktold

http://stackoverflow.com/questions/8355197/detrending-a-time-series-of-a-multi-dimensional-array-without-the-for-loops

I should also add the multivariate version to statsmodels

"""

import numpy as np

import matplotlib.pyplot as plt


def detrend_common(y, order=1):
    '''detrend multivariate series by common trend

    Paramters
    ---------
    y : ndarray
       data, can be 1d or nd. if ndim is greater then 1, then observations
       are along zero axis
    order : int
       degree of polynomial trend, 1 is linear, 0 is constant

    Returns
    -------
    y_detrended : ndarray
       detrended data in same shape as original 

    '''
    nobs = y.shape[0]
    shape = y.shape
    y_ = y.ravel()
    nobs_ = len(y_)
    t = np.repeat(np.arange(nobs), nobs_ /float(nobs))
    exog = np.vander(t, order+1)
    params = np.linalg.lstsq(exog, y_)[0]
    fittedvalues = np.dot(exog, params)
    resid = (y_ - fittedvalues).reshape(*shape)
    return resid, params

def detrend_separate(y, order=1):
    '''detrend multivariate series by series specific trends

    Paramters
    ---------
    y : ndarray
       data, can be 1d or nd. if ndim is greater then 1, then observations
       are along zero axis
    order : int
       degree of polynomial trend, 1 is linear, 0 is constant

    Returns
    -------
    y_detrended : ndarray
       detrended data in same shape as original 

    '''
    nobs = y.shape[0]
    shape = y.shape
    y_ = y.reshape(nobs, -1)
    kvars_ = len(y_)
    t = np.arange(nobs)
    exog = np.vander(t, order+1)
    params = np.linalg.lstsq(exog, y_)[0]
    fittedvalues = np.dot(exog, params)
    resid = (y_ - fittedvalues).reshape(*shape)
    return resid, params

nobs = 30
sige = 0.1
y0 = 0.5 * np.random.randn(nobs,4,3)
t = np.arange(nobs)
y_observed = y0 + t[:,None,None]

for detrend_func, name in zip([detrend_common, detrend_separate], 
                               ['common', 'separate']):
    y_detrended, params = detrend_func(y_observed, order=1)
    print '\n\n', name 
    print 'params for detrending'
    print params
    print 'std of detrended', y_detrended.std()  #should be roughly sig=0.5 (var of y0)
    print 'maxabs', np.max(np.abs(y_detrended - y0))

    print 'observed'
    print y_observed[-1]
    print 'detrended'
    print y_detrended[-1]
    print 'original "true"'
    print y0[-1]

    plt.figure()
    for i in range(4):
        for j in range(3):
            plt.plot(y0[:,i,j], 'bo', alpha=0.75)
            plt.plot(y_detrended[:,i,j], 'ro', alpha=0.75)
    plt.title(name + ' detrending: blue - original, red - detrended')


plt.show()

由于 Nicholas 指出了 scipy.signal.detrend。我的去趋势分离与 scipy.signal.detrend 基本相同,但选项更少(无轴或中断)或不同(具有多项式顺序)选项。

>>> res = signal.detrend(y_observed, axis=0)
>>> (res - y0).var()
0.016931858083279336
>>> (y_detrended - y0).var()
0.01693185808327945
>>> (res - y_detrended).var()
8.402584948582852e-30

【讨论】:

  • @感谢您的宝贵时间。我已经尝试过了,使用 detrend_seperate 返回与原始帖子和 scipy.signal.detrend 相同的结果。我试过使用 detrend_common 但我有一个屏蔽数组,所以我用零填充了屏蔽值,效果很好。感谢您的精彩回答!
【解决方案3】:

我认为简单的旧列表理解是最简单的:

cflux_detrended = np.array([[mlab.detrend_linear(t) for t in kk] for kk in cflux.T])

【讨论】:

  • 谢谢,我需要记住列表推导。
  • 当我按原样尝试这段代码时,结果与使用问题中的方法有很大不同。有什么理由会这样吗?
猜你喜欢
  • 2021-11-18
  • 2017-03-25
  • 2013-06-06
  • 2018-08-05
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多