Error trying to do cross validation after a classification tree - r

I am trying to run a simple classification tree using the tree package. I have taken the code from a textbook, copied one by one, but it doesn't work, no matter what I do.
library(ISLR)
library(tree)
C = Carseats
C$HighSales = ifelse(C$Sales<=8,"No","Yes")
C = C[,-1]
set.seed(2)
train = sample(1:nrow(C), 200)
carseats.test = C[-train,]
high.test = C$HighSales[-train]
tree.carseats = tree(HighSales~., C, subset = train)
tree.predict = predict(tree.carseats, carseats.test, type = "class")
table(tree.predict,high.test)
(93+48)/200
set.seed(3)
cv.cs = cv.tree(tree.carseats, FUN = prune.misclass)
I am getting the following error:
Error in as.data.frame.default(data, optional = TRUE) :
cannot coerce class ‘"function"’ to a data.frame
I have looked at the help of the function. It requires a tree object, which is what I put inside.
What can be the problem ? The code is identical to the textbook and to other websites who quote the book.

There are two problems. One is related to the formula in tree:
formula - A formula expression. The left-hand-side (response) should be either a numerical vector when a regression tree will be fitted or a factor, when a classification tree is produced. The right-hand-side should be a series of numeric or factor variables separated by +; there should be no interaction terms. Both . and - are allowed: regression trees can have offset terms.
So, we should instead have
C$HighSales <- factor(ifelse(C$Sales <= 8, "No", "Yes"))
Next, there's a problem with how cv.tree deals with variables (see here). Doing something like
mydf <- C
tree.carseats <- tree(HighSales ~ ., mydf, subset = train)
works. The issue is that there's a function called C and cv.tree refers exactly to this function rather than your dataset.

Related

How to input matrix data into brms formula?

