call a function from a vector of given functions in R - r

have the following function:
setTypes <- function(df2, ...) {
fns <- as.list(substitute(list(...)))
for(i in 1:length(df2)) {
if(fns[i] == '') {
next
}
df2[i,] <- fns[i](df2[i,])
}
return(df2)
}
want to do this:
test<-setTypes(sls,c('','as.Date','','','as.numeric','as.numeric'))
idea is to change the types of the fields in a data frame without having to do sls$field <- as.numeric(sls$field) for every field.
I had written a function like this that worked:
fn <- function(t) {
return(t("55.55000"))
}
and the output is this:
> fn(as.numeric)
[1] 55.55
however, i can't figure out why either doing variable length argument as a list and calling it as list[index](input) doesn't work. or even passing a vector of functions like c(as.Date, as.numeric, as.character) and doing c[1]('2015-10-10') # as.Date('2015-10-10')
I am receiving the error 'attempt to apply non-function'.. I've also tried using call but to no avail. Help?

The problem is that class(c[1]) is a list use c[[1]] instead
Example code
v <- c(as.numeric,as.character)
v[[1]]("1")
v[[2]](1)
EDIT
Your example should be:
setTypes <- function(df2, ...) {
fns <- list(...)
for(i in 1:NCOL(df2)) {
if(is.function(fns[[i]])) {
df2[,i] <- fns[[i]](df2[,i])
}
}
return(df2)
}
df <- data.frame(v1 = c(1,2), v2 = c("1","2"))
setTypes(df,as.character,'',as.numeric)

Related

Apply a function to objects in my global environment R

This code chunk creates a 10 objects based of length of alpha.
alpha <- seq(.1,1,by=.1)
for (i in 1:length(alpha)){
assign(paste0("list_ts_ses_tune", i),NULL)
}
How do I put each function into the new list_ts_ses_tune1 ... null objects I've created? Each function puts in a list, and works if I set list_ts_ses_tune1 <- lapply ...
for (i in 1:length(alpha))
{
list_ts_ses_tune[i] <- lapply(list_ts, function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i])))
list_ts_ses_tune[i] <- lapply(list_ts_ses_tune[i], "[", c("mean"))
}
Maybe this is a better way to do this? I need each individual output in a list of values.
Edit:
for (i in 1:length(alpha))
{
list_ts_ses_tune[[i]] <- lapply(list_ts[1:(length(list_ts)/2)],
function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i])))
list_ts_ses_tune[[i]] <- lapply(list_ts_ses_tune[[i]], "[", c("mean"))
}
We can use mget to return all the objects into a list
mget(ls(pattern = '^list_ts_ses_tune\\d+'))
Also, the NULL list can be created more easily instead of 10 objects in the global environment
list_ts_ses_tune <- vector('list', length(alpha))
Now, we can just use the OP's code
for (i in 1:length(alpha))
{
list_ts_ses_tune[[i]] <- lapply(list_ts, function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i])))
}
If we want to create a single data.frame
for(i in seq_along(alpha)) {
list_ts_ses_tune[[i]] <- data.frame(Mean = do.call(rbind, lapply(list_ts, function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i]))$mean)))
}
You could simply accomplish everything by doing:
library(forecast)
list_ts_ses_tune <- Map(function(x)
lapply(alpha, function(y)forecast(ses(x,h=24,alpha=y))['mean']), list_ts)

R - Fill the arguments of a function in an ellipsis

I have the following function :
ExampleFunction <- function(ListNumber1, ListNumber2)
{
OutputData <- list()
OutputData[[1]] <- rbind.fill(ListNumber1[[1]], ListNumber2[[1]])
OutputData[[2]] <- rbind.fill(ListNumber1[[2]], ListNumber2[[2]])
return(OutputData)
}
I want to improve this function introducing the possibility to use a variable number of arguments (i.e. lists in my example). Here is an attempt to do this but I don't see how to fill the arguments of rbind.fill().
ExampleFunctionUpgrade <- function(...)
{
Arguments <- list(...)
OutputData <- list()
VarNames <- paste0("List", seq_along(Arguments))
for (i in 1:length(Arguments))
{
assign(VarNames[i], Arguments[[i]])
}
OutputData <- rbind.fill(???)
return(OutputData)
}
I would try to iterate over the columns within an lapply call that is to be bound together.
ExampleFunctionUpgrade <- function(...)
{
Arguments <- list(...)
OutputData <- list()
for(i in 1:length(Arguments[[1]])) {
OutputData[[i]] <- rbind.fill(lapply(Arguments, '[[', i))
}
return(OutputData)
}
If you don't like 'for loops' you can use two lapply calls.

Trying to call a function in a for loop and getting unused argument error

