loading...

Among the ‘SciViews’ R packages, ‘form.io’ would be devoted to the input and especially output of textual data (input of tabular data being managed by ‘data.io’). Here, we reuse existing functions like knitr::kable() or pander::pander(), but we make them simpler to use and working in all cases being:

  • At the R console,
  • Inside an R Markdown/Notebook inline area just beneath a chunk,
  • In a knitted R markdown file, HTML format,
  • Idem, but with a LaTeX or Word format.

Both ‘knitr’ with kable(), and the ‘kableExtra’ functions, and ‘pander’ with the pander() functions do that. However, the syntax is not always easy, and code eventually differ depending on the situation among the four possible ones here above.

Moreover, none of kable() or pander() functions allow for the translation of the result in a different language, and they also do not take into account attributes like label or units (see the ‘data.io’ package).

So, the new generic function form() creates formatted tables or other objects (lists, …) to be directly integrated in an R Markdown document, or to be used at the R Console. Just load the package (and perhaps also ‘data.io’, or even the whole SciViews::R suite):

# Better to use SciViews::R() here
library(data.io)
#> 
#> Attaching package: 'data.io'
#> The following object is masked from 'package:base':
#> 
#>     write
library(form.io)
#> Registered S3 method overwritten by 'svMisc':
#>   method             from   
#>   $.subsettable_type data.io
# To change language to, e.g., French
options(data.io_lang = "FR") # Same language option is used by form.io::form()
# Test
trees <- read("trees", package = "datasets")
lm_1 <- lm(data = trees, volume ~ .)
form(trees)
Diamètre à 1,4m [m] Hauteur [m] Volume de bois [m3]
0,21 21 0,29
0,22 20 0,29
0,22 19 0,29
0,27 22 0,46
0,27 25 0,53
0,27 25 0,56
0,28 20 0,44
0,28 23 0,52
0,28 24 0,64
0,28 23 0,56
0,29 24 0,69
0,29 23 0,59
0,29 23 0,61
0,3 21 0,6
0,3 23 0,54
0,33 23 0,63
0,33 26 0,96
0,34 26 0,78
0,35 22 0,73
0,35 20 0,7
0,36 24 0,98
0,36 24 0,9
0,37 23 1
0,41 22 1,1
0,41 24 1,2
0,44 25 1,6
0,44 25 1,6
0,46 24 1,7
0,46 24 1,5
0,46 24 1,4
0,52 26 2,2
form(head(trees), data = trees) # Try without data =
Diamètre à 1,4m [m] Hauteur [m] Volume de bois [m3]
0,21 21 0,29
0,22 20 0,29
0,22 19 0,29
0,27 22 0,46
0,27 25 0,53
0,27 25 0,56
form(anova(lm_1), data = trees)
Analyse de la variance - Réponse : Volume de bois
  DDL Somme carrés Carrés moyens Stat. F Valeur p
Diamètre à 1,4m 1 6,1 6,1 499 2,2e-19 * * *
Hauteur 1 0,081 0,081 6,6 0,016 *
Résidus 28 0,34 0,012

Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ’ ’ 1

Using form()

pander::pander() works well with knitted documents, but it outputs text only in R notebooks. The form() function is solving this problem, while still outputting a textual markdown version at the R console.

trees <- read("trees", package = "datasets")

lm_1 <- lm(data = trees, volume ~ .)
lm_2 <- lm( data = trees, volume ~ I(diameter^2))
  • Data frame :
form(trees, caption = "Mesure de cerisiers noirs")
Mesure de cerisiers noirs
Diamètre à 1,4m [m] Hauteur [m] Volume de bois [m3]
0,21 21 0,29
0,22 20 0,29
0,22 19 0,29
0,27 22 0,46
0,27 25 0,53
0,27 25 0,56
0,28 20 0,44
0,28 23 0,52
0,28 24 0,64
0,28 23 0,56
0,29 24 0,69
0,29 23 0,59
0,29 23 0,61
0,3 21 0,6
0,3 23 0,54
0,33 23 0,63
0,33 26 0,96
0,34 26 0,78
0,35 22 0,73
0,35 20 0,7
0,36 24 0,98
0,36 24 0,9
0,37 23 1
0,41 22 1,1
0,41 24 1,2
0,44 25 1,6
0,44 25 1,6
0,46 24 1,7
0,46 24 1,5
0,46 24 1,4
0,52 26 2,2
# Equivalent to:
#form(trees, caption = "Mesure de cerisiers noirs", labels = TRUE, units = TRUE)

