Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Julia  Module 5 – Arrays, indexing and broadcasting

Reductions, reshaping and the dims keyword

Reduce Julia arrays along a dimension with dims, join them with hcat, vcat and stack, tell reshape from permutedims and the adjoint, and slice 3-D data.

  • Intermediate
  • 15 minutes
  • Examples run with julia 1.13.1 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Compute sums, maxima, counts and means along chosen dimensions with the dims keyword
  • Predict the shapes that reshape, permutedims, hcat, vcat, cat and stack return
  • Explain why A' is a lazy, conjugating adjoint while permutedims returns a new matrix
  • Iterate over the slices of a 3-D array with eachslice, and build new arrays from slices with mapslices

Before you start

What you’ll learn · Before you start

What you will learn

  • Compute sums, maxima, counts and means along chosen dimensions with the dims keyword
  • Predict the shapes that reshape, permutedims, hcat, vcat, cat and stack return
  • Explain why A' is a lazy, conjugating adjoint while permutedims returns a new matrix
  • Iterate over the slices of a 3-D array with eachslice, and build new arrays from slices with mapslices

Before you start

On this page

sum(M; dims = 2) of a 2×3 matrix does not give the two row sums as a vector: it gives a 2×1 matrix, which Julia prints as [6; 15;;]. The kept dimension is deliberate, because it lines the sums up with M for the next broadcast, and it is the first thing to check when code written for a vector rejects the result.

This lesson reduces along a chosen dimension with sum, maximum, count, mean and mapreduce, runs argmax, extrema, cumsum and diff along rows, joins arrays with hcat, vcat, cat and stack, tells reshape from permutedims and from the lazy adjoint A', and walks a station × day × variable array with eachslice. Every run below was recorded on Julia 1.13.1; one function needs Julia 1.12, and its run on the 1.10 LTS is shown too.

dims says which dimension to reduce, and keeps it as length 1

A reduction such as sum folds many values into one. Called on a whole matrix it adds everything, and with the keyword dims: The keyword of sum, maximum, count, mean and other reductions that names the dimensions to reduce: sum(M; dims = 1) adds down each column of M and keeps that dimension with length 1. it adds along one dimension only:

dims.jl: one matrix, reduced whole and along each dimension Julia · dims.jl
# A reduction over the whole matrix, down its columns (dims = 1) and along its rows (dims = 2).
using Statistics                     # mean: a standard library that comes with Julia
M = [1 2 3; 4 5 6]
println("sum(M)           = ", sum(M))
println("sum(M; dims = 1) = ", sum(M; dims = 1), ", size ", size(sum(M; dims = 1)))
println("sum(M; dims = 2) = ", sum(M; dims = 2), ", size ", size(sum(M; dims = 2)))
println("vec(sum(M; dims = 2))                = ", vec(sum(M; dims = 2)))
println("dropdims(sum(M; dims = 2); dims = 2) = ", dropdims(sum(M; dims = 2); dims = 2))
println("maximum(M; dims = 1) = ", maximum(M; dims = 1), ", minimum(M; dims = 2) = ", minimum(M; dims = 2))
println("mean(M; dims = 2) = ", mean(M; dims = 2), ", sum.(eachrow(M)) = ", sum.(eachrow(M)))
println("each value as a share of its row total: ", round.(M ./ sum(M; dims = 2); digits = 3))
dims.jl, run with Julia 1.13.1 Python · dims_session.py
from session import files, run

# Sums, extremes and means of a 2×3 matrix, whole and along each dimension.
files("dims.jl")
run("""
julia dims.jl
""")

Output

$ julia dims.jl
sum(M)           = 21
sum(M; dims = 1) = [5 7 9], size (1, 3)
sum(M; dims = 2) = [6; 15;;], size (2, 1)
vec(sum(M; dims = 2))                = [6, 15]
dropdims(sum(M; dims = 2); dims = 2) = [6, 15]
maximum(M; dims = 1) = [4 5 6], minimum(M; dims = 2) = [1; 4;;]
mean(M; dims = 2) = [2.0; 5.0;;], sum.(eachrow(M)) = [6, 15]
each value as a share of its row total: [0.167 0.333 0.5; 0.267 0.333 0.4]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 dims_session.py

Step through both reductions:

