R Language - Waiting for user input with scan or readline - r

I'm trying to get the user to input a few keywords for a query, and in my script I used either scan or readline. I tried it using the R-embeeded script editor (Windows) but when I execute the code, it uses my next lines of script as the standard input.
Here is my (part of) script
keywords <- scan(what=character(), nlines=1)
keywords <- paste(keywords, collapse=",")
keywords
And here is the output when executed from the editor
> keywords <- scan(what=character(), nlines=1)
1: keywords <- paste(keywords, collapse=",")
Read 4 items
> keywords
[1] "keywords" "<-" "paste(keywords," "collapse=\",\")"
Meanwhile when I use the source() command, I have my user input respected.
So is there any way to be able to input some things while executing the code right from the R software?

This is how I use readLInes:
FUN <- function(x) {
if (missing(x)) {
message("Uhh you forgot to eneter x...\nPlease enter it now.")
x <- readLines(n = 1)
}
x
}
FUN()
Or maybe something along these lines:
FUN2 <- function() {
message("How many fruits will you buy")
x <- readLines(n = 1)
message("Good you want to buy %s fruits.\n Enter them now.")
y <- readLines(n = x)
paste(y, collapse = ", ")
}
FUN2()
EDIT: With your approach in Rgui...
FUN3 <- function(n=2) {
keywords <- scan(what=character(), nlines=n)
paste(keywords, collapse=",")
}
## > FUN3 <- function(n=2) {
## + keywords <- scan(what=character(), nlines=n)
## + paste(keywords, collapse=",")
## + }
## > FUN3()
## 1: apple
## 2: chicken
## Read 2 items
## [1] "apple,chicken"

Related

Getting name of an object from list in Map

Given the following data:
list_A <- list(data_cars = mtcars,
data_air = AirPassengers,
data_list = list(A = 1,
B = 2))
I would like to print names of objects available across list_A.
Example:
Map(
f = function(x) {
nm <- deparse(match.call()$x)
print(nm)
# nm object is only needed to properly name flat file that may be
# produced within Map call
if (any(class(x) == "list")) {
length(x) + 1
} else {
length(x) + 1e6
saveRDS(object = x,
file = tempfile(pattern = make.names(nm), fileext = ".RDS"))
}
},
list_A
)
returns:
[1] "dots[[1L]][[1L]]"
[1] "dots[[1L]][[2L]]"
[1] "dots[[1L]][[3L]]"
$data_cars
NULL
$data_air
NULL
$data_list
[1] 3
Desired results
I would like to get:
`data_cars`
`data_air`
`data_list`
Update
Following the comments, I have modified the example to make it more reflective of my actual needs which are:
While using Map to iterate over list_A I'm performing some operations on each element of the list
Periodically I want to create a flat file with name reflecting name of object that was processed
In addition to list_A, there are also list_B, list_C and so forth. Therefore, I would like to avoid calling names(list) inside the function f of the Map as I will have to modify it n number of times. The solution I'm looking to find should lend itself for:
Map(function(l){...}, list_A)
So I can later replace list_A. It does not have to rely on Map. Any of the apply functions would do; same applied to purrr-based solutions.
Alternative example
do_stuff <- function(x) {
nm <- deparse(match.call()$x)
print(nm)
# nm object is only needed to properly name flat file that may be
# produced within Map call
if (any(class(x) == "list")) {
length(x) + 1
} else {
length(x) + 1e6
saveRDS(object = x,
file = tempfile(pattern = make.names(nm), fileext = ".RDS"))
}
}
Map(do_stuff, list_A)
As per the notes below, I want to avoid having to modify do_stuff function as I will be looking to do:
Map(do_stuff, list_A)
Map(do_stuff, list_B)
Map(do_stuff, list_...)
We could wrap it into a function, and do it in two steps:
myFun <- function(myList){
# do stuff
res <- Map(
f = function(x) {
#do stuff
head(x)
},
myList)
# write to a file, here we might add control
# if list is empty do not output to a file
for(i in names(res)){
write.table(res[[ i ]], file = paste0(i, ".txt"))
}
}
myFun(list_A)
Would something like this work ?
list_A2 <- Map(list, x = list_A,nm = names(list_A) )
trace(do_stuff, quote({ nm <- x$nm; x<- x$x}), at=3)
Map(do_stuff, list_A2)

