Skip to content
This repository has been archived by the owner on May 4, 2019. It is now read-only.

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
davidagold committed Sep 3, 2015
1 parent 33a8d1b commit e3d68ab
Showing 1 changed file with 100 additions and 2 deletions.
102 changes: 100 additions & 2 deletions README.md
Expand Up @@ -54,7 +54,7 @@ julia> NullableArray(Char, 3, 3)
Nullable{Char}() Nullable{Char}() Nullable{Char}()
```

One can also construct a `NullableArray` from a heterogeneous `Array` that uses a token object `x` to represent a missing value. For instance, if string `"NA"` represents a missing value in `[1, "NA", 2, 3, 5, "NA"]`, we can translate this pattern into a `NullableArray` object by passing to `NullableArray()` the latter `Array` object, the desired element type of the resultant `NullableArray` and the object that represents missingness in the first argument:
One can also construct a `NullableArray` from a heterogeneous `Array` that uses a token object `x` to represent a missing value. Suppose for instance that the string `"NA"` represents a missing value in `[1, "NA", 2, 3, 5, "NA"]`. One can translate this pattern into a `NullableArray` object by passing to the `NullableArray` constructor the `Array` object at hand, the desired element type of the resultant `NullableArray` and the object that represents missingness in the `Array` argument:
```julia
julia> NullableArray([1, "NA", 2, 3, 5, "NA"], Int, "NA")
6-element NullableArrays.NullableArray{Int64,1}:
Expand All @@ -66,8 +66,106 @@ julia> NullableArray(Char, 3, 3)
Nullable{Int64}()
```

Indexing
========
Indexing into a `NullableArray{T}` is just like indexing into a regular `Array{T}`, except that the returned object will always be of type `Nullable{T}` rather than type `T`. One can expect any indexing pattern that works on an `Array` to work on a `NullableArray`. This includes using a `NullableArray` to index into any container object that sufficiently implements the `AbstractArray` interface:
```julia
julia> A = [1:5...]
5-element Array{Int64,1}:
1
2
3
4
5

julia> X = NullableArray([2, 3])
2-element NullableArrays.NullableArray{Int64,1}:
Nullable(2)
Nullable(3)

julia> A[X]
2-element Array{Int64,1}:
2
3
```
Note, however, that attempting to index into any such `AbstractArray` with a null value will incur an error:
```julia
julia> Y = NullableArray([2, 3], [true, false])
2-element NullableArrays.NullableArray{Int64,1}:
Nullable{Int64}()
Nullable(3)

julia> A[Y]
ERROR: NullException()
in _checkbounds at /Users/David/.julia/v0.4/NullableArrays/src/indexing.jl:73
in getindex at abstractarray.jl:424
```

Working with `Nullable`s
========================
Using objects of type `Nullable{T}` to represent both present and missing values of type `T` may present an unfamiliar experience to users who have never encountered such specialized container types. This section of the documentation is devoted to explaining the dynamics of working with and illustrating common use patterns involving `Nullable` objects.

A central concern is how to extend methods originally defined for non-`Nullable` arguments to take `Nullable` arguments.

Suppose for instance that I have a method
```julia
f(x::Float64, y::Float64) = exp(x * y)
```

that I wish to `broadcast` over the two columns of `X::NullableArray{Float64, 2}`:
```julia
julia> X
10x2 NullableArrays.NullableArray{Float64,2}:
Nullable(0.5503097221926698) Nullable{Float64}()
Nullable{Float64}() Nullable{Float64}()
Nullable(0.7806818430935034) Nullable(0.4966357239169863)
Nullable{Float64}() Nullable(0.8819582596721911)
Nullable(0.04244733980209592) Nullable(0.8634601571530616)
Nullable{Float64}() Nullable(0.44256797964251904)
Nullable(0.3722788311682259) Nullable(0.26740280850120346)
Nullable(0.4591974149414313) Nullable{Float64}()
Nullable{Float64}() Nullable{Float64}()
Nullable(0.2485293010638474) Nullable(0.6661164389956584)
```
As one may expect, simply calling `broadcast(f, X[:,1], X[:,2])` incurs a `MethodError`:
```julia
julia> broadcast(f, X[:,1], X[:,2])
ERROR: MethodError: `f` has no method matching g(::Nullable{Float64}, ::Nullable{Float64})
in _F_ at broadcast.jl:80
in broadcast! at broadcast.jl:229
in broadcast at broadcast.jl:236
```
Let `v, w` be two `Nullable{Float64}` objects. If both `v, w` are non-null, the convention is to have `f(v, w)` return a similarly non-null `Nullable{Float64}` object whose `value` field agrees with `f(v.value, w.value)`. If either of `v, w` is null, the convention is to return an empty `Nullable{Float64}` object, i.e. to propogate the uncertainty introduced by the null argument. Providing a systematic means of extending `f` to `Nullable` arguments in such a way that satisfies the above behavior is sometimes called *lifting* `f` over `Nullable` arguments.

Arguably, the best way to lift existing methods over `Nullable` arguments is to use multiple dispatch. That is, one can very easily extend `f` to handle `Nullable{Float64}` arguments by simply defining an appropriate method:
```julia
function f(x::Nullable{Float64}, y::Nullable{Float64})
if x.isnull | y.isnull
return Nullable{Float64}()
else
return Nullable(f(x.value, y.value))
end
end
```
Now `broadcast` works as one would expect:
```julia
julia> broadcast(f, X[:,1], X[:,2])
10-element Array{Nullable{Float64},1}:
Nullable{Float64}()
Nullable{Float64}()
Nullable(1.4736089974970423)
Nullable{Float64}()
Nullable(1.037331537760899)
Nullable{Float64}()
Nullable(1.1046719410910564)
Nullable{Float64}()
Nullable{Float64}()
Nullable(1.1800413178724836)
```

The convention is not to support signatures of mixed `Nullable` and non `Nullable` arguments for solely the purposes of lifting. This reflects both conceptual concerns as well practical limitations -- in particular, to cover all possible combinations of `Nullable` and non-`Nullable` arguments for a signature of length N would require 2^N method definitions. If one finds that one is calling a function `f` on both `Nullable` and non-`Nullable` arguments, it is typically best to wrap the non-`Nullable` arguments into `Nullable` arguments and invoke the lifted version. Alternatively, one can instead pass their respective `value` fields to `f` -- **HOWEVER**, this approach is both less safe and less general and should only be used if one is certain that the `Nullable` arguments are non-null.

Implementation Details
`NullableArray` Implementation Details
======================
Under the hood of each `NullableArray{T, N}` object are two fields: a `values::Array{T, N}` field and an `isnull:Array{Bool, N}` field:
```julia
Expand Down

0 comments on commit e3d68ab

Please sign in to comment.