24
$\begingroup$

I often find myself wondering about the best way to present R code, and output.

The issue is in R itself, in the console, we will see this type of thing:

> X <- rnorm(100)
> Y <- X + rnorm(100)
> lm(Y ~ X) %>% summary()


Residuals:
    Min      1Q  Median      3Q     Max 
-3.0024 -0.6662  0.0044  0.7071  2.2079 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.0247     0.1073  -0.230    0.818    
X             0.9865     0.1091   9.041 1.46e-14 ***
---


> lm(Y ~ -1 + X) %>% summary()

Residuals:
     Min       1Q   Median       3Q      Max 
-3.02849 -0.68970 -0.02017  0.68257  2.18443 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)    
X   0.9855     0.1085   9.082  1.1e-14 ***

So it seems perfectly natural to present this code and output exactly as above. It has the advantage of clearly marking code (prefaced with > as per the R console) from the output of the code.

But the problem with that is, for a user that wants to actually try the code out for themselves, they have to manually remove the > from each line. I have had a number of my posts edited by other users to remove them. So one alternative is to present it like this:

X <- rnorm(100)
Y <- X + rnorm(100)
lm(Y ~ X) %>% summary()


Residuals:
    Min      1Q  Median      3Q     Max 
-3.0024 -0.6662  0.0044  0.7071  2.2079 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.0247     0.1073  -0.230    0.818    
X             0.9865     0.1091   9.041 1.46e-14 ***
---

lm(Y ~ -1 + X) %>% summary()

Residuals:
     Min       1Q   Median       3Q      Max 
-3.02849 -0.68970 -0.02017  0.68257  2.18443 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)    
X   0.9855     0.1085   9.082  1.1e-14 ***

This might be fine for experienced R users, but it blurs the distinction between code and output. So we might break it up like this:

X <- rnorm(100)
Y <- X + rnorm(100)
lm(Y ~ X) %>% summary()

which produces

Residuals:
    Min      1Q  Median      3Q     Max 
-3.0024 -0.6662  0.0044  0.7071  2.2079 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.0247     0.1073  -0.230    0.818    
X             0.9865     0.1091   9.041 1.46e-14 ***
---

and then we fit the model:

lm(Y ~ -1 + X) %>% summary()

which produces :

Residuals:
     Min       1Q   Median       3Q      Max 
-3.02849 -0.68970 -0.02017  0.68257  2.18443 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)    
X   0.9855     0.1085   9.082  1.1e-14 ***

which takes more time to write and makes the post longer and more verbose.

Maybe I am being a little too pedantic, but I just wondered if others have had similar thoughts or if there is an alternative, or indeed if one of the above approaches is considered better in general ?

$\endgroup$
11
  • 1
    $\begingroup$ The same issue arises for just about every other language I know about -- and it's worse for anything based on a series of menu choices. But in terms of R and any similar language (it's certainly true of Stata which I know quite well) there can be a bonus in (a) showing code and results AND ALSO (b) showing code that can be copied and pasted by a serious reader. The balance depends on the question: (1) sometimes a reader familiar with the language doesn't need to run anything as the question makes sense immediately (2) sometimes only playing with the code is going to point to an answer. $\endgroup$ – Nick Cox Oct 8 '20 at 18:24
  • 1
    $\begingroup$ Naturally this intersects with other issues, including whether the OP has posted a reproducible example. $\endgroup$ – Nick Cox Oct 8 '20 at 18:26
  • $\begingroup$ @NickCox I agree that sometimes it is obvious which approach to take, but on a daily basis I find myself in a situation where it isn't obvious and we usually don't know how proficient the OP is in the language and then we also need to consider other users who may be interested in the question/answer. The 3rd solution I suggested is OK for those who are not so proficient in the language, but again, for those who want to run the code, they have to either copy/paste one block at a time, or copy the whole thing and then cut out the commentary. I have found myself preferring the 1st option usually $\endgroup$ – Robert Long Oct 8 '20 at 18:32
  • $\begingroup$ Indeed. But remember that the criterion here should be that the question should be statistical in essence, so how the code is presented shouldn't matter too much if it is clear. $\endgroup$ – Nick Cox Oct 8 '20 at 18:42
  • 3
    $\begingroup$ FYI: for syntax highlighting, you can add <!-- language-all: lang-r --> anywhere in the body of the message (it will be invisible). $\endgroup$ – Tim Mod Oct 8 '20 at 19:04
  • 1
    $\begingroup$ @Tim thanks, I didn't know that. Presumably that's for the cases where the OP doesn't contain the R tag ? $\endgroup$ – Robert Long Oct 8 '20 at 19:08
  • $\begingroup$ @NickCox Quite. I am probably overthinking this :/ $\endgroup$ – Robert Long Oct 8 '20 at 19:10
  • $\begingroup$ @RobertLong correct. $\endgroup$ – Tim Mod Oct 8 '20 at 19:26
  • 4
    $\begingroup$ I don't think you're over-thinking. Good presentation of code helps any question greatly, just like good writing. $\endgroup$ – Nick Cox Oct 8 '20 at 21:39
  • 1
    $\begingroup$ I comment out the output (eg, here). I'll try to put an answer together later. $\endgroup$ – gung - Reinstate Monica Oct 9 '20 at 11:37
  • 1
    $\begingroup$ Robert, occasionally I have resorted to presenting the code block by block and then appending the complete code to the post. See stats.stackexchange.com/a/129786/919 for an example. $\endgroup$ – whuber Mod Oct 9 '20 at 13:19
16
$\begingroup$