Interactive · step by step sum(M; dims = 1) and sum(M; dims = 2) on M = [1 2 3; 4 5 6] Step through both reductions. dims names the dimension that is added up and shrinks to length 1.
sum(M; dims = 1) and sum(M; dims = 2) on M = [1 2 3; 4 5 6], step 6 of 6. Showing dims = 1: down columns, column 1: 1 + 4 = 5, column 2: 2 + 5 = 7, column 3: 3 + 6 = 9, a 1×3 matrix: [5 7 9], dims = 2: along rows, row 1: 1 + 2 + 3 = 6, row 2: 4 + 5 + 6 = 15, a 2×1 matrix: [6; 15;;]. In focus: a 1×3 matrix: [5 7 9], a 2×1 matrix: [6; 15;;].dims = 1: down columnsrows are added upcolumn 1: 1 + 4 = 5column 2: 2 + 5 = 7column 3: 3 + 6 = 9a 1×3 matrix: [5 7 9]size (1, 3)dims = 2: along rowscolumns are added uprow 1: 1 + 2 + 3 = 6row 2: 4 + 5 + 6 = 15a 2×1 matrix: [6; 15;;]size (2, 1)

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. dims = 1. dims = 1 reduces the first dimension, the rows: each column is added from top to bottom.
  2. Three sums. Column 1 gives 1 + 4 = 5, column 2 gives 7 and column 3 gives 9.
  3. Kept as length 1. The result keeps the reduced dimension with length 1: a 1×3 matrix, [5 7 9], not a vector of 3.
  4. dims = 2. dims = 2 reduces the second dimension, the columns: each row is added from left to right.
  5. A 2×1 matrix. The result is 2×1, which Julia prints as [6; 15;;]. vec or dropdims(…; dims = 2) turns it into the vector [6, 15].
  6. Why keep it. The kept dimension lines the result up with M, so M ./ sum(M; dims = 2) divides each row by its own total: 0.167, 0.333 and 0.5 in row 1.

sum(M) gave 21. sum(M; dims = 1) added down each column and gave the 1×3 matrix [5 7 9], and sum(M; dims = 2) added along each row and gave a 2×1 matrix. The documentation of sum says it sums the elements of an array over the given dimensions, and its examples keep the reduced dimension with length 1. vec and dropdims(…; dims = 2) remove it when a plain vector is wanted: both gave [6, 15]. maximum, minimum and count take dims the same way, and so does mean from Statistics, the standard library for basic statistics: mean(M; dims = 2) gave the row means 2.0 and 5.0.

A 2×1 matrix where a vector was expected

Wrong: passing sum(M; dims = 2) to code written for a Vector, such as a method with a ::Vector argument, which rejects the 2×1 Matrix. Right: vec(sum(M; dims = 2)), or sum.(eachrow(M)), which gave the vector [6, 15] directly.

The kept dimension is what makes the next step a single broadcast. M ./ sum(M; dims = 2) divided each row by its own total, 0.167, 0.333 and 0.5 in the first row, because a 2×1 matrix stretches across the 3 columns of M.

Key idea

dims names the dimension that a reduction adds up, and that dimension stays in the result with length 1, ready to broadcast against the original.

argmax, extrema, cumsum and diff work along a dimension too

The other reductions and the running operations follow the same keyword. A vector first, then the rows of M:

along.jl: positions, extremes, running totals and differences Julia · along.jl
# Other reductions, and running operations, along a dimension.
v = [3, 9, 2, 5]
println("argmax(v) = ", argmax(v), ", findmax(v) = ", findmax(v), ", extrema(v) = ", extrema(v))
println("cumsum(v) = ", cumsum(v), ", diff(v) = ", diff(v))
M = [1 2 3; 4 5 6]
println("extrema(M; dims = 2) = ", extrema(M; dims = 2))
println("findmax(M; dims = 2) = ", findmax(M; dims = 2))
println("cumsum(M; dims = 2)  = ", cumsum(M; dims = 2))
println("diff(M; dims = 2)    = ", diff(M; dims = 2))
println("count(>(2), M; dims = 1)        = ", count(>(2), M; dims = 1))
println("mapreduce(abs2, +, M; dims = 1) = ", mapreduce(abs2, +, M; dims = 1))
try
    cumsum(M)                       # a matrix has no single order to accumulate in
catch err
    println(sprint(showerror, err))
end
along.jl, run with Julia 1.13.1 Python · along_session.py
from session import files, run

# argmax, extrema, findmax, cumsum, diff, count and mapreduce, on a vector and along the rows of a matrix.
files("along.jl")
run("""
julia along.jl
""")

Output

