Add arrays that do not have the same length - arrays

Problem
Is there a smart way to add arrays/vectors that do not have the same column length, by just adding zeros to columns that are too short? I have additions/subtractions that include multiple variables.
so that:
a=[ 1; 2; 3]; b=[1;5]
a+b=[2; 5; 3]
or:
a-b=[0;-3;3]
instead of:
Error using +
Matrix dimensions must agree.
What I did
b(numel(a),1) = 0;
This works fine, if adding a few variables, but this gets quite annoying while repeating for multiple variables, especially if you don't know which has the longest column. Hence the question if there is an easier fast way for an addition of different column lengths.
Edit
The question really is, if there is a way to automate this for having more than "just a few" variables.

You need to do it more or less manually. For example:
s = [];
s(1,1:numel(a)) = a;
s(2,1:numel(b)) = b; % assigning a row (or column) automatically pads with zeros
% if needed. This works irrespective of which is bigger, a or b
s = sum(s,1); % sum along each column
If you have several variables, it's probably better to put them in a cell array, so you can loop over them:
c = {[1; 2; 3] [1;5] [10 20 30 40]}; % cell array of all "variables"
s = [];
for k = 1:numel(c);
s(k,1:numel(c{k})) = c{k}; % this zero-pads preceding cells' contents if needed
end
s = sum(s,1); % sum along each column
The above may be slow because s is dynamically reallocated. You can preallocate as follows:
c = {[1; 2; 3] [1;5] [10 20 30 40]}; % cell array of all "variables"
m = max(cellfun(#numel, c)); % maximum vector size
s = zeros(numel(c), m); % preallocate and initiallize to zeros
for k = 1:numel(c);
s(k,1:numel(c{k})) = c{k}; % some entries maybe be left as zero
end
s = sum(s,1); % sum along each column

a=[ 1; 2; 3]; b=[1;5];
if numel(b)~=numel(a)
if numel(b)<numel(a) % If b is shorter, extend it
b = [b; zeros(numel(a)-numel(b),1)];
else % If a is shorter, extend it
a = [a; zeros(numel(b)-numel(a),1)];
end
end
a+b

A=[1, 2, 3]; B=[1,5];
[A,zeros(1,length(B)-length(A))]+[B,zeros(1,length(A)-length(B))]
ans =
2 7 3
[A,zeros(1,length(B)-length(A))]-[B,zeros(1,length(A)-length(B))]
ans =
0 -3 3
Stick them in a function and you are done

Related

How to create matrix in matlab that adds cells horizontally

I want to create a matrix, for example, a 1xn, where n is determined by another input.
For example, I have the following:
Example = [3 5 7 9];
New_matrix = [int_col(Example (1)) int_col(Example (2)) int_col(Example (3)) int_col(Example (4))];
This New_matrix is how I want my outputs.
However, for my actual data input, my Example matrix is 1x47. How can I put this in a for loop so it gives the outputs of 1 to a specified number for all of the length Example? (keeping it horizontal as well)
Help function I used is added here:
% Helper function added as well
function v = int_col(n)
v = zeros(1,n);
for index=1:n
val = randi(n);
while (val == index || any(val == v))
val = randi(n);
if (index == n && (any(v == n) ~= 1))
val = n;
break
end
end
v(index) = val;
if (index == n && val == n)
% v(n) = v(n-1);
% v(n-1) = n;
end
if n == 1
v = 1;
end
end
v = v';
v = (sortrows(v))';
end
Are you trying to reimplement randperm() in int_col()? I'm not sure what the purpose of running sortrows() on each column separately is, but I guess this may be what you wanted:
% Make up vector of column lengths
Example=randi(60,[1 47]);
% Create matrix
New_matrix=nan([max(Example) numel(Example)]);
for i=1:numel(Example)
New_matrix(1:Example(i),i)=randperm(Example(i));
end
% Sort matrix rows from left to right
New_matrix=sortrows(New_matrix);
Please note that all columns in a matrix must have the same number of entries, so you will need to pad shorter columns with some value; a typical choice is NaN, which is treated as a special value by sortrows() and some other functions.
~
If my guess is incorrect and you do mean your code as it currently is, you just need:
New_matrix=[];
for i=1:numel(Example)
New_matrix=[New_matrix 1:Example(i)];
end
Changing the size of New_matrix on every iteration is generally not good practice, but for 47 iterations only this should be fine.

How do I delete all-zero pages from a 3D matrix in a loop?

How can I delete all-zero pages from a 3D matrix in a loop?
I have come up with the following code, though it is not 'entirely' correct, if at all. I am using MATLAB 2019b.
%pseudo data
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;
xy = cat(3,x,y); %this is a 3x2x4 array; (:,:,1) and (:,:,2) are all zeros,
% (:,:,3) is ones and zeros, and (:,:,4) is all ones
%my aim is to delete the arrays that are entirely zeros i.e. xy(:,:,1) and xy(:,:,2),
%and this is what I have come up with; it doesn't delete the arrays but instead,
%all the ones.
for ii = 1:size(xy,3)
for idx = find(xy(:,:,ii) == 0)
xy(:,:,ii) = strcmp(xy, []);
end
end
Use any to find indices of the slices with at least one non-zero value. Use these indices to extract the required result.
idx = any(any(xy)); % idx = any(xy,[1 2]); for >=R2018b
xy = xy(:,:,idx);
I am unsure what you'd expect your code to do, especially given you're comparing strings in all-numerical arrays. Here's a piece of code which does what you desire:
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;
xy = cat(3,x,y);
idx = ones(size(xy,3),1,'logical'); % initialise catching array
for ii = 1:size(xy,3)
if sum(nnz(xy(:,:,ii)),'all')==0 % If the third dimension is all zeros
idx(ii)= false; % exclude it
end
end
xy = xy(:,:,idx); % reindex to get rid of all-zero pages
The trick here is that sum(xy(:,:,ii),'all')==0 is zero iff all elements on the given page (third dimension) are zero. In that case, exclude it from idx. Then, in the last row, simply re-index using logical indexing to retain only pages whit at least one non-zero element.
You can do it even faster, without a loop, using sum(a,[1 2]), i.e. the vectorial-dimension sum:
idx = sum(nnz(xy),[1 2])~=0;
xy = xy(:,:,idx);

Column vector switching values

I have a column vector X of size N-by-1 with two values: 10 and 25. I want to switch those values, i.e. every element with value 10 should become 25 and vice versa.
Pseudo-code:
A = [ 10; 25; 25; 10; 25; 25; 10]
NewNew = find(A == 10)
NewNew2 = find(A == 25)
NewNew3 = [ ];
for NewNew3
if NewNew
NewNew=25
else NewNew2
NewNew2=10
end
I tried this now still no success:
for B = 1:size(A)
if A == 10
A = 25
elseif A == 25
A = 10
end
end
How can I switch values?
If you only have two values, why not subtracting from their addition:
A = 35 - A;
N=10;
X = randi([1 2],N,1);
A = zeros(size(X)); % create output vector
A(X==2)=1; % whenever X==2, set A=1
A(X==1) = 2; % whenever X==1, set A=2
[X A]
ans =
1 2
2 1
2 1
1 2
2 1
1 2
1 2
2 1
2 1
2 1
You can do this with logical indexing. Just make sure to get a separate output vector, as setting X(X==1)=2 will make your whole vector 2, and then calling X(X==2)=1 sets the whole vector to 1. If necessary, use X=A at the end.
Final advice: your for loop needs indexing, which is about as basic as MATLAB goes. You can solve this problem with a for loop, although I'd recommend using logical indexing as per the above. I strongly suggest you to either take a basic course in MATLAB, or read the MathWork's own tutorial on MATLAB.
There are some other options you can consider.
1.) (Irrelevant option. Use ibancg's option, cleaner and faster.)
If you have just those two values, a simpler and faster solution would be to initialize to one of these two numbers and flip just a subset of the numbers:
A = [ 10; 25; 25; 10; 25; 25; 10];
X = ones(size(A)) * 10; % initialize X with 10.
X(A == 10) = 25; % Set values where A is 10 to 25.
2.) (irrelevant, too slow): Another possibility if you know that some numbers will never appear is to use temporary third number and avoid creating a new matrix - operate kind of similar to the typical "swap two numbers" recipe.
A(A == 10) = 1; % Temp value
A(A == 25) = 10;
A(A == 1) = 25;
3.) Finally, the third option would be to save logical matrix and then overwrite A.
Ais10 = A==10;
Ais25 = A==25;
A(Ais10) = 25;
A(Ais25) = 10;
Now, for the benchmarks: I used very simple script, varying N and M.
tic
for i = 1 : M
X1 = randi([1 2],N,1);
... % depends on test cases. Result can be in X or A.
toc
Therefore, it also times randi, but as it is a part of every code it should only mask the relative speed differences, keeping absolute ones the same. Computer is i7 4770k, 32GB RAM.
Row vector has 1000 elements, 1M runs; 100M elements, 10 runs; 1B elements, 1 run; 3B elements, 1 run
Codes that rely on having just 2 values:
1I): 18.3 s; 20.7 s; 20.3 s; 63.6 s
1Z): 33.0 s; 38.2 s; 38.0 s; NA (out of memory)
Code relies on lacking one value that can be used for swap:
2Z): 54.0 s; 60.5 s; 60.0 s; 659 s
Code handles arbitrary data, swapping 2 values.
3A): 45.2 s; 50.5 s; 49.0 s; NA (out of memory)
3Z): 41.0 s; 46.1 s; 45.8 s; NA (out of memory)
So, to summarize, Ibancg's option is much faster and simpler than the rest and should be used if possible (= just those 2 different values in the vector). My 2nd option is far too slow to be relevant. Rather do step by step swap on the vector using any of the general methods. Either general option is fine.

