通过在 MATLAB 中计算平均值进行下采样

Posted

技术标签:

【中文标题】通过在 MATLAB 中计算平均值进行下采样【英文标题】:Downsample by calculating mean value in MATLAB 【发布时间】:2021-12-24 06:11:33 【问题描述】:

假设我有一个包含 44100 个样本的文件,采样频率为 fs=44100 Hz。 所以我的文件是 1 秒长。

我想将其下采样到 8 Hz 的采样频率,但我不想通过获取每个 44100/8 = 5512,5 个样本并保存在一个新数组中来做到这一点。但是通过取前 5512 个样本的平均值,保存新数组的位置 1。然后取样本 5513-11024 的平均值,并将其保存在新数组的位置 2 中。以此类推...并取样本 11025 到 11025+5512 并将其平均值放在数组的第 3 位。

我知道它必须是某种双重 for 循环,但我就是不知道如何...有什么帮助吗?

编辑:

以非常手动的方式执行我正在寻找的代码。

` fs=44100; fo=8; A = randn(44100,1); %array 在 fs=44100 采样随机数据

A_resampled = zeros(numel(A)/5512);     

first_block = zeros(5512,1);  

for i = 1:length(first_block)  
   first_block(i) = A(i);  
end  

first_mean_value = mean(first_block);  

A_resampled(1) = first_mean_value;  

second_block = zeros(5512,1);  

for k = 5513:5512+length(second_block)  
   second_block(k) = A(k);  
end  

second_mean_value = mean(second_block);  

A_resampled(2) = second_mean_value;`

等等……

【问题讨论】:

单个for循环就足够了。试一试,我们会在看到一些代码后提供帮助 【参考方案1】:
sampling = 1:5512:length(sample);
resampled = zeros(numel(sampling),1);
for ii = 1:length(sampling)-1
    resampled(ii,1) = mean(sampling(ii:ii+1));
end

sampling 是一个带有索引的数组,开始采样,然后一个循环简单地取所有这些位的平均值。

【讨论】:

您知道 Divakar 会怎么说您使用循环,对吗? :-P 但是 OP 要求这样做,所以 +1 感谢您的回复。但这并不能满足我的要求。也许我没有很好地解释我自己。我会尝试发布一些代码(在主帖中),以非常手动的方式完成我之后的操作,也许这会让它更容易理解。不过非常感谢。【参考方案2】:

reshape 将数组转换成二维矩阵,然后沿着列找到mean。为了确保我们可以正确执行此操作,我们将填充数组的末尾,以便我们确定您的信号是fs/fo 的整数倍,其中fs 是采样频率,fo是所需的频率...所以这将分别是 44.1 kHz8 Hz

因此,假设您的信号存储在A,请执行以下操作:

%// Defines
A = ....; %// Define your array here
fs = 44100; %// Sampling frequency
fo = 8; %// Desired frequency

%// Determine how many samples there are per chunk
samples_per_chunk = floor(fs/fo);

%// Determine how many total chunks there are
num_chunks = ceil(numel(A)/samples_per_chunk);

%// Create a padded array where it is an integer multiple
%// of the chunk size and insert the original array into this padded
%// array
Apad = zeros(samples_per_chunk*num_chunks,1);
Apad(1:numel(A)) = A;

%// Reshape into 2D matrix
M = reshape(Apad, [], num_chunks);

%// Find average per chunk
out = mean(M, 1);

但是,这样做的结果是,如果您的数组不是fs/fo 的整数倍,您将不必要地平均一堆附加到数组末尾的零。如果您不希望发生这种情况,一种方法是创建一个最初用NaN 填充的矩阵,然后改用nanmean,这样NaN 的值都不包含在平均值中...因此有效地忽略了填充值。

这样的东西可以代替:

%// Code as before..
%// ...
%// ...

%// New
Apad = nan(samples_per_chunk*num_chunks,1);
Apad(1:numel(A)) = A;

M = reshape(Apad, [], num_chunks);
out = nanmean(M, 1);

但是,nanmean 需要统计工具箱。如果你没有这个,那么你可以自己实现nanmean。将数组重新整形为矩阵M 后,计算每列有多少NaN 元素,然后通过将所有列相加并除以不是NaN 的条目总数自行计算平均值。

%// Code as before...
%//....
%//....

%// New
M = reshape(Apad, [], num_chunks);

%// Count how total number of NaN values
counts = sum(isnan(M), 1);

%// Set NaN values to zero to not affect mean
M(isnan(M)) = 0;

%// Calculate new average
out = sum(M,2) ./ (size(M,1) - counts);

【讨论】:

@LuisMendo - 谢谢 :) 请注意在 mean 中明确使用了 1 ;) 哦...我没有注意到。你能在某处添加.' 让它更漂亮吗? :-D @LuisMendo - :D... 我可以转置M 并使用2 表示平均值;) 我并没有真正 100% 理解代码,但我得到了前 5512 个样本的第一个平均值的正确结果。然后数组中的第二个值应该是数组中接下来的 5512 个样本的平均值会出错。至少如果我手动正确计算它。请参阅主帖中的代码更新。 @ELEFAAANT - 这没有意义。我所做的手工计算表明此代码是正确的。要么你没有正确描述你想要什么,要么你没有正确计算你想要什么。请更新您的帖子。【参考方案3】:

使用accumarray怎么样?让x 表示您的输入向量。那么

n = ceil((1:numel(x))/5512.5); %// blocks of 5512, 5513, 5512, 5513... samples
result = accumarray(n(:), x (:), [], @mean);

请注意,n 会自动定义不相等的块大小,最多相差一个样本,以匹配您的分数目标块大小。在您的示例中,块大小为5512551355125513...

【讨论】:

感谢您的回复。但这并不能满足我的要求。也许我没有很好地解释我自己。我会尝试发布一些代码(在主帖中),以非常手动的方式完成我之后的操作,也许这会让它更容易理解。不过非常感谢。 天哪,对不起,我的测试错了,@rayryeng 和 Luis Mendo 都给出了正确的结果。选择 Mendo 的,因为它是在 5512 和 5513 块之间切换的更正确的结果。

以上是关于通过在 MATLAB 中计算平均值进行下采样的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 或 MATLAB 中对 ECG 信号进行上采样和下采样?

对图像进行上采样和下采样的最佳方法

请教个matlab程序问题,帮个忙。

MATLAB对离散采样信号添加高斯白噪声(已知Eb/N0)

高分!急!用matlab分析功率谱密度,采样频率的设定.高手进!

ArcGIS中的栅格数据重采样方法都有哪些?