On formatting the display of integer arrays

I do combinatorial work which generates arrays containing small integers.
Some arrays are large, so to reduce memory footprint all calculations are done in UInt8. Part of the analysis is to visually scan displays of selected areas of the arrays for numeric patterns (ie, it’s not just to check numeric correctness). Unfortunately Julia prints integer arrays in this verbose & visually indigestible format:

 0x00  0x01  0x01  0x02  0x01  0x02  0x02  0x03  0x01  0x02
 0x01  0x00  0x02  0x01  0x02  0x01  0x03  0x02  0x02  0x01
 0x01  0x02  0x00  0x01  0x02  0x03  0x01  0x02  0x02  0x03
 0x02  0x01  0x01  0x00  0x03  0x02  0x02  0x01  0x03  0x02
 0x01  0x02  0x02  0x03  0x00  0x01  0x01  0x02  0x02  0x03

What I need is this:

0 1 1 2 1 2 2 3 1 2
1 0 2 1 2 1 3 2 2 1
1 2 0 1 2 3 1 2 2 3
2 1 1 0 3 2 2 1 3 2
1 2 2 3 0 1 1 2 2 3

Converting the array to floats helps a bit - but still too cluttered:

 0.0  1.0  1.0  2.0  1.0  2.0  2.0  3.0  1.0  2.0
 1.0  0.0  2.0  1.0  2.0  1.0  3.0  2.0  2.0  1.0
 1.0  2.0  0.0  1.0  2.0  3.0  1.0  2.0  2.0  3.0
 2.0  1.0  1.0  0.0  3.0  2.0  2.0  1.0  3.0  2.0
 1.0  2.0  2.0  3.0  0.0  1.0  1.0  2.0  2.0  3.0

I can’t find display functions in base Julia that offer any further control over the display formatting. The manual doesn’t have any section on display, and only passing minor references in the text. Am I missing some key section? This is basic functionality IMO.

At a minimum I want to eliminate leading zeroes & the fractional field, and control the spacing width. Choice of the radix used for the displayed numbers would also be useful. Please tell me the simplest way to do these things in Julia.

Simplest would probably be to convert the small matrix you are looking at to Int which is displayed in base 10.

Why not just convert arrays to Int for display purposes?

julia> a
5×10 Matrix{UInt8}:
 0x00  0x01  0x01  0x02  0x01  0x02  0x02  0x03  0x01  0x02
 0x01  0x00  0x02  0x01  0x02  0x01  0x03  0x02  0x02  0x01
 0x01  0x02  0x00  0x01  0x02  0x03  0x01  0x02  0x02  0x03
 0x02  0x01  0x01  0x00  0x03  0x02  0x02  0x01  0x03  0x02
 0x01  0x02  0x02  0x03  0x00  0x01  0x01  0x02  0x02  0x03

julia> Int.(a)
5×10 Matrix{Int64}:
 0  1  1  2  1  2  2  3  1  2
 1  0  2  1  2  1  3  2  2  1
 1  2  0  1  2  3  1  2  2  3
 2  1  1  0  3  2  2  1  3  2
 1  2  2  3  0  1  1  2  2  3

Or just use Int8 to store the values instead of UInt8, if they don’t exceed 127.

julia> Int8.(a)
5×10 Matrix{Int8}:
 0  1  1  2  1  2  2  3  1  2
 1  0  2  1  2  1  3  2  2  1
 1  2  0  1  2  3  1  2  2  3
 2  1  1  0  3  2  2  1  3  2
 1  2  2  3  0  1  1  2  2  3

Ultimately you can just write your own loop and call @printf or similar for individual elements if you want fine control over formatting. e.g.

using Printf
function myprint(io::IO, a::AbstractMatrix{<:Integer})
    Base.summary(stdout, a)
    rows, cols = axes(a)
    for i in rows
        println(io)
        for j in cols
            @printf(io, "%2d", a[i,j])
        end
    end
end
myprint(a::AbstractMatrix{<:Integer}) = myprint(stdout, a)

gives

julia> myprint(a)
5×10 Matrix{UInt8}
 0 1 1 2 1 2 2 3 1 2
 1 0 2 1 2 1 3 2 2 1
 1 2 0 1 2 3 1 2 2 3
 2 1 1 0 3 2 2 1 3 2
 1 2 2 3 0 1 1 2 2 3
1 Like

It’s also easy to overwrite the default printing method, if you want to:

julia> mat = rand(UInt8, 2, 5)
2×5 Matrix{UInt8}:
 0x4a  0xfc  0x7e  0x14  0xeb
 0x7b  0xc9  0x73  0xf9  0xc9

julia> Base.show(io::IO, n::UInt8) = show(io, Int(n))

julia> mat  # here the Matrix{UInt8} still reminds you of the type
2×5 Matrix{UInt8}:
  74  252  126   20  235
 123  201  115  249  201

julia> 0x4a  # also changed. Could overwrite 3-arg show & avoid this?
74

Use at own risk, and don’t do this in package code where it could surprise others! But sometimes useful.

One compact alternative:

using Printf: format, Format

foreach(println ∘ join, eachrow(format.((Format("%3i"),), a)))

  0  1  1  2  1  2  2  3  1  2
  1  0  2  1  2  1  3  2  2  1
  1  2  0  1  2  3  1  2  2  3
  2  1  1  0  3  2  2  1  3  2
  1  2  2  3  0  1  1  2  2  0

where a is the same 5×10 Matrix{UInt8} as in post further above.

Or shorter:

println.(join.(eachrow(format.((Format("%3i"),), a))));
1 Like

Yes, just the thing. I felt that if C has had printf since the early 70s, Julia must have something, and I hoped that it wouldn’t all require separate packages.
Somehow I missed Chap 85 in the language manual, putting the wrong choices into the search bar. But documentation is always confusing to a newbie, you don’t know where to drive the pitons in.

The options mentioned by other respondents will be put to use, too. Thanks to all.

1 Like

Technically, printf isn’t part of the C language, you do have to add an import to use it, so it’s pretty much the same. Printf is also overkill for this particular situation.