diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9ef036b4075cee7445550a28527290ae9faf2a82..4fd9ceb23f4b9876d0292cca26dd087016a80fab 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,3 +24,4 @@ pages:
       - public
   rules:
     - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
diff --git a/_extensions/callout-exercise/_extension.yml b/_extensions/callout-exercise/_extension.yml
new file mode 100644
index 0000000000000000000000000000000000000000..271461555d9dbf03071710a4a57a7cfdf90cdaa1
--- /dev/null
+++ b/_extensions/callout-exercise/_extension.yml
@@ -0,0 +1,7 @@
+title: Callout exercise
+author: Christophe Dervieux (https://github.com/quarto-dev/quarto-cli/issues/844#issuecomment-1653419884)
+version: 1.0.0
+quarto-required: ">=1.3.0"
+contributes:
+  filters:
+    - callout-exercise.lua
diff --git a/_extensions/callout-exercise/callout-exercise.lua b/_extensions/callout-exercise/callout-exercise.lua
new file mode 100644
index 0000000000000000000000000000000000000000..827ca3676809f3590335cbf40472555f38a5c8e9
--- /dev/null
+++ b/_extensions/callout-exercise/callout-exercise.lua
@@ -0,0 +1,19 @@
+function Div(div)
+  -- process exercise
+  if div.classes:includes("callout-exercise") then
+    -- default title
+    local title = "Exercise"
+    -- Use first element of div as title if this is a header
+    if div.content[1] ~= nil and div.content[1].t == "Header" then
+      title = pandoc.utils.stringify(div.content[1])
+      div.content:remove(1)
+    end
+    -- return a callout instead of the Div
+    return quarto.Callout({
+      type = "exercise",
+      content = div,
+      title = title,
+      collapse = nil
+    })
+  end
+end
diff --git a/_extensions/callout-exercise/exercise-icon.svg b/_extensions/callout-exercise/exercise-icon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3db1adab5fbf2eb8ff6f67dd23985d48e390e45e
--- /dev/null
+++ b/_extensions/callout-exercise/exercise-icon.svg
@@ -0,0 +1 @@
+<svg width="15" height="15" xmlns="http://www.w3.org/2000/svg"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="m14.587 4.16-1.35 1.35a.353.353 0 0 1-.5 0L9.485 2.258a.353.353 0 0 1 0-.496L10.838.41a1.41 1.41 0 0 1 1.99 0l1.759 1.76a1.404 1.404 0 0 1 0 1.99zM8.325 2.923.63 10.616l-.62 3.56a.705.705 0 0 0 .815.814l3.558-.626 7.696-7.692a.353.353 0 0 0 0-.498L8.828 2.922a.356.356 0 0 0-.501 0zM3.633 9.955a.408.408 0 0 1 0-.58l4.514-4.51a.408.408 0 0 1 .579 0 .408.408 0 0 1 0 .578l-4.51 4.512a.408.408 0 0 1-.581 0zM2.577 12.42h1.407v1.064l-1.89.33-.912-.91.331-1.89h1.065z" style="fill:#ac00e6;fill-opacity:1;stroke-width:.0293002"/></svg>
\ No newline at end of file
diff --git a/_quarto.yml b/_quarto.yml
index 6f4cf181e201b5322f5a1c26c714aedb861d4f6b..918af3fb7948cef6529e2a77801bbeb48ab2df95 100644
--- a/_quarto.yml
+++ b/_quarto.yml
@@ -40,10 +40,8 @@ book:
 format:
   html:
     theme:
-      light: flatly
-      dark: darkly
+      light: [flatly, _sass/main-light.scss]
+      dark: [darkly, _sass/main-dark.scss]
 
-execute: 
+execute:
   cache: true
-
-  
\ No newline at end of file
diff --git a/_sass/common/_callout.scss b/_sass/common/_callout.scss
new file mode 100644
index 0000000000000000000000000000000000000000..168176aaf8d071a3e5d530af885b048a7113ae42
--- /dev/null
+++ b/_sass/common/_callout.scss
@@ -0,0 +1,19 @@
+/* ==========================================================================
+   Callout
+   ========================================================================== */
+
+div.callout:not(.no-icon), div.callout {
+   margin-left: 0px !important;
+   
+}
+
+.callout.callout-style-simple .callout-body,.callout.callout-style-default .callout-body,
+.callout.callout-style-minimal .callout-body{ 
+   font-size: 1rem !important;
+}
+
+.callout.callout-style-default > div.callout-header,
+.callout.callout-style-simple > div.callout-header,
+.callout.callout-style-minimal > div.callout-header { 
+   font-size: 1.1rem !important;
+}
diff --git a/_sass/common/_fonts.scss b/_sass/common/_fonts.scss
new file mode 100644
index 0000000000000000000000000000000000000000..80f2b4d34b04ff792e22101576bbd662f83c1b76
--- /dev/null
+++ b/_sass/common/_fonts.scss
@@ -0,0 +1,3 @@
+// Path
+$font-path: "_sass/common/fonts" !default;
+@import "fonts/raleway";
diff --git a/_sass/common/_variables.scss b/_sass/common/_variables.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f5dddf3117e125f15c10569a235346d07a92c41b
--- /dev/null
+++ b/_sass/common/_variables.scss
@@ -0,0 +1,11 @@
+/* ==========================================================================
+   Variables
+   
+   See:
+   https://quarto.org/docs/output-formats/html-themes.html#sass-variables
+   https://getbootstrap.com/docs/5.0/layout/grid/#variables
+   ========================================================================== */
+
+// font families
+$font-family-sans-serif:      "Raleway" !default;
+$headings-font-family:        "Raleway" !default;
diff --git a/_sass/common/fonts/Raleway/Raleway-Bold.woff b/_sass/common/fonts/Raleway/Raleway-Bold.woff
new file mode 100644
index 0000000000000000000000000000000000000000..fa019bb740444579e79e91085348714a9ae3208c
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Bold.woff differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Bold.woff2 b/_sass/common/fonts/Raleway/Raleway-Bold.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..d510b22101b73a11c69dd5ea27a82517d3cb32ab
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Bold.woff2 differ
diff --git a/_sass/common/fonts/Raleway/Raleway-BoldItalic.woff b/_sass/common/fonts/Raleway/Raleway-BoldItalic.woff
new file mode 100644
index 0000000000000000000000000000000000000000..f8fda9528ced2913b0f96cde531fada7d405a2bd
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-BoldItalic.woff differ
diff --git a/_sass/common/fonts/Raleway/Raleway-BoldItalic.woff2 b/_sass/common/fonts/Raleway/Raleway-BoldItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..8125b4c53e123a0bc67c8015572020afb28f4c47
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-BoldItalic.woff2 differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Italic.woff b/_sass/common/fonts/Raleway/Raleway-Italic.woff
new file mode 100644
index 0000000000000000000000000000000000000000..0c7d4d8578d6dc0e62ea83f7a63a3624dbdf6010
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Italic.woff differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Italic.woff2 b/_sass/common/fonts/Raleway/Raleway-Italic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..ae34b62949088fa6539b53945b5202ab425c80ef
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Italic.woff2 differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Medium.woff b/_sass/common/fonts/Raleway/Raleway-Medium.woff
new file mode 100644
index 0000000000000000000000000000000000000000..802d177302023db071e9f8b9891fc3127155ca9d
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Medium.woff differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Medium.woff2 b/_sass/common/fonts/Raleway/Raleway-Medium.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..b03ca0524345a41497070e22aaf11dc2e27a13ee
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Medium.woff2 differ
diff --git a/_sass/common/fonts/Raleway/Raleway-MediumItalic.woff b/_sass/common/fonts/Raleway/Raleway-MediumItalic.woff
new file mode 100644
index 0000000000000000000000000000000000000000..cddb266bbf39029053f055ecefafbc14d2aef5fd
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-MediumItalic.woff differ
diff --git a/_sass/common/fonts/Raleway/Raleway-MediumItalic.woff2 b/_sass/common/fonts/Raleway/Raleway-MediumItalic.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..596eb5b43d93b6858fd74d58815e7d8e50e8284c
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-MediumItalic.woff2 differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Regular.woff b/_sass/common/fonts/Raleway/Raleway-Regular.woff
new file mode 100644
index 0000000000000000000000000000000000000000..152df09931c5b311868f48e1ea94d3e7c9959c76
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Regular.woff differ
diff --git a/_sass/common/fonts/Raleway/Raleway-Regular.woff2 b/_sass/common/fonts/Raleway/Raleway-Regular.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..77ad81c11c1c22fc45bf0c66ae8458e39421f84e
Binary files /dev/null and b/_sass/common/fonts/Raleway/Raleway-Regular.woff2 differ
diff --git a/_sass/common/fonts/Raleway/SIL Open Font License.txt b/_sass/common/fonts/Raleway/SIL Open Font License.txt
new file mode 100644
index 0000000000000000000000000000000000000000..458026d72fe76514864e29ffda5b1067cd8a97b3
--- /dev/null
+++ b/_sass/common/fonts/Raleway/SIL Open Font License.txt	
@@ -0,0 +1,49 @@
+Copyright (c) 2010, Matt McInerney (matt@pixelspread.com),
+Copyright (c) 2011, Pablo Impallari (www.impallari.com|impallari@gmail.com),
+Copyright (c) 2011, Rodrigo Fuenzalida (www.rfuenzalida.com|hello@rfuenzalida.com), with Reserved Font Name Raleway
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
+
+5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/_sass/common/fonts/_raleway.scss b/_sass/common/fonts/_raleway.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4319004b8a2bd829b267872504ec7641bca33e7f
--- /dev/null
+++ b/_sass/common/fonts/_raleway.scss
@@ -0,0 +1,65 @@
+/* Italic bold */
+@font-face {
+  font-family: 'Raleway';
+  font-style: italic;
+  font-display: auto;
+  font-weight: 700;
+  src:  url('#{$font-path}/Raleway/Raleway-BoldItalic.woff2') format("woff2"),
+        url('#{$font-path}/Raleway/Raleway-BoldItalic.woff') format("woff");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* Italic */
+@font-face {
+  font-family: 'Raleway';
+  font-style: italic;
+  font-display: auto;
+  font-weight: 400;
+  src:  url('#{$font-path}/Raleway/Raleway-MediumItalic.woff2') format("woff2"),
+        url('#{$font-path}/Raleway/Raleway-MediumItalic.woff') format("woff");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* Italic light*/
+@font-face {
+  font-family: 'Raleway';
+  font-style: italic;
+  font-display: auto;
+  font-weight: 300;
+  src:  url('#{$font-path}/Raleway/Raleway-Italic.woff2') format("woff2"),
+        url('#{$font-path}/Raleway/Raleway-Italic.woff') format("woff");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* Normal bold */
+@font-face {
+  font-family: 'Raleway';
+  font-style: normal;
+  font-display: auto;
+  font-weight: 700;
+  src:  url('#{$font-path}/Raleway/Raleway-Bold.woff2') format("woff2"),
+        url('#{$font-path}/Raleway/Raleway-Bold.woff') format("woff");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* Normal */
+@font-face {
+  font-family: 'Raleway';
+  font-style: normal;
+  font-display: auto;
+  font-weight: 400;
+  src:  url('#{$font-path}/Raleway/Raleway-Medium.woff2') format("woff2"),
+        url('#{$font-path}/Raleway/Raleway-Medium.woff') format("woff");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* Normal light*/
+@font-face {
+  font-family: 'Raleway';
+  font-style: normal;
+  font-display: auto;
+  font-weight: 300;
+  src:  url('#{$font-path}/Raleway/Raleway-Regular.woff2') format("woff2"),
+        url('#{$font-path}/Raleway/Raleway-Regular.woff') format("woff");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
diff --git a/_sass/dark/_callout_exercise.scss b/_sass/dark/_callout_exercise.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e6d0277c817ca99db0d234004c91ddfcbc46e077
--- /dev/null
+++ b/_sass/dark/_callout_exercise.scss
@@ -0,0 +1,25 @@
+/* ==========================================================================
+   Callout
+   ========================================================================== */
+
+$border-color-left: #730099 !default;
+$background-color: #39004d !default;
+$icon: url("data:image/svg+xml,%3csvg width='15' height='15' xmlns='http://www.w3.org/2000/svg'%3e%3c!--!Font Awesome Free 6.5.2 by %40fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons%2c Inc.--%3e%3cpath d='m14.587 4.16-1.35 1.35a.353.353 0 0 1-.5 0L9.485 2.258a.353.353 0 0 1 0-.496L10.838.41a1.41 1.41 0 0 1 1.99 0l1.759 1.76a1.404 1.404 0 0 1 0 1.99zM8.325 2.923.63 10.616l-.62 3.56a.705.705 0 0 0 .815.814l3.558-.626 7.696-7.692a.353.353 0 0 0 0-.498L8.828 2.922a.356.356 0 0 0-.501 0zM3.633 9.955a.408.408 0 0 1 0-.58l4.514-4.51a.408.408 0 0 1 .579 0 .408.408 0 0 1 0 .578l-4.51 4.512a.408.408 0 0 1-.581 0zM2.577 12.42h1.407v1.064l-1.89.33-.912-.91.331-1.89h1.065z' style='fill:%23ac00e6%3bfill-opacity:1%3bstroke-width:.0293002'/%3e%3c/svg%3e") !default;
+
+div.callout-body-container.callout-body > div.callout-exercise {
+  padding-top: 0.7rem !important;
+  padding-bottom: 0rem !important;
+}
+
+div.callout-exercise.callout {
+  border-left-color: $border-color-left;
+}
+
+div.callout-exercise.callout-style-default>.callout-header {
+  background-color: $background-color;
+}
+
+div.callout-exercise > .callout-header::before {
+  content: $icon;
+  margin-right: .35em !important;
+}
diff --git a/www/Raleway-Regular.ttf b/_sass/deprecated/www/Raleway-Regular.ttf
similarity index 100%
rename from www/Raleway-Regular.ttf
rename to _sass/deprecated/www/Raleway-Regular.ttf
diff --git a/www/YanoneKaffeesatz-Bold.ttf b/_sass/deprecated/www/YanoneKaffeesatz-Bold.ttf
similarity index 100%
rename from www/YanoneKaffeesatz-Bold.ttf
rename to _sass/deprecated/www/YanoneKaffeesatz-Bold.ttf
diff --git a/www/github-pandoc.css b/_sass/deprecated/www/github-pandoc.css
similarity index 100%
rename from www/github-pandoc.css
rename to _sass/deprecated/www/github-pandoc.css
diff --git a/www/style_Rmd.css b/_sass/deprecated/www/style_Rmd.css
similarity index 100%
rename from www/style_Rmd.css
rename to _sass/deprecated/www/style_Rmd.css
diff --git a/_sass/light/_callout_exercise.scss b/_sass/light/_callout_exercise.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a63a0dec55a4fe137382e6db9e9c1ddfb0831fc6
--- /dev/null
+++ b/_sass/light/_callout_exercise.scss
@@ -0,0 +1,25 @@
+/* ==========================================================================
+   Callout
+   ========================================================================== */
+
+$border-color-left: #ac00e6 !default;
+$background-color: #f1ccff !default;
+$icon: url("data:image/svg+xml,%3csvg width='15' height='15' xmlns='http://www.w3.org/2000/svg'%3e%3c!--!Font Awesome Free 6.5.2 by %40fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons%2c Inc.--%3e%3cpath d='m14.587 4.16-1.35 1.35a.353.353 0 0 1-.5 0L9.485 2.258a.353.353 0 0 1 0-.496L10.838.41a1.41 1.41 0 0 1 1.99 0l1.759 1.76a1.404 1.404 0 0 1 0 1.99zM8.325 2.923.63 10.616l-.62 3.56a.705.705 0 0 0 .815.814l3.558-.626 7.696-7.692a.353.353 0 0 0 0-.498L8.828 2.922a.356.356 0 0 0-.501 0zM3.633 9.955a.408.408 0 0 1 0-.58l4.514-4.51a.408.408 0 0 1 .579 0 .408.408 0 0 1 0 .578l-4.51 4.512a.408.408 0 0 1-.581 0zM2.577 12.42h1.407v1.064l-1.89.33-.912-.91.331-1.89h1.065z' style='fill:%23ac00e6%3bfill-opacity:1%3bstroke-width:.0293002'/%3e%3c/svg%3e") !default;
+
+div.callout-body-container.callout-body > div.callout-exercise {
+  padding-top: 0.7rem !important;
+  padding-bottom: 0rem !important;
+}
+
+div.callout-exercise.callout {
+  border-left-color: $border-color-left;
+}
+
+div.callout-exercise.callout-style-default>.callout-header {
+  background-color: $background-color;
+}
+
+div.callout-exercise > .callout-header::before {
+  content: $icon;
+  margin-right: .35em !important;
+}
diff --git a/_sass/main-dark.scss b/_sass/main-dark.scss
new file mode 100644
index 0000000000000000000000000000000000000000..75cd0cf0d653485804b10d70be33c8835c721789
--- /dev/null
+++ b/_sass/main-dark.scss
@@ -0,0 +1,8 @@
+/*-- scss:defaults --*/
+@import "common/variables";
+@import "common/callout";
+/*-- scss:rules --*/
+/* Components */
+@import "dark/callout_exercise";
+/* Fonts */
+@import "common/fonts";
diff --git a/_sass/main-light.scss b/_sass/main-light.scss
new file mode 100644
index 0000000000000000000000000000000000000000..72c95c89535c5c1ee0e50b85a052855b217a7303
--- /dev/null
+++ b/_sass/main-light.scss
@@ -0,0 +1,8 @@
+/*-- scss:defaults --*/
+@import "common/variables";
+@import "common/callout";
+/*-- scss:rules --*/
+/* Components */
+@import "light/callout_exercise";
+/* Fonts */
+@import "common/fonts";
diff --git a/session_1/session_1.Rmd b/session_1/session_1.Rmd
index a5995184ad45cb6598fe8e41d162a5f0c3e233e0..d7ac9e38cff8e0649419a8a9f861c1f018b792c1 100644
--- a/session_1/session_1.Rmd
+++ b/session_1/session_1.Rmd
@@ -1,7 +1,11 @@
 ---
 title: 'R.1: Introduction to R and RStudio'
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr);\n Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+  - "Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
@@ -9,7 +13,7 @@ library(fontawesome)
 ```
 
 ```{r setup, include=FALSE , echo=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
@@ -59,20 +63,23 @@ computing and graphics supported by the *R Foundation for Statistical Computing*
 Reasons to use it:
 
 - It's open source, which means that we have access to every bit of underlying computer code to prove that our results are correct (which is always a good point in science).
-- It’s free, well documented, and runs almost everywhere
+- It's free, well documented, and runs almost everywhere
 - It has a large (and growing) user base among scientists
 - It has a large library of external packages available for performing diverse tasks.
 
 ```{r echo=F, message=F}
 cran_packages <- nrow(available.packages(repos = "http://cran.us.r-project.org"))
 
-if (! require("rvest")) {
+if (!require("rvest")) {
   install.packages("rvest", quiet = T)
 }
 
 library(rvest)
-url <- 'https://www.bioconductor.org/packages/release/bioc/'
-biocPackages <- url %>% read_html() %>% html_table() %>%.[[1]]
+url <- "https://www.bioconductor.org/packages/release/bioc/"
+biocPackages <- url %>%
+  read_html() %>%
+  html_table() %>%
+  .[[1]]
 bioconductor_packages <- nrow(biocPackages)
 ```
 
@@ -86,7 +93,7 @@ Unlike other statistical software programs like Excel, SPSS, or Minitab that pro
 
 This means that you have to write instructions for R. Which means that you are going to learn to write code / program in R.
 
-R is usually used in a terminal in which you can type or paste your R code:
+R is generally used in a terminal in which you can type or paste your R code:
 
 ![](./img/R_terminal.png)
 
@@ -95,13 +102,13 @@ But navigating between your terminal, your code and your plots can be tedious, t
 ### RStudio, the R Integrated development environment (*IDE*)
 
 An IDE application provides **comprehensive facilities** to computer programmers for
-software development. Rstudio is **free** and **open-source**.
+software development. RStudio is **free** and **open-source**.
 
 To open RStudio, you can install the [RStudio application](https://www.rstudio.com/products/rstudio/) and open the app.
 
-Otherwise you can use the link and the login details provided to you by email. The web version of Rstudio is the same as the application expect that you can open it any recent browser.
+Otherwise you can use the link and the login details provided to you by email. The web version of RStudio is the same as the application except that you can open it in any recent browser.
 
-#### Rstudio interface
+#### RStudio interface
 
 ![](./img/RStudio.png)
 
@@ -118,7 +125,7 @@ The same console as before (in Red box)
 We are now going to write our first commands.
 We could do it directly in the R console, with multi-line commands but this process is tedious.
 
-Instead we are going to use the Rstudio code editor panel, to write our code.
+Instead we are going to use the RStudio code editor panel, to write our code.
 You can go to **File > New File > R script** to open your editor panel.
 
 Beside, you can keep your code history.
@@ -126,15 +133,15 @@ Beside, you can keep your code history.
 ![](./img/RStudio_editor.png)
 
 
-### How to execute R code in Rstudio ?
+### How to execute R code in RStudio ?
 
-RStudio offers you great flexibility in running code from within the editor window. There are buttons, menu choices, and keyboard shortcuts. To run the current line, you can
+RStudio gives you great flexibility in running code from the editor window. There are buttons, menu choices, and keyboard shortcuts. To run the current line, you can:
 
-- click on the `Run button` above the editor panel, or
+- click on the `Run` button above the editor panel, or
 - select `Run Selected Lines` from the `Code` menu, or
-- hit `Ctrl`+`Return` in Windows or Linux or `Cmd`+`Return` on OS X. To run a block of code, select it and then Run.
+- hit `Ctrl`+`Return` in Windows or Linux or `Cmd`+`Return` on OS X. To run a block of code, select it and then click on `Run`.
 
-If you have modified a line of code within a block of code you have just run, there is no need to reselect the section and Run, you can use the next button along, Rerun the previous region. This will run the previous code block including the modifications you have made.
+If you have modified a line of code within a block of code you have just run, there is no need to re-select the section and press `Run`. Instead, you can use the next button `Re-run the previous code region`. This will run the previous code block including the modifications you have made.
 
 
 ## R as a Calculator
@@ -148,15 +155,14 @@ Now that we know what we should do and what to expect, we are going to try some
 - Exponents: `^` or `**`
 - Parentheses: `(`, `)`
 
-<div class="pencadre">
 Now Open RStudio.
 
-You can `copy paste`  but I advise you to practice writing directly in the terminal.
+::: {.callout-tip}
+You can `copy paste` but I advise you to practice writing directly in the terminal.
 Like all the languages, you will become more familiar with R by using it.
 
 To validate the line at the end of your command: press `Return`.
-</div>
-
+:::
 
 ### First commands
 
@@ -165,7 +171,7 @@ You should see a `>` character before a blinking cursor. The `>` is called a pro
 1 + 100
 ```
 
-For classical output R will write the results with a `[N]` with `N` the row number.
+For classical output, R will write the results with a `[N]` with `N` the row number.
 Here you have a one-line results `[1]`
 
 ```{r calculatorstep1res, echo=F, eval=T}
@@ -195,7 +201,7 @@ It is waiting for the next command. Write just `100` and press `⏎`:
 
 The R console is a textual interface, which means that you will enter code, but it also means that R is going to write information back to you and that you will have to pay attention at what is written.
 
-There are 3 categories of messages that R can send you: **Errors** prefaced with `Error in…`, **Warnings** prefaced with `Warning:` and **Messages** which don’t start with either `Error` or `Warning`.
+There are 3 categories of messages that R can send you: **Errors** prefaced with `Error in…`, **Warnings** prefaced with `Warning:` and **Messages** which don't start with either `Error` or `Warning`.
 
 - **Errors**, you must consider them as red light. You must figure out what is causing it. Usually you can find useful clues in the errors message about how to solve it.
 - **Warning**, warnings are yellow light. The code is running but you have to pay attention. It's almost always a good idea to try to fix warnings.
@@ -216,11 +222,11 @@ You can use parenthesis `(` `)` to change this order.
 (3 + 5) * 2
 ```
 
-But to much parenthesis can be hard to read
+But too much parenthesis can be hard to read
 
 ```{r calculatorstep5, include=TRUE}
-(3 + (5 * (2 ^ 2))) # hard to read
-3 + 5 * (2 ^ 2)     # if you forget some rules, this might help
+(3 + (5 * (2^2))) # hard to read
+3 + 5 * (2^2) # if you forget some rules, this might help
 ```
 
 **Note :** The text following a `#` is a comment. It will not be interpreted by R. In the future, I advise you to use comments a lot to explain in your own words what the command means.
@@ -230,7 +236,7 @@ But to much parenthesis can be hard to read
 For small of large numbers, R will automatically switch to scientific notation.
 
 ```{r calculatorstep6, include=TRUE}
-2/10000
+2 / 10000
 ```
 
 `2e-4` is shorthand for `2 * 10^(-4)`
@@ -243,12 +249,12 @@ You can use `e` to write your own scientific notation.
 ### Mathematical functions
 
 R is distributed with a large number of existing functions.
-To call mathematical function you must with `function_name(<number>)`.
+To call a mathematical function, you must use `function_name(<number>)`.
 
 For example, for the natural logarithm:
 
 ```{r calculatorstep8, include=TRUE}
-log(2)  # natural logarithm
+log(2) # natural logarithm
 ```
 
 ```{r calculatorstep9, include=TRUE}
@@ -280,13 +286,13 @@ If we want our future programs to be able to perform automatic choices, we need
 
 Comparisons can be made with R. The result will return a `TRUE` or `FALSE` value (which is not a number as before but a `boolean` type).
 
-<div class="pencadre">
+::: {.callout-exercise}
 Try the following operator to get a `TRUE` then change your command to get a `FALSE`.
 
 You can use the `↑` (upper arrow) key to edit the last command and go through your history of commands
-</div>
+:::
 
-- equality (note two equal signs read as "is equal to")
+- equality (note: two equal signs read as "is equal to")
 
 ```{r calculatorstep13, include=TRUE}
 1 == 1
@@ -312,8 +318,8 @@ You can use the `↑` (upper arrow) key to edit the last command and go through
 1 > 0
 ```
 
-<div class="pencadre">
-  **Summary so far**
+::: {.callout-note}
+## Summary so far
 
   - R is a programming language and free software environment for statistical
 computing and graphics (free & opensource) with a large library of external packages available for performing diverse tasks.
@@ -321,18 +327,18 @@ computing and graphics (free & opensource) with a large library of external pack
   - R can be used as a calculator
   - R can perform comparisons
 
-</div>
+:::
 
 ## Variables and assignment
 
-In addition to being able to perform a huge number of computations very fast, computers can also store information to memory.
+In addition to being able to perform a huge number of computations very fast, computers can also store information in memory.
 This is a mandatory function to load your data and store intermediate states in your analysis.
 
-In R `<-` is the assignment operator (read as left members take right member value).
+In R `<-` is the assignment operator (read as left member take right member value).
 
-` = ` Also exists but is **not recommended!** It will be used preferentially in other cases.  (*We will see them later*).
+`=` Also exists but is **not recommended!** It will be used preferentially in other cases. (*We will see them later*).
 If you really don't want to press two consecutive keys for assignment, you can press `alt` + `-` to write `<-`.
-Rstudio provides lots of such shortcuts (you can display them by pressing `alt` + `shift` + `k`).
+RStudio provides lots of such shortcuts (you can display them by pressing `alt` + `shift` + `k`).
 
 We assign a value to `x`, `x` is called a variable.
 
@@ -351,6 +357,7 @@ x
 You now see the `x` value in the environment box (*in red*).
 
 ![](./img/RStudio_environment.png)
+
 This **variable** is present in your work environment. You can use it to perform different mathematical applications.
 
 
@@ -372,11 +379,11 @@ y
 
 A variable can be assigned a `numeric` value as well as a `character` value.
 
-Just put our character (or string) between double quote `"` when you assign this value.
+Just put the character (or string) between double quote `"` when you assign this value.
 ```{r VandAstep6, include=TRUE}
-z <- "x"  # One character
+z <- "x" # One character
 z
-a <- "Hello world"  # Multiple characters == String
+a <- "Hello world" # Multiple characters == String
 a
 ```
 
@@ -394,8 +401,8 @@ b
 typeof(b)
 ```
 
-You can type `is.` and press `tabulation`.
-Rstudio will show you a list of function whose names start with `is.`.
+You can type `is.` and press the `tabulation` key (`↹`).
+RStudio will show you a list of function whose names start with `is.`.
 This is called autocompletion, don't hesitate to spam your `tabulation` key as you write R code.
 
 ### Variables names
@@ -414,7 +421,9 @@ camelCaseToSeparateWords
 
 What you use is up to you, but be consistent.
 
-<div class="pencadre">   Which of the following are valid R variable names?</div>
+::: {.callout-exercise}
+Which of the following are valid R variable names?
+:::
 
 ```{r eval=F, }
 min_height
@@ -454,7 +463,7 @@ A R function can have different arguments
 function (x, base = exp(1))
 ```
 
-- `base` is a named argument are read from left to right
+- `base` is a named argument read from left to right
 - named arguments breaks the reading order
 - named arguments make your code more readable
 
@@ -474,9 +483,9 @@ This block allows you to view the different outputs (?help, graphs, etc.).
 
 ![](./img/formationR_VandAstep8_encadre.png)
 
-<div class="pencadre">
+::: {.callout-exercise}
 Test that your `logarithm` function can work in base 10
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -489,7 +498,7 @@ Test that your `logarithm` function can work in base 10
 
 ### Writing function
 
-We can  define our own function with :
+We can define our own function with:
 
 - function name,
 - declaration of function type: `function`,
@@ -508,7 +517,7 @@ function_name <- function(a, b){
 - a series of operations,
 
 The argument `a` and `b` are accessible from within the function body as the variable `a` and `b`.
-In the function body argument are independant of the global environment.
+In the function body argument are independent of the global environment.
 
 ```R
 function_name <- function(a, b){
@@ -518,7 +527,7 @@ function_name <- function(a, b){
 }
 ```
 
-- `return` operation
+- `return` operation,
 
 At the end of a function we want to return a result, so function calls will be equal to this result.
 
@@ -532,40 +541,40 @@ function_name <- function(a, b){
 
 **Note: ** if you don't use `return` by default the evaluation of the last line of your function body is returned.
 
-**Note: **  The function variables (here `a` and `b`) are independant of the global environment: They define to which values the operation will be applied in the function body.
+**Note: **  The function variables (here `a` and `b`) are independent of the global environment: They define to which values the operation will be applied in the function body.
 
 -  The order of arguments is important
 
-<div class="pencadre">
+::: {.callout-exercise}
 Predict the result of R1, R2 and R3.
+:::
 
 ```R
-minus <- function(a, b){
+minus <- function(a, b) {
   result_1 <- a - b
   return(result_1)
 }
 
-##R1:
-R1 <- minus(4,2)
+## R1:
+R1 <- minus(4, 2)
 
-##R2
-R2 <- minus(2,4)
+## R2
+R2 <- minus(2, 4)
 
-##R3
+## R3
 a <- 2
 b <- 10
-R3  <-  minus(b,a)
+R3 <- minus(b, a)
 ```
-</div>
 
 <details><summary>Solution 1</summary>
 <p>
 ```{r minus1, include=TRUE}
-minus <- function(a, b){
+minus <- function(a, b) {
   result_1 <- a - b
   return(result_1)
 }
-minus(4,2)
+minus(4, 2)
 ```
 </p>
 </details>
@@ -574,7 +583,7 @@ minus(4,2)
 <details><summary>Solution 2</summary>
 <p>
 ```{r minus2, include=TRUE}
-minus(2,4)
+minus(2, 4)
 ```
 </p>
 </details>
@@ -585,48 +594,49 @@ minus(2,4)
 ```{r minus3, include=TRUE}
 a <- 2
 b <- 10
-minus(b,a)
+minus(b, a)
 ```
 </p>
 </details>
 
 - Naming variables is more explicit and bypasses the order.
 
-<div class="pencadre">
+::: {.callout-exercise}
 Predict the result of R1, R2, R3 and R4.
+:::
+
 ```R
 a <- 10
 b <- 2
 
-minus <- function(a, b){
+minus <- function(a, b) {
   result_1 <- a - b
   return(result_1)
 }
 
-##R1:
-R1 <- minus(a=6,b=3)
+## R1:
+R1 <- minus(a = 6, b = 3)
 
-##R2
-R2 <- minus(b=3,a=6)
+## R2
+R2 <- minus(b = 3, a = 6)
 
-##R3
+## R3
 R3 <- a
 
-##R4
-R4  <- minus(b=b,a=a)
+## R4
+R4 <- minus(b = b, a = a)
 ```
-</div>
 
 <details><summary>Solution 1</summary>
 <p>
 ```{r minus21, include=TRUE}
 a <- 10
 b <- 2
-minus <- function(a, b){
+minus <- function(a, b) {
   result_1 <- a - b
   return(result_1)
 }
-R1 <- minus(a=6,b=3)
+R1 <- minus(a = 6, b = 3)
 R1
 ```
 </p>
@@ -636,7 +646,7 @@ R1
 <details><summary>Solution 2</summary>
 <p>
 ```{r minus22, include=TRUE}
-R2 <- minus(b=3,a=6)
+R2 <- minus(b = 3, a = 6)
 R2
 ```
 </p>
@@ -655,41 +665,40 @@ R3
 <details><summary>Solution 4</summary>
 <p>
 ```{r minus24, include=TRUE}
-R4  <- minus(b=b,a=a)
+R4 <- minus(b = b, a = a)
 R4
 ```
 </p>
 </details>
 
 
-- Default values for arguments may be set at definition and the Default value is used when argument is not provided.
+- Default values for arguments may be set at definition and the default value is used when argument is not provided.
 
 ```{r minus10, include=TRUE}
-minus_10 <- function(a, b=10){
+minus_10 <- function(a, b = 10) {
   result_1 <- a - b
   return(result_1)
 }
 minus_10(40)
-minus_10(40,b=5)
-minus_10(40,5)
+minus_10(40, b = 5)
+minus_10(40, 5)
 ```
 - Functions can be define without argument
 
 ```{r print_hw, include=TRUE}
-print_hw <- function(){
+print_hw <- function() {
   print("Hello world!")
   print("How R U?")
 }
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 What is the difference between `print_hw` and `print_hw()` ?
-
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
-`print_hw` is considered as an environment variable, and R return the definition of `print_hw`.
+`print_hw` is considered as an environment variable, and R returns the definition of `print_hw`.
 You need to add `()` to execute it
 
 ```{r print_hw_env, include=TRUE}
@@ -704,32 +713,29 @@ print_hw()
 </details>
 
 
-### Some exercices
+### Challenges
 
-<div class="pencadre">
 1. Try a function (`rect_area`) to calculate the area of a rectangle of length "L" and width "W"
 
 2. (more difficult) Try a function  (`even_test`) to test if a number is even?
-For that, you can use the `%%` modulo operators to get the remainder of an euclidean division and use the `==` comparison to test if the results
+For that, you can use the modulo operator `%%` to get the remainder of an euclidean division and use the comparison `==` to test if the results
 of the modulo is equal to `0`.
 
 ```{r modulo, include=TRUE}
 13 %% 2
 ```
 
-3. Using your `even_test` function, write a new function `even_print` which will print "This number is even" or "This number is odd". You will need the `if else` statement and the function `print`. Find help on how to use them.
-
-</div>
+3. Using your `even_test` function, write a new function `even_print` which will print the string "This number is even" or "This number is odd". You will need the `if`, `else` statements and the function `print`. Find help on how to use them.
 
 <details><summary>Solution 1 </summary>
 <p>
 
 ```{r rect_area, include=TRUE}
-rect_area <- function(L,W){
+rect_area <- function(L, W) {
   area <- L * W
   return(area)
 }
-rect_area(4,3)
+rect_area(4, 3)
 ```
 </p>
 </details>
@@ -738,7 +744,7 @@ rect_area(4,3)
 <details><summary>Solution 2 </summary>
 <p>
 ```{r VandAstep11, include=TRUE}
-even_test <- function(x){
+even_test <- function(x) {
   modulo_result <- x %% 2
   is_even <- modulo_result == 0
   return(is_even)
@@ -750,7 +756,7 @@ even_test(3)
 **Note :** A function can be written in several forms.
 
 ```{r VandAstep11smal2, include=TRUE}
-even_test2 <- function(x){
+even_test2 <- function(x) {
   (x %% 2) == 0
 }
 even_test(4)
@@ -764,8 +770,8 @@ even_test(3)
 <details><summary>Solution 3 </summary>
 <p>
 ```{r VandAstep13, include=TRUE}
-even_print <- function(x){
-  if(even_test(x) == TRUE) {
+even_print <- function(x) {
+  if (even_test(x) == TRUE) {
     print("This number is even")
   } else {
     print("This number is odd")
@@ -778,8 +784,8 @@ even_print(3)
 **Note :** There is no need to test whether a boolean variable (TRUE/FALSE) is TRUE or FALSE.
 
 ```{r VandAstep11small14, include=TRUE}
-even_print <- function(x){
-  if(even_test(x)) {
+even_print <- function(x) {
+  if (even_test(x)) {
     print("This number is even")
   } else {
     print("This number is odd")
@@ -794,14 +800,14 @@ even_print(3)
 
 ### Cleaning up
 
-We can now clean your environment
+We can now clean our environment
 
 ```{r VandAstep15, include=TRUE}
 rm(minus)
 ```
 
-What appenned in the *Environment* panel ?
-Check the documentation of this command
+What happened in the *Environment* panel ?
+Check the documentation of this command.
 
 <details><summary>Solution</summary>
 <p>
@@ -815,9 +821,9 @@ Check the documentation of this command
 ls()
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 Combine `rm` and `ls` to cleanup your *Environment*
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -831,25 +837,25 @@ rm(list = ls())
 ls()
 ```
 
-<div class='pencadre'>
+::: {.callout-note}
   **Summary so far:**
 
-  - Assigning a variable is done with ` <- `.
+  - Assigning a variable is done with `<-`.
   - The assigned variables are listed in the environment box.
   - Variable names can contain letters, numbers, underscores and periods.
-  - Functions are also variable and can write in several forms
-  - An editing box is available on Rstudio.
+  - Functions are also variables and can write in several forms.
+  - An editing box is available on RStudio.
 
-</div>
+:::
 
 ## Complex variable type
 
 You can only go so far with the variables we have already seen.
-In R there are also **complex variable type**, which can be seen as combination of simple variable type.
+In R there are also **complex variable type**, which can be seen as a combination of simple variable types.
 
 ### Vector (aka list)
 
-Vectors are simple list of variable of the same type
+Vectors are simple list of variable of the same type:
 
 ```{r Vecstep1, include=TRUE}
 c(1, 2, 3, 4, 5)
@@ -909,77 +915,77 @@ x == y
 ### Accessing values
 
 There are multiple ways to access or replace values in vectors or other data structures. The most common approach is to use "indexing".
-In the below, note that brackets `[ ]` are used for indexing, whereas you have already seen that parentheses `( )` are used to call a function and `{ }` to define function. It is very important not to mix these up.
+In what follows, note that brackets `[ ]` are used for indexing, whereas you have already seen that parentheses `( )` are used to call a function and `{ }` to define function. It is very important not to mix these up.
 
 
 Here are some examples that show how elements of vectors can be obtained by indexing.
 
 
-You can use the position(s) of the value(s) in the vector
+You can use the position(s) of the value(s) in the vector:
 
 ```{r index1, include=TRUE}
-x <- c(1,5,7,8)
+x <- c(1, 5, 7, 8)
 x[4]
-x[c(1,3,4)]
+x[c(1, 3, 4)]
 ```
-You can use booleans to define which values should be kept.
+You can use booleans to define which values should be kept:
 
 ```{r index2, include=TRUE}
-x <- c(1,5,7,8,15)
-x[c(TRUE,FALSE,TRUE,FALSE,TRUE)]
-x[c(FALSE,TRUE)] # Bolean vector is reused if it is not of the same size of the vector to index
+x <- c(1, 5, 7, 8, 15)
+x[c(TRUE, FALSE, TRUE, FALSE, TRUE)]
+x[c(FALSE, TRUE)] # Bolean vector is reused if it is not of the same size of the vector to index
 
-y <- c(TRUE,FALSE,FALSE,FALSE,TRUE)
+y <- c(TRUE, FALSE, FALSE, FALSE, TRUE)
 x[y]
 ```
 
-You can use names in the case of a named vector.
+You can use names in the case of a named vector:
 
 ```{r index3, include=TRUE}
-x <-c(a = 1, b = 2, c = 3, d = 4, e = 5)
-x[c("a","c")]
+x <- c(a = 1, b = 2, c = 3, d = 4, e = 5)
+x[c("a", "c")]
 ```
 
-You can also use an index to change values
+You can also use an index to change values:
 
 ```{r index4, include=TRUE}
-x <- c(1,5,7,8,15)
+x <- c(1, 5, 7, 8, 15)
 x[1] <- 3
 x
 
-x[x>5] <- 13
+x[x > 5] <- 13
 x
 ```
 
-<div class="pencadre">
+::: {.callout-note}
   **Summary so far**
 
   - A variable can be of different types : `numeric`, `character`, `vector`, `function`, etc.
   - Calculations and comparisons apply to vectors.
   - Do not hesitate to use the help box to understand functions!
 
-</div>
+:::
 
-We will see other complex variables type during this formation.
+We will see other complex variable types during this formation.
 
 ## Packages
 
-R base is like a new smartphone, you can do loots of things with it but you can also install new apps to a huge range of other things.
+R base is like a new smartphone, you can do lots of things with it but you can also install new apps to a huge range of other things.
 In R those apps are called **packages**.
 
 There are different sources to get packages from:
 
 - The [CRAN](https://cran.r-project.org/) which is the default source
 - [Bioconducor](http://www.bioconductor.org) which is another source specialized for biology packages
-- Directly from [github](https://github.com/)
+- Directly from [GitHub](https://github.com/)
 
-To install packages from [Bioconducor](http://www.bioconductor.org) and [github](https://github.com/) you will need to install specific packages from the [CRAN](https://cran.r-project.org/).
+To install packages from [Bioconducor](http://www.bioconductor.org) and [GitHub](https://github.com/) you will need to install specific packages from the [CRAN](https://cran.r-project.org/).
 
 ### Installing packages
 
 #### From CRAN
 
-To install packages, you can use the `install.packages` function (don't forget to use tabulation for long variable names).
+To install packages, you can use the `install.packages` function (don't forget to use tabulation for long variable names). For instance:
 
 ```R
 install.packages("tidyverse")
@@ -1017,9 +1023,9 @@ Then to install, for example "tximport", you just have to write:
 BiocManager::install("tximport")
 ```
 
-#### From github
+#### From GitHub
 
-If you need to install a package that is not available on the CRAN but on a github repository, you can do it using the "remotes" package. Indeed this package imports functions that will allow you to install a package available on [github](https://github.com/) or bitbucket or gitlab directly on your computer.
+If you need to install a package that is not available on the CRAN but on a GitHub repository, you can do it using the "remotes" package. Indeed this package imports functions that will allow you to install a package available on [GitHub](https://github.com/) or Bitbucket or GitLab directly on your computer.
 
 To use the "remotes" packages, you must first install it:
 
@@ -1027,35 +1033,35 @@ To use the "remotes" packages, you must first install it:
 install.packages("remotes")
 ```
 
-Once "remotes" is installed, you will be able to install all R package from github or from their URL.
+Once "remotes" is installed, you will be able to install all R packages from GitHub or from their URL.
 
-For example, if you want to install the last version of a "gganimate", which allow you to animate ggplot2 graphes, you can use :
+For example, if you want to install the last version of a "gganimate", which allow you to animate ggplot2 graphs, you can use:
 
 ```R
 remotes::install_github("thomasp85/gganimate")
 ```
 
-By default the latest version of the package is installed, if you want a given version you can specify it :
+By default the latest version of the package is installed, if you want a given version you can specify it:
 
 ```R
 remotes::install_github("thomasp85/gganimate@v1.0.7")
 ```
 
-You can find more information in the documentation of remotes : [https://remotes.r-lib.org](https://remotes.r-lib.org)
+You can find more information in the documentation of remotes: [https://remotes.r-lib.org](https://remotes.r-lib.org)
 
 ### Loading packages
 
 Once a package is installed, you need to load it in your R session to be able to use it.
-The command `sessionInfo` display your session information.
+The command `sessionInfo` displays your session information.
 
 
 ```{r packagesstep1, include=TRUE}
 sessionInfo()
 ```
 
-<div class='pencadre'>
+::: {.callout-exercise}
 Use the command `library` to load the `ggplot2` package and check your session
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -1068,7 +1074,7 @@ sessionInfo()
 
 ### Unloading packages
 
-Sometime, you may want to unload package from your session instead of relaunching R.
+Sometimes, you may want to unload a package from your session instead of relaunching R.
 
 ```{r packagesstep4, include=TRUE}
 unloadNamespace("ggplot2")
diff --git a/session_2/session_2.Rmd b/session_2/session_2.Rmd
index 04e443e87bcaaabba5f1c5c58e04ea3aaf4df173..31a153b396f1cf683c845d03242a0c384f47ee27 100644
--- a/session_2/session_2.Rmd
+++ b/session_2/session_2.Rmd
@@ -1,18 +1,23 @@
 ---
 title: "R.2: introduction to Tidyverse"
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr);\nHélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+  - "Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
 library(fontawesome)
 
-if("conflicted" %in% .packages())
-    conflicted::conflicts_prefer(dplyr::filter)
+if ("conflicted" %in% .packages()) {
+  conflicted::conflicts_prefer(dplyr::filter)
+}
 ``` 
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
@@ -21,8 +26,9 @@ knitr::opts_chunk$set(comment = NA)
 library("tidyverse")
 tmp <- tempfile(fileext = ".zip")
 download.file("http://www.fueleconomy.gov/feg/epadata/vehicles.csv.zip",
-              tmp,
-              quiet = TRUE)
+  tmp,
+  quiet = TRUE
+)
 unzip(tmp, exdir = "data-raw")
 new_class_level <- c(
   "Compact Cars",
@@ -110,7 +116,7 @@ read_csv("data-raw/vehicles.csv") %>%
 
 ## Introduction
 
-In the last session, we have gone through the basis of R.
+In the last session, we have gone through the basics of R.
 Instead of continuing to learn more about R programming, in this session we are going to jump directly to rendering plots.
 
 We make this choice for three reasons:
@@ -142,7 +148,7 @@ All packages share an underlying design philosophy, grammar, and data structures
 install.packages("tidyverse")
 ```
 
-Luckily for you, `tidyverse` is preinstalled on your Rstudio server. So you just have to load the ` library`
+Luckily for you, `tidyverse` is pre-installed on your RStudio server. So you just have to load the ` library`:
 
 ```{R load_tidyverse}
 library("tidyverse")
@@ -150,7 +156,7 @@ library("tidyverse")
  
 ### Toy data set `mpg`
 
-This dataset contains a subset of the fuel economy data that the EPA makes available on [fueleconomy.gov](http://fueleconomy.gov).
+This dataset contains a subset of the fuel economy data that the EPA made available on [fueleconomy.gov](http://fueleconomy.gov).
 It contains only models which had a new release every year between 1999 and 2008.
 
 You can use the `?` command to know more about this dataset.
@@ -159,10 +165,10 @@ You can use the `?` command to know more about this dataset.
 ?mpg
 ```
 
-But instead of using a dataset included in a R package, you may want to be able to use any dataset with the same format.
-For that we are going to use the command `read_csv` which is able to read a [csv](https://en.wikipedia.org/wiki/Comma-separated_values) file.
+But instead of using a dataset included in a R package, you may want to use any dataset with the same format.
+For that, we are going to use the command `read_csv` which is able to read a [csv](https://en.wikipedia.org/wiki/Comma-separated_values) file.
 
-This command also works for file URL
+This command also works for file URL:
 
 ```{r mpg_download_local, cache=TRUE, message=FALSE, echo = F, include=F}
 new_mpg <- read_csv("./mpg.csv")
@@ -180,87 +186,89 @@ You can check the number of lines and columns of the data with `dim`:
 dim(new_mpg)
 ```
 
-To visualize the data in Rstudio you can use the command. `View`
+To visualize the data in RStudio you can use the command `View`,
 
 ```R
 View(new_mpg)
 ```
 
-Or by simply calling the variable.
-Like for simple data type calling a variable print it.
+Or simply calling the variable.
+As with a simple data type, calling a variable prints it.
 But complex data type like `new_mpg` can use complex print function.
 
 ```{r mpg_inspect3, include=TRUE}
 new_mpg
 ```
 
-Here we can see that `new_mpg` is a `tibble` we will come back to `tibble` later.
+Here we can see that `new_mpg` is a `tibble`. We will come back to `tibble` later.
 
 
 ### New script
 
-Like in the last session, instead of typing your commands directly in the console, you are going to write them in an R script.
+As in the last session, instead of typing your commands directly in the console, you will write them in an R script.
 
 ![](./img/formationR_session2_scriptR.png)
 
 ## First plot with `ggplot2`
 
-We are going to make the simplest plot possible to study the relationship between two variables: the scatterplot.
+We are going to make the simplest plot possible to study the relationship between two variables: a scatterplot.
 
-The following command generates a plot between engine size `displ` and fuel efficiency `hwy` present the `new_mpg` `tibble`.
+The following command generates a plot between engine size `displ` and fuel efficiency `hwy` from the `new_mpg` `tibble`.
 
 ```{r new_mpg_plot_a, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg) + 
+ggplot(data = new_mpg) +
   geom_point(mapping = aes(x = displ, y = hwy))
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 Are cars with bigger engines less fuel efficient ? 
-</div>
+:::
 
 `ggplot2` is a system for declaratively creating graphics, based on [The Grammar of Graphics](https://www.amazon.com/Grammar-Graphics-Statistics-Computing/dp/0387245448/ref=as_li_ss_tl). You provide the data, tell `ggplot2` how to map variables to aesthetics, what graphical primitives to use, and it takes care of the details.
 
+All ggplot2 plots begin with the same call:
+
 ```
 ggplot(data = <DATA>) + 
   <GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))
 ```
 
-- you begin a plot with the function `ggplot()`
-- you complete your graph by adding one or more layers
-- `geom_point()` adds a layer with a scatterplot
-- each **geom **function in `ggplot2` takes a `mapping` argument
-- the `mapping` argument is always paired with `aes()`
+- you instantiate a plot with the function `ggplot()`
+- you complete your graph by adding, with `+`, one or more layers  
+  ( for instance, `geom_point()` adds a layer with a scatterplot )
+- each **geom** function in ggplot2 takes a `mapping` argument
+- the `mapping` argument is always paired with aesthetics `aes()`
 
-<div class="pencadre">
-What happend when you use only the command `ggplot(data = mpg)` ?
-</div>
+::: {.callout-exercise}
+What happened when you only use the command `ggplot(data = mpg)` ?
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r only_ggplot, cache = TRUE, fig.width=4.5, fig.height=2}
-ggplot(data = new_mpg) 
+ggplot(data = new_mpg)
 ```
 </p>
 </details>
  
  
-<div class="pencadre">
+::: {.callout-exercise}
 Make a scatterplot of `hwy` ( fuel efficiency ) vs. `cyl` ( number of cylinders ).
-</div>
+:::
  
 
 <details><summary>Solution</summary>
 <p>
 ```{r new_mpg_plot_b, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = hwy, y = cyl)) + 
+ggplot(data = new_mpg, mapping = aes(x = hwy, y = cyl)) +
   geom_point()
 ```
 
 </p>
 
-<div class="pencadre">
+::: {.callout-exercise}
 What seems to be the problem ?
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -276,16 +284,16 @@ Dots with the same coordinates are superposed.
 
 `ggplot2` will automatically assign a unique level of the aesthetic (here a unique color) to each unique value of the variable, a process known as scaling. `ggplot2` will also add a legend that explains which levels correspond to which values.
 
-Try the following aesthetic:
+Try the following aesthetics:
 
 - `size`
 - `alpha`
 - `shape`
 
-### `color` mapping
+### `color` mapping {#sec-color-mapping}
 
 ```{r new_mpg_plot_e, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) +
   geom_point()
 ```
 
@@ -293,29 +301,29 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) +
 ### `size` mapping
 
 ```{r new_mpg_plot_f, cache = TRUE, fig.width=8, fig.height=4.5, warning=FALSE}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, size = class)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, size = class)) +
   geom_point()
 ```
 
 ### `alpha` mapping
 
 ```{r new_mpg_plot_g, cache = TRUE, fig.width=8, fig.height=4.5, warning=FALSE}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, alpha = class)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, alpha = class)) +
   geom_point()
 ```
 
 ### `shape` mapping
 
 ```{r new_mpg_plot_h, cache = TRUE, fig.width=8, fig.height=4.5, warning=FALSE}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, shape = class)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, shape = class)) +
   geom_point()
 ```
 
 You can also set the aesthetic properties of your **geom** manually. For example, we can make all of the points in our plot blue and squares:
 
 ```{r new_mpg_plot_i, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
-  geom_point(color = "blue", shape=0)
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
+  geom_point(color = "blue", shape = 0)
 ```
 
 Here is a list of different shapes available in R:
@@ -323,41 +331,41 @@ Here is a list of different shapes available in R:
 ![](./img/shapes.png){width=300px}
 </center>
  
-<div class="pencadre">
-What’s gone wrong with this code? Why are the points not blue?
-</div>
+::: {.callout-exercise}
+What's gone wrong with this code? Why are the points not blue?
+:::
 
 ```{r new_mpg_plot_not_blue, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = "blue")) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = "blue")) +
   geom_point()
 ```
 
 <details><summary>Solution</summary>
 <p>
 ```{r new_mpg_plot_blue, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point(color = "blue")
 ```
 </p>
 </details>
  
-### Mapping a **continuous** variable to a color.
+### Mapping a **continuous** variable to a color
 
 You can also map continuous variable to a color
 
 ```{r continu, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = cyl)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = cyl)) +
   geom_point()
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 What happens if you map an aesthetic to something other than a variable name, like `color = displ < 5`?
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r condiColor, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = displ < 5)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = displ < 5)) +
   geom_point()
 ```
 </p>
@@ -367,19 +375,19 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = displ < 5)) +
 
 You can create multiple plots at once by faceting. For this you can use the command `facet_wrap`.
 This command takes a formula as input.
-We will come back to formulas in R later, for now, you have to know that formulas start with a `~` symbol.
+We will come back to formulas in R later, for now, you just have to know that formulas start with a `~` symbol.
 
 To make a scatterplot of `displ` versus `hwy` per car `class` you can use the following code:
 
 ```{r new_mpg_plot_k, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
-  geom_point() + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
+  geom_point() +
   facet_wrap(~class, nrow = 2)
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 Now try to facet your plot by `fuel + class`
-</div>
+:::
 
 
 <details><summary>Solution</summary>
@@ -396,33 +404,33 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
 
 ## Composition
 
-There are different ways to represent the information :
+There are different ways to represent the information:
 
 ```{r new_mpg_plot_o, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point()
 ```
 
  \ 
 
 ```{r new_mpg_plot_p, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_smooth()
 ```
 
  \ 
 
-We can add as many layers as we want
+We can add as many layers as we want:
 
 ```{r new_mpg_plot_q, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point() +
   geom_smooth()
 ```
 
  \
 
-We can make `mapping` layer specific
+We can make `mapping` layer specific:
 
 ```{r new_mpg_plot_s, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
 ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
@@ -435,34 +443,30 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
 We can use different `data` (here new_mpg and mpg tables) for different layers (you will lean more on `filter()` later)
 
 ```{r new_mpg_plot_t, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point(mapping = aes(color = class)) +
   geom_smooth(data = filter(mpg, class == "subcompact"))
 ```
 
-## Challenge  !
+## Challenges
 
 ### First challenge
-<div class="pencadre">
+
 Run this code in your head and predict what the output will look like. Then, run the code in R and check your predictions.
-</div>
 ```R
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = drive)) + 
-  geom_point(show.legend = FALSE) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = drive)) +
+  geom_point(show.legend = FALSE) +
   geom_smooth(se = FALSE)
 ```
 
-<div class="pencadre">
 - What does `show.legend = FALSE` do?
 - What does the `se` argument to `geom_smooth()` do?
-</div>
-
 
 <details><summary>Solution</summary>
 <p>
 ```{r soluce_challenge_1, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = drive)) + 
-  geom_point(show.legend = FALSE) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = drive)) +
+  geom_point(show.legend = FALSE) +
   geom_smooth(se = FALSE)
 ```
 </p>
@@ -472,20 +476,17 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = drive)) +
 
 ### Second challenge
 
-<div class="pencadre">
 How being a `Two Seaters` car (*class column*) impact the engine size (*displ column*) versus fuel efficiency relationship (*hwy column*) ?
 
 1. Make a plot of `hwy` in function of `displ ` 
 1. *Colorize* this plot in another color for `Two Seaters` class
 2. *Split* this plot for each *class*
 
-</div>
-
 <details><summary>Solution 1</summary>
 <p>
 
 ```{r new_mpg_plot_color_2seater1, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point()
 ```
 
@@ -495,7 +496,7 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
 <details><summary>Solution 2</summary>
 <p>
 ```{r new_mpg_plot_color_2seater2, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point() +
   geom_point(data = filter(new_mpg, class == "Two Seaters"), color = "red")
 ```
@@ -507,7 +508,7 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
 <p>
 
 ```{r new_mpg_plot_color_2seater_facet, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
   geom_point() +
   geom_point(data = filter(new_mpg, class == "Two Seaters"), color = "red") +
   facet_wrap(~class)
@@ -517,15 +518,13 @@ ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
 </details>
 
 
-<div class="pencadre">
-Write a `function` called `plot_color_a_class` that can take as argument the class and plot the same graph for this class
-</div>
+Write a `function` called `plot_color_a_class` that can take as argument the class and plot the same graph for this class.
 
 <details><summary>Solution</summary>
 <p>
 ```{r new_mpg_plot_color_2seater_fx, cache = TRUE, fig.width=8, fig.height=4.5}
 plot_color_a_class <- function(my_class) {
-  ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) + 
+  ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy)) +
     geom_point() +
     geom_point(data = filter(new_mpg, class == my_class), color = "red") +
     facet_wrap(~class)
@@ -540,9 +539,7 @@ plot_color_a_class("Compact Cars")
 
 ### Third challenge
 
-<div class="pencadre">
-Recreate the R code necessary to generate the following graph (see "linetype" option of "geom_smooth")
-</div>
+Recreate the R code necessary to generate the following graph (see "linetype" option of `geom_smooth`)
 
 ```{r new_mpg_plot_u, echo = FALSE, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
 ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = fuel)) +
@@ -572,11 +569,11 @@ You can do it with the `ggsave` function.
 First save your plot in a variable :
 
 ```{r}
-p1 <- ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
-      geom_point()
+p1 <- ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) +
+  geom_point()
 ```
 
-Then save it in the wanted format:
+Then save it in the format of your choice:
 
 ```{r, eval=F}
 ggsave("test_plot_1.png", p1, width = 12, height = 8, units = "cm")
@@ -605,7 +602,7 @@ install.packages("cowplot")
 ```
 
 ```{r, include=F, echo =F}
-if (! require("cowplot")) {
+if (!require("cowplot")) {
   install.packages("cowplot")
 }
 ```
@@ -617,62 +614,66 @@ library(cowplot)
 ```
 
 ```{r,fig.width=8, fig.height=4.5, message=FALSE}
-p1 <- ggplot(data = new_mpg) + 
+p1 <- ggplot(data = new_mpg) +
   geom_point(mapping = aes(x = displ, y = hwy))
 p1
 ```
 
 
 ```{r,fig.width=8, fig.height=4.5, message=FALSE}
-p2 <- ggplot(data = new_mpg, mapping = aes(x = cty, y = hwy)) + 
+p2 <- ggplot(data = new_mpg, mapping = aes(x = cty, y = hwy)) +
   geom_point()
 p2
 ```
 
 
 ```{r,fig.width=8, fig.height=4.5, message=FALSE}
-plot_grid(p1, p2, labels = c('A', 'B'), label_size = 12)
+plot_grid(p1, p2, labels = c("A", "B"), label_size = 12)
 ```
 
 You can also save it in a file.
 
 ```{r, eval=F}
-p_final = plot_grid(p1, p2, labels = c('A', 'B'), label_size = 12)
+p_final <- plot_grid(p1, p2, labels = c("A", "B"), label_size = 12)
 ggsave("test_plot_1_and_2.png", p_final, width = 20, height = 8, units = "cm")
 ```
 
 You can learn more features about `cowplot` on [https://wilkelab.org/cowplot/articles/introduction.html](its website). 
 
-<div class="pencadre">
+::: {.callout-exercise}
 Use the `cowplot` documentation to reproduce this plot and save it.
-</div>
+:::
 
 ```{r, echo=F}
-p1 <- ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
-  geom_point() + theme_bw()
+p1 <- ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) +
+  geom_point() +
+  theme_bw()
 
-p2 <- ggplot(data = new_mpg, mapping = aes(x = cty, y = hwy, color = class)) + 
-  geom_point() + theme_bw()
+p2 <- ggplot(data = new_mpg, mapping = aes(x = cty, y = hwy, color = class)) +
+  geom_point() +
+  theme_bw()
 
-p_row <- plot_grid(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), labels = c('A', 'B'), label_size = 12)
-p_legend <- get_legend(p1 + theme(legend.position = "top"))
+p_row <- plot_grid(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), labels = c("A", "B"), label_size = 12)
+p_legend <- get_plot_component(p1, "guide-box-top", return_all = TRUE)
 
-plot_grid(p_row, p_legend, nrow = 2, rel_heights = c(1,0.2))
+plot_grid(p_row, p_legend, nrow = 2, rel_heights = c(1, 0.2))
 ```
 
 <details><summary>Solution</summary>
 <p>
 ```{r , echo = TRUE, eval = F}
-p1 <- ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
-  geom_point() + theme_bw()
+p1 <- ggplot(data = new_mpg, mapping = aes(x = displ, y = hwy, color = class)) +
+  geom_point() +
+  theme_bw()
 
-p2 <- ggplot(data = new_mpg, mapping = aes(x = cty, y = hwy, color = class)) + 
-  geom_point() + theme_bw()
+p2 <- ggplot(data = new_mpg, mapping = aes(x = cty, y = hwy, color = class)) +
+  geom_point() +
+  theme_bw()
 
-p_row <- plot_grid(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), labels = c('A', 'B'), label_size = 12)
-p_legend <- get_legend(p1 + theme(legend.position = "top"))
+p_row <- plot_grid(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), labels = c("A", "B"), label_size = 12)
+p_legend <- get_plot_component(p1, "guide-box-top", return_all = TRUE)
 
-p_final <- plot_grid(p_row, p_legend, nrow = 2, rel_heights = c(1,0.2))
+p_final <- plot_grid(p_row, p_legend, nrow = 2, rel_heights = c(1, 0.2))
 p_final
 ```
 
diff --git a/session_3/session_3.Rmd b/session_3/session_3.Rmd
index 09a9513e052738db7f31c05bfcdff83901d5f894..3a918544288e8689b5a5335f52b31c62aaf615c9 100644
--- a/session_3/session_3.Rmd
+++ b/session_3/session_3.Rmd
@@ -1,7 +1,11 @@
 ---
 title: 'R.3: Transformations with ggplot2'
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr), Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+  - "Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
@@ -9,16 +13,16 @@ library(fontawesome)
 ``` 
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
 
 ## Introduction
 
-In the last session, we have seen how to use `ggplot2` and [The Grammar of Graphics](https://www.amazon.com/Grammar-Graphics-Statistics-Computing/dp/0387245448/ref=as_li_ss_tl). The goal of this practical is to practices more advanced features of `ggplot2`.
+In the last session, we have seen how to use `ggplot2` and [The Grammar of Graphics](https://www.amazon.com/Grammar-Graphics-Statistics-Computing/dp/0387245448/ref=as_li_ss_tl). The goal of this session is to practice more advanced features of `ggplot2`.
 
-The objectives of this session will be to:
+The objectives will be to:
 
 - learn about statistical transformations
 - practices position adjustments
@@ -39,19 +43,19 @@ Like in the previous sessions, it's good practice to create a new **.R** file to
  
 ## `ggplot2` statistical transformations
 
-In the previous session, we have plotted the data as they are by using the variable values as **x** or **y** coordinates, color shade, size or transparency.
+In the previous session, we have plotted the data as they are by using the variable values as **x** or **y** coordinates, color shade, size or transparency.  
 When dealing with categorical variables, also called **factors**, it can be interesting to perform some simple statistical transformations.
 For example, we may want to have coordinates on an axis proportional to the number of records for a given category.
 
 We are going to use the `diamonds` data set included in `tidyverse`.
 
-<div class="pencadre">
+::: {.callout-exercise}
 
-- Use the `help` and `View` command to explore this data set.
-- How much records does this dataset contain ?
-- Try the `str` command, which information are displayed ?
+- Use the `help` and `View` commands to explore this data set.
+- How many records does this dataset contain ?
+- Try the `str` command. What information is displayed ?
 
-</div>
+:::
 
 ```{r str_diamon}
 str(diamonds)
@@ -59,11 +63,11 @@ str(diamonds)
 
 ### Introduction to `geom_bar`
 
-We saw scatterplot (`geom_point()`), smoothplot (`geom_smooth()`).
-Now barplot with `geom_bar()` : 
+We saw scatterplot (`geom_point()`) and smoothplot (`geom_smooth()`).
+We can also use `geom_bar()` to draw barplot:
 
 ```{r diamonds_barplot, cache = TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = diamonds, mapping = aes(x = cut)) + 
+ggplot(data = diamonds, mapping = aes(x = cut)) +
   geom_bar()
 ```
 
@@ -82,15 +86,16 @@ The figure below describes how this process works with `geom_bar()`.
 You can generally use **geoms** and **stats** interchangeably. For example, you can recreate the previous plot using `stat_count()` instead of `geom_bar()`:
 
 ```{r diamonds_stat_count, include=TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = diamonds, mapping = aes(x = cut)) + 
+ggplot(data = diamonds, mapping = aes(x = cut)) +
   stat_count()
 ```
 
-Every **geom** has a default **stat**; and every **stat** has a default **geom**. This means that you can typically use **geoms** without worrying about the underlying statistical transformation. There are three reasons you might need to use a **stat** explicitly:
+Every **geom** has a default **stat**; and every **stat** has a default **geom**. This means that you can typically use **geoms** without worrying about the underlying statistical transformation. There are three main reasons you might need to use a **stat** explicitly, we discuss them in the next two sections.
 
 ### Why **stat** ?
 
 You might want to override the default stat.
+
 For example, in the following `demo` dataset we already have a variable for the **counts** per `cut`.
 
 ```{r 3_a, include=TRUE, fig.width=8, fig.height=4.5}
@@ -104,13 +109,13 @@ demo <- tribble(
 )
 ```
 
-(Don't worry that you haven't seen `tribble()` before. You might be able
-to guess at their meaning from the context, and you will learn exactly what
+(Don't worry that you haven't seen `tribble()` before. You may be able
+to guess their meaning from the context, and you will learn exactly what
 they do soon!)
 
-<div class="pencadre">
+::: {.callout-exercise}
 So instead of using the default `geom_bar` parameter `stat = "count"` try to use `"identity"`
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -124,51 +129,44 @@ ggplot(data = demo, mapping = aes(x = cut, y = freq)) +
 You might want to override the default mapping from transformed variables to aesthetics ( e.g., proportion). 
 
 ```{r 3_b, include=TRUE, fig.width=8, fig.height=4.5}
-ggplot(data = diamonds, mapping = aes(x = cut, y = ..prop.., group = 1)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = after_stat(prop), group = 1)) +
   geom_bar()
 ```
   
-<div class="pencadre">
+::: {.callout-exercise}
 In our proportion bar chart, we need to set `group = 1`. Why?
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r diamonds_stats_challenge, include=TRUE, message=FALSE, fig.width=8, fig.height=4.5}
-ggplot(data = diamonds, mapping = aes(x = cut, y = ..prop..)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = after_stat(prop))) +
   geom_bar()
 ```
 
-If group is not used, the proportion is calculated with respect to the data that contains that field and is ultimately going to be 100% in any case. For instance, the proportion of an ideal cut in the ideal cut specific data will be 1.
+If `group` is not used, the proportion is calculated with respect to the data that contain that field and is ultimately going to be 100% in any case. For instance, the proportion of an ideal cut in the ideal cut specific data will be 1.
 </p>
 </details>
 
 ### More details with `stat_summary`
 
-<div class="pencadre">
 You might want to draw greater attention to the statistical transformation in your code. 
-you might use `stat_summary()`, which summarize the **y** values for each unique **x**
-value, to draw attention to the summary that you are computing
-</div>
+You might use `stat_summary()`, which summarize the **y** values for each unique **x**
+value, to draw attention to the summary that you are computing.
 
-<details><summary>Solution</summary>
-<p>
 ```{r 3_c, include=TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth)) +
   stat_summary()
 ```
-</p>
-</details>
 
-<div class="pencadre">
-Set the `fun.min`, `fun.max` and `fun` to the `min`, `max` and `median` function respectively
-</div>
+::: {.callout-exercise}
+Set the `fun.min`, `fun.max` and `fun` to the `min`, `max` and `median` function respectively.
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r 3_d, include=TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth)) +
   stat_summary(
     fun.min = min,
     fun.max = max,
@@ -180,34 +178,36 @@ ggplot(data = diamonds, mapping = aes(x = cut, y = depth)) +
 
 ## Coloring area plots
 
-<div class="pencadre">
-You can color a bar chart using either the `color` aesthetic, or, more usefully `fill`:
-Try both solutions on a `cut`, histogram.
-</div>
+You can color a bar chart using either the `color` aesthetic, or, more usefully `fill`.
+
+::: {.callout-exercise}
+Try both approaches on a `cut`, histogram.
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r diamonds_barplot_color, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, color = cut)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, color = cut)) +
   geom_bar()
 ```
 
 ```{r diamonds_barplot_fill, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = cut)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, fill = cut)) +
   geom_bar()
 ```
 </p>
 </details>
 
-<div class="pencadre">
-You can also use `fill` with another variable:
-Try to color by `clarity`. Is `clarity` a continuous or categorial variable ?
-</div>
+You can also use `fill` with another variable.
+
+::: {.callout-exercise}
+Try to color by `clarity`. Is `clarity` a continuous or categorical variable ?
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r diamonds_barplot_fill_clarity, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
   geom_bar()
 ```
 </p>