I am trying to input matrix data into the brm() function to run a signal regression. brm is from the brms package, which provides an interface to fit Bayesian models using Stan. Signal regression is when you model one covariate using another within the bigger model, and you use the by parameter like this: model <- brm(response ~ s(matrix1, by = matrix2) + ..., data = Data). The problem is, I cannot input my matrices using the 'data' parameter because it only allows one data.frame object to be inputted.
Here are my code and the errors I obtained from trying to get around that constraint...
First off, my reproducible code leading up to the model-building:
library(brms)
#100 rows, 4 columns. Each cell contains a number between 1 and 10
Data <- data.frame(runif(100,1,10),runif(100,1,10),runif(100,1,10),runif(100,1,10))
#Assign names to the columns
names(Data) <- c("d0_10","d0_100","d0_1000","d0_10000")
Data$Density <- as.matrix(Data)%*%c(-1,10,5,1)
#the coefficients we are modelling
d <- c(-1,10,5,1)
#Made a matrix with 4 columns with values 10, 100, 1000, 10000 which are evaluation points. Rows are repeats of the same column numbers
Bins <- 10^matrix(rep(1:4,times = dim(Data)[1]),ncol = 4,byrow =T)
Bins
As mentioned above, since 'data' only allows one data.frame object to be inputted, I've tried other ways of inputting my matrix data. These methods include:
1) making the matrix within the brm() function using as.matrix()
signalregression.brms <- brm(Density ~ s(Bins,by=as.matrix(Data[,c(c("d0_10","d0_100","d0_1000","d0_10000"))])),data = Data)
#Error in is(sexpr, "try-error") :
argument "sexpr" is missing, with no default
2) making the matrix outside the formula, storing it in a variable, then calling that variable inside the brm() function
Donuts <- as.matrix(Data[,c(c("d0_10","d0_100","d0_1000","d0_10000"))])
signalregression.brms <- brm(Density ~ s(Bins,by=Donuts),data = Data)
#Error: The following variables can neither be found in 'data' nor in 'data2':
'Bins', 'Donuts'
3) inputting a list containing the matrix using the 'data2' parameter
signalregression.brms <- brm(Density ~ s(Bins,by=donuts),data = Data,data2=list(Bins = 10^matrix(rep(1:4,times = dim(Data)[1]),ncol = 4,byrow =T),donuts=as.matrix(Data[,c(c("d0_10","d0_100","d0_1000","d0_10000"))])))
#Error in names(dat) <- object$term :
'names' attribute [1] must be the same length as the vector [0]
None of the above worked; each had their own errors and it was difficult troubleshooting them because I couldn't find answers or examples online that were of a similar nature in the context of brms.
I was able to use the above techniques just fine for gam(), in the mgcv package - you don't have to define a data.frame using 'data', you can call on variables defined outside of the gam() formula, and you can make matrices inside the gam() function itself. See below:
library(mgcv)
signalregression2 <- gam(Data$Density ~ s(Bins,by = as.matrix(Data[,c("d0_10","d0_100","d0_1000","d0_10000")]),k=3))
#Works!
It seems like brms is less flexible... :(
My question: does anyone have any suggestions on how to make my brm() function run?
Thank you very much!
My understanding of signal regression is limited enough that I'm not convinced this is correct, but I think it's at least a step in the right direction. The problem seems to be that brm() expects everything in its formula to be a column in data. So we can get the model to compile by ensuring all the things we want are present in data:
library(tidyverse)
signalregression.brms = brm(Density ~
s(cbind(d0_10_bin, d0_100_bin, d0_1000_bin, d0_10000_bin),
by = cbind(d0_10, d0_100, d0_1000, d0_10000),
k = 3),
data = Data %>%
mutate(d0_10_bin = 10,
d0_100_bin = 100,
d0_1000_bin = 1000,
d0_10000_bin = 10000))
Writing out each column by hand is a little annoying; I'm sure there are more general solutions.
For reference, here are my installed package versions:
map_chr(unname(unlist(pacman::p_depends(brms)[c("Depends", "Imports")])), ~ paste(., ": ", pacman::p_version(.), sep = ""))
[1] "Rcpp: 1.0.6" "methods: 4.0.3" "rstan: 2.21.2" "ggplot2: 3.3.3"
[5] "loo: 2.4.1" "Matrix: 1.2.18" "mgcv: 1.8.33" "rstantools: 2.1.1"
[9] "bayesplot: 1.8.0" "shinystan: 2.5.0" "projpred: 2.0.2" "bridgesampling: 1.1.2"
[13] "glue: 1.4.2" "future: 1.21.0" "matrixStats: 0.58.0" "nleqslv: 3.3.2"
[17] "nlme: 3.1.149" "coda: 0.19.4" "abind: 1.4.5" "stats: 4.0.3"
[21] "utils: 4.0.3" "parallel: 4.0.3" "grDevices: 4.0.3" "backports: 1.2.1"

Insert multiple variables in the lda function from a list R

I have 6 variables for which I want to test which one is the best combination for a linear discriminant analysis lda .
I created a list with all the combinations.
I would like to loop through this list and run a lda for each combination
The lda formula wants column names to be specified with a + as follow:
lda(classification~ variable1+variable2, data=mydata)
However if I insert the value of my list in the lda function I get an error
unlist(mylist[i])
"variable1" "variable2"
Error in model.frame.default(formula = mylist ~ unlist(mylist[i]), :
variable lengths differ
reproducible example (variables are constant for illustrative purpose)
classification<-c("a","b","c","d","e","f")
variable1<-c(1,1,1,1,1,1)
variable2<-c(1,1,1,1,1,1)
variable3<-c(1,1,1,1,1,1)
variable4<-c(1,1,1,1,1,1)
variable5<-c(1,1,1,1,1,1)
variable6<-c(1,1,1,1,1,1)
mydata<-data.frame("classification","variable1","variable2","variable3","variable4","variable5","variable6")
para_combo1<-combn(mydata[2:7],1, simplify = FALSE)
para_combo2<-combn(mydata[2:7],2, simplify = FALSE)
para_combo3<-combn(mydata[2:7],3, simplify = FALSE)
para_combo4<-combn(mydata[2:7],4, simplify = FALSE)
para_combo5<-combn(mydata[2:7],5, simplify = FALSE)
para_combo6<-combn(mydata[2:7],6, simplify = FALSE)
para_combo<-c(para_combo1,para_combo2, para_combo3,
para_combo4,para_combo5, para_combo6)
#manual example
lda_table<-lda(classification~ variable1+variable2, data= mydata)
#example I would loop
lda_table<-lda(classification~ para_combo[7] , data= mydata)
I do not know how I could code my combination in the format lda requires
Apart from providing a formula, you can alternatively provide the features and the classes in the parameters x and grouping, respectively:
lda.result <- lda(x=mydata[,c(1,3)], grouping=mydata$classification)
# or simply:
lda.result <- lda(mydata[,c(1,3)], mydata$classification)
Note that the function lda in R actually does not only work with two variables, but with an arbitrary number of variables (sometimes called "multiple discriminant analysis"). There is thus no need to try out all pairs of variable combinations, but you can let lda figure it out for itself.

Can't give a subset when using randomForest inside a function

I'm wanting to create a function that uses within it the randomForest function from the randomForest package. This takes the "subset" argument, which is a vector of row numbers of the data frame to use for training. However, if I use this argument when calling the randomForest function in another defined function, I get the error:
Error in eval(substitute(subset), data, env) :
object 'tr_subset' not found
Here is a reproducible example, where we attempt to train a random forest to classify a response "type" either "A" or "B", based on three numerical predictors:
library(randomForest)
# define a random data frame to train with
test.data = data.frame(
type = rep(NA, times = 500),
x = runif(500),
y = runif(500),
z = runif(500)
)
train.data$type[runif(500) >= 0.5] = "A"
train.data$type[is.na(test.data$type)] = "B"
train.data$type = as.factor(test.data$type)
# define the training range
training.range = sample(500)[1:300]
# formula to use
tr_form = formula(type ~ x + y + z)
# Function that includes the randomForest function
train_rf = function(form, all_data, tr_subset) {
p = randomForest(
formula = form,
data = all_data,
subset = tr_subset,
na.action = na.omit
)
return(p)
}
# test the new defined function
test_tree = train_rf(form = tr_form, all_data = train.data, tr_subset = training.range)
Running this gives the error:
Error in eval(substitute(subset), data, env) :
object 'tr_subset' not found
If, however, subset = tr_subset is removed from the randomForest function, and tr_subset is removed from the train_rf function, this code runs fine, however the whole data set is used for training!
It should be noted that using the subset argument in randomForest when not defined in another function works completely fine, and is the intended method for the function, as described in the vignette linked above.
I know in the mean time I could just define another training set that has just the row numbers required, and train using all of that, but is there a reason why my original code doesn't work please?
Thanks.
EDIT: I conjecture that, as subset() is a base R function, R is getting confused and thinking you're wanting to use the base R function rather than defining an argument of the randomForest function. I'm not an expert, though, so I may be wrong.

R object is not a matrix

I am new to R and trying to save my svm model in R and have read the documentation but still do not understand what is wrong.
I am getting the error "object is not a matrix" which would seem to mean that my data is not a matrix, but it is... so something is missing.
My data is defined as:
data = read.table("data.csv")
trainSet = as.data.frame(data[,1:(ncol(data)-1)])
Where the last line is my label
I am trying to define my model as:
svm.model <- svm(type ~ ., data=trainSet, type='C-classification', kernel='polynomial',scale=FALSE)
This seems like it should be correct but I am having trouble finding other examples.
Here is my code so far:
# load libraries
require(e1071)
require(pracma)
require(kernlab)
options(warn=-1)
# load dataset
SVMtimes = 1
KERNEL="polynomial"
DEGREE = 2
data = read.table("head.csv")
results10foldAll=c()
# Cross Fold for training and validation datasets
for(timesRun in 1:SVMtimes) {
cat("Running SVM = ",timesRun," result = ")
trainSet = as.data.frame(data[,1:(ncol(data)-1)])
trainClasses = as.factor(data[,ncol(data)])
model = svm(trainSet, trainClasses, type="C-classification",
kernel = KERNEL, degree = DEGREE, coef0=1, cost=1,
cachesize = 10000, cross = 10)
accAll = model$accuracies
cat(mean(accAll), "/", sd(accAll),"\n")
results10foldAll = rbind(results10foldAll, c(mean(accAll),sd(accAll)))
}
# create model
svm.model <- svm(type ~ ., data = trainSet, type='C-classification', kernel='polynomial',scale=FALSE)
An example of one of my samples would be:
10.135338 7.214543 5.758917 6.361316 0.000000 18.455875 14.082668 31
Here, trainSet is a data frame but in the svm.model function it expects data to be a matrix(where you are assigning trainSet to data). Hence, set data = as.matrix(trainSet). This should work fine.
Indeed as pointed out by #user5196900 you need a matrix to run the svm(). However beware that matrix object means all columns have same datatypes, all numeric or all categorical/factors. If this is true for your data as.matrix() may be fine.
In practice more than often people want to model.matrix() or sparse.model.matrix() (from package Matrix) which gives dummy columns for categorical variables, while having single column for numerical variables. But a matrix indeed.

R caret nnet package

I have two R objects as below.
matrix "datamatrix" - 200 rows and 494 columns: these are my x variables
dataframe Y. Y$V1 is my Y variable. I have converted column V1 to a factor I am building a classification model.
I want to build a neural network and I ran below command.
model <- train(Y$V1 ~ datamatrix, method='nnet', linout=TRUE, trace = FALSE,
#Grid of tuning parameters to try:
tuneGrid=expand.grid(.size=c(1,5,10),.decay=c(0,0.001,0.1)))
I got an error - " argument "data" is missing, with no default"
Is there a way for caret package to understand that I have my X variables in one R object and Y variable in other? I dont want to combined two data objects and then write a formula as the formula will be too long
Y~x1+x2+x3.................x199+x200....x493+x494
The argument "data" is missing error is addressed by adding a data = datamatrix argument to the train call. The way I would do it would be something like:
datafr <- as.data.frame(datamatrix)
# V1 is the first column name if dimnames aren't specified
datafr$V1 <- as.factor(datafr$V1)
model <- train(V1 ~ ., data = datafr, method='nnet',
linout=TRUE, trace = FALSE,
tuneGrid=expand.grid(.size=c(1,5,10),.decay=c(0,0.001,0.1)))
Now you don't have to pull your response variable out separately.
The . identifier allows inclusion of all variables from datafr (see here for details).

Resources