Skip to content

Commit

Permalink
Merge pull request JuliaLang#13598 from artkuo/quick_print_matrix
Browse files Browse the repository at this point in the history
Fix Base.print_matrix() for big matrices
  • Loading branch information
jakebolewski committed Oct 14, 2015
2 parents d35e69c + dcb44c5 commit 8a86270
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 42 deletions.
103 changes: 61 additions & 42 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1062,79 +1062,98 @@ function print_matrix_vdots(io::IO,
end
end

"""
`print_matrix(io, X)` composes an entire matrix, taking into account the screen size.
If X is too big, it will be nine-sliced with vertical, horizontal, or diagonal
ellipsis inserted as appropriate.
Optional parameters are screen size tuple sz such as (24,80),
string pre prior to the matrix (e.g. opening bracket), which will cause
a corresponding same-size indent on following rows,
string post on the end of the last row of the matrix.
Also options to use different ellipsis characters hdots,
vdots, ddots. These are repeated every hmod or vmod elements.
"""
function print_matrix(io::IO, X::AbstractVecOrMat,
sz::Tuple{Integer, Integer} = (s = tty_size(); (s[1]-4, s[2])),
pre::AbstractString = " ",
sep::AbstractString = " ",
post::AbstractString = "",
pre::AbstractString = " ", # pre-matrix string
sep::AbstractString = " ", # separator between elements
post::AbstractString = "", # post-matrix string
hdots::AbstractString = " \u2026 ",
vdots::AbstractString = "\u22ee",
ddots::AbstractString = " \u22f1 ",
hmod::Integer = 5, vmod::Integer = 5)
rows, cols = sz
cols -= length(pre) + length(post)
presp = repeat(" ", length(pre))
screenheight, screenwidth = sz
screenwidth -= length(pre) + length(post)
presp = repeat(" ", length(pre)) # indent each row to match pre string
postsp = ""
@assert strwidth(hdots) == strwidth(ddots)
ss = length(sep)
sepsize = length(sep)
m, n = size(X,1), size(X,2)
if m <= rows # rows fit
A = alignment(X,1:m,1:n,cols,cols,ss)
if n <= length(A) # rows and cols fit
for i = 1:m
# To figure out alignments, only need to look at as many rows as could
# fit down screen. If screen has at least as many rows as A, look at A.
# If not, then we only need to look at the first and last chunks of A,
# each half a screen height in size.
halfheight = div(screenheight,2)
rowsA = m <= screenheight ? (1:m) : [1:halfheight; m-div(screenheight-1,2)+1:m]
# Similarly for columns, only necessary to get alignments for as many
# columns as could conceivably fit across the screen
maxpossiblecols = div(screenwidth, 1+sepsize)
colsA = n <= maxpossiblecols ? (1:n) : [1:maxpossiblecols; (n-maxpossiblecols+1):n]
A = alignment(X,rowsA,colsA,screenwidth,screenwidth,sepsize)
# Nine-slicing is accomplished using print_matrix_row repeatedly
if m <= screenheight # rows fit vertically on screen
if n <= length(A) # rows and cols fit so just print whole matrix in one piece
for i in rowsA
print(io, i == 1 ? pre : presp)
print_matrix_row(io, X,A,i,1:n,sep)
print_matrix_row(io, X,A,i,colsA,sep)
print(io, i == m ? post : postsp)
if i != m; println(io, ); end
end
else # rows fit, cols don't
c = div(cols-length(hdots)+1,2)+1
R = reverse(alignment(X,1:m,n:-1:1,c,c,ss))
c = cols - sum(map(sum,R)) - (length(R)-1)*ss - length(hdots)
L = alignment(X,1:m,1:n,c,c,ss)
for i = 1:m
else # rows fit down screen but cols don't, so need horizontal ellipsis
c = div(screenwidth-length(hdots)+1,2)+1 # what goes to right of ellipsis
Ralign = reverse(alignment(X,rowsA,reverse(colsA),c,c,sepsize)) # alignments for right
c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)
Lalign = alignment(X,rowsA,colsA,c,c,sepsize) # alignments for left of ellipsis
for i in rowsA
print(io, i == 1 ? pre : presp)
print_matrix_row(io, X,L,i,1:length(L),sep)
print_matrix_row(io, X,Lalign,i,1:length(Lalign),sep)
print(io, i % hmod == 1 ? hdots : repeat(" ", length(hdots)))
print_matrix_row(io, X,R,i,n-length(R)+1:n,sep)
print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep)
print(io, i == m ? post : postsp)
if i != m; println(io, ); end
end
end
else # rows don't fit
t = div(rows,2)
I = [1:t; m-div(rows-1,2)+1:m]
A = alignment(X,I,1:n,cols,cols,ss)
if n <= length(A) # rows don't fit, cols do
for i in I
else # rows don't fit so will need vertical ellipsis
if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis
for i in rowsA
print(io, i == 1 ? pre : presp)
print_matrix_row(io, X,A,i,1:n,sep)
print_matrix_row(io, X,A,i,colsA,sep)
print(io, i == m ? post : postsp)
if i != I[end]; println(io, ); end
if i == t
if i != rowsA[end]; println(io, ); end
if i == halfheight
print(io, i == 1 ? pre : presp)
print_matrix_vdots(io, vdots,A,sep,vmod,1)
println(io, i == m ? post : postsp)
end
end
else # neither rows nor cols fit
c = div(cols-length(hdots)+1,2)+1
R = reverse(alignment(X,I,n:-1:1,c,c,ss))
c = cols - sum(map(sum,R)) - (length(R)-1)*ss - length(hdots)
L = alignment(X,I,1:n,c,c,ss)
r = mod((length(R)-n+1),vmod)
for i in I
else # neither rows nor cols fit, so use all 3 kinds of dots
c = div(screenwidth-length(hdots)+1,2)+1
Ralign = reverse(alignment(X,rowsA,reverse(colsA),c,c,sepsize))
c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)
Lalign = alignment(X,rowsA,colsA,c,c,sepsize)
r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half
for i in rowsA
print(io, i == 1 ? pre : presp)
print_matrix_row(io, X,L,i,1:length(L),sep)
print_matrix_row(io, X,Lalign,i,1:length(Lalign),sep)
print(io, i % hmod == 1 ? hdots : repeat(" ", length(hdots)))
print_matrix_row(io, X,R,i,n-length(R)+1:n,sep)
print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep)
print(io, i == m ? post : postsp)
if i != I[end]; println(io, ); end
if i == t
if i != rowsA[end]; println(io, ); end
if i == halfheight
print(io, i == 1 ? pre : presp)
print_matrix_vdots(io, vdots,L,sep,vmod,1)
print_matrix_vdots(io, vdots,Lalign,sep,vmod,1)
print(io, ddots)
print_matrix_vdots(io, vdots,R,sep,vmod,r)
print_matrix_vdots(io, vdots,Ralign,sep,vmod,r)
println(io, i == m ? post : postsp)
end
end
Expand Down
11 changes: 11 additions & 0 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,14 @@ function f13127()
takebuf_string(buf)
end
@test f13127() == "f"