@@ -215,71 +215,76 @@ ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
 
 ## Position adjustments
 
-The stacking of the `fill` parameter is performed by the position adjustment `position`
+The stacking of the `fill` parameter is performed by the position adjustment `position`.
 
-<div class="pencadre">
+::: {.callout-exercise}
 Try the following `position` parameter for `geom_bar`: `"fill"`, `"dodge"` and `"jitter"`
-</div>
+:::
 
 
 <details><summary>Solution</summary>
 <p>
 ```{r diamonds_barplot_pos_fill, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) + 
-  geom_bar( position = "fill")
+ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
+  geom_bar(position = "fill")
 ```
 
 ```{r diamonds_barplot_pos_dodge, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) + 
-  geom_bar( position = "dodge")
+ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
+  geom_bar(position = "dodge")
 ```
 
 ```{r diamonds_barplot_pos_jitter, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) + 
-  geom_bar( position = "jitter")
+ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
+  geom_bar(position = "jitter")
 ```
 </p>
 </details>
 
 `jitter` is often used for plotting points when they are stacked on top of each other.
 
-<div class="pencadre">
+::: {.callout-exercise}
 Compare `geom_point` to `geom_jitter`  plot `cut` versus `depth` and color by `clarity`
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r dia_jitter2, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
   geom_point()
 ```
 
 ```{r dia_jitter3, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
   geom_jitter()
 ```
 </p>
 </details>
 
-<div class="pencadre">
+::: {.callout-exercise}
 What parameters of `geom_jitter` control the amount of jittering ?
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r dia_jitter4, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
   geom_jitter(width = .1, height = .1)
 ```
 </p>
 </details>
 
-In the `geom_jitter` plot that we made, we cannot really see the limits of the different clarity groups. Instead we can use the `geom_violin` to see their density.
+In the `geom_jitter` plot that we made, we cannot really see the limits of the different clarity groups.
+A `violin` plot can be used often to display their density.
+
+::: {.callout-exercise}
+Use `geom_violin` instead of `geom_jitter`.
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r dia_violon, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
   geom_violin()
 ```
 </p>
@@ -287,43 +292,43 @@ ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
 
 ## Coordinate systems
 
-Cartesian coordinate system where the x and y positions act independently to determine the location of each point. There are a number of other coordinate systems that are occasionally helpful.
+A Cartesian coordinate system is a coordinate system where the x and y positions act independently to determine the location of each point. There are a number of other coordinate systems that are occasionally helpful.
 
 ```{r dia_boxplot, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
   geom_boxplot()
 ```
 
-<div class="pencardre">
-Add the `coord_flip()` layer to the previous plot
-</div>
+::: {.callout-exercise}
+Add the `coord_flip()` layer to the previous plot.
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r dia_boxplot_flip, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) + 
+ggplot(data = diamonds, mapping = aes(x = cut, y = depth, color = clarity)) +
   geom_boxplot() +
   coord_flip()
 ```
 </p>
 </details>
 
-<div class="pencardre">
-Add the `coord_polar()` layer to this  plot:
+::: {.callout-exercise}
+Add the `coord_polar()` layer to the following plot.
+:::
 
 ```{r diamonds_bar, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE, eval=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = cut)) + 
-  geom_bar( show.legend = FALSE,  width = 1 ) + 
+ggplot(data = diamonds, mapping = aes(x = cut, fill = cut)) +
+  geom_bar(show.legend = FALSE, width = 1) +
   theme(aspect.ratio = 1) +
   labs(x = NULL, y = NULL)
 ```
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r diamonds_bar2, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
-ggplot(data = diamonds, mapping = aes(x = cut, fill = cut)) + 
-  geom_bar( show.legend = FALSE,  width = 1 ) + 
+ggplot(data = diamonds, mapping = aes(x = cut, fill = cut)) +
+  geom_bar(show.legend = FALSE, width = 1) +
   theme(aspect.ratio = 1) +
   labs(x = NULL, y = NULL) +
   coord_polar()
@@ -335,9 +340,9 @@ By combining the right **geom**, **coordinates** and **faceting** functions, you
 
 ## See you in [R.4: data transformation](/session_4/session_4.html) {.unnumbered .unlisted}
 
-## To go further:  animated plots from xls files
+## To go further: animated plots from xls files
 
-In order to be able to read information from a xls file, we will use the `openxlsx` packages. To generate animation we will use the `ggannimate` package. The additional `gifski` package will allow R to save your animation in the gif format (Graphics Interchange Format)
+In order to be able to read information from a xls file, we will use the `openxlsx` packages. To generate animation we will use the `ggannimate` package. The additional `gifski` package will allow R to save your animation in the GIF (Graphics Interchange Format) format.
 
 ```{r install_readxl, eval=F}
 install.packages(c("openxlsx", "gganimate", "gifski"))
@@ -348,20 +353,20 @@ library(gganimate)
 library(gifski)
 ```
 
-<div class="pencardre">
-Use the `openxlsx` package to save the [https://can.gitbiopages.ens-lyon.fr/R_basis/session_3/gapminder.xlsx](https://can.gitbiopages.ens-lyon.fr/R_basis/session_3/gapminder.xlsx) file to the `gapminder` variable
-</div>
+::: {.callout-exercise}
+Use the `openxlsx` package to save the [gapminder.xlsx](https://can.gitbiopages.ens-lyon.fr/R_basis/session_3/gapminder.xlsx) file into the `gapminder` variable.
+:::
 
 <details><summary>Solution</summary>
 <p>
 2 solutions :
 
-Use directly the url
+Use directly the url:
 ```{r load_xlsx_url, eval = F}
 gapminder <- read.xlsx("https://can.gitbiopages.ens-lyon.fr/R_basis/session_3/gapminder.xlsx")
 ```
 
-Dowload the file, save it in the same directory as your script then use the local path
+Download the file, save it in the same directory as your script then use the local path:
 ```{r load_xlsx}
 gapminder <- read.xlsx("gapminder.xlsx")
 ```
@@ -370,14 +375,15 @@ gapminder <- read.xlsx("gapminder.xlsx")
 </details>
 
 This dataset contains 4 variables of interest for us to display per country:
+
 - `gdpPercap` the GDP par capita (US$, inflation-adjusted)
 - `lifeExp` the life expectancy at birth, in years
 - `pop` the population size
 - `contient` a factor with 5 levels
 
-<div class="pencardre">
+::: {.callout-exercise}
 Using `ggplot2`, build a scatterplot of the `gdpPercap` vs `lifeExp`. Add the `pop` and `continent` information to this plot.
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -388,36 +394,37 @@ ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
 </p>
 </details>
 
-<div class="pencardre">
+::: {.callout-exercise}
 What's wrong ?
 You can use the `scale_x_log10()` to display the `gdpPercap` on the `log10` scale.
-</div>
+:::
 
 
 <details><summary>Solution</summary>
 <p>
 ```{r gapminder_plot_b}
 ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
-  geom_point() + 
+  geom_point() +
   scale_x_log10()
 ```
 </p>
 </details>
 
-<div class="pencardre">
+
 We would like to add the `year` information to the plots. We could use a `facet_wrap`, but instead we are going to use the `gganimate` package.
 
-For this we need to add a `transition_time` layer that will take as an argument `year` to our plot.
-</div>
+::: {.callout-exercise}
+Add a `transition_time` layer that will take as an argument `year` to our plot.
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r gapminder_plot_c}
 ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
-  geom_point() + 
+  geom_point() +
   scale_x_log10() +
   transition_time(year) +
-  labs(title = 'Year: {as.integer(frame_time)}')
+  labs(title = "Year: {as.integer(frame_time)}")
 ```
 </p>
 </details>
\ No newline at end of file
diff --git a/session_4/session_4.Rmd b/session_4/session_4.Rmd
index ed26d29ba5749f9ed8abf5f3140d6a65ef7b3223..537de42745f069cc89f526ab6c3d673af404842a 100644
--- a/session_4/session_4.Rmd
+++ b/session_4/session_4.Rmd
@@ -1,37 +1,42 @@
 ---
 title: "R.4: data transformation"
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr), Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+  - "Hélène Polvèche [hpolveche@istem.fr](mailto:hpolveche@istem.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
 library(fontawesome)
 
-if("conflicted" %in% .packages())
-    conflicted::conflicts_prefer(dplyr::filter)
+if ("conflicted" %in% .packages()) {
+  conflicted::conflicts_prefer(dplyr::filter)
+}
 ``` 
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
 
 ## Introduction
 
-The goal of this practical is to practice data transformation with `tidyverse`.
-The objectives of this session will be to:
+The goal of this session is to practice data transformation with `tidyverse`.
+The objectives will be to:
 
 - Filter rows with `filter()`
 - Arrange rows with `arrange()`
 - Select columns with `select()`
 - Add new variables with `mutate()`
 
-<div class="pencadre">
-For this session we are going to work with a new dataset included in the `nycflights13` package.
-Install this package and load it.
-As usual you will also need the `tidyverse` library.
-</div>
+For this session, we are going to work with a new dataset included in the `nycflights13` package.
+
+::: {.callout-exercise}
+Install this package and load it. As usual you will also need the `tidyverse` library.
+:::
 
 <details><summary>Solution</summary>
   <p> 
@@ -49,7 +54,7 @@ library("nycflights13")
  
 ### Data set : nycflights13
 
-`nycflights13::flights` Contains all 336,776 flights that departed from New York City in 2013.
+`nycflights13::flights` contains all 336,776 flights that departed from New York City in 2013.
 The data comes from the US Bureau of Transportation Statistics, and is documented in `?flights`
 
 ```R
@@ -62,7 +67,7 @@ You can display the first rows of the dataset to have an overview of the data.
 flights
 ```
 
-To know all the colnames of a table you can use the function `colnames(dataset)`
+You can use the function `colnames(dataset)` to get all the column names of a table:
 
 ```{r display_colnames, include=TRUE}
 colnames(flights)
@@ -71,9 +76,9 @@ colnames(flights)
 
 ### Data type
 
-In programming languages, all variables are not equal.
+In programming languages, variables can have different types.
 When you display a `tibble` you can see the **type** of a column.
-Here is a list of common variable **types** that you will encounter
+Here is a list of common variable **types** that you will encounter:
 
 - **int** stands for integers.
 - **dbl** stands for doubles or real numbers.
@@ -83,7 +88,8 @@ Here is a list of common variable **types** that you will encounter
 - **fctr** stands for factors, which R uses to represent categorical variables with fixed possible values.
 - **date** stands for dates.
 
-You cannot add an **int** to a **chr**, but you can add an **int** to a **dbl** the results will be a **dbl**.
+It's important for you to know about and understand the different types because certain operations are only allowed between certain types.
+For instance, you cannot add an **int** to a **chr**, but you can add an **int** to a **dbl** the results will be a **dbl**.
  
 ## `filter` rows
 
@@ -103,22 +109,22 @@ You can use the relational operators (`<`,`>`,`==`,`<=`,`>=`,`!=`) to make a tes
 
 ```{r filter_sup_eq, include=TRUE, eval=FALSE}
 filter(flights, air_time >= 680)
-filter(flights, carrier ==  "HA")
-filter(flights, origin !=  "JFK")
+filter(flights, carrier == "HA")
+filter(flights, origin != "JFK")
 ```
-The operator `%in%` is very usefull to test if a value is in a list.
+The operator `%in%` is very useful to test if a value is in a list.
 
 ```{r filter_sup_inf, include=TRUE, eval=FALSE}
-filter(flights, carrier %in% c("OO","AS"))
-filter(flights, month %in% c(5,6,7,12))
+filter(flights, carrier %in% c("OO", "AS"))
+filter(flights, month %in% c(5, 6, 7, 12))
 ```
 
 
-`dplyr` functions never modify their inputs, so if you want to save the result, you’ll need to use the assignment operator, `<-`
+`dplyr` functions never modify their inputs, so if you want to save the result, you'll need to use the assignment operator, `<-`.
 
-<div class="pencadre">
-Save the flights longer than 680 minutes in a `long_flights` variable
-</div> 
+::: {.callout-exercise}
+Save the flights longer than 680 minutes in a `long_flights` variable.
+:::
  
 <details><summary>Solution</summary>
 <p>
@@ -144,17 +150,16 @@ In R you can use the symbols `&` (and), `|` (or), `!` (not) and the function `xo
 ![](./img/transform-logical.png)
 
 
-<div class="pencadre">
-Display the `long_flights` variable and predict the results of 
+::: {.callout-exercise}
+Display the `long_flights` variable and predict the results of the following operations.
+:::
 
 ```{r logical_operators_exemples2, eval=FALSE}
-filter(long_flights,  day <= 15 & carrier == "HA")
-filter(long_flights,  day <= 15 | carrier == "HA")
-filter(long_flights,  (day <= 15 | carrier == "HA") & (! month > 2))
+filter(long_flights, day <= 15 & carrier == "HA")
+filter(long_flights, day <= 15 | carrier == "HA")
+filter(long_flights, (day <= 15 | carrier == "HA") & (!month > 2))
 ```
 
-
-</div> 
  
 <details><summary>Solution</summary>
 <p>
@@ -162,17 +167,18 @@ filter(long_flights,  (day <= 15 | carrier == "HA") & (! month > 2))
 ```{r logical_operators_exemples2_sol, include=TRUE}
 long_flights
 
-filter(long_flights,  day <= 15 & carrier == "HA")
-filter(long_flights,  day <= 15 | carrier == "HA")
-filter(long_flights,  (day <= 15 | carrier == "HA") & (! month > 2))
+filter(long_flights, day <= 15 & carrier == "HA")
+filter(long_flights, day <= 15 | carrier == "HA")
+filter(long_flights, (day <= 15 | carrier == "HA") & (!month > 2))
 ```
 </p>
 </details>
 
 
 
-<div class="pencadre">
-Test the following operations and translate them with words
+::: {.callout-exercise}
+Test the following operations and translate them with words.
+:::
 
 ```{r filter_logical_operators_a, eval=FALSE}
 filter(flights, month == 11 | month == 12)
@@ -194,28 +200,28 @@ filter(flights, arr_delay <= 120 & dep_delay <= 120)
 filter(flights, arr_delay <= 120, dep_delay <= 120)
 ```
 
-</div>
-
-Combinations of logical operators is a powerful programmatic way to select subset of data.
-Keep in mind, however, that long logical expression can be hard to read and understand, so it may be easier to apply successive small filters instead of one long one.
-
+::: {.callout-tip}
+Combining logical operators is a powerful programmatic way to select subset of data.
+However, keep in mind that long logical expression can be hard to read and understand, so it may be easier to apply successive small filters instead of a long one.
+:::
 
-<div class="pencadre">
 R either prints out the results, or saves them to a variable.
+
+::: {.callout-exercise}
 What happens when you put your variable assignment code between parenthesis `(` `)` ?
+:::
 
 ```{r filter_month_day_sav_display, eval=FALSE}
 (dec25 <- filter(flights, month == 12, day == 25))
 ```
-</div>
 
 ### Missing values
 
-One important feature of R that can make comparison tricky is missing values, or `NA`s for **Not Availables**.
-Indeed each of the variable type can contain either a value of this type (i.e., `2` for an **int**) or nothing.
+One important feature of R that can make comparison tricky are missing values, or `NA`s for **Not Availables**.
+Indeed, each of the variable type can contain either a value of this type (i.e., `2` for an **int**) or nothing.
 The *nothing recorded in a variable* status is represented with the `NA` symbol.
 
-As operations with `NA` values don't make sense, if you have `NA` somewhere in your operation, the results will be `NA`
+As operations with `NA` values don't make sense, if you have `NA` somewhere in your operation, the results will be `NA`:
 
 ```{r filter_logical_operators_NA, include=TRUE}
 NA > 5
@@ -232,9 +238,10 @@ is.na(NA)
 `filter()` only includes rows where the condition is `TRUE`; it excludes both `FALSE` and `NA` values. If you want to preserve missing values, ask for them explicitly:
 
 ```{r filter_logical_operators_test_NA2, include=TRUE}
-df <- tibble( x = c("A","B","C"),
-              y = c(1, NA, 3)
-            )
+df <- tibble(
+  x = c("A", "B", "C"),
+  y = c(1, NA, 3)
+)
 df
 filter(df, y > 1)
 filter(df, is.na(y) | y > 1)
@@ -242,11 +249,10 @@ filter(df, is.na(y) | y > 1)
 
 ### Challenges
 
-<div class="pencadre">
 Find all flights that:
+
 - Had an arrival delay (`arr_delay`) of two or more hours (you can check `?flights`)
 - Flew to Houston (IAH or HOU)
-</div>
 
 <details><summary>Solution</summary>
 <p>
@@ -256,9 +262,7 @@ filter(flights, arr_delay >= 120 & dest %in% c("IAH", "HOU"))
 </p>
 </details>
 
-<div class="pencadre">
 How many flights have a missing `dep_time` ?
-</div>
 
 <details><summary>Solution</summary>
 <p>
@@ -269,15 +273,13 @@ filter(flights, is.na(dep_time))
 </p>
 </details>
 
-<div class="pencadre">
 Why is `NA ^ 0` not missing? Why is `NA | TRUE` not missing? Why is `FALSE & NA` not missing? Can you figure out the general rule? (`NA * 0` is a tricky counterexample!)
-</div>
 
 <details><summary>Solution</summary>
 <p>
 
 ```{r filter_chalenges_d, eval=TRUE}
-NA ^ 0 # ^ 0 is always 1 it's an arbitrary rule not a computation
+NA^0 # ^ 0 is always 1 it's an arbitrary rule not a computation
 NA | TRUE # if a member of a OR operation is TRUE the results is TRUE
 FALSE & NA # if a member of a AND operation is FALSE the results is FALSE
 NA * 0 # here we have a true computation
@@ -306,9 +308,10 @@ arrange(flights, distance, desc(dep_delay))
 Missing values are always sorted at the end:
 
 ```{r arrange_NA, include=TRUE}
-df <- tibble( x = c("A","B","C"),
-              y = c(1, NA, 3)
-            )
+df <- tibble(
+  x = c("A", "B", "C"),
+  y = c(1, NA, 3)
+)
 df
 
 arrange(df, y)
@@ -316,18 +319,15 @@ arrange(df, desc(y))
 ```
 
 ### Challenges
-<div class="pencadre">
 
 - Find the most delayed flight at arrival (`arr_delay`).
 - Find the flight that left earliest (`dep_delay`).
 - How could you arrange all missing values to the start in the `df` tibble ?
 
-</div>
-
 <details><summary>Solution</summary>
 <p>
 
-Find the most delayed flight at arrival
+Find the most delayed flight at arrival.
 ```{r chalange_arrange_desc_a, include=TRUE}
 arrange(flights, desc(arr_delay))
 ```
@@ -336,7 +336,6 @@ Find the flight that left earliest.
 arrange(flights, dep_delay)
 ```
 How could you arrange all missing values to the start in the `df` tibble ?
-
 ```{r chalange_arrange_desc_c, include=TRUE}
 arrange(df, desc(is.na(y)))
 ```
@@ -346,27 +345,27 @@ arrange(df, desc(is.na(y)))
 
 ## Select columns with `select()`
 
-`select()` allows you to rapidly zoom in on a useful subset using operations based on the names of the variables.
+`select()` lets you quickly zoom in on a useful subset using operations based on variable names.
 
-You can select by column names
+You can select by column names:
 
 ```{r select_ymd_a, include=TRUE}
 select(flights, year, month, day)
 ```
 
-By defining a range of columns
+By defining a range of columns:
 
 ```{r select_ymd_b, include=TRUE}
 select(flights, year:day)
 ```
 
-Or, you can do a negative (`-`) to remove columns.
+Or, you can use a negative (`-`) to remove columns:
 
 ```{r select_ymd_c, include=TRUE}
 select(flights, -(year:day))
 ```
 
-And, you can also rename column names on the fly.
+You can also rename column names on the fly:
 
 ```{r select_ymd_d, include=TRUE}
 select(flights, Y = year, M = month, D = day)
@@ -375,21 +374,18 @@ select(flights, Y = year, M = month, D = day)
 
 ### Helper functions
 
-here are a number of helper functions you can use within `select()`:
+Here are a number of helper functions you can use within `select()`:
 
 - `starts_with("abc")`: matches column names that begin with `"abc"`.
 - `ends_with("xyz")`: matches column names that end with `"xyz"`.
 - `contains("ijk")`: matches column names that contain `"ijk"`.
 - `num_range("x", 1:3)`: matches `x1`, `x2` and `x3`.
-- `where(test_function)`: select columns for which the result is TRUE.
+- `where(test_function)`: selects columns for which the result is TRUE.
 
 See `?select` for more details.
 
 ### Challenges
 
-<div class="pencadre">
-<p>
-
 - Brainstorm as many ways as possible to select only `dep_time`, `dep_delay`, `arr_time`, and `arr_delay` from `flights`. You can associate several selections arguments with `|` , `&` and `!`. 
 
 The simplest way to start: 
@@ -399,17 +395,15 @@ df_dep_arr <- select(flights, dep_time, dep_delay, arr_time, arr_delay)
 colnames(df_dep_arr)
 ```
 
-
-
 <details><summary>Other solutions</summary>
 <p>
 
 ```{r challenge_select_a1, eval=FALSE}
 select(flights, dep_time, dep_delay, arr_time, arr_delay)
-select(flights, starts_with("dep"), starts_with("arr") )
-select(flights, starts_with("dep") | starts_with("arr") )
-select(flights, matches("^(dep|arr)") )
-select(flights, dep_time : arr_delay & !starts_with("sched"))
+select(flights, starts_with("dep"), starts_with("arr"))
+select(flights, starts_with("dep") | starts_with("arr"))
+select(flights, matches("^(dep|arr)"))
+select(flights, dep_time:arr_delay & !starts_with("sched"))
 ```
 </p>
 </details>
@@ -432,8 +426,8 @@ select(flights, all_of(vars))
 
 From the help message (`?all_of()`) :
 
- - all_of() is for strict selection. If any of the variables in the character vector is missing, an error is thrown.
- - any_of() doesn't check for missing variables. It is especially useful with negative selections, when you would like to make sure a variable is removed.
+ - `all_of()` is for strict selection. If any of the variables in the character vector is missing, an error is thrown.
+ - `any_of()` doesn't check for missing variables. It is particularly useful with negative selections, when you would like to make sure a variable is removed.
  
 ```{r challenge_select_b2, eval=FALSE}
 vars <- c(vars, "toto")
@@ -443,8 +437,7 @@ select(flights, all_of(vars))
 </p>
 </details>
 
-- Select all columns wich contain character values ? numeric values ?
-
+- Select all columns which contain character values ? numeric values ?
 
 
 <details><summary>Solution</summary>
@@ -471,39 +464,33 @@ select(flights, contains("TIME", ignore.case = FALSE))
 ```
 </p>
 </details>
-
-
-</p>
-</div>
- 
  
  
 ## Add new variables with `mutate()`
 
-It’s often useful to add new columns that are functions of existing columns. That’s the job of `mutate()`.
+It's often useful to add new columns that are functions of existing columns. That's the job of `mutate()`.
 
-<div class="pencadre">
-First let's create a thiner dataset to work on `flights_thin` that contains
+We will first create a thinner dataset `flights_thin_toy` to work on `flights_thin` that contains:
 
 - columns from `year` to `day`
-- columns that ends with `delays`
+- columns that ends with `delay`
 - the `distance` and `air_time` columns
 - the `dep_time` and `sched_dep_time` columns
 
-Then let's create an even smaller dataset as toy dataset to test your commands before using them on the large dataset (It a good reflex to take). For that you can use the function `head` or `sample_n` for a more random sampling.
-
-- select only 5 rows
+Then we will create an even smaller toy dataset `flights_thin_toy2` to test our commands before using them on the larger one (It a good reflex to take). For that you can use the function `head` or `sample_n` for a random sampling alternative.
 
-</div>
+::: {.callout-exercise}
+Create both `flights_thin_toy` and `flights_thin_toy2`, select only 5 row for the latter.
+:::
 
 
 <details><summary>Solution</summary>
 <p>
 
 ```{r mutate, include=TRUE}
-(flights_thin <- select(flights,  year:day, ends_with("delay"), distance, air_time, contains("dep_time")))
-(flights_thin_toy <- head(flights_thin, n=5))
-(flights_thin_toy2 <- sample_n(flights_thin, size=5))
+(flights_thin <- select(flights, year:day, ends_with("delay"), distance, air_time, contains("dep_time")))
+(flights_thin_toy <- head(flights_thin, n = 5))
+(flights_thin_toy2 <- sample_n(flights_thin, size = 5))
 ```
 </p>
 </details>
@@ -518,17 +505,15 @@ mutate(tbl, new_var_a = opperation_a, ..., new_var_n = opperation_n)
 
 `mutate()` allows you to add new columns (`new_var_a`, ... , `new_var_n`) and to fill them with the results of an operation.
 
-
-We can create a `gain` column whic can be the difference betwenn the delay at the departure and at the arrival to check if the pilot managed to compensate is departure delay.
+We can create a `gain` column, which can be the difference between departure and arrival delays, to check whether the pilot has managed to compensate for his departure delay.
 
 ```{r mutate_gain}
 mutate(flights_thin_toy, gain = dep_delay - arr_delay)
 ```
 
-<div class="pencadre">
-
-Using `mutate` to add a new column `gain` and `speed` that contains the average speed of the plane to the `flights_thin_toy` tibble (speed = distance / time).
-</div>
+::: {.callout-exercise}
+Use `mutate` to add a new column `gain` and `speed` that contains the average speed of the plane to the `flights_thin_toy` tibble (speed = distance / time).
+::: 
 
 <details><summary>Solution</summary>
 <p>
@@ -544,28 +529,30 @@ flights_thin_toy
 </details>
 
 
-<div class="pencadre">
-Currently `dep_time` and `sched_dep_time` are convenient to look at, but hard to compute with because they’re not really continuous numbers. (see the help to get more information on these columns) In the flight dataset, convert them to a more convenient representation of the number of minutes since midnight.
+Currently `dep_time` and `sched_dep_time` are convenient to look at, but difficult to work with, as they're not really continuous numbers (see the help to get more information on these columns).
+
+::: {.callout-exercise}
+In the flight dataset, convert `dep_time` and `sched_dep_time` to a more convenient representation of the number of minutes since midnight.
+:::
 
 **Hints** :
 
  - `dep_time` and `sched_dep_time` are in the HHMM format (see the help to get these information). So you have to first get the number of hours `HH`, convert them in minutes and then add the number of minutes `MM`.
 
- - For exemple : `20:03` will be display `2003`, so to convert it in minutes you have to do `20 * 60 + 03 (= 1203) `. 
+ - For example: `20:03` will be display `2003`, so to convert it in minutes you have to do `20 * 60 + 03 (= 1203)`. 
 
- - To split the number `HHMM` in hours  (`HH`) and minutes (`MM`) you have to use an eucledean division of HHMM by 100 to get the number of hours as the divisor and the number of minute as the remainder. For that use the modulo operator `%%` to get the remainder and it's friend `%/%` which return the divisor.
+ - To split the number `HHMM` in hours (`HH`) and minutes (`MM`) you have to use an euclidean division of HHMM by 100 to get the number of hours as the divisor and the number of minute as the remainder. For that, use the modulo operator `%%` to get the remainder and it's friend `%/%` which returns the divisor.
 
 ```{r mutate_exemple, include=TRUE}
 HH <- 2003 %/% 100
 HH
-MM <- 2003 %% 100 
+MM <- 2003 %% 100
 MM
 HH * 60 + MM
 ``` 
 It is always a good idea to decompose a problem in small parts.
-First train you only on  `dep_time`. Build the HH and MM columns. Then try to do the convertions in one row.
+First, only start with `dep_time`. Build the HH and MM columns. Then, try to write both conversions in one row.
 
-</div>
 
 <details><summary> Partial solution </summary>
 <p>
@@ -579,7 +566,7 @@ mutate(
 )
 ```
 
-** Note ** You can use the `.after` option to tell where to put the new columns
+**Note**: You can use the `.after` option to tell where to put the new columns,
 
 ```{r mutate_challenges_a2, include=TRUE}
 mutate(
@@ -587,10 +574,11 @@ mutate(
   HH = dep_time %/% 100,
   MM = dep_time %% 100,
   dep_time2 = HH * 60 + MM,
-  .after = "dep_time" )
+  .after = "dep_time"
+)
 ``` 
 
-or `.keep = "used"` to keep only the columns used for the calculus which can be usefull for debugging
+or `.keep = "used"` to keep only the columns used for the calculus which can be usefull for debugging,
 
 ```{r mutate_challenges_a21, include=TRUE}
 mutate(
@@ -598,7 +586,8 @@ mutate(
   HH = dep_time %/% 100,
   MM = dep_time %% 100,
   dep_time2 = HH * 60 + MM,
-  .keep = "used" )
+  .keep = "used"
+)
 ``` 
 
 In one row (or you can also remove columns HH and MM using select): 
@@ -606,16 +595,18 @@ In one row (or you can also remove columns HH and MM using select):
 ```{r mutate_challenges_a3, include=TRUE, eval = F}
 mutate(
   flights_thin_toy,
-  dep_time2 =  dep_time %/% 100 * 60 + dep_time %% 100,
-  .after = "dep_time" )
+  dep_time2 = dep_time %/% 100 * 60 + dep_time %% 100,
+  .after = "dep_time"
+)
 ``` 
 
-** Note ** You can also directly replace a column by the result of the mutate operation.
+**Note**: You can also directly replace a column by the result of the mutate operation,
 
 ```{r mutate_challenges_a4, include=TRUE, eval = F}
 mutate(
   flights_thin_toy,
-  dep_time = dep_time * 60 + dep_time)
+  dep_time = dep_time * 60 + dep_time
+)
 ``` 
 </p>
 </details>
@@ -626,25 +617,23 @@ mutate(
 ```{r mutate_challenges_b, eval=F, message=F, cache=T}
 mutate(
   flights,
-  dep_time = (dep_time %/% 100) * 60 +
-    dep_time %% 100,
-  sched_dep_time = (sched_dep_time %/% 100) * 60 +
-    sched_dep_time %% 100
+  dep_time = (dep_time %/% 100) * 60 + dep_time %% 100,
+  sched_dep_time = (sched_dep_time %/% 100) * 60 + sched_dep_time %% 100
 )
 ```
 
 
-
 </p>
 </details>
 
 
 ### Useful creation functions
 
-- Offsets: lead(x) and lag(x) allow you to refer to the previous or next values of the column x. This allows you to compute running differences (e.g. `x - lag(x)`) or find when values change (`x != lag(x)`).
+- Offsets: `lead(x)` and `lag(x)` allow you to refer to the previous or next values of the column x.  
+  This allows you to compute running differences (e.g. `x - lag(x)`) or find when values change (`x != lag(x)`).
 - R provides functions for running cumulative sums, products, mins and maxes: `cumsum()`, `cumprod()`, `cummin()`, `cummax()`; and dplyr provides `cummean()` for cumulative means. 
-- Logical comparisons, `<`, `<=`, `>`, `>=`, `!=`, and `==`
-- Ranking: there are a number of ranking functions, the most frequently used being min_rank(). They differ by the way ties are treated, etc. Try ?mutate, ?min_rank, ?rank, for more information.
+- Logical comparisons, `<`, `<=`, `>`, `>=`, `!=`, and `==`.
+- Ranking: there are a number of ranking functions, the most frequently used being `min_rank()`. They differ by the way ties are treated, etc. Try ?mutate, ?min_rank, ?rank, for more information.
 
 
 ## See you in [R.5: Pipping and grouping](/session_5/session_5.html) {.unnumbered .unlisted}
@@ -669,33 +658,36 @@ library(viridis)
 
 ### RColorBrewer & Ghibli 
 
-Using `mpg` and the 'ggplot2' package, reproduce the graph studied in session 2, 3.1: color mapping. 
+Using `mpg` and the ggplot2 package, reproduce the graph studied in @sec-color-mapping.
 Modify the colors representing the class of cars with the palettes `Dark2` of [RColorBrewer](https://www.datanovia.com/en/fr/blog/palette-de-couleurs-rcolorbrewer-de-a-a-z/), then `MononokeMedium` from [Ghibli](https://github.com/ewenme/ghibli). 
 
 ```{r mpg_color}
-ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
+ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) +
   geom_point()
 ```
+
+::: {.callout-exercise}
 Go to the links to find the appropriate function: they are very similar between the two packages.
+:::
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
 ```{r mpg_color1}
-ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
+ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) +
   geom_point() +
   scale_color_brewer(palette = "Dark2")
 ```
 
 ```{r mpg_color2}
-ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) + 
+ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) +
   geom_point() +
   scale_colour_ghibli_d("MononokeMedium")
 ```
-  </p>
+</p>
 </details>
  
-The choice of colors is very important for the comprehension of a graphic. Some palettes are not suitable for everyone. For example, for people with color blindness, color gradients from green to red, or from yellow to blue should be avoided. 
+The choice of colors is very important for the comprehension of a graphic. Some palettes are not suitable for everyone. For example, for people with color blindness, color gradients from green to red, or from yellow to blue should be avoided.
 
 To display only Brewer palettes that are colorblind friendly, specify the option `colorblindFriendly = TRUE` as follows:
 
@@ -705,18 +697,21 @@ display.brewer.all(colorblindFriendly = TRUE)
 
 ### Viridis
 
-`viridis` package provide a series of color maps that are designed to improve graph readability for readers with common forms of color blindness and/or color vision deficiency. 
+The `viridis` package provides a series of color maps that are designed to improve graph readability for readers with common forms of color blindness and/or color vision deficiency. 
 
-For the next part, we will use a real data set. Anterior tibial muscle tissue was collected from 20 patients, with or without confirmed myotonic dystrophy type 1 (DM1). Illumina RNAseq was performed on these samples and the sequencing data are available on GEO with the identifier GSE86356. 
+For the next part, we will use a real data set. Anterior tibial muscle tissue was collected from 20 patients, with or without confirmed myotonic dystrophy type 1 (DM1). Illumina RNAseq was performed on these samples and the sequencing data are available on GEO with the identifier GSE86356.
 
 First, we will use the gene count table of these samples, formatted for use in ggplot2 ( `pivot_longer()` [function](https://tidyr.tidyverse.org/reference/pivot_longer.html) ).
 
-Open the csv file using the `read_csv2()` function. The file is located at "https://can.gitbiopages.ens-lyon.fr/R_basis/session_4/Expression_matrice_pivot_longer_DEGs_GSE86356.csv".
+::: {.callout-exercise}
+Open the csv file using the `read_csv2()` function. The file is located at:  
+<span style="font-size:0.75em;"><i>https://can.gitbiopages.ens-lyon.fr/R_basis/session_4/Expression_matrice_pivot_longer_DEGs_GSE86356.csv</i></span>
+:::
 
 <details><summary>Solution</summary>
 <p>
 
-Download the Expression_matrice_pivot_longer_DEGs_GSE86356.csv file and save it in your working directory.
+Download the file "Expression_matrice_pivot_longer_DEGs_GSE86356.csv" and save it in your working directory.
 You may have to set you working directory using `setwd()`
 
 ```{r read_csv1}
