Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/matlab/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Matlab 将函数应用于滚动窗口_Matlab_Vector - Fatal编程技术网

Matlab 将函数应用于滚动窗口

Matlab 将函数应用于滚动窗口,matlab,vector,Matlab,Vector,假设我有一长串的a值(比如长度1000),我想计算100对std,即我想计算std(a(1:100)),std(a(2:101)),std(a(3:102)),std(a(901:1000)) 在Excel/VBA中,您可以通过在一个单元格中编写例如=STDEV(A1:A100)来轻松完成此任务,然后一次性填写。现在我的问题是,如何在Matlab中高效地实现这一点,而不必使用任何昂贵的for循环。 编辑:是否也可以对时间序列列表执行此操作,例如a的维度为1000 x 4(即长度为1000的4个时

假设我有一长串的
a
值(比如长度1000),我想计算100对
std
,即我想计算
std(a(1:100))
std(a(2:101))
std(a(3:102))
std(a(901:1000))

在Excel/VBA中,您可以通过在一个单元格中编写例如
=STDEV(A1:A100)
来轻松完成此任务,然后一次性填写。现在我的问题是,如何在Matlab中高效地实现这一点,而不必使用任何昂贵的for循环。



编辑:是否也可以对时间序列列表执行此操作,例如a的维度为1000 x 4(即长度为1000的4个时间序列)?然后,输出矩阵的维数应为901 x 4。

您所做的基本上是一个过滤操作

如果您可以访问图像处理工具箱

stdfilt(A,ones(101,1)) %# assumes that data series are in columns

将实现这一技巧(无论
A
的维度如何)。请注意,如果您还可以访问并行计算工具箱,那么您可以让类似这些过滤操作在GPU上运行,尽管您的问题可能太小,无法产生明显的加速

注意:有关最快的解决方案,请参见

因此,首先使用for循环来实现这一点(特别是如果这些是您的实际维度的话)并不昂贵。除非您使用的是非常旧的Matlab版本,否则JIT编译器(当然还有预分配)使循环变得便宜

第二,你试过循环吗?因为在过早开始优化之前,您应该首先尝试天真的实现

第三-
arrayfun
可以使它成为一个单行程序,但它基本上只是一个for循环,有额外的开销,如果你真的关心速度的话,它很可能比for循环慢

最后是一些代码:

n = 1000;
A = rand(n,1);
l = 100;
对于回路(几乎不笨重,可能是高效的):

矢量化(高效内存!)解决方案:

[X,Y] = meshgrid(1:l)
S = std(A(X+Y-1))
一个可能更好的矢量化解决方案(和一条直线),但仍具有高效的内存:

S = zeros(n-l+1,1);  %//Pre-allocation of memory like this is essential for efficiency!
for t = 1:(n-l+1)
    S(t) = std(A(t:(t+l-1)));
end
S = std(A(bsxfun(@plus, 0:l-1, (1:l)')))
请注意,使用所有这些方法,您可以用任何函数替换
std
,只要它适用于矩阵的列(这是Matlab中的标准)


进入2D:

要进入2D,我们需要进入3D

n = 1000;
k = 4;
A = rand(n,k);
l = 100;

ind = bsxfun(@plus, permute(o:n:(k-1)*n, [3,1,2]), bsxfun(@plus, 0:l-1, (1:l)'));    %'
S = squeeze(std(A(ind)));
M = squeeze(mean(A(ind)));
%// etc...

或者(摘自-注意,在他的回答中,他展示了一个比这个简单循环更快的选择)


在Matlab中有几个函数可以有效地完成这项工作

一方面,您可以使用诸如
colfilt
nlfilter
之类的函数,这些函数在滑动块上执行计算
colfilt
nlfilter
更有效,但仅当块内元素的顺序无关紧要时才能使用。以下是如何在您的数据上使用它:

S = colfilt(A, [100,1], 'sliding', @std);

在您的示例中,您可以清楚地看到性能的差异。但有一个技巧:两个函数都填充输入数组,以便输出向量与输入数组具有相同的大小。要仅获取输出向量的相关部分,需要跳过第一个
floor((100-1)/2)=49
第一个元素,并获取
1000-100+1

S(50:end-50)
但还有另一种解决方案,接近于
colfilt
,效率更高
colfilt
调用
col2im
将输入向量重塑为一个矩阵,它在该矩阵上对每个不同的列应用给定的函数。这会将大小为[1000,1]的输入向量转换为大小为[100901]的矩阵。但是
colfilt
用0或1填充输入数组,您不需要它。因此,您可以在不使用填充步骤的情况下运行
colfilt
,然后对每列应用
std
,这很容易,因为对矩阵应用
std
会返回列的std的行向量。最后,如果需要,将其转置以获得列向量。简而言之,一行:

S = std(im2col(X,[100 1],'sliding')).';
S = foldfunction( @std , A , l ) ;

备注:如果您想应用更复杂的函数,请参阅
colfilt
的代码,第144行和第147行(对于v2013b)。

如果您关心的是
for
循环的速度,您可以通过将向量折叠成数组(使用
重塑
)来大大减少循环迭代次数列具有要应用函数的元素数

这将使Matlab和JIT通过计算数组中每列的函数来执行优化(在大多数情况下,他们比我们做得更好)

然后重塑阵列的偏移版本,并执行相同的操作。您仍然需要一个循环,但迭代次数将仅为
l
(在您的示例中为
100
),而不是
n-l+1=901
的经典
for
循环(一次一个窗口)。 完成后,在向量中重塑结果数组,然后仍然需要手动计算最后一个窗口,但总体而言,速度要快得多

采用与Dan相同的输入符号:

n = 1000;
A = rand(n,1);
l = 100;
它将采取这种形式:

width = (n/l)-1 ;           %// width of each line in the temporary result array
tmp = zeros( l , width ) ;  %// preallocation never hurts
for k = 1:l                 
    tmp(k,:) = std( reshape( A(k:end-l+k-1) , l , [] ) ) ; %// calculate your stat on the array (reshaped vector)
end
S2 = [tmp(:) ; std( A(end-l+1:end) ) ] ;  %// "unfold" your results then add the last window calculation
如果我
tic。。。toc
完整循环版本和折叠版本,我得到了以下平均结果:

Elapsed time is 0.057190 seconds. %// windows by window FOR loop
Elapsed time is 0.016345 seconds. %// "Folded" FOR loop
我知道
tic/toc
不是实现完美计时的方法,但我的matlab版本中没有
timeit
功能。此外,差异非常显著,足以表明存在改进(尽管这种方法无法精确量化)。当然,我删除了第一次运行,并检查结果是否与不同的矩阵大小一致


关于您的“一行程序”请求,我建议您将此代码包装成如下函数:

function out = foldfunction( func , vec , nPts )

n = length( vec ) ;
width = (n/nPts)-1 ;
tmp = zeros( nPts , width ) ;

for k = 1:nPts
    tmp(k,:) = func( reshape( vec(k:end-nPts+k-1) , nPts , [] ) ) ;
end
out = [tmp(:) ; func( vec(end-nPts+1:end) ) ] ;
主代码中的哪一个允许您在一行中调用它:

S = std(im2col(X,[100 1],'sliding')).';
S = foldfunction( @std , A , l ) ;
这种格式的另一个好处是,您可以对其他统计函数使用非常相同的子函数。例如,如果您想要窗口的“平均值”,只需更改
func
参数即可调用相同的值:

S = foldfunction( @mean , A , l ) ;
唯一的限制,因为它是
S = foldfunction( @std , A , l ) ;
S = foldfunction( @mean , A , l ) ;
A = randn(1000,4); %// random data
N = 100; %// window size
c = size(A,2);
A1 = [zeros(1,c); cumsum(A)];
A2 = [zeros(1,c); cumsum(A.^2)];
S = sqrt( (A2(1+N:end,:)-A2(1:end-N,:) ...
    - (A1(1+N:end,:)-A1(1:end-N,:)).^2/N) / (N-1) ); %// result
%// File loop_approach.m
function S = loop_approach(A,N);
[n, p] = size(A);
S = zeros(n-N+1,p);
for k = 1:(n-N+1)
    S(k,:) = std(A(k:(k+N-1),:));
end

%// File bsxfun_approach.m
function S = bsxfun_approach(A,N);
[n, p] = size(A);
ind = bsxfun(@plus, permute(0:n:(p-1)*n, [3,1,2]), bsxfun(@plus, 0:n-N, (1:N).')); %'
S = squeeze(std(A(ind)));

%// File cumsum_approach.m
function S = cumsum_approach(A,N);
c = size(A,2);
A1 = [zeros(1,c); cumsum(A)];
A2 = [zeros(1,c); cumsum(A.^2)];
S = sqrt( (A2(1+N:end,:)-A2(1:end-N,:) ...
    - (A1(1+N:end,:)-A1(1:end-N,:)).^2/N) / (N-1) );

%// Benchmarking code
clear all
A = randn(1000,4); %// Or A = randn(1000,1);
N = 100;
t_loop   = timeit(@() loop_approach(A,N));
t_bsxfun = timeit(@() bsxfun_approach(A,N));
t_cumsum = timeit(@() cumsum_approach(A,N));
disp(' ')
disp(['loop approach:   ' num2str(t_loop)])
disp(['bsxfun approach: ' num2str(t_bsxfun)])
disp(['cumsum approach: ' num2str(t_cumsum)])
disp(' ')
disp(['bsxfun/loop gain factor: ' num2str(t_loop/t_bsxfun)])
disp(['cumsum/loop gain factor: ' num2str(t_loop/t_cumsum)])
loop approach:   0.092035
bsxfun approach: 0.023535
cumsum approach: 0.0002338

bsxfun/loop gain factor: 3.9106
cumsum/loop gain factor: 393.6526
loop approach:   0.085618
bsxfun approach: 0.0040495
cumsum approach: 8.3642e-05

bsxfun/loop gain factor: 21.1431
cumsum/loop gain factor: 1023.6236