# print_matrix should be able to handle small and large objects easily, test by
# calling writemime. This also indirectly tests print_matrix_row, which
# is used repeatedly by print_matrix.
# This fits on screen:
@test replstr(eye(10)) == "10x10 Array{Float64,2}:\n 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0"
# an array too long vertically to fit on screen, and too long horizontally:
@test replstr(collect(1.:100.)) == "100-element Array{Float64,1}:\n 1.0\n 2.0\n 3.0\n 4.0\n 5.0\n 6.0\n 7.0\n 8.0\n 9.0\n 10.0\n\n 92.0\n 93.0\n 94.0\n 95.0\n 96.0\n 97.0\n 98.0\n 99.0\n 100.0"
@test replstr(collect(1.:100.)') == "1x100 Array{Float64,2}:\n 1.0 2.0 3.0 4.0 5.0 6.0 7.0 … 95.0 96.0 97.0 98.0 99.0 100.0"
# too big in both directions to fit on screen:
@test replstr((1.:100.)*(1:100)') == "100x100 Array{Float64,2}:\n 1.0 2.0 3.0 4.0 5.0 6.0 … 97.0 98.0 99.0 100.0\n 2.0 4.0 6.0 8.0 10.0 12.0 194.0 196.0 198.0 200.0\n 3.0 6.0 9.0 12.0 15.0 18.0 291.0 294.0 297.0 300.0\n 4.0 8.0 12.0 16.0 20.0 24.0 388.0 392.0 396.0 400.0\n 5.0 10.0 15.0 20.0 25.0 30.0 485.0 490.0 495.0 500.0\n 6.0 12.0 18.0 24.0 30.0 36.0 … 582.0 588.0 594.0 600.0\n 7.0 14.0 21.0 28.0 35.0 42.0 679.0 686.0 693.0 700.0\n 8.0 16.0 24.0 32.0 40.0 48.0 776.0 784.0 792.0 800.0\n 9.0 18.0 27.0 36.0 45.0 54.0 873.0 882.0 891.0 900.0\n 10.0 20.0 30.0 40.0 50.0 60.0 970.0 980.0 990.0 1000.0\n ⋮ ⋮ ⋱ \n 92.0 184.0 276.0 368.0 460.0 552.0 8924.0 9016.0 9108.0 9200.0\n 93.0 186.0 279.0 372.0 465.0 558.0 9021.0 9114.0 9207.0 9300.0\n 94.0 188.0 282.0 376.0 470.0 564.0 9118.0 9212.0 9306.0 9400.0\n 95.0 190.0 285.0 380.0 475.0 570.0 9215.0 9310.0 9405.0 9500.0\n 96.0 192.0 288.0 384.0 480.0 576.0 … 9312.0 9408.0 9504.0 9600.0\n 97.0 194.0 291.0 388.0 485.0 582.0 9409.0 9506.0 9603.0 9700.0\n 98.0 196.0 294.0 392.0 490.0 588.0 9506.0 9604.0 9702.0 9800.0\n 99.0 198.0 297.0 396.0 495.0 594.0 9603.0 9702.0 9801.0 9900.0\n 100.0 200.0 300.0 400.0 500.0 600.0 9700.0 9800.0 9900.0 10000.0"

0 comments on commit 8a86270

Please sign in to comment.