rstatix/0000755000176200001440000000000014011734072011747 5ustar liggesusersrstatix/NAMESPACE0000644000176200001440000001062114011727567013201 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(plot,anova_test) S3method(print,anova_test) S3method(tukey_hsd,data.frame) S3method(tukey_hsd,default) S3method(tukey_hsd,lm) export("%>%") export(Anova) export(Manova) export(add_significance) export(add_x_position) export(add_xy_position) export(add_y_position) export(adjust_pvalue) export(anova_summary) export(anova_test) export(as_cor_mat) export(augment) export(binom_test) export(box_m) export(chisq_descriptives) export(chisq_test) export(cochran_qtest) export(cohens_d) export(convert_as_factor) export(cor_as_symbols) export(cor_gather) export(cor_get_pval) export(cor_mark_significant) export(cor_mat) export(cor_plot) export(cor_pmat) export(cor_reorder) export(cor_select) export(cor_spread) export(cor_test) export(counts_to_cases) export(cramer_v) export(create_test_label) export(desc) export(df_arrange) export(df_get_var_names) export(df_group_by) export(df_label_both) export(df_label_value) export(df_nest_by) export(df_select) export(df_split_by) export(df_unite) export(df_unite_factors) export(doo) export(drop_na) export(dunn_test) export(emmeans_test) export(eta_squared) export(expected_freq) export(factorial_design) export(filter) export(fisher_test) export(freq_table) export(friedman_effsize) export(friedman_test) export(games_howell_test) export(gather) export(get_anova_table) export(get_comparisons) export(get_description) export(get_emmeans) export(get_mode) export(get_n) export(get_pwc_label) export(get_summary_stats) export(get_test_label) export(get_y_position) export(group_by) export(identify_outliers) export(is_extreme) export(is_outlier) export(kruskal_effsize) export(kruskal_test) export(levene_test) export(mahalanobis_distance) export(make_clean_names) export(mcnemar_test) export(mshapiro_test) export(multinom_test) export(mutate) export(observed_freq) export(p_adj_names) export(p_detect) export(p_format) export(p_mark_significant) export(p_names) export(p_round) export(pairwise_binom_test) export(pairwise_binom_test_against_p) export(pairwise_chisq_gof_test) export(pairwise_chisq_test_against_p) export(pairwise_fisher_test) export(pairwise_mcnemar_test) export(pairwise_prop_test) export(pairwise_sign_test) export(pairwise_t_test) export(pairwise_wilcox_test) export(partial_eta_squared) export(pearson_residuals) export(prop_test) export(prop_trend_test) export(pull_lower_triangle) export(pull_triangle) export(pull_upper_triangle) export(remove_ns) export(reorder_levels) export(replace_lower_triangle) export(replace_triangle) export(replace_upper_triangle) export(row_wise_fisher_test) export(row_wise_prop_test) export(sample_n_by) export(select) export(set_ref_level) export(shapiro_test) export(sign_test) export(spread) export(std_residuals) export(t_test) export(tibble) export(tidy) export(tukey_hsd) export(welch_anova_test) export(wilcox_effsize) export(wilcox_test) importFrom(broom,tidy) importFrom(car,Anova) importFrom(car,Manova) importFrom(dplyr,as_data_frame) importFrom(dplyr,bind_rows) importFrom(dplyr,desc) importFrom(dplyr,do) importFrom(dplyr,everything) importFrom(dplyr,filter) importFrom(dplyr,group_by) importFrom(dplyr,is_grouped_df) importFrom(dplyr,left_join) importFrom(dplyr,mutate) importFrom(dplyr,mutate_all) importFrom(dplyr,mutate_at) importFrom(dplyr,n) importFrom(dplyr,pull) importFrom(dplyr,rename) importFrom(dplyr,select) importFrom(dplyr,summarise) importFrom(dplyr,tibble) importFrom(dplyr,ungroup) importFrom(generics,augment) importFrom(generics,tidy) importFrom(magrittr,"%<>%") importFrom(magrittr,"%>%") importFrom(magrittr,extract) importFrom(magrittr,set_colnames) importFrom(purrr,map) importFrom(purrr,map2) importFrom(rlang,"!!!") importFrom(rlang,"!!") importFrom(rlang,":=") importFrom(rlang,.data) importFrom(rlang,quo_name) importFrom(rlang,quos) importFrom(rlang,sym) importFrom(rlang,syms) importFrom(stats,IQR) importFrom(stats,TukeyHSD) importFrom(stats,as.formula) importFrom(stats,complete.cases) importFrom(stats,cor.test) importFrom(stats,cov) importFrom(stats,median) importFrom(stats,pbinom) importFrom(stats,pchisq) importFrom(stats,qbinom) importFrom(stats,quantile) importFrom(stats,sd) importFrom(stats,shapiro.test) importFrom(stats,t.test) importFrom(stats,var) importFrom(stats,wilcox.test) importFrom(tibble,add_column) importFrom(tibble,as_tibble) importFrom(tibble,tibble) importFrom(tidyr,drop_na) importFrom(tidyr,gather) importFrom(tidyr,nest) importFrom(tidyr,separate) importFrom(tidyr,spread) rstatix/tools/0000755000176200001440000000000014011711140013076 5ustar liggesusersrstatix/tools/README-unnamed-chunk-8-1.png0000644000176200001440000010003213501114407017601 0ustar liggesusersPNG  IHDRǵ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx]|TEBB =ޕ^ ]D@"(Վ `Q((*{'t{v6\+ݽ8{[f;3;!L#0vݵ0# `/#0v ;xn6#0,`FN``fF#),ٌ#w`;Ev<7`0#`ӎf3#F`vlF`X;0"N;0 ~FSXisF`#0v ;xn6#0,`FN``fF#),ٌ#w`;Ev<7`0#`ӎf3#F`vlF`X;0"N;0 ~FSXisF`#0v ;xn6#0,`FN``fF#),ٌ#w`;Ev<7`0#`ӎf3#F`vlF`X;0"N;0 ~FSXisF`#0v ;xn6#0,`FN``fF#),mvTToߎ+W ##윞`4 vX3g*W֭[B h߾=]56F0Ffoɏ9Ç믿Fxx86n܈W^z2 |aQ۲{nlݺ6l]G&MP|y_G06 ,KZjI1k֬lՓph޼9h@&F=Xd{}fzj(Q˗/ǭ[۸qcݻ#,l,uh"DDDH-[^zI͛7#%%ʕoF1Xdc/^W_Q< VZ%}ɒ%h֬._yĈw/^ݻcƌJ0Juƴi/"==SLӧA3]v!((H{5~Jhر8~8&LQF6m"\,~x.Ȑ!ݻ,;!!ݺu{m6k%,Bٲe a :kV kעCYȂ0[kNg϶m@3|ɓ'q%xxxdhw0m cbDm\Ν+ úJH;w.?xLF6x8ճMklDBf97{a`lrP".`4 Mtv !@FϣZja9aE`p"##_x-aG`$|`@wG?s+Fx<nN>44111c|}}2*#@e(>66'N@*U(je=z\1#yƯAⳗX01"F`|9pZ\MBsV ɚWz2l;r#cfB uo6!6!E| gh:ZD%I ft#1X1ykNyݰkn'#i[e]}`l ^Vug0]@ԹWoF&``T0G/')8 )h%O;pZ s#,Bւ'ʼn߾VUBR}N`Q`TG_wˎH5_Cxt"~{F6``'DHY3MwEdlfX k3@?cHh_=vFs=ve.RˬkKH ן}$Я s;L#mX?Cڵ([ހ;Q!&F6̌1cϺ`(bֈl_GZ%0zhqǞ~c„ Sʕ+?tOBBB0wܬ[?#U///ԯ_SLR޽=ߏfD Eѷo_:t(+?hբ%-=N0F@ɓ1d9]wt;wUVaȑӧ~w NoNN2b54O 6ٳgqEj ]t/bJ XMϟwK~URrIywޘ:u*N8!g4ر#BC˔)ŋKqB89jW8;:jbwrAPaÆ RCjUqeŊRꫯJ5իW *36 \T=D4&&1cp wŞ={3d+#***5+T!n cV)MGӷ~+0~~~}wߡXb *Xҝ 4w7oޜt4Eǧ6m_|cN]=۷gAn*=n!v'ނGlH%KdcE%ˆ \z5((;#H;VsI.oИ9sZʕ+.L=j(cDNIMC"y=}''',ZHzf߱cGodX~T e@!BTDShWͥH3h 8ax%}?}k6hٲ%Ȇpll`ƍXtI#iæTQl 4W4ǗN'TC-Zs(h_Ik%ܤ!IoODw}@~'ӆD'V KبK헌c$#[3mj𙚚*sΡ a@:~"x?RJ -xzj%^\fG@}L3@C9=}r%bOekTĎ[:]KD {;Yy=ZGuI>}Kw^[N_Ufb +ظRD5QjZ6 =h֯M6I,I.l2؏?O?ŋѫW/Xl.;3R'G3˞.x9MpW'Vyq "=[@;iO{r !@[L@aE@!O_n&[[9;A(]8No䁄 ,$0}Ei{B@!fD+6Б wW'|F|7A7/H(8Nt#`o($=M^;.!^ei߱PEePt@zx>rЎW͓_kfbsUZ:?­18:d{2ӥ+' oFө9DWorM8uu}X3:7 Ƹ뀠]Np#ʁ^kI&@; +Ž妊I w5AzyM=g]LF**]vMHtRT֜+reb xXI' 􄧂qi_7~ i6O8/p7#`(Ľ߸Fv1և m*Q|?/Wӯ!v,V-njQ#_sV߮]6ߏf͚e{=#``ϨP Z{O;w,ՓL5jKߌ# ``' }*?q{6] K8! |<]A V 14YBj3(?$$DӇAx {6fN' Z MC'40Ga)p8?L#(xSHt$7C5\0#0FW,e66_ݿqqq6}&˴L316Ћ^A}RYM4vvc^c`` ZVJKvzڹڮ];+qL+WD LV(HLLB\%#<lPS.`@W6Mdn$ܕ8w.n݋BL\"E(l?owOP%8J-;cv aXs7":'%"%50Ga3qsua=/O7} wo *11wF8q&& I;u~3҅̈́z)5u3ޏ \廱1="D٢^(/"~6( h|0u֊f4B-6>Iftr:L}758 Xn4IXpᤔ4eT]Y b-~-+8ZhE6;J,% K0x9yy)Q4,lLc62. SW?O%Ŧ~vpJZ:ReeW ~zӿFuiUG߭v nG2$f}UӇ031[ qo^E kr51g 4;QSמ+0~  5&i_x!,߭c*Y0,7}n2H, io_b#tm.e}!|IgW[>` tLh(Ϙ2{v\mLN4,lrg>M ̃ۊ A4%2x}K)z_Be "P3p'W<,,5pz$Huv4Nߡ*ARφ!-{ Ii[F-^.¢,Zy.[TDZ1kټt;_`>ŷ9s V6+\?#:,Trw0[OREW+H4kYl>~3$~DR w+O a |L8l_ ->$6~OhuՏ-oZ LmB Bҭ?9pІ9};c TnmVFDF``7oĉ̐yF3lf: 5b72#PX``Ş6l~w9Xwfv1-9v줞. [C{,==44y*|ӆ?D!CisbvCW"W`"%x2X@ !9sL,Y7o*_ѣG=z4|}}-[ݴxǰj*,Y2+?h&W=ݱ+'$kb2y[kM0 ^ۣjԨ!'Oʇ4/X@;{6O>%%sEJгgOTX3f ZEQ=BΎ(6B3$UӤ {3j!@!d7o???lڴIx1šTRضm{nƢsrʡL20`L G|n";qAsUD-hWt)<ęK綉 :t$4i-[f kJuO%boў7UKA II[b㗳8Nx-<2F#hSOeVZe]vͻD] c3Y(Q\b]qEJpdmj\9"@cRm׮]:.]ԛΎBҙ+Ae X2B쏠 4hӦMCBB6m www+V ƍ=oذaVf3Ũe ^VޞnV 00%'cl mMm \%/m"oӦ ֯_/~>>> ! * 7 yy%xؔ?O)TpF`p PuDj ꟁ":::u*"]zcc~5CtJXQx|8蓓ڵkHDDD cj$ҝ lQ\ZV+[ku2/8#`k =F*!Sb7RʭȬ"->[Բ&<]5{^LAFV?6`Uz9w`i״T I߾yu ך{uV|пܾ}[:@{M|מ`Pze ՋŨHV!lY?_,|@69s`ٲe\2BCC $h6p]C09(kOՀvtFJCj rUmٰfr&(_RB32# HlFQj̗DGD]!ܬw^P7-̽O;xW1Hj s, I;UAŞw)E#r&!IIoԜ18 };52MjdsϟUO>ߤ:hO-[V"ٹ&<, z:R@.Z Owa_MU ?P{%{9;w#GDxx dlmXOTMaȁ,涇x[m's\YNqC 7fٻwo~).\ $OV6lZQ11"oHPWJV/G *UߪBZ1aLŇT=p PҥA823jݱo>ЉtD"T1 B#*iaJF xw1׫Fu(zO6o=hFADWmlg")S[okVj֬C|PVApdډ#`w9ˣ8v~Łm7ճ3I|7>ʕ+ѢE ƺQ!J= ^GÇ,;88XIr-<|ׯ/-Z(Ə/ύPWvDBz<%x Xz$үb,\0|i7wAEa ĉ2E>'c/Ìe6%,tSGIh G!9%ͨeh'TC;vB<[Bڼk.WXQR 11J w%Zej(lg[3X$]+}Tl ".! tc@ԁM7o[bR sIII,ĵB2pCbPrAB^2tU޼"4پ;*)eSOSF@BP J!X DFLd0u^@keF]P0d޴iS 91^dm`AU@z)^^^ Q5+AM[We ԄJղoݺ%7Z nn<ٶfA Gݴ?f3_/<`‹ ۷2F`E@C k_ͭel[g44G>]{'UDE#=%]] QEsmE10GH#"qq\\Od8ibWiƃS+HONA{q(.Μ Gw7*~{Õ>((=s? Hd *HA\.ΚK+Qypr~dCF w;.n)1/BzbQN(?. A}D<3 _3!+0Cٽ+1'Šti Ϯ~QӏPS#QZt/SH xdNp)]E|&FȎ x/é͚;"6}] JK'cRݠr@JCb2+>Rn\D‘ҸѠ5<wg+<h %X`1N~ET>F}AI&U"N ed##IHA6!VxF!Bn༅e+vřS,"^H8I#z,!ll_Mim+6r8#PiP!(4ZIdg80uĪRur9D-d17eJj pbbOOVg՜8Փ?r񷀘+FBd$'"fb*P\3 zHdEynFV叏M<|7VPK2OnW,Eŗ£TIEN@)BĀxӾ#վ.#f 4h }a 7 c`8z@Om|@WSz-ݴ 1]9Kjc,TWM#T@ Gw5x6I&/YG{pa>B!6 ׎a?Y!4wŋjOAAPWJ;}oY/Tb2AUb%bn=%$*!HdIrX~mνyDGGʼn/J,ʕ+gf[,, ^IpSdJ}w {3)V7<ݴ*a/!#55 }GÊ l}=Ĥd =:ヒSNaݺu?1a„4ʹv %JaÆXzq,]4~Gy*X~}L2%۲466ÇGpp0-={իYѷo_/^m۶Iz]SH35Rw'BK00'eH~|wABC~0OA]!P!c̙Ѿ}{ udF űc q-4$ ^'Ob3f ^z%] UVaȑӧ|)|I曘3g|Nڵ3$ """2˗ѹsg5k Λ}7S/r$0,<"o\-&*W#Zwy>0 ϜrРO3K.L2Z9Sm6䭮]f*zJԩ+'Nĸq`Ŋ{{Uew)\ d HOn8q&BR"WA>g$X~shZ} Xe9PʘhBO=zɓoo` ;|T’ N...Y6iD4ѥK|YϷo?+;vzNW^#GOg;PT@R;p(MDd `[w[ȥE܁cPyj^,1S|8b0&4_O7r'SB .qݺuQBNUwڵѨQ#]˥G=g2C7_ F:ݻ7N r#.IIR-[W\ATTZj˒M6zϝ;HT [3 =:6̸A 8rjH_J;/se&m#ۇoT4߱c"p5](d>lx…?&nܸݻw<ٓ#Mc`! ___DFF)Zí[3"CxQ5FzS&aEwԱUS8 IDM``ߤۤ')M@dݓztrNTR -|~/fJ G9kr oڴ) D% jՒ?{LK~R f͚r[uhB~gፔA ]9L~;ϋWugBH\HgMYZea.KIuf7߀&Qw@^%ҡrF3&e` p6lȵ_͛71_Άp [ʕ+̅fT#FڱIn*"U*E/O~;˘.܊Z5C^YîEz\}@dV _䦧syTL?8r^.N.b1d:y9Г3MHC7{"r(],ba&dժUCHHH6cmǎNyPhٲU.\P,.hJ7J?D4sO>'O"ZMr򄓧)YUC%->%?Tu:]DE~/ v o֭[+OE*UJzəP,g1"Zn2T?I_~4Flڴ)TIUZR}'?! md<]Z}vUc^A a*L@h8x`OEk5 Rp Wj?tPÞTDLGW\)[NݐzCzt zLRwKo:Z.G"hCUJru:b8{;ܪ72c31(X $(&n%;"&&ev3Y]Z]]ĆgD;:bQP)\>g?Rdel "Z ^~B`^>RET5^0vȋv: J_ppeb)lCh*8UGjI=}(D~HX5M|,{hjƦ3C6{zzgv`mU8Sj<˕<ͮXMFí6ltsRp)QEDg8tr/^toJo"x[UOQc"_LBЌHIA}駟Ѩ7_:.~I t $iXʹ(ݭD<+qwv$\Gwa %F êT''ólizM^c+ih7/IOq;CAhw=\ d=XX{LCZR2N;wA )cLV/1'2#` ~~~r/x9w/SH d]XX*6oU2•Z(s6f2[,3# <#nX1#'bMQ䙝XXC̓>Ֆ)YxQG[Hm 徠ӹ(, [EAm jsPlY<\ZoD^9QQQ6>Y0@Ke'PF KUJ=ϟQ mᔵqㆌuc}A1)-K!E&`Ïs3#` fHOsqbBo"96^lnKT"U+"Fex,XW#t> )21p/׮ eZsFMgq±vG8F$$Ș{LHI}G{P>:L-1l.ĝ66}_CđblߐS ab%l= wլ4wǯUCO_Y9!qXt]Z 3kj˼?')?/݂ks>kF@Xm&gV{6jFnAgEnB>Z̙WaIވB5Bo| LciW# g,~4vL>cfE@?c* N~k.ly͋o 8b|]CQ!pPo B~ c˸i-*Rp9<Ka{%nDfT҅-I{ @{}bGg/Us2&mRM&#L$$ayAFjjYO );' &aLTz'">.NbDANt̐0;eRQo4%|@O k_X'O"|փlVk؏7 S3 obv'&w|enlx3S0;_0Rj'./SӱS2gig)$ ٻ1r?.NU =o+!* +tOc菔.֠҄~א}AsKDz򕽆 eT&@{ )K?{'/Hw1y;$fݿ6jT1C M#2! ]á Ad=XX{jݯzE䏿WĆF2H~zjdVE3aX \7oOF6: pz|),?Daz07{849)CyGZSf#)<º|#_qy|9|g~[!(6: hڗ5;GO+5dvYmQ/1sgd_~xg6sHd&#+Zc%pAYkpt7Fqv~u5k7A'bJ[>ZJVo?|K7-%a&+#"m$y}akL? K n{ؙy7 iٮO5k7l{CdP &s$(9DW_}=^~>dYXXok;1?gh弝y49pRP˫7p]R86ZAG̡—?,6YsSN~yp-V X ;pTE f: 'n"O3$)N]yN)Ur~䚂l'mkkzh6 >Z%Z0uQkD:ۤ)Ew Hety|HF|Xa}Il\Gli]VHADÇO̺gM&gǒD;qj0_|\]]f͚{-[jN (.^xOI]Szj… 3{ԩ>, L2Y?رcVzcO2 !(1BLǷcv ` e4d7'cڳk1kRo>k˗ǂ Aag,rt:˕+Ƚl7 DD'$Μ9ZSɒ% gG:jw(f:?_k?#!Y,_k3i@)X="BG6`O:Mڴi///\t ݺu{^Rd 4B'*THDD<!2Ҵ-&$go|Yrag-b,9;6iY^["߾};hOȶGҐ-RYl ikU@:Jn݊[͛7Ǟ={ڵKuyv'*9*Y|ц0|O"YiýQ~k ƶd3g*W֭[~oG/$$˗/^$ Z <رc}t ";Ho d o Aƍ?MiwIAl98:£x`AɬܳLPu1^AdhJwenשg{P8$p|Ë<믿7n"W45y߻ヒ'|yl4ʲַvBČz{);>115kҥKtF{!((H OO}UFmg7V.HqwJeKZ@Պ O!-ηrb`JΞFnEӑuEA+5^]?yK˴oѢ $}_w^ȿqjy+pÆ yo,r@M9鯿y+ߗ~4$ )- }TGxPX1-tm{{!]c~NbP/ӹ]m)| v)ߡuiPmܓp*+676O+=\fi»ܷIO9) |;&W?z)RW-* {|sTŖ-[ (4vXPzPf j*)eG‚hQ`n>ݻLLNmV0EW7zW1ԅ}Q"Mp.+6*< %J[neԼ~ý0'Ր7VX!U2Y[j%}iܹsːPkTGɼӆt:JP֠bfuPF6* S_kkO* &W_V4a0ahCײeˤ&u4KtЦQ] o.]Z[^zgm毫f @! fΜ;wJ#1̡cDv l Ҁg@Vj0;aSwg}U_^!!6ThkgeK:}Drn)? ͏=;}4V\)wo B?of HKh@}[ KHn/ H B&5̣_ZgGU0>1jaύ;8/,}]0eż|IPG )'#9o]R۔-# I+}!@*/]l* uSb h;A`3 MUtj*VM*,hY2 *6|Ԩ,W JQti-6TI7ql0`[Ȉ[[xЇ(2!9$$<](6yo[ :ڕv8P Kp"Z<7g+[\/)vvX<&'yU4:n^ Zu(MB@ڮ=Um3۶TQ#zM߹p{4@6o-:& P7)i3=K/.g$h-~^3碏CQ~[ A~d0٤2"v͠P^ U\\I0rC6|䱠ʝ]_Vk0{ -mHcR4S/iӦKf`ݗ?.bWno>A=Pg'9x00Ȼ`35xTyVቩ, b2Nf6M1:>d%}^]NuzrWM{Rv4߆Rˋo":!xGEj.4%3WZ/^\\'!$ 2D4$ ai I3EgI'_gH?TGg.3Dtˤ"蜡D#CDq,-խǔ[y8|6vw8onɼʔBPF?ZT+)WDI>f;&)*(䝠ґBD,]ə8нxQUUUvzhZdJj;uc~m%.ق2.']Fo3 /djP\wxS;p52;><g wg"LE(]b⌅z//G+5;ZXl*y壍Zk׮mҲѭ [R6#?fD2+}R;.#&&cj&YeV='M.y^}B8$EǂTF` ()l:.&j[C&z0Fe?&??lضgFdx`2tH NlF榳g*[9+)..Vb}AmfzhFiD灗,YjM``5荫Lu;+m-77ccc_-O4r'Nuұ-M,,ѡ3lbg#/ joLY^@FVxr-O Xnֵ;! $"k:b @^2FX@'1# 5P2F`,@❻|בt,BûB0+U4kSIjlb.^BhG0'\||U+WfOHNy/qra%g q"%po tg$p)j)4? !|WE^=Ps'!hk|nod2MMa!OqHQ2%ꀲ9ҝxRvm'o#4,Q2: UrwFiq~qj%ТFUŮt['ރv? 7VS@}—Q>IP."DK8()T<˔=wđc84D9*ϐ+b UĶѧtrsKGB`\u>.[P1l_ wl#1% Iⓚ0Sx@6?61goD( j!O@qjN0=kNw!A0KjS(M X/n^/`C81sٸ%SxЈ !$q( Mg}L(~Ќm0m 1ȐhMֿ︄;/8rdZb`{"|!K"q )"Md$ɹ'\JKrp-]Qeݗ/wӑ @LMxfe-6a{ב*t4+Ad'+T`[:vC)Q"2ލwFx~h ԯhzVsx15/ S|/!vHx\ʅGZ}iE@OE۵+VT?ñqr>3 TLvmȿs p=6}I>/}kM*Zrlߊ:D3 aBB,fIB-cMgB.ODد_ lHg10}?CuU*6oVsF*v7Ǫ23Koo=.܊i[$C zUHU aPۚ@ݣ sq H EK)WތwtLMxR>v,]I IkH{;va!$U,͙Hb^G)7*h J|Dkg13m2}UepC*6JO{GWUOKHѱ\L2M«3[t|h&Tl%KvCH%xZ1;W+QT2pUۅP%s?Q,ɋ!:ߙ5ś5;wxXDlO;,2r[©^ԼY^@֕H<'|3_O3pTT @K!mKLz 1'dqWI9ƮÐovݏIĢ-Bab1(DCL-|X> }T J'}Lnm6#MkR8\WSL7E5xp?BCW7^Q>_z4Q(=)u :CvLș=KXX͛W4Lm&z_TC\$n )FDl"-P8Φ-j*5 -[˗!Ckb%6)Ez̙3ѥK'+?vębØV{fذa -&!NI#;kVU4@.[&))Э5.&t_boNe++ X=cz׮]ѢE \v !!! f MD BD%~\nE%dkIA?qZ`?v\.b~nZ,J,3b M)IwϓI۝SCD:%[@򕆰,6*4e~Iz:j@:v͚5YBDDS"}gKM?W_}USLJQ'nQIe+"@B PN+W@YgDzkWNZ]9"/ %(5Vf K0BMP9S(C#)Cb{?FT [aB#ɣ*9rB*$8^b%@B=p`XZlۼ)k?~D6B#1:;V ވ4*§ Ut'f0 ?.=4[ב?:t <{M~СC2Uܹp9"» uF´֙ ;mH2FZh-Ep6d2-u|'U+)YD$m0):Ddd栧?_I*o"("κy*iOTD TRYߤק;wJ@5kbҤIW+;_ʼnNuHp󪕍N#WJC)UEJQ(n:!PBŬeu·V Uu)&?v'2э7SOK.Exx\!YMV3Dqn}9S[GqLdLZCT"|?'-_^~tG ùdYYn.*ḪNjɕST d͛7gC3ݻwg]c !1}ttEїSA*%\@uMv ]Jl]{G0uFLr?* hnuVTS-zJ1jP(6lMew$10x`  e?FjbFn)rpqgVfU1mmO&TdT/7T~93Wb*UO$t2Sm)z7AۇT&|-;6 ɏ#<\QTQՋDjO5_JB d}o G1ؠ,\ HuЊ¿@Iߥj+4Nb(S ժJɧ:ZiF. o)ш._L`tΜaJB c^k`]A}ՄjYP9{{"5ZqcMC523m4 3#Dbs;\=MZ[lsۻ*4'$@؄}#(Cӣ=G=4Nmiq[##3J̸B{ED6BR}U^w/T[u{3V>f\&8oNJzjٲi'K׿Ec| t7ZvO*29}^傩xeMѽt01w4% zIR:&У/SSRm.'{Μh&q|1')8w9<{NvE0oѺ?׾3ÐQBǰ $_ο]6,\$UXڠ1R~˖R^?'1lJy?? k_|Ec*,Q2/ y᫼ϙ4WW6T& .kZ&;~!|FQ їkY5M+|T*&.0u8M~ zӖ!I#x9:%&tФG|Qy-| E \~rCCIi=?u,+=D*ncr@Ci`벏'-EEo9-\ح&y{;[߲zeꉧo "-`N ҾR:aZ ھ\Ǵǝݦݲaً7\ʢ]\"}[5Z!ʱ knrܾ! iu@.m#%>MّRbv!`440 憀5ͭ_C0 h$ "vʶ3b9oeA9TQSZOͺLy)9m #Qmi?[ bTz7߻w~uܮUԘF m9!2eMC06 \0 C6z֯rwD@Gl9rt?t q|J al:!_'V/1*|V?aGid"P}4Hc iR bX"lաjyfzE 4| *R¤yƖuG]Y'I6ɪ',]ՑC&=P#%T''0Cb7  I 4,^B̉h*DȈq? > _Pv' D: e&OX >B>$نDPwp;I}.z1ͮK唩 @=/>X>^:`"/j&W}QȓG_V tUHAkTAoPy2_`!tdЉP_tfU'(*("lJ~5a ;~W`ytsgD́_a eߤr(_A}"UsV ہߡ>!CE n@ Pu*xC]ڐyBE< ɴ}f2dݿ].Z,>e %s 7miW('{u]<"w.D:`otgAZ!z-P+LhXKRy[WCEX吽|t(N\Ry<G0IL*cʁGM A}RGW@~N~*. vB% Y8yWMyɏO*}`_  xyr,\AڔΝCc×9QGji<{o0 ^Ad 4!Eˆ^G|^J2 =iVioF0rHuQgH1,J'HC&dҺC')n._C^OlC :-}4EJR9 dIΐƓ|zbTUJGKZu qiO:цШ, E'%rVٱ'k\RztG3a( Iظ8.|(\#qu14 uR!Uy<ٴs|@tQ?^U0Ix1 SC&!:iԊIfӫI#mqKQC3Y1ah5 CE=8 0J'o4\L0)hG BCA.V苆g76ӊ=O~BK_aAkNWmS3r+_]lL:U %U0UoIV.tD}7e%c0n,hՕ.|? FO6 4C:YLjJ}YKN1U&ց~X(_XDYPTBRQ^ CR2ٗCK;X! >Z > ᖴ)_ :pURѤG<-`81hpU6NN7TrܘcUCo+x#@'v!ӑAL>B>yL:_;>-+ǎ|'pXjX;c7~Yq1xk׮͚ ,p YOs"mV̚_~ٛ1cFSiYsNM6YO=w饗fرc?62)74lV|9Ѓl8pB/{~ϴa8E,k]^XPBoa@)`vZ!`.XJr֭iӦFz՘'2)7c&Չ-JMkǎ֭['K'~gƍqTCS{\YVq1Z_ KGZRT{/3̡:]v&,i!{noС }]wk۶ow{=zr)'qo6SN^nݼ#FxSd駟u$ӽ}M&N:I劋/JGɐ/_U'0ρ8M4^{Q10^.|/:޸q㼹sw<)'N'^^rk׮]wݕ)nTBz &dbj',5>c 0_~ /g͈;:ꫯ>۾}^xW_]'ZK,|͚5KnOFhޕ3tWzg}6~׷o_7H+FݣFiZj{)3)2`B)_6f j4h#(ڷo_.T{x۶m\~ DŶi߿kx]C> a%V;{TꪫB1/Њ7U108Nç[o;sӱ{]ww5hC NZ/ķeR6Y:!=>w'OQxϟ[ Չ{/$%xll lb>\4}ٳ hR 0#*3}=Ƨ~*Ç~(e>=ǐ<ƀk(B:;Mn @+k<l1oP2蛜8ϡ 2syɂ o&cF*i:} &Cnt7.6:h `c$%: +Ib7oq>`,Zi(ʫL+E,RZWߣ=>!fڰaeGJK'N>R&LFdYgJqpr/dd'&l#ɆL3!>Mbػx=yT`*@Ǐ./nJu8S'_G)++^z%5ŐP<x\C+S sp>dkQ@ud4&FMtY+sF1:L<ƣQ`͐:Ƹ`_t7MȘiӦiC@9V i#w9`R|H!< %L&$xT)j@٨8 0@ωc$J8:BL'3|>qTƵ#^(/ŽM shȽ dͲ.J}7BdSF%sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iP &IDATx tǟe  a(64m5R)帰b- EЈ@=G1" e}o{:ͻ$缼޹ܹ!Ɏ@@B&P.  @@ @ aC40 @@h  3 ap޼yܹ& D8J@kԨAӧOB@%a2@44,l DP<  & (ٲ ;StƍT\9ڴiGE/_LcǎxUPAAG &W\fZZeffRzz:eeeѣG[n~Ú @>f>//f͚%k׮ŋ)11rrrgϞ>q f0_S\X"K@͛7l  `6{EEE}ʔ)SRLLQM{Җ-[Tw׸ .Ff͚ /@ ,OyhݳgG Rp  `Ctsʗ//fGDDu׺$W~i_?|^ ЫW^O?-z:~1Q}^~+[.&ow^7}W&zx@@g ( 'h⣭װaèM6Rll,;wx|ԩ=nYvuݩB žY͚5E4t,aIC@ C@F,X@m۶/>}Ə/앱qu޼y4aťıw<+ٵphF  P-sH@- nii@ @@)p [Z(s2GAmF3gtU!jnT#a7n~X0eE=  E=  E=  E=  E=  E=  4XK" [ @ #GЫJ 6XaO1R|0Ojբ*((Pn@,Ap0`mٲܹsiǎԣGiiiI锕EGnݺz@dݺui&ھ};nZd} JMM8h֬YB@v*,^)''zidKc=F 'mK߹TbEի$$$P);;[MhDD5iDҥKG9eӧ5nXsO4&&FN\\9sFk0)i~wbhΜ9ꭢ"z"::G@C/֭[5.@@@ok+AW-[҃>(&~\9bq͛$ٳ&~O<٧o&mݺß'%?K' <\OW B¢ٻwo1YtU裏Du֥BR\pWߪU+ڽ{QQ=+ܽ{f̘A'Ocǎbʕta:uM>8eĄChΝtR/ WD%"`+lj?;vT [F j׮ITv6ܹsaDySOM ~ج.?+GyСo.V+V͛Eϟ?/8@GŦ PC{~!uYT ċjڴҥ Bz~O{d A>SN裏Rg޽;UP F5k֤Aʕ+ť~RJN$mhb,S JJJB9rH*c^/ %qǏ'_Fq<$4o<0axg3`j۶-)KCI A@H7ɋFIr!ħAg}Q)ɯ<)G%'!ˢ݂?P܋9k%o(+7o䭙<^cz:}%'O܍tg;6N֯_Oϧjq  `2mڴIdf(""£5jt- >dZ@-^ysKk׮Mڵkj-+T@111o\X@г=Τ2+9伇 ^zIhddJr4m4W Z@WN;v%K ¾}ĤWJjݺ5]|ƎKTV-JIIm^WVNCՏ*U19zLbpBݸqc`ۺuk1=/^HlÄgyb衇R@aa[1R^uuԡ^+4p  ʖ=Moc-[$䵢%9> kӗ_~)i XBҷ(%o&&.L,Y_y_={^ٽm;ý`?ʽ\˚Ia@@@O! R&Mv8MoHʎ9nr% (Ԯ MOp@}xVʔ(ի ؞@ڠAq+ މGH@LjӮ\XԚ"|n5 @@.( @@mH("5 @@.( @@mH("5 ԚEFD`ƍS臘C]dP<g+[8[ڬ[zB@ܺVPюM[6mc p!uN[ڮ&|HlQ`/(Hm[ @@gP#y:mQ3 @@uAKܶE@t&0p.s5ЙTgH@ m[ @@gP#y:mQ3 `/΀,XWnD#]%1@Y+|YPD $ue eAZ+ @@]4@Y$R/_O>p?(ڵkJY'xB,*۔84@{fffs= ~L_%oرcK[ πb*Uu5 r@@lUF@@ 0L"f  /x@` (/޼yRJT}&wޥ}}Kx8>>jժE)))TPP  `&Sƍ4tPZfߺ/JOO,:z(u֍ &0|~ǎԿ:uթSǧyyy4k,!]v/^LC={0=Ќ Wܹ6mS\X"K@͛7l  `6{o{#DT(..Μ95Jp-IB<.\cǦ}",[z>p ` @ccc{ܛDycǎ@L͛G*U2-#36l!/- h.]o޼|ڻw/kׯk1bi1؉^ᓒp9;FgϞ%zjjآ '`9eދ-HjҤX3ʳ~Ǭ9iD@fL}ߺu_\ 6$ǽOp  `5 h @JVrTK  h@ Z@@C  %5@˘BnAk- k͗+@ 6|7m4b F:>qqqGYt(@I&8D(4Lp P<  &h @ x@@ L0!@@ @ aC40 @@h  3 a @@*IZ @,Ize;v,SZ(%% , p/K hZZeffRzz:eeeѣG[nި{THr'ѬYvU0[x1%&&RNNӊQ&\477*VHzR#!!7oN٪.@@lݻU?\,'IIIB(GIǎgҰaè}j6/ * (lѢEIM4z۷)##x" @BrH 'n*z<+@@j,) $^P V%`WxB@@@K=PmA歜{}0q@ |(ɸq-/m #GГO>IE@ ?{1i޼yt={'u۶m7y!=S%t}ڵkjǏOϫ~ݻwyool.[yKwo> c"Xq>oU\tI'餿/S/ oŸu ڝ0+o^lڪU+q_~  L40OX<Ξ=ڴiƯV色; -|!_G|)ӧO{+?؟8p(EEE&~ ;.qk^p)!B<:~&0tr+5k%(//Oo~CNPwС'˦-[7,&G ; -̖G =r"6ݬb]48;k@L/^RNN=Cfǰ oψ4k@s|_ygj&CR=z_~0wVIjBKf+ݵfqbqyJqnذAW F෦>}Џ?(LlҨ-ԄfK^ATݳ>+f_}UK5B׿0&`!|f-/Ox:9uj*vڒLj?h 1q 1'R~ĸïKēQp 9؜@mހ(>y G 6'y 9؜ Gj{\233a,8i " !'  4 ""իW (vXzꩧ|N2dqg9ca, ;ć|7mgq4֭[aý m{֜Ra|+a|Z?^YsΥ5܈#(779s? 2ڶm+~O: tq.M[Q 7`|ɽPq|ʕP#`~#kԨ$(O$:,T,,j<\zi lJRSS՟gqZ~̼r_G_ />{uڴiĽW>zN:pB7RN)6HS\91?!n,U\!>իS*~| ݻ7͜9SrʽVau_ |%I|l"Xu^ZjURl RqѢmw7mTp+W~ٴd?~;MJJ">OVN?RVVW>Y-&$$h*UH>}x'@-AȽFI>Z$$ۋdS(H$4{lIJHIQx+,D%y5ߗME4.ɶK̾ԢE I^A\Nw8!pleҤIʳTZ5a6E~dˑ܃T,cJ&Iq^4fΝ;j`^܏x Sp"=BlK@KǏ'6¯2#-?' HG^7GD111$$RP6<  Pzl>] 'u| zE8M EY  xP71* ^d. @@Ĩ ^ zE '# t/IENDB`rstatix/tools/README-paired-t-test-1.png0000644000176200001440000010514014011711122017360 0ustar liggesusersPNG  IHDRP:iCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iPth@IDATxTDRD-W͛ػտM$M4$j,ѨXI( HQAE@" E|rfNߙٙs>;s˹<=$6Y X X X D-ϰ'X X X X h X7@iVVV@=`%`%`%,(8{P{X X X ( 1 Ξf%`%`%`VVV1JhY X X X@iVVV@=`%`%`%,(8{P{X X X ( 1 Ξf%`%`%`VVV1Jh%W?&{V'$VS(Ij) -+!?웄܅aA@c=%0q؏1wrjkR-Jp@Gs̭Ԭ.#hwXpx D]rf]pl36Vz$Vljrϑ"{?OS} t%FWrL$͓$ X$AjK`JԽxYvhԹ%7#3LvWT%ŕUSŞJ)[SqW|{R$`4q%G!^SGqo,ʊ+m۶rdžT7n(:u;FUU^K%#kT@v;wJuuׯQ={Hyyϱ:YlkN#)l@Eu,ߴ_+p׿ݻ 7 k׮;ArtԖԀ̝;WF)555_veO{s=RXX(\rL:U "Jh X -D/_.֭w}W~?zBTRɲoR<(L =cwr5kO;vٳ媫~y'eɒ%f?>7-3dG qذ*|jEL6M{=('xBm۶IΝ5tf_ *))O?Tjkkuw8SBn&)..$PvikɼykuuY-%e5`uqsμ9x+%+ӥl>y(~>g̘!Gy7q'<\zru)T8S<ٷH\پjx~bZ{=xj_Z,6䢋._|Qׁ'JQQfpo~ͦyACݾ}lݺU3srIZJ~m 42|qg!7xf۷L>] y8l" Wme @4_:վUVIUULٍ$Un9-$3ϔGyDb /նh:O8\ԧO9Nr;BdefH~nѧ]坟gfJ,q(9sDFz*/~4i{n?f1^ JYC$>K_KNSuvi7O>y9 X / mƔnJJ.ر̛xUqVf·˟U`]A1P^ehusq+tu"l7Lj)CҦ R-SƉU#״/,;,h_͂ iӻ+v]4ՖQ5Tg}}eiˏ,O$++V&· nk%`%? X,mIVVL@[ݵ,O$++V& Z X O@'K[@+Vvmw' -JJIh+රVVҖd%`%$`]p]++IhdiKhe.@$`4~%Y X 2 XmevJJ ~?YڒZ, nk%`%? X,mIVVL@[ݵ,O$++V& Z X O@'K[@+Vvmw'۷oIǖd%`%`%B- @ $Oy솕@sKL* 01c<oƍeʔ)6mVVWYҦMFկXB{1 'OǧO.Æ Yf駟JJJ9$, tܹ(>Í3UV_|{)ki߾ٗ{b9n7)1P[lzqv믗?^>cѣٳGnOKJJJ YHӧ\g!ƓM^~e>i$&.\AU &x+++@(G_dG[n^{M`0q\sݰhN $F^3޽{ ~Z FSk%`%`%( $ ׯ,ZHv-x5*H@uzRU)§lꔆ]B@j%`%`%P4캕@Hi'RY[v%ݻWOkxdžs$0ÃG!sN7 3p[nr2 uqg,33^袋m۶zqN;ZTQIyyy5M^Ok:t9>N}gKo+fQt{>]WWQs*xM?bm۶m~ԧftf9Rγ_rΝ;O?}E(szTa'Nέj(FOcK/{ܛIkv! 9˖-j}l7Z,˙ޝٴi F1֭[1>88p@݀8cgSms>s֎5;=ergJ.]N #eɒ%^g[= 3zGʗ_~͗qpǽ'ٕ)$h cjEzyƣֽYf{(۞χ~QLb}£=J=¤Uм>sy<,&t|GC=Gee;3ٟ)"/kvJQq̮lo}[ꫯz[Igv<sPu&Qh =F.(QN"O1XyRWr#$m@(--5kt3nz[J,+i$pQi h2d}ydh~qdw<^ou2w-[<+KJKWF)!k;7ϾGK˻ϮD/ ̞a%,@E߰aN:Ʉ 8O> ov]ݮNRod<ϻϮD/ ̞a%P I7 hܱc]vRWW'|7\.n=߮gߦMdWVo=5$#wۮ& ͞e%: 8L-YYYRNs^`4ihT)Cl}dt,F$&J@ĨZ-̀쐐#'bܺuXN5֔X)J)Q &֢y Xuî. NslJU%:Ѭ$W_}%k׮.]ȤI >AFe?,<HhBYR_NU ha$a~ho5 $mپ}EDcƌ]n,ȅh@8U f%Uـ@nТĮݻWskUAmm-=Z XVb-$iezHEs UـaۆR BGHUT&C/aN8A;Rm I OOd(^A C:w[fR\wN3LUٿ?ͱciݺu~zѣL2EڶmMu6Q6}o<6mVs-S (=ٮa=%'%յ<Й.߳vژكIƃ+Y&;# ,CFexheŊci9ydg2l05k~&ôjϵ/*6L5nmeƭǧUҡ8ˆDݻ&6, $uFzun(rΝZ&nΜ9uq^I36u͜9ӻϮ.w33_>$Hz48D7nٳgixĈzIh"h6}e۶m?A(hVm+Lg³kywYl;슕@%91p#z!US]zݴڃ_jӮ]ͦ3޽{cݮ|fԤJoS$T袋H{qI7| K[nM6 2uxQ&,5=[;]%$<M^hK9 NpVDBd@U{uW3ҟԼ m'ՉD믿Ao% 2dy9PX+N'; wuo^_Y(axԔ| "0zt4.斨=8mB:?cǎqOR6tdcޒ¦WKp %QT Kx #Ќ` H_INNglW)| ޠUTT[4^e|'bɈSvbc]X3d@ifOJ3P ?&m]n 2P>$ƆKvFpxnv ^ԫ K qӥKnݺi@!;H&&6&Nd. 4dċ7fЦ5ٞ, GNXն%co7C%Ph5L+JُPc  րm]>Qfh0D"U'6eo>/hȂCJj4lU}Ɓƨ@=cs692eܡmԓ*wMҮE<8.X {Bg; @hn> z׺'6$y ;8Jv7kl4m&GU}k֬6;W?Y QVV=a( FLG3zhgSHh\l)>X9`-fBmM M@dhM$l6,﵋/-*`Ԫ8;_^-w?,沢91RU(10%0 [PP LlL@yN6 @Vfdɠ5c5\eGn/>»onZ}XW %tXm 12v h2S#ljyiҪF_X->AUGTi 28Ɣh;'1&q&Fu4mUny@[5 #DDvɜD418hS0poNyi;2%`Lh9yf"|0XjS@VOX;B`/6"슉sbQq!7|2䪻_|SGY0TrL y=^@˨E3۟*~Wɿ'?MA >쎑ExfL'?I >m ަAb4)/) D}ܥ !R>NB2w}< J;9qT}eC4***6DLܬL73a3ssSl# `:TOzɩ3O?J[Y~/cq<j<熍sgY9h_c72βFHdHm]Vj/ ,$=i]xbOȗ4`6l7AubLL:|C=ܡ9|Sɇa͛uRմ M0Ne?@G U$;aԕ+[G/dS]{Vϟ <q3F8 cPM>l{9 F P'y>gG)%@ Bu>rebi,mўt93C0#'mҗ';bO9.PR(퐇L&dPMAf` k6vؘ)2?,mt)lk摵m}'o,ì@s0kt\P!Yiضk1IYf33҇T t>f()ޤYm ( =bd>JsLg@Rǵ vJ~x?uUݤfMbD !@W )C[ 3`g` r'D$\*g%HNyBHh$PyU٦\ooܠg7UMYXQ۴z-==J{)ׯLulaҡcϭ)5dW[JzSagճ>xHhGye[YTTI:(N(!JA:m8w&L;suD*kPQ?#@ zmeaι>m5uY`WGxՇ0лۮD) Q ,ݳA}R嬳Hׯ}σ 1Tg 0=qLm"t;Nwmddi瑨À9!q |9';uE`bzs#% g/?c r([w}Wj2 , t>sAcGA++VРGq!rXv+ ڵkɀ)&͊ ѿtN2`G g?D2K`cqz`\A.3>r楃| >1籞?qt?PN$57W)OՋCCm@ffc{, pJBd9? pbC`rfk۶ZV}I|@ۢ-K0]49F9vMjd Цꟼ ǀiϨ /\0di`>h]qeI\o)KswwF9-4`0'TRR~4R} (?ox~pYWTi/Rϙ@ "Qupdi' P6$3L ,``c m*ۦfMjqIycu{f>nݺU`:j3ߟ\a$Ĕ sڴA{{ۣad k5Y:L`xa$7h'?v8jYY.6^N|d'C . e"tkۨ_ms8a6*&  icu3)2v:i瘔 W̊'S@ `E`~0_p(lD mX- }'Y:Tؚ2a)6`SjJhj^=塙egMgS"¨ ڤ썀~U#Fha&|| }u"v쑼m2w6Xq^/ ɼPqH^\K2LY}u1Y'`2;%yU@mJ XMkQ䑥ҵ}|Jbbp4b0wyG@;(,09r~, tj)T8W  j\D}Avbe!)/ q'â \: 6 13"˴ϪNL u=Қ:1Wv쫔臒2 ` L(v󥼲T8jsE5,wTi';]|@uPa0B5Z'\GjTl'Xv?B XPP-%m?,vWJjRxϴv<<9윀)&+&ZC$P~%}Ic7s`n1`Ff4Sv058PҖ`Կ^e*TyQ9b{ {dfŧSv*!y5; N{A@d*^\Q? @qrx8ǎ) F AOc:@/?vTO?` Nt.^#,`,cRXe-UێvR}~v; XBX-!kA0&^A u†i@NJL|̹}0xv<#Ǎ=XI?)@VﴱjWu{|,5+ÆGE#@ycPl/n:sNY5*gCPK~1>y@Zqk T;?bAÙqI]eQQN8#" J(ȧCy ĉu0>^@q6Q.CC8}NYp(X_s;BCN~V'@;L0'Y=jQF5U#v%6 D xH}qڶdu~iy'͌ߦԖC$&f3av)@h" wȃNHٳ6g<9Y> G^uPܤTvR PMQq{>uƖ8 @32k8ʋąo+S;B\j䑧BB9F@)w~_j?Q$Z&#&y{d"]O&rل12C!-<ԡ>CJy,xP@S'r, k^wvP{I9^9s'*=]tlaN&: 8:n;hq'l[$]N-5KFnw;o&G }Y;MΤ܌F]Mh=5IvX' LQC,a8q`;-;8z@1ZoE#TX"$ 6X1c@ )w^M8a0bʧݑC 'PVT0a;4ʀ"֑ f(flw\uT~G}t:yLLM* $Tnt cz#j]cx#ѩ> UHHYGrx'1L`4j}3`n m¡p:Q/ ;>P&X. NТ,T`;S a (3ΐJo ԅ&33Jvjl*oUdO&ҪØ~Vӂ]eD'LݟFx P*5 <[j}pl_X*0Kl~H,P3sX[L\Xa & #XI.h)<$yӖ@NJ]A25a_0~:_j'i ۗ7Ixsc1" uԖ ,-W;h50}u"vlf|/{;*?QF .LfmքCarqFY5}LAPDFef{v pMͱxw6o<5ĥfdi @W\|"`&3%$tl! Vϯ7322eΆLeKuZ! z^ƀS<Ȅ r WM d H&a@-z+= 3} @ >M_pԓr&4VrLl:`=2e~~m˂ek*^M7d6%7+CoWfP0A'S?X' 6H2  <'vބ Mg $P|z ;qa`)d㌲|¤h_,&jٲe^53vG˥f\!=bc@\h.q3 V &'a!q͘1C'M'+Ai /7I}[@19 Ҭ `ah$ T5/0Ȃ՘xi8>움7žp v\=c}] kMBW{X"SzO~SXAm:U0DXl?Mﬞ ЅJĮfďrO3TXh ddd&ʢ{NчuHbSƇz#  `05y[I3*7/c:KP5)/k}zpZ fXa%K|@Q11`'E]g1 vj‹'sksOw D~ scc'bD*P0PnB?xt=#{Lɓ'<ӧOj۬Yt$@:$0nH`VX=U1\+#z*-2rdԪcuF7)~ax@,1tc^$%^"tru@IDATLGGb?؇qܘ!GP^skǿĎi<Yf`Ya\"Y0QMFFK*}h2=>xiL auM> x^h_\@ :/fv(Ϧ˖fEchMS=@y3(҄*,,c.Jv g &/̇ i&&p4m(]#=6k 2jPGmA-Ź  Il$*7 5\ 24ـ%Il0E8Ѱil&vNX0`\á=L>&řw-λ*Y'4av(켴vFFgvy${n|7j(?Vs 7h_W8X<f5Kw637JֺOOrk~El`-]T)/mT Gk y0Pp޼y7/&1-Ըl\k<92K 0/@k'8 brm%L)a-]{ cŋ'카vrSF~TyƳH=h>L>)@5ŀ2/"OYEfSjJ $Fd3 Tw܌8b74 6?R=om1_/:_doR'` PSy10xBzPdžIR {lN s` šac0F ƉDه0ILt@;b[+/2eq D :ir)u$'>j))s89} ;n^s$sM?vR쪦3Os:)p0ep p5F``jO]qlT(j3S X$@~Vй4"X& gcd Xc#E2Ͽ$Yu8 $Wo^6& S2|ԟLki+fc"y$>pZ#XG}TD̈́=ɨ'n:Q$܌EJ{饗 /?Żbgc#ʞRtl.S#C1%$^eH+u^fNP5>S)N&HR@{6 VA04#` EDkԹ`1c3]Ȇ>I?/1QRmn{vR?,^BpcHi(͵%12rEp/X.R^t7ku(UW]%cw&n']2I37_WoT`h0?(*x2wp .`b@+8P 2@ VVܳXrmW P.üP[q&&G? '09s`8^xmsRu-:BۄIar?\& @WgY*+_/9]դݜު7)ofoʤ>O) I" ܘLRSF#{VӁ~KQOHERED j&qq"J$x:QYxýY#)承xHYatU6̒GɶUJK/ `^BX'r5(+l,j^L0 0 eS F}X}]NjKW[m1R3i:=v&͞$Ì*׮P\)?89fk8aG00.x ´LTo0LiqO;8V` ˜!SD Sе@:{>$)0MSQ LȜu7mQݞv%;Q(oUG&bO.%d%GX`El&@Jf:)w@GYqS` )k.(m L`J//[jcTY?Q&6L}93Lyp4\ڂLy `cDwRue e+#aۮ$`Ƹ;o˜bݞd%pHKHYRr&#UlL>g+҄{P]E ad'(ԈK^%m `2f X1u`ΠLH]I #s,350B<.ٽaD)` HiQ܀) l`u+P?l: S58 S 8̕uHz! ¶K"gQ(u|Y(6l\b2$ PS:o .@/܄/Ӈ~X{R< 62{4CZ0ȣooJYuJ҄L1 lL3:xN3҈rVa\Ԁ* $Mt*PԹQfI,3O90,&=JxͬQ0}0@ SYc}W.oبpKqI4 D j~녛z]DK [}@ 8MSRYyT{¨8xH)^eLFWL?*{(řPDeAve2 $`ME행lxP f&Pu&32B)Ӌ8}"4+rTbmIi璕?[]^1(pq {Ḛ )%p͔σ+#rz6&HT!7 N#֝@ݨ `]V: M8w`{`ȰOcm8w`6Wg-$y.uBpH)Mm)`r/Sax%[ߴ1I *Fx_s!bhgbL拚1ž4 tQ~4=HLBvcqkmP^3VΡ<@2 la\` cFa,vAѪeu}?j&p@bZ \wyG&N$l?L3G=45`9Vۓ$69j a; 55$ [dAp JjS4)I$Lax9~޼y(wP%m.gZcOsnL6)S_aL{̴л˟yV_Bxwۮ&j'UٖҦ?o;_RvTɵ.jCaz#"GRHyp:_, '`o0Cg< ,/<@jXL^'`J>rF4(WP&38@?H0m$Wkw_iB(oZnDG#5Z{vsI %r˽?< ȍ )6nHa#PKF@1;N攉YF0e";ր08q(;dȢ$?)RWLU^$*Vvnm6J.g .}ղll<0B*(VR)ە-5 (]-ĥGg]/s$$ < >W|k/WnjӐʍnSˑ?hԙhdp0.@@hUI8ztQnft'6S<jS` e13;0SHڷdodɮ3W['ut5|$Ki@Qq,+*m]GƻŵXyHSR'H9dB 4vK쇀`fH@ 6 s^<Li3@ʲ>˨nx[]96YqTGݼ,st),} [~2ޒ2YeSs >ܽ]WPwzvn'ctl%Da4QMr'9w=u7wmu,%dË ؏c)l0ROX4E *n0- Ctϝ;W R ٲf)+_aAE/hf(j>-NרQeHzJ^Gk[Jez{kaJv RYh;@-^݋ΈCF pFۣ|&H!tw@M@uᅕj{D] k #46K`ì`RxK☱~@~ gvKrh t)LS1 K!P⠽K&M~8rȋ(>\ڽ ,UQ<nPVî7}@V k%q|`RmR\04-,1vW3R`!Q@PIV 4@jb.Ҧ:`ǂc 0YG CB;J e=q=Y&:9IWyW.'~aA4`g(ٲfN({d2S_F!Xn(u9^uLys&|[2<26HpT풷guȀn|E"sKn5P@`mGVȋ`7%UUJ3,O>`q1fLQ1 \\Yjc&NsMj*gbEJPz"f9Zdb %?g@jH'ǻ?(*: :q6f-kx&km:r,o+TxI <0HliY P^rf& C9F%e97yn*M"{#/4\?"#c[@%lZ|9ev̓Wd8 },Y?,Iz&f.> ^^0PF j!NpA07uIa37Gi 3 Q> dr*a)0,=3mRG.x뱍2{`Jx3Z=V꾺~SW.hY]^ڨӞ^.O@I@:v*:Nٳp^*pMpMm1T=F?b<S1>^Fnxj꧲.x|XК˼K"7LgS` tm+,sJ/u [&v>F$Q$\pa}֮=HɍfK(׏녝N!PȩTxQJ2χ3%j4wekJ^։R^~'N׽lT'A0}y̑.+vZDJ4j:hX)J#wۮ& ɭ՞JaPx?PP`/Xa0HӀRf<ieDRO 8'S b6vRNJ+VH^K%p\˷/qb:dx}mLF,g(0Ù (Fh:j(Ot<ITb$}%@ c5e?Z:O$5>T̳dwkQ<,:aWc;)^ylR3 'TT$4#`)3': 0$ږ@KYH3rInsQ@c=RF4adeL|C')PQ#Mƌ P$֓|6 ' '6[~,;!!pRF2; Χ`IOb>o&ڂ~;VZ }R@^,.}LvӵG)%~,ĕ7R<0`A3J7nC(BJ R5Y #Zҹ=@YQQ2";'@'`8RKاq4a4Pɣ 'ݝ@JYH&J?x1D#4R@Sr2&2 v4+%f" HMH9iZ|X | " MǾo4Z i3`D0I33fgX&y'kNJq̘(ꄑNG9~ĮֶnW{&#/52yϰRX`>*? srV q8 T d)1T~qqM&겋}@c='!@mكj 4|i$@ p`Cux&3}4&:dXP cR0% (8%@wDғWf'-G8JڨLZbJQS8ф F ~Xti4+]{QȒe sd"~eh,R$EFE& (Ry2>~hh^2P@Z{h猪ɕǡȊ;L--YmiWg*:LJ`T)PlݰX 5=䌽d4Hz3߾Dӌy<9R#QwjOR;}#Փ$3ew^W(_ d %D}9$\jK5%Ttj' }@4[*@q@8A8R:=hF-`"Ҽ>HV05;d5nvKuпcFW*qodL>}!Vyj32$fSM(_~<쳵-{dƌڡ0i$yꩧ>qC09m߄YٛQ2vPf2"Cq4F;6 5ө %Z `@ `*fQo2T`֤Vs]|= b`qj/} vӨfPniӦ[o%_~y#qqؓO>)yLD[6H\ru2{O{0TG,2԰Rb0^Yu4qp1`F< ?0$9 X *AKc*f)_9͠J;[}N'@?SK5Ĉ{Lɓ'ӧOla֬Yrb[P 'F Y7@zCe)`p>m_39 $ #bGVIhX#h1 8l6+퐑Ue,O?~srE5j.*jg= ̙3-z`u%_ȨAc/,E[6N`RM3"˗/s2E9ߜ ( K04L&_?ʨfi^IлKؙxs4 -?Y-dɫ?%ջ/WPuaNm#dޮNPD &~&=k|@"?aR &S."؊TXTlzTZw3;H)2sc)ΞsHIpa$puOj.dvDoD٦dΒ.VLJlj" ^WD%7JLkRsMv{iVz HLmv y9Ug#:G:bmq e B`:O&쐨:C9]@jpk\],3}' p*eknRex(|x]Ӿ١ꓭIjdr۷1'Կ ((l*PpyAfLAI䙲CHL9FkFiÏ'(Q2GOI#diٹ&!Hs]2& "F`Ht}ÀcD $)+hL|WE vp'Nh>l0iY?Zӗnj';Pa'FP+B&I(i6Czt^wz 5Lwƣ+ouѹ3 y?z‚ <)'͚5*j~$hH{)}Gy'VEzeM(wh C!Mas)C˳ce)*ڄH%i JWkYb6{#:c#3-!!<"ҧ(Ot} gO\H>8)_حJvMhҼ1BJDJL'U%WIKXѫUdny 6]jG014Q#zRLJ(!|RYe9$ hE8yhqpLT+K~\䇦y㺘/ZTsT!'o7V;.2ezԞa!yA abpC"oK9RG*幌"EM6D*&hqTk1FIJGU^XUhi:|Vx+G>H,By.) ѢEDbނĚKxWgsͦ_f[1T<8TDՀK LJ>j*Gg֔yPo4E}*dzYHgC^zm"+|$ls Die;_zc4:tKG)+^Z읕@,}^]bڍn-Px+DOĊ!˫8ҟtyyr77Wz7MsSj74'oBȢ dJibyJgN@!C M,ޏ&QHHH/$Oҫ'Q?Lu1N C@Zn`чO2ʑXt艵O>N9|鉩wHB[su 14A$G҅t+!T/b+eM#!2RVf!PKH#rDE#f[Jː"{ёBXճ>yDdu_zf˺OR:qRt"Rm!PQcDG&$CQB;Wd.; bxBzN1/bY:7?c\櫘{6 ϕ) ᦻ@^05Gn'$K̳#8! GHL)Xx4J<{_K_lT]]ѱD[@xbh{@H :NI"eR'95@]KԧU+ZPƿ.`?eB@\,vC#ؑҍU+$ʢ}|b`B "4h{a! I!fD"@@b3;ыBXY]U,܇xQ *~?ƩvxZ! |Yt S O~)Oň9ARE2*;UO]i*6|**y/v @]!4GfРA#2 3%UlZ?tPMme珀4tخ uۑ#%q*ыfJ ]ȓs1U-H!Su3;gJПF{cX(GMSfvlK3(hE[@}"8pD; M},}b?mָSYEymRQZ pM'Hg;Cք6jlsi`jӨiޡdE@:Ь?LՉqǹ%NW"Vhjy Uqh2 B@ZlI Lv,͜9T8@Ԭ<$7"Є8u[dB?D$'=)Z>ݲ6[7tD@:<Su!؉Էo_ }VxkKڰҬi1 44 K%MB`,ys#5Z7WO,&E5E)#6*Rt?"1B q}\Vn775;u;'nqtq{#v8Ӕl\iukRZz\#.Q*Z6|R- !Pb@KkB@hTKB@"{C@Z<,Ւ%^+@K$@! - !P<DR- !Pb@KkB@hTKB@"{C@Z<,Ւ%^+@K$@!{ l%! @%o6ݺu3mڴ1Æ s$@"K3f:u?~yͲem AI !;v#A9&Olo?3}t3dȐ !PN}w;`ݻ}L6-,SF!PĎ@/^lڵkH4 N̊+E ! u(֭[W! mۆ7ߦs! @!;hyyiԨ*tB'L4)+#kbG۷7fͪ2kך-Z0aBX/yeu@UQXCᄏYjٺukJM]tI)ӉB>s1gqͧ~j>L! @}#;=蠃Q5,_fȑ_~f/ҳ' ӵkWӱcGyfO IJB@ ;#t̟?IX屶+ ! @, ԃĂz%! @\>`_B@("( ! @@X*"kh7-ܫ:B@tp!;[*`7tMgTׯ_v{㎥ AɍxtHs6md.\v1W="c(Q4/?:7%2\ 3XH(净4?U_5\r GP>J_2LK^5ᩬ2kl{ꩧ\QGeZj圕NJ!  óh^{5G~y뭷Ga^3bӀ'mҤy26/0^xax[o5v/ȗ)S뮻.L ^!`T8;u2U  QJgyf{6Np ^lذ]?~{=Xcǎu|*TPI5$219?stP?|wߙs禌b/W^y~ꫯNW\agRYF >7*KeK}u_8)%C]wդG9sRg@H믿^rT-Yu(H|@ƍ3֪kL=!W="cO2u/:O3SY| g͚:iu@DO͚5sY6Q(h1] vW_}o˖-s铵.$ ^z9iOM{@ò rC@Nջwoz7M{e]̻kz衔H/;sJN$%( 'P ԳgOd\Ɔ; 2$AE1x B˖- Ӷ,]J3fp_2.p޼y;0O{ Ais͚56HG抏>D?fʕOLLT]٤X+|`w?x`6Ttpw֙np饗|? ,NI:$k86͌F(svЯ_el (V nz\ 쒕t$jx)n l(:I'Ov?W^yeNkg,IDATA[c`ׁJ>]HE@ޘ2 ̇&`z-_nmh׮]gyͰM=*/B@ȈBB ?DB@h2B@懗j ! BD! )Q_빵E@Z[uwHS-e]f=p< ,0s ӯ)YD%wq )ASN9^rGy׀F/zzz\,.Q?Jze!%5\Px ꪫ"7l#z=2%$В~3|CJ0&B(uf6Sc"뮻\4ҍxhyp4 R*ӽ3x$c;8u&_tEcǎ.)mF tR,^~'$Ư06QRbѢEfEf„ N:}'Ͱa1C\[Rq9묳:xnfyD;}dK׹@Xc= NjС-DXiő'}MӦMga?VWQ@x E`qsIV l8hk/B$(/ dPd.8#dxxY$mouϺsD[ l:&on/ܕ0Ү F:7߸2"c*%IKd )a^Ne4Se$hJM=(>%5WdO?_vG*++ :}7lyN3WhpN:9)tݺu*'hB_\ROH t#1'듍{ؤIwܲeYz 7S)TsNOzvR~)%haFkH AHxXOO"!I|J1XUکM`5 Q>T7G4-ѽ5R^{emMzN}hLybCڳgN<[  "*"bFX4{(ېo7Gvl+ˆlH c 1cƌ1/7n[C[:I=dCW8PX!=k2hСCW{qLVxnqӵS`קݻwwm`' [풥5w_JN<7$ +LYi I~M]Jd3)gC͖|tcQ*o8@λH; ! 4)oJB v@cJ!! 4)oJB v@cJ!! 4)oJB v@cJ!! 4)oJB v@cJ!! HIENDB`rstatix/tools/README-unnamed-chunk-10-1.png0000644000176200001440000010017114011711132017651 0ustar liggesusersPNG  IHDRǵiCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iOHC @IDATx]|TJ(лTUHQ(ҭO T,HAH{キ@zo .!{wf>-}ٝuJ&F`CZ fF`Ev<7`0#pЎf3#F`lF`X;0"A;0 ~FpPX8hsF`#0 xn6#0,`FA`fF#8(,ٌ#w`Ev<7`0#pЎf3#F`lF`X;0"A;0 ~FpPX8hsF`#0 xn6#0,`FA`fF#8(,ٌ#w`Ev<7`0#pЎf3#F`lF`X;0"A;0 ~FpPX8hsF`#0 xn6#0,`FA`fF#8(,ٌ#w`Ev<7`0#pЎf3#F`6;22۷oǕ+WnlvN0D;Ek,͜9˗GQLi׮]# #sG!Cwظq#^5koGÃNb9y{Q۲{nlݺ~!4hҥKcÆ prr=oF!XPgYTTVMӧgCƍA+Z 01!* 3p5k֠hѢXln߾r0 v"_Gxx4nٲ֭ؼy3QT)-fCU@6aJ{EL<~-`/bʕ .\Fhٲ?tPܿ ,@Ν1eXrF €kÇcҤI߿?0qD>}4ߵk'&OZ =ǏǸq믿{#&~Sk/8ݻweǣSN cUЪU̕!!!0u``5SNϺuжmLXdB]".ոFyl۶ 4בN&&'N@ (je=zAAA\1#PRMF-XV8|8u'v'N Y}z]6BPHFv`#]w[M5g$^{%vf`l6YJꚷ߄d14hE׾ހ^04c!* u^{']s)?GL*!L#`[ +On?~e_:J@fl6M3y}|:cO UHA+k*E2ZP Y 7H} VTrto,FPC~?-9" הw 30 \/B:L^?im"~Zz1a%0y# o|4t?ǐ& 53=vFyQ޽Ѻu 7|sކO4AxS A0F+Ff`^֭GoBHq(#hfF/f6?릃#LK*O?tOZjٳggޚ&V ((N:~8^{5B#~+ӧC>rT O]v_zrO+pL0jd8֑'ڷoCn-"}:;9 ]\dPKkJ}/2ԩgŋh֬ݔ"9#` n:wi%={ĕ+WlE4ԩSlRdɒK]hQXYYÕV{#͛m,Zrֶm[R˯,~ȡt4)&&oѣq]lܸC ߤק謺qXp[J`NI0 ?n%@^Zû\rrHxq |f1tFxxx=S٭b-%":Y,&!NRG9Zݻׯ;@+r@;{EU@6ܳB5JIMGMy4ѦMNmɞC?d;vA>}ݺu+R11 ,铏;_Dz>]JS\M-hϖSWVK>p"m2e4Ӧ9stB6ݺuRls=E``!}}}\ zBbs=2h-uh\>s>#KN&Σ(^1AmYJʕ(6B~5$bInڴJ `4Ӟ "ʺt钼O} GDz۷o]4={4( M%#`nF2 >WjR<{IH >w\[ ?J]y-M؈-Gn@b=E놡]uWځH&@;s ! `&Ak֡*H/yt} ?hVFK(h'J-ptXa( oӬ?/Fh6_T}2꩟GCx*5'FT ~nD}|~:KSE|)YA'~U^ṳ<#`R <[M T˷_쵧qCxP q CMRDVTBxUxh!CN?0`<\=i'#p~@!F1,caW4xD&a;8v1 /=€ੲAb/UmlR)3m (#`)+Y mJte֭M,A(2VT|}燉`DWObw#!F`ՠF6vdr$gް |C ,nb 3,lA}RY 4rvc^c`` ZVJKvzڹjF+Vv W{a$$$_hhF`:שI)8w]# ;S.0*A" a ֣vޞ,YH|[p;&& 9%0ga3pwa]=oztn&˂11@[Zlw4'f\ H6zKCK6ƚ.vDz[کD &F`࠽)AƃDNjU  B YLmME?.:1x,wS%ʬM^oǏcS˧L)0v Fᯝp+<b6ԬJe06 .ӘMď+ObՁk'9SI194YB?8nZdUO+߻Q7C#Q?]@b!( L#!0;m?ޫh7nV&ߘf'UBRJsmǮC׍nt7of.ۭc*I0,7=nbb vb~g^_rq5~K$bEYy?:#s˧7*%3z/}326 Osb黈(M ϡw@u)EsVAPAYH%i}+kuZ\I8}#Qhp' &PD?q?ORφ̓{ Ii[F-^oE%Yr3cWb%1>w$ok+8rgmV~F@uX*X}:'V:hgܒ{[o:K%O'?<[J ,ƈ3}?_|آ$>YpHm 6g8gU?i%0ϭ%,[=pІ;c TnmVFDF``5kĊ̐yF3df: b72#`b</6GnifH6cgojZN?2=5XX@LsiVg6-q -ω -_tO^Eq`Bc)iӰh"lڴ)QFرcY1Ė-[n4ԫW+WDb2zA;|܌04]y($Y$(8,B^Zl2oI $؞TJl޼Ne)ܹs彳g3Slw([,:u$CCC1l0=YwhE : fzߐdVM"wW mܸV4󏍍۷Zvލht>,J*%KwSXr/Zԝ["h~DbgO!\@'?MX(orP Z 4h l۶m_^{-P~Q "TXtBOފ\ő@wD##`4,, 4ߺuhѢ7o `ݺuxs/'QbתL kEady3Ij HUUWO*x !/ :OXAAOmBbTp| [D½F=I#D?}*#gZ҅hY%SߥYZ%@Pf1Ùp^}붇 d6m ""B ]?RE\~X1*ڎC}YbqÉ[CWV1R :딿~VnEFiRrU sc ڕ4_pD6(z4)cըBMHC ٺaeWfBBi\لg!g?};wG0Pf+tyӪp׀OQE R`wm f2sL,Y˗ $1VCՠWb sUn4ѱу>-M56SN2VZt2Hrr&3 ;MʢPZS"13O a5sG-r+txᅬ7|SF&`.XLW_ |R4o^}$$I;]5g *&52ݽ{sAŊѣGRB@ Lˏ[Z- u / *0]*j`  |SSU~?ݻg ޽{ܹs2!`#h/F6?S/4 !NC!re5+&/1ڹ6+ ޽{3p,\P#!tR^*UZQ11"oHP1/VRu%@Tk/?cU!@b"7Lـ}CX8` D9r+E͉:w,wӉtl`A[a*ގP zsuyZ"7S v)޳`»/(ZQTߩC /4BAB8q"}]{|ƌMZ* v`gf GIluNS(VP֭sN]:q :"Wϲ"[q|VX! ʓ;O{Fɇz*!$l5*_$ MP%Yȱ,Uv*#ʎ;V0WL=zT(,#`*09W.K>hqꠔC0H-ByBŀ+u݄ E¶M`ݤ&nR43_M>lkf͒>...r^Ck׮͛7WtM]3 5R23V~mj,OͪadJlb=-y0hӗ)!!t믿~Z t~{'WSNW^fqXdh (E% TXA/ U $CU@uR y+~Y%li7kORŌj Љ'd‹<}HO^7,l=PKX4 eRä\c-^t'U*Mt~iIHw_~i{@}`kF``=sL9 Hd t*HAl,.NKgP~p~dCF g3.vq7qll#-!Ѩ?;V ܦ#"OΞ3!+0Cٽ`1N} bPgW'yAH'RFz|R (ҕ&>Nb#dE@VͻnK!7xE`N_p^@}lE83aE[D_'4'qD. wU)5m9RE7z1< LHCDvCF!9:FCiHO~S}!^~~O '&F``'z8kܤ_ ~]1B$=)UbQmK@Z==!"=6\Ȼ43J嬌@ (o|li+_%e5Zyr/}/SpD-M(D g~mz>Xiii2hXЀ 6ph6t2pӵOJ |j;~>wZibP0mbFFFklHA_5¹Hz1 V9S!4B Ą!1lXTERݤ{..^(VC&~u@\-V*vPeDV䛗tUV!@ KE^6܋=^FTT(#P؁+Xb(_<^|EGhEp+_ٝ d7K6Dpo7bv~35{J-F ~ |))9#,o[уM=F^~8u֯_Kbܸq4ʹ~:`ԩSk׮3+&O,QkժI&e> 2eʔA…ѵkW\v-KgϞ(Z(Zj)SHnDFy+RbbȡnҔbHKC2-ElEj#5Fڝ a ;zS>{:CvLϟǴiӰh"iݺuCQioرcvz5:QH%T3gå0B?+hժU:tL`<35jf͚%6$0hJO#<<\r :t:^f͚aȑXp|n7%y(Դ9|ѷP3%?jM9O ldCw  M*U >0 Ϝr̛7ƥK"B޽3Yغu+*T{]tgۺ4ٳG ;{4J_|>HHЙBw~W˙uz&9'>xhhrmDrӴ$Ŋ3,ԤtI<{'!@R/ѥMs:4Sba _vIwCOׯ_6ބ !+ۑ#GJG7/2QWhYJ;hB/᫯*l۶ "V>ӧOvعsLCRV@qZ)gVfEpTw2,``J'"-L@.5J/'Ⱦ>z.wS@4؟;wN-5QI:l0ys&ٳR'#ۓ oիfSN^ ,22 4e zɣA^)“ĜFTeTR'g hT>]x42P\;Z#'_ J;'se*m#RmJDdkǎRUJmۂ~L#+!>|R# 4zhOƍ섾w.(@Oy!OJ!_f@}P" k(2L&+lU7RB׮YCID s+`2H+{Naaar@{R@ؽ{7郱cU:`I-D/&` 0Y4GDDd"JnN8T fTR%)`L3v瓫hF~K>y-[g̘ƍˢȯy͚5n͛ cɣPU?/5^@nXob. +TZŸ=)tZ1;Q7p@ȣ6?:s=-k޽RCcƌx7C%mb dž lH"hݺ7Ғ\hP'coݺu?|w ɷkNV\ ;@d^xA̴gd2\}m0.cjyɭDYnnk}v?a8f141hOvXA:~Ou/^\zYG'oڡe&rr3#0yFZ ^RP wR' heOǀ'OR1-[ TRԬ QͬJU#*n!N s@{A :6r"sN{ƺg E[IR{AQ2^r ;"ڵktt6}Wlm>1]QkϳdWJ)_ }+\LʝBhaV.?ə̙B\d~ (?lyͯ>^@[\S_Sj6=3/;hq̇s)| E|ḧ#@dݽ}{Cz~":MTўo ma` dժqQɘoY!@? $]@I8{CbJzx4#Y ?c]¤ x޾}[Ţ?yP&#`ٵP5Vx S[$4 5iT^A&54_nVV)&K46=  r&Aɳ1HEdyx`yUWln oV`4϶FnU1TCeH mJNʧN'P'Oou.=LD'}i͕D$ J@fF`f:g[=}"qA~ZߍJh5E$>(>rS!@ FUn%[O:XjxT['= ``>PZ 4;YT tM7E@2h]N9=.i1":jZXI{6 $D!)B.Dv`:os@rUymԟ1ޥB>oLe-s@C6gotkp+ׂ"T3YE_mʕB@gOזEexmAOE+«DqE4 ? _kebrCN#KOQ?IOwuB… -O,@N')\_lGpRj0:K5JZRCJHfEnv;~@vݓNǣ>vel_v* oi}HMLBܵkH{t02)d3~2(31 @gf_ @a_}ɺ.V]l*/?Ve+[$샿6f2[,3# \#7,u#䊀Dif@6lܸQgԪ#IxQG[Hm "ݶE}@amhآW+WlMlD74POϝ;<\ZoD^96>Ee`K<,@*U,V?^FSrk͛7e[ Ol] 9l0$xmk3#0 +@"@zZ}bDHQbYU)aЀZ#v?Ĉ($E (R 6lRB (0#p*exx8"^D51x#0@.JbSC\Tq85{)n9g7Wv5 g77]#5!>Q֨zO]w,\s}7)&Q8]w} W/OTD;5Ў/[D-Fvq7`c-9)fO i<@&M]_!YJsq̨R4՟՛S0".\vb;N~J^ۥ 0RKgXm7g"Yu[0r+].c |!W5;2~"bYTZMR,̢Xؼ;b_ jh۟S9HTLxe瑄3<;1fD@ӿ#" tNƖ^o?k.lymwEju~}Im~򭢨8(ˆw`YڱoI-*SqA \{°[H &!6Yc!T1l5 'Gzelo2yuIyMDBI>VI7RSˢ|ÚgN&9ٖc\1RCd8rw8;1C씎r|ѰL*=e6, aRjgMر1 S3 obOM>ʖMe|)hSxK5 HJIswbh,ON͘᧦d'4xDgEXx[TOՆ?}#>QXK:]CK)Wt$>Q%8"vjN0K'7HOu'$fݿo7j1C M#">s]_ ``=vnh ?#멑YMNhϸIb5pr7܍ltF2|͐/RX az47{z>[{RL) 1z &CČo8ryFљU@ֽQ%=z Lhe궽F#d̝Y~!!Q, ch"o~dMѱwԬ݀GF;ྈc(m;+Y.Yoo| 6S_49nD5& %P[|flMᤋ]g,4bgͻm'޹O*=&k@Bd"\3&PQ7nƍ8$elK '2dy ޶?1fΜz ŋå_gV^'N̹gԫW@ƍQP! LCto!ˬQ-ZW_}{tfj1f !tzcͦeGVqe.6vE7w^IUdoGoE(c1bg1m]ٳ#8*;IDATx'p7ի/~_~Oƍ7oԩSxwЪU+ԪUK}AtG&L.]ȑ# D8ٽ{w$p$<>|C"NPB6. %"Zj蠙(\e͍_Gcndx8A]]KF}?;3NpŰXxdd3###Qn]Ԯ].߿K!i&9AJcXwu 6`ƍpʕ+'s]VI0Jtog !q TVM$4L!p(vjoyp82SqrpG̭%CqeNҘZ3뾁6S|KFiǩWpwwi״iS VE1k*V9ӽ%JtIJ浧'ڷoC{4:I7:ȕFf&#.'ZF"p+OBvo'< (!"h6(k ovgc^IۦMdoܹUŞjE t/;,Y2˭Ef R!5j(sQX'rC9S8Hwc^il^gȳ|ͤӦ0ch&k%HmOQ-K.SNO5N T 6ϳ h {N!kSfz<є qG/wcY4}>` >ў9sH7cǎՀ=*' Ě5kBMRH͓}*l"]Ki@D 4FiC)TM,fJa~IE{!l|Y+/B۷ȳzZ\>4~\>MSD!_4]6/W"uS 9 N^~߿}vׯrw^Zj˗/EP/L\5k&=H%4k,2*e XJx3mfʅ¯LAU<˚աL>Y{hZ$ROzY=WhpV܅V6h6n8TdI ݺu̱yf&aA?͟j~$= -rX>5tڴiعs"Ð9Te@GV\?Ty31J=:=uD՞yr~OkItx{@VqK(EqPpn"B"TT ϋ=M+Vx|Ca_t+bT;V 4۷b` 4jXX_"xxO@_7طbdJ_kjPp\6&hqg熥5-cﻈJнBd&HsbvdH=sҊ@_ʇôkN PlVW4hj-ojP{GY(P4j=:}ZMԨvh6|i}4_ *uJD B $VLKDҝ6y=RC}u4lذmٲ%_KoŶx5-r)-lr~|5; >dж@@6SOQQ}<"Fc^n=ns_W~ܬnQb 5+_7*@=A $gpN|r:@XXu-]- ~f j%w&W*|UW!rboEU1s6%?f>S)B^Z;<ѩvйF .V d e߶r&E>X5Z5F+1v̯ X\(aGб5^Y?sZ_Պ>'?AeaQD|&sHHx Hl$߶@t+t4,n]tj @|&7YB/(C1 ?%\,]Ġ4#R%n_UTV5ܾ40ZIS!׶5j7_GC j:Nm0e rQ*Cɂ63C:I!Nx Z[ܦYt|z5FhVBBe@8:Y@=L]zYEW,+o! ]ܐ _yw`P@Vk0 [mHcR4 ZPxw{ ЋMʭg%Jum/oy*vv:j>obgJ:tԳMRlD[R)# T *O)>>ׯSOYΝWw51mwFop#8,##@ fF`u=65=\74*BoP+kŖ)%&/!V#YSnn~~)] ˂,R~Gl^{֎~.\v YxPTQ<#KQK) W"Jv PP^Vֿkw8@#ߔdt899XM^łQ칶-#°l;yb$O+3 RDOW7TMQej݆=t!ߚF+Ap}—Q, 4NGO"%;*x %K[;1~ 1"bS9 ᥣ8!0.͘˳#J%T~w${ꗮn{0wy$$"Q|RRcJX$H.KGFl)`"UG )~#ċfBc A+ŭ5P!XL=ݍ[2 HB>ᇏbߠ(԰j|"x_aJs,4sLZyB!o("9b2K%ֱX%؞=ogғű}7/!U$߻X9q[+eʼn\}/>~G=ZЇfw6lӿ"N7 1<ݏCG!EiWNpO'hJke\RoNS2V _ĪW&U(bj^"gn\B̎Hx\ʅ'zb>" zպ(͕qOU9YJL  ֭8p>ޚ]QHuD~ڊO{EJz9:ڨ aEB,f‰B-mܦ3c "lWR[1~"u:Ս* BJT464j%9xa;U%{ȁ7GZ #1`& ^R4f>;|]Yh{a.ܟ6I/a҅gJҕ?{tLMx R>v ]I IkH;vaO߁$U,͙&JbIAG/?@/։󂔛͇JP^iVzB%>"7,FԺB,~sf>-QF? vFn&\<V~n} WRu wrJ 8:)S1xsv‘[p@z Yb*c`h%F Ա׋KG]k(*KI8hUtY*P sNQ,ɍ!:ߙ5ƛ50kyXlHG}jXhw-Dm˼Yn@H8'|3(f& wL.V0OM.ĜB^&'cyw]/!YϷߒz[U@Ýc*k1X>vZDHl:҄p? 7^!6pYOP;҅/SMnm6#MkR8\̷ 7E5xp̿ _7^ݴfGmàBIIu$vkX1{F֭Mș5KXXf͚hOڔ.LԦvjAm.Ig%nNjDZqhM[Tceʕ>|86m˗/gC81E KLbiӦcǎѣGf5.v{JbaL=3x`,^35:!NI#;cVU4@\_LMRf1#2S,k7]v*]Y>(ʾ[U>hҤ ]-[8ALD#BX 2>cq;2^N oǏ/N;.JO 6@Z,J,3ib$M)IqwϕI'۝SCD:%[e+ a=3 1YlUfi\<+$uԀtڵk3@ !ΉDΖtM)SÛo)'U((ڤ2 `N ^A>C5PT)|n3FY#WXFBܣ#eP~:&me^ظ#BXb@+u~G TV42Ki\NŽ !VcU)wkn@+O7o |pyO$R"Rv:Xwr(@>h}W1`|Xz5/_+R.۷o #00}+`h֬&M &ȼ:uBٲeaÆOxi qH R)o4?LT H {#O:58z IFՕK*OyꚈkM_`'@Mı2Ҍ~؉ڴi,T1S8p >cf'Oĸq0dy{8x vIbrпRng @0M(3b.>6"_<$(V @հ&4<Μ3!aY(B#V$^+%aM3'`[Ce"Ca"|PYN_&YET ,v!<&7S{G0u}$`hkX4\s2&?]ɌJ͘juK)o#N +_v)~˫ \'QDowu 2RFZI g̘!ichh_}<7p.]cǎ+13|ҍ :u]Uæ|XD^`?ɉ'\|MTll;Uӷl\)c(yre)+Bh oQy6\XT29=W'T )cWW[iTNE |)INO>D28N<P*s?&/_WN\D{tjRS-zBajP(6l +չWv~YiD/n)=Qj}궀[]Y֯` _F~GĞE #(Hj~-#Uͷ?T.]Fb8HVpԦۡY6_}ݯ3C;VEBbs;\֜|}4SeMѽ’R6f1ƻuym38)szHH){))ƶ1'{͜`&qVuo 3 ρO.cai`:lf_ء$!}$;hH77^w&μX*vH~3ٷaT7p!,\غwHj8nq̒eڻ)Kf-{lJЉ}'mhժSw+dpß: *cО?g?syf _j1)C?5FJ$n=1RiC:Q]'+xZ*ү˳B&[{ٱUUAQoYxYO_opکRK&F@ȴῑCЪu)RkꂚxCأ}?/˖5ku5NpRC)O::JKsMí!CoA|lrl~lsٹrdEmZK>}пðyP~+A(L~!m%=x=xɥ,ڷ]Jov3pb inr2y0l"m"pDv`)1;OC0kHSc@sCVb!` 4VSh;wL^ә0Iۜσ7è|-fgDyL)9m #Qmi/[bT:7߳g~M]!ϝusM~ElfSJ 4 C `sMC0| `s>Pa:~J $Xl[#z!b˹~{](Sjȇ cA:jaU ;J#AT}SoD#oR|rYd#T33օª 1Uɧ@KY -5E,1HjKȡmUOY#LzСG%WK+:=0X@Naoi 3qi.1|UY^J̩h*DȈq?$>_Pv' D:e&OT >">نDPwx;I |*z1ú &+zB_| stE\QuZ<ȏyiGuˣ'=ĠbP9˫:* !TWשi?>0t"98ɦ-̪OQ TP&C>ٔ^3 ˧kvAv)HXTZ,!C٠ʤ'Ʉ!c.KQp?D'SC#}6CRLݐ&T8$G"G+DiyRieȺw,XH~{TJ6z'F<6Kۂb9uuI Ћ`߹=B>-Ui5@VT2a-ImA^9֓G>bCR н/,tt!2s^]!%*ɫ|ѡ0*S@:ur5K`$}$1<)Zu6/I3y^9z"P@Zh2->h&d_i4%?J$> _ }6Ks@%EZ'3Ӭy(CT4QMV4*TTU(,WD)K>ڡV SNyDW:>>O:õtjyDrsN\nЇr8uǴU<T-Cc7!(˧V.<J >wTW!x+*<I(B{8taРe*d讗sd!p"` AXvʆ!`k:0 C E CC0R`#}N BD>0PB0:WE1c>Ǧ+_(oFF+]ES[2jTFg<U>ھK/=FPyb 2B-k2g4P: ;*k=CQPSRyG!/O.KҭK>XdykQ6Z([,'N!DWMbc0bǤWѼR "y'?m"?m, ԈF. 41E<#|HQޔ?N6Z!m:vVmۧ"zȫ-#}_@g% #f?KiA*W_, xrO@Om ]y+x|I!?n>IG?PtBCEyc 0bINcx!*":r06.  H\] MC|Tj%+)>Hah:ecVJ}LR|ZȚOKVҿ7?ehZ}UT{n.]*ӦMb`ILU[gd_d @ynYn߸^6}Z&ZU Q /S G ͐N14D_|һ[gƘ* ,/,Q(*C(B)E烈pKwׂ 1\eh40OlN 3\eS>9ϟntZ{am9s"r!ē[~::G'IgK`#Ȥq#5\48˗/wGyd^ߞW~[fMl`q{vCuv뮻4eߍ5^n=9q\,ydfqqqFϽ z֌ԩSƨzn۶m{y+V/^իUg˔n&w#b¡w-]~3ou}uoFV1=zt N׊ߧU{6NYɐ H{]eOin1AGyDquסCnݺjO|Vx^|EGQ{bUll$H}Zfꡗ?0Al;|*IYg]s5҆"@^{jϗ_~odlT9l:"zc_9>-Ei?:n1W+Ip뽐S^9 5@Gg}&h4T> ;իW5j4`8F0T$v\wuc ؏&xOi1#6m#Z#wʹ1^[Fs<e…rmeR' a)VzJƎq1l6Iݽ{+O>`.멼$erJЛr-`Q׊z/NiC{q񮳵7Nm~ya@~i1!-[ʶD'\[$uFTwiY cy3B's⍌3FN:_N i 8yyW\jvtO?<kۏYmz'. ɨQC2%@@IV0(gVR]VsIp뽐xCǥ[F[d^9^C o.W9VIn} C4`9ӵ׎Kes &J~Ml$XcxF&M<3nA{9[oۺukvrP  &cĤO}py;3bʵeI{\yW{6>S1bDd~-3)c^G>4g6^$8ŹitkX cJDZ^}U,`D).fqRW2_ 1mm,;"@PZ?Iʘ2Q0":WJS{!&>1aMLgx }G5lCcz*@&LdkQ@dUԷ&FMtE+sF1:J<ƣQ`͐:Ƹ`_t7MȘOsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iP %IDATxE/9 d  $0/.zDuU@؃qIGE0 ]AuU\((""AXrF2̿oޙyޛy{s驮U @H@@0Pn@y$A@@ Gp\@ G#G $GWZlYyꩧ#@j@y$+#. 'hq ;&7|ү_?i۶S֭[gxburM7ܹss@ h"Hqt0_.L'ݻw̋/XN8!7oK +WNڵk'K.8_)wP>\.\rWDm=YȒ(_L8Qn6;v/~zYbyφˮdDvJY4~ѢE2zhwԩSGf̘aNܛl/r-hB.BWpaӧ|ҫW/)Q(^Zk/"s]˖-%;.٭\R}]ݻuViҤh:G` @\_U'vT\Yn&ٳgOTQ/ uJk׮cǎ4 5G_ĉ矗%K%\a2m40a|fС@cO>sŋiӦ&ÇKndժUŹl2;vۛ4o4h@f̘!;wv@ [@;v(O-͛M)bg͚%E.]O^z2}t;@^pZ@]y޽E9rH˥VZ&^[+V4" jժe˖(9p]֎F W4 S3qv+W.jE]d'-X/@0 x&&M7ʨQQFw*TH ^9UKXv¡O>C!g .Lgz7VRE̙ݻwK2e+U$={.@&3eQ>-[VZh!-2:7trĉt':@pU@_~ei۶W'RNծ];щ 'u˥M6v&઀p RX1&һwo9z{fEÆ Pߌo۶Moe˖f@fߚy* .45k.kBuSjرcfH҅Hڦ:^F,,=yVm}꨼O n\P-ɚ5kB' +W h^q  m@ <2@( <d>e2@ [n+V$$:=QlϞ=Y X;餓NG `҇@߾}eԩH_"^EM'RHСCf3S,A"իnm#gBNgi& gth O@N:.@,4}OY8hJb4@J )Q@  2>B)!+F! `"}jG@w}x/@뮽^+ X`A`DJ!\M+"@Oh|.B TT,(N$Xx]o v6,OA -4n%܄0sLرYyo@rxMQp@ 9hrH @&(8 49^ `@@m@H/RC 6  $GM!P#&Njl@`5xiO@2{lRe B&?pOLP7kl'N-[ʖ-[8v)6l3fONd„ T_6l 959KKO?lP׉!p!ꫯs?)T>YhQ}m۶0l0Yt >=ˏgX \wuңG@سgiـ8 R@*7! 3" h@*7! 3" h@*7! 3" h@*7! 0yX裏ի_Glbp,OG]s55ãG~jpcG|LG4ܷ~[M&" @êUKR@M{`…YJY≀%z?KUV%KF)RDjԨ@{uUor)\iO}'}DhѢr1;ur7_YXt'(mdĈ7ߘVRx?t!3o]T`5\sB駟Ε o ^1e޽?^N:$uƍ%Sl٘+\b\zRL״iSO߿_ $u֕K׮]eǎQi@k @'3gΔ7|S׿(ڱcGс^xAN9ygJw;x`H{w~h@s 4(V{=+ݬY3`͍+F@-[&cǎ5s!۷ooҨh7hL.- ҁ@ȭ'Os4i"ƍS fPݽ{;k,Vg.]|ׯ/Փӧq@@-P-s=g}vW]uy_ڵke&ZߺXte@$2¦ '6h+1'M+t,ի9}:pʕ+E@u իNڎ@ $%Nu+իWٳy:'&+'|I[oBT M ) _zqحoTREmdj:u+Ws=7l؛3g;|}ӎE8" HS'k30+mۚ+)޽{K.1 dl2ʕ+E'Gm֪U+2c@H:l0.})x͚5E-[je(}jn 㫟LtӦMe-ؠ}JNII@_z%s1cD?ADU@5kmڵkU4iRR@H!T&K(a" >ڼyIٳg8*> vi%%oZS"vHtxӿɗP%v/8AXNt vwiе Lb#yWFqr-QXn-KcKdЁߩSFFcF%^-ZDe.ByGc˲P&{L"ҥK_~QX1#,6$7ސ_~9քtzdP]\Cߌ)$,jD U4荠7VA[ɱB髡FzS//^<]MoLĖAӪxAo٭ĦUڍ/hXycbEt [rdWn^<.ȴtXk⥍]?l#oevvS4;+ ٱ7:I-wir?k-^͎u2Svc}.` JI[[:k.3I[4T s,ޔeP cq꾊Ƕ@,M ۴@Ǡtt9})hb@8H8_sP ;_H' nJ#Gզߟ:M ! 'Vp)> (X !@@S6fׯ_/N&@IMz}fдε%@IMzT@@@3(" Jd4*"BIMzT@@@3(" r|3=LD@f;w\]/3cu Y]?H %IbhV]f\s՞([D4jH~a!e@@SIճnTx9縑yH>\\RKM-_C>&rq H-4|5@ : c+ @@S >\\RKM-_C>&rq H-4|5@ : c+ `=z曥f͚iX2iL1ʛo ȷ @@ }@ <2@( <@@  H#8. r@#nr5sI'NHѢE媫B 9k<k  &u]KJNC \͚5sNyGH"C/g)$&wRdSgv'x"p}.&@P)A.@@]M6 S<\"l @@WxDSGP(yNBgo\qһw,߿_ $u֕K׮]eǎY@KÇ;?8iӦ ?ѷ:t F"o"-YDn&ٴizY^l;h7|S4h 3f̐Ν;g@^p:i$V|7RN,>Ϛ5,ѥK\^z2}t;@^p裏JJ{ժURbE#V*[l\МSI۷ R)W\]r 2Nz1>L/ٴ}i BA  hneҵ/ ڳkTNyڽ{*茁 W*UȜ9sV,SLT|`T8DC!@ hʕeȖn:@ô'N@vډN7oiuݺu|ry"ѣk.\r@# 64ٿYvl۶M#-[nݺy\: ?i'Z4mCk6sFut]@@~…q9vi#><@T@s ҕ@Z>§+,@ Ic@I@@ER@HCH,B$F@$`@$4 &" 48 ~3 ?RgϞRp0p?4mTZh\ @@@s&MȂ rIQ:,3fo }F@ Р<~C&! @@Z oJ j7 ohb*45@ F@ Р<~C&! @@Z oJ j7 ohb*45@ F@ Р<~C&aj tIҩS'PBj2* o쉔o1PF X!@ H b3 H b3 H b3 H b3 _MB2k,9r#p0APBB(`N(۳vb2p@)X0ǏCIbŤpa_:pG/yYTm=zTJ*m&E_ h^f/B.ywk@ҷlVdʔ)[ ,ـdԨQ6YM\]$ @@S@~Y|ԫWOʕ+b}wq@zi&i޼yb!3" <{\ApuT:b38qℳQ}ҽ{wx^N:th%:uԯ_?G XfTZGc+Vȥ^*eʔ%KJӦMO?d5ZiѢ̞=[.]*}5%y衇-Nmko֨4*rWʎ;T9عstAzSNg}E? xC_팇 n5yގ=CrKl4$0vؐ5;tg#?0+I_|Ѱꫯ${ Ytˎ Os&o./ζDRmҤ9oݐ٦D|#F?L>=~b&qYfK6-ݻwq~=@@=_~EWsj͛7GŇhV?%J&}'$F`ɒ%cj^bH&ڧOOڵkMU @@=TGʗ/'l~oݺ5*>cٲePL?.rqFi۶/Nw*UI8B;S֭+zrf:aɃAbWx)PqK{9m;LK[q#nЗ?+jfpT{@@=ʕ+ˮ]r"ԩ BZ_sUT1 5xI`Ϟ=ҩS'Yr̘1C=\/Z<»mٲQwڵ?|1>*C KK.1l2Y}ԃh׮+lQnޜ>k@޸3gδG|C-tˆ d޼yҸqcN|PA.uʒiN89rh_fٲeM~m *kq~cNi愊w 6W^yYS}tUP!d P޽Cǎm-Nk&(pЉb&޺1C~hܹ|'H~3 H nھX}9\SH2FBzv5TH( ; d8@3)> ; d84+C@@cO@@@3)> ; iӦXXiAʕ+"@OυX@@@sEDt$GmQa媫W,M6f}]WAcѣyKE^i?ls~Μ9rE=tC:h  vg;w4kƮDT8 qaG5NW0i$Ke̘1Sm>#fU^wAZfӦMo}5X׭[6w 'R+?]KK*eO) 2ʕ+gZNy:u꘤nԩ&NA "=O!K+ׯE%%Ǝ5)GI*V(3A\@ x܆O  @@}_8*؅|O}  *hb=U@ "]@PW1B",v!?3IENDB`rstatix/tools/README-custoize-p-value-labels-1.png0000644000176200001440000003600314011711120021351 0ustar liggesusersPNG  IHDRP:iCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iPth7aIDATxxUƿt@^ HY\WtW,*b[ kGAPEPQ\DdHSP&(Uj%dvfoKr̽sy;sڜ;7oSMwBG$@$2ĐS0 "@H$@   4LpLF$@P~HH L01 P@  0 P@d$@$@wH$@   4LpLF$@P~HH L01 P@  0 P@d%0/0ӊ#4>JP+i2/[)'ؒ3=9dA4&,Й=|+,T>ɖLHJPG#DWR:yGc?O% Ep 7\-ł!p[[Os*\ }IBZx+]_[YLIjDf!|Lvȗȭ2KTNȪ;+wJQq%&;r%)ma=X+Η'%JDǖ9@aF%ٷi)&wh8.//O6n(UT38̮rvvdddH5BzTSIbZK+5ܽ{J&M۷O;V\\,6ljժI6mУtD* Cz-"_N:2qRk~饗;Sj>sܹkiӦƍ5kW_-֭[ˇ~hlвdÇ뮓?޽{[gȑ#?.]?>yUVҢE =z=zL׳gOy媫ڵk/3f0Ý~1ay7d͚57HVV,^X~iy嗝^R7d?Eyٵk|gr+˔)Sdڵ˯*U=d 7[iYvm_6ydرCjժ%n۶ͫ؇uɉ'?{=gǏ.7E߿̝;W.]*#FP}eշo_eio޼YN?tPK._] `%סq5U-E&Lqz*fbb]_|Q{9eu}CºupXN=TR}"q>lu]ޯD9%-YN;T^@᫳N סCٻwNMMabKںBV[ocR?ƻ1+aO_r2K@mc7|Sw.=2j;C{#dC<]DOg3)m۶Ulfg͚Dg(%LFHwrJ5a7o.\RG?oj>0,X@fU!tFEn=9Vgk#!oکt˗/W;a8fDfkO>]yX6m$999^N#C8$W_U˷>cecXVN'W1e MKKS"$p_YdL:կ١Ce=Ԥ ,Z͙3G-sU䓡,IЀsjDXڃitm!, R0ԱcGT*+cKB)C()gdX(ør'Ry+ ! ]\ X7l0sSO?,Iw}WmbO b ,XAL 0 ?8'ciFR@ޏ2eqNIYÑb()I kʹ:Ęƶ9XP{}殽T@yk%ǕwBb@'zw%fT $UqBL6mpiԨ_PtGa,1"C@`<J]7i_X,G,IOk{OlyƍܵVR 5^wuمOJJR]ҪA@q}gi ux#x"Ixj8%O kHA*69;W bFe˖ o` cvJVRruN^D+cѽ,u(ֱdN$@.#.%:̉He(.kpVH:PX2' Y] P@cɜH\FguI#@%s"p %:̉He(.kpVH:PX2' Y] P@cɜH\FguI#@%s"p %:̉He(.kpVH:PX2' Y] P@cɜH\FguI#WsN9rut A E2y2  @\ uX OZ>#  (ГH'@-c @@PM$@$mQe˖Ibb|^{VZI͚5eȐ!~8! h=zTz da;V͛'SL Hvv7`hIK 9ZU{r~߸qL4I h>}Tٳ]vh"0`_z @4Dyɒ%*4ڶm+[ ~  h婮ĉaÆ~ʒ:u( D={xzHJ ][Zl)Ç[U#_'kf'N׼   DT@/^,fs撒̼oxBBzyo۶M{13t$@$)Scǎ)$WZ%~-[S 4СCݗ ?)))^! ; Do&ԨQk׮_Ke߾}RRRe"yLr)T: Yzl޼k Puaͽ ,((MNN3y ުöO?nMPn_1d@EgBDa-DG*i !*-Pк'㴴4K `lذAW.#FPZU=~{1WgDXl:6Ҙ B'")&W .OƄY WjU%awr8%wo5C=iDVwީ?T3 xQ/)aVou:~xDym(ߌ1cͱ JB<]L,ˁňq>E;pCBȡ+1U_ ay:CE AuHz# f#oƦ}.kԨꋥT86㡳~qE [O1QYYU<1& ~իWqJU5/)BqbBCp8&V>ZPWӕ8 9Xx>Q0HvPY`lFO?t GzOqd#?m qM2&{15}ϼk8hCQl&MS-@M!MmWKztVN%?SM(5]5Rt1qUMV7Tճtq:u0~8z_-'l4,aBH_$ofSOij㯚n*&G >f/Qܱc/ːt ^ѻnɫ|/#q(6e('1}1*w55yM5ۭ (S8tU'T~.h]]K^jժi_~PA|t˖-5>$ᕽnhO`ڳAaP&MRCb@oXzW^J9aX$Rb2#?qa qANc]j)֌bB90ēԓ1IuNE?x,KŒv E/-;wWRRb^H&Tv?C4 &;&o iР3={\~0)))IJG` %g&$@$PAeG憐uXMl" tRtE/^$#GȽ+Z5kʐ!Cd^qxC$@&B;<3gNe>pO0ꫯG}$ 6Tn:3߱cʼydʔ)`Vcf^ @݅G0z + ҳ_|iߗ;w >vºuDtƌҹsgٸqL4I h>}T kN-Z$ ʓ7$@$-! >(駟BnݺL=3_6U&kזC,Y1p@3m ^p!Ԥ  hI@SB@VX!999rw+שּׁ,SQxWH*J $5V\\D/99Y4h )))a͔=zTn5Y4b=f1q222tͦ"~QQo2ޓ mDB XH_F :З^zIVZ%z1Bb.YMLIII6a)Saa3e۶mW oHHf!Y?|X.B5_H=ԯ__'Xd0i<ӟ$@$-A ]wXcgq`B,w\5;_VH &R ׋E'$Rih%g9P' Slйks֘H"@-lKr1Ra`S͵Crh6nMPk82H:|JK%ڲ|م %3"p Z%ĉ?9&F.(GG$K_T"[հ$/fBVZDrx&RF~ZEڜkvɮC2_3d]ZeXs" P@-,4~,l~sT2? x%1xmY֋HvP$@J-z Nj;b>H ^ P@eY/  P@mG + h,E$`;  x%@זeHl'@1@$(ڲ (#H4^["v| @k˲^$@ڎ WxmY֋HvP$@J-z Nj;b>H ^ P@eY/  P@mG + h,E$`; o;b> 7{5#^fJI@R%9uӬ✢*łRi$$$&sWJI,B.)ܯ(3zOŘ ɒ\kKP&%%%rEIݺueƌ^}ٳgKvdѢE2`$' :m4iԨ_^7oW%K1сam۶֭[… M?^ @ D}G^z;++Kԩ7԰aCٳgO@$@&q-Kh- K,)# P % ` P@%x$@$C$@$, hH|P@}H%@   ޒ @(b< !@[ 4XRG$@>(>@xK$@KHHoIH X`I1 - K,)# PM|[ pG #G{VZI͚5eȐ!~gc)HH)cǎyɔ)Sd-}Zޒ 8NܸqL4I h>}ٳgKvdѢE2`'cYH\Lq%K$55Uh6K۶muֲpBӏ$@$mЬ,SQO8 6={xzHJq&|ddd UvOaao2ޓ m7$ p~m$@$`7 h dٲe~>t萤{c\tƌ;c^H&or_۷OJJJbbYf^~! hp[~&m۶͛gϞ/HH ';vTB9zhɑ{ʨQG2thIH$8EfΜ)))))5'NȴiIt$@$D&Mʕ+Yy̶ӑ 8R@ HXPOG$@N%.Sa\$@$II$@$ hH< 8z Գ\cPg0iH| `cwLdtщ33^y{K_竷TV7ԏ;v:2A `:,ŕVI1)6lP' ޽[ĉ*FN8r>H ^P@%Y `>ȝ@Q93Q vEEE~zt'@8C@$R»wڥ tIuY`EĦ=t+bʈ#!}8Js9裏 se9W"0,Ne_uɶ_|W3<#ԨQCmV~) N 48N1~:ȇ~orO?-_k+ }rdΜ9Ÿ? _x_͝;W3/!tG@tSӻ~5k^? 0 .C.2SOp |]wiZAA 2ej^x/IT'|F@6`S+PAQ\sl߾]+l1|5ϗnݺɭ77tk{B/MTpTi{>JݺudڥKΝ;k׮pwmA~%(Ah?*nYb LtO 99YY(MرCZli3FacJD.Wx %?YMOӏ2 M={x7Zh{J,O:j:PM/5 p7@fyG{_;~Z),}iq?0#fZR@C$@$`$$@$ hhHLP/HH 4x16 (& ^ @h(bl 0 P@M  P@C$@$`(x+>,]weH[>v26F7n'6P.Uhnܸqr}~.jJZh!{Of0qTfZ'wumYX$pm_|vKɓ'#^y^lf}pl>5o]/3fPI ^}m5ƎÆ [nEg=A CN;4$==]p[ L\I]xW6{lWsH~CӰ:Nշp b.Ub'bays L#.?G6O6Mms?qDϩajTq~ի'YYY0qȼp <+/so}ԄQ"R}8p7uyi0q|{P@qQS}u+>_}ղ`u#r̭[ƍ3 c. 83p1}(kqJ$--MMGS'!999ry空tJtĒ~4L:կ١Cʖ-[ 7 }Q98F\~#ЗiC Q=T\\ [_1Ax6mw}W[wUӶm[Oc-I$M_d&$]O3^ ҴiSILL.yFlLn~jU/8^ x:\& o*A}E1P\,/<m_Y# `eN'}v~˖-K-j0qJMWYI ; oUI$ PW43+I$` T' +P@]̬$ (vPe$@ @uE3$@v`\DŗIENDB`rstatix/tools/README--grouped-two-sample-t-test-1.png0000644000176200001440000005106613330662123021744 0ustar liggesusersPNG  IHDR@ \ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATxE䜃 **'("D̢`ġAAD ! HK^_ӳ;3;ٝ ~ 儎HHH@rՕU%   M; @pM Pb   H8Ya    @$@$@$@ GP59+L$@$@$@}HHH PJ&gIHH( $ @ 0     #@(᚜&  >@$@$@$p(%\$@$@$@HHHkrV;d_VN,%=Br~ @$ P$ME$6<j=a_k o'c$P(%Ts$ t7JXbMד D@jcr$@$C ?tc+IЃYop8yZ :T++W^x!`#ӧO .s9Grss}s7ʔ)S\O?TPAzڵ4lP W8H PJgmIr-Hj˭SN|rW]F-t/s=WKJ䢋.w}ƍ_VX!?2|yG?`z-Νg׮]`[رc孷ޒ+We֭裏={@ $o &/'xB &'N+;w+ qlQCɏ?(NOJ*Z:y >\08 hC B-AY 4pŁdZ3  $  @ 謲 @V/p /PW= 15nX_RLy睧2ŋSR8кYe \VC^vdgg˥^ꃣsZoI};NG?_8_vnw!l{{=y1m"o7j(o/@BJۙ@v䥗^huz%sZ7hCLF«Vr @]wUW]:p~ٵKKi栖V3]2fa<ွOZZO0h~B @Okʶ/}^QRXvM4њ9hu݃lٲRxq+E`HHYi;[233lt[B30K-հP Rh  :׬+{/ i~ݡ>DGLpLu'""R3gJnǏ%p !vvxa֫WL=+r@Oft}GZ(7|MW1<)c|ae˖i{iӦ "ӡ}0W_^6`:t_|ß'$(%bζ%ce {͘1CJ*%c̱ ֬Y~W^-!1f =zTZkjɒ%fl2h B x@Bc`Cfa8_ך0ڼy4m4jn=ݻܓ&M̙5a5{'tA'@sm#<۴i7n(>,ֈVӠ맧K@hkimfxu]ڷo'H.]kL0A^yWA??}䫭rCxǒ0 -Z3ǡCB|X̟ӧ_T^=;w$/^~eAߞ>}z >38qnAn0(-SgԨQrKϞ=믿O,Yr&?6lEIŊu}lYoڹs='h¹eEĉ2fywyuV+sD9]qu 8l!?tX͚5%%%E/ylyqyN yXdO~e˖~G(J=otEY MaS 4uիu.?DpT#G|ʎs9G&L ~^;hCEIq%} [lK&@0. @/_^=Q>Z^J?\s5+a0 >\k04FG$@$Px?Wn6[ SIPHDΌ۷{Tjɂ ~pc8 @HHHp-[& rWXs5ph9(瞫\~p0 U,H)=zo-m۶uӑHH l0G;\,CrK7nSY*W6 5(fϞg}'M C1ZjkmX0  ws13Mڵ˗RJpx yGt <@^t$`{.8{q#y@*~ѡ? PSk$@$@$@!@(" PSk$@$@$@!@(" 8n؏?(^xڀeu8L[Brʔ)_$+X̊:t,YJT!tn~~~Ȋ3 Ċ)SLf]dddAg 1&иqcIN>(ԩSRlYRqY3;H+g;v,`8?.;wl2KGB`Ȑ!8~F%X]6%;Jxa5k66mkk7mTn&Azd4D%뉥q=իW˿/w\rЪa xl\~ҪU+±uǬYreIZZGɿoٹsg f9b%/E"_U>Ày#gۣ>#y?+oAB74-bmڴJrO?zUvRJY3όZ5 '[6{FE~(h!NGO.ڨK.>Øw}E[hD\{ߋ \?۶m(,ظnܸqtR֭[ձtyB֭O|>S ;ciw$ޛ_~~k.9s+Jpxyakur HT }0a|駮sO$\.g$XI;w|^u_\$JtO/Rmw#R,D ߼P`6 AZ)Xx>rOlܸP_݆ g?lܹscǎߠq"^F:u,%>R,D P@)_)͚>kk׮W_:_hQxqCi\~|4/h7Ob)vQtۥG3~%q QQ/#7܊+dA v ^p`vhV𵩄X|wW_'Nz-PsZ )ZV{_a:K! g7pAߦPbJ Ç`ZF_r%u]zcV$/|ԕ&+2C} #' s+W_"M4 Oԭ[W'>>Qz]1KvN@%*6m [)lAHpUonΜ9O3Pw.Cc נbx7] z3FЏ1ISllH>4eM57ʄA ӼysP;(E%RF}zËBxCQ+ 7EIҩS'Yj2_ٳG̚;^6|A=[\Q'MT^=onTDY$C 3 ܙ܈x`oF%tx-(/0(x9<į Q_ҲeKm=,-?)͖wPv ]RZ XCUC,!af -f}_u7}T^]6l #H3>la%xNW #5`|D,YD0+ LϘ1C?\rƙՆd6YP;-<aZ}{?*jCaXqAC6  >EWiz !P/sCi, ]PWqo}U&0C`PB@ 0~!H?fI?ooLZ<-ԯLp_Ûj&fi?Cr02o%o3a;jر{RTR>:2%͢xąFcӡMއQ0 1{Wc^0eC~FvbfsTA+B780U>n 34kwOq @{>炷ӧ~[hR`g0pw? 9˄{YqYK=CЂbdZ)hH1 J>l`w9,9)X+(*@@>nNxO c|˗//qa Xt)P&ah}=30vjԨ!k֬ä|{ ㅉ|a נlX0X8 Q g; @EPޞ={(P_C:Or 7np/VceÇC9Z?Ã)Xq)]ǘ͉a=w?1[ pO'$t-vxcν裏j538ã迁ógowg?1pb(=CJ,9-~OӺN3 q~ ܝ-4.ЖcI8P`G0ȰP?͔v_oP;>pymˇs< ؚwٯ:7ӷ͹z@9ţ RqU5`B 7 b# !'69J[@ Z׌bsʸZPDHK* 5PBv4m2xjP•DԃPR=68W_pqe)RCih4먡f*\JH2f* 6Zוmiz i}Nhʸ_oVO:uΤm۶27Щ+^6g.Pl mЇ"BG8pرC???sm e `u8aJ<[X?q>vrE+6@BsGpGx7n RwJ5kK IK]ͼr/C kQv 1B+4 0F L3X- H>xq ?ȁI9ib4iXG/8j!|(սG I%=Oe/tjy %8|@M߱ʤV ?T84`JMm Rx\ qsBt>tcC-C~?@{dsz}|GӅoyp i_.X}μ^-[yk 6?HMAh9+;oa8?kbFEϊD %™y0 E`,[kDy/TBc6 OsfN 0&ş?~?`*ػrP!CP; \0FcXd 93e& z, jM :  5lqA&XyGyVJ{Jݽ&=pXZ q# !"s$ds1nvja3М=2vU+U 6#  p tm^YʔHd%m2RMB $m V7NR.4[re-’?IHH b)RX(){ƦҽUP2ܡb:ڠQ6 6[t }-aG]K.f< $2d[%^Ycbr/ڣJ0ծ];Q ,Qc(,9ٷX^ݩW#3HHH _I#PîF3fЂZ&cxKs9G֯_5j{0IHHH/ @酙\Եm֥lM>ݻ^z^<&  D}At`7 hΝ;˱cdѢEfкuK/uHH B༇ss515߿;V &/穧 ׬Y3ԩ1B/]t $:t>}8=B$@$@"`#7;$#?g^@xQRZyIJNpb*ak̙2x`i޼.9Ę={nUHW5j$Z 4iE\HH@֚!㣘6kUVkyL]vez1ĭ[JJJԪU˧5k֔Kʾ}0}. $CidRJ7Z U?yjNVb."^NuTR8@$@$@H 9dT혪g#yvbj0}   BJC$@$@$(՜ Pb   G(#hGdeH A |b$I6M;A@DoȮC'0" !'GҳLT&tGP KD ­R,=-Z+ M1+/EU*.'tv6L(/! ,O)MILed.Ծ\G-Q%`P Fa$@$@1#,ƽcƎOF3$@$@$@6'@ ' 8'OZȺGQwOe#;W咔V.f3̈HH S2ulf8bdT3RۅY) 53"8z&͏zf9?>QϫXZYOD=/f@$(%F; B`C2?z艶>%}{T9gȾJҮAċ@b)2;e҂-L3I<=m|v1s f53̈O47ҸfgVo>,>TgIJ0 KRSm}o8$IIIN++iYbkH=Fqt2fu`F$@$@$@$ @abd   'Zu  RFu@MNW-GS'V[u" 8vi^`h=tT)Y70T@pHH r޶/-_e_^/18%@8mHHH z[LEF )9t,dƨ  DHez$:=]E .#{EE:oHH$,}_ Zm H(f Ďf8e߬j߶(wrX"I$QȁI& ]^ğcX;%dZu03d/ 8{wvVvmbbըu$kX8 Z/T:-eTW$4})nfF$H3r!$au%$@(."pC+$)ksO% cH8fcI@RjiQ^EL ľ$pR MK P:u[n*ڐ @K̔{G.9soBVwڵңG)S(QB_?|=*#G H w޲~8djܪU+[dl޼Yxa\ըQC%۷oS;: $"g.-٧/4,u$''M+DuIRQχ $27kL1ի4j+1Ն*7Gц/_Gڴi5H Q T,.n{XzL&Qٳ$@"4zh- @t ٺu,ZȥIKKS_{ xd {{?~$@$@$@$@XaJ;fmٲE`ӲeKeժUU84o߾ڠ:55U D&M*xN$`^$.%>j2$d]v 肦Sп8SK.}an @d #eϓұY^#e *^`; oe@I)D("  RJHz|O)OL sm 6*6ؔtOò$$@$@$@ Xĸq0?ۜ ȧ~gΊ!$@$@$@$0]{1_t|) ؁@XY3V]=V3 %c/%,bD4SGVKޯDERG4m&FN"T:t<# HNUkMqCĶO @"2@Pvڒ"HHse߼a8. D@P(3m ƩCRH F(=5ޜy\U/D$^Y_7( I 1 $WrLō"H 8 @~IӚvU/n/rAby؇@rT> QR]sGX & RGvAzPs3   ;"vh%HHH (N&F$`;Zt75 LEAPQPg$@qA tSI.QG|d vNJ_jv"Q3 ;dcI E rW16}kK3E-Ty1 @d(2 PdbQI SFSH, X&@2*F$p'L<& [%   @4HHHlE%+䌪ZڮgIqh&eH@WcX&HX%lӳ$@$@$(%n۳$@$@$(%lӳ$@$@$h8* ~ ^Ay Ғ @P [Nq\)Mz  ؏5@A&eİWДfً\t"V6ȇV{e#+ӂi@L+KItiLb&$@$@#soQv)UT5kd„ ZnڴiҴiS7otz-#?W̹3%l-Lr;E-$f",! 1L"5k֔9su]aZs嗻š4i" 6Pra Q-U?Wr  SOi@߰a6Qٳ㲬,t6m2K$@$@$@ \s0wPO@?\|ŮZ{+HHHH@zZZ$'.O999.h̙.۷:/9eM&n?7eaAHHH( ĝTZ5YhJ2R`Z|޽]qq]$kduv{WnW=ZY=j <$@$@$`q'U^]233))).w;Σ}LB/7;wI~̄HHH@wtY;ڸq[N.q0{  p5kW~BرC $:t>}8:+A$@$@$PN?P4jH֭+ 4iW.ھIHH1hٲe~AbĥK}0#ߴxғHHH,(R(T*U*˖H:۾ ؉@\ @vYвRGGG$@$@$;qi3'   D$@([u&  '!KrZH&Ydiy 3c  @1(Y:V_ԟ$ ٧XR   (B HVJjٖ@|^ua%HH| PeBH)^KRK/uY^m_X 9ټY|   P>3^@{6rث,- @X(HΨ"I) %E8]?%bЋHH(ٽ)kKn{cRë)ϋI~̄HHhveHHHHHHI3ە"  BP8 "  p& @lW֊HHH  @A0HHH8 ޙZ{HI֏#   @\"D BdHHYw  HPYm  HdYw  HPYm  Hd3墋.J6emۤ!KoCc_RVwu-[R\F"XM\X6%k֬IiOs֬Y2o Vxa<~ׂ%yW%Zaߍ¥E۶mvbڴiچ^hj#'2}t(?]=<#>Y|9R16X.zBXhx}2Q/ZjF- N 믿Wk w u=`Se˖:L 0 zyߴiQD [oI:C}jbm 3=Ԛ7w㫅cV0Հi#:rV[2y8 t9,/#^ތ"7t~.Zȅ b &]L䬴᧌왋[l)V ?'_'j[ᩴ8 M:U mڴ~x߿q] @&6 =zavfǹF"`:rt#`􀙠±#}7`Wy@>c^8{?TގMo:ssQ S/n YKv t"7x(9@r_oX:SF(Dko38Ǿ`ܸqwy֭[km_ 60`A1J`;vNF9C螓9/}?2@W vOw 7ȁdkA5 syvwxf$mɣ x9+m+k[ay|,KO=&JEw=NŐ l9՚N (<~1]^s5>UV6Ar10ݩ=k 6:v@`I0v+QձcW^yEO-V_j #/u9n72p0Y*V[ 5n(I{wM#ݷ.]jz9K]`f/^PkY} i.?Of)aǘ0aOX<{p| Z\#A X'A-0e`M 5jd/] 4q ;?! .,PoCjΜ9~ˁ^gFm` N}i4taܸqZPvƃ>wq+1ePjJ5 :T-]q}7>ZY J3XS C P >Pn^_}zI'HN *\8GZPѸ袋[!KZt IYͮ1&Cj=J;VAh2Zb#O Z:Ќrj^ :_ FK&xC 5 Юu$nj_.he4iŒh}7~Z  $>Z^Jq5tz`15=JB5;3`}Q%2{#>,kwWwF,Ɨaq_vbZx0N/cI,GYav$rQCzm 콄Y6t Pˠ}A0=3"$%zSrteHHH,0TG$@$@$hݼ ?Q Prtr$@$@$@PG~$@$@$@&@ʑ #@ 8 G7/+G$@$@$ TG$Pd>,jJڗH:u$j2WyR2p@9.\({m ?vW{ɫ*jcL9묳{^k:+q̸%pU6eH &/ߘTTI&N(jgoQ;Va6mڤq/O&âJF'UTBɓ'e>q1 PrV{6$`{~/C u Z={hjq쫧6XjCWg%H^8fbiIڵk'/]eܹZ2e4m4nZLvUJ(!Vrc%+2HE!6l$@"@Qʐ `jԩ~9stMƍ'ǏוKJJ\"1 Pz\V"HQ(99Y7+Kf̘!J޽{c駟tQ5kxdӭ\R=*-[4,qE PrTs2$`oŊC^<|7zZ;A7n3Ps9G;V4i{>̰kai2h =־}{W\+q\y@$(՜ ؟?.-Zv@XB̨Q\k 0@'=ԭ[WOs'5iDN8!UVUVMM&=ı?QրH$C9# $[J:u<L_5L//J؊+?RtiW8'$@"u՜ 8@JJy+[)rVJ$@".^fiIHHH8V`tH ^ l߾]0 rV$@'@m IC`act   d6d HHH$@(L`N$@$@$`߆ @( IHHOې5   el}M IENDB`rstatix/tools/README-two-sample-t-test-1.png0000644000176200001440000003015713330662122020221 0ustar liggesusersPNG  IHDR   iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o,IDATx xEƿ )HoD\ DW!DYXEQQ* r< ,rp(@!!v; 3tϼHwH `(@C\d D/5'7I~ƠL[E t/HmA$7YrNp=&@joa.2K@d)ܓ BŨqPW"Axx]ZݳzOq"&aaL_c@3b.!ǻ:5ce۽JBwbGO[qMärHtl7XpYOJkmtٸ"lH͖H~A gJfHXd+zV+9{"^|(s߇שZV{5 稑z t%̙3Gʗ//m۶-~ZTRŧG5FJ}K^^\{.QܹsN d۶mRR%i֬5v`#s/m?+"\s 6LnQL2c  ,x ';С绘3h 3g>~xV<r7KӦMeҥy{(@SNVZJLL 6<2tP;vlsر2m4q2j(1馛䫯Obcco>M6ɏ?({+Wĉ޳߿@\/_ѣj*yUId֬Yyfٳg:tH{9u>##8< `fy0̘1C Gի t2ɓex /Rj֬D(77WF!(ϝ;W?-[nQBzjINNVϸۜ_B9/kv/.Z=['!(,ZHZhan׮c=7h_o]j|^/aQU D߸lٲGO>f4C6l&ʫJsM41㠄_U%CM=dp R~ @:ԼysuILL[ p:uRaxb% B|݇rk k.pG^TIoΝҪU+ݭz/[pfpwC QSOwgΜiZ.l_͋A5|=gA2Rh޽Nv=9Xj.+V&C8~ݎ;6áٳҦoUk7zP**a˘1cTC4zA3k߾KKK~ZE{Uݵh,І#oJS۷tՌ_HRRs=>ӧOz<6K]Kk.n=C ={phnݺ4jHz+j:ET(b^FtRďӱcTo)tTXLv \/"#GTc{ "h8Ư ?^ET=2bᅄaoLC{Q7 hļ[U1%zqy饗|J2k`2).QaGSo|+ !@Н;}k<@qix{kUpXx1 aRUzw+MU?o@*eU t7hxTSm @a}gt!@j S^=1_wn^].Ӈnu6%ZѮ CM{yG0'6Bv>X^#¢\=p},#ysVlAyrMq?h3*m?ҵIUT.  X,CPm,FU0e!P"@ fZIb(@CDJʹP,!4B(ri%Y,Ch  P(6J#@X%Pm,Fd 9$J(@L+ XbBsH PB)V 2@(Rn3$`1 e!P"@ fZIb(@CDJʹP,!4B(ri%Y,Ch #GٳgC)V5ƍ̙3m!4BHӴPB@@˚5k5vXiҤTVM/Ǐw Ad,J8ƍ ʌ3dɒ%&IIIn? ]J1c|.߱cL>]-ZDϟ/ b ӧ5 'VZ% ,iӦP[rDGGK~sҴiSYlF ]N>-?L:UԩBp߾}RF %B'7##1~ =䓂 9UЦM$..$@&6Z A;'zƊ SbEi۶СC HrrR߿sAEh۶mRBСԪUKV^B.;;[bbbQBcʕ+gxM$`~cǎ J|GfȑҩS'vڒ)aFKOO=z ؟ڀׯJ<(}tRիX 8ݻwKϞ=U!~+EFFʵ^D Bw5ר-ZDAQ5Cխ{2`D#~+k޼yYfҰaCUKII4Dӑ !C#u֕uIVVs-~ # P^. ϓ ؘ\Ct” 6H;ly]j֬)wy =@im-aO*U8g Y Ȃh.^(]t!Cn >\ T/tQ+\믿׼[jb2cUqXӗ[QN5T|r}xO>6?u^=.\X!_?3aÆiqnnϪ +bX/Ê{>ыsa)nݺX4ja{Ǽ|ҤIdT(ZB͍`~ۀd'1DȱJx]F F_pAP킨|Wrm1bsΕիA?lem,j8z4{QEY`8RSSSeԨQje_|QC5= Po1(T\٭eʔ3c 8pIB5Lo@g_7,g9Fc5'N8نcT9,4.>DU^=C8 rawdPlYv0UV~z'S1 \˖-Uc)q<}9娎a-(Lp7 r֏1Bnh`gr9tPe+_{55F1Cm„ RXX(;w9s:vFGu?N˵ . X{oqoJ+K/TTIkӦ7ߘWDQFap^R=s9x6nܨ{ĩ@zv]wi+dr;0 ]6M&cEǫ_K4-[]?PIFi*42{XQ*YӸ[E pu <;M?o]h`G@@ P&P'@ 7'>MNoO$@ |>B@I dI.@iiijY,; rtR2vX ,߿?~1 $@A@4x`Yv̚5K{=ɜqƩ0ޒ%K,< %NXb;dʦMB EWS۷L>]-Zde&+[R/afʍWwHt t7 6>P^c{씉UgDxѷU;_3K)<B% ,ilϒ-˗/ɓ'/pWF %B!V ؗ_JCX?;)4H)'aGyļqk3 k̑¼yխ.]s`\8T]B1Ӹ;a#$@ u;?Ν;+ի]ʆz]֭U]1HCQ{A,G{G)1J@kזL'cts H*@ ,P["ád]4׫W/R޽[zi\o W|2|p駟 ćzHvMZh!2rHIMMÇKrrt] ș _U ʞ={UVҩS'پ},^Xڵkg$hF{OfT=vLIIcy!=$@"F޽{R[֫W6q[ndee1w.1Hv.@ . \Vlle0 } f_LH4PJ*I$ WH4PJ*I$ WH4PJ*I$ WH4PJ*I$ D2 Gzrde,tkF|26ժRVyie!#@1K.ʫժRFU҉^ܥlܐ]:'r%ukjF%+g_^}GEoXpӶwd$`k [g'{;h= ؚGI(@?ZO&@ux7 ֓ pƻ#PxHaS ӴB &=4,2F?.w;|"Wv:EЋr%풽=Ӄwc$U:/ 9+̂K >t"Pwi:J$lkgkr"1 E^ QkȊKArURpf%RClh0F,We2;L]I233e„ uV9whM4I[H'r2dԭC$@YFu]Urʕn (tYj̟?_BrqTH !hݺUQصkr-#˗;ʕ+yY;v4iD/|q댡p2$@WLk`t]>+~؉'wޒ!3gΔKJZ[oUJƍǍ' .3fȒ%K$--M\ڜ&'`Hڀx A)Eo]5k_x>|X+ԩ:׵kW[|Ҿ}{ٱcL>]-ZDPKHH+VH>} IyVB1(6mZA`P1ףd+N]Eu ׯyx-[F2C't5m۶[v8p@ƌ'5jP"N:F? $@FR hDFFJڵ%**zг6l0i޼ :TӪx]jUڹs=ڈ" I` b*U}]ٰaCRSS}J-χR ۨvA]BW|^ \9xF&8J@[裏ʏ?oР$ӭ[7UB>PRGom>mڴ1AWlVE1;tčr^~ ؀kQÃG}6lbB6nܨJ-ׯpoGcԨjrtaJ[.==]'9ѣ(u6q/z#ѻ ѵ^KrrrJA{nٳg<&1`#>> `2 /O?rd$TEG$ tCps7o|0DG$@(VN-ժUsRjUJMMUFS6l7 g~ Ϧ\:%ᮣP-e-˗/7o5: pOjKQ.]ȱcOjSd Xvڒ)%..1~ Krrrd&ڴ4ٽ{ H,'@-Z|@E42>|X{2`g HL X6o<ӬY3iذDDDHJJ GAF _-E T\n1wn/d T.G*66rQx^'08|HdGx|d'w  r6@i2Y$` ;m$ %@ ҌeH(@v%HAJdPK(H3";!h# ) Pf,Ev @C.FR X&@d\$Gf,e[)ZX YBPfД$PRS*x @ HކHw ߙ  "@*! (@3$@%DTB y  P|g+HJ@6$@i=[y ؃%ٳ2vXiҤrq{$@^7N.\(3f̐%KHZZ$%% KC^+#-Xn.؎;dh"%:8|IHH+VH>}lF \J@+Whׯi}||4mT-[fC$`}I59Sddd8O$`s>}Z5<ZjUB;cFx駇H,'@QQQZ0 <'G~)$@!`kժ%.V ,&&){RPP`~vDG$`ڵkKffGD? XNz%999zj-zv-={4!? P-$11QF)raINNT `L Ir͛{5k& 6III4Dӑ mݺueݺuzĪU<ę %Ȱ.66zb~KwW3 @r`<06hֻէ~8"O!*ʕ ]Щm8L=ť;(\/t 7Haa_+_G)S뱶y%mC \ U$@WCҍWPv١\[CG6l X́Εۀ\0HOX|ѣGfٗ@nn+-[/B aM` 駟b sϹ3~iЕ,Kӧ i,?䓚>_p:7k,Oz.q}]uNQq9夳0}r੧:G`>QGGkƛn @I3jcƌZMoV£W4}dq$N/izT7niKCk 3rq ΆaYXrG c5j$.,ӊBC{IkH :%kY+?h  PHe7K"@V~)n&Ed5$R(@!L, XZAkH PB*XLɳFVرSNTfcvLԼq2j(byLjok}k拭B}d穉0` l //իW_=L~c,JX"%EFfϝ;W]Mgo= -w}%(nA'##C :9(9a-98M,IU0KfwS>3\RϜ9s$!!aAC|p7,˗}>1#c9 e ={{f/v4j7Wˆ8}-9.OM02(@Ɋ1U%,Mq# .TK J$/4uh.겲$.. &Y.Km+8 XRX9m߾]av;K`}eyLjoY/OlmQٲeU {[4uAO4Iȇ~n=#JFq?oܘ!=2m!p!M/7]gy\:V߶FZXZiM)ǖ2zȐ!Z||T<,1퍱4Ἁc5 pED%htHjlHtSN`:tzl/7q\nK8 KeGpء֤*l9~ex9M݃Om@g' `|,I;zG <9ox @F> Id3$`  k $@ lgI(@ZA!ID5PB2IENDB`rstatix/tools/README-grouped-two-sample-t-test-1.png0000644000176200001440000005373714011711121021664 0ustar liggesusersPNG  IHDR@EiCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*i@B0w@IDATxEw#9g(QAPDQҩS )읢 (&PEPQAPQHeSߌ3;ywgv_ϳ0]]ޝw,; @H!+UE@ Pʝr* @\  r@)wʩ0 q  @ )  @5 )'@r #  Pʝr* @\  r@)wʩ0 q  @ )  @5 )'@r #|SC'_ʡD9 `r@ *OM1 +('~/,N`ϯ: JMeH>_?JXiŊ˺Gr,;@ @8LtYWlGB;[\,tpAP%g* '43onn@ u{q<2WalIoRv?[&I#ޓL(Z] @h?Ɂ[a_˫KAQTJψ;qmV,:-iKu`(OvtPJ'@T:7B֩,hY3_&ʒ%KjժҵkװVZ%uԑڵkt J bƓ` <#qFϗ-Zdݲeٳo]QQ,ZHjԨ!:t؆SSo\.w}4jHF%gy4h@z衐5?W_ >V|ҽ{w)(((/&M.;vԭ[W;<9}zT JO)sN qí_~`_zrM?4{!۷o7n{Lڵk'x=ZvݮO>o˹+חc9F{9dʔ)Sd…xbYb|rO<Y!Cw3g˳aO䢋.2x8q|7O?ڵk[n17mr~ A "d+> t400ahHzDoYƯ999JaaY|MG{ʕW^)[n94hu 'o!gϖ#FctI~f_A.9˷v݊ ®/rǎgڴir{WwMy6l\zv} ϓny e:=,ʕsΑ^zI|σ۷jMW,siדhn:2YP*uh?|mM2s7 (ڤA—_~)t٤f͚2h Mk.l޼L:]4tʚ-{e}REڦ-[&jkҥҹs,(ڵdX6 (wi>\>hMڷGvmM[LGB &?~aSO޽{[=VC;{vRJ4O]&Olnigh!kHY2C4_o{5` CdeeրaÆ/9ʬuVϏ%=;:*.%;̚cڒթS'y뭷D[uuOokvif XV-s>K _+ h'W˸qL~1֝I; k_O:TCc6kܼyLM'i3f,2k0m1㷜7P*uX<wo>}vTzu*._s'}AѡuJ?`}ܹs3Ioips׊vQSԳ>kô%M[J~Yzmdjz T;vۙZz MK3fq꫓j f@J*q B}npӤCt~YfO?P jkߟK.t%OzW0y6|tア?\;A_:]g8֑[w!:|^3uP?s!`r-HDJiNXڗdȑv[,hcE&~:&0]yER9+]2Bkb&c:jc=ֱ`I[4(50(Y(;9~*kW z 5ɪ+t2)mpH6E Q@ - Zlf͚@ 奷~{S{2km$=v.9}_$G̔;};fuߜ!V>}t'2#ZnR1 ddd[7Mɠ&Xޒ ܩ`X8ENN9S xnie֖3H 0_[`7$em۶ Y6MX;Zkxl#ՑU(]'H<&ے#@t@9 @\$-0L  ED.@pN&UA@s" HE'  @t@9 @\$@䢓IU@@ :ȅ . rɤ*  PtNB@ dR@N(:'r! \t2  D'@@@E@.:T@ Ή\  " L  ED.@pN&UA@s" HE'  @t@9 @\$@䢓IU@@ :9#WAAlڴ)%PfMџP)77Wvj50 Jvvvo޼Yr=+(8 ,;᠕~ 4H4hRST hps饗<z  Q("@@m@n;.a7#tX0GX.VV@qq\K( E3Ӕ![n| u]gꑓc9=O?]~'6m̙3y@<â,Ex뭷'Lrذa36,P md̘1r'| G/"`(m_eR^=Sߴ4G֛BW@gҾwޑ.]U{{W&O,;wkI'd.3o|O5뛴{_|h`&A[ J+Vu͚5 3k5Kn{!-?zoN8"%\ba-iӦ_Ih6^ӄk@㛚4i"7n]d^k->CC=|;Yti@> P7xyH5*Zl)_|w޴uͻ kUu;w (~޽?^Νkn/9R,HEU<֬Y#_~隠)@MڽשSGvW>}h>d?!ƍ36ٿoRH eXxy2|p袋ʾCQ(";3uUNp~'|"_wӾ@ @͛'#F诲YƲ5P,Z.ۣG3W^1GufQQԦXFI[nVv~g#3@ fpeQ: ɶm̏ηF뛴{[k6H=(~m3"_O>C:bL)7n,=X֍!Nx͗KmM[a"MǞ={e֭R~}r_y啢?4X;[n1֭뗗7$*CB \s)KyRQ ~BPg)% r@' 9lQV@(rad'  $ '-ʊ "@T.@$Q`:&!,:NGEJ Z} dXreTEycrL& PXX( e˖E8H@͚5IᎧ ǓpSb]9 ؝;wk7+D`f pwUTPP ?#S;K>@vas1} $R^=\ҠuҪUpX@BƎknӷJׄ"ɗ/mݖ䥤x$dwpFd"CU$}~>Ɓ@w}B'Dd@@ R@(b"C2  @}璞.s+v]vSC 1@@޽[.袠tM2m48qL>]VZ% f3@Ha vuI*U,Y"Ǐ7PSJNd̙2pmX "P!-@~hP裏u֬Y- رo^f̘] @@ o5z!iڴi@WX! 40AJͻi&EtY Hx И1cm۶2|e15@[n:}L@Hg=:`;  %>4OJfdXiiiS7o\/'ikŋ=o@*hϞ=34-9ښzjS(}4i":ߣn۶MyߤgϞr'{W?3ۖLjՒ:,?3]{RF[ӠAk׮ak͵^prvA$u'pp׾_Fޤ@ƍs0wi9}(x'C?3!駟ﶢᇲ~z袋RRyZbJHh2|pپ}\qr-]w%=z+Vȋ/hnݺmj{$''oɓcIlV2eƓ{1yG~.rcҸqcꪫ̾^9ERG`ɒ%FxQGɛo k=ҵ=dz=17lpO?T;o [=ͺ;qݻ[yoU~a2H׶NaviҤuw{7l~G*W췮4׾w.x1p@k̙akҷo_?'Vzmۡj۷حG|{ˡ+5jeJ~d狴_@w;\(Lp)СRZ5SVZdRYoiӿ~Ք.Æ 饗J*UL3j}lڴIN=T9쳽zevZ2ϋ\ {׊hz[ndJ*`{3Uʈ# D[ܻEk=ҵT?e˖fz׬YSl= / EֱcGr}1oRF kt_G˩qҿwu>+ڵv!eHiMzKr/h~̭zyQ v=2eB]bJLvKPiMu1o5,1+?}K~i;vv~IYYYA%ͷ6h?!Rj臄,-mV 뵨--%S4z4׶WطOzkL^:HD[=zPY.f^;}vJ&A˵NA%>!֭4N8Qo1IP +@!ΪB/2H0}HPQ7GaNL䊗stCj:Q[H/q:ҊY@vsc"೪u"Efˈ u{xn*B{:-)c"vNƁ:>@(@   6 Q (9y̓~{]g'sO\?o\]Q˔q+;6> />rU9yp+9{{W@n\.gr\=9#*U23Ah~A.)((v:G6}{6o,#G^z\w @Bh,Z%@B }[o%-2h3o<<f͚Iaahh /@@ G[h ռys40#! ~ݿߧĝòD+Eۍ7)IRqq T[rZl/& ;ہITjr s.a5(a@ T Z;qkj3o^$PjŁ@@0@apX  ryV  F( @@@< @a Sa< OR)+]n=}܏/PPX,?o%ZA6lϓȖ&u*8 vl%kvJlGWm\>;b/Z'ީVLc> /P)@|v+vU=wi 6k+:P8!$L +#]zwtϳy|.RR 8Yg# @V*66B(o=ErK{Ewڣ7h/kV>y|Fma @"fN!~8g)޻V~-xV( @ eՓ??,Βh l3$:%on#cL)P˓R!;o@_dRW%QwZfs #"a߯Ȳe/c%o뒖QMjv}V2k䢫T@ZZe׍ӳjcgՔzq?^"@Hm)'ˣ(wE}=%~ߔ$lPp@/hoc*gIWT' 9QfpU#iYujo XƂ mTMtiRj p ,?G8 d:Lx/aҲ$>@^xVa9f d' {2*7_1Jˬ):%jkT PZz(68CR~ROReUƧ!!@ U?GGȨB258G hrw}'{8IPҽE?uMI@J@(Stʜ9sLӶm[7j%nG;>\%R60G>SC^o۶M;8iڴd[cO?in2j(yĈfSnK:u@\veެ]v 7/p@Vf}trv\ЧyBA@)eܸqK/^lѲeKѠ娣2JFFtϸSN~MfϞ-*U2|k?"m1$-ӓO>y  @PH#h61#SLUV&@ ,0A˼yBlxǎMtTvl>ü۽=/M+WH  @$ o6S2i y";wo|gҫW/M7n,[l$jOo@@40R_cϱ6nhncikP:tIn:;w Jׯ t'YF.]*},@@Q~9ÇP[FL4I&L`p a sh`.\x6Q^}5@goHjdȑhС Z H֙o e:>Bڵk뢐if?.4xH?oںuk3^kU&3  1 -g}Vn6Mge퀬GVDJmѢh͛7V`m:@ K*5;G"يFyHI ONHB PwX;PBi%FI˨Z t~m6S޵E ȨZ3 Rνs,f͚a) $@yo߾$*.EA@.<@e?{@@C(9@@ rp(@ :;}Ubc[VoݿX陵$h-@G@,yO]?&n^D+K7IZzVpSF2 XD2V7˼dAOdj{}.Pʑ|@wN($R -]#XEyv[r&N*n$ܪ ga"!|@wN($R<"B$`| (#lbm-޶@-ui=س:ȀCũ(`FԼpb=ԇ / |Oٶth'h E),!O!r٤" {dĀar8kմ78@8 (N@W(y %C@8 p ,N"P/9:YocL@!"@Ȫwd7>~lw??%zǸ+#d+iUzv~)?Z V7u˥> |\*59]%ȋqP`- $PJ qX`-njR?pKO,B GwC@ n-;F@d J3C@@ n@qe  J3C@U;/5:NT' 9Qvp@uLY)( -T8@8x  @ g:" o@@ ,]U 09=uq$lKB 9^^IV 9AH@@+B> j{M7u'N4Cwh"Z*~UZ%z/P98oZz @ $<oeذaaSdɒ%2~xӿzԩҩS'9s 8&OGh$Q H(I&Ifモ6mi֬YgРAu;vˌ3x V -@wq4j(dyWX! 4ݴiSٴivy_#v+/o@@ ?Z\[nbԩS' Z`{޼]v 7/@@[ P$ IO3C7-~ػhΝ2gfQ史&i/(,@HI&H͚5k_ڻlթ%/]g~pzCϹQ@Hqf'ȷ%H;J56x-(."n+剙e5N'YH @@:N8w\ӧ9?k֬K7ߜU9;C'ީ} ʼn lm8? F@%@Eaj eX  8Mig" @L@@i@N;c@,@TfBv N r  Pf2@pE@2   v(/ Ȳ_61IDAT@&@3Fy@@@e&d  4 1ʋ e *3!;@@ $}dYL)/ $@R@v믿^ڵk'u֕!C֭[! S2馛dڴi2qD>}ZJ  9岢 $@foɒ%2~xoԩҩS'9s 80يLy@@aI4k,ΖAy);v(۷3fx@(@@+V  ȷRM6M6.5  P*k:>M:uEýY5k} @@ @@0&~̔ZjyU\Y} @@ @@M4?<999RfM;w>̻l2vX{^  ZJƍ˖-[UV~x F ~N8w\o}֬Y#K.>}x@(@@]t1ѣeղyf9rKZz  HHKKVVnZtdWaaL4I#4 @(@u hB͛gZtT> @R@鄈$@@XyW! @F@H[`9~s1ٔm.sN}?3GG%?Fu/oV6"qطo_ĽYv!9b?CJӦM>H&LrWTaǍg扺[*:`:X$qRh1c8VQF)>< +Ci|!mǎ>Rxr''uYp@N>@@}@;@"P&j~zٳgR/ O?IAAt9يrѾҩS{i*_KFe˖ٜmI@pBG /K:ugvZ 0 eX cڃFC*_5kݻwO`Æ fBޔYk̄awPTTd-?vYNM@ :sO=m 'K.D~װ%gFmڴ)l>'ԺeffJ:KGeȐ!LiժT^]>hY`9=bcܴiS#ANs+2\ac}u\{;3']k׫WόґᒞS{ݜ|ISO5*uQN=T?q][Z57"''/%,#Geٲxb~Μ9rqǥ|8^PRWZ/ihtĉe޽&(r/c.Řtw},4w7(7|tE|MIrUWy9}A(`_$:Ѥ7nlz-91`.x+A׻e᫯j_Tifk֭ZʪYe0Xv_իW[UVg弉]iٝ-Wv@jΉ0`g=獥?nO\uon٭=rO?ݲ?-8hoֿnOv0h-u)~=Ye4.=Xɬͺ> `]2/op8 ؑuGvX+##ò-Wв'͵S)[wܹs,}ҿ4p@4X1kѢE[`qla˖-}<6joBI+W}D믿n=u7l9̐k]V:+i:ë "^H~܉^֭7O Yy\>Irɒ%3x`-NUAIÆ X_,u|o{bNa{TY/מ>pAn73>5\ :6O? E9)Ht塒n$ 7WK׮Ze+%#>%}.u;R?˶lbR_ 2`OvY%&pYRb.qiѓQ'3)~k7;;;Aĵ+I\!/k7j5#MiQ)Y{䶩FM7Tr?PYRjo~֗!q衇 `rssE ^f6wڝM\sKv]z+WJ=lnݺjiƌfs=׼6=`Gt&Pi͡V%r8e ʷyӧO7o5߶m{1*ɫs4ҵ ޵ߪ$&]5<þNaw]ʐ(`[vS&X0vckβ#p~ew(5=}G0HPM]^5ʲ;E[JuQ:}!ϲX"3w~yҌMlmޓ~O?}>[رcn{LɁ(Dghx?O }UF d+B:ח &u?aG1''G{|2X%o<#:Jp)=\y|kp'Aeذa2j(Sr7ƍM@mw!_~tAf}kMk.&O#8K[`:_ t]~aeҰaC2e|1~7 (UTλhx3\%@IepId㏛?7o]鱤͛doԨX»<<̼@W tR/PN?^xA]ڴi# .4^ x/_P%KȞ={˿{zefJU lʕ+KJLyɮ]L0zjݻ\nm,E_z%y*AСCeٲe#.߿iQd&'/#u> ;SNP5sϕ1cȿ/S?_:,3I&fΟgy&;v49CCE_̿F'`,@WYvrEMJH֮]+-[jVf=b[^zl˖-UI)MFw U]6 p iݺuZh@S2 Y'SiGg ~rV)-  b&cHvu։(k۶mȢF'Ƭ@ 9R@U[`@/@SH@@ VXȏ  r)  PbG@ 9R@UtSbvkIENDB`rstatix/tools/README-comaprison-against-reference-group-3.png0000644000176200001440000002572513331755343023617 0ustar liggesusersPNG  IHDR ۤQ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o'IDATxKr)JAQԀb!`cbPT% #61bX(U@){]^޻;;0s9y|sz@#s9& ` <}Z{=Y|_^,Y"&M=zlݺU6m$7'Ho*ZEВGʴiwߕˏ?(={H>}dڵү_?ٻwc9rĊ#HMXőh [ tWW_--Z|=#%KnݺI…nB sгgO5ט5j$7'ɓ'o7bu]']v͛73pB曥I&̴XMXőX+вѯqM3}rW۷oAeΝYAsqmn6i4kLJ.-*ҴiS49@"~K(MO2i#~ o߾iZ\d)?nja?_뫯mܸQ6l Z2w˗7f⋦Pn]d;8zh{b& L 'LB&D3' (f2 @ $;vʕ+[Dᤉ`@7r/ 1 |QV;vL(< ""` *H"E,̟?T\9̏@Y@kw9rD_`Ĉ '@X*g@rIի4iDnV5 ,x; @ Fܹde'åyӳiɓ'k 4R7=zt| d@o߈Hn;v+VL.Kʕ+믗%JvˬYR+/5jԐRJIǎeaa8 v g;#7tSRfVZy'/)SFt۸m7|#_~w'0ܡm-ZyPzn E h/?Pn*|4hDsWJŊ^3b y8w}Sѩ[̜9SZnmrG M@dnS/|mB 5"Wk7js~i&N:uf͚lm$ v @>_.ޡCUVL}Ν+6m~Kٲeӱ;vrA .x͋Yb/WzV^t(cڵ[nLod(onep =q @"E߮];ٲe̞=;ůjo dɒҬY`N{1 5 թSLD-_K;9:SGm޼ W^ j)Wk B[ot4 d@;5:}6L˻zfIyuיu鳓=̙3Gt@SA gV۾}TV-ԋc@jC 1O.LUTmoܸi=5zoәḻ́6 foذAVZ% 9^u)]tE1 d[tO?m\ڷo՗vX~]XrǏ wՉZ8袦MJ>}dʔ)RhQ޽W7AA >b-۶m]A#ϯ:Kx7ͭ: 8 &G3HTƍW?CdC@8l v.\Xt6o`f *m&],f8M̛7OveFz@8tR! Yg|8ߙӬz-&MfMի_? ĩr 2G-\]P.6PͯJ9Z1G:ͻ.ZOS99 .=Cg.!E/B-:GC;#֪"jǻƌ#}Qȶ5kHϞ=/1#/Gƍüĉa~z2yd3c<¨Q"_˗KFG^Qf`{'MSs9M4{Ƞ #/3MU"oaPQteHϘNTϷA36r(}tP}x-[ 2W_} 80{BH1m u˖-x 3foW'x 9ki:w)GZg:U$tE}ڏVHoWiӦEF!y_GГ# ?n胬/@Ѧ]k)VxrѰKgT\"pV:*a>Ȱ6Y9 )?}VZQiʎXv+Xka5M+j[ ?_8r4_]WZyeM"kbi6bX?`G=xuo7k}cqDYjUgB M 9( *ȣ>; ѣG;iD>4*L@ @< @  iTd@<xh@@Ҩ0  x ", 4"R8uꔜ8q"K>/\ H,K=,?t_jԨ!J;ݻ@9'8~K>K  SLcʴidÆ ҢE 6` O@9"`Lŋn?˹e+^Sxwnݺ2sLiݺu=x@@l?^*V(K.jժEY/Q";RZliGRKy殳!Uo(Wݻ7 +QDÆ e۶mf@@ʗ//ڔBwھ}\wuaƧL2ҹsT%t^kj8@'ڄpa={t4ޟJ+W{.&82-[!#K `MqM@]t4mT#k׮[JݥI&ҩS'\ @qph&L`kժ%\pi 7n q/7@M M@ϷL'͛7OveFz@8@H, ٲ8@HG6%' @PCCMV!@Bip @C6Y J1 @cdrJ@1"~[.C"IK.NpINݺu4͚5sI}.*,L H@"i @\TX @ D$.@."0@" I\\DpQaa* D@I T@$$&qApEIH$M " S!$H@EBH$ 4  .*,L H@"i @\TX+V[tKC8+>+"\|5 G8@#$-1Cp4ŃqGH[b h $< @GAH yl D0'8q1/y捙TyeƎ+=㍊ͩSرcR`Aɗ/eO^G1`B/p2Q믿JѢESfC*޹s(P fi%1skꪫdҩS4ͥKt4nX&M>}ȑ#%wRRTN*" ^*Or@ h6*}98!aeҥK[n1;ժU[ 2eԮ];OqظqTPze*+W믿^J(!EˬYK<ֱ;w6m_~,[LzeƗ?1֡zo.]TZ5윓ЗeIXM`Ϟ=ҪU+9䥗^2eȳ>+m۶oF.lǕu"y|IwѠq|{ y& J3M6:Ck.YpaL/_.]vNgLLI0tPݻ|'IH#_:@ 7jSB_~TX1̺'SW^-͚53?*H@lz,'1y p饗w;w\ٴi\ya^=AZҥKYWdIscǎ07otMOAP1К.ʖ-=Wvé .> s@q' W@\]|,eĿ\ٔGڄGwZӥ9Q_?4I;EWv/!M6ZuŊfbq 0@b$ D>;ْܹ70',/6/Ly7͢{/t1N/ef]k@?ªUaÆfgyFg+ۃqp̪Gd$мys>,خ]W_//9 _m҄5kRG ^}]v/>cǎL<HjIW"w#p a1ʢEDlө~ߙ Wu?rHٳ̘14'Łuu G9Ik VH $ IT';HiM**TfNtP?+VVM_&?/_>yWeرf3[ f 裏_0lڴ)ҋs$5#%B76}pG mL :|o[|//k oi2Mwq^dqƾ7>PPnيs=n9|0?G CB|͠栍[lH%K{)bF:u*fܸqfOW,k۶IZ"r7a}Ba6o,˗ ^ _&Pwr-[ xs@!8-, T@R9 @[6X@ %r@%8l @R IKKpn` @!8ܲ2@I%_1@1IENDB`rstatix/tools/README-unpaired-two-sample-t-test-1.png0000644000176200001440000003407514011711120022017 0ustar liggesusersPNG  IHDRP:iCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iPth3IDATx xE܄# ܂A$(n(牢ת(T@!"YQc% wC&I !rN۽s%tyW Iv  6`  _Cp @  m  w@<$n(  !p@@ @@=@@xH!8 P|@@CP6; z@O')ķJvP>z%4WӃ=g3tx6]̘bz rŰϰaB4!I.IR:*qYI9 {ؕc*VSQP>;`? Hk֏ãC?dUQtݡ'!-9o.V8bzo o8ĕH#h4S1犨}HBLʼny"[("8ԾfZ^=X*/&KqG&z-rv@6na4Tz (==j֬I=zIT^=U8 h=JbK!uUOjݺCҜtM\yy9ٳԩC:ur$Rlcp, 5i҄&MDwq5jԈ̙Scƌ/x=DlذzIŹ{ij3~t=аaècǎ7ߨTNZ9p?6m*8 B۷os=G>mG׫W/:?~}]ɓŋ} UVwM 6믿.\3{lZhرKvZz>{+,_bb"?5kؤΦu/g͚EɴsN:t;vOğ9c%c6@4@^zGX(Y͛G,v'O wC=jS켼<ڵkpꫯD~._L="ɢB=|pZ|9mܸxQF_Eq.惹%4.#x|\buEk?q?,OHU!IW&JϧQP{~PRٕZJ^JxE5jԠcҒ%KȺzԶm[y5^}UњSĉe9s.i&!-e-gyxlU"O?UW]%n#F#<"ZتU+JIIyqbíyOќ4bwjС~ (/<(_؝mNӖw,p„ 4`]dǽ^6Mp;yFJ_o,=z4 8PtU5'T{(22vޭ (t'O>D d+k:ԠN{+ɝYs6\ui@naxgXXC4 NƍֹҺw-þ@BOLQ9;wL+W$nUrzȞ{!ui<\N:Iɓܕ~wNn nuIuix… ײeK+>9ӧW+4$jEU-J,~{ƕ[gqºvJgϞ6IeqD+Zcf=)B?s!i⏋lfԗ.]*D'cȊ%@.aR'x&*]vի ccy7lq9 -**_<%55gծ]S =xXr͟6Cq#?Pke1]`=;v`q:u*DZyO?<乥/PVVͰmd!I1\>H,oEK5L"8<z6M7eC]7UMAx6""BO?v<>WXnvZ~=͟?ߡ,IIIbY}>Cb҅[[lXC۶mܙ3Y'^_%W?wg滚TMw7 b^3ܵea9rHJݺury2t)RK}[K[ɭs'x\r't]w[ޅQ|܂൝<߰^{M$^~_M,^ 'Wx g-Xc10OB ' 4M8Q mWSڬ o ʩFX0U6Wm8ME74o sg[,* s6a,EpPp+aA!TVi}x!n]R6m8y z"##Ns/SRRsδfJHHp  ~inذX:ؼ~z #FqqqqԱcGZz@t̙C͛7w?##5j$D:Ӟ9s:~+wLB:t &g#{ ǎ?XMZVVo𩀮].]Jklْok<-Z 2dhMnٲE\/ RY W]uJm۶#u&rɴ|r&POIII6@Ig-Pw\xdQmѢ H\wBZ@o9{u֔FgϞ%wY~_Ve(/e^P.&?A,$kPk nBR&?A,$kPk nBR&?A@ׯraD&^{-z뭕n,Jz{vݺub1cƈs矴rJ:w8Gti{׎=x18@ |uPN\}I>({JwynF:|0}Wbǯ[y3- Ƈ^wu"`޽Ă^?uT%f" dWF I>0Iz%HkI>E̔BCC͛7;SUz(dٲe&M^SzzKU*--aÆ n65~ƍRddyÙo'g5LU\?^Gn=J,z\U_{5ꫯY:QeggKrJRJ^ jRs U`m"pah›-Nj׮MYl”s=..N_r-GpWA",񩜐-hǖxXf͚"\܌_!5MUrN:b O;Wҳٳ~i(66|Iz饗Dv,rOFu4ٓ'OoY|7|C''X `urKO$YukW\\Luֵ~Wҗӑ#GO>`8k,zg)""X~L?[I#zӦMiܸqocTaa!M6Sbb"ڤEl9KvdW/ &'s?~yV߇2nIiӦJ@J]xB@@@PO @@5 @@=@@xH!8 P|@@CP6; zCp @  m  w@<$n(  !HaYu}$ISP2Z-tBujQX(뺒LV8Fڪ4seF.Ga|Ҩkв{V3j|a 5NsQƚ5tNf z"հ61}i#R|#NpRn|]@ ^AMju ^$"\d  `lPc/""kc~a @@Y @HE@ Ʈ_X EP/E &5v:/nL^O _RV*Gҥ3r滁QP1Fuey!VKEžΪb}ٵ݇BT}-ETtb1s=iU犨?i#зC=}\F4M10*$WMke5t51u4Y)2N_TjXME0Gd8CO@^7Uh  PT  h Vc(/n@@uS(@pkٳ4sLڽ{7]t$I~5͚5j/[zw͛HvЁlԭ[  `d. hAA[RRR;ԄbGk/Ԛ<@5 T^V+B֭[6M8p9f͚ԫW/ZvՓ iڴiKקD͵I 7ol2|96lXG}DyBPwڥ;}tZb%''Sjj*effa1Wx@@\sx #nEvE A@\n7x-Z$>۶m+ }vjLKK.\nI Կ{6mJ999ċ*ϴ@Ee ܹix GB9QFщ'h˖-C xq7; @Ne!wy ;}4mܸ5Z㵞,cƌ;vҥKB9ydJE'N-դ$@@@\w'L@F ,y扵@@R嵗SNuC- .BB@'PjՊF@BeL^y:2& CAK_x:@p %/<@ @@Pt?  \y(: @@O`v #Js7yyd=MC@X+(S@(/›a1F$1/SFq%˥1șK5l$A&O(*PzL@@+PHNoMMEȧjY#iⰶ^~v.3jԼ(;gsE*.{Yx@Z @ ^GԨ5 @NuxQ @@Z @ ^GԨ5 @NuxQ @@Z @ ^GԨ5 @NuxQ @@Z @ ^GԨ5 @NuxQ @@Z @ ^GԨ5 @NuxQ @@Z @ ^GԨ5 @N{1`tSTvs,e>K A@H _;AeqN~r ͒$*G@4"lK y›t>? zX7X,t7Sƍi…6,,,3fЊ+('' Bϧ ؤ@hX'B@PBL"?L}SDӧO♜LIC%n^k.7negg֧=t;wxB;w5kPBB-y tԢE ڽ{7k׋1#FqqqqԱcGZz[/"5iҤB322QFK͛73gTx"@@|.'_PP@w {OM[ZZoVePHH;,RlQo & hfhӦM6QTTMx׮]iڵjXdd@M'VӦMO^'jxm۶A_ N@y* E N׎Zv@p  J @@.A@UPWI!%J*);P; W @@]%t  `Gj  *@@@@\%uҁ\ B:#KpURH v v@p  J @@T$"@A@ZXXHӦMX_>%&&Rnn>  _ӧӊ+(99RSS)33Jh{  ';3==Ν+4>>^JIIΝ;Ӛ5k(!!AOP] tN#FP%..:vHWVНfddPFZi޼99s:~+ hAA8 ۶m ?%%%@F@wc!!!AAAd/yVg@@@ot) $^P z%.^a\  `MjM~p XH  t=j]PW_(dʧ+" y""$a驧r.}E^TT$YYnSt錗 UMס9C hEF"ܑgۿc,BH{M̙3LbD}n@}0 Qjv>G/^=z@( ݻś~ܕ>h"@$- 40 "M{#uW@ϧx@LJR]wKݹjW+#8^;O=zM[oE_=իWOlV^V-}\#uS:~8uڕ!{ @:d%W?Zɍe˖9͖=7gy}Yw+!|rztTAG%-NI;tR~yB─nA pmI-[p OJRqqKNN:;wH-*b465- >cu]bwEG:@ {t 駟l,uQrJ۷/=#6i&;8$FA@ %_U|TE_~ |#Fƍɴ7nSNtwI\Nx#şx ,os+uT %;|(ne;c |c'5tWӵ^+v>>h.yFI9+ lg!Li&QHySLq"##_>h.>}29r²eff޽{WEkMzjV n>o:tH Dp)RKo}7k׎4h@۷o7|&-^j׮m $^ċ>|p ݻӮ].cx[BBA !:Pn]nٳ?p~Æ LYuVzESq'O:( <<[Աu}u8}?iȑQvFqeB@@ޕ_g%-ޓ|T /Hf҃>cO3IޔZ\/YDMOg.]Hqӂˋ%y\8p% DSJKdE'$ySųW_*W$(x\GrC%Cz'%y$Wx"l `7&gr)Gp"we64&&Y"8xF7eow/&T{ BjP @@U{ BjP @@U{ BjP @@U{ BjP @@U ..C;RQwK.U3fO?M@K۷'p'ӕ4"32-+|}QRai4on84i6x7vDW]~~8DEE-ćqk+iDB2%tMYmtϞ=ɑ|ƻ nn&REx򍼉0y+Ε4JZ||u/X@l+s*? yS\1UVɚ4iBj+itUGGGK9>GbBHTs\OF;Ng}+iy@@S׆TaƎK_5 =C|'yg}a#d/t?== 1,|f\Iŧ@@Wmq5(""BLGS'YLh6>%h?,IIIt1sCQ||h*]Iŧ nP+?yy({d9>|Syy(ܪNq wI/űr_[nEc-I$I^Ƥp%S&iecǎQ6m(8ر3UPP -Zp0Y9@U[Nt#ur,G|~SE^U9WTG߶lE  {+2;'NwС¢›a PST3tAy #AA @ f  PoPE  5E5Ho7C&IENDB`rstatix/tools/README-comaprison-against-reference-group-1.png0000644000176200001440000002550214011711124023567 0ustar liggesusersPNG  IHDRP +iCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iP &IDATx xMg e,A4b7&:MiQ(3iu<]l}iǠUKLj0}&BE֠2Ȍ}Dk?w8wIr==>u}ϻ~ dGp  >9"P<  ' @ x@@OP?!@@ ,%t}?Q FRZzu9so@$`)_ ~aC$ )? ^@UϖE4 *[nPڶmSE_NǏY&%%%Q^^S &7nܠfJJ Sjj*eddЉ'k׮ ؗ@y>n8\[9994g!]t-[F񔕕E={t0!=-[~VlPKG@&{@O6"a/\|ڱcWTT\@&35kF Տ'\]DD8p )8ЋyfZt)8gq\rbf~HH^wKHH ~w8k  ͛ٯ_?ѓ'Oz:tH:_Pݺu7+WPj՜yco?@4&`9s.\(>z1ZnM}EEEѥK3Ղ;t蠎k0nק:}x'ŋӊ+uΝo߾]N:%%&&*~  ͛;՗55nܘ4i"y\rѴzj 'k׎E f [ԗ.YXpYTիGݣ4r/i!,hE@ "O[96lH;w/{Z)= * @@L oVX( @@4p  > AA@@MkX  jP5 \ >BPPi@| TM  mFSNu+{'+o}uqi @@mX5k={Vtj*ڿY;FΝ3g c+WG=z+5Z01-*4I#<}4͞=[G >\@\n-Fy5|2ta;v,7Ұ&1* Ea8>kȑTreر#=Sw۶munݨ|8w5hȐ!"RJwqi^9 mWϟ' lKi4i$ S-ZDmڴWťH &"Kj`'@@@kjMX&Lܴ|^\\K|.'? r)))&.!:t/_WP7rŊE&.!ܿ_CW~ jɂXmhD@${@ XܹsZn, H9!M"1-[N`)o  E `KP[6;*  @PD $e   E `KP[6;*  @PD $e   E `KP[6;*  @PD $e  `z{i/tǏӛoI 6(8pr&|8)&&j֬IIII縍o0tРAcFϧ={P=`Aj|_jj*eddЉ'k׮ި&0mڴmFwVZd:rqМ9svEYlSVV`d@G@h݉_E8(IXXΦ *P^oFL  `4]4$$4i"|5ZjM6C7"M.\P{Z'ܹ)7N۷(ΟUTILqh+ (m5Cy>y^we<+xh߾=5mԧ8Q^dު{=ھ}yWZUj;^UwZǻcx1c邀 <† ޛm p#"90^ܹs..bY[o՛8+-ۛ3r{0&N^z%a?@ Q}Jo聚P$ fCA@jV@@@Ph(81A@"|A@CArq%Wxm"u @ٗ}GmcpP##OИqFs1G_|uݐ@  VhE@PC#S+ZQC@@ LA@jVD@ !5;2Zu0@  : 4p@_k3S>͈=ݺuzI8^s7oRxx8uڕ7o)lBʕ[n\ɓ'ʕ+J t#0`4hnСC0L"Y-Q @@u@CjDM@t&8L"-yT~}ǤD@ &lcҼyĒk׮Q޽i&,)&")S޽{IDoԩb]'~DϱtjyfZp! qW ,RJaڠ `]> ;#mӦMW[~g?nZD'uѢEB@srrhΜ9N]tax5>>xgϞ7B'pB ٲeK;w"rOa+;;[t^U933P|PGa߿O'O$[.&GX7 (nǎ"1co63vKup^zq%7RJ`\@ x=pB5j㏉.;u$Z7nЫ*& "r)8runga-`g@@@/>@Yhذaߋ26jԈXڷo/-n{xgcǎ*V(q|^긇[XXV?6X  zpWbrpq"$ (`۹sg1^J| Ϫ?!|HyթS^~ex5K(|ǽFi:mѢx&-ɱq >XԩS_xQQQ@,TDy:8 e,%enذaSAt3Z@Yx!/ArW:*8W^qxy^l>}~۷O׀|R# 5OLLYl_ė{R۶mEXyx@Ϲr y4mڴnD}e3}&*E/m310I&IE6K>Wx~wb3~vupD-jժ`(Q]e%֘@  H@ lvT@  4@lIjfGAAHP~:]V'~GQ7ӧK)e`ogyFYؔXLOO^zPݞ6mvOǏ/!n@@KyOTJRB[vݺuuq[ja7P/[<<<6ʯp $R@<zO(㋅%R&E0-**}СCKx8&&j֬IIII  `$CΝ;4|pڰaǺ/JMM :quڕ }~Ϟ=4p@:wթSǭ9994g!]t-[F񔕕E={t0=д4Wݻ6mVlPKG@&{t„ C[#GPddQuhp  `($dbȕJDDCw2߼{vp!VrQhq}%1VHgrs6(RG;??-+WcӮ];.=D780zQQQ=KM{<١CC q ,+1B- hΝo߾SN6 lcLdԨQqF LL stIx"q_ד-  `qPd &M59,М9svE0[lSVVӌQ&0]4;;*T@zR#..bcc)33SMt=#GPddQ5>#… j/>ݸqC+ :0]@L=""M@wA|zs]h  0]\r!!!TXXQF?Y۷oSŊ|yK3͛73RR%bpDžS[Z ,%U2y/KZz5׿* 4ӊ+L[`,ؘ1c>^㓊2̀B#›x;+o_eCl.p{~F͚5 \HN>MΝ'|64 ijT@  h>gE>KyyyE@ \|w. -Xj׮M}]vOI$Zxaks?~*;|pѓ壝W\1ʓx}\ެ>A@k<$գG1_ ,C@ h >JwmlذAQ,n=O@9?_|237EߚC?8Ezem| 4]%q#^puT:t$߬[6N+(d߲1`/?/m۶q^8s`^+!X瑟IG-!5n"GHto8F< `q3@h7  `q3@y 99h7  `qs c,4@HTR!x&  P*h֯_/Es=f݊_Sbb/~gpleРAbG˷~-8 , }N#%%޽?Kj߶ښ!>Ք-/\PoT>6?6xTϟOIIIJQFQvv6I,q?ԦM{Ć\RSS%\؛5v" {lJxB(F}%d>ad~5r_>"E~'ɽN'0>=U%ҐZ @?lυRdYV0`wW}~7‹mΘ1ʦԩC/GyDߺu+uQ9ÄsP7t|jq)W߼y~[bF{ٳgONOT*N?mժW|6})G1dƪngJi!zN}Mn͚5/33/_N'N$רRJɓ';Vٜ g c=& VO.^~}!;MHH Ve_|222eu>!5..N@TB片 x $H8*rQ PK|$)EIQ(NH$4w\IJi<.="WX>EL@ZvGE4G)gQIeʔ)Nʳ|TjUql:.'GJrR<)˚$yX@ĕ{Ҹq+aXWzqr3<#_R`o!AK@KO&>_=9DŽ,į,"yYt8%$YD=%?ڴQmg"<ЊT+H@ obT@@+P"]ZQA@@"tA,Oj&FA"Պ,<ioIENDB`rstatix/tools/README-pairwise-comparisons-1.png0000644000176200001440000003157514011711123021067 0ustar liggesusersPNG  IHDRP +iCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*iP .IDATxEڅ?rArt%#q"I@UQQDQYVEPAQt (\YDvQ " a Qd$_fҽ3s>G9 ӧPe̙2~x={رCZl2oQ+oIvΝ^xAg.x^Tػw̙S^}Uɒ%dΜYN* k9k%_|RNT͛u?N3SL;׿ՊF2n8/̟a M@/9re2biѢ4nX>e޼yAANeڴi_J*UL_=p_ĉ2|wqGg} {x~iҤsNALjB ҰaC} =ݻ˧~֮];^zDN .`-NPNSRR$k֬Ҷm[ʕ+˜9s,?@tQl"7|s,dȐA?33gtW=+\r^֠Atk g~~7!qeٳlݺUW.i(._,Wo pZժURJI߾}=C d<=!O<Ç%JEm6ݽ:=x𠯗>|X.\h#X(RHItM}ȑᆱ]C]3<#ڵ|>iР :T&N]vߋקѵkZƎ+h5Jкگ_?X2}tݻӟ[}\sMجaE ;њ5k/ǎ3/c=&ٲe 2'ɣø+\Fcl7 ٸEQŸ+%y W\i!$ 0YڴiSY|nhuň$1M6V'~PayS/,~9}zY^׺/&M7#\޽(fx&/[,Б@B5k~ΑI,aZl a$@$4 ę kP@]S, (Ngz$@!ГĤ$(]"1PNI"2d#ۗtZXZcKͲ Jj+NFF$%P/6J$`+ 8uŶܾ}ja'Vl`@61a^{-dV!C-8|-Ň LϤ&@Ms_a'[ǬTޑ@>u$,za5֭-~w=P{uYnvm;bWLOs)%UM-*+I2@m]/}/-yMLw富jJ}Z׀K}W:^eJC gNeP دS Bt%̛[.aLba5sUB6ح`ܸq[amaXl7w<v@/}ihZ;Na3_ft {2K2ydQ6ZжiF/߳g(~ija a'dO?TۓEW.={ۿhBqųGڏG WIYHv U={ ^۷DNٿaZzZ8!؊C=OoPk7PX ,.cֶm]+jt!|Z՟ˀtWI؉-Qts%f&E֟&3={ܥKs;hbv,KX%@B *:tK>!ׁn:Cu^jAC rJ]_]ǡAcƌaһ׺*6vX;E(6~@\XnZ/VX2޽J qڵK/iJkM:ڮL bV~qC&^qlٲɗ_~9dH 5"j㊇( \jlO (9dH y+>?,%$ h2Z YB ,%p? hױZcH5k[`>&pUD:H0dvdɮ-ڂ xԋ2 Bj FFB$EP/:L$`  -  P@X,3 -(`d$$@^$@b$@ڂ xԋ2 Bj FFB$EP/:L$`  -  P@X,3 -(`d$$@^$]9 Ð 2eiy0ŋe…tҭ[7ɝ;w$V^dϱhѢҡC{'˂헯Ɲ?^ϟ/+WZjmf='NK.E z@9on<Ƶ^k/qǎɓ'+V4g~DŽ:ɞ=1|P{)S&cٲe_~Il'MرnƹsBEcA_|E7|hCklٲs=x @$/"e S8p@nTNɚ5/FuDRdΜ9&Я_?[@|7ҠA`Æ zq٢͢ҷo_9}LG}D XO/{/MtIM|`O(ݻwyΜ9ԗ!phe>CVR!wy]#Gɓ=n>U+VLuA nGbU-i-2"7h-|/^\/^T~|AujO{ǿ4mTg◖Cw]M?3DvAweΝzfx)z蒷nZĐ->@*U6mdFm}x㍺ڳgO= s߮[NFa $''vڥ%٬Y3C}om\s5Z< Kly?_!Zz'$L&˗Ss{ァgα\pC Xf`kK70?x`C & $>oƌׄV J++0F2d^*fJ"9rX_T|\hFvjIY~(O/+a͛[pKLZF\ pbu5h9sƼ(& tUW(Y}BYuC !{n`/D׍=(T4ƕbW rwz;oUo/7\Ho c}z~#ys7~Uc^^zufΜ)uũvSNn KVTP0;9au'&0FHg/!ݻwވ8*ݪegv1ێہk СCzV^t8 76o,lo͓PPFK 9,Sҹsg/#I1p .= E' hP@Ű$@$C$@$  h4H|e'}AHiK0nuZ5c Nja0kҩLF^ī.:`,Ro4Z+IG^% eQ;=x}wbl;u6"`s/¡ X<@Ai BDC@*$b vS#CdG"E\[>9l:ǚ) e 8GkD$2PU(C$N"9ǚ)c5}K/ Ӷ+45ϴI FԞbRdhM›$I$@Q` 4J` N@ɐիѣGE{' hzHpؤQm๴'{|H%1PGq31 76Y G P@HDdYH%@u7#p je!pQLHM(nMHQPGq31 76Y G ]xGq31pUe˖$T G2dp4]Ƌӽ|jJ88{eٲe21Mk׮5k֘*͛7˶mۤC./o޼RZ5)SLH- W6R D9r䐡CJ~(3g$W\ҼysR' q,Yfz-֐_y1b;v,I%LQ@:$Æ 5knCdFE{ңGtݛl7Kd6[nd-+V4HH+"Г'O ~'r]wyI$@xhqbHH@$b͓'4nXMFn$@$@@]xh߾}eӦMr 7HΜ9 vI:?? [ D% ˾k_\24 I\K *cطo`yƍk[/^,K}LH *Q KdΝ9sf)^^ >}ZJ,)3fWR%ٺuk\޴. M"$@8>_|u\R5kB>\ir2dHld$@&U j=GyD֭[X#ڰaC~qLҝ( P~~=%J￯5jTLFծ][ߏ4Ν+Ǐ*}L֮]d$"fGG$"P7[ Ϙ1CΧUT^uAիuZ@ׯ_/G3gJ-t0Z̛7OڴiVF$@J@>_|Q6l ^΄nx$k֬)cƌZ֔W۶m-0̙CH P3ʕE ux¢|ӊ<(\p 7|H PD3R!,᭛\S߆41q$v풉'ZA/^hHbM MEg8x1RL{ǎez?[l:LLa*Uy޽[dzϼO @ZT)9wij0ce=1TV-+ [bOQFVB\k$  7N! 'N-ܢ=.\( 4 X"O hqb-G&&~ 8A [EzٳG.]r#Eכ6mNv{3acB/8L=:pT@ˆV&J̲7kL|R޻woRڵ.!7t!਀N4I{wcP8ˆwT Ok#qNүrr(j2񂃑;vx)-U,s7 *H]hѢzH!3z,& N [ Č@.]oY14ky"$$?cKͲ Jj+NFF$%P/6J$`+ 8ٳ nglSDvMԦ`dqƲzjbd4$@vM x3U͂ Mj7QG$PT5 J$`7 D gP@=S,( .D_T*cǎꞫ e6Ѝi4=(ˤ* 6Ėp<߳gؚu) m`m/{P@SIW7x#.yVckqI@S, (gr$@!@uO]$$@: ɑ =uɒ 8L0p&G$P%KB$0  {P@S, (gr$@!@uO]$$@: ɑ =uɒ 8L0p&y\r?#A9B Z˖-' $@$4<#  (!ГHg$@$4$z @xHB2XxرC^}UtO;SΕ~)^IWW~œG$@A(AH=-Z$'N5kJƌ5 ÐZjɡCڵkbŊsfOabuΜ9# ͛`I!5*! P@"tH\G*eH"@u4!p 몔"p@\k+ϟ?fYH@ҡCիWSNɀRJR@ܹ9r$(=HH "ΝG}TΝ3geقZl)lEO 8p|!ի{~)RHPׯ_/GڢE }O>UʼyM6AЃHA dɒf)_|PSRR$k֬Ҷm[Z*Ur2gˏ$@$o@ "EM۶m… k TD 9x𠯗\pol]|?<4L2*X \!Ç[Qb)Vn˖-:EБ 'pZxqYxqPay:4hu 5bŊiX'egƍc#1svK/ \!,C5o\~ҥV]vƍiӦsaH <jժi|eΝ"{޽Aҵk%b p@ (=uTɒ%+WNx`([F=LH "q]bEL.]Zp aFӑ @t$@$ / %@c 4 X J$@(4xL$@QFAIHԗIH (`1( Hu ${Ά#/: (aΝ[^rfTf%@ CK.mFnҾ}{$@$Ntm$@$@3@$@$@M'8F$@P>$@$Ntm$@$@3@$@$@M'8F$@ZHoY$@$p  2eJ5 JtT&مcJ%coX_tIɖ-d߹?UgΜH7e .H\\SCI֬YS-4R&˗KFdƌrw$a 7xMaÆ駟&n&0g#Gdak!ja$@$pe$@ .|VSdƍRreɟ?29n:+&g4׻wKz4gj?SH$ǹ4g~c+%pe{#L(qkJnt|2tа9ϤJ*A𧋜;D4X94CnڴIڵk'y䑜9sJ:udi㖋\`:.]HeѢE?c=s2hРTsM /";v#GDvCIѣҪU+KBdԨQZPW\)kNtx km={J_6g_7l<<ѣ ۸_Dx'F`ĉo9q℡&'xsq 믥M6& ZEUV#Tk֬2paD}eΜ97jxnj#uֵ͝;n;vs45WZK,8omݺUTKTrȡǚЭի_Oռbb(w3sN=Dw'8THRΗ/>͓C~#{f͚f8~NhѢ_[>}ZqTӖ89N"šv`$K Ν #,G-ٗ^zIO>zSo߮'Ga.V~)cX ʕ-[V^+^687kH ?.[-[ȼyVZ̎ci + A\07o^_zF={SOn45~gu# LiKC*'M'+~ XI7nt קOIII:vX5Q7o,7t>1b+x;^g)DˆSPmBYJ eP֪`fahj2eEi]W[wZO6{Pm` ۿIYzXǾPk׮֩:kjݺu3t"ol曂+L)RDL"7pxbiҤs a2f(ϤO ?JQmK5) gΜ_?5+ &Hvw㟈s*.ժw?SD6XweFOHd,:v/͕+WОR t-}p\|y>3퇽M&/o߾={ve+bFm}œ  g \zum7o:YB-7i&:V?+z{WfϞձCj*Ut 4gΜb8p@0\I*:Nt$TTP'j7HCeP&켡3fZ-P3ƍ7ިAՖ(FժU 5iSQ˚Bƃt>JRpaQ"* yԣb \=?W/c  Y@ XƊ,%p= 뫘$ h2^ Y@ XƊ,%p= 뫘$ h2^ ?Z.IENDB`rstatix/tools/README-unnamed-chunk-9-1.png0000644000176200001440000010003213516376250017616 0ustar liggesusersPNG  IHDRǵ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx]|TEBB =ޕ^ ]D@"(Վ `Q((*{'t{v6\+ݽ8{[f;3;!L#0vݵ0# `/#0v ;xn6#0,`FN``fF#),ٌ#w`;Ev<7`0#`ӎf3#F`vlF`X;0"N;0 ~FSXisF`#0v ;xn6#0,`FN``fF#),ٌ#w`;Ev<7`0#`ӎf3#F`vlF`X;0"N;0 ~FSXisF`#0v ;xn6#0,`FN``fF#),ٌ#w`;Ev<7`0#`ӎf3#F`vlF`X;0"N;0 ~FSXisF`#0v ;xn6#0,`FN``fF#),mvTToߎ+W ##윞`4 vX3g*W֭[B h߾=]56F0Ffoɏ9Ç믿Fxx86n܈W^z2 |aQ۲{nlݺ6l]G&MP|y_G06 ,KZjI1k֬lՓph޼9h@&F=Xd{}fzj(Q˗/ǭ[۸qcݻ#,l,uh"DDDH-[^zI͛7#%%ʕoF1Xdc/^W_Q< VZ%}ɒ%h֬._yĈw/^ݻcƌJ0Juƴi/"==SLӧA3]v!((H{5~Jhر8~8&LQF6m"\,~x.Ȑ!ݻ,;!!ݺu{m6k%,Bٲe a :kV kעCYȂ0[kNg϶m@3|ɓ'q%xxxdhw0m cbDm\Ν+ úJH;w.?xLF6x8ճMklDBf97{a`lrP".`4 Mtv !@FϣZja9aE`p"##_x-aG`$|`@wG?s+Fx<nN>44111c|}}2*#@e(>66'N@*U(je=z\1#yƯAⳗX01"F`|9pZ\MBsV ɚWz2l;r#cfB uo6!6!E| gh:ZD%I ft#1X1ykNyݰkn'#i[e]}`l ^Vug0]@ԹWoF&``T0G/')8 )h%O;pZ s#,Bւ'ʼn߾VUBR}N`Q`TG_wˎH5_Cxt"~{F6``'DHY3MwEdlfX k3@?cHh_=vFs=ve.RˬkKH ן}$Я s;L#mX?Cڵ([ހ;Q!&F6̌1cϺ`(bֈl_GZ%0zhqǞ~c„ Sʕ+?tOBBB0wܬ[?#U///ԯ_SLR޽=ߏfD Eѷo_:t(+?hբ%-=N0F@ɓ1d9]wt;wUVaȑӧ~w NoNN2b54O 6ٳgqEj ]t/bJ XMϟwK~URrIywޘ:u*N8!g4ر#BC˔)ŋKqB89jW8;:jbwrAPaÆ RCjUqeŊRꫯJ5իW *36 \T=D4&&1cp wŞ={3d+#***5+T!n cV)MGӷ~+0~~~}wߡXb *Xҝ 4w7oޜt4Eǧ6m_|cN]=۷gAn*=n!v'ނGlH%KdcE%ˆ \z5((;#H;VsI.oИ9sZʕ+.L=j(cDNIMC"y=}''',ZHzf߱cGodX~T e@!BTDShWͥH3h 8ax%}?}k6hٲ%Ȇpll`ƍXtI#iæTQl 4W4ǗN'TC-Zs(h_Ik%ܤ!IoODw}@~'ӆD'V KبK헌c$#[3mj𙚚*sΡ a@:~"x?RJ -xzj%^\fG@}L3@C9=}r%bOekTĎ[:]KD {;Yy=ZGuI>}Kw^[N_Ufb +ظRD5QjZ6 =h֯M6I,I.l2؏?O?ŋѫW/Xl.;3R'G3˞.x9MpW'Vyq "=[@;iO{r !@[L@aE@!O_n&[[9;A(]8No䁄 ,$0}Ei{B@!fD+6Б wW'|F|7A7/H(8Nt#`o($=M^;.!^ei߱PEePt@zx>rЎW͓_kfbsUZ:?­18:d{2ӥ+' oFө9DWorM8uu}X3:7 Ƹ뀠]Np#ʁ^kI&@; +Ž妊I w5AzyM=g]LF**]vMHtRT֜+reb xXI' 􄧂qi_7~ i6O8/p7#`(Ľ߸Fv1և m*Q|?/Wӯ!v,V-njQ#_sV߮]6ߏf͚e{=#``ϨP Z{O;w,ՓL5jKߌ# ``' }*?q{6] K8! |<]A V 14YBj3(?$$DӇAx {6fN' Z MC'40Ga)p8?L#(xSHt$7C5\0#0FW,e66_ݿqqq6}&˴L316Ћ^A}RYM4vvc^c`` ZVJKvzڹڮ];+qL+WD LV(HLLB\%#<lPS.`@W6Mdn$ܕ8w.n݋BL\"E(l?owOP%8J-;cv aXs7":'%"%50Ga3qsua=/O7} wo *11wF8q&& I;u~3҅̈́z)5u3ޏ \廱1="D٢^(/"~6( h|0u֊f4B-6>Iftr:L}758 Xn4IXpᤔ4eT]Y b-~-+8ZhE6;J,% K0x9yy)Q4,lLc62. SW?O%Ŧ~vpJZ:ReeW ~zӿFuiUG߭v nG2$f}UӇ031[ qo^E kr51g 4;QSמ+0~  5&i_x!,߭c*Y0,7}n2H, io_b#tm.e}!|IgW[>` tLh(Ϙ2{v\mLN4,lrg>M ̃ۊ A4%2x}K)z_Be "P3p'W<,,5pz$Huv4Nߡ*ARφ!-{ Ii[F-^.¢,Zy.[TDZ1kټt;_`>ŷ9s V6+\?#:,Trw0[OREW+H4kYl>~3$~DR w+O a |L8l_ ->$6~OhuՏ-oZ LmB Bҭ?9pІ9};c TnmVFDF``7oĉ̐yF3lf: 5b72#PX``Ş6l~w9Xwfv1-9v줞. [C{,==44y*|ӆ?D!CisbvCW"W`"%x2X@ !9sL,Y7o*_ѣG=z4|}}-[ݴxǰj*,Y2+?h&W=ݱ+'$kb2y[kM0 ^ۣjԨ!'Oʇ4/X@;{6O>%%sEJгgOTX3f ZEQ=BΎ(6B3$UӤ {3j!@!d7o???lڴIx1šTRضm{nƢsrʡL20`L G|n";qAsUD-hWt)<ęK綉 :t$4i-[f kJuO%boў7UKA II[b㗳8Nx-<2F#hSOeVZe]vͻD] c3Y(Q\b]qEJpdmj\9"@cRm׮]:.]ԛΎBҙ+Ae X2B쏠 4hӦMCBB6m www+V ƍ=oذaVf3Ũe ^VޞnV 00%'cl mMm \%/m"oӦ ֯_/~>>> ! * 7 yy%xؔ?O)TpF`p PuDj ꟁ":::u*"]zcc~5CtJXQx|8蓓ڵkHDDD cj$ҝ lQ\ZV+[ku2/8#`k =F*!Sb7RʭȬ"->[Բ&<]5{^LAFV?6`Uz9w`i״T I߾yu ך{uV|пܾ}[:@{M|מ`Pze ՋŨHV!lY?_,|@69s`ٲe\2BCC $h6p]C09(kOՀvtFJCj rUmٰfr&(_RB32# HlFQj̗DGD]!ܬw^P7-̽O;xW1Hj s, I;UAŞw)E#r&!IIoԜ18 };52MjdsϟUO>ߤ:hO-[V"ٹ&<, z:R@.Z Owa_MU ?P{%{9;w#GDxx dlmXOTMaȁ,涇x[m's\YNqC 7fٻwo~).\ $OV6lZQ11"oHPWJV/G *UߪBZ1aLŇT=p PҥA823jݱo>ЉtD"T1 B#*iaJF xw1׫Fu(zO6o=hFADWmlg")S[okVj֬C|PVApdډ#`w9ˣ8v~Łm7ճ3I|7>ʕ+ѢE ƺQ!J= ^GÇ,;88XIr-<|ׯ/-Z(Ə/ύPWvDBz<%x Xz$үb,\0|i7wAEa ĉ2E>'c/Ìe6%,tSGIh G!9%ͨeh'TC;vB<[Bڼk.WXQR 11J w%Zej(lg[3X$]+}Tl ".! tc@ԁM7o[bR sIII,ĵB2pCbPrAB^2tU޼"4پ;*)eSOSF@BP J!X DFLd0u^@keF]P0d޴iS 91^dm`AU@z)^^^ Q5+AM[We ԄJղoݺ%7Z nn<ٶfA Gݴ?f3_/<`‹ ۷2F`E@C k_ͭel[g44G>]{'UDE#=%]] QEsmE10GH#"qq\\Od8ibWiƃS+HONA{q(.Μ Gw7*~{Õ>((=s? Hd *HA\.ΚK+Qypr~dCF w;.n)1/BzbQN(?. A}D<3 _3!+0Cٽ+1'Šti Ϯ~QӏPS#QZt/SH xdNp)]E|&FȎ x/é͚;"6}] JK'cRݠr@JCb2+>Rn\D‘ҸѠ5<wg+<h %X`1N~ET>F}AI&U"N ed##IHA6!VxF!Bn༅e+vřS,"^H8I#z,!ll_Mim+6r8#PiP!(4ZIdg80uĪRur9D-d17eJj pbbOOVg՜8Փ?r񷀘+FBd$'"fb*P\3 zHdEynFV叏M<|7VPK2OnW,Eŗ£TIEN@)BĀxӾ#վ.#f 4h }a 7 c`8z@Om|@WSz-ݴ 1]9Kjc,TWM#T@ Gw5x6I&/YG{pa>B!6 ׎a?Y!4wŋjOAAPWJ;}oY/Tb2AUb%bn=%$*!HdIrX~mνyDGGʼn/J,ʕ+gf[,, ^IpSdJ}w {3)V7<ݴ*a/!#55 }GÊ l}=Ĥd =:ヒSNaݺu?1a„4ʹv %JaÆXzq,]4~Gy*X~}L2%۲466ÇGpp0-={իYѷo_/^m۶Iz]SH35Rw'BK00'eH~|wABC~0OA]!P!c̙Ѿ}{ udF űc q-4$ ^'Ob3f ^z%] UVaȑӧ|)|I曘3g|Nڵ3$ """2˗ѹsg5k Λ}7S/r$0,<"o\-&*W#Zwy>0 ϜrРO3K.L2Z9Sm6䭮]f*zJԩ+'Nĸq`Ŋ{{Uew)\ d HOn8q&BR"WA>g$X~shZ} Xe9PʘhBO=zɓoo` ;|T’ N...Y6iD4ѥK|YϷo?+;vzNW^#GOg;PT@R;p(MDd `[w[ȥE܁cPyj^,1S|8b0&4_O7r'SB .qݺuQBNUwڵѨQ#]˥G=g2C7_ F:ݻ7N r#.IIR-[W\ATTZj˒M6zϝ;HT [3 =:6̸A 8rjH_J;/se&m#ۇoT4߱c"p5](d>lx…?&nܸݻw<ٓ#Mc`! ___DFF)Zí[3"CxQ5FzS&aEwԱUS8 IDM``ߤۤ')M@dݓztrNTR -|~/fJ G9kr oڴ) D% jՒ?{LK~R f͚r[uhB~gፔA ]9L~;ϋWugBH\HgMYZea.KIuf7߀&Qw@^%ҡrF3&e` p6lȵ_͛71_Άp [ʕ+̅fT#FڱIn*"U*E/O~;˘.܊Z5C^YîEz\}@dV _䦧syTL?8r^.N.b1d:y9Г3MHC7{"r(],ba&dժUCHHH6cmǎNyPhٲU.\P,.hJ7J?D4sO>'O"ZMr򄓧)YUC%->%?Tu:]DE~/ v o֭[+OE*UJzəP,g1"Zn2T?I_~4Flڴ)TIUZR}'?! md<]Z}vUc^A a*L@h8x`OEk5 Rp Wj?tPÞTDLGW\)[NݐzCzt zLRwKo:Z.G"hCUJru:b8{;ܪ72c31(X $(&n%;"&&ev3Y]Z]]ĆgD;:bQP)\>g?Rdel "Z ^~B`^>RET5^0vȋv: J_ppeb)lCh*8UGjI=}(D~HX5M|,{hjƦ3C6{zzgv`mU8Sj<˕<ͮXMFí6ltsRp)QEDg8tr/^toJo"x[UOQc"_LBЌHIA}駟Ѩ7_:.~I t $iXʹ(ݭD<+qwv$\Gwa %F êT''ólizM^c+ih7/IOq;CAhw=\ d=XX{LCZR2N;wA )cLV/1'2#` ~~~r/x9w/SH d]XX*6oU2•Z(s6f2[,3# <#nX1#'bMQ䙝XXC̓>Ֆ)YxQG[Hm 徠ӹ(, [EAm jsPlY<\ZoD^9QQQ6>Y0@Ke'PF KUJ=ϟQ mᔵqㆌuc}A1)-K!E&`Ïs3#` fHOsqbBo"96^lnKT"U+"Fex,XW#t> )21p/׮ eZsFMgq±vG8F$$Ș{LHI}G{P>:L-1l.ĝ66}_CđblߐS ab%l= wլ4wǯUCO_Y9!qXt]Z 3kj˼?')?/݂ks>kF@Xm&gV{6jFnAgEnB>Z̙WaIވB5Bo| LciW# g,~4vL>cfE@?c* N~k.ly͋o 8b|]CQ!pPo B~ c˸i-*Rp9<Ka{%nDfT҅-I{ @{}bGg/Us2&mRM&#L$$ayAFjjYO );' &aLTz'">.NbDANt̐0;eRQo4%|@O k_X'O"|փlVk؏7 S3 obv'&w|enlx3S0;_0Rj'./SӱS2gig)$ ٻ1r?.NU =o+!* +tOc菔.֠҄~א}AsKDz򕽆 eT&@{ )K?{'/Hw1y;$fݿ6jT1C M#2! ]á Ad=XX{jݯzE䏿WĆF2H~zjdVE3aX \7oOF6: pz|),?Daz07{849)CyGZSf#)<º|#_qy|9|g~[!(6: hڗ5;GO+5dvYmQ/1sgd_~xg6sHd&#+Zc%pAYkpt7Fqv~u5k7A'bJ[>ZJVo?|K7-%a&+#"m$y}akL? K n{ؙy7 iٮO5k7l{CdP &s$(9DW_}=^~>dYXXok;1?gh弝y49pRP˫7p]R86ZAG̡—?,6YsSN~yp-V X ;pTE f: 'n"O3$)N]yN)Ur~䚂l'mkkzh6 >Z%Z0uQkD:ۤ)Ew Hety|HF|Xa}Il\Gli]VHADÇO̺gM&gǒD;qj0_|\]]f͚{-[jN (.^xOI]Szj… 3{ԩ>, L2Y?رcVzcO2 !(1BLǷcv ` e4d7'cڳk1kRo>k˗ǂ Aag,rt:˕+Ƚl7 DD'$Μ9ZSɒ% gG:jw(f:?_k?#!Y,_k3i@)X="BG6`O:Mڴi///\t ݺu{^Rd 4B'*THDD<!2Ҵ-&$go|Yrag-b,9;6iY^["߾};hOȶGҐ-RYl ikU@:Jn݊[͛7Ǟ={ڵKuyv'*9*Y|ц0|O"YiýQ~k ƶd3g*W֭[~oG/$$˗/^$ Z <رc}t ";Ho d o Aƍ?MiwIAl98:£x`AɬܳLPu1^AdhJwenשg{P8$p|Ë<믿7n"W45y߻ヒ'|yl4ʲַvBČz{);>115kҥKtF{!((H OO}UFmg7V.HqwJeKZ@Պ O!-ηrb`JΞFnEӑuEA+5^]?yK˴oѢ $}_w^ȿqjy+pÆ yo,r@M9鯿y+ߗ~4$ )- }TGxPX1-tm{{!]c~NbP/ӹ]m)| v)ߡuiPmܓp*+676O+=\fi»ܷIO9) |;&W?z)RW-* {|sTŖ-[ (4vXPzPf j*)eG‚hQ`n>ݻLLNmV0EW7zW1ԅ}Q"Mp.+6*< %J[neԼ~ý0'Ր7VX!U2Y[j%}iܹsːPkTGɼӆt:JP֠bfuPF6* S_kkO* &W_V4a0ahCײeˤ&u4KtЦQ] o.]Z[^zgm毫f @! fΜ;wJ#1̡cDv l Ҁg@Vj0;aSwg}U_^!!6ThkgeK:}Drn)? ͏=;}4V\)wo B?of HKh@}[ KHn/ H B&5̣_ZgGU0>1jaύ;8/,}]0eż|IPG )'#9o]R۔-# I+}!@*/]l* uSb h;A`3 MUtj*VM*,hY2 *6|Ԩ,W JQti-6TI7ql0`[Ȉ[[xЇ(2!9$$<](6yo[ :ڕv8P Kp"Z<7g+[\/)vvX<&'yU4:n^ Zu(MB@ڮ=Um3۶TQ#zM߹p{4@6o-:& P7)i3=K/.g$h-~^3碏CQ~[ A~d0٤2"v͠P^ U\\I0rC6|䱠ʝ]_Vk0{ -mHcR4S/iӦKf`ݗ?.bWno>A=Pg'9x00Ȼ`35xTyVቩ, b2Nf6M1:>d%}^]NuzrWM{Rv4߆Rˋo":!xGEj.4%3WZ/^\\'!$ 2D4$ ai I3EgI'_gH?TGg.3Dtˤ"蜡D#CDq,-խǔ[y8|6vw8onɼʔBPF?ZT+)WDI>f;&)*(䝠ґBD,]ə8нxQUUUvzhZdJj;uc~m%.ق2.']Fo3 /djP\wxS;p52;><g wg"LE(]b⌅z//G+5;ZXl*y壍Zk׮mҲѭ [R6#?fD2+}R;.#&&cj&YeV='M.y^}B8$EǂTF` ()l:.&j[C&z0Fe?&??lضgFdx`2tH NlF榳g*[9+)..Vb}AmfzhFiD灗,YjM``5荫Lu;+m-77ccc_-O4r'Nuұ-M,,ѡ3lbg#/ joLY^@FVxr-O Xnֵ;! $"k:b @^2FX@'1# 5P2F`,@❻|בt,BûB0+U4kSIjlb.^BhG0'\||U+WfOHNy/qra%g q"%po tg$p)j)4? !|WE^=Ps'!hk|nod2MMa!OqHQ2%ꀲ9ҝxRvm'o#4,Q2: UrwFiq~qj%ТFUŮt['ރv? 7VS@}—Q>IP."DK8()T<˔=wđc84D9*ϐ+b UĶѧtrsKGB`\u>.[P1l_ wl#1% Iⓚ0Sx@6?61goD( j!O@qjN0=kNw!A0KjS(M X/n^/`C81sٸ%SxЈ !$q( Mg}L(~Ќm0m 1ȐhMֿ︄;/8rdZb`{"|!K"q )"Md$ɹ'\JKrp-]Qeݗ/wӑ @LMxfe-6a{ב*t4+Ad'+T`[:vC)Q"2ލwFx~h ԯhzVsx15/ S|/!vHx\ʅGZ}iE@OE۵+VT?ñqr>3 TLvmȿs p=6}I>/}kM*Zrlߊ:D3 aBB,fIB-cMgB.ODد_ lHg10}?CuU*6oVsF*v7Ǫ23Koo=.܊i[$C zUHU aPۚ@ݣ sq H EK)WތwtLMxR>v,]I IkH{;va!$U,͙Hb^G)7*h J|Dkg13m2}UepC*6JO{GWUOKHѱ\L2M«3[t|h&Tl%KvCH%xZ1;W+QT2pUۅP%s?Q,ɋ!:ߙ5ś5;wxXDlO;,2r[©^ԼY^@֕H<'|3_O3pTT @K!mKLz 1'dqWI9ƮÐovݏIĢ-Bab1(DCL-|X> }T J'}Lnm6#MkR8\WSL7E5xp?BCW7^Q>_z4Q(=)u :CvLș=KXX͛W4Lm&z_TC\$n )FDl"-P8Φ-j*5 -[˗!Ckb%6)Ez̙3ѥK'+?vębØV{fذa -&!NI#;kVU4@.[&))Э5.&t_boNe++ X=cz׮]ѢE \v !!! f MD BD%~\nE%dkIA?qZ`?v\.b~nZ,J,3b M)IwϓI۝SCD:%[@򕆰,6*4e~Iz:j@:v͚5YBDDS"}gKM?W_}USLJQ'nQIe+"@B PN+W@YgDzkWNZ]9"/ %(5Vf K0BMP9S(C#)Cb{?FT [aB#ɣ*9rB*$8^b%@B=p`XZlۼ)k?~D6B#1:;V ވ4*§ Ut'f0 ?.=4[ב?:t <{M~СC2Uܹp9"» uF´֙ ;mH2FZh-Ep6d2-u|'U+)YD$m0):Ddd栧?_I*o"("κy*iOTD TRYߤק;wJ@5kbҤIW+;_ʼnNuHp󪕍N#WJC)UEJQ(n:!PBŬeu·V Uu)&?v'2э7SOK.Exx\!YMV3Dqn}9S[GqLdLZCT"|?'-_^~tG ùdYYn.*ḪNjɕST d͛7gC3ݻwg]c !1}ttEїSA*%\@uMv ]Jl]{G0uFLr?* hnuVTS-zJ1jP(6lMew$10x`  e?FjbFn)rpqgVfU1mmO&TdT/7T~93Wb*UO$t2Sm)z7AۇT&|-;6 ɏ#<\QTQՋDjO5_JB d}o G1ؠ,\ HuЊ¿@Iߥj+4Nb(S ժJɧ:ZiF. o)ш._L`tΜaJB c^k`]A}ՄjYP9{{"5ZqcMC523m4 3#Dbs;\=MZ[lsۻ*4'$@؄}#(Cӣ=G=4Nmiq[##3J̸B{ED6BR}U^w/T[u{3V>f\&8oNJzjٲi'K׿Ec| t7ZvO*29}^傩xeMѽt01w4% zIR:&У/SSRm.'{Μh&q|1')8w9<{NvE0oѺ?׾3ÐQBǰ $_ο]6,\$UXڠ1R~˖R^?'1lJy?? k_|Ec*,Q2/ y᫼ϙ4WW6T& .kZ&;~!|FQ їkY5M+|T*&.0u8M~ zӖ!I#x9:%&tФG|Qy-| E \~rCCIi=?u,+=D*ncr@Ci`벏'-EEo9-\ح&y{;[߲zeꉧo "-`N ҾR:aZ ھ\Ǵǝݦݲaً7\ʢ]\"}[5Z!ʱ knrܾ! iu@.m#%>MّRbv!`440 憀5ͭ_C0 h$ "vʶ3b9oeA9TQSZOͺLy)9m #Qmi?[ bTz7߻w~uܮUԘF m9!2eMC06 \0 C6z֯rwD@Gl9rt?t q|J al:!_'V/1*|V?aGid"P}4Hc iR bX"lաjyfzE 4| *R¤yƖuG]Y'I6ɪ',]ՑC&=P#%T''0Cb7  I 4,^B̉h*DȈq? > _Pv' D: e&OX >B>$نDPwp;I}.z1ͮK唩 @=/>X>^:`"/j&W}QȓG_V tUHAkTAoPy2_`!tdЉP_tfU'(*("lJ~5a ;~W`ytsgD́_a eߤr(_A}"UsV ہߡ>!CE n@ Pu*xC]ڐyBE< ɴ}f2dݿ].Z,>e %s 7miW('{u]<"w.D:`otgAZ!z-P+LhXKRy[WCEX吽|t(N\Ry<G0IL*cʁGM A}RGW@~N~*. vB% Y8yWMyɏO*}`_  xyr,\AڔΝCc×9QGji<{o0 ^Ad 4!Eˆ^G|^J2 =iVioF0rHuQgH1,J'HC&dҺC')n._C^OlC :-}4EJR9 dIΐƓ|zbTUJGKZu qiO:цШ, E'%rVٱ'k\RztG3a( Iظ8.|(\#qu14 uR!Uy<ٴs|@tQ?^U0Ix1 SC&!:iԊIfӫI#mqKQC3Y1ah5 CE=8 0J'o4\L0)hG BCA.V苆g76ӊ=O~BK_aAkNWmS3r+_]lL:U %U0UoIV.tD}7e%c0n,hՕ.|? FO6 4C:YLjJ}YKN1U&ց~X(_XDYPTBRQ^ CR2ٗCK;X! >Z > ᖴ)_ :pURѤG<-`81hpU6NN7TrܘcUCo+x#@'v!ӑAL>B>yL:_;>-+ǎ|'pXjX;c7~Yq1xk׮͚ ,p YOs"mV̚_~ٛ1cFSiYsNM6YO=w饗fرc?62)74lV|9Ѓl8pB/{~ϴa8E,k]^XPBoa@)`vZ!`.XJr֭iӦFz՘'2)7c&Չ-JMkǎ֭['K'~gƍqTCS{\YVq1Z_ KGZRT{/3̡:]v&,i!{noС }]wk۶ow{=zr)'qo6SN^nݼ#FxSd駟u$ӽ}M&N:I劋/JGɐ/_U'0ρ8M4^{Q10^.|/:޸q㼹sw<)'N'^^rk׮]wݕ)nTBz &dbj',5>c 0_~ /g͈;:ꫯ>۾}^xW_]'ZK,|͚5KnOFhޕ3tWzg}6~׷o_7H+FݣFiZj{)3)2`B)_6f j4h#(ڷo_.T{x۶m\~ DŶi߿kx]C> a%V;{TꪫB1/Њ7U108Nç[o;sӱ{]ww5hC NZ/ķeR6Y:!=>w'OQxϟ[ Չ{/$%xll lb>\4}ٳ hR 0#*3}=Ƨ~*Ç~(e>=ǐ<ƀk(B:;Mn @+k<l1oP2蛜8ϡ 2syɂ o&cF*i:} &Cnt7.6:h `c$%: +Ib7oq>`,Zi(ʫL+E,RZWߣ=>!fڰaeGJK'N>R&LFdYgJqpr/dd'&l#ɆL3!>Mbػx=yT`*@Ǐ./nJu8S'_G)++^z%5ŐP<x\C+S sp>dkQ@ud4&FMtY+sF1:L<ƣQ`͐:Ƹ`_t7MȘiӦiC@9V i#w9`R|H!< %L&$xT)j@٨8 0@ωc$J8:BL'3|>qTƵ#^(/ŽM shȽ dͲ.J}7BdSF% [![R build status](https://github.com/kassambara/rstatix/workflows/R-CMD-check/badge.svg)](https://github.com/kassambara/rstatix/actions) [![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/rstatix)](https://cran.r-project.org/package=rstatix) [![CRAN Checks](https://cranchecks.info/badges/summary/rstatix)](https://cran.r-project.org/web/checks/check_results_rstatix.html) [![Downloads](https://cranlogs.r-pkg.org/badges/rstatix)](https://cran.r-project.org/package=rstatix) [![Total Downloads](https://cranlogs.r-pkg.org/badges/grand-total/rstatix?color=orange)](https://cran.r-project.org/package=rstatix) rstatix ======= Provides a simple and intuitive pipe-friendly framework, coherent with the ‘tidyverse’ design philosophy, for performing basic statistical tests, including t-test, Wilcoxon test, ANOVA, Kruskal-Wallis and correlation analyses. The output of each test is automatically transformed into a tidy data frame to facilitate visualization. Additional functions are available for reshaping, reordering, manipulating and visualizing correlation matrix. Functions are also included to facilitate the analysis of factorial experiments, including purely ‘within-Ss’ designs (repeated measures), purely ‘between-Ss’ designs, and mixed ‘within-and-between-Ss’ designs. It’s also possible to compute several effect size metrics, including “eta squared” for ANOVA, “Cohen’s d” for t-test and “Cramer’s V” for the association between categorical variables. The package contains helper functions for identifying univariate and multivariate outliers, assessing normality and homogeneity of variances. Key functions ------------- ### Descriptive statistics - `get_summary_stats()`: Compute summary statistics for one or multiple numeric variables. Can handle grouped data. - `freq_table()`: Compute frequency table of categorical variables. - `get_mode()`: Compute the mode of a vector, that is the most frequent values. - `identify_outliers()`: Detect univariate outliers using boxplot methods. - `mahalanobis_distance()`: Compute Mahalanobis Distance and Flag Multivariate Outliers. - `shapiro_test()` and `mshapiro_test()`: Univariate and multivariate Shapiro-Wilk normality test. ### Comparing means - `t_test()`: perform one-sample, two-sample and pairwise t-tests - `wilcox_test()`: perform one-sample, two-sample and pairwise Wilcoxon tests - `sign_test()`: perform sign test to determine whether there is a median difference between paired or matched observations. - `anova_test()`: an easy-to-use wrapper around `car::Anova()` to perform different types of ANOVA tests, including **independent measures ANOVA**, **repeated measures ANOVA** and **mixed ANOVA**. - `get_anova_test_table()`: extract ANOVA table from `anova_test()` results. Can apply sphericity correction automatically in the case of within-subject (repeated measures) designs. `- welch_anova_test()`: Welch one-Way ANOVA test. A pipe-friendly wrapper around the base function `stats::oneway.test()`. This is is an alternative to the standard one-way ANOVA in the situation where the homogeneity of variance assumption is violated. - `kruskal_test()`: perform kruskal-wallis rank sum test - `friedman_test()`: Provides a pipe-friendly framework to perform a Friedman rank sum test, which is the non-parametric alternative to the one-way repeated measures ANOVA test. - `get_comparisons()`: Create a list of possible pairwise comparisons between groups. - `get_pvalue_position`: autocompute p-value positions for plotting significance using ggplot2. ### Facilitating ANOVA computation in R - `factorial_design()`: build factorial design for easily computing ANOVA using the `car::Anova()` function. This might be very useful for repeated measures ANOVA, which is hard to set up with the `car` package. - `anova_summary()`: Create beautiful summary tables of ANOVA test results obtained from either `car::Anova()` or `stats::aov()`. The results include ANOVA table, generalized effect size and some assumption checks, such as Mauchly’s test for sphericity in the case of repeated measures ANOVA. ### Post-hoc analyses - `tukey_hsd()`: performs tukey post-hoc tests. Can handle different inputs formats: aov, lm, formula. - `dunn_test()`: compute multiple pairwise comparisons following Kruskal-Wallis test. - `games_howell_test()`: Performs Games-Howell test, which is used to compare all possible combinations of group differences when the assumption of homogeneity of variances is violated. - `emmeans_test()`: pipe-friendly wrapper arround `emmeans` function to perform pairwise comparisons of estimated marginal means. Useful for post-hoc analyses following up ANOVA/ANCOVA tests. ### Comparing proportions - `prop_test()`, `pairwise_prop_test()` and `row_wise_prop_test()`. Performs one-sample and two-samples z-test of proportions. Wrappers around the R base function `prop.test()` but have the advantage of performing pairwise and row-wise z-test of two proportions, the post-hoc tests following a significant chi-square test of homogeneity for 2xc and rx2 contingency tables. - `fisher_test()`, `pairwise_fisher_test()` and `row_wise_fisher_test()`: Fisher’s exact test for count data. Wrappers around the R base function `fisher.test()` but have the advantage of performing pairwise and row-wise fisher tests, the post-hoc tests following a significant chi-square test of homogeneity for 2xc and rx2 contingency tables. - `chisq_test()`, `pairwise_chisq_gof_test()`, `pairwise_chisq_test_against_p()`: Performs chi-squared tests, including goodness-of-fit, homogeneity and independence tests. - `binom_test()`, `pairwise_binom_test()`, `pairwise_binom_test_against_p()`: Performs exact binomial test and pairwise comparisons following a significant exact multinomial test. Alternative to the chi-square test of goodness-of-fit-test when the sample. - `multinom_test()`: performs an exact multinomial test. Alternative to the chi-square test of goodness-of-fit-test when the sample size is small. - `mcnemar_test()`: performs McNemar chi-squared test to compare paired proportions. Provides pairwise comparisons between multiple groups. - `cochran_qtest()`: extension of the McNemar Chi-squared test for comparing more than two paired proportions. - `prop_trend_test()`: Performs chi-squared test for trend in proportion. This test is also known as Cochran-Armitage trend test. ### Comparing variances - `levene_test()`: Pipe-friendly framework to easily compute Levene’s test for homogeneity of variance across groups. Handles grouped data. - `box_m()`: Box’s M-test for homogeneity of covariance matrices ### Effect Size - `cohens_d()`: Compute cohen’s d measure of effect size for t-tests. - `wilcox_effsize()`: Compute Wilcoxon effect size (r). - `eta_squared()` and `partial_eta_squared()`: Compute effect size for ANOVA. - `kruskal_effsize()`: Compute the effect size for Kruskal-Wallis test as the eta squared based on the H-statistic. - `friedman_effsize()`: Compute the effect size of Friedman test using the Kendall’s W value. - `cramer_v()`: Compute Cramer’s V, which measures the strength of the association between categorical variables. ### Correlation analysis **Computing correlation**: - `cor_test()`: correlation test between two or more variables using Pearson, Spearman or Kendall methods. - `cor_mat()`: compute correlation matrix with p-values. Returns a data frame containing the matrix of the correlation coefficients. The output has an attribute named “pvalue”, which contains the matrix of the correlation test p-values. - `cor_get_pval()`: extract a correlation matrix p-values from an object of class `cor_mat()`. - `cor_pmat()`: compute the correlation matrix, but returns only the p-values of the correlation tests. - `as_cor_mat()`: convert a `cor_test` object into a correlation matrix format. **Reshaping correlation matrix**: - `cor_reorder()`: reorder correlation matrix, according to the coefficients, using the hierarchical clustering method. - `cor_gather()`: takes a correlation matrix and collapses (or melt) it into long format data frame (paired list) - `cor_spread()`: spread a long correlation data frame into wide format (correlation matrix). **Subsetting correlation matrix**: - `cor_select()`: subset a correlation matrix by selecting variables of interest. - `pull_triangle()`, `pull_upper_triangle()`, `pull_lower_triangle()`: pull upper and lower triangular parts of a (correlation) matrix. - `replace_triangle()`, `replace_upper_triangle()`, `replace_lower_triangle()`: replace upper and lower triangular parts of a (correlation) matrix. **Visualizing correlation matrix**: - `cor_as_symbols()`: replaces the correlation coefficients, in a matrix, by symbols according to the value. - `cor_plot()`: visualize correlation matrix using base plot. - `cor_mark_significant()`: add significance levels to a correlation matrix. ### Adjusting p-values, formatting and adding significance symbols - `adjust_pvalue()`: add an adjusted p-values column to a data frame containing statistical test p-values - `add_significance()`: add a column containing the p-value significance level - `p_round(), p_format(), p_mark_significant()`: rounding and formatting p-values ### Extract information from statistical tests Extract information from statistical test results. Useful for labelling plots with test outputs. - `get_pwc_label()`: Extract label from pairwise comparisons. - `get_test_label()`: Extract label from statistical tests. - `create_test_label()`: Create labels from user specified test results. ### Data manipulation helper functions These functions are internally used in the `rstatix` and in the `ggpubr` R package to make it easy to program with tidyverse packages using non standard evaluation. - `df_select()`, `df_arrange()`, `df_group_by()`: wrappers arround dplyr functions for supporting standard and non standard evaluations. - `df_nest_by()`: Nest a tibble data frame using grouping specification. Supports standard and non standard evaluations. - `df_split_by()`: Split a data frame by groups into subsets or data panel. Very similar to the function `df_nest_by()`. The only difference is that, it adds labels to each data subset. Labels are the combination of the grouping variable levels. - `df_unite()`: Unite multiple columns into one. - `df_unite_factors()`: Unite factor columns. First, order factors levels then merge them into one column. The output column is a factor. - `df_label_both()`, `df_label_value()`: functions to label data frames rows by by one or multiple grouping variables. - `df_get_var_names()`: Returns user specified variable names. Supports standard and non standard evaluation. ### Others - `doo()`: alternative to dplyr::do for doing anything. Technically it uses `nest() + mutate() + map()` to apply arbitrary computation to a grouped data frame. - `sample_n_by()`: sample n rows by group from a table - `convert_as_factor(), set_ref_level(), reorder_levels()`: Provides pipe-friendly functions to convert simultaneously multiple variables into a factor variable. - `make_clean_names()`: Pipe-friendly function to make syntactically valid column names (for input data frame) or names (for input vector). - `counts_to_cases()`: converts a contingency table or a data frame of counts into a data frame of individual observations. Installation and loading ------------------------ - Install the latest developmental version from [GitHub](https://github.com/kassambara/rstatix) as follow: ``` r if(!require(devtools)) install.packages("devtools") devtools::install_github("kassambara/rstatix") ``` - Or install from [CRAN](https://cran.r-project.org/package=ggpubr) as follow: ``` r install.packages("rstatix") ``` - Loading packages ``` r library(rstatix) library(ggpubr) # For easy data-visualization ``` Descriptive statistics ---------------------- ``` r # Summary statistics of some selected variables #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: iris %>% get_summary_stats(Sepal.Length, Sepal.Width, type = "common") #> # A tibble: 2 x 10 #> variable n min max median iqr mean sd se ci #> #> 1 Sepal.Length 150 4.3 7.9 5.8 1.3 5.84 0.828 0.068 0.134 #> 2 Sepal.Width 150 2 4.4 3 0.5 3.06 0.436 0.036 0.07 # Whole data frame #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: iris %>% get_summary_stats(type = "common") #> # A tibble: 4 x 10 #> variable n min max median iqr mean sd se ci #> #> 1 Petal.Length 150 1 6.9 4.35 3.5 3.76 1.76 0.144 0.285 #> 2 Petal.Width 150 0.1 2.5 1.3 1.5 1.20 0.762 0.062 0.123 #> 3 Sepal.Length 150 4.3 7.9 5.8 1.3 5.84 0.828 0.068 0.134 #> 4 Sepal.Width 150 2 4.4 3 0.5 3.06 0.436 0.036 0.07 # Grouped data #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: iris %>% group_by(Species) %>% get_summary_stats(Sepal.Length, type = "mean_sd") #> # A tibble: 3 x 5 #> Species variable n mean sd #> #> 1 setosa Sepal.Length 50 5.01 0.352 #> 2 versicolor Sepal.Length 50 5.94 0.516 #> 3 virginica Sepal.Length 50 6.59 0.636 ``` Comparing two means ------------------- To compare the means of two groups, you can use either the function `t_test()` (parametric) or `wilcox_test()` (non-parametric). In the following example the t-test will be illustrated. ### Data Preparing the demo data set: ``` r df <- ToothGrowth df$dose <- as.factor(df$dose) head(df) #> len supp dose #> 1 4.2 VC 0.5 #> 2 11.5 VC 0.5 #> 3 7.3 VC 0.5 #> 4 5.8 VC 0.5 #> 5 6.4 VC 0.5 #> 6 10.0 VC 0.5 ``` ### One-sample test The one-sample test is used to compare the mean of one sample to a known standard (or theoretical / hypothetical) mean (`mu`). ``` r df %>% t_test(len ~ 1, mu = 0) #> # A tibble: 1 x 7 #> .y. group1 group2 n statistic df p #> * #> 1 len 1 null model 60 19.1 59 6.94e-27 # One-sample test of each dose level df %>% group_by(dose) %>% t_test(len ~ 1, mu = 0) #> # A tibble: 3 x 8 #> dose .y. group1 group2 n statistic df p #> * #> 1 0.5 len 1 null model 20 10.5 19 2.24e- 9 #> 2 1 len 1 null model 20 20.0 19 3.22e-14 #> 3 2 len 1 null model 20 30.9 19 1.03e-17 ``` ### Compare two independent groups - Create a simple box plot with p-values: ``` r # T-test stat.test <- df %>% t_test(len ~ supp, paired = FALSE) stat.test #> # A tibble: 1 x 8 #> .y. group1 group2 n1 n2 statistic df p #> * #> 1 len OJ VC 30 30 1.92 55.3 0.0606 # Create a box plot p <- ggboxplot( df, x = "supp", y = "len", color = "supp", palette = "jco", ylim = c(0,40) ) # Add the p-value manually p + stat_pvalue_manual(stat.test, label = "p", y.position = 35) ``` ![](tools/README-unpaired-two-sample-t-test-1.png) - Customize labels using [glue expression](https://github.com/tidyverse/glue): ``` r p +stat_pvalue_manual(stat.test, label = "T-test, p = {p}", y.position = 36) ``` ![](tools/README-custoize-p-value-labels-1.png) - Grouped data: compare supp levels after grouping the data by “dose” ``` r # Statistical test stat.test <- df %>% group_by(dose) %>% t_test(len ~ supp) %>% adjust_pvalue() %>% add_significance("p.adj") stat.test #> # A tibble: 3 x 11 #> dose .y. group1 group2 n1 n2 statistic df p p.adj #> #> 1 0.5 len OJ VC 10 10 3.17 15.0 0.00636 0.0127 #> 2 1 len OJ VC 10 10 4.03 15.4 0.00104 0.00312 #> 3 2 len OJ VC 10 10 -0.0461 14.0 0.964 0.964 #> # … with 1 more variable: p.adj.signif # Visualization ggboxplot( df, x = "supp", y = "len", color = "supp", palette = "jco", facet.by = "dose", ylim = c(0, 40) ) + stat_pvalue_manual(stat.test, label = "p.adj", y.position = 35) ``` ![](tools/README-grouped-two-sample-t-test-1.png) ### Compare paired samples ``` r # T-test stat.test <- df %>% t_test(len ~ supp, paired = TRUE) stat.test #> # A tibble: 1 x 8 #> .y. group1 group2 n1 n2 statistic df p #> * #> 1 len OJ VC 30 30 3.30 29 0.00255 # Box plot p <- ggpaired( df, x = "supp", y = "len", color = "supp", palette = "jco", line.color = "gray", line.size = 0.4, ylim = c(0, 40) ) p + stat_pvalue_manual(stat.test, label = "p", y.position = 36) ``` ![](tools/README-paired-t-test-1.png) ### Multiple pairwise comparisons - Pairwise comparisons: if the grouping variable contains more than two categories, a pairwise comparison is automatically performed. ``` r # Pairwise t-test pairwise.test <- df %>% t_test(len ~ dose) pairwise.test #> # A tibble: 3 x 10 #> .y. group1 group2 n1 n2 statistic df p p.adj p.adj.signif #> * #> 1 len 0.5 1 20 20 -6.48 38.0 1.27e- 7 2.54e- 7 **** #> 2 len 0.5 2 20 20 -11.8 36.9 4.40e-14 1.32e-13 **** #> 3 len 1 2 20 20 -4.90 37.1 1.91e- 5 1.91e- 5 **** # Box plot ggboxplot(df, x = "dose", y = "len")+ stat_pvalue_manual( pairwise.test, label = "p.adj", y.position = c(29, 35, 39) ) ``` ![](tools/README-pairwise-comparisons-1.png) - Multiple pairwise comparisons against reference group: each level is compared to the ref group ``` r # Comparison against reference group #:::::::::::::::::::::::::::::::::::::::: # T-test: each level is compared to the ref group stat.test <- df %>% t_test(len ~ dose, ref.group = "0.5") stat.test #> # A tibble: 2 x 10 #> .y. group1 group2 n1 n2 statistic df p p.adj p.adj.signif #> * #> 1 len 0.5 1 20 20 -6.48 38.0 1.27e- 7 1.27e- 7 **** #> 2 len 0.5 2 20 20 -11.8 36.9 4.40e-14 8.80e-14 **** # Box plot ggboxplot(df, x = "dose", y = "len", ylim = c(0, 40)) + stat_pvalue_manual( stat.test, label = "p.adj.signif", y.position = c(29, 35) ) ``` ![](tools/README-comaprison-against-reference-group-1.png) ``` r # Remove bracket ggboxplot(df, x = "dose", y = "len", ylim = c(0, 40)) + stat_pvalue_manual( stat.test, label = "p.adj.signif", y.position = c(29, 35), remove.bracket = TRUE ) ``` ![](tools/README-comaprison-against-reference-group-2.png) - Multiple pairwise comparisons against all (base-mean): Comparison of each group against base-mean. ``` r # T-test stat.test <- df %>% t_test(len ~ dose, ref.group = "all") stat.test #> # A tibble: 3 x 10 #> .y. group1 group2 n1 n2 statistic df p p.adj p.adj.signif #> * #> 1 len all 0.5 60 20 5.82 56.4 2.90e-7 8.70e-7 **** #> 2 len all 1 60 20 -0.660 57.5 5.12e-1 5.12e-1 ns #> 3 len all 2 60 20 -5.61 66.5 4.25e-7 8.70e-7 **** # Box plot with horizontal mean line ggboxplot(df, x = "dose", y = "len") + stat_pvalue_manual( stat.test, label = "p.adj.signif", y.position = 35, remove.bracket = TRUE ) + geom_hline(yintercept = mean(df$len), linetype = 2) ``` ![](tools/README-comparison-against-base-mean-1.png) ANOVA test ---------- ``` r # One-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: df %>% anova_test(len ~ dose) #> ANOVA Table (type II tests) #> #> Effect DFn DFd F p p<.05 ges #> 1 dose 2 57 67.416 9.53e-16 * 0.703 # Two-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: df %>% anova_test(len ~ supp*dose) #> ANOVA Table (type II tests) #> #> Effect DFn DFd F p p<.05 ges #> 1 supp 1 54 15.572 2.31e-04 * 0.224 #> 2 dose 2 54 92.000 4.05e-18 * 0.773 #> 3 supp:dose 2 54 4.107 2.20e-02 * 0.132 # Two-way repeated measures ANOVA #::::::::::::::::::::::::::::::::::::::::: df$id <- rep(1:10, 6) # Add individuals id # Use formula # df %>% anova_test(len ~ supp*dose + Error(id/(supp*dose))) # or use character vector df %>% anova_test(dv = len, wid = id, within = c(supp, dose)) #> ANOVA Table (type III tests) #> #> $ANOVA #> Effect DFn DFd F p p<.05 ges #> 1 supp 1 9 34.866 2.28e-04 * 0.224 #> 2 dose 2 18 106.470 1.06e-10 * 0.773 #> 3 supp:dose 2 18 2.534 1.07e-01 0.132 #> #> $`Mauchly's Test for Sphericity` #> Effect W p p<.05 #> 1 dose 0.807 0.425 #> 2 supp:dose 0.934 0.761 #> #> $`Sphericity Corrections` #> Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF] #> 1 dose 0.838 1.68, 15.09 2.79e-09 * 1.008 2.02, 18.15 1.06e-10 #> 2 supp:dose 0.938 1.88, 16.88 1.12e-01 1.176 2.35, 21.17 1.07e-01 #> p[HF]<.05 #> 1 * #> 2 # Use model as arguments #::::::::::::::::::::::::::::::::::::::::: .my.model <- lm(yield ~ block + N*P*K, npk) anova_test(.my.model) #> ANOVA Table (type II tests) #> #> Effect DFn DFd F p p<.05 ges #> 1 block 4 12 4.959 0.014 * 0.623 #> 2 N 1 12 12.259 0.004 * 0.505 #> 3 P 1 12 0.544 0.475 0.043 #> 4 K 1 12 6.166 0.029 * 0.339 #> 5 N:P 1 12 1.378 0.263 0.103 #> 6 N:K 1 12 2.146 0.169 0.152 #> 7 P:K 1 12 0.031 0.863 0.003 #> 8 N:P:K 0 12 NA NA NA ``` Correlation tests ----------------- ``` r # Data preparation mydata <- mtcars %>% select(mpg, disp, hp, drat, wt, qsec) head(mydata, 3) #> mpg disp hp drat wt qsec #> Mazda RX4 21.0 160 110 3.90 2.620 16.46 #> Mazda RX4 Wag 21.0 160 110 3.90 2.875 17.02 #> Datsun 710 22.8 108 93 3.85 2.320 18.61 # Correlation test between two variables mydata %>% cor_test(wt, mpg, method = "pearson") #> # A tibble: 1 x 8 #> var1 var2 cor statistic p conf.low conf.high method #> #> 1 wt mpg -0.87 -9.56 1.29e-10 -0.934 -0.744 Pearson # Correlation of one variable against all mydata %>% cor_test(mpg, method = "pearson") #> # A tibble: 5 x 8 #> var1 var2 cor statistic p conf.low conf.high method #> #> 1 mpg disp -0.85 -8.75 9.38e-10 -0.923 -0.708 Pearson #> 2 mpg hp -0.78 -6.74 1.79e- 7 -0.885 -0.586 Pearson #> 3 mpg drat 0.68 5.10 1.78e- 5 0.436 0.832 Pearson #> 4 mpg wt -0.87 -9.56 1.29e-10 -0.934 -0.744 Pearson #> 5 mpg qsec 0.42 2.53 1.71e- 2 0.0820 0.670 Pearson # Pairwise correlation test between all variables mydata %>% cor_test(method = "pearson") #> # A tibble: 36 x 8 #> var1 var2 cor statistic p conf.low conf.high method #> #> 1 mpg mpg 1 Inf 0. 1 1 Pearson #> 2 mpg disp -0.85 -8.75 9.38e-10 -0.923 -0.708 Pearson #> 3 mpg hp -0.78 -6.74 1.79e- 7 -0.885 -0.586 Pearson #> 4 mpg drat 0.68 5.10 1.78e- 5 0.436 0.832 Pearson #> 5 mpg wt -0.87 -9.56 1.29e-10 -0.934 -0.744 Pearson #> 6 mpg qsec 0.42 2.53 1.71e- 2 0.0820 0.670 Pearson #> 7 disp mpg -0.85 -8.75 9.38e-10 -0.923 -0.708 Pearson #> 8 disp disp 1 Inf 0. 1 1 Pearson #> 9 disp hp 0.79 7.08 7.14e- 8 0.611 0.893 Pearson #> 10 disp drat -0.71 -5.53 5.28e- 6 -0.849 -0.481 Pearson #> # … with 26 more rows ``` Correlation matrix ------------------ ``` r # Compute correlation matrix #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cor.mat <- mydata %>% cor_mat() cor.mat #> # A tibble: 6 x 7 #> rowname mpg disp hp drat wt qsec #> * #> 1 mpg 1 -0.85 -0.78 0.68 -0.87 0.42 #> 2 disp -0.85 1 0.79 -0.71 0.89 -0.43 #> 3 hp -0.78 0.79 1 -0.45 0.66 -0.71 #> 4 drat 0.68 -0.71 -0.45 1 -0.71 0.091 #> 5 wt -0.87 0.89 0.66 -0.71 1 -0.17 #> 6 qsec 0.42 -0.43 -0.71 0.091 -0.17 1 # Show the significance levels #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cor.mat %>% cor_get_pval() #> # A tibble: 6 x 7 #> rowname mpg disp hp drat wt qsec #> #> 1 mpg 0. 9.38e-10 0.000000179 0.0000178 1.29e- 10 0.0171 #> 2 disp 9.38e-10 0. 0.0000000714 0.00000528 1.22e- 11 0.0131 #> 3 hp 1.79e- 7 7.14e- 8 0 0.00999 4.15e- 5 0.00000577 #> 4 drat 1.78e- 5 5.28e- 6 0.00999 0 4.78e- 6 0.62 #> 5 wt 1.29e-10 1.22e-11 0.0000415 0.00000478 2.27e-236 0.339 #> 6 qsec 1.71e- 2 1.31e- 2 0.00000577 0.62 3.39e- 1 0 # Replacing correlation coefficients by symbols #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cor.mat %>% cor_as_symbols() %>% pull_lower_triangle() #> rowname mpg disp hp drat wt qsec #> 1 mpg #> 2 disp * #> 3 hp * * #> 4 drat + + . #> 5 wt * * + + #> 6 qsec . . + # Mark significant correlations #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cor.mat %>% cor_mark_significant() #> rowname mpg disp hp drat wt qsec #> 1 mpg #> 2 disp -0.85**** #> 3 hp -0.78**** 0.79**** #> 4 drat 0.68**** -0.71**** -0.45** #> 5 wt -0.87**** 0.89**** 0.66**** -0.71**** #> 6 qsec 0.42* -0.43* -0.71**** 0.091 -0.17 # Draw correlogram using R base plot #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cor.mat %>% cor_reorder() %>% pull_lower_triangle() %>% cor_plot() ``` ![](tools/README-unnamed-chunk-10-1.png) Related articles ---------------- - [How to Add P-Values onto Basic GGPLOTS](https://www.datanovia.com/en/blog/how-to-add-p-values-onto-basic-ggplots/) - [How to Add Adjusted P-values to a Multi-Panel GGPlot](https://www.datanovia.com/en/blog/ggpubr-how-to-add-adjusted-p-values-to-a-multi-panel-ggplot/) - [How to Add P-values to GGPLOT Facets](https://www.datanovia.com/en/blog/how-to-add-p-values-to-ggplot-facets/) - [How to Add P-Values Generated Elsewhere to a GGPLOT](https://www.datanovia.com/en/blog/ggpubr-how-to-add-p-values-generated-elsewhere-to-a-ggplot/) - [How to Add P-Values onto a Grouped GGPLOT using the GGPUBR R Package](https://www.datanovia.com/en/blog/how-to-add-p-values-onto-a-grouped-ggplot-using-the-ggpubr-r-package/) - [How to Create Stacked Bar Plots with Error Bars and P-values](https://www.datanovia.com/en/blog/how-to-create-stacked-bar-plots-with-error-bars-and-p-values/) - [How to Add P-Values onto Horizontal GGPLOTS](https://www.datanovia.com/en/blog/how-to-add-p-values-onto-horizontal-ggplots/) - [Add P-values and Significance Levels to ggplots](http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/76-add-p-values-and-significance-levels-to-ggplots/) - [Comparing Means of Two Groups in R](https://www.datanovia.com/en/courses/comparing-means-of-two-groups-in-r/) - [T-test in R](https://www.datanovia.com/en/lessons/t-test-in-r/) - [Wilcoxon Test in R](https://www.datanovia.com/en/lessons/wilcoxon-test-in-r/) - [Sign Test in R](https://www.datanovia.com/en/lessons/sign-test-in-r/) - [Comparing Multiple Means in R](https://www.datanovia.com/en/courses/comparing-multiple-means-in-r/) - [ANOVA in R](https://www.datanovia.com/en/lessons/anova-in-r/) - [Repeated Measures ANOVA in R](https://www.datanovia.com/en/lessons/repeated-measures-anova-in-r/) - [Mixed ANOVA in R](https://www.datanovia.com/en/lessons/mixed-anova-in-r/) - [ANCOVA in R](https://www.datanovia.com/en/lessons/ancova-in-r/) - [One-Way MANOVA in R](https://www.datanovia.com/en/lessons/one-way-manova-in-r/) - [Kruskal-Wallis Test in R](https://www.datanovia.com/en/lessons/kruskal-wallis-test-in-r/) - [Friedman Test in R](https://www.datanovia.com/en/lessons/friedman-test-in-r/) rstatix/man/0000755000176200001440000000000014011725434012524 5ustar liggesusersrstatix/man/df_split_by.Rd0000644000176200001440000000363513640673450015327 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_split_by} \alias{df_split_by} \title{Split a Data Frame into Subset} \usage{ df_split_by( data, ..., vars = NULL, label_col = "label", labeller = df_label_both, sep = c(", ", ":") ) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used as grouping variables.} \item{vars}{a character vector containing the grouping variables of interest.} \item{label_col}{column to hold the label of the data subsets. Default column name is "label".} \item{labeller}{A function that takes a data frame, the grouping variables, label_col and label_sep arguments, and add labels into the data frame. Example of possible values are: \code{\link{df_label_both}()} and \code{\link{df_label_value}()}.} \item{sep}{String separating labelling variables and values. Should be of length 2 in the function \code{df_label_both()}. 1) One sep is used to separate groups, for example ','; 2) The other sep between group name and levels; for example ':'.} } \value{ A tbl with one row per unique combination of the grouping variables. The first columns are the grouping variables, followed by a list column of tibbles with matching rows of the remaining columns, and a column named label, containing labels. } \description{ Split a data frame by groups into subsets or data panel. Very similar to the function \code{\link{df_nest_by}()}. The only difference is that, it adds label to each data subset. Labels are the combination of the grouping variable levels. The column holding labels are named "label". } \examples{ # Split a data frame # ::::::::::::::::::::::::::::::::::::::::::::::::: # Create a grouped data res <- ToothGrowth \%>\% df_split_by(dose, supp) res # Show subsets res$data # Add panel/subset labels res <- ToothGrowth \%>\% df_split_by(dose, supp) res } rstatix/man/df_label_value.Rd0000644000176200001440000000312113651764443015750 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_label_both} \alias{df_label_both} \alias{df_label_value} \title{Functions to Label Data Frames by Grouping Variables} \usage{ df_label_both(data, ..., vars = NULL, label_col = "label", sep = c(", ", ":")) df_label_value(data, ..., vars = NULL, label_col = "label", sep = ", ") } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used as grouping variables.} \item{vars}{a character vector containing the grouping variables of interest.} \item{label_col}{column to hold the label of the data subsets. Default column name is "label".} \item{sep}{String separating labelling variables and values. Should be of length 2 in the function \code{df_label_both()}. 1) One sep is used to separate groups, for example ','; 2) The other sep between group name and levels; for example ':'.} } \value{ a modified data frame with a column containing row labels. } \description{ Functions to label data frame rows by one or multiple grouping variables. } \section{Functions}{ \itemize{ \item \code{df_label_both}: Displays both the variable name and the factor value. \item \code{df_label_value}: Displays only the value of a factor. }} \examples{ # Data preparation df <- head(ToothGrowth) # Labelling: Non standard evaluation df \%>\% df_label_both(dose, supp) # Standard evaluation df \%>\% df_label_both(dose, supp) # Nesting the data then label each subset by groups ToothGrowth \%>\% df_nest_by(dose, supp) \%>\% df_label_both(supp, dose) } rstatix/man/welch_anova_test.Rd0000644000176200001440000000305313520477224016346 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/welch_anova_test.R \name{welch_anova_test} \alias{welch_anova_test} \title{Welch One-Way ANOVA Test} \usage{ welch_anova_test(data, formula) } \arguments{ \item{data}{a data frame containing the variables in the formula.} \item{formula}{a formula specifying the ANOVA model similar to aov. Can be of the form y ~ group where y is a numeric variable giving the data values and group is a factor with one or multiple levels giving the corresponding groups. For example, formula = TP53 ~ cancer_group.} } \value{ return a data frame with the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{n}: sample count. \item \code{statistic}: the value of the test statistic. \item \code{p}: p-value. \item \code{method}: the statistical test used to compare groups.} } \description{ Tests for equal means in a one-way design (not assuming equal variance). A wrapper around the base function \code{\link[stats]{oneway.test}()}. This is is an alternative to the standard one-way ANOVA in the situation where the homogeneity of variance assumption is violated. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth df$dose <- as.factor(df$dose) # Welch one-way ANOVA test (not assuming equal variance) #::::::::::::::::::::::::::::::::::::::::: df \%>\% welch_anova_test(len ~ dose) # Grouped data #::::::::::::::::::::::::::::::::::::::::: df \%>\% group_by(supp) \%>\% welch_anova_test(len ~ dose) } rstatix/man/cor_reorder.Rd0000644000176200001440000000161513470305677015336 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_reorder.R \name{cor_reorder} \alias{cor_reorder} \title{Reorder Correlation Matrix} \usage{ cor_reorder(x) } \arguments{ \item{x}{a correlation matrix. Particularly, an object of class \code{cor_mat}.} } \value{ a data frame } \description{ reorder correlation matrix, according to the coefficients, using the hierarchical clustering method. } \examples{ # Compute correlation matrix #:::::::::::::::::::::::::::::::::::::::::: cor.mat <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_mat() # Reorder by correlation and get p-values #:::::::::::::::::::::::::::::::::::::::::: # Reorder cor.mat \%>\% cor_reorder() # Get p-values of the reordered cor_mat cor.mat \%>\% cor_reorder() \%>\% cor_get_pval() } \seealso{ \code{\link{cor_mat}()}, \code{\link{cor_gather}()}, \code{\link{cor_spread}()} } rstatix/man/get_mode.Rd0000644000176200001440000000114013470227216014575 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/get_mode.R \name{get_mode} \alias{get_mode} \title{Compute Mode} \usage{ get_mode(x) } \arguments{ \item{x}{a vector. Can be numeric, factor or character vector.} } \description{ Compute the mode in a given vector. Mode is the most frequent value. } \examples{ # Mode of numeric vector x <- c(1:5, 6, 6, 7:10) get_mode(x) # Bimodal x <- c(1:5, 6, 6, 7, 8, 9, 9, 10) get_mode(x) # No mode x <- c(1, 2, 3, 4, 5) get_mode(x) # Nominal vector fruits <- c(rep("orange", 10), rep("apple", 5), rep("lemon", 2)) get_mode(fruits) } rstatix/man/friedman_test.Rd0000644000176200001440000000317013570426745015654 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/friedman_test.R \name{friedman_test} \alias{friedman_test} \title{Friedman Rank Sum Test} \usage{ friedman_test(data, formula, ...) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{a ~ b | c}, where \code{a} (numeric) is the dependent variable name; \code{b} is the within-subjects factor variables; and \code{c} (factor) is the column name containing individuals/subjects identifier. Should be unique per individual.} \item{...}{other arguments to be passed to the function \code{\link[stats]{friedman.test}}.} } \value{ return a data frame with the following columns: \itemize{ \item \code{.y.}: the y (dependent) variable used in the test. \item \code{n}: sample count. \item \code{statistic}: the value of Friedman's chi-squared statistic, used to compute the p-value. \item \code{p}: p-value. \item \code{method}: the statistical test used to compare groups.} } \description{ Provides a pipe-friendly framework to perform a Friedman rank sum test, which is the non-parametric alternative to the one-way repeated measures ANOVA test. Wrapper around the function \code{\link[stats]{friedman.test}()}. Read more: \href{https://www.datanovia.com/en/lessons/friedman-test-in-r/}{Friedman test in R}. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth \%>\% filter(supp == "VC") \%>\% mutate(id = rep(1:10, 3)) head(df) # Friedman rank sum test #::::::::::::::::::::::::::::::::::::::::: df \%>\% friedman_test(len ~ dose | id) } rstatix/man/binom_test.Rd0000644000176200001440000001044213640215135015156 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/binom_test.R \name{binom_test} \alias{binom_test} \alias{pairwise_binom_test} \alias{pairwise_binom_test_against_p} \title{Exact Binomial Test} \usage{ binom_test( x, n, p = 0.5, alternative = "two.sided", conf.level = 0.95, detailed = FALSE ) pairwise_binom_test( x, p.adjust.method = "holm", alternative = "two.sided", conf.level = 0.95 ) pairwise_binom_test_against_p( x, p = rep(1/length(x), length(x)), p.adjust.method = "holm", alternative = "two.sided", conf.level = 0.95 ) } \arguments{ \item{x}{numeric vector containing the counts.} \item{n}{number of trials; ignored if \code{x} has length 2.} \item{p}{a vector of probabilities of success. The length of p must be the same as the number of groups specified by x, and its elements must be greater than 0 and less than 1.} \item{alternative}{indicates the alternative hypothesis and must be one of \code{"two.sided"}, \code{"greater"} or \code{"less"}. You can specify just the initial letter.} \item{conf.level}{confidence level for the returned confidence interval.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} } \value{ return a data frame containing the p-value and its significance. with some the following columns: \itemize{ \item \code{group, group1, group2}: the categories or groups being compared. \item \code{statistic}: the number of successes. \item \code{parameter}: the number of trials. \item \code{p}: p-value of the test. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the used statistical test. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: the estimated probability of success. \item \code{alternative}: a character string describing the alternative hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a confidence interval for the probability of success.} The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs exact binomial test and pairwise comparisons following a significant exact multinomial test. Wrapper around the R base function \code{link[stats]{binom.test}()} that returns a data frame as a result. } \section{Functions}{ \itemize{ \item \code{binom_test}: performs exact binomial test. Wrapper around the R base function \code{\link[stats]{binom.test}} that returns a dataframe as a result. \item \code{pairwise_binom_test}: performs pairwise comparisons (binomial test) following a significant exact multinomial test. \item \code{pairwise_binom_test_against_p}: performs pairwise comparisons (binomial test) following a significant exact multinomial test for given probabilities. }} \examples{ # Exact binomial test #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: 160 mice with cancer including 95 male and 65 female # Q1: Does cancer affect more males than females? binom_test(x = 95, n = 160) # => yes, there are a significant difference # Q2: compare the observed proportion of males # to an expected proportion (p = 3/5) binom_test(x = 95, n = 160, p = 3/5) # => there are no significant difference # Multinomial test #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data tulip <- c(red = 81, yellow = 50, white = 27) # Question 1: are the color equally common ? # this is a test of homogeneity res <- multinom_test(tulip) res attr(res, "descriptives") # Pairwise comparisons between groups pairwise_binom_test(tulip, p.adjust.method = "bonferroni") # Question 2: comparing observed to expected proportions # this is a goodness-of-fit test expected.p <- c(red = 0.5, yellow = 0.33, white = 0.17) res <- multinom_test(tulip, expected.p) res attr(res, "descriptives") # Pairwise comparisons against a given probabilities pairwise_binom_test_against_p(tulip, expected.p) } \seealso{ \link{multinom_test} } rstatix/man/cramer_v.Rd0000644000176200001440000000230013471040021014573 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cramer_v.R \name{cramer_v} \alias{cramer_v} \title{Compute Cramer's V} \usage{ cramer_v(x, y = NULL, correct = TRUE, ...) } \arguments{ \item{x}{a numeric vector or matrix. \code{x} and \code{y} can also both be factors.} \item{y}{a numeric vector; ignored if \code{x} is a matrix. If \code{x} is a factor, \code{y} should be a factor of the same length.} \item{correct}{a logical indicating whether to apply continuity correction when computing the test statistic for 2 by 2 tables: one half is subtracted from all \eqn{|O - E|} differences; however, the correction will not be bigger than the differences themselves. No correction is done if \code{simulate.p.value = TRUE}.} \item{...}{other arguments passed to the function \code{\link[stats]{chisq.test}()}.} } \description{ Compute Cramer's V, which measures the strength of the association between categorical variables. } \examples{ # Data preparation df <- as.table(rbind(c(762, 327, 468), c(484, 239, 477))) dimnames(df) <- list( gender = c("F", "M"), party = c("Democrat","Independent", "Republican") ) df # Compute cramer's V cramer_v(df) } rstatix/man/df_group_by.Rd0000644000176200001440000000137413640733363015326 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_group_by} \alias{df_group_by} \title{Group a Data Frame by One or more Variables} \usage{ df_group_by(data, ..., vars = NULL) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest.} \item{vars}{a character vector containing the variable names of interest.} } \description{ Group a data frame by one or more variables. Supports standard and non standard evaluation. } \examples{ # Non standard evaluation by_dose <- head(ToothGrowth) \%>\% df_group_by(dose) by_dose # Standard evaluation head(ToothGrowth) \%>\% df_group_by(vars = c("dose", "supp")) } rstatix/man/Manova.Rd0000644000176200001440000000037413672731023014243 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils-manova.R \name{Manova} \alias{Manova} \title{Manova exported from car package} \description{ See \code{car::\link[car:Anova]{Manova}} for details. } \keyword{internal} rstatix/man/pull_triangle.Rd0000644000176200001440000000325513337207400015657 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pull_triangle.R \name{pull_triangle} \alias{pull_triangle} \alias{pull_upper_triangle} \alias{pull_lower_triangle} \title{Pull Lower and Upper Triangular Part of a Matrix} \usage{ pull_triangle(x, triangle = c("lower", "upper"), diagonal = FALSE) pull_upper_triangle(x, diagonal = FALSE) pull_lower_triangle(x, diagonal = FALSE) } \arguments{ \item{x}{a (correlation) matrix} \item{triangle}{the triangle to pull. Allowed values are one of "upper" and "lower".} \item{diagonal}{logical. Default is FALSE. If TRUE, the matrix diagonal is included.} } \value{ an object of class \code{cor_mat_tri}, which is a data frame } \description{ Returns the lower or the upper triangular part of a (correlation) matrix. } \section{Functions}{ \itemize{ \item \code{pull_triangle}: returns either the lower or upper triangular part of a matrix. \item \code{pull_upper_triangle}: returns an object of class \code{upper_tri}, which is a data frame containing the upper triangular part of a matrix. \item \code{pull_lower_triangle}: returns an object of class \code{lower_tri}, which is a data frame containing the lower triangular part of a matrix. }} \examples{ # Data preparation #:::::::::::::::::::::::::::::::::::::::::: mydata <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) head(mydata, 3) # Compute correlation matrix and pull triangles #:::::::::::::::::::::::::::::::::::::::::: # Correlation matrix cor.mat <- cor_mat(mydata) cor.mat # Pull lower triangular part cor.mat \%>\% pull_lower_triangle() # Pull upper triangular part cor.mat \%>\% pull_upper_triangle() } \seealso{ \code{\link{replace_triangle}()} } rstatix/man/replace_triangle.Rd0000644000176200001440000000367513640215135016325 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/replace_triangle.R \name{replace_triangle} \alias{replace_triangle} \alias{replace_upper_triangle} \alias{replace_lower_triangle} \title{Replace Lower and Upper Triangular Part of a Matrix} \usage{ replace_triangle(x, triangle = c("lower", "upper"), by = "", diagonal = FALSE) replace_upper_triangle(x, by = "", diagonal = FALSE) replace_lower_triangle(x, by = "", diagonal = FALSE) } \arguments{ \item{x}{a (correlation) matrix} \item{triangle}{the triangle to replace. Allowed values are one of "upper" and "lower".} \item{by}{a replacement argument. Appropriate values are either "" or NA. Used to replace the upper, lower or the diagonal part of the matrix.} \item{diagonal}{logical. Default is FALSE. If TRUE, the matrix diagonal is included.} } \value{ an object of class \code{cor_mat_tri}, which is a data frame } \description{ Replace the lower or the upper triangular part of a (correlation) matrix. } \section{Functions}{ \itemize{ \item \code{replace_triangle}: replaces the specified triangle by empty or NA. \item \code{replace_upper_triangle}: replaces the upper triangular part of a matrix. Returns an object of class \code{lower_tri}. \item \code{replace_lower_triangle}: replaces the lower triangular part of a matrix. Returns an object of class \code{lower_tri} }} \examples{ # Compute correlation matrix and pull triangles #:::::::::::::::::::::::::::::::::::::::::: # Correlation matrix cor.mat <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_mat() cor.mat # Replace upper triangle by NA #:::::::::::::::::::::::::::::::::::::::::: cor.mat \%>\% replace_upper_triangle(by = NA) # Replace upper triangle by NA and reshape the # correlation matrix to have unique combinations of variables #:::::::::::::::::::::::::::::::::::::::::: cor.mat \%>\% replace_upper_triangle(by = NA) \%>\% cor_gather() } \seealso{ \code{\link{pull_triangle}()} } rstatix/man/factorial_design.Rd0000644000176200001440000000541013621171635016314 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/factorial_design.R \name{factorial_design} \alias{factorial_design} \title{Build Factorial Designs for ANOVA} \usage{ factorial_design(data, dv, wid, between, within, covariate) } \arguments{ \item{data}{a data frame containing the variables} \item{dv}{(numeric) dependent variable name.} \item{wid}{(factor) column name containing individuals/subjects identifier. Should be unique per individual.} \item{between}{(optional) between-subject factor variables.} \item{within}{(optional) within-subjects factor variables} \item{covariate}{(optional) covariate names (for ANCOVA)} } \value{ a list with the following components: \itemize{ \item \strong{the specified arguments}: \code{dv, wid, between, within} \item \strong{data}: the original data (long format) or independent ANOVA. The wide format is returned for repeated measures ANOVA. \item \strong{idata}: an optional data frame giving the levels of factors defining the intra-subject model for multivariate repeated-measures data. \item \strong{idesign}: a one-sided model formula using the “data” in idata and specifying the intra-subject design. \item \strong{repeated}: logical. Value is TRUE when the data is a repeated design. \item \strong{lm_formula}: the formula used to build the \code{lm} model. \item \strong{lm_data}: the data used to build the \code{lm} model. Can be either in a long format (i.e., the original data for independent measures ANOVA) or in a wide format (case of repeated measures ANOVA). \item \strong{model}: the \code{lm} model } } \description{ Provides helper functions to build factorial design for easily computing ANOVA using the \code{\link[car]{Anova}()} function. This might be very useful for repeated measures ANOVA, which is hard to set up with the \code{car} package. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth head(df) # Repeated measures designs #::::::::::::::::::::::::::::::::::::::::: # Prepare the data df$id <- rep(1:10, 6) # Add individuals id head(df) # Build factorial designs design <- factorial_design(df, dv = len, wid = id, within = c(supp, dose)) design # Easily perform repeated measures ANOVA using the car package res.anova <- Anova(design$model, idata = design$idata, idesign = design$idesign, type = 3) summary(res.anova, multivariate = FALSE) # Independent measures designs #::::::::::::::::::::::::::::::::::::::::: # Build factorial designs df$id <- 1:nrow(df) design <- factorial_design(df, dv = len, wid = id, between = c(supp, dose)) design # Perform ANOVA Anova(design$model, type = 3) } \seealso{ \code{\link{anova_test}()}, \code{\link{anova_summary}()} } \author{ Alboukadel Kassambara, \email{alboukadel.kassambara@gmail.com} } rstatix/man/games_howell_test.Rd0000644000176200001440000000617113534273547016541 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/games_howell_test.R \name{games_howell_test} \alias{games_howell_test} \title{Games Howell Post-hoc Tests} \usage{ games_howell_test(data, formula, conf.level = 0.95, detailed = FALSE) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{conf.level}{confidence level of the interval.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y (outcome) variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n1,n2}: Sample counts. \item \code{estimate, conf.low, conf.high}: mean difference and its confidence intervals. \item \code{statistic}: Test statistic (t-value) used to compute the p-value. \item \code{df}: degrees of freedom calculated using Welch’s correction. \item \code{p.adj}: adjusted p-value using Tukey's method. \item \code{method}: the statistical test used to compare groups. \item \code{p.adj.signif}: the significance level of p-values. } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs Games-Howell test, which is used to compare all possible combinations of group differences when the assumption of homogeneity of variances is violated. This post hoc test provides confidence intervals for the differences between group means and shows whether the differences are statistically significant. The test is based on Welch’s degrees of freedom correction and uses Tukey’s studentized range distribution for computing the p-values. The test compares the difference between each pair of means with appropriate adjustment for the multiple testing. So there is no need to apply additional p-value corrections. } \details{ The Games-Howell method is an improved version of the Tukey-Kramer method and is applicable in cases where the equivalence of variance assumption is violated. It is a t-test using Welch’s degree of freedom. This method uses a strategy for controlling the type I error for the entire comparison and is known to maintain the preset significance level even when the size of the sample is different. However, the smaller the number of samples in each group, the it is more tolerant the type I error control. Thus, this method can be applied when the number of samples is six or more. } \examples{ # Simple test ToothGrowth \%>\% games_howell_test(len ~ dose) # Grouped data ToothGrowth \%>\% group_by(supp) \%>\% games_howell_test(len ~ dose) } \references{ \itemize{ \item Aaron Schlege, https://rpubs.com/aaronsc32/games-howell-test. \item Sangseok Lee, Dong Kyu Lee. What is the proper way to apply the multiple comparison test?. Korean J Anesthesiol. 2018;71(5):353-360. } } rstatix/man/cohens_d.Rd0000644000176200001440000001131213654501434014577 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cohens_d.R \name{cohens_d} \alias{cohens_d} \title{Compute Cohen's d Measure of Effect Size} \usage{ cohens_d( data, formula, comparisons = NULL, ref.group = NULL, paired = FALSE, mu = 0, var.equal = FALSE, hedges.correction = FALSE, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000 ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group). If \code{ref.group = "all"}, pairwise two sample tests are performed for comparing each grouping variable levels against all (i.e. basemean).} \item{paired}{a logical indicating whether you want a paired test.} \item{mu}{theoretical mean, use for one-sample t-test. Default is 0.} \item{var.equal}{a logical variable indicating whether to treat the two variances as being equal. If TRUE then the pooled variance is used to estimate the variance otherwise the Welch (or Satterthwaite) approximation to the degrees of freedom is used. Used only for unpaired or independent samples test.} \item{hedges.correction}{logical indicating whether apply the Hedges correction by multiplying the usual value of Cohen's d by \code{(N-3)/(N-2.25)} (for unpaired t-test) and by \code{(n1-2)/(n1-1.25)} for paired t-test; where \code{N} is the total size of the two groups being compared (N = n1 + n2).} \item{ci}{If TRUE, returns confidence intervals by bootstrap. May be slow.} \item{conf.level}{The level for the confidence interval.} \item{ci.type}{The type of confidence interval to use. Can be any of "norm", "basic", "perc", or "bca". Passed to \code{boot::boot.ci}.} \item{nboot}{The number of replications to use for bootstrap.} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. \item \code{effsize}: estimate of the effect size (\code{d} value). \item \code{magnitude}: magnitude of effect size. \item \code{conf.low,conf.high}: lower and upper bound of the effect size confidence interval.} } \description{ Compute the effect size for t-test. T-test conventional effect sizes, proposed by Cohen, are: 0.2 (small effect), 0.5 (moderate effect) and 0.8 (large effect). Cohen's \code{d} is calculated as the difference between means or mean minus \code{mu} divided by the estimated standardized deviation. For independent samples t-test, there are two possibilities implemented. If the t-test did not make a homogeneity of variance assumption, (the Welch test), the variance term will mirror the Welch test, otherwise a pooled estimate is used. If a paired samples t-test was requested, then effect size desired is based on the standard deviation of the differences. It can also returns confidence intervals by bootstap. } \details{ Quantification of the effect size magnitude is performed using the thresholds defined in Cohen (1992). The magnitude is assessed using the thresholds provided in (Cohen 1992), i.e. \code{|d| < 0.2} "negligible", \code{|d| < 0.5} "small", \code{|d| < 0.8} "medium", otherwise "large". } \examples{ # One-sample t test effect size ToothGrowth \%>\% cohens_d(len ~ 1, mu = 0) # Two indepedent samples t-test effect size ToothGrowth \%>\% cohens_d(len ~ supp, var.equal = TRUE) # Paired samples effect size df <- data.frame( id = 1:5, pre = c(110, 122, 101, 120, 140), post = c(150, 160, 110, 140, 155) ) df <- df \%>\% gather(key = "treatment", value = "value", -id) head(df) df \%>\% cohens_d(value ~ treatment, paired = TRUE) } \references{ \itemize{ \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd ed.). New York:Academic Press. \item Cohen, J. (1992). A power primer. Psychological Bulletin, 112, 155-159. \item Hedges, Larry & Olkin, Ingram. (1985). Statistical Methods in Meta-Analysis. 10.2307/1164953. \item Navarro, Daniel. 2015. Learning Statistics with R: A Tutorial for Psychology Students and Other Beginners (Version 0.5). } } rstatix/man/add_significance.Rd0000644000176200001440000000166113640215135016250 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/add_significance.R \name{add_significance} \alias{add_significance} \title{Add P-value Significance Symbols} \usage{ add_significance( data, p.col = NULL, output.col = NULL, cutpoints = c(0, 1e-04, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "ns") ) } \arguments{ \item{data}{a data frame containing a p-value column.} \item{p.col}{column name containing p-values.} \item{output.col}{the output column name to hold the adjusted p-values.} \item{cutpoints}{numeric vector used for intervals.} \item{symbols}{character vector, one shorter than cutpoints, used as significance symbols.} } \value{ a data frame } \description{ Add p-value significance symbols into a data frame. } \examples{ # Perform pairwise comparisons and adjust p-values ToothGrowth \%>\% t_test(len ~ dose) \%>\% adjust_pvalue() \%>\% add_significance("p.adj") } rstatix/man/get_test_label.Rd0000644000176200001440000001210513705507113015770 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/get_test_label.R \name{get_pwc_label} \alias{get_pwc_label} \alias{get_test_label} \alias{create_test_label} \alias{get_n} \alias{get_description} \title{Extract Label Information from Statistical Tests} \usage{ get_pwc_label(stat.test, type = c("expression", "text")) get_test_label( stat.test, description = NULL, p.col = "p", type = c("expression", "text"), correction = c("auto", "GG", "HF", "none"), row = NULL, detailed = FALSE ) create_test_label( statistic.text, statistic, p, parameter = NA, description = NULL, n = NA, effect.size = NA, effect.size.text = NA, type = c("expression", "text"), detailed = FALSE ) get_n(stat.test) get_description(stat.test) } \arguments{ \item{stat.test}{statistical test results returned by \code{rstatix} functions.} \item{type}{the label type. Can be one of "text" and "expression". Partial match allowed. If you want to add the label onto a ggplot, it might be useful to specify \code{type = "expresion"}.} \item{description}{the test description used as the prefix of the label. Examples of description are "ANOVA", "Two Way ANOVA". To remove the default description, specify \code{description = NULL}. If missing, we'll try to guess the statistical test default description.} \item{p.col}{character specifying the column containing the p-value. Default is \code{"p"}, can be \code{"p.adj"}.} \item{correction}{character, considered only in the case of ANOVA test. Which sphericity correction of the degrees of freedom should be reported for the within-subject factors (repeated measures). The default is set to \code{"GG"} corresponding to the Greenhouse-Geisser correction. Possible values are \code{"GG"}, \code{"HF"} (i.e., Hyunh-Feldt correction), \code{"none"} (i.e., no correction) and \code{"auto"} (apply automatically GG correction if the sphericity assumption is not for within-subject design.} \item{row}{numeric, the row index to be considered. If NULL, the last row is automatically considered for ANOVA test.} \item{detailed}{logical value. If TRUE, returns detailed label.} \item{statistic.text}{character specifying the test statistic. For example \code{statistic.text = "F"} (for ANOVA test ); \code{statistic.text = "t"} (for t-test ).} \item{statistic}{the numeric value of a statistic.} \item{p}{the p-value of the test.} \item{parameter}{string containing the degree of freedom (if exists). Default is \code{NA} to accommodate non-parametric tests. For example \code{parameter = "1,9"} (for ANOVA test. Two parameters exist: DFn and DFd); \code{sparameter = "9"} (for t-test ).} \item{n}{sample count, example: \code{n = 10}.} \item{effect.size}{the effect size value} \item{effect.size.text}{a character specifying the relevant effect size. For example, for \code{Cohens d} statistic, \code{effect.size.text = "d"}. You can also use plotmath expression as follow \code{quote(italic("d"))}.} } \value{ a text label or an expression to pass to a plotting function. } \description{ Extracts label information from statistical tests. Useful for labelling plots with test outputs. } \section{Functions}{ \itemize{ \item \code{get_pwc_label}: Extract label from pairwise comparisons. \item \code{get_test_label}: Extract labels for statistical tests. \item \code{create_test_label}: Create labels from user specified test results. \item \code{get_n}: Extracts sample counts (n) from an rstatix test outputs. Returns a numeric vector. \item \code{get_description}: Extracts the description of an rstatix test outputs. Returns a character vector. }} \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # One-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: anov <- df \%>\% anova_test(len ~ dose) get_test_label(anov, detailed = TRUE, type = "text") # Two-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: anov <- df \%>\% anova_test(len ~ supp*dose) get_test_label(anov, detailed = TRUE, type = "text", description = "Two Way ANOVA") # Kruskal-Wallis test #::::::::::::::::::::::::::::::::::::::::: kruskal<- df \%>\% kruskal_test(len ~ dose) get_test_label(kruskal, detailed = TRUE, type = "text") # Wilcoxon test #::::::::::::::::::::::::::::::::::::::::: # Unpaired test wilcox <- df \%>\% wilcox_test(len ~ supp) get_test_label(wilcox, detailed = TRUE, type = "text") # Paired test wilcox <- df \%>\% wilcox_test(len ~ supp, paired = TRUE) get_test_label(wilcox, detailed = TRUE, type = "text") # T test #::::::::::::::::::::::::::::::::::::::::: ttest <- df \%>\% t_test(len ~ dose) get_test_label(ttest, detailed = TRUE, type = "text") # Pairwise comparisons labels #::::::::::::::::::::::::::::::::::::::::: get_pwc_label(ttest, type = "text") # Create test labels #::::::::::::::::::::::::::::::::::::::::: create_test_label( statistic.text = "F", statistic = 71.82, parameter = "4, 294", p = "<0.0001", description = "ANOVA", type = "text" ) # Extract infos #::::::::::::::::::::::::::::::::::::::::: stat.test <- df \%>\% t_test(len ~ dose) get_n(stat.test) get_description(stat.test) } rstatix/man/cor_mat.Rd0000644000176200001440000000640713640215135014445 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_mat.R \name{cor_mat} \alias{cor_mat} \alias{cor_pmat} \alias{cor_get_pval} \title{Compute Correlation Matrix with P-values} \usage{ cor_mat( data, ..., vars = NULL, method = "pearson", alternative = "two.sided", conf.level = 0.95 ) cor_pmat( data, ..., vars = NULL, method = "pearson", alternative = "two.sided", conf.level = 0.95 ) cor_get_pval(x) } \arguments{ \item{data}{a data.frame containing the variables.} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest.} \item{vars}{a character vector containing the variable names of interest.} \item{method}{a character string indicating which correlation coefficient is to be used for the test. One of \code{"pearson"}, \code{"kendall"}, or \code{"spearman"}, can be abbreviated.} \item{alternative}{indicates the alternative hypothesis and must be one of \code{"two.sided"}, \code{"greater"} or \code{"less"}. You can specify just the initial letter. \code{"greater"} corresponds to positive association, \code{"less"} to negative association.} \item{conf.level}{confidence level for the returned confidence interval. Currently only used for the Pearson product moment correlation coefficient if there are at least 4 complete pairs of observations.} \item{x}{an object of class \code{cor_mat}} } \value{ a data frame } \description{ Compute correlation matrix with p-values. Numeric columns in the data are detected and automatically selected for the analysis. You can also specify variables of interest to be used in the correlation analysis. } \section{Functions}{ \itemize{ \item \code{cor_mat}: compute correlation matrix with p-values. Returns a data frame containing the matrix of the correlation coefficients. The output has an attribute named "pvalue", which contains the matrix of the correlation test p-values. \item \code{cor_pmat}: compute the correlation matrix but returns only the p-values of the tests. \item \code{cor_get_pval}: extract a correlation matrix p-values from an object of class \code{cor_mat()}. }} \examples{ # Data preparation #::::::::::::::::::::::::::::::::::::::::::: mydata <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) head(mydata, 3) # Compute correlation matrix #:::::::::::::::::::::::::::::::::::::::::: # Correlation matrix between all variables cor.mat <- mydata \%>\% cor_mat() cor.mat # Specify some variables of interest mydata \%>\% cor_mat(mpg, hp, wt) # Or remove some variables in the data # before the analysis mydata \%>\% cor_mat(-mpg, -hp) # Significance levels #:::::::::::::::::::::::::::::::::::::::::: cor.mat \%>\% cor_get_pval() # Visualize #:::::::::::::::::::::::::::::::::::::::::: # Insignificant correlations are marked by crosses cor.mat \%>\% cor_reorder() \%>\% pull_lower_triangle() \%>\% cor_plot(label = TRUE) # Gather/collapse correlation matrix into long format #:::::::::::::::::::::::::::::::::::::::::: cor.mat \%>\% cor_gather() } \seealso{ \code{\link{cor_test}()}, \code{\link{cor_reorder}()}, \code{\link{cor_gather}()}, \code{\link{cor_select}()}, \code{\link{cor_as_symbols}()}, \code{\link{pull_triangle}()}, \code{\link{replace_triangle}()} } rstatix/man/get_summary_stats.Rd0000644000176200001440000000500613640215135016565 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/get_summary_stats.R \name{get_summary_stats} \alias{get_summary_stats} \title{Compute Summary Statistics} \usage{ get_summary_stats( data, ..., type = c("full", "common", "robust", "five_number", "mean_sd", "mean_se", "mean_ci", "median_iqr", "median_mad", "quantile", "mean", "median", "min", "max"), show = NULL, probs = seq(0, 1, 0.25) ) } \arguments{ \item{data}{a data frame} \item{...}{(optional) One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest. If no variable is specified, then the summary statistics of all numeric variables in the data frame is computed.} \item{type}{type of summary statistics. Possible values include: \code{"full", "common", "robust", "five_number", "mean_sd", "mean_se", "mean_ci", "median_iqr", "median_mad", "quantile", "mean", "median", "min", "max"}} \item{show}{a character vector specifying the summary statistics you want to show. Example: \code{show = c("n", "mean", "sd")}. This is used to filter the output after computation.} \item{probs}{numeric vector of probabilities with values in [0,1]. Used only when type = "quantile".} } \value{ A data frame containing descriptive statistics, such as: \itemize{ \item \strong{n}: the number of individuals \item \strong{min}: minimum \item \strong{max}: maximum \item \strong{median}: median \item \strong{mean}: mean \item \strong{q1, q3}: the first and the third quartile, respectively. \item \strong{iqr}: interquartile range \item \strong{mad}: median absolute deviation (see ?MAD) \item \strong{sd}: standard deviation of the mean \item \strong{se}: standard error of the mean \item \strong{ci}: 95 percent confidence interval of the mean } } \description{ Compute summary statistics for one or multiple numeric variables. } \examples{ # Full summary statistics data("ToothGrowth") ToothGrowth \%>\% get_summary_stats(len) # Summary statistics of grouped data # Show only common summary ToothGrowth \%>\% group_by(dose, supp) \%>\% get_summary_stats(len, type = "common") # Robust summary statistics ToothGrowth \%>\% get_summary_stats(len, type = "robust") # Five number summary statistics ToothGrowth \%>\% get_summary_stats(len, type = "five_number") # Compute only mean and sd ToothGrowth \%>\% get_summary_stats(len, type = "mean_sd") # Compute full summary statistics but show only mean, sd, median, iqr ToothGrowth \%>\% get_summary_stats(len, show = c("mean", "sd", "median", "iqr")) } rstatix/man/df_arrange.Rd0000644000176200001440000000205113641034256015104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_arrange} \alias{df_arrange} \title{Arrange Rows by Column Values} \usage{ df_arrange(data, ..., vars = NULL, .by_group = FALSE) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest. Use \code{\link[dplyr]{desc}()} to sort a variable in descending order.} \item{vars}{a character vector containing the variable names of interest.} \item{.by_group}{If TRUE, will sort first by grouping variable. Applies to grouped data frames only.} } \value{ a data frame } \description{ Order the rows of a data frame by values of specified columns. Wrapper arround the \code{\link[dplyr]{arrange}()} function. Supports standard and non standard evaluation. } \examples{ df <- head(ToothGrowth) df # Select column using standard evaluation df \%>\% df_arrange(vars = c("dose", "len")) # Select column using non-standard evaluation df \%>\% df_arrange(dose, desc(len)) } rstatix/man/df_nest_by.Rd0000644000176200001440000000163313640452737015144 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_nest_by} \alias{df_nest_by} \title{Nest a Tibble By Groups} \usage{ df_nest_by(data, ..., vars = NULL) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used as grouping variables.} \item{vars}{a character vector containing the grouping variables of interest.} } \value{ A tbl with one row per unique combination of the grouping variables. The first columns are the grouping variables, followed by a list column of tibbles with matching rows of the remaining columns. } \description{ Nest a tibble data frame using grouping specification. Supports standard and non standard evaluation. } \examples{ # Non standard evaluation ToothGrowth \%>\% df_nest_by(dose, supp) # Standard evaluation ToothGrowth \%>\% df_nest_by(vars = c("dose", "supp")) } rstatix/man/cor_test.Rd0000644000176200001440000001073313640215135014640 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_test.R \name{cor_test} \alias{cor_test} \title{Correlation Test} \usage{ cor_test( data, ..., vars = NULL, vars2 = NULL, alternative = "two.sided", method = "pearson", conf.level = 0.95, use = "pairwise.complete.obs" ) } \arguments{ \item{data}{a data.frame containing the variables.} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest. Alternative to the argument \code{vars}.} \item{vars}{optional character vector containing variable names for correlation analysis. Ignored when dot vars are specified. \itemize{ \item If \code{vars} is NULL, multiple pairwise correlation tests is performed between all variables in the data. \item If \code{vars} contain only one variable, a pairwise correlation analysis is performed between the specified variable vs either all the remaining numeric variables in the data or variables in \code{vars2} (if specified). \item If \code{vars} contain two or more variables: i) if \code{vars2} is not specified, a pairwise correlation analysis is performed between all possible combinations of variables. ii) if \code{vars2} is specified, each element in \code{vars} is tested against all elements in \code{vars2}}. Accept unquoted variable names: \code{c(var1, var2)}.} \item{vars2}{optional character vector. If specified, each element in \code{vars} is tested against all elements in \code{vars2}. Accept unquoted variable names: \code{c(var1, var2)}.} \item{alternative}{indicates the alternative hypothesis and must be one of \code{"two.sided"}, \code{"greater"} or \code{"less"}. You can specify just the initial letter. \code{"greater"} corresponds to positive association, \code{"less"} to negative association.} \item{method}{a character string indicating which correlation coefficient is to be used for the test. One of \code{"pearson"}, \code{"kendall"}, or \code{"spearman"}, can be abbreviated.} \item{conf.level}{confidence level for the returned confidence interval. Currently only used for the Pearson product moment correlation coefficient if there are at least 4 complete pairs of observations.} \item{use}{an optional character string giving a method for computing covariances in the presence of missing values. This must be (an abbreviation of) one of the strings \code{"everything"}, \code{"all.obs"}, \code{"complete.obs"}, \code{"na.or.complete"}, or \code{"pairwise.complete.obs"}.} } \value{ return a data frame with the following columns: \itemize{ \item \code{var1, var2}: the variables used in the correlation test. \item \code{cor}: the correlation coefficient. \item \code{statistic}: Test statistic used to compute the p-value. \item \code{p}: p-value. \item \code{conf.low,conf.high}: Lower and upper bounds on a confidence interval. \item \code{method}: the method used to compute the statistic.} } \description{ Provides a pipe-friendly framework to perform correlation test between paired samples, using Pearson, Kendall or Spearman method. Wrapper around the function \code{\link[stats]{cor.test}()}. Can also performs multiple pairwise correlation analyses between more than two variables or between two different vectors of variables. Using this function, you can also compute, for example, the correlation between one variable vs many. } \section{Functions}{ \itemize{ \item \code{cor_test}: correlation test between two or more variables. }} \examples{ # Correlation between the specified variable vs # the remaining numeric variables in the data #::::::::::::::::::::::::::::::::::::::::: mtcars \%>\% cor_test(mpg) # Correlation test between two variables #::::::::::::::::::::::::::::::::::::::::: mtcars \%>\% cor_test(wt, mpg) # Pairwise correlation between multiple variables #::::::::::::::::::::::::::::::::::::::::: mtcars \%>\% cor_test(wt, mpg, disp) # Grouped data #::::::::::::::::::::::::::::::::::::::::: iris \%>\% group_by(Species) \%>\% cor_test(Sepal.Width, Sepal.Length) # Multiple correlation test #::::::::::::::::::::::::::::::::::::::::: # Correlation between one variable vs many mtcars \%>\% cor_test( vars = "mpg", vars2 = c("disp", "hp", "drat") ) # Correlation between two vectors of variables # Each element in vars is tested against all elements in vars2 mtcars \%>\% cor_test( vars = c("mpg", "wt"), vars2 = c("disp", "hp", "drat") ) } \seealso{ \code{\link{cor_mat}()}, \code{\link{as_cor_mat}()} } rstatix/man/cor_mark_significant.Rd0000644000176200001440000000160613640215135017170 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_mark_significant.R \name{cor_mark_significant} \alias{cor_mark_significant} \title{Add Significance Levels To a Correlation Matrix} \usage{ cor_mark_significant( x, cutpoints = c(0, 1e-04, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "") ) } \arguments{ \item{x}{an object of class \code{\link{cor_mat}()}.} \item{cutpoints}{numeric vector used for intervals.} \item{symbols}{character vector, one shorter than cutpoints, used as significance symbols.} } \value{ a data frame containing the lower triangular part of the correlation matrix marked by significance symbols. } \description{ Combines correlation coefficients and significance levels in a correlation matrix data. } \examples{ mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_mat() \%>\% cor_mark_significant() } rstatix/man/remove_ns.Rd0000644000176200001440000000232413736604721015021 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/remove_ns.R \name{remove_ns} \alias{remove_ns} \title{Remove Non-Significant from Statistical Tests} \usage{ remove_ns(stat.test, col = NULL, signif.cutoff = 0.05) } \arguments{ \item{stat.test}{statistical test results returned by \code{rstatix} functions or any data frame containing a p-value column.} \item{col}{(optional) character specifying the column containing the p-value or the significance information, to be used for the filtering step. Possible values include: \code{"p"}, \code{"p.adj"}, \code{"p.signif"}, \code{"p.adj.signif"}. If missing, the function will automatically look for p.adj.signif, p.adj, p.signif, p in this order.} \item{signif.cutoff}{the significance cutoff; default is 0.05. Significance is declared at \code{p-value <= signif.cutoff}} } \value{ a data frame } \description{ Filter out non-significant (NS) p-values from a statistical test. Can detect automatically p-value columns } \examples{ # Statistical test stat.test <- PlantGrowth \%>\% wilcox_test(weight ~ group) # Remove ns: automatic detection of p-value columns stat.test \%>\% remove_ns() # Remove ns by the column p stat.test \%>\% remove_ns(col ="p") } rstatix/man/p_value.Rd0000644000176200001440000000727413640215135014457 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/p_value.R \name{p_round} \alias{p_round} \alias{p_format} \alias{p_mark_significant} \alias{p_detect} \alias{p_names} \alias{p_adj_names} \title{Rounding and Formatting p-values} \usage{ p_round(x, ..., digits = 3) p_format( x, ..., new.col = FALSE, digits = 2, accuracy = 1e-04, decimal.mark = ".", leading.zero = TRUE, trailing.zero = FALSE, add.p = FALSE, space = FALSE ) p_mark_significant( x, ..., new.col = FALSE, cutpoints = c(0, 1e-04, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "") ) p_detect(data, type = c("all", "p", "p.adj")) p_names() p_adj_names() } \arguments{ \item{x}{a numeric vector of p-values or a data frame containing a p value column. If data frame, the p-value column(s) will be automatically detected. Known p-value column names can be obtained using the functions \code{p_names()} and \code{p_adj_names()}} \item{...}{column names to manipulate in the case where \code{x} is a data frame. P value columns are automatically detected if not specified.} \item{digits}{the number of significant digits to be used.} \item{new.col}{logical, used only when \code{x} is a data frame. If TRUE, add a new column to hold the results. The new column name is created by adding, to the p column, the suffix "format" (for \code{p_format()}), "signif" (for \code{p_mak_significant()}).} \item{accuracy}{number to round to, that is the threshold value above wich the function will replace the pvalue by "<0.0xxx".} \item{decimal.mark}{the character to be used to indicate the numeric decimal point.} \item{leading.zero}{logical. If FALSE, remove the leading zero.} \item{trailing.zero}{logical. If FALSE (default), remove the training extra zero.} \item{add.p}{logical value. If TRUE, add "p=" before the value.} \item{space}{logical. If TRUE (default) use space as separator between different elements and symbols.} \item{cutpoints}{numeric vector used for intervals} \item{symbols}{character vector, one shorter than cutpoints, used as significance symbols.} \item{data}{a data frame} \item{type}{the type of p-value to detect. Can be one of \code{c("all", "p", "p.adj")}.} } \value{ a vector or a data frame containing the rounded/formatted p-values. } \description{ Round and format p-values. Can also mark significant p-values with stars. } \section{Functions}{ \itemize{ \item \code{p_round}: round p-values \item \code{p_format}: format p-values. Add a symbol "<" for small p-values. \item \code{p_mark_significant}: mark p-values with significance levels \item \code{p_detect}: detects and returns p-value column names in a data frame. \item \code{p_names}: returns known p-value column names \item \code{p_adj_names}: returns known adjust p-value column names }} \examples{ # Round and format a vector of p-values # :::::::::::::::::::::::::::::::::::::::::::: # Format p <- c(0.5678, 0.127, 0.045, 0.011, 0.009, 0.00002, NA) p_format(p) # Specify the accuracy p_format(p, accuracy = 0.01) # Add p and remove the leading zero p_format(p, add.p = TRUE, leading.zero = FALSE) # Remove space before and after "=" or "<". p_format(p, add.p = TRUE, leading.zero = FALSE, space = FALSE) # Mark significant p-values # :::::::::::::::::::::::::::::::::::::::::::: p_mark_significant(p) # Round, the mark significant p \%>\% p_round(digits = 2) \%>\% p_mark_significant() # Format, then mark significant p \%>\% p_format(digits = 2) \%>\% p_mark_significant() # Perform stat test, format p and mark significant # :::::::::::::::::::::::::::::::::::::::::::: ToothGrowth \%>\% group_by(dose) \%>\% t_test(len ~ supp) \%>\% p_format(digits = 2, leading.zero = FALSE) \%>\% p_mark_significant() } rstatix/man/freq_table.Rd0000644000176200001440000000131213651776427015135 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/freq_table.R \name{freq_table} \alias{freq_table} \title{Compute Frequency Table} \usage{ freq_table(data, ..., vars = NULL, na.rm = TRUE) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to specify variables of interest.} \item{vars}{optional character vector containing variable names.} \item{na.rm}{logical value. If TRUE (default), remove missing values in the variables used to create the frequency table.} } \value{ a data frame } \description{ compute frequency table. } \examples{ data("ToothGrowth") ToothGrowth \%>\% freq_table(supp, dose) } rstatix/man/emmeans_test.Rd0000644000176200001440000000752713640215135015511 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/emmeans_test.R \name{emmeans_test} \alias{emmeans_test} \alias{get_emmeans} \title{Pairwise Comparisons of Estimated Marginal Means} \usage{ emmeans_test( data, formula, covariate = NULL, ref.group = NULL, comparisons = NULL, p.adjust.method = "bonferroni", conf.level = 0.95, model = NULL, detailed = FALSE ) get_emmeans(emmeans.test) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{covariate}{(optional) covariate names (for ANCOVA)} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group). If \code{ref.group = "all"}, pairwise two sample tests are performed for comparing each grouping variable levels against all (i.e. basemean).} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{conf.level}{confidence level of the interval.} \item{model}{a fitted-model objects such as the result of a call to \code{lm()}, from which the overall degrees of freedom are to be calculated.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{emmeans.test}{an object of class \code{emmeans_test}.} } \value{ return a data frame with some the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{statistic}: Test statistic (t.ratio) used to compute the p-value. \item \code{df}: degrees of freedom. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the statistical test used to compare groups. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: estimate of the effect size, that is the difference between the two emmeans (estimated marginal means). \item \code{conf.low,conf.high}: Lower and upper bound on a confidence interval of the estimate. } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. It has also an attribute named "emmeans", a data frame containing the groups emmeans. } \description{ Performs pairwise comparisons between groups using the estimated marginal means. Pipe-friendly wrapper arround the functions \code{emmans() + contrast()} from the \code{emmeans} package, which need to be installed before using this function. This function is useful for performing post-hoc analyses following ANOVA/ANCOVA tests. } \section{Functions}{ \itemize{ \item \code{get_emmeans}: returns the estimated marginal means from an object of class \code{emmeans_test} }} \examples{ # Data preparation df <- ToothGrowth df$dose <- as.factor(df$dose) # Pairwise comparisons res <- df \%>\% group_by(supp) \%>\% emmeans_test(len ~ dose, p.adjust.method = "bonferroni") res # Display estimated marginal means attr(res, "emmeans") # Show details df \%>\% group_by(supp) \%>\% emmeans_test(len ~ dose, p.adjust.method = "bonferroni", detailed = TRUE) } rstatix/man/cor_reshape.Rd0000644000176200001440000000357114011715273015313 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_reshape.R \name{cor_gather} \alias{cor_gather} \alias{cor_spread} \title{Reshape Correlation Data} \usage{ cor_gather(data, drop.na = TRUE) cor_spread(data, value = "cor") } \arguments{ \item{data}{a data frame or matrix.} \item{drop.na}{logical. If TRUE, drop rows containing missing values after gathering the data.} \item{value}{column name containing the value to spread.} } \description{ Reshape correlation analysis results. Key functions: \itemize{ \item \code{cor_gather()}: takes a correlation matrix and collapses (i.e. melt) it into a paired list (long format). \item \code{cor_spread()}: spread a long correlation data format across multiple columns. Particularly, it takes the results of \code{\link{cor_test}} and transforms it into a correlation matrix. } } \section{Functions}{ \itemize{ \item \code{cor_gather}: takes a correlation matrix and collapses (or melt) it into long format data frame (paired list) \item \code{cor_spread}: spread a long correlation data frame into wide format. Expects the columns "var1", "var2" and "cor" in the data. (correlation matrix). }} \examples{ # Data preparation #:::::::::::::::::::::::::::::::::::::::::: mydata <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) head(mydata, 3) # Reshape a correlation matrix #:::::::::::::::::::::::::::::::::::::::::: # Compute a correlation matrix cor.mat <- mydata \%>\% cor_mat() cor.mat # Collapse the correlation matrix into long format # paired list data frame long.format <- cor.mat \%>\% cor_gather() long.format # Spread a correlation data format #:::::::::::::::::::::::::::::::::::::::::: # Spread the correlation coefficient value long.format \%>\% cor_spread(value = "cor") # Spread the p-value long.format \%>\% cor_spread(value = "p") } \seealso{ \code{\link{cor_mat}()}, \code{\link{cor_reorder}()} } rstatix/man/tukey_hsd.Rd0000644000176200001440000000511613520743305015016 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tukey_hsd.R \name{tukey_hsd} \alias{tukey_hsd} \alias{tukey_hsd.default} \alias{tukey_hsd.lm} \alias{tukey_hsd.data.frame} \title{Tukey Honest Significant Differences} \usage{ tukey_hsd(x, ...) \method{tukey_hsd}{default}(x, ...) \method{tukey_hsd}{lm}(x, ...) \method{tukey_hsd}{data.frame}(x, formula, ...) } \arguments{ \item{x}{an object of class \code{aov}, \code{lm} or \code{data.frame} containing the variables used in the formula.} \item{...}{other arguments passed to the function \code{\link[stats]{TukeyHSD}()}. These include: \itemize{ \item \strong{which}: A character vector listing terms in the fitted model for which the intervals should be calculated. Defaults to all the terms. \item \strong{ordered}: A logical value indicating if the levels of the factor should be ordered according to increasing average in the sample before taking differences. If ordered is true then the calculated differences in the means will all be positive. The significant differences will be those for which the lwr end point is positive. }} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{data}{a data.frame containing the variables in the formula.} } \value{ a tibble data frame containing the results of the different comparisons. } \description{ Provides a pipe-friendly framework to performs Tukey post-hoc tests. Wrapper around the function \code{\link[stats]{TukeyHSD}()}. It is essentially a t-test that corrects for multiple testing. Can handle different inputs formats: aov, lm, formula. } \section{Methods (by class)}{ \itemize{ \item \code{default}: performs tukey post-hoc test from \code{aov()} results. \item \code{lm}: performs tukey post-hoc test from \code{lm()} model. \item \code{data.frame}: performs tukey post-hoc tests using data and formula as inputs. ANOVA will be automatically performed using the function \code{\link[stats]{aov}()} }} \examples{ # Data preparation df <- ToothGrowth df$dose <- as.factor(df$dose) # Tukey HSD from ANOVA results aov(len ~ dose, data = df) \%>\% tukey_hsd() # two-way anova with interaction aov(len ~ dose*supp, data = df) \%>\% tukey_hsd() # Tukey HSD from lm() results lm(len ~ dose, data = df) \%>\% tukey_hsd() # Tukey HSD from data frame and formula tukey_hsd(df, len ~ dose) # Tukey HSD using grouped data df \%>\% group_by(supp) \%>\% tukey_hsd(len ~ dose) } rstatix/man/df_select.Rd0000644000176200001440000000155413640733363014757 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_select} \alias{df_select} \title{Select Columns in a Data Frame} \usage{ df_select(data, ..., vars = NULL) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest.} \item{vars}{a character vector containing the variable names of interest.} } \value{ a data frame } \description{ A wrapper around the \code{\link[dplyr]{select}()} function for selection data frame columns. Supports standard and non standard evaluations. Usefull to easily program with \code{dplyr} } \examples{ df <- head(ToothGrowth) df # Select column using standard evaluation df \%>\% df_select(vars = c("dose", "len")) # Select column using non-standard evaluation df \%>\% df_select(dose, len) } rstatix/man/anova_summary.Rd0000644000176200001440000001116713666532256015717 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/anova_summary.R \name{anova_summary} \alias{anova_summary} \title{Create Nice Summary Tables of ANOVA Results} \usage{ anova_summary(object, effect.size = "ges", detailed = FALSE, observed = NULL) } \arguments{ \item{object}{an object of returned by either \code{\link[car]{Anova}()}, or \code{\link[stats]{aov}()}.} \item{effect.size}{the effect size to compute and to show in the ANOVA results. Allowed values can be either "ges" (generalized eta squared) or "pes" (partial eta squared) or both. Default is "ges".} \item{detailed}{If TRUE, returns extra information (sums of squares columns, intercept row, etc.) in the ANOVA table.} \item{observed}{Variables that are observed (i.e, measured) as compared to experimentally manipulated. The default effect size reported (generalized eta-squared) requires correct specification of the observed variables.} } \value{ return an object of class \code{anova_test} a data frame containing the ANOVA table for independent measures ANOVA. However, for repeated/mixed measures ANOVA, it is a list containing the following components are returned: \itemize{ \item \strong{ANOVA}: a data frame containing ANOVA results \item \strong{Mauchly's Test for Sphericity}: If any within-Ss variables with more than 2 levels are present, a data frame containing the results of Mauchly's test for Sphericity. Only reported for effects that have more than 2 levels because sphericity necessarily holds for effects with only 2 levels. \item \strong{Sphericity Corrections}: If any within-Ss variables are present, a data frame containing the Greenhouse-Geisser and Huynh-Feldt epsilon values, and corresponding corrected p-values. } The \strong{returned object might have an attribute} called \code{args} if you compute ANOVA using the function \code{\link{anova_test}()}. The attribute \code{args} is a list holding the arguments used to fit the ANOVA model, including: data, dv, within, between, type, model, etc. The following abbreviations are used in the different results tables: \itemize{ \item DFn Degrees of Freedom in the numerator (i.e. DF effect). \item DFd Degrees of Freedom in the denominator (i.e., DF error). \item SSn Sum of Squares in the numerator (i.e., SS effect). \item SSd Sum of Squares in the denominator (i.e.,SS error). \item F F-value. \item p p-value (probability of the data given the null hypothesis). \item p<.05 Highlights p-values less than the traditional alpha level of .05. \item ges Generalized Eta-Squared measure of effect size. \item GGe Greenhouse-Geisser epsilon. \item p[GGe] p-value after correction using Greenhouse-Geisser epsilon. \item p[GGe]<.05 Highlights p-values (after correction using Greenhouse-Geisser epsilon) less than the traditional alpha level of .05. \item HFe Huynh-Feldt epsilon. \item p[HFe] p-value after correction using Huynh-Feldt epsilon. \item p[HFe]<.05 Highlights p-values (after correction using Huynh-Feldt epsilon) less than the traditional alpha level of .05. \item W Mauchly's W statistic } } \description{ Create beautiful summary tables of ANOVA test results obtained from either \code{\link[car]{Anova}()} or \code{\link[stats]{aov}()}. The results include ANOVA table, generalized effect size and some assumption checks. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth df$dose <- as.factor(df$dose) # Independent measures ANOVA #::::::::::::::::::::::::::::::::::::::::: # Compute ANOVA and display the summary res.anova <- Anova(lm(len ~ dose*supp, data = df)) anova_summary(res.anova) # Display both SSn and SSd using detailed = TRUE # Show generalized eta squared using effect.size = "ges" anova_summary(res.anova, detailed = TRUE, effect.size = "ges") # Show partial eta squared using effect.size = "pes" anova_summary(res.anova, detailed = TRUE, effect.size = "pes") # Repeated measures designs using car::Anova() #::::::::::::::::::::::::::::::::::::::::: # Prepare the data df$id <- as.factor(rep(1:10, 6)) # Add individuals ids head(df) # Easily perform repeated measures ANOVA using the car package design <- factorial_design(df, dv = len, wid = id, within = c(supp, dose)) res.anova <- Anova(design$model, idata = design$idata, idesign = design$idesign, type = 3) anova_summary(res.anova) # Repeated measures designs using stats::Aov() #::::::::::::::::::::::::::::::::::::::::: res.anova <- aov(len ~ dose*supp + Error(id/(supp*dose)), data = df) anova_summary(res.anova) } \seealso{ \code{\link{anova_test}()}, \code{\link{factorial_design}()} } \author{ Alboukadel Kassambara, \email{alboukadel.kassambara@gmail.com} } rstatix/man/df_get_var_names.Rd0000644000176200001440000000140113640733363016301 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_get_var_names} \alias{df_get_var_names} \title{Get User Specified Variable Names} \usage{ df_get_var_names(data, ..., vars = NULL) } \arguments{ \item{data}{a data frame} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest.} \item{vars}{a character vector containing the variable names of interest.} } \value{ a character vector } \description{ Returns user specified variable names. Supports standard and non standard evaluation. } \examples{ # Non standard evaluation ToothGrowth \%>\% df_get_var_names(dose, len) # Standard evaluation ToothGrowth \%>\% df_get_var_names(vars = c("len", "dose")) } rstatix/man/t_test.Rd0000644000176200001440000001420613640215135014317 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/t_test.R \name{t_test} \alias{t_test} \alias{pairwise_t_test} \title{T-test} \usage{ t_test( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", paired = FALSE, var.equal = FALSE, alternative = "two.sided", mu = 0, conf.level = 0.95, detailed = FALSE ) pairwise_t_test( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", paired = FALSE, pool.sd = !paired, detailed = FALSE, ... ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group). If \code{ref.group = "all"}, pairwise two sample tests are performed for comparing each grouping variable levels against all (i.e. basemean).} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{paired}{a logical indicating whether you want a paired test.} \item{var.equal}{a logical variable indicating whether to treat the two variances as being equal. If \code{TRUE} then the pooled variance is used to estimate the variance otherwise the Welch (or Satterthwaite) approximation to the degrees of freedom is used.} \item{alternative}{a character string specifying the alternative hypothesis, must be one of \code{"two.sided"} (default), \code{"greater"} or \code{"less"}. You can specify just the initial letter.} \item{mu}{a number specifying an optional parameter used to form the null hypothesis.} \item{conf.level}{confidence level of the interval.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{pool.sd}{logical value used in the function \code{pairwise_t_test()}. Switch to allow/disallow the use of a pooled SD. The \code{pool.sd = TRUE} (default) calculates a common SD for all groups and uses that for all comparisons (this can be useful if some groups are small). This method does not actually call t.test, so extra arguments are ignored. Pooling does not generalize to paired tests so pool.sd and paired cannot both be TRUE. If \code{pool.sd = FALSE} the standard two sample t-test is applied to all possible pairs of groups. This method calls the \code{t.test()}, so extra arguments, such as \code{var.equal} are accepted.} \item{...}{other arguments to be passed to the function \code{\link[stats]{t.test}}.} } \value{ return a data frame with some the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. \item \code{statistic}: Test statistic used to compute the p-value. \item \code{df}: degrees of freedom. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the statistical test used to compare groups. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: estimate of the effect size. It corresponds to the estimated mean or difference in means depending on whether it was a one-sample test or a two-sample test. \item \code{estimate1, estimate2}: show the mean values of the two groups, respectively, for independent samples t-tests. \item \code{alternative}: a character string describing the alternative hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a confidence interval. } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Provides a pipe-friendly framework to performs one and two sample t-tests. Read more: \href{https://www.datanovia.com/en/lessons/t-test-in-r/}{T-test in R}. } \details{ - If a list of comparisons is specified, the result of the pairwise tests is filtered to keep only the comparisons of interest. The p-value is adjusted after filtering. - For a grouped data, if pairwise test is performed, then the p-values are adjusted for each group level independently. } \section{Functions}{ \itemize{ \item \code{t_test}: t test \item \code{pairwise_t_test}: performs pairwise two sample t-test. Wrapper around the R base function \code{\link[stats]{pairwise.t.test}}. }} \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # One-sample test #::::::::::::::::::::::::::::::::::::::::: df \%>\% t_test(len ~ 1, mu = 0) # Two-samples unpaired test #::::::::::::::::::::::::::::::::::::::::: df \%>\% t_test(len ~ supp) # Two-samples paired test #::::::::::::::::::::::::::::::::::::::::: df \%>\% t_test (len ~ supp, paired = TRUE) # Compare supp levels after grouping the data by "dose" #:::::::::::::::::::::::::::::::::::::::: df \%>\% group_by(dose) \%>\% t_test(data =., len ~ supp) \%>\% adjust_pvalue(method = "bonferroni") \%>\% add_significance("p.adj") # pairwise comparisons #:::::::::::::::::::::::::::::::::::::::: # As dose contains more than two levels ==> # pairwise test is automatically performed. df \%>\% t_test(len ~ dose) # Comparison against reference group #:::::::::::::::::::::::::::::::::::::::: # each level is compared to the ref group df \%>\% t_test(len ~ dose, ref.group = "0.5") # Comparison against all #:::::::::::::::::::::::::::::::::::::::: df \%>\% t_test(len ~ dose, ref.group = "all") } rstatix/man/wilcox_effsize.Rd0000644000176200001440000001047113654501434016042 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wilcox_effsize.R \name{wilcox_effsize} \alias{wilcox_effsize} \title{Wilcoxon Effect Size} \usage{ wilcox_effsize( data, formula, comparisons = NULL, ref.group = NULL, paired = FALSE, alternative = "two.sided", mu = 0, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ... ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group). If \code{ref.group = "all"}, pairwise two sample tests are performed for comparing each grouping variable levels against all (i.e. basemean).} \item{paired}{a logical indicating whether you want a paired test.} \item{alternative}{a character string specifying the alternative hypothesis, must be one of \code{"two.sided"} (default), \code{"greater"} or \code{"less"}. You can specify just the initial letter.} \item{mu}{a number specifying an optional parameter used to form the null hypothesis.} \item{ci}{If TRUE, returns confidence intervals by bootstrap. May be slow.} \item{conf.level}{The level for the confidence interval.} \item{ci.type}{The type of confidence interval to use. Can be any of "norm", "basic", "perc", or "bca". Passed to \code{boot::boot.ci}.} \item{nboot}{The number of replications to use for bootstrap.} \item{...}{Additional arguments passed to the functions \code{coin::wilcoxsign_test()} (case of one- or paired-samples test) or \code{coin::wilcox_test()} (case of independent two-samples test).} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. \item \code{effsize}: estimate of the effect size (\code{r} value). \item \code{magnitude}: magnitude of effect size. \item \code{conf.low,conf.high}: lower and upper bound of the effect size confidence interval.} } \description{ Compute Wilcoxon effect size (\code{r}) for: \itemize{ \item one-sample test (Wilcoxon one-sample signed-rank test); \item paired two-samples test (Wilcoxon two-sample paired signed-rank test) and \item independent two-samples test ( Mann-Whitney, two-sample rank-sum test). } It can also returns confidence intervals by bootstap. The effect size \code{r} is calculated as \code{Z} statistic divided by square root of the sample size (N) (\eqn{Z/\sqrt{N}}). The \code{Z} value is extracted from either \code{coin::wilcoxsign_test()} (case of one- or paired-samples test) or \code{coin::wilcox_test()} (case of independent two-samples test). Note that \code{N} corresponds to total sample size for independent samples test and to total number of pairs for paired samples test. The \code{r} value varies from 0 to close to 1. The interpretation values for r commonly in published litterature and on the internet are: \code{0.10 - < 0.3} (small effect), \code{0.30 - < 0.5} (moderate effect) and \code{>= 0.5} (large effect). } \examples{ if(require("coin")){ # One-sample Wilcoxon test effect size ToothGrowth \%>\% wilcox_effsize(len ~ 1, mu = 0) # Independent two-samples wilcoxon effect size ToothGrowth \%>\% wilcox_effsize(len ~ supp) # Paired-samples wilcoxon effect size ToothGrowth \%>\% wilcox_effsize(len ~ supp, paired = TRUE) # Pairwise comparisons ToothGrowth \%>\% wilcox_effsize(len ~ dose) # Grouped data ToothGrowth \%>\% group_by(supp) \%>\% wilcox_effsize(len ~ dose) } } \references{ Maciej Tomczak and Ewa Tomczak. The need to report effect size estimates revisited. An overview of some recommended measures of effect size. Trends in Sport Sciences. 2014; 1(21):19-25. } rstatix/man/get_pvalue_position.Rd0000644000176200001440000001311413735200646017077 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/get_pvalue_position.R \name{get_y_position} \alias{get_y_position} \alias{add_y_position} \alias{add_x_position} \alias{add_xy_position} \title{Autocompute P-value Positions For Plotting Significance} \usage{ get_y_position( data, formula, fun = "max", ref.group = NULL, comparisons = NULL, step.increase = 0.12, y.trans = NULL, stack = FALSE, scales = c("fixed", "free", "free_y") ) add_y_position( test, fun = "max", step.increase = 0.12, data = NULL, formula = NULL, ref.group = NULL, comparisons = NULL, y.trans = NULL, stack = FALSE, scales = c("fixed", "free", "free_y") ) add_x_position(test, x = NULL, group = NULL, dodge = 0.8) add_xy_position( test, x = NULL, group = NULL, dodge = 0.8, stack = FALSE, fun = "max", step.increase = 0.12, scales = c("fixed", "free", "free_y"), ... ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{fun}{summary statistics functions used to compute automatically suitable y positions of p-value labels and brackets. Possible values include: \code{"max", "mean", "mean_sd", "mean_se", "mean_ci", "median", "median_iqr", "median_mad"}. For example, if \code{fun = "max"}, the y positions are guessed as follow: \itemize{ \item 1. Compute the maximum of each group (groups.maximum) \item 2. Use the highest groups maximum as the first bracket y position \item 3. Add successively a step increase for remaining bracket y positions. } When the main plot is a boxplot, you need the option \code{fun = "max"}, to have the p-value bracket displayed at the maximum point of the group. In some situations the main plot is a line plot or a barplot showing the \code{mean+/-error bars} of the groups, where error can be SE (standard error), SD (standard deviation) or CI (confidence interval). In this case, to correctly compute the bracket y position you need the option \code{fun = "mean_se"}, etc.} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group).} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{step.increase}{numeric vector with the increase in fraction of total height for every additional comparison to minimize overlap.} \item{y.trans}{a function for transforming y axis scale. Value can be \code{log2}, \code{log10} and \code{sqrt}. Can be also any custom function that can take a numeric vector as input and returns a numeric vector, example: \code{y.trans = function(x){log2(x+1)}}} \item{stack}{logical. If TRUE, computes y position for a stacked plot. Useful when dealing with stacked bar plots.} \item{scales}{Should scales be fixed (\code{"fixed"}, the default), free (\code{"free"}), or free in one dimension (\code{"free_y"})?. This option is considered only when determining the y position. If the specified value is \code{"free"} or \code{"free_y"}, then the step increase of y positions will be calculated by plot panels. Note that, using \code{"free"} or \code{"free_y"} gives the same result. A global step increase is computed when \code{scales = "fixed"}.} \item{test}{an object of class \code{rstatix_test} as returned by \code{\link{t_test}()}, \code{\link{wilcox_test}()}, \code{\link{sign_test}()}, \code{\link{tukey_hsd}()}, \code{\link{dunn_test}()}.} \item{x}{variable on x axis.} \item{group}{group variable (legend variable).} \item{dodge}{dodge width for grouped ggplot/test. Default is 0.8. Used only when \code{x} specified.} \item{...}{other arguments to be passed to the function \code{\link[stats]{t.test}}.} } \description{ Compute p-value x and y positions for plotting significance levels. Many examples are provided at : \itemize{ \item \href{https://www.datanovia.com/en/blog/how-to-add-p-values-onto-a-grouped-ggplot-using-the-ggpubr-r-package/}{How to Add P-Values onto a Grouped GGPLOT using the GGPUBR R Package} \item \href{https://www.datanovia.com/en/blog/ggpubr-how-to-add-adjusted-p-values-to-a-multi-panel-ggplot/}{How to Add Adjusted P-values to a Multi-Panel GGPlot} \item \href{https://www.datanovia.com/en/blog/ggpubr-how-to-add-p-values-generated-elsewhere-to-a-ggplot/}{How to Add P-Values Generated Elsewhere to a GGPLOT} } } \section{Functions}{ \itemize{ \item \code{get_y_position}: compute the p-value y positions \item \code{add_y_position}: add p-value y positions to an object of class \code{rstatix_test} \item \code{add_x_position}: compute and add p-value x positions. \item \code{add_xy_position}: compute and add both x and y positions. }} \examples{ # Data preparation #:::::::::::::::::::::::::::::::::::: df <- ToothGrowth df$dose <- as.factor(df$dose) df$group <- factor(rep(c(1, 2), 30)) head(df) # Stat tests #:::::::::::::::::::::::::::::::::::: stat.test <- df \%>\% t_test(len ~ dose) stat.test # Add the test into box plots #:::::::::::::::::::::::::::::::::::: stat.test <- stat.test \%>\% add_y_position() \donttest{ if(require("ggpubr")){ ggboxplot(df, x = "dose", y = "len") + stat_pvalue_manual(stat.test, label = "p.adj.signif", tip.length = 0.01) } } } rstatix/man/mcnemar_test.Rd0000644000176200001440000000710113640215135015472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mcnemar_test.R \name{mcnemar_test} \alias{mcnemar_test} \alias{pairwise_mcnemar_test} \title{McNemar's Chi-squared Test for Count Data} \usage{ mcnemar_test(x, y = NULL, correct = TRUE) pairwise_mcnemar_test( data, formula, type = c("mcnemar", "exact"), correct = TRUE, p.adjust.method = "bonferroni" ) } \arguments{ \item{x}{either a two-dimensional contingency table in matrix form, or a factor object.} \item{y}{a factor object; ignored if \code{x} is a matrix.} \item{correct}{a logical indicating whether to apply continuity correction when computing the test statistic.} \item{data}{a data frame containing the variables in the formula.} \item{formula}{a formula of the form \code{a ~ b | c}, where \code{a} is the outcome variable name; b is the within-subjects factor variables; and c (factor) is the column name containing individuals/subjects identifier. Should be unique per individual.} \item{type}{type of statistical tests used for pairwise comparisons. Allowed values are one of \code{c("mcnemar", "exact")}.} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} } \value{ return a data frame with the following columns: \itemize{ \item \code{n}: the number of participants. \item \code{statistic}: the value of McNemar's statistic. \item \code{df} the degrees of freedom of the approximate chi-squared distribution of the test statistic. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the used statistical test. \item \code{p.signif}: the significance level of p-values.} The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs McNemar chi-squared test to compare paired proportions. Wrappers around the R base function \code{\link[stats]{mcnemar.test}()}, but provide pairwise comparisons between multiple groups } \section{Functions}{ \itemize{ \item \code{mcnemar_test}: performs McNemar's chi-squared test for comparing two paired proportions \item \code{pairwise_mcnemar_test}: performs pairwise McNemar's chi-squared test between multiple groups. Could be used for post-hoc tests following a significant Cochran's Q test. }} \examples{ # Comparing two paired proportions #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: frequencies of smokers before and after interventions xtab <- as.table( rbind(c(25, 6), c(21,10)) ) dimnames(xtab) <- list( before = c("non.smoker", "smoker"), after = c("non.smoker", "smoker") ) xtab # Compare the proportion of smokers mcnemar_test(xtab) # Comparing multiple related proportions # \%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Generate a demo data mydata <- data.frame( outcome = c(0,1,1,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,1,0,0,1), treatment = gl(3,1,30,labels=LETTERS[1:3]), participant = gl(10,3,labels=letters[1:10]) ) mydata$outcome <- factor( mydata$outcome, levels = c(1, 0), labels = c("success", "failure") ) # Cross-tabulation xtabs(~outcome + treatment, mydata) # Compare the proportion of success between treatments cochran_qtest(mydata, outcome ~ treatment|participant) # pairwise comparisons between groups pairwise_mcnemar_test(mydata, outcome ~ treatment|participant) } rstatix/man/cor_plot.Rd0000644000176200001440000001004413640215135014632 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_plot.R \name{cor_plot} \alias{cor_plot} \title{Visualize Correlation Matrix Using Base Plot} \usage{ cor_plot( cor.mat, method = "circle", type = "full", palette = NULL, p.mat = NULL, significant.level = 0.05, insignificant = c("cross", "blank"), label = FALSE, font.label = list() ) } \arguments{ \item{cor.mat}{the correlation matrix to visualize} \item{method}{Character, the visualization method of correlation matrix to be used. Currently, it supports seven methods, named \code{"circle"} (default), \code{"square"}, \code{"ellipse"}, \code{"number"}, \code{"pie"}, \code{"shade"} and \code{"color"}. See examples for details. The areas of circles or squares show the absolute value of corresponding correlation coefficients. Method \code{"pie"} and \code{"shade"} came from Michael Friendly's job (with some adjustment about the shade added on), and \code{"ellipse"} came from D.J. Murdoch and E.D. Chow's job, see in section References.} \item{type}{Character, \code{"full"} (default), \code{"upper"} or \code{"lower"}, display full matrix, lower triangular or upper triangular matrix.} \item{palette}{character vector containing the color palette.} \item{p.mat}{matrix of p-value corresponding to the correlation matrix.} \item{significant.level}{significant level, if the p-value is bigger than \code{significant.level}, then the corresponding correlation coefficient is regarded as insignificant.} \item{insignificant}{character, specialized insignificant correlation coefficients, "cross" (default), "blank". If "blank", wipe away the corresponding glyphs; if "cross", add crosses (X) on corresponding glyphs.} \item{label}{logical value. If TRUE, shows the correlation coefficient labels.} \item{font.label}{a list with one or more of the following elements: size (e.g., 1), color (e.g., "black") and style (e.g., "bold"). Used to customize the correlation coefficient labels. For example \code{font.label = list(size = 1, color = "black", style = "bold")}.} } \description{ Provide a tibble-friendly framework to visualize a correlation matrix. Wrapper around the R base function \code{\link[corrplot]{corrplot}()}. Compared to \code{\link[corrplot]{corrplot}()}, it can handle directly the output of the functions \code{\link{cor_mat}() (in rstatix)}, \code{rcorr() (in Hmisc)}, \code{correlate() (in corrr)} and \code{cor() (in stats)}. The p-values contained in the outputs of the functions \code{\link{cor_mat}()} and \code{rcorr()} are automatically detected and used in the visualization. } \examples{ # Compute correlation matrix #:::::::::::::::::::::::::::::::::::::::::: cor.mat <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_mat() # Visualize correlation matrix #:::::::::::::::::::::::::::::::::::::::::: # Full correlation matrix, # insignificant correlations are marked by crosses cor.mat \%>\% cor_plot() # Reorder by correlation coefficient # pull lower triangle and visualize cor.lower.tri <- cor.mat \%>\% cor_reorder() \%>\% pull_lower_triangle() cor.lower.tri \%>\% cor_plot() # Change visualization methods #:::::::::::::::::::::::::::::::::::::::::: cor.lower.tri \%>\% cor_plot(method = "pie") cor.lower.tri \%>\% cor_plot(method = "color") cor.lower.tri \%>\% cor_plot(method = "number") # Show the correlation coefficient: label = TRUE # Blank the insignificant correlation #:::::::::::::::::::::::::::::::::::::::::: cor.lower.tri \%>\% cor_plot( method = "color", label = TRUE, insignificant = "blank" ) # Change the color palettes #:::::::::::::::::::::::::::::::::::::::::: # Using custom color palette # Require ggpubr: install.packages("ggpubr") if(require("ggpubr")){ my.palette <- get_palette(c("red", "white", "blue"), 200) cor.lower.tri \%>\% cor_plot(palette = my.palette) } # Using RcolorBrewer color palette if(require("ggpubr")){ my.palette <- get_palette("PuOr", 200) cor.lower.tri \%>\% cor_plot(palette = my.palette) } } \seealso{ \code{\link{cor_as_symbols}()} } rstatix/man/as_cor_mat.Rd0000644000176200001440000000177713470305677015151 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/as_cor_mat.R \name{as_cor_mat} \alias{as_cor_mat} \title{Convert a Correlation Test Data Frame into a Correlation Matrix} \usage{ as_cor_mat(x) } \arguments{ \item{x}{an object of class \code{cor_test}.} } \value{ Returns a data frame containing the matrix of the correlation coefficients. The output has an attribute named "pvalue", which contains the matrix of the correlation test p-values. } \description{ Convert a correlation test data frame, returned by the \code{\link{cor_test}()}, into a correlation matrix format. } \examples{ # Pairwise correlation tests between variables #::::::::::::::::::::::::::::::::::::::::::::::: res.cor.test <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_test() res.cor.test # Convert the correlation test into a correlation matrix #::::::::::::::::::::::::::::::::::::::::::::::: res.cor.test \%>\% as_cor_mat() } \seealso{ \code{\link{cor_mat}()}, \code{\link{cor_test}()} } rstatix/man/adjust_pvalue.Rd0000644000176200001440000000173413505626353015675 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/adjust_pvalue.R \name{adjust_pvalue} \alias{adjust_pvalue} \title{Adjust P-values for Multiple Comparisons} \usage{ adjust_pvalue(data, p.col = NULL, output.col = NULL, method = "holm") } \arguments{ \item{data}{a data frame containing a p-value column} \item{p.col}{column name containing p-values} \item{output.col}{the output column name to hold the adjusted p-values} \item{method}{method for adjusting p values (see \code{\link[stats]{p.adjust}}). Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} } \value{ a data frame } \description{ A pipe-friendly function to add an adjusted p-value column into a data frame. Supports grouped data. } \examples{ # Perform pairwise comparisons and adjust p-values ToothGrowth \%>\% t_test(len ~ dose) \%>\% adjust_pvalue() } rstatix/man/cor_select.Rd0000644000176200001440000000203113470305677015144 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_select.R \name{cor_select} \alias{cor_select} \title{Subset Correlation Matrix} \usage{ cor_select(x, ..., vars = NULL) } \arguments{ \item{x}{a correlation matrix. Particularly, an object of class \code{cor_mat}.} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select variables of interest.} \item{vars}{a character vector containing the variable names of interest.} } \value{ a data frame } \description{ Subset Correlation Matrix } \examples{ # Compute correlation matrix #:::::::::::::::::::::::::::::::::::::::::: cor.mat <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_mat() # Subsetting correlation matrix #:::::::::::::::::::::::::::::::::::::::::: # Select some variables of interest cor.mat \%>\% cor_select(mpg, drat, wt) # Remove variables cor.mat \%>\% cor_select(-mpg, -wt) } \seealso{ \code{\link{cor_mat}()}, \code{\link{pull_triangle}()}, \code{\link{replace_triangle}()} } rstatix/man/get_comparisons.Rd0000644000176200001440000000237713477036123016225 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/get_comparisons.R \name{get_comparisons} \alias{get_comparisons} \title{Create a List of Possible Comparisons Between Groups} \usage{ get_comparisons(data, variable, ref.group = NULL) } \arguments{ \item{data}{a data frame} \item{variable}{the grouping variable name. Can be unquoted.} \item{ref.group}{a character string specifying the reference group. Can be unquoted. If numeric, then it should be quoted. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group). If \code{ref.group = "all"}, pairwise comparisons are performed between each grouping variable levels against all (i.e. basemean).} } \value{ a list of all possible pairwise comparisons. } \description{ Create a list of possible pairwise comparisons between groups. If a reference group is specified, only comparisons against reference will be kept. } \examples{ # All possible pairwise comparisons ToothGrowth \%>\% get_comparisons("dose") # Comparisons against reference groups ToothGrowth \%>\% get_comparisons("dose", ref.group = "0.5") # Comparisons against all (basemean) ToothGrowth \%>\% get_comparisons("dose", ref.group = "all") } rstatix/man/df_unite.Rd0000644000176200001440000000270713640452737014630 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/df.R \name{df_unite} \alias{df_unite} \alias{df_unite_factors} \title{Unite Multiple Columns into One} \usage{ df_unite(data, col, ..., vars = NULL, sep = "_", remove = TRUE, na.rm = FALSE) df_unite_factors( data, col, ..., vars = NULL, sep = "_", remove = TRUE, na.rm = FALSE ) } \arguments{ \item{data}{a data frame} \item{col}{the name of the new column as a string or a symbol.} \item{...}{a selection of columns. One or more unquoted expressions (or variable names) separated by commas.} \item{vars}{a character vector containing the column names of interest.} \item{sep}{Separator to use between values.} \item{remove}{If \code{TRUE}, remove input columns from output data frame.} \item{na.rm}{If \code{TRUE}, missing values will be remove prior to uniting each value.} } \description{ Paste together multiple columns into one. Wrapper arround \code{\link[tidyr]{unite}()} that supports standard and non standard evaluation. } \section{Functions}{ \itemize{ \item \code{df_unite}: Unite multiple columns into one. \item \code{df_unite_factors}: Unite factor columns. First, order factors levels then merge them into one column. The output column is a factor. }} \examples{ # Non standard evaluation head(ToothGrowth) \%>\% df_unite(col = "dose_supp", dose, supp) # Standard evaluation head(ToothGrowth) \%>\% df_unite(col = "dose_supp", vars = c("dose", "supp")) } rstatix/man/sample_n_by.Rd0000644000176200001440000000107013466102724015305 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sample_n_by.R \name{sample_n_by} \alias{sample_n_by} \title{Sample n Rows By Group From a Table} \usage{ sample_n_by(data, ..., size = 1, replace = FALSE) } \arguments{ \item{data}{a data frame} \item{...}{Variables to group by} \item{size}{the number of rows to select} \item{replace}{with or without replacement?} } \description{ sample n rows by group from a table using the \code{\link[dplyr]{sample_n}()} function. } \examples{ ToothGrowth \%>\% sample_n_by(dose, supp, size = 2) } rstatix/man/kruskal_test.Rd0000644000176200001440000000261313534275744015545 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/kruskal_test.R \name{kruskal_test} \alias{kruskal_test} \title{Kruskal-Wallis Test} \usage{ kruskal_test(data, formula, ...) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{...}{other arguments to be passed to the function \code{\link[stats]{kruskal.test}}.} } \value{ return a data frame with the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{n}: sample count. \item \code{statistic}: the kruskal-wallis rank sum statistic used to compute the p-value. \item \code{p}: p-value. \item \code{method}: the statistical test used to compare groups.} } \description{ Provides a pipe-friendly framework to perform Kruskal-Wallis rank sum test. Wrapper around the function \code{\link[stats]{kruskal.test}()}. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # Kruskal-wallis rank sum test #::::::::::::::::::::::::::::::::::::::::: df \%>\% kruskal_test(len ~ dose) # Grouped data df \%>\% group_by(supp) \%>\% kruskal_test(len ~ dose) } rstatix/man/counts_to_cases.Rd0000644000176200001440000000173713544500215016214 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/counts_to_cases.R \name{counts_to_cases} \alias{counts_to_cases} \title{Convert a Table of Counts into a Data Frame of cases} \usage{ counts_to_cases(x, count.col = "Freq") } \arguments{ \item{x}{a contingency table or a data frame} \item{count.col}{the name of the column containing the counts. Default is "Freq".} } \value{ a data frame of cases } \description{ converts a contingency table or a data frame of counts into a data frame of individual observations. } \examples{ # Create a cross-tabulation demo data #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% xtab <- as.table( rbind(c(20, 5), c(16,9)) ) dimnames(xtab) <- list( before = c("non.smoker", "smoker"), after = c("non.smoker", "smoker") ) xtab # Convert into a data frame of cases #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% df <- counts_to_cases(xtab) head(df) } rstatix/man/reexports.Rd0000644000176200001440000000166313672730742015066 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reexports.R \docType{import} \name{reexports} \alias{reexports} \alias{tibble} \alias{mutate} \alias{filter} \alias{group_by} \alias{select} \alias{desc} \alias{drop_na} \alias{gather} \alias{spread} \alias{tidy} \alias{augment} \alias{Anova} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{car}{\code{\link[car]{Anova}}} \item{dplyr}{\code{\link[dplyr]{desc}}, \code{\link[dplyr]{filter}}, \code{\link[dplyr]{group_by}}, \code{\link[dplyr]{mutate}}, \code{\link[dplyr]{select}}} \item{generics}{\code{\link[generics]{augment}}, \code{\link[generics]{tidy}}} \item{tibble}{\code{\link[tibble]{tibble}}} \item{tidyr}{\code{\link[tidyr]{drop_na}}, \code{\link[tidyr]{gather}}, \code{\link[tidyr]{spread}}} }} rstatix/man/outliers.Rd0000644000176200001440000000512613525465505014676 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/outliers.R \name{identify_outliers} \alias{identify_outliers} \alias{is_outlier} \alias{is_extreme} \title{Identify Univariate Outliers Using Boxplot Methods} \usage{ identify_outliers(data, ..., variable = NULL) is_outlier(x, coef = 1.5) is_extreme(x) } \arguments{ \item{data}{a data frame} \item{...}{One unquoted expressions (or variable name). Used to select a variable of interest. Alternative to the argument \code{variable}.} \item{variable}{variable name for detecting outliers} \item{x}{a numeric vector} \item{coef}{coefficient specifying how far the outlier should be from the edge of their box. Possible values are 1.5 (for outlier) and 3 (for extreme points only). Default is 1.5} } \value{ \itemize{ \item \code{identify_outliers()}. Returns the input data frame with two additional columns: "is.outlier" and "is.extreme", which hold logical values. \item \code{is_outlier() and is_extreme()}. Returns logical vectors. } } \description{ Detect outliers using boxplot methods. Boxplots are a popular and an easy method for identifying outliers. There are two categories of outlier: (1) outliers and (2) extreme points. Values above \code{Q3 + 1.5xIQR} or below \code{Q1 - 1.5xIQR} are considered as outliers. Values above \code{Q3 + 3xIQR} or below \code{Q1 - 3xIQR} are considered as extreme points (or extreme outliers). Q1 and Q3 are the first and third quartile, respectively. IQR is the interquartile range (IQR = Q3 - Q1). Generally speaking, data points that are labelled outliers in boxplots are not considered as troublesome as those considered extreme points and might even be ignored. Note that, any \code{NA} and \code{NaN} are automatically removed before the quantiles are computed. } \section{Functions}{ \itemize{ \item \code{identify_outliers}: takes a data frame and extract rows suspected as outliers according to a numeric column. The following columns are added "is.outlier" and "is.extreme". \item \code{is_outlier}: detect outliers in a numeric vector. Returns logical vector. \item \code{is_extreme}: detect extreme points in a numeric vector. An alias of \code{is_outlier()}, where coef = 3. Returns logical vector. }} \examples{ # Generate a demo data set.seed(123) demo.data <- data.frame( sample = 1:20, score = c(rnorm(19, mean = 5, sd = 2), 50), gender = rep(c("Male", "Female"), each = 10) ) # Identify outliers according to the variable score demo.data \%>\% identify_outliers(score) # Identify outliers by groups demo.data \%>\% group_by(gender) \%>\% identify_outliers("score") } rstatix/man/chisq_test.Rd0000644000176200001440000001223013640215135015156 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/chisq_test.R \name{chisq_test} \alias{chisq_test} \alias{pairwise_chisq_gof_test} \alias{pairwise_chisq_test_against_p} \alias{chisq_descriptives} \alias{expected_freq} \alias{observed_freq} \alias{pearson_residuals} \alias{std_residuals} \title{Chi-squared Test for Count Data} \usage{ chisq_test( x, y = NULL, correct = TRUE, p = rep(1/length(x), length(x)), rescale.p = FALSE, simulate.p.value = FALSE, B = 2000 ) pairwise_chisq_gof_test(x, p.adjust.method = "holm", ...) pairwise_chisq_test_against_p( x, p = rep(1/length(x), length(x)), p.adjust.method = "holm", ... ) chisq_descriptives(res.chisq) expected_freq(res.chisq) observed_freq(res.chisq) pearson_residuals(res.chisq) std_residuals(res.chisq) } \arguments{ \item{x}{a numeric vector or matrix. \code{x} and \code{y} can also both be factors.} \item{y}{a numeric vector; ignored if \code{x} is a matrix. If \code{x} is a factor, \code{y} should be a factor of the same length.} \item{correct}{a logical indicating whether to apply continuity correction when computing the test statistic for 2 by 2 tables: one half is subtracted from all \eqn{|O - E|} differences; however, the correction will not be bigger than the differences themselves. No correction is done if \code{simulate.p.value = TRUE}.} \item{p}{a vector of probabilities of the same length of \code{x}. An error is given if any entry of \code{p} is negative.} \item{rescale.p}{a logical scalar; if TRUE then \code{p} is rescaled (if necessary) to sum to 1. If \code{rescale.p} is FALSE, and \code{p} does not sum to 1, an error is given.} \item{simulate.p.value}{a logical indicating whether to compute p-values by Monte Carlo simulation.} \item{B}{an integer specifying the number of replicates used in the Monte Carlo test.} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{...}{other arguments passed to the function \code{{chisq_test}()}.} \item{res.chisq}{an object of class \code{chisq_test}.} } \value{ return a data frame with some the following columns: \itemize{ \item \code{n}: the number of participants. \item \code{group, group1, group2}: the categories or groups being compared. \item \code{statistic}: the value of Pearson's chi-squared test statistic. \item \code{df}: the degrees of freedom of the approximate chi-squared distribution of the test statistic. NA if the p-value is computed by Monte Carlo simulation. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the used statistical test. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{observed}: observed counts. \item \code{expected}: the expected counts under the null hypothesis. } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs chi-squared tests, including goodness-of-fit, homogeneity and independence tests. } \section{Functions}{ \itemize{ \item \code{chisq_test}: performs chi-square tests including goodness-of-fit, homogeneity and independence tests. \item \code{pairwise_chisq_gof_test}: perform pairwise comparisons between groups following a global chi-square goodness of fit test. \item \code{pairwise_chisq_test_against_p}: perform pairwise comparisons after a global chi-squared test for given probabilities. For each group, the observed and the expected proportions are shown. Each group is compared to the sum of all others. \item \code{chisq_descriptives}: returns the descriptive statistics of the chi-square test. These include, observed and expected frequencies, proportions, residuals and standardized residuals. \item \code{expected_freq}: returns the expected counts from the chi-square test result. \item \code{observed_freq}: returns the observed counts from the chi-square test result. \item \code{pearson_residuals}: returns the Pearson residuals, \code{(observed - expected) / sqrt(expected)}. \item \code{std_residuals}: returns the standardized residuals }} \examples{ # Chi-square goodness of fit test #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% tulip <- c(red = 81, yellow = 50, white = 27) # Q1: Are the colors equally common? chisq_test(tulip) pairwise_chisq_gof_test(tulip) # Q2: comparing observed to expected proportions chisq_test(tulip, p = c(1/2, 1/3, 1/6)) pairwise_chisq_test_against_p(tulip, p = c(0.5, 0.33, 0.17)) # Homogeneity of proportions between groups #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: Titanic xtab <- as.table(rbind( c(203, 118, 178, 212), c(122, 167, 528, 673) )) dimnames(xtab) <- list( Survived = c("Yes", "No"), Class = c("1st", "2nd", "3rd", "Crew") ) xtab # Chi-square test chisq_test(xtab) # Compare the proportion of survived between groups pairwise_prop_test(xtab) } rstatix/man/cochran_qtest.Rd0000644000176200001440000000267613574263750015676 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cochran_qtest.R \name{cochran_qtest} \alias{cochran_qtest} \title{Cochran's Q Test} \usage{ cochran_qtest(data, formula) } \arguments{ \item{data}{a data frame containing the variables in the formula.} \item{formula}{a formula of the form \code{a ~ b | c}, where \code{a} is the outcome variable name; b is the within-subjects factor variables; and c (factor) is the column name containing individuals/subjects identifier. Should be unique per individual.} } \description{ Performs the Cochran's Q test for unreplicated randomized block design experiments with a binary response variable and paired data. This test is analogue to the \code{\link{friedman.test}()} with 0,1 coded response. It's an extension of the McNemar Chi-squared test for comparing more than two paired proportions. } \examples{ # Generate a demo data mydata <- data.frame( outcome = c(0,1,1,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,1,0,0,1), treatment = gl(3,1,30,labels=LETTERS[1:3]), participant = gl(10,3,labels=letters[1:10]) ) mydata$outcome <- factor( mydata$outcome, levels = c(1, 0), labels = c("success", "failure") ) # Cross-tabulation xtabs(~outcome + treatment, mydata) # Compare the proportion of success between treatments cochran_qtest(mydata, outcome ~ treatment|participant) # pairwise comparisons between groups pairwise_mcnemar_test(mydata, outcome ~ treatment|participant) } rstatix/man/make_clean_names.Rd0000644000176200001440000000115313470301364016255 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/make_clean_names.R \name{make_clean_names} \alias{make_clean_names} \title{Make Clean Names} \usage{ make_clean_names(data) } \arguments{ \item{data}{a data frame or vector} } \value{ a data frame or a vector depending on the input data } \description{ Pipe-friendly function to make syntactically valid names out of character vectors. } \examples{ # Vector make_clean_names(c("a and b", "a-and-b")) make_clean_names(1:10) # data frame df <- data.frame( `a and b` = 1:4, `c and d` = 5:8, check.names = FALSE ) df make_clean_names(df) } rstatix/man/eta_squared.Rd0000644000176200001440000000147413454727303015325 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/eta_squared.R \name{eta_squared} \alias{eta_squared} \alias{partial_eta_squared} \title{Effect Size for ANOVA} \usage{ eta_squared(model) partial_eta_squared(model) } \arguments{ \item{model}{an object of class aov or anova.} } \value{ a numeric vector with the effect size statistics } \description{ Compute eta-squared and partial eta-squared for all terms in an ANOVA model. } \section{Functions}{ \itemize{ \item \code{eta_squared}: compute eta squared \item \code{partial_eta_squared}: compute partial eta squared. }} \examples{ # Data preparation df <- ToothGrowth df$dose <- as.factor(df$dose) # Compute ANOVA res.aov <- aov(len ~ supp*dose, data = df) summary(res.aov) # Effect size eta_squared(res.aov) partial_eta_squared(res.aov) } rstatix/man/anova_test.Rd0000644000176200001440000001743513743650773015206 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/anova_test.R \name{anova_test} \alias{anova_test} \alias{get_anova_table} \alias{print.anova_test} \alias{plot.anova_test} \title{Anova Test} \usage{ anova_test( data, formula, dv, wid, between, within, covariate, type = NULL, effect.size = "ges", error = NULL, white.adjust = FALSE, observed = NULL, detailed = FALSE ) get_anova_table(x, correction = c("auto", "GG", "HF", "none")) \method{print}{anova_test}(x, ...) \method{plot}{anova_test}(x, ...) } \arguments{ \item{data}{a data.frame or a model to be analyzed.} \item{formula}{a formula specifying the ANOVA model similar to \link[stats]{aov}. Can be of the form \code{y ~ group} where \code{y} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}. Examples of supported formula include: \itemize{ \item Between-Ss ANOVA (independent measures ANOVA): \code{y ~ b1*b2} \item Within-Ss ANOVA (repeated measures ANOVA): \code{y ~ w1*w2 + Error(id/(w1*w2))} \item Mixed ANOVA: \code{y ~ b1*b2*w1 + Error(id/w1)} } If the formula doesn't contain any within vars, a linear model is directly fitted and passed to the ANOVA function. For repeated designs, the ANOVA variables are parsed from the formula.} \item{dv}{(numeric) dependent variable name.} \item{wid}{(factor) column name containing individuals/subjects identifier. Should be unique per individual.} \item{between}{(optional) between-subject factor variables.} \item{within}{(optional) within-subjects factor variables} \item{covariate}{(optional) covariate names (for ANCOVA)} \item{type}{the type of sums of squares for ANOVA. Allowed values are either 1, 2 or 3. \code{type = 2} is the default because this will yield identical ANOVA results as type = 1 when data are balanced but type = 2 will additionally yield various assumption tests where appropriate. When the data are unbalanced the \code{type = 3} is used by popular commercial softwares including SPSS.} \item{effect.size}{the effect size to compute and to show in the ANOVA results. Allowed values can be either "ges" (generalized eta squared) or "pes" (partial eta squared) or both. Default is "ges".} \item{error}{(optional) for a linear model, an lm model object from which the overall error sum of squares and degrees of freedom are to be calculated. Read more in \code{\link[car]{Anova}()} documentation.} \item{white.adjust}{Default is FALSE. If TRUE, heteroscedasticity correction is applied to the coefficient of covariance matrix. Used only for independent measures ANOVA.} \item{observed}{Variables that are observed (i.e, measured) as compared to experimentally manipulated. The default effect size reported (generalized eta-squared) requires correct specification of the observed variables.} \item{detailed}{If TRUE, returns extra information (sums of squares columns, intercept row, etc.) in the ANOVA table.} \item{x}{an object of class \code{anova_test}} \item{correction}{character. Used only in repeated measures ANOVA test to specify which correction of the degrees of freedom should be reported for the within-subject factors. Possible values are: \itemize{ \item{"GG"}: applies Greenhouse-Geisser correction to all within-subjects factors even if the assumption of sphericity is met (i.e., Mauchly's test is not significant, p > 0.05). \item{"HF"}: applies Hyunh-Feldt correction to all within-subjects factors even if the assumption of sphericity is met, \item{"none"}: returns the ANOVA table without any correction and \item{"auto"}: apply automatically GG correction to only within-subjects factors violating the sphericity assumption (i.e., Mauchly's test p-value is significant, p <= 0.05). }} \item{...}{additional arguments} } \value{ return an object of class \code{anova_test} a data frame containing the ANOVA table for independent measures ANOVA. However, for repeated/mixed measures ANOVA, a list containing the following components are returned: ANOVA table, Mauchly's Test for Sphericity, Sphericity Corrections. These table are described more in the documentation of the function \code{\link{anova_summary}()}. The \strong{returned object has an attribute} called \code{args}, which is a list holding the arguments used to fit the ANOVA model, including: data, dv, within, between, type, model, etc. } \description{ Provides a pipe-friendly framework to perform different types of ANOVA tests, including: \itemize{ \item \strong{\href{https://www.datanovia.com/en/lessons/anova-in-r/}{Independent measures ANOVA}}: between-Subjects designs, \item \strong{\href{https://www.datanovia.com/en/lessons/repeated-measures-anova-in-r/}{Repeated measures ANOVA}}: within-Subjects designs \item \strong{\href{https://www.datanovia.com/en/lessons/mixed-anova-in-r/}{Mixed ANOVA}}: Mixed within within- and between-Subjects designs, also known as split-plot ANOVA and \item \strong{\href{https://www.datanovia.com/en/lessons/ancova-in-r/}{ANCOVA: Analysis of Covariance}}. } The function is an easy to use wrapper around \code{\link[car]{Anova}()} and \code{\link[stats]{aov}()}. It makes ANOVA computation handy in R and It's highly flexible: can support model and formula as input. Variables can be also specified as character vector using the arguments \code{dv, wid, between, within, covariate}. The results include ANOVA table, generalized effect size and some assumption checks. } \details{ The setting in \code{anova_test()} is done in such a way that it gives the same results as SPSS, one of the most used commercial software. By default, R uses treatment contrasts, where each of the levels is compared to the first level used as baseline. The default contrast can be checked using \code{options('contrasts')}. In the function \code{anova_test()}, the following setting is used \code{options(contrasts=c('contr.sum','contr.poly'))}, which gives orthogonal contrasts where you compare every level to the overall mean. This setting gives the same output as the most commonly used commercial softwares, like SPSS. If you want to obtain the same result with the function \code{car::Anova()} as the one obtained with \code{rstatix::anova_test()}, then don't forget to set \code{options(contrasts=c('contr.sum','contr.poly'))}. } \section{Functions}{ \itemize{ \item \code{anova_test}: perform anova test \item \code{get_anova_table}: extract anova table from an object of class \code{anova_test}. When within-subject factors are present, either sphericity corrected or uncorrected degrees of freedom can be reported. }} \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # One-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: df \%>\% anova_test(len ~ dose) # Grouped One-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: df \%>\% group_by(supp) \%>\% anova_test(len ~ dose) # Two-way ANOVA test #::::::::::::::::::::::::::::::::::::::::: df \%>\% anova_test(len ~ supp*dose) # Two-way repeated measures ANOVA #::::::::::::::::::::::::::::::::::::::::: df$id <- rep(1:10, 6) # Add individuals id # Use formula \donttest{ df \%>\% anova_test(len ~ supp*dose + Error(id/(supp*dose))) } # or use character vector df \%>\% anova_test(dv = len, wid = id, within = c(supp, dose)) # Extract ANOVA table and apply correction #::::::::::::::::::::::::::::::::::::::::: res.aov <- df \%>\% anova_test(dv = len, wid = id, within = c(supp, dose)) get_anova_table(res.aov, correction = "GG") # Use model as arguments #::::::::::::::::::::::::::::::::::::::::: .my.model <- lm(yield ~ block + N*P*K, npk) anova_test(.my.model) } \seealso{ \code{\link{anova_summary}()}, \code{\link{factorial_design}()} } \author{ Alboukadel Kassambara, \email{alboukadel.kassambara@gmail.com} } rstatix/man/shapiro_test.Rd0000644000176200001440000000306113570426745015533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/shapiro_test.R \name{shapiro_test} \alias{shapiro_test} \alias{mshapiro_test} \title{Shapiro-Wilk Normality Test} \usage{ shapiro_test(data, ..., vars = NULL) mshapiro_test(data) } \arguments{ \item{data}{a data frame. Columns are variables.} \item{...}{One or more unquoted expressions (or variable names) separated by commas. Used to select a variable of interest.} \item{vars}{optional character vector containing variable names. Ignored when dot vars are specified.} } \value{ a data frame containing the value of the Shapiro-Wilk statistic and the corresponding p.value. } \description{ Provides a pipe-friendly framework to performs Shapiro-Wilk test of normality. Support grouped data and multiple variables for multivariate normality tests. Wrapper around the R base function \code{\link[stats]{shapiro.test}()}. Can handle grouped data. Read more: \href{https://www.datanovia.com/en/lessons/normality-test-in-r/}{Normality Test in R}. } \section{Functions}{ \itemize{ \item \code{shapiro_test}: univariate Shapiro-Wilk normality test \item \code{mshapiro_test}: multivariate Shapiro-Wilk normality test. This is a modified copy of the \code{mshapiro.test()} function of the package mvnormtest, for internal convenience. }} \examples{ # Shapiro Wilk normality test for one variable iris \%>\% shapiro_test(Sepal.Length) # Shapiro Wilk normality test for two variables iris \%>\% shapiro_test(Sepal.Length, Petal.Width) # Multivariate normality test mshapiro_test(iris[, 1:3]) } rstatix/man/pipe.Rd0000644000176200001440000000040513672727530013762 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils-pipe.R \name{\%>\%} \alias{\%>\%} \title{Pipe operator} \usage{ lhs \%>\% rhs } \description{ See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. } \keyword{internal} rstatix/man/sign_test.Rd0000644000176200001440000001073213640215135015014 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sign_test.R \name{sign_test} \alias{sign_test} \alias{pairwise_sign_test} \title{Sign Test} \usage{ sign_test( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", alternative = "two.sided", mu = 0, conf.level = 0.95, detailed = FALSE ) pairwise_sign_test( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", detailed = FALSE, ... ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ treatment}.} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group).} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{alternative}{a character string specifying the alternative hypothesis, must be one of \code{"two.sided"} (default), \code{"greater"} or \code{"less"}. You can specify just the initial letter.} \item{mu}{a single number representing the value of the population median specified by the null hypothesis.} \item{conf.level}{confidence level of the interval.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{...}{other arguments passed to the function \code{sign_test()}} } \value{ return a data frame with some the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. \item \code{statistic}: Test statistic used to compute the p-value. That is the S-statistic (the number of positive differences between the data and the hypothesized median), with names attribute \code{"S"}. \item \code{df, parameter}: degrees of freedom. Here, the total number of valid differences. \item \code{p}: p-value. \item \code{method}: the statistical test used to compare groups. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: estimate of the effect size. It corresponds to the median of the differences. \item \code{alternative}: a character string describing the alternative hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a confidence interval of the estimate. } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs one-sample and two-sample sign tests. Read more: \href{https://www.datanovia.com/en/lessons/sign-test-in-r/}{Sign Test in R}. } \section{Functions}{ \itemize{ \item \code{sign_test}: Sign test \item \code{pairwise_sign_test}: performs pairwise two sample Wilcoxon test. }} \note{ This function is a reimplementation of the function \code{SignTest()} from the \code{DescTools} package. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # One-sample test #::::::::::::::::::::::::::::::::::::::::: df \%>\% sign_test(len ~ 1, mu = 0) # Two-samples paired test #::::::::::::::::::::::::::::::::::::::::: df \%>\% sign_test(len ~ supp) # Compare supp levels after grouping the data by "dose" #:::::::::::::::::::::::::::::::::::::::: df \%>\% group_by(dose) \%>\% sign_test(data =., len ~ supp) \%>\% adjust_pvalue(method = "bonferroni") \%>\% add_significance("p.adj") # pairwise comparisons #:::::::::::::::::::::::::::::::::::::::: # As dose contains more than two levels ==> # pairwise test is automatically performed. df \%>\% sign_test(len ~ dose) # Comparison against reference group #:::::::::::::::::::::::::::::::::::::::: # each level is compared to the ref group df \%>\% sign_test(len ~ dose, ref.group = "0.5") } rstatix/man/fisher_test.Rd0000644000176200001440000001441713660026410015336 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/fisher_test.R \name{fisher_test} \alias{fisher_test} \alias{pairwise_fisher_test} \alias{row_wise_fisher_test} \title{Fisher's Exact Test for Count Data} \usage{ fisher_test( xtab, workspace = 2e+05, alternative = "two.sided", conf.int = TRUE, conf.level = 0.95, simulate.p.value = FALSE, B = 2000, detailed = FALSE, ... ) pairwise_fisher_test(xtab, p.adjust.method = "holm", detailed = FALSE, ...) row_wise_fisher_test(xtab, p.adjust.method = "holm", detailed = FALSE, ...) } \arguments{ \item{xtab}{a contingency table in a matrix form.} \item{workspace}{an integer specifying the size of the workspace used in the network algorithm. In units of 4 bytes. Only used for non-simulated p-values larger than \eqn{2 \times 2}{2 by 2} tables. Since \R version 3.5.0, this also increases the internal stack size which allows larger problems to be solved, however sometimes needing hours. In such cases, \code{simulate.p.values=TRUE} may be more reasonable.} \item{alternative}{indicates the alternative hypothesis and must be one of \code{"two.sided"}, \code{"greater"} or \code{"less"}. You can specify just the initial letter. Only used in the \eqn{2 \times 2}{2 by 2} case.} \item{conf.int}{logical indicating if a confidence interval for the odds ratio in a \eqn{2 \times 2}{2 by 2} table should be computed (and returned).} \item{conf.level}{confidence level for the returned confidence interval. Only used in the \eqn{2 \times 2}{2 by 2} case and if \code{conf.int = TRUE}.} \item{simulate.p.value}{a logical indicating whether to compute p-values by Monte Carlo simulation, in larger than \eqn{2 \times 2}{2 by 2} tables.} \item{B}{an integer specifying the number of replicates used in the Monte Carlo test.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{...}{Other arguments passed to the function \code{fisher_test()}.} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} } \value{ return a data frame with some the following columns: \itemize{ \item \code{group}: the categories in the row-wise proportion tests. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the used statistical test. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: an estimate of the odds ratio. Only present in the 2 by 2 case. \item \code{alternative}: a character string describing the alternative hypothesis. \item \code{conf.low,conf.high}: a confidence interval for the odds ratio. Only present in the 2 by 2 case and if argument conf.int = TRUE.} The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs Fisher's exact test for testing the null of independence of rows and columns in a contingency table. Wrappers around the R base function \code{\link[stats]{fisher.test}()} but have the advantage of performing pairwise and row-wise fisher tests, the post-hoc tests following a significant chi-square test of homogeneity for 2xc and rx2 contingency tables. } \section{Functions}{ \itemize{ \item \code{fisher_test}: performs Fisher's exact test for testing the null of independence of rows and columns in a contingency table with fixed marginals. Wrapper around the function \code{\link[stats]{fisher.test}()}. \item \code{pairwise_fisher_test}: pairwise comparisons between proportions, a post-hoc tests following a significant Fisher's exact test of homogeneity for 2xc design. \item \code{row_wise_fisher_test}: performs row-wise Fisher's exact test of count data, a post-hoc tests following a significant chi-square test of homogeneity for rx2 contingency table. The test is conducted for each category (row). }} \examples{ # Comparing two proportions #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: frequencies of smokers between two groups xtab <- as.table(rbind(c(490, 10), c(400, 100))) dimnames(xtab) <- list( group = c("grp1", "grp2"), smoker = c("yes", "no") ) xtab # compare the proportion of smokers fisher_test(xtab, detailed = TRUE) # Homogeneity of proportions between groups #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # H0: the proportion of smokers is similar in the four groups # Ha: this proportion is different in at least one of the populations. # # Data preparation grp.size <- c( 106, 113, 156, 102 ) smokers <- c( 50, 100, 139, 80 ) no.smokers <- grp.size - smokers xtab <- as.table(rbind( smokers, no.smokers )) dimnames(xtab) <- list( Smokers = c("Yes", "No"), Groups = c("grp1", "grp2", "grp3", "grp4") ) xtab # Compare the proportions of smokers between groups fisher_test(xtab, detailed = TRUE) # Pairwise comparison between groups pairwise_fisher_test(xtab) # Pairwise proportion tests #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: Titanic xtab <- as.table(rbind( c(122, 167, 528, 673), c(203, 118, 178, 212) )) dimnames(xtab) <- list( Survived = c("No", "Yes"), Class = c("1st", "2nd", "3rd", "Crew") ) xtab # Compare the proportion of survived between groups pairwise_fisher_test(xtab) # Row-wise proportion tests #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: Titanic xtab <- as.table(rbind( c(180, 145), c(179, 106), c(510, 196), c(862, 23) )) dimnames(xtab) <- list( Class = c("1st", "2nd", "3rd", "Crew"), Gender = c("Male", "Female") ) xtab # Compare the proportion of males and females in each category row_wise_fisher_test(xtab) # A r x c table Agresti (2002, p. 57) Job Satisfaction Job <- matrix(c(1,2,1,0, 3,3,6,1, 10,10,14,9, 6,7,12,11), 4, 4, dimnames = list(income = c("< 15k", "15-25k", "25-40k", "> 40k"), satisfaction = c("VeryD", "LittleD", "ModerateS", "VeryS"))) fisher_test(Job) fisher_test(Job, simulate.p.value = TRUE, B = 1e5) } rstatix/man/wilcox_test.Rd0000644000176200001440000001477013654634515015403 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wilcox_test.R \name{wilcox_test} \alias{wilcox_test} \alias{pairwise_wilcox_test} \title{Wilcoxon Tests} \usage{ wilcox_test( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", paired = FALSE, exact = NULL, alternative = "two.sided", mu = 0, conf.level = 0.95, detailed = FALSE ) pairwise_wilcox_test( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", detailed = FALSE, ... ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{comparisons}{A list of length-2 vectors specifying the groups of interest to be compared. For example to compare groups "A" vs "B" and "B" vs "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", "C"))}} \item{ref.group}{a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group). If \code{ref.group = "all"}, pairwise two sample tests are performed for comparing each grouping variable levels against all (i.e. basemean).} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{paired}{a logical indicating whether you want a paired test.} \item{exact}{a logical indicating whether an exact p-value should be computed.} \item{alternative}{a character string specifying the alternative hypothesis, must be one of \code{"two.sided"} (default), \code{"greater"} or \code{"less"}. You can specify just the initial letter.} \item{mu}{a number specifying an optional parameter used to form the null hypothesis.} \item{conf.level}{confidence level of the interval.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{...}{other arguments to be passed to the function \code{\link[stats]{wilcox.test}}.} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. \item \code{statistic}: Test statistic used to compute the p-value. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the statistical test used to compare groups. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: an estimate of the location parameter (Only present if argument \code{detailed = TRUE}). This corresponds to the pseudomedian (for one-sample case) or to the difference of the location parameter (for two-samples case). \itemize{ \item The pseudomedian of a distribution \code{F} is the median of the distribution of \code{(u+v)/2}, where \code{u} and {v} are independent, each with distribution \code{F}. If \code{F} is symmetric, then the pseudomedian and median coincide. \item Note that in the two-sample case the estimator for the difference in location parameters does not estimate the difference in medians (a common misconception) but rather the median of the difference between a sample from x and a sample from y. } \item \code{conf.low, conf.high}: a confidence interval for the location parameter. (Only present if argument conf.int = TRUE.) } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Provides a pipe-friendly framework to performs one and two sample Wilcoxon tests. Read more: \href{https://www.datanovia.com/en/lessons/wilcoxon-test-in-r/}{Wilcoxon in R}. } \details{ - \code{pairwise_wilcox_test()} applies the standard two sample Wilcoxon test to all possible pairs of groups. This method calls the \code{\link[stats]{wilcox.test}()}, so extra arguments are accepted. - If a list of comparisons is specified, the result of the pairwise tests is filtered to keep only the comparisons of interest.The p-value is adjusted after filtering. - For a grouped data, if pairwise test is performed, then the p-values are adjusted for each group level independently. - a nonparametric confidence interval and an estimator for the pseudomedian (one-sample case) or for the difference of the location parameters \code{x-y} is computed, where x and y are the compared samples or groups. The column \code{estimate} and the confidence intervals are displayed in the test result when the option \code{detailed = TRUE} is specified in the \code{wilcox_test()} and \code{pairwise_wilcox_test()} functions. Read more about the calculation of the estimate in the details section of the R base function \code{wilcox.test()} documentation by typing \code{?wilcox.test} in the R console. } \section{Functions}{ \itemize{ \item \code{wilcox_test}: Wilcoxon test \item \code{pairwise_wilcox_test}: performs pairwise two sample Wilcoxon test. }} \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # One-sample test #::::::::::::::::::::::::::::::::::::::::: df \%>\% wilcox_test(len ~ 1, mu = 0) # Two-samples unpaired test #::::::::::::::::::::::::::::::::::::::::: df \%>\% wilcox_test(len ~ supp) # Two-samples paired test #::::::::::::::::::::::::::::::::::::::::: df \%>\% wilcox_test (len ~ supp, paired = TRUE) # Compare supp levels after grouping the data by "dose" #:::::::::::::::::::::::::::::::::::::::: df \%>\% group_by(dose) \%>\% wilcox_test(data =., len ~ supp) \%>\% adjust_pvalue(method = "bonferroni") \%>\% add_significance("p.adj") # pairwise comparisons #:::::::::::::::::::::::::::::::::::::::: # As dose contains more than two levels ==> # pairwise test is automatically performed. df \%>\% wilcox_test(len ~ dose) # Comparison against reference group #:::::::::::::::::::::::::::::::::::::::: # each level is compared to the ref group df \%>\% wilcox_test(len ~ dose, ref.group = "0.5") # Comparison against all #:::::::::::::::::::::::::::::::::::::::: df \%>\% wilcox_test(len ~ dose, ref.group = "all") } rstatix/man/multinom_test.Rd0000644000176200001440000000327713574261202015730 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/multinom_test.R \name{multinom_test} \alias{multinom_test} \title{Exact Multinomial Test} \usage{ multinom_test(x, p = rep(1/length(x), length(x)), detailed = FALSE) } \arguments{ \item{x}{numeric vector containing the counts.} \item{p}{a vector of probabilities of success. The length of p must be the same as the number of groups specified by x, and its elements must be greater than 0 and less than 1.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} } \value{ return a data frame containing the p-value and its significance. The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs an exact multinomial test. Alternative to the chi-square test of goodness-of-fit-test when the sample size is small. } \examples{ # Data tulip <- c(red = 81, yellow = 50, white = 27) # Question 1: are the color equally common ? #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # this is a test of homogeneity res <- multinom_test(tulip) res attr(res, "descriptives") # Pairwise comparisons between groups pairwise_binom_test(tulip, p.adjust.method = "bonferroni") # Question 2: comparing observed to expected proportions #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # this is a goodness-of-fit test expected.p <- c(red = 0.5, yellow = 0.33, white = 0.17) res <- multinom_test(tulip, expected.p) res attr(res, "descriptives") # Pairwise comparisons against a given probabilities pairwise_binom_test_against_p(tulip, expected.p) } \seealso{ \link{binom_test} } rstatix/man/dunn_test.Rd0000644000176200001440000000540013677303352015025 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/dunn_test.R \name{dunn_test} \alias{dunn_test} \title{Dunn's Test of Multiple Comparisons} \usage{ dunn_test(data, formula, p.adjust.method = "holm", detailed = FALSE) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y (outcome) variable used in the test. \item \code{group1,group2}: the compared groups in the pairwise tests. \item \code{n1,n2}: Sample counts. \item \code{estimate}: mean ranks difference. \item \code{estimate1, estimate2}: show the mean rank values of the two groups, respectively. \item \code{statistic}: Test statistic (z-value) used to compute the p-value. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the statistical test used to compare groups. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. } The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs Dunn's test for pairwise multiple comparisons of the ranked data. The mean rank of the different groups is compared. Used for post-hoc test following Kruskal-Wallis test. } \details{ DunnTest performs the post hoc pairwise multiple comparisons procedure appropriate to follow up a Kruskal-Wallis test, which is a non-parametric analog of the one-way ANOVA. The Wilcoxon rank sum test, itself a non-parametric analog of the unpaired t-test, is possibly intuitive, but inappropriate as a post hoc pairwise test, because (1) it fails to retain the dependent ranking that produced the Kruskal-Wallis test statistic, and (2) it does not incorporate the pooled variance estimate implied by the null hypothesis of the Kruskal-Wallis test. } \examples{ # Simple test ToothGrowth \%>\% dunn_test(len ~ dose) # Grouped data ToothGrowth \%>\% group_by(supp) \%>\% dunn_test(len ~ dose) } \references{ Dunn, O. J. (1964) Multiple comparisons using rank sums Technometrics, 6(3):241-252. } rstatix/man/box_m.Rd0000644000176200001440000000211613470305677014132 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/box_m.R \name{box_m} \alias{box_m} \title{Box's M-test for Homogeneity of Covariance Matrices} \usage{ box_m(data, group) } \arguments{ \item{data}{a numeric data.frame or matrix containing n observations of p variables; it is expected that n > p.} \item{group}{a vector of length n containing the class of each observation; it is usually a factor.} } \value{ A data frame containing the following components: \itemize{ \item{statistic }{an approximated value of the chi-square distribution.} \item{parameter }{the degrees of freedom related of the test statistic in this case that it follows a Chi-square distribution.} \item{p.value }{the p-value of the test.} \item{method }{the character string "Box's M-test for Homogeneity of Covariance Matrices".} } } \description{ Performs the Box's M-test for homogeneity of covariance matrices obtained from multivariate normal data according to one grouping variable. The test is based on the chi-square approximation. } \examples{ data(iris) box_m(iris[, -5], iris[, 5]) } rstatix/man/prop_trend_test.Rd0000644000176200001440000000314613547432602016237 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/prop_trend_test.R \name{prop_trend_test} \alias{prop_trend_test} \title{Test for Trend in Proportions} \usage{ prop_trend_test(xtab, score = NULL) } \arguments{ \item{xtab}{a cross-tabulation (or contingency table) with two columns and multiple rows (rx2 design). The columns give the counts of successes and failures respectively.} \item{score}{group score. If \code{NULL}, the default is group number.} } \value{ return a data frame with some the following columns: \itemize{ \item \code{n}: the number of participants. \item \code{statistic}: the value of Chi-squared trend test statistic. \item \code{df}: the degrees of freedom. \item \code{p}: p-value. \item \code{method}: the used statistical test. \item \code{p.signif}: the significance level of p-values and adjusted p-values, respectively.} The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs chi-squared test for trend in proportion. This test is also known as Cochran-Armitage trend test. Wrappers around the R base function \code{\link[stats]{prop.trend.test}()} but returns a data frame for easy data visualization. } \examples{ # Proportion of renal stone (calculi) across age #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data xtab <- as.table(rbind( c(384, 536, 335), c(951, 869, 438) )) dimnames(xtab) <- list( stone = c("yes", "no"), age = c("30-39", "40-49", "50-59") ) xtab # Compare the proportion of survived between groups prop_trend_test(xtab) } rstatix/man/prop_test.Rd0000644000176200001440000001541213640215135015034 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/prop_test.R \name{prop_test} \alias{prop_test} \alias{pairwise_prop_test} \alias{row_wise_prop_test} \title{Proportion Test} \usage{ prop_test( x, n, p = NULL, alternative = c("two.sided", "less", "greater"), correct = TRUE, conf.level = 0.95, detailed = FALSE ) pairwise_prop_test(xtab, p.adjust.method = "holm", ...) row_wise_prop_test(xtab, p.adjust.method = "holm", detailed = FALSE, ...) } \arguments{ \item{x}{a vector of counts of successes, a one-dimensional table with two entries, or a two-dimensional table (or matrix) with 2 columns, giving the counts of successes and failures, respectively.} \item{n}{a vector of counts of trials; ignored if \code{x} is a matrix or a table.} \item{p}{a vector of probabilities of success. The length of \code{p} must be the same as the number of groups specified by \code{x}, and its elements must be greater than 0 and less than 1.} \item{alternative}{a character string specifying the alternative hypothesis, must be one of \code{"two.sided"} (default), \code{"greater"} or \code{"less"}. You can specify just the initial letter. Only used for testing the null that a single proportion equals a given value, or that two proportions are equal; ignored otherwise.} \item{correct}{a logical indicating whether Yates' continuity correction should be applied where possible.} \item{conf.level}{confidence level of the returned confidence interval. Must be a single number between 0 and 1. Only used when testing the null that a single proportion equals a given value, or that two proportions are equal; ignored otherwise.} \item{detailed}{logical value. Default is FALSE. If TRUE, a detailed result is shown.} \item{xtab}{a cross-tabulation (or contingency table) with two columns and multiple rows (rx2 design). The columns give the counts of successes and failures respectively.} \item{p.adjust.method}{method to adjust p values for multiple comparisons. Used when pairwise comparisons are performed. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".} \item{...}{Other arguments passed to the function \code{prop_test()}.} } \value{ return a data frame with some the following columns: \itemize{ \item \code{n}: the number of participants. \item \code{group}: the categories in the row-wise proportion tests. \item \code{statistic}: the value of Pearson's chi-squared test statistic. \item \code{df}: the degrees of freedom of the approximate chi-squared distribution of the test statistic. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the used statistical test. \item \code{p.signif, p.adj.signif}: the significance level of p-values and adjusted p-values, respectively. \item \code{estimate}: a vector with the sample proportions x/n. \item \code{estimate1, estimate2}: the proportion in each of the two populations. \item \code{alternative}: a character string describing the alternative hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a confidence interval. a confidence interval for the true proportion if there is one group, or for the difference in proportions if there are 2 groups and p is not given, or NULL otherwise. In the cases where it is not NULL, the returned confidence interval has an asymptotic confidence level as specified by conf.level, and is appropriate to the specified alternative hypothesis.} The \strong{returned object has an attribute called args}, which is a list holding the test arguments. } \description{ Performs proportion tests to either evaluate the homogeneity of proportions (probabilities of success) in several groups or to test that the proportions are equal to certain given values. Wrappers around the R base function \code{\link[stats]{prop.test}()} but have the advantage of performing pairwise and row-wise z-test of two proportions, the post-hoc tests following a significant chi-square test of homogeneity for 2xc and rx2 contingency tables. } \section{Functions}{ \itemize{ \item \code{prop_test}: performs one-sample and two-samples z-test of proportions. Wrapper around the function \code{\link[stats]{prop.test}()}. \item \code{pairwise_prop_test}: pairwise comparisons between proportions, a post-hoc tests following a significant chi-square test of homogeneity for 2xc design. Wrapper around \code{\link[stats]{pairwise.prop.test}()} \item \code{row_wise_prop_test}: performs row-wise z-test of two proportions, a post-hoc tests following a significant chi-square test of homogeneity for rx2 contingency table. The z-test of two proportions is calculated for each category (row). }} \examples{ # Comparing an observed proportion to an expected proportion #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% prop_test(x = 95, n = 160, p = 0.5, detailed = TRUE) # Comparing two proportions #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: frequencies of smokers between two groups xtab <- as.table(rbind(c(490, 10), c(400, 100))) dimnames(xtab) <- list( group = c("grp1", "grp2"), smoker = c("yes", "no") ) xtab # compare the proportion of smokers prop_test(xtab, detailed = TRUE) # Homogeneity of proportions between groups #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # H0: the proportion of smokers is similar in the four groups # Ha: this proportion is different in at least one of the populations. # # Data preparation grp.size <- c( 106, 113, 156, 102 ) smokers <- c( 50, 100, 139, 80 ) no.smokers <- grp.size - smokers xtab <- as.table(rbind( smokers, no.smokers )) dimnames(xtab) <- list( Smokers = c("Yes", "No"), Groups = c("grp1", "grp2", "grp3", "grp4") ) xtab # Compare the proportions of smokers between groups prop_test(xtab, detailed = TRUE) # Pairwise comparison between groups pairwise_prop_test(xtab) # Pairwise proportion tests #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: Titanic xtab <- as.table(rbind( c(122, 167, 528, 673), c(203, 118, 178, 212) )) dimnames(xtab) <- list( Survived = c("No", "Yes"), Class = c("1st", "2nd", "3rd", "Crew") ) xtab # Compare the proportion of survived between groups pairwise_prop_test(xtab) # Row-wise proportion tests #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Data: Titanic xtab <- as.table(rbind( c(180, 145), c(179, 106), c(510, 196), c(862, 23) )) dimnames(xtab) <- list( Class = c("1st", "2nd", "3rd", "Crew"), Gender = c("Male", "Female") ) xtab # Compare the proportion of males and females in each category row_wise_prop_test(xtab) } rstatix/man/mahalanobis_distance.Rd0000644000176200001440000000404613463131754017155 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mahalanobis_distance.R \name{mahalanobis_distance} \alias{mahalanobis_distance} \title{Compute Mahalanobis Distance and Flag Multivariate Outliers} \usage{ mahalanobis_distance(data, ...) } \arguments{ \item{data}{a data frame. Columns are variables.} \item{...}{One unquoted expressions (or variable name). Used to select a variable of interest. Can be also used to ignore a variable that are not needed for the computation. For example specify \code{-id} to ignore the id column.} } \value{ Returns the input data frame with two additional columns: 1) "mahal.dist": Mahalanobis distance values; and 2) "is.outlier": logical values specifying whether a given observation is a multivariate outlier } \description{ Pipe-friendly wrapper around to the function \code{\link[stats]{mahalanobis}()}, which returns the squared Mahalanobis distance of all rows in x. Compared to the base function, it automatically flags multivariate outliers. Mahalanobis distance is a common metric used to identify multivariate outliers. The larger the value of Mahalanobis distance, the more unusual the data point (i.e., the more likely it is to be a multivariate outlier). The distance tells us how far an observation is from the center of the cloud, taking into account the shape (covariance) of the cloud as well. To detect outliers, the calculated Mahalanobis distance is compared against a chi-square (X^2) distribution with degrees of freedom equal to the number of dependent (outcome) variables and an alpha level of 0.001. The threshold to declare a multivariate outlier is determined using the function \code{qchisq(0.999, df) }, where df is the degree of freedom (i.e., the number of dependent variable used in the computation). } \examples{ # Compute mahalonobis distance and flag outliers if any iris \%>\% doo(~mahalanobis_distance(.)) # Compute distance by groups and filter outliers iris \%>\% group_by(Species) \%>\% doo(~mahalanobis_distance(.)) \%>\% filter(is.outlier == TRUE) } rstatix/man/cor_as_symbols.Rd0000644000176200001440000000217713640215135016037 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cor_as_symbols.R \name{cor_as_symbols} \alias{cor_as_symbols} \title{Replace Correlation Coefficients by Symbols} \usage{ cor_as_symbols( x, cutpoints = c(0, 0.25, 0.5, 0.75, 1), symbols = c(" ", ".", "+", "*") ) } \arguments{ \item{x}{a correlation matrix. Particularly, an object of class \code{cor_mat}.} \item{cutpoints}{numeric vector used for intervals. Default values are \code{c(0, 0.25, 0.5, 0.75, 1)}.} \item{symbols}{character vector, one shorter than cutpoints, used as correlation coefficient symbols. Default values are \code{c(" ", ".", "+", "*")}.} } \description{ Take a correlation matrix and replace the correlation coefficients by symbols according to the level of the correlation. } \examples{ # Compute correlation matrix #:::::::::::::::::::::::::::::::::::::::::: cor.mat <- mtcars \%>\% select(mpg, disp, hp, drat, wt, qsec) \%>\% cor_mat() # Replace correlation coefficient by symbols #:::::::::::::::::::::::::::::::::::::::::: cor.mat \%>\% cor_as_symbols() \%>\% pull_lower_triangle() } \seealso{ \code{\link{cor_mat}()} } rstatix/man/levene_test.Rd0000644000176200001440000000202314011727452015327 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/levene_test.R \name{levene_test} \alias{levene_test} \title{Levene's Test} \usage{ levene_test(data, formula, center = median) } \arguments{ \item{data}{a data frame for evaluating the formula or a model} \item{formula}{a formula} \item{center}{The name of a function to compute the center of each group; mean gives the original Levene's test; the default, median, provides a more robust test.} } \value{ a data frame with the following columns: df1, df2 (df.residual), statistic and p. } \description{ Provide a pipe-friendly framework to easily compute Levene's test for homogeneity of variance across groups. Wrapper around the function \code{\link[car]{leveneTest}()}, which can additionally handles a grouped data. } \examples{ # Prepare the data data("ToothGrowth") df <- ToothGrowth df$dose <- as.factor(df$dose) # Compute Levene's Test df \%>\% levene_test(len ~ dose) # Grouped data df \%>\% group_by(supp) \%>\% levene_test(len ~ dose) } rstatix/man/factors.Rd0000644000176200001440000000375713523754324014477 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/factors.R \name{convert_as_factor} \alias{convert_as_factor} \alias{set_ref_level} \alias{reorder_levels} \title{Factors} \usage{ convert_as_factor(data, ..., vars = NULL, make.valid.levels = FALSE) set_ref_level(data, name, ref) reorder_levels(data, name, order) } \arguments{ \item{data}{a data frame} \item{...}{one unquoted expressions (or variable name) specifying the name of the variables you want to convert into factor. Alternative to the argument \code{vars}.} \item{vars}{a character vector specifying the variables to convert into factor.} \item{make.valid.levels}{logical. Default is FALSE. If TRUE, converts the variable to factor and add a leading character (x) if starting with a digit.} \item{name}{a factor variable name. Can be unquoted. For example, use \code{group} or \code{"group"}.} \item{ref}{the reference level.} \item{order}{a character vector specifying the order of the factor levels} } \description{ Provides pipe-friendly functions to convert simultaneously multiple variables into a factor variable. Helper functions are also available to set the reference level and the levels order. } \section{Functions}{ \itemize{ \item \code{convert_as_factor}: Convert one or multiple variables into factor. \item \code{set_ref_level}: Change a factor reference level or group. \item \code{reorder_levels}: Change the order of a factor levels }} \examples{ # Create a demo data df <- tibble( group = c("a", "a", "b", "b", "c", "c"), time = c("t1", "t2", "t1", "t2", "t1", "t2"), value = c(5, 6, 1, 3, 4, 5) ) df # Convert group and time into factor variable result <- df \%>\% convert_as_factor(group, time) result # Show group levels levels(result$group) # Set c as the reference level (the first one) result <- result \%>\% set_ref_level("group", ref = "c") levels(result$group) # Set the order of levels result <- result \%>\% reorder_levels("group", order = c("b", "c", "a")) levels(result$group) } rstatix/man/doo.Rd0000644000176200001440000000370413531032740013575 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/doo.R \name{doo} \alias{doo} \title{Alternative to dplyr::do for Doing Anything} \usage{ doo(data, .f, ..., result = ".results.") } \arguments{ \item{data}{a (grouped) data frame} \item{.f}{A function, formula, or atomic vector. For example \code{~t.test(len ~ supp, data = .)}.} \item{...}{Additional arguments passed on to .f} \item{result}{the column name to hold the results. Default is ".results.".} } \value{ a data frame } \description{ Provides a flexible alternative to the \code{dplyr:do()} function. Technically it uses \code{nest() + mutate() + map()} to apply arbitrary computation to a grouped data frame. The output is a data frame. If the applied function returns a data frame, then the output will be automatically unnested. Otherwise, the output includes the grouping variables and a column named ".results." (by default), which is a "list-columns" containing the results for group combinations. } \examples{ # Custom function #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% stat_test <- function(data, formula){ t.test(formula, data) \%>\% tidy() } # Example 1: pipe-friendly stat_test(). # Two possibilities of usage are available #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% # Use this ToothGrowth \%>\% group_by(dose) \%>\% doo(~stat_test(data =., len ~ supp)) # Or this ToothGrowth \%>\% group_by(dose) \%>\% doo(stat_test, len ~ supp) # Example 2: R base function t.test() (not pipe friendly) # One possibility of usage #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% comparisons <- ToothGrowth \%>\% group_by(dose) \%>\% doo(~t.test(len ~ supp, data =.)) comparisons comparisons$.results. # Example 3: R base function combined with tidy() #\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\% ToothGrowth \%>\% group_by(dose) \%>\% doo(~t.test(len ~ supp, data =.) \%>\% tidy()) } rstatix/man/kruskal_effsize.Rd0000644000176200001440000000512413640215135016203 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/kruskal_effesize.R \name{kruskal_effsize} \alias{kruskal_effsize} \title{Kruskal-Wallis Effect Size} \usage{ kruskal_effsize( data, formula, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000 ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{x ~ group} where \code{x} is a numeric variable giving the data values and \code{group} is a factor with one or multiple levels giving the corresponding groups. For example, \code{formula = TP53 ~ cancer_group}.} \item{ci}{If TRUE, returns confidence intervals by bootstrap. May be slow.} \item{conf.level}{The level for the confidence interval.} \item{ci.type}{The type of confidence interval to use. Can be any of "norm", "basic", "perc", or "bca". Passed to \code{boot::boot.ci}.} \item{nboot}{The number of replications to use for bootstrap.} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{n}: Sample counts. \item \code{effsize}: estimate of the effect size. \item \code{magnitude}: magnitude of effect size. \item \code{conf.low,conf.high}: lower and upper bound of the effect size confidence interval.} } \description{ Compute the effect size for Kruskal-Wallis test as the eta squared based on the H-statistic: \code{eta2[H] = (H - k + 1)/(n - k)}; where \code{H} is the value obtained in the Kruskal-Wallis test; \code{k} is the number of groups; \code{n} is the total number of observations. The eta-squared estimate assumes values from 0 to 1 and multiplied by 100% indicates the percentage of variance in the dependent variable explained by the independent variable. The interpretation values commonly in published litterature are: \code{0.01- < 0.06} (small effect), \code{0.06 - < 0.14} (moderate effect) and \code{>= 0.14} (large effect). Confidence intervals are calculated by bootstap. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth # Kruskal-wallis rank sum test #::::::::::::::::::::::::::::::::::::::::: df \%>\% kruskal_effsize(len ~ dose) # Grouped data df \%>\% group_by(supp) \%>\% kruskal_effsize(len ~ dose) } \references{ Maciej Tomczak and Ewa Tomczak. The need to report effect size estimates revisited. An overview of some recommended measures of effect size. Trends in Sport Sciences. 2014; 1(21):19-25. http://imaging.mrc-cbu.cam.ac.uk/statswiki/FAQ/effectSize http://www.psy.gla.ac.uk/~steve/best/effect.html } rstatix/man/friedman_effsize.Rd0000644000176200001440000000506013640215135016313 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/friedman_effsize.R \name{friedman_effsize} \alias{friedman_effsize} \title{Friedman Test Effect Size (Kendall's W Value)} \usage{ friedman_effsize( data, formula, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ... ) } \arguments{ \item{data}{a data.frame containing the variables in the formula.} \item{formula}{a formula of the form \code{a ~ b | c}, where \code{a} (numeric) is the dependent variable name; \code{b} is the within-subjects factor variables; and \code{c} (factor) is the column name containing individuals/subjects identifier. Should be unique per individual.} \item{ci}{If TRUE, returns confidence intervals by bootstrap. May be slow.} \item{conf.level}{The level for the confidence interval.} \item{ci.type}{The type of confidence interval to use. Can be any of "norm", "basic", "perc", or "bca". Passed to \code{boot::boot.ci}.} \item{nboot}{The number of replications to use for bootstrap.} \item{...}{other arguments passed to the function \code{\link[stats]{friedman.test}()}} } \value{ return a data frame with some of the following columns: \itemize{ \item \code{.y.}: the y variable used in the test. \item \code{n}: Sample counts. \item \code{effsize}: estimate of the effect size. \item \code{magnitude}: magnitude of effect size. \item \code{conf.low,conf.high}: lower and upper bound of the effect size confidence interval.} } \description{ Compute the effect size estimate (referred to as \code{w}) for Friedman test: \code{W = X2/N(K-1)}; where \code{W} is the Kendall's W value; \code{X2} is the Friedman test statistic value; \code{N} is the sample size. \code{k} is the number of measurements per subject. The Kendall’s W coefficient assumes the value from 0 (indicating no relationship) to 1 (indicating a perfect relationship). Kendalls uses the Cohen’s interpretation guidelines of \code{0.1 - < 0.3} (small effect), \code{0.3 - < 0.5} (moderate effect) and \code{>= 0.5} (large effect) Confidence intervals are calculated by bootstap. } \examples{ # Load data #::::::::::::::::::::::::::::::::::::::: data("ToothGrowth") df <- ToothGrowth \%>\% filter(supp == "VC") \%>\% mutate(id = rep(1:10, 3)) head(df) # Friedman test effect size #::::::::::::::::::::::::::::::::::::::::: df \%>\% friedman_effsize(len ~ dose | id) } \references{ Maciej Tomczak and Ewa Tomczak. The need to report effect size estimates revisited. An overview of some recommended measures of effect size. Trends in Sport Sciences. 2014; 1(21):19-25. } rstatix/DESCRIPTION0000644000176200001440000000624714011734072013466 0ustar liggesusersPackage: rstatix Type: Package Title: Pipe-Friendly Framework for Basic Statistical Tests Version: 0.7.0 Authors@R: c( person("Alboukadel", "Kassambara", role = c("aut", "cre"), email = "alboukadel.kassambara@gmail.com")) Description: Provides a simple and intuitive pipe-friendly framework, coherent with the 'tidyverse' design philosophy, for performing basic statistical tests, including t-test, Wilcoxon test, ANOVA, Kruskal-Wallis and correlation analyses. The output of each test is automatically transformed into a tidy data frame to facilitate visualization. Additional functions are available for reshaping, reordering, manipulating and visualizing correlation matrix. Functions are also included to facilitate the analysis of factorial experiments, including purely 'within-Ss' designs (repeated measures), purely 'between-Ss' designs, and mixed 'within-and-between-Ss' designs. It's also possible to compute several effect size metrics, including "eta squared" for ANOVA, "Cohen's d" for t-test and 'Cramer V' for the association between categorical variables. The package contains helper functions for identifying univariate and multivariate outliers, assessing normality and homogeneity of variances. License: GPL-2 LazyData: TRUE Encoding: UTF-8 Depends: R (>= 3.3.0) Imports: stats, utils, tidyr (>= 1.0.0), purrr, broom (>= 0.7.4), rlang (>= 0.3.1), tibble (>= 2.1.3), dplyr (>= 0.7.1), magrittr, corrplot, tidyselect (>= 1.0.0), car, generics (>= 0.0.2) Suggests: knitr, rmarkdown, ggpubr, graphics, emmeans, coin, boot, testthat, spelling URL: https://rpkgs.datanovia.com/rstatix/ BugReports: https://github.com/kassambara/rstatix/issues RoxygenNote: 7.1.0 Collate: 'utilities.R' 'add_significance.R' 'adjust_pvalue.R' 'factorial_design.R' 'utilities_two_sample_test.R' 'anova_summary.R' 'anova_test.R' 'as_cor_mat.R' 'binom_test.R' 'box_m.R' 'chisq_test.R' 'cochran_qtest.R' 'cohens_d.R' 'cor_as_symbols.R' 'replace_triangle.R' 'pull_triangle.R' 'cor_mark_significant.R' 'cor_mat.R' 'cor_plot.R' 'cor_reorder.R' 'cor_reshape.R' 'cor_select.R' 'cor_test.R' 'counts_to_cases.R' 'cramer_v.R' 'df.R' 'doo.R' 't_test.R' 'dunn_test.R' 'emmeans_test.R' 'eta_squared.R' 'factors.R' 'fisher_test.R' 'freq_table.R' 'friedman_test.R' 'friedman_effsize.R' 'games_howell_test.R' 'get_comparisons.R' 'get_manova_table.R' 'get_mode.R' 'get_pvalue_position.R' 'get_summary_stats.R' 'get_test_label.R' 'kruskal_effesize.R' 'kruskal_test.R' 'levene_test.R' 'mahalanobis_distance.R' 'make_clean_names.R' 'mcnemar_test.R' 'multinom_test.R' 'outliers.R' 'p_value.R' 'prop_test.R' 'prop_trend_test.R' 'reexports.R' 'remove_ns.R' 'sample_n_by.R' 'shapiro_test.R' 'sign_test.R' 'tukey_hsd.R' 'utils-manova.R' 'utils-pipe.R' 'welch_anova_test.R' 'wilcox_effsize.R' 'wilcox_test.R' Language: en-US NeedsCompilation: no Packaged: 2021-02-13 11:06:55 UTC; kassambara Author: Alboukadel Kassambara [aut, cre] Maintainer: Alboukadel Kassambara Repository: CRAN Date/Publication: 2021-02-13 11:30:02 UTC rstatix/tests/0000755000176200001440000000000014011731317013110 5ustar liggesusersrstatix/tests/spelling.R0000644000176200001440000000020413534011144015042 0ustar liggesusersif(requireNamespace('spelling', quietly = TRUE)) spelling::spell_check_test(vignettes = TRUE, error = FALSE, skip_on_cran = TRUE) rstatix/tests/testthat/0000755000176200001440000000000014011734072014751 5ustar liggesusersrstatix/tests/testthat/test-add_x_position.R0000644000176200001440000001351613736010746021072 0ustar liggesuserscontext("test-add_x_position") # Data preparation data("ToothGrowth") df <- ToothGrowth df$dose <- as.factor(df$dose) test_that("add_x_position works for any data frame with group1 and group2 cols", { stat.test <- data.frame( stringsAsFactors = FALSE, group1 = c("0.5", "0.5", "1"), group2 = c("1", "2", "2"), p = c(1.27e-07, 4.4e-14, 1.91e-05) ) %>% add_x_position() expect_equal(stat.test$xmin, c(1, 1, 2)) expect_equal(stat.test$xmax, c(2, 3, 3)) }) test_that("add_x_position works for rstatix in a basic ggplot setting", { stat.test <- df %>% t_test(len ~ supp) %>% add_x_position() expect_equal(stat.test$xmin, 1) expect_equal(stat.test$xmax, 2) }) test_that("add_x_position works for rstatix in a basic ggplot facet setting", { stat.test <- df %>% group_by(dose) %>% t_test(len ~ supp) %>% add_x_position(x = "supp") expect_equal(stat.test$xmin, c(1, 1, 1)) expect_equal(stat.test$xmax, c(2, 2, 2)) }) test_that("add_x_position works for comparison against reference groups", { stat.test <- df %>% t_test(len ~ dose, ref.group = "0.5") %>% add_x_position(x = "dose") expect_equal(stat.test$xmin, c(1, 1)) expect_equal(stat.test$xmax, c(2, 3)) }) test_that("add_x_position works for comparison against all (basemean)", { stat.test <- df %>% t_test(len ~ dose, ref.group = "all") %>% add_x_position(x = "dose") expect_equal(stat.test$xmin, c(1, 2, 3)) expect_equal(stat.test$xmax, c(1,2, 3)) }) test_that("add_x_position works for comparison against null (one-sample test)", { stat.test <- df %>% group_by(dose) %>% t_test(len ~ 1) %>% add_x_position(x = "dose") expect_equal(stat.test$x, c(1, 2, 3)) }) test_that("add_x_position works for specified comparisons of interest", { stat.test <- df %>% t_test(len ~ dose, comparisons = list(c("0.5", "1"), c("0.5", "2"))) %>% add_x_position(x = "dose") expect_equal(stat.test$xmin, c(1, 1)) expect_equal(stat.test$xmax, c(2,3)) }) test_that("add_x_position works for grouped plots: grouping by x-var and performing test between legend groups", { stat.test <- df %>% group_by(dose) %>% t_test(len ~ supp) %>% add_x_position(x = "dose") expect_equal(stat.test$x, c(1, 2, 3)) expect_equal(stat.test$xmin, c(0.8, 1.8, 2.8)) expect_equal(stat.test$xmax, c(1.2, 2.2, 3.2)) }) test_that("add_x_position works for grouped plots: grouping by legend-var and performing test between x-group", { stat.test <- df %>% group_by(supp) %>% t_test(len ~ dose) %>% add_x_position(x = "dose") expect_equal(stat.test$xmin, c(1, 1, 2, 1, 1, 2)) expect_equal(stat.test$xmax, c(2, 3, 3, 2, 3, 3)) }) test_that("Grouped pairwise tests: grouping by x-var and performing test between legend groups", { stat.test <- df %>% group_by(supp) %>% t_test(len ~ dose) %>% add_x_position(x = "supp", dodge = 0.8) expect_equal(stat.test$x, c(1, 1, 1, 2, 2, 2)) expect_equal(round(stat.test$xmin, 2), c(0.73, 0.73, 1, 1.73, 1.73, 2)) expect_equal(round(stat.test$xmax, 2), c(1, 1.27, 1.27, 2, 2.27, 2.27)) }) test_that("Grouped pairwise tests: grouping by x-var and performing test between legend groups using ref.group", { stat.test <- df %>% group_by(supp) %>% t_test(len ~ dose, ref.group = "0.5") %>% add_x_position(x = "supp", dodge = 0.8) expect_equal(stat.test$x, c(1, 1, 2, 2)) expect_equal(round(stat.test$xmin, 2), c(0.73, 0.73, 1.73, 1.73)) expect_equal(round(stat.test$xmax, 2), c(1, 1.27, 2, 2.27)) }) test_that("Grouped plots: test that add_x_position works with different number of groups at each x pos.", { # https://github.com/kassambara/ggpubr/issues/326 demo_data <- data.frame( stringsAsFactors = FALSE, Study = c("A","A","A","A","A","A", "A","A","A","A","B","B","B","B","B","B","B","B", "B","B","C","C","C","C","C","C","C","C","C", "C","C","C","C","C","C","D","D","D","D","D", "D","D","D","D","D","D","D","D","D","D"), Studytype = c("X","X","X","X","X","X", "X","X","X","X","X","X","X","X","X","X","X","X", "X","X","Y","Y","Y","Y","Y","Y","Y","Y","Y", "Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y", "Y","Y","Y","Y","Y","Y","Y","Y","Y","Y"), Values = c(4469L,4797L,5101L,5397L, 4542L,2780L,4326L,3396L,3657L,3199L,9221L,10176L, 9277L,10500L,9707L,7406L,7756L,7601L,7586L,7353L, 1811L,1485L,3003L,1629L,2495L,4207L,4265L,3629L, 4157L,3495L,2075L,2112L,2973L,3086L,2943L,5664L,6690L, 3538L,5741L,7880L,5848L,6390L,6569L,6114L,6520L, 7389L,6843L,7611L,6621L,7340L), Group = as.factor(c("CTR", "CTR","CTR","CTR","CTR","Dis1","Dis1","Dis1", "Dis1","Dis1","CTR","CTR","CTR","CTR", "CTR","Dis1","Dis1","Dis1","Dis1","Dis1", "CTR","CTR","CTR","CTR","CTR","Dis2","Dis2", "Dis2","Dis2","Dis2","Dis3","Dis3", "Dis3","Dis3","Dis3","CTR","CTR","CTR","CTR", "CTR","Dis2","Dis2","Dis2","Dis2","Dis2", "Dis3","Dis3","Dis3","Dis3","Dis3")) ) stat.test <- demo_data %>% group_by(Study) %>% wilcox_test(Values ~ Group, ref.group = "CTR") %>% add_significance("p") stat.test <- stat.test %>% add_xy_position(x = "Study", dodge = 0.8) #bxp <- ggpubr::ggboxplot(demo_data, x = "Study", y = "Values", fill = "Group") + #ggpubr::stat_pvalue_manual(stat.test, label = "p") stat.test$x <- round(stat.test$x, 2) stat.test$xmin <- round(stat.test$xmin, 2) stat.test$xmax <- round(stat.test$xmax, 2) expect_equal(stat.test$x, c(1, 2, 3, 3, 4, 4)) expect_equal(stat.test$xmin, c(0.8, 1.8, 2.73, 2.73, 3.73, 3.73)) expect_equal(stat.test$xmax, c(1.2, 2.2, 3, 3.27, 4, 4.27)) }) rstatix/tests/testthat/test-get_n.R0000644000176200001440000000114713705506147017161 0ustar liggesuserscontext("test-get_n") test_that("Checking that get_n works for T-test", { data("ToothGrowth") stat.test <- ToothGrowth %>% t_test(len ~ dose) expect_equal(get_n(stat.test), c(40, 40, 40)) }) test_that("Checking that get_n works for grouped T-test", { data("ToothGrowth") stat.test <- ToothGrowth %>% group_by(dose) %>% t_test(len ~ supp) expect_equal(get_n(stat.test), c(20, 20, 20)) }) test_that("Checking that get_n works for grouped ANOVA", { data("ToothGrowth") res.aov <- ToothGrowth %>% group_by(supp) %>% anova_test(len ~ dose) expect_equal(get_n(res.aov), c(30, 30)) }) rstatix/tests/testthat/test-remove_ns.R0000644000176200001440000000341213736576423020067 0ustar liggesuserscontext("test-remove_ns") stat.test <- PlantGrowth %>% wilcox_test(weight ~ group) test_that("remove_ns works when col = NULL", { result <- remove_ns(stat.test, col = NULL) p <- round(result$p, 3) expect_equal(p, 0.009) }) test_that("remove_ns works when col = NA", { result <- remove_ns(stat.test, col = NA) p <- round(result$p, 3) expect_equal(p, 0.009) }) test_that("remove_ns works when col is logical", { result <- remove_ns(stat.test, col = TRUE) p.true <- round(result$p, 3) result <- remove_ns(stat.test, col = FALSE) p.false <- round(result$p, 3) expect_equal(p.true, 0.009) expect_equal(p.false, c(0.199, 0.063, 0.009)) }) test_that("remove_ns works when col is specified", { stat.test2 <- stat.test %>% add_significance("p") result.when.p <- remove_ns(stat.test2, col = "p") result.when.p.adj <- remove_ns(stat.test2, col = "p.adj") result.when.p.adj.signif <- remove_ns(stat.test2, col = "p.adj.signif") result.when.p.signif <- remove_ns(stat.test2, col = "p.signif") expect_equal(result.when.p$p.signif, "**") expect_equal(result.when.p.adj$p.signif, "**") expect_equal(result.when.p.adj.signif$p.signif, "**") expect_equal(result.when.p.signif$p.signif, "**") }) test_that("remove_ns works when signif.cutoff is specified", { stat.test2 <- stat.test %>% add_significance("p") result <- remove_ns(stat.test2, signif.cutoff = 0.01) expect_equal(nrow(result), 0) }) test_that("remove_ns works when signif.cutoff and col are specified", { stat.test2 <- stat.test %>% add_significance("p") result1 <- remove_ns(stat.test2, col = "p.adj", signif.cutoff = 0.01) result2 <- remove_ns(stat.test2, col = "p", signif.cutoff = 0.01) expect_equal(nrow(result1), 0) expect_equal(nrow(result2), 1) expect_equal(result2$p.signif, "**") }) rstatix/tests/testthat/test-levene_test.R0000644000176200001440000000063314011707200020361 0ustar liggesuserscontext("test-levene_test") test_that("Levene test output is correctly formatted", { # Prepare the data data("ToothGrowth") df <- ToothGrowth df$dose <- as.factor(df$dose) # Compute Levene's Test results <- df %>% levene_test(len ~ dose) expect_equal(results$df1, 2) expect_equal(results$df2, 57) expect_equal(round(results$statistic, 3), 0.646) expect_equal(round(results$p, 3), 0.528) }) rstatix/tests/testthat/test-p_mark_significance.R0000644000176200001440000000024413710566360022034 0ustar liggesuserscontext("test-p_mark_significance") test_that("p_mark_significance works when NA only", { na_signif <- p_mark_significant(NA) expect_equal(na_signif, "NA") }) rstatix/tests/testthat/test-anova_test.R0000644000176200001440000000674413700036616020232 0ustar liggesuserscontext("test-anova_test") test_that("Checking one-way ANOVA test", { data("ToothGrowth") res.aov <- ToothGrowth %>% anova_test(len ~ dose) expect_equal(res.aov$Effect, "dose") expect_equal(res.aov$DFn, 1) expect_equal(res.aov$DFd, 58) expect_equal(res.aov$F, 105.065) expect_equal(res.aov$ges, 0.644) }) test_that("Checking grouped one-way ANOVA test", { data("ToothGrowth") res.aov <- ToothGrowth %>% group_by(supp) %>% anova_test(len ~ dose) expect_equal(res.aov$Effect, c("dose", "dose")) expect_equal(res.aov$DFn, c(1, 1)) expect_equal(res.aov$DFd, c(28, 28)) expect_equal(res.aov$F, c(36.013, 117.948)) expect_equal(res.aov$ges, c(0.563, 0.808)) }) test_that("Checking two-way ANOVA test", { data("ToothGrowth") res.aov <- ToothGrowth %>% anova_test(len ~ supp*dose) expect_equal(res.aov$Effect, c("supp", "dose", "supp:dose")) expect_equal(res.aov$DFn, c(1, 1, 1)) expect_equal(res.aov$DFd, c(56, 56, 56)) expect_equal(res.aov$F, c(12.317, 133.415, 5.333)) expect_equal(res.aov$p, c(8.94e-04, 1.91e-16, 2.50e-02)) expect_equal(res.aov$ges, c(0.180, 0.704, 0.087)) }) test_that("Checking repeated measures ANOVA test", { data("ToothGrowth") df <- ToothGrowth df$id <- rep(1:10, 6) res.aov <- df %>% anova_test(dv = len, wid = id, within = c(supp, dose)) anova.table <- res.aov$ANOVA sphericity <- res.aov$`Mauchly's Test for Sphericity` corrections <- res.aov$`Sphericity Corrections` expect_equal(anova.table$Effect, c("supp", "dose", "supp:dose")) expect_equal(anova.table$DFn, c(1, 2, 2)) expect_equal(anova.table$DFd, c(9, 18, 18)) expect_equal(anova.table$F, c(34.866, 106.470, 2.534)) expect_equal(anova.table$p, c(2.28e-04, 1.06e-10, 1.07e-01)) expect_equal(anova.table$ges, c(0.224, 0.773, 0.132)) expect_equal(sphericity$W, c(0.807, 0.934)) expect_equal(corrections$GGe, c(0.838, 0.938)) expect_equal(corrections$HFe, c(1.008, 1.176)) }) test_that("Checking that get_anova_table works with any data frame", { data("ToothGrowth") expect_is(get_anova_table(ToothGrowth), "data.frame") }) test_that("Checking that get_anova_table works for grouped repeated measures ANOVA", { data("ToothGrowth") df <- ToothGrowth df$id <- rep(1:10, 6) res.aov <- df %>% group_by(supp) %>% anova_test(dv = len, wid = id, within = dose) aov.table <- get_anova_table(res.aov) expect_equal(aov.table$F, c(23.936, 57.783)) }) test_that("Checking that get_anova_table performs auto sphericity correction", { data("ToothGrowth") df <- ToothGrowth df$id <- rep(1:10, 6) res.aov <- df %>% anova_test(dv = len, wid = id, within = c(supp, dose)) res.aov2 <- res.aov res.aov2$`Mauchly's Test for Sphericity`$p[1] <- 0.05 # significant # Correction not applied, because there is not significant sphericity test auto <- get_anova_table(res.aov, correction = "auto") expect_equal(auto$DFn, c(1, 2, 2)) expect_equal(auto$DFd, c(9, 18, 18)) expect_equal(auto$F, c(34.866, 106.470, 2.534)) # Correction automatically applied to the DF of the effect where sphericity is signiica,t auto2 <- get_anova_table(res.aov2, correction = "auto") expect_equal(auto2$DFn, c(1, 1.68, 2)) expect_equal(auto2$DFd, c(9, 15.09, 18)) expect_equal(auto2$F, c(34.866, 106.470, 2.534)) # Check that GG correction works for all within-subject variables gg <- get_anova_table(res.aov2, correction = "GG") expect_equal(gg$DFn, c(1, 1.68, 1.88)) expect_equal(gg$DFd, c(9, 15.09, 16.88)) expect_equal(gg$p, c(2.28e-04, 2.79e-09, 1.12e-01)) }) rstatix/tests/testthat/test-wilcox_test.R0000644000176200001440000000527213710031607020422 0ustar liggesuserscontext("test-wilcox-test") test_that("Checking one-sample test", { data("ToothGrowth") res <- ToothGrowth %>% wilcox_test(len ~ 1, mu = 0) expect_equal(res$group1, "1") expect_equal(res$group2, "null model") expect_equal(res$n, 60) expect_equal(as.numeric(res$statistic), 1830) expect_equal(signif(res$p, 3), 1.66e-11) }) test_that("Checking two-sample unpaired test", { data("ToothGrowth") res <- ToothGrowth %>% wilcox_test(len ~ supp) expect_equal(res$group1, "OJ") expect_equal(res$group2, "VC") expect_equal(res$n1, 30) expect_equal(res$n2, 30) expect_equal(as.numeric(res$statistic), 575.5) expect_equal(signif(res$p, 3), 0.0645) }) test_that("Checking two-sample paired test", { data("ToothGrowth") res <- ToothGrowth %>% wilcox_test(len ~ supp, paired = TRUE) expect_equal(res$group1, "OJ") expect_equal(res$group2, "VC") expect_equal(res$n1, 30) expect_equal(res$n2, 30) expect_equal(as.numeric(res$statistic), 350) expect_equal(signif(res$p, 3), 0.00431) }) test_that("Checking pairwise comparisons", { data("ToothGrowth") res <- ToothGrowth %>% wilcox_test(len ~ dose) expect_equal(res$group1, c("0.5", "0.5", "1")) expect_equal(res$group2, c("1", "2", "2")) expect_equal(res$n1, c(20, 20, 20)) expect_equal(res$n2, c(20, 20, 20)) expect_equal(as.numeric(res$statistic), c(33.5, 1.5, 61.0)) expect_equal(signif(res$p, 3), c(7.02e-6, 8.41e-08, 1.77e-04)) }) test_that("Checking pairwise comparison against ref group", { data("ToothGrowth") res <- ToothGrowth %>% wilcox_test(len ~ dose, ref.group = "0.5") expect_equal(res$group1, c("0.5", "0.5")) expect_equal(res$group2, c("1", "2")) expect_equal(res$n1, c(20, 20)) expect_equal(res$n2, c(20, 20)) expect_equal(as.numeric(res$statistic), c(33.5, 1.5)) expect_equal(signif(res$p, 3), c(7.02e-6, 8.41e-08)) }) test_that("Checking pairwise comparisons against all", { data("ToothGrowth") res <- ToothGrowth %>% wilcox_test(len ~ dose, ref.group = "all") expect_equal(res$group1, c("all", "all", "all")) expect_equal(res$group2, c("0.5", "1", "2")) expect_equal(res$n1, c(60, 60, 60)) expect_equal(res$n2, c(20, 20, 20)) expect_equal(as.numeric(res$statistic), c(965.0, 572.5, 262.5)) expect_equal(signif(res$p, 3), c(0.0000508, 0.764, 0.000179)) }) test_that("Checking grouped tests", { data("ToothGrowth") res <- ToothGrowth %>% group_by(dose) %>% wilcox_test(len ~ supp) expect_equal(res$group1, c("OJ", "OJ", "OJ")) expect_equal(res$group2, c("VC", "VC", "VC")) expect_equal(res$n1, c(10, 10, 10)) expect_equal(res$n2, c(10, 10, 10)) expect_equal(as.numeric(res$statistic), c(80.5, 88.5, 49.5)) expect_equal(signif(res$p, 3), c(0.0232, 0.00403, 1)) }) rstatix/tests/testthat.R0000644000176200001440000000007213533777631015113 0ustar liggesuserslibrary(testthat) library(rstatix) test_check("rstatix") rstatix/R/0000755000176200001440000000000014011725421012146 5ustar liggesusersrstatix/R/get_comparisons.R0000644000176200001440000000353613477040521015502 0ustar liggesusers#' @include utilities.R NULL #'Create a List of Possible Comparisons Between Groups #'@description Create a list of possible pairwise comparisons between groups. If #' a reference group is specified, only comparisons against reference will be #' kept. #'@param data a data frame #'@param variable the grouping variable name. Can be unquoted. #'@param ref.group a character string specifying the reference group. Can be #' unquoted. If numeric, then it should be quoted. If specified, for a #' given grouping variable, each of the group levels will be compared to the #' reference group (i.e. control group). #' #' If \code{ref.group = "all"}, pairwise comparisons are performed between each #' grouping variable levels against all (i.e. basemean). #'@return a list of all possible pairwise comparisons. #'@examples #' # All possible pairwise comparisons #' ToothGrowth %>% #' get_comparisons("dose") #' #' # Comparisons against reference groups #' ToothGrowth %>% #' get_comparisons("dose", ref.group = "0.5") #' #' # Comparisons against all (basemean) #' ToothGrowth %>% #' get_comparisons("dose", ref.group = "all") #'@export get_comparisons <- function(data, variable, ref.group = NULL){ group <- rlang::enquo(variable) %>% rlang::as_name() ref.group <- rlang::enquo(ref.group) if(rlang::quo_is_null(ref.group)) ref.group <- NULL else ref.group <- rlang::as_name(ref.group) group.levels <- data %>% .as_factor(group) %>% get_levels(group) asserttat_ref_group_iscorrect(group.levels, ref.group) comparisons <- c(ref.group, group.levels) %>% unique() %>% .possible_pairs(ref.group = ref.group) %>% map(as.character) comparisons } asserttat_ref_group_iscorrect <- function(.levels, .ref){ if(!is.null(.ref)){ .diff <- setdiff(.ref, c("all", ".all.", .levels)) if(!.is_empty(.diff)) stop("ref.group is incorrect") } } rstatix/R/factors.R0000644000176200001440000000565013523754321013750 0ustar liggesusers#' @include utilities.R NULL #'Factors #' #'@description Provides pipe-friendly functions to convert simultaneously #' multiple variables into a factor variable. #' #' Helper functions are also available to set the reference level and the #' levels order. #' #'@param data a data frame #'@param ... one unquoted expressions (or variable name) specifying the name of #' the variables you want to convert into factor. Alternative to the argument #' \code{vars}. #'@param vars a character vector specifying the variables to convert into #' factor. #'@param name a factor variable name. Can be unquoted. For example, use #' \code{group} or \code{"group"}. #'@param ref the reference level. #'@param order a character vector specifying the order of the factor levels #'@param make.valid.levels logical. Default is FALSE. If TRUE, converts the #' variable to factor and add a leading character (x) if starting with a digit. #'@examples #' # Create a demo data #' df <- tibble( #' group = c("a", "a", "b", "b", "c", "c"), #' time = c("t1", "t2", "t1", "t2", "t1", "t2"), #' value = c(5, 6, 1, 3, 4, 5) #' ) #' df #' # Convert group and time into factor variable #' result <- df %>% convert_as_factor(group, time) #' result #' # Show group levels #' levels(result$group) #' #' # Set c as the reference level (the first one) #' result <- result %>% #' set_ref_level("group", ref = "c") #' levels(result$group) #' #' # Set the order of levels #' result <- result %>% #' reorder_levels("group", order = c("b", "c", "a")) #' levels(result$group) #' #' @describeIn factors Convert one or multiple variables into factor. #' @export convert_as_factor <- function(data, ..., vars = NULL, make.valid.levels = FALSE){ vars <- c(get_dot_vars(...), vars) %>% unique() if(.is_empty(vars)){ return(data) } if(make.valid.levels){ for(variable in vars) { data <- make_valid_levels(data, variable) } } else{ data <- data %>% dplyr::mutate_at(vars, as.factor) } data } #' @describeIn factors Change a factor reference level or group. #' @export set_ref_level <- function(data, name, ref){ .args <- rlang::enquos(name = name) %>% select_quo_variables(data) data[[.args$name]] <- stats::relevel(data[[.args$name]], ref) data } #' @describeIn factors Change the order of a factor levels #' @export reorder_levels <- function(data, name, order){ .args <- rlang::enquos(name = name) %>% select_quo_variables(data) data[[.args$name]] <- factor(data[[.args$name]], levels = order) data } make_valid_levels <- function(data, name){ .args <- rlang::enquos(name = name) %>% select_quo_variables(data) name <- .args$name value <- data %>% pull(!!name) if(is.factor(value)){ levels(value) <- make.names(levels(value), unique = TRUE) } else{ value <- as.character(value) lab <- make.names(unique(value),unique=TRUE) value <- factor(value, levels = unique(value), labels = lab) } data[[name]] <- value data } rstatix/R/get_mode.R0000644000176200001440000000143513470227165014071 0ustar liggesusers#' @include utilities.R NULL #' Compute Mode #' #' @description Compute the mode in a given vector. Mode is the most frequent #' value. #' #' @param x a vector. Can be numeric, factor or character vector. #' #' @examples #' #' # Mode of numeric vector #' x <- c(1:5, 6, 6, 7:10) #' get_mode(x) #' #' # Bimodal #' x <- c(1:5, 6, 6, 7, 8, 9, 9, 10) #' get_mode(x) #' #' # No mode #' x <- c(1, 2, 3, 4, 5) #' get_mode(x) #' #' # Nominal vector #' fruits <- c(rep("orange", 10), rep("apple", 5), rep("lemon", 2)) #' get_mode(fruits) #' @export get_mode <- function(x){ .x <- factor(x) .table <- table(.x) .max <- max(.table) if(all(.table == .max)){ .mode <- NA } else{ .mode <- names(.table)[.table == .max] } if(is.numeric(x)){ .mode <- as.numeric(.mode) } .mode } rstatix/R/mcnemar_test.R0000644000176200001440000001271513640104727014767 0ustar liggesusers#' @include utilities.R NULL #'McNemar's Chi-squared Test for Count Data #'@description Performs McNemar chi-squared test to compare paired proportions. #' #' Wrappers around the R base function \code{\link[stats]{mcnemar.test}()}, but #' provide pairwise comparisons between multiple groups #'@inheritParams stats::mcnemar.test #'@param data a data frame containing the variables in the formula. #'@param formula a formula of the form \code{a ~ b | c}, where \code{a} is the #' outcome variable name; b is the within-subjects factor variables; and c #' (factor) is the column name containing individuals/subjects identifier. #' Should be unique per individual. #'@param type type of statistical tests used for pairwise comparisons. Allowed #' values are one of \code{c("mcnemar", "exact")}. #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #' #' #'@return return a data frame with the following columns: \itemize{ #' \item \code{n}: the number of participants. #' \item \code{statistic}: the value of McNemar's statistic. \item \code{df} the #' degrees of freedom of the approximate chi-squared distribution of the test #' statistic. \item \code{p}: p-value. \item \code{p.adj}: the adjusted #' p-value. \item \code{method}: the used statistical test. \item #' \code{p.signif}: the significance level of p-values.} #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' @examples #' #' # Comparing two paired proportions #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: frequencies of smokers before and after interventions #' xtab <- as.table( #' rbind(c(25, 6), c(21,10)) #' ) #' dimnames(xtab) <- list( #' before = c("non.smoker", "smoker"), #' after = c("non.smoker", "smoker") #' ) #' xtab #' #' # Compare the proportion of smokers #' mcnemar_test(xtab) #' #' # Comparing multiple related proportions #' # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Generate a demo data #' mydata <- data.frame( #' outcome = c(0,1,1,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,1,0,0,1), #' treatment = gl(3,1,30,labels=LETTERS[1:3]), #' participant = gl(10,3,labels=letters[1:10]) #' ) #' mydata$outcome <- factor( #' mydata$outcome, levels = c(1, 0), #' labels = c("success", "failure") #' ) #' # Cross-tabulation #' xtabs(~outcome + treatment, mydata) #' #' # Compare the proportion of success between treatments #' cochran_qtest(mydata, outcome ~ treatment|participant) #' #' # pairwise comparisons between groups #' pairwise_mcnemar_test(mydata, outcome ~ treatment|participant) #' #'@describeIn mcnemar_test performs McNemar's chi-squared test for comparing two #' paired proportions #'@export mcnemar_test <- function(x, y = NULL, correct = TRUE){ args <- as.list(environment()) %>% add_item(method = "mcnemar_test") if(is.data.frame(x)) x <- as.matrix(x) if(inherits(x, c("matrix", "table"))) n <- sum(x) else n <- length(x) results <- stats::mcnemar.test(x, y, correct) %>% as_tidy_stat() %>% add_significance("p") %>% mutate(method = "McNemar test", n = n) results[, c("n", "statistic", "df", "p", "p.signif", "method")] %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "mcnemar_test")) } #'@describeIn mcnemar_test performs pairwise McNemar's chi-squared test between #' multiple groups. Could be used for post-hoc tests following a significant Cochran's Q test. #'@export pairwise_mcnemar_test <- function (data, formula, type = c("mcnemar", "exact"), correct = TRUE, p.adjust.method = "bonferroni") { type <- match.arg(type) test.class <- switch (type, mcnemar = "mcnemar_test", exact = "exact_binom_test" ) data <- data %>% select(!!!syms(all.vars(formula))) colnames(data) <- c("outcome", "groups", "participant") if(length(unique(data$outcome))> 2){ stop("Unique possible outcome values should be 2") } args <- as.list(environment()) %>% add_item(method = test.class) # helper function to compar pairs compare_pair <- function(grps, data, type = "mcnemar"){ grps <- as.character(grps) grps.data <- data[, grps] colnames(grps.data) <- c("grp1", "grp2") xtab <- stats::xtabs(~grp1+grp2, grps.data) if(type == "mcnemar"){ results <- mcnemar_test(xtab, correct = correct) } else if(type == "exact"){ # Get off-diagonal values b <- xtab[2, 1] c <- xtab[1, 2] results <- binom_test(b, (b + c), p = 0.5, detailed = TRUE) } results %>% keep_only_tbl_df_classes() %>% select(.data$p, .data$method) %>% add_columns(group1 = grps[1], group2 = grps[2], .before = "p") } # Convert outcome into factor, then spread. data <- data %>% mutate(outcome = as.factor(.data$outcome)) %>% spread(key = "groups", value = "outcome") # Pairwise comparisons comparisons <- colnames(data)[-1] %>% .possible_pairs() results <- comparisons %>% map(compare_pair, data, type = type) %>% bind_rows() %>% adjust_pvalue("p", method = p.adjust.method) %>% add_significance("p.adj") %>% mutate(p.adj = signif(.data$p.adj, digits = 3)) results [, c("group1", "group2", "p", "p.adj", "p.adj.signif", "method")] %>% set_attrs(args = args) %>% add_class(c("rstatix_test", test.class)) } rstatix/R/factorial_design.R0000644000176200001440000002154213743642144015605 0ustar liggesusers#' @include utilities.R NULL #'Build Factorial Designs for ANOVA #' #' #'@description Provides helper functions to build factorial design for easily #' computing ANOVA using the \code{\link[car]{Anova}()} function. This might be #' very useful for repeated measures ANOVA, which is hard to set up with the #' \code{car} package. #'@inheritParams anova_test #'@param data a data frame containing the variables #'@return a list with the following components: \itemize{ \item \strong{the #' specified arguments}: \code{dv, wid, between, within} \item \strong{data}: #' the original data (long format) or independent ANOVA. The wide format is #' returned for repeated measures ANOVA. \item \strong{idata}: an optional data #' frame giving the levels of factors defining the intra-subject model for #' multivariate repeated-measures data. \item \strong{idesign}: a one-sided #' model formula using the “data” in idata and specifying the intra-subject #' design. \item \strong{repeated}: logical. Value is TRUE when the data is a #' repeated design. \item \strong{lm_formula}: the formula used to build the #' \code{lm} model. \item \strong{lm_data}: the data used to build the \code{lm} #' model. Can be either in a long format (i.e., the original data for #' independent measures ANOVA) or in a wide format (case of repeated measures ANOVA). \item \strong{model}: the \code{lm} model } #'@author Alboukadel Kassambara, \email{alboukadel.kassambara@@gmail.com} #'@seealso \code{\link{anova_test}()}, \code{\link{anova_summary}()} #'@examples #'# Load data #'#::::::::::::::::::::::::::::::::::::::: #'data("ToothGrowth") #'df <- ToothGrowth #' head(df) #' #'# Repeated measures designs #'#::::::::::::::::::::::::::::::::::::::::: #'# Prepare the data #'df$id <- rep(1:10, 6) # Add individuals id #'head(df) #'# Build factorial designs #'design <- factorial_design(df, dv = len, wid = id, within = c(supp, dose)) #'design #'# Easily perform repeated measures ANOVA using the car package #' res.anova <- Anova(design$model, idata = design$idata, idesign = design$idesign, type = 3) #' summary(res.anova, multivariate = FALSE) #' #'# Independent measures designs #'#::::::::::::::::::::::::::::::::::::::::: #'# Build factorial designs #' df$id <- 1:nrow(df) #' design <- factorial_design(df, dv = len, wid = id, between = c(supp, dose)) #' design #' # Perform ANOVA #' Anova(design$model, type = 3) #' #'@rdname factorial_design #'@export factorial_design <- function(data, dv, wid, between, within, covariate){ # Check factorial design %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% . <- NULL .args <- rlang::enquos(dv = dv, between = between, wid = wid, within = within, covariate = covariate) %>% get_quo_vars_list(data, .) %>% remove_null_items() %>% add_item(data = data) %>% check_factorial_design() dv <- .args$dv between <- .args$between within <- .args$within covariate <- .args$covariate data <- .args$data %>% select(!!!syms(c(.args$wid, dv, between, within, covariate))) rhs <- create_formula_right_hand_side(between, covariate) # Repeated measures designs %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if(!is.null(within)){ not.within.vars <- setdiff(colnames(data), within) nested <- data %>% group_by(!!!syms(within)) %>% nest() # Get intra-subject factor levels .args$idata <- nested %>% select(-data) %>% dplyr::arrange(!!!syms(within)) %>% as.data.frame() .args$idesign <- paste(within, collapse = "*") %>% paste0('~',.) %>% stats::as.formula() # Unite intra-subject factors into one grouping column, # then spread the data into wide format wide <- nested %>% tidyr::unite(!!!syms(within), col = ".group.", sep = "_") %>% select(.data$.group., data) %>% unnest() %>% spread(key = ".group.", value = dv) %>% as_tibble() .args$lm_data <- wide .args$repeated <- TRUE # Build model formula: orders of wide dv name and data colnames should match # dv are all possible combinations of within-subjects factor levels wide.dv.name <- setdiff(colnames(wide), not.within.vars) %>% paste(collapse = ", ") lm_formula <- paste0("cbind(", wide.dv.name, ") ~ ", rhs) } # Independent measures designs %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% else if(!is.null(between)){ .args$lm_data <- .args$data .args$repeated <- FALSE lm_formula <- paste0(dv, " ~ ", rhs) } # Fit lm lm_formula <- .args$lm_formula <- stats::as.formula(lm_formula) data <- .args$lm_data opt <- options( "contrasts" = c( "contr.sum", "contr.poly" ) ) .args$model <- stats::lm(lm_formula, data) options(opt) .args } create_formula_right_hand_side <- function(between, covariate = NULL){ covariate <- paste(covariate, collapse = "+") between <- paste(between, collapse = "*") bc.sep <- ifelse(covariate != "" & between != "", " + ", "") # Between and covariate vars separator rhs <- paste0(covariate, bc.sep, between) if(rhs == "") rhs <- "1" rhs } # Cheking the design #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% check_factorial_design <- function(.args){ if(!inherits(.args$data, "data.frame")){ stop('data should be a data frame.') } if(is.null(.args$within) & is.null(.args$between)){ stop("Specify at least one of the arguments: 'within' and 'between'") } .args$data <- droplevels(.args$data) %>% as_tibble() .args <- .args %>% remove_missing_values_in_data() %>% assertthat_dv_is_numeric() %>% assertthat_wid_is_specified() %>% asserthat_wid_is_unique() %>% convert_grouping_vars_to_factor() %>% assertthat_iv_has_enough_levels() .args } # Remove missing values remove_missing_values_in_data <- function(.args){ model.variables <- c( .args$dv, .args$wid, .args$between, .args$within, .args$covariate ) complete.rows <- stats::complete.cases(.args$data[, model.variables]) na.rows <- which(!complete.rows) na.exists <- length(na.rows) > 0 if(na.exists){ warning( "NA detected in rows: ", paste(na.rows, collapse = ","), ".", "\nRemoving this rows before the analysis.", call. = FALSE ) .args$data <- .args$data[complete.rows, ] } .args } # Make sure the dependent variable (dv) is numeric assertthat_dv_is_numeric <- function(.args){ if(is.null(.args$dv)){ stop("The dependent variable argument 'dv' is required") } dv.data <- .args$data %>% select(!!!syms(.args$dv)) if(!is_all_columns_numeric(dv.data)){ stop("The dependent variable 'dv' should be numeric") } invisible(.args) } # Make sure that the id is provided, otherwise # Create it in the case of between-Ss ANOVA assertthat_wid_is_specified <- function(.args){ if(is.null(.args$wid)){ if(!is.null(.args$within)){ stop("Specify the argument 'wid'", ", required for repeated measures ANOVA") } else{ .args$wid <- ".id." .args$data$.id. <- factor(1:nrow(.args$data)) } } .args } # Check if individual id is unique in each between groups # otherwise, create unique id accross between groups asserthat_wid_is_unique <- function(.args){ if(is.null(.args$between)) return(.args) if(!is_id_unique_by_between_vars(.args)){ warning("The 'wid' column contains duplicate ids across ", "between-subjects variables. ", "Automatic unique id will be created", immediate. = TRUE, call. = FALSE) wid <- .args$wid .args$data <- .args$data %>% mutate(!!wid := create_uniqueId_by_bteween_vars(.args)) } .args } is_id_unique_by_between_vars <- function(.args){ data <- .args$data wid <- .args$wid between <- .args$between # Split the data by between variables nested <- data %>% group_by(!!!syms(between)) %>% nest() %>% mutate(data = map(.data$data, dplyr::distinct, !!sym(wid), .keep_all = TRUE)) # Check that id is unique accross between groups freq <- nested %>% unnest() %>% group_by(!!!syms(c(wid))) %>% summarise(count = n()) %>% pull(.data$count) all(freq == 1) } create_uniqueId_by_bteween_vars <- function(.args){ data <- .args$data vars <- c(.args$wid, .args$between) data %>% select(!!!syms(vars)) %>% dplyr::mutate_all(as.character) %>% purrr::pmap(paste, sep = ".") %>% unlist() %>% factor() } # Make sure that independent variables (iv) has more than one levels assertthat_iv_has_enough_levels <- function(.args){ vars <- c(.args$within, .args$between) data <- .args$data for(.var in vars){ n.levels <- unique(data[[.var]]) %>% length() if(n.levels == 1){ stop("Variable ", .var, "has only one level. ", "Remove it from the model.") } } .args } # Convert the grouping variables to factor convert_grouping_vars_to_factor <- function(.args){ .args$data <- .args$data %>% convert_as_factor(vars = c(.args$wid, .args$between)) %>% convert_as_factor(vars = .args$within, make.valid.levels = TRUE) .args } rstatix/R/wilcox_test.R0000644000176200001440000001676313654634511014665 0ustar liggesusers#' @include utilities.R utilities_two_sample_test.R #' @importFrom stats wilcox.test NULL #'Wilcoxon Tests #' #' #'@description Provides a pipe-friendly framework to performs one and two sample #' Wilcoxon tests. Read more: #' \href{https://www.datanovia.com/en/lessons/wilcoxon-test-in-r/}{Wilcoxon in #' R}. #'@inheritParams stats::wilcox.test #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{x ~ group} where \code{x} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ cancer_group}. #'@param paired a logical indicating whether you want a paired test. #'@param ref.group a character string specifying the reference group. If #' specified, for a given grouping variable, each of the group levels will be #' compared to the reference group (i.e. control group). #' #' If \code{ref.group = "all"}, pairwise two sample tests are performed for #' comparing each grouping variable levels against all (i.e. basemean). #'@param mu a number specifying an optional parameter used to form the null #' hypothesis. #'@param comparisons A list of length-2 vectors specifying the groups of #' interest to be compared. For example to compare groups "A" vs "B" and "B" vs #' "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", #' "C"))} #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #' #'@param detailed logical value. Default is FALSE. If TRUE, a detailed result is #' shown. #'@param ... other arguments to be passed to the function #' \code{\link[stats]{wilcox.test}}. #' #'@details - \code{pairwise_wilcox_test()} applies the standard two sample #' Wilcoxon test to all possible pairs of groups. This method calls the #' \code{\link[stats]{wilcox.test}()}, so extra arguments are accepted. #' #' #' - If a list of comparisons is specified, the result of the pairwise tests is #' filtered to keep only the comparisons of interest.The p-value is adjusted #' after filtering. #' #' - For a grouped data, if pairwise test is performed, then the p-values are #' adjusted for each group level independently. #' #' #' - a nonparametric confidence interval and an estimator for the pseudomedian #' (one-sample case) or for the difference of the location parameters #' \code{x-y} is computed, where x and y are the compared samples or groups. #' The column \code{estimate} and the confidence intervals are displayed in the #' test result when the option \code{detailed = TRUE} is specified in the #' \code{wilcox_test()} and \code{pairwise_wilcox_test()} functions. Read more #' about the calculation of the estimate in the details section of the R base #' function \code{wilcox.test()} documentation by typing \code{?wilcox.test} in #' the R console. #' #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y variable used in the test. \item #' \code{group1,group2}: the compared groups in the pairwise tests. \item #' \code{n,n1,n2}: Sample counts. \item \code{statistic}: Test statistic used #' to compute the p-value. \item \code{p}: p-value. \item \code{p.adj}: the #' adjusted p-value. \item \code{method}: the statistical test used to compare #' groups. \item \code{p.signif, p.adj.signif}: the significance level of #' p-values and adjusted p-values, respectively. \item \code{estimate}: an #' estimate of the location parameter (Only present if argument \code{detailed #' = TRUE}). This corresponds to the pseudomedian (for one-sample case) or to #' the difference of the location parameter (for two-samples case). \itemize{ #' \item The pseudomedian of a distribution \code{F} is the median of the #' distribution of \code{(u+v)/2}, where \code{u} and {v} are independent, each #' with distribution \code{F}. If \code{F} is symmetric, then the pseudomedian #' and median coincide. \item Note that in the two-sample case the estimator #' for the difference in location parameters does not estimate the difference #' in medians (a common misconception) but rather the median of the difference #' between a sample from x and a sample from y. } \item \code{conf.low, #' conf.high}: a confidence interval for the location parameter. (Only present #' if argument conf.int = TRUE.) } #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # One-sample test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% wilcox_test(len ~ 1, mu = 0) #' #' #' # Two-samples unpaired test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% wilcox_test(len ~ supp) #' #' # Two-samples paired test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% wilcox_test (len ~ supp, paired = TRUE) #' #' # Compare supp levels after grouping the data by "dose" #' #:::::::::::::::::::::::::::::::::::::::: #' df %>% #' group_by(dose) %>% #' wilcox_test(data =., len ~ supp) %>% #' adjust_pvalue(method = "bonferroni") %>% #' add_significance("p.adj") #' #' # pairwise comparisons #' #:::::::::::::::::::::::::::::::::::::::: #' # As dose contains more than two levels ==> #' # pairwise test is automatically performed. #' df %>% wilcox_test(len ~ dose) #' #' # Comparison against reference group #' #:::::::::::::::::::::::::::::::::::::::: #' # each level is compared to the ref group #' df %>% wilcox_test(len ~ dose, ref.group = "0.5") #' #' # Comparison against all #' #:::::::::::::::::::::::::::::::::::::::: #' df %>% wilcox_test(len ~ dose, ref.group = "all") #' #'@describeIn wilcox_test Wilcoxon test #'@export wilcox_test <- function( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", paired = FALSE, exact = NULL, alternative = "two.sided", mu = 0, conf.level = 0.95, detailed = FALSE ) { env <- as.list(environment()) args <- env %>% add_item(method = "wilcox_test") params <- env %>% remove_null_items() %>% add_item(conf.int = TRUE, method = "wilcox.test") outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(number.of.groups > 2 & !is.null(ref.group)){ if(ref.group %in% c("all", ".all.")){ params$data <- create_data_with_all_ref_group(data, outcome, group) params$ref.group <- "all" } } test.func <- two_sample_test if(number.of.groups > 2) test.func <- pairwise_two_sample_test do.call(test.func, params) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "wilcox_test")) } #'@describeIn wilcox_test performs pairwise two sample Wilcoxon test. #'@export pairwise_wilcox_test <- function( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", detailed = FALSE, ...) { args <- as.list(environment()) %>% .add_item(method = "wilcox_test") res <- pairwise_two_sample_test( data, formula, method = "wilcox.test", comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, detailed = detailed, conf.int = TRUE, ... ) res %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "wilcox_test")) } rstatix/R/prop_test.R0000644000176200001440000002005613640124126014315 0ustar liggesusers#' @include utilities.R NULL #'Proportion Test #'@description Performs proportion tests to either evaluate the homogeneity of #' proportions (probabilities of success) in several groups or to test that the #' proportions are equal to certain given values. #' #' Wrappers around the R base function \code{\link[stats]{prop.test}()} but have #' the advantage of performing pairwise and row-wise z-test of two proportions, #' the post-hoc tests following a significant chi-square test of homogeneity #' for 2xc and rx2 contingency tables. #'@inheritParams stats::prop.test #'@param xtab a cross-tabulation (or contingency table) with two columns and #' multiple rows (rx2 design). The columns give the counts of successes and #' failures respectively. #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #'@param detailed logical value. Default is FALSE. If TRUE, a detailed result is #' shown. #'@param ... Other arguments passed to the function \code{prop_test()}. #' #'@return return a data frame with some the following columns: \itemize{ #' \item \code{n}: the number of participants. #'\item \code{group}: the categories in the row-wise proportion tests. \item #' \code{statistic}: the value of Pearson's chi-squared test statistic. \item #' \code{df}: the degrees of freedom of the approximate chi-squared #' distribution of the test statistic. \item \code{p}: p-value. \item #' \code{p.adj}: the adjusted p-value. \item \code{method}: the used #' statistical test. \item \code{p.signif, p.adj.signif}: the significance #' level of p-values and adjusted p-values, respectively. \item #' \code{estimate}: a vector with the sample proportions x/n. \item #' \code{estimate1, estimate2}: the proportion in each of the two populations. #' \item \code{alternative}: a character string describing the alternative #' hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a #' confidence interval. a confidence interval for the true proportion if there #' is one group, or for the difference in proportions if there are 2 groups and #' p is not given, or NULL otherwise. In the cases where it is not NULL, the #' returned confidence interval has an asymptotic confidence level as specified #' by conf.level, and is appropriate to the specified alternative hypothesis.} #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' @examples #' # Comparing an observed proportion to an expected proportion #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' prop_test(x = 95, n = 160, p = 0.5, detailed = TRUE) #' #' # Comparing two proportions #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: frequencies of smokers between two groups #' xtab <- as.table(rbind(c(490, 10), c(400, 100))) #' dimnames(xtab) <- list( #' group = c("grp1", "grp2"), #' smoker = c("yes", "no") #' ) #' xtab #' # compare the proportion of smokers #' prop_test(xtab, detailed = TRUE) #' #' # Homogeneity of proportions between groups #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # H0: the proportion of smokers is similar in the four groups #' # Ha: this proportion is different in at least one of the populations. #' # #' # Data preparation #' grp.size <- c( 106, 113, 156, 102 ) #' smokers <- c( 50, 100, 139, 80 ) #' no.smokers <- grp.size - smokers #' xtab <- as.table(rbind( #' smokers, #' no.smokers #' )) #' dimnames(xtab) <- list( #' Smokers = c("Yes", "No"), #' Groups = c("grp1", "grp2", "grp3", "grp4") #' ) #' xtab #' #' # Compare the proportions of smokers between groups #' prop_test(xtab, detailed = TRUE) #' #' # Pairwise comparison between groups #' pairwise_prop_test(xtab) #' #' #' # Pairwise proportion tests #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: Titanic #' xtab <- as.table(rbind( #' c(122, 167, 528, 673), #' c(203, 118, 178, 212) #' )) #' dimnames(xtab) <- list( #' Survived = c("No", "Yes"), #' Class = c("1st", "2nd", "3rd", "Crew") #' ) #' xtab #' # Compare the proportion of survived between groups #' pairwise_prop_test(xtab) #' #' # Row-wise proportion tests #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: Titanic #' xtab <- as.table(rbind( #' c(180, 145), c(179, 106), #' c(510, 196), c(862, 23) #' )) #' dimnames(xtab) <- list( #' Class = c("1st", "2nd", "3rd", "Crew"), #' Gender = c("Male", "Female") #' ) #' xtab #' # Compare the proportion of males and females in each category #' row_wise_prop_test(xtab) #' @describeIn prop_test performs one-sample and two-samples z-test of #' proportions. Wrapper around the function \code{\link[stats]{prop.test}()}. #' @export prop_test <- function(x, n, p = NULL, alternative = c("two.sided", "less", "greater"), correct = TRUE, conf.level = 0.95, detailed = FALSE){ args <- as.list(environment()) %>% add_item(method = "prop_test") if(is.data.frame(x)) x <- as.matrix(x) if(inherits(x, c("matrix", "table"))){ if(ncol(x) > 2 & nrow(x) == 2) x <- t(x) nb.grp <- nrow(x) row.sums <- rowSums(x) n <- sum(x) Ns <- matrix(c(n, row.sums), nrow = 1, ncol = nb.grp+1) colnames(Ns) <- c("n", paste0("n", 1:nb.grp)) } else{ row.sums <- x nb.grp <- length(x) Ns <- matrix(c(sum(n), row.sums), nrow = 1, ncol = nb.grp+1) colnames(Ns) <- c("n", paste0("n", 1:nb.grp)) } Ns <- as_tibble(Ns) results <- stats::prop.test(x, n, p, alternative, conf.level, correct) %>% as_tidy_stat() %>% add_significance("p") %>% mutate(method = "Prop test") results <- dplyr::bind_cols(Ns, results) if(!detailed) results <- remove_details(results, method = "prop.test") results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "prop_test")) } #' @describeIn prop_test pairwise comparisons between proportions, a post-hoc #' tests following a significant chi-square test of homogeneity for 2xc #' design. Wrapper around \code{\link[stats]{pairwise.prop.test}()} #' @export pairwise_prop_test <- function(xtab, p.adjust.method = "holm", ...){ if(is.data.frame(xtab)) xtab <- as.matrix(xtab) if(ncol(xtab) > 2 & nrow(xtab) == 2) xtab <- t(xtab) args <- c(as.list(environment()), list(...)) %>% add_item(method = "prop_test") results <- stats::pairwise.prop.test( xtab, p.adjust.method = "none", ... ) %>% tidy() %>% select(.data$group2, .data$group1, .data$p.value) colnames(results) <- c("group1", "group2", "p") results <- results %>% adjust_pvalue(method = p.adjust.method) %>% add_significance("p.adj") %>% mutate( p = signif(.data$p, digits = 3), p.adj = signif(.data$p.adj, digits = 3) ) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "prop_test")) } #' @describeIn prop_test performs row-wise z-test of two proportions, a post-hoc tests following a significant chi-square test #' of homogeneity for rx2 contingency table. The z-test of two proportions is calculated for each category (row). #' @export row_wise_prop_test <- function(xtab, p.adjust.method = "holm", detailed = FALSE, ...){ if(is.data.frame(xtab)) xtab <- as.matrix(xtab) if(!inherits(xtab, c("matrix", "table"))){ stop("An object of class 'matrix' or 'table' required") } if(ncol(xtab) !=2){ stop("A cross-tabulation with two columns required") } args <- c(as.list(environment()), list(...)) %>% add_item(method = "prop_test") columns.total <- margin.table(xtab, 2) results <- apply( xtab, MARGIN = 1, FUN = prop_test, n = columns.total, detailed = detailed, ... ) %>% map(keep_only_tbl_df_classes) %>% bind_rows(.id = "group") %>% adjust_pvalue(method = p.adjust.method) %>% add_significance("p.adj") %>% mutate( p = signif(.data$p, digits = 3), p.adj = signif(.data$p.adj, digits = 3) ) %>% select(-.data$p.signif) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "prop_test")) } rstatix/R/outliers.R0000644000176200001440000000713613567674224014171 0ustar liggesusers#' @include utilities.R #' @importFrom stats quantile #' @importFrom stats IQR NULL #'Identify Univariate Outliers Using Boxplot Methods #' #' #'@description Detect outliers using boxplot methods. Boxplots are a popular and #' an easy method for identifying outliers. There are two categories of #' outlier: (1) outliers and (2) extreme points. #' #' Values above \code{Q3 + 1.5xIQR} or below \code{Q1 - 1.5xIQR} are considered #' as outliers. Values above \code{Q3 + 3xIQR} or below \code{Q1 - 3xIQR} are #' considered as extreme points (or extreme outliers). #' #' Q1 and Q3 are the first and third quartile, respectively. IQR is the #' interquartile range (IQR = Q3 - Q1). #' #' Generally speaking, data points that are labelled outliers in boxplots are #' not considered as troublesome as those considered extreme points and might #' even be ignored. Note that, any \code{NA} and \code{NaN} are automatically removed #' before the quantiles are computed. #' #'@return \itemize{ \item \code{identify_outliers()}. Returns the input data #' frame with two additional columns: "is.outlier" and "is.extreme", which hold #' logical values. \item \code{is_outlier() and is_extreme()}. Returns logical #' vectors. } #' #'@param data a data frame #'@param ... One unquoted expressions (or variable name). Used to select a #' variable of interest. Alternative to the argument \code{variable}. #'@param variable variable name for detecting outliers #'@param x a numeric vector #'@param coef coefficient specifying how far the outlier should be from the edge #' of their box. Possible values are 1.5 (for outlier) and 3 (for extreme #' points only). Default is 1.5 #' #' #' @examples #' # Generate a demo data #' set.seed(123) #' demo.data <- data.frame( #' sample = 1:20, #' score = c(rnorm(19, mean = 5, sd = 2), 50), #' gender = rep(c("Male", "Female"), each = 10) #' ) #' #' # Identify outliers according to the variable score #' demo.data %>% #' identify_outliers(score) #' #' # Identify outliers by groups #' demo.data %>% #' group_by(gender) %>% #' identify_outliers("score") #'@describeIn outliers takes a data frame and extract rows suspected as outliers #' according to a numeric column. The following columns are added "is.outlier" #' and "is.extreme". #'@export identify_outliers <- function(data, ..., variable = NULL){ is.outlier <- NULL if(is_grouped_df(data)){ results <- data %>% doo(identify_outliers, ..., variable = variable) if(nrow(results) == 0) results <- as.data.frame(results) return(results) } if(!inherits(data, "data.frame")) stop("data should be a data frame") variable <- data %>% get_selected_vars(..., vars = variable) n.vars <- length(variable) if(n.vars > 1) stop("Specify only one variable") values <- data %>% pull(!!variable) results <- data %>% mutate( is.outlier = is_outlier(values), is.extreme = is_extreme(values) ) %>% filter(is.outlier == TRUE) if(nrow(results) == 0) results <- as.data.frame(results) results } #' @describeIn outliers detect outliers in a numeric vector. Returns logical vector. #' @export is_outlier <- function(x, coef = 1.5){ res <- x Q1 <- quantile(x, 0.25, na.rm = TRUE) Q3 <- quantile(x, 0.75, na.rm = TRUE) .IQR <- IQR(x, na.rm = TRUE) upper.limit <- Q3 + (coef*.IQR) lower.limit <- Q1 - (coef*.IQR) outlier <- ifelse(x < lower.limit | x > upper.limit, TRUE, FALSE ) outlier } #' @describeIn outliers detect extreme points in a numeric vector. An alias of #' \code{is_outlier()}, where coef = 3. Returns logical vector. #' @export is_extreme <- function(x){ is_outlier(x, coef = 3) } rstatix/R/doo.R0000644000176200001440000000631213570417613013066 0ustar liggesusers#' @include utilities.R NULL #'Alternative to dplyr::do for Doing Anything #' #' #'@description Provides a flexible alternative to the \code{dplyr:do()} function. #' Technically it uses \code{nest() + mutate() + map()} to apply arbitrary #' computation to a grouped data frame. #' #' The output is a data frame. If the applied function returns a data frame, #' then the output will be automatically unnested. Otherwise, the output includes the grouping #' variables and a column named ".results." (by default), which is a "list-columns" #' containing the results for group combinations. #' #'@param data a (grouped) data frame #'@param .f A function, formula, or atomic vector. For example #' \code{~t.test(len ~ supp, data = .)}. #' @param ... Additional arguments passed on to .f #' @param result the column name to hold the results. Default is ".results.". #' @return a data frame #' @examples #' # Custom function #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' stat_test <- function(data, formula){ #' t.test(formula, data) %>% #' tidy() #' } #' # Example 1: pipe-friendly stat_test(). #' # Two possibilities of usage are available #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Use this #' ToothGrowth %>% #' group_by(dose) %>% #' doo(~stat_test(data =., len ~ supp)) #' #' # Or this #' ToothGrowth %>% #' group_by(dose) %>% #' doo(stat_test, len ~ supp) #' #' # Example 2: R base function t.test() (not pipe friendly) #' # One possibility of usage #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' comparisons <- ToothGrowth %>% #' group_by(dose) %>% #' doo(~t.test(len ~ supp, data =.)) #' comparisons #' comparisons$.results. #' #' # Example 3: R base function combined with tidy() #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' ToothGrowth %>% #' group_by(dose) %>% #' doo(~t.test(len ~ supp, data =.) %>% tidy()) #'@export doo <- function(data, .f, ..., result = ".results."){ if(is_grouped_df(data)){ .results <- data %>% nest() } else{ .results <- data %>% nest(data = everything()) } .results <- .results %>% dplyr::ungroup() %>% mutate(data = map(.data$data, droplevels)) %>% mutate(data = map(.data$data, .f, ...)) if(inherits(.results$data[[1]], c("data.frame", "tbl_df"))){ # Suppress warning such as: # Binding character and factor vector, coercing into character vector .results <- suppressWarnings(unnest(.results)) } else{ colnames(.results)[ncol(.results)] <- result } if(is_grouped_df(data)){ .groups <- dplyr::group_vars(data) .results <- dplyr::arrange(.results, !!!syms(.groups)) } .results } # To be removed doo_old_version <- function(data, .f, ..., result = ".results."){ .nested <- data %>% nest() %>% dplyr::ungroup() %>% mutate(data = map(data, droplevels)) .computed <- .nested$data %>% map(.f, ...) .results <- .nested %>% select(-data) %>% mutate(!!result := .computed) if(inherits(.computed[[1]], c("data.frame", "tbl_df"))){ # Suppress warning such as: # Binding character and factor vector, coercing into character vector .results <- suppressWarnings(unnest(.results)) } if(is_grouped_df(data)){ .groups <- dplyr::group_vars(data) .results <- dplyr::arrange(.results, !!!syms(.groups)) } .results } rstatix/R/t_test.R0000644000176200001440000002212513570420357013606 0ustar liggesusers#' @include utilities.R utilities_two_sample_test.R NULL #'T-test #' #' #'@description Provides a pipe-friendly framework to performs one and two sample #' t-tests. Read more: \href{https://www.datanovia.com/en/lessons/t-test-in-r/}{T-test in R}. #'@inheritParams stats::t.test #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{x ~ group} where \code{x} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ cancer_group}. #'@param paired a logical indicating whether you want a paired test. #'@param ref.group a character string specifying the reference group. If #' specified, for a given grouping variable, each of the group levels will be #' compared to the reference group (i.e. control group). #' #' If \code{ref.group = "all"}, pairwise two sample tests are performed for #' comparing each grouping variable levels against all (i.e. basemean). #'@param mu a number specifying an optional parameter used to form the null hypothesis. #'@param comparisons A list of length-2 vectors specifying the groups of #' interest to be compared. For example to compare groups "A" vs "B" and "B" vs #' "C", the argument is as follow: \code{comparisons = list(c("A", "B"), c("B", #' "C"))} #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #'@param pool.sd logical value used in the function \code{pairwise_t_test()}. #' Switch to allow/disallow the use of a pooled SD. #' #' The \code{pool.sd = TRUE} (default) calculates a common SD for all groups #' and uses that for all comparisons (this can be useful if some groups are #' small). This method does not actually call t.test, so extra arguments are #' ignored. Pooling does not generalize to paired tests so pool.sd and paired #' cannot both be TRUE. #' #' If \code{pool.sd = FALSE} the standard two sample t-test is applied to all #' possible pairs of groups. This method calls the \code{t.test()}, so extra #' arguments, such as \code{var.equal} are accepted. #' #'@param detailed logical value. Default is FALSE. If TRUE, a detailed result is #' shown. #'@param ... other arguments to be passed to the function #' \code{\link[stats]{t.test}}. #' #'@details #' #'- If a list of comparisons is specified, the result of the pairwise tests is #'filtered to keep only the comparisons of interest. The p-value is adjusted #'after filtering. #' #'- For a grouped data, if pairwise test is performed, then the p-values are #'adjusted for each group level independently. #' #'@return return a data frame with some the following columns: \itemize{ \item #' \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the #' compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. #' \item \code{statistic}: Test statistic used to compute the p-value. \item #' \code{df}: degrees of freedom. \item \code{p}: p-value. \item \code{p.adj}: #' the adjusted p-value. \item \code{method}: the statistical test used to #' compare groups. \item \code{p.signif, p.adj.signif}: the significance level #' of p-values and adjusted p-values, respectively. \item \code{estimate}: #' estimate of the effect size. It corresponds to the estimated mean or #' difference in means depending on whether it was a one-sample test or a #' two-sample test. \item \code{estimate1, estimate2}: show the mean values of #' the two groups, respectively, for independent samples t-tests. \item #' \code{alternative}: a character string describing the alternative #' hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a #' confidence interval. } #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # One-sample test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% t_test(len ~ 1, mu = 0) #' #' #' # Two-samples unpaired test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% t_test(len ~ supp) #' #' # Two-samples paired test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% t_test (len ~ supp, paired = TRUE) #' #' # Compare supp levels after grouping the data by "dose" #' #:::::::::::::::::::::::::::::::::::::::: #' df %>% #' group_by(dose) %>% #' t_test(data =., len ~ supp) %>% #' adjust_pvalue(method = "bonferroni") %>% #' add_significance("p.adj") #' #' # pairwise comparisons #' #:::::::::::::::::::::::::::::::::::::::: #' # As dose contains more than two levels ==> #' # pairwise test is automatically performed. #' df %>% t_test(len ~ dose) #' #' # Comparison against reference group #' #:::::::::::::::::::::::::::::::::::::::: #' # each level is compared to the ref group #' df %>% t_test(len ~ dose, ref.group = "0.5") #' #' # Comparison against all #' #:::::::::::::::::::::::::::::::::::::::: #' df %>% t_test(len ~ dose, ref.group = "all") #' #'@describeIn t_test t test #'@export t_test <- function( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", paired = FALSE, var.equal = FALSE, alternative = "two.sided", mu = 0, conf.level = 0.95, detailed = FALSE ) { env <- as.list(environment()) args <- env %>% .add_item(method = "t_test") params <- env %>% remove_null_items() %>% add_item(method = "t.test") outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(number.of.groups > 2 & !is.null(ref.group)){ if(ref.group %in% c("all", ".all.")){ params$data <- create_data_with_all_ref_group(data, outcome, group) params$ref.group <- "all" } } test.func <- two_sample_test if(number.of.groups > 2) test.func <- pairwise_two_sample_test do.call(test.func, params) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "t_test")) } #'@describeIn t_test performs pairwise two sample t-test. Wrapper around the R #' base function \code{\link[stats]{pairwise.t.test}}. #'@export pairwise_t_test <- function( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", paired = FALSE, pool.sd = !paired, detailed = FALSE, ...) { args <- c(as.list(environment()), list(...)) %>% .add_item(method = "t_test") if(paired) pool.sd <- FALSE if(pool.sd){ res <- pairwise_t_test_psd( data, formula, comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, detailed = detailed, ... ) } else{ res <- pairwise_two_sample_test( data, formula, method = "t.test", comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, paired = paired, detailed = detailed, ... ) } res %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "t_test")) } pairwise_t_test_psd <- function( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", alternative = "two.sided", detailed = FALSE ) { . <- NULL if(is_grouped_df(data)){ results <- data %>% doo(pairwise_t_test_psd, formula, comparisons, ref.group, p.adjust.method, alternative = alternative, detailed = detailed) return(results) } outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) # Convert group into factor if this is not already the case data <- data %>% .as_factor(group, ref.group = ref.group) outcome.values <- data %>% pull(!!outcome) group.values <- data %>% pull(!!group) group.size <- data %>% get_group_size(group) # Compute pairwise t-test group1 <- group2 <- p.value <- NULL results <- stats::pairwise.t.test( outcome.values, group.values, p.adjust.method = "none", pool.sd = TRUE, alternative = alternative ) %>% tidy() %>% select(group2, group1, p.value) colnames(results) <- c("group1", "group2", "p") n1 <- group.size[results$group1] n2 <- group.size[results$group2] results <- results %>% mutate(method = "T-test") %>% add_column(.y. = outcome, .before = 1) %>% add_column(n1 = n1, n2 = n2, .after = "group2") # If ref.group specified, keep only comparisons against reference if(!is.null(ref.group)){ results <- results %>% filter(group1 == ref.group) } # If a comparison list is provided, extract the comparisons of interest if(!is.null(comparisons)){ results <- comparisons %>% purrr::map_dfr(~ results %>% filter(group1 %in% .x & group2 %in% .x) ) } p <- p.adj <- NULL results <- results %>% adjust_pvalue(method = p.adjust.method) %>% add_significance("p") %>% add_significance("p.adj") %>% mutate( p = signif(p, digits = 3), p.adj = signif(p.adj, digits = 3) ) if(!detailed) results <- remove_details(results, method = "t.test") results } rstatix/R/welch_anova_test.R0000644000176200001440000000423013534276317015633 0ustar liggesusers#' @include utilities.R NULL #' Welch One-Way ANOVA Test #' #' @description Tests for equal means in a one-way design (not assuming equal #' variance). A wrapper around the base function #' \code{\link[stats]{oneway.test}()}. This is is an alternative to the #' standard one-way ANOVA in the situation where the homogeneity of variance #' assumption is violated. #' @param data a data frame containing the variables in the formula. #' @param formula a formula specifying the ANOVA model similar to aov. Can be of #' the form y ~ group where y is a numeric variable giving the data values and #' group is a factor with one or multiple levels giving the corresponding #' groups. For example, formula = TP53 ~ cancer_group. #' @return return a data frame with the following columns: \itemize{ \item #' \code{.y.}: the y variable used in the test. \item \code{n}: sample count. #' \item \code{statistic}: the value of the test statistic. \item \code{p}: #' p-value. \item \code{method}: the statistical test used to compare groups.} #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' df$dose <- as.factor(df$dose) #' #' # Welch one-way ANOVA test (not assuming equal variance) #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% welch_anova_test(len ~ dose) #' #' # Grouped data #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% #' group_by(supp) %>% #' welch_anova_test(len ~ dose) #' @name welch_anova_test #' @export welch_anova_test <- function(data, formula){ args <- as.list(environment()) %>% .add_item(method = "welch_anova_test") data %>% doo(oneway_test, formula) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "welch_anova_test")) } oneway_test <- function(data, formula){ outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) res <- stats::oneway.test(formula, data = data, var.equal = FALSE) tibble( .y. = outcome, n = nrow(data), statistic = round_value(res$statistic, 2), DFn = res$parameter[1], DFd = res$parameter[2], p = round_value(res$p.value, 3), method = "Welch ANOVA" ) } rstatix/R/utils-manova.R0000644000176200001440000000030513672731014014715 0ustar liggesusers#' Manova exported from car package #' #' See \code{car::\link[car:Anova]{Manova}} for details. #' #' @name Manova #' @rdname Manova #' @keywords internal #' @export #' @importFrom car Manova NULL rstatix/R/emmeans_test.R0000644000176200001440000001573614011702721014767 0ustar liggesusers#' @include utilities.R NULL #'Pairwise Comparisons of Estimated Marginal Means #' #' #'@description Performs pairwise comparisons between groups using the estimated #' marginal means. Pipe-friendly wrapper arround the functions \code{emmans() + #' contrast()} from the \code{emmeans} package, which need to be installed #' before using this function. This function is useful for performing post-hoc #' analyses following ANOVA/ANCOVA tests. #'@inheritParams t_test #'@param model a fitted-model objects such as the result of a call to #' \code{lm()}, from which the overall degrees of #' freedom are to be calculated. #'@param covariate (optional) covariate names (for ANCOVA) #'@return return a data frame with some the following columns: \itemize{ \item #' \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the #' compared groups in the pairwise tests. \item \code{statistic}: Test #' statistic (t.ratio) used to compute the p-value. \item \code{df}: degrees of #' freedom. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. #' \item \code{method}: the statistical test used to compare groups. \item #' \code{p.signif, p.adj.signif}: the significance level of p-values and #' adjusted p-values, respectively. \item \code{estimate}: estimate of the #' effect size, that is the difference between the two emmeans (estimated #' marginal means). \item \code{conf.low,conf.high}: Lower and upper bound on a #' confidence interval of the estimate. } #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. It has also an attribute named "emmeans", a data #' frame containing the groups emmeans. #'@examples #' # Data preparation #' df <- ToothGrowth #' df$dose <- as.factor(df$dose) #' #'# Pairwise comparisons #' res <- df %>% #' group_by(supp) %>% #' emmeans_test(len ~ dose, p.adjust.method = "bonferroni") #'res #' #' # Display estimated marginal means #' attr(res, "emmeans") #' #' # Show details #' df %>% #' group_by(supp) %>% #' emmeans_test(len ~ dose, p.adjust.method = "bonferroni", detailed = TRUE) #'@export emmeans_test <- function(data, formula, covariate = NULL, ref.group = NULL, comparisons = NULL, p.adjust.method = "bonferroni", conf.level = 0.95, model = NULL, detailed = FALSE){ . <- NULL covariate <- rlang::enquos(covariate = covariate) %>% get_quo_vars_list(data, .) %>% unlist() args <- as.list(environment()) %>% .add_item(method = "emmeans_test") required_package("emmeans") outcome <- get_formula_left_hand_side(formula) rhs <- group <- get_formula_right_hand_side(formula) grouping.vars <- NULL if(is_grouped_df(data)){ grouping.vars <- dplyr::group_vars(data) rhs <- c(grouping.vars, rhs) %>% paste(collapse = "*") data <- dplyr::ungroup(data) } if(!is.null(covariate)){ covariate <- paste(covariate, collapse = "+") rhs <- paste(covariate, rhs, sep = "+") } data <- data %>% .as_factor(group, ref.group = ref.group) group.levels <- data %>% get_levels(group) # Build linear model formula <- stats::as.formula(paste(outcome, rhs, sep = " ~ ")) if(is.null(model)) model <- stats::lm(formula, data) # Fit emmeans # Possible pairwise comparisons: if ref.group specified, # only comparisons against reference will be kept if (is.null(comparisons)) { comparisons <- get_comparisons(data, variable = !!group, ref.group = !!ref.group) } method <- get_emmeans_contrasts(data, group, comparisons) formula.emmeans <- stats::as.formula(paste0("~", rhs)) res.emmeans <- emmeans::emmeans(model, formula.emmeans) comparisons <- pairwise_emmeans_test( res.emmeans, grouping.vars, method = method, p.adjust.method = p.adjust.method, conf.level = conf.level ) res.emmeans <- res.emmeans %>% tibble::as_tibble() %>% dplyr::arrange(!!!syms(grouping.vars)) %>% dplyr::rename(se = .data$SE, conf.low = .data$lower.CL, conf.high = .data$upper.CL) %>% mutate(method = "Emmeans test") if(!detailed){ to.remove <- c("estimate", "estimate1", "estimate2", "se", "conf.low", "conf.high", "method", "null.value") to.keep <- setdiff(colnames(comparisons), to.remove) comparisons <- comparisons[, to.keep] } comparisons %>% add_column(.y. = outcome, .before = "group1") %>% set_attrs(args = args, emmeans = res.emmeans) %>% add_class(c("rstatix_test", "emmeans_test")) } #' @export #' @param emmeans.test an object of class \code{emmeans_test}. #' @describeIn emmeans_test returns the estimated marginal means from an object of class \code{emmeans_test} get_emmeans <- function(emmeans.test){ if(!inherits(emmeans.test, "emmeans_test")){ stop("An object of class 'emmeans_test' required.") } attr(emmeans.test, "emmeans") } pairwise_emmeans_test <- function(res.emmeans, grouping.vars = NULL, method = "pairwise", p.adjust.method = "bonferroni", conf.level = 0.95){ # Comparisons without adjusting the pvalue # reverse the order of subtraction for consistency with pairwise_t_test comparisons <- emmeans::contrast( res.emmeans, by = grouping.vars, method = method, adjust = "none" ) comparisons <- tidy(comparisons, conf.int = TRUE, conf.level = conf.level) comparisons <- comparisons %>% tidyr::separate(col = "contrast", into = c("group1", "group2"), sep = "-") %>% dplyr::rename(se = .data$std.error, p = .data$p.value) %>% dplyr::select(!!!syms(grouping.vars), everything()) # Adjust the pvalue. We don't want to use adjust_pvalue here, because # emmeans support method = "tukey", but this is not the case for adjust_pvalue p.adjusted <- emmeans::contrast( res.emmeans, by = grouping.vars, method = method, adjust = p.adjust.method ) %>% as.data.frame() %>% pull("p.value") comparisons <- comparisons %>% mutate(p.adj = p.adjusted) %>% add_significance("p.adj") comparisons %>% dplyr::arrange(!!!syms(grouping.vars)) } # Returns a list of contrasts for specific comparisons # data: data frame, # group: grouping columns, # comparisons a list of comparisons get_emmeans_contrasts <- function(data, group, comparisons){ get_dummy_code <- function(level, group.levels){ dummy.code <- rep(0, length(group.levels)) lev.pos <- which(group.levels == level) dummy.code[lev.pos] <- 1 dummy.code } make_emmeans_contrast <- function(groups, contrasts.list ){ group1 <- groups[1] group2 <- groups[2] contrasts.list[[group1]]-contrasts.list[[group2]] } make_comparison_name <- function(groups){ paste(groups[1], groups[2], sep = "-") } group.levels <- get_levels(data, group) contrasts.list <- group.levels %>% map(get_dummy_code, group.levels) names(contrasts.list) <- group.levels comparison.contrasts <- comparisons %>% map(make_emmeans_contrast, contrasts.list) comparison.names <- comparisons %>% map(make_comparison_name) names(comparison.contrasts) <- comparison.names comparison.contrasts } rstatix/R/add_significance.R0000644000176200001440000000313513710565530015535 0ustar liggesusers#' @include utilities.R NULL #' Add P-value Significance Symbols #' @description Add p-value significance symbols into a data frame. #' @param data a data frame containing a p-value column. #' @param p.col column name containing p-values. #' @param output.col the output column name to hold the adjusted p-values. #' @param cutpoints numeric vector used for intervals. #' @param symbols character vector, one shorter than cutpoints, used as #' significance symbols. #' #' @return a data frame #' #' @examples #' # Perform pairwise comparisons and adjust p-values #' ToothGrowth %>% #' t_test(len ~ dose) %>% #' adjust_pvalue() %>% #' add_significance("p.adj") #' #' @rdname add_significance #' @export add_significance <- function( data, p.col = NULL, output.col = NULL, cutpoints = c(0, 0.0001, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "ns") ) { .attributes <- get_test_attributes(data) if(is.null(p.col)) p.col <- data %>% p_detect("p.adj") if(is.null(p.col)) p.col <- data %>% p_detect("p") if(is.null(p.col)) return(data) else if(!(p.col %in% colnames(data))) stop("The column ", p.col, " does not exist in the data") if(is.null(output.col)) output.col <- paste0(p.col, ".signif") .p.values <- data %>% pull(!!p.col) if(all(is.na(.p.values))) { .p.signif <- rep("", length(.p.values)) } else{ .p.signif <- .p.values %>% stats::symnum(cutpoints = cutpoints, symbols = symbols, na = "") %>% as.character() } data %>% keep_only_tbl_df_classes() %>% mutate(!!output.col := .p.signif) %>% set_test_attributes(.attributes) } rstatix/R/pull_triangle.R0000644000176200001440000000404513337207372015147 0ustar liggesusers#' @include utilities.R replace_triangle.R NULL #' Pull Lower and Upper Triangular Part of a Matrix #' @description Returns the lower or the upper triangular part of a #' (correlation) matrix. #' @param x a (correlation) matrix #' @param diagonal logical. Default is FALSE. If TRUE, the matrix diagonal is #' included. #' @param triangle the triangle to pull. Allowed values are one of #' "upper" and "lower". #' @return an object of class \code{cor_mat_tri}, which is a data frame #' @seealso \code{\link{replace_triangle}()} #' @examples #' #' # Data preparation #' #:::::::::::::::::::::::::::::::::::::::::: #' mydata <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) #' head(mydata, 3) #' #' # Compute correlation matrix and pull triangles #' #:::::::::::::::::::::::::::::::::::::::::: #' # Correlation matrix #' cor.mat <- cor_mat(mydata) #' cor.mat #' #' # Pull lower triangular part #' cor.mat %>% pull_lower_triangle() #' #' # Pull upper triangular part #' cor.mat %>% pull_upper_triangle() #' #' #' @describeIn pull_triangle returns either the lower or upper triangular part of a matrix. #' @export pull_triangle <- function(x, triangle = c("lower", "upper"), diagonal = FALSE){ triangle.to.pull <- match.arg(triangle) triangle.to.replace <- ifelse( triangle.to.pull == "lower", "upper", "lower" ) triangle.class <- paste0(triangle.to.pull, "_tri") res <- x %>% replace_triangle(triangle.to.replace, by = "", diagonal = diagonal) %>% add_class(triangle.class) res } #' @describeIn pull_triangle returns an object of class \code{upper_tri}, which #' is a data frame containing the upper triangular part of a matrix. #' @export pull_upper_triangle <- function(x, diagonal = FALSE){ x %>% pull_triangle("upper", diagonal = diagonal) } #' @describeIn pull_triangle returns an object of class \code{lower_tri}, which #' is a data frame containing the lower triangular part of a matrix. #' @export pull_lower_triangle <- function(x, diagonal = FALSE){ x %>% pull_triangle("lower", diagonal = diagonal) } rstatix/R/cor_reshape.R0000644000176200001440000000736014011725203014567 0ustar liggesusers#' @include utilities.R cor_mat.R NULL #' Reshape Correlation Data #' @description Reshape correlation analysis results. Key functions: \itemize{ #' \item \code{cor_gather()}: takes a correlation matrix and collapses (i.e. melt) it into a paired list #' (long format). \item \code{cor_spread()}: spread a long correlation data format across #' multiple columns. Particularly, it takes the results of \code{\link{cor_test}} #' and transforms it into a correlation matrix. } #' @param data a data frame or matrix. #' @param drop.na logical. If TRUE, drop rows containing missing values after gathering the data. #' @param value column name containing the value to spread. #' @seealso \code{\link{cor_mat}()}, \code{\link{cor_reorder}()} #' @examples #' # Data preparation #' #:::::::::::::::::::::::::::::::::::::::::: #' mydata <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) #' head(mydata, 3) #' #' # Reshape a correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' # Compute a correlation matrix #' cor.mat <- mydata %>% cor_mat() #' cor.mat #' #' # Collapse the correlation matrix into long format #' # paired list data frame #' long.format <- cor.mat %>% cor_gather() #' long.format #' #' # Spread a correlation data format #' #:::::::::::::::::::::::::::::::::::::::::: #' # Spread the correlation coefficient value #' long.format %>% cor_spread(value = "cor") #' # Spread the p-value #' long.format %>% cor_spread(value = "p") #' @describeIn cor_reshape takes a correlation matrix and collapses (or melt) it into long #' format data frame (paired list) #' @export cor_gather <- function(data, drop.na = TRUE){ rowname <- column <- NULL if(inherits(data, "cor_mat")){ cor.value <- data p.value <- data %>% cor_get_pval() } else if(inherits(data, "cor_mat_tri")){ cor.value <- data %>% as_numeric_triangle() p.value <- data %>% cor_get_pval() %>% as_numeric_triangle() } else if(inherits(data, "rcorr")){ cor.value <- data$r %>% as_tibble(rownames = "rowname") p.value <- data$P %>% as_tibble(rownames = "rowname") } else { cor.value <- data %>% as_tibble(rownames = "rowname") p.value <- NULL } cor.value <- cor.value %>% keep_only_tbl_df_classes() %>% gather(key = "column", value = "cor", -rowname) if(!is.null(p.value)){ p.value <- p.value %>% keep_only_tbl_df_classes() %>% gather(key = "column", value = "p", -rowname) cor.value <- cor.value %>% left_join(p.value, by = c("rowname", "column")) colnames(cor.value) <- c("var1", "var2", "cor", "p") } else{ colnames(cor.value) <- c("var1", "var2", "cor") } if(drop.na) cor.value <- cor.value %>% drop_na() cor.value } #' @describeIn cor_reshape spread a long correlation data frame into wide #' format. Expects the columns "var1", "var2" and "cor" in the data. #' (correlation matrix). #' @export cor_spread <- function(data, value = "cor"){ if(!(all(c("var1", "var2", value) %in% colnames(data)))){ stop("The input data should contains the columns: var1, var2 and cor") } var1 <- var2 <- cor <- p <- NULL row.vars <- data %>% pull(var1) %>% unique() col.vars <- data %>% pull(var2) %>% unique() res <- data %>% keep_only_tbl_df_classes() %>% select(var1, var2, !!value) %>% spread(key = "var2", value = value) %>% rename(rowname = var1) %>% respect_variables_order(row.vars = row.vars, col.vars = col.vars) colnames(res)[1] <- "rowname" res } # Helper functions # ::::::::::::::::::::::::::::::::::::::::::::::::::::: # Reorder a correlation matrix according # to the order of variables in vars respect_variables_order <- function(data, vars, row.vars = vars, col.vars = vars){ data %>% subset_matrix(row.vars = row.vars, col.vars = col.vars) } rstatix/R/binom_test.R0000644000176200001440000001505513640103445014445 0ustar liggesusers#' @include utilities.R NULL #'Exact Binomial Test #' #'@description Performs exact binomial test and pairwise comparisons following a #' significant exact multinomial test. Wrapper around the R base function #' \code{link[stats]{binom.test}()} that returns a data frame as a result. #' #'@inheritParams stats::binom.test #'@param x numeric vector containing the counts. #'@param p a vector of probabilities of success. The length of p must be the #' same as the number of groups specified by x, and its elements must be #' greater than 0 and less than 1. #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #'@param detailed logical value. Default is FALSE. If TRUE, a detailed result is #' shown. #'@seealso \link{multinom_test} #'@return return a data frame containing the p-value and its significance. with #' some the following columns: \itemize{ \item \code{group, group1, group2}: #' the categories or groups being compared. \item \code{statistic}: the number #' of successes. \item \code{parameter}: the number of trials. \item \code{p}: #' p-value of the test. \item \code{p.adj}: the adjusted p-value. \item #' \code{method}: the used statistical test. \item \code{p.signif, #' p.adj.signif}: the significance level of p-values and adjusted p-values, #' respectively. \item \code{estimate}: the estimated probability of success. #' \item \code{alternative}: a character string describing the alternative #' hypothesis. \item \code{conf.low,conf.high}: Lower and upper bound on a #' confidence interval for the probability of success.} #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' #' @examples #' # Exact binomial test #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: 160 mice with cancer including 95 male and 65 female #' # Q1: Does cancer affect more males than females? #' binom_test(x = 95, n = 160) #' # => yes, there are a significant difference #' #' #' # Q2: compare the observed proportion of males #' # to an expected proportion (p = 3/5) #' binom_test(x = 95, n = 160, p = 3/5) #' # => there are no significant difference #' #' # Multinomial test #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data #' tulip <- c(red = 81, yellow = 50, white = 27) #' # Question 1: are the color equally common ? #' # this is a test of homogeneity #' res <- multinom_test(tulip) #' res #' attr(res, "descriptives") #' #' # Pairwise comparisons between groups #' pairwise_binom_test(tulip, p.adjust.method = "bonferroni") #' #' #' # Question 2: comparing observed to expected proportions #' # this is a goodness-of-fit test #' expected.p <- c(red = 0.5, yellow = 0.33, white = 0.17) #' res <- multinom_test(tulip, expected.p) #' res #' attr(res, "descriptives") #' #' # Pairwise comparisons against a given probabilities #' pairwise_binom_test_against_p(tulip, expected.p) #' @describeIn binom_test performs exact binomial test. Wrapper around the R #' base function \code{\link[stats]{binom.test}} that returns a dataframe as a #' result. #' @export binom_test <- function(x, n, p = 0.5, alternative = "two.sided", conf.level = 0.95, detailed = FALSE){ args <- as.list(environment()) %>% add_item(method = "exact_binom_test") if(length(x) == 2) n <- sum(x) results <- stats::binom.test(x, n, p, alternative, conf.level) %>% tidy() %>% rename(p = .data$p.value) %>% add_significance("p") %>% add_columns(n = n, .before = 1) if(!detailed){ to.keep <- c("n", "estimate", "conf.low", "conf.high", "p", "p.signif") results <- results[, to.keep] } results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "exact_binom_test")) } #' @describeIn binom_test performs pairwise comparisons (binomial test) #' following a significant exact multinomial test. #' @export pairwise_binom_test <- function(x, p.adjust.method = "holm", alternative = "two.sided", conf.level = 0.95){ if(is.null(names(x))){ names(x) <- paste0("grp", 1:length(x)) } compare_pair <- function(levs, x){ levs <- as.character(levs) lev1 <- levs[1] lev2 <- levs[2] binom_test( x[lev1], x[lev1] + x[lev2], p = 0.5, alternative = alternative, conf.level = conf.level ) %>% add_columns(group1 = levs[1], group2 = levs[2], .before = 1) } args <- as.list(environment()) %>% add_item(method = "exact_binom_test") comparisons <- names(x) %>% .possible_pairs() results <- comparisons %>% map(compare_pair, x) results <- comparisons %>% map(compare_pair, x) %>% map(keep_only_tbl_df_classes) %>% bind_rows() %>% adjust_pvalue("p", method = p.adjust.method) %>% add_significance("p.adj") %>% mutate(p.adj = signif(.data$p.adj, digits = 3)) %>% select(-.data$p.signif) # select(.data$group1, .data$group2, .data$p, .data$p.adj, .data$p.adj.signif) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "exact_binom_test")) } #' @describeIn binom_test performs pairwise comparisons (binomial test) #' following a significant exact multinomial test for given probabilities. #' @export pairwise_binom_test_against_p <- function(x, p = rep(1/length(x), length(x)), p.adjust.method = "holm", alternative = "two.sided", conf.level = 0.95){ if (sum(p) != 1) { stop("sum of probabilities must be 1") } if (length(x) != length(p)) { stop("'x' and 'p' lengths differ") } groups <- names(x) if(is.null(groups)) { names(groups) <- paste0("grp", 1:length(x)) } if(inherits(x, "table")){ x <- as.vector(x) } names(x) <- groups args <- as.list(environment()) %>% add_item(method = "exact_binom_test") input <- data.frame(x = x, n = sum(x), p = p) results <- purrr::pmap( input, binom_test, alternative = alternative, conf.level = conf.level ) %>% map(keep_only_tbl_df_classes) %>% bind_rows() %>% adjust_pvalue("p", method = p.adjust.method) %>% add_significance("p.adj") %>% mutate(p.adj = signif(.data$p.adj, digits = 3)) %>% select(-.data$p.signif) %>% # select(.data$p, .data$p.adj, .data$p.adj.signif) %>% add_columns(group = groups, observed = x, expected = p*sum(x), .before = 1) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "exact_binom_test")) } rstatix/R/prop_trend_test.R0000644000176200001440000000440513640215221015506 0ustar liggesusers#' @include utilities.R NULL #'Test for Trend in Proportions #'@description Performs chi-squared test for trend in proportion. This test is #' also known as Cochran-Armitage trend test. #' #' Wrappers around the R base function \code{\link[stats]{prop.trend.test}()} but #' returns a data frame for easy data visualization. #'@param xtab a cross-tabulation (or contingency table) with two columns and #' multiple rows (rx2 design). The columns give the counts of successes and #' failures respectively. #' @param score group score. If \code{NULL}, the default is group number. #' #'@return return a data frame with some the following columns: \itemize{ \item #' \code{n}: the number of participants. \item \code{statistic}: the value of #' Chi-squared trend test statistic. \item \code{df}: the degrees of #' freedom. #' \item \code{p}: p-value. \item #' \code{method}: the used statistical test. \item \code{p.signif}: the significance level of p-values and adjusted p-values, #' respectively.} #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' @examples #' # Proportion of renal stone (calculi) across age #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data #' xtab <- as.table(rbind( #' c(384, 536, 335), #' c(951, 869, 438) #' )) #' dimnames(xtab) <- list( #' stone = c("yes", "no"), #' age = c("30-39", "40-49", "50-59") #' ) #' xtab #' # Compare the proportion of survived between groups #' prop_trend_test(xtab) #' @export prop_trend_test <- function(xtab, score = NULL){ args <- as.list(environment()) %>% add_item(method = "chisq_trend_test") if(is.data.frame(xtab)) xtab <- as.matrix(xtab) if(inherits(xtab, c("matrix", "table"))){ if(ncol(xtab) > 2 & nrow(xtab) == 2) xtab <- t(xtab) } total <- sum(xtab) events <- xtab[, 1] trials <- rowSums(xtab) if(is.null(score)) score <- seq_along(events) results <- stats::prop.trend.test(events, trials, score) %>% as_tidy_stat() %>% add_significance("p") %>% mutate(method = "Chi-square trend test") %>% add_columns(n = total, .before = 1) %>% select(.data$n, .data$statistic, .data$p, .data$p.signif, everything()) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "chisq_trend_test")) } rstatix/R/utilities.R0000644000176200001440000004544413672612673014337 0ustar liggesusers#' @importFrom magrittr %>% #' @importFrom magrittr %<>% #' @importFrom magrittr extract #' @importFrom magrittr set_colnames #' @importFrom dplyr mutate #' @importFrom dplyr mutate_at #' @importFrom dplyr mutate_all #' @importFrom dplyr pull #' @importFrom dplyr select #' @importFrom dplyr rename #' @importFrom dplyr as_data_frame #' @importFrom dplyr bind_rows #' @importFrom dplyr group_by #' @importFrom dplyr summarise #' @importFrom dplyr n #' @importFrom dplyr is_grouped_df #' @importFrom dplyr ungroup #' @importFrom dplyr do #' @importFrom dplyr filter #' @importFrom dplyr tibble #' @importFrom dplyr everything #' @importFrom dplyr left_join #' @importFrom purrr map map2 #' @importFrom broom tidy #' @importFrom stats t.test #' @importFrom rlang sym #' @importFrom rlang !! #' @importFrom rlang := #' @importFrom rlang .data #' @importFrom rlang syms #' @importFrom rlang !!! #' @importFrom rlang quos #' @importFrom rlang quo_name #' @importFrom tibble add_column #' @importFrom tibble as_tibble #' @importFrom tidyr spread #' @importFrom tidyr gather #' @importFrom tidyr nest # Unnesting, adapt to tidyr 1.0.0 unnest <- function(data, cols = "data", ...){ if(is_pkg_version_sup("tidyr", "0.8.3")){ results <- tidyr::unnest(data, cols = cols, ...) } else {results <- tidyr::unnest(data, ...)} results } # Check if an installed package version is superior to a specified version # Version, pkg: character vector is_pkg_version_sup<- function(pkg, version){ vv <- as.character(utils::packageVersion(pkg)) cc <- utils::compareVersion(vv, version) > 0 cc } # Rounding values -------------------------------------- # Round a vector, conditionnaly rounding round_value <- function(x, digits = 0){ sapply( x, function(x, digits){ if(is.na(x)) x else if(abs(x) > 10^-digits) round(x, digits) else signif(x, digits) }, digits ) } # Round a whole data frame or selected columns round_column <- function(data, ..., digits = 0){ dot.vars <- get_existing_dot_vars(data, ...) if(.is_empty(dot.vars)){ data %<>% dplyr::mutate_if(is.numeric, round_value, digits = digits) } data %<>% dplyr::mutate_at(dot.vars, round_value, digits = digits) data } # Extract or replace number from character string extract_number <- function(x){ as.numeric(gsub("[^0-9.-]+", "", as.character(x))) } replace_number <- function(x, replacement = ""){ gsub("[0-9.]", replacement, as.character(x)) } # Add columns into data frame # If specified before or after columns does not exist, columns are appended at the end add_columns <- function(.data, ..., .before = NULL, .after = NULL){ if(is.character(.before)){ if(!(.before %in% colnames(.data))){ .before <- NULL } } if(is.character(.after)){ if(!(.after %in% colnames(.data))){ .after <- NULL } } tibble::add_column(.data, ..., .before = .before, .after = .after) } # Check if required package is installed required_package <- function(pkg){ if (!requireNamespace(pkg, quietly = TRUE)) { stop( pkg, " package needed to be installed before using this function. ", "Type this in R: install.packages('", pkg, "')" ) } } # Check if a given column name is in the data assertthat_column_exists <-function(data, cols){ .diff <- setdiff(cols, colnames(data)) if(!.is_empty(.diff)){ stop("Can't find the following variable(s) in the data: ", paste(col, collapse = ", ")) } } # remove null elements from a list remove_null_items <- function(.list){ Filter(Negate(is.null), .list) } # Count a pattern in a string str_count <- function(x, pattern){ lengths(regmatches(x, gregexpr(pattern, x))) } # Check if all columns in a data frame are numeric is_all_columns_numeric <- function(data){ data %>% map(is.numeric) %>% unlist() %>% all() } is_lm <- function(object){ inherits(object, "lm") } # Check odd and even numbers is_odd_number <- function(x){ x %% 2 != 0 } is_even_number <- function(x){ x %% 2 == 0 } # Set diff that can keep duplicates set_diff <- function(x, y, keep.dup = FALSE){ if(!keep.dup) res <- setdiff(x, y) else{ ins <- x %in% y res <- x[!ins] } res } # NSE #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Get the value of enquo variables. Usage: #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # .args <- rlang::enquos(x = x, y = y, ...) %>% # map(~get_quo_vars(data, .)) get_quo_vars <- function (data, vars) { if(rlang::quo_is_missing(vars)){ return(NULL) } names(data) %>% tidyselect::vars_select(!!vars) %>% magrittr::set_names(NULL) } # .args <- rlang::enquos(x = x, y = y, ...) %>% # get_quo_vars_list(data, .) get_quo_vars_list <- function(data, .enquos){ . <- NULL res <- .enquos %>% map(~get_quo_vars(data, .)) res <- map(res, set_empty_to_null ) res } # pipe friendly alias of get_quo_vars_list select_quo_variables <- function(.enquos, data){ get_quo_vars_list(data, .enquos) } set_empty_to_null <- function(x){ if(.is_empty(x)) x <- NULL x } # Extract variables used in a formula #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: get_formula_left_hand_side <- function(formula){ deparse(formula[[2]]) } get_formula_right_hand_side <- function(formula){ attr(stats::terms(formula), "term.labels") } .extract_formula_variables <- function(formula){ outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) list(outcome = outcome, group = group) } # Get formula error term: len ~ dose*diet + Error(id/diet) # retruns Error(id/diet) get_formula_error_term <- function(formula){ rhs <- get_formula_right_hand_side(formula) error.term <- rhs[grepl("^Error\\(", rhs)] error.term } # Get variables included in the error terms get_formula_error_vars <- function(formula){ error.term <- get_formula_error_term(formula) all.vars(parse(text = error.term)) } is_error_term_in_formula <- function(formula){ error.term <- get_formula_error_term(formula) length(error.term) > 0 } # Grouping variables manipulation #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: guess_number_of_groups <- function(data, group){ if(.is_empty(group)){ number.of.groups <- 1 # Null model } else{ number.of.groups <- data %>% pull(!!group) %>% unique() %>% length() } number.of.groups } get_levels <- function(data, group){ data %>% pull(!!group) %>% levels() } # Convert a group column into a factor if this is not already the case #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # group.col column name containing groups # ref.group: optional reference group # ToothGrowth %>% .as_factor("dose", ref.group = "0.5") %>% pull("dose") .as_factor <- function (data, group.col, ref.group = NULL){ if(.is_empty(group.col)) return(data) group.values <- data %>% pull(group.col) if(!is.factor(group.values)) group.values <- as.factor(group.values) if(!is.null(ref.group)){ if(ref.group != "") group.values <- stats::relevel(group.values, ref.group) } data %>% mutate(!!group.col := group.values) } # Guess p-value column name from a statistical test output #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # transform vector into regular expression as_regexp <- function(x){ . <- NULL gsub(".", "\\.", x, fixed = TRUE) %>% paste(collapse = "$|^") %>% paste("^", ., "$", sep = "") } # Generate all possible pairs of a factor levels #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # if ref.group is specified, then all possible pairs, # against reference group, are generated .possible_pairs <- function(group.levels, ref.group = NULL){ # Ref group should be always the first group if(!is.null(ref.group)) group.levels <- c(ref.group, group.levels) group.levels <- unique(group.levels) # Generate possible pairs possible.pairs <- utils::combn(group.levels, 2) %>% as.data.frame() mate1 <- possible.pairs[1,] # select only comparisons against ref.group (if specified) if(!is.null(ref.group)) possible.pairs <- possible.pairs %>% select(which(mate1 == ref.group)) possible.pairs %>% as.list() } # Create a tidy statistical output #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Generic function to create a tidy statistical output as_tidy_stat <- function(x, round.p = TRUE, digits = 3, stat.method = NULL){ estimate <- estimate1 <- estimate2 <- p.value <- alternative <- p <- NULL res <- tidy(x) if(!is.null(stat.method)){ res %<>% mutate(method = stat.method) } else if("method" %in% colnames(res)){ stat.method <- get_stat_method(x) res %<>% mutate(method = stat.method) } if("p.value" %in% colnames(res)){ res<- res %>% rename(p = p.value) if(round.p) res <- res %>% mutate(p = signif(p, digits)) } if("parameter" %in% colnames(res)){ res <- res %>% rename(df = .data$parameter) } res } get_stat_method <- function(x){ if(inherits(x, c("aov", "anova"))){ return("Anova") } available.methods <- c( "T-test", "Wilcoxon", "Kruskal-Wallis", "Pearson", "Spearman", "Kendall", "Sign-Test", "Cohen's d", "Chi-squared test" ) used.method <- available.methods %>% map(grepl, x$method, ignore.case = TRUE) %>% unlist() if(sum(used.method) > 0){ results <- available.methods %>% extract(used.method) if(length(results) >= 2) results <- paste(results, collapse = " ") } else results <- x$method results } # Check if en object is empty #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .is_empty <- function(x){ length(x) == 0 } # Check if is a list #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .is_list <- function(x){ inherits(x, "list") } # Returns the levels of a factor variable #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .levels <- function(x){ if(!is.factor(x)) x <- as.factor(x) levels(x) } # Add/remove items in a list #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: add_item <- function(.list, ...){ pms <- list(...) for(pms.names in names(pms)){ .list[[pms.names]] <- pms[[pms.names]] } .list } remove_item <- function(.list, items){ for(item in items) .list[[item]] <- NULL .list } remove_null_items <- function(.list){ Filter(Negate(is.null), .list) } # depreciated .add_item <- function(.list, ...){ add_item(.list, ...) } # First letter uppercase #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: to_uppercase_first_letter <- function(x) { if(is.null(x)) return(x) substr(x, 1, 1) <- toupper(substr(x, 1, 1)) x } # Data conversion #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: as_matrix <- function(x){ if(inherits(x, "tbl_df")){ tibble_to_matrix(x) } else if(inherits(x, "matrix")){ x } else if(is.data.frame(x)){ if("rowname" %in% colnames(x)){ x %>% tibble::remove_rownames() %>% tibble::column_to_rownames("rowname") %>% as_matrix() } else { as.matrix(x) } } else{ as.matrix(x) } } # Convert a tbl to matrix tibble_to_matrix <- function(x){ x <- as.data.frame(x) rownames(x) <- x[, 1] x <- x[, -1] as.matrix(x) } # Convert a matrix to standard data frame matrix_to_dataframe <- function(x){ x <- as.data.frame(x, stringsAsFactors = FALSE) %>% add_column(rowname = rownames(x), .before = 1) rownames(x) <- NULL x } # Convert a matrix to tibble matrix_to_tibble <- function(x){ as_tibble(x, rownames = "rowname") } # Replace empty space as na replace_empty_by <- function(x, replacement = NA){ x %>% keep_only_tbl_df_classes() %>% dplyr::mutate_all( function(x){x[x==""] <- replacement; x} ) } # Correlation analysis #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Stop if not an object of class cor_mat #+++++++++++++++++++++++++++++++++++++++++++++++++++++++ stop_ifnot_cormat <- function(x){ if(!inherits(x, "cor_mat")){ stop("An object of class cor_mat is required") } } # Subset a correlation matrix, return a tibble #+++++++++++++++++++++++++++++++++++++++++++++++++++++++ subset_matrix <- function(x, vars, row.vars = vars, col.vars = vars){ if(inherits(x, c("tbl_df", "data.frame"))){ . <- NULL x %>% as_matrix() %>% .[row.vars, col.vars, drop = FALSE] %>% as_tibble(rownames = "rowname") } else if(inherits(x, "matrix")){ x[row.vars, col.vars, drop = FALSE] %>% as_tibble(rownames ="rowname") } else{ stop("x should be a data frame or rownames") } } # Tidy Select #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Collect variables provided by users; get selected variables get_selected_vars <- function(x, ..., vars = NULL){ if(is_grouped_df(x)) x <- x %>% dplyr::ungroup() dot.vars <- rlang::quos(...) if(length(vars) > 0){ return(vars) } if (length(dot.vars) == 0) selected <- colnames(x) else selected <- tidyselect::vars_select(names(x), !!! dot.vars) selected %>% as.character() } # Return dot variables ----------------------- get_dot_vars <- function(...){ rlang::quos(...) %>% map(rlang::quo_text) %>% unlist() } get_existing_dot_vars <- function(data, ...){ tidyselect::vars_select(colnames(data), !!!rlang::quos(...)) } # Select numeric columns #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: select_numeric_columns <- function(data){ if(is_grouped_df(data)) data <- data %>% dplyr::ungroup() data %>% dplyr::select_if(is.numeric) } # Add a class to an object #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: add_class <- function(x, .class){ class(x) <- unique(c(.class, class(x))) x } prepend_class <- function(x, .class){ current.class <- class(x) diff.class <- setdiff(class(x), .class) x <- structure(x, class = c(.class, diff.class)) x } remove_class <- function(x, toremove){ class(x) <- setdiff(class(x), toremove) x } keep_only_tbl_df_classes <- function(x){ toremove <- setdiff(class(x), c("tbl_df", "tbl", "data.frame")) if(length(toremove) > 0){ x <- remove_class(x, toremove) } x } # Add/set attributes #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: set_attrs <- function (x, ...) { attrs <- list(...) attributes(x) <- c(attributes(x), attrs) x } # Correlation analysis #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Check classes #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .is_cor_mat <- function(x){ inherits(x, "cor_mat") } .is_cor_test <- function(x){ inherits(x, "cor_test") } # Convert a cor_mat_tri to numeric data as_numeric_triangle <- function(x){ rrowname <- x %>% pull(1) res <- x %>% replace_empty_by(NA) %>% select(-1) %>% mutate_all(as.numeric) %>% add_column(rowname = rrowname, .before = 1) res } # Create label for each row in a grouped data #:::::::::::::::::::::::::::::::::::::::::::::::: # Labels are the combination of the levels of the grouping variables # ex: dose:0.5,supp:VC create_grouped_data_label <- function(data){ if(!is_grouped_df(data)){ stop("data should be a grouped data") } .nested <- nest(data) .vars <- dplyr::group_vars(data) .data <- .nested %>% select(!!!syms(.vars)) for(.var in .vars){ values <- .data %>% pull(!!.var) .data <- .data %>% mutate(!!.var := paste0(.var, ":", values)) } .results <- .data %>% purrr::pmap(paste, sep = ",") %>% unlist() .results } #:::::::::::::::::::::::::::::::::::::::::::::::: # Helper functions to process rstatix test results #:::::::::::::::::::::::::::::::::::::::::::::::: is_rstatix_test <- function(x){ inherits(x, "rstatix_test") } # get rstatix test arguments get_test_arguments <- function(test){ attr(test, "args") } # get test grouping variables # exist when tests are performed on grouped data get_test_grouping_vars <- function(test){ args <- get_test_arguments(test) grouping.vars <- dplyr::group_vars(args$data) if(.is_empty(grouping.vars)) grouping.vars <- NULL grouping.vars } # Get and set the test attributes: class and attr # used to propagate attributes get_test_attributes <- function(test){ .attributes <- attributes(test) .attributes$names <- .attributes$row.names <- NULL .attributes } set_test_attributes <- function(test, .attributes){ class(test) <- .attributes$class .attributes$class <- NULL for (attr.name in names(.attributes)){ attr(test, attr.name ) <- .attributes[[attr.name]] } test } get_group_size <- function(data, group){ result <- data %>% group_by(!!sym(group)) %>% dplyr::count() n <- result$n group.levels <- result %>% pull(1) names(n) <- group.levels n } stop_ifnot_class <- function(x, .class){ object.name <- deparse(substitute(x)) if(!inherits(x, .class)){ stop(object.name, " should be an object of class: ", paste(.class, collapse = ", ")) } } # Allowed pairwise comparison tests get_pairwise_comparison_methods <- function(){ c( t_test = "T test", wilcox_test = "Wilcoxon test", sign_test = "Sign test", dunn_test = "Dunn test", emmeans_test = "Emmeans test", tukey_hsd = "Tukey HSD", games_howell_test = "Games Howell", prop_test = "Z-Prop test", fisher_test = "Fisher's exact test", chisq_test = "Chi-square test", exact_binom_test = "Exact binomial test", mcnemar_test = "McNemar test" ) } # Bootstrap confidence intervals ------------------------- get_boot_ci <- function(data, stat.func, conf.level = 0.95, type = "perc", nboot = 500){ required_package("boot") Boot = boot::boot(data, stat.func, R = nboot) BCI = boot::boot.ci(Boot, conf = conf.level, type = type, parallel = "multicore") type <- switch( type, norm = "normal", perc = "percent", basic = "basic", bca = "bca", stud = "student", type ) CI <- as.vector(BCI[[type]]) %>% utils::tail(2) %>% round_value(digits = 2) CI } get_complete_cases <- function(data){ data %>% filter(complete.cases(data)) } # transform squared matrix into tidy data frame tidy_squared_matrix <- function(data, value = "value"){ data %>% as_tibble(rownames = "group2") %>% gather(key = "group1", value = !!value, -.data$group2) %>% stats::na.omit() %>% as_tibble() %>% select(.data$group1, everything()) } # Binomial proportion confidence interval get_prop_conf_int <- function(x, n, p = 0.5, conf.level = 0.95, alternative = "two.sided"){ .get_conf <- function(x, n, p, alternative, conf.level){ res <- stats::binom.test(x, n, p, alternative, conf.level)$conf.int tibble(conf.low = res[1], conf.high = res[2]) } results <- list(x = x, n = n, p = p) %>% purrr::pmap( .get_conf, conf.level = conf.level, alternative = alternative ) %>% dplyr::bind_rows() } rstatix/R/box_m.R0000644000176200001440000000513013672604762013414 0ustar liggesusers#' @include utilities.R #' @importFrom stats cov #' @importFrom stats pchisq NULL #' Box's M-test for Homogeneity of Covariance Matrices #' @description Performs the Box's M-test for homogeneity of covariance matrices #' obtained from multivariate normal data according to one grouping variable. #' The test is based on the chi-square approximation. #' @param data a numeric data.frame or matrix containing n observations of p #' variables; it is expected that n > p. #' @param group a vector of length n containing the class of each #' observation; it is usually a factor. #' @return A data frame containing the following components: #' \itemize{ #' \item{statistic }{an approximated value of the chi-square distribution.} #' \item{parameter }{the degrees of freedom related of the test statistic in this case that it follows a Chi-square distribution.} #' \item{p.value }{the p-value of the test.} #' \item{method }{the character string "Box's M-test for Homogeneity of Covariance Matrices".} #' } #' @examples #' data(iris) #' box_m(iris[, -5], iris[, 5]) #' @export box_m <-function(data, group) { if (!inherits(data, c("data.frame", "matrix"))) stop("'data' must be a numeric data.frame or matrix!") if (length(group) != nrow(data)) stop("incompatible dimensions!") dname <- deparse(substitute(data)) data <- as.matrix(data) group <- as.factor(as.character(group)) p <- ncol(data) nlev <- nlevels(group) lev <- levels(group) dfs <- tapply(group, group, length) - 1 if (any(dfs < p)) warning("there are one or more levels with less observations than variables!") mats <- aux <- list() for(i in 1:nlev) { mats[[i]] <- cov(data[group == lev[i], , drop = FALSE]) aux[[i]] <- mats[[i]] * dfs[i] } names(mats) <- lev pooled <- Reduce("+", aux) / sum(dfs) logdet <- log(unlist(lapply(mats, det))) minus2logM <- sum(dfs) * log(det(pooled)) - sum(logdet * dfs) sum1 <- sum(1 / dfs) Co <- (((2 * p^2) + (3 * p) - 1) / (6 * (p + 1) * (nlev - 1))) * (sum1 - (1 / sum(dfs))) X2 <- minus2logM * (1 - Co) dfchi <- (choose(p, 2) + p) * (nlev - 1) pval <- pchisq(X2, dfchi, lower.tail = FALSE) out <- structure( list(statistic = c("Chi-Sq (approx.)" = X2), parameter = c(df = dfchi), p.value = pval, cov = mats, pooled = pooled, logDet = logdet, data.name = dname, method = "Box's M-test for Homogeneity of Covariance Matrices" ), class = c("htest", "boxM") ) out <- broom::tidy(out) return(out) } rstatix/R/cor_reorder.R0000644000176200001440000000223713470305624014611 0ustar liggesusers#' @include utilities.R NULL #' Reorder Correlation Matrix #' @description reorder correlation matrix, according to the coefficients, #' using the hierarchical clustering method. #'@param x a correlation matrix. Particularly, an object of class \code{cor_mat}. #'@return a data frame #'@seealso \code{\link{cor_mat}()}, \code{\link{cor_gather}()}, \code{\link{cor_spread}()} #' @examples #' # Compute correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_mat() #' # #' # Reorder by correlation and get p-values #' #:::::::::::::::::::::::::::::::::::::::::: #' # Reorder #' cor.mat %>% #' cor_reorder() #' # Get p-values of the reordered cor_mat #' cor.mat %>% #' cor_reorder() %>% #' cor_get_pval() #' #' @name cor_reorder #' @export cor_reorder <- function(x){ pvalue <- attr(x, "pvalue") x <- as_matrix(x) hc <- stats::as.dist(1 - x) %>% stats::hclust(method = "complete") x <- x %>% subset_matrix(hc$order) if(!is.null(pvalue)){ pvalue <- pvalue %>% subset_matrix(hc$order) x <- x %>% set_attrs(pvalue = pvalue) %>% add_class("cor_mat") } x } rstatix/R/df.R0000644000176200001440000002464313651764434012713 0ustar liggesusers#' @include utilities.R NULL #' Select Columns in a Data Frame #' #' @description A wrapper around the \code{\link[dplyr]{select}()} function for #' selection data frame columns. Supports standard and non standard #' evaluations. Usefull to easily program with \code{dplyr} #' @param data a data frame #' @param vars a character vector containing the variable names of interest. #' @param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to select a variable of interest. #' #' @return a data frame #' @examples #' df <- head(ToothGrowth) #' df #' #' # Select column using standard evaluation #' df %>% df_select(vars = c("dose", "len")) #' #' # Select column using non-standard evaluation #' df %>% df_select(dose, len) #' @rdname df_select #' @export df_select <- function(data, ..., vars = NULL){ if(is.null(vars)){ results <- data %>% select(...) } else{ results <- data %>% select(!!!syms(vars)) } results } #' Arrange Rows by Column Values #' #' @description Order the rows of a data frame by values of specified columns. #' Wrapper arround the \code{\link[dplyr]{arrange}()} function. Supports #' standard and non standard evaluation. #' @param data a data frame #' @param vars a character vector containing the variable names of interest. #' @param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to select a variable of interest. Use #' \code{\link[dplyr]{desc}()} to sort a variable in descending order. #' @param .by_group If TRUE, will sort first by grouping variable. Applies to #' grouped data frames only. #' #' @return a data frame #' @examples #' df <- head(ToothGrowth) #' df #' #' # Select column using standard evaluation #' df %>% df_arrange(vars = c("dose", "len")) #' #' # Select column using non-standard evaluation #' df %>% df_arrange(dose, desc(len)) #' @rdname df_arrange #' @export df_arrange <- function(data, ..., vars = NULL, .by_group = FALSE ){ if(is.null(vars)){ results <- data %>% dplyr::arrange(..., .by_group = .by_group) } else{ results <- data %>% dplyr::arrange(!!!syms(vars), .by_group = .by_group) } results } #' Group a Data Frame by One or more Variables #' #' @description Group a data frame by one or more variables. Supports standard #' and non standard evaluation. #' @inheritParams df_select #' @examples #' #' # Non standard evaluation #' by_dose <- head(ToothGrowth) %>% #' df_group_by(dose) #' by_dose #' #' # Standard evaluation #' head(ToothGrowth) %>% #' df_group_by(vars = c("dose", "supp")) #' @rdname df_group_by #' @export df_group_by <- function(data, ..., vars = NULL){ if(is.null(vars)){ results <- data %>% group_by(...) } else{ results <- data %>% group_by(!!!syms(vars)) } results } #' Nest a Tibble By Groups #' #' @description Nest a tibble data frame using grouping specification. Supports standard and non standard evaluation. #' @param data a data frame #' @param ... One or more unquoted expressions (or variable names) separated by #' commas. Used as grouping variables. #' @param vars a character vector containing the grouping variables of interest. #' #' @return A tbl with one row per unique combination of the grouping variables. #' The first columns are the grouping variables, followed by a list column of #' tibbles with matching rows of the remaining columns. #' @examples #' #' # Non standard evaluation #' ToothGrowth %>% #' df_nest_by(dose, supp) #' #' # Standard evaluation #' ToothGrowth %>% #' df_nest_by(vars = c("dose", "supp")) #' #' @rdname df_nest_by #' @export df_nest_by <- function(data, ..., vars = NULL){ data %>% df_group_by(..., vars = vars) %>% nest() %>% ungroup() } #' Split a Data Frame into Subset #' #' @description Split a data frame by groups into subsets or data panel. Very #' similar to the function \code{\link{df_nest_by}()}. The only difference is #' that, it adds label to each data subset. Labels are the combination of the #' grouping variable levels. The column holding labels are named "label". #' @inheritParams df_nest_by #' @inheritParams df_label_both #' @param labeller A function that takes a data frame, the grouping variables, #' label_col and label_sep arguments, and add labels into the data frame. #' Example of possible values are: \code{\link{df_label_both}()} and #' \code{\link{df_label_value}()}. #' #' @return A tbl with one row per unique combination of the grouping variables. #' The first columns are the grouping variables, followed by a list column of #' tibbles with matching rows of the remaining columns, and a column named #' label, containing labels. #' @examples #' #' # Split a data frame #' # ::::::::::::::::::::::::::::::::::::::::::::::::: #' # Create a grouped data #' res <- ToothGrowth %>% #' df_split_by(dose, supp) #' res #' #' # Show subsets #' res$data #' #' # Add panel/subset labels #' res <- ToothGrowth %>% #' df_split_by(dose, supp) #' res #' @rdname df_split_by #' @export df_split_by <- function(data, ..., vars = NULL, label_col = "label", labeller = df_label_both, sep = c(", ", ":")){ groups <- df_get_var_names(data, ..., vars = vars) data %>% df_nest_by(vars = groups) %>% labeller(vars = groups, label_col = label_col, sep = sep) %>% mutate(data = map2(.data$data, .data[[label_col]], add_panel_name, col = label_col)) } #' Functions to Label Data Frames by Grouping Variables #' #' @description Functions to label data frame rows by one or multiple grouping #' variables. #' #' @inheritParams df_nest_by #' @param label_col column to hold the label of the data subsets. Default column #' name is "label". #' @param sep String separating labelling variables and values. Should be of #' length 2 in the function \code{df_label_both()}. 1) One sep is used to #' separate groups, for example ','; 2) The other sep between group name and #' levels; for example ':'. #' @return a modified data frame with a column containing row labels. #' @examples #' # Data preparation #' df <- head(ToothGrowth) #' #' # Labelling: Non standard evaluation #' df %>% #' df_label_both(dose, supp) #' #' # Standard evaluation #' df %>% #' df_label_both(dose, supp) #' #' # Nesting the data then label each subset by groups #' ToothGrowth %>% #' df_nest_by(dose, supp) %>% #' df_label_both(supp, dose) #' #' @describeIn df_label_value Displays both the variable name and the factor value. #' @export df_label_both <- function(data, ..., vars = NULL, label_col = "label", sep = c(", ", ":")){ vars <- df_get_var_names(data, ..., vars = vars) if(length(sep) < 2){ warning( "Argument sep sould be of length 2, otherwise it will be ignored; example: sep = c(', ', ':', )\n", " 2. One sep is used to separate groups, for example ','\n", " 1. The other sep between group name and levels; for example ':'", call. = FALSE ) sep <- c(":", ", ") } label <- data %>% df_select(vars = vars) %>% concat_groupname_to_levels(vars, sep = sep[2]) %>% df_unite_factors(col = label_col, vars = vars, sep = sep[1]) %>% pull(!!label_col) data %>% mutate(!!label_col := label) } #' @describeIn df_label_value Displays only the value of a factor. #' @export df_label_value <- function(data, ..., vars = NULL, label_col = "label", sep = ", "){ vars <- df_get_var_names(data, ..., vars = vars) label <- data %>% df_select(vars = vars) %>% df_unite_factors(col = label_col, vars = vars, sep = sep[1]) %>% pull(!!label_col) data %>% mutate(!!label_col := label) } # Add panel label to a data # Labels are the combination of the grouping variable labels add_panel_label <- function(data, groups, col = "label") { label <- data %>% df_select(vars = groups) %>% concat_groupname_to_levels(groups, sep = ":") %>% df_unite_factors(col = col, vars = groups, sep = ", ") %>% pull(!!col) data %>% mutate(!!col := label) } # Add a column containing panel name add_panel_name <- function(data, panel, col = "label") { data %>% mutate(!!col := !!panel) } concat_groupname_to_levels <- function(group.data, groups, sep = ":"){ purrr::map2( group.data, groups, function(x, name) {paste(name, x, sep = sep)} ) %>% as_tibble() } #' Unite Multiple Columns into One #' #' @description Paste together multiple columns into one. Wrapper arround #' \code{\link[tidyr]{unite}()} that supports standard and non standard #' evaluation. #' @inheritParams tidyr::unite #' @param data a data frame #' @param col the name of the new column as a string or a symbol. #' @param ... a selection of columns. One or more unquoted expressions (or variable names) separated by #' commas. #' @param vars a character vector containing the column names of interest. #' @examples #' # Non standard evaluation #' head(ToothGrowth) %>% #' df_unite(col = "dose_supp", dose, supp) #' #' # Standard evaluation #' head(ToothGrowth) %>% #' df_unite(col = "dose_supp", vars = c("dose", "supp")) #' @describeIn df_unite Unite multiple columns into one. #' @export df_unite <- function(data, col, ..., vars = NULL, sep = "_", remove = TRUE, na.rm = FALSE){ if(is.null(vars)){ results <- data %>% tidyr::unite( col = !!col, ..., sep = sep, remove = remove, na.rm = na.rm ) } else{ results <- data %>% tidyr::unite( col = !!col, !!!syms(vars), sep = sep, remove = remove, na.rm = na.rm ) } results } #' @export #' @describeIn df_unite Unite factor columns. First, order factors levels then #' merge them into one column. The output column is a factor. df_unite_factors <- function(data, col, ..., vars = NULL, sep = "_", remove = TRUE, na.rm = FALSE){ vars <- df_get_var_names(data, ..., vars = vars) data %>% dplyr::arrange(!!!syms(vars)) %>% df_unite(col = col, vars = vars, sep = sep, remove = remove, na.rm = na.rm) %>% dplyr::mutate_at(col, function(x){factor(x, levels = unique(x))}) } #' Get User Specified Variable Names #' #' @description Returns user specified variable names. Supports standard and non standard evaluation. #' @inheritParams df_select #' @return a character vector #' @examples #' #' # Non standard evaluation #' ToothGrowth %>% #' df_get_var_names(dose, len) #' #' # Standard evaluation #' ToothGrowth %>% #' df_get_var_names(vars = c("len", "dose")) #' @rdname df_get_var_names #' @export df_get_var_names <- function(data, ..., vars = NULL){ dot_vars <- tidyselect::vars_select(colnames(data), !!!rlang::quos(...)) unique(c(vars, dot_vars)) } rstatix/R/get_pvalue_position.R0000644000176200001440000004013013735200714016353 0ustar liggesusers#' @include utilities.R NULL #'Autocompute P-value Positions For Plotting Significance #'@description Compute p-value x and y positions for plotting significance #' levels. Many examples are provided at : \itemize{ \item #' \href{https://www.datanovia.com/en/blog/how-to-add-p-values-onto-a-grouped-ggplot-using-the-ggpubr-r-package/}{How #' to Add P-Values onto a Grouped GGPLOT using the GGPUBR R Package} \item #' \href{https://www.datanovia.com/en/blog/ggpubr-how-to-add-adjusted-p-values-to-a-multi-panel-ggplot/}{How #' to Add Adjusted P-values to a Multi-Panel GGPlot} \item #' \href{https://www.datanovia.com/en/blog/ggpubr-how-to-add-p-values-generated-elsewhere-to-a-ggplot/}{How #' to Add P-Values Generated Elsewhere to a GGPLOT} } #'@inheritParams t_test #'@param ref.group a character string specifying the reference group. If #' specified, for a given grouping variable, each of the group levels will be #' compared to the reference group (i.e. control group). #'@param fun summary statistics functions used to compute automatically suitable #' y positions of p-value labels and brackets. Possible values include: #' \code{"max", "mean", "mean_sd", "mean_se", "mean_ci", "median", #' "median_iqr", "median_mad"}. #' #' For example, if \code{fun = "max"}, the y positions are guessed as follow: #' \itemize{ \item 1. Compute the maximum of each group (groups.maximum) \item #' 2. Use the highest groups maximum as the first bracket y position \item 3. #' Add successively a step increase for remaining bracket y positions. } #' #' When the main plot is a boxplot, you need the option \code{fun = "max"}, to #' have the p-value bracket displayed at the maximum point of the group. #' #' In some situations the main plot is a line plot or a barplot showing the #' \code{mean+/-error bars} of the groups, where error can be SE (standard #' error), SD (standard deviation) or CI (confidence interval). In this case, #' to correctly compute the bracket y position you need the option \code{fun = #' "mean_se"}, etc. #'@param step.increase numeric vector with the increase in fraction of total #' height for every additional comparison to minimize overlap. #'@param y.trans a function for transforming y axis scale. Value can be #' \code{log2}, \code{log10} and \code{sqrt}. Can be also any custom function #' that can take a numeric vector as input and returns a numeric vector, #' example: \code{y.trans = function(x){log2(x+1)}} #'@param test an object of class \code{rstatix_test} as returned by #' \code{\link{t_test}()}, \code{\link{wilcox_test}()}, #' \code{\link{sign_test}()}, \code{\link{tukey_hsd}()}, #' \code{\link{dunn_test}()}. #'@param x variable on x axis. #'@param group group variable (legend variable). #'@param dodge dodge width for grouped ggplot/test. Default is 0.8. Used only #' when \code{x} specified. #'@param stack logical. If TRUE, computes y position for a stacked plot. Useful #' when dealing with stacked bar plots. #'@param scales Should scales be fixed (\code{"fixed"}, the default), free #' (\code{"free"}), or free in one dimension (\code{"free_y"})?. This option is #' considered only when determining the y position. If the specified value is #' \code{"free"} or \code{"free_y"}, then the step increase of y positions will #' be calculated by plot panels. Note that, using \code{"free"} or #' \code{"free_y"} gives the same result. A global step increase is computed #' when \code{scales = "fixed"}. #' @examples #' # Data preparation #' #:::::::::::::::::::::::::::::::::::: #' df <- ToothGrowth #' df$dose <- as.factor(df$dose) #' df$group <- factor(rep(c(1, 2), 30)) #' head(df) #' #' # Stat tests #' #:::::::::::::::::::::::::::::::::::: #' stat.test <- df %>% #' t_test(len ~ dose) #' stat.test #' #' # Add the test into box plots #' #:::::::::::::::::::::::::::::::::::: #' stat.test <- stat.test %>% #' add_y_position() #' \donttest{ #' if(require("ggpubr")){ #' ggboxplot(df, x = "dose", y = "len") + #' stat_pvalue_manual(stat.test, label = "p.adj.signif", tip.length = 0.01) #' } #' } #'@describeIn get_pvalue_position compute the p-value y positions #'@export get_y_position <- function(data, formula, fun = "max", ref.group = NULL, comparisons = NULL, step.increase = 0.12, y.trans = NULL, stack = FALSE, scales = c("fixed", "free", "free_y")){ # Estimate step increase # 1. Get groups y scale outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) if(.is_empty(group)) group <- NULL if(is_grouped_df(data)) group <- c(group , dplyr::group_vars(data)) yscale <- get_y_scale(data, outcome, group, fun, stack = stack) # 2. Step increase # If fixed scales, then a global step increase is computed, # otherwise step.increase is estimated by panel in get_y_position_core(). scales <- match.arg(scales) if(scales == "fixed"){ step.increase <- step.increase*(yscale$max - yscale$min) } get_y_position_core( data = data, formula = formula, fun = fun, ref.group = ref.group, comparisons = comparisons, step.increase = step.increase, y.trans = y.trans, stack = stack, scales = scales ) } get_y_position_core <- function(data, formula, fun = "max", ref.group = NULL, comparisons = NULL, step.increase = 0.12, y.trans = NULL, stack = FALSE, scales = "fixed"){ if(is_grouped_df(data)){ results <- data %>% doo( get_y_position_core, formula = formula, fun = fun, ref.group = ref.group, comparisons = comparisons, step.increase = step.increase, y.trans = y.trans, stack = stack, scales = scales ) return(results) } outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) # Possible comparisons between groups if(is.null(comparisons)){ if(.is_empty(group)){ comparisons <- list(c("1", "null model")) } else{ comparisons <- data %>% get_comparisons(!!group, !!ref.group) } } ncomparisons <- length(comparisons) group1 <- comparisons %>% get_group(1) group2 <- comparisons %>% get_group(2) k <- 1.08 # Estimate y axis scale yscale <- get_y_scale(data, y = outcome, group = group, fun = fun, stack = stack) if(is.null(step.increase)) step.increase <- yscale$max/20 else if(scales %in% c("free", "free_y")){ step.increase <- step.increase*(yscale$max - yscale$min) } # ystart <- k*yscale$max ystart <- yscale$max + step.increase yend <- ystart + (step.increase*ncomparisons) if(is.null(ref.group)) ref.group <- "" if(ref.group %in% c("all", ".all.")){ # y.position <- yscale$y*k y.position <- yscale$y + step.increase } else{ y.position <- seq( from = ystart, to = yend, length.out = ncomparisons ) } if(!is.null(y.trans)) y.position <- y.trans(y.position) results <- tibble(group1, group2, y.position) %>% mutate(groups = combine_this(group1, group2)) results } #' @describeIn get_pvalue_position add p-value y positions to an object of class \code{rstatix_test} #' @export add_y_position <- function(test, fun = "max", step.increase = 0.12, data = NULL, formula = NULL, ref.group = NULL, comparisons = NULL, y.trans = NULL, stack = FALSE, scales = c("fixed", "free", "free_y")) { scales <- match.arg(scales) asserttat_group_columns_exists(test) .attributes <- get_test_attributes(test) args <- get_test_arguments(test) test <- keep_only_tbl_df_classes(test) if(!is.null(args)){ if(missing(data)) data <- args$data if(missing(formula)) formula <- args$formula if(missing(ref.group)) ref.group <- args$ref.group if(missing(comparisons)) comparisons <- args$comparisons } if(is.null(data) | is.null(formula)){ stop("data and formula arguments should be specified.") } positions <- get_y_position( data = data, formula = formula, fun = fun, ref.group = ref.group, comparisons = comparisons, step.increase = step.increase, y.trans = y.trans, stack = stack, scales = scales ) if(nrow(test) == nrow(positions)){ test$y.position <- positions$y.position test$groups <- positions$groups } else{ # this occurs when tests are grouped by two variables (for ex), # but y positions are grouped by one variable # merging positions and test data frame if("y.position" %in% colnames(test)){ test <- test %>% select(-.data$y.position) } if("groups" %in% colnames(test)){ test <- test %>% select(-.data$groups) } common.columns <- intersect(colnames(test), colnames(positions)) test <- test %>% dplyr::left_join(positions, by = common.columns) } test %>% set_test_attributes(.attributes) } # Compute y scale depending on fun get_y_scale <- function(data, y, group, fun = "max", stack = FALSE){ if(!.is_empty(group)){ desc.stat <- data %>% group_by(!!!syms(group)) %>% get_summary_stats(!!y, type = fun) } else { desc.stat <- data %>% get_summary_stats(!!y, type = fun) } # Add error bars positions if any fun.splitted <- unlist(strsplit(fun, "_", fixed = TRUE)) .center <- fun.splitted[1] .error <- ifelse(length(fun.splitted) == 2, fun.splitted[2], 0) .center <- desc.stat %>% pull(!!.center) if(.error != 0) .error <- desc.stat %>% pull(!!.error) if(stack){ .center <- rep(sum(.center), length(.center)) } y <- .center + .error ymax <- max(y, na.rm = TRUE) ymin <- min(y, na.rm = TRUE) list(y = y, max = ymax, min = ymin) } # Return group1 and group2 values from possible pairs get_group <- function(possible.pairs, index){ possible.pairs %>% map( function(x){as.character(x[index])}) %>% unlist() } # Combine vectors: c(a, b) & c(c, d) --> list(c(a, c), c(b, d)) # ... two or more character vectors combine_this <- function(...){ params <- list(...) purrr::pmap(params, c) } #' @describeIn get_pvalue_position compute and add p-value x positions. #' @export add_x_position <- function(test, x = NULL, group = NULL, dodge = 0.8){ # Checking asserttat_group_columns_exists(test) .attributes <- get_test_attributes(test) if(any(test$group1 %in% c("all", ".all."))) { # case when ref.group = "all" test$group1 <- test$group2 } groups <- c(as.character(test$group1), as.character(test$group2)) %>% unique() %>% setdiff(c("all", ".all.")) # case when ref.group = "all" is_rstatix_test <- inherits(test, "rstatix_test") is.null.model <- ("null model" %in% test$group2) & all(test$group1 %in% 1) is.grouped.by.legend <- test %>% is_stat_test_grouped_by(group) is.grouped.by.x <- test %>% is_stat_test_grouped_by(x) is.basic <- !is.grouped.by.x & !is.grouped.by.legend # Data preparation if(is_rstatix_test) { data <- attr(test, "args")$data if(is.basic & is.null(x)) x <- get_formula_right_hand_side(.attributes$args$formula) else if(is.grouped.by.x & is.null(group)) group <- get_formula_right_hand_side(.attributes$args$formula) } else if(is.basic){ data <- data.frame(x = groups, stringsAsFactors = FALSE) x <- "x" } else{ if(is.grouped.by.x) data <- expand.grid(x = unique(test[[x]]), group = groups) else if(is.grouped.by.legend) data <- expand.grid(x = groups, group = test[[group]]) colnames(data) <- c(x, group) } if(is.null.model) { data$group <- rep(1, nrow(data)) group <- "group" } # Add xmin and x max if(is.basic){ x_coords <- as_numeric_group(data[[x]]) xmin_id <- as.character(test$group1) xmax_id <- as.character(test$group2) } else{ x_coords <- get_grouped_x_position(data, x = x, group = group, dodge = dodge) if(is.grouped.by.legend){ # Add x position to stat test when the test is grouped by the legend variable # Case when you group by legend and pairwise compare between x-axis groups xmin_id <- paste(test$group1, test[[group]], sep = "_") xmax_id <- paste(test$group2, test[[group]], sep = "_") } else if(is.grouped.by.x){ # Add x position to stat test when the test is grouped by the x variable # Case when you pairwise compare legend groups at each x-axis position, # so the data is grouped by x position xmin_id <- paste(test[[x]], test$group1, sep = "_") xmax_id <- paste(test[[x]], test$group2, sep = "_") test$x <- unname(as_numeric_group(test[[x]])) } } test$xmin <- unname(x_coords[xmin_id]) test$xmax <- unname(x_coords[xmax_id]) if(is.null.model) test$xmax <- test$xmin test %>% set_test_attributes(.attributes) } # Compute grouped x positions or coordinates # data is a dataframe containing the x and the group columns get_grouped_x_position<- function(data, x, group, dodge = 0.8){ data <- data.frame(x = data[[x]], group = data[[group]]) %>% dplyr::distinct(.data$x, .data$group) %>% dplyr::arrange(.data$x, .data$group) data$x.position <- as_numeric_group(data$x) # Add group.ranks and ngroups at x position data <- data %>% rstatix::df_nest_by(vars = "x") %>% mutate( data = map(.data$data, function(data){data$group.ranks = 1:nrow(data); data}), n = unlist(map(.data$data, nrow)) ) %>% tidyr::unnest(cols = "data") # Compute x coords d <- data x_coords <- (((dodge - dodge*d$n) / (2*d$n)) + ((d$group.ranks - 1) * (dodge / d$n))) + d$x.position names(x_coords) <- paste(d$x, d$group, sep = "_") x_coords } # Check if a stat test is grouped by a given variable is_stat_test_grouped_by <- function(test, x = NULL){ answer <- FALSE if(!is.null(x)){ if(x %in% colnames(test)){ answer <- TRUE } } answer } # Return a numeric named vector # c("a", "b", "a") -> c(a = 1, b = 2, a = 1) as_numeric_group <- function(x){ grp <- x %>% as.factor() %>% as.numeric() names(grp) <- x grp } #' @describeIn get_pvalue_position compute and add both x and y positions. #' @export add_xy_position <- function(test, x = NULL, group = NULL, dodge = 0.8, stack = FALSE, fun = "max", step.increase = 0.12, scales = c("fixed", "free", "free_y"), ...){ if(missing(dodge)){ if(stack) dodge <- 0 } test %>% add_y_position( fun = fun, step.increase = step.increase, stack = stack, scales = scales, ... ) %>% add_x_position(x = x, group = group, dodge = dodge) } # Helper functions #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% asserttat_group_columns_exists <- function(data){ groups.exist <- all(c("group1", "group2") %in% colnames(data)) if(!groups.exist){ stop("data should contain group1 and group2 columns") } } .is_grouped_test <- function(test, x = NULL){ answer <- FALSE if(!is.null(x)){ if(x %in% colnames(test)){ answer <- TRUE } } answer } .contains_selected_comparisons <- function(test){ answer <- FALSE if(is_rstatix_test(test)){ comparisons <- attr(test, "args")$comparisons answer <- !is.null(comparisons) } answer } # To be removed add_x_position0 <- function(test, x = NULL, dodge = 0.8){ asserttat_group_columns_exists(test) .attributes <- get_test_attributes(test) group1 <- set_diff(test$group1, c("all", ".all."), keep.dup = TRUE) group2 <- set_diff(test$group2, c("all", ".all."), keep.dup = TRUE) groups <- unique(c(group1, group2)) if(.contains_selected_comparisons(test)){ # get all data groups group.var <- get_formula_right_hand_side(.attributes$args$formula) groups <- .attributes$args$data %>% convert_as_factor(vars = group.var) %>% pull(!!group.var) %>% levels() } group1.coords <- group1.ranks <- match(group1, groups) group2.coords <- group2.ranks <- match(group2, groups) n <- length(groups) if(.is_grouped_test(test, x)){ test <- test %>% .as_factor(x) %>% mutate(x = as.numeric(!!sym(x))) # x levels order xpos <- test$x group1.coords <- (((dodge - dodge*n) / (2*n)) + ((group1.ranks - 1) * (dodge / n))) + xpos group2.coords <- (((dodge - dodge*n) / (2*n)) + ((group2.ranks - 1) * (dodge / n))) + xpos } if(.is_empty(group1)) { # case when ref.group = "all" group1.coords <- group2.coords } test %>% mutate(xmin = group1.coords, xmax = group2.coords) %>% set_test_attributes(.attributes) } rstatix/R/levene_test.R0000644000176200001440000000277414011727437014631 0ustar liggesusers#' @include utilities.R #' @importFrom stats median NULL #' Levene's Test #' #' @description Provide a pipe-friendly framework to easily compute Levene's #' test for homogeneity of variance across groups. #' #' Wrapper around the function \code{\link[car]{leveneTest}()}, which can #' additionally handles a grouped data. #' @param data a data frame for evaluating the formula or a model #' @param formula a formula #' @param center The name of a function to compute the center of each group; #' mean gives the original Levene's test; the default, median, provides a more #' robust test. #' @return a data frame with the following columns: df1, df2 #' (df.residual), statistic and p. #' #' @examples #' # Prepare the data #' data("ToothGrowth") #' df <- ToothGrowth #' df$dose <- as.factor(df$dose) #' # Compute Levene's Test #' df %>% levene_test(len ~ dose) #' #' # Grouped data #' df %>% #' group_by(supp) %>% #' levene_test(len ~ dose) #' #' @export levene_test <- function(data, formula, center = median){ if(is_grouped_df(data)){ results <- data %>% doo(~levene_test(., formula = formula, center = center)) return(results) } else if(is_lm(data)){ results <- car::leveneTest(data, center = center) } else{ results <- car::leveneTest(formula, data, center = center) } results <- broom::tidy(results) %>% rename( df1 = .data$df, df2 = .data$df.residual, p = .data$p.value ) %>% select(.data$df1, .data$df2, .data$statistic, .data$p) results } rstatix/R/fisher_test.R0000644000176200001440000001755113640107577014636 0ustar liggesusers#' @include utilities.R NULL #'Fisher's Exact Test for Count Data #'@description Performs Fisher's exact test for testing the null of independence #' of rows and columns in a contingency table. #' #' Wrappers around the R base function \code{\link[stats]{fisher.test}()} but #' have the advantage of performing pairwise and row-wise fisher tests, the #' post-hoc tests following a significant chi-square test of homogeneity for 2xc #' and rx2 contingency tables. #'@inheritParams stats::fisher.test #'@param xtab a contingency table in a matrix form. #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #'@param detailed logical value. Default is FALSE. If TRUE, a detailed result is #' shown. #'@param ... Other arguments passed to the function \code{fisher_test()}. #' #'@return return a data frame with some the following columns: \itemize{ \item #' \code{group}: the categories in the row-wise proportion tests. \item #' \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. \item #' \code{method}: the used statistical test. \item \code{p.signif, #' p.adj.signif}: the significance level of p-values and adjusted p-values, #' respectively. \item \code{estimate}: an estimate of the odds ratio. Only #' present in the 2 by 2 case. \item \code{alternative}: a character string #' describing the alternative hypothesis. \item \code{conf.low,conf.high}: a #' confidence interval for the odds ratio. Only present in the 2 by 2 case and #' if argument conf.int = TRUE.} #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' @examples #' #' # Comparing two proportions #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: frequencies of smokers between two groups #' xtab <- as.table(rbind(c(490, 10), c(400, 100))) #' dimnames(xtab) <- list( #' group = c("grp1", "grp2"), #' smoker = c("yes", "no") #' ) #' xtab #' # compare the proportion of smokers #' fisher_test(xtab, detailed = TRUE) #' #' # Homogeneity of proportions between groups #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # H0: the proportion of smokers is similar in the four groups #' # Ha: this proportion is different in at least one of the populations. #' # #' # Data preparation #' grp.size <- c( 106, 113, 156, 102 ) #' smokers <- c( 50, 100, 139, 80 ) #' no.smokers <- grp.size - smokers #' xtab <- as.table(rbind( #' smokers, #' no.smokers #' )) #' dimnames(xtab) <- list( #' Smokers = c("Yes", "No"), #' Groups = c("grp1", "grp2", "grp3", "grp4") #' ) #' xtab #' #' # Compare the proportions of smokers between groups #' fisher_test(xtab, detailed = TRUE) #' #' # Pairwise comparison between groups #' pairwise_fisher_test(xtab) #' #' #' # Pairwise proportion tests #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: Titanic #' xtab <- as.table(rbind( #' c(122, 167, 528, 673), #' c(203, 118, 178, 212) #' )) #' dimnames(xtab) <- list( #' Survived = c("No", "Yes"), #' Class = c("1st", "2nd", "3rd", "Crew") #' ) #' xtab #' # Compare the proportion of survived between groups #' pairwise_fisher_test(xtab) #' #' # Row-wise proportion tests #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: Titanic #' xtab <- as.table(rbind( #' c(180, 145), c(179, 106), #' c(510, 196), c(862, 23) #' )) #' dimnames(xtab) <- list( #' Class = c("1st", "2nd", "3rd", "Crew"), #' Gender = c("Male", "Female") #' ) #' xtab #' # Compare the proportion of males and females in each category #' row_wise_fisher_test(xtab) #' #' # A r x c table Agresti (2002, p. 57) Job Satisfaction #' Job <- matrix(c(1,2,1,0, 3,3,6,1, 10,10,14,9, 6,7,12,11), 4, 4, #' dimnames = list(income = c("< 15k", "15-25k", "25-40k", "> 40k"), #' satisfaction = c("VeryD", "LittleD", "ModerateS", "VeryS"))) #' fisher_test(Job) #' fisher_test(Job, simulate.p.value = TRUE, B = 1e5) #' @describeIn fisher_test performs Fisher's exact test for testing the null of #' independence of rows and columns in a contingency table with fixed #' marginals. Wrapper around the function \code{\link[stats]{fisher.test}()}. #' @export fisher_test <- function(xtab, workspace = 200000, alternative = "two.sided", conf.int = TRUE, conf.level = 0.95, simulate.p.value = FALSE, B = 2000, detailed = FALSE, ...){ if(is.data.frame(xtab)) xtab <- as.matrix(xtab) args <- as.list(environment()) %>% add_item(method = "fisher_test") results <- stats::fisher.test( xtab, workspace = workspace, alternative = alternative, conf.int = conf.int, conf.level = conf.level, simulate.p.value = simulate.p.value, B = B, ... ) %>% as_tidy_stat() %>% add_significance("p") %>% add_columns(n = sum(xtab), .before = 1) %>% mutate(method = "Fisher's Exact test") if(!detailed) results <- remove_details(results, method = "prop.test") results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "fisher_test")) } #' @describeIn fisher_test pairwise comparisons between proportions, a post-hoc #' tests following a significant Fisher's exact test of homogeneity for 2xc #' design. #' @export pairwise_fisher_test <- function(xtab, p.adjust.method = "holm", detailed = FALSE, ...){ if(is.data.frame(xtab)) xtab <- as.matrix(xtab) if(ncol(xtab) > 2 & nrow(xtab) == 2) xtab <- t(xtab) if (is.null(colnames(xtab)) | any(0 %in% nchar(colnames(xtab)))) { colnames(xtab) <- paste0("col", 1:ncol(xtab)) } if (is.null(rownames(xtab)) | any(0 %in% nchar(rownames(xtab)))) { rownames(xtab) <- paste0("row", 1:nrow(xtab)) } if(ncol(xtab) > 2){ stop("A two-dimensionnal contingency table required.") } compare_pair <- function(rows, xtab, ...){ rows <- as.character(rows) fisher_test(xtab[rows, ], detailed = detailed, ...) %>% add_columns(group1 = rows[1], group2 = rows[2], .before = 1) %>% keep_only_tbl_df_classes() } args <- c(as.list(environment()), list(...)) %>% add_item(method = "fisher_test") comparisons <- rownames(xtab) %>% .possible_pairs() results <- comparisons %>% map(compare_pair, xtab, ...) %>% bind_rows() %>% adjust_pvalue("p", method = p.adjust.method) %>% add_significance("p.adj") %>% mutate(p.adj = signif(.data$p.adj, digits = 3)) %>% select(-.data$p.signif) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "fisher_test")) } #' @describeIn fisher_test performs row-wise Fisher's exact test of count data, a post-hoc tests following a significant chi-square test #' of homogeneity for rx2 contingency table. The test is conducted for each category (row). #' @export row_wise_fisher_test <- function(xtab, p.adjust.method = "holm", detailed = FALSE, ...){ if(is.data.frame(xtab)) xtab <- as.matrix(xtab) if(!inherits(xtab, c("matrix", "table"))){ stop("An object of class 'matrix' or 'table' required") } if(ncol(xtab) !=2){ stop("A cross-tabulation with two columns required") } args <- c(as.list(environment()), list(...)) %>% add_item(method = "fisher_test") # Create xtab for each category (row) columns.total <- margin.table(xtab, 2) create_xtab <- function(x, n){ as.data.frame(rbind(x, n-x)) } xtab.list <- apply(xtab, 1, create_xtab, columns.total ) results <- xtab.list %>% map(fisher_test, detailed = detailed, ...) %>% map(keep_only_tbl_df_classes) %>% bind_rows(.id = "group") %>% adjust_pvalue(method = p.adjust.method) %>% add_significance("p.adj") %>% mutate( p = signif(.data$p, digits = 3), p.adj = signif(.data$p.adj, digits = 3) ) %>% select(-.data$p.signif) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "fisher_test")) } rstatix/R/as_cor_mat.R0000644000176200001440000000240713640065741014414 0ustar liggesusers#' @include utilities.R NULL #' Convert a Correlation Test Data Frame into a Correlation Matrix #' #' @description Convert a correlation test data frame, returned by the #' \code{\link{cor_test}()}, into a correlation matrix format. #' #' @param x an object of class \code{cor_test}. #' @return Returns a data frame containing the matrix of the correlation #' coefficients. The output has an attribute named "pvalue", which contains #' the matrix of the correlation test p-values. #' @seealso \code{\link{cor_mat}()}, \code{\link{cor_test}()} #' @examples #' # Pairwise correlation tests between variables #' #::::::::::::::::::::::::::::::::::::::::::::::: #' res.cor.test <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_test() #' res.cor.test #' #' # Convert the correlation test into a correlation matrix #' #::::::::::::::::::::::::::::::::::::::::::::::: #' res.cor.test %>% as_cor_mat() #' #' @export as_cor_mat <- function(x){ if(!inherits(x, "cor_test")){ stop("x should be an object of class cor_test") } x <- keep_only_tbl_df_classes(x) p.mat <- x %>% cor_spread(value = "p") %>% add_class("pvalue") cor.mat <- x %>% cor_spread(value = "cor") %>% add_class("cor_mat") %>% set_attrs(pvalue = p.mat) cor.mat } rstatix/R/wilcox_effsize.R0000644000176200001440000001641113515405617015326 0ustar liggesusers#' @include utilities.R utilities_two_sample_test.R NULL #'Wilcoxon Effect Size #'@description Compute Wilcoxon effect size (\code{r}) for: \itemize{ \item #' one-sample test (Wilcoxon one-sample signed-rank test); \item paired #' two-samples test (Wilcoxon two-sample paired signed-rank test) and \item #' independent two-samples test ( Mann-Whitney, two-sample rank-sum test). } #' #' It can also returns confidence intervals by bootstap. #' #' The effect size \code{r} is calculated as \code{Z} statistic divided by #' square root of the sample size (N) (\eqn{Z/\sqrt{N}}). The \code{Z} value is #' extracted from either \code{coin::wilcoxsign_test()} (case of one- or #' paired-samples test) or \code{coin::wilcox_test()} (case of independent #' two-samples test). #' #' Note that \code{N} corresponds to total sample size for independent samples #' test and to total number of pairs for paired samples test. #' #' The \code{r} value varies from 0 to close to 1. The interpretation values #' for r commonly in published litterature and on the internet are: \code{0.10 #' - < 0.3} (small effect), \code{0.30 - < 0.5} (moderate effect) and \code{>= #' 0.5} (large effect). #' #'@inheritParams wilcox_test #'@param ci If TRUE, returns confidence intervals by bootstrap. May be slow. #'@param conf.level The level for the confidence interval. #'@param ci.type The type of confidence interval to use. Can be any of "norm", #' "basic", "perc", or "bca". Passed to \code{boot::boot.ci}. #'@param nboot The number of replications to use for bootstrap. #'@param ... Additional arguments passed to the functions #' \code{coin::wilcoxsign_test()} (case of one- or paired-samples test) or #' \code{coin::wilcox_test()} (case of independent two-samples test). #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y variable used in the test. \item #' \code{group1,group2}: the compared groups in the pairwise tests. \item #' \code{n,n1,n2}: Sample counts. \item \code{effsize}: estimate of the effect #' size (\code{r} value). \item \code{magnitude}: magnitude of effect size. #' \item \code{conf.low,conf.high}: lower and upper bound of the effect size #' confidence interval.} #'@references Maciej Tomczak and Ewa Tomczak. The need to report effect size #' estimates revisited. An overview of some recommended measures of effect #' size. Trends in Sport Sciences. 2014; 1(21):19-25. #' @examples #' if(require("coin")){ #' #' # One-sample Wilcoxon test effect size #' ToothGrowth %>% wilcox_effsize(len ~ 1, mu = 0) #' #' # Independent two-samples wilcoxon effect size #' ToothGrowth %>% wilcox_effsize(len ~ supp) #' #' #' # Paired-samples wilcoxon effect size #' ToothGrowth %>% wilcox_effsize(len ~ supp, paired = TRUE) #' #' # Pairwise comparisons #' ToothGrowth %>% wilcox_effsize(len ~ dose) #' #' # Grouped data #' ToothGrowth %>% #' group_by(supp) %>% #' wilcox_effsize(len ~ dose) #' #' } #'@export wilcox_effsize <- function(data, formula, comparisons = NULL, ref.group = NULL, paired = FALSE, alternative = "two.sided", mu = 0, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ...){ env <- as.list(environment()) args <- env %>% .add_item(method = "wilcox_effsize") params <- c(env, list(...)) %>% remove_null_items() %>% add_item(method = "coin.wilcox.test", detailed = FALSE) outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(number.of.groups > 2 & !is.null(ref.group)){ if(ref.group %in% c("all", ".all.")){ params$data <- create_data_with_all_ref_group(data, outcome, group) params$ref.group <- "all" } } test.func <- two_sample_test if(number.of.groups > 2) test.func <- pairwise_two_sample_test res <- do.call(test.func, params) %>% select(.data$.y., .data$group1, .data$group2, .data$estimate, everything()) %>% rename(effsize = .data$estimate) %>% mutate(magnitude = get_wilcox_effsize_magnitude(.data$effsize)) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "wilcox_effsize")) res } # Wilcoxon test using coin R package; returns effect size coin.wilcox.test <- function(x, y = NULL, mu = 0, paired = FALSE, alternative = c("two.sided", "less", "greater"), ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ...){ required_package("coin") alternative <- match.arg(alternative) check_two_samples_test_args( x = x, y = y, mu = mu, paired = paired, conf.level = conf.level ) if (!is.null(y)) { DNAME <- paste(deparse(substitute(x)), "and", deparse(substitute(y))) if (paired) { # Transform paired test into one-sample test problem OK <- complete.cases(x, y) x <- x[OK] - y[OK] y <- NULL METHOD <- "Paired Wilcoxon test (coin)" } else { x <- x[is.finite(x)] y <- y[is.finite(y)] METHOD <- "Independent Wilcoxon test (coin)" } } else { DNAME <- deparse(substitute(x)) METHOD <- "One-sample Wilcoxon test (coin)" x <- x[is.finite(x)] } if(is.null(y)){ y <- rep(mu, length(x)) test.type <- "symmetry" } else{ group <- rep(c("grp1", "grp2"), times = c(length(x), length(y))) %>% factor() x <- c(x, y) y <- group test.type <- "independence" } data <- data.frame(x, y) results <- coin_wilcox_test( data, x ~ y, type = test.type, alternative = alternative, ... ) # Confidence interval of the effect size r if (ci == TRUE) { stat.func <- function(data, subset) { coin_wilcox_test( data, formula = x ~ y, subset = subset, type = test.type, alternative = alternative, ... )$r } CI <- get_boot_ci( data, stat.func, conf.level = conf.level, type = ci.type, nboot = nboot ) results <- results %>% mutate(conf.low = CI[1], conf.high = CI[2]) } RVAL <- list(statistic = results$z, parameter = results$n, p.value = results$p, null.value = mu, alternative = alternative, method = METHOD, data.name = DNAME, estimate = results$r) if (ci) { attr(CI, "conf.level") <- conf.level RVAL <- c(RVAL, list(conf.int = CI)) } names(RVAL$statistic) <- "Z" names(RVAL$parameter) <- "n" names(RVAL$estimate) <- "Effect size (r)" class(RVAL) <- "htest" RVAL } # Perform wilcoxon test using coin package coin_wilcox_test <- function(data, formula, subset = NULL, type = c("independence", "symmetry"), ...){ type <- match.arg(type) coin_wilcox_test_func <- switch ( type, independence = coin::wilcox_test, symmetry = coin::wilcoxsign_test ) if(!is.null(subset)) data <- data[subset, ] res.wilcox <-suppressWarnings(coin_wilcox_test_func(formula, data = data,...)) n <- nrow(data) z <- as.vector(coin::statistic(res.wilcox, type = "standardized")) p <- coin::pvalue(res.wilcox) r <- abs(z)/sqrt(n) # Effect size tibble(n = n, z = z, r = r, p = p) } get_wilcox_effsize_magnitude <- function(d){ magnitude.levels = c(0.3, 0.5, Inf) magnitude = c("small","moderate","large") d.index <- findInterval(abs(d), magnitude.levels)+1 magnitude <- factor(magnitude[d.index], levels = magnitude, ordered = TRUE) magnitude } rstatix/R/get_manova_table.R0000644000176200001440000000506313520103642015563 0ustar liggesusers#' @include utilities.R NULL # Helper function to get MANOVA table # The codes is from: getAnywhere("print.Anova.mlm") # # x a manova test result get_manova_table <- function (x) { if ((!is.null(x$singular)) && x$singular) stop( "singular error SSP matrix; multivariate tests unavailable\n", "try summary(object, multivariate=FALSE)" ) test <- x$test repeated <- x$repeated ntests <- length(x$terms) tests <- matrix(NA, ntests, 4) . <- NULL if (!repeated) SSPE.qr <- qr(x$SSPE) for (term in 1:ntests) { eigs <- qr.coef( if (repeated) qr(x$SSPE[[term]]) else SSPE.qr, x$SSP[[term]] ) %>% eigen(symmetric = FALSE) %>% .$values %>% Re() tests[term, 1:4] <- switch( test, Pillai = Pillai(eigs, x$df[term], x$error.df), Wilks = Wilks(eigs, x$df[term], x$error.df), `Hotelling-Lawley` = HL(eigs, x$df[term], x$error.df), Roy = Roy(eigs, x$df[term], x$error.df) ) } ok <- tests[, 2] >= 0 & tests[, 3] > 0 & tests[, 4] > 0 ok <- !is.na(ok) & ok tests <- cbind( x$df, tests, stats::pf(tests[ok, 2], tests[ok, 3], tests[ok, 4], lower.tail = FALSE) ) rownames(tests) <- x$terms colnames(tests) <- c("Df", "test stat", "approx F", "num Df", "den Df", "Pr(>F)") heading <- paste( "\nType ", x$type, if (repeated) " Repeated Measures", " MANOVA Tests: ", test, " test statistic",sep = "" ) tests <- structure( as.data.frame(tests), heading = heading, class = c("anova", "data.frame") ) tests } Pillai <- function (eig, q, df.res) { test <- sum(eig/(1 + eig)) p <- length(eig) s <- min(p, q) n <- 0.5 * (df.res - p - 1) m <- 0.5 * (abs(p - q) - 1) tmp1 <- 2 * m + s + 1 tmp2 <- 2 * n + s + 1 c(test, (tmp2/tmp1 * test)/(s - test), s * tmp1, s * tmp2) } Wilks <- function (eig, q, df.res) { test <- prod(1/(1 + eig)) p <- length(eig) tmp1 <- df.res - 0.5 * (p - q + 1) tmp2 <- (p * q - 2)/4 tmp3 <- p^2 + q^2 - 5 tmp3 <- if (tmp3 > 0) sqrt(((p * q)^2 - 4)/tmp3) else 1 c(test, ((test^(-1/tmp3) - 1) * (tmp1 * tmp3 - 2 * tmp2))/p/q, p * q, tmp1 * tmp3 - 2 * tmp2) } HL <- function (eig, q, df.res) { test <- sum(eig) p <- length(eig) m <- 0.5 * (abs(p - q) - 1) n <- 0.5 * (df.res - p - 1) s <- min(p, q) tmp1 <- 2 * m + s + 1 tmp2 <- 2 * (s * n + 1) c(test, (tmp2 * test)/s/s/tmp1, s * tmp1, tmp2) } Roy <- function (eig, q, df.res) { p <- length(eig) test <- max(eig) tmp1 <- max(p, q) tmp2 <- df.res - tmp1 + q c(test, (tmp2 * test)/tmp1, tmp1, tmp2) } rstatix/R/utils-pipe.R0000644000176200001440000000031713672727360014405 0ustar liggesusers#' Pipe operator #' #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. #' #' @name %>% #' @rdname pipe #' @keywords internal #' @export #' @importFrom magrittr %>% #' @usage lhs \%>\% rhs NULL rstatix/R/adjust_pvalue.R0000644000176200001440000000332713640061453015151 0ustar liggesusers#' @include utilities.R NULL #' Adjust P-values for Multiple Comparisons #' @description A pipe-friendly function to add an adjusted p-value column into #' a data frame. Supports grouped data. #' @param data a data frame containing a p-value column #' @param p.col column name containing p-values #' @param output.col the output column name to hold the adjusted p-values #' @param method method for adjusting p values (see #' \code{\link[stats]{p.adjust}}). Allowed values include "holm", "hochberg", #' "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to #' adjust the p value (not recommended), use p.adjust.method = "none". #' @return a data frame #' #' @examples #' # Perform pairwise comparisons and adjust p-values #' ToothGrowth %>% #' t_test(len ~ dose) %>% #' adjust_pvalue() #' #' @rdname adjust_pvalue #' @export adjust_pvalue <- function(data, p.col = NULL, output.col = NULL, method = "holm"){ if (is_grouped_df(data)) { res <- data %>% doo(adjust_pvalue, p.col, output.col, method = method) return(res) } .attributes <- get_test_attributes(data) if(!is.null(.attributes$args)){ .attributes$args$p.adjust.method = method } p.adjust <- stats::p.adjust p.adjust.method <- method # Guess p-value columns if missing if(is.null(p.col)) p.col <- data %>% p_detect("p") if(is.null(p.col)) return(data) else if(!(p.col %in% colnames(data))) stop("The column ", p.col, "does not exist in the data") if(is.null(output.col)) output.col <- paste0(p.col, ".adj") # Adjust p-value data %>% keep_only_tbl_df_classes() %>% mutate( !!output.col := p.adjust(!!sym(p.col), method = p.adjust.method) ) %>% set_test_attributes(.attributes) } rstatix/R/tukey_hsd.R0000644000176200001440000001203113672604675014310 0ustar liggesusers#' @include utilities.R #' @importFrom stats TukeyHSD #' @importFrom dplyr everything #' @importFrom tidyr separate NULL #'Tukey Honest Significant Differences #' #' #'@description Provides a pipe-friendly framework to performs Tukey post-hoc #' tests. Wrapper around the function \code{\link[stats]{TukeyHSD}()}. It is #' essentially a t-test that corrects for multiple testing. #' #' Can handle different inputs formats: aov, lm, formula. #'@param x an object of class \code{aov}, \code{lm} or \code{data.frame} #' containing the variables used in the formula. #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{x ~ group} where \code{x} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ cancer_group}. #'@param ... other arguments passed to the function #' \code{\link[stats]{TukeyHSD}()}. These include: \itemize{ \item #' \strong{which}: A character vector listing terms in the fitted model for #' which the intervals should be calculated. Defaults to all the terms. \item #' \strong{ordered}: A logical value indicating if the levels of the factor #' should be ordered according to increasing average in the sample before #' taking differences. If ordered is true then the calculated differences in #' the means will all be positive. The significant differences will be those #' for which the lwr end point is positive. } #'@return a tibble data frame containing the results of the different #' comparisons. #' @examples #' # Data preparation #' df <- ToothGrowth #' df$dose <- as.factor(df$dose) #' # Tukey HSD from ANOVA results #' aov(len ~ dose, data = df) %>% tukey_hsd() #' #' # two-way anova with interaction #' aov(len ~ dose*supp, data = df) %>% tukey_hsd() #' #' # Tukey HSD from lm() results #' lm(len ~ dose, data = df) %>% tukey_hsd() #' #' # Tukey HSD from data frame and formula #' tukey_hsd(df, len ~ dose) #' #' # Tukey HSD using grouped data #' df %>% #' group_by(supp) %>% #' tukey_hsd(len ~ dose) #' #'@export tukey_hsd <- function(x, ...){ UseMethod("tukey_hsd", x) } #' @export #' @describeIn tukey_hsd performs tukey post-hoc test from \code{aov()} results. tukey_hsd.default <- function(x, ...) { tukey_hsd_of_model(x, ...) %>% set_attrs(args = list(x = x, p.adjust.method = "Tukey", method = "tukey_hsd")) %>% add_class(c("rstatix_test", "tukey_hsd")) } #' @export #' @describeIn tukey_hsd performs tukey post-hoc test from \code{lm()} model. tukey_hsd.lm <- function(x, ...) { stats::aov(x) %>% tukey_hsd.default(...) } #' @describeIn tukey_hsd performs tukey post-hoc tests using data and formula as #' inputs. ANOVA will be automatically performed using the function #' \code{\link[stats]{aov}()} #' @export tukey_hsd.data.frame <- function(x, formula, ...){ args <- list( data = x, formula = formula, method = "tukey_hsd", p.adjust.method = "Tukey" ) if(is_grouped_df(x)){ results <- x %>% doo(tukey_hsd_core, formula, ...) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "tukey_hsd")) return(results) } tukey_hsd_core (x, formula) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "tukey_hsd")) } tukey_hsd_core <- function(x, formula, ...){ stats::aov(formula, x) %>% tukey_hsd_of_model(...) } tukey_hsd_of_model <- function(model, ...){ comparison <- adj.p.value <- p.adj <- term <- group1 <- group2 <- NULL magic.text <- "_XX.MAGIC.XX_" model %>% replace_eventual_minus_symbols_in_factors(by = magic.text) %>% TukeyHSD( ...) %>% broom::tidy() %>% replace_contrast_colname_by_comparison() %>% separate(comparison, into= c("group2", "group1"), sep = "-") %>% revert_back_eventual_minus_symbols(magic.text) %>% rename(p.adj = adj.p.value) %>% mutate(p.adj = signif(p.adj, 3)) %>% select(term, group1, group2, everything()) %>% add_significance("p.adj") } # Handling possible minus symbols in factor levels replace_eventual_minus_symbols_in_factors <- function(res.aov, by = "_XX.MAGIC.XX_"){ res.aov$model <- res.aov$model %>% dplyr::mutate_if(is.factor, fct_replace_minus, by = by) res.aov } revert_back_eventual_minus_symbols <- function(res.tukey.df, magic.text = "_XX.MAGIC.XX_" ){ res.tukey.df %>% mutate( group1 = gsub(magic.text, replacement = "-", .data$group1, fixed = TRUE), group2 = gsub(magic.text, replacement = "-", .data$group2, fixed = TRUE) ) } fct_replace_minus <- function(.factor, by = "_XX.MAGIC.XX_"){ new.levels <- gsub( pattern = "-", replacement = by, x = levels(.factor), fixed = TRUE ) levels(.factor) <- new.levels .factor } # in broom v>= 0.7.0; contrast is used instead of comparison # so this helper function ensures that "comparison" is used as # column name no matter the version of broom replace_contrast_colname_by_comparison <- function(data){ if("contrast" %in% colnames(data)){ data <- data %>% rename(comparison = .data$contrast) } data } rstatix/R/games_howell_test.R0000644000176200001440000001270513651525662016021 0ustar liggesusers#' @include utilities.R t_test.R NULL #'Games Howell Post-hoc Tests #' #'@description Performs Games-Howell test, which is used to compare all possible #' combinations of group differences when the assumption of homogeneity of #' variances is violated. This post hoc test provides confidence intervals for #' the differences between group means and shows whether the differences are #' statistically significant. #' #' The test is based on Welch’s degrees of freedom correction and uses Tukey’s #' studentized range distribution for computing the p-values. The test compares #' the difference between each pair of means with appropriate adjustment for #' the multiple testing. So there is no need to apply additional p-value #' corrections. #' #'@inheritParams t_test #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y (outcome) variable used in the test. \item #' \code{group1,group2}: the compared groups in the pairwise tests. \item #' \code{n1,n2}: Sample counts. \item \code{estimate, conf.low, conf.high}: #' mean difference and its confidence intervals. \item \code{statistic}: Test #' statistic (t-value) used to compute the p-value. \item \code{df}: degrees of #' freedom calculated using Welch’s correction. \item \code{p.adj}: adjusted p-value using Tukey's method. \item #' \code{method}: the statistical test used to compare groups. \item #' \code{p.adj.signif}: the significance level of p-values. } #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #'@details The Games-Howell method is an improved version of the Tukey-Kramer #' method and is applicable in cases where the equivalence of variance #' assumption is violated. It is a t-test using Welch’s degree of freedom. This #' method uses a strategy for controlling the type I error for the entire #' comparison and is known to maintain the preset significance level even when #' the size of the sample is different. However, the smaller the number of #' samples in each group, the it is more tolerant the type I error control. #' Thus, this method can be applied when the number of samples is six or more. #' #'@references \itemize{ \item Aaron Schlege, #' https://rpubs.com/aaronsc32/games-howell-test. \item Sangseok Lee, Dong Kyu #' Lee. What is the proper way to apply the multiple comparison test?. Korean J #' Anesthesiol. 2018;71(5):353-360. } #' #' #' @examples #' # Simple test #' ToothGrowth %>% games_howell_test(len ~ dose) #' #' # Grouped data #' ToothGrowth %>% #' group_by(supp) %>% #' games_howell_test(len ~ dose) #' #'@rdname games_howell_test #'@export games_howell_test <- function(data, formula, conf.level = 0.95, detailed = FALSE){ args <- as.list(environment()) %>% .add_item(p.adjust.method = "Tukey", method = "games_howell_test") results <- data %>% doo(.games_howell_test, formula, conf.level = conf.level) if(!detailed){ results <- results %>% select( -.data$se, -.data$method, -.data$statistic, -.data$df, -.data$n1, -.data$n2 ) } results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "games_howell_test")) } .games_howell_test <- function(data, formula, conf.level = 0.95){ outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(number.of.groups == 1){ stop("all observations are in the same group") } data <- data %>% select(!!!syms(c(outcome, group))) %>% get_complete_cases() %>% .as_factor(group) x <- data %>% pull(!!outcome) g <- data %>% pull(!!group) if (!all(is.finite(g))) stop("all group levels must be finite") # Statistics for games howell tests grp.sizes <- tapply(x, g, length) nb.groups <- length(grp.sizes) grp.means <- tapply(x, g, mean) grp.vars <- tapply(x, g, stats::var) # Helper functions get_mean_diff <- function(i, j){ grp.means[i] - grp.means[j] } get_weltch_sd <- function(i, j){ sqrt((grp.vars[i]/grp.sizes[i]) + (grp.vars[j]/grp.sizes[j])) } get_degree_of_freedom <- function(i, j){ A <- ((grp.vars[i]/grp.sizes[i]) + (grp.vars[j]/grp.sizes[j]))^2 B <- ((grp.vars[i]/grp.sizes[i])^2)/(grp.sizes[i] - 1) C <- ((grp.vars[j]/grp.sizes[j])^2)/(grp.sizes[j] - 1) A/(B+C) } mean.diff <- stats::pairwise.table( get_mean_diff, levels(g), p.adjust.method = "none" ) %>% tidy_squared_matrix() weltch.sd <- stats::pairwise.table( get_weltch_sd, levels(g), p.adjust.method = "none" ) %>% tidy_squared_matrix() df <- stats::pairwise.table( get_degree_of_freedom, levels(g), p.adjust.method = "none" ) %>% tidy_squared_matrix() t <- abs(mean.diff$value)/weltch.sd$value p <- stats::ptukey(t*sqrt(2), nb.groups, df$value, lower.tail = FALSE) se <- weltch.sd$value*sqrt(0.5) q <- stats::qtukey(p = conf.level, nb.groups, df = df$value) conf.high <- mean.diff$value + q*se conf.low <- mean.diff$value - q*se n1 <- grp.sizes[mean.diff$group1] n2 <- grp.sizes[mean.diff$group2] results <- mean.diff %>% rename(estimate = .data$value) %>% mutate( conf.low = conf.low, conf.high = conf.high, se = se, statistic = t, df = df$value, p.adj = p_round(p, digits = 3) ) %>% add_column(n1 = n1, n2 = n2, .after = "group2") %>% add_column(.y. = outcome, .before = "group1") %>% add_significance("p.adj") %>% mutate(method = "Games-Howell") results } rstatix/R/remove_ns.R0000644000176200001440000000446013736604711014305 0ustar liggesusers#' @include utilities.R NULL #' Remove Non-Significant from Statistical Tests #' @description Filter out non-significant (NS) p-values from a statistical #' test. Can detect automatically p-value columns #' @param stat.test statistical test results returned by \code{rstatix} #' functions or any data frame containing a p-value column. #' @param col (optional) character specifying the column containing the p-value #' or the significance information, to be used for the filtering step. #' Possible values include: \code{"p"}, \code{"p.adj"}, \code{"p.signif"}, #' \code{"p.adj.signif"}. If missing, the function will automatically look for #' p.adj.signif, p.adj, p.signif, p in this order. #' @param signif.cutoff the significance cutoff; default is 0.05. Significance #' is declared at \code{p-value <= signif.cutoff} #' @return a data frame #' @examples #' # Statistical test #' stat.test <- PlantGrowth %>% wilcox_test(weight ~ group) #' # Remove ns: automatic detection of p-value columns #' stat.test %>% remove_ns() #' # Remove ns by the column p #' stat.test %>% remove_ns(col ="p") #' @export remove_ns <- function(stat.test, col = NULL, signif.cutoff = 0.05){ if(is.null(col)) col <- "any" else if(is.na(col)) col <- "any" else if(is.logical(col) ){ if(is.na(col)) col <- "any" else if(col == TRUE) col <- "any" else if(col == FALSE) return(stat.test) } if(col == "any"){ p.adj <- p_adj_names() p.adj.signif <- paste0(p.adj, ".signif") p <- p_names() p.signif <- paste0(p, ".signif") possible.cols <- c(p.adj.signif, p.adj, p.signif, p) if(!missing(signif.cutoff)) { # numeric columns are checked first possible.cols <- c(p.adj, p, p.adj.signif, p.signif) } col <- intersect(possible.cols, colnames(stat.test)) if(length(col) > 1) col <- col[1] else if(length(col) == 0) { warning("Specify a column for filtering out ns.", "Can't found any automatically", call. = FALSE) } } if(col %in% colnames(stat.test)){ .value <- stat.test[[col]] if(is.numeric(.value)) stat.test <- filter(stat.test, .value <= signif.cutoff) else if(is.character(.value)) stat.test <- filter(stat.test, !(.value %in% c("ns", "NS"))) } else{ stop("Can't find the column `", col, ", in the data", call. = FALSE) } stat.test } rstatix/R/cor_mark_significant.R0000644000176200001440000000230113470305575016454 0ustar liggesusers#' @include utilities.R add_significance.R pull_triangle.R NULL #' Add Significance Levels To a Correlation Matrix #' @description Combines correlation coefficients and significance levels in a #' correlation matrix data. #' @inheritParams add_significance #' @param x an object of class \code{\link{cor_mat}()}. #' @return a data frame containing the lower triangular part of the correlation #' matrix marked by significance symbols. #' @examples #' mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_mat() %>% #' cor_mark_significant() #' @export cor_mark_significant <- function(x, cutpoints = c(0, 0.0001, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "")) { if(!inherits(x, c("cor_mat", "cor_mat_tri"))) stop("x should be an object of class cor_mat or cor_mat_tri.") cor <- p.signif <- var1 <- var2 <- NULL res <- x %>% cor_gather (drop.na = FALSE) %>% add_significance(cutpoints = cutpoints, symbols = symbols) %>% mutate(cor = paste0(cor, p.signif)) %>% select(var1, var2, cor) %>% cor_spread() if(inherits(x, "upper_tri")) res %>% pull_upper_triangle() else res %>% pull_lower_triangle() } rstatix/R/sign_test.R0000644000176200001440000002003513570420617014300 0ustar liggesusers#' @include utilities.R utilities_two_sample_test.R #' @importFrom stats qbinom #' @importFrom stats pbinom NULL #'Sign Test #' #'@description Performs one-sample and two-sample sign tests. Read more: #' \href{https://www.datanovia.com/en/lessons/sign-test-in-r/}{Sign Test in R}. #'@inheritParams t_test #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{x ~ group} where \code{x} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ treatment}. #'@param mu a single number representing the value of the population median #' specified by the null hypothesis. #'@param ref.group a character string specifying the reference group. If #' specified, for a given grouping variable, each of the group levels will be #' compared to the reference group (i.e. control group). #'@param ... other arguments passed to the function \code{sign_test()} #' #'@return return a data frame with some the following columns: \itemize{ \item #' \code{.y.}: the y variable used in the test. \item \code{group1,group2}: the #' compared groups in the pairwise tests. \item \code{n,n1,n2}: Sample counts. #' \item \code{statistic}: Test statistic used to compute the p-value. That is #' the S-statistic (the number of positive differences between the data and the #' hypothesized median), with names attribute \code{"S"}. \item \code{df, #' parameter}: degrees of freedom. Here, the total number of valid differences. #' \item \code{p}: p-value. \item \code{method}: the statistical test used to #' compare groups. \item \code{p.signif, p.adj.signif}: the significance level #' of p-values and adjusted p-values, respectively. \item \code{estimate}: #' estimate of the effect size. It corresponds to the median of the #' differences. \item \code{alternative}: a character string describing the #' alternative hypothesis. \item \code{conf.low,conf.high}: Lower and upper #' bound on a confidence interval of the estimate. } #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #'@note This function is a reimplementation of the function \code{SignTest()} #' from the \code{DescTools} package. #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # One-sample test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% sign_test(len ~ 1, mu = 0) #' #' #' # Two-samples paired test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% sign_test(len ~ supp) #' #' #' # Compare supp levels after grouping the data by "dose" #' #:::::::::::::::::::::::::::::::::::::::: #' df %>% #' group_by(dose) %>% #' sign_test(data =., len ~ supp) %>% #' adjust_pvalue(method = "bonferroni") %>% #' add_significance("p.adj") #' #' # pairwise comparisons #' #:::::::::::::::::::::::::::::::::::::::: #' # As dose contains more than two levels ==> #' # pairwise test is automatically performed. #' df %>% sign_test(len ~ dose) #' #' # Comparison against reference group #' #:::::::::::::::::::::::::::::::::::::::: #' # each level is compared to the ref group #' df %>% sign_test(len ~ dose, ref.group = "0.5") #' #' #'@describeIn sign_test Sign test #'@export sign_test <- function(data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", alternative = "two.sided", mu = 0, conf.level = 0.95, detailed = FALSE){ args <- as.list(environment()) %>% .add_item(method = "sign_test") outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(!is.null(ref.group)){ if(ref.group %in% c("all", ".all.")) stop("ref.group can't be 'all'.") } # Case of one sample test if(number.of.groups == 1){ res <- one_sample_sign_test( data = data, formula = formula, alternative = alternative, mu = mu, conf.level = conf.level, detailed = detailed ) } # Case of two independents or paired groups else if (number.of.groups == 2) { res <- two_sample_sign_test( data = data, formula = formula, alternative = alternative, conf.level = conf.level, ref.group = ref.group, detailed = detailed ) } # Pairwise comparisons else if(number.of.groups > 2){ res <- pairwise_sign_test( data = data, formula = formula, comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, alternative = alternative, conf.level = conf.level, detailed = detailed ) } res %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "sign_test")) } one_sample_sign_test <- function(data, formula, mu = 0, ...){ two_sample_test(data, formula, method = "sign.test", mu = mu, ...) } two_sample_sign_test <- function(data, formula, ...) { two_sample_test(data, formula, method = "sign.test", ...) } #'@describeIn sign_test performs pairwise two sample Wilcoxon test. #'@export pairwise_sign_test <- function( data, formula, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", detailed = FALSE, ...) { args <- as.list(environment()) %>% .add_item(method = "sign_test") res <- pairwise_two_sample_test( data, formula, method = "sign.test", comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, detailed = detailed, ... ) res %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "sign_test")) } sign.test <- function(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0, conf.level = 0.95, ...) { alternative <- match.arg(alternative) check_two_samples_test_args(x = x, y = y, mu = mu, conf.level = conf.level) if (!is.null(y)) { if (length(x) != length(y)) stop("'x' and 'y' must have the same length") DNAME <- paste(deparse(substitute(x)), "and", deparse(substitute(y))) OK <- complete.cases(x, y) x <- x[OK] y <- y[OK] METHOD <- "Paired-samples Sign-Test" x <- (x - y) } else { DNAME <- deparse(substitute(x)) x <- x[is.finite(x)] METHOD <- "One-sample Sign-Test" } d <- (x - mu) n.valid <- sum(d > 0) + sum(d < 0) if(n.valid > 0) { RVAL <- stats::binom.test(x=sum(d > 0), n=n.valid, p=0.5, alternative = alternative, conf.level = conf.level ) } else { RVAL <- stats::binom.test(x=1, n=1) } RVAL$method <- METHOD RVAL$data.name <- DNAME names(mu) <- if (!is.null(y)) "median difference" else "median" names(RVAL$statistic) <- "S" RVAL$estimate <- median(d + mu, na.rm=TRUE) names(RVAL$parameter) <- "number of differences" mci <- get_median_ci(d + mu, conf.level=conf.level, alternative=alternative) RVAL$conf.int <- mci attr(RVAL$conf.int, "conf.level") = round(attr(mci,"conf.level"), 3) names(RVAL$estimate) <- "median of the differences" RVAL$null.value <- mu class(RVAL) <- "htest" return(RVAL) } get_median_ci <- function( x, conf.level = 0.95, alternative = c("two.sided", "less", "greater")){ # http://www.stat.umn.edu/geyer/old03/5102/notes/rank.pdf # http://de.scribd.com/doc/75941305/Confidence-Interval-for-Median-Based-on-Sign-Test x <- stats::na.omit(x) n <- length(x) switch( match.arg(alternative) , "two.sided" = { k <- qbinom(p = (1 - conf.level) / 2, size=n, prob=0.5, lower.tail=TRUE) ci <- sort(x)[c(k, n - k + 1)] attr(ci, "conf.level") <- 1 - 2 * pbinom(k-1, size=n, prob=0.5) } , "greater" = { k <- qbinom(p = (1 - conf.level), size=n, prob=0.5, lower.tail=TRUE) ci <- c(sort(x)[k], Inf) attr(ci, "conf.level") <- 1 - pbinom(k-1, size=n, prob=0.5) } , "less" = { k <- qbinom(p = conf.level, size=n, prob=0.5, lower.tail=TRUE) ci <- c(-Inf, sort(x)[k]) attr(ci, "conf.level") <- pbinom(k, size=n, prob=0.5) } ) return(ci) } rstatix/R/cor_test.R0000644000176200001440000001504413655566252014141 0ustar liggesusers#' @include utilities.R #' @importFrom stats as.formula #' @importFrom stats cor.test #' NULL #'Correlation Test #' #' #'@description Provides a pipe-friendly framework to perform correlation test #' between paired samples, using Pearson, Kendall or Spearman method. Wrapper #' around the function \code{\link[stats]{cor.test}()}. #' #' Can also performs multiple pairwise correlation analyses between more than #' two variables or between two different vectors of variables. Using this #' function, you can also compute, for example, the correlation between one #' variable vs many. #' #' #'@inheritParams stats::cor.test #'@inheritParams stats::cor #'@param data a data.frame containing the variables. #'@param vars optional character vector containing variable names for #' correlation analysis. Ignored when dot vars are specified. \itemize{ \item #' If \code{vars} is NULL, multiple pairwise correlation tests is performed #' between all variables in the data. \item If \code{vars} contain only one #' variable, a pairwise correlation analysis is performed between the specified #' variable vs either all the remaining numeric variables in the data or #' variables in \code{vars2} (if specified). \item If \code{vars} contain two #' or more variables: i) if \code{vars2} is not specified, a pairwise #' correlation analysis is performed between all possible combinations of #' variables. ii) if \code{vars2} is specified, each element in \code{vars} is #' tested against all elements in \code{vars2}}. Accept unquoted #' variable names: \code{c(var1, var2)}. #'@param vars2 optional character vector. If specified, each element in #' \code{vars} is tested against all elements in \code{vars2}. Accept unquoted #' variable names: \code{c(var1, var2)}. #'@param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to select a variable of interest. Alternative to the argument #' \code{vars}. #' #'@return return a data frame with the following columns: \itemize{ \item #' \code{var1, var2}: the variables used in the correlation test. \item #' \code{cor}: the correlation coefficient. \item \code{statistic}: Test #' statistic used to compute the p-value. \item \code{p}: p-value. \item #' \code{conf.low,conf.high}: Lower and upper bounds on a confidence interval. #' \item \code{method}: the method used to compute the statistic.} #'@seealso \code{\link{cor_mat}()}, \code{\link{as_cor_mat}()} #' @examples #' #' # Correlation between the specified variable vs #' # the remaining numeric variables in the data #' #::::::::::::::::::::::::::::::::::::::::: #' mtcars %>% cor_test(mpg) #' #' # Correlation test between two variables #' #::::::::::::::::::::::::::::::::::::::::: #' mtcars %>% cor_test(wt, mpg) #' #' # Pairwise correlation between multiple variables #' #::::::::::::::::::::::::::::::::::::::::: #' mtcars %>% cor_test(wt, mpg, disp) #' #' # Grouped data #' #::::::::::::::::::::::::::::::::::::::::: #' iris %>% #' group_by(Species) %>% #' cor_test(Sepal.Width, Sepal.Length) #' #' # Multiple correlation test #' #::::::::::::::::::::::::::::::::::::::::: #' # Correlation between one variable vs many #' mtcars %>% cor_test( #' vars = "mpg", #' vars2 = c("disp", "hp", "drat") #' ) #' #' # Correlation between two vectors of variables #' # Each element in vars is tested against all elements in vars2 #' mtcars %>% cor_test( #' vars = c("mpg", "wt"), #' vars2 = c("disp", "hp", "drat") #' ) #' #' #'@describeIn cor_test correlation test between two or more variables. #'@export cor_test <- function( data, ..., vars = NULL, vars2 = NULL, alternative = "two.sided", method = "pearson", conf.level = 0.95, use = "pairwise.complete.obs" ) { . <- NULL # Accept unquoted variables .args <- rlang::enquos(vars = vars, vars2 = vars2) %>% get_quo_vars_list(data, .) vars <- .args$vars vars2 <- .args$vars2 vars <- data %>% get_selected_vars(..., vars = vars) n.vars <- length(vars) # Select only numeric columns data.numeric <- data %>% select_numeric_columns() if(is.null(vars2)){ if(is.null(vars)){ # Pairwise correlation test between all vars in data vars <- vars2 <- colnames(data.numeric) } else if(n.vars == 1){ # Correlation between the specified variable vs # all numeric vars in the data vars2 <- colnames( data.numeric) %>% setdiff(vars) } else if(n.vars == 2){ # Correlation test between two variables vars2 <- vars[2] vars <- vars[1] } # Multiple pairwise correlation between multiple variables else if(n.vars >2){ vars2 <- vars } } else if(is.null(vars)){ stop("You should specify the argument vars in addition to vars2") } # Multiple correlation tests between two vectors of variables. expand.grid(y = vars2, x = vars, stringsAsFactors = FALSE) %>% as.list() %>% purrr::pmap_dfr( cor_test_xy, data = data, alternative = alternative, method = method, conf.level = conf.level, use = use ) %>% add_class("cor_test") } #::::::::::::::::::::::::::::::::::::::::::::::::::: # Helper functions #::::::::::::::::::::::::::::::::::::::::::::::::::: # Correlation test between two variables x and y #++++++++++++++++++++++++++++++++++++++++++++++++++++ cor_test_xy <- function( data, x, y, method = "pearson", use = "pairwise.complete.obs", ... ) { if(is_grouped_df(data)){ results <- data %>% doo(cor_test_xy, x, y, method = method, use = use, ...) return(results) } # Correlation test, supress the warning when method = "spearman" or "kendall". x.value <- data %>% pull(x) y.value <- data %>% pull(y) suppressWarnings(cor.test(x.value, y.value, method = method, use = use, ...)) %>% as_tidy_cor() %>% add_column(var1 = x, var2 = y, .before = "cor") } # Multiple correlation tests between two vectors of variables. #++++++++++++++++++++++++++++++++++++++++++++++++++++ # x,y character vectors containing variable names to be used in the # correlation analysis. mcor_test <- function(data, x, y, ...){ expand.grid(y = y, x = x, stringsAsFactors = FALSE) %>% as.list() %>% purrr::pmap_dfr(cor_test_xy, data = data, ...) %>% add_class("cor_test") } # Tidy output for correlation test as_tidy_cor <- function(x){ estimate <- cor <- statistic <- p <- conf.low <- conf.high <- method <- NULL res <- x %>% as_tidy_stat() %>% rename(cor = estimate) %>% mutate(cor = signif(cor, 2)) if(res$method == "Pearson"){ res %>% select(cor, statistic, p, conf.low, conf.high, method) } else { res %>% select(cor, statistic, p, method) } } rstatix/R/mahalanobis_distance.R0000644000176200001440000000505513463131735016437 0ustar liggesusers#' @include utilities.R NULL #'Compute Mahalanobis Distance and Flag Multivariate Outliers #' #'@description Pipe-friendly wrapper around to the function #' \code{\link[stats]{mahalanobis}()}, which returns the squared #' Mahalanobis distance of all rows in x. Compared to the base function, it #' automatically flags multivariate outliers. #' #' Mahalanobis distance is a common metric used to identify multivariate #' outliers. The larger the value of Mahalanobis distance, the more unusual the #' data point (i.e., the more likely it is to be a multivariate outlier). #' #' The distance tells us how far an observation is from the center of the cloud, taking into #' account the shape (covariance) of the cloud as well. #' #' To detect outliers, the calculated Mahalanobis distance is compared against #' a chi-square (X^2) distribution with degrees of freedom equal to the number #' of dependent (outcome) variables and an alpha level of 0.001. #' #' The threshold to declare a multivariate outlier is determined using the #' function \code{qchisq(0.999, df) }, where df is the degree of freedom (i.e., #' the number of dependent variable used in the computation). #' #'@param data a data frame. Columns are variables. #'@param ... One unquoted expressions (or variable name). Used to select a #' variable of interest. Can be also used to ignore a variable that are not #' needed for the computation. For example specify \code{-id} to ignore the id #' column. #' #'@return Returns the input data frame with two additional columns: 1) #' "mahal.dist": Mahalanobis distance values; and 2) "is.outlier": logical #' values specifying whether a given observation is a multivariate outlier #' #' @examples #' #' # Compute mahalonobis distance and flag outliers if any #' iris %>% #' doo(~mahalanobis_distance(.)) #' #'# Compute distance by groups and filter outliers #' iris %>% #' group_by(Species) %>% #' doo(~mahalanobis_distance(.)) %>% #' filter(is.outlier == TRUE) #' #'@export mahalanobis_distance <- function(data, ...){ if(is_grouped_df(data)){ results <- data %>% doo(~mahalanobis_distance(.)) } data <- data %>% select_numeric_columns() vars <- data %>% get_selected_vars(...) n.vars <- length(vars) threshold <- stats::qchisq(0.999, n.vars) .data <- data %>% select(!!!syms(vars)) %>% as.matrix() distance <- stats::mahalanobis( .data, center = colMeans(.data), cov = cov(.data) ) results <- data %>% mutate( mahal.dist = round(distance, 3), is.outlier = .data$mahal.dist > threshold ) results } rstatix/R/chisq_test.R0000644000176200001440000001525713640103756014461 0ustar liggesusers#' @include utilities.R NULL #'Chi-squared Test for Count Data #'@description Performs chi-squared tests, including goodness-of-fit, #' homogeneity and independence tests. #'@inheritParams stats::chisq.test #'@param res.chisq an object of class \code{chisq_test}. #'@param p.adjust.method method to adjust p values for multiple comparisons. #' Used when pairwise comparisons are performed. Allowed values include "holm", #' "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't #' want to adjust the p value (not recommended), use p.adjust.method = "none". #'@param ... other arguments passed to the function \code{{chisq_test}()}. #' #'@return return a data frame with some the following columns: \itemize{ \item #' \code{n}: the number of participants. \item \code{group, group1, group2}: #' the categories or groups being compared. \item \code{statistic}: the value #' of Pearson's chi-squared test statistic. \item \code{df}: the degrees of #' freedom of the approximate chi-squared distribution of the test statistic. #' NA if the p-value is computed by Monte Carlo simulation. \item \code{p}: #' p-value. \item \code{p.adj}: the adjusted p-value. \item \code{method}: the #' used statistical test. \item \code{p.signif, p.adj.signif}: the significance #' level of p-values and adjusted p-values, respectively. \item #' \code{observed}: observed counts. \item #' \code{expected}: the expected counts under the null hypothesis. #' } #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' #' @examples #' # Chi-square goodness of fit test #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' tulip <- c(red = 81, yellow = 50, white = 27) #' # Q1: Are the colors equally common? #' chisq_test(tulip) #' pairwise_chisq_gof_test(tulip) #' # Q2: comparing observed to expected proportions #' chisq_test(tulip, p = c(1/2, 1/3, 1/6)) #' pairwise_chisq_test_against_p(tulip, p = c(0.5, 0.33, 0.17)) #' #' # Homogeneity of proportions between groups #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # Data: Titanic #' xtab <- as.table(rbind( #' c(203, 118, 178, 212), #' c(122, 167, 528, 673) #' )) #' dimnames(xtab) <- list( #' Survived = c("Yes", "No"), #' Class = c("1st", "2nd", "3rd", "Crew") #' ) #' xtab #' # Chi-square test #' chisq_test(xtab) #' # Compare the proportion of survived between groups #' pairwise_prop_test(xtab) #' @describeIn chisq_test performs chi-square tests including goodness-of-fit, #' homogeneity and independence tests. #' @export chisq_test <- function(x, y = NULL, correct = TRUE, p = rep(1/length(x), length(x)), rescale.p = FALSE, simulate.p.value = FALSE, B = 2000){ args <- as.list(environment()) %>% add_item(method = "chisq_test") if(is.data.frame(x)) x <- as.matrix(x) if(inherits(x, c("matrix", "table"))) n <- sum(x) else n <- length(x) res.chisq <- stats::chisq.test( x, y, correct = correct, p = p, rescale.p = rescale.p, simulate.p.value = simulate.p.value, B = B ) as_tidy_stat(res.chisq, stat.method = "Chi-square test") %>% add_significance("p") %>% add_columns(n = n, .before = 1) %>% set_attrs(args = args, test = res.chisq) %>% add_class(c("rstatix_test", "chisq_test")) } #' @describeIn chisq_test perform pairwise comparisons between groups following a global #' chi-square goodness of fit test. #' @export pairwise_chisq_gof_test <- function(x, p.adjust.method = "holm", ...){ if(is.null(names(x))){ names(x) <- paste0("grp", 1:length(x)) } compare_pair <- function(levs, x, ...){ levs <- as.character(levs) suppressWarnings(chisq_test(x[levs], ...)) %>% add_columns(group1 = levs[1], group2 = levs[2], .before = "statistic") } args <- as.list(environment()) %>% add_item(method = "chisq_test") comparisons <- names(x) %>% .possible_pairs() results <- comparisons %>% map(compare_pair, x, ...) %>% map(keep_only_tbl_df_classes) %>% bind_rows() %>% adjust_pvalue("p", method = p.adjust.method) %>% add_significance("p.adj") %>% mutate(p.adj = signif(.data$p.adj, digits = 3)) %>% select(-.data$p.signif, -.data$method) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "chisq_test")) } #' @describeIn chisq_test perform pairwise comparisons after a global #' chi-squared test for given probabilities. For each group, the observed and #' the expected proportions are shown. Each group is compared to the sum of #' all others. #' @export pairwise_chisq_test_against_p <- function(x, p = rep(1/length(x), length(x)), p.adjust.method = "holm", ...){ args <- as.list(environment()) %>% add_item(method = "chisq_test") if (sum(p) != 1) { stop( "Make sure that the `p` argument is correctly specified.", "sum of probabilities must be 1." ) } if(is.null(names(x))){ names(x) <- paste0("grp", 1:length(x)) } results <- list() for (i in 1:length(x)) { res.chisq <- suppressWarnings(chisq_test(c(x[i], sum(x) - x[i]), p = c(p[i], 1 - p[i]), ...)) res.desc <- chisq_descriptives(res.chisq) res.chisq <- res.chisq %>% add_columns(observed = res.desc$observed[1], expected = res.desc$expected[1], .before = 1) results[[i]] <- res.chisq } results <- results %>% map(keep_only_tbl_df_classes) %>% bind_rows() %>% add_columns(group = names(x), .before = 1) %>% adjust_pvalue("p", method = p.adjust.method) %>% add_significance("p.adj") %>% mutate(p.adj = signif(.data$p.adj, digits = 3)) %>% select(-.data$p.signif, -.data$method) results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "chisq_test")) } #' @describeIn chisq_test returns the descriptive statistics of the chi-square #' test. These include, observed and expected frequencies, proportions, #' residuals and standardized residuals. #' @export chisq_descriptives <- function(res.chisq){ res <- attr(res.chisq, "test") %>% augment() colnames(res) <- gsub(pattern = "^\\.", replacement = "", colnames(res)) res } #' @describeIn chisq_test returns the expected counts from the chi-square test result. #' @export expected_freq <- function(res.chisq){ attr(res.chisq, "test")$expected } #' @describeIn chisq_test returns the observed counts from the chi-square test result. #' @export observed_freq <- function(res.chisq){ attr(res.chisq, "test")$observed } #' @describeIn chisq_test returns the Pearson residuals, \code{(observed - expected) / sqrt(expected)}. #' @export pearson_residuals <- function(res.chisq){ attr(res.chisq, "test")$residuals } #' @describeIn chisq_test returns the standardized residuals #' @export std_residuals <- function(res.chisq){ attr(res.chisq, "test")$stdres } rstatix/R/get_summary_stats.R0000644000176200001440000001760413652071244016062 0ustar liggesusers#' @include utilities.R NULL #'Compute Summary Statistics #'@description Compute summary statistics for one or multiple numeric variables. #'@param data a data frame #'@param ... (optional) One or more unquoted expressions (or variable names) #' separated by commas. Used to select a variable of interest. If no variable #' is specified, then the summary statistics of all numeric variables in the #' data frame is computed. #'@param type type of summary statistics. Possible values include: \code{"full", #' "common", "robust", "five_number", "mean_sd", "mean_se", "mean_ci", #' "median_iqr", "median_mad", "quantile", "mean", "median", "min", "max"} #'@param show a character vector specifying the summary statistics you want to #' show. Example: \code{show = c("n", "mean", "sd")}. This is used to filter #' the output after computation. #' @param probs numeric vector of probabilities with values in [0,1]. Used only when type = "quantile". #'@return A data frame containing descriptive statistics, such as: \itemize{ #' \item \strong{n}: the number of individuals \item \strong{min}: minimum #' \item \strong{max}: maximum \item \strong{median}: median \item #' \strong{mean}: mean \item \strong{q1, q3}: the first and the third quartile, #' respectively. \item \strong{iqr}: interquartile range \item \strong{mad}: #' median absolute deviation (see ?MAD) \item \strong{sd}: standard deviation #' of the mean \item \strong{se}: standard error of the mean \item \strong{ci}: 95 percent confidence interval of the mean } #' @examples #' # Full summary statistics #' data("ToothGrowth") #' ToothGrowth %>% get_summary_stats(len) #' #' # Summary statistics of grouped data #' # Show only common summary #' ToothGrowth %>% #' group_by(dose, supp) %>% #' get_summary_stats(len, type = "common") #' #' # Robust summary statistics #' ToothGrowth %>% get_summary_stats(len, type = "robust") #' #' # Five number summary statistics #' ToothGrowth %>% get_summary_stats(len, type = "five_number") #' #' # Compute only mean and sd #' ToothGrowth %>% get_summary_stats(len, type = "mean_sd") #' #' # Compute full summary statistics but show only mean, sd, median, iqr #' ToothGrowth %>% #' get_summary_stats(len, show = c("mean", "sd", "median", "iqr")) #' #'@export get_summary_stats <- function( data, ..., type = c("full", "common", "robust", "five_number", "mean_sd", "mean_se", "mean_ci", "median_iqr", "median_mad", "quantile", "mean", "median", "min", "max" ), show = NULL, probs = seq(0, 1, 0.25) ){ type = match.arg(type) if(is_grouped_df(data)){ results <- data %>% doo(get_summary_stats, ..., type = type, show = show) return(results) } data <- data %>% select_numeric_columns() vars <- data %>% get_selected_vars(...) n.vars <- length(vars) if(n.vars >= 1){ data <- data %>% select(!!!syms(vars)) } variable <- .value. <- NULL data <- data %>% gather(key = "variable", value = ".value.") %>% filter(!is.na(.value.)) %>% group_by(variable) results <- switch( type, common = common_summary(data), robust = robust_summary(data), five_number = five_number_summary(data), mean_sd = mean_sd(data), mean_se = mean_se(data), mean_ci = mean_ci(data), median_iqr = median_iqr(data), median_mad = median_mad(data), quantile = quantile_summary(data, probs), mean = mean_(data), median = median_(data), min = min_(data), max = max_(data), full_summary(data) ) %>% dplyr::mutate_if(is.numeric, round, digits = 3) if(!is.null(show)){ show <- unique(c("variable", "n", show)) results <- results %>% select(!!!syms(show)) } results } full_summary <- function(data){ confidence <- 0.95 alpha <- 1 - confidence .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), min = min(.value., na.rm=TRUE), max = max(.value., na.rm=TRUE), median = stats::median(.value., na.rm=TRUE), q1 = stats::quantile(.value., 0.25, na.rm = TRUE), q3 = stats::quantile(.value., 0.75, na.rm = TRUE), iqr = stats::IQR(.value., na.rm=TRUE), mad = stats::mad(.value., na.rm=TRUE), mean = mean(.value., na.rm = TRUE), sd = stats::sd(.value., na.rm = TRUE) ) %>% mutate( se = .data$sd / sqrt(.data$n), ci = abs(stats::qt(alpha/2, .data$n-1)*.data$se) ) } common_summary <- function(data){ confidence <- 0.95 alpha <- 1 - confidence .value. <- ci <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), min = min(.value., na.rm=TRUE), max = max(.value., na.rm=TRUE), median = stats::median(.value., na.rm=TRUE), iqr = stats::IQR(.value., na.rm=TRUE), mean = mean(.value., na.rm = TRUE), sd = stats::sd(.value., na.rm = TRUE) ) %>% mutate( se = .data$sd / sqrt(.data$n), ci = abs(stats::qt(alpha/2, .data$n-1)*.data$se) ) } robust_summary <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), median = stats::median(.value., na.rm=TRUE), iqr = stats::IQR(.value., na.rm=TRUE) ) } quantile_summary <- function(data, probs = seq(0, 1, 0.25)){ core_func <- function(data, probs){ .value. <- NULL n <- sum(!is.na(data$.value.)) names(n) <- "n" q <- stats::quantile(data$.value., probs, na.rm = TRUE) results <- t(matrix(c(n, q))) colnames(results) <- c("n", names(q)) tibble::as_tibble(results) } results <- data %>% nest() %>% mutate(.results. = map(data, core_func, probs)) %>% select(.data$variable, .data$.results.) %>% unnest(cols = ".results.") results } five_number_summary <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), min = min(.value., na.rm=TRUE), max = max(.value., na.rm=TRUE), q1 = stats::quantile(.value., 0.25, na.rm = TRUE), median = stats::median(.value., na.rm=TRUE), q3 = stats::quantile(.value., 0.75, na.rm = TRUE) ) } mean_ <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), mean = mean(.value., na.rm = TRUE) ) } median_ <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), median = stats::median(.value., na.rm=TRUE) ) } max_ <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), max = max(.value., na.rm = TRUE) ) } min_ <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), min = min(.value., na.rm = TRUE) ) } mean_sd <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), mean = mean(.value., na.rm = TRUE), sd = stats::sd(.value., na.rm = TRUE) ) } mean_se <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), mean = mean(.value., na.rm = TRUE), sd = stats::sd(.value., na.rm = TRUE) ) %>% mutate(se = .data$sd / sqrt(.data$n))%>% select(-.data$sd) } mean_ci <- function(data){ confidence <- 0.95 alpha <- 1 - confidence .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), mean = mean(.value., na.rm = TRUE), sd = stats::sd(.value., na.rm = TRUE) ) %>% mutate( se = .data$sd / sqrt(.data$n), ci = abs(stats::qt(alpha/2, .data$n-1)*.data$se) )%>% select(-.data$se, -.data$sd) } median_iqr <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), median = stats::median(.value., na.rm=TRUE), iqr = stats::IQR(.value., na.rm=TRUE) ) } median_mad <- function(data){ .value. <- NULL data %>% dplyr::summarise( n = sum(!is.na(.value.)), median = stats::median(.value., na.rm=TRUE), mad = stats::mad(.value., na.rm=TRUE) ) } rstatix/R/cochran_qtest.R0000644000176200001440000001000013640104221015111 0ustar liggesusers#' @include utilities.R NULL #'Cochran's Q Test #'@description Performs the Cochran's Q test for unreplicated randomized block #' design experiments with a binary response variable and paired data. This #' test is analogue to the \code{\link{friedman.test}()} with 0,1 coded #' response. It's an extension of the McNemar Chi-squared test for comparing #' more than two paired proportions. #'@param data a data frame containing the variables in the formula. #'@param formula a formula of the form \code{a ~ b | c}, where \code{a} is the #' outcome variable name; b is the within-subjects factor variables; and c #' (factor) is the column name containing individuals/subjects identifier. #' Should be unique per individual. #'@examples #' # Generate a demo data #' mydata <- data.frame( #' outcome = c(0,1,1,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,1,0,0,1), #' treatment = gl(3,1,30,labels=LETTERS[1:3]), #' participant = gl(10,3,labels=letters[1:10]) #' ) #' mydata$outcome <- factor( #' mydata$outcome, levels = c(1, 0), #' labels = c("success", "failure") #' ) #' # Cross-tabulation #' xtabs(~outcome + treatment, mydata) #' #' # Compare the proportion of success between treatments #' cochran_qtest(mydata, outcome ~ treatment|participant) #' #' # pairwise comparisons between groups #' pairwise_mcnemar_test(mydata, outcome ~ treatment|participant) #' #'@export cochran_qtest <- function(data, formula){ args <- as.list(environment()) %>% add_item(method = "cochran_qtest") friedman_test(data, formula) %>% keep_only_tbl_df_classes() %>% mutate(method = "Cochran's Q test") %>% remove_class("friedman_test") %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "cochran_qtest")) } # http://geai.univ-brest.fr/carpentier/2008-2009/Notes-Cochran-Q.pdf exact_cochran_qtest <- function(data, formula, nboot = 500) { # Data preparation data <- data %>% select(!!!syms(all.vars(formula))) colnames(data) <- c("outcome", "groups", "participant") nb.outcome <- length(unique(data$outcome)) if(nb.outcome > 2 | nb.outcome == 1){ stop("Unique possible outcome values should be 2") } # Convert outcome into 0/1 if(!is.numeric(data$outcome)){ data$outcome <- as.numeric(as.factor(data$outcome)) - 1 } if(!all(unique(data$outcome) %in% c(0, 1))){ stop("Outcome values should be 0 or 1") } data.wide <- data %>% spread(key = "groups", value = "outcome") %>% select(-.data$participant) nb.row <- nrow(data.wide) nb.col <- ncol(data.wide) results <- cochran_qtest(data, outcome ~ groups|participant) qobs <- results$statistic freq <- 0 perm <- permutations(nb.col) # perm.list <- purrr::array_tree(perm) for (boot in 1:nboot) { data.permutated <- data.wide k <- 1+ as.integer(stats::runif(nb.row)*gamma(nb.col+1)) for (j in 1:nb.row) { k <- 1+ as.integer(stats::runif(1)*gamma(nb.col+1)) data.permutated[j,] <- data.wide[j, perm[k,]] } qperm <- get_cochran_q(data.permutated) if (qperm >= qobs) {freq <- freq + 1}} results %>% select(-.data$df) %>% mutate( p = freq/nboot, method = "Exact Cochran's Q test" ) } # e1071::permutations # Returns a matrix containing all permutations of the integers 1:n (one permutation per row). permutations <- function (n) { if (n == 1) return(matrix(1)) else if (n < 2) stop("n must be a positive integer") z <- matrix(1) for (i in 2:n) { x <- cbind(z, i) a <- c(1:i, 1:(i - 1)) z <- matrix(0, ncol = ncol(x), nrow = i * nrow(x)) z[1:nrow(x), ] <- x for (j in 2:i - 1) { z[j * nrow(x) + 1:nrow(x), ] <- x[, a[1:i + j]] } } dimnames(z) <- NULL z } get_cochran_q <- function(data.wide){ # Compute rows and column totals row.total <- apply(data.wide, 1, sum) column.total <- apply(data.wide, 2, sum) grand.total <- sum(data.wide) k <- ncol(data.wide) # Cochran's Q test statistic numerator <- sum((column.total - (grand.total/k))^2) denominator <- sum(row.total * (k - row.total)) q = k*(k-1)*(numerator/denominator) q } rstatix/R/cramer_v.R0000644000176200001440000000145413471040013014067 0ustar liggesusers#' @include utilities.R #' NULL #'Compute Cramer's V #'@description Compute Cramer's V, which measures the strength of the #' association between categorical variables. #'@inheritParams stats::chisq.test #'@param ... other arguments passed to the function #' \code{\link[stats]{chisq.test}()}. #'@examples #' #' # Data preparation #' df <- as.table(rbind(c(762, 327, 468), c(484, 239, 477))) #' dimnames(df) <- list( #' gender = c("F", "M"), #' party = c("Democrat","Independent", "Republican") #' ) #' df #' # Compute cramer's V #' cramer_v(df) #' #'@export cramer_v <- function(x, y = NULL, correct = TRUE, ...) { test <- stats::chisq.test(x, y, correct = correct, ...) chi2 <- test$statistic N <- sum(test$observed) k <- min(dim(test$observed)) V <- sqrt(chi2/(N * (k - 1))) as.numeric(V) } rstatix/R/anova_summary.R0000644000176200001440000003070013666532250015165 0ustar liggesusers#' @include utilities.R utilities_two_sample_test.R factorial_design.R NULL #'Create Nice Summary Tables of ANOVA Results #' #' #'@description Create beautiful summary tables of ANOVA test results obtained #' from either \code{\link[car]{Anova}()} or \code{\link[stats]{aov}()}. #' #' The results include ANOVA table, generalized effect size and some assumption #' checks. #' #'@param effect.size the effect size to compute and to show in the ANOVA #' results. Allowed values can be either "ges" (generalized eta squared) or #' "pes" (partial eta squared) or both. Default is "ges". #'@param observed Variables that are observed (i.e, measured) as compared to #' experimentally manipulated. The default effect size reported (generalized #' eta-squared) requires correct specification of the observed variables. #'@param detailed If TRUE, returns extra information (sums of squares columns, #' intercept row, etc.) in the ANOVA table. #'@param object an object of returned by either \code{\link[car]{Anova}()}, or #' \code{\link[stats]{aov}()}. #' #'@return return an object of class \code{anova_test} a data frame containing #' the ANOVA table for independent measures ANOVA. However, for repeated/mixed #' measures ANOVA, it is a list containing the following components are #' returned: #' #' \itemize{ \item \strong{ANOVA}: a data frame containing ANOVA results \item #' \strong{Mauchly's Test for Sphericity}: If any within-Ss variables with more #' than 2 levels are present, a data frame containing the results of Mauchly's #' test for Sphericity. Only reported for effects that have more than 2 levels #' because sphericity necessarily holds for effects with only 2 levels. \item #' \strong{Sphericity Corrections}: If any within-Ss variables are present, a #' data frame containing the Greenhouse-Geisser and Huynh-Feldt epsilon values, #' and corresponding corrected p-values. } #' #' The \strong{returned object might have an attribute} called \code{args} if #' you compute ANOVA using the function \code{\link{anova_test}()}. The attribute \code{args} is a #' list holding the arguments used to fit the ANOVA model, including: data, dv, #' within, between, type, model, etc. #' #' #' The following abbreviations are used in the different results tables: #' #' \itemize{ \item DFn Degrees of Freedom in the numerator (i.e. DF effect). #' \item DFd Degrees of Freedom in the denominator (i.e., DF error). \item #' SSn Sum of Squares in the numerator (i.e., SS effect). \item SSd Sum of #' Squares in the denominator (i.e.,SS error). \item F F-value. \item p p-value #' (probability of the data given the null hypothesis). \item p<.05 Highlights #' p-values less than the traditional alpha level of .05. \item ges Generalized #' Eta-Squared measure of effect size. \item GGe Greenhouse-Geisser epsilon. #' \item p[GGe] p-value after correction using Greenhouse-Geisser epsilon. #' \item p[GGe]<.05 Highlights p-values (after correction using #' Greenhouse-Geisser epsilon) less than the traditional alpha level of .05. #' \item HFe Huynh-Feldt epsilon. \item p[HFe] p-value after correction using #' Huynh-Feldt epsilon. \item p[HFe]<.05 Highlights p-values (after correction #' using Huynh-Feldt epsilon) less than the traditional alpha level of .05. #' \item W Mauchly's W statistic } #' #'@author Alboukadel Kassambara, \email{alboukadel.kassambara@@gmail.com} #'@seealso \code{\link{anova_test}()}, \code{\link{factorial_design}()} #' @examples #'# Load data #'#::::::::::::::::::::::::::::::::::::::: #'data("ToothGrowth") #'df <- ToothGrowth #'df$dose <- as.factor(df$dose) #' #'# Independent measures ANOVA #'#::::::::::::::::::::::::::::::::::::::::: #'# Compute ANOVA and display the summary #' res.anova <- Anova(lm(len ~ dose*supp, data = df)) #' anova_summary(res.anova) #' #'# Display both SSn and SSd using detailed = TRUE #'# Show generalized eta squared using effect.size = "ges" #'anova_summary(res.anova, detailed = TRUE, effect.size = "ges") #' #'# Show partial eta squared using effect.size = "pes" #'anova_summary(res.anova, detailed = TRUE, effect.size = "pes") #' #'# Repeated measures designs using car::Anova() #'#::::::::::::::::::::::::::::::::::::::::: #'# Prepare the data #'df$id <- as.factor(rep(1:10, 6)) # Add individuals ids #'head(df) #' #'# Easily perform repeated measures ANOVA using the car package #' design <- factorial_design(df, dv = len, wid = id, within = c(supp, dose)) #' res.anova <- Anova(design$model, idata = design$idata, idesign = design$idesign, type = 3) #' anova_summary(res.anova) #' #'# Repeated measures designs using stats::Aov() #'#::::::::::::::::::::::::::::::::::::::::: #' res.anova <- aov(len ~ dose*supp + Error(id/(supp*dose)), data = df) #' anova_summary(res.anova) #'@name anova_summary #'@export anova_summary <- function(object, effect.size = "ges", detailed = FALSE, observed = NULL){ if(inherits(object, "Anova.mlm")){ results <- repeated_anova_summary(object) } else if(inherits(object, "anova")){ results <- summary_independent_anova(object) } else if(inherits(object, c("aov", "aovlist"))){ results <- summary_aov(object) } else{ stop("Non-supported object passed: ", paste(class(object), collapse = ", "), ". ", "Object needs to be of class 'Anova.mlm' or 'anova'.") } .args <- attr(object, "args") # exist only in anova_test() results <- results %>% add_anova_effect_size(effect.size, observed) if(!detailed){ results <- remove_details(results, method = "anova") } results$ANOVA <- order_by_interaction_levels(results$ANOVA) results <- results %>% map(~dplyr::mutate_if(., is.numeric, round_value, 3)) if(length(results) == 1) results <- results[[1]] results %>% set_attrs(args = .args) } # Summary of Anova.mlm object: summary_anova_mlm #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # this function is used for repeated and mixed anova repeated_anova_summary <- function(res.anova, detailed = FALSE){ .summary <- suppressWarnings(summary(res.anova)) # Anova table converted into data frame aov.table <- .summary$univariate.tests %>% convert_anova_object_as_data_frame() %>% set_colnames(c("Effect", "SSn", "DFn", "SSd", "DFd", "F", "p")) %>% select( .data$Effect, .data$DFn, .data$DFd, .data$SSn, .data$SSd, .data$F, .data$p ) %>% mutate(`p<.05` = ifelse(.data$p < 0.05, "*",'')) sphericity.test <- corrections <- NULL # Mauchly's Test for Sphericity if(nrow(.summary$sphericity.tests) > 0){ sphericity.test <- .summary$sphericity.tests %>% convert_anova_object_as_data_frame() %>% set_colnames(c("Effect", "W", "p")) %>% mutate(`p<.05` = ifelse(.data$p < 0.05, "*",'')) } # Sphericity corrections if(nrow(.summary$sphericity.tests) > 0){ corrections <- .summary$pval.adjustments %>% as.data.frame() %>% set_colnames(c("GGe", "p[GG]", "HFe", "p[HF]")) %>% tibble::rownames_to_column("Effect") p.gg.signif <- ifelse(corrections[["p[GG]"]] < 0.05, "*",'') p.hf.signif <- ifelse(corrections[["p[HF]"]] < 0.05, "*",'') corrections <- corrections %>% add_column(`p[GG]<.05` = p.gg.signif, .after = "p[GG]") %>% add_column(`p[HF]<.05` = p.hf.signif, .after = "p[HF]") } # Results results <- list(ANOVA = aov.table) if(!is.null(sphericity.test)){ results $`Mauchly's Test for Sphericity` <- sphericity.test results$`Sphericity Corrections` <- corrections results <- results %>% add_corrected_df() } results } convert_anova_object_as_data_frame <- function(aov.table){ aov.table.list <- list(Effect = rownames(aov.table)) for(col in colnames(aov.table)){ aov.table.list[[col]] <- aov.table[, col] } aov.table <- as.data.frame(aov.table.list, stringsAsFactors = FALSE) rownames(aov.table) <- 1:nrow(aov.table) aov.table } add_corrected_df <- function(.summary){ aov.table <- .summary$ANOVA %>% select(.data$Effect, .data$DFn, .data$DFd) corrections <- .summary$`Sphericity Corrections` %>% dplyr::left_join(aov.table, by = "Effect") %>% mutate( df.gg = paste(round_value(.data$GGe*.data$DFn, 2), round_value(.data$GGe*.data$DFd, 2), sep = ", "), df.hf = paste(round_value(.data$HFe*.data$DFn, 2), round_value(.data$HFe*.data$DFd, 2), sep = ", ") ) %>% select(-.data$DFd, -.data$DFn) df.gg <- corrections$df.gg df.hf <- corrections$df.hf .summary$`Sphericity Corrections` <- corrections %>% select(-.data$df.gg, -.data$df.hf) %>% add_column(`DF[GG]` = df.gg, .after = "GGe") %>% add_column(`DF[HF]` = df.hf, .after = "HFe") .summary } # Summary of independent anova #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% summary_independent_anova <- function(res.anova){ res.anova <- as.data.frame(res.anova) .residuals <- res.anova["Residuals", 1:2] if('Mean Sq' %in% colnames(res.anova)){ # exists when res.anova is from stats::anova res.anova <- select(res.anova, -.data$`Mean Sq`) } if('Sum Sq' %in% colnames(res.anova)){ # in stats::anova, Sum Sq is not the first column, so do select res.anova <- res.anova %>% select(.data$`Sum Sq`, dplyr::everything()) colnames(res.anova) <- c('SSn','DFn','F','p') ss.exists <- TRUE } else{ # case of white.adjust = TRUE. SS doesnt exist in the results colnames(res.anova) <- c('DFn','F','p') ss.exists <- FALSE } res.anova <- res.anova %>% tibble::rownames_to_column("Effect") %>% add_column(DFd = .residuals$Df, .after = "DFn") %>% mutate(`p<.05` = ifelse(.data$p < 0.05, "*",'')) %>% filter(.data$Effect != "Residuals") if(ss.exists){ res.anova <- res.anova %>% add_column(SSd = .residuals$`Sum Sq`, .after = "SSn") } results <- list(ANOVA = res.anova) results } # Summary of anova from stats::aov #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% summary_aov <- function(res.anova){ remove_empty_space <- function(x){ sapply(x, function(x){strsplit(x, " ")[[1]][1]}) } reformat_aov_summary <- function(aov.summary){ if(inherits(aov.summary, "listof")) aov.summary <- as.data.frame(aov.summary[[1]]) else as.data.frame(aov.summary) .residuals <- aov.summary["Residuals", 1:2] aov.summary <- aov.summary %>% set_colnames(c("DFn", "SSn", "MS", "F", "p")) %>% tibble::rownames_to_column("Effect") %>% add_column(DFd = .residuals$Df, .after = "DFn") %>% add_column(SSd = .residuals$`Sum Sq`, .after = "SSn") %>% mutate(`p<.05` = as.character(ifelse(.data$p < 0.05, "*",''))) %>% mutate(Effect = remove_empty_space(.data$Effect)) %>% filter(!is.na(.data$p)) %>% select(-.data$MS) aov.summary } res.anova <- summary(res.anova) %>% map(reformat_aov_summary) %>% dplyr::bind_rows() %>% order_by_interaction_levels() results <- list(ANOVA = res.anova) results } # Reorder ANOVA table by interaction levels in the term order_by_interaction_levels <- function(aov.table){ .terms <- aov.table$Effect nb.interaction <- str_count(.terms, ":") aov.table %>% dplyr::arrange(nb.interaction) } # Add effect size #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% add_anova_effect_size <- function(res.anova.summary, effect.size = "ges", observed = NULL){ ss.exists <- "SSn" %in% colnames(res.anova.summary$ANOVA) if(!ss.exists){ return(res.anova.summary) } if("pes" %in% effect.size){ res.anova.summary <- res.anova.summary %>% add_partial_eta_squared() } else { res.anova.summary <- res.anova.summary %>% add_generalized_eta_squared(observed) } res.anova.summary } # Generalized eta squared add_generalized_eta_squared <- function(res.anova.summary, observed = NULL){ aov.table <- res.anova.summary$ANOVA if(!is.null(observed)){ obs <- rep(FALSE, nrow(aov.table)) for(i in observed){ if (!any(grepl(paste0("\\b",i,"\\b"), aov.table$Effect))) stop("Specified observed variable not found in data: ", i) obs <- obs | grepl(paste0("\\b",i,"\\b"), aov.table$Effect) } obs.SSn1 = sum(aov.table$SSn*obs) obs.SSn2 = aov.table$SSn*obs } else{ obs.SSn1 <- 0 obs.SSn2 <- 0 } aov.table <- aov.table %>% mutate(ges = .data$SSn / (.data$SSn + sum(unique(.data$SSd)) + obs.SSn1 - obs.SSn2)) res.anova.summary$ANOVA <- aov.table res.anova.summary } # Partial eta squared add_partial_eta_squared <- function(res.anova.summary){ res.anova.summary$ANOVA <- res.anova.summary$ANOVA %>% mutate(pes = .data$SSn/(.data$SSn + .data$SSd)) res.anova.summary } rstatix/R/reexports.R0000644000176200001440000000120413672730633014336 0ustar liggesusers#' @importFrom tibble tibble #' @export tibble::tibble #' @importFrom dplyr mutate #' @export dplyr::mutate #' @importFrom dplyr filter #' @export dplyr::filter #' @importFrom dplyr group_by #' @export dplyr::group_by #' @importFrom dplyr select #' @export dplyr::select #' @importFrom dplyr desc #' @export dplyr::desc #' @importFrom tidyr drop_na #' @export tidyr::drop_na #' @importFrom tidyr gather #' @export tidyr::gather #' @importFrom tidyr spread #' @export tidyr::spread #' @importFrom generics tidy #' @export generics::tidy #' @importFrom generics augment #' @export generics::augment #' @importFrom car Anova #' @export car::Anova rstatix/R/friedman_test.R0000644000176200001440000000555113570423467015141 0ustar liggesusers#' @include utilities.R NULL #'Friedman Rank Sum Test #' #' #'@description Provides a pipe-friendly framework to perform a Friedman rank sum #' test, which is the non-parametric alternative to the one-way repeated #' measures ANOVA test. Wrapper around the function #' \code{\link[stats]{friedman.test}()}. Read more: #' \href{https://www.datanovia.com/en/lessons/friedman-test-in-r/}{Friedman #' test in R}. #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{a ~ b | c}, where \code{a} #' (numeric) is the dependent variable name; \code{b} is the within-subjects #' factor variables; and \code{c} (factor) is the column name containing #' individuals/subjects identifier. Should be unique per individual. #'@param ... other arguments to be passed to the function #' \code{\link[stats]{friedman.test}}. #' #'@return return a data frame with the following columns: \itemize{ \item #' \code{.y.}: the y (dependent) variable used in the test. \item \code{n}: #' sample count. \item \code{statistic}: the value of Friedman's chi-squared #' statistic, used to compute the p-value. \item \code{p}: p-value. \item #' \code{method}: the statistical test used to compare groups.} #' #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth %>% #' filter(supp == "VC") %>% #' mutate(id = rep(1:10, 3)) #' head(df) #' #' # Friedman rank sum test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% friedman_test(len ~ dose | id) #' #'@name friedman_test #'@export friedman_test <- function(data, formula, ...){ args <- c(as.list(environment()), list(...)) %>% add_item(method = "friedman_test") vars <- get_friedman_vars(formula) args <- args %>% add_item(dv = vars$dv, wid = vars$wid, within = args$within) if(is_grouped_df(data)){ results <- data %>% doo(.friedman_test, formula, ...) } else{ results <- .friedman_test(data, formula, ...) } results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "friedman_test")) } .friedman_test <- function(data, formula, ...){ vars <- get_friedman_vars(formula) term <- statistic <- p <- df <- method <- NULL sample.size <- data %>% pull(vars$wid) %>% unique() %>% length() res <- stats::friedman.test(formula, data = data, ...) %>% tidy() %>% rename(p = .data$p.value, df = .data$parameter) %>% mutate(method = "Friedman test") %>% select(.data$statistic, .data$df, .data$p, .data$method) %>% add_columns(.y. = vars$dv, n = sample.size, .before = "statistic") res } get_friedman_vars <- function(formula){ outcome <- get_formula_left_hand_side(formula) rhs <- get_formula_right_hand_side(formula) rhs <- gsub(pattern = " ", replacement = "", rhs) rhs <- strsplit(rhs, "|", fixed = TRUE) %>% unlist() list(dv = outcome, within = rhs[1], wid = rhs[2]) } rstatix/R/replace_triangle.R0000644000176200001440000000533513337207515015610 0ustar liggesusers#' @include utilities.R NULL #' Replace Lower and Upper Triangular Part of a Matrix #' @description Replace the lower or the upper triangular part of a #' (correlation) matrix. #' @param x a (correlation) matrix #' @param diagonal logical. Default is FALSE. If TRUE, the matrix diagonal is #' included. #' @param triangle the triangle to replace. Allowed values are one of #' "upper" and "lower". #' @param by a replacement argument. Appropriate values are either "" or NA. Used to replace #' the upper, lower or the diagonal part of the matrix. #' @return an object of class \code{cor_mat_tri}, which is a data frame #' @seealso \code{\link{pull_triangle}()} #' @examples #' # Compute correlation matrix and pull triangles #' #:::::::::::::::::::::::::::::::::::::::::: #' # Correlation matrix #' cor.mat <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_mat() #' cor.mat #' #' # Replace upper triangle by NA #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat %>% replace_upper_triangle(by = NA) #' #' #' # Replace upper triangle by NA and reshape the #' # correlation matrix to have unique combinations of variables #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat %>% #' replace_upper_triangle(by = NA) %>% #' cor_gather() #' @describeIn replace_triangle replaces the specified triangle by empty or NA. #' @export replace_triangle <- function(x, triangle = c("lower", "upper"), by = "", diagonal = FALSE){ triangle <- match.arg(triangle) remaining.triangle <- ifelse( triangle == "lower", "upper", "lower" ) remaining.triangle.class <- paste0(remaining.triangle, "_tri") replacement <- by get_tri <- switch( triangle, upper = upper.tri, lower = lower.tri ) res <- as_matrix(x) res[get_tri(res)] <- replacement if (!diagonal) diag(res) <- replacement res <- res %>% matrix_to_dataframe() if(.is_cor_mat(x)){ pvalue <- x %>% attr("pvalue") %>% as_matrix() pvalue[get_tri(pvalue)] <- replacement if (!diagonal) diag(pvalue) <- replacement pvalue <- pvalue %>% matrix_to_dataframe() res <- res %>% set_attrs(pvalue = pvalue) %>% add_class("cor_mat_tri") } res %>% add_class(remaining.triangle.class) } #' @describeIn replace_triangle replaces the upper triangular part of a matrix. #' Returns an object of class \code{lower_tri}. #' @export replace_upper_triangle <- function(x, by = "", diagonal = FALSE){ x %>% replace_triangle("upper", by = by, diagonal = diagonal) } #' @describeIn replace_triangle replaces the lower triangular part of a matrix. #' Returns an object of class \code{lower_tri} #' @export replace_lower_triangle <- function(x, by = "", diagonal = FALSE){ x %>% replace_triangle("lower", by = by, diagonal = diagonal) } rstatix/R/get_test_label.R0000644000176200001440000004170113705507103015255 0ustar liggesusers#' @include utilities.R NULL #' Extract Label Information from Statistical Tests #' @description Extracts label information from statistical tests. Useful for #' labelling plots with test outputs. #' @param stat.test statistical test results returned by \code{rstatix} #' functions. #' @param description the test description used as the prefix of the label. #' Examples of description are "ANOVA", "Two Way ANOVA". To remove the default #' description, specify \code{description = NULL}. If missing, we'll try to #' guess the statistical test default description. #' @param p.col character specifying the column containing the p-value. Default #' is \code{"p"}, can be \code{"p.adj"}. #' @param type the label type. Can be one of "text" and "expression". Partial #' match allowed. If you want to add the label onto a ggplot, it might be #' useful to specify \code{type = "expresion"}. #' @param correction character, considered only in the case of ANOVA test. Which sphericity #' correction of the degrees of freedom should be reported for the #' within-subject factors (repeated measures). The default is set to #' \code{"GG"} corresponding to the Greenhouse-Geisser correction. Possible #' values are \code{"GG"}, \code{"HF"} (i.e., Hyunh-Feldt correction), #' \code{"none"} (i.e., no correction) and \code{"auto"} (apply automatically #' GG correction if the sphericity assumption is not for within-subject #' design. #' @param row numeric, the row index to be considered. If NULL, the last row is #' automatically considered for ANOVA test. #' @param statistic.text character specifying the test statistic. For example #' \code{statistic.text = "F"} (for ANOVA test ); \code{statistic.text = "t"} #' (for t-test ). #' @param statistic the numeric value of a statistic. #' @param p the p-value of the test. #' @param parameter string containing the degree of freedom (if exists). Default #' is \code{NA} to accommodate non-parametric tests. For example #' \code{parameter = "1,9"} (for ANOVA test. Two parameters exist: DFn and #' DFd); \code{sparameter = "9"} (for t-test ). #' @param n sample count, example: \code{n = 10}. #' @param effect.size the effect size value #' @param effect.size.text a character specifying the relevant effect size. For #' example, for \code{Cohens d} statistic, \code{effect.size.text = "d"}. You #' can also use plotmath expression as follow \code{quote(italic("d"))}. #' @param detailed logical value. If TRUE, returns detailed label. #' @return a text label or an expression to pass to a plotting function. #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # One-way ANOVA test #' #::::::::::::::::::::::::::::::::::::::::: #' anov <- df %>% anova_test(len ~ dose) #' get_test_label(anov, detailed = TRUE, type = "text") #' #' # Two-way ANOVA test #' #::::::::::::::::::::::::::::::::::::::::: #' anov <- df %>% anova_test(len ~ supp*dose) #' get_test_label(anov, detailed = TRUE, type = "text", #' description = "Two Way ANOVA") #' #' #' # Kruskal-Wallis test #' #::::::::::::::::::::::::::::::::::::::::: #' kruskal<- df %>% kruskal_test(len ~ dose) #' get_test_label(kruskal, detailed = TRUE, type = "text") #' #' # Wilcoxon test #' #::::::::::::::::::::::::::::::::::::::::: #' # Unpaired test #' wilcox <- df %>% wilcox_test(len ~ supp) #' get_test_label(wilcox, detailed = TRUE, type = "text") #'# Paired test #' wilcox <- df %>% wilcox_test(len ~ supp, paired = TRUE) #' get_test_label(wilcox, detailed = TRUE, type = "text") #' #' # T test #' #::::::::::::::::::::::::::::::::::::::::: #' ttest <- df %>% t_test(len ~ dose) #' get_test_label(ttest, detailed = TRUE, type = "text") #' #' #' # Pairwise comparisons labels #' #::::::::::::::::::::::::::::::::::::::::: #' get_pwc_label(ttest, type = "text") #' #' #' # Create test labels #' #::::::::::::::::::::::::::::::::::::::::: #' create_test_label( #' statistic.text = "F", statistic = 71.82, #' parameter = "4, 294", #' p = "<0.0001", #' description = "ANOVA", #' type = "text" #' ) #' #' #' # Extract infos #' #::::::::::::::::::::::::::::::::::::::::: #' stat.test <- df %>% t_test(len ~ dose) #' get_n(stat.test) #' get_description(stat.test) #' #' #' @describeIn get_test_label Extract label from pairwise comparisons. #' @export get_pwc_label <- function(stat.test, type = c("expression", "text")){ methods <- get_pairwise_comparison_methods() stat.test %>% stop_ifnot_class(names(methods)) type <- match.arg(type) args <- attr(stat.test, "args") stat.method <- methods[args$method] p.adjust.method <- args$p.adjust.method %>% to_uppercase_first_letter() if(! "p.adj" %in% colnames(stat.test)){ p.adjust.method <- "None" } if(type == "text"){ paste0("pwc: ", stat.method, "; p.adjust: ", p.adjust.method) } else if(type == "expression"){ substitute( expr = paste( "pwc: ", bold(stat.method), "; p.adjust: ", bold(p.adjust.method) ), env = list(stat.method = stat.method, p.adjust.method = p.adjust.method) ) } } #' @describeIn get_test_label Extract labels for statistical tests. #' @export get_test_label <- function(stat.test, description = NULL, p.col = "p", type = c("expression", "text"), correction = c("auto", "GG", "HF", "none"), row = NULL, detailed = FALSE){ type = match.arg(type) allowed.tests <- c( get_pairwise_comparison_methods(), kruskal_test = "Kruskal-Wallis", friedman_test = "Friedman test", anova_test = "Anova", welch_anova_test = "Welch ANOVA", chisq_test = "Chi-square test", exact_multinom_test = "Exact multinomial test", exact_binom_test = "Exact binomial test", cochran_qtest = "Cochran Q test", chisq_trend_test = "Chi-square trend test" ) stop_ifnot_class(stat.test, .class = names(allowed.tests)) is_anova_test <- inherits(stat.test, "anova_test") if(is_anova_test){ stat.test <- get_anova_table(stat.test, correction = correction) if(is.null(row)) row <- nrow(stat.test) # consider the last row } if(!is.null(row)) { stat.test <- stat.test %>% keep_only_tbl_df_classes() %>% dplyr::slice(row) } statistic.text <- get_statistic_text(stat.test, type = type) statistic <- get_statistic(stat.test) df <- get_df(stat.test) n <- get_n(stat.test) effect <- get_effect_size(stat.test, type) effect.size <- effect$value effect.size.text <- effect$text if(missing(description)){ description <- get_description(stat.test) } if(!is.null(description)){ if(description != ""){ description <- paste0(description, ", ") } } if(!(p.col %in% colnames(stat.test))){ # automatic detection of p.col p.col <- p_detect(stat.test) } stat.test <- stat.test %>% keep_only_tbl_df_classes() %>% select(!!sym(p.col)) %>% rename(p = p.col) %>% mutate( row.id = 1:nrow(stat.test), n = n, statistic = statistic, parameter = df, effect.size = effect.size ) if(is.numeric(stat.test$p)){ stat.test$p <- p_format(stat.test$p, 3) } get_label_func <- switch ( type, expression = create_test_label.expression, text = create_test_label.text ) get_label_func_df <- function(df){ get_label_func( description, statistic.text = statistic.text, statistic = df$statistic, parameter = df$parameter, p = df$p, n = df$n, effect.size = df$effect.size, effect.size.text = effect.size.text, detailed = detailed ) } if(nrow(stat.test) > 1){ results <- stat.test %>% group_by(.data$row.id) %>% doo(get_label_func_df) %>% pull(.data$.results.) } else{ results <- get_label_func_df(stat.test) } results } #' @describeIn get_test_label Create labels from user specified test results. #' @export create_test_label <- function( statistic.text, statistic, p, parameter = NA, description = NULL, n = NA, effect.size = NA, effect.size.text = NA, type = c("expression", "text"), detailed = FALSE) { type <- match.arg(type) if(!is.null(description)){ if(description != ""){ description <- paste0(description, ", ") } } else description <- "" label_func <- switch( type, text = create_test_label.text, expression = create_test_label.expression, create_test_label.text ) label_func( description = description, statistic.text = statistic.text, statistic = statistic, parameter = parameter, p = p, n = n, effect.size = effect.size, effect.size.text = effect.size.text, detailed = detailed ) } # Build test labeles #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # description: stat test description, e.g "T test" # statistic.text: statistic text, example: "t", # statistic: statistic value, example: 10 # parameter: string containing the degree of freedom, # ex: "9" for t-test or "1,9" for ANOVA (DFn = 1 and DFd = 9) # p: p value # n: sample count create_test_label.expression <- function( description, statistic.text, statistic, parameter, p, n = NA, effect.size = NA, effect.size.text = NA, detailed = FALSE) { if(is.na(parameter)) parameter <- "" else parameter <- paste0("(", parameter, ")") # Sample count if(is.na(n)) { n <- "" } else{ n <- substitute( expr = paste(", ", italic("n"), " = ", n), env = list(n = n) ) } # Effect size if(is.na(effect.size)){ effect.size <- "" } else{ effect.size <- round_value(effect.size, 2) effect.size <- substitute( expr = paste(", ", effect.size.text, " = ", effect.size), env = list(effect.size.text = effect.size.text, effect.size = effect.size) ) } # Create label statistic <- round_value(statistic, 2) equal <- " = " if(is.na(statistic)) statistic.text <- equal <- statistic <- "" else statistic <- paste0(statistic, ", ") env <- as.list(environment()) if(detailed){ substitute( expr = paste( description, statistic.text, parameter, equal, statistic, italic("p"), " = ", p, effect.size, n ), env = env ) } else{ substitute( expr = paste(description, italic("p"), " = ", p), env = env ) } } create_test_label.text <- function(description, statistic.text, statistic, parameter, p, n = NA, effect.size = NA, effect.size.text = NA, detailed = FALSE){ if(is.na(parameter)) parameter <- "" else parameter <- paste0("(", parameter, ")") if(is.na(effect.size)) effect.size <- "" else effect.size <- paste0(", ", effect.size.text, " = ", effect.size) if(is.na(n)) n <- "" else n <- paste0(", ", "n", " = ", n) if(!is.na(statistic)){ statistics <- paste0(statistic.text, parameter, " = ", round_value(statistic, 2), ", ") } else statistics <- "" if(detailed){ paste0( description, statistics, "p", " = ", p, effect.size, n ) } else{ paste0(description, "p = ", p) } } # Get label parameters # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Statical test text: F, t, W, V, X2, ------------------------------------------- get_statistic_text <- function(stat.test, type = c("expression", "text")){ type <- match.arg(type) args <- attr(stat.test, "args") stat.method <- args$method is.paired <- args$paired if(!is.null(is.paired)){ if(is.paired & stat.method == "wilcox_test"){ stat.method = "wilcox_test_paired" } } if(is.null(is.paired)) is.paired <- FALSE if(type == "expression"){ statistic.text <- switch( stat.method, t_test = quote(italic("t")), wilcox_test = quote(italic("W")), wilcox_test_paired = quote(italic("V")), sign_test = quote(italic("S")), dunn_test = quote(italic("Z")), emmeans_test = quote(italic("t")), tukey_hsd = quote(italic("t")), games_howell_test = quote(italic("t")), kruskal_test = quote(italic(chi)^2), friedman_test = quote(italic(chi)^2), anova_test = quote(italic("F")), welch_anova_test = quote(italic("F")), chisq_test = quote(italic(chi)^2), mcnemar_test = quote(italic(chi)^2), prop_test = quote(italic(chi)^2), cochran_qtest = quote(italic(chi)^2), chisq_trend_test = quote(italic(chi)^2), quote(italic("Stat")) ) } else{ statistic.text <- switch( stat.method, t_test = "t", wilcox_test = "W", wilcox_test_paired = "V", sign_test = "S", dunn_test = "Z", emmeans_test = "t", tukey_hsd = "t", games_howell_test = "t", kruskal_test = "X2", friedman_test = "X2", anova_test = "F", welch_anova_test = "F", chisq_test = "X2", mcnemar_test = "X2", prop_test = "X2", cochran_qtest = "X2", chisq_trend_test = "X2", "Stat" ) } statistic.text } # Statistic values ------------------------------------------------- get_statistic <- function(stat.test){ stat.cols <- colnames(stat.test) if("statistic" %in% stat.cols){ result <- stat.test$statistic } else if ("F" %in% stat.cols){ result <- stat.test$F } else{ # statistic column not found result <- rep(NA, nrow(stat.test)) } result } # Degree of freedom------------------------------------------------- get_df <- function(stat.test){ args <- attr(stat.test, "args") df.cols <- c("df", "DFn", "DFd") if(!any(df.cols %in% colnames(stat.test))){ return(NA) } if(all(c("DFn", "DFd") %in% colnames(stat.test))){ dfn <- round_value(stat.test$DFn, 2) dfd <- round_value(stat.test$DFd, 2) df <- paste(dfn, dfd, sep = ",") } else{ df <- round_value(stat.test$df, 2) } df } # Sample count------------------------------------------------- #' @describeIn get_test_label Extracts sample counts (n) from an rstatix test outputs. Returns a numeric vector. #' @export get_n <- function(stat.test){ if(inherits(stat.test, "anova_test")){ .args <- attr(stat.test, "args") wid <- .args$wid if(is.null(wid)) n <- nrow(.args$data) else n <- .args$data %>% pull(!!wid) %>% unique() %>% length() stat.test$n <- n } else if(inherits(stat.test, "grouped_anova_test")){ # compute sample size of data subsets .args <- attr(stat.test, "args") stat.test$n <- .args$data %>% dplyr::summarise(n = dplyr::n()) %>% pull(.data$n) } n.cols <- c("n", "n1", "n2") if(!any(n.cols %in% colnames(stat.test))){ return(NA) } if("n" %in% colnames(stat.test)){ n <- stat.test$n } else if(all(c("n1", "n2") %in% colnames(stat.test))){ if(is_paired(stat.test)) n <- stat.test$n1 else n <- stat.test$n1 + stat.test$n2 } n } # Statistical test description --------------------------------- #' @describeIn get_test_label Extracts the description of an rstatix test outputs. Returns a character vector. #' @export get_description <- function(stat.test){ tests <- c( t_test = "T test", wilcox_test = "Wilcoxon test", sign_test = "Sign test", dunn_test = "Dunn test", emmeans_test = "Emmeans test", tukey_hsd = "Tukey HSD", anova_test = "Anova", welch_anova_test = "Welch Anova", kruskal_test = "Kruskal-Wallis", friedman_test = "Friedman test", cor_test = "Correlation", prop_test = "Z-Prop test", fisher_test = "Fisher's exact test", chisq_test = "Chi-square test", exact_multinom_test = "Exact multinomial test", exact_binom_test = "Exact binomial test", mcnemar_test = "McNemar test", cochran_qtest = "Cochran Q test", chisq_trend_test = "Chi-square trend test" ) args <- attr(stat.test, "args") if(is.null(args)) return("") stat.method <- args$method if(stat.method %in% names(tests)){ description <- tests[stat.method] } else{ description <- stat.method } as.character(description) } # Efect size --------------------------------- get_effect_size <- function(stat.test, type = "text"){ stat.method <- attr(stat.test, "args")$method value <- text <- NA if("ges" %in% colnames(stat.test)) { value <- stat.test$ges if(type == "expression") text <- quote(eta["g"]^2) else text <- "eta2[g]" } else if("pes" %in% colnames(stat.test)) { if(type == "expression") text <- quote(eta["p"]^2) else text <- "eta2[p]" value <- stat.test$pes } else if("effsize" %in% colnames(stat.test)){ value <- stat.test$effsize if(type == "expression"){ text <- switch( stat.method, t_test = quote(italic("d")), wilcox_test = quote(italic("r")), kruskal_test = quote(eta["H"]^2), friedman_test = quote(italic("W")["Kendall"]), quote(italic("effsize")) ) } else{ text <- switch( stat.method, t_test = "d", wilcox_test = "r", kruskal_test = "eta2[H]", friedman_test = "W[Kendall]", "effsize" ) } } list(value = value, text = text) } # Check if paired stat test-------------------------------------------- is_paired <- function(stat.test){ args <- attr(stat.test, "args") is.paired <- args$paired if(is.null(is.paired)) is.paired <- FALSE is.paired } rstatix/R/make_clean_names.R0000644000176200001440000000136413470301356015544 0ustar liggesusers#' @include utilities.R NULL #'Make Clean Names #' #' #'@description Pipe-friendly function to make syntactically valid names out of #' character vectors. #' #' @param data a data frame or vector #' @return a data frame or a vector depending on the input data #' #' @examples #' #' # Vector #' make_clean_names(c("a and b", "a-and-b")) #' make_clean_names(1:10) #' #' # data frame #' df <- data.frame( #' `a and b` = 1:4, #' `c and d` = 5:8, #' check.names = FALSE #' ) #' df #' make_clean_names(df) #' #' @export make_clean_names <- function(data){ if(is.vector(data)){ data <- make.names(data) } else if(inherits(data, c("data.frame", "matrix"))){ .colnames <- colnames(data) %>% make.names() colnames(data) <- .colnames } data } rstatix/R/kruskal_effesize.R0000644000176200001440000000700213534275446015644 0ustar liggesusers#' @include utilities.R NULL #'Kruskal-Wallis Effect Size #' #'@description Compute the effect size for Kruskal-Wallis test as the eta #' squared based on the H-statistic: \code{eta2[H] = (H - k + 1)/(n - k)}; #' where \code{H} is the value obtained in the Kruskal-Wallis test; \code{k} is #' the number of groups; \code{n} is the total number of observations. #' #' #' The eta-squared estimate assumes values from 0 to 1 and multiplied by 100% #' indicates the percentage of variance in the dependent variable explained by #' the independent variable. The interpretation values commonly in published #' litterature are: \code{0.01- < 0.06} (small effect), \code{0.06 - < 0.14} #' (moderate effect) and \code{>= 0.14} (large effect). #' #' Confidence intervals are calculated by bootstap. #' #'@inheritParams wilcox_effsize #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y variable used in the test. \item \code{n}: Sample #' counts. \item \code{effsize}: estimate of the effect size. \item #' \code{magnitude}: magnitude of effect size. \item \code{conf.low,conf.high}: #' lower and upper bound of the effect size confidence interval.} #' #'@references Maciej Tomczak and Ewa Tomczak. The need to report effect size #' estimates revisited. An overview of some recommended measures of effect #' size. Trends in Sport Sciences. 2014; 1(21):19-25. #' #' http://imaging.mrc-cbu.cam.ac.uk/statswiki/FAQ/effectSize #' #' http://www.psy.gla.ac.uk/~steve/best/effect.html #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # Kruskal-wallis rank sum test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% kruskal_effsize(len ~ dose) #' #' # Grouped data #' df %>% #' group_by(supp) %>% #' kruskal_effsize(len ~ dose) #' @export kruskal_effsize <- function(data, formula, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000){ args <- as.list(environment()) %>% .add_item(method = "kruskal_effsize") data %>% doo( .kruskal_effsize, formula, ci = ci, conf.level = conf.level, ci.type = ci.type, nboot = nboot ) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "kruskal_effsize")) } .kruskal_effsize <- function(data, formula, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000){ results <- eta_squared_h(data, formula) # Confidence interval of the effect size r if (ci == TRUE) { stat.func <- function(data, subset) { eta_squared_h(data, formula, subset = subset)$effsize } CI <- get_boot_ci( data, stat.func, conf.level = conf.level, type = ci.type, nboot = nboot ) results <- results %>% add_columns(conf.low = CI[1], conf.high = CI[2], .after = "effsize") } results %>% mutate(magnitude = get_eta_squared_magnitude(.data$effsize)) } eta_squared_h <- function(data, formula, subset = NULL, ...){ if(!is.null(subset)) data <- data[subset, ] res.kw <- kruskal_test(data, formula, ...) nb.groups <- res.kw$df + 1 nb.samples <- res.kw$n etasq <- (res.kw$statistic - nb.groups + 1) / (nb.samples - nb.groups) tibble( .y. = get_formula_left_hand_side(formula), n = nb.samples, effsize = etasq, method = "eta2[H]" ) } get_eta_squared_magnitude <- function(d){ magnitude.levels = c(0.06, 0.14, Inf) magnitude = c("small","moderate","large") d.index <- findInterval(abs(d), magnitude.levels)+1 magnitude <- factor(magnitude[d.index], levels = magnitude, ordered = TRUE) magnitude } rstatix/R/cohens_d.R0000644000176200001440000002214413651775021014070 0ustar liggesusers#' @include utilities.R utilities_two_sample_test.R #' @importFrom stats sd #' @importFrom stats var NULL #'Compute Cohen's d Measure of Effect Size #' #'@description Compute the effect size for t-test. T-test conventional effect #' sizes, proposed by Cohen, are: 0.2 (small effect), 0.5 (moderate effect) and #' 0.8 (large effect). #' #' Cohen's \code{d} is calculated as the difference between means or mean minus #' \code{mu} divided by the estimated standardized deviation. #' #' For independent samples t-test, there are two possibilities implemented. If #' the t-test did not make a homogeneity of variance assumption, (the Welch #' test), the variance term will mirror the Welch test, otherwise a pooled #' estimate is used. #' #' If a paired samples t-test was requested, then effect size desired is based #' on the standard deviation of the differences. #' #' It can also returns confidence intervals by bootstap. #' #'@inheritParams wilcox_effsize #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{x ~ group} where \code{x} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ cancer_group}. #'@param paired a logical indicating whether you want a paired test. #'@param mu theoretical mean, use for one-sample t-test. Default is 0. #'@param var.equal a logical variable indicating whether to treat the two #' variances as being equal. If TRUE then the pooled variance is used to #' estimate the variance otherwise the Welch (or Satterthwaite) approximation #' to the degrees of freedom is used. Used only for unpaired or independent samples test. #'@param hedges.correction logical indicating whether apply the Hedges #' correction by multiplying the usual value of Cohen's d by #' \code{(N-3)/(N-2.25)} (for unpaired t-test) and by \code{(n1-2)/(n1-1.25)} for paired t-test; #' where \code{N} is the total size of the two groups being compared (N = n1 + #' n2). #'@details Quantification of the effect size magnitude is performed using the #' thresholds defined in Cohen (1992). The magnitude is assessed using the #' thresholds provided in (Cohen 1992), i.e. \code{|d| < 0.2} "negligible", #' \code{|d| < 0.5} "small", \code{|d| < 0.8} "medium", otherwise "large". #'@references \itemize{ \item Cohen, J. (1988). Statistical power analysis for #' the behavioral sciences (2nd ed.). New York:Academic Press. \item Cohen, J. #' (1992). A power primer. Psychological Bulletin, 112, 155-159. \item Hedges, #' Larry & Olkin, Ingram. (1985). Statistical Methods in Meta-Analysis. #' 10.2307/1164953. \item Navarro, Daniel. 2015. Learning Statistics with R: A #' Tutorial for Psychology Students and Other Beginners (Version 0.5). } #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y variable used in the test. \item #' \code{group1,group2}: the compared groups in the pairwise tests. \item #' \code{n,n1,n2}: Sample counts. \item \code{effsize}: estimate of the effect #' size (\code{d} value). \item \code{magnitude}: magnitude of effect size. #' \item \code{conf.low,conf.high}: lower and upper bound of the effect size #' confidence interval.} #' @examples #' # One-sample t test effect size #' ToothGrowth %>% cohens_d(len ~ 1, mu = 0) #' #' # Two indepedent samples t-test effect size #' ToothGrowth %>% cohens_d(len ~ supp, var.equal = TRUE) #' #' # Paired samples effect size #' df <- data.frame( #' id = 1:5, #' pre = c(110, 122, 101, 120, 140), #' post = c(150, 160, 110, 140, 155) #' ) #' df <- df %>% gather(key = "treatment", value = "value", -id) #' head(df) #' #' df %>% cohens_d(value ~ treatment, paired = TRUE) #'@export cohens_d <- function(data, formula, comparisons = NULL, ref.group = NULL, paired = FALSE, mu = 0, var.equal = FALSE, hedges.correction = FALSE, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000){ env <- as.list(environment()) args <- env %>% .add_item(method = "cohens_d") params <- env %>% remove_null_items() %>% add_item(method = "cohens.d", detailed = FALSE) outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(number.of.groups > 2 & !is.null(ref.group)){ if(ref.group %in% c("all", ".all.")){ params$data <- create_data_with_all_ref_group(data, outcome, group) params$ref.group <- "all" } } test.func <- two_sample_test if(number.of.groups > 2) test.func <- pairwise_two_sample_test res <- do.call(test.func, params) %>% select(.data$.y., .data$group1, .data$group2, .data$estimate, everything()) %>% rename(effsize = .data$estimate) %>% mutate(magnitude = get_cohens_magnitude(.data$effsize)) %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "cohens_d")) res } # Cohens d core function ------------------------------- cohens.d <- function(x, y = NULL, mu = 0, paired = FALSE, var.equal = FALSE, hedges.correction = FALSE, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ...){ check_two_samples_test_args( x = x, y = y, mu = mu, paired = paired, conf.level = conf.level ) if (!is.null(y)) { DNAME <- paste(deparse(substitute(x)), "and", deparse(substitute(y))) if (paired) { OK <- complete.cases(x, y) x <- x[OK] - y[OK] y <- NULL mu <- 0 METHOD <- "Paired T-test" } else { x <- x[is.finite(x)] y <- y[is.finite(y)] METHOD <- "Independent T-test" } } else { DNAME <- deparse(substitute(x)) METHOD <- "One-sample T-test" x <- x[is.finite(x)] } if(is.null(y)){ formula <- x ~ 1 y <- rep(mu, length(x)) } else{ group <- rep(c("grp1", "grp2"), times = c(length(x), length(y))) %>% factor() x <- c(x, y) y <- group formula <- x ~ y } data <- data.frame(x, y) results <- get_cohens_d( data, formula, paired = paired, var.equal = var.equal, mu = mu, hedges.correction = hedges.correction ) # Confidence interval of the effect size r if (ci == TRUE) { stat.func <- function(data, subset) { get_cohens_d( data, formula = formula, subset = subset, paired = paired, var.equal = var.equal, mu = mu, hedges.correction = hedges.correction )$d } CI <- get_boot_ci( data, stat.func, conf.level = conf.level, type = ci.type, nboot = nboot ) results <- results %>% mutate(conf.low = CI[1], conf.high = CI[2]) } RVAL <- list(statistic = NA, p.value = NA, null.value = mu, method = METHOD, data.name = DNAME, estimate = results$d) if (ci) { attr(CI, "conf.level") <- conf.level RVAL <- c(RVAL, list(conf.int = CI)) } names(RVAL$estimate) <- "Cohen's d" class(RVAL) <- "htest" RVAL } # Helper to compute cohens d ----------------------------------- get_cohens_d <- function(data, formula, subset = NULL, paired = FALSE, mu = 0, var.equal = FALSE, hedges.correction = FALSE){ outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) if(!is.null(subset)) data <- data[subset, ] if(.is_empty(group)) number.of.groups <- 1 # Null model else number.of.groups <- data %>% pull(group) %>% unique() %>% length() unpaired.two.samples <- paired == FALSE & number.of.groups == 2 if(number.of.groups == 1){ x <- data %>% pull(outcome) d <- one_sample_d(x, mu) } else if(number.of.groups == 2){ groups <- data %>% pull(group) data <- data %>% split(groups) x <- data[[1]] %>% pull(outcome) y <- data[[2]] %>% pull(outcome) if(paired){ d <- paired_sample_d(x, y) } else{ d <- two_independent_sample_d(x, y, var.equal) } } else{ stop("The grouping factors contain more than 2 levels.") } # Hedge's correction if(hedges.correction){ if(paired){ n <- length(x) d <- d*(n - 2)/(n - 1.25) } else if (unpaired.two.samples){ n <- length(x) + length(y) d <- d * (n - 3)/(n - 2.25) } else{ stop( "Hedge's Correction for One Sample Test is not supported.\n", "Please use `hedges.correction = FALSE` (default) for one sample test.", call. = FALSE ) } } tibble( d, magnitude = get_cohens_magnitude(d) ) } one_sample_d <- function(x, mu = 0){ (mean(x) - mu)/sd(x) } two_independent_sample_d <- function(x, y, var.equal = TRUE){ if(var.equal){ squared.dev <- (c(x - mean(x), y - mean(y)))^2 n <- length(squared.dev) SD <- sqrt(sum(squared.dev)/(n-2)) } else { SD <- sqrt((var(x) + var(y))/2) } mean.diff <- mean(x) - mean(y) mean.diff/SD } paired_sample_d <- function(x, y){ mean(x-y)/sd(x-y) } get_cohens_magnitude <- function(d){ magnitude.levels = c(0.2,0.5,0.8) magnitude = c("negligible","small","moderate","large") d.index <- findInterval(abs(d), magnitude.levels)+1 magnitude <- factor(magnitude[d.index], levels = magnitude, ordered = TRUE) magnitude } rstatix/R/multinom_test.R0000644000176200001440000000524313574261160015210 0ustar liggesusers#' @include utilities.R NULL #'Exact Multinomial Test #' #'@description Performs an exact multinomial test. Alternative to the chi-square test of goodness-of-fit-test when the sample #' size is small. #' #'@inheritParams binom_test #' #'@seealso \link{binom_test} #'@return return a data frame containing the p-value and its significance. #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #' #' @examples #' # Data #' tulip <- c(red = 81, yellow = 50, white = 27) #' #' # Question 1: are the color equally common ? #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # this is a test of homogeneity #' res <- multinom_test(tulip) #' res #' #' attr(res, "descriptives") #' #' # Pairwise comparisons between groups #' pairwise_binom_test(tulip, p.adjust.method = "bonferroni") #' #' #' # Question 2: comparing observed to expected proportions #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' # this is a goodness-of-fit test #' expected.p <- c(red = 0.5, yellow = 0.33, white = 0.17) #' res <- multinom_test(tulip, expected.p) #' res #' attr(res, "descriptives") #' #' # Pairwise comparisons against a given probabilities #' pairwise_binom_test_against_p(tulip, expected.p) #' @export multinom_test <- function (x, p = rep(1/length(x), length(x)), detailed = FALSE) { args <- as.list(environment()) %>% add_item(method = "exact_multinom_test") if (!is.vector(x)) { stop("'x' must be a vector") } if (sum(p) != 1) { stop("sum of probabilities must be 1") } if (length(x) != length(p)) { stop("'x' and 'p' lengths differ") } if(is.null(names(x))){ names(x) <- paste0("grp", 1:length(x)) } size <- sum(x) groups <- length(x) numEvents <- choose(size + groups - 1, groups - 1) pObs <- stats::dmultinom(x, size, p) findVectors <- function(groups, size) { if (groups == 1) { mat <- size } else { mat <- matrix(rep(0, groups - 1), nrow = 1) for (i in 1:size) { mat <- rbind(mat, findVectors(groups - 1, i)) } mat <- cbind(mat, size - rowSums(mat)) } mat } eventMat <- findVectors(groups, size) eventProb <- apply(eventMat, 1, function(x) stats::dmultinom(x, size, p)) p.val <- sum(eventProb[eventProb <= pObs]) results <- tibble( p = p.val, method = "Exact multinomial test" ) %>% add_significance() %>% select(.data$p, .data$p.signif, .data$method) descriptives <- tibble( group = names(x), observed = x, expected = p*size ) if(!detailed){ results <- results[, c("p", "p.signif")] } results %>% set_attrs(args = args, descriptives = descriptives) %>% add_class(c("rstatix_test", "exact_multinom_test")) } rstatix/R/eta_squared.R0000644000176200001440000000414313672605044014602 0ustar liggesusers#' @include utilities.R NULL #' Effect Size for ANOVA #' @description Compute eta-squared and partial eta-squared for all terms in an #' ANOVA model. #' @param model an object of class aov or anova. #' @return a numeric vector with the effect size statistics #' @describeIn eta_squared compute eta squared #' @examples #' # Data preparation #' df <- ToothGrowth #' df$dose <- as.factor(df$dose) #' #' # Compute ANOVA #' res.aov <- aov(len ~ supp*dose, data = df) #' summary(res.aov) #' #' # Effect size #' eta_squared(res.aov) #' partial_eta_squared(res.aov) #' @export eta_squared <- function(model){ model %>% aov_stat_summary() %>% aov_stat_core("eta") } #' @describeIn eta_squared compute partial eta squared. #' @export partial_eta_squared <- function(model){ model %>% aov_stat_summary() %>% aov_stat_core("peta") } aov_stat_summary <- function (model) { if (!inherits(model, c("aov", "anova"))) model <- stats::anova(model) aov.sum <- broom::tidy(model) if (!tibble::has_name(aov.sum, "meansq")) aov.sum <- tibble::add_column(aov.sum, meansq = aov.sum$sumsq/aov.sum$df, .after = "sumsq") aov.sum } aov_stat_core <- function(aov.sum, type){ meansq.resid <- aov.sum[["meansq"]][nrow(aov.sum)] ss.total <- sum(aov.sum[["sumsq"]]) ss.resid <- aov.sum[["sumsq"]][nrow(aov.sum)] n_terms <- nrow(aov.sum) - 1 if (type == "omega") { aovstat <- purrr::map_dbl(1:n_terms, function(x) { ss.term <- aov.sum[["sumsq"]][x] df.term <- aov.sum[["df"]][x] (ss.term - df.term * meansq.resid)/(ss.total + meansq.resid) }) } else if (type == "eta") { aovstat <- purrr::map_dbl(1:n_terms, ~aov.sum[["sumsq"]][.x]/sum(aov.sum[["sumsq"]])) } else if (type %in% c("cohens.f", "peta")) { aovstat <- purrr::map_dbl(1:n_terms, ~aov.sum[["sumsq"]][.x]/(aov.sum[["sumsq"]][.x] + ss.resid)) } if (type == "cohens.f") aovstat <- sqrt(aovstat/(1 - aovstat)) names(aovstat) <- aov.sum[["term"]][1:n_terms] aovstat } rstatix/R/friedman_effsize.R0000644000176200001440000001024613534271106015601 0ustar liggesusers#' @include utilities.R friedman_test.R NULL #' Friedman Test Effect Size (Kendall's W Value) #' #'@description Compute the effect size estimate (referred to as \code{w}) for #' Friedman test: \code{W = X2/N(K-1)}; where \code{W} is the Kendall's W #' value; \code{X2} is the Friedman test statistic value; \code{N} is the sample #' size. \code{k} is the number of measurements per subject. #' #' The Kendall’s W coefficient assumes the value from 0 (indicating no #' relationship) to 1 (indicating a perfect relationship). #' #' Kendalls uses the Cohen’s interpretation guidelines of \code{0.1 - < 0.3} (small #' effect), \code{0.3 - < 0.5} (moderate effect) and \code{>= 0.5} (large #' effect) #' #' Confidence intervals are calculated by bootstap. #' #'@inheritParams friedman_test #'@inheritParams wilcox_effsize #'@param ... other arguments passed to the function \code{\link[stats]{friedman.test}()} #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y variable used in the test. \item \code{n}: Sample #' counts. \item \code{effsize}: estimate of the effect size. \item #' \code{magnitude}: magnitude of effect size. \item \code{conf.low,conf.high}: #' lower and upper bound of the effect size confidence interval.} #' #'@references Maciej Tomczak and Ewa Tomczak. The need to report effect size #' estimates revisited. An overview of some recommended measures of effect #' size. Trends in Sport Sciences. 2014; 1(21):19-25. #' #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth %>% #' filter(supp == "VC") %>% #' mutate(id = rep(1:10, 3)) #' head(df) #' #' # Friedman test effect size #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% friedman_effsize(len ~ dose | id) #' @export friedman_effsize <- function(data, formula, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ...){ args <- as.list(environment()) %>% .add_item(method = "friedman_effsize") if(is_grouped_df(data)){ results <- data %>% doo( .friedman_effsize, formula, ci = ci, conf.level = conf.level, ci.type = ci.type, nboot = nboot, ... ) } else{ results <- .friedman_effsize( data, formula, ci = ci, conf.level = conf.level, ci.type = ci.type, nboot = nboot, ... ) } results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "friedman_effsize")) } .friedman_effsize <- function(data, formula, ci = FALSE, conf.level = 0.95, ci.type = "perc", nboot = 1000, ...){ results <- kendall_w(data, formula, ...) # Confidence interval of the effect size if (ci == TRUE) { # Paired data, bootstrap should be performed on wide data vars <- get_friedman_vars(formula) data.wide <- data %>% select(!!!syms(c(vars$wid, vars$dv, vars$within))) %>% spread(key = vars$within, value = vars$dv) # Boot function stat.func <- function(data.wide, subset) { if(!is.null(subset)) data.wide <- data.wide[subset, ] data.long <- data.wide %>% mutate(!!vars$wid := 1:nrow(data.wide)) %>% gather(key = !!vars$within, value = !!vars$dv, - !!vars$wid) kendall_w(data.long, formula)$effsize } CI <- get_boot_ci( data.wide, stat.func, conf.level = conf.level, type = ci.type, nboot = nboot ) results <- results %>% add_columns(conf.low = CI[1], conf.high = CI[2], .after = "effsize") } results %>% mutate(magnitude = get_kendall_w_magnitude(.data$effsize)) } kendall_w <- function(data, formula, subset = NULL, ...){ if(!is.null(subset)) data <- data[subset, ] res.f <- friedman_test(data, formula, ...) x2 <- res.f$statistic nb.samples <- res.f$n k <- nrow(data)/nb.samples # number of measurements per sample w <- x2 / (nb.samples * (k-1)) tibble( .y. = get_formula_left_hand_side(formula), n = nb.samples, effsize = w, method = "Kendall W" ) } get_kendall_w_magnitude <- function(d){ magnitude.levels = c(0.3, 0.5, Inf) magnitude = c("small","moderate","large") d.index <- findInterval(abs(d), magnitude.levels)+1 magnitude <- factor(magnitude[d.index], levels = magnitude, ordered = TRUE) magnitude } rstatix/R/sample_n_by.R0000644000176200001440000000114113466102717014570 0ustar liggesusers#' @include utilities.R NULL #' Sample n Rows By Group From a Table #' #' @description sample n rows by group from a table using the \code{\link[dplyr]{sample_n}()} function. #' #' @param data a data frame #' @param ... Variables to group by #' @param size the number of rows to select #' @param replace with or without replacement? #' #' @examples #' ToothGrowth %>% sample_n_by(dose, supp, size = 2) #' @name sample_n_by #' @export sample_n_by <- function(data, ..., size = 1, replace = FALSE){ data %>% group_by(...) %>% dplyr::sample_n(size = size, replace = replace) %>% dplyr::ungroup() } rstatix/R/utilities_two_sample_test.R0000644000176200001440000002323013710022470017574 0ustar liggesusers # Comparing means # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Global function to compare means compare_mean <- function( data, formula, method = "t.test", paired = FALSE, comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", detailed = FALSE, ...) { outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(method %in% c("anova", "kruskal.test") & number.of.groups <= 2) stop("The number of groups <= 2; you should use t.test or wilcox.test") # Case of one sample test if(number.of.groups <= 2){ res <- two_sample_test(data, formula, method = method, paired = paired, ...) } # Pairwise comparisons else if(number.of.groups > 2){ if(method == "anova"){ res <- anova_test(data, formula, ...) %>% select(.data$Effect, .data$F, .data$p) %>% set_colnames(c("term", "statistic", "p")) %>% add_column(method = "Anova", .after = "p") %>% add_column(.y. = outcome, .before = "term") %>% as_tibble() } else if(method == "kruskal.test") res <- kruskal_test(data, formula, ...) else if(is.null(ref.group)) res <- pairwise_two_sample_test( data, formula, method = method, paired = paired, comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, detailed = detailed, ... ) else if(ref.group %in% c("all", ".all.")) res <- two_sample_test_one_vs_all ( data, formula, method = method, p.adjust.method = p.adjust.method, detailed = detailed, ... ) else res <- pairwise_two_sample_test( data, formula, method = method, paired = paired, comparisons = comparisons, ref.group = ref.group, p.adjust.method = p.adjust.method, detailed = detailed, ... ) } if(!detailed) res <- remove_details(res, method = method) res } # Performs one or two samples mean comparisons two_sample_test <- function(data, formula, method = "t.test", ref.group = NULL, detailed = FALSE, ...) { if (is_grouped_df(data)) { res <- data %>% doo(two_sample_test, formula, method = method, ref.group = ref.group, detailed = detailed, ...) return(res) } test.function <- method test.args <- list() grp1 <- grp2 <- NULL outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) # One sample mean comparison ========================= if (.is_empty(group)) { grp1 <- "1" grp2 <- "null model" outcome.values <- data %>% pull(!!outcome) n <- length(outcome.values) test.args <- list(x = outcome.values, ...) } # Two sample mean comparisons ======================== else { # Convert group into factor if this is not already the case data <- data %>% .as_factor(group, ref.group = ref.group) outcome.values <- data %>% pull(!!outcome) group.values <- data %>% pull(!!group) group.levels <- data %>% get_levels(group) grp1 <- group.levels[1] grp2 <- group.levels[2] x <- outcome.values[group.values == grp1] y <- outcome.values[group.values == grp2] n1 <- length(x) n2 <- length(y) test.args <- list(x = x, y = y, ...) } statistic <- p <- NULL res <- suppressWarnings(do.call(test.function, test.args)) %>% as_tidy_stat() %>% add_columns( .y. = outcome, group1 = grp1, group2 = grp2, .before = "statistic" ) # Add n columns if(grp2 == "null model"){ res <- res %>% add_columns(n = n, .before = "statistic") } else{ res <- res %>% add_columns(n1 = n1, n2 = n2, .before = "statistic") } if(!detailed) res <- remove_details(res, method = method) res } # Pairwise mean comparisons pairwise_two_sample_test <- function(data, formula, method = "t.test", comparisons = NULL, ref.group = NULL, p.adjust.method = "holm", detailed = FALSE, ...) { if (is_grouped_df(data)) { res <- data %>% doo( pairwise_two_sample_test, formula, method, comparisons, ref.group, p.adjust.method, detailed = detailed, ... ) return(res) } outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) data <- data %>% .as_factor(group, ref.group = ref.group) group.levels <- data %>% get_levels(group) # All possible pairwise comparisons # if ref.group specified, only comparisons against reference will be kept if (is.null(comparisons)) { comparisons <- group.levels %>% .possible_pairs(ref.group = ref.group) } res <- compare_pairs(data, formula, comparisons, method, detailed = detailed, ...) %>% adjust_pvalue(method = p.adjust.method) %>% add_significance() %>% p_round(digits = 3) if(!detailed) res <- remove_details(res, method = method) res } # One vs all mean comparisons ----------------------------------- two_sample_test_one_vs_all <- function(data, formula, method = "t.test", p.adjust.method = "holm", detailed = FALSE, ...) { if (is_grouped_df(data)) { results <- data %>% doo(two_sample_test_one_vs_all, formula, method, p.adjust.method, detailed = detailed, ...) return(results) } outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) new.data <- create_data_with_all_ref_group(data, outcome, group) pairwise_two_sample_test( data = new.data, formula = formula, method = method, ref.group = "all", p.adjust.method = p.adjust.method, detailed = detailed, ... ) } # Create new data set containing the "all" group level create_data_with_all_ref_group <- function(data, outcome, group){ grouping.vars <- grouping.vars.data <- NULL if(is_grouped_df(data)){ grouping.vars <- dplyr::group_vars(data) data <- dplyr::ungroup(data) grouping.vars.data <- data %>% select(!!!syms(grouping.vars)) } data <- data %>% .as_factor(group) outcome.values <- data %>% pull(!!outcome) group.values <- data %>% pull(!!group) group.levels <- group.values %>% levels() all.data <- tibble( outcome = outcome.values, group = "all" ) source.data <- tibble( outcome = outcome.values, group = as.character(group.values) ) new.data <- all.data %>% bind_rows(source.data) %>% mutate(group = factor(group, levels = c("all", group.levels))) colnames(new.data) <- c(outcome, group) if(!is.null(grouping.vars)){ # repeat grouping.vars.data for "all" group new.data <- dplyr::bind_rows(grouping.vars.data, grouping.vars.data) %>% dplyr::bind_cols(new.data) %>% group_by(!!!syms(grouping.vars)) } new.data } # compare_pair(ToothGrowth, len ~ dose, c("0.5", "1")) compare_pair <- function(data, formula, pair, method = "t.test", ...){ group <- get_formula_right_hand_side(formula) data %>% filter(!!sym(group) %in% pair) %>% droplevels() %>% two_sample_test(formula, method = method, ...) } # compare_pairs(ToothGrowth, len ~ dose, list(c("0.5", "1"), c("1", "2"))) compare_pairs <- function(data, formula, pairs, method = "t.test", ...){ .f <- function(pair, data, formula, method, ...){ compare_pair(data, formula, pair, method, ...) } pairs %>% map(.f, data, formula, method, ...) %>% bind_rows() } # Remove details from statistical test results #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% remove_details <- function(res, method){ if(method == "anova"){ # Remove details from ANOVA summary: such as intercept row, Sum Sq columns aov.table <- res$ANOVA aov.table = aov.table[, names(aov.table) %in% c('Effect','DFn','DFd','F','p','p<.05', 'ges', 'pes')] intercept.row <- grepl("Intercept", aov.table$Effect) res$ANOVA<- aov.table[!intercept.row, ] } else if(method %in% c("t.test", "wilcox.test", "kruskal.test", "sign.test") ){ columns.to.keep <- intersect( c(".y.", "group1", "group2", "n", "n1", "n2", "statistic", "df", "p", "p.signif", "p.adj", "p.adj.signif"), colnames(res) ) res <- res[, columns.to.keep] } else if(method %in% c("coin.wilcox.test", "cohens.d")){ columns.to.remove <- c("p", "p.adj", "p.adj.signif", "p.signif", "statistic", "method", "alternative", "df") columns.to.keep <- setdiff(colnames(res), columns.to.remove) res <- res %>% select(!!!syms(columns.to.keep)) } else if(method %in% c("prop.test")){ columns.to.keep <- intersect( c("n", "group", "statistic", "df", "p", "p.signif", "p.adj", "p.adj.signif"), colnames(res) ) res <- res[, columns.to.keep] } else{ columns.to.remove <- c("n1", "n2", "n", "method", "alternative", "statistic", "df") columns.to.keep <- setdiff(colnames(res), columns.to.remove) res <- res %>% select(!!!syms(columns.to.keep)) } res } # Two samples tests-------------------------------------- # Check two samples test args check_two_samples_test_args <- function(x, y = NULL, mu = 0, paired = FALSE, conf.level = 0.5){ if (!missing(mu) & ((length(mu) > 1L) || !is.finite(mu))) stop("'mu' must be a single number") if (!((length(conf.level) == 1L) & is.finite(conf.level) & (conf.level > 0) & (conf.level < 1))) stop("'conf.level' must be a single number between 0 and 1") if (!is.numeric(x)) stop("'x' must be numeric") if (!is.null(y)) { if (!is.numeric(y)) stop("'y' must be numeric") if (paired) { if (length(x) != length(y)) stop("'x' and 'y' must have the same length") } } else { if (paired) stop("'y' is missing for paired test") } if (length(x) < 1L) stop("not enough (finite) 'x' observations") } rstatix/R/anova_test.R0000644000176200001440000005414113743650760014457 0ustar liggesusers#' @include utilities.R factorial_design.R anova_summary.R NULL #'Anova Test #' #' #'@description Provides a pipe-friendly framework to perform different types of #' ANOVA tests, including: \itemize{ \item #' \strong{\href{https://www.datanovia.com/en/lessons/anova-in-r/}{Independent #' measures ANOVA}}: between-Subjects designs, \item #' \strong{\href{https://www.datanovia.com/en/lessons/repeated-measures-anova-in-r/}{Repeated #' measures ANOVA}}: within-Subjects designs \item #' \strong{\href{https://www.datanovia.com/en/lessons/mixed-anova-in-r/}{Mixed #' ANOVA}}: Mixed within within- and between-Subjects designs, also known as #' split-plot ANOVA and \item #' \strong{\href{https://www.datanovia.com/en/lessons/ancova-in-r/}{ANCOVA: #' Analysis of Covariance}}. } #' #' The function is an easy to use wrapper around \code{\link[car]{Anova}()} and #' \code{\link[stats]{aov}()}. It makes ANOVA computation handy in R and It's #' highly flexible: can support model and formula as input. Variables can be #' also specified as character vector using the arguments \code{dv, wid, #' between, within, covariate}. #' #' The results include ANOVA table, generalized effect size and some assumption #' checks. #' #' #'@param data a data.frame or a model to be analyzed. #'@param formula a formula specifying the ANOVA model similar to #' \link[stats]{aov}. Can be of the form \code{y ~ group} where \code{y} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ cancer_group}. #' #' Examples of supported formula include: \itemize{ \item Between-Ss ANOVA #' (independent measures ANOVA): \code{y ~ b1*b2} \item Within-Ss ANOVA #' (repeated measures ANOVA): \code{y ~ w1*w2 + Error(id/(w1*w2))} \item Mixed #' ANOVA: \code{y ~ b1*b2*w1 + Error(id/w1)} } #' #' If the formula doesn't contain any within vars, a linear model is directly #' fitted and passed to the ANOVA function. For repeated designs, the ANOVA #' variables are parsed from the formula. #' #'@param dv (numeric) dependent variable name. #'@param wid (factor) column name containing individuals/subjects identifier. #' Should be unique per individual. #'@param between (optional) between-subject factor variables. #'@param within (optional) within-subjects factor variables #'@param covariate (optional) covariate names (for ANCOVA) #'@param type the type of sums of squares for ANOVA. Allowed values are either #' 1, 2 or 3. \code{type = 2} is the default because this will yield identical #' ANOVA results as type = 1 when data are balanced but type = 2 will #' additionally yield various assumption tests where appropriate. When the data #' are unbalanced the \code{type = 3} is used by popular commercial softwares #' including SPSS. #'@param effect.size the effect size to compute and to show in the ANOVA #' results. Allowed values can be either "ges" (generalized eta squared) or #' "pes" (partial eta squared) or both. Default is "ges". #'@param white.adjust Default is FALSE. If TRUE, heteroscedasticity correction #' is applied to the coefficient of covariance matrix. Used only for #' independent measures ANOVA. #'@param error (optional) for a linear model, an lm model object from which the #' overall error sum of squares and degrees of freedom are to be calculated. #' Read more in \code{\link[car]{Anova}()} documentation. #'@param observed Variables that are observed (i.e, measured) as compared to #' experimentally manipulated. The default effect size reported (generalized #' eta-squared) requires correct specification of the observed variables. #'@param detailed If TRUE, returns extra information (sums of squares columns, #' intercept row, etc.) in the ANOVA table. #'@param x an object of class \code{anova_test} #'@param correction character. Used only in repeated measures ANOVA test to #' specify which correction of the degrees of freedom should be reported for #' the within-subject factors. Possible values are: \itemize{ \item{"GG"}: #' applies Greenhouse-Geisser correction to all within-subjects factors even if #' the assumption of sphericity is met (i.e., Mauchly's test is not #' significant, p > 0.05). \item{"HF"}: applies Hyunh-Feldt correction to all #' within-subjects factors even if the assumption of sphericity is met, #' \item{"none"}: returns the ANOVA table without any correction and #' \item{"auto"}: apply automatically GG correction to only within-subjects #' factors violating the sphericity assumption (i.e., Mauchly's test p-value is #' significant, p <= 0.05). } #'@seealso \code{\link{anova_summary}()}, \code{\link{factorial_design}()} #'@return return an object of class \code{anova_test} a data frame containing #' the ANOVA table for independent measures ANOVA. #' #' However, for repeated/mixed measures ANOVA, a list containing the following #' components are returned: ANOVA table, Mauchly's Test for Sphericity, #' Sphericity Corrections. These table are described more in the documentation #' of the function \code{\link{anova_summary}()}. #' #' The \strong{returned object has an attribute} called \code{args}, which is a #' list holding the arguments used to fit the ANOVA model, including: data, dv, #' within, between, type, model, etc. #' #'@details The setting in \code{anova_test()} is done in such a way that it #' gives the same results as SPSS, one of the most used commercial software. By #' default, R uses treatment contrasts, where each of the levels is compared to #' the first level used as baseline. The default contrast can be checked using #' \code{options('contrasts')}. In the function \code{anova_test()}, the #' following setting is used #' \code{options(contrasts=c('contr.sum','contr.poly'))}, which gives #' orthogonal contrasts where you compare every level to the overall mean. This #' setting gives the same output as the most commonly used commercial #' softwares, like SPSS. If you want to obtain the same result with the #' function \code{car::Anova()} as the one obtained with #' \code{rstatix::anova_test()}, then don't forget to set #' \code{options(contrasts=c('contr.sum','contr.poly'))}. #'@author Alboukadel Kassambara, \email{alboukadel.kassambara@@gmail.com} #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # One-way ANOVA test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% anova_test(len ~ dose) #' #' # Grouped One-way ANOVA test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% #' group_by(supp) %>% #' anova_test(len ~ dose) #' #' # Two-way ANOVA test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% anova_test(len ~ supp*dose) #' #' # Two-way repeated measures ANOVA #' #::::::::::::::::::::::::::::::::::::::::: #' df$id <- rep(1:10, 6) # Add individuals id #' # Use formula #' \donttest{ #' df %>% anova_test(len ~ supp*dose + Error(id/(supp*dose))) #' } #' #' #' # or use character vector #' df %>% anova_test(dv = len, wid = id, within = c(supp, dose)) #' #' # Extract ANOVA table and apply correction #' #::::::::::::::::::::::::::::::::::::::::: #' res.aov <- df %>% anova_test(dv = len, wid = id, within = c(supp, dose)) #' get_anova_table(res.aov, correction = "GG") #' #' #' # Use model as arguments #' #::::::::::::::::::::::::::::::::::::::::: #' .my.model <- lm(yield ~ block + N*P*K, npk) #' anova_test(.my.model) #' #' #'@describeIn anova_test perform anova test #'@export anova_test <- function(data, formula, dv, wid, between, within, covariate, type = NULL, effect.size = "ges", error = NULL, white.adjust = FALSE, observed = NULL, detailed = FALSE){ .args <- rlang::enquos( dv = dv, wid = wid, between = between, within = within, covariate = covariate) %>% select_quo_variables(data) %>% add_item(type = type, white.adjust = white.adjust, method = "anova_test") if(!missing(formula)) .args$formula <- formula .anova_test <- function(data, .args, effect.size = "ges", error = NULL, observed = NULL, detailed = FALSE){ .args <- .args %>% add_item(data = data) %>% check_anova_arguments() if(.args$type != 1) { if(is.null(error)) res.anova <- car_anova(.args) else res.anova <- car_anova(.args, error = error) } else if(.args$type == 1) res.anova <- stats_aov(.args) else stop("Something is wrong...") results <- res.anova %>% anova_summary( effect.size = effect.size, detailed = detailed, observed = observed ) results } .append_anova_class <- function(x){ class(x) <- c("anova_test", class(x), "rstatix_test") x } if(is_grouped_df(data)){ results <- data %>% doo( ~.anova_test(data = ., .args = .args, effect.size = effect.size, error = error, observed = observed, detailed = detailed), result = "anova" ) if("anova" %in% colnames(results)){ # This happens for repeated measure anova results <- results %>% mutate(anova = map(.data$anova, .append_anova_class)) } results <- results %>% set_attrs(args = list(data = data)) class(results) <- c("grouped_anova_test", class(results), "rstatix_test") } else{ results <- .anova_test( data, .args = .args, effect.size = effect.size, error = error, observed = observed, detailed = detailed ) %>% .append_anova_class() } results } # Extract ANOVA table ----------------------------------------------- #' @describeIn anova_test extract anova table from an object of class #' \code{anova_test}. When within-subject factors are present, either #' sphericity corrected or uncorrected degrees of freedom can be reported. #' @export get_anova_table <- function(x, correction = c("auto", "GG", "HF", "none")){ correction <- match.arg(correction) if(is_grouped_anova_test(x)){ results <- get_anova_table_from_grouped_test(x, correction = correction) } else{ results <- get_anova_table_from_simple_test(x, correction = correction) } results } get_anova_table_from_simple_test <- function(x, correction = "auto"){ correction.method <- method <- correction if(method == "auto") method = "GG" # Independent anova if(!inherits(x, "list")){ return(x) } if(correction.method == "none"){ res.aov <- x$ANOVA attr(res.aov, "args") <- attr(x, "args") class(res.aov) <- c("anova_test", class(res.aov), "rstatix_test") return(res.aov) } # repeated/mixed design # Get correction table from anova_test .args <- attr(x, "args") get_corrections_table <- function(x, method = c("GG", "HF")){ method <- match.arg(method) pattern <- paste0("Effect|", method) corrections <- x$`Sphericity Corrections` %>% select(tidyselect::matches(pattern)) colnames(corrections) <- c("Effect", "epsilon", "df", "p", "p<.05") corrections <- corrections %>% tidyr::separate(col = "df", into = c("DFn", "DFd"), sep = ", ", convert = TRUE) %>% mutate(method = method) corrections } res.aov <- x$ANOVA sphericity <- x$`Mauchly's Test for Sphericity` corrections <- get_corrections_table(x, method) # If auto apply correction only when sphericity is not assumed (Mauchly p < 0.05) if(correction.method == "auto"){ corrections %<>% filter(sphericity$p <= 0.05) } if(nrow(corrections) > 0){ rownames(res.aov) <- res.aov$Effect rownames(corrections) <- corrections$Effect cols.to.update <- c("DFn", "DFd", "p", "p<.05") rows.to.update <- rownames(corrections) res.aov[rows.to.update, cols.to.update] <- corrections[rows.to.update, cols.to.update] rownames(res.aov) <- 1:nrow(res.aov) } res.aov <- res.aov %>% set_attrs(args = .args) class(res.aov) <- c("anova_test", class(res.aov), "rstatix_test") res.aov } get_anova_table_from_grouped_test <- function(x, correction = "auto"){ if(!is_grouped_anova_test(x)){ return(x) } extract_table <- function(x, correction){ get_anova_table_from_simple_test(x, correction = correction) %>% remove_class(c("anova_test", "rstatix_test")) } x %>% keep_only_tbl_df_classes() %>% mutate(anova = map(.data$anova, extract_table, correction = correction)) %>% unnest(cols = "anova") } is_anova_test <- function(x){ inherits(x, "anova_test") } is_grouped_anova_test <- function(x){ answer <- FALSE if(("anova" %in% colnames(x))){ if(inherits(x$anova, "list")){ answer <- inherits(x$anova[[1]], "anova_test") } } answer } # Printing anova and plotting model diagnostic ----------------------------------------------- #' @rdname anova_test #' @method print anova_test #' @param ... additional arguments #' @export print.anova_test <- function(x, ...) { .args <- attr(x, "args") type <- switch(.args$type, `1` = "I", `2` = "II", `3` = "III") cat("ANOVA Table (type", type, "tests)\n\n") if(inherits(x, "data.frame")) print.data.frame(x) else if(inherits(x, "list")){ attr(x, "args") <- NULL class(x) <- "list" print(x) } } #' @rdname anova_test #' @method plot anova_test #' @export plot.anova_test <- function(x, ...) { .args <- attr(x, "args") graphics::plot(.args$model, ...) } # Check arguments ----------------------------------------------- # Check the arguments of ANOVA # .args is a list check_anova_arguments <- function(.args){ if(!is.null(.args$formula)){ .args <- get_anova_vars_from_formula(.args) if(is.null(.args$within)) .args$model <- fit_lm(.args) } if(inherits(.args$data, "aovlist")){ stop("A model of class aovlist is not supported.") } else if(has_model(.args)){ if(is.null(.args$type)) .args$type <- 2 return(.args) } .args <- .args %>% check_factorial_design() %>% check_anova_type() .args } get_anova_vars_from_formula <- function(.args){ formula <- .args$formula data <- .args$data vars <- all.vars(formula) stop_if_multiple_error_terms(formula) # Detect transformed responses: lhs <- all.names(formula[[2]]) transf <- setdiff(lhs, all.vars(formula[[2]])) if (length(transf) == 0) transf = NULL if (!is.null(transf)) { origdv <- setdiff(lhs, transf) dv <- paste0(transf[1], ".", origdv) data[[dv]] <- eval(formula[[2]], envir = data) # add transformed version vars <- vars[!(vars %in% lhs)] }else { dv <- vars[1] vars <- vars[-1] } error.vars <- get_formula_error_vars(formula) id <- error.vars[1] within <- error.vars[-1] between <- vars[!(vars %in% c(id, within))] if(length(within) == 0) within <- NULL if(length(between) == 0) between <- NULL if(is.na(id)) id <- NULL .args <- .args %>% .add_item(data = data, dv = dv, wid = id, between = between, within = within) .args } stop_if_multiple_error_terms <- function(formula){ .terms <- stats::terms(formula, "Error") .error.terms <- attr(.terms, "specials")$Error if (length(.error.terms) > 1L) stop(sprintf("there are %d Error terms: only 1 is allowed", length(.error.terms))) } # stop if ancova with repeated variables stop_if_repeated_ancova <- function(.args){ if(is_repeated_ancova(.args) | is_mixed_ancova(.args)){ stop("Don't support ANCOVA with repeated measures") } .args } # Check anova design and type is_design_balanced <- function(.args){ res <- .args$data %>% group_by(!!!syms(.args$between)) %>% summarise(count = n()) length(unique(res$count)) == 1 } is_repeated_anova <- function(.args){ is.null(.args$between) & !is.null(.args$within) & is.null(.args$covariate) } is_independent_anova <- function(.args){ !is.null(.args$between) & is.null(.args$within) & is.null(.args$covariate) } is_mixed_anova <- function(.args){ !is.null(.args$between) & !is.null(.args$within) & is.null(.args$covariate) } is_repeated_ancova <- function(.args){ !is.null(.args$within) & !is.null(.args$covariate) & is.null(.args$between) } is_independent_ancova <- function(.args){ is.null(.args$within) & !is.null(.args$covariate) & !is.null(.args$between) } is_mixed_ancova <- function(.args){ !is.null(.args$between) & !is.null(.args$within) & !is.null(.args$covariate) } # Check anova type check_anova_type <- function(.args){ n.vars <- length(c(.args$between, .args$within)) if(is.null(.args$type)){ .args$type <- 2 if(is_repeated_anova(.args)) .args$type <- 3 else if(!is.null(.args$between)) { if(!is_design_balanced(.args) & n.vars > 1) .args$type <- 3 } } else if (.args$type == 1){ if(!is_design_balanced(.args) & n.vars > 1){ warning("Your data are unbalanced and there are more than one variable. ", "In this case, using 'type = 1' is not recommended. ", "Consider using type 3 ANOVA.", immediate.=TRUE, call.=FALSE) } } .args } is_model <- function(object){ models <- c("lm", "aov", "glm", "multinom", "polr", "mlm", "manova") inherits(object, models) } # Get anova model from the list of arguments get_anova_model <- function(.args){ if(!is.null(.args$model)) return(.args$model) else if(is_model(.args$data)) return(.args$data) else stop("No model detected in ANOVA arguments") } # Check if ANOVA arguments contain model has_model <- function(.args){ !is.null(.args$model) | is_model(.args$data) } # Fit lm from formula and data ------------------------------------ #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% fit_lm <- function(.args){ .args <- remove_missing_values_in_data(.args) lm_data <- droplevels(.args$data) lm_formula <- .args$formula opt <- options( "contrasts" = c( "contr.sum", "contr.poly" ) ) results <- stats::lm(lm_formula, lm_data) options(opt) results } # Compute the different types of ANOVA ----------------------------- #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% car_anova <- function(.args, ...){ if(has_model(.args)){ .model <- get_anova_model(.args) res.anova <- car::Anova( .model, type = .args$type, white.adjust = .args$white.adjust, ... ) .args$model <- .model } else{ design <- factorial_design( data = .args$data, dv = .args$dv, wid = .args$wid, between = .args$between, within = .args$within, covariate = .args$covariate ) if(is_independent_anova(.args)){ res.anova <- Anova( design$model, type = .args$type, white.adjust = .args$white.adjust, ... ) } else{ res.anova <- Anova( design$model, idata = design$idata, idesign = design$idesign, type = .args$type, ... ) } .args$model <- design$model } attr(res.anova, "args") <- .args res.anova } # R stats aov stats_aov <- function(.args){ if(has_model(.args)){ .model <- get_anova_model(.args) res.anova <- stats::aov(.model) } else{ aov.formula <- create_aov_formula(.args) data <- .args$data res.anova <- .model <- stats::aov(aov.formula, data) } .args$model <- .model attr(res.anova, "args") <- .args res.anova } create_aov_formula <- function(.args){ between <- paste(.args$between, collapse = "*") within <- paste(.args$within, collapse = "*") covariate <- paste(.args$covariate, collapse = "+") error <- ifelse( within != "", error <- paste0("+Error(", .args$wid, "/(", within, "))"), "" ) bw.sep <- ifelse(between != "" & within != "", "*", "") # Between and Within vars separator bc.sep <- ifelse(covariate != "", "+", "") # Between and covariate vars separator .formula <- paste0(.args$dv, " ~ ", covariate, bc.sep, between, bw.sep, within, error) %>% stats::as.formula() .formula } # Check assumptions (Not used helpers)------------------------- check_anova_assumptions <- function(data, dv, between){ . <- NULL outliers <- data %>% group_by(!!!syms(between)) %>% identify_outliers(!!dv) groups.normality <- data %>% group_by(!!!syms(between)) %>% shapiro_test(vars = dv) formula <- paste(between, collapse = "*") %>% paste(dv, ., sep = " ~ ") %>% stats::as.formula() model <- stats::lm(formula, data) .residuals <- stats::residuals(model) variance.homogeneity <- levene_test(data, formula) arguments <- list( dv = dv, between = between) results <- list( outliers = outliers, residuals.normality = shapiro_test(.residuals), groups.normality = groups.normality, variance.homogeneity = variance.homogeneity ) %>% set_attrs(arguments = arguments) results } check_repeated_anova_assumptions <- function(data, dv, wid, within){ . <- NULL results <- check_anova_assumptions(data, dv, within) results$variance.homogeneity <- NULL arguments <- list( dv = dv, wid = wid, within = within) results <- results %>% set_attrs(arguments = arguments) within <- paste(within, collapse = ", ") %>% paste0("c(", ., ")") data.name <- deparse(substitute(data)) anova.formula <- paste0( "anova_test(", data.name, ", dv = ", dv, ", wid = ", wid, ", within = ", within, ")" ) res.anova <- eval(parse(text = anova.formula)) results <- results %>% .add_item(sphericity = res.anova$`Mauchly's Test for Sphericity`) results } check_mixed_anova_assumptions <- function(data, dv, wid , between, within){ . <- NULL arguments <- list( dv = dv, wid = wid, between = between, within = within) grouping <- c(between, within) outliers <- data %>% group_by(!!!syms(grouping)) %>% identify_outliers(!!dv) groups.normality <- data %>% group_by(!!!syms(grouping)) %>% shapiro_test(vars = dv) formula <- paste(between, collapse = "*") %>% paste(dv, ., sep = " ~ ") %>% stats::as.formula() variance.homogeneity <- data %>% group_by(!!!syms(within)) %>% levene_test(formula) results <- list( outliers = outliers, groups.normality = groups.normality, variance.homogeneity = variance.homogeneity ) %>% set_attrs(arguments = arguments) within <- paste(within, collapse = ", ") %>% paste0("c(", ., ")") between <- paste(between, collapse = ", ") %>% paste0("c(", ., ")") data.name <- deparse(substitute(data)) anova.formula <- paste0( "anova_test(", data.name, ", dv = ", dv, ", wid = ", wid, ", within = ", within, ", between = ", between, ")" ) res.anova <- eval(parse(text = anova.formula)) results <- results %>% .add_item(sphericity = res.anova$`Mauchly's Test for Sphericity`) results } rstatix/R/kruskal_test.R0000644000176200001440000000411113570014763015012 0ustar liggesusers#' @include utilities.R NULL #'Kruskal-Wallis Test #' #' #'@description Provides a pipe-friendly framework to perform Kruskal-Wallis #' rank sum test. Wrapper around the function #' \code{\link[stats]{kruskal.test}()}. #'@param data a data.frame containing the variables in the formula. #'@param formula a formula of the form \code{x ~ group} where \code{x} is a #' numeric variable giving the data values and \code{group} is a factor with #' one or multiple levels giving the corresponding groups. For example, #' \code{formula = TP53 ~ cancer_group}. #'@param ... other arguments to be passed to the function #' \code{\link[stats]{kruskal.test}}. #' #'@return return a data frame with the following columns: \itemize{ \item #' \code{.y.}: the y variable used in the test. \item \code{n}: sample count. #' \item \code{statistic}: the kruskal-wallis rank sum statistic used to #' compute the p-value. \item \code{p}: p-value. \item \code{method}: the #' statistical test used to compare groups.} #' @examples #' # Load data #' #::::::::::::::::::::::::::::::::::::::: #' data("ToothGrowth") #' df <- ToothGrowth #' #' # Kruskal-wallis rank sum test #' #::::::::::::::::::::::::::::::::::::::::: #' df %>% kruskal_test(len ~ dose) #' #' # Grouped data #' df %>% #' group_by(supp) %>% #' kruskal_test(len ~ dose) #'@name kruskal_test #'@export kruskal_test <- function(data, formula, ...){ args <- c(as.list(environment()), list(...)) %>% .add_item(method = "kruskal_test") if(is_grouped_df(data)){ results <- data %>% doo(.kruskal_test, formula, ...) } else{ results <- .kruskal_test(data, formula, ...) } results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "kruskal_test")) } .kruskal_test <- function(data, formula, ...) { outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) term <- statistic <- p <- df <- method <- NULL stats::kruskal.test(formula, data = data, ...) %>% as_tidy_stat() %>% select(statistic, df, p, method) %>% add_column(.y. = outcome, n = nrow(data), .before = "statistic") } rstatix/R/cor_mat.R0000644000176200001440000000636213470305605013732 0ustar liggesusers#' @include utilities.R as_cor_mat.R NULL #'Compute Correlation Matrix with P-values #'@description Compute correlation matrix with p-values. Numeric columns in the #' data are detected and automatically selected for the analysis. You can also #' specify variables of interest to be used in the correlation analysis. #'@inheritParams cor_test #'@param x an object of class \code{cor_mat} #'@param vars a character vector containing the variable names of interest. #'@param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to select a variable of interest. #'@return a data frame #'@seealso \code{\link{cor_test}()}, \code{\link{cor_reorder}()}, #' \code{\link{cor_gather}()}, \code{\link{cor_select}()}, #' \code{\link{cor_as_symbols}()}, \code{\link{pull_triangle}()}, #' \code{\link{replace_triangle}()} #' @examples #' # Data preparation #' #::::::::::::::::::::::::::::::::::::::::::: #' mydata <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) #' head(mydata, 3) #' #' # Compute correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' # Correlation matrix between all variables #' cor.mat <- mydata %>% cor_mat() #' cor.mat #' #' # Specify some variables of interest #' mydata %>% cor_mat(mpg, hp, wt) #' #' # Or remove some variables in the data #' # before the analysis #' mydata %>% cor_mat(-mpg, -hp) #' #' # Significance levels #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat %>% cor_get_pval() #' #' #' # Visualize #' #:::::::::::::::::::::::::::::::::::::::::: #' # Insignificant correlations are marked by crosses #' cor.mat %>% #' cor_reorder() %>% #' pull_lower_triangle() %>% #' cor_plot(label = TRUE) #' #' # Gather/collapse correlation matrix into long format #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat %>% cor_gather() #' #' #'@describeIn cor_mat compute correlation matrix with p-values. Returns a data #' frame containing the matrix of the correlation coefficients. The output has #' an attribute named "pvalue", which contains the matrix of the correlation #' test p-values. #'@export cor_mat <- function(data, ..., vars = NULL, method = "pearson", alternative = "two.sided", conf.level = 0.95){ vars <- data %>% get_selected_vars(..., vars = vars) n.vars <- length(vars) if(n.vars > 1 & n.vars <= 2){ stop("At least, 3 variables are required for a correlation matrix. ", "Use the function cor_test() for 2 or less variables.", call. = FALSE) } cor_test( data, vars = vars, method = method, alternative = alternative, conf.level = conf.level ) %>% as_cor_mat() } #' @describeIn cor_mat compute the correlation matrix but returns only the p-values of the tests. #' @export cor_pmat <- function(data, ..., vars = NULL, method = "pearson", alternative = "two.sided", conf.level = 0.95){ cor_mat( data = data, ..., vars = vars, method = method, alternative = alternative, conf.level = conf.level ) %>% cor_get_pval() } #' @describeIn cor_mat extract a correlation matrix p-values from an object of #' class \code{cor_mat()}. #' @export cor_get_pval <- function(x){ res <- x %>% attr("pvalue") if(is.null(res)) warning("Can't find p-value attributes.", call.= FALSE) res } rstatix/R/counts_to_cases.R0000644000176200001440000000212613544500207015470 0ustar liggesusers#' Convert a Table of Counts into a Data Frame of cases #' @description converts a contingency table or a data frame of counts into a #' data frame of individual observations. #' @param x a contingency table or a data frame #' @param count.col the name of the column containing the counts. Default is "Freq". #' @return a data frame of cases #' #' @examples #' # Create a cross-tabulation demo data #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' xtab <- as.table( #' rbind(c(20, 5), c(16,9)) #' ) #' dimnames(xtab) <- list( #' before = c("non.smoker", "smoker"), #' after = c("non.smoker", "smoker") #' ) #' xtab #' #' # Convert into a data frame of cases #' #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #' df <- counts_to_cases(xtab) #' head(df) #' #' @export counts_to_cases <- function(x, count.col = "Freq") { if(!inherits(x, "table")) x <- as.table(as.matrix(x)) x <- as.data.frame(x) # Get the row indices to pull from x idx <- rep.int(seq_len(nrow(x)), x[[count.col]]) # Drop count column x[[count.col]] <- NULL # Get the rows from x x <- x[idx, ] rownames(x) <- 1:nrow(x) x } rstatix/R/dunn_test.R0000644000176200001440000001212513677303341014307 0ustar liggesusers#' @include utilities.R t_test.R NULL #'Dunn's Test of Multiple Comparisons #' #'@description Performs Dunn's test for pairwise multiple comparisons of the #' ranked data. The mean rank of the different groups is compared. Used for #' post-hoc test following Kruskal-Wallis test. #'@inheritParams t_test #'@return return a data frame with some of the following columns: \itemize{ #' \item \code{.y.}: the y (outcome) variable used in the test. \item #' \code{group1,group2}: the compared groups in the pairwise tests. \item #' \code{n1,n2}: Sample counts. \item \code{estimate}: mean ranks difference. #' \item \code{estimate1, estimate2}: show the mean rank values of #' the two groups, respectively. #' \item \code{statistic}: Test statistic (z-value) used to compute the #' p-value. \item \code{p}: p-value. \item \code{p.adj}: the adjusted p-value. #' \item \code{method}: the statistical test used to compare groups. \item #' \code{p.signif, p.adj.signif}: the significance level of p-values and #' adjusted p-values, respectively. } #' #' The \strong{returned object has an attribute called args}, which is a list #' holding the test arguments. #'@details DunnTest performs the post hoc pairwise multiple comparisons #' procedure appropriate to follow up a Kruskal-Wallis test, which is a #' non-parametric analog of the one-way ANOVA. The Wilcoxon rank sum test, #' itself a non-parametric analog of the unpaired t-test, is possibly #' intuitive, but inappropriate as a post hoc pairwise test, because (1) it #' fails to retain the dependent ranking that produced the Kruskal-Wallis test #' statistic, and (2) it does not incorporate the pooled variance estimate #' implied by the null hypothesis of the Kruskal-Wallis test. #' #'@references Dunn, O. J. (1964) Multiple comparisons using rank sums #' Technometrics, 6(3):241-252. #' @examples #' # Simple test #' ToothGrowth %>% dunn_test(len ~ dose) #' #' # Grouped data #' ToothGrowth %>% #' group_by(supp) %>% #' dunn_test(len ~ dose) #'@export dunn_test <- function(data, formula, p.adjust.method = "holm", detailed = FALSE){ args <- as.list(environment()) %>% .add_item(method = "dunn_test") if(is_grouped_df(data)){ results <- data %>% doo(.dunn_test, formula, p.adjust.method ) } else{ results <- .dunn_test(data, formula, p.adjust.method) } if(!detailed){ results <- results %>% select(-.data$method, -.data$estimate, -.data$estimate1, -.data$estimate2) } results %>% set_attrs(args = args) %>% add_class(c("rstatix_test", "dunn_test")) } .dunn_test <- function(data, formula, p.adjust.method = "holm"){ outcome <- get_formula_left_hand_side(formula) group <- get_formula_right_hand_side(formula) number.of.groups <- guess_number_of_groups(data, group) if(number.of.groups == 1){ stop("all observations are in the same group") } data <- data %>% select(!!!syms(c(outcome, group))) %>% get_complete_cases() %>% .as_factor(group) x <- data %>% pull(!!outcome) g <- data %>% pull(!!group) group.size <- data %>% get_group_size(group) if (!all(is.finite(g))) stop("all group levels must be finite") x.rank <- rank(x) mean.ranks <- tapply(x.rank, g, mean, na.rm=TRUE) grp.sizes <- tapply(x, g, length) n <- length(x) C <- get_ties(x.rank, n) compare.meanrank <- function(i, j){ mean.ranks[i] - mean.ranks[j] } compare.stats <- function(i,j) { dif <- mean.ranks[i] - mean.ranks[j] A <- n * (n+1) / 12 B <- (1 / grp.sizes[i] + 1 / grp.sizes[j]) zval <- dif / sqrt((A - C) * B) zval } compare.levels <- function(i, j) { dif <- abs(mean.ranks[i] - mean.ranks[j]) A <- n * (n+1) / 12 B <- (1 / grp.sizes[i] + 1 / grp.sizes[j]) zval <- dif / sqrt((A - C) * B) pval <- 2 * stats::pnorm(abs(zval), lower.tail = FALSE) pval } ESTIMATE <- stats::pairwise.table( compare.meanrank, levels(g), p.adjust.method = "none" ) %>% tidy_squared_matrix("diff") PSTAT <- stats::pairwise.table( compare.stats, levels(g), p.adjust.method = "none" ) %>% tidy_squared_matrix("statistic") PVAL <- stats::pairwise.table( compare.levels, levels(g), p.adjust.method = "none" ) %>% tidy_squared_matrix("p") %>% mutate(method = "Dunn Test", .y. = outcome) %>% adjust_pvalue(method = p.adjust.method) %>% add_significance("p.adj") %>% add_column(statistic = PSTAT$statistic, .before = "p") %>% add_column(estimate = ESTIMATE$diff, .before = "group1") %>% select(.data$.y., .data$group1, .data$group2, .data$estimate, everything()) n1 <- group.size[PVAL$group1] n2 <- group.size[PVAL$group2] mean.ranks1 <- mean.ranks[PVAL$group1] mean.ranks2 <- mean.ranks[PVAL$group2] PVAL %>% add_column(n1 = n1, n2 = n2, .after = "group2") %>% add_column(estimate1 = mean.ranks1, estimate2 = mean.ranks2, .after = "estimate") } get_ties <- function(x, n) { x.sorted <- sort(x) pos <- 1 tiesum <- 0 while (pos <= n) { val <- x.sorted[pos] nt <- length(x.sorted[x.sorted == val]) pos <- pos + nt if (nt > 1){ tiesum <- tiesum + nt^3 - nt } } tiesum / (12 * (n - 1)) } rstatix/R/cor_as_symbols.R0000644000176200001440000000326513470305570015324 0ustar liggesusers#' @include utilities.R NULL #' Replace Correlation Coefficients by Symbols #' #' @description Take a correlation matrix and replace the correlation coefficients by symbols according to the #' level of the correlation. #' @param x a correlation matrix. Particularly, an object of class \code{cor_mat}. #' @param cutpoints numeric vector used for intervals. Default values are #' \code{c(0, 0.25, 0.5, 0.75, 1)}. #' @param symbols character vector, one shorter than cutpoints, used as #' correlation coefficient symbols. Default values are \code{c(" ", ".", "+", #' "*")}. #' @seealso \code{\link{cor_mat}()} #' @examples #' # Compute correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_mat() #' #' # Replace correlation coefficient by symbols #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat %>% #' cor_as_symbols() %>% #' pull_lower_triangle() #' #' @name cor_as_symbols #' @export cor_as_symbols <- function( x, cutpoints = c(0, 0.25, 0.5, 0.75, 1), symbols = c(" ", ".", "+", "*")) { if(inherits(x, "cor_mat_tri")){ cor.mat <- x %>% replace_empty_by(0) %>% as_numeric_triangle() %>% as_matrix() } else{ cor.mat <- as_matrix(x) } res <- stats::symnum( abs(cor.mat), cutpoints = cutpoints, symbols = symbols, abbr.colnames = FALSE ) %>% structure(class = "matrix") %>% # overwrite "noquote" class matrix_to_dataframe() %>% add_class(c("data.frame", "tbl_df")) pvalue <- attr(x, "pvalue") if(!is.null(pvalue)){ res <- res %>% set_attrs(pvalue = pvalue) %>% add_class("cor_mat") } res } rstatix/R/shapiro_test.R0000644000176200001440000000623013570426724015013 0ustar liggesusers#' @include utilities.R #' @importFrom stats shapiro.test #' @importFrom stats complete.cases NULL #' Shapiro-Wilk Normality Test #' #' @description Provides a pipe-friendly framework to performs Shapiro-Wilk test #' of normality. Support grouped data and multiple variables for multivariate #' normality tests. Wrapper around the R base function #' \code{\link[stats]{shapiro.test}()}. Can handle grouped data. Read more: #' \href{https://www.datanovia.com/en/lessons/normality-test-in-r/}{Normality #' Test in R}. #' @param data a data frame. Columns are variables. #' @param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to select a variable of interest. #' @param vars optional character vector containing variable names. Ignored when #' dot vars are specified. #' @return a data frame containing the value of the Shapiro-Wilk statistic and #' the corresponding p.value. #' @examples #' #' # Shapiro Wilk normality test for one variable #' iris %>% shapiro_test(Sepal.Length) #' #' # Shapiro Wilk normality test for two variables #' iris %>% shapiro_test(Sepal.Length, Petal.Width) #' #' # Multivariate normality test #' mshapiro_test(iris[, 1:3]) #' #' @describeIn shapiro_test univariate Shapiro-Wilk normality test #' @export shapiro_test <- function(data, ..., vars = NULL){ if(is_grouped_df(data)){ results <- data %>% doo(shapiro_test, ..., vars = vars) return(results) } else if(is.numeric(data)){ results <- shapiro.test(data) data.name <- deparse(substitute(data)) results <- tidy(results) %>% add_column(variable = data.name, .before = 1) %>% select(-.data$method) return(results) } vars <- c(get_dot_vars(...), vars) %>% unique() n.vars <- length(vars) if(.is_empty(vars)){ stop("Specify one or more variables...") } data <- data %>% select(!!!syms(vars)) variable <- value <- method <- NULL data <- data %>% gather(key = "variable", value = "value") %>% filter(!is.na(value)) data %>% group_by(variable) %>% doo(~tidy(shapiro.test(.$value))) %>% select(-.data$method) %>% rename(p = .data$p.value) } #' @describeIn shapiro_test multivariate Shapiro-Wilk normality test. This is a #' modified copy of the \code{mshapiro.test()} function of the package #' mvnormtest, for internal convenience. #' @export mshapiro_test <- function(data) { if(is_grouped_df(data)){ results <- data %>% doo(~shapiro.test(.)) } x <- data if (!is.matrix(x)) { x <- as.matrix(x) } x <- x[complete.cases(x), ] x <- t(x) n <- ncol(x) if (n < 3 || n > 5000) { stop("sample size must be between 3 and 5000") } rng <- range(x) rng <- rng[2] - rng[1] if (rng == 0) { stop("all `x[]' are identical") } Us <- apply(x, 1, mean) R <- x - Us M.1 <- solve(R %*% t(R), tol = 1e-18) Rmax <- diag(t(R) %*% M.1 %*% R) C <- M.1 %*% R[, which.max(Rmax)] Z <- t(C) %*% x result <- shapiro.test(Z) result$method <- "Multivariate Shapiro-Wilk normality test" result$data.name <- paste("(", paste(rownames(x), collapse = ","), ")", sep = "") tidy(result) %>% select(-.data$method) } rstatix/R/freq_table.R0000644000176200001440000000266613651776420014425 0ustar liggesusers#' @include utilities.R NULL #'Compute Frequency Table #'@description compute frequency table. #'@param data a data frame #'@param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to specify variables of interest. #'@param vars optional character vector containing variable names. #'@param na.rm logical value. If TRUE (default), remove missing values in the #' variables used to create the frequency table. #'@return a data frame #' @examples #' data("ToothGrowth") #' ToothGrowth %>% freq_table(supp, dose) #'@export freq_table <- function(data, ..., vars = NULL, na.rm = TRUE){ if(is.vector(data) | is.factor(data)){ data <- data.frame(group = data) vars <- "group" } data <- data %>% df_select(..., vars = vars) vars <- colnames(data) if(length(vars) == 0){ stop("Specify at least one variable") } if(na.rm){ data <- data %>% filter(stats::complete.cases(data)) } results <- data %>% group_by(!!!syms(vars)) %>% summarise(n = n()) %>% mutate(prop = round(.data$n *100 / sum (.data$n), 1)) %>% dplyr::ungroup() results } spread_table <- function(data, vars){ last.var <- dplyr::last(vars) grouping.vars <- utils::head(vars, -2) if(length(vars) >= 2){ data <- data %>% select(-.data$prop) %>% group_by(!!!syms(grouping.vars)) %>% nest() %>% mutate(data = map(.data$data, spread, key = last.var, value = "n")) } data } rstatix/R/p_value.R0000644000176200001440000002142313710025405013726 0ustar liggesusers#' @include utilities.R NULL #'Rounding and Formatting p-values #' #'@description Round and format p-values. Can also mark significant p-values with stars. #'@param x a numeric vector of p-values or a data frame containing a p value #' column. If data frame, the p-value column(s) will be automatically detected. #' Known p-value column names can be obtained using the functions #' \code{p_names()} and \code{p_adj_names()} #'@param digits the number of significant digits to be used. #'@param accuracy number to round to, that is the threshold value above wich the #' function will replace the pvalue by "<0.0xxx". #'@param decimal.mark the character to be used to indicate the numeric decimal #' point. #'@param leading.zero logical. If FALSE, remove the leading zero. #'@param trailing.zero logical. If FALSE (default), remove the training extra #' zero. #'@param space logical. If TRUE (default) use space as separator between #' different elements and symbols. #'@param cutpoints numeric vector used for intervals #'@param symbols character vector, one shorter than cutpoints, used as #' significance symbols. #'@param add.p logical value. If TRUE, add "p=" before the value. #'@param ... column names to manipulate in the case where \code{x} is a data #' frame. P value columns are automatically detected if not specified. #'@param new.col logical, used only when \code{x} is a data frame. If TRUE, add #' a new column to hold the results. The new column name is created by adding, #' to the p column, the suffix "format" (for \code{p_format()}), "signif" (for #' \code{p_mak_significant()}). #'@return a vector or a data frame containing the rounded/formatted p-values. #' @examples #' #' # Round and format a vector of p-values #' # :::::::::::::::::::::::::::::::::::::::::::: #' # Format #' p <- c(0.5678, 0.127, 0.045, 0.011, 0.009, 0.00002, NA) #' p_format(p) #' #'# Specify the accuracy #' p_format(p, accuracy = 0.01) #' #' # Add p and remove the leading zero #' p_format(p, add.p = TRUE, leading.zero = FALSE) #' #' # Remove space before and after "=" or "<". #' p_format(p, add.p = TRUE, leading.zero = FALSE, space = FALSE) #' #' # Mark significant p-values #' # :::::::::::::::::::::::::::::::::::::::::::: #' p_mark_significant(p) #' #' # Round, the mark significant #' p %>% p_round(digits = 2) %>% p_mark_significant() #' #' # Format, then mark significant #' p %>% p_format(digits = 2) %>% p_mark_significant() #' #' # Perform stat test, format p and mark significant #' # :::::::::::::::::::::::::::::::::::::::::::: #' ToothGrowth %>% #' group_by(dose) %>% #' t_test(len ~ supp) %>% #' p_format(digits = 2, leading.zero = FALSE) %>% #' p_mark_significant() #' #'@describeIn p_value round p-values #'@export p_round <- function(x, ..., digits = 3){ if(is.numeric(x)){ round_value(x, digits = digits) } else if(is.data.frame(x)){ p_round_at(x, ..., digits = digits) } else{ stop("x should be a numeric vector or a data frame") } } #' @describeIn p_value format p-values. Add a symbol "<" for small p-values. #' @export p_format <- function(x, ..., new.col = FALSE, digits = 2, accuracy = 0.0001, decimal.mark = ".", leading.zero = TRUE, trailing.zero = FALSE, add.p = FALSE, space = FALSE){ if(is.data.frame(x)){ .attributes <- attributes(x) res <- x %>% keep_only_tbl_df_classes() %>% p_format_at( ..., new.col = new.col, digits = digits, accuracy = accuracy, decimal.mark = decimal.mark, leading.zero = leading.zero, trailing.zero = trailing.zero, add.p = add.p, space = space ) .attributes$names <- colnames(res) attributes(res) <- .attributes return(res) } res <- format.pval( pv = x, digits = digits, eps = accuracy, # nsmall = how much tails 0 to keep if digits of # original value < to digits defined nsmall = 0 ) res <- gsub(pattern = " ", replacement = "", res, fixed = TRUE) res <- gsub("<1e-04", "<0.0001", res) if(!leading.zero) res <- remove_leading_zero(res) if(!trailing.zero) res <- remove_trailing_zero(res) if(!missing(decimal.mark)) res <- gsub("\\.", decimal.mark, res) if(add.p){ contain.inf.symbol <- grepl("<", res) res2 <- paste0("p", "=", res) if(sum(contain.inf.symbol) > 0){ # no need to add = res2[contain.inf.symbol] <- paste0("p", res[contain.inf.symbol]) } res <- res2 } if(space){ if(add.p) res <- gsub(pattern = "(=|<)", replacement = " \\1 ", x = res) else res <- gsub(pattern = "(=|<)", replacement = "\\1 ", x = res) } res } #' @describeIn p_value mark p-values with significance levels #' @export p_mark_significant <- function(x, ..., new.col = FALSE, cutpoints = c(0, 1e-04, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "")){ if(is.data.frame(x)){ .attributes <- attributes(x) res <- x %>% keep_only_tbl_df_classes() %>% p_mark_significant_at( ..., new.col = new.col, cutpoints = cutpoints, symbols = symbols ) attributes(res) <- .attributes return(res) } contains.leading.zero <- TRUE is.char.x <- is.character(x) if(is.char.x){ contains.leading.zero <- p_contains_leading_zero(x) leading.character <- replace_number(x, "") leading.character <- gsub("NA", "", leading.character) x <- extract_number(x) } x <- tibble(p = x) %>% add_significance("p", cutpoints = cutpoints, symbols = symbols) %>% mutate(.signif = paste0(.data$p, .data$p.signif)) %>% pull(".signif") if(!contains.leading.zero) x <- remove_leading_zero(x) if(is.char.x) x <- paste(leading.character, x, sep = "") x <- gsub("NA?", "NA", x, fixed = TRUE) x <- gsub("<1e-04", "<0.0001", x, fixed = TRUE) x } #' @describeIn p_value detects and returns p-value column names in a data frame. #' @param data a data frame #' @param type the type of p-value to detect. Can be one of \code{c("all", "p", "p.adj")}. #' @export p_detect <- function(data, type = c("all", "p", "p.adj")){ type <- match.arg(type) p.cols <- switch (type, all = c(p_adj_names(), p_names()), p = p_names(), p.adj = p_adj_names() ) existing.p.cols <- intersect(p.cols, colnames(data)) if(.is_empty(existing.p.cols)) existing.p.cols <- NULL existing.p.cols } #' @describeIn p_value returns known p-value column names #' @export p_names <- function(){ c("p", "pval", "pvalue", "p.val", "p.value") } #' @describeIn p_value returns known adjust p-value column names #' @export p_adj_names <- function(){ p_names() %>% paste0(".adj") } # Rounding specified columns p_round_at <- function(data, ..., digits = 3){ p.cols <- p_select(data, ...) if(!is.null(p.cols)){ data %<>% dplyr::mutate_at(rlang::quos(p.cols), round_value, digits = digits) } data } # Formatting (specified) p value columns p_format_at <- function(data, ..., new.col = FALSE, digits = 2, accuracy = 0.0001, decimal.mark = ".", leading.zero = TRUE, trailing.zero = FALSE, add.p = FALSE, space = TRUE){ mutate_func <- dplyr::mutate_at if(new.col) mutate_func <- dplyr::transmute_at results <- data p.cols <- p_select(data, ...) if(!is.null(p.cols)){ results <- results %>% mutate_func( p.cols, p_format, digits = digits, accuracy = accuracy, decimal.mark = decimal.mark, leading.zero = leading.zero, trailing.zero = trailing.zero, add.p = add.p, space = space ) if(new.col){ colnames(results) <- paste0(p.cols, ".format") results <- dplyr::bind_cols(data, results) } } results } # Mark significant at a specified column p_mark_significant_at <- function(data, ..., new.col = FALSE, cutpoints = c(0, 1e-04, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "")){ mutate_func <- dplyr::mutate_at if(new.col) mutate_func <- dplyr::transmute_at results <- data p.cols <- p_select(data, ...) if(!is.null(p.cols)){ results %<>% mutate_func( p.cols, p_mark_significant, cutpoints = cutpoints, symbols = symbols ) if(new.col){ colnames(results) <- paste0(p.cols, ".signif") results <- dplyr::bind_cols(data, results) } } results } # Manipulating leading zero----------------------------- # Check if formatted p-values contain leading zero p_contains_leading_zero <- function(p){ any(grepl(pattern = "0.", p, fixed = TRUE)) } remove_leading_zero <- function(x){ sapply(x, function(x){ sub("^([-|<|=|>]?)0[.]", "\\1.", x)}) %>% as.character() } remove_trailing_zero <- function(x){ gsub("0+$", "", x) } # Select p-value columns: p and p.adj ----------------------- p_select <- function(data, ...){ p.col <- get_existing_dot_vars(data, ...) if(is.null(p.col) | .is_empty(p.col)){ p.col <- p_detect(data, type = "all") } p.col } rstatix/R/cor_select.R0000644000176200001440000000256313470305647014435 0ustar liggesusers#' @include utilities.R NULL #' Subset Correlation Matrix #' @name cor_select #' @param x a correlation matrix. Particularly, an object of class \code{cor_mat}. #' @param vars a character vector containing the variable names of interest. #' @param ... One or more unquoted expressions (or variable names) separated by #' commas. Used to select variables of interest. #'@return a data frame #'@seealso \code{\link{cor_mat}()}, \code{\link{pull_triangle}()}, \code{\link{replace_triangle}()} #' @examples #' # Compute correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_mat() #' #' # Subsetting correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' #' # Select some variables of interest #' cor.mat %>% #' cor_select(mpg, drat, wt) #' #' # Remove variables #' cor.mat %>% #' cor_select(-mpg, -wt) #' #' @export cor_select <- function(x, ..., vars = NULL){ vars <- x %>% get_selected_vars(..., vars = vars) %>% setdiff("rowname") if(!.is_empty(vars)){ # Filter the correlation matrix vars <- unique(vars) x <- x %>% subset_matrix(vars) # Filter p-values pvalue <- x %>% attr("pvalue") if(!is.null(pvalue)){ pvalue <- pvalue %>% subset_matrix(vars) x <- x %>% set_attrs(pvalue = pvalue) %>% add_class("cor_mat") } } x } rstatix/R/cor_plot.R0000644000176200001440000001340013621171523014114 0ustar liggesusers#' @include utilities.R NULL #' Visualize Correlation Matrix Using Base Plot #' @description Provide a tibble-friendly framework to visualize a correlation #' matrix. Wrapper around the R base function #' \code{\link[corrplot]{corrplot}()}. Compared to #' \code{\link[corrplot]{corrplot}()}, it can handle directly the output of the #' functions \code{\link{cor_mat}() (in rstatix)}, \code{rcorr() (in Hmisc)}, #' \code{correlate() (in corrr)} and \code{cor() (in stats)}. #' #' The p-values contained in the outputs of the functions #' \code{\link{cor_mat}()} and \code{rcorr()} are automatically detected and #' used in the visualization. #' @inheritParams corrplot::corrplot #' @param cor.mat the correlation matrix to visualize #' @param palette character vector containing the color palette. #' @param p.mat matrix of p-value corresponding to the correlation matrix. #' @param significant.level significant level, if the p-value is bigger than #' \code{significant.level}, then the corresponding correlation coefficient is #' regarded as insignificant. #' @param insignificant character, specialized insignificant correlation #' coefficients, "cross" (default), "blank". If "blank", wipe away the #' corresponding glyphs; if "cross", add crosses (X) on corresponding glyphs. #' @param label logical value. If TRUE, shows the correlation coefficient #' labels. #' @param font.label a list with one or more of the following elements: size #' (e.g., 1), color (e.g., "black") and style (e.g., "bold"). Used to #' customize the correlation coefficient labels. For example \code{font.label #' = list(size = 1, color = "black", style = "bold")}. #' @seealso \code{\link{cor_as_symbols}()} #' @examples #' # Compute correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.mat <- mtcars %>% #' select(mpg, disp, hp, drat, wt, qsec) %>% #' cor_mat() #' #' # Visualize correlation matrix #' #:::::::::::::::::::::::::::::::::::::::::: #' # Full correlation matrix, #' # insignificant correlations are marked by crosses #' cor.mat %>% cor_plot() #' #' # Reorder by correlation coefficient #' # pull lower triangle and visualize #' cor.lower.tri <- cor.mat %>% #' cor_reorder() %>% #' pull_lower_triangle() #' cor.lower.tri %>% cor_plot() #' #' # Change visualization methods #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.lower.tri %>% #' cor_plot(method = "pie") #' #' cor.lower.tri %>% #' cor_plot(method = "color") #' #' cor.lower.tri %>% #' cor_plot(method = "number") #' #' # Show the correlation coefficient: label = TRUE #' # Blank the insignificant correlation #' #:::::::::::::::::::::::::::::::::::::::::: #' cor.lower.tri %>% #' cor_plot( #' method = "color", #' label = TRUE, #' insignificant = "blank" #' ) #' #' # Change the color palettes #' #:::::::::::::::::::::::::::::::::::::::::: #' #' # Using custom color palette #' # Require ggpubr: install.packages("ggpubr") #' if(require("ggpubr")){ #' my.palette <- get_palette(c("red", "white", "blue"), 200) #' cor.lower.tri %>% #' cor_plot(palette = my.palette) #' } #' #' # Using RcolorBrewer color palette #' if(require("ggpubr")){ #' my.palette <- get_palette("PuOr", 200) #' cor.lower.tri %>% #' cor_plot(palette = my.palette) #' } #' #' @export cor_plot <- function(cor.mat, method = "circle", type = "full", palette = NULL, p.mat = NULL, significant.level = 0.05, insignificant = c("cross", "blank"), label = FALSE, font.label = list()) { insignificant <- match.arg(insignificant) if(insignificant == "cross") insignificant <- "pch" # Outline color of circle, ellipse, .... outline <- ifelse(method == "color", "white", FALSE) # Correlation coefficients label parameters font <- parse_font(font.label) addCoef.col <- NULL if(label) addCoef.col <- font$color # Correlation matrix data show.diagonal <- TRUE if(inherits(cor.mat, "cor_mat")){ cor.value <- cor.mat %>% as_matrix() p.mat <- cor.mat %>% cor_get_pval() %>% as_matrix() } else if(inherits(cor.mat, "cor_mat_tri")){ cor.value <- cor.mat %>% as_numeric_triangle() %>% as_matrix() p.mat <- cor.mat %>% cor_get_pval() %>% as_numeric_triangle() %>% as_matrix() if(inherits(cor.mat, "lower_tri")) type <- "lower" else type <- "upper" cor.diagonal <- diag(cor.value) cor.diagonal.is.na <- all(is.na(cor.diagonal)) if(cor.diagonal.is.na) show.diagonal <- FALSE else show.diagonal <- TRUE } else if(inherits(cor.mat, "rcorr")){ cor.value <- cor.mat$r p.mat <- cor.mat$P } else { cor.value <- cor.mat %>% as_matrix() } # Correlation matrix p-value if(inherits(p.mat, "tbl_df")) p.mat <- p.mat %>% as_matrix() corrplot <- corrplot::corrplot corrplot( cor.value, method = method, type = type, tl.col="black", tl.srt = 45, col = palette, diag = show.diagonal, p.mat = p.mat, sig.level = significant.level, insig = insignificant, pch.cex = 2, outline = outline, addCoef.col = addCoef.col, number.cex = font$size, number.font = font$style ) } # Parse label font parse_font <- function(font){ if(.is_empty(font)){ font <- list(size = 1, color = "black", style = "plain") } else if(!is.list(font)){ stop("The argument font should be a list. ", "Example: font <- list(size = 1, color = 'black', style = 2)") } else{ font$size <- ifelse(is.null(font$size), 1, font$size) font$color <- ifelse(is.null(font$color), "black", font$color) font$style <- ifelse(is.null(font$style), "plain", font$style) } # convert fon style to numeric available.styles <- c(1, 2, 3, 4) %>% rlang::set_names(c("plain", "bold", "italic", "bold.italic")) font$style <- available.styles[font$style] font } rstatix/NEWS.md0000644000176200001440000002554014011723677013064 0ustar liggesusers# rstatix 0.7.0 ## New features - New function to extract information from rstatix statistical tests: - `get_n()` to extract sample count (n) from statistical test results. - `get_description` to extract stat test description or name - `remove_ns()` to remove non-significant rows. ## Major changes - Rewritting `add_x_position()` to better support different situations (#73). - Now, the output of the function `dunn_test()` include `estimate1` and `estimate2` when the argument `detailed = TRUE` is specified. The `estimate1` and `estimate2` values represent the mean rank values of the two groups being compared, respectively (#59). ## Minor changes - `cor_spread()` doc updated, error is explicitly shown if the input data doesn't contain the columns "var1", "var2" and "cor" (#95) - Maintenance updates of the functions `emmeans_test()` and `levene_test()` to adapt to broom release 0.7.4 (#89) - The documentation of the function `anova_test()` is updated to explain the internal contrast setting (#74). - Now, `p_mark_significance()` works when all p-values are NA. Empty character ("") is returned for NA (#64). - Classes (`rstatix` and `grouped_anova_test`) added to grouped ANOVA test (#61) - New argument `scales` added in the function `get_y_position()`. If the specified value is "free" or "free_y", then the step increase of y positions will be calculated by plot panels. Note that, using "free" or "free_y" gives the same result. A global step increase is computed when scales = "fixed" (#56). ## Bug fixes - The function `anova_test()` computes now repeated measures ANOVA without error when unused columns are present in the input data frame (#55) # rstatix 0.6.0 ## Minor changes - Adapted to upcoming broom v0.7.0 release (#49) - New argument `stack` added in `get_y_position()` to compute p-values y position for stacked bar plots ([#48](https://github.com/kassambara/rstatix/issues/48)). - `wilcox_test()`: Now, if `detailed = TRUE`, an estimate of the location parameter (Only present if argument detailed = TRUE). This corresponds to the pseudomedian (for one-sample case) or to the difference of the location parameter (for two-samples case) ([#45](https://github.com/kassambara/rstatix/issues/45)). ## Bug fixes - `anova_test()` function: Changing R default contrast setting (`contr.treatment`) into orthogonal contrasts (`contr.sum`) to have comparable results to SPSS when users define the model using formula (@benediktclaus, [#40](https://github.com/kassambara/rstatix/issues/40)). - Now, the option `type = "quantile"` of `get_summary_stats()` works properly (@Boyoron, [#39](https://github.com/kassambara/rstatix/issues/39)). # rstatix 0.5.0 ## New features - New functions added for easy data frame manipulation. These functions are internally used in the `rstatix` and the `ggpubr` package and makes it easy to program with tidyverse packages using non standard evaluation. - df_select - df_arrange - df_group_by - df_nest_by - df_split_by - df_unite - df_get_var_names - df_label_both - df_label_value ## Minor changes - Now, in `freq_table()` the option `na.rm` removes only missing values in the variables used to create the frequency table (@JuhlinF, [#25](https://github.com/kassambara/rstatix/issues/25)). - Missing values are now correctly handled in `anova_test()` (@benediktclaus, [#31](https://github.com/kassambara/rstatix/issues/31)) - Maintenance for adapting to the future dplyr 1.0.0 version [#32](https://github.com/kassambara/rstatix/issues/32) ## Bug fixes - An informative message is now displayed when users try to apply Hedge's correction when computing the Cohen's D for one sample test (@GegznaV, [#36](https://github.com/kassambara/rstatix/issues/36)). - Bug fixes in the `games_howell_test()` function : the t-statistic is now calculated using the **absolute** mean difference between groups (@GegznaV, [#37](https://github.com/kassambara/rstatix/issues/37)). - x position is now correctly computed when when making custom comparisons (@barrel0luck, [#28](https://github.com/kassambara/rstatix/issues/28)). # rstatix 0.4.0 ## New features - The `cohens_d()` function now supports Hedge's correction. New argument `hedge.correction` added . logical indicating whether apply the Hedges correction by multiplying the usual value of Cohen's d by `(N-3)/(N-2.25)` (for unpaired t-test) and by `(n1-2)/(n1-1.25)` for paired t-test; where N is the total size of the two groups being compared (N = n1 + n2) (@IndrajeetPatil, [#9](https://github.com/kassambara/rstatix/issues/9)). ## Minor changes - Now, the function `cohens_d()` outputs values with directionality. The absolute value is no longer returned. It can now be positive or negative depending on the data (@narunpat, [#9](https://github.com/kassambara/rstatix/issues/13)). ## Bug fixes - The value of `mu` is now considered when calculating `cohens_d()` for one sample t-test (@mllewis, [#22](https://github.com/kassambara/rstatix/issues/22)). - The function `tukey_hsd()` now handles situation where minus `-` symbols are present in factor levels (@IndrajeetPatil, [#19](https://github.com/kassambara/rstatix/issues/19)). # rstatix 0.3.1 ## Minor changes - tidyr > 1.0.0 now required - know, `identify_outliers` returns a basic data frame instead of tibble when nrow = 0 (for nice printing) - new argument `detailed` added in `dunn_test()`. If TRUE, then estimate and method columns are shown in the results. # rstatix 0.3.0 ## New features - `prop_test()`, `pairwise_prop_test()` and `row_wise_prop_test()`. Performs one-sample and two-samples z-test of proportions. Wrappers around the R base function `prop.test()` but have the advantage of performing pairwise and row-wise z-test of two proportions, the post-hoc tests following a significant chi-square test of homogeneity for 2xc and rx2 contingency tables. - `fisher_test()`, `pairwise_fisher_test()` and `row_wise_fisher_test()`: Fisher's exact test for count data. Wrappers around the R base function `fisher.test()` but have the advantage of performing pairwise and row-wise fisher tests, the post-hoc tests following a significant chi-square test of homogeneity for 2xc and rx2 contingency tables. - `chisq_test()`, `pairwise_chisq_gof_test()`, `pairwise_chisq_test_against_p()` : Chi-square test for count data. - `binom_test()`, `pairwise_binom_test()`, `pairwise_binom_test_against_p()` and `multinom_test()`: performs exact binomial and multinomial tests. Alternative to the chi-square test of goodness-of-fit-test when the sample. - `counts_to_cases()`: converts a contingency table or a data frame of counts into a data frame of individual observations. - New functions `mcnemar_test()` and `cochran_qtest()` for comparing two ore more related proportions. - `prop_trend_test()`: Performs chi-squared test for trend in proportion. This test is also known as Cochran-Armitage trend test. ## Minor changes - Now `get_test_label()` and `get_pwc_label()` return expression by default - Unit testing and spelling check added - Code rewritten to adapt tidyr 1.0.0 # rstatix 0.2.0 ## Minor changes - `get_anova_table()` supports now an object of class `grouped_anova_test` - ANOVA table is now correctly returned when `correction = "none"` for repeated measures ANOVA - `NAs` are now automatically removed before quantile computation for identifying outliers (@IndrajeetPatil, [#10](https://github.com/kassambara/rstatix/issues/10)). - Unquoted factor variable name is now supported in factor manipulation functions: `set_ref_level()`, `reorder_levels()` and `make_valid_levels()` - New argument `model` added in the function `emmeans_test()` - Adapting to tidyr v1.0.0 (@jennybc, [#6](https://github.com/kassambara/rstatix/issues/6)) ## New features - New function `welch_anova_test()`: Welch one-Way ANOVA test. A wrapper around the base function `stats::oneway.test()`. This is is an alternative to the standard one-way ANOVA in the situation where the homogeneity of variance assumption is violated. - New function `friedman_effsize()`, computes the effect size of Friedman test using the Kendall's W value. - New function `friedman_test()`, provides a pipe-friendly framework to perform a Friedman rank sum test, which is the non-parametric alternative to the one-way repeated measures ANOVA test. - New function `games_howell_test()`: Performs Games-Howell test, which is used to compare all possible combinations of group differences when the assumption of homogeneity of variances is violated. - New function `kruskal_effsize()` for computing effect size for Kruskal-Wallis test. - New functions added to round and format p-values: `p_round(), p_format(), p_mark_significant()`. - New function `wilcox_effsize()` added for computing effect size (r) for wilcoxon test. - New function `get_anova_table()` added to extract ANOVA table from `anova_test()` results. Can apply sphericity correction automatically in the case of within-subject (repeated measures) designs. - New functions added to extract information from statistical tests: `get_anova_label()` - New function `emmeans_test()` added for pairwise comparisons of estimated marginal means. ## Minor changes - the unnecessary column `comparison` removed from `tukey_hsd()` results (breaking change). - New column `n` (sample count) added to statistical tests results: `t_test()`, `wilcox_test()`, `sign_test()`, `dunn_test()` and `kruskal_test()` (@ShixiangWang, [#4](https://github.com/kassambara/rstatix/issues/4)). - `rstatix_test` class added to `anova_test()` results - the results of `kruskal_test()` is now an object of class `rstatix_test` that has an attribute named **args** for holding the test arguments. - In `get_y_position()`, y positions and test data are merged now for grouped plots. - New argument `y.trans` added in `get_y_position()` for y scale transformation. - significance column added in `tukey_hsd()` results. - `adjust_pvalue()` now supports grouped data ## Bug fixes - `detailed` arguments correctly propagated when grouped stats are performed # rstatix 0.1.1 ## New features - New function `get_pvalue_position` added to autocompute p-value positions for plotting significance using ggplot2. - New function `get_comparisons()` added to create a list of possible pairwise comparisons between groups. - New function `dunn_test()` added for multiple pairwise comparisons following Kruskal-Wallis test. - New function `sign_test()` added. ## Minor changes - `get_summary_stats()` now supports type = "min", "max", "mean" or "median" - the results of `t_test()`, `wilcox_test()`, `dunn_test()` and `sign_test()` are now an object of class `rstatix_test` that has an attribute named **args** for holding the test arguments. - The results of `cohens_d()` is now a data frame containing the Cohen's d and the magnitude. ## Bug fixes - the argument `detatiled` is now passed to `compare_pairs()`. # rstatix 0.1.0 First release rstatix/MD50000644000176200001440000002125314011734072012262 0ustar liggesusers1a6e9e6a68ded314247e7a6d1b391384 *DESCRIPTION 557de5785071bac01d4dd7bc439a99b6 *NAMESPACE bf1658dd99547701d5e63bcfe1a0ab00 *NEWS.md d6ac9490368da98e62ee5fee3d1439f2 *R/add_significance.R 7bfc41c8e2bdfaeb81bedaa86211eed3 *R/adjust_pvalue.R 1338c6db639dc3dee6600e9443e019a0 *R/anova_summary.R af0e6470c80aa1e03dfb90dca1b2b74c *R/anova_test.R 7118eac79e848e7ed5ec8e59b093299f *R/as_cor_mat.R 34f6380e599280c2a9074608ef42fddf *R/binom_test.R 8310e8fe53005efcea8853cd94fc8919 *R/box_m.R da3f183168c934db393d646158236650 *R/chisq_test.R f1f5d16b71fc333c5fecd804918c4ce9 *R/cochran_qtest.R ca6eacc7db90238c5734ab7744d951b9 *R/cohens_d.R bb1c918e4d0cdd3ddfa0353b682dd469 *R/cor_as_symbols.R 2d62b214a65bc7a0a0260508b513f38a *R/cor_mark_significant.R ab8dd210b306c5c83602f8a027ec7b79 *R/cor_mat.R fe1cc1281a2cc604b0316580a9b62d83 *R/cor_plot.R 8350cc882a0bf4383722417aea0efc8f *R/cor_reorder.R f47b1241f146e1a58edca20fee1f1be6 *R/cor_reshape.R 74f9cc86893a92bf5a4a8f34bea01dbe *R/cor_select.R 0c5476801289da56a065912fe345e0e4 *R/cor_test.R 9afd5dbb7eb4eef3c854443ad1fb3328 *R/counts_to_cases.R 584116d12f1cf518509e655016ff4a13 *R/cramer_v.R b7a7b07d19454112b01ac395977d0001 *R/df.R f648a46129eaa297b25fd2719358004a *R/doo.R 8c40faea0dfe21f86e02416dcd914cd4 *R/dunn_test.R 1100189901f2004b357072ea168b034b *R/emmeans_test.R e3231b7384bf86cf64f4628b40ef0b5a *R/eta_squared.R b0971d54cef8279d90dff336390e7c0d *R/factorial_design.R 8e05326cc8f25d112411331bf8dea853 *R/factors.R e63bcbdd3b9c977628389f2973bf48c4 *R/fisher_test.R 739121ee1921ccdd0ccd1b941fc955f6 *R/freq_table.R 61a085a041d81a4f181fdc269279c0e1 *R/friedman_effsize.R cf5081d4c5df62671554ae70fa00d5fe *R/friedman_test.R 94777c99bf3122d82811f2aea83f1058 *R/games_howell_test.R 79592e72d3d9c8ba70b26a4d9210f904 *R/get_comparisons.R df5f3e755e717c04456374f17fed6b41 *R/get_manova_table.R aef2d86c7337b5be9b01c35d80c6f0e4 *R/get_mode.R 53a6bc03995549a87d1d6737088c7bd0 *R/get_pvalue_position.R 4e6d45af8bd228761c7991913b94c329 *R/get_summary_stats.R bb1b7d4373ece84009d7705bb20fac5b *R/get_test_label.R ffed7d90833d4d32dbc5c71094f16bc8 *R/kruskal_effesize.R cf85b73c1e82d4552195fbc5cdf2f943 *R/kruskal_test.R a05f798b023323202bd5c5d13cb4e9de *R/levene_test.R 93473f3cc29fbc2009601e89e6c01be2 *R/mahalanobis_distance.R a0f8b7f9f0539719c405e38f1b5c88a0 *R/make_clean_names.R c34753dcc932f05b3abad4dd543914ff *R/mcnemar_test.R a8cacf22040f2c9d5614dac6b76192d9 *R/multinom_test.R 6d04aeceb048ee6f4a032cdb290e998a *R/outliers.R 379ddc7fdfded0a791a245b1b0f07830 *R/p_value.R 4335eca5d152e9dea191688188bf81f0 *R/prop_test.R 217b76648b64bd00caef6fd732ae93ff *R/prop_trend_test.R d007ab4b8df2b0469607582f9512f801 *R/pull_triangle.R f9e62df69d12206ebfc6007f06eff534 *R/reexports.R b6939ad3d433bb756ab5f79192486919 *R/remove_ns.R 1f35ba26e8d8f38cd9197882d65dff73 *R/replace_triangle.R 031df170ca0e9ec318d1f372f45ad0bb *R/sample_n_by.R c8b884e4b05ede79890e56c70c167767 *R/shapiro_test.R 71ec673c964a708243fddfbb0f074fb0 *R/sign_test.R 6cdbe0bfe3500ad5d18976ba51c8430f *R/t_test.R b305b3c418bc56d3eda69d4f22f2f392 *R/tukey_hsd.R 5bc6b25de0df2fbdf1d0795d29ec28da *R/utilities.R 798acce02e112786e7377b1d72f2e54e *R/utilities_two_sample_test.R 29fd2f2c91abc81242969187953d650d *R/utils-manova.R 65cef5e0674056f6fc07dd2b3ca85c13 *R/utils-pipe.R 7a8bed3a365425332d890fb7eea43707 *R/welch_anova_test.R 3c7c89e84d4bef649216d213f96cc321 *R/wilcox_effsize.R dcf93ede6c902df30bb3268105742889 *R/wilcox_test.R bc9682b0985d20fb2129762f61452202 *README.md 0d1df7c3030e35d5354ccff95550bf07 *inst/WORDLIST a88996d6ebb74e7d78dc4fc2ede7240f *man/Manova.Rd c7b514e6b6ba50d031e74c85a7de4f6a *man/add_significance.Rd f80f1f8a83ece26d3c9c69cfc9abf928 *man/adjust_pvalue.Rd 2a7a63ad9b3d21a9236dc485769f51fa *man/anova_summary.Rd a423b3e096026768476b5251cd4aa755 *man/anova_test.Rd a87a2fb7ccb20c762d78940df6d283d4 *man/as_cor_mat.Rd d09bc2866d5eed1136e641fd52fd50e9 *man/binom_test.Rd 5d0161443e31fdd8f0f222189217abe9 *man/box_m.Rd 4e51fb20995b74d25f1204085462a04c *man/chisq_test.Rd a4e6f30d831c859087756467bfdf4513 *man/cochran_qtest.Rd 020868513b9f3ae2cad65d212c5a7dc6 *man/cohens_d.Rd 13888842d8cffab804d589ef1e1a9078 *man/cor_as_symbols.Rd 40bfc70aa098fabe1495b5c77c3cfa01 *man/cor_mark_significant.Rd 47df22846bcba4e927e8dd782e0d114a *man/cor_mat.Rd 77bbb5d6bd949771044dc4f19b35a273 *man/cor_plot.Rd 132087a5d673f8cf467539ebae8182a7 *man/cor_reorder.Rd 63f2a0f2355634df33126ac231e81ba9 *man/cor_reshape.Rd 3ff5d235dd3b844bb01a18b27cd148c5 *man/cor_select.Rd eeb7072f065674d92fcd8ae27710300f *man/cor_test.Rd 90e795efb08cd85567ab17d7a7a99053 *man/counts_to_cases.Rd 2772cf3340824b1d12d603d3b06e8863 *man/cramer_v.Rd c100a522a902ed5968a136d87c611f49 *man/df_arrange.Rd 5ad9b10b4e9752b2ef7f7b48d6393b48 *man/df_get_var_names.Rd 5a104c6455c799ea630fb4ebbda4072c *man/df_group_by.Rd dbc245edc7bf35942c9ce149a308cbb3 *man/df_label_value.Rd 1b18ab01364afdf6b1ff6227878fc30b *man/df_nest_by.Rd 7548b599cdb275869fde090d733c17ee *man/df_select.Rd daa7e610c83ea0d52680db7f0d7785b4 *man/df_split_by.Rd 415c9c0684dcf77923396f009de097f6 *man/df_unite.Rd c828eda713217a6ed2c4a4cfed1f91b2 *man/doo.Rd c997f578c029ce9964958c36679ab148 *man/dunn_test.Rd 669d4a9332ab2210fcf5ab0e0a603f06 *man/emmeans_test.Rd 9016a7f170be53d8aa3ec46a97d68ff5 *man/eta_squared.Rd a8cc2066ce655796674312adb3f7880e *man/factorial_design.Rd 1e2b39ea95a01570ca6f64ffe850ffd0 *man/factors.Rd 8b3a6337ae2f044d05470e55c6f69230 *man/fisher_test.Rd 267c2558568c097b56c745dfb1022a0e *man/freq_table.Rd 38f3bdca58c2b15923862c0ccf3f1e11 *man/friedman_effsize.Rd 431d23b8b4d37f963e696537e0c7214b *man/friedman_test.Rd 7db5421b21e2b9c3ac6f107d1f9d3036 *man/games_howell_test.Rd bf55765139dc09a0aa5d299a699f25c5 *man/get_comparisons.Rd 9d0226d5c3e7aee77a9c30a1b1af2e49 *man/get_mode.Rd 069c50a4c43302f994f986a420eff38e *man/get_pvalue_position.Rd d1d8dc15d6de4606e64a63f799772e88 *man/get_summary_stats.Rd 1a459cadff167a76970af7d84a79d5b8 *man/get_test_label.Rd 01df0345b3d71faeb9fdc5dc4dc5d04a *man/kruskal_effsize.Rd aad26da1a0dbbb2696b5c396f06a5316 *man/kruskal_test.Rd 87e09851c062d3495f314fe6cc096f79 *man/levene_test.Rd cedc3ab69094e5ceae1d197de3c69605 *man/mahalanobis_distance.Rd 7d83ccf15aaf4cafe96bcf1a710cb160 *man/make_clean_names.Rd 6d268702ce77533baef0b44abf45a138 *man/mcnemar_test.Rd e7f29d9fa81116023e94ccd3d9218d2c *man/multinom_test.Rd 0b45592fc6084bdbef95f18f1c6028eb *man/outliers.Rd ac855e617e3ace701fcfaf03ff49bb11 *man/p_value.Rd 4894ca10756199e25f914bfd34f517b0 *man/pipe.Rd 43cdc0135bb0ef2417c3d7bf76401607 *man/prop_test.Rd a263be3d488679d1fdbd6d539ade9eea *man/prop_trend_test.Rd 7bb618f2d6fabc04a830706533893c24 *man/pull_triangle.Rd 8f1b01ffaae44cf0122f2eb076c77da1 *man/reexports.Rd 2c1d214a21a23367854e769731391837 *man/remove_ns.Rd 322cc72bcda2b21e833210e1c311d0d3 *man/replace_triangle.Rd 658a7cc0b91a6c08e2298989b0da82da *man/sample_n_by.Rd eabe61d2ef817435c418e94bea2fcc97 *man/shapiro_test.Rd 65dc3c83d395e632e42b4bef5c0d057d *man/sign_test.Rd fc8db07771f49372c6ea2f8713b4d073 *man/t_test.Rd 7ab960dca75a18a83ea9494b4fa1a379 *man/tukey_hsd.Rd 98987b08fadb5806ed139e548102f8d3 *man/welch_anova_test.Rd 6779653990efa8b4ccf0e40fd8731ef2 *man/wilcox_effsize.Rd 55182cbe58c36fe838f5cd3178069877 *man/wilcox_test.Rd 1a37bf6f61366dcab43a7a1d11136ea3 *tests/spelling.R c93b013fe9b4bdf779bd4df2ec8390e5 *tests/testthat.R a7ca7e063336230d10d319f496f7a688 *tests/testthat/test-add_x_position.R a033534a633b7ea35800c7121e3b1d92 *tests/testthat/test-anova_test.R 082fe154487fa2c68bc68a470543231c *tests/testthat/test-get_n.R b922019dde91bd6b5168fe9f44b38b18 *tests/testthat/test-levene_test.R a82e342765f643049607382e557336bb *tests/testthat/test-p_mark_significance.R b36908ac355e036b7c562735b44ee9fa *tests/testthat/test-remove_ns.R e01a7f9ca2986e12d673aabaf1055a69 *tests/testthat/test-wilcox_test.R 66adb57e436ee0670acd6fcc030c961d *tools/README--grouped-two-sample-t-test-1.png 742e89c812cb0bf2379584abf92d6ee6 *tools/README-comaprison-against-reference-group-1.png dc0613237e5a458430ff07d4c302ce6f *tools/README-comaprison-against-reference-group-2.png eb2a8978bcaa5194829a0356dfaaae41 *tools/README-comaprison-against-reference-group-3.png c3e2816805ba8d6c6a3ff37b67182b1b *tools/README-comparison-against-base-mean-1.png b41e0f8f7053861a96408b0b720e4c00 *tools/README-custoize-p-value-labels-1.png efc7fb88a9229659527119982bb62862 *tools/README-grouped-two-sample-t-test-1.png 0ca7e90ec4a424d8c78a09293649d9e3 *tools/README-paired-t-test-1.png df42908783e170a580a99c4a8ae00165 *tools/README-pairwise-comparisons-1.png b8f0992d4126b19e0869cf444a295eea *tools/README-two-sample-t-test-1.png c91faf29c508c0491ede6af892989aeb *tools/README-unnamed-chunk-10-1.png 3b61e644baf07ee14903b46a715101b0 *tools/README-unnamed-chunk-8-1.png 3b61e644baf07ee14903b46a715101b0 *tools/README-unnamed-chunk-9-1.png 3788b88672a82230eebb7b23d5d8f728 *tools/README-unpaired-two-sample-t-test-1.png rstatix/inst/0000755000176200001440000000000013722737540012737 5ustar liggesusersrstatix/inst/WORDLIST0000644000176200001440000000155213672731673014141 0ustar liggesusersaaronsc ANCOVA anova Anova aov args arround autocompute Autocompute basemean bca BH bonferroni bootstap cbu ci coef cohen's Cramer's df DF DFd DFn dplyr DunnTest dv effectSize emmeans Ewa fdr Feldt frindly Geisser ges GG GGe ggplot glyphs HFe hochberg holm hommel howell http https Huynh idata idesign intra iqr Kendalls kruskal Kyu labelled labelling Levene's litterature lm lwr Maciej mahal Mauchly's mrc mvnormtest nd perc pes plotmath psy pvalue quartile reimplementation rpubs Sangseok Satterthwaite Schlege sd se signif softwares sphericity Sphericity Ss SSd SSn statswiki steve Technometrics tibble tidyr tidyverse Tomczak TP tukey uk unnested wallis Welch Welch’s wich wilcoxon Wilk www Armitage Anesthesiol Hyunh McNemar McNemar's conf gla nrow rescaled rx unreplicated xc tbl sep directionality Usefull Cramer Olkin GGPUBR GGPlot Ss' tidyverse tidyverse' Manova