2017-07-20 67 views
3

我有一個非常大的陣列,有五個通道和約600萬個條目(5 x 6000000)。我的目標是通過7點窗口掃過陣列,並去除「尖峯」,其定義爲比中值絕對偏差(MAD)大一些的縮放量。MATLAB:尋找更快/更優雅的方式來計算很長時間系列的移動中值絕對偏差

我正在測試代碼,只運行10000個時間序列的初始點。目前,我需要大約3秒才能運行前10000個點。我正在運行一臺相對較舊的帶有2.30 GHz處理器和4 GB RAM的32位戴爾筆記本電腦。很明顯,如果我使用的是新電腦,我可以很快完成任務。例如,我的功能更強大的桌面可以在0.7秒內完成相同的任務。但是,我需要在筆記本電腦上運行代碼,每次需要運行代碼時都不能等待35-40分鐘。我正在尋找幫助找到效率低下的地方,以及可以讓代碼更快的地方。

以下是代碼。有關如何提高速度的任何建議表示讚賞。我注意到,「MAD」的計算是最耗時的(需要大約1.9秒,或者超過總時間的一半)。

load('data.mat') % data (approx 5 channels x 6000000 data points (int32)) 
nscans = length(data); %number of data points in each channel 

nwide = 7; %number of data points in the window 

% Rejection parameters (not so important for the question) 
iscale = 50; %scale factor for MAD 
minmad = 2; 
mincrit = [100 100 100 500 500]; 

nfixed=zeros(1,5); 
L = floor(nwide/2); %half of window (odd window length only) 

%Padding for beginning and end of data 
data = [repmat(data(:,1),[1 L]) data repmat(data(:,end),[1 L])]; 

nfixed = zeros(1,5); %initialize counter 
tic 
for n = L+1:10000 
    idata = data(:,n-L:n+L)'; % temporary window 

    % compute median of window 
    med=median(idata); 

    %compute median absolute deviation (MAD) 
    % Note: mad = median(abs(X - median(X))) 
    mad = median(abs(idata-repmat(median(idata),[nwide 1]))); 
    mad = max([mad;minmad*ones(1,5)]); %minmad threshold added 

    %compute rejection threshold 
    icrit=max([iscale*mad;mincrit]); 

    for i = 1:5 %loop over channels 
     if abs(data(i,n)-med(i)) > icrit(i) %if threshold is exceeded 
      data(i,n)=med(i); %then replace with median value 
      nfixed(i)=nfixed(i)+1; %count number of replacements 
     end 
    end 

end 
toc 

data = data(:,L+1:end-L)'; %remove padding 

我覺得有可能是一個更優雅的方式來執行「repmat」命令。

任何想法表示讚賞。

乾杯

+0

當你刪除一個尖峯,這是否會影響未來的Windows計算?它看起來很像你的代碼。這就是這種情況,很難避免一個循環 –

+0

我必須更多地思考這個問題,但第一件事就是你可以做'array-scalar'而不必重新編寫它。這可能會爲你節省一些時間。 – Durkee

回答

2

如何提高速度的任何建議表示讚賞。

您可以通過不再重複撥打median(idata)來強化您的代碼。

更改此:

mad = median(abs(idata-repmat(median(idata),[nwide 1])));

這樣:

mad = median(abs(idata-repmat(med,[nwide 1])));

或者,你可能會得到更多的里程出MATLAB's mad功能,它來到約2006年之前,你需要來改變你的變量名稱。

例如,你可以從這個更改代碼:

mad = median(abs(idata-repmat(median(idata),[nwide 1]))); 
mad = max([mad;minmad*ones(1,5)]); %minmad threshold added 

madV = max(mad(idata);[2 2 2 2 2]); 

我只是擺在那裏2周的的載體如無物中的代碼顯示minmad被更新。

+0

matlab的'mad'命令不太可能幫助。我查看了源代碼,這正是他的實施和更多開銷。可能會花費更長的時間,除非2017b之前的版本有聯繫,並且無論出於何種原因他們都將其更改。 – Durkee

+0

@Durkee我明白你對'mad()'的意思。希望將「中位數(idata)」稱爲「一次」而不是「兩次」,仍然會有所幫助。 – informaton

0

你可以用movmedian來得到移動中位數嗎?

med = movmedian(data,7,2,'Endpoints','discard'); 

儘可能避免發生for-loops。

例如:

data = randn([5,1E6]);tic; 
med = movmedian(data,7,2); %moving median 
dev = abs(data-med); %deviation from median 
thres = median(dev,2); %threshold 
rep = dev>thres; %points to replace 
data(rep) = med(rep); %replace data with median 
toc 
>>Elapsed time is 0.285828 seconds. 
memory 
>>Memory used by MATLAB:  1629 MB (1.708e+09 bytes) 
相關問題