C'est de la data et mon expeRtise afin d'en tirer le maximum

Créer une table 1 avec R

C’est quoi une table 1 ?

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 : 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 :

Table 1 d'une étude prédictive

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.

 

Faire une table 1 avec R

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.

Les data

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 noet 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`

Le package Table1

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 Fonction make.table et ses principaux arguments

La principale fonction de ce package est la fonction make.table(). Cette fonction comporte de nombreux arguments. Les principaux sont :
  •  `strat` : qui permet d’indiquer la variable qui identifie les groupes de patients, ici `has_heart_disease`.
  • `cat.varlist` : qui permet de lister les variables démographiques catégorielles que l’on souhaite inclure dans la Table.
  • `cont.varlist` : qui permet de lister les variables numériques continues.
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
##
 

Amélioration de la Table 1

D’autres arguments peuvent être employés pour améliorer cette table, notamment :
  • `colnames` pour renommer les colonnes.
  • `cat.header` pour renommer les variables catégorielles.
  • `cat.rownames` pour renommer les niveaux des variables catégorielles.
  • `cont.header` pour renommer les variables numériques continues.
Par exemple :
make.table(dat=HD,
strat="has_heart_disease",
colnames=c("", "Without heart disease","With heart disease", "Overall"),

cat.varlist=c("gender","chest_pain"),
cat.header=c("Gender", "Chest pain"),
cat.rownames = list(c("Female", "Male"),c("I", "II", "III", "IV")) ,

cont.varlist=c("age","serum_cholestoral", "max_heart_rate"),
cont.header=c("Age","Serum cholestoral (mg/dl)", "Max heart rate (bpm)"))
##                                  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
##                        
##                     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
##
   

Ajouter les tests statistiques

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 :
  •  `cat.ptype` qui permet de préciser les tests qui doivent être employés pour les variables catégorielles.
  • `cont.ptype` qui permet de faire de même avec les variables numériques continues.
Une liste des tests utilisables est fournie dans l’aide de la fonction stat.col
?stat.col
    Ainsi, par exemple, si je souhaite faire :
  • un test du Chi2 pour la variable `chest_pain`.
  • un test exact de Fisher pour la variable `gender`.
  • un test t de Student pour la variable `age`.
  • un test de Wilcoxon pour les variables `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                  
##
 

Exporter la table

Une première solution (qui ne me plaît pas tellement) consiste à éditer la sortie de la fonction make.table dans un fichier texte, en utilisant la fonction sink(), comme ceci :  
sink("Table1.txt")
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"))
  Cela va créer, à la racine de votre dossier de travail (ou projetR) un fichier texte nommé Table1.txt contenant les sortie de la fonction make.table. L’autre solution (que je préfère largement), consiste à :
  1. utiliser un script en R markdown,
  2. stocker la table dans un objet
  3. supprimer le nom des lignes de cet objet table, car sinon ils apparaîtront en trop
  4. générer avec la fonction kable()
  5. kniter le document en format word pour récupérer la table
# 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 : rendu d'une table 1 obtenu via r markdown

Pour aller plus loin

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 🙏 👉 Cliquez ici pour soutenir le blog Statistiques et Logiciel R  

Poursuivez votre lecture : 

14 réponses

  1. 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

  2. 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!

  3. 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

  4. 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

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Bonjour !

vous venez souvent ?

Identifiez-vous pour avoir accès à toutes les fontionnalités !

Aide mémoire off'R ;)

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.