How to average independent consecutive blocks of an array as fast as possible?

Here is the problem:
data = 1:0.5:(8E6+0.5);
An array of 16 million points, needs to be averaged every 10,000 elements.
Like this:
x = mean(data(1:10000))
But repeated N times, where N depends on the number of elements we average over
range = 10000;
N = ceil(numel(data)/range);
My current method is this:
data(1) = mean(data(1,1:range));
for i = 2:N
data(i) = mean(data(1,range*(i-1):range*i));
end
How can the speed be improved?
N.B: We need to overwrite the original array of data (essentially bin the data and average it)
data = 1:0.5:(8E6-0.5); % Your data, actually 16M-2 elements
N = 1e4; % Amount to average over
tmp = mod(numel(data),N); % find out whether it fits
data = [data nan(1,N-tmp)]; % add NaN if necessary
data2=reshape(data,N,[]); % reshape into a matrix
out = nanmean(data2,1); % get average over the rows, ignoring NaN
Visual confirmation that it works using plot(out)
Note that technically you can't do what you want if mod(numel(data),N) is not equal to 0, since then you'd have a remainder. I elected to average over everything in there, although ignoring the remainder is also an option.
If you're sure mod(numel(data),N) is zero every time, you can leave all that out and reshape directly. I'd not recommend using this though, because if your mod is not 0, this will error out on the reshape:
data = 1:0.5:(8E6+0.5); % 16M elements now
N = 1e4; % Amount to average over
out = sum(reshape(data,N,[]),1)./N; % alternative
This is a bit wasteful, but you can use movmean (which will handle the endpoints the way you want it to) and then subsample the output:
y = movmean(x, [0 9999]);
y = y(1:10000:end);
Even though this is wasteful (you're computing a lot of elements you don't need), it appears to outperform the nanmean approach (at least on my machine).
=====================
There's also the option to just compensate for the extra elements you added:
x = 1:0.5:(8E6-0.5);
K = 1e4;
Npad = ceil(length(x)/K)*K - length(x);
x((end+1):(end+Npad)) = 0;
y = mean(reshape(x, K, []));
y(end) = y(end) * K/(K - Npad);
reshape the data array into a 10000XN matrix, then compute the mean of each column using the mean function.