@@ -725,7 +720,7 @@ expr_DM1 <- read_csv2("Expression_matrice_pivot_longer_DEGs_GSE86356.csv")
 expr_DM1
 ```
 
-or you can read it from the url
+or you can read it from the following url:
 
 ```{r read_csv1_url, eval = F}
 (expr_DM1 <- read_csv2("https://can.gitbiopages.ens-lyon.fr/R_basis/session_4/Expression_matrice_pivot_longer_DEGs_GSE86356.csv"))
@@ -734,37 +729,41 @@ or you can read it from the url
 </p>
 </details>
 
+::: {.callout-exercise}
 With this tibble, use `ggplot2` and the `geom_tile()` function to make a heatmap.
 Fit the samples on the x-axis and the genes on the y-axis.
+:::
 
-**Tip:** Transform the counts into log10(x + 1) for a better visualization.
+**Tip**: Transform the counts into log10(x + 1) for a better visualization.
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
 ```{r heatmap1}
 (DM1_tile_base <-
   ggplot(expr_DM1, aes(samples, Genes, fill = log1p(counts))) +
   geom_tile() +
-  labs(y="Genes", x = "Samples") +
+  labs(y = "Genes", x = "Samples") +
   theme(
-    axis.text.y = element_text(size= 6),
+    axis.text.y = element_text(size = 6),
     axis.text.x = element_text(size = 6, angle = 90)
   ))
 ```
