Setup

Global options

The first step is the setup chunk, to configure the global options of your Rmarkdown document. Here we will chose to display the code in the ouput unless sepcified otherwise.

# The next function specify that every chunk will have the echo parameter to TRUE and the code will be displayed in the html
knitr::opts_chunk$set(echo = TRUE)
options(knitr.duplicate.label = "allow")

Note : chunks are named (either explicity or with a number). This is useful for debugging (as the error message will indicate which chunk is problematic). But Rmarkdown doesn’t allow to have several chunks with the same name. In the setup chunk you can specify options(knitr.duplicate.label = "allow") to allow this.

You could have specified {r setup, include = FALSE, message = FALSE} to remove the chunk from the rendering and to ignore all messages. (This is often done for the setup chunk.)

Packages

The second step is to load the libraries you will use throughout the R Markdown report.

When writing an R Markdown document, you may use different packages at different stages.
A good practice is to load all of them at the beginning of the report.

library(tidyverse)
# You may install the DT package if not in your library
# install.packages("DT")
# You shouldn't install packages directly in an R Markdown report, as it may cause errors when knitting the document. Instead, install the packages in your R console, and simply add a comment in your report to indicate which packages need to be installed.
library(DT)

In the library chunk, you can specify message = FALSE to suppress messages, as library messages often include advice on how to cite the package in publications or describe the package’s purpose.

Data import

The new step is to import your data. In this example, my data are located in the folder Data.

You should adjust the file path according to the location of your data to ensure the report can be knitted successfully.

Cancer_Data = read.csv("../Data/cancer_data/Cancer_Data.csv", stringsAsFactors = TRUE, row.names = 1)

Make sure that every object called in a Rmd has been created beforehand in the same Rmd.

Data inspection and modification

Dealing with missing values

In order to use the dataset for plotting, you first need to inspect it and remove any missing values. To perform this, you can use the function summary().

summary(Cancer_Data)
##        ID            diagnosis      radius          texture     
##  Min.   :     8670   B   :339   Min.   : 6.981   Min.   : 9.71  
##  1st Qu.:   869218   M   :203   1st Qu.:11.700   1st Qu.:16.21  
##  Median :   906024   NA's: 27   Median :13.370   Median :18.88  
##  Mean   : 30371831              Mean   :14.127   Mean   :19.35  
##  3rd Qu.:  8813129              3rd Qu.:15.780   3rd Qu.:21.80  
##  Max.   :911320502              Max.   :28.110   Max.   :39.28  
##                                                  NA's   :33     
##    perimeter        smoothness        concavity       concave.points   
##  Min.   : 43.79   Min.   :0.05263   Min.   :0.00000   Min.   :0.00000  
##  1st Qu.: 75.17   1st Qu.:0.08637   1st Qu.:0.02956   1st Qu.:0.02033  
##  Median : 86.24   Median :0.09587   Median :0.06154   Median :0.03350  
##  Mean   : 91.97   Mean   :0.09636   Mean   :0.08880   Mean   :0.04912  
##  3rd Qu.:104.10   3rd Qu.:0.10530   3rd Qu.:0.13070   3rd Qu.:0.07409  
##  Max.   :188.50   Max.   :0.16340   Max.   :0.42680   Max.   :0.20120  
##                                                       NA's   :22

You can observe several missing values, which we will remove using na.omit().

We will also create a smaller dataset containing only the relevant columns, which will be easier to work with. Specifically, we will use the solution from Question 3 of TP6 to generate a reduced dataset and then perform some basic analysis.

This new dataset, called Cancer_Data_subset, will exclude missing values and include only the columns we are interested in.

Creating a copy of the dataset allows us to retain the original Cancer_Data in case we need to use it later.

Cancer_Data_subset = Cancer_Data %>%
  na.omit() %>%
  dplyr::select(
    diagnosis,
    radius,
    texture,
    perimeter,
    smoothness,
    concavity
  )

Note : We used explicit function calls here to avoid potential issues. In fact, it is considered good practice to always call functions this way to prevent accidentally using a function from another package without realizing it.

Warning : In the previous code, you can see that filtering with na.omit() is performed before selecting columns. If the order was different, the number of rows might change. It’s up to you whether to apply filtering to the full dataset or only to a subset of selected columns, but be aware that this choice can lead to different results.

We can inspect the data again to visualize if there are missing values left.

summary(Cancer_Data_subset)
##  diagnosis     radius          texture        perimeter        smoothness     
##  B:310     Min.   : 6.981   Min.   : 9.71   Min.   : 43.79   Min.   :0.06251  
##  M:182     1st Qu.:11.688   1st Qu.:16.33   1st Qu.: 75.14   1st Qu.:0.08587  
##            Median :13.335   Median :18.82   Median : 86.14   Median :0.09589  
##            Mean   :14.082   Mean   :19.35   Mean   : 91.67   Mean   :0.09648  
##            3rd Qu.:15.750   3rd Qu.:21.79   3rd Qu.:103.62   3rd Qu.:0.10540  
##            Max.   :28.110   Max.   :39.28   Max.   :188.50   Max.   :0.16340  
##    concavity      
##  Min.   :0.00000  
##  1st Qu.:0.02964  
##  Median :0.05994  
##  Mean   :0.08704  
##  3rd Qu.:0.12662  
##  Max.   :0.42680

Display table of the dataset

Basic display

We can see the head of the dataset below.

