Overwrite variable inside function - r

I'm sure this question may have been asked already, but I couldnt find an answer to my satisfaction.
So my Problem I defined a function (See below) which should take a Variable (x) and check if its part of a dataframe (y). The function should than ask for a promt until it is part of said dataframe.
However when I let it run it wont overwrite the variable inside the function so that the global enviroment variable gets also changed.
Thus var1 should store the value I gave through the prompt inside the function.
Thx :)
#Function
fn_Valid_prompt <- function(x, y, boolOP= FALSE){
while(is.element(x, colnames(y)) == boolOP){
cat("A")
x <<- readline(prompt="Please enter variable: ")
}
if (is.element(x, colnames(y)) != boolOP){
cat(green(bold("Success!")))}
}
#
var1 <- "V1"
data <- c(1:9)
metadata <- as.data.frame(matrix(data,3,3))
fn_Valid_prompt(var1, metadata, boolOP= FALSE)

The following version works, although i'm not sure of your intent with this code :
#Function
fn_Valid_prompt <- function(x, y, boolOP= FALSE){
while(is.element(x, colnames(y)) == boolOP){
x <- readline(prompt="Please enter variable: ")
}
if (is.element(x, colnames(y)) != boolOP){
cat("Success!")}
return(x)
}
#
var1 <- "V1"
data <- c(1:9)
metadata <- as.data.frame(matrix(data,3,3))
result = fn_Valid_prompt("V10", metadata, boolOP= FALSE)
cat(result)
Your mistake was to use <<- instead of <-. Furthermore, i assume you wanted to return the result ?

Related

IF statements inside function do not recognize conditions

