Decreasing data points by half via downsampling - matlab

Let say I have a 6x5 matrix (my actual data is way bigger)
A B C D E
1 5 7 2 3
2 1 9 8 5
3 1 2 3 1
4 1 3 4 2
5 2 9 0 1
6 5 3 4 3
I have to make a plot with A on the x-axis and B,C,D on the y-axis. If I want to reduce the data points by half (by averaging each adjacent pair of data points), how do I do it? What if I want to decrease the points even further by averaging every five (or n) points?
I looked at the MATLAB help document but I'm still confused
I got what I needed, thanks for the input guys, it really helped

There you go:
M = [1 5 7 2 3
2 1 9 8 5
3 1 2 3 1
4 1 3 4 2
5 2 9 0 1
6 5 3 4 3]; % data
>>result = (M(1:2:end-1,:) + M(2:2:end,:))/2;
result =
1.5000 3.0000 8.0000 5.0000 4.0000
3.5000 1.0000 2.5000 3.5000 1.5000
5.5000 3.5000 6.0000 2.0000 2.0000

An even number of rows scenario is straightforward, using mean to do the work:
>> M = magic(4)
M =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> reshape(mean(reshape(M,2,[]),1),[],size(M,2))
ans =
10.5000 6.5000 6.5000 10.5000
6.5000 10.5000 10.5000 6.5000
For the odd number of rows scenario, let's say you want to retain the last row. Here's a general even/odd solution:
>> M = magic(5) % 5 rows!
M =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
>> Mp = [M; repmat(M(end,:),mod(size(M,1),2),1)]; % replicate last row if odd
>> Mthin = reshape(mean(reshape(Mp,2,[]),1),[],size(Mp,2))
Mthin =
20.0000 14.5000 4.0000 11.0000 15.5000
7.0000 9.0000 16.0000 20.5000 12.5000
11.0000 18.0000 25.0000 2.0000 9.0000
Alternatively, if you want to throw away the last row when encountered with an odd number of rows:
>> Mp = M(1:end-mod(size(M,1),2),:);
>> Mthin = reshape(mean(reshape(Mp,2,[]),1),[],size(Mp,2))
Mthin =
20.0000 14.5000 4.0000 11.0000 15.5000
7.0000 9.0000 16.0000 20.5000 12.5000
Now for averaging n points, retaining the average of the mod(size(M,1),n) last rows:
n = 5;
M = rand(972,5); % or whatever
p = mod(size(M,1),n);
r = repmat(mean(M(end-p+1:end,:),1),(p>0)*(n-p),1);
Mp = [M; r];
Mthin = reshape(mean(reshape(Mp,n,[]),1),[],size(Mp,2));
And for throwing out the last mod(size(M,1),n) rows:
Mp = M(1:end-mod(size(M,1),n),:);
Mthin = reshape(mean(reshape(Mp,n,[]),1),[],size(Mp,2));

