# Skip empty entries of sparse matrix when plotting a heatmap

**URL:** <https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343>\
**Category:** General Usage\
**Tags:** plots\
**Created:** [January 30, 2026, 1:00pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343 "2026-01-30T13:00:10Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 30, 2026, 1:00pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343/1 "2026-01-30T13:00:10Z")

</div>

I want to plot a sparse matrix as a heatmap, something like this:

```julia-auto
julia> using Plots, SparseArrays

julia> x = sprand(Float64, 5, 5, 0.5)
5×5 SparseMatrixCSC{Float64, Int64} with 11 stored entries:
  ⋅ ⋅ ⋅ ⋅ ⋅ 
  ⋅ 0.838339 0.330949 ⋅ ⋅ 
  ⋅ 0.456005 0.704216 ⋅ ⋅ 
 0.0524143 0.622022 0.319052 0.406603 ⋅ 
 0.356594 ⋅ 0.787174 0.693707 ⋅ 

julia> heatmap(x)

```

which gives:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/8/3/83179888bdf3054df9fbc00aa6580371f5dc7343.png)

The issue is that the non-existing values are treated as zeros, and I want them to be treated as `missing`, which would give:

```julia-auto
julia> y = Matrix(x); z = @. ifelse(y == 0, missing, y)
5×5 Matrix{Union{Missing, Float64}}:
  missing missing missing missing missing
  missing 0.838339 0.330949 missing missing
  missing 0.456005 0.704216 missing missing
 0.0524143 0.622022 0.319052 0.406603 missing
 0.356594 missing 0.787174 0.693707 missing

julia> heatmap(z)

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/6/2/62c9be515b9c26dabb437e1640360c52e274fb50.png)

Can anyone see a smart way of achieving this without creating a dense matrix as an intermediate?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 30, 2026, 1:37pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343/2 "2026-01-30T13:37:21Z")

</div>

> [@lmiq](#):
>
> Can anyone see a smart way of achieving this without creating a dense matrix as an intermediate?

Is there a strong reason to avoid this? The memory usage for `heatmap` is probably going to be proportional to the size of a dense matrix anyway.

To ensure that you only avoid plotting “structural” zeros, you can use `findnz` when creating the dense matrix:

```julia-auto
X = sprand(Float64, 5, 5, 0.5)
I, J, V = findnz(X)
A = fill(NaN, size(X))
setindex!.((A,), V, I, J)

```

(here I used `NaN` instead of `missing` to avoid having to deal with `Union` types).

If the matrix is so huge that allocating a dense version is a problem, then probably you don’t want `heatmap` — you want some kind of smart downsampling algorithm, as discussed in [Plotting image data in Julia is much slower than MATLAB - #17 by stevengj](https://discourse.julialang.org/t/plotting-image-data-in-julia-is-much-slower-than-matlab/79660/17)

---

<div class="post-metadata">

**Author:** ![mxhbl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mxhbl/32/211886_2.png) [@mxhbl](https://discourse.julialang.org/u/mxhbl)\
**Post date:** [January 30, 2026, 2:15pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343/3 "2026-01-30T14:15:55Z")

</div>

`Plots.spy` seems to not allocate, although its a bit difficult to confirm because `@edit spy(x)` does not really lead to anywhere useful.

```julia-repl
julia> using BenchmarkTools, Plots, SparseArrays

julia> x = sprand(Float64, 500, 500, 0.01)
julia> @benchmark Plots.spy(x)
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min … max): 164.625 μs … 23.158 ms ┊ GC (min … max): 0.00% … 97.84%
 Time (median): 199.167 μs ┊ GC (median): 0.00%
 Time (mean ± σ): 231.186 μs ± 569.189 μs ┊ GC (mean ± σ): 12.81% ± 5.26%

             ▁▃▃▅▄▅▆▇██▇▇█▆▄▅▄▃▂▁▁                               
  ▂▂▂▁▂▃▄▅▆▅██████████████████████▇▇▆▅▅▅▄▅▃▃▃▃▃▃▃▃▂▂▃▂▂▂▂▂▂▂▂▂▂ ▅
  165 μs Histogram: frequency by time 260 μs <

 Memory estimate: 296.45 KiB, allocs estimate: 1286.

julia> x_alloc = Matrix(x)
julia> julia> @benchmark Plots.spy(x_alloc)
BenchmarkTools.Trial: 7196 samples with 1 evaluation per sample.
 Range (min … max): 300.791 μs … 51.989 ms ┊ GC (min … max): 0.00% … 98.56%
 Time (median): 465.625 μs ┊ GC (median): 0.00%
 Time (mean ± σ): 693.803 μs ± 1.509 ms ┊ GC (mean ± σ): 31.62% ± 16.23%

  ▃█▃ ▁
  ███▆▁▃▁▃▁▄▃▁▄▄▃▁▄▃▁▁▁▁▁▁▁▁▁▁▃▄▆▆▅▃▄▆▄▅▅▅▅▅▆▆▆▅▅▆▅▆▅▄▄▅▄▅▄▅▅▅ █
  301 μs Histogram: log(frequency) by time 7.06 ms <

 Memory estimate: 2.28 MiB, allocs estimate: 1284.

```

The output of `spy` is a bit different than that of `heatmap`, but when fiddling with the markersize and permuting the matrix you could probably approximate it.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 30, 2026, 2:16pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343/4 "2026-01-30T14:16:57Z")

</div>

Yes, probably. For the moment I added a safeguard to just avoid plotting. But I think that “heatmap” using a memory proportional to the size of the dense matrix is an implementation detail. For instance, a scatter plot can easily look pretty much the same and use only the actual values for plotting.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 30, 2026, 2:20pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343/5 "2026-01-30T14:20:09Z")

</div>

Uhm, I didn’t know about `spy`. That seems a reasonable alternative, if tuned. Thanks!

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 30, 2026, 8:11pm UTC](https://discourse.julialang.org/t/skip-empty-entries-of-sparse-matrix-when-plotting-a-heatmap/135343/6 "2026-01-30T20:11:30Z")

</div>

I actually ended using a scatter plot, which I found also allows creating a colorbar for the marker colors. That worked fine.

 ![image](https://global.discourse-cdn.com/julialang/original/3X/3/0/30f746852bc23c5851d937e6575bfcf64062fe92.jpeg)
