surface fitting with least square

217 Views Asked by At

I want to fit a polynomial surface to some 3d points. First in a loop, matrix R is calculated as follows:

for j=1:size(indices,1)
            A(j,1:3)=Temp.Location(indices(j),:);
            X = Temp.Location(indices(j),1);
            Y = Temp.Location(indices(j),2);
            R(j, 1) = X*X;
            R(j, 2) = Y*Y;
            R(j, 3) = Y;
            R(j, 4) = X;
            R(j, 5) = X*Y;
            R(j, 6) = 1;
            Z(j, 1) = Temp.Location(indices(j),3);
        end

And then coeficients are calculated as follows:

 Cof=inv(R'*R)*R'*Z;

The results (z and predicted z) are as follows: enter image description here enter image description here enter image description here

enter image description here Is the method wrong? How least square is used as surface fitiing? Thanks

points: [ x ,y ,z ,predicted z] [enter image description here]4

2

There are 2 best solutions below

2
On

NOT AN ANSWER.

This isn't an answer to your question, but it's a cleanup of your code, because ... because it was needed.

You wrote:

for j=1:size(indices,1)
        A(j,1:3)=Temp.Location(indices(j),:);
        X = Temp.Location(indices(j),1);
        Y = Temp.Location(indices(j),2);
        R(j, 1) = X*X;
        R(j, 2) = Y*Y;
        R(j, 3) = Y;
        R(j, 4) = X;
        R(j, 5) = X*Y;
        R(j, 6) = 1;
        Z(j, 1) = Temp.Location(indices(j),3);
    end

In doing this, you're re-allocating the matrix $R$ each time through the loop. No big deal for 40 points, but a total mess when there are 100000 points. So let's just fix that a bit by allocating $R$ in advance:

n = size(indices, 1);
R = zeros(n, 6);

for j=1:n
    ... 
end

Now let's get more idomatic: when you want to extract items 1, 2, 3 of an array, you can write

for j = 1:3
  U(j) = X(j)
end

or you can just write

U([1 2 3]) = X([1 2 3])

which is the more "matlab-like" way to do things. Since you're taking each of the items in Temp.Location, in the order specified by indices, you can rewrite most of your code like this:

n = size(indices, 1);
R = zeros(n, 6);


//A(j,1:3)=Temp.Location(indices(j),:); // deleted because never used!
X = Temp.Location(indices(1:n),1); // all the x-values in one columnay list!
Y = Temp.Location(indices(1:n),2);
Z = Temp.Location(indices(1:n),3);
R(:, 1) = X.*X; // term by term squaring
R(:, 2) = Y.*Y;
R(:, 3) = Y;
R(:, 4) = X;
R(:, 5) = X.*Y;
R(:, 6) = 1;

That's looking better, but it's still only partway there. I'm going to make a vector the same length as X containing only the number $1$, and call this vector U, for "unit":

n = size(indices, 1);
R = zeros(n, 6);    
X = Temp.Location(indices(1:n),1); // all the x-values in one columnay list!
Y = Temp.Location(indices(1:n),2);
Z = Temp.Location(indices(1:n),3);
U = ones(n, 1); 
...

And now I'm going to stick $X, Y,$ and $U$ all into an array $M$, as its columns. I've gotten rid of $A$ because you never used it.

R = zeros(n, 6);    
X = Temp.Location(indices(1:n),1); // all the x-values in one columnay list!
Y = Temp.Location(indices(1:n),2);
Z = Temp.Location(indices(1:n),3);
U = ones(n, 1);
M = [X, Y, U]; 
...

And finally, I'm going to look at M'M. The first entry of this will be the row-vector $X'$ multiplied by the column vector $X$, i.e., it'll be $$ x_1^2 + x_2 ^2 + \ldots + x_n^2. $$ Now that might seem odd, because your code just computes $x_1^2$ and puts it into $R(j, 1)$, and similarly for the others. But if you're doing least-squares fitting right, you need this sum, not the individual values. Your code, later, perhaps sums R(j, ...) over all j --- it almost certainly should do so. So now my (conjectured) version of the right code looks like this:

R = zeros(n, 6);    
X = Temp.Location(indices(1:n),1); // all the x-values in one columnay list!
Y = Temp.Location(indices(1:n),2);
Z = Temp.Location(indices(1:n),3);
U = ones(n, 1);
M = [X, Y, U]; 
R = M' * M; // 1,1 entry is X dot X; 1,2 entry is X dot Y; 1, 3 entry
            // is X dot U = sum of all X values, etc. 

At this point R is a symmetric matrix containing all the values you need to complete the least-squares process. Of course, I may have mangled your intentions somewhat --- just looking at your code fragment, it's not clear where you're going with the matrix R that you built --- but I've written far more idiomatic and readable matlab code, which will run hugely faster if you ever get around to processing large amounts of data.

Finally, now that we compute R only once, that first line, where we allocate R as an array full of zeroes ... that can be removed!

N.B.:

In your coefficient computation, you're computing inv(R'*R) * R' * Z. That's not going to get you the coefficient you need, because you're squaring a bunch of things that you've already squared. It's also computationally a terrible idea. We seldom solve $Ax = b$ by writing

x = inv(A) * b;

Instead, you get a huge efficiency improvement if you write

x = A \ b; 

You might want to read the documentation for the backslash operator; it could substantially simplify your program.

More important though: I want to suggest that you go back and re-read whatever presentation of least-squares fitting you've seen, until you understand it deeply. Then, and only then, start writing code. Randomly searching through the space of all possible programs for the one that does what you want is almost never a good strategy, even if the random search is clustered "near" some correct code: the search space is simply too large to ever result in a correct answer.

If you think you've already done that deep understanding perhaps you can write out here the mathematics that you're trying to emulate with your program.

2
On

There is a function called fit in matlab which fits a line or surface to given values with a model or regression type. If you simply look. This is an example as I not going to deal with that mess.

load franke
sf = fit([x, y],z,'poly23')

 Linear model Poly23:
     sf(x,y) = p00 + p10*x + p01*y + p20*x^2 + p11*x*y + p02*y^2 + p21*x^2*y 
                    + p12*x*y^2 + p03*y^3
     Coefficients (with 95% confidence bounds):
       p00 =       1.118  (0.9149, 1.321)
       p10 =  -0.0002941  (-0.000502, -8.623e-05)
       p01 =       1.533  (0.7032, 2.364)
       p20 =  -1.966e-08  (-7.084e-08, 3.152e-08)
       p11 =   0.0003427  (-0.0001009, 0.0007863)
       p02 =      -6.951  (-8.421, -5.481)
       p21 =   9.563e-08  (6.276e-09, 1.85e-07)
       p12 =  -0.0004401  (-0.0007082, -0.0001721)
       p03 =       4.999  (4.082, 5.917)


plot(sf,[x,y],z)

enter image description here