Any manipulation of a data frame with labels and units makes it loose its attributes, … but the data = argument is there to look at original data set for these attributes :

form(head(trees))
Diamètre à 1,4m [m] Hauteur [m] Volume de bois [m3]
0,21 21 0,29
0,22 20 0,29
0,22 19 0,29
0,27 22 0,46
0,27 25 0,53
0,27 25 0,56
form(head(trees), data = trees)
Diamètre à 1,4m [m] Hauteur [m] Volume de bois [m3]
0,21 21 0,29
0,22 20 0,29
0,22 19 0,29
0,27 22 0,46
0,27 25 0,53
0,27 25 0,56
  • Objects data.table :
library(data.table)
trees_dt <- as.data.table(trees)
class(trees_dt)
#> [1] "data.table" "data.frame"
form(trees_dt)
Diamètre à 1,4m [m] Hauteur [m] Volume de bois [m3]
0,21 21 0,29
0,22 20 0,29
0,22 19 0,29
0,27 22 0,46
0,27 25 0,53
0,27 25 0,56
0,28 20 0,44
0,28 23 0,52
0,28 24 0,64
0,28 23 0,56
0,29 24 0,69
0,29 23 0,59
0,29 23 0,61
0,3 21 0,6
0,3 23 0,54
0,33 23 0,63
0,33 26 0,96
0,34 26 0,78
0,35 22 0,73
0,35 20 0,7
0,36 24 0,98
0,36 24 0,9
0,37 23 1
0,41 22 1,1
0,41 24 1,2
0,44 25 1,6
0,44 25 1,6
0,46 24 1,7
0,46 24 1,5
0,46 24 1,4
0,52 26 2,2
  • Object lm :
form(lm_1, data = trees, signif_stars = TRUE)
Paramètres de la régression linéaire : volume ~ .
  Estimateur Erreur type Stat. t Valeur p
(Ordonnée à l’origine) -1,6 0,24 -6,7 2,9e-07 * * *
Diamètre à 1,4m 5,3 0,3 18 8,9e-17 * * *
Hauteur 0,031 0,012 2,6 0,016 *
  • Summary of lm :
form(summary(lm_1))
Résumé de la régression linéaire : volume ~ .
Observations Erreur type des résidus R2 R2 ajusté
31 0,11 0,948 0,944
  • Anova of lm :
form(anova(lm_1), data = trees) #, caption = "ANOVA de ma regression linéaire")
Analyse de la variance - Réponse : Volume de bois
  DDL Somme carrés Carrés moyens Stat. F Valeur p
Diamètre à 1,4m 1 6,1 6,1 499 2,2e-19 * * *
Hauteur 1 0,081 0,081 6,6 0,016 *
Résidus 28 0,34 0,012

Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ’ ’ 1

  • Generalized linear models :
# From ?glm
## Dobson (1990) Page 93: Randomized Controlled Trial :
ad <- data.frame(treatment = gl(3, 3), outcome = gl(3, 1, 9),
  counts = c(18, 17, 15, 20, 10, 20, 25, 13, 12))
