Repository: JuliaAI/MLJBase.jl (julia)

Issue:
`selectcols` should handle tuple input
`selectcols` seems to expect `AbstractVector` input though sometimes this may not be the output type of `schema(...).names`:

```julia
julia> using MLJBase

julia> import MLJModelInterface as MMI

julia> X = (; x1=ones(3), x2=ones(3), x3=ones(3));

julia> MMI.selectcols(X, MMI.schema(X).names)  # Fails
ERROR: MethodError: no method matching _selectcols(::MLJModelInterface.FullInterface, ::Val{…}, ::@NamedTuple{…}, ::Tuple{…})
The function `_selectcols` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  _selectcols(::Union{MLJModelInterface.FullInterface, MLJModelInterface.LightInterface}, ::Val{:other}, ::Any, ::Any)
   @ MLJModelInterface ~/.julia/packages/MLJModelInterface/y9x5A/src/data_utils.jl:381
  _selectcols(::Union{MLJModelInterface.FullInterface, MLJModelInterface.LightInterface}, ::Val{:other}, ::Nothing, ::Any)
   @ MLJModelInterface ~/.julia/packages/MLJModelInterface/y9x5A/src/data_utils.jl:379
  _selectcols(::Union{MLJModelInterface.FullInterface, MLJModelInterface.LightInterface}, ::Val{:other}, ::AbstractMatrix, ::Any)
   @ MLJModelInterface ~/.julia/packages/MLJModelInterface/y9x5A/src/data_utils.jl:377
  ...

Stacktrace:
 [1] selectcols(m::MLJModelInterface.FullInterface, v::Val{…}, X::@NamedTuple{…}, r::Tuple{…})
   @ MLJModelInterface ~/.julia/packages/MLJModelInterface/y9x5A/src/data_utils.jl:374
 [2] selectcols(X::@NamedTuple{x1::Vector{…}, x2::Vector{…}, x3::Vector{…}}, c::Tuple{Symbol, Symbol, Symbol})
   @ MLJModelInterface ~/.julia/packages/MLJModelInterface/y9x5A/src/data_utils.jl:370
 [3] top-level scope
   @ REPL[12]:1
Some type information was truncated. Use `show(err)` to see complete types.

julia> MMI.selectcols(X, collect(MMI.schema(X).names))  # Works
(x1 = [1.0, 1.0, 1.0],
 x2 = [1.0, 1.0, 1.0],
 x3 = [1.0, 1.0, 1.0],)
```

Pull request description:
This PR closes #991. It also fixes an unrelated test that seems to have been broken by recent Julia update. Something about the behaviour of `@doc`, it seems. 

@OkonSamuel Let me know if you had a better suggestion for this PR. Otherwise, please review.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a103cea1..891ee3d8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,7 @@ jobs:
         with:
           version: ${{ matrix.version }}
           arch: ${{ matrix.arch }}
-      - uses: actions/cache@v1
+      - uses: julia-actions/cache@v1
         env:
           cache-name: cache-artifacts
         with:
diff --git a/src/interface/data_utils.jl b/src/interface/data_utils.jl
index 7b20178c..c0b137af 100644
--- a/src/interface/data_utils.jl
+++ b/src/interface/data_utils.jl
@@ -101,7 +101,7 @@ function MMI.selectcols(::FI, ::Val{:table}, X, c::Union{Symbol, Integer})
     return Tables.getcolumn(cols, c)
 end
 
-function MMI.selectcols(::FI, ::Val{:table}, X, c::Union{Colon, AbstractArray})
+function MMI.selectcols(::FI, ::Val{:table}, X, c)
     if isdataframe(X)
         return X[!, c]
     else
@@ -115,7 +115,7 @@ end
 # utils for `select`*
 
 # project named tuple onto a tuple with only specified `labels` or indices:
-function project(t::NamedTuple, labels::AbstractArray{Symbol})
+function project(t::NamedTuple, labels::Union{AbstractArray{Symbol},NTuple{<:Any,Symbol}})
     return NamedTuple{tuple(labels...)}(t)
 end
 
@@ -123,10 +123,20 @@ project(t::NamedTuple, label::Colon) = t
 project(t::NamedTuple, label::Symbol) = project(t, [label,])
 project(t::NamedTuple, i::Integer) = project(t, [i,])
 
-function project(t::NamedTuple, indices::AbstractArray{<:Integer})
+function project(
+    t::NamedTuple,
+    indices::AbstractArray{<:Integer},
+    )
     return NamedTuple{tuple(keys(t)[indices]...)}(tuple([t[i] for i in indices]...))
 end
 
+function project(
+    t::NamedTuple,
+    indices::Tuple{<:Any,Vararg{<:Integer}},
+    )
+    return NamedTuple{tuple(keys(t)[[indices...]]...)}(tuple([t[i] for i in indices]...))
+end
+
 # utils for selectrows
 typename(X) = split(string(supertype(typeof(X))), '.')[end]
 isdataframe(X) = typename(X) == "AbstractDataFrame"


Test patch:
diff --git a/test/interface/data_utils.jl b/test/interface/data_utils.jl
index 84d35a5e..df3ef722 100644
--- a/test/interface/data_utils.jl
+++ b/test/interface/data_utils.jl
@@ -149,7 +149,9 @@ end
     s = schema(tt)
     @test nrows(tt) == N
 
-    @test selectcols(tt, 4:6) ==
+    @test selectcols(tt, 4:6) == selectcols(tt, (4, 5, 6)) ==
+        selectcols(tt, (:x4, :x5, :z)) ==
+        selectcols(tt, [:x4, :x5, :z]) ==
         selectcols(TypedTables.Table(x4=tt.x4, x5=tt.x5, z=tt.z), :)
     @test selectcols(tt, [:x1, :z]) ==
         selectcols(TypedTables.Table(x1=tt.x1, z=tt.z), :)
@@ -197,6 +199,9 @@ end
     v = categorical(collect("asdfasdf"))
     tt = TypedTables.Table(v=v, w=v)
     @test selectcols(tt, :w) == v
+
+    X = (; x1=ones(3), x2=ones(3), x3=ones(3));
+    selectcols(X, MLJBase.schema(X).names)
 end
 
 # https://github.com/JuliaAI/MLJBase.jl/issues/784
diff --git a/test/resampling.jl b/test/resampling.jl
index 62812eec..80f50c1b 100644
--- a/test/resampling.jl
+++ b/test/resampling.jl
@@ -862,6 +862,8 @@ end
              measures=[LogLoss(), BrierScore()], verbosity=0)
 end
 
+docstring_text = @doc(PerformanceEvaluation) |> string
+
 @testset "reported fields in documentation" begin
     # Using `evaluate` to obtain a `PerformanceEvaluation` object.
     clf = ConstantClassifier()
@@ -875,11 +877,11 @@ end
     cols = ["measure", "operation", "measurement", "1.96*SE", "per_fold"]
     @test all(contains.(show_text, cols))
     print(show_text)
-    docstring_text = string(@doc(PerformanceEvaluation))
     for fieldname in fieldnames(PerformanceEvaluation)
         @test contains(show_text, string(fieldname))
         # string(text::Markdown.MD) converts `-` list items to `*`.
-        @test contains(docstring_text, " * `$fieldname`")
+        @test contains(docstring_text, "* `$fieldname`") ||
+            contains(docstring_text, "- `$fieldname`")
     end
 
     measures = [LogLoss(), Accuracy()]