平均計(jì)算:從原理到環(huán)境監(jiān)測(cè)實(shí)戰(zhàn))
1. 項(xiàng)目概述什么是“最大8小時(shí)內(nèi)滑動(dòng)平均”在數(shù)據(jù)分析、環(huán)境監(jiān)測(cè)、金融量化等領(lǐng)域我們常常需要處理時(shí)間序列數(shù)據(jù)。一個(gè)典型的需求是從連續(xù)的時(shí)間序列中找出任意連續(xù)的8小時(shí)窗口內(nèi)某個(gè)指標(biāo)比如PM2.5濃度、股票成交量、服務(wù)器負(fù)載的平均值然后從所有可能的8小時(shí)窗口中找出那個(gè)最大的平均值。這就是“計(jì)算最大8小時(shí)內(nèi)滑動(dòng)平均”的核心任務(wù)。聽起來簡(jiǎn)單但手動(dòng)計(jì)算幾乎不可能尤其是面對(duì)動(dòng)輒數(shù)萬、數(shù)十萬條記錄的數(shù)據(jù)時(shí)。MATLAB作為強(qiáng)大的數(shù)值計(jì)算和工程仿真平臺(tái)其向量化操作和豐富的內(nèi)置函數(shù)讓這類計(jì)算變得高效而優(yōu)雅。今天我就以一個(gè)從業(yè)多年的數(shù)據(jù)分析師視角帶你從零開始手把手實(shí)現(xiàn)這個(gè)功能并深入探討其中的技術(shù)細(xì)節(jié)、性能優(yōu)化和那些“教科書上不會(huì)寫”的避坑經(jīng)驗(yàn)。2. 核心思路與方案選型為什么是“滑動(dòng)窗口”在動(dòng)手寫代碼之前我們必須先理清思路。計(jì)算“最大8小時(shí)滑動(dòng)平均”本質(zhì)上是一個(gè)滑動(dòng)窗口統(tǒng)計(jì)問題。這里的“8小時(shí)”是窗口的寬度而“滑動(dòng)”意味著這個(gè)窗口會(huì)沿著時(shí)間軸以一個(gè)固定的步長(zhǎng)通常是1個(gè)數(shù)據(jù)點(diǎn)即逐點(diǎn)滑動(dòng)移動(dòng)每移動(dòng)一次就計(jì)算一次窗口內(nèi)數(shù)據(jù)的平均值。2.1 方案對(duì)比循環(huán) vs. 向量化面對(duì)這個(gè)問題新手最容易想到的方法是使用for循環(huán)遍歷時(shí)間序列的每一個(gè)可能起點(diǎn)截取接下來8小時(shí)的數(shù)據(jù)計(jì)算平均值然后更新最大值。這個(gè)方法直觀但效率是硬傷。MATLAB的for循環(huán)在處理大規(guī)模數(shù)據(jù)時(shí)性能較差尤其是在腳本中直接操作時(shí)。向量化操作是MATLAB的靈魂。我們的目標(biāo)是盡可能利用MATLAB內(nèi)置的、用C/C優(yōu)化過的函數(shù)避免顯式循環(huán)。對(duì)于滑動(dòng)平均MATLAB提供了幾個(gè)潛在的“武器”movmean函數(shù)這是最直接的工具專門用于計(jì)算移動(dòng)平均值。語法簡(jiǎn)潔性能優(yōu)異。卷積操作利用conv函數(shù)與一個(gè)全為1的向量進(jìn)行卷積再除以窗口長(zhǎng)度可以實(shí)現(xiàn)滑動(dòng)求和進(jìn)而得到平均值。這是一種更底層、更靈活的方法。filter函數(shù)作為信號(hào)處理工具箱的一員它本質(zhì)上也是在實(shí)現(xiàn)卷積可以用于計(jì)算滑動(dòng)平均。對(duì)于“最大8小時(shí)滑動(dòng)平均”這個(gè)具體任務(wù)movmean函數(shù)是首選。因?yàn)樗Z義清晰無需自己處理邊界條件如窗口在數(shù)據(jù)開頭和結(jié)尾時(shí)數(shù)據(jù)不足的問題并且經(jīng)過了高度優(yōu)化。2.2 數(shù)據(jù)準(zhǔn)備與關(guān)鍵假設(shè)在編碼前我們必須明確幾個(gè)前提這直接關(guān)系到代碼的健壯性時(shí)間間隔你的數(shù)據(jù)必須是等時(shí)間間隔的。例如每小時(shí)一個(gè)數(shù)據(jù)點(diǎn)或者每分鐘一個(gè)數(shù)據(jù)點(diǎn)。如果數(shù)據(jù)間隔不均勻直接滑動(dòng)平均沒有物理意義需要先進(jìn)行重采樣或插值。窗口單位的轉(zhuǎn)換“8小時(shí)”是一個(gè)時(shí)間長(zhǎng)度而你的數(shù)據(jù)索引通常是數(shù)據(jù)點(diǎn)序號(hào)。你需要知道數(shù)據(jù)的采樣頻率。例如如果你的數(shù)據(jù)是每小時(shí)一個(gè)點(diǎn)那么8小時(shí)窗口就對(duì)應(yīng)8個(gè)數(shù)據(jù)點(diǎn)。如果是每5分鐘一個(gè)點(diǎn)那么8小時(shí)窗口就對(duì)應(yīng)8 * 60 / 5 96個(gè)數(shù)據(jù)點(diǎn)。邊界處理movmean函數(shù)默認(rèn)會(huì)處理邊界。對(duì)于窗口起始部分不足8個(gè)點(diǎn)的情況它會(huì)計(jì)算已有數(shù)據(jù)的平均值這被稱為‘shrink’模式。我們需要決定這個(gè)行為是否符合需求。在環(huán)境標(biāo)準(zhǔn)計(jì)算中如計(jì)算“日最大8小時(shí)平均”通常要求窗口必須完整包含8個(gè)數(shù)據(jù)點(diǎn)不足的則不予計(jì)算。movmean可以通過指定‘Endpoints’參數(shù)來控制。注意如果你的數(shù)據(jù)時(shí)間戳是datetime格式而數(shù)值是單獨(dú)數(shù)組你需要先將時(shí)間戳轉(zhuǎn)換為等間隔的索引或者利用時(shí)間戳直接邏輯索引來構(gòu)造窗口。本文假設(shè)你已經(jīng)有了一個(gè)等間隔的數(shù)值向量data和對(duì)應(yīng)的采樣頻率Fs單位點(diǎn)/小時(shí)。3. 核心實(shí)現(xiàn)與代碼逐行解析理論清晰后我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我將提供兩個(gè)版本的代碼一個(gè)基礎(chǔ)通用版一個(gè)考慮環(huán)境監(jiān)測(cè)實(shí)際應(yīng)用的加強(qiáng)版。3.1 基礎(chǔ)通用版實(shí)現(xiàn)假設(shè)我們有一個(gè)向量data它是按小時(shí)采樣的濃度數(shù)據(jù)例如PM2.5單位μg/m3。我們要找出任意連續(xù)8小時(shí)內(nèi)的最大平均濃度。% 基礎(chǔ)版計(jì)算最大8小時(shí)滑動(dòng)平均 % 假設(shè)數(shù)據(jù) data 是每小時(shí)一個(gè)點(diǎn)的濃度序列 Fs 1; % 采樣頻率1點(diǎn)/小時(shí) windowLengthHours 8; windowLengthPoints windowLengthHours * Fs; % 窗口長(zhǎng)度 8個(gè)點(diǎn) % 使用 movmean 計(jì)算滑動(dòng)平均 % ‘Endpoints’, ‘discard’ 表示在數(shù)據(jù)兩端當(dāng)窗口不能完整覆蓋時(shí)結(jié)果中丟棄這些位置的值。 % 這符合“必須完整8小時(shí)”的常見要求。 movingAvg movmean(data, windowLengthPoints, ‘Endpoints’, ‘discard’); % 找出所有滑動(dòng)平均值中的最大值 max8hrAvg max(movingAvg); % 可選找出最大值發(fā)生的位置窗口的起始索引 [maxValue, maxIndex] max(movingAvg); % 注意maxIndex 對(duì)應(yīng)的是 movingAvg 向量中的位置。 % 要找到原始數(shù)據(jù)中對(duì)應(yīng)窗口的起始索引因?yàn)椤甦iscard’了前(windowLengthPoints-1)個(gè)點(diǎn)所以需要加上偏移量。 windowStartIndex maxIndex; % 因?yàn)閬G棄了端點(diǎn)所以 movingAvg 的第一個(gè)值對(duì)應(yīng)原始數(shù)據(jù)中第一個(gè)完整窗口的開始 fprintf(‘最大8小時(shí)滑動(dòng)平均值為%.2f\n’, max8hrAvg); fprintf(‘該最大值出現(xiàn)在從第%d小時(shí)開始的8小時(shí)窗口內(nèi)。\n’, windowStartIndex);代碼解析與注意事項(xiàng)movmean參數(shù)詳解data: 輸入的時(shí)間序列向量。windowLengthPoints: 窗口長(zhǎng)度以數(shù)據(jù)點(diǎn)數(shù)為單位。這里是8?!瓻ndpoints’, ‘discard’: 這是關(guān)鍵參數(shù)。它指定了在數(shù)據(jù)序列的開始和結(jié)尾當(dāng)滑動(dòng)窗口無法被數(shù)據(jù)完全填滿時(shí)如何處理輸出。‘discard’會(huì)直接忽略這些不完整的窗口不在movingAvg中輸出它們的值。這對(duì)于尋找“完整8小時(shí)窗口內(nèi)的最大平均”至關(guān)重要。如果使用默認(rèn)值或‘shrink’則會(huì)用已有數(shù)據(jù)計(jì)算平均值可能導(dǎo)致結(jié)果偏大或偏小。索引對(duì)齊的坑 計(jì)算出的maxIndex是movingAvg向量中的索引。由于我們丟棄了前7個(gè)不完整窗口對(duì)于8點(diǎn)窗口movingAvg(1)實(shí)際上對(duì)應(yīng)的是原始數(shù)據(jù)data(1:8)這個(gè)完整窗口的平均值。因此原始數(shù)據(jù)中對(duì)應(yīng)窗口的起始索引就是maxIndex。如果你使用了‘shrink’或其他端點(diǎn)處理方法這個(gè)對(duì)應(yīng)關(guān)系會(huì)發(fā)生變化必須仔細(xì)推算。3.2 環(huán)境監(jiān)測(cè)應(yīng)用加強(qiáng)版在實(shí)際環(huán)境空氣質(zhì)量評(píng)價(jià)中“日最大8小時(shí)平均”是一個(gè)重要指標(biāo)。它的規(guī)則更具體一天有24小時(shí)但計(jì)算的是“移動(dòng)的8小時(shí)平均”即從0點(diǎn)到24點(diǎn)每一個(gè)小時(shí)作為起始點(diǎn)取其后8小時(shí)的平均值全天共有17個(gè)24-81這樣的滑動(dòng)平均值再取這17個(gè)值中的最大值作為當(dāng)日的“日最大8小時(shí)平均”。并且通常要求每小時(shí)的數(shù)據(jù)是有效的非缺失。下面我們模擬一個(gè)更真實(shí)的場(chǎng)景數(shù)據(jù)包含日期時(shí)間信息并且可能存在缺失值用NaN表示。% 加強(qiáng)版考慮日期時(shí)間和缺失值計(jì)算“日最大8小時(shí)平均” % 1. 生成模擬數(shù)據(jù)假設(shè)為2023年某一天每小時(shí)的數(shù)據(jù) dateVector datetime(2023, 6, 1, 0, 0, 0):hours(1):datetime(2023, 6, 1, 23, 0, 0); % 模擬一些隨機(jī)濃度數(shù)據(jù)并插入一些缺失值(NaN) rng(‘default’); % 保證可重復(fù)性 data 30 20 * randn(size(dateVector)); % 均值為50的正態(tài)分布隨機(jī)數(shù) data([5, 15, 22]) NaN; % 在第5, 15, 22小時(shí)設(shè)置數(shù)據(jù)缺失 % 2. 處理缺失值 - 對(duì)于滑動(dòng)平均常見的簡(jiǎn)單處理是線性插值 dataFilled fillmissing(data, ‘linear’); % 使用線性插值填充NaN % 注意也可以使用 ‘previous’, ‘next’ 或 ‘nearest’。選擇取決于實(shí)際業(yè)務(wù)邏輯。 % 如果缺失值過多插值可能引入較大誤差需要評(píng)估。 % 3. 計(jì)算8小時(shí)滑動(dòng)平均完整窗口 windowHours 8; movingAvgFull movmean(dataFilled, windowHours, ‘Endpoints’, ‘discard’); % 4. 找出最大值及其位置 [maxAvgValue, maxAvgIdxInMoving] max(movingAvgFull); % 計(jì)算該最大值對(duì)應(yīng)的原始數(shù)據(jù)時(shí)間窗口的起始時(shí)間 % movingAvgFull(1) 對(duì)應(yīng)原始時(shí)間 dateVector(1) 到 dateVector(8) 的平均值 windowStartTime dateVector(maxAvgIdxInMoving); windowEndTime dateVector(maxAvgIdxInMoving windowHours - 1); % 結(jié)束時(shí)間是起始時(shí)間7小時(shí) % 5. 輸出結(jié)果 fprintf(‘日期%s\n’, datestr(dateVector(1), ‘yyyy-mm-dd’)); fprintf(‘經(jīng)過線性插值處理后日最大8小時(shí)平均濃度為%.2f μg/m3\n’, maxAvgValue); fprintf(‘該最大值對(duì)應(yīng)的8小時(shí)窗口為%s 至 %s\n’, ... datestr(windowStartTime, ‘HH:MM’), datestr(windowEndTime, ‘HH:MM’)); % 6. 可視化繪制原始數(shù)據(jù)、插值后數(shù)據(jù)及滑動(dòng)平均曲線 figure(‘Position’, [100, 100, 1200, 500]); subplot(2,1,1); plot(dateVector, data, ‘o-‘, ‘DisplayName’, ‘原始數(shù)據(jù)含NaN’); hold on; plot(dateVector, dataFilled, ‘x–‘, ‘DisplayName’, ‘插值后數(shù)據(jù)’); xlabel(‘時(shí)間’); ylabel(‘濃度 (μg/m3)’); title(‘原始數(shù)據(jù)與缺失值處理’); legend(‘Location’, ‘best’); grid on; subplot(2,1,2); % 為滑動(dòng)平均結(jié)果生成對(duì)應(yīng)的時(shí)間軸丟棄了前7個(gè)點(diǎn) timeForMovingAvg dateVector(1:end-windowHours1) hours((windowHours-1)/2); % 將時(shí)間點(diǎn)標(biāo)在窗口中部 plot(timeForMovingAvg, movingAvgFull, ‘s-‘, ‘LineWidth’, 1.5, ‘DisplayName’, ‘8小時(shí)滑動(dòng)平均’); hold on; % 標(biāo)記出最大值點(diǎn) plot(timeForMovingAvg(maxAvgIdxInMoving), maxAvgValue, ‘r*’, ‘MarkerSize’, 15, ‘DisplayName’, ‘日最大8小時(shí)平均’); xlabel(‘時(shí)間窗口中心點(diǎn)’); ylabel(‘平均濃度 (μg/m3)’); title(‘8小時(shí)滑動(dòng)平均序列與最大值’); legend(‘Location’, ‘best’); grid on;關(guān)鍵點(diǎn)解析與實(shí)操心得缺失值處理是重中之重movmean函數(shù)遇到NaN時(shí)整個(gè)窗口的平均值也會(huì)是NaN。這會(huì)導(dǎo)致最大值查找失敗max函數(shù)會(huì)忽略NaN但你可能得到的是一個(gè)非完整窗口的最大值。因此必須先處理缺失值。fillmissing函數(shù)非常強(qiáng)大‘linear’插值適用于連續(xù)變化的數(shù)據(jù)。但在實(shí)際業(yè)務(wù)中需要根據(jù)數(shù)據(jù)缺失機(jī)制和行業(yè)規(guī)范選擇方法有時(shí)甚至需要將缺失過多的小時(shí)所在日的計(jì)算視為無效。時(shí)間戳對(duì)齊滑動(dòng)平均結(jié)果movingAvgFull的長(zhǎng)度比原始數(shù)據(jù)短。為了繪圖或分析需要為其創(chuàng)建正確的時(shí)間標(biāo)簽。常見的做法是將平均值對(duì)應(yīng)的時(shí)間點(diǎn)放在窗口的中間時(shí)刻如上例代碼所示這樣在圖上看起來更合理。而查找出的maxAvgIdxInMoving對(duì)應(yīng)的是這個(gè)“中間時(shí)刻”序列的索引要反推回窗口的起止時(shí)間需要做簡(jiǎn)單的加減運(yùn)算?!瓻ndpoints’, ‘discard’的必然性在環(huán)境標(biāo)準(zhǔn)計(jì)算中必須使用此參數(shù)。因?yàn)橐惶靸啥说拇翱谌?-7點(diǎn)17-24點(diǎn)是不完整的24小時(shí)內(nèi)的8小時(shí)窗口不符合“日內(nèi)滑動(dòng)”的定義。計(jì)算時(shí)只考慮從0點(diǎn)至16點(diǎn)開始的共17個(gè)完整窗口。4. 性能優(yōu)化與高級(jí)技巧當(dāng)數(shù)據(jù)量極大例如多年、多站點(diǎn)的每小時(shí)數(shù)據(jù)時(shí)基礎(chǔ)方法可能仍有優(yōu)化空間。此外一些特殊需求也需要更靈活的方案。4.1 處理超長(zhǎng)序列與分塊計(jì)算對(duì)于長(zhǎng)達(dá)數(shù)年的每小時(shí)數(shù)據(jù)直接計(jì)算內(nèi)存占用可能很高。雖然movmean已經(jīng)優(yōu)化得很好但我們可以考慮分日計(jì)算因?yàn)椤叭兆畲?小時(shí)平均”本身就是按日統(tǒng)計(jì)的。% 假設(shè)我們有長(zhǎng)時(shí)間序列數(shù)據(jù) dates 和 values % 首先將數(shù)據(jù)按日期分組 [year, month, day] ymd(dates); % 需要 datetime 數(shù)組 dateGroups findgroups(year, month, day); % 為每一天創(chuàng)建一個(gè)分組ID % 預(yù)分配結(jié)果數(shù)組 uniqueDates unique(dates, ‘day’); % 獲取不重復(fù)的日期 max8hrDaily zeros(size(uniqueDates)); % 對(duì)每一天進(jìn)行循環(huán)計(jì)算 for i 1:length(uniqueDates) dayMask dates uniqueDates(i) dates uniqueDates(i) days(1); dataOfDay values(dayMask); % 處理缺失值這里簡(jiǎn)單用前后值均值填充實(shí)際需謹(jǐn)慎 dataFilled fillmissing(dataOfDay, ‘linear’); % 確保一天有24個(gè)數(shù)據(jù)點(diǎn)處理可能的嚴(yán)重缺失 if length(dataFilled) 24 movingAvg movmean(dataFilled, 8, ‘Endpoints’, ‘discard’); max8hrDaily(i) max(movingAvg); else max8hrDaily(i) NaN; % 數(shù)據(jù)不全記為缺失 end end % 現(xiàn)在 max8hrDaily 就是每一天的“日最大8小時(shí)平均”4.2 自定義滑動(dòng)窗口函數(shù)以應(yīng)對(duì)復(fù)雜邏輯如果業(yè)務(wù)邏輯非常特殊比如窗口長(zhǎng)度可變或者計(jì)算的不是算術(shù)平均而是其他統(tǒng)計(jì)量如中位數(shù)、百分位數(shù)可以自定義滑動(dòng)窗口函數(shù)。% 示例計(jì)算8小時(shí)滑動(dòng)中位數(shù)對(duì)異常值更魯棒 windowLen 8; data randn(1000,1); % 模擬數(shù)據(jù) % 方法使用循環(huán)但利用預(yù)分配和向量索引提高效率 n length(data); result zeros(n - windowLen 1, 1); % 預(yù)分配結(jié)果數(shù)組 for startIdx 1:(n - windowLen 1) windowData data(startIdx : startIdx windowLen - 1); result(startIdx) median(windowData); end maxSlidingMedian max(result);雖然用了循環(huán)但對(duì)于窗口操作MATLAB R2016a以后版本對(duì)for循環(huán)進(jìn)行了JIT即時(shí)編譯加速在不是極端性能瓶頸的場(chǎng)景下這種寫法清晰易懂。當(dāng)然也可以探索用arrayfun或編寫MEX文件來進(jìn)一步優(yōu)化。4.3 利用卷積conv實(shí)現(xiàn)底層滑動(dòng)平均理解movmean的底層原理有助于解決更復(fù)雜的問題?;瑒?dòng)平均可以通過卷積實(shí)現(xiàn)windowLen 8; kernel ones(windowLen, 1) / windowLen; % 卷積核長(zhǎng)度為8每個(gè)元素為1/8 movingAvgConv conv(data, kernel, ‘valid’); % ‘valid’模式只返回完全重疊的部分相當(dāng)于‘discard’‘valid’模式的結(jié)果長(zhǎng)度是length(data) - windowLen 1與movmean(data, windowLen, ‘Endpoints’, ‘discard’)結(jié)果完全相同。這種方法在你想自定義加權(quán)平均如指數(shù)加權(quán)時(shí)特別有用只需修改kernel向量即可。5. 常見問題、錯(cuò)誤排查與調(diào)試技巧在實(shí)際操作中你幾乎一定會(huì)遇到下面這些問題。這里是我的“踩坑”實(shí)錄和解決方案。5.1 數(shù)據(jù)長(zhǎng)度與窗口長(zhǎng)度不匹配問題計(jì)算時(shí)MATLAB報(bào)錯(cuò)“窗口長(zhǎng)度必須小于或等于輸入長(zhǎng)度”或結(jié)果的長(zhǎng)度出乎意料。排查檢查你的windowLengthPoints計(jì)算是否正確。確保它是標(biāo)量整數(shù)。檢查輸入數(shù)據(jù)data是否是向量。movmean也支持矩陣按指定維度計(jì)算如果data是矩陣需要指定維度參數(shù)如movmean(data, k, 1)對(duì)列滑動(dòng)?;叵搿瓻ndpoints’參數(shù)的影響。如果使用‘discard’輸出長(zhǎng)度會(huì)是length(data) - windowLengthPoints 1。如果你期望輸出長(zhǎng)度與輸入相同應(yīng)使用‘shrink’默認(rèn)或‘fill’。5.2 結(jié)果全是NaN或包含NaN問題計(jì)算出的movingAvg里有很多甚至全部是NaN。原因與解決輸入數(shù)據(jù)包含NaN這是最常見原因。使用any(isnan(data))檢查。務(wù)必在計(jì)算前處理缺失值插值、刪除或標(biāo)記。窗口內(nèi)全是NaN即使做了插值如果數(shù)據(jù)開頭或結(jié)尾連續(xù)缺失插值可能失敗。考慮使用‘omitnan’選項(xiàng)MATLAB R2015b以上movmean(data, k, ‘omitnan’)。這個(gè)選項(xiàng)會(huì)在計(jì)算每個(gè)窗口的平均值時(shí)忽略該窗口內(nèi)的NaN。但要注意這可能導(dǎo)致窗口實(shí)際用于計(jì)算的數(shù)據(jù)點(diǎn)數(shù)少于k從而影響結(jié)果的可比性需結(jié)合業(yè)務(wù)判斷。5.3 最大值對(duì)應(yīng)的時(shí)間窗口找錯(cuò)問題找到了最大平均值但根據(jù)索引回溯到原始數(shù)據(jù)時(shí)發(fā)現(xiàn)對(duì)應(yīng)的8小時(shí)窗口不對(duì)。調(diào)試步驟打印關(guān)鍵索引在計(jì)算后立即打印maxIndex,length(data),length(movingAvg)確認(rèn)它們的關(guān)系。手動(dòng)驗(yàn)證一個(gè)小例子用一個(gè)人工構(gòu)造的簡(jiǎn)單數(shù)組如data [1:24]運(yùn)行你的代碼。因?yàn)榈炔顢?shù)列的平均值就是中間值你可以很容易地心算出最大8小時(shí)平均應(yīng)該是[17:24]這個(gè)窗口平均值為20.5。檢查你的程序結(jié)果是否匹配。繪制示意圖像前面的加強(qiáng)版代碼一樣將原始數(shù)據(jù)、滑動(dòng)平均序列以及標(biāo)記的最大值點(diǎn)畫在同一張圖上。視覺檢查是最有效的調(diào)試手段之一。5.4 處理非整點(diǎn)或不等間隔數(shù)據(jù)問題數(shù)據(jù)時(shí)間戳不是規(guī)整的整點(diǎn)時(shí)間或者采樣間隔不穩(wěn)定。解決方案重采樣使用retime針對(duì)timetable或resample針對(duì)信號(hào)函數(shù)將數(shù)據(jù)插值或聚合到等間隔的時(shí)間網(wǎng)格上例如每小時(shí)一個(gè)點(diǎn)。這是最規(guī)范的做法?;跁r(shí)間戳的滑動(dòng)如果堅(jiān)持使用原始時(shí)間戳你需要編寫自定義循環(huán)。在循環(huán)中對(duì)于每一個(gè)數(shù)據(jù)點(diǎn)i使用邏輯索引找出時(shí)間在[time(i), time(i)hours(8)]范圍內(nèi)的所有數(shù)據(jù)點(diǎn)然后計(jì)算它們的平均值。這種方法計(jì)算量很大但能最大程度保留原始信息。% 偽代碼示意 times ... % datetime 向量 values ... % 數(shù)值向量 maxAvg -inf; for i 1:length(times) windowMask times times(i) times times(i) hours(8); if sum(windowMask) 6 % 至少需要一定數(shù)量的數(shù)據(jù)點(diǎn)例如6個(gè) avg mean(values(windowMask), ‘omitnan’); if avg maxAvg maxAvg avg; bestStartTime times(i); end end end5.5 內(nèi)存不足Out of Memory問題處理超大型數(shù)組時(shí)MATLAB報(bào)內(nèi)存錯(cuò)誤。優(yōu)化策略使用單精度如果數(shù)據(jù)精度要求不高在數(shù)據(jù)導(dǎo)入時(shí)使用single類型data single(yourData);可以減半內(nèi)存占用。分塊處理如4.1節(jié)所示將數(shù)據(jù)按天、按月分割處理每次只加載一部分到內(nèi)存。避免創(chuàng)建中間大數(shù)組例如movmean的結(jié)果是一個(gè)新數(shù)組。如果原始數(shù)據(jù)很大這個(gè)結(jié)果數(shù)組也很大。如果后續(xù)只需要最大值可以考慮分塊計(jì)算并實(shí)時(shí)比較更新最大值而不是保存整個(gè)滑動(dòng)平均序列。使用內(nèi)存映射文件對(duì)于存儲(chǔ)在磁盤上的巨型數(shù)據(jù)文件可以使用memmapfile函數(shù)進(jìn)行內(nèi)存映射實(shí)現(xiàn)按需訪問而不是一次性全部讀入。6. 擴(kuò)展應(yīng)用從滑動(dòng)平均到滑動(dòng)統(tǒng)計(jì)掌握了滑動(dòng)平均你就可以輕松擴(kuò)展到其他滑動(dòng)窗口統(tǒng)計(jì)量MATLAB提供了統(tǒng)一的movXXX函數(shù)家族movmedian: 滑動(dòng)中位數(shù)抗噪聲movstd: 滑動(dòng)標(biāo)準(zhǔn)差看波動(dòng)movvar: 滑動(dòng)方差movsum: 滑動(dòng)總和movmin/movmax: 滑動(dòng)最小/最大值例如在金融分析中我們??垂蓛r(jià)的20日滑動(dòng)標(biāo)準(zhǔn)差波動(dòng)率在工業(yè)監(jiān)控中看設(shè)備溫度最近1小時(shí)的滑動(dòng)最大值是否超閾值。其調(diào)用語法與movmean高度一致。最后關(guān)于工具版本我強(qiáng)烈建議使用MATLAB R2016a或更高版本。這些版本對(duì)movmean等函數(shù)以及循環(huán)的JIT編譯都有了顯著優(yōu)化性能提升非常明顯。如果你還在使用更舊的版本升級(jí)帶來的效率提升可能會(huì)讓你驚喜。