1.3.5 Matrices from math/matrix
glmnet/data/math converts the matrices of Racket’s math/matrix library to design matrices and back. A matrix’s rows are the observations. A response can be a column matrix, a row matrix or a one-dimensional math/array array. (require glmnet) does not load the module, so a program that does not use math/matrix does not wait for it to load.
See Matrices from math/matrix in the Reference for the three procedures.
The Quick Start of R’s glmnet fits glmnet(x, y) to the 100 × 20 matrix x and the response vector y of QuickStartExample, then predicts at new rows nx drawn at random. Here is the same fit, with x a math/matrix matrix and y a column matrix:
> (require math/matrix math/distributions glmnet/datasets glmnet/data/math) > (define-values (qs-x qs-y) (quick-start-example)) > (define x (design-matrix->matrix qs-x)) > (define y (->col-matrix qs-y)) > (matrix-shape x)
100
20
> (define fit (elnet-path (matrix->design-matrix x) (array->response y))) > (coef fit #:lambda 0.1)
'#(0.15092807247412926
1.3205971945479142
0.0
0.6751102344847403
0.0
-0.8174115175489668
0.5214366714587341
0.004829335148616431
0.3194159167155697
0.0
0.0
0.14249851856673404
0.0
0.0
-1.0599787019192302
0.0
0.0
0.0
0.0
0.0
-1.0218737036830785)
> (random-seed 29) > (define nx (build-matrix 5 20 (lambda (i j) (sample (normal-dist))))) > (predict fit (matrix->design-matrix nx) #:lambda '(0.1 0.05))
'((1.3062999979031626
2.0460482043995163
-0.3251741946880151
-1.9745626856083445
1.6552393883406682)
(1.387664042746005
2.126839326881185
-0.26250673336767805
-2.1506980670436624
1.8246177456688972))
matrix->design-matrix copies and checks a matrix as rows->design-matrix does a list of rows, and array->response gives the response as a list, so the fit is the one from the same numbers as lists. design-matrix->matrix converts back, to a matrix of flonums:
> (define X (matrix->design-matrix x))
> (equal? (elnet-path X (array->response y)) (elnet-path (matrix->list* x) (matrix->list y))) #t
> (equal? (design-matrix->matrix X) x) #t
> (design-matrix->matrix (rows->design-matrix '((1 2) (3 4)))) (flarray #[#[1.0 2.0] #[3.0 4.0]])
A flonum array, from array->flarray or design-matrix->matrix, or a mutable array, from vector->matrix, is copied straight from the vector that holds its elements. Any other array is read one element at a time, through the contract that the Typed Racket library puts on each array it returns to untyped code, which makes a large matrix several times slower to convert, and a lazy one, made while array-strictness is #f or returned by array-broadcast or array-lazy, slower still. Converting once and passing the design matrix to every fit pays that cost once.