views:

149

answers:

5

Hi,

I have a m x n matrix where each row consists of zeros and same values for each row.

an example would be:

M = [-0.6 1.8 -2.3 0 0 0; 0 0 0 3.4 -3.8 -4.3; -0.6 0 0 3.4 0 0]

In this example the first column consists of 0s and -0.6, second 0 and 1.8, third -2.3 and so on.

In such case I would like to reduce m to 1 (get a vector from a given matrix) so in this example a vector would be [-0.6 1.8 -2.3 3.4 -3.8 -4.3]

Does anyone know what is the best way to get a vector from such matrix?

Thank you!

A: 

M = M(M~=0)

or

M(find(M))

and please learn how to format code for SO readers.

EDIT @Jonas suggests that OP wants only one occurrence of each non-zero element from M. To get this try wrapping either of the foregoing suggestions in unique(), such as

unique(M(M~=0))
High Performance Mark
A: 

This is NOT actually a sparse matrix. A sparse matrix in MATLAB is defined as such. If you use the sparse or spdiags functions to define that matrix, then the zero elements will not need to be stored, only the non-zeros. Of course, MATLAB knows how to work with these true sparse matrices in conjunction with other standard double arrays.

Finally, true sparse matrices are usually much more sparse than this, or you would not bother to use the sparse storage form at all.

Regardless, if you only want the non-zero elements of ANY matrix, then you can do this:

NZ = M(M ~= 0);

alternatively,

NZ = M(find(M));

Either will string out the non-zeros by columns, because that is how numbers are stored in a matrix in MATLAB.

NZ = M(find(M))
NZ =
         -0.6
         -0.6
          1.8
         -2.3
          3.4
          3.4
         -3.8
         -4.3

In your question, you asked for how to do this by rows, extracting the non-zero elements in the first row, then the second row, etc.

This is most simply done by transposing the array first. Thus, we might do something like...

NZ = M.';
NZ = NZ(find(NZ))
NZ =
         -0.6
          1.8
         -2.3
          3.4
         -3.8
         -4.3
         -0.6
          3.4

See that I used .' to do the transpose, just in case any elements were complex.

woodchips
If I understand the question correctly, this is not what @niko is looking for.
Jonas
How is this not what niko was looking for? This returns a vector with the non-zero elements, EXACTLY in the order as requested. The only difference is I did not transpose the result into a row vector. But only a "vector" was requested. If the OP really wants specifically a row vector, then .' will transpose it into one.IF the OP has some other vague scheme that is implied by this question, then he needs to make the question clear, far more clear than it is now.
woodchips
A: 

If there's an unknown number of non-zeros and zeros, one way to fix the problem is to first replace the zeros with NaNs, and then use something like max or min to find the data.

%# create an array
M = [-0.6 1.8 -2.3 0 0 0; 0 0 0 3.4 -3.8 -4.3; -0.6 0 0 3.4 0 0];

%# replace zeros with NaN
M(M==0) = NaN;

%# get, for each column, the number
numbers = max(M,[],1)

numbers =

    -0.6000    1.8000   -2.3000    3.4000   -3.8000   -4.3000

EDIT

This is how I understood the question: "I want, for every column, to know the value of the non-zero entries. There is only one non-zero number per column, but it could occur multiple times"

Here is a more Matlab-like (but longer) way to get the solution:

%# create an array
    M = [-0.6 1.8 -2.3 0 0 0; 0 0 0 3.4 -3.8 -4.3; -0.6 0 0 3.4 0 0];

%# find the non-zero entries
[r,c] = find(M);

%# only take one entry per column
[uniqueCols, sortIdx] = unique(c);

%# fix the rows correspondingly
uniqueRows = r(sortIdx);

%# convert to index
idx = sub2ind(size(M),uniqueRows,uniqueCols);

%# get the numbers per column (transpose as needed)
numbers = M(idx)

numbers =

   -0.6000
    1.8000
   -2.3000
    3.4000
   -3.8000
   -4.3000
Jonas
This is a very convoluted way of doing something for which there are natural Matlab idioms !
High Performance Mark
@High Performance Mark: Your and @woodchips' solution finds multiple versions of the numeric values, which is not what the OP wants as far as I understand. I don't think that there is a much less convoluted version. You could possibly run find with two outputs and only take one numeric value per row, but it would involve more typing than this.
Jonas
@Jonas: see my edit above. I still think your approach is unidiomatic and too convoluted.
High Performance Mark
Sorry, but this is totally convoluted.
woodchips
@woodchips, @High Performance Mark: Do you have a less convoluted way to get the solution vector from the OP?
Jonas
+1 for solutions that *exactly* reproduce the output the OP wanted, even if they are "convoluted". ;)
gnovice
@gnovice: I'm so glad thet there's one person that understands me here.
Jonas
A: 

If the OP REALLY wants to find the non-zero elements that are also unique, then there are far better ways to do so than Jonas suggests.

The logical solution is to find the non-zero elements FIRST. Then apply the function unique. So do this:

unique(M(find(M)))

If your goal is to find those elements in a specific order, then that order needs to be explicitly defined in your goal.

woodchips
I think the solution he provides is the goal.
Jonas
At last, the three of us agree !
High Performance Mark
Actually we don't, since I think the order is important :).
Jonas
+1  A: 

Here's a one-liner that uses the function SUM:

nonZeroColumnValues = sum(M)./sum(M ~= 0);

This will return a 1-by-n vector that contains the repeated non-zero value from each column. It does so by summing each column, then dividing the result by the number of non-zero values in each column. If there are no non-zero values in a column, the result for that column will be NaN.

Here's an example using the sample matrix M in the question:

>> M = [-0.6 1.8 -2.3 0 0 0; 0 0 0 3.4 -3.8 -4.3; -0.6 0 0 3.4 0 0]

M =

   -0.6000    1.8000   -2.3000         0         0         0
         0         0         0    3.4000   -3.8000   -4.3000
   -0.6000         0         0    3.4000         0         0

>> nonZeroColumnValues = sum(M)./sum(M ~= 0)

nonZeroColumnValues =

   -0.6000    1.8000   -2.3000    3.4000   -3.8000   -4.3000
gnovice
How "un-idiomatic"! But even shorter and cleverer than my version. +1
Jonas

related questions