What does passing a vector as `dims` to `Statistics.std` mean?

I have been trying to use the std function on a batch of images. For instance, this is a batch of 2 images having 3 channels each -

xs = rand(3, 3, 3, 2)

I want to find the standard deviation of the first image which can be achieved using -

std(xs[:, :, :, 1])

Now I want to achieve the same thing using the dims kwarg, but I cannot figure out the usage of the same. More precisely, what is the function actually calculating if I pass in a vector as dims? I couldn’t find any examples of this on the internet or in Julia’s documentation. An example with the batch of images defined above -

julia> xs
3×3×3×2 Array{Float64, 4}:
[:, :, 1, 1] =
 0.775483  0.804976  0.805342
 0.502332  0.571661  0.788025
 0.627064  0.175482  0.733361

[:, :, 2, 1] =
 0.744905  0.861811  0.236851
 0.493482  0.672934  0.954064
 0.382317  0.368025  0.0896878

[:, :, 3, 1] =
 0.287127  0.505461  0.280565
 0.637652  0.07373   0.132018
 0.493528  0.173168  0.0917483

[:, :, 1, 2] =
 0.993559  0.85186   0.974956
 0.962802  0.20569   0.171647
 0.344311  0.158447  0.0416952

[:, :, 2, 2] =
 0.0756434   0.800149  0.0489549
 0.868386    0.197549  0.56844
 0.00663242  0.131731  0.614948

[:, :, 3, 2] =
 0.154051   0.93772    0.143768
 0.196975   0.0213649  0.0269268
 0.0658219  0.913233   0.179747

julia> std(xs, dims=[3,1])
1×3×1×2 Array{Float64, 4}:
[:, :, 1, 1] =
 0.16137  0.287385  0.354948

[:, :, 1, 2] =
 0.412971  0.39161  0.332775

Could someone please explain to me how is the final 1x3x1x2 Array has been calculated? Thank you!

Hi @Saransh-cpp

dims is used to specify on which dimensions you want to calculate the std on…
Let’s say for example we have two 3-channel images of size (4, 4) as below →

julia> using Statistics

# two 3-channel images of size (4, 4)
julia> xs = rand(3, 4, 4, 2)
3×4×4×2 Array{Float64, 4}:
[:, :, 1, 1] =
 0.638898  0.0607577   0.248664  0.134614
 0.359026  0.045089    0.934976  0.208916
 0.574276  0.00640597  0.916209  0.146101

[:, :, 2, 1] =
 0.337945    0.385004  0.941823  0.879369
 0.00222145  0.191058  0.53481   0.513988
 0.760298    0.589457  0.286568  0.510345

[:, :, 3, 1] =
 0.815596  0.157969  0.623036  0.0807889
 0.557733  0.813058  0.474988  0.159235
 0.893057  0.386838  0.570377  0.518802

[:, :, 4, 1] =
 0.104052  0.380098  0.268024   0.578795
 0.413343  0.998084  0.449842   0.224211
 0.864892  0.808143  0.0459052  0.457739

[:, :, 1, 2] =
 0.24586   0.870185   0.770928  0.31385
 0.3981    0.391596   0.845867  0.146243
 0.114382  0.0579019  0.247182  0.0315621

[:, :, 2, 2] =
 0.306497  0.418139    0.713051  0.87407
 0.14916   0.302198    0.621749  0.0677304
 0.753772  0.00230659  0.938192  0.106774

[:, :, 3, 2] =
 0.624643  0.598719   0.160968   0.766046
 0.755545  0.0378673  0.397636   0.86422
 0.809038  0.915949   0.0150156  0.0320573

[:, :, 4, 2] =
 0.128597  0.0363205  0.862884  0.957192
 0.646652  0.159509   0.553012  0.71586
 0.5693    0.270781   0.950244  0.844724


# std on over all numbers (on all dims)
julia> std(xs, dims=[1, 2, 3, 4])
1×1×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 0.308191790621495
# we will just get a single number, preserving input shape (x, x, x, x)


# std on batch dimension (i.e, last dim), we specify 1st 3 dims to calc std on
julia> std(xs, dims=[1, 2, 3])
1×1×1×2 Array{Float64, 4}:
[:, :, 1, 1] =
 0.29192537121237955

[:, :, 1, 2] =
 0.32666112721755525
# we will just get two numbers, each for one image

# std on channel dimension (i.e, first dim), we specify last 3 dims to calc std on
julia> std(xs, dims=[2, 3, 4])
3×1×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 0.30341597287159655
 0.2841992921298127
 0.34238183603676203
# we will just get three numbers, each for one channel

Input shape will be preserved in outputs, IMO there is no real benefit of thinking in terms of output dim, rather we can just think of it as numbers with length == dim length you wanted the output to be on.

1 Like

Thank you for the detailed explanation and the examples! I have been using the WHCN format for images (as used by Flux) and tweaking your examples just by a tiny bit did the trick. Additionally, I discovered that the dims can also be specified like this -

julia> std(xs, dims=1:3)
1 Like

That’s cool ! :raised_hands:

1 Like