diff --git a/.gitignore b/.gitignore
index e69de29bb..cd67eac66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+.Rproj.user
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..445857fb1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,30 @@
+language: r
+warnings_are_errors: true
+
+before_install:
+ - cd ./ranger-r-package/ranger
+ - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test -y
+ - sudo apt-get -qq update
+ - sudo apt-get -qq install gcc-4.8
+ - sudo apt-get -qq install g++-4.8
+ - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90
+ - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
+ - sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-4.8 90
+
+r:
+ - oldrel
+ - release
+ - devel
+
+r_github_packages:
+ - jimhester/covr
+
+after_success:
+ - Rscript -e 'covr::codecov(quiet = FALSE)'
+
+notifications:
+ email:
+ recipients:
+ - work@wrig.de
+ on_success: change
+ on_failure: change
\ No newline at end of file
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 000000000..ea9ce727d
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,101 @@
+##### Version 0.3.8
+* Remove tuning functions, please use mlr or caret.
+
+##### Version 0.3.7
+* Fix bug with alternative interface and prediction
+* Small fixes
+
+##### Version 0.3.6
+* Add keep.inbag option to track in-bag counts
+* Add option sample.fraction for fraction of sampled observations
+
+##### Version 0.3.5
+* Add tree-wise split.select.weights
+
+##### Version 0.3.4
+* Add predict.all option in predict() to get individual predictions for each tree for classification and regression
+* Small changes in documentation
+
+##### Version 0.3.3
+* Add case-specific random forests
+
+##### Version 0.3.2
+* Add case weights (weighted bootstrapping or subsampling)
+
+##### Version 0.3.1
+* Catch error of outdated gcc not supporting C++11 completely
+
+##### Version 0.3.0
+* Allow the user to interrupt computation from R
+* Transpose classification.table and rename to confusion.matrix
+* Respect R seed for prediction
+* Memory improvements for variable importance computation
+* Fix bug: Probability prediction for single observations
+* Fix bug: Results not identical when using alternative interface
+
+##### Version 0.2.7
+* Small fixes for Solaris compiler
+
+##### Version 0.2.6
+* Add C-index splitting
+* Fix NA SNP handling
+
+##### Version 0.2.5
+* Fix matrix and gwaa alternative survival interface
+* Version submitted to JSS
+
+##### Version 0.2.4
+* Small changes in documentation
+
+##### Version 0.2.3
+* Preallocate memory for splitting
+
+##### Version 0.2.2
+* Remove recursive splitting
+
+##### Version 0.2.1
+* Allow matrix as input data in R version
+
+##### Version 0.2.0
+* Fix prediction of classification forests in R
+
+##### Version 0.1.9
+* Speedup growing for continuous covariates
+* Add memory save option to save memory for very large datasets (but slower)
+* Remove memory mode option from R version since no performance gain
+
+##### Version 0.1.8
+* Fix problems when using Rcpp <0.11.4
+
+##### Version 0.1.7
+* Add option to split on unordered categorical covariates
+
+##### Version 0.1.6
+* Optimize memory management for very large survival forests
+
+##### Version 0.1.5
+* Set required Rcpp version to 0.11.2
+* Fix large $call objects when using BatchJobs
+* Add details and example on GenABEL usage to documentation
+* Minor changes to documentation
+
+##### Version 0.1.4
+* Speedup for survival forests with continuous covariates
+* R version: Generate seed from R. It is no longer necessary to set the
+ seed argument in ranger calls.
+
+##### Version 0.1.3
+* Windows support for R version (without multithreading)
+
+##### Version 0.1.2
+* Speedup growing of regression and probability prediction forests
+* Prediction forests are now handled like regression forests: MSE used for
+ prediction error and permutation importance
+* Fixed name conflict with randomForest package for "importance"
+* Fixed a bug: prediction function is now working for probability
+ prediction forests
+* Slot "predictions" for probability forests now contains class probabilities
+* importance function is now working even if randomForest package is
+ loaded after ranger
+* Fixed a bug: Split selection weights are now working as expected
+* Small changes in documentation
diff --git a/README b/README
deleted file mode 100644
index a8c8208c4..000000000
--- a/README
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-Ranger 0.1.0
-
-Written by:
-
- Marvin N. Wright
- Institut für Medizinische Biometrie und Statistik
- Universität zu Lübeck
- Ratzeburger Allee 160
- 23562 Lübeck
-
- http://www.imbs-luebeck.de
- wright@imbs.uni-luebeck.de
-
-1. Introduction
-
- Ranger is a fast implementation of Random Forest (Breiman 2001) or recursive partitioning, particularly suited for high dimensional data. Classification, Regression, and Survival forests are supported. Classification and Regression forests are implemented as in the original Random Forest (Breiman 2001), Survival forests as in Random Survival Forests (Ishwaran et al. 2008).
-
- Ranger is written in C++, but a version for R is avaiable, too. We recommend to use the R version. It is easy to install and use and the results are readily available for further analysis. The R version is as fast as the pure C++ version.
-
-2. Installation
-
- To install the Ranger R package from the provided archive just run
-
- install.packages("ranger_X.Y.tar.gz")
-
- in R from the directory where the downloaded archive is or give the full path. If any dependencies are missing (i.e. Rcpp), please install them from CRAN. R version >= 3.1 is required. Note that for now the R package cannot be compiled from source in Microsoft Windows (because the compiler in RTools is too old).
-
- To install the C++ version of Ranger in Linux or Mac OS X you will need a compiler supporting C++11 (i.e. gcc >= 4.7 or Clang >= 3.0) and Cmake. To build start a terminal from the Ranger main directory and run the following commands
-
- cd source
- mkdir build
- cd build
- cmake ..
- make
-
- After compilation there should be an executable called "ranger" in the current directory.
-
- To run Ranger in Microsoft Windows please use one of the provided binaries.
-
-3. Usage
-
- For usage of the R version see ?ranger in R. As a first example you could try ranger(Species ~ ., data = iris).
-
- In the C++ version type
-
- ranger --help
-
- for a list of commands. First you need a training dataset in a file. This file should contain one header line with variable names and one line with variable values per sample. Variable names must not contain any whitespace, comma or semicolon. Values can be seperated by whitespace, comma or semicolon but can not be mixed in one file. A typical call of Ranger would be for example
-
- ranger --verbose --file data.dat --depvarname Species --treetype 1 --ntree 1000 --nthreads 4
-
- If you find any bugs, or if you experience any crashes, please report to us. If you have any questions just ask, we won't bite.
-
-References
-
- Breiman, L. (2001). Random forests. Machine learning, 45(1), 5-32.
- Ishwaran, H., Kogalur, U. B., Blackstone, E. H., & Lauer, M. S. (2008). Random survival forests. The Annals of Applied Statistics, 841-860.
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..eae55ae94
--- /dev/null
+++ b/README.md
@@ -0,0 +1,60 @@
+[](https://travis-ci.org/imbs-hl/ranger)
+[](https://codecov.io/github/imbs-hl/ranger?branch=master)
+
+
+## ranger: A Fast Implementation of Random Forests
+Marvin N. Wright, wright@imbs.uni-luebeck.de
+
+### Introduction
+Ranger is a fast implementation of random forest (Breiman 2001) or recursive partitioning, particularly suited for high dimensional data. Classification, regression, probability estimation and survival forests are supported. Classification and regression forests are implemented as in the original Random Forest (Breiman 2001), survival forests as in Random Survival Forests (Ishwaran et al. 2008). For probability estimation forests see Malley et al. (2012).
+
+Ranger is written in C++, but a version for R is available, too. We recommend to use the R version. It is easy to install and use and the results are readily available for further analysis. The R version is as fast as the pure C++ version.
+
+### Installation
+To install the Ranger R package from CRAN, just run
+
+```R
+install.packages("ranger”)
+```
+
+R version >= 3.1 is required. Note that, for now, no multithreading is supported in the R version on Windows platforms (the compiler in RTools is too old).
+
+To install the C++ version of Ranger in Linux or Mac OS X you will need a compiler supporting C++11 (i.e. gcc >= 4.7 or Clang >= 3.0) and Cmake. To build start a terminal from the Ranger main directory and run the following commands
+
+```bash
+cd source
+mkdir build
+cd build
+cmake ..
+make
+```
+
+After compilation there should be an executable called "ranger" in the build directory.
+
+To run the C++ version in Microsoft Windows please cross compile or ask for a binary.
+
+### Usage
+For usage of the R version see ?ranger in R. Most importantly, see the Examples section. As a first example you could try
+
+```R
+ranger(Species ~ ., data = iris)
+```
+
+In the C++ version type
+
+```bash
+ranger --help
+```
+
+for a list of commands. First you need a training dataset in a file. This file should contain one header line with variable names and one line with variable values per sample. Variable names must not contain any whitespace, comma or semicolon. Values can be seperated by whitespace, comma or semicolon but can not be mixed in one file. A typical call of Ranger would be for example
+
+```bash
+ranger --verbose --file data.dat --depvarname Species --treetype 1 --ntree 1000 --nthreads 4
+```
+
+If you find any bugs, or if you experience any crashes, please report to us. If you have any questions just ask, we won't bite.
+
+### References
+* Breiman, L. (2001). Random forests. Machine learning, 45(1), 5-32.
+* Ishwaran, H., Kogalur, U. B., Blackstone, E. H., & Lauer, M. S. (2008). Random survival forests. The Annals of Applied Statistics, 841-860.
+* Malley, J. D., Kruppa, J., Dasgupta, A., Malley, K. G., & Ziegler, A. (2012). Probability machines: consistent probability estimation using nonparametric learning machines. Methods Inf Med, 51(1), 74.
diff --git a/R_package/.gitignore b/R_package/.gitignore
deleted file mode 100644
index 45798cd2c..000000000
--- a/R_package/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-ranger/*
-ranger_*.tar.gz
-ranger.Rcheck/*
diff --git a/R_package/Makevars b/R_package/Makevars
deleted file mode 100644
index 3d174ea9c..000000000
--- a/R_package/Makevars
+++ /dev/null
@@ -1,2 +0,0 @@
-## Use c++11
-CXX_STD = CXX11
\ No newline at end of file
diff --git a/R_package/create_package.R b/R_package/create_package.R
deleted file mode 100644
index 8c5aabe57..000000000
--- a/R_package/create_package.R
+++ /dev/null
@@ -1,75 +0,0 @@
-
-## Package infos
-package.name <- "ranger"
-package.title <- "Ranger"
-package.description <- paste("Ranger is a fast implementation of Random Forest (tm),",
- "particularly suited for high dimensional data.",
- "For now, ensembles of classification, regression and survival trees are supported.",
- "With Ranger data from Genome-wide association (GWA) studies can be analyzed efficiently.",
- "In addition to data frames, datasets of class 'gwaa.data' (R package GenABEL) can be directly analyzed.")
-package.depends <- "R (>= 3.1)"
-package.suggests <- "survival"
-
-package.author <- "Marvin N. Wright"
-package.email <- "wright@imbs.uni-luebeck.de"
-package.license <- "GPL-3"
-
-## Add C++ and R files
-setwd("~/myWork/ranger/R_package")
-src.file <- c("Makevars", "Makevars.win", "rangerCpp.cpp",
- list.files("../source/src", recursive = TRUE, full.names = TRUE))
-src.file <- src.file[src.file != ("../source/src/main.cpp") &
- src.file != ("../source/src/utility/ArgumentHandler.cpp") &
- src.file != ("../source/src/utility/ArgumentHandler.h")]
-code.file <- c("ranger.R", "predict.R", "print.R", "importance.R", "predictions.R", "timepoints.R")
-
-## Get version
-package.version <- scan("../source/src/version.h", character(0))[5]
-
-## Cleanup
-remove.packages(package.name)
-unlink(file.path(package.name, ""), recursive=TRUE)
-unlink(paste(package.name, "_", package.version, ".tar.gz", sep = ""))
-unlink(paste(package.name, ".Rcheck/", sep = ""), recursive = TRUE)
-
-## Create package
-library(Rcpp)
-Rcpp.package.skeleton(package.name, attributes = TRUE, force = TRUE,
- cpp_files = src.file, code_files = code.file,
- example_code = FALSE, author = package.author,
- email = package.email,
- license = package.license)
-compileAttributes(file.path(".", package.name, ""))
-unlink(file.path(package.name, "man", paste(package.name, "-package.Rd", sep = "")))
-
-## Change DESCRIPTION file
-dcf.file <- file.path(".", package.name, "DESCRIPTION")
-dcf <- read.dcf(dcf.file)
-dcf[1, "Title"] <- package.title
-dcf[1, "Description"] <- package.description
-dcf[1, "Version"] <- package.version
-dcf.new <- cbind(cbind(dcf, "Depends" = package.depends), "Suggests" = package.suggests)
-write.dcf(dcf.new, dcf.file)
-
-## Add Documentation
-library(roxygen2)
-roxygenize(package.name)
-
-## Update NAMESPACE file
-namespace.file <- file.path(".", package.name, "NAMESPACE")
-cat(paste("useDynLib(", package.name, ")\n", sep = ""), file = namespace.file, append = TRUE)
-cat("importFrom(Rcpp, evalCpp)\n", file = namespace.file, append = TRUE)
-
-## Build package
-system(paste("R CMD build", package.name))
-
-## Check
-##system(paste("R CMD check", package.name))
-
-## Install and load package
-install.packages(paste(package.name, "_", package.version, ".tar.gz", sep = ""), repos = NULL)
-library(package.name, character.only = TRUE)
-
-## Test
-library(testthat)
-test_dir("test/", reporter = "summary")
diff --git a/R_package/ranger.R b/R_package/ranger.R
deleted file mode 100644
index 5100038b5..000000000
--- a/R_package/ranger.R
+++ /dev/null
@@ -1,389 +0,0 @@
-# -------------------------------------------------------------------------------
-# This file is part of Ranger.
-#
-# Ranger is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ranger is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ranger. If not, see .
-#
-# Written by:
-#
-# Marvin N. Wright
-# Institut für Medizinische Biometrie und Statistik
-# Universität zu Lübeck
-# Ratzeburger Allee 160
-# 23562 Lübeck
-#
-# http://www.imbs-luebeck.de
-# wright@imbs.uni-luebeck.de
-# -------------------------------------------------------------------------------
-
-##' Ranger is a fast implementation of Random Forest (Breiman 2001) or recursive partitioning, particularly suited for high dimensional data.
-##' Classification, Regression, and Survival forests are supported.
-##' Classification and Regression forests are implemented as in the original Random Forest (Breiman 2001), Survival forests as in Random Survival Forests (Ishwaran et al. 2008).
-##'
-##' The tree type is determined by the type of the dependent variable.
-##' For factors Classification trees are grown, for numeric values Regression trees and for survival objects Survival trees.
-##' The Gini index is used as splitting rule for Classification and the estimated response variances for Regression.
-##' For Survival the log-rank test or an AUC-based splitting rule are available.
-##'
-##' With the \code{probability} option and factor dependent variable a Probability forest is grown.
-##' Here, the estimated response variances are used for splitting, as in Regression forests.
-##' In contrast, the prediction error is measured with missclassifications as in Classification forests.
-##' Predictions are class probabilities for each sample.
-##' For details see Malley et al. (2012).
-##'
-##' Note that for Classification and Regression nodes with size smaller than min.node.size can occur, like in original Random Forest.
-##' For Survival all nodes contain at least min.node.size samples. Variables selected with always.split.variables are tried additionaly to the mtry variables randomly selected.
-##'
-##' For a large number of variables and data frame as input data the formula interface can be slow.
-##' Alternatively dependent.variable.name (and status.variable.name for Survival) can be used.
-##'
-##' @title Ranger
-##' @param formula Object of class \code{formula} or \code{character} describing the model to fit.
-##' @param data Training data of class \code{data.frame} or \code{gwaa.data} (GenABEL).
-##' @param num.trees Number of trees.
-##' @param mtry Number of variables to possibly split at in each node.
-##' @param importance Variable importance mode, one of 'none', 'impurity', 'permutation'. The 'impurity' measure is the Gini index for Classification and the variance of the responses for Regression.
-##' @param write.forest Save \code{ranger.forest} object, needed for prediction.
-##' @param probability Grow a Probability forest. This is a Classification forest which returns class probabilities instead of classifications.
-##' @param min.node.size Minimal node size. Default 1 for Classification, 5 for Regression, 3 for Survival, and 10 for Probability.
-##' @param replace Sample with replacement. Default TRUE.
-##' @param splitrule Splitting rule, Survival only. The splitting rule can be chosen of "logrank" and "auc" with dedault "logrank".
-##' @param split.select.weights Numeric vector with weights representing the probability to select variables for splitting.
-##' @param always.split.variables Character vector with variable names to be always tried for splitting.
-##' @param scale.permutation.importance Scale permutation importance by standard error as in (Breiman 2001). Only applicable if permutation variable importance mode selected.
-##' @param num.threads Number of threads. Default is number of CPUs available.
-##' @param verbose Verbose output on or off.
-##' @param seed Random seed.
-##' @param memory Memory mode, one of 'double', 'float', 'char'.
-##' @param dependent.variable.name Name of dependent variable, needed if no formula given. For survival forests this is the time variable.
-##' @param status.variable.name Name of status variable, only applicable to survival data and needed if no formula given. Use 1 for event and 0 for censoring.
-##' @return Object of class \code{ranger} with elements
-##' \tabular{ll}{
-##' \code{forest} \tab Saved forest (If write.forest set to TRUE). \cr
-##' \code{predictions} \tab Predicted classes/values, based on out of bag samples (Classification and Regression only). \cr
-##' \code{variable.importance} \tab Variable importance for each independent variable. \cr
-##' \code{prediction.error} \tab Overall out of bag prediction error. For Classification this is the fraction of missclassified samples, for Regression the mean squared error and for Survival one minus Harrell's c-index. \cr
-##' \code{r.squared} \tab R squared. Also called explained variance or coefficient of determination (Regression only). \cr
-##' \code{classification.table} \tab Contingency table for classes and predictions (Classification only). \cr
-##' \code{unique.death.times} \tab Unique death times (Survival only). \cr
-##' \code{chf} \tab Estimated cumulative hazard function for each sample (Survival only). \cr
-##' \code{survival} \tab Estimated survival function for each sample (Survival only). \cr
-##' \code{call} \tab Function call. \cr
-##' \code{num.trees} \tab Number of trees. \cr
-##' \code{num.independent.variables} \tab Number of independent variables. \cr
-##' \code{mtry} \tab Value of mtry used. \cr
-##' \code{min.node.size} \tab Value of minimal node size used. \cr
-##' \code{treetype} \tab Type of forest/tree. Classification, Regression or Survival. \cr
-##' \code{memory.mode} \tab Memory mode used. \cr
-##' \code{importance.mode} \tab Importance mode used. \cr
-##' \code{num.samples} \tab Number of samples.
-##' }
-##' @examples
-##' require(ranger)
-##'
-##' ## Classification forest with default settings
-##' ranger(Species ~ ., data = iris)
-##'
-##' ## Prediction
-##' train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
-##' iris.train <- iris[train.idx, ]
-##' iris.test <- iris[-train.idx, ]
-##' rg.iris <- ranger(Species ~ ., data = iris.train, write.forest = TRUE)
-##' pred.iris <- predict(rg.iris, dat = iris.test)
-##' table(iris.test$Species, pred.iris$predictions)
-##'
-##' ## Variable importance
-##' rg.iris <- ranger(Species ~ ., data = iris, importance = "impurity")
-##' rg.iris$variable.importance
-##'
-##' ## Survival forest
-##' require(survival)
-##' rg.veteran <- ranger(Surv(time, status) ~ ., data = veteran)
-##' plot(rg.veteran$unique.death.times, rg.veteran$survival[1,])
-##'
-##' ## Alternative interface
-##' ranger(dependent.variable.name = "Species", data = iris)
-##'
-##' @author Marvin N. Wright
-##' @references
-##' Breiman, L. (2001). Random forests. Mach Learn, 45(1), 5-32. \cr
-##' Ishwaran, H., Kogalur, U. B., Blackstone, E. H., & Lauer, M. S. (2008). Random survival forests. Ann Appl Stat, 841-860. \cr
-##' Malley, J. D., Kruppa, J., Dasgupta, A., Malley, K. G., & Ziegler, A. (2012). Probability machines: consistent probability estimation using nonparametric learning machines. Methods Inf Med, 51(1), 74.
-##' @seealso \code{\link{predict.ranger}}
-##' @export
-ranger <- function(formula = NULL, data = NULL, num.trees = 500, mtry = NULL,
- importance = "none", write.forest = FALSE, probability = FALSE,
- min.node.size = NULL, replace = TRUE, splitrule = NULL,
- split.select.weights = NULL, always.split.variables = NULL,
- scale.permutation.importance = FALSE,
- num.threads = NULL,
- verbose = TRUE, seed = NULL, memory = "double",
- dependent.variable.name = NULL, status.variable.name = NULL) {
-
- ## GenABEL GWA data
- if (class(data) == "gwaa.data") {
- snp.names <- data@gtdata@snpnames
- sparse.data <- data@gtdata@gtps@.Data
- data <- data@phdata
- if ("id" %in% names(data)) {
- data$"id" <- NULL
- }
- gwa.mode <- TRUE
- } else {
- sparse.data <- as.matrix(0)
- gwa.mode <- FALSE
- }
-
- ## Formula interface. Use whole data frame is no formula provided and depvarname given
- if (is.null(formula)) {
- if (is.null(dependent.variable.name)) {
- stop("Error: Please give formula or dependent variable name.")
- }
- if (is.null(status.variable.name)) {
- status.variable.name <- "none"
- response <- data[, dependent.variable.name]
- } else {
- response <- data[, c(dependent.variable.name, status.variable.name)]
- }
- data.selected <- data
- } else {
- formula <- formula(formula)
- if (class(formula) != "formula") {
- stop("Error: Invalid formula.")
- }
- data.selected <- model.frame(formula, data, na.action = na.fail)
- response <- data.selected[[1]]
- }
-
- ## Probability estimation
- if (probability & !is.factor(response)) {
- stop("Error: Probability estimation is only applicable to categorical (factor) dependent variables.")
- }
-
- ## Treetype
- if (is.factor(response)) {
- if (probability) {
- treetype <- 9
- } else {
- treetype <- 1
- }
- } else if (is.numeric(response) & is.vector(response)) {
- treetype <- 3
- } else if (class(response) == "Surv" | class(response) == "data.frame") {
- treetype <- 5
- } else {
- stop("Error: Unsupported type of dependent variable.")
- }
-
- ## Dependent and status variable name. For non-survival dummy status variable name.
- if (!is.null(formula)) {
- if (treetype == 5) {
- dependent.variable.name <- dimnames(response)[[2]][1]
- status.variable.name <- dimnames(response)[[2]][2]
- } else {
- dependent.variable.name <- names(data.selected)[1]
- status.variable.name <- "none"
- }
- independent.variable.names <- names(data.selected)[-1]
- } else {
- independent.variable.names <- names(data.selected)[names(data.selected) != dependent.variable.name &
- names(data.selected) != status.variable.name]
- }
-
- ## Input data and variable names
- if (!is.null(formula)) {
- if (treetype == 5) {
- data.final <- data.matrix(cbind(response[, 1], response[, 2],
- data.selected[-1]))
- variable.names <- c(dependent.variable.name, status.variable.name,
- independent.variable.names)
- } else {
- data.final <- data.matrix(data.selected)
- variable.names <- names(data.selected)
- }
- } else {
- data.final <- data.matrix(data.selected)
- variable.names <- names(data.selected)
- }
-
- ## If gwa mode, add snp variable names
- if (gwa.mode) {
- variable.names <- c(variable.names, snp.names)
- all.independent.variable.names <- c(independent.variable.names, snp.names)
- } else {
- all.independent.variable.names <- independent.variable.names
- }
-
- ## Number of trees
- if (!is.numeric(num.trees) | num.trees < 1) {
- stop("Error: Invalid value for num.trees.")
- }
-
- ## mtry
- if (is.null(mtry)) {
- mtry <- 0
- } else if (!is.numeric(mtry) | mtry < 0) {
- stop("Error: Invalid value for mtry")
- }
-
- ## Memory mode
- if (is.null(memory) | memory == "double") {
- memory.mode <- 0
- } else if (memory == "float") {
- memory.mode <- 1
- } else if (memory == "char") {
- memory.mode <- 2
- } else {
- stop("Error: Unknown memory mode.")
- }
-
- ## Seed
- if (is.null(seed)) {
- seed <- 0
- }
-
- ## Num threads
- ## Default 0 -> detect from system in C++.
- if (is.null(num.threads)) {
- num.threads = 0
- } else if (!is.numeric(num.threads) | num.threads < 0) {
- stop("Error: Invalid value for num.threads")
- }
-
- ## Minumum node size
- if (is.null(min.node.size)) {
- min.node.size <- 0
- } else if (!is.numeric(min.node.size) | min.node.size < 0) {
- stop("Error: Invalid value for min.node.size")
- }
-
- ## Importance mode
- if (is.null(importance) | importance == "none") {
- importance.mode <- 0
- } else if (importance == "impurity") {
- importance.mode <- 1
- if (treetype == 5) {
- stop("Node impurity variable importance not supported for survival forests.")
- }
- } else if (importance == "permutation") {
- if (scale.permutation.importance) {
- importance.mode <- 2
- } else {
- importance.mode <- 3
- }
- } else {
- stop("Error: Unknown importance mode.")
- }
-
- ## Split select weights: NULL for no weights
- if (is.null(split.select.weights)) {
- split.select.weights <- c(0,0)
- use.split.select.weights <- FALSE
- } else {
- use.split.select.weights <- TRUE
- }
-
- ## Always split variables: NULL for no variables
- if (is.null(always.split.variables)) {
- always.split.variables <- c("0", "0")
- use.always.split.variables <- FALSE
- } else {
- use.always.split.variables <- TRUE
- }
-
- if (use.split.select.weights & use.always.split.variables) {
- stop("Error: Please use only one option of use.split.select.weights and use.always.split.variables.")
- }
-
- ## Splitting rule
- if (is.null(splitrule)) {
- splitrule <- 1
- } else if (treetype == 5 & splitrule == "logrank") {
- splitrule <- 1
- } else if (treetype == 5 & splitrule == "auc") {
- splitrule <- 2
- } else {
- stop("Error: Unknown splitrule.")
- }
-
- ## Prediction mode always false. Use predict.ranger() method.
- prediction.mode <- FALSE
-
- ## No loaded forest object
- loaded.forest <- list()
-
- ## Call Ranger
- result <- rangerCpp(treetype, dependent.variable.name, memory.mode, data.final, variable.names, mtry,
- num.trees, verbose, seed, num.threads, write.forest, importance.mode,
- min.node.size, split.select.weights, use.split.select.weights,
- always.split.variables, use.always.split.variables,
- status.variable.name, prediction.mode, loaded.forest, sparse.data,
- replace, probability, splitrule)
- replace, probability)
-
- if (length(result) == 0) {
- stop("Internal error.")
- }
-
- ## Prepare results
- result$predictions <- drop(do.call(rbind, result$predictions))
- if (importance.mode != 0) {
- names(result$variable.importance) <- all.independent.variable.names
- }
-
- if (treetype == 1) {
- result$predictions <- factor(result$predictions, levels = 1:nlevels(response),
- labels = levels(response))
- result$classification.table <- table(result$predictions, unlist(data[, dependent.variable.name]), dnn = c("predicted", "true"))
- } else if (treetype == 5) {
- result$chf <- result$predictions
- result$predictions <- NULL
- result$survival <- exp(-result$chf)
- } else if (treetype == 9) {
- result$predictions <- factor(result$predictions, levels = 1:nlevels(response),
- labels = levels(response))
- result$classification.table <- table(result$predictions, unlist(data[, dependent.variable.name]), dnn = c("predicted", "true"))
- }
-
- if (treetype == 1) {
- result$treetype <- "Classification"
- } else if (treetype == 3) {
- result$treetype <- "Regression"
- } else if (treetype == 5) {
- result$treetype <- "Survival"
- } else if (treetype == 9) {
- result$treetype <- "Probability estimation"
- }
- if (treetype == 3) {
- result$r.squared <- 1 - result$prediction.error / var(response)
- }
- result$call <- match.call()
- result$memory.mode <- memory
- result$importance.mode <- importance
- result$num.samples <- nrow(data.final)
-
- ## Write forest object
- if (write.forest) {
- result$forest$levels <- levels(response)
- result$forest$independent.variable.names <- independent.variable.names
- result$forest$treetype <- result$treetype
- class(result$forest) <- "ranger.forest"
- }
-
- class(result) <- "ranger"
- return(result)
-}
-
-
-
-
diff --git a/R_package/test/test_ranger.R b/R_package/test/test_ranger.R
deleted file mode 100644
index 3d5953d26..000000000
--- a/R_package/test/test_ranger.R
+++ /dev/null
@@ -1,94 +0,0 @@
-library(ranger)
-library(survival)
-
-context("ranger")
-
-rg.class <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE)
-rg.reg <- ranger(Sepal.Length ~ ., data = iris, verbose = FALSE, write.forest = TRUE)
-rg.surv <- ranger(Surv(time, status) ~ ., data = veteran, verbose = FALSE, write.forest = TRUE)
-
-test_that("classification result is of class ranger with 15 elements", {
- expect_that(rg.class, is_a("ranger"))
- expect_that(length(rg.class), equals(15))
-})
-
-test_that("regression result is of class ranger with 15 elements", {
- expect_that(rg.reg, is_a("ranger"))
- expect_that(length(rg.reg), equals(15))
-})
-
-test_that("survival result is of class ranger with 16 elements", {
- expect_that(rg.surv, is_a("ranger"))
- expect_that(length(rg.surv), equals(16))
-})
-
-test_that("results have 500 trees", {
- expect_that(rg.class$num.trees, equals(500))
- expect_that(rg.reg$num.trees, equals(500))
- expect_that(rg.surv$num.trees, equals(500))
-})
-
-test_that("results have right number of independent variables", {
- expect_that(rg.class$num.independent.variables, equals(ncol(iris) - 1))
- expect_that(rg.reg$num.independent.variables, equals(ncol(iris) - 1))
- expect_that(rg.surv$num.independent.variables, equals(ncol(veteran) - 2))
-})
-
-test_that("unique death times in survival result is right", {
- expect_that(rg.surv$unique.death.times, equals(sort(unique(veteran$time[veteran$status == 1]))))
-})
-
-test_that("importance measures work", {
- rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
- importance = "impurity")
- expect_that(rg.imp$variable.importance, is_a("numeric"))
- rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
- importance = "permutation")
- expect_that(rg.imp$variable.importance, is_a("numeric"))
- rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
- importance = "permutation", scale.permutation.importance = TRUE)
- expect_that(rg.imp$variable.importance, is_a("numeric"))
-})
-
-test_that("gini importance is larger than 1", {
- rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
- importance = "impurity")
- expect_that(rg.imp$variable.importance[1], is_more_than(1))
-})
-
-test_that("unscaled importance is smaller than 1", {
- rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
- importance = "permutation", scale.permutation.importance = FALSE)
- expect_that(rg.imp$variable.importance[1], is_less_than(1))
-})
-
-test_that("scaled importance is larger than 1", {
- rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
- importance = "permutation", scale.permutation.importance = TRUE)
- expect_that(rg.imp$variable.importance[1], is_more_than(1))
-})
-
-test_that("probability estimations are a matrix with correct size", {
- train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
- iris.train <- iris[train.idx, ]
- iris.test <- iris[-train.idx, ]
-
- rg.prob <- ranger(Species ~ ., data = iris.train, write.forest = TRUE, probability = TRUE)
- prob <- predict(rg.prob, iris.test)
-
- expect_that(prob$predictions, is_a("matrix"))
- expect_that(nrow(prob$predictions), equals(nrow(iris.test)))
- expect_that(ncol(prob$predictions), equals(length(rg.prob$forest$levels)))
-})
-
-test_that("probability estimations are between 0 and 1 and sum to 1", {
- train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
- iris.train <- iris[train.idx, ]
- iris.test <- iris[-train.idx, ]
-
- rg.prob <- ranger(Species ~ ., data = iris.train, write.forest = TRUE, probability = TRUE)
- prob <- predict(rg.prob, iris.test)
-
- expect_that(all(prob$predictions > -1e-5 & prob$predictions <= 1 + 1e-5), is_true())
- expect_that(rowSums(prob$predictions), equals(rep(1, nrow(prob$predictions))))
-})
diff --git a/TODO b/TODO
deleted file mode 100644
index 63c23afdf..000000000
--- a/TODO
+++ /dev/null
@@ -1,86 +0,0 @@
-
-## ----------------------------------------------------------------------- ##
-## TOOD software
-## ----------------------------------------------------------------------- ##
-
-- Compare speed with biomarcare data with RSF
-- Very slow for survival.. Why? Profile! Check for non-GWA data (biomarcare)
- -> Problems with variables with many different values
-- Slow for variables with many different values (continous)
- -> Section 3.5 in Daniels diss
-
-
-## ----------------------------------------------------------------------- ##
-## TODO after publication
-## ----------------------------------------------------------------------- ##
-
-- Change citation information
- -> in C++ (ArgumentHandler)
- -> in R
-- Check R package description
-- Add reference to roxygen
-- Upload C++ version, source + binaries. Gitlab public?
-- Create vignette
-- Check if new Rtools version with C++11 version is available, until then no
- windows support. If availabel check README file.
-- Submit to CRAN
- -> Add to readme
-
-
-## ----------------------------------------------------------------------- ##
-## Features: Maybe implement
-## ----------------------------------------------------------------------- ##
-
-- Lotus (high priority)
-- Conditional importance (Meng?)
-- Other splitting rules for Survival
-- Randomsplit-VIMP for Survival
-- For permutation importance use random splits for variable importance
- (Hapfelmeier et al. 2012)
-- Conditional Inference Forests
- -> Or another method to get unbiased vimp and faster? Use simpler test?
- -> Maybe just use other split method, no new tree types?
-- Unbiased survival? Different to Hothorn et al.!
-- Different kinds of bootstrapping:
- -> Use down/upsampling. For example in a study with less cases than
- controls upsample the cases and downsample the controls
- -> Use balancing. Draw 63.2% of cases and 63.2% of controls
-- Handle missing data
-
-
-## ----------------------------------------------------------------------- ##
-## Ideas
-## ----------------------------------------------------------------------- ##
-
-- Tune terminal node size
-- Pruning for probability estimation
-- Cross validation
-- Look at similiarity of trees, find representative trees (Idea: sequence
- alignment)
-
-
-## ----------------------------------------------------------------------- ##
-## Advantages
-## ----------------------------------------------------------------------- ##
-
-- Platform independent, no/few dependencies
-- Modular
-- Faster
-- Parallel (very easy)
-- Open source
-- Use as R package
-- Handling of GenABEL gwa data
-- Survival (faster!)
-- Probability estimations, multiclass!
-- Split selection weights
-
-
-## ----------------------------------------------------------------------- ##
-## Archive
-## ----------------------------------------------------------------------- ##
-
-- Early stopping for AUC splitting
-- Validate survival
- -> C index is okay
- -> Check validation_survival script..
- -> Different for return 0/-1 for denominator=0
\ No newline at end of file
diff --git a/ranger-r-package/.gitignore b/ranger-r-package/.gitignore
new file mode 100644
index 000000000..08932e17f
--- /dev/null
+++ b/ranger-r-package/.gitignore
@@ -0,0 +1,2 @@
+ranger.Rcheck/*
+.Rproj.user
diff --git a/ranger-r-package/create_package.R b/ranger-r-package/create_package.R
new file mode 100644
index 000000000..9a99bf26a
--- /dev/null
+++ b/ranger-r-package/create_package.R
@@ -0,0 +1,32 @@
+
+## Builds R package. Updates version and date first.
+library(Rcpp)
+library(roxygen2)
+library(devtools)
+library(testthat)
+
+package.name <- "ranger"
+
+## Set version and date
+package.version <- scan("../source/src/version.h", character(0))[5]
+dcf.file <- file.path(".", package.name, "DESCRIPTION")
+dcf <- read.dcf(dcf.file)
+dcf[1, "Version"] <- package.version
+dcf[1, "Date"] <- as.character(Sys.Date())
+write.dcf(dcf, dcf.file)
+
+## Create Rcpp files
+compileAttributes(package.name)
+
+## Add Documentation
+roxygenize(package.name)
+
+## Build/check/install/load package
+clean_dll(package.name)
+build(package.name)
+##check(package.name)
+install(package.name)
+library(package.name, character.only = TRUE)
+
+## Test
+test_package(package.name)
diff --git a/ranger-r-package/ranger.Rproj b/ranger-r-package/ranger.Rproj
new file mode 100644
index 000000000..aac6ccb53
--- /dev/null
+++ b/ranger-r-package/ranger.Rproj
@@ -0,0 +1,19 @@
+Version: 1.0
+
+RestoreWorkspace: Default
+SaveWorkspace: Default
+AlwaysSaveHistory: Default
+
+EnableCodeIndexing: Yes
+UseSpacesForTab: Yes
+NumSpacesForTab: 2
+Encoding: UTF-8
+
+RnwWeave: knitr
+LaTeX: pdfLaTeX
+
+BuildType: Package
+PackageUseDevtools: Yes
+PackagePath: ranger
+PackageInstallArgs: --no-multiarch --with-keep.source
+PackageRoxygenize: rd,collate,namespace
diff --git a/ranger-r-package/ranger/.gitignore b/ranger-r-package/ranger/.gitignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/ranger-r-package/ranger/DESCRIPTION b/ranger-r-package/ranger/DESCRIPTION
new file mode 100644
index 000000000..d8b2eff35
--- /dev/null
+++ b/ranger-r-package/ranger/DESCRIPTION
@@ -0,0 +1,17 @@
+Package: ranger
+Type: Package
+Title: A Fast Implementation of Random Forests
+Version: 0.3.7
+Date: 2016-03-02
+Author: Marvin N. Wright
+Maintainer: Marvin N. Wright
+Description: A fast implementation of Random Forests, particularly suited for high dimensional data. Ensembles of
+ classification, regression, survival and probability prediction trees are supported. Data from
+ genome-wide association studies can be analyzed efficiently. In addition to data frames, datasets of
+ class 'gwaa.data' (R package GenABEL) can be directly analyzed.
+License: GPL-3
+Imports: Rcpp (>= 0.11.2)
+LinkingTo: Rcpp
+Depends: R (>= 3.1)
+Suggests: survival, testthat
+RoxygenNote: 5.0.1
diff --git a/ranger-r-package/ranger/NAMESPACE b/ranger-r-package/ranger/NAMESPACE
new file mode 100644
index 000000000..d2a3a6811
--- /dev/null
+++ b/ranger-r-package/ranger/NAMESPACE
@@ -0,0 +1,22 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method(importance,ranger)
+S3method(predict,ranger)
+S3method(predict,ranger.forest)
+S3method(predictions,ranger)
+S3method(predictions,ranger.prediction)
+S3method(print,ranger)
+S3method(print,ranger.forest)
+S3method(print,ranger.prediction)
+S3method(timepoints,ranger)
+S3method(timepoints,ranger.prediction)
+export(csrf)
+export(getTerminalNodeIDs)
+export(importance)
+export(predictions)
+export(ranger)
+export(timepoints)
+import(stats)
+import(utils)
+importFrom(Rcpp,evalCpp)
+useDynLib(ranger)
diff --git a/ranger-r-package/ranger/NEWS b/ranger-r-package/ranger/NEWS
new file mode 100644
index 000000000..ea9ce727d
--- /dev/null
+++ b/ranger-r-package/ranger/NEWS
@@ -0,0 +1,101 @@
+##### Version 0.3.8
+* Remove tuning functions, please use mlr or caret.
+
+##### Version 0.3.7
+* Fix bug with alternative interface and prediction
+* Small fixes
+
+##### Version 0.3.6
+* Add keep.inbag option to track in-bag counts
+* Add option sample.fraction for fraction of sampled observations
+
+##### Version 0.3.5
+* Add tree-wise split.select.weights
+
+##### Version 0.3.4
+* Add predict.all option in predict() to get individual predictions for each tree for classification and regression
+* Small changes in documentation
+
+##### Version 0.3.3
+* Add case-specific random forests
+
+##### Version 0.3.2
+* Add case weights (weighted bootstrapping or subsampling)
+
+##### Version 0.3.1
+* Catch error of outdated gcc not supporting C++11 completely
+
+##### Version 0.3.0
+* Allow the user to interrupt computation from R
+* Transpose classification.table and rename to confusion.matrix
+* Respect R seed for prediction
+* Memory improvements for variable importance computation
+* Fix bug: Probability prediction for single observations
+* Fix bug: Results not identical when using alternative interface
+
+##### Version 0.2.7
+* Small fixes for Solaris compiler
+
+##### Version 0.2.6
+* Add C-index splitting
+* Fix NA SNP handling
+
+##### Version 0.2.5
+* Fix matrix and gwaa alternative survival interface
+* Version submitted to JSS
+
+##### Version 0.2.4
+* Small changes in documentation
+
+##### Version 0.2.3
+* Preallocate memory for splitting
+
+##### Version 0.2.2
+* Remove recursive splitting
+
+##### Version 0.2.1
+* Allow matrix as input data in R version
+
+##### Version 0.2.0
+* Fix prediction of classification forests in R
+
+##### Version 0.1.9
+* Speedup growing for continuous covariates
+* Add memory save option to save memory for very large datasets (but slower)
+* Remove memory mode option from R version since no performance gain
+
+##### Version 0.1.8
+* Fix problems when using Rcpp <0.11.4
+
+##### Version 0.1.7
+* Add option to split on unordered categorical covariates
+
+##### Version 0.1.6
+* Optimize memory management for very large survival forests
+
+##### Version 0.1.5
+* Set required Rcpp version to 0.11.2
+* Fix large $call objects when using BatchJobs
+* Add details and example on GenABEL usage to documentation
+* Minor changes to documentation
+
+##### Version 0.1.4
+* Speedup for survival forests with continuous covariates
+* R version: Generate seed from R. It is no longer necessary to set the
+ seed argument in ranger calls.
+
+##### Version 0.1.3
+* Windows support for R version (without multithreading)
+
+##### Version 0.1.2
+* Speedup growing of regression and probability prediction forests
+* Prediction forests are now handled like regression forests: MSE used for
+ prediction error and permutation importance
+* Fixed name conflict with randomForest package for "importance"
+* Fixed a bug: prediction function is now working for probability
+ prediction forests
+* Slot "predictions" for probability forests now contains class probabilities
+* importance function is now working even if randomForest package is
+ loaded after ranger
+* Fixed a bug: Split selection weights are now working as expected
+* Small changes in documentation
diff --git a/ranger-r-package/ranger/R/.gitignore b/ranger-r-package/ranger/R/.gitignore
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/ranger-r-package/ranger/R/.gitignore
@@ -0,0 +1 @@
+
diff --git a/ranger-r-package/ranger/R/RcppExports.R b/ranger-r-package/ranger/R/RcppExports.R
new file mode 100644
index 000000000..8774ca738
--- /dev/null
+++ b/ranger-r-package/ranger/R/RcppExports.R
@@ -0,0 +1,7 @@
+# This file was generated by Rcpp::compileAttributes
+# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
+
+rangerCpp <- function(treetype, dependent_variable_name, input_data, variable_names, mtry, num_trees, verbose, seed, num_threads, write_forest, importance_mode_r, min_node_size, split_select_weights, use_split_select_weights, always_split_variable_names, use_always_split_variable_names, status_variable_name, prediction_mode, loaded_forest, sparse_data, sample_with_replacement, probability, unordered_variable_names, use_unordered_variable_names, save_memory, splitrule_r, case_weights, use_case_weights, predict_all, keep_inbag, sample_fraction) {
+ .Call('ranger_rangerCpp', PACKAGE = 'ranger', treetype, dependent_variable_name, input_data, variable_names, mtry, num_trees, verbose, seed, num_threads, write_forest, importance_mode_r, min_node_size, split_select_weights, use_split_select_weights, always_split_variable_names, use_always_split_variable_names, status_variable_name, prediction_mode, loaded_forest, sparse_data, sample_with_replacement, probability, unordered_variable_names, use_unordered_variable_names, save_memory, splitrule_r, case_weights, use_case_weights, predict_all, keep_inbag, sample_fraction)
+}
+
diff --git a/ranger-r-package/ranger/R/csrf.R b/ranger-r-package/ranger/R/csrf.R
new file mode 100644
index 000000000..e25491e04
--- /dev/null
+++ b/ranger-r-package/ranger/R/csrf.R
@@ -0,0 +1,96 @@
+# -------------------------------------------------------------------------------
+# This file is part of Ranger.
+#
+# Ranger is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ranger is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ranger. If not, see .
+#
+# Written by:
+#
+# Marvin N. Wright
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
+# Ratzeburger Allee 160
+# 23562 Luebeck
+# Germany
+#
+# http://www.imbs-luebeck.de
+# wright@imbs.uni-luebeck.de
+# -------------------------------------------------------------------------------
+
+##' Case-specific random forests.
+##'
+##' In case-specific random forests (CSRF), random forests are built specific to the cases of interest.
+##' Instead of using equal probabilities, the cases are weighted according to their difference to the case of interest.
+##'
+##' The algorithm consists of 3 steps:
+##' \enumerate{
+##' \item Grow a random forest on the training data
+##' \item For each observation of interest (test data), the weights of all training observations are computed by counting the number of trees in which both observations are in the same terminal node.
+##' \item For each test observation, grow a weighted random forest on the training data, using the weights obtained in step 2. Predict the outcome of the test observation as usual.
+##' }
+##' In total, n+1 random forests are grown, where n is the number observations in the test dataset.
+##' For details, see Xu et al. (2014).
+##'
+##' @param formula Object of class \code{formula} or \code{character} describing the model to fit.
+##' @param training_data Training data of class \code{data.frame}.
+##' @param test_data Test data of class \code{data.frame}.
+##' @param params1 Parameters for the proximity random forest grown in the first step.
+##' @param params2 Parameters for the prediction random forests grown in the second step.
+##'
+##' @return Predictions for the test dataset.
+##'
+##' @examples
+##' ## Split in training and test data
+##' train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+##' iris.train <- iris[train.idx, ]
+##' iris.test <- iris[-train.idx, ]
+##'
+##' ## Run case-specific RF
+##' csrf(Species ~ ., training_data = iris.train, test_data = iris.test,
+##' params1 = list(num.trees = 50, mtry = 4),
+##' params2 = list(num.trees = 5))
+##'
+##' @author Marvin N. Wright
+##' @references
+##' Xu, R., Nettleton, D. & Nordman, D.J. (2014). Case-specific random forests. J Comp Graph Stat, in press. DOI: 10.1080/10618600.2014.983641
+##' @export
+csrf <- function(formula, training_data, test_data, params1 = list(), params2 = list()) {
+ ## Grow a random forest on the training data to obtain weights
+ rf.proximity <- do.call(ranger, c(list(formula = formula, data = training_data,
+ write.forest = TRUE), params1))
+
+ ## Get terminal nodes
+ terminal.nodeIDs.train <- getTerminalNodeIDs(rf.proximity, training_data)
+ terminal.nodeIDs.test <- getTerminalNodeIDs(rf.proximity, test_data)
+
+ ## Grow weighted RFs for test observations, predict the outcome
+ predictions <- sapply(1:nrow(test_data), function(i) {
+ ## Compute weights from first RF
+ num.same.node <- rowSums(terminal.nodeIDs.test[i, ] == terminal.nodeIDs.train)
+ weights <- num.same.node / sum(num.same.node)
+
+ ## Grow weighted RF
+ rf.prediction <- do.call(ranger, c(list(formula = formula, data = training_data,
+ write.forest = TRUE, case.weights = weights),
+ params2))
+
+ ## Predict outcome
+ predict(rf.prediction, test_data[i, ])$predictions
+ })
+
+ ## Return predictions
+ predictions
+}
+
+
+
diff --git a/ranger-r-package/ranger/R/getTerminalNodeIDs.R b/ranger-r-package/ranger/R/getTerminalNodeIDs.R
new file mode 100644
index 000000000..85310a954
--- /dev/null
+++ b/ranger-r-package/ranger/R/getTerminalNodeIDs.R
@@ -0,0 +1,82 @@
+# -------------------------------------------------------------------------------
+# This file is part of Ranger.
+#
+# Ranger is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ranger is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ranger. If not, see .
+#
+# Written by:
+#
+# Marvin N. Wright
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
+# Ratzeburger Allee 160
+# 23562 Luebeck
+# Germany
+#
+# http://www.imbs-luebeck.de
+# wright@imbs.uni-luebeck.de
+# -------------------------------------------------------------------------------
+
+##' Get terminal node IDs of observations.
+##'
+##' @param rf \code{ranger} object.
+##' @param dat New dataset. Terminal node IDs for this dataset are obtained.
+##'
+##' @return Matrix with terminal nodeIDs for all observations in dataset and trees.
+##'
+##' @examples
+##' library(ranger)
+##' rf <- ranger(Species ~ ., data = iris, write.forest = TRUE)
+##' getTerminalNodeIDs(rf, iris)
+##' @export
+getTerminalNodeIDs <- function(rf, dat) {
+ ## Check if forests exists
+ if (is.null(rf$forest)) {
+ stop("Error: No saved forest in ranger object. Please set write.forest to TRUE when calling ranger.")
+ }
+
+ ## Get terminal node IDs for each observation
+ result <- t(apply(dat, 1, function(obs) {
+ ## Drop observation down the trees
+ sapply(1:rf$num.trees, function(tree) {
+
+ child.nodeIDs <- rf$forest$child.nodeIDs[[tree]]
+ split.varIDs <- rf$forest$split.varIDs[[tree]]
+ split.values <- rf$forest$split.values[[tree]]
+
+ nodeID <- 1
+ while (1) {
+
+ ## Break if terminal node
+ if (length(child.nodeIDs[[nodeID]]) < 1) {
+ break
+ }
+
+ ## Move to child
+ split.varID <- split.varIDs[[nodeID]]
+ value <- obs[split.varID]
+ if (value <= split.values[[nodeID]]) {
+ ## Move to left child
+ nodeID <- child.nodeIDs[[nodeID]][1] + 1
+ } else {
+ ## Move to right child
+ nodeID <- child.nodeIDs[[nodeID]][2] + 1
+ }
+ }
+
+ return(nodeID)
+ })
+ }))
+
+ return(result)
+}
\ No newline at end of file
diff --git a/R_package/importance.R b/ranger-r-package/ranger/R/importance.R
similarity index 93%
rename from R_package/importance.R
rename to ranger-r-package/ranger/R/importance.R
index 5006e6298..7ed09fa30 100644
--- a/R_package/importance.R
+++ b/ranger-r-package/ranger/R/importance.R
@@ -17,10 +17,11 @@
# Written by:
#
# Marvin N. Wright
-# Institut für Medizinische Biometrie und Statistik
-# Universität zu Lübeck
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
# Ratzeburger Allee 160
-# 23562 Lübeck
+# 23562 Luebeck
+# Germany
#
# http://www.imbs-luebeck.de
# wright@imbs.uni-luebeck.de
@@ -39,7 +40,7 @@ importance <- function(x, ...) UseMethod("importance")
##' @seealso \code{\link{ranger}}
##' @author Marvin N. Wright
##' @aliases importance
-##' @export
+##' @export
importance.ranger <- function(x, ...) {
if (class(x) != "ranger") {
stop("Object ist no ranger object.")
diff --git a/R_package/predict.R b/ranger-r-package/ranger/R/predict.R
similarity index 53%
rename from R_package/predict.R
rename to ranger-r-package/ranger/R/predict.R
index ba6d20a04..3a4c344be 100644
--- a/R_package/predict.R
+++ b/ranger-r-package/ranger/R/predict.R
@@ -17,10 +17,11 @@
# Written by:
#
# Marvin N. Wright
-# Institut für Medizinische Biometrie und Statistik
-# Universität zu Lübeck
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
# Ratzeburger Allee 160
-# 23562 Lübeck
+# 23562 Luebeck
+# Germany
#
# http://www.imbs-luebeck.de
# wright@imbs.uni-luebeck.de
@@ -31,30 +32,31 @@
##' @title Ranger prediction
##' @param object Ranger \code{ranger.forest} object.
##' @param data New test data of class \code{data.frame} or \code{gwaa.data} (GenABEL).
+##' @param predict.all Return a matrix with individual predictions for each tree instead of aggregated predictions for all trees (classification and regression only).
##' @param seed Random seed used in Ranger.
##' @param num.threads Number of threads. Default is number of CPUs available.
-##' @param memory.mode Memory mode, one of 'double', 'float', 'char'.
##' @param verbose Verbose output on or off.
##' @param ... further arguments passed to or from other methods.
##' @return Object of class \code{ranger.prediction} with elements
##' \tabular{ll}{
-##' \code{predictions} \tab Predicted classes/values (only for Classification and Regression) \cr
-##' \code{unique.death.times} \tab Unique death times (only for Survival). \cr
-##' \code{chf} \tab Estimated cumulative hazard function for each sample (only for Survival). \cr
-##' \code{survival} \tab Estimated survival function for each sample (only for Survival). \cr
+##' \code{predictions} \tab Predicted classes/values (only for classification and regression) \cr
+##' \code{unique.death.times} \tab Unique death times (only for survival). \cr
+##' \code{chf} \tab Estimated cumulative hazard function for each sample (only for survival). \cr
+##' \code{survival} \tab Estimated survival function for each sample (only for survival). \cr
##' \code{num.trees} \tab Number of trees. \cr
##' \code{num.independent.variables} \tab Number of independent variables. \cr
-##' \code{treetype} \tab Type of forest/tree. Classification, Regression or Survival. \cr
+##' \code{treetype} \tab Type of forest/tree. Classification, regression or survival. \cr
##' \code{num.samples} \tab Number of samples.
##' }
##' @seealso \code{\link{ranger}}
##' @author Marvin N. Wright
##' @export
-predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
- memory.mode = "double", verbose = TRUE, ...) {
+predict.ranger.forest <- function(object, data, predict.all = FALSE,
+ seed = NULL, num.threads = NULL,
+ verbose = TRUE, ...) {
## GenABEL GWA data
- if (class(data) == "gwaa.data") {
+ if ("gwaa.data" %in% class(data)) {
snp.names <- snp.names(data)
sparse.data <- data@gtdata@gtps@.Data
data <- data@phdata[, -1]
@@ -63,7 +65,7 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
} else {
sparse.data <- as.matrix(0)
gwa.mode <- FALSE
- variable.names <- names(data)
+ variable.names <- colnames(data)
}
## Check forest argument
@@ -82,22 +84,60 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
is.null(forest$chf) | is.null(forest$unique.death.times))) {
stop("Error: Invalid forest object.")
}
- if (forest$treetype == "Classification" & (is.null(forest$levels))) {
- stop("Error: Invalid forest object.")
- }
-
- if (sum(!(forest$independent.variable.names %in% variable.names)) > 0) {
- stop("Error: One or more independent variables not found in data.")
- }
- data.selected <- subset(data, select = forest$independent.variable.names)
+
+ ## If alternative interface used, don't subset data
if (forest$treetype == "Survival") {
- data.final <- data.matrix(cbind(0, 0, data.selected))
- variable.names <- c("time", "status", forest$independent.variable.names)
+ if (forest$dependent.varID > 0 & forest$status.varID > 1) {
+ if (!is.matrix(data)) {
+ ## Recode characters
+ char.columns <- sapply(data, is.character)
+ data[char.columns] <- lapply(data[char.columns], factor)
+ }
+ data.final <- data.matrix(data)
+ } else {
+ data.selected <- subset(data, select = forest$independent.variable.names)
+ if (!is.matrix(data.selected)) {
+ ## Recode characters
+ char.columns <- sapply(data.selected, is.character)
+ data.selected[char.columns] <- lapply(data.selected[char.columns], factor)
+ }
+ data.final <- data.matrix(cbind(0, 0, data.selected))
+ variable.names <- c("time", "status", forest$independent.variable.names)
+ }
} else {
- data.final <- data.matrix(cbind(0, data.selected))
- variable.names <- c("dependent", forest$independent.variable.names)
+ if (ncol(data) == length(forest$independent.variable.names)+1 & forest$dependent.varID > 0) {
+ if (!is.matrix(data)) {
+ ## Recode characters
+ char.columns <- sapply(data, is.character)
+ data[char.columns] <- lapply(data[char.columns], factor)
+ }
+ data.final <- data.matrix(data)
+ } else {
+ data.selected <- subset(data, select = forest$independent.variable.names)
+ if (!is.matrix(data.selected)) {
+ ## Recode characters
+ char.columns <- sapply(data.selected, is.character)
+ data.selected[char.columns] <- lapply(data.selected[char.columns], factor)
+ }
+ ## Arange data as in original data
+ if (forest$dependent.varID == 0) {
+ data.final <- data.matrix(cbind(0, data.selected))
+ variable.names <- c("dependent", forest$independent.variable.names)
+ } else if (forest$dependent.varID >= ncol(data)) {
+ data.final <- data.matrix(cbind(data.selected, 0))
+ variable.names <- c(forest$independent.variable.names, "dependent")
+ } else {
+ data.final <- data.matrix(cbind(data.selected[, 1:forest$dependent.varID],
+ 0,
+ data.selected[, (forest$dependent.varID+2):ncol(data.selected)]))
+ variable.names <- c(forest$independent.variable.names[1:forest$dependent.varID],
+ "dependent",
+ forest$independent.variable.names[(forest$dependent.varID+1):length(forest$independent.variable.names)])
+ }
+ }
}
+
## If gwa mode, add snp variable names
if (gwa.mode) {
variable.names <- c(variable.names, snp.names)
@@ -106,6 +146,10 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
if (any(is.na(data.final))) {
stop("Missing values in data.")
}
+
+ if (sum(!(forest$independent.variable.names %in% variable.names)) > 0) {
+ stop("Error: One or more independent variables not found in data.")
+ }
## Num threads
## Default 0 -> detect from system in C++.
@@ -115,20 +159,9 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
stop("Error: Invalid value for num.threads")
}
- ## Memory mode
- if (is.null(memory.mode) | memory.mode == "double") {
- memory.mode <- 0
- } else if (memory.mode == "float") {
- memory.mode <- 1
- } else if (memory.mode == "char") {
- memory.mode <- 2
- } else {
- stop("Error: Unknown memory mode.")
- }
-
## Seed
if (is.null(seed)) {
- seed <- 0
+ seed <- runif(1 , 0, .Machine$integer.max)
}
if (forest$treetype == "Classification") {
@@ -148,7 +181,7 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
mtry <- 0
importance <- 0
min.node.size <- 0
- split.select.weights <- c(0, 0)
+ split.select.weights <- list(c(0, 0))
use.split.select.weights <- FALSE
always.split.variables <- c("0", "0")
use.always.split.variables <- FALSE
@@ -157,35 +190,50 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
write.forest <- FALSE
replace <- TRUE
probability <- FALSE
+ unordered.factor.variables <- c("0", "0")
+ use.unordered.factor.variables <- FALSE
+ save.memory <- FALSE
splitrule <- 1
-
+ case.weights <- c(0, 0)
+ use.case.weights <- FALSE
+ keep.inbag <- FALSE
+ sample.fraction <- 1
+
## Call Ranger
- result <- rangerCpp(treetype, dependent.variable.name, memory.mode, data.final, variable.names, mtry,
- forest$num.trees, verbose, seed, num.threads, write.forest, importance,
- min.node.size, split.select.weights, use.split.select.weights,
- always.split.variables, use.always.split.variables,
- status.variable.name, prediction.mode, forest, sparse.data,
- replace, probability, splitrule)
+ result <- rangerCpp(treetype, dependent.variable.name, data.final, variable.names, mtry,
+ forest$num.trees, verbose, seed, num.threads, write.forest, importance,
+ min.node.size, split.select.weights, use.split.select.weights,
+ always.split.variables, use.always.split.variables,
+ status.variable.name, prediction.mode, forest, sparse.data, replace, probability,
+ unordered.factor.variables, use.unordered.factor.variables, save.memory, splitrule,
+ case.weights, use.case.weights, predict.all, keep.inbag, sample.fraction)
if (length(result) == 0) {
- stop("Internal error.")
+ stop("User interrupt or internal error.")
}
-
+
## Prepare results
result$predictions <- drop(do.call(rbind, result$predictions))
result$num.samples <- nrow(data.final)
result$treetype <- forest$treetype
-
- if (forest$treetype == "Classification") {
- result$predictions <- factor(result$predictions, levels = 1:length(forest$levels),
- labels = forest$levels)
+
+ if (forest$treetype == "Classification" & !is.null(forest$levels)) {
+ if (!predict.all) {
+ result$predictions <- factor(result$predictions, levels = 1:length(forest$levels),
+ labels = forest$levels)
+ }
} else if (forest$treetype == "Survival") {
result$unique.death.times <- forest$unique.death.times
result$chf <- result$predictions
result$predictions <- NULL
result$survival <- exp(-result$chf)
- } else if (forest$treetype == "Probability estimation") {
- colnames(result$predictions) <- forest$levels
+ } else if (forest$treetype == "Probability estimation" & !is.null(forest$levels)) {
+ if (is.matrix(result$predictions)) {
+ colnames(result$predictions) <- forest$levels
+ } else {
+ names(result$predictions) <- forest$levels
+ }
+
}
class(result) <- "ranger.prediction"
@@ -193,34 +241,38 @@ predict.ranger.forest <- function(object, data, seed = NULL, num.threads = NULL,
}
##' Prediction with new data and a saved forest from Ranger.
+##'
+##' For classification and predict.all = TRUE, a matrix of factor levels is returned.
+##' To retrieve the corresponding factor levels, use rf$forest$levels, if rf is the ranger object.
##'
##' @title Ranger prediction
##' @param object Ranger \code{ranger} object.
##' @param data New test data of class \code{data.frame} or \code{gwaa.data} (GenABEL).
+##' @param predict.all Return a matrix with individual predictions for each tree instead of aggregated predictions for all trees (classification and regression only).
##' @param seed Random seed used in Ranger.
##' @param num.threads Number of threads. Default is number of CPUs available.
-##' @param memory.mode Memory mode, one of 'double', 'float', 'char'.
##' @param verbose Verbose output on or off.
##' @param ... further arguments passed to or from other methods.
##' @return Object of class \code{ranger.prediction} with elements
##' \tabular{ll}{
-##' \code{predictions} \tab Predicted classes/values (only for Classification and Regression) \cr
-##' \code{unique.death.times} \tab Unique death times (only for Survival). \cr
-##' \code{chf} \tab Estimated cumulative hazard function for each sample (only for Survival). \cr
-##' \code{survival} \tab Estimated survival function for each sample (only for Survival). \cr
+##' \code{predictions} \tab Predicted classes/values (only for classification and regression) \cr
+##' \code{unique.death.times} \tab Unique death times (only for survival). \cr
+##' \code{chf} \tab Estimated cumulative hazard function for each sample (only for survival). \cr
+##' \code{survival} \tab Estimated survival function for each sample (only for survival). \cr
##' \code{num.trees} \tab Number of trees. \cr
##' \code{num.independent.variables} \tab Number of independent variables. \cr
-##' \code{treetype} \tab Type of forest/tree. Classification, Regression or Survival. \cr
+##' \code{treetype} \tab Type of forest/tree. Classification, regression or survival. \cr
##' \code{num.samples} \tab Number of samples.
##' }
##' @seealso \code{\link{ranger}}
##' @author Marvin N. Wright
##' @export
-predict.ranger <- function(object, data, seed = NULL, num.threads = NULL,
- memory.mode = "double", verbose = TRUE, ...) {
+predict.ranger <- function(object, data, predict.all = FALSE,
+ seed = NULL, num.threads = NULL,
+ verbose = TRUE, ...) {
forest <- object$forest
if (is.null(forest)) {
stop("Error: No saved forest in ranger object. Please set write.forest to TRUE when calling ranger.")
}
- predict(forest, data, seed, num.threads, memory.mode, verbose)
+ predict(forest, data, predict.all, seed, num.threads, verbose)
}
diff --git a/R_package/predictions.R b/ranger-r-package/ranger/R/predictions.R
similarity index 93%
rename from R_package/predictions.R
rename to ranger-r-package/ranger/R/predictions.R
index 662f6159a..25d879b0d 100644
--- a/R_package/predictions.R
+++ b/ranger-r-package/ranger/R/predictions.R
@@ -17,10 +17,11 @@
# Written by:
#
# Marvin N. Wright
-# Institut für Medizinische Biometrie und Statistik
-# Universität zu Lübeck
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
# Ratzeburger Allee 160
-# 23562 Lübeck
+# 23562 Luebeck
+# Germany
#
# http://www.imbs-luebeck.de
# wright@imbs.uni-luebeck.de
@@ -44,7 +45,7 @@ predictions.ranger.prediction <- function(x, ...) {
if (class(x) != "ranger.prediction") {
stop("Object ist no ranger.prediction object.")
}
- if (x$treetype == "Classification" | x$treetype == "Regression") {
+ if (x$treetype == "Classification" | x$treetype == "Regression" | x$treetype == "Probability estimation") {
if (is.null(x$predictions)) {
stop("No predictions found.")
} else {
@@ -75,7 +76,7 @@ predictions.ranger<- function(x, ...) {
if (class(x) != "ranger") {
stop("Object ist no ranger object.")
}
- if (x$treetype == "Classification" | x$treetype == "Regression") {
+ if (x$treetype == "Classification" | x$treetype == "Regression" | x$treetype == "Probability estimation") {
if (is.null(x$predictions)) {
stop("No predictions found.")
} else {
diff --git a/R_package/print.R b/ranger-r-package/ranger/R/print.R
similarity index 95%
rename from R_package/print.R
rename to ranger-r-package/ranger/R/print.R
index 6e703ee4e..a48312986 100644
--- a/R_package/print.R
+++ b/ranger-r-package/ranger/R/print.R
@@ -17,10 +17,11 @@
# Written by:
#
# Marvin N. Wright
-# Institut für Medizinische Biometrie und Statistik
-# Universität zu Lübeck
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
# Ratzeburger Allee 160
-# 23562 Lübeck
+# 23562 Luebeck
+# Germany
#
# http://www.imbs-luebeck.de
# wright@imbs.uni-luebeck.de
@@ -45,7 +46,6 @@ print.ranger <- function(x, ...) {
cat("Mtry: ", x$mtry, "\n")
cat("Target node size: ", x$min.node.size, "\n")
cat("Variable importance mode: ", x$importance.mode, "\n")
- cat("Memory mode: ", x$memory.mode, "\n")
if (x$treetype == "Survival") {
cat("Number of unique death times: ", length(x$unique.death.times), "\n")
}
diff --git a/ranger-r-package/ranger/R/ranger.R b/ranger-r-package/ranger/R/ranger.R
new file mode 100644
index 000000000..8c01dd3e5
--- /dev/null
+++ b/ranger-r-package/ranger/R/ranger.R
@@ -0,0 +1,494 @@
+# -------------------------------------------------------------------------------
+# This file is part of Ranger.
+#
+# Ranger is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ranger is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ranger. If not, see .
+#
+# Written by:
+#
+# Marvin N. Wright
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
+# Ratzeburger Allee 160
+# 23562 Luebeck
+# Germany
+#
+# http://www.imbs-luebeck.de
+# wright@imbs.uni-luebeck.de
+# -------------------------------------------------------------------------------
+
+##' Ranger is a fast implementation of Random Forest (Breiman 2001) or recursive partitioning, particularly suited for high dimensional data.
+##' Classification, regression, and survival forests are supported.
+##' Classification and regression forests are implemented as in the original Random Forest (Breiman 2001), survival forests as in Random Survival Forests (Ishwaran et al. 2008).
+##'
+##' The tree type is determined by the type of the dependent variable.
+##' For factors classification trees are grown, for numeric values regression trees and for survival objects survival trees.
+##' The Gini index is used as splitting rule for classification, the estimated response variances for regression and the log-rank test for survival.
+##' For Survival the log-rank test or an AUC-based splitting rule are available.
+##'
+##' With the \code{probability} option and factor dependent variable a probability forest is grown.
+##' Here, the estimated response variances are used for splitting, as in regression forests.
+##' Predictions are class probabilities for each sample.
+##' For details see Malley et al. (2012).
+##'
+##' Note that for classification and regression nodes with size smaller than min.node.size can occur, like in original Random Forest.
+##' For survival all nodes contain at least min.node.size samples.
+##' Variables selected with \code{always.split.variables} are tried additionaly to the mtry variables randomly selected.
+##' In \code{split.select.weights} variables weighted with 0 are never selected and variables with 1 are always selected.
+##' Weights do not need to sum up to 1, they will be normalized later.
+##' The usage of \code{split.select.weights} can increase the computation times for large forests.
+##'
+##' For a large number of variables and data frame as input data the formula interface can be slow or impossible to use.
+##' Alternatively dependent.variable.name (and status.variable.name for survival) can be used.
+##' Consider setting \code{save.memory = TRUE} if you encounter memory problems for very large datasets.
+##'
+##' For GWAS data consider combining \code{ranger} with the \code{GenABEL} package.
+##' See the Examples section below for a demonstration using \code{Plink} data.
+##' All SNPs in the \code{GenABEL} object will be used for splitting.
+##' To use only the SNPs without sex or other covariates from the phenotype file, use \code{0} on the right hand side of the formula.
+##' Note that missing values are treated as an extra category while splitting.
+##'
+##' See \url{https://github.com/mnwright/ranger} for the development version.
+##'
+##' Notes:
+##' \itemize{
+##' \item Multithreading is currently not supported for Microsoft Windows platforms.
+##' }
+##'
+##' @title Ranger
+##' @param formula Object of class \code{formula} or \code{character} describing the model to fit.
+##' @param data Training data of class \code{data.frame}, \code{matrix} or \code{gwaa.data} (GenABEL).
+##' @param num.trees Number of trees.
+##' @param mtry Number of variables to possibly split at in each node. Default is the (rounded down) square root of the number variables.
+##' @param importance Variable importance mode, one of 'none', 'impurity', 'permutation'. The 'impurity' measure is the Gini index for classification and the variance of the responses for regression. For survival, only 'permutation' is available.
+##' @param write.forest Save \code{ranger.forest} object, needed for prediction.
+##' @param probability Grow a probability forest as in Malley et al. (2012).
+##' @param min.node.size Minimal node size. Default 1 for classification, 5 for regression, 3 for survival, and 10 for probability.
+##' @param replace Sample with replacement.
+##' @param sample.fraction Fraction of observations to sample. Default is 1 for sampling with replacement and 0.632 for sampling without replacement.
+##' @param splitrule Splitting rule, survival only. The splitting rule can be chosen of "logrank" and "C" with default "logrank".
+##' @param case.weights Weights for sampling of training observations. Observations with larger weights will be selected with higher probability in the bootstrap (or subsampled) samples for the trees.
+##' @param split.select.weights Numeric vector with weights between 0 and 1, representing the probability to select variables for splitting. Alternatively, a list of size num.trees, containing split select weight vectors for each tree can be used.
+##' @param always.split.variables Character vector with variable names to be always tried for splitting.
+##' @param respect.unordered.factors Regard unordered factor covariates as unordered categorical variables. If \code{FALSE}, all factors are regarded ordered.
+##' @param scale.permutation.importance Scale permutation importance by standard error as in (Breiman 2001). Only applicable if permutation variable importance mode selected.
+##' @param keep.inbag Save how often observations are in-bag in each tree.
+##' @param num.threads Number of threads. Default is number of CPUs available.
+##' @param save.memory Use memory saving (but slower) splitting mode. No effect for GWAS data.
+##' @param verbose Verbose output on or off.
+##' @param seed Random seed. Default is \code{NULL}, which generates the seed from \code{R}.
+##' @param dependent.variable.name Name of dependent variable, needed if no formula given. For survival forests this is the time variable.
+##' @param status.variable.name Name of status variable, only applicable to survival data and needed if no formula given. Use 1 for event and 0 for censoring.
+##' @param classification Only needed if data is a matrix. Set to \code{TRUE} to grow a classification forest.
+##' @return Object of class \code{ranger} with elements
+##' \item{\code{forest}}{Saved forest (If write.forest set to TRUE). Note that the variable IDs in the \code{split.varIDs} object do not necessarily represent the column number in R.}
+##' \item{\code{predictions}}{Predicted classes/values, based on out of bag samples (classification and regression only).}
+##' \item{\code{forest}}{Saved forest (If write.forest set to TRUE). Note that the variable IDs in the \code{split.varIDs} object do not necessarily represent the column number in R.}
+##' \item{\code{predictions}}{Predicted classes/values, based on out of bag samples (classification and regression only).}
+##' \item{\code{variable.importance}}{Variable importance for each independent variable.}
+##' \item{\code{prediction.error}}{Overall out of bag prediction error. For classification this is the fraction of missclassified samples, for regression the mean squared error and for survival one minus Harrell's c-index.}
+##' \item{\code{r.squared}}{R squared. Also called explained variance or coefficient of determination (regression only).}
+##' \item{\code{confusion.matrix}}{Contingency table for classes and predictions based on out of bag samples (classification only).}
+##' \item{\code{unique.death.times}}{Unique death times (survival only).}
+##' \item{\code{chf}}{Estimated cumulative hazard function for each sample (survival only).}
+##' \item{\code{survival}}{Estimated survival function for each sample (survival only).}
+##' \item{\code{call}}{Function call.}
+##' \item{\code{num.trees}}{Number of trees.}
+##' \item{\code{num.independent.variables}}{Number of independent variables.}
+##' \item{\code{mtry}}{Value of mtry used.}
+##' \item{\code{min.node.size}}{Value of minimal node size used.}
+##' \item{\code{treetype}}{Type of forest/tree. classification, regression or survival.}
+##' \item{\code{importance.mode}}{Importance mode used.}
+##' \item{\code{num.samples}}{Number of samples.}
+##' \item{\code{inbag.counts}}{Number of times the observations are in-bag in the trees.}
+##' @examples
+##' require(ranger)
+##'
+##' ## Classification forest with default settings
+##' ranger(Species ~ ., data = iris)
+##'
+##' ## Prediction
+##' train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+##' iris.train <- iris[train.idx, ]
+##' iris.test <- iris[-train.idx, ]
+##' rg.iris <- ranger(Species ~ ., data = iris.train, write.forest = TRUE)
+##' pred.iris <- predict(rg.iris, dat = iris.test)
+##' table(iris.test$Species, pred.iris$predictions)
+##'
+##' ## Variable importance
+##' rg.iris <- ranger(Species ~ ., data = iris, importance = "impurity")
+##' rg.iris$variable.importance
+##'
+##' ## Survival forest
+##' require(survival)
+##' rg.veteran <- ranger(Surv(time, status) ~ ., data = veteran)
+##' plot(rg.veteran$unique.death.times, rg.veteran$survival[1,])
+##'
+##' ## Alternative interface
+##' ranger(dependent.variable.name = "Species", data = iris)
+##'
+##' \dontrun{
+##' ## Use GenABEL interface to read Plink data into R and grow a classification forest
+##' ## The ped and map files are not included
+##' library(GenABEL)
+##' convert.snp.ped("data.ped", "data.map", "data.raw")
+##' dat.gwaa <- load.gwaa.data("data.pheno", "data.raw")
+##' phdata(dat.gwaa)$trait <- factor(phdata(dat.gwaa)$trait)
+##' ranger(trait ~ ., data = dat.gwaa)
+##' }
+##'
+##' @author Marvin N. Wright
+##' @references
+##' Wright, M. N., & Ziegler, A. (2015). ranger: A fast implementation of random forests for high dimensional data in C++ and R. arXiv preprint \url{http://arxiv.org/abs/1508.04409}.
+##'
+##' Breiman, L. (2001). Random forests. Mach Learn, 45(1), 5-32. \cr
+##' Ishwaran, H., Kogalur, U. B., Blackstone, E. H., & Lauer, M. S. (2008). Random survival forests. Ann Appl Stat, 841-860. \cr
+##' Malley, J. D., Kruppa, J., Dasgupta, A., Malley, K. G., & Ziegler, A. (2012). Probability machines: consistent probability estimation using nonparametric learning machines. Methods Inf Med, 51(1), 74.
+##' @seealso \code{\link{predict.ranger}}
+##' @useDynLib ranger
+##' @importFrom Rcpp evalCpp
+##' @import stats
+##' @import utils
+##' @export
+ranger <- function(formula = NULL, data = NULL, num.trees = 500, mtry = NULL,
+ importance = "none", write.forest = FALSE, probability = FALSE,
+ min.node.size = NULL, replace = TRUE,
+ sample.fraction = ifelse(replace, 1, 0.632),
+ splitrule = NULL, case.weights = NULL,
+ split.select.weights = NULL, always.split.variables = NULL,
+ respect.unordered.factors = FALSE,
+ scale.permutation.importance = FALSE,
+ keep.inbag = FALSE,
+ num.threads = NULL, save.memory = FALSE,
+ verbose = TRUE, seed = NULL,
+ dependent.variable.name = NULL, status.variable.name = NULL,
+ classification = NULL) {
+
+ ## GenABEL GWA data
+ if ("gwaa.data" %in% class(data)) {
+ snp.names <- data@gtdata@snpnames
+ sparse.data <- data@gtdata@gtps@.Data
+ data <- data@phdata
+ if ("id" %in% names(data)) {
+ data$"id" <- NULL
+ }
+ gwa.mode <- TRUE
+ save.memory <- FALSE
+ } else {
+ sparse.data <- as.matrix(0)
+ gwa.mode <- FALSE
+ }
+
+ ## Formula interface. Use whole data frame is no formula provided and depvarname given
+ if (is.null(formula)) {
+ if (is.null(dependent.variable.name)) {
+ stop("Error: Please give formula or dependent variable name.")
+ }
+ if (is.null(status.variable.name)) {
+ status.variable.name <- "none"
+ response <- data[, dependent.variable.name]
+ } else {
+ response <- data[, c(dependent.variable.name, status.variable.name)]
+ }
+ data.selected <- data
+ } else {
+ formula <- formula(formula)
+ if (class(formula) != "formula") {
+ stop("Error: Invalid formula.")
+ }
+ data.selected <- model.frame(formula, data, na.action = na.fail)
+ response <- data.selected[[1]]
+ }
+
+ ## Probability estimation
+ if (probability & !is.factor(response)) {
+ stop("Error: Probability estimation is only applicable to categorical (factor) dependent variables.")
+ }
+
+ ## Treetype
+ if (is.factor(response)) {
+ if (probability) {
+ treetype <- 9
+ } else {
+ treetype <- 1
+ }
+ } else if (is.numeric(response) & is.vector(response)) {
+ if (!is.null(classification) && classification) {
+ treetype <- 1
+ } else {
+ treetype <- 3
+ }
+ } else if (class(response) == "Surv" | is.data.frame(response) | is.matrix(response)) {
+ treetype <- 5
+ } else {
+ stop("Error: Unsupported type of dependent variable.")
+ }
+
+ ## Dependent and status variable name. For non-survival dummy status variable name.
+ if (!is.null(formula)) {
+ if (treetype == 5) {
+ dependent.variable.name <- dimnames(response)[[2]][1]
+ status.variable.name <- dimnames(response)[[2]][2]
+ } else {
+ dependent.variable.name <- names(data.selected)[1]
+ status.variable.name <- "none"
+ }
+ independent.variable.names <- names(data.selected)[-1]
+ } else {
+ independent.variable.names <- colnames(data.selected)[colnames(data.selected) != dependent.variable.name &
+ colnames(data.selected) != status.variable.name]
+ }
+
+ ## Recode characters as factors
+ if (!is.matrix(data.selected)) {
+ char.columns <- sapply(data.selected, is.character)
+ data.selected[char.columns] <- lapply(data.selected[char.columns], factor)
+ }
+
+ ## Input data and variable names
+ if (!is.null(formula) & treetype == 5) {
+ data.final <- cbind(response[, 1], response[, 2],
+ data.selected[-1])
+ colnames(data.final) <- c(dependent.variable.name, status.variable.name,
+ independent.variable.names)
+ } else {
+ data.final <- data.selected
+ }
+ if (!is.matrix(data.selected)) {
+ ## Create matrix
+ data.final <- data.matrix(data.final)
+ }
+ variable.names <- colnames(data.final)
+
+
+ ## If gwa mode, add snp variable names
+ if (gwa.mode) {
+ variable.names <- c(variable.names, snp.names)
+ all.independent.variable.names <- c(independent.variable.names, snp.names)
+ } else {
+ all.independent.variable.names <- independent.variable.names
+ }
+
+ ## Number of trees
+ if (!is.numeric(num.trees) | num.trees < 1) {
+ stop("Error: Invalid value for num.trees.")
+ }
+
+ ## mtry
+ if (is.null(mtry)) {
+ mtry <- 0
+ } else if (!is.numeric(mtry) | mtry < 0) {
+ stop("Error: Invalid value for mtry")
+ }
+
+ ## Seed
+ if (is.null(seed)) {
+ seed <- runif(1 , 0, .Machine$integer.max)
+ }
+
+ ## Keep inbag
+ if (!is.logical(keep.inbag)) {
+ stop("Error: Invalid value for keep.inbag")
+ }
+
+ ## Num threads
+ ## Default 0 -> detect from system in C++.
+ if (is.null(num.threads)) {
+ num.threads = 0
+ } else if (!is.numeric(num.threads) | num.threads < 0) {
+ stop("Error: Invalid value for num.threads")
+ }
+
+ ## Minumum node size
+ if (is.null(min.node.size)) {
+ min.node.size <- 0
+ } else if (!is.numeric(min.node.size) | min.node.size < 0) {
+ stop("Error: Invalid value for min.node.size")
+ }
+
+ ## Sample fraction
+ if (!is.numeric(sample.fraction) | sample.fraction <= 0 | sample.fraction > 1) {
+ stop("Error: Invalid value for sample.fraction. Please give a value in (0,1].")
+ }
+
+ ## Importance mode
+ if (is.null(importance) | importance == "none") {
+ importance.mode <- 0
+ } else if (importance == "impurity") {
+ importance.mode <- 1
+ if (treetype == 5) {
+ stop("Node impurity variable importance not supported for survival forests.")
+ }
+ } else if (importance == "permutation") {
+ if (scale.permutation.importance) {
+ importance.mode <- 2
+ } else {
+ importance.mode <- 3
+ }
+ } else {
+ stop("Error: Unknown importance mode.")
+ }
+
+ ## Case weights: NULL for no weights
+ if (is.null(case.weights)) {
+ case.weights <- c(0,0)
+ use.case.weights <- FALSE
+ } else {
+ use.case.weights <- TRUE
+ }
+
+ ## Split select weights: NULL for no weights
+ if (is.null(split.select.weights)) {
+ split.select.weights <- list(c(0,0))
+ use.split.select.weights <- FALSE
+ } else if (is.numeric(split.select.weights)) {
+ if (length(split.select.weights) != length(all.independent.variable.names)) {
+ stop("Error: Number of split select weights not equal to number of independent variables.")
+ }
+ split.select.weights <- list(split.select.weights)
+ use.split.select.weights <- TRUE
+ } else if (is.list(split.select.weights)) {
+ if (length(split.select.weights) != num.trees) {
+ stop("Error: Size of split select weights list not equal to number of trees.")
+ }
+ use.split.select.weights <- TRUE
+ } else {
+ stop("Error: Invalid split select weights.")
+ }
+
+ ## Always split variables: NULL for no variables
+ if (is.null(always.split.variables)) {
+ always.split.variables <- c("0", "0")
+ use.always.split.variables <- FALSE
+ } else {
+ use.always.split.variables <- TRUE
+ }
+
+ if (use.split.select.weights & use.always.split.variables) {
+ stop("Error: Please use only one option of split.select.weights and always.split.variables.")
+ }
+
+ ## Splitting rule
+ if (is.null(splitrule)) {
+ splitrule <- 1
+ } else if (treetype == 5 & splitrule == "logrank") {
+ splitrule <- 1
+ } else if (treetype == 5 & (splitrule == "auc" | splitrule == "C")) {
+ splitrule <- 2
+ } else if (treetype == 5 & (splitrule == "auc_ignore_ties" | splitrule == "C_ignore_ties")) {
+ splitrule <- 3
+ } else {
+ stop("Error: Unknown splitrule.")
+ }
+
+ ## Unordered factors
+ if (respect.unordered.factors) {
+ names.selected <- names(data.selected)
+ ordered.idx <- sapply(data.selected, is.ordered)
+ factor.idx <- sapply(data.selected, is.factor)
+ independent.idx <- names.selected != dependent.variable.name & names.selected != status.variable.name
+ unordered.factor.variables <- names.selected[factor.idx & !ordered.idx & independent.idx]
+
+ if (length(unordered.factor.variables) > 0) {
+ use.unordered.factor.variables <- TRUE
+ ## Check level count
+ num.levels <- sapply(data.selected[, factor.idx & !ordered.idx & independent.idx, drop = FALSE], nlevels)
+ max.level.count <- 8*.Machine$sizeof.pointer - 1
+ if (max(num.levels) > max.level.count) {
+ stop(paste("Too many levels in unordered categorical variable ", unordered.factor.variables[which.max(num.levels)],
+ ". Only ", max.level.count, " levels allowed on this system. Consider ordering this factor.", sep = ""))
+ }
+ } else {
+ unordered.factor.variables <- c("0", "0")
+ use.unordered.factor.variables <- FALSE
+ }
+ } else {
+ unordered.factor.variables <- c("0", "0")
+ use.unordered.factor.variables <- FALSE
+ }
+
+ ## Prediction mode always false. Use predict.ranger() method.
+ prediction.mode <- FALSE
+ predict.all <- FALSE
+
+ ## No loaded forest object
+ loaded.forest <- list()
+
+ ## Clean up
+ rm("data.selected")
+
+ ## Call Ranger
+ result <- rangerCpp(treetype, dependent.variable.name, data.final, variable.names, mtry,
+ num.trees, verbose, seed, num.threads, write.forest, importance.mode,
+ min.node.size, split.select.weights, use.split.select.weights,
+ always.split.variables, use.always.split.variables,
+ status.variable.name, prediction.mode, loaded.forest, sparse.data,
+ replace, probability, unordered.factor.variables, use.unordered.factor.variables,
+ save.memory, splitrule, case.weights, use.case.weights, predict.all,
+ keep.inbag, sample.fraction)
+
+ if (length(result) == 0) {
+ stop("User interrupt or internal error.")
+ }
+
+ ## Prepare results
+ result$predictions <- drop(do.call(rbind, result$predictions))
+ if (importance.mode != 0) {
+ names(result$variable.importance) <- all.independent.variable.names
+ }
+
+ ## Set predictions
+ if (treetype == 1 & is.factor(response)) {
+ result$predictions <- factor(result$predictions, levels = 1:nlevels(response),
+ labels = levels(response))
+ result$confusion.matrix <- table(unlist(data.final[, dependent.variable.name]), result$predictions, dnn = c("true", "predicted"))
+ } else if (treetype == 5) {
+ result$chf <- result$predictions
+ result$predictions <- NULL
+ result$survival <- exp(-result$chf)
+ } else if (treetype == 9 & !is.matrix(data)) {
+ colnames(result$predictions) <- levels(response)
+ }
+
+ ## Set treetype
+ if (treetype == 1) {
+ result$treetype <- "Classification"
+ } else if (treetype == 3) {
+ result$treetype <- "Regression"
+ } else if (treetype == 5) {
+ result$treetype <- "Survival"
+ } else if (treetype == 9) {
+ result$treetype <- "Probability estimation"
+ }
+ if (treetype == 3) {
+ result$r.squared <- 1 - result$prediction.error / var(response)
+ }
+ result$call <- sys.call()
+ result$importance.mode <- importance
+ result$num.samples <- nrow(data.final)
+
+ ## Write forest object
+ if (write.forest) {
+ result$forest$levels <- levels(response)
+ result$forest$independent.variable.names <- independent.variable.names
+ result$forest$treetype <- result$treetype
+ class(result$forest) <- "ranger.forest"
+ }
+
+ class(result) <- "ranger"
+ return(result)
+}
+
+
+
+
diff --git a/R_package/timepoints.R b/ranger-r-package/ranger/R/timepoints.R
similarity index 95%
rename from R_package/timepoints.R
rename to ranger-r-package/ranger/R/timepoints.R
index 9ecf50267..4189983f3 100644
--- a/R_package/timepoints.R
+++ b/ranger-r-package/ranger/R/timepoints.R
@@ -17,10 +17,11 @@
# Written by:
#
# Marvin N. Wright
-# Institut für Medizinische Biometrie und Statistik
-# Universität zu Lübeck
+# Institut fuer Medizinische Biometrie und Statistik
+# Universitaet zu Luebeck
# Ratzeburger Allee 160
-# 23562 Lübeck
+# 23562 Luebeck
+# Germany
#
# http://www.imbs-luebeck.de
# wright@imbs.uni-luebeck.de
diff --git a/ranger-r-package/ranger/inst/include/ranger.h b/ranger-r-package/ranger/inst/include/ranger.h
new file mode 100644
index 000000000..cda58d820
--- /dev/null
+++ b/ranger-r-package/ranger/inst/include/ranger.h
@@ -0,0 +1 @@
+#include "../../src/globals.h"
\ No newline at end of file
diff --git a/ranger-r-package/ranger/man/csrf.Rd b/ranger-r-package/ranger/man/csrf.Rd
new file mode 100644
index 000000000..09406975b
--- /dev/null
+++ b/ranger-r-package/ranger/man/csrf.Rd
@@ -0,0 +1,55 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/csrf.R
+\name{csrf}
+\alias{csrf}
+\title{Case-specific random forests.}
+\usage{
+csrf(formula, training_data, test_data, params1 = list(), params2 = list())
+}
+\arguments{
+\item{formula}{Object of class \code{formula} or \code{character} describing the model to fit.}
+
+\item{training_data}{Training data of class \code{data.frame}.}
+
+\item{test_data}{Test data of class \code{data.frame}.}
+
+\item{params1}{Parameters for the proximity random forest grown in the first step.}
+
+\item{params2}{Parameters for the prediction random forests grown in the second step.}
+}
+\value{
+Predictions for the test dataset.
+}
+\description{
+In case-specific random forests (CSRF), random forests are built specific to the cases of interest.
+Instead of using equal probabilities, the cases are weighted according to their difference to the case of interest.
+}
+\details{
+The algorithm consists of 3 steps:
+\enumerate{
+ \item Grow a random forest on the training data
+ \item For each observation of interest (test data), the weights of all training observations are computed by counting the number of trees in which both observations are in the same terminal node.
+ \item For each test observation, grow a weighted random forest on the training data, using the weights obtained in step 2. Predict the outcome of the test observation as usual.
+}
+ In total, n+1 random forests are grown, where n is the number observations in the test dataset.
+ For details, see Xu et al. (2014).
+}
+\examples{
+## Split in training and test data
+train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+iris.train <- iris[train.idx, ]
+iris.test <- iris[-train.idx, ]
+
+## Run case-specific RF
+csrf(Species ~ ., training_data = iris.train, test_data = iris.test,
+ params1 = list(num.trees = 50, mtry = 4),
+ params2 = list(num.trees = 5))
+
+}
+\author{
+Marvin N. Wright
+}
+\references{
+Xu, R., Nettleton, D. & Nordman, D.J. (2014). Case-specific random forests. J Comp Graph Stat, in press. DOI: 10.1080/10618600.2014.983641
+}
+
diff --git a/ranger-r-package/ranger/man/getTerminalNodeIDs.Rd b/ranger-r-package/ranger/man/getTerminalNodeIDs.Rd
new file mode 100644
index 000000000..1a7d4c2f5
--- /dev/null
+++ b/ranger-r-package/ranger/man/getTerminalNodeIDs.Rd
@@ -0,0 +1,25 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/getTerminalNodeIDs.R
+\name{getTerminalNodeIDs}
+\alias{getTerminalNodeIDs}
+\title{Get terminal node IDs of observations.}
+\usage{
+getTerminalNodeIDs(rf, dat)
+}
+\arguments{
+\item{rf}{\code{ranger} object.}
+
+\item{dat}{New dataset. Terminal node IDs for this dataset are obtained.}
+}
+\value{
+Matrix with terminal nodeIDs for all observations in dataset and trees.
+}
+\description{
+Get terminal node IDs of observations.
+}
+\examples{
+library(ranger)
+rf <- ranger(Species ~ ., data = iris, write.forest = TRUE)
+getTerminalNodeIDs(rf, iris)
+}
+
diff --git a/ranger-r-package/ranger/man/importance.ranger.Rd b/ranger-r-package/ranger/man/importance.ranger.Rd
new file mode 100644
index 000000000..17e6674e2
--- /dev/null
+++ b/ranger-r-package/ranger/man/importance.ranger.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/importance.R
+\name{importance.ranger}
+\alias{importance}
+\alias{importance.ranger}
+\title{Ranger variable importance}
+\usage{
+\method{importance}{ranger}(x, ...)
+}
+\arguments{
+\item{x}{Ranger object.}
+
+\item{...}{Further arguments passed to or from other methods.}
+}
+\value{
+Variable importance measures.
+}
+\description{
+Extract variable importance of Ranger object.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/predict.ranger.Rd b/ranger-r-package/ranger/man/predict.ranger.Rd
new file mode 100644
index 000000000..f92a00495
--- /dev/null
+++ b/ranger-r-package/ranger/man/predict.ranger.Rd
@@ -0,0 +1,51 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/predict.R
+\name{predict.ranger}
+\alias{predict.ranger}
+\title{Ranger prediction}
+\usage{
+\method{predict}{ranger}(object, data, predict.all = FALSE, seed = NULL,
+ num.threads = NULL, verbose = TRUE, ...)
+}
+\arguments{
+\item{object}{Ranger \code{ranger} object.}
+
+\item{data}{New test data of class \code{data.frame} or \code{gwaa.data} (GenABEL).}
+
+\item{predict.all}{Return a matrix with individual predictions for each tree instead of aggregated predictions for all trees (classification and regression only).}
+
+\item{seed}{Random seed used in Ranger.}
+
+\item{num.threads}{Number of threads. Default is number of CPUs available.}
+
+\item{verbose}{Verbose output on or off.}
+
+\item{...}{further arguments passed to or from other methods.}
+}
+\value{
+Object of class \code{ranger.prediction} with elements
+ \tabular{ll}{
+ \code{predictions} \tab Predicted classes/values (only for classification and regression) \cr
+ \code{unique.death.times} \tab Unique death times (only for survival). \cr
+ \code{chf} \tab Estimated cumulative hazard function for each sample (only for survival). \cr
+ \code{survival} \tab Estimated survival function for each sample (only for survival). \cr
+ \code{num.trees} \tab Number of trees. \cr
+ \code{num.independent.variables} \tab Number of independent variables. \cr
+ \code{treetype} \tab Type of forest/tree. Classification, regression or survival. \cr
+ \code{num.samples} \tab Number of samples.
+ }
+}
+\description{
+Prediction with new data and a saved forest from Ranger.
+}
+\details{
+For classification and predict.all = TRUE, a matrix of factor levels is returned.
+To retrieve the corresponding factor levels, use rf$forest$levels, if rf is the ranger object.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/predict.ranger.forest.Rd b/ranger-r-package/ranger/man/predict.ranger.forest.Rd
new file mode 100644
index 000000000..da86db6aa
--- /dev/null
+++ b/ranger-r-package/ranger/man/predict.ranger.forest.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/predict.R
+\name{predict.ranger.forest}
+\alias{predict.ranger.forest}
+\title{Ranger prediction}
+\usage{
+\method{predict}{ranger.forest}(object, data, predict.all = FALSE,
+ seed = NULL, num.threads = NULL, verbose = TRUE, ...)
+}
+\arguments{
+\item{object}{Ranger \code{ranger.forest} object.}
+
+\item{data}{New test data of class \code{data.frame} or \code{gwaa.data} (GenABEL).}
+
+\item{predict.all}{Return a matrix with individual predictions for each tree instead of aggregated predictions for all trees (classification and regression only).}
+
+\item{seed}{Random seed used in Ranger.}
+
+\item{num.threads}{Number of threads. Default is number of CPUs available.}
+
+\item{verbose}{Verbose output on or off.}
+
+\item{...}{further arguments passed to or from other methods.}
+}
+\value{
+Object of class \code{ranger.prediction} with elements
+ \tabular{ll}{
+ \code{predictions} \tab Predicted classes/values (only for classification and regression) \cr
+ \code{unique.death.times} \tab Unique death times (only for survival). \cr
+ \code{chf} \tab Estimated cumulative hazard function for each sample (only for survival). \cr
+ \code{survival} \tab Estimated survival function for each sample (only for survival). \cr
+ \code{num.trees} \tab Number of trees. \cr
+ \code{num.independent.variables} \tab Number of independent variables. \cr
+ \code{treetype} \tab Type of forest/tree. Classification, regression or survival. \cr
+ \code{num.samples} \tab Number of samples.
+ }
+}
+\description{
+Prediction with new data and a saved forest from Ranger.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/predictions.ranger.Rd b/ranger-r-package/ranger/man/predictions.ranger.Rd
new file mode 100644
index 000000000..6f5aee4d5
--- /dev/null
+++ b/ranger-r-package/ranger/man/predictions.ranger.Rd
@@ -0,0 +1,26 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/predictions.R
+\name{predictions.ranger}
+\alias{predictions.ranger}
+\title{Ranger predictions}
+\usage{
+\method{predictions}{ranger}(x, ...)
+}
+\arguments{
+\item{x}{Ranger object.}
+
+\item{...}{Further arguments passed to or from other methods.}
+}
+\value{
+Predictions: Classes for Classification forests, Numerical values for Regressions forests and the estimated survival functions for all individuals for Survival forests.
+}
+\description{
+Extract training data predictions of Ranger object.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/predictions.ranger.prediction.Rd b/ranger-r-package/ranger/man/predictions.ranger.prediction.Rd
new file mode 100644
index 000000000..910733637
--- /dev/null
+++ b/ranger-r-package/ranger/man/predictions.ranger.prediction.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/predictions.R
+\name{predictions.ranger.prediction}
+\alias{predictions}
+\alias{predictions.ranger.prediction}
+\title{Ranger predictions}
+\usage{
+\method{predictions}{ranger.prediction}(x, ...)
+}
+\arguments{
+\item{x}{Ranger prediction object.}
+
+\item{...}{Further arguments passed to or from other methods.}
+}
+\value{
+Predictions: Classes for Classification forests, Numerical values for Regressions forests and the estimated survival functions for all individuals for Survival forests.
+}
+\description{
+Extract predictions of Ranger prediction object.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/print.ranger.Rd b/ranger-r-package/ranger/man/print.ranger.Rd
new file mode 100644
index 000000000..63d4e7fdb
--- /dev/null
+++ b/ranger-r-package/ranger/man/print.ranger.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.ranger}
+\alias{print.ranger}
+\title{Print Ranger}
+\usage{
+\method{print}{ranger}(x, ...)
+}
+\arguments{
+\item{x}{Object of class 'ranger'.}
+
+\item{...}{Further arguments passed to or from other methods.}
+}
+\description{
+Print contents of Ranger object.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/print.ranger.forest.Rd b/ranger-r-package/ranger/man/print.ranger.forest.Rd
new file mode 100644
index 000000000..e1d96cdf5
--- /dev/null
+++ b/ranger-r-package/ranger/man/print.ranger.forest.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.ranger.forest}
+\alias{print.ranger.forest}
+\title{Print Ranger forest}
+\usage{
+\method{print}{ranger.forest}(x, ...)
+}
+\arguments{
+\item{x}{Object of class 'ranger.forest'.}
+
+\item{...}{further arguments passed to or from other methods.}
+}
+\description{
+Print contents of Ranger forest object.
+}
+\author{
+Marvin N. Wright
+}
+
diff --git a/ranger-r-package/ranger/man/print.ranger.prediction.Rd b/ranger-r-package/ranger/man/print.ranger.prediction.Rd
new file mode 100644
index 000000000..831d37469
--- /dev/null
+++ b/ranger-r-package/ranger/man/print.ranger.prediction.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.ranger.prediction}
+\alias{print.ranger.prediction}
+\title{Print Ranger prediction}
+\usage{
+\method{print}{ranger.prediction}(x, ...)
+}
+\arguments{
+\item{x}{Object of class 'ranger.prediction'.}
+
+\item{...}{further arguments passed to or from other methods.}
+}
+\description{
+Print contents of Ranger prediction object.
+}
+\author{
+Marvin N. Wright
+}
+
diff --git a/ranger-r-package/ranger/man/ranger.Rd b/ranger-r-package/ranger/man/ranger.Rd
new file mode 100644
index 000000000..22805994f
--- /dev/null
+++ b/ranger-r-package/ranger/man/ranger.Rd
@@ -0,0 +1,179 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ranger.R
+\name{ranger}
+\alias{ranger}
+\title{Ranger}
+\usage{
+ranger(formula = NULL, data = NULL, num.trees = 500, mtry = NULL,
+ importance = "none", write.forest = FALSE, probability = FALSE,
+ min.node.size = NULL, replace = TRUE, sample.fraction = ifelse(replace,
+ 1, 0.632), splitrule = NULL, case.weights = NULL,
+ split.select.weights = NULL, always.split.variables = NULL,
+ respect.unordered.factors = FALSE, scale.permutation.importance = FALSE,
+ keep.inbag = FALSE, num.threads = NULL, save.memory = FALSE,
+ verbose = TRUE, seed = NULL, dependent.variable.name = NULL,
+ status.variable.name = NULL, classification = NULL)
+}
+\arguments{
+\item{formula}{Object of class \code{formula} or \code{character} describing the model to fit.}
+
+\item{data}{Training data of class \code{data.frame}, \code{matrix} or \code{gwaa.data} (GenABEL).}
+
+\item{num.trees}{Number of trees.}
+
+\item{mtry}{Number of variables to possibly split at in each node. Default is the (rounded down) square root of the number variables.}
+
+\item{importance}{Variable importance mode, one of 'none', 'impurity', 'permutation'. The 'impurity' measure is the Gini index for classification and the variance of the responses for regression. For survival, only 'permutation' is available.}
+
+\item{write.forest}{Save \code{ranger.forest} object, needed for prediction.}
+
+\item{probability}{Grow a probability forest as in Malley et al. (2012).}
+
+\item{min.node.size}{Minimal node size. Default 1 for classification, 5 for regression, 3 for survival, and 10 for probability.}
+
+\item{replace}{Sample with replacement.}
+
+\item{sample.fraction}{Fraction of observations to sample. Default is 1 for sampling with replacement and 0.632 for sampling without replacement.}
+
+\item{splitrule}{Splitting rule, survival only. The splitting rule can be chosen of "logrank" and "C" with default "logrank".}
+
+\item{case.weights}{Weights for sampling of training observations. Observations with larger weights will be selected with higher probability in the bootstrap (or subsampled) samples for the trees.}
+
+\item{split.select.weights}{Numeric vector with weights between 0 and 1, representing the probability to select variables for splitting. Alternatively, a list of size num.trees, containing split select weight vectors for each tree can be used.}
+
+\item{always.split.variables}{Character vector with variable names to be always tried for splitting.}
+
+\item{respect.unordered.factors}{Regard unordered factor covariates as unordered categorical variables. If \code{FALSE}, all factors are regarded ordered.}
+
+\item{scale.permutation.importance}{Scale permutation importance by standard error as in (Breiman 2001). Only applicable if permutation variable importance mode selected.}
+
+\item{keep.inbag}{Save how often observations are in-bag in each tree.}
+
+\item{num.threads}{Number of threads. Default is number of CPUs available.}
+
+\item{save.memory}{Use memory saving (but slower) splitting mode. No effect for GWAS data.}
+
+\item{verbose}{Verbose output on or off.}
+
+\item{seed}{Random seed. Default is \code{NULL}, which generates the seed from \code{R}.}
+
+\item{dependent.variable.name}{Name of dependent variable, needed if no formula given. For survival forests this is the time variable.}
+
+\item{status.variable.name}{Name of status variable, only applicable to survival data and needed if no formula given. Use 1 for event and 0 for censoring.}
+
+\item{classification}{Only needed if data is a matrix. Set to \code{TRUE} to grow a classification forest.}
+}
+\value{
+Object of class \code{ranger} with elements
+ \item{\code{forest}}{Saved forest (If write.forest set to TRUE). Note that the variable IDs in the \code{split.varIDs} object do not necessarily represent the column number in R.}
+ \item{\code{predictions}}{Predicted classes/values, based on out of bag samples (classification and regression only).}
+ \item{\code{forest}}{Saved forest (If write.forest set to TRUE). Note that the variable IDs in the \code{split.varIDs} object do not necessarily represent the column number in R.}
+ \item{\code{predictions}}{Predicted classes/values, based on out of bag samples (classification and regression only).}
+ \item{\code{variable.importance}}{Variable importance for each independent variable.}
+ \item{\code{prediction.error}}{Overall out of bag prediction error. For classification this is the fraction of missclassified samples, for regression the mean squared error and for survival one minus Harrell's c-index.}
+ \item{\code{r.squared}}{R squared. Also called explained variance or coefficient of determination (regression only).}
+ \item{\code{confusion.matrix}}{Contingency table for classes and predictions based on out of bag samples (classification only).}
+ \item{\code{unique.death.times}}{Unique death times (survival only).}
+ \item{\code{chf}}{Estimated cumulative hazard function for each sample (survival only).}
+ \item{\code{survival}}{Estimated survival function for each sample (survival only).}
+ \item{\code{call}}{Function call.}
+ \item{\code{num.trees}}{Number of trees.}
+ \item{\code{num.independent.variables}}{Number of independent variables.}
+ \item{\code{mtry}}{Value of mtry used.}
+ \item{\code{min.node.size}}{Value of minimal node size used.}
+ \item{\code{treetype}}{Type of forest/tree. classification, regression or survival.}
+ \item{\code{importance.mode}}{Importance mode used.}
+ \item{\code{num.samples}}{Number of samples.}
+ \item{\code{inbag.counts}}{Number of times the observations are in-bag in the trees.}
+}
+\description{
+Ranger is a fast implementation of Random Forest (Breiman 2001) or recursive partitioning, particularly suited for high dimensional data.
+Classification, regression, and survival forests are supported.
+Classification and regression forests are implemented as in the original Random Forest (Breiman 2001), survival forests as in Random Survival Forests (Ishwaran et al. 2008).
+}
+\details{
+The tree type is determined by the type of the dependent variable.
+For factors classification trees are grown, for numeric values regression trees and for survival objects survival trees.
+The Gini index is used as splitting rule for classification, the estimated response variances for regression and the log-rank test for survival.
+For Survival the log-rank test or an AUC-based splitting rule are available.
+
+With the \code{probability} option and factor dependent variable a probability forest is grown.
+Here, the estimated response variances are used for splitting, as in regression forests.
+Predictions are class probabilities for each sample.
+For details see Malley et al. (2012).
+
+Note that for classification and regression nodes with size smaller than min.node.size can occur, like in original Random Forest.
+For survival all nodes contain at least min.node.size samples.
+Variables selected with \code{always.split.variables} are tried additionaly to the mtry variables randomly selected.
+In \code{split.select.weights} variables weighted with 0 are never selected and variables with 1 are always selected.
+Weights do not need to sum up to 1, they will be normalized later.
+The usage of \code{split.select.weights} can increase the computation times for large forests.
+
+For a large number of variables and data frame as input data the formula interface can be slow or impossible to use.
+Alternatively dependent.variable.name (and status.variable.name for survival) can be used.
+Consider setting \code{save.memory = TRUE} if you encounter memory problems for very large datasets.
+
+For GWAS data consider combining \code{ranger} with the \code{GenABEL} package.
+See the Examples section below for a demonstration using \code{Plink} data.
+All SNPs in the \code{GenABEL} object will be used for splitting.
+To use only the SNPs without sex or other covariates from the phenotype file, use \code{0} on the right hand side of the formula.
+Note that missing values are treated as an extra category while splitting.
+
+See \url{https://github.com/mnwright/ranger} for the development version.
+
+Notes:
+\itemize{
+ \item Multithreading is currently not supported for Microsoft Windows platforms.
+}
+}
+\examples{
+require(ranger)
+
+## Classification forest with default settings
+ranger(Species ~ ., data = iris)
+
+## Prediction
+train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+iris.train <- iris[train.idx, ]
+iris.test <- iris[-train.idx, ]
+rg.iris <- ranger(Species ~ ., data = iris.train, write.forest = TRUE)
+pred.iris <- predict(rg.iris, dat = iris.test)
+table(iris.test$Species, pred.iris$predictions)
+
+## Variable importance
+rg.iris <- ranger(Species ~ ., data = iris, importance = "impurity")
+rg.iris$variable.importance
+
+## Survival forest
+require(survival)
+rg.veteran <- ranger(Surv(time, status) ~ ., data = veteran)
+plot(rg.veteran$unique.death.times, rg.veteran$survival[1,])
+
+## Alternative interface
+ranger(dependent.variable.name = "Species", data = iris)
+
+\dontrun{
+## Use GenABEL interface to read Plink data into R and grow a classification forest
+## The ped and map files are not included
+library(GenABEL)
+convert.snp.ped("data.ped", "data.map", "data.raw")
+dat.gwaa <- load.gwaa.data("data.pheno", "data.raw")
+phdata(dat.gwaa)$trait <- factor(phdata(dat.gwaa)$trait)
+ranger(trait ~ ., data = dat.gwaa)
+}
+
+}
+\author{
+Marvin N. Wright
+}
+\references{
+Wright, M. N., & Ziegler, A. (2015). ranger: A fast implementation of random forests for high dimensional data in C++ and R. arXiv preprint \url{http://arxiv.org/abs/1508.04409}.
+
+ Breiman, L. (2001). Random forests. Mach Learn, 45(1), 5-32. \cr
+ Ishwaran, H., Kogalur, U. B., Blackstone, E. H., & Lauer, M. S. (2008). Random survival forests. Ann Appl Stat, 841-860. \cr
+ Malley, J. D., Kruppa, J., Dasgupta, A., Malley, K. G., & Ziegler, A. (2012). Probability machines: consistent probability estimation using nonparametric learning machines. Methods Inf Med, 51(1), 74.
+}
+\seealso{
+\code{\link{predict.ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/timepoints.ranger.Rd b/ranger-r-package/ranger/man/timepoints.ranger.Rd
new file mode 100644
index 000000000..359d01eb7
--- /dev/null
+++ b/ranger-r-package/ranger/man/timepoints.ranger.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/timepoints.R
+\name{timepoints.ranger}
+\alias{timepoints}
+\alias{timepoints.ranger}
+\title{Ranger timepoints}
+\usage{
+\method{timepoints}{ranger}(x, ...)
+}
+\arguments{
+\item{x}{Ranger Survival forest object.}
+
+\item{...}{Further arguments passed to or from other methods.}
+}
+\value{
+Unique death times
+}
+\description{
+Extract unique death times of Ranger Survival forest
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/man/timepoints.ranger.prediction.Rd b/ranger-r-package/ranger/man/timepoints.ranger.prediction.Rd
new file mode 100644
index 000000000..d7d7e95ce
--- /dev/null
+++ b/ranger-r-package/ranger/man/timepoints.ranger.prediction.Rd
@@ -0,0 +1,26 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/timepoints.R
+\name{timepoints.ranger.prediction}
+\alias{timepoints.ranger.prediction}
+\title{Ranger timepoints}
+\usage{
+\method{timepoints}{ranger.prediction}(x, ...)
+}
+\arguments{
+\item{x}{Ranger Survival prediction object.}
+
+\item{...}{Further arguments passed to or from other methods.}
+}
+\value{
+Unique death times
+}
+\description{
+Extract unique death times of Ranger Survival prediction object.
+}
+\author{
+Marvin N. Wright
+}
+\seealso{
+\code{\link{ranger}}
+}
+
diff --git a/ranger-r-package/ranger/src/.gitignore b/ranger-r-package/ranger/src/.gitignore
new file mode 100644
index 000000000..9d22eb46a
--- /dev/null
+++ b/ranger-r-package/ranger/src/.gitignore
@@ -0,0 +1,2 @@
+*.o
+*.so
diff --git a/ranger-r-package/ranger/src/AAA_check_cpp11.cpp b/ranger-r-package/ranger/src/AAA_check_cpp11.cpp
new file mode 100644
index 000000000..51a57ee2d
--- /dev/null
+++ b/ranger-r-package/ranger/src/AAA_check_cpp11.cpp
@@ -0,0 +1,7 @@
+
+#ifndef WIN_R_BUILD
+#if __cplusplus < 201103L
+#error Error: ranger requires a real C++11 compiler. You probably have to update gcc.
+#endif
+#endif
+
diff --git a/ranger-r-package/ranger/src/Data.cpp b/ranger-r-package/ranger/src/Data.cpp
new file mode 120000
index 000000000..d3f51f582
--- /dev/null
+++ b/ranger-r-package/ranger/src/Data.cpp
@@ -0,0 +1 @@
+../../../source/src/utility/Data.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/Data.h b/ranger-r-package/ranger/src/Data.h
new file mode 120000
index 000000000..f6221a638
--- /dev/null
+++ b/ranger-r-package/ranger/src/Data.h
@@ -0,0 +1 @@
+../../../source/src/utility/Data.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/DataChar.cpp b/ranger-r-package/ranger/src/DataChar.cpp
new file mode 120000
index 000000000..2f1934213
--- /dev/null
+++ b/ranger-r-package/ranger/src/DataChar.cpp
@@ -0,0 +1 @@
+../../../source/src/utility/DataChar.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/DataChar.h b/ranger-r-package/ranger/src/DataChar.h
new file mode 120000
index 000000000..a24bd6007
--- /dev/null
+++ b/ranger-r-package/ranger/src/DataChar.h
@@ -0,0 +1 @@
+../../../source/src/utility/DataChar.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/DataDouble.cpp b/ranger-r-package/ranger/src/DataDouble.cpp
new file mode 120000
index 000000000..abd697242
--- /dev/null
+++ b/ranger-r-package/ranger/src/DataDouble.cpp
@@ -0,0 +1 @@
+../../../source/src/utility/DataDouble.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/DataDouble.h b/ranger-r-package/ranger/src/DataDouble.h
new file mode 120000
index 000000000..22440b870
--- /dev/null
+++ b/ranger-r-package/ranger/src/DataDouble.h
@@ -0,0 +1 @@
+../../../source/src/utility/DataDouble.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/DataFloat.cpp b/ranger-r-package/ranger/src/DataFloat.cpp
new file mode 120000
index 000000000..2a007274e
--- /dev/null
+++ b/ranger-r-package/ranger/src/DataFloat.cpp
@@ -0,0 +1 @@
+../../../source/src/utility/DataFloat.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/DataFloat.h b/ranger-r-package/ranger/src/DataFloat.h
new file mode 120000
index 000000000..b78f5bdef
--- /dev/null
+++ b/ranger-r-package/ranger/src/DataFloat.h
@@ -0,0 +1 @@
+../../../source/src/utility/DataFloat.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/Forest.cpp b/ranger-r-package/ranger/src/Forest.cpp
new file mode 120000
index 000000000..f940c7802
--- /dev/null
+++ b/ranger-r-package/ranger/src/Forest.cpp
@@ -0,0 +1 @@
+../../../source/src/Forest/Forest.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/Forest.h b/ranger-r-package/ranger/src/Forest.h
new file mode 120000
index 000000000..4f00ccab7
--- /dev/null
+++ b/ranger-r-package/ranger/src/Forest.h
@@ -0,0 +1 @@
+../../../source/src/Forest/Forest.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestClassification.cpp b/ranger-r-package/ranger/src/ForestClassification.cpp
new file mode 120000
index 000000000..02dc0367e
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestClassification.cpp
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestClassification.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestClassification.h b/ranger-r-package/ranger/src/ForestClassification.h
new file mode 120000
index 000000000..ea97b60bf
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestClassification.h
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestClassification.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestProbability.cpp b/ranger-r-package/ranger/src/ForestProbability.cpp
new file mode 120000
index 000000000..3dd8065b0
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestProbability.cpp
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestProbability.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestProbability.h b/ranger-r-package/ranger/src/ForestProbability.h
new file mode 120000
index 000000000..0ed02dd2b
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestProbability.h
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestProbability.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestRegression.cpp b/ranger-r-package/ranger/src/ForestRegression.cpp
new file mode 120000
index 000000000..7fe0d22e9
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestRegression.cpp
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestRegression.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestRegression.h b/ranger-r-package/ranger/src/ForestRegression.h
new file mode 120000
index 000000000..706357809
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestRegression.h
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestRegression.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestSurvival.cpp b/ranger-r-package/ranger/src/ForestSurvival.cpp
new file mode 120000
index 000000000..96951aef8
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestSurvival.cpp
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestSurvival.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/ForestSurvival.h b/ranger-r-package/ranger/src/ForestSurvival.h
new file mode 120000
index 000000000..01a400a66
--- /dev/null
+++ b/ranger-r-package/ranger/src/ForestSurvival.h
@@ -0,0 +1 @@
+../../../source/src/Forest/ForestSurvival.h
\ No newline at end of file
diff --git a/R_package/Makevars.win b/ranger-r-package/ranger/src/Makevars
similarity index 54%
rename from R_package/Makevars.win
rename to ranger-r-package/ranger/src/Makevars
index 64cf9cd65..89fd506db 100644
--- a/R_package/Makevars.win
+++ b/ranger-r-package/ranger/src/Makevars
@@ -1,3 +1,4 @@
## Use c++11
CXX_STD = CXX11
+PKG_CPPFLAGS = -DR_BUILD
diff --git a/ranger-r-package/ranger/src/Makevars.win b/ranger-r-package/ranger/src/Makevars.win
new file mode 100644
index 000000000..1c1fd15b4
--- /dev/null
+++ b/ranger-r-package/ranger/src/Makevars.win
@@ -0,0 +1,4 @@
+## Use c++11
+CXX_STD = CXX11
+PKG_CPPFLAGS = -DR_BUILD -DWIN_R_BUILD
+
diff --git a/ranger-r-package/ranger/src/RcppExports.cpp b/ranger-r-package/ranger/src/RcppExports.cpp
new file mode 100644
index 000000000..2bf9183ec
--- /dev/null
+++ b/ranger-r-package/ranger/src/RcppExports.cpp
@@ -0,0 +1,49 @@
+// This file was generated by Rcpp::compileAttributes
+// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
+
+#include "../inst/include/ranger.h"
+#include
+
+using namespace Rcpp;
+
+// rangerCpp
+Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, Rcpp::NumericMatrix input_data, std::vector variable_names, uint mtry, uint num_trees, bool verbose, uint seed, uint num_threads, bool write_forest, uint importance_mode_r, uint min_node_size, std::vector>& split_select_weights, bool use_split_select_weights, std::vector& always_split_variable_names, bool use_always_split_variable_names, std::string status_variable_name, bool prediction_mode, Rcpp::List loaded_forest, Rcpp::RawMatrix sparse_data, bool sample_with_replacement, bool probability, std::vector& unordered_variable_names, bool use_unordered_variable_names, bool save_memory, uint splitrule_r, std::vector& case_weights, bool use_case_weights, bool predict_all, bool keep_inbag, double sample_fraction);
+RcppExport SEXP ranger_rangerCpp(SEXP treetypeSEXP, SEXP dependent_variable_nameSEXP, SEXP input_dataSEXP, SEXP variable_namesSEXP, SEXP mtrySEXP, SEXP num_treesSEXP, SEXP verboseSEXP, SEXP seedSEXP, SEXP num_threadsSEXP, SEXP write_forestSEXP, SEXP importance_mode_rSEXP, SEXP min_node_sizeSEXP, SEXP split_select_weightsSEXP, SEXP use_split_select_weightsSEXP, SEXP always_split_variable_namesSEXP, SEXP use_always_split_variable_namesSEXP, SEXP status_variable_nameSEXP, SEXP prediction_modeSEXP, SEXP loaded_forestSEXP, SEXP sparse_dataSEXP, SEXP sample_with_replacementSEXP, SEXP probabilitySEXP, SEXP unordered_variable_namesSEXP, SEXP use_unordered_variable_namesSEXP, SEXP save_memorySEXP, SEXP splitrule_rSEXP, SEXP case_weightsSEXP, SEXP use_case_weightsSEXP, SEXP predict_allSEXP, SEXP keep_inbagSEXP, SEXP sample_fractionSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject __result;
+ Rcpp::RNGScope __rngScope;
+ Rcpp::traits::input_parameter< uint >::type treetype(treetypeSEXP);
+ Rcpp::traits::input_parameter< std::string >::type dependent_variable_name(dependent_variable_nameSEXP);
+ Rcpp::traits::input_parameter< Rcpp::NumericMatrix >::type input_data(input_dataSEXP);
+ Rcpp::traits::input_parameter< std::vector >::type variable_names(variable_namesSEXP);
+ Rcpp::traits::input_parameter< uint >::type mtry(mtrySEXP);
+ Rcpp::traits::input_parameter< uint >::type num_trees(num_treesSEXP);
+ Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP);
+ Rcpp::traits::input_parameter< uint >::type seed(seedSEXP);
+ Rcpp::traits::input_parameter< uint >::type num_threads(num_threadsSEXP);
+ Rcpp::traits::input_parameter< bool >::type write_forest(write_forestSEXP);
+ Rcpp::traits::input_parameter< uint >::type importance_mode_r(importance_mode_rSEXP);
+ Rcpp::traits::input_parameter< uint >::type min_node_size(min_node_sizeSEXP);
+ Rcpp::traits::input_parameter< std::vector>& >::type split_select_weights(split_select_weightsSEXP);
+ Rcpp::traits::input_parameter< bool >::type use_split_select_weights(use_split_select_weightsSEXP);
+ Rcpp::traits::input_parameter< std::vector& >::type always_split_variable_names(always_split_variable_namesSEXP);
+ Rcpp::traits::input_parameter< bool >::type use_always_split_variable_names(use_always_split_variable_namesSEXP);
+ Rcpp::traits::input_parameter< std::string >::type status_variable_name(status_variable_nameSEXP);
+ Rcpp::traits::input_parameter< bool >::type prediction_mode(prediction_modeSEXP);
+ Rcpp::traits::input_parameter< Rcpp::List >::type loaded_forest(loaded_forestSEXP);
+ Rcpp::traits::input_parameter< Rcpp::RawMatrix >::type sparse_data(sparse_dataSEXP);
+ Rcpp::traits::input_parameter< bool >::type sample_with_replacement(sample_with_replacementSEXP);
+ Rcpp::traits::input_parameter< bool >::type probability(probabilitySEXP);
+ Rcpp::traits::input_parameter< std::vector& >::type unordered_variable_names(unordered_variable_namesSEXP);
+ Rcpp::traits::input_parameter< bool >::type use_unordered_variable_names(use_unordered_variable_namesSEXP);
+ Rcpp::traits::input_parameter< bool >::type save_memory(save_memorySEXP);
+ Rcpp::traits::input_parameter< uint >::type splitrule_r(splitrule_rSEXP);
+ Rcpp::traits::input_parameter< std::vector& >::type case_weights(case_weightsSEXP);
+ Rcpp::traits::input_parameter< bool >::type use_case_weights(use_case_weightsSEXP);
+ Rcpp::traits::input_parameter< bool >::type predict_all(predict_allSEXP);
+ Rcpp::traits::input_parameter< bool >::type keep_inbag(keep_inbagSEXP);
+ Rcpp::traits::input_parameter< double >::type sample_fraction(sample_fractionSEXP);
+ __result = Rcpp::wrap(rangerCpp(treetype, dependent_variable_name, input_data, variable_names, mtry, num_trees, verbose, seed, num_threads, write_forest, importance_mode_r, min_node_size, split_select_weights, use_split_select_weights, always_split_variable_names, use_always_split_variable_names, status_variable_name, prediction_mode, loaded_forest, sparse_data, sample_with_replacement, probability, unordered_variable_names, use_unordered_variable_names, save_memory, splitrule_r, case_weights, use_case_weights, predict_all, keep_inbag, sample_fraction));
+ return __result;
+END_RCPP
+}
diff --git a/ranger-r-package/ranger/src/Tree.cpp b/ranger-r-package/ranger/src/Tree.cpp
new file mode 120000
index 000000000..e19f8b2e7
--- /dev/null
+++ b/ranger-r-package/ranger/src/Tree.cpp
@@ -0,0 +1 @@
+../../../source/src/Tree/Tree.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/Tree.h b/ranger-r-package/ranger/src/Tree.h
new file mode 120000
index 000000000..53aea3158
--- /dev/null
+++ b/ranger-r-package/ranger/src/Tree.h
@@ -0,0 +1 @@
+../../../source/src/Tree/Tree.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeClassification.cpp b/ranger-r-package/ranger/src/TreeClassification.cpp
new file mode 120000
index 000000000..5c06cfd8b
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeClassification.cpp
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeClassification.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeClassification.h b/ranger-r-package/ranger/src/TreeClassification.h
new file mode 120000
index 000000000..4791422ea
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeClassification.h
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeClassification.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeProbability.cpp b/ranger-r-package/ranger/src/TreeProbability.cpp
new file mode 120000
index 000000000..f99a90401
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeProbability.cpp
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeProbability.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeProbability.h b/ranger-r-package/ranger/src/TreeProbability.h
new file mode 120000
index 000000000..7b66d671c
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeProbability.h
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeProbability.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeRegression.cpp b/ranger-r-package/ranger/src/TreeRegression.cpp
new file mode 120000
index 000000000..46fce733b
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeRegression.cpp
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeRegression.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeRegression.h b/ranger-r-package/ranger/src/TreeRegression.h
new file mode 120000
index 000000000..51d33478a
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeRegression.h
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeRegression.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeSurvival.cpp b/ranger-r-package/ranger/src/TreeSurvival.cpp
new file mode 120000
index 000000000..c84d90350
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeSurvival.cpp
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeSurvival.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/TreeSurvival.h b/ranger-r-package/ranger/src/TreeSurvival.h
new file mode 120000
index 000000000..409cab04b
--- /dev/null
+++ b/ranger-r-package/ranger/src/TreeSurvival.h
@@ -0,0 +1 @@
+../../../source/src/Tree/TreeSurvival.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/globals.h b/ranger-r-package/ranger/src/globals.h
new file mode 120000
index 000000000..56062b35c
--- /dev/null
+++ b/ranger-r-package/ranger/src/globals.h
@@ -0,0 +1 @@
+../../../source/src/globals.h
\ No newline at end of file
diff --git a/R_package/rangerCpp.cpp b/ranger-r-package/ranger/src/rangerCpp.cpp
similarity index 70%
rename from R_package/rangerCpp.cpp
rename to ranger-r-package/ranger/src/rangerCpp.cpp
index ca3a3f0f4..c03d7a2f4 100644
--- a/R_package/rangerCpp.cpp
+++ b/ranger-r-package/ranger/src/rangerCpp.cpp
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
+ This file is part of Ranger.
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Written by:
+ Written by:
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#include
#include
@@ -42,13 +42,16 @@ wright@imbs.uni-luebeck.de
#include "DataFloat.h"
// [[Rcpp::export]]
-Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint memory_mode_r,
+Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name,
Rcpp::NumericMatrix input_data, std::vector variable_names, uint mtry, uint num_trees, bool verbose,
uint seed, uint num_threads, bool write_forest, uint importance_mode_r, uint min_node_size,
- std::vector& split_select_weights, bool use_split_select_weights,
+ std::vector>& split_select_weights, bool use_split_select_weights,
std::vector& always_split_variable_names, bool use_always_split_variable_names,
std::string status_variable_name, bool prediction_mode, Rcpp::List loaded_forest, Rcpp::RawMatrix sparse_data,
- bool sample_with_replacement, bool probability, uint splitrule) {
+ bool sample_with_replacement, bool probability, std::vector& unordered_variable_names,
+ bool use_unordered_variable_names, bool save_memory, uint splitrule_r,
+ std::vector& case_weights, bool use_case_weights, bool predict_all,
+ bool keep_inbag, double sample_fraction) {
Rcpp::List result;
Forest* forest = 0;
@@ -62,6 +65,12 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
if (!use_always_split_variable_names) {
always_split_variable_names.clear();
}
+ if (!use_unordered_variable_names) {
+ unordered_variable_names.clear();
+ }
+ if (!use_case_weights) {
+ case_weights.clear();
+ }
std::ostream* verbose_out;
if (verbose) {
@@ -73,24 +82,8 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
size_t num_rows = input_data.nrow();
size_t num_cols = input_data.ncol();
- // Initialize data with memmode
- MemoryMode memory_mode = (MemoryMode) memory_mode_r;
- switch (memory_mode) {
- case MEM_DOUBLE:
- data = new DataDouble(input_data.begin(), variable_names, num_rows, num_cols);
- break;
- case MEM_FLOAT:
- data = new DataFloat(input_data.begin(), variable_names, num_rows, num_cols);
- break;
- case MEM_CHAR:
- bool error;
- data = new DataChar(input_data.begin(), variable_names, num_rows, num_cols, error);
- if (error) {
- *verbose_out << "Warning: Rounding or Integer overflow occurred. Use FLOAT or DOUBLE precision to avoid this."
- << std::endl;
- }
- break;
- }
+ // Initialize data with double memmode
+ data = new DataDouble(input_data.begin(), variable_names, num_rows, num_cols);
// If there is sparse data, add it
if (sparse_data.nrow() > 1) {
@@ -117,11 +110,13 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
}
ImportanceMode importance_mode = (ImportanceMode) importance_mode_r;
+ SplitRule splitrule = (SplitRule) splitrule_r;
// Init Ranger
- forest->initR(dependent_variable_name, memory_mode, data, mtry, num_trees, verbose_out, seed, num_threads,
+ forest->initR(dependent_variable_name, data, mtry, num_trees, verbose_out, seed, true, num_threads,
importance_mode, min_node_size, split_select_weights, always_split_variable_names, status_variable_name,
- prediction_mode, sample_with_replacement, splitrule);
+ prediction_mode, sample_with_replacement, unordered_variable_names, save_memory, splitrule, case_weights,
+ predict_all, keep_inbag, sample_fraction);
// Load forest object if in prediction mode
if (prediction_mode) {
@@ -130,25 +125,27 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
std::vector> > child_nodeIDs = loaded_forest["child.nodeIDs"];
std::vector> split_varIDs = loaded_forest["split.varIDs"];
std::vector> split_values = loaded_forest["split.values"];
+ std::vector is_ordered = loaded_forest["is.ordered"];
if (treetype == TREE_CLASSIFICATION) {
std::vector class_values = loaded_forest["class.values"];
((ForestClassification*) forest)->loadForest(dependent_varID, num_trees, child_nodeIDs, split_varIDs,
- split_values, class_values);
+ split_values, class_values, is_ordered);
} else if (treetype == TREE_REGRESSION) {
- ((ForestRegression*) forest)->loadForest(dependent_varID, num_trees, child_nodeIDs, split_varIDs, split_values);
+ ((ForestRegression*) forest)->loadForest(dependent_varID, num_trees, child_nodeIDs, split_varIDs, split_values,
+ is_ordered);
} else if (treetype == TREE_SURVIVAL) {
size_t status_varID = loaded_forest["status.varID"];
std::vector> > chf = loaded_forest["chf"];
std::vector unique_timepoints = loaded_forest["unique.death.times"];
((ForestSurvival*) forest)->loadForest(dependent_varID, num_trees, child_nodeIDs, split_varIDs, split_values,
- status_varID, chf, unique_timepoints);
+ status_varID, chf, unique_timepoints, is_ordered);
} else if (treetype == TREE_PROBABILITY) {
std::vector class_values = loaded_forest["class.values"];
- std::vector>> terminal_class_counts =
- loaded_forest["terminal.class.counts"];
+ std::vector>>terminal_class_counts =
+ loaded_forest["terminal.class.counts"];
((ForestProbability*) forest)->loadForest(dependent_varID, num_trees, child_nodeIDs, split_varIDs, split_values,
- class_values, terminal_class_counts);
+ class_values, terminal_class_counts, is_ordered);
}
}
@@ -180,6 +177,10 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
result.push_back(forest->getVariableImportance(), "variable.importance");
result.push_back(forest->getOverallPredictionError(), "prediction.error");
}
+
+ if (keep_inbag) {
+ result.push_back(forest->getInbagCounts(), "inbag.counts");
+ }
// Save forest if needed
if (write_forest) {
@@ -189,6 +190,7 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
forest_object.push_back(forest->getChildNodeIDs(), "child.nodeIDs");
forest_object.push_back(forest->getSplitVarIDs(), "split.varIDs");
forest_object.push_back(forest->getSplitValues(), "split.values");
+ forest_object.push_back(forest->getIsOrderedVariable(), "is.ordered");
if (treetype == TREE_CLASSIFICATION) {
ForestClassification* temp = (ForestClassification*) forest;
@@ -209,7 +211,9 @@ Rcpp::List rangerCpp(uint treetype, std::string dependent_variable_name, uint me
delete forest;
delete data;
} catch (std::exception& e) {
- Rcpp::Rcerr << "Error: " << e.what() << " Ranger will EXIT now." << std::endl;
+ if (strcmp(e.what(), "User interrupt.") != 0) {
+ Rcpp::Rcerr << "Error: " << e.what() << " Ranger will EXIT now." << std::endl;
+ }
delete forest;
delete data;
return result;
diff --git a/ranger-r-package/ranger/src/utility.cpp b/ranger-r-package/ranger/src/utility.cpp
new file mode 120000
index 000000000..f4022a5b2
--- /dev/null
+++ b/ranger-r-package/ranger/src/utility.cpp
@@ -0,0 +1 @@
+../../../source/src/utility/utility.cpp
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/utility.h b/ranger-r-package/ranger/src/utility.h
new file mode 120000
index 000000000..63356a925
--- /dev/null
+++ b/ranger-r-package/ranger/src/utility.h
@@ -0,0 +1 @@
+../../../source/src/utility/utility.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/src/version.h b/ranger-r-package/ranger/src/version.h
new file mode 120000
index 000000000..176edb744
--- /dev/null
+++ b/ranger-r-package/ranger/src/version.h
@@ -0,0 +1 @@
+../../../source/src/version.h
\ No newline at end of file
diff --git a/ranger-r-package/ranger/tests/test_gwaa.Rds b/ranger-r-package/ranger/tests/test_gwaa.Rds
new file mode 100644
index 000000000..8b343a66a
Binary files /dev/null and b/ranger-r-package/ranger/tests/test_gwaa.Rds differ
diff --git a/ranger-r-package/ranger/tests/testthat.R b/ranger-r-package/ranger/tests/testthat.R
new file mode 100644
index 000000000..064741d6f
--- /dev/null
+++ b/ranger-r-package/ranger/tests/testthat.R
@@ -0,0 +1,4 @@
+library(testthat)
+library(ranger)
+
+test_check("ranger")
\ No newline at end of file
diff --git a/ranger-r-package/ranger/tests/testthat/test_csrf.R b/ranger-r-package/ranger/tests/testthat/test_csrf.R
new file mode 100644
index 000000000..8725f2e9a
--- /dev/null
+++ b/ranger-r-package/ranger/tests/testthat/test_csrf.R
@@ -0,0 +1,30 @@
+library(ranger)
+library(survival)
+
+context("case-specific RF")
+
+test_that("csrf classification returns predictions", {
+ train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+ iris.train <- iris[train.idx, ]
+ iris.test <- iris[-train.idx, ]
+
+ pred <- csrf(Species ~ ., training_data = iris.train, test_data = iris.test,
+ params1 = list(num.trees = 10),
+ params2 = list(num.trees = 3))
+
+ expect_that(pred, is_a("factor"))
+ expect_that(length(pred), equals(nrow(iris)/3))
+})
+
+test_that("csrf regression returns predictions", {
+ train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+ iris.train <- iris[train.idx, ]
+ iris.test <- iris[-train.idx, ]
+
+ pred <- csrf(Sepal.Length ~ ., training_data = iris.train, test_data = iris.test,
+ params1 = list(num.trees = 10),
+ params2 = list(num.trees = 3))
+
+ expect_that(pred, is_a("numeric"))
+ expect_that(length(pred), equals(nrow(iris)/3))
+})
\ No newline at end of file
diff --git a/ranger-r-package/ranger/tests/testthat/test_ranger.R b/ranger-r-package/ranger/tests/testthat/test_ranger.R
new file mode 100644
index 000000000..38996f589
--- /dev/null
+++ b/ranger-r-package/ranger/tests/testthat/test_ranger.R
@@ -0,0 +1,575 @@
+library(ranger)
+library(survival)
+
+context("ranger")
+
+rg.class <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE)
+rg.reg <- ranger(Sepal.Length ~ ., data = iris, verbose = FALSE, write.forest = TRUE)
+rg.surv <- ranger(Surv(time, status) ~ ., data = veteran, verbose = FALSE, write.forest = TRUE)
+
+#TODO: This would require the GenABEL package in suggests
+# dat.gwaa <- readRDS("../test_gwaa.Rds")
+# rg.gwaa <- ranger(CHD ~ ., data = dat.gwaa, verbose = FALSE, write.forest = TRUE)
+
+test_that("classification result is of class ranger with 14 elements", {
+ expect_that(rg.class, is_a("ranger"))
+ expect_that(length(rg.class), equals(14))
+})
+
+test_that("regression result is of class ranger with 14 elements", {
+ expect_that(rg.reg, is_a("ranger"))
+ expect_that(length(rg.reg), equals(14))
+})
+
+test_that("survival result is of class ranger with 15 elements", {
+ expect_that(rg.surv, is_a("ranger"))
+ expect_that(length(rg.surv), equals(15))
+})
+
+#TODO: This would require the GenABEL package in suggests
+# test_that("classification gwaa rf is of class ranger with 14 elements", {
+# expect_that(rg.gwaa, is_a("ranger"))
+# expect_that(length(rg.gwaa), equals(14))
+# })
+
+test_that("results have 500 trees", {
+ expect_that(rg.class$num.trees, equals(500))
+ expect_that(rg.reg$num.trees, equals(500))
+ expect_that(rg.surv$num.trees, equals(500))
+})
+
+test_that("results have right number of independent variables", {
+ expect_that(rg.class$num.independent.variables, equals(ncol(iris) - 1))
+ expect_that(rg.reg$num.independent.variables, equals(ncol(iris) - 1))
+ expect_that(rg.surv$num.independent.variables, equals(ncol(veteran) - 2))
+})
+
+test_that("unique death times in survival result is right", {
+ expect_that(rg.surv$unique.death.times, equals(sort(unique(veteran$time))))
+})
+
+test_that("importance measures work", {
+ rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
+ importance = "impurity")
+ expect_that(rg.imp$variable.importance, is_a("numeric"))
+ rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
+ importance = "permutation")
+ expect_that(rg.imp$variable.importance, is_a("numeric"))
+ rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
+ importance = "permutation", scale.permutation.importance = TRUE)
+ expect_that(rg.imp$variable.importance, is_a("numeric"))
+})
+
+test_that("gini importance is larger than 1", {
+ rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
+ importance = "impurity")
+ expect_that(rg.imp$variable.importance[1], is_more_than(1))
+})
+
+test_that("unscaled importance is smaller than 1", {
+ rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
+ importance = "permutation", scale.permutation.importance = FALSE)
+ expect_that(rg.imp$variable.importance[1], is_less_than(1))
+})
+
+test_that("scaled importance is larger than 1", {
+ rg.imp <- ranger(Species ~ ., data = iris, verbose = FALSE, write.forest = TRUE,
+ importance = "permutation", scale.permutation.importance = TRUE)
+ expect_that(rg.imp$variable.importance[1], is_more_than(1))
+})
+
+test_that("probability estimations are a matrix with correct size", {
+ train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+ iris.train <- iris[train.idx, ]
+ iris.test <- iris[-train.idx, ]
+
+ rg.prob <- ranger(Species ~ ., data = iris.train, write.forest = TRUE, probability = TRUE)
+ prob <- predict(rg.prob, iris.test)
+
+ expect_that(prob$predictions, is_a("matrix"))
+ expect_that(nrow(prob$predictions), equals(nrow(iris.test)))
+ expect_that(ncol(prob$predictions), equals(length(rg.prob$forest$levels)))
+})
+
+test_that("probability estimations are between 0 and 1 and sum to 1", {
+ train.idx <- sample(nrow(iris), 2/3 * nrow(iris))
+ iris.train <- iris[train.idx, ]
+ iris.test <- iris[-train.idx, ]
+
+ rg.prob <- ranger(Species ~ ., data = iris.train, write.forest = TRUE, probability = TRUE)
+ prob <- predict(rg.prob, iris.test)
+
+ expect_that(all(prob$predictions > -1e-5 & prob$predictions <= 1 + 1e-5), is_true())
+ expect_that(rowSums(prob$predictions), equals(rep(1, nrow(prob$predictions))))
+})
+
+test_that("predict returns good prediction", {
+ rf <- ranger(Species ~ ., iris, write.forest = TRUE)
+ pred <- predict(rf, iris)
+ expect_that(mean(iris$Species == predictions(pred)), is_more_than(0.9))
+})
+
+test_that("Alternative interface works for classification", {
+ rf <- ranger(dependent.variable.name = "Species", data = iris)
+ expect_that(rf$treetype, equals("Classification"))
+})
+
+test_that("Alternative interface works for regression", {
+ rf <- ranger(dependent.variable.name = "Sepal.Length", data = iris)
+ expect_that(rf$treetype, equals("Regression"))
+})
+
+test_that("Alternative interface works for survival", {
+ rf <- ranger(dependent.variable.name = "time", status.variable.name = "status", data = veteran)
+ expect_that(rf$treetype, equals("Survival"))
+})
+
+test_that("Matrix interface works for classification", {
+ rf <- ranger(dependent.variable.name = "Species", data = data.matrix(iris), write.forest = TRUE, classification = TRUE)
+ expect_that(rf$treetype, equals("Classification"))
+ expect_that(rf$forest$independent.variable.names, equals(colnames(iris)[1:4]))
+})
+
+test_that("Matrix interface prediction works for classification", {
+ dat <- data.matrix(iris)
+ rf <- ranger(dependent.variable.name = "Species", data = dat, write.forest = TRUE, classification = TRUE)
+ expect_that(predict(rf, dat), not(throws_error()))
+})
+
+test_that("Matrix interface works for regression", {
+ rf <- ranger(dependent.variable.name = "Sepal.Length", data = data.matrix(iris), write.forest = TRUE)
+ expect_that(rf$treetype, equals("Regression"))
+ expect_that(rf$forest$independent.variable.names, equals(colnames(iris)[2:5]))
+})
+
+test_that("Matrix interface prediction works for regression", {
+ dat <- data.matrix(iris)
+ rf <- ranger(dependent.variable.name = "Sepal.Length", data = dat, write.forest = TRUE)
+ expect_that(predict(rf, dat), not(throws_error()))
+})
+
+test_that("Matrix interface works for survival", {
+ rf <- ranger(dependent.variable.name = "time", status.variable.name = "status", data = data.matrix(veteran), write.forest = TRUE)
+ expect_that(rf$treetype, equals("Survival"))
+ expect_that(rf$forest$independent.variable.names, equals(colnames(veteran)[c(1:2, 5:8)]))
+})
+
+test_that("Matrix interface prediction works for survival", {
+ dat <- data.matrix(veteran)
+ rf <- ranger(dependent.variable.name = "time", status.variable.name = "status", data = dat, write.forest = TRUE)
+ expect_that(predict(rf, dat), not(throws_error()))
+})
+
+test_that("save.memory option works for classification", {
+ rf <- ranger(Species ~ ., data = iris, save.memory = TRUE)
+ expect_that(rf$treetype, equals("Classification"))
+})
+
+test_that("save.memory option works for regression", {
+ rf <- ranger(Sepal.Length ~ ., data = iris, save.memory = TRUE)
+ expect_that(rf$treetype, equals("Regression"))
+})
+
+test_that("save.memory option works for probability", {
+ rf <- ranger(Species ~ ., data = iris, probability = TRUE, save.memory = TRUE)
+ expect_that(rf$treetype, equals("Probability estimation"))
+})
+
+test_that("C-index splitting works", {
+ rf <- ranger(Surv(time, status) ~ ., data = veteran, verbose = FALSE,
+ splitrule = "C")
+ expect_that(rf$treetype, equals("Survival"))
+})
+
+test_that("predict works for single observations, classification", {
+ pred <- predict(rg.class, head(iris, 1))
+ expect_that(pred$predictions, equals(iris[1,"Species"]))
+})
+
+test_that("predict works for single observations, probability prediction", {
+ rf <- ranger(Species ~ ., iris, write.forest = TRUE, probability = TRUE)
+ pred <- predict(rf, head(iris, 1))
+ expect_that(names(which.max(pred$predictions)), equals(as.character(iris[1,"Species"])))
+})
+
+test_that("predict works for single observations, survival", {
+ rf <- ranger(Surv(time, status) ~ ., veteran, write.forest = TRUE)
+ pred <- predict(rf, head(veteran, 1))
+ expect_that(length(pred$survival), equals(length(rf$unique.death.times)))
+})
+
+test_that("same result with same seed", {
+ ind = 1:150 %in% sample(150, 100)
+
+ set.seed(2)
+ mod1 = ranger(Species ~ ., data = iris[ind, ], write.forest = TRUE, num.trees = 50)
+ pred1 = predict(mod1, data = iris[!ind, ])
+
+ set.seed(2)
+ mod2 = ranger(Species ~ ., data = iris[ind, ], write.forest = TRUE, num.trees = 50)
+ pred2 = predict(mod2, data = iris[!ind, ])
+
+ expect_that(pred1$predictions, equals(pred2$predictions))
+})
+
+test_that("same result with same seed, different interface", {
+ ind = 1:150 %in% sample(150, 100)
+
+ set.seed(2)
+ mod1 = ranger(Species ~ ., data = iris[ind, ], write.forest = TRUE, num.trees = 50)
+ pred1 = predict(mod1, data = iris[!ind, ])
+
+ set.seed(2)
+ mod3 = ranger(dependent.variable.name = "Species", data = iris[ind, ], write.forest = TRUE, num.trees = 50)
+ pred3 = predict(mod3, data = iris[!ind, ])
+
+ expect_that(pred1$predictions, equals(pred3$predictions))
+})
+
+test_that("no warning if data.frame has to classes", {
+ dat <- iris
+ class(dat) <- c("data.frame", "data.table")
+ expect_that(ranger(Species ~ ., data = dat, verbose = FALSE),
+ not(gives_warning()))
+})
+
+test_that("no warning if character vector in data", {
+ dat <- iris
+ dat$Test <- paste0("AA",as.character(1:nrow(dat)))
+ expect_that(ranger(Species ~ ., data = dat, verbose = FALSE),
+ not(gives_warning()))
+})
+
+test_that("no error if character vector in data, prediction", {
+ dat <- iris
+ dat$Test <- paste0("AA",as.character(1:nrow(dat)))
+ rf <- ranger(Species~., dat, write.forest = TRUE)
+ expect_that(predict(rf, dat),
+ not(throws_error()))
+})
+
+test_that("no warning if character vector in data, alternative interface", {
+ dat <- iris
+ dat$Test <- paste0("AA",as.character(1:nrow(dat)))
+ expect_that(ranger(dependent.variable.name = "Species", data = dat, verbose = FALSE),
+ not(gives_warning()))
+})
+
+test_that("no error if character vector in data, alternative interface, prediction", {
+ dat <- iris
+ dat$Test <- paste0("AA",as.character(1:nrow(dat)))
+ rf <- ranger(dependent.variable.name = "Species", data = dat, verbose = FALSE, write.forest = TRUE)
+ expect_that(predict(rf, dat),
+ not(throws_error()))
+})
+
+test_that("confusion matrix is of right dimension", {
+ expect_that(dim(rg.class$confusion.matrix),
+ equals(rep(nlevels(iris$Species), 2)))
+})
+
+test_that("confusion matrix rows are the true classes", {
+ expect_that(as.numeric(rowSums(rg.class$confusion.matrix)),
+ equals(as.numeric(table(iris$Species))))
+})
+
+test_that("case weights work", {
+ expect_that(ranger(Species ~ ., iris, num.trees = 5, case.weights = rep(1, nrow(iris))),
+ not(throws_error()))
+
+ ## Should only predict setosa now
+ weights <- c(rep(1, 50), rep(0, 100))
+ rf <- ranger(Species ~ ., iris, num.trees = 5, case.weights = weights, write.forest = TRUE)
+ pred <- predict(rf, iris)$predictions
+ expect_that(all(pred == "setosa"), is_true())
+})
+
+test_that("predict.all for classification returns numeric matrix of size trees x n", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, write.forest = TRUE)
+ pred <- predict(rf, iris, predict.all = TRUE)
+ expect_that(pred$predictions, is_a("matrix"))
+ expect_that(dim(pred$predictions),
+ equals(c(nrow(iris), rf$num.trees)))
+})
+
+test_that("predict.all for regression returns numeric matrix of size n x trees", {
+ rf <- ranger(Petal.Width ~ ., iris, num.trees = 5, write.forest = TRUE)
+ pred <- predict(rf, iris, predict.all = TRUE)
+ expect_that(pred$predictions, is_a("matrix"))
+ expect_that(dim(pred$predictions),
+ equals(c(nrow(iris), rf$num.trees)))
+})
+
+test_that("Majority vote of predict.all for classification is equal to forest prediction", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, write.forest = TRUE)
+ pred_forest <- predict(rf, iris, predict.all = FALSE)
+ pred_trees <- predict(rf, iris, predict.all = TRUE)
+
+ ## Majority vote
+ pred_num <- apply(pred_trees$predictions, 1, function(x) {
+ which(tabulate(x) == max(tabulate(x)))
+ })
+ pred <- factor(pred_num, levels = 1:length(rf$forest$levels),
+ labels = rf$forest$levels)
+
+ expect_that(pred, equals(pred_forest$predictions))
+})
+
+test_that("Mean of predict.all for regression is equal to forest prediction", {
+ rf <- ranger(Petal.Width ~ ., iris, num.trees = 5, write.forest = TRUE)
+ pred_forest <- predict(rf, iris, predict.all = FALSE)
+ pred_trees <- predict(rf, iris, predict.all = TRUE)
+ expect_that(rowMeans(pred_trees$predictions), equals(pred_forest$predictions))
+})
+
+test_that("split select weights work", {
+ expect_that(ranger(Species ~ ., iris, num.trees = 5, split.select.weights = c(0.1, 0.2, 0.3, 0.4)),
+ not(throws_error()))
+ expect_that(ranger(Species ~ ., iris, num.trees = 5, split.select.weights = c(0.1, 0.2, 0.3)),
+ throws_error())
+})
+
+test_that("Tree-wise split select weights work", {
+ num.trees <- 5
+ weights <- replicate(num.trees, runif(ncol(iris)-1), simplify = FALSE)
+ expect_that(ranger(Species ~ ., iris, num.trees = num.trees, split.select.weights = weights),
+ not(throws_error()))
+
+ weights <- replicate(num.trees+1, runif(ncol(iris)-1), simplify = FALSE)
+ expect_that(ranger(Species ~ ., iris, num.trees = num.trees, split.select.weights = weights),
+ throws_error())
+})
+
+test_that("Inbag count matrix if of right size, with replacement", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE)
+ expect_that(dim(data.frame(rf$inbag.counts)),
+ equals(c(nrow(iris), rf$num.trees)))
+})
+
+test_that("Inbag count matrix if of right size, without replacement", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, replace = FALSE, keep.inbag = TRUE)
+ expect_that(dim(data.frame(rf$inbag.counts)),
+ equals(c(nrow(iris), rf$num.trees)))
+})
+
+test_that("Inbag count matrix if of right size, with replacement, weighted", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, case.weights = runif(nrow(iris)), keep.inbag = TRUE)
+ expect_that(dim(data.frame(rf$inbag.counts)),
+ equals(c(nrow(iris), rf$num.trees)))
+})
+
+test_that("Inbag count matrix if of right size, without replacement, weighted", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, replace = FALSE, case.weights = runif(nrow(iris)), keep.inbag = TRUE)
+ expect_that(dim(data.frame(rf$inbag.counts)),
+ equals(c(nrow(iris), rf$num.trees)))
+})
+
+test_that("Error if sample fraction is 0 or >1", {
+ expect_that(ranger(Species ~ ., iris, num.trees = 5, sample.fraction = 0),
+ throws_error())
+ expect_that(ranger(Species ~ ., iris, num.trees = 5, sample.fraction = 1.1),
+ throws_error())
+})
+
+test_that("Number of samples is right sample fraction, replace=FALSE, default", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE, replace = FALSE)
+ num.inbag <- sapply(rf$inbag.counts, function(x) {
+ sum(x > 0)
+ })
+ sample.fraction <- mean(num.inbag/nrow(iris))
+
+ expect_that(sample.fraction, is_more_than(0.6))
+ expect_that(sample.fraction, is_less_than(0.7))
+})
+
+test_that("Number of samples is right sample fraction, replace=FALSE, 0.3", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE, replace = FALSE, sample.fraction = 0.3)
+ num.inbag <- sapply(rf$inbag.counts, function(x) {
+ sum(x > 0)
+ })
+ sample.fraction <- mean(num.inbag/nrow(iris))
+
+ expect_that(sample.fraction, is_more_than(0.25))
+ expect_that(sample.fraction, is_less_than(0.35))
+})
+
+test_that("Number of samples is right sample fraction, replace=TRUE, default", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE, replace = TRUE)
+ num.inbag <- sapply(rf$inbag.counts, function(x) {
+ sum(x > 0)
+ })
+
+ sample.fraction <- mean(num.inbag/nrow(iris))
+ expected.sample.fraction <- 1-exp(-1)
+
+ expect_that(sample.fraction, is_more_than(expected.sample.fraction-0.05))
+ expect_that(sample.fraction, is_less_than(expected.sample.fraction+0.05))
+})
+
+test_that("Number of samples is right sample fraction, replace=TRUE, 0.5", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE, replace = TRUE, sample.fraction = 0.5)
+ num.inbag <- sapply(rf$inbag.counts, function(x) {
+ sum(x > 0)
+ })
+
+ sample.fraction <- mean(num.inbag/nrow(iris))
+ expected.sample.fraction <- 1-exp(-0.5)
+
+ expect_that(sample.fraction, is_more_than(expected.sample.fraction-0.05))
+ expect_that(sample.fraction, is_less_than(expected.sample.fraction+0.05))
+})
+
+test_that("Number of samples is right sample fraction, replace=FALSE, 0.3, weighted", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE, replace = FALSE, sample.fraction = 0.3, case.weights = runif(nrow(iris)))
+ num.inbag <- sapply(rf$inbag.counts, function(x) {
+ sum(x > 0)
+ })
+ sample.fraction <- mean(num.inbag/nrow(iris))
+
+ expect_that(sample.fraction, is_more_than(0.25))
+ expect_that(sample.fraction, is_less_than(0.35))
+})
+
+test_that("Number of samples is right sample fraction, replace=TRUE, 0.5, weighted", {
+ rf <- ranger(Species ~ ., iris, num.trees = 5, keep.inbag = TRUE, replace = TRUE, sample.fraction = 0.5, case.weights = runif(nrow(iris)))
+ num.inbag <- sapply(rf$inbag.counts, function(x) {
+ sum(x > 0)
+ })
+
+ sample.fraction <- mean(num.inbag/nrow(iris))
+ expected.sample.fraction <- 1-exp(-0.5)
+
+ expect_that(sample.fraction, is_more_than(expected.sample.fraction-0.05))
+ expect_that(sample.fraction, is_less_than(expected.sample.fraction+0.05))
+})
+
+test_that("Alternative interface classification prediction works if only independent variable given, one independent variable", {
+ n <- 50
+
+ dt <- data.frame(x = runif(n), y = factor(rbinom(n, 1, 0.5)))
+ rf <- ranger(dependent.variable.name = "y", data = dt, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt),
+ not(throws_error()))
+ expect_that(predict(rf, dt[, 1, drop = FALSE]),
+ not(throws_error()))
+
+ dt2 <- data.frame(y = factor(rbinom(n, 1, 0.5)), x = runif(n))
+ rf <- ranger(dependent.variable.name = "y", data = dt2, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt2),
+ not(throws_error()))
+ expect_that(predict(rf, dt2[, 2, drop = FALSE]),
+ not(throws_error()))
+})
+
+test_that("Alternative interface classification prediction works if only independent variable given, two independent variables", {
+ n <- 50
+
+ dt <- data.frame(x1 = runif(n), x2 = runif(n), y = factor(rbinom(n, 1, 0.5)))
+ rf <- ranger(dependent.variable.name = "y", data = dt, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt),
+ not(throws_error()))
+ expect_that(predict(rf, dt[, 1:2]),
+ not(throws_error()))
+
+ dt2 <- data.frame(y = factor(rbinom(n, 1, 0.5)), x1 = runif(n), x2 = runif(n))
+ rf <- ranger(dependent.variable.name = "y", data = dt2, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt2),
+ not(throws_error()))
+ expect_that(predict(rf, dt2[, 2:3]),
+ not(throws_error()))
+})
+
+test_that("Alternative interface regression prediction works if only independent variable given, one independent variable", {
+ n <- 50
+
+ dt <- data.frame(x = runif(n), y = rbinom(n, 1, 0.5))
+ rf <- ranger(dependent.variable.name = "y", data = dt, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt),
+ not(throws_error()))
+ expect_that(predict(rf, dt[, 1, drop = FALSE]),
+ not(throws_error()))
+
+ dt2 <- data.frame(y = rbinom(n, 1, 0.5), x = runif(n))
+ rf <- ranger(dependent.variable.name = "y", data = dt2, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt2),
+ not(throws_error()))
+ expect_that(predict(rf, dt2[, 2, drop = FALSE]),
+ not(throws_error()))
+})
+
+test_that("Alternative interface regression prediction works if only independent variable given, two independent variables", {
+ n <- 50
+
+ dt <- data.frame(x1 = runif(n), x2 = runif(n), y = rbinom(n, 1, 0.5))
+ rf <- ranger(dependent.variable.name = "y", data = dt, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt),
+ not(throws_error()))
+ expect_that(predict(rf, dt[, 1:2]),
+ not(throws_error()))
+
+ dt2 <- data.frame(y = rbinom(n, 1, 0.5), x1 = runif(n), x2 = runif(n))
+ rf <- ranger(dependent.variable.name = "y", data = dt2, num.trees = 5, write.forest = TRUE)
+ expect_that(predict(rf, dt2),
+ not(throws_error()))
+ expect_that(predict(rf, dt2[, 2:3]),
+ not(throws_error()))
+})
+
+test_that("Alternative interface regression prediction: Results not all the same", {
+ n <- 50
+
+ dt <- data.frame(x = runif(n), y = rbinom(n, 1, 0.5))
+ rf <- ranger(dependent.variable.name = "y", data = dt, num.trees = 5, write.forest = TRUE)
+ expect_that(diff(range(predict(rf, dt)$predictions)), is_more_than(0))
+ expect_that(diff(range(predict(rf, dt[, 1, drop = FALSE])$predictions)), is_more_than(0))
+
+ dt2 <- data.frame(y = rbinom(n, 1, 0.5), x = runif(n))
+ rf <- ranger(dependent.variable.name = "y", data = dt2, num.trees = 5, write.forest = TRUE)
+ expect_that(diff(range(predict(rf, dt2)$predictions)), is_more_than(0))
+ expect_that(diff(range(predict(rf, dt2[, 2, drop = FALSE])$predictions)), is_more_than(0))
+})
+
+test_that("No error if survival tree without OOB observations", {
+ dat <- data.frame(time = c(1,2), status = c(0,1), x = c(1,2))
+ expect_that(ranger(Surv(time, status) ~ ., dat, num.trees = 1, num.threads = 1),
+ not(throws_error()))
+})
+
+test_that("as.factor() in formula works", {
+ n <- 20
+ dt <- data.frame(x = runif(n), y = rbinom(n, 1, 0.5))
+ expect_that(ranger(as.factor(y) ~ ., data = dt, num.trees = 5, write.forest = TRUE),
+ not(throws_error()))
+})
+
+test_that("If respect.unordered.factors=TRUE, regard characters as unordered", {
+ n <- 20
+ dt <- data.frame(x = sample(c("A", "B", "C", "D"), n, replace = TRUE),
+ y = rbinom(n, 1, 0.5),
+ stringsAsFactors = FALSE)
+
+ set.seed(2)
+ rf.char <- ranger(y ~ ., data = dt, num.trees = 5, min.node.size = n/2, respect.unordered.factors = TRUE)
+
+ dt$x <- factor(dt$x, ordered = FALSE)
+ set.seed(2)
+ rf.fac <- ranger(y ~ ., data = dt, num.trees = 5, min.node.size = n/2, respect.unordered.factors = TRUE)
+
+ expect_that(rf.char$prediction.error, equals(rf.fac$prediction.error))
+})
+
+test_that("If respect.unordered.factors=FALSE, regard characters as ordered", {
+ n <- 20
+ dt <- data.frame(x = sample(c("A", "B", "C", "D"), n, replace = TRUE),
+ y = rbinom(n, 1, 0.5),
+ stringsAsFactors = FALSE)
+
+ set.seed(2)
+ rf.char <- ranger(y ~ ., data = dt, num.trees = 5, min.node.size = n/2, respect.unordered.factors = FALSE)
+
+ dt$x <- factor(dt$x, ordered = FALSE)
+ set.seed(2)
+ rf.fac <- ranger(y ~ ., data = dt, num.trees = 5, min.node.size = n/2, respect.unordered.factors = FALSE)
+
+ expect_that(rf.char$prediction.error, equals(rf.fac$prediction.error))
+})
\ No newline at end of file
diff --git a/release/.gitignore b/release/.gitignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/release/auc_splitting/README b/release/auc_splitting/README
new file mode 100644
index 000000000..1808443e5
--- /dev/null
+++ b/release/auc_splitting/README
@@ -0,0 +1,2 @@
+
+0.1.7 is older than 0.1.2.77.
\ No newline at end of file
diff --git a/release/auc_splitting/ranger_0.1.2.77.tar.gz b/release/auc_splitting/ranger_0.1.2.77.tar.gz
new file mode 100644
index 000000000..020392857
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.2.77.tar.gz differ
diff --git a/release/auc_splitting/ranger_0.1.3.77.tar.gz b/release/auc_splitting/ranger_0.1.3.77.tar.gz
new file mode 100644
index 000000000..9ff208a9e
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.3.77.tar.gz differ
diff --git a/release/auc_splitting/ranger_0.1.3.77.zip b/release/auc_splitting/ranger_0.1.3.77.zip
new file mode 100644
index 000000000..605c06a90
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.3.77.zip differ
diff --git a/release/auc_splitting/ranger_0.1.4.77.tar.gz b/release/auc_splitting/ranger_0.1.4.77.tar.gz
new file mode 100644
index 000000000..3bfc67c4b
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.4.77.tar.gz differ
diff --git a/release/auc_splitting/ranger_0.1.4.77.zip b/release/auc_splitting/ranger_0.1.4.77.zip
new file mode 100644
index 000000000..7ce900fa0
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.4.77.zip differ
diff --git a/release/auc_splitting/ranger_0.1.7.77.tar.gz b/release/auc_splitting/ranger_0.1.7.77.tar.gz
new file mode 100644
index 000000000..9a571aad6
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.7.77.tar.gz differ
diff --git a/release/auc_splitting/ranger_0.1.7.tar.gz b/release/auc_splitting/ranger_0.1.7.tar.gz
new file mode 100644
index 000000000..0418c2f44
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.7.tar.gz differ
diff --git a/release/auc_splitting/ranger_0.1.8.77.tar.gz b/release/auc_splitting/ranger_0.1.8.77.tar.gz
new file mode 100644
index 000000000..2a96e2f69
Binary files /dev/null and b/release/auc_splitting/ranger_0.1.8.77.tar.gz differ
diff --git a/release/ranger_0.1.0.tar.gz b/release/ranger_0.1.0.tar.gz
new file mode 100644
index 000000000..02fb9f414
Binary files /dev/null and b/release/ranger_0.1.0.tar.gz differ
diff --git a/release/ranger_0.1.0_mac64 b/release/ranger_0.1.0_mac64
new file mode 100755
index 000000000..f71ca4212
Binary files /dev/null and b/release/ranger_0.1.0_mac64 differ
diff --git a/release/ranger_0.1.0_win32.exe b/release/ranger_0.1.0_win32.exe
new file mode 100755
index 000000000..48dafe37a
Binary files /dev/null and b/release/ranger_0.1.0_win32.exe differ
diff --git a/release/ranger_0.1.0_win64.exe b/release/ranger_0.1.0_win64.exe
new file mode 100755
index 000000000..e8940ab13
Binary files /dev/null and b/release/ranger_0.1.0_win64.exe differ
diff --git a/release/ranger_0.1.1.tar.gz b/release/ranger_0.1.1.tar.gz
new file mode 100644
index 000000000..5c16255f5
Binary files /dev/null and b/release/ranger_0.1.1.tar.gz differ
diff --git a/release/ranger_0.1.1_win32_only.zip b/release/ranger_0.1.1_win32_only.zip
new file mode 100644
index 000000000..dca4a365e
Binary files /dev/null and b/release/ranger_0.1.1_win32_only.zip differ
diff --git a/release/ranger_0.1.2.tar.gz b/release/ranger_0.1.2.tar.gz
new file mode 100644
index 000000000..684b84222
Binary files /dev/null and b/release/ranger_0.1.2.tar.gz differ
diff --git a/release/ranger_0.1.3.tar.gz b/release/ranger_0.1.3.tar.gz
new file mode 100644
index 000000000..ae6dd5db3
Binary files /dev/null and b/release/ranger_0.1.3.tar.gz differ
diff --git a/release/ranger_0.1.3.zip b/release/ranger_0.1.3.zip
new file mode 100644
index 000000000..2544a3572
Binary files /dev/null and b/release/ranger_0.1.3.zip differ
diff --git a/release/ranger_0.1.4.tar.gz b/release/ranger_0.1.4.tar.gz
new file mode 100644
index 000000000..10b5c614a
Binary files /dev/null and b/release/ranger_0.1.4.tar.gz differ
diff --git a/release/ranger_0.1.4.zip b/release/ranger_0.1.4.zip
new file mode 100644
index 000000000..a3a0989cc
Binary files /dev/null and b/release/ranger_0.1.4.zip differ
diff --git a/release/ranger_0.1.5.tar.gz b/release/ranger_0.1.5.tar.gz
new file mode 100644
index 000000000..984d0a95b
Binary files /dev/null and b/release/ranger_0.1.5.tar.gz differ
diff --git a/release/ranger_0.1.6.33.tar.gz b/release/ranger_0.1.6.33.tar.gz
new file mode 100644
index 000000000..04707bd1a
Binary files /dev/null and b/release/ranger_0.1.6.33.tar.gz differ
diff --git a/release/ranger_0.1.6.tar.gz b/release/ranger_0.1.6.tar.gz
new file mode 100644
index 000000000..bad4e0e37
Binary files /dev/null and b/release/ranger_0.1.6.tar.gz differ
diff --git a/release/ranger_0.1.6.zip b/release/ranger_0.1.6.zip
new file mode 100644
index 000000000..8f1b12f3d
Binary files /dev/null and b/release/ranger_0.1.6.zip differ
diff --git a/release/ranger_0.1.7.tar.gz b/release/ranger_0.1.7.tar.gz
new file mode 100644
index 000000000..5b564821e
Binary files /dev/null and b/release/ranger_0.1.7.tar.gz differ
diff --git a/release/ranger_0.1.8.tar.gz b/release/ranger_0.1.8.tar.gz
new file mode 100644
index 000000000..29452e3b8
Binary files /dev/null and b/release/ranger_0.1.8.tar.gz differ
diff --git a/release/ranger_0.1.9.tar.gz b/release/ranger_0.1.9.tar.gz
new file mode 100644
index 000000000..310fb227f
Binary files /dev/null and b/release/ranger_0.1.9.tar.gz differ
diff --git a/release/ranger_0.2.0.tar.gz b/release/ranger_0.2.0.tar.gz
new file mode 100644
index 000000000..3a08febdf
Binary files /dev/null and b/release/ranger_0.2.0.tar.gz differ
diff --git a/release/ranger_0.2.1.tar.gz b/release/ranger_0.2.1.tar.gz
new file mode 100644
index 000000000..59c080919
Binary files /dev/null and b/release/ranger_0.2.1.tar.gz differ
diff --git a/release/ranger_0.2.2.tar.gz b/release/ranger_0.2.2.tar.gz
new file mode 100644
index 000000000..c695b592f
Binary files /dev/null and b/release/ranger_0.2.2.tar.gz differ
diff --git a/release/ranger_0.2.3.tar.gz b/release/ranger_0.2.3.tar.gz
new file mode 100644
index 000000000..c90b82995
Binary files /dev/null and b/release/ranger_0.2.3.tar.gz differ
diff --git a/release/ranger_0.2.4.tar.gz b/release/ranger_0.2.4.tar.gz
new file mode 100644
index 000000000..123623111
Binary files /dev/null and b/release/ranger_0.2.4.tar.gz differ
diff --git a/release/ranger_0.2.4.zip b/release/ranger_0.2.4.zip
new file mode 100644
index 000000000..84b17532e
Binary files /dev/null and b/release/ranger_0.2.4.zip differ
diff --git a/release/ranger_0.2.5.tar.gz b/release/ranger_0.2.5.tar.gz
new file mode 100644
index 000000000..7c6138cc3
Binary files /dev/null and b/release/ranger_0.2.5.tar.gz differ
diff --git a/release/ranger_0.2.5.zip b/release/ranger_0.2.5.zip
new file mode 100644
index 000000000..e1c4eed1a
Binary files /dev/null and b/release/ranger_0.2.5.zip differ
diff --git a/release/ranger_0.2.6.tar.gz b/release/ranger_0.2.6.tar.gz
new file mode 100644
index 000000000..e7957dab9
Binary files /dev/null and b/release/ranger_0.2.6.tar.gz differ
diff --git a/release/ranger_0.2.6.zip b/release/ranger_0.2.6.zip
new file mode 100644
index 000000000..d59f42cd0
Binary files /dev/null and b/release/ranger_0.2.6.zip differ
diff --git a/release/ranger_0.2.7.tar.gz b/release/ranger_0.2.7.tar.gz
new file mode 100644
index 000000000..ef6434a3d
Binary files /dev/null and b/release/ranger_0.2.7.tar.gz differ
diff --git a/release/ranger_0.2.7.zip b/release/ranger_0.2.7.zip
new file mode 100644
index 000000000..e172e1c6a
Binary files /dev/null and b/release/ranger_0.2.7.zip differ
diff --git a/release/ranger_0.2.7_win_binaries.zip b/release/ranger_0.2.7_win_binaries.zip
new file mode 100644
index 000000000..cdfc1926a
Binary files /dev/null and b/release/ranger_0.2.7_win_binaries.zip differ
diff --git a/release/ranger_0.3.0.tar.gz b/release/ranger_0.3.0.tar.gz
new file mode 100644
index 000000000..5f1059104
Binary files /dev/null and b/release/ranger_0.3.0.tar.gz differ
diff --git a/release/ranger_0.3.5.tar.gz b/release/ranger_0.3.5.tar.gz
new file mode 100644
index 000000000..27cf0b5bd
Binary files /dev/null and b/release/ranger_0.3.5.tar.gz differ
diff --git a/release/ranger_0.3.6.zip b/release/ranger_0.3.6.zip
new file mode 100644
index 000000000..14924e181
Binary files /dev/null and b/release/ranger_0.3.6.zip differ
diff --git a/release/ranger_0.3.7_win_binaries.zip b/release/ranger_0.3.7_win_binaries.zip
new file mode 100644
index 000000000..5199ec312
Binary files /dev/null and b/release/ranger_0.3.7_win_binaries.zip differ
diff --git a/release/ranger_cpp_0.1.4.zip b/release/ranger_cpp_0.1.4.zip
new file mode 100644
index 000000000..be93844d7
Binary files /dev/null and b/release/ranger_cpp_0.1.4.zip differ
diff --git a/release/ranger_cpp_0.1.6.zip b/release/ranger_cpp_0.1.6.zip
new file mode 100644
index 000000000..f1cf2adb8
Binary files /dev/null and b/release/ranger_cpp_0.1.6.zip differ
diff --git a/release/ranger_cpp_0.1.9.zip b/release/ranger_cpp_0.1.9.zip
new file mode 100644
index 000000000..51876fd31
Binary files /dev/null and b/release/ranger_cpp_0.1.9.zip differ
diff --git a/release/ranger_cpp_0.2.4.zip b/release/ranger_cpp_0.2.4.zip
new file mode 100644
index 000000000..73c5c6650
Binary files /dev/null and b/release/ranger_cpp_0.2.4.zip differ
diff --git a/release/ranger_cpp_0.2.5.zip b/release/ranger_cpp_0.2.5.zip
new file mode 100644
index 000000000..d09645434
Binary files /dev/null and b/release/ranger_cpp_0.2.5.zip differ
diff --git a/release/ranger_cpp_0.2.7.zip b/release/ranger_cpp_0.2.7.zip
new file mode 100644
index 000000000..78288f9b4
Binary files /dev/null and b/release/ranger_cpp_0.2.7.zip differ
diff --git a/release/ranger_cpp_0.3.5.zip b/release/ranger_cpp_0.3.5.zip
new file mode 100644
index 000000000..8a10f4390
Binary files /dev/null and b/release/ranger_cpp_0.3.5.zip differ
diff --git a/source/.cproject b/source/.cproject
index af8c48b04..7c0cd1c8d 100644
--- a/source/.cproject
+++ b/source/.cproject
@@ -18,7 +18,7 @@
-
+
@@ -62,4 +62,5 @@
+
diff --git a/source/src/Forest/Forest.cpp b/source/src/Forest/Forest.cpp
index b51df18c1..a43f51794 100644
--- a/source/src/Forest/Forest.cpp
+++ b/source/src/Forest/Forest.cpp
@@ -30,10 +30,12 @@
#include
#include
#include
-#include
#include
-#include
#include
+#ifndef WIN_R_BUILD
+#include
+#include
+#endif
#include "utility.h"
#include "Forest.h"
@@ -42,10 +44,11 @@
#include "DataFloat.h"
Forest::Forest() :
- verbose_out(0), num_trees(DEFAULT_NUM_TREE), mtry(0), min_node_size(0), num_variables(0), num_independent_variables(
- 0), seed(0), dependent_varID(0), num_samples(0), prediction_mode(false), memory_mode(MEM_DOUBLE), sample_with_replacement(
- true), splitrule(DEFAULT_SPLITRULE), num_threads(DEFAULT_NUM_THREADS), data(0), overall_prediction_error(0), importance_mode(
- DEFAULT_IMPORTANCE_MODE), progress(0) {
+verbose_out(0), num_trees(DEFAULT_NUM_TREE), mtry(0), min_node_size(0), num_variables(0), num_independent_variables(
+ 0), seed(0), dependent_varID(0), num_samples(0), prediction_mode(false), memory_mode(MEM_DOUBLE), sample_with_replacement(
+ true), memory_saving_splitting(false), splitrule(DEFAULT_SPLITRULE), predict_all(false), keep_inbag(false), sample_fraction(1), enable_threading(true), num_threads(
+ DEFAULT_NUM_THREADS), data(0), overall_prediction_error(0), importance_mode(DEFAULT_IMPORTANCE_MODE), progress(
+ 0) {
}
Forest::~Forest() {
@@ -55,10 +58,12 @@ Forest::~Forest() {
}
void Forest::initCpp(std::string dependent_variable_name, MemoryMode memory_mode, std::string input_file, uint mtry,
- std::string output_prefix, uint num_trees, std::ostream* verbose_out, uint seed, uint num_threads,
- std::string load_forest_filename, ImportanceMode importance_mode, uint min_node_size,
+ std::string output_prefix, uint num_trees, std::ostream* verbose_out, uint seed, bool enable_threading,
+ uint num_threads, std::string load_forest_filename, ImportanceMode importance_mode, uint min_node_size,
std::string split_select_weights_file, std::vector& always_split_variable_names,
- std::string status_variable_name, bool sample_with_replacement, uint splitrule) {
+ std::string status_variable_name, bool sample_with_replacement, std::vector& unordered_variable_names,
+ bool memory_saving_splitting, SplitRule splitrule, std::string case_weights_file, bool predict_all,
+ double sample_fraction) {
this->verbose_out = verbose_out;
@@ -90,55 +95,88 @@ void Forest::initCpp(std::string dependent_variable_name, MemoryMode memory_mode
}
// Call other init function
- init(dependent_variable_name, memory_mode, data, mtry, output_prefix, num_trees, seed, num_threads, importance_mode,
- min_node_size, status_variable_name, prediction_mode, sample_with_replacement, splitrule);
+ init(dependent_variable_name, memory_mode, data, mtry, output_prefix, num_trees, seed, enable_threading, num_threads,
+ importance_mode, min_node_size, status_variable_name, prediction_mode, sample_with_replacement,
+ unordered_variable_names, memory_saving_splitting, splitrule, predict_all, sample_fraction);
if (prediction_mode) {
loadFromFile(load_forest_filename);
}
-
// Set variables to be always considered for splitting
if (!always_split_variable_names.empty()) {
setAlwaysSplitVariables(always_split_variable_names);
}
+ // TODO: Read 2d weights for tree-wise split select weights
// Load split select weights from file
if (!split_select_weights_file.empty()) {
- std::vector split_select_weights;
- loadDoubleVectorFromFile(split_select_weights, split_select_weights_file);
- if (split_select_weights.size() != num_variables - 1) {
+ std::vector> split_select_weights;
+ split_select_weights.resize(1);
+ loadDoubleVectorFromFile(split_select_weights[0], split_select_weights_file);
+ if (split_select_weights[0].size() != num_variables - 1) {
throw std::runtime_error("Number of split select weights is not equal to number of independent variables.");
}
setSplitWeightVector(split_select_weights);
}
+
+ // Load case weights from file
+ if (!case_weights_file.empty()) {
+ loadDoubleVectorFromFile(case_weights, case_weights_file);
+ if (case_weights.size() != num_samples - 1) {
+ throw std::runtime_error("Number of case weights is not equal to number of samples.");
+ }
+ }
+
+ // Check if all catvars are coded in integers starting at 1
+ if (!unordered_variable_names.empty()) {
+ std::string error_message = checkUnorderedVariables(data, unordered_variable_names);
+ if (!error_message.empty()) {
+ throw std::runtime_error(error_message);
+ }
+ }
}
-void Forest::initR(std::string dependent_variable_name, MemoryMode memory_mode, Data* input_data, uint mtry,
- uint num_trees, std::ostream* verbose_out, uint seed, uint num_threads, ImportanceMode importance_mode,
- uint min_node_size, std::vector& split_select_weights,
+void Forest::initR(std::string dependent_variable_name, Data* input_data, uint mtry, uint num_trees,
+ std::ostream* verbose_out, uint seed, bool enable_threading, uint num_threads, ImportanceMode importance_mode,
+ uint min_node_size, std::vector>& split_select_weights,
std::vector& always_split_variable_names, std::string status_variable_name, bool prediction_mode,
- bool sample_with_replacement, uint splitrule) {
+ bool sample_with_replacement, std::vector& unordered_variable_names, bool memory_saving_splitting,
+ SplitRule splitrule, std::vector& case_weights, bool predict_all, bool keep_inbag, double sample_fraction) {
this->verbose_out = verbose_out;
// Call other init function
- init(dependent_variable_name, memory_mode, input_data, mtry, "", num_trees, seed, num_threads, importance_mode,
- min_node_size, status_variable_name, prediction_mode, sample_with_replacement, splitrule);
+ init(dependent_variable_name, MEM_DOUBLE, input_data, mtry, "", num_trees, seed, enable_threading, num_threads,
+ importance_mode, min_node_size, status_variable_name, prediction_mode, sample_with_replacement,
+ unordered_variable_names, memory_saving_splitting, splitrule, predict_all, sample_fraction);
// Set variables to be always considered for splitting
- setAlwaysSplitVariables(always_split_variable_names);
+ if (!always_split_variable_names.empty()) {
+ setAlwaysSplitVariables(always_split_variable_names);
+ }
// Set split select weights
if (!split_select_weights.empty()) {
setSplitWeightVector(split_select_weights);
}
+ // Set case weights
+ if (!case_weights.empty()) {
+ if (case_weights.size() != num_samples) {
+ throw std::runtime_error("Number of case weights not equal to number of samples.");
+ }
+ this->case_weights = case_weights;
+ }
+
+ // Keep inbag counts
+ this->keep_inbag = keep_inbag;
}
void Forest::init(std::string dependent_variable_name, MemoryMode memory_mode, Data* input_data, uint mtry,
- std::string output_prefix, uint num_trees, uint seed, uint num_threads, ImportanceMode importance_mode,
- uint min_node_size, std::string status_variable_name, bool prediction_mode, bool sample_with_replacement,
- uint splitrule) {
+ std::string output_prefix, uint num_trees, uint seed, bool enable_threading, uint num_threads,
+ ImportanceMode importance_mode, uint min_node_size, std::string status_variable_name, bool prediction_mode,
+ bool sample_with_replacement, std::vector& unordered_variable_names, bool memory_saving_splitting,
+ SplitRule splitrule, bool predict_all, double sample_fraction) {
// Initialize data with memmode
this->data = input_data;
@@ -151,12 +189,21 @@ void Forest::init(std::string dependent_variable_name, MemoryMode memory_mode, D
random_number_generator.seed(seed);
}
- // Set number of threads
- if (num_threads == DEFAULT_NUM_THREADS) {
+ // Set up threading
+#ifndef WIN_R_BUILD
+ this->enable_threading = enable_threading;
+ if (!enable_threading) {
+ // set to 1 because calculation will be done in calling thread.
+ this->num_threads = 1;
+ } else if (num_threads == DEFAULT_NUM_THREADS) {
this->num_threads = std::thread::hardware_concurrency();
} else {
this->num_threads = num_threads;
}
+#else
+ this->enable_threading = false;
+ this->num_threads = 1;
+#endif
// Set member variables
this->num_trees = num_trees;
@@ -168,7 +215,10 @@ void Forest::init(std::string dependent_variable_name, MemoryMode memory_mode, D
this->memory_mode = memory_mode;
this->prediction_mode = prediction_mode;
this->sample_with_replacement = sample_with_replacement;
+ this->memory_saving_splitting = memory_saving_splitting;
this->splitrule = splitrule;
+ this->predict_all = predict_all;
+ this->sample_fraction = sample_fraction;
// Set number of samples and variables
num_samples = data->getNumRows();
@@ -179,6 +229,15 @@ void Forest::init(std::string dependent_variable_name, MemoryMode memory_mode, D
dependent_varID = data->getVariableID(dependent_variable_name);
}
+ // Set unordered factor variables
+ if (!prediction_mode) {
+ is_ordered_variable.resize(num_variables, true);
+ for (auto& variable_name : unordered_variable_names) {
+ size_t varID = data->getVariableID(variable_name);
+ is_ordered_variable[varID] = false;
+ }
+ }
+
no_split_variables.push_back(dependent_varID);
initInternal(status_variable_name);
@@ -188,16 +247,22 @@ void Forest::init(std::string dependent_variable_name, MemoryMode memory_mode, D
// Sort no split variables in ascending order
std::sort(no_split_variables.begin(), no_split_variables.end());
+ // Init split select weights
+ split_select_weights.push_back(std::vector());
+
// Check if mtry is in valid range
if (this->mtry > num_variables - 1) {
throw std::runtime_error("mtry can not be larger than number of variables in data.");
}
- // Load split select weights from file
- this->split_select_weights = split_select_weights;
+ // Check if any observations samples
+ if ((size_t) num_samples * sample_fraction < 1) {
+ throw std::runtime_error("sample_fraction too small, no observations sampled.");
+ }
}
void Forest::run(bool verbose) {
+
if (prediction_mode) {
if (verbose) {
*verbose_out << "Predicting .." << std::endl;
@@ -207,6 +272,7 @@ void Forest::run(bool verbose) {
if (verbose) {
*verbose_out << "Growing trees .." << std::endl;
}
+
grow();
if (verbose) {
@@ -214,12 +280,7 @@ void Forest::run(bool verbose) {
}
computePredictionError();
- if (importance_mode == IMP_GINI) {
- if (verbose) {
- *verbose_out << "Computing variable importance .." << std::endl;
- }
- computeGiniImportance();
- } else if (importance_mode > IMP_GINI) {
+ if (importance_mode > IMP_GINI) {
if (verbose) {
*verbose_out << "Computing permutation variable importance .." << std::endl;
}
@@ -242,6 +303,7 @@ void Forest::writeOutput() {
*verbose_out << "Variable importance mode: " << importance_mode << std::endl;
*verbose_out << "Memory mode: " << memory_mode << std::endl;
*verbose_out << "Seed: " << seed << std::endl;
+ *verbose_out << "Threading enabled: " << enable_threading << std::endl;
*verbose_out << "Number of threads: " << num_threads << std::endl;
*verbose_out << std::endl;
@@ -251,7 +313,7 @@ void Forest::writeOutput() {
*verbose_out << "Overall OOB prediction error: " << overall_prediction_error << std::endl;
*verbose_out << std::endl;
- if (!split_select_weights.empty()) {
+ if (!split_select_weights.empty() & !split_select_weights[0].empty()) {
*verbose_out
<< "Warning: Split select weights used. Variable importance measures are only comparable for variables with equal weights."
<< std::endl;
@@ -307,6 +369,9 @@ void Forest::saveToFile() {
// Write num_trees
outfile.write((char*) &num_trees, sizeof(num_trees));
+ // Write is_ordered_variable
+ saveVector1D(is_ordered_variable, outfile);
+
saveToFileInternal(outfile);
// Write tree data for each tree
@@ -336,35 +401,132 @@ void Forest::grow() {
} else {
tree_seed = (i + 1) * seed;
}
+
+ // Get split select weights for tree
+ std::vector* tree_split_select_weights;
+ if (split_select_weights.size() > 1) {
+ tree_split_select_weights = &split_select_weights[i];
+ } else {
+ tree_split_select_weights = &split_select_weights[0];
+ }
+
trees[i]->init(data, mtry, dependent_varID, num_samples, tree_seed, &deterministic_varIDs, &split_select_varIDs,
- &split_select_weights, importance_mode, min_node_size, &no_split_variables, sample_with_replacement, splitrule);
+ tree_split_select_weights, importance_mode, min_node_size, &no_split_variables, sample_with_replacement,
+ &is_ordered_variable, memory_saving_splitting, splitrule, &case_weights, keep_inbag, sample_fraction);
}
- // Grow trees in multiple threads
- progress = 0;
- std::vector threads;
- threads.reserve(num_threads);
- for (uint i = 0; i < num_threads; ++i) {
- threads.push_back(std::thread(&Forest::growTreesInThread, this, i));
+ // Init variable importance
+ variable_importance.resize(num_independent_variables, 0);
+
+ if (this->enable_threading) {
+#ifndef WIN_R_BUILD
+ // Grow trees in multiple threads
+ progress = 0;
+#ifdef R_BUILD
+ aborted = false;
+ aborted_threads = 0;
+#endif
+
+ std::vector threads;
+ threads.reserve(num_threads);
+
+ // Initailize importance per thread
+ std::vector> variable_importance_threads(num_threads);
+
+ for (uint i = 0; i < num_threads; ++i) {
+ if (importance_mode == IMP_GINI) {
+ variable_importance_threads[i].resize(num_independent_variables, 0);
+ }
+ threads.push_back(std::thread(&Forest::growTreesInThread, this, i, &(variable_importance_threads[i])));
+ }
+ showProgress("Growing trees..");
+ for (auto &thread : threads) {
+ thread.join();
+ }
+
+#ifdef R_BUILD
+ if (aborted_threads > 0) {
+ throw std::runtime_error("User interrupt.");
+ }
+#endif
+
+ // Sum thread importances
+ if (importance_mode == IMP_GINI) {
+ variable_importance.resize(num_independent_variables, 0);
+ for (size_t i = 0; i < num_independent_variables; ++i) {
+ for (uint j = 0; j < num_threads; ++j) {
+ variable_importance[i] += variable_importance_threads[j][i];
+ }
+ }
+ variable_importance_threads.clear();
+ }
+#endif
+ } else {
+#ifdef WIN_R_BUILD
+ progress = 0;
+ clock_t start_time = clock();
+ clock_t lap_time = clock();
+#endif
+
+ for (size_t i = 0; i < num_trees; ++i) {
+ trees[i]->grow(&variable_importance);
+#ifdef WIN_R_BUILD
+ progress++;
+ showProgress("Growing trees..", start_time, lap_time);
+#endif
+ }
}
- showProgress();
- for (auto &thread : threads) {
- thread.join();
+
+
+ // Divide importance by number of trees
+ if (importance_mode == IMP_GINI) {
+ for (auto& v : variable_importance) {
+ v /= num_trees;
+ }
}
}
void Forest::predict() {
- // Predict trees in multiple threads and join the threads with the main thread
- progress = 0;
- std::vector threads;
- threads.reserve(num_threads);
- for (uint i = 0; i < num_threads; ++i) {
- threads.push_back(std::thread(&Forest::predictTreesInThread, this, i, data, false));
- }
- showProgress();
- for (auto &thread : threads) {
- thread.join();
+ if (enable_threading) {
+#ifndef WIN_R_BUILD
+ // Predict trees in multiple threads and join the threads with the main thread
+ progress = 0;
+#ifdef R_BUILD
+ aborted = false;
+ aborted_threads = 0;
+#endif
+
+ std::vector threads;
+ threads.reserve(num_threads);
+ for (uint i = 0; i < num_threads; ++i) {
+ threads.push_back(std::thread(&Forest::predictTreesInThread, this, i, data, false));
+ }
+ showProgress("Predicting..");
+ for (auto &thread : threads) {
+ thread.join();
+ }
+
+#ifdef R_BUILD
+ if (aborted_threads > 0) {
+ throw std::runtime_error("User interrupt.");
+ }
+#endif
+#endif
+ } else {
+#ifdef WIN_R_BUILD
+ progress = 0;
+ clock_t start_time = clock();
+ clock_t lap_time = clock();
+#endif
+
+ for (size_t i = 0; i < num_trees; ++i) {
+ trees[i]->predict(data, false);
+#ifdef WIN_R_BUILD
+ progress++;
+ showProgress("Predicting..", start_time, lap_time);
+#endif
+ }
}
// Call special functions for subclasses
@@ -373,70 +535,121 @@ void Forest::predict() {
void Forest::computePredictionError() {
- // Predict trees in multiple threads
- std::vector threads;
- threads.reserve(num_threads);
- for (uint i = 0; i < num_threads; ++i) {
- threads.push_back(std::thread(&Forest::predictTreesInThread, this, i, data, true));
- }
- for (auto &thread : threads) {
- thread.join();
+ if (enable_threading) {
+#ifndef WIN_R_BUILD
+ // Predict trees in multiple threads
+ std::vector threads;
+ threads.reserve(num_threads);
+ for (uint i = 0; i < num_threads; ++i) {
+ threads.push_back(std::thread(&Forest::predictTreesInThread, this, i, data, true));
+ }
+ showProgress("Computing prediction error..");
+ for (auto &thread : threads) {
+ thread.join();
+ }
+
+#ifdef R_BUILD
+ if (aborted_threads > 0) {
+ throw std::runtime_error("User interrupt.");
+ }
+#endif
+#endif
+ } else {
+#ifdef WIN_R_BUILD
+ progress = 0;
+ clock_t start_time = clock();
+ clock_t lap_time = clock();
+#endif
+
+ for (size_t i = 0; i < num_trees; ++i) {
+ trees[i]->predict(data, true);
+#ifdef WIN_R_BUILD
+ progress++;
+ showProgress("Predicting..", start_time, lap_time);
+#endif
+ }
}
// Call special function for subclasses
computePredictionErrorInternal();
}
-void Forest::computeGiniImportance() {
-
- // Initialize with 0.
- variable_importance.resize(num_independent_variables, 0);
-
- // Sum tree importance and divide by number of trees
- for (size_t t = 0; t < trees.size(); ++t) {
- std::vector tree_importance = trees[t]->getVariableImportance();
- for (size_t i = 0; i < tree_importance.size(); ++i) {
- variable_importance[i] += tree_importance[i];
- }
- }
- for (auto& v : variable_importance) {
- v /= num_trees;
- }
-}
-
void Forest::computePermutationImportance() {
// Compute tree permutation importance in multiple threads
progress = 0;
- std::vector threads;
- threads.reserve(num_threads);
- for (uint i = 0; i < num_threads; ++i) {
- threads.push_back(std::thread(&Forest::computeTreePermutationImportanceInThread, this, i));
- }
- //showProgress();
- for (auto &thread : threads) {
- thread.join();
- }
+#ifdef R_BUILD
+ aborted = false;
+ aborted_threads = 0;
+#endif
- // Initialize with 0.
- std::vector variance;
- variance.resize(num_independent_variables, 0);
+ std::vector variance(num_independent_variables, 0);
variable_importance.resize(num_independent_variables, 0);
- // Sum tree importance and divide by number of trees
- for (size_t t = 0; t < trees.size(); ++t) {
- std::vector tree_importance = trees[t]->getVariableImportance();
- for (size_t i = 0; i < tree_importance.size(); ++i) {
- variable_importance[i] += tree_importance[i];
-
- // Variance for scaled permutation importance
- if (importance_mode == IMP_PERM_BREIMAN) {
- variance[i] += tree_importance[i] * tree_importance[i];
- } else if (importance_mode == IMP_PERM_LIAW) {
- variance[i] += tree_importance[i] * tree_importance[i] * trees[i]->getNumSamplesOob();
+ if (enable_threading) {
+#ifndef WIN_R_BUILD
+ std::vector threads;
+ threads.reserve(num_threads);
+
+ // Initailize importance and variance
+ std::vector> variable_importance_threads(num_threads);
+ std::vector> variance_threads(num_threads);
+
+ // Compute importance
+ for (uint i = 0; i < num_threads; ++i) {
+ variable_importance_threads[i].resize(num_independent_variables, 0);
+ if (importance_mode == IMP_PERM_BREIMAN || importance_mode == IMP_PERM_LIAW) {
+ variance_threads[i].resize(num_independent_variables, 0);
+ }
+ threads.push_back(
+ std::thread(&Forest::computeTreePermutationImportanceInThread, this, i, &(variable_importance_threads[i]),
+ &(variance_threads[i])));
+ }
+ showProgress("Computing permutation importance..");
+ for (auto &thread : threads) {
+ thread.join();
+ }
+
+#ifdef R_BUILD
+ if (aborted_threads > 0) {
+ throw std::runtime_error("User interrupt.");
+ }
+#endif
+
+ // Sum thread importances
+ for (size_t i = 0; i < num_independent_variables; ++i) {
+ for (uint j = 0; j < num_threads; ++j) {
+ variable_importance[i] += variable_importance_threads[j][i];
+ }
+ }
+ variable_importance_threads.clear();
+
+ // Sum thread variances
+ if (importance_mode == IMP_PERM_BREIMAN || importance_mode == IMP_PERM_LIAW) {
+ for (size_t i = 0; i < num_independent_variables; ++i) {
+ for (uint j = 0; j < num_threads; ++j) {
+ variance[i] += variance_threads[j][i];
+ }
}
+ variance_threads.clear();
+ }
+#endif
+ } else {
+#ifdef WIN_R_BUILD
+ progress = 0;
+ clock_t start_time = clock();
+ clock_t lap_time = clock();
+#endif
+
+ for (size_t i = 0; i < num_trees; ++i) {
+ trees[i]->computePermutationImportance(&variable_importance, &variance);
+#ifdef WIN_R_BUILD
+ progress++;
+ showProgress("Computing permutation importance..", start_time, lap_time);
+#endif
}
}
+
for (size_t i = 0; i < variable_importance.size(); ++i) {
variable_importance[i] /= num_trees;
@@ -450,10 +663,21 @@ void Forest::computePermutationImportance() {
}
}
-void Forest::growTreesInThread(uint thread_idx) {
+#ifndef WIN_R_BUILD
+void Forest::growTreesInThread(uint thread_idx, std::vector* variable_importance) {
if (thread_ranges.size() > thread_idx + 1) {
for (size_t i = thread_ranges[thread_idx]; i < thread_ranges[thread_idx + 1]; ++i) {
- trees[i]->grow();
+ trees[i]->grow(variable_importance);
+
+ // Check for user interrupt
+#ifdef R_BUILD
+ if (aborted) {
+ std::unique_lock lock(mutex);
+ ++aborted_threads;
+ condition_variable.notify_one();
+ return;
+ }
+#endif
// Increase progress by 1 tree
std::unique_lock lock(mutex);
@@ -468,6 +692,16 @@ void Forest::predictTreesInThread(uint thread_idx, const Data* prediction_data,
for (size_t i = thread_ranges[thread_idx]; i < thread_ranges[thread_idx + 1]; ++i) {
trees[i]->predict(prediction_data, oob_prediction);
+ // Check for user interrupt
+#ifdef R_BUILD
+ if (aborted) {
+ std::unique_lock lock(mutex);
+ ++aborted_threads;
+ condition_variable.notify_one();
+ return;
+ }
+#endif
+
// Increase progress by 1 tree
std::unique_lock lock(mutex);
++progress;
@@ -476,10 +710,21 @@ void Forest::predictTreesInThread(uint thread_idx, const Data* prediction_data,
}
}
-void Forest::computeTreePermutationImportanceInThread(uint thread_idx) {
+void Forest::computeTreePermutationImportanceInThread(uint thread_idx, std::vector* importance,
+ std::vector* variance) {
if (thread_ranges.size() > thread_idx + 1) {
for (size_t i = thread_ranges[thread_idx]; i < thread_ranges[thread_idx + 1]; ++i) {
- trees[i]->computePermutationImportance();
+ trees[i]->computePermutationImportance(importance, variance);
+
+ // Check for user interrupt
+#ifdef R_BUILD
+ if (aborted) {
+ std::unique_lock lock(mutex);
+ ++aborted_threads;
+ condition_variable.notify_one();
+ return;
+ }
+#endif
// Increase progress by 1 tree
std::unique_lock lock(mutex);
@@ -488,6 +733,7 @@ void Forest::computeTreePermutationImportanceInThread(uint thread_idx) {
}
}
}
+#endif
void Forest::loadFromFile(std::string filename) {
*verbose_out << "Loading forest from file " << filename << "." << std::endl;
@@ -503,36 +749,70 @@ void Forest::loadFromFile(std::string filename) {
infile.read((char*) &dependent_varID, sizeof(dependent_varID));
infile.read((char*) &num_trees, sizeof(num_trees));
+ // Read is_ordered_variable
+ readVector1D(is_ordered_variable, infile);
+
// Read tree data. This is different for tree types -> virtual function
loadFromFileInternal(infile);
+
infile.close();
// Create thread ranges
equalSplit(thread_ranges, 0, num_trees - 1, num_threads);
}
-void Forest::setSplitWeightVector(std::vector& split_select_weights) {
+void Forest::setSplitWeightVector(std::vector>& split_select_weights) {
+
+ // Size should be 1 x num_independent_variables or num_trees x num_independent_variables
+ if (split_select_weights.size() != 1 && split_select_weights.size() != num_trees) {
+ throw std::runtime_error("Size of split select weights not equal to 1 or number of trees.");
+ }
- if (split_select_weights.size() != num_independent_variables) {
- throw std::runtime_error("Number of split select weights not equal to number of independent variables.");
+ // Reserve space
+ if (split_select_weights.size() == 1) {
+ this->split_select_weights[0].resize(num_independent_variables);
+ } else {
+ this->split_select_weights.clear();
+ this->split_select_weights.resize(num_trees, std::vector(num_independent_variables));
}
+ this->split_select_varIDs.resize(num_independent_variables);
+ deterministic_varIDs.reserve(num_independent_variables);
// Split up in deterministic and weighted variables, ignore zero weights
for (size_t i = 0; i < split_select_weights.size(); ++i) {
- double weight = split_select_weights[i];
- size_t varID = i;
- for (auto& skip : no_split_variables) {
- if (varID >= skip) {
- ++varID;
- }
+
+ // Size should be 1 x num_independent_variables or num_trees x num_independent_variables
+ if (split_select_weights[i].size() != num_independent_variables) {
+ throw std::runtime_error("Number of split select weights not equal to number of independent variables.");
}
- if (weight == 1) {
- deterministic_varIDs.push_back(varID);
- } else if (weight < 1 && weight > 0) {
- this->split_select_varIDs.push_back(varID);
- this->split_select_weights.push_back(weight);
- } else if (weight < 0 || weight > 1) {
- throw std::runtime_error("One or more split select weights not in range [0,1].");
+
+ for (size_t j = 0; j < split_select_weights[i].size(); ++j) {
+ double weight = split_select_weights[i][j];
+
+ if (i == 0) {
+ size_t varID = j;
+ for (auto& skip : no_split_variables) {
+ if (varID >= skip) {
+ ++varID;
+ }
+ }
+
+ if (weight == 1) {
+ deterministic_varIDs.push_back(varID);
+ } else if (weight < 1 && weight > 0) {
+ this->split_select_varIDs[j] = varID;
+ this->split_select_weights[i][j] = weight;
+ } else if (weight < 0 || weight > 1) {
+ throw std::runtime_error("One or more split select weights not in range [0,1].");
+ }
+
+ } else {
+ if (weight < 1 && weight > 0) {
+ this->split_select_weights[i][j] = weight;
+ } else if (weight < 0 || weight > 1) {
+ throw std::runtime_error("One or more split select weights not in range [0,1].");
+ }
+ }
}
}
@@ -546,6 +826,8 @@ void Forest::setSplitWeightVector(std::vector& split_select_weights) {
void Forest::setAlwaysSplitVariables(std::vector& always_split_variable_names) {
+ deterministic_varIDs.reserve(num_independent_variables);
+
for (auto& variable_name : always_split_variable_names) {
size_t varID = data->getVariableID(variable_name);
deterministic_varIDs.push_back(varID);
@@ -557,7 +839,26 @@ void Forest::setAlwaysSplitVariables(std::vector& always_split_vari
}
}
-void Forest::showProgress() {
+#ifdef WIN_R_BUILD
+void Forest::showProgress(std::string operation, clock_t start_time, clock_t& lap_time) {
+
+ // Check for user interrupt
+ if (checkInterrupt()) {
+ throw std::runtime_error("User interrupt.");
+ }
+
+ double elapsed_time = (clock() - lap_time) / CLOCKS_PER_SEC;
+ if (elapsed_time > STATUS_INTERVAL) {
+ double relative_progress = (double) progress / (double) num_trees;
+ double time_from_start = (clock() - start_time) / CLOCKS_PER_SEC;
+ uint remaining_time = (1 / relative_progress - 1) * time_from_start;
+ *verbose_out << operation << " Progress: " << round(100 * relative_progress) << "%. Estimated remaining time: "
+ << beautifyTime(remaining_time) << "." << std::endl;
+ lap_time = clock();
+ }
+}
+#else
+void Forest::showProgress(std::string operation) {
using std::chrono::steady_clock;
using std::chrono::duration_cast;
using std::chrono::seconds;
@@ -571,13 +872,24 @@ void Forest::showProgress() {
condition_variable.wait(lock);
seconds elapsed_time = duration_cast(steady_clock::now() - last_time);
+ // Check for user interrupt
+#ifdef R_BUILD
+ if (!aborted && checkInterrupt()) {
+ aborted = true;
+ }
+ if (aborted && aborted_threads >= num_threads) {
+ return;
+ }
+#endif
+
if (progress > 0 && elapsed_time.count() > STATUS_INTERVAL) {
double relative_progress = (double) progress / (double) num_trees;
seconds time_from_start = duration_cast(steady_clock::now() - start_time);
uint remaining_time = (1 / relative_progress - 1) * time_from_start.count();
- *verbose_out << "Progress: " << round(100 * relative_progress) << "%. Estimated remaining time: "
+ *verbose_out << operation << " Progress: " << round(100 * relative_progress) << "%. Estimated remaining time: "
<< beautifyTime(remaining_time) << "." << std::endl;
last_time = steady_clock::now();
}
}
}
+#endif
diff --git a/source/src/Forest/Forest.h b/source/src/Forest/Forest.h
index 066f65396..3ff79cc33 100644
--- a/source/src/Forest/Forest.h
+++ b/source/src/Forest/Forest.h
@@ -32,8 +32,13 @@
#include
#include
#include
+#include
+#ifndef WIN_R_BUILD
+#include
+#include
#include
#include
+#endif
#include "globals.h"
#include "Tree.h"
@@ -46,18 +51,24 @@ class Forest {
// Init from c++ main or Rcpp from R
void initCpp(std::string dependent_variable_name, MemoryMode memory_mode, std::string input_file, uint mtry,
- std::string output_prefix, uint num_trees, std::ostream* verbose_out, uint seed, uint num_threads,
- std::string load_forest_filename, ImportanceMode importance_mode, uint min_node_size,
+ std::string output_prefix, uint num_trees, std::ostream* verbose_out, uint seed, bool enable_threading,
+ uint num_threads, std::string load_forest_filename, ImportanceMode importance_mode, uint min_node_size,
std::string split_select_weights_file, std::vector& always_split_variable_names,
- std::string status_variable_name, bool sample_with_replacement, uint splitrule);
- void initR(std::string dependent_variable_name, MemoryMode memory_mode, Data* input_data, uint mtry, uint num_trees,
- std::ostream* verbose_out, uint seed, uint num_threads, ImportanceMode importance_mode, uint min_node_size,
- std::vector& split_select_weights, std::vector& always_split_variable_names,
- std::string status_variable_name, bool prediction_mode, bool sample_with_replacement, uint splitrule);
+ std::string status_variable_name, bool sample_with_replacement,
+ std::vector& unordered_variable_names, bool memory_saving_splitting, SplitRule splitrule,
+ std::string case_weights_file, bool predict_all, double sample_fraction);
+ void initR(std::string dependent_variable_name, Data* input_data, uint mtry, uint num_trees,
+ std::ostream* verbose_out, uint seed, bool enable_threading, uint num_threads, ImportanceMode importance_mode,
+ uint min_node_size, std::vector>& split_select_weights,
+ std::vector& always_split_variable_names, std::string status_variable_name, bool prediction_mode,
+ bool sample_with_replacement, std::vector& unordered_variable_names, bool memory_saving_splitting,
+ SplitRule splitrule, std::vector& case_weights, bool predict_all, bool keep_inbag,
+ double sample_fraction);
void init(std::string dependent_variable_name, MemoryMode memory_mode, Data* input_data, uint mtry,
- std::string output_prefix, uint num_trees, uint seed, uint num_threads, ImportanceMode importance_mode,
- uint min_node_size, std::string status_variable_name, bool prediction_mode, bool sample_with_replacement,
- uint splitrule);
+ std::string output_prefix, uint num_trees, uint seed, bool enable_threading, uint num_threads,
+ ImportanceMode importance_mode, uint min_node_size, std::string status_variable_name, bool prediction_mode,
+ bool sample_with_replacement, std::vector& unordered_variable_names, bool memory_saving_splitting,
+ SplitRule splitrule, bool predict_all, double sample_fraction);
virtual void initInternal(std::string status_variable_name) = 0;
// Grow or predict
@@ -122,6 +133,18 @@ class Forest {
{
return num_independent_variables;
}
+ const std::vector& getIsOrderedVariable() const
+ {
+ return is_ordered_variable;
+ }
+
+ std::vector> getInbagCounts() const {
+ std::vector> result;
+ for (auto& tree : trees) {
+ result.push_back(tree->getInbagCounts());
+ }
+ return result;
+ }
protected:
void grow();
@@ -134,24 +157,27 @@ class Forest {
void computePredictionError();
virtual void computePredictionErrorInternal() = 0;
- void computeGiniImportance();
void computePermutationImportance();
// Multithreading methods for growing/prediction/importance, called by each thread
- void growTreesInThread(uint thread_idx);
+ void growTreesInThread(uint thread_idx, std::vector* variable_importance);
void predictTreesInThread(uint thread_idx, const Data* prediction_data, bool oob_prediction);
- void computeTreePermutationImportanceInThread(uint thread_idx);
+ void computeTreePermutationImportanceInThread(uint thread_idx, std::vector* importance, std::vector* variance);
// Load forest from file
void loadFromFile(std::string filename);
virtual void loadFromFileInternal(std::ifstream& infile) = 0;
// Set split select weights and variables to be always considered for splitting
- void setSplitWeightVector(std::vector& split_select_weights);
+ void setSplitWeightVector(std::vector>& split_select_weights);
void setAlwaysSplitVariables(std::vector& always_split_variable_names);
// Show progress every few seconds
- void showProgress();
+#ifdef WIN_R_BUILD
+ void showProgress(std::string operation, clock_t start_time, clock_t& lap_time);
+#else
+ void showProgress(std::string operation);
+#endif
// Verbose output stream, cout if verbose==true, logfile if not
std::ostream* verbose_out;
@@ -167,16 +193,26 @@ class Forest {
bool prediction_mode;
MemoryMode memory_mode;
bool sample_with_replacement;
- uint splitrule;
+ bool memory_saving_splitting;
+ SplitRule splitrule;
+ bool predict_all;
+ bool keep_inbag;
+ double sample_fraction;
+
+ // For each varID true if ordered
+ std::vector is_ordered_variable;
// Variable to not split at (only dependent_varID for non-survival forests)
std::vector no_split_variables;
// Multithreading
+ bool enable_threading;
uint num_threads;
std::vector thread_ranges;
+#ifndef WIN_R_BUILD
std::mutex mutex;
std::condition_variable condition_variable;
+#endif
std::vector trees;
Data* data;
@@ -188,7 +224,10 @@ class Forest {
// Deterministic variables are always selected
std::vector deterministic_varIDs;
std::vector split_select_varIDs;
- std::vector split_select_weights;
+ std::vector> split_select_weights;
+
+ // Bootstrap weights
+ std::vector case_weights;
// Random number generator
std::mt19937_64 random_number_generator;
@@ -201,6 +240,10 @@ class Forest {
// Computation progress (finished trees)
size_t progress;
+#ifdef R_BUILD
+ size_t aborted_threads;
+ bool aborted;
+#endif
private:
DISALLOW_COPY_AND_ASSIGN(Forest);
diff --git a/source/src/Forest/ForestClassification.cpp b/source/src/Forest/ForestClassification.cpp
index ca1d87ac7..18fcf55af 100644
--- a/source/src/Forest/ForestClassification.cpp
+++ b/source/src/Forest/ForestClassification.cpp
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#include
#include
@@ -48,17 +48,18 @@ ForestClassification::~ForestClassification() {
void ForestClassification::loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
- std::vector& class_values) {
+ std::vector& class_values, std::vector& is_ordered_variable) {
this->dependent_varID = dependent_varID;
this->num_trees = num_trees;
this->class_values = class_values;
+ this->is_ordered_variable = is_ordered_variable;
// Create trees
trees.reserve(num_trees);
for (size_t i = 0; i < num_trees; ++i) {
Tree* tree = new TreeClassification(forest_child_nodeIDs[i], forest_split_varIDs[i], forest_split_values[i],
- &class_values, &response_classIDs);
+ &this->class_values, &response_classIDs, &this->is_ordered_variable);
trees.push_back(tree);
}
@@ -70,8 +71,8 @@ void ForestClassification::initInternal(std::string status_variable_name) {
// If mtry not set, use floored square root of number of independent variables.
if (mtry == 0) {
- int temp = sqrt(num_variables - 1);
- mtry = std::max(1, temp);
+ unsigned long temp = sqrt((double) (num_variables - 1));
+ mtry = std::max((unsigned long) 1, temp);
}
// Set minimal node size
@@ -92,6 +93,11 @@ void ForestClassification::initInternal(std::string status_variable_name) {
response_classIDs.push_back(classID);
}
}
+
+ // Sort data if memory saving mode
+ if (!memory_saving_splitting) {
+ data->sort();
+ }
}
void ForestClassification::growInternal() {
@@ -104,22 +110,34 @@ void ForestClassification::growInternal() {
void ForestClassification::predictInternal() {
// First dim trees, second dim samples
- size_t num_prediction_samples = trees[0]->getPredictions()[0].size();
+ size_t num_prediction_samples = data->getNumRows();
predictions.reserve(num_prediction_samples);
- // For all samples take majority vote over all trees
+ // For all samples get tree predictions
for (size_t sample_idx = 0; sample_idx < num_prediction_samples; ++sample_idx) {
- // Count classes over trees and save class with maximum count
- std::unordered_map class_count;
- for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
- double value = trees[tree_idx]->getPredictions()[0][sample_idx];
- ++class_count[value];
+ if (predict_all) {
+ // Get all tree predictions
+ std::vector sample_predictions;
+ sample_predictions.reserve(num_trees);
+ for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
+ double value = ((TreeClassification*) trees[tree_idx])->getPrediction(sample_idx);
+ sample_predictions.push_back(value);
+ }
+ predictions.push_back(sample_predictions);
+ } else {
+ // Count classes over trees and save class with maximum count
+ std::unordered_map class_count;
+ for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
+ double value = ((TreeClassification*) trees[tree_idx])->getPrediction(sample_idx);
+ ++class_count[value];
+ }
+
+ std::vector temp;
+ temp.push_back(mostFrequentValue(class_count, random_number_generator));
+ predictions.push_back(temp);
}
- std::vector temp;
- temp.push_back(mostFrequentValue(class_count, random_number_generator));
- predictions.push_back(temp);
}
}
@@ -136,7 +154,7 @@ void ForestClassification::computePredictionErrorInternal() {
for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
for (size_t sample_idx = 0; sample_idx < trees[tree_idx]->getNumSamplesOob(); ++sample_idx) {
size_t sampleID = trees[tree_idx]->getOobSampleIDs()[sample_idx];
- double value = trees[tree_idx]->getPredictions()[0][sample_idx];
+ double value = ((TreeClassification*) trees[tree_idx])->getPrediction(sample_idx);
++class_counts[sampleID][value];
}
}
@@ -157,7 +175,7 @@ void ForestClassification::computePredictionErrorInternal() {
size_t num_missclassifications = 0;
for (size_t i = 0; i < predictions.size(); ++i) {
double predicted_value = predictions[i][0];
- if (!isnan(predicted_value)) {
+ if (!std::isnan(predicted_value)) {
double real_value = data->get(i, dependent_varID);
if (predicted_value != real_value) {
++num_missclassifications;
@@ -229,8 +247,9 @@ void ForestClassification::writePredictionFile() {
outfile << "Predictions: " << std::endl;
for (size_t i = 0; i < predictions.size(); ++i) {
for (size_t j = 0; j < predictions[i].size(); ++j) {
- outfile << predictions[i][j] << std::endl;
+ outfile << predictions[i][j] << " ";
}
+ outfile << std::endl;
}
*verbose_out << "Saved predictions to file " << filename << "." << std::endl;
@@ -285,7 +304,8 @@ void ForestClassification::loadFromFileInternal(std::ifstream& infile) {
}
// Create tree
- Tree* tree = new TreeClassification(child_nodeIDs, split_varIDs, split_values, &class_values, &response_classIDs);
+ Tree* tree = new TreeClassification(child_nodeIDs, split_varIDs, split_values, &class_values, &response_classIDs,
+ &is_ordered_variable);
trees.push_back(tree);
}
}
diff --git a/source/src/Forest/ForestClassification.h b/source/src/Forest/ForestClassification.h
index 632830883..4eda91f2c 100644
--- a/source/src/Forest/ForestClassification.h
+++ b/source/src/Forest/ForestClassification.h
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#ifndef FORESTCLASSIFICATION_H_
#define FORESTCLASSIFICATION_H_
@@ -45,7 +45,7 @@ class ForestClassification: public Forest {
void loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
- std::vector& class_values);
+ std::vector& class_values, std::vector& is_ordered_variable);
const std::vector& getClassValues() const {
return class_values;
diff --git a/source/src/Forest/ForestProbability.cpp b/source/src/Forest/ForestProbability.cpp
index 57bf1b005..89f986f24 100644
--- a/source/src/Forest/ForestProbability.cpp
+++ b/source/src/Forest/ForestProbability.cpp
@@ -1,30 +1,32 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
+
+#include
#include "utility.h"
#include "ForestProbability.h"
@@ -40,17 +42,18 @@ ForestProbability::~ForestProbability() {
void ForestProbability::loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
- std::vector& class_values, std::vector>>& forest_terminal_class_counts) {
+ std::vector& class_values, std::vector>>& forest_terminal_class_counts, std::vector& is_ordered_variable) {
this->dependent_varID = dependent_varID;
this->num_trees = num_trees;
this->class_values = class_values;
+ this->is_ordered_variable = is_ordered_variable;
// Create trees
trees.reserve(num_trees);
for (size_t i = 0; i < num_trees; ++i) {
Tree* tree = new TreeProbability(forest_child_nodeIDs[i], forest_split_varIDs[i], forest_split_values[i],
- &class_values, &response_classIDs, forest_terminal_class_counts[i]);
+ &this->class_values, &response_classIDs, forest_terminal_class_counts[i], &this->is_ordered_variable);
trees.push_back(tree);
}
@@ -62,8 +65,8 @@ void ForestProbability::initInternal(std::string status_variable_name) {
// If mtry not set, use floored square root of number of independent variables.
if (mtry == 0) {
- int temp = sqrt(num_variables - 1);
- mtry = std::max(1, temp);
+ unsigned long temp = sqrt((double) (num_variables - 1));
+ mtry = std::max((unsigned long) 1, temp);
}
// Set minimal node size
@@ -73,17 +76,29 @@ void ForestProbability::initInternal(std::string status_variable_name) {
// Create class_values and response_classIDs
if (!prediction_mode) {
+
+ // Find all unique response values and sort them
for (size_t i = 0; i < num_samples; ++i) {
double value = data->get(i, dependent_varID);
-
- // If classID is already in class_values, use ID. Else create a new one.
uint classID = find(class_values.begin(), class_values.end(), value) - class_values.begin();
if (classID == class_values.size()) {
class_values.push_back(value);
}
+ }
+ std::sort(class_values.begin(), class_values.end());
+
+ // Assign class ID to each observation
+ for (size_t i = 0; i < num_samples; ++i) {
+ double value = data->get(i, dependent_varID);
+ uint classID = find(class_values.begin(), class_values.end(), value) - class_values.begin();
response_classIDs.push_back(classID);
}
}
+
+ // Sort data if memory saving mode
+ if (!memory_saving_splitting) {
+ data->sort();
+ }
}
void ForestProbability::growInternal() {
@@ -96,7 +111,7 @@ void ForestProbability::growInternal() {
void ForestProbability::predictInternal() {
// First dim samples, second dim classes
- size_t num_prediction_samples = trees[0]->getPredictions().size();
+ size_t num_prediction_samples = data->getNumRows();
predictions.resize(num_prediction_samples);
for (size_t i = 0; i < num_prediction_samples; ++i) {
predictions[i].resize(class_values.size(), 0);
@@ -107,13 +122,10 @@ void ForestProbability::predictInternal() {
// For each sample compute proportions in each tree and average over trees
for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
- std::vector counts = trees[tree_idx]->getPredictions()[sample_idx];
- double sum = 0;
- for (size_t class_idx = 0; class_idx < counts.size(); ++class_idx) {
- sum += counts[class_idx];
- }
+ std::vector counts = ((TreeProbability*) trees[tree_idx])->getPrediction(sample_idx);
+
for (size_t class_idx = 0; class_idx < counts.size(); ++class_idx) {
- predictions[sample_idx][class_idx] += counts[class_idx] / sum / num_trees;
+ predictions[sample_idx][class_idx] += counts[class_idx] / num_trees;
}
}
}
@@ -122,55 +134,39 @@ void ForestProbability::predictInternal() {
void ForestProbability::computePredictionErrorInternal() {
- // Class counts for samples
- std::vector> class_counts;
- class_counts.resize(num_samples, std::vector());
+ // For each sample sum over trees where sample is OOB
+ std::vector samples_oob_count;
+ samples_oob_count.resize(num_samples, 0);
+ predictions.resize(num_samples);
for (size_t i = 0; i < num_samples; ++i) {
- class_counts[i].resize(class_values.size(), 0);
+ predictions[i].resize(class_values.size(), 0);
}
- // For each tree loop over OOB samples and count classes
for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
for (size_t sample_idx = 0; sample_idx < trees[tree_idx]->getNumSamplesOob(); ++sample_idx) {
size_t sampleID = trees[tree_idx]->getOobSampleIDs()[sample_idx];
- std::vector counts = trees[tree_idx]->getPredictions()[sample_idx];
- size_t classID = mostFrequentClass(counts, random_number_generator);
+ std::vector counts = ((TreeProbability*) trees[tree_idx])->getPrediction(sample_idx);
- // classID >= counts.size() means all zero
- if (classID < counts.size()) {
- ++class_counts[sampleID][classID];
+ for (size_t class_idx = 0; class_idx < counts.size(); ++class_idx) {
+ predictions[sampleID][class_idx] += counts[class_idx];
}
+ ++samples_oob_count[sampleID];
}
}
-// Compute majority vote for each sample
- predictions.reserve(num_samples);
- for (size_t i = 0; i < num_samples; ++i) {
- std::vector temp;
- size_t classID = mostFrequentClass(class_counts[i], random_number_generator);
-
- // classID >= class_counts[i].size() means all zero
- if (classID < class_counts[i].size()) {
- temp.push_back(class_values[classID]);
- } else {
- temp.push_back(NAN);
- }
- predictions.push_back(temp);
- }
-
- // Compare predictions with true data
- size_t num_missclassifications = 0;
+ // MSE with predicted probability and true data
for (size_t i = 0; i < predictions.size(); ++i) {
- double predicted_value = predictions[i][0];
- if (!isnan(predicted_value)) {
- double real_value = data->get(i, dependent_varID);
- if (predicted_value != real_value) {
- ++num_missclassifications;
+ if (samples_oob_count[i] > 0) {
+ for (size_t j = 0; j < predictions[i].size(); ++j) {
+ predictions[i][j] /= (double) samples_oob_count[i];
}
- ++classification_table[std::make_pair(real_value, predicted_value)];
+ size_t real_classID = response_classIDs[i];
+ double predicted_value = predictions[i][real_classID];
+ overall_prediction_error += (1 - predicted_value) * (1 - predicted_value);
}
}
- overall_prediction_error = (double) num_missclassifications / (double) predictions.size();
+
+ overall_prediction_error /= (double) predictions.size();
}
void ForestProbability::writeOutputInternal() {
@@ -188,36 +184,10 @@ void ForestProbability::writeConfusionFile() {
}
// Write confusion to file
- outfile << "Overall OOB prediction error (Fraction missclassified): " << overall_prediction_error << std::endl;
- outfile << std::endl;
- outfile << "Class specific prediction errors:" << std::endl;
- outfile << " ";
- for (auto& class_value : class_values) {
- outfile << " " << class_value;
- }
- outfile << std::endl;
- for (auto& predicted_value : class_values) {
- outfile << "predicted " << predicted_value << " ";
- for (auto& real_value : class_values) {
- size_t value = classification_table[std::make_pair(real_value, predicted_value)];
- outfile << value;
- if (value < 10) {
- outfile << " ";
- } else if (value < 100) {
- outfile << " ";
- } else if (value < 1000) {
- outfile << " ";
- } else if (value < 10000) {
- outfile << " ";
- } else if (value < 100000) {
- outfile << " ";
- }
- }
- outfile << std::endl;
- }
+ outfile << "Overall OOB prediction error (MSE): " << overall_prediction_error << std::endl;
outfile.close();
- *verbose_out << "Saved confusion matrix to file " << filename << "." << std::endl;
+ *verbose_out << "Saved prediction error to file " << filename << "." << std::endl;
}
void ForestProbability::writePredictionFile() {
@@ -310,7 +280,7 @@ void ForestProbability::loadFromFileInternal(std::ifstream& infile) {
// Create tree
Tree* tree = new TreeProbability(child_nodeIDs, split_varIDs, split_values, &class_values, &response_classIDs,
- terminal_class_counts);
+ terminal_class_counts, &is_ordered_variable);
trees.push_back(tree);
}
}
diff --git a/source/src/Forest/ForestProbability.h b/source/src/Forest/ForestProbability.h
index 6191cb2ea..99e7d37e3 100644
--- a/source/src/Forest/ForestProbability.h
+++ b/source/src/Forest/ForestProbability.h
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#ifndef FORESTPROBABILITY_H_
#define FORESTPROBABILITY_H_
@@ -45,42 +45,42 @@ class ForestProbability: public Forest {
void loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
- std::vector& class_values, std::vector>>& forest_terminal_class_counts);
-
- std::vector>> getTerminalClassCounts() {
- std::vector>> result;
- result.reserve(num_trees);
- for (Tree* tree : trees) {
- TreeProbability* temp = (TreeProbability*) tree;
- result.push_back(temp->getTerminalClassCounts());
- }
- return result;
- }
-
-const std::vector& getClassValues() const {
- return class_values;
-}
-
-protected:
- void initInternal(std::string status_variable_name);
- void growInternal();
- void predictInternal();
- void computePredictionErrorInternal();
- void writeOutputInternal();
- void writeConfusionFile();
- void writePredictionFile();
- void saveToFileInternal(std::ofstream& outfile);
- void loadFromFileInternal(std::ifstream& infile);
-
- // Classes of the dependent variable and classIDs for responses
- std::vector class_values;
- std::vector response_classIDs;
-
- // Table with classifications and true classes
- std::map, size_t> classification_table;
-
-private:
- DISALLOW_COPY_AND_ASSIGN(ForestProbability);
-};
+ std::vector& class_values, std::vector>>& forest_terminal_class_counts, std::vector& is_ordered_variable);
+
+ std::vector>> getTerminalClassCounts() {
+ std::vector>> result;
+ result.reserve(num_trees);
+ for (Tree* tree : trees) {
+ TreeProbability* temp = (TreeProbability*) tree;
+ result.push_back(temp->getTerminalClassCounts());
+ }
+ return result;
+ }
+
+ const std::vector& getClassValues() const {
+ return class_values;
+ }
+
+ protected:
+ void initInternal(std::string status_variable_name);
+ void growInternal();
+ void predictInternal();
+ void computePredictionErrorInternal();
+ void writeOutputInternal();
+ void writeConfusionFile();
+ void writePredictionFile();
+ void saveToFileInternal(std::ofstream& outfile);
+ void loadFromFileInternal(std::ifstream& infile);
+
+ // Classes of the dependent variable and classIDs for responses
+ std::vector class_values;
+ std::vector response_classIDs;
+
+ // Table with classifications and true classes
+ std::map, size_t> classification_table;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ForestProbability);
+ };
#endif /* FORESTPROBABILITY_H_ */
diff --git a/source/src/Forest/ForestRegression.cpp b/source/src/Forest/ForestRegression.cpp
index 3204eef3b..3b131ac5d 100644
--- a/source/src/Forest/ForestRegression.cpp
+++ b/source/src/Forest/ForestRegression.cpp
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#include
#include
@@ -43,15 +43,18 @@ ForestRegression::~ForestRegression() {
void ForestRegression::loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
- std::vector>& forest_split_varIDs, std::vector>& forest_split_values) {
+ std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
+ std::vector& is_ordered_variable) {
this->dependent_varID = dependent_varID;
this->num_trees = num_trees;
+ this->is_ordered_variable = is_ordered_variable;
// Create trees
trees.reserve(num_trees);
for (size_t i = 0; i < num_trees; ++i) {
- Tree* tree = new TreeRegression(forest_child_nodeIDs[i], forest_split_varIDs[i], forest_split_values[i]);
+ Tree* tree = new TreeRegression(forest_child_nodeIDs[i], forest_split_varIDs[i], forest_split_values[i],
+ &this->is_ordered_variable);
trees.push_back(tree);
}
@@ -63,14 +66,19 @@ void ForestRegression::initInternal(std::string status_variable_name) {
// If mtry not set, use number of independent variables / 3.
if (mtry == 0) {
- uint temp = (num_variables - 1) / 3;
- mtry = std::max((uint) 1, temp);
+ unsigned long temp = sqrt((double) (num_variables - 1));
+ mtry = std::max((unsigned long) 1, temp);
}
// Set minimal node size
if (min_node_size == 0) {
min_node_size = DEFAULT_MIN_NODE_SIZE_REGRESSION;
}
+
+ // Sort data if memory saving mode
+ if (!memory_saving_splitting) {
+ data->sort();
+ }
}
void ForestRegression::growInternal() {
@@ -82,43 +90,56 @@ void ForestRegression::growInternal() {
void ForestRegression::predictInternal() {
- size_t num_prediction_samples = trees[0]->getPredictions()[0].size();
+ size_t num_prediction_samples = data->getNumRows();
predictions.reserve(num_prediction_samples);
- // For all samples use mean over all trees
+ // For all samples get tree predictions
for (size_t sample_idx = 0; sample_idx < num_prediction_samples; ++sample_idx) {
- double prediction_sum = 0;
- for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
- prediction_sum += trees[tree_idx]->getPredictions()[0][sample_idx];
+
+ if (predict_all) {
+ // Get all tree predictions
+ std::vector sample_predictions;
+ sample_predictions.reserve(num_trees);
+ for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
+ double value = ((TreeRegression*) trees[tree_idx])->getPrediction(sample_idx);
+ sample_predictions.push_back(value);
+ }
+ predictions.push_back(sample_predictions);
+ } else {
+ // Mean over trees
+ double prediction_sum = 0;
+ for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
+ prediction_sum += ((TreeRegression*) trees[tree_idx])->getPrediction(sample_idx);
+ }
+ std::vector temp;
+ temp.push_back(prediction_sum / num_trees);
+ predictions.push_back(temp);
}
- std::vector temp;
- temp.push_back(prediction_sum / num_trees);
- predictions.push_back(temp);
}
}
void ForestRegression::computePredictionErrorInternal() {
- // For each sample sum over trees where sample is OOB
+// For each sample sum over trees where sample is OOB
std::vector samples_oob_count;
predictions.reserve(num_samples);
samples_oob_count.resize(num_samples, 0);
for (size_t i = 0; i < num_samples; ++i) {
- std::vector temp {0};
+ std::vector temp { 0 };
predictions.push_back(temp);
}
for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
for (size_t sample_idx = 0; sample_idx < trees[tree_idx]->getNumSamplesOob(); ++sample_idx) {
size_t sampleID = trees[tree_idx]->getOobSampleIDs()[sample_idx];
- double value = trees[tree_idx]->getPredictions()[0][sample_idx];
+ double value = ((TreeRegression*) trees[tree_idx])->getPrediction(sample_idx);
predictions[sampleID][0] += value;
++samples_oob_count[sampleID];
}
}
- // MSE with predictions and true data
- //oob_anytree_sampleIDs.reserve(predictions.size());
+// MSE with predictions and true data
+//oob_anytree_sampleIDs.reserve(predictions.size());
for (size_t i = 0; i < predictions.size(); ++i) {
if (samples_oob_count[i] > 0) {
//oob_anytree_sampleIDs.push_back(i);
@@ -138,7 +159,7 @@ void ForestRegression::writeOutputInternal() {
void ForestRegression::writeConfusionFile() {
- // Open confusion file for writing
+// Open confusion file for writing
std::string filename = output_prefix + ".confusion";
std::ofstream outfile;
outfile.open(filename, std::ios::out);
@@ -146,7 +167,7 @@ void ForestRegression::writeConfusionFile() {
throw std::runtime_error("Could not write to confusion file: " + filename + ".");
}
- // Write confusion to file
+// Write confusion to file
outfile << "Overall OOB prediction error (MSE): " << overall_prediction_error << std::endl;
outfile.close();
@@ -155,7 +176,7 @@ void ForestRegression::writeConfusionFile() {
void ForestRegression::writePredictionFile() {
- // Open prediction file for writing
+// Open prediction file for writing
std::string filename = output_prefix + ".prediction";
std::ofstream outfile;
outfile.open(filename, std::ios::out);
@@ -167,8 +188,9 @@ void ForestRegression::writePredictionFile() {
outfile << "Predictions: " << std::endl;
for (size_t i = 0; i < predictions.size(); ++i) {
for (size_t j = 0; j < predictions[i].size(); ++j) {
- outfile << predictions[i][j] << std::endl;
+ outfile << predictions[i][j] << " ";
}
+ outfile << std::endl;
}
*verbose_out << "Saved predictions to file " << filename << "." << std::endl;
@@ -176,21 +198,21 @@ void ForestRegression::writePredictionFile() {
void ForestRegression::saveToFileInternal(std::ofstream& outfile) {
- // Write num_variables
+// Write num_variables
outfile.write((char*) &num_variables, sizeof(num_variables));
- // Write treetype
+// Write treetype
TreeType treetype = TREE_REGRESSION;
outfile.write((char*) &treetype, sizeof(treetype));
}
void ForestRegression::loadFromFileInternal(std::ifstream& infile) {
- // Read number of variables
+// Read number of variables
size_t num_variables_saved;
infile.read((char*) &num_variables_saved, sizeof(num_variables_saved));
- // Read treetype
+// Read treetype
TreeType treetype;
infile.read((char*) &treetype, sizeof(treetype));
if (treetype != TREE_REGRESSION) {
@@ -217,7 +239,7 @@ void ForestRegression::loadFromFileInternal(std::ifstream& infile) {
}
// Create tree
- Tree* tree = new TreeRegression(child_nodeIDs, split_varIDs, split_values);
+ Tree* tree = new TreeRegression(child_nodeIDs, split_varIDs, split_values, &is_ordered_variable);
trees.push_back(tree);
}
}
diff --git a/source/src/Forest/ForestRegression.h b/source/src/Forest/ForestRegression.h
index 263cf39d2..6e782191b 100644
--- a/source/src/Forest/ForestRegression.h
+++ b/source/src/Forest/ForestRegression.h
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#ifndef FORESTREGRESSION_H_
#define FORESTREGRESSION_H_
@@ -42,7 +42,8 @@ class ForestRegression: public Forest {
void loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
- std::vector>& forest_split_varIDs, std::vector>& forest_split_values);
+ std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
+ std::vector& is_ordered_variable);
private:
void initInternal(std::string status_variable_name);
diff --git a/source/src/Forest/ForestSurvival.cpp b/source/src/Forest/ForestSurvival.cpp
index e48476180..ef6b725cf 100644
--- a/source/src/Forest/ForestSurvival.cpp
+++ b/source/src/Forest/ForestSurvival.cpp
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#include
#include
@@ -37,7 +37,7 @@ wright@imbs.uni-luebeck.de
#include "Data.h"
ForestSurvival::ForestSurvival() :
- status_varID(0) {
+ status_varID(0), response_timepointIDs(0) {
}
ForestSurvival::~ForestSurvival() {
@@ -47,17 +47,19 @@ void ForestSurvival::loadForest(size_t dependent_varID, size_t num_trees,
std::vector> >& forest_child_nodeIDs,
std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
size_t status_varID, std::vector> >& forest_chf,
- std::vector& unique_timepoints) {
+ std::vector& unique_timepoints, std::vector& is_ordered_variable) {
this->dependent_varID = dependent_varID;
this->status_varID = status_varID;
this->num_trees = num_trees;
+ this->is_ordered_variable = is_ordered_variable;
+ this->unique_timepoints = unique_timepoints;
// Create trees
trees.reserve(num_trees);
for (size_t i = 0; i < num_trees; ++i) {
Tree* tree = new TreeSurvival(forest_child_nodeIDs[i], forest_split_varIDs[i], forest_split_values[i],
- forest_chf[i], &unique_timepoints);
+ forest_chf[i], &this->unique_timepoints, &response_timepointIDs, &this->is_ordered_variable);
trees.push_back(tree);
}
@@ -76,8 +78,8 @@ void ForestSurvival::initInternal(std::string status_variable_name) {
// If mtry not set, use floored square root of number of independent variables.
if (mtry == 0) {
- int temp = ceil(sqrt(num_variables - 2));
- mtry = std::max(1, temp);
+ unsigned long temp = ceil(sqrt((double) (num_variables - 2)));
+ mtry = std::max((unsigned long) 1, temp);
}
// Set minimal node size
@@ -88,27 +90,36 @@ void ForestSurvival::initInternal(std::string status_variable_name) {
// Create unique timepoints
std::set unique_timepoint_set;
for (size_t i = 0; i < num_samples; ++i) {
- if (data->get(i, status_varID) == 1) {
- unique_timepoint_set.insert(data->get(i, dependent_varID));
- }
+ unique_timepoint_set.insert(data->get(i, dependent_varID));
}
unique_timepoints.reserve(unique_timepoint_set.size());
for (auto& t : unique_timepoint_set) {
unique_timepoints.push_back(t);
}
+
+ // Create response_timepointIDs
+ if (!prediction_mode) {
+ for (size_t i = 0; i < num_samples; ++i) {
+ double value = data->get(i, dependent_varID);
+
+ // If timepoint is already in unique_timepoints, use ID. Else create a new one.
+ uint timepointID = find(unique_timepoints.begin(), unique_timepoints.end(), value) - unique_timepoints.begin();
+ response_timepointIDs.push_back(timepointID);
+ }
+ }
}
void ForestSurvival::growInternal() {
trees.reserve(num_trees);
for (size_t i = 0; i < num_trees; ++i) {
- trees.push_back(new TreeSurvival(&unique_timepoints, status_varID));
+ trees.push_back(new TreeSurvival(&unique_timepoints, status_varID, &response_timepointIDs));
}
}
void ForestSurvival::predictInternal() {
- size_t num_prediction_samples = trees[0]->getPredictions().size();
- size_t num_timepoints = trees[0]->getPredictions()[0].size();
+ size_t num_prediction_samples = data->getNumRows();
+ size_t num_timepoints = unique_timepoints.size();
predictions.reserve(num_prediction_samples);
@@ -120,9 +131,9 @@ void ForestSurvival::predictInternal() {
for (size_t j = 0; j < num_timepoints; ++j) {
double sample_time_prediction = 0;
for (size_t k = 0; k < num_trees; ++k) {
- sample_time_prediction += trees[k]->getPredictions()[i][j];
+ sample_time_prediction += ((TreeSurvival*) trees[k])->getPrediction(i)[j];
}
- sample_prediction.push_back(sample_time_prediction/num_trees);
+ sample_prediction.push_back(sample_time_prediction / num_trees);
}
predictions.push_back(sample_prediction);
}
@@ -131,7 +142,7 @@ void ForestSurvival::predictInternal() {
void ForestSurvival::computePredictionErrorInternal() {
- size_t num_timepoints = trees[0]->getPredictions()[0].size();
+ size_t num_timepoints = unique_timepoints.size();
// For each sample sum over trees where sample is OOB
std::vector samples_oob_count;
@@ -145,7 +156,7 @@ void ForestSurvival::computePredictionErrorInternal() {
for (size_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) {
for (size_t sample_idx = 0; sample_idx < trees[tree_idx]->getNumSamplesOob(); ++sample_idx) {
size_t sampleID = trees[tree_idx]->getOobSampleIDs()[sample_idx];
- std::vector tree_sample_chf = trees[tree_idx]->getPredictions()[sample_idx];
+ std::vector tree_sample_chf = ((TreeSurvival*) trees[tree_idx])->getPrediction(sample_idx);
for (size_t time_idx = 0; time_idx < tree_sample_chf.size(); ++time_idx) {
predictions[sampleID][time_idx] += tree_sample_chf[time_idx];
@@ -158,12 +169,14 @@ void ForestSurvival::computePredictionErrorInternal() {
std::vector sum_chf;
sum_chf.reserve(predictions.size());
for (size_t i = 0; i < predictions.size(); ++i) {
- double sum = 0;
- for (size_t j = 0; j < predictions[i].size(); ++j) {
- predictions[i][j] /= samples_oob_count[i];
- sum += predictions[i][j];
+ if (samples_oob_count[i] > 0) {
+ double sum = 0;
+ for (size_t j = 0; j < predictions[i].size(); ++j) {
+ predictions[i][j] /= samples_oob_count[i];
+ sum += predictions[i][j];
+ }
+ sum_chf.push_back(sum);
}
- sum_chf.push_back(sum);
}
// Use empty vector to use all samples in computeConcordanceIndex
@@ -301,7 +314,8 @@ void ForestSurvival::loadFromFileInternal(std::ifstream& infile) {
}
// Create tree
- Tree* tree = new TreeSurvival(child_nodeIDs, split_varIDs, split_values, chf, &unique_timepoints);
+ Tree* tree = new TreeSurvival(child_nodeIDs, split_varIDs, split_values, chf, &unique_timepoints,
+ &response_timepointIDs, &is_ordered_variable);
trees.push_back(tree);
}
}
diff --git a/source/src/Forest/ForestSurvival.h b/source/src/Forest/ForestSurvival.h
index c02337bda..9f655fcc1 100644
--- a/source/src/Forest/ForestSurvival.h
+++ b/source/src/Forest/ForestSurvival.h
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#ifndef FORESTSURVIVAL_H_
#define FORESTSURVIVAL_H_
@@ -45,7 +45,7 @@ class ForestSurvival: public Forest {
std::vector> >& forest_child_nodeIDs,
std::vector>& forest_split_varIDs, std::vector>& forest_split_values,
size_t status_varID, std::vector> >& forest_chf,
- std::vector& unique_timepoints);
+ std::vector& unique_timepoints, std::vector& is_ordered_variable);
std::vector>>getChf() {
std::vector>> result;
@@ -76,6 +76,7 @@ class ForestSurvival: public Forest {
size_t status_varID;
std::vector unique_timepoints;
+ std::vector response_timepointIDs;
DISALLOW_COPY_AND_ASSIGN(ForestSurvival);
};
diff --git a/source/src/Tree/Tree.cpp b/source/src/Tree/Tree.cpp
index 53af9c476..e3f60c42f 100644
--- a/source/src/Tree/Tree.cpp
+++ b/source/src/Tree/Tree.cpp
@@ -1,30 +1,30 @@
/*-------------------------------------------------------------------------------
-This file is part of Ranger.
-
-Ranger is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+ This file is part of Ranger.
-Ranger is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ Ranger is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
-You should have received a copy of the GNU General Public License
-along with Ranger. If not, see .
+ Ranger is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-Written by:
+ You should have received a copy of the GNU General Public License
+ along with Ranger. If not, see .
-Marvin N. Wright
-Institut für Medizinische Biometrie und Statistik
-Universität zu Lübeck
-Ratzeburger Allee 160
-23562 Lübeck
+ Written by:
-http://www.imbs-luebeck.de
-wright@imbs.uni-luebeck.de
-#-------------------------------------------------------------------------------*/
+ Marvin N. Wright
+ Institut für Medizinische Biometrie und Statistik
+ Universität zu Lübeck
+ Ratzeburger Allee 160
+ 23562 Lübeck
+
+ http://www.imbs-luebeck.de
+ wright@imbs.uni-luebeck.de
+ #-------------------------------------------------------------------------------*/
#include
@@ -32,17 +32,19 @@ wright@imbs.uni-luebeck.de
#include "utility.h"
Tree::Tree() :
- dependent_varID(0), mtry(0), num_samples(0), num_samples_oob(0), no_split_variables(0), min_node_size(0), deterministic_varIDs(
- 0), split_select_varIDs(0), split_select_weights(0), oob_sampleIDs(0), data(0), importance_mode(
- DEFAULT_IMPORTANCE_MODE), sample_with_replacement(true), splitrule(DEFAULT_SPLITRULE) {
+ dependent_varID(0), mtry(0), num_samples(0), num_samples_oob(0), is_ordered_variable(0), no_split_variables(0), min_node_size(
+ 0), deterministic_varIDs(0), split_select_varIDs(0), split_select_weights(0), case_weights(0), oob_sampleIDs(0), keep_inbag(
+ false), data(0), variable_importance(0), importance_mode(DEFAULT_IMPORTANCE_MODE), sample_with_replacement(
+ true), sample_fraction(1), memory_saving_splitting(false), splitrule(DEFAULT_SPLITRULE) {
}
Tree::Tree(std::vector>& child_nodeIDs, std::vector& split_varIDs,
- std::vector& split_values) :
- dependent_varID(0), mtry(0), num_samples(0), num_samples_oob(0), no_split_variables(0), min_node_size(0), deterministic_varIDs(
- 0), split_select_varIDs(0), split_select_weights(0), split_varIDs(split_varIDs), split_values(split_values), child_nodeIDs(
- child_nodeIDs), oob_sampleIDs(0), data(0), importance_mode(DEFAULT_IMPORTANCE_MODE), sample_with_replacement(
- true), splitrule(DEFAULT_SPLITRULE) {
+ std::vector& split_values, std::vector* is_ordered_variable) :
+ dependent_varID(0), mtry(0), num_samples(0), num_samples_oob(0), is_ordered_variable(is_ordered_variable), no_split_variables(
+ 0), min_node_size(0), deterministic_varIDs(0), split_select_varIDs(0), split_select_weights(0), case_weights(0), split_varIDs(
+ split_varIDs), split_values(split_values), child_nodeIDs(child_nodeIDs), oob_sampleIDs(0), keep_inbag(false), data(
+ 0), variable_importance(0), importance_mode(DEFAULT_IMPORTANCE_MODE), sample_with_replacement(true), sample_fraction(
+ 1), memory_saving_splitting(false), splitrule(DEFAULT_SPLITRULE) {
}
Tree::~Tree() {
@@ -51,12 +53,15 @@ Tree::~Tree() {
void Tree::init(Data* data, uint mtry, size_t dependent_varID, size_t num_samples, uint seed,
std::vector* deterministic_varIDs, std::vector* split_select_varIDs,
std::vector* split_select_weights, ImportanceMode importance_mode, uint min_node_size,
- std::vector* no_split_variables, bool sample_with_replacement, uint splitrule) {
+ std::vector* no_split_variables, bool sample_with_replacement, std::vector* is_unordered,
+ bool memory_saving_splitting, SplitRule splitrule, std::vector* case_weights, bool keep_inbag,
+ double sample_fraction) {
this->data = data;
this->mtry = mtry;
this->dependent_varID = dependent_varID;
this->num_samples = num_samples;
+ this->memory_saving_splitting = memory_saving_splitting;
// Create root node, assign bootstrap sample and oob samples
createEmptyNode();
@@ -70,28 +75,51 @@ void Tree::init(Data* data, uint mtry, size_t dependent_varID, size_t num_sample
this->importance_mode = importance_mode;
this->min_node_size = min_node_size;
this->no_split_variables = no_split_variables;
+ this->is_ordered_variable = is_unordered;
this->sample_with_replacement = sample_with_replacement;
this->splitrule = splitrule;
+ this->case_weights = case_weights;
+ this->keep_inbag = keep_inbag;
+ this->sample_fraction = sample_fraction;
- // Initialize with variable importance with 0.
- if (importance_mode == IMP_GINI) {
- variable_importance.resize(data->getNumCols() - no_split_variables->size());
- }
+ initInternal();
}
-void Tree::grow() {
+void Tree::grow(std::vector* variable_importance) {
+
+ this->variable_importance = variable_importance;
- if (sample_with_replacement) {
- bootstrap();
+ // Bootstrap, dependent if weighted or not and with or without replacement
+ if (case_weights->empty()) {
+ if (sample_with_replacement) {
+ bootstrap();
+ } else {
+ bootstrapWithoutReplacement();
+ }
} else {
- bootstrapWithoutReplacement();
+ if (sample_with_replacement) {
+ bootstrapWeighted();
+ } else {
+ bootstrapWithoutReplacementWeighted();
+ }
}
- // Call recursive split function on root node
- splitNode(0);
+ // While not all nodes terminal, split next node
+ size_t num_open_nodes = 1;
+ size_t i = 0;
+ while (num_open_nodes > 0) {
+ bool is_terminal_node = splitNode(i);
+ if (is_terminal_node) {
+ --num_open_nodes;
+ } else {
+ ++num_open_nodes;
+ }
+ ++i;
+ }
// Delete sampleID vector to save memory
sampleIDs.clear();
+ cleanUpInternal();
}
void Tree::predict(const Data* prediction_data, bool oob_prediction) {
@@ -103,7 +131,7 @@ void Tree::predict(const Data* prediction_data, bool oob_prediction) {
num_samples_predict = prediction_data->getNumRows();
}
- reservePredictionMemory(num_samples_predict);
+ prediction_terminal_nodeIDs.resize(num_samples_predict, 0);
// For each sample start in root, drop down the tree and return final value
for (size_t i = 0; i < num_samples_predict; ++i) {
@@ -122,31 +150,47 @@ void Tree::predict(const Data* prediction_data, bool oob_prediction) {
}
// Move to child
- double value = prediction_data->get(sample_idx, split_varIDs[nodeID]);
- if (value <= split_values[nodeID]) {
- // Move to left child
- nodeID = child_nodeIDs[nodeID][0];
+ size_t split_varID = split_varIDs[nodeID];
+ double value = prediction_data->get(sample_idx, split_varID);
+ if ((*is_ordered_variable)[split_varID]) {
+ if (value <= split_values[nodeID]) {
+ // Move to left child
+ nodeID = child_nodeIDs[nodeID][0];
+ } else {
+ // Move to right child
+ nodeID = child_nodeIDs[nodeID][1];
+ }
} else {
- // Move to right child
- nodeID = child_nodeIDs[nodeID][1];
+ size_t factorID = floor(value) - 1;
+ size_t splitID = floor(split_values[nodeID]);
+
+ // Left if 0 found at position factorID
+ if (!(splitID & (1 << factorID))) {
+ // Move to left child
+ nodeID = child_nodeIDs[nodeID][0];
+ } else {
+ // Move to right child
+ nodeID = child_nodeIDs[nodeID][1];
+ }
}
}
- addPrediction(nodeID, i);
+ prediction_terminal_nodeIDs[i] = nodeID;
}
}
-void Tree::computePermutationImportance() {
+void Tree::computePermutationImportance(std::vector* forest_importance, std::vector* forest_variance) {
size_t num_independent_variables = data->getNumCols() - no_split_variables->size();
- variable_importance.clear();
- variable_importance.reserve(num_independent_variables);
// Compute normal prediction accuracy for each tree. Predictions already computed..
double accuracy_normal = computePredictionAccuracyInternal();
- predictions.clear();
- reservePredictionMemory(num_samples_oob);
+ prediction_terminal_nodeIDs.clear();
+ prediction_terminal_nodeIDs.resize(num_samples_oob, 0);
+
+ // Reserve space for permutations, initialize with oob_sampleIDs
+ std::vector permutations(oob_sampleIDs);
// Randomly permute for all independent variables
for (size_t i = 0; i < num_independent_variables; ++i) {
@@ -160,9 +204,17 @@ void Tree::computePermutationImportance() {
}
// Permute and compute prediction accuracy again for this permutation and save difference
- permuteAndPredictOobSamples(varID);
+ permuteAndPredictOobSamples(varID, permutations);
double accuracy_permuted = computePredictionAccuracyInternal();
- variable_importance.push_back(accuracy_normal - accuracy_permuted);
+ double accuracy_difference = accuracy_normal - accuracy_permuted;
+ (*forest_importance)[i] += accuracy_difference;
+
+ // Compute variance
+ if (importance_mode == IMP_PERM_BREIMAN) {
+ (*forest_variance)[i] += accuracy_difference * accuracy_difference;
+ } else if (importance_mode == IMP_PERM_LIAW) {
+ (*forest_variance)[i] += accuracy_difference * accuracy_difference * num_samples_oob;
+ }
}
}
@@ -177,7 +229,7 @@ void Tree::appendToFile(std::ofstream& file) {
appendToFileInternal(file);
}
-void Tree::createPossibleSplitVarSubset(std::unordered_set& result) {
+void Tree::createPossibleSplitVarSubset(std::vector& result) {
// Always use deterministic variables
std::copy(deterministic_varIDs->begin(), deterministic_varIDs->end(), std::inserter(result, result.end()));
@@ -192,16 +244,17 @@ void Tree::createPossibleSplitVarSubset(std::unordered_set& result) {
}
}
-void Tree::splitNode(size_t nodeID) {
+bool Tree::splitNode(size_t nodeID) {
// Select random subset of variables to possibly split at
- std::unordered_set possible_split_varIDs;
+ std::vector possible_split_varIDs;
createPossibleSplitVarSubset(possible_split_varIDs);
// Call subclass method, sets split_varIDs and split_values
bool stop = splitNodeInternal(nodeID, possible_split_varIDs);
if (stop) {
- return;
+ // Terminal node
+ return true;
}
size_t split_varID = split_varIDs[nodeID];
@@ -216,19 +269,34 @@ void Tree::splitNode(size_t nodeID) {
child_nodeIDs[nodeID].push_back(right_child_nodeID);
createEmptyNode();
- // For each sample in node, assign to left (<= split val) or right (> split val) child
- for (auto& sampleID : sampleIDs[nodeID]) {
- if (data->get(sampleID, split_varID) <= split_value) {
- sampleIDs[left_child_nodeID].push_back(sampleID);
- } else {
- sampleIDs[right_child_nodeID].push_back(sampleID);
+ // For each sample in node, assign to left or right child
+ if ((*is_ordered_variable)[split_varID]) {
+ // Ordered: left is <= splitval and right is > splitval
+ for (auto& sampleID : sampleIDs[nodeID]) {
+ if (data->get(sampleID, split_varID) <= split_value) {
+ sampleIDs[left_child_nodeID].push_back(sampleID);
+ } else {
+ sampleIDs[right_child_nodeID].push_back(sampleID);
+ }
+ }
+ } else {
+ // Unordered: If bit at position is 1 -> right, 0 -> left
+ for (auto& sampleID : sampleIDs[nodeID]) {
+ double level = data->get(sampleID, split_varID);
+ size_t factorID = floor(level) - 1;
+ size_t splitID = floor(split_value);
+
+ // Left if 0 found at position factorID
+ if (!(splitID & (1 << factorID))) {
+ sampleIDs[left_child_nodeID].push_back(sampleID);
+ } else {
+ sampleIDs[right_child_nodeID].push_back(sampleID);
+ }
}
}
- // Recursively call split node on child nodes
- for (size_t i = 0; i < child_nodeIDs[nodeID].size(); ++i) {
- splitNode(child_nodeIDs[nodeID][i]);
- }
+ // No terminal node
+ return false;
}
void Tree::createEmptyNode() {
@@ -254,68 +322,153 @@ size_t Tree::dropDownSamplePermuted(size_t permuted_varID, size_t sampleID, size
}
// Move to child
- if (data->get(sampleID_final, split_varID) <= split_values[nodeID]) {
- // Move to left child
- nodeID = child_nodeIDs[nodeID][0];
+ double value = data->get(sampleID_final, split_varID);
+ if ((*is_ordered_variable)[split_varID]) {
+ if (value <= split_values[nodeID]) {
+ // Move to left child
+ nodeID = child_nodeIDs[nodeID][0];
+ } else {
+ // Move to right child
+ nodeID = child_nodeIDs[nodeID][1];
+ }
} else {
- // Move to right child
- nodeID = child_nodeIDs[nodeID][1];
+ size_t factorID = floor(value) - 1;
+ size_t splitID = floor(split_values[nodeID]);
+
+ // Left if 0 found at position factorID
+ if (!(splitID & (1 << factorID))) {
+ // Move to left child
+ nodeID = child_nodeIDs[nodeID][0];
+ } else {
+ // Move to right child
+ nodeID = child_nodeIDs[nodeID][1];
+ }
}
+
}
return nodeID;
}
-void Tree::permuteAndPredictOobSamples(size_t permuted_varID) {
+void Tree::permuteAndPredictOobSamples(size_t permuted_varID, std::vector& permutations) {
// Permute OOB sample
- std::vector permutations(oob_sampleIDs);
+ //std::vector permutations(oob_sampleIDs);
std::shuffle(permutations.begin(), permutations.end(), random_number_generator);
// For each sample, drop down the tree and add prediction
for (size_t i = 0; i < num_samples_oob; ++i) {
size_t nodeID = dropDownSamplePermuted(permuted_varID, oob_sampleIDs[i], permutations[i]);
- addPrediction(nodeID, i);
+ prediction_terminal_nodeIDs[i] = nodeID;
}
}
void Tree::bootstrap() {
- // Reserve space (37% percent are outbag on average, reserve a little more)
- sampleIDs[0].reserve(num_samples);
- oob_sampleIDs.reserve(num_samples * 0.4);
+ // Use fraction (default 63.21%) of the samples
+ size_t num_samples_inbag = (size_t) num_samples * sample_fraction;
+
+ // Reserve space, reserve a little more to be save)
+ sampleIDs[0].reserve(num_samples_inbag);
+ oob_sampleIDs.reserve(num_samples * (exp(-sample_fraction) + 0.1));
std::uniform_int_distribution unif_dist(0, num_samples - 1);
// Start with all samples OOB
- std::vector is_oob;
- is_oob.resize(num_samples, true);
+ inbag_counts.resize(num_samples, 0);
- // Draw num_samples samples with replacement (n out of n) as inbag and mark as not OOB
- for (size_t s = 0; s < num_samples; ++s) {
+ // Draw num_samples samples with replacement (num_samples_inbag out of n) as inbag and mark as not OOB
+ for (size_t s = 0; s < num_samples_inbag; ++s) {
size_t draw = unif_dist(random_number_generator);
sampleIDs[0].push_back(draw);
- is_oob[draw] = false;
+ ++inbag_counts[draw];
+ }
+
+ // Save OOB samples
+ for (size_t s = 0; s < inbag_counts.size(); ++s) {
+ if (inbag_counts[s] == 0) {
+ oob_sampleIDs.push_back(s);
+ }
+ }
+ num_samples_oob = oob_sampleIDs.size();
+
+ if (!keep_inbag) {
+ inbag_counts.clear();
+ }
+}
+
+void Tree::bootstrapWeighted() {
+
+ // Use fraction (default 63.21%) of the samples
+ size_t num_samples_inbag = (size_t) num_samples * sample_fraction;
+
+ // Reserve space, reserve a little more to be save)
+ sampleIDs[0].reserve(num_samples_inbag);
+ oob_sampleIDs.reserve(num_samples * (exp(-sample_fraction) + 0.1));
+
+ std::discrete_distribution<> weighted_dist(case_weights->begin(), case_weights->end());
+
+ // Start with all samples OOB
+ inbag_counts.resize(num_samples, 0);
+
+ // Draw num_samples samples with replacement (n out of n) as inbag and mark as not OOB
+ for (size_t s = 0; s < num_samples_inbag; ++s) {
+ size_t draw = weighted_dist(random_number_generator);
+ sampleIDs[0].push_back(draw);
+ ++inbag_counts[draw];
}
// Save OOB samples
- for (size_t s = 0; s < is_oob.size(); ++s) {
- if (is_oob[s]) {
+ for (size_t s = 0; s < inbag_counts.size(); ++s) {
+ if (inbag_counts[s] == 0) {
oob_sampleIDs.push_back(s);
}
}
num_samples_oob = oob_sampleIDs.size();
+
+ if (!keep_inbag) {
+ inbag_counts.clear();
+ }
}
void Tree::bootstrapWithoutReplacement() {
- // As in sampling with replacement use 63.21% of the samples
- size_t num_samples_inbag = (size_t) num_samples * 0.6321;
+ // Use fraction (default 63.21%) of the samples
+ size_t num_samples_inbag = (size_t) num_samples * sample_fraction;
shuffleAndSplit(sampleIDs[0], oob_sampleIDs, num_samples, num_samples_inbag, random_number_generator);
num_samples_oob = oob_sampleIDs.size();
+
+ if (keep_inbag) {
+ // All observation are 0 or 1 times inbag
+ inbag_counts.resize(num_samples, 1);
+ for (size_t i = 0; i < oob_sampleIDs.size(); i++) {
+ inbag_counts[oob_sampleIDs[i]] = 0;
+ }
+ }
}
+void Tree::bootstrapWithoutReplacementWeighted() {
+ // Use fraction (default 63.21%) of the samples
+ size_t num_samples_inbag = (size_t) num_samples * sample_fraction;
+ drawWithoutReplacementWeighted(sampleIDs[0], random_number_generator, num_samples - 1, num_samples_inbag,
+ *case_weights);
+ // All observation are 0 or 1 times inbag
+ inbag_counts.resize(num_samples, 0);
+ for (auto& sampleID : sampleIDs[0]) {
+ inbag_counts[sampleID] = 1;
+ }
+ // Save OOB samples
+ for (size_t s = 0; s < inbag_counts.size(); ++s) {
+ if (inbag_counts[s] == 0) {
+ oob_sampleIDs.push_back(s);
+ }
+ }
+ num_samples_oob = oob_sampleIDs.size();
+ if (!keep_inbag) {
+ inbag_counts.clear();
+ }
+}
diff --git a/source/src/Tree/Tree.h b/source/src/Tree/Tree.h
index d2c688a26..35398ee3a 100644
--- a/source/src/Tree/Tree.h
+++ b/source/src/Tree/Tree.h
@@ -31,7 +31,6 @@
#include
#include
-#include
#include