readStateData <- function() {
infile <- paste("state",i,".txt",sep="")
state <- readLines(infile,n=1)
statedata <- read.table(infile,header=FALSE,sep=",",skip=1,col.names=c("Rank","City","Population"))
statename <- list(state,statedata)
statename
}
# Start loop
for(i in 1:50) {
readStateData()
# Add function to big.list
big.list[[i]] <- readStateData(statename)
}
The assignment for class is to bring in 50 files, all named state#.txt, get the state via readLines, get the data via read.table, and ultimately put it all into big.list that'll have all of the data through a for loop.
The problem I'm having is calling the function in during the for loop. I get the error:
Error in readStateData(statename) : unused argument (statename)
I'm either not calling in the function properly or I've written the function wrong. Both are likely.
Thank you for your help.
You have different issues here.
Do not refer inside a function to a variable which is defined outside. It means instead of access an outside the function defined i inside the function:
i <- 1
fct <- function() {
a <- i + 1
return(a)
}
fct()
Pass the variable as an argument to the function:
i <- 1
fct <- function(x) {
a <- x + 1
return(a)
}
fct(i)
In your function the return statement is missing. See point 1 the last command in the functions. Without a return statement the last written variable is on the stack and is "returned" by the function. This is not the clean way to return a value.
Ergo your code should look like this
readStateData <- function(x) {
infile <- paste("state",x,".txt",sep="")
state <- readLines(infile,n=1)
statedata <-read.table(infile,header=FALSE,sep=",",skip=1,col.names=c("Rank","City","Population"))
statename <- list(state,statedata)
return(statename)
}
# Start loop
for(i in 1:50) {
j <- readStateData(i)
# Add function to big.list
big.list[[i]] <- j
}
If your files are all of the pattern: state[number].txt you can simplify your code to:
# Get all files with pattern state*.txt
fls <- dir(pattern='state.*txt')
readStateData <- function(x) {
state <- readLines(x, n=1)
statedata <-read.table(x, header=FALSE,sep=",",skip=1,col.names=c("Rank","City","Population"))
statename <- list(state,statedata)
return(statename)
}
# Start loop
for(i in 1:length(fls)) {
j <- readStateData(fls[i])
# Add function to big.list
big.list[[i]] <- j
}

R - Subsetting subsets of variable names in loops

Q: How do I subset a subset of a changing variable inside a function or loop?
Assume I have the following code for determining regression stats for multiple data sets :
dat1 <- data.frame(col1=1:5,col2=6:10)
dat2 <- data.frame(col1=11:15,col2=16:20)
func <- function(data,col.no){
get(data)[,col.no]
}
for(i in c('dat1','dat2')) {
mod.name <- paste0('fit.',i)
assign(mod.name,lm(get(i)[,1]~get(i)[,2]),envir = .GlobalEnv)
}
mod.pvals <- NULL
p.func <- function(attr.name) {
for(i in c('dat1','dat2')) {
mod.name <- paste0('fit.',i)
p.val <- summary(get(mod.name))[attr.name]
mod.pvals <- c(mod.pvals,p.val)
}
mod.pvals
}
r.vals <- p.func('r.squared')
adj.r.vals <- p.func('adj.r.squared')
coef.vals <- p.func('coefficients')
This works just fine with 'r.squared','adj.r.squared',etc.
But I want to access the p-value of the model in the same function.
Outside of a function I'd choose:
summary(fit)$coefficients[2,4]
But how do I do this inside of the function??
I unsuccessfully tried:
summary(get(mod.name))['coefficients'][2,4]
Error in summary(get(mod.name))["coefficients"][[2, 4]] :
incorrect number of subscripts
So then I thought about just changing my code for p.val in the function above:
p.val <- paste0('summary(',mod.name ,')$',attr.name)
get(p.val)
But when I run the code I get the following error:
p.vals <- p.func('coefficients[2,4]')
Error in get(p.val) :
object 'summary(fit.dat1)$coefficients[2,4]' not found
I guess get() doesn't work like this. Is there a function that I can replace get() with?
Other thoughts on how I could make this work??
One solution is to use eval() and parse():
p.val <- eval(parse(text=paste0("summary(",mod.name,")[",paste0(attr.name),"]")))
So the function would be:
mod.pvals <- NULL
p.func <- function(attr.name) {
for(i in c('dat1','dat2')) {
mod.name <- paste0('fit.',i)
p.val <- eval(parse(text=paste0("summary(",mod.name,")[",attr.name,"]")))
mod.pvals <- c(mod.pvals,p.val)
}
mod.pvals
}
r.vals <- p.func(attr.name = '\'r.squared\'')
p.vals <- p.func(attr.name = '[\'coefficients\']][2,4')

How to check if a two data frame have the same column names?

I have two data frames like this:
quest1 <- c(5,5,5)
quest2 <- c(5,5,5)
quest3<- c("a","b","c")
quest4 <- c(7,7,7)
quest5 <- c(8,8,8)
myquest1 <- data.frame(quest1,quest2,quest3)
myquest2 <- data.frame(quest4,quest5)
How can I check if they have the same column names with an ifelse or if loop statement with a warning or stop function?
Or is there an other..? I would prefer the the former.
I think what you need is something like the following using a function.
Using your example:
quest1 <- c(5,5,5)
quest2 <- c(5,5,5)
quest3<- c("a","b","c")
quest4 <- c(7,7,7)
quest5 <- c(8,8,8)
myquest1 <- data.frame(quest1,quest2,quest3)
myquest2 <- data.frame(quest4,quest5)
myquest3 <- data.frame(quest1,quest2,quest3)
my_func <- function(x,y) {
for (i in names(x)) {
if (!(i %in% names(y))) {
print('Warning: Names are not the same')
break
}
else if(i==tail(names(y),n=1)) {
print('Names are identical')
}
}
}
> my_func(myquest1,myquest2)
[1] "Warning: Names are not the same"
> my_func(myquest1,myquest3)
[1] "Names are identical"

Resources