$ julia along.jl
argmax(v) = 2, findmax(v) = (9, 2), extrema(v) = (2, 9)
cumsum(v) = [3, 12, 14, 19], diff(v) = [6, -7, 3]
extrema(M; dims = 2) = [(1, 3); (4, 6);;]
findmax(M; dims = 2) = ([3; 6;;], CartesianIndex{2}[CartesianIndex(1, 3); CartesianIndex(2, 3);;])
cumsum(M; dims = 2)  = [1 3 6; 4 9 15]
diff(M; dims = 2)    = [1 1; 1 1]
count(>(2), M; dims = 1)        = [1 1 2]
mapreduce(abs2, +, M; dims = 1) = [17 29 45]
UndefKeywordError: keyword argument `dims` not assigned

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 along_session.py

argmax([3, 9, 2, 5]) gave 2, the position of the largest value; findmax gave the value and its position together, (9, 2), and extrema the smallest and the largest as one tuple, (2, 9). With dims = 2 each works row by row: extrema gave one tuple per row, and findmax returned the row maxima with their positions as CartesianIndex values, the positions of the previous lesson but one.

Call On v = [3, 9, 2, 5] On M with dims = 2
findmax (9, 2) [3; 6;;] at (1, 3) and (2, 3)
extrema (2, 9) [(1, 3); (4, 6);;]
cumsum [3, 12, 14, 19] [1 3 6; 4 9 15]
diff [6, -7, 3] [1 1; 1 1]

cumsum and diff are running operations rather than reductions: the running totals keep every position, and the differences between neighbours are one shorter than their input. On a matrix both need dims, because a matrix has no single order to accumulate in: cumsum(M) stopped with UndefKeywordError, as the array form in the documentation takes dims without a default. count(>(2), M; dims = 1) counted per column, and mapreduce(abs2, +, M; dims = 1) squared and added per column, [17 29 45], without an array of squares in between.

Checkpoint

Check: The dims keyword of reductions · argmax, extrema, cumsum and diff along a dimension

  1. Question 1 of 2 For a 4×5 matrix A, what is size(sum(A; dims = 1))?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: (1, 5)

    dims names the dimension that is added up; it stays in the result with length 1, so a 4×5 matrix gives 1×5.

  2. Question 2 of 2 For M = [2 7 4; 9 1 3], what does argmax(M; dims = 2) return?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A 2×1 matrix of CartesianIndex(1, 2) and CartesianIndex(2, 1)

    argmax and findmax with dims work row by row (dims = 2) or column by column (dims = 1) and return positions in M, as findmax(M; dims = 2) did in the run.

hcat, vcat, cat and stack join arrays along a dimension

Joining is the opposite of slicing, and it too is a question of which dimension:

concat_shapes.jl: two vectors, two matrices and four days, joined Julia · concat_shapes.jl
# Joining vectors and matrices: hcat, vcat, cat with dims, stack and reduce.
a = [1, 2, 3]
b = [4, 5, 6]
println("hcat(a, b) = ", hcat(a, b), ", size ", size(hcat(a, b)))
println("vcat(a, b) = ", vcat(a, b), ", size ", size(vcat(a, b)))
println("[a b] == hcat(a, b): ", [a b] == hcat(a, b), ", [a; b] == vcat(a, b): ", [a; b] == vcat(a, b))
println("size(cat(ones(2, 2), zeros(2, 2); dims = 3)) = ", size(cat(ones(2, 2), zeros(2, 2); dims = 3)))
days = [[12.0, 0.0, 7.5], [0.0, 2.5, 11.0], [30.5, 18.0, 0.0], [4.0, 9.5, 1.5]]   # one vector per day
println("stack(days) = ", stack(days), ", size ", size(stack(days)))
println("size(stack(days; dims = 1)) = ", size(stack(days; dims = 1)))
println("reduce(hcat, days) == stack(days): ", reduce(hcat, days) == stack(days))
println("size(reduce(vcat, days)) = ", size(reduce(vcat, days)))
concat_shapes.jl, run with Julia 1.13.1 Python · concat_session.py
from session import files, run

# Two vectors joined side by side and end to end, two matrices stacked in a third dimension, and four days stacked.
files("concat_shapes.jl")
run("""
julia concat_shapes.jl
""")

Output

$ julia concat_shapes.jl
hcat(a, b) = [1 4; 2 5; 3 6], size (3, 2)
vcat(a, b) = [1, 2, 3, 4, 5, 6], size (6,)
[a b] == hcat(a, b): true, [a; b] == vcat(a, b): true
size(cat(ones(2, 2), zeros(2, 2); dims = 3)) = (2, 2, 2)
stack(days) = [12.0 0.0 30.5 4.0; 0.0 2.5 18.0 9.5; 7.5 11.0 0.0 1.5], size (3, 4)
size(stack(days; dims = 1)) = (4, 3)
reduce(hcat, days) == stack(days): true
size(reduce(vcat, days)) = (12,)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 concat_session.py

