I see two major way to simplify and speed this up. First, rather than copying a
to c
at every step of the loop, you can define c
to include a
from the beginning. Second, you can find all the nonzero, non-NaN values in a vectorized manner at the very beginning This reduces the time for me be about 1/2 for a large (~10000 row) random data set.
function c = catnonzero_2(a, b)
c=[a, NaN(size(b))];
agood = a~=0;
bgood = b~=0;
for ii=1:size(a,1)
ailast=find(agood(ii,:), 1, 'last');
if isempty(ailast)
ailast = 0;
end
bilast=find(bgood(ii,:), 1, 'last');
if ~isempty(bilast)
c(ii, ailast+1:ailast+bilast) = b(ii, 1:bilast);
else
c(ii, ailast+1:end) = NaN;
end
end
end
This can be improved even more by separating out the rows that can be vectorized from those that can't.
- I find all the rows of
a
and b
with some non-null values ("good" rows). Rows that aren't good are then filled with NaN
.
- I find all the rows of
a
and b
where the last value is non-null ("perfect" rows).
- For rows that are good but not perfect ("imperfect" rows):
- For
b
I can loop over them an put the NaN
values at the end. This allows me to treat all b
rows as either "perfect" or "bad", since the "imperfect" rows already have the NaN
values at the end and thus can be used in the entirety.
- For
a
, I can loop over the rows where a
are imperfect and b
are not good and put NaN
at the end of those. Where b
is good, b
will be ovewriting those anyway so it isn't necessary as long as b
is at least as long as a
.
- I make
c
like in the previous example.
- In cases where
a
is perfect and b
is good, I can just write b
to c
at the point where a
ends.
- In the cases where
a
is bad and b
is food, I can just write b
from the start of c
.
In the remaining cases, I have to calculate where b
starts, and then write b
.
3.
This reduces the time for me by about another 1/4:
function c = catnonzero_3(a, b)
alen = size(a, 2);
blen = size(b, 2);
agood = a~=0;
bgood = b~=0;
agoodrows = any(agood, 2);
bgoodrows = any(bgood, 2);
aperfectrows = agood(:,end);
bperfectrows = bgood(:,end);
aimperfectrows = agoodrows & ~aperfectrows;
a(~agoodrows, :) = NaN;
b(~bgoodrows, :) = NaN;
if alen <= blen
apadtarg = aimperfectrows & ~bgoodrows;
else
apadtarg = aimperfectrows;
end
for ii=find(apadtarg)'
a(ii, ailast(ii)+1:end) = NaN;
end
for ii=find(bgoodrows & ~bperfectrows)'
b(ii, bilast(ii)+1:end) = NaN;
end
c=[a, NaN(size(b))];
ailast = @(ii) find(agood(ii,:), 1, 'last');
bilast = @(ii) find(bgood(ii,:), 1, 'last');
aperfbgood = aperfectrows & bgoodrows;
c(aperfbgood, size(a, 2)+1:end) = b(aperfbgood, :);
abadbgood = ~agoodrows & bgoodrows;
c(abadbgood, 1:blen) = b(abadbgood, :);
for ii=find(aimperfectrows & bgoodrows)'
ajj = ailast(ii);
c(ii, ajj+1:ajj+blen) = b(ii, :);
end
end
And here is the data set I used to test (your version is catnonzero_1
:
a=randi([-11, 10], 100000,80);
b=randi([-11, 10], 100000,100);
a(a==-11) = NaN;
b(b==-11) = NaN;
timeit(@() catnonzero_1(a, b))
timeit(@() catnonzero_2(a, b))
timeit(@() catnonzero_3(a, b))
And the result:
>> run_w_time
ans =
0.889284394000000
ans =
0.411362394000000
ans =
0.102184394000000
ans =
1
ans =
1
c1 = catnonzero_1(a, b);
c2 = catnonzero_2(a, b);
c3 = catnonzero_3(a, b);
all(all(c1(~isnan(c1))==c2(~isnan(c2))))
all(all(c1(~isnan(c1))==c3(~isnan(c3))))
a
andb
represent, and why do you want to do this operation? \$\endgroup\$c
containing an index that determines what I have to do with the values in a matrix A with the same dimensions. Unfortunately A got split ina
andb
during the data analysis. The fastest solution is now to merge them again. The data are temperature and stratification data of ocean water columns. \$\endgroup\$-Inf
\$\endgroup\$