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 %*%
- 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 withas.matrix(); see Data Frames in R.) - Check the dimensions.
dim(A)returns2 3anddim(B)returns3 2. The product exists only when the columns of the left matrix equal the rows of the right one (3 = 3 here). - Multiply.
C <- A %*% B. The result has the rows ofAand the columns ofB, sodim(C)is2 2. - Inspect the result. Printing
Cgives 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().
Related operators that are easy to confuse with %*%
A * B: element-by-element product of same-sized matrices, not the matrix product (An Introduction to R, 5.7).crossprod(x, y)andtcrossprod(x, y): documented as formally equivalent to, but faster than,t(x) %*% yandx %*% t(y). Ifyis omitted it defaults tox, socrossprod(X)givest(X) %*% X. The manual describes these as typically preferable for cross products.%o%andouter(a, b, "*"): the outer product of two vectors.solve(A, b): to solve a linear system, use this rather thansolve(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 byoptions("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)
- R Core Team, Matrix Multiplication (
matmult), R base documentation, read 2026-11-08. - R Core Team, Matrix Crossproduct (
crossprod), R base documentation, read 2026-11-08. Records thatcrossprodbecame generic in R 4.4.0. - R Core Team, An Introduction to R, section 5.7 “Matrix facilities”, read 2026-11-08.