hcat(a, b) put the two vectors side by side as the columns of a 3×2 matrix, and vcat(a, b) end to end as one vector of 6. [a b] and [a; b] are the same two calls, as the manual’s table of concatenation syntax shows, which is why the literals of the first lesson behave as they do. cat(…; dims = 3) joins along any dimension, so two 2×2 matrices became one 2×2×2 array.

To build a matrix from many vectors, stack puts each vector in a column of its own; its documentation says it needs Julia 1.9 or later. The four days of rainfall became the 3×4 matrix of the lesson on views, and stack(days; dims = 1) put them in rows instead, a 4×3 matrix. reduce(hcat, days) gave the same matrix as stack, and the documentation of vcat notes that reduce(vcat, A) calls an efficient method for a large vector of arrays.

Many vectors into one matrix

Use stack(vs) or reduce(hcat, vs) for a long vector of vectors, rather than hcat(vs...), which passes every vector as a separate argument of one call.

reshape keeps the storage order, permutedims moves the elements

reshape and permutedims both change a shape, and they do different things to the values:

reshape_permute.jl: reshape, permutedims, insertdims and dropdims Julia · reshape_permute.jl
# reshape keeps the storage order; permutedims moves the elements to swap the dimensions.
M = [1 2 3; 4 5 6]
println("reshape(M, 3, 2) = ", reshape(M, 3, 2))
println("permutedims(M)   = ", permutedims(M))
v = [1, 2, 3]
println("reshape(v, 1, 3) = ", reshape(v, 1, 3), ", permutedims(v) = ", permutedims(v))
r = insertdims(v; dims = 1)          # needs Julia 1.12 or later
println("insertdims(v; dims = 1) = ", r, ", size ", size(r))
println("dropdims(r; dims = 1) = ", dropdims(r; dims = 1))
reshape_permute.jl, run with Julia 1.13.1 and with the 1.10 LTS Python · reshape_session.py
from session import files, run, use

# reshape against permutedims, then insertdims, which the 1.10 LTS does not have.
files("reshape_permute.jl")
run("""
julia --version
julia reshape_permute.jl
""")
use("1.10.12")
run("""
julia --version
!julia reshape_permute.jl
""")

Output

$ julia --version
julia version 1.13.1
$ julia reshape_permute.jl
reshape(M, 3, 2) = [1 5; 4 3; 2 6]
permutedims(M)   = [1 4; 2 5; 3 6]
reshape(v, 1, 3) = [1 2 3], permutedims(v) = [1 2 3]
insertdims(v; dims = 1) = [1 2 3], size (1, 3)
dropdims(r; dims = 1) = [1, 2, 3]
$ julia --version
julia version 1.10.12
$ julia reshape_permute.jl
reshape(M, 3, 2) = [1 5; 4 3; 2 6]
permutedims(M)   = [1 4; 2 5; 3 6]
reshape(v, 1, 3) = [1 2 3], permutedims(v) = [1 2 3]
ERROR: LoadError: UndefVarError: `insertdims` not defined
Stacktrace:
 [1] top-level scope
   @ ~/demo/reshape_permute.jl:7
in expression starting at ~/demo/reshape_permute.jl:7
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 reshape_session.py

reshape(M, 3, 2) reread the same six slots as 3 rows and 2 columns and gave [1 5; 4 3; 2 6]: the storage order of the first lesson, not a transpose. permutedims(M) moved every element to the swapped position and gave [1 4; 2 5; 3 6], the rows of M as columns. For a vector the two agree: reshape(v, 1, 3) and permutedims(v) both gave the row [1 2 3].

insertdims(v; dims = 1) adds a dimension of length 1 at the position named, here turning the vector into a 1×3 matrix, and dropdims removes it again. The 1.12 release notes list insertdims as new, and on the 1.10.12 LTS the same file stopped at that line with UndefVarError: `insertdims` not defined; reshape(v, 1, 3) gives the shape on every version.

reshape used as a transpose

Wrong: reshape(M, 3, 2) to turn the rows of a 2×3 matrix into columns. It keeps the storage order and gave [1 5; 4 3; 2 6]. Right: permutedims(M), or M' for a matrix of real numbers in linear algebra, as the next section explains.

Checkpoint

