Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easier way to retrieve matrix columns or rows? #230

Closed
whitews opened this issue Nov 11, 2014 · 14 comments
Closed

Easier way to retrieve matrix columns or rows? #230

whitews opened this issue Nov 11, 2014 · 14 comments

Comments

@whitews
Copy link

whitews commented Nov 11, 2014

Hi Jos,

Thanks for providing such a useful math library for JS. I'm considering MathJS for a current project, and would like some guidance retrieving rows and columns from a Matrix object. Let's say I have a matrix:

var my_matrix = math.matrix(
    [
        [ 0.50, 0.25, 0.00], 
        [ 0.25, 0.50, 1.00]
    ]
);

And, now I want to retrieve the last column as a 1-D array. After trying various approaches, I came up with this:

var super_special_column = math.flatten(
    my_matrix.subset(
        math.index([0, 2], 2)
    )
).toArray();

Admittedly, I may be spoiled from Python's NumPy syntax, but this seems like a lot of work. Is this the easiest/best way to access members of a Matrix in MathJS?

Thanks again,
Scott

@josdejong
Copy link
Owner

Yes, the syntax for getting/setting subsets is terribly verbose. JavaScript doesn't have a colon operator like python :(. Using the (one-based) expression parser it's great, there you just do my_matrix[1:2, 2], similar to Python and Matlab.

If you know a less verbose solution please let me know, I'm very interested in that!

A few thoughts:

  • It's still on my to-do list to have the Index accept special values like '*' and end, to denote a whole row/column and the end of a column (Similar to doing A[:] and A[2:end] in the expression parser). That way you don't have to figure out the size of the dimensions to write your Index.

  • You could of course write two helper functions to get a column or row:

    /**
    * Retrieve a column from a matrix
    * @param {Matrix | Array} matrix
    * @param {number} index    Zero based column index
    * @return {Matrix | Array} Returns the column as a vector
    */
    function col(matrix, index) {
      var rows = math.size(matrix).valueOf()[0];
      return math.flatten(math.subset(matrix, math.index([0, rows], index)));
    }
    
    /**
    * Retrieve a row from a matrix
    * @param {Matrix | Array} matrix
    * @param {number} index    Zero based row index
    * @return {Matrix | Array} Returns the row as a vector
    */
    function row(matrix, index) {
      var rows = math.size(matrix).valueOf()[1];
      return math.flatten(math.subset(matrix, math.index(index, [0, rows])));
    }

    Whether you want to flatten the output and convert to an Array is application specific I think.

@josdejong
Copy link
Owner

I will close this issue. The (JavaScript) API is still verbose, but I don't know of a better solution.

@dzackgarza
Copy link

dzackgarza commented Mar 26, 2017

Hi guys, I think this issue should be reopened - if nothing else, it seems like this sort of functionality should be included in the API for matrices.

The actual helper functions can be very short, something like

getRow = (M, i) => math.flatten(M.subset(math.index(i, math.range(0, M._size[0])))).toArray();

getColumn = (M, i) => math.flatten(M.subset(math.index(math.range(0, M._size[0]),i))).toArray();

Ideally, though, these would be functions attached to any matrix, so you could call something like

let row = M.getRow(2);

This code is perhaps not the best solution, but isolating a single row/column from a matrix is such a common numerical procedure that it really should have some first-class support. If it's possible for something like this to be added in a PR, just point me towards the appropriate files!

@josdejong josdejong reopened this Mar 31, 2017
@josdejong
Copy link
Owner

Agree @dzackgarza , ok let's add these two functions.

I like the shorter names row() and col() or column() more than the getters. When the input is a Matrix, output should be a Matrix, and when input is an Array, output should be an Array.

I think we should at least create these two as standalone functions row(matrix, index) and column(matrix, index), so you can handle both Arrays and Matrices and not just matrices. We could add them as methods on a Matrix too. Does that make sense?

The functions are zero-based, and need a transform so they work with one-based indices in the expression parser. The Matrix methods are just zero-based.

Would be great if you could contribute these functions @dzackgarza ! Docs and unit tests is probably the most work here, not the actual implementation. You could look at how concat.js and concat.transform.js is implemented, it has a last index argument too which is transformed in the expression parser.

@artemave
Copy link

artemave commented May 1, 2017

@dzackgarza in getRow it should be M._size[1], shouldn't it?

getRow = (M, i) => math.flatten(M.subset(math.index(i, math.range(0, M._size[1])))).toArray();

@cshaa
Copy link
Collaborator

cshaa commented Feb 22, 2020

This is already implemented as math.row and math.column, no?

@josdejong
Copy link
Owner

Did you try it out? 😄

@josdejong
Copy link
Owner

good point though, this issue should be closed long time ago (the functions column and row are indeed implemented)

@cshaa
Copy link
Collaborator

cshaa commented Feb 22, 2020

But they do indeed work differently than documented 😅️
The documentation says:

// get a row
const d = [[1, 2], [3, 4]]
math.row(d, 1) // returns [3, 4]

// get a column
const d = [[1, 2], [3, 4]]
math.column(d, 1) // returns [2, 4]

But in reality they return [[3,4]] and [[2],[4]] respectively. What's up with math.js returning vectors inconsistently either as 1D or 2D?

EDIT: For example lsolve, usolve, lusolve all return 2D column vectors (both in documentation and in reality), kron returns 2D row vectors. If I were to chose, I'd definitely go with 1D vectors everywhere.

@josdejong
Copy link
Owner

hm have to double check that.

@josdejong
Copy link
Owner

The row and column implementations indeed returns 2d result so the comments are wrong.

What's up with math.js returning vectors inconsistently either as 1D or 2D?

I have a vague idea but can you give an example so I understand what you mean?

@cshaa
Copy link
Collaborator

cshaa commented Feb 26, 2020

I found out that I remembered the inconsistencies much worse than they actually were. Ah, those fake memories 😅️. If we fix the documentation of column and row to reflect the reality, there's a clear precedence for 2D vectors.

The only thing that really bugs me now is that kron converts 1D vectors to 2D row vectors. I think it shouldn't change the size of matrices.

@josdejong
Copy link
Owner

It is important that the functions are consistent with each other. It makes sense to me to have 1d in -> 2d out. I've opened an issue for that: #1753.

@cshaa
Copy link
Collaborator

cshaa commented Mar 12, 2020

Turns out these weren't fake memories in the end. The dot function only works for 1-dim vectors, not column vectors. I'm working on a PR already.

math.dot( math.matrix([[1],[2]]), math.matrix([[1],[2]]) )
// RangeError: Vector expected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants