Lorsqu’on rédige un rapport d’analyse statistique, ou une publication, pour communiquer les résultats d’une étude réalisée dans le domaine de la santé, on commence généralement par décrire dans un tableau les groupes de patients étudiés, en termes de variables démographiques (age, sexe, etc), et liées à la pathologie (paramètres sanguins, etc ) à baseline (avant la prise du ou des traitements). Cette table contient aussi généralement (mais pas toujours) des informations sur le test statistique employé pour comparer les deux groupes, et la p-value obtenue.
C’est ce tableau descriptif que l’on appelle la table 1.
Par exemple, dans un essai randomisé contrôlé, qui vise à comparer un groupe de patients recevant le traitement évalué, et un groupe de patients recevant un placebo, la table 1 va permettre de vérifier que la randomisation a bien fonctionné, et que les groupes sont donc comparables.
Voici un exemple d’une table 1 d’un essai de phase III :
Cette table 1 est issue de la publication :Fenaux, P., Platzbecker, U., Mufti, G. J., Garcia-Manero, G., Buckstein, R., Santini, V., … & Sekeres, M. A. (2020).. New England Journal of Medicine, 382(2), 140-151. Elle est consultable en cliquant ici.
On trouve également des tables 1 dans les publications relatives à des travaux de construction de scores prédictifs. Dans cette situation, la table 1 permet de décrire les liaisons uni-variées entre les variables d’intérêt et l’événement étudié. Les variables possédant une liaison significatives sont ensuite généralement incluses dans un modèle de régression multivariée.
En voici un exemple :
Cette table est issue de la publication Al-Hasan, M. N., Lahr, B. D., Eckel-Passow, J. E., & Baddour, L. M. (2013). Predictive scoring model of mortality in Gram-negative bloodstream infection. Clinical Microbiology and Infection, 19(10), 948-954. Elle est consultable ici.
Comment souvent avec le logiciel R, il existe plusieurs solutions pour réaliser une table 1. Ces diverses solutions passent notamment par les packages Table1
, table1
, ou encore tableone
, et sans doute d’autres encore.
A ce jour, je me suis uniquement intéressée au package Table1
, c’est donc celui que je vais utiliser ici.
Pour illustrer cette création de table 1, je vais employer le jeu de données heart_disease
(que je vais renommer HD) ; il est inclus dans le package funModelling
. Ce jeu de données concerne un essai clinique dans le domaine de la cardiologie.
library(funModeling)
HD <- heart_disease
str(HD)
## 'data.frame': 303 obs. of 16 variables:
## $ age : int 63 67 67 37 41 56 62 57 63 53 ...
## $ gender : Factor w/ 2 levels "female","male": 2 2 2 2 1 2 1 1 2 2 ...
## $ chest_pain : Factor w/ 4 levels "1","2","3","4": 1 4 4 3 2 2 4 4 4 4 ...
## $ resting_blood_pressure: int 145 160 120 130 130 120 140 120 130 140 ...
## $ serum_cholestoral : int 233 286 229 250 204 236 268 354 254 203 ...
## $ fasting_blood_sugar : Factor w/ 2 levels "0","1": 2 1 1 1 1 1 1 1 1 2 ...
## $ resting_electro : Factor w/ 3 levels "0","1","2": 3 3 3 1 3 1 3 1 3 3 ...
## $ max_heart_rate : int 150 108 129 187 172 178 160 163 147 155 ...
## $ exer_angina : int 0 1 1 0 0 0 0 1 0 1 ...
## $ oldpeak : num 2.3 1.5 2.6 3.5 1.4 0.8 3.6 0.6 1.4 3.1 ...
## $ slope : int 3 2 2 3 1 1 3 1 2 3 ...
## $ num_vessels_flour : int 0 3 2 0 0 0 2 0 1 0 ...
## $ thal : Factor w/ 3 levels "3","6","7": 2 1 3 1 1 1 1 1 3 3 ...
## $ heart_disease_severity: int 0 2 1 0 0 0 3 0 2 1 ...
## $ exter_angina : Factor w/ 2 levels "0","1": 1 2 2 1 1 1 1 2 1 2 ...
## $ has_heart_disease : Factor w/ 2 levels "no","yes": 1 2 2 1 1 1 2 1 2 2 ...
La variable spécifiant les deux groupes de patients pour lesquels je souhaite décrire les variables à baseline est la variable has_heart_disease
qui a deux niveaux no
et yes
.
Les variables démographiques et liées à l apathologie, que nous allons décrire sont :
gender
(male / female)age
chest_pain
(4 niveaux, 1 à 4)serum_cholestoral
max_heart_rate
A ce jour, le package Table1
n’est pas disponible sur CRAN. Pour le télécharger, nous devons le récupérer à partir du GitHub d’Erica Wozniak, son développeur. Pour cela, il est nécessaire d’employer les commandes suivantes :
# installation de devtools depuis CRAN
install.packages("devtools"
library(devtools)
# installation de Table1 depuis github
install_github("emwozniak/Table1")
library(Table1)
Remarque : sous Mac, il est, semble-t-il, nécessaire de charger le package remotes
pour insatller un package depuis github.
La principale fonction de ce package est la fonction make.table()
.
Cette fonction comporte de nombreux arguments. Les principaux sont :
Par exemple :
make.table(dat=HD,
strat="has_heart_disease",
cat.varlist=c("gender","chest_pain"),
cont.varlist=c("age","serum_cholestoral", "max_heart_rate"))
## Variable no yes
## age
## Count 164 139
## Mean (SD) 52.59 (9.51) 56.63 (7.94)
## Median (IQR) 52.00 (14.25) 58.00 (10.00)
## Q1, Q3 44.75, 59.00 52.00, 62.00
## Min, Max 29.00, 76.00 35.00, 77.00
## Missing 0 0
##
## gender
## Count (%) 164 (54.13%) 139 (45.87%)
## (Row %)(Col %)
## female 72 (74.23%) (43.90%) 25 (25.77%) (17.99%)
## male 92 (44.66%) (56.10%) 114 (55.34%) (82.01%)
## Missing 0 0
##
## chest_pain
## Count (%) 164 (54.13%) 139 (45.87%)
## (Row %)(Col %)
## 1 16 (69.57%) ( 9.76%) 7 (30.43%) ( 5.04%)
## 2 41 (82.00%) (25.00%) 9 (18.00%) ( 6.47%)
## 3 68 (79.07%) (41.46%) 18 (20.93%) (12.95%)
## 4 39 (27.08%) (23.78%) 105 (72.92%) (75.54%)
## Missing 0 0
##
## serum_cholestoral
## Count 164 139
## Mean (SD) 242.64 (53.46) 251.47 (49.49)
## Median (IQR) 234.50 (58.50) 249.00 (66.00)
## Q1, Q3 208.75, 267.25 217.50, 283.50
## Min, Max 126.00, 564.00 131.00, 409.00
## Missing 0 0
##
## max_heart_rate
## Count 164 139
## Mean (SD) 158.38 (19.20) 139.26 (22.59)
## Median (IQR) 161.00 (23.25) 142.00 (31.50)
## Q1, Q3 148.75, 172.00 125.00, 156.50
## Min, Max 96.00, 202.00 71.00, 195.00
## Missing 0 0
##
## Overall
##
## 303
## 54.44 (9.04)
## 56.00 (13.00)
## 48.00, 61.00
## 29.00, 77.00
## 0
##
##
## 303
##
## 97 (100.00%) (32.01%)
## 206 (100.00%) (67.99%)
## 0
##
##
## 303
##
## 23 (100.00%) ( 7.59%)
## 50 (100.00%) (16.50%)
## 86 (100.00%) (28.38%)
## 144 (100.00%) (47.52%)
## 0
##
##
## 303
## 246.69 (51.78)
## 241.00 (64.00)
## 211.00, 275.00
## 126.00, 564.00
## 0
##
##
## 303
## 149.61 (22.88)
## 153.00 (32.50)
## 133.50, 166.00
## 71.00, 202.00
## 0
##
Des arguments supplémentaires permettent de réaliser les tests statistiques de comparaison et d’indiquer leur p-value dans la table 1. Il s’agit de :
Une liste des tests utilisables est fournie dans l’aide de la fonction stat.col
?stat.col
Ainsi, par exemple, si je souhaite faire :
chest_pain
.gender
.age
.serum_cholestoral
, max_heart_rate
.je vais utiliser les commandes suivantes :
make.table(dat=HD,
strat="has_heart_disease",
colnames=c("", "Without heart disease","With heart disease", "Overall", "Test Statistic"),
cat.varlist=c("gender","chest_pain"),
cat.header=c("Gender", "Chest pain"),
cat.rownames = list(c("Female", "Male"),c("I", "II", "III", "IV")) ,
cat.ptype = c("fisher", "chisq"),
cont.varlist=c("age","serum_cholestoral", "max_heart_rate"),
cont.header=c("Age","Serum cholestoral (mg/dl)", "Max heart rate (bpm)"),
cont.ptype = c("ttest", "wilcox","wilcox")
## Without heart disease With heart disease
## Age
## Count 164 139
## Mean (SD) 52.59 (9.51) 56.63 (7.94)
## Median (IQR) 52.00 (14.25) 58.00 (10.00)
## Q1, Q3 44.75, 59.00 52.00, 62.00
## Min, Max 29.00, 76.00 35.00, 77.00
## Missing 0 0
##
## Gender
## Count (%) 164 (54.13%) 139 (45.87%)
## (Row %)(Col %)
## Female 72 (74.23%) (43.90%) 25 (25.77%) (17.99%)
## Male 92 (44.66%) (56.10%) 114 (55.34%) (82.01%)
## Missing 0 0
##
## Chest pain
## Count (%) 164 (54.13%) 139 (45.87%)
## (Row %)(Col %)
## I 16 (69.57%) ( 9.76%) 7 (30.43%) ( 5.04%)
## II 41 (82.00%) (25.00%) 9 (18.00%) ( 6.47%)
## III 68 (79.07%) (41.46%) 18 (20.93%) (12.95%)
## IV 39 (27.08%) (23.78%) 105 (72.92%) (75.54%)
## Missing 0 0
##
## Serum cholestoral (mg/dl)
## Count 164 139
## Mean (SD) 242.64 (53.46) 251.47 (49.49)
## Median (IQR) 234.50 (58.50) 249.00 (66.00)
## Q1, Q3 208.75, 267.25 217.50, 283.50
## Min, Max 126.00, 564.00 131.00, 409.00
## Missing 0 0
##
## Max heart rate (bpm)
## Count 164 139
## Mean (SD) 158.38 (19.20) 139.26 (22.59)
## Median (IQR) 161.00 (23.25) 142.00 (31.50)
## Q1, Q3 148.75, 172.00 125.00, 156.50
## Min, Max 96.00, 202.00 71.00, 195.00
## Missing 0 0
##
## Overall Test Statistic
## <0.001
## 303 t-test
## 54.44 (9.04)
## 56.00 (13.00)
## 48.00, 61.00
## 29.00, 77.00
## 0
##
## <0.001
## 303 Fisher exact
##
## 97 (100.00%) (32.01%)
## 206 (100.00%) (67.99%)
## 0
##
## <0.001
## 303 Chi-square
##
## 23 (100.00%) ( 7.59%)
## 50 (100.00%) (16.50%)
## 86 (100.00%) (28.38%)
## 144 (100.00%) (47.52%)
## 0
##
## 0.035
## 303 Wilcoxon rank-sum
## 246.69 (51.78)
## 241.00 (64.00)
## 211.00, 275.00
## 126.00, 564.00
## 0
##
## <0.001
## 303 Wilcoxon rank-sum
## 149.61 (22.88)
## 153.00 (32.50)
## 133.50, 166.00
## 71.00, 202.00
## 0
##
La table 1 créée peut facilement être exportée en format.csv.
Pour cela, il est nécessaire de :
# creation d'un dossier "resultats" à la racine du projet R
dir.create("resultats")
# stockage de la table dans l'objet mytable1
mytable1 <- make.table(dat=HD,
strat="has_heart_disease",
cat.varlist=c("gender","chest_pain"),
cont.varlist=c("age","serum_cholestoral", "max_heart_rate"))
# supression du nom de lignes
rownames(mytable1 )<-NULL
# exportation
write.csv2(mytable1, here::here("resultats", "ma_table1.csv"), row.names=FALSE)
kable()
# stockage de la table dans l'objet mytable1
mytable1 <- make.table(dat=HD,
strat="has_heart_disease",
colnames=c("", "Without heart disease","With heart disease", "Overall", "Test Statistic"),
cat.varlist=c("gender","chest_pain"),
cat.header=c("Gender", "Chest pain"),
cat.rownames = list(c("Female", "Male"),c("I", "II", "III", "IV")) ,
cat.ptype = c("chisq", "chisq"),
cont.varlist=c("age","serum_cholestoral", "max_heart_rate"),
cont.header=c("Age","Serum cholestoral (mg/dl)", "Max heart rate (bpm)"),
cont.ptype = c("wilcox", "wilcox","wilcox"))
# supression du nom de lignes
row.names(mytable1) <- NULL
# generation de la table
library(knitr)
kable(mytable1)
Voici un aperçu du rendu word obtenu :
Pour aller plus loin, vous pouvez consulter un tutoriel de Table1
rédigé par Erica Wozniak, en cliquant ici.
Si jamais ce package Table1
ne vous donne pas entière satisfaction, vous pouvez également consulter la vignette du package table1
(sans la majuscule), en cliquant ici.
Je vous recommande aussi cette discussion sur l’intérêt (ou pas) d’afficher les p-values dans la table 1 relative à un essai randomisé.
Allez zou…maintenant que vous avez le mode d’emploi, vous n’avez plus d’excuses pour ne pas produire de belles Tables 1 !
Si cet article vous a plu, ou vous a été utile, et si vous le souhaitez, vous pouvez soutenir ce blog en faisant un don sur sa page Tipeee
Enregistrez vous pour recevoir gratuitement mes fiches “aide mémoire” (ou cheat sheets) qui vous permettront de réaliser facilement les principales analyses biostatistiques avec le logiciel R et pour être informés des mises à jour du site.
25 Responses
Je n’ai pas trouvé le package Table1 sur le CRAN ?
Moi non plus ! C’est pour cela qu’il faut le télécharger sur son repo Github.
Et merci pour le lien : http://docteur-michel.fr/latexr/, je vais aller regarder ça.
bonjour,
merci pour ce sujet original mais très utilisé (dans les articles médicaux en particulier).
Une petite remarque à propos des data utilisées: il y a une coquille dans serum_cholestoral au lieu de serum_cholesterol. Pour pouvoir réaliser l’exemple présenté, il faut faire une faute d’orthographe ! Heureusement il y a colnames. Je renvoie les personnes intéressées aux relations cholestérol – infarctus…
Cela dit ce post est excellent (comme souvent)
JcB
Oups ! Désolé, j’avais lu en diagonale.
Merci Claire pour cet article très intéressant. On pourra l’utiliser aisément de Rmarkdown.
Merci Claire, pour cet article très utile qui m’a permis de faire un beau tableau que je vais mettre dans l’article que je suis en train d’écrire!
Bonjour Nathalie,
Ravie de vous avoir été utile !
Super post, merci.
Est-il possible de ne pas avoir de test stat pour une variable?
Bonjour,
je ne sais pas, il faut essayer, en mettant NA peut être.
Bonne continuation
Je suis très ravi Claire Della Vedova de vous relire. Je me demandais à quoi a été du ce silence.
Je vous félicite et vous encourage beaucoup pour cette dévotion de nous informer et nous former.
Don Folly Fofolo
Merci pour cet article
Bonjour,
votre artcile est excellent et vraiment très utile; j’aimerais tant pouvoir à utiliser ce package mais j’ai des difficultés à installer Table1 depuis git_hub:
install_github(“emwozniak/Table1”)
message affiché :
Installing 1 packages: rlang
trying URL ‘https://cran.rstudio.com/bin/windows/contrib/4.0/rlang_0.4.7.zip’
Content type ‘application/zip’ length 1137690 bytes (1.1 MB)
downloaded 1.1 MB
Que faire après téléchargement du dossier zip.?
merci par avance
C’est bon je suis parvenue à installe Table1 en installant RTtools40 puis en mettant à jour les packages.
Bonjour Cécile,
merci pour votre retour !
Bonjour Claire,
J’ai des difficultés avec kable(myTable1)
Le tableau d’affiche bien dans le fichier Rmd et dans la console mais impossible de l’afficher dans word.
Message d’erreur indiquant que l’objet (myTable1) n’existe pas.
J’ai essayé avec pander et j’ai le même problème.
Peut-être avez vous une solution??
merci par avance,
et encore mille fois merci pour vos articles que j’apprécie énormément et qui m’aide beaucoup
Super.
J’ai aimé.
Bravo à toi Claire.
Bonjour Claire
Est-il possible de paramétrer des value à 4 décimales avec Table1?
Bonjour Cécile,
j’ai essayé avec l’argument dec=4, mais cela ne fonctionne pas sur la pvalue.
make.table(dat=heart_disease,
strat=”has_heart_disease”,
cat.varlist=c(“gender”,”chest_pain”),
cont.varlist=c(“age”,”serum_cholestoral”, “max_heart_rate”),
cat.ptype = c(“chisq”, “chisq”),
cont.ptype = c(“wilcox”, “ttest”,”ttest”),
dec=4)
Je vous suggère de demander au développeur du package, sur le repo GitHub : https://github.com/emwozniak/Table1/issues
Bonne continuation
bonjour,
mes tables1 sont trop larges et dans word et Rstudio la dernière colonne se trouve en dessous du tableau.
mon tableau n’est pas d’un seul bloc, ne tient pas sur la largeur de page.
comment résourdre ce problème ?
merci de votre aide
Bonjour Bruno,
Cela est sans doute possible de régler la largeur des colonnes avec du code css, mais personnellement je ne sais pas le faire.
A part vous proposer de rectifier les largeurs sous Word, je n’ai pas d’autres solutions.
Bonne continuation
Bonjour la communauté,
Un autre package intéressant dans ce même objectif me semble être le package Boot (https://cran.r-project.org/web/packages/table1/vignettes/table1-examples.html).
L’avantage a mon avis est qu’il est plus user-friendly (avantage subjectif c’est vrai), l’inconvénient est qu’il est purement descriptif.
Je vous laisse vous faire votre idée 🙂
Bonne journée
Bonjour Claire,
merci pour vos articles cela m’aide énormément.
J’aurais besoin de votre aide je n’arrive pas à insérer une table avec kable (myTable1) comme “Cazes” :
” le tableau d’affiche bien dans le fichier Rmd et dans la console mais impossible de l’afficher dans word.
Message d’erreur indiquant que l’objet (myTable1) n’existe pas.”
Comme je débute je ne sais vraiment pas quoi faire.
Ma table est une data. frame et non un objet car elle vient de make.table.
Merci pour votre aide
Julie
Bonjour Julie,
je viens de rester le code, et de mon côté tout fonctionne.
Je vais vous envoyer mon .Rmd et vous me direz si celui-ci fonctionne ou pas de votre côté.
Bonjour Claire,
Je n’avais pas téléchargé le package funmodeling
Et je n’avais pas compris qu’il fallait charger le fichier de données sur le script rmd.
Merci énormément.
Bonne journée
Bjr très interessant le table1. Il facile l’analyse. Super!!