I have several matrices <1x1000> containing integers such as:
matrix = [0,0,0,0,0,30,30,30,40,40,50,50,50,40,0,0,0,30,30,30]
I want to print (disp, and later plot) them like this: 30,40,50,40,30. Basically ignore the duplicates if they come after each other.
Another example:
matrix = [0,0,0,0,10,10,10,10,50,50,50,50,10,10,10,50,50] shall give: 10,50,10,50
Help is very much appreciated!
Use this:
[~,c]=find([NaN diff(matrix)]);
output=matrix(c);
output = output(output~=0)
and to plot the output, simply use: plot(output)
Result = 0;
% loop over all nonzero values in matrix
for Element = matrix
if Element == Result(end)
% skip if equal
continue
else
% add new value
Result(end+1) = Element;
end
end
% discard zero entries
Result = Result(Result ~= 0);
All solutions provided so far use either loops or the function find which are both inefficient.
Just use matrix indexation:
[matrix((matrix(1:end-1)-matrix(2:end))~=0), matrix(end)]
ans =
0 30 40 50 40 0 30
By the way in your example are you discarting the 0s even if they come in repeated sequences?
Lets call the output matrix um then
um(1) = matrix(1);
j = 1;
for i=2: length(matrix)
% Ignore repeating numbers
if (um(j) ~= matrix(i))
j = j + 1;
um(j) = matrix(i);
end
end
% Remove zeros
um = um(um~=0);
Related
I'm currently working on creating a histogram of Altitudes at which a type of atmospheric instability happens. To be specific, it is when the values of what we call, N^2 is less than zero. This is where the problem comes in. I am trying to plot the occurrence frequency against the altitudes.
load /data/matlabst/DavidBloom/N_square_Ri_number_2005.mat
N_square(N_square > 0) = 0;
N_square = abs(N_square);
k = (1:87);
H = 7.5;
p0 = 101325;
nbins = (500);
N_square(N_square==0)=[];
Alt = zeros(1,578594);
PresNew = squeeze(N_square(:,:,k,:));
for lati = 1:32
for long = 1:64
for t = 1:1460
for k = 1:87
Alt(1,:) = -log((PresNew)/p0)*H;
end
end
end
end
So, let me explain what I am doing. I'm loading a file with all these different variables. Link To Image This shows the different variables it displays. Next, I take the 4-D matrix N_square and I filter all values greater than zero to equal 0. Then I take the absolute value of the leftover negative values. I then define several variables and move on to the next filtering.
(N_square(N_square==0)=[];
The goal of this one was give just discard all values of N_square that were 0. I think this is where the problem begins. Jumping down to the for loop, I am then taking the 3rd dimension of N_square and converting pressure to altitude.
My concern is that when I run this, PresNew = squeeze(N_square(:,:,k,:)); is giving me the error.
Error in PlottingN_2 (line 10)
PresNew = squeeze(N_square(:,:,k,:));
And I have no idea why.
Any thoughts or suggestions on how I could avoid this catastrophe and make my code simpler? Thanks.
When you remove random elements from a multi-dimensional array, they are removed but it can no longer be a valid multi-dimensional array because it has holes in it. Because of this, MATLAB will collapse the result into a vector, and you can't index into the third dimension of a vector like you're trying.
data = magic(3);
% 8 1 6
% 3 5 7
% 4 9 2
% Remove all values < 2
data(data < 2) = []
% 8 3 4 5 9 6 7 2
data(2,3)
% Index exceeds matrix dimensions.
The solution is to remove the 0 values after your indexing (i.e. within your loop).
Alt = zeros(1,578594);
for lati = 1:32
for long = 1:64
for t = 1:1460
for k = 1:87
% Index into 4D matrix
PresNew = N_square(:,:,k,:);
% NOW remove the 0 values
PresNew(PresNew == 0) = [];
Alt(1,:) = -log((PresNew)/p0)*H;
end
end
end
end
Let's say we have three m-by-n matrices of equal size: A, B, C.
Every column in C represents a time series.
A is the running maximum (over a fixed window length) of each time series in C.
B is the running minimum (over a fixed window length) of each time series in C.
Is there a way to determine T in a vectorized way?
[nrows, ncols] = size(A);
T = zeros(nrows, ncols);
for row = 2:nrows %loop over the rows (except row #1).
for col = 1:ncols %loop over the columns.
if C(row, col) > A(row-1, col)
T(row, col) = 1;
elseif C(row, col) < B(row-1, col)
T(row, col) = -1;
else
T(row, col) = T(row-1, col);
end
end
end
This is what I've come up with so far:
T = zeros(m, n);
T(C > circshift(A,1)) = 1;
T(C < circshift(B,1)) = -1;
Well, the trouble was the dependency with the ELSE part of the conditional statement. So, after a long mental work-out, here's a way I summed up to vectorize the hell-outta everything.
Now, this approach is based on mapping. We get column-wise runs or islands of 1s corresponding to the 2D mask for the ELSE part and assign them the same tags. Then, we go to the start-1 along each column of each such run and store that value. Finally, indexing into each such start-1 with those tagged numbers, which would work as mapping indices would give us all the elements that are to be set in the new output.
Here's the implementation to fulfill all those aspirations -
%// Store sizes
[m1,n1] = size(A);
%// Masks corresponding to three conditions
mask1 = C(2:nrows,:) > A(1:nrows-1,:);
mask2 = C(2:nrows,:) < B(1:nrows-1,:);
mask3 = ~(mask1 | mask2);
%// All but mask3 set values as output
out = [zeros(1,n1) ; mask1 + (-1*(~mask1 & mask2))];
%// Proceed if any element in mask3 is set
if any(mask3(:))
%// Row vectors for appending onto matrices for matching up sizes
mask_appd = false(1,n1);
row_appd = zeros(1,n1);
%// Get 2D mapped indices
df = diff([mask_appd ; mask3],[],1)==1;
cdf = cumsum(df,1);
offset = cumsum([0 max(cdf(:,1:end-1),[],1)]);
map_idx = bsxfun(#plus,cdf,offset);
map_idx(map_idx==0) = 1;
%// Extract the values to be used for setting into new places
A1 = out([df ; false(1,n1)]);
%// Map with the indices obtained earlier and set at places from mask3
newval = [row_appd ; A1(map_idx)];
mask3_appd = [mask_appd ; mask3];
out(mask3_appd) = newval(mask3_appd);
end
Doing this vectorized is rather difficult because the current row's output depends on the previous row's output. Doing vectorized operations usually means that each element should stand out on its own using some relationship that is independent of the other elements that surround it.
I don't have any input on how you would achieve this without a for loop but I can help you reduce your operations down to one instead of two. You can do the assignment vectorized per row, but I can't see how you'd do it all in one shot.
As such, try something like this instead:
[nrows, ncols] = size(A);
T = zeros(nrows, ncols);
for row = 2:nrows
out = T(row-1,:); %// Change - Make a copy of the previous row
out(C(row,:) > A(row-1,:)) = 1; %// Set those elements of C
%// in the current row that are larger
%// than the previous row of A to 1
out(C(row,:) < B(row-1,:)) = -1; %// Same logic but for B now and it's
%// less than and the value is -1 instead
T(row,:) = out; %// Assign to the output
end
I'm currently figuring out how to do this with any loops whatsoever. I'll keep you posted.
In MATLAB I have a vector with size 1-by-3. Now I need to insert an element to this vector but sometimes
this number must be first element of this vector, sometimes second and so on.
Does anyone know how I could do this?
Thanks
Your question is a bit vague, but if you mean you need to insert a new element into an existing vector, here's how it can be done:
>> insertAfter = 1; % insert element after first
>> newVec = cat(2, v(1:insertAfter), newElement, v( (insertAfter+1):end ) );
Insert element I to vector V at location N
V = [V(1:N-1) I V(N:end)]
Test
V = zeros(1,3);
I = 1;
N = 2;
V = [V(1:N-1) I V(N:end)]
V =
0 1 0 0
There are more than a few ways to do this, so you'll just have to take your pick. Here's one that I would prefer for in-place insertion of the scalar newEl at location ii of vector v:
v(ii:end+1) = [newEl v(ii:end)];
clear all
clc
v1 = [ 3 2 8 9 ] % The first vector
q=length(v1) % The length of the first vector
v2=1:q+1 % Creating a new vector with length old + 1
v2(1:q)=v1 % Changing the first part of the vector to the old (v1) vector
v1=v2 % To go back to the same name of the first vector
I am looking for a 'good' way to find a matrix (pattern) in a larger matrix (arbitrary number of dimensions).
Example:
total = rand(3,4,5);
sub = total(2:3,1:3,3:4);
Now I want this to happen:
loc = matrixFind(total, sub)
In this case loc should become [2 1 3].
For now I am just interested in finding one single point (if it exists) and am not worried about rounding issues. It can be assumed that sub 'fits' in total.
Here is how I could do it for 3 dimensions, however it just feels like there is a better way:
total = rand(3,4,5);
sub = total(2:3,1:3,3:4);
loc = [];
for x = 1:size(total,1)-size(sub,1)+1
for y = 1:size(total,2)-size(sub,2)+1
for z = 1:size(total,3)-size(sub,3)+1
block = total(x:x+size(sub,1)-1,y:y+size(sub,2)-1,z:z+size(sub,3)-1);
if isequal(sub,block)
loc = [x y z]
end
end
end
end
I hope to find a workable solution for an arbitrary number of dimensions.
Here is low-performance, but (supposedly) arbitrary dimensional function. It uses find to create a list of (linear) indices of potential matching positions in total and then just checks if the appropriately sized subblock of total matches sub.
function loc = matrixFind(total, sub)
%matrixFind find position of array in another array
% initialize result
loc = [];
% pre-check: do all elements of sub exist in total?
elements_in_both = intersect(sub(:), total(:));
if numel(elements_in_both) < numel(unique(sub))
% if not, return nothing
return
end
% select a pivot element
% Improvement: use least common element in total for less iterations
pivot_element = sub(1);
% determine linear index of all occurences of pivot_elemnent in total
starting_positions = find(total == pivot_element);
% prepare cell arrays for variable length subscript vectors
[subscripts, subscript_ranges] = deal(cell([1, ndims(total)]));
for k = 1:length(starting_positions)
% fill subscript vector for starting position
[subscripts{:}] = ind2sub(size(total), starting_positions(k));
% add offsets according to size of sub per dimension
for m = 1:length(subscripts)
subscript_ranges{m} = subscripts{m}:subscripts{m} + size(sub, m) - 1;
end
% is subblock of total equal to sub
if isequal(total(subscript_ranges{:}), sub)
loc = [loc; cell2mat(subscripts)]; %#ok<AGROW>
end
end
end
This is based on doing all possible shifts of the original matrix total and comparing the upper-leftmost-etc sub-matrix of the shifted total with the sought pattern subs. Shifts are generated using strings, and are applied using circshift.
Most of the work is done vectorized. Only one level of loops is used.
The function finds all matchings, not just the first. For example:
>> total = ones(3,4,5,6);
>> sub = ones(3,3,5,6);
>> matrixFind(total, sub)
ans =
1 1 1 1
1 2 1 1
Here is the function:
function sol = matrixFind(total, sub)
nd = ndims(total);
sizt = size(total).';
max_sizt = max(sizt);
sizs = [ size(sub) ones(1,nd-ndims(sub)) ].'; % in case there are
% trailing singletons
if any(sizs>sizt)
error('Incorrect dimensions')
end
allowed_shift = (sizt-sizs);
max_allowed_shift = max(allowed_shift);
if max_allowed_shift>0
shifts = dec2base(0:(max_allowed_shift+1)^nd-1,max_allowed_shift+1).'-'0';
filter = all(bsxfun(#le,shifts,allowed_shift));
shifts = shifts(:,filter); % possible shifts of matrix "total", along
% all dimensions
else
shifts = zeros(nd,1);
end
for dim = 1:nd
d{dim} = 1:sizt(dim); % vectors with subindices per dimension
end
g = cell(1,nd);
[g{:}] = ndgrid(d{:}); % grid of subindices per dimension
gc = cat(nd+1,g{:}); % concatenated grid
accept = repmat(permute(sizs,[2:nd+1 1]), [sizt; 1]); % acceptable values
% of subindices in order to compare with matrix "sub"
ind_filter = find(all(gc<=accept,nd+1));
sol = [];
for shift = shifts
total_shifted = circshift(total,-shift);
if all(total_shifted(ind_filter)==sub(:))
sol = [ sol; shift.'+1 ];
end
end
For an arbitrary number of dimensions, you might try convn.
C = convn(total,reshape(sub(end:-1:1),size(sub)),'valid'); % flip dimensions of sub to be correlation
[~,indmax] = max(C(:));
% thanks to Eitan T for the next line
cc = cell(1,ndims(total)); [cc{:}] = ind2sub(size(C),indmax); subs = [cc{:}]
Thanks to Eitan T for the suggestion to use comma-separated lists for a generalized ind2sub.
Finally, you should test the result with isequal because this is not a normalized cross correlation, meaning that larger numbers in a local subregion will inflate the correlation value potentially giving false positives. If your total matrix is very inhomogeneous with regions of large values, you might need to search other maxima in C.
I need to take away a random number of columns from an arbitrarily large matrix, I've put my attempt below, but I'm certain that there is a better way.
function new = reduceMatrices(original, colsToTakeAway)
a = colsToTakeAway(1);
b = colsToTakeAway(2);
c = colsToTakeAway(3);
x = original(1:a-1);
y = original(a+1:b-1);
z = original(b+1:c-1);
if c == size(original, 2);
new = [x,y,z];
elseif (c+1) == size(original, 2);
new = [x,y,z,c+1]
else
new = [x,y,z,c+1:size(original, 2)];
end
Here's one approach. First, generate a row vector of random numbers with numcols elements, where numcols is the number of columns in the original matrix:
rc = rand(1,numcols)
Next make a vector of 1s and 0s from this, for example
lv = rc>0.75
which will produce something like
0 1 1 0 1
and you can use Matlab's logical indexing feature to write
original(:,lv)
which will return only those columns of original which correspond to the 1s in lv.
It's not entirely clear from your question how you want to make the vector of column selections, but this should give you some ideas.
function newM = reduceMatrices(original, colsToTakeAway)
% define the columns to keep := cols \ colsToTakeAway
colsToKeep = setdiff(1:size(original,2), colsToTakeAway);
newM = original(:, colsToKeep);
end