Skip to content

Commit 5251f03

Browse files
authored
Merge pull request #400 from KristofferC/kc/extensions
2 parents 197f0a0 + cbf26b0 commit 5251f03

6 files changed

+118
-60
lines changed

Project.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1111
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1212
Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
1313

14+
[weakdeps]
15+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
16+
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
17+
SentinelArrays = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
18+
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
19+
20+
[extensions]
21+
CategoricalArraysJSONExt = "JSON"
22+
CategoricalArraysRecipesBaseExt = "RecipesBase"
23+
CategoricalArraysSentinelArraysExt = "SentinelArrays"
24+
CategoricalArraysStructTypesExt = "StructTypes"
25+
1426
[compat]
1527
DataAPI = "1.6"
1628
JSON = "0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21"

ext/CategoricalArraysJSONExt.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module CategoricalArraysJSONExt
2+
3+
if isdefined(Base, :get_extension)
4+
using CategoricalArrays
5+
using JSON
6+
else
7+
using ..CategoricalArrays
8+
using ..JSON
9+
end
10+
11+
# JSON of CategoricalValue is JSON of the value it refers to
12+
JSON.lower(x::CategoricalValue) = JSON.lower(unwrap(x))
13+
14+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module CategoricalArraysRecipesBaseExt
2+
3+
if isdefined(Base, :get_extension)
4+
using CategoricalArrays
5+
using RecipesBase
6+
else
7+
using ..CategoricalArrays
8+
using ..RecipesBase
9+
end
10+
11+
RecipesBase.@recipe function f(::Type{T}, v::T) where T <: CategoricalValue
12+
level_strings = [map(string, levels(v)); missing]
13+
ticks --> eachindex(level_strings)
14+
v -> ismissing(v) ? length(level_strings) : Int(CategoricalArrays.refcode(v)),
15+
i -> level_strings[Int(i)]
16+
end
17+
18+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module CategoricalArraysSentinelArraysExt
2+
3+
if isdefined(Base, :get_extension)
4+
using CategoricalArrays
5+
using SentinelArrays
6+
else
7+
using ..CategoricalArrays
8+
using ..SentinelArrays
9+
end
10+
11+
Base.copyto!(dest::CategoricalArrays.CatArrOrSub{<:Any, 1}, src::SentinelArrays.ChainedVector) =
12+
copyto!(dest, 1, src, 1, length(src))
13+
Base.copyto!(dest::CategoricalArrays.CatArrOrSub{<:Any, 1}, dstart::Union{Signed, Unsigned},
14+
src::SentinelArrays.ChainedVector, sstart::Union{Signed, Unsigned},
15+
n::Union{Signed, Unsigned}) =
16+
invoke(copyto!, Tuple{AbstractArray, Union{Signed, Unsigned},
17+
SentinelArrays.ChainedVector,
18+
Union{Signed, Unsigned}, Union{Signed, Unsigned}},
19+
dest, dstart, src, sstart, n)
20+
21+
end
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module CategoricalArraysStructTypesExt
2+
3+
if isdefined(Base, :get_extension)
4+
using CategoricalArrays
5+
using StructTypes
6+
else
7+
using ..CategoricalArrays
8+
using ..StructTypes
9+
end
10+
11+
# define appropriate handlers for JSON3 interface
12+
StructTypes.StructType(x::CategoricalValue) = StructTypes.StructType(unwrap(x))
13+
StructTypes.StructType(::Type{<:CategoricalValue{T}}) where {T} = StructTypes.StructType(T)
14+
StructTypes.numbertype(::Type{<:CategoricalValue{T}}) where {T <: Number} = T
15+
StructTypes.construct(::Type{T}, x::CategoricalValue{T}) where {T} = T(unwrap(x))
16+
17+
# JSON3 writing/reading
18+
StructTypes.StructType(::Type{<:CategoricalVector}) = StructTypes.ArrayType()
19+
20+
StructTypes.construct(::Type{<:CategoricalArray}, A::AbstractVector) =
21+
constructgeneral(A)
22+
StructTypes.construct(::Type{<:CategoricalArray}, A::Vector) =
23+
constructgeneral(A)
24+
25+
function constructgeneral(A)
26+
if eltype(A) === Any
27+
# unlike `replace`, broadcast narrows the type, which allows us to return small
28+
# union eltypes (e.g. Union{String,Missing})
29+
categorical(ifelse.(A .=== nothing, missing, A))
30+
elseif eltype(A) >: Nothing
31+
categorical(replace(A, nothing=>missing))
32+
else
33+
categorical(A)
34+
end
35+
end
36+
37+
StructTypes.construct(::Type{<:CategoricalArray{Union{Missing, T}}},
38+
A::AbstractVector) where {T} =
39+
CategoricalArray{Union{Missing, T}}(replace(A, nothing=>missing))
40+
StructTypes.construct(::Type{<:CategoricalArray{Union{Missing, T}}},
41+
A::Vector) where {T} =
42+
CategoricalArray{Union{Missing, T}}(replace(A, nothing=>missing))
43+
44+
end