MATLAB: Run Data Matrix through a loop and add results to data matrix

What I ultimately need is to input a 2-column matrix, run it through a bunch of conditions and have an output of the 2 original columns plus an additional three.
My initial data matrix I split into several arrays according to time (the second column) and continue with applying my conditions on each array individually:
A = arrayfun(#(x) M(M(:, 2) == x, :), unique(M(:,2)), 'uniformoutput', false);
n = numel(A);
k = 0;
for i = 1:n % for each # of arrays
matrix = A{i}; % array i
dat = size(matrix);
length = dat(1,1); % length of i array
adductName = zeros(length, 1); % preallocate columns
actualMass = zeros(length, 1);
adductMass = zeros(length, 1);
%... continued with conditions here's an example of one
for r = 1:length % for the length of array i
mass = matrix(1,r);
M = mass-1;
k=k+1;
if any(M == matrix(:, 1)) % if any M matches rest of column 1 in array
adductName(k) = 'M';
actualMass(k) = M;
adductMass(k) = mass;
else
adductName(k) = 'None';
actualMass(k) = 0;
adductMass(k) = 0;
adductName, actualMass and adductMass are the three additional columns I need added in my output
My question is, how do I recombine all of my arrays, A{i}'s along with my additional three columns into one data matrix to be outputted?
You can either use the [ ] operator or explicit calls to horzcat or vertcat to concatenate matrices in different ways. See the Creating and Concatenating Matrices documentation part for further reference.

Resources