Is there any way when using the str() function in R to start at the beginning of the output provided rather than the end after the function is run?
Basically I'd like a faster way to get to the beginning of the output rather than scrolling manual back up through the output. This would be especially useful when looking at the structure of larger objects like spatial data.
A variation of the answer linked to by Dason (https://stackoverflow.com/a/3837885/1017276) would be to redirect the output to a browser.
view_str <- function(x)
{
tmp <- tempfile(fileext = ".html")
x <- capture.output(str(x))
write(paste0(x, collapse = "<br/>"),
file = tmp)
viewer <- getOption("viewer")
if (!is.null(viewer)) # If in RStudio, use RStudio's browser
{
viewer(tmp)
}
else{ # Otherwise use the system's default browser
utils::browseURL(tmp)
}
}
view_str(mtcars)
Related
I have a script where I'd prefer to read in a certain file, but if that file doesn't exist then I want to read in a different file. Files change by day so I don't want to pick and choose manually.
I'm currently using tryCatch to do this. Basically, try to read the first file, and if there's an error then return an empty df. Then have logic where if the table is blank, read in the file I know exists. While my solution works, it feels extremely clunky and I'd like to learn best practices going forward.
Current solution:
df <- suppressWarnings(tryCatch({read.csv('this file will not exist on your computer', stringsAsFactors = FALSE)},
error = function(e) data.frame()
))
if(nrow(df) == 0){
df <- cars # this will be a file I know exists
}
## Continue with function using a df I know exists ##
I don't love using suppressWarnings if I don't need to, but I disliked the warning that kept popping up (obviously the file doesn't exist that's why I'm tryCatching).
Ideally this could be a 3-line code. 1) Check if file exists. 2) If it does exist then read it. 3) If it doesn't exist, then read the other file I know exists. Any thoughts?
As suggested by #All Downhill From Here using file.exists would check if the file exists at the given location. As file.exists returns a logical value you can wrap it in if/else block.
if(file.exists('data.csv')) {
data <- read.csv('data.csv')
} else {
data <- read.csv('confirm.csv')
}
This also could be an option using purrr::map_if. First we create a character vector of our desired file names:
library(purrr)
vec <- c("data.csv")
vec %>%
map_if(~ file.exists(.x), ~ read.csv(.x, header = TRUE),
.else = read.csv(gsub("([a-z0-9_]+)\\.([a-z]+$)", "confirm\\.\\2",
vec, perl = TRUE), header = TRUE))
We may make use of an index
data <- read.csv(c("confirm.csv", "data.csv")[1 + file.exists("data.csv")])
I am looking to create a custom function in R that will allow the user to call the function and then it will produce an auto complete pipeline ready for them to edit, this way they can jump into quickly customizing the standard pipeline instead of copy pasting from an old script or retyping. How can I go about setting up this sort of autocomplete:
#pseudo code what I type---
seq.Date(1,2,by = "days") %>%
pblapply(function(x){
read.fst(list.files(as.character(x), as.data.table = T) %>%
group_by(x) %>%
count()
}) %>% rbindlist()
#how can I write a function so that when I call that function, it ouputs an autocomplete
#of the above so that I can go ahead and start just customizing the code? Something like this
my_autocomplete_function = function(x) {
print(
"
seq.Date(as.Date(Sys.Date()),as.Date(Sys.Date()+1),by = 'days') %>%
pbapply::pblapply(function(x){
fst::read.fst(list.files(as.character(x), as.data.table = T)) %>%
#begin filtering and grouping by below custom
group_by()
}) %>% rbindlist()
")
}
#I can just print the function and copy paste the text from the output in my console to my script
my_autocomplete_function()
#but I rather it just autocomplete and appear in the script if possible?
Putting text into the command line will probably be a function of the interface you are using to run R - is it plain R, Rstudio, etc?
One possibility might be to use the clipr package and put the code into the clipboard, then prompt the user to hit their "paste" button to get it on the command line. For example this function which creates a little code string:
> writecode = function(x){
code = paste0("print(",x,")")
clipr::write_clip(code)
message("Code ready to paste")}
Use it like this:
> writecode("foo")
Code ready to paste
Then when I hit Ctrl-V to paste, I see this:
> print(foo)
I can then edit that line. Any string will do:
> writecode("bar")
Code ready to paste
[ctrl-V]
> print(bar)
Its one extra key for your user to press, but having a chunk of code appear on the command line with no prompting might be quite surprising for a user.
I spend my day reading the source code for utils auto completion. The linebuffer only contains the code for ... one line, so that can't do fully what your looking for here, but it is quite customizable. Maybe the rstudio source code for autocompletion has more answers. Here is an example to write and activate your own custom auto completer. It is not well suited for packages as any new pacakge could overwrite the settings.
Below a
#load this function into custom.completer setting to activate
rc.options("custom.completer" = function(x) {
#perhaps debug it, it will kill your rsession sometimes
#browser()
###default completion###
##activating custom deactivates anything else
#however you can run utils auto completer also like this
#rstudio auto completation is not entirely the same as utils
f = rc.getOption("custom.completer")
rc.options("custom.completer" = NULL)
#function running base auto complete.
#It will dump suggestion into mutable .CompletionEnv$comps
utils:::.completeToken() #inspect this function to learn more about completion
rc.options("custom.completer" = f)
###your custom part###
##pull this environment holding all input/output needed
.CompletionEnv = utils:::.CompletionEnv
#perhaps read the 'token'. Also 'linebuffer', 'start' & 'end' are also useful
token = .CompletionEnv$token
#generate a new completion or multiple...
your_comps = paste0(token,c("$with_tomato_sauce","$with_apple_sauce"))
#append your suggestions to the vanilla suggestions/completions
.CompletionEnv$comps = c(your_comps,.CompletionEnv$comps)
print(.CompletionEnv$comps)
#no return used
NULL
})
xxx<tab>
NB! currently rstudio IDE will backtick quote any suggestion which is not generated by them. I want to raise an issue on that someday.
Bonus info: A .DollarNames.mys3class-method can be very useful and works with both rstudio and utils out of the box e.g.
#' #export
.DollarNames.mys3class = function(x, pattern = "") {
#x is the .CompletionEnv
c("apple","tomato")
}
I am trying to automate the data ingestion process in an R script that pulls data from a directory that updates regularly.
The general framework follows this process
library(sp)
library(rgdal)
library(raster)
f1.t1.cir <- stack("../raster/field1/f1_cir_t1.tif")
f1.t1.NDVI <- stack("../raster/field1/f1_ndvi_t1.tif")
f1.t1.RGB <- stack("../raster/field1/f1_ndvi_t1.tif")
f1.dat <- c(f1.t1.cir, f1.t1.NDVI, f1.t1.RGB)
for (i in f1.dat){
plotRGB(i)
}
I would like to generate each f1.t1.cir type object from the directory directly such that when I add a new TIFF file f1_cir_t2.tif, the r script will create an object f1.cir.t2.
I am trying to use something like
a <- list.files(path= "../raster/field1", pattern = "\\.tif$")
b <- gsub("_", "\\.", a)
for (i in a) {
assign(get(b[(which(a==i))]), stack((paste("../raster/field1/", i,sep=""))))
}
At this point, I would have all tiff files as stacked multiband raster objects in the R workspace.
I am getting the following error,
Error in get(b[(which(a == i))]) : object 'f1_t1_DSM.tif' not found
I can not figure out if this is a get() problem, or something else.
for reference
> a
[1] "f1_t1_DSM.tif" "f1_t1_NDVI.tif"
> b
[1] "f1.t1.DSM.tif" "f1.t1.NDVI.tif"
so that much is working, I think.
Any suggestions?
#joran, great suggestion...
f1.t1<-list()
for(i in list.files(path= "../raster/field1", pattern = "\\.tif$")){
f1.t1[[i]]<-stack((paste("../raster/field1/", i, sep="")))
}
Worked very well, no need to change the names.
Thank you.
I have a function that reports progress via message(). Under normal use, this is desirable, and the messages can be suppressed via suppressMessages() if desired.
However, I'm writing a vignette (in Rmarkdown) that calls this function, and the results include a full page of these progress updates. I'd like to include the first few lines of messages, but not waste a whole page on them. Is there a way to configure this?
I've tried passing the chunk option R.options=list(max.print=10), as suggested in another answer here, but that doesn't appear to apply to messages. Or at least, not in this context, where the actual messages are generated one or two at a time within each function, but that function is called as part of a loop.
The general structure I'm working with is:
function1 <- function(file.list){
res <- list()
for(i in seq_along(file.list)){
message("processing ", file.list[i])
res[i] <- function2(file.list[i])
}
}
function2 <- function(file){
message("analyzing ", file)
tryVal <- try(res <- analysisFunction(file), silent = TRUE)
if(inherits(tryVal, "try-error")){
message("*** problem with ", file, " ***")
} else {
## additional processing
}
return(res)
}
The markdown chunk looks like this:
```{r batchFlowHist, R.options=list(max.print=10)}
batch1 <- function1(flowPloidyFiles)
```
I'd like my vignette to display the messages from the first few files that are processed, but not the entire loop. Is there some way to do this?
Turns out this is already supported in knitr via the message argument. The linked answer suggested it didn't work, but since that was posted it must have been added. So the following solves my problem:
```{r batchFlowHist, message = 1:10}
batch1 <- function1(flowPloidyFiles)
```
Is there an equivalent to the unix less command that can be used within the R console?
There is also page() which displays a representation of an object in a pager, like less.
dat <- data.frame(matrix(rnorm(1000), ncol = 10))
page(dat, method = "print")
Not really. There are the commands
head() and tail() for showing the beginning and end of objects
print() for explicitly showing an object, and just its name followed by return does the same
summary() for concise summary that depends on the object
str() for its structure
and more. An equivalent for less would be a little orthogonal to the language and system. Where the Unix shell offers you less to view the content of a file (which is presumed to be ascii-encoded), it cannot know about all types.
R is different in that it knows about the object types which is why summary() -- as well as the whole modeling framework -- are more appropriate.
Follow-up edit: Another possibility is provided by edit() as well as edit.data.frame().
I save the print output to a file and then read it using an editor or less.
Type the following in R
sink("Routput.txt")
print(varname)
sink()
Then in a shell:
less Routput.txt
If the file is already on disk, then you can use file.show
You might like my little toy here:
short <- function(x=seq(1,20),numel=4,skipel=0,ynam=deparse(substitute(x))) {
ynam<-as.character(ynam)
#clean up spaces
ynam<-gsub(" ","",ynam)
#unlist goes by columns, so transpose to get what's expected
if(is.list(x)) x<-unlist(t(x))
if(2*numel >= length(x)) {
print(x)
}
else {
frist=1+skipel
last=numel+skipel
cat(paste(ynam,'[',frist,'] thru ',ynam,'[',last,']\n',sep=""))
print(x[frist:last])
cat(' ... \n')
cat(paste(ynam,'[',length(x)-numel-skipel+1,'] thru ', ynam, '[', length(x)-skipel,']\n',sep=""))
print(x[(length(x)-numel-skipel+1):(length(x)-skipel)])
}
}
blahblah copyright by me, not Disney blahblah free for use, reuse, editing, sprinkling on your Wheaties, etc.