format multiline code to a single line

I'm not quite sure of the terminology here. I'm doing a code parsing project to visualize the relationships between objects of an R script. Almost everyting works except for parsing and evaluating functions and loops. I don't think I have a good enough handle on regex to find the beginning/end of nested curly braces.
In the script, it might look like this:
something_above <- "above"
my_function <- function(x){
x2 <- x^2
if(x2 >= 200){
res <- "200+"
} else {
res <- "<200"
}
return(res)
}
something_below <- "below"
When I read it in, it looks like this
string <- '\r\nsometing_above <- \"above\"\r\n\r\nmy_function <- function(x){\r\n x2 <- x^2\r\n if(x2 >= 200){\r\n res <- \"200+\"\r\n } else {\r\n res <- \"<200\"\r\n}\r\n return(res)\r\n }\r\nsomething_below <- \"below\"\r\n'
I would like to be able to collapse the function into a single line like this
... <- function(x){x2 <- x^2 ; if(x2 >= 200){res <- "200+" ; ...}; return(res)}\r\n ...
so that each step within the function is separated by a ; instead of a new line. I would, however, like to keep the new line \r\n pattern at the beginning of the assignment\r\nmy_function and once the function assignment is over };return(res)}\r\n.
The final result would be three lines:
[1] something_above <- "above"
[2] my_function <- function(x){x2 <- x^2; if(x2 >= 200 ...}
[3] something_below <- "below"
Thank you.

Repeating and incrementing a readline() user input x number of times

This function should take the integer that the user inputs in the first readline() prompt, and then run the next section of readline() inputs based on that integer.
If numSampFolders == 1, then it only requires one sample folder name, as per the code below. Similarly, if numSampFolders == 2, then it requires two sample folder names.
There must be a better way to write this than how I've set it up, though (and I don't really want to end up with dozens of if() statements just to cover every eventuality...). For example, if numSampFolders = 24, the code should ask the user, via readline(), to input 24 names and store those names as global variables. (I understand that this could be more easily done with list.files(), however there are folders in the same directory that are not required.)
Thanks for ideas.
set_params <- function(){
numSampFolders <- (readline("How many sample folders are you using? "))
numSampFolders <<- as.numeric(numSampFolders)
if (numSampFolders == 1)
SampFolder1 <<- readline("Enter the name of your only sample folder: ")
if (numSampFolders == 2)
SampFolder1 <<- readline("Enter the name of sample folder 1: ")
SampFolder2 <<- readline("Enter the name of sample folder 2: ")
}
if(interactive()) set_params()
The general approach is to use lapply (or sapply/for) for iteration and the R verb assign. assign behaves like <-, and the trick to making assign behave like <<- is to specify the environment with envir = .GlobalEnv. See the following solution
set_params <- function(){
numSampFolders <- (readline("How many sample folders are you using? "))
numSampFolders <<- as.numeric(numSampFolders)
if (numSampFolders == 1) {
SampFolder1 <<- readline("Enter the name of your only sample folder: ")
} else if (numSampFolders > 1) {
lapply(seq_len(numSampFolders), function(i) assign(paste0("SampFolder", i), readline(paste0("Enter the name of sample folder ", i, ": ")), envir = .GlobalEnv))
}
}
if(interactive()) set_params()
Here's the output of my console
ls()
# character(0)
# How many sample folders are you using? 2
# Enter the name of sample folder 1: ok
# Enter the name of sample folder 2: whatever
# [[1]]
# [1] "ok"
# [[2]]
# [1] "whatever"
ls()
# [1] "numSampFolders" "SampFolder1" "SampFolder2" "set_params"
SampFolder1
# [1] "ok"
You may not like that lapply prints the values to the console (in this case, it's a side-effect). The following is what I'm referring to
# [[1]]
# [1] "ok"
In that case, you can use purrr::walk to loop without the side-effects.
library(purrr)
set_params <- function(){
numSampFolders <- (readline("How many sample folders are you using? "))
numSampFolders <<- as.numeric(numSampFolders)
if (numSampFolders == 1) {
SampFolder1 <<- readline("Enter the name of your only sample folder: ")
} else if (numSampFolders > 1) {
walk(seq_len(numSampFolders), function(i) assign(paste0("SampFolder", i), readline(paste0("Enter the name of sample folder ", i, ": ")), envir = .GlobalEnv))
}
}
if(interactive()) set_params()

Money adding program in R

I want to create a program in R that takes integer user input and then adds it to the previous user input. ex. user input(say one day): 10, then (maybe the next day) user input: 15 --> output 25.Ideally this would accept nearly an infinite amount of input. here is what I have so far:
amount_spent <- function(){
i <-1
while(i<10){
n <- readline(prompt="How much did you spend?: ")
i<-i+1
}
print(c(as.integer(n)))
}
amount_spent()
Problems I have with this code are that it only saves the last input value, and it is difficult to control when User is allowed to input. Is there any way to save user input to a data that can be manipulated through readline()?
# 1.R
fname <- "s.data"
if (file.exists(fname)) {
load(fname)
}
if (!exists("s")) {
s <- 0
}
n <- 0
while (TRUE) {
cat ("Enter a number: ")
n <- scan("stdin", double(), n=1, quiet = TRUE)
if (length(n) != 1) {
print("exiting")
break
}
s <- s + as.numeric(n)
cat("Sum=", s, "\n")
save(list=c("s"), file=fname)
}
You should run the script like this: Rscript 1.R
To exit the loop press Ctrl-D in Unix, or Ctrl-Z in Windows.
An R-ish way to do it would be through closures. Here is an example for interactive use (i.e. within an R session).
balance_setup <- function() {
balance <- 0
change_balance <- function () {
n <- readline(prompt = "How much did you spend?: ")
n <- as.numeric(n)
if (!is.na(n))
balance <<- balance + n
balance
}
print_balance <- function() {
balance
}
list(change_balance = change_balance,
print_balance = print_balance)
}
funs <- balance_setup()
change_balance <- funs$change_balance
print_balance <- funs$print_balance
Calling balance_setup creates a variable balanceand two functions that can access it: one for changing the balance, one for printing it. In R, functions can only return a single value, so I bundle both functions together as a list.
change_balance()
## How much did you spend? 5
## [1] 5
change_balance()
## How much did you spend? 5
## [1] 10
print_balance()
## [1] 10
If you want many inputs, use a loop:
repeat{
change_balance()
}
Break the loop with Ctrl-C, Escape or whatever is used on your platform.

Is it possible for a function to know the name of its assignment target?

This is probably impossible but let me try anyway...
Is it possible to write this function?
Foo<-function(n){
X=runif(n)
write.csv(X,file=paste([the name of the assignment target],".csv",sep="")
}
So that when I do
ABC<-Foo(10)
It generates 10 random numbers and both store it to the R object ABC and write a csv file called "ABC.csv"?
I know you can do something like this:
Foo2<-function(n,name){
X=runif(n)
write.csv(X,file=paste(name,".csv",sep=""))
assign(name,X,envir=.GlobalEnv)
}
Foo2(10,"ABC")
But I would like to use the arrow operator <- because it is the usual syntax in R. So Foo2 really isn't perfect. And of course
ABC<-Foo2(10,"ABC")
is meaningless because it means typing the name twice.
If we change the syntax somewhat we can do it. First define:
Random <- structure(NA,class="Random")
"$<-.Random" <- function(x, name, value) {
X <- runif(value)
write.csv(X, file = paste0(name, ".csv"))
assign(name, X, parent.frame())
invisible(x)
}
This is used like this:
Random$abc <- 3
after which abc will be a vector of generated numbers and a abc.csv file will be written out.
I don't believe you can overwrite <- but you could make your own binary function %<-%
Foo <- function(n){
runif(n)
}
`%<-%` <- function(x, y){
xname <- as.character(substitute(x))
assign(xname, y, envir = .GlobalEnv)
write.csv(y, file = paste0(xname, ".csv"))
}
and an example
> dir()
character(0)
> ls()
[1] "%<-%" "Foo"
> x %<-% Foo(3)
> x
[1] 0.1838146 0.6785800 0.3758954
> read.csv("x.csv")
X x
1 1 0.1838146
2 2 0.6785800
3 3 0.3758954

Resources