-**Nota bene :** The elements of the axes, and the theme in general, are modified in the `theme()` function. 
-  </p>
+**Nota bene**: The elements of the axes, and the theme in general, are modified in the `theme()` function. 
+</p>
 </details>
 
 With the default color gradient, even with the transformation, the heatmap is difficult to study. 
 
-R interprets a large number of colors, indicated in RGB, hexadimal, or just by name. For example : 
+R interprets a large number of colors, indicated in RGB, hexadecimal, or just by name. For example : 
 
 <center>
 ![](./img/colorsR.png){width=400px}
 </center>
 
-With `scale_fill_gradient2()` function, change the colors of the gradient, taking white for the minimum value and 'springgreen4' for the maximum value.
+::: {.callout-exercise}
+With `scale_fill_gradient2()` function, change the colors of the gradient, taking "white" for the minimum value and "springgreen4" for the maximum value.
+:::
 
 <details><summary>Solution</summary>
   <p>
@@ -776,28 +775,34 @@ DM1_tile_base + scale_fill_gradient2(low = "white", high = "springgreen4")
   </p>
 </details>
 
-It s better, but still not perfect!
-Now let s use the [viridis color gradient](https://gotellilab.github.io/GotelliLabMeetingHacks/NickGotelli/ViridisColorPalette.html) for this graph.
+It's better, but still not perfect!
+
+::: {.callout-exercise}
+Use the [viridis color gradient](https://gotellilab.github.io/GotelliLabMeetingHacks/NickGotelli/ViridisColorPalette.html) for this graph.
+:::
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
 ```{r heatmapViridis}
 DM1_tile_base + scale_fill_viridis_c()
 ```
-  </p>
+</p>
 </details>
 
 ### Volcano Plot 
 
 For this last exercise, we will use the results of the differential gene expression analysis between DM1 vs WT conditions. 
 
-Open the csv file using the `read_csv2()` function. The file is located at "http://can.gitbiopages.ens-lyon.fr/R_basis/session_4/EWang_Tibialis_DEGs_GRCH37-87_GSE86356.csv".
+::: {.callout-exercise}
+Open the csv file using the `read_csv2()` function. The file is located at:  
+<span style="font-size:0.75em;"><i>http://can.gitbiopages.ens-lyon.fr/R_basis/session_4/EWang_Tibialis_DEGs_GRCH37-87_GSE86356.csv</i></span>
+:::
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
-Download the "EWang_Tibialis_DEGs_GRCH37-87_GSE86356.csv" file and save it in your working directory.
+Download the file "EWang_Tibialis_DEGs_GRCH37-87_GSE86356.csv" and save it in your working directory.
 
 ```{r read_csv2}
 tab <- read_csv2("EWang_Tibialis_DEGs_GRCH37-87_GSE86356.csv")
@@ -811,39 +816,47 @@ tab
 ```
 
 
-
-
-  </p>
+</p>
 </details>
 
-To make a Volcano plot, displaying different information about the significativity of the variation thanks to the colors, we will have to make a series of modifications on this table.
+To make a Volcano plot, displaying different information on the significance of variation using colors, we will have to make a series of modifications on this table.
+
+With `mutate()` and `ifelse()` [fonctions](https://dplyr.tidyverse.org/reference/if_else.html), we will have to create: 
 
-With `mutate()` and `ifelse()` [fonctions](https://dplyr.tidyverse.org/reference/if_else.html), we will have to create : 
+- a column `sig`: it indicates if the gene is significant ( TRUE or FALSE ).  
+  **Thresholds**: baseMean > 20 and  padj < 0.05 and abs(log2FoldChange) >= 1.5
 
-- a column 'sig' : it indicates if the gene is significant ( TRUE or FALSE ). 
-**Thresholds :** baseMean > 20 and  padj < 0.05 and abs(log2FoldChange) >= 1.5 
+- a column `UpDown`: it indicates if the gene is significantly up-regulated (Up), down-regulated (Down), or not significantly regulated (NO).
 
-- a column 'UpDown' : it indicates if the gene is significantly up-regulated (Up), down-regulated (Down), or not significantly regulated (NO).
+::: {.callout-exercise}
+Create the columns `sig` and `UpDown`.
+:::
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
 ```{r sig}
-(tab.sig <- mutate(tab,
-                   sig = baseMean > 20 & padj < 0.05 & abs(log2FoldChange) >= 1.5,
-                   UpDown = ifelse(sig, ### we can use in the same mutate a column created by a previous line
-                                  ifelse(log2FoldChange > 0, "Up", "Down"), "NO")
-                   )
+(
+  tab.sig <- mutate(
+    tab,
+    sig = baseMean > 20 & padj < 0.05 & abs(log2FoldChange) >= 1.5,
+    UpDown = ifelse(sig, ### we can use in the same mutate a column created by a previous line
+      ifelse(log2FoldChange > 0, "Up", "Down"), "NO"
+    )
+  )
 )
 ```
-  </p>
+</p>
 </details>
 
 We want to see the top10 DEGs on the graph. For this, we will use the package `ggrepel`.
+
+::: {.callout-exercise}
 Install and load the `ggrepel` package.
+:::
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
 ```{r ggrepel, eval = F}
 install.packages("ggrepel")
@@ -852,14 +865,17 @@ install.packages("ggrepel")
 ```{r ggrepel2}
 library(ggrepel)
 ```
-  </p>
+</p>
 </details>
 
 
-Let's **filter** out table into a new variable, top10, to keep only the significant differentialy expressed genes with the top 10 adjusted pvalue. The **smaller** the adjusted pvalue, the more significant.
+Let's **filter** out the table into a new variable, `top10`, to keep only the significant differentially expressed genes, those with the top 10 adjusted pvalue. The **smaller** the adjusted pvalue, the more significant the gene.
 
+::: {.callout-exercise}
+Create the new variable `top10`.
+:::
 
-**Tips :**  You can use the [function](https://dplyr.tidyverse.org/reference/slice.html) `slice_min()`
+**Tip**: You can use the [function](https://dplyr.tidyverse.org/reference/slice.html) `slice_min()`.
 
 <details><summary>Solution</summary>
 <p>
@@ -879,44 +895,44 @@ Let's **filter** out table into a new variable, top10, to keep only the signific
 
 The data is ready to be used to make a volcano plot!  
 
-<div class="pencadre">
+::: {.callout-exercise}
 To make the graph below, use `ggplot2`, the functions `geom_point()`, `geom_hline()`, `geom_vline()`, `theme_minimal()`, `theme()` (to remove the legend), `geom_label_repel()` and the function `scale_color_manual()` for the colors.
-</div> 
+:::
 
 
-- **Tips 1 :** Don t forget the transformation of the adjusted pvalue.
-- **Tips 2 :** Feel free to search your favorite Web browser for help. 
-- **Tips 3 :** `geom_label_repel()` function needs a new parameter 'data' and 'label' in aes parameters. 
+- **Tips 1**: Don't forget the transformation of the adjusted pvalue.
+- **Tips 2**: Feel free to search your favorite Web browser for help. 
+- **Tips 3**: `geom_label_repel()` function needs a new parameter 'data' and 'label' in `aes` parameters. 
 
 
 ```{r VolcanoPlotDemo, echo = FALSE}
 ggplot(tab.sig, aes(x = log2FoldChange, y = -log10(padj), color = UpDown)) +
   geom_point() +
-  scale_color_manual(values=c("steelblue", "lightgrey", "firebrick" )) +
-  geom_hline(yintercept=-log10(0.05), col="black") +
-  geom_vline(xintercept=c(-1.5, 1.5), col="black") +
+  scale_color_manual(values = c("steelblue", "lightgrey", "firebrick")) +
+  geom_hline(yintercept = -log10(0.05), col = "black") +
+  geom_vline(xintercept = c(-1.5, 1.5), col = "black") +
   theme_minimal() +
-  theme(legend.position="none") +
-  labs(y="-log10(p-value)", x = "log2(FoldChange)") +
-  geom_label_repel(data = top10, mapping = aes(label = gene_symbol)) 
+  theme(legend.position = "none") +
+  labs(y = "-log10(p-value)", x = "log2(FoldChange)") +
+  geom_label_repel(data = top10, mapping = aes(label = gene_symbol))
 
 ```
 
 <details><summary>Solution</summary>
-  <p>
+<p>
 
 ```{r VolcanoPlotSolut, echo = TRUE, results = 'hide'}
 ggplot(tab.sig, aes(x = log2FoldChange, y = -log10(padj), color = UpDown)) +
   geom_point() +
-  scale_color_manual(values=c("steelblue", "lightgrey", "firebrick" )) +
-  geom_hline(yintercept=-log10(0.05), col="black") +
-  geom_vline(xintercept=c(-1.5, 1.5), col="black") +
+  scale_color_manual(values = c("steelblue", "lightgrey", "firebrick")) +
+  geom_hline(yintercept = -log10(0.05), col = "black") +
+  geom_vline(xintercept = c(-1.5, 1.5), col = "black") +
   theme_minimal() +
-  theme(legend.position="none") +
-  labs(y="-log10(p-value)", x = "log2(FoldChange)") +
-  geom_label_repel(data = top10, mapping = aes(label = gene_symbol)) 
+  theme(legend.position = "none") +
+  labs(y = "-log10(p-value)", x = "log2(FoldChange)") +
+  geom_label_repel(data = top10, mapping = aes(label = gene_symbol))
 
 ```
-  </p>
+</p>
 </details>
 
diff --git a/session_5/session_5.Rmd b/session_5/session_5.Rmd
index 704c36433b67475357d02b5c885facc4b4454fe5..64c00e6282e866fae1e19e36280ac666fa68c4ae 100644
--- a/session_5/session_5.Rmd
+++ b/session_5/session_5.Rmd
@@ -1,35 +1,40 @@
 ---
 title: "R.5: Pipping and grouping"
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
 library(fontawesome)
 
-if("conflicted" %in% .packages())
-    conflicted::conflicts_prefer(dplyr::filter)
+if ("conflicted" %in% .packages()) {
+  conflicted::conflicts_prefer(dplyr::filter)
+}
 ```
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
 
 ## Introduction
 
-The goal of this practical is to practice combining data transformation with `tidyverse`.
-The objectives of this session will be to:
+The goal of this session is to practice combining data transformation with `tidyverse`.
+The objectives will be to:
 
 - Combining multiple operations with the pipe `%>%`
 - Work on subgroup of the data with `group_by`
 
-<div class="pencadre">
-For this session we are going to work with a new dataset included in the `nycflights13` package.
-Install this package and load it.
-As usual you will also need the `tidyverse` library.
-</div>
+
+For this session, we are going to work with a new dataset included in the `nycflights13` package.
+
+::: {.callout-exercise}
+Install this package and load it. As usual you will also need the `tidyverse` library.
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -42,50 +47,56 @@ library("nycflights13")
 
 ## Combining multiple operations with the pipe
 
-<div id="pencadre">
-Find the 10 most delayed flights using a ranking function. `min_rank()`
-</div>
+::: {.callout-exercise}
+Find the 10 most delayed flights using the ranking function `min_rank()`.
+:::
 
 <details><summary>Solution</summary>
 <p>
 ```{r pipe_example_a, include=TRUE}
-flights_md <- mutate(flights,
-                     most_delay = min_rank(desc(dep_delay)))
+flights_md <- mutate(
+  flights,
+  most_delay = min_rank(desc(dep_delay))
+)
 flights_md <- filter(flights_md, most_delay < 10)
 flights_md <- arrange(flights_md, most_delay)
 ```
 </p>
 </details>
 
-
 We don't want to create useless intermediate variables so we can use the pipe operator: `%>%`
 (or `ctrl + shift + M`). 
 
 Behind the scenes, `x %>% f(y)` turns into `f(x, y)`, and `x %>% f(y) %>% g(z)` turns into `g(f(x, y), z)` and so on. You can use the pipe to rewrite multiple operations in a way that you can read left-to-right, top-to-bottom. 
 
-<div id="pencadre">
+::: {.callout-exercise}
 Try to pipe operators to rewrite your precedent code with only **one** variable assignment.
-</div>
+:::
  
 <details><summary>Solution</summary>
 <p>
 ```{r pipe_example_b, include=TRUE}
 flights_md2 <- flights %>%
-    mutate(most_delay = min_rank(desc(dep_delay))) %>%
-    filter(most_delay < 10) %>%
-    arrange(most_delay)
+  mutate(most_delay = min_rank(desc(dep_delay))) %>%
+  filter(most_delay < 10) %>%
+  arrange(most_delay)
 ```
 </p>
 </details>
 
-Working with the pipe is one of the key criteria for belonging to the `tidyverse`. The only exception is `ggplot2`: it was written before the pipe was discovered and use `+` instead of `%>%`. Unfortunately, the next iteration of `ggplot2`, `ggvis`, which does use the pipe, isn’t quite ready for prime time yet.
+Working with the pipe is one of the key criteria for belonging to the `tidyverse`. The only exception is `ggplot2`: it was written before the pipe was discovered and use `+` instead of `%>%`.  
+<!-- ggvis project is dormant
+Unfortunately, the next iteration of `ggplot2`, `ggvis`, which does use the pipe, isn't quite ready for prime time yet.
+-->
 
-The pipe is a powerful tool, but it’s not the only tool at your disposal, and it doesn’t solve every problem! Pipes are most useful for rewriting a fairly short linear sequence of operations. I think you should reach for another tool when:
+The pipe is a powerful tool, but it's not the only tool at your disposal, and it doesn't solve every problem! Pipes are most useful for rewriting a fairly short linear sequence of operations.
 
 ### When not to use the pipe
 
+You should reach for another tool when:
+
 - Your pipes are longer than (say) ten steps. In that case, create intermediate functions with meaningful names. That will make debugging easier, because you can more easily check the intermediate results, and it makes it easier to understand your code, because the variable names can help communicate intent.
-- You have multiple inputs or outputs. If there isn’t one primary object being transformed, but two or more objects being combined together, don’t use the pipe. You can create a function that combines or split the results.
+- You have multiple inputs or outputs. If there isn't one primary object being transformed, but two or more objects being combined together, don't use the pipe. You can create a function that combines or split the results.
 
 ## Grouping variable
 
@@ -93,105 +104,107 @@ The `summarise()` function collapses a data frame to a single row.
 Check the difference between `summarise()` and `mutate()` with the following commands:
 
 ```{r load_data, eval=FALSE}
-flights %>% 
+flights %>%
   mutate(delay = mean(dep_delay, na.rm = TRUE))
-flights %>% 
+flights %>%
   summarise(delay = mean(dep_delay, na.rm = TRUE))
 ```
 
-Where mutate compute the `mean` of `dep_delay` row by row (which is not useful), `summarise` compute the `mean` of the whole `dep_delay` column.
+Whereas mutate compute the `mean` of `dep_delay` row by row (which is not useful), `summarise` compute the `mean` of the whole `dep_delay` column.
 
 ### The power of `summarise()` with `group_by()`
 
 The `group_by()` function changes the unit of analysis from the complete dataset to individual groups.
-Individual groups are defined by categorial variable or **factors**.
-Then, when you use the function you already know on grouped data frame and they’ll be automatically applied *by groups*.
+Individual groups are defined by categorical variable or **factors**.
+Then, when you use aggregation functions on the grouped data frame, they'll be automatically applied *by groups*.
 
 You can use the following code to compute the average delay per months across years.
 
 ```{r summarise_group_by, include=TRUE, fig.width=8, fig.height=3.5}
-flights_delay <- flights %>% 
-  group_by(year, month) %>% 
-  summarise(delay = mean(dep_delay, na.rm = TRUE), sd = sd(dep_delay, na.rm = TRUE)) %>% 
+flights_delay <- flights %>%
+  group_by(year, month) %>%
+  summarise(delay = mean(dep_delay, na.rm = TRUE), sd = sd(dep_delay, na.rm = TRUE)) %>%
   arrange(month)
 
 ggplot(data = flights_delay, mapping = aes(x = month, y = delay)) +
-  geom_bar(stat="identity", color="black", fill = "#619CFF") +
-  geom_errorbar(mapping = aes( ymin=0, ymax=delay+sd)) + 
+  geom_bar(stat = "identity", color = "black", fill = "#619CFF") +
+  geom_errorbar(mapping = aes(ymin = 0, ymax = delay + sd)) +
   theme(axis.text.x = element_blank())
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 Why did we `group_by` `year` and `month` and not only `year` ?
-</div>
+:::
 
 ### Missing values
 
-<div class="pencadre">
-You may have wondered about the `na.rm` argument we used above. What happens if we don’t set it?
-</div>
+::: {.callout-exercise}
+You may have wondered about the `na.rm` argument we used above. What happens if we don't set it?
+:::
 
 ```{r summarise_group_by_NA, include=TRUE}
-flights %>% 
-  group_by(dest) %>% 
+flights %>%
+  group_by(dest) %>%
   summarise(
     dist = mean(distance),
     delay = mean(arr_delay)
   )
 ```
 
-Aggregation functions obey the usual rule of missing values: **if there’s any missing value in the input, the output will be a missing value**.
+Aggregation functions obey the usual rule of missing values: **if there's any missing value in the input, the output will be a missing value**.
 
 ### Counts
 
-Whenever you do any aggregation, it’s always a good idea to include either a count (`n()`). That way you can check that you’re not drawing conclusions based on very small amounts of data.
+Whenever you do any aggregation, it's always a good idea to include a count (`n()`). This way, you can check that you're not drawing conclusions based on very small amounts of data.
 
 ```{r summarise_group_by_count, include = T, echo=F, warning=F, message=F, fig.width=8, fig.height=3.5}
-summ_delay_filghts <- flights %>% 
-  group_by(dest) %>% 
+summ_delay_filghts <- flights %>%
+  group_by(dest) %>%
   summarise(
     n_flights = n(),
     avg_distance = mean(distance, na.rm = TRUE),
     avg_delay = mean(arr_delay, na.rm = TRUE)
-  ) %>% 
-  filter(dest != "HNL") %>% 
+  ) %>%
+  filter(dest != "HNL") %>%
   filter(avg_delay < 40 & avg_delay > -20)
 
 ggplot(summ_delay_filghts, mapping = aes(x = avg_distance, y = avg_delay, size = n_flights)) +
   geom_point() +
   geom_smooth(method = lm, se = FALSE) +
-  theme(legend.position='none')
+  theme(legend.position = "none")
 ```
 
-<div class="pencadre">
+::: {.callout-exercise}
 Imagine that we want to explore the relationship between the average distance (`distance`) and average delay (`arr_delay`) for each location (`dest`) and recreate the above figure. 
-here are three steps to prepare this data: 
+:::
+
+**Hints** Here are the steps to prepare those data:
 
 1. Group flights by destination.
 2. Summarize to compute average distance (`avg_distance`), average delay (`avg_delay`), and number of flights using `n()` (`n_flights`).
-3. Filter to remove Honolulu airport, which is almost twice as far away as the next closest airport.
+3. Filter to remove Honolulu airport ("HNL"), which is almost twice as far away as the next closest airport.
 4. Filter to remove noisy points with delay superior to 40 or inferior to -20
 5. Create a `mapping` on `avg_distance`, `avg_delay` and `n_flights` as `size`.
 6. Use the layer `geom_point()` and `geom_smooth()` (use method = lm)
 7. We can hide the legend with the layer `theme(legend.position='none')`
-</div>
+
 
 <details><summary>Solution</summary>
 <p>
 ```{r summarise_group_by_count_b, include = T, eval=F, warning=F, message=F, fig.width=8, fig.height=3.5}
-flights %>% 
-  group_by(dest) %>% 
+flights %>%
+  group_by(dest) %>%
   summarise(
     n_flights = n(),
     avg_distance = mean(distance, na.rm = TRUE),
     avg_delay = mean(arr_delay, na.rm = TRUE)
-  ) %>% 
-  filter(dest != "HNL") %>% 
-  filter(avg_delay < 40 & avg_delay > -20) %>% 
+  ) %>%
+  filter(dest != "HNL") %>%
+  filter(avg_delay < 40 & avg_delay > -20) %>%
   ggplot(mapping = aes(x = avg_distance, y = avg_delay, size = n_flights)) +
   geom_point() +
   geom_smooth(method = lm, se = FALSE) +
-  theme(legend.position='none')
+  theme(legend.position = "none")
 ```
 </p>
 </details>
@@ -201,13 +214,13 @@ flights %>%
 
 If you need to remove grouping, and return to operations on ungrouped data, use `ungroup()`.
 
-<div class="pencadre">
-Try the following example
-</div>
+::: {.callout-exercise}
+Try the following example.
+:::
 
 ```{r ungroup, eval=T, message=FALSE, cache=T}
-flights %>% 
-  group_by(year, month, day) %>% 
+flights %>%
+  group_by(year, month, day) %>%
   ungroup() %>%
   summarise(delay = mean(dep_delay, na.rm = TRUE))
 ```
@@ -216,12 +229,9 @@ flights %>%
 
 ### First challenge
 
-<div class="pencadre">
-
-
 Look at the number of canceled flights per day. Is there a pattern?
 
-(A canceled flight is a flight where the `dep_time` or the `arr_time` is `NA`)
+(A canceled flight is a flight where either the `dep_time` or the `arr_time` is `NA`)
 
 **Remember to always try to decompose complex questions into smaller and simple problems**
 
@@ -232,18 +242,17 @@ Look at the number of canceled flights per day. Is there a pattern?
 - We can use `geom_col` to have a barplot of the number of `cancel_day` for each. `wday`
 - You can use the function `fct_reorder()` to reorder the `wday` by number of `cancel_day` and make the plot easier to read.
 
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r grouping_challenges_a, eval=T, message=FALSE, cache=T}
-flights %>% 
+flights %>%
   mutate(
     canceled = is.na(dep_time) | is.na(arr_time)
-  ) %>% 
-  filter(canceled) %>% 
-  mutate(wday = strftime(time_hour,'%A')) %>% 
-  group_by(wday) %>% 
+  ) %>%
+  filter(canceled) %>%
+  mutate(wday = strftime(time_hour, "%A")) %>%
+  group_by(wday) %>%
   summarise(
     cancel_day = n()
   ) %>%
@@ -255,24 +264,22 @@ flights %>%
 
 ### Second challenge
 
-<div class="pencadre">
 Is the proportion of canceled flights by day of the week related to the average departure delay?
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r grouping_challenges_b1, eval=T, message=FALSE, cache=T, fig.width=8, fig.height=3.5}
-flights %>% 
+flights %>%
   mutate(
     canceled = is.na(dep_time) | is.na(arr_time)
-  ) %>% 
-  mutate(wday = strftime(time_hour,'%A')) %>% 
-  group_by(wday) %>% 
+  ) %>%
+  mutate(wday = strftime(time_hour, "%A")) %>%
+  group_by(wday) %>%
   summarise(
-    prop_cancel_day = sum(canceled)/n(),
+    prop_cancel_day = sum(canceled) / n(),
     av_delay = mean(dep_delay, na.rm = TRUE)
   ) %>%
-  ungroup() %>% 
+  ungroup() %>%
   ggplot(mapping = aes(x = av_delay, y = prop_cancel_day, color = wday)) +
   geom_point()
 ```
@@ -281,82 +288,86 @@ Which day would you prefer to book a flight ?
 </p>
 </details>
 
-<div class="pencadre">
 We can add error bars to this plot to justify our decision.
 Brainstorm a way to have access to the mean and standard deviation or the `prop_cancel_day` and `av_delay`.
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r grouping_challenges_b2, eval=T, message=FALSE, cache=T, fig.width=8, fig.height=3.5}
-flights %>% 
+flights %>%
   mutate(
     canceled = is.na(dep_time) | is.na(arr_time)
-  ) %>% 
-  mutate(wday = strftime(time_hour,'%A')) %>% 
-  group_by(day) %>% 
+  ) %>%
+  mutate(wday = strftime(time_hour, "%A")) %>%
+  group_by(day) %>%
   mutate(
-    prop_cancel_day = sum(canceled)/sum(!canceled),
+    prop_cancel_day = sum(canceled) / sum(!canceled),
     av_delay = mean(dep_delay, na.rm = TRUE)
   ) %>%
