Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,23 @@ Xnew = transform(mach, X)
## Available Transformers
See [complete list](transformers/all_transformers) of transformers in this package.

In `MLJTransforms` we denote transformers that can operate on columns with `Continuous` and/or `Count` [scientific types](https://juliaai.github.io/ScientificTypes.jl/dev/) as *numerical transformers*. Meanwhile, *categorical transformers* operate on `Multiclass` and/or `OrderedFactor` [scientific types](https://juliaai.github.io/ScientificTypes.jl/dev/). Most categorical transformers in this package operate by converting categorical values into numerical values or vectors, and are therefore considered categorical encoders. We categorize categorical encoders as follows:
In `MLJTransforms` we denote transformers that can operate on columns with `Continuous` and/or `Count` [scientific types](https://juliaai.github.io/ScientificTypes.jl/dev/) as *numerical transformers*. Meanwhile, *categorical transformers* operate on `Multiclass` and/or `OrderedFactor` [scientific types](https://juliaai.github.io/ScientificTypes.jl/dev/). Most categorical transformers in this package operate by converting categorical values into numerical values or vectors, and are therefore considered categorical encoders.

Some transformers in this package can operate on both `Finite` and `Infinite` scientific
types or other special scientific types (eg, to represent time). To learn more about
scientific types see [the official
documentation](https://juliaai.github.io/ScientificTypes.jl/dev/#Type-hierarchy).

| **Category** | **Description** |
|:---------------------------:|:-------------------------------------------------------------------------------:|
| [Classical Encoders](transformers/classical.md) | Traditional categorical encoding algorithms and techniques. |
| [Neural-based Encoders](transformers/neural) | Categorical encoders based on neural networks. |
| [Contrast Encoders](transformers/contrast.md) | Categorical encoders that could be modeled via a contrast matrix. |
| [Utility Encoders](transformers/utility.md) | Categorical encoders meant to be used as preprocessors for other transformers or models.|
### Categorical encoders

The categorical encoders in this package can be further broken down as follows:


| **Category** | **Description** |
|:-----------------------------------------------:|:----------------------------------------------------------------------------------------:|
| [Classical Encoders](transformers/classical.md) | Traditional categorical encoding algorithms and techniques. |
| [Neural-based Encoders](transformers/neural) | Categorical encoders based on neural networks. |
| [Contrast Encoders](transformers/contrast.md) | Categorical encoders that could be modeled via a contrast matrix. |
| [Utility Encoders](transformers/utility.md) | Categorical encoders meant to be used as preprocessors for other transformers or models. |


Some transformers in this package can even operate on both `Finite` and `Infinite` scientific types or other special scientific types (eg, to represent time). To learn more about scientific types see [the official documentation](https://juliaai.github.io/ScientificTypes.jl/dev/#Type-hierarchy).
55 changes: 31 additions & 24 deletions docs/src/transformers/all_transformers.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
### Summary Table
| Transformer | Brief Description |
|:----------:|:----------:|
| [Standardizer](@ref) | Transforming columns of numerical features by standardization |
| [UnivariateBoxCoxTransformer](@ref) | Apply BoxCox transformation given a single vector |
| [InteractionTransformer](@ref) | Transforming columns of numerical features to create new interaction features |
| [UnivariateDiscretizer](@ref) | Discretize a continuous vector into an ordered factor |
| [FillImputer](@ref) | Fill missing values of features belonging to any scientific type |
| [UnivariateTimeTypeToContinuous](@ref) | Transform a vector of time type into continuous type |
| [UnivariateFillImputer](@ref) | Fill in missing values in a single vector |
| [OneHotEncoder](@ref) | Encode categorical variables into one-hot vectors |
| [ContinuousEncoder](@ref) | Adds type casting functionality to OnehotEncoder |
| [OrdinalEncoder](@ref) | Encode categorical variables into ordered integers |
| [FrequencyEncoder](@ref) | Encode categorical variables into their normalized or unormalized frequencies |
| [TargetEncoder](@ref) | Encode categorical variables into relevant target statistics |
| [DummyEncoder](@ref ContrastEncoder) | Encodes by comparing each level to the reference level, intercept being the cell mean of the reference group |
| [SumEncoder](@ref ContrastEncoder) | Encodes by comparing each level to the reference level, intercept being the grand mean |
| [HelmertEncoder](@ref ContrastEncoder) | Encodes by comparing levels of a variable with the mean of the subsequent levels of the variable
| [ForwardDifferenceEncoder](@ref ContrastEncoder) | Encodes by comparing adjacent levels of a variable (each level minus the next level)
| [ContrastEncoder](@ref) | Allows defining a custom contrast encoder via a contrast matrix |
| [HypothesisEncoder](@ref ContrastEncoder) | Allows defining a custom contrast encoder via a hypothesis matrix |
| [EntityEmbedder](@ref) | Encode categorical variables into dense embedding vectors |
| [CardinalityReducer](@ref) | Reduce cardinality of high cardinality categorical features by grouping infrequent categories |
| [MissingnessEncoder](@ref) | Encode missing values of categorical features into new values |
| Transformer | Brief Description |
|:------------------------------------------------:|:------------------------------------------------------------------------------------------------------------:|
| [Standardizer](@ref) | Transforming columns of numerical features by standardization |
| [UnivariateBoxCoxTransformer](@ref) | Apply BoxCox transformation given a single vector |
| [PolynomialTransformer](@ref) | Transformer to add columns that are products of other columns |
| [InteractionTransformer](@ref) | Transforming columns of numerical features to create new interaction features¹ |
| [UnivariateDiscretizer](@ref) | Discretize a continuous vector into an ordered factor |
| [FillImputer](@ref) | Fill missing values of features belonging to any scientific type |
| [UnivariateTimeTypeToContinuous](@ref) | Transform a vector of time type into continuous type |
| [UnivariateFillImputer](@ref) | Fill in missing values in a single vector |
| [OneHotEncoder](@ref) | Encode categorical variables into one-hot vectors |
| [ContinuousEncoder](@ref) | Adds type casting functionality to OnehotEncoder |
| [OrdinalEncoder](@ref) | Encode categorical variables into ordered integers |
| [FrequencyEncoder](@ref) | Encode categorical variables into their normalized or unormalized frequencies |
| [TargetEncoder](@ref) | Encode categorical variables into relevant target statistics |
| [DummyEncoder](@ref ContrastEncoder) | Encodes by comparing each level to the reference level, intercept being the cell mean of the reference group |
| [SumEncoder](@ref ContrastEncoder) | Encodes by comparing each level to the reference level, intercept being the grand mean |
| [HelmertEncoder](@ref ContrastEncoder) | Encodes by comparing levels of a variable with the mean of the subsequent levels of the variable |
| [ForwardDifferenceEncoder](@ref ContrastEncoder) | Encodes by comparing adjacent levels of a variable (each level minus the next level) |
| [ContrastEncoder](@ref) | Allows defining a custom contrast encoder via a contrast matrix |
| [HypothesisEncoder](@ref ContrastEncoder) | Allows defining a custom contrast encoder via a hypothesis matrix |
| [EntityEmbedder](@ref) | Encode categorical variables into dense embedding vectors |
| [CardinalityReducer](@ref) | Reduce cardinality of high cardinality categorical features by grouping infrequent categories |
| [MissingnessEncoder](@ref) | Encode missing values of categorical features into new values |

¹The `InteractionTransformer` is deprecated. Use `PolynomialTransformer(interactions_only=true)` instead.

### All Transformers

Expand All @@ -37,6 +40,10 @@ MLJTransforms.UnivariateStandardizer
MLJTransforms.UnivariateBoxCoxTransformer
```

```@docs; canonical = false
MLJTransforms.PolynomialTransformer
```

```@docs; canonical = false
MLJTransforms.InteractionTransformer
```
Expand Down Expand Up @@ -87,4 +94,4 @@ MLJTransforms.CardinalityReducer

```@docs; canonical = false
MLJTransforms.MissingnessEncoder
```
```
3 changes: 2 additions & 1 deletion src/MLJTransforms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export ContrastEncoder
# MLJModels transformers
include("transformers/other_transformers/continuous_encoder.jl")
include("transformers/other_transformers/interaction_transformer.jl")
include("transformers/other_transformers/polynomial_transformer.jl")
include("transformers/other_transformers/univariate_time_type_to_continuous.jl")
include("transformers/other_transformers/fill_imputer.jl")
include("transformers/other_transformers/one_hot_encoder.jl")
Expand All @@ -72,5 +73,5 @@ include("transformers/other_transformers/univariate_discretizer.jl")
export UnivariateDiscretizer,
UnivariateStandardizer, Standardizer, UnivariateBoxCoxTransformer,
OneHotEncoder, ContinuousEncoder, FillImputer, UnivariateFillImputer,
UnivariateTimeTypeToContinuous, InteractionTransformer
UnivariateTimeTypeToContinuous, InteractionTransformer, PolynomialTransformer
end
23 changes: 8 additions & 15 deletions src/transformers/other_transformers/interaction_transformer.jl
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
# The model implementation here is deprecated

@mlj_model mutable struct InteractionTransformer <: Static
order::Int = 2::(_ > 1)
features::Union{Nothing, Vector{Symbol}} = nothing::(_ !== nothing ? length(_) > 1 : true)
end

infinite_scitype(col) = eltype(scitype(col)) <: Infinite

actualfeatures(features::Nothing, table) =
filter(feature -> infinite_scitype(Tables.getcolumn(table, feature)), Tables.columnnames(table))

function actualfeatures(features::Vector{Symbol}, table)
diff = setdiff(features, Tables.columnnames(table))
diff != [] && throw(ArgumentError(string("Column(s) ", join([x for x in diff], ", "), " are not in the dataset.")))

for feature in features
infinite_scitype(Tables.getcolumn(table, feature)) || throw(ArgumentError("Column $feature's scitype is not Infinite."))
end
return Tuple(features)
end

interactions(columns, order::Int) =
collect(Iterators.flatten(combinations(columns, i) for i in 2:order))

interactions(columns, variables...) =
.*((Tables.getcolumn(columns, var) for var in variables)...)

const WARN_INTERACTION_DEPRECATED = """
`InteractionTransformer(; kwargs...)` is deprecated. Instead use
`PolynomialTransformer(; interactions_only=true, kwargs...)`. The
`PolynomialTransformer` type is also provided by the MLJTransforms module.
"""

function MMI.transform(model::InteractionTransformer, _, X)
Base.depwarn(WARN_INTERACTION_DEPRECATED, :transform)
features = actualfeatures(model.features, X)
interactions_ = interactions(features, model.order)
interaction_features = Tuple(Symbol(join(inter, "_")) for inter in interactions_)
Expand Down
192 changes: 192 additions & 0 deletions src/transformers/other_transformers/polynomial_transformer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# # STRUCT AND CONSTRUCTORS

const WARN_DEGREE = "The `degree` must be at least 1. "*
"Reset `degree=2`. "


mutable struct PolynomialTransformer <: Static
degree::Int
features::Union{Nothing, Vector{Symbol}}
interactions_only::Bool
end

function MMI.clean!(model::PolynomialTransformer)
message = ""
if model.degree ≤ 0
model.degree = 2
message *= WARN_DEGREE
end
return message
end

function PolynomialTransformer(
; order=2,
degree=order,
features=nothing,
interactions_only=false,
)
model = PolynomialTransformer(degree, features, interactions_only)
message = MMI.clean!(model)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not throwing here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean! mutates the model parameter so that it is valid, so no need to throw an exception. Or am I missing the point of your question?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose I am wondering whether it would be better to explicitly throw if a user's code does not make sense rather than assuming a fallback to order=2?

isempty(message) || @warn message
return model
end


# # HELPERS

abstract type Selection end
struct WithRepetitions <: Selection end
struct WithoutRepetitions <: Selection end

"""
premonomials(alphabet, degree, kind_of_selection::Selection)

*Private method* to help generate monomials. A **pre-monomial** is a vector with elements
from the alphabet, with possible repetitions, but with no element predecessor coming
*after* the element itself in the alphabet.

Note degree one "pre-monomials" are excluded.

# Example

```julia-repl
julia> premonomials((:x, :y, :z), 3, WithoutRepetitions())
4-element Vector{Vector{Symbol}}:
[:x, :y]
[:x, :z]
[:y, :z]
[:x, :y, :z]

julia> premonomials((:x, :y, :z), 3, WithRepetitions())
16-element Vector{Vector{Symbol}}:
[:x, :x]
[:x, :y]
[:x, :z]
[:y, :y]
[:y, :z]
[:z, :z]
[:x, :x, :x]
[:x, :x, :y]
[:x, :x, :z]
[:x, :y, :y]
[:x, :y, :z]
[:x, :z, :z]
[:y, :y, :y]
[:y, :y, :z]
[:y, :z, :z]
[:z, :z, :z]
```
"""
premonomials(alphabet, degree, ::WithoutRepetitions) =
premonomials(alphabet, degree, Combinatorics.combinations)
premonomials(alphabet, degree, ::WithRepetitions) =
premonomials(alphabet, degree, Combinatorics.with_replacement_combinations)
premonomials(alphabet, degree, fnctn) =
collect(Iterators.flatten(fnctn(alphabet, i) for i in 2:degree))

column_product(columns, premonomial...) =
.*((Tables.getcolumn(columns, feature) for feature in premonomial)...)


# # CORE IMPLEMENTATION

function MMI.transform(model::PolynomialTransformer, _, X)
features = MLJTransforms.actualfeatures(model.features, X)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to keep the MLJTransforms prefix here and line 142?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary but helpful for maintenance: The method actualfeaures, while not defined in the current file, is defined in MLJTransforms somewhere, and not from the namespace of some other package. At least, this is helpful to me.

kind_of_selection = model.interactions_only ? WithoutRepetitions() : WithRepetitions()
premonomials = MLJTransforms.premonomials(features, model.degree, kind_of_selection)
new_features = Tuple(Symbol(join(premon, "_")) for premon in premonomials)
materializer = Tables.materializer(X)
columns = Tables.Columns(X)
table_addendum =
NamedTuple{new_features}(
[column_product(columns, premon...) for premon in premonomials],
)
return merge(Tables.columntable(X), table_addendum) |> materializer
end


# # TRAITS

metadata_model(PolynomialTransformer,
input_scitype = Tuple{Table},
output_scitype = Table,
human_name = "polynomial transformer",
load_path = "MLJTransforms.PolynomialTransformer")

# Package metadata for docstring generation
metadata_pkg(PolynomialTransformer,
package_name = "MLJTransforms",
package_uuid = "23777cdb-d90c-4eb0-a694-7c2b83d5c1d6",
package_url = "https://github.com/JuliaAI/MLJTransforms.jl",
is_pure_julia = true,
package_license = "MIT")

"""
$(MLJModelInterface.doc_header(PolynomialTransformer))

This `Static` transformer generates new features comprised of monomials in existing
features that have `Continuous` or `Count` scitype, up to some specified degree. A
restricted set of features may be specified, and one may elect to generate only
interaction monomials (no feature appearing with degree higher than one).

In MLJ or MLJBase, you can transform features `X` with the single call

transform(machine(model), X)

See also the example below.


# Hyper-parameters

- `degree=2`: maximum degree of monomials to be generated

- `features=nothing`: vector of features for which monomials should be generated; if
`nothing` (unspecified) then all `Continuous` and `Count` features are used.

# Operations

- `transform(machine(model), X)`: Generate a new table from `X` with the monomial columnn
specified by hyper-parameters.

# Example

```
using MLJ

X = (
A = [1, 2, 3],
B = [4, 5, 6],
C = [7, 8, 9],
D = ["cat", "dog", "rat"]
)

transformer = PolynomialTransformer(degree=2, features=[:A, :B])
mach = machine(transformer)
julia> transform(mach, X) |> pretty
┌───────┬───────┬───────┬─────────┬───────┬───────┬───────┐
│ A │ B │ C │ D │ A_A │ A_B │ B_B │
│ Int64 │ Int64 │ Int64 │ String │ Int64 │ Int64 │ Int64 │
│ Count │ Count │ Count │ Textual │ Count │ Count │ Count │
├───────┼───────┼───────┼─────────┼───────┼───────┼───────┤
│ 1 │ 4 │ 7 │ cat │ 1 │ 4 │ 16 │
│ 2 │ 5 │ 8 │ dog │ 4 │ 10 │ 25 │
│ 3 │ 6 │ 9 │ rat │ 9 │ 18 │ 36 │
└───────┴───────┴───────┴─────────┴───────┴───────┴───────┘

transformer = PolynomialTransformer(degree=3, interactions_only=true)
mach = machine(transformer)
julia> transform(mach, X) |> pretty
┌───────┬───────┬───────┬─────────┬───────┬───────┬───────┬───────┐
│ A │ B │ C │ D │ A_B │ A_C │ B_C │ A_B_C │
│ Int64 │ Int64 │ Int64 │ String │ Int64 │ Int64 │ Int64 │ Int64 │
│ Count │ Count │ Count │ Textual │ Count │ Count │ Count │ Count │
├───────┼───────┼───────┼─────────┼───────┼───────┼───────┼───────┤
│ 1 │ 4 │ 7 │ cat │ 4 │ 7 │ 28 │ 28 │
│ 2 │ 5 │ 8 │ dog │ 10 │ 16 │ 40 │ 80 │
│ 3 │ 6 │ 9 │ rat │ 18 │ 27 │ 54 │ 162 │
└───────┴───────┴───────┴─────────┴───────┴───────┴───────┴───────┘

```

"""
PolynomialTransformer
Loading
Loading