glm_1 <- glm(data = ad, counts ~ outcome + treatment, family = poisson())
glm_1
#> 
#> Call:  glm(formula = counts ~ outcome + treatment, family = poisson(), 
#>     data = ad)
#> 
#> Coefficients:
#> (Intercept)     outcome2     outcome3   treatment2   treatment3  
#>   3.045e+00   -4.543e-01   -2.930e-01    1.338e-15    1.421e-15  
#> 
#> Degrees of Freedom: 8 Total (i.e. Null);  4 Residual
#> Null Deviance:       10.58 
#> Residual Deviance: 5.129     AIC: 56.76
summary(glm_1)
#> 
#> Call:
#> glm(formula = counts ~ outcome + treatment, family = poisson(), 
#>     data = ad)
#> 
#> Deviance Residuals: 
#>        1         2         3         4         5         6         7         8  
#> -0.67125   0.96272  -0.16965  -0.21999  -0.95552   1.04939   0.84715  -0.09167  
#>        9  
#> -0.96656  
#> 
#> Coefficients:
#>               Estimate Std. Error z value Pr(>|z|)    
#> (Intercept)  3.045e+00  1.709e-01  17.815   <2e-16 ***
#> outcome2    -4.543e-01  2.022e-01  -2.247   0.0246 *  
#> outcome3    -2.930e-01  1.927e-01  -1.520   0.1285    
#> treatment2   1.338e-15  2.000e-01   0.000   1.0000    
#> treatment3   1.421e-15  2.000e-01   0.000   1.0000    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for poisson family taken to be 1)
#> 
#>     Null deviance: 10.5814  on 8  degrees of freedom
#> Residual deviance:  5.1291  on 4  degrees of freedom
#> AIC: 56.761
#> 
#> Number of Fisher Scoring iterations: 4
anova(glm_1)
#> Analysis of Deviance Table
#> 
#> Model: poisson, link: log
#> 
#> Response: counts
#> 
#> Terms added sequentially (first to last)
#> 
#> 
#>           Df Deviance Resid. Df Resid. Dev
#> NULL                          8    10.5814
#> outcome    2   5.4523         6     5.1291
#> treatment  2   0.0000         4     5.1291
anorexia <- read("anorexia", package = "MASS")
form(anorexia)
Treat Prewt Postwt
Cont 81 80
Cont 89 80
Cont 92 86
Cont 74 86
Cont 78 76
Cont 88 78
Cont 87 75
Cont 75 87
Cont 81 74
Cont 78 85
Cont 78 77
Cont 89 80
Cont 81 90
Cont 78 81
Cont 70 82
Cont 77 77
Cont 85 84
Cont 86 75
Cont 84 80
Cont 80 73
Cont 86 88
Cont 84 85
Cont 80 81
Cont 78 81
Cont 72 88
Cont 89 79
CBT 80 82
CBT 85 86
CBT 82 81
CBT 83 82
CBT 80 76
CBT 89 104
CBT 95 98
CBT 76 93
CBT 81 73
CBT 80 82
CBT 85 97
CBT 89 95
CBT 81 82
CBT 76 72
CBT 70 91
CBT 80 71
CBT 83 85
CBT 83 82
CBT 88 89
CBT 84 84
CBT 86 83
CBT 76 76
CBT 80 83
CBT 88 100
CBT 83 85
CBT 80 84
CBT 84 85
CBT 81 96
CBT 87 87
FT 84 95
FT 83 94
FT 86 92
FT 82 92
FT 87 100
FT 80 77
FT 77 77
FT 94 102
FT 73 95
FT 80 75
FT 82 78
FT 82 96
FT 78 91
FT 84 92
FT 90 94
FT 86 92
FT 87 98
glm_2 <- glm(data = anorexia, Postwt ~ Prewt + Treat, family = gaussian)
glm_3 <- glm(data = anorexia, Postwt ~ Prewt + Treat + offset(Prewt), family = gaussian)
form(glm_2)
#> 
#> ( Dispersion parameter for   gaussian   family taken to be   48.69504 )
Paramètres de la régression linéaire : Postwt ~ Prewt + Treat
Null deviance: 4584 on 71 degrees of freedom
Residual deviance: 3311 on 68 degrees of freedom
form(summary(glm_2))
#> 
#> ( Dispersion parameter for   gaussian   family taken to be   48.69504 )
Paramètres de la régression linéaire généralisée : Postwt ~ Prewt + Treat
Null deviance: 4584 on 71 degrees of freedom
Residual deviance: 3311 on 68 degrees of freedom
#form(anova(glm_2))
form(aov(glm_2))
Paramètres de la régression linéaire : glm_2
  Df Sum Sq Mean Sq F value Pr(>F)
Prewt 1 507 507 10 0,0019
Treat 2 766 383 7,9 0,00084
Residuals 68 3311 49
# A Gamma example, from McCullagh & Nelder (1989, pp. 300-2)
clotting <- data.frame(
  u = c(5, 10, 15, 20, 30, 40, 60, 80, 100),
  lot1 = c(118, 58, 42, 35, 27, 25, 21, 19, 18),
  lot2 = c(69, 35, 26, 21, 18, 16, 13, 12, 12))
glm_4 <- glm(data = clotting, lot1 ~ log(u), family = Gamma)
glm_4
#> 
#> Call:  glm(formula = lot1 ~ log(u), family = Gamma, data = clotting)
#> 
#> Coefficients:
#> (Intercept)       log(u)  
#>    -0.01655      0.01534  
#> 
#> Degrees of Freedom: 8 Total (i.e. Null);  7 Residual
#> Null Deviance:       3.513 
#> Residual Deviance: 0.01673   AIC: 37.99
summary(glm_4)
#> 
#> Call:
#> glm(formula = lot1 ~ log(u), family = Gamma, data = clotting)
#> 
#> Deviance Residuals: 
#>      Min        1Q    Median        3Q       Max  
#> -0.04008  -0.03756  -0.02637   0.02905   0.08641  
#> 
#> Coefficients:
#>               Estimate Std. Error t value Pr(>|t|)    
#> (Intercept) -0.0165544  0.0009275  -17.85 4.28e-07 ***
#> log(u)       0.0153431  0.0004150   36.98 2.75e-09 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for Gamma family taken to be 0.002446059)
#> 
#>     Null deviance: 3.51283  on 8  degrees of freedom
#> Residual deviance: 0.01673  on 7  degrees of freedom
#> AIC: 37.99
#> 
#> Number of Fisher Scoring iterations: 3
anova(glm_4)
#> Analysis of Deviance Table
#> 
#> Model: Gamma, link: inverse
#> 
#> Response: lot1
#> 
#> Terms added sequentially (first to last)
#> 
#> 
#>        Df Deviance Resid. Df Resid. Dev
#> NULL                       8     3.5128
#> log(u)  1   3.4961         7     0.0167