>>> A= magic(5) %some "random" data
A =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
>>> B=(A(1:2:end-1,:)+A(2:2:end,:))/2
B =
20.0000 14.5000 4.0000 11.0000 15.5000
7.0000 9.0000 16.0000 20.5000 12.5000
Takes average of each pair of rows, ignores the last row if row count is not even.
And some general solution:
%input data data
X=randi(30,30,5)
step=7
%extend matrix, until size matches step (could be done faster using repmat)
while(mod(size(X,1),step)~=0)
X(end+1,:)=X(end,:)
end
%Split data into segments of size "step"
C=mat2cell(X,repmat(step,floor(size(X,1)/step),1),size(X,2))
%Average over each segment:
AVG=cell2mat(cellfun(#(x)(mean(x,1)),C,'UniformOutput',false))

Related

Piecewise average over n elements in Matlab

I want to piecewise-average a vector in Matlab. Vector x looks like this:
x = 1:15;
Respectively:
x = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
I want to find the mean value over n = 5 elements; therefore, the result-vector y should look like:
y = [1 1.5 2.5 3 4 5 6 7 8 9 10 11 12 13]
The code for generating the vector y should somehow work like this:
y = [
mean ([1])
mean ([1,2])
mean ([1,2,3])
mean ([1,2,3,4])
mean ([1,2,3,4,5])
mean ([2,3,4,5,6])
mean ([3,4,5,6,7])
mean ([4,5,6,7,8])
mean ([5,6,7,8,9])
mean ([6,7,8,9,10])
mean ([7,8,9,10,11])
mean ([8,9,10,11,12])
mean ([9,10,11,12,13])
mean ([10,11,12,13,14])
mean ([11,12,13,14,15])
]
For n < 5 elements, the program should average over n elements. For example, if there are only 3 elements available, the code should average the first 3 elements. For n > 5 elements, the program should average over the last 5 elements.
Any help is appreciated!
For such sliding summing or averaging operations, a very efficient vectorized approach would be with 1D convolution conv, like so -
n = 5
sums = conv(x,ones(1,n))
out = sums(1:numel(x))./[1:n n*ones(1,numel(x)-n)]
Try this:
x = 1:15;
for n = 1:length(x)
if n <= 5
y(n) = mean(x(1:n))
else
y(n) = mean(x(n-4:n))
end
end
Below is a kind of a brute force method.
for j=1:length(x)
A=j-4;
if A<1
A=1;
end;
y(j)=mean(x(A:j))
end;
or in a more compact form:
for j=1:length(x)
y(j)=mean(x(max(j-4,1):j));
end;
Here is an alternative method
make a matrix of all the numbers which you have to take average of in each row, here in bsxfun() function a vector four new rows are creating for each element of 1:15 row by row of previous 4 digits of the current number and then all the non zero elements are ommitted and amde 0
n =
5
A = bsxfun(#plus ,[1:15].',-(n -1):0)
A(A<0) = 0
A =
0 0 0 0 1
0 0 0 1 2
0 0 1 2 3
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
6 7 8 9 10
7 8 9 10 11
8 9 10 11 12
9 10 11 12 13
10 11 12 13 14
11 12 13 14 15
and then divide the sum of each row by number of non-zero elements in each row
>> sum(A,2)./sum(~ismember(A,0),2)
ans =
1.0000
1.5000
2.0000
2.5000
3.0000
4.0000
5.0000
6.0000
7.0000
8.0000
9.0000
10.0000
11.0000
12.0000
13.0000

Generating a grid in matlab with a general number of dimensions

Problem
I have a vector w containing n elements. I do not know n in advance.
I want to generate an n-dimensional grid g whose values range from grid_min to grid_max and obtain the "dimension-wise" product of w and g.
How can I do this for an arbitrary n?
Examples
For simplicity, let's say that grid_min = 0 and grid_max = 5.
Case: n=1
>> w = [0.75];
>> g = 0:5
ans =
0 1 2 3 4 5
>> w * g
ans =
0 0.7500 1.5000 2.2500 3.0000 3.7500
Case: n=2
>> w = [0.1, 0.2];
>> [g1, g2] = meshgrid(0:5, 0:5)
g1 =
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
g2 =
0 0 0 0 0 0
1 1 1 1 1 1
2 2 2 2 2 2
3 3 3 3 3 3
4 4 4 4 4 4
5 5 5 5 5 5
>> w(1) * g1 + w(2) * g2
ans =
0 0.1000 0.2000 0.3000 0.4000 0.5000
0.2000 0.3000 0.4000 0.5000 0.6000 0.7000
0.4000 0.5000 0.6000 0.7000 0.8000 0.9000
0.6000 0.7000 0.8000 0.9000 1.0000 1.1000
0.8000 0.9000 1.0000 1.1000 1.2000 1.3000
1.0000 1.1000 1.2000 1.3000 1.4000 1.5000
Now suppose a user passes in the vector w and we do not know how many elements (n) it contains. How can I create the grid and obtain the product?
%// Data:
grid_min = 0;
grid_max = 5;
w = [.1 .2 .3];
%// Let's go:
n = numel(w);
gg = cell(1,n);
[gg{:}] = ndgrid(grid_min:grid_max);
gg = cat(n+1, gg{:});
result = sum(bsxfun(#times, gg, shiftdim(w(:), -n)), n+1);
How this works:
The grid (variable gg) is generated with ndgrid, using as output a comma-separated list of n elements obtained from a cell array. The resulting n-dimensional arrays (gg{1}, gg{2} etc) are contatenated along the n+1-th dimension (using cat), which turns gg into an n+1-dimensional array. The vector w is reshaped into the n+1-th dimension (shiftdim), multiplied by gg using bsxfun, and the results are summed along the n+1-th dimension.
Edit:
Following #Divakar's insightful comment, the last line can be replaced by
sz_gg = size(gg);
result = zeros(sz_gg(1:end-1));
result(:) = reshape(gg,[],numel(w))*w(:);
which results in a significant speedup, because Matlab is even better at matrix multiplication than at bsxfun (see for example here and here).

Using MATLAB, how can I find the moving average?

Using MATLAB, how can I find the 3-day moving average of a specific column of a matrix and append the moving average to that matrix? I am trying to compute the 3-day moving average from bottom to top of the matrix. I have provided my code:
Given the following matrix a and mask:
a = [1,2,3;4,5,6;7,8,9;10,11,12;13,14,15;16,17,18];
mask = ones(3,1);
I have tried implementing the conv command but I am receiving an error. Here is the conv command I have been trying to use on the 2nd column of matrix a:
a(:,4) = conv(a(:,2),mask,'valid');
The output I desire is given in the following matrix:
desiredOutput = [1,2,3,5;4,5,6,8;7,8,9,11;10,11,12,14;13,14,15,0;16,17,18,0;]
If you have any suggestions, I would greatly appreciate it. Thank you!
In general it would help if you would show the error. In this case you are doing two things wrong:
First your convolution needs to be divided by three (or the length of the moving average)
c = conv(a(:,2),mask,'valid')/3
c =
5
8
11
14
Second, notice the size of c. You cannot just fit c into a. The typical way of getting a moving average would be to use same:
a(:,4) = conv(a(:,2),mask,'same')/3
a =
1.0000 2.0000 3.0000 2.3333
4.0000 5.0000 6.0000 5.0000
7.0000 8.0000 9.0000 8.0000
10.0000 11.0000 12.0000 11.0000
13.0000 14.0000 15.0000 14.0000
16.0000 17.0000 18.0000 10.3333
but that doesn't look like what you want.
Instead you are forced to use a couple of lines:
c = conv(a(:,2),mask,'valid')/3;
a(1:length(c),4) = c
a =
1 2 3 5
4 5 6 8
7 8 9 11
10 11 12 14
13 14 15 0
16 17 18 0

I would like to average the first n columns based on duplicate values of the n+1th column

I have a matrix with duplicate numbers in one of the columns. I would like to average the rows with duplicate numbers. For example, I have duplicate values in a matrix A in column 3:
A =
1 2 1
4 4 2
5 4 2
4 5 2
5 5 3
10 3 3
I would like to get
B =
1 2 1
4.3333 4.3333 2.0000
7.5000 4.0000 3.0000
where each row is the average values of the duplicate rows of column 3.
Can anyone help?
The following one-liner averages all rows with duplicate values in column N.
cell2mat(arrayfun(#(x)mean(A(A(:, N)==x,:),1), unique(A(:, N)), 'Uniform', 0))
Example
Let's follow your example and average all lines with duplicates in column 3:
A = [1 2 1; 4 4 2; 5 4 2; 4 5 2; 5 5 3; 10 3 3];
N = 3;
B = cell2mat(arrayfun(#(x)mean(A(A(:, N)==x,:),1), unique(A(:,N)), 'Uniform', 0))
The result is:
B =
1.0000 2.0000 1.0000
4.3333 4.3333 2.0000
7.5000 4.0000 3.0000

Inserting rows into matrix matlab

I have a ~ 100000/2 matrix. I'd like to go down the columns, average each vertically adjacent value, and insert that value in between the two values. For example...
1 2
3 4
4 6
7 8
would become
1 2
2 3
3 4
3.5 5
4 6
5.5 7
7 8
I'm not sure if there is a terse way to do this in matlab. I took a look at http://www.mathworks.com/matlabcentral/fileexchange/9984 but it seems to insert all of the rows in a matrix into the other one at a specific point. Obviously it can still be used, but just wondering if there is a simpler way.
Any help is appreciated, thanks.
Untested:
% Take the mean of adjacent pairs
x_mean = ([x; 0 0] + [0 0; x]) / 2;
% Interleave the two matrices
y = kron(x, [1;0]) + kron(x_mean(1:end-1,:), [0;1]);
%# works for any 2D matrix of size N-by-M
X = rand(100,2);
adjMean = mean(cat(3, X(1:end-1,:), X(2:end,:)), 3);
Y = zeros(2*size(X,1)-1, size(X,2));
Y(1:2:end,:) = X;
Y(2:2:end,:) = adjMean;
octave-3.0.3:57> a = [1,2; 3,4; 4,6; 7,8]
a =
1 2
3 4
4 6
7 8
octave-3.0.3:58> b = (circshift(a, -1) + a) / 2
b =
2.0000 3.0000
3.5000 5.0000
5.5000 7.0000
4.0000 5.0000
octave-3.0.3:60> reshape(vertcat(a', b'), 2, [])'(1:end-1, :)
ans =
1.0000 2.0000
2.0000 3.0000
3.0000 4.0000
3.5000 5.0000
4.0000 6.0000
5.5000 7.0000
7.0000 8.0000