通过在 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 kHz
和 8 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
会自动定义不相等的块大小,最多相差一个样本,以匹配您的分数目标块大小。在您的示例中,块大小为5512
、5513
、5512
、5513
...
【讨论】:
感谢您的回复。但这并不能满足我的要求。也许我没有很好地解释我自己。我会尝试发布一些代码(在主帖中),以非常手动的方式完成我之后的操作,也许这会让它更容易理解。不过非常感谢。 天哪,对不起,我的测试错了,@rayryeng 和 Luis Mendo 都给出了正确的结果。选择 Mendo 的,因为它是在 5512 和 5513 块之间切换的更正确的结果。以上是关于通过在 MATLAB 中计算平均值进行下采样的主要内容,如果未能解决你的问题,请参考以下文章
如何在 python 或 MATLAB 中对 ECG 信号进行上采样和下采样?