Skip to main content
v2026.11,986 entries · CC-BY 4.0

Matrix Multiplication in R: %*%, crossprod and Common Errors

Multiply matrices in R with %*%: a worked 2 x 3 by 3 x 2 example, vector promotion, crossprod(), and the non-conformable arguments error.

Written and maintained by CASRAI Editorial Board

Published

On this page

Short answer: in R, matrix multiplication is the %*% operator. A * B multiplies element by element, which is a different operation. If the multiplication fails with “non-conformable arguments”, the number of columns of the left matrix does not equal the number of rows of the right one. This guide is for researchers and analysts who need to multiply matrices in R, read the result, and avoid the handful of mistakes that silently change it.

What matrix multiplication in R is used for in research

Matrix products appear wherever an analysis is written in linear algebra: ordinary least squares (t(X) %*% X), covariance and correlation matrices, principal components, transition matrices in Markov models, and weighting a design matrix by a coefficient vector. R’s own manual states the rule plainly: “The operator %*% is used for matrix multiplication” (An Introduction to R, section 5.7, Matrix facilities). Related reading on this site: BLAS (Basic Linear Algebra Subprograms) explains the library that does the heavy lifting underneath, and Matrix Multiplication in MATLAB is the equivalent for MATLAB users.

Install or access (official source only)

Matrix multiplication is part of base R, so no package is needed. Obtain R from the Comprehensive R Archive Network (CRAN) at cran.r-project.org. Step-by-step installation, including RStudio, is in How to Install R and RStudio on Windows, Mac and Linux.

Step-by-step: multiply two matrices with %*%

  1. Build the two matrices. A <- matrix(1:6, nrow = 2) creates a 2 x 3 matrix; B <- matrix(1:6, nrow = 3) creates a 3 x 2 matrix. (Data frames must first be converted with as.matrix(); see Data Frames in R.)
  2. Check the dimensions. dim(A) returns 2 3 and dim(B) returns 3 2. The product exists only when the columns of the left matrix equal the rows of the right one (3 = 3 here).
  3. Multiply. C <- A %*% B. The result has the rows of A and the columns of B, so dim(C) is 2 2.
  4. Inspect the result. Printing C gives 22 and 49 in the first row and 28 and 64 in the second. For example, the top-left entry is 1×1 + 3×2 + 5×3 = 22.

Vectors are promoted to row or column matrices

The R documentation (?matmult) says that if one argument is a vector, it is promoted to either a row or a column matrix to make the two arguments conformable. When both arguments are vectors of the same length, the result is the inner product as a 1 x 1 matrix. The manual’s own example:

x <- 1:4
(z <- x %*% x)   # scalar ("inner") product, a 1 x 1 matrix
drop(z)           # as a plain scalar

y <- diag(x)
z <- matrix(1:12, ncol = 3, nrow = 4)
y %*% z
y %*% x
x %*% z

Here x %*% x is 30. drop() removes the length-one dimensions so you get the number 30 rather than a 1 x 1 matrix. y %*% z is a 4 x 3 matrix whose row i is row i of z multiplied by i (because diag(1:4) is diagonal), y %*% x is a 4 x 1 column (1, 4, 9, 16), and x %*% z is a 1 x 3 row (30, 70, 110). The documentation also notes that a promoted vector’s names are not turned into row or column names, unlike as.matrix().

  • A * B: element-by-element product of same-sized matrices, not the matrix product (An Introduction to R, 5.7).
  • crossprod(x, y) and tcrossprod(x, y): documented as formally equivalent to, but faster than, t(x) %*% y and x %*% t(y). If y is omitted it defaults to x, so crossprod(X) gives t(X) %*% X. The manual describes these as typically preferable for cross products.
  • %o% and outer(a, b, "*"): the outer product of two vectors.
  • solve(A, b): to solve a linear system, use this rather than solve(A) %*% b, which the R manual calls “both inefficient and potentially unstable”. See Solve in R.

Output and how to read it

The result of %*% is a numeric (double) or complex matrix. Read it as follows:

  • Check dim() first: result rows come from the left operand, result columns from the right.
  • A 1 x 1 matrix from two vectors is an inner product. Wrap it in drop() if downstream code expects a scalar.
  • A promoted vector’s names do not become row or column names of the result.
  • For reproducible reports, record dim() of every operand and your R version; numerical details such as NaN/Inf propagation, precision and speed are governed by options("matprod") as described in ?matmult. Linked BLAS libraries can also change last-digit results (see BLAS and IEEE 754), so pin the environment with renv where it matters.

Troubleshooting

Symptom Cause Fix
Error: “non-conformable arguments” Columns of the left operand do not equal rows of the right operand, for example a 2 x 3 matrix times another 2 x 3 matrix. Print dim() of both; transpose one with t() if the intended product requires it.
The result looks like element-wise multiplication * was used instead of %*%. Use %*% for the matrix product.
Error when an argument is a data frame %*% is documented for numeric or complex matrices or vectors, and a data frame is neither. Convert numeric columns with as.matrix() first; check that every column is numeric.
A 1 x 1 matrix where a number was expected Two vectors produced an inner product matrix. Use drop().
Unexpected orientation of a vector result The vector was promoted to a row or column depending on which choice makes the operands conformable. Make the vector a matrix explicitly with matrix(), cbind() or rbind() (see cbind in R).

Free alternatives

Option What it does Source
crossprod() / tcrossprod() (base R) Cross products t(x) %*% y and x %*% t(y), formally equivalent but faster. R documentation, ?crossprod
outer() / %o% (base R) Outer product of two vectors. An Introduction to R, 5.7
solve(A, b) (base R) Solves a linear system without forming an explicit inverse. An Introduction to R, 5.7

Every option above ships with base R and costs nothing. MATLAB users can compare notation in Matrix Multiplication in MATLAB.

FAQ

How do you multiply two matrices in R?

Use A %*% B. The number of columns in A must equal the number of rows in B; the result has the rows of A and the columns of B.

What is the difference between * and %*% in R?

* multiplies corresponding elements of same-sized matrices; %*% computes the matrix product.

Why does R say “non-conformable arguments”?

The inner dimensions do not match: ncol(left) is not equal to nrow(right). Check both with dim().

How do I get the transpose product t(X) %*% X?

Use crossprod(X), which the R documentation describes as formally equivalent to but faster than t(X) %*% X.

Why is x %*% x a matrix and not a number?

For two vectors of the same length it returns the inner product as a 1 x 1 matrix; use drop() to get a plain scalar.

Sources (official documentation)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Matrix Multiplication in R: %*%, crossprod and Common Errors

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →