On this page:
2.1 Common arguments
2.2 Input data
design-matrix?
design-matrix/  c
response/  c
rows->design-matrix
columns->design-matrix
f64vector->design-matrix
design-matrix-nrows
design-matrix-ncols
design-matrix-column-names
design-matrix-ref
design-matrix-select-rows
design-matrix->rows
design-matrix->columns
design-matrix->f64vector
response->f64vector
2.2.1 Tables
table?
table-column-names
table->design-matrix
2.3 Nested lists and vectors
nested->design-matrix
design-matrix->nested
2.4 CSV files
csv->table
csv-file->table
table->csv
table->csv-file
2.5 Matrices from math/  matrix
matrix->design-matrix
design-matrix->matrix
array->response
2.6 Polars dataframes
polars->design-matrix
design-matrix->polars
polars->response
polars->table
table->polars
2.7 Example datasets
quick-start-example
binomial-example
multinomial-example
poisson-example
cox-example
multi-gaussian-example
sparse-example
mtcars
iris
2.8 Gaussian models
elnet-result
elnet-fit
ols
ridge
lasso
elastic-net
elnet-predict
2.9 Binomial models
logistic-result
logistic-fit
logistic-predict-proba
logistic-predict
2.10 Multinomial models
multinomial-result
multinomial-fit
multinomial-predict-proba
multinomial-predict
2.11 Cox models
cox-result
cox-fit
cox-linear-predictor
cox-relative-risk
2.12 Poisson models
poisson-result
poisson-fit
poisson-predict-mean
2.13 Multi-response Gaussian models
mgaussian-result
mgaussian-fit
mgaussian-predict
2.14 Regularization paths
glmnet-path
elnet-path
logistic-path
multinomial-path
cox-path
poisson-path
mgaussian-path
2.15 Cross-validation
glmnet-cv
elnet-cv
logistic-cv
multinomial-cv
cox-cv
poisson-cv
mgaussian-cv
random-fold-ids
fold-ids/  c
2.16 Formulas
~
formula-term/  c
formula-rhs/  c
formula-response/  c
transform-term
transform-term?
make-formula
formula?
formula-response
formula-terms
formula-predictor-names
formula-design-matrix
formula-model
formula-model-levels
formula-fit
formula-path
formula-cv
2.17 Generic model interface
gen:  glmnet-model
glmnet-model?
glmnet-model->path
glmnet-model-default-lambda
glmnet-model-named-lambda
glmnet-model-predictor-names
glmnet-model-response-names
deviance-ratio
predict
coef
2.17.1 Printing
2.18 Native library
glmnet-default-real-bytes
glmnet-capi-abi-version
2.19 Plots
plot-coefficient-path
coefficient-path-renderers
plot-cv
cv-renderers
9.3

2 Reference🔗ℹ

Every binding below is provided by glmnet, except those of Nested lists and vectors, CSV files, Matrices from math/matrix, Polars dataframes, Example datasets and Plots, whose sections name the modules that provide them. Each model family has a fit procedure, a path fitter, a cross-validation procedure, a transparent result struct and prediction helpers. The formula front end (Formulas) fits any family from a table. Every result type, glmnet-path, glmnet-cv and formula-model also implement the generic interface of Generic model interface: predict, coef and deviance-ratio work on all of them, and they print as summaries. Concepts explains how the pieces fit together.

2.1 Common arguments🔗ℹ

The fit procedures share their argument conventions:

  • X is a design matrix, one row per observation: a design-matrix? value, or a non-empty list or vector of equal-length rows of reals, each row a list or a vector (see design-matrix/c, Input data and Nested lists and vectors). Prediction helpers take new data in the same forms, with as many columns as the fit has coefficients.

  • A response y, and a Cox model’s times and statuses, is a non-empty list, vector, flvector or f64vector with one entry per row of X (see response/c).

  • #:lambda is the penalty strength λ ≥ 0. It is required.

  • #:alpha is the mixing parameter α ∈ [0, 1]: 0.0 is the ridge penalty, 1.0 (the default) the lasso, and values in between the elastic net.

  • #:standardize? scales each predictor to unit variance before fitting; coefficients are always reported on the original scale.

  • #:intercept? fits an unpenalized intercept; #f fixes it at zero.

  • #:thresh is the coordinate-descent convergence threshold and #:max-iters the maximum number of passes.

Before the solver runs, an argument of the wrong type, a ragged or empty matrix, a non-finite entry, or a response of the wrong length raises exn:fail:contract; the message names the offending row and column or position. A fatal condition reported by glmnet raises exn:fail with a readable message. So does a fit for which glmnet fits no λ at all, for example because it does not converge within #:max-iters passes; the message names the procedure called and gives glmnet’s reason. (R warns instead, and returns an empty model.)

2.2 Input data🔗ℹ

 (require glmnet/data) package: glmnet

A design-matrix? value holds a design matrix in the layout the Fortran reads: an array of doubles stored column by column, so that element (i, j) of a matrix with n rows is at index i + jn, with indices counted from 0. It also records the numbers of rows and columns, and optional column names. See Data layout.

Every conversion into a design matrix copies its input and checks it: the matrix needs at least one row and one column, every row (or column) the same length, and every entry must be a real, finite number. Exact numbers become flonums. A violation raises exn:fail:contract, with a message that names the offending row and column. Because nothing can modify a design matrix after it is built, it passes these checks for its whole life, and one design matrix can be passed to any number of fits.

glmnet re-exports every binding in this section. glmnet/data provides them without loading the native library.

procedure

(design-matrix? v) → boolean?

  v : any/c
Returns #t if v is a design matrix, and #f otherwise. Two design matrices are equal? when they have the same dimensions, entries and column names. A design matrix prints with its dimensions only.

