Huge props to @amoeba for making this great example. I just want to show that the auto-encoder training and reconstruction procedure described in that post can be done also in R with similar ease. The auto-encoder below is setup so it emulates amoeba's example as close as possible - same optimiser and overall architecture. The exact costs are not reproducible due to the TensorFlow back-end not being seeded similarly.
Initialisation
library(keras)
library(rARPACK) # to use SVDS
rm(list=ls())
mnist = dataset_mnist()
x_train = mnist$train$x
y_train = mnist$train$y
x_test = mnist$test$x
y_test = mnist$test$y
# reshape & rescale
dim(x_train) = c(nrow(x_train), 784)
dim(x_test) = c(nrow(x_test), 784)
x_train = x_train / 255
x_test = x_test / 255
PCA
mus = colMeans(x_train)
x_train_c = sweep(x_train, 2, mus)
x_test_c = sweep(x_test, 2, mus)
digitSVDS = svds(x_train_c, k = 2)
ZpcaTEST = x_test_c %*% digitSVDS$v # PCA projection of test data
Autoencoder
model = keras_model_sequential()
model %>%
layer_dense(units = 512, activation = 'elu', input_shape = c(784)) %>%
layer_dense(units = 128, activation = 'elu') %>%
layer_dense(units = 2, activation = 'linear', name = "bottleneck") %>%
layer_dense(units = 128, activation = 'elu') %>%
layer_dense(units = 512, activation = 'elu') %>%
layer_dense(units = 784, activation='sigmoid')
model %>% compile(
loss = loss_mean_squared_error, optimizer = optimizer_adam())
history = model %>% fit(verbose = 2, validation_data = list(x_test, x_test),
x_train, x_train, epochs = 5, batch_size = 128)
# Unsurprisingly a 3-year old laptop is slower than a desktop
# Train on 60000 samples, validate on 10000 samples
# Epoch 1/5
# - 14s - loss: 0.0570 - val_loss: 0.0488
# Epoch 2/5
# - 15s - loss: 0.0470 - val_loss: 0.0449
# Epoch 3/5
# - 15s - loss: 0.0439 - val_loss: 0.0426
# Epoch 4/5
# - 15s - loss: 0.0421 - val_loss: 0.0413
# Epoch 5/5
# - 14s - loss: 0.0408 - val_loss: 0.0403
# Set the auto-encoder
autoencoder = keras_model(model$input, model$get_layer('bottleneck')$output)
ZencTEST = autoencoder$predict(x_test) # bottleneck representation of test data
Plotting PCA projection side-by-side with the bottleneck representation
par(mfrow=c(1,2))
myCols = colorRampPalette(c('green', 'red', 'blue', 'orange', 'steelblue2',
'darkgreen', 'cyan', 'black', 'grey', 'magenta') )
plot(ZpcaTEST[1:5000,], col= myCols(10)[(y_test+1)],
pch=16, xlab = 'Score 1', ylab = 'Score 2', main = 'PCA' )
legend( 'bottomright', col= myCols(10), legend = seq(0,9, by=1), pch = 16 )
plot(ZencTEST[1:5000,], col= myCols(10)[(y_test+1)],
pch=16, xlab = 'Score 1', ylab = 'Score 2', main = 'Autoencoder' )
legend( 'bottomleft', col= myCols(10), legend = seq(0,9, by=1), pch = 16 )

Reconstructions
We can make the reconstruction of the digits with the usual manner. (Top row are the original digits, middle row the PCA reconstructions and bottom row the autoencoder reconstructions.)
Renc = predict(model, x_test) # autoencoder reconstruction
Rpca = sweep( ZpcaTEST %*% t(digitSVDS$v), 2, -mus) # PCA reconstruction
dev.off()
par(mfcol=c(3,9), mar = c(1, 1, 0, 0))
myGrays = gray(1:256 / 256)
for(u in seq_len(9) ){
image( matrix( x_test[u,], 28,28, byrow = TRUE)[,28:1], col = myGrays,
xaxt='n', yaxt='n')
image( matrix( Rpca[u,], 28,28, byrow = TRUE)[,28:1], col = myGrays ,
xaxt='n', yaxt='n')
image( matrix( Renc[u,], 28,28, byrow = TRUE)[,28:1], col = myGrays,
xaxt='n', yaxt='n')
}

As noted, more epochs and a deeper and/or more smartly trained network will give much better results. For example, the PCA reconstruction error of $k$= 9 is approximately $0.0356$, we can get almost the same error ($0.0359$) from the autoencoder described above, just by increasing the training epochs from 5 to 25. In this use-case, the 2 autoencoder-derived components will provide similar reconstruction error as 9 principal components. Cool!