Skip to content

Commit

Permalink
Merge pull request #24 from evanfields/ef_update_07
Browse files Browse the repository at this point in the history
Ef update 0.7
  • Loading branch information
tlnagy authored Jul 18, 2018
2 parents bcc8f1a + 60ae38e commit acf298f
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 52 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ os:
- linux
- osx
julia:
- 0.5
- 0.6
- 0.7
- nightly
notifications:
email: false
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Loess

[![Build Status](https://travis-ci.org/JuliaStats/Loess.jl.svg?branch=master)](https://travis-ci.org/JuliaStats/Loess.jl)
[![Loess](http://pkg.julialang.org/badges/Loess_0.5.svg)](http://pkg.julialang.org/?pkg=Loess)
[![Loess](http://pkg.julialang.org/badges/Loess_0.6.svg)](http://pkg.julialang.org/?pkg=Loess)
[![Loess](http://pkg.julialang.org/badges/Loess_0.7.svg)](http://pkg.julialang.org/?pkg=Loess)

This is a pure Julia loess implementation, based on the fast kd-tree based
approximation described in the original Cleveland, et al papers, implemented
Expand Down
4 changes: 1 addition & 3 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
julia 0.5
Compat 0.17
julia 0.7.0-beta2
Distances
IterTools
40 changes: 20 additions & 20 deletions src/Loess.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
isdefined(Base, :__precompile__) && __precompile__()
__precompile__()

module Loess

using Compat
module Loess

import IterTools.product
import Distances.euclidean

using Statistics

export loess, predict

include("kd.jl")


type LoessModel{T <: AbstractFloat}
mutable struct LoessModel{T <: AbstractFloat}
xs::AbstractMatrix{T} # An n by m predictor matrix containing n observations from m predictors
ys::AbstractVector{T} # A length n response vector
bs::Matrix{T} # Least squares coefficients
Expand All @@ -37,8 +37,8 @@ Returns:
A fit `LoessModel`.
"""
function loess{T <: AbstractFloat}(xs::AbstractMatrix{T}, ys::AbstractVector{T};
normalize::Bool=true, span::T=0.75, degree::Int=2)
function loess(xs::AbstractMatrix{T}, ys::AbstractVector{T};
normalize::Bool=true, span::T=0.75, degree::Int=2) where T <: AbstractFloat
if size(xs, 1) != size(ys, 1)
error("Predictor and response arrays must of the same length")
end
Expand All @@ -54,7 +54,7 @@ function loess{T <: AbstractFloat}(xs::AbstractMatrix{T}, ys::AbstractVector{T};
end

kdtree = KDTree(xs, 0.05 * span)
verts = Array{T}(length(kdtree.verts), m)
verts = Array{T}(undef, length(kdtree.verts), m)

# map verticies to their index in the bs coefficient matrix
verts = Dict{Vector{T}, Int}()
Expand All @@ -63,13 +63,13 @@ function loess{T <: AbstractFloat}(xs::AbstractMatrix{T}, ys::AbstractVector{T};
end

# Fit each vertex
ds = Array{T}(n) # distances
ds = Array{T}(undef, n) # distances
perm = collect(1:n)
bs = Array{T}(length(kdtree.verts), 1 + degree * m)
bs = Array{T}(undef, length(kdtree.verts), 1 + degree * m)

# TODO: higher degree fitting
us = Array{T}(q, 1 + degree * m)
vs = Array{T}(q)
us = Array{T}(undef, q, 1 + degree * m)
vs = Array{T}(undef, q)
for (vert, k) in verts
# reset perm
for i in 1:n
Expand All @@ -82,7 +82,7 @@ function loess{T <: AbstractFloat}(xs::AbstractMatrix{T}, ys::AbstractVector{T};
end

# copy the q nearest points to vert into X
select!(perm, q, by=i -> ds[i])
partialsort!(perm, q, by=i -> ds[i])
dmax = maximum([ds[perm[i]] for i = 1:q])

for i in 1:q
Expand All @@ -105,8 +105,8 @@ function loess{T <: AbstractFloat}(xs::AbstractMatrix{T}, ys::AbstractVector{T};
LoessModel{T}(xs, ys, bs, verts, kdtree)
end

function loess{T <: AbstractFloat}(xs::AbstractVector{T}, ys::AbstractVector{T};
normalize::Bool=true, span::T=0.75, degree::Int=2)
function loess(xs::AbstractVector{T}, ys::AbstractVector{T};
normalize::Bool=true, span::T=0.75, degree::Int=2) where T <: AbstractFloat
loess(reshape(xs, (length(xs), 1)), ys, normalize=normalize, span=span, degree=degree)
end

Expand All @@ -125,12 +125,12 @@ end
# Returns:
# A length n' vector of predicted response values.
#
function predict{T <: AbstractFloat}(model::LoessModel{T}, z::T)
function predict(model::LoessModel{T}, z::T) where T <: AbstractFloat
predict(model, T[z])
end


function predict{T <: AbstractFloat}(model::LoessModel{T}, zs::AbstractVector{T})
function predict(model::LoessModel{T}, zs::AbstractVector{T}) where T <: AbstractFloat
m = size(model.xs, 2)

# in the univariate case, interpret a non-singleton zs as vector of
Expand Down Expand Up @@ -163,8 +163,8 @@ function predict{T <: AbstractFloat}(model::LoessModel{T}, zs::AbstractVector{T}
end


function predict{T <: AbstractFloat}(model::LoessModel{T}, zs::AbstractMatrix{T})
ys = Array{T}(size(zs, 1))
function predict(model::LoessModel{T}, zs::AbstractMatrix{T}) where T <: AbstractFloat
ys = Array{T}(undef, size(zs, 1))
for i in 1:size(zs, 1)
# the vec() here is not necessary on 0.5 anymore
ys[i] = predict(model, vec(zs[i,:]))
Expand Down Expand Up @@ -226,7 +226,7 @@ Args:
Modifies:
`xs`
"""
function tnormalize!{T <: AbstractFloat}(xs::AbstractMatrix{T}, q::T=0.1)
function tnormalize!(xs::AbstractMatrix{T}, q::T=0.1) where T <: AbstractFloat
n, m = size(xs)
cut = ceil(Int, (q * n))
for j in 1:m
Expand Down
45 changes: 21 additions & 24 deletions src/kd.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
using Compat
import Compat.view

# Simple static kd-trees.

@compat abstract type KDNode end
abstract type KDNode end

immutable KDLeafNode <: KDNode
struct KDLeafNode <: KDNode
end

immutable KDInternalNode{T <: AbstractFloat} <: KDNode
struct KDInternalNode{T <: AbstractFloat} <: KDNode
j::Int # dimension on which the data is split
med::T # median value where the split occours
leftnode::KDNode
rightnode::KDNode
end


immutable KDTree{T <: AbstractFloat}
struct KDTree{T <: AbstractFloat}
xs::AbstractMatrix{T} # A matrix of n, m-dimensional observations
perm::Vector{Int} # permutation of data to avoid modifying xs
root::KDNode # root node
Expand All @@ -42,14 +39,14 @@ Returns:
A `KDTree` object
"""
function KDTree{T <: AbstractFloat}(xs::AbstractMatrix{T},
leaf_size_factor=0.05,
leaf_diameter_factor=0.0)
function KDTree(xs::AbstractMatrix{T},
leaf_size_factor=0.05,
leaf_diameter_factor=0.0) where T <: AbstractFloat

n, m = size(xs)
perm = collect(1:n)

bounds = Array{T}(2, m)
bounds = Array{T}(undef, 2, m)
for j in 1:m
col = xs[:,j]
bounds[1, j] = minimum(col)
Expand All @@ -63,7 +60,7 @@ function KDTree{T <: AbstractFloat}(xs::AbstractMatrix{T},
verts = Set{Vector{T}}()

# Add a vertex for each corner of the hypercube
for vert in product([bounds[:,j] for j in 1:m]...)
for vert in Iterators.product([bounds[:,j] for j in 1:m]...)
push!(verts, T[vert...])
end

Expand Down Expand Up @@ -116,12 +113,12 @@ Modifies:
Returns:
Either a `KDLeafNode` or a `KDInternalNode`
"""
function build_kdtree{T}(xs::AbstractMatrix{T},
perm::AbstractArray,
bounds::Matrix{T},
leaf_size_cutoff::Int,
leaf_diameter_cutoff::T,
verts::Set{Vector{T}})
function build_kdtree(xs::AbstractMatrix{T},
perm::AbstractArray,
bounds::Matrix{T},
leaf_size_cutoff::Int,
leaf_diameter_cutoff::T,
verts::Set{Vector{T}}) where T
n, m = size(xs)

if length(perm) <= leaf_size_cutoff || diameter(bounds) <= leaf_diameter_cutoff
Expand All @@ -148,14 +145,14 @@ function build_kdtree{T}(xs::AbstractMatrix{T},
# find the median and partition
if isodd(length(perm))
mid = length(perm) ÷ 2
select!(perm, mid, by=i -> xs[i, j])
partialsort!(perm, mid, by=i -> xs[i, j])
med = xs[perm[mid], j]
mid1 = mid
mid2 = mid + 1
else
mid1 = length(perm) ÷ 2
mid2 = mid1 + 1
select!(perm, mid1:mid2, by=i -> xs[i, j])
partialsort!(perm, mid1:mid2, by=i -> xs[i, j])
med = (xs[perm[mid1], j] + xs[perm[mid2], j]) / 2
end

Expand All @@ -169,7 +166,7 @@ function build_kdtree{T}(xs::AbstractMatrix{T},
rightnode = build_kdtree(xs, view(perm,mid2:length(perm)), rightbounds,
leaf_size_cutoff, leaf_diameter_cutoff, verts)

coords = Array{Array}(m)
coords = Array{Array}(undef, m)
for i in 1:m
if i == j
coords[i] = [med]
Expand All @@ -178,7 +175,7 @@ function build_kdtree{T}(xs::AbstractMatrix{T},
end
end

for vert in product(coords...)
for vert in Iterators.product(coords...)
push!(verts, T[vert...])
end

Expand All @@ -192,7 +189,7 @@ end
Given a bounding hypecube `bounds`, return its verticies
"""
function bounds_verts(bounds::Matrix)
collect(product([bounds[:, i] for i in 1:size(bounds, 2)]...))
collect(Iterators.product([bounds[:, i] for i in 1:size(bounds, 2)]...))
end


Expand All @@ -202,7 +199,7 @@ end
Traverse the tree `kdtree` to the bottom and return the verticies of
the bounding hypercube of the leaf node containing the point `x`.
"""
function traverse{T}(kdtree::KDTree{T}, x::AbstractVector{T})
function traverse(kdtree::KDTree{T}, x::AbstractVector{T}) where T
m = size(kdtree.bounds, 2)

if length(x) != m
Expand Down
5 changes: 3 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# These tests don't do much except ensure that the package loads,
# and does something sensible if it does.
using Loess
using Base.Test
using Compat
using Test
using Random
using Statistics

srand(100)
xs = 10 .* rand(100)
Expand Down

0 comments on commit acf298f

Please sign in to comment.