src/CategoricalArrays.jl

Lines changed: 9 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ module CategoricalArrays
1414
using DataAPI
1515
using Missings
1616
using Printf
17-
using Requires: @require
1817

1918
# JuliaLang/julia#36810
2019
if VERSION < v"1.5.2"
@@ -35,66 +34,16 @@ module CategoricalArrays
3534

3635
include("deprecated.jl")
3736

38-
function __init__()
39-
@require JSON="682c06a0-de6a-54ab-a142-c8b1cf79cde6" begin
40-
# JSON of CategoricalValue is JSON of the value it refers to
41-
JSON.lower(x::CategoricalValue) = JSON.lower(unwrap(x))
42-
end
43-
44-
@require RecipesBase="3cdcf5f2-1ef4-517c-9805-6587b60abb01" @eval begin
45-
RecipesBase.@recipe function f(::Type{T}, v::T) where T <: CategoricalValue
46-
level_strings = [map(string, levels(v)); missing]
47-
ticks --> eachindex(level_strings)
48-
v -> ismissing(v) ? length(level_strings) : Int(refcode(v)),
49-
i -> level_strings[Int(i)]
50-
end
51-
end
52-
53-
@require SentinelArrays="91c51154-3ec4-41a3-a24f-3f23e20d615c" begin
54-
copyto!(dest::CatArrOrSub{<:Any, 1}, src::SentinelArrays.ChainedVector) =
55-
copyto!(dest, 1, src, 1, length(src))
56-
copyto!(dest::CatArrOrSub{<:Any, 1}, dstart::Union{Signed, Unsigned},
57-
src::SentinelArrays.ChainedVector, sstart::Union{Signed, Unsigned},
58-
n::Union{Signed, Unsigned}) =
59-
invoke(copyto!, Tuple{AbstractArray, Union{Signed, Unsigned},
60-
SentinelArrays.ChainedVector,
61-
Union{Signed, Unsigned}, Union{Signed, Unsigned}},
62-
dest, dstart, src, sstart, n)
63-
end
64-
65-
@require StructTypes="856f2bd8-1eba-4b0a-8007-ebc267875bd4" begin
66-
# define appropriate handlers for JSON3 interface
67-
StructTypes.StructType(x::CategoricalValue) = StructTypes.StructType(unwrap(x))
68-
StructTypes.StructType(::Type{<:CategoricalValue{T}}) where {T} = StructTypes.StructType(T)
69-
StructTypes.numbertype(::Type{<:CategoricalValue{T}}) where {T <: Number} = T
70-
StructTypes.construct(::Type{T}, x::CategoricalValue{T}) where {T} = T(unwrap(x))
71-
72-
# JSON3 writing/reading
73-
StructTypes.StructType(::Type{<:CategoricalVector}) = StructTypes.ArrayType()
74-
75-
StructTypes.construct(::Type{<:CategoricalArray}, A::AbstractVector) =
76-
constructgeneral(A)
77-
StructTypes.construct(::Type{<:CategoricalArray}, A::Vector) =
78-
constructgeneral(A)
79-
80-
function constructgeneral(A)
81-
if eltype(A) === Any
82-
# unlike `replace`, broadcast narrows the type, which allows us to return small
83-
# union eltypes (e.g. Union{String,Missing})
84-
categorical(ifelse.(A .=== nothing, missing, A))
85-
elseif eltype(A) >: Nothing
86-
categorical(replace(A, nothing=>missing))
87-
else
88-
categorical(A)
89-
end
90-
end
37+
if !isdefined(Base, :get_extension)
38+
using Requires: @require
39+
end
9140

92-
StructTypes.construct(::Type{<:CategoricalArray{Union{Missing, T}}},
93-
A::AbstractVector) where {T} =
94-
CategoricalArray{Union{Missing, T}}(replace(A, nothing=>missing))
95-
StructTypes.construct(::Type{<:CategoricalArray{Union{Missing, T}}},
96-
A::Vector) where {T} =
97-
CategoricalArray{Union{Missing, T}}(replace(A, nothing=>missing))
41+
@static if !isdefined(Base, :get_extension)
42+
function __init__()
43+
@require JSON="682c06a0-de6a-54ab-a142-c8b1cf79cde6" include("../ext/CategoricalArraysJSONExt.jl")
44+
@require RecipesBase="3cdcf5f2-1ef4-517c-9805-6587b60abb01" include("../ext/CategoricalArraysRecipesBaseExt.jl")
45+
@require SentinelArrays="91c51154-3ec4-41a3-a24f-3f23e20d615c" include("../ext/CategoricalArraysSentinelArraysExt.jl")
46+
@require StructTypes="856f2bd8-1eba-4b0a-8007-ebc267875bd4" include("../ext/CategoricalArraysStructTypesExt.jl")
9847
end
9948
end
10049
end

0 commit comments

Comments
 (0)