├── .gitignore ├── LICENSE ├── README.md ├── other ├── jordy_australia.jpg ├── njudd.jpg ├── njudd_edited.jpg ├── nwo_openscience.jpg ├── photo_rogier.jpeg └── raincloudplots_NWO_webinar.png └── tutorial ├── 20231120_Quick_setup.md ├── 20231120_Quick_setup.pdf └── R_tutorial ├── 20230213_rainclouds_tutorial.R ├── 20230213_rainclouds_tutorial_cheatsheet.R ├── fn_summary_SE.r ├── geom_flat_violin.R └── repeated_measures_data.csv /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | tutorial/.DS_Store 3 | tutorial/.Rhistory 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raincloudplots Workshops 2 |
3 |
4 |
5 |
6 |
7 | Repository about the upcoming 'raincloudplots' workshops which are part of our NWO 'Open Science' funded project: raincloudplots 2.0 by 8 | Rogier Kievit and Jordy van Langen. 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | You can find additional info about our funded project here. 20 | 21 | 22 | ## [Click here to download all the material](https://surfdrive.surf.nl/files/index.php/s/1YdcZalIZxhFigy) 23 | 24 | 25 | ## R package - ggrain 26 | 27 | ### There is a [walkthrough](https://www.njudd.com/raincloud-ggrain/) of the functionality along with a [GitHub](https://github.com/njudd/ggrain) repo. 28 | 29 | ### How to install from CRAN: 30 | ```r 31 | install.packages("ggrain") 32 | library(ggrain) 33 | ``` 34 | ### How to install from GitHub: 35 | ```r 36 | if (!require(remotes)) { 37 | install.packages("remotes") 38 | } 39 | remotes::install_github('njudd/ggrain') 40 | 41 | library(ggrain) 42 | ``` 43 | 44 | ## Paper 45 |
46 |
 47 | - Allen, M., Poggiali, D., Whitaker, K., Marshall, T. R., van Langen, J., & Kievit, R. A.
 48 |     Raincloud plots: a multi-platform tool for robust data visualization [version 2; peer review: 2 approved] 
 49 |     Wellcome Open Research 2021, 4:63. https://doi.org/10.12688/wellcomeopenres.15191.2
 50 | 