Examples:
> (define D (rows->design-matrix '((1.0 4.0) (2.0 5.0) (3.0 6.0))))
> (design-matrix? D)

#t

> D

#<design-matrix 3x2>

> (equal? D (columns->design-matrix '((1 2 3) (4 5 6))))

#t

The contract on the X argument of every fit and prediction procedure, and on the response matrix Y of mgaussian-fit, mgaussian-path and mgaussian-cv. It accepts a design-matrix? value, or rows in any of four nestings: a list or a vector of rows, each row a list or a vector. Each of those procedures converts and checks the rows as nested->design-matrix does, so every nesting fits exactly as the same list of lists. A violation reports the contract’s name, (or/c design-matrix? (listof (or/c list? vector?)) (vectorof (or/c list? vector?))); unlike vectorof, the contract never wraps a vector.

Examples:
> (require racket/contract)
> (contract-first-order-passes? design-matrix/c D)

#t

> (contract-first-order-passes? design-matrix/c '((1.0 2.0) (3.0 4.0)))

#t

> (contract-first-order-passes? design-matrix/c (vector #(1.0 2.0) '(3.0 4.0)))

#t

> (contract-first-order-passes? design-matrix/c '(1.0 2.0))

#f

procedure

(response/c elem) → flat-contract?

  elem : flat-contract?
A contract for a one-dimensional input, such as a response: a non-empty list, vector, flvector or f64vector each of whose elements is a real number that satisfies elem. The elements are checked with real? first because a number contract such as (or/c 0 1) compares with =, which the complex number 1.0+0.0i passes. The fit procedures use it for their responses, for example (response/c (or/c 0 1)) for the labels of logistic-fit, and check elem again on the flonums they convert the response to, so that a positive time too small for a flonum, which becomes 0.0, is an error. A violation names the accepted shapes, or the position of the first element that fails elem, counting from 0. Like design-matrix/c, it never wraps a vector. Two response/c contracts are contract-equivalent? when their element contracts are, and one is contract-stronger? than another when its element contract is.

Examples:
> (require racket/flonum)
> (define labels/c (response/c (or/c 0 1)))
> (contract-first-order-passes? labels/c '(0 1 1))

#t

> (contract-first-order-passes? labels/c (flvector 0.0 1.0))

#t

> (contract-first-order-passes? labels/c (vector 0 2))

#f

> (contract-first-order-passes? labels/c (vector))

#f

> (define/contract (events statuses)
    (-> (response/c (or/c 0 1)) exact-nonnegative-integer?)
    (for/sum ([s statuses]) (if (= s 1) 1 0)))
> (events (vector 1 0 1))

2

> (events (flvector 1.0 0.0 0.5))

events: contract violation

  expected: (or/c 0 1)

  given: 0.5

  in: the element at position 2 of

      the 1st argument of

      (-> (response/c (or/c 0 1)) natural?)

  contract from: (function events)

  blaming: top-level

   (assuming the contract is correct)

  at: eval:16:0

> (contract-equivalent? (response/c (>/c 0)) (response/c (>/c 0)))

#t

procedure

(rows->design-matrix rows 
  [#:column-names column-names]) 
 → design-matrix?
  rows : (listof list?)
  column-names : (or/c #f (listof (or/c string? symbol?))) = #f
Builds a design matrix from a list of rows, one per observation. Every row must have the same length, and every entry must be a real, finite number. column-names, when given, names the columns: one distinct string or symbol per column. Names are compared as strings, so "x" and 'x are the same name, and the design matrix keeps each as a string, so that the same data named with symbols or with strings gives equal? design matrices, whatever it was converted from. For rows that are vectors, or a vector of rows, see nested->design-matrix.

Examples:
> (define named (rows->design-matrix '((1 2) (3 4)) #:column-names '(age dose)))
> (design-matrix->rows named)

'((1.0 2.0) (3.0 4.0))

> (design-matrix-column-names named)

'("age" "dose")

> (rows->design-matrix '((1.0 2.0) (3.0 4.0) (5.0)))

rows->design-matrix: the matrix has rows of different

lengths

  row: 2

  length: 1

  length of row 0: 2

> (rows->design-matrix '((1.0 2.0) (3.0 +inf.0)))

rows->design-matrix: the matrix has an element that is not

finite

  column: 1

  row: 1

  element: +inf.0

> (rows->design-matrix '((1.0 2.0)) #:column-names '(a))

rows->design-matrix: the number of column names does not

match the columns

  column names: 1

  columns: 2

> (rows->design-matrix '((1.0 2.0)) #:column-names '("x" x))

rows->design-matrix: the column names are not distinct

  duplicate: 'x

  column names: '("x" x)

procedure

(columns->design-matrix columns 
  [#:column-names column-names]) 
 → design-matrix?
  columns : (listof list?)
  column-names : (or/c #f (listof (or/c string? symbol?))) = #f
Builds a design matrix from a list of columns, one per predictor, under the same rules as rows->design-matrix. For columns in the other nestings, see nested->design-matrix with #:by 'columns.

Examples:
> (design-matrix->rows (columns->design-matrix '((1 2 3) (4 5 6))))

'((1.0 4.0) (2.0 5.0) (3.0 6.0))

> (columns->design-matrix '((1.0 2.0 3.0) (4.0 +nan.0 6.0)))

columns->design-matrix: the matrix has an element that is

not finite

  column: 1

  row: 1

  element: +nan.0

procedure

(f64vector->design-matrix v 
  nrows 
  ncols 
  [#:column-names column-names]) 
 → design-matrix?
  v : f64vector?
  nrows : exact-positive-integer?
  ncols : exact-positive-integer?
  column-names : (or/c #f (listof (or/c string? symbol?))) = #f
Builds a design matrix from v, an array already in the column-major layout: element (i, j) is at index i + j · nrows. The contract requires the length of v to be (* nrows ncols), and every entry must be finite. The design matrix holds a copy, so later changes to v do not affect it.

Examples:
> (require ffi/vector)
> (define v (f64vector 1.0 2.0 3.0 4.0 5.0 6.0))
> (design-matrix->rows (f64vector->design-matrix v 3 2))

'((1.0 4.0) (2.0 5.0) (3.0 6.0))

> (design-matrix->rows (f64vector->design-matrix v 2 3))

'((1.0 3.0 5.0) (2.0 4.0 6.0))

> (f64vector->design-matrix v 4 2)

f64vector->design-matrix: contract violation

  expected: an f64vector of length nrows * ncols = 8

  given: an f64vector of length 6

  in: the v argument of

      (->i

       ((v

         (nrows ncols)

         (f64vector-length/c nrows ncols))

        (nrows exact-positive-integer?)

        (ncols exact-positive-integer?))

       (#:column-names

        (column-names

         (or/c

          #f

          (listof (or/c string? symbol?)))))

       (result design-matrix?))

  contract from: <pkgs>/glmnet/data.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/data.rkt:34:3

The number of rows (observations) and columns (predictors) of dm.

Examples:

procedure

(design-matrix-column-names dm) → (or/c #f (listof string?))

  dm : design-matrix?
The column names dm was built with, as strings, or #f if it has none. A design matrix with column names is a table, from which the formula front end fits by name; the fits from a matrix do not use the names.

Examples:
> (design-matrix-column-names D)

#f

> (define cols '((1 2) (3 4)))
> (design-matrix-column-names
   (columns->design-matrix cols #:column-names '("x1" x2)))

'("x1" "x2")

The entry in row i and column j of dm, counting from 0. The contract requires i to be less than (design-matrix-nrows dm) and j less than (design-matrix-ncols dm).

Examples:
> (design-matrix-ref D 2 1)

6.0

> (design-matrix-ref D 3 0)

design-matrix-ref: contract violation

  expected: (integer-in 0 2)

  given: 3

  in: the i argument of

      (->i

       ((dm design-matrix?)

        (i

         (dm)

         (integer-in

          0

          (sub1 (design-matrix-nrows dm))))

        (j

         (dm)

         (integer-in

          0

          (sub1 (design-matrix-ncols dm)))))

       (result flonum?))

  contract from: <pkgs>/glmnet/data.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/data.rkt:43:3

A new design matrix of the rows of dm at the indices rows, counting from 0, in the order given and with the same column names. An index can appear more than once. The rows are copied without being checked again. Cross-validation uses it to split one design matrix into folds.

Examples:
> (design-matrix->rows (design-matrix-select-rows D '(2 0)))

'((3.0 6.0) (1.0 4.0))

> (design-matrix->rows (design-matrix-select-rows D '(1 1)))

'((2.0 5.0) (2.0 5.0))

> (design-matrix-select-rows D '(0 3))

design-matrix-select-rows: row index is out of range

  row index: 3

  valid range: [0, 2]

  design matrix: #<design-matrix 3x2>

procedure

(design-matrix->rows dm) → (listof (listof flonum?))

  dm : design-matrix?

procedure

(design-matrix->columns dm) → (listof (listof flonum?))

  dm : design-matrix?
The entries of dm as a list of rows or as a list of columns.

Examples:
> (design-matrix->rows D)

'((1.0 4.0) (2.0 5.0) (3.0 6.0))

> (design-matrix->columns D)

'((1.0 2.0 3.0) (4.0 5.0 6.0))

procedure

(design-matrix->f64vector dm) → f64vector?

  dm : design-matrix?
A fresh copy of the column-major array that dm holds.

Example:
> (f64vector->list (design-matrix->f64vector D))

'(1.0 2.0 3.0 4.0 5.0 6.0)

Converts a non-empty list, vector, flvector or f64vector of real, finite numbers to a fresh f64vector, the form in which a response reaches the Fortran. The fit procedures apply the same conversion to their responses, after checking that the response has one entry per row of X. An flvector is always copied, since the FFI cannot pass one where the Fortran expects an f64vector; so is an f64vector, so that the result never shares memory with y. An error names the position of the offending entry, counting from 0.

Examples:
> (f64vector->list (response->f64vector '(1 1/2 2.5)))

'(1.0 0.5 2.5)

> (f64vector->list (response->f64vector (vector 1 1/2 2.5)))

'(1.0 0.5 2.5)

> (f64vector->list (response->f64vector (flvector 1.0 0.5 2.5)))

'(1.0 0.5 2.5)

> (response->f64vector (flvector 1.0 +nan.0 2.0))

response->f64vector: the response has an element that is not

finite

  position: 1

  element: +nan.0

2.2.1 Tables🔗ℹ

A table holds named columns, and is what the formula front end (Formulas) fits from. A column name is a string or a symbol, and names are compared as strings; a column is a list, vector, flvector or f64vector of reals, or a list or vector of strings, symbols or booleans, which the formula front end reads as a factor. A table is one of:

  • a non-empty association list of (name . column) pairs, whose columns are in the list’s order;

  • a non-empty hash from name to column, whose columns are in the order of their names, sorted with string<?;

  • a design-matrix? with column names, in the order of its columns.

Only the columns that are selected are checked: every entry of a column selected as numbers must be a real, finite number, and the selected columns must have the same length, at least 1. An error names the column, and the row of an entry that is wrong. The guide’s Formulas and named data chapter shows tables in use.

procedure

(table? v) → boolean?

  v : any/c
Returns #t if v is a table: an association list or hash whose names are strings or symbols and whose columns are lists, vectors, flvectors or f64vectors, or a design matrix with column names. The entries of the columns are not checked.

Examples:
> (define patients
    (list (cons "age" '(34 51 67))
          (cons 'dose #(2.0 5.5 1.0))
          (cons "id" '("a" "b" "c"))))
> (table? patients)

#t

> (table? (hash "x" '(1 2)))

#t

> (table? named)

#t

> (table? D)

#f

> (table? '((1 2) (3 4)))

#f

procedure

(table-column-names table) → (listof string?)

  table : table?
The names of table’s columns, as strings, in the table’s order. Two columns whose names are the same string are an error.

Examples:
> (table-column-names patients)

'("age" "dose" "id")

> (table-column-names (hash 'b '(1) "a" '(2)))

'("a" "b")

> (table-column-names (hash "a" '(1) 'a '(2)))

table-column-names: the table has two columns with the same

name

  name: "a"

procedure

(table->design-matrix table [names]) → design-matrix?

  table : table?
  names : (and/c (listof (or/c string? symbol?)) pair?)
   = (table-column-names table)
A design matrix of the columns of table named by names, in that order, with those names, as strings, as its column names. Each name must be a column of the table, and must appear once.

Examples:
> (define P (table->design-matrix patients '(dose "age")))
> (design-matrix->rows P)

'((2.0 34.0) (5.5 51.0) (1.0 67.0))

> (design-matrix-column-names P)

'("dose" "age")

> (table->design-matrix patients)

table->design-matrix: the table has an element that is not a

real number

  column: "id"

  row: 0

  element: "a"

> (table->design-matrix patients '("age" "weight"))

table->design-matrix: the table has no column with this name

  column: "weight"

  columns of the table: '("age" "dose" "id")

2.3 Nested lists and vectors🔗ℹ

 (require glmnet/data/nested) package: glmnet

Racket’s own data for a design matrix is a list or a vector of rows, each row a list or a vector. Every fit and prediction procedure accepts these four nestings as they are (see design-matrix/c). This module converts them to a design-matrix? value, to check the data once and fit it many times or to name its columns, and converts a design matrix back to any of them. A response needs no conversion of its own: a list, vector, flvector or f64vector is accepted wherever a response is (see response/c and response->f64vector). glmnet does not re-export this module.

rows->design-matrix and columns->design-matrix are the list-of-lists cases of nested->design-matrix. The three read their input the same way, so the same entries give equal? design matrices, and so do the fits and predictions on them.

procedure

(nested->design-matrix xss 
  [#:by by 
  #:column-names column-names]) 
 → design-matrix?
  xss : 
(or/c (listof (or/c list? vector?))
      (vectorof (or/c list? vector?)))
  by : (or/c 'rows 'columns) = 'rows
  column-names : (or/c #f (listof (or/c string? symbol?))) = #f
Builds a design matrix from xss, whose elements are the rows of the matrix when by is 'rows and its columns when by is 'columns. xss is one list or one vector, and its rows (or columns) can be any mix of lists and vectors. The rules are those of rows->design-matrix: at least one row and one column, every row (or column) of the same length, every entry a real, finite number, and column-names, when given, one distinct name per column. A design matrix with column names is a table, from which the formula front end fits by name.

Examples:
> (require glmnet/data/nested)
> (define vrows (vector #(1.0 4.0) #(2.0 5.0) #(3.0 6.0)))
> (define V (nested->design-matrix vrows))
> (design-matrix->rows V)

'((1.0 4.0) (2.0 5.0) (3.0 6.0))

> (equal? V (nested->design-matrix (list #(1 2 3) '(4 5 6)) #:by 'columns))

#t

> (equal? V (rows->design-matrix '((1 4) (2 5) (3 6))))

#t

> (design-matrix-column-names (nested->design-matrix vrows #:column-names '(x z)))

'("x" "z")

> (nested->design-matrix (vector #(1.0 2.0) #(3.0)))

nested->design-matrix: the matrix has rows of different

lengths

  row: 1

  length: 1

  length of row 0: 2

procedure

(design-matrix->nested dm    
  [#:by by    
  #:outer outer    
  #:inner inner]) → (or/c list? vector?)
  dm : design-matrix?
  by : (or/c 'rows 'columns) = 'rows
  outer : (or/c 'list 'vector) = 'list
  inner : (or/c 'list 'vector) = 'list
The entries of dm, as flonums: its rows when by is 'rows, and its columns when it is 'columns. They are held in an outer (a list or a vector) of inners, so that the defaults give the list of lists of design-matrix->rows. The vectors are fresh and mutable. The column names are not included; see design-matrix-column-names.

Examples:
> (design-matrix->nested V #:outer 'vector #:inner 'vector)

'#(#(1.0 4.0) #(2.0 5.0) #(3.0 6.0))

> (design-matrix->nested V #:inner 'vector)

'(#(1.0 4.0) #(2.0 5.0) #(3.0 6.0))

> (design-matrix->nested V #:by 'columns #:outer 'vector)

'#((1.0 2.0 3.0) (4.0 5.0 6.0))

> (equal? (design-matrix->nested V) (design-matrix->rows V))

#t

2.4 CSV files🔗ℹ

 (require glmnet/data/csv) package: glmnet

glmnet/data/csv reads and writes tables as CSV files, following RFC 4180. glmnet does not re-export it. See CSV files.

When reading, the input must be UTF-8. The first record is the header, whose cells name the columns: each name must be non-empty and appear once. Records end with a line feed, a carriage return or both, and the last one need not; a byte-order mark at the start is skipped. A cell in double quotes can hold commas, line breaks and double quotes, which are written twice. Every record must have one cell per column.

Each cell is typed on its own, as R’s read.csv types a column that holds only that cell:

  • A cell is missing when it is NA, quoted or not, or when it is not quoted and is empty or holds only white space. A missing cell is an error.

  • A number is a flonum. A number is a decimal number, such as 3, -2.5, .5, 5. or 1e-3; a hexadecimal one, such as 0x1A, 0x.8 or 0x1.8p1; or Inf, Infinity or NaN, in any case; each with an optional sign. White space around a number is ignored. A number is rounded correctly, however many digits it has, which R’s reader does not always do for a decimal number. -0 is -0.0, where R reads it as the integer 0.

  • TRUE and T are #t, and FALSE and F are #f. Other spellings, such as true or False, are strings, as they are in R.

  • Any other cell is a string, as it is written between the quotes, white space included: " TRUE" and "NA " are strings, as they are in R, and "" is the empty string.

White space is what R takes as white space in a UTF-8 locale: space, tab, line feed, vertical tab, form feed and carriage return, and the Unicode spaces U+1680, U+2000 to U+2006, U+2008 to U+200A, U+2028, U+2029, U+205F and U+3000, but not the no-break spaces U+00A0, U+2007 and U+202F. It makes a cell blank, and it can follow a number; before a number, only the ASCII white space is skipped, as in R. In the C locale R takes only the ASCII white space, and reads a cell of Unicode spaces as a string.

Quotes change the value of a cell only when it is empty or holds only white space: then it is a string when it is quoted, and missing when it is not, where R reads both as missing. A number, a logical or NA is the same with or without quotes, as in R. Unlike R, which gives a whole column one type, a column can mix kinds: a column of the letters A, C, G and T, which R reads as strings, holds three strings and #t, and a formula that reads it raises an error naming the column. R’s complex numbers, such as 1i, are strings.

The table is an association list from each name, a string, to its column, a vector, in the header’s order. An error names the procedure called, the column, the row, counting from 0, and the line of the input on which the row starts, counting from 1, and the file when there is one. Input that is not UTF-8 is an error that names the line and the offset of its first byte that is not, counting from 0.

When writing, the header is the table’s column names, in its order, and then there is one line per row, each ended by a line feed; a table whose columns are empty is its header alone. A real is written as the shortest decimal that reads back as the same flonum, without a trailing .0; an infinity as Inf or -Inf, as R writes it; and a NaN as NaN, which R reads back as NaN, where R’s write.csv writes NA. A boolean is TRUE or FALSE, and a string, or a symbol’s name, is written as it is. A cell or a name is quoted when it holds a comma, a double quote or a line break, starts or ends with white space or starts with a byte-order mark, or is empty, and a string is also quoted when it would read as another value, such as "42" or "T", as R’s write.csv quotes every string. It still reads back as that value, as it does in R. A string that would read back as a missing value, "NA", cannot be written, nor can a value of another kind or a column without a name; an error names the column and the row.

procedure

(csv->table [in]) → table?

  in : input-port? = (current-input-port)
Reads a CSV file from in to its end, and returns it as a table.

Examples:
> (require glmnet/data/csv)
> (csv->table (open-input-string "id,dose,treated\nA,2.5,TRUE\n\"B, 2\",5,F\n"))

'(("id" . #("A" "B, 2")) ("dose" . #(2.5 5.0)) ("treated" . #(#t #f)))

> (csv->table (open-input-string "note\n\"said \"\"hi\"\"\"\n\"two\nlines\"\n"))

'(("note" . #("said \"hi\"" "two\nlines")))

> (csv->table (open-input-string "x\n0x1A\n-inf\n 7 \n\" TRUE\"\ntrue\n"))

'(("x" . #(26.0 -inf.0 7.0 " TRUE" "true")))

> (csv->table (open-input-string "x,y\n1,2\n3,NA\n"))

csv->table: the input has a missing value

  column: "y"

  row: 1

  line: 3

> (csv->table (open-input-string "x\n1 \n \n"))

csv->table: the input has a missing value

  column: "x"

  row: 1

  line: 3

> (csv->table (open-input-bytes #"name\nM\374ller\n"))

csv->table: the input is not UTF-8

  line: 2

  byte offset: 6

procedure

(csv-file->table path) → table?

  path : path-string?
Reads the CSV file at path as csv->table reads a port.

Examples:
> (define mtcars-file (collection-file-path "mtcars.csv" "glmnet" "datasets"))
> (map car (csv-file->table mtcars-file))

'("mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear" "carb")

procedure

(table->csv table [out]) → void?

  table : table?
  out : output-port? = (current-output-port)
Writes table to out as a CSV file.

Examples:
> (table->csv (list (cons "x" (list 1 0.1 1/3 +inf.0))
                    (cons "label" '("a" "b, c" "42" say))
                    (cons "flag" '(#t #f #t #f))))

x,label,flag

1,a,TRUE

0.1,"b, c",FALSE

0.3333333333333333,"42",TRUE

Inf,say,FALSE

> (define out (open-output-string))
> (table->csv (list (cons "x" (list 0.1 (+ 0.1 0.2) 1e-300))) out)
> (get-output-string out)

"x\n0.1\n0.30000000000000004\n1e-300\n"

> (csv->table (open-input-string (get-output-string out)))

'(("x" . #(0.1 0.30000000000000004 1e-300)))

> (table->csv (csv->table (open-input-string "id,dose\n")))

id,dose

> (table->csv (list (cons "code" '("NA" "b"))))

table->csv: the table has a string that would be read back

as a missing value

  column: "code"

  row: 0

  element: "NA"

procedure

(table->csv-file table path [#:exists exists]) → void?

  table : table?
  path : path-string?
  exists : (or/c 'error 'replace 'truncate 'truncate/replace)
   = 'error
Writes table to the file at path as table->csv writes it to a port. exists says what to do when the file exists, as for open-output-file; none of the choices keeps the file’s old contents.

Examples:
> (require racket/file)
> (define path (make-temporary-file "table-~a.csv"))
> (table->csv-file (list (cons "x" '(1 2)) (cons "y" '(3.5 4.5))) path #:exists 'replace)
> (file->string path)

"x,y\n1,3.5\n2,4.5\n"

> (csv-file->table path)

'(("x" . #(1.0 2.0)) ("y" . #(3.5 4.5)))

2.5 Matrices from math/matrix🔗ℹ

 (require glmnet/data/math) package: glmnet

Conversions between the matrices of math/matrix and design matrices, and from math/array arrays to responses. The rows of a matrix are observations and its columns predictors. glmnet does not re-export these bindings, and (require glmnet) does not load math/matrix. See Matrices from math/matrix.

Every array that math/array returns to untyped code carries a contract, and so does the procedure that computes its elements, which checks the index it is called with each time. The conversions’ element loops are written in Typed Racket, so they call that procedure without adding contracts of their own, and they avoid it where they can. The design matrix is written in place, with no intermediate copy:

For a large matrix, the first two take about as long as rows->design-matrix takes for the same rows as lists, the third several times as long, and the fourth far longer.

procedure

(matrix->design-matrix M 
  [#:column-names column-names]) 
 → design-matrix?
  M : (and/c array? matrix?)
  column-names : (or/c #f (listof (or/c string? symbol?))) = #f
A design matrix of the entries of M, one row per row of M. Every entry must be a real, finite number, and exact numbers become flonums; an error names the column of an entry that is not (by its name, when the columns have names), its row, counting from 0, and the entry. column-names names the columns, as for rows->design-matrix, and the names become strings. A design matrix with column names is a table, which the formula front end fits from by name. The response matrix Y of the multi-response Gaussian family converts the same way.

Examples:
> (require math/array math/matrix glmnet/data/math)
> (define M (matrix [[1 2 3] [2 1 5] [3 4 11] [4 3 11] [5 6 17]]))
> (define D (matrix->design-matrix M #:column-names '(x1 x2 y)))
> (design-matrix->rows D)

'((1.0 2.0 3.0) (2.0 1.0 5.0) (3.0 4.0 11.0) (4.0 3.0 11.0) (5.0 6.0 17.0))

> (equal? D (rows->design-matrix (matrix->list* M) #:column-names '(x1 x2 y)))

#t

> (coef (formula-fit (~ y x1 x2) D #:lambda 0))

'(("(Intercept)" . -1.0345797337849323)

  ("x1" . 2.234820972582017)

  ("x2" . 1.1656615050121506))

> (matrix->design-matrix (matrix [[1.0 2.0] [3.0 +nan.0]])
                         #:column-names '(x1 x2))

matrix->design-matrix: the matrix has an element that is not

finite

  column: "x2"

  row: 1

  element: +nan.0

procedure

(design-matrix->matrix dm) → (and/c array? matrix?)

  dm : design-matrix?
A matrix of the entries of dm, one row per row of dm: a flonum array, an FlArray, that holds a copy of them. It does not carry dm’s column names, which design-matrix-column-names gives.

Examples:
> (design-matrix->matrix D)

(flarray

 #[#[1.0 2.0 3.0]

   #[2.0 1.0 5.0]

   #[3.0 4.0 11.0]

   #[4.0 3.0 11.0]

   #[5.0 6.0 17.0]])

> (equal? (matrix->design-matrix (design-matrix->matrix D)) (matrix->design-matrix M))

#t

> (matrix* (design-matrix->matrix (rows->design-matrix '((1 2) (3 4))))
           (col-matrix [1 -1]))

(array #[#[-1.0] #[-1.0]])

procedure

(array->response A) → (and/c (listof real?) pair?)

  A : 
(and/c array?
       (or/c row-matrix?
             col-matrix?
             (property/c array-shape (vector/c exact-positive-integer?))))
The elements of A as a list, the form in which the fit procedures take a response. A is a row matrix, a column matrix or a one-dimensional array with at least one element. Every element must be a real, finite number; an error names the position of one that is not, counting from 0, and the element. The elements are returned as they are, so class labels such as those of multinomial-fit stay exact integers.

Examples:
> (array->response (col-matrix [1 0 1]))

'(1 0 1)

> (array->response (row-matrix [2.5 1/2]))

'(2.5 1/2)

> (array->response (array #[0 2 1]))

'(0 2 1)

> (lasso (matrix->design-matrix M) (array->response (->col-matrix '(1 3 2 5 4)))
         #:lambda 0.1)

#<glmnet:gaussian λ=0.1 dev=0.9170 nz=2/3>

> (array->response (matrix [[1 2] [3 4]]))

array->response: contract violation

  expected: (and/c array? (or/c row-matrix? col-matrix?

(property/c array-shape (vector/c

exact-positive-integer?))))

  given: (array #[#[1 2] #[3 4]])

  in: the 1st argument of

      (->

       (and/c

        array?

        (or/c

         row-matrix?

         col-matrix?

         (property/c

          array-shape

          (vector/c exact-positive-integer?))))

       (non-empty-listof real?))

  contract from: <pkgs>/glmnet/data/math.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/data/math.rkt:123:3

2.6 Polars dataframes🔗ℹ

 (require glmnet/data/polars) package: glmnet

Conversions between the dataframes of polars and glmnet’s design matrices, responses and tables. glmnet does not re-export them and does not load Polars. See Polars dataframes.

A column that is converted to numbers must have an integer or floating-point dtype; Polars casts it to doubles as it copies it out. Column names are strings or symbols, compared as strings, and must be names of the dataframe’s columns; the contracts check the names and the dtypes. A missing value (a null), anywhere in a column that is converted, is an error naming its column and row, and so is a value that is not finite where numbers are needed. Nothing is dropped or filled in.

procedure

(polars->design-matrix df columns) → design-matrix?

  df : dataframe?
  columns : (and/c (listof (or/c string? symbol?)) pair?)
A design matrix of the columns of df named by columns, in that order, with their names, as strings, as its column names. The contract requires df to have at least one row, and columns to name distinct columns of df with numeric dtypes. The matrix is Polars’ column-major export, dataframe->f64vector, taken over without a further copy; it shares no memory with df.

Examples:
> (define trial
    (dataframe (list (series '(1 2 3) #:name "dose")
                     (series '(0.5 1.5 2.5) #:name "age")
                     (series '("a" "b" "a") #:name "site"))))
> (define trial-matrix (polars->design-matrix trial '(age "dose")))
> (design-matrix->rows trial-matrix)

'((0.5 1.0) (1.5 2.0) (2.5 3.0))

> (design-matrix-column-names trial-matrix)

'("age" "dose")

> (polars->design-matrix trial '("dose" "site"))

polars->design-matrix: contract violation

  expected: a non-empty list of distinct names of numeric

columns of the dataframe

  given: column "site", of dtype string

  in: the columns argument of

      (->i

       ((df

         (and/c dataframe? dataframe-with-rows/c))

        (columns

         (df)

         (frame-columns/c

          df

          numeric-dtype?

          "numeric")))

       (result design-matrix?))

  contract from: <pkgs>/glmnet/data/polars.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/data/polars.rkt:22:3

procedure

(design-matrix->polars dm 
  [#:column-names column-names]) 
 → dataframe?
  dm : design-matrix?
  column-names : (listof (or/c string? symbol?))
   = 
(or (design-matrix-column-names dm)
    (list "V1" "V2" ...))
A dataframe of the columns of dm, each a 'float64 series, named by column-names: by default, dm’s column names, or "V1", "V2", …, as R names the columns of a matrix, when it has none. The contract requires one distinct name per column. Polars dataframes says why this direction is the slower one.

Examples:
> (design-matrix->polars trial-matrix)

shape: (3, 2)

┌─────┬──────┐

│ age ┆ dose │

│ --- ┆ ---  │

│ f64 ┆ f64  │

╞═════╪══════╡

│ 0.5 ┆ 1.0  │

│ 1.5 ┆ 2.0  │

│ 2.5 ┆ 3.0  │

└─────┴──────┘

> (column-names (design-matrix->polars (rows->design-matrix '((1 2)))))

'("V1" "V2")

> (column-names (design-matrix->polars trial-matrix #:column-names '(a b)))

'("a" "b")

procedure

(polars->response df column) → (and/c (listof real?) pair?)

  df : dataframe?
  column : (or/c string? symbol?)
The values of the column of df named column as a response: exact integers from an integer column, flonums from a floating-point one. The contract requires df to have at least one row, and column to name a column of df with a numeric dtype.

Examples:
> (polars->response trial "dose")

'(1 2 3)

> (polars->response (dataframe (list (series (list 1.0 polars-null 3.0)
                                             #:name "y")))
                    "y")

polars->response: the dataframe has a missing value

  column: "y"

  row: 1

procedure

(polars->table df [columns]) → table?

  df : dataframe?
  columns : (and/c (listof (or/c string? symbol?)) pair?)
   = (column-names df)
A table of the columns of df named by columns, in that order: an association list from each name to a vector of the column’s values. Numeric columns hold numbers, exact integers from an integer column, and boolean columns booleans. String columns hold strings, and categorical and enum columns symbols, which the formula front end reads as factors. A factor’s levels are sorted as strings, so the order of an enum’s categories is not kept (see Polars dataframes). The contract requires df to have at least one row. When columns is given, it must name distinct columns of df with those dtypes; when it is not, every column must have one of them.

Examples:
> (polars->table trial)

'(("dose" . #(1 2 3)) ("age" . #(0.5 1.5 2.5)) ("site" . #("a" "b" "a")))

> (polars->table trial '("site"))

'(("site" . #("a" "b" "a")))

procedure

(table->polars t [columns]) → dataframe?

  t : table?
  columns : (and/c (listof (or/c string? symbol?)) pair?)
   = (table-column-names t)
A dataframe of the columns of t named by columns, in that order. A column’s values choose its dtype:
  • 'int64 for exact integers from −263 to 263 − 1;

  • 'uint64 for exact integers from 0 to 264 − 1, when some are 263 or more;

  • 'float64 for any other reals, and for integers mixed with them;

  • 'boolean for booleans, 'categorical for symbols, and 'string for strings, or for strings and symbols mixed.

The contract requires columns to name distinct columns of t. An exact integer outside both ranges, any other exact number too large for a flonum, a column of integers that needs both (a negative one and one of 263 or more), and a column whose values no one dtype holds are errors naming the column, the row and the element.

Examples:
> (define cars (table->polars mtcars '("mpg" "cyl" "wt")))
> (map (lambda (name) (dtype (ref cars name))) (column-names cars))

'(float64 float64 float64)

> (table->polars (list (cons "id" '(a b a)) (cons "x" '(1 2.5 3))))

shape: (3, 2)

┌─────┬─────┐

│ id  ┆ x   │

│ --- ┆ --- │

│ cat ┆ f64 │

╞═════╪═════╡

│ a   ┆ 1.0 │

│ b   ┆ 2.5 │

│ a   ┆ 3.0 │

└─────┴─────┘

> (dtype (ref (table->polars (list (cons "n" (list 1 (expt 2 63))))) "n"))

'uint64

> (table->polars (list (cons "x" '(1 "two"))))

table->polars: the table has a column whose values no Polars

dtype holds

  column: "x"

  row: 1

  element: "two"

2.7 Example datasets🔗ℹ

 (require glmnet/datasets) package: glmnet

glmnet/datasets provides the example datasets of R’s glmnet 4.1.10, which its vignettes use, and R’s mtcars and iris, which the formula examples use. glmnet does not re-export it. See Example datasets.

A dataset is a CSV file in the package, under "glmnet/datasets/", which "scripts/export-datasets.R" writes from R with 17 significant digits, so that every number reads back as R’s double. R glmnet’s datasets have a procedure each, which reads its file on its first call and returns the same values on every call. The values are the arguments of the family’s fitter, in order: the predictors as a design matrix whose columns are named "V1", "V2", and so on, as R’s coef names the columns of these unnamed matrices, and then the response, in the shape the family takes (see Data layout). mtcars and iris are tables, read when the module is instantiated. Every module that requires them shares them, so their columns are immutable vectors; csv-file->table reads a fresh copy whose columns can be changed.

R’s QuickStartExample, the data of the vignette’s Quick Start: 100 observations of 20 predictors and a numeric response, for elnet-fit and its relatives. See Quick start.

Examples:
> (require glmnet/datasets)
> (define-values (x y) (quick-start-example))
> x

#<design-matrix 100x20>

> (length y)

100

> (lasso x y #:lambda 0.1)

#<glmnet:gaussian λ=0.1 dev=0.8994 nz=9/20>

procedure

(binomial-example) → 
design-matrix? (listof (or/c 0 1))
R’s BinomialExample: 100 observations of 30 predictors and a 0/1 response, for logistic-fit and its relatives.

Examples:
> (define-values (bx by) (binomial-example))
> (list (design-matrix-nrows bx) (design-matrix-ncols bx))

'(100 30)

> (for/sum ([label (in-list by)]) label)

56

procedure

(multinomial-example) → 
design-matrix? (listof (or/c 0 1 2))
R’s MultinomialExample: 500 observations of 30 predictors and a class label, for multinomial-fit and its relatives. R’s classes are 1, 2 and 3; they are 0, 1 and 2 here, the labels multinomial-fit takes.

Examples:
> (define-values (mx my) (multinomial-example))
> (for/list ([k (in-range 3)])
    (for/sum ([label (in-list my)]) (if (= label k) 1 0)))

'(142 174 184)

R’s PoissonExample: 500 observations of 20 predictors and a count, for poisson-fit and its relatives.

Examples:
> (define-values (px py) (poisson-example))
> (apply max py)

395

R’s CoxExample: 1000 observations of 30 predictors, and the survival times and statuses, R’s columns time and status, for cox-fit and its relatives, which take them as two arguments. A status is 1 for a death and 0 for a censored time.

Examples:
> (define-values (cx time status) (cox-example))
> (for/sum ([s (in-list status)]) s)

692

> (cox-path cx time status #:nlambda 5)

#<glmnet-path:cox

  Df %Dev   Lambda

1  0 0.00 0.236800

2 13 5.62 0.023680

3 25 5.87 0.002368

4 29 5.87 0.000237

5 30 5.87 0.000024>

R’s MultiGaussianExample: 100 observations of 20 predictors and four numeric responses, for mgaussian-fit and its relatives. The responses are a design matrix whose columns are named "y1" to "y4", as R’s coef names them.

Examples:
> (define-values (gx gy) (multi-gaussian-example))
> (design-matrix-column-names gy)

'("y1" "y2" "y3" "y4")

R’s SparseExample: 100 observations of 20 predictors, 84% of whose entries are zero, and a numeric response. R holds the predictors as a sparse matrix; here they are a dense design matrix, until sparse input is supported (#11).

Examples:
> (define-values (sx sy) (sparse-example))
> (for*/sum ([row (in-list (design-matrix->rows sx))] [v (in-list row)])
    (if (zero? v) 1 0))

1678

value

mtcars : table?

R’s datasets::mtcars, from the 1974 Motor Trend road tests of 32 cars, as an association list from each of R’s eleven column names to its column, an immutable vector: "mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear" and "carb", in R’s order, with the cars in R’s order, from the Mazda RX4 to the Volvo 142E. R’s row names, the cars’ models, are not a column.

Examples:
> (table-column-names mtcars)

'("mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear" "carb")

> (cdr (assoc "wt" mtcars))

'#(2.62

   2.875

   2.32

   3.215

   3.44

   3.46

   3.57

   3.19

   3.15

   3.44

   3.44

   4.07

   3.73

   3.78

   5.25

   5.424

   5.345

   2.2

   1.615

   1.835

   2.465

   3.52

   3.435

   3.84

   3.845

   1.935

   2.14

   1.513

   3.17

   2.77

   3.57

   2.78)

> (vector-set! (cdr (assoc "wt" mtcars)) 0 3.0)

vector-set!: contract violation

  expected: (and/c vector? (not/c immutable?))

  given: '#(2.62 2.875 2.32 3.215 3.44 3.46 3.57 3.19 3.15

3.44 3.44 4.07 3.73 3.78 5.25 5.424 5.345 2.2 1.615 1.835

2.465 3.52 3.435 3.84 3.845 1.935 2.14 1.513 3.17 2.77 3.57

2.78)

value

iris : table?

R’s datasets::iris, Anderson’s measurements of 150 irises, 50 of each of three species, as an association list from each of R’s five column names to its column, an immutable vector: "Sepal.Length", "Sepal.Width", "Petal.Length" and "Petal.Width" in centimetres, and "Species", whose values are the strings "setosa", "versicolor" and "virginica". R holds the species as a factor with these levels, which is also the order that sorting the strings gives. The flowers are in R’s order.

Examples:
> (table-column-names iris)

'("Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species")

> (formula-predictor-names (Sepal.Length . ~ . Petal.Width + Species) iris)

'("Petal.Width" "Speciesversicolor" "Speciesvirginica")

2.8 Gaussian models🔗ℹ

The Gaussian family. The four named models are elnet-fit with a fixed #:alpha. See Ordinary least squares (λ = 0), Ridge regression (L2, α = 0), Lasso (L1, α = 1) and Elastic net (0 < α < 1).

struct

(struct elnet-result (intercept
    coefficients
    r-squared
    lambda
    num-passes)
    #:transparent)
  intercept : real?
  coefficients : (vectorof real?)
  r-squared : real?
  lambda : real?
  num-passes : exact-nonnegative-integer?
A fitted Gaussian model. coefficients has one entry per column of X, on the original predictor scale; r-squared is the fraction of variance explained; lambda is the penalty the solver used; num-passes is the number of coordinate-descent passes.

Like every result type, it prints as a summary of its family, λ, deviance ratio and number of nonzero coefficients (see Printing), and it implements gen:glmnet-model.

Examples:
> (define X '((1.0 2.0 1.0) (2.0 1.0 4.0) (3.0 4.0 9.0)
              (4.0 3.0 16.0) (5.0 6.0 25.0) (6.0 5.0 36.0)))
> (define y '(1.0 4.0 3.0 6.0 5.0 8.0))
> (define fit (lasso X y #:lambda 0.05))
> fit

#<glmnet:gaussian λ=0.05 dev=0.9941 nz=2/3>

> (elnet-result-intercept fit)

0.9999999999999996

> (elnet-result-coefficients fit)

'#(1.8292174872340037 -0.8292174872340038 0.0)

procedure

(elnet-fit X    
  y    
  #:lambda lambda    
  [#:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → elnet-result?
  X : design-matrix/c
  y : (response/c real?)
  lambda : (>=/c 0)
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits a Gaussian elastic-net model of the response y, one real per row of X, at a single lambda. As in R, a constant y (all zero, when intercept? is #f) has nothing to standardize and raises exn:fail. The four models below call elnet-fit, and its errors name the one called.

Examples:
> (elnet-fit X y #:alpha 0.5 #:lambda 0.5)

#<glmnet:gaussian λ=0.5 dev=0.7838 nz=2/3>

> (lasso X '(2.0 2.0 2.0 2.0 2.0 2.0) #:lambda 0.5)

lasso: y is constant; gaussian glmnet fails at

standardization step

procedure

(ols X    
  y    
  [#:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → elnet-result?
  X : design-matrix/c
  y : (response/c real?)
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-10
  max-iters : exact-positive-integer? = 100000
Ordinary least squares: elnet-fit with #:lambda 0.0, where #:alpha has no effect. The default thresh is tighter than the penalized fits’ because coordinate descent approaches the unpenalized solution slowly.

Example:
> (elnet-result-coefficients
   (ols '((1.0 2.0) (2.0 1.0) (3.0 4.0) (4.0 3.0) (5.0 6.0))
        '(1.0 4.0 3.0 6.0 5.0)))

'#(1.9999829127580482 -0.9999884545662485)

procedure

(ridge X    
  y    
  #:lambda lambda    
  [#:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → elnet-result?
  X : design-matrix/c
  y : (response/c real?)
  lambda : (>=/c 0)
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Ridge regression: elnet-fit with #:alpha 0.0. Shrinks every coefficient toward zero as lambda grows, but sets none exactly to zero.

Example:
> (elnet-result-coefficients (ridge X y #:lambda 0.1))

'#(1.1614345231080536 -0.7532810830027743 0.08306586531538782)

procedure

(lasso X    
  y    
  #:lambda lambda    
  [#:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → elnet-result?
  X : design-matrix/c
  y : (response/c real?)
  lambda : (>=/c 0)
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The lasso: elnet-fit with #:alpha 1.0. Sets coefficients exactly to zero, more of them as lambda grows.

Example:
> (elnet-result-coefficients (lasso X y #:lambda 0.5))

'#(0.8786585495440113 0.0 0.0)

procedure

(elastic-net X    
  y    
  #:alpha alpha    
  #:lambda lambda    
  [#:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → elnet-result?
  X : design-matrix/c
  y : (response/c real?)
  alpha : (real-in 0 1)
  lambda : (>=/c 0)
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The elastic net at an explicit alpha, which is required here. #:alpha 0.0 is ridge and #:alpha 1.0 is lasso.

Example:
> (elnet-result-coefficients (elastic-net X y #:alpha 0.5 #:lambda 0.5))

'#(0.5773773035370475 0.0 0.054686809526739884)

procedure

(elnet-predict fit X) → (listof real?)

  fit : elnet-result?
  X : design-matrix/c
The fitted value β₀ + xβ for each row of X: predict with its defaults.

Example:
> (elnet-predict fit '((7.0 6.0 49.0) (0.0 0.0 0.0)))

'(8.829217487234002 0.9999999999999996)

2.9 Binomial models🔗ℹ

The binomial family: two-class logistic regression on 0/1 labels. See Binomial logistic regression (classification).

struct

(struct logistic-result (intercept
    coefficients
    dev-ratio
    lambda
    num-passes)
    #:transparent)
  intercept : real?
  coefficients : (vectorof real?)
  dev-ratio : real?
  lambda : real?
  num-passes : exact-nonnegative-integer?
A fitted two-class model. intercept and coefficients are on the log-odds scale for class 1; dev-ratio is the fraction of null deviance explained.

Examples:
> (define X '((1.0 5.0 2.0) (2.0 6.0 1.0) (2.0 5.0 3.0) (1.0 4.0 1.0)
              (6.0 2.0 2.0) (5.0 1.0 1.0) (6.0 1.0 3.0) (5.0 2.0 1.0)))
> (define y '(0 0 0 0 1 1 1 1))
> (define fit (logistic-fit X y #:lambda 0.05))
> (logistic-result-coefficients fit)

'#(0.9597305669295235 -0.5738296629445224 0.0)

> (logistic-result-dev-ratio fit)

0.9168013564414029

procedure

(logistic-fit X    
  y    
  #:lambda lambda    
  [#:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → logistic-result?
  X : design-matrix/c
  y : (response/c (or/c 0 1))
  lambda : (>=/c 0)
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits a two-class logistic elastic-net model of the labels y. If a class probability collapses, typically under perfect separation, the exn:fail exception is raised; a larger lambda usually fixes it.

Example:
> (logistic-fit X y #:lambda 0.05)

#<glmnet:binomial λ=0.05 dev=0.9168 nz=2/3>

procedure

(logistic-predict-proba fit X) → (listof (real-in 0 1))

  fit : logistic-result?
  X : design-matrix/c
The class-1 probability 1 / (1 + exp(−(β₀ + xβ))) for each row of X: predict with #:type 'response.

Example:
> (logistic-predict-proba fit '((2.0 5.0 2.0) (5.0 2.0 2.0)))

'(0.0829824131519593 0.9000862010987204)

procedure

(logistic-predict fit    
  X    
  [#:threshold threshold]) → (listof (or/c 0 1))
  fit : logistic-result?
  X : design-matrix/c
  threshold : (real-in 0 1) = 0.5
Hard labels: 1 where logistic-predict-proba is at least threshold, otherwise 0. At the default threshold this is predict with #:type 'class, except for a row whose probability is exactly 0.5, which R and predict label 0.

Example:
> (logistic-predict fit '((2.0 5.0 2.0) (5.0 2.0 2.0)))

'(0 1)

2.10 Multinomial models🔗ℹ

The multinomial family: K-class logistic regression on integer labels. See Multinomial classification (K classes).

struct

(struct multinomial-result (intercepts
    coefficients
    dev-ratio
    lambda
    num-passes)
    #:transparent)
  intercepts : (vectorof real?)
  coefficients : (vectorof (vectorof real?))
  dev-ratio : real?
  lambda : real?
  num-passes : exact-nonnegative-integer?
A fitted K-class model. intercepts holds one real per class and coefficients one coefficient vector per class, each with one entry per predictor. Adding the same constant to every intercept leaves the probabilities unchanged, so, as R does, the intercepts are centred to sum to zero. dev-ratio is the fraction of null deviance explained.

Examples:
> (define X '((1.0 1.0) (2.0 1.0) (5.0 1.0) (6.0 1.0) (3.0 5.0) (4.0 6.0)))
> (define y '(0 0 1 1 2 2))
> (define fit (multinomial-fit X y #:lambda 0.05))
> (multinomial-result-coefficients fit)

'#(#(-0.6863780549780656 0.0)

   #(0.8092663108066648 0.0)

   #(0.0 1.140531561288397))

procedure

(multinomial-fit X 
  y 
  #:lambda lambda 
  [#:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → multinomial-result?
  X : design-matrix/c
  y : (response/c (and/c integer? (>=/c 0)))
  lambda : (>=/c 0)
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits a K-class multinomial elastic-net model. The labels y are integers, exact or inexact as in an flvector, and must cover 0 to K−1 with every class present; otherwise the exn:fail exception is raised. As with logistic-fit, a collapsed class probability raises exn:fail.

Examples:
> (multinomial-result-intercepts (multinomial-fit X y #:lambda 0.05))

'#(3.497091517940383 -1.7181551950723566 -1.778936322868027)

> (multinomial-fit X '(0 0 2 2 2 2) #:lambda 0.05)

multinomial-fit: class 1 has no observations; labels must

cover 0..2 contiguously

procedure

(multinomial-predict-proba fit X)

 → (listof (listof (real-in 0 1)))
  fit : multinomial-result?
  X : design-matrix/c
The softmax class probabilities for each row of X: one list of K entries, summing to 1, per row. This is predict with #:type 'response.

Example:
> (multinomial-predict-proba fit '((1.5 1.0) (3.5 5.5)))

'((0.912412962930536 0.046726550367775885 0.04086048670168831)

  (0.03128871441725081 0.03190510530876551 0.9368061802739837))

The most probable class for each row of X: predict with #:type 'class. On a tie the lowest class wins.

Example:
> (multinomial-predict fit '((1.5 1.0) (5.5 1.0) (3.5 5.5)))

'(0 1 2)

2.11 Cox models🔗ℹ

The Cox family: proportional-hazards survival models. There is no intercept, and so no #:intercept? keyword. See Cox proportional hazards (survival).

struct

(struct cox-result (coefficients dev-ratio lambda num-passes)
    #:transparent)
  coefficients : (vectorof real?)
  dev-ratio : real?
  lambda : real?
  num-passes : exact-nonnegative-integer?
A fitted Cox model. coefficients are on the log-hazard-ratio scale; dev-ratio is the fraction of null partial-likelihood deviance explained.

Examples:
> (define X '((0.5 1.0) (1.0 2.0) (1.5 1.0) (2.0 2.0)
              (2.5 1.0) (3.0 2.0) (3.5 1.0) (4.0 2.0)))
> (define times '(12.0 10.0 11.0 8.0 6.0 7.0 4.0 3.0))
> (define statuses '(0 0 0 1 1 1 1 1))
> (define fit (cox-fit X times statuses #:lambda 0.1))
> (cox-result-coefficients fit)

'#(1.9229529670643517 0.0)

procedure

(cox-fit X    
  times    
  statuses    
  #:lambda lambda    
  [#:alpha alpha    
  #:standardize? standardize?    
  #:thresh thresh    
  #:max-iters max-iters]) → cox-result?
  X : design-matrix/c
  times : (response/c (>/c 0))
  statuses : (response/c (or/c 0 1))
  lambda : (>=/c 0)
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits a Cox proportional-hazards elastic-net model. times are positive follow-up times and statuses the matching event indicators: 1 for an observed event, 0 for right-censoring. If no status is 1, the exn:fail exception is raised.

Examples:
> (cox-fit X times statuses #:lambda 0.5)

#<glmnet:cox λ=0.5 dev=0.2367 nz=1/2>

> (cox-fit X times '(0 0 0 0 0 0 0 0) #:lambda 0.1)

cox-fit: at least one observation must be an event (status =

1)

procedure

(cox-linear-predictor fit X) → (listof real?)

  fit : cox-result?
  X : design-matrix/c
The log relative hazard xβ for each row of X: predict with its defaults.

Example:
> (cox-linear-predictor fit '((1.0 1.0) (3.0 1.0)))

'(1.9229529670643517 5.768858901193055)

procedure

(cox-relative-risk fit X) → (listof (>/c 0))

  fit : cox-result?
  X : design-matrix/c
The relative risk exp(xβ) for each row of X: the factor by which the row’s hazard exceeds the baseline hazard. This is predict with #:type 'response.

Example:
> (cox-relative-risk fit '((1.0 1.0) (3.0 1.0)))

'(6.8411303037210045 320.17217603082736)

2.12 Poisson models🔗ℹ

The Poisson family: counts with a log link. See Poisson regression (counts).

struct

(struct poisson-result (intercept
    coefficients
    dev-ratio
    lambda
    num-passes)
    #:transparent)
  intercept : real?
  coefficients : (vectorof real?)
  dev-ratio : real?
  lambda : real?
  num-passes : exact-nonnegative-integer?
A fitted Poisson model. intercept and coefficients are on the log-mean scale; dev-ratio is the fraction of null deviance explained.

Examples:
> (define X '((1.0 2.0) (2.0 1.0) (3.0 2.0) (4.0 1.0)
              (5.0 2.0) (6.0 1.0) (7.0 2.0) (8.0 1.0)))
> (define y '(1 2 2 3 4 6 8 11))
> (define fit (poisson-fit X y #:lambda 0.2))
> (poisson-result-intercept fit)

-0.026590920441019072

> (poisson-result-coefficients fit)

'#(0.29703115403010283 0.0)

procedure

(poisson-fit X    
  y    
  #:lambda lambda    
  [#:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → poisson-result?
  X : design-matrix/c
  y : (response/c (>=/c 0))
  lambda : (>=/c 0)
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits a Poisson elastic-net model of the non-negative response y, which need not be integral. At least one value must be positive; an all-zero response raises exn:fail before fitting (R warns and returns an empty model).

Examples:
> (poisson-fit X y #:lambda 0.5)

#<glmnet:poisson λ=0.5 dev=0.9596 nz=1/2>

> (poisson-fit X '(0 0 0 0 0 0 0 0) #:lambda 0.5)

poisson-fit: the response has no positive count; Poisson

needs at least one y > 0

procedure

(poisson-predict-mean fit X) → (listof (>/c 0))

  fit : poisson-result?
  X : design-matrix/c
The fitted mean exp(β₀ + xβ) for each row of X: predict with #:type 'response.

Example:
> (poisson-predict-mean fit '((2.0 1.0) (9.0 1.0)))

'(1.7638014377572937 14.107258692835407)

2.13 Multi-response Gaussian models🔗ℹ

The multi-response Gaussian family: several numeric responses fitted jointly under a grouped penalty. See Multi-response Gaussian (grouped).

struct

(struct mgaussian-result (intercepts
    coefficients
    r-squared
    lambda
    num-passes)
    #:transparent)
  intercepts : (vectorof real?)
  coefficients : (vectorof (vectorof real?))
  r-squared : real?
  lambda : real?
  num-passes : exact-nonnegative-integer?
A fitted multi-response model. intercepts holds one real per response and coefficients one coefficient vector per response, each with one entry per predictor. r-squared is the fraction of variance explained across all responses.

Examples:
> (define X '((1.0 2.0) (2.0 1.0) (3.0 2.0) (4.0 1.0) (5.0 2.0) (6.0 1.0)))
> (define Y '((3.0 9.0) (5.0 8.0) (7.0 7.0) (9.0 6.0) (11.0 5.0) (13.0 4.0)))
> (define fit (mgaussian-fit X Y #:lambda 0.1))
> (mgaussian-result-intercepts fit)

'#(1.1833030277982335 9.908348486100884)

> (mgaussian-result-coefficients fit)

'#(#(1.9476277063433618 0.0) #(-0.9738138531716809 0.0))

procedure

(mgaussian-fit X    
  Y    
  #:lambda lambda    
  [#:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → mgaussian-result?
  X : design-matrix/c
  Y : design-matrix/c
  lambda : (>=/c 0)
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits a multi-response Gaussian elastic-net model. Y is a matrix with one row per observation and one column per response. With alpha above zero, the grouped lasso keeps or drops each predictor for every response at once. A constant column is fitted by its intercept alone, but when every column is constant (all zero, when intercept? is #f) there is nothing to fit and the exn:fail exception is raised, as for elnet-fit.

Example:
> (mgaussian-fit X Y #:lambda 0.5)

#<glmnet:mgaussian λ=0.5 dev=0.9829 nz=1/2>

procedure

(mgaussian-predict fit X) → (listof (listof real?))

  fit : mgaussian-result?
  X : design-matrix/c
The predictions a0r + xβr for each row of X: one list per row, with one entry per response. This is predict with its defaults.

Example:
> (mgaussian-predict fit '((7.0 1.0) (8.0 2.0)))

'((14.816696972201767 3.0916515138991176)

  (16.76432467854513 2.117837660727437))

2.14 Regularization paths🔗ℹ

A regularization path fits a decreasing sequence of λ in one call. Every family has a path fitter, and they all return a glmnet-path. Each fitter takes its family’s data arguments and keywords, with #:lambda made optional:

  • #:lambda is a list of penalties, fitted largest first. When it is #f (the default), glmnet chooses the sequence as R does: #:nlambda values from λmax, where every coefficient is zero, down to #:lambda-min-ratio times λmax. The ratio defaults to 0.01 when there are fewer observations than predictors, and to 0.0001 otherwise; glmnet raises a ratio below 1e-6, including 0, to 1e-6. The first value of an automatic sequence is the extrapolation R’s glmnet reports.

  • A path can hold fewer λ than asked for, as in R. An automatic sequence stops early once another λ would barely change the fit; the rule depends on the family (below). A binomial or multinomial path, automatic or not, stops where the fitted probabilities saturate at 0 and 1. And any path ends where glmnet fails at one of its λ, for example by not converging within #:max-iters passes: it keeps the λ before that one and logs a warning with the topic 'glmnet (shown with PLTSTDERR="warning@glmnet"), where R warns. The warning names the procedure called: the path fitter, or the cross-validation or formula procedure that called it, with the fold whose training data it was fitting. When glmnet fails at the first λ, the fitter raises exn:fail instead (R returns an empty model).

glmnet’s early-stopping rules apply only to an automatic sequence, and only from its fifth value on. It stops at the λ where, writing D for the deviance ratio:

  • Gaussian: D gains less than 1e-5 of its own value over the previous λ, or exceeds 0.999.

  • Multi-response Gaussian: the residual sum of squares falls by less than 1e-5 of its own value, or D exceeds 0.999.

  • Binomial and multinomial: D gains less than 1e-5 over the previous λ, or exceeds 0.999.

  • Poisson: D gains less than 0.0001 of its own value over the previous four λ, or exceeds 0.999.

  • Cox: D gains less than 0.001 of its own value over the previous four λ, or exceeds 0.99.

These are R’s glmnet.control defaults (fdev = 1e-5, devmax = 0.999, mnlam = 5) as each solver applies them.

struct

(struct glmnet-path (family
    lambda
    intercepts
    coefficients
    dev-ratio
    df
    num-passes)
    #:transparent)
  family : (or/c 'gaussian 'binomial 'multinomial 'cox 'poisson 'mgaussian)
  lambda : (vectorof real?)
  intercepts : (or/c #f vector?)
  coefficients : vector?
  dev-ratio : (vectorof real?)
  df : (vectorof exact-nonnegative-integer?)
  num-passes : exact-nonnegative-integer?
A fitted path. lambda holds the fitted penalties, decreasing, and every other per-λ field has one entry for each of them:

  • intercepts holds a real per λ, or a vector of one real per class or response for the multinomial and multi-response families. Multinomial intercepts are centred to sum to zero, as in multinomial-result. It is #f for Cox, which has no intercept.

  • coefficients holds a dense vector per λ with one entry per predictor, or a vector of such vectors (one per class or response).

  • dev-ratio is the fraction of null deviance explained.

  • df counts the predictors with a nonzero coefficient, in any class or response.

num-passes counts the coordinate-descent passes over the whole path.

A path prints as R prints one: a table with, for each fitted λ, the number of nonzero coefficients (Df), the percentage of null deviance explained (%Dev) and λ itself (see Printing). predict and coef evaluate a path at any λ.

Examples:
> (define X '((1.0 2.0 1.0) (2.0 1.0 4.0) (3.0 4.0 9.0)
              (4.0 3.0 16.0) (5.0 6.0 25.0) (6.0 5.0 36.0)))
> (define y '(1.0 4.0 3.0 6.0 5.0 8.0))
> (define path (elnet-path X y #:lambda '(1.0 0.1 0.01)))
> path

#<glmnet-path:gaussian

  Df  %Dev Lambda

1  1 61.07   1.00

2  2 97.63   0.10

3  3 99.98   0.01>

> (glmnet-path-lambda path)

'#(1.0 0.1 0.01)

> (glmnet-path-coefficients path)

'#(#(0.5858885276594514 0.0 0.0)

   #(1.6584349744677054 -0.6584349744677581 0.0)

   #(1.9597271819899955 -0.9658434974467867 0.0008200416386725593))

> (glmnet-path-df path)

'#(1 2 3)

procedure

(elnet-path X    
  y    
  [#:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-path?
  X : design-matrix/c
  y : (response/c real?)
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The Gaussian path, as elnet-fit fits one point of it.

Examples:
> (vector-length (glmnet-path-lambda (elnet-path X y)))

51

> (glmnet-path-df (elnet-path X y #:alpha 0.0 #:nlambda 5))

'#(3 3 3 3 3)

> (glmnet-path-lambda (elnet-path X y #:nlambda 5 #:lambda-min-ratio 0.1))

'#(2.000595149544493

   1.1250173275642348

   0.6326437348445768

   0.35576171622587116

   0.2000595149544493)

> (elnet-path X y #:lambda '(0.1) #:max-iters 1)

elnet-path: glmnet fitted no lambda: convergence was not

reached at the first lambda within #:max-iters passes; try a

larger #:max-iters (jerr=-1)

procedure

(logistic-path X 
  y 
  [#:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → glmnet-path?
  X : design-matrix/c
  y : (response/c (or/c 0 1))
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The binomial path, as logistic-fit fits one point of it.

Example:
> (glmnet-path-df (logistic-path X '(0 0 0 1 1 1) #:lambda '(0.3 0.1 0.03)))

'#(1 1 1)

procedure

(multinomial-path X 
  y 
  [#:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → glmnet-path?
  X : design-matrix/c
  y : (response/c (and/c integer? (>=/c 0)))
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The multinomial path, as multinomial-fit fits one point of it.

Examples:
> (define mpath (multinomial-path X '(0 0 1 1 2 2) #:lambda '(0.3 0.03)))
> (vector-ref (glmnet-path-coefficients mpath) 1)

'#(#(-1.2226648880844682 -1.2255649598724434 0.0)

   #(0.0 0.0 0.0)

   #(0.0 1.0608631342199921 0.1698597888820383))

procedure

(cox-path X    
  times    
  statuses    
  [#:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-path?
  X : design-matrix/c
  times : (response/c (>/c 0))
  statuses : (response/c (or/c 0 1))
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The Cox path, as cox-fit fits one point of it. There is no intercept, so glmnet-path-intercepts is #f.

Examples:
> (define cpath (cox-path X '(12.0 10.0 8.0 6.0 4.0 3.0) '(0 1 1 1 1 1)
                          #:lambda '(0.5 0.05)))
> (glmnet-path-coefficients cpath)

'#(#(0.353519335866768 0.0 0.0) #(2.336596711472855 0.0 0.0))

procedure

(poisson-path X 
  y 
  [#:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → glmnet-path?
  X : design-matrix/c
  y : (response/c (>=/c 0))
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The Poisson path, as poisson-fit fits one point of it.

Example:
> (glmnet-path-df (poisson-path X '(1 2 2 3 5 8) #:lambda '(0.5 0.05)))

'#(1 2)

procedure

(mgaussian-path X 
  Y 
  [#:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → glmnet-path?
  X : design-matrix/c
  Y : design-matrix/c
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
The multi-response Gaussian path, as mgaussian-fit fits one point of it.

Examples:
> (define gpath (mgaussian-path X '((3.0 9.0) (5.0 8.0) (7.0 7.0)
                                    (9.0 6.0) (11.0 5.0) (13.0 4.0))
                                #:lambda '(1.0 0.1)))
> (glmnet-path-intercepts gpath)

'#(#(2.8330302779823358 9.083484861008833)

   #(1.1833030277982326 9.908348486100884))

2.15 Cross-validation🔗ℹ

Every family has a cross-validation procedure, the equivalent of R’s cv.glmnet for that family, and they all return a glmnet-cv. Each takes its family’s data arguments and every keyword of its path fitter, which it passes on to each fit, and these:

  • #:type-measure is the loss, R’s type.measure. Each family offers R’s choices for it and defaults to R’s default, the first in the table below.

  • #:nfolds is the number of folds, at least 3 and at most the number of observations. The folds are drawn by random-fold-ids from current-pseudo-random-generator. It is ignored when #:fold-ids is given.

  • #:fold-ids, R’s foldid, assigns the observations to folds: one fold id per row of X, counting from 0, in a list, vector, flvector or f64vector, as a response can be. Every id from 0 to the largest must appear, and there must be at least 3 (see fold-ids/c).

  • #:grouped?, R’s grouped, computes the error and its standard error from the per-fold means when true, and from the per-observation losses otherwise. When the folds average fewer than 3 observations, they are never grouped; 'auc and 'C are always computed per fold; and when the folds average fewer than 10 observations, the Cox deviance is grouped. These adjustments, which R also makes, log a warning.

  • #:lambda is as for the path fitter, but needs at least two values. Every fold’s path is fitted at those values. Without it, each fold’s path chooses its own sequence, as R’s does, and is evaluated at the full-data path’s values by interpolation.

The data are checked before any fold is fitted: the response must have what its family needs, and so must every fold’s training data (each class, or an event). When a fit fails all the same, the error names the cross-validation procedure and says which fit failed: the one to all the data, or the one to the training data of a given held-out fold.

Family

  

Procedure

  

#:type-measure

Gaussian

  

elnet-cv

  

'mse, 'deviance, 'mae

Binomial

  

logistic-cv

  

'deviance, 'class, 'auc, 'mse, 'mae

Multinomial

  

multinomial-cv

  

'deviance, 'class, 'mse, 'mae

Cox

  

cox-cv

  

'deviance, 'C

Poisson

  

poisson-cv

  

'deviance, 'mse, 'mae

Multi-response

  

mgaussian-cv

  

'mse, 'deviance, 'mae

Choosing λ by cross-validation describes the procedure and each measure. The examples in this section use 60 observations of 8 predictors, of which the first three carry the signal:

> (random-seed 8)
> (define (noise) (- (* 2 (random)) 1))
> (define X60
    (for/list ([i (in-range 60)])
      (for/list ([j (in-range 8)])
        (noise))))
> (define beta '(3.0 -2.0 1.0 0.0 0.0 0.0 0.0 0.0))
> (define y60
    (for/list ([row (in-list X60)])
      (+ 1.0
         (for/sum ([b (in-list beta)] [x (in-list row)]) (* b x))
         (noise))))

struct

(struct glmnet-cv (lambda
    cvm
    cvsd
    cvup
    cvlo
    nzero
    measure
    name
    path
    lambda-min
    lambda-1se
    index-min
    index-1se
    fold-ids)
    #:transparent)
  lambda : (vectorof real?)
  cvm : (vectorof real?)
  cvsd : (vectorof real?)
  cvup : (vectorof real?)
  cvlo : (vectorof real?)
  nzero : (vectorof exact-nonnegative-integer?)
  measure : (or/c 'mse 'deviance 'mae 'class 'auc 'C)
  name : string?
  path : glmnet-path?
  lambda-min : real?
  lambda-1se : real?
  index-min : exact-nonnegative-integer?
  index-1se : exact-nonnegative-integer?
  fold-ids : (listof exact-nonnegative-integer?)
A cross-validated path, R’s cv.glmnet object. lambda holds the λ values of the full-data path, and cvm, cvsd, cvup, cvlo and nzero one entry for each:

  • cvm is the cross-validated error, cvsd its standard error, and cvup and cvlo are cvm plus and minus cvsd.

  • nzero counts the predictors in the model, as R counts them: the path’s glmnet-path-df, except for the multinomial, where it is the median over the classes rounded up, and the multi-response family, where R also counts the first response’s intercept.

A λ at which the standard error is undefined is left out, as R leaves it out; path still holds every fitted λ.

measure is the loss and name R’s name for it. lambda-min is the largest λ at which cvm is smallest, or largest for 'auc and 'C. lambda-1se is the largest λ whose cvm is within cvsd of that best value, with cvsd taken at lambda-min. index-min and index-1se are their positions in lambda, counting from 0. path is the path fitted to all the data, and fold-ids the fold of each observation.

A glmnet-cv implements gen:glmnet-model. Its path is path; predict and coef default to lambda-1se, as R’s predict.cv.glmnet does, and also accept 'lambda-min, 'lambda-1se or any λ; and deviance-ratio is the path’s at lambda-1se. It prints like R’s print.cv.glmnet, with the index counted from 0: the measure, then for each of lambda-min and lambda-1se its value, index, cvm, cvsd and nzero (see Printing).

Examples:
> (define cv (elnet-cv X60 y60))
> cv

#<glmnet-cv:gaussian Mean-Squared Error

        Lambda  Index  Measure       SE  Nonzero

  min  0.05424     37   0.3984  0.05967        6

  1se   0.1656     25   0.4468  0.06523        3>

> (glmnet-cv-lambda-min cv)

0.054239392702548954

> (vector-ref (glmnet-cv-cvm cv) (glmnet-cv-index-min cv))

0.3984109992356747

> (coef cv)

'#(0.878293049854064

   2.7178140193293583

   -1.4713240447178129

   0.6956206079817456

   0.0

   0.0

   0.0

   0.0

   0.0)

> (coef cv #:lambda 'lambda-min)

'#(0.9154747391997028

   2.903879277459048

   -1.7054265800660615

   0.8686835340984955

   -0.008767521015963693

   -0.11136295679101096

   0.0

   0.0

   -0.0343600088528011)

> (predict cv '((0.5 -0.5 0.0 0.0 0.0 0.0 0.0 0.0)))

'(2.9728620818776497)

> (deviance-ratio cv)

0.9109831280347133

procedure

(elnet-cv X    
  y    
  [#:type-measure type-measure    
  #:nfolds nfolds    
  #:fold-ids fold-ids    
  #:grouped? grouped?    
  #:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-cv?
  X : design-matrix/c
  y : (response/c real?)
  type-measure : (or/c 'mse 'deviance 'mae) = 'mse
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates elnet-path. 'mse and 'deviance are both the mean squared error, as in R; 'mae is the mean absolute error.

Examples:
> (elnet-cv X60 y60 #:type-measure 'mae #:alpha 0.5)

#<glmnet-cv:gaussian Mean Absolute Error

        Lambda  Index  Measure       SE  Nonzero

  min  0.05656     44   0.5059  0.04876        6

  1se   0.2506     28   0.5471  0.04654        3>

> (define halves (for/list ([i (in-range 60)]) (modulo i 2)))
> (elnet-cv X60 y60 #:fold-ids halves)

elnet-cv: contract violation;

 cross-validation needs at least 3 folds

  expected: fold ids from 0 to at least 2

  given: '(0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0

1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0

1 0 1 0 1)

  in: the #:fold-ids argument of

      (->*

       ((or/c

         design-matrix?

         (listof (or/c list? vector?))

         (vectorof (or/c list? vector?)))

        (response/c real?))

       (#:alpha

        (real-in 0 1)

        #:fold-ids

        fold-ids/c

        #:grouped?

        boolean?

        #:intercept?

        boolean?

        #:lambda

        (or/c

         #f

         (and/c

          (listof (>=/c 0))

          (property/c length (>=/c 2))))

        #:lambda-min-ratio

        (or/c #f (and/c real? (>=/c 0) (</c 1)))

        #:max-iters

        exact-positive-integer?

        #:nfolds

        (integer-in 3 #f)

        #:nlambda

        exact-positive-integer?

        #:standardize?

        boolean?

        #:thresh

        (>/c 0)

        #:type-measure

        (or/c 'mse 'deviance 'mae))

       glmnet-cv?)

  contract from: <pkgs>/glmnet/core/elnet.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/core/elnet.rkt:78:3

procedure

(logistic-cv X    
  y    
  [#:type-measure type-measure    
  #:nfolds nfolds    
  #:fold-ids fold-ids    
  #:grouped? grouped?    
  #:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-cv?
  X : design-matrix/c
  y : (response/c (or/c 0 1))
  type-measure : (or/c 'deviance 'class 'auc 'mse 'mae)
   = 'deviance
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates logistic-path. 'deviance is the binomial deviance, 'class the misclassification rate, 'auc the area under the ROC curve of each fold, and 'mse and 'mae the squared and absolute differences between the 0/1 labels of both classes and their predicted probabilities, summed over the two classes. When the folds average fewer than 10 observations, 'auc becomes 'deviance, as in R. y must hold both classes.

Examples:
> (define labels (for/list ([v (in-list y60)]) (if (> v 1.0) 1 0)))
> (logistic-cv X60 labels #:type-measure 'class)

#<glmnet-cv:binomial Misclassification Error

        Lambda  Index  Measure       SE  Nonzero

  min  0.05351     20     0.05  0.02546        4

  1se  0.05872     19  0.06667  0.02722        4>

> (logistic-cv X60 labels #:type-measure 'auc #:nfolds 5)

#<glmnet-cv:binomial AUC

        Lambda  Index  Measure       SE  Nonzero

  min   0.0336     25   0.9875   0.0125        4

  1se  0.05872     19   0.9813  0.01875        4>

procedure

(multinomial-cv X 
  y 
  [#:type-measure type-measure 
  #:nfolds nfolds 
  #:fold-ids fold-ids 
  #:grouped? grouped? 
  #:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → glmnet-cv?
  X : design-matrix/c
  y : (response/c (and/c integer? (>=/c 0)))
  type-measure : (or/c 'deviance 'class 'mse 'mae) = 'deviance
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates multinomial-path. The measures are those of logistic-cv, summed over the K classes, without 'auc. Every fold’s training data must contain every class.

Examples:
> (define classes
    (for/list ([v (in-list y60)])
      (cond [(< v -0.5) 0] [(< v 2.5) 1] [else 2])))
> (multinomial-cv X60 classes #:type-measure 'class)

#<glmnet-cv:multinomial Misclassification Error

         Lambda  Index  Measure       SE  Nonzero

  min  0.009155     38   0.1333  0.04157        4

  1se     0.016     32   0.1667  0.04303        4>

procedure

(cox-cv X    
  times    
  statuses    
  [#:type-measure type-measure    
  #:nfolds nfolds    
  #:fold-ids fold-ids    
  #:grouped? grouped?    
  #:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-cv?
  X : design-matrix/c
  times : (response/c (>/c 0))
  statuses : (response/c (or/c 0 1))
  type-measure : (or/c 'deviance 'C) = 'deviance
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates cox-path. 'deviance is the partial likelihood deviance per observation: when grouped, each fold contributes the deviance of all the data less that of its training data, at the fold’s coefficients, as in R; otherwise the deviance of the fold itself. 'C is Harrell’s concordance index of each fold. Every fold’s training data must contain an event.

A held-out fold’s own deviance is undefined when the fold has no event, or when its first event is among its last two observations in time order. R stops then, and so does cox-cv, naming the fold. With #:grouped? #t, the default, the fold’s own deviance is not needed.

Examples:
> (define times (for/list ([v (in-list y60)]) (exp (* -0.3 v))))
> (define statuses
    (for/list ([i (in-range 60)])
      (if (zero? (modulo i 5)) 0 1)))
> (cox-cv X60 times statuses)

#<glmnet-cv:cox Partial Likelihood Deviance

        Lambda  Index  Measure      SE  Nonzero

  min  0.02501     34    3.955  0.2003        7

  1se  0.07637     22    4.137  0.1787        4>

> (cox-cv X60 times statuses #:type-measure 'C)

#<glmnet-cv:cox C-index

       Lambda  Index  Measure       SE  Nonzero

  min  0.1764     13     0.95  0.01859        3

  1se  0.1764     13     0.95  0.01859        3>

> (define thirds (for/list ([i (in-range 60)]) (modulo i 3)))
> (define fold-0-censored
    (for/list ([i (in-range 60)])
      (if (zero? (modulo i 3)) 0 1)))
> (cox-cv X60 times fold-0-censored #:fold-ids thirds #:grouped? #f)

cox-cv: a held-out fold has no event, so its deviance is

undefined without grouping; use #:grouped? #t

  held-out fold: 0

procedure

(poisson-cv X    
  y    
  [#:type-measure type-measure    
  #:nfolds nfolds    
  #:fold-ids fold-ids    
  #:grouped? grouped?    
  #:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-cv?
  X : design-matrix/c
  y : (response/c (>=/c 0))
  type-measure : (or/c 'deviance 'mse 'mae) = 'deviance
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates poisson-path. 'deviance is the Poisson deviance; 'mse and 'mae compare the counts with the predicted means.

Examples:
> (define counts
    (for/list ([v (in-list y60)])
      (inexact->exact (round (exp (* 0.3 v))))))
> (poisson-cv X60 counts)

#<glmnet-cv:poisson Poisson Deviance

        Lambda  Index  Measure       SE  Nonzero

  min  0.03115     33   0.1973  0.03133        5

  1se  0.08667     22   0.2268  0.04235        5>

procedure

(mgaussian-cv X    
  Y    
  [#:type-measure type-measure    
  #:nfolds nfolds    
  #:fold-ids fold-ids    
  #:grouped? grouped?    
  #:lambda lambda    
  #:nlambda nlambda    
  #:lambda-min-ratio lambda-min-ratio    
  #:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → glmnet-cv?
  X : design-matrix/c
  Y : design-matrix/c
  type-measure : (or/c 'mse 'deviance 'mae) = 'mse
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = #t
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates mgaussian-path. The measures are those of elnet-cv, summed over the responses.

Examples:
> (define Y60
    (for/list ([row (in-list X60)] [v (in-list y60)])
      (list v (- (list-ref row 1) (list-ref row 3)))))
> (mgaussian-cv X60 Y60)

#<glmnet-cv:mgaussian Mean-Squared Error

        Lambda  Index  Measure       SE  Nonzero

  min   0.0341     42   0.3735  0.03533        7

  1se  0.08645     32   0.4027  0.04467        6>

procedure

(random-fold-ids n [#:nfolds nfolds])

 → (listof exact-nonnegative-integer?)
  n : exact-positive-integer?
  nfolds : exact-positive-integer? = 10
Assigns n observations to nfolds folds at random, as R’s sample(rep(seq(nfolds), length = n)) does: the fold ids 0, 1, ..., (- nfolds 1), 0, ... are shuffled with current-pseudo-random-generator, so that the folds differ in size by at most one. nfolds, given or by default, must not exceed n. The cross-validation procedures call it when they are not given #:fold-ids; calling it directly gives folds to reuse across calls.

Examples:
> (random-fold-ids 10 #:nfolds 3)

'(2 0 1 2 0 0 0 2 1 1)

> (define (draw)
    (parameterize ([current-pseudo-random-generator
                    (make-pseudo-random-generator)])
      (random-seed 1)
      (random-fold-ids 10 #:nfolds 3)))
> (equal? (draw) (draw))

#t

> (random-fold-ids 3 #:nfolds 5)

random-fold-ids: contract violation;

 there are more folds than observations

 folds: 5

 observations: 3

  in: (->i

       ((n exact-positive-integer?))

       (#:nfolds (nfolds exact-positive-integer?))

       #:pre/desc

       (n nfolds)

       (folds-within n nfolds)

       (_ (listof natural?)))

  contract from: <pkgs>/glmnet/core/cv.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/core/cv.rkt:30:3

The contract on the #:fold-ids argument of every cross-validation procedure. It accepts #f, for folds drawn by random-fold-ids, or fold ids: a non-empty list, vector, flvector or f64vector of non-negative integers, exact or, as in an flvector, inexact, that use every fold from 0 to the largest id, of which there are at least 3. A violation says which fold is missing. The procedures check the ids again on their own copy, with one id per observation.

Examples:
> (require racket/contract racket/flonum)
> (contract-first-order-passes? fold-ids/c '(0 1 2 0 1 2))

#t

> (contract-first-order-passes? fold-ids/c (flvector 0.0 1.0 2.0 2.0))

#t

> (contract-first-order-passes? fold-ids/c #f)

#t

> (contract-first-order-passes? fold-ids/c '(0 1 0 1))

#f

> (contract-first-order-passes? fold-ids/c (vector 0 2 3 0 2 3))

#f

> (elnet-cv X60 y60 #:fold-ids (for/vector ([i (in-range 60)]) (modulo i 4)))

#<glmnet-cv:gaussian Mean-Squared Error

        Lambda  Index  Measure       SE  Nonzero

  min  0.04942     38    0.416  0.04962        6

  1se   0.1375     27   0.4639   0.1194        3>

> (elnet-cv X60 y60 #:fold-ids (for/list ([i (in-range 60)]) (* 2 (modulo i 3))))

elnet-cv: contract violation;

 a fold has no observations; fold ids must cover 0 to 4

  expected: an observation in fold 1

  given: '(0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0

2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 0

2 4 0 2 4)

  in: the #:fold-ids argument of

      (->*

       ((or/c

         design-matrix?

         (listof (or/c list? vector?))

         (vectorof (or/c list? vector?)))

        (response/c real?))

       (#:alpha

        (real-in 0 1)

        #:fold-ids

        fold-ids/c

        #:grouped?

        boolean?

        #:intercept?

        boolean?

        #:lambda

        (or/c

         #f

         (and/c

          (listof (>=/c 0))

          (property/c length (>=/c 2))))

        #:lambda-min-ratio

        (or/c #f (and/c real? (>=/c 0) (</c 1)))

        #:max-iters

        exact-positive-integer?

        #:nfolds

        (integer-in 3 #f)

        #:nlambda

        exact-positive-integer?

        #:standardize?

        boolean?

        #:thresh

        (>/c 0)

        #:type-measure

        (or/c 'mse 'deviance 'mae))

       glmnet-cv?)

  contract from: <pkgs>/glmnet/core/elnet.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/core/elnet.rkt:78:3

2.16 Formulas🔗ℹ

The formula front end fits any family from a table (see Tables). A formula names the response and, in R’s formula algebra, the predictor terms. formula-fit, formula-path and formula-cv expand the terms against the table, as R’s terms does, build their design matrix, as R’s model.matrix does, and call the fit procedure, path fitter or cross-validation procedure of the family that #:family names on it. They return a formula-model, which keeps the formula, the names of the design matrix’s columns and the expanded terms, so that coef keys the coefficients by name and predict builds the design matrix of a new table (see Generic model interface). Formulas and named data works through examples, The formula algebra explains the algebra, Transforms the transforms, and Categorical predictors the factors.

The terms of a formula expand as R’s terms expands them:

  • A column name is a term of one variable, that column. all is a term for each column of the table that is not a response column, in the table’s order, as R’s . is.

  • A transform, such as (log hp) or (I (expt hp 2)), is a term of one variable, computed from columns (see below).

  • (factor x), R’s factor(x), is a term of one variable, the values of the column x or of the transform x as a factor (see below).

  • (+ a b), or a + b, is the terms of a, then those of b. Terms written side by side after the response are joined in the same way.

  • (- a b), or a - b, is the terms of a except those equal to a term of b. A term of b that a does not have is ignored. (- a) removes a from nothing.

  • (: a b), or a : b, is every term of a combined with every term of b: an interaction, whose variables are those of both.

  • (* a b), or a * b, crosses them: the terms of a + b + a : b. When a has no terms, as 1 and 0 have none, neither has a * b, as in R, whose y ~ 0*x + z is y ~ z - 1.

  • (^ a n), or a ^ n, crosses a with itself: every interaction of at most n of the terms of a, for an exact integer n of at least 2. A variable crossed with itself is itself, so (^ x 2) is x, as x^2 is in R.

  • 1 keeps the intercept and 0 removes it, so - 1 removes it too. Inside the removed operand of - their meanings swap, and the last one written wins, as in R. A formula without them has an intercept.

A term is a set of variables, so (: wt hp) and (: hp wt) are the same term. Each operation drops the terms it repeats, keeping the first. The terms are then ordered by degree, the main effects first and then the interactions of two variables, of three, and so on, each degree in the order the terms first appear; the variables of a term are in the order they first appear in the formula. The prefix forms with more operands are the infix forms folded from the left: (* a b c) is a * b * c.

The design matrix, which formula-design-matrix returns, has one column per term for numeric variables: a column of the table, or for an interaction the product of its variables’ columns, named by joining their names with colons, as R names them: "wt:hp". A factor has a column for each of its levels, or for each but the first (see below), and an interaction a column for each combination of its variables’ columns, the first variable’s varying fastest. A column of the table keeps its name, where R puts backticks around a name that R’s syntax does not allow, such as "blood pressure". It has no intercept column, since the family’s procedure fits the intercept. Every column the formula names, even one that it removes, must be a column of the table, and so must the response columns, which must be distinct. Names are compared as strings. The design matrix’s column names must be distinct, and none can be "(Intercept)", since coef keys the coefficients by them and the intercept by "(Intercept)"; the formula procedures raise an error that names the column otherwise.

A group that starts with an identifier other than the operators, such as (log hp), is a transform, as R’s log(hp) is: a Racket expression, evaluated for each row of the table, whose values are a column of the design matrix named by the transform’s source, "(log hp)". (I expr), R’s I(), is the transform whose expression is expr. Inside a transform the operators are Racket’s: (I (* wt hp)) is the product of two columns, and (log (+ hp 1)) adds 1. An identifier in argument position, that is, not first in a group, is the table’s column of that name when the table has one, and otherwise the Racket binding of that name where the formula is written, as R looks for a name in the data and then in the formula’s environment. An identifier first in a group is always the Racket binding, even when the table has a column of that name, as R looks up the function of a call by name and skips a column: with a column max, (max max x) is the larger of that column and x, R’s pmax(max, x). A name that the transform binds itself, with let, lambda or a for form, is its own, a name in quoted data is data, and a name that Racket’s forms match as a literal, =>, else, unquote, unquote-splicing, ... or _, keeps its Racket meaning; the columns a transform reads are the others. A column named like one of these literals cannot be read inside a transform, where (log else) is Racket’s syntax error else: not allowed as an expression; write it as an ordinary term, or rename it in the table. The expression is evaluated once for each row, with each column name standing for the row’s value, so a transform is elementwise. A fit evaluates each transform the terms use once on the table, as R’s model.frame does, and takes a factor’s levels and the design matrix from those values, so a transform with side effects or random values gives both from the same draw. A transform that - removes is not evaluated, where R’s model.frame still evaluates it once. A column of numbers gives its value as a flonum, and a column of strings, symbols or booleans gives the value itself, as R’s calls read a character or logical column, so (equal? Species "setosa") is R’s I(Species == "setosa") and (not b) is R’s I(!b). A column that a transform reads must hold one kind of value, numbers, strings, symbols or booleans, and its numbers must be finite; an error names the transform and the column. Strings and symbols are two kinds here, though a factor’s levels match them by label, since a transform can tell them apart: (equal? g "a") is false for the symbol 'a. predict needs each such column to hold the kind of value it held when the model was fitted, and an error names the transform, the column and both kinds; a boolean column given as 1 and 0 would otherwise change (if b x 0) silently, as 0 is true in Racket. A name that is neither a column nor bound is an error, naming it, when the transform is evaluated, not when the formula is compiled. Each value must be a real number and finite, or else each a string, a symbol or a boolean, which makes the transform a factor (see below), and an error names the transform and the row. A transform must read at least one column. It is a variable of the algebra, so (* (log hp) wt) has the terms "(log hp)", "wt" and "(log hp):wt", and a transform written twice, as the same datum, is one term.

A variable is a factor, a categorical variable, when its values, a column’s or a transform’s, are strings or symbols, or booleans, as R’s character and logical variables are, and under (factor x), whose values may be numbers too. Otherwise its values must be numbers. A factor’s levels are its distinct values, labelled as R labels them: a string itself, a symbol’s name, "FALSE" and "TRUE" for booleans, and a number as Racket prints it, without a trailing .0; values with the same label are one level. They are sorted as R’s factor() sorts them: numbers by value, labels by string<?, which is R’s order in the C locale, and "FALSE" before "TRUE". A boolean variable other than (factor x) has both boolean levels, even when one is absent, as R’s logical variable has. A factor needs at least two levels; a column or a transform whose values mix numbers, strings or symbols, and booleans is an error that names it. A factor is coded by treatment contrasts, as R’s model.matrix codes it by default: a 0/1 column for each level but the first, the baseline, named by the variable’s name and the level’s label, as "(factor cyl)6" or "Speciesversicolor". It has a column for every level instead, dummies, where R’s model.matrix has: in a term that has other variables, when no earlier term contains all of them (R’s factors attribute of terms), and, without an intercept, for the first factor of the first term that has one. So in (~ mpg wt : (factor cyl)) the factor has dummies, and after wt, or after wt : hp, which contains wt, it has contrasts. The fitted model keeps the levels (see formula-model-levels), and predict codes a new table with them; a value whose level the model was not fitted with is an error that names the factor and the new levels.

A response column that stands alone as a term on the right-hand side is dropped from it, and a warning naming the procedure called is logged on the 'glmnet topic, as R’s model.matrix warns. An interaction with a response column is kept, as in R. The columns of a (surv time status) or multi-column response are each treated in the same way.

The intercept of the fit is the formula’s: without #:intercept?, the fit procedures fit an intercept unless the formula has 0 or - 1. An explicit #:intercept? must agree with a formula that writes 1, 0 or - 1, which its contract checks, naming both; with a formula that writes none, it decides. The Cox family fits no intercept, and accepts 1, 0 and - 1 with any #:intercept?. As in R, they still decide how a factor is coded: with 0 or - 1, the first factor has a column for every level (see above). A formula whose terms leave no predictors, such as (~ y 1), cannot be fitted: glmnet needs at least one predictor.

The examples in this section use R’s mtcars, 32 cars of the 1974 Motor Trend road tests, and R’s iris, 150 irises of three species, which glmnet/datasets provides as tables (see Example datasets):

> (require glmnet/datasets)
> (table-column-names mtcars)

'("mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear" "carb")

> (table-column-names iris)

'("Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species")

syntax

(~ response term ...)

(~ response maybe-sign term operation ...)
 
response = column
  | (surv time-column status-column)
  | (column ...+)
     
term = column
  | all
  | 1
  | 0
  | (+ term ...+)
  | (- term ...+)
  | (* term term ...+)
  | (: term term ...+)
  | (^ term power)
  | (maybe-sign term operation ...+)
  | (I expr)
  | (proc-id arg-expr ...)
  | (factor column)
  | (factor (I expr))
  | (factor (proc-id arg-expr ...))
     
operation = + term
  | - term
  | * term
  | : term
  | ^ power
     
maybe-sign = 
  | +
  | -
     
column = identifier
  | string
A formula: the response, then the right-hand side, either terms side by side, which are joined as by +, or terms with infix operators between them. The infix operators have R’s precedence: ^ binds tightest, then a leading sign, then :, then *, then + and -; each groups from the left, except that a power cannot be raised again, which R does not allow either. A parenthesized group is a prefix form when it starts with an operator and has no operators between its terms, a factor when it starts with factor, a transform when it starts with I or another identifier that is not a word of the formula language, and an infix one otherwise. A power is an exact integer of at least 2. The Racket reader’s infix dots make (y . ~ . x + z) the same as (~ y x + z), so a formula can read as R writes it.

The body is quoted, as by quote, except for its transforms, and the result is (make-formula 'response rhs ...), with the elements of the right-hand side as written. Each transform becomes a transform-term? value that computes it, whose name is its source, the datum as write writes it with print-reader-abbreviations on, so that quoted data is written 'x and not (quote x). A transform’s proc-id must be bound where the formula is written: in a module, anywhere in it; at the top level, as in the REPL, by a definition evaluated before the formula, since a later one cannot be seen there. A name in argument position is read when the transform runs, so at the top level it can be defined after the formula. An arg-expr or the expr of I is any Racket expression, checked as it is expanded, where the names bound in it are known: ^, R’s power, is a syntax error that says to write expt unless it is bound, and so is R’s infix arithmetic, such as (hp * wt), whose first name is not bound, which says to write (* hp wt). A column is written as an identifier or a string, and as a string when its name is a word of the formula language (all, surv, the operators, and R’s / and %in%, which it does not have), starts with -, or contains :, *, ^, / or +. The reader reads wt:hp and -wt as one name, so an operator needs spaces around it; written as an identifier, such a name is a syntax error that says so. So is a number other than 0 and 1; a 0 or 1 written with more than its digit, such as -0 or +0, since the reader reads both as 0 and drops the sign that R reads as an operator, so that R’s -0 is written (- 0) and its +0 (+ 0); a group that is neither a prefix form, an infix form nor a transform, such as (1 x z); a quoted name, such as 'hp, since ~ quotes the names itself; and R’s / or %in%, between terms or at the head of a group, where the error for / says to write a ratio as (I (/ hp wt)). The error points at the form that is wrong. Inside a transform, a column whose name is not an identifier is written with bars, as (log |blood pressure|).

Whether a 0 or 1 is written with more than its digit is read from its source location, the only trace of its spelling. A macro that builds one into a formula therefore gives it a location of its own, or none: a 0 made by datum->syntax with the location of a longer form, such as the #f option it stands for, counts as written with more than its digit, and is a syntax error.

(factor column) is R’s factor(column), and is quoted as the list (factor column). factor of a transform is the list of 'factor and the transform’s transform-term? value. A factor group takes one column or transform, and is named by its source, as "(factor cyl)"; a column whose name is not an identifier is written there as a string, "(factor \"n k\")".

The response is one column, (surv time-column status-column) for the Cox family, or a list of columns for the multi-response Gaussian family. It is not a transform: a response that starts with I, or with a bound name and holds more than column names, such as (log (+ mpg 1)), is a syntax error that says a transformed response is not supported. So is a list of columns whose first name is a procedure where the formula is written, such as (log mpg), an error raised when the formula is made, as only then is the value known; the columns of such a response are written as strings, as in ("log" "mpg").

Examples:
> (~ mpg (* wt hp))

(~ mpg (* wt hp))

> (mpg . ~ . wt * hp)

(~ mpg wt * hp)

> (formula-predictor-names (mpg . ~ . wt * hp) mtcars)

'("wt" "hp" "wt:hp")

> (formula-predictor-names (~ mpg (^ (+ wt hp qsec) 2)) mtcars)

'("wt" "hp" "qsec" "wt:hp" "wt:qsec" "hp:qsec")

> (formula-predictor-names (mpg . ~ . (wt + hp + qsec) ^ 2 - wt : hp) mtcars)

'("wt" "hp" "qsec" "wt:qsec" "hp:qsec")

> (formula-predictor-names (mpg . ~ . 0 + wt + hp) mtcars)

'("wt" "hp")

> (formula-predictor-names (~ mpg (- all cyl disp)) mtcars)

'("hp" "drat" "wt" "qsec" "vs" "am" "gear" "carb")

> (formula-predictor-names (mpg . ~ . (log hp) * wt + (I (/ disp cyl))) mtcars)

'("(log hp)" "wt" "(I (/ disp cyl))" "(log hp):wt")

> (formula-predictor-names (mpg . ~ . wt * (factor cyl)) mtcars)

'("wt" "(factor cyl)6" "(factor cyl)8" "wt:(factor cyl)6" "wt:(factor cyl)8")

> (formula-predictor-names (mpg . ~ . (factor (> gear 3)) + wt) mtcars)

'("(factor (> gear 3))TRUE" "wt")

> (formula-predictor-names (Sepal.Length . ~ . 0 + Species + Petal.Width) iris)

'("Speciessetosa" "Speciesversicolor" "Speciesvirginica" "Petal.Width")

> (formula-predictor-names (Sepal.Length . ~ . (equal? Species "setosa") + Petal.Width) iris)

'("(equal? Species \"setosa\")TRUE" "Petal.Width")

> (~ (surv time status) age "blood pressure")

(~ (surv time status) age "blood pressure")

> (~ mpg wt:hp)

eval:272:0: ~: wt:hp reads as one name; put spaces around an

operator, as in wt : hp, or write a column with this name as

a string, "wt:hp"

  at: wt:hp

  in: (~ mpg wt:hp)

  parsing context:

   while parsing a column name

    term: wt:hp

    location: eval:272:0

   while parsing a term: a column, all, 1, 0, a transform

such as (log x), (factor x), or a group such as (* x z) or

(x + z)

    term: wt:hp

    location: eval:272:0

   while parsing the right-hand side of a formula

    term: (wt:hp)

    location: eval:272:0

Accepts a term as data, as ~ makes it: a column name (a string, or a symbol that ~ accepts as a column name), 'all, 0, 1, a transform-term? value, a list of 'factor and a column name or a transform-term? value, a prefix form, or a group of terms with infix operators between them. A transform is a transform-term? value, not a list, which would be a group of terms.

Examples:
> (formula-term/c '(* wt hp))

#t

> (formula-term/c '(wt + hp))

#t

> (formula-term/c '(^ (+ wt hp) 2))

#t

> (formula-term/c (transform-term "(log wt)" '(wt) log))

#t

> (formula-term/c '(factor cyl))

#t

> (formula-term/c '(log wt))

#f

Accepts the right-hand side of a formula as data, as ~ takes it: a list of terms, joined as by +, or an infix sequence, a term with a leading sign or with infix operators and terms after it. The empty list is the right-hand side of a formula without predictors.

Examples:
> (formula-rhs/c '(wt hp (: wt hp)))

#t

> (formula-rhs/c '(- 1 + wt * hp))

#t

> (formula-rhs/c '(wt hp + qsec))

#f

Accepts a response as data: a column name, a list of 'surv and two column names, or a non-empty list of column names.

Examples:
> (formula-response/c '(surv time status))

#t

> (formula-response/c '(surv time))

#f

procedure

(transform-term name columns proc) → transform-term?

  name : string?
  columns : (and/c (listof (or/c string? symbol?)) pair?)
  proc : (procedure-arity-includes/c (length columns))
A transform as data, for make-formula: the design-matrix column name, whose value in each row is proc applied to the row’s value of each of the columns, in order: a flonum for a column of numbers, and the value itself for a column of strings, symbols or booleans. Each of the columns must be a column of the table that holds one kind of value, strings and symbols counting as two, and whose numbers are finite; new data for predict must give it the same kind. ~ makes the same value from a transform written in a formula, with its source as the name and the names it reads as the columns, except that a name of that transform that is not a column of the table is the Racket binding of that name.

A procedure has no source and names no columns, so a transform as data names both. Two transforms are equal? when their names and columns are, so a transform named by its source is equal? to the one that ~ makes from that source. It prints as #<transform-term and its name, and a formula prints it as its name.

Examples:
> (define log-hp (transform-term "(log hp)" '(hp) log))
> (define f (make-formula 'mpg log-hp '* 'wt))
> f

(~ mpg (log hp) * wt)

> (equal? f (mpg . ~ . (log hp) * wt))

#t

> (define power-to-weight (transform-term "hp/wt" '("hp" "wt") /))
> (define P (formula-design-matrix (make-formula 'mpg power-to-weight) mtcars))
> (design-matrix->rows (design-matrix-select-rows P '(0 1 2)))

'((41.98473282442748) (38.26086956521739) (40.08620689655173))

procedure

(transform-term? v) → boolean?

  v : any/c
Returns #t if v is a transform, as transform-term and ~ make them.

Examples:
> (formula-terms (~ mpg (log hp) wt))

'(#<transform-term (log hp)> wt)

> (transform-term? (car (formula-terms (~ mpg (log hp) wt))))

#t

procedure

(make-formula response rhs ...) → formula?

  response : formula-response/c
  rhs : any/c
The formula with response and right-hand side rhss, which is what ~ expands to. The list of the rhss must satisfy formula-rhs/c. It builds a formula from names or terms computed at run time; a transform in it is a transform-term.

Examples:
> (define chosen '("wt" "hp" "qsec"))
> (apply make-formula "mpg" chosen)

(~ "mpg" "wt" "hp" "qsec")

> (make-formula 'mpg `(^ (+ ,@chosen) 2))

(~ mpg (^ (+ "wt" "hp" "qsec") 2))

> (make-formula 'mpg 'wt '* 'hp)

(~ mpg wt * hp)

> (equal? (make-formula 'mpg 'wt '* 'hp) (mpg . ~ . wt * hp))

#t

> (make-formula 'mpg 'wt (list 'factor "cyl"))

(~ mpg wt (factor "cyl"))

procedure

(formula? v) → boolean?

  v : any/c
Returns #t if v is a formula. Two formulas are equal? when their responses and right-hand sides are, as written, so (~ mpg (* wt hp)) and (~ mpg wt * hp) are different formulas with the same terms, and two transforms are equal when they are written the same. A formula prints as the ~ form that makes it.

Examples:
> (formula? (~ mpg all))

#t

> (formula? '(~ mpg all))

#f

> (equal? (~ mpg wt * hp) (mpg . ~ . wt * hp))

#t

procedure

(formula-response f) → formula-response/c

  f : formula?

procedure

(formula-terms f) → formula-rhs/c

  f : formula?
The response and the right-hand side of f, as written: its terms, and the infix operators between them.

Examples:
> (formula-response (~ (surv time status) age))

'(surv time status)

> (formula-terms (mpg . ~ . 1 + wt * hp))

'(1 + wt * hp)

> (formula-terms (~ mpg wt (- all cyl)))

'(wt (- all cyl))

procedure

(formula-predictor-names f table) → (listof string?)

  f : formula?
  table : table?
The names of the columns of f’s design matrix on table, which are the names of the model’s coefficients, in their order, as R’s colnames(model.matrix(f, table)) without (Intercept). It is empty for a formula without predictors. It reads the table’s values, which decide a factor’s levels, and evaluates the transforms, as R’s model.matrix does.

Examples:
> (formula-predictor-names (~ mpg wt hp (: wt hp qsec)) mtcars)

'("wt" "hp" "wt:hp:qsec")

> (formula-predictor-names (mpg . ~ . wt * hp * qsec - wt : hp : qsec) mtcars)

'("wt" "hp" "qsec" "wt:hp" "wt:qsec" "hp:qsec")

> (formula-predictor-names (~ mpg (: all wt)) mtcars)

'("wt"

  "cyl:wt"

  "disp:wt"

  "hp:wt"

  "drat:wt"

  "wt:qsec"

  "wt:vs"

  "wt:am"

  "wt:gear"

  "wt:carb")

> (formula-predictor-names (mpg . ~ . (factor cyl) * (factor am)) mtcars)

'("(factor cyl)6"

  "(factor cyl)8"

  "(factor am)1"

  "(factor cyl)6:(factor am)1"

  "(factor cyl)8:(factor am)1")

> (formula-predictor-names (~ mpg 1) mtcars)

'()

procedure

(formula-design-matrix f table) → design-matrix?

  f : formula?
  table : table?
The design matrix of f on table, with one named column per predictor, which is what the formula procedures fit: R’s model.matrix(f, table) without its (Intercept) column. The formula must have at least one predictor. The name is not formula-model-matrix, which would read as an accessor of a formula-model.

Examples:
> (define X (formula-design-matrix (~ mpg (* wt hp)) mtcars))
> X

#<design-matrix 32x3>

> (design-matrix-column-names X)

'("wt" "hp" "wt:hp")

> (design-matrix->rows (design-matrix-select-rows X '(0 1 2)))

'((2.62 110.0 288.2) (2.875 110.0 316.25) (2.32 93.0 215.76))

> (define L (formula-design-matrix (~ mpg hp (log hp) (I (expt hp 2))) mtcars))
> (design-matrix->rows (design-matrix-select-rows L '(0 1 2)))

'((110.0 4.700480365792417 12100.0)

  (110.0 4.700480365792417 12100.0)

  (93.0 4.532599493153256 8649.0))

> (define F (formula-design-matrix (~ mpg wt (factor cyl)) mtcars))
> (design-matrix-column-names F)

'("wt" "(factor cyl)6" "(factor cyl)8")

> (design-matrix->rows (design-matrix-select-rows F '(0 1 2)))

'((2.62 1.0 0.0) (2.875 1.0 0.0) (2.32 0.0 0.0))

struct

(struct formula-model (formula predictor-names fit)
    #:transparent)
  formula : formula?
  predictor-names : (listof string?)
  fit : glmnet-model?
A model fitted from a formula. fit is the result of the family’s procedure: a single fit, a glmnet-path or a glmnet-cv. predictor-names names its predictors, the columns of the design matrix, in the order of its coefficients. Only formula-fit, formula-path and formula-cv make a formula model, since only they know that its names are those of the columns its fit was fitted to; the constructor is not exported. The model also keeps the terms it was fitted with, expanded, with its factors’ levels (see formula-model-levels), and for a binomial or multinomial response of strings, symbols or booleans, its classes; these are internal.

A formula model implements gen:glmnet-model through fit: predict, coef and deviance-ratio give what they give for fit, except that coef keys the coefficients by name and predict takes a table, from whose columns it builds the design matrix of the fitted terms. It does not expand the formula again, so all stands for the columns it stood for in the fit, and the new table’s columns can come in any order. It evaluates the transforms again, on the new table’s rows: each name of a transform that was a column in the fit must be a column of the new table, and one that was a Racket binding is read again. It codes the factors with the fit’s levels, whichever of them the new rows have, and a level that the fit did not have is an error that names the factor and the new levels. For a response with classes, coef keys a multinomial model’s coefficients by class, and predict with #:type 'class returns classes, as R’s glmnet does for a factor response. glmnet-model-predictor-names returns predictor-names, and glmnet-model-response-names the formula’s response columns. A formula model prints as fit does, with the formula after the family.

Examples:
> (define m (formula-fit (mpg . ~ . wt * hp) mtcars #:lambda 0.1))
> m

#<glmnet:gaussian (~ mpg wt * hp) λ=0.1 dev=0.8321 nz=3/3>

> (formula-model-predictor-names m)

'("wt" "hp" "wt:hp")

> (formula-model-fit m)

#<glmnet:gaussian λ=0.1 dev=0.8321 nz=3/3>

> (predict m (list (cons "hp" '(100 200)) (cons "wt" '(2.5 3.5))))

'(24.262928395522646 17.32517427651279)

> (define logged (formula-fit (mpg . ~ . (log hp) + wt) mtcars #:lambda 0.1))
> (predict logged (list (cons "hp" '(100 200)) (cons "wt" '(2.5 3.5))))

'(24.008240583293016 16.76430275840009)

> (define by-cylinders (formula-fit (mpg . ~ . wt + (factor cyl)) mtcars #:lambda 0.1))
> (predict by-cylinders (list (cons "cyl" '(8 4)) (cons "wt" '(3.5 2.5))))

'(16.846901087575166 25.69733799740451)

procedure

(formula-model-levels m)

 → (listof (cons/c string? (listof string?)))
  m : formula-model?
The levels of m’s factors, as R’s xlevels: for each variable of its terms that is a factor, in the order the variables first appear in the formula, a list of its name and the labels of its levels, the baseline first. A response column in an interaction is left out, as in R. A boolean variable has the levels "FALSE" and "TRUE", which R’s xlevels leaves out. predict codes new data with these levels.

Examples:
> (formula-model-levels
   (formula-fit (mpg . ~ . wt + (factor cyl)) mtcars #:lambda 0.1))

'(("(factor cyl)" "4" "6" "8"))

> (formula-model-levels
   (formula-fit (Sepal.Length . ~ . Species * Petal.Width + (> Sepal.Width 3))
                iris #:lambda 0.01))

'(("Species" "setosa" "versicolor" "virginica")

  ("(> Sepal.Width 3)" "FALSE" "TRUE"))

> (formula-model-levels
   (formula-fit (mpg . ~ . wt + hp) mtcars #:lambda 0.1))

'()

procedure

(formula-fit f    
  table    
  #:lambda lambda    
  [#:family family    
  #:alpha alpha    
  #:standardize? standardize?    
  #:intercept? intercept?    
  #:thresh thresh    
  #:max-iters max-iters]) → formula-model?
  f : formula?
  table : table?
  lambda : (>=/c 0)
  family : (or/c 'gaussian 'binomial 'multinomial 'poisson 'cox 'mgaussian)
   = 'gaussian
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = the formula’s
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits f to table at a single λ, with the fit procedure of family: elnet-fit, logistic-fit, multinomial-fit, poisson-fit, cox-fit or mgaussian-fit, on the design matrix of f. The keywords are passed on to it. intercept? defaults to the formula’s intercept and must agree with a formula’s 1, 0 or - 1; the Cox family has no intercept, so it does not apply to it.

The response of f must suit family, which the contract on f checks: (surv time status) for the Cox family, one or more columns for the multi-response family, and one column otherwise. Its values must be ones the family models: 0 or 1 for the binomial family, class labels 0, 1, …, K−1 with K ≥ 2 and every class present for the multinomial family, non-negative for the Poisson family, and for the Cox family positive times and 0/1 statuses. An error names the column, and either the row of a value the family does not model or the missing class label. A binomial or multinomial response may instead hold strings, symbols or booleans, whose levels, sorted as a factor’s are, are its classes, as R’s glmnet makes a factor response’s: two for the binomial family, whose second is the class the model gives the probability of, and at least two for the multinomial family. An error that the family’s procedure raises names formula-fit.

Examples:
> (define fit (formula-fit (mpg . ~ . wt * hp) mtcars #:lambda 0.1))
> (coef fit)

'(("(Intercept)" . 37.528269700740594)

  ("wt" . -4.032185075172319)

  ("hp" . -0.03534015584099164)

  ("wt:hp" . 0.0013965478672480604))

> (coef (formula-fit (mpg . ~ . 0 + wt * hp) mtcars #:lambda 0.1))

'(("(Intercept)" . 0.0)

  ("wt" . 8.54890203027654)

  ("hp" . 0.1709322727567094)

  ("wt:hp" . -0.06445423956453895))

> (coef (formula-fit (mpg . ~ . hp + (I (expt hp 2))) mtcars #:lambda 0.1))

'(("(Intercept)" . 36.37284540325939)

  ("hp" . -0.15811445359642218)

  ("(I (expt hp 2))" . 0.00026508932547340944))

> (coef (formula-fit (mpg . ~ . wt + (factor cyl)) mtcars #:lambda 0.1))

'(("(Intercept)" . 33.87775166152442)

  ("wt" . -3.2721654656479635)

  ("(factor cyl)6" . -3.7451819901218832)

  ("(factor cyl)8" . -5.578271444181383))

> (formula-fit (am . ~ . wt + hp) mtcars #:family 'binomial #:lambda 0.05)

#<glmnet:binomial (~ am wt + hp) λ=0.05 dev=0.5891 nz=2/2>

> (map car (coef (formula-fit (~ Species all) iris #:family 'multinomial #:lambda 0.05)))

'("setosa" "versicolor" "virginica")

> (formula-fit (~ mpg 1) mtcars #:lambda 0.1)

formula-fit: the formula has no predictors, and glmnet needs

at least one

  formula: (~ mpg 1)

procedure

(formula-path f 
  table 
  [#:family family 
  #:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → formula-model?
  f : formula?
  table : table?
  family : (or/c 'gaussian 'binomial 'multinomial 'poisson 'cox 'mgaussian)
   = 'gaussian
  lambda : (or/c #f (and/c (listof (>=/c 0)) pair?)) = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = the formula’s
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Fits the regularization path of f on table, with the path fitter of family, such as elnet-path, to which the keywords are passed. The response and the intercept are as for formula-fit, and an error that the path fitter raises names formula-path.

Examples:
> (define path
    (formula-path (~ mpg (^ (+ wt hp qsec) 2)) mtcars #:lambda '(1.0 0.5 0.1)))
> path

#<glmnet-path:gaussian (~ mpg (^ (+ wt hp qsec) 2))

  Df  %Dev Lambda

1  2 79.13    1.0

2  3 82.40    0.5

3  5 84.72    0.1>

> (coef path #:lambda 0.1)

'(("(Intercept)" . 26.53010769463421)

  ("wt" . -3.8107083219778004)

  ("hp" . 0.0)

  ("qsec" . 0.616278426076173)

  ("wt:hp" . 0.0023632916212049395)

  ("wt:qsec" . -0.023873512814479348)

  ("hp:qsec" . -0.001985660316630189))

procedure

(formula-cv f 
  table 
  [#:family family 
  #:type-measure type-measure 
  #:nfolds nfolds 
  #:fold-ids fold-ids 
  #:grouped? grouped? 
  #:lambda lambda 
  #:nlambda nlambda 
  #:lambda-min-ratio lambda-min-ratio 
  #:alpha alpha 
  #:standardize? standardize? 
  #:intercept? intercept? 
  #:thresh thresh 
  #:max-iters max-iters]) 
 → formula-model?
  f : formula?
  table : table?
  family : (or/c 'gaussian 'binomial 'multinomial 'poisson 'cox 'mgaussian)
   = 'gaussian
  type-measure : (or/c #f 'mse 'deviance 'mae 'class 'auc 'C)
   = #f
  nfolds : (and/c exact-integer? (>=/c 3)) = 10
  fold-ids : fold-ids/c = #f
  grouped? : boolean? = #t
  lambda : (or/c #f (and/c (listof (>=/c 0)) (property/c length (>=/c 2))))
   = #f
  nlambda : exact-positive-integer? = 100
  lambda-min-ratio : (or/c #f (and/c real? (>=/c 0) (</c 1)))
   = #f
  alpha : (real-in 0 1) = 1.0
  standardize? : boolean? = #t
  intercept? : boolean? = the formula’s
  thresh : (>/c 0) = 1e-7
  max-iters : exact-positive-integer? = 100000
Cross-validates the path of f on table with the cross-validation procedure of family, such as elnet-cv, to which the keywords are passed. type-measure must be one of the family’s measures (see Cross-validation), which the contract on it checks; #f, the default, is the family’s default. fold-ids needs one entry per row of table. The response and the intercept are as for formula-fit, and an error that the cross-validation procedure raises names formula-cv.

Examples:
> (define folds (for/list ([i (in-range 32)]) (modulo i 4)))
> (define cv-model (formula-cv (mpg . ~ . wt * hp) mtcars #:fold-ids folds))
> cv-model

#<glmnet-cv:gaussian (~ mpg wt * hp) Mean-Squared Error

         Lambda  Index  Measure      SE  Nonzero

  min  0.002503     82     6.08  0.6064        3

  1se   0.04477     51     6.61  0.6951        3>

> (coef cv-model)

'(("(Intercept)" . 44.18599782499345)

  ("wt" . -6.301381407673056)

  ("hp" . -0.08130232129662496)

  ("wt:hp" . 0.015743383605847892))

2.17 Generic model interface🔗ℹ

Every result type implements one generic interface, gen:glmnet-model: the six single-λ results (elnet-result, logistic-result, multinomial-result, cox-result, poisson-result and mgaussian-result), glmnet-path, glmnet-cv and formula-model. predict, coef and deviance-ratio follow R’s predict, coef and dev.ratio for glmnet and cv.glmnet fits. See Predictions and coefficients.

The interface reads every model as a regularization path: a single-λ fit is a path with one λ. Where #:lambda names a λ that is not on the path, predict and coef follow R’s rule (its exact = FALSE, the default):

  • Between two fitted values λl > s > λr, the intercepts and coefficients are interpolated linearly in λ: β(s) = w β(λl) + (1 − w) β(λr), with w = (s − λr) / (λl − λr).

  • Above the largest fitted λ, or below the smallest, s is clamped to that end of the path.

  • A model with one λ, such as a single fit, gives that fit for every s.

A cross-validated model also names two λ values, 'lambda-min and 'lambda-1se, which #:lambda accepts in place of a number, as R’s s accepts "lambda.min" and "lambda.1se".

A model can also name its predictors, as a formula-model does. Then coef keys its coefficients by name, as R’s coef names its rows, and predict takes a table and reads the predictors from it by name, or, for a formula model, builds them from the table’s columns, as its formula’s terms say.

predict, coef and glmnet-model-default-lambda need a model with at least one fitted λ. A glmnet-path built by hand can have none, and they reject such a model with a contract error that blames the caller.

The examples in this section use a single fit and a path of the Gaussian family:

> (define X '((1.0 2.0 1.0) (2.0 1.0 4.0) (3.0 4.0 9.0)
              (4.0 3.0 16.0) (5.0 6.0 25.0) (6.0 5.0 36.0)))
> (define y '(1.0 4.0 3.0 6.0 5.0 8.0))
> (define fit (lasso X y #:lambda 0.05))
> (define path (elnet-path X y #:lambda '(1.0 0.1 0.01)))

A generic interface for fitted models. It has six methods:

The defaults suit a single fit. glmnet-path and glmnet-cv implement the ones that differ for them, and formula-model implements all six. A new type, such as a path together with a chosen λ, implements the interface through the #:methods option of struct, and then works with predict and coef:

Examples:
> (require racket/generic)
> (struct chosen (path index)
    #:methods gen:glmnet-model
    [(define (glmnet-model->path m) (chosen-path m))
     (define (glmnet-model-default-lambda m)
       (vector-ref (glmnet-path-lambda (chosen-path m))
                   (chosen-index m)))
     (define (deviance-ratio m)
       (vector-ref (glmnet-path-dev-ratio (chosen-path m))
                   (chosen-index m)))])
> (define best (chosen path 1))
> (coef best)

'#(1.0000000000001843 1.6584349744677054 -0.6584349744677581 0.0)

> (deviance-ratio best)

0.9762711864406388

procedure

(glmnet-model? v) → boolean?

  v : any/c
Returns #t if v implements gen:glmnet-model: every result type, glmnet-path, glmnet-cv and formula-model do.

Examples:
> (glmnet-model? fit)

#t

> (glmnet-model? path)

#t

> (glmnet-model? X)

#f

procedure

(glmnet-model->path model) → glmnet-path?

  model : glmnet-model?
model as a glmnet-path. A single fit becomes a path with one λ, and a path is returned as it is.

Examples:
> (glmnet-model->path fit)

#<glmnet-path:gaussian

  Df  %Dev Lambda

1  2 99.41   0.05>

> (glmnet-path-coefficients (glmnet-model->path fit))

'#(#(1.8292174872340037 -0.8292174872340038 0.0))

procedure

(glmnet-model-default-lambda model)

 → (or/c (>=/c 0) (and/c (listof (>=/c 0)) pair?))
  model : glmnet-model?
The #:lambda that predict and coef use when none is given: a single fit’s λ, or the list of a path’s fitted λ values, which is R’s default s = NULL. For a glmnet-cv it is glmnet-cv-lambda-1se, R’s default for cv.glmnet.

Examples:
> (glmnet-model-default-lambda fit)

0.05

> (glmnet-model-default-lambda path)

'(1.0 0.1 0.01)

procedure

(glmnet-model-named-lambda model name) → (or/c #f (>=/c 0))

  model : glmnet-model?
  name : (or/c 'lambda-min 'lambda-1se)
The λ that name stands for in model, or #f if the model does not name one. A glmnet-cv names its glmnet-cv-lambda-min and glmnet-cv-lambda-1se; single fits and paths name none, so predict and coef reject a name for them.

Examples:
> (glmnet-model-named-lambda path 'lambda-min)

#f

> (coef path #:lambda 'lambda-min)

coef: only a cross-validated model has a named lambda

  lambda: 'lambda-min

procedure

(glmnet-model-predictor-names model)

 → (or/c #f (listof string?))
  model : glmnet-model?
The names of model’s predictors, in the order of its coefficients, or #f if it does not name them. A formula-model names them; the results of the family procedures do not. When a model names its predictors, coef keys its coefficients by these names and predict reads the columns with these names from a table, except that a formula model builds its predictors, such as the interaction "a:b" or the transform "(log a)", from the table’s columns. The names must be distinct, one per predictor of the model’s path; coef and predict raise an error for a model whose names are not.

Examples:
> (define named-model
    (formula-fit (~ y all)
                 (list (cons "y" y) (cons "a" (map car X))
                       (cons "b" (map cadr X)) (cons "c" (map caddr X)))
                 #:lambda 0.05))
> (glmnet-model-predictor-names named-model)

'("a" "b" "c")

> (glmnet-model-predictor-names fit)

#f

procedure

(glmnet-model-response-names model)

 → (or/c #f (listof string?))
  model : glmnet-model?
The names of model’s response columns, or #f if it does not name them. For a formula-model, they are the columns of its formula’s response: for the Cox family the time and status columns. For the multi-response family, coef keys the coefficients of each response by its name, and raises an error for a model that does not name each response once.

Examples:

procedure

(deviance-ratio model) → (or/c real? (vectorof real?))

  model : glmnet-model?
The fraction of null deviance explained, which is R² for the Gaussian families: a real for a single fit, and for a path a vector with one entry per fitted λ, R’s dev.ratio. It is not interpolated.

Examples:
> (deviance-ratio fit)

0.9940677966101694

> (deviance-ratio path)

'#(0.6106537530266343 0.9762711864406388 0.9997591646825783)

procedure

(predict model X [#:type type #:lambda lambda]) → list?

  model : glmnet-model?
  X : (or/c design-matrix/c table?)
  type : (or/c 'link 'response 'class) = 'link
  lambda : 
(or/c (>=/c 0) (and/c (listof (>=/c 0)) pair?)
      'lambda-min 'lambda-1se)
   = (glmnet-model-default-lambda model)
Predictions for each row of X, as R’s predict(fit, newx, s, type). X needs one column per coefficient, in the order of the coefficients. For a model that names its predictors (see glmnet-model-predictor-names), X is instead a table with a column of each of those names, in any order; its other columns are ignored, and a missing one is an error that names it. For a formula-model, the table needs the columns that the formula’s terms read, from which predict builds the design matrix as formula-design-matrix does, transforms included; an error names the missing ones. A model that does not name its predictors reads X by position, and so does not take an association list or a hash. type chooses what is predicted:

Family

  

'link

  

'response

  

'class

Gaussian, multi-response

  

η = β₀ + xβ

  

η

  

---

Binomial

  

log-odds η

  

P(y = 1)

  

1 if η > 0, else 0

Multinomial

  

ηk, one per class

  

softmax of the ηk

  

the class with the largest ηk

Cox

  

xβ

  

exp(xβ)

  

---

Poisson

  

log μ = η

  

μ = exp(η)

  

---

'class is an error for the families without classes. A class is its label, 0, 1, …, except for a formula-model whose response holds strings, symbols or booleans, whose classes are the labels of its levels, such as "setosa" (see formula-fit). For one λ, the result has one entry per row of X: a real, a class, or, for the multinomial and multi-response families, a list with one entry per class or response. When lambda is a list, the result is a list of those, one per element of lambda, in the same order. 'lambda-min and 'lambda-1se stand for the λ values a glmnet-cv chose (see glmnet-model-named-lambda).

Examples:
> (predict fit '((7.0 6.0 49.0) (0.0 0.0 0.0)))

'(8.829217487234002 0.9999999999999996)

> (predict path '((7.0 6.0 49.0)) #:lambda 0.5)

'(7.72162380661891)

> (predict path '((7.0 6.0 49.0)) #:lambda '(2.0 0.5 0.01))

'((6.550609846808079) (7.72162380661891) (8.972181135456438))

> (define clf (logistic-fit X '(0 0 0 1 1 1) #:lambda 0.05))
> (predict clf '((2.0 3.0 4.0) (5.0 4.0 25.0)))

'(-2.7629680455517835 2.7629680455517853)

> (predict clf '((2.0 3.0 4.0) (5.0 4.0 25.0)) #:type 'response)

'(0.05935842823867659 0.9406415717613236)

> (predict clf '((2.0 3.0 4.0) (5.0 4.0 25.0)) #:type 'class)

'(0 1)

> (predict fit X #:type 'class)

predict: type 'class is only for the binomial and

multinomial families

  family: 'gaussian

> (define new-table
    (list (cons "c" '(49.0)) (cons "b" '(6.0)) (cons "a" '(7.0))))
> (predict named-model new-table)

'(8.829217487234002)

> (predict named-model (cdr new-table))

predict: the table has no column with this name

  column: "c"

  columns of the table: '("b" "a")

procedure

(coef model [#:lambda lambda]) → (or/c vector? list?)

  model : glmnet-model?
  lambda : 
(or/c (>=/c 0) (and/c (listof (>=/c 0)) pair?)
      'lambda-min 'lambda-1se)
   = (glmnet-model-default-lambda model)
The intercept and coefficients at lambda, as R’s coef(fit, s): a vector holding the intercept, then one coefficient per predictor. Cox models have no intercept, so their vector holds only the coefficients. For the multinomial and multi-response families the result is a vector of such vectors, one per class or response. When lambda is a list, the result is a list with one entry per element of lambda. As for predict, lambda can name a λ that a glmnet-cv chose.

For a model that names its predictors, each vector is an association list instead, keyed as R’s coef names its rows: "(Intercept)" (not for Cox), then the predictor names. For the multinomial family, the association lists are in turn keyed by class, as predict labels the classes, and for the multi-response family by response name (see glmnet-model-response-names; "y1", "y2", … when the model names no responses), as R names the elements of its lists.

Examples:
> (coef fit)

'#(0.9999999999999996 1.8292174872340037 -0.8292174872340038 0.0)

> (coef path #:lambda 0.1)

'#(1.0000000000001843 1.6584349744677054 -0.6584349744677581 0.0)

> (coef path #:lambda 0.4)

'#(1.483130051064096 1.3009194921982872 -0.43895664964517206 0.0)

> (coef path #:lambda 5.0)

'#(2.449390153191919 0.5858885276594514 0.0 0.0)

> (coef named-model)

'(("(Intercept)" . 0.9999999999999996)

  ("a" . 1.8292174872340037)

  ("b" . -0.8292174872340038)

  ("c" . 0.0))

> (coef (glmnet-path 'gaussian (vector) (vector) (vector)
                     (vector) (vector) 0))

coef: contract violation;

 the model has at least one fitted λ

      model: #<glmnet-path:gaussian

 Df %Dev Lambda>

  in: (->i

       ((model glmnet-model?))

       (#:lambda

        (s

         (or/c

          (or/c

           (>=/c 0)

           (non-empty-listof (>=/c 0)))

          (or/c 'lambda-min 'lambda-1se))))

       #:pre/name

       (model)

       "the model has at least one fitted λ"

       (fitted? model)

       (result (or/c vector? (listof any/c))))

  contract from: <pkgs>/glmnet/core/model.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/glmnet/core/model.rkt:75:3

2.17.1 Printing🔗ℹ

A single fit prints on one line with its family, its λ (to four significant digits), its deviance ratio (to four decimal places) and the number of nonzero coefficients out of the number of predictors, counting a predictor once when it is nonzero for any class or response.

A path prints R’s print.glmnet table line for line, without the Call: line R prints above it. Each fitted λ has a row, numbered from 1, with Df, the deviance ratio as a percentage rounded to two places (%Dev), and λ to four significant digits (Lambda). As in R, %Dev and Lambda are then rounded to about five significant digits of the column’s largest value, so a λ far below the first can show as 0, and each column is written with one number of decimals throughout, or in scientific notation when that is narrower.

A glmnet-cv prints like R’s print.cv.glmnet: the name of its measure, then a row for each of glmnet-cv-lambda-min and glmnet-cv-lambda-1se with that λ, its index, the cross-validated error, its standard error and the number of nonzero coefficients. It differs from R’s in two ways: the index counts from 0, where R’s counts from 1, and each real is rounded to four significant digits on its own, where R formats each column as a whole and can show a digit more. A formula-model prints as the result it holds, with its formula after the family.

Printing does not change equal?, which compares results field by field.

Examples:
> (list fit clf)

'(#<glmnet:gaussian λ=0.05 dev=0.9941 nz=2/3>

  #<glmnet:binomial λ=0.05 dev=0.8046 nz=1/3>)

> (define Xm '((1.0 1.0) (2.0 1.0) (5.0 1.0) (6.0 1.0) (3.0 5.0) (4.0 6.0)))
> (multinomial-fit Xm '(0 0 1 1 2 2) #:lambda 0.05)

#<glmnet:multinomial λ=0.05 dev=0.9150 nz=2/2>

> (multinomial-path Xm '(0 0 1 1 2 2) #:nlambda 4)

#<glmnet-path:multinomial

  Df  %Dev  Lambda

1  0  0.00 0.46710

2  2 96.30 0.02168

3  2 99.82 0.00101

4  2 99.99 0.00005>

> cv

#<glmnet-cv:gaussian Mean-Squared Error

        Lambda  Index  Measure       SE  Nonzero

  min  0.05424     37   0.3984  0.05967        6

  1se   0.1656     25   0.4468  0.06523        3>

> named-model

#<glmnet:gaussian (~ y all) λ=0.05 dev=0.9941 nz=2/3>

> (equal? fit (lasso X y #:lambda 0.05))

#t

2.18 Native library🔗ℹ

These call straight into libglmnetcompat. Loading glmnet checks both and raises exn:fail if either is wrong. See The native library.

The width in bytes of the Fortran default real in the loaded library. It is 8 for a correctly built library, which the numeric API requires.

Example:

The version of the C entry points the library exports. It changes whenever an entry point is added or changed incompatibly.

Example:

2.19 Plots🔗ℹ

 (require glmnet/plot) package: glmnet

glmnet/plot draws a glmnet-path and a glmnet-cv, or a formula-model that holds one, as R’s plot draws a glmnet and a cv.glmnet fit. The guide’s Plots chapter shows every plot and how to read it. glmnet does not re-export these bindings, so that (require glmnet) does not load the plot library. Each plot is a pict?, drawn with plot/no-gui, whose parameters apply to it, and each plot procedure has a companion that returns the plot’s renderers, without the axes, to combine with other plot-lib renderers.

The examples in this section plot the lasso path of the fixture of Gaussian models, a multinomial path of three separable classes, the cross-validated fit cv of Cross-validation, and the formula model cv-model of formula-cv:

> (require glmnet/plot)
> (define X '((1.0 2.0 1.0) (2.0 1.0 4.0) (3.0 4.0 9.0)
              (4.0 3.0 16.0) (5.0 6.0 25.0) (6.0 5.0 36.0)))
> (define y '(1.0 4.0 3.0 6.0 5.0 8.0))
> (define path (elnet-path X y))
> (define X3 '((1.0 1.0) (2.0 1.0) (1.0 2.0) (2.0 2.0)
               (5.0 1.0) (6.0 1.0) (5.0 2.0) (6.0 2.0)
               (3.0 5.0) (4.0 5.0) (3.0 6.0) (4.0 6.0)))
> (define mpath (multinomial-path X3 '(0 0 0 0 1 1 1 1 2 2 2 2)))

procedure

(plot-coefficient-path model    
  [#:xvar xvar    
  #:sign-lambda sign-lambda    
  #:label label    
  #:type-coef type-coef    
  #:width width    
  #:height height    
  #:title title    
  #:out-file out-file]) → pict?
  model : (or/c glmnet-path? glmnet-cv? formula-model?)
  xvar : (or/c 'lambda 'norm 'dev) = 'lambda
  sign-lambda : (or/c -1 1) = -1
  label : (or/c boolean? design-matrix? (listof (or/c string? symbol?)))
   = #f
  type-coef : (or/c 'coef '2norm) = 'coef
  width : exact-positive-integer? = (plot-width)
  height : exact-positive-integer? = (plot-height)
  title : (or/c #f string?) = (plot-title)
  out-file : (or/c #f (and/c path-string? has-image-extension?))
   = #f
Plots the coefficients of model’s path against xvar, as R’s plot.glmnet does (see Coefficient paths). For a glmnet-cv, it plots the path fitted to all the data. For a formula-model, it plots the model’s fit, which must be a path or a glmnet-cv.

  • xvar is 'lambda for sign-lambda times log λ, 'norm for the L1 norm of the coefficients, or 'dev for the fraction of deviance explained.

  • label labels each curve at the end of the path: #f for no labels, #t for the predictor’s name if the model names its predictors, as a formula-model does, and otherwise its position counting from 1, or the names of the predictors, as a list or as the column names of a design matrix. A design matrix without column names gives positions.

  • type-coef applies to the multinomial and multi-response families: 'coef stacks one plot per class or response, '2norm draws one plot of the 2-norms across them.

  • width and height are the size of each plot in pixels, and title is its title.

  • out-file, when given, names a file to which the plot is also written, in the format its extension names: "png", "pdf", "svg" or "eps", in upper or lower case. has-image-extension? in the contract holds for exactly those paths, so any other extension, or none, raises exn:fail:contract before anything is drawn.

An error is raised if every coefficient is zero at every λ, as there is then nothing to plot. A λ of 0 has no position on a log λ axis and is left out: the curves, and their labels, end at the smallest positive λ. If every λ of the path is 0, an error is raised for a log λ axis, as R’s plot stops too; the path can still be plotted against the L1 norm or the deviance ratio.

Examples:
> (plot-coefficient-path path #:xvar 'norm #:label '("x1" "x2" "x3")
                         #:width 400 #:height 300 #:title "Lasso path")

image

> (plot-coefficient-path (elnet-path X y #:lambda '(10.0 5.0)))

plot-coefficient-path: every coefficient is zero at every λ,

so there is nothing to plot

  λ values: 2

> (plot-coefficient-path (elnet-path X y #:lambda '(0.0)))

plot-coefficient-path: every λ of the path is 0, which has

no place on a log λ axis

  λ values: '(0.0)

  xvar: 'lambda

procedure

(coefficient-path-renderers model 
  [#:xvar xvar 
  #:sign-lambda sign-lambda 
  #:label label 
  #:type-coef type-coef 
  #:response response]) 
 → (listof renderer2d?)
  model : (or/c glmnet-path? glmnet-cv? formula-model?)
  xvar : (or/c 'lambda 'norm 'dev) = 'lambda
  sign-lambda : (or/c -1 1) = -1
  label : (or/c boolean? design-matrix? (listof (or/c string? symbol?)))
   = #f
  type-coef : (or/c 'coef '2norm) = 'coef
  response : exact-nonnegative-integer? = 0
The renderers of one plot of plot-coefficient-path: a line for each predictor that is nonzero at some λ, and a label for each when label asks for them. For the multinomial and multi-response families with type-coef 'coef, response chooses the class or response, counting from 0; for the others it must be 0. The list is empty when every coefficient is zero.

The axes are not included. Labels start at the end of the path, where plot-lib cuts them off unless the x axis is widened.

Examples:
> (length (coefficient-path-renderers path))

3

> (length (coefficient-path-renderers path #:label #t))

6

> (length (coefficient-path-renderers mpath #:response 2))

1

procedure

(plot-cv cv    
  [#:sign-lambda sign-lambda    
  #:width width    
  #:height height    
  #:title title    
  #:out-file out-file]) → pict?
  cv : (or/c glmnet-cv? formula-model?)
  sign-lambda : (or/c -1 1) = -1
  width : exact-positive-integer? = (plot-width)
  height : exact-positive-integer? = (plot-height)
  title : (or/c #f string?) = (plot-title)
  out-file : (or/c #f (and/c path-string? has-image-extension?))
   = #f
Plots the cross-validation curve of cv against sign-lambda times log λ, as R’s plot.cv.glmnet does (see Cross-validation curves), with the number of nonzero coefficients along the top. A formula-model must hold a glmnet-cv, from formula-cv. width, height, title and out-file are as for plot-coefficient-path. As there, a λ of 0 is left out, and an error is raised if every λ is 0.

Example:
> (plot-cv cv #:sign-lambda 1 #:width 400 #:height 300)

image

procedure

(cv-renderers cv [#:sign-lambda sign-lambda])

 → (listof renderer2d?)
  cv : (or/c glmnet-cv? formula-model?)
  sign-lambda : (or/c -1 1) = -1
The renderers of plot-cv: the error bars, the points, and a vertical line at each of glmnet-cv-lambda-min and glmnet-cv-lambda-1se. The axes are not included.

Examples:
> (length (cv-renderers cv))

4

> (length (cv-renderers cv-model))

4