-  group_by(wday) %>% 
+  group_by(wday) %>%
   summarize(
     mean_cancel_day = mean(prop_cancel_day, na.rm = TRUE),
     sd_cancel_day = sd(prop_cancel_day, na.rm = TRUE),
     mean_av_delay = mean(av_delay, na.rm = TRUE),
     sd_av_delay = sd(av_delay, na.rm = TRUE)
-  ) %>% 
+  ) %>%
   ggplot(mapping = aes(x = mean_av_delay, y = mean_cancel_day, color = wday)) +
   geom_point() +
-  geom_errorbarh(mapping = aes(
-    xmin = -sd_av_delay + mean_av_delay,
-    xmax = sd_av_delay + mean_av_delay
-  )) +
-  geom_errorbar(mapping = aes(
-    ymin = -sd_cancel_day + mean_cancel_day,
-    ymax = sd_cancel_day + mean_cancel_day
-  ))
+  geom_errorbarh(
+    mapping = aes(
+      xmin = -sd_av_delay + mean_av_delay,
+      xmax = sd_av_delay + mean_av_delay
+    )
+  ) +
+  geom_errorbar(
+    mapping = aes(
+      ymin = -sd_cancel_day + mean_cancel_day,
+      ymax = sd_cancel_day + mean_cancel_day
+    )
+  )
 ```
 </p>
 </details>
 
-<div class="pencadre">
+
 Now that you are aware of the interest of using `geom_errorbar`, what `hour` of the day should you fly if you want to avoid delays as much as possible?
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r group_filter_b3, eval=T, warning=F, message=FALSE, cache=T, fig.width=8, fig.height=3.5}
-flights %>% 
-  group_by(hour) %>% 
+flights %>%
+  group_by(hour) %>%
   summarise(
     mean_delay = mean(arr_delay, na.rm = T),
     sd_delay = sd(arr_delay, na.rm = T),
-  ) %>% 
+  ) %>%
   ggplot() +
-  geom_errorbar(mapping = aes(
-    x = hour,
-    ymax = mean_delay + sd_delay,
-    ymin = mean_delay - sd_delay)) +
-  geom_point(mapping = aes(
-    x = hour,
-    y = mean_delay,
-  ))
+  geom_errorbar(
+    mapping = aes(
+      x = hour,
+      ymax = mean_delay + sd_delay,
+      ymin = mean_delay - sd_delay
+    )
+  ) +
+  geom_point(
+    mapping = aes(
+      x = hour,
+      y = mean_delay,
+    )
+  )
 ```
 </p>
 </details>
 
 ### Third challenge
 
