JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.73k stars 5.48k forks source link

More proactive error or better error message for transposing array of strings? #20595

Open tlnagy opened 7 years ago

tlnagy commented 7 years ago
julia> x = rand(["A", "T", "C", "G"], 10)
10-element Array{String,1}:
 "G"
 "G"
 "A"
 "G"
 "A"
 "T"
 "A"
 "T"
 "C"
 "A"

julia> transpose(x)
1×10 RowVector{Any,Array{String,1}}:
Error showing value of type RowVector{Any,Array{String,1}}:
ERROR: MethodError: no method matching transpose(::String)
Closest candidates are:
  transpose(::BitArray{2}) at linalg/bitarray.jl:265
  transpose(::Number) at number.jl:100
  transpose(::RowVector{T,CV} where CV<:(ConjArray{T,1,V} where V<:(AbstractArray{T,1} where T) where T) where T) at linalg/rowvector.jl:81
  ...
Stacktrace:
 [1] _getindex at ./abstractarray.jl:831 [inlined]
 [2] getindex(::RowVector{Any,Array{String,1}}, ::Int64, ::Int64) at ./abstractarray.jl:810
 [3] isassigned(::RowVector{Any,Array{String,1}}, ::Int64, ::Int64, ::Vararg{Int64,N} where N) at ./abstractarray.jl:214
 [4] alignment(::IOContext{Base.Terminals.TTYTerminal}, ::RowVector{Any,Array{String,1}}, ::Array{Int64,1}, ::Array{Int64,1}, ::Int64, ::Int64, ::Int64) at ./show.jl:1317
 [5] print_matrix(::IOContext{Base.Terminals.TTYTerminal}, ::RowVector{Any,Array{String,1}}, ::String, ::String, ::String, ::String, ::String, ::String, ::Int64, ::Int64) at ./show.jl:1447
 [6] print_matrix(::IOContext{Base.Terminals.TTYTerminal}, ::RowVector{Any,Array{String,1}}, ::String, ::String, ::String) at ./show.jl:1419
 [7] #showarray#250(::Bool, ::Function, ::IOContext{Base.Terminals.TTYTerminal}, ::RowVector{Any,Array{String,1}}, ::Bool) at ./show.jl:1669
 [8] display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::MIME{Symbol("text/plain")}, ::RowVector{Any,Array{String,1}}) at ./REPL.jl:122
 [9] display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::RowVector{Any,Array{String,1}}) at ./REPL.jl:125
 [10] display(::RowVector{Any,Array{String,1}}) at ./multimedia.jl:194

julia> transpose(rand(10))
1×10 RowVector{Float64,Array{Float64,1}}:
 0.249592  0.533949  0.989713  0.971423  0.517202  0.195772  0.778558  0.860598  0.105252  0.94939

This is on 5258d51 (3 day old master).

tkelman commented 7 years ago

was deprecated in 0.5, removed in 0.6. use permutedims

KristofferC commented 7 years ago

? This error is thrown when showing the RowVector.

mbauman commented 7 years ago

Yeah, that's because it's now lazy. The error is happening at the same place, but it now only occurs upon accesses.

KristofferC commented 7 years ago

Yes, but having the error happen in the show function instead of up front when the call is made indicates to the user that he/she didn't do anything wrong and that the show function is bugged.

KristofferC commented 7 years ago

Maybe this could be handled in a similar manner as the #undef for array printing. It will also error when you try to access it but the show method doesn't throw.

andyferris commented 7 years ago

Did LinAlg.transpose_type(String) return Any or Union{}?

EDIT: It's Any.

andyferris commented 7 years ago

Hmm... this is interesting:

julia> Core.Inference.return_type(transpose, Tuple{String})
Union{}

julia> Base.promote_op(transpose, String)
Any

Can anyone explain why? Is there a good reason not to use a direct call to inference? I could fix this easily if transpose_type returned Union{}.

pabloferz commented 7 years ago

promote_op is used in Base mainly for matrix multiplication, so we need to return a type where the result fits. You could use Base._return_type(transpose, Tuple{String}) for this.

tlnagy commented 7 years ago

What I took away from this error was that julia has a bug, not that I did something wrong. I wasn't aware of the deprecation.

tlnagy commented 7 years ago
julia> y = reshape(x, 2, 5)
2×5 Array{String,2}:
 "T"  "C"  "G"  "A"  "G"
 "A"  "T"  "C"  "A"  "C"

julia> y'
ERROR: MethodError: no method matching transpose(::String)
Closest candidates are:
  transpose(::BitArray{2}) at linalg/bitarray.jl:265
  transpose(::Number) at number.jl:100
  transpose(::RowVector{T,CV} where CV<:(ConjArray{T,1,V} where V<:(AbstractArray{T,1} where T) where T) where T) at linalg/rowvector.jl:81
  ...
Stacktrace:
 [1] transpose_f!(::Base.#ctranspose, ::Array{String,2}, ::Array{String,2}) at ./linalg/transpose.jl:54
 [2] ctranspose(::Array{String,2}) at ./linalg/transpose.jl:126

julia> permutedims(y, (2, 1))
5×2 Array{String,2}:
 "T"  "A"
 "C"  "T"
 "G"  "C"
 "A"  "A"
 "G"  "C"

I see that permutedims works, but it's definitely not what I think of first. Maybe it should be suggested when transpose is called on an unsupported type?

andyferris commented 7 years ago

@tlnagy I agree, this is definitely unclear to users, and a good error message for failed transposition would be very useful. I'm going to try and work on it sometime in the next week or so.

One major difference between permutedims and transpose is that transpose is recursive - if you have a matrix of matrices, each matrix will also be transposed. This is useful for a certain type of block representation of matrices.

If you have "data" in multidimensional arrays (as opposed to for linear algebra purposes), it's quite often clearer (to read) to use permutedims anyway. The amount of MATLAB bugs and obscure code involving many ' compressed into one line of code that I've seen makes me believe using ' and .' to rearrange data is a bad idea.