An option I found more appealing was using a "dummy" heatmap from Plots.jl.
# Data
df = DataFrame(a='a':'e', b = rand(5), c=rand(5))
# Get column names and row names
row_labels = df[:, 1]
col_labels = names(df)[2:end]
# Extract matrix of values (excluding the first column which is the label)
matrix_vals = Matrix(df[:, 2:end])
# Prepare annotations for each cell
ann = [(j - .5, i - .5, text("$(round(matrix_vals[i,j], digits=2))", 8, :black, :center)) for i in 1:size(matrix_vals,1), j in 1:size(matrix_vals,2)]
ann = reduce(vcat, ann)
# Annotated heatmap
plot_df = heatmap(
string.(col_labels),
string.(row_labels),
fill(0.92, size(matrix_vals)), # all tiles are light gray
xlabel = "", ylabel = "",
color = cgrad([RGB(0.92,0.92,0.92), RGB(0.92,0.92,0.92)]), # enforce light gray
xticks=:auto,
yticks=:auto,
yflip=true,
framestyle = :box,
annotations=ann,
colorbar=false
)
# Adding grid lines
n_rows, n_cols = size(matrix_vals)
for v in 1:n_cols-1
vline!([v], c=:black, lw=.5, label=nothing)
end
for h in 1:n_rows-1
hline!([h], c=:black, lw=.5, label=nothing)
end
plot_df