-<div class="pencadre">
 Which carrier has the worst delays?
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r grouping_challenges_c2, eval=F, echo = T, message=FALSE, cache=T}
-flights %>% 
-  group_by(carrier) %>% 
+flights %>%
+  group_by(carrier) %>%
   summarise(
     carrier_delay = mean(arr_delay, na.rm = T)
   ) %>%
@@ -367,15 +378,13 @@ flights %>%
 </p>
 </details>
 
-<div class="pencadre">
 Can you disentangle the effects of bad airports vs. bad carriers? (Hint: think about `group_by(carrier, dest) %>% summarise(n=n())`)
-</div>
 
 <details><summary>Solution</summary>
 <p>
 ```{r grouping_challenges_c1, eval=F, echo = T, message=FALSE, cache=T}
-flights %>% 
-  group_by(carrier, dest) %>% 
+flights %>%
+  group_by(carrier, dest) %>%
   summarise(
     carrier_delay = mean(arr_delay, na.rm = T),
     number_of_flight = n()
diff --git a/session_6/session_6.Rmd b/session_6/session_6.Rmd
index d7f621f4040d292b1de94f1f9fe77beaafed9cd7..3413668637938bb78ca1c8c8c248b83e135a7faa 100644
--- a/session_6/session_6.Rmd
+++ b/session_6/session_6.Rmd
@@ -1,7 +1,11 @@
 ---
 title: "R.6: tidydata"
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr);\nCarine Rey [carine.rey@ens-lyon.fr](mailto:carine.rey@ens-lyon.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+  - "Carine Rey [carine.rey@ens-lyon.fr](mailto:carine.rey@ens-lyon.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
@@ -9,16 +13,16 @@ library(fontawesome)
 ``` 
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
 
 ## Introduction
 
-Until now we have worked with data already formated in a *nice way*.
-In the `tidyverse` data formated in a *nice way* are called **tidy**
-The goal of this practical is to understand how to transform an hugly blob of information into a **tidy** data set.
+Until now we have worked with data already formatted in a *nice way*.
+In the `tidyverse` data formatted in a *nice way* are called **tidy**
+The goal of this session is to understand how to transform an ugly blob of information into a **tidy** data set.
 
 ### Tidydata
 
@@ -28,13 +32,13 @@ There are three interrelated rules which make a dataset tidy:
 - Each observation must have its own row.
 - Each value must have its own cell.
 
-Doing this kind and transformation is often called **data wrangling**, due to the felling that we have to *wrangle* with the data to force them into a **tidy** format.
+Doing this kind and transformation is often called **data wrangling**, due to the feeling that we have to *wrangle* (struggle) with the data to force them into a **tidy** format.
 
-But once this step is finish most of the subsequent analysis will be realy fast to do !
+But once this step is finish most of the subsequent analysis will be really fast to do !
 
-<div class="pencadre">
+::: {.callout-exercise}
 As usual we will need the `tidyverse` library.
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -44,11 +48,11 @@ library(tidyverse)
 </p>
 </details>
 
-For this practical we are going to use the `table` set of datasets which demonstrate multiple ways to layout the same tabular data.
+For this session, we are going to use the `table*` set of datasets which demonstrate multiple ways to layout the same tabular data.
 
-<div class="pencadre">
-Use the help to know more about `table1` dataset
-</div>
+::: {.callout-exercise}
+Use the help to know more about `table1` dataset.
+:::
 
 <details><summary>Solution</summary>
 
@@ -72,25 +76,28 @@ knitr::include_graphics('img/pivot_longer.png')
 ```
 
 ```{r, eval = F}
-wide_example <- tibble(X1 = c("A","B"),
-                      X2 = c(1,2),
-                      X3 = c(0.1,0.2),
-                      X4 = c(10,20))
+wide_example <- tibble(
+  X1 = c("A", "B"),
+  X2 = c(1, 2),
+  X3 = c(0.1, 0.2),
+  X4 = c(10, 20)
+)
 ```
 
 If you have a wide dataset, such as `wide_example`, that you want to make longer, you will use the `pivot_longer()` function. 
 
-You have to specify the names of the columns you want to pivot into longer format (X2,X3,X4):
+You have to specify the names of the columns you want to pivot into longer format (X2, X3, X4):
 
 ```{r, eval = F}
 wide_example %>%
-  pivot_longer(c(X2,X3,X4))
+  pivot_longer(c(X2, X3, X4))
 ```
 
 ... or the reverse selection (-X1):
 
 ```{r, eval = F}
-wide_example %>% pivot_longer(-X1)
+wide_example %>%
+  pivot_longer(-X1)
 ```
 
 You can specify the names of the columns where the data will be tidy (by default, it is `names` and `value`):
@@ -102,25 +109,25 @@ long_example <- wide_example %>%
 
 #### Exercice
 
-<div class="pencadre">
 Visualize the `table4a` dataset (you can use the `View()` function).
 
 ```{r table4a, eval=F, message=T}
 View(table4a)
 ```
 
+::: {.callout-exercise}
 Is the data **tidy** ? How would you transform this dataset to make it **tidy** ?
-</div>
+:::
 
 
 <details><summary>Solution</summary>
 <p>
 
-We have information about 3 variables in the `table4a`: `country`, `year` and number of `cases`.
+We have information about 3 variables in `table4a`: `country`, `year` and number of `cases`.
 However, the variable information (`year`) is stored as column names.
 We want to pivot the horizontal column year, vertically and make the table longer.
 
-You can use the `pivot_longer` fonction to make your table longer and have one observation per row and one variable per column.
+You can use the `pivot_longer` function to make your table longer and have one observation per row and one variable per column.
 
 For this we need to :
 
@@ -129,10 +136,11 @@ For this we need to :
 - give the name of the variable stored in the cells of the columns years (`case`)
 
 ```{r pivot_longer, eval=T, message=T}
-table4a %>% 
+table4a %>%
   pivot_longer(-country,
-               names_to = "year",
-               values_to = "case")
+    names_to = "year",
+    values_to = "case"
+  )
 ```
 </p>
 </details>
@@ -148,28 +156,34 @@ If you have a long dataset, that you want to make wider, you will use the `pivot
 You have to specify which column contains the name of the output column (`names_from`), and which column contains the cell values from (`values_from`).
 
 ```{r, eval = F}
-long_example %>% pivot_wider(names_from = V1,
-                             values_from = V2)
+long_example %>% pivot_wider(
+  names_from = V1,
+  values_from = V2
+)
 ```
 
 
 #### Exercice
 
-<div class="pencadre">
-Visualize the `table2` dataset
+
+Visualize the `table2` dataset.
+
+::: {.callout-exercise}
 Is the data **tidy** ? How would you transform this dataset to make it **tidy** ? (you can now make also make a guess from the name of the subsection)
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
 The column `count` store two types of information: the `population` size of the country and the number of `cases` in the country.
 
-You can use the `pivot_wider` fonction to make your table wider and have one observation per row and one variable per column.
+You can use the `pivot_wider` function to make your table wider and have one observation per row and one variable per column.
 
 ```{r pivot_wider, eval=T, message=T}
-table2 %>% 
-  pivot_wider(names_from = type,
-              values_from = count)
+table2 %>%
+  pivot_wider(
+    names_from = type,
+    values_from = count
+  )
 ```
 </p>
 </details>
@@ -178,7 +192,7 @@ table2 %>%
 
 ### Relational data
 
-To avoid having a huge table and to save space, information is often splited between different tables.
+To avoid having a huge table and to save space, information is often split between different tables.
 
 In our `flights` dataset, information about the `carrier` or the `airports` (origin and dest) are saved in a separate table (`airlines`, `airports`).
 
@@ -188,13 +202,13 @@ flights
 airlines
 airports
 weather
-flights2 <- flights %>% 
+flights2 <- flights %>%
   select(year:day, hour, origin, dest, tailnum, carrier)
 ```
 
 ### Relational schema
 
-The relationships between tables can be seen in a relational graph. The variables used to connect each pair of tables are called keys. A key is a variable (or set of variables) that uniquely identifies an observation.
+Relationships between tables can be displayed in a relational graph. The variables used to connect each pair of tables are called keys. A key is a variable (or set of variables) that uniquely identifies an observation.
 
 ```{r airlines_dag, echo=FALSE, out.width='100%'}
 knitr::include_graphics('img/relational-nycflights.png')
@@ -202,9 +216,9 @@ knitr::include_graphics('img/relational-nycflights.png')
 
 ### Joints
 
-If you have to combine data from 2 tables in a a new table, you will use `joints`.
+If you have to combine data from two tables in a new one, you will use `*_joint` functions.
 
-There are several types of joints depending of what you want to get. 
+There are several types of joints depending of what you want to get.
 
 ```{r joints, echo=FALSE, out.width='100%'}
 knitr::include_graphics('img/join-venn.png')
@@ -218,7 +232,7 @@ knitr::include_graphics('img/overview_joins.png')
 
 #### `inner_joint()`
 
-keeps observations in `x` AND `y`
+Keeps observations in `x` AND `y`
 
 ```{r inner_joint, eval=T}
 flights2 %>%
@@ -227,7 +241,7 @@ flights2 %>%
 
 #### `left_joint()`
 
-keeps all observations in `x`
+Keeps all observations in `x`
 
 ```{r left_joint, eval=T}
 flights2 %>%
@@ -236,7 +250,7 @@ flights2 %>%
 
 #### `right_joint()`
 
-keeps all observations in `y`
+Keeps all observations in `y`
 
 ```{r right_joint, eval=T}
 flights2 %>%
@@ -245,7 +259,7 @@ flights2 %>%
 
 #### `full_joint()`
 
-keeps all observations in `x` and `y`
+Keeps all observations in `x` and `y`
 
 ```{r full_joint, eval=T}
 flights2 %>%
@@ -257,7 +271,7 @@ flights2 %>%
 The default, `by = NULL`, uses all variables that appear in both tables, the so called natural join.
 
 ```{r , eval=T}
-flights2 %>% 
+flights2 %>%
   left_join(weather)
 ```
 
@@ -271,24 +285,24 @@ flights2 %>%
 If you want to join by data that are in two columns with different names, you must specify the correspondence with a named character vector: `by = c("a" = "b")`. This will match variable `a` in table `x` to variable `b` in table `y`.
 
 ```{r , eval=T, echo = T}
-flights2 %>% 
+flights2 %>%
   left_join(airports, c("dest" = "faa"))
 ```
 
-If 2 columns have identical names in the input tables but are not used in the join, they are automatically renamed with the suffix `.x` and `.y` because all column names must be different in the output table.
+If two columns have identical names in the input tables but are not used in the join, they are automatically renamed with the suffix `.x` and `.y` because all column names must be different in the output table.
 
 ```{r , eval=T, echo = T}
-flights2 %>% 
-  left_join(airports, c("dest" = "faa")) %>% 
+flights2 %>%
+  left_join(airports, c("dest" = "faa")) %>%
   left_join(airports, c("origin" = "faa"))
 ```
 
 You can change the suffix using the option `suffix`
 
 ```{r , eval=T, echo = T}
-flights2 %>% 
-  left_join(airports, by = c("dest" = "faa")) %>% 
-  left_join(airports, by = c("origin" = "faa"), suffix = c(".dest",".origin"))
+flights2 %>%
+  left_join(airports, by = c("dest" = "faa")) %>%
+  left_join(airports, by = c("origin" = "faa"), suffix = c(".dest", ".origin"))
 ```
 
 ### Filtering joins
@@ -302,13 +316,13 @@ Filtering joins match observations in the same way as mutating joins, but affect
 top_dest <- flights %>%
   count(dest, sort = TRUE) %>%
   head(10)
-flights %>% 
+flights %>%
   semi_join(top_dest)
 ```
 
 ### Set operations
 
-These expect the x and y inputs to have the same variables, and treat the observations like sets:
+These expect the `x` and `y` inputs to have the same variables, and treat the observations like sets:
 
 - `intersect(x, y)`: return only observations in both `x` and `y`.
 - `union(x, y)`: return unique observations in `x` and `y`.
diff --git a/session_7/session_7.Rmd b/session_7/session_7.Rmd
index 0e1ae139c24bbcf88fb94a292b170e3ee1a13614..f86925872fd2448b33944af3819f435f54e551f7 100644
--- a/session_7/session_7.Rmd
+++ b/session_7/session_7.Rmd
@@ -1,18 +1,22 @@
 ---
 title: "R.7: String & RegExp"
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
 library(fontawesome)
 
-if("conflicted" %in% .packages())
-    conflicted::conflicts_prefer(dplyr::filter)
+if ("conflicted" %in% .packages()) {
+  conflicted::conflicts_prefer(dplyr::filter)
+}
 ```
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
@@ -22,11 +26,11 @@ knitr::opts_chunk$set(comment = NA)
 In the previous session, we have often overlooked a particular type of data, the **string**.
 In R a sequence of characters is stored as a string.
 
-In this session you will learn the distinctive features of the string type and how we can use string of character within a programming language which is composed of particular string of characters as function names, variables.
+In this session you will learn the distinctive features of the string type and how we can use string of characters within a programming language which is composed of particular string of characters as function names, variables.
 
-<div class="pencadre">
+::: {.callout-exercise}
 As usual we will need the `tidyverse` library.
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -40,7 +44,7 @@ library(tidyverse)
 
 ### String definition
 
-A string can be defined within double `"` or simple `'` quote
+A string can be defined within double `"` or simple `'` quote:
 
 ```{r string_def, eval=F, message=T}
 string1 <- "This is a string"
@@ -48,7 +52,7 @@ string2 <- 'If I want to include a "quote"
 inside a string, I use single quotes'
 ```
 
-If you forget to close a quote, you’ll see +, the continuation character:
+If you forget to close a quote, you'll see `+`, the continuation character:
 
 ```
 > "This is a string without a closing quote
@@ -65,12 +69,12 @@ To include a literal single or double quote in a string you can use \\ to *escap
 double_quote <- "\"" # or '"'
 single_quote <- '\'' # or "'"
 ```
-If you want to include a literal backslash, you’ll need to double it up: `"\\"`.
+If you want to include a literal backslash, you'll need to double it up: `"\\"`.
  
  
 ### String representation
 
-The printed representation of a string is not the same as string itself
+The printed representation of a string is not the same as a string itself:
 
 ```{r string_rep_escape_a, eval=T, message=T}
 x <- c("\"", "\\")
@@ -104,8 +108,7 @@ x <- c("Apple", "Banana", "Pear")
 str_sub(x, 1, 3)
 ```
 
-- Subsetting strings
-negative numbers count backwards from the end
+- Subsetting strings negative numbers count backwards from the end
 ```{r str_sub2, eval=T, message=FALSE, cache=T}
 str_sub(x, -3, -1)
 ```
@@ -115,20 +118,21 @@ str_sub(x, -3, -1)
 str_to_lower(x)
 ```
 
-- ordering
+- Ordering
 ```{r str_sort, eval=T, message=FALSE, cache=T}
 str_sort(x)
 ```
 
-## Matching patterns with regular expressions
+## Matching patterns with REGular EXpressions (regex)
+
+regexps form a very terse language that allows you to describe patterns in strings.
 
-Regexps are a very terse language that allows you to describe patterns in strings.
+To learn regular expressions, we'll use `str_view()` and `str_view_all()`. These functions take a character vector and a regular expression, and show you how they match.
 
-To learn regular expressions, we’ll use `str_view()` and `str_view_all()`. These functions take a character vector and a regular expression, and show you how they match.
+::: {.callout-exercise}
 
-<div class="pencadre">
-You need to install the `htmlwidgets` packages to use these functions
-</div>
+You need to install the `htmlwidgets` packages to use these functions.
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -159,8 +163,8 @@ x <- c("apple", "banana", "pear")
 str_view(x, ".a.")
 ```
 
-But if “`.`” matches any character, how do you match the character “`.`”?
-You need to use an “escape” to tell the regular expression you want to match it exactly, not use its special behavior.
+But if `.` matches any character, how do you match the character "`.`"?
+You need to use an "escape" to tell the regular expression you want to match it exactly, not use its special behaviour.
 
 Like strings, regexps use the backslash, `\`, to escape special behaviour.
 So to match an `.`, you need the regexp `\.`. Unfortunately this creates a problem.
@@ -182,11 +186,38 @@ writeLines(x)
 str_view(x, "\\\\")
 ```
 
-### Exercises
+::: {.callout-exercise}
+
+## Exercises
+
+1. Explain why each of these strings doesn't match a \: `"\"`, `"\\"`, `"\\\"`.
+2. How would you match the sequence `"'\`?
+3. What patterns will the regular expression `\..\..\..` match? How would you represent it as a string?
+
+:::
 
-- Explain why each of these strings doesn’t match a \: "`\`", "`\\`", "`\\\`".
-- How would you match the sequence `"'\`?
-- What patterns will the regular expression `\..\..\..` match? How would you represent it as a string?
+<details><summary>Solution</summary>
+<p>
+
+1.
+    - `"\"`: would leave an open quote as `\"` would be interpreted as a literal double quote,
+    - `"\\"`: would escape the second `\` so we would be left with a blank,
+    - `"\\\"`: `\"` would again escape the quote so we would be left with an open quote.
+
+<p></p>
+2.  We would need the following pattern `"\\\"'\\\\"`:
+
+    - `\\\"` to escape the double quote,
+    - `'` doesn't need to be escaped (because the string is defined within double quote),
+    - `\\\\` to escape `\`.
+<p></p>
+3. It would match a string of the form: ".(anychar).(anychar).(anychar)"
+   ```{r str_dotstring, eval=F, message=FALSE, cache=T}
+   x <- c("alf.r.e.dd.ss..lsdf.d.kj")
+   str_view(x, "\\..\\..\\..")
+   ```
+</p>
+</details>
 
 ### Anchors
 
@@ -209,16 +240,37 @@ x <- c("apple pie", "apple", "apple cake")
 str_view(x, "^apple$")
 ```
 