I occasionally run the (R) code in people's answers, so I think about it from that perspective. I don't include the R console's command prompt, >, because when you copy and paste code you have the added hassle of taking all that out before you can run it. I also don't like code in many separate small chunks with text in between, because it's more of a hassle to copy and paste. Consider:

First I did

stuff

Then I did

other stuff

And then I fit the model

model

which yields output

output

but notice that to get what you want, you have to process the output

process

then you can get what you want by computing

extra computations

so now you can get your desired output

 output

I understand the idea of narrative flow, but this is really tedious to copy and paste if you want to try playing with the code yourself.

I prefer to put the code into fewer, longer code blocks to the extent possible. When small comments are needed to discuss moving from one step to the next, I use code-comments. (I also try to use comments to make the code as transparent as possible for people who may not be very fluent in R, and I use only base R, use = instead of <-, etc.) Of course, showing the output is important to the answer, so I do, but I comment it out.

Here is an example code block from a recent answer of mine:

set.seed(8649)     # this makes the example exactly reproducible
N      = 10        # this is how much data I'll generate
latent = rnorm(N)  # this is the actual latent variable I want to be measuring

##### generate latent responses to items
item1 = latent + rnorm(N, mean=0, sd=0.2)  # the strongest correlate
item2 = latent + rnorm(N, mean=0, sd=0.3)
item3 = latent + rnorm(N, mean=0, sd=0.5)
item4 = latent + rnorm(N, mean=0, sd=1.0)
item5 = latent + rnorm(N, mean=0, sd=1.2)  # the weakest

##### convert latent responses to ordered categories
item1 = findInterval(item1, vec=c(-Inf,-2.5,-1, 1,2.5,Inf))  # fairly unbiased
item2 = findInterval(item2, vec=c(-Inf,-2.5,-1, 1,2.5,Inf))
item3 = findInterval(item3, vec=c(-Inf,-3,  -2, 2,3,  Inf))  # middle values typical
item4 = findInterval(item4, vec=c(-Inf,-3,  -2, 2,3,  Inf))
item5 = findInterval(item5, vec=c(-Inf,-3.5,-3,-1,0.5,Inf))  # high ratings typical

##### combined into final scale
manifest = round(rowMeans(cbind(item1, item2, item3, item4, item5)), 1)
manifest
# [1]  3.4  3.6  3.4  3.8  2.6  3.4  3.2  2.0  3.8  3.2
round(latent, 1)
# [1]  1.3  0.6  0.2  1.0 -1.5  0.1  0.4 -2.5  2.3 -0.3
cor(manifest, latent)
# [1] 0.9280074

There was also some discussion about how to get syntax highlighting. If the [r] tag is on a thread, syntax highlighting (for R) should automatically occur. Before any code block, you can initiate, or override, local syntax highlighting with <!-- language: lang-r --> on a prior line. If you want that to span several code blocks within your answer, use <!-- language-all: lang-r -->, instead. Note that SE is switching the engine it uses for formatting. The new system will let you set off a code block without indenting by using ``` before and after the block, and you can turn on syntax highlighting for that block by appending lang-r to the set of opening backticks.

$\endgroup$
15
$\begingroup$

This is R code:

lm(rnorm(10) ~ I(rnorm(10)))

It has been formatted according to the new method (here on CV) of preceding and following it with a left-justified row of three backticks ```. The user can copy everything directly because there are no extra formatting marks within the block itself -- not even indentation.

This is its output:

Call:
lm(formula = y ~ x)

Coefficients:
(Intercept)            x  
     0.5912      -0.7138

The latter was formatted first as a code block then that itself was quoted, producing the vertical line to the left that visually distinguishes the output from the code. These are fast, easy steps: select the text and hit the appropriate icon above the textbox.

$\endgroup$
3
  • 2
    $\begingroup$ (+1) thanks, I hadn't thought of that. I have always used quoting only for quoting from the original post ! $\endgroup$ – Robert Long Oct 8 '20 at 18:49
  • 6
    $\begingroup$ Hm. Interesting. This is the first time I see this "new method", did I miss an announcement? But if I understand correctly, then it's still hard to show a longer snippet with code and output interspersed, as in this recent answer of mine (the second code block, scroll down a bit). It's still not possible for the user to just collect everything and copy-paste it into the console, right? (I know I'm badly mixing up styles of code presentation there.) $\endgroup$ – Stephan Kolassa Oct 9 '20 at 9:24
  • 1
    $\begingroup$ @Stephan Code is code. It sounds rather like you're referring to pasting a long list of lines on the R console in which function calls and print output are intermingled. IMHO, such long sequences won't work well in a post here: consider breaking them into smaller sections with interleaved explanations or captions. But if you really want to present such a monolithic block, then simply paste it into the textbox, press the icon for formatting code, and then go through separately selecting the print output portions and pressing the quote icon for them. $\endgroup$ – whuber Mod Oct 9 '20 at 13:17
14
$\begingroup$

I like to use the reprex package. If you do reprex::reprex(venue="so") it produces a stackoverflow-friendly formatted text out of whatever R code you have on the clipboard right now:

lm(rnorm(10) ~ I(rnorm(10)))
#> 
#> Call:
#> lm(formula = rnorm(10) ~ I(rnorm(10)))
#> 
#> Coefficients:
#>  (Intercept)  I(rnorm(10))  
#>     -0.13374      -0.09452

Created on 2020-10-09 by the reprex package (v0.3.0)

As you see, the output is included as R comments so it will not interfere with copy/paste. As a bonus it automatically renders whatever plots are in there and uploads to imgur, inserts the right link.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .