forcats/0000755000176200001440000000000014365570422011723 5ustar liggesusersforcats/NAMESPACE0000644000176200001440000000200614357073063013140 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(as_factor,character) S3method(as_factor,factor) S3method(as_factor,logical) S3method(as_factor,numeric) export("%>%") export(as_factor) export(fct) export(fct_anon) export(fct_c) export(fct_collapse) export(fct_count) export(fct_cross) export(fct_drop) export(fct_expand) export(fct_explicit_na) export(fct_infreq) export(fct_inorder) export(fct_inseq) export(fct_lump) export(fct_lump_lowfreq) export(fct_lump_min) export(fct_lump_n) export(fct_lump_prop) export(fct_match) export(fct_na_level_to_value) export(fct_na_value_to_level) export(fct_other) export(fct_recode) export(fct_relabel) export(fct_relevel) export(fct_reorder) export(fct_reorder2) export(fct_rev) export(fct_shift) export(fct_shuffle) export(fct_unify) export(fct_unique) export(first2) export(last2) export(lvls_expand) export(lvls_reorder) export(lvls_revalue) export(lvls_union) import(rlang) importFrom(glue,glue) importFrom(lifecycle,deprecated) importFrom(magrittr,"%>%") importFrom(stats,median) forcats/LICENSE0000644000176200001440000000005513764153133012726 0ustar liggesusersYEAR: 2020 COPYRIGHT HOLDER: forcats authors forcats/README.md0000644000176200001440000000767114360013050013175 0ustar liggesusers # forcats [![CRAN status](https://www.r-pkg.org/badges/version/forcats)](https://cran.r-project.org/package=forcats) [![R-CMD-check](https://github.com/tidyverse/forcats/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/tidyverse/forcats/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/tidyverse/forcats/branch/main/graph/badge.svg)](https://app.codecov.io/gh/tidyverse/forcats?branch=main) ## Overview R uses **factors** to handle categorical variables, variables that have a fixed and known set of possible values. Factors are also helpful for reordering character vectors to improve display. The goal of the **forcats** package is to provide a suite of tools that solve common problems with factors, including changing the order of levels or the values. Some examples include: - `fct_reorder()`: Reordering a factor by another variable. - `fct_infreq()`: Reordering a factor by the frequency of values. - `fct_relevel()`: Changing the order of a factor by hand. - `fct_lump()`: Collapsing the least/most frequent values of a factor into “other”. You can learn more about each of these in `vignette("forcats")`. If you’re new to factors, the best place to start is the [chapter on factors](https://r4ds.hadley.nz/factors.html) in R for Data Science. ## Installation # The easiest way to get forcats is to install the whole tidyverse: install.packages("tidyverse") # Alternatively, install just forcats: install.packages("forcats") # Or the the development version from GitHub: # install.packages("devtools") devtools::install_github("tidyverse/forcats") ## Cheatsheet ## Getting started forcats is part of the core tidyverse, so you can load it with `library(tidyverse)` or `library(forcats)`. ``` r library(forcats) library(dplyr) library(ggplot2) ``` ``` r starwars %>% filter(!is.na(species)) %>% count(species, sort = TRUE) #> # A tibble: 37 × 2 #> species n #> #> 1 Human 35 #> 2 Droid 6 #> 3 Gungan 3 #> 4 Kaminoan 2 #> 5 Mirialan 2 #> 6 Twi'lek 2 #> 7 Wookiee 2 #> 8 Zabrak 2 #> 9 Aleena 1 #> 10 Besalisk 1 #> # … with 27 more rows ``` ``` r starwars %>% filter(!is.na(species)) %>% mutate(species = fct_lump(species, n = 3)) %>% count(species) #> # A tibble: 4 × 2 #> species n #> #> 1 Droid 6 #> 2 Gungan 3 #> 3 Human 35 #> 4 Other 39 ``` ``` r ggplot(starwars, aes(x = eye_color)) + geom_bar() + coord_flip() ``` ![](man/figures/README-unordered-plot-1.png) ``` r starwars %>% mutate(eye_color = fct_infreq(eye_color)) %>% ggplot(aes(x = eye_color)) + geom_bar() + coord_flip() ``` ![](man/figures/README-ordered-plot-1.png) ## More resources For a history of factors, I recommend [*stringsAsFactors: An unauthorized biography*](https://simplystats.github.io/2015/07/24/stringsasfactors-an-unauthorized-biography/) by Roger Peng and [*stringsAsFactors = \*](https://notstatschat.tumblr.com/post/124987394001/stringsasfactors-sigh) by Thomas Lumley. If you want to learn more about other approaches to working with factors and categorical data, I recommend [*Wrangling categorical data in R*](https://peerj.com/preprints/3163/), by Amelia McNamara and Nicholas Horton. ## Getting help If you encounter a clear bug, please file a minimal reproducible example on [Github](https://github.com/tidyverse/forcats/issues). For questions and other discussion, please use [community.rstudio.com](https://community.rstudio.com/). forcats/data/0000755000176200001440000000000012754676654012652 5ustar liggesusersforcats/data/gss_cat.rda0000644000176200001440000021734112754704400014752 0ustar liggesusersBZh91AY&SYC]@UUᖘt|$}u3`Zh!ҬDCD =Ѡ:( /A{@4^z(TքP R((UU@)G )=t6"ҏLeJkFȨU l-jafiz5+U9R P lҪm()VRI@PڶiZiJkjl f MEbE-: tT֮Sz J=P(Dr !U4-` 0H P( @@l@րʠC=1pQz TE tDJ JEP={ PSѥ*@-͖1b)oVڭ6D%h jdF!fPP` t4l)Z0B: ht:į@yP(PU^ƴ4R (@R"T%J$IP B%PJ Q "Un*zJ(  2" ȑS B!I44)hSmz$2L jx"HBFDz 1P= d4O$DFP2RTH5 4 JHOJyMģ6zdz(zOM6Q*`*~,xu\jOYY6˶M<:$=ٵQ Zv0[dngmT[+,AUā"X0EEYCpGGZQřQY"ZhiIl\یҍ;.lYP82mvutu;muf\DqnۺΫ." ($ig ^Fq}ҕM]e&]Fs}o#x"&o!'F/ ZQ{\^%;xgqZEIEֳ,x}fQ$B" C5/'nxq|cS9yKy7*@*K{GqMsݛgǤNKUIu{";P9٣b'T?ؾAn)Glgk(s`M]ɴGW=(4;(޽39ȔmklZՀR}z>ۛ[7'I8זּݼ#nZÒMDNR'g&BrPFgG/o=[j6B󆦗(0fVv,Wj+$㶰rgBe(m_;K$Jfpᵩv-eg VTT %vD9Fmnt,cg8ɵ mnA%Yt۳AEg)ېZ\LM;mm;Sb:ʒ@\Mc8Mb%.KAE%Id%g}9e>=z`F mNjEf J+`O+a[ce6Bn 5C`d!v̡pƈz!4Yd=qأ]8E19}^!!JVaH*(FjWm9ZrHB 7YekR@i `m \ -i%l(A4;6͍DH\|N"Dͺ\p Ek-d|Ѓ l9`$~y9gr7.6R.&0jy;!}G橐K?r XBجa \λe e.["mXM$k5U%D\XBfc4ZPњ[#4&jBJ 6uT7(SM0 6 @L4+&aQOwN U#KazǤsĦ䷵|cHlwCx#V@ʡeݚZ#("td-lh\HaX`ebEJZфD,8=nY92gl#SVFW4ٷa IW;(D!5&۩`V 5kҐ(hWM, R?}-'«UjCg&% 6 "KQ 8pk5da,i@%T u6&BAT(H]e2ǐ%4K5uP Z ,1&&-pl+7eTE$"ŕo[""֖\R"QiYhlF$ZYeC2U*dh,Et [.ܧ5ȵg4)-l"$6g*yiKj@# v@m1GաkZ@H .¬YK,axlr eBR+hc*.@ Fb0 aU14Jˀ2LYIjlf2ֳyUR2lHmB1ENv,[24P DG \ \՘PD`mͺHUJM&pi-) Xn؀AX^v( YR`pb l*`ZJ*FMD47k͛&ٺ VցJQZ`e3KfaJYmFƐ2DVDš%6ŷmfս⍖)$!TeK#4ARXVDՓDf&HB$,dkS\M\VZ(jH6 :>K"6бc TRrXiHdf`\BsT Ndcf:*]̒~\3ݳ5Z:QVɂ=)Dt4#$!0REn4* ʫv=50TO"KwA?ZH.tolyeiv<Ķ^]IKI'AvU2/ fƵDrM3keFY-Q29aʹ3ɱ%zuvYZu6f3@&[CR@%P8NÂdQehɴ[n8tU~tuge;3#mGwg-:ɷH]ZiIhu'ivg$g[c,.® ˲+6sk.(mNtuGqqgVY6ˡ@$E k1ŒJDu7ؾKZZAG<<"`Gfmk;(Ӕ%bi֖۽\y038۷$n8v$.:; ;G,63;0C~{~7 JC8ﵽ=͵r̉ldvD9eeIfu~kB)2l$eNHC+sJëNmpS? _:$:,ȋkr>D;2K0/6;Gp+*Nˊ(.(K+"̣ ;eev7YvVVaQGgvë We @03Zo4B@Mv1Q6A*%vt/J5&&&ͦu-!Fގ퉅\D9HQ7H"B nĂrXIڼe'w\17` ζ6yblY6ֵk:n賬. Vֳ/v¶ݜ$h6KgE KJňL:볤̢¬8;3(,qgGigZucbmДZJq(Rhs٢:]VUe%YG_^GrF]GvؙYUݝUgM" Ec;(wq6J8mqmҿw4K#! ˨YtTYkn*έq%# ֐KJhӤ8"#K))- TE!iݶ(3328&uiEFd "DgM'jCӽ䄄":-E,gaAKJMm`) 4gYGv;c, *:8m]jˬ*[FEUn#*HC45ҚB'sj*(;s΢Ц4δC-.932λ,.nJ3;;.2(8mٕuEtRZFڇQJm%;+";ˋmVgXT\vGgeqTQf]ewhTdtvYBFƑ)( &Ix:;4!&SPѤiZb t RY\qݘ ;㎴롶TQiQkAK R!B:16h 1)Ji(-҅%i [![(it+TPP%!M!FJhN˺Kӊ&CM-;b@Д&[.(Ӥ4(Z#8ҫ8ŗMChjRhiM FFҔSJRINl PaRM C1mBtMItkk bКMh4 H GM.4HWBRѣKFÎ;P:Ѡ1&٢JSNZ4 (bVJjJ %*]Ev$weehZAl kB M&vZt.44)Hi5we6(;KcJ+H[`u@:Cl)R[`h]:4hMPVg\'YvtEVWhJKl-qQZg\EAFJj FBi(m4KUvuݶ"EhiFq!J JZҚCC*4;#:NZ5@eiPBCkA*E:MIH 4DCC PqfVtutTQ +X+FHRwVQۨ:mZK:4:&TRJBҚ4 RҺJF)CHP!CHe+;+:"B:Sb )ZPJҸH- @:Q҆ր5KM&kK2..HJZѥ)(JiqS:(Ȑi4 uJ$";ά,: ]& VWq\uEƝgf] 4 @4Д@%M t.ЦBКi J44І(hBR Hh!I)HPt4&AAJVҭ+H44%(P&i)CKJJ&i: J @!tK D٤ZECJ @Bƕ(ҭ H+J -](ZFBJit@$+:;wtGXQAJP4PP4P.KKl"u]Zj $@$\?|ЫՅ=+说Aa6x$'=LZYXhh&Yg{Ђ pUo!^kXm(>T\01 `w{ƇNV'D"lpCIDWɑxtHujRnC ̎ӟz>:9)+ӮME>njȼJ '#bK\iOuCAu(Р@ߡH%žsӷ"Jgo zHuqi(u"g٤68Ѓͽun""A _ѡ6~c]d#+.uc(>-b3_2l3eG B!ovY>$~wgw ޜ׼yG!>qyPgH  hU3KoZEa8&Nx ~V(_DE䍏wgvyO9ЪaL컭D$>Y`1nۨzX@UTBh%$]RH:*53YC" T߮V[QYSNEhDwT+8;d9r}jJN"Sy [r'ȳW&(BBԿ>bp; u!(K쉆3obl]ŭi[Ȃ}kG^'8 fj`sP䵣Y}BA HpY B$z[%G XIv.+:؂eC,: "9jܛ\>~za=*q0 t(+P ECT ǯ?$!,+YX ^)Ž- Jck;t'7Jzv.붐$'ɫζ(@PF~ MTA+q xpxpC?g8O'є"H2 /ϻ2G_x@zq);H"|nt=cת3> qf'?K,B(I&vtg4VP7T+o {LVH"I؎\w/hI= ȏB9zޚsjb; C"nnXZ QOYZp0>y&.c\j4NW6ϫ.o?;HBz2i3L\4jQ6.Ω'i܅HGOH}@|b x4@2$V)Ia? *m9qpanLhD"'>l zGYEѣM)[E1q*=&  C,+&?8ut3=ȋKHfޙ`MA)Jm1& sMHB"7@ pu ^ҁ4EMb2~8&RsrD[ !ɭ~lw^W7(|Eb3^@̑"Z=P;M1887>.!zS>)L~eDDu=WJ#0Y'91 5eͼR' ÌBDT(906̈́ZTw]G&,K I} ~`ؒAyx0M}HN.FJ4>+~/~G4$F#46"Owc $FVi9 ɥʫhXoͩqᑉp7;@46 BnǞg=DLNb"[.9qI{ 5Jf!)! @Ӥlbx<.HugZ$^=DY~nt'yi+c_wfK cA;9LCbz*m7` jٚB6$AE/K=Z܀n&]t"O+Y/ P$]!@B3@RFĒ1V8 0D{I=_I,]ASZ3 MeO}om+Ć_ 40=Xo ߾L }XDD&6<=~JNxCh >|`$0GH||x!>Ȉ_K$c?9S)4<1CB$1\ brcHQ?7Q<2ZF-''mCtEY"m_EQQ#Gm_x Dv\ U/EPD_ybs,pƤ@7#_gHI4YRngizvyoqdI-`-OʻTDx_c&q&GC塐I6$:SfgY dbHƔg̕pX jk] . s؈5P #K] \z+ؚ *=xALIW8'ǪX0+VSb*?0Ia-a>{'m?6>;*B$,IڰPՆ^V}~3ZXbFtuieXp@<ҕ ?kŒ"#S ҂Hĩ 檵LxT =LT:^qk39'{)rC~ IJ#ӤLLjR ZO H]l~-~k+69b6Ӊ 3p- s&FHF>o3B`p+SR0$Ռev&iDEYDoG=99c{Yg ײy%I?L\d7̤B8 &j7<))YAuwD֔-wg߸(+L XZE &i]Ͻz^֗+>W$+G p.-a9RX 1,|hA'b| R"Aze 4q!s95߲0P #_9p1e )hQ](3j:4rCllNRee~~WίvYu`$Q$M SV@5EFl"C ";vhcH +n]R[g8tEz4r qpud<qU/zU!KH)RA5_QCk[S([y+hz|`]D쁓Dw&Qq}szҹ}H 6h_opF*U>޵z TLB`3n&Ģsh -n fd#P8!dnRH)=ۍə@xHsy₩oK,Kה4YvRJ #|94Vy`[M+Jl2H,-}QZ0>3,AatǓ 4(Obi!x\>B'PϮ+]83İ6{#3DI٣S&GMzlT)j,S/ reհV#ҩ$aT/i:u}śn!;y\?1"Vq&KFNG&qAC$by"M\~{!Z\ /7a1 :O]czESI^wQ.l磝Ftb0%vA$3)Gk"kd1XR(UiX[Hc]r݈gB\*Vf_3/\nHPAb!Y|zPpq[w$rhŏrYσb_= @gr1n340GVHK'ǔƹыu)Cgf(Yi~NA:F@č#C :H진.`u2F8ZjÊd2$mmX0 a힍S{!;tt@Ǿ= !3_ЈdnN$2J{.L# [LQxo‹1mECU b{!{h} C|ߩbzLx־".zxݾ JbHyl'9Od|dM2)'?gD9EqJ) )kȍeqRyh$i#6_F%xdI d !T?cޒZmwc%=Cm @τ~3ٿXp6"lƟH J۹BC .y^IK<3_}l`C$QOߘ蹌w:6EH+O>iQ=7hTҀ$lkӒ6Q{&D.PK@aWJ5>`L&G=AW Ho8<<%qxS"A!>0}6;629=K2ioϞI}NzDW}~}s?dK$8Ʒဈ䣴ϞMXpB@%$V>|~ ǩ/~ o$lXO׭CGT"e 3v4[4ڮT`v,sSR#\4H odIMpr`E"0]rޑ[@ʹK @O8 4ְ xD !L(FWb?K A AaFFlO x#|5+2.c[R@L}`0F!hLy,~lAM[tD7(R[.rV ~R-%,*\"Co|]8Py6ԬZ&p;OQbaTŊ(W4B6Eal1bBIM VP#>?^*& D;"Hv⨐A h"c1aђ Pz+3>$1<XhC" @dZx}#.ܕ,A{EDb|xcU;y79+3h_ kd +I y7ŢxX5hbJ bh3-L>nYG1Avsk<$Zd#aE-xqP$1]FOo<b@ܮl`;IJQ;H L![$!B!Z|?[ofl)}{^ة5%M] HnvT$2Cq(l0wkbvMk><28jFd`s,iYcEd%Bܸxc.*XkN< zMB3<5%z^1+5+.[+e"FDx+0RK mednׅ%Ē.f>9ݰ *WH 8 i.5ZpVlvH羻9^̾!q =Y58)9i]mB,[ L`eblٲ @b %@DIj}{Z>-UBB+PJVU*4wVh88:"8fJKsu<3izٛ1w =^!`Wz=vL% QƇq7,K W=>纍va^8aE:BFi>xO~WӺ=ǩ{ݷP3 /\&/O |-сԴ'tꈹGwd5˦>ı/n}{*!#AX#;BlJg|Qތ91=/ʴ(B>ä$ҞjLP#gv;D9#Qa #Cupf$KlIA$),40ЌkO BI[.R6 _u"1yQxY-t!UTy+T҃ԇݚ:`w^=>GM+dziqe4ȓ88qdn69J&@4_T+m,e"M)}DQ(;^o[On!/~s)lݣi+gS|z54@SrǠRewoꐣclq Y<i{Oo{s1r @ uwvt |KkAC;;"5Zߟv*z4j|"wYIyt(AHN36 HɛL hL#jDv4ȵeZE~p6d2!܁WH.xOB==N,[ q(H5 cZ̙d| GNCD'.T'0۫0J5I%X3fju 9FWnʩxyEGXZXnCL'E5-H8E9_LwTf F*ޕcFy< Ab:}gPvncx ۛH1=.ؐHdkHk*:Dvf̀R 9mCee !G6.K^GҀyd=["LE9,+2$ѴBG7o(eHߜH6K~6^vO,:޶6StPb6B$x=ฒzVQeg:(ۺ@aV0F,1%ٰ5LhҌYX)$uf[ ݬMm̀a[Y1;6Whiavf0baKQfD\ YHJ4h-r[B*%ݾw7^4u=12cݸzX;S3tZ)ƎxrŠTuo0\.l5qpZ$Im#IXvs}1X\ePUg4昪1v9g33z) Cb}̰uLne݃\Tftp]s*G-B% 90ځ6@]$dGt>[6yKK8R5ņc^)E`Ѩq3h1<Ŗ lNb hɁUx BQ&Vhi3*3N(7d o,0< mV@B@,eD ,V%K(O7-9 R9J#JahW.ӸD:ץ6S4om!Wn](".1> jPa9QBnBq`L`0lH읊ex/Ӈw#hcɷkMma/r3$qD& U0pG8eS~צ7 [ jV"&4 .جAJQIBy;-`l@bsg:v76zEX')ݑ:pwq drKϪ0iԐ) eM" !%Ԡ"uaq+8|"S*0!K7/U}7Yd@񗘲 oLxU 0%+ůFF^,-S"-PAD$^u :U2I!S1ŹkɻY:HNi0!Zs&p,A\ PY S[hK3&33&jźVAbz^60PÖ38ۀԀTT3w 9YrMQ4U1<\x!Y 0F/'F²kB9O]5;𘢣wJb8h@J eȔb5'%-;d4z v3<:V&GHyg 4̼eba~k,$'uxi iI&dy}[]t gFCq.jXat@"$ 1n BXRxNÐf _,c[HPq & ]aǡmR'r%x &4l=\C3oh`K9Cp2+sY{Ί?df`" 6Py~ پlkRgu}XmHS!cTbecq!@'B%82AC+y#K%@usI1qw!& ,-(H\e* &6dڀdiYkeub@&]ikKM$H"!BU fpթC vַV;f=1Xt!TcemmȐR"eE`1d8#X*+lwmD ` Q)B)fXKsmzwyyݧ֛f"@ 1c nܠ- UߕH,].\" j@r+LM%ft6 %2¥l &RhshfZ>_8/֭266ɖ/ٱ])\ 9>Q}.֊4D5#edip !YB\l-e%5nJ $ذ@v$mG`H"]%7eղ2 K)D1hK[vޣAHݖ@!5 kUXYI)!V&v؎2>֓6 (!1,c8Y7hp U#U0 F0^ɣD(EZ"B*R!& .2ji( e`M֚ۈDX9a.n&R& ٔ4dγݝe>YFxi#Y ۔~< X^2&фG0$j>yPL p*O{ ʌ)aYVs7HdI $l%9l]' 2D%ZbI4M GlXB&@1$f罞u"+==+|y"<1d%x{,8|󜐚9+kHKV nօ%Ƴ Ie8$̀S+ "$3]Uj)Fl#!%)"Hm B0k ;qXj[J۔F 4S `+ݔ HSX-exz" |`ӟdm>vtci%4!}l/ڰp9 +Cq!H3zw]Cv; .K o~>l6^=9Kq8;eo J)DsS<8Kpf$I L1%M25t l!r{UtQ &U[њ]7Nk95k Snmi\ϧ4A0>wvsǃ H,+HԊ\J6mNj}' 5ң5y;mT,QLL0̈́2$fY*Hm p(~!.Gݲu\($6%/ D) ILR԰!'-4 rÌH([B8@JJ [zYzټ:tFk` \mLՖ,1 [I@ ru ι;11:ul@ )(#7zZP7—=j<\VE2Hx"R73zHL a%\Fh`T!tGH_G>=(zAٺ$S< i۔ IVܫEm)- %%$LaOzl7'!pmI4:pAP`a0ZX T%(RYW;GJt=NcLB1:(*a#<5äxhF"H37y8Y}Ȕû[b.I "O<,xĉ@60` i&j,"3u' #+ ,!YN{pzvN=;eȥyY-0fDIl+|$a @a.FDlh5@$ctŖϓ! P6y3OWmA==ϥ癃F}qUޝ5W ڃ'ʰȲ5pfNYhYx?_}]30P75uow~A4L6IIDLV#AmͰYJFn7XFDKL qnȱ Zovh.a K֬mF(V! WmM9,KM r aRM015ReP"BWn;q`إm D[la+K-yA==tW)% -len5+mJC )Lk( i3Iuݎĉh$ep&2\W!r'o{{/8Lȕg}<y[ǽe^ Mę5H[qBJc5PH(a 04MI43dm53P8d2S!N{1vkJ@Uݻ͑ٸҶ@k4v0$\@iIerQ$J1QYF))(i[Yqӥ&r8ARC^lleYmB0 aQ"dKӾvR#Fs\JB'L;$BX5eRJR&JJBm$S1f6)%b*;`pRb1-}>lm*td"`I!S.Ȑ!D%3 Ѽ;!( a[F҂A P1 c&$KA.A6R ,slJہk™ۡV"5( 0 [!!+1$!Y/۶DI*+d e0W%R R"I}}>폴Ly}݄yyV n".XM WIEelq#$!HnX$! Rbe0 T.wni "HDϾ|i{e0"ξ]5ݱ[%)M3WDQ -9L* E4n+ҳI4T4JGiM$7mlh𣣩yBR"Ai.R@UKpZa<ɤ4)'smL"HbVbd "H4%%9d˥,r ʔT] Q.0Y)IBDI EP"Y Ѻz7vԐ'7nme|(}O{[I&[R$H0ך$8L @cQXv 5ZDdH#v$4mA  +b8 IIScwmܜwxcKfUM2h4{&dm{yYe-yOomYE|ܻ&6w>e|Ӏmr`d])XSwK42l)n[KXAr8m)2&ZKR602rd+K,4a$۫i,2%! YDRa Ёm&\f@f+L 2ī@ Hݹ(]e vՁun&Ai+k©&b, $434Ҙc KRWLbJe&# m@%&S-aZ"H[jca0"d "hiu$ eif]yyF^y^ېRijDVjJD RD"3ftlܠk em@p$ FUC稺;E}:^,`q0R`H4B 'KXcq.75 KV"ě38 %pV3(7Գ1dj̒5wFx #e"LX)tH^qLbzyizWBe۲~[GF54 Dc%/wC/TVU!-2 kd"nMcStq(o:0HOP[ɒx]Z\@(˕}SjIƗ.: 'lK!Q'NIߚ^̏df"OE3FrHzDۦFpO :0(Y s+R'xB2P {.wSxYB ":Ɯ6тZViAP;q"~3<>ܴŠ@H&' .Z/0j荰F2FA@deFlW TzfmgHR;l[ &3ZゼH%jnzܥaC`+:h`%0Txq.WM)Gs$c/BF#,i2\y@$5/_4UN LMaN1ȆL.0 P|$}7 e !.5WDo$1B)kj ?xٱ4,o0 ȃ9nU96t5F0SZ/dy'fCTp{1C1Y)Wk,sc5:pXi3fTDnb2t!XTu$C7 bHK&w1wZ#1-3(T }}[R=cY!ISW4,i<ã̍mTrNM8$`WDpeEYb'2WZ,qц@Y|B;j^m 8/dхM>G7` )Y؂E"ٍcuTcZ |萄Fows)T wm]۫GӍn1_ϛ: bwkk<6M%{Ĺv\Z/*`=2#iWCdqkdGϏ;o4얎=}0D#Ћh>G*\3 %LE1w䍧.gA_K:~n_1XǎuDVrlbfq0-̲"⢫ |Wٝf0Ktsè|}jvS u4k<j>Yպoy;EeyV^S>՛ξ*Lg{i8ՀPT:*6 oppp=ݬcNg9p37&b CoM"uJZJۧ&ps" l`zc̚YÅ,,ddS$H>wwZ P\LmlhÓU˄!t噇 lqP­3nތ!D:8 Ǧ1C#D-:1$ ?*"4Y:kE705ü'uMESBw:YFŨ> \RI#e+(S!cƉ!t:5C*aG0yC$ݩG dLAI<$ Qi0eab$Bl$R:ܒ7aOM]4u ߅DW|8#F"3rjmm,1؂ő{r^ e:1X\&BRm̮JZGZ21Z^$ ^e! J!bؑJg !Y,fRN@,"9,#F@d5ZBхs d*70@ⱸ^5|Al&JS}wGy#ߴs^9v$PvxX4!OA`2,OC/=7Fn(LsG!iIa>z-iReNNNbVn69 d2cYoRMa5[l~;:ހkߙ1X\T܇{;@VFg dڌwqV&&tZVbi?^EەknBT!a$Y>nўbGŚ#6 "XuŊE%i|xI˻Df&[Dw(өa9c5ydZ`j5w{$p ˙Bc$| ?G~$2 ".G|;ކG\ af`R! 6,HaȜlLeK[-YFmQeD0ph̔]#Vx$0k`a&k.2녋VUl82gJg- (ƞ4V6%qcp6yThl7n8* Mt$a^5 `xQe@Uaz ׾=G}|H"}CQ&g|ptuvZtaD"j>wXK: cn 7FkD#iS.ƚB`1 ȑdaeFK%5#'v bA^੕HvXڐdM̳^K1HjgwyRTг1%r)16ŒڪG35%ągC ,e =7nXFN¼4Bg+& "6pA,[1*;F"[%NbB #(;6:A镭&ѨWIe1Y5.Zh^'*r V%}Lh%ô xQ Z&c.!MNH 율Rzos"%(BIXILVB%'[tFWsyfɌ@ص5iR([*apݘD2 | 4v<7 x Bk%-tDZ )4iI*A@w)tZebFvŭ*kCpL̬816FZ li5fZ@[a%YUU( r9P3mvTyZ#WG0T$Bp„mQ,3Yq'U$pڒP"0Y(lzyLt4Ђ&zT-=S ZhQb )JmC`mq˧^)+"-z6HxQB grwV(NMh$X"XrmPfCE]SȀ  #XI$䂡ɨpׄ]!bfZ!"Kq YX䱼Pp)8ّ0V5brdb1Z/1elk2b LQ6AXf(!4/*l7Ka\8\"  uzFM}m;/$hemw]ƴ$p|Q;Ӊ=zlqw0Ѭ'Y8 Ivm0I?&'NH>P62p{"iWa?C/RgF8&o#O^ lx\=o2ك{LOPYO9xC'ЩЉDB;ݝx&H= x-J%*Dax I3t[&pVAAQR;qYunVI(:Xpcx>iOIqC\Ȼ3M;{T d%an]5S̅adWAA XJ5Ҡ tEdN#Q'"SȈfڨX1ZC {ZXRӔc3|y Ս=K37NʚWrҖЅ<M0©1M0MjhZS>7dbV\G6_qCG\c!m$ ̡~ 3 ^&.Rc NK ?'/^%cٳB3Tw+#JYr|dGKtdTZ̆bPr8RNPqFc.*R,i陁Ƒ4*1ܸMi%JYvHEnGf>Z:p,;IdJɆܯ`^ځY1l8Rk* -+YNb4]b|f3,rCDׄ(,T# HoyD%  c!'Yg}_tnt7$lەL4 ƒ0(MF^NЬ7KzŭN;-"ZmӬ}u.3}w/X01"FDz2rJRR@?mΜ2<=pz`pJ-=>U~u؝Rnm >ea,,4ŁAQ5C0Ғ組c[)9q~ =ⱔH|=I1GsraX}6M)$dϳ\?CbGsfב@lb3[aݹǕId,3@FEbG!UVbHЀ*aݥhQ2nzNe @71SH2xP4Ͻ X`fIhR7tVРQE=C,H,I 7gL"}A8ΉB f. ״6gVfO'ӓ!"B_nRmM01ߟ7gldi 16GeHx@X=/Ia0z"BB(Ȱd/K9X#X{u6贳;*J)IvM'c"I`zbXbD)>88mZ F1PC 8@LN !ݷ ~{~wJUaLHC~V1햘J`Kde夦M- c2ãV!;/"m5,fKk+$߭._K_8+M8 bbbI^>5'3ar_6m}AeGF/ŷ -rKs|߲P${_#k?=12'Je4#*EE+3;R0)DÂѰajN[ni):;$/ib ,HCJ7D5,̶ T۠wBо3j=a=fF{{݄WJ}ݪw}/?b`XDX<E]>,+%JbEL@g͛($6g8Chz3Ƿraioe .wUI? }zXC$@/c+/Ed C"mĿ*A$\Ck=W \d8\,1|HD9 ͼfm-~~Ww\UY[,R}VRx:D$1"UFR3bG= Lhɨ}9($CVɺNCLBK Ԫ mܐͶ#76v[RDU& ʐX .TAb 2U@i_^Ilwm[~$D1@@OZb\Ѡb<آ`00 ÊTBT\a gX߮;?{~6l'K8G-*MS{ד32,'CnKsFD:!OeP>3ʐ&ΔH~ D{P"Xec.l`Wi ncz>0i$N{ggy# =S@֖b}Թzjd$D*]" SR|/+b=;S3\d&L6 ?f}9ʭ_hY` T6\ PE'ƌ0a` S@=6eکU zRQ.|hd զrN Ol4Q\)>;+@!el%!Desmv|UH+a((-Lgdц=>xB/nܼM-P6Y-PL!\8lm0AsZڟ{ߧƳi]6[igX`zYKdtP#a HƸ-ͅ N[8nD͇s-NNz އ?UĚLdbV`] ȕ*$צb}|_ g9A eP;a @Hdz@؛#@XYnFszsONjr;B"#2OgЎ\x}^Law(ͯ0 @vרqdTnԉo4"Ǖb{Hz|Tv2! ŅU`J&S)~-a,!,J8 &Q!8~mbLZRR=HBt~1FxOV" ,JhD٢݀dX"8oV㊣o*ӔH;q" ^ȀFn0B B@dyt+~"6Qj5I C>|DL( CH `K }>$.Jh-)~o3ؒW'.Xd ~ϓyv[7gDIՆ#!)Mϛj>~q9V?وFg˙J1+.`>Yj+:tɵR(!Tjc+b@A=@4mB0Ȯ*d3'ٔ [ha)x[b qAK֗,XF ;+.K/~L"Cl B=3yS#4'蔘9Rq/]6BESmGJzv_tZ2$9wAV*I \XF*%4xd$3r0V*T^r0^2QפM$ճa"$I@!/S`F&b,^IܒՑ P&`bڳ;M2^݋6bӳm_]Yzv0 %$E~M{rk0bI|e da%77be$F :4ٟѧƟey?l(ԋ:z\T`.͊@D5.Þ=3I369X:?p! "?B{--}NNH>maP`hGH> t].;[q)R0Ŏ,ֲoSk[XAޫyhXdbe B&@![%ch Ŷ[=|aqFN2;B-sJlr9[[%Z#G@3Y$ F,EX $g0 5ljH8cQi ()!%Ok)ARRg|xjIIL@B,V@U?Ktm'M12Pݥ5V8]tm=o,I4 >`OqYP>|V(Cd[>500>)*V ,Vpk x!%# ?dqF(яHX5dpҁm&= H~7޿{h8N/ܑQc`bH1$ʡ("FBWkj <1$4 (*ݹDu4<3>W6-N )/$F0R d/8~̜H:q$Xyl H2D²&C{?d\'[8Fl5OlH{݀]FdLG0Q9NEH~)խ2QD`0;X!Hn2i +3oS h{X<#/(U  `,apH t,*1Z.YI f!>2Ɥ\3 '-Đ'Jbd ?DtZ ..԰bĔ%u%Y@/bmiqewNB@LΙ9m F}9Ôfd! ?2iT>;ANm4(,K%I;#d=Bp4GY B lbrӼȮkXv|y:p(Hu9w("(b$DіudWFpvଊmfGdW Hc 6 l!v0T01"+0$53)JJHgdӿx@ܼ͟fRiM$5zY$!")s' bܰ }5O^2, ZdCXBEaJI~z̔6v䤤xL_GYHn*,mtǭYl 2j"&#mā<=X{9\VJFyۺ$j Mvۑ&$W# L OזtTo󽶄k1C6L@2G*BAB0~͔=Ь罳pLM# |e2R",pqgfg[s9__N+Y4IX`dX#wd۶YZf`5uQ'y [^PbW󼼻(+,/i(8Nl+Kn~52좞՗E:ױQqu5ӣC贸:Ή'M$A022<>kR% `;{ 4=Oːwr=b^Z ZEi^BVeI)0c&+8f7Y2bL4$ri9:[ׂu6ɧl=a !TF)O)Ihh)th5Cմ2Eq2$q'fD3܇y=MjxIC샐3 B[wu_YΊ(8y^۬ }kBh(t$UYM8mז]WVʓih(hO픵2J@τ89z"@$%blMo]TqAkl #h`wnv[o;_ + ` y:,χ4;JƯ`u4>6$CX٦BEok }kaz#.&D(:1I*\r'pvX$)2xʦ1 Ab`xZpC/?{F(WK>1 z>kyr}5ʼnaK Ͻ7 0310yzۖqvIQ~\wNҐ:dod,A o^>Jl:HqH=) coX5Ltfa2"I,PW 4G-Aݞ룅' 0$0 ؙNMڅdAKG&rc@ha޷ ~}n|ӥ*Ei2  NG݌yfeYg RʇR9FB預i ,RCԅ^QG&Tu6īQC7ME 1tRH 7j@6$4Cg;8`gݞu#>6l,QW}Nqv%ځ 얁ܱ,#Ss4=n6A$ls(Ne\FppI>{#?OL0@HKъUG׶l:wi]Գ;cf9p()v* slfjE)F#!p( NMhsu&rq͈(FՙU.D. wL𾖴CT&y߷pIH+S{oVCODdjOHg~_Mjo^0 !=kgӚ{n}aD fXCGl̆V[I!#/7aKfV˴hQu=nt]m0F!Ӳ4&@nex ^5Ϫ# 2MJ$ %$$Ik*mTall*t x!(τu,X@C ='t&x g-\M ␦MTG4ݤ-jVB3L4AK.~F8-ăbL@Y(Mg "/(d> %`N9_9A&=)b~Y9; (PsD DpB;)K6Xg6flVLKIH/>#5eEk"dلD6aB%:"F$7 !ZB c40{2I&B I3,Q#ţ1B& /)GJ'O8X'_/6= 6z[4MߍJt9 jt@A $T~aVJY#iXU8#>&(fX "$'D>YXXoc8ӵCO`Bɴ $V0䮦Y;VdJCd6l##:ŬHW5rdHAf޲np<>;pbJfs$@ {,vTֺfqAڼ.]x{>_ $IR 7̓$,4id)$dH{M^$酢=+)2XN[$AdfGK@tgX!= b]q)jh@ Xac63İNi[vґ2 1"Q˄eծFD˴5 0Z"K{m863 jg- LKёrpk;ٞ y )so4ߌxHù% 21]P =06"p IJw,ڢpJatjW0=d˘dJI%Ȓ/+IBS~ters}r6cd,\]h'6?OS-'Ӊf0 ,\#8O!8>H۳}pv}&] + ZWZ}{)gW[T$C`|?f gWϮ &28LX@ca @AH4A|yIHn gH !샱W-Zd3xɶe k(NCizpO&* 'I@ӛJ²9#*[p-5cAtum@:lIƚo>9ҩ@'P$iC|,6ȁS}x3 8qɕ *b.c$;6}!*,n`a)VS)ǥ9UΌH9+Y]nfa AtY#W&vIcNh1y 78CӲ8aS]e`2qt? oT=b qtD(K: K?V#g t ͦiA,qp^/P%-`/Wd;j,J hyV:hmaސ%6C܈?|QU?- ?+?}?v8!~Y6Q[c܁PNA$G$a 1@? (!%ou|E_?oGI!&G(/q%b<\6V E+#k+fOu`✔lQ`P `aa$p+/DkPM9m29ex'{~q{+Rqp%vM#yQ'IzL(c9+ԊֳdN{zO 47]/T%)mS>{tn+9 c:OmU$38BhÃ?x  /yz1]ȑ/i)N*T A P~X˷_ԑ{; r?ſn[sU9~?~ڿ3{ T/iOwWd 8a2so2"H4I9't ^ 7SXX]tTڱ v\GjE و۬rJ>3@[V~C~YGRhs9lˁ 13b-$oXZCwoh/)(v=37mj`҉?Xo&5V)J(y.%]fLDg }M8b0"Y_ބj=jYVM hPQUڲ\AAyX72'&x,t +bһTA$O8aң?=_|G0~_^'0 ,y[WsF  @Ն~Q#QUDtOS2:9ym[gTɷG޳P&70%~Dm)E7'w#09A G?WJф-;~8 @Kpy?#c 1ӳڬ?Yy *Boqv KtZT{7Rj+vEx҄cW&9q94C ??_;Z=E=}~bށ$ ?  P_m`o㘎3"V* \jBڀ "ͨݐK[7qcEA3|0YF=i?E?q]~0nNV={QoCjm 2N 2CE\/bX%lAT,N _&/۴O*JF[P$4} GTIteD3& GgܸWh*$(Ѩ#H@3Aj3?t@,@oۈo>p礩 Gh 7 N+s~cϼiȣĔzH|`.#CR]2u`,F/ノVԽkKL 2h!,Muz/b9J[Y3gm#?{:)DhځUY#$^=G(V-UUWuXܡ m|a\ ԁ+g,(_Cumqx@D(nqȊDCX/҃c5wSi/R|J}qH0@C# Dp\O?X)82 m _jx2kPrIm|&q1&l?gd~_o_h} nZҔ 9Lg6r<裈6Oeqf/GCi O3w~6U _Bh"|ݙ#sl{\S|De j4¹B-a4 u܌K~:He~g}7VP.8-Ow&9go7xjʘyDTOJ!_pnIkhY|hy-$u4xkt/=P/P)$S-d]Q%\$~,kNa,#q,q?/hW vI/K%qذJ܀Ϝa)RhC_)86눡\I3.%0_F@Y;Orq=#|vI^8_N0%!__j|T}Aяc ]quZM!Az\&zQ0P@SB?,?pJf?`cx'Tn*bZAP A@ׇ0.㛪 _!uAch9~k_RI;_?g7>K 2C~H?e/4"1h2M:YSZ/$Qjtnh/ N( 6$U>9Fͺ"tUng-eWUTr6S{(DQw+?Qq?2!m3jߺLQj. ‘CࠂM$"9qs:GP\{T -D?d3K,^MAbbDɢX鑢4/f4'N+5طr"?XU N2qpܲv:3t} [>a]P^n郹P:F"AKډ·w949b;xl`%olQp1|f֏Z8TN>IHC5>SLr nW#E(Q뇾IgUM}m7Z\'?Ma$-$N8ƃGOt. ^M:҆f>~Twޑo 329Iw^)qESl`UArPp,HQ?*;_w;2X@=߱S@l)wYuOHS̷RQC$hRF ֮DX |x*y yK{DȥlңʁQ~SDY;ƤMjmuQr} ԳaJˢu4(jVʿO[J> ?9}} l _0 V4J#-$,@~!˜,A| A4Aaq8C Ta":wD<J]Y8`p N0 * (h 8O Z-( XPK7Da?!+s~enO=C-J8k h? ?G=g0 U`eWta鋤#역A6"@Dir^zXY &?mQ۪ hM+-=i}+_.5 ;)l.D&M_:W 8?Ȯv$;PNc0o@m@0P_Ύ>#{ލ 8#" a$C@p_IA_{$>AB+_g`Mp:BWq,"AY [1}kA|jLQ}sE?>ߵ#LyOIg5o'Ty37*?WvC~Is7bj.A?~9$/+ʝ;8-{"QH$ LcyjKaGM-00mD{3#m&i{PħyLO:Sf9FE؛$S̱\3 w=@@$ +&Mdqt?ErIFiA..C64U86P18$$%2L R}J7Q:U ꃿ~x7nwc\۔3X(aWJn]/f1c! F? CBݙF\uxpbyllҀTwV6mhZHtAjpJ7@r*Μ1f̏kT#;gt7eD @,0{a ۼ@jx #fDwlh== ϲP2S>ld2&Q0?!VM$}]%'k,F"LjY0.B" DžDmr}0 cQڒ܌7 l\!n/I̲ \k P;|?EE+1 (f<9Ң7 !rDh G~Cs0Q^j<熈6 Q@?TeaNMxGXz6( Cm {2lͫ/( pM{Bp1D~fK& \{O/w7i)׶y= yc"Pai|ӍI C!td (^Oէlٵ$"1Q蕄VX3aX@4% b.l;OIcM~G? m[=W?&0nG S4r}x\9$^ՙf*bc;(#"Hexade#9k+vFSw 7zI1$ID#H|BX2 4 VM'i[9W?~G|/c~Bڢ :"/ Qw73D~c},J؅eʃsbG%h!i?sO{q6lJvhNlDKʿWp$2iVbhA9+J ~Ūyy._!*CB_a5Ķ!u_ ˷E  <12ۙl +U{µ?XD~Q;LVO'm7ѷ܊|.rP@AEy/~#0Gv' ^QzLڒ P~q9$} ?^˪rcL3)D߁`U?Sc`1M)oboZ)0*PnUx76eGZ"^_)k)%t$u ҄3)aIY ,,3S' d>[C6٧x۰l &t 8L~BR?y:7MvH$)\D h;"hn6=ϹPCͰl ~U1깙pG=ޢ\iT*6Ӳ \6O6K0a`lOP c4z>4{Y&h?i#)`Oe/]e@h|խk`ѹr{wפ͆zN8"E" D}ϡ޺W3'V r+J=̜*EGalCKꆍD =|Nf֙I|Ek1\gWGo'T0lٶ͡!g lai#DZ?wl´IO>\(6("={J~lQ_]C=y.9H+)wcw b/m&.`$}N#!^JhѾ=W$\2XtMtJ#g4$ '>ׇ`ͼ0Dwen((1sE,Ꝼ4bO.;#!C"y^O|xѶ]J8iS5V, PĊ$nٶ=T:fs-9-#\fxgVN ̕ `*bj=͇Fc/qԟ2z;mN"(c(Z6BvN~R"EgwEsRbđ28CF/64j#޽ֈA={oZ |@n"2:3b/t#>`<ɰFܳob4Rl P_M`IA#$[h|2lM&IryB@S<b*C1t~<,EU~{y=Gh3Go,zHIi6D셦xOW'1?|^)'o?^> L_[DHhF$c4Ec>pHdx5ڰ@A"G Ɉ^n"ۋ("!J]d 4Fh'y_W@mdͭ}XH~mNa6@r&2@sal9vDI3q ! o֒ } wf|VdBQO;|F @"/ a>AѱѰoCOidWyrOlbD <9>;Px$1yJr燓c6$ɳQ?=0vQ1=MZMfѳ"Gb͇lɥ`Ř6-D+6p@0/ 4Epv@ď|7Fۗ~8qkIum&^.HvE!l MOj2F?8L?vIIp@O鷆23@ 4g>3''|dۚNI?u>3xp "n0'MB0E:>, I q-˅kxw܋{bH$`/֤3l0y"@Eڍ-{87hX<`Z'bC"0[H rphh ?Rb 7ϓ wxϯ\}לW #rTE $xhdbPA$<{lqʷ~HC$qBZ/"$D8V+l8aM׬{:V#kw"r,;$(}%BkZm>&MyǺ_XNA5C8u|9/R ʰ桒-sllH"Z0' qucudE;bJ 3or$I; ;^+ i"#aN߄=`Ȳ0x;jALP A?_<_jyb7~mo_=M)g?䇑C|lGT-yL,OM$N<1 Xf/p7|u"J]ZwT߬&b ¥a^q[ڄτchih(S{}Qnc"(FontZw?$Z#Sr8Y !"m7?𸑽 RTN?~8oƩ4 قVw3 0]3Wa5l}AU_WrC:/nIӪ6MŽ4LB|VSgnfOMy\u^x$if4qCߌu1]P#w )ZCHމ֪P oz+&kvؘrWxN*on^vbPnyLe?zi9',z~<.eN2\| O i2Ǎ_qn %V1zW@n;?2Xoexu†%8/8nj-ЖxI?BZjЀ F&@n ,"4PTrTE`YJϮ T$Y?Kߠ.џӬ.&EMc"&8@|aD.0S}SO_7J˷k.ϡG_W@OT9NKwq7;b%-S84YRaUh#; iq_f:zOCr>7#$Ǵ; ]>Ʌd @yhպh|$*C3 'E z0ϛ_Юs3PmK.>gV`Bx]iO?)k U`믧+R4j60T oPȒ )[㊖Wb/BA#MحB.ƷaZ3-eUJ$m^DӑJ% hn:ؑ֟p_EYV]:=*=(GscPVpG!XP'Gxey˂E_`"<]|/%ny5|YɏNV0pPߝ&@zz/NsƲߴ`E~L@4٫w R0V1N@ q<#"kJ*".il'c@C*Đ+ xך=[&;`` Ζ_&w>dvRoF&"R@I}n),jZ/WW֑BVX|$`b~]h$ DM^o)Zh/%wK.`KL7-S0jFrpa6qm2b`CÄǦ<@QL5mI/\zr׾$%r3ڮ ZE5z\LJ ]ܻɠu;#WEBELrj'ovpÒE,OߩʷCA '|i+P6Qd`h`'Cgn#*DDk6@:6~8-dtٞ(Km1"=tL(B9\H:Y.0# nlƨs/kDx8xk!@ZAk OYa:w1DiޒUϥg=̊NڠdZ^}qIE mz j9F$:BD('KgRɏ! )P#3qpϙ u?IB\E`\:Bڔ(-qn\k>1mwP(#ULw栯PY;'RpʕQ_[ZN`7SޞCvD^ q21۲)N8HYfbz!E{첝@{0 ңn0YxhV X{ ;&:PFM<Jబ<QY/ɂ} 7$d۶ \K#Z@zt{#J_7$I'6 P1sYrqlF"B@/EG~e6YA1A6X$7P?17HD:pL*O$y_u8՘8PhDB"#y bEM["ly6[x7dq\M&ްx@9aEvΨeߙ3鲸WE&fLΖLI!_2v0c)ŀZǵJAnp2Ѥr,58wAl4_+ jbnNF +"|spo`u-=PֽI@ -v)@zwDٝx6q*U~iY# ͖4O~iDV3>Y03ۥxML$D>G.fF>]B'WU"`[Qiw 7O,x[{rcz#qSpRi۹-#7S r̼Qqx E!Yy"|Qm@Jj #hޡ]eݻ1|l5^E6g&kCIIWiMc #qʉJ^i ywH#i'vot<%HdmK1_<:Ѥ3a]~Ҙ@iKamTPqi%c&&&}NVш[v:b0L phw+4>ieCvQ ;W`䌟Sq~/Ͽ;+Kz+b' #8IČaVʛK0\@wj+[_;"%_jJ\1N \!QN|/cKŌ L:hu^Qƈ5]x|W5sS †PY(*%۵-:|3~ J0dl}3&OFfyR&gٍcuoPvtFnOi؝EO Cū-jiشar,H/J#R⬎2 qtJtaP&Zp@*x@a3=&ڭYQNZApw~R=in մDRP(Gq3ӞtE#OƆj-&,b?77`azlͻY]&z% HSN_,Ð*j_t6!s/o+tE/n;0{ӏyJ}~5og"YX rK]9pIk"s/8 _=tD=>|o%{AE9)\MrYfZ4d({Рh?á+7*h_ Zݸ$*oZ2:bn)c# >gW> oؖO8OM^x[$T"m%y&&(ފo^hܼ>K#=^eM3:rt)g޽ɺzRkb1 D;^uaT-x# D{/IgOt ԔK#S/tÑsךR0PA\k`w-Ā/R kR^\쓋ىf>]#ici`3Ao_)HM8/M@el #{ų\,%%or*Y)UW܁+w (ASpn\uV87T(BH˘e뮄F,Qۓ֢Dj;-MKz 2%XpץWl^TMCڗr9:%{!W5Y ByNB›X3K(O Hm+uy;pZTL2GbY^Ls Fv ?1xfbk7M}c)wy E\,iذn deGҧC!`Ol%9exG# m!D9؏qMb-'A'nBgϾp)OAUgEڈiZ#{k1+LQ0weÑ3_!Qܢmӟ#ڃM}}@Y8%M  I~-7R=<59L^ dQuTTq 4M S RcD{iVG(ů6*=*zśy|:wP3Y(FhxA#*^bE Mkw9_GW;0]c?=_'gig<u #;8''4%+mxw'jWHIpJm5^ , +mˁAIn幋{%Y{sCdYGC2EԘsՓ_(T{"#cU-cBJ~zFC\'p2edBbx3ɒw/46sMwf\-%8>h壀)dZ_ pst~A'ul *9v"kE4ؐ"ؿϥ,Hц=nk:l_I{y^:lcI|dQɔ3+gZvfdz4)B䭾kLqc^6 ؚz,n")@!sgk!˦`EWv0Y{Dս-,lHmVOkN?wmnI449 x׸x O:Jf.]$1B W}6L 5'M tB:E GW*\8wTƌUUS\:̐IvHƁykU)@N견d$. kٰ.R ҫ)O kbVHV#I:oVֲ9֫O`Q3{Z?-_\Zδh Jq-/pۚýeL1LҬ8 {GFºSu5o`bXN.EPIwR+N E~H$V|BA:p`zN[+5}s:UeQN`L Tλ0vHA }ڵ0Z2Ooy 7HA`!vYB#jܜKg:P&% |RQ+num(,mJw喃#cClz-XWĸm!KL;덺hh)W:]x=? ٗ/ilXċHĴdX ݃vFK"B^#'Z[b.)@}Md2 nRWߜn]@d@tp EtYxMʹv~b(p<*RhW_xwߍ* ٺ#LB>NNxrLm+v&]>?l2%s9w5|I(hveA͑c&uPt mڔpP34eW| t»T 'S6u) B0pC?e*:ztIBX %JD:NYLu 5WZ%$iG@dBJl H*¼U(r iU ܠ2xA`"!B;J~Tk) "5} !RmL3>kޯ*xʱvPr>OP@My){ZP)gi85ʹ`ZVh1/+/C& MeWxg= K=+-y?r#O10aVb(8>E[3=ҲT Y%#؜3n(Dd|\;qɬП[4evʳt<-OOar~73k";0qmpn7P^aԕ<4~/1o6W NF!^I1}p-dee :쾮ub#E^6K RzUƛ8NakoESGBJ䔟Y`t|)pk=; O "z6w 6qҏ9Ty9i:v} "tU[n9F)T(CwI4e?3DKX(HfޱgdsGXSk\-P>u^iAYqdp}cbP hk"$! Δf&Βm/B߬џKW˨d@`-QuSM˞Y ~'cϫ-ǖ@q<J~Isʹf;z#fvu0\߂*=bԕ0%&/o31۸u#wo^s>qI{r$,8.nE ȢZ!%9JlpT-\(h| .e=pkLH}A€x}*|Cm D!tS#=}{Mm]YyX3LOU+hϋ=Iʷ~OT>M:M<Fp[ a3D;14(ӓҍt,w \P |Qc9V>~37Hjey荚"#nQ"\;4zDhQxA.[\FV ; >/]A#i'._j5kLyk9i)!w0y-jkZU*4`'mu{~&Tb\$&wv>˽6|7x񇼱 qxgK3lZPFu>`5I7=Tё6A)[:.ys Dg*|r fT:q7 4`V5k(IvP-㵦;1yun-xZU=U`knaiQw/E:\t8 m) jylZwB,~UGQ̍PnOFv, \"U]O2nuyƲyiYY rr@PԘWq l3sV?n"}+]n(zm J"0%8;4<';}ϔh7J!xn{kH\9Mi< #^iT9wX X{>'bvn!.J9cPuizEmSzc)'-z5 Q>'^)usbGu۹[CX3/C?{;iE*fs۵l&% e/VmYx9,IֆP!A/ye9X6[UTr wj S(vC1y8 ɳmM{ƁrR.݁ߋfoqAY̌}/H@sXۏv8ؑzP7M4'E7*K>̂\21(ě'$yD^)cn7fgU=nVdnQ?nGv!6"#4R ,V,lv/ё$bdzg$9p a? oW6<lN#roӘ :pnoS|JH?N8&_zoy+RgпNF,Aƍ]TTGlM%[w!^!mGU Vwʉ6hzKo8u)>9Yu}T=N{Jd7Fvdf9Ք50Hex'`I mf9vu>x`kU"hf.DeN`4H>(#x#뀬{d AĮw owad+T%C߫fq\a~^H2ԹtM503ˊ,Ҵvo^A?AJL)N_w3mx6O~U-A/t@1H{h&hs3l7pH\L⧸J-7US]6#îIA&e!kj~w' q,HPi@Kl1{ n ƀN  ēo弱{F\%!j[,D@0WJgЧ%#-dK WQ_E8Ռʼn;^>8*XqqV@qߩ@dԯ1lہG3 h$P;1k\{u6n![_G ^aT=&NBi!qKL]HgZleY!Ase92'V'GML[粿iE8 0ץ,I\- /}oiG+D4 Läl~ҊFxYB,"ȻG@omJ/ZwBXD:VA-7z | b7%Aה|DD;SK,!GaDc7nDxjtoE.3Sj0I"լۑn1k]q",9a'pT@bqɌJ@+ ~+6jʟ뉡khSZxUᆴ\5an`7EHkgMPn0HCtuon%%##X[[A%2RۉAy|Jog8BJH(2{5oΆtP,"!^䦍&fOuԴ1{) DZ >e>B¨|KUxD^aFg7mvj B '~VYe|rfͲxM 7L|WE!ǖr+$)2՘tU}ht#XGTo06.C@вdӀVt٩?)} 8Fa8:ND0m`9 4*PQ3Q8cm@SqTw]יGK֒rEt\Ky=vVT] Gq]{ZN.: <")èRN- qmG0]!p(^h*'P"DTЭ/VQww]y%A)9'GHrDprss.9@ĔynRWRHpEt\Q!8$BPK;'9RT]ךsfyjJb쳣 *(i&cDPɴtupKډhY|5mZHHQ+شS r$.p: qrtKYXyR'=8μv֧:N(A$9夑@.RA8q!Ĥt hm JqHs6w:KjçCXl[gIdr9 {uCqΧB&"";o"q.9NNNH.D$I p"=S8?9ʢ(y5(hGDHZ=:"8:H8r{vu9'G98tȊN%-Ȋ %9{[2(ݕ'UTRB!B&VB"D@hJ*% H)T U@B"DJ@4 @TRAbsts!' )Zܽܧ!9sNq<7(㻮%""(E)" -b" UJ ЕE]iWwtGn00ZB99 ! q!*c5 *i:$r "ChTTRRlNr"NH'BD1ʑRQJRBG0hRpctQJ*P P%]Up38P[Z]mIIМ\% 5KH=AB*Pg'!;:H)$(N R _l5"AptQ t: ,.pH!::NY7Y=#Y{׷NRqa"\/j\HN=jI'"ttJQ$9jqBI$QG"@hTiDU((TTEQ(fB%("DerL܈8\@yM"TTRtH45TSs8)U(D)X]]S@r99Lh-J4%l1MUl:bVNJJJh&Ӂ*hhT(XJP+l-- (P4Y]vgX3G!NDpDybtq˴q.D yQ:RI^VnHvIE$8'tN~ YI.HjZ3pIQ@%)G'h:kG"q 8g$HBr9P ĤPt 'TOl$tH9 s#mQJ!Gt!;s.H^B"Aӑ[hN䐊RDGqN(&( (JD*R"P @` fdΰFD:r*:"jBbtD h(NlGT5Li.NBVPZTU@PDPFHβ9:;DFfJU F)PPFbhG 䂱"  HB!@*@AtmW`8%3[aӸQ Ien9CDHBEj!9QrCJ .rJ ;;NL8JDDrڭk')ns9<ԅĎ9I9BkjH$B%{V)Ј]wT Kۻ;) *f() œGGwEwQԔDU͊H*$(PHsh" !Q@ZI" FQ)ihUCt1QQtB% A15$۶Y'H)L梁w9!n EH"I.S+Gt) rtN TGG98tSHwGu-D\ȺB`TEhU a h1CM*1RDEDT[fU4*A*!HAU %(((DL" HSBPR P)T]WP([Z8$t#:{`p8^v !"$p]s\D*(&PJ&ET(UARV(҅'..8cp"B$PJ(VT(QH('"!":{]RHHC*ĊRTCʸ8HGHEDHBM(( }CA-4LG'1 (RTH1DLBVT))F$( *@*iWD()Z UPDZPQ*BE(AQH(hSB(t]&Z4 %!@  RH B4!JP Ѐ B"P(( @- @R /~EOx'/G72??:GG~S6#\խcAP(4"P@P( ""}ϭ<:V_D +JҠ qy(w/oa(> >:T&J?5D~9\OX(s Qs~;H9۷OiQ(CȨ9 R P ~o____^~J"*( JҨҪ4 J !_7}>J B P8/P 2_? {~Sh -j~1lnҟ_@?_~~cVߢ>c x@R`{1~]>{2~N'J>Gv;| F=C4@{`/Ae !d7lV` a7("^ g7mo}>a d[_#;z;ion]G]ov}/93_ ~nGr#X$;`0cͿa#? C|`4qq9lDyT>EXh s"دMIAD(?Ea,ianݮ' r *nY@<ޛ!3r4Iu QCFr8C($\4LS}8xC"lr$Nq^1ѩ$쌄Hތt0XxO*])T[<.L@-BPDD$aY҇^uPau3bU{8G)p0#G;_3T'J.)>[~1G-蚬8Y~J: Yw...5by^^Xe/ÛR9NYۤμt"ԆLi )7JD XȔɰ13@Wo\ nYyX8D8g 2]s+wd$hI=6? #> A¢gbcXXVIjCם7Hԇ#zQԆ0k7fX*YLn@$P݅ӧ5K vӞ^iYNTyfNɶi4VrKٙUDh<*EULIud8+YG+䦁gKKgi4 Cߊ~EOϖ^1NihEI;f qgN8 RU6@nt)3@$8391EK,1f Ǐ<2#8Li+9Y]2|˭%>{hk=:duR'IH {&l! BHd<]p @F!rfr׬6pFXFrR5.PU($>7+|$9 (𭓆x#,|873JGPIMYH0Hx4T0x(]#5r!O8C1f#O<# ;adlrXϕH޵?w?w;wǟ>B0/#~Я aG6_1П|T5 `b~?wԽ:H?ܙL!}\"hdY?ϏhNki!0pڙnس)wN;Q".GI zg7q>/u&њfNbi${_=6fH4Bn?&buֳEh$Һ^ 4aCmr:@r7]WD8i7nN*sTe-R`A:t1̺1 dQ3 :"O)S 2Y}ŔʹH#Ӆ3IJs'!qd\@ ( $Q&t Jxf$I:c┉we:g55ɢgܒZ̟z)!d遁$gG\uo=H+q`7lxpiIS INޯVo:\4rSLWHX9K Cy('a읹w$S 7forcats/man/0000755000176200001440000000000014364755572012510 5ustar liggesusersforcats/man/fct_inorder.Rd0000644000176200001440000000205614355354616015272 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reorder.R \name{fct_inorder} \alias{fct_inorder} \alias{fct_infreq} \alias{fct_inseq} \title{Reorder factor levels by first appearance, frequency, or numeric order} \usage{ fct_inorder(f, ordered = NA) fct_infreq(f, w = NULL, ordered = NA) fct_inseq(f, ordered = NA) } \arguments{ \item{f}{A factor} \item{ordered}{A logical which determines the "ordered" status of the output factor. \code{NA} preserves the existing status of the factor.} \item{w}{An optional numeric vector giving weights for frequency of each value (not level) in f.} } \description{ This family of functions changes only the order of the levels. \itemize{ \item \code{fct_inorder()}: by the order in which they first appear. \item \code{fct_infreq()}: by number of observations with each level (largest first) \item \code{fct_inseq()}: by numeric value of level. } } \examples{ f <- factor(c("b", "b", "a", "c", "c", "c")) f fct_inorder(f) fct_infreq(f) f <- factor(1:3, levels = c("3", "2", "1")) f fct_inseq(f) } forcats/man/lvls.Rd0000644000176200001440000000246013626051403013737 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lvls.R \name{lvls} \alias{lvls} \alias{lvls_reorder} \alias{lvls_revalue} \alias{lvls_expand} \title{Low-level functions for manipulating levels} \usage{ lvls_reorder(f, idx, ordered = NA) lvls_revalue(f, new_levels) lvls_expand(f, new_levels) } \arguments{ \item{f}{A factor (or character vector).} \item{idx}{A integer index, with one integer for each existing level.} \item{ordered}{A logical which determines the "ordered" status of the output factor. \code{NA} preserves the existing status of the factor.} \item{new_levels}{A character vector of new levels.} } \description{ \code{lvls_reorder} leaves values as they are, but changes the order. \code{lvls_revalue} changes the values of existing levels; there must be one new level for each old level. \code{lvls_expand} expands the set of levels; the new levels must include the old levels. } \details{ These functions are less helpful than the higher-level \code{fct_} functions, but are safer than the very low-level manipulation of levels directly, because they are more specific, and hence can more carefully check their arguments. } \examples{ f <- factor(c("a", "b", "c")) lvls_reorder(f, 3:1) lvls_revalue(f, c("apple", "banana", "carrot")) lvls_expand(f, c("a", "b", "c", "d")) } forcats/man/fct_relabel.Rd0000644000176200001440000000234214357100046015220 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/relabel.R \name{fct_relabel} \alias{fct_relabel} \title{Relabel factor levels with a function, collapsing as necessary} \usage{ fct_relabel(.f, .fun, ...) } \arguments{ \item{.f}{A factor (or character vector).} \item{.fun}{A function to be applied to each level. Must accept one character argument and return a character vector of the same length as its input. You can also use \code{~} to create as shorthand (in the style of purrr). \code{~ paste(., "x")} is equivalent to \code{function(.) paste(., "x")}} \item{...}{Additional arguments to \code{fun}.} } \description{ Relabel factor levels with a function, collapsing as necessary } \examples{ gss_cat$partyid \%>\% fct_count() gss_cat$partyid \%>\% fct_relabel(~ gsub(",", ", ", .x)) \%>\% fct_count() convert_income <- function(x) { regex <- "^(?:Lt |)[$]([0-9]+).*$" is_range <- grepl(regex, x) num_income <- as.numeric(gsub(regex, "\\\\1", x[is_range])) num_income <- trunc(num_income / 5000) * 5000 x[is_range] <- paste0("Gt $", num_income) x } fct_count(gss_cat$rincome) convert_income(levels(gss_cat$rincome)) rincome2 <- fct_relabel(gss_cat$rincome, convert_income) fct_count(rincome2) } forcats/man/fct_c.Rd0000644000176200001440000000124213626252103014032 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/c.R \name{fct_c} \alias{fct_c} \title{Concatenate factors, combining levels} \usage{ fct_c(...) } \arguments{ \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> Individual factors. Uses tidy dots, so you can splice in a list of factors with \verb{!!!}.} } \description{ This is a useful way of patching together factors from multiple sources that really should have the same levels but don't. } \examples{ fa <- factor("a") fb <- factor("b") fab <- factor(c("a", "b")) c(fa, fb, fab) fct_c(fa, fb, fab) # You can also pass a list of factors with !!! fs <- list(fa, fb, fab) fct_c(!!!fs) } forcats/man/fct_collapse.Rd0000644000176200001440000000205013626261320015411 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/collapse.R \name{fct_collapse} \alias{fct_collapse} \title{Collapse factor levels into manually defined groups} \usage{ fct_collapse(.f, ..., other_level = NULL, group_other = "DEPRECATED") } \arguments{ \item{.f}{A factor (or character vector).} \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> A series of named character vectors. The levels in each vector will be replaced with the name.} \item{other_level}{Value of level used for "other" values. Always placed at end of levels.} \item{group_other}{Deprecated. Replace all levels not named in \code{...} with "Other"?} } \description{ Collapse factor levels into manually defined groups } \examples{ fct_count(gss_cat$partyid) partyid2 <- fct_collapse(gss_cat$partyid, missing = c("No answer", "Don't know"), other = "Other party", rep = c("Strong republican", "Not str republican"), ind = c("Ind,near rep", "Independent", "Ind,near dem"), dem = c("Not str democrat", "Strong democrat") ) fct_count(partyid2) } forcats/man/gss_cat.Rd0000644000176200001440000000145014241555020014376 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{gss_cat} \alias{gss_cat} \title{A sample of categorical variables from the General Social survey} \format{ \describe{ \item{year}{year of survey, 2000--2014 (every other year)} \item{age}{age. Maximum age truncated to 89.} \item{marital}{marital status} \item{race}{race} \item{rincome}{reported income} \item{partyid}{party affiliation} \item{relig}{religion} \item{denom}{denomination} \item{tvhours}{hours per day watching tv} } } \source{ Downloaded from \url{https://gssdataexplorer.norc.org/}. } \usage{ gss_cat } \description{ A sample of categorical variables from the General Social survey } \examples{ gss_cat fct_count(gss_cat$relig) fct_count(fct_lump(gss_cat$relig)) } \keyword{datasets} forcats/man/fct_rev.Rd0000644000176200001440000000053613626051403014411 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rev.R \name{fct_rev} \alias{fct_rev} \title{Reverse order of factor levels} \usage{ fct_rev(f) } \arguments{ \item{f}{A factor (or character vector).} } \description{ This is sometimes useful when plotting a factor. } \examples{ f <- factor(c("a", "b", "c")) fct_rev(f) } forcats/man/fct_explicit_na.Rd0000644000176200001440000000206514357073063016123 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/explicit_na.R \name{fct_explicit_na} \alias{fct_explicit_na} \title{Make missing values explicit} \usage{ fct_explicit_na(f, na_level = "(Missing)") } \arguments{ \item{f}{A factor (or character vector).} \item{na_level}{Level to use for missing values: this is what \code{NA}s will be changed to.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} This function is deprecated because the terminology is confusing; please use \code{\link[=fct_na_value_to_level]{fct_na_value_to_level()}} instead. This gives missing values an explicit factor level, ensuring that they appear in summaries and on plots. } \examples{ f1 <- factor(c("a", "a", NA, NA, "a", "b", NA, "c", "a", "c", "b")) fct_count(f1) table(f1) sum(is.na(f1)) # previously f2 <- fct_explicit_na(f1) # now f2 <- fct_na_value_to_level(f1) fct_count(f2) table(f2) sum(is.na(f2)) } \keyword{internal} forcats/man/fct_match.Rd0000644000176200001440000000160714360012612014704 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/match.R \name{fct_match} \alias{fct_match} \title{Test for presence of levels in a factor} \usage{ fct_match(f, lvls) } \arguments{ \item{f}{A factor (or character vector).} \item{lvls}{A character vector specifying levels to look for.} } \value{ A logical vector } \description{ Do any of \code{lvls} occur in \code{f}? Compared to \link{\%in\%}, this function validates \code{lvls} to ensure that they're actually present in \code{f}. In other words, \code{x \%in\% "not present"} will return \code{FALSE}, but \code{fct_match(x, "not present")} will throw an error. } \examples{ table(fct_match(gss_cat$marital, c("Married", "Divorced"))) # Compare to \%in\%, misspelled levels throw an error table(gss_cat$marital \%in\% c("Maried", "Davorced")) \dontrun{ table(fct_match(gss_cat$marital, c("Maried", "Davorced"))) } } forcats/man/forcats-package.Rd0000644000176200001440000000175014241066126016014 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/forcats-package.R \docType{package} \name{forcats-package} \alias{forcats} \alias{forcats-package} \title{forcats: Tools for Working with Categorical Variables (Factors)} \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} Helpers for reordering factor levels (including moving specified levels to front, ordering by first appearance, reversing, and randomly shuffling), and tools for modifying factor levels (including collapsing rare levels into other, 'anonymising', and manually 'recoding'). } \seealso{ Useful links: \itemize{ \item \url{https://forcats.tidyverse.org/} \item \url{https://github.com/tidyverse/forcats} \item Report bugs at \url{https://github.com/tidyverse/forcats/issues} } } \author{ \strong{Maintainer}: Hadley Wickham \email{hadley@rstudio.com} Other contributors: \itemize{ \item RStudio [copyright holder, funder] } } \keyword{internal} forcats/man/fct_unify.Rd0000644000176200001440000000075013626051403014745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/c.R \name{fct_unify} \alias{fct_unify} \title{Unify the levels in a list of factors} \usage{ fct_unify(fs, levels = lvls_union(fs)) } \arguments{ \item{fs}{A list of factors} \item{levels}{Set of levels to apply to every factor. Default to union of all factor levels} } \description{ Unify the levels in a list of factors } \examples{ fs <- list(factor("a"), factor("b"), factor(c("a", "b"))) fct_unify(fs) } forcats/man/fct_other.Rd0000644000176200001440000000167114357100046014737 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/other.R \name{fct_other} \alias{fct_other} \title{Manually replace levels with "other"} \usage{ fct_other(f, keep, drop, other_level = "Other") } \arguments{ \item{f}{A factor (or character vector).} \item{keep, drop}{Pick one of \code{keep} and \code{drop}: \itemize{ \item \code{keep} will preserve listed levels, replacing all others with \code{other_level}. \item \code{drop} will replace listed levels with \code{other_level}, keeping all as is. }} \item{other_level}{Value of level used for "other" values. Always placed at end of levels.} } \description{ Manually replace levels with "other" } \examples{ x <- factor(rep(LETTERS[1:9], times = c(40, 10, 5, 27, 1, 1, 1, 1, 1))) fct_other(x, keep = c("A", "B")) fct_other(x, drop = c("A", "B")) } \seealso{ \code{\link[=fct_lump]{fct_lump()}} to automatically convert the rarest (or most common) levels to "other". } forcats/man/figures/0000755000176200001440000000000014360013050014122 5ustar liggesusersforcats/man/figures/lifecycle-defunct.svg0000644000176200001440000000170414241066126020244 0ustar liggesuserslifecyclelifecycledefunctdefunct forcats/man/figures/lifecycle-maturing.svg0000644000176200001440000000170614241066126020444 0ustar liggesuserslifecyclelifecyclematuringmaturing forcats/man/figures/logo.png0000644000176200001440000013154013626051403015604 0ustar liggesusersPNG  IHDRX?gAMA a cHRMz&u0`:pQ<bKGD pHYs!7!73XztIME 7xWIDATxgG}5l{̹:ՅvI#ʍ A }Yi ֌ F{sۑ&tU4ES2GӇ +Ev=2YQ}yvϿ< (v>+oRwMb{ 7wp%b ū]0?- Зn%.w>%;]w:?;%]B(o8>=7P]%;-ĕρ3@{ɗ4;]"K%;}@j~D\|p;x]k ߁wsK- 1 ?u%N.wWNsO7>pػDa%C8~w[5:%.2"n?9ou$?6:%."W/NmquMe]E-` q>忏ߐy7#<~c.v PK7]ju7ŗ.;HOO] `cd~#m7 .wHaW%cgnxؕ->'v { }C?dWXRaW +oؕ->_-E2 -W,{߰+[WD]6e;p[ᅤ_}mτˬN !*:z@~-Cw$@D^oy["-"@u,穭۾]׋-~=ލK2?I5N k*9\z#"FPW._Q]Y^!wpS?=-W!y}]-)сG( z˗/8]}CȪU|a JCnݺw} RQTYU+훶!#߻[]7Z7~HoQwh RuΝZ(?4؍ʛA$DKtsUΜ9C<\edxbV6^m?Row}";>QB5*m./_&J1<4X47_C(`z\ի:tq"쓘| elϿ<^Y?twx/d~(!H2JQ$3;;ӧQUa3g0??twIN 0;$m񮞏)d~g}4B{XZ`uu'Ow~FqhZ4 x[ޑ~jd~>%I4R??|79rH$B>gssZF¶m\k'(Ȳ-2Z>3t]￧ZQA<E5vF?z5$K`s'yxWx>AϚ[w}W_}W_}ŷ~O?9088eY *xR$IȲLn;8dlϿ}姾ߕDi}R;J'6.Z4 w^666( VB!"8Z$}<ϢŲh j$!ia$EEDOlx׶I$ヴ@#udEr]^S_qjqrT AXZZΝ;O eYF$TU\.G(buu~gP(8nը>us# ;U'}F EY?t-wi6qqd,"+xgT0ؔ[X^Dr9 a:BLFvF0" lݍ}]~(;"j$xJ%l&l6Y[{5ݥyZ-J β뺄h-B#xk<yb@%Q[ԱDID33?5סfil"GF^C k(Mz*]=j뺻D~uH∴o/e5 ε+++O:feeZm?ϲ,"sssT8!MES~m0G&2Ze e")~c;Aۣ(H,+ױ]4,buvA$>L_\^C:IY'lWHLjg\ͦ{KWHSw.$=3˂@ZܹsDQz{{4myG^޽{,,,`xp,Nr}#|-*8~h,)~,)B{ 8˲XHUQ@R \ϽNp퐴;XRRb7\VP4sx={p&&&X[[{RekX9‡~. (`^"5yK[HM ޞlYRo]",wfXQR0M7oRVQU$Ibvv:TURlRJQF$ןKйW%DIĵE^UEHO3p>޽{4pd2?ܾ}VVH gFضiOxOGm1`6h20l7u9KD~a;C`E yGww7xD"iξdu󬮮Q"IQ"&Y~ɖTg$!h*e1؉N-K(ȲDĮPh4V_0442ΝcyyUU?$͢( ru}6KKKut]gxx zzzP4eY棏>b1 >ۿ7Q%̥ T-; `E4 "ȝ;whҧ^H$&Ȑw BdONmI^9ew v]~-"rN4͏49p?2== ~!VL裏gjj uh}i_N$2>>(T >ˡCw7s_z줺~IS7qvNϋw.".]T*Q(XZZ ܽ{k¯ ˲X^^&If(*i{Al۶?Mkے$RGeJbp5 àEQ󬬬p<ׯ_4M`jjsαNOO{eppA8ViZ\z7n,A=22qf4-,uDZo햝wl1ݴ,27eaayn߾%۶Y]]%ɠF4,z(Q3 C-?nۖ( lfig8weuu`fѕ+W8}4zA8x dgrlۦ^@@u0m/۷l6yhF<0 DQB¡-M ea`&A4j9e DP0 ܼyN0~Ԏ(aBm mINIt?<{2=7E"ׯ_dccAt]woe}}FAoݺƲ,VWWsnb~~qh6K__]]]066Fwww0P4$I-s_CSvjI-s(8冉eY$~; 2vd{a$l6K<G~Y:$$Ezٯ'p\~~ EQ/HR>}bssreYO{8Aw޽{pyTUEQΝ;Ǚ3gexxqJ2q]vj1$zzz%m(P#IXFΙ3g^kE!"l1z.E!B hA6d I!m8CRHjfݥ rYDQO?3gpu<ϣZ駟_s9nݺ\a~9:?#BK&&&XXXR)LA{]~XmU DeNf 0uo|ac|ƎށEI%5\|7oKRJoo/'N>?31 MUIub^wKr-)R w2ԝ˲HY sNL>0 ǙիAx\.3x6˾}r,p_{e||SNVX0$b?{~~O?q tLA%5}v1P~m4]ZUyXdXy[k$) EQT*ܼy7o2uwwDڋ@ӑC!As=&aDVw0&yW677V5=Q^s_옱X MEqO}NpuuP(mYBE~&W/j*'icL-XY_Uw&;_#B\fvv\F"ɓi0r֭`GڊA4M >. JXz Y70`_w; h4sN֘QU#;EUUdYy3LqaЏ)(Ԫ;2a8ΎR^= 4 ,Q( ݕf7H8ե=Si:Jw;c6pd2an"EOOM:&,˔J%6K%fzepmj8{,˾A;bu?8ofKC{4J/; |=p #C, m~-w D蚦f%?h4$Illlv6sVK5 qb,Q<}6oH 5v*:=ϣ*4uG."!LE>%`u9˛f CO0)1yHL.eo.Fծ3&޽{Xr9r$hx"pu1MfeY1J/ 8p!֪ "K\|9贊D"@^߱ X[[g+ 5h ¦a^$Fnw{ 蔝*% ƙ#S:H2bPzx#*w@n6KKKeB(ٳGDizR mted2El… ܻw}144LKOx[&.,HF㍻k :rJ"IM4`gt|"4I])LA-'x zPupKM;xd[|q|rj5Y]'aE!Յ X8h@ n`O+yf:2̚DDAGJH-=Zue2MO\ob;d8rxt1&/%l  Nܻw">5r8qMdjj|>8B!طoH~0ry[[.# k2d20at]~(1[U1mfL.˜={qu=ST*yjPa6W\ٶ~cI O ] 0 & t|dyyyG;z*100Eܫr7_[RY QfڵkLMMQ$N{N zfRTw{=L[P,57BNC#[9sx_h4z }蝫غ4ړ)Уdr&LRyem%_gC~Ѣku2պC ճH uJ{oky駟 VΌ޷a#28qH$70[Z.ƐD"d2Ǟ={( 377(^'EԩS %{rܓr&!>ZHSPi: Q!hBaR*YY^fqqB\NΣR<ťq=s7&-ac=(}{2-Zvm!Eߕh>W/l\|r0.6S$< Sp3x؞H,܈NcӧX󿪪tww399do޼ə3g^{q666`zzL& R)r`ee%z]h4(J8`6( !6"Ҳ,*B ju,p8DC$Ro($`ǣ)3Td%vW"$QuM: u\,qT!BldMX1PѱMplV$3boplnnIww7CCC Μ9\õW($ &͢*J)xj/_'8/w˥R9޽ƶ&:S u]<($II< woR(jjeUM:?T*hUU X"hk}u7(|֓b.9ĵu-/M`]ӛb_ B Mݔ:*#+r0˱D8fhUkӪ5p,%+/Ǔ=lŬ(N%/E;wO\~WE( Li׮] &3mun߾ݻwI$ 2<[)=7cKl,KTl.ΖdQUb6%PdIPí<,FE{I"k(nڵkX _5044===hFzyȑ#;v R?[n?F#镚m9 P-}o۶-KZq۷1M3pXZ$II( 'pW"T*r9Ñ#GH$eYp5~\"Zm[ LNjޱY\\dnn.irGfh4J. vṹWDQdbbl6ŋ}eBQbxxfwbQޖn?MA%/%X,9V4RTNNn3@R}ӳ ICGs QU@;e`4H;ݺ{I.)~ؽ]$Ӥi"Paٳg#w/Z-HbtT*E<GUU333\r/r5W_}ʼn'@HӄB zp]wKRV+hDu2 d2677_^344RX;ܱ1N8W^}VGU, tww#I.]ݻD( aD\vţӎ %)meE!RCdiZբ)Qj!1`7ySP 0/u1MO33s[fT 4XB)-zC""{-ہ0c9#qD>>$")nu$}[˪"M3TXDe lۦX,,hqY]:NEcL&ٷosss|7Ɲ;wXXXT*8a;c===B`hvm<Բ4_\.ׇa5n-LNN266ٳg~hvv7oRTPUx׮_T|29(r֘0 &&&Nq_h?}(D"(hp˛'VEQ@D򰑸65ٳgOV>}]70{ۥ1ܟ³w}Du=\IBJТ)pV ha EUMeZk tW#$?D򰎬A:Fpxl$۶) OC7|0 q)ϝ;w{ /1qf裏ؿ?ܹs@9.w^>R?Ӷ%LٳˡCi4M9>X~'xGWWT~aG4 666BUՠu(~)TeEQ'Lo>Vpc+"\}Vw02/@4fgg%N.ZRDXZ>X9{,x<(MOO?)iy'3qMϋt:M"Zyu"'+(EX@oWwۮv b$V!O}m)8vA$%(5DA4dYV+Xq^KSYQ;mrGR@q ^駟r Nxxs WWWr 'OСC`;VT*44MVWWY]]}{l4A3 `:B:uIOZ7H{5t+(-z46K؍s6Gn r&C<\_!#Mý~BvCNZ$tNcZ l ;mr̥Kq7npE>\ejj9hZ`ϳĈƍ ޽{矟[8d 9vj#4?SVE|^i4u¸ѶlaT2hpUd]b J4{eñL*-*57p(֣$l7zLX]]err?? vVS<`z=s->|UUσ]޽{4D"AOOr9;wHYl255E.c||7o>5[!@&g>cָtRP=~8\vUgdm,C&pAUUt]hyB^~ۿA<'UILden-< 2M3+HR 2448cccyn߾Yv2r…'Np8L.CUUjФ<B>|RğeZV<4xi42Ǐguut:CwO7(2֛b4@tgCHhDj϶o$Z4bgD$eHou&({PSUzHCמ*$ 5u,ˁ& D"Au=̙3E"b;Y\\KKKضAvV%:77fggQU>O>ZH"ɲW^1O8x DV }}}pڵaAx.www駟ҥKAifq~7nCN<'3{ynn.(qp!&&&XXXޅBT*Eww7w>_Г*}pӧOS.d2LNN_sܹg QpzG]:>|Bи#ys308D6fKd Ϭ3`H $,x4M\ǥ^*z< ߢDTI\NO$>fccˈDI74k <̆z ۶;d2d2RH$dZE٤T*QשVTՠ3S$I&''T*ܹsδD"G :qX__g}} =660~$188c eY&wԩS|J6x$I|gAHUU駟k\(iR)%J54rQ*{Ud[^0|\@KZ,uudAТQDI^,6f8sO'pmm!)jj"@m#븴SGlEv Dꢷ7P _mFA([VoT7Y9z(hϿM˲8wtJ•+Wa``IH$ȠP{V=<<w &Ug߾}8e*B4iZ 0H$ e9H&)J_ĥO C2B4B,vl RȺ H†Oju %S c5[.# ˤaȪNxt?ZjͅX*դ[(P裏D"A產([OOOԱ,jʽ{Wygyy7i]T8Koo/DGҙ= ./_&N_i$^[[_EX|ƞNʕ+( {uV%"^nd`WjM<ˣJdi 5Em#O(B P pNUSsB4":Lzl>nc ױ!A@n։8A z;Ff1pŠ~&iV!V9::7e}A\&0<>8;]aq)\r婝\.===Q)˩i>#J(BXaGˠj"zp%d' Nk7xxȚ_uA;$6MFVeUn޼]eYȑ#=zVŏ?Wy011ׯ_eYa<Ⱦ}hZ۷_d'cjfn4,lRg}}bHV{yB/{PExLoo/W\AeD"M .NP =Algy}EPuE D+lT &j{הϱF<?6jkNE7;g62h?"(q<ˁ0@u9B.*I;vH4,@XH.)ԊÆY._LVHDQIJ,666}6w}v\O>$HBW6k4AIJ]d3L3nnnR((__ST?30nݢ^4?~:p, (QP1d*T c*-D42Z]UKAln>2)d F&LjK`srgΜ6 Cecc#>YӰ5xk?& ,zUUI&R۷oS# |8~Usc7}<ӧOsG0UUd20<<̉'ػw/sssܾ}|>eYȲ8p۶9k F]Ndr zTT_(d4x?رcz>p@гƾ}VLMMr*L266S5ʝpa~)esƍfǡRp=i4@Q smhǡ^3f-sUr Udɗ#[Hxv[8&R+/tdU(BҗNͮrB<D˯hMf/"UPCDؓ ;)̫ b8rv#s癞ۏ ]m(dp|>Ͼ}4ׯoˀ"N=x<_ڊNdY~iZƵk׸yf࿝d"F~Ll\__f:yϺojA};綪A]X,}ײԊ\!;O $qQ2 W\y, "iسg!MJkÃ{ $r!PDcX7f8H r4 a}}=8`ϵ099*/^|}i|^M(bݫV=Yӳy$9뺁1vrE";7n`zzG{ɤ_xY0sqUk8ܾ}u/9bsCI(DA [l+V'I.^VJ0;;GHѰ k%+@w7Eí[+ԩSsi:{exxsqڵ M+iΟ?ii,]g\Š<9o Bi_(:(j6\~EQ$ǏtݣT*=I&FtejjQ*p\I}y. {χ~ӧ2Mp/#fx8Ӏĺn:ȑ#4M\lzcVx޽O?h 矓f|w  !K-za Hjz\YI淿-7nڵkA9aȲLOOO0 EQm;05w]7366~ &&&8~8xΜ9RPgR}ww7׮]od.T*(8ֳWv qiQΝ;v:4 R 122 EQ}L( hP(DTҥKukm .pG[ 000LU\[[˻I+da<0fǟ!mhj5էe!2ݸc6vXo r!Ȃ$ɽ{Yw98> ,BO-|_'}y7?}ۿmMs7YXDDF{S(ڒ8fjMHv<<4GM67KD.C5I $aIc&^1vhY-\A]GU$bpd"\'0z2IÑX.lKӆ٤ZP((llP*YzZtv` B]@5TU;$YQ$MJFRõZ8v nc#xN@dCwIV7jKc$B(mV+,l6Z [rVqii5*#I8|0ǎX̡rkEħ$W z]NrblmDOE1eS`&а}Qp=߸u=t% !y : d"2IТQ,`wUY Ȫy˫%6mMRK戴hYQSרUj5:f4ùV6[Ň#;VHMӂ0cTUCS$YnPeU0]t&a%x$!Ud=O7)]((0Ij?tGAUDz3wIQf: FrFKb!j*l Ӧ^oP,Y_[#SuQFF+|UwtLBhvTănNr>j{x?z-.Fǻ\t7"GeUn | -f~!+].1PJD(.*`*PLt"!$Z+P%jr04cZ.x`bwnn?GBQE)aEm'$3dCWe U,.l8VC1N% .fĪ7<,q{ƿyb$B*G3ЍjrsWJI.u`LFZ6PAdHRH`Ȣԕv{Zec$2 /tҚpޮ'lFztvn/spxX+ n t=\4][@W& UȐV_JX@N7Ň% $ĖeuT*IR0ػsBQjN1e\nޤՖGⰔRC.d1> < 9*CeIx!]R|됺sjݦ6(("zJt@ِ$'iLЕx J0(() y 7~h>c8FL#i$C@RdY3lmS/i(IPV,6zLĈ'D<F{2vD~1wlzauB$IH$%Jw7VY۬# pdja^n6oE\{6nN|#-֞ sR q'$ TԟT/N)OKL?Uy륚Ȑ%,U/HFCNN2ޟf?M"SoZ145\_Ȅ<[vTVQ؛nOU1tYeg q5㲮Vyw[j:x>mV@rEBQU$YD3 D\MkȲk(е9i H(ئ?[m(U\/'OO5[\|`wQQuK%^<T $E7]d6%QUFF,AF{.=m#i? {gFz |.^AU$ M:uu#~;c+)Hg$7<(Q]m8~˒s$UERAueAl?p=Aܲ({2[[DY8E`ͅ$) g۔kM7]ABvs_#~wiLͮ/ysl|:x*u;N(gS_8:n KSOX)΃9n ) X$DQj'4Ã,xXKaZ1BZ"1ƦKDI" IzcjI
mS{yp=#C)/ ]uY\ldEIf2Ȫmz~x[Drsre[ ;KrIԛc%ڰx*qJ|Ƈ7f=ʫvcوŹ5ܚFV.ٛyԋ|OH8 Y_S_Ip[껮xOA|.P3m!Ο ]Y޿6ݢG\G cU^u$j"Q <L.2G2BNgx ~um|C72, pxG8ՔcS/[mKeE!Cqvj, @P'Ȋu=ߦUї.%'D{$11c~!yzaY ϗwyqPuµܟ]n yi_w_f$ "rq0%|/7*@*YQ.xC7B7ޛvі%L``CYm_1u$'GJ"uBl./KcTY©TcsFAFysP]Q]s;Lw]ݛk g@FD2 C#5Us=Q]F'$HȵUwnz$By2"2CIDATZ!9uY,Lqs,ZҼqz*>FHsl:TJL8!h3%M#r©4,w{DzHԕRm_Ark 'G6}e)Wҽͮa;.r>Gò9rdŒjE2}&tjvXl~jܵ/K0L}hz4*LO/eA>qTh'ZFuC'5]ouW$,J$4\dywi:Ns9FlPMT~ LjPɶjO\WHQT@*(=2TA` |Xh\$`73͗5rNTYc$"F$r%!A ^ܵNÙ8(keꦍ,I!$Xn@`Vh3̬Ux8H|a z[ #y o__Pj'6chx-.t<>;G#EMGO8AUmJ}sJ.a*(/{/# 2Q!}vI m ? $LVi,skz,:of%tetI>džGw)TL.O %FM (h]әRRR x}}Yi#Ziy %8=6oNMGcʜ-K,j]6!(ld](\ӤiDK I}urAD H4JYoTJS[!Ȳ fsYqV!mXcv7kl+u?敊+Em6st(w/ WS4L{Ò$J&&b!pP @A#>dERIp]G JI\,Z Op&J\[L-[ ӑ4}CDNs9ܚUfVBXo'Mg %ihȡK?4Ï~}ZltC$eQ<|xH_v#\9i)I^(N }Rt=8jMgQCVT"}}Σf|bmMı30)JqkJsh? k%>3,IL.6r! wԵ,liFU !BB`ՒY)P k2qIz.Ux԰eTYŮ(4JEV5>2b* ܞZ zQASū繭\$,M"ŚLz..aނ߭Dx"w=D,n2~6l3e6bjyzZzI`h*.  z;K*YQ&d>xeOF7$$AjR48GS ]sj,D=J@d^&$}LNqmre 5͒ ٽOu g2B2PtGط^8tZ6OŹ6/.?)^={w.?`VTi(Tlk&+mbP2mȚTbvׁ0^IE  mgURf\KbIԒ0pyK:ް!*RB2?q(@:#Rаl$0m4Ua|x=%?+JgkI0"B@ hr.j66.OpDZjDWFz(*G|! d5M/M+j6,q|0_pU71J^$:^ j 6Ba |C Qc:R!zu"m++JzPࢢi݅⁩]Zbr1j=X$ÃP&’$1Te[jhf[$U~/*ܟϒ/7a;($P8H0-szov+1m7xp7q~5p@, 9qh,zU~W'D㍳B2mX&w [PplC#4dME7B<\̳-7:9?N L6G6zvM#';e_t7fV`2&PnP2DdcRJ"svaCԑ ]e1[fXE$L!3ڟо*VܚZq<*eH;X8$4!XݖN q],Jbn( 99a(@\8u(C*fZnvo._\a>_$ƇyH+lt|Mh}!r,nSH5 lh$'yFSGGMgnAm#hD6&ΓLEfRT3ǧFG ƇSdj# spNf2>W#o%_aPABq=NE yֹ 4R ^`\2/ ISO'Dik7%n/޵YLCBB`Y~Ӽzz3Mjj6(2c# &WMTYձ4?xqL%iTC!< Mf1m^#cMHs_99±AHq{SȠf#efm9^6%yEClL:-Z=Bυ)$96&50ދ̯?}Lۡ/P_beܝiKb;G4w]frkrɲ da\ϧP+-ױlETyÏ%eYUW|GY&4sz7 r?N$mAG@a%7BT V|q:ݫoP_t:4X,D_HI`L+ 3E\' k'JStM|ءĶ!I|kj)&{O2X*;XQd>1ͽ5, d¨R3[-r.Kj2:@S4)sU03e޼>̡d;LN9AC:lMJmXx|ǡǷyI&V8>;8ʹl@$A4, 2"Pg,|  S6XΕ}\Α񰱭RB7V6XS 9QR7iTn;.Ūٱ"a#q5QJ: '>d.HB|ӣi1y@wevIQzĸ"]u}4E3 EUqu<`\E޹2hrѾѾ(E#[ 48 50K&C?jJyiZܚ\H Zb;{4п l03J%5!Xdg$Y/afHaѰJ*DD}RD8ٱ}>mӺ~P6k¤/bR$μ,K\|̇7fZk^KuMllqaK2]bv(&aD u b2afK,CI.|14cv. -$ V":/q0F2d¼ʹ]_'4z`j-y4 foƐ!L)2xHD YnT]%޽2Cf$ i'IAiEY&2jŢ\dȪR a.#I2)* [Y޻ĕU)|S| dUjt)B!|J5N#7B̕ҍuiyjAq,w9r{z+\@lͿqPwFA,L * baXX.N8ڟ@UeʸGBJ$dhښ7\[|pck]c#iNu]{}>$!~c6!4 Ѱ.$HFũ3e>1# |0'Ro`E&YS L:wfڽ\x0Ɲ><Hw^N(&`>$?Dۜ`Dk@o8`'cv.OI^ 97׹=B.灟ׁCavq>|b ! ҟq3 q<%ͮ1X`f5>3|nCy-INʎ6$0> .I0N~$`sxvPPsfS]L>ɝ2 dEQY)T9Ѷv)TtwnW$j||Z0z|륱.Zf;HGvn) ""r,QOYy q=02fJAe7?}̊@b:N ky>,zeY~[g99Hy%ܩB:=[9rzq5 YBHڽH;P3]C]D3tL%޺8͝"{Egۍk;U$}Ϗ֏5ffJBHWID炖8q(C4i \m"$zf3Gֽk>D(C6+u>b9$YSṳP_k6(,>@t3#IJFh4BJܾ/`".oc?={ɏݒm1(ح]ǁufrv͙M! Fm;ܝ]͋||crM?k ]3%rqxH"Ѱr`rHh"yjMHp|$)T6 $ >? $jB7>0>әB6dV\ζ*lh|1$лd\?Z=.̪[b(m-cqd8/+-ʱ֩2at] H*j8<ڪX?S?〢EBawPG{ڦyZ(Z_\/ﮰRS[?A$یmձ#M?AׇQe ^jrqIB;HHgl(ũ>642q^?;7^0:" '(Y ܱa^}a;WN2 #{6뀄jH ¼I%ى{{gm #eR|>9;ZN\xIf ڱ 8 v,neVKL2j<Űq/Ω#}ŀ|q0GRs7$J]$әIGvqwz{skPe^ZΕa88}d@OPnB8 ΕFZ@/Y ,d{!ȽNC,"!eQ71㧟Ormbe; wFQm}*v) ZF$hՆJH,uU!di4&wgW\aPes1Brجd [=ޑT<ՇmbhN) bdMHYl76r6u/,"]UCGbzۗl"GngD"rh[rV.Wπ~`lnϩlq'$$na yf-pGpTH()7ޞk#&@@oMBn&Sm z0wrfjk"1n `WX*Z.B OfVHd@nNe-6J`#jKyrZ[g$p:,Υs.sqlKk# p~I?^ཷv+ʹQEA `T,o_|n+Wmk66;vc3?J-VE]}L%t(E2KgLtkwxEIj6 ys_Yt<||N|h8)bWoҤ7n6¼"{8ĭѦnq|u&si_]"_? {rx"pD^B$C<b9-JDaq}b ' ^.]4p}[y>+LO=r Y3,f>C)~QaҾ̯ljB-ey鍢 wYmF"ǁN &a$XdEAY-T%~yklm!x?CPH6k:#ՏQs'[lN[W(T JX]5;3mpI&S9nNqcEa&x~P)qݗK`7-/[Γ6YGoܮ{d`ouEwxI/Fl3>mƾ4\;vNb=ϥlQʐ&Ω(oa*uNM:FN"3Cq*4banďɻ8JN{zf, q=ƛOٝyͶ~2y*1CsdȌ}U GvJ)*}IWdÆKc| g`[Hs/|s[Gg'; jV.1@߼M|ϯуUJ[+CS@#†Pttg|FYzH4ء~:rZ,|r~,>?N2m0?/+ޔigܳ-l>yX``JFY8V;mަ,k3sqlu/NL'n30:m@Y;P`jg[؍xOrd(E*H_t GS|>uuVڎ@~so\]9ຬM>{XX@V8?fl1gl٦sMLey$7&W1mw_d~_ӽlF$R"5؍:1:ǩ`>C" GڣAzK$UüvrْrJRn8Si1f;;T!p[4[7pDZc~S=-&[,/NwI 0Y޺4ͭ"-x˻,:e~K~So"[9ma]ᕲ(H#Fleڜ[@^ u|tZ7NQ68[Tipn(@Bmj6-.y$\T3rmړoMfKl1-sgm9(?ʂYRoHR"R7m>(1`827)[E4q# mf9Kk%~ug_\d1Wi2%xO}7c 0=f^ĵ,1iz@ { ֖s ^).eCɱ2[%N鋡xNNU}iz7oIfeLˎ[ET``SفǓ-$+[z ߱iǒh~VaPn|>Up IQ4+SoX|s,BGQ|xo]fz}26lqq>޽l1WfvESSw>&RjtM״Mƒt}gBq}(2j e\0 AYuЍ o_bko{S#ۯx*7'e5iEk>,s?΅#LOI]>LL0%GcidUw]f $˄Sixf$vGYŘ,zkS9das!]}Zz.:|瓇kL,p]o;.uCD_mӶm9;Wf4j~ŗ2\5[Xm[ԆI|HVg[dK 8p e"jspJHץް:!W,Ұdg33$ۯR'0<DzŦ]%]Ǝqន]'[\WL>,k 0Ʉ?$`'bUUt 9޽(T4 p}0>‰aȁqF{.VdxRcfJ.W&,ܢ?|u#h "f}$5__*'cq3rŃ}t$#u`܌(Gy X|1 jH (ݨlkJ(L8:[+&TCJ P#-\!JF@TL%* k/ vڿ[p;zlqi-m$;$ ,]J:,&VKGSMT>n_.TJ9,W9Wue1_vMZH&΅Ì P=2%D?7 G8"HpyR0Z/3IDP$HxNȓjx$} A6x$eN 3[٢$LgglI %0s6@z(hALͭDe̽_*~A OB"HchH IrHDH W|p{w2e m%W1֕\ a^9>ĩ> ]vYDjZ.'(h oeJ#W` @<-6PH:#G@XY>}tsd~O{$DVo!ǿvs=5^9>D2X"?DTO\nYـdX-)?\n@)Y~qOû ]P_-8lŃ ![٢Ϟ6e~| q8H0lqq {F X[D C9 y8HHdM?B̂Y~bs : h ?@A?gn/$?Y%tEXtdate:create2018-06-06T08:50:26-05:00QE%tEXtdate:modify2017-11-21T00:25:55-06:00IENDB`forcats/man/figures/lifecycle-archived.svg0000644000176200001440000000170714241066126020404 0ustar liggesusers lifecyclelifecyclearchivedarchived forcats/man/figures/lifecycle-questioning.svg0000644000176200001440000000171414241066126021162 0ustar liggesuserslifecyclelifecyclequestioningquestioning forcats/man/figures/README-unordered-plot-1.png0000644000176200001440000006652614360013050020703 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_@IDATxxU7$FR(*kgAWa]׆"RemOeaU,JYq)*",EI )߳܄Lryig|眙) @@ RAa7  PN@@  Pnv  @9  P@@@9@@*T BJHMMӧOޙPZ5ѻo39622dSYl~UV c,j]gggw!W:###dэ1*U933|~ayVZ111 * W^T@uC5!!iiiSJثAko[9**JK-I˫M/bK5rYunGGGY?m"m[=Zj/9FU@|ʯ@JԪꦰ  @ 0R`_v>}7F4$B@W4|떒! !)@A! +@uK@@ j@@ ߺd  @H dpP  @ Y/SNɑ#GdժUAnj  @ 3ˉ'd޽2sbb6  @IVriyd߾}rA;v,Ǐ\:ɓUR,G@VGqN2EeذabgϞ-iii2d9rdgg͛ĉ%""@uyJJh٬Y3yer=ȥ^*͓YfɢEL>ÇQFIVL;v쐵kСԩS7ƛ*U%y"`@\\\H522RB߿X_ mg'ވ-{8'+뮻N|I_d0a|ҢE  رceӦMryh|F䥗^2A?,֭nݺɚ5kL_KjDͤ$:f˟x _SN |I @( PKxLn%$$zz(iodeںuk?m oM6W_}Xi0I?b͛7c=foz?X,< :t_}ղ~zݻ_7t 8Й4}nqՍc&O\?J[:BѶ$mU:tHĖd[=kLR^=9zdffz Kae5˖-3^;J۶m}Ӂmj ?Tvvkb iӦo^t`SwN׊39o@@0v0J~zj+L[lbgjcCb]_t~׀S[Ku;M]viӦ .Nnݚ/-6c  a,`m veX˪U 5VoQj֬iനs++WʠALg=fU}q:To,*O! .m½ŕO/2ҀsVIOO7_&-bbbL0INNv6M:to߾e @>ӧ目 2ϸՆ⒕-ڭ>|TN:)gƉʜ  V2biyA@@ @֭+_kd  PWO@@7@%o@@BH    PHʋ )X8CoCbM>222)sbb[֖z7-Io;p-E A%R@@K^-@àRe }ଈ  :1;@@ =  @@P # .@:1;@@ =  @@PoڴIo^hcǎɊ+|333}y  _4GO?TBhrJ3ݺu?:@@g%77L>}ZRSS}ɓ'%++7&{9?lw-@޵kdgg;xE@Q?Wѣ{K/T͛'f͒EIDD >\Fe\jhKhNNoaQm|6)))]ne'ݻw^|&-Zd_[7Z%&M2s222d˖-u9ߘQm)͔f&oP MC)Js7222pǫպU(m^[?::Zbcceo|||Ng%٭=lp1/m-5k.P"YYfѺ^ަMi߾FhꇽDn.Ӏŭ"{qVЀVU7SE]q/d֭Ҷm۠K/IӫW/gmڽ{ʻk :SVD@Hԯ2^B$ $7o.ڵ3~|_JرcM3A̸aÆOB@l>deΠ:>Gn8z%Kw{b:uo߾gP’7>}z+U111fo6uq%-I˫]vQ 9l9p@5_"PkyB腮 ޶z.zѣG}|*UXv9[i-N 3x dS@@ l@æ*)  PoG hT%A@!@z(@@   7@QO%  6<3lt?ypbm@@h=sCr@@(h)X@@?sCO0`OwEt=N"ξ@pSP7u@@h!f  )@.y#  -D @@7@%o@@BH    PH 3@@.ݼyl߾MSF@ `ݓ/_.5jԐ-Z`a  [n=ydffl%++K%--MrrrdݢJr1~JJY?ӧeϞ=f]]ө ǃ>_&A@,t ɓA2x`SU&L/\>ly饗D̄ٱc<Ҿ}{_gyFT"ƍ;S.B|j裲~y/8VX!}tMddن]jt$""B^TWVZ(׿DgΜ)1112w\yd̘1RNٶm4lP=*ׯ7_H=@ǡ+? f2z|zr挰NY8y}]Zg6:ez=ta֯_Z7 6[4niӦO]еkW\[P.##C%޽{ː!Cdȑҽ{wYf{&\zi!T8t?NJNN6i7^5p&,3D[^ԁ7666luD[Klg$ʬ &tx-ɶz7tC/ַǀjO:+^hT\Сhs=өꇸou5=:T$VN @׮]+^z]v|&q8y $T[x4Ӥ-Lj֬ϴh|teW_}xbiݺ @[liƌjڮ];3 p5@%! t^kZ2o6L^pKXmqz!xյO>BڵkKE/vҤyt\8/3gϞa@@g:zDLz5 wcڒ]n& ."ox@P=lI6=p-li@g C = J^z1+ƀS[@?iѢ5cǎM6I֭g5נpŹ߹sg뮻LyM7W\oyql2yZj… eMk?~oZh j֬"XOڶdc=X5jvjz.BEڠA[)3ߔygwռ.3>48ܺu$$$^Zu |дr 7[N8!w&]yS+gv{' :%%&&/99ٖ"Z?SSS)ϣXSf-hRRVZrСRyĶz^+M=zdffz+ aرmWhpqc5I_I ǃ:]S߿ѼuI%5Y; xU CݻielժiFfϞ-~wիMiԹpHF0df͚[UD"#MNW^6Ǣ2O] &-+o4F#--O [5?O2o<|#rh1c|7za/<[l)Iǭ 4Hnfiܸiu1$@@f߉ݣ:zk&@ 8ѣGn)Ӯ~J| 8}QzLS7VQn&ڷo_7w!ӧOw5fҊys}ԿSƀzJsԶ d hinc@urq)Z@?oM|-#s;?Œ/   @ mZ H  #v!U {A@(hY@@Lebc#@@ U@@$@Z&66B@(WU͟?D=^$@<"@ G*D@E4\jr   #Uއ9`ΒBP`ѢE!xT ~P~@@@+!  ~P~@@@+!  ~P~@@@+!  ~P~@@@+|߾}v #B@BOd˖-p #B@BO urIʒQMsNIOO/ѣGȑ#3@QGq֧L"{m۶UW]%ݻwzJ5k&[nx@cr~M{\\O:uQY ^#$K6ONY3ƔcVzm*SV(~ᇒ'<?^ڷo/کu]'_|lܸQ̙#cۂ ̶΂SJϞ=I^8#UJ(/n\zu/lc֭{F7XUY{%@:m| =~ 2/^,K,1[͛E|jPA.]s?,_ 6d?g7=C9o"ʇXVJVdddkYttHJJJ(f(wZFEE~v8qBrss+H*v׶ճ|&&&|OXrޛӚ[\ɔ0?66ַ~0WUSM v;YLk~yr)gҕxW%TnOTj Ԧ2ky5zVfI\i-ɶzֿg @uh 2WVC@Wrۻ[|I={4hԮ]p 2doM6-  m2?o /P͛'ǎ5MMǹ@EZ@CV8&@@ @ør)  tbT1 0սL>@+@ w#G@<)@j@@ z8r@@6@w#G@<)@j@@ z8r@@6@͛esf@33-[._\3 " %8KlȐjժɁQFR7!:w^[977WRRR$!!A/ @B(j^0Af dǎ+H&MdڴiRF K䥗^ 05u~ii߾/w]3Ϙuܸq+V~7vMdd˳VZ+{WهRa/9)M-uժUMH=TZz~Tf=5%&&W[~VNj,QTC Oxyp2c iSйpB4iԱ4mTf͚%-yyy2qDѣGZP5gN])u?e.uy^ ~6 @U˦rX^^Z%ƍSWڵkoSg6l-;wS%YأG'%''˱cǜIW^ԩJ]}>&&Ƭ)lIRVnkyu(Ljj D[Klg$ʬ ø ƶzA_Ifff9kVlvzJ\HeN2-Nv:t #F{t@@@e7k~Ү] ef͚I~Lˌvϓ@@'4nW.?|[_E3F &ݻw͛_)@@ *Z:LZ9pޚDϞ=E4pZx  tRlƪ  ~~E+ޢ1@@ %lo(_,F@(A # hz  @ %@@|@ד@@J>%g k( -[7  aY @]Cn\=?#ӧ{P9N@ lh   7@QO%  6aS@7ꉣD@F4l   F=q  @MUR@@A(G  A\p,Z( ! @PSdǎVӧO˞={$;;W3'O,}9rķn&999{nIOO-7N޺ĉ[~qٵk/   (XyeѲo>iѢ<'թSGZnL믿.V5kÇeҤIҬY32e Jm&W]u4mTf̘amٲEz)FN^z%'$$駟ʕ+^0h`z3gԨQC&O,}4j_s9gsNYnoK.ڵkxF䉀'?˼XUz9JkPiQQwƠVhR%6pVNYiPnSOIJJ<o+s-4?Tfh|r9s{'cƌ1~~'W_ eРA2rHw1AY̐.H}YyפUVf?Ǐ7;vL-[&TVc…2|pڵkY_gN:U7o[pW@C r.l,6lؖlxW0JA߭쟩^(߼ytz\{!C˶mۊFMK[yWXaT 8ڽ~&7 6`t?ljWVM>ٟλ$ͤ-$on;?*ўRRR|[ժUK:dRnY[=ի'GLOWqqqŖ!T?t,ٳE5k׮\~往ؽTu.s0ţ8NוCnݺIN[n:@7@(_[Q5 X5xծw89&gW@@ `qF97k믿޴2e]&_}ŋ͘Ղ aZ.]N-ť:K6l(K.5-XRܫW/ٺu}ҦMٻwd>  A7AuV=modf̤32˹O>2x`sOe„ U5hVP]I&f,?Ph]:FT/qMM:o6]9vXMy X'S )v18Gm=^dɒ|CqBƵ]KJZ^ uBII*kt}POW_Р 4  A[8Jfi0-?Eǂ^yhtƍg"hK^E뭷-+"  (zCnA:v(zU>H+׹OmxNySo?$@@A#jSU/6, "  @n*oJ_    @6KE]xT0ke˖g3  @!m&˒R? g9  @  @K-hQB ̟?]v#ȧ~Zy%`  * g{f8X@@ L @y|>B@*A TK ի'jՒ뮻Nڷoo+%  Y/Brl2ٺuΓ_מ-<  PA-?ȑ#GLw)r˒]G@@<)t:bٽ{tT6+ȟgYh' A# T@PhVV,YDf͚%-[4GW|cٳ+yK.r5vzjժ3B@,*{~>}ZWĉrB67}Ԩi޽2sL7wA  @ VZUz-cǎ 71\s3:U[* & {ћ;Icǎ5333eΝ:+  &}s9GjԨ!-a.Dҧ!C4ij׮]r<`qs DoEu&/7%%Ŵ/\Pm&O=4k1@ӧODڲrrrD[I/zw~*_b3b{̡QnmiӹՆz:~E7|#: /??Hrrm7Kow.7xwgzEǻxX @5Ԕ*{$!!Af̘!Ǐ7HՖS @EueSN={:V@+qp)GiDW^b]2׭[7,zNJJ* QHc\tn_QC~%\bI&-zQR[o9seӦM˝-z!W_-_~߸q,^7ov6کS'y|6lXT9K182gfʴ7Ϩjժ/Y9::ܗY{alJz~.@   ?tPӺy!iܸOnq3@@|A5k֘'魈7Y衇䣏>rf  @@I &}ƹ32CW@Mh}&} @*W P}^`4bYv9bm={r5Tn);  gjsoرy_.hJ}F=S`@@r@4h W6D[E/bSE`e0`@Y6 zӧ.+" ]A'N0Bٳ @@5T3nٲtMm@@{ϴ|ҨQ#5j$@@J#tڣG_j/>K/:믿n-}J̺  @bcc͕Ǐ}7|#G ʣ>jw@@ @{y'EҵkWٽ{̙3G;&+V w4  @W[/_.[wrgc>6m   @/ySNM |ӼA@@@]ӟ䢋.2]O?_Knn/ƍKڵ}fr)9r䈹8  )tWkw)>}>^2_BBرCt@ 3C711FJ͞=C #GXZ;jw_JJ)7{̅Y͓YfɢEL9tĿUV&+{3|7Å7.?ˤ3*y)F̖TjUs΅Z]_G?OlIιmS=kmS֤Ėd[=;Zzu_S\T ~_T> (jGڧ]$kyQɓ'VT^o[oi=xz ޣ 4so.O<[o5^Q̻dٲeΤ,YDtާ~Z}+ny}*Դi#rnL8jժ899N5V?G@lgBٶf+@@ʴr4Ǧroرc=^ 0kԨkj~NjРԭ[Lji„ U?3mܸQB 5i< ~ҴiS3_lذ/ecc?r_XM34Hի|F<NpWͷ3z)$ Rn:u긙2111fnDP 7A%B?6ճkMeZj7fK1J_i/;rs6P * ZٱcGjw嗛qV..]E "TKTo۶|c۷kK?Ca Kx㴂je_{ۿo7E[{e6mژB8p_E@ ^Yr&.s HQ/@r wYg@qҥfl |]כoٲŴj-z~i;:T^-â-E%m/L Jq/j{;ҀS[k4iKvX .UձY_  eAW,e^أMpHhLi7(5k4Npxw˓O>iR|РA枣/oZ/j``ҖYH8kW@/N_y啢Xh0ܣGіYM7"iwKYTB@,iоi}n]>Srj@[Tҋ4P+j ]vڵk妛n*j7f;wη<71̞i[oӤCt8inf_mZ\/@=~n:T{lJ tƀK z8I{y=c@᭸T58߹IO;(6RnN~ڭ?|T-h8  @pzƍ3W}?G">C,Z:-:. -@@LBPӋoB_frl  P&/Sl  @ L" +@/#  -$  rE mRRRʲ)  g$@ 1  @i@K+  g$@ywz(NW K  P*Z@K  g*@zl  P*Rq2   # JT\  pg*   -+#  ŋԩS۷O֮][h>3@@@]_]N8Q(-[… g  `h}iٳg2//Ϸ,;;7Gʑ#G f@RGqP+W^xA5k&r3gԨQCsժURfM9|L4ɬ?oDԩXv%ׯͻ⋥vھi7DDDmyd2ٹsiSS׮]Zj͛ʹw2d9rYtgiҥK(m&M`+p =f222JgOLL4ɞ-CiiLMM-]_Q%$$ $ǎ+ÖD[jժ%2Ym8YzBY-.18F:S :SSݺu}uZ[KGO'it83=# "K.%*ruYҰaCYtEF \X(IDAT,.L+7pR֭:S+u>@@o _Bi^H4k,ӧY[[-uN<\4Tzu0aB4L׼6E7m4r&@@@ԺvرCz-ӲyA>}ˡCR,'}[\TƷ*Uo@@?P?o֭R]/DO;[ 8O";΅K4  %qDx߽{=%lb@@ptl۶)aU# !UA   @ %9! !@  hY  @\R82|III ǢQ&@qZ@C8<@@ @íF)  txux p+k> %@ hQ*C@pM5Z2F@(J(! &@-# %@Z @@\ u@@ -Jy   vڵo߾Rl!yfپ}{ae@@ @KQ ,o[,_\Rm  @8 Q{Bk:uJv!G-;ydeeɏ?(پ&77W;Vh~@@\GqG< ,ժUh"1c4kLl"={QFɔ)SdϞ=m6ꪫ$22l3<#UTq UV'zΤg_C z\nTq#PӦzv2;jߴMSV4go!ݻwORRRy~z^^xW_}U4h Aȑ#Mn999yĉ%::ZFmPg͓;2uTfxM=zw6Eï"K(lc֭[™~m礤$WJteS[55%&&\`7 $WX!s5iyim۶Ztt6mZSWرth~L`:k֬{d  {\0-ڂ֢E vƍW^TD4ibƂMcƌaÆq͛7/v]  ,@v+_Tnkmw^ɮl*wf-[:WI5ŋ;" V Qz^AB@(/Mzێ@@(h@@ec;@@2 @@*@ZV9C@(h@@܆rn=;U# 6MUR@@ި'@æ*KWnF@>}zJ@KԮ  @ Vzp  ]v7E@*]ҫ@@ )-  P^  `]Mi@@J6ݴi|^  `'|"_}mMy@@J6u;&s&%;;[%%%E?iٳgY3u:55շɓm54ɑݻw|}+@Tg/_\-Z$Rvm4il۶M^}U|FEEɜ9s7ސUVI͚5f86lپJ*2biѢ<裲~y^zIrss%!!Av!O?ow\Rf̘᛾u־i7hyI  `@RRF@TYfÇ>@Zj%;ws皠TO TgΜ)111f{'cƌ:uꘀaÆrQY~/GwޑM}i! (`uڽ{wVNM]t-[ARn]3ҵkW|޽{ː!CdȑۯYF=\p^ڴjh_ >}ƍeÆ ֗z)dan& I  `qFGGĵz |:Io6jL:M =%j+k׮K/_]vɅ^h6Q)  ? Yȡ-zЩSDGH.3W;/^lhjڲeK9x @۵kg~mС P}dfLhe„ fԩH/JKXТ|2 X,S;]2:j+B]n& .@BT`UVTUUf~yAժUEH  g&`3ck@@Em@@,@Zf:6D@(hY@@ecC@@Em@@,`}@,҆uPiJJKlcbbA\mI(sʖG馦Rds㄄9p5eւ&%%: SСCcCMmg}bzCm^N9[\h-N  J   X$`}\-bE<9 &@ kd  PhQ*C@pM5Z2F@(J(! &@-# %@Z @@\ u@@di& ȑ#jժB  : @?ꫯ )ݻWfΜYh>3@@BGw1ٷo3ɓ雗.YYYi]sN%% l5SNLIIl}=zԴ꺚rssE?<.eG@l8/_.-H]L4)_}M (uYBB>|G߿ 2D7o.}hsΕJbb|KN;^zɬyw}2zh9uRv%|y." DDDk,cccϨ䩪U#WrQ}*UHՅ]xrTfܶzvny)&&4n ~Vhs{Wں8k,SAÇ>@~_W|g̘!Ǐ뮻N bkҳgO`ZJ >?{/xy뭷~fϞ-K,nݺUW]%K.{״j|k֬1ԩS}3ϋ5ka1XOڶdc=WVͶj5jXWfYc '7rx6FإKٲeKPvoܸQ/^lDIMM͛7 4@tM7yoʹ ;v4__| Z%[o9sE/jҤYMۑ#G=cy:ׁo~<pߴovJ{mR5^Fve>^і2 L/ؔ4>~5E VZ7.''ǚrV{Yg{^~><j%9I+QFΤTo:I)jzÆ M:]'Np6+OZ~<Ӧ_ʶmnkڴԩSt2e_~F u:##C[y1aqeu~?󴩞z̎Dx7mӹՆz%_|aj`tA[t|&mڶmy$mڴ1Z^=8q n3SwjШ}Im(K[<;_Ǜwy_ TOs@@<j 4HZj%-ZWGn6;v\pw} 1bɣn~mnvY` .[+ jf9眓_צk`=  M?5f_iU:}c>ճhi0Y~}뮻sEe'zk& ^b&c+% ZE3:4}t7/uE):dPCcoΖzPw~͝u=tPX Ҷz3wP-=;T \I]qnh.S]B۵kL`M73-X n4  @ݮ=z7l`P >ݷ^ܤ&;MX@kqƢ?eMZL˚'! ^EH^F@@@m}ʎ Th%K@@fPk#  EH ?oi( ǀ v jW}SZ@@@+ 8@@.o_i {@N 5QZ^pD  @X uR8@@ @CN8"@@ @úz)  zW'  a]@=Ы@kwҥrBk9rDVZUh>3@@ 7xC?^h{̙3 g  @`>p\߬cǎ `w%3 (@D?S2qD4h̚5v>|ɑ~ɓ'ː!CdҤIr7|@6ߊL  a"Ν;]we̛nI іe˖/ժU?P.\(ÇmƏ:u7@>Up{E]?OPb_vefIݺuyuVIHH(f_fEJQQQSjK-[bd„ 6lXS z Y  `@Qׅ.O.l+%')nͯR嗑 ҨQBA+]ğg߿_n6kLIr)gҕxW%S@]ohx9EGG<_"ٽpɒ%"۷ԨQ\dW^/&˴js}6mD/dzw}z" 6 DmٳGn9qℌ3~u^zge (7S:u̞@ܨ"ܥ~&[6`h\JJRlmS=kKwz$55z tq,JVNy2tP3SNOnɓ':Ȉ#˷oc   U={DoI|Ita.]Ȇ b6m^r/گ_?Yfg{@@:s1c5]"$Ըqchj+zm%[8il2>ޖRN#Z@è2)   P/Lj hU&EA@ PK|7xjJ&LP9]( u]h K-wqyw}'mSdKuϛ7O^x}/, .,yEzXI(?syWСCo[J#GdZ{4lPlM E'>mVn&6lGUfBuҨQ#i*~?~$''+ֹ'ߗ*UȜ9sd֬Y%yOz֒l߾]gdzdd>|Xf̘afocǎKFf ~2ZՔ RY4ŋcěG8~ ^+_^7v%cƌp8 >,_G]vmIOOwe~rCRS: ݄DՅL-J?UA ͕"MDigmQ.Z(n5c%A QQAqrV QK{r;8sj{{~35Ϝ{sC:I ]OLL ;e`XC-tCEٳ;w94?Gj^ʒ^S~bbB͟#U@˜3L8Q233<:D+3-./vϛ,CѣGrBNNH[+Y?zNAk6\kkk&~eedX-߷{MmMvv/mmmfWE3huV8k` :;::իWf|: @C;?64Y ͒f n͘d3n3[Lj@@o=/ 8Sԙ;F@&@7z^@p3wZ Mo0 :  pq, 0)((pXi.IR 333涾AD @z ȼ p޽JqqѷouNn߾-244dڒJYXX---m:Nd?}$z|/_]+ 7hQ۷o֭[&H,)))--5gazzer NH:;;y외z/qݦwY/,,4eQj񒔔$.]2?@o¼= {I{{9 0޽+sss Y__ϟ?{yUӛYQQq?~lʧH^^~dee)=rDz Yߗߛ:XT\|Z5@լ>X}y]&99٬onnXA+@z\AGNI@ }gBBy׭EYkqϝkyx+@@? DFFq:γL4Aڵk2880q3A,..ZGz 9R9 ! @3&Y---2<<,T&)UUUf|fss :11!O>5 I.\/$ϟU cbbxЏ?ϟ?: %@jI@ccJL(y|d\.ɓ''Z&--MiFfGGGKFFhR7KNN&'MMMys(e@[ q= 7 pExsu^rEwullƋZc@]؊  #.j@@< zva+  @}K  @=@@G>Z@@]؊  #PR-  gP.lE@o4SPIENDB`forcats/man/figures/lifecycle-superseded.svg0000644000176200001440000000171314241066126020757 0ustar liggesusers lifecyclelifecyclesupersededsuperseded forcats/man/figures/lifecycle-stable.svg0000644000176200001440000000167414241066126020074 0ustar liggesuserslifecyclelifecyclestablestable forcats/man/figures/lifecycle-experimental.svg0000644000176200001440000000171614241066126021314 0ustar liggesuserslifecyclelifecycleexperimentalexperimental forcats/man/figures/lifecycle-deprecated.svg0000644000176200001440000000171214241066126020713 0ustar liggesuserslifecyclelifecycledeprecateddeprecated forcats/man/figures/README-ordered-plot-1.png0000644000176200001440000006645014360013050020334 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 xT7d! ! Qe;U\"T-*U֢"Eˢ"RAR@يBHYd$$s|$3ssIsν7$$@@*HJ   `@y#  Thrs0@@P  *@Z @@  @ T8XYYYrR; DKott2///Ym~%gΜ 2`Z_m`gi}srr¶(]J:>}i8i[;kǛv~~~86IeҶV @}T &ήJVQZ%icbbDؒIII 4gbbUUYoEڶv/ ecުT@|oJ X%@jUsSY@@ZmP)%ׯ_H;s̐O  ^z@v@p+B#  uoQr@@l6  WԽmG@@W (4  ^P%G@\)@Zf۸ql߾rJHDb{hg|( @@#@;رco^={V<'Oʙ3gCiР>|lv!k׮u^Jv$--:OT Py^u/w!YnmjNα:kjgXBBЛ?B_shZyGhjܺ;w5k֘믿8f͚=-ZKӟAdÆ brIMMo]V^mO_|;f>@-ZdZMz駝2c iڴ[8&n-$ƶ& .6uNNNIIIIoB=@ڵ?^,믿^֭[gawcx嗛S4kL=gho~kK˖-'Oȑ#jժbY  _4??_&O\jꫯʱcNJ)ݻWfϞ]l9 @@ >͕'NHff'P^}̙3p1JJ\Z'edd8Oͣv-+ @]ܼyL21112c y'L-M6'JTT|g2i$iܸ>TRwu7)w)N:&>}Ȋ+Dkɇ~( 4Çˋ/(\pg O:yA)yto ׺:?l 8._\y>@-Z$C{رc=5֭[ԭ[׭ErժUv,B6u]vio[oc;׬YX3ޕ^zQjϣ &G ?SIHHUE]dw<Gf3=nݺUKUOR^Pk)''GG|'/)-:Y'\:|@8ے%::ZlBW^GrݓHwDwM$>F#gϞuuȱN۷֭[{ awSvO6E:CSʾ}:Y{ EnUu(+wHpLZPXi`l~H߫MuelWTg{֤AYlkg'mk7'=S_{={^-ZHVdܹwߙy3zjSz)uNҠQOL.]jݾ}[ڤIBV^ՅN{7e͛mH  mh\RnVӍ'M a~ڜ' 9?O3 zgyg;2j(r牞K5͛KJJY o]6lhz]uN* @Y abWbw^I ӻHj@i9YTQPYRҹ]^ٮs{C𡞧&{.A[6s̠M66 !}{ʖտS9r[SOl,KMMCY5o[;vf=z4"=+ME|6z_W67K cccEJJX%2@@ "6-KcB$@@*  Wr  P.r  @y@+~   -;! W+.\h)jP|@p=.l4 YͭG@@ 0F Fl|nYPV  @ Z@[  @ V89D@ =  PN@@@nj Ths@@@nP۟# .@z6m۷!mfJ@@ @ϡA7o\jb /( D,=sss%''GҠAR?q~+gٻwԮ][土/,11b X!@TJ3oٲE^xd֫WOv!QFK+^zI4R7nm֓?~ ^njYrJo>*_|g}(DGG"Byz]// ] ;~/Hj[Ɔ0alS;kۦ:{[SJJym촫%ud9xY$󟕻wYfIEɓeڴiԹo4nX̙#/2qDZ9Ӄ臇wPdt{pSe)?u.uyt_ _beSmSgsi'Ֆ.%5lYNdԩWhuSׯ_`8q:=q.]8)==]222!yLKK Iޙ y||LSؒD{­-Bթ0YYYd͊N߳'!)aݩS|PgD}vɰa7Cc9  `h'}fˏ>Hڴi^ݤI&ҧO3$@@fh}~g̼jժɤIث&:gԨQ2d3޴i @D4y7zƻ+ӹVNzᇝf[n?K8-YٌG@@JgvcS@@/P/<@IX  PRR6/esV# "@Z @@+@\OrC@(E V# W4  P-(RW/\S8RG@@ | ߶d  @D FdR)@@ |߶ igΜ@+@{ێ#  ueQh@@m;J RԕF@@ (9  JPW6F@+@޶  +@l%KȩSȑ#jժb3@@EE|6m?~\+g@@Jٳg3\AAʉ'$33S;fʪ',VӧOΝ;>J#;;{Bm>feeI\iw|gʫu7y N<)gΜ)@@k*V} ҤI 8p@ϟ/0N21112c y'L`AeӦMeĉ%|<&[?,z-[K/ddٱc7NڶmO.)))2dܹsM;h >|x/)39oҤI2rH+w}W̙#/6:t1BZhajݜԪUyG uJHH!ʔllپ"^pcR[[-Zo켷mm5{:8̂e[;;QUV5ܼN]|!=s2uTtXBƎ)h.X]4khS:zhٸq\z2k,G5M7dپ}[ҸqcO/yOtR0a|'>)oР '3dΝe͚5&%..5k4|jVCp ݞjԨ*DLml -6ֹz궽vNJJr};눯TLqN:)Tzv4ԤC۟~ 6l z"t|ӦMyݺuM/6l(ׯ7}jٲ)ÿ/ #11쯽_'4i壏>ef:_/֭=N.] e_Zn2g~PG{% Yz\s5&_u ީ[Ӥ= >/3WUz׾e9  !@OO{3=Etz[oճw}'}2l00`@'|y=y=:9!I;~I\{f8DrwE]dzfuۮ]w=H{8Xmڴ@@~PA(+ᇢ:A/t]wLIhh i׮]vZJ\Orҡz//t1tF-i@|P_ItP3g222r怺Uh2\ois@W* kr. S/3gH|ah0zI.\hjԡC?-p橖e  BƌcX߽{FsF|,)i|͵9Kچe  Tp9G/t7F\@@ TIH~KJ@@@jn* Th%@@ ,  PrRWPeff]k@@ 􀆜  x zk@@  8<Я_,nʒ9 I2q1   # IL\l  p*  e -#  ?  @@  *@z%d9uT5퓵k[@@&ir޼y,Zr  6 gϞ={H^^ , <{9rss=ˊ>9z9rb^# V p+R>^xA4i"'Nz=V5jÇef;,'MdEҼW˙3gӞeFEEy^I)Vײ[e?7o봯溔Թb޶im%N]mhgP?48|dԩҢE Yb;졁={˂ ߖQFyreÆ &`'|ҳy{ye3f̐nݺ9]Xn]ז= nc[TV-Қ6ֹvڥ"m۹f͚oƓ'O;wN >5uIM6|jٳ 4Hn^/ٶm[5:vX$J~{_;v:O*9u(:e–=UTl[l999ԹjժKpff5u֊H߭GRŶv֞ϔ3+7WiyjjڃCBرó~;I]$R4]xᅢ?NJOO/ zg}0}9s9"@@`O愡;̹i֬ٺaÆҽ{w ZZ5iԨ ?MG%C 1J6ms[V  ,@uu(_6ts&g% Ty͛7/E Ȓ%K<" V :d%}J?@@0   -7;" G|G۷ 4H6m*[l{rAIIINz)Сs=K/m5|8^|Z']q8Pc O,sc5X,mҶ%:W^ݶS=9pC%|3+qejcǎyP߰a,YZ,ٴi9IDMvY&}3o͓Ҧ+ho墊#V 2͜93ٗ9o怖̕;0ԕVB679znH}*Ʃ%ѤtM:Hk0WZ@+2[unz%^}%-48a|gZZYk@@ ޣ=zȨQe4y\Pf$]Zj&}E%MzbN ! Pza[n\vڢg @:԰aCџ&=͓@@7 I>zǞ>ȜxzL: @@@@5C~[Qt@v  v @%xTLϪn޼yżF@@TO[T*^𥕉  )7;ZRaXpuKI  @E @ '_`@@pԻz$ .Ç6Νf9  E@{74h`') ^/ROrɿ@NiY { _˄ LEqOScJ  Pz/J  (^e'@@ ^O@j߾ԩSGRSS妛nmF@@ ~uVsE]$/|Pv@@W Pȑ#GL=#wq˒k 5G@(@adҩS's(,^Lec@@{?s,]^zFKό%++KΝkF}\\  @ <{۷X ?.G-<\XB$@@(={ѣe޽u 7YVOOĉ:'5;;[̴]^Rϗ }ffDןI={xkuRѲ;v,r8y  B/޽{\ իW7^NN9ITI/ Shwjϗ^zI4LNN;vȸq%rNjN%3fIU^z >5|}QidժURF 9|Lusj=zҒ.׻Aiҩ 2ҥYF.Bp^jhW]ߡCyI}( u;LeG{oVؒzڒrRo}I111 %󙒒bb!usrzs}աLL*sY;ԩSS3MȓvI6m7wx?lM:\$NmtrN:U:$;v4'ji]L͛C^Wq {YǤ`fyB ^8i}oӦ:k}5EC~3 #mXnhO2iPW4 9s |萴lIZ_DZ]DzU O~[g:˦!xYGOԩc:"a^߳{@J:P  pnLT<7?F@(h@@4f6דw~Gssu]g.GtnEbo@@H8D:׉z\=Eo5?ֻ,q!!  K !M=O,Iz; @@@ @vgzq^*G?0c   s{;zѤJw]i6;Zoɦ{ﻏ!.PSZO06lm'5iܹsOp ;R@@ռO.ro܂ꫯ;eС@@l8W^ZVZ%[l͏n~X -oYX @2?~ܜԭ[7  hfܼysyyy9   oۥA2b3I  E k׮? _3ҕW^)ڵiӦYuI-  @aPgsرcEȑ#~O3myD@@@^yi֬tIv-ϗ Yr,ZȬ/z^#  ||Ϟ=eŊҲeKo+wuN>f8^qF2   PT +^:tP4k }bb5O@@(*̐qZ=y6lPjժy'˖-u:9r\?y  6P=]ySOI.]̙nx=;VtqP_ݻWfϞ< @p@cǎr!ўNM_J*2o @] ^zzjs-[^~':`5y P $֪U+set+l#  `@*i`!H r̘1f>ivTORڵeIz}'<󌙢At. *0=ӕ{=˽z&U5:?giPWʤs@uɿ̙3ok9%<4ms9z)DF[]+e_L=2a~55,kl Q ѻr@@ @ïM(  ݼT@?kJ DhD7/C@O4ڄ! -e"y}Wn…¾5zo]M999U1j uaQd@@nn=ʎ P!x6Z0ܯ_`dCa.x0/!CQP[:# (@Z@QV  @% V">F@l թ3  Pϡ@@@mlu Th۷O֮][GP  @ V`l޼Y-ZTGP  @ MN<)gΜQrssMO;wʉ'xQ9rH,@@lVhӧ˞={d۶mruI.]g&M֭[^z'MdE q׮]n:ϲ/\jժy'QQQȖWZUS_u %S<@q;vmVSЛnI ٰa̟?|'fbƌҴiS%~hըQp!m[msm{k[?,))??Zn-;vK,K=dӦMs>5(uz(:vX$K>}03<#SLze2|p孷2A9sdres='SN-Z;l!˗/wyG1-Z$CG}]qׅ3f0Ag @H֭+*HGYlsڵB5kt}dEi&ԩ >gϞ2h O`ٺukM#::ZF)+W4A:@{H55lP֯_/;w4=|jhI{\cbbLoU}GTX埔Tl*TX0ȅ߭St̖d[;kgJJyo;[ۺ\+Pƨs:CS&$$U D 6Ȏ;J\WM6Kی  5?7? oV GڵkK\W+Vȗ_~YfG@ -gΜ]v>JN2=G)%;;r]/[z" 6p/xVրTٻw ҲeK-D/^,f͒&M͛[n2b9q &5k TL1bccŦ:犦)((08mWVM|,ԫYM0بQ#yeԩꫯz?z2eԫWO/ Çˌ3D{'MQ!&N(UV#G ԓ)O@@@\HԹsg|jϥLrJY`9H˳gw}'wy4&&F^|Eg7łOA'iLiii̞@BiSCG233%//ϯM$;>>LU7'}Kq8t6rz/ɑ:t~ڳxw78p@N|||衇cǎ~z^:S L<;v̬}3gNb1  @iVYYYrY>e d߾}rAҞdddرcov[]RRRdȐ!aΝ+2h >|M6'JTTToAg&MdҤI2rH+w}W̙#/6 :TF!-Z0yڵK[O]vԪU:O|'"O H*U$J|\{:8Byp۶vv>V*Zw7'.`ezM73< @.]*&LO>D5kfļ<=zlܸQ.tXAK/oF:w,k֬1_-qqqcY TS3Ǝƌ/ '5jSqLYmzU|V餤$WYG|%+Ж-[7/ #11Q7n,SL1VjҡO?gi&y'Ͷ{Ge2x`3˺uDӥK_y)'Nx^I{XCQfDNby%i* %yyӮnf}j+Y*._7xǧ}ҺukdO %չA5_rjJڶm+_%::Z4N:C+<''Ǫ./ٲelݺPp3cV  ,`me\XsBky뭷v밼k>^ˮ]J߾}ͦw1@Z,)O! .S7oTdUW]U:SE= -p35=ה4ԁ޽{kQ,9sfXVOy$z@^   -'! O!~~T@Vtu   F   $  $@! K4X  h@Ll  ,`I  @@0-[LNꫯʱcNJmw^={v,@@/@dzVLk'+'NȜ9sqwÇ<ӧ'|dРA2ydwuϏw [hC^  "hȫJ^dvmr5._\y>@-Z$CƎy=c ֭5O@@ |?&555J:>ì]4mTn*>b=I)&&~Rܼy<#e6l׮L0SKs OU\@J:/$X ȉ'ٳʶRq_'%㵼JT8}4hР|PJm[M4qRzz:uyǤK D@(?5Tc qsZ7+.]jN2ھ}0LIF|`RMov׷jJDyyz?xD@l4޳gqrq5jT=.yN:ҼysE7Q9qIO>jذDGGѣ@@& lpy뚕% fNgIy\ ./i~^~I7CgΜ)A[&{Z~d -3g YUu^;=CN4QicllOIIOD*mc  VN  @ qP4@@ @#U  a8 @DHlU h7EC@"Q0EbP hvxS֡۹%ڲ$^WkK[e8`KM=k֬)YokTʉ'$33S;Y~YٳgY uVVgɓ'Ӟ׺敝-yyy{ng   X}/+Vŋ%::Zjժ%'Om۶ɔ)SL#ϗW_}UVZ%5jԐÇeȐ!f*UȰaäYfOzJ^z%ϗdٱc7Nڶmyig~䢋./ @Yf+ps *hBv) ,0Ajo2j(IKK3kѣn:c_H׮]۷[o%76Ҁ;2x7RAADEEk3! 8K.:v(7o6hzvfMSN&={AE_f\x&\z!TTn]| 'u]I&u^Qg )fNUV{jϧtf ˄g DuΜ9#j/k׮+]vɥ^jv^+  ? 7C{*dSNݺu+УGsiɒ%ҲeK6o\   -7;" G  : юuPifffj~ǛB\mIz-I뫷ʲrkY8w5:$yyy6TѶv|שSFog}ՁP_2,G@hHX@@/# D9!a LoViѢL0! JI'^~sʇ[7ސW^yE.(;'UV~;Yt\p;G2uTk·A(=A@t[:=??mŦe6ˈ :)ŅM"k;߳>"[v&1C@N &JDB.йsgW^ȏ*W.;r C.иqcC~PkזnA+ =z"mgS'Fz S?@@ 8  @ 0-\~GoF5jd΄/"~zF%555BjG5T`rW]9"[n={W\!iiib],PeƍjtUWz w J+ڎz'7'Eu3?%ͯo{NZn-v 2Dz%qqq)D~Uw- 40J֮ꗋcJzzzku"y{Iϟ?_̙#z-X|dҧO%}5ړ}a5kYֿۛ/yPwC?h?њ7LϦܷo_ΪK,orI;!"z _NsN&߼y&<$"֫:_޽{7lFtԃN]vɨQ{5#z;NNyMw GZjɉ' \>]Ux2R EʽG3{wUp1T4ŕ ?V5 5#4ŝFʅb"Dp1*Ak%A(с7Q \d*DNC:4;{L3\]OKK ;g`XC-tZ[[sHw{vvVƍR^^n:kzɮH|0tk^{N;eeerMY]]5_|)?ϟ?#@e@ zJMXYY)RUUeZŋE/kp'r]ggG;`{6s֏?kLcrrZs$##CN>m5c/@_(< @Z[[Nn߾mfMMeccC޿/ts6ȍL .Hii9 #EEEhΔB "8z IߗiZmLNN-[)))1_TZ6XXXh4뛛6V@ W@$Ao̙3>_˗/挧TT/[֢>.:ׄ1W_1#A70f8j,y/QQQk Z#=FDD@ N} ,>M*))((F3>׌x<288hbcc%..$ WxЅ_R@- @0Y隩~Yy\% fct_count() gss_cat$relig \%>\% fct_anon() \%>\% fct_count() gss_cat$relig \%>\% fct_anon("X") \%>\% fct_count() } forcats/man/pipe.Rd0000644000176200001440000000036613626051403013717 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{\%>\%} \alias{\%>\%} \title{Pipe operator} \usage{ lhs \%>\% rhs } \description{ See \code{\link[magrittr]{\%>\%}} for more details. } \keyword{internal} forcats/man/fct_drop.Rd0000644000176200001440000000140114360012612014544 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/drop.R \name{fct_drop} \alias{fct_drop} \title{Drop unused levels} \usage{ fct_drop(f, only = NULL) } \arguments{ \item{f}{A factor (or character vector).} \item{only}{A character vector restricting the set of levels to be dropped. If supplied, only levels that have no entries and appear in this vector will be removed.} } \description{ Compared to \code{base::droplevels()}, does not drop \code{NA} levels that have values. } \examples{ f <- factor(c("a", "b"), levels = c("a", "b", "c")) f fct_drop(f) # Set only to restrict which levels to drop fct_drop(f, only = "a") fct_drop(f, only = "c") } \seealso{ \code{\link[=fct_expand]{fct_expand()}} to add additional levels to a factor. } forcats/man/fct.Rd0000644000176200001440000000256114241555020013533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/fct.R \name{fct} \alias{fct} \title{Create a factor} \usage{ fct(x = character(), levels = NULL, na = character()) } \arguments{ \item{x}{A character vector. Values must occur in either \code{levels} or \code{na}.} \item{levels}{A character vector of known levels. If not supplied, will be computed from the unique values of \code{x}, in the order in which they occur.} \item{na}{A character vector of values that should become missing values.} } \value{ A factor. } \description{ \code{fct()} is a stricter version of \code{\link[=factor]{factor()}} that errors if your specification of \code{levels} is inconsistent with the values in \code{x}. } \examples{ # Use factors when you know the set of possible values a variable might take x <- c("A", "O", "O", "AB", "A") fct(x, levels = c("O", "A", "B", "AB")) # If you don't specify the levels, fct will create from the data # in the order that they're seen fct(x) # Differences with base R ----------------------------------------------- # factor() silently generates NAs x <- c("a", "b", "c") factor(x, levels = c("a", "b")) # fct() errors try(fct(x, levels = c("a", "b"))) # Unless you explicitly supply NA: fct(x, levels = c("a", "b"), na = "c") # factor() sorts default levels: factor(c("y", "x")) # fct() uses in order of appearance: fct(c("y", "x")) } forcats/man/fct_cross.Rd0000644000176200001440000000156514241066126014753 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cross.R \name{fct_cross} \alias{fct_cross} \title{Combine levels from two or more factors to create a new factor} \usage{ fct_cross(..., sep = ":", keep_empty = FALSE) } \arguments{ \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> Additional factors or character vectors.} \item{sep}{A character string to separate the levels} \item{keep_empty}{If TRUE, keep combinations with no observations as levels} } \value{ The new factor } \description{ Computes a factor whose levels are all the combinations of the levels of the input factors. } \examples{ fruit <- factor(c("apple", "kiwi", "apple", "apple")) colour <- factor(c("green", "green", "red", "green")) eaten <- c("yes", "no", "yes", "no") fct_cross(fruit, colour) fct_cross(fruit, colour, eaten) fct_cross(fruit, colour, keep_empty = TRUE) } forcats/man/as_factor.Rd0000644000176200001440000000222213626262603014722 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/as_factor.R \name{as_factor} \alias{as_factor} \alias{as_factor.factor} \alias{as_factor.character} \alias{as_factor.numeric} \alias{as_factor.logical} \title{Convert input to a factor} \usage{ as_factor(x, ...) \method{as_factor}{factor}(x, ...) \method{as_factor}{character}(x, ...) \method{as_factor}{numeric}(x, ...) \method{as_factor}{logical}(x, ...) } \arguments{ \item{x}{Object to coerce to a factor.} \item{...}{Other arguments passed down to method.} } \description{ Compared to base R, when \code{x} is a character, this function creates levels in the order in which they appear, which will be the same on every platform. (Base R sorts in the current locale which can vary from place to place.) When \code{x} is numeric, the ordering is based on the numeric value and consistent with base R. } \details{ This is a generic function. } \examples{ # Character object x <- c("a", "z", "g") as_factor(x) as.factor(x) # Character object containing numbers y <- c("1.1", "11", "2.2", "22") as_factor(y) as.factor(y) # Numeric object z <- as.numeric(y) as_factor(z) as.factor(z) } forcats/man/lvls_union.Rd0000644000176200001440000000056713626051403015155 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lvls.R \name{lvls_union} \alias{lvls_union} \title{Find all levels in a list of factors} \usage{ lvls_union(fs) } \arguments{ \item{fs}{A list of factors.} } \description{ Find all levels in a list of factors } \examples{ fs <- list(factor("a"), factor("b"), factor(c("a", "b"))) lvls_union(fs) } forcats/man/fct_lump.Rd0000644000176200001440000000624414357100046014574 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lump.R \name{fct_lump} \alias{fct_lump} \alias{fct_lump_min} \alias{fct_lump_prop} \alias{fct_lump_n} \alias{fct_lump_lowfreq} \title{Lump uncommon factor together levels into "other"} \usage{ fct_lump( f, n, prop, w = NULL, other_level = "Other", ties.method = c("min", "average", "first", "last", "random", "max") ) fct_lump_min(f, min, w = NULL, other_level = "Other") fct_lump_prop(f, prop, w = NULL, other_level = "Other") fct_lump_n( f, n, w = NULL, other_level = "Other", ties.method = c("min", "average", "first", "last", "random", "max") ) fct_lump_lowfreq(f, w = NULL, other_level = "Other") } \arguments{ \item{f}{A factor (or character vector).} \item{n}{Positive \code{n} preserves the most common \code{n} values. Negative \code{n} preserves the least common \code{-n} values. It there are ties, you will get at least \code{abs(n)} values.} \item{prop}{Positive \code{prop} lumps values which do not appear at least \code{prop} of the time. Negative \code{prop} lumps values that do not appear at most \code{-prop} of the time.} \item{w}{An optional numeric vector giving weights for frequency of each value (not level) in f.} \item{other_level}{Value of level used for "other" values. Always placed at end of levels.} \item{ties.method}{A character string specifying how ties are treated. See \code{\link[=rank]{rank()}} for details.} \item{min}{Preserve levels that appear at least \code{min} number of times.} } \description{ A family for lumping together levels that meet some criteria. \itemize{ \item \code{fct_lump_min()}: lumps levels that appear fewer than \code{min} times. \item \code{fct_lump_prop()}: lumps levels that appear in fewer than (or equal to) \code{prop * n} times. \item \code{fct_lump_n()} lumps all levels except for the \code{n} most frequent (or least frequent if \code{n < 0}) \item \code{fct_lump_lowfreq()} lumps together the least frequent levels, ensuring that "other" is still the smallest level. } \code{fct_lump()} exists primarily for historical reasons, as it automatically picks between these different methods depending on its arguments. We no longer recommend that you use it. } \examples{ x <- factor(rep(LETTERS[1:9], times = c(40, 10, 5, 27, 1, 1, 1, 1, 1))) x \%>\% table() x \%>\% fct_lump_n(3) \%>\% table() x \%>\% fct_lump_prop(0.10) \%>\% table() x \%>\% fct_lump_min(5) \%>\% table() x \%>\% fct_lump_lowfreq() \%>\% table() x <- factor(letters[rpois(100, 5)]) x table(x) table(fct_lump_lowfreq(x)) # Use positive values to collapse the rarest fct_lump_n(x, n = 3) fct_lump_prop(x, prop = 0.1) # Use negative values to collapse the most common fct_lump_n(x, n = -3) fct_lump_prop(x, prop = -0.1) # Use weighted frequencies w <- c(rep(2, 50), rep(1, 50)) fct_lump_n(x, n = 5, w = w) # Use ties.method to control how tied factors are collapsed fct_lump_n(x, n = 6) fct_lump_n(x, n = 6, ties.method = "max") # Use fct_lump_min() to lump together all levels with fewer than `n` values table(fct_lump_min(x, min = 10)) table(fct_lump_min(x, min = 15)) } \seealso{ \code{\link[=fct_other]{fct_other()}} to convert specified levels to other. } forcats/man/fct_unique.Rd0000644000176200001440000000150514355354616015134 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/unique.R \name{fct_unique} \alias{fct_unique} \title{Unique values of a factor, as a factor} \usage{ fct_unique(f) } \arguments{ \item{f}{A factor.} } \value{ A factor. } \description{ \code{fct_unique()} extracts the complete set of possible values from the levels of the factor, rather than looking at the actual values, like \code{\link[=unique]{unique()}}. \code{fct_unique()} only uses the values of \code{f} in one way: it looks for implicit missing values so that they can be included in the result. } \examples{ f <- fct(letters[rpois(100, 10)]) unique(f) # in order of appearance fct_unique(f) # in order of levels f <- fct(letters[rpois(100, 2)], letters[1:20]) unique(f) # levels that appear in data fct_unique(f) # all possible levels } forcats/man/fct_recode.Rd0000644000176200001440000000223413764157244015070 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/recode.R \name{fct_recode} \alias{fct_recode} \title{Change factor levels by hand} \usage{ fct_recode(.f, ...) } \arguments{ \item{.f}{A factor (or character vector).} \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> A sequence of named character vectors where the name gives the new level, and the value gives the old level. Levels not otherwise mentioned will be left as is. Levels can be removed by naming them \code{NULL}.} } \description{ Change factor levels by hand } \examples{ x <- factor(c("apple", "bear", "banana", "dear")) fct_recode(x, fruit = "apple", fruit = "banana") # If you make a mistake you'll get a warning fct_recode(x, fruit = "apple", fruit = "bananana") # If you name the level NULL it will be removed fct_recode(x, NULL = "apple", fruit = "banana") # Wrap the left hand side in quotes if it contains special variables fct_recode(x, "an apple" = "apple", "a bear" = "bear") # When passing a named vector to rename levels use !!! to splice x <- factor(c("apple", "bear", "banana", "dear")) levels <- c(fruit = "apple", fruit = "banana") fct_recode(x, !!!levels) } forcats/man/fct_shift.Rd0000644000176200001440000000122413626051403014725 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/shift.R \name{fct_shift} \alias{fct_shift} \title{Shift factor levels to left or right, wrapping around at end} \usage{ fct_shift(f, n = 1L) } \arguments{ \item{f}{A factor.} \item{n}{Positive values shift to the left; negative values shift to the right.} } \description{ This is useful when the levels of an ordered factor are actually cyclical, with different conventions on the starting point. } \examples{ x <- factor( c("Mon", "Tue", "Wed"), levels = c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"), ordered = TRUE ) x fct_shift(x) fct_shift(x, 2) fct_shift(x, -1) } forcats/man/fct_reorder.Rd0000644000176200001440000000547014360012625015260 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reorder.R \name{fct_reorder} \alias{fct_reorder} \alias{fct_reorder2} \alias{last2} \alias{first2} \title{Reorder factor levels by sorting along another variable} \usage{ fct_reorder( .f, .x, .fun = median, ..., .na_rm = NULL, .default = Inf, .desc = FALSE ) fct_reorder2( .f, .x, .y, .fun = last2, ..., .na_rm = NULL, .default = -Inf, .desc = TRUE ) last2(.x, .y) first2(.x, .y) } \arguments{ \item{.f}{A factor (or character vector).} \item{.x, .y}{The levels of \code{f} are reordered so that the values of \code{.fun(.x)} (for \code{fct_reorder()}) and \code{fun(.x, .y)} (for \code{fct_reorder2()}) are in ascending order.} \item{.fun}{n summary function. It should take one vector for \code{fct_reorder}, and two vectors for \code{fct_reorder2}, and return a single value.} \item{...}{Other arguments passed on to \code{.fun}.} \item{.na_rm}{Should \code{fct_reorder()} remove missing values? If \code{NULL}, the default, will remove missing values with a warning. Set to \code{FALSE} to preserve \code{NA}s (if you \code{.fun} already handles them) and \code{TRUE} to remove silently.} \item{.default}{What default value should we use for \code{.fun} for empty levels? Use this to control where empty levels appear in the output.} \item{.desc}{Order in descending order? Note the default is different between \code{fct_reorder} and \code{fct_reorder2}, in order to match the default ordering of factors in the legend.} } \description{ \code{fct_reorder()} is useful for 1d displays where the factor is mapped to position; \code{fct_reorder2()} for 2d displays where the factor is mapped to a non-position aesthetic. \code{last2()} and \code{first2()} are helpers for \code{fct_reorder2()}; \code{last2()} finds the last value of \code{y} when sorted by \code{x}; \code{first2()} finds the first value. } \examples{ # fct_reorder() ------------------------------------------------------------- # Useful when a categorical variable is mapped to position boxplot(Sepal.Width ~ Species, data = iris) boxplot(Sepal.Width ~ fct_reorder(Species, Sepal.Width), data = iris) # or with library(ggplot2) ggplot(iris, aes(fct_reorder(Species, Sepal.Width), Sepal.Width)) + geom_boxplot() # fct_reorder2() ------------------------------------------------------------- # Useful when a categorical variable is mapped to color, size, shape etc chks <- subset(ChickWeight, as.integer(Chick) < 10) chks <- transform(chks, Chick = fct_shuffle(Chick)) # Without reordering it's hard to match line to legend ggplot(chks, aes(Time, weight, colour = Chick)) + geom_point() + geom_line() # With reordering it's much easier ggplot(chks, aes(Time, weight, colour = fct_reorder2(Chick, Time, weight))) + geom_point() + geom_line() + labs(colour = "Chick") } forcats/man/fct_na_value_to_level.Rd0000644000176200001440000000324014357073063017303 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/na.R \name{fct_na_value_to_level} \alias{fct_na_value_to_level} \alias{fct_na_level_to_value} \title{Convert between \code{NA} values and \code{NA} levels} \usage{ fct_na_value_to_level(f, level = NA) fct_na_level_to_value(f, extra_levels = NULL) } \arguments{ \item{f}{A factor (or character vector).} \item{level}{Optionally, instead of converting the \code{NA} values to an \code{NA} level, convert it to a level with this value.} \item{extra_levels}{Optionally, a character vector giving additional levels that should also be converted to \code{NA} values.} } \description{ There are two ways to represent missing values in factors: in the values and in the levels. \code{NA}s in the values are most useful for data analysis (since \code{\link[=is.na]{is.na()}} returns what you expect), but because the \code{NA} is not explicitly recorded in the levels, there's no way to control its position (it's almost always displayed last or not at all). Putting the \code{NA}s in the levels allows you to control its display, at the cost of losing accurate \code{is.na()} reporting. (It is possible to have a factor with missing values in both the values and the levels but it requires some explicit gymnastics and we don't recommend it.) } \examples{ # Most factors store NAs in the values: f1 <- fct(c("a", "b", NA, "c", "b", NA)) levels(f1) as.integer(f1) is.na(f1) # But it's also possible to store them in the levels f2 <- fct_na_value_to_level(f1) levels(f2) as.integer(f2) is.na(f2) # If needed, you can convert back to NAs in the values: f3 <- fct_na_level_to_value(f2) levels(f3) as.integer(f3) is.na(f3) } forcats/man/fct_expand.Rd0000644000176200001440000000130314355354616015101 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expand.R \name{fct_expand} \alias{fct_expand} \title{Add additional levels to a factor} \usage{ fct_expand(f, ..., after = Inf) } \arguments{ \item{f}{A factor (or character vector).} \item{...}{Additional levels to add to the factor. Levels that already exist will be silently ignored.} \item{after}{Where should the new values be placed?} } \description{ Add additional levels to a factor } \examples{ f <- factor(sample(letters[1:3], 20, replace = TRUE)) f fct_expand(f, "d", "e", "f") fct_expand(f, letters[1:6]) fct_expand(f, "Z", after = 0) } \seealso{ \code{\link[=fct_drop]{fct_drop()}} to drop unused factor levels. } forcats/man/fct_relevel.Rd0000644000176200001440000000301214241066126015245 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/relevel.R \name{fct_relevel} \alias{fct_relevel} \title{Reorder factor levels by hand} \usage{ fct_relevel(.f, ..., after = 0L) } \arguments{ \item{.f}{A factor (or character vector).} \item{...}{Either a function (or formula), or character levels. A function will be called with the current levels as input, and the return value (which must be a character vector) will be used to relevel the factor. Any levels not mentioned will be left in their existing order, by default after the explicitly mentioned levels. Supports tidy dots.} \item{after}{Where should the new values be placed?} } \description{ This is a generalisation of \code{\link[stats:relevel]{stats::relevel()}} that allows you to move any number of levels to any location. } \examples{ f <- factor(c("a", "b", "c", "d"), levels = c("b", "c", "d", "a")) fct_relevel(f) fct_relevel(f, "a") fct_relevel(f, "b", "a") # Move to the third position fct_relevel(f, "a", after = 2) # Relevel to the end fct_relevel(f, "a", after = Inf) fct_relevel(f, "a", after = 3) # Relevel with a function fct_relevel(f, sort) fct_relevel(f, sample) fct_relevel(f, rev) # Using 'Inf' allows you to relevel to the end when the number # of levels is unknown or variable (e.g. vectorised operations) df <- forcats::gss_cat[, c("rincome", "denom")] lapply(df, levels) df2 <- lapply(df, fct_relevel, "Don't know", after = Inf) lapply(df2, levels) # You'll get a warning if the levels don't exist fct_relevel(f, "e") } forcats/man/fct_shuffle.Rd0000644000176200001440000000055713626051403015254 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/shuffle.R \name{fct_shuffle} \alias{fct_shuffle} \title{Randomly permute factor levels} \usage{ fct_shuffle(f) } \arguments{ \item{f}{A factor (or character vector).} } \description{ Randomly permute factor levels } \examples{ f <- factor(c("a", "b", "c")) fct_shuffle(f) fct_shuffle(f) } forcats/man/fct_count.Rd0000644000176200001440000000130013626051403014733 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/count.R \name{fct_count} \alias{fct_count} \title{Count entries in a factor} \usage{ fct_count(f, sort = FALSE, prop = FALSE) } \arguments{ \item{f}{A factor (or character vector).} \item{sort}{If \code{TRUE}, sort the result so that the most common values float to the top.} \item{prop}{If \code{TRUE}, compute the fraction of marginal table.} } \value{ A tibble with columns \code{f}, \code{n} and \code{p}, if prop is \code{TRUE}. } \description{ Count entries in a factor } \examples{ f <- factor(sample(letters)[rpois(1000, 10)]) table(f) fct_count(f) fct_count(f, sort = TRUE) fct_count(f, sort = TRUE, prop = TRUE) } forcats/DESCRIPTION0000644000176200001440000000240214365570422013427 0ustar liggesusersPackage: forcats Title: Tools for Working with Categorical Variables (Factors) Version: 1.0.0 Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), person("RStudio", role = c("cph", "fnd")) ) Description: Helpers for reordering factor levels (including moving specified levels to front, ordering by first appearance, reversing, and randomly shuffling), and tools for modifying factor levels (including collapsing rare levels into other, 'anonymising', and manually 'recoding'). License: MIT + file LICENSE URL: https://forcats.tidyverse.org/, https://github.com/tidyverse/forcats BugReports: https://github.com/tidyverse/forcats/issues Depends: R (>= 3.4) Imports: cli (>= 3.4.0), glue, lifecycle, magrittr, rlang (>= 1.0.0), tibble Suggests: covr, dplyr, ggplot2, knitr, readr, rmarkdown, testthat (>= 3.0.0), withr VignetteBuilder: knitr Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Encoding: UTF-8 LazyData: true RoxygenNote: 7.2.3 NeedsCompilation: no Packaged: 2023-01-27 14:11:11 UTC; hadleywickham Author: Hadley Wickham [aut, cre], RStudio [cph, fnd] Maintainer: Hadley Wickham Repository: CRAN Date/Publication: 2023-01-29 22:20:02 UTC forcats/build/0000755000176200001440000000000014364755577013041 5ustar liggesusersforcats/build/vignette.rds0000644000176200001440000000032314364755577015376 0ustar liggesusersb```b`abd`b2 1# 'N/JN,) MA+)O)M.S(W*ES3!$7Mn:%`  `aBRȚn2KjAj^ HvѴpxVaaqIY0AAn0Ez0?½Ht&${+%$Q/n̜/6forcats/tests/0000755000176200001440000000000014364755572013077 5ustar liggesusersforcats/tests/testthat/0000755000176200001440000000000014365570422014725 5ustar liggesusersforcats/tests/testthat/test-fct.R0000644000176200001440000000166314241555020016575 0ustar liggesuserstest_that("can create simple example", { expect_equal( fct(c("x", "y", "z")), factor(c("x", "y", "z")) ) }) test_that("orders by appearance", { expect_equal( fct(c("y", "x")), factor(c("y", "x"), levels = c("y", "x")) ) }) test_that("checks input types", { expect_snapshot(error = TRUE, { fct(1:3) fct("x", 1:3) fct("x", "y", na = 1) }) }) test_that("clear error if levels are incomplete", { expect_snapshot(error = TRUE, fct(c("x", "y", "z"), c("x", "y")) ) }) test_that("can covert values to implicit or explcit NA", { expect_equal( fct(c("x", "y", "z"), na = "z"), factor(c("x", "y", NA), levels = c("x", "y")) ) expect_equal( fct(c("x", "y", "z"), c("x", "y"), na = "z"), factor(c("x", "y", NA), levels = c("x", "y")) ) expect_equal( fct(c("x", "y", "z"), c("x", "y", NA), na = "z"), factor(c("x", "y", NA), levels = c("x", "y", NA), exclude = NULL) ) }) forcats/tests/testthat/test-match.R0000644000176200001440000000066114360012612017107 0ustar liggesuserstest_that("equivalent to %in% when levels present", { f <- factor(c("a", "b", "c", NA)) expect_equal(fct_match(f, "a"), f %in% "a") expect_equal(fct_match(f, NA), f %in% NA) }) test_that("error when levels are missing", { f <- factor(c("a", "b", "c")) expect_snapshot(fct_match(f, "d"), error = TRUE) }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_match(1) fct_match("x", 1) }) }) forcats/tests/testthat/test-relevel.R0000644000176200001440000000212714355407761017471 0ustar liggesuserstest_that("warns about unknown levels", { f1 <- factor(c("a", "b")) expect_snapshot(f2 <- fct_relevel(f1, "d")) expect_equal(levels(f2), levels(f1)) }) test_that("moves supplied levels to front", { f1 <- factor(c("a", "b", "c", "d")) f2 <- fct_relevel(f1, "c", "b") expect_equal(levels(f2), c("c", "b", "a", "d")) }) test_that("can moves supplied levels to end", { f1 <- factor(c("a", "b", "c", "d")) f2 <- fct_relevel(f1, "a", "b", after = 2) f3 <- fct_relevel(f1, "a", "b", after = Inf) expect_equal(levels(f2), c("c", "d", "a", "b")) expect_equal(levels(f3), c("c", "d", "a", "b")) }) test_that("can relevel with function ", { f1 <- fct_rev(factor(c("a", "b"))) f2a <- fct_relevel(f1, rev) f2b <- fct_relevel(f1, ~ rev(.)) expect_equal(levels(f2a), c("a", "b")) expect_equal(levels(f2b), c("a", "b")) }) test_that("function must return character vector", { f <- factor(c("a", "b")) expect_error(fct_relevel(f, ~1), "character vector") }) test_that("dots must be unnamed", { f <- fct(c("a", "b", "c")) expect_snapshot(fct_relevel(f, a = "b"), error = TRUE) }) forcats/tests/testthat/test-lump.R0000644000176200001440000001324514357077664017022 0ustar liggesusers# common behaviour --------------------------------------------------------- test_that("can use other_level = NA", { f <- fct(c("a", "a", "a", "a", "b")) expect_equal(levels(fct_lump_lowfreq(f, other_level = NA)), c("a", NA)) expect_equal(levels(fct_lump_n(f, n = 1, other_level = NA)), c("a", NA)) expect_equal(levels(fct_lump_prop(f, prop = .2, other_level = NA)), c("a", NA)) expect_equal(levels(fct_lump_min(f, 2, other_level = NA)), c("a", NA)) }) test_that("bad weights generates friendly error messages", { expect_snapshot(error = TRUE, { fct_lump(letters, w = letters) fct_lump(letters, w = 1:10) fct_lump(letters, w = c(-1, rep(1, 24), -1)) }) }) # fct_lump() ---------------------------------------------------------------- test_that("can only supply one of n and prop", { f <- c("a", "b", "c") expect_snapshot(fct_lump(f, n = 1, prop = 0.1), error = TRUE) }) # fct_lump_min ------------------------------------------------------------ test_that("fct_lump_min works when not weighted", { f <- c("a", "a", "a", "b", "b", "c", "d", "e", "f", "g") expect_equal(levels(fct_lump_min(f, min = 3)), c("a", "Other")) expect_equal(levels(fct_lump_min(f, min = 2)), c("a", "b", "Other")) }) test_that("fct_lump_min works when weighted", { f <- c("a", "b", "c", "d", "e") w <- c( 0.2, 2, 6, 4, 1) expect_equal(levels(fct_lump_min(f, min = 6, w = w)), c("c", "Other")) expect_equal(levels(fct_lump_min(f, min = 1.5, w = w)), c("b", "c", "d", "Other")) }) test_that("checks inputs", { expect_snapshot(error = TRUE, { fct_lump_min(1:3) fct_lump_min(factor(), min = "x") }) }) # fct_lump_n() ------------------------------------------------------------ test_that("can keep/drop with positive/negative values", { f <- c("a", "a", "a", "b", "b", "c", "d", "e", "f", "g") expect_equal(levels(fct_lump_n(f, n = 1)), c("a", "Other")) expect_equal(levels(fct_lump_n(f, n = 2)), c("a", "b", "Other")) f <- c("a", "a", "a", "a", "b", "b", "b", "b", "c", "d") expect_equal(levels(fct_lump_n(f, n = -1)), c("c", "d", "Other")) }) test_that("ties are respected and can be controled", { f <- c("a", "a", "a", "b", "b", "c", "d", "e", "f", "g") expect_equal(levels(fct_lump_n(f, 2)), c("a", "b", "Other")) expect_equal( levels(fct_lump_n(f, n = 4, ties.method = "min")), c("a", "b", "c", "d", "e", "f", "g") ) expect_equal( levels(fct_lump_n(f, n = 4, ties.method = "max")), c("a", "b", "Other") ) }) test_that("idempotent if all element satisfies condition", { f <- c("a", "a", "a", "b", "b", "c", "d", "e", "f", "g") expect_equal(levels(fct_lump_n(f, n = 10)), c("a", "b", "c", "d", "e", "f", "g")) expect_equal(levels(fct_lump_n(f, n = -10)), c("a", "b", "c", "d", "e", "f", "g")) }) test_that("can supply weights", { f <- c("a", "b", "c", "d", "e") w <- c( 0.2, 2, 6, 4, 1) expect_equal(levels(fct_lump_n(f, n = 2, w = w)), c("c", "d", "Other")) expect_equal(levels(fct_lump_n(f, n = 3, w = w)), c("b", "c", "d", "Other")) }) test_that("checks inputs", { expect_snapshot(error = TRUE, { fct_lump_n(1:3) fct_lump_n(factor(), n = "x") }) }) # fct_lump_prop ----------------------------------------------------------- test_that("positive/negative prop keeps/drops most commmon", { f <- c("a", "a", "a", "b", "b", "c", "d", "e", "f", "g") expect_equal(levels(fct_lump_prop(f, prop = 0.25)), c("a", "Other")) expect_equal(levels(fct_lump_prop(f, prop = 0.15)), c("a", "b", "Other")) f <- c("a", "a", "a", "a", "b", "b", "b", "b", "c", "d") expect_equal(levels(fct_lump(f, n = -1)), c("c", "d", "Other")) expect_equal(levels(fct_lump(f, prop = -0.2)), c("c", "d", "Other")) }) test_that("can use weights", { f <- c("a", "b", "c", "d", "e") w <- c( 0.2, 2, 6, 4, 1) expect_equal(levels(fct_lump_prop(f, prop = 0.3, w = w)), c("c", "d", "Other")) expect_equal(levels(fct_lump_prop(f, prop = 0.2, w = w)), c("c", "d", "Other")) }) test_that("can use weights with empty levels", { f <- factor(c("a", "a", "b", "c"), levels = c("a", "b", "c", "d")) expect_equal( fct_lump_prop(f, prop = 0.25, w = rep(1, 4)), fct(c("a", "a", "Other", "Other")) ) }) test_that("NAs included in total", { f <- factor(c("a", "a", "b", "c", rep(NA, 7))) o1 <- fct_lump_prop(f, prop = 0.10) expect_equal(levels(o1), c("a", "Other")) o2 <- fct_lump_prop(f, w = rep(1, 11), prop = 0.10) expect_equal(levels(o2), c("a", "Other")) }) test_that("idempotent if element satisfy n condition", { f <- c("a", "a", "a", "b", "b", "c", "d", "e", "f", "g") expect_equal(levels(fct_lump_prop(f, prop = 0.01)), c("a", "b", "c", "d", "e", "f", "g")) expect_equal(levels(fct_lump_prop(f, prop = -1)), c("a", "b", "c", "d", "e", "f", "g")) }) test_that("checks inputs", { expect_snapshot(error = TRUE, { fct_lump_prop(1:3) fct_lump_prop(factor(), prop = "x") }) }) # fct_lump_lowfreq() ----------------------------------------------------------- test_that("only have one small other level", { f <- c("a", "a", "a", "a", "b", "b", "b", "c", "c", "d") expect_equal(levels(fct_lump(f)), c("a", "b", "c", "Other")) }) test_that("lumps smallest", { lump_test <- function(x) { paste(ifelse(in_smallest(x), "X", letters[seq_along(x)]), collapse = "") } # smallest expect_equal(lump_test(c(1, 2, 3, 6)), "Xbcd") expect_equal(lump_test(c(1, 2, 3, 7)), "XXXd") expect_equal(lump_test(c(1, 2, 3, 7, 13)), "XXXde") expect_equal(lump_test(c(1, 2, 3, 7, 14)), "XXXXe") # doesn't lump if none small enough expect_equal(lump_test(c(2, 2, 4)), "abc") # order doesn't matter expect_equal(lump_test(c(2, 2, 5)), "XXc") expect_equal(lump_test(c(2, 5, 2)), "XbX") expect_equal(lump_test(c(5, 2, 2)), "aXX") }) forcats/tests/testthat/test-anon.R0000644000176200001440000000062614360012612016747 0ustar liggesuserstest_that("new levels are padded numerics", { f1 <- factor(letters[1:10]) f2 <- fct_anon(f1) expect_equal(levels(f2), sprintf("%02d", 1:10)) }) test_that("prefix added to start of level", { f1 <- factor("x") f2 <- fct_anon(f1, prefix = "X") expect_equal(levels(f2), "X1") }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_anon(1) fct_anon("x", 1) }) }) forcats/tests/testthat/test-collapse.R0000644000176200001440000000371714360012612017622 0ustar liggesuserstest_that("can collapse multiple values", { f1 <- factor(letters[1:3]) f2 <- fct_collapse(f1, x = c("a", "b"), y = "c") expect_equal(f2, factor(c("x", "x", "y"))) }) test_that("empty dots yields unchanged factor", { f1 <- factor(letters[1:3]) f2 <- fct_collapse(f1) expect_identical(f1, f2) }) test_that("can collapse missing levels", { f1 <- factor(c("x", NA), exclude = NULL) f2 <- fct_collapse(f1, y = NA_character_) expect_equal(f2, factor(c("x", "y"))) }) test_that("can collapse un-named levels", { f <- factor(letters[1:3]) expect_equal( fct_collapse(f, xy = c("a", "b"), other_level = "Other"), fct(c("xy", "xy", "Other"), levels = c("xy", "Other")) ) expect_equal( fct_collapse(f, xy = c("a", "b"), other_level = NA), fct(c("xy", "xy", NA), levels = c("xy", NA)) ) }) test_that("collapses levels correctly when group_other is TRUE but no other variables to group", { f1 <- factor(letters[1:4]) f2 <- fct_collapse(f1, x1 = c("a", "b", "d"), x2 = "c", other_level = "Other") expect_equal(f2, factor(c("x1", "x1", "x2", "x1"), levels = c("x1", "x2"))) }) test_that("collapses levels correctly when group_other is TRUE and some Other variables to group", { f1 <- factor(letters[1:4]) f2 <- fct_collapse(f1, x1 = c("a", "d"), x2 = "c", other_level = "Other") expect_equal(f2, factor(c("x1", "Other", "x2", "x1"), levels = c("x1", "x2", "Other"))) }) test_that("does not automatically collapse unnamed levels to Other", { f1 <- factor(letters[1:3]) f2 <- fct_collapse(f1, xy = c("a", "b")) expect_equal(f2, factor(c("xy", "xy", "c"), levels = c("xy", "c"))) }) test_that("group_other is deprecated", { f1 <- factor(letters[1:4]) expect_snapshot( f2 <- fct_collapse(f1, x1 = c("a", "d"), x2 = "c", group_other = TRUE) ) expect_equal(levels(f2), c("x1", "x2", "Other")) }) test_that("valdiates inputs", { expect_snapshot(error = TRUE, { fct_collapse(1) fct_collapse("x", other_level = 1) }) }) forcats/tests/testthat/test-drop.R0000644000176200001440000000133014360012612016751 0ustar liggesuserstest_that("doesn't add NA level", { f1 <- factor(c("a", NA), levels = c("a", "b")) f2 <- fct_drop(f1) expect_equal(levels(f2), "a") }) test_that("can optionally restrict levels to drop", { f1 <- factor("a", levels = c("a", "b", "c")) expect_equal(levels(fct_drop(f1, only = "b")), c("a", "c")) expect_equal(levels(fct_drop(f1, only = "a")), c("a", "b", "c")) }) test_that("preserves ordered class and attributes", { f1 <- ordered(letters[1:2], letters[1:3]) attr(f1, "x") <- "test" f2 <- fct_drop(f1) expect_s3_class(f2, "ordered") expect_equal(attr(f2, "x"), attr(f1, "x")) }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_drop(1) fct_drop("x", only = 1) }) }) forcats/tests/testthat/test-relabel.R0000644000176200001440000000167314241066126017434 0ustar liggesuserstest_that("identity", { f1 <- factor(c("a", "b")) expect_identical(fct_relabel(f1, identity), f1) }) test_that("gives useful errors", { f1 <- factor("a") expect_snapshot(error = TRUE, { fct_relabel(f1, 1) fct_relabel(f1, function(x) 1) fct_relabel(f1, function(x) x[-1]) }) }) test_that("total collapse", { f1 <- factor(letters) new_levels <- function(x) rep("1", length(x)) expect_identical(fct_relabel(f1, new_levels), factor(new_levels(letters))) }) test_that("additional arguments", { f1 <- factor(letters) expect_identical(fct_relabel(f1, paste0, "."), factor(paste0(letters, "."))) }) test_that("formulas are coerced to functions", { f1 <- factor(letters) expect_identical( fct_relabel(f1, ~ paste0(.x, ".")), factor(paste0(letters, ".")) ) }) test_that("string input is coerced to a factor", { expect_identical( fct_relabel(LETTERS[1:2], function(x) x), factor(LETTERS[1:2]) ) }) forcats/tests/testthat/test-count.R0000644000176200001440000000211214360012612017134 0ustar liggesuserstest_that("0 count for empty levels", { f <- factor(levels = c("a", "b")) expect_equal(fct_count(f)$n, c(0, 0)) f <- factor("a", levels = c("a", "b", "c")) expect_equal(fct_count(f)$n, c(1, 0, 0)) }) test_that("counts NA values and levels", { f1 <- factor(c("a", "a", NA)) expect_equal( fct_count(f1), tibble::tibble( f = fct(c("a", NA)), n = c(2, 1) ) ) f2 <- factor(c("a", "a", NA), exclude = NULL) expect_equal( fct_count(f2), tibble::tibble( f = fct(c("a", NA), c("a", NA)), n = c(2, 1) ) ) }) test_that("returns marginal table", { f <- factor(c("a", "a", "b")) out <- fct_count(f, prop = TRUE) expect_equal(out$n, c(2, 1)) expect_equal(out$p, c(2, 1) / 3) }) test_that("sort = TRUE brings most frequent values to top", { f <- factor(c("a", "b", "b")) out <- fct_count(f, sort = TRUE) expect_equal(out$f, factor(c("b", "a"), levels = c("a", "b"))) }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_count(1) fct_count("x", sort = 1) fct_count("x", prop = 1) }) }) forcats/tests/testthat/test-c.R0000644000176200001440000000100014360012612016221 0ustar liggesuserstest_that("uses tidy_dots", { fs <- list(factor("a"), factor("b")) fab <- factor(c("a", "b")) expect_equal(fct_c(!!!fs), fab) expect_equal(fct_c(fs[[1]], fs[[2]]), fab) }) test_that("all inputs must be factors", { expect_error(fct_c("a"), "must be factors") }) test_that("empty input yields empty factor", { expect_equal(fct_c(), factor()) }) test_that("validates inputs", { expect_snapshot(error = TRUE, { fct_c(1) fct_unify(list(1)) fct_unify(list(factor()), levels = 1) }) }) forcats/tests/testthat/test-other.R0000644000176200001440000000200414360012612017125 0ustar liggesuserstest_that("keeps levels in keep", { x1 <- factor(c("a", "b")) x2 <- fct_other(x1, keep = "a") expect_equal(levels(x2), c("a", "Other")) }) test_that("drops levels in drop", { x1 <- factor(c("a", "b")) x2 <- fct_other(x1, drop = "a") # other always placed at end expect_equal(levels(x2), c("b", "Other")) }) test_that("works without warning if no levels replaced", { x <- factor("a") expect_no_warning(fct_other(x, keep = "a")) expect_no_warning(fct_other(x, drop = "b")) }) test_that("can use NA as other level", { f <- fct(c("a", "b")) expect_equal( fct_other(f, keep = "a", other_level = NA), fct(c("a", NA), levels = c("a", NA)) ) expect_equal( levels(lvls_other(f, c(TRUE, FALSE), other_level = NA)), c("a", NA) ) }) test_that("validates its inputs", { f <- factor(c("a", "b")) expect_snapshot(error = TRUE, { fct_other(1) fct_other(f) fct_other(f, keep = "a", drop = "a") fct_other(f, keep = 1) fct_other(f, keep = "a", other_level = 1) }) }) forcats/tests/testthat/test-reorder.R0000644000176200001440000001436414360012623017464 0ustar liggesusers# fct_reorder ------------------------------------------------------------- test_that("can reorder can control ordering", { f <- c("a", "a", "b", "b", "b") x <- c(3, 3, 2, 2, 1) f1 <- fct_reorder(f, x) expect_equal(levels(f1), c("b", "a")) f2 <- fct_reorder(f, x, .desc = TRUE) expect_equal(levels(f2), c("a", "b")) }) test_that("automatically removes missing values with a warning", { f1 <- fct(c("a", "b", "c", "c")) x <- c(3, 2, 1, NA) expect_snapshot(f2 <- fct_reorder(f1, x)) expect_equal(levels(f2), c("c", "b", "a")) expect_no_warning(fct_reorder(f1, x, .na_rm = TRUE)) expect_no_warning(f3 <- fct_reorder(f1, x, .na_rm = FALSE)) expect_equal(levels(f3), c("b", "a", "c")) }) test_that("can control the placement of empty levels", { f1 <- fct(c("a", "b", "c"), letters[1:4]) x <- c(1, 2, 3) f2 <- fct_reorder(f1, x, .default = -Inf) expect_equal(levels(f2), c("d", "a", "b", "c")) }) test_that("can control the placement of levels with all missing data", { f1 <- fct(c("a", "b", "c")) x <- c(1, 2, NA) f2 <- fct_reorder(f1, x, .na_rm = TRUE, .default = -Inf) expect_equal(levels(f2), c("c", "a", "b")) }) test_that("fct_reorder() complains if summary doesn't return single value", { expect_snapshot(error = TRUE, { fct_reorder("a", 1, function(x) c(1, 2)) }) }) test_that("fct_reorder() validates its inputs", { expect_snapshot(error = TRUE, { fct_reorder(1) fct_reorder("x", 1, 1) fct_reorder("x", 1, .na_rm = 1) fct_reorder("x", 1, .desc = 1) }) }) # fct_reorder2 ------------------------------------------------------------ test_that("can reorder by 2d summary", { df <- tibble::tribble( ~g, ~x, ~y, "a", 1, 10, "a", 2, 5, "b", 1, 5, "b", 2, 10 ) f1 <- fct_reorder2(df$g, df$x, df$y) expect_equal(levels(f1), c("b", "a")) f2 <- fct_reorder(df$g, df$x, .desc = TRUE) expect_equal(levels(f2), c("a", "b")) }) test_that("fct_reorder2() automatically removes missing values with a warning", { f1 <- fct(c("a", "b", "c", "c")) x <- c(1, 1, 1, 2) y <- c(1, 2, 3, NA) expect_snapshot(f2 <- fct_reorder2(f1, x, y)) expect_equal(levels(f2), c("c", "b", "a")) expect_no_warning(fct_reorder2(f1, x, y, .na_rm = TRUE)) # unlike fct_reorder() the default summary function can handle missing values expect_no_warning(f3 <- fct_reorder2(f1, x, y, .na_rm = FALSE)) expect_equal(levels(f2), c("c", "b", "a")) }) test_that("can control the placement of empty levels", { f1 <- fct(c("a", "b", "c"), letters[1:4]) x <- c(1, 2, 3) f2 <- fct_reorder(f1, x, .default = -Inf) expect_equal(levels(f2), c("d", "a", "b", "c")) }) test_that("missing groups appear at end by default", { df <- tibble::tribble( ~g, ~x, ~y, "a", NA, NA, "b", 1, 10, ) f1 <- fct_reorder2(df$g, df$x, df$y, .na_rm = TRUE) expect_equal(levels(f1), c("b", "a")) f2 <- fct_reorder2(df$g, df$x, df$y, .default = Inf, .na_rm = TRUE) expect_equal(levels(f2), c("a", "b")) }) test_that("fct_reorder2() complains if summary doesn't return single value", { expect_snapshot(error = TRUE, { fct_reorder2("a", 1, 1, function(x, y) c(1, 2)) }) }) test_that("first2/last2 return expected values", { expect_equal(first2(4:1, 1:4), 4) expect_equal(last2(4:1, 1:4), 1) }) test_that("last2 ignores points where either value is missing", { expect_equal(last2(1:4, c(1:3, NA)), 3) expect_equal(last2(c(1:3, NA), 1:4), 3) }) test_that("last2 returns NA if no non-missing pairs", { expect_equal(last2(c(NA, 1), c(1, NA)), NA_real_) expect_equal(last2(c(NA, 1), c("x", NA)), NA_character_) }) # fct_infreq -------------------------------------------------------------- test_that("fct_infreq() preserves explicit NA", { x <- c("a", "b", "b", NA, NA, NA) f <- factor(x, exclude = FALSE) expect_equal(fct_infreq(f), fct(x, c(NA, "b", "a"))) }) test_that("fct_infreq() ignores implict NA", { x <- c("a", "b", "b", NA, NA, NA) f <- factor(x) expect_equal(fct_infreq(f), fct(x, c("b", "a"))) }) test_that("fct_infreq() respects weights", { x <- c("a", "b", "b", "c") f <- factor(x) w <- c(1, 1, 3, 3) expect_equal(fct_infreq(f, w), fct(x, c("b", "c", "a"))) }) test_that("fct_infreq() validates its inputs", { f <- fct(c("a", "b", "c")) expect_snapshot(error = TRUE, { fct_infreq(1) fct_infreq(f, 1:4) fct_infreq(f, "x") fct_infreq(f, ordered = 1) }) }) test_that("fct_infreq() preserves empty levels", { # at end x <- c("b", "b", "a") f <- fct(x, letters[1:3]) expect_equal(fct_infreq(f), fct(x, c("b", "a", "c"))) # at beginning x <- c("b", "b", "c") f <- fct(x, letters[1:3]) expect_equal(fct_infreq(f), fct(x, c("b", "c", "a"))) }) # fct_inorder ------------------------------------------------------------- test_that("fct_inorder() preserves explicit NA", { x <- c(NA, "a", "b", "b", NA, NA) f <- factor(x, exclude = FALSE) expect_equal(fct_inorder(f), fct(x, c(NA, "a", "b"))) }) test_that("fct_inorder() ignores implict NA", { x <- c(NA, "a", "b", "b", NA, NA) f <- factor(x) expect_equal(fct_inorder(f), fct(x, c("a", "b"))) }) test_that("fct_inorder() preserves empty levels", { # at beginning x <- c("b", "b", "a") f <- fct(x, letters[1:3]) expect_equal(fct_inorder(f), fct(x, c("b", "a", "c"))) # at end x <- c("b", "b", "c") f <- fct(x, letters[1:3]) expect_equal(fct_inorder(f), fct(x, c("b", "c", "a"))) }) test_that("fct_inorder() validates its inputs", { f <- fct(c("a", "b", "c")) expect_snapshot(error = TRUE, { fct_inorder(1) fct_inorder(f, 1:4) fct_inorder(f, "x") fct_inorder(f, ordered = 1) }) }) # fct_inseq --------------------------------------------------------------- test_that("fct_inseq sorts in numeric order", { x <- c("1", "2", "3") expect_equal( fct_inseq(factor(x, levels = c("3", "1", "2"))), factor(x, levels = c("1", "2", "3")) ) # non-numeric go to end x <- c("1", "2", "3", "a") expect_equal( fct_inseq(factor(x, levels = c("a", "3", "1", "2"))), factor(x, levels = c("1", "2", "3", "a")) ) }) test_that("fct_inseq gives error for non-numeric levels", { expect_snapshot(error = TRUE, { fct_inseq("x") }) }) test_that("fct_inorder() validates its inputs", { expect_snapshot(error = TRUE, { fct_inseq(1) fct_inseq("1", ordered = 1) }) }) forcats/tests/testthat/test-shift.R0000644000176200001440000000074114360012612017127 0ustar liggesuserstest_that("can shift in either direction", { f1 <- factor(c("a", "b", "c")) f2_l <- fct_shift(f1, 1) expect_equal(levels(f2_l), c("b", "c", "a")) f2_r <- fct_shift(f1, -1) expect_equal(levels(f2_r), c("c", "a", "b")) }) test_that("0 shift leaves unchanged", { f1 <- factor(c("a", "b", "c")) f2 <- fct_shift(f1, 0) expect_identical(f1, f2) }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_shift(1) fct_shift("x", NA) }) }) forcats/tests/testthat/test-cross.R0000644000176200001440000000434614360012612017150 0ustar liggesuserstest_that("empty input returns empty factor", { expect_equal(fct_cross(), factor()) }) test_that("gives correct levels", { fruit <- as_factor(c("apple", "kiwi", "apple", "apple")) colour <- as_factor(c("green", "green", "red", "green")) f2 <- fct_cross(fruit, colour) expect_setequal(levels(f2), c("apple:green", "kiwi:green", "apple:red")) }) test_that("recycle inputs", { expect_length(fct_cross("a", c("a", "b", "c"), "d"), 3) expect_error(fct_cross(c("a", "b", "c"), c("a", "b")), "recycle", class = "error") }) test_that("keeps empty levels when requested", { fruit <- as_factor(c("apple", "kiwi", "apple", "apple")) colour <- as_factor(c("green", "green", "red", "green")) f2 <- fct_cross(fruit, colour, keep_empty = TRUE) expect_setequal(levels(f2), c("apple:green", "kiwi:green", "apple:red", "kiwi:red")) }) test_that("order of levels is preserved", { fruit <- as_factor(c("apple", "kiwi", "apple", "apple")) colour <- as_factor(c("green", "green", "red", "green")) fruit <- fct_relevel(fruit, c("kiwi", "apple")) colour <- fct_relevel(colour, c("red", "green")) f2 <- fct_cross(fruit, colour) expect_setequal(levels(f2), c("kiwi:green", "apple:red", "apple:green")) }) test_that("gives NA output on NA input", { fruit <- as_factor(c("apple", "kiwi", "apple", "apple")) colour <- as_factor(c("green", "green", "red", "green")) fruit[1] <- NA f2 <- fct_cross(fruit, colour) expect_true(is.na(f2[1])) }) test_that("gives NA output on NA input, when keeping empty levels", { fruit <- as_factor(c("apple", "kiwi", "apple", "apple")) colour <- as_factor(c("green", "green", "red", "green")) fruit[1] <- NA f2 <- fct_cross(fruit, colour, keep_empty = TRUE) expect_true(is.na(f2[1])) }) test_that("can combine more than two factors", { fruit <- as_factor(c("apple", "kiwi", "apple", "apple")) colour <- as_factor(c("green", "green", "red", "green")) eaten <- c("yes", "no", "yes", "no") f2 <- fct_cross(fruit, colour, eaten) expect_setequal(levels(f2), c("apple:green:no", "apple:green:yes", "apple:red:yes", "kiwi:green:no")) }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_cross(x = "x") fct_cross("x", sep = 1) fct_cross("x", keep_empty = 1) }) }) forcats/tests/testthat/test-recode.R0000644000176200001440000000200214241066126017252 0ustar liggesuserstest_that("warns about unknown levels", { f1 <- factor(c("a", "b")) expect_warning(f2 <- fct_recode(f1, d = "e"), "Unknown levels") expect_equal(levels(f2), levels(f1)) }) test_that("can collapse levels", { f1 <- factor(c("a1", "a2", "b1", "b2")) f2 <- factor(c("a", "a", "b", "b")) expect_equal(fct_recode(f1, a = "a1", a = "a2", b = "b1", b = "b2"), f2) }) test_that("can recode multiple levels to NA", { f1 <- factor(c("a1", "empty", "a2", "b", "missing")) f2 <- factor(c("a", NA, "a", "b", NA)) expect_equal(fct_recode(f1, NULL = "missing", NULL = "empty", a = "a1", a = "a2"), f2) }) test_that("can just remove levels", { f1 <- factor(c("a", "missing")) f2 <- factor(c("a", NA)) expect_equal(fct_recode(f1, NULL = "missing"), f2) }) # check_recode_levels ----------------------------------------------------- test_that("new levels must be character", { f <- factor(c("a", "b")) expect_snapshot(error = TRUE, { fct_recode(f, "a") fct_recode(f, x = 1, y = c("a", "b")) }) }) forcats/tests/testthat/test-explicit_na.R0000644000176200001440000000133114357073063020322 0ustar liggesuserstest_that("factor unchanged if no missing levels", { withr::local_options(lifecycle_verbosity = "quiet") f1 <- factor(letters[1:3]) f2 <- fct_explicit_na(f1) expect_identical(f1, f2) }) test_that("converts implicit NA", { withr::local_options(lifecycle_verbosity = "quiet") f1 <- factor(c("a", NA)) f2 <- fct_explicit_na(f1) expect_equal(f2, fct_inorder(c("a", "(Missing)"))) }) test_that("converts explicit NA", { withr::local_options(lifecycle_verbosity = "quiet") f1 <- factor(c("a", NA), exclude = NULL) f2 <- fct_explicit_na(f1) expect_equal(f2, fct_inorder(c("a", "(Missing)"))) }) test_that("fct_explicit_na is deprecated", { expect_snapshot({ . <- fct_explicit_na(factor()) }) }) forcats/tests/testthat/test-unique.R0000644000176200001440000000124214355354616017336 0ustar liggesuserstest_that("includes implicit missing values", { x <- factor(c(NA, "x")) expect_equal(fct_unique(x), fct(c("x", NA))) }) test_that("preserve position of explicit NA levels", { x <- fct(c(NA, "x"), levels = c(NA, "x")) expect_equal(fct_unique(x), x) }) test_that("preserve mix of implicit and explicit NA (#328)", { # Possible to create with factor(exclude = NULL) + is.na(), but directly # specifying makes it a little easier to see what's happening. f <- structure( c(2L, 1L, NA, 1L, 2L, NA), levels = c("x", NA), class = "factor" ) expect_equal( fct_unique(f), structure(c(1L, 2L, NA), levels = c("x", NA), class = "factor") ) }) forcats/tests/testthat/test-expand.R0000644000176200001440000000073714360012612017276 0ustar liggesuserstest_that("can add levels at any location", { x <- c("a", "b", "c") expect_equal(fct_expand(x, "d"), fct(x, c("a", "b", "c", "d"))) expect_equal(fct_expand(x, "d", after = 0), fct(x, c("d", "a", "b", "c"))) }) test_that("doesn't repeat existing levels", { x <- c("a", "b", "c") expect_equal(fct_expand(x, "a", "b"), fct(x)) }) test_that("validates its inputs", { expect_snapshot(error = TRUE, { fct_expand("x", d = "d") fct_expand("x", after = "x") }) }) forcats/tests/testthat/_snaps/0000755000176200001440000000000014360020241016170 5ustar liggesusersforcats/tests/testthat/_snaps/cross.md0000644000176200001440000000077614360012612017660 0ustar liggesusers# validates its inputs Code fct_cross(x = "x") Condition Error in `fct_cross()`: ! Arguments in `...` must be passed by position, not name. x Problematic argument: * x = "x" Code fct_cross("x", sep = 1) Condition Error in `fct_cross()`: ! `sep` must be a single string, not the number 1. Code fct_cross("x", keep_empty = 1) Condition Error in `fct_cross()`: ! `keep_empty` must be `TRUE` or `FALSE`, not the number 1. forcats/tests/testthat/_snaps/na.md0000644000176200001440000000130614360012612017113 0ustar liggesusers# checks input types Code fct_na_value_to_level(1) Condition Error in `fct_na_value_to_level()`: ! `f` must be a factor or character vector, not a number. Code fct_na_value_to_level(f, level = 1) Condition Error in `fct_na_value_to_level()`: ! `level` must be a single string or `NA`, not the number 1. Code fct_na_level_to_value(1) Condition Error in `fct_na_level_to_value()`: ! `f` must be a factor or character vector, not a number. Code fct_na_level_to_value(f, extra_levels = 1) Condition Error in `fct_na_level_to_value()`: ! `extra_levels` must be a character vector or `NULL`, not the number 1. forcats/tests/testthat/_snaps/explicit_na.md0000644000176200001440000000033614360012553021022 0ustar liggesusers# fct_explicit_na is deprecated Code . <- fct_explicit_na(factor()) Condition Warning: `fct_explicit_na()` was deprecated in forcats 1.0.0. i Please use `fct_na_value_to_level()` instead. forcats/tests/testthat/_snaps/other.md0000644000176200001440000000136214360012612017640 0ustar liggesusers# validates its inputs Code fct_other(1) Condition Error in `fct_other()`: ! `f` must be a factor or character vector, not a number. Code fct_other(f) Condition Error in `fct_other()`: ! One of `keep` or `drop` must be supplied. Code fct_other(f, keep = "a", drop = "a") Condition Error in `fct_other()`: ! Exactly one of `keep` or `drop` must be supplied. Code fct_other(f, keep = 1) Condition Error in `fct_other()`: ! `keep` must be a character vector, not the number 1. Code fct_other(f, keep = "a", other_level = 1) Condition Error in `fct_other()`: ! `other_level` must be a single string or `NA`, not the number 1. forcats/tests/testthat/_snaps/count.md0000644000176200001440000000070114360012612017643 0ustar liggesusers# validates its inputs Code fct_count(1) Condition Error in `fct_count()`: ! `f` must be a factor or character vector, not a number. Code fct_count("x", sort = 1) Condition Error in `fct_count()`: ! `sort` must be `TRUE` or `FALSE`, not the number 1. Code fct_count("x", prop = 1) Condition Error in `fct_count()`: ! `prop` must be `TRUE` or `FALSE`, not the number 1. forcats/tests/testthat/_snaps/expand.md0000644000176200001440000000055414360012612020000 0ustar liggesusers# validates its inputs Code fct_expand("x", d = "d") Condition Error in `fct_expand()`: ! Arguments in `...` must be passed by position, not name. x Problematic argument: * d = "d" Code fct_expand("x", after = "x") Condition Error in `fct_expand()`: ! `after` must be a number, not the string "x". forcats/tests/testthat/_snaps/lump.md0000644000176200001440000000320014360020236017466 0ustar liggesusers# bad weights generates friendly error messages Code fct_lump(letters, w = letters) Condition Error in `fct_lump_lowfreq()`: ! `w` must be a numeric vector, not a string. Code fct_lump(letters, w = 1:10) Condition Error in `fct_lump_lowfreq()`: ! `w` must be the same length as `f` (26), not length 10. Code fct_lump(letters, w = c(-1, rep(1, 24), -1)) Condition Error in `fct_lump_lowfreq()`: ! All `w` must be non-negative and non-missing. 2 problems at positions 1 and 26. # can only supply one of n and prop Code fct_lump(f, n = 1, prop = 0.1) Condition Error in `fct_lump()`: ! Must supply only one of `n` and `prop`. # checks inputs Code fct_lump_min(1:3) Condition Error in `fct_lump_min()`: ! `f` must be a factor or character vector, not an integer vector. Code fct_lump_min(factor(), min = "x") Condition Error in `fct_lump_min()`: ! `min` must be a number, not the string "x". --- Code fct_lump_n(1:3) Condition Error in `fct_lump_n()`: ! `f` must be a factor or character vector, not an integer vector. Code fct_lump_n(factor(), n = "x") Condition Error in `fct_lump_n()`: ! `n` must be a number, not the string "x". --- Code fct_lump_prop(1:3) Condition Error in `fct_lump_prop()`: ! `f` must be a factor or character vector, not an integer vector. Code fct_lump_prop(factor(), prop = "x") Condition Error in `fct_lump_prop()`: ! `prop` must be a number, not the string "x". forcats/tests/testthat/_snaps/reorder.md0000644000176200001440000000647714360012623020177 0ustar liggesusers# automatically removes missing values with a warning Code f2 <- fct_reorder(f1, x) Condition Warning: `fct_reorder()` removing 1 missing value. i Use `.na_rm = TRUE` to silence this message. i Use `.na_rm = FALSE` to preserve NAs. # fct_reorder() complains if summary doesn't return single value Code fct_reorder("a", 1, function(x) c(1, 2)) Condition Error in `fct_reorder()`: ! `.fun` must return a single value per group # fct_reorder() validates its inputs Code fct_reorder(1) Condition Error in `fct_reorder()`: ! `.f` must be a factor or character vector, not a number. Code fct_reorder("x", 1, 1) Condition Error in `fct_reorder()`: ! Can't convert `.fun`, a number, to a function. Code fct_reorder("x", 1, .na_rm = 1) Condition Error in `fct_reorder()`: ! `.na_rm` must be `TRUE`, `FALSE`, or `NULL`, not the number 1. Code fct_reorder("x", 1, .desc = 1) Condition Error in `fct_reorder()`: ! `.desc` must be `TRUE` or `FALSE`, not the number 1. # fct_reorder2() automatically removes missing values with a warning Code f2 <- fct_reorder2(f1, x, y) Condition Warning: `fct_reorder2()` removing 1 missing value. i Use `.na_rm = TRUE` to silence this message. i Use `.na_rm = FALSE` to preserve NAs. # fct_reorder2() complains if summary doesn't return single value Code fct_reorder2("a", 1, 1, function(x, y) c(1, 2)) Condition Error in `fct_reorder2()`: ! `.fun` must return a single value per group # fct_infreq() validates its inputs Code fct_infreq(1) Condition Error in `fct_infreq()`: ! `f` must be a factor or character vector, not a number. Code fct_infreq(f, 1:4) Condition Error in `fct_infreq()`: ! `w` must be the same length as `f` (3), not length 4. Code fct_infreq(f, "x") Condition Error in `fct_infreq()`: ! `w` must be a numeric vector, not a string. Code fct_infreq(f, ordered = 1) Condition Error in `fct_infreq()`: ! `ordered` must be `TRUE`, `FALSE`, or `NA`, not the number 1. # fct_inorder() validates its inputs Code fct_inorder(1) Condition Error in `fct_inorder()`: ! `f` must be a factor or character vector, not a number. Code fct_inorder(f, 1:4) Condition Error in `fct_inorder()`: ! `ordered` must be `TRUE`, `FALSE`, or `NA`, not an integer vector. Code fct_inorder(f, "x") Condition Error in `fct_inorder()`: ! `ordered` must be `TRUE`, `FALSE`, or `NA`, not the string "x". Code fct_inorder(f, ordered = 1) Condition Error in `fct_inorder()`: ! `ordered` must be `TRUE`, `FALSE`, or `NA`, not the number 1. --- Code fct_inseq(1) Condition Error in `fct_inseq()`: ! `f` must be a factor or character vector, not a number. Code fct_inseq("1", ordered = 1) Condition Error in `fct_inseq()`: ! `ordered` must be `TRUE`, `FALSE`, or `NA`, not the number 1. # fct_inseq gives error for non-numeric levels Code fct_inseq("x") Condition Error in `fct_inseq()`: ! At least one existing level must be coercible to numeric. forcats/tests/testthat/_snaps/c.md0000644000176200001440000000064014360012612016737 0ustar liggesusers# validates inputs Code fct_c(1) Condition Error in `fct_c()`: ! All elements of `...` must be factors. Code fct_unify(list(1)) Condition Error in `fct_unify()`: ! All elements of `fs` must be factors. Code fct_unify(list(factor()), levels = 1) Condition Error in `fct_unify()`: ! `levels` must be a character vector, not the number 1. forcats/tests/testthat/_snaps/anon.md0000644000176200001440000000044614360012612017454 0ustar liggesusers# validates its inputs Code fct_anon(1) Condition Error in `fct_anon()`: ! `f` must be a factor or character vector, not a number. Code fct_anon("x", 1) Condition Error in `fct_anon()`: ! `prefix` must be a single string, not the number 1. forcats/tests/testthat/_snaps/relabel.md0000644000176200001440000000074514360012612020131 0ustar liggesusers# gives useful errors Code fct_relabel(f1, 1) Condition Error in `fct_relabel()`: ! Can't convert `.fun`, a number, to a function. Code fct_relabel(f1, function(x) 1) Condition Error in `lvls_revalue()`: ! `new_levels` must be a character vector, not the number 1. Code fct_relabel(f1, function(x) x[-1]) Condition Error in `lvls_revalue()`: ! `new_levels` must be the same length (0) as `levels(f)` (1). forcats/tests/testthat/_snaps/collapse.md0000644000176200001440000000116714360012612020324 0ustar liggesusers# group_other is deprecated Code f2 <- fct_collapse(f1, x1 = c("a", "d"), x2 = "c", group_other = TRUE) Condition Warning: The `group_other` argument of `fct_collapse()` is deprecated as of forcats 0.5.0. i Please use the `other_level` argument instead. # valdiates inputs Code fct_collapse(1) Condition Error in `fct_collapse()`: ! `.f` must be a factor or character vector, not a number. Code fct_collapse("x", other_level = 1) Condition Error in `fct_collapse()`: ! `other_level` must be a single string, `NA`, or `NULL`, not the number 1. forcats/tests/testthat/_snaps/lvls.md0000644000176200001440000000107114360012612017474 0ustar liggesusers# must include all existing levels Code lvls_expand(f1, c("a", "c")) Condition Error in `lvls_expand()`: ! `new_levels` must include all levels in `f`. i Missing 1 level: b # `new_levels` must checks its inputs Code lvls_revalue(f1, 1:5) Condition Error in `lvls_revalue()`: ! `new_levels` must be a character vector, not an integer vector. Code lvls_revalue(f1, c("a", "b", "c")) Condition Error in `lvls_revalue()`: ! `new_levels` must be the same length (3) as `levels(f)` (2). forcats/tests/testthat/_snaps/relevel.md0000644000176200001440000000054314360012554020162 0ustar liggesusers# warns about unknown levels Code f2 <- fct_relevel(f1, "d") Condition Warning: 1 unknown level in `f`: d # dots must be unnamed Code fct_relevel(f, a = "b") Condition Error in `fct_relevel()`: ! Arguments in `...` must be passed by position, not name. x Problematic argument: * a = "b" forcats/tests/testthat/_snaps/fct.md0000644000176200001440000000120114360012612017263 0ustar liggesusers# checks input types Code fct(1:3) Condition Error in `fct()`: ! `x` must be a character vector, not an integer vector. Code fct("x", 1:3) Condition Error in `fct()`: ! `levels` must be a character vector or `NULL`, not an integer vector. Code fct("x", "y", na = 1) Condition Error in `fct()`: ! `na` must be a character vector, not the number 1. # clear error if levels are incomplete Code fct(c("x", "y", "z"), c("x", "y")) Condition Error in `fct()`: ! All values of `x` must appear in `levels` or `na` i Missing level: "z" forcats/tests/testthat/_snaps/recode.md0000644000176200001440000000061514360012554017765 0ustar liggesusers# new levels must be character Code fct_recode(f, "a") Condition Error in `fct_recode()`: ! Each element of `...` must be a named string. i Problems with 1 argument: 1 Code fct_recode(f, x = 1, y = c("a", "b")) Condition Error in `fct_recode()`: ! Each element of `...` must be a named string. i Problems with 2 arguments: x and y forcats/tests/testthat/_snaps/drop.md0000644000176200001440000000047014360012612017462 0ustar liggesusers# validates its inputs Code fct_drop(1) Condition Error in `fct_drop()`: ! `f` must be a factor or character vector, not a number. Code fct_drop("x", only = 1) Condition Error in `fct_drop()`: ! `only` must be a character vector or `NULL`, not the number 1. forcats/tests/testthat/_snaps/match.md0000644000176200001440000000074114360012612017613 0ustar liggesusers# error when levels are missing Code fct_match(f, "d") Condition Error in `fct_match()`: ! All `lvls` must be present in `f`. i Missing levels: "d" # validates its inputs Code fct_match(1) Condition Error in `fct_match()`: ! `f` must be a factor or character vector, not a number. Code fct_match("x", 1) Condition Error in `fct_match()`: ! `lvls` must be a character vector, not the number 1. forcats/tests/testthat/_snaps/utils.md0000644000176200001440000000065014360012555017664 0ustar liggesusers# check_factor() fails when needed Code check_factor(NA) Condition Error: ! `NA` must be a factor or character vector, not `NA`. # check_factor_list() checks its inputs Code check_factor_list(1) Condition Error: ! `1` must be a list, not a number. Code check_factor_list(list(1)) Condition Error: ! All elements of `list(1)` must be factors. forcats/tests/testthat/_snaps/shift.md0000644000176200001440000000043514360012612017634 0ustar liggesusers# validates its inputs Code fct_shift(1) Condition Error in `fct_shift()`: ! `f` must be a factor or character vector, not a number. Code fct_shift("x", NA) Condition Error in `fct_shift()`: ! `n` must be a whole number, not `NA`. forcats/tests/testthat/test-utils.R0000644000176200001440000000055614241066126017165 0ustar liggesuserstest_that("check_factor() fails when needed", { expect_equal(check_factor("x"), factor("x")) expect_equal(check_factor(factor("x")), factor("x")) expect_snapshot(error = TRUE, { check_factor(NA) }) }) test_that("check_factor_list() checks its inputs", { expect_snapshot(error = TRUE, { check_factor_list(1) check_factor_list(list(1)) }) }) forcats/tests/testthat/test-shuffle.R0000644000176200001440000000023214241066126017450 0ustar liggesuserstest_that("reproducibility shuffles", { set.seed(1014) f1 <- factor(c("a", "b")) f2 <- fct_shuffle(f1) expect_equal(levels(f2), c("a", "b")) }) forcats/tests/testthat/test-rev.R0000644000176200001440000000020014241066126016603 0ustar liggesuserstest_that("reverses levels", { f1 <- factor(c("a", "b", "a")) f2 <- fct_rev(f1) expect_equal(levels(f2), c("b", "a")) }) forcats/tests/testthat/test-as_factor.R0000644000176200001440000000070314241066126017760 0ustar liggesuserstest_that("equivalent to fct_inorder", { x <- c("a", "z", "g") expect_equal(as_factor(x), fct_inorder(x)) }) test_that("leaves factors as is", { f1 <- factor(letters) f2 <- as_factor(f1) expect_identical(f1, f2) }) test_that("logical has fixed levels", { f <- as_factor(FALSE) expect_equal(levels(f), c("FALSE", "TRUE")) }) test_that("supports NA (#89)", { x <- c("a", "z", "g", NA) expect_equal(as_factor(x), fct_inorder(x)) }) forcats/tests/testthat/test-na.R0000644000176200001440000000162314357073063016425 0ustar liggesuserstest_that("can turn a NA value into a NA level", { x <- c("a", "b", NA) f <- fct(x) expect_equal(fct_na_value_to_level(f), fct(x, x)) }) test_that("can turn a NA value into a custom level", { x <- c("a", "b", NA) f <- fct(x) expect_equal( fct_na_value_to_level(f, "MISSING"), fct(c("a", "b", "MISSING")) ) }) test_that("can turn a NA level into an NA value", { x <- c("a", "b", NA) f <- fct(x, x) expect_equal(fct_na_level_to_value(f), fct(x)) }) test_that("can turn custom levels into an NA value", { x <- c("a", "b", NA) f <- fct(x, x) expect_equal( fct_na_level_to_value(f, extra_levels = "a"), fct(c(NA, "b", NA), "b") ) }) test_that("checks input types", { f <- fct("a") expect_snapshot(error = TRUE, { fct_na_value_to_level(1) fct_na_value_to_level(f, level = 1) fct_na_level_to_value(1) fct_na_level_to_value(f, extra_levels = 1) }) }) forcats/tests/testthat/test-lvls.R0000644000176200001440000000442014355410031016771 0ustar liggesusers# lvls_expand ------------------------------------------------------------- test_that("changes levels, not values", { f1 <- factor(c("a")) f2 <- factor(c("a"), levels = c("a", "b")) expect_equal(lvls_expand(f1, c("a", "b")), f2) }) test_that("must include all existing levels", { f1 <- factor(c("a", "b")) expect_snapshot(error = TRUE, { lvls_expand(f1, c("a", "c")) }) }) # lvls_revalue ------------------------------------------------------------ test_that("changes values and levels", { f1 <- factor(c("a", "b")) f2 <- factor(c("b", "a"), levels = c("b", "a")) expect_equal(lvls_revalue(f1, c("b", "a")), f2) }) test_that("can collapse values", { f1 <- factor(c("a", "b")) f2 <- factor(c("a", "a")) expect_equal(lvls_revalue(f1, c("a", "a")), f2) }) test_that("preserves missing values", { f1 <- factor(c("a", NA), exclude = NULL) f2 <- lvls_revalue(f1, levels(f1)) expect_equal(levels(f2), levels(f1)) }) test_that("`new_levels` must checks its inputs", { f1 <- factor(c("a", "b")) expect_snapshot(error = TRUE, { lvls_revalue(f1, 1:5) lvls_revalue(f1, c("a", "b", "c")) }) }) # lvls_reorder ------------------------------------------------------------ test_that("changes levels, not values", { f1 <- factor(c("a", "b")) f2 <- factor(c("a", "b"), levels = c("b", "a")) expect_equal(lvls_reorder(f1, 2:1), f2) }) test_that("idx must be numeric", { f <- factor(c("a", "b")) expect_error(lvls_reorder(f, "a"), "must be numeric") }) test_that("must have one integer per level", { f <- factor(c("a", "b", "c")) expect_error(lvls_reorder(f, c(1, 2)), "one integer for each level") expect_error(lvls_reorder(f, c(1, 2, 2)), "one integer for each level") expect_error(lvls_reorder(f, c(1, 2.5)), "one integer for each level") }) test_that("can change ordered status of output", { f1 <- factor(letters[1:3]) f2 <- ordered(f1) expect_equal(is.ordered(lvls_reorder(f1, 1:3)), FALSE) expect_equal(is.ordered(lvls_reorder(f1, 1:3, ordered = FALSE)), FALSE) expect_equal(is.ordered(lvls_reorder(f1, 1:3, ordered = TRUE)), TRUE) expect_equal(is.ordered(lvls_reorder(f2, 1:3)), TRUE) expect_equal(is.ordered(lvls_reorder(f2, 1:3, ordered = FALSE)), FALSE) expect_equal(is.ordered(lvls_reorder(f2, 1:3, ordered = TRUE)), TRUE) }) forcats/tests/testthat.R0000644000176200001440000000007212752645041015045 0ustar liggesuserslibrary(testthat) library(forcats) test_check("forcats") forcats/vignettes/0000755000176200001440000000000014364755577013752 5ustar liggesusersforcats/vignettes/forcats.Rmd0000644000176200001440000001671014360103324016031 0ustar liggesusers--- title: "Introduction to forcats" author: "Emily Robinson" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to forcats} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` The goal of the **forcats** package is to provide a suite of useful tools that solve common problems with factors. Factors are useful when you have categorical data, variables that have a fixed and known set of values, and when you want to display character vectors in non-alphabetical order. If you want to learn more, the best place to start is the [chapter on factors](https://r4ds.had.co.nz/factors.html) in R for Data Science. ## Ordering by frequency ```{r message = FALSE} library(dplyr) library(ggplot2) library(forcats) ``` Let's try answering the question, "what are the most common hair colors of star wars characters?" Let's start off by making a bar plot: ```{r initial-plot} #| fig.alt: > #| A bar chart of hair color of starwars characters. The bars are #| alphabetically ordered, making it hard to see general patterns. ggplot(starwars, aes(y = hair_color)) + geom_bar() ``` That's okay, but it would be more helpful the graph was ordered by count. This is a case of an **unordered** categorical variable where we want it ordered by its frequency. To do so, we can use the function `fct_infreq()`: ```{r fct-infreq-hair} #| fig.alt: > #| The bar chart of hair color, now ordered so that the least #| frequent colours come first and the most frequent colors come last. #| This makes it easy to see that the most common hair color is none #| (~35), followed by brown (~18), then black (~12). Surprisingly, #| NAs are at the top of the graph, even though there are ~5 NAs and #| other colors have smaller values. ggplot(starwars, aes(y = fct_infreq(hair_color))) + geom_bar() ``` Note that `fct_infreq()` it automatically puts NA at the top, even though that doesn't have the smallest number of entries. It's a little surprising that the `NA` bar isn't ordered by frequency. To understand why we need to make a brief digression to discuss `NA`s in values vs. `NA`s in levels ## `NAs`s in levels and values There are two ways to represent a missing value in a factor: - You can include it in the values of the factor; it does not appear in the levels and `is.na()` reports it as missing. This is how missing values are encoded in a factor by default: ```{r} f <- factor(c("x", "y", NA)) levels(f) is.na(f) ``` - You can include it in the levels of the factor and `is.na()` does not report it as missing. This requires a little more work to create, because, by default, `factor()` uses `exclude = NA`, meaning that missing values are not included in the levels. You can force `NA` to be included by setting `exclude = NULL`: ```{r} f <- factor(c("x", "y", NA), exclude = NULL) levels(f) is.na(f) ``` `NA`s in the values tend to be best for data analysis, since `is.na()` works as you'd expect. `NA`s in the levels can be useful for display if you need to control where they appear in a table or a plot. To fix the issue above, we can use `fct_na_value_to_level()` to convert the `NA` in the value to an NA in the levels. Then they appear where you'd expect: ```{r} #| fig.alt: > #| The bar chart of hair color, now ordered so that NAs are #| ordered where you'd expect: in between white (4) and black (12). ggplot(starwars, aes(y = fct_infreq(fct_na_value_to_level(hair_color)))) + geom_bar() + labs(y = "Hair color") ``` (If you need the opposite operation, you can use `fct_na_level_to_value()`.) ## Combining levels Let's take a look at skin color now: ```{r} starwars %>% count(skin_color, sort = TRUE) ``` We see that there's 31 different skin colors - if we want to make a plot this would be way too many to display! Let's reduce it to only be the top 5. We can use `fct_lump()` to "lump" all the infrequent colors into one factor, "other." The argument `n` is the number of levels we want to keep. ```{r} starwars %>% mutate(skin_color = fct_lump(skin_color, n = 5)) %>% count(skin_color, sort = TRUE) ``` We could also have used `prop` instead, which keeps all the levels that appear at least `prop` of the time. For example, let's keep skin colors that at least 10% of the characters have: ```{r} starwars %>% mutate(skin_color = fct_lump(skin_color, prop = .1)) %>% count(skin_color, sort = TRUE) ``` Only light and fair remain; everything else is other. If you wanted to call it something than "other", you can change it with the argument `other_level`: ```{r} starwars %>% mutate(skin_color = fct_lump(skin_color, prop = .1, other_level = "extra")) %>% count(skin_color, sort = TRUE) ``` What if we wanted to see if the average mass differed by eye color? We'll only look at the 6 most popular eye colors and remove `NA`s. ```{r fct-lump-mean} avg_mass_eye_color <- starwars %>% mutate(eye_color = fct_lump(eye_color, n = 6)) %>% group_by(eye_color) %>% summarise(mean_mass = mean(mass, na.rm = TRUE)) avg_mass_eye_color ``` ## Ordering by another variable It looks like people (or at least one person) with orange eyes are definitely heavier! If we wanted to make a graph, it would be nice if it was ordered by `mean_mass`. We can do this with `fct_reorder()`, which reorders one variable by another. ```{r fct-reorder} #| fig-alt: > #| A column chart with eye color on the x-axis and mean mass on the #| y-axis. The bars are ordered by mean_mass, so that the tallest bar #| (orange eye color with mean mass of ~275) is at the far right. avg_mass_eye_color %>% mutate(eye_color = fct_reorder(eye_color, mean_mass)) %>% ggplot(aes(x = eye_color, y = mean_mass)) + geom_col() ``` ## Manually reordering Let's switch to using another dataset, `gss_cat`, the general social survey. What is the income distribution among the respondents? ```{r} gss_cat %>% count(rincome) ``` Notice that the income levels are in the correct order - they start with the non-answers and then go from highest to lowest. This is the same order you'd see if you plotted it as a bar chart. This is not a coincidence. When you're working with ordinal data, where there is an order, you can have an ordered factor. You can examine them with the base function `levels()`, which prints them in order: ```{r} levels(gss_cat$rincome) ``` But what if your factor came in the wrong order? Let's simulate that by reordering the levels of `rincome` randomly with `fct_shuffle()`: ```{r} reshuffled_income <- gss_cat$rincome %>% fct_shuffle() levels(reshuffled_income) ``` Now if we plotted it, it would show in this order, which is all over the place! How can we fix this and put it in the right order? We can use the function `fct_relevel()` when we need to manually reorder our factor levels. In addition to the factor, you give it a character vector of level names, and specify where you want to move them. It defaults to moving them to the front, but you can move them after another level with the argument `after`. If you want to move it to the end, you set `after` equal to `Inf`. For example, let's say we wanted to move `Lt $1000` and `$1000 to 2999` to the front. We would write: ```{r} fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999")) %>% levels() ``` What if we want to move them to the second and third place? ```{r} fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999"), after = 1) %>% levels() ``` forcats/R/0000755000176200001440000000000014360012623012111 5ustar liggesusersforcats/R/count.R0000644000176200001440000000153414360012612013365 0ustar liggesusers#' Count entries in a factor #' #' @param f A factor (or character vector). #' @param sort If `TRUE`, sort the result so that the most common values #' float to the top. #' @param prop If `TRUE`, compute the fraction of marginal table. #' @return A tibble with columns `f`, `n` and `p`, if prop is `TRUE`. #' @export #' @examples #' f <- factor(sample(letters)[rpois(1000, 10)]) #' table(f) #' fct_count(f) #' fct_count(f, sort = TRUE) #' fct_count(f, sort = TRUE, prop = TRUE) fct_count <- function(f, sort = FALSE, prop = FALSE) { f <- check_factor(f) check_bool(sort) check_bool(prop) n_na <- sum(is.na(f)) n <- c(tabulate(f, nlevels(f)), if (n_na > 0) n_na) df <- tibble::tibble( f = fct_unique(f), n = n ) if (sort) { df <- df[order(df$n, decreasing = TRUE), ] } if (prop) { df$p <- prop.table(df$n) } df } forcats/R/rev.R0000644000176200001440000000044613626051403013037 0ustar liggesusers#' Reverse order of factor levels #' #' This is sometimes useful when plotting a factor. #' #' @param f A factor (or character vector). #' @export #' @examples #' f <- factor(c("a", "b", "c")) #' fct_rev(f) fct_rev <- function(f) { f <- check_factor(f) lvls_reorder(f, rev(lvls_seq(f))) } forcats/R/utils.R0000644000176200001440000000157714360012612013404 0ustar liggesusers#' Pipe operator #' #' See \code{\link[magrittr]{\%>\%}} for more details. #' #' @name %>% #' @rdname pipe #' @keywords internal #' @export #' @importFrom magrittr %>% #' @usage lhs \%>\% rhs NULL check_factor <- function(x, arg = caller_arg(x), call = caller_env()) { if (is.character(x)) { factor(x) } else if (is.factor(x)) { x } else { cli::cli_abort( "{.arg {arg}} must be a factor or character vector, not {.obj_type_friendly {x}}.", call = call ) } } check_factor_list <- function(x, arg = caller_arg(x), call = caller_env()) { if (!is.list(x)) { cli::cli_abort( "{.arg {arg}} must be a list, not {.obj_type_friendly {x}}.", call = call ) } is_factor <- vapply(x, is.factor, logical(1)) if (any(!is_factor)) { cli::cli_abort( "All elements of {.arg {arg}} must be factors.", call = call ) } x } forcats/R/anon.R0000644000176200001440000000142314360012612013165 0ustar liggesusers#' Anonymise factor levels #' #' Replaces factor levels with arbitrary numeric identifiers. Neither #' the values nor the order of the levels are preserved. #' #' @param f A factor. #' @param prefix A character prefix to insert in front of the random labels. #' @export #' @examples #' gss_cat$relig %>% fct_count() #' gss_cat$relig %>% #' fct_anon() %>% #' fct_count() #' gss_cat$relig %>% #' fct_anon("X") %>% #' fct_count() fct_anon <- function(f, prefix = "") { f <- check_factor(f) check_string(prefix) levels <- paste0(prefix, zero_pad(seq_len(nlevels(f)))) f <- lvls_revalue(f, sample(levels)) lvls_reorder(f, match(levels, levels(f))) } digits <- function(x) nchar(max(x, na.rm = TRUE)) zero_pad <- function(x) { sprintf(paste0("%0", digits(x), "d"), x) } forcats/R/shuffle.R0000644000176200001440000000041413626051403013672 0ustar liggesusers#' Randomly permute factor levels #' #' @param f A factor (or character vector). #' @export #' @examples #' f <- factor(c("a", "b", "c")) #' fct_shuffle(f) #' fct_shuffle(f) fct_shuffle <- function(f) { f <- check_factor(f) lvls_reorder(f, sample(lvls_seq(f))) } forcats/R/lump.R0000644000176200001440000001434514360020235013216 0ustar liggesusers#' Lump uncommon factor together levels into "other" #' #' @description #' A family for lumping together levels that meet some criteria. #' * `fct_lump_min()`: lumps levels that appear fewer than `min` times. #' * `fct_lump_prop()`: lumps levels that appear in fewer than (or equal to) #' `prop * n` times. #' * `fct_lump_n()` lumps all levels except for the `n` most frequent #' (or least frequent if `n < 0`) #' * `fct_lump_lowfreq()` lumps together the least frequent levels, ensuring #' that "other" is still the smallest level. #' #' `fct_lump()` exists primarily for historical reasons, as it automatically #' picks between these different methods depending on its arguments. #' We no longer recommend that you use it. #' #' @param f A factor (or character vector). #' @param n Positive `n` preserves the most common `n` values. #' Negative `n` preserves the least common `-n` values. #' It there are ties, you will get at least `abs(n)` values. #' @param prop Positive `prop` lumps values which do not appear at least #' `prop` of the time. Negative `prop` lumps values that #' do not appear at most `-prop` of the time. #' @param min Preserve levels that appear at least `min` number of times. #' @param w An optional numeric vector giving weights for frequency of #' each value (not level) in f. #' @param other_level Value of level used for "other" values. Always #' placed at end of levels. #' @param ties.method A character string specifying how ties are #' treated. See [rank()] for details. #' @export #' @seealso [fct_other()] to convert specified levels to other. #' @examples #' x <- factor(rep(LETTERS[1:9], times = c(40, 10, 5, 27, 1, 1, 1, 1, 1))) #' x %>% table() #' x %>% #' fct_lump_n(3) %>% #' table() #' x %>% #' fct_lump_prop(0.10) %>% #' table() #' x %>% #' fct_lump_min(5) %>% #' table() #' x %>% #' fct_lump_lowfreq() %>% #' table() #' #' x <- factor(letters[rpois(100, 5)]) #' x #' table(x) #' table(fct_lump_lowfreq(x)) #' #' # Use positive values to collapse the rarest #' fct_lump_n(x, n = 3) #' fct_lump_prop(x, prop = 0.1) #' #' # Use negative values to collapse the most common #' fct_lump_n(x, n = -3) #' fct_lump_prop(x, prop = -0.1) #' #' # Use weighted frequencies #' w <- c(rep(2, 50), rep(1, 50)) #' fct_lump_n(x, n = 5, w = w) #' #' # Use ties.method to control how tied factors are collapsed #' fct_lump_n(x, n = 6) #' fct_lump_n(x, n = 6, ties.method = "max") #' #' # Use fct_lump_min() to lump together all levels with fewer than `n` values #' table(fct_lump_min(x, min = 10)) #' table(fct_lump_min(x, min = 15)) fct_lump <- function(f, n, prop, w = NULL, other_level = "Other", ties.method = c("min", "average", "first", "last", "random", "max")) { if (missing(n) && missing(prop)) { fct_lump_lowfreq(f, w = w, other_level = other_level) } else if (missing(prop)) { fct_lump_n(f, n, w = w, other_level = other_level, ties.method = ties.method) } else if (missing(n)) { fct_lump_prop(f, prop, w = w, other_level = other_level) } else { cli::cli_abort("Must supply only one of {.arg n} and {.arg prop}.") } } #' @export #' @rdname fct_lump fct_lump_min <- function(f, min, w = NULL, other_level = "Other") { f <- check_factor(f) check_number_decimal(min, min = 0) check_string(other_level, allow_na = TRUE) level_w <- compute_weights(f, w) lvls_other(f, level_w >= min, other_level) } #' @export #' @rdname fct_lump fct_lump_prop <- function(f, prop, w = NULL, other_level = "Other") { f <- check_factor(f) check_number_decimal(prop) check_string(other_level, allow_na = TRUE) level_w <- compute_weights(f, w) # Compute proportion of total, including NAs if (is.null(w)) { prop_n <- level_w / length(f) } else { prop_n <- level_w / sum(w) } if (prop < 0) { lvls_other(f, prop_n <= -prop, other_level) } else { lvls_other(f, prop_n > prop, other_level) } } #' @export #' @rdname fct_lump fct_lump_n <- function(f, n, w = NULL, other_level = "Other", ties.method = c("min", "average", "first", "last", "random", "max")) { f <- check_factor(f) check_number_decimal(n) check_string(other_level, allow_na = TRUE) ties.method <- arg_match(ties.method) level_w <- compute_weights(f, w) if (n < 0) { rank <- rank(level_w, ties.method = ties.method) n <- -n } else { rank <- rank(-level_w, ties.method = ties.method) } lvls_other(f, rank <= n, other_level) } #' @export #' @rdname fct_lump fct_lump_lowfreq <- function(f, w = NULL, other_level = "Other") { f <- check_factor(f) check_string(other_level, allow_na = TRUE) level_w <- compute_weights(f, w) lvls_other(f, !in_smallest(level_w), other_level) } # helpers ----------------------------------------------------------------- compute_weights <- function(f, w = NULL, call = caller_env()) { w <- check_weights(w, length(f), call = call) w <- w %||% rep(1L, length(f)) n <- as.vector(tapply(w, f, sum)) # fill in counts for empty levels n[is.na(n)] <- 0 n } # Lump together smallest groups, ensuring that the collective # "other" is still the smallest group. Assumes x is vector # of counts in descending order lump_cutoff <- function(x) { left <- sum(x) for (i in seq_along(x)) { # After group, there are this many left left <- left - x[i] if (x[i] > left) { return(i + 1) } } length(x) + 1 } # Given vector of counts, returns logical vector if in # smallest groups in_smallest <- function(x) { ord_x <- order(x, decreasing = TRUE) idx <- lump_cutoff(x[ord_x]) to_lump <- seq_along(x) >= idx # Undo initial ordering to_lump[order(ord_x)] } check_weights <- function(w, n = length(w), call = caller_env()) { if (is.null(w)) { return(w) } if (!is.numeric(w)) { cli::cli_abort( "{.arg w} must be a numeric vector, not {.obj_type_friendly w}.", call = call ) } if (length(w) != n) { cli::cli_abort( "{.arg w} must be the same length as {.arg f} ({n}), not length {length(w)}.", call = call ) } bad <- w < 0 | is.na(w) if (any(bad)) { probs <- which(bad) cli::cli_abort( c( "All {.arg w} must be non-negative and non-missing.", "{length(probs)} problem{?s} at positions {probs}." ), call = call ) } w } forcats/R/drop.R0000644000176200001440000000161014360012612013174 0ustar liggesusers#' Drop unused levels #' #' Compared to `base::droplevels()`, does not drop `NA` levels that have values. #' #' @param f A factor (or character vector). #' @param only A character vector restricting the set of levels to be dropped. #' If supplied, only levels that have no entries and appear in this vector #' will be removed. #' @export #' @seealso [fct_expand()] to add additional levels to a factor. #' @examples #' f <- factor(c("a", "b"), levels = c("a", "b", "c")) #' f #' fct_drop(f) #' #' # Set only to restrict which levels to drop #' fct_drop(f, only = "a") #' fct_drop(f, only = "c") fct_drop <- function(f, only = NULL) { f <- check_factor(f) check_character(only, allow_null = TRUE) levels <- levels(f) count <- table(f) to_drop <- levels[count == 0] if (!is.null(only)) { to_drop <- intersect(to_drop, only) } refactor(f, new_levels = setdiff(levels, to_drop)) } forcats/R/shift.R0000644000176200001440000000144314360012612013351 0ustar liggesusers#' Shift factor levels to left or right, wrapping around at end #' #' This is useful when the levels of an ordered factor are actually cyclical, #' with different conventions on the starting point. #' #' @param f A factor. #' @param n Positive values shift to the left; negative values shift to #' the right. #' @export #' @examples #' x <- factor( #' c("Mon", "Tue", "Wed"), #' levels = c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"), #' ordered = TRUE #' ) #' x #' fct_shift(x) #' fct_shift(x, 2) #' fct_shift(x, -1) fct_shift <- function(f, n = 1L) { check_factor(f) check_number_whole(n) lvls_reorder(f, shift(nlevels(f), n)) } shift <- function(m, n) { stopifnot(is.numeric(m), length(m) == 1L) stopifnot(is.numeric(n), length(n) == 1L) ((seq_len(m) - 1) + n) %% m + 1 } forcats/R/data.R0000644000176200001440000000115414241555020013147 0ustar liggesusers#' A sample of categorical variables from the General Social survey #' #' @source Downloaded from \url{https://gssdataexplorer.norc.org/}. #' @format #' \describe{ #' \item{year}{year of survey, 2000--2014 (every other year)} #' \item{age}{age. Maximum age truncated to 89.} #' \item{marital}{marital status} #' \item{race}{race} #' \item{rincome}{reported income} #' \item{partyid}{party affiliation} #' \item{relig}{religion} #' \item{denom}{denomination} #' \item{tvhours}{hours per day watching tv} #' } #' @examples #' gss_cat #' #' fct_count(gss_cat$relig) #' fct_count(fct_lump(gss_cat$relig)) "gss_cat" forcats/R/compat-obj-type.R0000644000176200001440000001764214360012612015256 0ustar liggesusers# nocov start --- r-lib/rlang compat-obj-type # # Changelog # ========= # # 2022-10-04: # - `obj_type_friendly(value = TRUE)` now shows numeric scalars # literally. # - `stop_friendly_type()` now takes `show_value`, passed to # `obj_type_friendly()` as the `value` argument. # # 2022-10-03: # - Added `allow_na` and `allow_null` arguments. # - `NULL` is now backticked. # - Better friendly type for infinities and `NaN`. # # 2022-09-16: # - Unprefixed usage of rlang functions with `rlang::` to # avoid onLoad issues when called from rlang (#1482). # # 2022-08-11: # - Prefixed usage of rlang functions with `rlang::`. # # 2022-06-22: # - `friendly_type_of()` is now `obj_type_friendly()`. # - Added `obj_type_oo()`. # # 2021-12-20: # - Added support for scalar values and empty vectors. # - Added `stop_input_type()` # # 2021-06-30: # - Added support for missing arguments. # # 2021-04-19: # - Added support for matrices and arrays (#141). # - Added documentation. # - Added changelog. #' Return English-friendly type #' @param x Any R object. #' @param value Whether to describe the value of `x`. Special values #' like `NA` or `""` are always described. #' @param length Whether to mention the length of vectors and lists. #' @return A string describing the type. Starts with an indefinite #' article, e.g. "an integer vector". #' @noRd obj_type_friendly <- function(x, value = TRUE) { if (is_missing(x)) { return("absent") } if (is.object(x)) { if (inherits(x, "quosure")) { type <- "quosure" } else { type <- paste(class(x), collapse = "/") } return(sprintf("a <%s> object", type)) } if (!is_vector(x)) { return(.rlang_as_friendly_type(typeof(x))) } n_dim <- length(dim(x)) if (!n_dim) { if (!is_list(x) && length(x) == 1) { if (is_na(x)) { return(switch( typeof(x), logical = "`NA`", integer = "an integer `NA`", double = if (is.nan(x)) { "`NaN`" } else { "a numeric `NA`" }, complex = "a complex `NA`", character = "a character `NA`", .rlang_stop_unexpected_typeof(x) )) } show_infinites <- function(x) { if (x > 0) { "`Inf`" } else { "`-Inf`" } } str_encode <- function(x, width = 30, ...) { if (nchar(x) > width) { x <- substr(x, 1, width - 3) x <- paste0(x, "...") } encodeString(x, ...) } if (value) { if (is.numeric(x) && is.infinite(x)) { return(show_infinites(x)) } if (is.numeric(x) || is.complex(x)) { number <- as.character(round(x, 2)) what <- if (is.complex(x)) "the complex number" else "the number" return(paste(what, number)) } return(switch( typeof(x), logical = if (x) "`TRUE`" else "`FALSE`", character = { what <- if (nzchar(x)) "the string" else "the empty string" paste(what, str_encode(x, quote = "\"")) }, raw = paste("the raw value", as.character(x)), .rlang_stop_unexpected_typeof(x) )) } return(switch( typeof(x), logical = "a logical value", integer = "an integer", double = if (is.infinite(x)) show_infinites(x) else "a number", complex = "a complex number", character = if (nzchar(x)) "a string" else "\"\"", raw = "a raw value", .rlang_stop_unexpected_typeof(x) )) } if (length(x) == 0) { return(switch( typeof(x), logical = "an empty logical vector", integer = "an empty integer vector", double = "an empty numeric vector", complex = "an empty complex vector", character = "an empty character vector", raw = "an empty raw vector", list = "an empty list", .rlang_stop_unexpected_typeof(x) )) } } vec_type_friendly(x) } vec_type_friendly <- function(x, length = FALSE) { if (!is_vector(x)) { abort("`x` must be a vector.") } type <- typeof(x) n_dim <- length(dim(x)) add_length <- function(type) { if (length && !n_dim) { paste0(type, sprintf(" of length %s", length(x))) } else { type } } if (type == "list") { if (n_dim < 2) { return(add_length("a list")) } else if (is.data.frame(x)) { return("a data frame") } else if (n_dim == 2) { return("a list matrix") } else { return("a list array") } } type <- switch( type, logical = "a logical %s", integer = "an integer %s", numeric = , double = "a double %s", complex = "a complex %s", character = "a character %s", raw = "a raw %s", type = paste0("a ", type, " %s") ) if (n_dim < 2) { kind <- "vector" } else if (n_dim == 2) { kind <- "matrix" } else { kind <- "array" } out <- sprintf(type, kind) if (n_dim >= 2) { out } else { add_length(out) } } .rlang_as_friendly_type <- function(type) { switch( type, list = "a list", NULL = "`NULL`", environment = "an environment", externalptr = "a pointer", weakref = "a weak reference", S4 = "an S4 object", name = , symbol = "a symbol", language = "a call", pairlist = "a pairlist node", expression = "an expression vector", char = "an internal string", promise = "an internal promise", ... = "an internal dots object", any = "an internal `any` object", bytecode = "an internal bytecode object", primitive = , builtin = , special = "a primitive function", closure = "a function", type ) } .rlang_stop_unexpected_typeof <- function(x, call = caller_env()) { abort( sprintf("Unexpected type <%s>.", typeof(x)), call = call ) } #' Return OO type #' @param x Any R object. #' @return One of `"bare"` (for non-OO objects), `"S3"`, `"S4"`, #' `"R6"`, or `"R7"`. #' @noRd obj_type_oo <- function(x) { if (!is.object(x)) { return("bare") } class <- inherits(x, c("R6", "R7_object"), which = TRUE) if (class[[1]]) { "R6" } else if (class[[2]]) { "R7" } else if (isS4(x)) { "S4" } else { "S3" } } #' @param x The object type which does not conform to `what`. Its #' `obj_type_friendly()` is taken and mentioned in the error message. #' @param what The friendly expected type as a string. Can be a #' character vector of expected types, in which case the error #' message mentions all of them in an "or" enumeration. #' @param show_value Passed to `value` argument of `obj_type_friendly()`. #' @param ... Arguments passed to [abort()]. #' @inheritParams args_error_context #' @noRd stop_input_type <- function(x, what, ..., allow_na = FALSE, allow_null = FALSE, show_value = TRUE, arg = caller_arg(x), call = caller_env()) { # From compat-cli.R cli <- env_get_list( nms = c("format_arg", "format_code"), last = topenv(), default = function(x) sprintf("`%s`", x), inherit = TRUE ) if (allow_na) { what <- c(what, cli$format_code("NA")) } if (allow_null) { what <- c(what, cli$format_code("NULL")) } if (length(what)) { what <- oxford_comma(what) } message <- sprintf( "%s must be %s, not %s.", cli$format_arg(arg), what, obj_type_friendly(x, value = show_value) ) abort(message, ..., call = call, arg = arg) } oxford_comma <- function(chr, sep = ", ", final = "or") { n <- length(chr) if (n < 2) { return(chr) } head <- chr[seq_len(n - 1)] last <- chr[n] head <- paste(head, collapse = sep) # Write a or b. But a, b, or c. if (n > 2) { paste0(head, sep, final, " ", last) } else { paste0(head, " ", final, " ", last) } } # nocov end forcats/R/lvls.R0000644000176200001440000000602614360012612013216 0ustar liggesusers#' Low-level functions for manipulating levels #' #' `lvls_reorder` leaves values as they are, but changes the order. #' `lvls_revalue` changes the values of existing levels; there must #' be one new level for each old level. #' `lvls_expand` expands the set of levels; the new levels must #' include the old levels. #' #' These functions are less helpful than the higher-level `fct_` functions, #' but are safer than the very low-level manipulation of levels directly, #' because they are more specific, and hence can more carefully check their #' arguments. #' #' @param f A factor (or character vector). #' @param idx A integer index, with one integer for each existing level. #' @param new_levels A character vector of new levels. #' @param ordered A logical which determines the "ordered" status of the #' output factor. `NA` preserves the existing status of the factor. #' @name lvls #' @examples #' f <- factor(c("a", "b", "c")) #' lvls_reorder(f, 3:1) #' lvls_revalue(f, c("apple", "banana", "carrot")) #' lvls_expand(f, c("a", "b", "c", "d")) NULL #' @export #' @rdname lvls lvls_reorder <- function(f, idx, ordered = NA) { f <- check_factor(f) if (!is.numeric(idx)) { cli::cli_abort("{.arg idx} must be numeric") } if (!setequal(idx, lvls_seq(f)) || length(idx) != nlevels(f)) { cli::cli_abort("{.arg idx} must contain one integer for each level of {.arg f}") } refactor(f, levels(f)[idx], ordered = ordered) } #' @export #' @rdname lvls lvls_revalue <- function(f, new_levels) { f <- check_factor(f) check_character(new_levels) if (length(new_levels) != nlevels(f)) { n_new <- length(new_levels) n_old <- nlevels(f) cli::cli_abort( "{.arg new_levels} must be the same length ({n_new}) as {.code levels(f)} ({n_old})." ) } if (anyDuplicated(new_levels)) { # Collapse levels, creating a new factor u_levels <- unique(new_levels) index <- match(new_levels, u_levels) out <- index[f] attributes(out) <- attributes(f) attr(out, "levels") <- u_levels out } else { attr(f, "levels") <- new_levels f } } #' @export #' @rdname lvls lvls_expand <- function(f, new_levels) { f <- check_factor(f) missing <- setdiff(levels(f), new_levels) if (length(missing) > 0) { cli::cli_abort(c( "{.arg new_levels} must include all levels in {.arg f}.", i = "Missing {length(missing)} level{?s}: {missing}" )) } refactor(f, new_levels) } lvls_seq <- function(f) { seq_along(levels(f)) } refactor <- function(f, new_levels, ordered = NA) { if (is.na(ordered)) { ordered <- is.ordered(f) } new_f <- factor(f, levels = new_levels, exclude = NULL, ordered = ordered) attributes(new_f) <- utils::modifyList(attributes(f), attributes(new_f)) new_f } #' Find all levels in a list of factors #' #' @param fs A list of factors. #' @export #' @examples #' fs <- list(factor("a"), factor("b"), factor(c("a", "b"))) #' lvls_union(fs) lvls_union <- function(fs) { fs <- check_factor_list(fs) Reduce(function(x, y) union(x, levels(y)), fs, init = character()) } forcats/R/unique.R0000644000176200001440000000174414355354616013567 0ustar liggesusers#' Unique values of a factor, as a factor #' #' @description #' `fct_unique()` extracts the complete set of possible values from the #' levels of the factor, rather than looking at the actual values, like #' [unique()]. #' #' `fct_unique()` only uses the values of `f` in one way: it looks for #' implicit missing values so that they can be included in the result. #' #' @param f A factor. #' @return A factor. #' @export #' @examples #' f <- fct(letters[rpois(100, 10)]) #' unique(f) # in order of appearance #' fct_unique(f) # in order of levels #' #' f <- fct(letters[rpois(100, 2)], letters[1:20]) #' unique(f) # levels that appear in data #' fct_unique(f) # all possible levels fct_unique <- function(f) { f <- check_factor(f) levels <- levels(f) out <- seq_along(levels) # Ensure out includes any implicit missings if (anyNA(f)) { out <- c(out, NA_integer_) } structure( out, levels = levels, class = c(if (is.ordered(f)) "ordered", "factor") ) } forcats/R/fct.R0000644000176200001440000000336214360012612013012 0ustar liggesusers#' Create a factor #' #' `fct()` is a stricter version of [factor()] that errors if your #' specification of `levels` is inconsistent with the values in `x`. #' #' @param x A character vector. Values must occur in either `levels` or `na`. #' @param levels A character vector of known levels. If not supplied, will #' be computed from the unique values of `x`, in the order in which they #' occur. #' @param na A character vector of values that should become missing values. #' @return A factor. #' @export #' @examples #' # Use factors when you know the set of possible values a variable might take #' x <- c("A", "O", "O", "AB", "A") #' fct(x, levels = c("O", "A", "B", "AB")) #' #' # If you don't specify the levels, fct will create from the data #' # in the order that they're seen #' fct(x) #' #' #' # Differences with base R ----------------------------------------------- #' # factor() silently generates NAs #' x <- c("a", "b", "c") #' factor(x, levels = c("a", "b")) #' # fct() errors #' try(fct(x, levels = c("a", "b"))) #' # Unless you explicitly supply NA: #' fct(x, levels = c("a", "b"), na = "c") #' #' # factor() sorts default levels: #' factor(c("y", "x")) #' # fct() uses in order of appearance: #' fct(c("y", "x")) fct <- function(x = character(), levels = NULL, na = character()) { check_character(x) check_character(levels, allow_null = TRUE) check_character(na) x[x %in% na] <- NA if (is.null(levels)) { levels <- unique(x) levels <- levels[!is.na(levels)] } invalid <- setdiff(x, c(levels, NA)) if (length(invalid) > 0 ) { cli::cli_abort(c( "All values of {.arg x} must appear in {.arg levels} or {.arg na}", i = "Missing level{?s}: {.str {invalid}}" )) } factor(x, levels = levels, exclude = NULL) } forcats/R/relevel.R0000644000176200001440000000424514357357714013722 0ustar liggesusers#' Reorder factor levels by hand #' #' This is a generalisation of [stats::relevel()] that allows you to move any #' number of levels to any location. #' #' @param .f A factor (or character vector). #' @param ... Either a function (or formula), or character levels. #' #' A function will be called with the current levels as input, and the #' return value (which must be a character vector) will be used to relevel #' the factor. #' #' Any levels not mentioned will be left in their existing order, by default #' after the explicitly mentioned levels. Supports tidy dots. #' @param after Where should the new values be placed? #' @export #' @examples #' f <- factor(c("a", "b", "c", "d"), levels = c("b", "c", "d", "a")) #' fct_relevel(f) #' fct_relevel(f, "a") #' fct_relevel(f, "b", "a") #' #' # Move to the third position #' fct_relevel(f, "a", after = 2) #' #' # Relevel to the end #' fct_relevel(f, "a", after = Inf) #' fct_relevel(f, "a", after = 3) #' #' # Relevel with a function #' fct_relevel(f, sort) #' fct_relevel(f, sample) #' fct_relevel(f, rev) #' #' # Using 'Inf' allows you to relevel to the end when the number #' # of levels is unknown or variable (e.g. vectorised operations) #' df <- forcats::gss_cat[, c("rincome", "denom")] #' lapply(df, levels) #' #' df2 <- lapply(df, fct_relevel, "Don't know", after = Inf) #' lapply(df2, levels) #' #' # You'll get a warning if the levels don't exist #' fct_relevel(f, "e") fct_relevel <- function(.f, ..., after = 0L) { f <- check_factor(.f) check_dots_unnamed() old_levels <- levels(f) if (dots_n(...) == 1L && (is.function(..1) || is_formula(..1))) { fun <- as_function(..1) first_levels <- fun(old_levels) if (!is.character(first_levels)) { cli::cli_abort("Re-leveling function must return character vector") } } else { first_levels <- chr(...) } unknown <- setdiff(first_levels, old_levels) if (length(unknown) > 0) { cli::cli_warn("{length(unknown)} unknown level{?s} in `f`: {unknown}") first_levels <- intersect(first_levels, old_levels) } new_levels <- append(setdiff(old_levels, first_levels), first_levels, after = after) lvls_reorder(f, match(new_levels, old_levels)) } forcats/R/relabel.R0000644000176200001440000000233114357077752013665 0ustar liggesusers#' Relabel factor levels with a function, collapsing as necessary #' #' @param .f A factor (or character vector). #' @param .fun A function to be applied to each level. Must accept one #' character argument and return a character vector of the same length as #' its input. #' #' You can also use `~` to create as shorthand (in the style of purrr). #' `~ paste(., "x")` is equivalent to `function(.) paste(., "x")` #' @param ... Additional arguments to `fun`. #' @export #' @examples #' gss_cat$partyid %>% fct_count() #' gss_cat$partyid %>% #' fct_relabel(~ gsub(",", ", ", .x)) %>% #' fct_count() #' #' convert_income <- function(x) { #' regex <- "^(?:Lt |)[$]([0-9]+).*$" #' is_range <- grepl(regex, x) #' num_income <- as.numeric(gsub(regex, "\\1", x[is_range])) #' num_income <- trunc(num_income / 5000) * 5000 #' x[is_range] <- paste0("Gt $", num_income) #' x #' } #' fct_count(gss_cat$rincome) #' convert_income(levels(gss_cat$rincome)) #' rincome2 <- fct_relabel(gss_cat$rincome, convert_income) #' fct_count(rincome2) fct_relabel <- function(.f, .fun, ...) { f <- check_factor(.f) .fun <- as_function(.fun) old_levels <- levels(f) new_levels <- .fun(old_levels, ...) lvls_revalue(f, new_levels) } forcats/R/compat-types-check.R0000644000176200001440000002435114360012612015737 0ustar liggesusers# nocov start --- r-lib/rlang compat-types-check # # Dependencies # ============ # # - compat-obj-type.R # # Changelog # ========= # # 2022-10-07: # - `check_number_whole()` and `_decimal()` no longer treat # non-numeric types such as factors or dates as numbers. Numeric # types are detected with `is.numeric()`. # # 2022-10-04: # - Added `check_name()` that forbids the empty string. # `check_string()` allows the empty string by default. # # 2022-09-28: # - Removed `what` arguments. # - Added `allow_na` and `allow_null` arguments. # - Added `allow_decimal` and `allow_infinite` arguments. # - Improved errors with absent arguments. # # # 2022-09-16: # - Unprefixed usage of rlang functions with `rlang::` to # avoid onLoad issues when called from rlang (#1482). # # 2022-08-11: # - Added changelog. # Scalars ----------------------------------------------------------------- check_bool <- function(x, ..., allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_bool(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } if (allow_na && identical(x, NA)) { return(invisible(NULL)) } } stop_input_type( x, c("`TRUE`", "`FALSE`"), ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_string <- function(x, ..., allow_empty = TRUE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { is_string <- .rlang_check_is_string( x, allow_empty = allow_empty, allow_na = allow_na, allow_null = allow_null ) if (is_string) { return(invisible(NULL)) } } stop_input_type( x, "a single string", ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } .rlang_check_is_string <- function(x, allow_empty, allow_na, allow_null) { if (is_string(x)) { if (allow_empty || !is_string(x, "")) { return(TRUE) } } if (allow_null && is_null(x)) { return(TRUE) } if (allow_na && (identical(x, NA) || identical(x, na_chr))) { return(TRUE) } FALSE } check_name <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { is_string <- .rlang_check_is_string( x, allow_empty = FALSE, allow_na = FALSE, allow_null = allow_null ) if (is_string) { return(invisible(NULL)) } } stop_input_type( x, "a valid name", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_number_decimal <- function(x, ..., min = -Inf, max = Inf, allow_infinite = TRUE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { .rlang_types_check_number( x, ..., min = min, max = max, allow_decimal = TRUE, allow_infinite = allow_infinite, allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_number_whole <- function(x, ..., min = -Inf, max = Inf, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { .rlang_types_check_number( x, ..., min = min, max = max, allow_decimal = FALSE, allow_infinite = FALSE, allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } .rlang_types_check_number <- function(x, ..., min = -Inf, max = Inf, allow_decimal = FALSE, allow_infinite = FALSE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (allow_decimal) { what <- "a number" } else { what <- "a whole number" } .stop <- function(x, what, ...) stop_input_type( x, what, ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) if (!missing(x)) { is_number <- is_number( x, allow_decimal = allow_decimal, allow_infinite = allow_infinite ) if (is_number) { if (min > -Inf && max < Inf) { what <- sprintf("a number between %s and %s", min, max) } else { what <- NULL } if (x < min) { what <- what %||% sprintf("a number larger than %s", min) .stop(x, what, ...) } if (x > max) { what <- what %||% sprintf("a number smaller than %s", max) .stop(x, what, ...) } return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } if (allow_na && (identical(x, NA) || identical(x, na_dbl) || identical(x, na_int))) { return(invisible(NULL)) } } .stop(x, what, ...) } is_number <- function(x, allow_decimal = FALSE, allow_infinite = FALSE) { if (!typeof(x) %in% c("integer", "double")) { return(FALSE) } if (!is.numeric(x)) { return(FALSE) } if (length(x) != 1) { return(FALSE) } if (is.na(x)) { return(FALSE) } if (!allow_decimal && !is_integerish(x)) { return(FALSE) } if (!allow_infinite && is.infinite(x)) { return(FALSE) } TRUE } check_symbol <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_symbol(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a symbol", ..., allow_null = allow_null, arg = arg, call = call ) } check_arg <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_symbol(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an argument name", ..., allow_null = allow_null, arg = arg, call = call ) } check_call <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_call(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a defused call", ..., allow_null = allow_null, arg = arg, call = call ) } check_environment <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_environment(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an environment", ..., allow_null = allow_null, arg = arg, call = call ) } check_function <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_function(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a function", ..., allow_null = allow_null, arg = arg, call = call ) } check_closure <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_closure(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an R function", ..., allow_null = allow_null, arg = arg, call = call ) } check_formula <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_formula(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a formula", ..., allow_null = allow_null, arg = arg, call = call ) } # Vectors ----------------------------------------------------------------- check_character <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_character(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a character vector", ..., allow_null = allow_null, arg = arg, call = call ) } # nocov end forcats/R/explicit_na.R0000644000176200001440000000240314357073063014545 0ustar liggesusers#' Make missing values explicit #' #' @description #' `r lifecycle::badge("deprecated")` #' #' This function is deprecated because the terminology is confusing; #' please use [fct_na_value_to_level()] instead. #' #' This gives missing values an explicit factor level, ensuring that they #' appear in summaries and on plots. #' #' @param f A factor (or character vector). #' @param na_level Level to use for missing values: this is what `NA`s will be #' changed to. #' @export #' @keywords internal #' @examples #' f1 <- factor(c("a", "a", NA, NA, "a", "b", NA, "c", "a", "c", "b")) #' fct_count(f1) #' table(f1) #' sum(is.na(f1)) #' #' # previously #' f2 <- fct_explicit_na(f1) #' # now #' f2 <- fct_na_value_to_level(f1) #' #' fct_count(f2) #' table(f2) #' sum(is.na(f2)) fct_explicit_na <- function(f, na_level = "(Missing)") { lifecycle::deprecate_warn( when = "1.0.0", what = "fct_explicit_na()", with = "fct_na_value_to_level()" ) f <- check_factor(f) is_missing <- is.na(f) is_missing_level <- is.na(levels(f)) if (any(is_missing)) { f <- fct_expand(f, na_level) f[is_missing] <- na_level f } else if (any(is_missing_level)) { levs <- levels(f) levs[is.na(levs)] <- na_level lvls_revalue(f, levs) } else { f } } forcats/R/match.R0000644000176200001440000000224314360012612013327 0ustar liggesusers#' Test for presence of levels in a factor #' #' Do any of `lvls` occur in `f`? Compared to [%in%], this function validates #' `lvls` to ensure that they're actually present in `f`. In other words, #' `x %in% "not present"` will return `FALSE`, but `fct_match(x, "not present")` #' will throw an error. #' #' @rdname fct_match #' @param f A factor (or character vector). #' @param lvls A character vector specifying levels to look for. #' @return A logical vector #' @export #' @examples #' table(fct_match(gss_cat$marital, c("Married", "Divorced"))) #' #' # Compare to %in%, misspelled levels throw an error #' table(gss_cat$marital %in% c("Maried", "Davorced")) #' \dontrun{ #' table(fct_match(gss_cat$marital, c("Maried", "Davorced"))) #' } fct_match <- function(f, lvls) { f <- check_factor(f) if (identical(lvls, NA)) { lvls <- NA_character_ } check_character(lvls) bad_lvls <- setdiff(lvls, levels(f)) bad_lvls <- bad_lvls[!is.na(bad_lvls)] if (length(bad_lvls) > 0) { missing <- encodeString(bad_lvls, quote = '"') cli::cli_abort(c( "All {.arg lvls} must be present in {.arg f}.", i = "Missing levels: {missing}" )) } f %in% lvls } forcats/R/forcats-package.R0000644000176200001440000000030714241066126015273 0ustar liggesusers#' @keywords internal "_PACKAGE" ## usethis namespace: start #' @import rlang #' @importFrom glue glue #' @importFrom lifecycle deprecated #' @importFrom stats median ## usethis namespace: end NULL forcats/R/as_factor.R0000644000176200001440000000251214355407043014205 0ustar liggesusers#' Convert input to a factor #' #' Compared to base R, when `x` is a character, this function creates #' levels in the order in which they appear, which will be the same on every #' platform. (Base R sorts in the current locale which can vary from place #' to place.) When `x` is numeric, the ordering is based on the numeric #' value and consistent with base R. #' #' This is a generic function. #' #' @param x Object to coerce to a factor. #' @param ... Other arguments passed down to method. #' @export #' @examples #' # Character object #' x <- c("a", "z", "g") #' as_factor(x) #' as.factor(x) #' #' # Character object containing numbers #' y <- c("1.1", "11", "2.2", "22") #' as_factor(y) #' as.factor(y) #' #' # Numeric object #' z <- as.numeric(y) #' as_factor(z) #' as.factor(z) as_factor <- function(x, ...) { check_dots_used() UseMethod("as_factor") } #' @rdname as_factor #' @export as_factor.factor <- function(x, ...) { x } #' @rdname as_factor #' @export as_factor.character <- function(x, ...) { # Preserve label for future haven compatibility structure( fct_inorder(x), label = attr(x, "label", exact = TRUE) ) } #' @rdname as_factor #' @export as_factor.numeric <- function(x, ...) { factor(x) } #' @rdname as_factor #' @export as_factor.logical <- function(x, ...) { factor(x, levels = c("FALSE", "TRUE")) } forcats/R/recode.R0000644000176200001440000000450514357073063013514 0ustar liggesusers#' Change factor levels by hand #' #' @param .f A factor (or character vector). #' @param ... <[`dynamic-dots`][rlang::dyn-dots]> A sequence of named character #' vectors where the name gives the new level, and the value gives the old #' level. Levels not otherwise mentioned will be left as is. Levels can #' be removed by naming them `NULL`. #' @export #' @examples #' x <- factor(c("apple", "bear", "banana", "dear")) #' fct_recode(x, fruit = "apple", fruit = "banana") #' #' # If you make a mistake you'll get a warning #' fct_recode(x, fruit = "apple", fruit = "bananana") #' #' # If you name the level NULL it will be removed #' fct_recode(x, NULL = "apple", fruit = "banana") #' #' # Wrap the left hand side in quotes if it contains special variables #' fct_recode(x, "an apple" = "apple", "a bear" = "bear") #' #' # When passing a named vector to rename levels use !!! to splice #' x <- factor(c("apple", "bear", "banana", "dear")) #' levels <- c(fruit = "apple", fruit = "banana") #' fct_recode(x, !!!levels) fct_recode <- function(.f, ...) { f <- check_factor(.f) new_levels <- check_recode_levels(...) # Remove any named NULL and finish if all NULLs nulls <- names(new_levels) == "NULL" if (any(nulls)) { f <- factor(f, levels = setdiff(levels(f), new_levels[nulls])) new_levels <- new_levels[!nulls] } lvls_revalue(f, lvls_rename(f, new_levels)) } lvls_rename <- function(f, new_levels) { # Match old levels with new levels old_levels <- levels(f) idx <- match(new_levels, old_levels) # Handle levels that don't exist if (any(is.na(idx))) { bad <- new_levels[is.na(idx)] warning("Unknown levels in `f`: ", paste(bad, collapse = ", "), call. = FALSE) new_levels <- new_levels[!is.na(idx)] idx <- idx[!is.na(idx)] } old_levels[idx] <- names(new_levels) old_levels } check_recode_levels <- function(..., call = caller_env()) { levels <- list2(...) is_ok <- function(x) is.character(x) && length(x) == 1 ok <- vapply(levels, is_ok, logical(1)) & names2(levels) != "" if (!all(ok)) { indx <- names2(levels) indx[indx == ""] <- seq_along(levels)[indx == ""] problems <- indx[!ok] cli::cli_abort(c( "Each element of {.arg ...} must be a named string.", i = "Problems with {length(problems)} argument{?s}: {problems}" ), call = call) } unlist(levels) } forcats/R/na.R0000644000176200001440000000404314360012612012631 0ustar liggesusers#' Convert between `NA` values and `NA` levels #' #' @description #' There are two ways to represent missing values in factors: in the values #' and in the levels. `NA`s in the values are most useful for data analysis #' (since [is.na()] returns what you expect), but because the `NA` is not #' explicitly recorded in the levels, there's no way to control its position #' (it's almost always displayed last or not at all). Putting the `NA`s in the levels allows #' you to control its display, at the cost of losing accurate `is.na()` #' reporting. #' #' (It is possible to have a factor with missing values in both the values #' and the levels but it requires some explicit gymnastics and we don't #' recommend it.) #' #' @param f A factor (or character vector). #' @param level Optionally, instead of converting the `NA` values to an #' `NA` level, convert it to a level with this value. #' @export #' @examples #' # Most factors store NAs in the values: #' f1 <- fct(c("a", "b", NA, "c", "b", NA)) #' levels(f1) #' as.integer(f1) #' is.na(f1) #' #' # But it's also possible to store them in the levels #' f2 <- fct_na_value_to_level(f1) #' levels(f2) #' as.integer(f2) #' is.na(f2) #' #' # If needed, you can convert back to NAs in the values: #' f3 <- fct_na_level_to_value(f2) #' levels(f3) #' as.integer(f3) #' is.na(f3) fct_na_value_to_level <- function(f, level = NA) { f <- check_factor(f) check_string(level, allow_na = TRUE) f <- fct_expand(f, NA) new_levels <- levels(f) new_levels[is.na(new_levels)] <- level lvls_revalue(f, new_levels) } #' @export #' @rdname fct_na_value_to_level #' @param extra_levels Optionally, a character vector giving additional levels #' that should also be converted to `NA` values. fct_na_level_to_value <- function(f, extra_levels = NULL) { f <- check_factor(f) check_character(extra_levels, allow_null = TRUE) new_levels <- setdiff(levels(f), union(NA, extra_levels)) idx <- match(levels(f), new_levels) out <- idx[as.integer(f)] attributes(out) <- attributes(f) attr(out, "levels") <- new_levels out } forcats/R/collapse.R0000644000176200001440000000273314360012612014041 0ustar liggesusers#' Collapse factor levels into manually defined groups #' #' @param .f A factor (or character vector). #' @param ... <[`dynamic-dots`][rlang::dyn-dots]> A series of named character vectors. The levels in #' each vector will be replaced with the name. #' @inheritParams fct_other #' @param group_other Deprecated. Replace all levels not named in `...` with "Other"? #' @export #' @examples #' fct_count(gss_cat$partyid) #' #' partyid2 <- fct_collapse(gss_cat$partyid, #' missing = c("No answer", "Don't know"), #' other = "Other party", #' rep = c("Strong republican", "Not str republican"), #' ind = c("Ind,near rep", "Independent", "Ind,near dem"), #' dem = c("Not str democrat", "Strong democrat") #' ) #' fct_count(partyid2) fct_collapse <- function(.f, ..., other_level = NULL, group_other = "DEPRECATED") { f <- check_factor(.f) check_string(other_level, allow_null = TRUE, allow_na = TRUE) if (!missing(group_other)) { lifecycle::deprecate_warn( when = "0.5.0", what = "fct_collapse(group_other)", with = "fct_collapse(other_level)", always = TRUE ) if (isTRUE(group_other) && is.null(other_level)) { other_level <- "Other" } } dots <- rlang::list2(...) old <- unlist(dots, use.names = FALSE) %||% character() new <- rep(names(dots), lengths(dots)) out <- lvls_revalue(f, lvls_rename(f, set_names(old, new))) if (!is.null(other_level)) { out <- lvls_other(out, levels(out) %in% new, other_level) } out } forcats/R/compat-purrr.R0000644000176200001440000001154514355304071014702 0ustar liggesusers# nocov start - compat-purrr.R # Latest version: https://github.com/r-lib/rlang/blob/master/R/compat-purrr.R # This file provides a minimal shim to provide a purrr-like API on top of # base R functions. They are not drop-in replacements but allow a similar style # of programming. # # Changelog: # 2020-04-14: # * Removed `pluck*()` functions # * Removed `*_cpl()` functions # * Used `as_function()` to allow use of `~` # * Used `.` prefix for helpers # # 2021-05-21: # * Fixed "object `x` not found" error in `imap()` (@mgirlich) # # 2021-12-15: # * `transpose()` now supports empty lists. map <- function(.x, .f, ...) { .f <- as_function(.f, env = global_env()) lapply(.x, .f, ...) } walk <- function(.x, .f, ...) { map(.x, .f, ...) invisible(.x) } map_lgl <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, logical(1), ...) } map_int <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, integer(1), ...) } map_dbl <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, double(1), ...) } map_chr <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, character(1), ...) } .rlang_purrr_map_mold <- function(.x, .f, .mold, ...) { .f <- as_function(.f, env = global_env()) out <- vapply(.x, .f, .mold, ..., USE.NAMES = FALSE) names(out) <- names(.x) out } map2 <- function(.x, .y, .f, ...) { .f <- as_function(.f, env = global_env()) out <- mapply(.f, .x, .y, MoreArgs = list(...), SIMPLIFY = FALSE) if (length(out) == length(.x)) { set_names(out, names(.x)) } else { set_names(out, NULL) } } map2_lgl <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "logical") } map2_int <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "integer") } map2_dbl <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "double") } map2_chr <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "character") } imap <- function(.x, .f, ...) { map2(.x, names(.x) %||% seq_along(.x), .f, ...) } pmap <- function(.l, .f, ...) { .f <- as.function(.f) args <- .rlang_purrr_args_recycle(.l) do.call("mapply", c( FUN = list(quote(.f)), args, MoreArgs = quote(list(...)), SIMPLIFY = FALSE, USE.NAMES = FALSE )) } .rlang_purrr_args_recycle <- function(args) { lengths <- map_int(args, length) n <- max(lengths) stopifnot(all(lengths == 1L | lengths == n)) to_recycle <- lengths == 1L args[to_recycle] <- map(args[to_recycle], function(x) rep.int(x, n)) args } keep <- function(.x, .f, ...) { .x[.rlang_purrr_probe(.x, .f, ...)] } discard <- function(.x, .p, ...) { sel <- .rlang_purrr_probe(.x, .p, ...) .x[is.na(sel) | !sel] } map_if <- function(.x, .p, .f, ...) { matches <- .rlang_purrr_probe(.x, .p) .x[matches] <- map(.x[matches], .f, ...) .x } .rlang_purrr_probe <- function(.x, .p, ...) { if (is_logical(.p)) { stopifnot(length(.p) == length(.x)) .p } else { .p <- as_function(.p, env = global_env()) map_lgl(.x, .p, ...) } } compact <- function(.x) { Filter(length, .x) } transpose <- function(.l) { if (!length(.l)) { return(.l) } inner_names <- names(.l[[1]]) if (is.null(inner_names)) { fields <- seq_along(.l[[1]]) } else { fields <- set_names(inner_names) } map(fields, function(i) { map(.l, .subset2, i) }) } every <- function(.x, .p, ...) { .p <- as_function(.p, env = global_env()) for (i in seq_along(.x)) { if (!rlang::is_true(.p(.x[[i]], ...))) return(FALSE) } TRUE } some <- function(.x, .p, ...) { .p <- as_function(.p, env = global_env()) for (i in seq_along(.x)) { if (rlang::is_true(.p(.x[[i]], ...))) return(TRUE) } FALSE } negate <- function(.p) { .p <- as_function(.p, env = global_env()) function(...) !.p(...) } reduce <- function(.x, .f, ..., .init) { f <- function(x, y) .f(x, y, ...) Reduce(f, .x, init = .init) } reduce_right <- function(.x, .f, ..., .init) { f <- function(x, y) .f(y, x, ...) Reduce(f, .x, init = .init, right = TRUE) } accumulate <- function(.x, .f, ..., .init) { f <- function(x, y) .f(x, y, ...) Reduce(f, .x, init = .init, accumulate = TRUE) } accumulate_right <- function(.x, .f, ..., .init) { f <- function(x, y) .f(y, x, ...) Reduce(f, .x, init = .init, right = TRUE, accumulate = TRUE) } detect <- function(.x, .f, ..., .right = FALSE, .p = is_true) { .p <- as_function(.p, env = global_env()) .f <- as_function(.f, env = global_env()) for (i in .rlang_purrr_index(.x, .right)) { if (.p(.f(.x[[i]], ...))) { return(.x[[i]]) } } NULL } detect_index <- function(.x, .f, ..., .right = FALSE, .p = is_true) { .p <- as_function(.p, env = global_env()) .f <- as_function(.f, env = global_env()) for (i in .rlang_purrr_index(.x, .right)) { if (.p(.f(.x[[i]], ...))) { return(i) } } 0L } .rlang_purrr_index <- function(x, right = FALSE) { idx <- seq_along(x) if (right) { idx <- rev(idx) } idx } # nocov end forcats/R/reorder.R0000644000176200001440000001511314360012623013677 0ustar liggesusers#' Reorder factor levels by sorting along another variable #' #' `fct_reorder()` is useful for 1d displays where the factor is mapped to #' position; `fct_reorder2()` for 2d displays where the factor is mapped to #' a non-position aesthetic. `last2()` and `first2()` are helpers for `fct_reorder2()`; #' `last2()` finds the last value of `y` when sorted by `x`; `first2()` finds the first value. #' #' @param .f A factor (or character vector). #' @param .x,.y The levels of `f` are reordered so that the values #' of `.fun(.x)` (for `fct_reorder()`) and `fun(.x, .y)` (for `fct_reorder2()`) #' are in ascending order. #' @param .fun n summary function. It should take one vector for #' `fct_reorder`, and two vectors for `fct_reorder2`, and return a single #' value. #' @param .na_rm Should `fct_reorder()` remove missing values? #' If `NULL`, the default, will remove missing values with a warning. #' Set to `FALSE` to preserve `NA`s (if you `.fun` already handles them) and #' `TRUE` to remove silently. #' @param .default What default value should we use for `.fun` for #' empty levels? Use this to control where empty levels appear in the #' output. #' @param ... Other arguments passed on to `.fun`. #' @param .desc Order in descending order? Note the default is different #' between `fct_reorder` and `fct_reorder2`, in order to #' match the default ordering of factors in the legend. #' @export #' @examples #' # fct_reorder() ------------------------------------------------------------- #' # Useful when a categorical variable is mapped to position #' boxplot(Sepal.Width ~ Species, data = iris) #' boxplot(Sepal.Width ~ fct_reorder(Species, Sepal.Width), data = iris) #' #' # or with #' library(ggplot2) #' ggplot(iris, aes(fct_reorder(Species, Sepal.Width), Sepal.Width)) + #' geom_boxplot() #' #' # fct_reorder2() ------------------------------------------------------------- #' # Useful when a categorical variable is mapped to color, size, shape etc #' #' chks <- subset(ChickWeight, as.integer(Chick) < 10) #' chks <- transform(chks, Chick = fct_shuffle(Chick)) #' #' # Without reordering it's hard to match line to legend #' ggplot(chks, aes(Time, weight, colour = Chick)) + #' geom_point() + #' geom_line() #' #' # With reordering it's much easier #' ggplot(chks, aes(Time, weight, colour = fct_reorder2(Chick, Time, weight))) + #' geom_point() + #' geom_line() + #' labs(colour = "Chick") fct_reorder <- function(.f, .x, .fun = median, ..., .na_rm = NULL, .default = Inf, .desc = FALSE) { f <- check_factor(.f) stopifnot(length(f) == length(.x)) .fun <- as_function(.fun) check_dots_used() check_bool(.na_rm, allow_null = TRUE) check_bool(.desc) miss <- is.na(.x) if (any(miss)) { if (is.null(.na_rm)) { cli::cli_warn(c( "{.fn fct_reorder} removing {sum(miss)} missing value{?s}.", i = "Use {.code .na_rm = TRUE} to silence this message.", i = "Use {.code .na_rm = FALSE} to preserve NAs." )) .na_rm <- TRUE } if (isTRUE(.na_rm)) { .x <- .x[!miss] .f <- .f[!miss] } } summary <- tapply(.x, .f, function(x) .fun(x, ...), default = .default) check_single_value_per_group(summary, ".fun") lvls_reorder(f, order(summary, decreasing = .desc)) } #' @export #' @rdname fct_reorder fct_reorder2 <- function(.f, .x, .y, .fun = last2, ..., .na_rm = NULL, .default = -Inf, .desc = TRUE) { .f <- check_factor(.f) stopifnot(length(.f) == length(.x), length(.x) == length(.y)) check_dots_used() check_bool(.na_rm, allow_null = TRUE) check_bool(.desc) miss <- is.na(.x) | is.na(.y) if (any(miss)) { if (is.null(.na_rm)) { cli::cli_warn(c( "{.fn fct_reorder2} removing {sum(miss)} missing value{?s}.", i = "Use {.code .na_rm = TRUE} to silence this message.", i = "Use {.code .na_rm = FALSE} to preserve NAs." )) .na_rm <- TRUE } if (isTRUE(.na_rm)) { .x <- .x[!miss] .y <- .y[!miss] .f <- .f[!miss] } } summary <- tapply( seq_along(.x), .f, function(i) .fun(.x[i], .y[i], ...), default = .default ) check_single_value_per_group(summary, ".fun") lvls_reorder(.f, order(summary, decreasing = .desc)) } check_single_value_per_group <- function(x, fun_arg, call = caller_env()) { # This is a bit of a weak test, but should detect the most common case # where `.fun` returns multiple values. if (is.list(x)) { cli::cli_abort("{.arg {fun_arg}} must return a single value per group", call = call) } } #' @export #' @rdname fct_reorder last2 <- function(.x, .y) { terminal(.x, .y, desc = TRUE) } #' @export #' @rdname fct_reorder first2 <- function(.x, .y) { terminal(.x, .y, desc = FALSE) } terminal <- function(x, y, desc) { miss <- is.na(x) | is.na(y) x <- x[!miss] y <- y[!miss] if (length(x) == 0) { y[NA_integer_] } else { y[[order(x, decreasing = desc)[[1]]]] } } #' Reorder factor levels by first appearance, frequency, or numeric order #' #' This family of functions changes only the order of the levels. #' * `fct_inorder()`: by the order in which they first appear. #' * `fct_infreq()`: by number of observations with each level (largest first) #' * `fct_inseq()`: by numeric value of level. #' #' @inheritParams lvls_reorder #' @param f A factor #' @export #' @examples #' f <- factor(c("b", "b", "a", "c", "c", "c")) #' f #' fct_inorder(f) #' fct_infreq(f) #' #' f <- factor(1:3, levels = c("3", "2", "1")) #' f #' fct_inseq(f) fct_inorder <- function(f, ordered = NA) { f <- check_factor(f) check_bool(ordered, allow_na = TRUE) idx <- as.integer(f)[!duplicated(f)] idx <- union(idx[!is.na(idx)], lvls_seq(f)) lvls_reorder(f, idx, ordered = ordered) } #' @export #' @rdname fct_inorder #' @inheritParams fct_lump fct_infreq <- function(f, w = NULL, ordered = NA) { f <- check_factor(f) w <- compute_weights(f, w) check_bool(ordered, allow_na = TRUE) lvls_reorder(f, order(w, decreasing = TRUE), ordered = ordered) } #' @export #' @rdname fct_inorder fct_inseq <- function(f, ordered = NA) { f <- check_factor(f) check_bool(ordered, allow_na = TRUE) num_levels <- suppressWarnings(as.numeric(levels(f))) if (all(is.na(num_levels))) { cli::cli_abort("At least one existing level must be coercible to numeric.") } lvls_reorder(f, order(num_levels), ordered = ordered) } forcats/R/expand.R0000644000176200001440000000137114360012612013513 0ustar liggesusers#' Add additional levels to a factor #' #' @param f A factor (or character vector). #' @param ... Additional levels to add to the factor. Levels that already #' exist will be silently ignored. #' @inheritParams fct_relevel #' @export #' @seealso [fct_drop()] to drop unused factor levels. #' @examples #' f <- factor(sample(letters[1:3], 20, replace = TRUE)) #' f #' fct_expand(f, "d", "e", "f") #' fct_expand(f, letters[1:6]) #' fct_expand(f, "Z", after = 0) fct_expand <- function(f, ..., after = Inf) { f <- check_factor(f) check_dots_unnamed() check_number_decimal(after, min = 0) old_levels <- levels(f) new_levels <- chr(...) new_levels <- append(old_levels, setdiff(new_levels, old_levels), after = after) lvls_expand(f, new_levels) } forcats/R/other.R0000644000176200001440000000240414360012612013353 0ustar liggesusers#' Manually replace levels with "other" #' #' @inheritParams fct_lump #' @param keep,drop Pick one of `keep` and `drop`: #' * `keep` will preserve listed levels, replacing all others with #' `other_level`. #' * `drop` will replace listed levels with `other_level`, keeping all #' as is. #' @seealso [fct_lump()] to automatically convert the rarest (or most #' common) levels to "other". #' @export #' @examples #' x <- factor(rep(LETTERS[1:9], times = c(40, 10, 5, 27, 1, 1, 1, 1, 1))) #' #' fct_other(x, keep = c("A", "B")) #' fct_other(x, drop = c("A", "B")) fct_other <- function(f, keep, drop, other_level = "Other") { f <- check_factor(f) check_exclusive(keep, drop) check_string(other_level, allow_na = TRUE) if (!missing(keep)) { check_character(keep) lvls_other(f, levels(f) %in% keep, other_level) } else { check_character(drop) lvls_other(f, !levels(f) %in% drop, other_level) } } # Replace specified levels (if any), with other. # @param keep A logical vector the same length as `levels(f)` lvls_other <- function(f, keep, other_level = "Other") { if (all(keep)) { f } else { new_levels <- ifelse(keep, levels(f), other_level) f <- lvls_revalue(f, new_levels) fct_relevel(f, other_level, after = Inf) } } forcats/R/c.R0000644000176200001440000000235614360012612012462 0ustar liggesusers#' Concatenate factors, combining levels #' #' This is a useful way of patching together factors from multiple sources #' that really should have the same levels but don't. #' #' @param ... <[`dynamic-dots`][rlang::dyn-dots]> Individual #' factors. Uses tidy dots, so you can splice in a list of factors #' with `!!!`. #' @export #' @examples #' fa <- factor("a") #' fb <- factor("b") #' fab <- factor(c("a", "b")) #' #' c(fa, fb, fab) #' fct_c(fa, fb, fab) #' #' # You can also pass a list of factors with !!! #' fs <- list(fa, fb, fab) #' fct_c(!!!fs) fct_c <- function(...) { fs <- list2(...) fs <- check_factor_list(fs, "...") if (length(fs) == 0) { return(factor()) } levels <- lvls_union(fs) all <- unlist(fct_unify(fs, levels), use.names = FALSE) factor(all, levels = levels, exclude = NULL) } #' Unify the levels in a list of factors #' #' @param fs A list of factors #' @param levels Set of levels to apply to every factor. Default to #' union of all factor levels #' @export #' @examples #' fs <- list(factor("a"), factor("b"), factor(c("a", "b"))) #' fct_unify(fs) fct_unify <- function(fs, levels = lvls_union(fs)) { fs <- check_factor_list(fs) check_character(levels) lapply(fs, lvls_expand, new_levels = levels) } forcats/R/cross.R0000644000176200001440000000245314360012612013367 0ustar liggesusers#' Combine levels from two or more factors to create a new factor #' #' Computes a factor whose levels are all the combinations of the levels of the input factors. #' #' @param ... <[`dynamic-dots`][rlang::dyn-dots]> Additional factors #' or character vectors. #' @param sep A character string to separate the levels #' @param keep_empty If TRUE, keep combinations with no observations as levels #' @return The new factor #' #' @export #' @examples #' fruit <- factor(c("apple", "kiwi", "apple", "apple")) #' colour <- factor(c("green", "green", "red", "green")) #' eaten <- c("yes", "no", "yes", "no") #' fct_cross(fruit, colour) #' fct_cross(fruit, colour, eaten) #' fct_cross(fruit, colour, keep_empty = TRUE) fct_cross <- function(..., sep = ":", keep_empty = FALSE) { check_dots_unnamed() check_string(sep, allow_empty = TRUE) check_bool(keep_empty) flist <- list2(...) if (length(flist) == 0) { return(factor()) } .data <- tibble::as_tibble(flist, .name_repair = "minimal") .data <- lapply(.data, check_factor) newf <- exec(paste, !!!.data, sep = sep) old_levels <- lapply(.data, levels) grid <- exec(expand.grid, old_levels) new_levels <- exec(paste, !!!grid, sep = sep) if (!keep_empty) { new_levels <- intersect(new_levels, newf) } factor(newf, levels = new_levels) } forcats/NEWS.md0000644000176200001440000001623614364755560013040 0ustar liggesusers# forcats 1.0.0 ## New features * New `fct_na_value_to_level()` and `fct_na_level_to_value()` to convert NA values to NA levels and vice versa (#337). ## Minor improvement and bug fixes * All functions now validate their inputs, giving more useful errors if you accidentally misspecify an input. * `fct_collapse()` can now use `other_level = NA` (#291). * `fct_count()` works with factors that contain `NA`s in levels. * `fct_explicit_na()` is deprecated in favour of `fct_na_value_to_level()`. * `fct_expand()` gains an `after` argument so that you can choose where the new levels are placed (#138). * `fct_infreq()` gains the ability to weight by another variable using the `w` argument (#261). * `fct_inorder()` now works when not all levels appear in the data (#262). * `fct_lump_prop()` and friends now work correctly if you supply weights and have empty levels (#292). * `fct_lump_n()` and `fct_lump_prop()` will now create an "Other" level even if it only consists of a single level. This makes them consistent with the other `fct_lump_*` functions (#274). * `fct_other()` no longer generates a warning if no levels are replaced with other (#265). * `fct_relevel()`, `fct_cross()`, and `fct_expand()` now error if you name the arguments in `...` since those names are ignored and your code probably doesn't do what you think it does (#319). * `fct_reorder()` and `fct_reorder2()` now remove `NA` values in `.x` with a warning (like `ggplot2::geom_point()` and friends). You can suppress the warning by setting `.na_rm = TRUE` (#315). * `fct_reorder()` and `fct_reorder2()` gain a new `.default` argument that controls the placement of empty levels (including levels that might become empty after removing missing values in `.x`) (#266). * `fct_unique()` now captures implicit missing values if present (#293). # forcats 0.5.2 * New `fct()` which works like `factor()` but errors if values of `x` are not included in the levels specification (#299) * `first2()` and `last2()` now ignore missing values in both `x` and `y` (#303). * Error messages are more informative. # forcats 0.5.1 * Re-license as MIT (#277). * `fct_lump_n()` no longer uses a partial argument name (@malcolmbarrett, #276). # forcats 0.5.0 * `as_factor()` gains a logical method that always returns a factor with levels "FALSE" and "TRUE" (#185). * `fct_c()`, `fct_collapse()` and `fct_recode()` are now explicitly documented as using [dynamic dots](https://rlang.r-lib.org/reference/dyn-dots.html) (@labouz, #234). * `fct_collapse()` now accepts a `other_level` argument, to allow a user-specified `Other` level (@gtm19, #194). It now correctly collapses factors when `other_level` is not `NULL` (#172), and makes `"Other"` the last level (#202) (@gtm19, #172 & #202) * `fct_count()` no longer converts implicit NAs into explicit NAs (#151). * `fct_inseq()` behaves more robustly when factor levels aren't all numbers (#221). * `fct_lump()` has been split up into three new functions: `fct_lump_prop()`, `fct_lump_n()`, and `fct_lump_lowfreq()`. (@jonocarroll, #167, #142). All `fct_lump_()` functions check their inputs more carefully (@robinson_es, #169) * `fct_reorder2()` gains a helper function `first2()`, that sorts `.y` by the first value of `.x` (@jtr13). # forcats 0.4.0 ## New features * `fct_collapse()` gains a `group_other` argument to allow you to group all un-named levels into `"Other"`. (#100, @AmeliaMN) * `fct_cross()` creates a new factor containing the combined levels from two or more input factors, similar to `base::interaction` (@tslumley, #136) * `fct_inseq()` reorders labels in numeric order, if possible (#145, @kbodwin). * `fct_lump_min()` preserves levels that appear at least `min` times (can also be used with the `w` weighted argument) (@robinsones, #142). * `fct_match()` performs validated matching, providing a safer alternative to `f %in% c("x", "y")` which silently returns `FALSE` if `"x"` or `"y"` are not levels of `f` (e.g. because of a typo) (#126, @jonocarroll). * `fct_relevel()` can now level factors using a function that is passed the current levels (#117). * `as_factor()` now has a numeric method. By default, orders factors in numeric order, unlike the other methods which default to order of appearance. (#145, @kbodwin) ## Minor bug fixes and improvements * `fct_count()` gains a parameter to also compute the proportion (@zhiiiyang, #146). * `fct_lump()` now does not change the label if no lumping occurs (@zhiiiyang, #130). * `fct_relabel()` now accepts character input. * `fct_reorder()` and `fct_reorder2()` no longer require that the summary function return a numeric vector of length 1; instead it can return any orderable vector of length 1 (#147). * `fct_reorder()`, `fct_reorder2()` and `as_factor()` now use the ellipsis package to warn if you pass in named components to `...` (#174). # forcats 0.3.0 ## API changes * `fct_c()` now requires explicit splicing with `!!!` if you have a list of factors that you want to combine. This is consistent with an emerging standards for handling `...` throughout the tidyverse. * `fct_reorder()` and `fct_reorder2()` have renamed `fun` to `.fun` to avoid spurious matching of named arguments. ## New features * All functions that take `...` use "tidy" dots: this means that you use can `!!!` to splice in a list of values, and trailing empty arguments are automatically removed. Additionally, all other arguments gain a `.` prefix in order to avoid unhelpful matching of named arguments (#110). * `fct_lump()` gains `w` argument (#70, @wilkox) to weight value frequencies before lumping them together (#68). ## Improvements to NA handling * `as_factor()` and `fct_inorder()` accept NA levels (#98). * `fct_explicit_na()` also replaces NAs encoded in levels. * `fct_lump()` correctly accounts for `NA` values in input (#41) * `lvls_revalue()` preserves NA levels. ## Minor improvements and bug fixes * Test coverage increased from 80% to 99%. * `fct_drop()` now preserves attributes (#83). * `fct_expand()` and `lvls_expand()` now also take character vectors (#99). * `fct_relabel()` now accepts objects coercible to functions by `rlang::as_function` (#91, @alistaire47) # forcats 0.2.0 ## New functions * `as_factor()` which works like `as.factor()` but orders levels by appearance to avoid differences between locales (#39). * `fct_other()` makes it easier to convert selected levels to "other" (#40) * `fct_relabel()` allows programmatic relabeling of levels (#50, @krlmlr). ## Minor improvements and bug fixes * `fct_c()` can take either a list of factors or individual factors (#42). * `fct_drop()` gains `only` argument to restrict which levels are dropped (#69) and no longer adds `NA` level if not present (#52). * `fct_recode()` is now checks that each new value is of length 1 (#56). * `fct_relevel()` gains `after` argument so you can also move levels to the end (or any other position you like) (#29). * `lvls_reorder()`, `fct_inorder()`, and `fct_infreq()` gain an `ordered` argument, allowing you to override the existing "ordered" status (#54). # forcats 0.1.1 * Minor fixes for R CMD check * Add package docs forcats/MD50000644000176200001440000001547414365570422012246 0ustar liggesusersa4f2bfc739dd8db1f4ca5c7fda94fb5b *DESCRIPTION 500bcbd26c47066e09614bf631a00918 *LICENSE d8643b38be14ae3050e4c946bcf48fd0 *NAMESPACE 46e72ec4a8e25f9aadb701c78ed9802c *NEWS.md 45bcddef681bf2f519865ab1db6d3603 *R/anon.R e12ba39cd7eff746d1c2dafffe6bcc76 *R/as_factor.R 57138a5d89f0c3efc4b45a642fbeef7b *R/c.R 7ce872c518a3ab355ec1933732dfdfb0 *R/collapse.R 07c06e6be0443b7d5b9094f11daa406f *R/compat-obj-type.R 0fa60b49bc17479ffacbdf7e1d738a31 *R/compat-purrr.R e39d35d817dec590fcb8b9a2715ba226 *R/compat-types-check.R 7cd532c336e275a55c1363c1875cfe1c *R/count.R 1ab292b28151c8eaf6a7230e3acf625e *R/cross.R 9fa75f8a9b75947ae8c96b056f2aa14d *R/data.R e863fa1fb87c673cf59b74efe9f648d3 *R/drop.R 67fefb42acff5d243ffc343159a6732f *R/expand.R f28c779b0d5425a71ebb352a0aa559a6 *R/explicit_na.R ea44ffa298e055dda7e3d08d2672e0ce *R/fct.R 26855623d2ed5f6bb59935f6e0b4433e *R/forcats-package.R 8e8af8e0dfa91679a26142054c49e374 *R/lump.R d56bc9d2a8dc0a40b2563e21e26a769e *R/lvls.R 96614e2742061692d0f02dae5a98a4fa *R/match.R 5572d511d7b5bb0a9cf55d5b3d9965d6 *R/na.R 49d497d7e7e1a4c718adc3a28f315abb *R/other.R 75559f555d9f11e3570338a1e92d074c *R/recode.R 231a197dff1dd14402de380e8190a250 *R/relabel.R a6b48ecf867ff1761b504d6fb51e8dba *R/relevel.R 8c4baa85cf5ada9e01926644143ef92b *R/reorder.R 2513b5d4ddb3af53f57f9ef368200112 *R/rev.R d55288e95054eef5890808392e382ed7 *R/shift.R 9aebfd5ecc427de639e076a022cb0a53 *R/shuffle.R 4041fd2a6104c1a3de19103c8e31da13 *R/unique.R f1809804fc7f0a9c532172065c273635 *R/utils.R 5dbb0234ec23547b315e778c3110bc46 *README.md f3b930e8d049201ca0dcb6acc97dc69b *build/vignette.rds a6984b4f6c2e70deebeca86c7b3d76ea *data/gss_cat.rda 95f06af1f237da638a0b355e04eab4c7 *inst/doc/forcats.R 46f3fdb2904c70a13a191d3a6c4286cb *inst/doc/forcats.Rmd de81b94175bc6a44fc6dc74869ac5922 *inst/doc/forcats.html b7ee1e2072b474ba8ea48f495361cc2f *man/as_factor.Rd a0bfc7a785c4de707e59045624dbac7b *man/fct.Rd 828d9d9b2fded68dabd4b0f1910f3322 *man/fct_anon.Rd e10a6e7d3527d4948d37dddcdeaeb977 *man/fct_c.Rd 73ac4d6b4fa9232f4baed433303f4e1f *man/fct_collapse.Rd f060da2e6dff2914fb1bc54504ca4a67 *man/fct_count.Rd c86d523021d2533ca33c0fd007ce5fca *man/fct_cross.Rd c4000cf7406ebc3b4800c9fbbab59af2 *man/fct_drop.Rd 1c64c1c3de2a87ba626c80110b5be45e *man/fct_expand.Rd 0aa6490d8092593b06eed62e27c162a7 *man/fct_explicit_na.Rd 098e22e78e75a9d1ae2ccc4d55d1b913 *man/fct_inorder.Rd ab8884a33b8bd3c25838c82a779848ec *man/fct_lump.Rd 08728197b424674bc62f9727b8dc51e7 *man/fct_match.Rd 0cc64f7f0beef76ab257901f4da3e7bf *man/fct_na_value_to_level.Rd af91762b039638371c4954746eb8a126 *man/fct_other.Rd d86392e9a40f6ce7c5fdfeca710fcf99 *man/fct_recode.Rd 608418138301fd31810cc5316ff03e76 *man/fct_relabel.Rd 47ad24b67aa7406c9917983d6245dd81 *man/fct_relevel.Rd ab5048dcc2330ce7a648d508016faf77 *man/fct_reorder.Rd cf766cb9ca0d896a0955c0ad191b1e78 *man/fct_rev.Rd fa07b6aa7ca3c5c73b573f497dc5e68e *man/fct_shift.Rd e37efbf13b7923999c1412bcb819a57b *man/fct_shuffle.Rd 9122ce4e63cfe89aaed8a006a6748c1d *man/fct_unify.Rd 8a9afe7c2c73a2e1060049afef0bbfef *man/fct_unique.Rd 283fc49792198c0b132add3a4a7169fb *man/figures/README-ordered-plot-1.png 472495682a824c9b876005bd0ab7841d *man/figures/README-unordered-plot-1.png cb1e46f469cfbbbde29c8b5113e1d789 *man/figures/lifecycle-archived.svg c0d2e5a54f1fa4ff02bf9533079dd1f7 *man/figures/lifecycle-defunct.svg a1b8c987c676c16af790f563f96cbb1f *man/figures/lifecycle-deprecated.svg c3978703d8f40f2679795335715e98f4 *man/figures/lifecycle-experimental.svg 952b59dc07b171b97d5d982924244f61 *man/figures/lifecycle-maturing.svg 27b879bf3677ea76e3991d56ab324081 *man/figures/lifecycle-questioning.svg 53b3f893324260b737b3c46ed2a0e643 *man/figures/lifecycle-stable.svg 1c1fe7a759b86dc6dbcbe7797ab8246c *man/figures/lifecycle-superseded.svg 3252fda93a16a69b6deb795c4866b7cb *man/figures/logo.png f6dfe20c7e2230b884b5326e73cf2a51 *man/forcats-package.Rd 8ff049d2c0b87cd6bd4544e5970a51c0 *man/gss_cat.Rd 679f67140aa7a12ffa8e2644324a1231 *man/lvls.Rd 50ed89e2798ad529ca69dad8d85851dd *man/lvls_union.Rd 0f020b37daf27c2fd4c78c574285ef1b *man/pipe.Rd 7b0f13301043822509977d02384cb6e9 *tests/testthat.R a9fa58319df7a1eb56ccc0035180403b *tests/testthat/_snaps/anon.md 1f3c82f622263d1b7d194e57a0f633ca *tests/testthat/_snaps/c.md 078dee87e4d76d1c2594ed1e2a679adc *tests/testthat/_snaps/collapse.md 4495edfc0899d1987d740affcd43eba4 *tests/testthat/_snaps/count.md e024ffec65492ac204428fdb5bf10840 *tests/testthat/_snaps/cross.md 5a141f3f06c9ca2357a6458d05827419 *tests/testthat/_snaps/drop.md 31937a31e9fdaba4878aeb40efc0df8f *tests/testthat/_snaps/expand.md 1807a87899f0357239fd58928383dd18 *tests/testthat/_snaps/explicit_na.md a83482d18cdc1e7a35a433facc368c80 *tests/testthat/_snaps/fct.md 66c0a4c6e409cab91df9e52b01eea2c6 *tests/testthat/_snaps/lump.md 38c90b8beac6125efff5e2ff49555203 *tests/testthat/_snaps/lvls.md 184c9ec331c0a71277c2352e3f79fa37 *tests/testthat/_snaps/match.md a9a1281db7b6ad751c9ca8c95c24e59c *tests/testthat/_snaps/na.md 09e8e83b8a1a76ab1dbefb1a8e5d61b0 *tests/testthat/_snaps/other.md 379df00534fe932c6b7025ecc695cedc *tests/testthat/_snaps/recode.md 03305f450a49dd624654bc260c965358 *tests/testthat/_snaps/relabel.md a5a3f91ef129610a2fd7491f4cdfcfce *tests/testthat/_snaps/relevel.md 0438e01dace419f8567dfa4380819306 *tests/testthat/_snaps/reorder.md 2653e9a498d2a6797ac8422e90c910dd *tests/testthat/_snaps/shift.md dc5755e934b57c53b9e13b97eeea4b98 *tests/testthat/_snaps/utils.md 66091c3cb1f0f78860ad430b40b637ca *tests/testthat/test-anon.R 488e881283f84aec6fb0acf6aa107c28 *tests/testthat/test-as_factor.R 1b6a13e44c0137fb07f41919aaeda0f5 *tests/testthat/test-c.R b8a73a7395a395422776a00bed5d23c4 *tests/testthat/test-collapse.R 4667169751f4b8d86df2350d2d952742 *tests/testthat/test-count.R c36c6fe0820c27a8ee7559b3025e3145 *tests/testthat/test-cross.R 5759a49bd0717ebca785ddc10f6b1db8 *tests/testthat/test-drop.R 43a18734cade7118759a6f17b64a69a4 *tests/testthat/test-expand.R c9ccc31b9ce68abbf1c3cb47e1b6c0df *tests/testthat/test-explicit_na.R af7602e103c465989a7d46adc23c4466 *tests/testthat/test-fct.R f5ca3fea7e341b725200debb6fad4a7a *tests/testthat/test-lump.R d199afcedfd14af1eb67c7ffd0aa48fb *tests/testthat/test-lvls.R f1c61b38debb5db82e40cb1654c50709 *tests/testthat/test-match.R 210ce289487d4a5d01b378b7597fa701 *tests/testthat/test-na.R f6a7caf23ea42136e7f039a4e0661533 *tests/testthat/test-other.R 97cf819454725614cf97d0e6f23ac029 *tests/testthat/test-recode.R 71237f456927f53d2aeb1ba3f03e52f0 *tests/testthat/test-relabel.R 4c2c10190e8496a878055c547986cd36 *tests/testthat/test-relevel.R 22a140307f414469f6672661d068ed55 *tests/testthat/test-reorder.R d9ff4cd69cfe07632abc902f83c86e65 *tests/testthat/test-rev.R f6174b12a8ed4bf8d1ea0df40158dd5a *tests/testthat/test-shift.R a79cc6f56ea23f5614ac7b02dbc982e5 *tests/testthat/test-shuffle.R 8b6ae7bfc489dac59c69ca0012f1601c *tests/testthat/test-unique.R bbdb91afad85f5260e20a9da8256afac *tests/testthat/test-utils.R 46f3fdb2904c70a13a191d3a6c4286cb *vignettes/forcats.Rmd forcats/inst/0000755000176200001440000000000014364755577012717 5ustar liggesusersforcats/inst/doc/0000755000176200001440000000000014364755577013464 5ustar liggesusersforcats/inst/doc/forcats.R0000644000176200001440000000560214364755577015253 0ustar liggesusers## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----message = FALSE---------------------------------------------------------- library(dplyr) library(ggplot2) library(forcats) ## ----initial-plot------------------------------------------------------------- ggplot(starwars, aes(y = hair_color)) + geom_bar() ## ----fct-infreq-hair---------------------------------------------------------- ggplot(starwars, aes(y = fct_infreq(hair_color))) + geom_bar() ## ----------------------------------------------------------------------------- f <- factor(c("x", "y", NA)) levels(f) is.na(f) ## ----------------------------------------------------------------------------- f <- factor(c("x", "y", NA), exclude = NULL) levels(f) is.na(f) ## ----------------------------------------------------------------------------- ggplot(starwars, aes(y = fct_infreq(fct_na_value_to_level(hair_color)))) + geom_bar() + labs(y = "Hair color") ## ----------------------------------------------------------------------------- starwars %>% count(skin_color, sort = TRUE) ## ----------------------------------------------------------------------------- starwars %>% mutate(skin_color = fct_lump(skin_color, n = 5)) %>% count(skin_color, sort = TRUE) ## ----------------------------------------------------------------------------- starwars %>% mutate(skin_color = fct_lump(skin_color, prop = .1)) %>% count(skin_color, sort = TRUE) ## ----------------------------------------------------------------------------- starwars %>% mutate(skin_color = fct_lump(skin_color, prop = .1, other_level = "extra")) %>% count(skin_color, sort = TRUE) ## ----fct-lump-mean------------------------------------------------------------ avg_mass_eye_color <- starwars %>% mutate(eye_color = fct_lump(eye_color, n = 6)) %>% group_by(eye_color) %>% summarise(mean_mass = mean(mass, na.rm = TRUE)) avg_mass_eye_color ## ----fct-reorder-------------------------------------------------------------- avg_mass_eye_color %>% mutate(eye_color = fct_reorder(eye_color, mean_mass)) %>% ggplot(aes(x = eye_color, y = mean_mass)) + geom_col() ## ----------------------------------------------------------------------------- gss_cat %>% count(rincome) ## ----------------------------------------------------------------------------- levels(gss_cat$rincome) ## ----------------------------------------------------------------------------- reshuffled_income <- gss_cat$rincome %>% fct_shuffle() levels(reshuffled_income) ## ----------------------------------------------------------------------------- fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999")) %>% levels() ## ----------------------------------------------------------------------------- fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999"), after = 1) %>% levels() forcats/inst/doc/forcats.Rmd0000644000176200001440000001671014360103324015543 0ustar liggesusers--- title: "Introduction to forcats" author: "Emily Robinson" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to forcats} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` The goal of the **forcats** package is to provide a suite of useful tools that solve common problems with factors. Factors are useful when you have categorical data, variables that have a fixed and known set of values, and when you want to display character vectors in non-alphabetical order. If you want to learn more, the best place to start is the [chapter on factors](https://r4ds.had.co.nz/factors.html) in R for Data Science. ## Ordering by frequency ```{r message = FALSE} library(dplyr) library(ggplot2) library(forcats) ``` Let's try answering the question, "what are the most common hair colors of star wars characters?" Let's start off by making a bar plot: ```{r initial-plot} #| fig.alt: > #| A bar chart of hair color of starwars characters. The bars are #| alphabetically ordered, making it hard to see general patterns. ggplot(starwars, aes(y = hair_color)) + geom_bar() ``` That's okay, but it would be more helpful the graph was ordered by count. This is a case of an **unordered** categorical variable where we want it ordered by its frequency. To do so, we can use the function `fct_infreq()`: ```{r fct-infreq-hair} #| fig.alt: > #| The bar chart of hair color, now ordered so that the least #| frequent colours come first and the most frequent colors come last. #| This makes it easy to see that the most common hair color is none #| (~35), followed by brown (~18), then black (~12). Surprisingly, #| NAs are at the top of the graph, even though there are ~5 NAs and #| other colors have smaller values. ggplot(starwars, aes(y = fct_infreq(hair_color))) + geom_bar() ``` Note that `fct_infreq()` it automatically puts NA at the top, even though that doesn't have the smallest number of entries. It's a little surprising that the `NA` bar isn't ordered by frequency. To understand why we need to make a brief digression to discuss `NA`s in values vs. `NA`s in levels ## `NAs`s in levels and values There are two ways to represent a missing value in a factor: - You can include it in the values of the factor; it does not appear in the levels and `is.na()` reports it as missing. This is how missing values are encoded in a factor by default: ```{r} f <- factor(c("x", "y", NA)) levels(f) is.na(f) ``` - You can include it in the levels of the factor and `is.na()` does not report it as missing. This requires a little more work to create, because, by default, `factor()` uses `exclude = NA`, meaning that missing values are not included in the levels. You can force `NA` to be included by setting `exclude = NULL`: ```{r} f <- factor(c("x", "y", NA), exclude = NULL) levels(f) is.na(f) ``` `NA`s in the values tend to be best for data analysis, since `is.na()` works as you'd expect. `NA`s in the levels can be useful for display if you need to control where they appear in a table or a plot. To fix the issue above, we can use `fct_na_value_to_level()` to convert the `NA` in the value to an NA in the levels. Then they appear where you'd expect: ```{r} #| fig.alt: > #| The bar chart of hair color, now ordered so that NAs are #| ordered where you'd expect: in between white (4) and black (12). ggplot(starwars, aes(y = fct_infreq(fct_na_value_to_level(hair_color)))) + geom_bar() + labs(y = "Hair color") ``` (If you need the opposite operation, you can use `fct_na_level_to_value()`.) ## Combining levels Let's take a look at skin color now: ```{r} starwars %>% count(skin_color, sort = TRUE) ``` We see that there's 31 different skin colors - if we want to make a plot this would be way too many to display! Let's reduce it to only be the top 5. We can use `fct_lump()` to "lump" all the infrequent colors into one factor, "other." The argument `n` is the number of levels we want to keep. ```{r} starwars %>% mutate(skin_color = fct_lump(skin_color, n = 5)) %>% count(skin_color, sort = TRUE) ``` We could also have used `prop` instead, which keeps all the levels that appear at least `prop` of the time. For example, let's keep skin colors that at least 10% of the characters have: ```{r} starwars %>% mutate(skin_color = fct_lump(skin_color, prop = .1)) %>% count(skin_color, sort = TRUE) ``` Only light and fair remain; everything else is other. If you wanted to call it something than "other", you can change it with the argument `other_level`: ```{r} starwars %>% mutate(skin_color = fct_lump(skin_color, prop = .1, other_level = "extra")) %>% count(skin_color, sort = TRUE) ``` What if we wanted to see if the average mass differed by eye color? We'll only look at the 6 most popular eye colors and remove `NA`s. ```{r fct-lump-mean} avg_mass_eye_color <- starwars %>% mutate(eye_color = fct_lump(eye_color, n = 6)) %>% group_by(eye_color) %>% summarise(mean_mass = mean(mass, na.rm = TRUE)) avg_mass_eye_color ``` ## Ordering by another variable It looks like people (or at least one person) with orange eyes are definitely heavier! If we wanted to make a graph, it would be nice if it was ordered by `mean_mass`. We can do this with `fct_reorder()`, which reorders one variable by another. ```{r fct-reorder} #| fig-alt: > #| A column chart with eye color on the x-axis and mean mass on the #| y-axis. The bars are ordered by mean_mass, so that the tallest bar #| (orange eye color with mean mass of ~275) is at the far right. avg_mass_eye_color %>% mutate(eye_color = fct_reorder(eye_color, mean_mass)) %>% ggplot(aes(x = eye_color, y = mean_mass)) + geom_col() ``` ## Manually reordering Let's switch to using another dataset, `gss_cat`, the general social survey. What is the income distribution among the respondents? ```{r} gss_cat %>% count(rincome) ``` Notice that the income levels are in the correct order - they start with the non-answers and then go from highest to lowest. This is the same order you'd see if you plotted it as a bar chart. This is not a coincidence. When you're working with ordinal data, where there is an order, you can have an ordered factor. You can examine them with the base function `levels()`, which prints them in order: ```{r} levels(gss_cat$rincome) ``` But what if your factor came in the wrong order? Let's simulate that by reordering the levels of `rincome` randomly with `fct_shuffle()`: ```{r} reshuffled_income <- gss_cat$rincome %>% fct_shuffle() levels(reshuffled_income) ``` Now if we plotted it, it would show in this order, which is all over the place! How can we fix this and put it in the right order? We can use the function `fct_relevel()` when we need to manually reorder our factor levels. In addition to the factor, you give it a character vector of level names, and specify where you want to move them. It defaults to moving them to the front, but you can move them after another level with the argument `after`. If you want to move it to the end, you set `after` equal to `Inf`. For example, let's say we wanted to move `Lt $1000` and `$1000 to 2999` to the front. We would write: ```{r} fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999")) %>% levels() ``` What if we want to move them to the second and third place? ```{r} fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999"), after = 1) %>% levels() ``` forcats/inst/doc/forcats.html0000644000176200001440000035466014364755577016031 0ustar liggesusers Introduction to forcats

Introduction to forcats

Emily Robinson

The goal of the forcats package is to provide a suite of useful tools that solve common problems with factors. Factors are useful when you have categorical data, variables that have a fixed and known set of values, and when you want to display character vectors in non-alphabetical order. If you want to learn more, the best place to start is the chapter on factors in R for Data Science.

Ordering by frequency

library(dplyr)
library(ggplot2)
library(forcats)

Let’s try answering the question, “what are the most common hair colors of star wars characters?” Let’s start off by making a bar plot:

ggplot(starwars, aes(y = hair_color)) + 
  geom_bar()

A bar chart of hair color of starwars characters. The bars are alphabetically ordered, making it hard to see general patterns.

That’s okay, but it would be more helpful the graph was ordered by count. This is a case of an unordered categorical variable where we want it ordered by its frequency. To do so, we can use the function fct_infreq():

ggplot(starwars, aes(y = fct_infreq(hair_color))) + 
  geom_bar()

The bar chart of hair color, now ordered so that the least frequent colours come first and the most frequent colors come last. This makes it easy to see that the most common hair color is none (~35), followed by brown (~18), then black (~12). Surprisingly, NAs are at the top of the graph, even though there are ~5 NAs and other colors have smaller values.

Note that fct_infreq() it automatically puts NA at the top, even though that doesn’t have the smallest number of entries.

It’s a little surprising that the NA bar isn’t ordered by frequency. To understand why we need to make a brief digression to discuss NAs in values vs. NAs in levels

NAss in levels and values

There are two ways to represent a missing value in a factor:

  • You can include it in the values of the factor; it does not appear in the levels and is.na() reports it as missing. This is how missing values are encoded in a factor by default:

    f <- factor(c("x", "y", NA))
    levels(f)
    #> [1] "x" "y"
    is.na(f)
    #> [1] FALSE FALSE  TRUE
  • You can include it in the levels of the factor and is.na() does not report it as missing. This requires a little more work to create, because, by default, factor() uses exclude = NA, meaning that missing values are not included in the levels. You can force NA to be included by setting exclude = NULL:

    f <- factor(c("x", "y", NA), exclude = NULL)
    levels(f)
    #> [1] "x" "y" NA
    is.na(f)
    #> [1] FALSE FALSE FALSE

NAs in the values tend to be best for data analysis, since is.na() works as you’d expect. NAs in the levels can be useful for display if you need to control where they appear in a table or a plot.

To fix the issue above, we can use fct_na_value_to_level() to convert the NA in the value to an NA in the levels. Then they appear where you’d expect:

ggplot(starwars, aes(y = fct_infreq(fct_na_value_to_level(hair_color)))) + 
  geom_bar() + 
  labs(y = "Hair color")

The bar chart of hair color, now ordered so that NAs are ordered where you'd expect: in between white (4) and black (12).

(If you need the opposite operation, you can use fct_na_level_to_value().)

Combining levels

Let’s take a look at skin color now:

starwars %>%
  count(skin_color, sort = TRUE)
#> # A tibble: 31 × 2
#>    skin_color     n
#>    <chr>      <int>
#>  1 fair          17
#>  2 light         11
#>  3 dark           6
#>  4 green          6
#>  5 grey           6
#>  6 pale           5
#>  7 brown          4
#>  8 blue           2
#>  9 blue, grey     2
#> 10 orange         2
#> # … with 21 more rows

We see that there’s 31 different skin colors - if we want to make a plot this would be way too many to display! Let’s reduce it to only be the top 5. We can use fct_lump() to “lump” all the infrequent colors into one factor, “other.” The argument n is the number of levels we want to keep.

starwars %>%
  mutate(skin_color = fct_lump(skin_color, n = 5)) %>%
  count(skin_color, sort = TRUE)
#> # A tibble: 6 × 2
#>   skin_color     n
#>   <fct>      <int>
#> 1 Other         41
#> 2 fair          17
#> 3 light         11
#> 4 dark           6
#> 5 green          6
#> 6 grey           6

We could also have used prop instead, which keeps all the levels that appear at least prop of the time. For example, let’s keep skin colors that at least 10% of the characters have:

starwars %>%
  mutate(skin_color = fct_lump(skin_color, prop = .1)) %>%
  count(skin_color, sort = TRUE)
#> # A tibble: 3 × 2
#>   skin_color     n
#>   <fct>      <int>
#> 1 Other         59
#> 2 fair          17
#> 3 light         11

Only light and fair remain; everything else is other.

If you wanted to call it something than “other”, you can change it with the argument other_level:

starwars %>%
  mutate(skin_color = fct_lump(skin_color, prop = .1, other_level = "extra")) %>%
  count(skin_color, sort = TRUE)
#> # A tibble: 3 × 2
#>   skin_color     n
#>   <fct>      <int>
#> 1 extra         59
#> 2 fair          17
#> 3 light         11

What if we wanted to see if the average mass differed by eye color? We’ll only look at the 6 most popular eye colors and remove NAs.

avg_mass_eye_color <- starwars %>%
  mutate(eye_color = fct_lump(eye_color, n = 6)) %>%
  group_by(eye_color) %>%
  summarise(mean_mass = mean(mass, na.rm = TRUE))

avg_mass_eye_color
#> # A tibble: 7 × 2
#>   eye_color mean_mass
#>   <fct>         <dbl>
#> 1 black          76.3
#> 2 blue           86.5
#> 3 brown          66.1
#> 4 orange        282. 
#> 5 red            81.4
#> 6 yellow         81.1
#> 7 Other          68.4

Ordering by another variable

It looks like people (or at least one person) with orange eyes are definitely heavier! If we wanted to make a graph, it would be nice if it was ordered by mean_mass. We can do this with fct_reorder(), which reorders one variable by another.

avg_mass_eye_color %>%
  mutate(eye_color = fct_reorder(eye_color, mean_mass)) %>%
  ggplot(aes(x = eye_color, y = mean_mass)) + 
  geom_col()

A column chart with eye color on the x-axis and mean mass on the y-axis. The bars are ordered by mean_mass, so that the tallest bar (orange eye color with mean mass of ~275) is at the far right.

Manually reordering

Let’s switch to using another dataset, gss_cat, the general social survey. What is the income distribution among the respondents?

gss_cat %>%
  count(rincome)
#> # A tibble: 16 × 2
#>    rincome            n
#>    <fct>          <int>
#>  1 No answer        183
#>  2 Don't know       267
#>  3 Refused          975
#>  4 $25000 or more  7363
#>  5 $20000 - 24999  1283
#>  6 $15000 - 19999  1048
#>  7 $10000 - 14999  1168
#>  8 $8000 to 9999    340
#>  9 $7000 to 7999    188
#> 10 $6000 to 6999    215
#> 11 $5000 to 5999    227
#> 12 $4000 to 4999    226
#> 13 $3000 to 3999    276
#> 14 $1000 to 2999    395
#> 15 Lt $1000         286
#> 16 Not applicable  7043

Notice that the income levels are in the correct order - they start with the non-answers and then go from highest to lowest. This is the same order you’d see if you plotted it as a bar chart. This is not a coincidence. When you’re working with ordinal data, where there is an order, you can have an ordered factor. You can examine them with the base function levels(), which prints them in order:

levels(gss_cat$rincome)
#>  [1] "No answer"      "Don't know"     "Refused"        "$25000 or more"
#>  [5] "$20000 - 24999" "$15000 - 19999" "$10000 - 14999" "$8000 to 9999" 
#>  [9] "$7000 to 7999"  "$6000 to 6999"  "$5000 to 5999"  "$4000 to 4999" 
#> [13] "$3000 to 3999"  "$1000 to 2999"  "Lt $1000"       "Not applicable"

But what if your factor came in the wrong order? Let’s simulate that by reordering the levels of rincome randomly with fct_shuffle():

reshuffled_income <- gss_cat$rincome %>%
  fct_shuffle()

levels(reshuffled_income)
#>  [1] "Refused"        "$1000 to 2999"  "$7000 to 7999"  "$10000 - 14999"
#>  [5] "$25000 or more" "$15000 - 19999" "Lt $1000"       "$3000 to 3999" 
#>  [9] "$6000 to 6999"  "$5000 to 5999"  "$8000 to 9999"  "No answer"     
#> [13] "Not applicable" "$20000 - 24999" "Don't know"     "$4000 to 4999"

Now if we plotted it, it would show in this order, which is all over the place! How can we fix this and put it in the right order?

We can use the function fct_relevel() when we need to manually reorder our factor levels. In addition to the factor, you give it a character vector of level names, and specify where you want to move them. It defaults to moving them to the front, but you can move them after another level with the argument after. If you want to move it to the end, you set after equal to Inf.

For example, let’s say we wanted to move Lt $1000 and $1000 to 2999 to the front. We would write:

fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999")) %>%
  levels()
#>  [1] "Lt $1000"       "$1000 to 2999"  "Refused"        "$7000 to 7999" 
#>  [5] "$10000 - 14999" "$25000 or more" "$15000 - 19999" "$3000 to 3999" 
#>  [9] "$6000 to 6999"  "$5000 to 5999"  "$8000 to 9999"  "No answer"     
#> [13] "Not applicable" "$20000 - 24999" "Don't know"     "$4000 to 4999"

What if we want to move them to the second and third place?

fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999"), after = 1) %>%
  levels()
#>  [1] "Refused"        "Lt $1000"       "$1000 to 2999"  "$7000 to 7999" 
#>  [5] "$10000 - 14999" "$25000 or more" "$15000 - 19999" "$3000 to 3999" 
#>  [9] "$6000 to 6999"  "$5000 to 5999"  "$8000 to 9999"  "No answer"     
#> [13] "Not applicable" "$20000 - 24999" "Don't know"     "$4000 to 4999"