glm_5 <- glm(data = clotting, lot2 ~ log(u), family = Gamma)
glm_5
#> 
#> Call:  glm(formula = lot2 ~ log(u), family = Gamma, data = clotting)
#> 
#> Coefficients:
#> (Intercept)       log(u)  
#>    -0.02391      0.02360  
#> 
#> Degrees of Freedom: 8 Total (i.e. Null);  7 Residual
#> Null Deviance:       3.119 
#> Residual Deviance: 0.01267   AIC: 27.03
summary(glm_5)
#> 
#> Call:
#> glm(formula = lot2 ~ log(u), family = Gamma, data = clotting)
#> 
#> Deviance Residuals: 
#>      Min        1Q    Median        3Q       Max  
#> -0.05574  -0.02925   0.01030   0.01714   0.06371  
#> 
#> Coefficients:
#>               Estimate Std. Error t value Pr(>|t|)    
#> (Intercept) -0.0239085  0.0013265  -18.02 4.00e-07 ***
#> log(u)       0.0235992  0.0005768   40.91 1.36e-09 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for Gamma family taken to be 0.001813354)
#> 
#>     Null deviance: 3.118557  on 8  degrees of freedom
#> Residual deviance: 0.012672  on 7  degrees of freedom
#> AIC: 27.032
#> 
#> Number of Fisher Scoring iterations: 3
anova(glm_5)
#> Analysis of Deviance Table
#> 
#> Model: Gamma, link: inverse
#> 
#> Response: lot2
#> 
#> Terms added sequentially (first to last)
#> 
#> 
#>        Df Deviance Resid. Df Resid. Dev
#> NULL                       8    3.11856
#> log(u)  1   3.1059         7    0.01267

## Aliased ("S"ingular) -> 1 NA coefficient
(fS <- glm(lot2 ~ log(u) + log(u^2), data = clotting, family = Gamma))
#> 
#> Call:  glm(formula = lot2 ~ log(u) + log(u^2), family = Gamma, data = clotting)
#> 
#> Coefficients:
#> (Intercept)       log(u)     log(u^2)  
#>    -0.02391      0.02360           NA  
#> 
#> Degrees of Freedom: 8 Total (i.e. Null);  7 Residual
#> Null Deviance:       3.119 
#> Residual Deviance: 0.01267   AIC: 27.03
tools::assertError(update(fS, singular.ok = FALSE), verbose = interactive())
## -> .. "singular fit encountered"
  • Nonlinear regression objects :
nls_1 <- nls(data = trees, volume ~ SSlogis(diameter, Asymptote, Inflexion, Echelle))
form(nls_1)
Paramètres de la régression non linéaire : volume ~ SSlogis(diameter, Asymptote, Inflexion, Echelle)
  Estimateur Erreur type Stat. t Valeur p
Asymptote 5 2,1 2,4 0,026
Inflexion 0,56 0,096 5,8 2,8e-06
Echelle 0,13 0,018 7,5 4e-08
form(nls_1, params_labels = c("Asym", "xmoyen", "scal")) # Rename the parameters
Paramètres de la régression non linéaire : volume ~ SSlogis(diameter, Asymptote, Inflexion, Echelle)
  Estimateur Erreur type Stat. t Valeur p
Asym 5 2,1 2,4 0,026
xmoyen 0,56 0,096 5,8 2,8e-06
scal 0,13 0,018 7,5 4e-08
form(summary(nls_1))
Résumé de la régression non linéaire : volume ~ SSlogis(diameter, Asymptote, Inflexion, Echelle)
Observations DDL Erreur type des résidus Itérations Tolérance finale
31 28 0,0943 3 5,12e-07
nls_2 <- nls(data = trees, volume ~ SSgompertz(diameter, Asymptote, B2, B3))
form(anova(nls_1, nls_2))
DDL rés. | Somme carrés rés. | DDL | Somme carrés | Stat. F | Valeur p | |