51 | 52 | 53 | ## Our plan 54 | With these workshops, we hope to educate & showcase 'raincloudplots' to the wider academic community. 55 | We will host three online workshops in which we will present step-by-step tutorials for experts, who are already familiar with 'raincloudplots', but also to those who find using code (unnecessarily) daunting. The free workshops will entail plenary walk-throughs and smaller break-out-rooms. 56 | 57 | ## When? (all finished) 58 | The workshops will take place on the following dates: 59 |
60 | - 23-sept-2022 Local in-person workshop (no external sign up possible, Donders Institute only) 61 | - 10:00-12:00 CET 62 | - Language: R 63 | 64 | - 30-sept-2022 (online, worldwide) 65 | - 14:00-16:00 CET 66 | - Language: R 67 | - Break-out rooms 68 | 69 | - 11-nov-2022 (online, worldwide) 70 | - 14:00-16:00 CET 71 | - Languages: R/Python/MATLAB/JASP 72 | - Break-out rooms 73 | 74 | - 17-feb-2023 (online, worldwide) 75 | - 14:00-16:00 CET 76 | - Languages: R/Python/MATLAB/JASP 77 | - Break-out rooms 78 | 79 | - 31-mar-2023 (online, Oxford serotonin seminar only) 80 | - 14:00-16:00 CET 81 | - Languages: R/Python 82 | - Break-out rooms 83 | 84 | 85 | ## Interested in attending? 86 | #### Please read the following documentation about each workshop. 87 | 88 | - Duration: Max. 2 hours 89 | 90 | - Outline: 91 | - What are raincloudplots? 92 | - What are alternatives? 93 | - How to plot: from simple to advanced 94 | - JASP Statistics 95 | - Community input: what would you like to be improved/see? 96 | - raincloudplots: ShinyApp demo 97 | - Break-out rooms: (Python/Matlab/R/JASP) 98 | - Bring Your Own Data! 99 | 100 | - Please indicate whether you want to learn about Python/Matlab/R/JASP, which is necessary information for the subsequent Zoom break-out rooms in which we will answer any questions you have, but most importantly, **give you the opportunity to Bring Your Own Data (BYOD)** to co-develop & fine-tune your amazing plots! 101 | 102 | - There will be a maximum amount of 100 attendees for each workshop. 103 | 104 | ## Sign-up (not possible anymore) 105 | - You can register your attendance here: 106 | https://docs.google.com/forms/d/e/1FAIpQLSeTykveO8tJl2zP-7QgNOfM3vMTbkEe5RtqxMKbE5AiVDzb1A/viewform 107 |
108 |
109 | 110 | -------------------------------------------------------------------------------- /other/jordy_australia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/other/jordy_australia.jpg -------------------------------------------------------------------------------- /other/njudd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/other/njudd.jpg -------------------------------------------------------------------------------- /other/njudd_edited.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/other/njudd_edited.jpg -------------------------------------------------------------------------------- /other/nwo_openscience.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/other/nwo_openscience.jpg -------------------------------------------------------------------------------- /other/photo_rogier.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/other/photo_rogier.jpeg -------------------------------------------------------------------------------- /other/raincloudplots_NWO_webinar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/other/raincloudplots_NWO_webinar.png -------------------------------------------------------------------------------- /tutorial/20231120_Quick_setup.md: -------------------------------------------------------------------------------- 1 | # Quick setup R 2 | 3 | ### 1. Make sure you have the latest R downloaded 4 | - Type `R.version$version.string` in R it should be **4.3.2** (aka 'Eye Holes' with `R.version$nickname`) 5 | - If not download the latest version for [Windows](https://cran.r-project.org/bin/windows/base/R-4.3.2-win.exe) or [Mac](https://cran.r-project.org/bin/macosx/) 6 | 7 | ### 2. Make sure you have the latest Rstudio downloaded 8 | - You can check this by clicking on the menu `About Rstudio` in the Rstudio or Help interface dropdown. 9 | - If not download the latest version of [Rstudio](https://posit.co/download/rstudio-desktop/) 10 | 11 | ### 3. Make sure you have the packages installed 12 | 13 | - You can do this by running the following code 14 | 15 | ``` if (!require(pacman)) { install.packages("pacman") } 16 | pacman::p_load(patchwork, tidyverse, lavaan, ggpp, plyr, ggrain) #for rainclouds ``` 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tutorial/20231120_Quick_setup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorvlan/raincloudplots-workshops/b0104ad5279f230a73bc647594edc945216ecbb2/tutorial/20231120_Quick_setup.pdf -------------------------------------------------------------------------------- /tutorial/R_tutorial/20230213_rainclouds_tutorial.R: -------------------------------------------------------------------------------- 1 | # running on MacOS 12.5 Rstudio 2022.07.01 on 22-10-2022 by Nicholas Judd 2 | # updated for ggrain on 2023-02-11 3 | # original source: https://github.com/RainCloudPlots/RainCloudPlots/tree/master/tutorial_R 4 | 5 | # R package https://github.com/njudd/ggrain 6 | # R package vingette https://www.njudd.com/raincloud-ggrain/ 7 | 8 | if (!require(pacman)) { 9 | install.packages("pacman") 10 | } 11 | pacman::p_load(patchwork, tidyverse, lavaan, ggpp, plyr, 12 | ggrain) #for rainclouds 13 | 14 | getwd() # this tells you the current path of R 15 | # we need to direct R to the folder with the scripts & data 16 | # we set the path by using setwd() 17 | # for example: 18 | # setwd("/Users/njudd/projects/rain/raincloudplots-workshops/tutorial/R_tutorial") 19 | #or select Session->set working directory 20 | 21 | #setwd("/Users/njudd/surfdrive/Shared/NWO Open Science Fund/Workshops/Workshop4 - 17 feb-2023/Materials/R_tutorial/") 22 | 23 | source("geom_flat_violin.R") #this only works if you set the right path! 24 | source("fn_summary_SE.r") # This does the summary. For each group's data frame, return a vector with 25 | # N, mean, median, and sd 26 | 27 | ################################################# 28 | # first we will start by simulating some data! :) 29 | ################################################# 30 | m <- 50 # mean 31 | s <- 25 # sd 32 | sim_n <- 250 # draws 33 | 34 | # Calculate log-normal parameters ---- 35 | location <- log(m^2 / sqrt(s^2 + m^2)) 36 | shape <- sqrt(log(1 + (s^2 / m^2))) 37 | 38 | # Set seed to get same data everytime ---- 39 | set.seed(42) 40 | 41 | # Create data by hand ---- 42 | simdat_group1 <- rlnorm(sim_n, location, shape) 43 | simdat_group2 <- rnorm(sim_n, m, s) 44 | 45 | simdat <- c(simdat_group1, simdat_group2) %>% as_tibble() %>% dplyr::rename(score = 1) 46 | simdat <- simdat %>% mutate(group = 47 | fct_inorder(c(rep("Group1", times = sim_n), 48 | rep("Group2", times = sim_n)))) 49 | 50 | # Calculate summary stats ---- 51 | summary_simdat <- fn_summary_SE(simdat, score, group) 52 | 53 | # lets look at the data 54 | head(simdat) 55 | str(simdat) 56 | summary_simdat 57 | 58 | 59 | ################################################# 60 | # Example 1: Two group plot with colours and coordinate flip 61 | ################################################# 62 | 63 | # lets plot the individual data points of each group jittered 64 | p1 <- ggplot(simdat, aes(x=group,y=score, fill = group))+ 65 | geom_point(position = position_jitter(width = .15), size = .25) + 66 | labs(title = 'Figure 1: The Basic Raincloud with Colour', y = 'Score', x = 'Group') + 67 | theme_classic()+guides(fill = "none") 68 | p1 69 | # lets flip the points 70 | p1 <- p1 + coord_flip() 71 | p1 72 | 73 | # here we have a raincloud with two groups 74 | p1 + geom_flat_violin(position = position_nudge(x = .2, y = 0),adjust = 2) 75 | 76 | # we can also change the smoothing with adjust 77 | # here is one with reduced smoothing 78 | p1 + geom_flat_violin(position = position_nudge(x = .2, y = 0), adjust = .1) 79 | 80 | ################################################# 81 | # Example 2: now we will have a basic example of a single raincloud plot 82 | ################################################# 83 | p2_h <- ggplot(simdat, aes(x = 1, y=score)) + 84 | geom_flat_violin(position = position_nudge(x = .23, y = 0),adjust = 2, width = .5, fill = "red", color = NA)+ 85 | geom_boxplot(position = position_nudge(x = .17, y = 0), width = .1, outlier.colour = NA, fill = "red") + 86 | geom_point(position = position_jitter(width = .10), size = .3, color = "red", alpha = .4)+ 87 | coord_cartesian(xlim = c(0.2, 2)) + # change to .75 & 1.5 to zoom in 88 | labs(x = "Group", y = "Score") + 89 | theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) + 90 | theme_minimal() 91 | 92 | p2_h # printing the plot 93 | p2_v <- p2_h + coord_flip() #flipping the plot 94 | p2_v 95 | 96 | # lets do it with ggrain; it doesn't have to look the exact same 97 | ggplot(simdat, aes(x = 1, y=score)) + 98 | geom_rain() + 99 | coord_flip() + 100 | theme_minimal() 101 | 102 | # can you make them red? 103 | #*answer 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | #try to save the plot with ggsave()! 113 | #ggsave("~/Desktop/p0_v.png", p0_v, bg = "white") # need to specify bg white for theme_minimal now 114 | 115 | 116 | # can you modify the violin to have similar smoothing? 117 | #answer* 118 | ggplot(simdat, aes(x = 1, y=score)) + 119 | geom_rain(point.args = list(color = "red"), 120 | boxplot.args = list(fill = "red", outlier.color = NA), 121 | violin.args = list(fill = "red", color = NA, adjust = 2)) + 122 | coord_flip() + 123 | theme_minimal() 124 | 125 | ################################################# 126 | # Example 3: Two group raincloud 127 | ################################################# 128 | 129 | # lets make two group raincloud 130 | p3 <- ggplot(simdat, aes(x=group,y=score, fill = group))+ 131 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2)+ 132 | geom_point(position = position_jitter(width = .15), size = .25)+ 133 | #note that here we need to set the x-variable to a numeric variable and bump it to get the boxplots to line up with the rainclouds. 134 | geom_boxplot(aes(x = as.numeric(group)+0.25, y = score), outlier.shape = NA, alpha = 0.3, width = .1, colour = "BLACK") + 135 | ylab('Score')+xlab('Group')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + 136 | labs(title = 'Figure 3: Raincloud Plot w/Boxplots', x = 'Group', y = 'Score') 137 | p3 138 | 139 | # we can change the colors! 140 | p3c <- p3 + ggtitle("Figure 3c: Change in Colour Palette") + 141 | scale_colour_brewer(palette = "Dark2") + 142 | scale_fill_brewer(palette = "Dark2") 143 | p3c 144 | 145 | # lets do it with ggrain; it doesn't have to look the exact same 146 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 147 | geom_rain() 148 | 149 | # position arguements are given as a list for each element 150 | # when you give them the defaults are written over 151 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 152 | geom_rain(violin.args.pos = list(side = "r", width = 0.7, position = position_nudge(x =0.3))) 153 | 154 | # can you change the side with rain.side arguement? 155 | #*answer 156 | 157 | 158 | 159 | # can you change the smoothing? 160 | #answer* 161 | 162 | 163 | 164 | # can you flip them? 165 | #answer* 166 | 167 | 168 | 169 | 170 | ################################################# 171 | # Example 4: advanced rainclouds! 172 | ################################################# 173 | 174 | #Rainclouds with mean and confidence interval 175 | p4 <- ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 176 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2)+ 177 | geom_point(position = position_jitter(width = .15), size = .25)+ 178 | geom_point(data = summary_simdat, aes(x = group, y = score_mean), position = position_nudge(.25), colour = "BLACK")+ 179 | geom_errorbar(data = summary_simdat, aes(x = group, y = score_mean, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), position = position_nudge(.25), colour = "BLACK", width = 0.1, size = 0.8)+ 180 | ylab('Score')+xlab('Group')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + 181 | scale_colour_brewer(palette = "Dark2")+ 182 | scale_fill_brewer(palette = "Dark2")+ 183 | ggtitle("Figure 7: Raincloud Plot with Mean ± 95% CI") 184 | p4 185 | 186 | # see if you can get stat_summary to do the same! (check out the lines commented off) 187 | # If you have issues installing Hmisc just move on 188 | 189 | p4_c <- ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 190 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2)+ 191 | geom_point(position = position_jitter(width = .15), size = .25) + 192 | #stat_summary(aes(y = score), fun = "mean", position = position_nudge(.25), colour = "BLACK", geom = "point") + 193 | #stat_summary(aes(y = score), fun.data = "mean_cl_normal", position = position_nudge(.25), colour = "BLACK", geom = "errorbar", width = .1) + 194 | geom_point(data = summary_simdat, aes(x = group, y = score_mean), position = position_nudge(.25), colour = "BLACK")+ 195 | geom_errorbar(data = summary_simdat, aes(x = group, y = score_mean, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), position = position_nudge(.25), colour = "BLACK", width = 0.1, size = 0.8)+ 196 | ylab('Score')+xlab('Group')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + 197 | scale_colour_brewer(palette = "Dark2")+ 198 | scale_fill_brewer(palette = "Dark2")+ 199 | ggtitle("Figure 7: Raincloud Plot with Mean ± 95% CI") 200 | p4_c 201 | 202 | # Now try to add stat_summary to geom_rain() 203 | # see vingette https://www.njudd.com/raincloud-ggrain/ for help :) 204 | #answer* 205 | ggplot(simdat,aes(x=group, y=score, fill = group))+ 206 | geom_rain(boxplot.args = list(color = NA, fill = NA), violin.args = list(color = NA)) + 207 | stat_summary(fun = mean, geom = "point", aes(color = group), color = "black", position = position_nudge(x = .15, y = 0)) + 208 | stat_summary(fun = mean, geom = "errorbar", fun.data = "mean_cl_normal", color = "black", position = position_nudge(x = .15, y = 0), 209 | aes(color = group, width = .1)) + 210 | coord_flip() + 211 | scale_colour_brewer(palette = "Dark2")+ 212 | scale_fill_brewer(palette = "Dark2") 213 | 214 | 215 | ################################################# 216 | #Example 5: Rainclouds with striated data 217 | ################################################# 218 | 219 | #Round data 220 | simdat_round<-simdat 221 | simdat_round$score<-round(simdat$score,0) 222 | 223 | #Striated/grouped when no jitter applied 224 | ap5.1 <- ggplot(simdat_round,aes(x=group,y=score,fill=group,col=group))+ 225 | geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .6,adjust =4)+ 226 | geom_point(size = 1, alpha = 0.6)+ylab('Score')+xlab("")+ 227 | scale_fill_brewer(palette = "Dark2")+ 228 | scale_colour_brewer(palette = "Dark2")+ 229 | guides(fill = "none", col = "none")+ 230 | ggtitle('Striated') 231 | 232 | #Added jitter helps 233 | ap5.2 <- ggplot(simdat_round,aes(x=group,y=score,fill=group,col=group))+ 234 | geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .4,adjust =4)+ 235 | geom_point(position=position_jitter(width = .15),size = 1, alpha = 0.4)+ylab('Score')+ xlab("")+ 236 | scale_fill_brewer(palette = "Dark2")+ 237 | scale_colour_brewer(palette = "Dark2")+ 238 | guides(fill = "none", col = "none")+ 239 | ggtitle('Added jitter') 240 | 241 | 242 | # with patchwork you can add ggplots together! 243 | p5 <- ap5.1 + ap5.2 + plot_annotation("Figure 8: Jittering Ordinal Data", tag_levels = "a") 244 | p5 245 | 246 | # can you make two similar ones quickly with geom_rain and patch them together? 247 | #*answer 248 | #* 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | ################################################# 264 | #Example 6: Add additional factor/condition 265 | ################################################# 266 | 267 | simdat$gr2<-as.factor(c(rep('high',125),rep('low',125),rep('high',125),rep('low',125))) 268 | 269 | p6 <- ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 270 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2, trim = TRUE)+ 271 | geom_point(position = position_jitter(width = .15), size = .25)+ 272 | geom_boxplot(aes(x = as.numeric(group)+0.25, y = score),outlier.shape = NA, alpha = 0.3, width = .1, colour = "BLACK") + 273 | ylab('Score')+xlab('')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + facet_wrap(~gr2)+ 274 | scale_colour_brewer(palette = "Dark2")+ 275 | scale_fill_brewer(palette = "Dark2")+ 276 | ggtitle("Figure 9: Complex Raincloud Plots with Facet Wrap") 277 | p6 278 | 279 | # can you make a similar one with ggrain? 280 | #*answer 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | # can you see the median and the IQR of the boxplot? 289 | # if you can't try to fix it with ggrain while also having the violin without a boarder 290 | #*answer 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | # p10 needs a csv in the same folder 299 | # loading the data: make sure to get getwd() and do setwd() 300 | rep_data <- read_csv("repeated_measures_data.csv", 301 | col_types = cols(group = col_factor(levels = c("1", "2")), 302 | time = col_factor(levels = c("1", "2", "3")))) 303 | 304 | sumrepdat <- fn_summary_SE(rep_data, "score", c("group", "time")) 305 | head(sumrepdat); str(sumrepdat) 306 | 307 | ################################################# 308 | #Example 7: Rainclouds for repeated measures, continued 309 | ################################################# 310 | 311 | p7 <- ggplot(rep_data, aes(x = time, y = score, fill = group)) + 312 | geom_flat_violin(aes(fill = group),position = position_nudge(x = .1, y = 0), adjust = 1.5, trim = FALSE, alpha = .5, colour = NA)+ 313 | geom_point(aes(x = as.numeric(time)-.15, y = score, colour = group),position = position_jitter(width = .05), size = 1, shape = 20)+ 314 | geom_boxplot(aes(x = time, y = score, fill = group),outlier.shape = NA, alpha = .5, width = .1, colour = "black")+ 315 | scale_colour_brewer(palette = "Dark2")+ 316 | scale_fill_brewer(palette = "Dark2")+ 317 | ggtitle("Figure 10: Repeated Measures Factorial Rainclouds") 318 | 319 | p7 320 | 321 | # can you make a similar graph with geom_rain? hint ggpp::position_jitternudge & ggpp::position_dodgenudge exist 322 | #*answer 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | ################################################# 335 | #Example 8: Rainclouds for repeated measures, additional plotting options 336 | ################################################# 337 | 338 | p8 <- ggplot(rep_data, aes(x = time, y = score, fill = group)) + 339 | geom_flat_violin(aes(fill = group),position = position_nudge(x = .1, y = 0), adjust = 1.5, trim = FALSE, alpha = .5, colour = NA)+ 340 | geom_point(aes(x = as.numeric(time)-.15, y = score, colour = group),position = position_jitter(width = .05), size = .25, shape = 20)+ 341 | geom_boxplot(aes(x = time, y = score, fill = group),outlier.shape = NA, alpha = .5, width = .1, colour = "black")+ 342 | geom_line(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group), linetype = 3)+ 343 | geom_point(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group), shape = 18) + 344 | geom_errorbar(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), width = .05)+ 345 | scale_colour_brewer(palette = "Dark2")+ 346 | scale_fill_brewer(palette = "Dark2")+ 347 | ggtitle("Figure 11: Repeated Measures - Factorial (Extended)") 348 | p8 349 | 350 | # can you replicate this with geom_rain? hint use some of the code in the last examples 351 | #*answer 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | ################################################# 367 | ################## advanced ################# 368 | ################################################# 369 | #Example 9: can you connect the individual data points using id.long.var arg in geom_rain? 370 | ################################################# 371 | 372 | #*answer 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | # can you subset the first two timepoints and make a flanking raincloud with connected points? 387 | # hint check side argument in geom_rain 388 | #*answer 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | -------------------------------------------------------------------------------- /tutorial/R_tutorial/20230213_rainclouds_tutorial_cheatsheet.R: -------------------------------------------------------------------------------- 1 | # running on MacOS 12.5 Rstudio 2022.07.01 on 22-10-2022 by Nicholas Judd 2 | # updated for ggrain on 2023-02-11 3 | # original source: https://github.com/RainCloudPlots/RainCloudPlots/tree/master/tutorial_R 4 | 5 | # R package https://github.com/njudd/ggrain 6 | # R package vingette https://www.njudd.com/raincloud-ggrain/ 7 | 8 | if (!require(pacman)) { 9 | install.packages("pacman") 10 | } 11 | pacman::p_load(patchwork, tidyverse, lavaan, ggpp, plyr, 12 | ggrain) #for rainclouds 13 | 14 | getwd() # this tells you the current path of R 15 | # we need to direct R to the folder with the scripts & data 16 | # we set the path by using setwd() 17 | # for example: 18 | # setwd("/Users/njudd/projects/rain/raincloudplots-workshops/tutorial/R_tutorial") 19 | #or select Session->set working directory 20 | 21 | setwd("/Users/njudd/surfdrive/Shared/NWO Open Science Fund/Workshops/Workshop6 - Marburg/") 22 | 23 | source("geom_flat_violin.R") #this only works if you set the right path! 24 | source("fn_summary_SE.r") # This does the summary. For each group's data frame, return a vector with 25 | # N, mean, median, and sd 26 | 27 | ################################################# 28 | # first we will start by simulating some data! :) 29 | ################################################# 30 | m <- 50 # mean 31 | s <- 25 # sd 32 | sim_n <- 250 # draws 33 | 34 | # Calculate log-normal parameters ---- 35 | location <- log(m^2 / sqrt(s^2 + m^2)) 36 | shape <- sqrt(log(1 + (s^2 / m^2))) 37 | 38 | # Set seed to get same data everytime ---- 39 | set.seed(42) 40 | 41 | # Create data by hand ---- 42 | simdat_group1 <- rlnorm(sim_n, location, shape) 43 | simdat_group2 <- rnorm(sim_n, m, s) 44 | 45 | simdat <- c(simdat_group1, simdat_group2) %>% as_tibble() %>% dplyr::rename(score = 1) 46 | simdat <- simdat %>% mutate(group = 47 | fct_inorder(c(rep("Group1", times = sim_n), 48 | rep("Group2", times = sim_n)))) 49 | 50 | # Calculate summary stats ---- 51 | summary_simdat <- fn_summary_SE(simdat, score, group) 52 | 53 | # lets look at the data 54 | head(simdat) 55 | str(simdat) 56 | summary_simdat 57 | 58 | 59 | ################################################# 60 | # Example 1: Two group plot with colours and coordinate flip 61 | ################################################# 62 | 63 | # lets plot the individual data points of each group jittered 64 | p1 <- ggplot(simdat, aes(x=group,y=score, fill = group))+ 65 | geom_point(position = position_jitter(width = .15), size = .25) + 66 | labs(title = 'Figure 1: The Basic Raincloud with Colour', y = 'Score', x = 'Group') + 67 | theme_classic()+guides(fill = "none") 68 | p1 69 | # lets flip the points 70 | p1 <- p1 + coord_flip() 71 | p1 72 | 73 | # here we have a raincloud with two groups 74 | p1 + geom_flat_violin(position = position_nudge(x = .2, y = 0),adjust = 2) 75 | 76 | # we can also change the smoothing with adjust 77 | # here is one with reduced smoothing 78 | p1 + geom_flat_violin(position = position_nudge(x = .2, y = 0), adjust = .1) 79 | 80 | ################################################# 81 | # Example 2: now we will have a basic example of a single raincloud plot 82 | ################################################# 83 | p2_h <- ggplot(simdat, aes(x = 1, y=score)) + 84 | geom_flat_violin(position = position_nudge(x = .23, y = 0),adjust = 2, width = .5, fill = "red", color = NA)+ 85 | geom_boxplot(position = position_nudge(x = .17, y = 0), width = .1, outlier.colour = NA, fill = "red") + 86 | geom_point(position = position_jitter(width = .10), size = .3, color = "red", alpha = .4)+ 87 | coord_cartesian(xlim = c(0.2, 2)) + # change to .75 & 1.5 to zoom in 88 | labs(x = "Group", y = "Score") + 89 | theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) + 90 | theme_minimal() 91 | 92 | p2_h # printing the plot 93 | p2_v <- p2_h + coord_flip() #flipping the plot 94 | p2_v 95 | 96 | # now lets do it with rainclouds 97 | ggplot(simdat, aes(x = 1, y=score)) + 98 | geom_rain() + 99 | coord_flip() + 100 | theme_minimal() 101 | 102 | # can you make them red? 103 | #*answer 104 | p2_rain <- ggplot(simdat, aes(x = 1, y=score)) + 105 | geom_rain(point.args = list(color = "red"), 106 | boxplot.args = list(fill = "red", outlier.color = NA), 107 | violin.args = list(fill = "red", color = NA)) + 108 | coord_flip() + 109 | theme_minimal() 110 | p2_rain 111 | 112 | #try to save the plot with ggsave()! 113 | #ggsave("~/Desktop/p0_v.png", p0_v, bg = "white") # need to specify bg white for theme_minimal now 114 | 115 | #If you look at the two plots you can tell they use a different amount of smoothing 116 | p2_v 117 | p2_rain 118 | 119 | # can you modify the violin to have similar smoothing? 120 | #answer* 121 | ggplot(simdat, aes(x = 1, y=score)) + 122 | geom_rain(point.args = list(color = "red"), 123 | boxplot.args = list(fill = "red", outlier.color = NA), 124 | violin.args = list(fill = "red", color = NA, adjust = 2)) + 125 | coord_flip() + 126 | theme_minimal() 127 | 128 | ################################################# 129 | # Example 3: Two group raincloud 130 | ################################################# 131 | 132 | # lets make two group raincloud 133 | p3 <- ggplot(simdat, aes(x=group,y=score, fill = group))+ 134 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2)+ 135 | geom_point(position = position_jitter(width = .15), size = .25)+ 136 | #note that here we need to set the x-variable to a numeric variable and bump it to get the boxplots to line up with the rainclouds. 137 | geom_boxplot(aes(x = as.numeric(group)+0.25, y = score), outlier.shape = NA, alpha = 0.3, width = .1, colour = "BLACK") + 138 | ylab('Score')+xlab('Group')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + 139 | labs(title = 'Figure 3: Raincloud Plot w/Boxplots', x = 'Group', y = 'Score') 140 | p3 141 | 142 | # we can change the colors! 143 | p3c <- p3 + ggtitle("Figure 3c: Change in Colour Palette") + 144 | scale_colour_brewer(palette = "Dark2") + 145 | scale_fill_brewer(palette = "Dark2") 146 | p3c 147 | 148 | # lets do it with ggrain; it doesn't have to look the exact same 149 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 150 | geom_rain() 151 | 152 | # position arguements are given as a list for each element 153 | # when you give them the defaults are written over 154 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 155 | geom_rain(violin.args.pos = list(side = "r", width = 0.7, position = position_nudge(x =0.3))) 156 | 157 | # can you change the side with rain.side arguement? 158 | #*answer 159 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 160 | geom_rain(rain.side = "l") 161 | 162 | # can you change the smoothing? 163 | #answer* 164 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 165 | geom_rain(rain.side = "l", violin.args = list(adjust = 2)) 166 | 167 | # can you flip them? 168 | #answer* 169 | ggplot(simdat, aes(x=group,y=score, fill = group)) + 170 | geom_rain(rain.side = "l", violin.args = list(adjust =2)) + 171 | coord_flip() 172 | 173 | ################################################# 174 | # Example 4: advanced rainclouds! 175 | ################################################# 176 | 177 | #Rainclouds with mean and confidence interval 178 | p4 <- ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 179 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2)+ 180 | geom_point(position = position_jitter(width = .15), size = .25)+ 181 | geom_point(data = summary_simdat, aes(x = group, y = score_mean), position = position_nudge(.25), colour = "BLACK")+ 182 | geom_errorbar(data = summary_simdat, aes(x = group, y = score_mean, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), position = position_nudge(.25), colour = "BLACK", width = 0.1, size = 0.8)+ 183 | ylab('Score')+xlab('Group')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + 184 | scale_colour_brewer(palette = "Dark2")+ 185 | scale_fill_brewer(palette = "Dark2")+ 186 | ggtitle("Figure 7: Raincloud Plot with Mean ± 95% CI") 187 | p4 188 | 189 | # see if you can get stat_summary to do the same! (check out the lines commented off) 190 | # If you have issues installing Hmisc just move on 191 | 192 | p4_c <- ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 193 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2)+ 194 | geom_point(position = position_jitter(width = .15), size = .25) + 195 | #stat_summary(aes(y = score), fun = "mean", position = position_nudge(.25), colour = "BLACK", geom = "point") + 196 | #stat_summary(aes(y = score), fun.data = "mean_cl_normal", position = position_nudge(.25), colour = "BLACK", geom = "errorbar", width = .1) + 197 | geom_point(data = summary_simdat, aes(x = group, y = score_mean), position = position_nudge(.25), colour = "BLACK")+ 198 | geom_errorbar(data = summary_simdat, aes(x = group, y = score_mean, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), position = position_nudge(.25), colour = "BLACK", width = 0.1, size = 0.8)+ 199 | ylab('Score')+xlab('Group')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + 200 | scale_colour_brewer(palette = "Dark2")+ 201 | scale_fill_brewer(palette = "Dark2")+ 202 | ggtitle("Figure 7: Raincloud Plot with Mean ± 95% CI") 203 | p4_c 204 | 205 | # Now try to add stat_summary to geom_rain() 206 | # see vingette https://www.njudd.com/raincloud-ggrain/ for help :) 207 | #answer* 208 | ggplot(simdat,aes(x=group, y=score, fill = group))+ 209 | geom_rain(boxplot.args = list(color = NA, fill = NA), violin.args = list(color = NA)) + 210 | stat_summary(fun = mean, geom = "point", aes(color = group), color = "black", position = position_nudge(x = .15, y = 0)) + 211 | stat_summary(fun = mean, geom = "errorbar", fun.data = "mean_cl_normal", color = "black", position = position_nudge(x = .15, y = 0), 212 | aes(color = group, width = .1)) + 213 | coord_flip() + 214 | scale_colour_brewer(palette = "Dark2")+ 215 | scale_fill_brewer(palette = "Dark2") 216 | 217 | 218 | ################################################# 219 | #Example 5: Rainclouds with striated data 220 | ################################################# 221 | 222 | #Round data 223 | simdat_round<-simdat 224 | simdat_round$score<-round(simdat$score,0) 225 | 226 | #Striated/grouped when no jitter applied 227 | ap5.1 <- ggplot(simdat_round,aes(x=group,y=score,fill=group,col=group))+ 228 | geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .6,adjust =4)+ 229 | geom_point(size = 1, alpha = 0.6)+ylab('Score')+xlab("")+ 230 | scale_fill_brewer(palette = "Dark2")+ 231 | scale_colour_brewer(palette = "Dark2")+ 232 | guides(fill = "none", col = "none")+ 233 | ggtitle('Striated') 234 | 235 | #Added jitter helps 236 | ap5.2 <- ggplot(simdat_round,aes(x=group,y=score,fill=group,col=group))+ 237 | geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .4,adjust =4)+ 238 | geom_point(position=position_jitter(width = .15),size = 1, alpha = 0.4)+ylab('Score')+ xlab("")+ 239 | scale_fill_brewer(palette = "Dark2")+ 240 | scale_colour_brewer(palette = "Dark2")+ 241 | guides(fill = "none", col = "none")+ 242 | ggtitle('Added jitter') 243 | 244 | 245 | # with patchwork you can add ggplots together! 246 | p5 <- ap5.1 + ap5.2 + plot_annotation("Figure 8: Jittering Ordinal Data", tag_levels = "a") 247 | p5 248 | 249 | # can you make two similar ones quickly with geom_rain and patch them together? 250 | #*answer 251 | #* 252 | ap5.1_r <- ggplot(simdat_round,aes(x=group,y=score,fill=group)) + 253 | geom_rain(point.args.pos = list(), point.args = list(alpha = .4)) + 254 | # geom_rain(point.args.pos = list(alpha = .4)) # this would work but it is not proper convention 255 | scale_fill_brewer(palette = "Dark2") + 256 | guides(fill = "none", col = "none") 257 | 258 | ap5.2_r <- ggplot(simdat_round,aes(x=group,y=score,fill=group)) + 259 | geom_rain(point.args = list(alpha = .4)) + 260 | scale_fill_brewer(palette = "Dark2") + 261 | guides(fill = "none", col = "none") 262 | p5_r <- ap5.1_r + ap5.2_r + plot_annotation("Figure 8: Jittering Ordinal Data", tag_levels = "a") 263 | p5_r 264 | 265 | 266 | ################################################# 267 | #Example 6: Add additional factor/condition 268 | ################################################# 269 | 270 | simdat$gr2<-as.factor(c(rep('high',125),rep('low',125),rep('high',125),rep('low',125))) 271 | 272 | p6 <- ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 273 | geom_flat_violin(position = position_nudge(x = .25, y = 0),adjust =2, trim = TRUE)+ 274 | geom_point(position = position_jitter(width = .15), size = .25)+ 275 | geom_boxplot(aes(x = as.numeric(group)+0.25, y = score),outlier.shape = NA, alpha = 0.3, width = .1, colour = "BLACK") + 276 | ylab('Score')+xlab('')+coord_flip()+theme_classic()+guides(fill = "none", colour = "none") + facet_wrap(~gr2)+ 277 | scale_colour_brewer(palette = "Dark2")+ 278 | scale_fill_brewer(palette = "Dark2")+ 279 | ggtitle("Figure 9: Complex Raincloud Plots with Facet Wrap") 280 | p6 281 | 282 | # can you make a similar one with ggrain? 283 | #*answer 284 | ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 285 | geom_rain() + 286 | scale_fill_brewer(palette = "Dark2") + 287 | scale_color_brewer(palette = "Dark2") + 288 | coord_flip() + 289 | facet_wrap(~gr2) 290 | 291 | # can you see the median and the IQR of the boxplot? 292 | # if you can't try to fix it with ggrain while also having the violin without a boarder 293 | #*answer 294 | ggplot(simdat,aes(x=group,y=score, fill = group, colour = group))+ 295 | geom_rain(boxplot.args = list(color = "black", outlier.color = NA)) + 296 | scale_fill_brewer(palette = "Dark2") + 297 | scale_color_brewer(palette = "Dark2") + 298 | coord_flip() + 299 | facet_wrap(~gr2) 300 | 301 | # p10 needs a csv in the same folder 302 | # loading the data: make sure to get getwd() and do setwd() 303 | rep_data <- read_csv("repeated_measures_data.csv", 304 | col_types = cols(group = col_factor(levels = c("1", "2")), 305 | time = col_factor(levels = c("1", "2", "3")))) 306 | 307 | sumrepdat <- fn_summary_SE(rep_data, "score", c("group", "time")) 308 | head(sumrepdat); str(sumrepdat) 309 | 310 | ################################################# 311 | #Example 7: Rainclouds for repeated measures, continued 312 | ################################################# 313 | 314 | p7 <- ggplot(rep_data, aes(x = time, y = score, fill = group)) + 315 | geom_flat_violin(aes(fill = group),position = position_nudge(x = .1, y = 0), adjust = 1.5, trim = FALSE, alpha = .5, colour = NA)+ 316 | geom_point(aes(x = as.numeric(time)-.15, y = score, colour = group),position = position_jitter(width = .05), size = 1, shape = 20)+ 317 | geom_boxplot(aes(x = time, y = score, fill = group),outlier.shape = NA, alpha = .5, width = .1, colour = "black")+ 318 | scale_colour_brewer(palette = "Dark2")+ 319 | scale_fill_brewer(palette = "Dark2")+ 320 | ggtitle("Figure 10: Repeated Measures Factorial Rainclouds") 321 | 322 | p7 323 | 324 | # can you make a similar graph with geom_rain? hint ggpp::position_jitternudge & ggpp::position_dodgenudge exist 325 | #*answer 326 | ggplot(rep_data, aes(x = time, y = score, fill = group, color = group)) + 327 | geom_rain(alpha = .8, 328 | boxplot.args = list(color = "black", outlier.color = NA), 329 | violin.args = list(adjust = 1.5, trim = FALSE), 330 | point.args.pos = list(position = ggpp::position_jitternudge(width = 0.04, height = 0, x = -.2, 331 | y = 0, nudge.from = "jittered")), 332 | boxplot.args.pos = list(position = ggpp::position_dodgenudge(x = .0), width = .2)) + 333 | scale_color_brewer(palette = "Dark2") + 334 | scale_fill_brewer(palette = "Dark2") + 335 | ggtitle("Figure 10: Repeated Measures Factorial Rainclouds with geom_rain") 336 | 337 | ################################################# 338 | #Example 8: Rainclouds for repeated measures, additional plotting options 339 | ################################################# 340 | 341 | p8 <- ggplot(rep_data, aes(x = time, y = score, fill = group)) + 342 | geom_flat_violin(aes(fill = group),position = position_nudge(x = .1, y = 0), adjust = 1.5, trim = FALSE, alpha = .5, colour = NA)+ 343 | geom_point(aes(x = as.numeric(time)-.15, y = score, colour = group),position = position_jitter(width = .05), size = .25, shape = 20)+ 344 | geom_boxplot(aes(x = time, y = score, fill = group),outlier.shape = NA, alpha = .5, width = .1, colour = "black")+ 345 | geom_line(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group), linetype = 3)+ 346 | geom_point(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group), shape = 18) + 347 | geom_errorbar(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), width = .05)+ 348 | scale_colour_brewer(palette = "Dark2")+ 349 | scale_fill_brewer(palette = "Dark2")+ 350 | ggtitle("Figure 11: Repeated Measures - Factorial (Extended)") 351 | p8 352 | 353 | # can you replicate this with geom_rain? hint use some of the code in the last examples 354 | #*answer 355 | 356 | ggplot(rep_data, aes(x = time, y = score, fill = group, color = group)) + 357 | geom_rain(boxplot.args = list(color = "black", outlier.color = NA), 358 | violin.args = list(adjust = 1.5, trim = FALSE), 359 | point.args.pos = list(position = ggpp::position_jitternudge(width = 0.04, height = 0, x = -.15, 360 | y = 0, nudge.from = "jittered")), 361 | boxplot.args.pos = list(position = ggpp::position_dodgenudge(x = .0), width = .1)) + 362 | geom_line(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group), linetype = 3)+ 363 | geom_point(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group), shape = 18) + 364 | geom_errorbar(data = sumrepdat, aes(x = as.numeric(time)+.1, y = score_mean, group = group, colour = group, ymin = score_mean-score_sem_ci, ymax = score_mean+score_sem_ci), width = .05)+ 365 | scale_colour_brewer(palette = "Dark2")+ 366 | scale_fill_brewer(palette = "Dark2")+ 367 | ggtitle("Figure 11: Repeated Measures - Factorial (Extended)") 368 | 369 | ################################################# 370 | ################## advanced ################# 371 | ################################################# 372 | #Example 9: can you connect the individual data points using id.long.var arg in geom_rain? 373 | ################################################# 374 | 375 | #*answer 376 | table(rep_data$time) 377 | 378 | rep_data <- rep_data[order(rep_data$time),] 379 | rep_data$id <- rep(1:29,3) 380 | 381 | ggplot(rep_data, aes(x = time, y = score, fill = group, color = group)) + 382 | geom_rain(id.long.var = "id", 383 | line.args = list(color = "black", alpha = .2), 384 | boxplot.args = list(color = "black", outlier.color = NA), 385 | violin.args = list(adjust = 1.5, trim = FALSE), 386 | boxplot.args.pos = list(position = ggpp::position_dodgenudge(x = .1), width = .1)) + 387 | scale_color_brewer(palette = "Dark2") + scale_fill_brewer(palette = "Dark2") 388 | 389 | # can you subset the first two timepoints and make a flanking raincloud with connected points? 390 | # hint check side argument in geom_rain 391 | #*answer 392 | rep_data <- rep_data[rep_data$time != "3",] 393 | ggplot(rep_data, aes(x = time, y = score, fill = group, color = group)) + 394 | geom_rain(id.long.var = "id", rain.side = "f2x2", 395 | boxplot.args = list(color = "black", outlier.color = NA), 396 | violin.args = list(adjust = 1.5, trim = FALSE)) + 397 | scale_color_brewer(palette = "Dark2") + scale_fill_brewer(palette = "Dark2") + 398 | theme_minimal() 399 | 400 | -------------------------------------------------------------------------------- /tutorial/R_tutorial/fn_summary_SE.r: -------------------------------------------------------------------------------- 1 | # function updated from Rogier Kievit 2 | # 28-10-22 Nicholas Judd nickkjudd@gmail.com 3 | # https://dplyr.tidyverse.org/reference/across.html 4 | 5 | fn_summary_SE <- function(tblData, MeasureVar, arrGroupVars, dblCI = .95, blNa = TRUE) { 6 | 7 | sum_statistics <- tblData %>% 8 | group_by(across({{ arrGroupVars }})) %>% 9 | dplyr::summarise(across({{MeasureVar}}, 10 | list(N = ~length(.x), 11 | mean = ~ mean(.x, na.rm = blNa), #blNA might not work 12 | median = ~ median(.x, na.rm = blNa), 13 | sd = ~ sd(.x, na.rm = blNa), 14 | sem = ~ sd(.x, na.rm = blNa)/sqrt(length(.x)), # sd/sqrt(n) 15 | sem_ci = ~ (sd(.x, na.rm = blNa)/sqrt(length(.x))) * qt(dblCI / 2 + .5, length(.x) - 1)), 16 | .names = "{.col}_{.fn}")) 17 | return(sum_statistics) 18 | } 19 | 20 | # old function doesn't work with multiple groups 21 | 22 | # fn_summary_SE_old <- function(tblData, quoMeasureVar, arrGroupVars, dblCI = .95, blNa = TRUE) { 23 | # sum_statistics <- tblData %>% 24 | # group_by(across({{ arrGroupVars }})) %>% 25 | # dplyr::summarise( 26 | # N = n(), 27 | # score_mean := mean(!!quoMeasureVar, na.rm = blNa), 28 | # score_median := median(!!quoMeasureVar, na.rm = blNa), 29 | # sd := sd(!!quoMeasureVar, na.rm = blNa), 30 | # sem = sd / sqrt(N), 31 | # sem_ci = sem * qt(dblCI / 2 + .5, N - 1) 32 | # ) 33 | # 34 | # return(sum_statistics) 35 | # } 36 | 37 | # older summary function with explanations (forces pylr installation) 38 | 39 | # summarySE <- function(data = NULL, measurevar, groupvars = NULL, na.rm = FALSE, 40 | # conf.interval = .95, .drop = TRUE) { 41 | # 42 | # # New version of length which can handle NA's: if na.rm==T, don't count them 43 | # length2 <- function(x, na.rm = FALSE) { 44 | # if (na.rm) { 45 | # sum(!is.na(x)) 46 | # } else { 47 | # length(x) 48 | # } 49 | # } 50 | # 51 | # # This does the summary. For each group's data frame, return a vector with 52 | # # N, mean, median, and sd 53 | # 54 | # datac <- plyr::ddply(data, groupvars, .drop=.drop, 55 | # .fun = function(xx, col) { 56 | # c(N = length2(xx[[col]], na.rm=na.rm), 57 | # mean = mean(xx[[col]], na.rm=na.rm), 58 | # median = median(xx[[col]], na.rm=na.rm), 59 | # sd = sd(xx[[col]], na.rm=na.rm) 60 | # ) 61 | # }, 62 | # measurevar 63 | # ) 64 | # 65 | # 66 | # # playspace 67 | # ct <- plyr::ddply(rep_data, c("group", "time"), .drop=T, 68 | # .fun = function(xx, col) { 69 | # c(N = length2(xx[[col]], na.rm=F), 70 | # mean = mean(xx[[col]], na.rm=F), 71 | # median = median(xx[[col]], na.rm=F), 72 | # sd = sd(xx[[col]], na.rm=F) 73 | # ) 74 | # }, 75 | # "score" 76 | # ) 77 | # 78 | # ct1 <- rep_data %>% 79 | # group_by(group, time) %>% # so this needs to be changed from a char 80 | # summarise(N = n(), 81 | # mean = mean(score), 82 | # median = median(score), 83 | # sd = sd(score), 84 | # sem = sd / sqrt(N), 85 | # ci = sem * qt(.95 / 2 + .5, N - 1)) 86 | # 87 | # 88 | # 89 | # 90 | # # Rename the "mean" and "median" columns 91 | # datac <- plyr::rename(datac, c("mean" = paste0(measurevar, "_mean", sep = ""))) 92 | # datac <- plyr::rename(datac, c("median" = paste(measurevar, "_median", sep = ""))) 93 | # 94 | # datac$sem <- datac$sd / sqrt(datac$N) # Calculate standard error of the mean 95 | # 96 | # # Confidence interval multiplier for standard error 97 | # # Calculate t-statistic for confidence interval: 98 | # # e.g., if conf.interval is .95, use .975 (above/below), and use df=N-1 99 | # ciMult <- qt(conf.interval / 2 + .5, datac$N - 1) 100 | # datac$ci <- datac$sem * ciMult 101 | # 102 | # return(datac) 103 | # } 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /tutorial/R_tutorial/geom_flat_violin.R: -------------------------------------------------------------------------------- 1 | # Defining the geom_flat_violin function ---- 2 | # Note: the below code modifies the 3 | # existing github page by removing a parenthesis in line 50 4 | 5 | "%||%" <- function(a, b) { 6 | if (!is.null(a)) a else b 7 | } 8 | 9 | geom_flat_violin <- function(mapping = NULL, data = NULL, stat = "ydensity", 10 | position = "dodge", trim = TRUE, scale = "area", 11 | show.legend = NA, inherit.aes = TRUE, ...) { 12 | layer( 13 | data = data, 14 | mapping = mapping, 15 | stat = stat, 16 | geom = GeomFlatViolin, 17 | position = position, 18 | show.legend = show.legend, 19 | inherit.aes = inherit.aes, 20 | params = list( 21 | trim = trim, 22 | scale = scale, 23 | ... 24 | ) 25 | ) 26 | } 27 | 28 | #' @rdname ggplot2-ggproto 29 | #' @format NULL 30 | #' @usage NULL 31 | #' @export 32 | GeomFlatViolin <- 33 | ggproto("GeomFlatViolin", Geom, 34 | setup_data = function(data, params) { 35 | data$width <- data$width %||% 36 | params$width %||% (resolution(data$x, FALSE) * 0.9) 37 | 38 | # ymin, ymax, xmin, and xmax define the bounding rectangle for each group 39 | data %>% 40 | group_by(group) %>% 41 | mutate( 42 | ymin = min(y), 43 | ymax = max(y), 44 | xmin = x, 45 | xmax = x + width / 2 46 | ) 47 | }, 48 | 49 | draw_group = function(data, panel_scales, coord) { 50 | # Find the points for the line to go all the way around 51 | data <- transform(data, 52 | xminv = x, 53 | xmaxv = x + violinwidth * (xmax - x) 54 | ) 55 | 56 | # Make sure it's sorted properly to draw the outline 57 | newdata <- rbind( 58 | plyr::arrange(transform(data, x = xminv), y), 59 | plyr::arrange(transform(data, x = xmaxv), -y) 60 | ) 61 | 62 | # Close the polygon: set first and last point the same 63 | # Needed for coord_polar and such 64 | newdata <- rbind(newdata, newdata[1, ]) 65 | 66 | ggplot2:::ggname("geom_flat_violin", GeomPolygon$draw_panel(newdata, panel_scales, coord)) 67 | }, 68 | 69 | draw_key = draw_key_polygon, 70 | 71 | default_aes = aes( 72 | weight = 1, colour = "grey20", fill = "white", size = 0.5, 73 | alpha = NA, linetype = "solid" 74 | ), 75 | 76 | required_aes = c("x", "y") 77 | ) 78 | 79 | -------------------------------------------------------------------------------- /tutorial/R_tutorial/repeated_measures_data.csv: -------------------------------------------------------------------------------- 1 | score,time,group 2 | 5.57,1,1 3 | 7.43,1,1 4 | 4.37,1,1 5 | 8.22,1,1 6 | 8.17,1,1 7 | 4.6,1,1 8 | 5.52,1,1 9 | 6.4,1,1 10 | 4.55,1,1 11 | 6.94,1,1 12 | 7.3,1,1 13 | 6.26,1,1 14 | 7.34,1,1 15 | 2.66,1,1 16 | 5.08,1,1 17 | 7.53,1,1 18 | 8.96,1,1 19 | 7.62,1,1 20 | 7.81,2,1 21 | 8.9,2,1 22 | 7.65,2,1 23 | 5.76,2,1 24 | 5.25,2,1 25 | 6.8,2,1 26 | 8.59,2,1 27 | 9.35,2,1 28 | 6.13,2,1 29 | 6.65,2,1 30 | 8.93,2,1 31 | 8.09,2,1 32 | 8.73,2,1 33 | 4.7,2,1 34 | 5.18,2,1 35 | 8.83,2,1 36 | 9.48,2,1 37 | 7.6,2,1 38 | 9.85,3,1 39 | 10.06,3,1 40 | 9.97,3,1 41 | 10.53,3,1 42 | 11.82,3,1 43 | 10.68,3,1 44 | 11.65,3,1 45 | 11.64,3,1 46 | 9.51,3,1 47 | 9.54,3,1 48 | 11.02,3,1 49 | 10.55,3,1 50 | 12.3,3,1 51 | 7.99,3,1 52 | 10.38,3,1 53 | 11.57,3,1 54 | 9.88,3,1 55 | 9.75,3,1 56 | 1.17,1,2 57 | 0.86,1,2 58 | 1.21,1,2 59 | 0.45,1,2 60 | 0.09,1,2 61 | 7.51,1,2 62 | 1.98,1,2 63 | 2.72,1,2 64 | 1.55,1,2 65 | 1.69,1,2 66 | 1.09,1,2 67 | 1.6,2,2 68 | 1.97,2,2 69 | 3.21,2,2 70 | 2.66,2,2 71 | 1.86,2,2 72 | 8.74,2,2 73 | 4.78,2,2 74 | 6.04,2,2 75 | 2.82,2,2 76 | 3.93,2,2 77 | 2.92,2,2 78 | 4.64,3,2 79 | 5.13,3,2 80 | 6.24,3,2 81 | 8.59,3,2 82 | 5.85,3,2 83 | 11.58,3,2 84 | 10.14,3,2 85 | 8.97,3,2 86 | 5.28,3,2 87 | 7.5,3,2 88 | 7.02,3,2 89 | --------------------------------------------------------------------------------