Pretty printing R function
There's probably a better way but something like this would work:
f <- function(x, y = 2) {
return(x^y)
}
pretty <- function(fun){
captured <- capture.output(fun)
captured[1] <- paste(as.character(substitute(fun)), "<-", captured[1])
cat(paste(captured, collapse="\n"))
}
pretty(f)
## f <- function(x, y = 2) {
## return(x^y)
## }
print(list(1,2,3)) is long and ugly; how to print lists using print [ie, pretty-printing without having to switch between print vs paste0]
You can overwrite the print methods - even the ones for built-in types.
print.list <- function(x, ...) {
writeLines(paste0(x, collapse = ","))
}
print(list(1, 2))
#> 1,2
Or, you can create your own function.
wp <- function(x, ...) {
UseMethod("wp")
}
wp.default <- print
wp.list <- function(x, ...) {
writeLines(paste0(x, collapse = ","))
}
wp(list(1, 2))
#> 1,2
wp(matrix(1:6, nrow = 2))
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
Print pretty data.frames/tables to console
In case it helps anyone, I just stumbled across the fact that knitr
's kable
achieves a nice pretty print. Combine with some of the .Rprofile
suggestions above, this seems to achieve what I had in mind.
> knitr::kable(head(iris))
| Sepal.Length| Sepal.Width| Petal.Length| Petal.Width|Species |
|------------:|-----------:|------------:|-----------:|:-------|
| 5.1| 3.5| 1.4| 0.2|setosa |
| 4.9| 3.0| 1.4| 0.2|setosa |
| 4.7| 3.2| 1.3| 0.2|setosa |
| 4.6| 3.1| 1.5| 0.2|setosa |
| 5.0| 3.6| 1.4| 0.2|setosa |
| 5.4| 3.9| 1.7| 0.4|setosa |
Why is message() a better choice than print() in R for writing a package?
TL;DR
You should use cat()
when making the print.*()
functions for S3 objects. For everything else, you should use message()
unless the state of the program is problematic. e.g. bad error that is recoverable gives warning()
vs. show stopping error uses stop()
.
Goal
The objective of this post is to provide feedback on the different output options a package developer has access to and how one should structure output that is potentially on a new object or based upon strings.
R Output Overview
The traditional output functions are:
print()
cat()
message()
warning()
stop()
Now, the first two functions (print()
and cat()
) send their output to stdout
or standard output. The last three functions (message()
, warning()
, and stop()
) send their output to stderr
or the standard error. That is, the result output from a command like lm()
is sent to one file and the error output - if it exists - is sent to a completely separate file. This is particularly important for the user experience as diagnostics then are not cluttering the output of the results in log files and errors are then available to search through quickly.
Designing for Users and External Packages
Now, the above is framed more in a I/O mindset and not necessarily a user-facing frameset. So, let's provide some motivation for it in the context of an everyday R user. In particular, by using 3-5 or the stderr
functions, their output is able to be suppressed without tinkering with the console text via sink()
or capture.output()
. The suppression normally comes in the form of suppressWarnings()
, suppressMessages()
, suppressPackageStartupMessages()
, and so on. Thus, users are only confronted with result facing output. This is particularly important if you plan to allow users the flexibility of turning off text-based output when creating dynamic documents via either knitr, rmarkdown, or Sweave.
In particular, knitr
offers chunk options such as error = F
, message = F
, and warning = F
. This enables the reduction of text accompanying a command in the document. Furthermore, this prevents the need from using the results = "hide"
option that would disable all output.
Specifics of Output
print()
Up first, we have an oldie but a goodie, print()
. This function has some severe limitations. One of them being the lack of embedded concatenation of terms. The second, and probably more severe, is the fact that each output is preceded by [x]
followed by quotations around the actual content. The x
in this case refers to the element number being printed. This is helpful for debugging purposes, but outside of that it doesn't serve any purpose.
e.g.
print("Hello!")
[1] "Hello!"
For concatenation, we rely upon the paste()
function working in sync with print()
:
print(paste("Hello","World!"))
[1] "Hello World!"
Alternatively, one can use the paste0(...)
function in place of paste(...)
to avoid the default use of a space between elements governed by paste()
's sep = " "
parameter. (a.k.a concatenation without spaces)
e.g.
print(paste0("Hello","World!"))
[1] "HelloWorld!"
print(paste("Hello","World!", sep = ""))
[1] "HelloWorld!"
cat()
On the flip side, cat()
addresses all of these critiques. Most notably, the sep=" "
parameter of the paste()
functionality is built in allowing one to skip writing paste()
within cat()
. However, the cat()
function's only downside is you have to force new lines via \n
appended at the end or fill = TRUE
(uses default print width).
e.g.
cat("Hello!\n")
Hello!
cat("Hello","World!\n")
Hello World!
cat("Hello","World!\n", sep = "")
HelloWorld!
It is for this very reason why you should use cat()
when designing a print.*()
S3 method.
message()
The message()
function is one step better than even cat()
! The reason why is the output is distinct from traditional plain text as it is directed to stderr
instead of stdout
. E.g. They changed the color from standard black output to red output to catch the users eye.
Furthermore, you have the built in paste0()
functionality.
message("Hello ","World!") # Note the space after Hello
"Hello World!"
Moreover, message()
provides an error state that can be used with tryCatch()
e.g.
tryCatch(message("hello\n"), message=function(e){cat("goodbye\n")})
goodbye
warning()
The warning()
function is not something to use casually. The warning function is differentiated from the message function primarily by having a line prefixed to it ("Warning message:"
) and its state is consider to be problematic.
Misc: Casual use in a function may inadvertently trigger heartbreak while trying to upload the package to CRAN due to the example checks and warnings normally being treated as "errors".
stop()
Last but not least, we have stop()
. This takes warnings to the next level by completely killing the task at hand and returning control back to the user. Furthermore, it has the most serious prefix with the term "Error:"
being added.
Can I pretty print a complicated string in R console?
Did you try cat
?
cat(string)
b" it's me "ha ha"
How to do printf in r?
printf <- function(...) invisible(print(sprintf(...)))
The outer invisible
call may be unnecessary, I'm not 100% clear on how that works.
R cut Pretty-print Values Beyond Boundaries
It would be chop()
from my santoku
package:
library(santoku)
data <- seq(5, 95, 10)
chop(data, c(30, 40, 50, 60, 70))
## [1] [5, 30) [5, 30) [5, 30) [30, 40) [40, 50) [50, 60) [60, 70) [70, 95] [70, 95]
## [10] [70, 95]
## Levels: [5, 30) [30, 40) [40, 50) [50, 60) [60, 70) [70, 95]
If you want specific labels you can either pass them in yourself:
chop(data, c(30, 40, 50, 60, 70), c("< 30", "[30-40)", "[40-50)", "[50-60)", "[60-70)", ">= 70"))
Or in the latest version, you can use lbl_dash()
and specify first
and last
:
chop(data, c(30, 40, 50, 60, 70), labels = lbl_dash(first = "< 30", last = ">= 70"))
## [1] < 30 < 30 < 30 30 - 40 40 - 50 50 - 60 60 - 70 >= 70 >= 70 >= 70
## Levels: < 30 30 - 40 40 - 50 50 - 60 60 - 70 >= 70
There's no such argument for the default interval labels, but maybe there should be.
Related Topics
Get Country (And Continent) from Longitude and Latitude Point in R
How to Generate Multivariate Random Numbers with Different Marginal Distributions
Ggplot2 Equivalent of 'Factorization or Categorization' in Googlevis in R
Change Font Size for All Inline Equations R Markdown
R Plotly: Preserving Appearance of Two Legends When Converting Ggplot2 with Ggplotly
Under What Circumstances Does R Recycle
How to Programmatically Create Binary Columns Based on a Categorical Variable in Data.Table
Using Sample() with Sample Space Size = 1
Ggplot2: Shape, Color and Linestyle into One Legend
Could Not Find Function Tagpos
Adding Values in Two Data.Tables
How to Plot Contours on a Map with Ggplot2 When Data Is on an Irregular Grid
How to Show Directlabels After Geom_Smooth and Not After Geom_Line
Collapse Vector to String of Characters with Respective Numbers of Consequtive Occurences