|:——–:|:—————–:|:—:|:————:|:———:|:———-:|::| | 28 | 0,25 | | | | | | | 28 | 0,25 | 0 | 0 | | | |

Table: Analyse de la variance - modèle 1: volume ~ SSlogis(diameter, Asymptote, Inflexion, Echelle), modèle 2: volume ~ SSgompertz(diameter, Asymptote, B2, B3)

Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ’ ’ 1

  • Lists :
form(list(a = 1:3, b = TRUE, c = LETTERS))
  • a: 1, 2 et 3
  • b: TRUE
  • c: A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y et Z
  • Objects ts :
form(nottem, caption = "températures à Notthingam")
températures à Notthingam
  Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1920 41 41 44 47 54 58 58 56 54 50 43 40
1921 44 40 45 47 54 59 66 60 57 54 40 43
1922 38 39 40 42 56 58 57 54 54 47 42 42
1923 42 40 43 46 49 53 64 60 54 49 36 38
1924 39 38 38 46 53 58 61 58 56 50 44 44
1925 40 40 41 45 54 59 64 61 53 50 38 36
1926 39 43 43 49 51 57 62 62 58 47 42 40
1927 39 38 45 47 52 55 60 60 55 50 42 35
1928 41 41 43 47 51 56 62 60 55 50 43 37
1929 35 31 41 44 53 57 62 60 60 49 43 42
1930 42 37 41 47 51 60 60 62 57 51 43 39
1931 37 38 38 46 54 58 61 58 54 47 46 41
1932 42 38 40 45 51 57 62 64 56 47 44 42
1933 36 39 44 49 54 61 66 65 60 50 42 36
1934 39 38 40 47 53 60 66 60 59 51 43 46
1935 40 43 44 47 50 60 65 64 57 49 44 36
1936 37 35 44 44 53 59 60 61 58 50 42 41
1937 41 41 38 47 54 59 61 62 56 51 41 37
1938 42 41 47 47 52 59 60 60 57 51 48 39
1939 39 41 42 48 52 58 61 62 58 47 47 38
  • Unknown object type (coerced to a list with a warning) :
form(structure(list(a = 1, b = 3:5), class = "new_oject"))
#> Warning in pander.default(x, ...): No pander.method for "new_oject", reverting
#> to default.
  • a: 1
  • b: 3, 4 et 5

Using kable()

Note: the kable() functions are not integrated yet into form() for the moment! The kable() function is standard in ‘knitr’. Moreover, the ‘kableExtra’ package allows for further configuration of the output.

#options( knitr.table.format = "markdown")
knitr::kable(trees, caption = "The `trees` dataset")
The trees dataset
diameter height volume
0.211 21.3 0.292
0.218 19.8 0.292
0.224 19.2 0.289
0.267 21.9 0.464
0.272 24.7 0.532
0.274 25.3 0.558
0.279 20.1 0.442
0.279 22.9 0.515
0.282 24.4 0.640
0.284 22.9 0.563
0.287 24.1 0.685
0.290 23.2 0.595
0.290 23.2 0.606
0.297 21.0 0.603
0.305 22.9 0.541
0.328 22.6 0.629
0.328 25.9 0.957
0.338 26.2 0.776
0.348 21.6 0.728
0.351 19.5 0.705
0.356 23.8 0.977
0.361 24.4 0.898
0.368 22.6 1.028
0.406 21.9 1.085
0.414 23.5 1.206
0.439 24.7 1.569
0.444 25.0 1.577
0.455 24.4 1.651
0.457 24.4 1.458
0.457 24.4 1.444
0.523 26.5 2.180
#kableExtra::xtable2kable(xtable::xtable(linear.1))
knitr::kable(broom::glance(lm_1)[, c(1, 3, 5, 8, 11)])
r.squared sigma p.value AIC df.residual
0.947516 0.1103626 0 -43.82811 28
knitr::kable(broom::tidy(lm_1), caption = "Tableau des coefficients")
Tableau des coefficients
term estimate std.error statistic p.value
(Intercept) -1.6356345 0.2446222 -6.686369 0.0000003
diameter 5.2564322 0.2959381 17.761931 0.0000000
height 0.0311215 0.0120890 2.574370 0.0156218

Using stargazer

Not good! But we may considering to fix these…

#stargazer::stargazer(trees, type = "text")
#stargazer::stargazer(trees, type = "latex")
#stargazer::stargazer(lm_1, type = "text")