Check: reshape against permutedims · Joining arrays with hcat, vcat, cat and stack

  1. Question 1 of 2 Which call turns M = [1 2 3; 4 5 6] into [1 4; 2 5; 3 6]?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: permutedims(M)

    reshape changes the shape and keeps the order of the slots; permutedims swaps the dimensions and moves the values with them.

  2. Question 2 of 2 What is size(stack([[1, 2], [3, 4], [5, 6]]))?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: (2, 3)

    stack builds a matrix with one column per vector by default; reduce(hcat, vs) gives the same matrix.

M’ is a lazy adjoint, permutedims a new matrix

The postfix ' gives the adjoint: The conjugate transpose of a matrix or vector, written A'. Julia returns it as a lazy wrapper that reads A, swapping the indices and conjugating complex values as they are read. of a matrix, its conjugate transpose, and Julia does not build a new matrix for it. A complex matrix shows what that means:

adjoint_vs_permutedims.jl: A', transpose and permutedims Julia · adjoint_vs_permutedims.jl
# A' is a lazy wrapper that conjugates; transpose wraps without conjugating; permutedims copies.
A = [1+2im 3-1im; 0+1im 4+0im]
println("typeof(A') = ", typeof(A'))
println("A'             = ", A')
println("transpose(A)   = ", transpose(A))
println("permutedims(A) = ", permutedims(A))
At = A'
P = permutedims(A)
A[1, 2] = 100 + 0im
println("after A[1, 2] = 100: A' reads ", At, ", the copy reads ", P)
x = [1.0, 2.0, 3.0]
println("size(x') = ", size(x'), ", typeof(x') = ", typeof(x'))
println("typeof(collect(A')) = ", typeof(collect(A')))
adjoint_vs_permutedims.jl, run with Julia 1.13.1 Python · adjoint_session.py
from session import files, run

# A complex matrix through ', transpose and permutedims, then one write into the original.
files("adjoint_vs_permutedims.jl")
run("""
julia adjoint_vs_permutedims.jl
""")

Output

$ julia adjoint_vs_permutedims.jl
typeof(A') = LinearAlgebra.Adjoint{Complex{Int64}, Matrix{Complex{Int64}}}
A'             = Complex{Int64}[1 - 2im 0 - 1im; 3 + 1im 4 + 0im]
transpose(A)   = Complex{Int64}[1 + 2im 0 + 1im; 3 - 1im 4 + 0im]
permutedims(A) = Complex{Int64}[1 + 2im 0 + 1im; 3 - 1im 4 + 0im]
after A[1, 2] = 100: A' reads Complex{Int64}[1 - 2im 0 - 1im; 100 + 0im 4 + 0im], the copy reads Complex{Int64}[1 + 2im 0 + 1im; 3 - 1im 4 + 0im]
size(x') = (1, 3), typeof(x') = LinearAlgebra.Adjoint{Float64, Vector{Float64}}
typeof(collect(A')) = Matrix{Complex{Int64}}

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 adjoint_session.py

Step through it and watch which result sees the write into A:

Interactive · step by step A' wraps the matrix, permutedims copies it Step through the program. Watch which result sees the write into A.
A' wraps the matrix, permutedims copies it, step 5 of 5. Showing A: a 2×2 complex matrix (A[1, 2] = 100 + 0im), At = A' (At[2, 1] reads 100 + 0im), P = permutedims(A) (P[2, 1] still 3 - 1im).reads Acopies 4 valuesA: a 2×2 complex matrixA[1, 2] = 100 + 0imAt = A'At[2, 1] reads 100 + 0imP = permutedims(A)P[2, 1] still 3 - 1im

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The matrix. A holds four complex numbers; A[1, 2] is 3 - 1im.
  2. The adjoint. A' is a LinearAlgebra.Adjoint: its documentation calls it a lazy wrapper, a view of A. It swaps the indices and conjugates each value as it is read, so At[2, 1] reads 3 + 1im.
  3. The copy. permutedims(A) builds a new Matrix with the rows and columns swapped and nothing conjugated: P[2, 1] is 3 - 1im.
  4. One write. A[1, 2] = 100 changes A. At reads A again, so At[2, 1] is now 100 + 0im; P is a copy and still holds 3 - 1im.
  5. Materialise. copy(A') or collect(A') turns the wrapper into an ordinary Matrix when an independent copy is needed: collect(A') printed Matrix{Complex{Int64}}.

The type of A' is LinearAlgebra.Adjoint{Complex{Int64}, Matrix{Complex{Int64}}}, which its documentation describes as a lazy wrapper: it stores no values of its own and reads A as its adjoint. It swaps the indices and conjugates every value as it is read, so 3 - 1im at A[1, 2] reads as 3 + 1im at position (2, 1). transpose(A) swaps without conjugating, and permutedims(A) built an ordinary Matrix with the same swap and no conjugation. The write A[1, 2] = 100 made the difference visible: the adjoint, still reading A, showed 100 + 0im, while the copy kept 3 - 1im.

The documentation says the adjoint type is intended for linear algebra and points to permutedims for general data manipulation, and copy materialises the wrapper: collect(A') returned an ordinary Matrix{Complex{Int64}}. For real numbers ' and transpose give the same values, and for a vector x' is a 1×3 Adjoint row, which is how x' * x became a dot product in the previous lesson.

Key idea

A' is a view that swaps the indices and conjugates complex values, transpose a view that only swaps, and permutedims a new array with the dimensions swapped.

Checkpoint

Check: The lazy adjoint and transpose

  1. One question A = [1 2im; 3 4] and B = A'. After A[1, 2] = 0, what is B[2, 1]?

    Choose one answer.

    Show the answer: One question

    Answer: 0 + 0im

    A' shares A's memory and conjugates as it reads. Use permutedims, copy or collect for an independent matrix.

eachslice walks a 3-D array in views, mapslices builds a new one

A third dimension adds a third index. Here it is the variable, rainfall or maximum temperature, beside station and day, in invented readings of three stations:

station_cube.jl: a station × day × variable array Julia · station_cube.jl
# Rainfall in mm and maximum temperature in °C of 3 stations over 6 days (invented), as one 3×6×2 array.
stations = ["Pune", "Chennai", "Delhi"]
rain = [12.0  0.0 30.5  4.0 18.0  0.0;
         0.0  2.5  8.0 41.0  9.5 22.0;
         7.5 11.0  0.0  1.5  0.0  3.0]
tmax = [29.5 31.0 27.0 30.5 28.0 31.5;
        33.0 34.5 32.0 29.5 31.0 30.0;
        35.5 34.0 36.5 37.0 36.0 35.0]
A = cat(rain, tmax; dims = 3)                       # station × day × variable
println("size(A) = ", size(A))
println("total rain per station: ", vec(sum(A[:, :, 1]; dims = 2)))
println("mean maximum temperature per station: ", round.(vec(sum(A[:, :, 2]; dims = 2)) ./ size(A, 2); digits = 2))
wettest, at = findmax(A[:, :, 1]; dims = 2)
for s in 1:3
    println(rpad(stations[s], 8), "wettest on day ", at[s][2], ", ", wettest[s], " mm")
end
anomaly = A[:, :, 1] .- sum(A[:, :, 1]; dims = 2) ./ size(A, 2)
println("Pune's rain minus its mean: ", round.(anomaly[1, :]; digits = 2))
for (k, layer) in enumerate(eachslice(A; dims = 3))  # one view per variable
    println("slice ", k, ": size ", size(layer), ", a view: ", layer isa SubArray, ", maximum ", maximum(layer))
end
println("size(mapslices(maximum, A; dims = (1, 2))) = ", size(mapslices(maximum, A; dims = (1, 2))))
station_cube.jl, run with Julia 1.13.1 Python · cube_session.py
from session import files, run

# A station × day × variable array reduced to station totals, wettest days and anomalies, then sliced.
files("station_cube.jl")
run("""
julia station_cube.jl
""")

Output

$ julia station_cube.jl
size(A) = (3, 6, 2)
total rain per station: [64.5, 83.0, 23.0]
mean maximum temperature per station: [29.58, 31.67, 35.67]
Pune    wettest on day 3, 30.5 mm
Chennai wettest on day 4, 41.0 mm
Delhi   wettest on day 2, 11.0 mm
Pune's rain minus its mean: [1.25, -10.75, 19.75, -6.75, 7.25, -10.75]
slice 1: size (3, 6), a view: true, maximum 41.0
slice 2: size (3, 6), a view: true, maximum 37.0
size(mapslices(maximum, A; dims = (1, 2))) = (1, 1, 2)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 cube_session.py

Step down the reduction from the 3×6×2 array to the station totals:

Interactive · stage by stage From a station × day × variable array to station totals Step down the column. Each stage names the array and its size.
From a station × day × variable array to station totals, step 5 of 5. Showing A: 3 × 6 × 2, A[:, :, 1]: 3 × 6, sum(…; dims = 2): 3 × 1, vec(…): 3 values, findmax(…; dims = 2). In focus: findmax(…; dims = 2).same layerA: 3 × 6 × 2station × day × variableA[:, :, 1]: 3 × 6rainfall onlysum(…; dims = 2): 3 × 1one total per stationvec(…): 3 values[64.5, 83.0, 23.0]findmax(…; dims = 2)values and positions

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The array. cat(rain, tmax; dims = 3) stacked two 3×6 matrices into a 3×6×2 array: stations down the rows, days across, and the variable as the third dimension.
  2. One variable. A[:, :, 1] takes the whole first layer, rainfall, as a 3×6 matrix.
  3. Along the days. sum with dims = 2 adds the 6 days of each station and keeps the day dimension with length 1: a 3×1 matrix.
  4. A vector. vec drops the length-1 dimension: Pune 64.5 mm, Chennai 83.0 and Delhi 23.0.
  5. The wettest day. findmax with dims = 2 returns each station's largest value and its CartesianIndex: day 3 with 30.5 mm for Pune, day 4 with 41.0 for Chennai, day 2 with 11.0 for Delhi.

cat(rain, tmax; dims = 3) stacked the two layers. A[:, :, 1] took the rainfall layer, sum(…; dims = 2) totalled each station’s six days, and findmax(…; dims = 2) found each station’s wettest day: day 3 for Pune with 30.5 mm, day 4 for Chennai with 41.0 mm and day 2 for Delhi with 11.0 mm. Subtracting the 3×1 matrix of station means from the 3×6 layer gave each day’s departure from its station’s mean, a broadcast that works because dims kept the dimension.

eachslice(A; dims = 3) handed out the two 3×6 layers, and its documentation says it returns views, as the run’s true confirmed. mapslices(maximum, A; dims = (1, 2)) applies a function to every slice and assembles the results into a new array, here of size (1, 1, 2). Its documentation explains that it passes each slice as a copy, which is why the function may change a slice without affecting A, and why eachslice is the cheaper choice inside a loop.

Checkpoint

Check: eachslice and mapslices

  1. One question What does eachslice(A; dims = 3) give for a 3×6×2 array A?

    Choose one answer.

    Show the answer: One question

    Answer: 2 views, each of size 3×6

    eachslice(A; dims = d) gives one view for each index of dimension d, holding all the data of the other dimensions.

Practise: summaries of a station cube

Totals, wettest days and anomalies with dims

Build a 3-station × 30-day × 2-variable array of your own numbers. Compute the rainfall total of each station with sum(…; dims = 2), the day of each station’s highest temperature with findmax(…; dims = 2), and the rainfall anomalies against each station’s mean with one broadcast. Check on your machine that the totals have size (3, 1) before vec and (3,) after it, and that the anomalies of each station add up to zero, within rounding.

The self-check works through the reductions and shapes of this lesson on matrices it has not shown yet, beginning with the idea you have answered least well; each answer explains itself, the wrong ones by the slip they come from:

Practice that adapts to you

Practise this lesson

12 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 12 In sum(M; dims = 1), what does dims = 1 name?

    Choose one answer.

    Show the answer: Question 1 of 12

    Answer: The dimension that is added up, the rows, so each column gets one sum

    sum(M; dims = 1) gave [5 7 9] for M = [1 2 3; 4 5 6]: the rows were added up, one sum per column.

  2. Question 2 of 12 Each row of a 3×4 matrix S holds one shop's sales in 4 quarters. Which expression gives every quarter's share of its own shop's yearly total?

    Choose one answer.

    Show the answer: Question 2 of 12

    Answer: S ./ sum(S; dims = 2)

    Reduce along the dimension you want to total over, and broadcast the kept result against the original.

  3. Question 3 of 12 How many elements does diff(v) have for a vector v of 10 elements?

    Type a number.

    Show the answer: Question 3 of 12

    Answer: 9

    diff gives the difference of each pair of neighbours, so a vector of n values has n − 1 differences, as diff([3, 9, 2, 5]) gave [6, -7, 3].

  4. Question 4 of 12 What does cumsum(M) do for a matrix M?

    Choose one answer.

    Show the answer: Question 4 of 12

    Answer: It stops with UndefKeywordError, because dims must be given

    For vectors cumsum needs nothing more; for matrices say which way to run: dims = 1 down the columns, dims = 2 along the rows.

  5. Question 5 of 12 What is hcat([1, 2], [3, 4])?

    Choose one answer.

    Show the answer: Question 5 of 12

    Answer: [1 3; 2 4]

    [a b] is hcat(a, b) and [a; b] is vcat(a, b), as the manual's table of concatenation syntax lists.

  6. Question 6 of 12 What is size(cat(zeros(2, 3), ones(2, 3); dims = 3))?

    Choose one answer.

    Show the answer: Question 6 of 12

    Answer: (2, 3, 2)

    cat joins along the dimension named in dims, and a dimension beyond the inputs' own becomes a new one.

  7. Question 7 of 12 What does reshape do to the order of the values it is given?

    Choose one answer.

    Show the answer: Question 7 of 12

    Answer: It keeps the storage order and reads the same slots in the new shape

    reshape changes how the slots are read, never where the values are.

  8. Question 8 of 12 Code must run on the 1.10 LTS and on 1.13. Which line makes the 1×3 matrix [1 2 3] from v = [1, 2, 3] on both?

    Choose one answer.

    Show the answer: Question 8 of 12

    Answer: reshape(v, 1, 3)

    reshape and permutedims(v) give a row on every version; insertdims is the newer name for adding singleton dimensions.

  9. Question 9 of 12 For C = [1+2im 0; 0 1], what is C'[1, 1]?

    Choose one answer.

    Show the answer: Question 9 of 12

    Answer: 1 - 2im

    ' is the conjugate transpose of linear algebra; for real numbers it equals transpose.

  10. Question 10 of 12 A table of strings, names = ["a" "b"; "c" "d"], needs its rows and columns swapped. Which call works?

    Choose one answer.

    Show the answer: Question 10 of 12

    Answer: permutedims(names)

    Use ' and transpose for linear algebra on numbers, and permutedims to rearrange any other data.

  11. Question 11 of 12 What does mapslices hand to the function it applies?

    Choose one answer.

    Show the answer: Question 11 of 12

    Answer: A copy of each slice

    mapslices copies each slice and assembles the results; eachslice gives views, which is cheaper in a loop.

  12. Question 12 of 12 A is a 3×6×2 array (station × day × variable). What is size(sum(A; dims = (1, 2)))?

    Choose one answer.

    Show the answer: Question 12 of 12

    Answer: (1, 1, 2)

    dims can name several dimensions at once; each named one shrinks to length 1, as mapslices(maximum, A; dims = (1, 2)) also returned size (1, 1, 2).

Interview questions

Warm-up (fresher to 2–5 years): why does sum(M; dims = 2) return a matrix, and how do you get a vector?

sum(M; dims = 2) adds along the second dimension, the columns of each row, and keeps that dimension in the result with length 1, so a 2×3 M gives a 2×1 Matrix, printed [6; 15;;]. Keeping it is deliberate: the result lines up with M, so M ./ sum(M; dims = 2) divides each row by its own total in one broadcast. When a plain vector is needed, vec(sum(M; dims = 2)) or dropdims(sum(M; dims = 2); dims = 2) gives [6, 15], and sum.(eachrow(M)) builds the vector directly. The same holds for maximum, minimum, count, mean and mapreduce with dims.

Two questions take the shapes of this lesson into real data: numbers that come in from a row-major program, and a complex matrix whose phases change sign. Commit to an answer before you compare it with each first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to senior

    A NumPy program writes a 3 × 4 array of station readings to a binary file in its default order, and the Julia code that reads it calls reshape(values, 3, 4). Every value is there, but they are in the wrong places. Why, and how do you read the file correctly?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A filter-design routine rearranges its matrix of complex coefficients with H' before writing it out, and every phase in the file has the wrong sign. A colleague switches to transpose(H), and a later version that stores 2 × 2 blocks as the elements breaks again. Explain both failures and choose the right call.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • dims names the dimension a reduction adds up, and the result keeps it with length 1: sum(M; dims = 2) of a 2×3 matrix is a 2×1 matrix, and vec or dropdims makes it a vector.
  • argmax, findmax and extrema give positions and extremes, row by row with dims, and cumsum and diff need dims on a matrix.
  • hcat and vcat join side by side and end to end, cat along any dimension, and stack or reduce(hcat, vs) turns many vectors into one matrix.
  • reshape keeps the storage order, so it never transposes; permutedims moves the elements, and insertdims (Julia 1.12) and dropdims add and remove dimensions of length 1.
  • A' is a lazy, conjugating view of A, transpose a view that only swaps, and permutedims or copy gives an independent matrix.
  • eachslice hands out views of the slices of a 3-D array, while mapslices copies each slice and assembles the results into a new array.

Terms in this lesson

adjoint
The conjugate transpose of a matrix or vector, written A'. Julia returns it as a lazy wrapper that reads A, swapping the indices and conjugating complex values as they are read.
dims
The keyword of sum, maximum, count, mean and other reductions that names the dimensions to reduce: sum(M; dims = 1) adds down each column of M and keeps that dimension with length 1.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.