# Using the head() function on our data :
head(Cancer_Data_subset)
##          diagnosis radius texture perimeter smoothness concavity
## 842302           M  17.99   10.38    122.80    0.11840    0.3001
## 842517           M  20.57   17.77    132.90    0.08474    0.0869
## 84348301         M  11.42   20.38     77.58    0.14250    0.2414
## 84358402         M  20.29   14.34    135.10    0.10030    0.1980
## 843786           M  12.45   15.70     82.57    0.12780    0.1578
## 844359           M  18.25   19.98    119.60    0.09463    0.1127
# Or we could have used dplyr with
# Cancer_Data_subset %>%
#   head()

We can display it more properly with knitr::kable().

knitr::kable(head(Cancer_Data_subset))
diagnosis radius texture perimeter smoothness concavity
842302 M 17.99 10.38 122.80 0.11840 0.3001
842517 M 20.57 17.77 132.90 0.08474 0.0869
84348301 M 11.42 20.38 77.58 0.14250 0.2414
84358402 M 20.29 14.34 135.10 0.10030 0.1980
843786 M 12.45 15.70 82.57 0.12780 0.1578
844359 M 18.25 19.98 119.60 0.09463 0.1127

Datatable display

Here we use the function datatable from the DT package to show the table. Using this function allows many things, notably to sort and filter the table directly on the html report (sort by clicking on a column name and filter with the search cell of the html). This is not possible with some other basic functions (knitr::kable for example).

# The option `scrollX=TRUE` mean you can scroll in the x axis, if the table is too wide.
DT::datatable(data = Cancer_Data_subset,  
              rownames = TRUE,         # This displays the rownames of our data
              options = list(          # datatable function has many other options as well
                  scrollX = TRUE,      # This enables to scroll horizontally if there is too many columns
                  pageLength=5         # This controls the number of lines displayed
              ))        

Data visualisation

Now we will use the package ggplot2 in order to make plots. We will recreate the plots from TP6.

Histogram

The first step was to create an histogram showing the distribution of the radius of the tumors.

You can use ggplot:Esquisse to have the main code. But Esquisse has its limits and you can’t add vertical lines for example. To do it, you can copy and paste the code from Esquisse then add + geom_vline() to add this vertical line.
Note that you can’t use Esquisse when generating a Rmarkdown report. If you want to use it, you necessarily have to retrieve the code from Esquisse and copy it in a chunk, and then render your document.

ggplot(Cancer_Data_subset) +
  aes(x = radius) +
  geom_histogram(bins = 30L, fill = "#91ABDA") +
  theme_minimal() +
  geom_vline(xintercept = mean(Cancer_Data_subset$radius),
             colour = "red")

Boxplot

You can also create a boxplot showing the radius depending of the type diagnosis of the tumors.

ggplot(Cancer_Data_subset) +
  aes(x = diagnosis, y = radius, fill = diagnosis) +
  geom_boxplot() +
  scale_fill_viridis_d(option = "viridis", direction = 1) +
  labs(
    x = "Diagnosis",
    y = "Radius",
    title = "Radius by diagnosis",
    fill = "Diagnosis"
  ) +
  theme_minimal() +
  theme(legend.position = "none") # This argument is used to remove the legend

Scatterplot

Here we display the scatterplot of the perimeter according to concavity colored by diagnosis.

ggplot(Cancer_Data_subset) +
  aes(x = perimeter, y = concavity, colour = diagnosis) +
  geom_point(size = 2.3, shape = "triangle") +
  scale_color_manual(
    values = c(
      B = "#6D7BF8",
      M = "#E85C3C"
    )
  ) +
  labs(x = "Perimeter",
       y = "Concavity",
       title = "Perimeter according to Concavity, colored by diagnosis",
       fill = "Region") +
  theme_minimal()
## Ignoring unknown labels:
## • fill : "Region"

Session info

It is useful to show the session info at the end of the report to indicate which packages have been used and their versions.

sessionInfo()
## R version 4.4.2 (2024-10-31)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.1 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0 
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0
## 
## locale:
##  [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=fr_FR.UTF-8    
##  [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
##  [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Europe/Paris
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] DT_0.34.0       lubridate_1.9.4 forcats_1.0.1   stringr_1.6.0  
##  [5] dplyr_1.1.4     purrr_1.2.1     readr_2.1.6     tidyr_1.3.2    
##  [9] tibble_3.3.1    ggplot2_4.0.1   tidyverse_2.0.0
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       jsonlite_2.0.0     compiler_4.4.2     tidyselect_1.2.1  
##  [5] dichromat_2.0-0.1  jquerylib_0.1.4    scales_1.4.0       yaml_2.3.12       
##  [9] fastmap_1.2.0      R6_2.6.1           labeling_0.4.3     generics_0.1.4    
## [13] knitr_1.51         htmlwidgets_1.6.4  tzdb_0.5.0         bslib_0.9.0       
## [17] pillar_1.11.1      RColorBrewer_1.1-3 rlang_1.1.7        stringi_1.8.7     
## [21] cachem_1.1.0       xfun_0.55          sass_0.4.10        S7_0.2.1          
## [25] otel_0.2.0         viridisLite_0.4.2  timechange_0.3.0   cli_3.6.5         
## [29] withr_3.0.2        magrittr_2.0.4     crosstalk_1.2.2    digest_0.6.39     
## [33] grid_4.4.2         rstudioapi_0.17.1  hms_1.1.4          lifecycle_1.0.5   
## [37] vctrs_0.6.5        evaluate_1.0.5     glue_1.8.0         farver_2.1.2      
## [41] rmarkdown_2.30     tools_4.4.2        pkgconfig_2.0.3    htmltools_0.5.9