I want to adjust my function so that my if and else if statements recognize the name of the dataframe used and execute the correct plotting function. These are some mock data structured the same as mine:
df1<-data.frame(A=c(1,2,2,3,4,5,1,1,2,3),
B=c(4,4,2,3,4,2,1,5,2,2),
C=c(3,3,3,3,4,2,5,1,2,3),
D=c(1,2,5,5,5,4,5,5,2,3),
E=c(1,4,2,3,4,2,5,1,2,3),
dummy1=c("yes","yes","no","no","no","no","yes","no","yes","yes"),
dummy2=c("high","low","low","low","high","high","high","low","low","high"))
df1[colnames(df1)] <- lapply(df1[colnames(df1)], factor)
vals <- colnames(df1)[1:5]
dummies <- colnames(df1)[-(1:5)]
step1 <- lapply(dummies, function(x) df1[, c(vals, x)])
step2 <- lapply(step1, function(x) split(x, x[, 6]))
names(step2) <- dummies
tbls <- unlist(step2, recursive=FALSE)
tbls<-lapply(tbls, function(x) x[(names(x) %in% names(df1[c(1:5)]))])
A<-lapply(tbls,"[", c(1,2))
B<-lapply(tbls,"[", c(3,4))
C<-lapply(tbls,"[", c(3,4))
list<-list(A,B,C)
names(list)<-c("A","B","C")
And this is my function:
plot_1<-function (section, subsample) {
data<-list[grep(section, names(list))]
data<-data[[1]]
name=as.character(names(data))
if(section=="A" && subsample=="None"){plot_likert_general_section(df1[c(1:2)],"A")}
else if (section==name && subsample=="dummy1"){plot_likert(data$dummy1.yes, title=paste("How do the",name,"topics rank?"));plot_likert(data$Ldummy1.no, title = paste("How do the",name,"topics rank?"))}
}
Basically what I want it to do is plot a certain graph by specifying section and subsample I'm interested in if, for example, I want to plot section C and subsample dummy.1, I just write:
plot_1(section="C", subsample="dummy1)
I want to avoid writing this:
else if (section=="A" && subsample=="dummy1"){plot_likert(data$dummy1.yes, title=paste("How do the A topics rank?"));plot_likert(data$Ldummy1.no, title = paste("How do the A topics rank?"))}
else if (section=="B" && subsample=="dummy1"){plot_likert(data$dummy1.yes, title=paste("How do the B topics rank?"));plot_likert(data$Ldummy1.no, title = paste("How do the B topics rank?"))}
else if (section=="C" && subsample=="dummy1"){plot_likert(data$dummy1.yes, title=paste("How do the c topics rank?"));plot_likert(data$Ldummy1.no, title = paste("How do the C topics rank?"))}
else if (section=="C" && subsample=="dummy2")...
.
.
}
So I tried to extract the dataframe used from the list so that it matches the string of the section typed in the function (data<-list[grep(section, names(list))]) and store its name as a character (name=as.character(names(data))), because I thought that in this way the function would have recognized the string "A", "B" or "C" by itself, without the need for me to specify each condition.
However, if I run it, I get this error: Warning message: In section == name && subsample == "dummy1" : 'length(x) = 4 > 1' in coercion to 'logical(1)', that, from what I understand, is due to the presence of a vector in the statement. But I have no idea how to correct for this (I'm still quite new to R).
How can I fix the function so that it does what I want? Thanks in advance!
Well, I can't really test your code without the plot_likert_general_section function or the plot_likert function, but I've done a bit of simplifying and best practices--passing list in as an argument, consistent spaces and assignment operators, etc.--and this is my best guess as to what you want:
plot_1 = function(list, section, subsample) { ## added `list` as an argument
data = list[[grep(section, names(list))]] # use [[ to extract a single item
name = as.character(names(data))
if(subsample == "None"){
plot_likert_general_section(df1[c(1:2)], section)
} else {
yesno = paste(subsample, c("yes", "no"), sep = ".")
plot_likert(data[[yesno[1]]], title = paste("How do the", name, "topics rank?"))
plot_likert(data[[yesno[2]]], title = paste("How do the", name, "topics rank?"))
}
}
plot_1(list, section = "C", subsample = "dummy1)
I'm not sure if your plot_likert functions use base or grid graphics--but either way you'll need to handle the multiple plots. With base, probably use mfrow() to display both of them, if grid I'd suggest putting them in a list to return them both, and then maybe using gridExtra::grid.arrange() (or similar) to plot both of them.
You're right that the error is due to passing a vector where a single value is expected. Try inserting print statements before the equality test to diagnose why this is.
Also, be careful with choosing variable names like name which are baseR functions (e.g. ?name). I'd also recommend following the tidyverse style guide here: https://style.tidyverse.org/.

R - creating variable from reading line

I would like to create a variable XO from user's answer on a quick question. And I also would like system to write, what user has selected. The code looks like this:
fun1 <- function() {
XO <- readline(prompt = "Do you want X, or O? ")
if (substr(XO, 1, 1) == "X")
cat("You have chosen X.\n") & XO = "X"
else
cat("You have chosen O.\n") & XO = "O"
}
The function fun1 is created properly, but after answering the question (my answer is e.g. "X"), system shows error:
Error in cat("You have chosen X.\n") & XO = "X" :
target of assignment expands to non-language object
And XO is not created.
Please, could you help me, what am I doing wrong? Thanks in advance.
In R, & is just used in logical assignments, not for joining sentences.
What you wanna do is to put that piece of code in a chunks inside curly brackets {} and split them in different lines. If the condition is true R will run the hole chunk inside the curly brackets.
fun1 <- function() {
XO <- readline(prompt = "Do you want X, or O? ")
if (substr(XO, 1, 1) == "X") {
cat("You have chosen X.\n")
XO <<- "X"
} else {
cat("You have chosen O.\n")
XO <<- "O"
}
}
You're using = to assign the XO variable inside the fun1 function. Take a look at this question to be sure that's what you want. If you want it to be available also in the global environment, use <<- instead.

Delete data frame column within function

I have the following code:
df<- iris
library(svDialogs)
columnFunction <- function (x) {
column.D <- dlgList(names(x), multiple = T, title = "Spalten auswaehlen")$res
if (!length((column.D))) {
cat("No column selected\n")
} else {
cat("The following columns are choosen:\n")
print(column.D)
for (z in column.D) {
x[[z]] <- NULL #with this part I wanted to delete the above selected columns
}
}
}
columnFunction(df)
So how is it possible to address data.frame columns "dynamically" so: x[[z]] <- NULL should translate to:
df$Species <- NULL
df[["Species"]] <- NULL
df[,"Species"] <- NULL
and that for every selected column in every data.frame chosen for the function.
Well does anyone know how to archive something like that? I tried several things like with the paste command or sprintf, deparse but i didnt get it working. I also tied to address the data.frame as a global variable by using <<- but didn`t help, too. (Well its the first time i even heard about that). It looks like i miss the right method transferring x and z to the variable assignment.
If you want to create a function columnFunction that removes columns from a passed data frame df, all you need to do is pass the data frame to the function, return the modified version of df, and replace df with the result:
library(svDialogs)
columnFunction <- function (x) {
column.D <- dlgList(names(x), multiple = T, title = "Spalten auswaehlen")$res
if (!length((column.D))) {
cat("No column selected\n")
} else {
cat("The following columns are choosen:\n")
print(column.D)
x <- x[,!names(x) %in% column.D]
}
return(x)
}
df <- columnFunction(df)

R function not returning values

I am writing my first R function.
IMDBmovierating <- function(movie){
link <- paste("http://www.omdbapi.com/?t=", movie, "&y=&plot=short&r=json", `sep = "")`
jsonData <- fromJSON(link)
df <- data.frame(jsonData)
}
And then nothing happens. Suspect it has something to do with return being needed. Not sure how I would write this.
To return df, simply write return(df):
IMDBmovierating <- function(movie){
link <- paste("http://www.omdbapi.com/?t=", movie, "&y=&plot=short&r=json", sep = "")
jsonData <- fromJSON(link)
df <- data.frame(jsonData)
return(df)
}
or, even simpler in this case, omit the last assignment:
IMDBmovierating <- function(movie){
link <- paste("http://www.omdbapi.com/?t=", movie, "&y=&plot=short&r=json", sep = "")
jsonData <- fromJSON(link)
data.frame(jsonData)
}
If the last expression evaluates to a result object, as data.frame(..) does, then this gets the return object of the enclosing expression and the explicit return statement may be omitted.
edit: and remove the back-ticks before sep and after you closing parenthesis
edit2: Of course MrFlick's comment is correct: the only thing really wrong with your code are the back-ticks that probably are simply a typo here on the site. Even the assignment produces the assigned value as a result object, but it is invisible. Hence, you can assign it, but it is not automatically printed on the console.
You simply need to evaluate the object at the end of your function so it will return a value. See the simple example below:
funA <- function(x) {
a <- x
}
funB <- function(x) {
b <- x
b
}
funA(1) # prints nothing
funB(1) # prints 'b'
[1] 1
EDIT:
As #MrFlick points out, both funA and funB return the evaluation of the last expression, but funA will not print anything. However, if you assign the output of funA(1) an object, that object will produce the value 1:
z <- funA(1)
z
[1] 1
z == funB(1)
[1] TRUE
The moral of the story is that you either need to assign the output of IMDBmovierating to an object, or explicitly evaluate df at the end of the function.
Looks like you just had a few typos.
Try this and dont forget to include your library to help people out when answering you. :)
library(RJSONIO)
IMDBmovierating <- function(movie){
link <- paste("http://www.omdbapi.com/?t=", movie,"&y=&plot=short&r=json", sep = "")
jsonData <- fromJSON(link)
df <- data.frame(jsonData)
}
test <- IMDBmovierating(1984)
test

How can I include a variable name in a function call in R?

I'm trying to change the name of a variable that is included inside a for loop and function call. In the example below, I'd like column_1 to be passed to the plot function, then column_2 etc. I've tried using do.call, but it returns "object 'column_j' not found". But object column_j is there, and the plot function works if I hard-code them in. Help much appreciated.
for (j in 2:12) {
column_to_plot = paste("column_", j, sep = "")
do.call("plot", list(x, as.name(column_to_plot)))
}
I do:
x <- runif(100)
column_2 <-
column_3 <-
column_4 <-
column_5 <-
column_6 <-
column_7 <-
column_8 <-
column_9 <-
column_10 <-
column_11 <-
column_12 <- rnorm(100)
for (j in 2:12) {
column_to_plot = paste("column_", j, sep = "")
do.call("plot", list(x, as.name(column_to_plot)))
}
And I have no errors. Maybe you could provide hard-code which (according to your question) works, then will be simpler to find a reason of the error.
(I know that I can generate vectors using loop and assign, but I want to provide clear example)
You can do it without the paste() command in your for loop. Simply assign the columns via the function colnames() in your loop:
column_to_plot <- colnames(dataframeNAME)[j]
Hope that helps as a first kludge.
Are you trying to retrieve an object in the workspace by a character string? In that case, parse() might help:
for (j in 2:12) {
column_to_plot = paste("column_", j, sep = "")
plot(x, eval(parse(text=column_to_plot)))
}
In this case you could use do.call(), but it would not be required.
Edit: wrapp parse() in eval()
Here is one way to do it:
tmp.df <- data.frame(col_1=rnorm(10),col_2=rnorm(10),col_3=rnorm(10))
x <- seq(2,20,by=2)
plot(x, tmp.df$col_1)
for(j in 2:3){
name.list <- list("x",paste("col_",j,sep=""))
with(tmp.df, do.call("lines",lapply(name.list,as.name))) }
You can also do colnames(tmp.df)[j] instead of paste(..) if you'd like.

Resources