ggridges/0000755000176200001440000000000014553650472012060 5ustar liggesusersggridges/NAMESPACE0000644000176200001440000000421214536504206013270 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(GeomDensityLine) export(GeomDensityRidges) export(GeomDensityRidges2) export(GeomDensityRidgesGradient) export(GeomRidgeline) export(GeomRidgelineGradient) export(GeomVRidgeline) export(PositionPointsJitter) export(PositionPointsSina) export(PositionRaincloud) export(ScaleCyclical) export(StatBinline) export(StatDensityRidges) export(cyclical_scale) export(geom_density_line) export(geom_density_ridges) export(geom_density_ridges2) export(geom_density_ridges_gradient) export(geom_ridgeline) export(geom_ridgeline_gradient) export(geom_vridgeline) export(position_points_jitter) export(position_points_sina) export(position_raincloud) export(scale_alpha_cyclical) export(scale_color_cyclical) export(scale_colour_cyclical) export(scale_fill_cyclical) export(scale_linetype_cyclical) export(scale_point_color_continuous) export(scale_point_color_discrete) export(scale_point_color_gradient) export(scale_point_color_hue) export(scale_point_colour_continuous) export(scale_point_colour_discrete) export(scale_point_colour_gradient) export(scale_point_colour_hue) export(scale_point_fill_continuous) export(scale_point_fill_discrete) export(scale_point_fill_gradient) export(scale_point_fill_hue) export(scale_point_shape) export(scale_point_shape_discrete) export(scale_point_size_continuous) export(scale_size_cyclical) export(scale_vline_color_continuous) export(scale_vline_color_discrete) export(scale_vline_color_gradient) export(scale_vline_color_hue) export(scale_vline_colour_continuous) export(scale_vline_colour_discrete) export(scale_vline_colour_gradient) export(scale_vline_colour_hue) export(scale_vline_linetype) export(scale_vline_linetype_discrete) export(scale_vline_width_continuous) export(stat_binline) export(stat_density_ridges) export(theme_ridges) import(ggplot2) importFrom(ggplot2,Geom) importFrom(ggplot2,ScaleDiscrete) importFrom(ggplot2,Stat) importFrom(ggplot2,StatBin) importFrom(ggplot2,draw_key_polygon) importFrom(ggplot2,geom_density) importFrom(ggplot2,ggproto) importFrom(ggplot2,layer) importFrom(grid,gList) importFrom(grid,gTree) importFrom(scales,train_discrete) importFrom(stats,quantile) ggridges/LICENSE0000644000176200001440000004317614536504206013072 0ustar liggesusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ggridges/README.md0000644000176200001440000000367614536610215013343 0ustar liggesusers # ggridges: Ridgeline plots in ggplot2 [![R build status](https://github.com/wilkelab/ggridges/workflows/R-CMD-check/badge.svg)](https://github.com/wilkelab/ggridges/actions) [![Coverage Status](https://img.shields.io/codecov/c/github/wilkelab/ggridges/master.svg)](https://app.codecov.io/github/wilkelab/ggridges?branch=master) [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/ggridges)](https://CRAN.R-project.org/package=ggridges) [![CRAN_Downloads_Badge](https://cranlogs.r-pkg.org/badges/ggridges)](https://cranlogs.r-pkg.org/downloads/total/last-month/ggridges) [![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html#maturing) Ridgeline plots are partially overlapping line plots that create the impression of a mountain range. They can be quite useful for visualizing changes in distributions over time or space. ## Installation Please install the stable release from CRAN: ``` r install.packages("ggridges") ``` Alternatively, you can install the latest development version from github: ``` r remotes::install_github("wilkelab/ggridges") ``` ## Usage ``` r library(ggplot2) library(ggridges) ggplot(diamonds, aes(x = price, y = cut)) + geom_density_ridges(scale = 4) + scale_y_discrete(expand = c(0, 0)) + # will generally have to set the `expand` option scale_x_continuous(expand = c(0, 0)) + # for both axes to remove unneeded padding coord_cartesian(clip = "off") + # to avoid clipping of the very top of the top ridgeline theme_ridges() #> Picking joint bandwidth of 458 ``` ![](man/figures/README-diamonds-1.png) ## Documentation and Examples First read the [package vignette.](https://wilkelab.org/ggridges/articles/introduction.html) Then read the [reference manual.](https://wilkelab.org/ggridges/reference/index.html) ggridges/data/0000755000176200001440000000000014536504206012763 5ustar liggesusersggridges/data/Aus_athletes.rda0000644000176200001440000001165314536504206016102 0ustar liggesusersBZh91AY&SYhc߿o}oy=k;WNVx"$gKw\iS4i5=f@LRzO0iL5`ɨ@P44d='B) &*@C@C@HC'=&i 4M@F4 Ph@)$e=Gꍩ4h@2FFh@ѡ F4 bh2=M0 Fjy&ڦjzQOQLi OQڃCOHCA 1iq!,cͺh1!ûiN)q'nNNt#8j*t`i|;91'5P&l?N١dbx}*`(L`zhQm9Qה] "D,$=Z)yd"r&m4;EI2@%g )]]&kkjw/T8 lͦ4 MCr0n~@vD$ n*;VPo`eH :(h+d]]xWW !we,-## *6zUS;g6oQQ-ɮ&2 .YFZOiB9Xl܎KS 79 FijDBR.Rl۴ bYdvF,;)r1CVccf2^h1XH2ZF=x!F(uNǽOܟqE<wtjbm/ossų$-`WĻ[sAФ+oPxQJj@ xA*T@žiHI  2qXHJ+"!bƙ*bUr%iAIjg*d*,XDLB!\H*[")!TUIi"\iTk%HaVrS33v4⍃PQ% +$480EC#T @̕E#H$ S%#9PT*B,.Q-1*2@`+P4e2gaa㺛nO^En-lY|?_99#w)q PԶD MbǍޞ+ŗ&g:},,l'A7U`g,~Ug8aт\e@0mRdtn9t*]O99=i:~VApwe% §p7@ ј2 j/#m8ܞqf]e4,pH"U 86@8j3Ec>]yoMdG_bbҔVQb b [Fp)\ UUbR mܠLk5V[ 5s9:e9 U|\Yh˝[XEbt{YFfŧ%)Zgg弰bࢮ+$!$*@5&j[FQ c #bv7.Q&sjtuS/Ȣ]R,SnS-4 D3 L0ύai*kr'kZ?)rĸ4 + J>9Ip5ӕ'IU{oW^_9 aiq0s.3WgtND01S>h_%͟PjJ5pL,[B @R&&=`cv.EܤeJ! -ma5܂d8fN9|!v5iJVR,m"=;<$i`ƍUi7uPOt3n:pX·I[z%K7nη;p]R*nO1gp\n/tU Zal+eǀfDnV+\Tpkbz,[O%lC2"X&rn%߃z#@M.2x!;)p/@ ERe$jis*Z̕ @ )tUeT:֨j TBճ:Xd*jI# K`F52VVJ"aF,:bE(7\lBxXck5IifCXgu%XJJJ,L+MN7Hu:>-cȺ9Q松뛚/u)XlusNn^:ΕU@D"d UmhںuaT-)a 9ES,jm)-}V9\e;VidiLʼnRųezS k$X Etl.SUx>փ>6!h(E;wcF`rGYd1o;pm5EPc*Dc Qն߀lԦ0jf Dj \#LԹ)-N)yUEtM[mU85<&؟]^JmN.͓ضKsԱԟ׎]޼ks^ |]yd<1#b2sLpG!@0A Y)l nX14sҶ`e+QӼ*#$aW5u`fbp־ϼ 'EcCx:|%R\N.sI̜v\\TB* {֫^lk":I!KC939Lk.Ƥ8OS4V][ MVpQʌsDY̪QfqpMW*5&a5RWSjR!"po;Zھ Z9DJpbB^WHjruw{tBኺ`RBJnپ};*8"LBCQ_^=@4@xx|hq9 lfX޽v:3o!8yʀ[eJ[R(X&25Qf@(xtz Ŵ\AYaȍ-ޞl2t0-l?>JJi0MU^!I *X0yj;P$0%Xnj?hsz =-$|_As ~ q,pwɆ^k|h4;'[@@03TP3rg ],D/nE8}_g<|vKI2IeXo+:_úQ(xdTPLz!_*0TD:~ߒ^FK\ ɤy9])762:9[UzUM(pC) *Y |Y6/."S.JVn6gP(klZu$1 MJN];m d+DX 7jީmms}[oAMtu` -@p/{jsEi)l)gh1TQy/Ԣ$ ϊGHz󲷆z{=_*@>u_[\L-1ɞR=31=2ZGGx$F).c(TDEh3iv)D Kw轒K[ 躴 +K }GKs~YZ~xPQُGGfYA <ތ[nrS:u*,CD3P;`t:LZ(DP:*/wphtaؕc{R^omvtP%MNmh0 B}@(̹6n݉W8&l^Ѕ| O[aH!WbR=i2\ӻVr.N|,;rw@ 4m)(N;iY?udG;v˘5TX dY$r\h1RA*P̦A*|4i!iH Baf{G/beMkCcy]o$Y  F(H "H, @(Q4:hr*X"6O& XWH(^J\`0_zύ J 4+R$f-DIjk"yV!*|6Cm@@4 qA@?௵$3QN0d¦[/;+X]$мjeyM=rBƇD 7E]VuzY=<{ ̌3nz1+Ws4 j:&mAձއ|U.Pcz,&HJvDL W&}i JEDDC>s6^,@G{vYYohNc K$!%Y$ӯ3cc{w=VĜAxW`Aw&'fhhL]W>I ;]JB./L-̸,wrL6m~}y6{"1^";d_P*C8Pm 6WOc"M8NC7-hu?$xU> kmm4(Ѡ2AJ(P/lR (5@ (h"Lku`Հ;LSÔpq>$`Bh0A`#A d'zi6dA=L ڏDF=@@d )JT  4hd h ѡ22aidh@ShBTM @44Pɠ @@4hbh44 US#z&A 2= 2a0dhi#@1 1 #M0 M 221C?J*Bi&Sę4ddjzm#M51Sj Ѡ 4hC "Bhb)OJ~MIhzM4 M4 C@4 hh 44է"DW\Cw%ܽYi?ɺ8jj8Ё 5Ttɉ3%`rfKL Nɞ]-*GSMj>7Χ n{ǚ@5b.hX?zK'Zq8Ї]OCa`v?oM {jb4t6}6^T9PQFy$mzY{<~߼s}*}ovG~_h:G߾߽O?|K"J7/s^; {Lq]lzl^8t[5mGؾk%Q{YBX*MERIj'bR/Wl",H1$a!RVф )#'B)}LUU`LWbh)/(zfĉME^L'&&4Oc 9N[& d2jhMlLɣI;1&ܓL'9y' rz9Spa<;ytO.IӢi؟m$ӂl]p. ݥ>$4ИŲiLpOblrmNN]Bv'f|&4N&ܚ&4M;N=La8}DrrO]-$E. |hDco6NzDSiܝMI4&6L&0)iiy'dؽ ҝM46n= B{0Oʚ'/NrM;'g.䜰ڜ$J{8'$ǒv{6ǒw'gtLSd{ɢr'Np/*pO;N&;D9i&jtpM&&9&'*c a<drĘ=a7)L}ה2rN= liSĚSc)m? '/btNOgD>r{ɶ=ҜѲy'I=i4ڝ)c ${0͓|clcb-14ƉI<&ޤ$䝚SN_d<4Na9'jha4NO6'DjhӒiӒm!*o0 39;v={9{YafUd"I"&[Q!Yȇ&blUiqFaq%!**%Ff)a$hB+IJEVW+IiVlZX"J"AbbrBlUD"ZTXJBRʄUlj"Q$D "YVEpJaaFi %%rY$jrAbĉDj" E!QWHeDRDVR%"G EZZq["ZVqV J JF&jQ"B\XRiq!FED**YIAJE&ŌaRED342_c3u>5 F@w/3 G3sIA,qFEqj*k"hrG q"lQ\I@''s|_{zn׏ן #ȹ$kgLaB, b~X &?~mft?0IU4UŎFc  DA\( 5ñٌ[/Q^_<η[+nO&g~@ D!,^!+ GdE@: eVJV;Ny>[.C~ 6Ώ*4YA@ ;\P 6D@]gn j/}~E$˜1LT0)b)1(01L)0))1L ˜)0˜ 1LS˜)1)b )aLS1LS1LSA1LSLS,S,)bS˜*XS b)0b)1L)a)0 ˜0)Sb) ))K˜S,AU bbL)1LS˜)baLS0S˜ aL 0)1LS˜"SS,S0bS1LS…aLSV¬SbS)bbA`S1LS aL aLAb1LS bb 1RLS1L"0)1L)b aL)bdS bbS1LJ1LA0S1LS1L)`)1LAaLS10S1LJ0)aLS˜) 0)b 1L)aL)bK1LS b S˜aL)0)0)00*XS b bQ1L)1LAaLS1L)b1LS`)aLS aL)1U`};wc 0|~o?ij\:.ٸnN84 B݃kKoG'  > A҉`e PIM ,>Q4&DLgjPi;5_{O7 uz&}ݬ6Ns:V}.mFkJNkBz$mHQֈD4Lh[0 .^MJT'*X)3X-WP!hBxk:#O;9:q ˬ3$q7n;5nc^[47*=BrçKb@wy;5ɬi{Wu&yOu`%Vm[CV#M @+!֝$%jt+hdډ- OHH5Ji@UW25&4%5 * J_n9*y6A:G""-Փ"!dcB@yDD`t^+v5u|QjjOHH!5 UBTގ~9>=8,iϡL*gfpDDDa9pݢD@]V<SBDgezZ}?ѷ|7۾뾺a%ZI~_]V EWJ᥏ )Zͧ01gdye D8BeTa* ZP BA!q]{fD}-ӿ\&S~`gLD4t 3 pXx榿Ftr43 0) 0,V? DD |_~]['Nm<"mRDols|!UW!2X_fr\^fTϪ">k!K<5!f j >T,<:q :bʢCh\pTVVNj!g'! gҎ ∴Oڻ>#1|1Qjeuc~=Zgp븝ekZ ~dQ5(Ɏs[ALb[ .6aJ Re|VqbA{\ezx 4J@Fa ďc"sPbdq1:'{y Hm .9D C 9C՟˾Ov pw&Y۠gv vƌv`p#0H  WnlVl*jM}g)Y0}}}ESQAEWNY=<,ȉfLS2@txx*bQb `JbҚS?Adv5MY5F]22kѮ1Ll@dHMy b4~/ǻO̜߻g2t}?FCjpSyx-Kt'djj9uMr5%ܹbJ-.!0Pk:Jn0UX,gJ!0[[7'QMQe$ :J* xb;1݀S1؛&қ&0tM6M'JmMÖ0N&v&:r&1Ra6Lv&ɍD5M~3=5W02P&ϲphAH"}1Oҧަ1L)0D0A{y}_'Oww2Co oRm( C|p ܜMTRa31}tb:PN"a8F@ ^@uCi +U&*f}#!4==5RN4؇'H6+~?̫)ꋙ589߬K~: w&/u<9w!wݍN={4:&0J(ۗ!#h ,%/ }LwVːd iObܟ/Bv([9qjdޒkOaiv5_7 GӉ57,Ȩ2~QiW`../$m)2_s"=G!g枯qZ`y#!?F.!'ǓHŮhc8AgZecțXٓ< qB\l鏵yb_Gp㾫n?xm[$6]h5ak9%TCG d_7",nRCY5ߥ 3Wg|I'_BŪU8끅Ar|bjA?0jN' dyeΖC%*>CtЄ'*猂N`K2Bd#td>!rem?n:˻\rpu&KHW6ڜ\*VT_ R tٱ 8`wSm?(WXq fՆ˗ mJdpa}{a] `~8B!'f|tarƹqjGNN<FKS4v5ӷå\4<0sJbVa5jY*ȓ8FG6c2{5'2}?r}<;Xdq!`C ²-@aOGnoEczWhfԍaKݠ=1f+ b&2;ٔY^} K xMd{=ЄaF¸S2D-6LYe짳f-wSNb?wa:g[K sRhkR89lak;#u=p~.ĝк!$2;l$*[RW98ć)S5HKY)H_eY3i`[Ӯ4MCqw;nYjΕjG8"y}+i|AmǸ5HקF dذAٜ&1ѐY|1ܨ;ń*mo[՛zڑ(n-vιYrrcPʩ OBzGHkh9ڮoI|bxyd}ʒ7F)J#fOP B(oWߊ9mJnweQH|9|ŃgƯIeWOW2./^TVuygGa]juuK }?pz۲Cۇpq.Ϝ.v]`a9AACDWǯ7&NnҔnz:~_Eۓ O3`_$p%$$cz2iqyfnn"%5Z"i~c)(2! X'` m o.Ho~%ׇ) 0Vd:]`-jq=VK_:nN<3SMv&cKnm=b)xWT |ٽP/Ư9Z:mߘ"?{H˕ʽwuȓ35Nq ],tÃw],ӐJIB~ P[NXKA)2(BS\T2D%jx"0(=T7t#@*z'hbq\ஐlэ\d3JRsɞR,X9tuWo1ćyxŭ`!QY{ӭ m˼|[X#!G.uƜ#Z' DkfAJr!Ą,Nn[ ;l4Y"ww;?0<#;̼JuN+z`pcOj;lJ~4qdә!]H;;[NTq*S} =DP!`hvעs(5 9:+:SqܺDŽx%)}ybLܶ?6zWYMAzbӷrq>Dpg 0BE"զ Sn絯:Sm])Z.'`Dd FZFF\/Uq,6y Y$25j{KPخ)QEvٳ*ݮ4cjco܆1(Z:W^nۑln.`-B9ޚ]ɬYŜNmYIgo! n?.%*Jbdž[1\*oyH92z|sQ|ȗdeU EI,+Mڦe˱}EKu^2V =,[p+ȧך}OxUxZ!7ŝXݗn,8a%- 4 l6ۖWK8c7'C]TcSӁ~b7tuõ DڹLF5%iTRkLGŝ]ۺRcSԿ-Yp{vW7 ժOOszeͺ1B>p2r[&M9pѕFAKktRMikkyb)t*[ž3>KpK|$o\mB-Ƈ?nq#A::9uv/&|x-Zͧ\h}(p| m{sL.:k EWvGAԧ4ͧ-l?˕zʁa\HP[;  ܛvy ݝO]׷G^hNwz2؛rcIp[U.bM>߰޺#VX,T066h^޳{% NsgN܅/RaH[@e 䁂άuGDuxfr!yt b}Z5 HC4ya2g WŮt*.yНִ+%M{8ޚoJ$-TgwFyG~jD~wҥF^[23fo5BahګjޯL(uwȂ2هLg7zʆЭE)I5 u̪'Z]YWdvq1hE ۖ+~Ks]1yJ)ޮhC6sq0,nzF)CS8Œq ԒHjuʯʊ)$e_S: eE!x{jSΩUś.u I.sn@:!*P7h׻lԀ:'U(ə*[dvD_=˵Lw@҂>\T{y-+ё3#*JiXru!GZGI)]5r aZ@Cҷ-PqYK thnR-k2IIl[nxgyQuI_( ]/u4w${hk;ԬUkSo>wXLv ~-# طT^x%MÒmܨ}"xuGc]PM{'ן$wO%L>ϔL筥-L.㶾YG[ >dHZ,qE5Ĥ\$HOGeg1S9HӐq :2:/bҔQ8t2.o8ou8ڷGO,┙)|KZGfyG9_8ثuB gpzRW]A}&>'8r٧Y;AR.z$`7Nwʦ ,4:( t&A͌ǩ;{Wvuڝ\<[Nfb-W3c|rD߭r?%Px(f^ɩ,)rܵ_WEwH#g92N,A#s S ԍn=\F.L]jqlq:mCnXK{.ŔwR|Xd+JG/U%OyqWrxO7`٧Q OjUwL W]U 0+v{ﵦC>\_:%Ct|R5l:QB\}B :7L^JvQqH-8^g.xzy7 `$)uc;JntG+7WsN&htN硷yY`\Lg53`39,X)`]R'B;^kj.Y2+#n}~#3tM 5F31x?qiAq/!o?ܝF}~Un:FN.#U/ }|+~Q|ABᘐLwlH/󖗝tar#G$BķMY267B@J vMWJ_vTj_=>}im!o [ ya$˖Q#-pt1CNo#/i̟p` ׶@z)D-x TU_М3{C PMpα2Y&q̦]U_Z3\ojpv/ PH4_nG?NWNyޙo*9J3x[pל`9YXqC ƖM4H|ƝJX{ >n:]7ax | 5[xvf& [;I:z!TgRWgt܀b`==Wn7:| 6h"qs0XgIn.Q/"Q';8Y#zd=0S$PFG_:<0~IW= jvqgIBNtrѼn{w"gFDZ#(tLIdM;j@U?uްe 3_8V r#%mA3|nƪDI\/8|{t.>]%kZ壬ؽ;>C3F4.Vŕ4w$/M]!]9"s6h&&𐣰W#*=d;M(fk(2uq)G0qa˘Y 1`w WЄRu}p#UJ:PL϶f*jRuI)bϻߟx}aI旖7ZQ/P|pswoNr:YNIdwQcm8b(_9K cB";˶gu[rC%IZD ! OLDEdֆ]aLy@:{[qt'B.i9sU*NR"KKED>>Oީ)_S2™)u?ڦ2اTMҝڟJl)5)SE4S50>N1NmN?JmNvS p)*xSJq9܃šSjmM >)覊qM kS*}bMJdf=LaMSTަr){ ~O"U:we:v2ؤ)+AULщa0LSJiMSHM))Of)6SM)OU6%3)OjaNMJ`AʜSJ~ zSʟe;yS;4jxS$SM vš֦ oSE56LT^>;~tvGSvxs?C1@O*n#=^iqOJyxSS z*dwT6Mš)0SSZ`))MJaMaOSjpS)M)NmLSJp8ž<*d3S8TS53SRSSb>E;=S)=zS=b51Oئ"h}{)SMtS)OE;8M|징1NySOe;iOSO*zjg)NE4UlS52g' { Ggİ$^Eq%"|}?ܦ 4S%SËc=IW Cm˛vZ0[ĹZ[bޘ 1XT˜wLOQL))=6)){-0g/E//2kʨ*JbJ XmSS)쩒RL˜߅ _s9x}*08sjktajdz0EP)S)jf"됎s3TaWx_:7ոA~;~]O)aϩ)SC53TyjvTF" &Jbp1&'DT/#C E3 S:41g:6|Bj"" P!1MnSPUL {*|8T@0D%"_k_GOGIySݿ?T;9"շgsVY!^re;*vL)SSEPS"0)T™STOE;)M<)9S1Np{N ҜGM)L)b)iN%4S%;oS%=Tަe=Ȧ)ҟ66()ʘeԧSS%0SZSH-s)Sb%0O|bOȧZSLP0=U0S@꩒S)Oe;NSN6SE5E5ܦ4NvS1LS=~e9TaO!L)L)̧ t)ħ2NU8TS")Sr 5:*jS"ܧ*jl1/W;Mݯ=ח{%Wlc'yōy\9cw?iOE1JE;)?=ژOE9SLڝM)ҚSgH2.~*b))1OE:SNT)7U)N<{BĦ7S fANr̦S*\7- QSJrNRcxS)S)SOM)қSONzS:q)NR r) A0@D gߢ/GOIWˏVqSR{kgSe?*SDq>)4Sb^ؗ0]d76 "D2!==%;wS5;l)<;wN; y7ө)ܧ*N:9W.>yL rENE=4S4}bȦ9h)M7))OjTTS6SžTS)O)NTJtا2Sh0|M<^.89>]t;/{:R]HՒ&0 @s/h +O)XQc@s@ Pgq>Teݖ$24^/gqjo5Y3G55spx+i47U~WqW}8umw3'כ^GMՊX,pN39,>_uoe10x~ 䗭OWcv:oPy!Aٓ::@1OSia0\\)OSjAaNb%5SfeO)MuAMJ3Sz*zt)𧲟E=TL)0SSMN=Osʘާ pS}>=E4Ji(?;Sy))0r%3SNAO )dDK:*q)ʦ:7) >*vSjvSS)SܦSԦ0*b2SN\*r)L}t9&M0_%?|S;iĜ$`tSSJ}p)S4*b)S4> mOȦ*a4")Tģba1U1OL'*cm _S$`驛L)S6壱0SNM)S)ҚS))̧ENq)MtNpNXrN9TOE:S:S4te6e4 |]n؞1ÂpO^XO&6ҚDE1OU<=ma2U8Sb)SNr;+ʘ*|ܓ )AܧN |_bBrƗim0~e9dS 9)uTš)NrdN%0SN))M`U8̧MMOOr) ;)))rSSOOiL<|T지}/?;&1Jt$S8w&S'4 &觅1LSj}|)LS)ҘS9SJhܧاM~wFOLSSŸ"N"{ iؘ'D[Dj9' L&1NxOSJziNrަySOMSܧ*h&9rN5:jl)ƦvSڜ=ž>=ܦ)a1r));SJywc}ת aL vJ~%8S> ltM-iN9SNTNҙ֧)MaM9Se6*wSʚSЧ vS4*vSSuS `" A{~M^ݏ統''Lpwlޯ4χՇs-]d)A]elv~,~ Dhe=|bcD'g4O$8tM)~Ÿ51Oe:Sǂh&OE<)QTnNmMjrNI;=bSH>*{)yS1M)ʝ)Ҙ)bS4Li1NOB}M)14OO#& cwS˜>~P>=T1LS)M1)MSO*\u>U0)ܦML&' SSS a>H4J|a&MSH?)1NwS {ߨhOjz)&%;'brNq9vpM8&SD])r|O pN &؜bx'. Қ'vۖ')4t)L}hjlSH1O)SLSbbE;%L&žz)SO*hڧNbb=);)S)6Oc ħSO'6J}{)Y>3⦔ޕw'2cM9)ʞ|TaiiĜ)cgf4NIv&8px'09'fBhۂiݍ9iNI6MdmSM)4 0W 'gNmMDLSB]ɲl'v͓<8cJcNrç:'u= hw&ܜDdLlh','D;t*]t)݌&)LSjvSjwS<)Nv)ӻm0&606OU1LS=}b;)˜tSS)wS<)NT)ĝS'g$a9&9&10X1LSJiM)piMra1)S꧹Oe6JtS a4NS"ԧ :lS lAN^jϫ;z*zJsUuLҞL'OmM,: 0 o[mkZD0|ovPƷI,!rcY(:@(."aqip0Gԧxxఒ"&ԝeE6RuFK>=e^FBI89]E$[grw4s]xRͼnlfwcFyZ~*bMXIpLh͚S Q bOlOMƉ 4lIJԃzSE4SHjq){r̘nS5=e;OTЧ/ . |8AOLSjS=4Ɖe:Sৣ$œ0;d%0E6|T򧲞hSS "B%8)ʥ4OS =YMNpa0MkSSQLANU=z)Sʞy|O1S by)tL&цS>JySĘަrBz)ŸE>e=)1Oe4܃>J|bG9Se1Ozҡ mM;I4 }r}TSS9SǢL&4Mv'S:SL&={}b}Oa46MPiOzbҟߞ1Iܞ '/4M&rӇ.ͩWS4DiM)L觪r C1e͹<Tp 8 it5/A2)S"uJvL,NšS6NM)I SQTZr~r'w= ~&y8ַJC*7"pE"J& vɄmScM6MDi1&Қ64*ҘM0Lb& cMBa0L&a4&ɀcmVئښSjmM6SM~߿^_YqD0&"aLbLSz-1LM$1F+LiM1L&4i004Mѣ  ca10 & 14Li iLhSIJ~M˜)bSJh6)hDcJia1%)F)bLa6,SjiL)41MқSo{0&6S4)œ)iMg$cN))rmM9S?S p&Sj~NTNTO*laNzN rnS2SZOEOUMJkTUSBAlbA44AiM)SфmOE=.r1ژJb )rLSxiÁp0ҘLSM0)'4Ӓhja6MJc N IM :SJvSLSwS r0S)9SJhL ˜)iM))ȧ93)SrSJb1ҟ=0*x0)Ҧ0O xSL& N44¬&%1Rb)1JLDmLibMM*LUhiǩ1LSKS r0S6%1NTঊb S1MmM)9SpS pSmaZ&.bLSpM )6a1)ʟ)0)pЧRiL)QNTئŌ&)i&HҘ& )0*ɋ4M4Ua&1|ҘS0)M)9SJp)˜t4SJiNœ4iLSJmM)):QܘLc i;)o8p6Dr䜓Nɴ&$l N m¬&)Ę ',l[ iMa0A˜UbҘSH4'e1Ҝz) ʞ4L,>?+&*\)4Sڜœ r' mNb)Sr0ަJwvSE0u4S68)NM))tt; bb()hϳ3޵z>Oe0T&)1L)˜)&ݘ؝)ma4i@ `HCay#d߮.55=iCvH~x1' l(& @iJh& i4 VM?i0baL&lLr)˜œŸ*aNTLSJr 1rl0r pSjy: &6)4P NжSah p)š&bSml&64M`)Jce4jbiLST)2))<6Mj~ʡ 6)ROE8SebLSSmLb).OQL JjS$c6iMd6LSЛlSDc 0M M)46-Bhmhc JmX0SJmLSjbқ0BDSښA1i)bbS8S*h) ~ mNXab&NJb1u0S&iiLSH8Sj`a4L&ؘM0LiOwW1O iLAAȦ?;wJiOe6-0OO[WLc 6M9iӤS9SJiLSS=NkalSJw}c*}TSSN{)S=iFSS}6)œ)ŸU6{hj|XDNU8)N tSj`iOrM))SSma?Q~Κ'䝜К'INIrOR{)mDӻ; ;SROr)ʟB*bvSPbO̧=ORcDʞvRJaL)))FģDcjmRҟ?2;a9cJh1V)8~ bM)!O =N8 fAOJxS> aN6A0E>6)Ortާ"h8Pt꩒J|iL r }TLSOahLbwS󩲝SjiLSঔS<9r9`MS4=~*}mNTOOOO>iM):'$M4Q'Lh??PSNTN?cTڟ:Sj~??L&1bIa1OLSSjhkSЦjh)8)bڜ)ySjl4SJMZSbML)M zͩ+zi9r}*ϿJ;xrڵLβl\ji#k*i'2r/ 51e-!&4RF-i4M(T}W9p.F2^LE'c:iOΦ畧w3+րGH>wxei>cX [nO'&&zőr g UsabFR)Ze=JTd(lzqܑVj.[w?h'R~/s|>R=JfB'{axE?lbj5JTT)y{뇫O%1O)(BPD=~ܹ=ı%Fu9&Et&d0L)LiIĘӆD1LSN4؜4NXi&c&&*I6BbXҚ6ib& )bS0) clD4-,&& 6NI8r᠚&X卶L&L' U )rқ1L&b&hLS *9iULA9SJS fd{JlSbL' p&pSJ}T?wS~SrM; a'"M0N?)ҞšSJiS zҚ )қSM )›S=bOSM{)bL&/I Jl Ҙ!8ia1'50vžSR dS%9Β{=' :&`r:اb Ц)1LS rTiRRT)Ppa0}N9SD=ﵢh&œ8SOʧ)0rژ iNmb&1= hS)ħ qM04SbbSJ{)JtS }TҜM0M14Sꦂ)4Sh8S>%?<)=;)SܧDژ䟲BJd lS {| pS a4A6)p)AO=LSܦ)TJ}TҩSJiLSOOڜ)SibmbL"ĚSLSJb J>0S6*[SD1Lcܦژm(?SB)b)Sja| l0m"ҥʜbXM ))SjmLS6ž?2O6›ڧML*=0SSݮX:DD6L٪{1&1Ҝ))ShE:STSku9'YFfgD6Lr&Irܜ9xrNI4O$tO/'q=;6NҜ NZxvwr:x.N^ |9*So-mjRa=41mDZ0Z'iMLm'tn 0ےbN L8&~8&4D0&ld&OR{)S1'/a9|Z'baN0Mn]Ҟ|X'|Ta<h݂a06jz&Ʌ6c } prmz4N 'Dҝ0rɱݎ[a;L^)f1NScM&i1<*rLb -'pLDኯ1M1L1L)8S> }{)Ӧ0&*^b;1O)SOU1MMNXMD61LpL'Jp*vSE9SjiL*SܦrN{>;Ԛ'$orO&NBd =|?mֿGG3tTc`AcI1h&D4E6MLS ~6SJmN bSJl1Oߩ2)O1Me<ڧL)*hMxSSM)ħMe8){)U1ƘJcpSE6)he8ra=Tҟ:)1Mʞ{pM)SjhLSJpmM›SJabL'$a44)1SSJm)5(Bt:&SbS0ڥ4Mš ֧ )LS lS:OOǖVZ6n7[h00iM6M&&La=Q:lp.\DI:cDv&0a0f6c:ctv!:&IcD;1ؚl)l0EZbl&lӁ'L*]ENNTڛSژ˜M)M6S6қSM):S4e:ta1LSnU`K{vr&)pN*a9f8&HқS0Lr)MMS8SnbS0˜u6žܦ-ah)LSN¥ %SJhJaM vSE:S9)ʘJrb)MڜSJtE1N0*mM*iL' y)ASJru6 )˜JpL' aLS46t!×u<{)bh܍;mp^#wT&iRLSjaM)0_ ذM)ih&ɥ4ƛ&JiJhχ>oJxSšS˜)X)1ښSJb)1Mu?y̦)hҜ))Szœ❅3)5S F1L)0)0)A )9S6SҘ)SObhS|ThSЦ1LSܧ%7)橅4Suܦ7SSRzšMT)AʦMy*s)SE0+Sަ)N?iL{)OSSJca14Lc=?BSOz jS lS"=O>W߾_|{}]HgltۥQ4*b?%H"`}اiMa'N;L2&L0ѢpVbA`SJ~ƜM)S*bpNx?eNTJiM8r0ILSTҝM}}26;8j8SM h))AL @YSGp+3I ưƖ,+ 9|\“ȂccVP@"g=X|ƿ&e/%L)91 !rnE)Y&ˎQFG 3xO ffT}V{Fh`A"bcڛr@40j?R<Z4otH͌ Zph%܄ FhSwN3 \8U?¾@|'fbEP )L)L%XÛ髒 x}k`3@W[o!jq$J$od'Z I ];L;F4N̵$@0)HB`n*:@G8ƳrҶY"Ia+Yth`r͉Ύg0K,_3Ϙ{~n_wv=9ߓw7'8w)w"qqäOFe A b#^u~݌#uq 637?ٵvњbnYѢh6@c 4WHW91PלfЩOT]TG9*"61sQ8go69 z-̺v,E;Zw5/ %n(bAD HYV 1͵ijnT88d jHK6V|Lg ,,bt}R.KU!sWgZ 4LRHA=3Th"D@{W_Ï/7 iޑ"ݺ-C_4iuxއLUW@l>Lww{"t$U'.6m|yWtQMQxa4gOiP$%]Ir:B'8"&rQ&{Viκc9}@o;zޟ˼Kfs]N ZZt뮺а[{KywBuTFkzhMj}8MRXf eW)AA:λ$gR[O)5eBּb6$ fD@B޺D~~IfS)6ԁKL]ҙ}[Um5ҩ4` RpM: whp)݀t4]#Bzj}a5UԷZ's:=3wTfwBLu;{!Oj9IHBOJ:3fIK%oOҵuɏv<? eNsң3ψݓ"2Y`=^i=>C5 ۊwh2TC a͟: 94sk4M{lB10f"{%w5|CGi :Sѵ< #4dfh#U\y?|n F}{%lKM~iq <!v8^1䚖δաM9i-2`n631G𞎊xLJ.Ҹi5ַK8@Ia jey,"3ԂVVRm#Ҫ;9ܺ kD=nύ.K!@옉rSCzWԻC乩 UD4^\ĖR0R7UEFDp~@.]LLLao:Ky[!zq߫gvqW#S5=LmSsWgZyۮF/\hԍHѮo\bG=v V&Kg>?Y0ͯp9d~\rv578ƨSSEPD1PUP 3ps0\p3L̬ə,e3 ((v>'==I&'b VS_]ۺjcϪ*c8ŴQwf@Jc1yH `͊82T(P͈2ܞv8]Yscw|d0aTXZ%԰KẺ9MYE'[mIϕ!V@ӭMy?0""/{unr{ti~yn!X zjUHzll=?(`#؜Xww#rng0e@ab!CS^]*cfrXq13p=Pt)A  " Ix^ԡS^o:qbwͿ5|&${]R/#<+ɞL&W9sm91L3B( Umt5ߓ!jnf1i %E+4Lo ~)$s@O=2F<:;:V0DLC Lz!9lſǓý7,0w/'2AÞӇ,+$7=m{_k8$3 p g|ƴiYf ZTZ{WS3tZEjޠ54*܋5WiR2&كNnqN?vMbRQ:뗡1W3b>kYK% m[[LtR:L~nu<$c4Iqhbf%YPj(Յc"*//2T:$&P_fPg>99Tˡz߬3jr Yrbccz"s=}YaW-skR,8)͓,SZlS4CTSn'9 sd1PW6R3 L&{di"xBM"j$.ʳz~fjϓٴ~M6Fcv\cvzykٹ;6gX.]kF\$4cr1賵LRgMJ&M=!K ?SyE<*irV#:ydtOQwshgZr t9GJ i%v:J(DaZi i5+Lrmq߭521[T5)yVj@)$"U8FcU+>(3V*.H1$ZYֲLDsD+dV̝~GaVlq 6lb5GS-XLD@^$hs@fP]>m9JIbZ VJbeI:"S@.!LD3nQe=geVo{%m+f"\⩋"x9a``5NJi)t 44 f/t>+3-\ߝ  QP5+яiTDF4S0u؂q(k8nuJH/O'"rpstZum밸}d zMkuynu*υ E`3.A,*k5XolẆc3Cgŷe'2-/X! } 2sl~ O6-nXRϕU6뎓XuYw"DmuW17 FT'44#5E do*FTړm'J 5\0 9`V5* H`qɧ&*1QJ4Bȝ$nd4y1DEX¤fDTj|aL0))?L Ʋ'd?O֓rj Bv'"vSv'Dғ4U˱< ܜ1':Dd8&=T p1G\=\;1i^+˜iL)OE4 a1Mz1U5) ܧN?Sʟz*ci)MKŸ}Sy:S0&ɢli6)' a=SLl;4aVmؘLLdSJa0Ŧ0 )4 c02e9SOݓ^LjfSV2a'DI<؜9rӁOzSҥSҚSjsا27MjsxSON&)SM;O= ܞrNǼRw'lpM0'Ț*Mi'gv/,%Ҙʚ&li?S䧂y'rx&Lz =MtӧNT"OBivSS⧅9'dMs'W~0Vq5g'#Bj9_c>L{l2e8+ɔew 3fb@Dd=jFa&48F!Ĭ`3 <_'n}~kAn;l\+E#V-[hȚROo D2bz(ݓwKJ\$hcǎ((4q}){ rc.̓52ڑ-8{5-nM@׫a5YLrɛ1qE}k[0YFB|AP8fu+8<۝nc&rqiW+ !֞Gշ~Μ~UqpTpG \s1fVeY1fYfUYfU3,fW;v~Zf(wlvRwLGW_$?-A0ha"("h=7l)4c.ly;GG!9BI܏eSTy Hf*\3#F4L~.97ghFj밹L05d/2zR2j508An"P]ΈtK2NYB+12)bbER==qvyXfs:*,yؠ!f`,[GA[hs,lR EmEF*;w""z97oPj;Gwr3ݧY1& b0`ϣ;FokŢjKC AX5α]7#)N1zCf D "0%1$Jj2azX,1O\KkVJSFnQ|L71csCocm2_G :ئzݱ2eg͵?Wƞw\קי27gSJxM#WYN)uxtRbddGH#؈DCU@rJX|M}7HJ ĭ):K4l$Dm|yS7/Y8)xeៜrve|w@ P75lv yTlEt4FPchRii7\2`liq)|8EbX#ҲFRci DF̌F>ᣄN*HmHhQ%(QA`$H @z;>?rln\za~q.C͘T;MnoP ?\ !ᔴ&>|W;d~ L;Kh=: jmޭ[SB<CN}[0&00&ExY^Sv7zٶȥ"r{S 5& j^2&iGC<ݧZtQRꯎ1jhLdr eilWDf3$d(SpWm$(WҐ$1X 5H1Xi9ڥ8'<جbdХA TG>Q,ȁ"",y2jbu =}[Rۏ_$Qy|dpZa ;đw8/tİcB~/Ҙwf8-$Z gWQXNjq~YӇWou-ߔe ن[Ƽ&+)!'ަFiy(]5:iUC\!y,bd*6rD5Gߞݵ-61+:SQ@Bf9ѝ._cVƉ<JJ0lRa" _f?_W/tI1t`xN@vs\1íYȊ I S?}]qjG!Ż{VGP KjF^8@o5m;s^e#$&sp3LI z7;lVFqdA㺶QL-F^ƞek]IBhxʡ=[4P?[Gv~|@`333#?41}$ޝ|I 1t%8/;C8^QËiAڀsaS@sRD8"&h 0{Kvfc <^Zc٭TAq;ۋ)rEq >wθq=00t.W%7k}vhmzĆ;"ޚRE;Ө5N#ı E t3.`ʎmy=%uRa+ TV`XD@ !׆cNz /&#TzTkzBu)=ov6Lֻ0~]w"ׄ!Ha4Qxg)/`ǺLPhy6$@ՠjg]جէ+sm`KԺ.31*!ei1z(^6 뮊)7JZp4k_g}e;lǂ>…[A{t%5 Ʀ b}tANJTeJ (o LdMe]/w0 0aA3@{ 0a`3 10fc`azq 4BbjiMl(4LL@=@@ih@ 4hUSLFhJ0`Lɀ 0SSLL&F& ` Fi `4 ` OU*FS<A =M44CL4`CF i#`&#L44d@h44hh@hFM򒔆4OD F5EKq%S"򱤦ޣcgR_YCrHG$$!>='{]陙W뼁<ߩ}n&s`i-sr$ir"*eRhdb[,$Th[ eHkH`q1a&JȜi+)&$)"WʂYiqTZFd\XEAKjrreqF!XTI&bFG!lFdhbEEJ1QQHDajfV{OT uuDNLq,f aJ'VGAg $!%LDPX+!fq2r bZ1k!FrTErd",U,bF!fK)m5|fx3>\ȏlPt|׊SfC:XxB[$ɽo_:$ÂiB#krWeUH&,'@Hb }QV.ZRa3*:}(* 5k,!fBVm͠#Є !eoob~hmQd]؅^{4U"yŤOKzMtZ "]TTܸt5r/lAqeqY9r5)gYtݦd]ZmK yl !L aDD$,JpiRcT[hSr4qnbd >4״54_A+]d-S3DC}UuEˡE2 dI̦|+O=c1$Dc#GF6b1Y5Fx;&jg ҋMZ E)`` Ԉ3a(Ml9t3 D۶S#`,GCiPv#쁪/T*[0Sr8ܰ_{K8>M9qR޷<"]rŘ̌d7~g ϘdD@4.*"溼i"ۇEE9"bH5 D3J !qҎG#L˲<ȔiX-hgThY3VЮc3ae7v0)au<888:sc!cjH4 {kI\m5W۽4JHU-EI8#g?GEG&a*F뤃Gn rxoh OT8-,LLn tWW*$I`ج @B(͒NبSl4, S:*Jt%BPV!28g,V[!z6uFOE_ꁸtV[͟Gӵr0Uv~dVQ>9iejBPDI#@-\|IH^T;j<66ESwKY]*$dI1m\ LrGE0XV$aC06"@CII ƜNBtK0nSُNft{>T.UǓB&r'>4\|Zy @&X1)\6%|JuPY/` Yj_n ]=,q%%Gl#IxpFyQ—B0+cv9,{gsKY/uc.;/*Ɉ Oqǜeҙ( /{1di'Q\osmi 啖5P_~[z*`6pߢ火YiJ6 3]kesKrXn ~<OOG;qfÆ "܎du8`3sAoz3d:͞eFHpآov8xvۡ!2ۿ[ra #T)oN-p!)s 7;jdfvk+ѽC3,\7F )̿L2 ;na `=u yfv:^.C:Yfz۶|ѫJRd ۛ7+3:CVcteU`Y7޾֚RgP}jG)~\QI33kۋbOoif4nxłdsZLlB5Z/ IZԍ0J02G.崦k^ahKu˺#z1}n`A^Gylk|zH.qg/ \`Aˏ5ÉlcpkSGm f+N4km6Tan #vneue~й Wk-$Ld,h27j5Pr9mF[1ޞ7 i xoKe5ڒ͕ 5ZgX{/Y1́ 2|hṕٯ#4(mV'PٻkРbw擹x9:}cZb1_Tz":Bt&}+cXlh!UoQ,DݝܝM?RՅg2-Z@ySUծ֊l7+_n9i:|}vԓvk@6*XصZ6dZ&6O7|dz`-[@dW.'_D9$6@㔥;IAgEx;6(r Y]*.v?=!|ñX Qf}ݟmoRM>[ 5PiyMp/1vz.fNbT*°LR~Sƍ6nVz<9;\.D9UJ c7|q4L{X(4U==nz5.n̡EXv9pzŚqj=$Y@hfC!5KIMEA왞6&A,9lYID q{RSVqr)^`nt sF)X}҅e@y^0ǙY;:OwypY_5B_.qˎ[u>>ܯqq2zA]9Zdiq ^`9FGS}_lw+]'ݚ(ՎmqըהM&P~>Y' rNُ)]BwgGTjt;\P'gzdqűgҔF\=rŜy)db#Vi\.9Ɉs/CLj3:5դn1a6jq?lFbגɢǒnZ^3zI7ryfqlpږz#Ҳo]3a)> 9< wHWe5g-VP4B;ٮarmwuRu*hrIq[onu`[-^;$ә&h%f͹cֳ p`*W]o;INkw'0wۛӯЬʒFV{@ƞ$a' "aattlb%CTTkg̦)ToeRS\n |-Nc)",0am۾[ ãCJsu7-Zw<_s *bbt 1]ŹI-ůrY6 Y[R8tuIsx ]ES۷vVvsQ!wL|Rٲ`5ؘ;K{#S/Ai8Frpnƥ#u /Mn-h.V89i9 ]>yIđΰ_IguE|t9ؖ>M2$JuTߴ:2߁sif7Z-pV 칥i&B\ ʎƕS*Xxmыc[ZQ,q7ᒎ ]Jr/Ȗ3h2Eˋ`T(T歼FՎDYwFp587ah>}{dË/Pd}.#rźe\Dmֈ\Yj߅#8!E; c"2ݠYS }nLmh[XVe W'^; w@S)`ek?1r.B]2\GFcxnf3cLo"}6qrPڣnRHaD[FV'=zvfkGVh\;-Sxc+ݐ7#V;#ݘPFQ1L#cϘ\Uۜ#˶:ͷ%!y W gz@wok(/,RFcY7ajjmJQ-V1wz'>!֟'=ň>! .K=s/7̬d(漆{YFn{A2KD㦦 %,Z@9BxEY$ʤjNbA9zp#m7̅ &5f\MA\Tl^5:oiVҗvD۞5kV=8e;VfYtj6{' R X_ْ'N1se)кձ_$7$tئ6YJJ:V$J[vҞ%'PKws'buݔHF`NO v{Gw5S4$ (ps FOCΡ:TǾcdJy۳è4;NrBїnt."Mqceg sg9(mNnLvb(422t\jeɮS_Q_eD5Zei혥ܿr:bT\CU|$dRrVt|zV-эUp*L8 ."7:%o1υbLag.wJL[‡u(zw#EKcz=(c6sXaȊ:Kѡ kG2k99^혔Њ4o͎pB;kfEړ`X 6#EnR~lEm&F U#9 C {y-旰$syݪXceNX}'ր&QҔS8u1-T8I􎆙-=D3̍ET\ֶt\$Jٙ2om.(3:r,5})jGdaio-ن(Y<£8rLC䜮{i1ݣt 7g9s4".'Oξ:FS6VBm^)[R1+2RGN +םOIsƳ&۶ 2zB85y[/5)jΝ|YÀ.rg;L# ꘦=ۑ/Xݙ J0LюYm ÷E܆u`X؇7;2e%,/nE 񠯾.kM2`|yKM{5N>~ĸs8\O}|Mah[]Q/.ᷠ ^I2o! ܔۦom1Ll"=Kb.3Fڵ[TAS:})qsmE)7m4 1&,غ*v8se}"1Hҹ.g&;d[)0lL8$# RxcXyIٺ"A4SV+?52r $R}%YfM_GiίE.)7g{ˋB^ٱFSL&f3 zsGͤ@/_Q6:Yua:vxK^;LY.7 :z{[JdchŘ Nbhbc*\֩ZuZ4Zє(\n3fSb>)VeYå(6[gFPf ;Hlx$ /ó\o}:oAy{0s9OX냦W%ou`|=ܒ,%#/䲥;y:Ұ]4MJJl(mI,Pu p33ɨݶy&quYEfD%QuG#t$?q]R:4FƲ9".pc )WElj!7Hi2Q Xl2J%RhY-b?sW{vqe|1G~nԝ4jWu> װ21s ؓ!K~={ix<0p s0RId=ËIKPB1zYƚ9-:`ؤr@rjB+T5fE.c[&z !9-M92F8ZaRm@\ZP2Z EmlU |bBVjJZw@@fw3DJ-*(*:i6㦲;Nm;)5mUdkgnh;+d"Ƙ)\"2ʄ$ ,C2$[SDf(q(k @o1Zr:]hီ(C@%gX_bDe*pbz M(L ];:<|~>N谪ԥ+^TbBG:'2>3mξ$!AHGA#0D΍}Smgg`^+WkVl JPDXFYxIJҧ8mkV5qi{n ~LffO߻ A!CS|\%'FLPkwdRˑp|{:ٷ5lXYvҙ/5P 058` 72 B fYUvQ+V:@]c~ đ_Gsl\g'X77gwZ6(Mw]--Tf {]itHpO`BUuJ#f(yxA q$Ecr2H-LdܦN\hQkBƒ j׻`_D.uE]Oyn{:qϧAW92>poD;|\1YwuW[Ri8%rzw^o<R+. qK:q먌oReRS`ؚK @o[5糗k!r.nHA&I |^cБ_1WUKonQMeLO)wqbJ5 Zfr뜆0~K,}{BRI> iA9.'jIdV&Ոftɔ}f,vz$[w/gڿŻvV$ۺ2%,Xb}bBrQ|.(.N2=tQxK]ezoХmj łFHV2I!3 ʛn7[ϒ7)TBuE=z=n(މ'^eπgx8gv( eI²vݗelXwiHkv.iWl{ԣ 2A(ePAeU G4鬮 Ei@Q NKnIYw;7lQ& ŔN7Ry5b˱<?{|M}~ƮY[И/z@9 ~zW5yҍL! zb'÷x?ݖ U'ޚVPAf5m W$ e*R>zzUz@^I≑2"z[:2’KS"FRըfҙ6Xbdn Mcױ^cEvRRĹ})JsF39w|&"o.5sMDQAQ/涮Rrh5@=yǃ DT1K\Nt̲'ަ8[㉯M64Ԇs~;m'%Bx\##*Z0,$ܻ0es3 8]#q64lʖɌbHl4!J}mo|ӷ۲d, &P'Of{b~`ݼ;'4sTբM .廉de8ŭy>Sxzgdh$M4/+c!i)Nk;hty0Er6w'\Km)u%)،/ٶxe0s*I{ef5f biZL4X!ѡFy$(Q$Yix{aap=Y&&Tt"+|1; tF|;N־Zm# ^ȒJ+,7uMsmm_%,;R`av#6uy*Y}+;JWQl hP:leI X ϫ>W$aj͕ڟms.?s2Vf')k9\LՏZkc{Pew[ߺd|Q|js1p_ N1lUD64 0QeO!ch& sA? pQ Z6"I9q)q-4m3Y3MGр?Ns9d"孾nz0<\y[CJTjGfV#0ՑʧE.U3饉sWQ\)_W]4*?5ⴙ",+oO^?{vZawro&I8qs,G4&f .pdVVߴJMѕ>9Uحɻa`YS>1OWa6*Qhu)5W9ǘ9dsFtkt( 18G"5ʥhCtD+Q[p&nka f[Vn7#lأQ8a9Ϫ.LtC[-/cI+RcOֽ&)2dӘ^_fyrc|[Qzː-P6ӆlWJwŵ{1w.^.Ǖ%{(1HY$[`RV*ɪ E;1q9WIp6gb/0P>RR >@זj}uz嬫Ï41ؚbᒔN`즟3AK.PJ.2 !۸s}:H"ܔNΑpu 5kƻu'wSMk-'¸Zq)?cK$!#B L)Dnd[01[TWźiok WSl(qa&"f,X=FܧÕ@@ZlBq*Q5<^..IK:)]kJŮm$TNWM:4gr{h^).^ˑZ,Ωe#^k$5Vw]`yVDE ZW**U RiVkc8at&vk0)%ا]$B @3Ĕ wzFIcrcS͑I}i[sǰ*m-Bf0VϪ KvY*.DY~nNnV*7Tv(n,m:<84z^]!IC\y28IXTqJ6t\%tuv[Ee=P z2H9ziJ1)l1N֜cNK~^]!9X@WEGT]W158xdbz)AHJVzr1ՙbpT3e |khdBI-R31$4>k;sr}|1{^UH}RD;S|3]ã{g踸x+-T9C};w]ziuU]ϏM8$̈́"M6E\q#aDJhaz-p S2%#W F*ݳ1(frFlh5Vҭ6![A׳f=Md+cAy-l!B`+PL ]ʵv,p5ENn{5FI0ѦkҥXxG43|sby ~.hO,lM7#_NU%_G2(b&.8yo%gXzw8Cս 8cuw+z=;9d+黱ܽ{|"0bmʞ @3o_,wf{uy|nn~ _}" D DqEPqWUTGWWp\DQAEUU1qE3PsL\1D>)4;J-%8˾R.Yو.[8 vXOߏʘêڴhP~Aqo5Ǯ攫x6q|Bv_LN\w7,% Hc3f˹ 0'su_aIJv-L]꜀}BIyyVP;vMKS5@uE~6C) nj o19fd.:*c SȐ74?tqBK B8&$:,:[QFӝ:t8"E"BBƈDcD",&$nY* ɺ^ۮ,ssdPl#Q"/SBK>a I|}7mk9A:6vp NSLZF;]thȽ:zuiZ귞pY`\qN/Mb[q^ y+XRH2[3u{aYN׾ f1.arIb8ZPBV+Qa$)]+y~(pwώ,$[!كL/LÕYV"8&C#w!/>Zf.L^w};׺:3mӼFld48B7wZTt6^pԓVtui5𢝌HXDE"ѭ|(Jxxpv1qaD95(Ɇ 9)x#gfXА"%f1JOtO^n6ٿ.4ǃUoaK$DS=E xw׭]xmNyZg'`AkFkv/f;g)n_ӝniuwnRk 0C0&Cס*)چQD4X۱n 35#әfM+B@ 0~SZLC7rҜ9wΉ4nV8\kM̎ץWIJrϝP2Tu^5P;iڗĔrCn TX/:&G}T'FϾjzOnKGbp2e5+!BْۻveQjT0u\@Qć@aԘܑN$~ggridges/data/lincoln_weather.rda0000644000176200001440000001764614536504206016646 0ustar liggesusersBZh91AY&SYEMV#_}{#\ע/|â `@*'ꪀJU< M& "fySCj jzjLM&F@ z 44 Bj24A&5M44 ѠC44A4 j=L=zG'm44z➈1Fȇ40&CGyGꞧz&jmGFFa=&I2& 2jy)'&L0C@mC&P 4@4@4@@h/wֽ׹|Օ+Xa=G:.vMԻ] ѶU\TԘtH jVtřDE9B0Dғ$PsERdVZL&:j~_su $3,iY`c)BpLUH26GV6sZBL#R+AuUi" Qzn`2Gw(Ik}xD@3Xv$X6#LM@":*3Qeg[]3\^: 牷 EW*IZS[:MWk&hϻlsZt$ժG)<t2rԵ4b\WCED1uTvmQͦJLaxQdDS D[/4P5u4d@+GID+4Q+u͊b ̱rRբ/i-e,ƐI<)W5v&PxL c)S A.)ԢD)EpJ2t<{yd2^%Ix'0*A@arktjV=z6׵ zܻI''yȪ1M$..:aACM-уeTTX# f&к]1V6ȼ:,`;ZVSfMr÷ΊP5{1SfJ3fs֓hm^)͛3MlŜᆆp g& j͘hκqi6VFS-fynI1Xc1'jJ6tb\7ÚɊ;XX@1C6dZlb*6smV){yk@ ʰwgÞd")yd/ѠV(#JP4Xz3;CMmyEO!5hg`F~(Γ_s/&9M.nŎcbzj"7y78dÜBxЬC 0am+zXiy~tGXc8QyE0CLI/T_Ѡ|2Q*skda:r(G]@ץW{irLy'B,f RfZz4NcXP25]̏B}-M%+ aiOqy5md*R 5zӓDҭ 5Q_"|\`-U5hmNڨnVznE J1 rub kE.:O$t;g'u^Ad)-HV(CdVYZjfeSn]'$B[RbY:dVRn=ҦXH bMd[O{O~zo)(YO@!Tkɯlի&Y-;^`Bl-X=a+%9PZ'a3]&ľ@+5/LViO;a {Cy6ݥ6NdknB԰ JY%偪 6u؆W1iHZM#OPP`v[cE+Ip?i@v'@))SmkzV,(\rh5M78.`(ԷL̤yt?{wuݎސj{裿zrn$]]CRj}JkR¦ë,^ixU} hm < pPq&_5hDc,bO'`ǍP5#ݒܳ{^-H^ƗHuA v Aђ!d~QxF˅HY\n6ݧlT|!e\S$muV^IJ!ە#Rޒ2c@3q-9 ]\;6p y 8Ta'vA?#Rp0vr (MO@eZ[u1H%^^]a]ی~uɌHa%PqY&Ctݞ _iلCTg27AlNk|T##s'N<3ua\j.:J b/X/JJ/5ߏ}q7PV*;ks]3/ {2YSZ|F2*j-Ʈ  c˕ ӥcܺnG#3.*j[܂jzfdZ˂q@DT^Qrv.y%L}F}̴ z 4[tn֊1l ׿e 4yu:ZL۶QwzOm@ v:,"ܲa|N^LFR: v[<1l;p6aUv8a/bNyPt(.b Pwbr[ZP >l޴ɩ :)M (0siS`>Koǹ[K/W@"ײ NqhW]eÎǪ7Ԫ(%oGHO<,u?ؕ=@U_XSa!gj0'^!P`:Axʤ"ڈ`n]plAXMrb˼iwѷ"^RҠE/>bmw'{]_mOjXʈjRtt;f;+j.8@,d.&!4l}"C%oE47)J:l +mH7)Q2vi-} s,Ʊ2 @ƙjvX1ھb HJ*᪯>M߳{*U[$ךMaE"H1!RK{|Eli<@VR^3A֐/_cZ='*:@6fSؤ60BGfc`:cՕ6e_=6bXdi1s%wi njvN A@DDP󐔓h뗹$-}Ue]>7UPjLՊmħ. M+,r\vEY1AQ6B#B<%]:E# lQYmzPȑQ]p Xd!g:o YT; 3Ѝ#{UZsF&l9p)*$,J0YЀro枕I-$x>}=V &(C(^#S eaWЂȀ " קAILJH'C'![lzDɣQ&']k($y5Cl!ź!P٬ ҪqjȤ5XMsራ9:\`Vpkc1/wugF -AG̜DcC )Ddzf<\{t^{GJ#pJFұ2ETw1s}""";!tZ-Dj&Q-"h( -bP!< 0PH]UJI `Cv B b QBp B BhCƄ ߟ+mZ؇5++I7HY$/3!Ύ6q, @l.  @`meFa$Д(#QfPbˆؐP HX`dT:;;;;;;92^U&LY2=kZRֶ0ֵZ0 " " " "" =kZrs@@l`k[Zzֵ8l`kvP4$P3*c uJ!m+$ d :jVڶjV$!Ӥ5$t@6p^/[z7(F1UUUUVUUUUUUUUUUUEUUy@d@E !T[ hamm(YjUUUUUUUUUUU% ?AH(r AFDVDQ%H)Q  dH)dH) AFDT:P#B(DB(NHu:9+HUUUUUz!IH!Q;PA<$ -UUUUUpB+9@̲UUUU[$HC:tU,UUUUV$Y$RHHbVVEYdUVBIHMuo$o$27olUUUUV&RM͞w~-mmmmpoMYX$~8;Pѭ-PQ-RBoF Ñ D2^_ҹr5 E>5X=Vşȡ+W&b-=ڛcRMVYT3]#aXO4A%E$I2MͣdD%6 ё`T-H*ʲ;,Pvf}7;MgQe#8a"ܻ8 m-v+WL#֪3n&0ḷO-&xk7SQ,J$5t/Dh.kRE;>:Wd7#fum\0ѩLd3KeQ ޔ+tbZ2/ʱnmu[u}Tge]+Ig<+`މrE#Bȳ Y)^b4!IPپ:f=G(imji%ab]H&9bM1 Z8ReXBvAE%~%HTI :lX|K$" /@Qw=iƟXjCDz3)e3gچss!&z`.ajr\J@'! * $X 0(YEAFv jޣWkXS `vM2+Xi8Khꠚti4ob 8 ,84Qߠ:#Tng47" Fqpjb ς+qQHiȤCgy;1ׂ9-mDtJzBj糅QTPրj!`(}$2(ZD.9*eZ#e@/oOM&T:"bI( m]m1B)!,q '(xEFY&5  c m*+I I; E}B!O:0an5ȫrP$ d!`z/.aQ\01E(ΤQH2= J1DEuPR) H"HJ@?kD+ "P,ǠVX:RI!! ?V(. h% BXR2ef1UjUQq*zDDy H4+ YDE|}]{yo9C||9*?\TܑN$rS@ggridges/man/0000755000176200001440000000000014536505336012632 5ustar liggesusersggridges/man/stat_binline.Rd0000644000176200001440000001530114536504206015567 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/stats.R \docType{data} \name{stat_binline} \alias{stat_binline} \alias{StatBinline} \title{Stat for histogram ridgeline plots} \usage{ stat_binline( mapping = NULL, data = NULL, geom = "density_ridges", position = "identity", ..., binwidth = NULL, bins = NULL, center = NULL, boundary = NULL, breaks = NULL, closed = c("right", "left"), pad = TRUE, draw_baseline = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. All objects will be fortified to produce a data frame. See \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame}, and will be used as the layer data. A \code{function} can be created from a \code{formula} (e.g. \code{~ head(.x, 10)}).} \item{geom}{The geom to use for drawing.} \item{position}{Position adjustment, either as a string naming the adjustment (e.g. \code{"jitter"} to use \code{position_jitter}), or the result of a call to a position adjustment function. Use the latter if you need to change the settings of the adjustment.} \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{colour = "red"} or \code{size = 3}. They may also be parameters to the paired geom/stat.} \item{binwidth}{The width of the bins. Can be specified as a numeric value or as a function that calculates width from unscaled x. Here, "unscaled x" refers to the original x values in the data, before application of any scale transformation. When specifying a function along with a grouping structure, the function will be called once per group. The default is to use the number of bins in \code{bins}, covering the range of the data. You should always override this value, exploring multiple widths to find the best to illustrate the stories in your data. The bin width of a date variable is the number of days in each time; the bin width of a time variable is the number of seconds.} \item{bins}{Number of bins. Overridden by \code{binwidth}. Defaults to 30.} \item{center, boundary}{bin position specifiers. Only one, \code{center} or \code{boundary}, may be specified for a single plot. \code{center} specifies the center of one of the bins. \code{boundary} specifies the boundary between two bins. Note that if either is above or below the range of the data, things will be shifted by the appropriate integer multiple of \code{binwidth}. For example, to center on integers use \code{binwidth = 1} and \code{center = 0}, even if \code{0} is outside the range of the data. Alternatively, this same alignment can be specified with \code{binwidth = 1} and \code{boundary = 0.5}, even if \code{0.5} is outside the range of the data.} \item{breaks}{Alternatively, you can supply a numeric vector giving the bin boundaries. Overrides \code{binwidth}, \code{bins}, \code{center}, and \code{boundary}.} \item{closed}{One of \code{"right"} or \code{"left"} indicating whether right or left edges of bins are included in the bin.} \item{pad}{If \code{TRUE}, adds empty bins at either end of x. This ensures that the binline always goes back down to 0. Defaults to \code{TRUE}.} \item{draw_baseline}{If \code{FALSE}, removes lines along 0 counts. Defaults to \code{TRUE}.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes. It can also be a named logical vector to finely select the aesthetics to display.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} } \description{ Works like \code{stat_bin} except that the output is a ridgeline describing the histogram rather than a set of counts. } \examples{ library(ggplot2) ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species, fill = Species)) + geom_density_ridges(stat = "binline", bins = 20, scale = 2.2) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges() ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species, fill = Species)) + stat_binline(bins = 20, scale = 2.2, draw_baseline = FALSE) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0)) + scale_fill_grey() + coord_cartesian(clip = "off") + theme_ridges() + theme(legend.position = 'none') library(ggplot2movies) ggplot(movies[movies$year>1989,], aes(x = length, y = year, fill = factor(year))) + stat_binline(scale = 1.9, bins = 40) + scale_x_continuous(limits = c(1, 180), expand = c(0, 0)) + scale_y_reverse(expand = c(0, 0)) + scale_fill_viridis_d(begin = 0.3, option = "B") + coord_cartesian(clip = "off") + labs(title = "Movie lengths 1990 - 2005") + theme_ridges() + theme(legend.position = "none") count_data <- data.frame( group = rep(letters[1:5], each = 10), mean = rep(1:5, each = 10) ) count_data$group <- factor(count_data$group, levels = letters[5:1]) count_data$count <- rpois(nrow(count_data), count_data$mean) ggplot(count_data, aes(x = count, y = group, group = group)) + geom_density_ridges2( stat = "binline", aes(fill = group), binwidth = 1, scale = 0.95 ) + geom_text( stat = "bin", aes(y = group + 0.9*stat(count/max(count)), label = ifelse(stat(count) > 0, stat(count), "")), vjust = 1.2, size = 3, color = "white", binwidth = 1 ) + scale_x_continuous(breaks = c(0:12), limits = c(-.5, 13), expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + scale_fill_cyclical(values = c("#0000B0", "#7070D0")) + guides(y = "none") + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE) } \keyword{datasets} ggridges/man/position_raincloud.Rd0000644000176200001440000000356114536504206017025 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/position.R \docType{data} \name{position_raincloud} \alias{position_raincloud} \alias{PositionRaincloud} \title{Create a cloud of randomly jittered points below a ridgeline plot} \usage{ position_raincloud( width = 0, height = 0.4, ygap = 0.05, adjust_vlines = FALSE, seed = NULL ) } \arguments{ \item{width}{Width for horizontal jittering. By default set to 0.} \item{height}{Total height of point cloud. By default 0.4.} \item{ygap}{Vertical gap between ridgeline baseline and point cloud.} \item{adjust_vlines}{If \code{TRUE}, adjusts vertical lines (as are drawn for quantile lines, for example) to align with the point cloud.} \item{seed}{Random seed. See \code{\link{position_points_jitter}}.} } \description{ This is a position adjustment specifically for \code{\link[=geom_density_ridges]{geom_density_ridges()}} and related geoms. It only jitters the points drawn by these geoms, if any. If no points are present, the plot remains unchanged. The effect is similar to \code{\link[=position_points_jitter]{position_points_jitter()}}, only that by default the points lie all underneath the baseline of each individual ridgeline. } \details{ The idea for this position adjustment comes from Micah Allen's work on raincloud plots (Allen et al. 2021). } \examples{ library(ggplot2) ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(jittered_points = TRUE, position = "raincloud", alpha = 0.7) } \references{ Allen, M., Poggiali, D., Whitaker, K., Marshall, T. R., van Langen, J., Kievit, R. A. (2021) Raincloud plots: a multi-platform tool for robust data visualization [version 2; peer review: 2 approved]. Wellcome Open Res 4:63. } \seealso{ Other position adjustments for ridgeline plots: \code{\link{position_points_jitter}}, \code{\link{position_points_sina}} } \keyword{datasets} ggridges/man/geom_density_ridges.Rd0000644000176200001440000001400314536504206017135 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/geoms.R \docType{data} \name{geom_density_ridges} \alias{geom_density_ridges} \alias{GeomDensityRidges} \alias{geom_density_ridges2} \alias{GeomDensityRidges2} \title{Create ridgeline plot} \usage{ geom_density_ridges( mapping = NULL, data = NULL, stat = "density_ridges", position = "points_sina", panel_scaling = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ... ) geom_density_ridges2( mapping = NULL, data = NULL, stat = "density_ridges", position = "points_sina", panel_scaling = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ... ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[=aes]{aes()}} or \code{\link[=aes_]{aes_()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[=ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame.}, and will be used as the layer data.} \item{stat}{The statistical transformation to use on the data for this layer, as a string.} \item{position}{Position adjustment, either as a string, or the result of a call to a position adjustment function.} \item{panel_scaling}{If \code{TRUE}, the default, relative scaling is calculated separately for each panel. If \code{FALSE}, relative scaling is calculated globally.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them.} \item{...}{other arguments passed on to \code{\link[=layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{color = "red"} or \code{linewidth = 3}. They may also be parameters to the paired geom/stat.} } \description{ \code{geom_density_ridges} arranges multiple density plots in a staggered fashion, as in the cover of the famous Joy Division album Unknown Pleasures. } \details{ By default, this geom calculates densities from the point data mapped onto the x axis. If density calculation is not wanted, use \code{stat="identity"} or use \code{\link{geom_ridgeline}}. The difference between \code{geom_density_ridges} and \code{\link{geom_ridgeline}} is that \code{geom_density_ridges} will provide automatic scaling of the ridgelines (controlled by the \code{scale} aesthetic), whereas \link{geom_ridgeline} will plot the data as is. Note that when you set \code{stat="identity"}, the \code{height} aesthetic must be provided. Note that the default \code{\link{stat_density_ridges}} makes joint density estimation across all datasets. This may not generate the desired result when using faceted plots. As an alternative, you can set \code{stat = "density"} to use \code{\link{stat_density}}. In this case, it is required to add the aesthetic mapping \code{height = after_stat(density)} (see examples). } \section{Aesthetics}{ Required aesthetics are in bold. \itemize{ \item \strong{\code{x}} \item \strong{\code{y}} \item \code{group} Defines the grouping. Not needed if a categorical variable is mapped onto \code{y}, but needed otherwise. Will typically be the same variable as is mapped to \code{y}. \item \code{height} The height of each ridgeline at the respective x value. Automatically calculated and provided by \code{\link{stat_density_ridges}} if the default stat is not changed. \item \code{scale} A scaling factor to scale the height of the ridgelines relative to the spacing between them. A value of 1 indicates that the maximum point of any ridgeline touches the baseline right above, assuming even spacing between baselines. \item \code{rel_min_height} Lines with heights below this cutoff will be removed. The cutoff is measured relative to the overall maximum, so \code{rel_min_height=0.01} would remove everything that is 1\\% or less than the highest point among all ridgelines. Default is 0, so nothing is removed. alpha \item \code{colour}, \code{fill}, \code{group}, \code{alpha}, \code{linetype}, \code{linewidth}, as in \code{\link{geom_ridgeline}}. \item \code{point_shape}, \code{point_colour}, \code{point_size}, \code{point_fill}, \code{point_alpha}, \code{point_stroke}, as in \code{\link{geom_ridgeline}}. } } \examples{ library(ggplot2) # set the `rel_min_height` argument to remove tails ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(rel_min_height = 0.005) + scale_y_discrete(expand = c(0.01, 0)) + scale_x_continuous(expand = c(0.01, 0)) + theme_ridges() # set the `scale` to determine how much overlap there is among the plots ggplot(diamonds, aes(x = price, y = cut)) + geom_density_ridges(scale = 4) + scale_y_discrete(expand = c(0.01, 0)) + scale_x_continuous(expand = c(0.01, 0)) + theme_ridges() # the same figure with colors, and using the ggplot2 density stat ggplot(diamonds, aes(x = price, y = cut, fill = cut, height = after_stat(density))) + geom_density_ridges(scale = 4, stat = "density") + scale_y_discrete(expand = c(0.01, 0)) + scale_x_continuous(expand = c(0.01, 0)) + scale_fill_brewer(palette = 4) + theme_ridges() + theme(legend.position = "none") # use geom_density_ridges2() instead of geom_density_ridges() for solid polygons ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges2() + scale_y_discrete(expand = c(0.01, 0)) + scale_x_continuous(expand = c(0.01, 0)) + theme_ridges() } \keyword{datasets} ggridges/man/geom_ridgeline.Rd0000644000176200001440000001104414536504206016065 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/geoms.R \docType{data} \name{geom_ridgeline} \alias{geom_ridgeline} \alias{GeomRidgeline} \title{Plot a ridgeline (line with filled area underneath)} \usage{ geom_ridgeline( mapping = NULL, data = NULL, stat = "identity", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ... ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[=aes]{aes()}} or \code{\link[=aes_]{aes_()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[=ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame.}, and will be used as the layer data.} \item{stat}{The statistical transformation to use on the data for this layer, as a string.} \item{position}{Position adjustment, either as a string, or the result of a call to a position adjustment function.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them.} \item{...}{other arguments passed on to \code{\link[=layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{color = "red"} or \code{linewidth = 3}. They may also be parameters to the paired geom/stat.} } \description{ Plots the sum of the \code{y} and \code{height} aesthetics versus \code{x}, filling the area between \code{y} and \code{y + height} with a color. Thus, the data mapped onto y and onto height must be in the same units. If you want relative scaling of the heights, you can use \code{\link{geom_density_ridges}} with \code{stat = "identity"}. } \details{ In addition to drawing ridgelines, this geom can also draw points if they are provided as part of the dataset. The stat \code{\link[=stat_density_ridges]{stat_density_ridges()}} takes advantage of this option to generate ridgeline plots with overlaid jittered points. } \section{Aesthetics}{ Required aesthetics are in bold. \itemize{ \item \strong{\code{x}} \item \strong{\code{y}} \item \strong{\code{height}} Height of the ridgeline, measured from the respective \code{y} value. Assumed to be positive, though this is not required. \item \code{group} Defines the grouping. Required when the dataset contains multiple distinct ridgelines. Will typically be the same variable as is mapped to \code{y}. \item \code{scale} A scaling factor to scale the height of the ridgelines. A value of 1 indicates that the heights are taken as is. This aesthetic can be used to convert \code{height} units into \code{y} units. \item \code{min_height} A height cutoff on the drawn ridgelines. All values that fall below this cutoff will be removed. The main purpose of this cutoff is to remove long tails right at the baseline level, but other uses are possible. The cutoff is applied before any height scaling is applied via the \code{scale} aesthetic. Default is 0, so negative values are removed. \item \code{colour} Color of the ridgeline \item \code{fill} Fill color of the area under the ridgeline \item \code{alpha} Transparency level of \code{fill}. Not applied to \code{color}. If you want transparent lines, you can set their color as RGBA value, e.g. #FF0000A0 for partially transparent red. \item \code{group} Grouping, to draw multiple ridgelines from one dataset \item \code{linetype} Linetype of the ridgeline \item \code{linewidth} Line thickness \item \code{point_shape}, \code{point_colour}, \code{point_size}, \code{point_fill}, \code{point_alpha}, \code{point_stroke} Aesthetics applied to points drawn in addition to ridgelines. } } \examples{ library(ggplot2) d <- data.frame(x = rep(1:5, 3), y = c(rep(0, 5), rep(1, 5), rep(3, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) ggplot(d, aes(x, y, height = height, group = y)) + geom_ridgeline(fill="lightblue") } \keyword{datasets} ggridges/man/scale_vline.Rd0000644000176200001440000000301514536504206015377 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/scale-vline.R \name{scale_vline} \alias{scale_vline_linetype} \alias{scale_vline_width_continuous} \alias{scale_vline_colour_hue} \alias{scale_vline_color_hue} \alias{scale_vline_colour_gradient} \alias{scale_vline_color_gradient} \alias{scale_vline_linetype_discrete} \alias{scale_vline_color_discrete} \alias{scale_vline_colour_discrete} \alias{scale_vline_color_continuous} \alias{scale_vline_colour_continuous} \title{Scales for vline aesthetics} \description{ These are various scales that can be applied to vline aesthetics, such as \code{vline_color}, \code{vline_width}, \code{vline_linetype}. The individual scales all have the same usage as existing standard ggplot2 scales, only the name differs. } \examples{ library(ggplot2) # default scales ggplot(iris, aes(x=Sepal.Length, y=Species, fill = Species, color = Species)) + geom_density_ridges( aes(vline_color = Species, vline_linetype = Species), alpha = .4, quantile_lines = TRUE ) + theme_ridges() # modified scales ggplot(iris, aes(x=Sepal.Length, y=Species, fill = Species, color = Species)) + geom_density_ridges( aes(vline_color = Species), alpha = .4, quantile_lines = TRUE ) + scale_fill_hue(l = 50) + scale_vline_color_hue(l = 30) + theme_ridges() } \seealso{ See \code{\link[=scale_point_color_hue]{scale_point_color_hue()}} for specific scales for point aesthetics and \code{\link[=scale_discrete_manual]{scale_discrete_manual()}} for a general discrete scale. } ggridges/man/Catalan_elections.Rd0000644000176200001440000000134514536504206016527 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{Catalan_elections} \alias{Catalan_elections} \title{Results from Catalan regional elections (1980-2015)} \format{ A tibble with 20764 rows and 4 variables: \describe{ \item{\code{Municipality}}{} \item{\code{Year}}{} \item{\code{Option}}{The voter option; either "Indy" or "Unionist"} \item{\code{Percent}}{The percentage of the voters choosing the given option} } } \usage{ Catalan_elections } \description{ Data from Catalan regional elections for 949 municipalities, from 11 elections spanning the years 1980-2015. The data was obtained and processed from Idescat.cat by Marc Belzunces (Twitter: @marcbeldata). } \keyword{datasets} ggridges/man/position_points_jitter.Rd0000644000176200001440000000363714536504206017746 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/position.R \docType{data} \name{position_points_jitter} \alias{position_points_jitter} \alias{PositionPointsJitter} \title{Randomly jitter the points in a ridgeline plot} \usage{ position_points_jitter( width = 0, height = 0.2, yoffset = 0, adjust_vlines = FALSE, seed = NULL ) } \arguments{ \item{width}{Width for horizontal jittering. By default set to 0.} \item{height}{Height for vertical jittering, applied in both directions (up and down). By default 0.2.} \item{yoffset}{Vertical offset applied in addition to jittering.} \item{adjust_vlines}{If \code{TRUE}, adjusts vertical lines (as are drawn for quantile lines, for example) to align with the point cloud.} \item{seed}{Random seed. If set to NULL, the current random number generator is used. If set to NA, a new random random seed is generated. If set to a number, this number is used as seed for jittering only.} } \description{ This is a position adjustment specifically for \code{\link[=geom_density_ridges]{geom_density_ridges()}} and related geoms. It only jitters the points drawn by these geoms, if any. If no points are present, the plot remains unchanged. The effect is similar to \code{\link[=position_jitter]{position_jitter()}}: points are randomly shifted up and down and/or left and right. } \examples{ library(ggplot2) # default jittered points ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(jittered_points = TRUE, position = "points_jitter", alpha = 0.7) # simulating a rug ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(jittered_points = TRUE, point_shape = '|', alpha = 0.7, point_size = 2, position = position_points_jitter(width = 0.02, height = 0)) } \seealso{ Other position adjustments for ridgeline plots: \code{\link{position_points_sina}}, \code{\link{position_raincloud}} } \keyword{datasets} ggridges/man/theme_ridges.Rd0000644000176200001440000000270014536504206015552 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/theme.R \name{theme_ridges} \alias{theme_ridges} \title{A custom theme specifically for use with ridgeline plots} \usage{ theme_ridges( font_size = 14, font_family = "", line_size = 0.5, grid = TRUE, center_axis_labels = FALSE ) } \arguments{ \item{font_size}{Overall font size. Default is 14.} \item{font_family}{Default font family.} \item{line_size}{Default line size.} \item{grid}{If \code{TRUE} (default), a background grid is drawn. If \code{FALSE}, background is left empty.} \item{center_axis_labels}{If \code{TRUE}, axis labels are drawn centered. If \code{FALSE} (default), axis lables are drawn right/top-aligned.} } \value{ The theme. } \description{ This theme has some special modifications that make ridgeline plots look better, such as properly aligned y axis labels. It can draw plots with and without background grids (see examples). } \examples{ library(ggplot2) # Example with background grid ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species)) + geom_density_ridges(rel_min_height = 0.005) + scale_y_discrete(expand = c(0.01, 0)) + scale_x_continuous(expand = c(0.01, 0)) + theme_ridges() # Example without background grid ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species)) + geom_density_ridges() + scale_y_discrete(expand = c(0.01, 0)) + scale_x_continuous(expand = c(0.01, 0)) + theme_ridges(grid = FALSE) } ggridges/man/geom_density_line.Rd0000644000176200001440000000701414536504206016613 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/geom-density-line.R \docType{data} \name{geom_density_line} \alias{geom_density_line} \alias{GeomDensityLine} \title{Smoothed density estimates drawn with a ridgeline rather than area} \usage{ geom_density_line( mapping = NULL, data = NULL, stat = "density", position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE ) } \arguments{ \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and \code{inherit.aes = TRUE} (the default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. There are three options: If \code{NULL}, the default, the data is inherited from the plot data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. A \code{data.frame}, or other object, will override the plot data. All objects will be fortified to produce a data frame. See \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. A \code{function} will be called with a single argument, the plot data. The return value must be a \code{data.frame}, and will be used as the layer data. A \code{function} can be created from a \code{formula} (e.g. \code{~ head(.x, 10)}).} \item{stat}{The statistical transformation to use on the data for this layer, either as a \code{ggproto} \code{Geom} subclass or as a string naming the stat stripped of the \code{stat_} prefix (e.g. \code{"count"} rather than \code{"stat_count"})} \item{position}{Position adjustment, either as a string naming the adjustment (e.g. \code{"jitter"} to use \code{position_jitter}), or the result of a call to a position adjustment function. Use the latter if you need to change the settings of the adjustment.} \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}. These are often aesthetics, used to set an aesthetic to a fixed value, like \code{colour = "red"} or \code{size = 3}. They may also be parameters to the paired geom/stat.} \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. \code{FALSE} never includes, and \code{TRUE} always includes. It can also be a named logical vector to finely select the aesthetics to display.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} } \description{ This function is a drop-in replacement for ggplot2's \code{\link[=geom_density]{geom_density()}}. The only difference is that the geom draws a ridgeline (line with filled area underneath) rather than a polygon. } \examples{ library(ggplot2) ggplot(diamonds, aes(carat)) + geom_density_line() ggplot(diamonds, aes(carat)) + geom_density_line(adjust = 1/5) ggplot(diamonds, aes(carat)) + geom_density_line(adjust = 5) ggplot(diamonds, aes(depth, colour = cut)) + geom_density_line(alpha = 0.5) + xlim(55, 70) ggplot(diamonds, aes(depth, fill = cut, colour = cut)) + geom_density_line(alpha = 0.1) + xlim(55, 70) } \seealso{ See \code{\link[=geom_density]{geom_density()}}. } \keyword{datasets} ggridges/man/lincoln_weather.Rd0000644000176200001440000000305414536504206016273 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{lincoln_weather} \alias{lincoln_weather} \title{Weather in Lincoln, Nebraska in 2016.} \format{ A tibble with 366 rows and 24 variables: \describe{ \item{\code{CST}}{Day of the measurement} \item{\verb{Max Temperature [F]}}{} \item{\verb{Mean Temperature [F]}}{} \item{\verb{Min Temperature [F]}}{} \item{\verb{Max Dew Point [F]}}{} \item{\verb{Mean Dew Point [F]}}{} \item{\verb{Min Dewpoint [F]}}{} \item{\verb{Max Humidity}}{} \item{\verb{Mean Humidity}}{} \item{\verb{Min Humidity}}{} \item{\verb{Max Sea Level Pressure [In]}}{} \item{\verb{Mean Sea Level Pressure [In]}}{} \item{\verb{Min Sea Level Pressure [In]}}{} \item{\verb{Max Visibility [Miles]}}{} \item{\verb{Mean Visibility [Miles]}}{} \item{\verb{Min Visibility [Miles]}}{} \item{\verb{Max Wind Speed [MPH]}}{} \item{\verb{Mean Wind Speed[MPH]}}{} \item{\verb{Max Gust Speed [MPH]}}{} \item{\code{Precipitation [In]}}{} \item{\code{CloudCover}}{} \item{\code{Events}}{Specific weather events, such as rain, snow, or fog} \item{\code{WindDir [Degrees]}}{} \item{\code{Month}}{The month in which the measurement was taken} } } \usage{ lincoln_weather } \description{ A dataset containing weather information from Lincoln, Nebraska, from 2016. Originally downloaded from Weather Underground by Austin Wehrwein, http://austinwehrwein.com/. The variables are listed below. Most are self-explanatory. Max, mean, and min measurements are calculated relative to the specific day of measurement. } \keyword{datasets} ggridges/man/reduce.Rd0000644000176200001440000000164414536504206014370 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{reduce} \alias{reduce} \title{Reduce a list to a single value by iteratively applying a binary function} \usage{ reduce(.x, .f, ..., .init) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{A 2-argument function. The function will be passed the accumulated value as the first argument and the "next" value as the second argument.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.init}{If supplied, will be used as the first value to start the accumulation, rather than using \code{x[[1]]}. This is useful if you want to ensure that \code{reduce} returns a correct value when \code{.x} is empty. If missing, and \code{x} is empty, will throw an error.} } \description{ Inspired by \code{reduce()} from the \code{purrr} package } \author{ Jonathon Love \href{mailto:jon@thon.cc}{jon@thon.cc} } \keyword{internal} ggridges/man/figures/0000755000176200001440000000000014536610215014267 5ustar liggesusersggridges/man/figures/README-diamonds-1.png0000644000176200001440000013156514536610215017677 0ustar liggesusersPNG  IHDRz4iCCPkCGColorSpaceGenericRGB8U]hU>+$΃Ԧ5lRфem,lAݝi&3i)>A['!j-P(G 3k~s ,[%,-:t} }-+*&¿ gPG݅ج8"eŲ]A b ;l õWϙ2_E,(ۈ#Zsێ<5)"E6N#ӽEkۃO0}*rUt.iei #]r >cU{t7+ԙg߃xuWB_-%=^ t0uvW9 %/VBW'_tMۓP\>@y0`D i|[` hh)Tj0B#ЪhU# ~yhu fp#1I/I"0! 'Sdd:J5ǖ"sdy#R7wAgdJ7kʕn^:}nWFVst$gj-tԝr_װ_7Z ~V54V }o[G=Nd>-UlaY5V}xg[?k&>srq߀].r_r_qsGjy4k iQܟBZ-<(d=dKO a/zv7]ǰod}sn?TF'|3Nn#I?"mzv~K=گsl<b|_|4>?pߋQrib 2* (Ѧh{28oIyes8';Z9h6g>xRx'b8ՃWOϫ[xn%|^z}%x c8eXIfMM*i_@IDATx OTH"Z%""[Ȯd lYB"!dk!iZY*B([ſOs{g{<sgΜy̼})bTD@D@D@D@D Gqt$@C) МD@D@D@D@$@) МD@D@D@D@$@) МD@D@D@D@$@(SN5/YjUY X@ޯ{mO?dVXi뮦Ce˖fҥ֯2'Q/#,=7?\ ,^,_< gAXr3&@K+&*M41W]uUH&ȑ#͍7hڵkgZ矛/Hhi^p?/tpUJכRs͹UgSEHgΜij֬YZ~rHARիSԩYoLǎO?m.{]ߋD@Vt…yeoX\9sA%^_@,ٳg{tŊOo۶y}g^x]YD@D@D M65 0s5>}.ҍ6ڨi#<7pC+V[n];}d}YSreӪUaE4{f-\D@D@D@O +B?͕W^iWndK70TXLUXډg袋|" " " &XRڵ_BO8mj]>}i֬:UUV4n u HL"Tr"DE2d}cwnfƍgSE@+E@D@D@"I .xwV5j0g}vdC&";wlSR "ѣm;xE橧2Æ 3vZMND@D@D [oyGb~L 7ܐp}!^cǎ55*&\?F!Z>@ 4;<ͬ9H{|wvjРAR ;ywn/E@D@D@E gl̴iӢE@$>+i CΘ1#FK gtO?5źMԚR$0a_1/-JÆ mҘ1cmD@D@D@"D 0/s1o6G( ,"CLUHR(S#JsTvtԩ$!jk/۵ajZ0+Sn;3qtvѶ" " " y"믿̋/hz3|𵧳z7xÐV⌈"3]J'|bxQ6oKm^}ݭ~"̙3'mNJ+ٳRD@D@D a}ZjUSP!$"@>Y~GX@sҤIzLvkƬ^z6l7n"̜9~ņQ ,Y,(^z.̌?FqҥV 2Ĵh&%Q#RdMnyCeMvLK.l{{1=˛s9Ӱ"Ùi2ym.|TD@D@D@I p5H&&ǚE~FR0L"%&@8H5jIGD@D@D B3ᓇ_(B@3WHW_Zjet6n$fO;@&@sv:PXpM[ftNիW7LSLh$" " "ᬣ _ڭb [ߤvFfZ  Ш\ n!S ( / . ^kP+U#@g?d\vh|U{@ʦC!@)GD@D@"I@444#F}Ŋ%@E@D@D $@#xQJIs55kI@O ҴiӲG;G@4<9 K,dkl`ME@D@D $@sM\NjK`޼yv}tm54q" " " % _:'@;%.x@2TD@D@2& 1:'@m~]e{y ?)aY@ԩcWBzAD@D@D $@#qJX@Vj6pP lfS7|(tUdF@43n+ aE1<"T~" " "%hX,H'Ov53 <_R?<]a$rt̙vT$z-䇀h~w} (CrΚ5KE@D@D@"@@4T@=t"Z4@HF튔P{\а}@7h#+r5"R t" " & S܍"}',䙀h/@)>nݺ/]D@D R$@#u9J1t=^bO?_̛7/c" " "  H&oC$4LX@)j1䕀h^V^='j֬i nDD@D@MG_I  yO ]e<t|9wn; PCs# 補S(K@,}\G~͒%Kܹs*E@D@D@rL@4u#@M6$8u(" " k HE\ȗeOsyu,(K@,}|uszc?~|T% KDC'_Y?|tsr3~YreK@t]&Z2|BZR%_|.jղ㷏1b]A4{a^xVh ӲΜ9矆1v}wGu歷޲s&j֋O@4}f#t?eSzuv%=ڞ{i~W3{e%$O?t&k$@X&O'(&MlD \txM#<tժOD@D@JhI_ܜ|9@ILU4 ʱ۴ic+jI@'(m9'@7l3_̚5nGaf… ilМ[l۷o{iSd$@w XY8\r6jilD!}}p3$衇Mۊ$ VG $vG֫W/NիW{キׯM gm#" " "hb6& ;w陉%o9sjuj9dϖBD@D@D@& 62.oƷ *$~3ѱl1cFMZϐD}67hVig(q%铄o<P~/_|ZMc{r Vi׮w7 WD@D@JhI\Eߤ__ufƍۜ#Ft7zkիN(%sstSe$k6I7~X}vy%(pxPm$~a lڣGJs4 H LGQIիܑwEeRؗa P}f̘1fԨQ4S@-@t3f{]vHz6lhꎳv^wun" " " iH ҨL@,$"@)52$ĞU{Ͱa:% Z6'#@WZe~ᇬ(Crf7hCdܠD@D@ h^;tArUʗ/o9W_a:su\GD@D@ h!]j? &@_\:uR3fذa<%" " A@ .S5l`W?VZI˺u *,~qmW<^HBK@~r8't)W HyO<ъlZR& ZW?s%QIs6lhudM;vr}xOD@D@"K@4ߥQ`[!;8c(k>iӦc5wq4hP>c@HF~:DzLU.xw$gvr>'5S-ifN" " "5Q"E %i|Xe 74#ԝ${fM75Gqwi." " H@zR0_CjFL>-RJk׮69?"sWR% ZW>TpF]MYq饗>tQ:@^ Hq "'@SYJ̚5+۪?߼vĤ+T" " "P$@ Ekz7TbvۼwûlٲܹyǕA\D@D/t]vIy,,awF4nL0}m۶-W^v+2mRD@D@D Wd9ΟiS0dl>#;4'OΊ b.[|Ys9CUW]edժKD@D@"M@4җ_FL">opݻ?~|@%̙33#O9['x"CN ]n z뭓Y@}QXmL͢E֛K@ԩ9J{8 ӺukӥKO&:! Z42'BM=Y(XR9zryYg80YIcX3f| aXf9ۜy68)ND@D@ h_ 5`:ta ]wuU\fĉfkקФI [Ϝ}f}PF =" " A ]~6h#o̙3tAvy3ժU3Ç_;?+4h`*T`OgoD 뮻rPD@D@rA@4K_~=zA+3/_x>CZ~sŊDBw<ڵ^{mTv@$@3Fc 0iR _?!6W8"~w@E|3vUD@D@DXH˕yC#@ B⛹dӢEgCpN;dƍpd_ @sQ<~?#˽_kYD@D@ h^5mR'mI/kHw'+Dϝ;ZQm;njNȭcĤK.ļ{6Jo\ HhĴ}BmLLJb$P(YBJG}l]?ej!7}"" " L R(3SsN"4U Lٞg4ҼystRjtMf76mڴ1})" " " uhE&oW"nwX$Su ?UWO?>QW,KQGG\& Z?G@ZԮ];e"K~,h֬!t ]#ejAMxAnCw^xfĈV<8C.hK'O'x 47<~ wag6XP  fy=4z_Dc """ " @@R6Z15n~L0ryUq1xL߾}mIߠAQ8Sre`׷/=1J*6[m=uE(" "9$נI&FV^$gOGiӦv\xISNX}͇~h:v2~:u{[ŀ %=_\OeT*p O~^ׯo\fMSHE@4!}ĉm;< ,0 (!Ese3GCsb-ֲ[ sիW[\Dn~7GAQhÚul߾uUh$ Z=S?~~}U%I0$7?>y5 B'SJw4]-ꭻԗb q:^! *\Mf?|;nڜ|ɦC W։?!5 1JX԰z-cn8L?NI&V&>zvQ-^8զ>5jd=XӣGΝ;[+9c-'t5jTJD@D HUID7ʰlMDO| ɴ @ qC7<"T'0m۶Æw}6=lXYG7@-kșANBwHDWv:7nlϤ ~.?̤ 퓀QXF{mJV_~u{`HQ`w(REzasuZ'9^ &F3~|!"6SJڵA6P U]vUz衇ư'  IBs3+Prp"B-\fZN8;|%"ёTrOQN9C61^k'͛[)sF{" C ¸Nk%cƌ#iܢEh9^k'4ww|yS54B/?D"*#@pQGeHx?g_3vgϞQRxIq~3!M}w͊+f8}(uW%{<-@ ]J![ZZ9ݢdV$/  d+ cp+^zM>}lʠ뎥y~ Eؔb8QJ/ ؏@'~w݄T]:@ HFDuSzl b  ޽P@~wć=DAg[^tEvYZ83jÅ))y?~uK&|(XKv[~j\@-K >>|IQ`x'o?U?=J DQy+W6;wZQ/'-[,suOa W4Qނh&#t7d6*&؞+7w{zjYD $@~"־}Z!I߂(㿓&k[d0`gse}#=vyD,A-:LSȝK%K B^~,LSpAbY%)@"n>E9zk;PB:ж" " PE< k'>Xt("nRI3pN#>}XWTgF"DݴrJgzlo*,̗-[f.,\᷌id~ @5. ht)GKs H@?>`_~y{'KPAO ʄ}VH> +6 :ѕ|g P?m裏>j_|Ŷ{[s]{=:Q,Xa2-^m1p]j%1K%5,~}½kYDt Hp4=]vM-{W?/;l:o<O>>|F<ZD m"@(Izo\q3cNȔ(-TE Jo/ڈWDg:XXُ^vB*" E@ghgs .cxt-IC$V /gݻ)6OT}"6a*7(. J`]۟͘xYƯeh0 u`]z8h#s&,nA""P$@CN_d4麍W*L< ئM2]l!<>{B[7j(3p@s[΁e2 `u循""{!1[_oD!Xi,͚5Bγ.Gyme {!l }/ODR)jk/{g rN2%4Jxq{1:u2twGTR2X_?+saٳg[W+=1 /820auu6>u{L΅Fָ0l+J@4+?zhs 7~HiHf%n_u! &o>vmVpr3M34>q z҅~-d$ܼ@PH?U@ |M{0}[/lS\x N '|GOT9*s& L܋ƹ 9\,/,}RZdm}MSge,#B3bYEhWt;sm#8! ۯˬe2v-צ/fH27? -bJzgDR216W^yZ rαVCnBNJ[O.?@1c}ad^xb!ȋ+$wy\s>K%hsB5ۺR=ډ\Gֹ0 AO=[mp?HUvYgc|;%7ޜuyQ/\h~Ww_ e>홰t9uMS1K@4k!y Rxϟ?D1'zn̙3(~nVv >|I&{fP \X@3Ţ駟yKyM )R]"!A+uƍ3oFbM,-Xx>V$FΆL 2:"^y)dª:7?] L,떽ݶòZN2g Z}m넭#pxnٻuznq޹j`wnr_huJRx{eG8餓ZIۺta ;8{a/2jM L>]-y+D焴697$!>Hp~Qp466h k̺C+)y"I9oXXc_lb&#|'H9ŲXwY;%jێ}f؉v߹}u$[usbX2͹oεn]"yQ0<_&݁gb-!\Y~}uCAmD"ɭ Bfty#B~isUWnے{P}|%6]c9Ċ(W"y{[n]sȑ#ܹs" Ku]g>#Vp 9o$e%UQoxv6:tѣcǎ6Qy1YDp8-GD%}]X&g9um2<07qu2;pyI̝0E"X)sOlC4X(~!8W['lw%؉ .GzL,6nԃU*U[On}{H4q̙cS@ѽ?K'/-D3|m`Ln@yg[ys/-`m 7A~7`@0{aIJ1R[эk,yEr (EAτgm`I=Ƌ?Ax ,Uܤ\pb !tcm7vCν2w g ۰}(h9Ú,KfB->dpaX2vT=Ï>\` Vkƾ ; $/\-{𝼐`]vYlnwu}h׮&q!UV6-BϦ7"" " <Wc KDw= :m׿V@pq1ܑ B,gnf~oGC7&{hX77k*aJ)1'X4p/W!!T JvX=N;9Q,t%6`aP#UW,N>+/a>;M0[Go)VU&\\P DgaEKX>Xuqr=䣎:*1q%\\|:L}&1{KgwA.#@18?dz>W *@ !8O>$#(%dSO ;0s[1Yge5it՞oxh]a!C+W Rܭtݱu3^k};"Wx8}e(\J*Q4p1 *Rq1rnC}dLyҌՑ!粐QxCk(AK*" " "% ꥡ("Re;6A2t!awAw}(K/e,ʋoi۶m$,_[;WMw@@TPqO @smt'ǨXdklP~D=ND:gF_:S 4r2cM:~NfԚ*" " " @(ɧuQd*(q*% fnt{)wqڦΌZ ;(NS;Cb 2(M% ҽ9 t"@)__|栃W3-Z0D3hW\aG]%tcM$#=!^H #H |@@-k3zG+DPbs9A\IAP̈#"'@တt#aŏX ɖED39? .z@V|fu,HBKRGXOu S:Clrv"Fu]vSTۘi \{tv|y WV(~+kPtgzNɷhq~ Rt "< cRe' Z8g!I]A<8Mf͛js}21[nfVvZC># Hz (ނJ*vO̰'𩧞2wjժ6%H$EW6_vԩS';gÆ 5d( Zש ZW_Y_x4<tzf+Cv{ Xz#+VhG{ }YӵkWhѢRǣ%*;6~eI &Mz>MΝm~+ r P >o޴iZYyQR{5|u u]g_=jȔh:n~DoAzjޤIӮ]UO4X)g̘v}P gؿ3|U=9 GǎM޽M֭m:uꘛo9h!" " thiۤ"'q,_ܬX"LYD+ⱅxrG4Bj&$o۶ 4u C`>s[ TD@D@A@4ס[?hF9$%wyF8Rtw ".N; oF3B&PX6k{ 7(VU! ꇒII߷ Ѕ ~̙c Ao$=Gh!?TPS'f.]X! 6,/?jȈhFشS,2kժex{ PoF Ƨy #<ҐvgIK^{̞=;*oIJMҁhȰ>or" K@t}gNyƍש _'>| ڵkf!]7t)_%|G *Gz6X3~ fefmC~N:NJ-$ Z-jM+Li,~?IO2#~ ӧ 5jƝiӦsҩZ,mV0z-OG.FoI7ݟKq.3;y;V'eSn3L5ItP\s9 yJU Xߙ>"l."ժU9^~p " ";c]GBd5wq_:?!ڤSo?3rH3eӢEtvz}׊ξ}ڮ\uQ6UT֕efۨyD).'t矶Ep{`-WHL@41}#\nDG[3B}iܸq9믿޼KfРA믿1T)+Wy,=34,O<7tc)c=LժUw#" "$@KȊ/l QıJL1LOIbĥX76kYN J<}+aOuN~L.g-?(R,zVRŊ)b.~e%uT D@G@4 ͡H4zN;=X<}cgonĞHx'0do+^An*nѢE_~1eRg?0I7l\`^Ș0w\3o<8% >yeҧ >ЁC"uDˊZz+  by8'|ZlxX+P?AX?)5|~idɒw}#C2~K;֕QVRNj22F@cEl2 (K;ԭ[׊^\B-@ HF@xX6̜xq[tR5'U_&ljӄcexEva {m.b_bh n1C=d ;@X$3ĩ˟@ ؂D b++Xn/^ o_~98۳/TTϋfխG@a-V+vs9'a;XNR$o&-?~| =vy^x|>U7D!]9{ ]W͌SLd{#\,X`Lt32aD1V\paº4Yv ݪ_D4k[4oͤ8aԜ|u#9!VB.X)n&6&hD3gԩS'k,X`>>x {V ?A}衇K_@ 8Kf+dznrV{XB-0v"`*@̀`uJj2 t(:Л4ian 71ikoֿ)7x; R_4f2]w'g gT#( ,Axۭ,#c["K.瞳c=6j~v{@Ig ,Q,eq3!^,[OR`ZVXX۵kgO24k̎ +qeCbM?z|Ɉ13=NsjV\!&MWW _Ш PwcnXB2tu--_ dΝ@ɩGFaq ?p*?N:ّoErL'@4\'UHrM=}5ck4.$Gt"0AX9>.nty%޽+":gŌz)=Ĩ2xu,ug'(щ[L]gwYw=ˮ8AG݄_ez37މ9}ܻ~ . m*e s@$[u->yd[vĜy晶ۙ+ހڶmk93dpzrg(;hn{tss;!\aHHgm-Qn=oft] 6nVF޲-:Nvn+1Q)TC9a}XMBbrQxw~z0D@i[oe\,XּÏy7F\:|&Bh~5\pAބL}D@DT kD4=P 4ȺJ Ahcp sjHz(d:'+*?9!^5/_B سBbU'1^0t|/Ck&{k+k*OndLT"7LhbV ;# iRG~(i_p'JX@YeySHN7i(~%aC5_ |v2<.rӪU+ߣGb95@$@CZU' ]𩬤f͚YaLJ8!G>[FK馛LΝ&N爪UD@DP $W zVjw^,ݏ$U*?ѠOQL6mN8ZA{9hZoQ/ P/ -gL|a_}Uʑf[Fu!It_]%\￿裏lz&~*" " "H:gE$0ZPY5&qvhNYA@ p5#kx6V˖-ͻ`JD@D HՋPId{:u O3i%Y|ٶm[;/E-/\-HM n_TD@D@hq^לVZ)GLv+kϚ5+J!biɒ%B," " HI&~r ,$ <31rU' C^|vXԦMQF`AD@D $@#yY Q q9c _/X O?F2`xk/[n1UT#';4" ZZ;>}Xnݤ3>a7 +(-S,?3PcwqO>FАz  ^Bkv@J%@]|N:M7`UG}ٳ=(iB"" "P$@~'N+WNz,(#_c /,\<_s۽{wsi7x`f˗:@H|w„ fOyFXb ÐQ)m2>0JJtA殻2{キbMݻKNVD@Jh\_5g6;C#`Jmڴ dFJ9$@&,wyiԨꪫЮ7|YhQ[CM@4lE^?z<Ө PޱcGSO=e\AxwyVD|ڵ8CED@D H57TT d YlYbE$-gqg?oy Yge>sᇛz瞦I&{~CtH HA;v~Ǘ`D ޵HΝ;:o57؎^u.|wKz[!ov#1OjeUY@gV{ZW0*5CWbY":1`:slD~lH+B~mC??:a}饗#i.l,K,1\s=H_ O?Y1+]\O#/֭[v-d$@}a*r!@. JHƏo'UX )QOu|N'xV7 7/xp iON6r-F%`?l.OhM7ԜtI'WED@D <-ꚇ f#" R0mVE^zwv{?p. (F{K)J*[nB)vm7kWo43f}@m |TD@D@%.`yLm$gċ.(90yRn[h^C\ȓnw&;w\s=]vh!ݻwVW^yeAGg–}HYElDՓ偮ۛ=أ$xy K4_ٝ #E`f͚jWuI6"tc'I+r__Qkժe믿ήE-/vu @ Z^c Еπ*(5a5"8oasH.ڲeK_F@+' <f1RQXJYgb.2B "z5kf}ʤVBܒX=XuI*@lռBI/bm|V׊ jsR|r>#8n8t%Ϲ2)He:A-ٔӧۮ௾ʎNOvA8tR>bb$/Q6m؄aY*" " (OK b?ߔBO\6`5|'MmN;*mR֣G6׷&kjz!-Tíz-ejNK(>X)\<ЊSƭ/vu" P|0Kv0_/XLkbhҤIgDDGvLڵ&GaG ]i+>ް OF_X%kƌ6ŋz~ԢE 뮻ZByQS|_Z<]𲀖o*ep^zz9, >| \K~mbyʕʕ+͏?h A.tӅKwo0m=묳l7kf:vڱb!/L(+)R`IimV-@|FV6a#"  h1WGn|_N`J*RZ 7`%}衇ڠ?`E¾3U,u%2d A8dN$L)QjW22{lQ8wD~70!Haw",;1K=NJ#u" A hG0Uoz!٢Eů0)L#3g!XsO;8B)(D]A8D}ORZ H jO$1|r;o/7|GwLgp]Xmp֨QNX`"RxOT~*" " PyǍ.^y饗l'q3ǒx$g'Huo~ o$'pB%t2<߂ER(~m H3 dM6C`b!Ă?SёM"> HcD3$6cǎVFmgĺmA0z)Y&^0Nr"x"N  1<2" !Y +¾C2p#d4 GA #. 74\rꪫlbrlO V0(݂tNװT ݣtH/!.Ҡ޲짟~ 6)dR2gB>Rj;N^n=oAr% k/b'^W낔ubIS+#o1֨xI" %Iƍ%)IwS2oO,\$&HQw}6_##Ȝr)*V 5Ƀ K^:J7^ EÀ R^HN=Tsnk'Bz;G~0QRzd2{j卹[ol/&н8ށ+[E"96۸9nb;79J^}t޽{ۇ۱ݸDraHaDZ]/ѭZQaHC<7L!V7 b_ŝxFA0B.GyiWӽ{wkMh'E`r\?Q>lCbkڧ L0(։͛ۇ@Ϟ=/6{Zyxᇭ./u,a {I΃ў(c<A1^X7p>.swn,#7^HkeX Y$^%@]]WKnė+t # sc7 aQUc%mԨ?;M"g;m4j([=(f,~B]F37/&u2$4RuQV lÏ)z!e0(A'a"'a:s:쳭 ⓗ 4C,K#<]Μ9aӠ"NSYj{;OĆs1`NO&3= 79,3wnk8̽V`xMG,޹{{N\)8wJn BFԡ>3m/cbF >3e|>S3yd+pL!M!b ؈Ũ=ͳVw% _TԸB;GfͲ| С;Ƣ+@m.\h86]ު)GA .]->iK! . P ns=殻2XVyy*B= l7(f·4ޟYR&'HgD\v;km9ϗ̙(L;!*t8.b1|x+ֽ݉YP^+[:l#v}.K ?G `fll҄Pyyۢ`Au#X"Ak".t+N, zᄊ˟}1O7?J-1!@ p!:6b|&P˝?-0{O>mMKޖ)!vj>X|rc pc$Ǐ5=vd[/@L (Ƣ\4pc¯g#=uԱۖZyM% OZw˙閏hlۻMfO< Ю]z,S KFWh#BsO[+rumx+LTaI'dGɴn(h޶fZWZM{X&5 t, Lxy)‹$'{+sP[E 8k[!_ř{+b+ԝumXv9d9^q۹}q< 9.v eFCsc:ۈOtSc5ץћW^I4&+t#^oߤOTр nCPHލ>uOJmI]:$ 4?\r0MowRD cs p/B[?+Z?Ǹ<o/@A2HkBC+.q^6 R EZn]kauzCZSM"JPFL*1qbLe"Ƅ13J "tJȵDfywͻ}Ne?{}g?Zߵ>__3b#>0 C ^r%#d6̟XAYU?^^f,W1) ɐ;t(X3? ,XOŰEjyǹsk^9f|Dڅ 'vWvy0k#Țx|Y2bf#3Ī9+E4$鼼4YYONry').E ?SNV1>IMDN~ᣬ8 IX7p}r%qӹ%}>/o t 0N&__"] fniaE߸hb*:Zծb(';Ί!CT"063 U59d5)"`r{E&;D(VB'!G"v_|x凌a? 0'z%:$,3̂Wʝ:HZd3xO<t| D7&JB}B)" "? ֦?kؕW.ˠf[-/oF@aƏw"!x&ai:XmD^ r!!жɬ" " %K- )w?Qe!:LW11k9Z;i$Ŀ^tEvI^zY " " (y hċ$sU׎ b!wAdpAT \\$f]r$BE:w.01hÇ+XeLID@D@$@4>gLA8sƌY{X? [N3ճq P>ݒpOX&+# H5Pbjʈ˖-VOb>a=mѣG"50QحINeСCli@}@uKidf;Ý@.>]!8&b`׊7 e$VJRH:qTQNVFA,QWd-!>֭Et'5~z]U”D@D@D@T@hČsV;K2{ (nŚIJ ! dΝ'"i߾}jO*&" "  X0IX Hq PD/˨5\^ةm֌5Zzxj\lSJ]~vϏ74=9餓LV"k[ n߾Gk-!NB֭[z(TD@D@"$ !R*?78d蝡FV)뮻Z+0+(믿H,-[>͛77g}]j4uM$@UJD@ hw`R`[?*1!\V b tH$3+;CD@D@hqol{Mڵ52~fʔ)cǎQX]v5,֭[X ǵM ϳ0UlX'|Ej֮][SE@DH[&!>|̡j!B {C'C,LT p3 h.]̄ lBo/" N@{0g" ΍:61-%p '&`s$2i鮻VQ =\ӠAӧOװ@:c`A۶my^x"v#oLO_|v{N: eXE֯_oX6G" zߣG{U,:JD@D*d - cY>dq+.\h}kϰې qVnjSwEl2,Ҥ?Ougzꙮ];I2OD@ hAw_~+?}ts5jDZ˗uߙuK/N:X Jvv |W&MX>~/rPD@D v#/+vȖI,Q XT+-EݖߪU+;{|ƌ[ڽ{w3dsw[Q&4a)eK̙3GїEvDJ@4R[ɓwK}QFer)6kcUo tQGYKsUW&46w_&1%FD@@4P_W|v+WK,v! g{%ӳ%~~{fr Z=# ?qlRv@U h|U){뭷'9ŋ=sd:A;̚5NID ̉yƤw}p}⎂5_hMJL<41KID@D`[;nD{*!@]vtAXޘiriq}a A*}OqƆY,dTQ̍7hL`(ص:u" ߣ;0D&Ì;oٸqR%94guy <\C}774+V0]i֭ jsxq_f͘kӉ@~ H}ʔ)?+#f"G=)vrͰaԩSM޽ 9d#!H+ U8qAȏ%Bx蛿 .IDATըQ#5l՛"! Z$W3?Q'|ڴi#[Ԡ+)'xyG3êXֿܿuՙx'X)7ovY+@ѽ+-P|Y ZVAD@J@4=z=s6$N3ɣ[nQJ #>̊1b:A*t%b '6lڴɾi[DYB)X_[haGA Ω$h_E!5R˖-m',`i%ᣊ)&^Y@N(@$@_iݺuk{gmt 0Ç7qL,x衇OԾy<;wկ~/ [n!6zO=T"̐w>aG'PeH-;co{=LÆ 6+xM6:uPg‚g" "* j\tG j$&^yֺP,V"V#˭AN O:館OH_P&;ĪXD <4dkiA8}7̚5k("%a1_D*R~J" "+ j۷ B8왩*)< /DO@_z%{DEcⓛ2Px;|5e,/Oweyl?VOF۲=*"XvfrN;A~I:2*ݻwP2?rHMf tBƃGEaqr3yeR j %/#I_M21XoaD1-]$6{6pv"LnC#vz!MG\*I% ZŞJGʟ~_'N͆;X @Mw;O>%bՔ_޼Mzw}3>L^`_T}.yz0h2+~-~ޟٜ05k6p#X; qO2̃<hv`"Q'9uU$0C߄_ܹ].*BX=]3" 4Eb={|!e"H./2|~֯;o7\XnqY?V0sډj&BIDb!1 2 KsYi')τnsN'e4%s95؉b2#/KRڐh5&!P.(?{>Qqfنw.W_}l5k]]R"ǏonfEݡCJDD.>~vID.ࠉ/C+V0ԟ7oR"rP* j_ ڣS<#s{ҥK2*ZGI,"T%f9s|ͱVT4K\49ܗ lXi3Չf9z_Sz^p\pp 3Hy.K.ND bK9cLq%4%B%-%.FXcǎzKʵ͝W\y[Ϫ[Y{Px T;^De1)z =\\j(.Z A6oH[1$+AxZp,NgH|6}奠yHE ԟ_ʊ'`QxUy_ %RXW*< +X?xsF5É<`Em2 M2$K): > hnF9M FW D4() LIU8Yc֮]k9kyASgذa6DӘ1cV;U',XDxXv?*W$sdR!U0T'>𦥅Y+Urx 뛂 R`pOg$%(hYT/v??kTaFboP U@$˗0 5]D@D<B8)?)B3AH-ö8ZVA@4 *#'|9Zr˜!ILЏ>ˆJTU+~̘f蝐RJCPL ]tQ4L-șhU@f̘aC<{LVAqM:FD@D_]r:._b>8TH{v~Ĉ[nI^U# 5=脥Hלּ H7rnZ'" "P! Q`}>QKgi+0`:M7ݔ " " y# 7:13Νkz)%B ÇVV;v+W3E@DO@OCc%@O֥=Cb=N_ שSnj?ެ^L<.Z" " q$8i\~Wiܸq~}(~se$|oZ(" "& F7qHRf'􄝫e˖v(nݺX6mJX-U(HFAUeVJ`ѢEv W3ԭ[ 4pf„ %% Z}a] TX{fȑf6{Ϝ9" "P$@sܴS6mژ<$SukРo~c-_~G1~mUѹD@D@"& 1`-UV^{ʹo~jOhժM BO>I&B'<b Z X`m2e~AT`5Wu$Ю];3tPsۡ!CFYA:k,wYKD@D D  F!֭[ۀPR&ЬY3 g}n\CQE>l%%! hUQ6/ߋ7mnN:$s뭷k4mvmycǎ⭔&" "P-¦K`ĉvaV"t\QiѢ:f襗^jWT:S nK/" %!MQN&%ȕ;h:(}_4/6mmL޽m.]=)$臒믿n.]j8−h/OGa:wlqg6lhg͛Kv&" eL,/VZCx)i쳏a;3oaϟoz!Ð=F?S̱kvaqz#" "ޒwZ.%swy0ad=Hl}t.~ +… D8&,vz,T~Sa˖-\ Qxs>֭~:Ed܆3 )Zݫp-ܾNN*7nٺu!$X&-'M>ԬYs1OIMJ" " U# hx)w~ (C+""%5tѢEv[j-J>} ۗBz9f(OqX@دsrHL-[?ƌc:t`7$Gt~G_~I"Kv%&7!D׬YcI&-Lܹݘtw;o#%>u'|b"Bi˜0"qI@q|}W]u_暝C 1 J3g }i;,",366nh}IdìZ!e!bb6{⟾[Vdr^ʕ+\XNӴiSuwu㾈o #(Lm/a)ZZ֭kT'eRC1C0y`iDek%%L_jذ]׽˾ǚ)/^@jР w T=]~>L=`E{\A,"we nW:RCxޯ! [#62QK$DQ+"aDS A|͕Y\e2eMBjZcwhZNBfqu s"(X;"R&]:Ə;/>|~aU'ѥ&M؇ o?נ"/FL>G1w0`&6}tӵkW[Kn. 82ݳgO;S/70nq&n.\\{Fu. xHN Mz4 VIΊu&J}/™(BH[yϬ:P'?"cǺq"^beFQO܆( .aEY3S\ `X/؉c1封 ubAWlب/Z#$ 'sKԇao j-2^,j0jԨmO=Xk9¤^zs&rwhve CqOn~X>-[f/A,FpyKvbxYzp%N^(i.]0¢˖Y=guF{1?q=~?|F[Z>&3#l[D'm !FaOوSoKxU%X##kc"Zĺ7vH<͝;4 <w^~v+//H暌|z/"  G=<'{?.e%=gD"k7“(7~?6~܆dߖ~h|{q}{NүÒSXpE`Ï_z `IH|"=0#\^(a%3t?tP3a+:qQ^ID@D y5y53V!Nf۰Ξ= i!DZ ;WHfl,+/Bģs#@ W](n* D'>\}6d >(e[U+[?s _:ˮRq" " WVA^,hp5`Â$: 7CD!2ho\ cXԈSNb¦'d9 gX٘cXՅ$@Ѓ;Bz,]J ,_9~)u,VS*ΰ2 +09I4=Cph.iӦ/n;|M\. YndsA.?|ɹh>$D&e &e|eׅQr˂XjٜU>- p N< 8pn=VSա1q"8?#F/sm٠0&qdEE5 w1ɓ'5RpCK\OXcvT* oGiZ}w6)Cq C*w]oO|R:^>6g  A12ܵkZsz!w?V pc΃ "T #GkkBH+s?dV-*0{k;f:@4^;rPD@D@D@J,Ywm" />g"'Jv"9 M 辞nBϹs*4**WD@D@D@DL/vDE@4**WD@D@D@DLebNU*,ZL<C@P/lV^~Xsմ}] SXyp)oޜV8Rޭw/H|РA)Op;TƍS2)OϛdxS"s9g2_xm׮]cǎ)o%Ԅ 2ɣk Y?y{=cƌm>Ƚ?Hm*U"3Ϥ=Jy٤"R+VH rD g H5k,-eZlY ۑ;vleh x˱%YSw}w[-csg~饗R^Xܹs.nR;wN.K*<&T'NL5hl-ї,_Ws92YOR*Wv_<[;~oya};B`ᶏl裏Y&}*.n/H]i|ysAYwa6[r' %%A~'} H*IHگ_xO׻oX+OI!M65/6}]omׯomڴI53UVN;V&?}{k,+(;<3b4'˲k {+z_wo<0>Ƚ?H0ڣ2'0|sI'nݺ#H?ƽJI~gzWM{7)>b=د"MTba1={Gzf<"'$ _N>׿AB9I yBi NjH%K.6$kC<8}gT7Ha居O;g>o iôz뭩O?45iҤTS I LMlHyVєju +O@M&NԩSgLx65IT7;0La}ɩ%~7Q.0A#Sy<#a}IQ~*#Z|זZ$" " " &IHUND@D@D@hZ$" " " & QD@D@D@DH_E" " " "hUND@D@D@hZ$" " " & QD@D@D@DH_E" " " "hUND@D@D@hZ$" " " & QD@D@D@D 8כ?f͚5CN <@)E@D@D@D +W4?ͼy(>2%@CCD@D@D@D 4ib dڶmߊTr)/UGJ$" " " 'ꫯ^z^{ >yG?9r裏2 8p5jyM駟n.]j-[f˘3gNFG}\pM66SL1_uFM6tٴjʜqf…y I#" " " 9ԩS~3s=z#8Šў={ҟyۼ֭3=i'|bxwy>s'+VSN90QwvezwmfvqG!ܩS'Cl2$" " " "_O=n Wu͛7ק~ӟd?/^8Uf͔gL}/oz7>ςj\.y7uQG֭OP^e O˫$ș@~'B0 %'Lso~_۷;Sە7?˾aÆfv34i9퐿+`v0w̌3P^ "" " " " :hr<@3~xe;N[Mfߎ%K4k̷ǞzvߢEo?##^ڿ;9#T" " " "]ve¼rwߙ[{M֪U+GƍveNjٲ6s! =+" " " !x)qڵf}`ӦMƍM:u/watbo?]w5cƌI vs3@%x <e0ݛ\d}43!H,Oƍ3C 1>Ϟ=0ޟܹss~/ hUGyo ԿӰaC'!n;j7,BӟyJnԫWφv=z۷9c|'" " " 1@,byf=rMjԨaf͚e.RsUWoƖ;vlzHtlQ2q,a'-6Q'" " "  o&L`W+"8= 3.2) Imports: ggplot2 (>= 3.4.0), grid (>= 3.0.0), scales (>= 0.4.1), withr (>= 2.1.1) License: GPL-2 | file LICENSE LazyData: true Suggests: covr, dplyr, patchwork, ggplot2movies, forcats, knitr, rmarkdown, testthat, vdiffr VignetteBuilder: knitr Collate: 'data.R' 'ggridges.R' 'geoms.R' 'geomsv.R' 'geoms-gradient.R' 'geom-density-line.R' 'position.R' 'scale-cyclical.R' 'scale-point.R' 'scale-vline.R' 'stats.R' 'theme.R' 'utils_ggplot2.R' 'utils.R' RoxygenNote: 7.2.3 Encoding: UTF-8 NeedsCompilation: no Packaged: 2024-01-23 01:28:20 UTC; clauswilke Author: Claus O. Wilke [aut, cre] () Maintainer: Claus O. Wilke Repository: CRAN Date/Publication: 2024-01-23 05:40:10 UTC ggridges/build/0000755000176200001440000000000014553613063013152 5ustar liggesusersggridges/build/vignette.rds0000644000176200001440000000036414553613063015514 0ustar liggesusersu0E_q/+Ƹ1ƅlR)5/Gh%qܙ^\\H%ȇ{`Ω,SJ4 X b"-YI(0I$# C\?3B%άYS)lalz`n;p=5:2NMÙpNgw-BBm/xFf qn8&X(.ggridges/tests/0000755000176200001440000000000014536504206013214 5ustar liggesusersggridges/tests/testthat/0000755000176200001440000000000014553650472015062 5ustar liggesusersggridges/tests/testthat/test_stat_density_ridges.R0000644000176200001440000001147614536504206022316 0ustar liggesuserscontext("stat_density_ridges") test_that("no ecdf or quantiles by default", { df <- data.frame(x = rnorm(20)) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges()) expect_false("ecdf" %in% names(out)) expect_false("quantile" %in% names(out)) }) test_that("from and to arguments work", { df <- data.frame(x = rnorm(20)) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges(from = -2, to = 2)) expect_equal(-2, min(out$x)) expect_equal(2, max(out$x)) }) test_that("calculation of ecdf and quantiles can be turned on", { df <- data.frame(x = rnorm(20)) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges(calc_ecdf = TRUE, quantiles = 5)) expect_true("ecdf" %in% names(out)) expect_true("quantile" %in% names(out)) expect_length(unique(out$quantile), 5) # either calc_ecdf = TRUE or quantile_lines = TRUE switches on quantile lines out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges(quantile_lines = TRUE, quantiles = 5)) expect_true("ecdf" %in% names(out)) expect_true("quantile" %in% names(out)) expect_length(unique(out$quantile), 5) }) test_that("jittered points and quantile lines can be turned on and off", { df <- data.frame(x = rnorm(20)) # no point or vline data type by default out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges()) expect_equal(unique(out$datatype), "ridgeline") # data points can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges(jittered_points = TRUE)) expect_setequal(out$datatype, c("ridgeline", "point")) expect_equal(out$x[out$datatype=="point"], df$x) # quantile lines can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges(quantile_lines = TRUE)) expect_setequal(out$datatype, c("ridgeline", "vline")) expect_equal(out$x[out$datatype=="vline"], unname(quantile(df$x)[2:4])) # quantile lines and data points can be turned on at once out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_density_ridges(jittered_points = TRUE, quantile_lines = TRUE)) expect_setequal(out$datatype, c("ridgeline", "vline", "point")) expect_equal(out$x[out$datatype=="point"], df$x) expect_equal(out$x[out$datatype=="vline"], unname(quantile(df$x)[2:4])) ## now repeat everything with geom_density_ridges and geom_density_ridges_gradient # no points or vlines data type by default out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges()) expect_equal(unique(out$datatype), "ridgeline") # data points can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges(jittered_points = TRUE)) expect_setequal(out$datatype, c("ridgeline", "point")) expect_equal(out$x[out$datatype=="point"], df$x) # quantile lines can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges(quantile_lines = TRUE)) expect_setequal(out$datatype, c("ridgeline", "vline")) expect_equal(out$x[out$datatype=="vline"], unname(quantile(df$x)[2:4])) # quantile lines and data points can be turned on at once out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges_gradient(jittered_points = TRUE, quantile_lines = TRUE)) expect_setequal(out$datatype, c("ridgeline", "vline", "point")) expect_equal(out$x[out$datatype=="point"], df$x) expect_equal(out$x[out$datatype=="vline"], unname(quantile(df$x)[2:4])) # no points or vlines data type by default out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges_gradient()) expect_equal(unique(out$datatype), "ridgeline") # data points can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges_gradient(jittered_points = TRUE)) expect_setequal(out$datatype, c("ridgeline", "point")) expect_equal(out$x[out$datatype=="point"], df$x) # quantile lines can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges_gradient(quantile_lines = TRUE)) expect_setequal(out$datatype, c("ridgeline", "vline")) expect_equal(out$x[out$datatype=="vline"], unname(quantile(df$x)[2:4])) # quantile lines and data points can be turned on at once out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges_gradient(jittered_points = TRUE, quantile_lines = TRUE)) expect_setequal(out$datatype, c("ridgeline", "vline", "point")) expect_equal(out$x[out$datatype=="point"], df$x) expect_equal(out$x[out$datatype=="vline"], unname(quantile(df$x)[2:4])) }) test_that("alternative quantile function can be provided", { df <- data.frame(x = rnorm(20)) # quantile lines can be turned on out <- layer_data(ggplot(df, aes(x = x, y = 0)) + geom_density_ridges(quantile_lines = TRUE, quantile_fun = mean)) expect_setequal(out$datatype, c("ridgeline", "vline")) expect_equal(out$x[out$datatype=="vline"], mean(df$x)) }) ggridges/tests/testthat/helper-vdiffr.R0000644000176200001440000000025214536504206017733 0ustar liggesusersexpect_doppelganger <- function(title, fig, path = NULL, ...) { testthat::skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger(title, fig, path = path, ...) } ggridges/tests/testthat/test_geom_vridgeline.R0000644000176200001440000000126014536504206021374 0ustar liggesuserscontext("geom_vridgeline") # Visual tests ------------------------------------------------------------ test_that("visual appearance of geom_vridgeline", { d <- data.frame(y = rep(1:5, 3), x = c(rep(0, 5), rep(1, 5), rep(3, 5)), width = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) p <- ggplot(d, aes(x, y, width = width, group = x)) + geom_vridgeline(fill="lightblue") expect_doppelganger("geom_vridgeline basic use", p) p <- ggplot(iris, aes(x=Species, y=Sepal.Width, width = after_stat(density), fill=Species)) + geom_vridgeline(stat="ydensity", trim=FALSE, alpha = 0.85, scale = 2) expect_doppelganger("geom_vridgeline using stat ydensity", p) }) ggridges/tests/testthat/test_scale_cyclical.R0000644000176200001440000000530514553567625021210 0ustar liggesuserscontext("scale_*_cyclical") test_that("basic tests", { df <- data.frame(x=sample(1:26), y=sample(1:26), letters) p <- ggplot(df, aes(x, y, label=letters, color=letters)) + geom_text() + scale_color_cyclical(values = c("#F00000", "#0000F0")) d <- layer_data(p) # make sure color pattern repeats as expected expect_equal(d$colour, rep(c("#F00000", "#0000F0"), 13)) # make sure there is no legend being generated # in ggplot2 >= 3.5.0 legend structure in gtable changed if ("get_guide_data" %in% getNamespaceExports("ggplot2")) { expect_null(get_guide_data(p, "colour")) } else { expect_equal("guide-box" %in% ggplotGrob(p)$layout$name, FALSE) } # once again, different aesthetic, different cyclical pattern, now with legend p <- ggplot(df, aes(x, y, label=letters, color=factor(x))) + geom_text() + scale_color_cyclical(values = c("#F00000", "#0000F0", "#F0F000"), guide = "legend") d <- layer_data(p) # make sure color pattern repeats as expected expect_equal(d$colour[order(d$x)], rep(c("#F00000", "#0000F0", "#F0F000"), 9)[1:26]) # make sure there is a legend # in ggplot2 >= 3.5.0 legend structure in gtable changed if ("get_guide_data" %in% getNamespaceExports("ggplot2")) { expect_s3_class(get_guide_data(p, "colour"), "data.frame") } else { expect_equal("guide-box" %in% ggplotGrob(p)$layout$name, TRUE) } # test that breaks must match labels expect_error( ggplot(df, aes(x, y, label=letters, color=factor(x))) + geom_text() + scale_color_cyclical(values = c("#F00000", "#0000F0", "#F0F000"), breaks = c(1, 2, 3), labels = c("red", "blue")), "`breaks` and `labels` must have the same length") # test that legend is omitted if breaks are manually set to NULL, even when legend is switched on p <- ggplot(df, aes(x, y, label=letters, color=factor(x))) + geom_text() + scale_color_cyclical(values = c("#F00000", "#0000F0", "#F0F000"), breaks = NULL, guide = "legend") expect_equal("guide-box" %in% ggplotGrob(p)$layout$name, FALSE) }) # Visual tests ------------------------------------------------------------ test_that("visual appearance of scale_*_cyclical", { df <- data.frame(x=1:30, y=1:30) p <- ggplot(df, aes(x, y, fill = factor(x))) + geom_point(shape = 21, size = 3) + scale_fill_cyclical(values = c("#F00000", "#00F000", "#0000F0")) expect_doppelganger("scale_fill_cyclical red-green-blue dots, no legend", p) p <- ggplot(df, aes(x, y, color = factor(x))) + geom_point(size = 3) + scale_color_cyclical(values = c("#F00000", "#00F000", "#0000F0"), guide = "legend") expect_doppelganger("scale_fill_cyclical red-green-blue dots, with legend", p) }) ggridges/tests/testthat/test_geom_density_ridges.R0000644000176200001440000000137014536504206022262 0ustar liggesuserscontext("geom_density_ridges") # Visual tests ------------------------------------------------------------ test_that("geom_density_ridges draws correctly", { p <- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() expect_doppelganger("geom_density_ridges basic", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(rel_min_height = 0.005) expect_doppelganger("geom_density_ridges no trailing lines", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 3) expect_doppelganger("geom_density_ridges scale=3", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges2() expect_doppelganger("geom_density_ridges2 solid polygons", p) }) ggridges/tests/testthat/test_utils.R0000644000176200001440000000101514536504206017373 0ustar liggesusers context('utils') test_that('reduce works', { expect_equal(reduce(1:10, function(x, y) x + y), sum(1:10)) expect_equal(reduce(1:20, .init=1, function(x, y) x + y), sum(1:20)+1) expect_equal(reduce(as.list(1:20), function(x, y) x + y), sum(1:20)) expect_equal(reduce(c(T, F, T), function(x, y) x & y), FALSE) expect_equal(reduce(c('the', 'fish', 'was', 'delish'), paste), 'the fish was delish') expect_equal(reduce(list(), .init=0), 0) expect_error(reduce(list()), "`.x` is empty, and no `.init` supplied") }) ggridges/tests/testthat/test_theme_ridges.R0000644000176200001440000000231114536504206020672 0ustar liggesuserscontext("theme_ridges") test_that("key theme_ridges settings", { # y axis labels are vertically aligned expect_equal(theme_ridges()$axis.text.y$vjust, 0) # no minor grid expect_equal(theme_ridges()$panel.grid.minor, ggplot2::element_blank()) # major grid can be switched off expect_equal(theme_ridges(grid = FALSE)$panel.grid.major, ggplot2::element_blank()) # centered axis labels can be switched on expect_equal(theme_ridges(center_axis_labels = TRUE)$axis.title.x$hjust, 0.5) expect_equal(theme_ridges(center_axis_labels = TRUE)$axis.title.y$hjust, 0.5) }) # Visual tests ------------------------------------------------------------ test_that("theme_ridges draws correctly", { d <- data.frame(x = rep(1:5, 3), y = c(rep(0, 5), rep(1, 5), rep(3, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) p <- ggplot(d, aes(x, y, height = height, group = y)) + geom_ridgeline(fill="lightblue") expect_doppelganger("theme_ridges default", p + theme_ridges() ) expect_doppelganger("theme_ridges without grid", p + theme_ridges(grid = FALSE) ) expect_doppelganger("theme_ridges centered axis labels", p + theme_ridges(center_axis_labels = TRUE) ) }) ggridges/tests/testthat/test_geom_gradient.R0000644000176200001440000000346214536504206021047 0ustar liggesuserscontext("geom_*_gradient") # Visual tests ------------------------------------------------------------ test_that("visual appearance of gradient geoms", { df <- data.frame(x = c(1, 2, 3, 4, 5, 6), height = c(1, 2, 3, 2, 1, 2), type = c("A", "A", "B", "B", "C", "C")) p <- ggplot(df, aes(x, y = 0, height = height, group = 0, fill = type)) + geom_ridgeline_gradient() expect_doppelganger("geom_ridgeline_gradient basic fill pattern", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = after_stat(x))) + geom_density_ridges_gradient() expect_doppelganger("geom_density_ridges_gradient continuous fill", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = after_stat(ecdf))) + geom_density_ridges_gradient(calc_ecdf = TRUE) expect_doppelganger("geom_density_ridges_gradient ecdf fill", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = after_stat(quantile))) + geom_density_ridges_gradient(calc_ecdf = TRUE, quantiles = 5) expect_doppelganger("geom_density_ridges_gradient quintiles", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = after_stat(quantile))) + geom_density_ridges_gradient(calc_ecdf = TRUE, quantiles = c(0.05, 0.95)) expect_doppelganger("geom_density_ridges_gradient probability tails", p) p <- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = after_stat(quantile))) + geom_density_ridges_gradient(calc_ecdf = TRUE, quantiles = 5, quantile_lines = TRUE) expect_doppelganger("geom_density_ridges_gradient quantile lines match shading", p) set.seed(1234) p <- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = after_stat(x))) + geom_density_ridges_gradient(jittered_points = TRUE) expect_doppelganger("geom_density_ridges_gradient jittered points can be turned on", p) }) ggridges/tests/testthat/test_stat_binline.R0000644000176200001440000000147714536504206020722 0ustar liggesuserscontext("stat_binline") test_that("binning works", { df <- data.frame(x = sample(c(rep(1, 10), rep(2, 5), rep(3, 2), rep(6, 1)))) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_binline(binwidth = 1)) expect_equal(out$count, c(0, 0, 10, 10, 5, 5, 2, 2, 0, 0, 0, 0, 1, 1, 0, 0)) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_binline(binwidth = 1, pad = FALSE)) expect_equal(out$count, c(10, 10, 5, 5, 2, 2, 0, 0, 0, 0, 1, 1)) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_binline(binwidth = 1, draw_baseline = FALSE)) expect_equal(out$count, c(NA, 0, 10, 10, 5, 5, 2, 2, 0, NA, NA, 0, 1, 1, 0, NA)) out <- layer_data(ggplot(df, aes(x = x, y = 0)) + stat_binline(binwidth = 1, pad = FALSE, draw_baseline = FALSE)) expect_equal(out$count, c(10, 10, 5, 5, 2, 2, 0, NA, NA, 0, 1, 1)) }) ggridges/tests/figs/0000755000176200001440000000000014536504206014144 5ustar liggesusersggridges/tests/figs/theme-ridges/0000755000176200001440000000000014536504206016521 5ustar liggesusersggridges/tests/figs/theme-ridges/theme-ridges-default.svg0000644000176200001440000002016314536504206023243 0ustar liggesusers 0 2 4 6 8 1 2 3 4 5 x y theme_ridges default ggridges/tests/figs/theme-ridges/theme-ridges-centered-axis-labels.svg0000644000176200001440000002020114536504206025603 0ustar liggesusers 0 2 4 6 8 1 2 3 4 5 x y theme_ridges centered axis labels ggridges/tests/figs/theme-ridges/theme-ridges-without-grid.svg0000644000176200001440000001302014536504206024237 0ustar liggesusers 0 2 4 6 8 1 2 3 4 5 x y theme_ridges without grid ggridges/tests/figs/deps.txt0000644000176200001440000000010314536504206015632 0ustar liggesusers- vdiffr-svg-engine: 1.0 - vdiffr: 0.3.3 - freetypeharfbuzz: 0.2.5 ggridges/tests/figs/geom-density-ridges/0000755000176200001440000000000014536504206020023 5ustar liggesusersggridges/tests/figs/geom-density-ridges/geom-density-ridges-no-trailing-lines.svg0000644000176200001440000013326514536504206027766 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species geom_density_ridges no trailing lines ggridges/tests/figs/geom-density-ridges/geom-density-ridges-basic.svg0000644000176200001440000021062014536504206025503 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species geom_density_ridges basic ggridges/tests/figs/geom-density-ridges/geom-density-ridges-scale-3.svg0000644000176200001440000021070614536504206025656 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species geom_density_ridges scale=3 ggridges/tests/figs/geom-density-ridges/geom-density-ridges2-solid-polygons.svg0000644000176200001440000013625514536504206027501 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species geom_density_ridges2 solid polygons ggridges/tests/figs/geom-gradient/0000755000176200001440000000000014536504206016666 5ustar liggesusersggridges/tests/figs/geom-gradient/geom-ridgeline-gradient-basic-fill-pattern.svg0000644000176200001440000001654414536504206027561 0ustar liggesusers 0 1 2 3 2 4 6 x y type A B C geom_ridgeline_gradient basic fill pattern ggridges/tests/figs/geom-gradient/geom-density-ridges-gradient-continuous-fill.svg0000644000176200001440000065765514536504206030241 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species 4 5 6 7 8 x geom_density_ridges_gradient continuous fill ggridges/tests/figs/geom-gradient/geom-density-ridges-gradient-quantile-lines-match-shading.svg0000644000176200001440000023023214536504206032520 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species quantile 1 2 3 4 5 geom_density_ridges_gradient quantile lines match shading ggridges/tests/figs/geom-gradient/geom-density-ridges-gradient-ecdf-fill.svg0000644000176200001440000053230414536504206026713 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species 0.00 0.25 0.50 0.75 1.00 ecdf geom_density_ridges_gradient ecdf fill ggridges/tests/figs/geom-gradient/geom-density-ridges-gradient-jittered-points-can-be-turned-on.svg0000644000176200001440000073250014536504206033246 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species 4 5 6 7 8 x geom_density_ridges_gradient jittered points can be turned on ggridges/tests/figs/geom-gradient/geom-density-ridges-gradient-probability-tails.svg0000644000176200001440000021660214536504206030520 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species quantile 1 2 3 geom_density_ridges_gradient probability tails ggridges/tests/figs/geom-gradient/geom-density-ridges-gradient-quintiles.svg0000644000176200001440000022267214536504206027107 0ustar liggesusers setosa versicolor virginica 4 5 6 7 8 Sepal.Length Species quantile 1 2 3 4 5 geom_density_ridges_gradient quintiles ggridges/tests/figs/geom-vridgeline/0000755000176200001440000000000014536504206017221 5ustar liggesusersggridges/tests/figs/geom-vridgeline/geom-vridgeline-using-stat-ydensity.svg0000644000176200001440000021252714536504206026772 0ustar liggesusers 2 3 4 setosa versicolor virginica Species Sepal.Width Species setosa versicolor virginica geom_vridgeline using stat ydensity ggridges/tests/figs/geom-vridgeline/geom-vridgeline-basic-use.svg0000644000176200001440000001575114536504206024701 0ustar liggesusers 1 2 3 4 5 0 2 4 6 8 x y geom_vridgeline basic use ggridges/tests/figs/scale-cyclical/0000755000176200001440000000000014536504206017014 5ustar liggesusersggridges/tests/figs/scale-cyclical/scale-fill-cyclical-red-green-blue-dots-with-legend.svg0000644000176200001440000002723014536504206031304 0ustar liggesusers 0 10 20 30 0 10 20 30 x y factor(x) 1 2 3 scale_fill_cyclical red-green-blue dots, with legend ggridges/tests/figs/scale-cyclical/scale-fill-cyclical-red-green-blue-dots-no-legend.svg0000644000176200001440000002232114536504206030741 0ustar liggesusers 0 10 20 30 0 10 20 30 x y scale_fill_cyclical red-green-blue dots, no legend ggridges/tests/testthat.R0000644000176200001440000000007414536504206015200 0ustar liggesuserslibrary(testthat) library(ggridges) test_check("ggridges") ggridges/vignettes/0000755000176200001440000000000014553613063014063 5ustar liggesusersggridges/vignettes/introduction.Rmd0000644000176200001440000005004014536504206017246 0ustar liggesusers--- title: "Introduction to ggridges" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: fig_width: 6 fig_height: 4 vignette: > %\VignetteIndexEntry{Introduction to ggridges} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- Ridgeline plots are partially overlapping line plots that create the impression of a mountain range. They can be quite useful for visualizing changes in distributions over time or space. ## Geoms The **ggridges** package provides two main geoms, `geom_ridgeline` and `geom_density_ridges`. The former takes height values directly to draw ridgelines, and the latter first estimates data densities and then draws those using ridgelines. ### Ridgelines The geom `geom_ridgeline` can be used to draw lines with a filled area underneath. ```{r warning = FALSE, message = FALSE} library(ggplot2) library(ggridges) data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, 3, 4, 2)) ggplot(data, aes(x, y, height = height)) + geom_ridgeline() ``` Negative heights are allowed, but are cut off unless the `min_height` parameter is set negative as well. ```{r message = FALSE, fig.width=9, fig.height=3} library(patchwork) # for side-by-side plotting data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, -1, 3, 2)) plot_base <- ggplot(data, aes(x, y, height = height)) plot_base + geom_ridgeline() | plot_base + geom_ridgeline(min_height = -2) ``` Multiple ridgelines can be drawn at the same time. They will be ordered such that the ones drawn higher up are in the background. When drawing multiple ridgelines at once, the `group` aesthetic must be specified so that the geom knows which parts of the data belong to which ridgeline. ```{r message = FALSE} d <- data.frame( x = rep(1:5, 3), y = c(rep(0, 5), rep(1, 5), rep(2, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1) ) ggplot(d, aes(x, y, height = height, group = y)) + geom_ridgeline(fill = "lightblue") ``` It is also possible to draw ridgelines with `geom_density_ridges` if we set `stat = "identity"`. In this case, the heights are automatically scaled such that the highest ridgeline just touches the one above at `scale = 1`. ```{r message = FALSE} ggplot(d, aes(x, y, height = height, group = y)) + geom_density_ridges(stat = "identity", scale = 1) ``` ### Density ridgeline plots The geom `geom_density_ridges` calculates density estimates from the provided data and then plots those, using the ridgeline visualization. The `height` aesthetic does not need to be specified in this case. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() ``` There is also `geom_density_ridges2`, which is identical to `geom_density_ridges` except it uses closed polygons instead of ridgelines for drawing. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges2() ``` The grouping aesthetic does not need to be provided if a categorical variable is mapped onto the y axis, but it does need to be provided if the variable is numerical. ```{r message=FALSE} # modified dataset that represents species as a number iris_num <- transform(iris, Species_num = as.numeric(Species)) # does not work, causes error # ggplot(iris_num, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() # works ggplot(iris_num, aes(x = Sepal.Length, y = Species_num, group = Species_num)) + geom_density_ridges() ``` Trailing tails can be cut off using the `rel_min_height` aesthetic. This aesthetic sets a percent cutoff relative to the highest point of any of the density curves. A value of 0.01 usually works well, but you may have to modify this parameter for different datasets. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(rel_min_height = 0.01) ``` The extent to which the different densities overlap can be controlled with the `scale` parameter. A setting of `scale=1` means the tallest density curve just touches the baseline of the next higher one. Smaller values create a separation between the curves, and larger values create more overlap. ```{r message=FALSE} # scale = 0.9, not quite touching ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 0.9) # scale = 1, exactly touching ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1) # scale = 5, substantial overlap ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 5) ``` The scaling is calculated separately per panel, so if we facet-wrap by species each density curve exactly touches the next higher baseline. (This can be disabled by setting `panel_scaling = FALSE`.) ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1) + facet_wrap(~Species) ``` ### Varying fill colors along the x axis Sometimes we would like to have the area under a ridgeline not filled with a single solid color but rather with colors that vary in some form along the x axis. This effect can be achieved with the geoms `geom_ridgeline_gradient` and `geom_density_ridges_gradient`. Both geoms work just like `geom_ridgeline` and `geom_density_ridges`, except that they allow for varying fill colors. **However,** they do not allow for alpha transparency in the fill. For technical reasons, we can have changing fill colors or transparency but not both. Here is a simple example of changing fill colors with `geom_ridgeline_gradient`: ```{r message = FALSE} d <- data.frame( x = rep(1:5, 3) + c(rep(0, 5), rep(0.3, 5), rep(0.6, 5)), y = c(rep(0, 5), rep(1, 5), rep(3, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) ggplot(d, aes(x, y, height = height, group = y, fill = factor(x+y))) + geom_ridgeline_gradient() + scale_fill_viridis_d(direction = -1, guide = "none") ``` And here is an example using `geom_density_ridges_gradient`. Note that we need to map the calculated x value (`stat(x)`) onto the fill aesthetic, not the original temperature variable. This is the case because `geom_density_ridges_gradient` calls `stat_density_ridges` (described in the next section) which calculates new x values as part of its density calculation. ```{r message = FALSE} ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) + geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) + scale_fill_viridis_c(name = "Temp. [F]", option = "C") + labs(title = 'Temperatures in Lincoln NE in 2016') ``` ## Stats The ggridges package provides a stat `stat_density_ridges` that replaces `stat_density` in the context of ridgeline plots. In addition to setting up the proper `height` for `geom_density_ridges`, this stat has a number of additional features that may be useful. ### Quantile lines and coloring by quantiles or probabilities By setting the option `quantile_lines = TRUE`, we can make `stat_density_ridges` calculate the position of lines indicating quantiles. By default, three lines are drawn, corresponding to the first, second, and third quartile: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE) ``` We can change the number of quantiles by specifying it via the `quantiles` option. Note that `quantiles = 2` implies one line (the median) at the boundary between the two quantiles. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE, quantiles = 2) ``` We can also specify quantiles by cut points rather than number. E.g., we can indicate the 2.5% and 97.5% tails. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE, quantiles = c(0.025, 0.975), alpha = 0.7) ``` Using the geom `geom_density_ridges_gradient` we can also color by quantile, via the calculated `stat(quantile)` aesthetic. Note that this aesthetic is only calculated if `calc_ecdf = TRUE`. ```{r message = FALSE} ggplot(iris, aes(x=Sepal.Length, y=Species, fill = factor(stat(quantile)))) + stat_density_ridges( geom = "density_ridges_gradient", calc_ecdf = TRUE, quantiles = 4, quantile_lines = TRUE ) + scale_fill_viridis_d(name = "Quartiles") ``` We can use the same approach to highlight the tails of the distributions. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = factor(stat(quantile)))) + stat_density_ridges( geom = "density_ridges_gradient", calc_ecdf = TRUE, quantiles = c(0.025, 0.975) ) + scale_fill_manual( name = "Probability", values = c("#FF0000A0", "#A0A0A0A0", "#0000FFA0"), labels = c("(0, 0.025]", "(0.025, 0.975]", "(0.975, 1]") ) ``` Finally, when `calc_ecdf = TRUE`, we also have access to a calculated aesthetic `stat(ecdf)`, which represents the empirical cumulative density function for the distribution. This allows us to map the probabilities directly onto color. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = 0.5 - abs(0.5 - stat(ecdf)))) + stat_density_ridges(geom = "density_ridges_gradient", calc_ecdf = TRUE) + scale_fill_viridis_c(name = "Tail probability", direction = -1) ``` ### Jittering points The stat `stat_density_ridges` also provides the option to visualize the original data points from which the distributions are generated. This can be done by setting `jittered_points = TRUE`, either in `stat_density_ridges` or in `geom_density_ridges`: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(jittered_points = TRUE) ``` Where the points are shown can be controlled with position options, e.g. "raincloud" for the raincloud effect: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, position = "raincloud", alpha = 0.7, scale = 0.9 ) ``` We can also simulate a rug: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, position = position_points_jitter(width = 0.05, height = 0), point_shape = '|', point_size = 3, point_alpha = 1, alpha = 0.7, ) ``` Note that we are using `position_points_jitter()` here, not `position_jitter()`. We do this because `position_points_jitter()` knows to jitter only the points in a ridgeline plot, without touching the density lines. Styling the jittered points is a bit tricky but is possible with special scales provided by ggridges. First, there is `scale_discrete_manual()` which can be used to make arbitrary discrete scales for arbitrary aesthetics. We use it in the next example to style the point shapes. Second, there are various point aesthetic scales, such as `scale_point_color_hue()`. See the reference documentation for these scales for more details. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) + geom_density_ridges( aes(point_color = Species, point_fill = Species, point_shape = Species), alpha = .2, point_alpha = 1, jittered_points = TRUE ) + scale_point_color_hue(l = 40) + scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23)) ``` All common aesthetics for points can be applied to the jittered points. However, the aesthetic names start with `point_`. In the next example, we have mapped an additional variable onto the size of the points. ```{r message = FALSE, fig.width = 6, fig.height = 6} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) + geom_density_ridges( aes(point_shape = Species, point_fill = Species, point_size = Petal.Length), alpha = .2, point_alpha = 1, jittered_points = TRUE ) + scale_point_color_hue(l = 40) + scale_point_size_continuous(range = c(0.5, 4)) + scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23)) ``` Similarly, we have aesthetics for the vertical lines, named `vline_`. And the vertical lines can also be shifted so they are aligned with the jittered points. This allows us to generate figures such as the following: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, quantile_lines = TRUE, scale = 0.9, alpha = 0.7, vline_size = 1, vline_color = "red", point_size = 0.4, point_alpha = 1, position = position_raincloud(adjust_vlines = TRUE) ) ``` ### Using alternative stats The stat `stat_density_ridges` may not always do exactly what you want it to do. If this is the case, you can use other stats that may be better for your respective application. First, `stat_density_ridges` estimates the data range and bandwidth for the density estimation from the entire data at once, rather than from each individual group of data. This choice makes ridgeline plots look more uniform, but the density estimates can in some cases look quite different from what you would get from `geom_density` or `stat_density`. This problem can be remedied by using `stat_density` with `geom_density_ridges`. This works just fine, we just need to make sure that we map the calculated density onto the `height` aesthetic. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + geom_density_ridges(stat = "density") ``` Second, there may be scenarios in which you don't want `geom_density_ridges` to do any density estimation, for example because you have done so already yourself. In this case, you can use `stat_identity`. The benefit of using `geom_density_ridges` with `stat_identiy` over using `geom_ridgeline` directly is that `geom_density_ridges` provides automatic scaling. As an example, assume we have calculated density curves for the `Sepal.Length` column in the `iris` dataset: ```{r message=FALSE} library(dplyr) iris_densities <- iris %>% group_by(Species) %>% group_modify(~ ggplot2:::compute_density(.x$Sepal.Length, NULL)) %>% rename(Sepal.Length = x) iris_densities ``` We can plot these as follows: ```{r message=FALSE} ggplot(iris_densities, aes(x = Sepal.Length, y = Species, height = density)) + geom_density_ridges(stat = "identity") ``` Notice how this plot looks different from the one generated using `stat = "density"`, even though the density computation was exactly the same: (i) The density curves extend all the way to zero. (ii) There is no horizontal line extending all the way to the limits of the x axis. Finally, if you prefer histograms to density plots, you can also use `stat_binline`. Note that overlapping histograms can look strange, so this option is probably best used with a `scale` parameter < 1. The option `draw_baseline = FALSE` removes trailing lines to either side of the histogram. (For histograms, the `rel_min_height` parameter doesn't work very well.) ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + geom_density_ridges(stat = "binline", bins = 20, scale = 0.95, draw_baseline = FALSE) ``` ## Themes ridgeline plots tend to require some theme modifications to look good. Most importantly, the y-axis tick labels should be vertically aligned so that they are flush with the axis ticks rather than vertically centered. The ggridges package provides a theme `theme_ridges` that does this and a few other theme modifications. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + theme_ridges() ``` However, without any further modifications, there are still a few issues with this plot. First, the ridgeline for the virginica species is slightly cut off at the very top point. Second, the space between the x and y axis labels and the ridgelines is too large. We can fix both issues using the `expand` option for the axis scales. ```{r message=FALSE, warning=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expand_scale(mult = c(0.01, .7))) + theme_ridges() ``` Instead of expanding the axis, you can also turn off clipping for the plot panel. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges() ``` By default, `theme_ridges` adds a grid, but the grid can be switched off when not needed. Also, axis titles can be centered. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE, center_axis_labels = TRUE) ``` If you prefer to use a different theme than `theme_ridges`, for example `theme_minimal`, it is still advisable to adjust the alignment of the axis tick labels and the axis scales. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_minimal(base_size = 14) + theme(axis.text.y = element_text(vjust = 0)) ``` ## Cyclical scales Many ridgeline plots improve in appearance if the filled areas are drawn with alternating colors. To simplify the generation of such plots, **ggridges** provides cyclical scales. These are scales that cycle through the aesthetic values provided. For example, if we use `scale_fill_cyclical(values = c("blue", "green"))` then `ggplot` will cycle through these two fill colors throughout the plot. ```{r message=FALSE} ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical(values = c("blue", "green")) ``` By default, the cyclical scales will not draw a legend, because the legend will usually be confusing unless the labels are manually altered. Legends can be switched on via the `guide = "legend"` option, just like for all other scales. ```{r message=FALSE, fig.width = 5.5} ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical(values = c("blue", "green"), guide = "legend") ``` Legends can be modified as usual. ```{r message=FALSE, fig.width = 5.5} ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical( name = "Fill colors", values = c("blue", "green"), labels = c("Fair" = "blue", "Good" = "green"), guide = "legend" ) ``` Cyclical scales are defined for all the common aesthetics one might want to change, such as color, size, alpha, and linetype, and the legends are combined when possible ```{r message=FALSE, fig.width = 6.5} ggplot(diamonds, aes(x = price, y = cut, fill = cut, color = cut)) + geom_density_ridges(scale = 4, size = 1) + scale_fill_cyclical( name = "Color scheme", values = c("blue", "green"), guide = "legend", labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline") ) + scale_color_cyclical( name = "Color scheme", values = c("black", "yellow"), guide = "legend", labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline") ) ``` Because these cyclical scales are generic **ggplot2** scales, they work with any geom that accepts the respective aesthetic. Thus, for example, we can make histograms with alternatingly colored bars. ```{r message=FALSE, fig.width = 6.5} ggplot(mpg, aes(x = class, fill = class, color = class)) + geom_bar(size = 1.5) + scale_fill_cyclical( name = "Color scheme", values = c("blue", "green"), guide = "legend", labels = c("blue w/ black outline", "green w/ yellow outline") ) + scale_color_cyclical( name = "Color scheme", values = c("black", "yellow"), guide = "legend", labels = c("blue w/ black outline", "green w/ yellow outline") ) ``` While the previous example won't win any design awards, more subtle effects can be helpful. ```{r message=FALSE, fig.width=5.5} mpg %>% group_by(class) %>% tally() %>% arrange(desc(n)) %>% mutate(class = factor(class, levels = class)) %>% ggplot(aes(x = class, y = n, fill = class)) + geom_col() + scale_fill_cyclical(values = c("#4040B0", "#9090F0")) + scale_y_continuous(expand = c(0, 0)) + theme_minimal() ``` ggridges/vignettes/gallery.Rmd0000644000176200001440000001431614536504206016172 0ustar liggesusers--- title: "Gallery of ggridges examples" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: fig_width: 4.5 fig_height: 3 vignette: > %\VignetteIndexEntry{Gallery of ggridges examples} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- ```{r echo=FALSE, include=FALSE} library(ggplot2) library(ggridges) ``` ## Evolution of movie lengths over time Data from the IMDB, as provided in the ggplot2movies package. ```{r message=FALSE, warning=FALSE, fig.width = 6, fig.height = 6} library(ggplot2movies) ggplot(movies[movies$year>1912,], aes(x = length, y = year, group = year)) + geom_density_ridges(scale = 10, size = 0.25, rel_min_height = 0.03) + theme_ridges() + scale_x_continuous(limits = c(1, 200), expand = c(0, 0)) + scale_y_reverse( breaks = c(2000, 1980, 1960, 1940, 1920, 1900), expand = c(0, 0) ) + coord_cartesian(clip = "off") ``` ## Results from Catalan regional elections, 1980-2015 Modified after a figure originally created by Marc Belzunces (@marcbeldata on Twitter). ```{r message=FALSE, warning=FALSE, fig.width = 6, fig.height = 8} library(dplyr) library(forcats) Catalan_elections %>% mutate(YearFct = fct_rev(as.factor(Year))) %>% ggplot(aes(y = YearFct)) + geom_density_ridges( aes(x = Percent, fill = paste(YearFct, Option)), alpha = .8, color = "white", from = 0, to = 100 ) + labs( x = "Vote (%)", y = "Election Year", title = "Indy vs Unionist vote in Catalan elections", subtitle = "Analysis unit: municipalities (n = 949)", caption = "Marc Belzunces (@marcbeldata) | Source: Idescat" ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0)) + scale_fill_cyclical( breaks = c("1980 Indy", "1980 Unionist"), labels = c(`1980 Indy` = "Indy", `1980 Unionist` = "Unionist"), values = c("#ff0000", "#0000ff", "#ff8080", "#8080ff"), name = "Option", guide = "legend" ) + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE) ``` ## Temperatures in Lincoln, Nebraska Modified from a [blog post](https://austinwehrwein.com/data-visualization/it-brings-me-ggjoy/) by Austin Wehrwein. ```{r message=FALSE, warning=FALSE, fig.width = 7.5, fig.height = 5} ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) + geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01, gradient_lwd = 1.) + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) + scale_fill_viridis_c(name = "Temp. [F]", option = "C") + labs( title = 'Temperatures in Lincoln NE', subtitle = 'Mean temperatures (Fahrenheit) by month for 2016' ) + theme_ridges(font_size = 13, grid = TRUE) + theme(axis.title.y = element_blank()) ``` ## Visualization of Poisson random samples with different means Inspired by a ggridges example by Noam Ross (twitter.com/noamross/status/888405434381545472). ```{r message=FALSE, warning=FALSE, fig.width = 6, fig.height = 7} # generate data set.seed(1234) pois_data <- data.frame(mean = rep(1:5, each = 10)) pois_data$group <- factor(pois_data$mean, levels = 5:1) pois_data$value <- rpois(nrow(pois_data), pois_data$mean) # make plot ggplot(pois_data, aes(x = value, y = group, group = group)) + geom_density_ridges2(aes(fill = group), stat = "binline", binwidth = 1, scale = 0.95) + geom_text( stat = "bin", aes( y = group + 0.95*stat(count/max(count)), label = ifelse(stat(count) > 0, stat(count), "") ), vjust = 1.4, size = 3, color = "white", binwidth = 1 ) + scale_x_continuous( breaks = c(0:12), limits = c(-.5, 13), expand = c(0, 0), name = "random value" ) + scale_y_discrete( expand = expansion(add = c(0, 1.)), name = "Poisson mean", labels = c("5.0", "4.0", "3.0", "2.0", "1.0") ) + scale_fill_cyclical(values = c("#0000B0", "#7070D0")) + labs( title = "Poisson random samples with different means", subtitle = "sample size n=10" ) + guides(y = "none") + theme_ridges(grid = FALSE) + theme( axis.title.x = element_text(hjust = 0.5), axis.title.y = element_text(hjust = 0.5) ) ``` ## Height of Australian athletes ```{r message=FALSE, fig.width = 7, fig.height = 5.5} ggplot(Aus_athletes, aes(x = height, y = sport, color = sex, point_color = sex, fill = sex)) + geom_density_ridges( jittered_points = TRUE, scale = .95, rel_min_height = .01, point_shape = "|", point_size = 3, size = 0.25, position = position_points_jitter(height = 0) ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0), name = "height [cm]") + scale_fill_manual(values = c("#D55E0050", "#0072B250"), labels = c("female", "male")) + scale_color_manual(values = c("#D55E00", "#0072B2"), guide = "none") + scale_discrete_manual("point_color", values = c("#D55E00", "#0072B2"), guide = "none") + coord_cartesian(clip = "off") + guides(fill = guide_legend( override.aes = list( fill = c("#D55E00A0", "#0072B2A0"), color = NA, point_color = NA) ) ) + ggtitle("Height in Australian athletes") + theme_ridges(center = TRUE) ``` ## A cheese plot Inspired by a tweet by Leonard Kiefer (twitter.com/lenkiefer/status/932237461337575429). ```{r message=FALSE, fig.width = 6, fig.height = 5} set.seed(423) n1 <- 200 n2 <- 25 n3 <- 50 cols <- c('#F2DB2F', '#F7F19E', '#FBF186') cols_dark <- c("#D7C32F", "#DBD68C", "#DFD672") cheese <- data.frame( cheese = c(rep("buttercheese", n1), rep("Leerdammer", n2), rep("Swiss", n3)), x = c(runif(n1), runif(n2), runif(n3)), size = c( rnorm(n1, mean = .1, sd = .01), rnorm(n2, mean = 9, sd = 3), rnorm(n3, mean = 3, sd = 1) ) ) ggplot(cheese, aes(x = x, point_size = size, y = cheese, fill = cheese, color = cheese)) + geom_density_ridges( jittered_points = TRUE, point_color="white", scale = .8, rel_min_height = .2, size = 1.5 ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(limits = c(0, 1), expand = c(0, 0), name = "", breaks = NULL) + scale_point_size_continuous(range = c(0.01, 10), guide = "none") + scale_fill_manual(values = cols, guide = "none") + scale_color_manual(values = cols_dark, guide = "none") + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE, center = TRUE) ``` ggridges/R/0000755000176200001440000000000014536504206012253 5ustar liggesusersggridges/R/geoms.R0000644000176200001440000005211114536504206013510 0ustar liggesusers#' Plot a ridgeline (line with filled area underneath) #' #' Plots the sum of the `y` and `height` aesthetics versus `x`, filling the area between `y` and `y + height` with a color. #' Thus, the data mapped onto y and onto height must be in the same units. #' If you want relative scaling of the heights, you can use [`geom_density_ridges`] with `stat = "identity"`. #' #' In addition to drawing ridgelines, this geom can also draw points if they are provided as part of the dataset. #' The stat [`stat_density_ridges()`] takes advantage of this option to generate ridgeline plots with overlaid #' jittered points. #' #' @param mapping Set of aesthetic mappings created by [`aes()`] or #' [`aes_()`]. If specified and `inherit.aes = TRUE` (the #' default), it is combined with the default mapping at the top level of the #' plot. You must supply `mapping` if there is no plot mapping. #' @param data The data to be displayed in this layer. There are three #' options: #' #' If `NULL`, the default, the data is inherited from the plot #' data as specified in the call to [`ggplot()`]. #' #' A `data.frame`, or other object, will override the plot #' data. #' #' A `function` will be called with a single argument, #' the plot data. The return value must be a `data.frame.`, and #' will be used as the layer data. #' @param stat The statistical transformation to use on the data for this #' layer, as a string. #' @param position Position adjustment, either as a string, or the result of #' a call to a position adjustment function. #' @param show.legend logical. Should this layer be included in the legends? #' `NA`, the default, includes if any aesthetics are mapped. #' `FALSE` never includes, and `TRUE` always includes. #' @param inherit.aes If `FALSE`, overrides the default aesthetics, #' rather than combining with them. #' @param na.rm If `FALSE`, the default, missing values are removed with #' a warning. If `TRUE`, missing values are silently removed. #' @param ... other arguments passed on to [`layer()`]. These are #' often aesthetics, used to set an aesthetic to a fixed value, like #' `color = "red"` or `linewidth = 3`. They may also be parameters #' to the paired geom/stat. #' #' @section Aesthetics: #' #' Required aesthetics are in bold. #' #' * **`x`** #' * **`y`** #' * **`height`** Height of the ridgeline, measured from the respective `y` value. Assumed to be positive, though this is not required. #' * `group` Defines the grouping. Required when the dataset contains multiple distinct ridgelines. Will typically be the same #' variable as is mapped to `y`. #' * `scale` A scaling factor to scale the height of the ridgelines. #' A value of 1 indicates that the heights are taken as is. This aesthetic can be used to convert #' `height` units into `y` units. #' * `min_height` A height cutoff on the drawn ridgelines. All values that fall below this cutoff will be removed. #' The main purpose of this cutoff is to remove long tails right at the baseline level, but other uses are possible. #' The cutoff is applied before any height #' scaling is applied via the `scale` aesthetic. Default is 0, so negative values are removed. #' * `colour` Color of the ridgeline #' * `fill` Fill color of the area under the ridgeline #' * `alpha` Transparency level of `fill`. Not applied to `color`. If you want transparent lines, you can set their #' color as RGBA value, e.g. #FF0000A0 for partially transparent red. #' * `group` Grouping, to draw multiple ridgelines from one dataset #' * `linetype` Linetype of the ridgeline #' * `linewidth` Line thickness #' * `point_shape`, `point_colour`, `point_size`, `point_fill`, `point_alpha`, `point_stroke` Aesthetics applied #' to points drawn in addition to ridgelines. #' #' @examples #' library(ggplot2) #' #' d <- data.frame(x = rep(1:5, 3), y = c(rep(0, 5), rep(1, 5), rep(3, 5)), #' height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) #' ggplot(d, aes(x, y, height = height, group = y)) + geom_ridgeline(fill="lightblue") #' #' @importFrom ggplot2 layer #' @export geom_ridgeline <- function(mapping = NULL, data = NULL, stat = "identity", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...) { layer( data = data, mapping = mapping, stat = stat, geom = GeomRidgeline, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, ... ) ) } #' @rdname geom_ridgeline #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto Geom #' @export GeomRidgeline <- ggproto("GeomRidgeline", Geom, default_aes = aes( # ridgeline aesthetics color = "black", fill = "grey70", y = 0, linewidth = 0.5, linetype = 1, min_height = 0, scale = 1, alpha = NA, datatype = "ridgeline", # point aesthetics with default point_shape = 19, point_size = 1.5, point_stroke = 0.5, # point aesthetics, inherited point_colour = NULL,# point_color = NULL, point_fill = NULL, point_alpha = NULL, # vline aesthetics, all inherited vline_colour = NULL, #vline_color = NULL, vline_width = NULL, vline_linetype = NULL, vline_size = NULL #<- line size deprecated in ggplot2 3.4.0 ), required_aes = c("x", "y", "height"), optional_aes = c("point_color", "vline_color", "vline_width", "vline_size"), extra_params = c("na.rm", "jittered_points"), setup_data = function(self, data, params) { params <- check_vline_size_param(params) params <- check_size_param(params) if (!"scale" %in% names(data)) { if (!"scale" %in% names(params)) data <- cbind(data, scale = self$default_aes$scale) else data <- cbind(data, scale = params$scale) } if (!"min_height" %in% names(data)){ if (!"min_height" %in% names(params)) data <- cbind(data, min_height = self$default_aes$min_height) else data <- cbind(data, min_height = params$min_height) } transform(data, ymin = y, ymax = y + scale*height) }, draw_key = function(data, params, linewidth) { data <- check_vline_size(data) data <- check_size(data) lwd <- min(data$linewidth, min(linewidth) / 4) rect_grob <- grid::rectGrob( width = grid::unit(1, "npc") - grid::unit(lwd, "mm"), height = grid::unit(1, "npc") - grid::unit(lwd, "mm"), gp = grid::gpar( col = data$colour, fill = alpha(data$fill, data$alpha), lty = data$linetype, lwd = lwd * .pt, linejoin = "mitre" ) ) # if vertical lines were drawn then we need to add them to the legend also if (is.null(params$quantile_lines) || !params$quantile_lines) { vlines_grob <- grid::nullGrob() } else { vlines_grob <- grid::segmentsGrob(0.5, 0.1, 0.5, 0.9, gp = grid::gpar( col = data$vline_colour %||% data$vline_color %||% data$colour, lwd = (data$vline_width %||% data$linewidth) * .pt, lty = data$vline_linetype %||% data$linetype, lineend = "butt" ) ) } # if jittered points were drawn then we need to add them to the legend also if (is.null(params$jittered_points) || !params$jittered_points) { point_grob <- grid::nullGrob() } else { point_grob <- grid::pointsGrob(0.5, 0.5, pch = data$point_shape, gp = grid::gpar( col = alpha( data$point_colour %||% data$point_color %||% data$colour, data$point_alpha %||% data$alpha ), fill = alpha( data$point_fill %||% data$fill, data$point_alpha %||% data$alpha ), fontsize = data$point_size * .pt + data$point_stroke * .stroke / 2, lwd = data$point_stroke * .stroke / 2 ) ) } grid::grobTree(rect_grob, vlines_grob, point_grob) }, handle_na = function(data, params) { data }, draw_panel = function(self, data, panel_params, coord, ...) { groups <- split(data, factor(data$group)) # sort list so highest ymin values are in the front # we take a shortcut here and look only at the first ymin value given o <- order(unlist(lapply(groups, function(data){data$ymin[1]})), decreasing = TRUE) groups <- groups[o] grobs <- lapply(groups, function(group) { self$draw_group(group, panel_params, coord, ...) }) ggname(snake_class(self), gTree( children = do.call("gList", grobs) )) }, draw_group = function(self, data, panel_params, coord, na.rm = FALSE) { if (na.rm) data <- data[stats::complete.cases(data[c("x", "ymin", "ymax")]), ] # split data into data types (ridgeline, vline, point) data_list <- split(data, factor(data$datatype)) point_grob <- self$make_point_grob(data_list[["point"]], panel_params, coord) vline_grob <- self$make_vline_grob(data_list[["vline"]], panel_params, coord) data <- data_list[["ridgeline"]] # if the final data set is empty then we're done here if (is.null(data)) { return(grid::grobTree(vline_grob, point_grob)) } # otherwise, continue. First we order the data, in preparation for polygon drawing data <- data[order(data$group, data$x), ] # remove all points that fall below the minimum height data$ymax[data$height < data$min_height] <- NA # Check that aesthetics are constant aes <- unique(data[c("colour", "fill", "linewidth", "linetype", "alpha")]) if (nrow(aes) > 1) { stop("Aesthetics can not vary along a ridgeline") } aes <- as.list(aes) # Instead of removing NA values from the data and plotting a single # polygon, we want to "stop" plotting the polygon whenever we're # missing values and "start" a new polygon as soon as we have new # values. We do this by creating an id vector for polygonGrob that # has distinct polygon numbers for sequences of non-NA values and NA # for NA values in the original data. Example: c(NA, 2, 2, 2, NA, NA, # 4, 4, 4, NA) missing_pos <- !stats::complete.cases(data[c("x", "ymin", "ymax")]) ids <- cumsum(missing_pos) + 1 ids[missing_pos] <- NA # munching for polygon positions <- with(data, data.frame( x = c(x, rev(x)), y = c(ymax, rev(ymin)), id = c(ids, rev(ids)) )) munched_poly <- ggplot2::coord_munch(coord, positions, panel_params) # munching for line positions <- with(data, data.frame( x = x, y = ymax, id = ids )) munched_line <- ggplot2::coord_munch(coord, positions, panel_params) # calculate line and area grobs line_grob <- self$make_line_grob(munched_line, munched_poly, aes) area_grob <- self$make_area_grob(munched_poly, aes) # combine everything and return grid::grobTree(area_grob, vline_grob, line_grob, point_grob) }, make_point_grob = function(data, panel_params, coord) { if (is.null(data)) { return(grid::nullGrob()) } data$y <- data$ymin coords <- coord$transform(data, panel_params) ggname("geom_ridgeline", grid::pointsGrob( coords$x, coords$y, pch = coords$point_shape, gp = grid::gpar( col = alpha( data$point_colour %||% data$point_color %||% data$colour, data$point_alpha %||% data$alpha ), fill = alpha( data$point_fill %||% data$fill, data$point_alpha %||% data$alpha ), # Stroke is added around the outside of the point fontsize = coords$point_size * .pt + coords$point_stroke * .stroke / 2, lwd = coords$point_stroke * .stroke / 2 ) ) ) }, make_vline_grob = function(data, panel_params, coord) { if (is.null(data)) { return(grid::nullGrob()) } data <- check_vline_size(data) data <- check_size(data) data$xend <- data$x data$y <- data$ymin data$yend <- data$ymax data$alpha <- NA # copy vline aesthetics over if set data$colour <- data$vline_colour %||% data$vline_color %||% data$colour data$linetype <- data$vline_linetype %||% data$linetype data$linewidth <- data$vline_width %||% data$linewidth ggplot2::GeomSegment$draw_panel(data, panel_params, coord) }, make_line_grob = function(munched_line, munched_poly, aes) { ggname("geom_ridgeline", grid::polylineGrob( munched_line$x, munched_line$y, id = munched_line$id, default.units = "native", gp = grid::gpar( col = aes$colour, lwd = aes$linewidth * .pt, lty = aes$linetype) ) ) }, make_area_grob = function(munched_poly, aes) { ggname("geom_ridgeline", grid::polygonGrob( munched_poly$x, munched_poly$y, id = munched_poly$id, default.units = "native", gp = grid::gpar( fill = ggplot2::alpha(aes$fill, aes$alpha), lty = 0) ) ) } ) #' Create ridgeline plot #' #' `geom_density_ridges` arranges multiple density plots in a staggered fashion, as in the cover of the famous Joy Division album Unknown Pleasures. #' #' By default, this geom calculates densities from the point data mapped onto the x axis. If density calculation is #' not wanted, use `stat="identity"` or use [`geom_ridgeline`]. The difference between `geom_density_ridges` and [`geom_ridgeline`] #' is that `geom_density_ridges` will provide automatic scaling of the ridgelines (controlled by the `scale` aesthetic), whereas #' [geom_ridgeline] will plot the data as is. Note that when you set `stat="identity"`, the `height` aesthetic must #' be provided. #' #' Note that the default [`stat_density_ridges`] makes joint density estimation across all datasets. This may not generate #' the desired result when using faceted plots. As an alternative, you can set `stat = "density"` to use [`stat_density`]. #' In this case, it is required to add the aesthetic mapping `height = after_stat(density)` (see examples). #' #' @param panel_scaling If `TRUE`, the default, relative scaling is calculated separately #' for each panel. If `FALSE`, relative scaling is calculated globally. #' @inheritParams geom_ridgeline #' #' @section Aesthetics: #' #' Required aesthetics are in bold. #' #' * **`x`** #' * **`y`** #' * `group` Defines the grouping. Not needed if a categorical variable is mapped onto `y`, but needed otherwise. Will typically be the same #' variable as is mapped to `y`. #' * `height` The height of each ridgeline at the respective x value. Automatically calculated and #' provided by [`stat_density_ridges`] if the default stat is not changed. #' * `scale` A scaling factor to scale the height of the ridgelines relative to the spacing between them. #' A value of 1 indicates that the maximum point of any ridgeline touches the baseline right above, assuming #' even spacing between baselines. #' * `rel_min_height` Lines with heights below this cutoff will be removed. The cutoff is measured relative to the #' overall maximum, so `rel_min_height=0.01` would remove everything that is 1\% or less than the highest point among all #' ridgelines. Default is 0, so nothing is removed. #' alpha #' * `colour`, `fill`, `group`, `alpha`, `linetype`, `linewidth`, as in [`geom_ridgeline`]. #' * `point_shape`, `point_colour`, `point_size`, `point_fill`, `point_alpha`, `point_stroke`, as in [`geom_ridgeline`]. #' #' @importFrom ggplot2 layer #' @export #' @examples #' library(ggplot2) #' #' # set the `rel_min_height` argument to remove tails #' ggplot(iris, aes(x = Sepal.Length, y = Species)) + #' geom_density_ridges(rel_min_height = 0.005) + #' scale_y_discrete(expand = c(0.01, 0)) + #' scale_x_continuous(expand = c(0.01, 0)) + #' theme_ridges() #' #' # set the `scale` to determine how much overlap there is among the plots #' ggplot(diamonds, aes(x = price, y = cut)) + #' geom_density_ridges(scale = 4) + #' scale_y_discrete(expand = c(0.01, 0)) + #' scale_x_continuous(expand = c(0.01, 0)) + #' theme_ridges() #' #' # the same figure with colors, and using the ggplot2 density stat #' ggplot(diamonds, aes(x = price, y = cut, fill = cut, height = after_stat(density))) + #' geom_density_ridges(scale = 4, stat = "density") + #' scale_y_discrete(expand = c(0.01, 0)) + #' scale_x_continuous(expand = c(0.01, 0)) + #' scale_fill_brewer(palette = 4) + #' theme_ridges() + theme(legend.position = "none") geom_density_ridges <- function(mapping = NULL, data = NULL, stat = "density_ridges", position = "points_sina", panel_scaling = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...) { layer( data = data, mapping = mapping, stat = stat, geom = GeomDensityRidges, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, panel_scaling = panel_scaling, ... ) ) } #' @rdname geom_density_ridges #' @format NULL #' @usage NULL #' @importFrom grid gTree gList #' @export GeomDensityRidges <- ggproto("GeomDensityRidges", GeomRidgeline, default_aes = aes( # ridgeline aesthetics color = "black", fill = "grey70", linewidth = 0.5, linetype = 1, rel_min_height = 0, scale = 1.8, alpha = NA, datatype = "ridgeline", # point aesthetics with default point_shape = 19, point_size = 1.5, point_stroke = 0.5, # point aesthetics, inherited point_colour = NULL, #point_color = NULL, point_fill = NULL, point_alpha = NULL, # vline aesthetics, all inherited vline_colour = NULL, #vline_color = NULL, vline_width = NULL, vline_linetype = NULL, vline_size = NULL #<- line size deprecated in ggplot2 3.4.0 ), required_aes = c("x", "y", "height"), optional_aes = c("point_color", "vline_color", "vline_size", "vline_width"), extra_params = c("na.rm", "panel_scaling"), setup_data = function(self, data, params) { # check for size deprecation params <- check_vline_size_param(params) params <- check_size(params) # provide default for panel scaling parameter if it doesn't exist, # happens if the geom is called from a stat if (is.null(params$panel_scaling)) { params$panel_scaling <- TRUE } # calculate internal scale yrange = max(data$y) - min(data$y) n = length(unique(data$y)) if (n<2) { hmax <- max(data$height, na.rm = TRUE) iscale <- 1 } else { # scale per panel or globally? if (params$panel_scaling) { heights <- split(data$height, data$PANEL) max_heights <- vapply(heights, max, numeric(1), na.rm = TRUE) hmax <- max_heights[data$PANEL] iscale <- yrange/((n-1)*hmax) } else { hmax <- max(data$height, na.rm = TRUE) iscale <- yrange/((n-1)*hmax) } } #print(iscale) #print(hmax) data <- cbind(data, iscale) if (!"scale" %in% names(data)) { if (!"scale" %in% names(params)) data <- cbind(data, scale = self$default_aes$scale) else data <- cbind(data, scale = params$scale) } if (!"rel_min_height" %in% names(data)){ if (!"rel_min_height" %in% names(params)) data <- cbind(data, rel_min_height = self$default_aes$rel_min_height) else data <- cbind(data, rel_min_height = params$rel_min_height) } # warn for vline_size or size arg data <- check_vline_size(data) data <- check_size(data) transform(data, ymin = y, ymax = y + iscale*scale*height, min_height = hmax*rel_min_height) } ) #' `geom_density_ridges2` is identical to `geom_density_ridges` except it draws closed polygons rather than ridgelines. #' #' @rdname geom_density_ridges #' @importFrom ggplot2 layer #' @export #' @examples #' #' # use geom_density_ridges2() instead of geom_density_ridges() for solid polygons #' ggplot(iris, aes(x = Sepal.Length, y = Species)) + #' geom_density_ridges2() + #' scale_y_discrete(expand = c(0.01, 0)) + #' scale_x_continuous(expand = c(0.01, 0)) + #' theme_ridges() geom_density_ridges2 <- function(mapping = NULL, data = NULL, stat = "density_ridges", position = "points_sina", panel_scaling = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...) { layer( data = data, mapping = mapping, stat = stat, geom = GeomDensityRidges2, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, panel_scaling = panel_scaling, ... ) ) } #' @rdname geom_density_ridges #' @format NULL #' @usage NULL #' @export GeomDensityRidges2 <- ggproto("GeomDensityRidges2", GeomDensityRidges, make_line_grob = function(munched_line, munched_poly, aes) { grid::nullGrob() }, make_area_grob = function(munched_poly, aes) { ggname("geom_density_ridges2", grid::polygonGrob( munched_poly$x, munched_poly$y, id = munched_poly$id, default.units = "native", gp = grid::gpar( fill = ggplot2::alpha(aes$fill, aes$alpha), col = aes$colour, lwd = aes$linewidth * .pt, lty = aes$linetype) )) } ) ggridges/R/utils.R0000644000176200001440000000414214536504206013537 0ustar liggesusers #' Reduce a list to a single value by iteratively applying a binary function #' #' Inspired by \code{reduce()} from the \code{purrr} package #' #' @param .x A list or atomic vector. #' @param .f A 2-argument function. The function will be #' passed the accumulated value as the first argument and the "next" value #' as the second argument. #' @param ... Additional arguments passed on to `.f`. #' @param .init If supplied, will be used as the first value to start #' the accumulation, rather than using \code{x[[1]]}. This is useful if #' you want to ensure that `reduce` returns a correct value when `.x` #' is empty. If missing, and `x` is empty, will throw an error. #' #' @author Jonathon Love #' @keywords internal #' reduce <- function(.x, .f, ..., .init) { if (missing(.init)) { if (length(.x) == 0) stop('`.x` is empty, and no `.init` supplied') v <- .x[[1]] i <- 2 } else { v <- .init i <- 1 } while (i <= length(.x)) { v <- .f(v, .x[[i]], ...) i <- i + 1 } v } check_vline_size <- function(data) { if (is.null(data$vline_width) && !is.null(data$vline_size)) { warning(" Use of the `vline_size` aesthetics is deprecated, please use `vline_width` instead of `vline_size`.", call. = FALSE) data$vline_width <- data$vline_size } data } check_size <- function(data) { if (is.null(data$linewidth) && !is.null(data$size)) { warning(" Use of the `size` aesthetic is deprecated, please use `linewidth` instead of `size`", call. = FALSE) data$width <- data$size } data } check_vline_size_param <- function(params) { if ("vline_size" %in% names(params)) { warning(" Use of the `vline_size` or `size` aesthetic are deprecated, please use `linewidth` instead of `size` and `vline_width` instead of `vline_size`.", call. = FALSE) params$vline_width <- params$vline_size } params } check_size_param <- function(params) { if ("size" %in% names(params)) { warning(" Use of the `size` aesthetic is deprecated, please use `linewidth` instead of `size`.", call. = FALSE) params$linewidth <- params$size } params } ggridges/R/utils_ggplot2.R0000644000176200001440000000313614536504206015177 0ustar liggesusers# code that needed to be copied from ggplot2 # Name ggplot grid object, from ggplot2/R/utilities-grid.r # Convenience function to name grid objects # # @keyword internal ggname <- function(prefix, grob) { grob$name <- grid::grobName(grob, prefix) grob } # From ggplot2/R/utilities.r snakeize <- function(x) { x <- gsub("([A-Za-z])([A-Z])([a-z])", "\\1_\\2\\3", x) x <- gsub(".", "_", x, fixed = TRUE) x <- gsub("([a-z])([A-Z])", "\\1_\\2", x) tolower(x) } snake_class <- function(x) { snakeize(class(x)[1]) } with_seed_null <- function(seed, code) { if (is.null(seed)) { code } else { withr::with_seed(seed, code) } } "%||%" <- function(a, b) { if (!is.null(a)) a else b } # ggplot2 range code, from ggplot2/R/range.r #' @importFrom ggplot2 ggproto #' @noRd Range <- ggproto("Range", NULL, range = NULL, reset = function(self) { self$range <- NULL } ) #' @importFrom scales train_discrete #' @noRd RangeDiscrete <- ggproto("RangeDiscrete", Range, train = function(self, x, drop = FALSE, na.rm = FALSE) { self$range <- scales::train_discrete(x, self$range, drop = drop, na.rm = na.rm) } ) discrete_range <- function() { ggproto(NULL, RangeDiscrete) } # ggplot2 aes code, from ggplot2/R/aes.r # Look up the scale that should be used for a given aesthetic aes_to_scale <- function(var) { var[var %in% c("x", "xmin", "xmax", "xend", "xintercept")] <- "x" var[var %in% c("y", "ymin", "ymax", "yend", "yintercept")] <- "y" var } # Figure out if an aesthetic is a position aesthetic or not is_position_aes <- function(vars) { aes_to_scale(vars) %in% c("x", "y") } ggridges/R/scale-vline.R0000644000176200001440000001034214536504206014600 0ustar liggesusers#' Scales for vline aesthetics #' #' These are various scales that can be applied to vline aesthetics, such as #' `vline_color`, `vline_width`, `vline_linetype`. The individual scales all have the #' same usage as existing standard ggplot2 scales, only the name differs. #' #' @name scale_vline #' @seealso See [`scale_point_color_hue()`] for specific scales for point aesthetics #' and [`scale_discrete_manual()`] for a general discrete scale. #' @examples #' library(ggplot2) #' #' # default scales #' ggplot(iris, aes(x=Sepal.Length, y=Species, fill = Species, color = Species)) + #' geom_density_ridges( #' aes(vline_color = Species, vline_linetype = Species), #' alpha = .4, quantile_lines = TRUE #' ) + #' theme_ridges() #' #' # modified scales #' ggplot(iris, aes(x=Sepal.Length, y=Species, fill = Species, color = Species)) + #' geom_density_ridges( #' aes(vline_color = Species), #' alpha = .4, quantile_lines = TRUE #' ) + #' scale_fill_hue(l = 50) + #' scale_vline_color_hue(l = 30) + #' theme_ridges() #' @aliases NULL NULL #' `scale_vline_linetype()`: Equivalent to [`scale_linetype()`]. #' @rdname scale_vline #' @usage NULL #' @export scale_vline_linetype <- function(..., na.value = "blank", aesthetics = "vline_linetype") { discrete_scale(aesthetics, "linetype_d", scales::linetype_pal(), na.value = na.value, ...) } #' `scale_vline_width_continuous()`: Equivalent to [`scale_linewidth_continuous()`]. #' @rdname scale_vline #' @usage NULL #' @export scale_vline_width_continuous <- function(name = ggplot2::waiver(), breaks = ggplot2::waiver(), labels = ggplot2::waiver(), limits = NULL, range = c(1, 6), trans = "identity", guide = "legend", aesthetics = "vline_width") { ggplot2::continuous_scale(aesthetics, "area", scales::area_pal(range), name = name, breaks = breaks, labels = labels, limits = limits, trans = trans, guide = guide) } #' `scale_vline_colour_hue()`: Equivalent to [`scale_colour_hue()`]. #' @rdname scale_vline #' @usage NULL #' @export scale_vline_colour_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1, na.value = "grey50", aesthetics = "vline_colour") { ggplot2::discrete_scale(aesthetics, "hue", scales::hue_pal(h, c, l, h.start, direction), na.value = na.value, ...) } #' @rdname scale_vline #' @usage NULL #' @export scale_vline_color_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1, na.value = "grey50", aesthetics = "vline_color") { ggplot2::discrete_scale(aesthetics, "hue", scales::hue_pal(h, c, l, h.start, direction), na.value = na.value, ...) } #' `scale_vline_colour_gradient()`: Equivalent to [`scale_colour_gradient()`]. #' @rdname scale_vline #' @usage NULL #' @export scale_vline_colour_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = "Lab", na.value = "grey50", guide = "none", aesthetics = "vline_colour") { ggplot2::continuous_scale(aesthetics, "gradient", scales::seq_gradient_pal(low, high, space), na.value = na.value, guide = guide, ...) } #' @rdname scale_vline #' @usage NULL #' @export scale_vline_color_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = "Lab", na.value = "grey50", guide = "none", aesthetics = "vline_color") { ggplot2::continuous_scale(aesthetics, "gradient", scales::seq_gradient_pal(low, high, space), na.value = na.value, guide = guide, ...) } # default scales #' @rdname scale_vline #' @usage NULL #' @export scale_vline_linetype_discrete <- scale_vline_linetype #' @rdname scale_vline #' @usage NULL #' @export scale_vline_color_discrete <- scale_vline_color_hue #' @rdname scale_vline #' @usage NULL #' @export scale_vline_colour_discrete <- scale_vline_colour_hue #' @rdname scale_vline #' @usage NULL #' @export scale_vline_color_continuous <- scale_vline_color_gradient #' @rdname scale_vline #' @usage NULL #' @export scale_vline_colour_continuous <- scale_vline_colour_gradient ggridges/R/stats.R0000644000176200001440000003766714536504206013557 0ustar liggesusers# Code for stat_density_ridges based on stat_density_common in the "extending ggplot2" vignette #' Stat for density ridgeline plots #' #' This stat is the default stat used by [`geom_density_ridges`]. It is very similar to [`stat_density`], #' however there are a few differences. Most importantly, the density bandwidth is chosen across #' the entire dataset. #' #' @param geom The geometric object to use to display the data. #' @param bandwidth Bandwidth used for density calculation. If not provided, is estimated from the data. #' @param from,to The left and right-most points of the grid at which the density is to be estimated, #' as in [`density()`]. If not provided, these are estimated from the data range and the bandwidth. #' @param jittered_points If `TRUE`, carries the original point data over to the processed data frame, #' so that individual points can be drawn by the various ridgeline geoms. The specific position of these #' points is controlled by various position objects, e.g. [`position_points_sina()`] or [`position_raincloud()`]. #' @param quantile_lines If `TRUE`, enables the drawing of quantile lines. Overrides the `calc_ecdf` setting #' and sets it to `TRUE`. #' @param calc_ecdf If `TRUE`, `stat_density_ridges` calculates an empirical cumulative distribution function (ecdf) #' and returns a variable `ecdf` and a variable `quantile`. Both can be mapped onto aesthetics via #' `stat(ecdf)` and `stat(quantile)`, respectively. #' @param quantiles Sets the number of quantiles the data should be broken into. Used if either `calc_ecdf = TRUE` #' or `quantile_lines = TRUE`. If `quantiles` is an integer then the data will be cut into that many equal quantiles. #' If it is a vector of probabilities then the data will cut by them. #' @param quantile_fun Function that calculates quantiles. The function needs to accept two parameters, #' a vector `x` holding the raw data values and a vector `probs` providing the probabilities that #' define the quantiles. Default is `quantile`. #' @param n The number of equally spaced points at which the density is to be estimated. Should be a power of 2. Default #' is 512. #' @inheritParams geom_ridgeline #' @importFrom ggplot2 layer #' @examples #' library(ggplot2) #' #' # Examples of coloring by ecdf or quantiles #' ggplot(iris, aes(x = Sepal.Length, y = Species, fill = factor(stat(quantile)))) + #' stat_density_ridges( #' geom = "density_ridges_gradient", #' calc_ecdf = TRUE, #' quantiles = 5 #' ) + #' scale_fill_viridis_d(name = "Quintiles") + #' theme_ridges() #' #' ggplot(iris, #' aes( #' x = Sepal.Length, y = Species, fill = 0.5 - abs(0.5-stat(ecdf)) #' )) + #' stat_density_ridges(geom = "density_ridges_gradient", calc_ecdf = TRUE) + #' scale_fill_viridis_c(name = "Tail probability", direction = -1) + #' theme_ridges() #' #' ggplot(iris, #' aes( #' x = Sepal.Length, y = Species, fill = factor(stat(quantile)) #' )) + #' stat_density_ridges( #' geom = "density_ridges_gradient", #' calc_ecdf = TRUE, quantiles = c(0.025, 0.975) #' ) + #' scale_fill_manual( #' name = "Probability", #' values = c("#FF0000A0", "#A0A0A0A0", "#0000FFA0"), #' labels = c("(0, 0.025]", "(0.025, 0.975]", "(0.975, 1]") #' ) + #' theme_ridges() #' @export stat_density_ridges <- function(mapping = NULL, data = NULL, geom = "density_ridges", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, bandwidth = NULL, from = NULL, to = NULL, jittered_points = FALSE, quantile_lines = FALSE, calc_ecdf = FALSE, quantiles = 4, quantile_fun = quantile, n = 512, ...) { layer( stat = StatDensityRidges, data = data, mapping = mapping, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(bandwidth = bandwidth, from = from, to = to, calc_ecdf = calc_ecdf, quantiles = quantiles, jittered_points = jittered_points, quantile_lines = quantile_lines, quantile_fun = quantile_fun, n = n, na.rm = na.rm, ...) ) } #' @rdname stat_density_ridges #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto Stat #' @export StatDensityRidges <- ggproto("StatDensityRidges", Stat, required_aes = "x", default_aes = aes(height = after_stat(density)), calc_panel_params = function(data, params) { if (is.null(params$bandwidth)) { xdata <- na.omit(data.frame(x=data$x, group=data$group)) xs <- split(xdata$x, xdata$group) xs_mask <- vapply(xs, length, numeric(1)) > 1 bws <- vapply(xs[xs_mask], bw.nrd0, numeric(1)) bw <- mean(bws, na.rm = TRUE) message("Picking joint bandwidth of ", signif(bw, 3)) params$bandwidth <- bw } if (is.null(params$from)) { params$from <- min(data$x, na.rm=TRUE) - 3 * params$bandwidth } if (is.null(params$to)) { params$to <- max(data$x, na.rm=TRUE) + 3 * params$bandwidth } data.frame( bandwidth = params$bandwidth, from = params$from, to = params$to ) }, setup_params = function(self, data, params) { # calculate bandwidth, min, and max for each panel separately panels <- split(data, data$PANEL) pardata <- lapply(panels, self$calc_panel_params, params) pardata <- reduce(pardata, rbind) if (length(params$quantiles) > 1 && (max(params$quantiles, na.rm = TRUE) > 1 || min(params$quantiles, na.rm = TRUE) < 0)) { stop('invalid quantiles used: c(', paste0(params$quantiles, collapse = ','), ') must be within [0, 1] range') } params$bandwidth <- pardata$bandwidth params$from <- pardata$from params$to <- pardata$to params }, compute_group = function(data, scales, from, to, bandwidth = 1, calc_ecdf = FALSE, jittered_points = FALSE, quantile_lines = FALSE, quantiles = 4, quantile_fun = quantile, n = 512) { # ignore too small groups if(nrow(data) < 3) return(data.frame()) if (is.null(calc_ecdf)) calc_ecdf <- FALSE if (is.null(jittered_points)) jittered_points <- FALSE if (is.null(quantile_lines)) quantile_lines <- FALSE # when quantile lines are requested, we also calculate ecdf # this simplifies things for now; in principle, could disentangle # the two if (quantile_lines) calc_ecdf <- TRUE panel <- unique(data$PANEL) if (length(panel) > 1) { stop("Error: more than one panel in compute group; something's wrong.") } panel_id <- as.numeric(panel) d <- stats::density( data$x, bw = bandwidth[panel_id], from = from[panel_id], to = to[panel_id], na.rm = TRUE, n = n ) # calculate maximum density for scaling maxdens <- max(d$y, na.rm = TRUE) # make interpolating function for density line densf <- approxfun(d$x, d$y, rule = 2) # calculate jittered original points if requested if (jittered_points) { df_jittered <- data.frame( x = data$x, # actual jittering is handled in the position argument density = densf(data$x), ndensity = densf(data$x) / maxdens, datatype = "point", stringsAsFactors = FALSE) # see if we need to carry over other point data # capture all data columns starting with "point", as those are relevant for point aesthetics df_points <- data[grepl("point_", names(data))] # uncomment following line to switch off carrying over data #df_points <- data.frame() if (ncol(df_points) == 0) { df_points <- NULL df_points_dummy <- NULL } else { # combine additional points data into results dataframe df_jittered <- cbind(df_jittered, df_points) # make a row of dummy data to merge with the other dataframes df_points_dummy <- na.omit(df_points)[1, , drop = FALSE] } } else { df_jittered <- NULL df_points_dummy <- NULL } # calculate quantiles, needed for both quantile lines and ecdf if ((length(quantiles)==1) && (all(quantiles >= 1))) { if (quantiles > 1) { probs <- seq(0, 1, length.out = quantiles + 1)[2:quantiles] } else { probs <- NA } } else { probs <- quantiles probs[probs < 0 | probs > 1] <- NA } qx <- na.omit(quantile_fun(data$x, probs = probs)) # if requested, add data frame for quantile lines df_quantiles <- NULL if (quantile_lines && length(qx) > 0) { qy <- densf(qx) df_quantiles <- data.frame( x = qx, density = qy, ndensity = qy / maxdens, datatype = "vline", stringsAsFactors = FALSE ) if (!is.null(df_points_dummy)){ # add in dummy points data if necessary df_quantiles <- data.frame(df_quantiles, as.list(df_points_dummy)) } } # combine the quantiles and jittered points data frames into one, the non-density frame df_nondens <- rbind(df_quantiles, df_jittered) if (calc_ecdf) { n <- length(d$x) ecdf <- c(0, cumsum(d$y[1:(n-1)]*(d$x[2:n]-d$x[1:(n-1)]))) ecdf_fun <- approxfun(d$x, ecdf, rule = 2) ntile <- findInterval(d$x, qx, left.open = TRUE) + 1 # if make changes here, make them also below if (!is.null(df_nondens)) { # we add data for ecdf and quantiles back to all other data points df_nondens <- data.frame( df_nondens, ecdf = ecdf_fun(df_nondens$x), quantile = findInterval(df_nondens$x, qx, left.open = TRUE) + 1 ) } df_density <- data.frame( x = d$x, density = d$y, ndensity = d$y / maxdens, ecdf = ecdf, quantile = ntile, datatype = "ridgeline", stringsAsFactors = FALSE ) } else { df_density <- data.frame( x = d$x, density = d$y, ndensity = d$y / maxdens, datatype = "ridgeline", stringsAsFactors = FALSE ) } if (!is.null(df_points_dummy)){ # add in dummy points data if necessary df_density <- data.frame(df_density, as.list(df_points_dummy)) } # now combine everything and turn quantiles into factor df_final <- rbind(df_density, df_nondens) if ("quantile" %in% names(df_final)) { df_final$quantile <- factor(df_final$quantile) } df_final } ) #' Stat for histogram ridgeline plots #' #' Works like `stat_bin` except that the output is a ridgeline describing the histogram rather than #' a set of counts. #' #' @param geom The geom to use for drawing. #' @param draw_baseline If `FALSE`, removes lines along 0 counts. Defaults to `TRUE`. #' @param pad If `TRUE`, adds empty bins at either end of x. This ensures that the binline always goes #' back down to 0. Defaults to `TRUE`. #' @inheritParams ggplot2::geom_histogram #' #' @examples #' library(ggplot2) #' #' ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species, fill = Species)) + #' geom_density_ridges(stat = "binline", bins = 20, scale = 2.2) + #' scale_y_discrete(expand = c(0, 0)) + #' scale_x_continuous(expand = c(0, 0)) + #' coord_cartesian(clip = "off") + #' theme_ridges() #' #' ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species, fill = Species)) + #' stat_binline(bins = 20, scale = 2.2, draw_baseline = FALSE) + #' scale_y_discrete(expand = c(0, 0)) + #' scale_x_continuous(expand = c(0, 0)) + #' scale_fill_grey() + #' coord_cartesian(clip = "off") + #' theme_ridges() + #' theme(legend.position = 'none') #' #' library(ggplot2movies) #' ggplot(movies[movies$year>1989,], aes(x = length, y = year, fill = factor(year))) + #' stat_binline(scale = 1.9, bins = 40) + #' scale_x_continuous(limits = c(1, 180), expand = c(0, 0)) + #' scale_y_reverse(expand = c(0, 0)) + #' scale_fill_viridis_d(begin = 0.3, option = "B") + #' coord_cartesian(clip = "off") + #' labs(title = "Movie lengths 1990 - 2005") + #' theme_ridges() + #' theme(legend.position = "none") #' #' count_data <- data.frame( #' group = rep(letters[1:5], each = 10), #' mean = rep(1:5, each = 10) #' ) #' count_data$group <- factor(count_data$group, levels = letters[5:1]) #' count_data$count <- rpois(nrow(count_data), count_data$mean) #' #' ggplot(count_data, aes(x = count, y = group, group = group)) + #' geom_density_ridges2( #' stat = "binline", #' aes(fill = group), #' binwidth = 1, #' scale = 0.95 #' ) + #' geom_text( #' stat = "bin", #' aes(y = group + 0.9*stat(count/max(count)), #' label = ifelse(stat(count) > 0, stat(count), "")), #' vjust = 1.2, size = 3, color = "white", binwidth = 1 #' ) + #' scale_x_continuous(breaks = c(0:12), limits = c(-.5, 13), expand = c(0, 0)) + #' scale_y_discrete(expand = c(0, 0)) + #' scale_fill_cyclical(values = c("#0000B0", "#7070D0")) + #' guides(y = "none") + #' coord_cartesian(clip = "off") + #' theme_ridges(grid = FALSE) #' @importFrom stats quantile #' @export stat_binline <- function(mapping = NULL, data = NULL, geom = "density_ridges", position = "identity", ..., binwidth = NULL, bins = NULL, center = NULL, boundary = NULL, breaks = NULL, closed = c("right", "left"), pad = TRUE, draw_baseline = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { layer( data = data, mapping = mapping, stat = StatBinline, geom = geom, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( binwidth = binwidth, bins = bins, center = center, boundary = boundary, breaks = breaks, closed = closed, pad = pad, draw_baseline = draw_baseline, na.rm = na.rm, ... ) ) } #' @rdname stat_binline #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto StatBin #' @export StatBinline <- ggproto("StatBinline", StatBin, required_aes = "x", default_aes = aes(height = after_stat(density)), setup_params = function(data, params) { # provide default value if not given, happens when stat is called from a geom if (is.null(params$pad)) { params$pad <- TRUE } # provide default value if not given, happens when stat is called from a geom if (is.null(params$draw_baseline)) { params$draw_baseline <- TRUE } if (!is.null(params$boundary) && !is.null(params$center)) { stop("Only one of `boundary` and `center` may be specified.", call. = FALSE) } if (is.null(params$breaks) && is.null(params$binwidth) && is.null(params$bins)) { message("`stat_binline()` using `bins = 30`. Pick better value with `binwidth`.") params$bins <- 30 } params }, compute_group = function(self, data, scales, binwidth = NULL, bins = NULL, center = NULL, boundary = NULL, closed = c("right", "left"), pad = TRUE, breaks = NULL, origin = NULL, right = NULL, drop = NULL, width = NULL, draw_baseline = TRUE) { binned <- ggproto_parent(StatBin, self)$compute_group(data = data, scales = scales, binwidth = binwidth, bins = bins, center = center, boundary = boundary, closed = closed, pad = pad, breaks = breaks) result <- rbind(transform(binned, x=xmin), transform(binned, x=xmax-0.00001*width)) result <- result[order(result$x), ] # remove zero counts if requested if (!draw_baseline) { zeros <- result$count == 0 protected <- (zeros & !c(zeros[2:length(zeros)], TRUE)) | (zeros & !c(TRUE, zeros[1:length(zeros)-1])) to_remove <- zeros & !protected result$count[to_remove] <- NA result$density[to_remove] <- NA } result } ) ggridges/R/geomsv.R0000644000176200001440000002020614536504206013676 0ustar liggesusers#' Plot a vertical ridgeline (ridgeline rotated 90 degrees) #' #' Plots the sum of the `x` and `width` aesthetics versus `y`, filling the area between `x` and `x + width` with a color. #' Just like [geom_ridgeline()], but with y and x replaced. #' #' @param mapping Set of aesthetic mappings created by [`aes()`] or #' [`aes_()`]. If specified and `inherit.aes = TRUE` (the #' default), it is combined with the default mapping at the top level of the #' plot. You must supply `mapping` if there is no plot mapping. #' @param data The data to be displayed in this layer. There are three #' options: #' #' If `NULL`, the default, the data is inherited from the plot #' data as specified in the call to [`ggplot()`]. #' #' A `data.frame`, or other object, will override the plot #' data. #' #' A `function` will be called with a single argument, #' the plot data. The return value must be a `data.frame.`, and #' will be used as the layer data. #' @param stat The statistical transformation to use on the data for this #' layer, as a string. #' @param position Position adjustment, either as a string, or the result of #' a call to a position adjustment function. #' @param show.legend logical. Should this layer be included in the legends? #' `NA`, the default, includes if any aesthetics are mapped. #' `FALSE` never includes, and `TRUE` always includes. #' @param inherit.aes If `FALSE`, overrides the default aesthetics, #' rather than combining with them. #' @param na.rm If `FALSE`, the default, missing values are removed with #' a warning. If `TRUE`, missing values are silently removed. #' @param ... other arguments passed on to [`layer()`]. These are #' often aesthetics, used to set an aesthetic to a fixed value, like #' `color = "red"` or `linewidth = 3`. They may also be parameters #' to the paired geom/stat. #' #' @section Aesthetics: #' #' Required aesthetics are in bold. #' #' * **`x`** #' * **`y`** #' * **`width`** Width of the ridgeline, measured from the respective `x` value. Assumed to be positive, though this is not required. #' * `group` Defines the grouping. Required when the dataset contains multiple distinct ridgelines. Will typically be the same #' variable as is mapped to `x`. #' * `scale` A scaling factor to scale the widths of the ridgelines. #' A value of 1 indicates that the widths are taken as is. This aesthetic can be used to convert #' `width` units into `x` units. #' * `min_width` A width cutoff on the drawn ridgelines. All values that fall below this cutoff will be removed. #' The main purpose of this cutoff is to remove long tails right at the baseline level, but other uses are possible. #' The cutoff is applied before any width #' scaling is applied via the `scale` aesthetic. Default is 0, so negative values are removed. #' * `color` Color of the ridgeline #' * `fill` Fill color of the area under the ridgeline #' * `alpha` Transparency level of `fill`. Not applied to `color`. If you want transparent lines, you can set their #' color as RGBA value, e.g. #FF0000A0 for partially transparent red. #' * `group` Grouping, to draw multiple ridgelines from one dataset #' * `linetype` Linetype of the ridgeline #' * `linewidth` Line thickness #' #' @examples #' library(ggplot2) #' #' d <- data.frame(y = rep(1:5, 3), x = c(rep(0, 5), rep(1, 5), rep(3, 5)), #' width = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) #' ggplot(d, aes(x, y, width = width, group = x)) + geom_vridgeline(fill="lightblue") #' #' ggplot(iris, aes(x=Species, y=Sepal.Width, width = after_stat(density), fill=Species)) + #' geom_vridgeline(stat="ydensity", trim=FALSE, alpha = 0.85, scale = 2) #' #' @importFrom ggplot2 layer #' @export geom_vridgeline <- function(mapping = NULL, data = NULL, stat = "identity", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...) { layer( data = data, mapping = mapping, stat = stat, geom = GeomVRidgeline, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, ... ) ) } #' @rdname geom_vridgeline #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto Geom draw_key_polygon #' @export GeomVRidgeline <- ggproto("GeomVRidgeline", Geom, default_aes = aes(color = "black", fill = "grey80", x = 0, linewidth = 0.5, linetype = 1, min_width = 0, scale = 1, alpha = NA), required_aes = c("x", "y", "width"), setup_data = function(self, data, params) { if (!"scale" %in% names(data)) { if (!"scale" %in% names(params)) data <- cbind(data, scale = self$default_aes$scale) else data <- cbind(data, scale = params$scale) } if (!"min_width" %in% names(data)){ if (!"min_width" %in% names(params)) data <- cbind(data, min_width = self$default_aes$min_width) else data <- cbind(data, min_width = params$min_width) } transform(data, xmin = x, xmax = x + scale*width) }, draw_key = draw_key_polygon, handle_na = function(data, params) { data }, draw_panel = function(self, data, panel_params, coord, ...) { groups <- split(data, factor(data$group)) # sort list so highest xmin values are in the front # we take a shortcut here and look only at the first xmin value given o <- order(unlist(lapply(groups, function(data){data$xmin[1]})), decreasing = TRUE) groups <- groups[o] grobs <- lapply(groups, function(group) { self$draw_group(group, panel_params, coord, ...) }) ggname(snake_class(self), gTree( children = do.call("gList", grobs) )) }, draw_group = function(self, data, panel_params, coord, na.rm = FALSE) { if (na.rm) data <- data[stats::complete.cases(data[c("y", "xmin", "xmax")]), ] #if dataframe is empty there's nothing to draw if (nrow(data) == 0) return(grid::nullGrob()) data <- data[order(data$group, data$y), ] # remove all points that fall below the minimum width data$xmax[data$width < data$min_width] <- NA # Check that aesthetics are constant aes <- unique(data[c("colour", "fill", "linewidth", "linetype", "alpha")]) if (nrow(aes) > 1) { stop("Aesthetics can not vary along a ridgeline") } aes <- as.list(aes) # Instead of removing NA values from the data and plotting a single # polygon, we want to "stop" plotting the polygon whenever we're # missing values and "start" a new polygon as soon as we have new # values. We do this by creating an id vector for polygonGrob that # has distinct polygon numbers for sequences of non-NA values and NA # for NA values in the original data. Example: c(NA, 2, 2, 2, NA, NA, # 4, 4, 4, NA) missing_pos <- !stats::complete.cases(data[c("y", "xmin", "xmax")]) ids <- cumsum(missing_pos) + 1 ids[missing_pos] <- NA # munching for polygon positions <- with(data, data.frame( y = c(y, rev(y)), x = c(xmax, rev(xmin)), id = c(ids, rev(ids)) )) munched_poly <- ggplot2::coord_munch(coord, positions, panel_params) # munching for line positions <- with(data, data.frame( y = y, x = xmax, id = ids )) munched_line <- ggplot2::coord_munch(coord, positions, panel_params) # placing the actual grob generation into a separate function allows us to override for geom_density_ridges2 self$make_group_grob(munched_line, munched_poly, aes) }, make_group_grob = function(munched_line, munched_poly, aes) { lg <- ggname("geom_ridgeline", grid::polylineGrob( munched_line$x, munched_line$y, id = munched_line$id, default.units = "native", gp = grid::gpar( col = aes$colour, lwd = aes$linewidth * .pt, lty = aes$linetype) )) ag <- ggname("geom_ridgeline", grid::polygonGrob( munched_poly$x, munched_poly$y, id = munched_poly$id, default.units = "native", gp = grid::gpar( fill = ggplot2::alpha(aes$fill, aes$alpha), lty = 0) )) grid::grobTree(ag, lg) } ) ggridges/R/position.R0000644000176200001440000001671014536504206014247 0ustar liggesusers#' Randomly jitter the points in a ridgeline plot #' #' This is a position adjustment specifically for [`geom_density_ridges()`] and related geoms. It #' only jitters the points drawn by these geoms, if any. If no points are present, the plot #' remains unchanged. The effect is similar to [`position_jitter()`]: points are randomly shifted up and down #' and/or left and right. #' #' @param width Width for horizontal jittering. By default set to 0. #' @param height Height for vertical jittering, applied in both directions (up and down). By default 0.2. #' @param yoffset Vertical offset applied in addition to jittering. #' @param adjust_vlines If `TRUE`, adjusts vertical lines (as are drawn for #' quantile lines, for example) to align with the point cloud. #' @param seed Random seed. If set to NULL, the current random number generator is used. #' If set to NA, a new random random seed is generated. If set to a number, this #' number is used as seed for jittering only. #' @seealso Other position adjustments for ridgeline plots: [`position_points_sina`], [`position_raincloud`] #' @examples #' library(ggplot2) #' #' # default jittered points #' ggplot(iris, aes(x = Sepal.Length, y = Species)) + #' geom_density_ridges(jittered_points = TRUE, position = "points_jitter", alpha = 0.7) #' #' # simulating a rug #' ggplot(iris, aes(x = Sepal.Length, y = Species)) + #' geom_density_ridges(jittered_points = TRUE, point_shape = '|', alpha = 0.7, point_size = 2, #' position = position_points_jitter(width = 0.02, height = 0)) #' @export position_points_jitter <- function(width = 0, height = 0.2, yoffset = 0, adjust_vlines = FALSE, seed = NULL) { if (!is.null(seed) && is.na(seed)) { seed <- sample.int(.Machine$integer.max, 1L) } ggproto(NULL, PositionPointsJitter, width = width, height = height, yoffset = yoffset, adjust_vlines = adjust_vlines, seed = seed ) } #' @rdname position_points_jitter #' @format NULL #' @usage NULL #' @export PositionPointsJitter <- ggproto("PositionPointsJitter", Position, required_aes = c("x", "ymin", "ymax"), setup_params = function(self, data) { list( width = self$width %||% 0, height = self$height %||% 0.2, yoffset = self$yoffset %||% 0, adjust_vlines = self$adjust_vlines %||% FALSE, seed = self$seed ) }, compute_layer = function(data, params, panel) { # if there's no datatype aesthetic then we're done by default if (!"datatype" %in% names(data)) { return(data) } points <- data$datatype == "point" with_seed_null(params$seed, { if (params$width > 0) { data$x[points] <- data$x[points] - params$width + 2 * params$width * runif(sum(points)) }; data$ymin[points] <- data$ymin[points] + params$yoffset - params$height + 2 * params$height * runif(sum(points)) }) # do we need to adjust vertical lines as well? if (!params$adjust_vlines) { return(data) # no, we're done } vlines <- data$datatype == "vline" data$ymin[vlines] <- data$ymin[vlines] + params$yoffset - params$height data$ymax[vlines] <- data$ymin[vlines] + 2 * params$height data } ) #' Create a cloud of randomly jittered points below a ridgeline plot #' #' This is a position adjustment specifically for [`geom_density_ridges()`] and related geoms. It #' only jitters the points drawn by these geoms, if any. If no points are present, the plot #' remains unchanged. The effect is similar to [`position_points_jitter()`], only that by default the #' points lie all underneath the baseline of each individual ridgeline. #' #' The idea for this position adjustment comes from Micah Allen's work #' on raincloud plots (Allen et al. 2021). #' #' @param width Width for horizontal jittering. By default set to 0. #' @param height Total height of point cloud. By default 0.4. #' @param ygap Vertical gap between ridgeline baseline and point cloud. #' @param adjust_vlines If `TRUE`, adjusts vertical lines (as are drawn for #' quantile lines, for example) to align with the point cloud. #' @param seed Random seed. See [`position_points_jitter`]. #' @seealso Other position adjustments for ridgeline plots: [`position_points_jitter`], [`position_points_sina`] #' @references #' Allen, M., Poggiali, D., Whitaker, K., Marshall, T. R., #' van Langen, J., Kievit, R. A. (2021) Raincloud plots: #' a multi-platform tool for robust data visualization #' \[version 2; peer review: 2 approved\]. Wellcome Open Res 4:63. #' @examples #' library(ggplot2) #' #' ggplot(iris, aes(x = Sepal.Length, y = Species)) + #' geom_density_ridges(jittered_points = TRUE, position = "raincloud", alpha = 0.7) #' @export position_raincloud <- function(width = 0, height = 0.4, ygap = 0.05, adjust_vlines = FALSE, seed = NULL) { if (!is.null(seed) && is.na(seed)) { seed <- sample.int(.Machine$integer.max, 1L) } ggproto(NULL, PositionRaincloud, width = width, height = height, ygap = ygap, adjust_vlines = adjust_vlines, seed = seed ) } #' @rdname position_raincloud #' @format NULL #' @usage NULL #' @export PositionRaincloud <- ggproto("PositionRaincloud", PositionPointsJitter, required_aes = c("x", "ymin", "ymax"), setup_params = function(self, data) { height <- (self$height %||% 0.4)/2 yoffset <- -height - (self$ygap %||% 0.05) list( width = self$width %||% 0, height = height, yoffset = yoffset, adjust_vlines = self$adjust_vlines %||% FALSE, seed = self$seed ) } ) #' Randomly distribute points in a ridgeline plot between baseline and ridgeline #' #' This is a position adjustment specifically for [`geom_density_ridges()`] and related geoms. It #' only jitters the points drawn by these geoms, if any. If no points are present, the plot #' remains unchanged. The effect is similar to a sina plot: Points are randomly distributed to fill #' the entire shaded area representing the data density. #' #' @param rel_min The relative minimum value at which a point can be placed. #' @param rel_max The relative maximum value at which a point can be placed. #' @param seed See [`position_points_jitter`]. #' @seealso Other position adjustments for ridgeline plots: [`position_points_jitter`], [`position_raincloud`] #' @examples #' library(ggplot2) #' #' ggplot(iris, aes(x = Sepal.Length, y = Species)) + #' geom_density_ridges(jittered_points = TRUE, position = "points_sina", alpha = 0.7) #' @export position_points_sina <- function(rel_min = 0.02, rel_max = 0.98, seed = NULL) { if (!is.null(seed) && is.na(seed)) { seed <- sample.int(.Machine$integer.max, 1L) } ggproto(NULL, PositionPointsSina, rel_min = rel_min, rel_max = rel_max, seed = seed ) } #' @rdname position_points_sina #' @format NULL #' @usage NULL #' @export PositionPointsSina <- ggproto("PositionPointsSina", Position, required_aes = c("x", "ymin", "ymax"), setup_params = function(self, data) { list( rel_min = self$rel_abc %||% 0.02, rel_max = self$rel_max %||% 0.98, seed = self$seed ) }, compute_layer = function(data, params, panel) { # if there's no datatype aesthetic then we're done by default if (!"datatype" %in% names(data)) { return(data) } points <- data$datatype == "point" with_seed_null(params$seed, data$ymin[points] <- data$ymin[points] + (params$rel_min + (params$rel_max - params$rel_min) * runif(sum(points))) * (data$ymax[points] - data$ymin[points]) ) data } ) ggridges/R/data.R0000644000176200001440000000440714536504206013314 0ustar liggesusers#' Weather in Lincoln, Nebraska in 2016. #' #' A dataset containing weather information from Lincoln, Nebraska, from 2016. #' Originally downloaded from Weather Underground by Austin Wehrwein, http://austinwehrwein.com/. #' The variables are listed below. Most are self-explanatory. Max, mean, and min measurements are #' calculated relative to the specific day of measurement. #' #' @format A tibble with 366 rows and 24 variables: #' \describe{ #' \item{`CST`}{Day of the measurement} #' \item{`Max Temperature [F]`}{} #' \item{`Mean Temperature [F]`}{} #' \item{`Min Temperature [F]`}{} #' \item{`Max Dew Point [F]`}{} #' \item{`Mean Dew Point [F]`}{} #' \item{`Min Dewpoint [F]`}{} #' \item{`Max Humidity`}{} #' \item{`Mean Humidity`}{} #' \item{`Min Humidity`}{} #' \item{`Max Sea Level Pressure [In]`}{} #' \item{`Mean Sea Level Pressure [In]`}{} #' \item{`Min Sea Level Pressure [In]`}{} #' \item{`Max Visibility [Miles]`}{} #' \item{`Mean Visibility [Miles]`}{} #' \item{`Min Visibility [Miles]`}{} #' \item{`Max Wind Speed [MPH]`}{} #' \item{`Mean Wind Speed[MPH]`}{} #' \item{`Max Gust Speed [MPH]`}{} #' \item{`Precipitation [In]`}{} #' \item{`CloudCover`}{} #' \item{`Events`}{Specific weather events, such as rain, snow, or fog} #' \item{`WindDir [Degrees]`}{} #' \item{`Month`}{The month in which the measurement was taken} #' } "lincoln_weather" #' Results from Catalan regional elections (1980-2015) #' #' Data from Catalan regional elections for 949 municipalities, from 11 elections spanning the years #' 1980-2015. The data was obtained and processed from Idescat.cat by Marc Belzunces (Twitter: @marcbeldata). #' @format A tibble with 20764 rows and 4 variables: #' \describe{ #' \item{`Municipality`}{} #' \item{`Year`}{} #' \item{`Option`}{The voter option; either "Indy" or "Unionist"} #' \item{`Percent`}{The percentage of the voters choosing the given option} #' } "Catalan_elections" #' Australian athletes #' #' This dataset is equivalent to `ais` from the `DAAG` package. #' #' @references #' Telford, R.D. and Cunningham, R.B. 1991. Sex, sport and body-size dependency of hematology in #' highly trained athletes. Medicine and Science in Sports and Exercise 23: 788-794. #' #' @examples #' # none yet "Aus_athletes" ggridges/R/scale-point.R0000644000176200001440000001212714536504206014617 0ustar liggesusers#' Scales for point aesthetics #' #' These are various scales that can be applied to point aesthetics, such as #' `point_color`, `point_fill`, `point_size`. The individual scales all have the #' same usage as existing standard ggplot2 scales, only the name differs. #' #' @name scale_point #' @seealso See [`scale_vline_color_hue()`] for specific scales for vline aesthetics #' and [`scale_discrete_manual()`] for a general discrete scale. #' @examples #' library(ggplot2) #' #' # default scales #' ggplot(iris, aes(x=Sepal.Length, y=Species, fill = Species)) + #' geom_density_ridges( #' aes( #' point_color = Species, point_fill = Species, #' point_shape = Species #' ), #' alpha = .4, jittered_points = TRUE #' ) + #' theme_ridges() #' #' # modified scales #' ggplot(iris, aes(x=Sepal.Length, y=Species, fill = Species)) + #' geom_density_ridges( #' aes( #' point_color = Species, point_fill = Species, #' point_shape = Species #' ), #' alpha = .4, point_alpha = 1, #' jittered_points = TRUE #' ) + #' scale_fill_hue(l = 50) + #' scale_point_color_hue(l = 20) + #' scale_point_fill_hue(l = 70) + #' scale_discrete_manual("point_shape", values = c(21, 22, 23)) + #' theme_ridges() #' @aliases NULL NULL #' `scale_point_shape()`: Equivalent to [`scale_shape()`]. #' @rdname scale_point #' @usage NULL #' @export scale_point_shape <- function(..., solid = TRUE, aesthetics = "point_shape") { discrete_scale(aesthetics, "shape_d", scales::shape_pal(solid), ...) } #' `scale_point_size_continuous()`: Equivalent to [`scale_size_continuous()`]. #' @rdname scale_point #' @usage NULL #' @export scale_point_size_continuous <- function(name = ggplot2::waiver(), breaks = ggplot2::waiver(), labels = ggplot2::waiver(), limits = NULL, range = c(1, 6), trans = "identity", guide = "legend", aesthetics = "point_size") { ggplot2::continuous_scale(aesthetics, "area", scales::area_pal(range), name = name, breaks = breaks, labels = labels, limits = limits, trans = trans, guide = guide) } #' `scale_point_colour_hue()`: Equivalent to [`scale_colour_hue()`]. #' @rdname scale_point #' @usage NULL #' @export scale_point_colour_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1, na.value = "grey50", aesthetics = "point_colour") { ggplot2::discrete_scale(aesthetics, "hue", scales::hue_pal(h, c, l, h.start, direction), na.value = na.value, ...) } #' @rdname scale_point #' @usage NULL #' @export scale_point_color_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1, na.value = "grey50", aesthetics = "point_color") { ggplot2::discrete_scale(aesthetics, "hue", scales::hue_pal(h, c, l, h.start, direction), na.value = na.value, ...) } #' `scale_point_fill_hue()`: Equivalent to [`scale_fill_hue()`]. #' @rdname scale_point #' @usage NULL #' @export scale_point_fill_hue <- function(...) scale_point_color_hue(..., aesthetics = "point_fill") #' `scale_point_colour_gradient()`: Equivalent to [`scale_colour_gradient()`]. #' @rdname scale_point #' @usage NULL #' @export scale_point_colour_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = "Lab", na.value = "grey50", guide = "none", aesthetics = "point_colour") { ggplot2::continuous_scale(aesthetics, "gradient", scales::seq_gradient_pal(low, high, space), na.value = na.value, guide = guide, ...) } #' @rdname scale_point #' @usage NULL #' @export scale_point_color_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = "Lab", na.value = "grey50", guide = "none", aesthetics = "point_color") { ggplot2::continuous_scale(aesthetics, "gradient", scales::seq_gradient_pal(low, high, space), na.value = na.value, guide = guide, ...) } #' `scale_point_fill_gradient()`: Equivalent to [`scale_fill_gradient()`]. Note that this scale cannot #' draw a legend, however, because of limitations in [`guide_colorbar()`]. #' @rdname scale_point #' @usage NULL #' @export scale_point_fill_gradient <- function(...) scale_point_color_gradient(..., aesthetics = "point_fill") # default scales #' @rdname scale_point #' @usage NULL #' @export scale_point_shape_discrete <- scale_point_shape #' @rdname scale_point #' @usage NULL #' @export scale_point_color_discrete <- scale_point_color_hue #' @rdname scale_point #' @usage NULL #' @export scale_point_colour_discrete <- scale_point_colour_hue #' @rdname scale_point #' @usage NULL #' @export scale_point_fill_discrete <- scale_point_fill_hue #' @rdname scale_point #' @usage NULL #' @export scale_point_color_continuous <- scale_point_color_gradient #' @rdname scale_point #' @usage NULL #' @export scale_point_colour_continuous <- scale_point_colour_gradient #' @rdname scale_point #' @usage NULL #' @export scale_point_fill_continuous <- scale_point_fill_gradient ggridges/R/scale-cyclical.R0000644000176200001440000001167614536504206015261 0ustar liggesusers#' Create a discrete scale that cycles between values #' #' The readability of ridgeline plots can often be improved by alternating between fill colors and #' other aesthetics. The various cyclical scales make it easy to create plots with this feature, #' simply map your grouping variable to the respective aesthetic (e.g., `fill`) and then use #' `scale_fill_cyclical` to define the fill colors between you want to alternate. Note that the #' cyclical scales do not draw legends by default, because the legends will usually be wrong #' unless the labels are properly adjusted. To draw legends, set the `guide` argument to `"legend"`, #' as shown in the examples. #' #' @param values The aesthetic values that the scale should cycle through, e.g. colors if it is #' a scale for the color or fill aesthetic. #' @param ... Common discrete scale parameters: `name`, `breaks`, `labels`, `na.value`, `limits` and `guide`. #' See [`discrete_scale`] for more details. #' #' @examples #' library(ggplot2) #' #' # By default, scale_cyclical sets `guide = "none"`, i.e., no legend #' # is drawn #' ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + #' geom_density_ridges(scale = 4) + #' scale_fill_cyclical(values = c("#3030D0", "#9090F0")) #' #' # However, legends can be turned on by setting `guide = "legend"` #' ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + #' geom_density_ridges(scale = 4) + #' scale_fill_cyclical(values = c("#3030D0", "#9090F0"), #' guide = "legend", name = "Fill colors", #' labels = c("dark blue", "light blue")) #' #' # Cyclical scales are also available for the various other aesthetics #' ggplot(diamonds, aes(x = price, y = cut, fill = cut, #' color = cut, size = cut, #' alpha = cut, linetype = cut)) + #' geom_density_ridges(scale = 4, fill = "blue") + #' scale_fill_cyclical(values = c("blue", "green")) + #' scale_color_cyclical(values = c("black", "white")) + #' scale_size_cyclical(values = c(2, 1)) + #' scale_alpha_cyclical(values = c(0.4, 0.8)) + #' scale_linetype_cyclical(values = c(1, 2)) #' #' @name scale_cyclical #' @aliases NULL NULL #' @rdname scale_cyclical #' @export scale_colour_cyclical <- function(..., values) { cyclical_scale("colour", values, ...) } #' @rdname scale_cyclical #' @export #' @usage NULL scale_color_cyclical <- scale_colour_cyclical #' @rdname scale_cyclical #' @export scale_fill_cyclical <- function(..., values) { cyclical_scale("fill", values, ...) } #' @rdname scale_cyclical #' @export scale_alpha_cyclical <- function(..., values) { cyclical_scale("alpha", values, ...) } #' @rdname scale_cyclical #' @export scale_linetype_cyclical <- function(..., values) { cyclical_scale("linetype", values, ...) } #' @rdname scale_cyclical #' @export scale_size_cyclical <- function(..., values) { cyclical_scale("size", values, ...) } #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto ScaleDiscrete #' @rdname scale_cyclical #' @export cyclical_scale <- function(aesthetics, values, name = waiver(), breaks = waiver(), labels = waiver(), limits = NULL, expand = waiver(), na.translate = TRUE, na.value = NA, drop = TRUE, guide = "none", position = "left") { check_breaks_labels(breaks, labels) position <- match.arg(position, c("left", "right", "top", "bottom")) if (is.null(breaks) && !is_position_aes(aesthetics) && guide != "none") { guide <- "none" } # palette function pal <- function(n) { rep_len(values, n) } ggproto(NULL, ScaleCyclical, # standard fields of ScaleDiscrete call = match.call(), aesthetics = aesthetics, scale_name = "cyclical", palette = pal, range = discrete_range(), limits = limits, na.value = na.value, na.translate = na.translate, expand = expand, name = name, breaks = breaks, labels = labels, drop = drop, guide = guide, position = position, # new fields for ScaleCyclical cycle_length = length(values) ) } #' @rdname scale_cyclical #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto ScaleDiscrete #' @export ScaleCyclical <- ggproto("ScaleCyclical", ScaleDiscrete, get_breaks = function(self, limits = self$get_limits()){ breaks <- ggproto_parent(ScaleDiscrete, self)$get_breaks(limits) return(na.omit(breaks[1:self$cycle_length])) }, get_labels = function(self, breaks = self$get_breaks()){ labels <- ggproto_parent(ScaleDiscrete, self)$get_labels(breaks) return(na.omit(labels[1:self$cycle_length])) } ) # internal functions, copied over from ggplot2 check_breaks_labels <- function(breaks, labels) { if (is.null(breaks)) return(TRUE) if (is.null(labels)) return(TRUE) bad_labels <- is.atomic(breaks) && is.atomic(labels) && length(breaks) != length(labels) if (bad_labels) { stop("`breaks` and `labels` must have the same length", call. = FALSE) } TRUE } ggridges/R/geom-density-line.R0000644000176200001440000000357414536504206015740 0ustar liggesusers#' Smoothed density estimates drawn with a ridgeline rather than area #' #' This function is a drop-in replacement for ggplot2's [geom_density()]. The only difference is that #' the geom draws a ridgeline (line with filled area underneath) rather than a polygon. #' #' @seealso See [geom_density()]. #' @importFrom ggplot2 layer #' @importFrom ggplot2 geom_density #' @inheritParams ggplot2::layer #' @inheritParams ggplot2::geom_density #' @export #' @examples #' library(ggplot2) #' ggplot(diamonds, aes(carat)) + #' geom_density_line() #' #' ggplot(diamonds, aes(carat)) + #' geom_density_line(adjust = 1/5) #' ggplot(diamonds, aes(carat)) + #' geom_density_line(adjust = 5) #' #' ggplot(diamonds, aes(depth, colour = cut)) + #' geom_density_line(alpha = 0.5) + #' xlim(55, 70) #' ggplot(diamonds, aes(depth, fill = cut, colour = cut)) + #' geom_density_line(alpha = 0.1) + #' xlim(55, 70) geom_density_line <- function(mapping = NULL, data = NULL, stat = "density", position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { layer( data = data, mapping = mapping, stat = stat, geom = GeomDensityLine, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, ... ) ) } #' @rdname geom_density_line #' @format NULL #' @usage NULL #' @export GeomDensityLine <- ggproto("GeomDensityLine", GeomRidgeline, required_aes = c("x", "y"), setup_data = function(self, data, params) { if (!"min_height" %in% names(data)){ if (!"min_height" %in% names(params)) data <- cbind(data, min_height = self$default_aes$min_height) else data <- cbind(data, min_height = params$min_height) } transform(data, ymin = 0, ymax = y) } ) ggridges/R/ggridges.R0000644000176200001440000000034314536505332014172 0ustar liggesusers#' Ridgeline plots with ggplot2 #' #' Please see the package vignettes for usage instructions. For a quick start, #' check out the examples for [`geom_density_ridges()`]. #' #' @import ggplot2 #' @keywords internal "_PACKAGE" ggridges/R/geoms-gradient.R0000644000176200001440000004331714536504206015313 0ustar liggesusers#' Plot ridgelines and ridgeline plots with fill gradients along the x axis #' #' The geoms `geom_ridgeline_gradient` and `geom_density_ridges_gradient` work just like [`geom_ridgeline`] and [`geom_density_ridges`] except #' that the `fill` aesthetic can vary along the x axis. Because filling with color gradients is fraught with issues, #' these geoms should be considered experimental. Don't use them unless you really need to. Note that due to limitations #' in R's graphics system, transparency (`alpha`) has to be disabled for gradient fills. #' #' @param mapping Set of aesthetic mappings created by [`aes()`] or #' [`aes_()`]. If specified and `inherit.aes = TRUE` (the #' default), it is combined with the default mapping at the top level of the #' plot. You must supply `mapping` if there is no plot mapping. #' @param data The data to be displayed in this layer. There are three #' options: #' #' If `NULL`, the default, the data is inherited from the plot #' data as specified in the call to [`ggplot()`]. #' #' A `data.frame`, or other object, will override the plot #' data. #' #' A `function` will be called with a single argument, #' the plot data. The return value must be a `data.frame.`, and #' will be used as the layer data. #' @param stat The statistical transformation to use on the data for this #' layer, as a string. #' @param position Position adjustment, either as a string, or the result of #' a call to a position adjustment function. #' @param show.legend logical. Should this layer be included in the legends? #' `NA`, the default, includes if any aesthetics are mapped. #' `FALSE` never includes, and `TRUE` always includes. #' @param inherit.aes If `FALSE`, overrides the default aesthetics, #' rather than combining with them. #' @param na.rm If `FALSE`, the default, missing values are removed with #' a warning. If `TRUE`, missing values are silently removed. #' @param gradient_lwd A parameter to needed to remove rendering artifacts inside the #' rendered gradients. Should ideally be 0, but often needs to be around 0.5 or higher. #' @param ... other arguments passed on to [`layer()`]. These are #' often aesthetics, used to set an aesthetic to a fixed value, like #' `color = "red"` or `linewidth = 3`. They may also be parameters #' to the paired geom/stat. #' #' @examples #' library(ggplot2) #' #' # Example for `geom_ridgeline_gradient()` #' d <- data.frame( #' x = rep(1:5, 3) + c(rep(0, 5), rep(0.3, 5), rep(0.6, 5)), #' y = c(rep(0, 5), rep(1, 5), rep(3, 5)), #' height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1) #' ) #' ggplot(d, aes(x, y, height = height, group = y, fill = factor(x+y))) + #' geom_ridgeline_gradient() + #' scale_fill_viridis_d(direction = -1) + #' theme(legend.position = 'none') #' @importFrom ggplot2 layer #' @export geom_ridgeline_gradient <- function(mapping = NULL, data = NULL, stat = "identity", position = "identity", na.rm = FALSE, gradient_lwd = 0.5, show.legend = NA, inherit.aes = TRUE, ...) { layer( data = data, mapping = mapping, stat = stat, geom = GeomRidgelineGradient, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, gradient_lwd = gradient_lwd, ... ) ) } #' @rdname geom_ridgeline_gradient #' @format NULL #' @usage NULL #' @importFrom ggplot2 ggproto Geom draw_key_polygon #' @export GeomRidgelineGradient <- ggproto("GeomRidgelineGradient", Geom, default_aes = aes( # ridgeline aesthetics color = "black", fill = "grey70", y = 0, linewidth = 0.5, linetype = 1, min_height = 0, scale = 1, alpha = NA, datatype = "ridgeline", # point aesthetics with default point_shape = 19, point_size = 1.5, point_stroke = 0.5, # point aesthetics, inherited point_colour = NULL, #point_color = NULL, point_fill = NULL, point_alpha = NULL, # vline aesthetics, all inherited vline_colour = NULL, #vline_color = NULL, vline_width = NULL, vline_linetype = NULL, vline_size = NULL #<- line size deprecated in ggplot2 3.4.0 ), required_aes = c("x", "y", "height"), optional_aes = c("point_color", "vline_color", "vline_size", "vline_width"), extra_params = c("na.rm", "jittered_points"), setup_data = function(self, data, params) { if (!"scale" %in% names(data)) { if (!"scale" %in% names(params)) data <- cbind(data, scale = self$default_aes$scale) else data <- cbind(data, scale = params$scale) } if (!"min_height" %in% names(data)){ if (!"min_height" %in% names(params)) data <- cbind(data, min_height = self$default_aes$min_height) else data <- cbind(data, min_height = params$min_height) } transform(data, ymin = y, ymax = y + scale*height) }, draw_key = function(data, params, linewidth) { lwd <- min(data$linewidth, min(linewidth) / 4) rect_grob <- grid::rectGrob( width = grid::unit(1, "npc") - grid::unit(lwd, "mm"), height = grid::unit(1, "npc") - grid::unit(lwd, "mm"), gp = grid::gpar( col = data$colour, fill = data$fill, lty = data$linetype, lwd = lwd * .pt, linejoin = "mitre" )) # if vertical lines were drawn then we need to add them to the legend also if (is.null(params$quantile_lines) || !params$quantile_lines) { vlines_grob <- grid::nullGrob() } else { vlines_grob <- grid::segmentsGrob(0.5, 0.1, 0.5, 0.9, gp = grid::gpar( col = data$vline_colour %||% data$vline_color %||% data$colour, lwd = (data$vline_width %||% data$linewidth) * .pt, lty = data$vline_linetype %||% data$linetype, lineend = "butt" ) ) } # if jittered points were drawn then we need to add them to the legend also if (is.null(params$jittered_points) || !params$jittered_points) { point_grob <- grid::nullGrob() } else { point_grob <- grid::pointsGrob(0.5, 0.5, pch = data$point_shape, gp = grid::gpar( col = alpha( data$point_colour %||% data$point_color %||% data$colour, data$point_alpha %||% data$alpha ), fill = alpha( data$point_fill %||% data$fill, data$point_alpha %||% data$alpha ), fontsize = data$point_size * .pt + data$point_stroke * .stroke / 2, lwd = data$point_stroke * .stroke / 2 ) ) } grid::grobTree(rect_grob, vlines_grob, point_grob) }, handle_na = function(data, params) { data }, draw_panel = function(self, data, panel_params, coord, ...) { groups <- split(data, factor(data$group)) # sort list so highest ymin values are in the front # we take a shortcut here and look only at the first ymin value given o <- order(unlist(lapply(groups, function(data){data$ymin[1]})), decreasing = TRUE) groups <- groups[o] grobs <- lapply(groups, function(group) { self$draw_group(group, panel_params, coord, ...) }) ggname(snake_class(self), gTree( children = do.call("gList", grobs) )) }, draw_group = function(self, data, panel_params, coord, na.rm = FALSE, gradient_lwd = 0.5) { if (na.rm) data <- data[stats::complete.cases(data[c("x", "ymin", "ymax")]), ] # split data into data types (ridgeline, vline, point) data_list <- split(data, factor(data$datatype)) point_grob <- self$make_point_grob(data_list[["point"]], panel_params, coord) vline_grob <- self$make_vline_grob(data_list[["vline"]], panel_params, coord) data <- data_list[["ridgeline"]] # if the final data set is empty then we're done here if (is.null(data)) { return(grid::grobTree(vline_grob, point_grob)) } # otherwise, continue. First we order the data, in preparation for polygon drawing data <- data[order(data$group, data$x), ] # remove all points that fall below the minimum height data$ymax[data$height < data$min_height] <- NA # Check that aesthetics are constant aes <- unique(data[c("colour", "linewidth", "linetype")]) if (nrow(aes) > 1) { stop("These aesthetics can not vary along a ridgeline: color, linewidth, linetype") } aes <- as.list(aes) # Instead of removing NA values from the data and plotting a single # polygon, we want to "stop" plotting the polygon whenever we're # missing values and "start" a new polygon as soon as we have new # values. We do this by creating an id vector for polygonGrob that # has distinct polygon numbers for sequences of non-NA values and NA # for NA values in the original data. Example: c(NA, 2, 2, 2, NA, NA, # 4, 4, 4, NA) missing_pos <- !stats::complete.cases(data[c("x", "ymin", "ymax")]) ids <- cumsum(missing_pos) + 1 ids[missing_pos] <- NA data <- cbind(data, ids) data <- data[!missing_pos,] # munching for line positions <- with(data, data.frame( x = x, y = ymax, id = ids )) munched_line <- ggplot2::coord_munch(coord, positions, panel_params) # We now break down the polygons further by fill color, since # we need to draw a separate polygon for each color # calculate all the positions where the fill type changes fillchange <- c(FALSE, data$fill[2:nrow(data)] != data$fill[1:nrow(data)-1]) # and where the id changes idchange <- c(TRUE, data$ids[2:nrow(data)] != data$ids[1:nrow(data)-1]) # make new ids from all changes in fill style or original id data$ids <- cumsum(fillchange | idchange) # get fill color for all ids fill <- data$fill[fillchange | idchange] # append to aes list aes <- c(aes, list(fill=fill)) # rows to be duplicated dupl_rows <- which(fillchange & !idchange) if (length(dupl_rows)>0){ rows <- data[dupl_rows, ] rows$ids <- data$ids[dupl_rows-1] # combine original and duplicated data data <- rbind(data, rows) } # munching for polygon positions <- with(data, data.frame( x = c(x, rev(x)), y = c(ymax, rev(ymin)), id = c(ids, rev(ids)) )) munched_poly <- ggplot2::coord_munch(coord, positions, panel_params) # calculate line and area grobs line_grob <- self$make_line_grob(munched_line, aes) area_grob <- self$make_area_grob(munched_poly, aes, gradient_lwd) # combine everything and return grid::grobTree(area_grob, vline_grob, line_grob, point_grob) }, make_point_grob = function(data, panel_params, coord) { if (is.null(data)) { return(grid::nullGrob()) } data$y <- data$ymin coords <- coord$transform(data, panel_params) ggname("geom_ridgeline_gradient", grid::pointsGrob( coords$x, coords$y, pch = coords$point_shape, gp = grid::gpar( col = alpha( data$point_colour %||% data$point_color %||% data$colour, data$point_alpha %||% data$alpha ), fill = alpha( data$point_fill %||% data$fill, data$point_alpha %||% data$alpha ), # Stroke is added around the outside of the point fontsize = coords$point_size * .pt + coords$point_stroke * .stroke / 2, lwd = coords$point_stroke * .stroke / 2 ) ) ) }, make_vline_grob = function(data, panel_params, coord) { if (is.null(data)) { return(grid::nullGrob()) } data <- check_vline_size(data) data <- check_size(data) data$xend <- data$x data$y <- data$ymin data$yend <- data$ymax data$alpha <- NA # copy vline aesthetics over if set data$colour <- data$vline_colour %||% data$vline_color %||% data$colour data$linetype <- data$vline_linetype %||% data$linetype data$linewidth <- data$vline_width %||% data$linewidth ggplot2::GeomSegment$draw_panel(data, panel_params, coord) }, make_line_grob = function(munched_line, aes) { ggname("geom_ridgeline_gradient", grid::polylineGrob( munched_line$x, munched_line$y, id = munched_line$id, default.units = "native", gp = grid::gpar( col = aes$colour, lwd = aes$linewidth * .pt, lty = aes$linetype) ) ) }, make_area_grob = function(munched_poly, aes, gradient_lwd) { ggname("geom_ridgeline_gradient", grid::polygonGrob( munched_poly$x, munched_poly$y, id = munched_poly$id, default.units = "native", gp = grid::gpar( fill = aes$fill, col = aes$fill, # we need to draw polygons with colored outlines lwd = gradient_lwd, # to prevent drawing artifacts at polygon boundaries lty = 1) ) ) }, make_group_grob_delete = function(munched_line, munched_poly, aes, gradient_lwd) { lg <- ggname("geom_ridgeline_gradient", grid::polylineGrob( munched_line$x, munched_line$y, id = munched_line$id, default.units = "native", gp = grid::gpar( col = aes$colour, lwd = aes$linewidth * .pt, lty = aes$linetype) )) ag <- ggname("geom_ridgeline_gradient", grid::polygonGrob( munched_poly$x, munched_poly$y, id = munched_poly$id, default.units = "native", gp = grid::gpar( fill = aes$fill, col = aes$fill, # we need to draw polygons with colored outlines lwd = gradient_lwd, # to prevent drawing artifacts at polygon boundaries lty = 1) )) grid::grobTree(ag, lg) } ) #' @param panel_scaling Argument only to `geom_density_ridges_gradient`. If `TRUE`, the default, relative scaling is calculated separately #' for each panel. If `FALSE`, relative scaling is calculated globally. #' #' @rdname geom_ridgeline_gradient #' @importFrom ggplot2 layer #' @export geom_density_ridges_gradient <- function(mapping = NULL, data = NULL, stat = "density_ridges", position = "points_sina", panel_scaling = TRUE, na.rm = TRUE, gradient_lwd = 0.5, show.legend = NA, inherit.aes = TRUE, ...) { layer( data = data, mapping = mapping, stat = stat, geom = GeomDensityRidgesGradient, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list( na.rm = na.rm, gradient_lwd = gradient_lwd, panel_scaling = panel_scaling, ... ) ) } #' @rdname geom_ridgeline_gradient #' @format NULL #' @usage NULL #' @importFrom grid gTree gList #' @examples #' #' # Example for `geom_density_ridges_gradient()` #' ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = `Month`, fill = stat(x))) + #' geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) + #' scale_x_continuous(expand = c(0, 0)) + #' scale_y_discrete(expand = c(0, 0)) + #' scale_fill_viridis_c(name = "Temp. [F]", option = "C") + #' coord_cartesian(clip = "off") + #' labs(title = 'Temperatures in Lincoln NE in 2016') + #' theme_ridges(font_size = 13, grid = TRUE) + #' theme(axis.title.y = element_blank()) #' @export GeomDensityRidgesGradient <- ggproto("GeomDensityRidgesGradient", GeomRidgelineGradient, default_aes = aes( # ridgeline aesthetics color = "black", fill = "grey70", linewidth = 0.5, linetype = 1, rel_min_height = 0, scale = 1.8, alpha = NA, datatype = "ridgeline", # point aesthetics with default point_shape = 19, point_size = 1.5, point_stroke = 0.5, # point aesthetics, inherited point_colour = NULL,# point_color = NULL, point_fill = NULL, point_alpha = NULL, # vline aesthetics, all inherited vline_colour = NULL,# vline_color = NULL, vline_width = NULL, vline_linetype = NULL, vline_size = NULL #<- line size deprecated in ggplot2 3.4.0 ), required_aes = c("x", "y", "height"), optional_aes = c("point_color", "vline_color", "vline_size", "vline_width"), extra_params = c("na.rm", "panel_scaling", "jittered_points"), setup_data = function(self, data, params) { params <- check_vline_size_param(params) params <- check_size_param(params) # provide default for panel scaling parameter if it doesn't exist, # happens if the geom is called from a stat if (is.null(params$panel_scaling)) { params$panel_scaling <- TRUE } # calculate internal scale yrange = max(data$y) - min(data$y) n = length(unique(data$y)) if (n<2) { hmax <- max(data$height, na.rm = TRUE) iscale <- 1 } else { # scale per panel or globally? if (params$panel_scaling) { heights <- split(data$height, data$PANEL) max_heights <- vapply(heights, max, numeric(1), na.rm = TRUE) hmax <- max_heights[data$PANEL] iscale <- yrange/((n-1)*hmax) } else { hmax <- max(data$height, na.rm = TRUE) iscale <- yrange/((n-1)*hmax) } } #print(iscale) #print(hmax) data <- cbind(data, iscale) if (!"scale" %in% names(data)) { if (!"scale" %in% names(params)) data <- cbind(data, scale = self$default_aes$scale) else data <- cbind(data, scale = params$scale) } if (!"rel_min_height" %in% names(data)){ if (!"rel_min_height" %in% names(params)) data <- cbind(data, rel_min_height = self$default_aes$rel_min_height) else data <- cbind(data, rel_min_height = params$rel_min_height) } transform(data, ymin = y, ymax = y + iscale*scale*height, min_height = hmax*rel_min_height) } ) ggridges/R/theme.R0000644000176200001440000001057314536504206013506 0ustar liggesusers #' A custom theme specifically for use with ridgeline plots #' #' This theme has some special modifications that make ridgeline plots look better, such as properly aligned y axis labels. #' It can draw plots with and without background grids (see examples). #' #' @param font_size Overall font size. Default is 14. #' @param font_family Default font family. #' @param line_size Default line size. #' @param grid If `TRUE` (default), a background grid is drawn. If `FALSE`, background is left empty. #' @param center_axis_labels If `TRUE`, axis labels are drawn centered. If `FALSE` (default), axis lables are #' drawn right/top-aligned. #' @return The theme. #' @examples #' library(ggplot2) #' #' # Example with background grid #' ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species)) + #' geom_density_ridges(rel_min_height = 0.005) + #' scale_y_discrete(expand = c(0.01, 0)) + #' scale_x_continuous(expand = c(0.01, 0)) + #' theme_ridges() #' #' # Example without background grid #' ggplot(iris, aes(x = Sepal.Length, y = Species, group = Species)) + #' geom_density_ridges() + #' scale_y_discrete(expand = c(0.01, 0)) + #' scale_x_continuous(expand = c(0.01, 0)) + #' theme_ridges(grid = FALSE) #' #' @export theme_ridges <- function(font_size = 14, font_family = "", line_size = .5, grid = TRUE, center_axis_labels = FALSE) { half_line <- font_size / 2 small_rel <- 0.857 small_size <- small_rel * font_size color <- "grey90" if (grid) { panel.grid.major <- element_line(colour = color, linewidth = line_size) axis.ticks <- element_line(colour = color, linewidth = line_size) axis.ticks.y <- axis.ticks } else { panel.grid.major <- element_blank() axis.ticks <- element_line(colour = "black", linewidth = line_size) axis.ticks.y <- element_blank() } if (center_axis_labels) { axis_just <- 0.5 } else { axis_just <- 1.0 } theme_grey(base_size = font_size, base_family = font_family) %+replace% theme( rect = element_rect(fill = "transparent", colour = NA, color = NA, linewidth = 0, linetype = 0), text = element_text(family = font_family, face = "plain", colour = "black", size = font_size, hjust = 0.5, vjust = 0.5, angle = 0, lineheight = .9, margin = margin(), debug = FALSE), axis.text = element_text(colour = "black", size = small_size), #axis.title = element_text(face = "bold"), axis.text.x = element_text(margin = margin(t = small_size / 4), vjust = 1), axis.text.y = element_text(margin = margin(r = small_size / 4), hjust = 1, vjust = 0), axis.title.x = element_text( margin = margin(t = small_size / 2, b = small_size / 4), hjust = axis_just ), axis.title.y = element_text( angle = 90, margin = margin(r = small_size / 2, l = small_size / 4), hjust = axis_just ), axis.ticks = axis.ticks, axis.ticks.y = axis.ticks.y, axis.line = element_blank(), legend.key = element_blank(), legend.key.size = grid::unit(1, "lines"), legend.text = element_text(size = rel(small_rel)), legend.justification = c("left", "center"), panel.background = element_blank(), panel.border = element_blank(), # make grid lines panel.grid.major = panel.grid.major, panel.grid.minor = element_blank(), strip.text = element_text(size = rel(small_rel)), strip.background = element_rect(fill = "grey80", colour = "grey50", linewidth = 0), plot.background = element_blank(), plot.title = element_text(face = "bold", size = font_size, margin = margin(b = half_line), hjust = 0), plot.subtitle = element_text(size = rel(small_rel), hjust = 0, vjust = 1, margin = margin(b = half_line * small_rel)), plot.caption = element_text(size = rel(small_rel), hjust = 1, vjust = 1, margin = margin(t = half_line * small_rel)), plot.margin = margin(half_line, font_size, half_line, half_line), complete = TRUE ) } ggridges/NEWS.md0000644000176200001440000001002714553567672013167 0ustar liggesusersggridges 0.5.6 ---------------------------------------------------------------- - fix issues with upcoming ggplot 3.5.0. ggridges 0.5.5 ---------------------------------------------------------------- - Replace `ggplot2::expand_scale()` with `ggplot2::expansion()` in vignettes to avoid deprecation warnings in [ggplot2 0.3.3](https://ggplot2.tidyverse.org/news/index.html#minor-improvements-and-bug-fixes-3-3-0) (@jthomasmock, #78) - Replace `size` argument in line functions with `linewidth` to avoid deprecation warnings in [ggplot2 0.3.4](https://ggplot2.tidyverse.org/news/index.html#breaking-changes-3-4-0) (@jthomasmock, #78) - Replace use of `..density..` with `ggplot2::after_stat(density)` to avoid deprecation warnings in [ggplot2 0.3.4](https://ggplot2.tidyverse.org/news/index.html#breaking-changes-3-4-0) (@jthomasmock, #78) ggridges 0.5.4 ---------------------------------------------------------------- - Remove broken link in docs. ggridges 0.5.3 ---------------------------------------------------------------- - Make sure tests don't fail if vdiffr is missing. ggridges 0.5.2 ---------------------------------------------------------------- - There is now a project website at https://wilkelab.org/ggridges. - A new example dataset has been added, `Aus_athletes`. - `scale_discrete_manual()` has been removed from the ggridges package, as it has been available in ggplot2 since version 3.0.0. - `stat_density_ridges()` now has a parameter `n` that determines at how many points along the x axis the density is estimated. ggridges 0.5.1 ---------------------------------------------------------------- - The `alpha` aesthetic is now by default applied to jittered points. If you don't want this to happen, set `point_alpha = 1`. - Allow custom function to calculate the position of quantile lines. This makes it possible, for example, to place a line at the mean, via `quantile_fun = mean`. - Allow coloring of quantile lines and jittered points by quantile. ggridges 0.5.0 ---------------------------------------------------------------- - Expanded documentation and gallery of example plots. - Reworked stat_density_ridges, geom_ridgeline, geom_density_ridges, etc. so that jittered points and quantile lines can be drawn on top of the distributions. Points and quantile lines can be styled separately from the rest of the ridgeline plot, via special aesthetics. - Added position options to control how the jittered points are drawn. Default is uniformly spaced within the density area. - Added `geom_density_line()` which is a drop-in replacement for `geom_density()` but draws a ridgeline rather than a closed polygon. - `theme_ridges()` has been modified so that font sizes match the cowplot themes. In particular, this means smaller axis tick labels. ggridges 0.4.1 ---------------------------------------------------------------- - Skip vdiffr visual tests when compiling on CRAN ggridges 0.4.0 ---------------------------------------------------------------- - Initial import of code base from ggjoy, and renaming: geom_joy -> geom_density_ridges stat_joy -> stat_density_ridges theme_joy -> theme_ridges ggjoy 0.3.1 ---------------------------------------------------------------- - Added an option to center axis labels to `theme_joy()`. ggjoy 0.3.0 ---------------------------------------------------------------- - Added cyclical scales that make it easy to alternate between fill colors and other aesthetics. ggjoy 0.2.0 ---------------------------------------------------------------- Numerous improvements: - New stat `binline` that can be used to draw histogram joyplots. - Various improvements in `stat_joy`. In particular, it now works properly with multiple panels. It also now has parameters `from` and `to` to limit the range of density estimation, just like `density()`. - Improvements in the vignettes - New geoms `geom_ridgeline_gradient` and `geom_joy_gradient` that can handle gradient fills. ggjoy 0.1.0 ---------------------------------------------------------------- First complete implementation ready for initial release ggridges/MD50000644000176200001440000001240314553650472012370 0ustar liggesusers5720074ba02c3697e8bd8f5928830bcd *DESCRIPTION a23a74b3f4caf9616230789d94217acb *LICENSE 35ee0e252254477b06165a3522e667ca *NAMESPACE d7361b13d01241e1a1c5b45f0480a7a1 *NEWS.md 600f6c999c2a094c8459493c2afbb6ee *R/data.R f1c95d269d553f1c91c2c413badc067c *R/geom-density-line.R f83bee1536eee1276f618f92ad1ef173 *R/geoms-gradient.R 92cfb1a6ccc4f2248f403543691db4c1 *R/geoms.R 46fd8a18c02428609c187d5756748eed *R/geomsv.R d1be6d96483475950b1ae9f5e5d4c3d0 *R/ggridges.R cb0bd6dd93325524289995e9babde53f *R/position.R 6a46c69a8130645008e2c5ba0dd58081 *R/scale-cyclical.R 23f6fdcdeb6cd71f86d016f6cf2bc4ec *R/scale-point.R 8e13fcd977814ea53ac3c9dcdb5c0c1e *R/scale-vline.R c268d48b5c9589ab7a5c9ef2e3399bde *R/stats.R c1c0747760e41730d4c7f8d05fe3a29d *R/theme.R 187bf58af4eb5e0bbcf59a72c2cc93a0 *R/utils.R 4e7b6b046e9c32177db46516a4b15121 *R/utils_ggplot2.R c4127adc6802b75a84e8bffe1178a157 *README.md a6827d97b9a55b34332eaff78a6477e5 *TODO 048b4faf7c295bb053b7d75124bb9d1e *build/vignette.rds 129d760577f3098da30b7d2e53ef7b71 *data/Aus_athletes.rda ae6a032f21b19edd2822adc7354cab7b *data/Catalan_elections.rda e2635efc0fc194d818e9d815160d91b6 *data/lincoln_weather.rda 610699e98beb8b6bf63809e2d51edf3f *inst/doc/gallery.R b0fcee8e73588b1a89f9661040f8c4cd *inst/doc/gallery.Rmd fa68894efa6d74f85b50d0371b8f5efd *inst/doc/gallery.html c7b79b51ee2e36ec0767ed07a8485734 *inst/doc/introduction.R c6dd17bd1b89943776c59c9c85bec8f2 *inst/doc/introduction.Rmd 6219511f3c5e602aad96f3a5e8f04fd9 *inst/doc/introduction.html b37805aa13716031a54b667ec2357cdf *man/Aus_athletes.Rd 99eb5a4f13789baa5727ee75ffe6b3ab *man/Catalan_elections.Rd 15da9cbfb842a2a1003b980036afb175 *man/figures/README-diamonds-1.png 08f2f7c4afee38581d99827088180035 *man/geom_density_line.Rd 5f0bae84cf893613a3207a62ca4c9012 *man/geom_density_ridges.Rd be0a4bf4bf9e9077b58fc2e51c7abb4f *man/geom_ridgeline.Rd 4fb81eb58ac984d1c1744ae98499388b *man/geom_ridgeline_gradient.Rd 8daeece5a5487187316d347c7c4c306b *man/geom_vridgeline.Rd 4530408318702a3bb759b0b40f71fc8b *man/ggridges-package.Rd c796d747479637fd51985572b3df2ff3 *man/lincoln_weather.Rd 0121458ad7d42d53722086d22cec47c3 *man/position_points_jitter.Rd e85113eabea86cbbf705d80954f85c88 *man/position_points_sina.Rd a98e765c40e1e005e5bc68cee68e7563 *man/position_raincloud.Rd 7d86865972d753401911168b8e792f22 *man/reduce.Rd c450f250ffd34ed5439eeb2ee308cabb *man/scale_cyclical.Rd 0433828694f2e29e7fb0f3d00ceb22a7 *man/scale_point.Rd 1ded3ee358f708f7f62241f836c9e66c *man/scale_vline.Rd 9dc62c2851c6d2b16ee7646f42c52f13 *man/stat_binline.Rd c71cf0d8405c5b5b047db8abc58efac4 *man/stat_density_ridges.Rd 500302139d2b9719629e7d5a88781f50 *man/theme_ridges.Rd 76f9e928d1e06336050fbbf9afa18edd *tests/figs/deps.txt b3c2466e8455bf6950a804551004e250 *tests/figs/geom-density-ridges/geom-density-ridges-basic.svg fcfd5f79cf13263c97589d6aca9bca13 *tests/figs/geom-density-ridges/geom-density-ridges-no-trailing-lines.svg 71ce481733f4cbeb679f89adffeffc53 *tests/figs/geom-density-ridges/geom-density-ridges-scale-3.svg 0bc4803c4c9628caf3d843d8614f9812 *tests/figs/geom-density-ridges/geom-density-ridges2-solid-polygons.svg 2677a8da5b4677a721038871270ce5b1 *tests/figs/geom-gradient/geom-density-ridges-gradient-continuous-fill.svg afa3eac5d48f21f1a9f5df90cc7618fa *tests/figs/geom-gradient/geom-density-ridges-gradient-ecdf-fill.svg bf56a702adbd7c7f1dc29d4d2bcde533 *tests/figs/geom-gradient/geom-density-ridges-gradient-jittered-points-can-be-turned-on.svg b54643adbc609a0a03d01977c59ed48b *tests/figs/geom-gradient/geom-density-ridges-gradient-probability-tails.svg 2cace57a01617baa9e22cee44354379d *tests/figs/geom-gradient/geom-density-ridges-gradient-quantile-lines-match-shading.svg b7e82447043e16329aa2aff3849e59a8 *tests/figs/geom-gradient/geom-density-ridges-gradient-quintiles.svg 358fa65d0e727524f187e6f182321555 *tests/figs/geom-gradient/geom-ridgeline-gradient-basic-fill-pattern.svg 90beb311a5740e8a6398e246aede401f *tests/figs/geom-vridgeline/geom-vridgeline-basic-use.svg c8e7d77da0d8407bde8e9cb89c515fed *tests/figs/geom-vridgeline/geom-vridgeline-using-stat-ydensity.svg 92edf1274914cd6a149299c7b48dfbcf *tests/figs/scale-cyclical/scale-fill-cyclical-red-green-blue-dots-no-legend.svg 501c80ce8e47ea60dc95f344e6b63183 *tests/figs/scale-cyclical/scale-fill-cyclical-red-green-blue-dots-with-legend.svg fd3cf02dcb4af666b9b7feee811ea72b *tests/figs/theme-ridges/theme-ridges-centered-axis-labels.svg aa9fbc6a7b786e124279249a3527af75 *tests/figs/theme-ridges/theme-ridges-default.svg 1d7ae4c4687f5a87a80ee067023776bc *tests/figs/theme-ridges/theme-ridges-without-grid.svg 3f388ab80be34273aa399278dd3f7a30 *tests/testthat.R d9a7d5e06f4322eac9e14fddf2d6c974 *tests/testthat/helper-vdiffr.R 7384499684ac2d1b4a2788192aecd22a *tests/testthat/test_geom_density_ridges.R 8e739d6de46b6e0083b18b54caddd459 *tests/testthat/test_geom_gradient.R 1fdb92a7f5c0d5927e175b0952ee1aa1 *tests/testthat/test_geom_vridgeline.R b17d943f749c5b29f2e17aef4137e2a7 *tests/testthat/test_scale_cyclical.R c43d6abcb71872b2a2743cd886f6561a *tests/testthat/test_stat_binline.R 9b0e54d9ac3c769afead970a06ec5ef4 *tests/testthat/test_stat_density_ridges.R f88007e2900e87f9d4f80f92e7678499 *tests/testthat/test_theme_ridges.R 9a93a481b7411f8a456c009f4a7f1a79 *tests/testthat/test_utils.R b0fcee8e73588b1a89f9661040f8c4cd *vignettes/gallery.Rmd c6dd17bd1b89943776c59c9c85bec8f2 *vignettes/introduction.Rmd ggridges/inst/0000755000176200001440000000000014553613063013030 5ustar liggesusersggridges/inst/doc/0000755000176200001440000000000014553613063013575 5ustar liggesusersggridges/inst/doc/introduction.Rmd0000644000176200001440000005004014536504206016760 0ustar liggesusers--- title: "Introduction to ggridges" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: fig_width: 6 fig_height: 4 vignette: > %\VignetteIndexEntry{Introduction to ggridges} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- Ridgeline plots are partially overlapping line plots that create the impression of a mountain range. They can be quite useful for visualizing changes in distributions over time or space. ## Geoms The **ggridges** package provides two main geoms, `geom_ridgeline` and `geom_density_ridges`. The former takes height values directly to draw ridgelines, and the latter first estimates data densities and then draws those using ridgelines. ### Ridgelines The geom `geom_ridgeline` can be used to draw lines with a filled area underneath. ```{r warning = FALSE, message = FALSE} library(ggplot2) library(ggridges) data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, 3, 4, 2)) ggplot(data, aes(x, y, height = height)) + geom_ridgeline() ``` Negative heights are allowed, but are cut off unless the `min_height` parameter is set negative as well. ```{r message = FALSE, fig.width=9, fig.height=3} library(patchwork) # for side-by-side plotting data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, -1, 3, 2)) plot_base <- ggplot(data, aes(x, y, height = height)) plot_base + geom_ridgeline() | plot_base + geom_ridgeline(min_height = -2) ``` Multiple ridgelines can be drawn at the same time. They will be ordered such that the ones drawn higher up are in the background. When drawing multiple ridgelines at once, the `group` aesthetic must be specified so that the geom knows which parts of the data belong to which ridgeline. ```{r message = FALSE} d <- data.frame( x = rep(1:5, 3), y = c(rep(0, 5), rep(1, 5), rep(2, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1) ) ggplot(d, aes(x, y, height = height, group = y)) + geom_ridgeline(fill = "lightblue") ``` It is also possible to draw ridgelines with `geom_density_ridges` if we set `stat = "identity"`. In this case, the heights are automatically scaled such that the highest ridgeline just touches the one above at `scale = 1`. ```{r message = FALSE} ggplot(d, aes(x, y, height = height, group = y)) + geom_density_ridges(stat = "identity", scale = 1) ``` ### Density ridgeline plots The geom `geom_density_ridges` calculates density estimates from the provided data and then plots those, using the ridgeline visualization. The `height` aesthetic does not need to be specified in this case. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() ``` There is also `geom_density_ridges2`, which is identical to `geom_density_ridges` except it uses closed polygons instead of ridgelines for drawing. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges2() ``` The grouping aesthetic does not need to be provided if a categorical variable is mapped onto the y axis, but it does need to be provided if the variable is numerical. ```{r message=FALSE} # modified dataset that represents species as a number iris_num <- transform(iris, Species_num = as.numeric(Species)) # does not work, causes error # ggplot(iris_num, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() # works ggplot(iris_num, aes(x = Sepal.Length, y = Species_num, group = Species_num)) + geom_density_ridges() ``` Trailing tails can be cut off using the `rel_min_height` aesthetic. This aesthetic sets a percent cutoff relative to the highest point of any of the density curves. A value of 0.01 usually works well, but you may have to modify this parameter for different datasets. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(rel_min_height = 0.01) ``` The extent to which the different densities overlap can be controlled with the `scale` parameter. A setting of `scale=1` means the tallest density curve just touches the baseline of the next higher one. Smaller values create a separation between the curves, and larger values create more overlap. ```{r message=FALSE} # scale = 0.9, not quite touching ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 0.9) # scale = 1, exactly touching ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1) # scale = 5, substantial overlap ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 5) ``` The scaling is calculated separately per panel, so if we facet-wrap by species each density curve exactly touches the next higher baseline. (This can be disabled by setting `panel_scaling = FALSE`.) ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1) + facet_wrap(~Species) ``` ### Varying fill colors along the x axis Sometimes we would like to have the area under a ridgeline not filled with a single solid color but rather with colors that vary in some form along the x axis. This effect can be achieved with the geoms `geom_ridgeline_gradient` and `geom_density_ridges_gradient`. Both geoms work just like `geom_ridgeline` and `geom_density_ridges`, except that they allow for varying fill colors. **However,** they do not allow for alpha transparency in the fill. For technical reasons, we can have changing fill colors or transparency but not both. Here is a simple example of changing fill colors with `geom_ridgeline_gradient`: ```{r message = FALSE} d <- data.frame( x = rep(1:5, 3) + c(rep(0, 5), rep(0.3, 5), rep(0.6, 5)), y = c(rep(0, 5), rep(1, 5), rep(3, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) ggplot(d, aes(x, y, height = height, group = y, fill = factor(x+y))) + geom_ridgeline_gradient() + scale_fill_viridis_d(direction = -1, guide = "none") ``` And here is an example using `geom_density_ridges_gradient`. Note that we need to map the calculated x value (`stat(x)`) onto the fill aesthetic, not the original temperature variable. This is the case because `geom_density_ridges_gradient` calls `stat_density_ridges` (described in the next section) which calculates new x values as part of its density calculation. ```{r message = FALSE} ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) + geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) + scale_fill_viridis_c(name = "Temp. [F]", option = "C") + labs(title = 'Temperatures in Lincoln NE in 2016') ``` ## Stats The ggridges package provides a stat `stat_density_ridges` that replaces `stat_density` in the context of ridgeline plots. In addition to setting up the proper `height` for `geom_density_ridges`, this stat has a number of additional features that may be useful. ### Quantile lines and coloring by quantiles or probabilities By setting the option `quantile_lines = TRUE`, we can make `stat_density_ridges` calculate the position of lines indicating quantiles. By default, three lines are drawn, corresponding to the first, second, and third quartile: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE) ``` We can change the number of quantiles by specifying it via the `quantiles` option. Note that `quantiles = 2` implies one line (the median) at the boundary between the two quantiles. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE, quantiles = 2) ``` We can also specify quantiles by cut points rather than number. E.g., we can indicate the 2.5% and 97.5% tails. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE, quantiles = c(0.025, 0.975), alpha = 0.7) ``` Using the geom `geom_density_ridges_gradient` we can also color by quantile, via the calculated `stat(quantile)` aesthetic. Note that this aesthetic is only calculated if `calc_ecdf = TRUE`. ```{r message = FALSE} ggplot(iris, aes(x=Sepal.Length, y=Species, fill = factor(stat(quantile)))) + stat_density_ridges( geom = "density_ridges_gradient", calc_ecdf = TRUE, quantiles = 4, quantile_lines = TRUE ) + scale_fill_viridis_d(name = "Quartiles") ``` We can use the same approach to highlight the tails of the distributions. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = factor(stat(quantile)))) + stat_density_ridges( geom = "density_ridges_gradient", calc_ecdf = TRUE, quantiles = c(0.025, 0.975) ) + scale_fill_manual( name = "Probability", values = c("#FF0000A0", "#A0A0A0A0", "#0000FFA0"), labels = c("(0, 0.025]", "(0.025, 0.975]", "(0.975, 1]") ) ``` Finally, when `calc_ecdf = TRUE`, we also have access to a calculated aesthetic `stat(ecdf)`, which represents the empirical cumulative density function for the distribution. This allows us to map the probabilities directly onto color. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = 0.5 - abs(0.5 - stat(ecdf)))) + stat_density_ridges(geom = "density_ridges_gradient", calc_ecdf = TRUE) + scale_fill_viridis_c(name = "Tail probability", direction = -1) ``` ### Jittering points The stat `stat_density_ridges` also provides the option to visualize the original data points from which the distributions are generated. This can be done by setting `jittered_points = TRUE`, either in `stat_density_ridges` or in `geom_density_ridges`: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(jittered_points = TRUE) ``` Where the points are shown can be controlled with position options, e.g. "raincloud" for the raincloud effect: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, position = "raincloud", alpha = 0.7, scale = 0.9 ) ``` We can also simulate a rug: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, position = position_points_jitter(width = 0.05, height = 0), point_shape = '|', point_size = 3, point_alpha = 1, alpha = 0.7, ) ``` Note that we are using `position_points_jitter()` here, not `position_jitter()`. We do this because `position_points_jitter()` knows to jitter only the points in a ridgeline plot, without touching the density lines. Styling the jittered points is a bit tricky but is possible with special scales provided by ggridges. First, there is `scale_discrete_manual()` which can be used to make arbitrary discrete scales for arbitrary aesthetics. We use it in the next example to style the point shapes. Second, there are various point aesthetic scales, such as `scale_point_color_hue()`. See the reference documentation for these scales for more details. ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) + geom_density_ridges( aes(point_color = Species, point_fill = Species, point_shape = Species), alpha = .2, point_alpha = 1, jittered_points = TRUE ) + scale_point_color_hue(l = 40) + scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23)) ``` All common aesthetics for points can be applied to the jittered points. However, the aesthetic names start with `point_`. In the next example, we have mapped an additional variable onto the size of the points. ```{r message = FALSE, fig.width = 6, fig.height = 6} ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) + geom_density_ridges( aes(point_shape = Species, point_fill = Species, point_size = Petal.Length), alpha = .2, point_alpha = 1, jittered_points = TRUE ) + scale_point_color_hue(l = 40) + scale_point_size_continuous(range = c(0.5, 4)) + scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23)) ``` Similarly, we have aesthetics for the vertical lines, named `vline_`. And the vertical lines can also be shifted so they are aligned with the jittered points. This allows us to generate figures such as the following: ```{r message = FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, quantile_lines = TRUE, scale = 0.9, alpha = 0.7, vline_size = 1, vline_color = "red", point_size = 0.4, point_alpha = 1, position = position_raincloud(adjust_vlines = TRUE) ) ``` ### Using alternative stats The stat `stat_density_ridges` may not always do exactly what you want it to do. If this is the case, you can use other stats that may be better for your respective application. First, `stat_density_ridges` estimates the data range and bandwidth for the density estimation from the entire data at once, rather than from each individual group of data. This choice makes ridgeline plots look more uniform, but the density estimates can in some cases look quite different from what you would get from `geom_density` or `stat_density`. This problem can be remedied by using `stat_density` with `geom_density_ridges`. This works just fine, we just need to make sure that we map the calculated density onto the `height` aesthetic. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + geom_density_ridges(stat = "density") ``` Second, there may be scenarios in which you don't want `geom_density_ridges` to do any density estimation, for example because you have done so already yourself. In this case, you can use `stat_identity`. The benefit of using `geom_density_ridges` with `stat_identiy` over using `geom_ridgeline` directly is that `geom_density_ridges` provides automatic scaling. As an example, assume we have calculated density curves for the `Sepal.Length` column in the `iris` dataset: ```{r message=FALSE} library(dplyr) iris_densities <- iris %>% group_by(Species) %>% group_modify(~ ggplot2:::compute_density(.x$Sepal.Length, NULL)) %>% rename(Sepal.Length = x) iris_densities ``` We can plot these as follows: ```{r message=FALSE} ggplot(iris_densities, aes(x = Sepal.Length, y = Species, height = density)) + geom_density_ridges(stat = "identity") ``` Notice how this plot looks different from the one generated using `stat = "density"`, even though the density computation was exactly the same: (i) The density curves extend all the way to zero. (ii) There is no horizontal line extending all the way to the limits of the x axis. Finally, if you prefer histograms to density plots, you can also use `stat_binline`. Note that overlapping histograms can look strange, so this option is probably best used with a `scale` parameter < 1. The option `draw_baseline = FALSE` removes trailing lines to either side of the histogram. (For histograms, the `rel_min_height` parameter doesn't work very well.) ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + geom_density_ridges(stat = "binline", bins = 20, scale = 0.95, draw_baseline = FALSE) ``` ## Themes ridgeline plots tend to require some theme modifications to look good. Most importantly, the y-axis tick labels should be vertically aligned so that they are flush with the axis ticks rather than vertically centered. The ggridges package provides a theme `theme_ridges` that does this and a few other theme modifications. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + theme_ridges() ``` However, without any further modifications, there are still a few issues with this plot. First, the ridgeline for the virginica species is slightly cut off at the very top point. Second, the space between the x and y axis labels and the ridgelines is too large. We can fix both issues using the `expand` option for the axis scales. ```{r message=FALSE, warning=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expand_scale(mult = c(0.01, .7))) + theme_ridges() ``` Instead of expanding the axis, you can also turn off clipping for the plot panel. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges() ``` By default, `theme_ridges` adds a grid, but the grid can be switched off when not needed. Also, axis titles can be centered. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE, center_axis_labels = TRUE) ``` If you prefer to use a different theme than `theme_ridges`, for example `theme_minimal`, it is still advisable to adjust the alignment of the axis tick labels and the axis scales. ```{r message=FALSE} ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_minimal(base_size = 14) + theme(axis.text.y = element_text(vjust = 0)) ``` ## Cyclical scales Many ridgeline plots improve in appearance if the filled areas are drawn with alternating colors. To simplify the generation of such plots, **ggridges** provides cyclical scales. These are scales that cycle through the aesthetic values provided. For example, if we use `scale_fill_cyclical(values = c("blue", "green"))` then `ggplot` will cycle through these two fill colors throughout the plot. ```{r message=FALSE} ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical(values = c("blue", "green")) ``` By default, the cyclical scales will not draw a legend, because the legend will usually be confusing unless the labels are manually altered. Legends can be switched on via the `guide = "legend"` option, just like for all other scales. ```{r message=FALSE, fig.width = 5.5} ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical(values = c("blue", "green"), guide = "legend") ``` Legends can be modified as usual. ```{r message=FALSE, fig.width = 5.5} ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical( name = "Fill colors", values = c("blue", "green"), labels = c("Fair" = "blue", "Good" = "green"), guide = "legend" ) ``` Cyclical scales are defined for all the common aesthetics one might want to change, such as color, size, alpha, and linetype, and the legends are combined when possible ```{r message=FALSE, fig.width = 6.5} ggplot(diamonds, aes(x = price, y = cut, fill = cut, color = cut)) + geom_density_ridges(scale = 4, size = 1) + scale_fill_cyclical( name = "Color scheme", values = c("blue", "green"), guide = "legend", labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline") ) + scale_color_cyclical( name = "Color scheme", values = c("black", "yellow"), guide = "legend", labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline") ) ``` Because these cyclical scales are generic **ggplot2** scales, they work with any geom that accepts the respective aesthetic. Thus, for example, we can make histograms with alternatingly colored bars. ```{r message=FALSE, fig.width = 6.5} ggplot(mpg, aes(x = class, fill = class, color = class)) + geom_bar(size = 1.5) + scale_fill_cyclical( name = "Color scheme", values = c("blue", "green"), guide = "legend", labels = c("blue w/ black outline", "green w/ yellow outline") ) + scale_color_cyclical( name = "Color scheme", values = c("black", "yellow"), guide = "legend", labels = c("blue w/ black outline", "green w/ yellow outline") ) ``` While the previous example won't win any design awards, more subtle effects can be helpful. ```{r message=FALSE, fig.width=5.5} mpg %>% group_by(class) %>% tally() %>% arrange(desc(n)) %>% mutate(class = factor(class, levels = class)) %>% ggplot(aes(x = class, y = n, fill = class)) + geom_col() + scale_fill_cyclical(values = c("#4040B0", "#9090F0")) + scale_y_continuous(expand = c(0, 0)) + theme_minimal() ``` ggridges/inst/doc/gallery.R0000644000176200001440000001254214553613057015366 0ustar liggesusers## ----echo=FALSE, include=FALSE------------------------------------------------ library(ggplot2) library(ggridges) ## ----message=FALSE, warning=FALSE, fig.width = 6, fig.height = 6-------------- library(ggplot2movies) ggplot(movies[movies$year>1912,], aes(x = length, y = year, group = year)) + geom_density_ridges(scale = 10, size = 0.25, rel_min_height = 0.03) + theme_ridges() + scale_x_continuous(limits = c(1, 200), expand = c(0, 0)) + scale_y_reverse( breaks = c(2000, 1980, 1960, 1940, 1920, 1900), expand = c(0, 0) ) + coord_cartesian(clip = "off") ## ----message=FALSE, warning=FALSE, fig.width = 6, fig.height = 8-------------- library(dplyr) library(forcats) Catalan_elections %>% mutate(YearFct = fct_rev(as.factor(Year))) %>% ggplot(aes(y = YearFct)) + geom_density_ridges( aes(x = Percent, fill = paste(YearFct, Option)), alpha = .8, color = "white", from = 0, to = 100 ) + labs( x = "Vote (%)", y = "Election Year", title = "Indy vs Unionist vote in Catalan elections", subtitle = "Analysis unit: municipalities (n = 949)", caption = "Marc Belzunces (@marcbeldata) | Source: Idescat" ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0)) + scale_fill_cyclical( breaks = c("1980 Indy", "1980 Unionist"), labels = c(`1980 Indy` = "Indy", `1980 Unionist` = "Unionist"), values = c("#ff0000", "#0000ff", "#ff8080", "#8080ff"), name = "Option", guide = "legend" ) + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE) ## ----message=FALSE, warning=FALSE, fig.width = 7.5, fig.height = 5------------ ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) + geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01, gradient_lwd = 1.) + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) + scale_fill_viridis_c(name = "Temp. [F]", option = "C") + labs( title = 'Temperatures in Lincoln NE', subtitle = 'Mean temperatures (Fahrenheit) by month for 2016' ) + theme_ridges(font_size = 13, grid = TRUE) + theme(axis.title.y = element_blank()) ## ----message=FALSE, warning=FALSE, fig.width = 6, fig.height = 7-------------- # generate data set.seed(1234) pois_data <- data.frame(mean = rep(1:5, each = 10)) pois_data$group <- factor(pois_data$mean, levels = 5:1) pois_data$value <- rpois(nrow(pois_data), pois_data$mean) # make plot ggplot(pois_data, aes(x = value, y = group, group = group)) + geom_density_ridges2(aes(fill = group), stat = "binline", binwidth = 1, scale = 0.95) + geom_text( stat = "bin", aes( y = group + 0.95*stat(count/max(count)), label = ifelse(stat(count) > 0, stat(count), "") ), vjust = 1.4, size = 3, color = "white", binwidth = 1 ) + scale_x_continuous( breaks = c(0:12), limits = c(-.5, 13), expand = c(0, 0), name = "random value" ) + scale_y_discrete( expand = expansion(add = c(0, 1.)), name = "Poisson mean", labels = c("5.0", "4.0", "3.0", "2.0", "1.0") ) + scale_fill_cyclical(values = c("#0000B0", "#7070D0")) + labs( title = "Poisson random samples with different means", subtitle = "sample size n=10" ) + guides(y = "none") + theme_ridges(grid = FALSE) + theme( axis.title.x = element_text(hjust = 0.5), axis.title.y = element_text(hjust = 0.5) ) ## ----message=FALSE, fig.width = 7, fig.height = 5.5--------------------------- ggplot(Aus_athletes, aes(x = height, y = sport, color = sex, point_color = sex, fill = sex)) + geom_density_ridges( jittered_points = TRUE, scale = .95, rel_min_height = .01, point_shape = "|", point_size = 3, size = 0.25, position = position_points_jitter(height = 0) ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0), name = "height [cm]") + scale_fill_manual(values = c("#D55E0050", "#0072B250"), labels = c("female", "male")) + scale_color_manual(values = c("#D55E00", "#0072B2"), guide = "none") + scale_discrete_manual("point_color", values = c("#D55E00", "#0072B2"), guide = "none") + coord_cartesian(clip = "off") + guides(fill = guide_legend( override.aes = list( fill = c("#D55E00A0", "#0072B2A0"), color = NA, point_color = NA) ) ) + ggtitle("Height in Australian athletes") + theme_ridges(center = TRUE) ## ----message=FALSE, fig.width = 6, fig.height = 5----------------------------- set.seed(423) n1 <- 200 n2 <- 25 n3 <- 50 cols <- c('#F2DB2F', '#F7F19E', '#FBF186') cols_dark <- c("#D7C32F", "#DBD68C", "#DFD672") cheese <- data.frame( cheese = c(rep("buttercheese", n1), rep("Leerdammer", n2), rep("Swiss", n3)), x = c(runif(n1), runif(n2), runif(n3)), size = c( rnorm(n1, mean = .1, sd = .01), rnorm(n2, mean = 9, sd = 3), rnorm(n3, mean = 3, sd = 1) ) ) ggplot(cheese, aes(x = x, point_size = size, y = cheese, fill = cheese, color = cheese)) + geom_density_ridges( jittered_points = TRUE, point_color="white", scale = .8, rel_min_height = .2, size = 1.5 ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(limits = c(0, 1), expand = c(0, 0), name = "", breaks = NULL) + scale_point_size_continuous(range = c(0.01, 10), guide = "none") + scale_fill_manual(values = cols, guide = "none") + scale_color_manual(values = cols_dark, guide = "none") + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE, center = TRUE) ggridges/inst/doc/gallery.Rmd0000644000176200001440000001431614536504206015704 0ustar liggesusers--- title: "Gallery of ggridges examples" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: fig_width: 4.5 fig_height: 3 vignette: > %\VignetteIndexEntry{Gallery of ggridges examples} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- ```{r echo=FALSE, include=FALSE} library(ggplot2) library(ggridges) ``` ## Evolution of movie lengths over time Data from the IMDB, as provided in the ggplot2movies package. ```{r message=FALSE, warning=FALSE, fig.width = 6, fig.height = 6} library(ggplot2movies) ggplot(movies[movies$year>1912,], aes(x = length, y = year, group = year)) + geom_density_ridges(scale = 10, size = 0.25, rel_min_height = 0.03) + theme_ridges() + scale_x_continuous(limits = c(1, 200), expand = c(0, 0)) + scale_y_reverse( breaks = c(2000, 1980, 1960, 1940, 1920, 1900), expand = c(0, 0) ) + coord_cartesian(clip = "off") ``` ## Results from Catalan regional elections, 1980-2015 Modified after a figure originally created by Marc Belzunces (@marcbeldata on Twitter). ```{r message=FALSE, warning=FALSE, fig.width = 6, fig.height = 8} library(dplyr) library(forcats) Catalan_elections %>% mutate(YearFct = fct_rev(as.factor(Year))) %>% ggplot(aes(y = YearFct)) + geom_density_ridges( aes(x = Percent, fill = paste(YearFct, Option)), alpha = .8, color = "white", from = 0, to = 100 ) + labs( x = "Vote (%)", y = "Election Year", title = "Indy vs Unionist vote in Catalan elections", subtitle = "Analysis unit: municipalities (n = 949)", caption = "Marc Belzunces (@marcbeldata) | Source: Idescat" ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0)) + scale_fill_cyclical( breaks = c("1980 Indy", "1980 Unionist"), labels = c(`1980 Indy` = "Indy", `1980 Unionist` = "Unionist"), values = c("#ff0000", "#0000ff", "#ff8080", "#8080ff"), name = "Option", guide = "legend" ) + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE) ``` ## Temperatures in Lincoln, Nebraska Modified from a [blog post](https://austinwehrwein.com/data-visualization/it-brings-me-ggjoy/) by Austin Wehrwein. ```{r message=FALSE, warning=FALSE, fig.width = 7.5, fig.height = 5} ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) + geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01, gradient_lwd = 1.) + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) + scale_fill_viridis_c(name = "Temp. [F]", option = "C") + labs( title = 'Temperatures in Lincoln NE', subtitle = 'Mean temperatures (Fahrenheit) by month for 2016' ) + theme_ridges(font_size = 13, grid = TRUE) + theme(axis.title.y = element_blank()) ``` ## Visualization of Poisson random samples with different means Inspired by a ggridges example by Noam Ross (twitter.com/noamross/status/888405434381545472). ```{r message=FALSE, warning=FALSE, fig.width = 6, fig.height = 7} # generate data set.seed(1234) pois_data <- data.frame(mean = rep(1:5, each = 10)) pois_data$group <- factor(pois_data$mean, levels = 5:1) pois_data$value <- rpois(nrow(pois_data), pois_data$mean) # make plot ggplot(pois_data, aes(x = value, y = group, group = group)) + geom_density_ridges2(aes(fill = group), stat = "binline", binwidth = 1, scale = 0.95) + geom_text( stat = "bin", aes( y = group + 0.95*stat(count/max(count)), label = ifelse(stat(count) > 0, stat(count), "") ), vjust = 1.4, size = 3, color = "white", binwidth = 1 ) + scale_x_continuous( breaks = c(0:12), limits = c(-.5, 13), expand = c(0, 0), name = "random value" ) + scale_y_discrete( expand = expansion(add = c(0, 1.)), name = "Poisson mean", labels = c("5.0", "4.0", "3.0", "2.0", "1.0") ) + scale_fill_cyclical(values = c("#0000B0", "#7070D0")) + labs( title = "Poisson random samples with different means", subtitle = "sample size n=10" ) + guides(y = "none") + theme_ridges(grid = FALSE) + theme( axis.title.x = element_text(hjust = 0.5), axis.title.y = element_text(hjust = 0.5) ) ``` ## Height of Australian athletes ```{r message=FALSE, fig.width = 7, fig.height = 5.5} ggplot(Aus_athletes, aes(x = height, y = sport, color = sex, point_color = sex, fill = sex)) + geom_density_ridges( jittered_points = TRUE, scale = .95, rel_min_height = .01, point_shape = "|", point_size = 3, size = 0.25, position = position_points_jitter(height = 0) ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(expand = c(0, 0), name = "height [cm]") + scale_fill_manual(values = c("#D55E0050", "#0072B250"), labels = c("female", "male")) + scale_color_manual(values = c("#D55E00", "#0072B2"), guide = "none") + scale_discrete_manual("point_color", values = c("#D55E00", "#0072B2"), guide = "none") + coord_cartesian(clip = "off") + guides(fill = guide_legend( override.aes = list( fill = c("#D55E00A0", "#0072B2A0"), color = NA, point_color = NA) ) ) + ggtitle("Height in Australian athletes") + theme_ridges(center = TRUE) ``` ## A cheese plot Inspired by a tweet by Leonard Kiefer (twitter.com/lenkiefer/status/932237461337575429). ```{r message=FALSE, fig.width = 6, fig.height = 5} set.seed(423) n1 <- 200 n2 <- 25 n3 <- 50 cols <- c('#F2DB2F', '#F7F19E', '#FBF186') cols_dark <- c("#D7C32F", "#DBD68C", "#DFD672") cheese <- data.frame( cheese = c(rep("buttercheese", n1), rep("Leerdammer", n2), rep("Swiss", n3)), x = c(runif(n1), runif(n2), runif(n3)), size = c( rnorm(n1, mean = .1, sd = .01), rnorm(n2, mean = 9, sd = 3), rnorm(n3, mean = 3, sd = 1) ) ) ggplot(cheese, aes(x = x, point_size = size, y = cheese, fill = cheese, color = cheese)) + geom_density_ridges( jittered_points = TRUE, point_color="white", scale = .8, rel_min_height = .2, size = 1.5 ) + scale_y_discrete(expand = c(0, 0)) + scale_x_continuous(limits = c(0, 1), expand = c(0, 0), name = "", breaks = NULL) + scale_point_size_continuous(range = c(0.01, 10), guide = "none") + scale_fill_manual(values = cols, guide = "none") + scale_color_manual(values = cols_dark, guide = "none") + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE, center = TRUE) ``` ggridges/inst/doc/introduction.html0000644000176200001440000675603114553613063017226 0ustar liggesusers Introduction to ggridges

Introduction to ggridges

Claus O. Wilke

2024-01-22

Ridgeline plots are partially overlapping line plots that create the impression of a mountain range. They can be quite useful for visualizing changes in distributions over time or space.

Geoms

The ggridges package provides two main geoms, geom_ridgeline and geom_density_ridges. The former takes height values directly to draw ridgelines, and the latter first estimates data densities and then draws those using ridgelines.

Ridgelines

The geom geom_ridgeline can be used to draw lines with a filled area underneath.

library(ggplot2)
library(ggridges)

data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, 3, 4, 2))
ggplot(data, aes(x, y, height = height)) + geom_ridgeline()

Negative heights are allowed, but are cut off unless the min_height parameter is set negative as well.

library(patchwork) # for side-by-side plotting

data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, -1, 3, 2))
plot_base <- ggplot(data, aes(x, y, height = height))

plot_base + geom_ridgeline() | plot_base + geom_ridgeline(min_height = -2)

Multiple ridgelines can be drawn at the same time. They will be ordered such that the ones drawn higher up are in the background. When drawing multiple ridgelines at once, the group aesthetic must be specified so that the geom knows which parts of the data belong to which ridgeline.

d <- data.frame(
  x = rep(1:5, 3),
  y = c(rep(0, 5), rep(1, 5), rep(2, 5)),
  height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)
)

ggplot(d, aes(x, y, height = height, group = y)) + 
  geom_ridgeline(fill = "lightblue")

It is also possible to draw ridgelines with geom_density_ridges if we set stat = "identity". In this case, the heights are automatically scaled such that the highest ridgeline just touches the one above at scale = 1.

ggplot(d, aes(x, y, height = height, group = y)) + 
  geom_density_ridges(stat = "identity", scale = 1)

Density ridgeline plots

The geom geom_density_ridges calculates density estimates from the provided data and then plots those, using the ridgeline visualization. The height aesthetic does not need to be specified in this case.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges()

There is also geom_density_ridges2, which is identical to geom_density_ridges except it uses closed polygons instead of ridgelines for drawing.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges2()

The grouping aesthetic does not need to be provided if a categorical variable is mapped onto the y axis, but it does need to be provided if the variable is numerical.

# modified dataset that represents species as a number
iris_num <- transform(iris, Species_num = as.numeric(Species))

# does not work, causes error
# ggplot(iris_num, aes(x = Sepal.Length, y = Species)) + geom_density_ridges()

# works 
ggplot(iris_num, aes(x = Sepal.Length, y = Species_num, group = Species_num)) + 
  geom_density_ridges()

Trailing tails can be cut off using the rel_min_height aesthetic. This aesthetic sets a percent cutoff relative to the highest point of any of the density curves. A value of 0.01 usually works well, but you may have to modify this parameter for different datasets.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
  geom_density_ridges(rel_min_height = 0.01)

The extent to which the different densities overlap can be controlled with the scale parameter. A setting of scale=1 means the tallest density curve just touches the baseline of the next higher one. Smaller values create a separation between the curves, and larger values create more overlap.

# scale = 0.9, not quite touching
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 0.9)

# scale = 1, exactly touching
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1)

# scale = 5, substantial overlap
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 5)

The scaling is calculated separately per panel, so if we facet-wrap by species each density curve exactly touches the next higher baseline. (This can be disabled by setting panel_scaling = FALSE.)

ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
  geom_density_ridges(scale = 1) + facet_wrap(~Species)

Varying fill colors along the x axis

Sometimes we would like to have the area under a ridgeline not filled with a single solid color but rather with colors that vary in some form along the x axis. This effect can be achieved with the geoms geom_ridgeline_gradient and geom_density_ridges_gradient. Both geoms work just like geom_ridgeline and geom_density_ridges, except that they allow for varying fill colors. However, they do not allow for alpha transparency in the fill. For technical reasons, we can have changing fill colors or transparency but not both.

Here is a simple example of changing fill colors with geom_ridgeline_gradient:

d <- data.frame(
  x = rep(1:5, 3) + c(rep(0, 5), rep(0.3, 5), rep(0.6, 5)),
  y = c(rep(0, 5), rep(1, 5), rep(3, 5)),
  height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1))

ggplot(d, aes(x, y, height = height, group = y, fill = factor(x+y))) +
  geom_ridgeline_gradient() +
  scale_fill_viridis_d(direction = -1, guide = "none")

And here is an example using geom_density_ridges_gradient. Note that we need to map the calculated x value (stat(x)) onto the fill aesthetic, not the original temperature variable. This is the case because geom_density_ridges_gradient calls stat_density_ridges (described in the next section) which calculates new x values as part of its density calculation.

ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) +
  geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) +
  scale_fill_viridis_c(name = "Temp. [F]", option = "C") +
  labs(title = 'Temperatures in Lincoln NE in 2016')

Stats

The ggridges package provides a stat stat_density_ridges that replaces stat_density in the context of ridgeline plots. In addition to setting up the proper height for geom_density_ridges, this stat has a number of additional features that may be useful.

Quantile lines and coloring by quantiles or probabilities

By setting the option quantile_lines = TRUE, we can make stat_density_ridges calculate the position of lines indicating quantiles. By default, three lines are drawn, corresponding to the first, second, and third quartile:

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  stat_density_ridges(quantile_lines = TRUE)

We can change the number of quantiles by specifying it via the quantiles option. Note that quantiles = 2 implies one line (the median) at the boundary between the two quantiles.

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  stat_density_ridges(quantile_lines = TRUE, quantiles = 2)

We can also specify quantiles by cut points rather than number. E.g., we can indicate the 2.5% and 97.5% tails.

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  stat_density_ridges(quantile_lines = TRUE, quantiles = c(0.025, 0.975), alpha = 0.7)

Using the geom geom_density_ridges_gradient we can also color by quantile, via the calculated stat(quantile) aesthetic. Note that this aesthetic is only calculated if calc_ecdf = TRUE.

ggplot(iris, aes(x=Sepal.Length, y=Species, fill = factor(stat(quantile)))) +
  stat_density_ridges(
    geom = "density_ridges_gradient", calc_ecdf = TRUE,
    quantiles = 4, quantile_lines = TRUE
  ) +
  scale_fill_viridis_d(name = "Quartiles")

We can use the same approach to highlight the tails of the distributions.

ggplot(iris, aes(x = Sepal.Length, y = Species, fill = factor(stat(quantile)))) +
  stat_density_ridges(
    geom = "density_ridges_gradient",
    calc_ecdf = TRUE,
    quantiles = c(0.025, 0.975)
  ) +
  scale_fill_manual(
    name = "Probability", values = c("#FF0000A0", "#A0A0A0A0", "#0000FFA0"),
    labels = c("(0, 0.025]", "(0.025, 0.975]", "(0.975, 1]")
  )

Finally, when calc_ecdf = TRUE, we also have access to a calculated aesthetic stat(ecdf), which represents the empirical cumulative density function for the distribution. This allows us to map the probabilities directly onto color.

ggplot(iris, aes(x = Sepal.Length, y = Species, fill = 0.5 - abs(0.5 - stat(ecdf)))) +
  stat_density_ridges(geom = "density_ridges_gradient", calc_ecdf = TRUE) +
  scale_fill_viridis_c(name = "Tail probability", direction = -1)

Jittering points

The stat stat_density_ridges also provides the option to visualize the original data points from which the distributions are generated. This can be done by setting jittered_points = TRUE, either in stat_density_ridges or in geom_density_ridges:

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  geom_density_ridges(jittered_points = TRUE)

Where the points are shown can be controlled with position options, e.g. “raincloud” for the raincloud effect:

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  geom_density_ridges(
    jittered_points = TRUE, position = "raincloud",
    alpha = 0.7, scale = 0.9
  )

We can also simulate a rug:

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  geom_density_ridges(
    jittered_points = TRUE,
    position = position_points_jitter(width = 0.05, height = 0),
    point_shape = '|', point_size = 3, point_alpha = 1, alpha = 0.7,
  )

Note that we are using position_points_jitter() here, not position_jitter(). We do this because position_points_jitter() knows to jitter only the points in a ridgeline plot, without touching the density lines.

Styling the jittered points is a bit tricky but is possible with special scales provided by ggridges. First, there is scale_discrete_manual() which can be used to make arbitrary discrete scales for arbitrary aesthetics. We use it in the next example to style the point shapes. Second, there are various point aesthetic scales, such as scale_point_color_hue(). See the reference documentation for these scales for more details.

ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) +
  geom_density_ridges(
    aes(point_color = Species, point_fill = Species, point_shape = Species),
    alpha = .2, point_alpha = 1, jittered_points = TRUE
  ) +
  scale_point_color_hue(l = 40) +
  scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23))

All common aesthetics for points can be applied to the jittered points. However, the aesthetic names start with point_. In the next example, we have mapped an additional variable onto the size of the points.

ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) +
  geom_density_ridges(
    aes(point_shape = Species, point_fill = Species, point_size = Petal.Length), 
    alpha = .2, point_alpha = 1, jittered_points = TRUE
  ) +
  scale_point_color_hue(l = 40) + scale_point_size_continuous(range = c(0.5, 4)) +
  scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23))

Similarly, we have aesthetics for the vertical lines, named vline_. And the vertical lines can also be shifted so they are aligned with the jittered points. This allows us to generate figures such as the following:

ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  geom_density_ridges(
    jittered_points = TRUE, quantile_lines = TRUE, scale = 0.9, alpha = 0.7,
    vline_size = 1, vline_color = "red",
    point_size = 0.4, point_alpha = 1,
    position = position_raincloud(adjust_vlines = TRUE)
  )
## Warning: Use of the `vline_size` or `size` aesthetic are deprecated, please use
## `linewidth` instead of `size` and `vline_width` instead of `vline_size`.
## Warning: Use of the `vline_size` aesthetics is deprecated, please use
## `vline_width` instead of `vline_size`.

## Warning: Use of the `vline_size` aesthetics is deprecated, please use
## `vline_width` instead of `vline_size`.

## Warning: Use of the `vline_size` aesthetics is deprecated, please use
## `vline_width` instead of `vline_size`.

Using alternative stats

The stat stat_density_ridges may not always do exactly what you want it to do. If this is the case, you can use other stats that may be better for your respective application. First, stat_density_ridges estimates the data range and bandwidth for the density estimation from the entire data at once, rather than from each individual group of data. This choice makes ridgeline plots look more uniform, but the density estimates can in some cases look quite different from what you would get from geom_density or stat_density. This problem can be remedied by using stat_density with geom_density_ridges. This works just fine, we just need to make sure that we map the calculated density onto the height aesthetic.

ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + 
  geom_density_ridges(stat = "density")

Second, there may be scenarios in which you don’t want geom_density_ridges to do any density estimation, for example because you have done so already yourself. In this case, you can use stat_identity. The benefit of using geom_density_ridges with stat_identiy over using geom_ridgeline directly is that geom_density_ridges provides automatic scaling.

As an example, assume we have calculated density curves for the Sepal.Length column in the iris dataset:

library(dplyr)

iris_densities <- iris %>%
  group_by(Species) %>%
  group_modify(~ ggplot2:::compute_density(.x$Sepal.Length, NULL)) %>%
  rename(Sepal.Length = x)

iris_densities
## # A tibble: 1,536 × 7
## # Groups:   Species [3]
##    Species Sepal.Length  density   scaled ndensity  count     n
##    <fct>          <dbl>    <dbl>    <dbl>    <dbl>  <dbl> <int>
##  1 setosa          3.93 0.000869 0.000701 0.000701 0.0435    50
##  2 setosa          3.94 0.000973 0.000785 0.000785 0.0487    50
##  3 setosa          3.94 0.00109  0.000876 0.000876 0.0543    50
##  4 setosa          3.94 0.00121  0.000975 0.000975 0.0604    50
##  5 setosa          3.95 0.00135  0.00109  0.00109  0.0673    50
##  6 setosa          3.95 0.00150  0.00121  0.00121  0.0749    50
##  7 setosa          3.96 0.00166  0.00134  0.00134  0.0831    50
##  8 setosa          3.96 0.00184  0.00149  0.00149  0.0922    50
##  9 setosa          3.97 0.00205  0.00165  0.00165  0.102     50
## 10 setosa          3.97 0.00226  0.00183  0.00183  0.113     50
## # ℹ 1,526 more rows

We can plot these as follows:

ggplot(iris_densities, aes(x = Sepal.Length, y = Species, height = density)) + 
  geom_density_ridges(stat = "identity")

Notice how this plot looks different from the one generated using stat = "density", even though the density computation was exactly the same: (i) The density curves extend all the way to zero. (ii) There is no horizontal line extending all the way to the limits of the x axis.

Finally, if you prefer histograms to density plots, you can also use stat_binline. Note that overlapping histograms can look strange, so this option is probably best used with a scale parameter < 1. The option draw_baseline = FALSE removes trailing lines to either side of the histogram. (For histograms, the rel_min_height parameter doesn’t work very well.)

ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + 
  geom_density_ridges(stat = "binline", bins = 20, scale = 0.95, draw_baseline = FALSE)

Themes

ridgeline plots tend to require some theme modifications to look good. Most importantly, the y-axis tick labels should be vertically aligned so that they are flush with the axis ticks rather than vertically centered. The ggridges package provides a theme theme_ridges that does this and a few other theme modifications.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
  geom_density_ridges() + 
  theme_ridges()

However, without any further modifications, there are still a few issues with this plot. First, the ridgeline for the virginica species is slightly cut off at the very top point. Second, the space between the x and y axis labels and the ridgelines is too large. We can fix both issues using the expand option for the axis scales.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
  geom_density_ridges() + 
  scale_x_continuous(expand = c(0, 0)) +
  scale_y_discrete(expand = expand_scale(mult = c(0.01, .7))) +
  theme_ridges()

Instead of expanding the axis, you can also turn off clipping for the plot panel.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
  geom_density_ridges() +
  scale_x_continuous(expand = c(0, 0)) +
  scale_y_discrete(expand = c(0, 0)) +
  coord_cartesian(clip = "off") +
  theme_ridges()

By default, theme_ridges adds a grid, but the grid can be switched off when not needed. Also, axis titles can be centered.

ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
  geom_density_ridges() +
  scale_x_continuous(expand = c(0, 0)) +
  scale_y_discrete(expand = c(0, 0)) +
  coord_cartesian(clip = "off") + 
  theme_ridges(grid = FALSE, center_axis_labels = TRUE)

If you prefer to use a different theme than theme_ridges, for example theme_minimal, it is still advisable to adjust the alignment of the axis tick labels and the axis scales.

 ggplot(iris, aes(x = Sepal.Length, y = Species)) + 
   geom_density_ridges() + 
   scale_x_continuous(expand = c(0, 0)) +
   scale_y_discrete(expand = c(0, 0)) +
   coord_cartesian(clip = "off") +
   theme_minimal(base_size = 14) + 
   theme(axis.text.y = element_text(vjust = 0))

Cyclical scales

Many ridgeline plots improve in appearance if the filled areas are drawn with alternating colors. To simplify the generation of such plots, ggridges provides cyclical scales. These are scales that cycle through the aesthetic values provided. For example, if we use scale_fill_cyclical(values = c("blue", "green")) then ggplot will cycle through these two fill colors throughout the plot.

 ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + 
   geom_density_ridges(scale = 4) + 
   scale_fill_cyclical(values = c("blue", "green"))

By default, the cyclical scales will not draw a legend, because the legend will usually be confusing unless the labels are manually altered. Legends can be switched on via the guide = "legend" option, just like for all other scales.

 ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + 
   geom_density_ridges(scale = 4) + 
   scale_fill_cyclical(values = c("blue", "green"), guide = "legend")

Legends can be modified as usual.

 ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + 
   geom_density_ridges(scale = 4) + 
   scale_fill_cyclical(
     name = "Fill colors",
     values = c("blue", "green"),
     labels = c("Fair" = "blue", "Good" = "green"),
     guide = "legend"
   )

Cyclical scales are defined for all the common aesthetics one might want to change, such as color, size, alpha, and linetype, and the legends are combined when possible

 ggplot(diamonds, aes(x = price, y = cut, fill = cut, color = cut)) + 
   geom_density_ridges(scale = 4, size = 1) + 
   scale_fill_cyclical(
     name = "Color scheme",
     values = c("blue", "green"), guide = "legend",
     labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline")
    ) +
   scale_color_cyclical(
     name = "Color scheme",
     values = c("black", "yellow"), guide = "legend",
     labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline")
   )
## Warning in geom_density_ridges(scale = 4, size = 1): Ignoring unknown
## parameters: `size`

Because these cyclical scales are generic ggplot2 scales, they work with any geom that accepts the respective aesthetic. Thus, for example, we can make histograms with alternatingly colored bars.

ggplot(mpg, aes(x = class, fill = class, color = class)) + 
  geom_bar(size = 1.5) +
  scale_fill_cyclical(
    name = "Color scheme",
    values = c("blue", "green"), guide = "legend",
    labels = c("blue w/ black outline", "green w/ yellow outline")
  ) +
  scale_color_cyclical(
    name = "Color scheme",
    values = c("black", "yellow"), guide = "legend",
    labels = c("blue w/ black outline", "green w/ yellow outline")
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

While the previous example won’t win any design awards, more subtle effects can be helpful.

mpg %>% group_by(class) %>% 
  tally() %>% 
  arrange(desc(n)) %>%
  mutate(class = factor(class, levels = class)) %>%
  ggplot(aes(x = class, y = n, fill = class)) + 
  geom_col() +
  scale_fill_cyclical(values = c("#4040B0", "#9090F0")) +
  scale_y_continuous(expand = c(0, 0)) + 
  theme_minimal()

ggridges/inst/doc/gallery.html0000644000176200001440000261005014553613057016131 0ustar liggesusers Gallery of ggridges examples

Gallery of ggridges examples

Claus O. Wilke

2024-01-22

Evolution of movie lengths over time

Data from the IMDB, as provided in the ggplot2movies package.

library(ggplot2movies)

ggplot(movies[movies$year>1912,], aes(x = length, y = year, group = year)) +
  geom_density_ridges(scale = 10, size = 0.25, rel_min_height = 0.03) +
  theme_ridges() +
  scale_x_continuous(limits = c(1, 200), expand = c(0, 0)) +
  scale_y_reverse(
    breaks = c(2000, 1980, 1960, 1940, 1920, 1900),
    expand = c(0, 0)
  ) +
  coord_cartesian(clip = "off")

Results from Catalan regional elections, 1980-2015

Modified after a figure originally created by Marc Belzunces (@marcbeldata on Twitter).

library(dplyr)
library(forcats)

Catalan_elections %>%
  mutate(YearFct = fct_rev(as.factor(Year))) %>%
  ggplot(aes(y = YearFct)) +
  geom_density_ridges(
    aes(x = Percent, fill = paste(YearFct, Option)), 
    alpha = .8, color = "white", from = 0, to = 100
  ) +
  labs(
    x = "Vote (%)",
    y = "Election Year",
    title = "Indy vs Unionist vote in Catalan elections",
    subtitle = "Analysis unit: municipalities (n = 949)",
    caption = "Marc Belzunces (@marcbeldata) | Source: Idescat"
  ) +
  scale_y_discrete(expand = c(0, 0)) +
  scale_x_continuous(expand = c(0, 0)) +
  scale_fill_cyclical(
    breaks = c("1980 Indy", "1980 Unionist"),
    labels = c(`1980 Indy` = "Indy", `1980 Unionist` = "Unionist"),
    values = c("#ff0000", "#0000ff", "#ff8080", "#8080ff"),
    name = "Option", guide = "legend"
  ) +
  coord_cartesian(clip = "off") +
  theme_ridges(grid = FALSE)

Temperatures in Lincoln, Nebraska

Modified from a blog post by Austin Wehrwein.

ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) +
  geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01, gradient_lwd = 1.) +
  scale_x_continuous(expand = c(0, 0)) +
  scale_y_discrete(expand = expansion(mult = c(0.01, 0.25))) +
  scale_fill_viridis_c(name = "Temp. [F]", option = "C") +
  labs(
    title = 'Temperatures in Lincoln NE',
    subtitle = 'Mean temperatures (Fahrenheit) by month for 2016'
  ) +
  theme_ridges(font_size = 13, grid = TRUE) + 
  theme(axis.title.y = element_blank())

Visualization of Poisson random samples with different means

Inspired by a ggridges example by Noam Ross (twitter.com/noamross/status/888405434381545472).

# generate data
set.seed(1234)
pois_data <- data.frame(mean = rep(1:5, each = 10))
pois_data$group <- factor(pois_data$mean, levels = 5:1)
pois_data$value <- rpois(nrow(pois_data), pois_data$mean)

# make plot
ggplot(pois_data, aes(x = value, y = group, group = group)) +
  geom_density_ridges2(aes(fill = group), stat = "binline", binwidth = 1, scale = 0.95) +
  geom_text(
    stat = "bin",
    aes(
      y = group + 0.95*stat(count/max(count)),
      label = ifelse(stat(count) > 0, stat(count), "")
    ),
    vjust = 1.4, size = 3, color = "white", binwidth = 1
  ) +
  scale_x_continuous(
    breaks = c(0:12), limits = c(-.5, 13),
    expand = c(0, 0), name = "random value"
  ) +
  scale_y_discrete(
    expand = expansion(add = c(0, 1.)), name = "Poisson mean",
    labels = c("5.0", "4.0", "3.0", "2.0", "1.0")
  ) +
  scale_fill_cyclical(values = c("#0000B0", "#7070D0")) +
  labs(
    title = "Poisson random samples with different means",
    subtitle = "sample size n=10"
  ) +
  guides(y = "none") +
  theme_ridges(grid = FALSE) +
  theme(
    axis.title.x = element_text(hjust = 0.5),
    axis.title.y = element_text(hjust = 0.5)
  )

Height of Australian athletes

 ggplot(Aus_athletes, aes(x = height, y = sport, color = sex, point_color = sex, fill = sex)) +
  geom_density_ridges(
    jittered_points = TRUE, scale = .95, rel_min_height = .01,
    point_shape = "|", point_size = 3, size = 0.25,
    position = position_points_jitter(height = 0)
  ) +
  scale_y_discrete(expand = c(0, 0)) +
  scale_x_continuous(expand = c(0, 0), name = "height [cm]") +
  scale_fill_manual(values = c("#D55E0050", "#0072B250"), labels = c("female", "male")) +
  scale_color_manual(values = c("#D55E00", "#0072B2"), guide = "none") +
  scale_discrete_manual("point_color", values = c("#D55E00", "#0072B2"), guide = "none") +
  coord_cartesian(clip = "off") +
  guides(fill = guide_legend(
    override.aes = list(
      fill = c("#D55E00A0", "#0072B2A0"),
      color = NA, point_color = NA)
    )
  ) +
  ggtitle("Height in Australian athletes") +
  theme_ridges(center = TRUE)
## Warning in geom_density_ridges(jittered_points = TRUE, scale = 0.95,
## rel_min_height = 0.01, : Ignoring unknown parameters: `size`

A cheese plot

Inspired by a tweet by Leonard Kiefer (twitter.com/lenkiefer/status/932237461337575429).

set.seed(423)
n1 <- 200
n2 <- 25
n3 <- 50
cols <- c('#F2DB2F', '#F7F19E', '#FBF186')
cols_dark <- c("#D7C32F", "#DBD68C", "#DFD672")
cheese <- data.frame(
  cheese = c(rep("buttercheese", n1), rep("Leerdammer", n2), rep("Swiss", n3)),
  x = c(runif(n1), runif(n2), runif(n3)),
  size = c(
    rnorm(n1, mean = .1, sd = .01),
    rnorm(n2, mean = 9, sd = 3),
    rnorm(n3, mean = 3, sd = 1)
  )
)
ggplot(cheese, aes(x = x, point_size = size, y = cheese, fill = cheese, color = cheese)) +
  geom_density_ridges(
    jittered_points = TRUE, point_color="white", scale = .8, rel_min_height = .2,
    size = 1.5
  ) +
  scale_y_discrete(expand = c(0, 0)) +
  scale_x_continuous(limits = c(0, 1), expand = c(0, 0), name = "", breaks = NULL) +
  scale_point_size_continuous(range = c(0.01, 10), guide = "none") +
  scale_fill_manual(values = cols, guide = "none") +
  scale_color_manual(values = cols_dark, guide = "none") +
  coord_cartesian(clip = "off") +
  theme_ridges(grid = FALSE, center = TRUE)
## Warning in geom_density_ridges(jittered_points = TRUE, point_color = "white", :
## Ignoring unknown parameters: `size`

ggridges/inst/doc/introduction.R0000644000176200001440000002672314553613063016453 0ustar liggesusers## ----warning = FALSE, message = FALSE----------------------------------------- library(ggplot2) library(ggridges) data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, 3, 4, 2)) ggplot(data, aes(x, y, height = height)) + geom_ridgeline() ## ----message = FALSE, fig.width=9, fig.height=3------------------------------- library(patchwork) # for side-by-side plotting data <- data.frame(x = 1:5, y = rep(1, 5), height = c(0, 1, -1, 3, 2)) plot_base <- ggplot(data, aes(x, y, height = height)) plot_base + geom_ridgeline() | plot_base + geom_ridgeline(min_height = -2) ## ----message = FALSE---------------------------------------------------------- d <- data.frame( x = rep(1:5, 3), y = c(rep(0, 5), rep(1, 5), rep(2, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1) ) ggplot(d, aes(x, y, height = height, group = y)) + geom_ridgeline(fill = "lightblue") ## ----message = FALSE---------------------------------------------------------- ggplot(d, aes(x, y, height = height, group = y)) + geom_density_ridges(stat = "identity", scale = 1) ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges2() ## ----message=FALSE------------------------------------------------------------ # modified dataset that represents species as a number iris_num <- transform(iris, Species_num = as.numeric(Species)) # does not work, causes error # ggplot(iris_num, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() # works ggplot(iris_num, aes(x = Sepal.Length, y = Species_num, group = Species_num)) + geom_density_ridges() ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(rel_min_height = 0.01) ## ----message=FALSE------------------------------------------------------------ # scale = 0.9, not quite touching ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 0.9) # scale = 1, exactly touching ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1) # scale = 5, substantial overlap ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 5) ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1) + facet_wrap(~Species) ## ----message = FALSE---------------------------------------------------------- d <- data.frame( x = rep(1:5, 3) + c(rep(0, 5), rep(0.3, 5), rep(0.6, 5)), y = c(rep(0, 5), rep(1, 5), rep(3, 5)), height = c(0, 1, 3, 4, 0, 1, 2, 3, 5, 4, 0, 5, 4, 4, 1)) ggplot(d, aes(x, y, height = height, group = y, fill = factor(x+y))) + geom_ridgeline_gradient() + scale_fill_viridis_d(direction = -1, guide = "none") ## ----message = FALSE---------------------------------------------------------- ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = Month, fill = stat(x))) + geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) + scale_fill_viridis_c(name = "Temp. [F]", option = "C") + labs(title = 'Temperatures in Lincoln NE in 2016') ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE, quantiles = 2) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + stat_density_ridges(quantile_lines = TRUE, quantiles = c(0.025, 0.975), alpha = 0.7) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x=Sepal.Length, y=Species, fill = factor(stat(quantile)))) + stat_density_ridges( geom = "density_ridges_gradient", calc_ecdf = TRUE, quantiles = 4, quantile_lines = TRUE ) + scale_fill_viridis_d(name = "Quartiles") ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = factor(stat(quantile)))) + stat_density_ridges( geom = "density_ridges_gradient", calc_ecdf = TRUE, quantiles = c(0.025, 0.975) ) + scale_fill_manual( name = "Probability", values = c("#FF0000A0", "#A0A0A0A0", "#0000FFA0"), labels = c("(0, 0.025]", "(0.025, 0.975]", "(0.975, 1]") ) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = 0.5 - abs(0.5 - stat(ecdf)))) + stat_density_ridges(geom = "density_ridges_gradient", calc_ecdf = TRUE) + scale_fill_viridis_c(name = "Tail probability", direction = -1) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(jittered_points = TRUE) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, position = "raincloud", alpha = 0.7, scale = 0.9 ) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, position = position_points_jitter(width = 0.05, height = 0), point_shape = '|', point_size = 3, point_alpha = 1, alpha = 0.7, ) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) + geom_density_ridges( aes(point_color = Species, point_fill = Species, point_shape = Species), alpha = .2, point_alpha = 1, jittered_points = TRUE ) + scale_point_color_hue(l = 40) + scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23)) ## ----message = FALSE, fig.width = 6, fig.height = 6--------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species, fill = Species)) + geom_density_ridges( aes(point_shape = Species, point_fill = Species, point_size = Petal.Length), alpha = .2, point_alpha = 1, jittered_points = TRUE ) + scale_point_color_hue(l = 40) + scale_point_size_continuous(range = c(0.5, 4)) + scale_discrete_manual(aesthetics = "point_shape", values = c(21, 22, 23)) ## ----message = FALSE---------------------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges( jittered_points = TRUE, quantile_lines = TRUE, scale = 0.9, alpha = 0.7, vline_size = 1, vline_color = "red", point_size = 0.4, point_alpha = 1, position = position_raincloud(adjust_vlines = TRUE) ) ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + geom_density_ridges(stat = "density") ## ----message=FALSE------------------------------------------------------------ library(dplyr) iris_densities <- iris %>% group_by(Species) %>% group_modify(~ ggplot2:::compute_density(.x$Sepal.Length, NULL)) %>% rename(Sepal.Length = x) iris_densities ## ----message=FALSE------------------------------------------------------------ ggplot(iris_densities, aes(x = Sepal.Length, y = Species, height = density)) + geom_density_ridges(stat = "identity") ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species, height = stat(density))) + geom_density_ridges(stat = "binline", bins = 20, scale = 0.95, draw_baseline = FALSE) ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + theme_ridges() ## ----message=FALSE, warning=FALSE--------------------------------------------- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = expand_scale(mult = c(0.01, .7))) + theme_ridges() ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges() ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_ridges(grid = FALSE, center_axis_labels = TRUE) ## ----message=FALSE------------------------------------------------------------ ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() + scale_x_continuous(expand = c(0, 0)) + scale_y_discrete(expand = c(0, 0)) + coord_cartesian(clip = "off") + theme_minimal(base_size = 14) + theme(axis.text.y = element_text(vjust = 0)) ## ----message=FALSE------------------------------------------------------------ ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical(values = c("blue", "green")) ## ----message=FALSE, fig.width = 5.5------------------------------------------- ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical(values = c("blue", "green"), guide = "legend") ## ----message=FALSE, fig.width = 5.5------------------------------------------- ggplot(diamonds, aes(x = price, y = cut, fill = cut)) + geom_density_ridges(scale = 4) + scale_fill_cyclical( name = "Fill colors", values = c("blue", "green"), labels = c("Fair" = "blue", "Good" = "green"), guide = "legend" ) ## ----message=FALSE, fig.width = 6.5------------------------------------------- ggplot(diamonds, aes(x = price, y = cut, fill = cut, color = cut)) + geom_density_ridges(scale = 4, size = 1) + scale_fill_cyclical( name = "Color scheme", values = c("blue", "green"), guide = "legend", labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline") ) + scale_color_cyclical( name = "Color scheme", values = c("black", "yellow"), guide = "legend", labels = c("Fair" = "blue w/ black outline", "Good" = "green w/ yellow outline") ) ## ----message=FALSE, fig.width = 6.5------------------------------------------- ggplot(mpg, aes(x = class, fill = class, color = class)) + geom_bar(size = 1.5) + scale_fill_cyclical( name = "Color scheme", values = c("blue", "green"), guide = "legend", labels = c("blue w/ black outline", "green w/ yellow outline") ) + scale_color_cyclical( name = "Color scheme", values = c("black", "yellow"), guide = "legend", labels = c("blue w/ black outline", "green w/ yellow outline") ) ## ----message=FALSE, fig.width=5.5--------------------------------------------- mpg %>% group_by(class) %>% tally() %>% arrange(desc(n)) %>% mutate(class = factor(class, levels = class)) %>% ggplot(aes(x = class, y = n, fill = class)) + geom_col() + scale_fill_cyclical(values = c("#4040B0", "#9090F0")) + scale_y_continuous(expand = c(0, 0)) + theme_minimal()