Skip to main content
expand explanation
Source Link
TheBlackCat
  • 2.4k
  • 11
  • 10

This can be improved even more by separating out the rows that can be vectorized from those that can't. First I write NaN values to the end of all the b rows. This

  1. 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.

  2. I find all the rows of a and b where the last value is non-null ("perfect" rows).

  3. For rows that are good but not perfect ("imperfect" rows):

  4. 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.

  5. 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.

  6. I make c like in the previous example.

  7. In cases where a is perfect and b is good, I can just write b to c at the point where a ends.

  8. In the cases where a is bad and b is food, I can just write b from the start of c.

  9. In the remaining cases, I have to calculate where b starts, and then write b.

This reduces the time for me by about another 1/4:

function c = catnonzero_3(a, b)
    c=[a,alen NaN(= size(b)a, 2)];;
    blen = size(b, 2);
    
    agood = a~=0;
    bgood = b~=0;
    
    ailast = @(ii) find(agood(ii,:), 1, 'last');
    bilast = @(ii) find(bgood(ii,:), 1, 'last');
    
    agoodrows = any(agood, 2);
    bgoodrows = any(bgood, 2);
    
    aperfectrows = agood(:,end);
    bperfectrows = bgood(:,end);
    
    aimperfectrows = agoodrows & ~aperfectrows;
    
    a(~agoodrows, :) = NaN;
    b(~bgoodrows, :) = NaN; 

    forif ii=find(bgoodrowsalen <= blen
       apadtarg = aimperfectrows & ~bperfectrows~bgoodrows;
    else
       apadtarg = aimperfectrows;
    end

    for ii=find(apadtarg)'
        ba(ii, bilastailast(ii)+1:end) = NaN;
    end
    for ii=find(aimperfectrowsbgoodrows & ~bgoodrows~bperfectrows)'
        cb(ii, ailastbilast(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

This can be improved even more by separating out the rows that can be vectorized from those that can't. First I write NaN values to the end of all the b rows. This reduces the time for me by about another 1/4:

function c = catnonzero_3(a, b)
    c=[a, NaN(size(b))];
    blen = size(b, 2);
    
    agood = a~=0;
    bgood = b~=0;
    
    ailast = @(ii) find(agood(ii,:), 1, 'last');
    bilast = @(ii) find(bgood(ii,:), 1, 'last');
    
    agoodrows = any(agood, 2);
    bgoodrows = any(bgood, 2);
    
    aperfectrows = agood(:,end);
    bperfectrows = bgood(:,end);
    
    aimperfectrows = agoodrows & ~aperfectrows;
    
    a(~agoodrows, :) = NaN;
    b(~bgoodrows, :) = NaN;
    for ii=find(bgoodrows & ~bperfectrows)'
        b(ii, bilast(ii)+1:end) = NaN;
    end
    for ii=find(aimperfectrows & ~bgoodrows)'
        c(ii, ailast(ii)+1:end) = NaN;
    end
    
    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

This can be improved even more by separating out the rows that can be vectorized from those that can't.

  1. 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.

  2. I find all the rows of a and b where the last value is non-null ("perfect" rows).

  3. For rows that are good but not perfect ("imperfect" rows):

  4. 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.

  5. 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.

  6. I make c like in the previous example.

  7. In cases where a is perfect and b is good, I can just write b to c at the point where a ends.

  8. In the cases where a is bad and b is food, I can just write b from the start of c.

  9. In the remaining cases, I have to calculate where b starts, and then write b.

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
added 232 characters in body
Source Link
TheBlackCat
  • 2.4k
  • 11
  • 10

This can be improved a littleeven more by separating out the rows that can be vectorized from those that can't. First I write NaN values to the end of all the b rows. This reduces the time for me by about another 1/4:

function c = catnonzero_3(a, b)
    c=[a, NaN(size(b))];
    blen = size(b, 2);
    
    agood = a~=0;
    bgood = b~=0;
    
    ailast = @(ii) find(agood(ii,:), 1, 'last');
    bilast = @(ii) find(bgood(ii,:), 1, 'last');
    
    agoodrows = any(agood, 2);
    bgoodrows = any(bgood, 2);
    
    aperfectrows = agood(:,end);
    bperfectrows = bgood(:,end);
    
    aimperfectrows = agoodrows & ~aperfectrows;
    
    a(~agoodrows, :) = NaN;
    b(~bgoodrows, :) = NaN;
    for ii=find(bgoodrows & ~bperfectrows)'
        b(ii, bilast(ii)+1:end) = NaN;
    end
    for ii=find(aimperfectrows & ~bgoodrows)'
        c(ii, ailast(ii)+1:end) = NaN;
    end
    
    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

This can be improved a little more by separating out the rows that can be vectorized from those that can't. This reduces the time for me by about another 1/4:

function c = catnonzero_3(a, b)
    c=[a, NaN(size(b))];
    blen = size(b, 2);
    
    agood = a~=0;
    bgood = b~=0;
    
    ailast = @(ii) find(agood(ii,:), 1, 'last');
    bilast = @(ii) find(bgood(ii,:), 1, 'last');
    
    agoodrows = any(agood, 2);
    bgoodrows = any(bgood, 2);
    
    aperfectrows = agood(:,end);
    bperfectrows = bgood(:,end);
    
    aimperfectrows = agoodrows & ~aperfectrows;
    
    for ii=find(bgoodrows & ~bperfectrows)'
        b(ii, bilast(ii)+1:end) = NaN;
    end
    for ii=find(aimperfectrows & ~bgoodrows)'
        c(ii, ailast(ii)+1:end) = NaN;
    end
    
    aperfbgood = aperfectrows & bgoodrows;
    c(aperfbgood, size(a, 2)+1:end) = b(aperfbgood, :);
    
    for ii=find(aimperfectrows & bgoodrows)'
        ajj = ailast(ii);
        c(ii, ajj+1:ajj+blen) = b(ii, :);
    end
end

This can be improved even more by separating out the rows that can be vectorized from those that can't. First I write NaN values to the end of all the b rows. This reduces the time for me by about another 1/4:

function c = catnonzero_3(a, b)
    c=[a, NaN(size(b))];
    blen = size(b, 2);
    
    agood = a~=0;
    bgood = b~=0;
    
    ailast = @(ii) find(agood(ii,:), 1, 'last');
    bilast = @(ii) find(bgood(ii,:), 1, 'last');
    
    agoodrows = any(agood, 2);
    bgoodrows = any(bgood, 2);
    
    aperfectrows = agood(:,end);
    bperfectrows = bgood(:,end);
    
    aimperfectrows = agoodrows & ~aperfectrows;
    
    a(~agoodrows, :) = NaN;
    b(~bgoodrows, :) = NaN;
    for ii=find(bgoodrows & ~bperfectrows)'
        b(ii, bilast(ii)+1:end) = NaN;
    end
    for ii=find(aimperfectrows & ~bgoodrows)'
        c(ii, ailast(ii)+1:end) = NaN;
    end
    
    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
Source Link
TheBlackCat
  • 2.4k
  • 11
  • 10

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 a little more by separating out the rows that can be vectorized from those that can't. This reduces the time for me by about another 1/4:

function c = catnonzero_3(a, b)
    c=[a, NaN(size(b))];
    blen = size(b, 2);
    
    agood = a~=0;
    bgood = b~=0;
    
    ailast = @(ii) find(agood(ii,:), 1, 'last');
    bilast = @(ii) find(bgood(ii,:), 1, 'last');
    
    agoodrows = any(agood, 2);
    bgoodrows = any(bgood, 2);
    
    aperfectrows = agood(:,end);
    bperfectrows = bgood(:,end);
    
    aimperfectrows = agoodrows & ~aperfectrows;
    
    for ii=find(bgoodrows & ~bperfectrows)'
        b(ii, bilast(ii)+1:end) = NaN;
    end
    for ii=find(aimperfectrows & ~bgoodrows)'
        c(ii, ailast(ii)+1:end) = NaN;
    end
    
    aperfbgood = aperfectrows & bgoodrows;
    c(aperfbgood, size(a, 2)+1:end) = b(aperfbgood, :);
    
    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))))