Switch statement is not working for numerical objects - r

I am new to R programming. I don't know whether we could use switch statements for numerical objects.
This is my code,
myfunction <- function() {
x <- 10
switch(x,
1={
print("one")
},
2={
print("two")
},
3={
print("three")
},
{
print("default") #Edited one..
}
)
}
I got this error,
test.R:4:18: unexpected '='
3: switch(x,
4: 1=
^
Please help me out this problem.

To take full advantage of switch's functionality (in particular its ability to handle arbitrary values with a final "default" expression) and to handle numbers other than 1,2,3,..., you'd be better off converting any input to a character string.
I would do something like this:
myfunction <- function(x) {
switch(as.character(x),
"1" = print("one"),
"2" = print("two"),
"3" = print("three"),
print("something other than 'one', 'two', or 'three'"))
}
myfunction(1)
# [1] "one"
myfunction(345)
# [1] "something other than 'one', 'two', or 'three'"

myfunction <- function(x) {
switch(x,
print("one"),
print("two"),
print("three"))}
myfunction(1)
## [1] "one"
Edit:
As mentioned in comments, this method isn't evaluating the values that are being entered, rather uses them as an index. Thus, it works in your case but it won't work if the statements were to be reordered (see #Joshs answer for better approach).
Either way, I don't think switch is the right function to use in this case, because it is mainly meant for switching between different alternatives, while in your case, you are basically running the same function over and over. Thus, adding extra a statement for each alternative seems like too much work (if you, for example, wanted to display 20 different numbers, you'll have to write 20 different statements).
Instead, you could try the english package which will allow you to display as many numbers as you will define in the ifelse statement
library(english)
myfunction2 <- function(x) {
ifelse(x %in% 1:3,
as.character(as.english(x)),
"default")}
myfunction2(1)
## [1] "one"
myfunction2(4)
## [1] "default"
Alternatively, you could also avoid using switch (though not necessarily recommended) by using match
myfunction3 <- function(x) {
df <- data.frame(A = 1:3, B = c("one", "two", "three"), stringsAsFactors = FALSE)
ifelse(x %in% 1:3,
df$B[match(x, df$A)],
"default")}
myfunction3(1)
## [1] "one"
myfunction3(4)
## [1] "default"

I would suggest reading the ?switch help page. This seems fairly well described there. Names in R can never be numeric, ie c(1=5) is not allowed, nor is f(1=5, 2=5). If you really have 1,2 or 3, then you want just
switch(x,
{print("one")},
{print("two")},
{print("three")}
)
(omit the names for numeric values)

Related

R: using a switch function with a numeric type value? [duplicate]

I am new to R programming. I don't know whether we could use switch statements for numerical objects.
This is my code,
myfunction <- function() {
x <- 10
switch(x,
1={
print("one")
},
2={
print("two")
},
3={
print("three")
},
{
print("default") #Edited one..
}
)
}
I got this error,
test.R:4:18: unexpected '='
3: switch(x,
4: 1=
^
Please help me out this problem.
To take full advantage of switch's functionality (in particular its ability to handle arbitrary values with a final "default" expression) and to handle numbers other than 1,2,3,..., you'd be better off converting any input to a character string.
I would do something like this:
myfunction <- function(x) {
switch(as.character(x),
"1" = print("one"),
"2" = print("two"),
"3" = print("three"),
print("something other than 'one', 'two', or 'three'"))
}
myfunction(1)
# [1] "one"
myfunction(345)
# [1] "something other than 'one', 'two', or 'three'"
myfunction <- function(x) {
switch(x,
print("one"),
print("two"),
print("three"))}
myfunction(1)
## [1] "one"
Edit:
As mentioned in comments, this method isn't evaluating the values that are being entered, rather uses them as an index. Thus, it works in your case but it won't work if the statements were to be reordered (see #Joshs answer for better approach).
Either way, I don't think switch is the right function to use in this case, because it is mainly meant for switching between different alternatives, while in your case, you are basically running the same function over and over. Thus, adding extra a statement for each alternative seems like too much work (if you, for example, wanted to display 20 different numbers, you'll have to write 20 different statements).
Instead, you could try the english package which will allow you to display as many numbers as you will define in the ifelse statement
library(english)
myfunction2 <- function(x) {
ifelse(x %in% 1:3,
as.character(as.english(x)),
"default")}
myfunction2(1)
## [1] "one"
myfunction2(4)
## [1] "default"
Alternatively, you could also avoid using switch (though not necessarily recommended) by using match
myfunction3 <- function(x) {
df <- data.frame(A = 1:3, B = c("one", "two", "three"), stringsAsFactors = FALSE)
ifelse(x %in% 1:3,
df$B[match(x, df$A)],
"default")}
myfunction3(1)
## [1] "one"
myfunction3(4)
## [1] "default"
I would suggest reading the ?switch help page. This seems fairly well described there. Names in R can never be numeric, ie c(1=5) is not allowed, nor is f(1=5, 2=5). If you really have 1,2 or 3, then you want just
switch(x,
{print("one")},
{print("two")},
{print("three")}
)
(omit the names for numeric values)

How to Perform a Regex Task in R

So I am trying to find a way for R to detect the characters "ar1" for a function I am making.
if(str_detect(as.character(y1.AR2), regex('ar1', ignore_case = T)) == T){
print('love')
} else {
print('nolove')
}
For example, the above code evaluates out to True, but I want it evaluate to false because there is no 'AR1', in the order 'A' followed by 'R' followed by '1', in the name of the object 'y1.AR2'. The only time I want the statement to evaluate to True is if it matches 'AR1' in that order whether it is in upper or lowercase.
Anyone know of a way to make this possible?
Thank you in advance!
func <- function(x) {
xname <- deparse(substitute(x))
if (grepl("ar1", xname, ignore.case = TRUE)) "love" else "nolove"
}
y1.AR2 <- 1
func(y1.AR2)
# [1] "nolove"
y1.AR1 <- 2
func(y1.AR1)
# [1] "love"
Finding the name of an argument to a function is a little fragile. For instance, doing
func(c("1", "quux", "tar1234"))
# [1] "love"
because deparse(substitute(x)) resolved to the literal expression used to form the first argument.

Is it possible to add external arguments to form partial field names?

I have two fields:
FirstVisit
SecondVisit
I am building a function to pull data from either field depending on user input (heavily reduced yet relevant version of function):
pullData(visit){
# Do something
}
What I am looking to do is for the function to take the user's input and use it to form part of the call to the data frame field.
For example, when the user runs:
pullData(First)
The function will run like this:
print(df$FirstVisit)
Conversely, when the user runs:
pullData(Second)
The function will run:
print(df$SecondVisit)
My function is considerably more complex than this, but this basic example relates to just the specific aspect of it that I am trying to work out.
So far I have tried something like:
print(paste0(df["df$", visit, "Visit", ])
# The intention is to result in df$FirstVisit or df$SecondVisit depending on the input
And this:
print(paste0(df[df$", visit, "Visit, ])
# Again, intended result should be df$FirstVisit or df$SecondVisit, depending on the input
among other alternatives (some with paste()), yet nothing has worked so far.
I suspect that it is possible and feel that I am close.
How can I achieve this?
If you really want to run the function like pullData(First), you need to use metaprogramming (to get the name of the argument instead of the arguements value) like
pullData <- function(...) {
arg <- rlang::ensyms(...)
if(length(arg)!=1) stop("invalid argument in pullData")
dataName <- paste0(as.character(arg[[1]]),"Visit")
print(df[[dataName]])
}
If you can manage to call the function with a character-argument like pullData("First"), you can simply do:
pullData <- function(choice = "First") {
dataName <- paste0(choice,"Visit")
print(df[[dataName]])
}
I am not quite sure if this is what you're going for, but here's a possible solution:
pullData <- function(visit){
visit <- rlang::quo_text(enquo(visit))
visit <- tolower(visit)
if (visit %in% c("first", "firstvisit")){
data <- df$FirstVisit
}
if (visit %in% c("second", "secondvisit")){
data <- df$SecondVisit
}
data
}
Using this sample data:
df <- data.frame(FirstVisit = c("first value"),
SecondVisit = c("second value"))
Gets us:
> pullData(first)
[1] "first value"
> pullData(second)
[1] "second value"
For the sake of completeness, R allows for partial matching when subsetting with character indices; see help("$").
df <- data.frame(FirstVisit = 11:12, SecondVisit = 21:22)
For interactive use:
df$F
[1] 11 12
df$S
[1] 21 22
For programming on computed indices, the [[ operator has to be used, e.g.,
df[["F", exact = FALSE]]
[1] 11 12
This can be wrapped in a function call:
pullData <- function(x) df[[x, exact = FALSE]]
Thus,
pullData("F")
pullData("Fi")
pullData("First")
pullData("FirstVisit")
return all
[1] 11 12
while
pullData("S")
pullData("Second")
return
[1] 21 22
But watchout when dealing with user supplied input as typos might lead to unexpected results:
pullData("f")
pullData("first")
pullData("Frist")
NULL

How to get only one iteration in ifelse statement

I am trying to pass a string through a function to call a column from a data frame. I made a very simple if-else function to make sure the foundation was working before adding complexity. Essentially, if the column name is found within the data frame, print "Hi", if not, print "No". The function correctly identifies if the column name is in the data frame but it prints a duplicate "No".
I have tried using if(){}else(){}
Using ifelse()
Using break and next with the if(){}else(){} method
df <- as.data.frame(cbind("a" = 1:5,
"b" = 6:10))
testingIf <- function(x){
if(x %in% colnames(df)){
print("Hi")
}
else{
(print("No"))
}
}
testingIf("a")
testingIf("This is true")
if(){}else(){}
-This was my initial attempt and if the 'if' is true it works as intended, but if the 'if' is false it prints out [1] No [1] No.
Using ifelse()
-Prints out [1] Hi [1] Hi when true and [1] No [1] No when false
Using break and next with the if(){}else(){} syntax
-This works so far as it give me the correct output but I get
"Error: no loop for break/next, jumping to top level"
, which makes me feel like I am missing something.
Your function can be made simpler by using the ifelse() function.
Using your dataframe df <- as.data.frame(cbind("a" = 1:5,"b" = 6:10)), we can rewrite your function as:
testingIf <- function(x,dt){
ifelse(x %in% colnames(dt),"Hi","No")
}
Now testing the code, we have:
> testingIf("a",df)
[1] "Hi"
> testingIf("This is true",df)
[1] "No"
Hope this helps!

R: passing argument name in dots (...) through a third string variable

Imagine you have a simple function that specifies which statistical tests to run for each variable. Its syntax, simplified for the purposes of this question is as follows:
test <- function(...) {
x <- list(...)
return(x)
}
which takes argument pairs such as Gender = 'Tukey', and intends to pass its result to other functions down the line. The output of test() is as follows:
test(Gender = 'Tukey')
# $Gender
# [1] "Tukey"
What is desired is the ability to replace the literal Gender by a dynamically assigned variable varname (e.g., for looping purposes). Currently what happens is:
varname <- 'Gender'
test(varname = 'Tukey')
# $varname
# [1] "Tukey"
but what is desired is this:
varname <- 'Gender'
test(varname = 'Tukey')
# $Gender
# [1] "Tukey"
I tried tinkering with functions such as eval() and parse(), but to no avail. In practice, I resolved the issue by simply renaming the resulting list, but it is an ugly solution and I am sure there is an elegant R way to achieve it. Thank in advance for the educational value of your answer.
NB: This question occurred to me while trying to program a custom function which uses mcp() from the effects package in its internals. The said mcp() function is the real world counterpart of test().
EDIT1: Perhaps it needs to be clarified that (for educational purposes) changing test() is not an option. The question is about how to pass the tricky argument to test(). If you take a look at NB, it becomes clear why: the real world counterpart of test(), namely mcp(), comes with a package. And while it is possible to create a modified copy of it, I am really curious whether there exists a simple solution in somehow 'converting' the dynamically assigned variable to a literal in the context of dot-arguments.
This works:
test <- function(...) {
x = list(...)
names(x) <- sapply(names(x),
function(p) eval(as.symbol(p)))
return(x)
}
apple = "orange"
test(apple = 5)
We can use
test <- function(...) {
x <- list(...)
if(exists(names(x))) names(x) <- get(names(x))
x
}
test(Gender = 'Tukey')
#$Gender
#[1] "Tukey"
test(varname = 'Tukey')
#$Gender
#[1] "Tukey"
What about this:
varname <- "Gender"
args <- list()
args[[varname]] <- "Tukey"
do.call(test, args)

Resources