-### Exercices
+::: {.callout-exercise}
+
+## Exercises
 
-- How would you match the literal string `"$^$"`?
-- Given the corpus of common words in stringr::words, create regular expressions that find all words that:
-  -Start with “y”.
-  - End with “x”
-  - Are exactly three letters long. (Don’t cheat by using `str_length()`!)
-  - Have seven letters or more.
+1. How would you match the literal string `"$^$"`?
 
-Since this list is long, you might want to use the match argument to `str_view()` to show only the matching or non-matching words.
+2. Given the corpus of common words in `stringr::words`, create regular expressions that find all words that:
+
+    a. Start with "y".
+    b. End with "x".
+    c. Are exactly three letters long (Don't cheat by using `str_length()`!).
+    d. Have seven letters or more.
+
+    Since this list is long, you might want to use the match argument to `str_view()` to show only the matching or non-matching words.
+:::
+
+<details><summary>Solution</summary>
+<p>
+
+1. We would need the pattern `"\\$\\^\\$"`
+
+<p></p>
+2.
+
+    a. start with "y": `"^y"`
+    b. end with "x": `"x$"`
+    c. three letters long: `"^...$"`
+    d. seven letters or more: `"......."`
+
+</p>
+</details>
 
 ### Character classes and alternatives
 
@@ -235,20 +287,45 @@ str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c")
 str_view(c("abc", "a.c", "a*c", "a c"), "a[ ]")
 ```
 
-You can use alternations to pick between one or more alternative patterns. For example, `abc|d..f` will match either `abc`, or `deaf`. Note that the precedent for `|` is low, so that `abc|xyz` matches `abc` or `xyz` not `abcyz` or `abxyz`. Like with mathematical expressions, if presidents ever get confusing, use parentheses to make it clear what you want:
+You can use alternations to pick between one or more alternative patterns. For example, `abc|d..f` will match either `abc`, or `deaf`. Note that the precedent for `|` is low, so that `abc|xyz` matches `abc` or `xyz` not `abcyz` or `abxyz`.  
+Like with mathematical expressions, if alternations ever get confusing, use parentheses to make it clear what you want:
 
 ```{r str_viewanchorsstartend_c, eval=T, cache=T}
 str_view(c("grey", "gray"), "gr(e|a)y")
 ```
 
-### Exercices
+::: {.callout-exercise}
+
+## Exercises
 
 Create regular expressions to find all words that:
 
-- Start with a vowel.
-- That only contains consonants. (Hint: thinking about matching “not”-vowels.)
-- End with ed, but not with eed.
-- End with ing or ise.
+1. Start with a vowel.
+2. That only contains consonants (Hint: thinking about matching "not"-vowels).
+3. End with "ed", but not with "eed".
+4. End with "ing" or "ise".
+
+:::
+
+<details><summary>Solution</summary>
+<p>
+
+1. start with a vowel: `"^[aeiouy]"`
+
+2. decomposition:
+    - start with a consonant: `"^[^aeiouy]"`
+    - contains one or more consonant: `"[^aeiouy]+"`
+    - end with a consonant: `"[^aeiouy]$"`
+    
+    result is: `"^[^aeiouy][^aeiouy]+[^aeiouy]$"`.
+   
+3. `"[^e]ed$"`
+
+4. `"(ing|ise)$"`
+
+</p>
+</details>
+
 
 ### Repetition
 
@@ -278,17 +355,42 @@ str_view(x, "C{2,}")
 str_view(x, "C{2,3}")
 ```
 
-### Exercices
+::: {.callout-exercise}
+
+1. Describe in words what these regular expressions match (read carefully to see if I'm using a regular expression or a string that defines a regular expression):
+
+    a. `^.*$`
+    b. `"\\{.+\\}"`
+    c. `\d{4}-\d{2}-\d{2}`
+    d. `"\\\\{4}"`
 
-- Describe in words what these regular expressions match: (read carefully to see if I’m using a regular expression or a string that defines a regular expression.)
-  - `^.*$`
-  - `"\\{.+\\}"`
-  - `\d{4}-\d{2}-\d{2}`
-  - `"\\\\{4}"`
-- Create regular expressions to find all words that:
-  - Start with three consonants.
-  - Have three or more vowels in a row.
-  - Have two or more vowel-consonant pairs in a row.
+2. Create regular expressions to find all words that:
+    
+    a. Start with three consonants.
+    b. Have three or more vowels in a row.
+    c. Have two or more vowel-consonant pairs in a row.
+
+:::
+
+<details><summary>Solution</summary>
+<p>
+
+1.
+
+    a. (regex) starts with anything and ends with anything, matches whole thing
+    b. (string regex) matches non-empty text in brackets
+    c. (regex) matches date in format `yyyy-mm-dd`
+    d. (string regex) matches string that contains `\` repeated 4 times
+
+<p></p>
+2.
+
+    a. `"^[^aeoiouy]{3}"`
+    b. `"[aeiou]{3,}"`
+    c. `"([aeiou][^aeiou]){2,}"`
+
+</p>
+</details>
 
 
 ### Grouping
@@ -299,18 +401,47 @@ You learned about parentheses as a way to disambiguate complex expressions. Pare
 str_view(fruit, "(..)\\1", match = TRUE)
 ```
 
-### Exercices
+::: {.callout-exercise}
+
+## Exercises
+
+1. Describe, in words, what these expressions will match:
+
+    a. `"(.)\\1\\1"`
+    b. `"(.)(.)\\2\\1"`
+    c. `"(..)\\1"`
+    d. `"(.).\\1.\\1"`
+    e. `"(.)(.)(.).*\\3\\2\\1"`
+
+2. Construct regular expressions to match words that:
+
+    a. Start and end with the same character.
+    b. Contain a repeated pair of letters (e.g. `"church"` contains `"ch"` repeated twice).
+    c. Contain one letter repeated in at least three places (e.g. `"eleven"` contains three `"e"`s).
+
+:::
+
+<details><summary>Solution</summary>
+<p>
+
+1.
+
+    a. matches a character repeated thrice
+    b. matches two characters followed by their reverse order ("abba")
+    c. matches two characters repeated twice (not each)
+    d. matches a character repeated 3 times with one character between each repeat
+    e. matches 3 characters, followed by any characters, then the 3 characters in reverse order
+
+<p></p>
+2.
+
+    a. `"^(.).*\\1$"`
+    b. `"([A-Za-z]{2}).*\\1"`
+    c. `"([A-Za-z]).*\\1.*\\1"`
+
+</p>
+</details>
 
-- Describe, in words, what these expressions will match:
-  - `"(.)\1\1"`
-  - `"(.)(.)\\2\\1"`
-  - `"(..)\1"`
-  - `"(.).\\1.\\1"`
-  - `"(.)(.)(.).*\\3\\2\\1"`
-- Construct regular expressions to match words that:
-  - Start and end with the same character.
-  - Contain a repeated pair of letters (e.g. `“church”` contains `“ch”` repeated twice.)
-  - Contain one letter repeated in at least three places (e.g. `“eleven”` contains three `“e”`s.)
 
 ### Detect matches
 
@@ -319,7 +450,7 @@ x <- c("apple", "banana", "pear")
 str_detect(x, "e")
 ```
 
-How many common words start with t?
+How many common words start with "t"?
 
 ```{r str_view_match_b, eval=T, cache=T}
 sum(str_detect(words, "^t"))
@@ -350,10 +481,10 @@ identical(no_vowels_1, no_vowels_2)
 
 ```{r str_detecttibble, eval=T, cache=T}
 df <- tibble(
-  word = words, 
+  word = words,
   i = seq_along(word)
 )
-df %>% 
+df %>%
   filter(str_detect(word, "x$"))
 ```
 
@@ -383,49 +514,128 @@ head(matches)
 
 ### Grouped matches
 
-Imagine we want to extract nouns from the sentences. As a heuristic, we’ll look for any word that comes after “a” or “the”.
+Imagine we want to extract nouns from the sentences. As a heuristic, we'll look for any word that comes after "a" or "the".
 
 ```{r noun_regex, eval=T, cache=T}
 noun <- "(a|the) ([^ ]+)"
 has_noun <- sentences %>%
   str_subset(noun) %>%
   head(10)
-has_noun %>% 
+has_noun %>%
   str_extract(noun)
 ```
 
 `str_extract()` gives us the complete match; `str_match()` gives each individual component.
 
 ```{r noun_regex_match, eval=T, cache=T}
-has_noun %>% 
+has_noun %>%
   str_match(noun)
 ```
 
-### Exercises
+::: {.callout-exercise}
+Find all words that come after a `number` like `one`, `two`, `three` etc. Pull out both the number and the word.
+:::
+
+<details><summary>Solution</summary>
+<p>
+
+Start by creating a vector of words defining digits:
+```{r digit_vec, eval=T, cache=T}
+nums <- c("one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
+```
+
+Next, create the corresponding regular expression to catch any worded digit:
+```{r digit_regex, eval=T, cache=T}
+nums_c <- str_c(nums, collapse = "|")
+```
+
+Then, construct the full regular expression where:  
+`(?<![Y])X` means capture string `X` only if not preceded by string `Y`.  
+Here, `X` corresponds to our worded digit expression and `Y` is any letter (`:alpha:`).
+
+This way, `(?<![:alpha:]) (one|two|three|four|five|six|seven|eight|nine)` will match any of our digit only if not preceded by a letter.
 
-- Find all words that come after a `number` like `one`, `two`, `three` etc. Pull out both the number and the word.
+We then add a blank space and `[A-Za-z]+` to capture the word following our worded digit:
+```{r digit_regex_full, eval=T, cache=T}
+re_str <- str_c("(?<![:alpha:])", "(", nums_c, ")", " ", "([A-Za-z]+)", sep = "")
+```
+
+Let's apply it to our sentences:
+```{r sentences_digit_regex, eval=T, cache=T}
+sentences %>%
+  # get the subset of sentences where a match occurred
+  str_subset(regex(re_str, ignore_case = TRUE)) %>%
+  # for each sentence get the matched string
+  str_extract_all(regex(re_str, ignore_case = TRUE)) %>%
+  # convert to vector
+  unlist() %>%
+  # convert to tibble
+  as_tibble_col(column_name = "expr") %>%
+  # split matched strings into components
+  tidyr::separate(
+    col = "expr", 
+    into = c("digit", "word"),
+    remove = FALSE
+  )
+```
+</p>
+</details>
 
 ### Replacing matches
 
 Instead of replacing with a fixed string, you can use back references to insert components of the match. In the following code, I flip the order of the second and third words.
 
 ```{r replacing_matches, eval=T, cache=T}
-sentences %>% 
+sentences %>%
   str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%
   head(5)
 ```
 
-### Exercices
+::: {.callout-exercise}
+
+## Exercises
 
-- Replace all forward slashes in a string with backslashes.
-- Implement a simple version of `str_to_lower()` using `replace_all()`.
-- Switch the first and last letters in words. Which of those strings are still words?
+1. Replace all forward slashes in a string with backslashes.
+2. Implement a simple version of `str_to_lower()` using `str_replace_all()`.
+3. Switch the first and last letters in words. Which of those strings are still words?
+
+:::
+
+<details><summary>Solution</summary>
+<p>
+
+1. We can use the function `str_replace_all` with a replacement string:
+   ```{r replacing_slashes, eval=T, cache=T}
+   test_str <- "/test/"
+   writeLines(test_str)
+
+   test_str %>% 
+     str_replace_all(pattern = "/", replacement = "\\\\") %>%
+     writeLines()
+   ```
+
+2. We also can use the function `str_replace_all` with a replacement function:
+   ```{r replacing_to_lower, eval=T, cache=T}
+   sentences %>%
+     str_replace_all(pattern = "([A-Z])", replacement = tolower) %>%
+     head(5)
+   ```
+
+3. Any words that start and end with the same letter and a few other examples like "war –> raw":
+   ```{r switching_words, eval=T, cache=T}
+   words %>%
+     str_replace(pattern = "(^.)(.*)(.$)", replacement = "\\3\\2\\1") %>%
+     head(5)
+   ```
+
+</p>
+</details>
 
 ### Splitting
 
 ```{r splitting, eval=T, cache=T}
 sentences %>%
-  head(5) %>% 
+  head(5) %>%
   str_split("\\s")
 ```
 
diff --git a/session_8/session_8.Rmd b/session_8/session_8.Rmd
index ad0e404819c2fd68d9dcc1dda142c475f11e8c38..bf8f341adfc982aaa2937d40d2cf9a622ae4125d 100644
--- a/session_8/session_8.Rmd
+++ b/session_8/session_8.Rmd
@@ -1,18 +1,22 @@
 ---
 title: "R.8: Factors"
-author: "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
+author:
+  - "Laurent Modolo [laurent.modolo@ens-lyon.fr](mailto:laurent.modolo@ens-lyon.fr)"
 date: "2022"
+filters:
+  - callout-exercise
 ---
 
 ```{r include=FALSE}
 library(fontawesome)
 
-if("conflicted" %in% .packages())
-    conflicted::conflicts_prefer(dplyr::filter)
+if ("conflicted" %in% .packages()) {
+  conflicted::conflicts_prefer(dplyr::filter)
+}
 ```
 
 ```{r setup, include=FALSE}
-rm(list=ls())
+rm(list = ls())
 knitr::opts_chunk$set(echo = TRUE)
 knitr::opts_chunk$set(comment = NA)
 ```
@@ -21,11 +25,11 @@ knitr::opts_chunk$set(comment = NA)
 
 In this session, you will learn more about the factor type in R.
 Factors can be very useful, but you have to be mindful of the implicit conversions from simple vector to factor !
-They are the source of loot of pain for R programmers.
+They are the source of lot of pain for R programmers.
 
-<div class="pencadre">
+::: {.callout-exercise}
 As usual we will need the `tidyverse` library.
-</div>
+:::
 
 <details><summary>Solution</summary>
 <p>
@@ -45,13 +49,13 @@ x1 <- c("Dec", "Apr", "Jan", "Mar")
 
 Using a string to record this variable has two problems:
 
-1. There are only twelve possible months, and there’s nothing saving you from typos:
+1. There are only twelve possible months, and there's nothing saving you from typos:
 
 ```{r declare_month2, eval=T, cache=T}
 x2 <- c("Dec", "Apr", "Jam", "Mar")
 ```
 
-2. It doesn’t sort in a useful way:
+2. It doesn't sort in a useful way:
 
 ```{r sort_month, eval=T, cache=T}
 sort(x1)
@@ -61,7 +65,7 @@ You can fix both of these problems with a factor.
 
 ```{r sort_month_factor, eval=T, cache=T}
 month_levels <- c(
-  "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
+  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 )
 y1 <- factor(x1, levels = month_levels)
@@ -76,10 +80,12 @@ y2 <- parse_factor(x2, levels = month_levels)
 y2
 ```
 
-Sometimes you’d prefer that the order of the levels match the order of the first appearance in the data.
+Sometimes you'd prefer that the order of the levels match the order of the first appearance in the data.
 
 ```{r inorder_month_factor, eval=T, cache=T}
-f2 <- x1 %>% factor() %>% fct_inorder()
+f2 <- x1 %>%
+  factor() %>%
+  fct_inorder()
 f2
 levels(f2)
 ```
@@ -91,7 +97,7 @@ gss_cat %>%
   count(race)
 ```
 
-By default, `ggplot2` will drop levels that don’t have any values. You can force them to display with:
+By default, `ggplot2` will drop levels that don't have any values. You can force them to display with:
 
 ```{r race_plot, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
 ggplot(gss_cat, aes(x = race)) +
@@ -101,7 +107,7 @@ ggplot(gss_cat, aes(x = race)) +
 
 ## Modifying factor order
 
-It’s often useful to change the order of the factor levels in a visualisation.
+It's often useful to change the order of the factor levels in a visualisation.
 
 ```{r tv_hour, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
 relig_summary <- gss_cat %>%
@@ -111,32 +117,33 @@ relig_summary <- gss_cat %>%
     tvhours = mean(tvhours, na.rm = TRUE),
     n = n()
   )
-ggplot(relig_summary, aes(x = tvhours, y = relig)) + geom_point()
+ggplot(relig_summary, aes(x = tvhours, y = relig)) +
+  geom_point()
 ```
 
-It is difficult to interpret this plot because there’s no overall pattern. We can improve it by reordering the levels of relig using `fct_reorder()`. `fct_reorder()` takes three arguments:
+It is difficult to interpret this plot because there's no overall pattern. We can improve it by reordering the levels of the factor relig using `fct_reorder()`. `fct_reorder()` takes three arguments:
 
 - `f`, the factor whose levels you want to modify.
 - `x`, a numeric vector that you want to use to reorder the levels.
-- Optionally, `fun`, a function that’s used if there are multiple values of `x` for each value of `f`. The default value is `median`.
+- Optionally, `fun`, a function that's used if there are multiple values of `x` for each value of `f`. The default value is `median`.
 
 ```{r tv_hour_order, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
 ggplot(relig_summary, aes(x = tvhours, y = fct_reorder(relig, tvhours))) +
   geom_point()
 ```
 
-As you start making more complicated transformations, I’d recommend moving them out of `aes()` and into a separate `mutate()` step. For example, you could rewrite the plot above as:
+As you start making more complicated transformations, I would recommend moving them out of `aes()` and into a separate `mutate()` step. For example, you could rewrite the plot above as:
 
 ```{r tv_hour_order_mutate, cache = TRUE, fig.width=8, fig.height=4.5, message=FALSE}
 relig_summary %>%
   mutate(relig = fct_reorder(relig, tvhours)) %>%
   ggplot(aes(x = tvhours, y = relig)) +
-    geom_point()
+  geom_point()
 ```
 
 ## `fct_reorder2()`
 
-Another type of reordering is useful when you are colouring the lines on a plot. `fct_reorder2()` reorders the factor by the `y` values associated with the largest `x` values. This makes the plot easier to read because the line colours line up with the legend.
+Another useful type of reordering is when you are colouring the lines on a plot. `fct_reorder2()` reorders the factor by the `y` values associated with the largest `x` values. This makes the plot easier to read because the line colours line up with the legend.
 
 ```{r fct_reorder2, eval=T, plot=T}
 by_age <- gss_cat %>%
@@ -159,21 +166,21 @@ ggplot(by_age, aes(x = age, y = prop, colour = fct_reorder2(marital, age, prop))
 
 ## Materials
 
-There are lots of material online for R and more particularly on `tidyverse` and `Rstudio`
+There are lots of material online for R and more particularly on `tidyverse` and `RStudio`
 
 You can find cheat sheet for all the packages of the `tidyverse` on this page:
-[https://www.rstudio.com/resources/cheatsheets/](https://www.rstudio.com/resources/cheatsheets/)
+[https://posit.co/resources/cheatsheets/](https://posit.co/resources/cheatsheets/)
 
-The `Rstudio` websites are also a good place to learn more about R and the meta-package maintenained by the `Rstudio` community:
+The `RStudio` websites are also a good place to learn more about R and the meta-package maintained by the `RStudio` community:
 
-- [https://www.rstudio.com/resources/webinars/](https://www.rstudio.com/resources/webinars/)
-- [https://www.rstudio.com/products/rpackages/](https://www.rstudio.com/products/rpackages/)
+- [webinars](https://posit.co/resources/videos/)
+- [R packages](https://posit.co/products/open-source/rpackages/)
 
 For example [rmarkdown](https://rmarkdown.rstudio.com/) is a great way to turn your analyses into high quality documents, reports, presentations and dashboards:
 
- - A comprehensive guide: [https://bookdown.org/yihui/rmarkdown/](https://bookdown.org/yihui/rmarkdown/)
- - The cheatsheet [https://raw.githubusercontent.com/rstudio/cheatsheets/main/rmarkdown-2.0.pdf](https://raw.githubusercontent.com/rstudio/cheatsheets/main/rmarkdown-2.0.pdf)
+ - [a comprehensive guide](https://bookdown.org/yihui/rmarkdown/)
+ - [the cheatsheet](https://raw.githubusercontent.com/rstudio/cheatsheets/main/rmarkdown-2.0.pdf)
 
-In addition most packages will provide **vignette**s on how to perform an analysis from scratch. On the [bioconductor.org](http://www.bioconductor.org/packages/release/bioc/html/DESeq2.html) website (specialised on R packages for biologists), you will have direct links to the packages vignette.
+In addition most packages will provide **vignette**s on how to perform an analysis from scratch. On the [cran.r-project.org](https://cran.r-project.org/web/packages/ggplot2/index.html) or [bioconductor.org](http://www.bioconductor.org/packages/release/bioc/html/DESeq2.html) (specialised on R packages for biologists) websites, you will have direct links to a package vignettes.
 
-Finally, don't forget to search the web for your problems or error in R websites like [stackoverflow](https://stackoverflow.com/) contains high quality and well-curated answers.
\ No newline at end of file
+Finally, don't forget to search the web for your problems or error in R, for instance [stackoverflow](https://stackoverflow.com/) contains high quality and well-curated answers.
\ No newline at end of file