?option says this.
‘prompt’: a non-empty string to be used for R's prompt; should
usually end in a blank (‘" "’).
Is it possible to make the prompt to include some dynamic stuffs, e.g., the current time?
You should take a look at the taskCallbackManager (https://developer.r-project.org/TaskHandlers.pdf). With prompt you can call the current time and save it. Example: options("prompt"=format(Sys.time(), "%H:%M:%S> ")). But this is fixed with the time it was set.
The doc for the function taskCallbackManager has the rest:
R> h <- taskCallbackManager()
R> h$add(function(expr, value, ok, visible) {
+ options("prompt"=format(Sys.time(), "%H:%M:%S> "));
+ return(TRUE) },
+ name = "simpleHandler")
[1] "simpleHandler"
07:25:42> a <- 2
07:25:48>
This registers a callback that gets evaluated after each command completes.
Related
How to break a loop after a certain elapsed time? I have a function that collects observational data from a user. The user should have a pre-defined time limit, when the data are recorded (30 sec in the example). At the moment, the function breaks, if the user-input arrives later than the end of the time limit.
record.events <- function(duration = 30, first.event = "a"){
# Initial settings
time.start <- proc.time()[3]
events <- c(first.event, 0)
# Timed data collection
while(proc.time()[3] - time.start < duration){
temp <- readline("record events...")
events <- c(events, temp, proc.time()[3] - time.start)
}
# Format recorded data for post-processing
events <- as.data.frame(matrix(events, byrow=T, ncol=2,
dimnames=list(NULL, c("events","stroke.time"))))
events[,2] <- round(as.numeric(as.character(events[,2])),3)
return(events)
}
Gives for example this result:
events stroke.time
1 a 0.000
2 f 2.618
3 a 23.791
4 f 24.781
5 a 33.488
The last event (a) arrived after the time limit. SO has a solution for this in matlab. Is there a way in R, how to stop waiting for the user input as soon as the time is up?
Edit:
While functions setTimeLimit() and R.utils::withTimeout() can terminate execution of a function that takes too long (thanks to Kodl, Carl Witthoft and Colombo, together with this answer), neither can interrupt readline(). Documentation to withTimeout specifies:
Furthermore, it is not possible to interrupt/break out of a "readline" prompt (e.g. readline() and readLines()) using timeouts; the timeout exception will not be thrown until after the user completes the prompt (i.e. after pressing ENTER).
The user input after the time limit is thus the only way, how to stop waiting for readline. The check can be executed with the while loop as in my code, or with setTimeLimit or withTimeout in a combination with tryCatch. I therefore accept Kodl's answer.
I've also been looking for a solution to this, ended up writing my own function below, but it only works in some setups/platforms.
The main problem is that readline suspends execution until input is provided, so it may hang indefinitely, without ever returning.
My workaround is to open(file('stdin'), blocking=FALSE), and then use readLines(n=1). Problem with that is that it only accepts file('stdin'), which is not always connected. It fails in RGui for windows and MacOS, and for RStudio (at least for MacOS). But it seems to work for R when run from terminal under MacOS.
readline_time <- function(prompt, timeout = 3600, precision=.1) {
stopifnot(length(prompt)<=1, is.numeric(timeout), length(timeout)==1, !is.na(timeout), timeout>=0, is.numeric(precision), length(precision)==1, !is.na(precision), precision>0)
if(!interactive()) return(NULL)
if(timeout==0) return(readline(prompt))
my_in <- file('stdin')
open(my_in, blocking=FALSE)
cat(prompt)
ans <- readLines(my_in, n=1)
while(timeout>0 && !length(ans)) {
Sys.sleep(precision)
timeout <- timeout-precision
ans <- readLines(my_in, n=1)
}
close(my_in)
return(ans)
}
Or if you want to import (along with some other functions):
devtools::install_github('EmilBode/EmilMisc')
i think you can use fucntion "setTimeLimit" from library base. so...
record.events <- function(duration = 30, first.event = "a"){
# Initial settings
time.start <- proc.time()[3]
events<-first.event
stroke.time<-c(0)
# Timed data collection
while(proc.time()[3] - time.start < duration){
temp <- tryCatch({setTimeLimit(elapsed=(time.start + duration - proc.time()[3]),
transient = TRUE);readline("record events...")},
error = function(e) { return("NULL")})
#you need to set up back this function... (but why i dont know????)
setTimeLimit(elapsed = Inf, transient = TRUE)
events[length(events)+1] <- temp
stroke.time[length(stroke.time)+1]<-round(proc.time()[3],3)
}
# Format recorded data for post-processing
events<-data.frame(events, stroke.time)
return(events)
}
But setTimeLimit inst great for use in user functions.. My results is:
events stroke.time
1 a 0.00
2 s 1539.12
3 s 1539.52
4 ass 1539.96
5 s 1540.49
6 asd 1540.94
7 fed 1541.27
8 NULL 1541.55
For more info see:
https://stackoverflow.com/a/7891479
https://stat.ethz.ch/R-manual/R-devel/library/base/html/setTimeLimit.html
How does setTimeLimit work in R?
setTimeLimit fails to terminate idle call in R
I was curious to see if anyone had a real Rland solution to this problem, but it looks like not.
One possible solution is to shell out with system() and run a command that allows reading input with a time limit. This is inherently platform-specific. The Unix bash shell provides a read builtin that is perfect for this purpose, and this will also work on the Cygwin emulation layer on Windows. Unfortunately, I haven't ever come across a command available on the vanilla native Windows platform that provides sufficient functionality for this. set /p can read arbitrary string input but does not provide a timeout, while choice.exe provides a timeout (accurate to the second) but only supports selection of an item from a finite list of (single-character!) items, as opposed to arbitrary string input. Fun fact: choice.exe has its own Wikipedia article.
Here's how you can use read to do this:
LIMIT <- 10; ## conceptual constant
end <- Sys.time()+(left <- LIMIT); ## precompute end of input window and init left
repeat {
input <- suppressWarnings(system(intern=T,sprintf(
'read -r -t %.2f; rc=$?; echo "$REPLY"; exit $rc;',
left
))); ## suppress warnings on non-zero return codes
left <- difftime(end,Sys.time(),units='secs');
cat(sprintf('got input: \"%s\" [%d] with %.2fs left\n',
input,
if ('status'%in%names(attributes(input))) attr(input,'status') else 0L,
left
));
if (left<=0) break;
};
## asdf
## got input: "asdf" [0] with 9.04s left
## xcv
## got input: "xcv" [0] with 8.15s left
## a
## got input: "a" [0] with 6.89s left
## b
## got input: "b" [0] with 6.68s left
## c
## got input: "c" [0] with 6.44s left
##
## got input: "" [0] with 5.88s left
##
## got input: "" [1] with 4.59s left
## got input: "" [1] with 3.70s left
##
## got input: "" [0] with 0.86s left
##
## got input: "" [0] with 0.15s left
## got input: "" [142] with -0.03s left
The sample output I've shown above was me playing around during the input window. I mostly typed some random lines and pressed enter to submit them, giving a return code of 0. The two lines of output that show a return code of 1 were me pressing ^d, which causes read to return 1 immediately, leaving whatever input that was in the buffer in $REPLY (nothing, in those two cases). The final line of output was read terminating immediately upon hitting the timeout, which I believe is the functionality you're looking for. You can use the return code of 142 to distinguish the timeout event from other input events. I'm not completely certain that the return code of 142 is consistent and reliable on all Unix systems, but there's also another way to detect the timeout event: check the current time against end (i.e. the left calculation), as I do in the code. Although I suppose that approach introduces a race condition between a possible last-moment submission and the time check in Rland, but you probably don't need that level of design criticality.
I am aware of how to add a timestamp during an R session using
R> h <- taskCallbackManager()
R> h$add(function(expr, value, ok, visible) {
+ options("prompt"=format(Sys.time(), "%H:%M:%S> "));
+ return(TRUE) },
+ name = "simpleHandler")
[1] "simpleHandler"
07:25:42> a <- 2
as described in this answer.
How can I make this a permanent feature, so that RStudio always has this as the prompt?
One option is to have a .Rprofile file in the ~/ (usually "C:/Users/me/Documents" in windows), and add the following into it.
It will show time as soon as you do something on the console.
.First <- function(){
h <- taskCallbackManager()
h$add(function(expr, value, ok, visible) {
options("prompt"=format(Sys.time(), "%H:%M:%S> "));
return(TRUE) }, name = "simpleHandler")
}
I think you could do this in the Rprofile.site in your
"C:\Program Files\R\R-x.x.x\etc" as well. As noted by #r2evans, this seems like a bad idea.
I have a function called plot.medals2 that has an input that includes a variable named start and a variable named finish. Both variables are numbers and I want that in the event that start is bigger than finish the function will stop running and draw an empty ggplot like the one i attached here.
This is the part of the code that is supposed to take care of that:
plot.medals2 <- function(code,start,finish,medalsvec) {
if(start>finish) { #years not make sense return empty drawing
ggplot() + ylab('medals won') + xlab('year') +
ggtitle(paste('Medals by',"",countries.df[countries.df$country_id==code,]$country_name,',',"",finish,'-',start))
return()
}
}
this is what I get when I try to run it:
plot.medals2('USA',1933,1456,c('GOLD'))
NULL
I tried to use the breakpoint option to see what is going on and it seems the
function does enter the if statement meaning it recognizes that the variable start is larger than finish but then for some reason it skips the next 2 rows and does not plot anything. Now when I use this
ggplot() + ylab('medals won') + xlab('year') +
ggtitle(paste('Medals by',"",countries.df[countries.df$country_id==code,]$country_name,',',"",finish,'-',start))
outside of a function it works but not when its a part of a function.
do you see a way to run a computation in R while waiting for a user input?
I'm writing a script that makes differents types of plots which are defined by user input, but in first lot of data has to be loaded and processed. But in fact, user could start defining what he wants already while the processing is running - that's what I would like to do!
I think package Rdsn might provide the functionality that I need, but I was not able to figure out how.
Thanks!
You didn't give me much context, nor reproducible code, so I will just provide a simple example. I am not familiar with the Rdsn package, so I will use provide a solution I know.
# create a function to prompt the user for some input
readstuff = function(){
stuff = readline(prompt = "Enter some stuff: ")
# Here is where you set the condition for the parameter
# Let's say you want it to be an integer
stuff = as.integer(stuff)
if(is.na(stuff)){
return(readstuff())
} else {
return(stuff)
}
}
parameter = readstuff()
print(parameter)
print(parameter + 10)
The key here is to "source" the script instead of "running" it. You can find the "source" button on the top right of RStudio. You can also use source(yourscript) to source it.
So for every parameter you want to prompt the user for input, just call readstuff(). You can also tweak it a little to make it more general. For example:
# create a function to prompt the user for some input
readstuff = function(promptMessage = "stuff", class = "integer"){
stuff = readline(prompt = paste("Enter the", promptMessage, ": "))
# Here is where you set the condition for the parameter
# Let's say you want it to be an integer
stuff = as(stuff, class)
if(is.na(stuff)){
return(readstuff(promptMessage, class))
} else {
return(stuff)
}
}
plotColor = readstuff("plot color", "character")
size = readstuff("size parameter")
xvarName = readstuff("x axis name", "character")
df = data.frame(x = 1:100, y = 1:100)
library(ggplot2)
p = ggplot(df, aes(x = x, y = y, size = size, color = plotColor)) +
labs(x = xvarName) + geom_point()
print(p)
The if(is.na(stuff)) statements won't work if class is character, but I won't get into details on how to fix that, since this question is mainly about how to wait for user input. There are also ways to suppress the warning messages if the user entered something other than what is intended, but again, a bit off topic to talk about it here.
One important thing you have to watch out for is that anything you want R to print or plot, you need to wrap it with a print() function. Otherwise sourcing it won't print nor plot anything. Also, when typing in a parameter that is intended to be a string or character, don't add quotes. For example, for plotColor, type red instead of "red" in the prompt.
Most of the readline code are referenced from here:
I'm debugging a Qt application using LLDB. At a breakpoint I can write
(lldb) p myQString.toUtf8().data()
and see the string contained within myQString, as data() returns char*. I would like to be able to write
(lldb) p myQString
and get the same output. This didn't work for me:
(lldb) type summary add --summary-string "${var.toUtf8().data()}" QString
Is it possible to write a simple formatter like this, or do I need to know the internals of QString and write a python script?
Alternatively, is there another way I should be using LLDB to view QStrings this way?
The following does work.
First, register your summary command:
debugger.HandleCommand('type summary add -F set_sblldbbp.qstring_summary "QString"')
Here is an implementation
def make_string_from_pointer_with_offset(F,OFFS,L):
strval = 'u"'
try:
data_array = F.GetPointeeData(0, L).uint16
for X in range(OFFS, L):
V = data_array[X]
if V == 0:
break
strval += unichr(V)
except:
pass
strval = strval + '"'
return strval.encode('utf-8')
#qt5
def qstring_summary(value, unused):
try:
d = value.GetChildMemberWithName('d')
#have to divide by 2 (size of unsigned short = 2)
offset = d.GetChildMemberWithName('offset').GetValueAsUnsigned() / 2
size = get_max_size(value)
return make_string_from_pointer_with_offset(d, offset, size)
except:
print '?????????????????????????'
return value
def get_max_size(value):
_max_size_ = None
try:
debugger = value.GetTarget().GetDebugger()
_max_size_ = int(lldb.SBDebugger.GetInternalVariableValue('target.max-string-summary-length', debugger.GetInstanceName()).GetStringAtIndex(0))
except:
_max_size_ = 512
return _max_size_
It is expected that what you tried to do won't work. The summary strings feature does not allow calling expressions.
Calling expressions in a debugger is always interesting, in a data formatter more so (if you're in an IDE - say Xcode - formatters run automatically). Every time you stop somewhere, even if you just stepped over one line, all these little expressions would all automatically run over and over again, at a large performance cost - and this is not even taking into account the fact that your data might be in a funny state already and running expressions has the potential to alter it even more, making your debugging sessions trickier than needed.
If the above wall of text still hasn't discouraged you ( :-) ), you want to write a Python formatter, and use the SB API to run your expression. Your value is an SBValue object, which has access to an SBFrame and an SBTarget. The combination of these two allows you to run EvaluateExpression("blah") and get back another SBValue, probably a char* to which you can then ask GetSummary() to get your c-string back.
If, on the other hand, you are now persuaded that running expressions in formatters is suboptimal, the good news is that QString most certainly has to store its data pointer somewhere.. if you find out where that is, you can just write a formatter as ${var.member1.member2.member3.theDataPointer} and obtain the same result!
this is my trial-and-error adaptation of a UTF16 string interpretation lldb script I found online (I apologise that I don't remember the source - and that I can't credit the author)
Note that this is for Qt 4.3.2 and versions close to it - as the handling of the 'data' pointer has since changed between then and Qt 5.x
def QString_SummaryProvider(valobj, internal_dict):
data = valobj.GetChildMemberWithName('d')#.GetPointeeData()
strSize = data.GetChildMemberWithName('size').GetValueAsUnsigned()
newchar = -1
i = 0
s = u'"'
while newchar != 0:
# read next wchar character out of memory
data_val = data.GetChildMemberWithName('data').GetPointeeData(i, 1)
size = data_val.GetByteSize()
e = lldb.SBError()
if size == 1:
newchar = data_val.GetUnsignedInt8(e, 0) # utf-8
elif size == 2:
newchar = data_val.GetUnsignedInt16(e, 0) # utf-16
elif size == 4:
newchar = data_val.GetUnsignedInt32(e, 0) # utf-32
else:
s = s + '<unexpected char size - error parsing QString>'
break
if e.fail:
s = s + '<parse error:' + e.why() + '>'
break
i = i + 1
if i > strSize:
break
# add the character to our string 's'
# print "char2 = %s" % newchar
if newchar != 0:
s = s + unichr(newchar)
s = s + u'"'
return s.encode('utf-8')