MatchIt/0000755000176200001440000000000014172363674011620 5ustar liggesusersMatchIt/NAMESPACE0000644000176200001440000000123114142512050013011 0ustar liggesusersuseDynLib("MatchIt", .registration = TRUE) import(stats) import(graphics) importFrom("grDevices", "devAskNewPage", "nclass.FD", "nclass.Sturges", "nclass.scott") importFrom("utils", "setTxtProgressBar", "txtProgressBar", "capture.output", "combn") importFrom("Rcpp", "evalCpp") export(get_matches) export(match.data) export(matchit) export(add_s.weights) S3method(plot, matchit) S3method(plot, matchit.subclass) S3method(plot, summary.matchit) S3method(print, matchit) S3method(print, summary.matchit) S3method(print, summary.matchit.subclass) S3method(summary, matchit) S3method(summary, matchit.subclass) S3method(rbind, matchdata) S3method(rbind, getmatches) MatchIt/data/0000755000176200001440000000000013705231731012516 5ustar liggesusersMatchIt/data/lalonde.tab0000644000176200001440000007242613705231557014645 0ustar liggesusers"treat" "age" "educ" "race" "married" "nodegree" "re74" "re75" "re78" "NSW1" 1 37 11 "black" 1 1 0 0 9930.046 "NSW2" 1 22 9 "hispan" 0 1 0 0 3595.894 "NSW3" 1 30 12 "black" 0 0 0 0 24909.45 "NSW4" 1 27 11 "black" 0 1 0 0 7506.146 "NSW5" 1 33 8 "black" 0 1 0 0 289.7899 "NSW6" 1 22 9 "black" 0 1 0 0 4056.494 "NSW7" 1 23 12 "black" 0 0 0 0 0 "NSW8" 1 32 11 "black" 0 1 0 0 8472.158 "NSW9" 1 22 16 "black" 0 0 0 0 2164.022 "NSW10" 1 33 12 "white" 1 0 0 0 12418.07 "NSW11" 1 19 9 "black" 0 1 0 0 8173.908 "NSW12" 1 21 13 "black" 0 0 0 0 17094.64 "NSW13" 1 18 8 "black" 0 1 0 0 0 "NSW14" 1 27 10 "black" 1 1 0 0 18739.93 "NSW15" 1 17 7 "black" 0 1 0 0 3023.879 "NSW16" 1 19 10 "black" 0 1 0 0 3228.503 "NSW17" 1 27 13 "black" 0 0 0 0 14581.86 "NSW18" 1 23 10 "black" 0 1 0 0 7693.4 "NSW19" 1 40 12 "black" 0 0 0 0 10804.32 "NSW20" 1 26 12 "black" 0 0 0 0 10747.35 "NSW21" 1 23 11 "black" 0 1 0 0 0 "NSW22" 1 41 14 "white" 0 0 0 0 5149.501 "NSW23" 1 38 9 "white" 0 1 0 0 6408.95 "NSW24" 1 24 11 "black" 0 1 0 0 1991.4 "NSW25" 1 18 10 "black" 0 1 0 0 11163.17 "NSW26" 1 29 11 "black" 1 1 0 0 9642.999 "NSW27" 1 25 11 "black" 0 1 0 0 9897.049 "NSW28" 1 27 10 "hispan" 0 1 0 0 11142.87 "NSW29" 1 17 10 "black" 0 1 0 0 16218.04 "NSW30" 1 24 11 "black" 0 1 0 0 995.7002 "NSW31" 1 17 10 "black" 0 1 0 0 0 "NSW32" 1 48 4 "black" 0 1 0 0 6551.592 "NSW33" 1 25 11 "black" 1 1 0 0 1574.424 "NSW34" 1 20 12 "black" 0 0 0 0 0 "NSW35" 1 25 12 "black" 0 0 0 0 3191.753 "NSW36" 1 42 14 "black" 0 0 0 0 20505.93 "NSW37" 1 25 5 "black" 0 1 0 0 6181.88 "NSW38" 1 23 12 "black" 1 0 0 0 5911.551 "NSW39" 1 46 8 "black" 1 1 0 0 3094.156 "NSW40" 1 24 10 "black" 0 1 0 0 0 "NSW41" 1 21 12 "black" 0 0 0 0 1254.582 "NSW42" 1 19 9 "white" 0 1 0 0 13188.83 "NSW43" 1 17 8 "black" 0 1 0 0 8061.485 "NSW44" 1 18 8 "hispan" 1 1 0 0 2787.96 "NSW45" 1 20 11 "black" 0 1 0 0 3972.54 "NSW46" 1 25 11 "black" 1 1 0 0 0 "NSW47" 1 17 8 "black" 0 1 0 0 0 "NSW48" 1 17 9 "black" 0 1 0 0 0 "NSW49" 1 25 5 "black" 0 1 0 0 12187.41 "NSW50" 1 23 12 "black" 0 0 0 0 4843.176 "NSW51" 1 28 8 "black" 0 1 0 0 0 "NSW52" 1 31 11 "black" 1 1 0 0 8087.487 "NSW53" 1 18 11 "black" 0 1 0 0 0 "NSW54" 1 25 12 "black" 0 0 0 0 2348.973 "NSW55" 1 30 11 "black" 1 1 0 0 590.7818 "NSW56" 1 17 10 "black" 0 1 0 0 0 "NSW57" 1 37 9 "black" 0 1 0 0 1067.506 "NSW58" 1 41 4 "black" 1 1 0 0 7284.986 "NSW59" 1 42 14 "black" 1 0 0 0 13167.52 "NSW60" 1 22 11 "white" 0 1 0 0 1048.432 "NSW61" 1 17 8 "black" 0 1 0 0 0 "NSW62" 1 29 8 "black" 0 1 0 0 1923.938 "NSW63" 1 35 10 "black" 0 1 0 0 4666.236 "NSW64" 1 27 11 "black" 0 1 0 0 549.2984 "NSW65" 1 29 4 "black" 0 1 0 0 762.9146 "NSW66" 1 28 9 "black" 0 1 0 0 10694.29 "NSW67" 1 27 11 "black" 0 1 0 0 0 "NSW68" 1 23 7 "white" 0 1 0 0 0 "NSW69" 1 45 5 "black" 1 1 0 0 8546.715 "NSW70" 1 29 13 "black" 0 0 0 0 7479.656 "NSW71" 1 27 9 "black" 0 1 0 0 0 "NSW72" 1 46 13 "black" 0 0 0 0 647.2046 "NSW73" 1 18 6 "black" 0 1 0 0 0 "NSW74" 1 25 12 "black" 0 0 0 0 11965.81 "NSW75" 1 28 15 "black" 0 0 0 0 9598.541 "NSW76" 1 25 11 "white" 0 1 0 0 18783.35 "NSW77" 1 22 12 "black" 0 0 0 0 18678.08 "NSW78" 1 21 9 "black" 0 1 0 0 0 "NSW79" 1 40 11 "black" 0 1 0 0 23005.6 "NSW80" 1 22 11 "black" 0 1 0 0 6456.697 "NSW81" 1 25 12 "black" 0 0 0 0 0 "NSW82" 1 18 12 "black" 0 0 0 0 2321.107 "NSW83" 1 38 12 "white" 0 0 0 0 4941.849 "NSW84" 1 27 13 "black" 0 0 0 0 0 "NSW85" 1 27 8 "black" 0 1 0 0 0 "NSW86" 1 38 11 "black" 0 1 0 0 0 "NSW87" 1 23 8 "hispan" 0 1 0 0 3881.284 "NSW88" 1 26 11 "black" 0 1 0 0 17230.96 "NSW89" 1 21 12 "white" 0 0 0 0 8048.603 "NSW90" 1 25 8 "black" 0 1 0 0 0 "NSW91" 1 31 11 "black" 1 1 0 0 14509.93 "NSW92" 1 17 10 "black" 0 1 0 0 0 "NSW93" 1 25 11 "black" 0 1 0 0 0 "NSW94" 1 21 12 "black" 0 0 0 0 9983.784 "NSW95" 1 44 11 "black" 0 1 0 0 0 "NSW96" 1 25 12 "white" 0 0 0 0 5587.503 "NSW97" 1 18 9 "black" 0 1 0 0 4482.845 "NSW98" 1 42 12 "black" 0 0 0 0 2456.153 "NSW99" 1 25 10 "black" 0 1 0 0 0 "NSW100" 1 31 9 "hispan" 0 1 0 0 26817.6 "NSW101" 1 24 10 "black" 0 1 0 0 0 "NSW102" 1 26 10 "black" 0 1 0 0 9265.788 "NSW103" 1 25 11 "black" 0 1 0 0 485.2298 "NSW104" 1 18 11 "black" 0 1 0 0 4814.627 "NSW105" 1 19 11 "black" 0 1 0 0 7458.105 "NSW106" 1 43 9 "black" 0 1 0 0 0 "NSW107" 1 27 13 "black" 0 0 0 0 34099.28 "NSW108" 1 17 9 "black" 0 1 0 0 1953.268 "NSW109" 1 30 11 "black" 0 1 0 0 0 "NSW110" 1 26 10 "black" 1 1 2027.999 0 0 "NSW111" 1 20 9 "black" 0 1 6083.994 0 8881.665 "NSW112" 1 17 9 "hispan" 0 1 445.1704 74.34345 6210.67 "NSW113" 1 20 12 "black" 0 0 989.2678 165.2077 0 "NSW114" 1 18 11 "black" 0 1 858.2543 214.5636 929.8839 "NSW115" 1 27 12 "black" 1 0 3670.872 334.0493 0 "NSW116" 1 21 12 "white" 0 0 3670.872 334.0494 12558.02 "NSW117" 1 27 12 "black" 0 0 2143.413 357.9499 22163.25 "NSW118" 1 20 12 "black" 0 0 0 377.5686 1652.637 "NSW119" 1 19 10 "black" 0 1 0 385.2741 8124.715 "NSW120" 1 23 12 "black" 0 0 5506.308 501.0741 671.3318 "NSW121" 1 29 14 "black" 0 0 0 679.6734 17814.98 "NSW122" 1 18 10 "black" 0 1 0 798.9079 9737.154 "NSW123" 1 19 9 "black" 0 1 0 798.9079 17685.18 "NSW124" 1 27 13 "white" 1 0 9381.566 853.7225 0 "NSW125" 1 18 11 "white" 0 1 3678.231 919.5579 4321.705 "NSW126" 1 27 9 "black" 1 1 0 934.4454 1773.423 "NSW127" 1 22 12 "black" 0 0 5605.852 936.1773 0 "NSW128" 1 23 10 "black" 1 1 0 936.4386 11233.26 "NSW129" 1 23 12 "hispan" 0 0 9385.74 1117.439 559.4432 "NSW130" 1 20 11 "black" 0 1 3637.498 1220.836 1085.44 "NSW131" 1 17 9 "black" 0 1 1716.509 1253.439 5445.2 "NSW132" 1 28 11 "black" 0 1 0 1284.079 60307.93 "NSW133" 1 26 11 "black" 1 1 0 1392.853 1460.36 "NSW134" 1 20 11 "black" 0 1 16318.62 1484.994 6943.342 "NSW135" 1 24 11 "black" 1 1 824.3886 1666.113 4032.708 "NSW136" 1 31 9 "black" 0 1 0 1698.607 10363.27 "NSW137" 1 23 8 "white" 1 1 0 1713.15 4232.309 "NSW138" 1 18 10 "black" 0 1 2143.411 1784.274 11141.39 "NSW139" 1 29 12 "black" 0 0 10881.94 1817.284 0 "NSW140" 1 26 11 "white" 0 1 0 2226.266 13385.86 "NSW141" 1 24 9 "black" 0 1 9154.7 2288.675 4849.559 "NSW142" 1 25 12 "black" 0 0 14426.79 2409.274 0 "NSW143" 1 24 10 "black" 0 1 4250.402 2421.947 1660.508 "NSW144" 1 46 8 "black" 0 1 3165.658 2594.723 0 "NSW145" 1 31 12 "white" 0 0 0 2611.218 2484.549 "NSW146" 1 19 11 "black" 0 1 2305.026 2615.276 4146.603 "NSW147" 1 19 8 "black" 0 1 0 2657.057 9970.681 "NSW148" 1 27 11 "black" 0 1 2206.94 2666.274 0 "NSW149" 1 26 11 "black" 1 1 0 2754.646 26372.28 "NSW150" 1 20 10 "black" 0 1 5005.731 2777.355 5615.189 "NSW151" 1 28 10 "black" 0 1 0 2836.506 3196.571 "NSW152" 1 24 12 "black" 0 0 13765.75 2842.764 6167.681 "NSW153" 1 19 8 "black" 0 1 2636.353 2937.264 7535.942 "NSW154" 1 23 12 "black" 0 0 6269.341 3039.96 8484.239 "NSW155" 1 42 9 "black" 1 1 0 3058.531 1294.409 "NSW156" 1 25 13 "black" 0 0 12362.93 3090.732 0 "NSW157" 1 18 9 "black" 0 1 0 3287.375 5010.342 "NSW158" 1 21 12 "black" 0 0 6473.683 3332.409 9371.037 "NSW159" 1 27 10 "black" 0 1 1001.146 3550.075 0 "NSW160" 1 21 8 "black" 0 1 989.2678 3695.897 4279.613 "NSW161" 1 22 9 "black" 0 1 2192.877 3836.986 3462.564 "NSW162" 1 31 4 "black" 0 1 8517.589 4023.211 7382.549 "NSW163" 1 24 10 "black" 1 1 11703.2 4078.152 0 "NSW164" 1 29 10 "black" 0 1 0 4398.95 0 "NSW165" 1 29 12 "black" 0 0 9748.387 4878.937 10976.51 "NSW166" 1 19 10 "white" 0 1 0 5324.109 13829.62 "NSW167" 1 19 11 "hispan" 1 1 5424.485 5463.803 6788.463 "NSW168" 1 31 9 "black" 0 1 10717.03 5517.841 9558.501 "NSW169" 1 22 10 "black" 1 1 1468.348 5588.664 13228.28 "NSW170" 1 21 9 "black" 0 1 6416.47 5749.331 743.6666 "NSW171" 1 17 10 "black" 0 1 1291.468 5793.852 5522.788 "NSW172" 1 26 12 "black" 1 0 8408.762 5794.831 1424.944 "NSW173" 1 20 9 "hispan" 0 1 12260.78 5875.049 1358.643 "NSW174" 1 19 10 "black" 0 1 4121.949 6056.754 0 "NSW175" 1 26 10 "black" 0 1 25929.68 6788.958 672.8773 "NSW176" 1 28 11 "black" 0 1 1929.029 6871.856 0 "NSW177" 1 22 12 "hispan" 1 0 492.2305 7055.702 10092.83 "NSW178" 1 33 11 "black" 0 1 0 7867.916 6281.433 "NSW179" 1 22 12 "white" 0 0 6759.994 8455.504 12590.71 "NSW180" 1 29 10 "hispan" 0 1 0 8853.674 5112.014 "NSW181" 1 33 12 "black" 1 0 20279.95 10941.35 15952.6 "NSW182" 1 25 14 "black" 1 0 35040.07 11536.57 36646.95 "NSW183" 1 35 9 "black" 1 1 13602.43 13830.64 12803.97 "NSW184" 1 35 8 "black" 1 1 13732.07 17976.15 3786.628 "NSW185" 1 33 11 "black" 1 1 14660.71 25142.24 4181.942 "PSID1" 0 30 12 "white" 1 0 20166.73 18347.23 25564.67 "PSID2" 0 26 12 "white" 1 0 25862.32 17806.55 25564.67 "PSID3" 0 25 16 "white" 1 0 25862.32 15316.21 25564.67 "PSID4" 0 42 11 "white" 1 1 21787.05 14265.29 15491.01 "PSID5" 0 25 9 "black" 1 1 14829.69 13776.53 0 "PSID6" 0 37 9 "black" 1 1 13685.48 12756.05 17833.2 "PSID7" 0 32 12 "white" 1 0 19067.58 12625.35 14146.28 "PSID8" 0 20 12 "black" 0 0 7392.314 12396.19 17765.23 "PSID9" 0 38 9 "hispan" 1 1 16826.18 12029.18 0 "PSID10" 0 39 10 "white" 1 1 16767.41 12022.02 4433.18 "PSID11" 0 41 5 "white" 1 1 10785.76 11991.58 19451.31 "PSID12" 0 31 14 "white" 1 0 17831.29 11563.69 22094.97 "PSID13" 0 34 8 "white" 1 1 8038.872 11404.35 5486.799 "PSID14" 0 29 12 "white" 1 0 14768.95 11146.55 6420.722 "PSID15" 0 22 14 "black" 1 0 748.4399 11105.37 18208.55 "PSID16" 0 42 0 "hispan" 1 1 2797.833 10929.92 9922.934 "PSID17" 0 25 9 "hispan" 0 1 5460.477 10589.76 7539.361 "PSID18" 0 28 9 "white" 1 1 11091.41 10357.02 15406.78 "PSID19" 0 40 13 "white" 1 0 3577.621 10301.52 11911.95 "PSID20" 0 35 9 "white" 1 1 11475.43 9397.403 11087.38 "PSID21" 0 27 10 "hispan" 1 1 15711.36 9098.419 17023.41 "PSID22" 0 27 6 "hispan" 1 1 7831.189 9071.565 5661.171 "PSID23" 0 36 12 "white" 1 0 25535.12 8695.597 21905.82 "PSID24" 0 47 8 "black" 1 1 9275.169 8543.419 0 "PSID25" 0 40 11 "white" 1 1 20666.35 8502.242 25564.67 "PSID26" 0 27 7 "white" 1 1 3064.293 8461.065 11149.45 "PSID27" 0 36 9 "black" 1 1 13256.4 8457.484 0 "PSID28" 0 39 6 "hispan" 1 1 13279.91 8441.371 25048.94 "PSID29" 0 21 9 "white" 1 1 11156.07 8441.371 1213.214 "PSID30" 0 29 12 "white" 1 0 11199.17 8081.516 0 "PSID31" 0 22 13 "hispan" 0 0 6404.843 7882.79 9453.017 "PSID32" 0 25 10 "white" 1 1 13634.54 7793.274 11688.82 "PSID33" 0 27 12 "white" 1 0 12270.89 7709.129 7806.829 "PSID34" 0 45 8 "white" 1 1 22415.97 7635.726 15931.37 "PSID35" 0 26 12 "white" 1 0 2345.242 7565.903 2838.713 "PSID36" 0 27 12 "white" 1 0 9788.497 7496.081 14038.4 "PSID37" 0 33 8 "white" 1 1 12312.03 7474.597 25514.43 "PSID38" 0 25 12 "white" 1 0 11381.38 7467.435 4162.756 "PSID39" 0 49 8 "white" 1 1 6459.703 7431.629 7503.896 "PSID40" 0 40 3 "hispan" 1 1 7576.485 7426.258 12104.06 "PSID41" 0 22 12 "black" 1 0 9729.719 7372.548 2231.367 "PSID42" 0 25 5 "white" 1 1 7891.927 7293.774 14617.67 "PSID43" 0 25 12 "white" 1 0 11516.57 7263.339 19588.74 "PSID44" 0 21 12 "white" 1 0 13601.23 7202.468 10746.03 "PSID45" 0 33 9 "hispan" 1 1 11959.36 7087.887 25564.67 "PSID46" 0 20 12 "black" 1 0 9555.344 7055.661 0 "PSID47" 0 19 11 "white" 1 1 4306.468 6978.677 837.871 "PSID48" 0 25 12 "black" 1 0 295.8493 6942.871 461.0507 "PSID49" 0 29 12 "white" 1 0 15303.83 6932.129 24290.87 "PSID50" 0 20 12 "white" 1 0 3558.029 6797.855 6680.802 "PSID51" 0 29 6 "hispan" 1 1 8542.403 6701.177 7196.528 "PSID52" 0 25 13 "white" 1 0 19259.59 6652.839 13015.82 "PSID53" 0 41 15 "white" 1 0 25862.32 6563.323 24647 "PSID54" 0 39 10 "white" 1 1 22745.13 6493.5 25564.67 "PSID55" 0 33 12 "white" 1 0 10819.07 6369.968 2936.243 "PSID56" 0 29 8 "white" 1 1 9169.369 6352.065 20575.86 "PSID57" 0 21 11 "white" 1 1 10679.96 6276.871 10923.35 "PSID58" 0 31 12 "white" 1 0 23652.27 6228.532 22403.81 "PSID59" 0 36 12 "black" 1 0 11040.47 6221.371 7215.739 "PSID60" 0 25 7 "white" 1 1 5597.625 6099.629 122.6513 "PSID61" 0 35 7 "white" 1 1 10715.23 6087.097 15177.73 "PSID62" 0 22 9 "white" 1 1 5683.833 6038.758 4742.025 "PSID63" 0 31 2 "hispan" 1 1 3262.179 5965.355 9732.307 "PSID64" 0 40 15 "white" 1 0 10907.24 5922.387 6238.962 "PSID65" 0 47 3 "white" 1 1 9047.894 5911.645 6145.865 "PSID66" 0 26 8 "hispan" 0 1 3168.134 5872.258 11136.15 "PSID67" 0 42 7 "white" 1 1 10971.89 5806.016 9241.702 "PSID68" 0 53 12 "white" 0 0 17104.4 5775.581 19965.56 "PSID69" 0 30 17 "black" 0 0 17827.37 5546.419 14421.13 "PSID70" 0 28 10 "white" 1 1 10415.46 5544.629 10289.41 "PSID71" 0 46 11 "white" 1 1 14753.28 5299.355 0 "PSID72" 0 28 12 "white" 0 0 8256.35 5279.661 21602.88 "PSID73" 0 27 12 "hispan" 1 0 17604.01 5222.371 25564.67 "PSID74" 0 25 10 "white" 1 1 4335.857 5181.194 12418.81 "PSID75" 0 38 8 "white" 1 1 11242.27 5174.032 0 "PSID76" 0 26 12 "hispan" 0 0 7968.338 5109.581 4181.966 "PSID77" 0 54 12 "white" 0 0 7165.039 5012.903 0 "PSID78" 0 38 8 "hispan" 1 1 22606.02 4978.887 8720.065 "PSID79" 0 23 17 "white" 0 0 0 4876.839 16747.08 "PSID80" 0 23 8 "white" 1 1 3595.255 4866.097 2782.559 "PSID81" 0 23 12 "white" 1 0 11690.95 4764.048 14065 "PSID82" 0 25 12 "hispan" 1 0 8746.167 4762.258 379.7757 "PSID83" 0 25 15 "white" 1 0 7386.436 4738.984 12705.49 "PSID84" 0 37 11 "hispan" 0 1 615.2098 4713.919 0 "PSID85" 0 40 12 "white" 1 0 18389.68 4688.855 21857.05 "PSID86" 0 19 10 "white" 0 1 5777.878 4672.742 135.9508 "PSID87" 0 48 7 "white" 1 1 13326.93 4636.935 0 "PSID88" 0 19 12 "white" 0 0 8530.648 4620.823 0 "PSID89" 0 16 9 "white" 0 1 2539.21 4579.645 0 "PSID90" 0 29 10 "white" 1 1 713.1731 4542.048 7781.708 "PSID91" 0 30 16 "white" 0 0 3093.682 4468.645 15538.29 "PSID92" 0 22 11 "white" 1 1 8761.841 4463.274 10642.59 "PSID93" 0 22 10 "white" 0 1 17268.98 4400.613 2453.026 "PSID94" 0 47 10 "black" 1 1 13311.26 4397.032 19330.14 "PSID95" 0 25 12 "hispan" 1 0 2266.872 4361.226 3020.473 "PSID96" 0 47 10 "black" 0 1 21918.32 4323.629 19438.02 "PSID97" 0 24 12 "black" 1 0 8573.752 4293.194 0 "PSID98" 0 20 12 "black" 1 0 2648.929 4273.5 0 "PSID99" 0 28 12 "black" 0 0 16722.34 4253.806 7314.747 "PSID100" 0 47 11 "white" 0 1 8060.424 4232.323 3358.873 "PSID101" 0 50 0 "white" 1 1 10162.72 4218 220.1813 "PSID102" 0 18 12 "white" 0 0 2217.89 4191.145 8957.978 "PSID103" 0 21 12 "white" 0 0 9665.063 4110.581 1687.564 "PSID104" 0 47 11 "white" 1 1 23924.61 4096.258 17358.85 "PSID105" 0 21 12 "white" 0 0 2827.222 4056.871 5937.505 "PSID106" 0 34 11 "white" 1 1 0 4010.323 18133.18 "PSID107" 0 19 12 "white" 1 0 5817.063 3919.016 1066.919 "PSID108" 0 44 13 "white" 1 0 8032.994 3881.419 3104.704 "PSID109" 0 21 15 "white" 1 0 6951.479 3879.629 0 "PSID110" 0 20 12 "black" 0 0 5099.971 3842.032 12718.79 "PSID111" 0 51 11 "white" 0 1 48.98167 3813.387 1525.014 "PSID112" 0 28 13 "white" 0 0 5260.631 3790.113 9253.524 "PSID113" 0 24 15 "white" 0 0 12746.99 3743.565 0 "PSID114" 0 28 8 "hispan" 1 1 8305.332 3718.5 0 "PSID115" 0 20 11 "white" 1 1 5822.941 3532.306 11075.56 "PSID116" 0 29 12 "white" 1 0 14288.93 3503.661 8133.407 "PSID117" 0 23 12 "white" 1 0 14347.71 3482.177 3818.445 "PSID118" 0 20 11 "black" 0 1 0 3480.387 5495.665 "PSID119" 0 42 7 "white" 1 1 4324.102 3457.113 9856.436 "PSID120" 0 43 12 "white" 1 0 14328.12 3453.532 18781.9 "PSID121" 0 27 13 "white" 0 0 16406.9 3426.677 5344.937 "PSID122" 0 27 4 "hispan" 1 1 626.9654 3410.565 3367.739 "PSID123" 0 25 12 "white" 1 0 21469.65 3405.194 7981.201 "PSID124" 0 18 12 "white" 0 0 4729.67 3328.21 12602.05 "PSID125" 0 31 16 "white" 1 0 25862.32 3254.806 25564.67 "PSID126" 0 27 12 "white" 1 0 4043.927 3231.532 7240.86 "PSID127" 0 18 11 "white" 0 1 0 3226.161 15814.63 "PSID128" 0 24 7 "white" 1 1 7860.578 3213.629 0 "PSID129" 0 23 12 "white" 1 0 7856.66 3213.629 5535.564 "PSID130" 0 50 12 "white" 1 0 19929.66 3190.355 18597.19 "PSID131" 0 19 12 "white" 0 0 99.92261 3172.452 15436.33 "PSID132" 0 23 10 "white" 1 1 15811.28 3145.597 6398.556 "PSID133" 0 51 12 "white" 1 0 21001.38 3140.226 16015.6 "PSID134" 0 19 11 "black" 0 1 5607.422 3054.29 94.5745 "PSID135" 0 20 10 "white" 1 1 3099.56 2970.145 21141.83 "PSID136" 0 20 11 "hispan" 0 1 2868.367 2968.355 7403.41 "PSID137" 0 21 12 "white" 0 0 8128.998 2939.71 0 "PSID138" 0 39 10 "white" 1 1 0 2886 18761.22 "PSID139" 0 36 5 "white" 0 1 3814.692 2873.468 2751.527 "PSID140" 0 19 9 "black" 0 1 1079.556 2873.468 14344.29 "PSID141" 0 42 6 "hispan" 1 1 2425.572 2832.29 1907.745 "PSID142" 0 20 7 "white" 0 1 1902.448 2792.903 6098.578 "PSID143" 0 23 12 "white" 1 0 4954.986 2771.419 0 "PSID144" 0 35 12 "white" 1 0 1469.45 2719.5 0 "PSID145" 0 18 12 "white" 0 0 881.6701 2696.226 12120.31 "PSID146" 0 43 8 "white" 1 1 18338.74 2674.742 6395.601 "PSID147" 0 37 14 "white" 1 0 18501.36 2638.935 13429.58 "PSID148" 0 24 10 "white" 1 1 4719.874 2565.532 2173.736 "PSID149" 0 51 12 "white" 0 0 20742.76 2538.677 1019.631 "PSID150" 0 22 11 "hispan" 0 1 7341.373 2535.097 14187.65 "PSID151" 0 19 12 "white" 0 0 336.9939 2518.984 7118.209 "PSID152" 0 52 0 "hispan" 1 1 773.9104 2506.452 0 "PSID153" 0 21 12 "white" 0 0 2903.633 2456.323 4787.834 "PSID154" 0 24 12 "white" 0 0 9784.578 2413.355 0 "PSID155" 0 35 8 "white" 1 1 2241.401 2399.032 9460.406 "PSID156" 0 20 13 "white" 0 0 0 2352.484 0 "PSID157" 0 17 7 "black" 0 1 1054.086 2286.242 1613.677 "PSID158" 0 18 10 "black" 0 1 311.5234 2284.452 8154.095 "PSID159" 0 28 12 "black" 0 0 6285.328 2255.806 7310.313 "PSID160" 0 25 14 "hispan" 1 0 1622.273 2239.694 1892.968 "PSID161" 0 40 12 "hispan" 0 0 13616.9 2228.952 876.2919 "PSID162" 0 50 3 "white" 1 1 3136.786 2203.887 13976.34 "PSID163" 0 48 8 "white" 1 1 16050.31 2116.161 11600.15 "PSID164" 0 17 7 "hispan" 0 1 0 2082.145 6460.621 "PSID165" 0 30 12 "white" 1 0 7347.251 2080.355 14475.81 "PSID166" 0 30 7 "white" 1 1 574.0652 2010.532 366.4762 "PSID167" 0 22 11 "white" 1 1 3030.986 1976.516 0 "PSID168" 0 27 12 "white" 1 0 11493.06 1906.694 13419.24 "PSID169" 0 25 9 "white" 1 1 23377.97 1901.323 1898.879 "PSID170" 0 21 14 "white" 0 0 80.32994 1890.581 6389.69 "PSID171" 0 17 10 "white" 0 1 0 1888.79 19993.64 "PSID172" 0 39 7 "white" 0 1 7786.126 1844.032 9206.237 "PSID173" 0 18 9 "black" 0 1 1183.397 1822.548 803.8833 "PSID174" 0 25 12 "white" 1 0 2721.422 1754.516 1037.364 "PSID175" 0 20 8 "white" 1 1 2360.916 1741.984 0 "PSID176" 0 19 13 "white" 0 0 2366.794 1709.758 0 "PSID177" 0 19 11 "white" 0 1 0 1693.645 9853.481 "PSID178" 0 22 12 "white" 0 0 10137.25 1679.323 25564.67 "PSID179" 0 18 11 "black" 0 1 2068.986 1623.823 20243.38 "PSID180" 0 21 10 "white" 0 1 1767.259 1555.79 7675.312 "PSID181" 0 24 12 "white" 1 0 7643.1 1546.839 3262.82 "PSID182" 0 18 11 "white" 0 1 1273.523 1532.516 12489.75 "PSID183" 0 17 10 "white" 0 1 568.1874 1525.355 6231.573 "PSID184" 0 17 10 "white" 0 1 0 1503.871 7843.773 "PSID185" 0 18 10 "white" 0 1 0 1491.339 237.914 "PSID186" 0 53 10 "hispan" 0 1 7878.212 1489.548 13170.98 "PSID187" 0 18 11 "black" 0 1 1191.234 1478.806 3683.972 "PSID188" 0 17 10 "hispan" 0 1 0 1453.742 6918.716 "PSID189" 0 26 12 "black" 0 0 0 1448.371 0 "PSID190" 0 39 5 "white" 1 1 13082.02 1434.048 18323.81 "PSID191" 0 18 12 "black" 0 0 1579.169 1408.984 3057.416 "PSID192" 0 23 13 "white" 0 0 601.4949 1394.661 4975.505 "PSID193" 0 18 8 "white" 0 1 5023.56 1391.081 6756.166 "PSID194" 0 28 10 "white" 1 1 7578.444 1383.919 2404.261 "PSID195" 0 32 4 "white" 1 1 0 1378.548 0 "PSID196" 0 18 11 "black" 0 1 0 1367.806 33.98771 "PSID197" 0 40 10 "white" 1 1 1543.902 1342.742 0 "PSID198" 0 21 14 "white" 0 0 8456.196 1330.21 16967.26 "PSID199" 0 29 10 "hispan" 0 1 3732.403 1323.048 6694.101 "PSID200" 0 31 6 "white" 0 1 2666.562 1321.258 0 "PSID201" 0 46 7 "white" 1 1 19171.43 1317.677 0 "PSID202" 0 20 9 "hispan" 1 1 0 1283.661 0 "PSID203" 0 36 18 "white" 1 0 3273.935 1269.339 18227.76 "PSID204" 0 45 12 "white" 1 0 16559.72 1265.758 7987.112 "PSID205" 0 16 10 "white" 0 1 1026.656 1224.581 6847.785 "PSID206" 0 18 12 "white" 0 0 818.9735 1208.468 2232.845 "PSID207" 0 40 12 "hispan" 0 0 11867.28 1195.935 3873.121 "PSID208" 0 16 9 "white" 0 1 0 1188.774 2451.548 "PSID209" 0 16 10 "white" 0 1 574.0652 1181.613 5578.418 "PSID210" 0 28 5 "hispan" 1 1 10967.98 1178.032 239.3917 "PSID211" 0 20 12 "white" 0 0 0 1147.597 15554.55 "PSID212" 0 19 8 "white" 1 1 39.18534 1136.855 5327.204 "PSID213" 0 16 8 "white" 0 1 0 1113.581 542.3257 "PSID214" 0 20 11 "white" 1 1 2547.047 1099.258 0 "PSID215" 0 35 10 "white" 1 1 4964.782 1086.726 1745.195 "PSID216" 0 32 6 "hispan" 1 1 979.6334 1036.597 0 "PSID217" 0 32 16 "black" 0 0 17135.75 1031.226 0 "PSID218" 0 17 9 "black" 0 1 0 981.0968 8900.347 "PSID219" 0 16 7 "white" 0 1 0 975.7258 4728.725 "PSID220" 0 32 15 "white" 0 0 489.8167 968.5645 7684.178 "PSID221" 0 19 12 "white" 0 0 815.055 964.9839 12059.73 "PSID222" 0 40 12 "white" 1 0 16851.65 961.4032 17717.94 "PSID223" 0 50 7 "white" 1 1 11473.47 956.0323 0 "PSID224" 0 39 11 "white" 0 1 0 930.9677 0 "PSID225" 0 18 8 "hispan" 0 1 0 902.3226 1306.31 "PSID226" 0 39 10 "black" 0 1 844.444 889.7903 701.9201 "PSID227" 0 17 11 "hispan" 0 1 0 873.6774 7759.542 "PSID228" 0 17 5 "black" 0 1 96.00407 868.3065 0 "PSID229" 0 19 12 "white" 0 0 2425.572 861.1452 2587.499 "PSID230" 0 27 15 "white" 0 0 0 857.5645 3392.86 "PSID231" 0 18 11 "black" 0 1 587.78 841.4516 7933.914 "PSID232" 0 20 14 "white" 1 0 0 805.6452 1454.083 "PSID233" 0 20 12 "white" 1 0 12145.49 791.3226 13683.75 "PSID234" 0 19 13 "black" 0 0 1714.358 785.9516 9067.33 "PSID235" 0 24 8 "white" 1 1 213.5601 760.8871 2340.719 "PSID236" 0 27 12 "white" 1 0 4222.22 751.9355 0 "PSID237" 0 19 9 "white" 0 1 773.9104 676.7419 5647.871 "PSID238" 0 52 8 "black" 1 1 5454.599 666 0 "PSID239" 0 18 11 "hispan" 0 1 0 630.1935 0 "PSID240" 0 16 10 "hispan" 0 1 0 630.1935 3892.332 "PSID241" 0 18 12 "hispan" 0 0 0 630.1935 4843.988 "PSID242" 0 45 12 "white" 0 0 4473.006 608.7097 0 "PSID243" 0 21 14 "white" 0 0 9708.167 594.3871 2256.488 "PSID244" 0 36 8 "white" 1 1 2715.544 585.4355 0 "PSID245" 0 21 13 "white" 0 0 513.3279 578.2742 0 "PSID246" 0 41 7 "white" 1 1 19573.08 565.7419 0 "PSID247" 0 18 7 "white" 0 1 491.776 558.5806 642.8111 "PSID248" 0 39 9 "white" 0 1 11230.52 537.0968 5752.79 "PSID249" 0 19 3 "white" 1 1 0 537.0968 0 "PSID250" 0 32 13 "white" 1 0 12553.02 524.5645 15353.58 "PSID251" 0 16 9 "white" 0 1 0 485.1774 4112.513 "PSID252" 0 16 7 "white" 0 1 658.3136 479.8065 6210.885 "PSID253" 0 21 9 "black" 0 1 1030.574 470.8548 1223.558 "PSID254" 0 22 12 "white" 1 0 12096.51 469.0645 14289.62 "PSID255" 0 23 11 "hispan" 1 1 8946.012 469.0645 4776.012 "PSID256" 0 17 8 "black" 0 1 0 451.1613 0 "PSID257" 0 21 8 "white" 1 1 5699.507 388.5 8844.194 "PSID258" 0 18 10 "white" 0 1 0 386.7097 0 "PSID259" 0 24 12 "white" 1 0 9051.813 327.629 8547.171 "PSID260" 0 24 12 "black" 1 0 4232.016 320.4677 1273.8 "PSID261" 0 16 9 "white" 0 1 0 320.4677 3707.616 "PSID262" 0 20 8 "white" 1 1 621.0876 306.1452 5551.819 "PSID263" 0 42 8 "white" 0 1 17925.33 300.7742 14116.72 "PSID264" 0 17 8 "hispan" 0 1 391.8534 300.7742 18891.26 "PSID265" 0 19 8 "hispan" 0 1 368.3422 300.7742 18510 "PSID266" 0 17 9 "black" 0 1 0 297.1935 54.67588 "PSID267" 0 21 14 "white" 0 0 107.7597 293.6129 7698.955 "PSID268" 0 16 9 "black" 0 1 0 277.5 3983.951 "PSID269" 0 23 13 "black" 0 0 172.4155 272.129 582.2243 "PSID270" 0 16 9 "white" 0 1 411.446 254.2258 1725.985 "PSID271" 0 17 11 "hispan" 0 1 803.2994 248.8548 5173.521 "PSID272" 0 46 7 "white" 0 1 1081.515 245.2742 0 "PSID273" 0 32 10 "white" 1 1 4145.809 238.1129 8245.714 "PSID274" 0 18 11 "white" 0 1 131.2709 218.4194 7503.896 "PSID275" 0 23 12 "hispan" 1 0 0 216.629 0 "PSID276" 0 18 10 "white" 1 1 0 211.2581 14053.18 "PSID277" 0 19 10 "black" 0 1 1056.045 205.8871 0 "PSID278" 0 16 7 "black" 0 1 133.2301 205.8871 6145.865 "PSID279" 0 26 7 "white" 1 1 1538.024 189.7742 650.1997 "PSID280" 0 16 10 "white" 0 1 0 189.7742 2136.793 "PSID281" 0 17 10 "white" 0 1 0 182.6129 6423.677 "PSID282" 0 17 10 "white" 0 1 0 171.871 1483.637 "PSID283" 0 23 8 "white" 1 1 33.30754 166.5 0 "PSID284" 0 29 12 "white" 1 0 14641.6 162.9194 9473.705 "PSID285" 0 17 10 "white" 0 1 0 152.1774 10301.23 "PSID286" 0 49 8 "white" 1 1 14684.7 136.0645 14963.46 "PSID287" 0 20 10 "white" 1 1 6563.544 134.2742 15363.92 "PSID288" 0 40 16 "white" 1 0 0 114.5806 0 "PSID289" 0 19 10 "white" 0 1 1933.796 112.7903 675.321 "PSID290" 0 18 11 "white" 0 1 1481.206 57.29032 1421.573 "PSID291" 0 16 6 "black" 0 1 0 44.75806 0 "PSID292" 0 22 8 "white" 1 1 105.8004 42.96774 209.8372 "PSID293" 0 31 12 "black" 1 0 0 42.96774 11023.84 "PSID294" 0 20 11 "white" 1 1 4478.884 39.3871 6280.338 "PSID295" 0 17 11 "hispan" 0 1 601.4949 10.74194 1913.656 "PSID296" 0 50 12 "white" 1 0 25862.32 0 25564.67 "PSID297" 0 49 14 "white" 1 0 25862.32 0 25564.67 "PSID298" 0 47 9 "white" 1 1 25862.32 0 25564.67 "PSID299" 0 34 11 "hispan" 1 1 22198.49 0 0 "PSID300" 0 22 8 "black" 1 1 16961.37 0 959.0445 "PSID301" 0 27 12 "white" 1 0 15509.56 0 12593.19 "PSID302" 0 30 10 "white" 1 1 14913.94 0 11563.21 "PSID303" 0 52 12 "white" 1 0 14780.71 0 25564.67 "PSID304" 0 43 12 "white" 1 0 13321.05 0 16860.86 "PSID305" 0 27 9 "hispan" 1 1 12829.28 0 0 "PSID306" 0 35 13 "white" 0 0 9537.711 0 11269.14 "PSID307" 0 45 12 "white" 1 0 9277.128 0 12108.49 "PSID308" 0 22 11 "black" 1 1 9049.853 0 9088.018 "PSID309" 0 22 12 "white" 1 0 9022.424 0 3342.618 "PSID310" 0 23 11 "white" 1 1 8910.745 0 4183.444 "PSID311" 0 55 7 "white" 1 1 8832.375 0 0 "PSID312" 0 26 14 "white" 0 0 8411.132 0 0 "PSID313" 0 34 12 "white" 0 0 8125.079 0 6032.08 "PSID314" 0 22 11 "white" 0 1 8013.401 0 5748.356 "PSID315" 0 31 12 "white" 0 0 6156.016 0 4094.78 "PSID316" 0 19 12 "white" 0 0 5797.47 0 2160.436 "PSID317" 0 24 10 "white" 1 1 5523.173 0 5040.525 "PSID318" 0 36 12 "white" 1 0 5374.269 0 0 "PSID319" 0 20 9 "white" 1 1 5229.283 0 15892.95 "PSID320" 0 23 8 "white" 1 1 4610.155 0 0 "PSID321" 0 35 11 "white" 1 1 3975.352 0 21963.45 "PSID322" 0 23 12 "white" 0 0 3893.063 0 16324.45 "PSID323" 0 29 10 "white" 0 1 3751.996 0 251.2135 "PSID324" 0 24 9 "white" 1 1 3438.513 0 818.6605 "PSID325" 0 18 10 "white" 0 1 3360.143 0 0 "PSID326" 0 45 8 "black" 0 1 3299.405 0 31.03226 "PSID327" 0 21 13 "hispan" 0 0 3015.312 0 17627.8 "PSID328" 0 29 13 "white" 1 0 2780.2 0 14339.86 "PSID329" 0 21 15 "white" 0 0 2629.336 0 1717.118 "PSID330" 0 22 16 "black" 0 0 2564.68 0 116.7404 "PSID331" 0 24 12 "black" 1 0 2355.039 0 2448.593 "PSID332" 0 20 14 "white" 0 0 2210.053 0 2813.591 "PSID333" 0 19 6 "black" 0 1 1955.348 0 14998.92 "PSID334" 0 19 9 "hispan" 0 1 1822.118 0 3372.172 "PSID335" 0 19 12 "black" 0 0 1681.051 0 0 "PSID336" 0 20 13 "white" 0 0 1657.54 0 913.235 "PSID337" 0 19 12 "black" 0 0 1655.58 0 0 "PSID338" 0 26 5 "white" 1 1 1573.291 0 3700.227 "PSID339" 0 26 9 "hispan" 0 1 1563.495 0 2862.356 "PSID340" 0 23 12 "white" 0 0 1504.717 0 0 "PSID341" 0 20 9 "hispan" 0 1 1500.798 0 12618.31 "PSID342" 0 20 10 "white" 0 1 1412.631 0 6290.682 "PSID343" 0 36 11 "white" 1 1 1404.794 0 0 "PSID344" 0 39 12 "white" 1 0 1289.198 0 1202.869 "PSID345" 0 17 9 "black" 0 1 1222.582 0 422.6298 "PSID346" 0 55 3 "white" 0 1 1208.868 0 0 "PSID347" 0 28 8 "white" 1 1 1202.99 0 19516.33 "PSID348" 0 19 12 "hispan" 0 0 1058.004 0 8923.991 "PSID349" 0 37 7 "white" 1 1 963.9593 0 0 "PSID350" 0 16 9 "white" 1 1 920.8554 0 15997.87 "PSID351" 0 17 10 "white" 0 1 646.558 0 9438.24 "PSID352" 0 24 12 "black" 0 0 566.2281 0 2284.565 "PSID353" 0 19 11 "white" 0 1 540.7576 0 3406.16 "PSID354" 0 50 5 "black" 1 1 411.446 0 9166.338 "PSID355" 0 19 9 "black" 0 1 384.0163 0 0 "PSID356" 0 36 1 "black" 0 1 348.7495 0 0 "PSID357" 0 18 11 "white" 0 1 321.3198 0 7722.599 "PSID358" 0 16 7 "hispan" 0 1 289.9715 0 7515.717 "PSID359" 0 21 11 "white" 1 1 246.8676 0 6708.879 "PSID360" 0 55 6 "white" 1 1 111.6782 0 0 "PSID361" 0 37 12 "white" 0 0 48.98167 0 877.7696 "PSID362" 0 26 12 "hispan" 1 0 47.0224 0 0 "PSID363" 0 54 12 "white" 1 0 0 0 0 "PSID364" 0 50 12 "white" 1 0 0 0 0 "PSID365" 0 16 8 "white" 0 1 0 0 2559.422 "PSID366" 0 16 9 "hispan" 0 1 0 0 0 "PSID367" 0 18 10 "black" 0 1 0 0 2281.61 "PSID368" 0 40 11 "black" 1 1 0 0 0 "PSID369" 0 16 8 "white" 0 1 0 0 0 "PSID370" 0 16 9 "black" 0 1 0 0 2158.959 "PSID371" 0 26 14 "white" 0 0 0 0 6717.745 "PSID372" 0 20 9 "black" 0 1 0 0 6083.8 "PSID373" 0 20 12 "black" 0 0 0 0 0 "PSID374" 0 18 11 "black" 0 1 0 0 0 "PSID375" 0 46 11 "black" 1 1 0 0 2820.98 "PSID376" 0 17 8 "black" 0 1 0 0 12760.17 "PSID377" 0 16 9 "white" 0 1 0 0 4974.028 "PSID378" 0 30 10 "white" 1 1 0 0 3151.991 "PSID379" 0 33 12 "hispan" 1 0 0 0 5841.453 "PSID380" 0 34 12 "black" 1 0 0 0 18716.88 "PSID381" 0 21 13 "black" 0 0 0 0 17941.08 "PSID382" 0 29 11 "white" 1 1 0 0 0 "PSID383" 0 19 12 "white" 0 0 0 0 0 "PSID384" 0 31 4 "hispan" 0 1 0 0 1161.493 "PSID385" 0 19 12 "hispan" 0 0 0 0 18573.55 "PSID386" 0 20 12 "black" 0 0 0 0 11594.24 "PSID387" 0 55 4 "black" 0 1 0 0 0 "PSID388" 0 19 11 "black" 0 1 0 0 16485.52 "PSID389" 0 18 11 "black" 0 1 0 0 7146.286 "PSID390" 0 48 13 "white" 1 0 0 0 0 "PSID391" 0 16 9 "hispan" 1 1 0 0 6821.186 "PSID392" 0 17 10 "black" 0 1 0 0 0 "PSID393" 0 38 12 "white" 1 0 0 0 18756.78 "PSID394" 0 34 8 "white" 1 1 0 0 2664.341 "PSID395" 0 53 12 "white" 0 0 0 0 0 "PSID396" 0 48 14 "white" 1 0 0 0 7236.427 "PSID397" 0 16 9 "white" 0 1 0 0 6494.608 "PSID398" 0 17 8 "black" 0 1 0 0 4520.366 "PSID399" 0 27 14 "black" 0 0 0 0 10122.43 "PSID400" 0 37 8 "black" 0 1 0 0 648.722 "PSID401" 0 17 10 "black" 0 1 0 0 1053.619 "PSID402" 0 16 8 "white" 0 1 0 0 0 "PSID403" 0 48 12 "white" 1 0 0 0 1491.026 "PSID404" 0 55 7 "white" 0 1 0 0 0 "PSID405" 0 21 15 "white" 0 0 0 0 0 "PSID406" 0 16 10 "black" 0 1 0 0 1730.418 "PSID407" 0 23 12 "white" 0 0 0 0 3902.676 "PSID408" 0 46 11 "black" 1 1 0 0 0 "PSID409" 0 17 10 "white" 0 1 0 0 14942.77 "PSID410" 0 42 16 "white" 0 0 0 0 23764.8 "PSID411" 0 18 10 "black" 0 1 0 0 5306.516 "PSID412" 0 53 12 "black" 0 0 0 0 0 "PSID413" 0 17 10 "white" 1 1 0 0 3859.822 "PSID414" 0 17 6 "white" 0 1 0 0 0 "PSID415" 0 43 6 "white" 1 1 0 0 0 "PSID416" 0 34 12 "black" 0 0 0 0 0 "PSID417" 0 16 8 "hispan" 0 1 0 0 12242.96 "PSID418" 0 27 12 "white" 1 0 0 0 1533.88 "PSID419" 0 51 4 "black" 0 1 0 0 0 "PSID420" 0 39 2 "black" 1 1 0 0 964.9555 "PSID421" 0 55 8 "white" 1 1 0 0 0 "PSID422" 0 16 9 "white" 0 1 0 0 5551.819 "PSID423" 0 27 10 "black" 0 1 0 0 7543.794 "PSID424" 0 25 14 "white" 0 0 0 0 0 "PSID425" 0 18 11 "white" 0 1 0 0 10150.5 "PSID426" 0 24 1 "hispan" 1 1 0 0 19464.61 "PSID427" 0 21 18 "white" 0 0 0 0 0 "PSID428" 0 32 5 "black" 1 1 0 0 187.6713 "PSID429" 0 16 9 "white" 0 1 0 0 1495.459 MatchIt/man/0000755000176200001440000000000014170752616012367 5ustar liggesusersMatchIt/man/matchit.Rd0000644000176200001440000005605114146245513014312 0ustar liggesusers\name{matchit} \alias{matchit} \alias{MatchIt} \alias{print.matchit} \title{ Matching for Causal Inference } \description{ \code{matchit()} is the main function of \emph{MatchIt} and performs pairing, subset selection, and subclassification with the aim of creating treatment and control groups balanced on included covariates. MatchIt implements the suggestions of Ho, Imai, King, and Stuart (2007) for improving parametric statistical models by preprocessing data with nonparametric matching methods. MatchIt implements a wide range of sophisticated matching methods, making it possible to greatly reduce the dependence of causal inferences on hard-to-justify, but commonly made, statistical modeling assumptions. The software also easily fits into existing research practices since, after preprocessing with MatchIt, researchers can use whatever parametric model they would have used without MatchIt, but produce inferences with substantially more robustness and less sensitivity to modeling assumptions. This page documents the overall use of \code{matchit()}, but for specifics of how \code{matchit()} works with individual matching methods, see the individual pages linked in the Details section below. } \usage{ matchit(formula, data = NULL, method = "nearest", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", exact = NULL, mahvars = NULL, antiexact = NULL, discard = "none", reestimate = FALSE, s.weights = NULL, replace = FALSE, m.order = NULL, caliper = NULL, std.caliper = TRUE, ratio = 1, verbose = FALSE, include.obj = FALSE, ...) \method{print}{matchit}(x, ...) } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the distance measure used in the matching. This formula will be supplied to the functions that estimate the distance measure. The formula should be specified as \code{A ~ X1 + X2 + ...} where \code{A} represents the treatment variable and \code{X1} and \code{X2} are covariates. } \item{data}{ a data frame containing the variables named in \code{formula} and possible other arguments. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ the matching method to be used. The allowed methods are \code{\link[=method_nearest]{"nearest"}} for nearest neighbor matching (on the propensity score by default), \code{\link[=method_optimal]{"optimal"}} for optimal pair matching, \code{\link[=method_full]{"full"}} for optimal full matching, \code{\link[=method_genetic]{"genetic"}} for genetic matching, \code{\link[=method_cem]{"cem"}} for coarsened exact matching, \code{\link[=method_exact]{"exact"}} for exact matching, and \code{\link[=method_subclass]{"subclass"}} for subclassification. When set to \code{NULL}, no matching will occur, but propensity score estimation and common support restrictions will still occur if requested. See the linked pages for each method for more details on what these methods do, how the arguments below are used by each on, and what additional arguments are allowed. } \item{distance}{ the distance measure to be used. Can be either a string containing the name of a distance measure, a vector of already-computed distance measures, or a matrix of pairwise distances. When supplied as a vector, the distance measures should be values whose pairwise difference is the distance between two units, e.g., propensity scores for propensity score matching. When supplied as a matrix, each value should represent the pairwise distance between units. See \code{\link{distance}} for allowable options. The default is \code{"glm"} for propensity scores estimated with logistic regression using \fun{glm}. Ignored for some methods; see individual methods pages for information on whether and how the distance measure is used. } \item{link}{ when \code{distance} is specified as a string, an additional argument controlling the link function used in estimating the distance measure. Allowable options depend on the specific \code{distance} value specified. See \code{\link{distance}} for allowable options with each option. The default is \code{"logit"}, which, along with \code{distance = "glm"}, identifies the default measure as logistic regression propensity scores. } \item{distance.options}{ a named list containing additional arguments supplied to the function that estimates the distance measure as determined by the argument to \code{distance}. See \code{\link{distance}} for an example of its use. } \item{estimand}{ a string containing the name of the target estimand desired. Can be one of \code{"ATT"} or \code{"ATC"}. Some methods accept \code{"ATE"} as well. Default is \code{"ATT"}. See Details and the individual methods pages for information on how this argument is used. } \item{exact}{ for methods that allow it, for which variables exact matching should take place. Can be specified as a string containing the names of variables in \code{data} to be used or a one-sided formula with the desired variables on the right-hand side (e.g., \code{~ X3 + X4}). See the individual methods pages for information on whether and how this argument is used. } \item{mahvars}{ for methods that allow it, on which variables Mahalanobis distance matching should take place when a distance measure other than \code{"mahalanobis"} is used. Usually used to perform Mahalanobis distance matching within propensity score calipers, where the propensity scores are computed using \code{formula} and \code{distance}. Can be specified as a string containing the names of variables in \code{data} to be used or a one-sided formula with the desired variables on the right-hand side (e.g., \code{~ X3 + X4}). See the individual methods pages for information on whether and how this argument is used. } \item{antiexact}{ for methods that allow it, for which variables anti-exact matching should take place. Anti-exact matching ensures paired individuals do not have the same value of the anti-exact matching variable(s). Can be specified as a string containing the names of variables in \code{data} to be used or a one-sided formula with the desired variables on the right-hand side (e.g., \code{~ X3 + X4}). See the individual methods pages for information on whether and how this argument is used. } \item{discard}{ a string containing a method for discarding units outside a region of common support. When a propensity score is estimated or supplied to \code{distance} as a vector, the options are \code{"none"}, \code{"treated"}, \code{"control"}, or \code{"both"}. For \code{"none"}, no units are discarded for common support. Otherwise, units whose propensity scores fall outside the corresponding region are discarded. Can also be a \code{logical} vector where \code{TRUE} indicates the unit is to be discarded. Default is \code{"none"} for no common support restriction. See Details. } \item{reestimate}{ if \code{discard} is not \code{"none"} and propensity scores are estimated, whether to re-estimate the propensity scores in the remaining sample. Default is \code{FALSE} to use the propensity scores estimated in the original sample. } \item{s.weights}{ an optional numeric vector of sampling weights to be incorporated into propensity score models and balance statistics. Can also be specified as a string containing the name of variable in \code{data} to be used or a one-sided formula with the variable on the right-hand side (e.g., \code{~ SW}). Not all propensity score models accept sampling weights; see \code{\link{distance}} for information on which do and do not, and see \code{vignette("sampling-weights")} for details on how to use sampling weights in a matching analysis. } \item{replace}{ for methods that allow it, whether matching should be done with replacement (\code{TRUE}), where control units are allowed to be matched to several treated units, or without replacement (\code{FALSE}), where control units can only be matched to one treated unit each. See the individual methods pages for information on whether and how this argument is used. Default is \code{FALSE} for matching without replacement. } \item{m.order}{ for methods that allow it, the order that the matching takes place. Allowable options depend on the matching method but include \code{"largest"}, where matching takes place in descending order of distance measures; \code{"smallest"}, where matching takes place in ascending order of distance measures; \code{"random"}, where matching takes place in a random order; and \code{"data"} where matching takes place based on the order of units in the data. When \code{m.order = "random"}, results may differ across different runs of the same code unless a seed is set and specified with \fun{set.seed}. See the individual methods pages for information on whether and how this argument is used. The default of \code{NULL} corresponds to \code{"largest"} when a propensity score is estimated or supplied as a vector and \code{"data"} otherwise. } \item{caliper}{ for methods that allow it, the width(s) of the caliper(s) to use in matching. Should be a numeric vector with each value named according to the variable to which the caliper applies. To apply to the distance measure, the value should be unnamed. See the individual methods pages for information on whether and how this argument is used. The default is \code{NULL} for no caliper. } \item{std.caliper}{ \code{logical}; when a caliper is specified, whether the the caliper is in standard deviation units (\code{TRUE}) or raw units (\code{FALSE}). Can either be of length 1, applying to all calipers, or of length equal to the length of \code{caliper}. Default is \code{TRUE}. } \item{ratio}{ for methods that allow it, how many control units should be matched to each treated unit in k:1 matching. Should be a single integer value. See the individual methods pages for information on whether and how this argument is used. The default is 1 for 1:1 matching. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. What is printed depends on the matching method. Default is \code{FALSE} for no printing other than warnings. } \item{include.obj}{ \code{logical}; whether to include any objects created in the matching process in the output, i.e., by the functions from other packages \code{matchit()} calls. What is included depends on the matching method. Default is \code{FALSE}. } \item{\dots}{ additional arguments passed to the functions used in the matching process. See the individual methods pages for information on what additional arguments are allowed for each method. Ignored for \code{print}. } \item{x}{ a \code{matchit} object. } } \details{ Details for the various matching methods can be found at the following help pages: \itemize{ \item{\code{\link{method_nearest}} for nearest neighbor matching} \item{\code{\link{method_optimal}} for optimal pair matching} \item{\code{\link{method_full}} for optimal full matching} \item{\code{\link{method_genetic}} for genetic matching} \item{\code{\link{method_cem}} for coarsened exact matching} \item{\code{\link{method_exact}} for exact matching} \item{\code{\link{method_subclass}} for subclassification} \item{\code{\link{method_cardinality}} for cardinality and template matching} } The pages contain information on what the method does, which of the arguments above are allowed with them and how they are interpreted, and what additional arguments can be supplied to further tune the method. Note that the default method with no arguments supplied other than \code{formula} and \code{data} is 1:1 nearest neighbor matching without replacement on a propensity score estimated using a logistic regression of the treatment on the covariates. This is not the same default offered by other matching programs, such as those in \emph{Matching}, \code{teffects} in Stata, or \code{PROC PSMATCH} in SAS, so care should be taken if trying to replicate the results of those programs. When \code{method = NULL}, no matching will occur, but any propensity score estimation and common support restriction will. This can be a simple way to estimate the propensity score for use in future matching specifications without having to reestimate it each time. The \code{matchit()} output with no matching can be supplied to \code{summary} to examine balance prior to matching on any of the included covariates and on the propensity score if specified. All arguments other than \code{distance}, \code{discard}, and \code{reestimate} will be ignored. See the \code{\link{distance}} argument for details on the several ways to specify the \code{distance} and \code{link} arguments to estimate propensity scores and create distance measures. When the treatment variable is not a \code{0/1} variable, it will be coerced to one and returned as such in the \code{matchit()} output (see section Value, below). The following rules are used: 1) if \code{0} is one of the values, it will be considered the control and the other value the treated; otherwise, 2) if the variable is a factor, \code{levels(treat)[1]} will be considered control and the other variable the treated; otherwise, 3) \code{sort(unique(treat))[1]} will be considered control and the other value the treated. It is safest to ensure the treatment variable is a \code{0/1} variable. The \code{discard} option implements a common support restriction. It can only be used when a distance measure is estimated or supplied as a vector, i.e., when \code{distance} is specified as something other than \code{"mahalanobis"} and is not a matrix, and is ignored for some matching methods. When specified as \code{"treated"}, treated units whose distance measure is outside the range of distance measures of the control units will be discarded. When specified as \code{"control"}, control units whose distance measure is outside the range of distance measures of the treated units will be discarded. When specified as \code{"both"}, treated and controls units whose distance measure is outside the intersection of the range of distance measures of the treated units and the range of distance measures of the control units will be discarded. When \code{reestimate = TRUE} and \code{distance} corresponds to a propensity score-estimating function, the propensity scores are re-estimated in the remaining units prior to being used for matching or calipers. Caution should be used when interpreting effects estimated with various values of \code{estimand}. Setting \code{estimand = "ATT"} doesn't necessarily mean the average treatment effect in the treated is being estimated; it just means that for matching methods, treated units will be untouched and given weights of 1 and control units will be matched to them (and the opposite for \code{estimand = "ATC"}). If a caliper is supplied or treated units are removed for common support or some other reason (e.g., lacking matches when using exact matching), the actual estimand targeted is not the ATT but the treatment effect in the matched sample. The argument to \code{estimand} simply triggers which units are matched to which, and for stratification-based methods (exact matching, CEM, full matching, and subclassification), determines the formula used to compute the stratification weights. \subsection{How Matching Weights Are Computed}{ Matching weights are computed in one of two ways depending on whether matching was done with replacement or not. For matching without replacement, each unit is assigned to a subclass, which represents the pair they are a part of (in the case of k:1 matching) or the stratum they belong to (in the case of exact matching, coarsened exact matching, full matching, or subclassification). The formula for computing the weights depends on the argument supplied to \code{estimand}. A new stratum "propensity score" (\code{p}) is computed as the proportion of units in each stratum that are in the treated group, and all units in that stratum are assigned that propensity score. Weights are then computed using the standard formulas for inverse probability weights: for the ATT, weights are 1 for the treated units and \code{p/(1-p)} for the control units; for the ATC, weights are \code{(1-p)/p} for the treated units and 1 for the control units; for the ATE, weights are \code{1/p} for the treated units and \code{1/(1-p)} for the control units. For matching with replacement, units are not assigned to unique strata. For the ATT, each treated unit gets a weight of 1. Each control unit is weighted as the sum of the inverse of the number of control units matched to the same treated unit across its matches. For example, if a control unit was matched to a treated unit that had two other control units matched to it, and that same control was matched to a treated unit that had one other control unit matched to it, the control unit in question would get a weight of 1/3 + 1/2 = 5/6. For the ATC, the same is true with the treated and control labels switched. The weights are computed using the \code{match.matrix} component of the \code{matchit()} output object. In each treatment group, weights are divided by the mean of the nonzero weights in that treatment group to make the weights sum to the number of units in that treatment group. If sampling weights are included through the \code{s.weights} argument, they will be included in the \code{matchit()} output object but not incorporated into the matching weights. \fun{match.data}, which extracts the matched set from a \code{matchit} object, combines the matching weights and sampling weights. } } \value{ When \code{method} is something other than \code{"subclass"}, a \code{matchit} object with the following components: \item{match.matrix}{a matrix containing the matches. The rownames correspond to the treated units and the values in each row are the names (or indices) of the control units matched to each treated unit. When treated units are matched to different numbers of control units (e.g., with exact matching or matching with a caliper), empty spaces will be filled with \code{NA}. Not included when \code{method} is \code{"full"}, \code{"cem"}, \code{"exact"}, or \code{"cardinality"}.} \item{subclass}{a factor containing matching pair/stratum membership for each unit. Unmatched units will have a value of \code{NA}. Not included when \code{replace = TRUE}.} \item{weights}{a numeric vector of estimated matching weights. Unmatched and discarded units will have a weight of zero.} \item{model}{the fit object of the model used to estimate propensity scores when \code{distance} is specified and not \code{"mahalanobis"} or a numeric vector. When \code{reestimate = TRUE}, this is the model estimated after discarding units.} \item{X}{a data frame of covariates mentioned in \code{formula}, \code{exact}, \code{mahvars}, and \code{antiexact}.} \item{call}{the \code{matchit()} call.} \item{info}{information on the matching method and distance measures used.} \item{estimand}{the argument supplied to \code{estimand}.} \item{formula}{the \code{formula} supplied.} \item{treat}{a vector of treatment status converted to zeros (0) and ones (1) if not already in that format.} \item{distance}{a vector of distance values (i.e., propensity scores) when \code{distance} is specified and not \code{"mahalanobis"} or a matrix.} \item{discarded}{a logical vector denoting whether each observation was discarded (\code{TRUE}) or not (\code{FALSE}) by the argument to \code{discard}.} \item{s.weights}{the vector of sampling weights supplied to the \code{s.weights} argument, if any.} \item{exact}{a one-sided formula containing the variables, if any, supplied to \code{exact}.} \item{mahvars}{a one-sided formula containing the variables, if any, supplied to \code{mahvars}.} \item{obj}{when \code{include.obj = TRUE}, an object containing the intermediate results of the matching procedure. See the individual methods pages for what this component will contain.} When \code{method = "subclass"}, a \code{matchit.subclass} object with the same components as above except that \code{match.matrix} is excluded and one additional component, \code{q.cut}, is included, containing a vector of the distance measure cutpoints used to define the subclasses. See \code{\link{method_subclass}} for details. } \references{ Ho, D. E., Imai, K., King, G., & Stuart, E. A. (2007). Matching as Nonparametric Preprocessing for Reducing Model Dependence in Parametric Causal Inference. Political Analysis, 15(3), 199–236. \doi{10.1093/pan/mpl013} Ho, D. E., Imai, K., King, G., & Stuart, E. A. (2011). MatchIt: Nonparametric Preprocessing for Parametric Causal Inference. Journal of Statistical Software, 42(8). \doi{10.18637/jss.v042.i08} } \author{ Daniel Ho \email{dho@law.stanford.edu}; Kosuke Imai \email{imai@harvard.edu}; Gary King \email{king@harvard.edu}; Elizabeth Stuart \email{estuart@jhsph.edu} Version 4.0.0 update by Noah Greifer \email{noah.greifer@gmail.com} } \seealso{ \fun{summary.matchit} for balance assessment after matching. \fun{plot.matchit} for plots of covariate balance and propensity score overlap after matching. \code{vignette("MatchIt")} for an introduction to matching with \emph{MatchIt}; \code{vignette("matching-methods")} for descriptions of the variety of matching methods and options available; \code{vignette("assessing-balance")} for information on assessing the quality of a matching specification; \code{vignette("estimating-effects")} for instructions on how to estimate treatment effects after matching; and \code{vignette("sampling-weights")} for a guide to using \emph{MatchIt} with sampling weights. } \examples{ data("lalonde") # Default: 1:1 NN PS matching w/o replacement m.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde) m.out1 summary(m.out1) # 1:1 NN Mahalanobis distance matching w/ replacement and # exact matching on married and race m.out2 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, distance = "mahalanobis", replace = TRUE, exact = ~ married + race) m.out2 summary(m.out2) # 2:1 NN Mahalanobis distance matching within caliper defined # by a probit pregression PS m.out3 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, distance = "glm", link = "probit", mahvars = ~ age + educ + re74 + re75, caliper = .1, ratio = 2) m.out3 summary(m.out3) # Optimal full PS matching for the ATE within calipers on # PS, age, and educ m.out4 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "full", estimand = "ATE", caliper = c(.1, age = 2, educ = 1), std.caliper = c(TRUE, FALSE, FALSE)) m.out4 summary(m.out4) # Subclassification on a logistic PS with 10 subclasses after # discarding controls outside common support of PS s.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "subclass", distance = "glm", discard = "control", subclass = 10) s.out1 summary(s.out1) } MatchIt/man/add_s.weights.Rd0000644000176200001440000000462414044455776015416 0ustar liggesusers\name{add_s.weights} \alias{add_s.weights} \title{ Add sampling weights to a \code{matchit} object } \description{ Adds sampling weights to a \code{matchit} object so that they are incorporated into balance assessment and creation of the weights. This would typically only be used when an argument to \code{s.weights} was not supplied to \fun{matchit} (i.e., because they were not to be included in the estimation of the propensity score) but sampling weights are required for generalizing an effect to the correct population. Without adding sampling weights to the \code{matchit} object, balance assessment tools (i.e., \fun{summary.matchit} and \fun{plot.matchit}) will not calculate balance statistics correctly, and the weights produced by \fun{match.data} and \fun{get_matches} will not incorporate the sampling weights. } \usage{ add_s.weights(m, s.weights = NULL, data = NULL) } \arguments{ \item{m}{ a \code{matchit} object; the output of a call to \fun{matchit}, typically with the \code{s.weights} argument unspecified. } \item{s.weights}{ an numeric vector of sampling weights to be added to the \code{matchit} object. Can also be specified as a string containing the name of variable in \code{data} to be used or a one-sided formula with the variable on the right-hand side (e.g., \code{~ SW}). } \item{data}{ a data frame containing the sampling weights if given as a string or formula. If unspecified, \code{add_s.weights} will attempt to find the dataset using the environment of the \code{matchit} object. } } \value{ a \code{matchit} object with an \code{s.weights} component containing the supplied sampling weights. The \code{nn} component containing the sample sizes before and after matching will be adjusted to incorporate the sampling weights. If \code{s.weights = NULL}, the original \code{matchit} object is returned. } \author{ Noah Greifer } \seealso{ \fun{matchit}; \fun{match.data} } \examples{ data("lalonde") # Generate random sampling weights, just # for this example sw <- rchisq(nrow(lalonde), 2) # NN PS match using logistic regression PS that doesn't # include sampling weights m.out <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde) m.out # Add s.weights to the matchit object m.out <- add_s.weights(m.out, sw) m.out #note additional output # Check balance; note that sample sizes incorporate # s.weights summary(m.out, improvement = FALSE) } MatchIt/man/method_full.Rd0000644000176200001440000002600714110073157015153 0ustar liggesusers\name{method_full} \alias{method_full} \title{Optimal Full Matching} \description{ In \fun{matchit}, setting \code{method = "full"} performs optimal full matching, which is a form of subclassification wherein all units, both treatment and control (i.e., the "full" sample), are assigned to a subclass and receive at least one match. The matching is optimal in the sense that that sum of the absolute distances between the treated and control units in each subclass is as small as possible. The method relies on and is a wrapper for \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch}. Advantages of optimal full matching include that the matching order is not required to be specified, units do not need to be discarded, and it is less likely that extreme within-subclass distances will be large, unlike with standard subclassification. The primary output of full matching is a set of matching weights that can be applied to the matched sample; in this way, full matching can be seen as a robust alternative to propensity score weighting, robust in the sense that the propensity score model does not need to be correct to estimate the treatment effect without bias. This page details the allowable arguments with \code{method = "fullmatch"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for optimal full matching: \preformatted{ matchit(formula, data = NULL, method = "full", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", exact = NULL, mahvars = NULL, anitexact = NULL, discard = "none", reestimate = FALSE, s.weights = NULL, caliper = NULL, std.caliper = TRUE, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the distance measure used in the matching. This formula will be supplied to the functions that estimate the distance measure. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"full"}. } \item{distance}{ the distance measure to be used. See \code{\link{distance}} for allowable options. When set to \code{"mahalanobis"}, optimal full Mahalanobis distance matching will be performed on the variables named in \code{formula}. Can be supplied as a distance matrix. } \item{link}{ when \code{distance} is specified as a string and not \code{"mahalanobis"}, an additional argument controlling the link function used in estimating the distance measure. See \code{\link{distance}} for allowable options with each option. } \item{distance.options}{ a named list containing additional arguments supplied to the function that estimates the distance measure as determined by the argument to \code{distance}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"}, \code{"ATC"}, and \code{"ATE"}. The estimand controls how the weights are computed; see the Computing Weights section at \fun{matchit} for details. } \item{exact}{ for which variables exact matching should take place. Exact matching is processed using \pkgfun2{optmatch}{exactMatch}{optmatch::exactMatch}. } \item{mahvars}{ for which variables Mahalanobis distance matching should take place when a distance measure other than \code{"mahalanobis"} is used (e.g., for caliper matching or to discard units for common support). If specified, the distance measure will not be used in matching. } \item{antiexact}{ for which variables ant-exact matching should take place. Anti-exact matching is processed using \pkgfun2{optmatch}{antiExactMatch}{optmatch::antiExactMatch}. } \item{discard}{ a string containing a method for discarding units outside a region of common support. Only allowed when \code{distance} is not \code{"mahalanobis"} and not a matrix. } \item{reestimate}{ if \code{discard} is not \code{"none"}, whether to re-estimate the propensity score in the remaining sample prior to matching. } \item{s.weights}{ the variable containing sampling weights to be incorporated into propensity score models and balance statistics. } \item{caliper}{ the width(s) of the caliper(s) used for caliper matching. Calipers are processed by \pkgfun2{optmatch}{caliper}{optmatch::caliper} See Notes and Examples. } \item{std.caliper}{ \code{logical}; when calipers are specified, whether they are in standard deviation units (\code{TRUE}) or raw units (\code{FALSE}). } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. } \item{\dots}{ additional arguments passed to \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch}. Allowed arguments include \code{min.controls}, \code{max.controls}, \code{omit.fraction}, \code{mean.controls}, and \code{tol}. See the \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch} documentation for details. } The arguments \code{replace}, \code{m.order}, and \code{ratio} are ignored with a warning. } \note{ Due to what appears to be a bug in \emph{optmatch} (version 0.9-13), calipers can only be used when \code{min.controls} is left at its default. The option \code{"optmatch_max_problem_size"} is automatically set to \code{Inf} during the matching process, different from its default in \emph{optmatch}. This enables matching problems of any size to be run, but may also let huge, infeasible problems get through and potentially take a long time or crash R. See \pkgfun2{optmatch}{setMaxProblemSize}{optmatch::setMaxProblemSize} for more details. } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "full"} except for \code{match.matrix}. This is because matching strata are not indexed by treated units as they are in some other forms of matching. When \code{include.obj = TRUE} in the call to \code{matchit()}, the output of the call to \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch} will be included in the output. When \code{exact} is specified, this will be a list of such objects, one for each stratum of the exact variables. } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch}, which is the workhorse. \code{\link{method_optimal}} for optimal pair matching, which is a special case of optimal full matching, and which relies on similar machinery. Results from \code{method = "optimal"} can be replicated with \code{method = "full"} by setting \code{min.controls}, \code{max.controls}, and \code{mean.controls} to the desired \code{ratio}. } \details{ \subsection{Mahalanobis Distance Matching}{ Mahalanobis distance matching can be done one of two ways: 1) If no propensity score needs to be estimated, \code{distance} should be set to \code{"mahalanobis"}, and Mahalanobis distance matching will occur on all the variables in \code{formula}. Arguments to \code{discard} and \code{mahvars} will be ignored, and a caliper can only be placed on named variables. For example, to perform simple Mahalanobis distance matching, the following could be run: \preformatted{ matchit(treat ~ X1 + X2, method = "nearest", distance = "mahalanobis") } With this code, the Mahalanobis distance is computed using \code{X1} and \code{X2}, and matching occurs on this distance. The \code{distance} component of the \code{matchit()} output will be empty. 2) If a propensity score needs to be estimated for any reason, e.g., for common support with \code{discard} or for creating a caliper, \code{distance} should be whatever method is used to estimate the propensity score or a vector of distance measures, i.e., it should not be \code{"mahalanobis"}. Use \code{mahvars} to specify the variables used to create the Mahalanobis distance. For example, to perform Mahalanobis within a propensity score caliper, the following could be run: \preformatted{ matchit(treat ~ X1 + X2 + X3, method = "nearest", distance = "glm", caliper = .25, mahvars = ~ X1 + X2) } With this code, \code{X1}, \code{X2}, and \code{X3} are used to estimate the propensity score (using the \code{"glm"} method, which by default is logistic regression), which is used to create a matching caliper. The actual matching occurs on the Mahalanobis distance computed only using \code{X1} and \code{X2}, which are supplied to \code{mahvars}. Units whose propensity score difference is larger than the caliper will not be paired, and some treated units may therefore not receive a match. The estimated propensity scores will be included in the \code{distance} component of the \code{matchit()} output. See Examples. When sampling weights are supplied through the \code{s.weights} argument, the covariance matrix of the covariates used in the Mahalanobis distance is \strong{not} weighted by the sampling weights. Mahalanobis distance matching can also be done by supplying a Mahalanobis distance matrix (e.g., the output of a call to \pkgfun2{optmatch}{match_on}{optmatch::match_on}) to the \code{distance} argument. This makes it straightforward to use the robust rank-based Mahalanobis distance available in \pkg{optmatch}. } } \references{ In a manuscript, be sure to cite the following paper if using \code{matchit()} with \code{method = "full"}: Hansen, B. B., & Klopfer, S. O. (2006). Optimal Full Matching and Related Designs via Network Flows. Journal of Computational and Graphical Statistics, 15(3), 609–627. \doi{10.1198/106186006X137047} For example, a sentence might read: \emph{Optimal full matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R, which calls functions from the optmatch package (Hansen & Klopfer, 2006).} Theory is also developed in the following article: Hansen, B. B. (2004). Full Matching in an Observational Study of Coaching for the SAT. Journal of the American Statistical Association, 99(467), 609–618. \doi{10.1198/016214504000000647} } \examples{\dontshow{if (requireNamespace("optmatch", quietly = TRUE)) \{} data("lalonde") # Optimal full PS matching m.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "full") m.out1 summary(m.out1) # Optimal full Mahalanobis distance matching within a PS caliper m.out2 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "full", caliper = .01, mahvars = ~ age + educ + re74 + re75) m.out2 summary(m.out2, un = FALSE) # Optimal full Mahalanobis distance matching within calipers # of 500 on re74 and re75 m.out3 <- matchit(treat ~ age + educ + re74 + re75, data = lalonde, distance = "mahalanobis", method = "full", caliper = c(re74 = 500, re75 = 500), std.caliper = FALSE) m.out3 summary(m.out3, addlvariables = ~race + nodegree + married, data = lalonde, un = FALSE) \dontshow{\}}} MatchIt/man/method_nearest.Rd0000644000176200001440000003250414125224621015651 0ustar liggesusers\name{method_nearest} \alias{method_nearest} \title{ Nearest Neighbor Matching } \description{ In \fun{matchit}, setting \code{method = "nearest"} performs greedy nearest neighbor matching. A distance is computed between each treated unit and each control unit, and, one by one, each treated unit is assigned a control unit as a match. The matching is "greedy" in the sense that there is no action taken to optimize an overall criterion; each match is selected without considering the other matches that may occur subsequently. This page details the allowable arguments with \code{method = "nearest"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for nearest neighbor matching: \preformatted{ matchit(formula, data = NULL, method = "nearest", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", exact = NULL, mahvars = NULL, antiexact = NULL, discard = "none", reestimate = FALSE, s.weights = NULL, replace = TRUE, m.order = NULL, caliper = NULL, ratio = 1, min.controls = NULL, max.controls = NULL, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the distance measure used in the matching. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"nearest"}. } \item{distance}{ the distance measure to be used. See \code{\link{distance}} for allowable options. Can be supplied as a distance matrix. } \item{link}{ when \code{distance} is specified as a string and not \code{"mahalanobis"}, an additional argument controlling the link function used in estimating the distance measure. See \code{\link{distance}} for allowable options with each option. } \item{distance.options}{ a named list containing additional arguments supplied to the function that estimates the distance measure as determined by the argument to \code{distance}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"} and \code{"ATC"}. See Details. } \item{exact}{ for which variables exact matching should take place. } \item{mahvars}{ for which variables Mahalanobis distance matching should take place when a distance measure other than \code{"mahalanobis"} is used (e.g., for caliper matching or to discard units for common support). If specified, the distance measure will not be used in matching. } \item{antiexact}{ for which variables ant-exact matching should take place. } \item{discard}{ a string containing a method for discarding units outside a region of common support. Only allowed when \code{distance} is not \code{"mahalanobis"} and not a matrix. } \item{reestimate}{ if \code{discard} is not \code{"none"}, whether to re-estimate the propensity score in the remaining sample prior to matching. } \item{s.weights}{ the variable containing sampling weights to be incorporated into propensity score models and balance statistics. } \item{replace}{ whether matching should be done with replacement. } \item{m.order}{ the order that the matching takes place. The default is \code{"data"} for \code{distance = "mahalanobis"} or when \code{distance} is supplied as a matrix, and \code{"largest"} otherwise. See \fun{matchit} for allowable options. } \item{caliper}{ the width(s) of the caliper(s) used for caliper matching. See Details and Examples. } \item{std.caliper}{ \code{logical}; when calipers are specified, whether they are in standard deviation units (\code{TRUE}) or raw units (\code{FALSE}). } \item{ratio}{ how many control units should be matched to each treated unit for k:1 matching. For variable ratio matching, see section "Variable Ratio Matching" in Details below. } \item{min.controls, max.controls}{ for variable ratio matching, the minimum and maximum number of controls units to be matched to each treated unit. See section "Variable Ratio Matching" in Details below. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. When \code{TRUE}, a progress bar implemented using \emph{RcppProgress} will be displayed. } \item{\dots}{ additional arguments that control the matching specification: \describe{ \item{\code{reuse.max}}{ \code{numeric}; the maximum number of times each control can be used as a match. Setting \code{reuse.max = 1} correpsonds to matching without replacement (i.e., \code{replace = FALSE}), and setting \code{reuse.max = Inf} corresponds to traditional matching with replacement (i.e., \code{replace = TRUE}) with no limit on the number of times each control unit can be matched. Other values restrict the number of times each control can be matched when matching with replacement. \code{replace} is ignored when \code{reuse.max} is specified. } } } } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "nearest"}. When \code{replace = TRUE}, the \code{subclass} component is omitted. \code{include.obj} is ignored. } \details{ \subsection{Mahalanobis Distance Matching}{ Mahalanobis distance matching can be done one of two ways: 1) If no propensity score needs to be estimated, \code{distance} should be set to \code{"mahalanobis"}, and Mahalanobis distance matching will occur on all the variables in \code{formula}. Arguments to \code{discard} and \code{mahvars} will be ignored, and a caliper can only be placed on named variables. For example, to perform simple Mahalanobis distance matching, the following could be run: \preformatted{ matchit(treat ~ X1 + X2, method = "nearest", distance = "mahalanobis") } With this code, the Mahalanobis distance is computed using \code{X1} and \code{X2}, and matching occurs on this distance. The \code{distance} component of the \code{matchit()} output will be empty. 2) If a propensity score needs to be estimated for any reason, e.g., for common support with \code{discard} or for creating a caliper, \code{distance} should be whatever method is used to estimate the propensity score or a vector of distance measures, i.e., it should not be \code{"mahalanobis"}. Use \code{mahvars} to specify the variables used to create the Mahalanobis distance. For example, to perform Mahalanobis within a propensity score caliper, the following could be run: \preformatted{ matchit(treat ~ X1 + X2 + X3, method = "nearest", distance = "glm", caliper = .25, mahvars = ~ X1 + X2) } With this code, \code{X1}, \code{X2}, and \code{X3} are used to estimate the propensity score (using the \code{"glm"} method, which by default is logistic regression), which is used to create a matching caliper. The actual matching occurs on the Mahalanobis distance computed only using \code{X1} and \code{X2}, which are supplied to \code{mahvars}. Units whose propensity score difference is larger than the caliper will not be paired, and some treated units may therefore not receive a match. The estimated propensity scores will be included in the \code{distance} component of the \code{matchit()} output. See Examples. When sampling weights are supplied through the \code{s.weights} argument, the covariance matrix of the covariates used in the Mahalanobis distance is weighted by the sampling weights. Mahalanobis distance matching can also be done by supplying a Mahalanobis distance matrix (e.g., the output of a call to \pkgfun2{optmatch}{match_on}{optmatch::match_on}) to the \code{distance} argument. This makes it straightforward to use the robust rank-based Mahalanobis distance available in \pkg{optmatch}. } \subsection{Estimand}{ The \code{estimand} argument controls whether control units are selected to be matched with treated units (\code{estimand = "ATT"}) or treated units are selected to be matched with control units (\code{estimand = "ATC"}). The "focal" group (e.g., the treated units for the ATT) is typically made to be the smaller treatment group, and a warning will be thrown if it is not set that way unless \code{replace = TRUE}. Setting \code{estimand = "ATC"} is equivalent to swapping all treated and control labels for the treatment variable. When \code{estimand = "ATC"}, the default \code{m.order} is \code{"smallest"}, and the \code{match.matrix} component of the output will have the names of the control units as the rownames and be filled with the names of the matched treated units (opposite to when \code{estimand = "ATT"}). Note that the argument supplied to \code{estimand} doesn't necessarily correspond to the estimand actually targeted; it is merely a switch to trigger which treatment group is considered "focal". } \subsection{Variable Ratio Matching}{ \code{matchit()} can perform variable ratio "extremal" matching as described by Ming and Rosenbaum (2000). This method tends to result in better balance than fixed ratio matching at the expense of some precision. When \code{ratio > 1}, rather than requiring all treated units to receive \code{ratio} matches, each treated unit is assigned a value that corresponds to the number of control units they will be matched to. These values are controlled by the arguments \code{min.controls} and \code{max.controls}, which correspond to \ifelse{latex}{\out{$\alpha$}}{\ifelse{html}{\out{α}}{alpha}} and \ifelse{latex}{\out{$\beta$}}{\ifelse{html}{\out{β}}{beta}}, respectively, in Ming and Rosenbaum (2000), and trigger variable ratio matching to occur. Some treated units will receive \code{min.controls} matches and others will receive \code{max.controls} matches (and one unit may have an intermediate number of matches); how many units are assigned each number of matches is determined by the algorithm described in Ming and Rosenbaum (2000, p119). \code{ratio} controls how many total control units will be matched: \code{n1 * ratio} control units will be matched, where \code{n1} is the number of treated units, yielding the same total number of matched controls as fixed ratio matching does. Variable ratio matching cannot be used with Mahalanobis distance matching or when \code{distance} is supplied as a matrix. The calculations of the numbers of control units each treated unit will be matched to occurs without consideration of \code{caliper} or \code{discard}. \code{ratio} does not have to be an integer but must be greater than 1 and less than \code{n0/n1}, where \code{n0} and \code{n1} are the number of control and treated units, respectively. Setting \code{ratio = n0/n1} performs a crude form of full matching where all control units are matched. If \code{min.controls} is not specified, it is set to 1 by default. \code{min.controls} must be less than \code{ratio}, and \code{max.controls} must be greater than \code{ratio}. See Examples below for an example of their use. } } \note{ Sometimes an error will be produced by \emph{Rcpp} along the lines of \code{"function 'Rcpp_precious_remove' not provided by package 'Rcpp'"}. It is not immediately clear why this happens, though \href{https://lists.r-forge.r-project.org/pipermail/rcpp-devel/2021-July/010648.html}{this} thread appears to provide some insight. In a fresh \R session, run \code{remove.packages(c("MatchIt", "Rcpp")); install.packages("MatchIt")}. This should sync \emph{MatchIt} and \emph{Rcpp} and ensure they work correctly. } \references{ In a manuscript, you don't need to cite another package when using \code{method = "nearest"} because the matching is performed completely within \emph{MatchIt}. For example, a sentence might read: \emph{Nearest neighbor matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R.} } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. \code{\link{method_optimal}} for optimal pair matching, which is similar to nearest neighbor matching except that an overall distance criterion is minimized. } \examples{ data("lalonde") # 1:1 greedy NN matching on the PS m.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "nearest") m.out1 summary(m.out1) # 3:1 NN Mahalanobis distance matching with # replacement within a PS caliper m.out2 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "nearest", replace = TRUE, mahvars = ~ age + educ + re74 + re75, ratio = 3, caliper = .02) m.out2 summary(m.out2, un = FALSE) # 1:1 NN Mahalanobis distance matching within calipers # on re74 and re75 and exact matching on married and race m.out3 <- matchit(treat ~ age + educ + re74 + re75, data = lalonde, method = "nearest", distance = "mahalanobis", exact = ~ married + race, caliper = c(re74 = .2, re75 = .15)) m.out3 summary(m.out3, un = FALSE) # 2:1 variable ratio NN matching on the PS m.out4 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "nearest", ratio = 2, min.controls = 1, max.controls = 12) m.out4 summary(m.out4, un = FALSE) # Some units received 1 match and some received 12 table(table(m.out4$subclass[m.out4$treat == 0])) } MatchIt/man/method_exact.Rd0000644000176200001440000000731314075177171015327 0ustar liggesusers\name{method_exact} \alias{method_exact} \title{Exact Matching} \description{ In \fun{matchit}, setting \code{method = "exact"} performs exact matching. With exact matching, a complete cross of the covariates is used to form subclasses defined by each combination of the covariate levels. Any subclass that doesn't contain both treated and control units is discarded, leaving only subclasses containing treatment and control units that are exactly equal on the included covariates. The benefits of exact matching are that confounding due to the covariates included is completely eliminated, regardless of the functional form of the treatment or outcome models. The problem is that typically many units will be discarded, sometimes dramatically reducing precision and changing the target population of inference. To use exact matching in combination with another matching method (i.e., to exact match on some covariates and some other form of matching on others), use the \code{exact} argument with that method. This page details the allowable arguments with \code{method = "exact"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for exact matching: \preformatted{ matchit(formula, data = NULL, method = "exact", estimand = "ATT", s.weights = NULL, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the subclasses defined by a full cross of the covariate levels. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"exact"}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"}, \code{"ATC"}, and \code{"ATE"}. The estimand controls how the weights are computed; see the Computing Weights section at \fun{matchit} for details. } \item{s.weights}{ the variable containing sampling weights to be incorporated into balance statistics. These weights do not affect the matching process. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. } \item{\dots}{ ignored. } The arguments \code{distance} (and related arguments), \code{exact}, \code{mahvars}, \code{discard} (and related arguments), \code{replace}, \code{m.order}, \code{caliper} (and related arguments), and \code{ratio} are ignored with a warning. } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "exact"} except for \code{match.matrix}. This is because matching strata are not indexed by treated units as they are in some other forms of matching. \code{include.obj} is ignored. } \references{ In a manuscript, you don't need to cite another package when using \code{method = "exact"} because the matching is performed completely within \emph{MatchIt}. For example, a sentence might read: \emph{Exact matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R.} } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. The \code{exact} argument can be used with other methods to perform exact matching in combination with other matching methods. \code{\link{method_cem}} for coarsened exact matching, which performs exact matching on coarsened versions of the covariates. } \examples{ data("lalonde") # Exact matching on age, race, married, and educ m.out1 <- matchit(treat ~ age + race + married + educ, data = lalonde, method = "exact") m.out1 summary(m.out1) } MatchIt/man/method_optimal.Rd0000644000176200001440000003131214110073307015646 0ustar liggesusers\name{method_optimal} \alias{method_optimal} \title{Optimal Pair Matching} \description{ In \fun{matchit}, setting \code{method = "optimal"} performs optimal pair matching. The matching is optimal in the sense that that sum of the absolute pairwise distances in the matched sample is as small as possible. The method functionally relies on \pkgfun2{optmatch}{pairmatch}{optmatch::pairmatch}. Advantages of optimal pair matching include that the matching order is not required to be specified and it is less likely that extreme within-pair distances will be large, unlike with nearest neighbor matching. Generally, however, as a subset selection method, optimal pair matching tends to perform similarly to nearest neighbor matching in that similar subsets of units will be selected to be matched. This page details the allowable arguments with \code{method = "optmatch"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for optimal pair matching: \preformatted{ matchit(formula, data = NULL, method = "optimal", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", exact = NULL, mahvars = NULL, antiexact = NULL, discard = "none", reestimate = FALSE, s.weights = NULL, ratio = 1, min.controls = NULL, max.controls = NULL, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the distance measure used in the matching. This formula will be supplied to the functions that estimate the distance measure. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"optimal"}. } \item{distance}{ the distance measure to be used. See \code{\link{distance}} for allowable options. When set to \code{"mahalanobis"}, optimal Mahalanobis distance matching will be performed on the variables named in \code{formula}. Can be supplied as a distance matrix. } \item{link}{ when \code{distance} is specified as a string and not \code{"mahalanobis"}, an additional argument controlling the link function used in estimating the distance measure. See \code{\link{distance}} for allowable options with each option. } \item{distance.options}{ a named list containing additional arguments supplied to the function that estimates the distance measure as determined by the argument to \code{distance}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"} and \code{"ATC"}. See Details. } \item{exact}{ for which variables exact matching should take place. Exact matching is processed using \pkgfun2{optmatch}{exactMatch}{optmatch::exactMatch}. } \item{mahvars}{ for which variables Mahalanobis distance matching should take place when a distance measure other than \code{"mahalanobis"} is used (e.g., to discard units for common support). If specified, the distance measure will not be used in matching. } \item{antiexact}{ for which variables ant-exact matching should take place. Anti-exact matching is processed using \pkgfun2{optmatch}{antiExactMatch}{optmatch::antiExactMatch}. } \item{discard}{ a string containing a method for discarding units outside a region of common support. Only allowed when \code{distance} is not \code{"mahalanobis"} and not a matrix. } \item{reestimate}{ if \code{discard} is not \code{"none"}, whether to re-estimate the propensity score in the remaining sample prior to matching. } \item{s.weights}{ the variable containing sampling weights to be incorporated into propensity score models and balance statistics. } \item{ratio}{ how many control units should be matched to each treated unit for k:1 matching. For variable ratio matching, see section "Variable Ratio Matching" in Details below. } \item{min.controls, max.controls}{ for variable ratio matching, the minimum and maximum number of controls units to be matched to each treated unit. See section "Variable Ratio Matching" in Details below. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. What is printed depends on the matching method. Default is \code{FALSE} for no printing other than warnings. } \item{\dots}{ additional arguments passed to \pkgfun2{optmatch}{pairmatch}{optmatch::pairmatch}. Only the \code{tols} argument, which is eventually passed to \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch}, is allowed. } The arguments \code{replace}, \code{caliper}, and \code{m.order} are ignored with a warning. } \note{ Calipers may eventually be compatible with this method, but due to what appears to be a bug in \emph{optmatch} (version 0.9-13), they are currently disabled. Optimal pair matching is a restricted form of optimal full matching where the number of treated units in each subclass is equal to 1, whereas in unrestricted full matching, multiple treated units can be assigned to the same subclass. \pkgfun2{optmatch}{pairmatch}{optmatch::pairmatch} is simply a wrapper for \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch}, which performs optimal full matching and is the workhorse for \code{\link{method_full}}. In the same way, \code{matchit()} uses \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch} under the hood, imposing the restrictions that make optimal full matching function like optimal pair matching (which is simply to set \code{min.controls >= 1} and to pass \code{ratio} to the \code{mean.controls} argument). This distinction is not important for regular use but may be of interest to those examining the source code. The option \code{"optmatch_max_problem_size"} is automatically set to \code{Inf} during the matching process, different from its default in \emph{optmatch}. This enables matching problems of any size to be run, but may also let huge, infeasible problems get through and potentially take a long time or crash R. See \pkgfun2{optmatch}{setMaxProblemSize}{optmatch::setMaxProblemSize} for more details. } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "optimal"}. When \code{include.obj = TRUE} in the call to \code{matchit()}, the output of the call to \pkgfun2{optmatch}{fullmatch}{optmatch::fullmatch} will be included in the output. When \code{exact} is specified, this will be a list of such objects, one for each stratum of the exact variables. } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. \pkgfun2{optmatch}{pairmatch}{optmatch::pairmatch}, which is the workhorse. \code{\link{method_full}} for optimal full matching, of which optimal pair matching is a special case, and which relies on similar machinery. } \details{ \subsection{Mahalanobis Distance Matching}{ Mahalanobis distance matching can be done one of two ways: 1) If no propensity score needs to be estimated, \code{distance} should be set to \code{"mahalanobis"}, and Mahalanobis distance matching will occur on all the variables in \code{formula}. Arguments to \code{discard} and \code{mahvars} will be ignored. For example, to perform simple Mahalanobis distance matching, the following could be run: \preformatted{ matchit(treat ~ X1 + X2, method = "nearest", distance = "mahalanobis") } With this code, the Mahalanobis distance is computed using \code{X1} and \code{X2}, and matching occurs on this distance. The \code{distance} component of the \code{matchit()} output will be empty. 2) If a propensity score needs to be estimated for common support with \code{discard}, \code{distance} should be whatever method is used to estimate the propensity score or a vector of distance measures, i.e., it should not be \code{"mahalanobis"}. Use \code{mahvars} to specify the variables used to create the Mahalanobis distance. For example, to perform Mahalanobis after discarding units outside the common support of the propensity score in both groups, the following could be run: \preformatted{ matchit(treat ~ X1 + X2 + X3, method = "nearest", distance = "glm", discard = "both", mahvars = ~ X1 + X2) } With this code, \code{X1}, \code{X2}, and \code{X3} are used to estimate the propensity score (using the \code{"glm"} method, which by default is logistic regression), which is used to identify the common support. The actual matching occurs on the Mahalanobis distance computed only using \code{X1} and \code{X2}, which are supplied to \code{mahvars}. The estimated propensity scores will be included in the \code{distance} component of the \code{matchit()} output. When sampling weights are supplied through the \code{s.weights} argument, the covariance matrix of the covariates used in the Mahalanobis distance is \strong{not} weighted by the sampling weights. Mahalanobis distance matching can also be done by supplying a Mahalanobis distance matrix (e.g., the output of a call to \pkgfun2{optmatch}{match_on}{optmatch::match_on}) to the \code{distance} argument. This makes it straightforward to use the robust rank-based Mahalanobis distance available in \pkg{optmatch}. } \subsection{Estimand}{ The \code{estimand} argument controls whether control units are selected to be matched with treated units (\code{estimand = "ATT"}) or treated units are selected to be matched with control units (\code{estimand = "ATC"}). The "focal" group (e.g., the treated units for the ATT) is typically made to be the smaller treatment group, and a warning will be thrown if it is not set that way unless \code{replace = TRUE}. Setting \code{estimand = "ATC"} is equivalent to swapping all treated and control labels for the treatment variable. When \code{estimand = "ATC"}, the \code{match.matrix} component of the output will have the names of the control units as the rownames and be filled with the names of the matched treated units (opposite to when \code{estimand = "ATT"}). Note that the argument supplied to \code{estimand} doesn't necessarily correspond to the estimand actually targeted; it is merely a switch to trigger which treatment group is considered "focal". } \subsection{Variable Ratio Matching}{ \code{matchit()} can perform variable ratio matching, which involves matching a different number of control units to each treated unit. When \code{ratio > 1}, rather than requiring all treated units to receive \code{ratio} matches, the arguments to \code{max.controls} and \code{min.controls} can be specified to control the maximum and minimum number of matches each treated unit can have. \code{ratio} controls how many total control units will be matched: \code{n1 * ratio} control units will be matched, where \code{n1} is the number of treated units, yielding the same total number of matched controls as fixed ratio matching does. Variable ratio matching can be used with either propensity score matching or Mahalanobis distance matching, including when \code{distance} is supplied as a matrix. \code{ratio} does not have to be an integer but must be greater than 1 and less than \code{n0/n1}, where \code{n0} and \code{n1} are the number of control and treated units, respectively. Setting \code{ratio = n0/n1} performs a restricted form of full matching where all control units are matched. If \code{min.controls} is not specified, it is set to 1 by default. \code{min.controls} must be less than \code{ratio}, and \code{max.controls} must be greater than \code{ratio}. See the Examples section of \code{\link{method_nearest}} for an example of their use, which is the same as it is with optimal matching. } } \references{ In a manuscript, be sure to cite the following paper if using \code{matchit()} with \code{method = "optimal"}: Hansen, B. B., & Klopfer, S. O. (2006). Optimal Full Matching and Related Designs via Network Flows. Journal of Computational and Graphical Statistics, 15(3), 609–627. \doi{10.1198/106186006X137047} For example, a sentence might read: \emph{Optimal pair matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R, which calls functions from the optmatch package (Hansen & Klopfer, 2006).} } \examples{\dontshow{if (requireNamespace("optmatch", quietly = TRUE)) \{} data("lalonde") # 1:1 optimal PS matching with exact matching on race m.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "optimal", exact = ~race) m.out1 summary(m.out1) #2:1 optimal Mahalanobis distance matching m.out2 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "optimal", distance = "mahalanobis", ratio = 2) m.out2 summary(m.out2, un = FALSE) \dontshow{\}}} MatchIt/man/rbind.matchdata.Rd0000644000176200001440000000570214067016337015703 0ustar liggesusers\name{rbind.matchdata} \alias{rbind.matchdata} \alias{rbind.getmatches} \title{ Append matched datasets together } \description{ These functions are \fun{rbind} methods for objects resulting from calls to \fun{match.data} and \fun{get_matches}. They function nearly identically to \code{rbind.data.frame()}; see Details for how they differ. } \usage{ \method{rbind}{matchdata}(\dots, deparse.level = 1) \method{rbind}{getmatches}(\dots, deparse.level = 1) } \arguments{ \item{\dots}{ Two or more \code{matchdata} or \code{getmatches} objects the output of calls to \fun{match.data} and \fun{get_matches}, respectively. Supplied objects must either be all \code{matchdata} objects or all \code{getmatches} objects. } \item{deparse.level}{ Passed to \fun{rbind}. } } \details{ \code{rbind()} appends two or more datasets row-wise. This can be useful when matching was performed separately on subsets of the original data and they are to be combined into a single dataset for effect estimation. Using the regular \code{data.frame} method for \code{rbind()} would pose a problem, however; the \code{subclass} variable would have repeated names across different datasets, even though units only belong to the subclasses in their respective datasets. \code{rbind.matchdata()} renames the subclasses so that the correct subclass membership is maintained. The supplied matched datasets must be generated from the same original dataset, that is, having the same variables in it. The added components (e.g., weights, subclass) can be named differently in different datasets but will be changed to have the same name in the output. \code{rbind.getmatches()} and \code{rbind.matchdata()} are identical. } \value{ An object of the same class as those supplied to it (i.e., a \code{matchdata} object if \code{matchdata} objects are supplied and a \code{getmatches} object if \code{getmatches} objects are supplied). \fun{rbind} is called on the objects after adjusting the variables so that the appropriate method will be dispatched corresponding to the class of the original data object. } \author{ Noah Greifer } \seealso{ \fun{match.data}, \fun{rbind} See \code{vignettes("estimating-effects")} for details on using \code{rbind()} for effect estimation after subsetting the data. } \examples{ data("lalonde") # Matching based on race subsets m.out_b <- matchit(treat ~ age + educ + married + nodegree + re74 + re75, data = subset(lalonde, race == "black")) md_b <- match.data(m.out_b) m.out_h <- matchit(treat ~ age + educ + married + nodegree + re74 + re75, data = subset(lalonde, race == "hispan")) md_h <- match.data(m.out_h) m.out_w <- matchit(treat ~ age + educ + married + nodegree + re74 + re75, data = subset(lalonde, race == "white")) md_w <- match.data(m.out_w) #Bind the datasets together md_all <- rbind(md_b, md_h, md_w) #Subclass conflicts are avoided levels(md_all$subclass) }MatchIt/man/match.data.Rd0000644000176200001440000002332414127350241014654 0ustar liggesusers\name{match.data} \alias{match.data} \alias{get_matches} \title{ Construct a matched dataset from a \code{matchit} object } \description{ \code{match.data()} and \code{get_matches()} create a data frame with additional variables for the distance measure, matching weights, and subclasses after matching. This dataset can be used to estimate treatment effects after matching or subclassification. \code{get_matches()} is most useful after matching with replacement; otherwise, \code{match.data()} is more flexible. See Details below for the difference between them. } \usage{ match.data(object, group = "all", distance = "distance", weights = "weights", subclass = "subclass", data = NULL, include.s.weights = TRUE, drop.unmatched = TRUE) get_matches(object, distance = "distance", weights = "weights", subclass = "subclass", id = "id", data = NULL, include.s.weights = TRUE) } \arguments{ \item{object}{ a \code{matchit} object; the output of a call to \fun{matchit}. } \item{group}{ which group should comprise the matched dataset: \code{"all"} for all units, \code{"treated"} for just treated units, or \code{"control"} for just control units. Default is \code{"all"}. } \item{distance}{ a string containing the name that should be given to the variable containing the distance measure in the data frame output. Default is \code{"distance"}, but \code{"prop.score"} or similar might be a good alternative if propensity scores were used in matching. Ignored if a distance measure was not supplied or estimated in the call to \code{matchit()}. } \item{weights}{ a string containing the name that should be given to the variable containing the matching weights in the data frame output. Default is \code{"weights"}. } \item{subclass}{ a string containing the name that should be given to the variable containing the subclasses or matched pair membership in the data frame output. Default is \code{"subclass"}. } \item{id}{ a string containing the name that should be given to the variable containing the unit IDs in the data frame output. Default is \code{"id"}. Only used with \code{get_matches()}; for \code{match.data()}, the units IDs are stored in the row names of the returned data frame. } \item{data}{ a data frame containing the original dataset to which the computed output variables (\code{distance}, \code{weights}, and/or \code{subclass}) should be appended. If empty, \code{match.data()} and \code{get_matches()} will attempt to find the dataset using the environment of the \code{matchit} object, which can be unreliable; see Notes. } \item{include.s.weights}{ \code{logical}; whether to multiply the estimated weights by the sampling weights supplied to \code{matchit()}, if any. Default is \code{TRUE}. If \code{FALSE}, the weights in the \code{match.data()} or \code{get_matches()} output should be multiplied by the sampling weights before being supplied to the function estimating the treatment effect in the matched data. } \item{drop.unmatched}{ \code{logical}; whether the returned data frame should contain all units (\code{FALSE}) or only units that were matched (i.e., have a matching weight greater than zero) (\code{TRUE}). Default is \code{TRUE} to drop unmatched units. } } \details{ \code{match.data()} creates a dataset with one row per unit. It will be identical to the dataset supplied except that several new columns will be added containing information related to the matching. When \code{drop.unmatched = TRUE}, the default, units with weights of zero, which are those units that were discarded by common support or the caliper or were simply not matched, will be dropped from the dataset, leaving only the subset of matched units. The idea is for the output of \code{match.data()} to be used as the dataset input in calls to \code{glm()} or similar to estimate treatment effects in the matched sample. It is important to include the weights in the estimation of the effect and its standard error. The subclass column, when created, contains par or subclass membership and should be used to estimate the effect and its standard error. Subclasses will only be included if there is a \code{subclass} component in the \code{matchit} object, which does not occur with matching with replacement, in which case \code{get_matches()} should be used. See \code{vignette("estimating-effects")} for information on how to use \code{match.data()} output to estimate effects. \code{get_matches()} is similar to \code{match.data()}; the primary difference occurs when matching is performed with replacement, i.e., when units do not belong to a single matched pair. In this case, the output of \code{get_matches()} will be a dataset that contains one row per unit for each pair they are a part of. For example, if matching was performed with replacement and a control unit was matched to two treated units, that control unit will have two rows in the output dataset, one for each pair it is a part of. Weights are computed for each row, and are equal to the inverse of the number of control units in each control unit's subclass. Unmatched units are dropped. An additional column with unit IDs will be created (named using the \code{id} argument) to identify when the same unit is present in multiple rows. This dataset structure allows for the inclusion of both subclass membership and repeated use of units, unlike the output of \code{match.data()}, which lacks subclass membership when matching is done with replacement. A \code{match.matrix} component of the \code{matchit} object must be present to use \code{get_matches()}; in some forms of matching, it is absent, in which case \code{match.data()} should be used instead. See \code{vignette("estimating-effects")} for information on how to use \code{get_matches()} output to estimate effects after matching with replacement. } \value{ A data frame containing the data supplied in the \code{data} argument or in the original call to \code{matchit()} with the computed output variables appended as additional columns, named according the arguments above. For \code{match.data()}, the \code{group} and \code{drop.unmatched} arguments control whether only subsets of the data are returned. See Details above for how \code{match.data()} and \code{get_matches()} differ. Note that \code{get_matches} sorts the data by subclass and treatment status, unlike \code{match.data()}, which uses the order of the data. The returned data frame will contain the variables in the original data set or dataset supplied to \code{data} and the following columns: \item{distance}{The propensity score, if estimated or supplied to the \code{distance} argument in \code{matchit()} as a vector.} \item{weights}{The computed matching weights. These must be used in effect estimation to correctly incorporate the matching.} \item{subclass}{Matching strata membership. Units with the same value are in the same stratum.} \item{id}{The ID of each unit, corresponding to the row names in the original data or dataset supplied to \code{data}. Only included in \code{get_matches} output. This column can be used to identify which rows belong to the same unit since the same unit may appear multiple times if reused in matching with replacement.} These columns will take on the name supplied to the corresponding arguments in the call to \code{match.data()} or \code{get_matches()}. See Examples for an example of rename the \code{distance} column to \code{"prop.score"}. If \code{data} or the original dataset supplied to \code{matchit()} was a \code{data.table} or \code{tbl}, the \code{match.data()} output will have the same class, but the \code{get_matches()} output will always be a base R \code{data.frame}. In addition to their base class (e.g., \code{data.frame} or \code{tbl}), returned objects have the class \code{matchdata} or \code{getmatches}. This class is important when using \code{\link[=rbind.matchdata]{rbind()}} to append matched datasets. } \note{ The most common way to use \code{match.data()} and \code{get_matches()} is by supplying just the \code{matchit} object, e.g., as \code{match.data(m.out)}. A data set will first be searched in the environment of the \code{matchit} formula, then in the calling environment of \code{match.data()} or \code{get_matches()}, and finally in the \code{model} component of the \code{matchit} object if a propensity score was estimated. When called from an environment different from the one in which \code{matchit()} was originally called and a propensity score was not estimated (or was but with \code{discard} not \code{"none"} and \code{reestimate = TRUE}), this syntax may not work because the original dataset used to construct the matched dataset will not be found. This can occur when \code{matchit()} was run within an \fun{lapply} or \code{purrr::map()} call. The solution, which is recommended in all cases, is simply to supply the original dataset to the \code{data} argument of \code{match.data()}, e.g., as \code{match.data(m.out, data = original_data)}, as demonstrated in the Examples. } \seealso{ \fun{matchit} \fun{rbind.matchdata} \code{vignette("estimating-effects")} for uses of \code{match.data()} and \code{get_matches()} in estimating treatment effects. } \examples{ data("lalonde") # 4:1 matching w/replacement m.out1 <- matchit(treat ~ age + educ + married + race + nodegree + re74 + re75, data = lalonde, replace = TRUE, caliper = .05, ratio = 4) m.data1 <- match.data(m.out1, data = lalonde, distance = "prop.score") dim(m.data1) #one row per matched unit head(m.data1, 10) g.matches1 <- get_matches(m.out1, data = lalonde, distance = "prop.score") dim(g.matches1) #multiple rows per matched unit head(g.matches1, 10) }MatchIt/man/macros/0000755000176200001440000000000013745733025013653 5ustar liggesusersMatchIt/man/macros/macros.Rd0000644000176200001440000000257014044456667015441 0ustar liggesusers% Rd macro for simplifying documentation writing \newcommand{\fun}{\code{\link[=#1]{#1()}}} % Because R packages need conditional use of packages in Suggests, any cross-reference to a doc in another package needs to be conditionally evaluated, too. %\pkgfun{}{})tests whether the package is available, and, if so, produces a cross-reference to the function in the package; if not, the function name is displayed without a cross-reference. The first argument is the package, the second is the function name, e.g., \pkgfun{optmatch}{pairmatch}. %\newcommand{\pkgfun}{\ifelse{\Sexpr[results=rd,stage=render]{requireNamespace("#1", quietly = TRUE)}}{\code{\link[#1:#2]{#2()}}}{\code{#2()}}} \newcommand{\pkgfun}{\code{\link[#1:#2]{#2()}}} %E.g., \pkgfun{sandwich}{vcovCL} is the same as \code{\link[sandwich:vcovCL]{vcovCL}} if the sandwich package is installed and \code{vcovCL} if not. %\pkgfun2{}{}{} does the same but allows the third argument to be printed, e.g., to use text that differs from the name of the function in the new package. %\newcommand{\pkgfun2}{\ifelse{\Sexpr[results=rd,stage=render]{requireNamespace("#1", quietly = TRUE)}}{\code{\link[#1:#2]{#3()}}}{\code{#3()}}} \newcommand{\pkgfun2}{\code{\link[#1:#2]{#3()}}} %E.g., \pkgfun2{sandwich}{vcovCL}{meatCL} is the same as \code{\link[sandwich:vcovCL]{meatCL}} if the sandwich package is installed and \code{meatCL} if not. MatchIt/man/lalonde.Rd0000644000176200001440000000337313754164073014303 0ustar liggesusers\name{lalonde} \docType{data} \alias{lalonde} \title{Data from National Supported Work Demonstration and PSID, as analyzed by Dehejia and Wahba (1999).} \description{ This is a subsample of the data from the treated group in the National Supported Work Demonstration (NSW) and the comparison sample from the Population Survey of Income Dynamics (PSID). This data was previously analyzed extensively by Lalonde (1986) and Dehejia and Wahba (1999). } \usage{data(lalonde)} \format{ A data frame with 614 observations (185 treated, 429 control). There are 9 variables measured for each individual. \itemize{ \item "treat" is the treatment assignment (1=treated, 0=control). \item "age" is age in years. \item "educ" is education in number of years of schooling. \item "race" is the individual's race/ethnicity, (Black, Hispanic, or White). Note previous versions of this dataset used indicator variables \code{black} and \code{hispan} instead of a single race variable. \item "married" is an indicator for married (1=married, 0=not married). \item "nodegree" is an indicator for whether the individual has a high school degree (1=no degree, 0=degree). \item "re74" is income in 1974, in U.S. dollars. \item "re75" is income in 1975, in U.S. dollars. \item "re78" is income in 1978, in U.S. dollars. } "treat" is the treatment variable, "re78" is the outcome, and the others are pre-treatment covariates. } \references{ Lalonde, R. (1986). Evaluating the econometric evaluations of training programs with experimental data. American Economic Review 76: 604-620. Dehejia, R.H. and Wahba, S. (1999). Causal Effects in Nonexperimental Studies: Re-Evaluating the Evaluation of Training Programs. Journal of the American Statistical Association 94: 1053-1062. } \keyword{datasets} MatchIt/man/figures/0000755000176200001440000000000014170752616014033 5ustar liggesusersMatchIt/man/figures/logo.png0000644000176200001440000003302213755115075015501 0ustar liggesusersPNG  IHDRBysRGBxeXIfMM*>F(iNBU pHYsgR53IDATx]xU>)Z TX@tA]A,]ՍZNpu]ume]wAA %!= |3}̹ϓgs3{=!@!@AA .(qqcf(뭏ǒ* rO-!p26ǡh1mF(KEB)Pƍ PUf? .t dl\+C[1ŬDH(`mH?6)|^UBEiy#L僅`Ӗ{9FJ(7n."@&W^F$ftҧ.WMTj|@5L>iHӈ1W0ʒ MyH̏!VIs jES6/cVB*7ꑱ¬ka*jױ "瓱tK/[6iAyMK =Ru$22H$>GH~+Bo3 S1USY z$ǼqMrS ?,_x%>,r|#cÇ,KTh>@l26*FXe59p'cù /5XӈωdlЃn)|:{|& S>$z.U#cvt"^,\%瘅zdlr2֯013:fL-7G25T6YH|vݠEnrvQC-1;yeAIpɅvoPy@[MxDdl"*T6~2N|N7riQB|!UVmPk/{z)6H=Q%cg3WΨ+*/RLs,26(שMO& !>)d.4^,t2rgm}0r2f">,rGa}*ax?t8 %^㭅9*7YiXk\Mn{34Un;\4H5/c*MXP^'/p*n1GQs@>ؘMfCrea6Aˡ8 ͒ 2Sz]?o!WQ (yM| 0= db$b!ކ8ޠ\->M268AA nSdl^*m}Cmlp*; }Zkxr-O<*Zkᮍo9xICwgh m1">{}/Chto4cCS<`J| -?T~QsOw4hcC;{!> |2wB| !^ه%,+ g4cC3G[%>~Tx4:V|klhS]TQĔ}glQ*RC\YWƆ+jڇMƆxsʥZ"@|1|66S&sΥ!GA@*'W:/,suč!^F[I?+ <764-Wł9'陱-'@wF@scCL\4:uy냫ƆBJ as\16˄DՇ@ض>8jlQ"&>1cCSVpt^$rxMyۍ 2'Ȇҡ % Py0\ 9;mCOƚU.ϲB9Seb=FFAAbRʇv#4؛\8Je3 9 P/]= Ynz oRgMBvm2o0k~tu&:uEW@#yhEmU.ϲ~߶h)T.|%?xĠ~T#rYz!^va8羧W`%+a/ 3RWp &Tte!amT nef ߹=⬓`VV۵Z_K.Lڶd;G|gs26UHHiSTA # x)ĸ,WAkϟ~Y pA--F@WزH5ey lvsthM-26/FD~c)x24R3rtAfʽ7u7[ʾp0;&ܒ ApX .N]&3n? MJɵԃ-\*HCo+$O ̢<;pKn >ipt7 y5D7H7Sd+Cx'AMDm]ԓ؟K%4ۣW kT-9̼L, [;(jÇ`kaO8Dv|9#2Kw[.Y5um28թ i9m[A0kZ3Hs) dI8c+;$|vVcY~N3!- n%:Wܮ5+g±Gdaۍ`tz pr%SNgG#|eEWPqs:B.Cձc0}Tn?>-- Ȋ4d;:7YZ"]16Ʉ t{PA ou*smI9=yj7H%ᛰŲ4#~.8VY)G7FĭvǍ NeG"c0}԰Ȣ[s^rO?~}aY_";~_'VQ'`e2]YMu8jlp?NQ~>EY2%UϤߤB Ou3xg`oe!Y{ei26ai%<%%=[VwÜ_`܎tg0ۗ-&^,Bdc:%cH;YE!@~jwQ#g T{vU JRa{ovui;d]@?ظVvIR?@;Ȳ,Id +26V3Q##OOXaW SIj+F{wӉZ oIO4C9Lʾr׮cMKfQLx x .Q @F?]}6yzZ@0D8/,(DkC&Dvhڱ 8 4),E#}W1xPjf+| &n L枌5.qa4KLtIAwd1,Hd>IW)$#.U93T!tqي<7 ]bRЏ@٪BX6Ay d([ OI266ׄssJ !Rؔ罜m>R_UV"dllzr.2z )פ0ۭF6JteiчųUb<mlt0[(f8t02ٵ8DIaaiHG.+@ ccC<-pɰmh|RA 3ח54dl,35V.Ha]ZJDח#h9,-#k(1;5b( fnUI7 {֮2Ӝ-uV W{lJ_<*QEoR΀ }KWۄ#:~?+Ki@Wx>8z`bg}E"#Q%fjb#LIL@ e|*25#p9dlx莡?6#Iaܵ.)8w=gRsЧ1춀ǟH0sY\WJ⒂sdx91Ldo26JLbbE^sg]Q̺N229H⒂3l$$ ,-26ߊF|Y}K`e4KU_),ĩ{"c#K&6FKhOxR8 .-)o? s~udia1ZjN,H/wKҒ`d1Nw<)I'cSi9*IÇa#2 tQ) oteI}v,&X y26jFRV-Y΢7%einFV)HaܝL<o(<Ō,To5jh٣$¿1)|]WyGC~}Yba dlbh>^RyEtK ˪sK G }f,NiЙ@#dl9gx\A L|VӵZ(Ia]WR ѥ$Օ[U dr2xNUBGҶy+OL\_KQ%#)rud+#Ck]UJ\Iil_CPN,*U]2dsc-v_+$ŀ+gDYܾEvb܋Y62L|ox-LU pZXaƘ\?>f=%Q'~_?hд(4Łmq vMRY!_CJf.@媓.x]Y5{yрO*źIlpeC}hȾpBoB+jg̚C. dSO'!..Np'}˨FK㨹_#`=JЏM닯V˲Fr035BklnZ_KV۵ZorVl1ӒpV%qJECP\k$6bw #J̫46IY ;YZR%j"a2CilPJpʁ8e\+q̼Zc)k~pπ035Bkl ypycRX7fƬCrńHQ0S@eBh nĽ;Fr=SvhU]I.]0 f28$DJ6lEiOdxR`AE~+c44S 6̜|8|qbըZgv=663"vIɩyW:nhDH. WL?VzJmlbv[p5iZS!)!6:!,p4}:ES< (1WTS_polw7DmӼpxOfьZe*?hSs%(N:JwJpiL7J~ "Ʀ3oA6ghuћ9O߯o4Mw<km#QPfph0x, YS6V{c v-o7񵓠-wWuOn|"/5cK |oljog_η,|3;vxj N=6& q* -S z =^q/Ykj&Iknȃo>9X9^i9mb|B~o^/CQp0rhw,YzUe%ܾYH * eBC鎿G‡IsT͞bX:%i3 \iWٔ"w M:m.U"}ض<ag._:񀢭kV)( #KcshN7Nim.A-Sτ̾E,cBӯVڵemu{V~ՙ\>O.kl$Vmht8m{ߤ9ck_p-^RRXET>eÎCtZJS\mcHMQS2[Ydi:"u=/f]{ 8: Ol|a"g]*#hU? !aРi 6 ,P:{[hL$zӖzó-`՗*xN5>N晐{ɵr-+<Z II~Zx֞5` pn}Y6;hE Kj2?,ؾlgt(>8:,K<|L~述q,Jo$$di0īloH>RY$[؂ތ`l/?.zYO=~rtu$'myOhhkֵ!C0z> VKȤF$I7̀A'B.5 xeNA|+7k'/VR54b+T0 zޒH\Bonfڕ/xrF'?"CN6IJR)uc ǯ /+?ilBIoݮȈuo`g>4mFXڿT!r#b"nUG·#K-vMОm%FoH V?A@ L\Sy|ivVo<,# IuMd͉L56 l߆uoc [ wle2Ձ: A b|B֧i{=U嬞ZRزpU;%MݹbZuEV?'>fԷ \Bcb.[\lߴ%4?7j7HVgWaٰc)$֩ sO^-$Ԫ%=SFTkL[z3#u|olVL2v⿺'E4KzxΛ;Aԉotd8?j> Q1HFoav&ꛦlz>EH.zSHdFR-޻~y6XgjlY:4لv-~e|-+7[KWʽ:a{5sCo. C F6.dy ;:v4If~v>\( ~~/}Ј*^2@u\X&lXktw44g ?3LeMT2uEr Wv≟( ʖïU]GM3ό)a@ݨH\3[vۺWdD9p I<2YZwpSJ\c6_\UqFmY0%Zq!xǂW+r(؏@`]WleŠ5aO Wo@MKy"Zk6M#A\*VpNsCL,<|Q )liמQŝ[@[!5h̓*ضTk2ESmͷv_h3U oje(8 a}-y$\(tr%e+ʻA_-20-wI; jm IiBD2N5Gy/>k[TEC}P@|}_¶[i ~Jiq=vrz\JHhg_N[K-I~ 8SUyb]|b-? 7 긿W D j3  [UG6)!t`νr.Ru͋:`}Q-6Y#vq; nC_p8[<ӯyte-sTöylݻ[2 pآ6\=-l[<9Zyqp[]CF'[lod6o}u:F>RX>8pz=24>,yzd=oN@PYmYa$-LneF :7"ccvhlұa&^_akln$^tј9 7V l9Xv: \"3bz 266k?S;IAtvpO+MwꮣUPkuo[2vHsԲU:Â<ӣ*vá]YG5+U[m?d;mHn&Z_rpbA\Fƫ,ߴKaE_<Y-$Ȁ`+IjE4= 7R$8 y!੝Zk}}3=kt˟.|=PNU@E}^YL5|Wq֦ZVV9,m2꽓zڱ͙y^J[ic UP56U.\56~Zխ*'>ǫG n2ZرO(;ku'})*C(O&!`'hiU2KT5D#d*>Uة3"FfOܼ5MT`F+o98/xeTPjb̢qcT[. D!pOt}w` Լ>%Ë&H/p*;ְe 46bƍ 87o9(m}@( xJ~nSjnt,_?56>|ˡr.TABcrni7@+ߐAVr#"A ")/l]ބ6yÇܔ!Y+>>kʡ\gK;~IgQn|rreZGr`F Uee؈#"AWB:GDN @S{cPHrea߳ha6:Aă ^nj؈%>GDFl#h/Hc#@[D$ ec_^ D|}K[~EsD$TAh-MHJwz&>GD"Wesh B_=k?%:{Uٴl.;:x҉D3Me֊66"HJ _$c#90||K #c*GQbh `8M&^SH"c#C+ 5lxHO0.Fe cxU\)pl* eEK'4͋&AF?VVN%^)do ih6T!^=lϱ4m,A9U ^F'Iy՟V }L rB|5@i5xM!c/c /?&c$VL24*U*{ յ0.i7GTZ%Wri*[Gcdl\pѡ˸qV>x6$c2$|NiGgݒX!As{26(#h|~2N.N cÉ"D1ʂk$dl" G>xE cñNb~T IL\XjtnnJك{p䪕jß+ /c9ꑱKJ>gMeHxOws Õ+#/aS4%e̊>x:5d|94myk oqI>L#^%{ e9x|&cuYmt]seASټ=C}ы SIpFH26Vs/GcotsY>x<"JF7T.h!^&܏hBCjH>,,b*/"A B3oy}IENDB`MatchIt/man/figures/README-unnamed-chunk-6-1.png0000644000176200001440000010674213756161671020550 0ustar liggesusersPNG  IHDRz4iCCPkCGColorSpaceGenericRGB8U]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_@IDATxֆ9g3H$I**HI*\EID%+9I9(9+3;;;;|yfޙoNc( @\?!    M7 _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2    DZw.k֬ ?|Pݻ'I$ģG0 /e޽+qıuAf$  ĉ… %EQ*q0yBH6̟?[n'ʸqnݻWu1cO Bj,C$@$@$@$3 PхngʁB{N$@$@$-xQFfk   h6BV@$@$@$@$Qż$@$@$@$@&@mHHHH *(@ByIHHHMOB6ЫuҼy8{L$@$@$#(@cchU(Q"F$@$@$@/XHHHHg>cA    _PBeHHHH|&@3-n:1cF`IHHE4ZBݻeʕyHHH JƋOK:Hҥp-HC;I$@$@fĈ2m4ɚ5,]TJ*%Wʕ+SpҶm[ɔ)j_|K?>yW4*UH)$wڋwe}[n:fqڵwY>|TMV2g,.ӆ*pSdII&mҤIɘ1TVMƏ/ڵKjժϓ?~߿ܿј6 -K,RP!d @oxyE5kAbjx,wȑ޽{ҨQ#^nܳgsM-&M*pC:iѢ>G#r7|SQ^|Ey'믿ĔnݺU^~e 9΁Ѿ}nJƍ<ǎ~@Au]f͚@Ж+WNp5J Чax^v:uDp6nh#:!E7֭[f6W^uIcy~~:% ]S,Xq^u9RBm߾]/^,tU>pYbV6&}GۈPfMɑ#d˖M ɓk9b90 sO)D+Ws?^~7} "V3~3h"iذ 2D{nǎ@$I.G7B^^ .!ѵۃdǎLa(=ɏӿ? #Ls9͎ISqDQ|yv)o7n\C o*Bo+F|#Gw)% 5& JV2W?ӧO|3gNmQttIE1<ߕp3dȠϭ s/JZY 5\`G{)7:JGN,K$@$@~'йsgcРAϛ.]:C9<{ŋ7q6!s; ~U-AĜ=چGsٲe(Q"ywębŊ4+<3O^ǛIEʍcv9`uaz0?bM1|!VecIvD[U_MyHHHq|w.m+Qw:-)@1Tnqۻp1b1߻w|ǂ+0 q}·55ŤoW^Luis玎{ĉ5l0{ueI@RJc%K&kז%K<6$@$@$ ڢe˖Rzu=gDyߕp4arZ 8R>S:>{LCl{ylz&40;^PXKi! # K(`^p|Ν8b5U !b:w4mjVCr0ajɰ EϞ==b |Cc[O˸q\8h0|+J5kL{03VVXĠ^zYK"`"~TPAgQ,a_~E  1c輞r?߻wo)Ly8ܘXrѣҥKLRzK;9F _(DHHH NZpϊ>駟 l}"K ^ӓ #yD>x13݌%mժꥅ(Ŏ;(D+eְ+~ .!n_ ÒM86sLQµhԩSq4   5q0Mɝ2HX\ָD EWBSLi&}x_Q7~%}I=q챰?&[u@<>޽5 C{#  &Hхw몪liIy'4X><ù)HHHBh_ 'OB*X6  HNk"F$@$@$@p$$_:2$@$@$@$@A40[I$@$@$@AC4h.%;B$@$@$@A40ZyYFƐ   ЀTi… eԨQi[B$@$@$P(@r$@$@$@$(@$@$@$@$P(@r$@$@$@$(@$@$@$@$P$\hl֭yh [A$@$@$p(@='JH /8 5!   => =3gɓ'%wܒ1cF'Npw:r߿_RJ%y Df'  $@=^߸qCj׮-۶mĉ ־}$^x }J ( ϟǏk-[PA~ ;Qy%W\"E )]̘1å?ԫWOҦM+5k֔իWK2eرcV]vIZtKqnw~z}\|Y ÐCIɒ%5uօ^׮]eĈɆ Je޽yHHb#k״1u+!bbQ*U\A9r؋sHHHH1p7,.wG9()̙Mqx{XHHH 8х28x,'ַm&3gΈX 3$ B `ٳ ^4    C$   p'@N$@$@$@$@J c+H,X1& @d(@##a.\X /8 5!   XHHHHPc     P.t ={V8s   h,pB5jThv&  6 h#d$@$@$@$@Q!@ZK$@$@$@$mA%@Ν+qđ/F +    \>vuG˫*yvIu<('Nԓr%[z*ys$@$@$@Q#@5^!{ƌ駟J߾}@xbɑ#nb3a„r=?΋<4   0 8v͛ҹsgɝ;dȐA6l('N0ۭgSKN^x9H]~w)S\|J߼y4iD2f(ժUˣGʕ+E/Ay2}t4iTXtB|}}VZ=!d 7|_1cCZzu}AJ#   yE7լiٶmnuU/PGAbhbLyb^RsefCͼAO K,:t!cA    _PQ[dĉGN8v?sܹ%Fs+M42bĈh$@$@$@$|οFB0CsY9pHH- P>cGŋRzu[[N2g,Wm]p5ʫD$@$@N'vZ)[dȐAҥK'*TSN9>G P<,_\e&UVK.ɽ{_~RxqI,(P@z-uunܸ!ݻw׳/ӧO[o޼);wܹsaÆ۷m&˗ԩSK5d׮]V޴ ek௾Zʽ*k駟[Zi1^ӧ5kh'<4k֌a1  vNx7%{iӦC 6ґիqFi߾TXQRL)iӦVZĉE2e}Ì3F>cѣGҼys;w[+s̑.]Z=NCǏx.4h@&O,ׂ.d#kJ*Ʉ ڵk6j>}7ސRJa$^x>> b4'a$@$@$j֬w[l[y %:g-2TwCZmSH'|;v%Jilb -5g!f}z&Mj:i:O׮], 5IիN?Σb! oڥ$IC bNcb_Wȑ#uÇ7~CCcܸqVޘx',Y Gɓ'ϻʕ+q`8RC,D=z0FmC oʛXx<;??"5y33gZZ(N$~E5on=MS"Q/(ʱcDx=.رC5Dz.RCJ2SdIi%ڵk[ɓ'fnx.LAPsƍb8qb9~nrJ8p4iD{?~<Sꂗn" ܍ɏ Ϙ]<]ȇs`v58>)ڎ6z2K5AƖOpL0al p;7>n4X^M Yf6mH)|("ye…R^=/^ ,\E]_7oJrF7.yGHH}EI&I |("f͒'|R&L GFI3i1G?*UȡCח̙3c$HH#ڵkg ug˖MV*.]{I~x,Y2)P[r- 7{C?~߿>}:/TΝ%wZ@6lPN8a778 5k֔ԩSK嵇6|=l>}zɓo2tUgϮ_zYz4kLΝ;8?r*U;9stM/_| >\3GmM40|HHH 8R^zU6n(۷+Jʔ)%mڴҪU+8qrSLBn̘1#i޼̝;Wz-}9sH.]q|?s: 1Ǐ/Jٲe]#2KZ蚢qra]ϵkd˖-r]=IC.]#}v-J!~=ۮ];Yh"_]nݪuݺueQ7C>QFɒ%K?:vYjQiWGwIJ<<6J4)/WPPm6+|J4%bŊYHl۶+W.}ܛ!#)]K=O?DǛ?*lH&n3RJe(ϝJ0vRg6WJWSܔҰa/IJ&v󦼙?z)Otի[ov>?Tyy=}iʙd~:À3SYc tCy:S:ߠAM߬bp+ 2D{1K^bV.LX^QY1FH$@$@&04gΜ2h M$?b",u7eo0 A&q5s^_,儵E:3O Q0n8-R}T_}Śxq_B7]tҺukq|7)/m2hxK99Y/ @٩4Fx"0'<)nxb< "}0 q}ȑ#z۷p+   3,D    /ԋ>9.g;al <:,$͈SAXjF$@$@$@ZګD#   _PBeHHHH|&@3:$   /XHHHHg> ݂x{O#   _p&_x 8vHHH :=%   2 (#c    =%   2 (#c    =U]vRlkI&1ʳ~zYvhӦM={Z^   ÅhѢ+VL3ȡCp9۷S9k%  z~_|ԫWO0 qݻwk׮ҸqcYfTXQ6m @Hسg.Κ5ɓGڷo/p9Rbx^˗Kl٤jժr%wO/.ɒ% [o%nݲӍ7{Rpaɟ?_N>mytYr-2d ʉ'#GH2edÆ VEI[pT\Y4^t?/gΜ1Ijdʔ)˗֭[[y&M$J)RHr-{dʔI+p<|P}ޥKiY/ ɓ'hѢRti=~پ}$MT.^h|W^7_̥LRҦM+Z' ! nj#2V͛7sJ޽o߾2gҥ>s=PF ˗(=u,Yĺ[lbLPu/ywm۶ګn:iԨ]LΝ j5jпx=zhСW ^yp| ׮],h$@$@$@GN\rõ^-_|z4e˖ұc;jVq6jMy.'|;v%J5kiJrJ0Zy~7CyK}>MCꗆNz׌g}}eԩgeɄPrHX ^O_ j]{@~W-y2޾C%JH{W'0n8s姺u!olX޼y5#GxS3f)#:'b`F$@$@$=sN$VLX&-`(~W1ԞK-]0AHJ޵k:" ֮][OF2K~X43EXg0x@a?o̜9SA &h*1ڨfkoXsԲN8'lԨQfg>   ` q,VɓG3xA@k u0:b&$Hn O)yAb֥)0í(@c[Ӱ>fC\{j/:/fޛF+y !ƻgC"ޅF$@$@${2uTS-S^  O9?c>oO$@$@$@D ` *J$@$@$@$>  @,!X`*H7oRN$@$@$@eX*rcXHHH p>.L$@$@$@M4[O$@$@$@G4.L$@$@$@M4ciuc;yR   '@ؽ{~3  `  Xl*  `  XNij,YPBNiA$@$@$`$`Nhn H@IDATHH @ ΅С.]:'7kLjԨ}$   '@h8bŊ5kpwao>ٳgKNʖ-+ɒ% Nw$M>K:$2e?`$IZYHh8׵u KڵeÆ 0aBy\vM9"sγw$#;wHݺuرc޽{e׮]r9ɘ1 /G_^ʗ/=p5k֔iJZѣuVZ0tUN>m]{I~xcW@y뭷֭[:ڵkg˗Klt=.]J*ɂ B /_>Yd|ꫯZuccҤIRT)I"+WN.\rbmȐ!RX1ɑ# 8P 8LށV\]rEO49OzhA\(D-X}d޼yf裏dҦMAŋO?dwŋBǏiӦIϞ=ݳpBet 6l0ҥ?ޞm  EWD>x"{EDhǎO>nݺ?r>}HFo%Jڵ^{=2dիW {ꩧd:tPiٲ̚5K a['h=q/'A =?h wǰ{ҤI퇬h!EĆ^~:a{'z>|؞$#n,O~|`T) 2 ?ox?bN7Gǀ>? kիga9, dz ~7oZe!|w4n\z@\a2Ζ-[E9shsCܾ4ih/˗4'l v< fao~wɘj7Ib3톴'xB{9}6ו@wv9ˆq`1JF3gd:^=pO&l͚5ڛ qkab&57tz si*`RXc :Н0a͛WLJ#|n:t'$@$NԽZJ0kiӦz"ODtpº8fc|akNt 7 jBcN}_`U]i}4h*mF&zH%ϜKu t @XSࡃ0`^25zi~2LӍ6oެ'BaX>QD:\0a8 sc38k+X"FA/?Yk)"  qڋ ʨQzNHHH xPϵdOHHHH Peb#IHHH xPϵdOHHHH Peb#IHHH x2Lsi,߼ys6-#  p4 PG_g6ŋF$@$@$@/XHHHHg>cA    _PBeHHHH|&@3-n:1cF`IHHE4ZBݻeʕyHHH (@ D@ йsJ8qŋQļ$@$@$@$@1D h qc5nv)XTRҠA={[!ƍ^xAU&={s9l 9.D86%K)TU֭[t/^裏e˖˖-[t={=;w^ jMN~yKMH~}<3gu+(h4lؼyZj7n\cرJ@?QhQsWk׮>6"<.FsGygZXPdk?<bB IU*rksD~5n̚5+LU9  $ŋ7Mv$$x0aBg/֭[+wCکSҥKڛawˠxϟ_|؆1JazqLZqٳ[IMBn v;ݥ E\ !a-Y(P@7 ?5ӜfHH(diҤky^|9aOdf ?bv6lX$H  18iZ3 "4oFoiӦ偖D bm$̖wBׯ/? NEo嘔 MdHHH 864"8y 8HL wT1k֬q 3MrB!}노ϦM0A =3լݛ^19ܴɓkO1XȜos;&a2fΝ[_~^P   `&?]K%aravoӦ+VL*V{X* 31yO?ԥW^ѳ1{_CE-2e0314    XO @p>zXHHHH (@EԳDA$@$@$@> [hZp^?)$@$@$@ˑ D'l,D$@$@$@$+ P_ɱ O(@}B$@$@$@$@: r[͛0vHHH :(@C/D&JH 6?CO. m~ݻw?1w: ИcɚHHH kNZl))R~?Ξ>}Z(cǎ}MA%@*ӦM#_|x0VZ%ժU+Wʳ>8HHH<H8ԩSGJ*9Sc *g?|b_rEƌ#+WvI͛7m۶bFcL{z=gΜfp^*'N,YH4i$n\ν  uၢE ~-[޽%I$|S t{ѿK.\ ǏD$)&? y敃JdӦM AG# `'HWڵkg˗Kl٤jժr%}-&M]7)W౐ي+Ǎ'I&u̙3ww4^y{t5-*TH3GLXbO?-;vׯŋ'֭[Nn:F$@AOgƍҾ}{XLRҦM+G:h/Ɣ)SB /ȼy\(x?aÆab;E#G/0C<ΦMKFN&l2ٷoj,ݓ'O 0 <>!_ `($#̶5kK{٨Q#ӧOK,CIΝի`R믿X' u.|ܹSnܸ1T :uJP4}{y3ȑ#zvj$aygݺuzh8q\5_LBs(O|ӺP }v-B˖-+[l1H"2gh_kԩ+W.Q֟1cL.Bh]Ɨtɭ FE^ެvǹM60aB6bc<7?y_ϻ?>xSBqh"LK7;fm: ^jbWŋjtcȐ!V6Ԭw#{~7ORɺjK`ɘ1qYk*NPBdž4YƓO>ip2#e"I <jqlC5 < D@ɒ% ~E!J>37 L 8jOYzcX̴s mڴ1u cx wna= R7Sv9pćׯ__/w^=y QҪU+'5m! #P+QpaxP !ٳgKN3Mۺu(OAo3h1  ta׮]zhC5A)>y/BpҥK >GSm$  8jSqs֤Yfyf֭(7˳1kDbFf~ܹs6bO96y<;I6H@x@„ zX{zY֭[ p<B%wDCxQ|F 3;|D鐋f pEb 1dn7 b4(ubf7!9ρ 3 P1PwC(49w>0OODt/}pXBEF*vO6M?3vjg$@$@$1QJ(!'N83:@@ PG Fݾ}[?0̮ DVY~O^7~    j~Z^~e03Er̙(Onݺ+h`]/HHVR)X|7zDtu3fI ϥ͓'%d/^x$G+X B֭]^`IcfΜ)6mҏ]ԬY3X~ [evIѢE?ԅ5jȠAP8u<#GH ^d ?xGGbǟ|IyHHH3G ЫWƍ}RbEI2MVF-:t Ȕ)SB 4o޼0Wy6l(>uܹs?ԟ{93j1taPY%KqբJ<~\~]>kxqo4  (N81Oxz&ن-5իY6ҥK9)bJ=n >x7=.۷o8 lޫV9'*}!8솵r_lݺZ?G980͛kۻwo۷̙3G;u#+ >\cTkچ&(עE r : 07f5ڦ'MgʔIB%`a/A+UAÿ̥pc{?P߯k׮HHdEL`T3%Z4  `&{?͒p(E}ݫW/ٰa<? (h%0W<kc]gѭr,*@rbbB N}O(G'V|:y' +&V02̫v  7n,,_)͚5 *p)>qVڟ hF%*&QF0>~N# y={:󭵾,XNjʽKL)++c9FK/-ZX.SfF'XRi :eڵi-q>_% >o!9 |e]@^ @y={4;In)x[f:۴i#SLYT"W]&nv̞b7#{k%xxNwF)Y;ⱔub@ӭr h!d[IHHrC=#ZvCTBLb*~; B!7SOk2׎GL){ܣ}h3ʅTk8@L'GY(@mDsПI&jc   B J qg8Nyn.)R  @$@$@$@QPF<$@$@$@$@ PFFnj$@$@$@$@QPF<$@$@$@$@ pRdtk׮t $@$@$@!@Zz%]v D!)(ԘHHHH 2 蘑HHHH  (ԘHHHH 2 J7{'7n,]9 @pRfӧ/,&L(M5 hҤw^vm!ݻwĉsXc櫢`ZD`ΝYfqjےCҥ4o\ϟ:Xp >.` \\f=$@$@$@$@F    )nVF$@$@$@$@ @N P7+#   gHHHH (@s P3@$@$@$@$StDS̟Ҳe5- ̱59C͊B(@C0xI$@$@$@$}>c@$@$@$@$"@K    >c@$@$@$@$"@K    >c@$@$@$@$"@K    >c@$@$@$@$"@K    >c@$@$@$@$"@K    >Xr;r%̐˜`}%;wR'޽{۷/dKhĉ{R~}ܹ̝;ZJ'5-[ȑ#+$e,N*m۶x%pKƍ@MZ3Ql޼Y;0yg*m*E P+eX`\}?AƎ+&L3[oߞV&&.ba(RڵsjL] -<@SuAmr 'u,{.w-7M6^z`& qƹ s0cƌJJ1Ap >yٰas9 -ݻ̜93!.|3gիԭ[7/>@/_"%;vwL;K`ʕh"y'dRFrQ?7>?Eo!CTb~$}6Pfj |7͚5Kh m۶M˄x|~w_￟ϛx2c|>6n(j/c(<we>}n|Mڅ3a XBF-kNn~2@4X'|b׿Cg}QBoϧqAP7'((c٢E kz״iSҥ^:!7I 8IfP㞏^'֙^nݺ5|c<1 * ַPoM Xb\w$[y]<|n  \q=Og͚P3vw-!.|SVV&gNؤ2ԍt)1'e,1~5=\3>fI 禚U2{; FQMɀ+'SEbMK;uHԽAuM0`@S79] >StRݘ1c<( 錥:_puj9묳ktf$կ~թk8[wD;`?$E&w@QwAt k={СCMP&">`Bo!4GS+W?m4tӔ޽{(/h%7il jyQ(j*I(ح?=HByi"] K X"3P˧ąK-B &J=OS(@i4   (܄T& @1-d_HHHHP $@$@$@$PL(@i4   (0Hl" "[ݻS=f Ėhl #HE7Z馛R=8@~ߧ|:uwߝv_~?^᭷ޒ] e8"k_{챲lٲ?Q߿ qٺSNJ?ۭFo۬.GH C(@3Ő 7;tĉ `=qH,V!Ž?xٹsfK/ɸqaÆvŋ(B~c)n?$PRa%&|r#Beɒ%}OO.zThAt L3g.m۶"J>}#.]X<ꄵ+g (l$PR`lݺtI_єK.=RVB[n6])+B>裠,X).9ꨣ,MΝ''_ >\ꪄ^x,(_WS&M >rq@TW6R'$;CL#\wu2ow l/2 =4q[n{N9qN-nڴi?iy*N8r7TdŨXtz;Pqiez Mڵ-/"tJة bԩGu7nt:nн꫖v̘1և /ҫhtjt1ͻTX;RBŴSQNbsq*l p׿}6-&M8A;1\:wje ׿vsxSNş5GA 5zh裏:wV M]f͜Zl aj*zjWn]w饗:ꫯ?kZ-AdFQ:]PHgbٝx7 Zyv>o=Vڎ:uݮ /i =A 6X[u- M% Ç}-Z/h@<Fe@B#TE"ɓ^`?؄e]f A3V .q|^@F p ^HO=b OLabBT$}<]1CT +Q%xNrfSBU/؁z7WXо7xC4jE+WZ#]vS-}3ϔ5̴` ?\T#= `Jb@x-1>/acyf[€XP+p(SE/w#6has1oBWoP\Ю/H2B2 dQkP\cǎ&fsr {7}w/K:Pqx/">^B`GLFTz:ZźWE/ }XTx dhf8 ,ZtxB&`]ED+CQ;NNsaÆ0T4N ©7p1@x+J^HہpH#ƧSXb*ְ’ \@d"秽}~~WZ?vTw~%C{ߙuԯDX稻㭍G; o;Nu>?֢ X&HiӦ>}L `\cXb'dA4B?~|@xd1?䓭XF<;w~k˫$@ /W - R1TdlZ֜:`\ rvXj4<iY: >>(cjYzQ\ " ^Ŝ!Eymڴ1D&Cq\sMj)4O(BT;> pA*L4ju?SO]víR8Y"Xu:mg n.P^36ڻ?R%;˩oݮw#Ҕ+,_~9`=anPxbzVv?SO(Jp?ayweҲeKyWL=mJP&3 @.P2 -qF{#;RG9ƪ8症Yg%s,{]ve$ G-]vxh}ړe &NW >s=WM&z|Bg{13p;lC>}رᄛ7o)o툩)SIF)7|uQ5j$gq(SO<,'5jKH 7n(zֻ`z^g?]W7XO>dsb% zEzκs2}tu&s̑k֎1Æ ڋt} :Tڵk'GyvL/t|'7 x[G$b}ҥAmڞ?~={~e$Gym] u, >(?ϖÇ[=Gw^ٴiSPGyQGI&MdȐ!&vQ/, gKrUW ̣['I'${6kիWˀdʕ&,!oر?ko`Yw  $@$PBjժe}W访}o>_n̘13fi*ׯԢiq3gtjtMˇ7x6Nzմ||'7 x[G$C_j̚5\:p@sWAp)4bsDu8Y;1_5}:aOv[ 뮳u~iA83#[oe'U!+Qn1 H^JW\qMGc (Gm. /Lc3|k =,E qD9^XyE{$c 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_@IDATx$ֆcֶm۶mcֶmYm{wm{?otWVdd/"d&A"B@! z zU#B@! ""B@! z^[ ! B@B@! UD@{nU&B@! {@! BWUUB@! ! B@^E@WVeB@! "B@! z^[ ! B@B@! UD@{nU&B@! {@! BWUUB@! ! B@^E@WVeB@! "B@! z^[ ! B@B@! UD@{nU&B@! {@! BWUUB@! ! B@^E@WVeB@! "B@! z^[ ! B@B@! UD@{nU&B@! {@! BWUUB@! ! B@^E`^M &F૯ kv 5PaQF s1Gvmp &_|]wݘM6٤jW_ 34SU{ᮻy0`wqǘ뮻VXj4`0hG9^Z/ jmG=?᠃ |0ay ]vYi'xbڜb!XcV_}˷9?((Gꭷ rJxW믿Fq0$5\3lqUBD3u7gFڔmUf}lׯo߾ywG9眪˪6?~0 ;e /~lQG1c_|-0&ob _XƠt}Q>6wqGf 4,h| hbd!SL1EXz饃/'x"{?Z7|3L04+RvXzyMW^ye6d=HcoZuN@͟0~?Ӳ'̏5;Q]e9y8x衇/xnVˆrȼ?;l68s0wޙ-2kzl%O/^ 1㭷ޚx裏`Fshc1],:"<7:C2aa2~mX,۸RKe 31ld0m/mys73{+W+y_?~kJJ/1͋n>|qqf4ϼ=I<@fT gyf6x =334kUؓ2L}otvT;FB``# :G@7 P:z^{- ?Ԃ LB>6<~:I`xmg>N_K)I Jy}fR[nljOp jƞߘ/X'[]x4Z\/B` # :@7н+Pr-se-2CvA"d~yZ/i@"O> v錀BL l9&f['DM|34gHhV"?uCdc,6=wY@@@U.pj$ccsm\rpt}1k3"5tFҼlJ曗+;(x}5uJeVJ8lf`fڰkO(aVdQ#,OO8;;6?AEg24W=C_2^I𱸸ڕ[mvbەU*<|{-ӚEKG`a?npE yXa-[:l0' ぃf&}M#&>HZA~Z Ӣ9c#hc Јb$`I#`ެ$62 g MEV*Qg,C;.m]| /_nf~WZ2녻9@@;hra21bK0e1_qY>BdӹO7`b1*9YGk.Q|I7]v%j,xS2!Htz+?!S"@AH;̓c#Tqʴ@ δZ,ôق . ˴eZDc) bH΂&xX_ }1"EcS{Xfc&z݅h`O: @ kcaQ4T؎2&Rgwї51d3Cٲ(BG#qq$!?قȱgKt;t\k]v>6RӸͼP龨tŋQ^>U+{kr8}@eܔKS=H-سk@ZE-&ci@-x{)i@mV W_=L68Wsb.GEhYH{Ec@fmN9SԘFA Z<8Z eڦlwpж 1LЍA;q_PQgW {ծvׂ iƿye%f_#A1@hY3B5ܶI{Mmwo34{k]ThgRmm/h{M:Z[na%9묳u $/o/D ,ZPƝW7mk7!;NUE20icZ( `:উ Q.++iH:_`%/,AT!F\SMՔYQggmi #ql1^ 08 kc:5^bpfʯE:p GeXn0Q`'J;JqMG\b:jcw.֫B'ItUB(#X}WFm=bo _~y׭ /o*M! @] P(G~եl"@" ڻx6! @!tI1~۲+2. s1GϪ*L~{Xfe裏V^y_k#YiǀcFSOի*Gn" MuC"O!$nV p@x'škg"\>ߊ+N?KN;-lєY_A& y8C 5TXkvۘU;sЮW";H$c rJ^ꢝ/8tx1Do5S:Sq7"L??VsNh1 .`ױdM =7TSK/;cGw._8,jMu /fX"@0Ӆ ,{ǻp.ͅb-oeiMƛԲ/l2#Jo7|wy1tIcw}7Op 3#wܑ3A9F,c4ia632_cZQLId͸Ŵ,P)3Fp3:c#~"裏<]vYl<#ZkUefe>ekua~̴șitdFUVY%[nK%X"3B_ÁX5I/3V6HL[Usmi5ű'|j3 e~'4y@"`֥ =ڬ(Bk & (k0z03!2`w!$<ފlrr<3l\)ޏvlZ(bFW>s%L7t_ I5H*Q D]&bN2i$%<~Wᦛn: .>ctFxI^M~m$ݘS)gzc!C8)7eQYYoe: BwߒwUmB@4 <+x .Eg\ʎ/fS'3Mu:j/AT#<I$4ue#McڞjğgALLJ~xe'O?4j;کr`tWs#mtۛCѸuW:f쮿_v8Cnk.@! {X&!Д`^6k?3_vꙀogyf BU',Cig'Щq7ЩXDs7fiS.1?s5UԜ>Sx{a 'h S:IZ`pdxQ1e9^ՔH#!O& kp,.i;}AJ nDoqH.aЁ?jhhZT73 .[~=FM1uCt >TB0#h 1Ӳژ^{-D/ac{#|Hن W0BI}I9ѿm%E%-Ť{E ܉nj>"D29^4s< hxِrfvE0tYpD~v=+BH0+5~~]5űGCP&0~`.JhL hi0q//F3m&z4mi&x^gUWd:#1jD=̈63qf"3Hefϯ}.cDiecי0Ft03' ޶%ʿs`uF:c;3e61#ۈg,@)Fd&.Of`.?0B#ió>337g曙vUjf[JcpA̍!`GrcD>E#;#c[;!ײQ _v4 ˊcoj@lc{MM"Zf6HjDS"%r!@_ }$hВ# 7&(Yڎ2&`)76xo , QeB4]:+|䱭92:ukm=.ūNkmgG}9!*s%DFm<j %]#uGԑ;[saHu%|gm&OJ>)r=OW[_kz֣B@tvC B@!P"5B@! G@! B@,eB@! >"P%! B@Ԁh `)B@! @>*A! BD@kKYB@! h1T B@! 5 ZX*B@!}D@JB@! RV! B@# } UB@! @  ! B@t!_JB@ Z^_unO>h0_~ oF|OjA==U-fۇ;/L1au O 5p[.L5Ta'tRM;3$LjF:<5Ѭ~=P֍o6\}kkuk uuU͊~rp'v)vZ~)za{w_>\|a ~aXb%ak03>8L;Rt '~8',bvm~8{ ;oRK-UQ9#\rзoߺ<0~.|]w K/t-s1Yf% ˶[Kor寿 rJqk)fͲ,`a1§. 0 b/*}\~1ȌfF>c_?{G|03vL3Bڮ_}6쳷KJ-VoK.]t4튤GqDaFfm+h/M0=쳙Xx:j6D|/|I^3<ӌgU{pG2h4ٺXcrfm5!w=d:kf3va}_V]gf^|._ިmQU@,ĥLf3(;iJӤEryffbR=3gq>[s5D,LˀxA(0:3 Z^wzpf뭷^g 7\nlUVǷrKf:ɿJ+#@ vÉ'^m23WlvpYqb1djnH{oΈi%.ǀq{ĉ殮L3<ˣI{X2,բxp0jĜ?(`a1餓FmkcV틜Fik"EDZh "D n4k5Y'<b Bwꩧfs5W|0CyxnfDM\5 :\pAkxuY1BzF-Gc_|x$˞5~>o"A<믿rlH&dc6#g#4RԆCxw Ҽ[Gj>Q+IhO? | /ZT̕ bNm$cR$Bkc Tƌ4",I!V 3}q񄴒N0$LiS*?(7Cd>z41XJdi+VC'hO4E9c;]Fm3*N4͘ŦBx~GSMM>6}li[nA0ɗZkm &q̞eYBFM"滙}Ǚd濘_fQIWfK<'뮻.dk(r b5^8j|[|CK/4c˅Z(C[lAGq6"Я_6i5d`榯꯺x㎋d!Z`?lWL姝vZιD /nՈ"CqCs` cHz?hԔ;&eyvZ3Py/@"#Qa1njAO=T0"`}0Sp )(c*yh??d! FD 8d) _ 1-_ >ᇊXeZX`٘@#a=cь]wj袋Ӎ~-`%#1X$A{W0H?KPk(c0 dā|ad+x;ƀӒӠzQHbg0&{iGMw_n&sF^Nl7Xj` xonAzWqg̬3͸KE/-rs.~+?;i,HYD@yt6n˅ 0hx !ʱG2/ )g}r'zGI~?1! u Dw̃F4]|G M5l;E!:J5A+^/RfG1}0 ?Q8E KL/BADO)H>Sq 0 7|s{YhloӨ5/ \'$lƨ(@;]&k!i U3F w'`;'͐:"a{.O_iE]8D@d"=B`EŃs%SO"F?y9f+lg/Q@xpx;ͯ2Ô d ɣ>'48=jHDŽng{GR+r$h:q s -'yiݝcF Z6ll]E_ s^|>mpiwAV_mmUׄ@ C55z0CbRkOAURR*h@51`RH*4䏀hܜ'ZG31"d!dw1;~C=Lxzy|@ps3sItMI 좖'hSqmgjZ31ŒO ^'˃ ~hB72!B 옶!`( $SD!?wNv0AKN,_3ha7y% -*C O "~>E@[mD&`P#LFE}y7@ +lj92Y@Fu6s=Ɇh!$+B .g|A8ЄbvM 0Y@D1ChseF  ‡o)~e⿈f 0DC`Čv,7<! EkOtON&-*E䛼h7} t6G( RIm.Qι !mQ$4 wϿ ~ A3馛9[%Ə~m< h OEwq D@.B*xAb*)ǐH>hжO%0MC* 㾕&a )BCF D71yԸD˵K<ҫ.&Qu\7)> h#D, s;ki6P[hH]ZEp-^ ~f_Fz{NjxyZsm3Ղz|2^eh^@ nG[5]·b >D " h h V31B@\?MCBb;'׀A1D3Q5AcV?u' ᣌ/+vxM,dVtCRє$0 … 4oJrLD㿉6ď4~ ecC1y;aU .ʄB %iQ}pG D X'?p䓱)O߄;Pd] ˆ'5\"#,ƒU L8cADN=qK̞a=!./D h+AnWA8`FhAَ ͢G`~X:!T 9rIHncGh<īҖA@o+)3Ak a=Q9CK98 {BRuX4_\XT*6g-oQsGM]&H2! 9ZO4hnqv(NY{aa=9-E[6! @0'kx=O˓N:)޿v3G -B>'sFic;\KYTVĞW=}7b4{0lhX#yu ">I(FFlh¾hZ$G|ȡŴfm4eDyŞ=cchL@ψ 4b⧚yAw1Sc%܅-}T٪(4DuE\U7ע#Jh_SSϵSa~]RSdn%74 OwKc|Y9զѶW|Qh'I9\Vy|v P!u"pe ɄnN#= 2!k-픁Ity!:Ho81\_$ɼ"Siț`.-afA'/ J2w1>AS5Wz;Z-s;tnqSE3FߌD # VZ1LvK .Ђٸ@`.gi , *=W)`-QPF3P! w(h2؟}!kf[k @IDAT}F{LP $i]z&B@!`6؀9B@! huD@[}?! B@4" 6 jB@! ZVaO!  h #B@VG@GXB@! @! `! BmVB@!`6؀9B@! huD@[}?! B@4" 6 jB@! ZVaO!  h #B@VG@GXB@! @! `fE`Yg } }-|?.S:+eۥ}ݱ>ݹz$Ї>M6$ N8kOG}EaX7|y'nhVzR⨣9t! І5B[o5:t7dr5ׄm6?^jpW3N{[~{]`?|w}G_03⛽/= A@`?px衇iή0sg}vTA338L<MboIefO?=oz%h4 Q]E`ǏnhSW_}k&/7tSbwqr-|͘[n <^på^N;i6L9a} |I<~KQF% l*KԬN:a1 J裏,? ni@9쳇{キ~<&`"o6wf-8a崞?3>af ?|j;~C oq~髯Zjc_!zh>N4D᠃:/`sOa„Nv}cE _|qž<3r+MIB@42"<:jhBV_}H*S-$sYf L2I]aUW 30C8–[n@҄`zH{{p SYm"I;m5 ^BۖXbx&_|Lgs9s25fϩo+C#?~:la#4R$6W^9F??n@O9pdž*jI=d>a]w sNS`:V:qwoO_ mYc .lᲾh`2pE ]w)@ ` !{ǻp.Zc͞x≼F"3 ,ɮx4܎; qpG<Պ_~ynZXs3fF2#$iFjcW_v[Mf&obl!ȼ$^ve:#uZkvY;<și-4Dfizvg33giFcLÜoy^LCnfD0?oوi$_8餓w,(?gD;3Mrf$>O3wXgFb_zꩼ~>Js*f!l1ݗh,L!,`Eۉs03c;8|V1 h %](ZشMM7] c1FxW%d:h+ > -P5Xa fFKaO'&vvpwj,~E#J+E-ukG5)Ёj-&K.$nOގ/obϢCU&oF-KI o< 0zt>DGzQMR4c9=CF&_Rrx/-P\(C:1C!e9|pۑW"mN6aj6[[ouc=ne̓|@gR!4&&~4Tna0|kM|u$h.!EdHHhV!3Aҿ_=f%8\4ż}wG>4#<Ҧ=6'm/~~ix뭷6^\e]J=c &t$h#)/(F{VѸ*F@ ^ek+DA@yJ-Mҩ:i@  9yc?][Ńf%&{.JcOאMH5A06dυ2['W&>!ŐuY'j;L˅RƾR72OTgm.. X滫R ү ('JDA@yJ-Mfx N$a&. &q|,ٖ2bD7y-`&^ey"k,)FAeYXMw8B82˶ElH{wQǏmO 6eFMD C}jۄbmYWďYT >ʤq>`S;O4:,D(bۙ9O; leB+ 1[FuU}az\غsIދ.(cDA@yJ-MfxHd%;كAV ҅i"Ⱦ hwF Yb)I"cj(>|KdPDA* M}K<{n4'Ci#x<b1ljh@7LlD;݄6D>ĖA s>$?]Z/}g[( v-dT1e[)Ϩ4|vGҾfXٵ]vN]+@#,=_wSjO5uGx!<~3Pb.yBr:#J~M I66~1mN \cpZd9:ŀ$?'$);!'cyyDlqgfFm%@#P )ےQ7xau M4Qm_U B@*,O=԰曇v-߿îqafscsW_uxM#(o&>GRO>ddI¦ng0h޻ݕLO?}XeUO?@=/1q'I?|c9wU=sk ;Sc=]w3T! ZO>y`NwyB} vXix≸?cUyuW>駟pZQ,`뭷^eQTSM{p 'QM=%]@`y.\9h^[oaɮ#1hK,<٬Κ=:$43gWN9{5\O[fe/~즛nib\p줓N۱ꪫff.[=*:~gLGu)WK#`]6te-\ֵoFf8-Bu?aVo /6n6g?䓘k쪫ߙAئ^83f?~fFhbCً/md#"|me>ÚP[aC=59ꨣfQJqb-"xGG933hfм+"[{l` ]QylG>jKs3q:a{\>„16k^ʾIDM&7|Ģ_mV6l]{=WY}w^f?Y>})2;s9',X!~,_xߧz6 .<~idMbځkRNظܙ2Ee]X =ٌ3Θt/Ń?>` HQD/IeD@+cg5zuErKgh6hǑ{%b袋f{o|25Pd&tH4v .KV]tQxN?6>:{?!Ŷ; hKx7}q7=DrH{e~]@5X#i&k!zjw\H6fEY$Z9ϟ}ٙl#>X+w_YwqY߾}_<㏳x}͸WFah9N (yg+Yg. Rl6& h[,h3ct%}e>b! ǁ-z뭥vmWzR%lj)qZ͏;c٫?-p4.QO?AhpgAfzK~N;%K.i79*_6,j(sONLPY+qsޒK.Ӷr*p=`|j<4EID7C4۲.h ^w~硇ɿ`aב/XWu/hRx^ {pm=4h QQGW"2y*Әy`!lc"s~'?dXG+ȭ:,0/H)]w5'8Tl#a44y!~,! CY",Y3!c'xbtL_&6QΡ'c\ *2,Y ?>/q e1r?}g\7`:\>x(A}Xlwom{V"O=Tp0 +\&rWM0vN6dq`Ys512Yn@H +v>17qcEB[) ?RVLx@o]lM<] YzH@r DŽY*g{@C0|WFS,Z%hO˄2AK@\-E"SD7Jc=le[ .5f|Y~P.J#x~dh!h,>B"ZjHYL@C2k8W/Cp@)_~_¼ql2[y1?~[@_̟<pbN' ꁔ3 7.L1"g[2a@9QK}0:ğɊQLLL)hPDdCd)6ɏ1cb"ma3Wj Lfv$K=A :`2-hbBLz d׵ NMKTF0Z0va2!n'.;G.KNX\/506P/ĝsoi@]\@J"h݅sfD؃ :Ds~.dC7~'4H,xAL]{b*s^\[g^]6zr׀n1 >c52LY4sq/`OhK!rQe Ikw³|Rb~}"âZA䔮:(&瓖Ox]1Q6 |uxX):L L>c P !5SrK!DV_//c|^x) xAr06%QhNFhg=ip- rݯ0;[11bG#y\`Šmn+߹_^:w(^ <* ;7&R^arG@@ V";L8CTxaͶ!pN )SL.E6wmaa\Cs?V̤و< M'td2u-o᠃ZtEFƒr8?k,MzhNZ!Xd;Ј5"5=Q/ULᨇߛy$͉rqVz2\HDTa.X YBRJvрq_9ְ͋9Mǐ%H5~.X&\CV!A׹v/v |-&ұa]h(JM\>X% S7jKAЉ,s LaL9,<c4,J90wp̜ZayOC }wGxNЎ2R3,鯤6D@kër|>6/.VlGĤĤKPVL^N`՛l,g`}?<A*0 Q? yDDa@~L_.LN'LjEAP1!1^H6ZIn^G|b:ѧt7'Q$CXqof¾"OZ%FGY񀮴Wݔ BSG!)Eo6.=e 6dX.ʥ‚7%JǘqAxR6o),Xܥ2;;D?'ksOXD@JWZivͥ\y EdM}([e$C ֋I5h'x(d&kd"qwphgZ7& &^Ѻ@ !&Vh0SA`V$=XBx?&q>)۬6S[*Mt_ƊL٤9ref;&P2auH(½ QBX.G=c Ek]v1N*Z߈;M0UA:1"`"~ }/׎T"B=ZJ3>qX{( J5¸0o0Wl<$d]kOi =CܔbBO@\ [mD 3/;w9vb E;-GX(@n|B};ĭ!CHwQ:6`GƗ4 b7L3 e(= LXQK029R>"GhP+;9~r6}I*\&R2*\k+pK.>}fL(L䊉*9WS`nHoqf>a˸bi{r3 y\ A*爖=PSh k~ECdKl֘9btl1FGtȤ/C H~o k%AP:CͤE;4<'a(!K!i܉ʉ~%e#צP`[ 癯|g~Dr!=h9)'|w!(h \1_V Uel 0;gBbcu I7o82_VL>So|jB˂ߕur IHJDƵ#Y0d69#`agtbK;f?feo]O`/g> ee3=g~/7G[d"gJpJ]aҲ2 J,mb%Hĭ8-f6. 7s/'!k1 3h R+ kL5.>H !δ+̳7SAhnIC>BK>,MhIbmBsk3pUb  1;E#\dD@?{IqLvÆ-U(3J?->* 4LԉN=Ҽ2dd]*MejТ L3S!žw'0>L~L%NҘ y0pF2Y˗icLb2ၚA ֪B hp*z{C:, fkj W~g, ,{a` 57z1 \$CYt$peBY]粅_4nY*qLy; g~h',(33^y3ςBIR}'dO(2wH.RDMV)~B]ɹԵ*-W=h'<&&C419Y4{Jqf/&f'„*sVeݴ9Mu]=FSW %<1M)ڎO[Go\yefZ'"1[Ǩb@~O3Q&e"ߟ/a"yt"_nؐVX9AɏрzpG4eGZ0[feFm~]y|.DžIGX3 bp`EC7#Rᛦ84JSlWzXv+$X`2v$64c.pX* j#PIi$y<rG; QvcHRHkoe\KL`7UC8dus䇔B9v4p*" Z_<4 d mpՈF1Y#LEKOZ$hxi0YW&Lh̵<ʄf;I}@C; YG Tg@RF uOV4e3zuX:a [ -.H _<= lɄ Ri6յX(\#4ϖKh9kAﴝ?X"vlz+@S#C&X'4jI" ڻxjm"8m&o&O߇ \D3C^yPHǃGdRǗQw'66foۿ VĒ CPIx觤Rt4h|Pbg̝<]k{hF-++ih 1e!OfKQ1/S0'R#M+a 80%amEUȩ[bEG@c~:H(ԁ֓9f1#H! vp*.eN[1)Dym0;+m➦LʣF6-G=h;PK𷫜[4<}厙?*|:|Ј29y8( -iqG`aDt>pg@Nx:YmV!hxW&p*LEͅn.|)C 4!!VpbBik3ĭ(,p3p3;™47l>AVi&X)O)ٔ'ՆY;E{޵=Q1j"7tLh-xxüäHhx#k|Oy`B1)6*td OWƀ# "B4jyܓ'm-cҎ8∞\'O:t:\^IbFWv{~J`AjDyFcׂhu?e% O@i~=4CfԈ!P/aLkl``f4`|͂iFjM!XdnI&#؃*7.؃)ϧ#`$1Xv 郑`Zg6t L d2}0בnYkF-_3s fA6c fWӚ3g35-f`|'4vj#m4KG0_`03X{.NHǒ]HslTȈv8])^}EFjI\ DӨ@ԅ uA4  (=ߝn!7tX mj´Nk0c?q^ՓbTo9*|evmFmsr0租|裏u @k! Z@y뭷]{nK,"w5taa饗X; qk9Lw03333333affffѿ}?wӤI$]yز,KKA^{m{'Zȶf[{͆Qyǖ_~H⾙{.fѶ-@j"@ 0ڃ>h<-"mju5جjO?tv hݚEVYe{7瞳I'Te]>b-W^Ft͆QlAi7dIh]mݾU˄@C3si$tUW-& 7`WLc5-DZ0B^?HmdMf{Wb[Fi٢˶usW1Kh+\rIuQmV/>ېϜ3ό9)ڶ ؈#hcwqu]x첋V/2;裏mzwKu鍃>{o GٖZj)J:Cmֳ_5xeb y1njBO׆ͅhsj+4ʢ&g&p6kmVi>lg5@[n9x<?|1mCrwm7c=ꫯb+M?vG[mwމ,gi9Ilg9H/K9F=2^{%9L:h`]&!|m'9K_MmZμ8' N.92rn馁\?R & 9.CZkKiB;&h&hvt|ֿVNxv[$L[o>&Jf^~'b)JiET 7x|TDkj'h(#٢v 3L/펕'žz)[gub?G)ٹtGz),e!jfke'62ƸHHgwqw /i&gѯMhu* L虀&I&]30=aAZ%q^+4Qfa%bO*n?̺`4$(v$30C~7fԥR}ǥ2,PC _O`_xq衇\چTfX?Cơ'gs O> >DC@L5M>h2ϳR!$f҇?fGBK ĮHZ iQCSYw!DL)f~&(X~ ΃R-x_* ~]Vm4`W-CZ^.8q_JttIlBImR5@3"0S H*P"_3ىhAV^|hr^K 2DP  }gByB O 6jSw$W_}u;Í(r!2 $BVrQȁe2[ӨW1A:VKQ9s9忕&Uuh_G`_G]Q! BmTSB@! 6C/B@! h!D@[3! B@4"KB@! ZL5E! ̀h3(B@B@:SMB@! @3  : ! BmTSB@! 6C/B@! h!D@[3! B@4"KB@! Z~-5ETE~_gL3 5PUB@RJ/O?ml96S 5 Zh:v{7mᇷ 'xko]|6TSٟiowe?l~<6Xcޅu>c"|AE/[mg[,onk]p*nc9lVE(_# ĝכUO 8H#`C=UW_eYl0/bOtIW_/UGXb%/2^}wm駷gy6l1c} wyǮ*{7}W\(z뭶ƽDm׶K.d_[W_}p K/cͼkG}M6d7Ok&o"mzѯ>HNHIpy䑴Yg59MtPrˊ8RG@%'x5I]ԝ2}5yvap7X[J(lM4Q'iI&1:mUik3Ϝ#l;&tI %ʋVm696n.]wݕ.t 7$iG:uYcI{<ϸ)r-11WZ-"<='7ir!czځ>騣TL) Kh)\/|˔PB{{ɵ_)qA+ǘ7xAvu״:wW$cufKb~86ǹN9ukPcv Ůs\Fq( 9nN6[1"w=HaFmTs8y}vatv0Rydt/f I+,ɵ$m "X3eKԃvb@rs`gqwyg z]3HlqfC@*flu6dܗ3ݤ܌+B<9ͩTcE!)Je 4 7GE{o>B:JAՙ@:ѴsosHKm[pB )z}um<D죏> &LF5rDD2ړ[n4c2>AhcЪ3.|A zHG߬|ע^h΋L0]*}hO!:jcg|{]l.[O?=Q6XL>q>ORヒ>j񻚀.Rһh@j,}fMoǀʋrM7ŠAH1hγe4L dGydhT(_^}J}iD O^̙gyA,RaBB` bE;SMgS=\1[4?pi5RʋHxsx%O?%3)(/¼CD~bi}ѣl\\sRQ{gX|H@ĝ .q%po01I|2Qa-32YC+ lv [M "Zhpj\}20xdnfI'44LO:sT)t?)$zƀqa@*ά(ԍ@@>C$M@{V.h °N;) S]vٴDp= FW^ye뱃KWB&h0=1.i OH oe㤜.(&PԜ2^AFe\p.e3[.c[lmf24JɄ{ ByƤC m6,L=<]wum|#%A m _M&<y,=~m_Ƈ1s^Թ@bQ HjG@v̾D@g8@HGP_]~1"gP,L۔ǀ읁2 9FzDpN6snۙ 9+/ O=p3`MDs^xKkqO@x顅üHC 23f06~jK-^Ea T'4bosqQ e<[.'fl!r3d",pdqMh"&M1q͗%myWЌRj$fqh,I>9`4YWDcABWaF8BhsP&Rk0ʄlNf|5@]|4מUVZX /K/`I&XQʸAb"`,5⚨Xeyhp}b ('.-/X_jd\kT#1ec|2 m9>X@[R]KdN_~ '*k[^81Kи],Qqoṛ{eP̙OdI|Xf_sɫܔO͌>41rBUZofti5Zds˸Osef,sWX;'1^&2e| sǸQk>|mN`#c,m8ʹ\'сkI}TKi;kTc$נk#K5qc#OOy8X餷t:Y^ݢ9|ʑ8h娄@i@Ui։0fcى`%6QXuU÷0o# `&Ϭ}3g>f"̃yMM`RbNf蘌ئϲ<h,3BZcblt4>Z Nt)ڗK0QDz RhvBک Ye_gY0s@H!DhebpPM03H@ cLb>>yCݑ[C:t'I9@E1={yg_BF%%cO<,([hо21Ƥ_t'b[/??2f^714ԅ?FPVw94,#.} =sf4n\8>w\,uwkh ('I  E baPc%Y|?I 8Q IJ`D68;cz'"S=uG&@aq>krD˟À d| iAHżV̰`hԋٲ҄6B_x1rK@:Lgmy^+LYX[HQ0Q,L }U#sk&e\` ///O Df_ͳFIOm| h*Zqd  c7x #x^( ɹcZ#?\x_6 uϜ-C3@fuҳ/ELfHQ3T 7&KX CL5sYE ynz:??:GqkY<6ccK*ڙY!nƢ%_X`h;dBF,FBƾlY l+%#̈́4- ?Z8& c6iAH;ėtR_/kor }_w!6K֖c"uLNOJBrrYD@{φ*( ,΀KAJ^Y5Gyiqhh/ɮXC<4MP&/xeE14q"hwG4 <"&hb@@&b|=Čn&L@JxgBP\QvA)eպQʁ  /&b*_+)?Ga&< "2/U <VF11yb}L)=Ls}ښ%yR<<{Uf~@z30+j1!iaΓy.!VٚL&sy/&0m 1EY&--g9{A,'vbb4Lh? j0+Oy:"]ǪrEQ80e8Ƌ_6ҋ>,y&b#/gai^Yxat˂0Q55 1C61?10cF̒57(/^>Χ.Yh`BtNO m}\&ԁA>}s9KT&D`y|T1|:W$colbqRYBK^IxQ3|ju9$R:/n&EXܮWj/9p-44b "&Ǵ R׍dr@Y1Qy./q2dd2N@.'I/ʓY o\bsy,ԏ>U 22&ٴ^&1#䙉#rg1IeR(1?}AN!@VѴ32.Wc?L&dwnTyH2egz&F` .}msPvTwB!"jhUNKKef%c?%W)+|>k9 }JH-9:-fvGՙ 2D ԃz+(TxYeȁ>u⥁r9.8:xQGKZ L{y@xy90gwH9d\xL 5FQO G F_V!h/|!]$xa]S y>ʟ\Z`N'ګ@$1y.YW_lVƢ-;*c 1Cr&Z:c&X߬afmkR.,hI-( yX|Bea;EJAvsa g ^@(Ih]KGyH!2rNghR!QH8!LcOLXJC\q} \ԥhuTm/ԇ:vZ.[C@V 첉+z4}YH2keY) 9 1e1gp>Rh(r(W y?Q%_.ePC#˄+fFg-*A ~f΋Ad zbe +xs/dIΟpR).ˀk`l Bʹ Y] o ea^4L+j[OH'GMu@D " Vȓ1! ?L*sh!Cy hsb DGy5vQ cAQC616M6Dgr L2YXaPmI.O)T9"cԴ9Z>/y+W4uT qx 9_m5!LfY0u2`Ñ1ea&fJd?l6sKnd`/ +GߢbzmcGeO6 H:Z Ӈ*9{?^87spbEɐ-BD8D=lSV:S2Fp8^ I1@0GgX8&RC:XMx6Gs>.AY[5*\6}S11H(8S~1iB'2q/A byX( z乆4r ԙNjs>$}͓]ʅR'ʆxRmq]FY' +֨GSLH:G@s6Gh<1+BH+I6kCx1c{Y\xi`G#/Z/eZ&NJfJCpD&,hy@FAE#1M/F^٬I&1^ IO 2_,=yF,vR^, k?V `)ug-קagBD[a*.fzݎc\@\ #vU hqjBr0-C°fČ Y0X LA8WYrpUv `;h4g/1D@ +B0'R le^6 [+A1Cs )\3xQݯ4١9Z9HC_>@BPqAAs_O^徬&JKs c<{Zy$DkF7:*qQL?&u\H+7$u&L0Ws>B,X%7/L X?4Kh@2i}7<hpy)&o khBsM.<1`fGÉfW"Ye_ |+uqc2@QRHhpj\}DGIe֎~d+10Fsʋ3St2hr^ V`@| s+ZS\D[k-쓚5x:YYҜGH&1*!hM$)v];E ?hz+A+dB”g+8ĕ(T{E LƝ Dò?+_%.h: Ef1gl162%x֚bqs*xu}p5ƨr_f&-?}}M?&AgYtЮct9E@e0/3/rykёR M)yKŮ]9e0E;r p Ř#yaAy4RE n#Ds|q"\.LШ{@:8JDhq/D S>nYs Iq-\4E?\gH)V$Z7k^OHvgCix1Ƈ쫍=qV- 7dp,+y|23'暨N/Ls9Y5'fEbdjtbN6rm\#muZc>@kȢPИkd#} NJ;@ ss-y`{yyt+Зn770{ +E͵qϸ9=qgKmٮ_f˜RIsSoh=#Z#p:M4P7q]KŶdkz5Ax@n++aRA澈>A5jbt kvzpf1Vs3 N徫9ڭ {n{}KcνsY[RD@KPhC<hܷ-4JhmVkGW0_~002Se]?o̗ gOrZf ZjB!P?D']AvCSٕ}BͿ^4)h8 pTB@! F C! }>jB@! F C! }>jB@! F C! }>jB@! F C! }>jB@! F C! }>jB@! F C! }>jB@! F C! }>j́n|QaR!P"5S@_CoAN8ᄊMzmQFx_; ";m~Wp8N;l泑F&pBvamM6s4ÀbQC9|U,"6$o-bas9o·_~ DMd>ɃFmdc9f6+\6s6l0`,?݈#m}?̇3+aDs=Qq\pAoCSOm?>F}%?x`oK,}}Q桇j뭷^Taߨj}㏶f,BOԫ*W8&@u ba.w}V[t?DpC/w!_|aes16PC{ク{Wc{喳'8|f!dr @/ FzGk8SC aa++>ɵcoqZR( [9IN_N3\:5boDkkRKpXr(žk=#kbg)%yOri;⸓q'Hr-<M 3LrH;3S~kEKy.8ωP)OqSO-&Wv"Im)k ڄtrhsi܏>( >o)7T'&'ܑ<C$w(fi '׮I;s='9KIq Nϻi &H.;_.iC:O|͔G6=\Y&|D؝nS};6z=nm4Z>c+ /Z2'nN짟~)Fv4 7xӻ{B#iht17x[} -f5A`LMA4Em +`h,s=(+W,'%swNw2gyI;kMyD JuEm1yhN34]9]y%T]FG jKA*Gm6 )g>8a",}$"bJ!JH52QW\$c1℈bW }Cٸ(A ZL+nC>cyM Er>ۮ/*a_\(gLPą[܇6+ީvdR9ꨣ9_xos9PY;u(/GB@҅@K BA* 1|RNv9~ >+4NղG:Kc@스9 fB[YJkvv$j.N]':fӜ_wԖyy@::)RW^Nqr7"eZ'|Bÿ \Kh D@T !pCu9<$Ե `DΚ_|1Kr)LȌm%Q]̷h:!qQhcV4CZ;LuQr)AG Ƙ5 xU)ǨyD#c '0wm1]VvBPЖhZ #ju׍ 7IhD@/T!ƊXވm.G̰+|_(o/x M,WXc' *euumM"i#Cf?b"ZkE6Qg\ǫ ,6QK5Ĕk5\,E$>ݴ?K?[kC[Q<jN,c2tn1XXb _oHK?,zBԹYue]!  D44 SX[n>hFo`=\G!D+1Y9##xƒmtI##ϻ\U$:\Bk:vZY B فr p>.+k9DA :ه6}A!^qnE,ǨRHu-_>]F"p "}aKFF4,#XJ RSh6A[h&! AǧrbԱ!k`J%!;CEy $fH)i!]68|yL8(fLźzLNǻ] JB V)_WҨ7dh{Jx/L=2 {4CB`` .I~WOqv%B Dwv:"9+r).?ޕ}4Iy4|g!fhi;HOJU.ϞeC{ZL':t}Uf2.B@! UD@{n]L! B@TB@! @" ګpbB@! "B@! z^[B@! = B@!Ы*ܺB@! ! B@^E@WńB@! D@u! B@*" .&B@! {@! BWUu1! B@~?ngyƆn8[ll沯qn}caSM5<6ꨣC SL1 9W_?lka֦f[x/&l2[tE?s>3jlA: 6`K﷔?ZguX ! =hA٘Agil{neQᄈ~g nzAGq bK!\o衇C9vq6@>{ &b[om;sbsO/b?O6t8ӗPyB@! Z A\CZE4w\h{|90تj~ۡG=H^{e{=裡5_~yT4c5}6 3ؔSNirK7|tE~-OntMAJ!;ohI!N;\23۴NkhF{R|I[felI& 5Ѷ{Q71_FZj,wh:#駟|osho7&=! S/x*,*psjO3xL&iW_ksc)#]LV6ys-j3'7ӗD9V5įk-(k63F6,]t7'';<9M=Xpmk\/HskrœfqƴB :(mVq.+h5iEI2Kr"~8v7^139_5P}Q!д"e6LR@@oZ @Ոa&ՎWJwmdJǺ8駟NnfmHDM(Us_ȳj/tI'm$Y&Wt7ݧC=4m56h%w+*}ݷAH.uТ&0/Ҵ'v:]Yeo4װnuimVH|I׎B@@@oV'\ĭj>U3ݑ`*. ~ey#H$;7d8)?SD!_{ꪫ졇)\ eP?_1c+7|[oٚki_|q]Q^Oɳ>01)u793m喋6:'x"0/E"RAp =\B@h cA8nV0TMQ o:/"v>kl=#O?zj_H~!q ?[/ߏ?X:?|b9_>#T[ne~BF beמG]\=OhX}*`=i&kv6lQW^yUm݂hMq%rr^죩& K"}{Q5*,A@™a&d.D AG kl/1P\L1va)Zʗ_~9ǒQJ"@ 'ш QFSf{ghMY >$rP\L4 K~wy4܏C qФq3$g`GB@Y-FBr\AHfMpDE{q<ǝ)&'ɗ㮍;>n'{Ҧ9昣T'W ptG8ev=b&h ";D`AQ=!^xa餓NJTrgre/ Hp@y^' 89O>Y(l! @ AHh$5 }'c9#Y~ȃ\INBptr[RgK,yRD nO%\Ӛܯ4KA"+dH|FZLՀmnom>t6@Xi#X2B|}vW` KWBYm֞Bzz64EL۾x|պCjgm_X.fײ@o |r6'QC\ͧKucfLY+Me(4,1U˚m@q,U~OgI)*@c=6֏-jl?_jԪ^B@F|@wהR8N,7"V"Nl]w-'߲?묳"߶'SlEPOnl9ל_AE8|\8∈w>ɉe'#r/:'5_vi';ꫯ6P'H]K/dNXU ˗XlIB`ZF׀.Vj?4r|e}Dӭ!e,Jvsp"5h*S_q}. _Q_+3C[M/MZS/]ƿy?@aJSgHTx~I@}t9T<6>R#9.+,ݺ>#-Bo# 𒁃/n,h)+ Z)/JG摨E瓃v|>uPYo;QE/Nh64ӄӿ~d/WΈ'׀̿=s6쳛.L8eK8|-%bA~G_⾭d׵Ma:3'IDATe7?| IgA{0Iv'6Zջ\V}pX!k͟|.B@@ߞ#F׀j:$'Oꉆ]&p #ݑK.$גt'q m{nJkxCݨ; fmb%H'J;\rvB|I|Ck]x'Ko{wT~O&P\'z6! xzW+bhѨc\RyZ~~)y7EwuWo9*\2vw&_c7{e ,G"@h kF'X 8[ͱıkrk*q`L@<裱Rye9d-<.Srδk$~Fd;D)GcϋO>9;_"gJ|!/)i,L\C*|RCSm}s"֨ā)u arsIW;bBur)KBs:JsM]vYl,}E}GF) yrpr |?Ao lcyr!uZF'@û; ٨k¢eyxy>Qy}M!B>kɇOO[ouZꎰ& )'OhrHC+H1y X `y&h%B%7|P\"iBXBheEiָ" m(K0QGQFmr oT> No*_k5AzmCG^* .! jG@vF'FzJ=Yrr0RA^b`+$5_'L9 BBW/rM9">іrbqLs0Cw;ת$ĸ1X6 2LE %ıDcK}Y(,4yE;@xȇ\&lX<!3hYc+$\$>['MÝط E._4w~qe>忙Wb-Vs/@hZ{DdEp &Y pHr_aI#eB[sRlM qBt#ĒPN̵Z&(MďrM|e?6߽w.vWs"2|.I=Ot y 2&s 7D`VƜSM591_ 3Nu||\fg^|ҔOpƧHa'>|e ~rZ*rS -BOHeYIl4 ?C}#BXjpJy\{& |r^FH#p7;_7?7Y*)Q~ ~,*(§UeO݉bs{Wb0Krr@Y=Q+%Ionn*lsxƲQGNrUyJ*#@T:΋4V+ ׸o\v!铚u ~,Egc{/Jj~(M4aw6/X$oYVLB*/oseHx>NX2,SЦn]r~_<ćv!s|6i /F7G=s BʗwyO=7KToimWX~_`MV"sߒ5UA^knSxF+0KbHے^Pf1 XrDzoՉ4fު#f %lSJv'aZ,?e4s"~d9tS[2LZD4}<71*A50Sg̈~7_ww1c- U.Ѿ$.|M(o7 'Kqb%Y[( `9,up y<{yWB@! @! {Gu#B@FG@{HB@! @! b! BmRB@!bX9B@! htD@T?! B@"-֡jB@! F!O! -hu#B@FG@{HB@! @! b! BmRB@!bX9B@! htD@T?! B@"-֡jB@! F!O! -hu#B@FG@{HB@! @! b! BmRB@!bX9B@! htD@T?! B@"-֡jB@! F!O! -hu#B@FG@{HB@! @! b! BmRB@!bX9B@! htD@T?! B@"-֡jB@! ~^Fagy_lL2ICKO>Fez;Ϭ w}g6h S'Usÿ ;Ϭ o+0 UF̷~HթXЊtxi\`5=sƯxwm1ưa*kW_s18>#_⪭duQF~{vimAKcTE TGydz8ы,Bۂ .؋WեGmУ:E^DW^5X^~^.~GlT@B@> x]V! }оjB@! " x]V! }оjB@! " x]V! }оjB@! " x]V! }оjB@! Z~ _]Xn/ ԫ\Ԝ/!5g)>&|l@믿_B|0 tC@! MLM]B@! C@! MhSu*+B@G@P-B@! @S! Tݥ ! Bm>T B@!T6UwB@! h~D@! B@4"M]B@! C@! MhSu,|Ej=kԊ۾0*]VRk@Vy*Zϫ:[v ?瞳uYFydx?6d(b+}7 =@-ꫯK/m#0 306묳wsRI"g_~I'ԶfN =^h .`<;>߿ |1>:6ڮj|I)C!PXI*6۰k.1~J-H%'iN . 91ItP-vmӽޛz43f!Vtg|{3]~ɉgZnR~O?3S)"glj'[wz-e9x4CSO=5=iJC 5TlWoo4s/]qi'O>v{ZJcL2Iꪫwܑfm4~kzTV}okeh~^H:hJ鯽ZoR6@-vF=䓥iKIqMZL$D@_+L5TN6hb6guVrR裏JzkR׌ҴQ?j+~+;R|HKih<dop uzwu-3R/o_=Sk=5RKrJvm4mZmfN;|&_%l_JFγɎ{2 ԯ* $VZi6+BcE]dt.0~14mZʑF\b,Umܗ$hMLHq'TIz-}уy׾b@-r-Gs6کyvb)l 6OTjUxސ<6挌y_}U hN7tv5ز.afO.BuF]wKo7tN6,\sU Я;'EMM)^_K?Ss8Hh no"͸H&~ٹ{K/^zR!@L˜$IyQ8iLoFs?Є~w{믿nhG%Gֱ'}w@-y/6׿ҺBHZ3t` ˜?ڤc(?˓_jb50z;6CkNx &N5S!󆔏{yҸ>12y%#G}.b#2[Rj+Q믿vjo󆔏y_rzc ;\'qLH5O=@-c.V[-$d3vz0gTLk} Xn6M4Qŋ@Swފ(gߨڴ_>XЗي%3LQ}-ćW"2= hgjo|DY[($1nJk+~a#,KG$6 Ҁ6`TB -Z=Ẓp&\@h0M,9IuD~CM g;Jѵ<;y,D&4H@b $EaՏ<'/GPs)w̹#PXIdEH,zgݺB UCH,D.$H=XA1S:xZ>&'53e/ɔ<:P їPFW5do|(aM; {@K?Kѿ#ЕggvK|ʰn O]r)'[nIBofzco}"m1N믿Ji~2Vj]( )9o'J-D߸}Sf[{QrsRl͒Ky!7ߖ\|Hb%ƔktoOk zb9zvi}ۉvR9@_jT20}Yv": h⭫ ! B# > ! B@."&B@>hB@! z[WB@! @G@@! h⭫ ! B# o B@E@wՄB@!B@! @" ڻxjB@! [@! Bw]u5! B@yD@- B@!л.޺B@! <"}B@! ]D@{o]M! }> ! B@."&B@>hB@! z[WB@! @G@@! h⭫ ! B# o B@E@wՄB@!B?nf}B@4"MYK7|6 tPilYg:nO?~n+&FqDowUW[d! @wtB^L5TF)%k7<6lz]Wgql 7>{UWV[me믿~+l֊2>h{{38c{W_?jX' !-A=u,h@!HoS:A)Z'|ͱVٹluֱنvؚۇ~h=\hWs!mFv}B -+UdUu1! 馛~(0c; ڷ~kp 6Sfqg믿X`6h aγgT7\㏇F,&0 3ifSL1EgNǨυ^h., +6˺k^zM0C~O6d^{u_M裏n+ewTu%B@QЀJΛL/TPƱ[n% 6` UZ}r-NkP7xctM$'2Ga~i5HiMc4\{iKswO[otgG$-Ps=޴;ƾk#]wNz7ľK/6x4Fо/2'K p:3G}xq9yON9眓&t$3iiM?~:s,}駑Zt]~{XkZ,@3 ueH_\\ .5 5C=ksB MMn֏}yDj%Fq:AI]._! 2,B1 Pf5׌?jvygꫯ̉[k=?oF2'/A=J+d~DSO69A+a뭷믿.S$|~)OeY$ jWRae ^@pҜuH6ds-z뭶&t? p1e-Qw\lg!XjWG}4'oytqt+Vmo!DZ {B"b؉XY*7g;D# $ܳK1wf7$ ` Df)$@c]~ F{zzJQIwqKKK^F|*!mmmUȺMMMe*b>߯r 뙑P:cGf,(qB~ga⃝Tӣjooz||Ljl2b?ڢ{P|Ŏ[MLz@';j ? We";&i>V^__s_,$0asYu_]]cCbR"vsSC)$J,&F.KQ>;;rYJ2Jx Y<LO!Gli$/e%כVBfy# <;ǛY":777m$  H@P$  H@>$  H@P$  H@>$  H@P$  H@>$  H@P$  H@>$  H@P$  H@>$  H@P$  H@>$  H@P$  H@>$  H@P$  H@>$  H@tIENDB`MatchIt/man/figures/README-unnamed-chunk-5-1.png0000644000176200001440000010726514147102525020534 0ustar liggesusersPNG  IHDRz4iCCPkCGColorSpaceGenericRGB8U]hU>+$΃Ԧ5lRфem,lAݝi&3i)>A['!j-P(G 3k~s ,[%,-:t} }-+*&¿ gPG݅ج8"eŲ]A b ;l õWϙ2_E,(ۈ#Zsێ<5)"E6N#ӽEkۃO0}*rUt.iei #]r >cU{t7+ԙg߃xuWB_-%=^ t0uvW9 %/VBW'_tMۓP\>@y0`D i|[` hh)Tj0B#ЪhU# ~yhu fp#1I/I"0! 'Sdd:J5ǖ"sdy#R7wAgdJ7kʕn^:}nWFVst$gj-tԝr_װ_7Z ~V54V }o[G=Nd>-UlaY5V}xg[?k&>srq߀].r_r_qsGjy4k iQܟBZ-<(d=dKO a/zv7]ǰod}sn?TF'|3Nn#I?"mzv~K=گsl<b|_|4>?pߋQrib 2* (Ѧh{28oIyes8';Z9h6g>xRx'b8ՃWOϫ[xn%|^z}%x c8eXIfMM*i_@IDATxֆ9gHN\D 9JDQ@PW$ɠd$$GWv3;;;;3NwuUu۽3ߜ::0 yx    (@y# _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   HHEw޲fI8Ç޽{,Y2ANB   8~ؿ*UHB3vHHH PUrX)"x1 /8 5!   XHHHHPc     P.t+={V88r   hЬ{;64Q Dh     -%   6 h#d$@$@$@$@Q!@Z,K$@$@$@$m|R^m۶-[9b   !@#C$I^L$@$@$@$ NBuHHHH|&@3:V$   /XHHHHg> ֭݊s.HHH Z(@/4+޽[VZIHHM h O>уԩ.]k/^ӧ{]IHHHb@@ P;ʕ+KFYnwY˄'O޽{PBRlY=z(8f  ԩS^zka :X =*ѣG/^T G6mq?^oٲEf͚%;wXHHH {lٲɲeˤTRK*UBOwҾ}{ɜ9j_bG)/yfVJJ#ڵ+Wz>k׮2eٳguE駟K,Y~o>Çː!CtMɒ%%]tfOm͚5L23<#SL^3ڵKj׮S@۶m_|bsotРA:t_^6mj9v>7O?տz-e h˕+'qǎrS\$L_~]ONc כ7o =ݽ{Ẅ́6!Eux]fVO~W??=ŋwz„0`խ[W~}+#(D姟~^zIz)}h *AFFҖH0aڈPV-ə3dϞ] )Sj9|9;,W^CtABe+%KHƍwՖI&6P>Yd`ڵXcQ|e?ˏ>|XN:EiXtc-XO~cL???1K)ac{ᅦڻm%H3*T/e5`2ر/>iϟ3f~QPSaīzjCY%}c>}Z{W\rm(z' e!5we3f̨ϭ sa,JZY `:tH3x=OKvIwcܸq1&#  &Э[7cذaO!CC<ŋ]P/8p@Sڻ(Q"QbΞemâ|rI$:Xb:* ?c=Mf1{9./#3 .\|Wu}I|IժU= HHH uO P3g %Jh:|3E#()n^/XR>6h \&i8POc hS>)w~zO4Gw׈#}nBBV4i$qJ*?ҖGȘ‚M6 ŲXPQXkBnܸi~ciڴ( C;]vBb۳g=z$۶m7n}zjZe5Ł\!SOYeCi#mڴ.Í/D "I& ドp'ɓ'FJ?f?GT '=%O<ɏOğ?/~~y |/:>)1'E5TԳߍʕ+J(~,p5ϟ;v|e3(5`)S&ٳpW\iT^fC;vUF?ʕڏh?4Pxp-NQX=Q % eAsl#L>3HHH/{ʟ}\ȳf͒.]huʊ+PCY?{ f =e5b(ߣHK".\8/tT5-VvsO}={7>}4jH#_HHH bSOlRfM_Ç,Q,]et}T$ M͛7ees 4lPi_XSݺu 6. /W\@0~ aLW!xrL$@$@$@puߎv>ߗ_~Y>Ho:Z:toV`9',K/Iݵ`O!o)Y(Hgx91URÈ8'}"  'pE 45jh% &M4O9s4kL3F QR͛7O/J4wHHHH 8.]:Ypx|tmZ@6oެ)" (g3 R<&cƌzY&ɒ%^_`7Y&`iׯ_4Z`U   P&h hDZGD;sQ!xSbi6C$@$@$VD$@$@$@$v >0$@$@$@$@(@݉pHHHH V |a `B y ݊gϞ.HHH Z(@/4+/^XƎIHHM4 @TPF˒ D@P Nŋ'.]66@$@$@$@$;}p VGxTtIRJqG 6/,y:H9} ѣo>W֭[:رcR~}q|RJݻ̟?ߥ^쀭9viѢEr.\m۶Yg͚%~avIʕ*6>|_<{c9=??e'?ܿ?S)@!wߟ~O WzգG=?K 2(Q"Yr7o߾qX-[h1-ߺuk9~Y*H_|o,Z4jHFeDcxby'NhRtP~f={Vm_~-[6e;M   "PD 9sݩɑAHv횔/_> ܹs˗Ȅ'$XH,YT V2@>9) @pÄ?~X=e`D5ǣ 153xL_~]Ga{?޽*Sfͪ]/#$@$@$@$> PX3\rETD?<皛!^\s 4l0B,A$@$@$@p|8}e6 @ ! @  ž @ ! @  吾bUD$@$@$@Znܸ8|   _ PJHHHH|"@6V"   XHHHH'ObX%PJ)TP / ྲྀ6"E^L$@$@$@$ NBuHHHH|&@3:V$   KJxĉnGusͭtVXHHHO=4 C:pGRH!2e3ĉ]ưxb;vKwHHH P ]Vʖ-2d +VSNpߎ*U$+VٳKr=y7xZ ,XP^{5}͛һwo]@}:~-֭ɓGh78J $mڴRfMٵkՆ7B?\_իW7eC/_^mfƑ:ujYlf}]rӇ~mlHH N߿_ 4tB=$Gy8S(ԑS6m;Jʕ(J>iFN*Z3ffk#iٲ|w2`4h,X@wﮏcjgyhcʔ)r%B˞NÆ er}-B^6~,ځ}>ׯKFmaBuҵkW)UX5j$H ̴;ȑ#vn6L$@$@qAVZ{uFw5(S,Pqiɒ%1|poO&MQD C<:o…֭[2}_}>̈́v'On 2DgOLϞ="6Tѿh"]FBK X#YdVP1cm;z!zLID믿(ǎW`TGرCb5Qh&L iҤSJMKɒ%eݺu>6ԩcLR+f ;pjnڴYM&M*Ǐ23WZ%o4kL[?~E=),`2ʡ>ؓ O~%5gL{-aBR۱Kqɴ*75-oԮ][[4PCGPºnwciРA+wܺiUn-(Y`9K+ (.\X}Ջj ,2`5,J]/k(a.{rwVa oM 7eYHHA@JCŋwOUU1',o[@NG4TS[3ĜqK3 QtmC@1,kϞ=.KǞ#9sf{_(7 ˡKqF O'b J\Xe>mV8ֻI$XHHH ď_KR~}o()?(=߉VAnL>*D+ Լysɒ%޽{5% [\{<Z|(QE9^7SG!Ǟe&|P,ݛ~*TH.]XYO<}`AyY*cJB}jFB zhw~fͪ5СCEŐX`0K"_tX SN:2\}zy&P_:L2Rn]ܹ@daY wyGM9(,_X}RNZ!vܩro^>Xb O37x'4 \$@$@&-[I"bHb@\rɰaôSJXR,hM"5A,儵EaCĢ*\pDjrErܹ>뎺'o:8СPKT̙3NMc5B)FOuS GLś"lڰ_93.ħن-+ B /d    uε`OHHHH $Pe IHHH9(@s-'4N$@$@$@!;D 6tb'   !@ h\(vHHHh\IHHHh\(vHHHh\IHHHh\('us֬YGk2 /(@}u<7n8HHHWc=    PHHHH|%@+9#   ˗KxرcnG]b.sȑ3ChJ*ҪU1J$@$@$(Nhf˖Mڵk'R򡶳\~]Ν;'ÇQFcE"E^Lصk/r-)WԬYSo? @0෠W'iӦIƌ}*'O֭[wߕ޽{1?~9 L0AU&+4h@d"$@$@$;G еkJJSٳgի˗޽{oH%ER`Ay7oj! ]dri8Pݺu|(<Cʟ?:uJo͛'zĉe:f͚a7Κ$@$@$)@]&6m;Jʕ%uԒ>}ziӦL:U 3fh!7~x%x葴lR;0` 4H,X ݻw<0N2E.]$e˖u( /hkMÇu;޺uܽ{WO֯__'Oַo_Yl۵(oldɒ%=ll(v^}UٶmՓq D5͞=[&MZ„ /x;v,]4KÙ3gd 4 $%0iP~Vߔx'I&Yy(Q,:o…V_PRc߾}W_}}3]%!C,%uwy,b(PIY!?*D,_:O hXb>2۷ooΝ[fl(vJ.N %u;Qn׹sgopjѢ~wviVYPV=+_Yo pSbÆؓ7TKYͮרQ_st/P5 l(wL:.t͚5a" eNMrÆ 3)%H&[l1̗j_3rA)Sγ PD -`մ1faf6!2j(sW)}-kK~xcC!]+NCEG>I$ŧ4ܹc@ٓ]4tV6 5oa^RΛf@_pp?w^=`c=/?@arut< o[NO}Jb ]x`*!ai%L+޷QHd͚Gĉk?I+87nҥ,:uXjO2eһBdtdc3>a] p 4iDF֭[WΞ=kf{ɗ/=~??+NO5%HKծ][~t(YUFY72/{eED ={T{K@UjD;ۣݟ*$ E66sQYӻ?r\HMSO=r;Q'[oIB9shB+=vQ?k @`p#EիWu,KX?!Ar7,2,6JYhQmD^a?ܬ 6HÆ u+ܹsu_ml(ɜ9)q\*~@DcI&Xp}j*}" ~pr  (X :XoԩiD2L.ay&,qu˨LT ti TU^>>rHt+XbŊV׋O?,*IflVC1ь1VC w+X Ky*BA5xHH<+W.6l&a$2b"ىu7eo0 !,15"бUN?'O"H x?SuG;5?a2<ȋ/n m۶z=z[cxFHHH Ctj ݅A=aR7 u<1S ¯a)LϫehtLK{9ofm뉚1+WjSQ*߯_?OD$@$@${2sLSk8S{i$6Yrw/N̷O /y;3HHH@LLHHHH rg|H+hw="   ̱Sd.~FK5a)&   /B^E BjC$@$@$@$3 Pѱ" /(@}:$@$@$@$@>]VӞN&   aZiذaIHHCc]    (22V     c]    (22V     Su;t e˖toٰa]YfI~/ CPBjJ+T:tgS?rƍi = P?\'J !RbEI&̙SƏ0}# ٳG6m*ٲeyJǎ`OpŊ={v^\|Yݻ'o/^\RH! ^{Mn߾m]7oJ޽H"R@}:~-֭ɓG2f(7'NG2eƍK, xbRi9sh+g QFr<32c *wyGڶmk6m*UJRJ%ʕ#.˥N:wׯɓ'5ܹsÇk<7 |-ZTJ. ?۷oɓ˥Kb ‘ڵki&+rʒ:ujI>iFN*҆U>{葴lR;0` 4H,X ݻway|guژ2erNKZ nݪŰ7h0믿dȐ!Ҿ}{ٳ[N4ib7ӹs$qZ@͚5/l7N:u5s=' .a[֭ϟhUb& X0=Z/7o֭[KΝ#?N2%eu1|pori<ƤIS?sN+WժU3ָsW[ R ƪU6EԥyŋƇԷo_C m7woXx<;??"5yY44/;?UK^Twڎ#-J脩w3 K#D9z..c?l3ըQC*TH~wψwX5ڻw,YR[-Qvƴ> "J<orYiРy QTx kߜ55S;,š};ùMYQ>nw@eݻwu[0Due.\Жespp??̜=3?p=1)a6\i1-ǎs鞚7j׮- |@5e5kPS.u;J4z-X9?^*? cF… %8u=_M_jPacVWyt m(SKɔ),J蛻] }m_ $0ԀKyovL 7eYHHʩ2a A{behUĎU>J|jk&@aۿ^ k{%A]4QBP[W~Y_BCB@)a&jY@eΜY5k6lrxGek"m{m57*F2)Om ty$@$@$@^@05fX 3mٲE~GfVP;z N|բ uR7LҊʆH1Lc+ fʪ*+Yd:j?NW^=ߴ(4$EU SqD#VЯZt"-B9bdlHHBo)Æ \rdb`N#@!Ǐ|ᇉq,d.Ä(tPSg2p@Q#i%B!a $X=?PQS|,/hh/kI$Ub(>LHrv9#}!+ָD0Yf:,:~G,Hv?]vy_9gi*1"VޡCF/ڤ#رccdlHHBfLZGRL5Fb(QDv~9m/ oS`ڏc"ؚ #Sryxyo~ 1޽Mxv<&   #3g ޝS{_fxQd ͈Q$@$@$@$@ Pz\    X"pSāFZl,J$@$@$@$jy(/b$@$@$@&)x_vHHHh0^UHHHL]#   `$@W_c@IDAT5Ǵn:;wn,͓ + `8ݻwg)4 @ ˡ  P:O$@$@$@$(@rh$@$@$@$DN*S֬Yp%G$@$@$T|SհaC]#  p:Z@ùB:uҥKs-ZH͚5$@$@$@$h W\YeQf+Wdr})_dΜ9H'>K:UM,Om ; pp۶m9lOO>4iR}W믿.|?Ns@ܹsGի'ǎe޽{e׮]r9ɔ)Sba ߰aTPA#jՒKڵѣm6^0SN>m]{o!ŋ)RH^۷o2k׮J*Ɋ+${캝˗/O?-?TXQ/K.#G/liӦIR$UTR\9YxGɻ+Ŋ9so-cڵK/1cȰa6S Kŋg_-C џSNrJW p G k׮͛N:Z 4Ho./RdI]&MdlӦL:UZj%3f̐*U-ݴitQ0Ş:uj-nO֭$H@ *$YdǏkl6>n8_(D-چX}d…f5j=Zڵk'(O?oYfI~be8%''X|dȐANG!ݻw)SسM$@$8R@@9`- B;w,~K|wdҤI/D2kE䫯VO@ƌSOuҺuk?8΅,}M?, &0t'A_ |wӞ,=7|hF>~ؓ>ݪm/mHXdK@x/|?giy|n Syy#ZjU ׯo!Hz?y" XuUwOƏY3vmݺժOȂ D?oO."I /^<)P@ ೧4iKlJyZuakyfQHp ? ̘xJ?q3a۽7O~1_wqhLAʝ;~3oq.e4eʔf#G}x3 A"ClHmIbӅܞӦM&n~Gt~y vqfˊP׽>ߘ09w/p]CL_{0@%L ̙3~/ކpK&Қ5k5B`͛7u>xGbx޼yZ|گy}č=߻VYb~~g/_>~M[oiם;;[& $hW͛7ׁD~,I 7_vҥ^H5y^@~b+k]Aa͚5%gβed_O: @XޛuTmQD:T9ct: FaO7ڲe´|$I0i8%.A{N.)c/ZL$(0 @+W.F$,pP"X S㑣q-) H޽M"LeʔK]V\n̝;7tp$@$@$@"@-|YyݲjժJׯ?So.fxeĉҽ{wȚ5.\@Ȭ]9rDƌ#iӦiӦI,YÒ7o@H$@$@M 6HСCRN-J(! .tvZVIF ( {nݺ!Cm:qڵK JҧO|si֬U駟~A*V(痥KcBޔN{ Ӟ05kس J߾}@x_~b^5jٳeرRV-sN }%  $H)͛7k)S&-&M*/=zTծz:uj>}}'Qa;vH&Mmʕ9sfӧyX۷Oʕ+}B!B >SyWr#GQFI֭e„ uVΞ={2On$ARP!-Tiۛ2Ib`B֭[%KHDmuΜ9a͛7]7nȃ\oV_ixLNg׏/?Cu7*kNzw ԩ̜9S:t [b! (?k,,.ӿ)S̘1C[?!,a CJdk[o^v(ֹo;# A>H"0cƌ kidmޔ7&wܩ)~9v[mi&~K>,N x]id˖M.^h P B6s\Zl惍7MYA e482'On78vExwuc=̼ytJh2J4*زeZz?~|cҤIJ@?QhQsWgϞ>6"k2.FsGYgV>3Co=>'Nl[<ۿՕe9̵Ԕ10'=+3HHH /^PF4GwAH(ړEC{BީSښiw{A.\E闽?}JZqO9r䰲iۛ2VK/믿.jvOܹ{v>/X j]3/ 8ZbNO)]tz-+W9 HfD7cܽzo)G-G~Wh8fO՚y1fmf{G8`9"eQL$@pҬY{@{: ȷ~t[j%J.^IYwvHHc}@# /_0DPE|D#ao&\,' (odDPB98E5rmoʸB?"h~>%XL!5!P lϓ'^V@M$@$@D_] Z!8 0mVOkN+&+W֣Ad3JB<`裏\Fv05/1=XDᬌiZXA ҥK|#|@]r߉m{1K,B)Ν[Ҙ9V  p IBX3eʔ:"LªKi~[OSqTp,UԴDyȅK.:½Ez ޖ7m{Sƽ] @ @3,EL{J'ABoPɓ'o SX*5jRd))c/6j*ʕ+nREϋZ5@m5`23  w&j̩/J'>Qr W|er"> *Ȱa6tOxp~OmzS^>.T"a"   _eQi>< U'b*R űs$@$@$@'AH-[" @X )e"   : P/ad"   NGk Dhz8p(HHHH'>a J/֋6HHHWc=    PHHHH|%@+9#   OXHHHHW\Wr!\m۶Ҳe& Dhth$I^L$@$@M?ٳgK BG]yct1ǒ- @HСnZRJR>}Z~m4iR\w>|̚5#O?իgUVIժUg gI&uJR<`n0aB?&(C}U?~TR%;nݒۋaar?~\ҦM+2eJ( A0k׮ɉ'$k֬.]:ApY9 'T^z=azhѢ_ i,Y2%J$1I<9rDҤIcqYn DpÁt|yU|aLqG /;>_/KK,AXq1oӧOyRVY/j(dic+ ȑ#ߍM2믿\㎊j7Ξ=k MJRyb. e5([CCXl7uieH8~Jl9{'L$@'PdIC[?ѣGu/n(]ǝpP| Қ5k<7wvڙY _>`]vXW{ F%]0aBȑV~Æ mn ?AZw^)\vۍ`6m8 _"E,b |}ҥKm&ҩ#<#E&rӧ|3gNc|_~e2qC`ĉ2zhҥK >GS=% (p> HӦMRMI-d˖-ҫW/QfhDDbOCړE6c?zh1$: @_A=0vH@X@>޽{eڶm+h:teu.!N|Y1 3.H$R)@1dLѐ*GtDӌ &aJsཡ2$@$@$ *JI0 PΝ; q)@#>Pt9sf͒~NlHH"!k.%JԩS#)N$ԉ CO~a(c% 2SV-t\u?3`RJIΝcx]o9s{B$@$@AIR|yyԩSOK*UEO3gԫWO?"+0 ^- %]P!/SP#w #5kyg8 0uaHHH  hq)aX({20OʵlR֭['X)pPεrLOT"ZrL  s~HBWmt… 4`ZQ F2;h`\'GKWUVQ}bgHHIrHAL)e&<G#F,͈W\a?6Y.n̓J$@$@$Xɟ?:tH˧/'|@wu0ߥnj#XI&.ǸLμ.  <X: B޽{rEYb+V̫qDN8~$@P^wٳ5ݘHHH *RL)ʕ<(*S~iɑ#SiFFi<gYxٻ}Y@$ |FнaVݻw|n8-θ  А( 83{A$@$@$@!C |@=]=,O?CXvȐ!a9RD&    #TL2CZW^ i?C?Iy5;Q|# 65kF:mۺ<- ^7ol޼Y?NN:RVO$@$@qG hz/{С-ZT&L`e ZÆ Bm VatHv*'O+VxqͪA׮][ݔ)SBH$@$@1N>k׮J*'$dϞ]W./_փ^xBTZbX('&<Uɭb;v쐒%K}2 V ߀8}vDĉvK17p@xb~a#IHH'׮]M6Iǎrʒ:uj-ƍ':u ʌ3bŊs… lƍjժsɅ B>Ǵ|#G'){'?~\nܸ!v5`G1 GœW^yI A]6ᱠӧO,_\رc^w}۾t钷U#(HJ~(E5|yo' {Zt:tHurHV^=}޽ۥl0d(Kϟׇ֭['ѣG`.3ﵰ7t!Yۅ SN`_??ys3dX |)H*TгmqnB`b9~,Ν;_QG cOq\R_xfoJ0<5=lX~[+UJs6ԍ蒯6/ +̙3FMZy)S&U$hnRX_Hs̉}ʂ}e4SRIzC# !,֭[#2"5&MC5EZ,SOժU3_lFPτ#PF#+f_l>4EL'Rn[YemZm=z]0oq'pW̭)sL;DZ}Q0ۓڗ>fʒ%W K1,p<䓢>\_e˖.".C$@$,rļu.*)[l * u`f=-ZH?GWbSrzi?@|N{/ ( ̘Ӂ(iӦ}rK0݌3 07!?ԭ[W2dȠ YHBAY8d0)]tzSYE$3Mޮ];3zDž5L?SǂmD)&BI&~ D>h-]&M_˧"dH$@$ Za?eu QMX6,=aom۶Y~?џ?9e˖Z0@ $ ,,{pJ)G'L7!Zj|  eHps`Vm:h:szYD9u;`a O?u=4o\7`vO~dzi~(S+u/Hǂgϖ͛ ? ӿ+02Ee" |n4k ׿ٸq<7|{[bF^ ?,EuZ>__p,ʽ@zb6jH#A[z|,PawW$X^a wĈz " 0!"ؓ-v0㗅~=XB!4M:XXkzJE8qbQ>&¯p_8A;ŧ竟(Q"mMرc)>=cb. x$",ᥬYw(|AX;UpT#f<=1 $uR'6̗}vXXPBVq[-L@Gj Q޽{2CP(ޙ[Uo馈a!"\DEKqB2̮>dHbD8"B"Ȥ V2[}r 9pxϷM0aMqD TYYYXAk.!_It/|`p=g3t|/,HH*"%t.oM/@a1;,VN]52| V49$+ gd   t `|*ϟ?_JJJLb13%`(l:TP^jOHii{챶.+ZX.SfF'XRi :eڵi-X>_% >o!ԃ/ 4>myȋub< dϝSG㮻e_ׯO;1Ӊɓ',f* .l7;fOam񛑽qXfK< ,',ixa-,C,:tl-P}$  F'a:|Ʋ4<0`NIL0Xa{قSSXA6Hצb&!7N^tEtRI8N1 /qxq o+sO:| ЂBvHH!SY?=#G NJq&f$.zLc*GweLDEVŖ-[T:b{UY~zlhE@#K P' :1: )],׀@HHH 7VZPKN3%^>`:D%$O^`Y "TXuzZvT=erOCQ.PZG]w9@L|^htnĉ9Wd5,HH*&JsA\z岪S^9ES|1vBc    ((@Pc    (@#cF    ((@Pc     )2صkׄp{N$@$@$hjEm۶ D!)(ԘHHHH 2 蘑HHHH  (ԘHHHH 2 7{'7n,^9 @pRgiӦ/,Ǐ/N5 hܸw^vm!ݻw˄ rXc櫢)γF`tǎ$RM :TڷopbB6.͛'Çsf_rmIH,k,X eee2MGw ׀4!   0 hNq2     P~HHHHrJ4Y (?$@$@$@$@9%@SܬHHHH    #_e-ZįqlQN$`m%ajV"@K    |      ^ dh      ^ dh      ^ dh      ^ dh      ^ dhǢ\%O+aXs+ٹsgMp>ݻe߾}o'X(@8O0Aw.חΝ;ܹs+U_~ҰaCiѢ14j(DܤU0͛#{6;BDL2P) 5\#e̘12~x9묳dٲeҡCޱcrI'ɼyd͚52x`K[VV2#I X.YD Ï$Oڿ|+v ‚ o6m$sl߾=6; -LLTU:PM m֩%3w۷w*,7z;Nѷ~Sˉ/8^ğ@Աٳ+AB۷/߸֭[zU'‚ /ƎJJJl9>}zw@ 8_UlذA=܄[f̘={K֭D| ˗/xQV;Z;&e %rJYh6n(j ' c(<e>}n|Mڅ3a XBF%kNn~2@4X'|b׿/ƍW.&nB8gUf8RKިi;vڕ* bJ XjJ?%\4hYŇ.=XL{fe@Mf9'An=裏z:蠄x|>Ӎ 9GD͛˵^л&MH.]d 񼩞|n'sDX>Z:B Э[&{L'&!6RA!ַM DK dx9({ hH'g3gL;u)--Yf%lRBC:uN2~x ]Ě>nyU$sS*=FMɀ+'SEbMK;uHԽAuM0`@S79] >StRѣ<( 錥:_puj9ktf$կ~թk8[wD;`?$2_Mj9)cjR798z ?ZS:=G띞j妛nJX: ~Y&#ԩ#{oE~2n8[zKvj'ܯ}krqɲeD}&eK.SO=5+?6۷o6l(oRWP??)k HC$_X &,!Y!}\z饢GwAvͦ1}WG}+=#ڵ4;wz*x|QVV&W_}uB /`J3ȯ~+{'iz!9z 6R'$;ìL#\2o+zݻlݺ5ȃRe"G [c N?NԩN$ E@$@'5jz=3AC:=BԩeM:?M8^ũ=#FNuxG*. ]O]EN ;S3\L21nƍN^W_}Ҏ=pEYzN-N<&yw kBXv*JIzNS򗿸O?=`ٸqcB;?`wG8=מzZNA;իWN;Nĺ=zi'רQ?TY*V4wM6uju*ݪUի]ݺue]T,kir6G]tYC*"ݞ={ewI'Y[TX[[0V7܇~hڵо}{裏Zyh;uH2KHH  !6l`mյ&4vP(7on/@B9?w !P&Mz!>cv_~?\p;mu9% b ᠖>v>} mģ(C- ef۶m|̘1 9r:LZZ}65H:N;-PS gϞyZhP/.bMDۧO$EP7)~ZE4QHxA%)xfb ?Lc_)38C0ߊ P7wEƴQ'*D *ˣ8Q˙M U'`z&ZN\bEBx ҨUV\)jmOs=W^6#֐"`3ӂ .܇Ə;v4!5ׯ;OPޛo$U~_ ցBC{';gj7Gb-ֽBAtBBeT8ENNUv8OkKt)@!O< _U.']]&Z"yC$1ٙ"Y d,ZX}3A)=` 6]V` ` lѢE8:4 `5DXp 4 RLtg?3 -,6fWf{7p~Is qg/P駟.6UyM,4]6,%l5 2tj 2!4Ѿ0_\C#OE h(@qc. ;vcg7v*_>E/|)X('hx>wW?I"_ $@$[pbƩHF9uRtzWh;i.y|\`~u:|N}P;ղj QL$95Pe  jGűbzV[=\Y=Ic9Fvj8;AL'?$@$PE"@f'(|wL:U\<g'w3:tmV>h3R_FEN$oH `9tҥK0=({iqVI5!8X?ZvmAuY͇~X~_9#eeeV\`ϣѣݻW6mrkN7n,C 1z`)No8^Z0ŏ58g}mժL0AN>dݻ^D^Z  +W4a |cƌ$зo_Fx PN}. Ĝf  $BWV- Fw 7x}9wGg>O>=HV9W~}M1cS˧ӝA]iq*ĜN;:P Y7رc͒%KZs-JES쮺*~֬Y޷3hZprtkРS!BŴS+fb՘ NgWRR^{5N 56H 70],rqʔ)Y? <*S*e/kX0_믿n;k֬);vxL3!,ؙ_ـkk/S> (a&‚ krF,yiiy, @Xfm4b6{N$oH G !:(Q˛ /bK-wɏlF &ܙ 8Ss|/}:uR>R^QB}CX!`j?UsWp[nvÆ ҤIcDL, Ėhl #%֭[ ,vب-޲e@2\7]6^(M8Q d{!LOˮ;֬%7_nʊ T .MH>@}eٯm6i_8?IG7&l @`;~h),qܣ>U^=۹)|GON=TKFnZWQ ěhLJ#!LïZJfΜY8pL X>|?{"ǺF,ݘևMPg n0͏] '\;E -UXljp opt:W UpHJә୷޲6W^}:xM$8$@1 pq&X0%*ĕ(7R$/%+ҦSԣF2FU p߄ܱ 5ntV^8e /bͽTyy1~^CN4ɒV}uH j`~~IH0 ҷk.9SvN8i/#.+[kݻq<hgy}<}ƒإ*'j||'/ g$@$@$@$Pt膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EG膜&   /N$@$@$@EGD8'IENDB`MatchIt/man/summary.matchit.Rd0000644000176200001440000002635714151175605016014 0ustar liggesusers\name{summary.matchit} \alias{summary.matchit} \alias{summary.matchit.subclass} \alias{print.summary.matchit} \alias{print.summary.matchit.subclass} \title{ View a balance summary of a \code{matchit} object } \description{ Computes and prints balance statistics for \code{matchit} and \code{matchit.subclass} objects. Balance should be assessed to ensure the matching or subclassification was effective at eliminating treatment group imbalance and should be reported in the write-up of the results of the analysis. } \usage{ \method{summary}{matchit}(object, interactions = FALSE, addlvariables = NULL, standardize = TRUE, data = NULL, pair.dist = TRUE, un = TRUE, improvement = TRUE, ...) \method{summary}{matchit.subclass}(object, interactions = FALSE, addlvariables = NULL, standardize = TRUE, data = NULL, pair.dist = FALSE, subclass = FALSE, un = TRUE, improvement = TRUE, ...) \method{print}{summary.matchit}(x, digits = max(3, getOption("digits") - 3), ...) \method{print}{summary.matchit.subclass}(x, digits = max(3, getOption("digits") - 3), ...) } \arguments{ \item{object}{ a \code{matchit} object; the output of a call to \fun{matchit}. } \item{interactions}{ \code{logical}; whether to compute balance statistics for two-way interactions and squares of covariates. Default is \code{FALSE}. } \item{addlvariables}{ additional variable for which balance statistics are to be computed along with the covariates in the \code{matchit} object. Can be entered in one of three ways: as a data frame of covariates with as many rows as there were units in the original \code{matchit()} call, as a string containing the names of variables in \code{data}, or as a right-sided \code{formula} with the additional variables (and possibly their transformations) found in \code{data}, the environment, or the \code{matchit} object. Balance on squares and interactions of the additional variables will be included if \code{interactions = TRUE}. } \item{standardize}{ \code{logical}; whether to compute standardized (\code{TRUE}) or unstandardized (\code{FALSE}) statistics. The standardized statistics are the standardized mean difference and the mean and maximum of the difference in the (weighted) empirical cumulative distribution functions (ECDFs). The unstandardized statistics are the raw mean difference and the mean and maximum of the quantile-quantile (QQ) difference. Variance ratios are produced either way. See Details below. Default is \code{TRUE}. } \item{data}{ a optional data frame containing variables named in \code{addlvariables} if specified as a string or formula. } \item{pair.dist}{ \code{logical}; whether to compute average absolute pair distances. For matching methods that don't include a \code{match.matrix} component in the output (i.e., exact matching, coarsened exact matching, full matching, and subclassification), computing pair differences can take a long time, especially for large datasets and with many covariates. For other methods (i.e., nearest neighbor, optimal, and genetic matching), computation is fairly quick. Default is \code{FALSE} for subclassification and \code{TRUE} otherwise. } \item{un}{ \code{logical}; whether to compute balance statistics for the unmatched sample. Default \code{TRUE}; set to \code{FALSE} for more concise output. } \item{improvement}{ \code{logical}; whether to compute the percent reduction in imbalance. Default \code{TRUE}; set to \code{FALSE} for more concise output. } \item{subclass}{ after subclassification, whether to display balance for individual subclasses, and, if so, for which ones. Can be \code{TRUE} (display balance for all subclasses), \code{FALSE} (display balance only in aggregate), or the indices (e.g., \code{1:6}) of the specific subclasses for which to display balance. When anything other than \code{FALSE}, aggregate balance statistics will not be displayed. Default is \code{FALSE}. } \item{digits}{ the number of digits to round balance statistics to. } \item{x}{ a \code{summay.matchit} or \code{summary.matchit.subclass} object; the output of a call to \code{summary()}. } \item{\dots}{ ignored. } } \details{ \code{summary()} computes a balance summary of a \code{matchit} object. This include balance before and after matching or subclassification, as well as the percent improvement in balance. The variables for which balance statistics are computed are those included in the \code{formula}, \code{exact}, and \code{mahvars} arguments to \fun{matchit}, as well as the distance measure if \code{distance} is not \code{"mahalanobis"}. The \code{X} component of the \code{matchit} object is used to supply the covariates. The standardized mean differences are computed both before and after matching or subclassification as the difference in treatment group means divided by a standardization factor computed in the unmatched (original) sample. The standardization factor depends on the argument supplied to \code{estimand} in \code{matchit()}: for \code{"ATT"}, it is the standard deviation in the treated group; for \code{"ATC"}, it is the standard deviation in the control group; for \code{"ATE"}, it is the square root of the average of the variances within each treatment group. The post-matching mean difference is computed with weighted means in the treatment groups using the matching or subclassification weights. The variance ratio is computed as the ratio of the treatment group variances. Variance ratios are not computed for binary variables because their variance is a function solely of their mean. After matching, weighted variances are computed using the formula used in \fun{cov.wt}. The percent reduction in bias is computed using the log of the variance ratios. The eCDF difference statistics are computed by creating a (weighted) eCDF for each group and taking the difference between them for each covariate value. The eCDF is a function that outputs the (weighted) proportion of units with covariate values at or lower than the input value. The maximum eCDF difference is the same thing as the Kolmogorov-Smirnov statistic. The values are bounded at zero and one, with values closer to zero indicating good overlap between the covariate distributions in the treated and control groups. For binary variables, all eCDF differences are equal to the (weighted) difference in proportion and are computed that way. The QQ difference statistics are computed by creating two samples of the same size by interpolating the values of the larger one. The values are arranged in order for each sample. The QQ difference for each quantile is the difference between the observed covariate values at that quantile between the two groups. The difference is on the scale of the original covariate. Values close to zero indicate good overlap between the covariate distributions in the treated and control groups. A weighted interpolation is used for post-matching QQ differences. For binary variables, all QQ differences are equal to the (weighted) difference in proportion and are computed that way. The pair distance is the average of the absolute differences of a variable between pairs. For example, if a treated unit was paired with four control units, that set of units would contribute four absolute differences to the average. Within a subclass, each combination of treated and control unit forms a pair that contributes once to the average. The pair distance is described in Stuart and Green (2008) and is the value that is minimized when using optimal (full) matching. When \code{standardize = TRUE}, the standardized versions of the variables are used, where the standardization factor is as described above for the standardized mean differences. Pair distances are not computed in the unmatched sample (because there are no pairs). Because pair distance can take a while to compute, especially with large datasets or for many covariates, setting \code{pair.dist = FALSE} is one way to speed up \code{summary()}. The effective sample size (ESS) is a measure of the size of a hypothetical unweighted sample with roughly the same precision as a weighted sample. When non-uniform matching weights are computed (e.g., as a result of full matching, matching with replacement, or subclassification), the ESS can be used to quantify the potential precision remaining in the matched sample. The ESS will always be less than or equal to the matched sample size, reflecting the loss in precision due to using the weights. With non-uniform weights, it is printed in the sample size table; otherwise, it is removed because it does not contain additional information above the matched sample size. After subclassification, the aggregate balance statistics are computed using the subclassification weights rather than averaging across subclasses. All balance statistics (except pair differences) are computed incorporating the sampling weights supplied to \code{matchit()}, if any. The unadjusted balance statistics include the sampling weights and the adjusted balance statistics use the matching weights multiplied by the sampling weights. When printing, \code{NA} values are replaced with periods (\code{.}), and the pair distance column in the unmatched and percent balance improvement components of the output are omitted. } \value{ For \code{matchit} objects, a \code{summary.matchit} object, which is a list with the following components: \item{call}{the original call to \fun{matchit}} \item{nn}{a matrix of the sample sizes in the original (unmatched) and matched samples} \item{sum.all}{if \code{un = TRUE}, a matrix of balance statistics for each covariate in the original (unmatched) sample} \item{sum.matched}{a matrix of balance statistics for each covariate in the matched sample} \item{reduction}{if \code{improvement = TRUE}, a matrix of the percent reduction in imbalance for each covariate in the matched sample} For \code{match.subclass} objects, a \code{summary.matchit.subclass} object, which is a list as above containing the following components: \item{call}{the original call to \fun{matchit}} \item{sum.all}{if \code{un = TRUE}, a matrix of balance statistics for each covariate in the original sample} \item{sum.subclass}{if \code{subclass} is not \code{FALSE}, a list of matrices of balance statistics for each subclass} \item{sum.across}{a matrix of balance statistics for each covariate computed using the subclassification weights} \item{reduction}{if \code{improvement = TRUE}, a matrix of the percent reduction in imbalance for each covariate in the matched sample} \item{qn}{a matrix of sample sizes within each subclass} \item{nn}{a matrix of the sample sizes in the original (unmatched) and matched samples} } \seealso{ \fun{summary} for the generic method; \fun{plot.summary.matchit} for making a Love plot from \code{summary()} output. \pkgfun2{cobalt}{bal.tab.matchit}{cobalt::bal.tab}, which also displays balance for \code{matchit} objects. } \examples{ data("lalonde") m.out <- matchit(treat ~ age + educ + married + race + re74, data = lalonde, method = "nearest", exact = ~ married, replace = TRUE) summary(m.out, interactions = TRUE) s.out <- matchit(treat ~ age + educ + married + race + nodegree + re74 + re75, data = lalonde, method = "subclass") summary(s.out, addlvariables = ~log(age) + I(re74==0)) summary(s.out, subclass = TRUE) } MatchIt/man/method_genetic.Rd0000644000176200001440000002764414110073174015636 0ustar liggesusers\name{method_genetic} \alias{method_genetic} \title{ Genetic Matching } \description{ In \fun{matchit}, setting \code{method = "genetic"} performs genetic matching. Genetic matching is a form of nearest neighbor matching where distances are computed as the generalized Mahalanobis distance, which is a generalization of the Mahalanobis distance with a scaling factor for each covariate that represents the importance of that covariate to the distance. A genetic algorithm is used to select the scaling factors. The scaling factors are chosen as those which maximize a criterion related to covariate balance, which can be chosen, but which by default is the smallest p-value in covariate balance tests among the covariates. This method relies on and is a wrapper for \pkgfun2{Matching}{GenMatch}{Matching::GenMatch} and \pkgfun2{Matching}{Match}{Matching::Match}, which use \pkgfun2{rgenoud}{genoud}{rgenoud::genoud} to perform the optimization using the genetic algorithm. This page details the allowable arguments with \code{method = "genetic"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for genetic matching: \preformatted{ matchit(formula, data = NULL, method = "genetic", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", exact = NULL, mahvars = NULL, antiexact = NULL, discard = "none", reestimate = FALSE, s.weights = NULL, replace = FALSE, m.order = NULL, caliper = NULL, ratio = 1, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the distance measure used in the matching. This formula will be supplied to the functions that estimate the distance measure and is used to determine the covariates whose balance is to be optimized. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"genetic"}. } \item{distance}{ the distance measure to be used. See \code{\link{distance}} for allowable options. When set to \code{"mahalanobis"}, only the covariates in \code{formula} are supplied to the generalized Mahalanobis distance matrix to have their scaling factors chosen. Otherwise, the distance measure is included with the covariates in \code{formula} to be supplied to the generalized Mahalanobis distance matrix unless \code{mahvars} is specified. \code{distance} \emph{cannot} be supplied as a distance matrix. } \item{link}{ when \code{distance} is specified as a string and not \code{"mahalanobis"}, an additional argument controlling the link function used in estimating the distance measure. See \code{\link{distance}} for allowable options with each option. } \item{distance.options}{ a named list containing additional arguments supplied to the function that estimates the distance measure as determined by the argument to \code{distance}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"} and \code{"ATC"}. See Details. } \item{exact}{ for which variables exact matching should take place. } \item{mahvars}{ when a distance measure other than \code{"mahalanobis"} is used (e.g., for caliper matching or to discard units for common support), which covariates should be supplied to the generalized Mahalanobis distance matrix. If unspecified, all variables in \code{formula} will be supplied to the distance matrix. Use \code{mahvars} to only supply a subset. Even if \code{mahvars} is specified, balance will be optimized on all covariates in \code{formula}. } \item{antiexact}{ for which variables ant-exact matching should take place. Anti-exact matching is processed using the \code{restrict} argument to \code{Matching::GenMatch()} and \code{Matching::Match()}. } \item{discard}{ a string containing a method for discarding units outside a region of common support. Only allowed when \code{distance} is not \code{"mahalanobis"}. } \item{reestimate}{ if \code{discard} is not \code{"none"}, whether to re-estimate the propensity score in the remaining sample prior to matching. } \item{s.weights}{ the variable containing sampling weights to be incorporated into propensity score models and balance statistics. These are also supplied to \code{GenMatch()} for use in computing the balance t-test p-values in the process of matching. } \item{replace}{ whether matching should be done with replacement. } \item{m.order}{ the order that the matching takes place. The default for \code{distance = "mahalanobis"} is \code{"data"}. Otherwise, the default is \code{"largest"}. See \fun{matchit} for allowable options. } \item{caliper}{ the width(s) of the caliper(s) used for caliper matching. See Details and Examples. } \item{std.caliper}{ \code{logical}; when calipers are specified, whether they are in standard deviation units (\code{TRUE}) or raw units (\code{FALSE}). } \item{ratio}{ how many control units should be matched to each treated unit for k:1 matching. Should be a single integer value. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. When \code{TRUE}, output from \code{GenMatch()} with \code{print.level = 2} will be displayed. Default is \code{FALSE} for no printing other than warnings. } \item{\dots}{ additional arguments passed to \pkgfun2{Matching}{GenMatch}{Matching::GenMatch}. Potentially useful options include \code{pop.size}, \code{max.generations}, and \code{fit.func}. If \code{pop.size} is not specified, a warning from \emph{Matching} will be thrown reminding you to change it. Note that the \code{ties} and \code{CommonSupport} arguments are set to \code{FALSE} and cannot be changed. } } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "genetic"}. When \code{replace = TRUE}, the \code{subclass} component is omitted. When \code{include.obj = TRUE} in the call to \code{matchit()}, the output of the call to \pkgfun2{Matching}{GenMatch}{Matching::GenMatch} will be included in the output. } \details{ In genetic matching, covariates play three roles: 1) as the variables on which balance is optimized, 2) as the variables in the generalized Mahalanobis distance between units, and 3) in estimating the propensity score. Variables supplied to \code{formula} are always used for role (1), as the variables on which balance is optimized. When \code{distance} is not \code{"mahalanobis"}, the covariates are also used to estimate the propensity score (unless it is supplied). When \code{mahvars} is specified, the named variables will form the covariates that go into the distance matrix. Otherwise, the variables in \code{formula} along with the propensity score will go into the distance matrix. This leads to three ways to use \code{distance} and \code{mahvars} to perform the matching: 1) When \code{distance = "mahalanobis"}, no propensity score is estimated, and the covariates in \code{formula} are used to form the generalized Mahalanobis distance matrix. In this sense, \code{"mahalanobis"} signals that no propensity score is to be estimated and that the matching variables are those in \code{formula}, consistent with setting \code{distance = "mahalanobis"} with other methods. 2) When \code{distance} is not \code{"mahalanobis"} and \code{mahvars} \emph{is not} specified, the covariates in \code{formula} along with the propensity score are used to form the generalized Mahalanobis distance matrix. This is the default and most typical use of \code{method = "genetic"} in \code{matchit()}. 3) When \code{distance} is not \code{"mahalanobis"} and \code{mahvars} \emph{is} specified, the covariates in \code{mahvars} are used to form the generalized Mahalanobis distance matrix. The covariates in \code{formula} are used to estimate the propensity score and have their balance optimized by the genetic algorithm. The propensity score is not included in the generalized Mahalanobis distance matrix. When a caliper is specified, any variables mentioned in \code{caliper}, possibly including the propensity score, will be added to the matching variables used to form the generalized Mahalanobis distance matrix. This is because \emph{Matching} doesn't allow for the separation of caliper variables and matching variables in genetic matching. \subsection{Estimand}{ The \code{estimand} argument controls whether control units are selected to be matched with treated units (\code{estimand = "ATT"}) or treated units are selected to be matched with control units (\code{estimand = "ATC"}). The "focal" group (e.g., the treated units for the ATT) is typically made to be the smaller treatment group, and a warning will be thrown if it is not set that way unless \code{replace = TRUE}. Setting \code{estimand = "ATC"} is equivalent to swapping all treated and control labels for the treatment variable. When \code{estimand = "ATC"}, the default \code{m.order} is \code{"smallest"}, and the \code{match.matrix} component of the output will have the names of the control units as the rownames and be filled with the names of the matched treated units (opposite to when \code{estimand = "ATT"}). Note that the argument supplied to \code{estimand} doesn't necessarily correspond to the estimand actually targeted; it is merely a switch to trigger which treatment group is considered "focal". Note that while \code{GenMatch()} and \code{Match()} support the ATE as an estimand, \code{matchit()} only supports the ATT and ATC for genetic matching. } } \references{ In a manuscript, be sure to cite the following papers if using \code{matchit()} with \code{method = "genetic"}: Diamond, A., & Sekhon, J. S. (2013). Genetic matching for estimating causal effects: A general multivariate matching method for achieving balance in observational studies. Review of Economics and Statistics, 95(3), 932–945. \doi{10.1162/REST_a_00318} Sekhon, J. S. (2011). Multivariate and Propensity Score Matching Software with Automated Balance Optimization: The Matching package for R. Journal of Statistical Software, 42(1), 1–52. \doi{10.18637/jss.v042.i07} For example, a sentence might read: \emph{Genetic matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R, which calls functions from the Matching package (Diamond & Sekhon, 2013; Sekhon, 2011).} } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. \pkgfun2{Matching}{GenMatch}{Matching::GenMatch} and \pkgfun2{Matching}{Match}{Matching::Match}, which do the work. } \examples{\dontshow{if (all(sapply(c("Matching", "rgenoud"), requireNamespace, quietly = TRUE))) \{} data("lalonde") # 1:1 genetic matching with PS as a covariate m.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "genetic", pop.size = 10) #use much larger pop.size m.out1 summary(m.out1) # 2:1 genetic matching with replacement without PS m.out2 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "genetic", replace = TRUE, ratio = 2, distance = "mahalanobis", pop.size = 10) #use much larger pop.size m.out2 summary(m.out2, un = FALSE) # 1:1 genetic matching on just age, educ, re74, and re75 # within calipers on PS and educ; other variables are # used to estimate PS m.out3 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "genetic", mahvars = ~ age + educ + re74 + re75, caliper = c(.05, educ = 2), std.caliper = c(TRUE, FALSE), pop.size = 10) #use much larger pop.size m.out3 summary(m.out3, un = FALSE) \dontshow{\}}} MatchIt/man/plot.summary.matchit.Rd0000644000176200001440000001005214030452667016754 0ustar liggesusers\name{plot.summary.matchit} \alias{plot.summary.matchit} \title{ Generate a Love Plot of Standardized Mean Differences } \description{ Generates a Love plot, which is a dot plot with variable names on the y-axis and standardized mean differences on the x-axis. Each point represents the standardized mean difference of the corresponding covariate in the matched or unmatched sample. Love plots are a simple way to display covariate balance before and after matching. The plots are generated using \fun{dotchart} and \fun{points}. } \usage{ \method{plot}{summary.matchit}(x, abs = TRUE, var.order = "data", threshold = c(.1, .05), position = "bottomright", ...) } \arguments{ \item{x}{ a \code{summary.matchit} object; the output of a call to \fun{summary.matchit}. The \code{standardize} argument must be set to \code{TRUE} (which is the default) in the call to \code{summary}. } \item{abs}{ \code{logical}; whether the standardized mean differences should be displayed in absolute value (\code{TRUE}, default) or not \code{FALSE}. } \item{var.order}{ how the variables should be ordered. Allowable options include \code{"data"}, ordering the variables as they appear in the \code{summary} output; \code{"unmatched"}, ordered the variables based on their standardized mean differences before matching; \code{"matched"}, ordered the variables based on their standardized mean differences after matching; and \code{"alphabetical"}, ordering the variables alphabetically. Default is \code{"data"}. Abbreviations allowed. } \item{threshold}{ numeric values at which to place vertical lines indicating a balance threshold. These can make it easier to see for which variables balance has been achieved given a threshold. Multiple values can be supplied to add multiple lines. When \code{abs = FALSE}, the lines will be displayed on both sides of zero. The lines are drawn with \code{abline} with the linetype (\code{lty}) argument corresponding to the order of the entered variables (see options at \fun{par}). The default is \code{c(.1, .05)} for a solid line (\code{lty = 1}) at .1 and a dashed line (\code{lty = 2}) at .05, indicating acceptable and good balance, respectively. Enter a value as \code{NA} to skip that value of \code{lty} (e.g., \code{c(NA, .05)} to have only a dashed vertical line at .05). } \item{position}{ the position of the legend. Should be one of the allowed keyword options supplied to \code{x} in \fun{legend} (e.g., \code{"right"}, \code{"bottomright"}, etc.). Default is \code{"bottomright"}. Set to \code{NULL} for no legend to be included. Note that the legend will cover up points if you are not careful; setting \code{var.order} appropriately can help in avoiding this. } \item{\dots}{ ignored. } } \details{ For matching methods other than subclassification, \code{plot.summary.matchit} uses \code{x$sum.all[,"Std. Mean Diff."]} and \code{x$sum.matched[,"Std. Mean Diff."]} as the x-axis values. For subclassification, in addition to points for the unadjusted and aggregate subclass balance, numerals representing balance in individual subclasses are plotted if \code{subclass = TRUE} in the call to \code{summary}. Aggregate subclass standardized mean differences are taken from \code{x$sum.across[,"Std. Mean Diff."]} and the subclass-specific mean differences are taken from \code{x$sum.subclass}. } \value{ A plot is displayed, and \code{x} is invisibly returned. } \author{ Noah Greifer } \seealso{ \fun{summary.matchit}, \fun{dotchart} \pkgfun2{cobalt}{love.plot}{cobalt::love.plot} is a more flexible and sophisticated function to make Love plots and is also natively compatible with \code{matchit} objects. } \examples{ data("lalonde") m.out <- matchit(treat ~ age + educ + married + race + re74, data = lalonde, method = "nearest") plot(summary(m.out, interactions = TRUE), var.order = "unmatched") s.out <- matchit(treat ~ age + educ + married + race + nodegree + re74 + re75, data = lalonde, method = "subclass") plot(summary(s.out, subclass = TRUE), var.order = "unmatched", abs = FALSE) } MatchIt/man/method_cardinality.Rd0000644000176200001440000003156114142530160016512 0ustar liggesusers\name{method_cardinality} \alias{method_cardinality} \title{ Cardinality Matching } \description{ In \fun{matchit}, setting \code{method = "cardinality"} performs cardinality matching and other forms of matching using mixed integer programming. Rather than forming pairs, cardinality matching selects the largest subset of units that satisfies user-supplied balance constraints on mean differences. One of several available optimization programs can be used to solve the mixed integer program. The default is the GLPK library as implemented in the \emph{Rglpk} package, but performance can be dramatically improved using Gurobi and the \emph{gurobi} package, for which there is a free academic license. This page details the allowable arguments with \code{method = "cardinality"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for cardinality matching: \preformatted{ matchit(formula, data = NULL, method = "cardinality", estimand = "ATT", exact = NULL, discard = "none", s.weights = NULL, ratio = 1, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be balanced. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"cardinality"}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"}, \code{"ATC"}, and \code{"ATE"}. See Details. } \item{exact}{ for which variables exact matching should take place. Separate optimization will occur within each subgroup of the exact matching variables. } \item{discard}{ a string containing a method for discarding units outside a region of common support. } \item{s.weights}{ the variable containing sampling weights to be incorporated into the optimization. The balance constraints refer to the product of the sampling weights and the matching weights, and the sum of the product of the sampling and matching weights will be maximized. } \item{ratio}{ the desired ratio of control to treated units. Can be set to \code{NA} to maximize sample size without concern for this ratio. See Details. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. } \item{\dots}{ additional arguments that control the matching specification: \describe{ \item{\code{tols}}{ \code{numeric}; a vector of imbalance tolerances for mean differences, one for each covariate in \code{formula}. If only one value is supplied, it is applied to all. See \code{std.tols} below. Default is \code{.05} for standardized mean differences of at most .05 for all covariates between the treatment groups in the matched sample. } \item{\code{std.tols}}{ \code{logical}; whether each entry in \code{tols} corresponds to a raw or standardized mean difference. If only one value is supplied, it is applied to all. Default is \code{TRUE} for standardized mean differences. The standardization factor is the pooled standard deviation when \code{estimand = "ATE"}, the standard deviation of the treated group when \code{estimand = "ATT"}, and the standard deviation of the control group when \code{estimand = "ATC"} (the same as used in \fun{summary.matchit}). } \item{\code{solver}}{ the name of solver to use to solve the optimization problem. Available options include \code{"glpk"}, \code{"symphony"}, and \code{"gurobi"} for GLPK (implemented in the \emph{Rglpk} package), SYMPHONY (implemented in the \emph{Rsymphony} package), and Gurobi (implemented in the \emph{gurobi} package), respectively. The differences between them are in speed and solving ability. GLPK (the default) is the easiest to install, but Gurobi is recommended as it consistently outperforms other solvers and can find solutions even when others can't, and in less time. Gurobi is proprietary but can be used with a free trial or academic license. SYMPHONY may not produce reproducible results, even with a seed set. } \item{\code{time}}{ the maximum amount of time before the optimization routine aborts, in seconds. Default is 120 (2 minutes). For large problems, this should be set much higher. } } } The arguments \code{distance} (and related arguments), \code{mahvars}, \code{replace}, \code{m.order}, and \code{caliper} (and related arguments) are ignored with a warning. } \section{Outputs}{ Most outputs described in \fun{matchit} are returned with \code{method = "cardinality"}. The \code{match.matrix} and \code{subclass} components are omitted because no pairing or subclassification is done. When \code{include.obj = TRUE} in the call to \code{matchit()}, the output of the optimization function will be included in the output. When \code{exact} is specified, this will be a list of such objects, one for each stratum of the exact variables. } \details{ \subsection{Cardinality and Template Matching}{ Two types of matching are available with \code{method = "cardinality"}: cardinality matching and template matching. Cardinality matching finds the largest matched set that satisfies the balance constraints between treatment groups, with the additional constraint that the ratio of the number of matched control to matched treated units is equal to \code{ratio} (1 by default), mimicking k:1 matching. When not all treated units are included in the matched set, the estimand no longer corresponds to the ATT, so cardinality matching should be avoided if retaining the ATT is desired. To request cardinality matching, \code{estimand} should be set to \code{"ATT"} or \code{"ATC"} and \code{ratio} should be set to a positive integer. 1:1 cardinality matching is the default method when no arguments are specified. Template matching finds the largest matched set that satisfies balance constraints between each treatment group and a specified target sample. When \code{estimand = "ATT"}, it will find the largest subset of the control units that satisfies the balance constraints with respect to the treated group, which is left intact. When \code{estimand = "ATE"}, it will find the largest subsets of the treated group and of the control group that are balanced to the overall sample. To request template matching for the ATT, \code{estimand} should be set to \code{"ATT"} and \code{"ratio"} to \code{NA}. To request template matching for the ATE, \code{estimand} should be set to \code{"ATE"} and \code{ratio} can be set either to \code{NA} to maximize the size of each sample independently or to a positive integer to ensure that the ratio of matched control units to matched treated treats is fixed, mimicking k:1 matching. Unlike cardinality matching, template matching retains the requested estimand if a solution is found. Neither method involves creating pairs in the matched set, but it is possible to perform an additional round of pairing within the matched sample after cardinality matching or template matching for the ATE with a fixed sample size ratio. See Examples for an example of optimal pair matching after cardinality matching. The balance will not change, but additional precision and robustness can be gained by forming the pairs. The weights are scaled so that the sum of the weights in each group is equal to the number of matched units in the smaller group when cardinality matching or template matching for the ATE, and scaled so that the sum of the weights in the control group is equal to the number of treated units when template matching for the ATT. When the sample sizes of the matched groups is the same (i.e., when \code{ratio = 1}), no scaling is done. Robust standard errors should be used in effect estimation after cardinality or template matching (and cluster-robust standard errors if additional pairing is done in the matched sample). See \code{vignette("estimating-effects")} for more information. } \subsection{Specifying Balance Constraints}{ The balance constraints are on the (standardized) mean differences between the matched treatment groups for each covariate. Balance constraints should be set by supplying arguments to \code{tols} and \code{std.tols}. For example, setting \code{tols = .1} and \code{std.tols = TRUE} requests that all the mean differences in the matched sample should be within .1 standard deviations for each covariate. Different tolerances can be set for different variables; it might be beneficial to constrain the mean differences for highly prognostic covariates more tightly than for other variables. For example, one could specify \code{tols = c(.001, .05), std.tols = c(TRUE, FALSE)} to request that the standardized mean difference for the first covariate is less than .001 and the raw mean difference for the second covariate is less than .05. The values should be specified in the order they appear in \code{formula}, except when interactions are present. One can run the following code: \preformatted{MatchIt:::get_assign(model.matrix(~X1*X2 + X3, data = data))[-1]} which will output a vector of numbers and the variable to which each number corresponds; the first entry in \code{tols} corresponds to the variable labeled 1, the second to the variable labeled 2, etc. } \subsection{Dealing with Errors and Warnings}{ When the optimization cannot be solved at all, or at least within the time frame specified in the argument to \code{time}, an error or warning will appear. Unfortunately, it is hard to know exactly the cause of the failure and what measures should be taken to rectify it. A warning that says \code{"The optimizer failed to find an optimal solution in the time alotted. The returned solution may not be optimal."} usually means that an optimal solution may be possible to find with more time, in which case \code{time} should be increased or a faster solver should be used. Even with this warning, a potentially usable solution will be returned, so don't automatically take it to mean the optimization failed. Sometimes, when there are multiple solutions with the same resulting sample size, the optimizers will stall at one of them, not thinking it has found the optimum. The result should be checked to see if it can be used as the solution. An error that says \code{"The optimization problem may be infeasible."} usually means that there is a issue with the optimization problem, i.e., that there is no possible way to satisfy the constraints. To rectify this, one can try relaxing the constraints by increasing the value of \code{tols} or use another solver. Sometimes Gurobi can solve problems that the other solvers cannot. } } \references{ In a manuscript, you should reference the solver used in the optimization. For example, a sentence might read: \emph{Cardinality matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R with the optimization performed by GLPK.} See \code{vignette("matching-methods")} for more literature on cardinality matching. } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. \CRANpkg{designmatch}, which performs cardinality and template matching with many more options and more flexibility. The implementations of cardinality matching differ between \code{MatchIt} and \code{designmatch}, so their results might differ. \CRANpkg{optweight}, which offers similar functionality but in the context of weighting rather than matching. } \examples{ data("lalonde") #Choose your solver; "gurobi" is best, "glpk" is free and #easiest to install solver <- "glpk" # 1:1 cardinality matching m.out1 <- matchit(treat ~ age + educ + re74, data = lalonde, method = "cardinality", estimand = "ATT", ratio = 1, tols = .15, solver = solver) m.out1 summary(m.out1) # Template matching for the ATT m.out2 <- matchit(treat ~ age + educ + re74, data = lalonde, method = "cardinality", estimand = "ATT", ratio = NA, tols = .15, solver = solver) m.out2 summary(m.out2, un = FALSE) # Template matching for the ATE m.out3 <- matchit(treat ~ age + educ + re74, data = lalonde, method = "cardinality", estimand = "ATE", ratio = NA, tols = .15, solver = solver) m.out3 summary(m.out3, un = FALSE) # Pairing after 1:1 cardinality matching: m.out4 <- matchit(treat ~ age + educ + re74, data = lalonde, method = "optimal", distance = "mahalanobis", discard = m.out1$weights == 0) # Note that balance doesn't change but pair distances # are lower for the paired-upon variables summary(m.out4, un = FALSE) summary(m.out1, un = FALSE) # In these examples, a high tol was used and # few covariate matched on in order to not take too long; # with real data, tols should be much lower and more # covariates included if possible. } MatchIt/man/distance.Rd0000644000176200001440000004027414116323045014445 0ustar liggesusers\name{distance} \alias{distance} \title{Propensity scores and other distance measures} \description{ Several matching methods require or can involve the distance between treated and control units. Options include the Mahalanobis distance, propensity score distance, or distance between user-supplied values. Propensity scores are also used for common support via the \code{discard} options and for defining calipers. This page documents the options that can be supplied to the \code{distance} argument to \fun{matchit}. There are four ways to specify the \code{distance} argument: 1) as the string \code{"mahalanobis"}, 2) as a string containing the name of a method for estimating propensity scores, 3) as a vector of values whose pairwise differences define the distance between units, or 4) as a distance matrix containing all pairwise differences. When \code{distance} is specified as one of the allowed strings (described below) other than \code{"mahalanobis"}, a propensity score is estimated using the variables in \code{formula} and the method corresponding to the given argument. This propensity score can be used to compute the distance between units as the absolute difference between the propensity scores of pairs of units. In this respect, the propensity score is more like a "position" measure than a distance measure, since it is the pairwise difference that form the distance rather than the propensity scores themselves. Still, this naming convention is used to reflect their primary purpose without committing to the status of the estimated values as propensity scores, since transformations of the scores are allowed and user-supplied values that are not propensity scores can also be supplied (detailed below). Propensity scores can also be used to create calipers and common support restrictions, whether or not they are used in the actual distance measure used in the matching, if any. In addition to the \code{distance} argument, two other arguments can be specified that relate to the estimation and manipulation of the propensity scores. The \code{link} argument allows for different links to be used in models that require them such as generalized linear models, for which the logit and probit links are allowed, among others. In addition to specifying the link, the \code{link} argument can be used to specify whether the propensity score or the linearized version of the propensity score should be used; by specifying \code{link = "linear.{link}"}, the linearized version will be used. The \code{distance.options} argument can also be specified, which should be a list of values passed to the propensity score-estimating function, for example, to choose specific options or tuning parameters for the estimation method. If \code{formula}, \code{data}, or \code{verbose} are not supplied to \code{distance.options}, the corresponding arguments from \code{matchit()} will be automatically supplied. See the Examples for demonstrations of the uses of \code{link} and \code{distance.options}. When \code{s.weights} is supplied in the call to \code{matchit()}, it will automatically be passed to the propensity score-estimating function as the \code{weights} argument unless otherwise described below. } \section{Allowable options}{ Below are the allowed options for \code{distance}: \describe{ \item{\code{"glm"}}{ The propensity scores are estimated using a generalized linear model (e.g., logistic regression). The \code{formula} supplied to \code{matchit()} is passed directly to \fun{glm}, and \fun{predict.glm} is used to compute the propensity scores. The \code{link} argument can be specified as a link function supplied to \fun{binomial}, e.g., \code{"logit"}, which is the default. When \code{link} is prepended by \code{"linear."}, the linear predictor is used instead of the predicted probabilities. \code{distance = "glm"} with \code{link = "logit"} (logistic regression) is the default in \code{matchit()}. } \item{\code{"gam"}}{ The propensity scores are estimated using a generalized additive model. The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{mgcv}{gam}{mgcv::gam}, and \pkgfun2{mgcv}{predict.gam}{mgcv::predict.gam} is used to compute the propensity scores. The \code{link} argument can be specified as a link function supplied to \fun{binomial}, e.g., \code{"logit"}, which is the default. When \code{link} is prepended by \code{"linear."}, the linear predictor is used instead of the predicted probabilities. Note that unless the smoothing functions \pkgfun{mgcv}{s}, \pkgfun{mgcv}{te}, \pkgfun{mgcv}{ti}, or \pkgfun{mgcv}{t2} are used in \code{formula}, a generalized additive model is identical to a generalized linear model and will estimate the same propensity scores as \code{glm}. See the documentation for \pkgfun2{mgcv}{gam}{mgcv::gam}, \pkgfun2{mgcv}{formula.gam}{mgcv::formula.gam}, and \pkgfun2{mgcv}{gam.models}{mgcv::gam.models} for more information on how to specify these models. Also note that the formula returned in the \code{matchit()} output object will be a simplified version of the supplied formula with smoothing terms removed (but all named variables present). } \item{\code{"gbm"}}{ The propensity scores are estimated using a generalized boosted model. The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{gbm}{gbm}{gbm::gbm}, and \pkgfun2{gbm}{predict.gbm}{gbm::predict.gbm} is used to compute the propensity scores. The optimal tree is chosen using 5-fold cross-validation by default, and this can be changed by supplying an argument to \code{method} to \code{distance.options}; see \pkgfun2{gbm}{gbm.perf}{gbm::gbm.perf} for details. The \code{link} argument can be specified as \code{"linear"} to use the linear predictor instead of the predicted probabilities. No other links are allowed. The tuning parameter defaults differ from \code{gbm::gbm()}; they are as follows: \code{n.trees = 1e4}, \code{interaction.depth = 3}, \code{shrinkage = .01}, \code{bag.fraction = 1}, \code{cv.folds = 5}, \code{keep.data = FALSE}. These are the same defaults as used in \pkg{WeightIt} and \pkg{twang}, except for \code{cv.folds} and \code{keep.data}. Note this is not the same use of generalized boosted modeling as in \pkg{twang}; here, the number of trees is chosen based on cross-validation or out-of-bag error, rather than based on optimizing balance. \pkg{twang} should not be cited when using this method to estimate propensity scores. } \item{\code{"lasso"}, \code{"ridge"}, \code{"elasticnet"}}{ The propensity scores are estimated using a lasso, ridge, or elastic net model, respectively. The \code{formula} supplied to \code{matchit()} is processed with \fun{model.matrix} and passed to \pkgfun2{glmnet}{cv.glmnet}{glmnet::cv.glmnet}, and \pkgfun2{glmnet}{predict.cv.glmnet}{glmnet::predict.cv.glmnet} is used to compute the propensity scores. The \code{link} argument can be specified as a link function supplied to \fun{binomial}, e.g., \code{"logit"}, which is the default. When \code{link} is prepended by \code{"linear."}, the linear predictor is used instead of the predicted probabilities. When \code{link = "log"}, a Poisson model is used. For \code{distance = "elasticnet"}, the \code{alpha} argument, which controls how to prioritize the lasso and ridge penalties in the elastic net, is set to .5 by default and can be changed by supplying an argument to \code{alpha} in \code{distance.options}. For \code{"lasso"} and \code{"ridge"}, \code{alpha} is set to 1 and 0, respectively, and cannot be changed. The \code{cv.glmnet()} defaults are used to select the tuning parameters and generate predictions and can be modified using \code{distance.options}. If the \code{s} argument is passed to \code{distance.options}, it will be passed to \code{predict.cv.glmnet()}. Note that because there is a random component to choosing the tuning parameter, results will vary across runs unless a \link[=set.seed]{seed} is set. } \item{\code{"rpart"}}{ The propensity scores are estimated using a classification tree. The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{rpart}{rpart}{rpart::rpart}, and \pkgfun2{rpart}{predict.rpart}{rpart::predict.rpart} is used to compute the propensity scores. The \code{link} argument is ignored, and predicted probabilities are always returned as the distance measure. } \item{\code{"randomforest"}}{ The propensity scores are estimated using a random forest. The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{randomForest}{randomForest}{randomForest::randomForest}, and \pkgfun2{randomForest}{predict.randomForest}{randomForest::predict.randomForest} is used to compute the propensity scores. The \code{link} argument is ignored, and predicted probabilities are always returned as the distance measure. When \code{s.weights} is supplied to \code{matchit()}, it will not be passed to \code{randomForest} because \code{randomForest} does not accept weights. } \item{\code{"nnet"}}{ The propensity scores are estimated using a single-hidden-layer neural network. The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{nnet}{nnet}{nnet::nnet}, and \fun{fitted} is used to compute the propensity scores. The \code{link} argument is ignored, and predicted probabilities are always returned as the distance measure. An argument to \code{size} must be supplied to \code{distance.options} when using \code{method = "nnet"}. } \item{\code{"cbps"}}{ The propensity scores are estimated using the covariate balancing propensity score (CBPS) algorithm, which is a form of logistic regression where balance constraints are incorporated to a generalized method of moments estimation of of the model coefficients. The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{CBPS}{CBPS}{CBPS::CBPS}, and \code{\link{fitted}} is used to compute the propensity scores. The \code{link} argument can be specified as \code{"linear"} to use the linear predictor instead of the predicted probabilities. No other links are allowed. The \code{estimand} argument supplied to \code{matchit()} will be used to select the appropriate estimand for use in defining the balance constraints, so no argument needs to be supplied to \code{ATT} in \code{CBPS}. } \item{\code{"bart"}}{ The propensity scores are estimated using Bayesian additive regression trees (BART). The \code{formula} supplied to \code{matchit()} is passed directly to \pkgfun2{dbarts}{bart}{dbarts::bart2}, and \pkgfun2{dbarts}{bart}{dbarts::fitted} is used to compute the propensity scores. The \code{link} argument can be specified as \code{"linear"} to use the linear predictor instead of the predicted probabilities. When \code{s.weights} is supplied to \code{matchit()}, it will not be passed to \code{bart2} because the \code{weights} argument in \code{bart2} does not correspond to sampling weights. } \item{\code{"mahalanobis"}}{ No propensity scores are estimated. Rather than using the propensity score difference as the distance between units, the Mahalanobis distance is used instead. See \fun{mahalanobis} for details on how it is computed. The Mahalanobis distance is always computed using all the variables in \code{formula}. With this specification, calipers and common support restrictions cannot be used and the \code{distance} component of the output object will be empty because no propensity scores are estimated. The \code{link} and \code{distance.options} arguments are ignored. See individual methods pages for whether the Mahalanobis distance is allowed and how it is used. Sometimes this setting is just a placeholder to indicate that no propensity score is to be estimated (e.g., with \code{method = "genetic"}). To perform Mahalanobis distance matching \emph{and} estimate propensity scores to be used for a purpose other than matching, the \code{mahvars} argument should be used along with a different specification to \code{distance}. See the individual matching method pages for details on how to use \code{mahvars}. } } \code{distance} can also be supplied as a numeric vector whose values will be taken to function like propensity scores; their pairwise difference will define the distance between units. This might be useful for supplying propensity scores computed outside \code{matchit()} or resupplying \code{matchit()} with propensity scores estimated before without having to recompute them. \code{distance} can also be supplied as a matrix whose values represent the pairwise distances between units. The matrix should either be a square, with a row and column for each unit (e.g., as the output of a call to \code{as.matrix(\link{dist}(.))}), or have as many rows as there are treated units and as many columns as there are control units (e.g., as the output of a call to \pkgfun2{optmatch}{match_on}{optmatch::match_on}). Distance values of \code{Inf} will disallow the corresponding units to be matched. When \code{distance} is a supplied as a numeric vector or matrix, \code{link} and \code{distance.options} are ignored. } \section{Outputs}{ When specifying an argument to \code{distance} that estimates a propensity score, the output of the function called to estimate the propensity score (e.g., the \code{glm} object when \code{distance = "glm"}) will be included in the \code{matchit()} output object in the \code{model} component. When \code{distance} is anything other than \code{"mahalanobis"} and not matrix, the estimated or supplied distance measures will be included in the \code{matchit()} output object in the \code{distance} component. } \note{ In versions of \emph{MatchIt} prior to 4.0.0, \code{distance} was specified in a slightly different way. When specifying arguments using the old syntax, they will automatically be converted to the corresponding method in the new syntax but a warning will be thrown. \code{distance = "logit"}, the old default, will still work in the new syntax, though \code{distance = "glm", link = "logit"} is preferred (note that these are the default settings and don't need to be made explicit). } \examples{ data("lalonde") # Linearized probit regression PS: m.out1 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = "glm", link = "linear.probit") \dontshow{if (requireNamespace("mgcv", quietly = TRUE)) \{} # GAM logistic PS with smoothing splines (s()): m.out2 <- matchit(treat ~ s(age) + s(educ) + race + married + nodegree + re74 + re75, data = lalonde, distance = "gam") summary(m.out2$model) \dontshow{\}; if (requireNamespace("CBPS", quietly = TRUE)) \{} # CBPS for ATC matching w/replacement, using the just- # identified version of CBPS (setting method = "exact"): m.out3 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = "cbps", estimand = "ATC", distance.options = list(method = "exact"), replace = TRUE) \dontshow{\}} # Mahalanobis distance matching - no PS estimated m.out4 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = "mahalanobis") m.out4$distance #NULL # Mahalanobis distance matching with PS estimated # for use in a caliper; matching done on mahvars m.out5 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = "glm", caliper = .1, mahvars = ~ age + educ + race + married + nodegree + re74 + re75) summary(m.out5) # User-supplied propensity scores p.score <- fitted(glm(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, family = binomial)) m.out6 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = p.score) # User-supplied distance matrix using optmatch::match_on() \dontshow{if (requireNamespace("optmatch", quietly = TRUE)) \{} dist_mat <- optmatch::match_on( treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "rank_mahalanobis") m.out7 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, distance = dist_mat) \dontshow{\}}} MatchIt/man/method_cem.Rd0000644000176200001440000003342114110073120014741 0ustar liggesusers\name{method_cem} \alias{method_cem} \title{Coarsened Exact Matching} \description{ In \fun{matchit}, setting \code{method = "cem"} performs coarsened exact matching. With coarsened exact matching, covariates are coarsened into bins, and a complete cross of the coarsened covariates is used to form subclasses defined by each combination of the coarsened covariate levels. Any subclass that doesn't contain both treated and control units is discarded, leaving only subclasses containing treatment and control units that are exactly equal on the coarsened covariates. The coarsening process can be controlled by an algorithm or by manually specifying cutpoints and groupings. The benefits of coarsened exact matching are that the tradeoff between exact matching and approximate balancing can be managed to prevent discarding too many units, which can otherwise occur with exact matching. This page details the allowable arguments with \code{method = "cem"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for coarsened exact matching: \preformatted{ matchit(formula, data = NULL, method = "cem", estimand = "ATT", s.weights = NULL, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the subclasses defined by a full cross of the coarsened covariate levels. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"cem"}. } \item{estimand}{ a string containing the desired estimand. Allowable options include \code{"ATT"}, \code{"ATC"}, and \code{"ATE"}. The estimand controls how the weights are computed; see the Computing Weights section at \fun{matchit} for details. When \code{k2k = TRUE} (see below), \code{estimand} also controls how the matching is done. } \item{s.weights}{ the variable containing sampling weights to be incorporated into balance statistics. These weights do not affect the matching process. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. } \item{\dots}{ additional arguments to control the matching process. \describe{ \item{\code{grouping}}{ a named list with an (optional) entry for each categorical variable to be matched on. Each element should itself be a list, and each entry of the sublist should be a vector containing levels of the variable that should be combined to form a single level. Any categorical variables not included in \code{grouping} will remain as they are in the data, which means exact matching, with no coarsening, will take place on these variables. See Details. } \item{\code{cutpoints}}{ a named list with an (optional) entry for each numeric variable to be matched on. Each element describes a way of coarsening the corresponding variable. They can be a vector of cutpoints that demarcate bins, a single number giving the number of bins, or a string corresponding to a method of computing the number of bins. Allowable strings include \code{"sturges"}, \code{"scott"}, and \code{"fd"}, which use the functions \pkgfun2{grDevices}{nclass}{nclass.Sturges}, \pkgfun2{grDevices}{nclass}{nclass.scott}, and \pkgfun2{grDevices}{nclass}{nclass.FD}, respectively. The default is \code{"sturges"} for variables that are not listed or if no argument is supplied. Can also be a single value to be applied to all numeric variables. See Details. } \item{\code{k2k}}{ code{logical}; whether 1:1 matching should occur within the matched strata. If \code{TRUE} nearest neighbor matching without replacement will take place within each stratum, and any unmatched units will be dropped (e.g., if there are more treated than control units in the stratum, the treated units without a match will be dropped). The \code{k2k.method} argument controls how the distance between units is calculated. } \item{\code{k2k.method}}{ \code{character}; how the distance between units should be calculated if \code{k2k = TRUE}. Allowable arguments include \code{NULL} (for random matching), \code{"mahalanobis"} (for Mahalanobis distance matching), or any allowable argument to \code{method} in \fun{dist}. Matching will take place on scaled versions of the original (non-coarsened) variables. The default is \code{"mahalanobis"}. } \item{\code{mpower}}{ if \code{k2k.method = "minkowski"}, the power used in creating the distance. This is passed to the \code{p} argument of \fun{dist}. } } } The arguments \code{distance} (and related arguments), \code{exact}, \code{mahvars}, \code{discard} (and related arguments), \code{replace}, \code{m.order}, \code{caliper} (and related arguments), and \code{ratio} are ignored with a warning. } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "cem"} except for \code{match.matrix}. When \code{k2k = TRUE}, a \code{match.matrix} component with the matched pairs is also included. \code{include.obj} is ignored. } \details{ If the coarsening is such that there are no exact matches with the coarsened variables, the \code{grouping} and \code{cutpoints} arguments can be used to modify the matching specification. Reducing the number of cutpoints or grouping some variable values together can make it easier to find matches. See Examples below. Removing variables can also help (but they will likely not be balanced unless highly correlated with the included variables). To take advantage of coarsened exact matching without failing to find any matches, the covariates can be manually coarsened outside of \code{matchit()} and then supplied to the \code{exact} argument in a call to \code{matchit()} with another matching method. Setting \code{k2k = TRUE} is equivalent to matching with \code{k2k = FALSE} and then supplying stratum membership as an exact matching variable (i.e., in \code{exact}) to another call to \code{matchit()} with \code{method = "nearest"}, \code{distance = "mahalanobis"} and an argument to \code{discard} denoting unmatched units. It is also equivalent to performing nearest neighbor matching supplying coarsened versions of the variables to \code{exact}, except that \code{method = "cem"} automatically coarsens the continuous variables. The \code{estimand} argument supplied with \code{method = "cem"} functions the same way it would in these alternate matching calls, i.e., by determining the "focal" group that controls the order of the matching. \subsection{Grouping and Cutpoints}{ The \code{grouping} and \code{cutpoints} arguments allow one to fine-tune the coarsening of the covariates. \code{grouping} is used for combining categories of categorical covariates and \code{cutpoints} is used for binning numeric covariates. The values supplied to these arguments should be iteratively changed until a matching solution that balances covariate balance and remaining sample size is obtained. The arguments are described below. The argument to \code{grouping} must be a list, where each component has the name of a categorical variable, the levels of which are to be combined. Each component must itself be a list; this list contains one or more vectors of levels, where each vector corresponds to the levels that should be combined into a single category. For example, if a variable \code{amount} had levels \code{"none"}, \code{"some"}, and \code{"a lot"}, one could enter \code{grouping = list(amount = list(c("none"), c("some", "a lot")))}, which would group \code{"some"} and \code{"a lot"} into a single category and leave \code{"none"} in its own category. Any levels left out of the list for each variable will be left alone (so \code{c("none")} could have been omitted from the previous code). Note that if a categorical variable does not appear in \code{grouping}, it will not be coarsened, so exact matching will take place on it. \code{grouping} should not be used for numeric variables; use \code{cutpoints}, described below, instead. The argument to \code{cutpoints} must also be a list, where each component has the name of a numeric variables that is to be binned. (As a shortcut, it can also be a single value that will be applied to all numeric variables). Each component can take one of three forms: a vector of cutpoints that separate the bins, a single number giving the number of bins, or a string corresponding to an algorithm used to compute the number of bins. Any values at a boundary will be placed into the higher bin; e.g., if the cutpoints were \code{(c(0, 5, 10))}, values of 5 would be placed into the same bin as values of 6, 7, 8, or 9, and values of 10 would be placed into a different bin. Internally, values of \code{-Inf} and \code{Inf} are appended to the beginning and end of the range. When given as a single number defining the number of bins, the bin boundaries are the maximum and minimum values of the variable with bin boundaries evenly spaced between them, i.e., not quantiles. A value of 0 will not perform any binning (equivalent to exact matching on the variable), and a value of 1 will remove the variable from the exact matching variables but it will be still used for pair matching when \code{k2k = TRUE}. The allowable strings include \code{"sturges"}, \code{"scott"}, and \code{"fd"}, which use the corresponding binning method, and \code{"q#"} where \code{#} is a number, which splits the variable into \code{#} equally-sized bins (i.e., quantiles). An example of a way to supply an argument to \code{cutpoints} would be the following: \preformatted{ cutpoints = list(X1 = 4, X2 = c(1.7, 5.5, 10.2), X3 = "scott", X4 = "q5") } This would split \code{X1} into 4 bins, \code{X2} into bins based on the provided boundaries, \code{X3} into a number of bins determined by \pkgfun2{grDevices}{nclass}{nclass.scott}, and \code{X4} into quintiles. All other numeric variables would be split into a number of bins determined by \pkgfun2{grDevices}{nclass}{nclass.Sturges}, the default. } } \note{ This method does not rely on the \emph{cem} package, instead using code written for \emph{MatchIt}, but its design is based on the original \emph{cem} functions. Versions of \emph{MatchIt} prior to 4.1.0 did rely on \emph{cem}, so results may differ between versions. There are a few differences between the ways \emph{MatchIt} and \emph{cem} (and older versions of \emph{MatchIt}) differ in executing coarsened exact matching, described below. \itemize{ \item{In \emph{MatchIt}, when a single number is supplied to \code{cutpoints}, it describes the number of bins; in \emph{cem}, it describes the number of cutpoints separating bins. The \emph{MatchIt} method is closer to how \fun{hist} processes breaks points to create bins.} \item{In \emph{MatchIt}, values on the cutpoint boundaries will be placed into the higher bin; in \emph{cem}, they are placed into the lower bin. To avoid consequences of this choice, ensure the bin boundaries do not coincide with observed values of the variables.} \item{When \code{cutpoints} are used, \code{"ss"} (for Shimazaki-Shinomoto's rule) can be used in \emph{cem} but not in \emph{MatchIt}.} \item{When \code{k2k = TRUE}, \emph{MatchIt} matches on the original variables (scaled), whereas \emph{cem} matches on the coarsened variables. Because the variables are already exactly matched on the coarsened variables, matching in \emph{cem} is equivalent to random matching within strata.} \item{When \code{k2k = TRUE}, in \emph{MatchIt} matched units are identified by pair membership, and the original stratum membership prior to 1:1 matching is discarded. In \emph{cem}, pairs are not identified beyond the stratum the members are part of.} \item{When \code{k2k = TRUE}, \code{k2k.method = "mahalanobis"} can be requested in \emph{MatchIt} but not in \emph{cem}.} } } \references{ In a manuscript, you don't need to cite another package when using \code{method = "cem"} because the matching is performed completely within \emph{MatchIt}. For example, a sentence might read: \emph{Coarsened exact matching was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R.} It would be a good idea to cite the following article, which develops the theory behind coarsened exact matching: Iacus, S. M., King, G., & Porro, G. (2012). Causal Inference without Balance Checking: Coarsened Exact Matching. Political Analysis, 20(1), 1–24. \doi{10.1093/pan/mpr013} } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. The \emph{cem} package, upon which this method is based and which provided the workhorse in previous versions of \emph{MatchIt}. \code{\link{method_exact}} for exact matching, which performs exact matching on the covariates without coarsening. } \examples{ data("lalonde") # Coarsened exact matching on age, race, married, and educ with educ # coarsened into 5 bins and race coarsened into 2 categories, # grouping "white" and "hispan" together m.out1 <- matchit(treat ~ age + race + married + educ, data = lalonde, method = "cem", cutpoints = list(educ = 5), grouping = list(race = list(c("white", "hispan"), c("black")))) m.out1 summary(m.out1) # The same but requesting 1:1 Mahalanobis distance matching with # the k2k and k2k.method argument. Note the remaining number of units # is smaller than when retaining the full matched sample. m.out2 <- matchit(treat ~ age + race + married + educ, data = lalonde, method = "cem", cutpoints = list(educ = 5), grouping = list(race = list(c("white", "hispan"), "black")), k2k = TRUE, k2k.method = "mahalanobis") m.out2 summary(m.out2, un = FALSE) }MatchIt/man/plot.matchit.Rd0000644000176200001440000001725514116323123015261 0ustar liggesusers\name{plot.matchit} \alias{plot.matchit} \alias{plot.matchit.subclass} \title{ Generate Balance Plots after Matching and Subclassification } \description{ Generates plots displaying distributional balance and overlap on covariates and propensity scores before and after matching and subclassification. For displaying balance solely on covariate standardized mean differences, see \fun{plot.summary.matchit}. The plots here can be used to assess to what degree covariate and propensity score distributions are balanced and how weighting and discarding affect the distribution of propensity scores. } \usage{ \method{plot}{matchit}(x, type = "qq", interactive = TRUE, which.xs = NULL, ...) \method{plot}{matchit.subclass}(x, type = "qq", interactive = TRUE, which.xs = NULL, subclass, ...) } \arguments{ \item{x}{ a \code{matchit} object; the output of a call to \fun{matchit}. } \item{type}{ the type of plot to display. Options include \code{"qq"}, \code{"ecdf"}, \code{"density"}, \code{"jitter"}, and \code{"histogram"}. See Details. Default is \code{"qq"}. Abbreviations allowed. } \item{interactive}{ \code{logical}; whether the graphs should be displayed in an interactive way. Only applies for \code{type = "qq"}, \code{"ecdf"}, \code{"density"}, and \code{"jitter"}. See Details. } \item{which.xs}{ with \code{type = "qq"}, \code{"ecdf"}, or \code{"density"}, for which covariate(s) plots should be displayed. Factor variables should be named by the original variable name rather than the names of individual dummy variables created after expansion with \code{model.matrix}. } \item{subclass}{ with subclassification and \code{type = "qq"}, \code{"ecdf"}, or \code{"density"}, whether to display balance for individual subclasses, and, if so, for which ones. Can be \code{TRUE} (display plots for all subclasses), \code{FALSE} (display plots only in aggregate), or the indices (e.g., \code{1:6}) of the specific subclasses for which to display balance. When unspecified, if \code{interactive = TRUE}, you will be asked for which subclasses plots are desired, and otherwise, plots will be displayed only in aggregate. } \item{\dots}{ arguments passed to \fun{plot} to control the appearance of the plot. Not all options are accepted. } } \details{ \code{plot.matchit} makes one of five different plots depending on the argument supplied to \code{type}. The first three, \code{"qq"}, \code{"ecdf"}, and \code{"density"}, assess balance on the covariates. When \code{interactive = TRUE}, plots for three variables will be displayed at a time, and the prompt in the console allows you to move on to the next set of variables. When \code{interactive = FALSE}, multiple pages are plotted at the same time, but only the last few variables will be visible in the displayed plot. To see only a few specific variables at a time, use the \code{which.xs} argument to display plots for just those variables. If fewer than three variables are available (after expanding factors into their dummies), \code{interactive} is ignored. With \code{type = "qq"}, empirical quantile-quantile (eQQ) plots are created for each covariate before and after matching. The plots involve interpolating points in the smaller group based on the weighted quantiles of the other group. When points are approximately on the 45-degree line, the distributions in the treatment and control groups are approximately equal. Major deviations indicate departures from distributional balance. With variable with fewer than 5 unique values, points are jittered to more easily visualize counts. With \code{type = "ecdf"}, empirical cumulative density function (eCDF) plots are created for each covariate before and after matching. Two eCDF lines are produced in each plot: a gray one for control units and a black one for treated units. Each point on the lines corresponds to the proportion of units (or proportionate share of weights) less than or equal to the corresponding covariate value (on the x-axis). Deviations between the lines on the same plot indicates distributional imbalance between the treatment groups for the covariate. The eCDF and eQQ statistics in \fun{summary.matchit} correspond to these plots: the eCDF max (also known as the Kolmogorov-Smirnov statistic) and mean are the largest and average vertical distance between the lines, and the eQQ max and mean are the largest and average horizontal distance between the lines. With \code{type = "density"}, density plots are created for each covariate before and after matching. Two densities are produced in each plot: a gray one for control units and a black one for treated units. The x-axis corresponds to the value of the covariate and the y-axis corresponds to the density or probability of that covariate value in the corresponding group. For binary covariates, bar plots are produced, having the same interpretation. Deviations between the black and gray lines represent imbalances in the covariate distribution; when the lines coincide (i.e., when only the black line is visible), the distributions are identical. The last two plots, \code{"jitter"} and \code{"histogram"}, visualize the distance (i.e., propensity score) distributions. These plots are more for heuristic purposes since the purpose of matching is to achieve balance on the covariates themselves, not the propensity score. With \code{type = "jitter"}, a jitter plot is displayed for distance values before and after matching. This method requires a distance variable (e.g., a propensity score) to have been estimated or supplied in the call to \code{matchit()}. The plot displays individuals values for matched and unmatched treatment and control units arranged horizontally by their propensity scores. Points are jitter so counts are easier to see. The size of the points increases when they receive higher weights. When \code{interactive = TRUE}, you can click on points in the graph to identify their rownames and indices to further probe extreme values, for example. With subclassification, vertical lines representing the subclass boundaries are overlay on the plots. With \code{type = "histogram"}, a histogram of distance values is displayed for the treatment and control groups before and after matching. This method requires a distance variable (e.g., a propensity score) to have been estimated or supplied in the call to \code{matchit()}. With subclassification, vertical lines representing the subclass boundaries are overlay on the plots. With all methods, sampling weights are incorporated into the weights if present. } \note{ Sometimes, bugs in the plotting functions can cause strange layout or size issues. Running \fun{frame} or \fun{dev.off} can be used to reset the plotting pane (note the latter will delete any plots in the plot history). } \seealso{ \fun{summary.matchit} for numerical summaries of balance, including those that rely on the eQQ and eCDF plots. \fun{plot.summary.matchit} for plotting standardized mean differences in a Love plot. \pkgfun2{cobalt}{bal.plot}{cobalt::bal.plot} for displaying distributional balance in several other ways that are more easily customizable and produce \emph{ggplot2} objects. \emph{cobalt} functions natively support \code{matchit} objects. } \examples{ data("lalonde") m.out <- matchit(treat ~ age + educ + married + race + re74, data = lalonde, method = "nearest") plot(m.out, type = "qq", interactive = FALSE, which.xs = c("age", "educ", "married")) plot(m.out, type = "histogram") s.out <- matchit(treat ~ age + educ + married + race + nodegree + re74 + re75, data = lalonde, method = "subclass") plot(s.out, type = "density", interactive = FALSE, which.xs = c("age", "educ", "married"), subclass = 3) plot(s.out, type = "jitter", interactive = FALSE) } MatchIt/man/method_subclass.Rd0000644000176200001440000001625414146245430016037 0ustar liggesusers\name{method_subclass} \alias{method_subclass} %%%%%NEEDS EDITING \title{ Subclassification } \description{ In \fun{matchit}, setting \code{method = "subclass"} performs subclassification on the distance measure (i.e., propensity score). Treatment and control units are placed into subclasses based on quantiles of the propensity score in the treated group, in the control group, or overall, depending on the desired estimand. Weights are computed based on the proportion of treated units in each subclass. Subclassification implemented here does not rely on any other package. This page details the allowable arguments with \code{method = "subclass"}. See \fun{matchit} for an explanation of what each argument means in a general context and how it can be specified. Below is how \code{matchit()} is used for subclassification: \preformatted{ matchit(formula, data = NULL, method = "subclass", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", discard = "none", reestimate = FALSE, s.weights = NULL, verbose = FALSE, ...) } } \arguments{ \item{formula}{ a two-sided \fun{formula} object containing the treatment and covariates to be used in creating the distance measure used in the subclassification. } \item{data}{ a data frame containing the variables named in \code{formula}. If not found in \code{data}, the variables will be sought in the environment. } \item{method}{ set here to \code{"subclass"}. } \item{distance}{ the distance measure to be used. See \code{\link{distance}} for allowable options. \code{distance = "mahalanobis"} and supplying a matrix are not allowed. } \item{link}{ when \code{distance} is specified as a string, an additional argument controlling the link function used in estimating the distance measure. See \code{\link{distance}} for allowable options with each option. } \item{distance.options}{ a named list containing additional arguments supplied to the function that estimates the distance measure as determined by the argument to \code{distance}. } \item{estimand}{ the target \code{estimand}. If \code{"ATT"}, the default, subclasses are formed based on quantiles of the distance measure in the treated group; if \code{"ATC"}, subclasses are formed based on quantiles of the distance measure in the control group; if \code{"ATE"}, subclasses are formed based on quantiles of the distance measure in the full sample. The estimand also controls how the subclassification weights are computed; see the Computing Weights section at \fun{matchit} for details. } \item{discard}{ a string containing a method for discarding units outside a region of common support. } \item{reestimate}{ if \code{discard} is not \code{"none"}, whether to re-estimate the propensity score in the remaining sample prior to subclassification. } \item{s.weights}{ the variable containing sampling weights to be incorporated into propensity score models and balance statistics. } \item{verbose}{ \code{logical}; whether information about the matching process should be printed to the console. } \item{\dots}{ additional arguments that control the subclassification: \describe{ \item{\code{subclass}}{ either the number of subclasses desired or a vector of quantiles used to divide the distance measure into subclasses. Default is 6. } \item{\code{min.n}}{ the minimum number of units of each treatment group that are to be assigned each subclass. If the distance measure is divided in such a way that fewer than \code{min.n} units of a treatment group are assigned a given subclass, units from other subclasses will be reassigned to fill the deficient subclass. Default is 1. } } } The arguments \code{exact}, \code{mahvars}, \code{replace}, \code{m.order}, \code{caliper} (and related arguments), and \code{ratio} are ignored with a warning. } \section{Outputs}{ All outputs described in \fun{matchit} are returned with \code{method = "subclass"} except that \code{match.matrix} is excluded and one additional component, \code{q.cut}, is included, containing a vector of the distance measure cutpoints used to define the subclasses. Note that when \code{min.n > 0}, the subclass assignments may not strictly obey the quantiles listed in \code{q.cut}. \code{include.obj} is ignored. } \details{ After subclassification, effect estimates can be computed separately in the subclasses and combined, or a single marginal effect can be estimated by using the weights in the full sample. When using the weights, the method is sometimes referred to as marginal mean weighting through stratification (MMWS; Hong, 2010) or fine stratification weighting (Desai et al., 2017). The weights can be interpreted just like inverse probability weights. Changing \code{min.n} can change the quality of the weights. Generally, a low \code{min.w} will yield better balance because subclasses only contain units with relatively similar distance values, but may yield higher variance because extreme weights can occur due to there being few members of a treatment group in some subclasses. Note that subclassification weights can also be estimated using \emph{WeightIt}, which provides some additional methods for estimating propensity scores. Where propensity score-estimation methods overlap, both packages will yield the same weights. } \references{ In a manuscript, you don't need to cite another package when using \code{method = "subclass"} because the subclassification is performed completely within \emph{MatchIt}. For example, a sentence might read: \emph{Propensity score subclassification was performed using the MatchIt package (Ho, Imai, King, & Stuart, 2011) in R.} It may be a good idea to cite Hong (2010) or Desai et al. (2017) if the treatment effect is estimated using the subclassification weights. Desai, R. J., Rothman, K. J., Bateman, B. . T., Hernandez-Diaz, S., & Huybrechts, K. F. (2017). A Propensity-score-based Fine Stratification Approach for Confounding Adjustment When Exposure Is Infrequent: Epidemiology, 28(2), 249–257. \doi{10.1097/EDE.0000000000000595} Hong, G. (2010). Marginal mean weighting through stratification: Adjustment for selection bias in multilevel data. Journal of Educational and Behavioral Statistics, 35(5), 499–531. \doi{10.3102/1076998609359785} } \seealso{ \fun{matchit} for a detailed explanation of the inputs and outputs of a call to \code{matchit()}. \code{\link{method_full}} for optimal full matching, which is similar to subclassification except that the number of subclasses and subclass membership are chosen to optimize the within-subclass distance. } \examples{ data("lalonde") # PS subclassification for the ATT with 7 subclasses s.out1 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "subclass", subclass = 7) s.out1 summary(s.out1, subclass = TRUE) # PS subclassification for the ATE with 10 subclasses # and at least 2 units in each group per subclass s.out2 <- matchit(treat ~ age + educ + race + nodegree + married + re74 + re75, data = lalonde, method = "subclass", subclass = 10, estimand = "ATE", min.n = 2) s.out2 summary(s.out2) } MatchIt/DESCRIPTION0000644000176200001440000000504614172363674013333 0ustar liggesusersPackage: MatchIt Version: 4.3.3 Title: Nonparametric Preprocessing for Parametric Causal Inference Description: Selects matched samples of the original treated and control groups with similar covariate distributions -- can be used to match exactly on covariates, to match on propensity scores, or perform a variety of other matching procedures. The package also implements a series of recommendations offered in Ho, Imai, King, and Stuart (2007) . (The 'gurobi' package, which is not on CRAN, is optional and comes with an installation of the Gurobi Optimizer, available at .) Authors@R: c( person("Daniel", "Ho", email = "daniel.e.ho@gmail.com", role = c("aut"), comment = c(ORCID = "0000-0002-2195-5469")), person("Kosuke", "Imai", email = "imai@harvard.edu", role = c("aut"), comment = c(ORCID = "0000-0002-2748-1022")), person("Gary", "King", email = "king@harvard.edu", role = c("aut"), comment = c(ORCID = "0000-0002-5327-7631")), person("Elizabeth", "Stuart", email = "estuart@jhu.edu", role = c("aut"), comment = c(ORCID = "0000-0002-9042-8611")), person("Alex", "Whitworth", email = "whitworth.alex@gmail.com", role = c("ctb")), person("Noah", "Greifer", role = c("ctb", "cre", "aut"), email = "noah.greifer@gmail.com", comment = c(ORCID="0000-0003-3067-7154")) ) Depends: R (>= 3.1.0) Imports: backports (>= 1.1.9), Rcpp (>= 1.0.7) Suggests: optmatch, Matching, rgenoud, nnet, rpart, mgcv, CBPS (>= 0.17), dbarts, randomForest, glmnet (>= 4.0), gbm (>= 2.1.7), cobalt (>= 4.2.3), boot, lmtest, sandwich (>= 2.5-1), survival, RcppProgress (>= 0.4.2), Rglpk, Rsymphony, gurobi, knitr, rmarkdown LinkingTo: Rcpp, RcppProgress SystemRequirements: C++11 Encoding: UTF-8 LazyData: true License: GPL (>= 2) URL: https://kosukeimai.github.io/MatchIt/, https://github.com/kosukeimai/MatchIt BugReports: https://github.com/kosukeimai/MatchIt/issues VignetteBuilder: knitr NeedsCompilation: yes Packaged: 2022-01-16 08:21:34 UTC; NoahGreifer Author: Daniel Ho [aut] (), Kosuke Imai [aut] (), Gary King [aut] (), Elizabeth Stuart [aut] (), Alex Whitworth [ctb], Noah Greifer [ctb, cre, aut] () Maintainer: Noah Greifer Repository: CRAN Date/Publication: 2022-01-20 22:52:44 UTC MatchIt/build/0000755000176200001440000000000014170752616012713 5ustar liggesusersMatchIt/build/vignette.rds0000644000176200001440000000054714170752616015260 0ustar liggesusersQO0;( O~ac }ۍ-YY/Y|{8[v]6ƃ>ݿvk1qXC=ua.h! q"}JRTn?D,*(@BKs$`kr-hҹ:@S/QӉE 7 ,> R "81_.Y!_++Xw8mBLmrO*ѸV#J#)1cb5-J@SfP?A']J(A52h?Ow8'֩z:b'7S߻D^LD6q:MatchIt/build/partial.rdb0000644000176200001440000022645314170752531015050 0ustar liggesusers u&`H7)R"5 pxIHJK<$JzfZPwc#YCc[gG68nl9sNI;zU_7`Ȯ*4q@WļT*IJS.Ju^.8+ðdqOfe"sp7UNw1:ՊgE҂~.ՂEﰱ ^΀f T)Z+>yձ16fc {NXָ M)}CoLLeߵ6<3%6L)Zݼr^/)2Xvʑ{)0qK_-Xk,s*"Ee7/߶oTH2|+ZjP*V@]/p=AQnaכּ#Uoô^:mT3Q;2=ǰLf?0j14kyY ,sԷ܆兊2ˆeCNQ,cr}1R$%1T .tXߤ yEʆ'Lu7˾m4;aEUfJT$Ye ۟"M4-}v 7La|lHV\;J+ρ.7Ztٔ\p r?P4^ʓ&vܙ5#G} 4du8>/fC ].03U%֯x6"/Q fN1<4xvpF3۾Q&M[/.X^޵mܓuX萇DPmȳliYh3J^4DE3,Za?4߿?l!Dd\".IE):,>$>1!NJ% #:ZjeW"Θ-S~ Ji,#|E&j?9_O4* sױcEB-תCO1tS4_>_NY6%s[=i""iriV~@ *7jglmS$j_4 j 'SxGq"lr=6Hi,U2ͼB^./Lm8˕.N56PY+ ] .*^BW0 ݽs=Sf{صȡ/D>]2X^ãr^[ +-WKf*Ց| %T 9R8`j.swʵ.yHtZ,%iKx`& .ִ Z ] q2[{KkS (՘EAZm:}z=7DkȦo0_M~dlJ;鐴|221N W'10Er<-5] *ꕰۘX}t#CMG]2:N. N+iڎ68jڢDABgCiYĠn!8aJ; NutƎnNt%l=Ee*#1FʮΖ{k.b,I:I"6r3\kQM =^֠6 2/)$D|/(QYɧu'ߣPr[ߧ\(bCXѥT\L^LkԬL7 m4]22 =J%$ug? o$S =SJ". /UCjszGE0 $D N .7zǒ>,8[\p$*VKy;*\ijJ wr1e*_00\p GpBMUq&Q&8bU^ūrcX$~ 5WyA@zKf xIP%)x?UP TW@ ϨT\Rh 3ZY'Tu٢Vj&8a:  NeYԦW0L4QE}lSX [#bhأ= 4p[hǃ`7VS@D[7=+.8YGe VD<>I̷L";1 e#E>{=ԳC`װjGXS9|&xň3bJs=gn'SB%2qzT6 eFͱ%&\1,[d; j@(,9X36&O2ku$Uou/k8 3_}B3W `_id";o'bֈlcG+ƈOZ,91egZ\vq1.n"fV<.Z jQ44B(I5jfgEeI] NOtZ} 7PrW;I)։FBw LSI%B\3GW`̤ @VСd+4s>8L]L-$ y;^3Wӊ,/f>D➌}RFl:q-ZdoyN)l!0 2m5>f=@io5w!4٘gcJ ԐgaSCY5cK^ #1GfO Sa~6wLqKg3VrxvtsK9Zu.ku"&M5 Bw!(twSa-΁]1XD5+عQk> "5~ Kʵ`Tq0etI?oԓ??)דk(JL7W"@~'5nDh@pT Ez$8}2Qꇷ:'SvOގb؞jg<7/!]pIJun"s[_FM~T^)5 *W%GVa*sy85p |ہ\RWBp7M Wq[,p^mT(MYt) ?rg>%%`|kQ צSΖ9D"/v('w{QS|>Zl͏ϚG$f v)WNfUf^+@@Yס>6-(DyVjSrO.ׁE?FH滬kAqo6bAܥ ֍k">ZBo+ju#Ɖ xӨ<:D:7: 7$;Op upoF44m>p\xi27_h?Mg"ة~zaWt7n|tO XA<:{ =5o|s,eg:K~[ܦAXK^~TX^[0dzio)Fж=fSfvrD6PmO_;f,xh8%uMk'TSdՒ4'LOoT#RqIMjҋ)s=& P6͂**fOFL}''䈉Dy & 7 N_hD9{ 'W)2d'/5bgtl1b"wFIE=ԈDO#=(=vz %._*=fR$1GbnRa;&1 # I`( `ܜ£zq+ ;"E+tI\rfI@,.pi璋@9;wH'LB.ł-[.%)8a]ܘꨋ@g")&MNqvt#zSUuFb, EY'jF#f]b:,z|*Ew Ntө|ۖf+IԕSIBX.d(jRЬcG׀\.g||{*jgɇǐ=EQ 9SDxrc+1sC**qP\A)2f2sJoд>NMQQ Ğ"pKSd\+ ||\j#1VDںاE#'D*[gliIy%Ko)YRkst/¢jWtD+HF3k*kfR td8 vZZuU,ǣ*8)+o:㍻h,|/{o}zRmkRMt۸֟??lB ,|&rYxIaڏ{XoQAb,?Oe,հT,6$'.C?&EMir0#>oo<W?1s֛u{u$D#q:$_ZVg)O.ub] U'ԣ: LP1!e'L@ W;q[7ytxZ-%}~FR) E3J|Pp$j(! LOMEM >ĮPq]dzj2 Mceޠ¯C1} P쇀 8b]A+ szi JJ { 18g8brofSJ%t(Mѥg .8az*깐 l1UO1;:JԼxESZ< 4:2l J̽wpC$@60vnPQ Dc:{>$+xӢ Zd*|0% E1ߎb2i>%.NR,xt v! 1l ]̛{skWxY3ό0|¥.Ko*ar1|*ZU?F)ն T<ĜkwA2_pv <%xZ-5*vA/z,N:qPvpg|/:xԾ>z#1Ώ MfE60]]B,w._\ 1ΊȑqKL.^ӥ]'lY޸(ST%(E$Q`%4HtI<| 1mSVAF$/d[ !TWk9aKv3RD'{ILu'3TO4bIur'UFbsc8/1( C7zh{4<\p."^| ^ͥ@hOA9x=uا YRgP/P?ГPHu_3씿`n0R;'7Ml$ GQK7 9"[ڸ֭Xm-zF*`k&C4@}ϧwXqArE"<_>k~W=k/~R$EKoZܱTG/%`8a<-VOt  "GU[![qÃ̆w2J |Rz0(mHUoƑSgF(mϚ:o8t݇;SM:5ΣЦyģX*Gq w‚Nk+S#̏t傁&vָUq[܌R.d9_Zemz9~O(!Q]nnGb3D-;YLHV{{5Ay)5kORr=/W5Q H 1 \mE3kR ;A<[hh;`A(.`rjZ.TT+ ZJn10A BY܂/`K[ ׶zPjsׁ_䯏 ĭ)a1;`=(.`|Xj1 W/[ 4JxeM)At\n:(RQL%3x7ha;`:(.`rǘU|;/QT+ :b`%x/k8H+[pPjs%o8(ēDAvo8pYb %hPrtZ Jhʕbn4zp 봙evp'TxPjsׂK Ԍ%]$n<|8vxP]0J .ՠJn12pCgªM*^H+tحOQӱ) Y;hG˪e+"۪]D]\7#gac4j]ϱBpBI?m{(.hoeZm%i. Pų52Iˁ}{3̢2J NR'TTX&[oGjz4(ƃa^eU"trTpBŪEtpdɘ7V uKjdgT P~*qޜO%LXvx̛PhL0Imm 2onWRG/g- cO'Lz'L|B3R1YQʿ d'LB)d'TTʜd0 #8&̫u?EJ[7yՂ*fpXg#Y%Oc+JQϰbn%4/s*U^?t+_Xr/ ^.֕Zi?p\.gMXYSSURwtm qZRW (2V;H1)p{ĚvQ<*x5J)uG|,rVӛy>"ALeN7cdzdDŒqEKPی~P_6("' ~ Mo*+dC5b_t54[u8V~5 ̧ k7Rg!;mcԤpcπP|K`jĒ"s^H݃HT0r׵Ʈ ہEE rY=hU[T N-8&M9W-T-[Vߟ~[p$Iw'Tw%|#p{(!L. ~A`& ;e N37oPQC GÂk؊{Pkz!$VGCꄳgi$|Rl2ejy-5Px3s E˦N8EFeō#qO=ZMJe΋sbRb7ywC[(#q0!~#5dm|6$4k?#m[[2i^)o<6?TVs<)lG;w:-)$e%T#킧A6[$ ['L@[1lݜ3sEv>bYCixfEIw<ߢ~ R{0_cd:G&5ՏZצ73N~aLȜ>,8a#Pr|f<ľK^QX54 4Q7֞`t*ůhGvK߬Ⱦѝu$0(xT:u;$xWIIe+89wM P|ָ\*];M 6j3?1L)t_X}vݒkLnCE7tXN:]+ĞYzYW>K=E왕J#*#&Ayx"cr#:@,] /GkBrN}YRrG&yt()p}VaZcb{R$ |1"0~J:KȱU"E=EE b1Jn#Ɓeߩ8LYbql05|'^W2UJaޒ/=rLLApjeaSt?gy]с0&Ln f>rcQ4x]5]8k^_ Otb,t,4h9)XX} jg,DՌJiiAwyN 4%8|Aכb:ƘSx':1;#1ig*}9u#&;L}5-k{ [H]ҴcRXs頑KxXO19XSTq^vMTY`׃&횾TT]SjzYe7;F[.ݢd[Vb,%%\NFl.eHb Ee>XaS@]`Ѥ#˾q*. vdoW_6%p:P*}׍ggp ̢0C#sbcbZ&665бi6X; O-P$O6Vf0?`fU7V8kJ)q2-2cO[ڂ*6s.6v +WoN_oO NIo銌ža$(8yZOF' L/c:O7T5AxT+E2ڗ -}XOran l嚔bH lKt E .UsaXsAG ? Qe$0I 5U@JD|U3RcW̧_PԴU7=UpB]:1>]ĺU_d %w:5ZYw~z׸\5(aX7:“X; ZӮ .9m)-q'LB[_%8V6omr$ի0ܫC2˼fP~W{"9qAx?fθ1ꂈP6bY|m|v=g쥷!MQs Pӯ?#8'i| K?0^-9D=AQT',"ᶽS~r`{")nڏz0E.~ g$_kcűq8{Iߵ9Rr{Wq}nVLF E% )?<_ޖ</<5L+ObޖvrشF]jXI4X<]D^!kyh`_y6G9kC-dd7L+jQZVיJ*# hĞԉ WK7XXNiˊ~6uRSQiR^})^ N | K+pI{U/N{hDH=v)L$-Ɠ(KisIE(@e8;ģX*RJ! >[՝#5$R.)e;uj𫵩j^oWF]C.DQ]YpQge.$Nwu!in?ZvTV˕`1 'Pmq-1 yXe!^=D=d[u߀ 3$L/K^ \Epofx.dBL\38@6?]cnv(`!~@*yo~84mg|`+>&I {8QX>Mc"fx?kX yvgapOB|b5RzRJ0|_R͓A"W Pi$a~ I챤^ |{,SiʛNK+W;oH¼&gPCj޼URG!xZꊾ*y7'J> ^uJ)/NR>32_~MpBMzYKqZI;M Nv~=rKO9 Y G[m*0%5 fOZ1>6IH\Dz\6A*;;6mw 8&RfܮAq" :'"ǏC&|NUHMݖ{e/;'*$ˁo6 -n^Z$ہ`2z!%߯P v+\#I_-xW՚cw$\p1bnQ(֭%˵ /olTx6MR'>x2>.Y.J., $=Ѧ3@$ׇ;πFYohO:"fVC4@"SE亀?Y.%/ē&8EH'x$݃qLW.X"ut$v 7h3R݈44ۀ׃_~Dmߥ^c/P7w֦|DF%LF%{uH%ߦ05H{MF)G'O*+eNV&p)dU>xO(Q`\溸%?A<(.$d%:x,j4yJf1\ I\@YjH*I |>;"} Y ۑ7h {{M;U>K omJe%S#Q8W?7۾P;#'qg_06p=)9I \_IJ"`/ϐPr׀QBKעׁkEyV-9\ -sbslEϻIT7(I3t~N RM_/+3sݡP}-/f{er[sh|k+ēxkx-3mpI}ۣ "8lrۇ=ˍ"q3%w8 >\"6M"/y2CD{΃Dy%)yPr]vԤ;JēxVQ9 w2K\ .6Ly"#7=Έgn eǽ7x64Kfg2 < ~x} `^?.|Ҍ ~m}GT ._5RuE?̬p)4 |\jm4l=p#`gԻ9%l#亀a9%|$ޑMgm|&zy<^.HLɒˁ7ߠG\'yoڧPrG(WetKy*!"y  ۔4!G#'qCvQ9͸w&zꓳF&wP6yGʢmm&YN&$J{({#&:r< ~2yDA^.e6痙h1!cTyry$!: 7$P)໓M6 3p@>|Xk5J>Ay\I/ j)竊k)GTu 6w5}Nj<-y#\;D;diZN[lC.Z^ԪP`e0DPmŸ7:t;Nh8-ٺb#6oǣ끃W:._^ <~(&(z8"agQȝhX$[Wxcr;raZ sÙܢYilqhE Q;il~Mce^lm[!qW@a_ʙe :*_Pa~9I ׃_MPP7;L7*-<~PYo2:9< ͖&\TkXAY r- &nzw 0 Ywqφ=qlXOt?m:sXU)  kY͚qEIJ)0}iSM#5:j}V<-ec+<}5iuV(]чzqgR m2rig2A@uT,w?%xZgTӂ*|:cU`IJ;.8a]\p\~_pBE=.lc8$#8B;YY&)l73#YU78- EA<Ų`f_o,:cb1hc8K[-8POɭ1Xfo3 FFZ-oAPx Z9UرK` M @T өΚAi-@H͉rcg33Zkk =&*<}je,#T頦!uȡL!39`"3vg mnQwe6hx?_܄;O5Ƀh;Y=~ r_k-͗!aTg"XkCg5]6fѮX.H V9Mw&(>ߙwhlr^ ~quniC7 3/d{P_ Xĺ#@U7 7oV=MK_i{*QFl2ړm"`vAЂ=7!&a<;[oAvuB?Lu;H;@m#CZ#)bnF&ilױ )-Є5L,,QS-A;o#uA !{ Zhv;ց2kq=w!0tˌ=P Fʊ/Cb-[_Es!Lx 5ҍSH@خtcBD^}! M">1&F+_\@lE M71p ;tkY_NCqIJD:O[h$i 7o@5>PSw%L &6􋬲G3/fYFIQfCB] 1.ۓ[kH@؆>˓\7Y6VMN61X>~.J>Ay Gm솗UG7&M<5RsҮ1outcU׊}/Fߘ5Ŗ^(;L3,D_ZZ4݁# Y 5uW5Jg FӅ{'e/f {Se^}8lM̆W/^٠~.7L{X/6MZZc h5MZD|Va6yDuX GNeafld9aFmi*hL ͢m*vuw=MU1+F]֭{c_+-iCTbsNkY-,~p[4HX EZtt߉TOV=qMwjFKu#G,Qz޸ZG b`9Mỳ!Ɵgs`n(~lT1/7O\opkgM£X3XZWG>=|D:Z\5M5MV;wԪYORǬ]$~F=$RxI/&ya3ghCM9Lxojb> :rsGֱR )xwXɝ,Q$8P-YfCO]L0Mr 9!\^6%Z6~'Zw!wߥZg(.g_`s']G ]l~2lK ~6Vs}dcS.SDч2 l2}yԕâvaVHG\ĤT"DŽZ!.ґo ɦx$_Y}02-M:_P֥9d*[pd]4j"Z0fpۚ{Z3c Nx2S_6Bͥ~m@m0 JΧz(R/\7KP ק9`ۮyktz>7«RgÌPI<"b w&W\b|ѐ1we2}7L3O(Tz-e3g iaw7t}Oo[@vPlBi] ĚVYZà2MPC.  15jsKiwtH$JIZhojݎzV*{8zSY_`Rt@H^IeēQՕг(xKk_و)Hd/o=I rbw.|I\ X=aIїʈ>C_>C;"k9hR[D,ߗsg5j)y^P;YVjW; u0KFGTuQ\gC3_>֮ 6h}˞8lQJ;ㅡk_U={(2$Y%k-(ZVSS(WiB-_1ehUȲ5'DTTJFc\*6^',LgZM8V~:y%o2ѭ2 ei̒ iy)~jW/L|ug5${&Ww͂匎U2U u,sAD&Pece9;t#/ì19nS=`_eI۳ 'bC}P[/k]kw St8:M`\~8FJ]pYZrp3p7['={9'g,~˹ ӥNDD?!#zV-ڞdw$:]aYLR}%,3؏ne}=ui%*'"7k`<^\1E1DG:qh<ˁ_A-^ ~ wj|mq y p\I{ֵń7XAI|T0%|}FQZUҵC)i7n ~g2: .e m6Iuk4T}}n'l)S/#> d2E4 b,t_Fg"ةbũvesP&eI{RLTUFb, QgƸیcJt F6*lR.AqF6B`dڞb[5JxV$YKSj|&jHӪ V"$._"m*gkixDx˳]ZD]&){j!.ˠj=!6ŵ$˭CRj<{H?2* xVRɝ{IF%G߫9bSZN j+S@IF-^QVboV^A[m kbwRvó,n2w=g5L_Fu &7 Lo$7DnDʈ.v 7Qȉ ZKu{߂9q+UTXѝwj?%;++jGVt6K+!iCAGg1̤!piml (hߠӻX rV7x|:ܽB|SI|Ű&wONBRpi JHģWFg#poI_)ـq+ˏ(oؼZp9:JKu Gb7Q]_ 75hAе5+PT^bX܍}/n]:#1E&Sdx•)qܒp!-݅+QYK-ERep@xĩ67;7Tk3qUl){N1cq%* n.54 !Dg")ԏbOK4\ٴڏģk , f1rw==$``KcW#ԱJu+uLXA7i pB~[ <{\p5o=XAng f)>U&bE`>Lm8"d_r9c/k-{V"_-,5U$^u SMcHp#6ߩB'k!P"RLc.bîz,O aRfC:4uT҂wmP$& l;;Oo;|ҥd: Ѷ 8` ?ꛧYE/{N؃͙ Z'T,ټ95c$]`2d]Hv`J;x:"Ϊ? ŵ$2jpۡZJnAk@Y9 #qI(vȅl>_CiϺ?XBT$zU'︮U2*./ bp2}=%1y*,t#l?fO)/7ī.(*˔d &IhI?;fyq!s )9 Շ/W(*pBpB]t^})< |I(e2 "dx95jR̬тV^|Zp$8*jeuV=hZ΋=z#J7 pLޘǚ7v%H+wTyqe׀,8avE.9㎣%H.D&r*tPD e%Y ^ ǟB?x]L=قgb}h6dkfN-cUJ'Y=P(|2Mu=58(8au~:L:XhX$[W̗f :-VqJ/XfnDR? !k(d0 OpBEh`CSa46ōeJyTwSt0`Noɤbq̙> "5<('"ϩ=%{~`'PC8SClp9ԆxF[\.Hj!1VFbUVg-T "ohqΰ q&Kn}G[: |fnmB6X[2xk&w#QzӦY2HOr[Je aBUR$.u-=e~(/q-WK_Tq=M>: ke9y[Jk0ox_YaZaH*BlS1~J<-թ܌af{cq k㮚:\T9,Fp Pxjj@lķ>FQ/8&|EIwG&&UEIe wkERwKrYZsHK#5g3jf-5gq5ƭ(% V6Gp [Kd {r2$y> A]Au8^PJ$zItQd#>.E2z:OsΠGVt2)#HWmzZ[23bǟ& HFa&o*+lP؁ZyԬfD}±&5Jc |6DsFtI(#Y**u `6peYTZ:j)I\&8aJZb 7U&-ڴ5ȑQյoHFUwV7R* WJKB?Rn?!?O)̪M7I3 IF)s̨Ѝm2z5R,+w=|\j٣Y9n!.I4YgQkρ?!<|+[)%o튜R~~pϠ?e\ڬI'>k.ϖQ7$)bq-"\1i, mOd I r9]{RQg-PկTc_q]J_8=Wm^ V* GOO&EWIMq$jZnF7őWFDRFt9ٛⴊha`%[WƵ[z U-s@ P6AZ`XV$$tx]ڴ7>-#X/&%𒲎[$!@\.S,$CK*/WV,+W_ sԸ=n^|yela8κhdi|\][6]·\9g~U~}'oeU.$Ma{JA[I-<4ؕDPmz87H4@<2o .IH3@࿚~]chtH? s y>]MFS~ʚ#m&fYm1[{et?LFGu c߁6 +!j$? $oxZjb4<-JrDV(E-^*8&J'i&\'8^c,/#9gzbt?8}u'VoBr^Kw0[/NX5e!|EB=_~K>Qq 0W&'PQO ;%\ӾeָNk>%ϿHkZ̆%cHh2x h2s1j 5Yh& -xF_{^g"fNN#W ̒ů }cG#xG}-=aIc#v>nb]1{uW^] EX.Şwg觱+oU-DʙvҟSvH3,ض&,e8Pr火x@",IRR1kfOY3{";xHe)[XղոEmXr,Ku< ,Rr%Cꋹ8a}y4"<ؘb]vtdc}z NRrIRFJK.-xaۅ $S~fn).S5fyϩv1J]*Vq~v y?xmu{ ~Т 8#>v.GpbcS\ӟ0ip!Y^-/[ -;mwI _&J-^ ~{Rtmn!d:W?nP_u8k\OWnY Yњ(ٯGX#xoOSt݄$Ҭ^|*6z,l䂀(D.pl8I+mTθ%1ںD6 *0S(Qӧmi g@v NK;eVcO0/Zp$5"(./RPNhNEp$t$ u2G*,jKw/ k$ͻ0 ż! ,;FL| 5)igxT`'D+' ^`˨3 PglX˳p<,gl3N2+ N؉@ `s0f7 N^n<#"4io Oh2~yvX.:;Iut3Y vghtH*?>`<_7Cy23 1'6 }2:I %t$8m."G.YdtJouJ&ךi ֟3:%vX?hV*Jlʦl~[`׵K]sߵ_(: 7驵fU-kxn 67Nx.(;'LBvS.du0LA40Nɨ]o N2ˮϐ<O ᷀.8/6.`ϷGH]pBE cd*⹱׹!_I _L- fkzI+}<r;ϊna*F#|5SF૚M:4-i"˼7EVmQAjX5}Iek!Ø=1=xnqbcy|<0I\Er&|{,2Lcĩ ; B?@,\ݰrc9·p$Jf N .Z$^3CY";MSWd}: s[M7r}/YcKָ­bN?g{tU f/}eX OaJSj̬uʣ2 / NDE4+vbbD*jRJN;5-8b'|B-Ɉ5;1Q0;Dd=scWSFk;PA@]xex1G7- ?c1qSWvb'i}U &]ُ Yq 0j G5߬u Y DZA dל '(bd8-ՔwzB #c٬?!8G5g`7maڬ~WpF_uǬnI?P(8G5߷@c9QKww@$O[p (GpjJ۸@2+>߮w NVyϖ:_WC6сdБsn=CpBm|`wo0 $81s"'Ԥa$C'T:->t¼Jz7%4wvTsa$|mwWR|XG | gI'.8gg}ݮrZҩ_VE '!&:uf)yg#pTSr9N\U#4pV0ZrDϲ߾ M!˾8xTK6͓'ۘ+agU૔7;1 dM9>-m_YK*!Sl!@0K/UBn$!W£"p!MS]8\a/9nɤ;-%y,QQc1:$bG._ɜϐ +lD~[\ ZY=ûs.wRndt3\>uYH71iיi6g/!jTYQdTw8>+E0nJFe'rѧ)zmkyh͆-=ck[^{|-qEf)$-zcMs+F塆4pT4 %?d0C%zA/=BEkIVdyTO 9Q~phu)}j t2d=}WZ8nh /<-J,Q)iW >^R#>a\tH=Bim$[W̋Ů5jV~9PB()iHo5OupDPzis5$;:[w JaU9Q, b\ 1fL1/LӕJJ]JWoT)s OUOl2/XƤK2Y1p9@$@LFuǁ##ʪ*}[zXfsfQFwy"H6oc2z{&7)me-xrθ=r5Da -OK Kk#O ~zQqm_17[oBؕI̧/6?~2.?Pky9gއ9' 7bƨ5Rp 鴱!v2_LoM2}=F Ptz&;Χ7 : ֹn.8rG:qB?K, . |HTdr/PQSh..+_/*?2$zia K*=OuKsE8J"%gϖN~A'!A==nqw=opmOfywv}Lm"Z .1P>Igp?xQr(kPkrPrD|# :銐 _.on>aucFնr"N F?'Y6p諂idf-EdzH|ܙQWeӝkyh`dtָIZ!)JKoL[5/ed%p|>/ bOJFy`Δ)>ݨ3)<\0'@׶xU1c{5y+kXeZgU&oaNلUh&v@%S+8a{O觱a'>Ѷ8 =aoHn| <ɳ t`&G-^~r=-+}҃|6Ƕ!Q׃OF5WՏ }]24O+;%w Zy4 owg&Al`=3%9ki< ~1 (/OF;SWBP 6;'DUJ9_AC jf\lj@~xg)Ë{kODɯa$8ԭq:)~98@ˀ :N>ivVΒ/wJw"a9\m-% PVaWY-L-NhmZPeq+\-E^S6h=`3G--)W(`']7a!?\nr]:g"r@<иX1]1ngLy8$`$:D+xb(s3&)NbPbfzasNdE\H+ׂ_.^~V).{Ԣ t=xO>0w RNO&x\n)iEJ~*xL Mf.Qw)`DJ>A<>3r9lGsMRUq. RٽN?reT1TSYp WUʆ"⑈sS"ǏR໵E5 u ;/Qr{'O(+wjKyf=E˧ EP'SaTS$gmMɽsj\3nt^~/UiQ)|Y}?3_ќq 4ٛrl;?>rsn7Ec_fx谱 4{ʟ26jvd//(٢t~gW,NyGo?k ౧6挘%s?Ji7HLprQtŽQxv3 sj <;N۶mۑIzdz '& ߺabJw`P3\H,qsGo49?dwQgЕA)u |t\PDH͟fbo3wmƸWm쭜 LٙgMVPwJb68n%\l h !J:hM#Jbzc!~ar!2ēzJSjUšIyģ8pKbJ*2s"#"XɃ c_"ED_$# 9h̦-5T~iNmhMuvRsΛŢDhF6*9co|J< ~2ޖ혹M^3R&p|$^"tYQ@ꄳgi۹H: O7Jn6p"ekT+ֈ5$?rOxqx,︧#>&lD .F&K AN$GU[%W(4DfUd'"iL )BJmqѦ% \ TWwrg1R=Lo5%>t'bM qeB`k*չÞeEi1+R{3R;liģX,K o͢S.XŚ&/plZ1Sk1Znb͎IKe\P͋b4' W9/׮lA7{IbDClx[cEH'ZL1\Z!2[\^Ur/ Y6BF~~eyo*!4v3QOQGW:>Q,e9 nHUZc0Lz NcSLHk3=a\)>ԳlX$0'G9+;geXVFlG 6Y $4Bnp]xkS}BDy,H;@=5. ,YRˡj'-h7EY@v]໔q1]<"EMwZ~8`kL|e ,~퐺&k5ms3Pq 6@0݁Q~A7/7O\op^ش9eppphd, hߗ fV.PAK01TXbRy>~|V.74zKth*Ӛc7F|8 rE?nܽS`^MsI2]/|s?pCM pt>tt߉TMS3lmYgAad &iP#'i1P6tcšoq#x1EAxE+of 8 wiIk$x#eMCEY*="GT$[IRev.uKRug'0L8dKeD?Em2ha؊-Yó|_"DMT૥izҳ6A.Noj5h'.$ ׫*)ڮzm7w hګ䋦٣v^le1p>Owʾ>;g|P&"|>屲~def+o(ZN+h ?Ye}W-)V}+\t˟,M l LV:TLK'RqXVFεZ~kV2(09 %,|*WܶfV"\VOzmjumY?_Ӯmp݆RgV4ɍLWi$+?Ɋ\W,LHDD=ՓШ??K~,0WO3{w&29ộv1̊Bh#Vq *2ILivE0ypYcjŮ*ۧ*ZNSrhfd!ںЖ:ߢe؟EXDa+M8uBeАwV-+ suE[,,p%v؏MSن$ug&%:#UuEr OٯV\ºH۟2úa!f=5%Lר1*_2D^sQz!؀:b^Ճ+ [<8Fj¶8$Uъ w ]FLCPp)i7U4Mm[euGzH[$m{Ř ~NPh9˜iIxGrIи.lfp4Ƭ2FuŸ3IEI+π&kh?!`<^\1E6ov\_?qKx "rzc,JXǸ/˦Y.]Ҋ;שׂvJ*ģXpKC3dբrW+J._,֊`v:<lі@+E[5}o# -^txÒ !3NJ-LMIʷ2~|颻VTnMcȖЉfl9!5,Fe^w; #-CG$[z d] 5˶Tz4j6j`h*^)[k^i@> gj5rU-A/̗IjBt G,ڴ#)]4P_${JHQ0a#'_.Htz r\ˍ߭Dbfv $8i +%o5ۇI^hDRX0\>jdЋ(]|CUrDbd #C#HJB4/i:#1E?x̤]]ӒlgV$B*]!?McOȫ#]v\h9h1+-ٺO49ܸ+$z<]6Zکnn?۞^~ U輘UtbGL[܁MvM3}7@g"( D?^b2wMJH\.յp*XA< 0K?o#EaF өjCl!#UUdn2ʻ{ʻ3[?LŘv:b~)[>ЦqANY(ͩNg$.INWUfPzc6o9:A9Uߣ$CV!C^a-1+ۀGhUn) dTrx/*#"BI$^ЪRj9tdb+e-fq1] "F qҏDR[ڊLxϳiƞ,=:ץ3@_+0|ѿ+#Y~h UD c%ٺ]d, *_uPTG|&lE1lMsX0[p|@V E&$0זƝj`)˨ͼ3ֿAA]'oy4^$rGnJ?!;HxD7DJ9Z8o}*H^ɋ\Z |Fu$"}Ң{ՖL[Tׂ]*YDF:ڥRvK\c#!\pqJX,xu6֬Syݴ>V\kN_pyqnDe z(LerRrR/X.)AHqZb B40ETr|eq~~.J߃IV"gqƋR"4vqjdMuԌSvʌ_b.N3c}[ftDbK3:"1Gfغ3vrm|M}t=* n&?LZDfoPiemlpr8|+[.nC&"yL~L8+$D˦E-YaST-Jcw:RvpC&{ՇMsTuFb,ax fę£ ٩a)s2w6k7Io"qD&%g''u?7IB7?ziIWd)t^x0”=F= |7ϑ-~ԡ~DS.ؾ~n^-|=eR}XA?E dk\ /bKtmbd*n-?&3u.\f\<%qFo(T7-/((/d2[XJ/אhp%[W̫xpo^vi[=\V2ԟHuo3Tzp0ξu~' {KcVq~BkY9Y\jy< sp"J|`XbG=p?nMP? `MdS֦YtrGr]O\sRA?c pG n2 zb'IK=9 R_L0އS)L;Փ߆b-Q2n;8:JR,x:܋I0^* ^E~yĩu)>^kyNsH:FhKtgn5G]3]hK)84qz SG2j .mbACm_*-[E!2aUyf~|o?zCimEĞ,dLV&IhZ%[W!E dp%6OhA. )q!> h2:+Lߌcq*\ %5cV̫O?f^ |#;7 fmYl?7ڍT6ߟj:?!})Y=Mg?j> U#d<+_Ѧ.߉=&I~Mo&-ei,? T`zأsO/ext'|Һ!I7 N;bL%HYT~Hد`Ẑф|.Af(L$-(ZJcѱHqψ)S$FoIpv$ :olю)/K_ }J)rƌJx5(R`KUš#GTd'RL78,`#1 #r^ė* Ʀn*-8.g}g>ulmW,Pɜ'y7 mJT&N''o)9 8 ~~w,Z>E0\kԬ: |7B!O`ĭ@U {std>YqU:?,J.fx[\NO6CVibHi+FmW!C&azanpM㎢QGG-nuR@I8$m'Bt HςK]1>.uzAkgEF2yt,,,(+&<3NS>w0^~ igUv#,="%ZUN"HxZ&zmPD%:G.;uuˣ. Ts3Tso74uuڬyh |NΘE3'TTI>TݭP߁&Mh{N{< +{Y)[;LWoUK5WՄĞ;j*ݹølI'E % *JU'̋ sw (._l(""ѓ*rjKxws{ibEJ,vrd0wK q4#I6})5%M I)ٝ{' y"=Q6DA&+=i>biaG%[ʟ64&ԸzE?y26}}- 'i[eeV2RҰ% <MpS:pKc7*] Ҧb7g[|JoVѶT6AA%1<@zJcwqIo*U~:ȓX!4ڀY91#AnQr;{t !Ө!orF<.7N~G8C/\[в^)$Q#hӜrR.J|= |Ie-)SX$?ko6-/%y 0Q;.USHH_~wUt X,cN_)g +7&Q GJq}}*=t|N)O%wo^rqu:p{$+_ w5$koc2zMoRÑv kMXn0dB EӚ2?=1a}m8`FYY~ݫQ,~@MBmVdZ \+8a"xbN8ɨ'\'xZnvA"IMp$n\!`黩1S (ASj9n=}zWܬE* N4)j]jgU$X^A<2b~oI)&NJ009CnhSlq^ ń :tCb3NF/RnJ͓DOb<POi,i)#}&bq W'o[ \JY;wtSK#5I׀K]_/\?M2\ *: ATO_'W7õ݀OHLl6MG! Q*xUY 3/;o邘Q†1}4`Yl>b0\&& >ڸ]$?Axrc PH-uk1%0/M&p*YL24o^EA厰h!i<Uu8,83פ I!;ƻF`U:Ajw"Nesj <;N۶mۑIzdz 3+^30~5Yܰe-2/k'5$w!TA0k|t9Fk"#5It hUF#_ ws;66SϦdDl a1JS5ٰյS^[=J bHTIE;fЩ2kZź!9U{EzE?]rզjg\[36wGͿQ4a{*z͆S X#bJ9RHi}E35d+_#Dzf.;il@~%Z @أ!.bH:@Ç]ScL^p5 %6sDO1w5L;keCYch=Ƒ`--{,2ۆc#|LˋA|̲{2%."Mh!ReEiG?|͡?A3_ŝNfL|P$oM"Dwf;^RIXLo,/Q.$R&x$%- ZTm:h,YVY}d)cqCC=JOgfpáGlWqgEXDK o͢S.XQh)Qa~c& us%|3"\.`SӭiƸ{4Y|ay.k N0[@f­[n4d.eb[[6 ؔ52#2_< K;2㷪g!pXZ 0x]-sST*~!ϧ:kzފczv53]pB!XG| ߥ@ %` 1 ; 5>5tFUt`| a YׂW[wAdkdnbKːPej⍻~b^~QL"ޝ2-ȋwCn;z 2nZAy=p5je7zBp#FewLK Eu>M|GNk|Ic-%uV>^=vRE:"x|? ӽr_r i? WAXbK!H@QcmfP]G\i>cW!6[#Pm]t%ZJ̊._ܯ!BIcdyߧ#I/_>/%8xJ:4JIx]F7m me?O.ɚCrM[g T\{fFby~~B~5c[6Mly``hޥ#~r牛6m<>pڴ9eppphd"(%8hbACM'6'hѨƬVu%9kia<59wo9l )a.I녛/~ՆlQ>|y?|~5&%P!FQi({Z3ft.Z3r˽tx <+jT#!6\.UDMS3PYAyd&iPDj2tAQ,7)+,(p)Rķbۅfrҕd6*Hx$+Ūг(xKk_[!X.iAhĝkyh`$vb7fqZ3FƗfVrN`zj9UoTK#/}tsw\䉈ha%-ٺ❗5<%B1EY ZJfUuYmEo; vp|(=]AUOjp3P|θ/HotF @qȤ>\ 1ǏPj.lA1rmY{ N5]Y!LãIB:Adko eI d5]@7+,VB%OYsWXdfqqY¥-;U\''.oh(%_,ʈneP2O%VR%c6_(Z(HxHYc.Cormdʺ Xq+7%6ky1l_D '\YY[RI(0ň{4<߬=8^|m[\y 5߱OόLwn$'ߕɉ\w4 HDDKu'Y{||/;;xR3]]݉ܥ 9i]sk ?rvQ(d'xEҺI@5Q΃D絿v̵rnۆ?5.wkgM!'RMH6Ɇd dQ ht$[WȽ-hBC1EO6<*$T?4\JB39:&޼LxR!{ (O |\7om>\feeIh AiY֥;M{OgчeDOvZWȉ mOKu;§o̲ar87ISe|-F2LyLq^rܙV0CU=j[V4t<~Bo-*R9yb4b6ޥ+i@,^~Q%x148;0 w pi-?ZbmH\A%J! aU&,w6\ֹK %Z@\.S+.J㿦.o$i;͕DRX0\o_B Pr8Q,.Nb q\ / 5[UikCQT'u]bo+ |5:)9>  3ղ?MI355t;N;a]w)khC5SZ uoHແO?l R}%`~C}(![p"lHģ:$,'ؘsS0B!iћP_(#WNY,!je %._"mۖ*- PtMGϵu%a0 B+I5 Ucۯ\MK6 [ ]\.};%g1n頿DWI?\X84nwI2]p7=eN88Gb'Ц1q8IgՓgUջyƩ3JU>M|vQr!;i{V\Mu#_Ҋ ? <'0}8雀'Lw NXqnD'.[uyM>-8.bbN PPQ=֎Gh藁MpBM:O6~  Pѯ?+8LF)ahRʜY.;q@,_4B{JPQ#kLIg ^)$8ah@9NM!PI| a0jB_mLKB\U#GQa'iSarv绠PzK.h}/WVLo ,sik-Ihmp3sdxN2k%-[oQV]!KiB9X;AK S,+R-&Z1HQo~J9GR}ykT=J/!Z\??-O"1{oDFI|&P CvIK+JH\ .uDr2qa(*q gtsTY k ҫ|,PO&NOo@!|{n$π'0K=|\}7b6yoaUUݘꨅ3씅݄bؔj$=GUiCYq,v /g`#7CՄ{h3YdxP!%xR̒תRZ~_2ZhZY,{D] 3Wen*C]%`mf/2GQci-"ֶqP ޸S-2+7 Ih+'U˶R)Pj[`eRr⒥ԈUG ࿂%GIFOw N >¿!ٹo ̵J}e][4 m1<{o*նU NxO?#x(~VpBUg8{HB$ Cv MvjHBg"ة!ɵ(kS#%.:̩gz/PeD_ bm sߌz9QKσKAo\ԁ%o3jd4.'?m'?~;Ou3 Ű+iQ I\R3cY(l٢\,@ .ԜO[5 _r!tjPb ٽF$hդl$mlsa NR> 6ݨ{L (1SOu[&QW;nDSZd'Py$.Jl$ƊQT[W߽ZV iiHy%)L%7 \jFKUp*m#ь\*M|ͬ_;iasՅحN*{?exyBU虥J^Bl79l] |\ʽׇFc}%`P 7:=^>#!^P}zXD M= |+[kA>DzAx %7*&bU, B ޥ-5mY}ZjJ>NY[P :k$.NNeTuFb, E SXc(@X̷BCCoiulKۏz`ZÁ4('!\PWu E"|y Ǐ_f>2^­[/+Lwx % xrg܀kK-^=@VECDYi~_M[=Ecޞ> .0^|#l}}B2 Ԏ .ޞ[:i$7iƠ3"OFی\䉈ha$[߱{JY϶'g|C|&$p=@u?3cYߘ xOsU<(/ ?4Xu>ݹ4_o k[hONg"ةvv]KuOz|^x5;O.OajkV܉<+1}j`htH=o9nwVnh':NOVةvz'ζ qwu.Nu|XA<n`],C.{=#Na0R3h R߃=)Z.B#s79$J X$1(yQNURG]$(ڴz)=|u4Gho6,ӬL#y;8;j9HF3~#h]_5e汅%߀a>Qǀ_bzѨXMjZBg2T)O5*MД3em Eqי,ũM9Uو{21:;HۿLNI|ے(5ǭ&'TVz;: 5)en%G'LB; N#n+ʒ tI2ڂjRܴ#REŎU մ7Y~Ea 'I j+4gD vjDR{tg$t̼W_+侔tޗąZZVģU_#NW/#F♨EWIA0t5pmJ2A0ʈWʈ.7_){LȉFU[u{w{\˯tryTRz: !^w4 ޸ v=6ےQ6p)Go DB ~6%-B%}A$}x),4'+3 'qf߫䋦{(N<|I*}J 7<63D= v3zX9#+w?d4oK?DMK$'_6mѭQI&Jԛ0|Y;+ .5{Nnd\L`r 5@9sҡ ZeOTZ7k=֕eR:B`EK= s/ܫt{Vʛ|hg'(LHFUšO"GTi_-<^H}iubOhyی~h,Y.WaBq ^CMy+qG,ҲpY+OF@ @iclڜq{B* ?6DG?o(s:x ')j7؟Y YGar֭d/!qDWG'TԳl~"_pBMQ dzI( toBbQ"=MQ NJޞL&$8l=Z6KL5cJ2M9or=33hHY]2P2YKwZ\hڼyUQY*ZfcmݺM3'<z̟Bp$m_ NvO$_(xF ͙IG%8^VSXnhzٶ#G &=C3ˆ2($ >W[ Z \ *6}x%ʺږgR~B  F|w$R&8 ._#@QVA>8:|N*r?Y]b!*OүMo&~ [j1<{ƀo~ŅQa+F#nQ6;! ޛqg3oRDR% $HDJ.8 /+~ ONqBh=&r-4Jۣ$*WRrR׍*1Ix-ɌdHGApĤِ|O HۀW/CV !tX4c OKEh$x(%w'4ie\3{m]ɦ%p^f44! 3 {Kj6/_7݆k3.3f۱SAE`v ՎIPQˁ Bv'!yV[p4#xVnF6С1s}(y؆)t`B9C1H@x:$*`% lCRl^nɴ Ci6:$Nc4S4t(!` JH5trf'Oȡ?y9$۽@ \j,S>D: WوI@OnȒ4_~Р6iԧTLPA蘀^f WDUhHP.~ŝ8S}[`=?'bɻGIT)i[RQ:ʐpx@l$mg-0YZv]s<ur%K5#MWƌ.IEsR$iěWQr>KQ+نkRkTucV !ZAs9W쮖2 ̞0"L]|ѡ⣦U)ZWׁKujaW;Æ/L0WW C,_A[\=lAD6>7z>FlqU>/u6*?9szkڏ8pt[ߒUYeu16 e/JwHiwO%z0m6wi]jFa [X;bS9+z@Uӈ @e]<=+9}1fcZVLxu~EgSa~l6J;S-J1MK2QfW̢X9W4E7 `wyahBXC~ϥٿblhq<,VV">w=}7V3˥Gl{gKݵǿ`]_pwЩg}};Ddm{(_ {)n&71 +Zi 3.=M $?HA͖|XkVZ{Iϫý콂L6{5jK/K2kQK鈸`v=RoDMU=9'C#R橎5E?_JhWy5^aKFݲNZo_Jiv"梓쾙?mof?[8Z4َQs(L7vo,X5Wr#LY8?@&9}qJ410"gᱰ]G6SF;e{:Mjj-z{8ȭa)cF7G ]v!'v+^k 7B#omݽnao@-cQO+~027H? I2RGGoNcy7|71 /1Za%&\zE5 @ iZxY1Y^.:jě'[U1oÏ9tGŵ gO?N ZCɸo\.jYX:ݰ<91ilT:WG>]i`"rݣ^`vi絣kwt1{;^^7mJ1]mfΘcWݽ1>f!R va2%!ēPw [3-~J7V+ ()BDƀ%sl\|X773A('29_DȞjD!%~+,.]Eƻ^ߕZ ,xA宙fh(dCٮ dm jfIq_(cӶs.T ,%V"KF]ˮaјL$υPFјZ7CwzZ.vwM+u9eJ]=y͡Cq(Іj^c6 ghOĝRl"x11-wls'鈤K֩y"0BGQ%ӻtcU-QԋHdZca??|pi:y`-APxAYJپ-5 4ZY3J"ݬYϟp Fc㐙p|HYZ*P&{]y/XPsNHM|r5q¾ n֊Ղk>N!zۻRc#uj8snHDCT< ;;ԻTVau[HK}]%AP%JbD\]T 'i2D%{CMw>?hӝ$ ltgtPml;uH#ROeIJ0r24MvO!ҎZ$ f^o %|e&Q+sid;0Bkn˚Crw{9&l%6S{;v9_=;^tVR}#vaw wWtoEz\,&_S?m(Ϡnh+cJRU"aGB\j?p=T5Mu(F#ijJκt1-1|XC֟UC;Npv% Q97oNxWnֆ8oI_C5!ēP>UTšge(ʂ/YQ$)H ?:P:G 3 &0yMH523G"Zނ%;&W<7Is@˘tEX vp| =] CYOuu5V=z 6qk-7GKz*}̵58mmSk&[뮸Hv]_fȅ@8b78F4D- g.Kw&gW wRф?ax/'L`PTIndl"7H 俘2R>bI6\)ݢk `ΈrLUéٿۭEs)~qqYdyl56ɳ<5AɭnW_Ba4E^<~@ZqW\rՎ"GU!ģXpkCZYG+TBSVX}'$x?s=E[ qq<_-*H*ԱGRMA|'ʕLVn V'L/tFNoPn+z)>&z؂p[&ٷǖ |e 6u0 a u+ V@JtHN;YeįyWA/#el CZmEiɓ!DKqb?`!a`[kZ2d,]vWC,"E UE8e8c+:B"kW("Db*[MS]1Ze/ٴ\e#kX aJ>B< ђ!sn~bd\ko!hȸ҅-[TŹG<ӴrtA#](R/4[E)!\'p*PTuFb !E-5o%#vt6RWW+$ Fc!cțBo]v]$*r`X {`MtC| ґ}]H5oe5Vxi(\go7nȴr]H+uK -\Ū #1Vzu<cvG$S,z4J +ݘfx -)>>}\[Ўs7qf*V )i7>`:: |!e jMpg25e:Em챌~<˨/L[2J>ve(L+DzbMBNJңBUkBGՒ$/IM:_YM.=(r«n}WHɭnW_x 5٨^RovS=% azQ 7gZٳ-m: :WKmKTv3~GxKH}GQn]w)DQ5T2xx-o[ȋ22e(w_1eH/[oPr pk_rinʈ$< o$hnԗi<υ]~Cճ2HH .::qu(*Y" 7HF wKg8b06#I ?ힹ8~í)zێRb{Xjԫs!lWŰ3U|tUŪJۉ1U3צd<Č_x&fNvAՄ&%.$'ZRr'O*+Ui^3j)4uefA\'EŒY05iEBخ!+tY EXhRG9GQc-o%C;il34~^A;rO͕G'ҹgXugnރ!ρ?7R+e'IH"Q>#,&O? >WQ(i"R)ڕmUz1`4|5it^p.nL}﷾F]-vה|.eZ]wu$rO "1օf6+r9t+*.b"mZshInC^,?͢MQYmQ>^ %șFW$T&bO,YE(,/~2mo)\5G1ϴ<|OBNyGFb a2d=2=v2}.vͣ1-ǘ@sA;agueB6NO&fI86p |&%guĿsCB7O?}خ$ӏt tEK > gZ?ψL[9J>vswȴr[Z:*!_'p-ؒPGb!E-0-xļ$#iWn}G-W# ;DU%11,Qe4o:_3)k< zBUjXhnv ?cT0ĽUǴ/HRn> .5߈7QCPc% aư(Ùa%TFb a2knjokp2bzXŚ&toO-tmu)eO4h]4A{>2b?z6;Q}lWS=b8ҦGhȥL=(jtYSEf#zX9EFj0iIrn?0$h"0%L@S ]o./ӱ]HtQ4۴FC]M=-mڛwվNbBQҵMTu)Zkvg+r ׿jy(_^{Prb[$7N.Y<}f)Cb/49_D9?w&iO Jnp3xW\exMFኋJ)ej82Jx3(Z`Hb%t4v}pL}AυP~*{8#xKKs>~hR=vymQBjV[Siq_V w7u B݄g&M-R+ RHQn)kOb-=|{Ś*_2]s?a&imK/zB1N{'p-5BRtԪH[kF;#dnתgG Ɗeuy0y/N7N^ʞIN^deE}ΥkK q[(cO/ug_ n<{䢇3eaK=+g]Ή jDLK7!Jjj ߹U(]oWu3)_ זl򋙔PV&eҮDOXi׉$P \OF1D7Ei97i;!\|r]/VYʃ-s}Q^~IpBJuEA;jL7׀$8aB[^dC|] ׁ)8"PM2 N#5O`js/<<9S-FL  /qICi)m׿-֗ @k>Ô;k݁of] $ 㓉uk'R[i}U'L| +*֑|F9iGBS_~U\r{,p^>k9YI%b0)}Kp„bqͫVp4m N`WD`}%RPx%d:D;4[2f,XNY(fJ׮9E,))/p `V>*Sɬ/# qNܖ \,ru?9[/:X٨u! R{ %|^l:SۀrړXaÉoQk\FOǀ'O#'tV9~34Vuvmi]h˧䟧j YG.PgĎ,%<UV1H(L. Cnz(l;5+ Tzٵ2k #O /LP;t$[VmUqcY6(:mG+:;H]L^ ^pXcN>-''Lyb<AYy*d$Q}I%OhLPBтJ{/{oÿM$筱ϔ|.x$H>'j^;pٌ xKGBV-!N\ʇ,:昸#xn(-^^mgWhCnnHl:,eOEf+C(#dEȩFĈX;wEV(!̎O¢ylß/{d5ke[+Njla=7ٖ0|m"TW|'WD_)#z6"Zlb6ԱFB["v$zN t/Ζ8'/xl:z(z"eJB v߁Dy"h$ #2YQd$&*|я$ln(ZéKX{IGQi(xU-iUsnZ;nߨ\+j|V>OsQ;d_usğ8F4xk׬yZ g&v,o=yENTm2()\ %aD(oPXU . .)c?ȈE[I|(֫W(2miHw8nΡ1w.21ߣ!g]7fFɏ'X}Q 3o&qo#%7_YkC69y<##iIhAnSzS EqDL],Te/ebs?~;(zAh.u.f_~ ʚ]UGZ$!I S?MGC 75H&%߀Nrza#kױ4Tz3_^XRܢHGefiY!E{6>ƸMA\{G9_ Nu'@,}Fno"yX`(vӞnku?gW>Uv"wi/Ld'!axWb-{hcRrES|.Jb()XU.QxGV"f<Mf6rHہwx`{&$!Q d@F%'L39p7ae)g!ISJLsK$;ρKꍯσ?ó-: O -2ˉiIޓ uQǁQzw;H&o?ׄLf?2VK\YSWhV&Rm#Gھ@?O <~FY2.gF&:t8o),Rr՗V 5Ɣ#kop|l4۔Qlʽ`nYy,=JQ' .T2w{ ө?GV,.hg\)?H\cd ڌEEp j_uiG/=`"@\/Y2pyèy眾1 ].3R. ӞM2Be #RXfۑZ p|@ݴm8QtMP /㐒0ŗ/( O@,-[.0zIYi(PJ>B6^}f+uy#p&:|NyK35kAp fb:\'2j=p/5>הՈ{lu .Ra6AzB'"|3|zglW!VyFnKJjJ`: :Hf=g{R9Yٛu IYE^ :Nw NJ{'TTmRL l*!I ۼ&D|Dp4Tu/M*jXt=كCBg0!eJ/ȓ8/?#8a0K V}/yy Ғ^.%o N~ۂ*ӏyw'L{-iY\Z[.91Iᖨ@P^Nlgx+ٔ\6۔uxpwHpgaǼu_xރ9܌}ej?LL ,;ne'?;F]ϱcs)'?hLqc3Շ 6vF~ ߵE;d(Y.o_F|O(LfM Vdp|F:a?el˟d4ؼA?(W_5n f&vk&yyb~ Ŀ*R=RJKqF#yQZXoh8~ K(? ה3sp ]3r%ii f:[X4GmiӴcקtLnN).V˭%zI 0!/7"՛[RSE NP蠕 5mDs!#Y/鯛C|ˊ򕍒p:TT ĸ*xuP;Pc/L;1WWPB,_[\D 4؀ejL]E^<~.1]uMSNѹ| [yV*r{nŴXd6*!~h+o)5n33Kx F)oC+zɿTا 0/ ]cM+eı-i)jZi}%_6_Memw NPMZղ=-e,! )ԣ컁/ NX'q@r |Ep˨g?ihU Nmk^6I>zʗhÙyg-ޠ}|(c_$8eu> SP{j`~e/ˬ͐ ݂lfZ' 2,[G'LC;{w NnNSbHf.mKT٨:>p6k&nWkz|,k׹CYVMsWsN r&omA{\yd\ 8e`H/w@%7lPQsJZ7zeUl(es8˺Tw ʯv& ^b\1}d cNLk5Qc,:yB*]@j Tf^e~ ئBJ>Bd3#, KBެZv$ƶIw!#c |U;w+5I4.֔]2ܺǁq[.̪U ~qD)b㲢]BY_|jZl?x_LF1pEbؕ'Ӫ1XC&3 4!Ҝm/4kRF BZ# B|[5/ Q7qFWUOHv*:B"(\RRV)٤<~d_La\`6R vy}HDk;ߥuoC0 :SH -f#07Jj3,HL+8aB\jk7i[gk-8Z9.;s9srvf\4 \*x.HC2sꑆzG0k3Q.]byKڸy}hUk-''L{ݢ̪5+؆-9''~Ƴi,2M7P;vV%q |Z)7 n갉/^t"=uy6t:u!~9ϥOUufUCQqjM3hyi"fO.~?ro ;?'g,.zϊBRއ5OHv]"9Q!)|\!8ab>0dW;pj Yk(]aSr ~{B6N} 5k"I{5eg(7'N&bOmgfHk,8r[4 PYD$ )Xt~=A~KhIrX{#KRA+kPQم_{cW&B< ђ?5l"ϷG}.ɬVvWbz+V-:fmWI&֤֯oRA[$Ć[y/FwmRm 'O'(9wa`+eb_B[@NS|Pn}s;u5v{/m.qk ډ=7(Fôu,pefڈQ+ ל\mԵ7m;ìr ]vͱ<5#^#)cyW67g^{A}(#ҷ9|;4zʸPufZs9ƾY.=g3pxxxt;[]=~l }=}{Y>пcwٛ7g~3n92 pwSq!<_?$:֢JY>3rϰ6yUw[ިףƳ6_/u22̮T풨jԲC?VēQEpSk^ˋ$Q/̚VHyYղ%nYw'U/ڴZCIRkknv͇7v-u ԏjmklǨ9nf;azc7R-K,+R&+F&~ LfkPub)&OV zpuo$|5^n:!GCȳ'G=v;vyn|5@_EL),^s슈(6}ؖF|̲Ɍ8UL ࿐`iTfJB'/eЁl!=U-X_XDHlQWD-)& o]~]k++ ZODTEZפ31 wls'鈤m] !UC{zwWY/V芼7R~(χ [`O\Ћ^̊9#p|PYJٔ-۵`sfjE$2.Ɩp|HYZ*a&{]y1_P#Ďp/^j"=)D&1K |Ė%H@ޡO SL*~! Q^δ81~njss%$::OBf7f)HM\:O+?? WƖO= &> oI/-g ar}憾t ,VXsP," 91=H)]! \*J|Kdз㤜"ƲO3#BI_!Nɯ !h>8'MWLFb@mfg鳅G0S_R5䚳VsJ)%q{bcohpb΁wWώ>߄{wīT|ȮgzGgĮ]XV/~12|eLЯs}/•Q;16V,n~ԶAC'A}e/>[48svJ.mЗr!hxD_KH2^1>{Y5wH8y_?-R}=Z]6[GZ*KcV;X[7<\J&EMQsetJ(%{Ug]CRe4ţzԛ n9Hp %!xX{wСC>zH\w8vf-@_~ O>⑬9UqGUazqȝ#ʵZv^Yߣ5KmG$ N;[Pr}GXP]'. z8 >WHGB?"#z3DEN0i{$PclPtPk@{ņ}o6aX4ָ Fyx8p>evL$Bi9 l8,ɟE'y7on}VoQ֐Fᑑa̙ &'\# Wb%JTg5kZ {-*C|h[zy"X3M$$E^d\/_Hܘfd|h@ Ӌ/ZHD\}+a+^-@vY7oMNͳ-:ȴ.Q5aQwxm 8:=w-ahj imHJp/ߔٮ|Zof#F9/^PtɊ)$ڜi+F%6g4զ]&J>B< ђ!bA5O;\BQLp@U_b,`$ V|;05n)p5jPT%SxSoh@Mc|'T.un~eλ GrAސ_HM!7Ɉ. '~9ˆXuH,ن}B3_QM?H7za:iiEz9t[<s,w3*=4\+T3i|.x\ASC1h!+-|BZ8\j@-VU*x֫5qbj5ܮF:hWZ1B$ [ZRr}}r+o+Ch}ܮYJxDbJͫ?NHYC;Zk( F4Ú2ۢA2XF? |֏eqK+X;(\52[8-+uWXB<]_/Qh5XEz9fv%)-{1] Zk[ߵQŤL[6J>vumP 2.v|uWnT}~dnhjg)CUiF-ꕤ%#.ta7߫\m6ǨéTOJl|"hI=<~>ݝ:zjzGEܥL>V 4~TF@ڝiJBخAГiW%Y'2X"1VMOKnCG./6PFv{7AQ7ȗg$񺽛B|bwtØպ!U4T2C|^)Kܕʏl%jAjEY_|.In 3I:Y&0 7A!V#Ob$+?֏ԋ]k(LBخQ8bgZ9 >)!e'psg2w$!ģgu4cix7Iy1 Wݽ|mNŴT8w'> l$p;bJ9G?޶cG/ZL[{XJ>v(LK}o :3~eqa2˸ /1azh-g#q7ol}F-nW_7%s5ZkS"~vBF+ۀ7ߔV47+k#¸^+{pZL:{KWJh>ZG/q+k~49e7U'/* g;Q׾r\-hɩ?]ۯ tS/VmIЖkd]pˌNN(Y)ĝ Am LowDNHȈ~IpŝHTT #H&ن]'}{{Oɶ_v٧|.OP Vڧ8+!\'p*i43cm(>aϏ)[(<\'kbo9- N|/=}5lok܉cZc財ƥSuׂKdI!ģAj^=#-2:p5d[\߼6GB5kJkeMUXь\0f6oSj@FA~:FcًAx#F;!j3m(\5A1ɴr0 H \.M%3$1օ·O_'[7"MQ6k<[a}KޙcZ4S!T IF4=hmè>k@uխ:#1ֆP lZF(%|Eg_\O f%B#u1fCp?0띊j]+c1sr۷z3m(\/݊b5tDNsjdGQKCMθ_t&vT۠P4 JiO-Q~T.WVƽN.(P2I8n~sa.rZG*>Jn9P&y %xsAdQSX[xxؿŵ)1?F9Ek[ ~cᷣ>\{ZHՖo:q t!v( ".K[T1?#%xԭnHM!ģXi>+ !Lˬ* k`K=lw_3 =8vNS uS(/=?VmZ;nLU}(*1'?9*WIW? '?s5Czϩ 6aNVPqǮ&ЀGBbY)98SF);CR+z8 .(.[5ld82Ji(e.ڤߓRz٬)AQ1CA^Ѻ1m {K@Ba/_Hn f mkv+k Ziz-A+Ķ*Iw?.4եAej]@YZVw|L29Sh)"RS΅2FHC(#<}j ΅%*r1$lC^j0 %= &4 Hvl?p|$ O˅" B3.'6#$tu(j9oSFI@IGI:wt33=kD3k24bI$| Ze7W_MGw/_MYwngZByƣ~uo;쪶I 1nZF9K ւe{RF`vY9Hn$Az`^RWz]pBzp4ߠ5>8iТW1Cf;Pp8ٙm-<$-i['Tl.vTqJm d:=bi( NBhjw NٍLjFs >f$0vs>t.8aڍ$1yǤnMP^@ ޙ@ʅ^H\8d!^)# ^$cL1,<*&?&e4y h!p <6ScFQgZx-6.փ3 _ [eY1˺S7r`X&Hk?N̸9F+CpN¸1yrwpg$T6Rʸ}o 7du;:|k3 9~%"Ѹ$qp^&EdD'O(U^a'i~Ab^V(b ݢ1`b܈~< #olNS֫۬;s,%?BtjM森vw5ݰvʪUŴ t09g^ص;V#(g¿_e 2.TU+{>w=֩#Ox]vGJg齳%{m+ =tP/߮]YwGpe|MSϸw^CҮK]%#u#:[3Lamֺό3Mz^elgw?i{S"rrVJGD%QYԪQf}~es!'y"Eԟ|ЮrkZ: eUӖl3o]ZKf$567S[-G5Mc7n7vo,h[pu5Wr#L]8kUo"d_~7G 0 ڱX;aLC(1SEfIec(k%әjJjw(2Un7&)v?Oڎ]ݻs];c[gQ` 7NmGw ڱkh)K̞_b]"I'!.#uq̷rbYKBLV$,$jpI԰RA,:$)/Z&kѫW՗ZmZxsdkk"m{ ԏjmknEğ _O7> hi;XEo/,N\H\HT!T;ٓB1(c,؎t\Co+C}79ˆK,F?LRfN}Z-&u =lk 9 ~2Ex%s?,tz/QGmCW#v^: Pag{NFGxTsnY 7oT{Jceq<տ?]AgPqҩ g".+L^]âr Ge~=+UD% {࿗$A"fF'C )dJ>BK'_\B<+!U˶U2zb5 MUڊciLٵU;yIOP- y wR}-Nvv͟Tp'X)&o ^:$> g>*i8JnZ!qv`[p| w ׁ̄oׅԄ{{K^P2E\}@.I {{(bK3 (V7 4 Q?6Ms-#9,Y J>Biç[|cN3vF&}l5}Pfv[겦\svުcN;;v ^X88ؽs`jcճE\ 9btJÕUkaYU_m&t_9h MatchIt/src/0000755000176200001440000000000014170752616012403 5ustar liggesusersMatchIt/src/tabulateC.cpp0000644000176200001440000000035513762532707015021 0ustar liggesusers#include #include "internal.h" using namespace Rcpp; // [[Rcpp::export]] IntegerVector tabulateC(const IntegerVector& bins, const Nullable& nbins = R_NilValue) { return tabulateC_(bins, nbins); } MatchIt/src/subclass2mm.cpp0000644000176200001440000000233713762634771015356 0ustar liggesusers#include #include "internal.h" using namespace Rcpp; //Turns subclass vector given as a factor into a numeric match.matrix. //focal is the treatment level (0/1) that corresponds to the rownames. // [[Rcpp::export]] IntegerMatrix subclass2mmC(const IntegerVector& subclass, const IntegerVector& treat, const int& focal) { IntegerVector tab = tabulateC_(subclass); int mm_col = max(tab) - 1; IntegerVector ind = Range(0, treat.size() - 1); IntegerVector ind1 = ind[treat == focal]; int n1 = ind1.size(); IntegerMatrix mm(n1, mm_col); mm.fill(NA_INTEGER); rownames(mm) = as(treat.names())[ind1]; IntegerVector in_sub = ind[!is_na(subclass)]; IntegerVector ind_in_sub = ind[in_sub]; IntegerVector ind0_in_sub = ind_in_sub[as(treat[in_sub]) != focal]; IntegerVector sub0_in_sub = subclass[ind0_in_sub]; int i, t, s, nmc, mci; IntegerVector mc(mm_col); for (i = 0; i < n1; i++) { t = ind1[i]; s = subclass[t]; if (s != NA_INTEGER) { mc = ind0_in_sub[sub0_in_sub == s]; nmc = mc.size(); for (mci = 0; mci < nmc; mci++) { mm(i, mci) = mc[mci] + 1; } } } return mm; } MatchIt/src/internal.cpp0000644000176200001440000000114413762533337014726 0ustar liggesusers#include using namespace Rcpp; // Rcpp internal functions //C implementation of tabulate. Faster than base::tabulate(), but real //use is in subclass2mmC(). // [[Rcpp::interfaces(cpp)]] IntegerVector tabulateC_(const IntegerVector& bins, const Nullable& nbins = R_NilValue) { int max_bin; if (nbins.isNotNull()) max_bin = as(nbins); else max_bin = max(na_omit(bins)); IntegerVector counts(max_bin); int n = bins.size(); for (int i = 0; i < n; i++) { if (bins[i] > 0 && bins[i] <= max_bin) counts[bins[i] - 1]++; } return counts; } MatchIt/src/pairdistC.cpp0000644000176200001440000000177613755311373015043 0ustar liggesusers#include using namespace Rcpp; // [[Rcpp::export]] double pairdistsubC(const NumericVector& x_, const IntegerVector& t_, const IntegerVector& s_, const int& num_sub) { double dist = 0; LogicalVector not_na_sub = !is_na(s_); NumericVector x = x_[not_na_sub]; IntegerVector t = t_[not_na_sub]; IntegerVector s = s_[not_na_sub]; int n = t.size(); LogicalVector t_i(n), in_s_i(n); NumericVector x_t1(n), x_t0(n); int k = 0; int i, i1, i0; for (i = 1; i <= num_sub; ++i) { in_s_i = (s == i); t_i = (t == 1); t_i[!in_s_i] = false; x_t1 = x[t_i]; t_i = (t == 0); t_i[!in_s_i] = false; x_t0 = x[t_i]; for (i1 = 0; i1 < x_t1.size(); ++i1) { for (i0 = 0; i0 < x_t0.size(); ++i0) { dist += std::abs(x_t1[i1] - x_t0[i0]); //Note: need std::abs because inner is float; see https://stackoverflow.com/questions/63586323/ ++k; } } } dist /= k; return dist; } MatchIt/src/nn_matchC.cpp0000644000176200001440000002015714137622624015004 0ustar liggesusers// [[Rcpp::depends(RcppProgress)]] #include #include using namespace Rcpp; // [[Rcpp::plugins(cpp11)]] // [[Rcpp::export]] IntegerMatrix nn_matchC(const IntegerMatrix& mm_, const IntegerVector& treat_, const IntegerVector& ord_, const IntegerVector& ratio, const int& max_rat, const LogicalVector& discarded, const int& reuse_max, const Nullable& distance_ = R_NilValue, const Nullable& distance_mat_ = R_NilValue, const Nullable& exact_ = R_NilValue, const Nullable& caliper_dist_ = R_NilValue, const Nullable& caliper_covs_ = R_NilValue, const Nullable& calcovs_covs_mat_ = R_NilValue, const Nullable& mah_covs_ = R_NilValue, const Nullable& antiexact_covs_ = R_NilValue, const bool& disl_prog = false) { // Initialize NumericVector distance, caliper_covs; double caliper_dist; NumericMatrix distance_mat, calcovs_covs_mat, mah_covs, mah_covs_c; IntegerMatrix antiexact_covs; IntegerVector exact, exact_c, antiexact_col; bool use_dist_mat = false; bool use_exact = false; bool use_caliper_dist = false; bool use_caliper_covs = false; bool use_mah_covs = false; bool use_antiexact = false; bool use_reuse_max = false; // Info about original treat int n_ = treat_.size(); IntegerVector ind_ = Range(0, n_ - 1); IntegerVector ind1_ = ind_[treat_ == 1]; IntegerVector ind0_ = ind_[treat_ == 0]; int n1_ = ind1_.size(); int n0_ = n_ - n1_; // Output matrix with sample indices of C units IntegerMatrix mm = mm_; // Store who has been matched IntegerVector matched = rep(0, n_); matched[discarded] = n1_; //discarded are unmatchable // After discarding IntegerVector ind = ind_[!discarded]; IntegerVector treat = treat_[!discarded]; IntegerVector ind0 = ind[treat == 0]; int n0 = ind0.size(); int t, t_ind, min_ind, c_chosen, num_eligible, cal_len, t_rat, n_anti; double dt, cal_var_t; NumericVector cal_var, cal_diff, ps_diff, diff, dist_t, mah_covs_t, mah_covs_row, match_distance(n0); IntegerVector c_eligible(n0), indices(n0); LogicalVector finite_match_distance(n0); if (distance_.isNotNull()) { distance = distance_; } if (exact_.isNotNull()) { exact = exact_; use_exact = true; } if (caliper_dist_.isNotNull()) { caliper_dist = as(caliper_dist_); use_caliper_dist = true; ps_diff = NumericVector(n_); } if (caliper_covs_.isNotNull()) { caliper_covs = caliper_covs_; use_caliper_covs = true; cal_len = caliper_covs.size(); cal_diff = NumericVector(n0); } if (calcovs_covs_mat_.isNotNull()) { calcovs_covs_mat = as(calcovs_covs_mat_); } if (mah_covs_.isNotNull()) { mah_covs = as(mah_covs_); NumericVector mah_covs_row(mah_covs.ncol()); use_mah_covs = true; } else { if (distance_mat_.isNotNull()) { distance_mat = as(distance_mat_); // IntegerVector ind0_ = ind_[treat_ == 0]; NumericVector dist_t(n0_); use_dist_mat = true; } ps_diff = NumericVector(n_); } if (antiexact_covs_.isNotNull()) { antiexact_covs = as(antiexact_covs_); n_anti = antiexact_covs.ncol(); use_antiexact = true; } if (reuse_max < n1_) { use_reuse_max = true; } bool ps_diff_assigned; //progress bar int prog_length; if (!use_reuse_max) prog_length = n1_ + 1; else prog_length = max_rat*n1_ + 1; Progress p(prog_length, disl_prog); //Counters int rat, i, x, j, j_, a, k; k = -1; //Matching for (rat = 0; rat < max_rat; ++rat) { for (i = 0; i < n1_; ++i) { k++; if (k % 500 == 0) Rcpp::checkUserInterrupt(); p.increment(); if (all(as(matched[ind0]) >= reuse_max).is_true()){ break; } t = ord_[i] - 1; // index among treated t_ind = ind1_[t]; // index among sample if (matched[t_ind]) { continue; } //Check if unit has enough matches t_rat = ratio[t]; if (t_rat < rat + 1) { continue; } c_eligible = ind0; // index among sample c_eligible = c_eligible[as(matched[c_eligible]) < reuse_max]; if (use_exact) { exact_c = exact[c_eligible]; c_eligible = c_eligible[exact_c == exact[t_ind]]; } if (c_eligible.size() == 0) { continue; } if (use_antiexact) { for (a = 0; a < n_anti; ++a) { antiexact_col = antiexact_covs(_, a); antiexact_col = antiexact_col[c_eligible]; c_eligible = c_eligible[antiexact_col != antiexact_col[t_ind]]; } } if (c_eligible.size() == 0) { continue; } ps_diff_assigned = false; if (use_caliper_dist) { if (use_dist_mat) { dist_t = distance_mat.row(t); diff = dist_t[match(c_eligible, ind0_) - 1]; } else { dt = distance[t_ind]; diff = Rcpp::abs(as(distance[c_eligible]) - dt); } ps_diff[c_eligible] = diff; ps_diff_assigned = true; c_eligible = c_eligible[diff <= caliper_dist]; if (c_eligible.size() == 0) { continue; } } if (use_caliper_covs) { for (x = 0; (x < cal_len) && c_eligible.size() > 0; ++x) { cal_var = calcovs_covs_mat( _ , x ); cal_var_t = cal_var[t_ind]; diff = Rcpp::abs(as(cal_var[c_eligible]) - cal_var_t); cal_diff = diff; c_eligible = c_eligible[cal_diff <= caliper_covs[x]]; } if (c_eligible.size() == 0) { continue; } } //Compute distances among eligible num_eligible = c_eligible.size(); //If replace and few eligible controls, assign all and move on if (!use_reuse_max && (num_eligible <= t_rat)) { for (j = 0; j < num_eligible; ++j) { mm( t , j ) = c_eligible[j] + 1; } continue; } if (use_mah_covs) { match_distance = rep(0.0, num_eligible); mah_covs_t = mah_covs( t_ind , _ ); for (j = 0; j < num_eligible; j++) { j_ = c_eligible[j]; mah_covs_row = mah_covs(c_eligible[j], _); match_distance[j] = sqrt(sum(pow(mah_covs_t - mah_covs_row, 2.0))); } } else if (ps_diff_assigned) { match_distance = ps_diff[c_eligible]; //c_eligible might have shrunk since previous assignment } else if (use_dist_mat) { dist_t = distance_mat.row(t); match_distance = dist_t[match(c_eligible, ind0_) - 1]; } else { dt = distance[t_ind]; match_distance = Rcpp::abs(as(distance[c_eligible]) - dt); } //Remove infinite distances finite_match_distance = is_finite(match_distance); c_eligible = c_eligible[finite_match_distance]; if (c_eligible.size() == 0) { continue; } match_distance = match_distance[finite_match_distance]; if (!use_reuse_max) { //When matching w/ replacement, get t_rat closest control units indices = Range(0, num_eligible - 1); std::partial_sort(indices.begin(), indices.begin() + t_rat, indices.end(), [&match_distance](int k, int j) {return match_distance[k] < match_distance[j];}); for (j = 0; j < t_rat; ++j) { min_ind = indices[j]; mm( t , j ) = c_eligible[min_ind] + 1; } } else { min_ind = which_min(match_distance); c_chosen = c_eligible[min_ind]; mm( t , rat ) = c_chosen + 1; // + 1 because C indexing starts at 0 but mm is sent to R matched[c_chosen] = matched[c_chosen] + 1; } } if (!use_reuse_max) break; } p.update(prog_length); return mm; } MatchIt/src/weights_matrixC.cpp0000644000176200001440000000241213763361321016243 0ustar liggesusers#include using namespace Rcpp; // Computes matching weights from match.matrix // [[Rcpp::export]] NumericVector weights_matrixC(const IntegerMatrix& mm, const IntegerVector& treat) { int n = treat.size(); IntegerVector ind = Range(0, n - 1); IntegerVector ind0 = ind[treat == 0]; IntegerVector ind1 = ind[treat == 1]; NumericVector weights = rep(0., n); // weights.fill(0); int nr = mm.nrow(); int nc = mm.ncol(); int r, c, row_not_na, which_c, t_ind; double weights_c, add_w; IntegerVector row_r(nc); for (r = 0; r < nr; r++) { row_r = na_omit(mm(r, _)); row_not_na = row_r.size(); if (row_not_na == 0) { continue; } add_w = 1.0/static_cast(row_not_na); for (c = 0; c < row_not_na; c++) { which_c = row_r[c] - 1; weights_c = weights[which_c]; weights[which_c] = weights_c + add_w; } t_ind = ind1[r]; weights[t_ind] = 1; } NumericVector c_weights = weights[ind0]; double sum_c_w = sum(c_weights); double sum_matched_c = sum(c_weights > 0); int n0 = ind0.size(); if (sum_c_w > 0) { for (int i = 0; i < n0; i++ ) { which_c = ind0[i]; weights[which_c] = c_weights[i] * sum_matched_c / sum_c_w; } } return weights; } MatchIt/src/RcppExports.cpp0000644000176200001440000001516714170744710015406 0ustar liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #include "../inst/include/MatchIt.h" #include #include #include using namespace Rcpp; #ifdef RCPP_USE_GLOBAL_ROSTREAM Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif // nn_matchC IntegerMatrix nn_matchC(const IntegerMatrix& mm_, const IntegerVector& treat_, const IntegerVector& ord_, const IntegerVector& ratio, const int& max_rat, const LogicalVector& discarded, const int& reuse_max, const Nullable& distance_, const Nullable& distance_mat_, const Nullable& exact_, const Nullable& caliper_dist_, const Nullable& caliper_covs_, const Nullable& calcovs_covs_mat_, const Nullable& mah_covs_, const Nullable& antiexact_covs_, const bool& disl_prog); RcppExport SEXP _MatchIt_nn_matchC(SEXP mm_SEXP, SEXP treat_SEXP, SEXP ord_SEXP, SEXP ratioSEXP, SEXP max_ratSEXP, SEXP discardedSEXP, SEXP reuse_maxSEXP, SEXP distance_SEXP, SEXP distance_mat_SEXP, SEXP exact_SEXP, SEXP caliper_dist_SEXP, SEXP caliper_covs_SEXP, SEXP calcovs_covs_mat_SEXP, SEXP mah_covs_SEXP, SEXP antiexact_covs_SEXP, SEXP disl_progSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< const IntegerMatrix& >::type mm_(mm_SEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type treat_(treat_SEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type ord_(ord_SEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type ratio(ratioSEXP); Rcpp::traits::input_parameter< const int& >::type max_rat(max_ratSEXP); Rcpp::traits::input_parameter< const LogicalVector& >::type discarded(discardedSEXP); Rcpp::traits::input_parameter< const int& >::type reuse_max(reuse_maxSEXP); Rcpp::traits::input_parameter< const Nullable& >::type distance_(distance_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type distance_mat_(distance_mat_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type exact_(exact_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type caliper_dist_(caliper_dist_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type caliper_covs_(caliper_covs_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type calcovs_covs_mat_(calcovs_covs_mat_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type mah_covs_(mah_covs_SEXP); Rcpp::traits::input_parameter< const Nullable& >::type antiexact_covs_(antiexact_covs_SEXP); Rcpp::traits::input_parameter< const bool& >::type disl_prog(disl_progSEXP); rcpp_result_gen = Rcpp::wrap(nn_matchC(mm_, treat_, ord_, ratio, max_rat, discarded, reuse_max, distance_, distance_mat_, exact_, caliper_dist_, caliper_covs_, calcovs_covs_mat_, mah_covs_, antiexact_covs_, disl_prog)); return rcpp_result_gen; END_RCPP } // pairdistsubC double pairdistsubC(const NumericVector& x_, const IntegerVector& t_, const IntegerVector& s_, const int& num_sub); RcppExport SEXP _MatchIt_pairdistsubC(SEXP x_SEXP, SEXP t_SEXP, SEXP s_SEXP, SEXP num_subSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< const NumericVector& >::type x_(x_SEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type t_(t_SEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type s_(s_SEXP); Rcpp::traits::input_parameter< const int& >::type num_sub(num_subSEXP); rcpp_result_gen = Rcpp::wrap(pairdistsubC(x_, t_, s_, num_sub)); return rcpp_result_gen; END_RCPP } // subclass2mmC IntegerMatrix subclass2mmC(const IntegerVector& subclass, const IntegerVector& treat, const int& focal); RcppExport SEXP _MatchIt_subclass2mmC(SEXP subclassSEXP, SEXP treatSEXP, SEXP focalSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< const IntegerVector& >::type subclass(subclassSEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type treat(treatSEXP); Rcpp::traits::input_parameter< const int& >::type focal(focalSEXP); rcpp_result_gen = Rcpp::wrap(subclass2mmC(subclass, treat, focal)); return rcpp_result_gen; END_RCPP } // tabulateC IntegerVector tabulateC(const IntegerVector& bins, const Nullable& nbins); RcppExport SEXP _MatchIt_tabulateC(SEXP binsSEXP, SEXP nbinsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< const IntegerVector& >::type bins(binsSEXP); Rcpp::traits::input_parameter< const Nullable& >::type nbins(nbinsSEXP); rcpp_result_gen = Rcpp::wrap(tabulateC(bins, nbins)); return rcpp_result_gen; END_RCPP } // weights_matrixC NumericVector weights_matrixC(const IntegerMatrix& mm, const IntegerVector& treat); RcppExport SEXP _MatchIt_weights_matrixC(SEXP mmSEXP, SEXP treatSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< const IntegerMatrix& >::type mm(mmSEXP); Rcpp::traits::input_parameter< const IntegerVector& >::type treat(treatSEXP); rcpp_result_gen = Rcpp::wrap(weights_matrixC(mm, treat)); return rcpp_result_gen; END_RCPP } // validate (ensure exported C++ functions exist before calling them) static int _MatchIt_RcppExport_validate(const char* sig) { static std::set signatures; if (signatures.empty()) { } return signatures.find(sig) != signatures.end(); } // registerCCallable (register entry points for exported C++ functions) RcppExport SEXP _MatchIt_RcppExport_registerCCallable() { R_RegisterCCallable("MatchIt", "_MatchIt_RcppExport_validate", (DL_FUNC)_MatchIt_RcppExport_validate); return R_NilValue; } static const R_CallMethodDef CallEntries[] = { {"_MatchIt_nn_matchC", (DL_FUNC) &_MatchIt_nn_matchC, 16}, {"_MatchIt_pairdistsubC", (DL_FUNC) &_MatchIt_pairdistsubC, 4}, {"_MatchIt_subclass2mmC", (DL_FUNC) &_MatchIt_subclass2mmC, 3}, {"_MatchIt_tabulateC", (DL_FUNC) &_MatchIt_tabulateC, 2}, {"_MatchIt_weights_matrixC", (DL_FUNC) &_MatchIt_weights_matrixC, 2}, {"_MatchIt_RcppExport_registerCCallable", (DL_FUNC) &_MatchIt_RcppExport_registerCCallable, 0}, {NULL, NULL, 0} }; RcppExport void R_init_MatchIt(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } MatchIt/src/internal.h0000644000176200001440000000032013762533661014366 0ustar liggesusers#ifndef INTERNAL_H #define INTERNAL_H #include using namespace Rcpp; IntegerVector tabulateC_(const IntegerVector& bins, const Nullable& nbins = R_NilValue); #endif MatchIt/vignettes/0000755000176200001440000000000014170752616013624 5ustar liggesusersMatchIt/vignettes/assessing-balance.Rmd0000644000176200001440000010140214142761517017647 0ustar liggesusers--- title: "Assessing Balance" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Assessing Balance} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE, message = FALSE, fig.width=7, fig.height=5) options(width = 200, digits = 4) ``` ```{=html} ``` ## Introduction Covariate balance is the degree to which the distribution of covariates is similar across levels of the treatment. It has three main roles in causal effect estimation using matching: 1) as a target to optimize with matching, 2) as a method of assessing the quality of the resulting matches, and 3) as evidence to an audience that the estimated effect is close to the true effect. When covariate balance is achieved, the resulting effect estimate is less sensitive to model misspecification and ideally close to true treatment effect. The benefit of randomization is that covariate balance is achieved automatically (in expectation), which is why unadjusted effects estimated from randomized trial data (in the absence of drop-out) can be validly interpreted as causal effects. When using matching to recover causal effect estimates form observational data, balance is not guaranteed and must be assessed. This document provides instructions for assessing and reporting covariate balance as part of a matching analysis. The tools available in `MatchIt` for balance assessment should be used during the process of selecting a good matching scheme and ensuring that the chosen scheme is adequate. These tools implement the recommendations of @ho2007 and others for assessing balance. In addition to the tools available in `matchIt`, the `cobalt` package has a suite of functions designed to assess and display balance and is directly compatible with `MatchIt` objects. `cobalt` has extensive documentation, but we describe some of its functionality here as a complement to the tools in `MatchIt`. The structure of this document is as follows: first, we describe some of the recommendations for balance checking and their rationale; next, we describe the tools for assessing balance present in `MatchIt` and display their use in evaluating several matching schemes; finally; we briefly describe some of the functionality in `cobalt` to extend that in `MatchIt`. ## Recommendations for Balance Assessment Assessing balance involves assessing whether the distributions of covariates are similar between the treated and control groups. Balance is typically assessed by examining univariate balance summary statistics for each covariate, though more complicated methods exist for assessing joint distributional balance as well. Visual depictions of distributional balance can be a helpful complement to numerical summaries, especially for hard to balance and prognostically important covariates. Many recommendations for balance assessment have been described in the methodological literature. Unfortunately, there is no single best way to assess balance or to weigh balance summary statistics because the degree and form of balance that will yield the least bias in an effect estimate depends on unknown qualities of the outcome data-generating model. Nonetheless, there are a number of valuable recommendations that can be implemented to ensure matching is successful at eliminating or reducing bias. We review some of these here. Common recommendations for assessing balance include the following: - **Standardized mean differences**. The standardized mean difference (SMD) is the difference in the means of each covariate between treatment groups standardized by a standardization factor so that it is on the same scale for all covariates. The standardization factor is typically the standard deviation of the covariate in the treated group when targeting the ATT or the pooled standard deviation across both groups when targeting the ATE. The standardization factor should be the same before and after matching to ensure changes in the mean difference are not confounded by changes in the standard deviation of the covariate. SMDs close to zero indicate good balance. Several recommended thresholds have been published in the literature; we recommend .1 and .05 for prognostically important covariates. Higher values may be acceptable when using covariate adjustment in the matched sample. In addition to computing SMDs on the covariates themselves, it is important to compute them on squares, cubes, and higher exponents as well as interactions between covariates. Several empirical studies have examined the appropriateness for using SMDs in balance assessment, including @belitser2011, @ali2014, and @stuart2013; in general, there is often a high correlation between the mean or maximum absolute SMD and the degree of bias in the treatment effect. - **Variance Ratios**. The variance ratio is the ratio of the variance of a covariate in one group to that in the other. Variance ratios close to 1 indicate good balance because they imply the variances of the samples are similar [@austin2009]. - **Empirical CDF Statistics**. Statistics related to the difference in the empirical cumulative density functions (eCDFs) of each covariate between groups allow assessment of imbalance across the entire covariate distribution of that covariate rather than just its mean or variance. The maximum eCDF difference, also known as the Kolmogorov-Smirnov statistic, is sometimes recommended as a useful supplement to SMDs for assessing balance [@austin2015] and is often used as a criterion to use in propensity score methods that attempt to optimize balance [e.g., @mccaffrey2004; @diamond2013]. Although the mean eCDF difference has not been as well studied, it provides a summary of imbalance that may be missed by relying solely on the maximum difference. - **Visual Diagnostics**. Visual diagnostics such as eCDF plots, empirical quantile-quantile (eQQ) plots, and kernel density plots can be used to see exactly how the covariate distributions differ from each other, i.e., where in the distribution the greatest imbalances are [@ho2007; @austin2009]. This can help to figure out how to tailor a matching method to target imbalance in a specific region of the covariate distribution. - **Prognostic scores**. The prognostic score is an estimate of the potential outcome under control for each unit [@hansen2008]. Balance on the prognostic score has been shown to be highly correlated with bias in the effect estimate, making it a useful tool in balance assessment [@stuart2013]. Estimating the prognostic score requires having access to the outcome data, and using it may be seen as violating the principle of separating the design and analysis stages of a matching analysis [@rubin2001]. However, because only the outcome values from the control group are required to use the prognostic score, some separation is maintained. Several multivariate statistics exist that summarize balance across the entire joint covariate distribution. These can be functions of the above measures, like the mean or maximum absolute SMD or the generalized weighted distance [GWD; @franklin2014], which is the sum of SMDs for the covariates and their squares and interactions, or separate statistics that measure quantities that abstract away from the distribution of individual covariates, like the L1 distance [@iacus2011], cross-match test [@heller2010], or energy distance [@huling2020]. Balance on the propensity score has often been considered a useful measure of balance, but we do not necessarily recommend it except as a supplement to balance on the covariates. Propensity score balance will generally be good with any matching method regardless of the covariate balancing potential of the propensity score, so a balanced propensity score does not imply balanced covariates [@austin2009]. Similarly, it may happen that covariates may be well balanced even if the propensity score is not balanced, such as when covariates are prioritized above the propensity score in the matching specification (e.g., with genetic matching). Given these observations, the propensity score should not be relied upon for assessing covariate balance. Simulation studies by @stuart2013 provide evidence for this recommendation against relying on propensity score balance. There has been some debate about the use of hypothesis tests, such as t-tests or Kolmogorov-Smirnov tests, for assessing covariate balance. The idea is that balance tests test the null hypothesis that the matched sample has equivalent balance to a randomized experiment. There are several problems with balance tests, described by @ho2007 and @imai2008: 1) balance is a property of the sample, not a of a population from which the sample was drawn; 2) the power of balance tests depends on the sample size, which changes during matching even if balance does not change; and 3) the use of hypothesis tests implies a uniform decision criterion for rejecting the null hypothesis (e.g., p-value less than .05, potentially with corrections for multiple comparisons), when balance should be improved without limit. `MatchIt` does not report any balance tests or p-values, instead relying on the descriptive statistics described above. ## Recommendations for Balance Reporting A variety of methods should be used when assessing balance to try to find an optimal matched set that will ideally yield a low-error estimate of the desired effect. However, reporting every balance statistic or plot in a research report or publication can be burdensome and unnecessary. That said, it is critical to report balance to demonstrate to readers that the resulting estimate is approximately unbiased and relies little on extrapolation or correct outcome model specification. We recommend the following in reporting balance in a matching analysis: - Report SMDs before and after matching for each covariate, any prognostically important interactions between covariates, and the prognostic score; this can be reported in a table or in a Love plot. - Report summaries of balance for other statistics, e.g., the largest mean and maximum eCDF difference among the covariates and the largest SMD among squares, cubes, and interactions of the covariates. `MatchIt` provides tools for calculating each of these statistics so they can be reported with ease in a manuscript or report. ## Assessing Balance with `MatchIt` `MatchIt` contains several tools to assess balance numerically and graphically. The primary balance assessment function is `summary.matchit()`, which is called when using `summary()` on a `MatchIt` object and produces several tables of balance statistics before and after matching. `plot.summary.matchit()` generates a Love plot using R's base graphics system containing the standardized mean differences resulting from a call to `summary.matchit()` and provides a nice way to display balance visually for inclusion in an article or report. `plot.matchit()` generates several plots that display different elements of covariate balance, including propensity score overlap and distribution plots of the covariates. These functions together form a suite that can be used to assess and report balance in a variety of ways. To demonstrate `MatchIt`'s balance assessment capabilities, we will use the Lalonde data included in `MatchIt` and used in `vignette("MatchIt")`. We will perform full matching on the propensity score, though the functionality is identical across all matching methods except propensity score subclassification, which we illustrate at the end. ```{r} library("MatchIt") data("lalonde", package = "MatchIt") #Full matching on a logistic regression PS m.out <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "full") m.out ``` ### `summary.matchit()` When `summary()` is called on a `matchit` object, several tables of information are displayed. These include balance statistics for each covariate before matching, balance statistics for each covariate after matching, the percent reduction in imbalance after matching, and the sample sizes before and after matching. `summary.matchit()` has four additional arguments that control how balance is computed: - `interactions` controls whether balance statistics for all squares and pairwise interactions of covariates are to be displayed in addition to the covariates. The default is `FALSE`, and setting to `TRUE` can make the output massive when many covariates are present, but it is important to ensure no important interactions remain imbalanced. - `addlvariables` allows for balance to be assessed on variables other than those inside the `matchit` object. For example, if the distance between units only relied on a subset of covariates but balance needed to be achieved on all covariates, `addlvariables` could be used to supply these additional covariates. In addition to adding other variables, `addlvariables` can be used to request balance on specific functions of the covariates already in the `matchit` object, such as polynomial terms or interactions. The input to `addlvariables` can be a one-sided formula with the covariates and any desired transformations thereof on the right hand side, just like a model formula (e.g., `addlvariables = ~ X1 + X2 + I(X1^2)` would request balance on `X1`, `X2`, and the square of `X1`). Additional variables supplied to `addlvariables` but not present in the `matchit` object can be supplied as a data frame using the `data` argument. - `standardize` controls whether standardized or unstandardized statistics are to displayed. Standardized statistics include the standardized mean difference and eCDF statistics; unstandardized statistics include the raw difference in means and eQQ plot statistics. (Regardless, the variance ratio will always be displayed.). The default is `TRUE` for standardized statistics, which are more common to report because they are all on the same scale regardless of the scale of the covariates[^1]. - `pair.dist` controls whether within-pair distances should be computed and displayed. These reflect the average distance between units within the same pair, standardized or unstandardized according to the argument to `standardize`. The default is `TRUE`. With full matching, exact matching, coarsened exact matching, and propensity score subclassification, computing pair distances can take a long time, and so it may be beneficial to set to `FALSE` in these cases. [^1]: Note that versions of `MatchIt` before 4.0.0 had `standardize` set to `FALSE` by default. In addition, the arguments `un` and `improvement` control whether balance prior to matching should be displayed and whether the percent balance improvement after matching should be displayed. These can be set to `FALSE` to reduce the output. Below, we call `summary.matchit()` with `addlvariables` to display balance on covariates and a few functions of them in the matched sample. In particular, we request balance on the square of `age`, the variables representing whether `re74` and `re75` were equal to 0, and the interaction between `educ` and `race`. ```{r} summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + I(re75==0) + educ:race) ``` Let's examine the output in detail. The first table (`Summary of Balance for All Data`) provides balance in the sample prior to matching. The included statistics are the mean of the covariates in the treated group (`Means Treated`), the mean of the covariate in the control group (`Means Control`), the SMDs (`Std. Mean Diff.`), the variance ratio (`Var. Ratio`), the average distance between the eCDFs of the covariate across the groups (`eCDF Mean`), and the largest distance between the eCDFs (`eCDF Max`). Setting `un = FALSE` would have suppressed the creation of this table. The second table (`Summary of Balance for Matched Data`) contains all the same statistics in the matched sample. Because we implicitly request pair distance, an additional column for standardized pair distances (`Std. Pair Dist.`) is displayed. The third table (`Percent Balance Improvement`) contains the percent balance improvement for each covariate. This is computed as $100\frac{|\theta_M| - |\theta_U|}{|\theta_U|}$, where $\theta_M$ is a given balance statistic in the matched sample and $\theta_U$ is a the same balance statistic in the unmatched sample. Values between 0 and 100 indicate that balance improved after matching as measured by the statistic; values less than 0 indicate that balance got worse after matching. When balance is good on a covariate prior to matching, it can sometimes look like balance got a lot worse after matching even though the balance statistic is quite low, so these values should not be taken too seriously and should be used primarily as heuristics. Setting `un = FALSE` or `improvement = FALSE` would have suppressed the creation of this table. The final table (`Sample Sizes`) contains the sizes of the samples before (`All`) and after (`Matched`) matching, as well as the number of units left unmatched (`Unmatched`) and the number of units dropped due to a common support restriction (`Discarded`). The SMDs are computed as the difference mean divided by a standardization factor computed in the **unmatched** sample. An absolute SMD close to 0 indicates good balance; although a number of recommendations for acceptable values have appeared in the literature, we recommend absolute values less than .1 and less than .05 for potentially prognostically important variables. The variance ratios are computed as the ratio of the variance of the treated group to that of the control group for each covariate. Variance ratios are not computed for binary covariates because they are a function of the prevalence in each group, which is captured in the mean difference and eCDF statistics. A variance ratio close to 1 indicates good balance; a commonly used recommendation is for variance ratios to be between .5 and 2. The eCDF statistics correspond to the difference in the overall distributions of the covariates between the treatment groups. The values of both statistics range from 0 to 1, with values closer to zero indicating better balance. There are no specific recommendations for the values these statistics should take, though notably high values may indicate imbalance on higher moments of the covariates. The eQQ statistics produced when `standardize = FALSE` are interpreted similarly but are on the scale of the covariate. All these statistics should be considered together. Imbalance as measured by any of them may indicate a potential failure of the matching scheme to achieve distributional balance. ### `plot.summary.matchit()` A Love plot is a clean way to visually summarize balance. Using `plot` on the output of a call to `summary()` on a `matchit` object produces a Love plot of the standardized mean differences. `plot.summary.matchit()` has several additional arguments that can be used to customize the plot. - `abs` controls whether standardized mean difference should be displayed in absolute value or not. Default is `TRUE`. - `var.order` controls how the variables are ordered on the y-axis. The options are `"data"` (the default), which orders the variables as they appear the in the `summary.matchit()` output; `"unmatched"`, which orders the variables based on their standardized mean differences before matching; `"matched"`, which orders the variables based on their standardized mean differences after matching; and `"alphabetical"`, which orders the variables alphabetically. Using `"unmatched"` tends to result in attractive plots and ensures the legend doesn't overlap with points in its default position. - `threshold` controls where vertical lines indicating chosen thresholds should appear on the x-axis. Should be a numeric vector. The default is `c(.1, .05)`, which display vertical lines at .1 and .05 standardized mean difference units. - `position` controls the position of the legend. The default is `"bottomright"`, which puts the legend in the bottom right corner of the plot, and any keyword value available to supplied to `x` in `legend()` is allowed. Below we create a Love plot of the covariates. ```{r} m.sum <- summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + I(re75==0) + educ:race) plot(m.sum, var.order = "unmatched") ``` From this plot it is clear to see that balance was quite poor prior to matching, but full matching improved balance on all covariates, and most within a threshold of .1. To make the variable names cleaner, the original variables should be renamed prior to matching. `cobalt` provides many additional options to generate and customize Love plots using the `love.plot()` function and should be used if a plot beyond what is available with `plot.summary.matchit()` is desired. ### `plot.matchit()` In addition to numeric summaries of balance, `MatchIt` offers graphical summaries as well using `plot.matchit()` (i.e., using `plot()` on a `matchit` object). We can create eQQ plots, eCDF plots, or density plots of the covariates and histograms or jitter plots of the propensity score. The covariate plots can provide a summary of the balance of the full marginal distribution of a covariate beyond just the mean and variance. `plot.matchit()` has a few arguments to customize the output: - `type` corresponds to the type of plot desired. Options include `"qq"` for eQQ plots (the default), `"ecdf"` for eCDF plots, `"density"` for density plots, `"jitter"` for jitter plots, and `"histogram"` for histograms. - `interactive` controls whether the plot is interactive or not. For eQQ, eCDF, and density plots, this allows us to control when the next page of covariates is to be displayed since only three can appear at a time. For jitter plots, this can allow us to select individual units with extreme values for further inspection. The default is `TRUE`. - `which.xs` is used to specify for which covariates to display balance in eQQ, eCDF, and density plots. The default is to display balance on all, but we can request balance just on a specific subset. If three or fewer are requested, `interactive` is ignored. Below, we demonstrate the eQQ plot: ```{r} #eQQ plot plot(m.out, type = "qq", which.xs = c("age", "nodegree", "re74")) ``` The y-axis displays the each value of the covariate for the treated units, and the x-axis displays the the value of the covariate at the corresponding quantile in the control group. When values fall on the 45 degree line, the groups are balanced. Above, we can see that `age` remains somewhat imbalanced, but `nodegree` and `re74` have much better balance after matching than before. The difference between the x and y values of each point are used to compute the eQQ difference statistics that are displayed in `summary.matchit()` with `standardize = FALSE`. Below, we demonstrate the eCDF plot: ```{r} #eCDF plot plot(m.out, type = "ecdf", which.xs = c("educ", "married", "re75")) ``` The x-axis display the covariate values and the y-axis displays the proportion of the sample at or less than that covariate value. Perfectly overlapping lines indicate good balance. The black line corresponds to the treated group and the gray line to the control group. Although `educ` and `re75` were fairly well balanced before matching, their balance has improved nonetheless. `married` appears far better balanced after matching than before. The vertical difference between the eCDFs lines of each treatment group is used to compute the eCDF difference statistics that are displayed in `summary.matchit()` with `standardize = TRUE`. Below, we demonstrate the density plot: ```{r} #density plot plot(m.out, type = "density", which.xs = c("age", "educ", "married")) ``` The x-axis display the covariate values and the y-axis displays the density of the sample at that covariate value. For binary variables, the y-axis displays the proportion of the sample at that covariate value. Perfectly overlapping lines indicate good balance. The black line corresponds to the treated group and the gray line to the control group. Density plots display similar information to eCDF plots but may be more intuitive for some users because of their link to histograms. ## Assessing Balance After Subclassification With subclassification, balance can be checked both within each subclass and overall. With `summary.matchit()`, we can request to view balance only in aggregate or in each subclass. The latter can help us decide if we can interpret effects estimated within each subclass as unbiased. The `plot.summary.matchit()` and `plot.matchit()` outputs can be requested either in aggregate or for each subclass. We demonstrate this below. First we will perform propensity score subclassification using 4 subclasses (typically more is beneficial). ```{r} #Subclassification on a logistic regression PS s.out <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "subclass", subclass = 4) s.out ``` When using `summary()`, the default is to display balance only in aggregate using the subclassification weights. This balance output looks similar to that for other matching methods. ```{r} summary(s.out) ``` An additional option in `summary()`, `subclass`, allows us to request balance for individual subclasses. `subclass` can be set to `TRUE` to display balance for all subclasses or the indices of individual subclasses for which balance is to be displayed. Below we call `summary()` and request balance to be displayed on all subclasses (setting `un = FALSE` to suppress balance in the original sample): ```{r} summary(s.out, subclass = TRUE, un = FALSE) ``` We can plot the standardized mean differences in a Love plot that also displays balance for the subclasses using `plot.summary.matchit()` on a `summary.matchit()` object with `subclass = TRUE`. ```{r} s <- summary(s.out, subclass = TRUE) plot(s, var.order = "unmatched", abs = FALSE) ``` Note that for some variables, while the groups are balanced in aggregate (black dots), the individual subclasses (gray numbers) may not be balanced, in which case unadjusted effect estimates within these subclasses should not be interpreted as unbiased. When we plot distributional balance using `plot.matchit()`, again we can choose whether balance should be displayed in aggregate or within subclasses again using the `subclass` option, which functions the same as it does with `summary.matchit()`. Below we demonstrate checking balance within a subclass. ```{r} plot(s.out, type = "density", which.xs = c("educ", "married", "re75"), subclass = 1) ``` If we had set `subclass = FALSE`, plots would have been displayed in aggregate using the subclassification weights. If `subclass` is unspecified, a prompt will ask us for which subclass we want to see balance. ## Assessing Balance with `cobalt` The `cobalt` package was designed specifically for checking balance before and after matching (and weighting). It offers three main functions, `bal.tab()`, `love.plot()`, and `bal.plot()`, which perform similar actions to `summary.matchit()`, `plot.summary.matchit()`, and `plot.matchit()`, respectively. These functions directly interface with `matchit` objects, making `cobalt` straightforward to use in conjunction with `MatchIt`. `cobalt` can be used as a complement to `MatchIt`, especially for more advanced uses that are not accommodated by `MatchIt`, such as comparing balance across different matching schemes and even different packages, assessing balance in clustered or multiply imputed data, and assessing balance with multi-category, continuous, and time-varying treatments. The main `cobalt` vignette contains many examples of its use with `MatchIt` objects, so we only provide a short demonstration of its capabilities here. ```{r, message = F} library("cobalt") ``` ### `bal.tab()` `bal.tab()` produces tables of balance statistics similar to `summary.matchit()`. The columns displayed can be customized to limit how much information is displayed and isolate desired information. We call `bal.tab()` with a few of its options specified below: ```{r} bal.tab(m.out, un = TRUE, stats = c("m", "v", "ks")) ``` The output is very similar to that of `summary.matchit()`, except that the balance statistics computed before matching (with the suffix `.Un`) and those computed after matching (with the suffix `.Adj`) are in the same table. By default, only SMDs after matching (`Diff.Adj`) are displayed; by setting `un = TRUE`, we requested that the balance statistics before matching also be displayed, and by setting `stats = c("m", "v", "ks")` we requested mean differences, variance ratios, and Kolmogorov-Smirnov statistics. Other balance statistics and summary statistics can be requested as well. One important detail to note is that the default for binary covariates is to print the raw difference in proportion rather than the standardized mean difference, so there will be an apparent discrepancy for these variables between `bal.tab()` and `summary.matchit()` output, though this behavior can be changed by setting `binary = "std"` in the call to `bal.tab()`. Functionality for producing balance statistics for additional variables and for powers and interactions of the covariates is available using the `addl`, `poly`, and `int` options. `bal.tab()` and other `cobalt` functions can produce balance not just on a single `matchit` object but on several at the same time, which facilitates comparing balance across several matching specifications. For example, if we wanted to compare the full matching results to the results of nearest neighbor matching without replacement, we could supply both to `bal.tab()`, which we demonstrate below: ```{r} #Nearest neighbor (NN) matching on the PS m.out2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde) #Balance on covariates after full and NN matching bal.tab(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, un = TRUE, weights = list(full = m.out, nn = m.out2)) ``` This time, we supplied `bal.tab()` with the covariates and dataset and supplied the `matchit` output objects in the `weights` argument (which extracts the matching weights from the objects). Here we can see that full matching yields better balance than nearest neighbor matching overall, though balance is slightly worse for `age` and `maried` and the effective sample size is lower. ### `love.plot` `love.plot()` creates a Love plot of chosen balance statistics. It offers many options for customization, including the shape and colors of the points, how the variable names are displayed, and for which statistics balance is to be displayed. Below is an example of its basic use: ```{r} love.plot(m.out, binary = "std") ``` The syntax is straightforward and similar to that of `bal.tab()`. Below we demonstrate a more advanced use that customizes the appearance of the plot and displays balance not only on mean differences but also on Kolmogorov-Smirnov statistics and for both full matching and nearest neighbor matching simultaneously. ```{r, fig.width=7} love.plot(m.out, stats = c("m", "ks"), poly = 2, abs = TRUE, weights = list(nn = m.out2), drop.distance = TRUE, thresholds = c(m = .1), var.order = "unadjusted", binary = "std", shapes = c("triangle", "square", "circle"), colors = c("blue", "darkgreen", "red"), sample.names = c("Full Matching", "NN Matching", "Original"), position = "bottom") ``` The `love.plot()` documentation explains what each of these arguments do and the several other ones available. `cobalt` contains a vignette for other advanced customization of `love.plot()`. ### `bal.plot()` `bal.plot()` displays distributional balance for a single covariate, similar to `plot.matchit()`. Its default is to display kernel density plots for continuous variables and bar graphs for categorical variables. It can also display eCDF plots and histograms. Below we demonstrate some of its uses: ```{r} #Density plot for continuous variables bal.plot(m.out, var.name = "educ", which = "both") #Bar graph for categorical variables bal.plot(m.out, var.name = "race", which = "both") #Mirrored histogram bal.plot(m.out, var.name = "distance", which = "both", type = "histogram", mirror = TRUE) ``` These plots help illuminate the specific ways in which the covariate distributions differ between treatment groups, which can aid in interpreting the balance statistics provided by `bal.tab()` and `summary.matchit()`. ## Conclusion The goal of matching is to achieve covariate balance, similarity between the covariate distributions of the treated and control groups. Balance should be assessed during the matching phase to find a matching specification that works. Balance must also be reported in the write-up of a matching analysis to demonstrate to readers that matching was successful. `MatchIt` and `cobalt` each offer a suite of functions to implement best practices in balance assessment and reporting. ## References MatchIt/vignettes/MatchIt.Rmd0000644000176200001440000006136314145073537015632 0ustar liggesusers--- title: 'MatchIt: Getting Started' author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: yes vignette: | %\VignetteIndexEntry{MatchIt: Getting Started} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE, message = FALSE, fig.width=7, fig.height=5) options(width = 200) ``` ```{=html} ``` ## Introduction `MatchIt` implements the suggestions of @ho2007 for improving parametric statistical models for estimating treatment effects in observational studies and reducing model dependence by preprocessing data with semi-parametric and non-parametric matching methods. After appropriately preprocessing with `MatchIt`, researchers can use whatever parametric model they would have used without `MatchIt` and produce inferences that are more robust and less sensitive to modeling assumptions. `MatchIt` reduces the dependence of causal inferences on commonly made, but hard-to-justify, statistical modeling assumptions using a large range of sophisticated matching methods. The package includes several popular approaches to matching and provides access to methods implemented in other packages through its single, unified, and easy-to-use interface. Matching is used in the context of estimating the causal effect of a binary treatment or exposure on an outcome while controlling for measured pre-treatment variables, typically confounding variables or variables prognostic of the outcome. Here and throughout the `MatchIt` documentation we use the word "treatment" to refer to the focal causal variable of interest, with "treated" and "control" reflecting the names of the treatment groups. The goal of matching is to produce *covariate balance*, that is, for the distributions of covariates in the two groups to be approximately equal to each other, as they would be in a successful randomized experiment. The importance of covariate balance is that it allows for increased robustness to the choice of model used to estimate the treatment effect; in perfectly balanced samples, a simple difference in means can be a valid treatment effect estimate. Here we do not aim to provide a full introduction to matching or causal inference theory, but simply to explain how to use `MatchIt` to perform nonparametric preprocessing. For excellent and accessible introductions to matching, see @stuart2010 and Austin @austin2011b. A matching analysis involves four primary steps: 1) planning, 2) matching, 3) assessing the quality of matches, and 4) estimating the treatment effect and its uncertainty. Here we briefly discuss these steps and how they can be implemented with `MatchIt`; in the other included vignettes, these steps are discussed in more detail. We will use Lalonde's data on the evaluation of the National Supported Work program to demonstrate `MatchIt`'s capabilities. First, we load `MatchIt` and bring in the `lalonde` dataset. ```{r} library("MatchIt") data("lalonde") head(lalonde) ``` The statistical quantity of interest is the causal effect of the treatment (`treat`) on 1978 earnings (`re78`). The other variables are pre-treatment covariates. See `?lalonde` for more information on this dataset. In particular, the analysis is concerned with the marginal, total effect of the treatment for those who actually received the treatment. In what follows, we briefly describe the four steps of a matching analysis and how to implement them in `MatchIt`. For more details, we recommend reading the other vignettes, `vignette("matching-methods")`, `vignette("assessing-balance")`, and `vignette("estimating-effects")`, especially for users less familiar with matching methods. For the use of `MatchIt` with sampling weights, also see `vignette("sampling-weights")`. It is important to recognize that the ease of using `MatchIt` does not imply the simplicity of matching methods; advanced statistical methods like matching that require many decisions to be made and caution in their use should only be performed by those with statistical training. ## Planning The planning phase of a matching analysis involves selecting the type of effect to be estimated, selecting the target population to which the treatment effect is to generalize, and selecting the covariates for which balance is required for an unbiased estimate of the treatment effect. Each of these are theoretical steps that do not involve performing analyses on the data. Ideally, they should be considered prior to data collection in the planning stage of a study. Thinking about them early can aid in performing a complete and cost-effective analysis. **Selecting the type of effect to be estimated.** There are a few different types of effects to be estimated. In the presence of mediating variables, one might be interested in the direct effect of the treatment that does not pass through the mediating variables or the total effect of the treatment across all causal pathways. Matching is well suited for estimating total effects, and specific mediation methods may be better suited for other mediation-related quantities. One may be interested in a conditional effect or a marginal effect. A conditional effect is the effect of a treatment within some strata of other prognostic variables (e.g., at the patient level), and a marginal effect is the average effect of a treatment in a population (e.g., for implementing a broad policy change). Different types of matching are well suited for each of these, but the most common forms are best used for estimating marginal treatment effects; for conditional treatment effects, typically modeling assumptions are required or matching must be done within strata of the conditioning variables. Matching can reduce the reliance on correct model specification for conditional effects. **Selecting a target population.** The target population is the population to which the effect estimate is to generalize. Typically, an effect estimated in a sample generalizes to the population from which the sample is a probability sample. If the sample is not a probability sample from any population (e.g., it is a convenience sample or involves patients from an arbitrary hospital), the target population can be unclear. Often, the target population is a group of units who are eligible for the treatment (or a subset thereof). Causal estimands are defined by the target population to which they generalize. The average treatment effect in the population (ATE) is the average effect of the treatment for all units in the target population. The average treatment effect in the treated (ATT) is the average effect of the treatment for units like those who actually were treated. The most common forms of matching are best suited for estimating the ATT, though some are also available for estimating the ATE. Some matching methods distort the sample in such a way that the estimated treatment effect corresponds neither to the ATE nor to the ATT, but rather to the effect in an unspecified population (sometimes called the ATM, or average treatment effect in the remaining matched sample). When the target population is not so important (e.g., in the case of treatment effect discovery), such methods may be attractive; otherwise, care should be taken in ensuring the effect generalizes to the target population of interest. Different matching methods allow for different target populations, so it is important to choose a matching method that allows one to estimate the desired effect. See @greiferChoosingEstimandWhen2021 for guidance on making this choice. **Selecting covariates to balance.** Selecting covariates carefully is critical for ensuring the resulting treatment effect estimate is free of confounding and can be validly interpreted as a causal effect. To estimate total causal effects, all covariates must be measured prior to treatment (or otherwise not be affected by the treatment). Covariates should be those that cause variation in the outcome and selection into treatment group; these are known as confounding variables. See @vanderweele2019 for a guide on covariate selection. Ideally these covariates are measured without error and are free of missingness. ## Check Initial Imbalance After planning and prior to matching, it can be a good idea to view the initial imbalance in one's data that matching is attempting to eliminate. We can do this using the code below: ```{r} # No matching; constructing a pre-match matchit object m.out0 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = NULL, distance = "glm") ``` The first argument is a `formula` relating the treatment to the covariates used in estimating the propensity score and for which balance is to be assessed. The `data` argument specifies the dataset where these variables exist. Typically, the `method` argument specifies the method of matching to be performed; here, we set it to `NULL` so we can assess balance prior to matching[^1]. The `distance` argument specifies the method for estimating the propensity score, a one-dimensional summary of all the included covariates, computed as the predicted probability of being the treated group given the covariates; here, we set it to `"glm"` for generalized linear model, which implements logistic regression by default[^2] (see `?distance` for other options). [^1]: Note that the default for `method` is `"nearest"` to perform nearest neighbor matching. To prevent any matching from taking place in order to assess pre-matching imbalance, `method` must be set to `NULL`. [^2]: Note that setting `distance = "logit"`, which was the default in `MatchIt` version prior to 4.0.0, will also estimate logistic regression propensity scores. Because it is the default, the `distance` argument can actually be omitted if logistic regression propensity scores are desired. Below we assess balance on the unmatched data using `summary()`: ```{r} # Checking balance prior to matching summary(m.out0) ``` We can see severe imbalances as measured by the standardized mean differences (`Std. Mean Diff.`), variance ratios (`Var. Ratio`), and empirical cumulative density function (eCDF) statistics. Values of standardized mean differences and eCDF statistics close to zero and values of variance ratios close to one indicate good balance, and here many of them are far from their ideal values. ## Matching Now, matching can be performed. There are several different classes and methods of matching, described in `vignette("matching-methods")`. Here, we begin by briefly demonstrating 1:1 nearest neighbor (NN) matching on the propensity score, which is appropriate for estimating the ATT. One by one, each treated unit is paired with an available control unit that has the closest propensity score to it. Any remaining control units are left unmatched and excluded from further analysis. Due to the theoretical balancing properties of the propensity score described by @rosenbaum1983, propensity score matching can be an effective way to achieve covariate balance in the treatment groups. Below we demonstrate the use of `matchit()` to perform nearest neighbor propensity score matching. ```{r} # 1:1 NN PS matching w/o replacement m.out1 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "nearest", distance = "glm") ``` We use the same syntax as before, but this time specify `method = "nearest"` to implement nearest neighbor matching, again using a logistic regression propensity score. Many other arguments are available for tuning the matching method and method of propensity score estimation. The matching outputs are contained in the `m.out1` object. Printing this object gives a description of the type of matching performed: ```{r} m.out1 ``` The key components of the `m.out1` object are `weights` (the computed matching weights), `subclass` (matching pair membership), `distance` (the estimated propensity score), and `match.matrix` (which control units are matched to each treated unit). How these can be used for estimating the effect of the treatment after matching is detailed in `vignette("estimating-effects")`. ## Assessing the Quality of Matches Although matching on the propensity score is often effective at eliminating differences between the treatment groups to achieve covariate balance, its performance in this regard must be assessed. If covariates remain imbalanced after matching, the matching is considered unsuccessful, and a different matching specification should be tried. `MatchIt` offers a few tools for the assessment of covariate balance after matching. These include graphical and statistical methods. More detail on the interpretation of the included plots and statistics can be found in `vignette("assessing-balance")`. In addition to covariate balance, the quality of the match is determined by how many units remain after matching. Matching often involves discarding units that are not paired with other units, and some matching options, such as setting restrictions for common support or calipers, can further decrease the number of remaining units. If, after matching, the remaining sample size is small, the resulting effect estimate may be imprecise. In many cases, there will be a trade-off between balance and remaining sample size. How to optimally choose among them is an instance of the fundamental bias-variance trade-off problem that cannot be resolved without substantive knowledge of the phenomena under study. Prospective power analyses can be used to determine how small a sample can be before necessary precision is sacrificed. To assess the quality of the resulting matches numerically, we can use the `summary()` function on `m.out1` as before. Here we set `un = FALSE` to suppress display of the balance before matching for brevity and because we already saw it. (Leaving it as `TRUE`, its default, would display balance both before and after matching.) ```{r} # Checking balance after NN matching summary(m.out1, un = FALSE) ``` At the top is a summary of covariate balance after matching. Although balance has improved for some covariates, in general balance is still quite poor, indicating that nearest neighbor propensity score matching is not sufficient for removing confounding in this dataset. The final column, `Std. Pair Diff`, displays the average absolute within-pair difference of each covariate. When these values are small, better balance is typically achieved and estimated effects are more robust to misspecification of the outcome model [@king2019; @rubin1973a]. Next is a table of the sample sizes before and after matching. The matching procedure left 244 control units unmatched. Ideally, unmatched units would be those far from the treated units and would require greater extrapolation were they to have been retained. We can visualize the distribution of propensity scores of those who were matched using `plot()` with `type = "jitter"`: ```{r} plot(m.out1, type = "jitter", interactive = FALSE) ``` We can visually examine balance on the covariates using `plot()` with `type = "qq"`: ```{r} plot(m.out1, type = "qq", interactive = FALSE, which.xs = c("age", "married", "re75")) ``` Points far from the solid diagonal line are the areas of the covariate distributions that differ between the treatment groups. Although `married` and `re75` appear to have improved balance after matching, the case is mixed for `age`. ### Trying a Different Matching Specification Given the poor performance of nearest neighbor matching in this example, we can try a different matching method or make other changes to the matching algorithm or distance specification. Below, we'll try full matching, which matches every treated unit to at least one control and every control to at least one treated unit [@hansen2004; @stuart2008a]. We'll also try a different link (probit) for the propensity score model. ```{r} # Full matching on a probit PS m.out2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "full", distance = "glm", link = "probit") m.out2 ``` We can examine balance on this new matching specification. ```{r} # Checking balance after full matching summary(m.out2, un = FALSE) ``` Balance is far better, as determined by the lower standardized mean differences and eCDF statistics. The balance should be reported when publishing the results of a matching analysis. This can be done either in a table, using the values resulting from `summary()`, or in a plot, such as a Love plot, which we can make by calling `plot()` on the `summary()` output: ```{r} plot(summary(m.out2)) ``` Love plots are a simple and straightforward way to summarize balance visually. See `vignette("assessing-balance")` for more information on how to customize `MatchIt`'s Love plot and how to use `cobalt`, a package designed specifically for balance assessment and reporting that is compatible with `MatchIt`. ## Estimating the Treatment Effect How treatment effects are estimated depends on what form of matching was performed. See `vignette("estimating-effects")` for information on the variety of way to estimate effects and standard errors after each type of matching and for several outcome types. After 1:1 matching without replacement (i.e., the first matching specification above), we can run a simple regression of the outcome on the treatment in the matched sample (i.e., including the matching weights). With continuous outcomes, it is often a good idea to also include the covariates used in the matching in the effect estimation, as doing so can provide additional robustness to slight imbalances remaining after the matching and can improve precision. Even though the 1:1 matching was not successful, we'll demonstrate here how to estimate a treatment effect after performing such an analysis. First, we'll extract the matched dataset from the `matchit` object using `match.data()`. This dataset only contains the matched units and adds columns for `distance`, `weights`, and `subclass` (described previously). ```{r} m.data1 <- match.data(m.out1) head(m.data1) ``` We can then estimate a treatment effect in this dataset using the standard regression functions in R, like `lm()` or `glm()`, being sure to include the matching weights (stored in the `weights` variable of the `match.data()` output) in the estimation[^3]. We recommend using cluster-robust standard errors for most analyses, with pair membership as the clustering variable; the `lmtest` and `sandwich` packages together make this straightforward. [^3]: With 1:1 nearest neighbor matching without replacement, excluding the matching weights does not change the estimates. For all other forms of matching, they are required, so we recommend always including them for consistency. ```{r} library("lmtest") #coeftest library("sandwich") #vcovCL fit1 <- lm(re78 ~ treat + age + educ + race + married + nodegree + re74 + re75, data = m.data1, weights = weights) coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ``` The coefficient on `treat` is the estimated ATT. The other coefficients and tests should not be interpreted or reported. Estimating standard errors with matched data is an area of ongoing development. Generally, the approach demonstrated above works well for continuous outcomes. See `vignette("estimating-effects")` for more information on how to estimate standard errors with each type of matching and with different outcome types. A benefit of matching is that the outcome model used to estimate the treatment effect is robust to misspecification when balance has been achieved. With 1:1 nearest neighbor matching, we failed to achieve balance, so one should be cautious about trusting the estimated effect. With full matching, we were able to achieve balance, so the effect estimate should depend less on the form of the outcome model used. Below we estimate the effect and standard error of the treatment effect after full matching. As before, we'll use functions from the `lmtest` and `sandwich` packages here because they provide a fairly general interface to estimating coefficients and standard errors. ```{r, message = FALSE} m.data2 <- match.data(m.out2) fit2 <- lm(re78 ~ treat + age + educ + race + married + nodegree + re74 + re75, data = m.data2, weights = weights) coeftest(fit2, vcov. = vcovCL, cluster = ~subclass) ``` Given the results of these two estimates, we would be inclined to trust the one resulting from the second analysis, i.e., using full matching, because better balance was achieved on all the variables, making the effect estimate less sensitive to the form of the outcome model we used. Effect estimation with nonlinear models (e.g., for binary or time-to-event event outcomes) is more complicated due to noncollapsibility of the estimated effects; including additional covariates in the model can change the meaning of the estimated effect. See `vignette("estimating-effects")` for more details. Note that for some models, effect and standard error estimation is still being researched. ## Reporting Results To report matching results in a manuscript or research report, a few key pieces of information are required. One should be as detailed as possible about the matching procedure and the decisions made to ensure the analysis is replicable and can be adequately assessed for soundness by the audience. Key pieces of information to include are 1) the matching specification used (including the method and any additional options, like calipers or common support restrictions), 2) the distance measure used (including how it was estimated e.g., using logistic regression for propensity scores), 3) which other matching methods were tried prior to settling on a final specification and how the choices were made, 4) the balance of the final matching specification (including standardized mean differences and other balance statistics for the variables, their powers, and their interactions; some of these can be reported as summaries rather than in full detail), 5) the number of matched, unmatched, and discarded units included in the effect estimation, and 6) the method of estimating the treatment effect and standard error or confidence interval (including the specific model used and the specific type of standard error). See @thoemmes2011 for a complete list of specific details to report. Below is an example of how we might write up the prior analysis: > We used propensity score matching to estimate the average marginal effect of the treatment on 1978 earnings on those who received it accounting for confounding by the included covariates. We first attempted 1:1 nearest neighbor propensity score matching without replacement with a propensity score estimated using logistic regression of the treatment on the covariates. This matching yielded poor balance, so we instead tried full matching on the propensity score, which yielded adequate balance, as indicated in Table 1 and Figure 1. The propensity score was estimated using a probit regression of the treatment on the covariates, which yielded better balance than did a logistic regression. After matching, all standardized mean differences for the covariates were below 0.1 and all standardized mean differences for squares and two-way interactions between covariates were below .15, indicating adequate balance. Full matching uses all treated and all control units, so no units were discarded by the matching. > > To estimate the treatment effect and its standard error, we fit a linear regression model with 1978 earnings as the outcome and the treatment and the covariates as additive predictors and included the full matching weights in the estimation. The coefficient on the treatment was taken to be the estimate of the treatment effect. The `lm()` function was used to estimate the effect, and a cluster-robust variance as implemented in the `vcovCL()` function in the `sandwich` package was used to estimate its standard error with matching stratum membership as the clustering variable. > > The estimated effect was \$1980 (SE = 756.1, p = .009), indicating that the average effect of the treatment for those who received it is to increase earnings. ## Conclusion Although we have covered the basics of performing a matching analysis here, to use matching to its full potential, the more advanced methods available in `MatchIt` should be considered. We recommend reading the other vignettes included here to gain a better understand of all the `MatchIt` has to offer and how to use it responsibly and effectively. As previously stated, the ease of using `MatchIt` does not imply that matching or causal inference in general are simple matters; matching is an advanced statistical technique that should be used with care and caution. We hope the capabilities of `MatchIt` ease and encourage the use of nonparametric preprocessing for estimating causal effects in a robust and well-justified way. ## References MatchIt/vignettes/matching-methods.Rmd0000644000176200001440000014032314145075073017523 0ustar liggesusers--- title: "Matching Methods" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Matching Methods} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) options(width = 200) ``` ## Introduction `MatchIt` implements several matching methods with a variety of options. Though the help pages for the individual methods describes each method and how they can be used, this vignette provides a broad overview of the available matching methods and their associated options. The choice of matching method depends on the goals of the analysis (e.g., the estimand, whether low bias or high precision is important) and the unique qualities of each dataset to be analyzed, so there is no single optimal choice for any given analysis. A benefit of nonparametric preprocessing through matching is that a number of matching methods can be tried and their quality assessed without consulting the outcome, reducing the possibility of capitalizing on chance while allowing for the benefits of an exploratory analysis in the design phase [@ho2007]. This vignette describes each matching method available in `MatchIt` and the various options that are allowed with matching methods and the consequences of their use. For a brief introduction to the use of `MatchIt` functions, see `vignette("MatchIt")`. For details on how to assess and report covariate balance, see `vignette("assessing-balance")`. For details on how to estimate treatment effects and standard errors after matching, see `vignette("estimating-effects")`. ## Matching Matching as implemented in `MatchIt` is a form of *subset selection*, that is, the pruning and weighting of units to arrive at a (weighted) subset of the units from the original dataset. Ideally, and if done successfully, subset selection produces a new sample where the treatment is unassociated with the covariates so that a comparison of the outcomes treatment and control groups is not confounded by the measured and balanced covariates. Although statistical estimation methods like regression can also be used to remove confounding due to measured covariates, @ho2007 argue that fitting regression models in matched samples reduces the dependence of the validity of the estimated treatment effect on the correct specification of the model. Matching is nonparametric in the sense that the estimated weights and pruning of the sample are not direct functions of estimated model parameters but rather depend on the organization of discrete units in the sample; this is in contrast to propensity score weighting (also known as inverse probability weighting), where the weights come more directly from the estimated propensity score model and therefore are more sensitive to its correct specification. These advantages, as well as the intuitive understanding of matching by the public compared to regression or weighting, make it a robust and effective way to estimate treatment effects. It is important to note that this implementation of matching differs from the methods described by Abadie and Imbens [-@abadie2006; -@abadie2016] and implemented in the `Matching` R package and `teffects` routine in Stata. That form of matching is *matching imputation*, where the missing potential outcomes for each unit are imputed using the observed outcomes of paired units. This is a critical distinction because matching imputation is a specific estimation method with its own effect and standard error estimators, in contrast to subset selection, which is a preprocessing method that does not require specific estimators and is broadly compatible with other parametric and nonparametric analyses. The benefits of matching imputation are that its theoretical properties (i.e., the rate of convergence and asymptotic variance of the estimator) are well understood, it can be used in a straightforward way to estimate not just the average treatment effect in the treated (ATT) but also the average treatment effect in the population (ATE), and additional effective matching methods can be used in the imputation (e.g., kernel matching). The benefits of matching as nonparametric preprocessing are that it is far more flexible with respect to the types of effects that can be estimated because it does not involve any specific estimator, its empirical and finite-sample performance has been examined in depth and is generally well understood, and it aligns well with the design of experiments, which are more familiar to non-technical audiences. In addition to subset selection, matching often (though not always) involves a form of *stratification*, the assignment of units to pairs or strata containing multiple units. The distinction between subset selection and stratification is described by @zubizarreta2014, who separate them into two separate steps. In `MatchIt`, with almost all matching methods, subset selection is performed by stratification; for example, treated units are paired with control units, and unpaired units are then dropped from the matched sample. With some methods, subclasses are used to assign matching or stratification weights to individual units, which increase or decrease each unit's leverage in a subsequent analysis. There has been some debate about the importance of stratification after subset selection; while some authors have argued that, with some forms of matching, pair membership is incidental [@stuart2008; @schafer2008], others have argued that correctly incorporating pair membership into effect estimation can improve the quality of inferences [@austin2014a; @wan2019]. For methods that allow it, `MatchIt` includes stratum membership as an additional output of each matching specification. How these strata can be used is detailed in `vignette("Estimating Effects")`. At the heart of `MatchIt` are three classes of methods: distance matching, stratum matching, and pure subset selection. *Distance matching* involves considering a focal group (usually the treated group) and selecting members of the non-focal group (i.e., the control group) to pair with each member of the focal group based on the *distance* between units, which can be computed in one of several ways. Members of either group that are not paired are dropped from the sample. Nearest neighbor (`method = "nearest"`), optimal pair (`method = "optimal"`), optimal full (`method = "full"`), and genetic matching (`method = "genetic"`) are the methods of distance matching implemented in `MatchIt`. Typically, only the average treatment in the treated (ATT) or average treatment in the control (ATC), if the control group is the focal group, can be estimated after distance matching in `MatchIt` (full matching is an exception, described later). *Stratum matching* involves creating strata based on unique values of the covariates and assigning units with those covariate values into those strata. Any units that are in strata that lack either treated or control units are then dropped from the sample. Strata can be formed using the raw covariates (`method = "exact"`), coarsened versions of the covariates (`method = "cem"`), or coarsened versions of the propensity score (`method = "subclass"`). When no units are discarded, either the ATT, ATC, or average treatment effect in the population (ATE) can be estimated after stratum matching, though often some units are discarded, especially with exact and coarsened exact matching, making the estimand less clear. For use in estimating marginal treatment effects after exact matching, stratification weights are computed for the matched units first by computing a new "stratum propensity score" for each unit, which is the proportion of treated units in its stratum. The formulas for computing inverse probability weights from standard propensity scores are then applied to the new stratum propensity scores to form the new weights. Pure subset selection involves selecting a subset of units form the original sample without considering the distance between individual units or strata that units might fall into. Subsets are selected to optimize a criterion subject to constraint on balance and remaining sample size. Cardinality and template matching (`method = "cardinality"`) are the methods of pure subset selection implemented in `MatchIt`. Both methods allow the user to specify the largest imbalance allowed in the resulting matched sample, and an optimization routine attempts to find the largest matched sample that satisfies those balance constraints. While cardinality matching does not target a specific estimand, template matching can be used to target the ATT, ATC, or ATE. Below, we describe each of the matching methods implemented in `MatchIt`. ## Matching Methods ### Nearest Neighbor Matching (`method = "nearest"`) Nearest neighbor matching is also known as greedy matching. It involves running through the list of treated units and selecting the closest eligible control unit to be paired with each treated unit. It is greedy in the sense that each pairing occurs without reference to how other units will be or have been paired, and therefore does not aim to optimize any criterion. Nearest neighbor matching is the most common form of matching used [@thoemmes2011; @zakrison2018] and has been extensively studied through simulations. See `?method_nearest` for the documentation for `matchit()` with `method = "nearest"`. Nearest neighbor matching requires the specification of a distance measure to define which control unit is closest to each treated unit. The default and most common distance is the *propensity score difference*, which is the difference between the propensity scores of each treated and control unit [@stuart2010]. Another popular distance is the Mahalanobis distance, described in the section "Mahalanobis distance matching" below. The order in which the treated units are to be paired must also be specified and has the potential to change the quality of the matches [@austin2013b; @rubin1973]; this is specified by the `m.order` argument. With propensity score matching, the default is to go in descending order from the highest propensity score; doing so allows the units that would have the hardest time finding close matches to be matched first [@rubin1973]. Other orderings are possible, including random ordering, which can be tried multiple times until an adequate matched sample is found. When matching with replacement (i.e., where each control unit can be reused to be matched with any number of treated units), the matching order doesn't matter. When using a matching ratio greater than 1 (i.e., when more than 1 control units are requested to be matched to each treated unit), matching occurs in a cycle, where each treated unit is first paired with one control unit, and then each treated unit is paired with a second control unit, etc. Ties are broken deterministically based on the order of the units in the dataset to ensure that multiple runs of the same specification yield the same result (unless the matching order is requested to be random). ### Optimal Pair Matching (`method = "optimal"`) Optimal pair matching (often just called optimal matching) is very similar to nearest neighbor matching in that it attempts to pair each treated unit with one or more control units. Unlike nearest neighbor matching, however, it is "optimal" rather than greedy; it is optimal in the sense that it attempts to choose matches that collectively optimize an overall criterion [@hansen2006; @gu1993]. The criterion used is the sum of the absolute pair distances in the matched sample. See `?method_optimal` for the documentation for `matchit()` with `method = "optimal"`. Optimal pair matching in `MatchIt` depends on the `fullmatch()` function in the `optmatch` package [@hansen2006]. Like nearest neighbor matching, optimal pair matching requires the specification of a distance measure between units. Optimal pair matching can be thought of simply as an alternative to selecting the order of the matching for nearest neighbor matching. Optimal pair matching and nearest neighbor matching often yield the same or very similar matched samples; indeed, some research has indicated that optimal pair matching is not much better than nearest neighbor matching at yielding balanced matched samples [@austin2013b]. The `tol` argument in `fullmatch()` can be supplied to `matchit()` with `method = "optimal"`; this controls the numerical tolerance used to determine whether the optimal solution has been found. The default is fairly high and, for smaller problems, should be set much lower (e.g., by setting `tol = 1e-7`). ### Optimal Full Matching (`method = "full"`) Optimal full matching (often just called full matching) assigns every treated and control unit in the sample to one subclass each [@hansen2004; @stuart2008a]. Each subclass contains one treated unit and one or more control units or one control units and one or more treated units. It is optimal in the sense that the chosen number of subclasses and the assignment of units to subclasses minimize the sum of the absolute within-subclass distances in the matched sample. Weights are computed based on subclass membership, and these weights then function like propensity score weights and can be used to estimate a weighted treatment effect, ideally free of confounding by the measured covariates. See `?method_full` for the documentation for `matchit()` with `method = "full"`. Optimal full matching in `MatchIt` depends on the `fullmatch()` function in the `optmatch` package [@hansen2006]. Like the other distance matching methods, optimal full matching requires the specification of a distance measure between units. It can be seen a combination of distance matching and stratum matching: subclasses are formed with varying numbers of treated and control units, as with stratum matching, but the subclasses are formed based on minimizing within-pair distances and do not involve forming strata based on any specific variable, similar to distance matching. Unlike other distance matching methods, full matching can be used to estimate the ATE. Full matching can also be seen as a form of propensity score weighting that is less sensitive to the form of the propensity score model because the original propensity scores are used just to create the subclasses, not to form the weights directly [@austin2015a]. In addition, full matching does not have to rely on estimated propensity scores to form the subclasses and weights; other distance measures are allowed as well. Although full matching uses all available units, there is a loss in precision due to the weights. Units may be weighted in such a way that they contribute less to the sample than would unweighted units, so the effective sample size (ESS) of the full matching weighted sample may be lower than even that of 1:1 pair matching. Balance is often far better after full matching than it is with 1:k matching, making full matching a good option to consider especially when 1:k matching is not effective or when the ATE is the target estimand. The specification of the full matching optimization problem can be customized by supplying additional arguments that are passed to `optmatch::fullmatch()`, such as `min.controls`, `max.controls`, `mean.controls`, and `omit.fraction`. As with optimal pair matching, the numerical tolerance value can be set much lower than the default with small problems by setting, e.g., `tol = 1e-7`. ### Genetic Matching (`method = "genetic"`) Genetic matching is less a specific form of matching and more a way of specifying a distance measure for another form of matching. In practice, though, the form of matching used is nearest neighbor pair matching. Genetic matching uses a genetic algorithm, which is an optimization routine used for non-differentiable objective functions, to find scaling factors for each variable in a generalized Mahalanobis distance formula [@diamond2013]. The criterion optimized by the algorithm is one based on covariate balance. Once the scaling factors have been found, nearest neighbor matching is performed on the scaled generalized Mahalanobis distance. See `?method_genetic` for the documentation for `matchit()` with `method = "genetic"`. Genetic matching in `MatchIt` depends on the `GenMatch()` function in the `Matching` package [@sekhon2011] to perform the genetic search and uses the `Matching` function to perform the nearest neighbor match using the scaled generalized Mahalanobis distance. Genetic matching considers the generalized Mahalanobis distance between a treated unit $i$ and a control unit $j$ as $$\delta_{GMD}(\mathbf{x}_i,\mathbf{x}_j, \mathbf{W})=\sqrt{(\mathbf{x}_i - \mathbf{x}_j)'(\mathbf{S}^{-1/2})'\mathbf{W}(\mathbf{S}^{-1/2})(\mathbf{x}_i - \mathbf{x}_j)}$$ where $\mathbf{x}$ is a $p \times 1$ vector containing the value of each of the $p$ included covariates for that unit, $\mathbf{S}^{-1/2}$ is the Cholesky decomposition of the covariance matrix $\mathbf{S}$ of the covariates, and $\mathbf{W}$ is a diagonal matrix with scaling factors $w$ on the diagonal: $$ \mathbf{W}=\begin{bmatrix} w_1 & & & \\ & w_2 & & \\ & & \ddots &\\ & & & w_p \\ \end{bmatrix} $$ When $w_k=1$ for all covariates $k$, the computed distance is the standard Mahalanobis distance between units. Genetic matching estimates the optimal values of the $w_k$s, where a user-specified criterion is used to define what is optimal. The default is to maximize the smallest p-value among balance tests for the covariates in the matched sample (both Kolmogorov-Smirnov tests and t-tests for each covariate). In `MatchIt`, if a propensity score is specified, the default is to include the propensity score and the covariates in $\mathbf{x}$ and to optimize balance on the covariates. When `distance = "mahalanobis"` or the `mahvars` argument is specified, the propensity score is left out of $\mathbf{x}$. In all other respects, genetic matching functions just like nearest neighbor matching except that the matching itself is carried out by `Matching::Match()` instead of by `MatchIt`. When using `method = "genetic"` in `MatchIt`, additional arguments passed to `Matching::GenMatch()` to control the genetic search process should be specified; in particular, the `pop.size` argument should be increased from its default of 100 to a much higher value. Doing so will make the algorithm take more time to finish but will generally improve the quality of the resulting matches. Different functions can be supplied to be used as the objective in the optimization using the `fit.func` argument. ### Exact Matching (`method = "exact"`) Exact matching is a form of stratum matching that involves creating subclasses based on unique combinations of covariate values and assigning each unit into their corresponding subclass so that only units with identical covariate values are placed into the same subclass. Any units that are in subclasses lacking either treated or control units will be dropped. Exact matching is the most powerful matching method in that no functional form assumptions are required on either the treatment or outcome model for the method to remove confounding due to the measured covariates; the covariate distributions are exactly balanced. The problem with exact matching is that in general, few if any units will remain after matching, so the estimated effect will only generalize to a very limited population and can lack precision. Exact matching is particularly ineffective with continuous covariates, for which it might be that no two units have the same value, and with many covariates, for which it might be the case that no two units have the same combination of all covariates; this latter problem is known as the "curse of dimensionality". See `?method_exact` for the documentation for `matchit()` with `method = "exact"`. It is possible to use exact matching on some covariates and another form of matching on the rest. This makes it possible to have exact balance on some covariates (typically categorical) and approximate balance on others, thereby gaining the benefits of both exact matching and the other matching method used. To do so, the other matching method should be specified in the `method` argument to `matchit()` and the `exact` argument should be specified to contain the variables on which exact matching is to be done. ### Coarsened Exact Matching (`method = "cem"`) Coarsened exact matching (CEM) is a form of stratum matching that involves first coarsening the covariates by creating bins and then performing exact matching on the new coarsened versions of the covariates [@iacus2012]. The degree and method of coarsening can be controlled by the user to manage the trade-off between exact and approximate balancing. For example, coarsening a covariate to two bins will mean that units that differ greatly on the covariate might be placed into the same subclass, while coarsening a variable to five bins may require units to be dropped due to not finding matches. Like exact matching, CEM is susceptible to the curse of dimensionality, making it a less viable solution with many covariates, especially with few units. Dropping units can also change the target population of the estimated effect. See `?method_cem` for the documentation for `matchit()` with `method = "cem"`. CEM in `MatchIt` does not depend on any other package to perform the coarsening and matching, though it used to rely on the `cem` package. ### Subclassification (`method = "subclass"`) Propensity score subclassification can be thought of as a form of coarsened exact matching with the propensity score as the sole covariate to be coarsened and matched on. The bins are usually based on specified quantiles of the propensity score distribution either in the treated group, control group, or overall, depending on the desired estimand. Propensity score subclassification is an old and well-studied method, though it can perform poorly compared to other, more modern propensity score methods such as full matching and weighting [@austin2010]. See `?method_subclass` for the documentation for `matchit()` with `method = "subclass"`. The binning of the propensity scores is typically based on dividing the distribution of covariates into approximately equally sized bins. The user specifies the number of subclasses using the `subclass` argument and which group should be used to compute the boundaries of the bins using the `estimand` argument. Sometimes, subclasses can end up with no units from one of the treatment groups; by default, `matchit()` moves a unit from an adjacent subclass into the lacking one to ensure that each subclass has at least one unit from each treatment group. The minimum number of units required in each subclass can be chosen by the `min.n` argument to `matchit()`. If set to 0, an error will be thrown if any subclass lacks units from one of the treatment groups. Moving units from one subclass to another generally worsens the balance in the subclasses but can increase precision. The default number of subclasses is 6, which is arbitrary and should not be taken as a recommended value. Although early theory has recommended the use of 5 subclasses, in general there is an optimal number of subclasses that is typically much larger than 5 but that varies among datasets [@orihara2021]. Rather than trying to figure this out for oneself, one can use optimal full matching (i.e., with `method = "full"`) to optimally create subclasses with one treated or one control unit that optimize a within-subclass distance criterion. The output of propensity score subclassification includes the assigned subclasses and the subclassification weights. Effects can be estimated either within each subclass and then averaged across them, or a single marginal effect can be estimated using the subclassification weights. This latter method has been called marginal mean weighting through subclassification [MMWS; @hong2010] and fine stratification weighting [@desai2017]. It is also implemented in the `WeightIt` package. ### Cardinality and Template Matching (`method = "cardinality"`) Cardinality and template matching are pure subset selection methods that involve selecting a subset of the original sample without considering the distance between individual units or assigning units to pairs or subclasses. They can be thought of as a weighting method where the weights are restricted to be zero or one. Cardinality matching involves finding the largest sample that satisfies user-supplied balance constraints and constraints on the ratio of matched treated to matched control units [@zubizarretaMatchingBalancePairing2014]. It does not consider a specific estimand and can be a useful alternative to matching with a caliper for handling data with little overlap [@visconti2018]. Template matching involves identifying a target distribution (e.g., the full sample for the ATE or the treated units for the ATT) and finding the largest subset of the treated and control groups that satisfy user-supplied balance constraints with respect to that target [@bennettBuildingRepresentativeMatched2020]. See `?method_cardinality` for the documentation for using `matchit()` with `method = "cardinality"`, including which inputs are required to request either cardinality matching or template matching. Subset selection is performed by solving a mixed integer programming optimization problem with linear constraints. The problem involves maximizing the size of the matched sample subject to constraints on balance and sample size. For cardinality matching, the balance constraints refer to the mean difference for each covariate between the matched treated and control groups, and the sample size constraints require the matched treated and control groups to be the same size (or differ by a user-supplied factor). For template matching, the balance constraints refer to the mean difference for each covariate between each treatment group and the target distribution; for the ATE, this requires the mean of each covariate in each treatment group to be within a given tolerance of the mean of the covariate in the full sample, and for the ATT, this requires the mean of each covariate in the control group to be within a given tolerance of the mean of the covariate in the treated group, which is left intact. The balance tolerances are controlled by the `tols` and `std.tols` arguments. The optimization problem requires a special solver to solve. Currently, the available options in `MatchIt` are the GLPK solver (through the `Rglpk` package), the SYMPHONY solver (through the `Rsymphony` package), and the Gurobi solver (through the `gurobi` package). The differences among the solvers are in performance; Gurobi is by far the best (fastest, least likely to fail to find a solution), but it is proprietary (though has a free trial and academic license) and is a bit more complicated to install. The `designmatch` package also provides an implementation of cardinality matching with more options than `MatchIt` offers. ## Customizing the Matching Specification In addition to the specific matching method, other options are available for many of the matching methods to further customize the matching specification. These include different specifications of the distance measure, methods to perform alternate forms of matching in addition to the main method, prune units far from other units prior to matching, restrict possible matches, etc. Not all options are compatible with all matching methods. ### Specifying the propensity score or other distance measure (`distance`) The distance measure is used to define how close two units are. In nearest neighbor matching, this is used to choose the nearest control unit to each treated unit. In optimal matching, this is used in the criterion that is optimized. By default, the distance measure is the propensity score difference, and the argument supplied to `distance` corresponds to the method of estimating the propensity score. In `MatchIt`, propensity scores are often labeled as "distance" values, even though the propensity score itself is not a distance measure. This is to reflect that the propensity score is used in creating the distance value, but other scores could be used, such as prognostic scores for prognostic score matching [@hansen2008a]. The propensity score is more like a "position" value, in that it reflects the position of each unit in the matching space, and the difference between positions is the distance between them. If the the argument to `distance` is one of the allowed values (see `?distance` for these values) other than `"mahalanobis"` or is a numeric vector with one value per unit, the distance between units will be computed as the pairwise difference between propensity scores or the supplied values. Propensity scores are also used in propensity score subclassification and can optionally be used in genetic matching as a component of the generalized Mahalanobis distance. For exact, coarsened exact, and cardinality matching, the `distance` argument is ignored. The default `distance` argument is `"glm"`, which estimates propensity scores using logistic regression or another generalized linear model. The `link` and `distance.options` arguments can be supplied to further specify the options for the propensity score models, including whether to use the raw propensity score or a linearized version of it (e.g., the logit of a logistic regression propensity score, which has been commonly referred to and recommended in the propensity score literature [@austin2011a; @stuart2010]). Allowable options for the propensity score model include parametric and machine learning-based models, each of which have their strengths and limitations and may perform differently depending on the unique qualities of each dataset. We recommend multiple types of models be tried to find one that yields the best balance, as there is no way to make a single recommendation that will work for all cases. The `distance` argument can also be specified as `"mahalanobis"`. For nearest neighbor and optimal (full or pair) matching, this triggers `matchit()` not to estimate propensity scores but rather to use just the Mahalanobis distance, which is defined for a treated unit $i$ and a control unit $j$ as $$\delta_{MD}(\mathbf{x}_i,\mathbf{x}_j)=\sqrt{(\mathbf{x}_i - \mathbf{x}_j)'S^{-1}(\mathbf{x}_i - \mathbf{x}_j)}$$ where $\mathbf{x}$ is a $p \times 1$ vector containing the value of each of the $p$ included covariates for that unit, $S$ is the covariance matrix among all the covariates, and $S^{-1}$ is the (generalized) inverse of $S$. The R function `mahalanobis()` is used to compute this. Mahalanobis distance matching tends to work better with continuous covariates than with categorical covariates. For creating close pairs, Mahalanobis distance matching tends work better than propensity score matching because Mahalanobis distance-paired units will have close values on all of the covariates, whereas propensity score-paired units may be close on the propensity score but not on any of the covariates themselves. This feature was the basis of King and Nielsen's [-@king2019] warning against using propensity scores for matching. `distance` can also be supplied as a matrix of distance values between units. This makes it possible to use handcrafted distance matrices or distances created outside `MatchIt`, e.g., using `optmatch::match_on()`. Only nearest neighbor, optimal pair, and optimal full matching allow this specification. The propensity score can have uses other than as the basis for matching. It can be used to define a region of common support, outside which units are dropped prior to matching; this is implemented by the `discard` option. It can also be used to define a caliper, the maximum distance two units can be before they are prohibited from being paired with each other; this is implemented by the `caliper` argument. To estimate or supply a propensity score for one of these purposes but not use it as the distance measure for matching (e.g., to perform Mahalanobis distance matching instead), the `mahvars` argument can be specified. These options are described below. ### Implementing common support restrictions (`discard`) The region of *common support* is the region of overlap between treatment groups. A common support restriction discards units that fall outside of the region of common support, preventing them from being matched to other units and included in the matched sample. This can reduce the potential for extrapolation and help the matching algorithms to avoid overly distant matches from occurring. In `MatchIt`, the `discard` option implements a common support restriction based on the propensity score. The argument can be supplied as `"treated"`, `"control"`, or `"both"`, which discards units in the corresponding group that fall outside the region of common support for the propensity score. The `reestimate` argument can be supplied to choose whether to re-estimate the propensity score in the remaining units. **If units from the treated group are discarded based on a common support restriction, the estimand no longer corresponds to the ATT.** ### Caliper matching (`caliper`) A *caliper* can be though of as a ring around each unit that limits to which other units that unit can be paired. Calipers are based on the propensity score or other covariates. Two units whose distance on a calipered covariate is larger than the caliper width for that covariate are not allowed to be matched to each other. Any units for which there are no available matches within the caliper are dropped from the matched sample. Calipers ensure paired units are close to each other on the calipered covariates, which can ensure good balance in the matched sample. Multiple variables can be supplied to `caliper` to enforce calipers on all of them simultaneously. Using calipers can be a good alternative to exact or coarsened exact matching to ensure only similar units are paired with each other. The `std.caliper` argument controls whether the provided calipers are in raw units or standard deviation units. **If units from the treated group are left unmatched due to a caliper, the estimand no longer corresponds to the ATT.** ### Mahalanobis distance matching (`mahvars`) To perform Mahalanobis distance matching without the need to estimate or use a propensity score, the `distance` argument can be set to `"mahalanobis"`. If a propensity score is to be estimated or used for a different purpose, such as in a common support restriction or a caliper, but you still want to perform Mahalanobis distance matching, variables should be supplied to the `mahvars` argument. The propensity scores will be generated using the `distance` specification, and matching will occur not on the covariates supplied to the main formula of `matchit()` but rather on the covariates supplied to `mahvars`. To perform Mahalanobis distance matching within a propensity score caliper, for example, the `distance` argument should be set to the method of estimating the propensity score (e.g., `"glm"` for logistic regression), the `caliper` argument should be specified to the desired caliper width, and `mahvars` should be specified to perform Mahalanobis distance matching on the desired covariates within the caliper. ### Exact matching (`exact`) To perform exact matching on all supplied covariates, the `method` argument can be set to `"exact"`. To perform exact matching only on some covariates and some other form of matching within exact matching strata on other covariates, the `exact` argument can be used. Covariates supplied to the `exact` argument will be matched exactly, and the form of matching specified by `method` (e.g., `"nearest"` for nearest neighbor matching) will take place within each exact matching stratum. This can be a good way to gain some of the benefits of exact matching without completely succumbing to the curse of dimensionality. As with exact matching performed with `method = "exact"`, any units in strata lacking members of one of the treatment groups will be left unmatched. Note that although matching occurs within each exact matching stratum, propensity score estimation and computation of the Mahalanobis distance occur in the full sample. **If units from the treated group are unmatched due to an exact matching restriction, the estimand no longer corresponds to the ATT.** ### Anti-exact matching (`antiexact`) Anti-exact matching adds a restriction such that a treated and control unit with same values of any of the specified anti-exact matching variables cannot be paired. This can be useful when finding comparison units outside of a unit's group, such as when matching units in one group to units in another when units within the same group might otherwise be close matches. See examples [here](https://stackoverflow.com/questions/66526115/propensity-score-matching-with-panel-data) and [here](https://stackoverflow.com/questions/61120201/avoiding-duplicates-from-propensity-score-matching?rq=1). ### Matching with replacement (`replace`) Nearest neighbor matching and genetic matching have the option of matching with or without replacement, and this is controlled by the `replace` argument. Matching without replacement means that each control unit is matched to only one treated unit, while matching with replacement means that control units can be reused and matched to multiple treated units. Matching without replacement carries certain statistical benefits in that weights for each unit can be omitted or are more straightforward to include and dependence between units depends only on pair membership. Special standard error estimators are sometimes required for estimating effects after matching with replacement [@austin2020a], and methods for accounting for uncertainty are not well understood for non-continuous outcomes. Matching with replacement will tend to yield better balance though, because the problem of "running out" of close control units to match to treated units is avoided, though the reuse of control units will decrease the effect sample size, thereby worsening precision. (This problem occurs in the Lalonde dataset used in `vignette("MatchIt")`, which is why nearest neighbor matching without replacement is not very effective there.) After matching with replacement, control units are assigned to more than one subclass, so the `get_matches()` function should be used instead of `match.data()` after matching with replacement if subclasses are to be used in follow-up analyses; see `vignette("Estimating Effects")` for details. The `reuse.max` argument can also be used with `method = "nearest"` to control how many times each control unit can be reused as a match. Setting `reuse.max = 1` is equivalent to requiring matching without replacement (i.e., because each control can be used only once). Other values allow control units to be matched more than once, though only up to the specified number of times. Higher values will tend to improve balance at the cost of precision. ### $k$:1 matching (`ratio`) The most common form of matching, 1:1 matching, involves pairing one control unit with each treated unit. To perform $k$:1 matching (e.g., 2:1 or 3:1), which pairs (up to) $k$ control units with each treated unit, the `ratio` argument can be specified. Performing $k$:1 matching can preserve precision by preventing too many control units from being unmatched and dropped from the matched sample, though the gain in precision by increasing $k$ diminishes rapidly after 4 [@rosenbaum2020]. Importantly, for $k>1$, the matches after the first match will generally be worse than the first match in terms of closeness to the treated unit, so increasing $k$ can also worsen balance. @austin2010a found that 1:1 or 1:2 matching generally performed best in terms of mean squared error. In general, it makes sense to use higher values of $k$ while ensuring that balance is satisfactory. With nearest neighbor and optimal pair matching, variable $k$:1 matching, in which the number of controls matched to each treated unit varies, can also be used; this can have improved performance over "fixed" $k$:1 matching [@ming2000]. See `?method_nearest` and `?method_optimal` for information on implementing variable $k$:1 matching. ## Choosing a Matching Method Choosing the best matching method for one's data depends on the unique characteristics of the dataset as well as the goals of the analysis. For example, because different matching methods can target different estimands, when certain estimands are desired, specific methods must be used. On the other hand, some methods may be more effective than others when retaining the target estimand is less important. Below we provide some guidance on choosing a matching method. Remember that multiple methods can (and should) be tried as long as the treatment effect is not estimated until a method has been settled on. The criteria on which a matching specification should be judged are balance and remaining (effective) sample size after matching. Assessing balance is described in `vignette("assessing-balance")`. A typical workflow is similar to that demonstrated in `vignette("MatchIt")`: try a matching method, and if it yields poor balance or an unacceptably low remaining sample size, try another, until a satisfactory specification has been found. It is important to assess balance broadly (i.e., beyond comparing the means of the covariates in the treated and control groups), and the search for a matching specification should not stop when a threshold is reached, but should attempt to come as close as possible to perfect balance [@ho2007]. Even if the first matching specification appears successful at reducing imbalance, there may be another specification that could reduce it even further, thereby increasing the robustness of the inference and the plausibility of an unbiased effect estimate. If the target of inference is the ATE, full matching, subclassification, and template matching can be used. If the target of inference is the ATT or ATC, any matching method may be used. When retaining the target estimand is not so important, additional options become available that involve discarding units in such a way that the original estimand is distorted. These include matching with a caliper, matching within a region of common support, cardinality matching, or exact or coarsened exact matching, perhaps on a subset of the covariates. Because exact and coarsened exact matching aim to balance the entire joint distribution of covariates, they are the most powerful methods. If it is possible to perform exact matching, this method should be used. If continuous covariates are present, coarsened exact matching can be tried. Care should be taken with retaining the target population and ensuring enough matched units remain; unless the control pool is much larger than the treated pool, it is likely some (or many) treated units will be discarded, thereby changing the estimand and possibly dramatically reducing precision. These methods are typically only available in the most optimistic of circumstances, but they should be used first when those circumstances arise. It may also be useful to combine exact or coarsened exact matching on some covariates with another form of matching on the others (i.e., by using the `exact` argument). When estimating the ATE, either subclassification, full matching, or template matching can be used. Full matching can be effective because it optimizes a balance criterion, often leading to better balance. With full matching, it's also possible to exact match on some variables and match using the Mahalanobis distance, eliminating the need to estimate propensity scores. Template matching also ensures good balance, but because units are only given weights of zero one, a solution may not be feasible and many units may have to be discarded. For large datasets, neither full matching nor template matching may be possible, in which case subclassification is a faster solution. When using subclassification, the number of subclasses should be varied. With large samples, higher numbers of subclasses tend to yield better performance; one should not immediately settle for the default (6) or the often-cited recommendation of 5 without trying several other numbers. When estimating the ATT, a variety of methods can be tried. Genetic matching can perform well at achieving good balance because it directly optimizes covariate balance. With larger datasets, it may take a long time to reach a good solution (though that solution will tend to be good as well). Template matching also will achieve good balance if a solution is feasible because balance is controlled by the user. Optimal pair matching and nearest neighbor matching without replacement tend to perform similarly to each other; nearest neighbor matching may be preferable for large datasets that cannot be handled by optimal matching. Nearest neighbor, optimal, and genetic matching allow some customizations like including covariates on which to exactly match, using the Mahalanobis distance instead of a propensity score difference, and performing $k$:1 matching with $k>1$. Nearest neighbor matching with replacement, full matching, and subclassification all involve weighting the control units with nonuniform weights, which often allows for improved balancing capabilities but can be accompanied by a loss in effective sample size, even when all units are retained. There is no reason not to try many of these methods, varying parameters here and there, in search of good balance and high remaining sample size. As previously mentioned, no single method can be recommended above all others because the optimal specification depends on the unique qualities of each dataset. When the target population is less important, for example, when engaging in treatment effect discovery or when when the sampled population is not of particular interest (e.g., it corresponds to an arbitrarily chosen hospital or school; see @mao2018 for these and other reasons why retaining the target population may not be important), other methods that do not retain the characteristics of the original sample become available. These include matching with a caliper (on the propensity score or on the covariates themselves), cardinality matching, and more restrictive forms of matching like exact and coarsened exact matching, either on all covariates or just a subset, that are prone to discard units from the sample in such a way that the target population is changed. @austin2013b and Austin and Stuart [-@austin2015c; -@austin2015a] have found that caliper matching can be a particularly effective modification to nearest neighbor matching for eliminating imbalance and reducing bias when the target population is less relevant, but when inference to a specific target population is desired, using calipers can induce bias due to incomplete matching [@rosenbaum1985; @wang2020]. Cardinality matching can be particularly effective in data with little overlap between the treatment groups [@visconti2018] and can perform better than caliper matching [@delosangelesresaDirectStableWeight2020]. It is important not to rely excessively on theoretical or simulation-based findings or specific recommendations when making choices about the best matching method to use. For example, although nearest neighbor matching without replacement balance covariates better than did subclassification with five or ten subclasses in Austin's [-@austin2009c] simulation, this does not imply it will be superior in all datasets. Likewise, though @rosenbaum1985a and @austin2011a both recommend using a caliper of .2 standard deviations of the logit of the propensity score, this does not imply that caliper will be optimal in all scenarios, and other widths should be tried, though it should be noted that tightening the caliper on the propensity score can sometimes degrade performance [@king2019]. ## Reporting the Matching Specification When reporting the results of a matching analysis, it is important to include the relevant details of the final matching specification and the process of arriving at it. Using `print()` on the `matchit` object synthesizes information on how the above arguments were used to provide a description of the matching specification. It is best to be as specific as possible to ensure the analysis is replicable and to allow audiences to assess its validity. Although citations recommending specific matching methods can be used to help justify a choice, the only sufficient justification is adequate balance and remaining sample size, regardless of published recommendations for specific methods. See `vignette("assessing-balance")` for instructions on how to assess and report the quality of a matching specification. After matching and estimating an effect, details of the effect estimation must be included as well; see `vignette("estimating-effects")` for instructions on how to perform and report on the analysis of a matched dataset. ## References MatchIt/vignettes/sampling-weights.Rmd0000644000176200001440000003036714067017372017561 0ustar liggesusers--- title: "Matching with Sampling Weights" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Matching with Sampling Weights} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{=html} ``` ```{r, include = FALSE} knitr::opts_chunk$set(echo = TRUE, eval=T) options(width = 200, digits = 4) #Generatng data similar to Austin (2009) for demonstrating treatment effect estimation with sampling weights gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 gen_SW <- function(X) { e <- rbinom(nrow(X), 1, .3) 1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e + -log(.5)*e*X[,2] + log(.6)*e*X[,4]) } set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) SW <- gen_SW(X) Y_C <- gen_Y_C(A, X) d <- data.frame(A, X, Y_C, SW) ``` ## Introduction Sampling weights (also known as survey weights) frequently appear when using large, representative datasets. They are required to ensure any estimated quantities generalize to a target population defined by the weights. Evidence suggests that sampling weights need to be incorporated into a propensity score matching analysis to obtain valid and unbiased estimates of the treatment effect in the sampling weighted population [@dugoff2014; @austin2016; @lenis2019]. In this guide, we demonstrate how to use sampling weights with `MatchIt` for propensity score estimation, balance assessment, and effect estimation. Fortunately, doing so is not complicated, but some care must be taken to ensure sampling weights are incorporated correctly. It is assumed one has read the other vignettes explaining matching (`vignette("matching-methods")`), balance assessment (`vignette("assessing-balance")`), and effect estimation (`vignette("estimating-effects")`. We will use the same simulated toy dataset used in `vignette("estimating-effects")` except with the addition of a sampling weights variable, `SW`, which is used to generalize the sample to a specific target population with a distribution of covariates different from that of the sample. Code to generate the covariates, treatment, and outcome is at the bottom of `vignette("estimating-effects")` and code to generate the sampling weights is at the end of this document. We will consider the effect of binary treatment `A` on continuous outcome `Y_C`, adjusting for confounders `X1`-`X9`. ```{r,message=FALSE,warning=FALSE} head(d) library("MatchIt") library("lmtest") library("sandwich") ``` ## Matching When using sampling weights with propensity score matching, one has the option of including the sampling weights in the model used to estimate the propensity scores. Although evidence is mixed on whether this is required [@austin2016; @lenis2019], it can be a good idea. The choice should depend on whether including the sampling weights improves the quality of the matches. Specifications including and excluding sampling weights should be tried to determine which is preferred. To supply sampling weights to the propensity score-estimating function in `matchit()`, the sampling weights variable should be supplied to the `s.weights` argument. It can be supplied either as a numerical vector containing the sampling weights, or a string or one-sided formula with the name of the sampling weights variable in the supplied dataset. Below we demonstrate including sampling weights into propensity scores estimated using logistic regression for optimal full matching for the average treatment effect in the population (ATE) (note that all methods and steps apply the same way to all forms of matching and all estimands). ```{r} mF_s <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", distance = "glm", estimand = "ATE", s.weights = ~SW) mF_s ``` Notice that the description of the matching specification when the `matchit` object is printed includes lines indicating that the sampling weights were included in the estimation of the propensity score and that they are present in the `matchit` object. It is stored in the `s.weights` component of the `matchit` object. Note that at this stage, the matching weights (stored in the `weights` component of the `matchit` object) do not incorporate the sampling weights; they are calculated simply as a result of the matching. Now let's perform full matching on a propensity score that does not include the sampling weights in its estimation. Here we use the same specification as was used in `vignette("estimating-effects")`. ```{r} mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", distance = "glm", estimand = "ATE") mF ``` Notice that there is no mention of sampling weights in the description of the matching specification. However, to properly assess balance and estimate effects, we need the sampling weights to be included in the `matchit` object, even if they were not used at all in the matching. To do so, we use the function `add_s.weights()`, which adds sampling weights to the supplied `matchit` objects. ```{r} mF <- add_s.weights(mF, ~SW) mF ``` Now when we print the `matchit` object, we can see lines have been added identifying that sampling weights are present but they were not used in the estimation of the propensity score used in the matching. Note that not all methods can involve sampling weights in the estimation. Only methods that use the propensity score will be affected by sampling weights; coarsened exact matching or Mahalanobis distance optimal pair matching, for example, ignore the sampling weights, and some propensity score estimation methods, like `randomForest` and `bart` (as presently implemented), cannot incorporate sampling weights. Sampling weights should still be supplied to `matchit()` even when using these methods to avoid having to use `add_s.weights()` and remembering which methods do or do not involve sampling weights. ## Assessing Balance Now we need to decide which matching specification is the best to use for effect estimation. We do this by selecting the one that yields the best balance without sacrificing remaining effective sample size. Because the sampling weights are incorporated into the `matchit` object, the balance assessment tools in `plot.matchit()` and `summary.matchit()` incorporate them into their output. We'll use `summary()` to examine balance on the two matching specifications. With sampling weights included, the balance statistics for the unmatched data are weighted by the sampling weights. The balance statistics for the matched data are weighted by the product of the sampling weights and the matching weights. It is the product of these weights that will be used in estimating the treatment effect. Below we use `summary()` to display balance for the two matching specifications. No additional arguments to `summary()` are required for it to use the sampling weights; as long as they are in the `matchit` object (either due to being supplied with the `s.weights` argument in the call to `matchit()` or to being added afterward by `add_s.weights()`), they will be correctly incorporated into the balance statistics. ```{r} #Balance before matching and for the SW propensity score full matching summary(mF_s, improvement = FALSE) #Balance for the non-SW propensity score full matching summary(mF, un = FALSE) ``` The results of the two matching specifications are similar. Balance appears to be slightly better when using the sampling weight-estimated propensity scores than when using the unweighted propensity scores. However, the effective sample size for the control group is larger when using the unweighted propensity scores. Neither propensity score specification achieves excellent balance, and more fiddling with the matching specification (e.g., by changing the method of estimating propensity scores, the type of matching, or the options used with the matching) might yield a better matched set. For the purposes of this analysis, we will move forward with the matching that used the sampling weight-estimated propensity scores (`mF_s`) because of its superior balance. Some of the remaining imbalance may be eliminated by adjusting for the covariates in the outcome model. Note that had we not added sampling weights to `mF`, the matching specification that did not include the sampling weights, our balance assessment would be inaccurate because the balance statistics would not include the sampling weights. In this case, in fact, assessing balance on `mF` without incorporated the sampling weights would have yielded radically different results and a different conclusion. It is critical to incorporate sampling weights into the `matchit` object using `add_s.weights()` even if they are not included in the propensity score estimation. ## Estimating the Effect Estimating the treatment effect after matching is straightforward when using sampling weights. Effects are estimated in the same way as when sampling weights are excluded, except that the matching weights must be multiplied by the sampling weights to yield accurate, generalizable estimates. `match.data()` and `get_matches()` do this automatically, so the weights produced by these functions already are a product of the matching weights and the sampling weights. Note this will only be true if sampling weights are incorporated into the `matchit` object. Below we estimate the effect of `A` on `Y_C` in the matched and sampling weighted sample, adjusting for the covariates to improve precision and decrease bias. ```{r} md_F_s <- match.data(mF_s) fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_F_s, weights = weights) coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ``` Note that `match.data()` and `get_weights()` have the option `include.s.weights`, which, when set to `FALSE`, makes it so the returned weights do not incorporate the sampling weights and are simply the matching weights. To incorporate sampling weights into the effect estimation with this option, one must multiply the returned weights by the sampling weights when including them in the outcome model estimation. We demonstrate this below: ```{r} md_F_s <- match.data(mF_s, include.s.weights = FALSE) fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_F_s, weights = weights * SW) coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ``` We get the same estimates using this syntax as we did above. Because one might to forget to multiply the two sets of weights together, it is easier to just use the default of `include.s.weights = TRUE` and ignore the sampling weights in the rest of the analysis (because they are already included in the returned weights). ## Code to Generate Data used in Examples ```{r, eval = FALSE} #Generatng data similar to Austin (2009) for demonstrating #treatment effect estimation with sampling weights gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 gen_SW <- function(X) { e <- rbinom(nrow(X), 1, .3) 1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e + -log(.5)*e*X[,2] + log(.6)*e*X[,4]) } set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) SW <- gen_SW(X) Y_C <- gen_Y_C(A, X) d <- data.frame(A, X, Y_C, SW) ``` ## References MatchIt/vignettes/estimating-effects.Rmd0000644000176200001440000024562714170731327020065 0ustar liggesusers--- title: "Estimating Effects After Matching" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Estimating Effects After Matching} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{=html} ``` ```{r, include = FALSE} knitr::opts_chunk$set(echo = TRUE, eval=T) options(width = 200, digits= 4) #Generating data similar to Austin (2009) for demonstrating treatment effect estimation gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 # Binary outcome gen_Y_B <- function(A, X) { LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] P_B <- plogis(LP_B) rbinom(length(A), 1, P_B) } #Conditional: # OR: 2.4 # logOR: .875 #Marginal: # RD: .144 # RR: 1.54 # logRR: .433 # OR: 1.92 # logOR .655 # Survival outcome gen_Y_S <- function(A, X) { LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] sqrt(-log(runif(length(A)))*2e4*exp(-LP_S)) } #Conditional: # HR: 2.4 # logHR: .875 #Marginal: # HR: 1.57 # logHR: .452 set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) Y_C <- gen_Y_C(A, X) Y_B <- gen_Y_B(A, X) Y_S <- gen_Y_S(A, X) d <- data.frame(A, X, Y_C, Y_B, Y_S) ``` ## Introduction After assessing balance and deciding on a matching specification, it comes time to estimate the effect of the treatment in the matched sample. How the effect is estimated and interpreted depends on the desired estimand and the type of model used (if any). In addition to estimating effects, estimating the uncertainty of the effects is critical in communicating them and assessing whether the observed effect is compatible with there being no effect in the population. This guide explains how to estimate effects after various forms of matching and with various outcome types. There may be situations that are not covered here for which additional methodological research may be required, but some of the recommended methods here can be used to guide such applications. This guide is structured as follows: first, information on the concepts related to effect and standard error estimation is presented below. Then, instructions for how to estimate effects and standard errors are described. This section is split up first by the type of matching method performed (matching without replacement, matching with replacement, full matching, and subclassification) and then by the type of outcome (continuous, binary, and survival). Finally recommendations for reporting results and tips to avoid making common mistakes are presented. ### Identifying the estimand Before an effect is estimated, the estimand must be specified and clarified. Although some aspects of the estimand depend not only on how the effect is estimated after matching but also on the matching method itself, other aspects must be considered at the tine of effect estimation and interpretation. Here, we consider three aspects of the estimand: the population the effect is meant to generalize to (the target population), the effect measure, and whether the effect is marginal or conditional. **The target population.** Different matching methods allow you to estimate effects that can generalize to different target populations. The most common estimand in matching the average treatment effect in the treated (ATT), which is the average effect of treatment for those who receive treatment. This estimand is estimable for matching methods that do not change the treated units (i.e., by weighting or discarding units) and is requested in `matchit()` by setting `estimand = "ATT"` (which is the default). The average treatment effect in the population (ATE) is the average effect of treatment for the population from which the sample is a random sample. This estimand is estimable only for methods that allow the ATE and either do not discard units from the sample or explicit target full sample balance, which in `MatchIt` is limited to full matching, subclassification, and template matching when setting `estimand = "ATE"`. When treated units are discarded (e.g., through the use of common support restrictions, calipers, cardinality matching, or [coarsened] exact matching), the estimand corresponds to neither the population ATT nor the population ATE, but rather to an average treatment effect in the remaining matched sample (ATM), which may not correspond to any specific target population. **Marginal and conditional effects.** A marginal effect is a comparison between the expected potential outcome under treatment and the expected potential outcome under control. This is the same quantity estimated in randomized trials without blocking or covariate adjustment and is particularly useful for quantifying the overall effect of a policy or population-wide intervention. A conditional effect is the comparison between the expected potential outcomes in the treatment groups within strata. This is useful for identifying the effect of a treatment for an individual patient or a subset of the population. **Effect measures.** The outcome types we consider here are continuous, with the effect measured by the mean difference; binary, with the effect measured by the risk difference (RD), risk ratio (RR), or odds ratio (OR); and time-to-event (i.e., survival), with the effect measured by the hazard ratio (HR). The RR, OR, and HR are *noncollapsible* effect measures, which means the marginal effect on that scale is not a (possibly) weighted average of the conditional effects within strata, even if the stratum-specific effects are of the same magnitude. For these effect measures, it is critical to distinguish between marginal and conditional effects because different statistical methods target different types of effects. The mean difference and RD are *collapsible* effect measures, so the same methods can be used to estimate marginal and conditional effects. In this guide, we will provide examples for estimating the ATT, ATE, and ATM for each of the three outcomes types. Our primary focus will be on marginal effects, which are appropriate for all effect measures, easily interpretable, and require few modeling assumptions. We will include a short section on estimating the conditional OR. The "Common Mistakes" section includes examples of commonly used methods that estimate conditional rather than marginal effects and should not be used when marginal effects are desired. ### Modeling the Outcome The type and form of the model used to estimate the treatment effect depend on the effect measure desired, whether a marginal or conditional effect is desired, whether covariates are to be included in the model, and whether effect modification by the covariates is present. For continuous outcomes, one can use a linear model regressing the outcome on the treatment; for binary outcomes, one can use a generalized linear model with a link function appropriate for the desired effect measure (e.g., a logistic link for the OR); for time-to-event outcomes, one can use a Cox proportional hazards model. An additional decision to make is whether (and how) to include covariates in the outcome model. One may ask, why use matching at all if you are going to model the outcome with covariates anyway? Matching reduces the dependence of the effect estimate on correct specification of the outcome model; this is the central thesis of @ho2007. Including covariates in the outcome model after matching has several functions: it can increase precision in the effect estimate, reduce the bias due to residual imbalance, and make the effect estimate "doubly robust", which means it is consistent if either the matching reduces sufficient imbalance in the covariates or if the outcome model is correct. For these reasons, we recommend covariate adjustment after matching when possible. There is some evidence that covariate adjustment is most helpful for covariates with standardized mean differences greater than .1 [@nguyen2017], so these covariates and covariates thought to be highly predictive of the outcome should be prioritized in treatment effect models if not all can be included due to sample size constraints. For continuous outcomes, we can simply include the covariates in the regression of the outcome on the treatment in the matched sample (i.e., using the matching weights). Because the mean difference is collapsible, the effect estimate conditioning on the covariates is still a marginal effect estimate. This is not the case with binary and time-to-event outcomes; including covariates in the outcome model makes the treatment effect a conditional effect. To recover a marginal effect while including covariates in an outcome model, one has to perform a marginal effects procedure, also known as g-computation [@snowden2011] or regression estimation [@schafer2008], which involves simulating the average potential outcomes under each treatment level and computing the effect as a contrast between those average potential outcomes. G-computation can also be used to estimate marginal effects when modeling effect modification by the covariates. We demonstrate examples of this below. Although there are many possible ways to include covariates (i.e., not just main effects but interactions, smoothing terms like splines, or other nonlinear transformations), it is important not to engage in specification search (i.e., trying many outcomes models in search of the "best" one). Doing so can invalidate results and yield a conclusion that fails to replicate. For this reason, we recommend only including the same terms included in the propensity score model unless there is a strong *a priori* and justifiable reason to model the outcome differently. Second, it is important not to interpret the coefficients and tests of the other covariates in the outcome model. These are not causal effects and their estimates may be severely confounded. Only the treatment effect estimate can be interpreted as causal assuming the relevant assumptions about unconfoundedness are met. Inappropriately interpreting the coefficients of covariates in the outcome model is known as the Table 2 fallacy [@westreich2013]. To avoid this, in all examples that incorporate covariates in the outcome model, we restrict the output of outcome regression models to just the treatment coefficient. ### Estimating Standard Errors and Confidence Intervals Uncertainty estimation (i.e., of standard errors, confidence intervals, and p-values) may consider the variety of sources of uncertainty present in the analysis, including (but not limited to!) estimation of the propensity score (if used), matching (i.e., because treated units might be matched to different control units if others had been sampled), and estimation of the treatment effect (i.e., because of sampling error). In general, there are no analytic solutions to all these issues, so much of the research done on uncertainty estimation after matching has relied on simulation studies. The two primary methods that have been shown to perform well in matched samples are using cluster-robust standard errors and the bootstrap. #### Robust and Cluster-Robust Standard Errors **Robust standard errors.** Also known as sandwich standard errors (due to the form of the formula for computing them), heteroscedasticity-consistent standard errors, or Huber-White standard errors, robust standard errors are an adjustment to the usual maximum likelihood or ordinary least squares standard errors that are robust to violations of some of the assumptions required for usual standard errors to be valid [@mackinnon1985]. Although there has been some debate about their utility [@king2015], robust standard errors rarely degrade inferences and often improve them. Generally, robust standard errors **must** be used when any non-uniform weights are included in the estimation (e.g., with full matching or inverse probability weighting). **Cluster-robust standard errors.** A version of robust standard errors known as cluster-robust standard errors [@liang1986] can be used to account for dependence between observations within clusters (e.g., matched pairs). @abadie2019 demonstrate analytically that cluster-robust standard errors are generally valid after matching, whereas regular robust standard errors can over- or under-estimate the true sampling variability of the effect estimator depending on the specification of the outcome model (if any) and degree of effect modification. A plethora of simulation studies have further confirmed the validity of cluster-robust standard errors after matching [e.g., @austin2009a; @austin2014; @gayat2012; @wan2019; @austin2013]. Given this evidence favoring the use of cluster-robust standard errors, we recommend them in most cases and use them judiciously in the this guide[^1]. [^1]: Because they are only appropriate with a large number of clusters, cluster-robust standard errors are generally not used with subclassification methods. Regular robust standard errors are valid with these methods when using the subclassification weights to estimate marginal effects. Here, we will use linear and generalized linear models as implemented by `lm()` and `glm()` and use `lmtest::coeftest()` and `lmtest::coefci()` along with `sandwich::vcovHC()` for robust standard errors and `sandwich::vcovCL()` for cluster-robust standard errors. There are several "types" (e.g., HC0, HC1, etc.) of robust standard errors that adjust the original standard errors in different ways; although more research is required on the benefits of using different types for estimating standard errors after matching, we use the default types here. #### Bootstrapping Bootstrapping is a technique used to simulate the sampling distribution of an estimator by repeatedly drawing samples with replacement and estimating the effect in each bootstrap sample [@efron1993]. From the bootstrap distribution, standard errors and confidence interval can be computed in several ways, including using the standard deviation of the bootstrap estimates as the standard error estimate or using the 2.5 and 97.5 percentiles as 95% confidence interval bounds. Bootstrapping tends to be most useful when no analytic estimator of a standard error is possible or has been derived yet. Although @abadie2008 found analytically that the bootstrap is inappropriate for matched samples, simulation evidence has found it to be adequate in many cases [@hill2006; @austin2014; @austin2017]. It is the most accessible method for computing standard errors after g-computation for nonlinear models. Typically, bootstrapping involves performing the entire estimation process in each bootstrap sample, including propensity score estimation, matching, and effect estimation. This tends to be the most straightforward route, though intervals from this method may be conservative in some cases (i.e., they are wider than necessary to achieve nominal coverage) [@austin2014]. Less conservative and more accurate intervals have been found when using different forms of the bootstrap, including the wild bootstrap develop by @bodory2020 and the matched bootstrap described by @austin2014 and @abadie2019. The block bootstrap involves sampling matched pairs/strata of units from the matched sample and performing the analysis within each sample composed of the sampled pairs. @abadie2019 derived analytically that the block bootstrap is valid for estimating SEs and CIs in the same circumstances cluster robust SEs are; indeed, the block bootstrap SE is known to approximate the cluster-robust SE [@cameron2015]. We use `boot()` from the `boot` package to implement bootstrapping. With bootstrapping, more bootstrap replications are always better but can take time and increase the chances that at least one error will occur within the bootstrap analysis (e.g., a bootstrap sample with zero treated units or zero units with an event). In general, numbers of replications upwards of 999 are recommended, with values one less than a multiple of 100 preferred to avoid interpolation when using the percentiles as confidence interval limits [@mackinnon2006]. There are several methods of computing bootstrap confidence intervals, but the bias-corrected accelerated (BCa) bootstrap confidence interval often performs best [@austin2014; @carpenter2000] and is easy to implement, simply by setting `type = "bca"` in the call to `boot.ci()` after running `boot()`[^2]. [^2]: Sometimes, an error will occur with this method, which usually means more bootstrap replications are required. The number of replicates must be greater than the original sample size when using the full bootstrap and greater than the number of pairs/strata when using the block bootstrap. ## Estimating Treatment Effects and Standard Errors After Matching Below, we describe effect estimation after several methods of matching. We consider four broad types of matching that require their own specific methods for estimation effects: 1) pair matching without replacement, 2) pair matching with replacement, 3) full matching, and 4) stratification. In some cases, methods for estimating effects are similar across methods, and we err on the side of redundancy here in our instructions. We also consider three different outcome types: 1) continuous, 2) binary, and 3) survival. We'll be using a simulated toy dataset `d` with several outcome types. Code to generate the dataset is at the end of this document. The focus here is not on evaluating the methods but simply on demonstrating them. In all cases, the correct propensity score model is used. Below we display the first six rows of `d`: ```{r} head(d) ``` `A` is the treatment variable, `X1` through `X9` are covariates, `Y_C` is a continuous outcome, `Y_B` is a binary outcome, and `Y_S` is a survival outcome. We will need to the following packages to perform the desired analyses: - `lmtest` provides the `coeftest()` and `coefci()` functions for estimating coefficients, standard errors, and confidence intervals incorporating robust standard errors - `sandwich` provides robust and cluster robust standard errors through the `vcovHC()` and `vcovCL()` functions, respectively - `boot` provides the `boot()` and `boot.ci()` functions for performing bootstrapping and estimating bootstrap effects, standard errors, and confidence intervals. - `survival` provides the functions `survdiff()` and `coxph()` to perform the log-rank test for differences in survival curves and estimate the coefficients in a Cox-proportional hazards model for the marginal hazard ratio, respectively, which we will use for survival outcomes. Of course, we also need `MatchIt` to perform the matching. ```{r,message=FALSE,warning=FALSE} library("MatchIt") library("lmtest") library("sandwich") library("boot") library("survival") ``` Other packages may be of use but are not used here. The `margins` package can be useful for computing marginal effects and standard errors without bootstrapping. Some examples using `margins` are presented. The `survey` package can be used to estimate robust standard errors incorporating weights and provides functions for survey-weighted generalized linear models and Cox-proportional hazards models. It is often used with propensity score weighting. The `Zelig` package provides a broad interface to estimating marginal and conditional effects using simulation and was included in the original documentation for `MatchIt`. The `lme4` (and `lmerTest`) package performs mixed effects modeling, which can be useful for accounting for pair membership or other clustering features of the data. Because different matching methods require different treatments, instructions for each method are organized in the following sections, as designated by the table below. It is important to ensure the right methods are used with the matching specification used in order for the estimated effects and standard errors to be valid. | Section | Matching Method | |-------------------------------------------------------------------------------------|-----------------------------------------------------------| | [After Pair Matching Without Replacement](#after-pair-matching-without-replacement) | Nearest neighbor matching without replacement | | \- | Optimal matching | | \- | Genetic matching without replacement | | \- | Coarsened exact matching with `k2k = TRUE` | | [After Pair Matching With Replacement](#after-pair-matching-with-replacement) | Nearest neighbor matching with replacement | | \- | Genetic matching with replacement | | [After Full Matching](#after-full-matching) | Full matching | | | Cardinality and template matching | | [After Stratum Matching](#after-stratum-matching) | Propensity score subclassification | | \- | Exact matching | | \- | Coarsened exact matching with `k2k = FALSE` (the default) | ### After Pair Matching Without Replacement {#after-pair-matching-without-replacement} Pair matching without replacement yields the simplest way to estimate treatment effects and standard errors. In general, whether a caliper, common support restriction, exact matching specification, or $k$:1 matching specification is used, estimating the effect in the matched dataset (i.e., the output of `match.data()`) is straightforward and involves fitting a model for the outcome that incorporates the matching weights[^3]. [^3]: The matching weights are not necessary when performing 1:1 matching, but we include them here for generality. When weights are not necessary, including them does not affect the estimates. Because it may not always be clear when weights are required, we recommend always including them. First, we will perform nearest 1:1 neighbor propensity score matching without replacement. ```{r} mNN <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d) mNN md <- match.data(mNN) head(md) ``` Typically one would assess balance and ensure that this matching specification works, but we will skip that step here to focus on effect estimation. See `vignette("MatchIt")` and `vignette("assessing-balance")` for more information on this necessary step. Because we did not use a caliper, the target estimand is the ATT. #### For continuous outcomes For continuous outcomes, estimating effects is fairly straightforward using linear regression. We perform all analyses using the matched dataset, `md`, which contains only units retained in the sample. **Without covariate adjustment.** To estimate the effect without covariate adjustment, we can run the following: ```{r} #Linear model without covariates fit1 <- lm(Y_C ~ A, data = md, weights = weights) ``` First, we will estimate the standard errors while accounting for pair membership using cluster-robust standard errors. ```{r} #Cluster-robust standard errors coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ``` **Using the bootstrap.** We demonstrate bootstrapping here using the block bootstrap as recommended by @abadie2019. The process is generalizable to other matching methods for which cluster-robust standard errors are valid and to other outcome types. We first need to write a function, `est_fun`, which takes in a vector of pair identifiers and an ordering and outputs an effect estimate. This function should include the effect estimation, though no standard error is required. ```{r} #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Effect estimation fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights) #Return the coefficient on treatment return(coef(fit_boot)["A"]) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ``` The value `t1*` in the `Bootstrap Statistics` table contains the effect estimate and its bootstrap standard error, and the interval in the confidence interval output is the bootstrap confidence interval computed using the specified type (here, `"bca"`). **With covariate adjustment.** Including covariates in the outcome model is straightforward with a continuous outcome. We can include main effects for each variable and use the coefficient on the treatment as the treatment effect estimate. It can also be helpful to include interactions between the covariates and treatment if effect modification is suspected, though it is important to center the covariates at their means in the focal (i.e., treated) group if the coefficient on the treatment is to be interpreted as an average treatment effect estimate (which we demonstrate with full matching). A marginal effects procedure can also be used, which we demonstrate with binary outcomes. Below we simply include main effects of each covariate, which is the most typical way to include covariates. ```{r} #Linear model with covariates fit3 <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md, weights = weights) coeftest(fit3, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ``` As previously mentioned, it is important to avoid interpreting coefficients in the outcome model on predictors other than the treatment, as the coefficients do no correspond to causal effects and are likely confounded. In the code above, we restricted the output to just the treatment effect. Note that sometimes the intercept of the outcome model can be useful as an estimate of the average potential outcome under control, but the covariates in the model (if any) must be centered at their means in the target population to allow for this interpretation. Without centering the covariates, the intercept is uninterpretable. #### For binary outcomes For binary outcomes, effect estimation can be a bit more challenging, especially when including covariates. There are several measures of the effect one can consider, which include the odds ratio (OR), risk ratio/relative risk (RR), and risk difference (RD). **Without covariate adjustment.** When omitting covariates from the outcome model, effect and standard error estimation is fairly straightforward. Below we demonstrate how to estimate the marginal log OR after nearest neighbor matching without replacement. ```{r} #Generalized linear model without covariates fit4 <- glm(Y_B ~ A, data = md, weights = weights, family = binomial(link = "logit")) ``` By specifying `link = "logit"`, we fit a logistic regression model to estimate the marginal OR for treatment. To estimate the marginal RR we can specify `link = "log"`, and to estimate the RD we can specify `link = "identity"` or use `lm()` instead of `glm()`. Below we estimate cluster-robust standard errors, though it should be noted that these standard errors can be biased for the OR and should be used with caution [@austin2007a]. Because we want the OR and the effects are estimated on the log OR scale, we have to exponentiate the coefficient on treatment to arrive at the OR (one would do this when estimating the OR or RR, but not the RD). ```{r} #Cluster-robust standard errors coeftest(fit4, vcov. = vcovCL, cluster = ~subclass) exp(coef(fit4)) #OR ``` **With covariate adjustment and bootstrapping.** If we want to include covariates in the model, we have to do some additional work to estimate the effect and its standard error. This is because the coefficient on treatment in a nonlinear model with covariates corresponds to the conditional rather than marginal effect. To estimate a marginal effect, we have to perform a marginal effects procedure, which is equivalent to g-computation in the matched set. First we estimate an outcome model including the covariates, and then we use the predictions from the outcome model to compute the contrast of the average potential outcomes under treatment and control. Bootstrapping is the most straightforward method to estimate standard errors. We demonstrate this below using the block bootstrap as recommended by @abadie2019. ```{r} #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Fitting outcome the model fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_boot, family = binomial(link = "logit"), weights = weights) #Estimate potential outcomes for each unit #Under control md_boot$A <- 0 P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds0 <- P0 / (1 - P0) #Under treatment md_boot$A <- 1 P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ``` To use bootstrapping for the RR or RD, the part of the code that computes the marginal OR can be replaced with code to compute the marginal RR (`P1 / P0`) or marginal RD (`P1 - P0`). Interactions between the treatment and covariates can be included in the outcome model; no other part of the procedure needs to be changed. Doing so can add robustness when effect modification is possible. #### For survival outcomes There are several measures of effect size for survival outcomes. When using the Cox proportional hazards model, the quantity of interest is the HR between the treated and control groups. As with the OR, the HR is non-collapsible, which means the estimated HR will only be a valid estimate of the marginal HR when no other covariates are included in the model. Other effect measures, such as the difference in mean survival times or probability of survival after a given time, can be treated just like continuous and binary outcomes as previously described. Here we describe estimating the marginal HR. **Without covariate adjustment.** Below we demonstrate estimation of the marginal hazard ratio using `coxph()` from the `survival` package. To request cluster-robust standard errors as recommended by @austin2013a, we need to supply pair membership (stored in the `subclass` column of `md`) to the `cluster` argument and set `robust = TRUE`. ```{r} #Cox Regression for marginal HR coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights, cluster = subclass) ``` The `coef` column contains the log HR, and `exp(coef)` contains the HR. Remember to always use the `robust se` for the standard error of the log HR. The displayed z-test p-value results from using the robust standard error. **Using the bootstrap.** @austin2014 found bootstrap confidence intervals to work well for marginal hazard ratios. Below we demonstrate this using similar code as before to implement the block bootstrap: ```{r, eval = FALSE} #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Effect estimation cox_fit_boot <- coxph(Surv(Y_S) ~ A, data = md_boot, weights = weights) #Compute the marginal HR by exponentiating the coefficient #on treatment HR <- exp(coef(cox_fit_boot)["A"]) #Return the HR return(HR) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ``` **With covariate adjustment.** As with binary outcomes, if covariates are included in the model, the resulting hazard ratios will be conditional rather than marginal. @austin2020 describe several ways of including covariates in a model to estimate the marginal HR, but because they do not develop standard errors and little research has been done on this method, we will not present it here. ### After Pair Matching With Replacement {#after-pair-matching-with-replacement} Pair matching with replacement makes estimating effects and standard errors a bit less straightforward. Control units paired with multiple treated units belong to multiple pairs at the same time and appear multiple times in the matched dataset. Effect and standard error estimation need to account for control unit multiplicity (i.e., repeated use) and within-pair correlations [@hill2006; @austin2020a]. `MatchIt` provides two interfaces for extracting the matched dataset after matching with replacement. The first uses `match.data()` and provides the same output as when used after matching without replacement, except that the `subclass` column is absent because units are not assigned to a single subclass but rather to several. Control units will have weights that differ from 1 to reflect their use as matches for multiple treated units. When using the `match.data()` interface, including the weights in the estimation of effects is crucial. Because pair membership is omitted, accounting for it (if desired) must be done by conditioning on covariates used to match the pairs or by bootstrapping the entire process. The second interface uses `get_matches()`, which functions similarly to `match.data()` except that the dataset contains one row per unit per pair, so control units matched to multiple treated units will have multiple rows in the dataset, and a `subclass` column is included denoting pair membership. In the `get_matches()` output, each reused control unit will have multiple rows, identical to each other except with different `subclass` membership. When performing k:1 matching, weights must also be included in effect estimation when using `get_matches()`. Here we will demonstrate the estimation of effects using both `match.data()` and `get_matches()` output. The `match.data()` output is preferred when pair membership is not directly included in the analysis, and the `get_matches()` output is preferred when pair membership is to be included. Here will will use 3:1 matching with replacement and with a caliper; note that because a caliper was used, the estimand corresponds to neither the ATT nor the ATE but rather to an ATM. ```{r} mNNr <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) mNNr #match.data output md <- match.data(mNNr) nrow(md) head(md) #get_matches output gm <- get_matches(mNNr) nrow(gm) head(gm) ``` The `get_matches()` output provides some additional information about the match. We can count how many times control units are reused and how many units are in each match strata (not all will have 3 control units due to the caliper). ```{r} #Number of time control units are rematched table(table(gm$id[gm$A == 0])) ``` Here we can see that 332 control units were only used in one pair each, and one control unit was paired with 14 treated units (i.e., heavily reused). ```{r} #Number of control units in each match stratum table(table(gm$subclass[gm$A == 0])) ``` Here we can see that 409 treated units have three matches, nine have two matches, and nine only have one match. The caliper did not end up restricting too many matches. #### For continuous outcomes **Without covariate adjustment.** For continuous outcomes, we can regress the outcome on the treatment and include the weights in the estimation. We do this regardless of whether we are using the `match.data()` output or the `get_matches()` output (if we were doing 1:1 matching, the weights would not be necessary when using the `get_matches()` output, but they don't change the results if included). If we don't mind ignoring pair membership, we can use the `match.data()` output to estimate the effect and standard errors. Here we use `vcovHC()` to estimate regular robust standard errors that ignoring pairing. @hill2006 found these standard errors to be conservative for continuous outcomes. ```{r} #match.data() output fit1md <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1md, vcov. = vcovHC) ``` If we want to incorporate pair membership into the standard error estimation, we have to use the `get_matches()` output. In addition to supplying pair membership (`subclass`) to the standard error estimator, we also supply unit ID (`id`) to account for the fact that several rows may refer to the same control unit. ```{r} #get_matches() output fit1gm <- lm(Y_C ~ A, data = gm, weights = weights) coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id) ``` Note that the effect estimates are identical; only the standard errors and p-values differ between the approaches[^4]. [^4]: It is possible to exactly reproduce the `match.data()` standard error using the `get_matches()` data, but doing so may require some fiddling due to the defaults in `sandwich`. **Using the bootstrap.** We can also use bootstrapping to estimate standard errors and confidence intervals. Although @abadie2008 demonstrated analytically that bootstrap standard errors may be invalid for matching with replacement, simulation work by @hill2006 and @bodory2020 has found that bootstrap standard errors are adequate and generally slightly conservative. Given this disagreement, it may be worth proceeding with caution when using bootstrapping to estimate standard errors after matching with replacement. Simulation experiments with matching with replacement have only considered the full bootstrap and found it to yield confidence intervals with approximately nominal coverage, so we recommend this approach over the block bootstrap for now and demonstrate it below. ```{r} #Full bootstrap confidence interval # library(boot) est_fun <- function(data, i) { #Matching function mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) md_boot <- match.data(mNNr_boot) #Effect estimation fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights) #Return the coefficient on treatment return(coef(fit_boot)["A"]) } boot_est <- boot(d, est_fun, R = 499) boot_est boot.ci(boot_est, type = "perc") ``` **With covariate adjustment.** We can include covariates in the outcome model just as we could when matching without replacement. @hill2006 found that standard errors that fail to account for pair membership can have lower the nominal confidence interval coverage, so we recommend using the `get_matches()` output to address both multiplicity and clustering. ```{r} fit2md <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = gm, weights = weights) coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id)["A",,drop = FALSE] ``` Remember that the coefficients and tests on the predictors other than the treatment should not be interpreted because they may be subject to confounding even if the treatment is not, so we omit them here. #### For binary outcomes The primary difference between dealing with binary and continuous outcomes is the noncollapsibility of the effect measures for binary outcomes, meaning that including covariates in the outcome model is less straightforward because the coefficient on treatment does not correspond to the marginal treatment effect. Similar to continuous outcomes, when estimating the treatment effect, we can use either the output of `match.data()` or the output of `get_matches()`, only the latter of which allows us to account both for multiplicity in the control units and for pair membership. Below we'll demonstrate estimating the marginal OR accounting for pair membership using the `get_matches()` output. Then we will demonstrate using the bootstrap to estimate standard errors that include covariates in the model. Note that there has been little research on the use of matching with replacement with binary outcomes, so one should proceed with caution and consider using a better-studied matching method. **Without covariate adjustment.** We include the weights in the call to `glm()` and we include both `subclass` and `id` as clustering variables in computing the cluster-robust standard errors, just as we would with a continuous outcome. We can use `family = quasibinomial` instead of `family = binomial` to avoid a warning due to the use of weights; the estimates will be the same either way. To estimate the marginal RR or RD, `"logit"` would be replaced with `"log"` or `"identity"`, respectively. ```{r} fit3gm <- glm(Y_B ~ A, data = gm, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3gm, vcov. = vcovCL, cluster = ~ subclass + id) exp(coef(fit3gm)) #OR ``` **With covariate adjustment and bootstrapping.** To include covariates, we can use the bootstrap as we did before when matching without replacement. It doesn't matter whether the `match.data()` or `get_matches()` output is used because, as we saw before, both yield the same effect estimate in each bootstrap replication. Here we use the full bootstrap rather than the block bootstrap because the performance of the block bootstrap after matching with replacement has not been evaluated. ```{r, eval = FALSE} #Bootstrap confidence intervals # library(boot) est_fun <- function(data, i) { #Matching function mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) md_boot <- match.data(mNNr_boot) #Fitting the model fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_boot, family = quasibinomial(link = "logit"), weights = weights) #Estimate potential outcomes for each unit md_boot$A <- 0 P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds0 <- P0 / (1 - P0) md_boot$A <- 1 P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(d, est_fun, R = 4999) boot_est boot.ci(boot_est, type = "bca") ``` As before, to use bootstrapping for the RR or RD, the part of the code that computes the marginal OR can be replaced with code to compute the marginal RR (`P1 / P0`) or marginal RD (`P1 - P0`). The outcome model can additionally include treatment-covariate interactions if desired. #### For survival outcomes Standard error estimation for the marginal HR after matching with replacement is not a well-studied area, with @austin2020a providing the sole examination into appropriate methods for doing so. With survival outcomes, other matching methods may be more appropriate until matching with replacement is better understood. Here we provide an example that implements the recommendations by @austin2020a. Any other methods (e.g., bootstrap) should be used with caution until they have been formally evaluated. **Without covariate adjustment.** According to the results of Austin and Cafri's [-@austin2020a] simulation studies, when prevalence of the treatment is low (\<30%), a standard error that does not involve pair membership is sufficient. When treatment prevalence is higher, the standard error that ignores pair membership may be too low, and the authors recommend a custom standard error estimator that uses information about both multiplicity and pairing. For the continuous and binary outcomes, accounting for both multiplicity and pair membership is fairly straightforward thanks to the ability of the `sandwich` package functions to include multiple sources of clustering. Unfortunately, this must be done manually for survival models. We perform this analysis below, adapting code from the appendix of @austin2020a to the `get_matches()` output. ```{r} #Austin & Cafri's (2020) SE estimator fs <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights, cluster = subclass) Vs <- fs$var ks <- nlevels(gm$subclass) fi <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights, cluster = id) Vi <- fi$var ki <- length(unique(gm$id)) fc <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights) Vc <- fc$var kc <- nrow(gm) #Compute the variance V <- (ks/(ks-1))*Vs + (ki/(ki-1))*Vi - (kc/(kc-1))*Vc #Sneak it back into the fit object fc$var <- V fc ``` The `robust se` column contains the computed standard error, and the reported Z-test uses this standard error. The `se(coef)` column should be ignored. ### After Full Matching {#after-full-matching} ```{r, include=FALSE} #In case optmatch goes offline, don't run lines below if (!requireNamespace("optmatch", quietly = TRUE)) knitr::opts_chunk$set(eval = FALSE) ``` Full matching presents fairly straightforward methods of effect and standard error estimation. The most common and recommended way to estimate effects after full matching is to use the computed matching weights to estimate weighted effects. These matching weights function essentially like inverse probability weights and can be treated as such in all analyses, including with respect to standard error estimation. For empirical comparisons between full matching and propensity score weighting, see Austin and Stuart [-@austin2015b; -@austin2017; -@austin2015a]. Standard error estimation involves using a cluster-robust standard error as recommended by @austin2017. Including covariates can improve precision and add robustness when valid. Note that the methods described here also work for other estimators that rely on matching weights, including cardinality and template matching and matching without replacement. The main difference is that full matching can be used to estimate the ATE, which we demonstrate below. Below, we perform optimal full propensity score matching. Here we use full matching to estimate the ATE, but the procedure is nearly identical when estimating the ATT, and we point out any differences. ```{r} mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", estimand = "ATE") mF md <- match.data(mF) head(md) ``` A benefit of full matching is that no units are discarded, which has the potential to improve precision and prevent bias due to incomplete matching. However, the "effective" sample size implied by the matching weights is lower than the actual remaining sample size, so one should not always expect full matching to yield more precise estimates than other forms of matching. #### For continuous outcomes **Without covariate adjustment.** Estimating effects and standard errors for continuous outcomes after full matching involves including the matching weights in the outcome model and using a cluster-robust standard error. For cardinality or template matching, a regular robust standard error can be requested using `vcovHC` and omitting the `cluster` argument in the code below. ```{r} fit1 <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ``` **Using the bootstrap.** Computing bootstrap standard errors and confidence intervals after full matching is identical to doing so after pair matching without replacement, so we do not demonstrate it again here. Covariates can be included in the outcome model used in the bootstrap replications. See below for a note on centering the covariates when including treatment-covariate interactions, as this must be done when using the bootstrap as well. **With covariate adjustment.** As previously mentioned, it is generally acceptable to include just the main effects, but including interactions between the treatment and covariates can be beneficial when effect modification by the covariates may be present. Because we have not demonstrated this strategy so far, we demonstrate it below. In order to interpret the coefficient on treatment as a marginal effect estimate, we need to center the covariates at their means in the target population (i.e., the original sample for the ATE, the treated units for the ATT, or the retained units for an ATM); we could also use a marginal effects procedure as has been demonstrated with binary outcomes for other matching methods. Below we use the strategy of centering the covariates at their means. Note that when factor predictors are present, they need to be split into dummy (0/1) variables prior to centering. The `splitfactor()` function in `cobalt` can make this straightforward. Although this procedure is more involved compared to simply including main effects, it can provide extra precision and robustness. ```{r} #Estimating a covariate-adjusted marginal effect #with treatment-covariate interactions #Create a new dataset for centered variables md_cen <- md covs_to_center <- c("X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9") md_cen[covs_to_center] <- scale(md_cen[covs_to_center], scale = FALSE) #Fit the model with every covariate interacting with treatment fit2 <- lm(Y_C ~ A * (X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9), data = md_cen, weights = weights) #Only output the intercept and coefficient on treatment coeftest(fit2, vcov. = vcovCL, cluster = ~subclass)[1:2,] ``` Remember not to interpret the coefficients on the covariates or the treatment-covariate interactions, as they are likely confounded. To keep the output clean, above we restricted the output to just the intercept and coefficient on treatment. Another benefit of centering the covariates is that we can interpret the intercept as an estimate of the average potential outcome under control. Note that the above strategy can be applied to all matching methods when analyzing continuous outcomes (but not binary or survival outcomes, which require bootstrapping to validly estimate standard errors with covariate adjustment). It is critical to center the covariates at their means in the target group, which may require some additional programming for estimands other than the ATE. #### For binary outcomes Using full matching with binary outcomes was described by @austin2017. In general, the procedures look similar to how they do with other matching methods. **Without covariate adjustment.** We can use a weighted generalized linear model regressing the outcome on the treatment with a link function appropriate to the effect measure of interest. Below we demonstrate estimating the marginal OR after full matching in a model without covariates: ```{r} fit3 <- glm(Y_B ~ A, data = md, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3, vcov. = vcovCL, cluster = ~subclass) exp(coef(fit3)) #OR ``` As with matching with replacement, we include weights in the call to `glm()` and set `family = quasibinomial()` to prevent a warning that occurs when using weights with binomial regression models (though the results do not differ). Setting `link = "logit"` provides the marginal OR; for the marginal RR, we would replace `"logit"` with `"log"`, and for the marginal RD, we would replace `"logit"` with `"identity"` and not exponentiate the coefficient. **With covariate adjustment and bootstrapping.** To include covariates in the model, a marginal effects procedure must be used with bootstrapping to recover the marginal effect because the coefficient on treatment in such a model corresponds to a conditional effect. A bootstrap must be used to estimate the standard error and confidence interval of the marginal effect. Either the full bootstrap or block bootstrap can be used, though the performance of the latter has not been formally evaluated (but because it is an approximation to cluster-robust standard errors, which are valid, it is likely valid). The code for the full bootstrap with full matching is identical to the code for bootstrapping with binary outcomes for pair matching with replacement (except that the call to `matchit()` in the `est_fun` function must be adjusted to perform full matching), and the code for the block bootstrap with full matching is identical to the code for bootstrapping with binary outcomes for pair matching without replacement, so we do not repeat it here. #### For survival outcomes @austin2015b describe the use of the full matching with survival outcomes. **Without covariate adjustment.** To estimate the marginal HR, we can regress the outcome on the treatment in a Cox regression model weighted by the matching weights and including subclasses as a cluster. Below we demonstrate how to estimate the marginal HR and its standard error after full matching. ```{r} coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights, cluster = subclass) ``` To perform the log-rank test or compute survival curves after full matching, functions designed for performing these tasks with inverse probability weights can be used with the matching weights; the `RISCA` package offers functionality for this purpose. Including covariates in the model is less straightforward because the resulting HR estimate is conditional rather than marginal. ```{r, include=FALSE, eval=TRUE} knitr::opts_chunk$set(eval = TRUE) ``` ### After Stratum Matching {#after-stratum-matching} Stratum matching includes exact matching, coarsened exact matching, and propensity score subclassification. There are two natural ways to estimate marginal effects after stratum matching: the first is to estimate stratum-specific treatment effects and pool them, and the second is to use the stratum weights to estimate a single marginal effect. This latter approach is also known as marginal mean weighting through stratification (MMWS), and is described in detail by @hong2010[^5]. When done properly, both methods should yield similar or identical estimates of the treatment effect. MMWS is generally preferable because it is far simpler to implement and avoids issues of noncollapsibility with non-continuous outcomes. All of the methods described above for use with full matching also work with MMWS because the formation of the weights is the same; the only difference is that it is not appropriate to use cluster-robust standard errors with MMWS because of how few clusters are present. [^5]: It is also known as fine stratification weighting, described by Desai et al. [-\@desai2017]. Unless exact matching is used, estimating stratum-specific treatment effects can be fraught because balance may not be achieved within strata even if balance is achieved across strata. Stratum-specific effects should be interpreted with caution. Stratum-specific effects are conditional effects, but conditional on stratum membership, which may not always be a useful conditioning variable. For each outcome type, we focus on estimating marginal effects using MMWS and using the strata directly. Below, we perform propensity score subclassification for the ATT using 8 subclasses. ```{r} mS <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "subclass", estimand = "ATT", subclass = 8) mS md <- match.data(mS) head(md) ``` #### For continuous outcomes For continuous outcomes, we can use either MMWS or compute the weighted average of within-subclass effects. First we illustrate weighting. **Without covariate adjustment.** With weighting, we can supply the weights that are in the `match.data()` output to a call to `lm()` to perform weighted least squares regression, as we did with full matching. We need a robust standard error estimator to account for the weights. Note that the subclasses don't even need to enter this analysis; they are fully incorporated through the MMWS weights[^6]. We use `vcovHC()` to estimate the regular robust standard error instead of the cluster-robust standard error used with other methods. [^6]: Including subclass as a main effect in the MMWS-weighted regression will not change the effect estimate but may slightly decrease its estimated standard error. ```{r} fit1 <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1, vcov. = vcovHC) ``` We can also fit a model within each subclass to estimate the within-stratum treatment effects and then compute a weighted average of them to be used as the marginal effect. The stratum weights in the weighted average must be equal to the proportion of treated units in each subclass if the ATT is targeted. If the ATE is targeted, the weights must be equal to the proportion of all units in each subclass. There are other ways to construct weight to minimize variance at the expense of losing the original target population [@rudolph2016]. Instead of fitting separate models for each subclass, we can fit a single model that fully interacts the treatment with subclass membership and then perform a linear hypothesis test. To do so, we use the form `Y ~ S + S:A - 1` in the call to `lm()`. This includes main effects for subclass and treatment interaction terms for each subclass and omits an intercept. The fit of this model is equivalent to that of a traditional full factorial model, so no information is lost using this parameterization and using it makes it easier to construct the stratum weights. This estimates the subclass-specific intercept and subclass-specific treatment effect in each subclass. We would use a robust standard error to account for different residual variance across subclasses. ```{r} fit2 <- lm(Y_C ~ subclass + subclass:A - 1, data = md) #Within-subclass effects # coeftest(fit2, vcov. = vcovHC) ``` The within-subclass effects should only be trusted if balance is achieved in each subclass. In this example, balance has not been achieved within some subclasses, so we would not interpret these effects. Next we construct the weights to form the weighted average of the subclass effects. The weights take the form of a linear contrast matrix with zeroes for the subclass-specific intercepts and the subclass proportions for the corresponding subclass-specific treatment effect coefficients. ```{r} #Subclass weights for ATT sub_w <- with(md, c(rep(0, nlevels(subclass)), table(subclass[A==1])/sum(A==1))) #Subclass weights for ATE (requires estimand = "ATE" in matchit()) # sub_w <- with(md, c(rep(0, nlevels(subclass)), # table(subclass)/nrow(md))) #Marginal effect (est <- weighted.mean(coef(fit2), sub_w)) #SE of marginal effect (se <- sqrt(drop(sub_w %*% vcovHC(fit2) %*% sub_w))) #CI c(ci_low = est - 1.96*se, ci_hi = est + 1.96*se) ``` The following lines would have produced the same output but require the `margins` package: ```{r,eval=FALSE} #Using margins() from margins summary(margins::margins(fit2, variables = "A", data = md[md$A == 1,], vcov = vcovHC(fit2))) #For ATE, omit the second line. ``` **With covariate adjustment.** To include covariates in the model when using MMWS, we can modify the code used for weighting after full matching, the only difference being that regular robust standard errors should be used with MMWS. As before, treatment-covariate interactions are optional but can reduce bias and improve precision when there effect modification by the covariates. When including these interactions in the outcome model, it is important to center the covariates at their means in the target population (i.e., the full sample for the ATE and the treated units for the ATT) in order to interpret the coefficient on treatment as a marginal effect. To include covariates in the model when combining subclass-specific effect estimates, it can be challenging to correctly parameterize the model so that the linear contrast matrix method works as expected. The simplest way would be to include covariates as desired, which include as main effects, interactions with treatment, interactions with subclass, or all three, and use the `margins()` code above, which should automatically provide the correct output. #### For binary outcomes Using stratification with binary outcomes is slightly more complicated than it is with continuous outcomes. This is because the OR and RR are not collapsible, so the marginal OR and RR cannot be computed as the weighted average of the stratum-specific effects. Instead, one must compute the average of the predicted stratum-specific risks under each treatment and then compute the marginal effect estimate from these marginal risks. Although stratum-specific conditional ORs are valid effect measures, they generally do not correspond to meaningful subpopulation effects unless the strata themselves are meaningful subpopulations. After exact matching or coarsened exact matching, strata may be meaningful because they correspond to specific combinations of covariates that may come close to designating specific patient attributes, but after propensity score subclassification, the strata correspond to propensity score bins, which are generally not meaningful. Although some researchers have interpreted stratum-specific effects after propensity score subclassification as representing effects at various risks or propensities for treatment, because the primary purpose of the propensity score is as a balancing score and not as an accurate estimate of propensity for treatment, such an interpretation should be regarded with caution. @austin2007a compared several methods of propensity score adjustment for estimating marginal ORs, including two methods based on propensity score stratification, one of which involved the stratified Mantel-Haenszel estimator, and the other of which involved averaging stratum-specific effects. Both of these estimate a common conditional OR, not a marginal OR [@forbes2008; @stampf2010], and both yielded positively biased effect estimates for non-null treatment effects, a common pattern when using conditional effect estimates as estimates of marginal effects. Given the difficulties in estimating marginal ORs after stratification, the most straightforward way to do so is to use MMWS. We do, however, also demonstrate estimating the marginal odds ratio and its standard error using bootstrapping in a way that can incorporate covariate adjustment and allow for differential effect modification across strata. **Without covariate adjustment.** As before, we can supply the stratification weights to a weighted generalized linear model with just the treatment as the sole predictor, and the coefficient on treatment will correspond to a marginal treatment effect. We use `vcovHC()` to estimate the regular robust standard error. ```{r} fit3 <- glm(Y_B ~ A, data = md, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3, vcov. = vcovHC) exp(coef(fit3)) ``` As with other matching methods, we include weights in the call to `glm()` and set `family = quasibinomial()` to prevent a warning that occurs when using weights with binomial regression models (though the results do not differ). Setting `link = "logit"` provides the marginal OR; for the marginal RR, we would replace `"logit"` with `"log"`, and for the marginal RD, we would replace `"logit"` with `"identity"` and not exponentiate the coefficient. **With covariate adjustment and bootstrapping.** We can use bootstrapping to estimate the marginal OR and its standard error by estimating the average of the stratum-specific risks under each treatment level, computing the marginal risks under each treatment, and computing marginal effects from the marginal risks. This also makes it fairly straightforward to include covariates in the model. Below we illustrate bootstrapping for the marginal OR with covariates included in the outcome model. ```{r, eval = FALSE} #Bootstrap confidence intervals library(boot) est_fun <- function(data, i) { #Subclassification function mS_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], method = "subclass", estimand = "ATT", subclass = 8) md_boot <- match.data(mS_boot) #Fitting the model fit_boot <- glm(Y_B ~ A * (subclass + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9), data = md_boot, family = quasibinomial(link = "logit")) #Estimate potential outcomes for each unit ## Subset to just the treated for the ATT; remove this for the ATE md_boot <- md_boot[md_boot$A == 1,] ## md_boot$A <- 0 P0 <- mean(predict(fit_boot, md_boot, type = "response")) Odds0 <- P0 / (1 - P0) md_boot$A <- 1 P1 <- mean(predict(fit_boot, md_boot, type = "response")) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(d, est_fun, R = 4999) boot_est boot.ci(boot_est, type = "bca") ``` In this example, we included interactions between treatment and subclass and between treatment and each covariate. Note that because we are interested in the ATT, we restricted the sample used to compute the predicted marginal risks (`P0`) and (`P1`) to just those with `A = 1`. If we were instead estimating the ATE, we would supply `"ATE"` that to the `estimand` argument in the call to `matchit()` and skip the step of restricting the data used for prediction of the marginal risks. As with other methods, to estimate the marginal RR or RD using the above code, the returned object can instead be specified as `P1 / P0` or `P1 - P0`, respectively. #### For survival outcomes Like ORs, HRs are not collapsible, so it is not straightforward to estimate marginal HRs using within-stratum HRs. @austin2013 examined the performance of several propensity score subclassification-based estimators of the marginal HR and found all to be positively biased for non-null effects, consistent with the use of conditional effect estimates as estimates of marginal effects; indeed, the subclassification methods examined all relied on pooling stratum-specific effects. Given these difficulties, the most straightforward method to estimate marginal HRs is to use MMWS weights. We demonstrate this below using essentially the same syntax as used with full matching, only omitting subclass membership as a clustering variable. ```{r} coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights) ``` The robust standard error must be used because of the MMWS weights. ### Estimating Conditional Effects As discussed previously, with binary and time-to-event outcomes, marginal and conditional effects generally differ. Though we have primarily focused on marginal effects, we offer some guidance here on estimating conditional effect as well. Estimating conditional effects typically involves modeling the outcome, and inferences are dependent on this model being correct, even in the presence is perfect covariate balance. Matching provides robustness to some forms of misspecification, however, by limiting the range of the covariate space. So, even though outcome modeling is required, matching prior to modeling the outcome can be beneficial in terms of reducing bias due to model misspecification. On some effect measures, conditional effects typically vary depending on the covariate values at which the conditional effect is to be estimated; for example, the decrease in the risk of death (i.e., on the RD scale) corresponding to receipt of a treatment for a patient with a high baseline risk of death will by higher than that for a patient with a low baseline risk of death. In this sense, there is necessarily effect modification of the RD by baseline risk (which depends on a patient's covariate values). Similarly, for an exposure that increases the risk by a factor (e.g., doubles the risk, for a RR of 2), a patient with a high baseline risk cannot experience the same change in risk as a patient with a low baseline risk could; for example, a RR of 2 is impossible for a patient with a baseline risk of .6 but is certainly plausible for a patient with a baseline risk of .02. In this sense, there is necessarily effect modification of the RR by baseline risk. This is not true for the OR; it is plausible that the effect on the OR scale could be consistent across levels of levels of the predictors because any OR is compatible with possible baselines risks. For this reason, we only consider estimating the conditional OR and not other effect measures. Although several methods exist for estimating conditional ORs after matching or subclassification, the one that generally works well is to run a logistic regression of the outcome on the treatment and covariates and use the coefficient on treatment as an estimate of the effect on the log OR scale. We demonstrate this below using full matching for the ATT: ```{r} mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", estimand = "ATT") md <- match.data(mF) fitcond <- lm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md, weights = weights) coeftest(fitcond, vcov. = vcovCL, cluster = ~subclass)["A",,drop = FALSE] exp(coef(fitcond)["A"]) ``` As with other covariate-adjusted models, it is important not to interpret the coefficients on covariates other than treatment to avoid committing the table 2 fallacy. Other causes of the outcome can be included in the outcome model even if they were not the focus of matching as long as they are not (even possibly) caused by the treatment. Alternative methods of estimating conditional effects include conditional logistic regression after matching and estimating stratum-specific effects after subclassification. We recommend using covariate-adjusted logistic regression models instead because the conditional effect is better defined (i.e., conditional on the specific covariates included in the model) and depends less on the estimand targeted [@forbes2008]. ### Moderation Analysis Moderation analysis involves determining whether a treatment effect differs across levels of another variable. The use of matching with moderation analysis is described in @greenExaminingModerationAnalyses2014. The goal is to achieve balance within each subgroup of the potential moderating variable, and there are several ways of doing so. Broadly, one can either perform matching in the full dataset, perhaps requiring exact matching on the moderator, or one can perform completely separate analyses in each subgroup. We'll demonstrate both approaches below. The chosen approach should be that which achieves the best balance, though we don't demonstrate assessing balance here to maintain focus on effect estimation. We'll consider the binary variable `X5` to be the potential moderator of the effect of `A` on `Y_C`. The first approach involves pooling information across subgroups. This could involve estimating propensity scores using a single model for both groups but exact matching on the potential moderator. The propensity score model could include moderator-by-covariate interactions to allow the propensity score model to vary across subgroups on some covariates. Below, we'll estimate a propensity score using a single propensity score model with a few moderator-by-covariate interactions and exact matching on the moderator, `X5`. We'll perform nearest neighbor matching on the propensity score. ```{r} mP <- matchit(A ~ X1 + X2 + X5*X3 + X4 + X5*X6 + X7 + X5*X8 + X9, data = d, exact = ~X5, method = "nearest") mP ``` Although it is straightforward to assess balance overall using `summary()`, it is more challenging to assess balance within subgroups. The easiest way to check subgroup balance would be to use `cobalt::bal.tab()`, which has a `cluster` argument that can be used to assess balance within subgroups, e.g., by `cobalt::bal.tab(mP, cluster = "X5")`. See the vignette "Appendix 2: Using cobalt with Clustered, Multiply Imputed, and Other Segmented Data" on the `cobalt` [website](https://ngreifer.github.io/cobalt/index.html) for details. If we are satisfied with balance, we can then estimate the subgroup effects using an outcome model with an interaction between the treatment and the moderator. ```{r} mdP <- match.data(mP) fitP <- lm(Y_C ~ A * X5, data = mdP, weights = weights) coeftest(fitP, vcov. = vcovCL, cluster = ~subclass) ``` A second approach is to perform the matching analyses separately and then combine the matched samples. We demonstrate this below. First, we split the data by levels of `X5`. ```{r} d_X5_0 <- subset(d, X5 == 0) d_X5_1 <- subset(d, X5 == 1) ``` Next we perform separate matching analyses in each new dataset, ```{r} mS0 <- matchit(A ~ X1 + X2 + X3 + X4 + X6 + X7 + X8 + X9, data = d_X5_0, method = "nearest") mS1 <- matchit(A ~ X1 + X2 + X3 + X4 + X6 + X7 + X8 + X9, data = d_X5_1, method = "nearest") ``` It is straightforward to assess balance within each subgroup using `summary()`, and `cobalt` functions can be used as well. We omit this step here, but you should not! To estimate the subgroup effects, we need to combine the matched datasets, which we can do using `rbind.matchdata()` (a special `rbind()` method for `matchdata` objects). Then we estimate the moderation effect just as we did previously. ```{r} mdS0 <- match.data(mS0) mdS1 <- match.data(mS1) mdS <- rbind(mdS0, mdS1) fitS <- lm(Y_C ~ A * X5, data = mdS, weights = weights) coeftest(fitS, vcov. = vcovCL, cluster = ~subclass) ``` There are benefits to using either approach, and @greenExaminingModerationAnalyses2014 find that either can be successful at balancing the subgroups. The first approach may be most effective with small samples, where separate propensity score models would be fit with greater uncertainty and an increased possibility of perfect prediction or failure to converge [@wangRelativePerformancePropensity2018]. The second approach may be more effective with larger samples or with matching methods that target balance in the matched sample, such as genetic matching [@kreifMethodsEstimatingSubgroup2012]. With genetic matching, separate subgroup analyses ensure balance is optimized within each subgroup rather than just overall. ### Reporting Results It is important to be as thorough and complete as possible when describing the methods of estimating the treatment effect and the results of the analysis. This improves transparency and replicability of the analysis. Results should at least include the following: - a description of the outcome model used (e.g., logistic regression, a linear model with treatment-covariate interactions and mean-centered covariates, a Cox proportional hazards model with the matching weights applied) - the way the effect was estimated (e.g., as the coefficient on treatment in the outcome model, as the result of a marginal effects procedure) - the way standard errors and confidence intervals were estimated (e.g., using robust standard errors, using cluster-robust standard errors with pair membership as the cluster, using the BCa bootstrap with 4999 bootstrap replications and the entire process of matching and effect estimation included in each replication) - R packages and functions used in estimating the effect and its standard error (e.g., `glm()` in base R, `vcovCL()` in `sandwich`, `boot()` and `boot.ci()` in `boot`) - The effect and its standard error and confidence interval All this is in addition to information about the matching method, propensity score estimation procedure (if used), balance assessment, etc. mentioned in the other vignettes. ## Common Mistakes There are a few common mistakes that should be avoided. It is important not only to avoid these mistakes in one's own research but also to be able to spot these mistakes in others' analyses. ### 1. Failing to include weights Several methods involve weights that are to be used in estimating the treatment effect. With full matching and stratification matching (when analyzed using MMWS), the weights do the entire work of balancing the covariates across the treatment groups. Omitting weights essentially ignores the entire purpose of matching. Some cases are less obvious. When performing matching with replacement and estimating the treatment effect using the `match.data()` output, weights must be included to ensure control units matched to multiple treated units are weighted accordingly. Similarly, when performing k:1 matching where not all treated units receive k matches, weights are required to account for the differential weight of the matched control units. The only time weights can be omitted after pair matching is when performing 1:1 matching without replacement. Including weights even in this scenario will not affect the analysis and it can be good practice to always include weights to prevent this error from occurring. There are some scenarios where weights are not useful because the conditioning occurs through some other means, such as when using the pooling strategy rather than MMWS for estimating marginal effects after stratification. ### 2. Failing to use robust or cluster-robust standard errors Robust standard errors are required when using weights to estimate the treatment effect. The model-based standard errors resulting from weighted least squares or maximum likelihood are inaccurate when using matching weights because they assume weights are frequency weights rather than probability weights. Cluster-robust standard errors account for both the matching weights and pair membership and should be used when appropriate (i.e., with all matching methods other than stratification matching). Sometimes, researchers use functions in the `survey` package to estimate robust standard errors, especially with inverse probability weighting; this is a valid way to compute robust standard errors and will give similar results to `sandwich::vcovHC()`. ### 3. Interpreting conditional effects as marginal effects The distinction between marginal and conditional effects is not always clear both in methodological and applied papers. Some statistical methods are valid only for estimating conditional effects and they should not be used to estimate marginal effects (without further modification). Sometimes conditional effects are desirable, and such methods may be useful for them, but when marginal effects are the target of inference, it is critical not to inappropriately interpret estimates resulting from statistical methods aimed at estimating conditional effects as marginal effects. Although this issue is particularly salient with binary and survival outcomes due to the general noncollapsibility of the OR, RR, and HR, this can also occur with linear models for continuous outcomes or the RD. The following methods estimate **conditional effects** for binary or survival outcomes (with noncollapsible effect measures) and should **not** be used to estimate marginal effects: - Logistic regression or Cox proportional hazards model with covariates and/or the propensity score included, using the coefficient on treatment as the effect estimate - Conditional logistic regression after matching - Stratified Cox regression after matching - Averaging stratum-specific effect estimates after stratification, including using Mantel-Haenszel OR pooling - Including pair or stratum fixed or random effects in a logistic regression model, using the coefficient on treatment as the effect estimate In addition, with continuous outcomes, conditional effects can be mistakenly interpreted as marginal effect estimates when treatment-covariate interactions are present in the outcome model. If the covariates are not centered at their mean in the target population (e.g., the treated group for the ATT, the full sample for the ATE, or the remaining matched sample for an ATM), the coefficient on treatment will not correspond to the marginal effect in the target population; it will correspond to the effect of treatment when the covariate values are equal to zero, which may not be meaningful or plausible. Marginal effects procedures (e.g., using the `margins` package or manually with bootstrapping as demonstrated above) are always the safest way to include covariates in the outcome model, especially in the presence of treatment-covariate interactions. Appropriately centering the covariates is a shortcut that is required when using the coefficient on treatment as a marginal effect estimate for continuous outcomes (demonstrated previously for full matching). ## References ::: {#refs} ::: ## Code to Generate Data used in Examples ```{r, eval = FALSE} #Generating data similar to Austin (2009) for demonstrating treatment effect estimation gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 # Binary outcome gen_Y_B <- function(A, X) { LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] P_B <- plogis(LP_B) rbinom(length(A), 1, P_B) } #Conditional: # OR: 2.4 # logOR: .875 #Marginal: # RD: .144 # RR: 1.54 # logRR: .433 # OR: 1.92 # logOR .655 # Survival outcome gen_Y_S <- function(A, X) { LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] sqrt(-log(runif(length(A)))*2e4*exp(-LP_S)) } #Conditional: # HR: 2.4 # logHR: .875 #Marginal: # HR: 1.57 # logHR: .452 set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) Y_C <- gen_Y_C(A, X) Y_B <- gen_Y_B(A, X) Y_S <- gen_Y_S(A, X) d <- data.frame(A, X, Y_C, Y_B, Y_S) ``` MatchIt/vignettes/references.bib0000644000176200001440000015301714142762570016431 0ustar liggesusers @article{ho2007, title = {Matching as Nonparametric Preprocessing for Reducing Model Dependence in Parametric Causal Inference}, author = {{Ho}, {Daniel E.} and {Imai}, {Kosuke} and {King}, {Gary} and {Stuart}, {Elizabeth A.}}, year = {2007}, month = {06}, date = {2007-06-20}, journal = {Political Analysis}, pages = {199--236}, volume = {15}, number = {3}, doi = {10.1093/pan/mpl013}, url = {https://pan.oxfordjournals.org/content/15/3/199}, langid = {en} } @article{mccaffrey2004, title = {Propensity Score Estimation With Boosted Regression for Evaluating Causal Effects in Observational Studies.}, author = {{McCaffrey}, {Daniel F.} and {Ridgeway}, {Greg} and {Morral}, {Andrew R.}}, year = {2004}, date = {2004}, journal = {Psychological Methods}, pages = {403--425}, volume = {9}, number = {4}, doi = {10.1037/1082-989X.9.4.403}, url = {https://doi.apa.org/getdoi.cfm?doi=10.1037/1082-989X.9.4.403}, langid = {en} } @article{diamond2013, title = {Genetic matching for estimating causal effects: A general multivariate matching method for achieving balance in observational studies}, author = {{Diamond}, {Alexis} and {Sekhon}, {Jasjeet S.}}, year = {2013}, date = {2013}, journal = {Review of Economics and Statistics}, pages = {932{\textendash}945}, volume = {95}, number = {3}, doi = {10.1162/REST_a_00318}, url = {https://www.mitpressjournals.org/doi/abs/10.1162/REST_a_00318}, langid = {en} } @article{belitser2011, title = {Measuring balance and model selection in propensity score methods}, author = {{Belitser}, {Svetlana V.} and {Martens}, {Edwin P.} and {Pestman}, {Wiebe R.} and {Groenwold}, {Rolf H.H.} and {de Boer}, {Anthonius} and {Klungel}, {Olaf H.}}, year = {2011}, month = {11}, date = {2011-11-01}, journal = {Pharmacoepidemiology and Drug Safety}, pages = {1115--1129}, volume = {20}, number = {11}, doi = {10.1002/pds.2188}, url = {https://onlinelibrary.wiley.com.libproxy.lib.unc.edu/doi/10.1002/pds.2188/abstract}, langid = {en} } @article{ali2014, title = {Propensity score balance measures in pharmacoepidemiology: a simulation study}, author = {{Ali}, {M. Sanni} and {Groenwold}, {Rolf H. H.} and {Pestman}, {Wiebe R.} and {Belitser}, {Svetlana V.} and {Roes}, {Kit C. B.} and {Hoes}, {Arno W.} and {de Boer}, {Anthonius} and {Klungel}, {Olaf H.}}, year = {2014}, month = {08}, date = {2014-08-01}, journal = {Pharmacoepidemiology and Drug Safety}, pages = {802--811}, volume = {23}, number = {8}, doi = {10.1002/pds.3574}, url = {https://onlinelibrary.wiley.com.libproxy.lib.unc.edu/doi/10.1002/pds.3574/abstract}, langid = {en} } @article{stuart2013, title = {Prognostic score-based balance measures can be a useful diagnostic for propensity score methods in comparative effectiveness research}, author = {{Stuart}, {Elizabeth A.} and {Lee}, {Brian K.} and {Leacy}, {Finbarr P.}}, year = {2013}, month = {08}, date = {2013-08}, journal = {Journal of Clinical Epidemiology}, pages = {S84}, volume = {66}, number = {8}, doi = {10.1016/j.jclinepi.2013.01.013}, langid = {English} } @article{austin2009, title = {Balance diagnostics for comparing the distribution of baseline covariates between treatment groups in propensity-score matched samples}, author = {{Austin}, {Peter C.}}, year = {2009}, month = {09}, date = {2009-09-15}, journal = {Statistics in Medicine}, pages = {3083--3107}, volume = {28}, number = {25}, doi = {10.1002/sim.3697}, url = {https://dx.doi.org/10.1002/sim.3697}, langid = {en} } @article{austin2015, title = {Moving towards best practice when using inverse probability of treatment weighting (IPTW) using the propensity score to estimate causal treatment effects in observational studies}, author = {{Austin}, {Peter C.} and {Stuart}, {Elizabeth A.}}, year = {2015}, month = {12}, date = {2015-12-10}, journal = {Statistics in Medicine}, pages = {3661--3679}, volume = {34}, number = {28}, doi = {10.1002/sim.6607}, url = {https://onlinelibrary.wiley.com.libproxy.lib.unc.edu/doi/10.1002/sim.6607/abstract}, langid = {en} } @article{hansen2008, title = {The prognostic analogue of the propensity score}, author = {{Hansen}, {Ben B.}}, year = {2008}, month = {02}, date = {2008-02-04}, journal = {Biometrika}, pages = {481--488}, volume = {95}, number = {2}, doi = {10.1093/biomet/asn004}, url = {https://academic.oup.com/biomet/article-lookup/doi/10.1093/biomet/asn004}, langid = {en} } @article{rubin2001, title = {Using Propensity Scores to Help Design Observational Studies: Application to the Tobacco Litigation}, author = {{Rubin}, {Donald B.}}, year = {2001}, month = {12}, date = {2001-12}, journal = {Health Services and Outcomes Research Methodology}, pages = {169--188}, volume = {2}, number = {3-4}, doi = {10.1023/A:1020363010465}, url = {https://link.springer.com/article/10.1023/A%3A1020363010465}, langid = {en} } @article{franklin2014, title = {Metrics for covariate balance in cohort studies of causal effects}, author = {{Franklin}, {Jessica M.} and {Rassen}, {Jeremy A.} and {Ackermann}, {Diana} and {Bartels}, {Dorothee B.} and {Schneeweiss}, {Sebastian}}, year = {2014}, month = {05}, date = {2014-05-10}, journal = {Statistics in Medicine}, pages = {1685--1699}, volume = {33}, number = {10}, doi = {10.1002/sim.6058}, url = {https://doi.wiley.com/10.1002/sim.6058}, langid = {en} } @article{iacus2011, title = {Multivariate Matching Methods That Are Monotonic Imbalance Bounding}, author = {{Iacus}, {Stefano M.} and {King}, {Gary} and {Porro}, {Giuseppe}}, year = {2011}, month = {03}, date = {2011-03}, journal = {Journal of the American Statistical Association}, pages = {345--361}, volume = {106}, number = {493}, doi = {10.1198/jasa.2011.tm09599}, url = {https://dx.doi.org/10.1198/jasa.2011.tm09599}, langid = {en} } @article{heller2010, title = {Using the Cross-Match Test to Appraise Covariate Balance in Matched Pairs}, author = {{Heller}, {Ruth} and {Rosenbaum}, {Paul R.} and {Small}, {Dylan S.}}, year = {2010}, month = {11}, date = {2010-11}, journal = {The American Statistician}, pages = {299--309}, volume = {64}, number = {4}, doi = {10.1198/tast.2010.09210}, url = {https://www.tandfonline.com/doi/abs/10.1198/tast.2010.09210}, langid = {en} } @article{huling2020, title = {Energy Balancing of Covariate Distributions}, author = {{Huling}, {Jared D.} and {Mak}, {Simon}}, year = {2020}, month = {04}, date = {2020-04-29}, journal = {arXiv:2004.13962 [stat]}, url = {https://arxiv.org/abs/2004.13962}, note = {arXiv: 2004.13962} } @article{imai2008, title = {Misunderstandings between Experimentalists and Observationalists about Causal Inference}, author = {{Imai}, {Kosuke} and {King}, {Gary} and {Stuart}, {Elizabeth A.}}, year = {2008}, date = {2008}, journal = {Journal of the Royal Statistical Society. Series A (Statistics in Society)}, pages = {481--502}, volume = {171}, number = {2}, doi = {10.1111/j.1467-985X.2007.00527.x}, url = {https://www.jstor.org/stable/30130768} } @article{huitfeldt2019, title = {On the collapsibility of measures of effect in the counterfactual causal framework}, author = {{Huitfeldt}, {Anders} and {Stensrud}, {Mats J.} and {Suzuki}, {Etsuji}}, year = {2019}, month = {01}, date = {2019-01-07}, journal = {Emerging Themes in Epidemiology}, pages = {1}, volume = {16}, number = {1}, doi = {10.1186/s12982-018-0083-9}, url = {https://doi.org/10.1186/s12982-018-0083-9} } @article{mackinnon1985, title = {Some heteroskedasticity-consistent covariance matrix estimators with improved finite sample properties}, author = {{MacKinnon}, {James G.} and {White}, {Halbert}}, year = {1985}, month = {09}, date = {1985-09}, journal = {Journal of Econometrics}, pages = {305--325}, volume = {29}, number = {3}, doi = {10.1016/0304-4076(85)90158-7}, url = {https://linkinghub.elsevier.com/retrieve/pii/0304407685901587}, langid = {en} } @article{king2015, title = {How Robust Standard Errors Expose Methodological Problems They Do Not Fix, and What to Do About It}, author = {{King}, {Gary} and {Roberts}, {Margaret E.}}, year = {2015}, date = {2015}, journal = {Political Analysis}, pages = {159--179}, volume = {23}, number = {2}, doi = {10.1093/pan/mpu015}, url = {https://dx.doi.org/10.1093/pan/mpu015}, langid = {en} } @article{wan2019, title = {Matched or unmatched analyses with propensity{-}score{\textendash}matched data?}, author = {{Wan}, {Fei}}, year = {2019}, month = {01}, date = {2019-01-30}, journal = {Statistics in Medicine}, pages = {289--300}, volume = {38}, number = {2}, doi = {10.1002/sim.7976}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.7976}, langid = {en} } @article{nguyen2017, title = {Double-adjustment in propensity score matching analysis: choosing a threshold for considering residual imbalance}, author = {{Nguyen}, {Tri-Long} and {Collins}, {Gary S.} and {Spence}, {Jessica} and {Daurès}, {Jean-Pierre} and {Devereaux}, {P. J.} and {Landais}, {Paul} and {Le Manach}, {Yannick}}, year = {2017}, date = {2017}, journal = {BMC Medical Research Methodology}, pages = {78}, volume = {17}, doi = {10.1186/s12874-017-0338-0}, url = {https://dx.doi.org/10.1186/s12874-017-0338-0} } @book{efron1993, title = {An Introduction to the Bootstrap}, author = {{Efron}, {Bradley} and {Tibshirani}, {Robert J.}}, year = {1993}, date = {1993}, publisher = {Springer US}, } @article{austin2014, title = {The use of bootstrapping when using propensity-score matching without replacement: a simulation study}, author = {{Austin}, {Peter C.} and {Small}, {Dylan S.}}, year = {2014}, month = {08}, date = {2014-08-04}, journal = {Statistics in Medicine}, pages = {4306--4319}, volume = {33}, number = {24}, doi = {10.1002/sim.6276}, url = {https://dx.doi.org/10.1002/sim.6276}, langid = {en} } @article{bodory2020, title = {The Finite Sample Performance of Inference Methods for Propensity Score Matching and Weighting Estimators}, author = {{Bodory}, {Hugo} and {Camponovo}, {Lorenzo} and {Huber}, {Martin} and {Lechner}, {Michael}}, year = {2020}, month = {01}, date = {2020-01-02}, journal = {Journal of Business & Economic Statistics}, pages = {183--200}, volume = {38}, number = {1}, doi = {10.1080/07350015.2018.1476247}, url = {https://www.tandfonline.com/doi/full/10.1080/07350015.2018.1476247}, langid = {en} } @article{snowden2011, title = {Implementation of G-Computation on a Simulated Data Set: Demonstration of a Causal Inference Technique}, author = {{Snowden}, {Jonathan M.} and {Rose}, {Sherri} and {Mortimer}, {Kathleen M.}}, year = {2011}, month = {04}, date = {2011-04-01}, journal = {American Journal of Epidemiology}, pages = {731--738}, volume = {173}, number = {7}, doi = {10.1093/aje/kwq472}, url = {https://academic.oup.com/aje/article/173/7/731/104142}, langid = {en} } @article{schafer2008, title = {Average causal effects from nonrandomized studies: A practical guide and simulated example}, author = {{Schafer}, {Joseph L.} and {Kang}, {Joseph}}, year = {2008}, month = {12}, date = {2008-12}, journal = {Psychological Methods}, pages = {279--313}, volume = {13}, number = {4}, doi = {10.1037/a0014268}, url = {https://auth.lib.unc.edu/ezproxy_auth.php?url=https://search.ebscohost.com/login.aspx?direct=true&db=pdh&AN=2008-17368-001&site=ehost-live&scope=site} } @article{pearl2015, title = {Detecting Latent Heterogeneity}, author = {{Pearl}, {Judea}}, year = {2015}, month = {08}, date = {2015-08-27}, journal = {Sociological Methods & Research}, pages = {370--389}, volume = {46}, number = {3}, doi = {10.1177/0049124115600597}, url = {https://dx.doi.org/10.1177/0049124115600597}, langid = {en} } @article{abadie2019, title = {Robust Post-Matching Inference}, author = {{Abadie}, {Alberto} and {Spiess}, {Jann}}, year = {2019}, month = {01}, date = {2019-01}, pages = {34}, langid = {en} } @article{austin2009a, title = {Type I Error Rates, Coverage of Confidence Intervals, and Variance Estimation in Propensity-Score Matched Analyses}, author = {{Austin}, {Peter C.}}, year = {2009}, month = {01}, date = {2009-01-14}, journal = {The International Journal of Biostatistics}, volume = {5}, number = {1}, doi = {10.2202/1557-4679.1146}, url = {https://dx.doi.org/10.2202/1557-4679.1146} } @article{gayat2012, title = {Propensity score applied to survival data analysis through proportional hazards models: a Monte Carlo study}, author = {{Gayat}, {Etienne} and {Resche{-}Rigon}, {Matthieu} and {Mary}, {Jean-Yves} and {Porcher}, {Raphaël}}, year = {2012}, date = {2012}, journal = {Pharmaceutical Statistics}, pages = {222--229}, volume = {11}, number = {3}, doi = {10.1002/pst.537}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/pst.537}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/pst.537}, langid = {en} } @article{austin2012, title = {The performance of different propensity score methods for estimating marginal hazard ratios}, author = {{Austin}, {Peter C.}}, year = {2012}, month = {12}, date = {2012-12-12}, journal = {Statistics in Medicine}, pages = {2837--2849}, volume = {32}, number = {16}, doi = {10.1002/sim.5705}, url = {https://dx.doi.org/10.1002/sim.5705}, langid = {en} } @article{austin2013, title = {The performance of different propensity score methods for estimating marginal hazard ratios}, author = {{Austin}, {Peter C.}}, year = {2013}, month = {07}, date = {2013-07-20}, journal = {Statistics in Medicine}, pages = {2837--2849}, volume = {32}, number = {16}, doi = {10.1002/sim.5705}, url = {https://onlinelibrary.wiley.com/doi/10.1002/sim.5705}, note = {Publisher: John Wiley & Sons, Ltd}, langid = {en} } @article{abadie2008, title = {On the Failure of the Bootstrap for Matching Estimators}, author = {{Abadie}, {Alberto} and {Imbens}, {Guido W.}}, year = {2008}, date = {2008}, journal = {Econometrica}, pages = {1537--1557}, volume = {76}, number = {6}, url = {https://www.jstor.org/stable/40056514}, note = {Publisher: [Wiley, Econometric Society]} } @article{hill2006, title = {Interval estimation for treatment effects using propensity score matching}, author = {{Hill}, {Jennifer} and {Reiter}, {Jerome P.}}, year = {2006}, date = {2006}, journal = {Statistics in Medicine}, pages = {2230--2256}, volume = {25}, number = {13}, doi = {10.1002/sim.2277}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.2277}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/sim.2277}, langid = {en} } @article{austin2017, title = {Estimating the effect of treatment on binary outcomes using full matching on the propensity score}, author = {{Austin}, {Peter C.} and {Stuart}, {Elizabeth A.}}, year = {2017}, month = {12}, date = {2017-12}, journal = {Statistical Methods in Medical Research}, pages = {2505--2525}, volume = {26}, number = {6}, doi = {10.1177/0962280215601134}, url = {https://journals.sagepub.com/doi/10.1177/0962280215601134}, langid = {en} } @article{mackinnon2006, title = {Bootstrap Methods in Econometrics*}, author = {{MacKinnon}, {James G.}}, year = {2006}, month = {09}, date = {2006-09}, journal = {Economic Record}, pages = {S2--S18}, volume = {82}, number = {s1}, doi = {10.1111/j.1475-4932.2006.00328.x}, url = {https://dx.doi.org/10.1111/j.1475-4932.2006.00328.x}, langid = {en} } @article{carpenter2000, title = {Bootstrap confidence intervals: when, which, what? A practical guide for medical statisticians}, author = {{Carpenter}, {James} and {Bithell}, {John}}, year = {2000}, date = {2000}, journal = {Statistics in Medicine}, pages = {1141--1164}, volume = {19}, number = {9}, doi = {10.1002/(SICI)1097-0258(20000515)19:9<1141::AID-SIM479>3.0.CO;2-F}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/%28SICI%291097-0258%2820000515%2919%3A9%3C1141%3A%3AAID-SIM479%3E3.0.CO%3B2-F}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/%28SICI%291097-0258%2820000515%2919%3A9%3C1141%3A%3AAID-SIM479%3E3.0.CO%3B2-F}, langid = {en} } @article{westreich2013, title = {The Table 2 Fallacy: Presenting and Interpreting Confounder and Modifier Coefficients}, author = {{Westreich}, {D.} and {Greenland}, {S.}}, year = {2013}, month = {01}, date = {2013-01-30}, journal = {American Journal of Epidemiology}, pages = {292--298}, volume = {177}, number = {4}, doi = {10.1093/aje/kws412}, url = {https://dx.doi.org/10.1093/aje/kws412}, langid = {en} } @article{austin2013a, title = {The use of propensity score methods with survival or time-to-event outcomes: reporting measures of effect similar to those used in randomized experiments}, author = {{Austin}, {Peter C.}}, year = {2013}, month = {09}, date = {2013-09-30}, journal = {Statistics in Medicine}, pages = {1242--1258}, volume = {33}, number = {7}, doi = {10.1002/sim.5984}, url = {https://dx.doi.org/10.1002/sim.5984}, langid = {en} } @article{austin2020, title = {Covariate-adjusted survival analyses in propensity-score matched samples: Imputing potential time-to-event outcomes}, author = {{Austin}, {Peter C.} and {Thomas}, {Neal} and {Rubin}, {Donald B.}}, year = {2020}, month = {03}, date = {2020-03-01}, journal = {Statistical Methods in Medical Research}, pages = {728--751}, volume = {29}, number = {3}, doi = {10.1177/0962280218817926}, url = {https://doi.org/10.1177/0962280218817926}, note = {Publisher: SAGE Publications Ltd STM}, langid = {en} } @article{austin2020a, title = {Variance estimation when using propensity{-}score matching with replacement with survival or time{-}to{-}event outcomes}, author = {{Austin}, {Peter C.} and {Cafri}, {Guy}}, year = {2020}, month = {02}, date = {2020-02-28}, journal = {Statistics in Medicine}, pages = {1623--1640}, volume = {39}, number = {11}, doi = {10.1002/sim.8502}, url = {https://dx.doi.org/10.1002/sim.8502}, langid = {en} } @article{austin2015a, title = {The performance of inverse probability of treatment weighting and full matching on the propensity score in the presence of model misspecification when estimating the effect of treatment on survival outcomes}, author = {{Austin}, {Peter C.} and {Stuart}, {Elizabeth A.}}, year = {2015}, month = {04}, date = {2015-04-30}, journal = {Statistical Methods in Medical Research}, pages = {1654--1670}, volume = {26}, number = {4}, doi = {10.1177/0962280215584401}, url = {https://dx.doi.org/10.1177/0962280215584401}, langid = {en} } @article{austin2015b, title = {Optimal full matching for survival outcomes: a method that merits more widespread use}, author = {{Austin}, {Peter C.} and {Stuart}, {Elizabeth A.}}, year = {2015}, month = {08}, date = {2015-08-06}, journal = {Statistics in Medicine}, pages = {3949--3967}, volume = {34}, number = {30}, doi = {10.1002/sim.6602}, url = {https://dx.doi.org/10.1002/sim.6602}, langid = {en} } @article{hong2010, title = {Marginal mean weighting through stratification: Adjustment for selection bias in multilevel data}, author = {{Hong}, {Guanglei}}, year = {2010}, month = {10}, date = {2010-10}, journal = {Journal of Educational and Behavioral Statistics}, pages = {499--531}, volume = {35}, number = {5}, doi = {10.3102/1076998609359785}, url = {https://journals.sagepub.com/doi/10.3102/1076998609359785}, langid = {en} } @article{rudolph2016, title = {Optimally combining propensity score subclasses}, author = {{Rudolph}, {Kara E.} and {Colson}, {K. Ellicott} and {Stuart}, {Elizabeth A.} and {Ahern}, {Jennifer}}, year = {2016}, date = {2016}, journal = {Statistics in Medicine}, pages = {4937--4947}, volume = {35}, number = {27}, doi = {10.1002/sim.7046}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.7046}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/sim.7046}, langid = {en} } @article{stampf2010, title = {Estimators and confidence intervals for the marginal odds ratio using logistic regression and propensity score stratification}, author = {{Stampf}, {Susanne} and {Graf}, {Erika} and {Schmoor}, {Claudia} and {Schumacher}, {Martin}}, year = {2010}, date = {2010}, journal = {Statistics in Medicine}, pages = {760--769}, volume = {29}, number = {7-8}, doi = {10.1002/sim.3811}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.3811}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/sim.3811}, langid = {en} } @article{cameron2015, title = {A Practitioner{\textquoteright}s Guide to Cluster-Robust Inference}, author = {{Cameron}, {A. Colin} and {Miller}, {Douglas L.}}, year = {2015}, month = {03}, date = {2015-03-31}, journal = {Journal of Human Resources}, pages = {317--372}, volume = {50}, number = {2}, doi = {10.3368/jhr.50.2.317}, url = {https://jhr.uwpress.org/lookup/doi/10.3368/jhr.50.2.317}, note = {Publisher: University of Wisconsin Press}, langid = {en} } @article{desai2017, title = {A Propensity-score-based Fine Stratification Approach for Confounding Adjustment When Exposure Is Infrequent:}, author = {{Desai}, {Rishi J.} and {Rothman}, {Kenneth J.} and {Bateman}, {Brian T.} and {Hernandez-Diaz}, {Sonia} and {Huybrechts}, {Krista F.}}, year = {2017}, month = {03}, date = {2017-03}, journal = {Epidemiology}, pages = {249--257}, volume = {28}, number = {2}, doi = {10.1097/EDE.0000000000000595}, url = {https://Insights.ovid.com/crossref?an=00001648-201703000-00014}, langid = {en} } @article{zubizarreta2014, title = {Matching for balance, pairing for heterogeneity in an observational study of the effectiveness of for-profit and not-for-profit high schools in Chile}, author = {{Zubizarreta}, {José R.} and {Paredes}, {Ricardo D.} and {Rosenbaum}, {Paul R.}}, year = {2014}, month = {03}, date = {2014-03}, journal = {The Annals of Applied Statistics}, pages = {204--231}, volume = {8}, number = {1}, doi = {10.1214/13-AOAS713}, url = {https://projecteuclid.org/euclid.aoas/1396966284}, langid = {en} } @article{stuart2008, title = {Developing practical recommendations for the use of propensity scores: Discussion of {\textquoteleft}A critical appraisal of propensity score matching in the medical literature between 1996 and 2003{\textquoteright} by Peter Austin,Statistics in Medicine}, author = {{Stuart}, {Elizabeth A.}}, year = {2008}, date = {2008}, journal = {Statistics in Medicine}, pages = {2062--2065}, volume = {27}, number = {12}, doi = {10.1002/sim.3207}, url = {https://dx.doi.org/10.1002/sim.3207}, langid = {en} } @article{austin2014a, title = {The use of bootstrapping when using propensity{-}score matching without replacement: a simulation study}, author = {{Austin}, {Peter C.} and {Small}, {Dylan S.}}, year = {2014}, month = {08}, date = {2014-08-04}, journal = {Statistics in Medicine}, pages = {4306--4319}, volume = {33}, number = {24}, doi = {10.1002/sim.6276}, url = {https://dx.doi.org/10.1002/sim.6276}, langid = {en} } @article{thoemmes2011, title = {A Systematic Review of Propensity Score Methods in the Social Sciences}, author = {{Thoemmes}, {Felix J.} and {Kim}, {Eun Sook}}, year = {2011}, month = {02}, date = {2011-02-07}, journal = {Multivariate Behavioral Research}, pages = {90--118}, volume = {46}, number = {1}, doi = {10.1080/00273171.2011.540475}, url = {https://dx.doi.org/10.1080/00273171.2011.540475}, langid = {en} } @article{zakrison2018, title = {A systematic review of propensity score methods in the acute care surgery literature: avoiding the pitfalls and proposing a set of reporting guidelines}, author = {{Zakrison}, {T. L.} and {Austin}, {Peter C.} and {McCredie}, {V. A.}}, year = {2018}, month = {06}, date = {2018-06-01}, journal = {European Journal of Trauma and Emergency Surgery}, pages = {385--395}, volume = {44}, number = {3}, doi = {10.1007/s00068-017-0786-6}, url = {https://doi.org/10.1007/s00068-017-0786-6}, langid = {en} } @article{stuart2010, title = {Matching Methods for Causal Inference: A Review and a Look Forward}, author = {{Stuart}, {Elizabeth A.}}, year = {2010}, month = {02}, date = {2010-02}, journal = {Statistical Science}, pages = {1--21}, volume = {25}, number = {1}, doi = {10.1214/09-STS313}, url = {https://projecteuclid.org/euclid.ss/1280841730}, langid = {en} } @article{austin2013b, title = {A comparison of 12 algorithms for matching on the propensity score}, author = {{Austin}, {Peter C.}}, year = {2013}, month = {10}, date = {2013-10-07}, journal = {Statistics in Medicine}, pages = {1057--1069}, volume = {33}, number = {6}, doi = {10.1002/sim.6004}, url = {https://dx.doi.org/10.1002/sim.6004}, langid = {en} } @article{rubin1973, title = {Matching to Remove Bias in Observational Studies}, author = {{Rubin}, {Donald B.}}, year = {1973}, month = {03}, date = {1973-03}, journal = {Biometrics}, pages = {159}, volume = {29}, number = {1}, doi = {10.2307/2529684}, url = {https://dx.doi.org/10.2307/2529684} } @article{hansen2006, title = {Optimal Full Matching and Related Designs via Network Flows}, author = {{Hansen}, {Ben B.} and {Klopfer}, {Stephanie O.}}, year = {2006}, month = {09}, date = {2006-09}, journal = {Journal of Computational and Graphical Statistics}, pages = {609--627}, volume = {15}, number = {3}, doi = {10.1198/106186006X137047}, url = {https://www.tandfonline.com/doi/abs/10.1198/106186006X137047}, langid = {en} } @article{gu1993, title = {Comparison of Multivariate Matching Methods: Structures, Distances, and Algorithms}, author = {{Gu}, {Xing Sam} and {Rosenbaum}, {Paul R.}}, year = {1993}, month = {12}, date = {1993-12}, journal = {Journal of Computational and Graphical Statistics}, pages = {405}, volume = {2}, number = {4}, doi = {10.2307/1390693}, url = {https://www.jstor.org/stable/1390693?origin=crossref}, langid = {en} } @article{hansen2004, title = {Full Matching in an Observational Study of Coaching for the SAT}, author = {{Hansen}, {Ben B.}}, year = {2004}, month = {09}, date = {2004-09}, journal = {Journal of the American Statistical Association}, pages = {609--618}, volume = {99}, number = {467}, doi = {10.1198/016214504000000647}, url = {https://www.tandfonline.com/doi/abs/10.1198/016214504000000647}, langid = {en} } @article{stuart2008a, title = {Using full matching to estimate causal effects in nonexperimental studies: Examining the relationship between adolescent marijuana use and adult outcomes.}, author = {{Stuart}, {Elizabeth A.} and {Green}, {Kerry M.}}, year = {2008}, month = {03}, date = {2008-03}, journal = {Developmental Psychology}, pages = {395--406}, volume = {44}, number = {2}, doi = {10.1037/0012-1649.44.2.395}, url = {https://dx.doi.org/10.1037/0012-1649.44.2.395}, langid = {en} } @article{sekhon2011, title = {Multivariate and Propensity Score Matching Software with Automated Balance Optimization: The Matching package for R}, author = {{Sekhon}, {Jasjeet S.}}, year = {2011}, month = {06}, date = {2011-06-14}, journal = {Journal of Statistical Software}, pages = {1--52}, volume = {42}, number = {1}, doi = {10.18637/jss.v042.i07}, url = {https://www.jstatsoft.org/index.php/jss/article/view/v042i07}, note = {Number: 1}, langid = {en} } @article{iacus2012, title = {Causal Inference without Balance Checking: Coarsened Exact Matching}, author = {{Iacus}, {Stefano M.} and {King}, {Gary} and {Porro}, {Giuseppe}}, year = {2012}, date = {2012}, journal = {Political Analysis}, pages = {1--24}, volume = {20}, number = {1}, doi = {10.1093/pan/mpr013}, url = {https://www.cambridge.org/core/product/identifier/S1047198700012985/type/journal_article}, langid = {en} } @article{iacus2009, title = {cem: Software for Coarsened Exact Matching}, author = {{Iacus}, {Stefano M.} and {King}, {Gary} and {Porro}, {Giuseppe}}, year = {2009}, month = {06}, date = {2009-06-25}, journal = {Journal of Statistical Software}, pages = {1--27}, volume = {30}, number = {1}, doi = {10.18637/jss.v030.i09}, url = {https://www.jstatsoft.org/index.php/jss/article/view/v030i09}, note = {Number: 1}, langid = {en} } @article{austin2010, title = {The performance of different propensity-score methods for estimating differences in proportions (risk differences or absolute risk reductions) in observational studies}, author = {{Austin}, {Peter C.}}, year = {2010}, month = {01}, date = {2010-01-27}, journal = {Statistics in Medicine}, pages = {2137--2148}, volume = {29}, number = {20}, doi = {10.1002/sim.3854}, url = {https://dx.doi.org/10.1002/sim.3854}, langid = {en} } @article{orihara2021, title = {Determination of the optimal number of strata for propensity score subclassification}, author = {{Orihara}, {Shunichiro} and {Hamada}, {Etsuo}}, year = {2021}, month = {01}, date = {2021-01-01}, journal = {Statistics & Probability Letters}, pages = {108951}, volume = {168}, doi = {10.1016/j.spl.2020.108951}, url = {https://www.sciencedirect.com/science/article/pii/S0167715220302546}, langid = {en} } @article{austin2011a, title = {Optimal caliper widths for propensity{-}score matching when estimating differences in means and differences in proportions in observational studies}, author = {{Austin}, {Peter C.}}, year = {2011}, month = {03}, date = {2011-03}, journal = {Pharmaceutical Statistics}, pages = {150--161}, volume = {10}, number = {2}, doi = {10.1002/pst.433}, url = {https://dx.doi.org/10.1002/pst.433}, langid = {en} } @article{king2019, title = {Why Propensity Scores Should Not Be Used for Matching}, author = {{King}, {Gary} and {Nielsen}, {Richard}}, year = {2019}, month = {05}, date = {2019-05-07}, journal = {Political Analysis}, pages = {1--20}, doi = {10.1017/pan.2019.11}, url = {https://www.cambridge.org/core/product/identifier/S1047198719000111/type/journal_article}, langid = {en} } @article{austin2010a, title = {Statistical Criteria for Selecting the Optimal Number of Untreated Subjects Matched to Each Treated Subject When Using Many-to-One Matching on the Propensity Score}, author = {{Austin}, {Peter C.}}, year = {2010}, month = {08}, date = {2010-08-28}, journal = {American Journal of Epidemiology}, pages = {1092--1097}, volume = {172}, number = {9}, doi = {10.1093/aje/kwq224}, url = {https://dx.doi.org/10.1093/aje/kwq224}, langid = {en} } @article{austin2011b, title = {An Introduction to Propensity Score Methods for Reducing the Effects of Confounding in Observational Studies}, author = {{Austin}, {Peter C.}}, year = {2011}, month = {05}, date = {2011-05-31}, journal = {Multivariate Behavioral Research}, pages = {399--424}, volume = {46}, number = {3}, doi = {10.1080/00273171.2011.568786}, url = {https://dx.doi.org/10.1080/00273171.2011.568786}, langid = {en} } @article{abadie2006, title = {Large Sample Properties of Matching Estimators for Average Treatment Effects}, author = {{Abadie}, {Alberto} and {Imbens}, {Guido W.}}, year = {2006}, month = {01}, date = {2006-01}, journal = {Econometrica}, pages = {235--267}, volume = {74}, number = {1}, doi = {10.1111/j.1468-0262.2006.00655.x}, url = {https://doi.wiley.com/10.1111/j.1468-0262.2006.00655.x}, langid = {en} } @article{abadie2016, title = {Matching on the Estimated Propensity Score}, author = {{Abadie}, {Alberto} and {Imbens}, {Guido W.}}, year = {2016}, date = {2016}, journal = {Econometrica}, pages = {781--807}, volume = {84}, number = {2}, doi = {10.3982/ECTA11293}, url = {https://www.econometricsociety.org/doi/10.3982/ECTA11293}, langid = {en} } @article{liang1986, title = {Longitudinal data analysis using generalized linear models}, author = {{Liang}, {Kung-Yee} and {Zeger}, {Scott L.}}, year = {1986}, date = {1986}, journal = {Biometrika}, pages = {13--22}, volume = {73}, number = {1}, doi = {10.1093/biomet/73.1.13}, url = {https://academic.oup.com/biomet/article-lookup/doi/10.1093/biomet/73.1.13}, langid = {en} } @article{austin2007a, title = {The performance of different propensity score methods for estimating marginal odds ratios}, author = {{Austin}, {Peter C.}}, year = {2007}, date = {2007}, journal = {Statistics in Medicine}, pages = {3078--3094}, volume = {26}, number = {16}, doi = {10.1002/sim.2781}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.2781}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/sim.2781}, langid = {en} } @article{austin2009b, title = {Type I Error Rates, Coverage of Confidence Intervals, and Variance Estimation in Propensity-Score Matched Analyses}, author = {{Austin}, {Peter C.}}, year = {2009}, month = {01}, date = {2009-01-14}, journal = {The International Journal of Biostatistics}, volume = {5}, number = {1}, doi = {10.2202/1557-4679.1146}, url = {https://dx.doi.org/10.2202/1557-4679.1146} } @article{austin2008, title = {The performance of different propensity-score methods for estimating relative risks}, author = {{Austin}, {Peter C.}}, year = {2008}, month = {06}, date = {2008-06}, journal = {Journal of Clinical Epidemiology}, pages = {537--545}, volume = {61}, number = {6}, doi = {10.1016/j.jclinepi.2007.07.011}, url = {https://dx.doi.org/10.1016/j.jclinepi.2007.07.011}, langid = {en} } @article{austin2010b, title = {The performance of different propensity-score methods for estimating differences in proportions (risk differences or absolute risk reductions) in observational studies}, author = {{Austin}, {Peter C.}}, year = {2010}, month = {01}, date = {2010-01-27}, journal = {Statistics in Medicine}, pages = {2137--2148}, volume = {29}, number = {20}, doi = {10.1002/sim.3854}, url = {https://dx.doi.org/10.1002/sim.3854}, langid = {en} } @article{vanderweele2019, title = {Principles of confounder selection}, author = {{VanderWeele}, {Tyler J.}}, year = {2019}, month = {03}, date = {2019-03}, journal = {European Journal of Epidemiology}, pages = {211--219}, volume = {34}, number = {3}, doi = {10.1007/s10654-019-00494-6}, url = {https://dx.doi.org/10.1007/s10654-019-00494-6}, langid = {en} } @article{rosenbaum1983, title = {The central role of the propensity score in observational studies for causal effects}, author = {{Rosenbaum}, {Paul R.} and {Rubin}, {Donald B.}}, year = {1983}, month = {04}, date = {1983-04-01}, journal = {Biometrika}, pages = {41--55}, volume = {70}, number = {1}, doi = {10.1093/biomet/70.1.41}, url = {https://biomet.oxfordjournals.org/content/70/1/41}, langid = {en} } @article{rubin1973a, title = {Matching to Remove Bias in Observational Studies}, author = {{Rubin}, {Donald B.}}, year = {1973}, month = {03}, date = {1973-03}, journal = {Biometrics}, pages = {159}, volume = {29}, number = {1}, doi = {10.2307/2529684}, url = {https://dx.doi.org/10.2307/2529684} } @article{cochran1973, title = {Controlling Bias in Observational Studies: A Review}, author = {{Cochran}, {William G.} and {Rubin}, {Donald B.}}, year = {1973}, date = {1973}, journal = {Sankhy{\={a}}: The Indian Journal of Statistics, Series A (1961-2002)}, pages = {417--446}, volume = {35}, number = {4}, url = {https://www.jstor.org/stable/25049893} } @article{hansen2008a, title = {The prognostic analogue of the propensity score}, author = {{Hansen}, {Ben B.}}, year = {2008}, month = {02}, date = {2008-02-04}, journal = {Biometrika}, pages = {481--488}, volume = {95}, number = {2}, doi = {10.1093/biomet/asn004}, url = {https://academic.oup.com/biomet/article-lookup/doi/10.1093/biomet/asn004}, langid = {en} } @article{mao2018, title = {Propensity score weighting analysis and treatment effect discovery}, author = {{Mao}, {Huzhang} and {Li}, {Liang} and {Greene}, {Tom}}, year = {2018}, month = {06}, date = {2018-06-19}, journal = {Statistical Methods in Medical Research}, pages = {096228021878117}, doi = {10.1177/0962280218781171}, url = {https://journals.sagepub.com/doi/10.1177/0962280218781171}, langid = {en} } @article{rosenbaum1985, title = {The Bias Due to Incomplete Matching}, author = {{Rosenbaum}, {Paul R.} and {Rubin}, {Donald B.}}, year = {1985}, date = {1985}, journal = {Biometrics}, pages = {103--116}, volume = {41}, number = {1}, doi = {10.2307/2530647}, url = {https://www.jstor.org/stable/2530647} } @article{wang2020, title = {To use or not to use propensity score matching?}, author = {{Wang}, {Jixian}}, year = {2020}, month = {08}, date = {2020-08-10}, journal = {Pharmaceutical Statistics}, doi = {10.1002/pst.2051}, url = {https://dx.doi.org/10.1002/pst.2051}, langid = {en} } @article{austin2015c, title = {Estimating the effect of treatment on binary outcomes using full matching on the propensity score}, author = {{Austin}, {Peter C.} and {Stuart}, {Elizabeth A.}}, year = {2015}, month = {09}, date = {2015-09}, journal = {Statistical Methods in Medical Research}, pages = {2505--2525}, volume = {26}, number = {6}, doi = {10.1177/0962280215601134}, url = {https://dx.doi.org/10.1177/0962280215601134}, langid = {en} } @article{austin2009c, title = {The Relative Ability of Different Propensity Score Methods to Balance Measured Covariates Between Treated and Untreated Subjects in Observational Studies}, author = {{Austin}, {Peter C.}}, year = {2009}, month = {08}, date = {2009-08-14}, journal = {Medical Decision Making}, pages = {661--677}, volume = {29}, number = {6}, doi = {10.1177/0272989x09341755}, url = {https://dx.doi.org/10.1177/0272989X09341755}, langid = {en} } @article{rosenbaum1985a, title = {Constructing a Control Group Using Multivariate Matched Sampling Methods That Incorporate the Propensity Score}, author = {{Rosenbaum}, {Paul R.} and {Rubin}, {Donald B.}}, year = {1985}, month = {02}, date = {1985-02}, journal = {The American Statistician}, pages = {33}, volume = {39}, number = {1}, doi = {10.2307/2683903}, url = {https://dx.doi.org/10.2307/2683903} } @article{forbes2008, title = {Inverse probability weighted estimation of the marginal odds ratio: Correspondence regarding {\textquoteleft}The performance of different propensity score methods for estimating marginal odds ratios{\textquoteright} by P. Austin, Statictics in Medicine, 2007; 26:3078{\textendash}3094}, author = {{Forbes}, {Andrew} and {Shortreed}, {Susan}}, year = {2008}, date = {2008}, journal = {Statistics in Medicine}, pages = {5556--5559}, volume = {27}, number = {26}, doi = {10.1002/sim.3362}, url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.3362}, note = {{\_}eprint: https://onlinelibrary.wiley.com/doi/pdf/10.1002/sim.3362}, langid = {en} } @article{dugoff2014, title = {Generalizing Observational Study Results: Applying Propensity Score Methods to Complex Surveys}, author = {{DuGoff}, {Eva H.} and {Schuler}, {Megan} and {Stuart}, {Elizabeth A.}}, year = {2014}, month = {02}, date = {2014-02}, journal = {Health Services Research}, pages = {284--303}, volume = {49}, number = {1}, doi = {10.1111/1475-6773.12090}, url = {https://doi.wiley.com/10.1111/1475-6773.12090}, langid = {en} } @article{austin2016, title = {Propensity score matching and complex surveys}, author = {{Austin}, {Peter C.} and {Jembere}, {Nathaniel} and {Chiu}, {Maria}}, year = {2016}, month = {07}, date = {2016-07-26}, journal = {Statistical Methods in Medical Research}, pages = {1240--1257}, volume = {27}, number = {4}, doi = {10.1177/0962280216658920}, url = {https://dx.doi.org/10.1177/0962280216658920}, langid = {en} } @article{lenis2019, title = {It{\textquoteright}s all about balance: propensity score matching in the context of complex survey data}, author = {{Lenis}, {David} and {Nguyen}, {Trang Quynh} and {Dong}, {Nianbo} and {Stuart}, {Elizabeth A.}}, year = {2019}, month = {01}, date = {2019-01-01}, journal = {Biostatistics}, pages = {147--163}, volume = {20}, number = {1}, doi = {10.1093/biostatistics/kxx063}, url = {https://academic.oup.com/biostatistics/article/20/1/147/4780267}, langid = {en} } @article{rosenbaum2020, title = {Modern Algorithms for Matching in Observational Studies}, author = {{Rosenbaum}, {Paul R.}}, year = {2020}, date = {2020}, journal = {Annual Review of Statistics and Its Application}, pages = {143--176}, volume = {7}, number = {1}, doi = {10.1146/annurev-statistics-031219-041058}, url = {https://doi.org/10.1146/annurev-statistics-031219-041058}, note = {{\_}eprint: https://doi.org/10.1146/annurev-statistics-031219-041058} } @article{ming2000, title = {Substantial Gains in Bias Reduction from Matching with a Variable Number of Controls}, author = {{Ming}, {Kewei} and {Rosenbaum}, {Paul R.}}, year = {2000}, month = {03}, date = {2000-03}, journal = {Biometrics}, pages = {118--124}, volume = {56}, number = {1}, doi = {10.1111/j.0006-341X.2000.00118.x}, url = {https://doi.wiley.com/10.1111/j.0006-341X.2000.00118.x}, langid = {en} } @article{greenExaminingModerationAnalyses2014, title = {Examining Moderation Analyses in Propensity Score Methods: {{Application}} to Depression and Substance Use}, shorttitle = {Examining Moderation Analyses in Propensity Score Methods}, author = {Green, Kerry M. and Stuart, Elizabeth A.}, year = {2014}, month = oct, volume = {82}, pages = {773--783}, issn = {0022-006X}, doi = {10.1037/a0036515}, abstract = {Objective: This study provides guidance on how propensity score methods can be combined with moderation analyses (i.e., effect modification) to examine subgroup differences in potential causal effects in nonexperimental studies. As a motivating example, we focus on how depression may affect subsequent substance use differently for men and women. Method: Using data from a longitudinal community cohort study (N = 952) of urban African Americans with assessments in childhood, adolescence, young adulthood, and midlife, we estimate the influence of depression by young adulthood on substance use outcomes in midlife, and whether that influence varies by gender. We illustrate and compare 5 different techniques for estimating subgroup effects using propensity score methods, including separate propensity score models and matching for men and women, a joint propensity score model for men and women with matching separately and together by gender, and a joint male/female propensity score model that includes theoretically important gender interactions with matching separately and together by gender. Results: Analyses showed that estimating separate models for men and women yielded the best balance and, therefore, is a preferred technique when subgroup analyses are of interest, at least in this data. Results also showed substance use consequences of depression but no significant gender differences. Conclusions: It is critical to prespecify subgroup effects before the estimation of propensity scores and to check balance within subgroups regardless of the type of propensity score model used. Results also suggest that depression may affect multiple substance use outcomes in midlife for both men and women relatively equally. (PsycINFO Database Record (c) 2016 APA, all rights reserved). (journal abstract)}, file = {/Users/NoahGreifer/Zotero/storage/FUTHAJER/Green and Stuart - 2014 - Examining moderation analyses in propensity score .pdf}, journal = {Journal of Consulting and Clinical Psychology}, keywords = {Blacks,causal effects,causality,depression,Drug Abuse,effect modification,Gender differences,Human Sex Differences,Major Depression,nonexperimental study,Observation Methods,observational data,substance abuse}, number = {5}, series = {Advances in {{Data Analytic Methods}}} } @article{kreifMethodsEstimatingSubgroup2012, title = {Methods for Estimating Subgroup Effects in Cost-Effectiveness Analyses That Use Observational Data}, author = {Kreif, Noemi and Grieve, Richard and Radice, Rosalba and Sadique, Zia and Ramsahai, Roland and Sekhon, Jasjeet S.}, year = {2012}, month = nov, volume = {32}, pages = {750--763}, issn = {0272-989X}, doi = {10.1177/0272989X12448929}, abstract = {Decision makers require cost-effectiveness estimates for patient subgroups. In nonrandomized studies, propensity score (PS) matching and inverse probability of treatment weighting (IPTW) can address overt selection bias, but only if they balance observed covariates between treatment groups. Genetic matching (GM) matches on the PS and individual covariates using an automated search algorithm to directly balance baseline covariates. This article compares these methods for estimating subgroup effects in cost-effectiveness analyses (CEA). The motivating case study is a CEA of a pharmaceutical intervention, drotrecogin alfa (DrotAA), for patient subgroups with severe sepsis (n = 2726). Here, GM reported better covariate balance than PS matching and IPTW. For the subgroup at a high level of baseline risk, the probability that DrotAA was cost-effective ranged from 30\% (IPTW) to 90\% (PS matching and GM), at a threshold of \textsterling 20 000 per quality-adjusted life-year. We then compared the methods in a simulation study, in which initially the PS was correctly specified and then misspecified, for example, by ignoring the subgroup-specific treatment assignment. Relative performance was assessed as bias and root mean squared error (RMSE) in the estimated incremental net benefits. When the PS was correctly specified and inverse probability weights were stable, each method performed well; IPTW reported the lowest RMSE. When the subgroup-specific treatment assignment was ignored, PS matching and IPTW reported covariate imbalance and bias; GM reported better balance, less bias, and more precise estimates. We conclude that if the PS is correctly specified and the weights for IPTW are stable, each method can provide unbiased cost-effectiveness estimates. However, unlike IPTW and PS matching, GM is relatively robust to PS misspecification.}, file = {/Users/NoahGreifer/Zotero/storage/2DX775MH/Web Appendix.pdf;/Users/NoahGreifer/Zotero/storage/IUAWWYQP/Kreif et al. - 2012 - Methods for Estimating Subgroup Effects in Cost-Ef.pdf}, journal = {Medical Decision Making}, language = {en}, number = {6} } @article{wangRelativePerformancePropensity2018, title = {Relative {{Performance}} of {{Propensity Score Matching Strategies}} for {{Subgroup Analyses}}}, author = {Wang, Shirley V. and Jin, Yinzhu and Fireman, Bruce and Gruber, Susan and He, Mengdong and Wyss, Richard and Shin, HoJin and Ma, Yong and Keeton, Stephine and Karami, Sara and Major, Jacqueline M. and Schneeweiss, Sebastian and Gagne, Joshua J.}, year = {2018}, month = aug, volume = {187}, pages = {1799--1807}, issn = {0002-9262}, doi = {10.1093/aje/kwy049}, abstract = {Abstract. Postapproval drug safety studies often use propensity scores (PSs) to adjust for a large number of baseline confounders. These studies may involve ex}, file = {/Users/NoahGreifer/Zotero/storage/PFIYDGIG/Wang et al. - 2018 - Relative Performance of Propensity Score Matching .pdf}, journal = {American Journal of Epidemiology}, language = {en}, number = {8} } @article{zubizarretaMatchingBalancePairing2014, title = {Matching for Balance, Pairing for Heterogeneity in an Observational Study of the Effectiveness of for-Profit and Not-for-Profit High Schools in {{Chile}}}, author = {Zubizarreta, Jos{\'e} R. and Paredes, Ricardo D. and Rosenbaum, Paul R.}, year = {2014}, month = mar, volume = {8}, pages = {204--231}, issn = {1932-6157}, doi = {10.1214/13-AOAS713}, file = {/Users/NoahGreifer/Zotero/storage/JACNSVBA/Zubizarreta et al. - 2014 - Matching for balance, pairing for heterogeneity in.pdf}, journal = {The Annals of Applied Statistics}, language = {en}, number = {1} } @article{bennettBuildingRepresentativeMatched2020, title = {Building {{Representative Matched Samples With Multi}}-{{Valued Treatments}} in {{Large Observational Studies}}}, author = {Bennett, Magdalena and Vielma, Juan Pablo and Zubizarreta, Jos{\'e} R.}, year = {2020}, month = may, pages = {1--29}, issn = {1061-8600, 1537-2715}, doi = {10.1080/10618600.2020.1753532}, abstract = {In this article, we present a new way of matching in observational studies that overcomes three limitations of existing matching approaches. First, it directly balances covariates with multi-valued treatments without explicitly estimating the generalized propensity score. Second, it builds self-weighted matched samples that are representative of a target population by design. Third, it can handle large datasets, with hundreds of thousands of observations, in a couple of minutes. The key insights of this new approach to matching are balancing the treatment groups relative to a target population and positing a linear-sized mixed integer formulation of the matching problem. We formally show that this formulation is more effective than alternative quadratic-sized formulations, as its reduction in size does not affect its strength from the standpoint of its linear programming relaxation. We also show that this formulation can be used for matching with distributional covariate balance in polynomial time under certain assumptions on the covariates and that it can handle large datasets in practice even when the assumptions are not satisfied. This algorithmic characterization is key to handling large datasets. We illustrate this new approach to matching in both a simulation study and an observational study of the impact of an earthquake on educational attainment. With this approach, the results after matching can be visualized with simple and transparent graphical displays: while increasing levels of exposure to the earthquake have a negative impact on school attendance, there is no effect on college admission test scores. Supplementary materials for this article are available online.}, file = {/Users/NoahGreifer/Zotero/storage/FPDB8NKK/Bennett et al. - 2020 - Building Representative Matched Samples With Multi.pdf}, journal = {Journal of Computational and Graphical Statistics}, language = {en} } @article{delosangelesresaDirectStableWeight2020, title = {Direct and Stable Weight Adjustment in Non-Experimental Studies with Multivalued Treatments: Analysis of the Effect of an Earthquake on Post-Traumatic Stress}, author = {{de los Angeles Resa}, Mar{\'i}a and Zubizarreta, Jos{\'e} R.}, year = {2020}, month = apr, volume = {n/a}, publisher = {{John Wiley \& Sons, Ltd}}, issn = {0964-1998}, doi = {10.1111/rssa.12561}, abstract = {Summary In February 2010, a massive earthquake struck Chile, causing devastation in certain parts of the country, affecting other areas, and leaving territories untouched. 2 months after the earthquake, Chile's Ministry of Social Development reinterviewed a representative subsample of its National Socioeconomic Characterization Survey, which had been completed 2 months before the earthquake, thereby creating a prospective longitudinal survey with detailed information of the same individuals before and after the earthquake. We use a new weighting method for non-experimental studies with multivalued treatments to estimate the effect of levels of exposure to the earthquake on post-traumatic stress. Unlike common weighting approaches for multivalued treatments, this new method does not require explicit modelling of the generalized propensity score and instead focuses on directly balancing the covariates across the multivalued treatments with weights that have minimum variance. As a result, the weighting estimator is stable and approximately unbiased. Furthermore, the weights are constrained to avoid model extrapolation. We illustrate this new method in a simulation study, with both categorical and continuous treatments. The results show that directly targeting balance instead of explicitly modelling the treatment assignment probabilities tends to provide the best results in terms of bias and root-mean-square error. Using this method, we estimate the effect of the intensity of the earthquake on post-traumatic stress. We implement this method in the new package msbw for R.}, file = {/Users/NoahGreifer/Zotero/storage/RN4P9UNF/de los Angeles Resa and Zubizarreta - 2020 - Direct and stable weight adjustment in non-experim.pdf}, journal = {Journal of the Royal Statistical Society: Series A (Statistics in Society)}, keywords = {Causal inference,Inverse probability weights,Observational studies,Propensity score}, number = {n/a} } @article{visconti2018, title = {Handling Limited Overlap in Observational Studies with Cardinality Matching}, author = {{Visconti}, {Giancarlo} and {Zubizarreta}, {Jos{\'e} R.}}, year = {2018}, date = {2018}, journal = {Observational Studies}, pages = {217--249}, volume = {4}, number = {1}, doi = {10.1353/obs.2018.0012}, url = {https://doi.org/10.1353/obs.2018.0012}, langid = {en} } @article{greiferChoosingEstimandWhen2021, title = {Choosing the {{Estimand When Matching}} or {{Weighting}} in {{Observational Studies}}}, author = {Greifer, Noah and Stuart, Elizabeth A.}, year = {2021}, month = jun, journal = {arXiv:2106.10577 [stat]}, eprint = {2106.10577}, eprinttype = {arxiv}, primaryclass = {stat}, abstract = {Matching and weighting methods for observational studies require the choice of an estimand, the causal effect with reference to a specific target population. Commonly used estimands include the average treatment effect in the treated (ATT), the average treatment effect in the untreated (ATU), the average treatment effect in the population (ATE), and the average treatment effect in the overlap (i.e., equipoise population; ATO). Each estimand has its own assumptions, interpretation, and statistical methods that can be used to estimate it. This article provides guidance on selecting and interpreting an estimand to help medical researchers correctly implement statistical methods used to estimate causal effects in observational studies and to help audiences correctly interpret the results and limitations of these studies. The interpretations of the estimands resulting from regression and instrumental variable analyses are also discussed. Choosing an estimand carefully is essential for making valid inferences from the analysis of observational data and ensuring results are replicable and useful for practitioners.}, archiveprefix = {arXiv}, url = {https://arxiv.org/abs/2106.10577}, keywords = {Statistics - Methodology}, } MatchIt/R/0000755000176200001440000000000014170752470012013 5ustar liggesusersMatchIt/R/aux_functions.R0000644000176200001440000011366114147012765015033 0ustar liggesusers#Auxiliary functions; some from WeightIt #Function to process inputs and throw warnings or errors if inputs are incompatible with methods check.inputs <- function(mcall, method, distance, exact, mahvars, antiexact, caliper, discard, reestimate, s.weights, replace, ratio, m.order, estimand, ..., min.controls = NULL, max.controls = NULL) { null.method <- is.null(method) if (null.method) { method <- "NULL" } else { method <- match_arg(method, c("exact", "cem", "nearest", "optimal", "full", "genetic", "subclass", "cardinality")) } ignored.inputs <- character(0) error.inputs <- character(0) if (null.method) { for (i in c("exact", "mahvars", "antiexact", "caliper", "std.caliper", "replace", "ratio", "min.controls", "max.controls", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "exact") { for (i in c("distance", "exact", "mahvars", "antiexact", "caliper", "std.caliper", "discard", "reestimate", "replace", "ratio", "min.controls", "max.controls", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "cem") { for (i in c("distance", "exact", "mahvars", "antiexact", "caliper", "std.caliper", "discard", "reestimate", "replace", "ratio", "min.controls", "max.controls", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "nearest") { if (is.character(distance) && distance == "mahalanobis") { for (e in c("mahvars", "reestimate")) { if (e %in% names(mcall) && !is.null(e_ <- get0(e, inherits = FALSE)) && !identical(e_, formals(MatchIt::matchit)[[e]])) { error.inputs <- c(error.inputs, e) } } } } else if (method == "optimal") { if (is.character(distance) && distance == "mahalanobis") { for (e in c("mahvars", "reestimate")) { if (e %in% names(mcall) && !is.null(e_ <- get0(e, inherits = FALSE)) && !identical(e_, formals(MatchIt::matchit)[[e]])) { error.inputs <- c(error.inputs, e) } } } for (i in c("replace", "caliper", "std.caliper", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "full") { if (is.character(distance) && distance == "mahalanobis") { for (e in c("mahvars", "reestimate")) { if (e %in% names(mcall) && !is.null(e_ <- get0(e, inherits = FALSE)) && !identical(e_, formals(MatchIt::matchit)[[e]])) { error.inputs <- c(error.inputs, e) } } } for (i in c("replace", "ratio", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "genetic") { if (is.character(distance) && distance == "mahalanobis") { for (e in c("mahvars", "reestimate")) { if (e %in% names(mcall) && !is.null(e_ <- get0(e, inherits = FALSE)) && !identical(e_, formals(MatchIt::matchit)[[e]])) { error.inputs <- c(error.inputs, e) } } } for (i in c("min.controls", "max.controls")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "cardinality") { for (i in c("distance", "mahvars", "antiexact", "caliper", "std.caliper", "reestimate", "replace", "min.controls", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } else if (method == "subclass") { if (is.character(distance) && distance == "mahalanobis") { stop("distance = \"mahalanobis\" is not compatible with subclassification.", call. = FALSE) } for (i in c("exact", "mahvars", "antiexact", "caliper", "std.caliper", "replace", "ratio", "min.controls", "max.controls", "m.order")) { if (i %in% names(mcall) && !is.null(i_ <- get0(i, inherits = FALSE)) && !identical(i_, formals(MatchIt::matchit)[[i]])) { ignored.inputs <- c(ignored.inputs, i) } } } if (length(ignored.inputs) > 0) warning(paste0(ngettext(length(ignored.inputs), "The argument ", "The arguments "), word_list(ignored.inputs, quotes = 1, is.are = TRUE), " not used with method = ", add_quotes(method, quotes = !null.method), " and will be ignored."), call. = FALSE, immediate. = TRUE) if (length(error.inputs) > 0) stop(paste0(ngettext(length(error.inputs), "The argument ", "The arguments "), word_list(error.inputs, quotes = 1, is.are = TRUE), " not allowed with method = ", add_quotes(method, quotes = !null.method), " and distance = \"", distance, "\"."), call. = FALSE) return(ignored.inputs) } #Function to process distance and give warnings about new syntax process.distance <- function(distance, method = NULL, treat) { if (is.null(distance) && !is.null(method)) stop(paste0("'distance' cannot be NULL with method = \"", method, "\"."), call. = FALSE) else if (is.character(distance) && length(distance) == 1) { allowable.distances <- c("glm", "cbps", "gam", "mahalanobis", "nnet", "rpart", "bart", "randomforest", "elasticnet", "lasso", "ridge", "gbm") if (tolower(distance) %in% c("cauchit", "cloglog", "linear.cloglog", "linear.log", "linear.logit", "linear.probit", "linear.cauchit", "log", "probit")) { warning(paste0("'distance = \"", distance, "\"' will be deprecated; please use 'distance = \"glm\", link = \"", distance, "\"' in the future."), call. = FALSE, immediate. = TRUE) link <- distance distance <- "glm" attr(distance, "link") <- link } else if (tolower(distance) %in% tolower(c("GAMcloglog", "GAMlog", "GAMlogit", "GAMprobit"))) { link <- sub("GAM", "", distance) warning(paste0("'distance = \"", distance, "\"' will be deprecated; please use 'distance = \"gam\", link = \"", link, "\"' in the future."), call. = FALSE, immediate. = TRUE) distance <- "gam" attr(distance, "link") <- link } else if (tolower(distance) == "logit") { distance <- "glm" attr(distance, "link") <- "logit" } else if (tolower(distance) == "glmnet") { distance <- "elasticnet" } else if (!tolower(distance) %in% allowable.distances) { stop("The argument supplied to distance is not an allowable value. See ?distance for allowable options.", call. = FALSE) } else { distance <- tolower(distance) } } else if (!is.numeric(distance) || (!is.null(dim(distance)) && length(dim(distance)) != 2)) { stop("'distance' must be a string with the name of the distance measure to be used or a numeric vector or matrix containing distance measures.", call. = FALSE) } else if (is.matrix(distance) && (is.null(method) || !method %in% c("nearest", "optimal", "full"))) { if (is.null(method)) method <- "NULL" else method <- paste0('"', method, '"') stop(paste0("'distance' cannot be supplied as a matrix with method = ", method, "."), call. = FALSE) } if (is.numeric(distance)) { if (is.matrix(distance)) { dim.distance <- dim(distance) if (all(dim.distance == length(treat))) { if (!is.null(rownames(distance))) distance <- distance[names(treat),, drop = FALSE] if (!is.null(colnames(distance))) distance <- distance[,names(treat), drop = FALSE] distance <- distance[treat == 1, treat == 0, drop = FALSE] } else if (all(dim.distance == c(sum(treat==1), sum(treat==0)))) { if (!is.null(rownames(distance))) distance <- distance[names(treat)[treat == 1],, drop = FALSE] if (!is.null(colnames(distance))) distance <- distance[,names(treat)[treat == 0], drop = FALSE] } else { stop("When supplied as a matrix, 'distance' must have dimensions NxN or N1xN0. See ?distance for details.", call. = FALSE) } } else { if (length(distance) != length(treat)) stop("'distance' must be the same length as the dataset if specified as a numeric vector.", call. = FALSE) } if (anyNA(distance)) stop("Missing values are not allowed in 'distance'.", call. = FALSE) } return(distance) } #Function to check ratio is acceptable process.ratio <- function(ratio, method = NULL, ..., min.controls = NULL, max.controls = NULL) { #Should be run after process.inputs() and ignored inputs set to NULL ratio.null <- length(ratio) == 0 ratio.na <- !ratio.null && anyNA(ratio) if (is.null(method)) return(1) else if (method %in% c("nearest", "optimal")) { if (ratio.null) ratio <- 1 else if (ratio.na) stop("'ratio' cannot be NA.", call. = FALSE) else if (!is.atomic(ratio) || !is.numeric(ratio) || length(ratio) > 1 || ratio < 1) { stop("'ratio' must be a single number greater than or equal to 1.", call. = FALSE) } if (is.null(max.controls)) { ratio <- round(ratio) } else if (anyNA(max.controls) || !is.atomic(max.controls) || !is.numeric(max.controls) || length(max.controls) > 1) { stop("'max.controls' must be a single positive number.", call. = FALSE) } else { if (ratio <= 1) stop("'ratio' must be greater than 1 for variable ratio matching.", call. = FALSE) max.controls <- ceiling(max.controls) if (max.controls <= ratio) stop("'max.controls' must be greater than 'ratio' for variable ratio matching.", call. = FALSE) if (is.null(min.controls)) min.controls <- 1 else if (anyNA(max.controls) || !is.atomic(max.controls) || !is.numeric(max.controls) || length(max.controls) > 1) { stop("'max.controls' must be a single positive number.", call. = FALSE) } else min.controls <- floor(min.controls) if (min.controls < 1) stop("'min.controls' cannot be less than 1 for variable ratio matching.", call. = FALSE) else if (min.controls >= ratio) stop("'min.controls' must be less than 'ratio' for variable ratio matching.", call. = FALSE) } } else if (method == "full") { if (is.null(max.controls)) max.controls <- Inf else if ((anyNA(max.controls) || !is.atomic(max.controls) || !is.numeric(max.controls) || length(max.controls) > 1)) { stop("'max.controls' must be a single positive number.", call. = FALSE) } if (is.null(min.controls)) min.controls <- 0 else if ((anyNA(min.controls) || !is.atomic(min.controls) || !is.numeric(min.controls) || length(min.controls) > 1)) { stop("'min.controls' must be a single positive number.", call. = FALSE) } ratio <- 1 #Just to get min.controls and max.controls out } else if (method == "genetic") { if (ratio.null) ratio <- 1 else if (ratio.na) stop("'ratio' cannot be NA.", call. = FALSE) else if (!is.atomic(ratio) || !is.numeric(ratio) || length(ratio) > 1 || ratio < 1) { stop("'ratio' must be a single number greater than or equal to 1.", call. = FALSE) } ratio <- round(ratio) min.controls <- max.controls <- NULL } else if (method == "cardinality") { if (ratio.null) ratio <- 1 else if (!ratio.na && (!is.atomic(ratio) || !is.numeric(ratio) || length(ratio) > 1 || ratio < 0)) { stop("'ratio' must be a single positive number or NA.", call. = FALSE) } min.controls <- max.controls <- NULL } else { min.controls <- max.controls <- NULL } if (!is.null(ratio)) { attr(ratio, "min.controls") <- min.controls attr(ratio, "max.controls") <- max.controls } return(ratio) } #Function to check if caliper is okay and process it process.caliper <- function(caliper = NULL, method = NULL, data = NULL, covs = NULL, mahcovs = NULL, distance = NULL, discarded = NULL, std.caliper = TRUE) { #Check method; must be able to use a caliper #Check caliper names; if "" is one of them but distance = "mahal", throw error; #otherwise make sure variables exist in data or covs #Make sure no calipers are used on binary or factor variables (throw error if so) #Ignore calipers used on single-value variables or with caliper = NA or Inf #Export caliper.formula to add to covs #If std, export standardized versions #Check need for caliper if (length(caliper) == 0 || is.null(method) || !method %in% c("nearest", "genetic", "full")) return(NULL) #Check if form of caliper is okay if (!is.atomic(caliper) || !is.numeric(caliper)) stop("'caliper' must be a numeric vector.", call. = FALSE) #Check caliper names if (length(caliper) == 1 && (is.null(names(caliper)) || identical(names(caliper), ""))) names(caliper) <- "" else if (is.null(names(caliper))) stop("'caliper' must be a named vector with names corresponding to the variables for which a caliper is to be applied.", call. = FALSE) else if (anyNA(names(caliper))) stop("'caliper' names cannot include NA.", call. = FALSE) else if (sum(names(caliper) == "") > 1) stop("No more than one entry in 'caliper' can have no name.", call. = FALSE) if (any(names(caliper) == "") && is.null(distance)) stop("All entries in 'caliper' must be named when distance = \"mahalanobis\".", call. = FALSE) #Check if caliper name is in available data cal.in.data <- setNames(names(caliper) %in% names(data), names(caliper)) cal.in.covs <- setNames(names(caliper) %in% names(covs), names(caliper)) cal.in.mahcovs <- setNames(names(caliper) %in% names(mahcovs), names(caliper)) if (any(names(caliper) != "" & !cal.in.covs & !cal.in.data)) stop(paste0("All variables named in 'caliper' must be in 'data'. Variables not in 'data':\n\t", paste0(names(caliper)[names(caliper) != "" & !cal.in.data & !cal.in.covs & !cal.in.mahcovs], collapse = ", ")), call. = FALSE) #Check std.caliper if (length(std.caliper) == 0 || !is.atomic(std.caliper) || !is.logical(std.caliper)) stop("'std.caliper' must be a logical (TRUE/FALSE) vector.", call. = FALSE) if (length(std.caliper) == 1) std.caliper <- setNames(rep.int(std.caliper, length(caliper)), names(caliper)) else if (length(std.caliper) != length(caliper)) stop("'std.caliper' must be the same length as 'caliper'", call. = FALSE) else names(std.caliper) <- names(caliper) #Remove trivial calipers caliper <- caliper[is.finite(caliper)] num.unique <- vapply(names(caliper), function(x) { if (x == "") var <- distance else if (cal.in.data[x]) var <- data[[x]] else if (cal.in.covs[x]) var <- covs[[x]] else var <- mahcovs[[x]] length(unique(var)) }, integer(1L)) caliper <- caliper[num.unique > 1] if (length(caliper) == 0) return(NULL) #Ensure no calipers on categorical variables cat.vars <- vapply(names(caliper), function(x) { if (num.unique[names(num.unique) == x] == 2) return(TRUE) else { if (x == "") var <- distance else if (cal.in.data[x]) var <- data[[x]] else if (cal.in.covs[x]) var <- covs[[x]] else var <- mahcovs[[x]] return(is.factor(var) || is.character(var)) } }, logical(1L)) if (any(cat.vars)) { stop(paste0("Calipers cannot be used with binary, factor, or character variables. Offending variables:\n\t", paste0(ifelse(names(caliper) == "", "", names(caliper))[cat.vars], collapse = ", ")), call. = FALSE) } #Process calipers according to std.caliper std.caliper <- std.caliper[names(std.caliper) %in% names(caliper)] if (anyNA(std.caliper)) stop("'std.caliper' cannot be NA.", call. = FALSE) if (any(std.caliper)) { if ("" %in% names(std.caliper) && isTRUE(std.caliper[names(std.caliper) == ""]) && is.matrix(distance)) { stop("When 'distance' is supplied as a matrix and a caliper for it is specified, 'std.caliper' must be FALSE for the distance measure.", call. = FALSE) } caliper[std.caliper] <- caliper[std.caliper] * vapply(names(caliper)[std.caliper], function(x) { if (x == "") sd(distance[!discarded]) else if (cal.in.data[x]) sd(data[[x]][!discarded]) else if (cal.in.covs[x]) sd(covs[[x]][!discarded]) else sd(mahcovs[[x]][!discarded]) }, numeric(1L)) } #Add cal.formula if (any(names(caliper) != "" & !cal.in.covs[names(caliper)] & !cal.in.mahcovs[names(caliper)])) { attr(caliper, "cal.formula") <- reformulate(names(caliper)[names(caliper) != "" & !cal.in.covs[names(caliper)] & !cal.in.mahcovs[names(caliper)]]) } return(abs(caliper)) } #Function to process replace argument process.replace <- function(replace, method = NULL, ..., reuse.max = NULL) { if (is.null(method)) return(FALSE) if (is.null(replace)) replace <- FALSE else if (anyNA(replace) || length(replace) != 1 || !is.logical(replace)) { stop("'replace' must be TRUE or FALSE.", call. = FALSE) } if (method %in% c("nearest")) { if (is.null(reuse.max)) { if (replace) reuse.max <- .Machine$integer.max else reuse.max <- 1L } else if (length(reuse.max) == 1 && is.numeric(reuse.max) && (!is.finite(reuse.max) || reuse.max > .Machine$integer.max) && !anyNA(reuse.max)) { reuse.max <- .Machine$integer.max } else if (abs(reuse.max - round(reuse.max)) > 1e-8 || length(reuse.max) != 1 || anyNA(reuse.max) || reuse.max < 1) { stop("'reuse.max' must be a positive integer of length 1.", call. = FALSE) } replace <- reuse.max != 1L attr(replace, "reuse.max") <- as.integer(reuse.max) } replace } #Function to ensure no subclass is devoid of both treated and control units by "scooting" units #from other subclasses. From WeightIt. subclass_scoot <- function(sub, treat, x, min.n = 1) { #Reassigns subclasses so there are no empty subclasses #for each treatment group. Copied from WeightIt with #slight modifications. treat <- as.character(treat) unique.treat <- unique(treat, nmax = 2) names(x) <- seq_along(x) names(sub) <- seq_along(sub) original.order <- names(x) nsub <- length(unique(sub)) #Turn subs into a contiguous sequence sub <- setNames(setNames(seq_len(nsub), sort(unique(sub)))[as.character(sub)], original.order) if (any(table(treat) < nsub * min.n)) { stop(paste0("Not enough units to fit ", min.n, ngettext(min.n, " treated and control unit", " treated and control units"), " in each subclass."), call. = FALSE) } for (t in unique.treat) { if (length(x[treat == t]) == nsub) { sub[treat == t] <- seq_len(nsub) } } sub_tab <- table(treat, sub) if (any(sub_tab < min.n)) { soft_thresh <- function(x, minus = 1) { x <- x - minus x[x < 0] <- 0 x } for (t in unique.treat) { for (n in seq_len(min.n)) { while (any(sub_tab[t,] == 0)) { first_0 <- which(sub_tab[t,] == 0)[1] if (first_0 == nsub || (first_0 != 1 && sum(soft_thresh(sub_tab[t, seq(1, first_0 - 1)]) / abs(first_0 - seq(1, first_0 - 1))) >= sum(soft_thresh(sub_tab[t, seq(first_0 + 1, nsub)]) / abs(first_0 - seq(first_0 + 1, nsub))))) { #If there are more and closer nonzero subs to the left... first_non0_to_left <- max(seq(1, first_0 - 1)[sub_tab[t, seq(1, first_0 - 1)] > 0]) name_to_move <- names(sub)[which(x == max(x[treat == t & sub == first_non0_to_left]) & treat == t & sub == first_non0_to_left)[1]] sub[name_to_move] <- first_0 sub_tab[t, first_0] <- 1L sub_tab[t, first_non0_to_left] <- sub_tab[t, first_non0_to_left] - 1L } else { #If there are more and closer nonzero subs to the right... first_non0_to_right <- min(seq(first_0 + 1, nsub)[sub_tab[t, seq(first_0 + 1, nsub)] > 0]) name_to_move <- names(sub)[which(x == min(x[treat == t & sub == first_non0_to_right]) & treat == t & sub == first_non0_to_right)[1]] sub[name_to_move] <- first_0 sub_tab[t, first_0] <- 1L sub_tab[t, first_non0_to_right] <- sub_tab[t, first_non0_to_right] - 1L } } sub_tab[t,] <- sub_tab[t,] - 1 } } #Unsort sub <- sub[names(sub)] } return(sub) } #Function to check if package is installed. From WeightIt. check.package <- function(package.name, alternative = FALSE) { packages.not.installed <- package.name[!vapply(package.name, requireNamespace, logical(1L), quietly = TRUE)] if (length(packages.not.installed) > 0) { if (alternative) return(FALSE) else { plural <- length(packages.not.installed) > 1 stop(paste0("Package", if (plural) "s " else " ", word_list(packages.not.installed, quotes = 1, is.are = TRUE), " needed for this function to work. Please install ", if (plural) "them" else "it","."), call. = FALSE) } } else return(invisible(TRUE)) } #Create info component of matchit object create_info <- function(method, fn1, link, discard, replace, ratio, mahalanobis, subclass, antiexact, distance_is_matrix) { info <- list(method = method, distance = if (is.null(fn1)) NULL else sub("distance2", "", fn1, fixed = TRUE), link = if (is.null(link)) NULL else link, discard = discard, replace = if (!is.null(method) && method %in% c("nearest", "genetic")) replace else NULL, ratio = if (!is.null(method) && method %in% c("nearest", "optimal", "genetic")) ratio else NULL, max.controls = if (!is.null(method) && method %in% c("nearest", "optimal")) attr(ratio, "max.controls") else NULL, mahalanobis = mahalanobis, subclass = if (!is.null(method) && method == "subclass") length(unique(subclass[!is.na(subclass)])) else NULL, antiexact = antiexact, distance_is_matrix = distance_is_matrix) info } #Function to turn a method name into a phrase describing the method info.to.method <- function(info) { out.list <- setNames(vector("list", 3), c("kto1", "type", "replace")) out.list[["kto1"]] <- if (!is.null(info$ratio)) paste0(if (!is.null(info$max.controls)) "variable ratio ", round(info$ratio, 2), ":1") else NULL out.list[["type"]] <- if (is.null(info$method)) "none (no matching)" else switch(info$method, "exact" = "exact matching", "cem" = "coarsened exact matching", "nearest" = "nearest neighbor matching", "optimal" = "optimal pair matching", "full" = "optimal full matching", "genetic" = "genetic matching", "subclass" = paste0("subclassification (", info$subclass, " subclasses)"), "cardinality" = "cardinality matching", if (is.null(attr(info$method, "method"))) "an unspecified matching method" else attr(info$method, "method")) out.list[["replace"]] <- if (!is.null(info$replace) && info$method %in% c("nearest", "genetic")) { if (info$replace) "with replacement" else "without replacement" } else NULL firstup(do.call("paste", c(unname(out.list), list(sep = " ")))) } info.to.distance <- function(info) { distance <- info$distance link <- info$link if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE link <- sub("linear.", "", as.character(link)) } else linear <- FALSE if (distance == "glm") { if (link == "logit") dist <- "logistic regression" else if (link == "probit") dist <- "probit regression" else dist <- paste("GLM with a", link, "link") } else if (distance == "gam") { dist <- paste("GAM with a", link, "link") } else if (distance == "gbm") { dist <- "GBM" } else if (distance == "elasticnet") { dist <- paste("an elastic net with a", link, "link") } else if (distance == "lasso") { if (link == "logit") dist <- "lasso logistic regression" else dist <- paste("lasso regression with a", link, "link") } else if (distance == "ridge") { dist <- paste("ridge regression with a", link, "link") } else if (distance == "rpart") { dist <- "CART" } else if (distance == "nnet") { dist <- "a neural network" } else if (distance == "cbps") { dist <- "CBPS" } else if (distance == "bart") { dist <- "BART" } else if (distance == "randomforest") { dist <- "a random forest" } if (linear) dist <- paste(dist, "and linearized") return(dist) } #Function to turn a vector into a string with "," and "and" or "or" for clean messages. 'and.or' #controls whether words are separated by "and" or "or"; 'is.are' controls whether the list is #followed by "is" or "are" (to avoid manually figuring out if plural); quotes controls whether #quotes should be placed around words in string. From WeightIt. word_list <- function(word.list = NULL, and.or = c("and", "or"), is.are = FALSE, quotes = FALSE) { #When given a vector of strings, creates a string of the form "a and b" #or "a, b, and c" #If is.are, adds "is" or "are" appropriately L <- length(word.list) word.list <- add_quotes(word.list, quotes) if (L == 0) { out <- "" attr(out, "plural") <- FALSE } else { word.list <- word.list[!word.list %in% c(NA_character_, "")] L <- length(word.list) if (L == 0) { out <- "" attr(out, "plural") <- FALSE } else if (L == 1) { out <- word.list if (is.are) out <- paste(out, "is") attr(out, "plural") <- FALSE } else { and.or <- match_arg(and.or) if (L == 2) { out <- paste(word.list, collapse = paste0(" ", and.or," ")) } else { out <- paste(paste(word.list[seq_len(L-1)], collapse = ", "), word.list[L], sep = paste0(", ", and.or," ")) } if (is.are) out <- paste(out, "are") attr(out, "plural") <- TRUE } } return(out) } #Add quotation marks around a string. add_quotes <- function(x, quotes = 2L) { if (!isFALSE(quotes)) { if (isTRUE(quotes) || as.integer(quotes) == 2L) x <- paste0("\"", x, "\"") else if (as.integer(quotes) == 1L) x <- paste0("\'", x, "\'") else stop("'quotes' must be boolean, 1, or 2.") } x } #More informative and cleaner version of base::match.arg. From WeightIt. match_arg <- function(arg, choices, several.ok = FALSE) { #Replaces match.arg() but gives cleaner error message and processing #of arg. if (missing(arg)) stop("No argument was supplied to match_arg.", call. = FALSE) arg.name <- paste(deparse(substitute(arg), width.cutoff = 500L), collapse = " ") if (missing(choices)) { formal.args <- formals(sys.function(sysP <- sys.parent())) choices <- eval(formal.args[[as.character(substitute(arg))]], envir = sys.frame(sysP)) } if (is.null(arg)) return(choices[1L]) else if (!is.character(arg)) stop(paste0("The argument to '", arg.name, "' must be NULL or a character vector"), call. = FALSE) if (!several.ok) { if (identical(arg, choices)) return(arg[1L]) if (length(arg) > 1L) stop(paste0("The argument to '", arg.name, "' must be of length 1"), call. = FALSE) } else if (length(arg) == 0) stop(paste0("The argument to '", arg.name, "' must be of length >= 1"), call. = FALSE) i <- pmatch(arg, choices, nomatch = 0L, duplicates.ok = TRUE) if (all(i == 0L)) stop(paste0("The argument to '", arg.name, "' should be ", if (length(choices) > 1) {if (several.ok) "at least one of " else "one of "} else "", word_list(choices, and.or = "or", quotes = 2), "."), call. = FALSE) i <- i[i > 0L] if (!several.ok && length(i) > 1) stop("There is more than one match in 'match_arg'") choices[i] } #Turn a vector into a 0/1 vector. 'zero' and 'one' can be supplied to make it clear which is #which; otherwise, a guess is used. From WeightIt. binarize <- function(variable, zero = NULL, one = NULL) { if (length(unique(variable)) > 2) stop(paste0("Cannot binarize ", paste(deparse(substitute(variable)), collapse = " "), ": more than two levels.")) if (is.character(variable) || is.factor(variable)) { variable <- factor(variable, nmax = 2) unique.vals <- levels(variable) } else { unique.vals <- unique(variable, nmax = 2) } if (is.null(zero)) { if (is.null(one)) { if (can_str2num(unique.vals)) { variable.numeric <- str2num(variable) } else { variable.numeric <- as.numeric(variable) } if (0 %in% variable.numeric) zero <- 0 else zero <- min(variable.numeric, na.rm = TRUE) return(setNames(as.integer(variable.numeric != zero), names(variable))) } else { if (one %in% unique.vals) return(setNames(as.integer(variable == one), names(variable))) else stop("The argument to 'one' is not the name of a level of variable.", call. = FALSE) } } else { if (zero %in% unique.vals) return(setNames(as.integer(variable != zero), names(variable))) else stop("The argument to 'zero' is not the name of a level of variable.", call. = FALSE) } } #Make interaction vector out of matrix of covs exactify <- function(X, nam = NULL, sep = "|", include_vars = FALSE) { if (is.null(nam)) nam <- rownames(X) if (is.matrix(X)) X <- setNames(lapply(seq_len(ncol(X)), function(i) X[,i]), colnames(X)) if (!is.list(X)) stop("X must be a matrix, data frame, or list.") #Ensure no ambiguity is created by sep sep0 <- sep unique.x <- unlist(lapply(X, function(x) as.character(unique(x)))) while (any(grepl(sep, unique.x, fixed = TRUE))) { sep0 <- paste0(sep0, sep) } if (include_vars) { for (i in seq_along(X)) { if (is.character(X[[i]]) || is.factor(X[[i]])) { X[[i]] <- paste0(names(X)[i], ' = "', X[[i]], '"') } else { X[[i]] <- paste0(names(X)[i], ' = ', X[[i]]) } } } out <- do.call("paste", c(X, sep = sep0)) if (!is.null(nam)) names(out) <- nam out } #Determine whether a character vector can be coerced to numeric can_str2num <- function(x) { nas <- is.na(x) suppressWarnings(x_num <- as.numeric(as.character(x[!nas]))) return(!anyNA(x_num)) } #Cleanly coerces a character vector to numeric; best to use after can_str2num() str2num <- function(x) { nas <- is.na(x) suppressWarnings(x_num <- as.numeric(as.character(x))) x_num[nas] <- NA return(x_num) } #Capitalize first letter firstup <- function(x) { substr(x, 1, 1) <- toupper(substr(x, 1, 1)) x } #Clean printing of data frames with numeric and NA elements. round_df_char <- function(df, digits, pad = "0", na_vals = "") { #Digits is passed to round(). pad is used to replace trailing zeros so decimal #lines up. Should be "0" or " "; "" (the empty string) un-aligns decimals. #na_vals is what NA should print as. if (NROW(df) == 0 || NCOL(df) == 0) return(df) if (!is.data.frame(df)) df <- as.data.frame.matrix(df, stringsAsFactors = FALSE) rn <- rownames(df) cn <- colnames(df) infs <- o.negs <- array(FALSE, dim = dim(df)) nas <- is.na(df) nums <- vapply(df, is.numeric, logical(1)) infs[,nums] <- vapply(which(nums), function(i) !nas[,i] & !is.finite(df[[i]]), logical(NROW(df))) for (i in which(!nums)) { if (can_str2num(df[[i]])) { df[[i]] <- str2num(df[[i]]) nums[i] <- TRUE } } o.negs[,nums] <- !nas[,nums] & df[nums] < 0 & round(df[nums], digits) == 0 df[nums] <- round(df[nums], digits = digits) for (i in which(nums)) { df[[i]] <- format(df[[i]], scientific = FALSE, justify = "none", trim = TRUE, drop0trailing = !identical(as.character(pad), "0")) if (!identical(as.character(pad), "0") && any(grepl(".", df[[i]], fixed = TRUE))) { s <- strsplit(df[[i]], ".", fixed = TRUE) lengths <- lengths(s) digits.r.of.. <- rep(0, NROW(df)) digits.r.of..[lengths > 1] <- nchar(vapply(s[lengths > 1], `[[`, character(1L), 2)) max.dig <- max(digits.r.of..) dots <- ifelse(lengths > 1, "", if (as.character(pad) != "") "." else pad) pads <- vapply(max.dig - digits.r.of.., function(n) paste(rep(pad, n), collapse = ""), character(1L)) df[[i]] <- paste0(df[[i]], dots, pads) } } df[o.negs] <- paste0("-", df[o.negs]) # Insert NA placeholders df[nas] <- na_vals df[infs] <- "N/A" if (length(rn) > 0) rownames(df) <- rn if (length(cn) > 0) names(df) <- cn return(df) } #Generalized inverse; port of MASS::ginv() generalized_inverse <- function(sigma) { sigmasvd <- svd(sigma) pos <- sigmasvd$d > max(1e-8 * sigmasvd$d[1L], 0) sigma_inv <- sigmasvd$v[, pos, drop = FALSE] %*% (sigmasvd$d[pos]^-1 * t(sigmasvd$u[, pos, drop = FALSE])) return(sigma_inv) } #Choleski decomp for non-negative definite matrices chol2 <- function(Sinv) { ch <- suppressWarnings(chol(Sinv, pivot = TRUE)) p <- order(attr(ch, "pivot")) return(ch[,p]) } #Get covariates (RHS) vars from formula get.covs.matrix <- function(formula = NULL, data = NULL) { if (is.null(formula)) { fnames <- colnames(data) fnames[!startsWith(fnames, "`")] <- paste0("`", fnames[!startsWith(fnames, "`")], "`") formula <- reformulate(fnames) } else formula <- update(terms(formula, data = data), NULL ~ . + 1) mf <- model.frame(terms(formula, data = data), data, na.action = na.pass) chars.in.mf <- vapply(mf, is.character, logical(1L)) mf[chars.in.mf] <- lapply(mf[chars.in.mf], factor) X <- model.matrix(formula, data = mf, contrasts.arg = lapply(Filter(is.factor, mf), contrasts, contrasts = FALSE)) assign <- attr(X, "assign")[-1] X <- X[,-1,drop=FALSE] attr(X, "assign") <- assign return(X) } #Extracts and names the "assign" attribute from get.covs.matrix() get_assign <- function(mat) { if (is.null(attr(mat, "assign"))) return(NULL) setNames(attr(mat, "assign"), colnames(mat)) } #Convert match.matrix (mm) using numerical indices to using char rownames nummm2charmm <- function(nummm, treat) { #Assumes nummm has rownames charmm <- matrix(NA_character_, nrow = nrow(nummm), ncol = ncol(nummm), dimnames = dimnames(nummm)) charmm[] <- names(treat)[nummm] charmm } charmm2nummm <- function(charmm, treat) { nummm <- matrix(NA_integer_, nrow = nrow(charmm), ncol = ncol(charmm)) n_index <- setNames(seq_along(treat), names(treat)) nummm[] <- n_index[charmm] nummm } #Get subclass from match.matrix. Only to be used if replace = FALSE. See subclass2mmC.cpp for reverse. mm2subclass <- function(mm, treat) { lab <- names(treat) ind1 <- which(treat == 1) subclass <- setNames(rep(NA_character_, length(treat)), lab) no.match <- is.na(mm) subclass[ind1[!no.match[,1]]] <- ind1[!no.match[,1]] subclass[mm[!no.match]] <- ind1[row(mm)[!no.match]] subclass <- setNames(factor(subclass, nmax = length(ind1)), lab) levels(subclass) <- seq_len(nlevels(subclass)) return(subclass) } #(Weighted) variance that uses special formula for binary variables wvar <- function(x, bin.var = NULL, w = NULL) { if (is.null(w)) w <- rep(1, length(x)) if (is.null(bin.var)) bin.var <- all(x == 0 | x == 1) w <- w / sum(w) #weights normalized to sum to 1 mx <- sum(w * x) #weighted mean if (bin.var) { mx*(1-mx) } else { #Reliability weights variance; same as cov.wt() sum(w * (x - mx)^2)/(1 - sum(w^2)) } } #Weighted mean faster than weighted.mean() wm <- function(x, w = NULL, na.rm = TRUE) { if (is.null(w)) { if (anyNA(x)) { if (!na.rm) return(NA_real_) nas <- which(is.na(x)) x <- x[-nas] } return(sum(x)/length(x)) } else { if (anyNA(x) || anyNA(w)) { if (!na.rm) return(NA_real_) nas <- which(is.na(x) | is.na(w)) x <- x[-nas] w <- w[-nas] } return(sum(x*w)/sum(w)) } } #Effective sample size ESS <- function(w) { sum(abs(w))^2/sum(w^2) } #Compute sample sizes nn <- function(treat, weights, discarded, s.weights) { if (is.null(s.weights)) s.weights <- rep(1, length(treat)) weights <- weights * s.weights n <- matrix(0, ncol=2, nrow=6, dimnames = list(c("All (ESS)", "All", "Matched (ESS)","Matched", "Unmatched","Discarded"), c("Control", "Treated"))) # Control Treated n["All (ESS)",] <- c(ESS(s.weights[treat==0]), ESS(s.weights[treat==1])) n["All",] <- c(sum(treat==0), sum(treat==1)) n["Matched (ESS)",] <- c(ESS(weights[treat==0]), ESS(weights[treat==1])) n["Matched",] <- c(sum(treat==0 & weights > 0), sum(treat==1 & weights > 0)) n["Unmatched",] <- c(sum(treat==0 & weights==0 & !discarded), sum(treat==1 & weights==0 & !discarded)) n["Discarded",] <- c(sum(treat==0 & discarded), sum(treat==1 & discarded)) return(n) } #Compute subclass sample sizes qn <- function(treat, subclass, discarded) { qn <- table(treat[!discarded], subclass[!discarded]) dimnames(qn) <- list(c("Control", "Treated"), levels(subclass)) if (any(discarded)) { qn <- cbind(qn, table(treat[discarded])) colnames(qn)[ncol(qn)] <- "Discarded" } qn <- rbind(qn, colSums(qn)) rownames(qn)[nrow(qn)] <- "Total" qn <- cbind(qn, rowSums(qn)) colnames(qn)[ncol(qn)] <- "All" return(qn) } #Used to load backports functions. No need to touch, but must always be included somewhere. .onLoad <- function(libname, pkgname) { backports::import(pkgname) }MatchIt/R/match.qoi.R0000644000176200001440000001652214170744614014030 0ustar liggesusers## Functions to calculate summary stats qoi <- function(xx, tt, ww = NULL, s.weights, subclass = NULL, mm = NULL, s.d.denom = "treated", standardize = FALSE, compute.pair.dist = TRUE) { un <- is.null(ww) bin.var <- all(xx == 0 | xx == 1) xsum <- rep(NA_real_, 7) if (standardize) names(xsum) <- c("Means Treated","Means Control", "Std. Mean Diff.", "Var. Ratio", "eCDF Mean", "eCDF Max", "Std. Pair Dist.") else names(xsum) <- c("Means Treated","Means Control", "Mean Diff.", "Var. Ratio", "eQQ Mean", "eQQ Max", "Pair Dist.") if (un) ww <- s.weights else ww <- ww * s.weights too.small <- sum(ww[tt==1] != 0) < 2 || sum(ww[tt==0] != 0) < 2 xsum["Means Treated"] <- wm(xx[tt==1], ww[tt==1], na.rm=TRUE) xsum["Means Control"] <- wm(xx[tt==0], ww[tt==0], na.rm=TRUE) mdiff <- xsum["Means Treated"] - xsum["Means Control"] if (standardize && abs(mdiff) > 1e-8) { if (!too.small) { if (is.numeric(s.d.denom)) { std <- s.d.denom } else { s.d.denom <- match_arg(s.d.denom, c("treated", "control", "pooled")) std <- switch(s.d.denom, "treated" = sqrt(wvar(xx[tt==1], bin.var, s.weights[tt==1])), "control" = sqrt(wvar(xx[tt==0], bin.var, s.weights[tt==0])), "pooled" = sqrt(.5*(wvar(xx[tt==1], bin.var, s.weights[tt==1]) + wvar(xx[tt==0], bin.var, s.weights[tt==0])))) if (std < sqrt(.Machine$double.eps)) std <- sqrt(wvar(xx, bin.var, s.weights)) #Avoid divide by zero } xsum[3] <- mdiff/std if (!un && compute.pair.dist) xsum[7] <- pair.dist(xx, tt, subclass, mm, std) } } else { xsum[3] <- mdiff if (!un && compute.pair.dist) xsum[7] <- pair.dist(xx, tt, subclass, mm) } if (bin.var) { xsum[5:6] <- abs(mdiff) } else if (!too.small) { xsum["Var. Ratio"] <- wvar(xx[tt==1], bin.var, ww[tt==1]) / wvar(xx[tt==0], bin.var, ww[tt==0]) qqmat <- qqsum(xx, tt, ww, standardize = standardize) xsum[5:6] <- qqmat[c("meandiff", "maxdiff")] } xsum } qoi.subclass <- function(xx, tt, s.weights, subclass, s.d.denom = "treated", standardize = FALSE, which.subclass = NULL) { #Within-subclass balance statistics bin.var <- all(xx == 0 | xx == 1) in.sub <- !is.na(subclass) & subclass == which.subclass xsum <- matrix(NA_real_, nrow = 1, ncol = 6) rownames(xsum) <- "Subclass" if (standardize) colnames(xsum) <- c("Means Treated","Means Control", "Std. Mean Diff.", "Var. Ratio", "eCDF Mean", "eCDF Max") else colnames(xsum) <- c("Means Treated","Means Control", "Mean Diff", "Var. Ratio", "eQQ Mean", "eQQ Max") too.small <- sum(in.sub & tt==1) < 2 || sum(in.sub & tt==0) < 2 xsum["Subclass","Means Treated"] <- wm(xx[in.sub & tt==1], s.weights[in.sub & tt==1], na.rm=TRUE) xsum["Subclass","Means Control"] <- wm(xx[in.sub & tt==0], s.weights[in.sub & tt==0], na.rm=TRUE) mdiff <- xsum["Subclass","Means Treated"] - xsum["Subclass","Means Control"] if (standardize && abs(mdiff) > 1e-8) { if (!too.small) { if (is.numeric(s.d.denom)) { std <- s.d.denom } else { #SD from full sample, not within subclass s.d.denom <- match_arg(s.d.denom, c("treated", "control", "pooled")) std <- switch(s.d.denom, "treated" = sqrt(wvar(xx[tt==1], bin.var, s.weights[tt==1])), "control" = sqrt(wvar(xx[tt==0], bin.var, s.weights[tt==0])), "pooled" = sqrt(.5*(wvar(xx[tt==1], bin.var, s.weights[tt==1]) + wvar(xx[tt==0], bin.var, s.weights[tt==0])))) } xsum["Subclass", 3] <- mdiff/std } } else { xsum["Subclass", 3] <- mdiff } if (bin.var) { xsum["Subclass", 5:6] <- abs(mdiff) } else if (!too.small) { xsum["Subclass", "Var. Ratio"] <- wvar(xx[in.sub & tt==1], bin.var, s.weights[in.sub & tt==1]) / wvar(xx[in.sub & tt==0], bin.var, s.weights[in.sub & tt==0]) qqall <- qqsum(xx[in.sub], tt[in.sub], standardize = standardize) xsum["Subclass", 5:6] <- qqall[c("meandiff", "maxdiff")] } xsum } #Compute within-pair/subclass distances pair.dist <- function(xx, tt, subclass = NULL, mm = NULL, std = NULL, fast = TRUE) { if (!is.null(mm)) { names(xx) <- names(tt) xx_t <- xx[rownames(mm)] xx_c <- matrix(0, nrow = nrow(mm), ncol = ncol(mm)) xx_c[] <- xx[mm] mpdiff <- mean(abs(xx_t - xx_c), na.rm = TRUE) } else if (!is.null(subclass)) { if (!fast) { dists <- unlist(lapply(levels(subclass), function(s) { t1 <- which(!is.na(subclass) & subclass == s & tt == 1) t0 <- which(!is.na(subclass) & subclass == s & tt == 0) if (length(t1) == 1 || length(t0) == 1) { xx[t1] - xx[t0] } else { outer(xx[t1], xx[t0], "-") } })) mpdiff <- mean(abs(dists)) } else { mpdiff <- pairdistsubC(as.numeric(xx), as.integer(tt), as.integer(subclass), nlevels(subclass)) } } else return(NA_real_) if (!is.null(std) && abs(mpdiff) > 1e-8) { mpdiff <- mpdiff/std } mpdiff } ## Function for QQ summary stats qqsum <- function(x, t, w = NULL, standardize = FALSE) { #x = variable, t = treat, w = weights n.obs <- length(x) if (is.null(w)) w <- rep(1, n.obs) if (all(x == 0 | x == 1)) { #For binary variables, just difference in means ediff <- abs(wm(x[t == t[1]], w[t == t[1]]) - wm(x[t != t[1]], w[t != t[1]])) return(c(meandiff = ediff, meddiff = ediff, maxdiff = ediff)) } else { for (i in unique(t, nmax = 2)) w[t==i] <- w[t==i]/sum(w[t==i]) ord <- order(x) x_ord <- x[ord] w_ord <- w[ord] t_ord <- t[ord] if (standardize) { #Difference between ecdf of x for each group w_ord_ <- w_ord w_ord_[t_ord==t_ord[1]] <- -w_ord_[t_ord==t_ord[1]] ediff <- abs(cumsum(w_ord_))[c(diff(x_ord) != 0, TRUE)] } else { #Horizontal distance of ecdf between groups #Need to interpolate larger group to be same size as smaller group u <- unique(x_ord) wn1 <- sum(w[t == t_ord[1]] > 0) wn0 <- sum(w[t != t_ord[1]] > 0) w1 <- w_ord[t_ord == t_ord[1]] w0 <- w_ord[t_ord != t_ord[1]] x1 <- x_ord[t_ord == t_ord[1]][w1 > 0] x0 <- x_ord[t_ord != t_ord[1]][w0 > 0] if (wn1 < wn0) { if (length(u) <= 5) { x0probs <- vapply(u, function(u_) wm(x0 == u_, w0[w0 > 0]), numeric(1L)) x0cumprobs <- c(0, cumsum(x0probs)[-length(u)], 1) x0 <- u[findInterval(cumsum(w1[w1 > 0]), x0cumprobs, rightmost.closed = TRUE)] } else { x0 <- approx(cumsum(w0[w0 > 0]), y = x0, xout = cumsum(w1[w1 > 0]), rule = 2, method = "constant", ties = "ordered")$y } } else { if (length(u) <= 5) { x1probs <- vapply(u, function(u_) wm(x1 == u_, w1[w1 > 0]), numeric(1L)) x1cumprobs <- c(0, cumsum(x1probs)[-length(u)], 1) x1 <- u[findInterval(cumsum(w0[w0 > 0]), x1cumprobs, rightmost.closed = TRUE)] } else { x1 <- approx(cumsum(w1[w1 > 0]), y = x1, xout = cumsum(w0[w0 > 0]), rule = 2, method = "constant", ties = "ordered")$y } } ediff <- abs(x1 - x0) } return(c(meandiff = mean(ediff), maxdiff = max(ediff))) } }MatchIt/R/class_functions.R0000644000176200001440000007403014146245341015334 0ustar liggesusers### PLOT METHODS------------------------------------------- plot.matchit <- function(x, type = "qq", interactive = TRUE, which.xs = NULL, ...) { type <- tolower(type) type <- match_arg(type, c("qq", "ecdf", "density", "jitter", "histogram")) if (type %in% c("qq", "ecdf", "density")) { matchit.covplot(x, type = type, interactive=interactive, which.xs = which.xs, ...) } else if (type == "jitter") { if (is.null(x$distance)) { stop("type = \"jitter\" cannot be used if a distance measure is not estimated or supplied. No plots generated.", call. = FALSE) } jitter.pscore(x, interactive = interactive,...) } else if (type =="histogram") { if (is.null(x$distance)) { stop("type = \"hist\" cannot be used if a distance measure is not estimated or supplied. No plots generated.", call. = FALSE) } hist.pscore(x,...) } } plot.matchit.subclass <- function(x, type = "qq", interactive = TRUE, which.xs = NULL, subclass, ...) { choice.menu <- function(choices, question) { k <- length(choices)-1 Choices <- data.frame(choices) row.names(Choices) <- 0:k names(Choices) <- "Choices" print.data.frame(Choices, right=FALSE) ans <- readline(question) while (!ans %in% 0:k) { message("Not valid -- please pick one of the choices") print.data.frame(Choices, right=FALSE) ans <- readline(question) } return(ans) } type <- tolower(type) type <- match_arg(type, c("qq", "ecdf", "density", "jitter", "histogram")) if (type %in% c("qq", "ecdf", "density")) { #If subclass = T, index, or range, display all or range of subclasses, using interactive to advance #If subclass = F, display aggregate across subclass, using interactive to advance #If subclass = NULL, if interactive, use to choose subclass, else display aggregate across subclass subclasses <- levels(x$subclass) miss.sub <- missing(subclass) || is.null(subclass) if (miss.sub || isFALSE(subclass)) which.subclass <- NULL else if (isTRUE(subclass)) which.subclass <- subclasses else if (!is.atomic(subclass) || !all(subclass %in% seq_along(subclasses))) { stop("'subclass' should be TRUE, FALSE, or a vector of subclass indices for which subclass balance is to be displayed.", call. = FALSE) } else which.subclass <- subclasses[subclass] if (!is.null(which.subclass)) { matchit.covplot.subclass(x, type = type, which.subclass = which.subclass, interactive = interactive, which.xs = which.xs, ...) } else if (interactive && miss.sub) { subclasses <- levels(x$subclass) choices <- c("No (Exit)", paste0("Yes: Subclass ", subclasses), "Yes: In aggregate") plot.name <- switch(type, "qq" = "quantile-quantile", "ecdf" = "empirical CDF", "density" = "density") question <- paste("Would you like to see", plot.name, "plots of any subclasses? ") ans <- -1 while(ans != 0) { ans <- as.numeric(choice.menu(choices, question)) if (ans %in% seq_along(subclasses) && any(x$subclass == subclasses[ans])) { matchit.covplot.subclass(x, type = type, which.subclass = subclasses[ans], interactive = interactive, which.xs = which.xs, ...) } else if (ans != 0) { matchit.covplot(x, type = type, interactive = interactive, which.xs = which.xs, ...) } } } else { matchit.covplot(x, type = type, interactive = interactive, which.xs = which.xs, ...) } } else if (type=="jitter") { if (is.null(x$distance)) { stop("type = \"jitter\" cannot be used when no distance variable was estimated or supplied.", call. = FALSE) } jitter.pscore(x, interactive = interactive, ...) } else if (type == "histogram") { if (is.null(x$distance)) { stop("type = \"histogram\" cannot be used when no distance variable was estimated or supplied.", call. = FALSE) } hist.pscore(x,...) } invisible(x) } plot.summary.matchit <- function(x, abs = TRUE, var.order = "data", threshold = c(.1, .05), position = "bottomright", ...) { .pardefault <- par(no.readonly = TRUE) on.exit(par(.pardefault)) sub <- inherits(x, "summary.matchit.subclass") matched <- sub || !is.null(x[["sum.matched"]]) un <- !is.null(x[["sum.all"]]) standard.sum <- if (un) x[["sum.all"]] else x[[if (sub) "sum.across" else "sum.matched"]] if (!"Std. Mean Diff." %in% colnames(standard.sum)) { stop("Not appropriate for unstandardized summary. Run summary() with the standardize = TRUE option, and then plot.", call. = FALSE) } if (un) { sd.all <- x[["sum.all"]][,"Std. Mean Diff."] } if (matched) { sd.matched <- x[[if (sub) "sum.across" else "sum.matched"]][,"Std. Mean Diff."] } var.names <- rownames(standard.sum) var.order <- match_arg(var.order, c("data", "matched", "unmatched", "alphabetical")) if (!un && var.order == "unmatched") stop("'var.order' cannot be \"unmatched\" if un = TRUE in the call to summary().", call. = FALSE) if (!matched && var.order == "matched") stop("'var.order' cannot be \"matched\" if method = NULL in the original call to matchit().", call. = FALSE) if (abs) { if (un) sd.all <- abs(sd.all) if (matched) sd.matched <- abs(sd.matched) xlab <- "Absolute Standardized\nMean Difference" } else { xlab <- "Standardized Mean Difference" } ord <- switch(var.order, "data" = rev(seq_along(var.names)), "matched" = order(sd.matched), "unmatched" = order(sd.all), "alphabetical" = order(var.names, decreasing = TRUE)) dotchart(if (un) sd.all[ord] else sd.matched[ord], labels = var.names[ord], xlab = xlab, bg = NA, color = NA, ...) abline(v = 0) if (sub && length(x$sum.subclass) > 0) { for (i in seq_along(x$sum.subclass)) { sd.sub <- x$sum.subclass[[i]][,"Std. Mean Diff."] if (abs) sd.sub <- abs(sd.sub) points(x = sd.sub[ord], y = seq_along(sd.sub), pch = as.character(i), col = "gray60", cex = .6) } } if (un) { points(x = sd.all[ord], y = seq_along(sd.all), pch = 21, bg = "white", col = "black") } if (matched) { points(x = sd.matched[ord], y = seq_along(sd.matched), pch = 21, bg = "black", col = "black") } if (!is.null(threshold)) { if (abs) { abline(v = threshold, lty = seq_along(threshold)) } else { abline(v = threshold, lty = seq_along(threshold)) abline(v = -threshold, lty = seq_along(threshold)) } } if (sum(matched, un) > 1 && !is.null(position)) { position <- match_arg(position, c("bottomright", "bottom", "bottomleft", "left", "topleft", "top", "topright", "right", "center")) legend(position, legend = c("All", "Matched"), pt.bg = c("white", "black"), pch = 21, inset = .015, xpd = TRUE) } invisible(x) } ### PRINT METHODS------------------------------------------ print.matchit <- function(x, ...) { info <- x[["info"]] cal <- !is.null(x[["caliper"]]) dis <- c("both", "control", "treat")[pmatch(info$discard, c("both", "control", "treat"), 0L)] disl <- length(dis) > 0 nm <- is.null(x[["method"]]) cat("A matchit object") cat(paste0("\n - method: ", info.to.method(info))) # if (!is.null(x[["distance"]]) || info$mahalanobis || identical(info$distance, "user")) { if (!is.null(info$distance) || info$mahalanobis) { cat("\n - distance: ") if (info$mahalanobis) { cat("Mahalanobis") } if (!is.null(info$distance) && info$distance != "mahalanobis") { if (info$mahalanobis) cat(" [matching]\n ") if (info$distance_is_matrix) cat("User-defined (matrix)") else if (info$distance != "user") cat("Propensity score") else if (!is.null(attr(info$distance, "custom"))) cat(attr(info$distance, "custom")) else cat("User-defined") if (cal || disl) { cal.ps <- "" %in% names(x[["caliper"]]) cat(" [") cat(paste(c("matching", "subclassification", "caliper", "common support")[c(!nm && !info$mahalanobis && info$method != "subclass", !nm && info$method == "subclass", cal.ps, disl)], collapse = ", ")) cat("]") } if (info$distance != "user") { cat("\n - estimated with ") cat(info.to.distance(info)) if (!is.null(x[["s.weights"]])) { if (isTRUE(attr(x[["s.weights"]], "in_ps"))) cat("\n - sampling weights included in estimation") else cat("\n - sampling weights not included in estimation") } } } } if (cal) { cat(paste0("\n - caliper: ", paste(vapply(seq_along(x[["caliper"]]), function(z) paste0(if (names(x[["caliper"]])[z] == "") "" else names(x[["caliper"]])[z], " (", format(round(x[["caliper"]][z], 3)), ")"), character(1L)), collapse = ", "))) } if (disl) { cat("\n - common support: ") if (dis == "both") cat("units from both groups") else if (dis == "treat") cat("treated units") else if (dis == "control") cat("control units") cat(" dropped") } cat(paste0("\n - number of obs.: ", length(x[["treat"]]), " (original)", if (!all(x[["weights"]] == 1)) paste0(", ", sum(x[["weights"]] != 0), " (matched)"))) if (!is.null(x[["s.weights"]])) cat("\n - sampling weights: present") if (!is.null(x[["estimand"]])) cat(paste0("\n - target estimand: ", x[["estimand"]])) if (!is.null(x[["X"]])) cat(paste0("\n - covariates: ", ifelse(length(names(x[["X"]])) > 40, "too many to name", paste(names(x[["X"]]), collapse = ", ")))) cat("\n") invisible(x) } print.summary.matchit <- function(x, digits = max(3, getOption("digits") - 3), ...){ if (!is.null(x$call)) cat("\nCall:", deparse(x$call), sep = "\n") if (!is.null(x$sum.all)) { cat("\nSummary of Balance for All Data:\n") print.data.frame(round_df_char(x$sum.all[,-7, drop = FALSE], digits, pad = "0", na_vals = ".")) cat("\n") } if (!is.null(x$sum.matched)) { cat("\nSummary of Balance for Matched Data:\n") if (all(is.na(x$sum.matched[,7]))) x$sum.matched <- x$sum.matched[,-7,drop = FALSE] #Remove pair dist if empty print.data.frame(round_df_char(x$sum.matched, digits, pad = "0", na_vals = ".")) } if (!is.null(x$reduction)) { cat("\nPercent Balance Improvement:\n") print.data.frame(round_df_char(x$reduction[,-5, drop = FALSE], 1, pad = "0", na_vals = ".")) } if (!is.null(x$nn)) { cat("\nSample Sizes:\n") nn <- x$nn if (isTRUE(all.equal(nn["All (ESS)",], nn["All",]))) { #Don't print ESS if same as full SS nn <- nn[rownames(nn) != "All (ESS)",,drop = FALSE] } if (isTRUE(all.equal(nn["Matched (ESS)",], nn["Matched",]))) { #Don't print ESS if same as matched SS nn <- nn[rownames(nn) != "Matched (ESS)",,drop = FALSE] } print.data.frame(round_df_char(nn, 2, pad = " ", na_vals = ".")) } cat("\n") invisible(x) } print.summary.matchit.subclass <- function(x, digits = max(3, getOption("digits") - 3), ...){ if (!is.null(x$call)) cat("\nCall:", deparse(x$call), sep = "\n") if (!is.null(x$sum.all)) { cat("\nSummary of Balance for All Data:\n") print.data.frame(round_df_char(x$sum.all[,-7, drop = FALSE], digits, pad = "0", na_vals = ".")) } if (length(x$sum.subclass) > 0) { cat("\nSummary of Balance by Subclass:\n") for (s in seq_along(x$sum.subclass)) { cat(paste0("\n- ", names(x$sum.subclass)[s], "\n")) print.data.frame(round_df_char(x$sum.subclass[[s]][,-7, drop = FALSE], digits, pad = "0", na_vals = ".")) } if (!is.null(x$qn)) { cat("\nSample Sizes by Subclass:\n") print.data.frame(round_df_char(x$qn, 2, pad = " ", na_vals = ".")) } } else { if (!is.null(x$sum.across)) { cat("\nSummary of Balance Across Subclasses\n") if (all(is.na(x$sum.across[,7]))) x$sum.across <- x$sum.across[,-7,drop = FALSE] print.data.frame(round_df_char(x$sum.across, digits, pad = "0", na_vals = ".")) } if (!is.null(x$reduction)) { cat("\nPercent Balance Improvement:\n") print.data.frame(round_df_char(x$reduction[,-5, drop = FALSE], 1, pad = "0", na_vals = ".")) } if (!is.null(x$nn)) { cat("\nSample Sizes:\n") nn <- x$nn if (isTRUE(all.equal(nn["All (ESS)",], nn["All",]))) { #Don't print ESS if same as full SS nn <- nn[rownames(nn) != "All (ESS)",,drop = FALSE] } if (isTRUE(all.equal(nn["Matched (ESS)",], nn["Matched",]))) { #Don't print ESS if same as matched SS nn <- nn[rownames(nn) != "Matched (ESS)",,drop = FALSE] } print.data.frame(round_df_char(nn, 2, pad = " ", na_vals = ".")) } } cat("\n") } ### SUMMARY METHODS---------------------------------------- summary.matchit <- function(object, interactions = FALSE, addlvariables = NULL, standardize = TRUE, data = NULL, pair.dist = TRUE, un = TRUE, improvement = TRUE, ...) { #Create covariate matrix; include caliper, exact, and mahvars if (is.null(object$X)) { X <- matrix(nrow = length(object$treat), ncol = 0) } else { X <- get.covs.matrix(data = object$X) } X_assign <- get_assign(X) if (!is.null(addlvariables)) { if (is.character(addlvariables)) { if (!is.null(data) && is.data.frame(data)) { if (all(addlvariables %in% names(data))) { addlvariables <- data[addlvariables] } else { stop("All variables in 'addlvariables' must be in 'data'.", call. = FALSE) } } else { stop("If 'addlvariables' is specified as a string, a data frame argument must be supplied to 'data'.", call. = FALSE) } } else if (inherits(addlvariables, "formula")) { vars.in.formula <- all.vars(addlvariables) if (!is.null(data) && is.data.frame(data)) data <- data.frame(data[names(data) %in% vars.in.formula], object$X[names(data) %in% setdiff(vars.in.formula, names(data))]) else data <- object$X addlvariables <- get.covs.matrix(addlvariables, data = data) } else if (!is.matrix(addlvariables) && !is.data.frame(addlvariables)) { stop("The argument to 'addlvariables' must be in one of the accepted forms. See ?summary.matchit for details.", call. = FALSE) } if (is.data.frame(addlvariables)) { addlvariables <- get.covs.matrix(data = addlvariables) } addl_assign <- get_assign(addlvariables) X <- cbind(X, addlvariables[, setdiff(colnames(addlvariables), colnames(X)), drop = FALSE]) } treat <- object$treat weights <- object$weights s.weights <- if (is.null(object$s.weights)) rep(1, length(weights)) else object$s.weights kk <- ncol(X) if (kk > 0) { nam <- colnames(X) nam[startsWith(nam, "`") & endsWith(nam, "`")] <- substr(nam[startsWith(nam, "`") & endsWith(nam, "`")], 2, nchar(nam[startsWith(nam, "`") & endsWith(nam, "`")]) - 1) } matched <- !is.null(object$info$method) un <- un || !matched if (standardize) { s.d.denom <- switch(object$estimand, "ATT" = "treated", "ATC" = "control", "ATE" = "pooled") } else s.d.denom <- NULL ## Summary Stats if (kk > 0) { if (un) { aa.all <- setNames(lapply(seq_len(kk), function(i) qoi(X[,i], tt = treat, ww = NULL, s.weights = s.weights, standardize = standardize, s.d.denom = s.d.denom)), colnames(X)) sum.all <- do.call("rbind", aa.all) dimnames(sum.all) <- list(nam, names(aa.all[[1]])) sum.all.int <- NULL } if (matched) { aa.matched <- setNames(lapply(seq_len(kk), function(i) qoi(X[,i], tt = treat, ww = weights, s.weights = s.weights, subclass = object$subclass, mm = object$match.matrix, standardize = standardize, s.d.denom = s.d.denom, compute.pair.dist = pair.dist)), colnames(X)) sum.matched <- do.call("rbind", aa.matched) dimnames(sum.matched) <- list(nam, names(aa.matched[[1]])) sum.matched.int <- NULL } if (interactions) { n.int <- kk*(kk+1)/2 if (un) sum.all.int <- matrix(NA_real_, nrow = n.int, ncol = length(aa.all[[1]]), dimnames = list(NULL, names(aa.all[[1]]))) if (matched) sum.matched.int <- matrix(NA_real_, nrow = n.int, ncol = length(aa.matched[[1]]), dimnames = list(NULL, names(aa.matched[[1]]))) to.remove <- rep(FALSE, n.int) int.names <- character(n.int) k <- 1 for (i in 1:kk) { for (j in i:kk) { x2 <- X[,i] * X[,j] if (all(abs(x2) < sqrt(.Machine$double.eps)) || all(abs(x2 - X[,i]) < sqrt(.Machine$double.eps))) { #prevent interactions within same factors to.remove[k] <- TRUE } else { if (un) { sum.all.int[k,] <- qoi(x2, tt = treat, ww = NULL, s.weights = s.weights, standardize = standardize, s.d.denom = s.d.denom) } if (matched) { sum.matched.int[k,] <- qoi(x2, tt = treat, ww = weights, s.weights = s.weights, subclass = object$subclass, mm = object$match.matrix, standardize = standardize, s.d.denom = s.d.denom, compute.pair.dist = pair.dist) } if (i == j) { int.names[k] <- paste0(nam[i], "\u00B2") } else { int.names[k] <- paste(nam[i], nam[j], sep=" * ") } } k <- k + 1 } } if (un) { rownames(sum.all.int) <- int.names sum.all <- rbind(sum.all, sum.all.int[!to.remove,,drop = FALSE]) } if (matched) { rownames(sum.matched.int) <- int.names sum.matched <- rbind(sum.matched, sum.matched.int[!to.remove,,drop = FALSE]) } } } if (!is.null(object$distance)) { if (un) { ad.all <- qoi(object$distance, tt = treat, ww = NULL, s.weights = s.weights, standardize = standardize, s.d.denom = s.d.denom) if (!exists("sum.all", inherits = FALSE)) { sum.all <- matrix(ad.all, nrow = 1, dimnames = list("distance", names(ad.all))) } else { sum.all <- rbind(ad.all, sum.all) rownames(sum.all)[1] <- "distance" } } if (matched) { ad.matched <- qoi(object$distance, tt = treat, ww = weights, s.weights = s.weights, subclass = object$subclass, mm = object$match.matrix, standardize = standardize, s.d.denom = s.d.denom, compute.pair.dist = pair.dist) if (!exists("sum.matched", inherits = FALSE)) { sum.matched <- matrix(ad.matched, nrow = 1, dimnames = list("distance", names(ad.matched))) } else { sum.matched <- rbind(ad.matched, sum.matched) rownames(sum.matched)[1] <- "distance" } } } ## Imbalance Reduction if (matched && un && improvement) { reduction <- matrix(NA_real_, nrow = nrow(sum.all), ncol = ncol(sum.all) - 2, dimnames = list(rownames(sum.all), colnames(sum.all)[-(1:2)])) stat.all <- abs(sum.all[,-(1:2), drop = FALSE]) stat.matched <- abs(sum.matched[,-(1:2), drop = FALSE]) #Everything but variance ratios reduction[,-2] <- 100*(stat.all[,-2]-stat.matched[,-2])/stat.all[,-2] #Just variance ratios; turn to log first vr.all <- abs(log(stat.all[,2])) vr.matched <- abs(log(stat.matched[,2])) reduction[,2] <- 100*(vr.all-vr.matched)/vr.all reduction[stat.all == 0 & stat.matched == 0] <- 0 reduction[stat.all == 0 & stat.matched > 0] <- -Inf } else { reduction <- NULL } #Sample size nn <- nn(treat, weights, object$discarded, s.weights) ## output res <- list(call = object$call, nn = nn, sum.all = if (un) sum.all, sum.matched = if (matched) sum.matched, reduction = reduction) class(res) <- "summary.matchit" return(res) } summary.matchit.subclass <- function(object, interactions = FALSE, addlvariables = NULL, standardize = TRUE, data = NULL, pair.dist = FALSE, subclass = FALSE, un = TRUE, improvement = TRUE, ...) { #Create covariate matrix X <- get.covs.matrix(data = object$X) if (!is.null(addlvariables)) { if (is.character(addlvariables)) { if (!is.null(data) && is.data.frame(data)) { if (all(addlvariables %in% names(data))) { addlvariables <- data[addlvariables] } else { stop("All variables in 'addlvariables' must be in 'data'.", call. = FALSE) } } else { stop("If 'addlvariables' is specified as a string, a data frame argument must be supplied to 'data'.", call. = FALSE) } } else if (inherits(addlvariables, "formula")) { vars.in.formula <- all.vars(addlvariables) if (!is.null(data) && is.data.frame(data)) data <- data.frame(data[names(data) %in% vars.in.formula], object$X[names(data) %in% setdiff(vars.in.formula, names(data))]) else data <- object$X addlvariables <- get.covs.matrix(addlvariables, data = data) } else if (!is.matrix(addlvariables) && !is.data.frame(addlvariables)) { stop("The argument to 'addlvariables' must be in one of the accepted forms. See ?summary.matchit for details.", call. = FALSE) } if (is.data.frame(addlvariables)) { if (!all(vapply(addlvariables, is.numeric, logical(1L)))) { addlvariables <- get.covs.matrix(data = addlvariables) } else { addlvariables <- as.matrix(addlvariables) } } } X <- cbind(X, addlvariables[, setdiff(colnames(addlvariables), colnames(X)), drop = FALSE]) which.subclass <- subclass treat <- object$treat weights <- object$weights s.weights <- if (is.null(object$s.weights)) rep(1, length(weights)) else object$s.weights subclass <- object$subclass nam <- colnames(X) kk <- ncol(X) subclasses <- levels(subclass) if (standardize) { s.d.denom <- switch(object$estimand, "ATT" = "treated", "ATC" = "control", "ATE" = "pooled") } else s.d.denom <- NULL if (isTRUE(which.subclass)) which.subclass <- subclasses else if (isFALSE(which.subclass)) which.subclass <- NULL else if (!is.atomic(which.subclass) || !all(which.subclass %in% seq_along(subclasses))) { stop("'subclass' should be TRUE, FALSE, or a vector of subclass indices for which subclass balance is to be displayed.") } else which.subclass <- subclasses[which.subclass] matched <- TRUE #always compute aggregate balance so plot.summary can use it subs <- !is.null(which.subclass) ## Aggregate Subclass #Use the estimated weights to compute aggregate balance. ## Summary Stats sum.all <- sum.matched <- sum.subclass <- reduction <- NULL if (un) { aa.all <- setNames(lapply(seq_len(kk), function(i) qoi(X[,i], tt = treat, ww = NULL, s.weights = s.weights, standardize = standardize, s.d.denom = s.d.denom)), colnames(X)) sum.all <- do.call("rbind", aa.all) dimnames(sum.all) <- list(nam, names(aa.all[[1]])) sum.all.int <- NULL } if (matched) { aa.matched <- setNames(lapply(seq_len(kk), function(i) qoi(X[,i], tt = treat, ww = weights, s.weights = s.weights, subclass = subclass, standardize = standardize, s.d.denom = s.d.denom, compute.pair.dist = pair.dist)), colnames(X)) sum.matched <- do.call("rbind", aa.matched) dimnames(sum.matched) <- list(nam, names(aa.matched[[1]])) sum.matched.int <- NULL } if (interactions) { n.int <- kk*(kk+1)/2 if (un) sum.all.int <- matrix(NA_real_, nrow = n.int, ncol = length(aa.all[[1]]), dimnames = list(NULL, names(aa.all[[1]]))) if (matched) sum.matched.int <- matrix(NA_real_, nrow = n.int, ncol = length(aa.matched[[1]]), dimnames = list(NULL, names(aa.matched[[1]]))) to.remove <- rep(FALSE, n.int) int.names <- character(n.int) k <- 1 for (i in 1:kk) { for (j in i:kk) { x2 <- X[,i] * X[,j] if (all(abs(x2) < sqrt(.Machine$double.eps)) || all(abs(x2 - X[,i]) < sqrt(.Machine$double.eps))) { #prevent interactions within same factors to.remove[k] <- TRUE } else { if (un) { sum.all.int[k,] <- qoi(x2, tt = treat, ww = NULL, s.weights = s.weights, standardize = standardize, s.d.denom = s.d.denom) } if (matched) { sum.matched.int[k,] <- qoi(x2, tt = treat, ww = weights, s.weights = s.weights, subclass = subclass, standardize = standardize, compute.pair.dist = pair.dist) } if (i == j) { int.names[k] <- paste0(nam[i], "\u00B2") } else { int.names[k] <- paste(nam[i], nam[j], sep=" * ") } } k <- k + 1 } } if (un) { rownames(sum.all.int) <- int.names sum.all <- rbind(sum.all, sum.all.int[!to.remove,,drop = FALSE]) } if (matched) { rownames(sum.matched.int) <- int.names sum.matched <- rbind(sum.matched, sum.matched.int[!to.remove,,drop = FALSE]) } } if (!is.null(object$distance)) { if (un) { ad.all <- qoi(object$distance, tt = treat, ww = NULL, s.weights = s.weights, standardize = standardize, s.d.denom = s.d.denom) sum.all <- rbind(ad.all, sum.all) rownames(sum.all)[1] <- "distance" } if (matched) { ad.matched <- qoi(object$distance, tt = treat, ww = weights, s.weights = s.weights, subclass = subclass, standardize = standardize, s.d.denom = s.d.denom, compute.pair.dist = pair.dist) sum.matched <- rbind(ad.matched, sum.matched) rownames(sum.matched)[1] <- "distance" } } ## Imbalance Reduction if (un && matched && improvement) { stat.all <- abs(sum.all[,-(1:2)]) stat.matched <- abs(sum.matched[,-(1:2)]) reduction <- 100*(stat.all-stat.matched)/stat.all reduction[stat.all == 0 & stat.matched == 0] <- 0 reduction[stat.all == 0 & stat.matched > 0] <- -Inf } ## By Subclass if (subs) { sum.subclass <- lapply(which.subclass, function(s) { #qoi.subclass only returns unmatched stats, which is all we need within #subclasses. Otherwise, identical to matched stats. aa <- setNames(lapply(seq_len(kk), function(i) { qoi.subclass(X[,i], tt = treat, s.weights = s.weights, subclass = subclass, s.d.denom = s.d.denom, standardize = standardize, which.subclass = s) }), colnames(X)) sum.sub <- matrix(NA_real_, nrow = kk, ncol = ncol(aa[[1]]), dimnames = list(nam, colnames(aa[[1]]))) sum.sub.int <- NULL for (i in 1:kk) { sum.sub[i,] <- aa[[i]] } if (interactions) { sum.sub.int <- matrix(NA_real_, nrow = kk*(kk+1)/2, ncol = length(aa[[1]]), dimnames = list(NULL, names(aa[[1]]))) to.remove <- rep(FALSE, nrow(sum.sub.int)) int.names <- character(nrow(sum.sub.int)) k <- 1 for (i in 1:kk) { for (j in i:kk) { if (!to.remove[k]) { #to.remove defined above x2 <- X[,i] * X[,j] jqoi <- qoi.subclass(x2, tt = treat, s.weights = s.weights, subclass = subclass, s.d.denom = s.d.denom, standardize = standardize, which.subclass = s) sum.sub.int[k,] <- jqoi if (i == j) { int.names[k] <- paste0(nam[i], "\u00B2") } else { int.names[k] <- paste(nam[i], nam[j], sep = " * ") } } k <- k + 1 } } rownames(sum.sub.int) <- int.names sum.sub <- rbind(sum.sub, sum.sub.int[!to.remove,,drop = FALSE]) } if (!is.null(object$distance)) { ad <- qoi.subclass(object$distance, tt = treat, s.weights = s.weights, subclass = subclass, s.d.denom = s.d.denom, standardize = standardize, which.subclass = s) sum.sub <- rbind(ad, sum.sub) rownames(sum.sub)[1] <- "distance" } return(sum.sub) }) names(sum.subclass) <- paste("Subclass", which.subclass) } ## Sample size qn <- qn(treat, subclass, object$discarded) nn <- nn(treat, weights, object$discarded, s.weights) if (subs) { small.subclass.control <- which.subclass[qn["Control", as.character(which.subclass)] <= 1] if (length(small.subclass.control) > 0) { if (length(small.subclass.control) == 1) warning(paste0("Not enough control units in subclass ", small.subclass.control, "."), call.= FALSE) else warning(paste0("Not enough control units in subclasses ", word_list(small.subclass.control), "."), call.= FALSE) } small.subclass.treated <- which.subclass[qn["Treated", as.character(which.subclass)] <= 1] if (length(small.subclass.treated) > 0) { if (length(small.subclass.treated) == 1) warning(paste0("Not enough treated units in subclass ", small.subclass.treated, "."), call.= FALSE) else warning(paste0("Not enough treated units in subclasses ", word_list(small.subclass.treated), "."), call.= FALSE) } } ## output res <- list(call=object$call, sum.all = sum.all, sum.across = sum.matched, sum.subclass = sum.subclass, reduction = reduction, qn = qn, nn = nn) class(res) <- c("summary.matchit.subclass", "summary.matchit") return(res) }MatchIt/R/discard.R0000644000176200001440000000525614033250602013543 0ustar liggesusersdiscard <- function(treat, pscore = NULL, option = NULL) { n.obs <- length(treat) if (length(option) == 0){ # keep all units discarded <- rep(FALSE, n.obs) } else if (is.logical(option) && length(option) == n.obs && !anyNA(option)) { # user input return(setNames(option, names(treat))) } else if (length(option) > 1 || !is.character(option)) { stop("'discard' must be \"none\", \"both\", \"control\", \"treated\" or a logical vector of observations to discard.", call. = FALSE) } else { option <- match_arg(option, c("none", "both", "control", "treated")) if (option == "none"){ # keep all units discarded <- rep(FALSE, n.obs) } else { if (is.null(pscore)) { stop("'discard' must be a logical vector or \"none\" in the absence of a propensity score.", call. = FALSE) } else if (is.matrix(pscore)) { stop("'discard' must be a logical vector or \"none\" when 'distance' is supplied as a matrix.", call. = FALSE) } pmax0 <- max(pscore[treat==0]) pmax1 <- max(pscore[treat==1]) pmin0 <- min(pscore[treat==0]) pmin1 <- min(pscore[treat==1]) if (option == "both") # discard units outside of common support discarded <- (pscore < max(pmin0, pmin1) | pscore > min(pmax0, pmax1)) else if (option == "control") # discard control units only discarded <- (pscore < pmin1 | pscore > pmax1) else if (option == "treated") # discard treated units only discarded <- (pscore < pmin0 | pscore > pmax0) } # NOTE: WhatIf package has been removed from CRAN, so hull options won't work # else if (option %in% c("hull.control", "hull.treat", "hull.both")) { # ## convex hull stuff # check.package("WhatIf") # X <- model.matrix(reformulate(names(covs), intercept = FALSE), data = covs, # contrasts.arg = lapply(Filter(is.factor, covs), # function(x) contrasts(x, contrasts = nlevels(x) == 1))) # discarded <- rep(FALSE, n.obs) # if (option == "hull.control"){ # discard units not in T convex hull # wif <- WhatIf::whatif(cfact = X[treat==0,], data = X[treat==1,]) # discarded[treat==0] <- !wif$in.hull # } else if (option == "hull.treat") { # wif <- WhatIf::whatif(cfact = X[treat==1,], data = X[treat==0,]) # discarded[treat==1] <- !wif$in.hull # } else if (option == "hull.both"){ # discard units not in T&C convex hull # wif <- WhatIf::whatif(cfact = cbind(1-treat, X), data = cbind(treat, X)) # discarded <- !wif$in.hull # } # } } names(discarded) <- names(treat) return(discarded) } MatchIt/R/cardinality_matchit.R0000644000176200001440000002332614115266340016153 0ustar liggesuserscardinality_matchit <- function(treat, X, estimand = "ATT", tols = .05, s.weights = NULL, ratio = 1, focal = NULL, tvals = NULL, solver = "glpk", time = 2*60, verbose = FALSE) { n <- length(treat) if (is.null(tvals)) tvals <- unique(treat) nt <- length(tvals) #Check inputs if (is.null(s.weights)) s.weights <- rep(1, n) else for (i in tvals) s.weights[treat == i] <- s.weights[treat == i]/mean(s.weights[treat == i]) if (is.null(focal)) focal <- max(tvals) if (length(time) != 1 || !is.numeric(time) || time <= 0) stop("'time' must be a positive number.", call. = FALSE) solver <- match_arg(solver, c("glpk", "symphony", "gurobi")) check.package(switch(solver, glpk = "Rglpk", symphony = "Rsymphony", gurobi = "gurobi")) #Select match type if (estimand == "ATE") match_type <- "template_ate" else if (!is.finite(ratio)) match_type <- "template_att" else match_type <- "cardinality" #Set objective and constraints if (match_type == "template_ate") { #Find largest sample that matches full sample #Objective function: total sample size O <- c( s.weights, #weight for each unit rep(0, nt) #slack coefs for each sample size (n1, n0) ) #Constraint matrix target.means <- apply(X, 2, wm, w = s.weights) C <- matrix(0, nrow = nt * (1 + 2*ncol(X)), ncol = length(O)) Crhs <- rep(0, nrow(C)) Cdir <- rep("==", nrow(C)) for (i in seq_len(nt)) { #Num in group i = ni C[i, seq_len(n)] <- s.weights * (treat == tvals[i]) C[i, n + i] <- -1 #Cov means must be less than target.means+tols/2 r1 <- nt + (i - 1)*2*ncol(X) + 1:ncol(X) C[r1, seq_len(n)] <- t((treat==tvals[i])*s.weights*X) C[r1, n + i] <- -target.means-tols/2 Cdir[r1] <- "<" #Cov means must be greater than target.means-tols/2 r2 <- r1 + ncol(X) C[r2, seq_len(n)] <- t((treat==tvals[i])*s.weights*X) C[r2, n + i] <- -target.means+tols/2 Cdir[r2] <- ">" } #If ratio != 0, constrain n0 to be ratio*n1 if (nt == 2L && is.finite(ratio)) { C_ratio <- c(rep(0, n), rep(-1, nt)) C_ratio[n + which(tvals == focal)] <- ratio C <- rbind(C, C_ratio) Crhs <- c(Crhs, 0) Cdir <- c(Cdir, "==") } #Coef types types <- c(rep("B", n), #Matching weights rep("C", nt)) #Slack coefs for matched group size lower.bound <- c(rep(0, n), rep(1, nt)) upper.bound <- NULL } else if (match_type == "template_att") { #Find largest control group that matches treated group nonf <- which(treat != focal) n0 <- length(nonf) tvals_ <- setdiff(tvals, focal) #Objective function: size of matched control group O <- c( rep(1, n0), #weights for each non-focal unit rep(0, nt - 1) #slack coef for size of non-focal groups ) #Constraint matrix target.means <- apply(X[treat==focal,,drop=FALSE], 2, wm, w = s.weights[treat==focal]) #One row per constraint, one column per coef C <- matrix(0, nrow = (nt - 1) * (1 + 2*ncol(X)), ncol = length(O)) Crhs <- rep(0, nrow(C)) Cdir <- rep("==", nrow(C)) for (i in seq_len(nt - 1)) { #Num in group i = ni C[i, seq_len(n0)] <- s.weights[nonf] * (treat[nonf] == tvals_[i]) C[i, n0 + i] <- -1 #Cov means must be less than target.means+tols r1 <- nt - 1 + (i - 1)*2*ncol(X) + 1:ncol(X) C[r1, seq_len(n0)] <- t((treat[nonf]==tvals_[i])*s.weights[nonf]*X[nonf,,drop = FALSE]) C[r1, n0 + i] <- -target.means-tols Cdir[r1] <- "<" #Cov means must be greater than target.means-tols r2 <- r1 + ncol(X) C[r2, seq_len(n0)] <- t((treat[nonf]==tvals_[i])*s.weights[nonf]*X[nonf,,drop = FALSE]) C[r2, n0 + i] <- -target.means+tols Cdir[r2] <- ">" } #Coef types types <- c(rep("B", n0), #Matching weights rep("C", nt - 1)) #Slack for num control matched lower.bound <- c(rep(0, n0), rep(0, nt - 1)) upper.bound <- NULL } else if (match_type == "cardinality") { #True cardinality matching: find largest balanced sample if (nt > 2) ratio <- 1 #Objective function: total sample size O <- c( s.weights, #weight for each unit 0 #coef for treated sample size (n1) ) #Constraint matrix t_combs <- combn(tvals, 2, simplify = FALSE) C <- matrix(0, nrow = nt + 2*ncol(X)*length(t_combs), ncol = length(O)) Crhs <- rep(0, nrow(C)) Cdir <- rep("==", nrow(C)) for (i in seq_len(nt)) { #Num in group i = ni C[i, seq_len(n)] <- s.weights * (treat == tvals[i]) C[i, n + 1] <- if (tvals[i] == focal) -1 else -ratio } for (j in seq_along(t_combs)) { t_comb <- t_combs[[j]] if (t_comb[2] == focal) t_comb <- rev(t_comb) r1 <- nt + (j - 1)*2*ncol(X) + 1:ncol(X) C[r1, seq_len(n)] <- t(((treat==t_comb[1]) - (treat==t_comb[2])/ratio)*s.weights*X) C[r1, n + 1] <- -tols Cdir[r1] <- "<" r2 <- r1 + ncol(X) C[r2, seq_len(n)] <- t(((treat==t_comb[1]) - (treat==t_comb[2])/ratio)*s.weights*X) C[r2, n + 1] <- tols Cdir[r2] <- ">" } #Coef types types <- c(rep("B", n), #Matching weights rep("C", 1)) #Slack coef for treated group size (n1) lower.bound <- c(rep(0, n), rep(0, 1)) upper.bound <- NULL } weights <- NULL opt.out <- dispatch_optimizer(solver = solver, obj = O, mat = C, dir = Cdir, rhs = Crhs, types = types, max = TRUE, lb = lower.bound, ub = upper.bound, time = time, verbose = verbose) cardinality_error_report(opt.out, solver) sol <- switch(solver, "glpk" = opt.out$solution, "symphony" = opt.out$solution, "gurobi" = opt.out$x) if (match_type %in% c("template_ate", "cardinality")) { weights <- round(sol[seq_len(n)]) } else if (match_type %in% c("template_att")) { weights <- rep(1, n) weights[treat != focal] <- round(sol[seq_len(n0)]) } #Make sure sum of weights in both groups is the same (important for exact matching) if (match_type == "template_att" && (is.na(ratio) || ratio != 1)) { for (t in setdiff(tvals, focal)) { weights[treat == t] <- weights[treat == t]*sum(weights[treat == focal])/sum(weights[treat == t]) } } else { smallest.group <- tvals[which.min(vapply(tvals, function(t) sum(treat == t), numeric(1L)))] for (t in setdiff(tvals, smallest.group)) { weights[treat == t] <- weights[treat == t]*sum(weights[treat == smallest.group])/sum(weights[treat == t]) } } return(list(weights = weights, opt.out = opt.out)) } cardinality_error_report <- function(out, solver) { if (solver == "glpk") { if (out$status == 1) { if (all(out$solution == 0)) { stop("The optimization problem may be infeasible. Try increasing the value of 'tols'.\nSee ?method_cardinality for additional details.", call. = FALSE) } else { warning("The optimizer failed to find an optimal solution in the time alotted. The returned solution may not be optimal.\nSee ?method_cardinality for additional details.", call. = FALSE) } } } else if (solver == "symphony") { if (names(out$status) %in% c("TM_TIME_LIMIT_EXCEEDED") && !all(out$solution == 0) && all(out$solution <= 1)) { warning("The optimizer failed to find an optimal solution in the time alotted. The returned solution may not be optimal.", call. = FALSE) } else if (names(out$status) != "TM_OPTIMAL_SOLUTION_FOUND") { stop("The optimizer failed to find an optimal solution in the time alotted. The optimization problem may be infeasible. Try increasing the value of 'tols'.\nSee ?method_cardinality for additional details.", call. = FALSE) } } else if (solver == "gurobi") { if (out$status %in% c("TIME_LIMIT", "SUBOPTIMAL") && !all(out$x == 0)) { warning("The optimizer failed to find an optimal solution in the time alotted. The returned solution may not be optimal.\nSee ?method_cardinality for additional details.", call. = FALSE) } else if (out$status %in% c("INFEASIBLE", "INF_OR_UNBD", "NUMERIC") || all(out$x == 0)) { stop("The optimization problem may be infeasible. Try increasing the value of 'tols'.\nSee ?method_cardinality for additional details.", call. = FALSE) } } } dispatch_optimizer <- function(solver = "glpk", obj, mat, dir, rhs, types, max = TRUE, lb = NULL, ub = NULL, time = NULL, verbose = FALSE) { if (solver == "glpk") { dir[dir == "="] <- "==" opt.out <- Rglpk::Rglpk_solve_LP(obj = obj, mat = mat, dir = dir, rhs = rhs, max = max, types = types, # bounds = list(lower = lb, upper = ub), #Spurious warning when using bounds control = list(tm_limit = time*1000, verbose = verbose)) } else if (solver == "symphony") { dir[dir == "<"] <- "<=" dir[dir == ">"] <- ">=" dir[dir == "="] <- "==" opt.out <- Rsymphony::Rsymphony_solve_LP(obj = obj, mat = mat, dir = dir, rhs = rhs, max = TRUE, types = types, verbosity = verbose - 2, # bounds = list(lower = lb, upper = ub), #Spurious warning when using bounds time_limit = time) } else if (solver == "gurobi") { dir[dir == "<="] <- "<" dir[dir == ">="] <- ">" dir[dir == "=="] <- "=" opt.out <- gurobi::gurobi(list(A = mat, obj = obj, sense = dir, rhs = rhs, vtype = types, modelsense = "max", lb = lb, ub = ub), params = list(OutputFlag = as.integer(verbose), TimeLimit = time)) } opt.out } MatchIt/R/jitter.pscore.R0000644000176200001440000000350614075746204014737 0ustar liggesusersjitter.pscore <- function(x, interactive, pch = 1, ...){ .pardefault <- par(no.readonly = TRUE) on.exit(par(.pardefault)) treat <- x$treat pscore <- x$distance s.weights <- if (is.null(x$s.weights)) rep(1, length(treat)) else x$s.weights weights <- x$weights * s.weights matched <- weights > 0 q.cut <- x$q.cut jitp <- jitter(rep(1,length(treat)), factor=6)+(treat==1)*(weights==0)-(treat==0) - (weights==0)*(treat==0) cswt <- sqrt(s.weights) cwt <- sqrt(weights) minp <- min(pscore, na.rm = TRUE) maxp <- max(pscore, na.rm = TRUE) plot(pscore, xlim = c(minp - 0.05*(maxp-minp), maxp + 0.05*(maxp-minp)), ylim = c(-1.5,2.5), type="n", ylab="", xlab="Propensity Score", axes=FALSE,main="Distribution of Propensity Scores",...) if (!is.null(q.cut)) abline(v = q.cut, col = "grey", lty = 1) #Matched treated points(pscore[treat==1 & matched], jitp[treat==1 & matched], pch = pch, cex = cwt[treat==1 & matched], ...) #Matched control points(pscore[treat==0 & matched], jitp[treat==0 & matched], pch = pch, cex = cwt[treat==0 & matched], ...) #Unmatched treated points(pscore[treat==1 & !matched], jitp[treat==1 & !matched], pch = pch, cex = cswt[treat==1 & matched],...) #Unmatched control points(pscore[treat==0 & !matched], jitp[treat==0 & !matched], pch = pch, cex = cswt[treat==0 & matched], ...) axis(1) center <- mean(par("usr")[1:2]) text(center, 2.5, "Unmatched Treated Units", adj = .5) text(center, 1.5, "Matched Treated Units", adj = .5) text(center, 0.5, "Matched Control Units", adj = .5) text(center, -0.5, "Unmatched Control Units", adj = .5) box() if (interactive) { print("To identify the units, use first mouse button; to stop, use second.") identify(pscore, jitp, names(treat), atpen = TRUE) } } MatchIt/R/plots.R0000644000176200001440000003612514075627463013315 0ustar liggesusersmatchit.covplot <- function(object, type = "qq", interactive = TRUE, which.xs = NULL, ...) { #Create covariate matrix; include exact and mahvars object$X <- droplevels(object$X) if (!is.null(which.xs)) { if (!is.character(which.xs)) { stop("'which.xs' should be a vector of variables for which balance is to be displayed.", call. = FALSE) } if (!all(which.xs %in% names(object$X))) { missing.vars <- setdiff(which.xs, names(object$X)) stop(paste0("The requested ", ngettext(length(missing.vars), "variable ", "variables "), word_list(missing.vars, is.are = TRUE, quotes = 2), " not present in the supplied matchit object."), call. = FALSE) } which.xs.f <- terms(reformulate(which.xs)) X <- get.covs.matrix(which.xs.f, data = object$X) } else { #Create covariate matrix; include exact and mahvars X <- get.covs.matrix(object$formula, data = object$X) if (!is.null(object$exact)) { Xexact <- get.covs.matrix(object$exact, data = object$X) X <- cbind(X, Xexact[,setdiff(colnames(Xexact), colnames(X)), drop = FALSE]) } if (!is.null(object$mahvars)) { Xmahvars <- get.covs.matrix(object$mahvars, data = object$X) X <- cbind(X, Xmahvars[,setdiff(colnames(Xmahvars), colnames(X)), drop = FALSE]) } } t <- object$treat sw <- if (is.null(object$s.weights)) rep(1, length(t)) else object$s.weights w <- object$weights * sw if (is.null(w)) w <- rep(1, length(t)) split(w, t) <- lapply(split(w, t), function(x) x/sum(x)) split(sw, t) <- lapply(split(sw, t), function(x) x/sum(x)) varnames <- colnames(X) .pardefault <- par(no.readonly = TRUE) on.exit(par(.pardefault)) oma <- c(2.25, 0, 3.75, 1.5) if (type == "qq") { opar <- par(mfrow = c(3, 3), mar = rep.int(1/2, 4), oma = oma) } else if (type %in% c("ecdf", "density")) { opar <- par(mfrow = c(3, 3), mar = c(1.5,.5,1.5,.5), oma = oma) } for (i in seq_along(varnames)){ x <- X[,i] plot(x, type= "n" , axes=FALSE) if (((i-1)%%3)==0) { if (type == "qq") { htext <- "eQQ Plots" mtext(htext, 3, 2, TRUE, 0.5, cex=1.1, font = 2) mtext("All", 3, .25, TRUE, 0.5, cex=1, font = 1) mtext("Matched", 3, .25, TRUE, 0.83, cex=1, font = 1) mtext("Control Units", 1, 0, TRUE, 2/3, cex=1, font = 1) mtext("Treated Units", 4, 0, TRUE, 0.5, cex=1, font = 1) } else if (type == "ecdf") { htext <- "eCDF Plots" mtext(htext, 3, 2, TRUE, 0.5, cex=1.1, font = 2) mtext("All", 3, .25, TRUE, 0.5, cex=1, font = 1) mtext("Matched", 3, .25, TRUE, 0.83, cex=1, font = 1) } else if (type == "density") { htext <- "Density Plots" mtext(htext, 3, 2, TRUE, 0.5, cex=1.1, font = 2) mtext("All", 3, .25, TRUE, 0.5, cex=1, font = 1) mtext("Matched", 3, .25, TRUE, 0.83, cex=1, font = 1) } } par(usr = c(0, 1, 0, 1)) l.wid <- strwidth(varnames, "user") cex.labels <- max(0.75, min(1.45, 0.85/max(l.wid))) text(0.5, 0.5, varnames[i], cex = cex.labels) if (type == "qq") { qqplot_match(x = x, t = t, w = w, sw = sw, ...) } else if (type == "ecdf") { ecdfplot_match(x = x, t = t, w = w, sw = sw, ...) } else if (type == "density") { densityplot_match(x = x, t = t, w = w, sw = sw, ...) } devAskNewPage(ask = interactive) } devAskNewPage(ask = FALSE) } matchit.covplot.subclass <- function(object, type = "qq", which.subclass = NULL, interactive = TRUE, which.xs = NULL, ...) { #Create covariate matrix; include exact and mahvars if (!is.null(which.xs)) { if (!is.character(which.xs)) { stop("'which.xs' should be a vector of variables for which balance is to be displayed.", call. = FALSE) } if (!all(which.xs %in% names(object$X))) { missing.vars <- setdiff(which.xs, names(object$X)) stop(paste0("The requested ", ngettext(length(missing.vars), "variable ", "variables "), word_list(missing.vars, is.are = TRUE, quotes = 2), " not present in the supplied matchit object."), call. = FALSE) } which.xs.f <- terms(reformulate(which.xs)) X <- get.covs.matrix(which.xs.f, data = object$X) } else { #Create covariate matrix; include exact and mahvars X <- get.covs.matrix(object$formula, data = object$X) if (!is.null(object$exact)) { Xexact <- get.covs.matrix(object$exact, data = object$X) X <- cbind(X, Xexact[,setdiff(colnames(Xexact), colnames(X)), drop = FALSE]) } if (!is.null(object$mahvars)) { Xmahvars <- get.covs.matrix(object$mahvars, data = object$X) X <- cbind(X, Xmahvars[,setdiff(colnames(Xmahvars), colnames(X)), drop = FALSE]) } } discrete.cutoff <- 5 t <- object$treat if (!is.atomic(which.subclass)) { stop("The argument to 'subclass' must be NULL or the indices of the subclasses for which to display covariate distributions.", call. = FALSE) } if (!all(which.subclass %in% object$subclass[!is.na(object$subclass)])) { stop("The argument supplied to 'subclass' is not the index of any subclass in the matchit object.", call. = FALSE) } varnames <- colnames(X) .pardefault <- par(no.readonly = TRUE) on.exit(par(.pardefault)) oma <- c(2.25, 0, 3.75, 1.5) for (s in which.subclass) { if (type == "qq") { opar <- par(mfrow = c(3, 3), mar = rep.int(1/2, 4), oma = oma) } else if (type %in% c("ecdf", "density")) { opar <- par(mfrow = c(3, 3), mar = c(1.5,.5,1.5,.5), oma = oma) } sw <- if (is.null(object$s.weights)) rep(1, length(t)) else object$s.weights w <- sw*(!is.na(object$subclass) & object$subclass == s) split(w, t) <- lapply(split(w, t), function(x) x/sum(x)) split(sw, t) <- lapply(split(sw, t), function(x) x/sum(x)) for (i in seq_along(varnames)){ x <- X[,i] plot(x, type = "n" , axes = FALSE) if (((i-1)%%3)==0) { if (type == "qq") { htext <- paste0("eQQ Plots (Subclass ", s,")") mtext(htext, 3, 2, TRUE, 0.5, cex=1.1, font = 2) mtext("All", 3, .25, TRUE, 0.5, cex=1, font = 1) mtext("Matched", 3, .25, TRUE, 0.83, cex=1, font = 1) mtext("Control Units", 1, 0, TRUE, 2/3, cex=1, font = 1) mtext("Treated Units", 4, 0, TRUE, 0.5, cex=1, font = 1) } else if (type == "ecdf") { htext <- paste0("eCDF Plots (Subclass ", s,")") mtext(htext, 3, 2, TRUE, 0.5, cex=1.1, font = 2) mtext("All", 3, .25, TRUE, 0.5, cex=1, font = 1) mtext("Matched", 3, .25, TRUE, 0.83, cex=1, font = 1) } else if (type == "density") { htext <- paste0("Density Plots (Subclass ", s,")") mtext(htext, 3, 2, TRUE, 0.5, cex=1.1, font = 2) mtext("All", 3, .25, TRUE, 0.5, cex=1, font = 1) mtext("Matched", 3, .25, TRUE, 0.83, cex=1, font = 1) } } #Empty plot with variable name par(usr = c(0, 1, 0, 1)) l.wid <- strwidth(varnames, "user") cex.labels <- max(0.75, min(1.45, 0.85/max(l.wid))) text(0.5, 0.5, varnames[i], cex = cex.labels) if (type == "qq") { qqplot_match(x = x, t = t, w = w, sw = sw, ...) } else if (type == "ecdf") { ecdfplot_match(x = x, t = t, w = w, sw = sw, ...) } else if (type == "density") { densityplot_match(x = x, t = t, w = w, sw = sw, ...) } devAskNewPage(ask = interactive) } } devAskNewPage(ask = FALSE) } qqplot_match <- function(x, t, w, sw, discrete.cutoff = 5, ...) { ord <- order(x) x_ord <- x[ord] t_ord <- t[ord] u <- unique(x_ord) #Need to interpolate larger group to be same size as smaller group #Unmatched sample sw_ord <- sw[ord] sw1 <- sw_ord[t_ord == 1] sw0 <- sw_ord[t_ord != 1] x1 <- x_ord[t_ord == 1][sw1 > 0] x0 <- x_ord[t_ord != 1][sw0 > 0] swn1 <- sum(sw[t==1] > 0) swn0 <- sum(sw[t==0] > 0) if (swn1 < swn0) { if (length(u) <= discrete.cutoff) { x0probs <- vapply(u, function(u_) wm(x0 == u_, sw0[sw0 > 0]), numeric(1L)) x0cumprobs <- c(0, cumsum(x0probs)[-length(u)], 1) x0 <- u[findInterval(cumsum(sw1[sw1 > 0]), x0cumprobs, rightmost.closed = TRUE)] } else { x0 <- approx(cumsum(sw0[sw0 > 0]), y = x0, xout = cumsum(sw1[sw1 > 0]), rule = 2, method = "constant", ties = "ordered")$y } } else { if (length(u) <= discrete.cutoff) { x1probs <- vapply(u, function(u_) wm(x1 == u_, sw1[sw1 > 0]), numeric(1L)) x1cumprobs <- c(0, cumsum(x1probs)[-length(u)], 1) x1 <- u[findInterval(cumsum(sw0[sw0 > 0]), x1cumprobs, rightmost.closed = TRUE)] } else { x1 <- approx(cumsum(sw1[sw1 > 0]), y = x1, xout = cumsum(sw0[sw0 > 0]), rule = 2, method = "constant", ties = "ordered")$y } } if (length(u) <= discrete.cutoff) { md <- min(diff(u)) x0 <- jitter(x0, amount = .1*md) x1 <- jitter(x1, amount = .1*md) } rr <- range(c(x0, x1)) plot(x0, x1, xlab = "", ylab = "", xlim = rr, ylim = rr, axes = FALSE, ...) abline(a = 0, b = 1) abline(a = (rr[2]-rr[1])*0.1, b = 1, lty = 2) abline(a = -(rr[2]-rr[1])*0.1, b = 1, lty = 2) axis(2) box() #Matched sample w_ord <- w[ord] w1 <- w_ord[t_ord == 1] w0 <- w_ord[t_ord != 1] x1 <- x_ord[t_ord == 1][w1 > 0] x0 <- x_ord[t_ord != 1][w0 > 0] wn1 <- sum(w[t==1] > 0) wn0 <- sum(w[t==0] > 0) if (wn1 < wn0) { if (length(u) <= discrete.cutoff) { x0probs <- vapply(u, function(u_) wm(x0 == u_, w0[w0 > 0]), numeric(1L)) x0cumprobs <- c(0, cumsum(x0probs)[-length(u)], 1) x0 <- u[findInterval(cumsum(w1[w1 > 0]), x0cumprobs, rightmost.closed = TRUE)] } else { x0 <- approx(cumsum(w0[w0 > 0]), y = x0, xout = cumsum(w1[w1 > 0]), rule = 2, method = "constant", ties = "ordered")$y } } else { if (length(u) <= discrete.cutoff) { x1probs <- vapply(u, function(u_) wm(x1 == u_, w1[w1 > 0]), numeric(1L)) x1cumprobs <- c(0, cumsum(x1probs)[-length(u)], 1) x1 <- u[findInterval(cumsum(w0[w0 > 0]), x1cumprobs, rightmost.closed = TRUE)] } else { x1 <- approx(cumsum(w1[w1 > 0]), y = x1, xout = cumsum(w0[w0 > 0]), rule = 2, method = "constant", ties = "ordered")$y } } if (length(u) <= discrete.cutoff) { md <- min(diff(u)) x0 <- jitter(x0, amount = .1*md) x1 <- jitter(x1, amount = .1*md) } plot(x0, x1, xlab = "", ylab = "", xlim = rr, ylim = rr, axes = FALSE, ...) abline(a = 0, b = 1) abline(a = (rr[2]-rr[1])*0.1, b = 1, lty = 2) abline(a = -(rr[2]-rr[1])*0.1, b = 1, lty = 2) box() } ecdfplot_match <- function(x, t, w, sw, ...) { ord <- order(x) x.min <- x[ord][1] x.max <- x[ord][length(x)] x.range <- x.max - x.min #Unmatched samples plot(x = x, y = w, type= "n" , xlim = c(x.min - .02 * x.range, x.max + .02 * x.range), ylim = c(0, 1), axes = TRUE, ...) for (tr in 0:1) { in.tr <- t[ord] == tr ordt <- ord[in.tr] cswt <- c(0, cumsum(sw[ordt]), 1) xt <- c(x.min - .02 * x.range, x[ordt], x.max + .02 * x.range) lines(x = xt, y = cswt, type = "s", col = if (tr == 0) "grey60" else "black") } abline(h = 0:1) box() #Matched sample plot(x = x, y = w, type= "n" , xlim = c(x.min - .02 * x.range, x.max + .02 * x.range), ylim = c(0, 1), axes = FALSE, ...) for (tr in 0:1) { in.tr <- t[ord] == tr ordt <- ord[in.tr] cwt <- c(0, cumsum(w[ordt]), 1) xt <- c(x.min - .02 * x.range, x[ordt], x.max + .02 * x.range) lines(x = xt, y = cwt, type = "s", col = if (tr == 0) "grey60" else "black") } abline(h = 0:1) axis(1) box() } densityplot_match <- function(x, t, w, sw, ...) { x.min <- min(x) x.max <- max(x) if (!all(x %in% c(x.min, x.max))) { #Density plot for continuous variable small.tr <- (0:1)[which.min(c(sum(t==0), sum(t==1)))] x_small <- x[t==small.tr] A <- list(...) bw <- A[["bw"]] if (is.null(bw)) A[["bw"]] <- bw.nrd0(x_small) else if (is.character(bw)) { bw <- tolower(bw) bw <- match_arg(bw, c("nrd0", "nrd", "ucv", "bcv", "sj", "sj-ste", "sj-dpi")) A[["bw"]] <- switch(bw, nrd0 = bw.nrd0(x_small), nrd = bw.nrd(x_small), ucv = bw.ucv(x_small), bcv = bw.bcv(x_small), sj = , `sj-ste` = bw.SJ(x_small, method = "ste"), `sj-dpi` = bw.SJ(x_small, method = "dpi")) } if (is.null(A[["cut"]])) A[["cut"]] <- 3 d_unmatched <- do.call("rbind", lapply(0:1, function(tr) { cbind(as.data.frame(do.call("density", c(list(x[t==tr], weights = sw[t==tr], from = x.min - A[["cut"]]*A[["bw"]], to = x.max + A[["cut"]]*A[["bw"]]), A))[1:2]), t = tr) })) d_matched <- do.call("rbind", lapply(0:1, function(tr) { cbind(as.data.frame(do.call("density", c(list(x[t==tr], weights = w[t==tr], from = x.min - A[["cut"]]*A[["bw"]], to = x.max + A[["cut"]]*A[["bw"]]), A))[1:2]), t = tr) })) y.max <- max(d_unmatched$y, d_matched$y) #Unmatched samples plot(x = d_unmatched$x, y = d_unmatched$y, type = "n", xlim = c(x.min - A[["cut"]]*A[["bw"]], x.max + A[["cut"]]*A[["bw"]]), ylim = c(0, 1.1*y.max), axes = TRUE, ...) for (tr in 0:1) { in.tr <- d_unmatched$t == tr xt <- d_unmatched$x[in.tr] yt <- d_unmatched$y[in.tr] lines(x = xt, y = yt, type = "l", col = if (tr == 0) "grey60" else "black") } abline(h = 0) box() #Matched sample plot(x = d_matched$x, y = d_matched$y, type = "n", xlim = c(x.min - A[["cut"]]*A[["bw"]], x.max + A[["cut"]]*A[["bw"]]), ylim = c(0, 1.1*y.max), axes = FALSE, ...) for (tr in 0:1) { in.tr <- d_matched$t == tr xt <- d_matched$x[in.tr] yt <- d_matched$y[in.tr] lines(x = xt, y = yt, type = "l", col = if (tr == 0) "grey60" else "black") } abline(h = 0) axis(1) box() } else { #Bar plot for binary variable x1_t1_un <- wm(x[t==1] == x.max, sw[t==1]) x1_t0_un <- wm(x[t==0] == x.max, sw[t==0]) x0_t1_un <- 1 - x1_t1_un x0_t0_un <- 1 - x1_t0_un x1_t1_m <- wm(x[t==1] == x.max, w[t==1]) x1_t0_m <- wm(x[t==0] == x.max, w[t==0]) x0_t1_m <- 1 - x1_t1_m x0_t0_m <- 1 - x1_t0_m ylim <- c(0, 1.1*max(x1_t1_un, x1_t0_un, x0_t1_un, x0_t0_un, x1_t1_m, x1_t0_m, x0_t1_m, x0_t0_m)) barplot(setNames(c(x0_t0_un, x1_t0_un), c(x.min, x.max)), border = "grey60", col = "white", ylim = ylim) barplot(setNames(c(x0_t1_un, x1_t1_un), c(x.min, x.max)), border = "black", col = NA, ylim = ylim, add = TRUE) abline(h = 0:1) box() barplot(setNames(c(x0_t0_m, x1_t0_m), c(x.min, x.max)), border = "grey60", col = "white", ylim = ylim, axes = FALSE) barplot(setNames(c(x0_t1_m, x1_t1_m), c(x.min, x.max)), border = "black", col = NA, ylim = ylim, axes = FALSE, add = TRUE) abline(h = 0:1) box() } }MatchIt/R/cem_matchit.R0000644000176200001440000001563614114473355014426 0ustar liggesuserscem_matchit <- function(treat, X, cutpoints = "sturges", grouping = list(), k2k = FALSE, k2k.method = "mahalanobis", mpower = 2, estimand = "ATT") { #In-house implementation of cem. Basically the same except: #treat is a vector if treatment status, not the name of a variable #X is a data.frame of covariates #when cutpoints are given as integer or string, they define the number of bins, not the number of breakpoints. "ss" is no longer allowed. #When k2k = TRUE, subclasses are created for each pair, mimicking true matching, not each covariate combo. #k2k.method is used instead of method. When k2k.method = NULL, units are matched based on order rather than random. Default is "mahalanobis" (not available in cem). #k2k now works with single covariates (previously it was ignored). k2k uses original variables, not coarsened versions if (k2k && !is.null(k2k.method)) { X.match <- scale(get.covs.matrix(data = X), center = FALSE) k2k.method <- tolower(k2k.method) k2k.method <- match_arg(k2k.method, c("mahalanobis", "euclidean", "maximum", "manhattan", "canberra", "binary", "minkowski")) if (k2k.method == "mahalanobis") mahSigma_inv <- generalized_inverse(cov(X.match)) } is.numeric.cov <- setNames(vapply(X, is.numeric, logical(1L)), names(X)) #Process cutpoints if (!is.list(cutpoints)) { cutpoints <- setNames(as.list(rep(cutpoints, sum(is.numeric.cov))), names(X)[is.numeric.cov]) } if (is.null(names(cutpoints))) stop("'cutpoints' must be a named list of binning values with an element for each numeric variable.", call. = FALSE) bad.names <- setdiff(names(cutpoints), names(X)) nb <- length(bad.names) if (nb > 0) { warning(paste0(ngettext(nb, "The variable ", "The variables "), word_list(bad.names, quotes = 2), " named in 'cutpoints' ", ngettext(nb, "is ", "are "), "not in the variables supplied to matchit() and will be ignored."), immediate. = TRUE, call. = FALSE) cutpoints[bad.names] <- NULL } bad.cuts <- setNames(rep(FALSE, length(cutpoints)), names(cutpoints)) for (i in names(cutpoints)) { if (length(cutpoints[[i]]) == 0) { cutpoints[[i]] <- "sturges" } else if (length(cutpoints[[i]]) == 1) { if (is.character(cutpoints[[i]])) { bad.cuts[i] <- !(startsWith(cutpoints[[i]], "q") && can_str2num(substring(cutpoints[[i]], 2))) && is.na(pmatch(cutpoints[[i]], c("sturges", "fd", "scott"))) } else if (is.numeric(cutpoints[[i]])) { if (!is.finite(cutpoints[[i]]) || cutpoints[[i]] < 0) bad.cuts[i] <- TRUE if (cutpoints[[i]] == 0) is.numeric.cov[i] <- FALSE #Will not be binned else if (cutpoints[[i]] == 1) X[[i]] <- NULL #Removing from X, still in X.match } } else { bad.cuts[i] <- !is.numeric(cutpoints[[i]]) } } if (any(bad.cuts)) { stop(paste0("All entries in the list supplied to 'cutpoints' must be one of the following:", "\n\t- a string containing the name of an allowable binning method", "\n\t- a single number corresponding to the number of bins", "\n\t- a numeric vector containing the cut points separating bins", "\nIncorrectly specified ", ngettext(sum(bad.cuts), "variable:\n\t", "variables:\n\t"), paste(names(cutpoints)[bad.cuts], collapse = ", ")), call. = FALSE) } #Process grouping if (!is.null(grouping) && !is.null(names(grouping))) { X[names(grouping)] <- lapply(names(grouping), function(g) { x <- X[[g]] groups <- grouping[[g]] for (i in seq_along(groups)) { x[x %in% groups[[i]]] <- groups[[i]][1] } x }) cutpoints[names(cutpoints) %in% names(grouping)] <- NULL } #Create bins for numeric variables for (i in names(X)[is.numeric.cov]) { if (is.null(cutpoints) || !i %in% names(cutpoints)) bins <- "sturges" else bins <- cutpoints[[i]] if (is.character(bins)) { if (startsWith(bins, "q") || can_str2num(substring(bins, 2))) { #Quantile bins q <- str2num(substring(bins, 2)) bins <- quantile(X[[i]], probs = seq(1/q, 1 - 1/q, by = 1/q), names = FALSE) #Outer boundaries will be added later } else { bins <- match_arg(tolower(bins), c("sturges", "fd", "scott")) bins <- switch(bins, sturges = nclass.Sturges(X[[i]]), fd = nclass.FD(X[[i]]), scott = nclass.scott(X[[i]])) #Breaks is now a single number } } if (length(bins) == 1) { #cutpoints is number of bins, unlike in cem breaks <- seq(min(X[[i]]), max(X[[i]]), length = bins + 1) breaks[c(1, bins + 1)] <- c(-Inf, Inf) } else { breaks <- c(-Inf, sort(unique(bins)), Inf) } X[[i]] <- findInterval(X[[i]], breaks) } #Exact match xx <- exactify(X, names(treat)) cc <- do.call("intersect", unname(split(xx, treat))) if (length(cc) == 0) { stop("No units were matched. Try coarsening the variables further or decrease the number of variables to match on.", call. = FALSE) } subclass <- setNames(match(xx, cc), names(treat)) extra.sub <- max(subclass, na.rm = TRUE) if (k2k) { na.sub <- is.na(subclass) s <- switch(estimand, "ATC" = 0, 1) for (i in which(tabulateC(subclass[!na.sub]) > 2)) { in.sub <- which(!na.sub & subclass == i) #Compute distance matrix; all 0s if k2k.method = NULL for matched based on data order if (is.null(k2k.method)) dist.mat <- matrix(0, nrow = length(in.sub), ncol = length(in.sub), dimnames = list(names(treat)[in.sub], names(treat)[in.sub])) else if (k2k.method == "mahalanobis") { dist.mat <- matrix(0, nrow = length(in.sub), ncol = length(in.sub), dimnames = list(names(treat)[in.sub], names(treat)[in.sub])) for (t in seq_along(in.sub)) dist.mat[t,] <- mahalanobis(X.match[in.sub,,drop = FALSE], X.match[in.sub[t],], mahSigma_inv, TRUE) } else dist.mat <- as.matrix(dist(X.match[in.sub,,drop = FALSE], method = k2k.method, p = mpower)) #Put smaller group on rows d.rows <- which(rownames(dist.mat) %in% names(treat[in.sub])[treat[in.sub] == s]) dist.mat <- dist.mat[d.rows, -d.rows, drop = FALSE] #For each member of group on row, find closest remaining pair from cols while (all(dim(dist.mat) > 0)) { extra.sub <- extra.sub + 1 closest <- which.min(dist.mat[1,]) subclass[c(rownames(dist.mat)[1], colnames(dist.mat)[closest])] <- extra.sub #Drop already paired units from dist.mat dist.mat <- dist.mat[-1,-closest, drop = FALSE] } #If any unmatched units remain, give them NA subclass if (any(dim(dist.mat) > 0)) subclass[unlist(dimnames(dist.mat))] <- NA_integer_ } } subclass <- factor(subclass, nmax = extra.sub) names(subclass) <- names(treat) return(subclass) }MatchIt/R/RcppExports.R0000644000176200001440000000223114170752470014425 0ustar liggesusers# Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 nn_matchC <- function(mm_, treat_, ord_, ratio, max_rat, discarded, reuse_max, distance_ = NULL, distance_mat_ = NULL, exact_ = NULL, caliper_dist_ = NULL, caliper_covs_ = NULL, calcovs_covs_mat_ = NULL, mah_covs_ = NULL, antiexact_covs_ = NULL, disl_prog = FALSE) { .Call(`_MatchIt_nn_matchC`, mm_, treat_, ord_, ratio, max_rat, discarded, reuse_max, distance_, distance_mat_, exact_, caliper_dist_, caliper_covs_, calcovs_covs_mat_, mah_covs_, antiexact_covs_, disl_prog) } pairdistsubC <- function(x_, t_, s_, num_sub) { .Call(`_MatchIt_pairdistsubC`, x_, t_, s_, num_sub) } subclass2mmC <- function(subclass, treat, focal) { .Call(`_MatchIt_subclass2mmC`, subclass, treat, focal) } tabulateC <- function(bins, nbins = NULL) { .Call(`_MatchIt_tabulateC`, bins, nbins) } weights_matrixC <- function(mm, treat) { .Call(`_MatchIt_weights_matrixC`, mm, treat) } # Register entry points for exported C++ functions methods::setLoadAction(function(ns) { .Call('_MatchIt_RcppExport_registerCCallable', PACKAGE = 'MatchIt') }) MatchIt/R/distance2_methods.R0000644000176200001440000002354114111337572015537 0ustar liggesusers#distance2glm----------------- distance2glm <- function(formula, data = NULL, link = "logit", ...) { if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE link <- sub("linear.", "", as.character(link), fixed = TRUE) } else linear <- FALSE A <- list(...) A[!names(A) %in% c(names(formals(glm)), names(formals(glm.control)))] <- NULL res <- do.call("glm", c(list(formula = formula, data = data, family = quasibinomial(link = link)), A)) if (linear) pred <- predict(res, type = "link") else pred <- predict(res, type = "response") return(list(model = res, distance = pred)) } #distance2gam----------------- distance2gam <- function(formula, data = NULL, link = "logit", ...) { check.package("mgcv") if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE link <- sub("linear.", "", as.character(link), fixed = TRUE) } else linear <- FALSE A <- list(...) weights <- A$weights A$weights <- NULL res <- do.call(mgcv::gam, c(list(formula, data, family = quasibinomial(link), weights = weights), A), quote = TRUE) if (linear) pred <- predict(res, type = "link") else pred <- predict(res, type = "response") return(list(model = res, distance = as.numeric(pred))) } #distance2rpart----------------- distance2rpart <- function(formula, data = NULL, link = NULL, ...) { check.package("rpart") A <- list(...) A[!names(A) %in% c(names(formals(rpart::rpart)), names(formals(rpart::rpart.control)))] <- NULL A$formula <- formula A$data <- data A$method <- "class" res <- do.call(rpart::rpart, A) return(list(model = res, distance = predict(res, type = "prob")[,"1"])) } #distance2nnet----------------- distance2nnet <- function(formula, data = NULL, link = NULL, ...) { check.package("nnet") A <- list(...) weights <- A$weights A$weights <- NULL res <- do.call(nnet::nnet, c(list(formula, data, weights = weights, entropy = TRUE), A), quote = TRUE) return(list(model = res, distance = drop(fitted(res)))) } #distance2cbps----------------- distance2cbps <- function(formula, data = NULL, link = NULL, ...) { check.package("CBPS") if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE } else linear <- FALSE A <- list(...) A[["standardized"]] <- FALSE if (is.null(A[["ATT"]])) { if (is.null(A[["estimand"]])) A[["ATT"]] <- 1 else { estimand <- toupper(A[["estimand"]]) estimand <- match_arg(estimand, c("ATT", "ATC", "ATE")) A[["ATT"]] <- switch(estimand, "ATT" = 1, "ATC" = 2, 0) } } if (is.null(A[["method"]])) { if (is.null(A[["over"]])) A[["method"]] <- "over" else { A[["method"]] <- if (isFALSE(A[["over"]])) "exact" else "over" } } A[c("estimand", "over")] <- NULL if (!is.null(A[["weights"]])) { A[["sample.weights"]] <- A[["weights"]] A[["weights"]] <- NULL } capture.output({ #Keeps from printing message about treatment res <- do.call(CBPS::CBPS, c(list(formula, data), A), quote = TRUE) }) pred <- fitted(res) if (linear) pred <- qlogis(pred) return(list(model = res, distance = pred)) } #distance2bart---------------- distance2bart <- function(formula, data = NULL, link = NULL, ...) { check.package("dbarts") if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE } else linear <- FALSE A <- list(...) A[!names(A) %in% c(names(formals(dbarts::bart2)), names(formals(dbarts::dbartsControl)))] <- NULL A$formula <- formula A$data <- data res <- do.call(dbarts::bart2, A) if (linear) pred <- fitted(res, type = "link") else pred <- fitted(res, type = "response") return(list(model = res, distance = pred)) } # distance2bart <- function(formula, data, link = NULL, ...) { # check.package("BART") # # if (!is.null(link) && startsWith(as.character(link), "linear")) { # linear <- TRUE # link <- sub("linear.", "", as.character(link), fixed = TRUE) # } # else linear <- FALSE # # #Keep link probit because default in matchit is logit but probit is much faster with BART # link <- "probit" # # # if (is.null(link)) link <- "probit" # # else if (!link %in% c("probit", "logit")) { # # stop("'link' must be \"probit\" or \"logit\" with distance = \"bart\".", call. = FALSE) # # } # # data <- model.frame(formula, data) # # treat <- binarize(data[[1]]) # X <- data[-1] # # chars <- vapply(X, is.character, logical(1L)) # X[chars] <- lapply(X[chars], factor) # # A <- list(...) # # if (!is.null(A[["mc.cores"]]) && A[["mc.cores"]][1] > 1) fun <- BART::mc.gbart # else fun <- BART::gbart # # res <- do.call(fun, c(list(X, # y.train = treat, # type = switch(link, "logit" = "lbart", "pbart")), # A[intersect(names(A), setdiff(names(formals(fun)), # c("x.train", "y.train", "x.test", "type", "ntype")))])) # # pred <- res$prob.train.mean # if (linear) pred <- switch(link, logit = qlogis, probit = qnorm)(pred) # # return(list(model = res, distance = pred)) # } #distance2randomforest----------------- distance2randomforest <- function(formula, data = NULL, link = NULL, ...) { check.package("randomForest") newdata <- get_all_vars(formula, data) treatvar <- as.character(formula[[2]]) newdata[[treatvar]] <- factor(newdata[[treatvar]], levels = c("0", "1")) res <- randomForest::randomForest(formula, data = newdata, ...) return(list(model = res, distance = predict(res, type = "prob")[,"1"])) } #distance2glmnet-------------- distance2elasticnet <- function(formula, data = NULL, link = NULL, ...) { if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE link <- sub("linear.", "", as.character(link), fixed = TRUE) } else linear <- FALSE A <- list(...) s <- A[["s"]] A[!names(A) %in% c(names(formals(glmnet::glmnet)), names(formals(glmnet::cv.glmnet)))] <- NULL if (is.null(link)) link <- "logit" if (link == "logit") A$family <- "binomial" else if (link == "log") A$family <- "poisson" else A$family <- binomial(link = link) if (is.null(A[["alpha"]])) A[["alpha"]] <- .5 mf <- model.frame(formula, data = data) A$y <- model.response(mf) A$x <- model.matrix(update(formula, . ~ . + 1), mf)[,-1,drop = FALSE] res <- do.call(glmnet::cv.glmnet, A) if (is.null(s)) s <- "lambda.1se" pred <- drop(predict(res, newx = A$x, s = s, type = if (linear) "link" else "response")) return(list(model = res, distance = pred)) } distance2lasso <- function(formula, data = NULL, link = NULL, ...) { if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE link <- sub("linear.", "", as.character(link), fixed = TRUE) } else linear <- FALSE A <- list(...) s <- A[["s"]] A[!names(A) %in% c(names(formals(glmnet::glmnet)), names(formals(glmnet::cv.glmnet)))] <- NULL if (is.null(link)) link <- "logit" if (link == "logit") A$family <- "binomial" else if (link == "log") A$family <- "poisson" else A$family <- binomial(link = link) A[["alpha"]] <- 1 mf <- model.frame(formula, data = data) A$y <- model.response(mf) A$x <- model.matrix(update(formula, . ~ . + 1), mf)[,-1,drop = FALSE] res <- do.call(glmnet::cv.glmnet, A) if (is.null(s)) s <- "lambda.1se" pred <- drop(predict(res, newx = A$x, s = s, type = if (linear) "link" else "response")) return(list(model = res, distance = pred)) } distance2ridge <- function(formula, data = NULL, link = NULL, ...) { if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE link <- sub("linear.", "", as.character(link), fixed = TRUE) } else linear <- FALSE A <- list(...) s <- A[["s"]] A[!names(A) %in% c(names(formals(glmnet::glmnet)), names(formals(glmnet::cv.glmnet)))] <- NULL if (is.null(link)) link <- "logit" if (link == "logit") A$family <- "binomial" else if (link == "log") A$family <- "poisson" else A$family <- binomial(link = link) A[["alpha"]] <- 0 mf <- model.frame(formula, data = data) A$y <- model.response(mf) A$x <- model.matrix(update(formula, . ~ . + 1), mf)[,-1,drop = FALSE] res <- do.call(glmnet::cv.glmnet, A) if (is.null(s)) s <- "lambda.1se" pred <- drop(predict(res, newx = A$x, s = s, type = if (linear) "link" else "response")) return(list(model = res, distance = pred)) } #distance2gbm-------------- distance2gbm <- function(formula, data = NULL, link = NULL, ...) { if (!is.null(link) && startsWith(as.character(link), "linear")) { linear <- TRUE } else linear <- FALSE A <- list(...) method <- A[["method"]] A[!names(A) %in% names(formals(gbm::gbm))] <- NULL A$formula <- formula A$data <- data A$distribution <- "bernoulli" if (is.null(A[["n.trees"]])) A[["n.trees"]] <- 1e4 if (is.null(A[["interaction.depth"]])) A[["interaction.depth"]] <- 3 if (is.null(A[["shrinkage"]])) A[["shrinkage"]] <- .01 if (is.null(A[["bag.fraction"]])) A[["bag.fraction"]] <- 1 if (is.null(A[["cv.folds"]])) A[["cv.folds"]] <- 5 if (is.null(A[["keep.data"]])) A[["keep.data"]] <- FALSE if (A[["cv.folds"]] <= 1 && A[["bag.fraction"]] == 1) { stop("Either 'bag.fraction' must be less than 1 or 'cv.folds' must be greater than 1 when using distance = \"gbm\".", call. = FALSE) } if (is.null(method)) { if (A[["bag.fraction"]] < 1) method <- "OOB" else method <- "cv" } else if (!tolower(method) %in% c("oob", "cv")) { stop("distance.options$method should be one of \"OOB\" or \"cv\".", call. = FALSE) } res <- do.call(gbm::gbm, A) best.tree <- gbm::gbm.perf(res, plot.it = FALSE, method = method) pred <- drop(predict(res, newdata = data, n.trees = best.tree, type = if (linear) "link" else "response")) return(list(model = res, distance = pred)) }MatchIt/R/matchit2_methods.R0000644000176200001440000011723714146245371015407 0ustar liggesusers# this function takes inputs from matchit() and returns the # strata for each observation in the subclass entry, the # weight for each observation in the weight entry, and the # match.matrix object # # MATCHIT method = NULL-------------------------------------- matchit2null <- function(discarded, ...) { res <- list(weights = as.numeric(!discarded)) class(res) <- "matchit" return(res) } # MATCHIT method = cem-------------------------------------- matchit2cem <- function(treat, covs, estimand = "ATT", verbose = FALSE, ...) { if (length(covs) == 0) stop("Covariates must be specified in the input formula to use coarsened exact matching.", call. = FALSE) if (verbose) cat("Coarsened exact matching...\n") A <- list(...) estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC", "ATE")) #Uses in-house cem, no need for cem package. See cem_matchit.R for code. strat <- do.call("cem_matchit", c(list(treat = treat, X = covs, estimand = estimand), A[names(A) %in% names(formals(cem_matchit))]), quote = TRUE) levels(strat) <- seq_len(nlevels(strat)) names(strat) <- names(treat) mm <- NULL if (isTRUE(A[["k2k"]])) { mm <- nummm2charmm(subclass2mmC(strat, treat, focal = switch(estimand, "ATC" = 0, 1)), treat) } if (verbose) cat("Calculating matching weights... ") res <- list(match.matrix = mm, subclass = strat, weights = weights.subclass(strat, treat, estimand)) if (verbose) cat("Done.\n") class(res) <- "matchit" return(res) } # MATCHIT method = exact------------------------------------ matchit2exact <- function(treat, covs, data, estimand = "ATT", verbose = FALSE, ...){ if(verbose) cat("Exact matching... \n") if (length(covs) == 0) stop("Covariates must be specified in the input formula to use exact matching.", call. = FALSE) estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC", "ATE")) xx <- exactify(covs, names(treat)) cc <- do.call("intersect", lapply(unique(treat), function(t) xx[treat == t])) if (length(cc) == 0) { stop("No exact matches were found.", call. = FALSE) } psclass <- setNames(factor(match(xx, cc), nmax = length(cc)), names(treat)) if (verbose) cat("Calculating matching weights... ") res <- list(subclass = psclass, weights = weights.subclass(psclass, treat, estimand)) if (verbose) cat("Done.\n") class(res) <- "matchit" return(res) } # MATCHIT method = full------------------------------------- matchit2full <- function(treat, formula, data, distance, discarded, ratio = NULL, #min.controls and max.controls in attrs of replace caliper = NULL, mahvars = NULL, exact = NULL, estimand = "ATT", verbose = FALSE, is.full.mahalanobis, antiexact = NULL, ...) { check.package("optmatch") if (verbose) cat("Full matching... \n") A <- list(...) fm.args <- c("omit.fraction", "mean.controls", "tol") A[!names(A) %in% fm.args] <- NULL #Set max problem size to Inf and return to original value after match omps <- getOption("optmatch_max_problem_size") on.exit(options(optmatch_max_problem_size = omps)) options(optmatch_max_problem_size = Inf) estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC", "ATE")) if (estimand == "ATC") { tc <- c("control", "treated") focal <- 0 } else { tc <- c("treated", "control") focal <- 1 } treat_ <- setNames(as.integer(treat[!discarded] == focal), names(treat)[!discarded]) if (!is.null(data)) data <- data[!discarded,] if (is.full.mahalanobis) { if (length(attr(terms(formula, data = data), "term.labels")) == 0) { stop("Covariates must be specified in the input formula when distance = \"mahalanobis\".", call. = FALSE) } mahvars <- formula } min.controls <- attr(ratio, "min.controls") max.controls <- attr(ratio, "max.controls") #Exact matching strata if (!is.null(exact)) { ex <- factor(exactify(model.frame(exact, data = data), sep = ", ", include_vars = TRUE)) cc <- intersect(ex[treat_==1], ex[treat_==0]) if (length(cc) == 0) stop("No matches were found.", call. = FALSE) } else { ex <- factor(rep("_", length(treat_)), levels = "_") } #Create distance matrix; note that Mahalanobis distance computed using entire #sample, like method2nearest, as opposed to within exact strata, like optmatch. if (!is.null(mahvars)) { mahvars <- update(mahvars, treat_ ~ .) environment(mahvars) <- sys.frame(sys.nframe()) mo <- optmatch::match_on(mahvars, data = data[names(data) != "treat_"], method = "mahalanobis") } else if (is.matrix(distance)) { if (focal == 0) distance <- t(distance) mo <- distance[!discarded[treat == focal], !discarded[treat != focal], drop = FALSE] dimnames(mo) <- list(names(treat_)[treat_ == 1], names(treat_)[treat_ == 0]) } else { mo <- optmatch::match_on(setNames(distance[!discarded], names(treat_)), z = treat_) } if (!is.null(antiexact)) { antiexactcovs <- model.frame(antiexact, data) for (i in seq_len(ncol(antiexactcovs))) { mo <- mo + optmatch::antiExactMatch(antiexactcovs[[i]], z = treat_) } } if (!is.null(caliper)) { if (min.controls != 0) { stop("Calipers cannot be used with method = \"full\" when 'min.controls' is specified.", call. = FALSE) } if (any(names(caliper) != "")) { cov.cals <- setdiff(names(caliper), "") calcovs <- get.covs.matrix(reformulate(cov.cals, intercept = FALSE), data = data) } for (i in seq_along(caliper)) { if (names(caliper)[i] != "") { mo_cal <- optmatch::match_on(setNames(calcovs[,names(caliper)[i]], names(treat_)), z = treat_) } else if (is.null(mahvars) || is.matrix(distance)) { mo_cal <- mo } else { mo_cal <- optmatch::match_on(setNames(distance[!discarded], names(treat_)), z = treat_) } mo <- mo + optmatch::caliper(mo_cal, caliper[i]) } rm(mo_cal) } #Initialize pair membership; must include names pair <- setNames(rep(NA_character_, length(treat)), names(treat)) p <- setNames(vector("list", nlevels(ex)), levels(ex)) for (e in levels(ex)) { if (nlevels(ex) > 1) { mo_ <- mo[ex[treat_==1] == e, ex[treat_==0] == e, drop = FALSE] } else mo_ <- mo if (any(dim(mo_) == 0) || !any(is.finite(mo_))) next else if (all(dim(mo_) == 1)) { pair[ex == e] <- paste(1, e, sep = "|") next } withCallingHandlers({ p[[e]] <- do.call(optmatch::fullmatch, c(list(mo_, min.controls = min.controls, max.controls = max.controls, data = treat), #just to get rownames; not actually used in matching A)) }, warning = function(w) { warning(paste0("(from optmatch) ", conditionMessage(w)), call. = FALSE, immediate. = TRUE) invokeRestart("muffleWarning") }, error = function(e1) { stop(paste0("(from optmatch) ", conditionMessage(e1)), call. = FALSE) }) pair[names(p[[e]])[!is.na(p[[e]])]] <- paste(as.character(p[[e]][!is.na(p[[e]])]), e, sep = "|") } if (all(is.na(pair))) stop("No matches were found.", call. = FALSE) if (length(p) == 1) p <- p[[1]] psclass <- factor(pair) levels(psclass) <- seq_len(nlevels(psclass)) names(psclass) <- names(treat) #No match.matrix because treated units don't index matched strata (i.e., more than one #treated unit can be in the same stratum). Stratum information is contained in subclass. if (verbose) cat("Calculating matching weights... ") res <- list(subclass = psclass, weights = weights.subclass(psclass, treat, estimand), obj = p) if (verbose) cat("Done.\n") class(res) <- c("matchit") return(res) } # MATCHIT method = optimal---------------------------------- matchit2optimal <- function(treat, formula, data, distance, discarded, ratio = 1, caliper = NULL, mahvars = NULL, exact = NULL, estimand = "ATT", verbose = FALSE, is.full.mahalanobis, antiexact = NULL, ...) { check.package("optmatch") if (verbose) cat("Optimal matching... \n") A <- list(...) pm.args <- c("tol") A[!names(A) %in% pm.args] <- NULL #Set max problem size to Inf and return to original value after match omps <- getOption("optmatch_max_problem_size") on.exit(options(optmatch_max_problem_size = omps)) options(optmatch_max_problem_size = Inf) estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC")) if (estimand == "ATC") { tc <- c("control", "treated") focal <- 0 } else { tc <- c("treated", "control") focal <- 1 } treat_ <- setNames(as.integer(treat[!discarded] == focal), names(treat)[!discarded]) if (!is.null(data)) data <- data[!discarded,] if (is.full.mahalanobis) { if (length(attr(terms(formula, data = data), "term.labels")) == 0) { stop("Covariates must be specified in the input formula when distance = \"mahalanobis\".", call. = FALSE) } mahvars <- formula } if (!is.null(caliper)) { warning("Calipers are currently not compatible with method = \"optimal\" and will be ignored.", call. = FALSE, immediate. = TRUE) caliper <- NULL } min.controls <- attr(ratio, "min.controls") max.controls <- attr(ratio, "max.controls") if (is.null(max.controls)) { min.controls <- max.controls <- ratio } #Exact matching strata if (!is.null(exact)) { ex <- factor(exactify(model.frame(exact, data = data), sep = ", ", include_vars = TRUE)) cc <- intersect(ex[treat_==1], ex[treat_==0]) if (length(cc) == 0) stop("No matches were found.", call. = FALSE) e_ratios <- vapply(levels(ex), function(e) sum(treat_[ex == e] == 0)/sum(treat_[ex == e] == 1), numeric(1L)) if (any(e_ratios < 1)) { warning(paste0("Fewer ", tc[2], " units than ", tc[1], " units in some 'exact' strata; not all ", tc[1], " units will get a match."), immediate. = TRUE, call. = FALSE) } if (ratio > 1 && any(e_ratios < ratio)) { if (ratio == max.controls) warning(paste0("Not all ", tc[1], " units will get ", ratio, " matches."), immediate. = TRUE, call. = FALSE) else warning(paste0("Not enough ", tc[2], " units for an average of ", ratio, " matches per ", tc[1], " unit in all 'exact' strata."), immediate. = TRUE, call. = FALSE) } } else { ex <- factor(rep("_", length(treat_)), levels = "_") e_ratios <- setNames(sum(treat_ == 0)/sum(treat_ == 1), levels(ex)) if (e_ratios < 1) { warning(paste0("Fewer ", tc[2], " units than ", tc[1], " units; not all ", tc[1], " units will get a match."), immediate. = TRUE, call. = FALSE) } else if (e_ratios < ratio) { if (ratio == max.controls) warning(paste0("Not all ", tc[1], " units will get ", ratio, " matches."), immediate. = TRUE, call. = FALSE) else warning(paste0("Not enough ", tc[2], " units for an average of ", ratio, " matches per ", tc[1], " unit."), immediate. = TRUE, call. = FALSE) } } #Create distance matrix; note that Mahalanobis distance computed using entire #sample, like method2nearest, as opposed to within exact strata, like optmatch. if (!is.null(mahvars)) { mahvars <- update(mahvars, treat_ ~ .) environment(mahvars) <- sys.frame(sys.nframe()) mo <- optmatch::match_on(mahvars, data = data[names(data) != "treat_"], method = "mahalanobis") } else if (is.matrix(distance)) { if (focal == 0) distance <- t(distance) mo <- distance[!discarded[treat == focal], !discarded[treat != focal], drop = FALSE] dimnames(mo) <- list(names(treat_)[treat_ == 1], names(treat_)[treat_ == 0]) } else { mo <- optmatch::match_on(setNames(distance[!discarded], names(treat_)), z = treat_) } if (!is.null(antiexact)) { antiexactcovs <- model.frame(antiexact, data) for (i in seq_len(ncol(antiexactcovs))) { mo <- mo + optmatch::antiExactMatch(antiexactcovs[[i]], z = treat_) } } #Initialize pair membership; must include names pair <- setNames(rep(NA_character_, length(treat)), names(treat)) p <- setNames(vector("list", nlevels(ex)), levels(ex)) for (e in levels(ex)) { if (nlevels(ex) > 1) { mo_ <- mo[ex[treat_==1] == e, ex[treat_==0] == e, drop = FALSE] } else mo_ <- mo if (any(dim(mo_) == 0)) next else if (all(dim(mo_) == 1)) { pair[ex == e] <- paste(1, e, sep = "|") next } #Process ratio, etc., when available ratio in exact matching categories #(e_ratio) differs from requested ratio if (e_ratios[e] < 1) { #Switch treatment and control labels; unmatched treated units are dropped ratio_ <- min.controls_ <- max.controls_ <- 1 mo_ <- t(mo_) } else if (e_ratios[e] < ratio) { #Lower ratio and min.controls. ratio_ <- e_ratios[e] min.controls_ <- min(min.controls, floor(e_ratios[e])) max.controls_ <- max.controls } else { ratio_ <- ratio min.controls_ <- min.controls max.controls_ <- max.controls } withCallingHandlers({ p[[e]] <- do.call(optmatch::fullmatch, c(list(mo_, mean.controls = ratio_, min.controls = min.controls_, max.controls = max.controls_, data = treat), #just to get rownames; not actually used in matching A)) }, warning = function(w) { warning(paste0("(from optmatch) ", conditionMessage(w)), call. = FALSE, immediate. = TRUE) invokeRestart("muffleWarning") }, error = function(e1) { stop(paste0("(from optmatch) ", conditionMessage(e1)), call. = FALSE) }) pair[names(p[[e]])[!is.na(p[[e]])]] <- paste(as.character(p[[e]][!is.na(p[[e]])]), e, sep = "|") } if (all(is.na(pair))) stop("No matches were found.", call. = FALSE) if (length(p) == 1) p <- p[[1]] psclass <- factor(pair) levels(psclass) <- seq_len(nlevels(psclass)) names(psclass) <- names(treat) mm <- nummm2charmm(subclass2mmC(psclass, treat, focal), treat) if (verbose) cat("Calculating matching weights... ") ## calculate weights and return the results res <- list(match.matrix = mm, subclass = psclass, weights = weights.subclass(psclass, treat, estimand), obj = p) if (verbose) cat("Done.\n") class(res) <- "matchit" return(res) } # MATCHIT method = genetic---------------------------------- matchit2genetic <- function(treat, data, distance, discarded, ratio = 1, s.weights = NULL, replace = FALSE, m.order = NULL, caliper = NULL, mahvars = NULL, exact = NULL, formula = NULL, estimand = "ATT", verbose = FALSE, is.full.mahalanobis, use.genetic = TRUE, antiexact = NULL, ...) { check.package(c("Matching", "rgenoud")) if (verbose) cat("Genetic matching... \n") A <- list(...) estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC")) if (estimand == "ATC") { tc <- c("control", "treated") focal <- 0 } else { tc <- c("treated", "control") focal <- 1 } if (!replace) { if (sum(!discarded & treat != focal) < sum(!discarded & treat == focal)) { warning("Fewer ", tc[2], " units than ", tc[1], " units; not all ", tc[1], " units will get a match.", immediate. = TRUE, call. = FALSE) } else if (sum(!discarded & treat != focal) < sum(!discarded & treat == focal)*ratio) { stop(paste0("Not enough ", tc[2], " units for ", ratio, " matches for each ", tc[1], " unit."), call. = FALSE) } } treat <- setNames(as.integer(treat == focal), names(treat)) n.obs <- length(treat) n1 <- sum(treat == 1) if (is.null(names(treat))) names(treat) <- seq_len(n.obs) if (!is.null(distance)) { if (is.null(m.order)) m.order <- if (estimand == "ATC") "smallest" else "largest" else m.order <- match_arg(m.order, c("largest", "smallest", "random", "data")) ord <- switch(m.order, "largest" = order(distance, decreasing = TRUE), "smallest" = order(distance), "random" = sample(seq_len(n.obs), n.obs, replace = FALSE), "data" = seq_len(n.obs)) } else { m.order <- match_arg(m.order, c("data", "random")) ord <- switch(m.order, "random" = sample(seq_len(n.obs), n.obs, replace = FALSE), "data" = seq_len(n.obs)) } ord <- ord[!ord %in% which(discarded)] #Create X (matching variables) and covs_to_balance covs_to_balance <- get.covs.matrix(formula, data = data) if (!is.null(mahvars)) { X <- get.covs.matrix(mahvars, data = data) } else if (is.full.mahalanobis) { X <- covs_to_balance } else { X <- cbind(covs_to_balance, distance) } if (ncol(covs_to_balance) == 0) { stop("Covariates must be specified in the input formula to use genetic matching.", call. = FALSE) } #Process exact; exact.log will be supplied to GenMatch() and Match() if (!is.null(exact)) { #Add covariates in exact not in X to X ex <- as.integer(factor(exactify(model.frame(exact, data = data), names(treat), sep = ", ", include_vars = TRUE))) cc <- intersect(ex[treat==1], ex[treat==0]) if (length(cc) == 0) stop("No matches were found.", call. = FALSE) X <- cbind(X, ex) exact.log <- c(rep(FALSE, ncol(X) - 1), TRUE) } else exact.log <- ex <- NULL #Process caliper; cal will be supplied to GenMatch() and Match() if (!is.null(caliper)) { #Add covariates in caliper other than distance (cov.cals) not in X to X cov.cals <- setdiff(names(caliper), "") if (length(cov.cals) > 0 && any(!cov.cals %in% colnames(X))) { calcovs <- get.covs.matrix(reformulate(cov.cals[!cov.cals %in% colnames(X)]), data = data) X <- cbind(X, calcovs) #Expand exact.log for newly added covariates if (!is.null(exact.log)) exact.log <- c(exact.log, rep(FALSE, ncol(calcovs))) } else cov.cals <- NULL #Matching::Match multiplies calipers by pop SD, so we need to divide by pop SD to unstandardize pop.sd <- function(x) sqrt(sum((x-mean(x))^2)/length(x)) caliper <- caliper / vapply(names(caliper), function(x) { if (x == "") pop.sd(distance[!discarded]) else pop.sd(X[!discarded, x]) }, numeric(1L)) #cal needs one value per variable in X cal <- setNames(rep(Inf, ncol(X)), colnames(X)) #First put covariate calipers into cal if (length(cov.cals) > 0) { cal[intersect(cov.cals, names(cal))] <- caliper[intersect(cov.cals, names(cal))] } #Then put distance caliper into cal if ("" %in% names(caliper)) { dist.cal <- caliper[names(caliper) == ""] if (!is.null(mahvars)) { #If mahvars specified, distance is not yet in X, so add it to X X <- cbind(X, distance) cal <- c(cal, dist.cal) #Expand exact.log for newly added distance if (!is.null(exact.log)) exact.log <- c(exact.log, FALSE) } else { #Otherwise, distance is in X at the specified index cal[ncol(covs_to_balance) + 1] <- dist.cal } } else dist.cal <- NULL } else { cal <- dist.cal <- cov.cals <- NULL } #Reorder data according to m.order since Match matches in order of data; #ord already excludes discarded units treat_ <- treat[ord] covs_to_balance <- covs_to_balance[ord,,drop = FALSE] X_ <- X[ord,,drop = FALSE] if (!is.null(s.weights)) s.weights <- s.weights[ord] if (!is.null(antiexact)) { antiexactcovs <- model.frame(antiexact, data)[ord,,drop = FALSE] antiexact_restrict <- cbind(do.call("rbind", lapply(seq_len(ncol(antiexactcovs)), function(i) { unique.vals <- unique(antiexactcovs[,i]) do.call("rbind", lapply(unique.vals, function(u) { t(combn(which(antiexactcovs[,i] == u), 2)) })) })), -1) if (!is.null(A[["restrict"]])) A[["restrict"]] <- rbind(A[["restrict"]], antiexact_restrict) else A[["restrict"]] <- antiexact_restrict } else { antiexactcovs <- NULL } if (use.genetic) { withCallingHandlers({ g.out <- do.call(Matching::GenMatch, c(list(Tr = treat_, X = X_, BalanceMatrix = covs_to_balance, M = ratio, exact = exact.log, caliper = cal, replace = replace, estimand = "ATT", ties = FALSE, CommonSupport = FALSE, verbose = verbose, weights = s.weights, print.level = 2*verbose), A[names(A) %in% names(formals(Matching::GenMatch))])) }, warning = function(w) { if (!startsWith(conditionMessage(w), "replace==FALSE, but there are more (weighted) treated obs than control obs.")) { warning(paste0("(from Matching) ", conditionMessage(w)), call. = FALSE, immediate. = TRUE) } invokeRestart("muffleWarning") }, error = function(e) { stop(paste0("(from Matching) ", conditionMessage(e)), call. = FALSE) }) } else { #For debugging g.out <- NULL } lab <- names(treat) lab1 <- names(treat[treat == 1]) lab_ <- names(treat_) ind_ <- seq_along(treat)[ord] # if (!isFALSE(A$use.Match)) { withCallingHandlers({ m.out <- Matching::Match(Tr = treat_, X = X_, M = ratio, exact = exact.log, caliper = cal, replace = replace, estimand = "ATT", ties = FALSE, weights = s.weights, CommonSupport = FALSE, Weight = 3, Weight.matrix = if (use.genetic) g.out else if (is.null(s.weights)) generalized_inverse(cor(X_)) else generalized_inverse(cov.wt(X_, s.weights, cor = TRUE)$cor), restrict = A[["restrict"]], version = "fast") }, warning = function(w) { if (!startsWith(conditionMessage(w), "replace==FALSE, but there are more (weighted) treated obs than control obs.")) { warning(paste0("(from Matching) ", conditionMessage(w)), call. = FALSE, immediate. = TRUE) } invokeRestart("muffleWarning") }, error = function(e) { stop(paste0("(from Matching) ", conditionMessage(e)), call. = FALSE) }) #Note: must use character match.matrix because of re-ordering treat into treat_ mm <- matrix(NA_integer_, nrow = n1, ncol = max(table(m.out$index.treated)), dimnames = list(lab1, NULL)) unique.matched.focal <- unique(m.out$index.treated, nmax = n1) ind1__ <- match(lab_, lab1) for (i in unique.matched.focal) { matched.units <- ind_[m.out$index.control[m.out$index.treated == i]] mm[ind1__[i], seq_along(matched.units)] <- matched.units } # } # else { # #Use nn_match() instead of Match() # ord1 <- ord[ord %in% which(treat == 1)] # if (!is.null(cov.cals)) calcovs <- get.covs.matrix(reformulate(cov.cals), data = data) # else calcovs <- NULL # # if (is.null(g.out)) MWM <- generalized_inverse(cov(X)) # else MWM <- g.out$Weight.matrix %*% diag(1/apply(X, 2, var)) # # if (isFALSE(A$fast)) { # mm <- nn_match(treat, ord1, ratio, replace, discarded, distance, ex, dist.cal, # cov.cals, calcovs, X, MWM) # } # else { # mm <- nn_matchC(treat, ord1, ratio, replace, discarded, distance, ex, dist.cal, # cov.cals, calcovs, X, MWM) # } # # mm[] <- names(treat)[mm] # dimnames(mm) <- list(lab1, seq_len(ratio)) # } if (verbose) cat("Calculating matching weights... ") if (replace) { psclass <- NULL } else { psclass <- mm2subclass(mm, treat) } res <- list(match.matrix = nummm2charmm(mm, treat), subclass = psclass, weights = weights.matrix(mm, treat), obj = g.out) if (verbose) cat("Done.\n") class(res) <- "matchit" return(res) } # MATCHIT method = nearest---------------------------------- matchit2nearest <- function(treat, data, distance, discarded, ratio = 1, s.weights = NULL, replace = FALSE, m.order = NULL, caliper = NULL, mahvars = NULL, exact = NULL, formula = NULL, estimand = "ATT", verbose = FALSE, is.full.mahalanobis, fast = TRUE, antiexact = NULL, ...){ if (verbose) { if (fast) check.package("RcppProgress") cat("Nearest neighbor matching... \n") } estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC")) if (estimand == "ATC") { tc <- c("control", "treated") focal <- 0 } else { tc <- c("treated", "control") focal <- 1 } treat <- setNames(as.integer(treat == focal), names(treat)) n.obs <- length(treat) n1 <- sum(treat == 1) lab <- names(treat) lab1 <- lab[treat == 1] if (!is.null(distance)) { names(distance) <- names(treat) } mahcovs <- mahSigma_inv <- distance_mat <- NULL if (is.full.mahalanobis) { mahcovs <- get.covs.matrix(formula, data) if (ncol(mahcovs) == 0) stop("Covariates must be specified in the input formula when distance = \"mahalanobis\".", call. = FALSE) if (is.null(s.weights)) mahSigma_inv <- generalized_inverse(cov(mahcovs)) else mahSigma_inv <- generalized_inverse(cov.wt(mahcovs, s.weights)$cov) mahcovs <- tcrossprod(mahcovs, chol2(mahSigma_inv)) } else if (!is.null(mahvars)) { mahcovs <- get.covs.matrix(mahvars, data) if (is.null(s.weights)) mahSigma_inv <- generalized_inverse(cov(mahcovs)) else mahSigma_inv <- generalized_inverse(cov.wt(mahcovs, s.weights)$cov) mahcovs <- tcrossprod(mahcovs, chol2(mahSigma_inv)) } else if (is.matrix(distance)) { distance_mat <- distance distance <- NULL } min.controls <- attr(ratio, "min.controls") max.controls <- attr(ratio, "max.controls") if (!is.null(caliper)) { if (any(names(caliper) != "")) { caliper.covs <- caliper[names(caliper) != ""] caliper.covs.mat <- get.covs.matrix(reformulate(names(caliper.covs)), data = data) } else { caliper.covs.mat <- caliper.covs <- NULL } if (any(names(caliper) == "")) { caliper.dist <- caliper[names(caliper) == ""] } else { caliper.dist <- NULL } } else { caliper.dist <- caliper.covs <- NULL caliper.covs.mat <- NULL } if (!is.null(antiexact)) { antiexactcovs <- model.frame(antiexact, data) antiexactcovs <- do.call("cbind", lapply(seq_len(ncol(antiexactcovs)), function(i) { as.integer(as.factor(antiexactcovs[[i]])) })) } else { antiexactcovs <- NULL } reuse.max <- attr(replace, "reuse.max") if (reuse.max >= n1) { m.order <- "data" } if (!is.null(exact)) { ex <- factor(exactify(model.frame(exact, data = data), nam = lab, sep = ", ", include_vars = TRUE)) cc <- intersect(as.integer(ex)[treat==1], as.integer(ex)[treat==0]) if (length(cc) == 0) stop("No matches were found.", call. = FALSE) if (reuse.max < n1) { e_ratios <- vapply(levels(ex), function(e) sum(treat[ex == e] == 0)*(reuse.max/sum(treat[ex == e] == 1)), numeric(1L)) if (any(e_ratios < 1)) { warning(paste0("Fewer ", tc[2], " units than ", tc[1], " units in some 'exact' strata; not all ", tc[1], " units will get a match."), immediate. = TRUE, call. = FALSE) } if (ratio > 1 && any(e_ratios < ratio)) { if (is.null(max.controls) || ratio == max.controls) warning(paste0("Not all ", tc[1], " units will get ", ratio, " matches."), immediate. = TRUE, call. = FALSE) else warning(paste0("Not enough ", tc[2], " units for an average of ", ratio, " matches per ", tc[1], " unit in all 'exact' strata."), immediate. = TRUE, call. = FALSE) } } } else { ex <- NULL # ex <- factor(rep("_", length(treat)), levels = "_") if (reuse.max < n1) { e_ratios <- setNames(sum(treat == 0)*(as.numeric(reuse.max)/sum(treat == 1)), levels(ex)) if (e_ratios < 1) { warning(paste0("Fewer ", tc[2], " units than ", tc[1], " units; not all ", tc[1], " units will get a match."), immediate. = TRUE, call. = FALSE) } else if (e_ratios < ratio) { if (is.null(max.controls) || ratio == max.controls) warning(paste0("Not all ", tc[1], " units will get ", ratio, " matches."), immediate. = TRUE, call. = FALSE) else warning(paste0("Not enough ", tc[2], " units for an average of ", ratio, " matches per ", tc[1], " unit."), immediate. = TRUE, call. = FALSE) } } } #Variable ratio (extremal matching), Ming & Rosenbaum (2000) #Each treated unit get its own value of ratio if (!is.null(max.controls)) { if (is.null(distance)) { if (is.null(distance_mat)) stop("'distance' cannot be \"mahalanobis\" for variable ratio matching.", call. = FALSE) else stop("'distance' cannot be supplied as a matrix for variable ratio matching.", call. = FALSE) } m <- round(ratio * n1) # if (m > sum(treat == 0)) stop("'ratio' must be less than or equal to n0/n1.", call. = FALSE) kmax <- floor((m - min.controls*(n1-1)) / (max.controls - min.controls)) kmin <- n1 - kmax - 1 kmed <- m - (min.controls*kmin + max.controls*kmax) ratio0 <- c(rep(min.controls, kmin), kmed, rep(max.controls, kmax)) #Make sure no units are assigned 0 matches if (any(ratio0 == 0)) { ind <- which(ratio0 == 0) ratio0[ind] <- 1 ratio0[ind + 1] <- ratio0[ind + 1] - 1 } ratio <- rep(NA_integer_, n1) #Order by distance; treated are supposed to have higher values ratio[order(distance[treat == 1], decreasing = mean(distance[treat == 1]) > mean(distance[treat != 1]))] <- ratio0 ratio <- as.integer(ratio) } else { ratio <- as.integer(rep(ratio, n1)) } max_rat <- max(ratio) if (!is.null(distance)) { if (is.null(m.order)) m.order <- if (estimand == "ATC") "smallest" else "largest" else m.order <- match_arg(m.order, c("largest", "smallest", "random", "data")) } else { #Mahalanobis or distance matrix m.order <- match_arg(m.order, c("data", "random")) } if (is.null(ex)) { ord <- switch(m.order, "largest" = order(distance[treat == 1], decreasing = TRUE), "smallest" = order(distance[treat == 1], decreasing = FALSE), "random" = sample(seq_len(n1), n1, replace = FALSE), "data" = seq_len(n1)) mm <- matrix(NA_integer_, nrow = n1, ncol = max_rat, dimnames = list(lab1)) mm <- nn_matchC(mm, treat, ord, ratio, max_rat, discarded, reuse.max, distance, distance_mat, ex, caliper.dist, caliper.covs, caliper.covs.mat, mahcovs, antiexactcovs, verbose) } else { mm_list <- lapply(levels(ex), function(e) { if (verbose) { cat(paste0("Matching subgroup ", match(e, levels(ex)), "/", nlevels(ex), ": ", e, "...\n")) } distance_ <- caliper.covs.mat_ <- mahcovs_ <- distance_mat_ <- NULL .e <- which(ex == e) treat_ <- treat[.e] discarded_ <- discarded[.e] if (!is.null(distance)) distance_ <- distance[.e] if (!is.null(caliper.covs.mat)) caliper.covs.mat_ <- caliper.covs.mat[.e,,drop = FALSE] if (!is.null(mahcovs)) mahcovs_ <- mahcovs[.e,,drop = FALSE] if (!is.null(distance_mat)) { .e1 <- which(ex[treat==1] == e) .e0 <- which(ex[treat==0] == e) distance_mat_ <- distance_mat[.e1, .e0, drop = FALSE] } ratio_ <- ratio[ex[treat==1]==e] n1_ <- sum(treat_ == 1) ord_ <- switch(m.order, "largest" = order(distance_[treat_ == 1], decreasing = TRUE), "smallest" = order(distance_[treat_ == 1], decreasing = FALSE), "random" = sample(seq_len(n1_), n1_, replace = FALSE), "data" = seq_len(n1_)) mm_ <- matrix(NA_integer_, nrow = sum(treat_==1), ncol = max_rat, dimnames = list(names(treat_)[treat_ == 1])) mm_ <- nn_matchC(mm_, treat_, ord_, ratio_, max_rat, discarded_, reuse.max, distance_, distance_mat_, NULL, caliper.dist, caliper.covs, caliper.covs.mat_, mahcovs_, antiexactcovs, verbose) mm_[] <- seq_along(treat)[.e][mm_] mm_ }) mm <- do.call("rbind", mm_list)[lab1,, drop = FALSE] } if (verbose) cat("Calculating matching weights... ") if (reuse.max > 1) { psclass <- NULL } else { psclass <- mm2subclass(mm, treat) } res <- list(match.matrix = nummm2charmm(mm, treat), subclass = psclass, weights = weights.matrix(mm, treat)) if (verbose) cat("Done.\n") class(res) <- "matchit" return(res) } # MATCHIT method = subclass--------------------------------- matchit2subclass <- function(treat, distance, discarded, replace = FALSE, exact = NULL, estimand = "ATT", verbose = FALSE, ...) { if(verbose) cat("Subclassifying... \n") A <- list(...) subclass <- A[["subclass"]] sub.by <- A[["sub.by"]] min.n <- A[["min.n"]] #Checks if (is.null(subclass)) subclass <- 6 else if (!is.numeric(subclass) || !is.null(dim(subclass))) { stop("subclass must be a numeric value.", call. = FALSE) } else if (length(subclass) == 1) { if (round(subclass) <= 1) { stop("subclass must be greater than 1.",call.=FALSE) } } else if (!all(subclass <= 1 & subclass >= 0)) { stop("When specifying subclass as a vector of quantiles, all values must be between 0 and 1.", call. = FALSE) } if (!is.null(sub.by)) { sub.by.choices <- c("treat", "control", "all") if (!is.character(sub.by) || length(sub.by) != 1 || anyNA(pmatch(sub.by, sub.by.choices))) { stop("'sub.by' is deprecated and can't be converted into a proper input. Please supply an argument to 'estimand' instead.", call. = FALSE) } else { sub.by <- sub.by.choices[pmatch(sub.by, sub.by.choices)] estimand <- switch(sub.by, "treat" = "ATT", "control" = "ATC", "ATE") warning(paste0("'sub.by' is deprecated and has been replaced with 'estimand'. Setting 'estimand' to \"", estimand, "\"."), call. = FALSE, immediate. = TRUE) } } else { estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC", "ATE")) } if (is.null(min.n)) min.n <- 1 else if (!is.numeric(min.n) || length(min.n) != 1) { stop("'min.n' must be a single number.", call. = FALSE) } n.obs <- length(treat) ## Setting Cut Points if (length(subclass) == 1) { sprobs <- seq(0, 1, length.out = round(subclass) + 1) } else { sprobs <- sort(subclass) if (sprobs[1] != 0) sprobs <- c(0, sprobs) if (sprobs[length(sprobs)] != 1) sprobs <- c(sprobs, 1) subclass <- length(sprobs) - 1 } q <- switch(estimand, "ATT" = quantile(distance[treat==1], probs = sprobs, na.rm = TRUE), "ATC" = quantile(distance[treat==0], probs = sprobs, na.rm = TRUE), "ATE" = quantile(distance, probs = sprobs, na.rm = TRUE)) ## Calculating Subclasses psclass <- setNames(rep(NA_integer_, n.obs), names(treat)) psclass[!discarded] <- as.integer(findInterval(distance[!discarded], q, all.inside = TRUE)) ## If any subclasses don't have members of a treatment group, fill them ## by "scooting" units from nearby subclasses until each subclass has a unit ## from each treatment group if (any(table(treat, psclass) < min.n)) { psclass[!discarded] <- subclass_scoot(psclass[!discarded], treat[!discarded], distance[!discarded], min.n) } psclass <- setNames(factor(psclass, nmax = length(q)), names(treat)) #warning for discrete data if (nlevels(psclass) != subclass){ warning("Due to discreteness in the distance measure, fewer subclasses were generated than were requested.", call.=FALSE) } if (verbose) cat("Calculating matching weights... ") res <- list(subclass = psclass, q.cut = q, weights = weights.subclass(psclass, treat, estimand)) if (verbose) cat("Done.\n") class(res) <- c("matchit.subclass", "matchit") return(res) } # MATCHIT method = cardinality---------------------------------- matchit2cardinality <- function(treat, data, discarded, formula, ratio = 1, focal = NULL, s.weights = NULL, replace = FALSE, exact = NULL, estimand = "ATT", verbose = FALSE, tols = .05, std.tols = TRUE, solver = "glpk", time = 1*60, ...){ if (verbose) { cat("Cardinality matching... \n") } tvals <- unique(treat) nt <- length(tvals) estimand <- toupper(estimand) estimand <- match_arg(estimand, c("ATT", "ATC", "ATE")) if (!is.null(focal)) { if (!focal %in% tvals) stop("'focal' must be a value of the treatment.", call. = FALSE) } else if (estimand == "ATC") { focal <- min(tvals) } else { focal <- max(tvals) } lab <- names(treat) weights <- setNames(rep(0, length(treat)), lab) X <- get.covs.matrix(formula, data = data) if (!is.null(exact)) { ex <- factor(exactify(model.frame(exact, data = data), nam = lab, sep = ", ", include_vars = TRUE)) cc <- do.call("intersect", lapply(tvals, function(t) ex[treat == t])) if (length(cc) == 0) stop("No matches were found.", call. = FALSE) } else { ex <- NULL } #Process tols assign <- get_assign(X) if (length(tols) == 0 || !is.numeric(tols) || anyNA(tols)) stop("'tols' must be numeric.", call. = FALSE) if (length(tols) == 1) tols <- rep(tols, ncol(X)) else if (length(tols) == max(assign)) { tols <- tols[assign] } else if (length(tols) != ncol(X)) { stop("'tols' must have length 1 or the number of covariates. See ?method_cardinality for details.", call. = FALSE) } if (length(std.tols) == 0 || !is.logical(std.tols) || anyNA(std.tols)) stop("'std.tols' must be logical (TRUE/FALSE).", call. = FALSE) if (length(std.tols) == 1) std.tols <- rep(std.tols, ncol(X)) else if (length(std.tols) == max(assign)) { std.tols <- std.tols[assign] } else if (length(std.tols) != ncol(X)) { stop("'std.tols' must have length 1 or the number of covariates. See ?method_cardinality for details.", call. = FALSE) } #Apply std.tols if (any(std.tols)) { if (estimand == "ATE") { sds <- sqrt(Reduce("+", lapply(tvals, function(t) { apply(X[treat==t, std.tols, drop = FALSE], 2, wvar, w = s.weights[treat==t]) }))/nt) } else { sds <- sqrt(apply(X[treat==focal,std.tols,drop=FALSE], 2, wvar, w = s.weights[treat==focal])) } X[,std.tols] <- scale(X[, std.tols, drop = FALSE], center = FALSE, scale = sds) } opt.out <- setNames(vector("list", if (is.null(ex)) 1L else nlevels(ex)), levels(ex)) for (i in seq_along(opt.out)) { if (is.null(ex)) in.exact <- which(!discarded) else in.exact <- which(!discarded & ex == levels(ex)[i]) out <- cardinality_matchit(treat = treat[in.exact], X = X[in.exact,, drop = FALSE], estimand = estimand, tols = tols, s.weights = s.weights[in.exact], ratio = ratio, focal = focal, tvals = tvals, solver = solver, time = time, verbose = verbose) weights[in.exact] <- out[["weights"]] opt.out[[i]] <- out[["opt.out"]] } if (length(opt.out) == 1L) out <- out[[1]] psclass <- NULL res <- list(subclass = psclass, weights = weights, obj = opt.out) if (verbose) cat("Done.\n") class(res) <- "matchit" return(res) }MatchIt/R/match.data.R0000644000176200001440000002222514170732045014141 0ustar liggesusersmatch.data <- function(object, group = "all", distance = "distance", weights = "weights", subclass = "subclass", data = NULL, include.s.weights = TRUE, drop.unmatched = TRUE) { if (!inherits(object, "matchit")) { stop("'object' must be a matchit object, the output of a call to matchit().", call. = FALSE) } if (is.null(data)) { env <- environment(object$formula) data <- try(eval(object$call$data, envir = env), silent = TRUE) if (length(data) == 0 || inherits(data, "try-error") || length(dim(data)) != 2 || nrow(data) != length(object[["treat"]])) { env <- parent.frame() data <- try(eval(object$call$data, envir = env), silent = TRUE) if (length(data) == 0 || inherits(data, "try-error") || length(dim(data)) != 2 || nrow(data) != length(object[["treat"]])) { data <- object[["model"]][["data"]] if (length(data) == 0 || nrow(data) != length(object[["treat"]])) { stop("A valid dataset could not be found. Please supply an argument to 'data' containing the original dataset used in the matching.", call. = FALSE) } } } } if (!is.data.frame(data)) { if (is.matrix(data)) data <- as.data.frame.matrix(data) else stop("'data' must be a data frame.", call. = FALSE) } if (nrow(data) != length(object$treat)) { stop("'data' must have as many rows as there were units in the original call to matchit().", call. = FALSE) } if (!is.null(object$distance)) { if (is.null(distance)) stop("The argument to 'distance' cannot be NULL.", call. = FALSE) if (!is.atomic(distance) || !is.character(distance) || length(distance) != 1 || is.na(distance)) { stop("The argument to 'distance' must be a string of length 1.", call. = FALSE) } if (distance %in% names(data)) { stop(paste0("\"", distance, "\" is already the name of a variable in the data. Please choose another name for distance using the 'distance' argument."), call. = FALSE) } data[[distance]] <- object$distance } if (!is.null(object$weights)) { if (is.null(weights)) stop("The argument to 'weights' cannot be NULL.", call. = FALSE) if (!is.atomic(weights) || !is.character(weights) || length(weights) != 1 || is.na(weights)) { stop("The argument to 'weights' must be a string of length 1.", call. = FALSE) } if (weights %in% names(data)) { stop(paste0("\"", weights, "\" is already the name of a variable in the data. Please choose another name for weights using the 'weights' argument."), call. = FALSE) } data[[weights]] <- object$weights if (!is.null(object$s.weights) && include.s.weights) { data[[weights]] <- data[[weights]] * object$s.weights } } if (!is.null(object$subclass)) { if (is.null(subclass)) stop("The argument to 'subclass' cannot be NULL.", call. = FALSE) if (!is.atomic(subclass) || !is.character(subclass) || length(subclass) != 1 || is.na(subclass)) { stop("The argument to 'subclass' must be a string of length 1.", call. = FALSE) } if (subclass %in% names(data)) { stop(paste0("\"", subclass, "\" is already the name of a variable in the data. Please choose another name for subclass using the 'subclass' argument."), call. = FALSE) } data[[subclass]] <- object$subclass } treat <- object$treat if (drop.unmatched && !is.null(object$weights)) { data <- data[object$weights > 0,,drop = FALSE] treat <- treat[object$weights > 0] } group <- match_arg(group, c("all", "treated", "control")) if (group == "treated") data <- data[treat == 1,,drop = FALSE] else if (group == "control") data <- data[treat == 0,] if (!is.null(object$distance)) attr(data, "distance") <- distance if (!is.null(object$weights)) attr(data, "weights") <- weights if (!is.null(object$subclass)) attr(data, "subclass") <- subclass class(data) <- c("matchdata", class(data)) return(data) } get_matches <- function(object, distance = "distance", weights = "weights", subclass = "subclass", id = "id", data = NULL, include.s.weights = TRUE) { if (!inherits(object, "matchit")) { stop("'object' must be a matchit object, the output of a call to matchit().", call. = FALSE) } if (is.null(object$match.matrix)) { stop("A match.matrix component must be present in the matchit object, which does not occur with all types of matching. Use match.data() instead.", call. = FALSE) } #Get initial data using match.data; note weights and subclass will be removed, #including them here just checks their names don't clash m.data <- match.data(object, group = "all", distance = distance, weights = weights, subclass = subclass, data = data, include.s.weights = FALSE, drop.unmatched = TRUE) if (is.null(id)) stop("The argument to 'id' cannot be NULL.", call. = FALSE) if (!is.atomic(id) || !is.character(id) || length(id) != 1 || is.na(id)) { stop("The argument to 'id' must be a string of length 1.", call. = FALSE) } if (id %in% names(m.data)) { stop(paste0("\"", id, "\" is already the name of a variable in the data. Please choose another name for id using the 'id' argument."), call. = FALSE) } m.data[[id]] <- names(object$treat)[object$weights > 0] for (i in c(weights, subclass)) { if (i %in% names(m.data)) m.data[[i]] <- NULL } mm <- object$match.matrix mm <- mm[!is.na(mm[,1]),,drop = FALSE] tmm <- t(mm) num.matches <- rowSums(!is.na(mm)) matched <- as.data.frame(matrix(NA_character_, nrow = nrow(mm) + sum(!is.na(mm)), ncol = 3)) names(matched) <- c(id, subclass, weights) matched[[id]] <- c(as.vector(tmm[!is.na(tmm)]), rownames(mm)) matched[[subclass]] <- c(as.vector(col(tmm)[!is.na(tmm)]), seq_len(nrow(mm))) matched[[weights]] <- c(1/num.matches[matched[[subclass]][seq_len(sum(!is.na(mm)))]], rep(1, nrow(mm))) if (!is.null(object$s.weights) && include.s.weights) { matched[[weights]] <- matched[[weights]] * object$s.weights[matched[[id]]] } out <- merge(matched, m.data, by = id, all.x = TRUE, sort = FALSE) out <- out[order(out[[subclass]], object$treat[out[[id]]], method = "radix", decreasing = c(FALSE, TRUE)),] rownames(out) <- NULL out[[subclass]] <- factor(out[[subclass]], labels = seq_len(nrow(mm))) if (!is.null(object$distance)) attr(out, "distance") <- distance attr(out, "weights") <- weights attr(out, "subclass") <- subclass attr(out, "id") <- id class(out) <- c("getmatches", class(out)) return(out) } rbind.matchdata <- function(..., deparse.level = 1) { allargs <- list(...) allargs <- allargs[lengths(allargs) > 0L] if (is.null(names(allargs))) { md_list <- allargs allargs <- list() } else { md_list <- allargs[names(allargs) == ""] allargs[names(allargs) == ""] <- NULL } allargs$deparse.level <- deparse.level type <- intersect(c("matchdata", "getmatches"), unlist(lapply(md_list, class))) if (length(type) == 0) stop("A matchdata or getmatches object must be supplied.", call. = FALSE) else if (length(type) == 2) stop("Supplied objects must be all matchdata objects or all getmatches objects.", call. = FALSE) attrs <- c("distance", "weights", "subclass", "id") attr_list <- setNames(vector("list", length(attrs)), attrs) key_attrs <- setNames(rep(NA_character_, length(attrs)), attrs) for (i in attrs) { attr_list[[i]] <- unlist(lapply(md_list, function(m) { a <- attr(m, i) if (length(a) == 0) NA_character_ else a })) if (all(is.na(attr_list[[i]]))) attr_list[[i]] <- NULL else key_attrs[i] <- attr_list[[i]][which(!is.na(attr_list[[i]]))[1]] } attrs <- names(attr_list) key_attrs <- key_attrs[attrs] #Check if all non-attr columns are the same across datasets other_col_list <- lapply(seq_along(md_list), function(d) { setdiff(names(md_list[[d]]), unlist(lapply(attr_list, `[`, d))) }) for (d in seq_along(md_list)[-1]) { if (length(other_col_list[[d]]) != length(other_col_list[[1]]) || !all(other_col_list[[d]] %in% other_col_list[[1]])) { stop(paste("The", switch(type, "matchdata" = "match.data()", "get_matches()"), "inputs must come from the same dataset."), call. = FALSE) } } for (d in seq_along(md_list)) { for (i in attrs) { #Rename columns of each attribute the same across datasets if (is.null(attr(md_list[[d]], i))) md_list[[d]] <- setNames(cbind(md_list[[d]], NA), c(names(md_list[[d]]), key_attrs[i])) else names(md_list[[d]])[names(md_list[[d]]) == attr_list[[i]][d]] <- key_attrs[i] #Give subclasses unique values across datasets if (i == "subclass") { if (all(is.na(md_list[[d]][[key_attrs[i]]]))) md_list[[d]][[key_attrs[i]]] <- factor(md_list[[d]][[key_attrs[i]]], levels = NA) else levels(md_list[[d]][[key_attrs[i]]]) <- paste(d, levels(md_list[[d]][[key_attrs[i]]]), sep = "_") } } #Put all columns in the same order if (d > 1) { md_list[[d]] <- md_list[[d]][names(md_list[[1]])] } class(md_list[[d]]) <- class(md_list[[d]])[class(md_list[[d]]) != type] } out <- do.call("rbind", c(md_list, allargs)) for (i in attrs) { attr(out, i) <- unname(key_attrs[i]) } class(out) <- c(type, class(out)) out } rbind.getmatches <- rbind.matchdataMatchIt/R/add_s.weights.R0000644000176200001440000000507113750077420014662 0ustar liggesusersadd_s.weights <- function(m, s.weights = NULL, data = NULL) { if (!inherits(m, "matchit")) stop("'m' must be a matchit object, the output of a call to matchit().", call. = FALSE) if (!is.null(s.weights)) { if (!is.numeric(s.weights)) { if (is.null(data)) { if (!is.null(m$model)) { env <- attributes(terms(m$model))$.Environment } else { env <- parent.frame() } data <- eval(m$call$data, envir = env) if (length(data) == 0) stop("A dataset could not be found. Please supply an argument to 'data' containing the original dataset used in the matching.", call. = FALSE) } else { if (!is.data.frame(data)) { if (is.matrix(data)) data <- as.data.frame.matrix(data) else stop("'data' must be a data frame.", call. = FALSE) } if (nrow(data) != length(m$treat)) { stop("'data' must have as many rows as there were units in the original call to matchit().", call. = FALSE) } } if (is.character(s.weights)) { if (is.null(data) || !is.data.frame(data)) { stop("If 's.weights' is specified a string, a data frame containing the named variable must be supplied to 'data'.", call. = FALSE) } if (!all(s.weights %in% names(data))) { stop("The name supplied to 's.weights' must be a variable in 'data'.", call. = FALSE) } s.weights.form <- reformulate(s.weights) s.weights <- model.frame(s.weights.form, data, na.action = "na.pass") if (ncol(s.weights) != 1) stop("'s.weights' can only contain one named variable.", call. = FALSE) s.weights <- s.weights[[1]] } else if (inherits(s.weights, "formula")) { s.weights.form <- update(s.weights, NULL ~ .) s.weights <- model.frame(s.weights.form, data, na.action = "na.pass") if (ncol(s.weights) != 1) stop("'s.weights' can only contain one named variable.", call. = FALSE) s.weights <- s.weights[[1]] } else { stop("'s.weights' must be supplied as a numeric vector, string, or one-sided formula.", call. = FALSE) } } if (anyNA(s.weights)) stop("Missing values are not allowed in 's.weights'.", call. = FALSE) if (length(s.weights) != length(m$treat)) stop("'s.weights' must be the same length as the treatment vector.", call. = FALSE) names(s.weights) <- names(m$treat) attr(s.weights, "in_ps") <- isTRUE(all.equal(s.weights, m$s.weights)) m$s.weights <- s.weights m$nn <- nn(m$treat, m$weights, m$discarded, s.weights) } return(m) }MatchIt/R/hist.pscore.R0000644000176200001440000000365313746177152014414 0ustar liggesusershist.pscore <- function(x, xlab="Propensity Score", freq = FALSE, ...){ .pardefault <- par(no.readonly = TRUE) on.exit(par(.pardefault)) treat <- x$treat pscore <- x$distance[!is.na(x$distance)] s.weights <- if (is.null(x$s.weights)) rep(1, length(treat)) else x$s.weights weights <- x$weights * s.weights matched <- weights != 0 q.cut <- x$q.cut minp <- min(pscore) maxp <- max(pscore) ratio <- x$call$ratio if (is.null(ratio)) ratio <- 1 for (i in unique(treat, nmax = 2)) { if (freq) s.weights[treat == i] <- s.weights[treat == i]/mean(s.weights[treat == i]) else s.weights[treat == i] <- s.weights[treat == i]/sum(s.weights[treat == i]) if (freq) weights[treat == i] <- weights[treat == i]/mean(weights[treat == i]) else weights[treat == i] <- weights[treat == i]/sum(weights[treat == i]) } ylab <- if (freq) "Count" else "Proportion" par(mfrow = c(2,2)) # breaks <- pretty(na.omit(pscore), 10) breaks <- seq(minp, maxp, length = 11) xlim <- range(breaks) for (n in c("Raw Treated", "Matched Treated", "Raw Control", "Matched Control")) { if (startsWith(n, "Raw")) w <- s.weights else w <- weights if (endsWith(n, "Treated")) t <- 1 else t <- 0 #Create histogram using weights #Manually assign density, which is used as height of the bars. The scaling #of the weights above determine whether they are "counts" or "proportions". #Regardless, set freq = FALSE in plot() to ensure density is used for bar #height rather than count. pm <- hist(pscore[treat==t], plot = FALSE, breaks = breaks) pm[["density"]] <- vapply(seq_len(length(pm$breaks) - 1), function(i) { sum(w[treat == t & pscore >= pm$breaks[i] & pscore < pm$breaks[i+1]]) }, numeric(1L)) plot(pm, xlim = xlim, xlab = xlab, main = n, ylab = ylab, freq = FALSE, col = "lightgray", ...) if (!startsWith(n, "Raw") && !is.null(q.cut)) abline(v = q.cut, lty=2) } } MatchIt/R/matchit.R0000644000176200001440000002744314147100035013565 0ustar liggesusersmatchit <- function(formula, data = NULL, method = "nearest", distance = "glm", link = "logit", distance.options = list(), estimand = "ATT", exact = NULL, mahvars = NULL, antiexact = NULL, discard = "none", reestimate = FALSE, s.weights = NULL, replace = FALSE, m.order = NULL, caliper = NULL, std.caliper = TRUE, ratio = 1, verbose = FALSE, include.obj = FALSE, ...) { #Checking input format #data input mcall <- match.call() ## Process method if (length(method) == 1 && is.character(method)) { method <- tolower(method) method <- match_arg(method, c("exact", "cem", "nearest", "optimal", "full", "genetic", "subclass", "cardinality")) fn2 <- paste0("matchit2", method) } else if (is.null(method)) { fn2 <- "matchit2null" } else { stop("'method' must be the name of a supported matching method. See ?matchit for allowable options.", call. = FALSE) } #Process formula and data inputs if (!inherits(formula, "formula")) stop("'formula' must be a formula object.", call. = FALSE) tt <- terms(formula, data = data) if (attr(tt, "response") != 1) stop("A treatment variable must be included on the left-hand side of 'formula'.", call. = FALSE) treat.form <- update(tt, . ~ 0) treat.mf <- model.frame(treat.form, data = data, na.action = "na.pass") treat <- model.response(treat.mf) if (anyNA(treat)) stop("Missing values are not allowed in the treatment.", call. = FALSE) if (length(unique(treat)) != 2) stop("The treatment must be a binary variable.", call. = FALSE) treat <- binarize(treat) #make 0/1 names(treat) <- rownames(treat.mf) n.obs <- length(treat) #Process inputs ignored.inputs <- check.inputs(mcall = mcall, method = method, distance = distance, exact = exact, mahvars = mahvars, antiexact = antiexact, caliper = caliper, discard = discard, reestimate = reestimate, s.weights = s.weights, replace = replace, ratio = ratio, m.order = m.order, estimand = estimand) if (length(ignored.inputs) > 0) { for (i in ignored.inputs) assign(i, NULL) } #Process replace replace <- process.replace(replace, method, ...) #Process ratio ratio <- process.ratio(ratio, method, ...) #Process s.weights if (!is.null(s.weights)) { if (is.character(s.weights)) { if (is.null(data) || !is.data.frame(data)) { stop("If 's.weights' is specified a string, a data frame containing the named variable must be supplied to 'data'.", call. = FALSE) } if (!all(s.weights %in% names(data))) { stop("The name supplied to 's.weights' must be a variable in 'data'.", call. = FALSE) } s.weights.form <- reformulate(s.weights) s.weights <- model.frame(s.weights.form, data, na.action = "na.pass") if (ncol(s.weights) != 1) stop("'s.weights' can only contain one named variable.", call. = FALSE) s.weights <- s.weights[[1]] } else if (inherits(s.weights, "formula")) { s.weights.form <- update(s.weights, NULL ~ .) s.weights <- model.frame(s.weights.form, data, na.action = "na.pass") if (ncol(s.weights) != 1) stop("'s.weights' can only contain one named variable.", call. = FALSE) s.weights <- s.weights[[1]] } else if (!is.numeric(s.weights)) { stop("'s.weights' must be supplied as a numeric vector, string, or one-sided formula.", call. = FALSE) } if (anyNA(s.weights)) stop("Missing values are not allowed in 's.weights'.", call. = FALSE) if (length(s.weights) != n.obs) stop("'s.weights' must be the same length as the treatment vector.", call. = FALSE) names(s.weights) <- names(treat) } #Process distance function if (!is.null(method) && method %in% c("exact", "cem", "cardinality")) { fn1 <- NULL } else { distance <- process.distance(distance, method, treat) if (is.numeric(distance)) { fn1 <- "distance2user" } else { fn1 <- paste0("distance2", distance) } } is.full.mahalanobis <- identical(fn1, "distance2mahalanobis") #Process exact, mahvars, and antiexact exactcovs <- mahcovs <- antiexactcovs <- NULL if (!is.null(exact)) { if (is.character(exact)) { if (is.null(data) || !is.data.frame(data)) { stop("If 'exact' is specified as strings, a data frame containing the named variables must be supplied to 'data'.", call. = FALSE) } if (!all(exact %in% names(data))) { stop("All names supplied to 'exact' must be variables in 'data'.", call. = FALSE) } exact <- reformulate(exact) } else if (inherits(exact, "formula")) { exact <- update(exact, NULL ~ .) } else { stop("'exact' must be supplied as a character vector of names or a one-sided formula.", call. = FALSE) } exactcovs <- model.frame(exact, data, na.action = "na.pass") if (anyNA(exactcovs)) stop("Missing values are not allowed in the covariates named in 'exact'.", call. = FALSE) } if (!is.null(mahvars)) { if (is.character(mahvars)) { if (is.null(data) || !is.data.frame(data)) { stop("If 'mahvars' is specified as strings, a data frame containing the named variables must be supplied to 'data'.", call. = FALSE) } if (!all(mahvars %in% names(data))) { stop("All names supplied to 'mahvars' must be variables in 'data'.", call. = FALSE) } mahvars <- reformulate(mahvars) } else if (inherits(mahvars, "formula")) { mahvars <- update(mahvars, NULL ~ .) } else { stop("'mahvars' must be supplied as a character vector of names or a one-sided formula.", call. = FALSE) } mahcovs <- model.frame(mahvars, data, na.action = "na.pass") if (anyNA(mahcovs)) stop("Missing values are not allowed in the covariates named in 'mahvars'.", call. = FALSE) } if (!is.null(antiexact)) { if (is.character(antiexact)) { if (is.null(data) || !is.data.frame(data)) { stop("If 'antiexact' is specified as strings, a data frame containing the named variables must be supplied to 'data'.", call. = FALSE) } if (!all(antiexact %in% names(data))) { stop("All names supplied to 'antiexact' must be variables in 'data'.", call. = FALSE) } antiexact <- reformulate(antiexact) } else if (inherits(antiexact, "formula")) { antiexact <- update(antiexact, NULL ~ .) } else { stop("'antiexact' must be supplied as a character vector of names or a one-sided formula.", call. = FALSE) } antiexactcovs <- model.frame(antiexact, data, na.action = "na.pass") if (anyNA(antiexactcovs)) stop("Missing values are not allowed in the covariates named in 'antiexact'.", call. = FALSE) } #Estimate distance, discard from common support, optionally re-estimate distance if (is.null(fn1) || fn1 == "distance2mahalanobis") { #No distance measure dist.model <- distance <- link <- NULL } else if (fn1 == "distance2user") { dist.model <- link <- NULL } else { if (verbose) { cat("Estimating propensity scores... \n") } if (!is.null(s.weights)) attr(s.weights, "in_ps") <- !distance %in% c("bart", "randomforest") #Estimate distance if (is.null(distance.options$formula)) distance.options$formula <- formula if (is.null(distance.options$data)) distance.options$data <- data if (is.null(distance.options$verbose)) distance.options$verbose <- verbose if (is.null(distance.options$estimand)) distance.options$estimand <- estimand if (is.null(distance.options$weights) && !fn1 %in% c("distance2bart", "distance2randomforest")) { distance.options$weights <- s.weights } if (!is.null(attr(distance, "link"))) distance.options$link <- attr(distance, "link") else distance.options$link <- link dist.out <- do.call(fn1, distance.options, quote = TRUE) dist.model <- dist.out$model distance <- dist.out$distance #Remove smoothing terms from gam formula if (inherits(dist.model, "gam")) { env <- environment(formula) formula <- mgcv::interpret.gam(formula)$fake.formula environment(formula) <- env } } #Process covs covs.formula <- delete.response(terms(formula, data = data)) covs <- model.frame(covs.formula, data = data, na.action = "na.pass") for (i in seq_len(ncol(covs))) { if (anyNA(covs[[i]])) stop("Missing values are not allowed in the covariates.", call. = FALSE) if (is.character(covs[[i]])) covs[[i]] <- factor(covs[[i]]) else if (any(!is.finite(covs[[i]]))) stop("Non-finite values are not allowed in the covariates.", call. = FALSE) } #Process discard if (is.null(fn1) || fn1 %in% c("distance2mahalanobis", "distance2user")) { discarded <- discard(treat, distance, discard) } else { discarded <- discard(treat, dist.out$distance, discard) #Optionally reestimate if (reestimate && any(discarded)) { for (i in seq_along(distance.options)) { if (length(distance.options[[i]]) == n.obs) { distance.options[[i]] <- distance.options[[i]][!discarded] } else if (length(dim(distance.options[[i]])) == 2 && nrow(distance.options[[i]]) == n.obs) { distance.options[[i]] <- distance.options[[i]][!discarded,,drop = FALSE] } } dist.out <- do.call(fn1, distance.options, quote = TRUE) dist.model <- dist.out$model distance[!discarded] <- dist.out$distance } } #Process caliper calcovs <- NULL if (!is.null(caliper)) { caliper <- process.caliper(caliper, method, data, covs, mahcovs, distance, discarded, std.caliper) if (!is.null(attr(caliper, "cal.formula"))) { calcovs <- model.frame(attr(caliper, "cal.formula"), data, na.action = "na.pass") if (anyNA(calcovs)) stop("Missing values are not allowed in the covariates named in 'caliper'.", call. = FALSE) attr(caliper, "cal.formula") <- NULL } } #Matching! match.out <- do.call(fn2, list(treat = treat, covs = covs, data = data, distance = distance, discarded = discarded, exact = exact, mahvars = mahvars, replace = replace, m.order = m.order, caliper = caliper, s.weights = s.weights, ratio = ratio, is.full.mahalanobis = is.full.mahalanobis, formula = formula, estimand = estimand, verbose = verbose, antiexact = antiexact, ...), quote = TRUE) info <- create_info(method, fn1, link, discard, replace, ratio, mahalanobis = is.full.mahalanobis || !is.null(mahvars), subclass = match.out$subclass, antiexact = colnames(antiexactcovs), distance_is_matrix = !is.null(distance) && is.matrix(distance)) #Create X.list for X output, removing duplicate variables X.list <- list(covs, exactcovs, mahcovs, calcovs, antiexactcovs) all.covs <- lapply(X.list, names) for (i in seq_along(X.list)[-1]) if (!is.null(X.list[[i]])) X.list[[i]][names(X.list[[i]]) %in% unlist(all.covs[1:(i-1)])] <- NULL X.list[lengths(X.list) == 0] <- NULL ## putting all the results together out <- list( match.matrix = match.out[["match.matrix"]], subclass = match.out[["subclass"]], weights = match.out[["weights"]], X = do.call("cbind", X.list), call = mcall, info = info, estimand = estimand, formula = formula, treat = treat, distance = if (!is.null(distance) && !is.matrix(distance)) setNames(distance, names(treat)), discarded = discarded, s.weights = s.weights, exact = exact, mahvars = mahvars, caliper = caliper, q.cut = match.out[["q.cut"]], model = dist.model, obj = if (include.obj) match.out[["obj"]] ) out[lengths(out) == 0] <- NULL class(out) <- class(match.out) return(out) } MatchIt/R/weights.subclass.R0000644000176200001440000000310214030163166015413 0ustar liggesusersweights.subclass <- function(psclass, treat, estimand = "ATT") { NAsub <- is.na(psclass) i1 <- treat == 1 & !NAsub i0 <- treat == 0 & !NAsub weights <- setNames(rep(0, length(treat)), names(treat)) if (!is.factor(psclass)) { psclass <- factor(psclass, nmax = min(sum(i1), sum(i0))) levels(psclass) <- seq_len(nlevels(psclass)) } treated_by_sub <- setNames(tabulateC(psclass[i1], nlevels(psclass)), levels(psclass)) control_by_sub <- setNames(tabulateC(psclass[i0], nlevels(psclass)), levels(psclass)) total_by_sub <- treated_by_sub + control_by_sub psclass <- as.character(psclass) if (estimand == "ATT") { weights[i1] <- 1 weights[i0] <- (treated_by_sub/control_by_sub)[psclass[i0]] #Weights average 1 weights[i0] <- weights[i0]*sum(i0)/sum(weights[i0]) } else if (estimand == "ATC") { weights[i1] <- (control_by_sub/treated_by_sub)[psclass[i1]] weights[i0] <- 1 #Weights average 1 weights[i1] <- weights[i1]*sum(i1)/sum(weights[i1]) } else if (estimand == "ATE") { weights[i1] <- (total_by_sub/treated_by_sub)[psclass[i1]] weights[i0] <- (total_by_sub/control_by_sub)[psclass[i0]] #Weights average 1 weights[i1] <- weights[i1]*sum(i1)/sum(weights[i1]) weights[i0] <- weights[i0]*sum(i0)/sum(weights[i0]) } if (sum(weights)==0) stop("No units were matched.", call. = FALSE) else if (sum(weights[treat == 1])==0) stop("No treated units were matched.", call. = FALSE) else if (sum(weights[treat == 0])==0) stop("No control units were matched.", call. = FALSE) return(weights) } MatchIt/R/weights.matrix.R0000644000176200001440000000076314015411753015113 0ustar liggesusersweights.matrix <- function(match.matrix, treat) { if (!is.integer(match.matrix)) match.matrix <- charmm2nummm(match.matrix, treat) weights <- weights_matrixC(match.matrix, treat) if (sum(weights)==0) stop("No units were matched.", call. = FALSE) else if (sum(weights[treat == 1])==0) stop("No treated units were matched.", call. = FALSE) else if (sum(weights[treat == 0])==0) stop("No control units were matched.", call. = FALSE) return(setNames(weights, names(treat))) }MatchIt/NEWS.md0000644000176200001440000006533014170744604012717 0ustar liggesusers--- output: html_document: default pdf_document: default --- `MatchIt` News and Updates ====== # MatchIt 4.3.3 * Fixed a bug where `rbind.matchdata()` would produce datasets twice their expected length. Thanks to @sconti555. (#98) # MatchIt 4.3.2 * Fixed a bug where the `q.cut` component of the `matchit` object when `method = "subclass"` was not included. Now it is. Thanks to @aldencabajar. (#92) * The `nn` and `qn` components of the `matchit` object have been removed. They are now computed by `summary.matchit()` and included in the `summary.matchit` object. * Removed the code to disable compiler checks to satisfy CRAN requirements. # MatchIt 4.3.1 * Added the `reuse.max` argument to `matchit()` with `method = "nearest"`. This controls the maximum number of times each control unit can be used as a match. Setting `reuse.max = 1` is equivalent to matching without replacement (i.e., like setting `replace = FALSE`), and setting `reuse.max = Inf` is equivalent to matching with replacement with no restriction on the reuse of controls (i.e., like setting `replace = TRUE`). Values in between restrict how many times each control unit can be used as a match. Higher values will tend to improve balance but decrease precision. * Mahalanobis distance matching with `method = "nearest"` is now a bit faster. * Fixed a bug where `method = "full"` would fail when some exact matching strata contained exactly one treated unit and exactly one control unit. (#88) * Fixed a bug introduced in 4.3.0 where the inclusion of character variables would cause the error `"Non-finite values are not allowed in the covariates."` Thanks to Moaath Mustafa. * Documentation updates. # MatchIt 4.3.0 * Cardinality and template matching can now be used by setting `method = "cardinality"` in `matchit()`. These methods use mixed integer programming to directly select a matched subsample without pairing or stratifying units that satisfied user-supplied balance constraints. Their results can be dramatically improved when using the Gurobi optimizer. See `?method_cardinality` and `vignette("matching-methods")` for more information. * Added `"lasso"`, `"ridge"`, and `"elasticnet"` as options for `distance`. These estimate propensity scores using lasso, ridge, or elastic net regression, respectively, as implemented in the `glmnet` package. * Added `"gbm"` as an option for `distance`. This estimates propensity scores using generalized boosted models as implemented in the `gbm` package. This implementation differs from that in `twang` by using cross-validation or out-of-bag error to choose the tuning parameter as opposed to balance. * A new argument, `include.obj`, has been added to `matchit()`. When `TRUE`, the intermediate matching object created internally will be included in the output in the `obj` component. See the individual methods pages for information on what is included in each output. This is ignored for some methods. * Density plots can now be requested using `plot.matchit()` by setting `type = "density"`. These display the density of each covariate in the treatment groups before and after matching and are similar to the plots created by `cobalt::bal.plot()`. Density plots can be easier to interpret than eCDF plots. `vignette("assessing-balance")` has been updated with this addition. * A clearer error is now produced when the treatment variable is omitted from the `formula` argument to `matchit()`. * Improvements in how `match.data()` finds the original dataset. It's still always safer to supply an argument to `data`, but now `match.data()` will look in the environment of the `matchit` formula, then the calling environment of `match.data()`, then the `model` component of the `matchit` object. A clearer error message is now printed when a valid dataset cannot be found in these places. * Fixed a bug that would occur when using `summary.matchit()` with just one covariate. * When `verbose = TRUE` and a propensity score is estimated (i.e., using the `distance` argument), a message saying so will be displayed. * Fixed a bug in `print.matchit()` where it would indicate that the propensity score was used in a caliper if any caliper was specified, even if not on the propensity score. Now, it will only indicate that the propensity score was used in a caliper if it actually was. * Fixed a bug in `plot.matchit()` that would occur when a level of a factor had no values. * Speed improvements for `method = "full"` with `exact` specified. These changes can make current results differ slightly from past results when the `tol` value is high. It is recommended to always use a low value of `tol`. * Typo fixes in documentation and vignettes. * Fixed a bug where supplying a "GAM" string to the `distance` argument (i.e., using the syntax prior to version 4.0.0) would ignore the link supplied. * When an incompatible argument is supplied to `matchit()` (e.g., `reestimate` with `distance = "mahalanobis"`), an error or warning will only be produced when that argument has been set to a value other than its default (e.g., so setting `reestimate = FALSE` will no longer throw an error). This fixes an issue brought up by Vu Ng when using `MatchThem`. * A clearer error is produced when non-finite values are present in the covariates. # MatchIt 4.2.0 * `distance` can now be supplied as a distance matrix containing pairwise distances with nearest neighbor, optimal, and full matching. This means users can create a distance matrix outside `MatchIt` (e.g., using `optmatch::match_on()` or `dist()`) and `matchit()` will use those distances in the matching. See `?distance` for details. * Added `rbind.matchdata()` method for `matchdata` and `getmatches` objects (the output of `match.data()` and `get_matches()`, respectively) to avoid subclass conflicts when combining matched samples after matching within subgroups. * Added a section in `vignette("estimating-effects")` on moderation analysis with matching, making use of the new `rbind()` method. * Added `antiexact` argument to perform anti-exact matching, i.e., matching that ensures treated and control units have different values of certain variables. See [here](https://stackoverflow.com/questions/66526115/propensity-score-matching-with-panel-data) and [here](https://stackoverflow.com/questions/61120201/avoiding-duplicates-from-propensity-score-matching?rq=1) for examples where this feature was requested and might be useful. Anti-exact matching works with nearest neighbor, optimal, full, and genetic matching. The argument to `antiexact` should be similar to an argument to `exact`: either a string or a one-sided `formula` containing the names of the anti-exact matching variables. * Slight speed improvements for nearest neighbor matching, especially with `exact` specified. * With `method = "nearest"`, `verbose = TRUE`, and `exact` specified, separate messages and progress bars will be shown for each subgroup of the `exact` variable(s). * A spurious warning that would appear when using a large `ratio` with `replace = TRUE` and `method = "nearest"` no longer appears. * Fixed a bug when trying to supply `distance` as a labelled numeric vector (e.g., resulting from `haven`). * Fixed some typos in the documentation and vignettes. # MatchIt 4.1.0 * Coarsened exact matching (i.e., `matchit()` with `method = "cem"`) has been completely rewritten and no longer involves the `cem` package, eliminating some spurious warning messages and fixing some bugs. All the same arguments can still be used, so old code will run, though some results will differ slightly. Additional options are available for matching and performance has improved. See `?method_cem` for details on the differences between the implementation in the current version of `MatchIt` and that in `cem` and older versions of `MatchIt`. In general, these changes make coarsened exact matching function as one would expect it to, circumventing some peculiarities and bugs in the `cem` package. * Variable ratio matching is now compatible with `method = "optimal"` in the same way it is with `method = "nearest"`, i.e., by using the `min.controls` and `max.controls` arguments. * With `method = "full"` and `method = "optimal"`, the maximum problem size has been set to unlimited, so that larger datasets can be used with these methods without error. They may take a long time to run, though. * Processing improvements with `method = "optimal"` due to rewriting some functions in `Rcpp`. * Using `method = "optimal"` runs more smoothly when combining it with exact matching through the `exact` argument. * When using `ratio` different from 1 with `method = "nearest"` and `method = "optimal"` and with exact matching, errors and warnings about the number of units that will be matched are clearer. Certain `ratio`s that would produce errors now only produce warnings. * Fixed a bug when no argument was supplied to `data` in `matchit()`. * Improvements to vignettes and documentation. # MatchIt 4.0.1 * Restored `cem` functionality after it had been taken down and re-uploaded. * Added `pkgdown` website. * Computing matching weights after matching with replacement is faster due to programming in `Rcpp`. * Fixed issues with `Rcpp` code that required C++11. C++11 has been added to SystemRequirements in DESCRIPTION, and `MatchIt` now requires R version 3.1.0 or later. # MatchIt 4.0.0 ## General Fixes and New Features * `match.data()`, which is used to create matched datasets, has a few new arguments. The `data` argument can be supplied with a dataset that will have the matching weights and subclasses added. If not supplied, `match.data()` will try to figure out the appropriate dataset like it did in the past. The `drop.unmatched` argument controls whether unmatched units are dropped from the output. The default is `TRUE`, consistent with past behavior. Warnings are now more informative. * `get_matches()`, which seems to have been rarely used since it performed a similar function to `match.data()`, has been revamped. It creates a dataset with one row per unit per matched pair. If a unit is part of two separate pairs (e.g., as a result of matching with replacement), it will get two rows in the output dataset. The goal here was to be able to implement standard error estimators that rely both on repeated use of the same unit and subclass/pair membership, e.g., Austin & Cafri (2020). Otherwise, it functions similarly to `match.data()`. *NOTE: the changes to `get_matches()` are breaking changes! Legacy code will not work with the new syntax!* * `print.matchit()` has completely changed and now prints information about the matching type and specifications. `summary.matchit()` contains all the information that was in the old `print` method. * A new function, `add_s.weights()`, adds sampling weights to `matchit` objects for use in balance checking and effect estimation. Sampling weights can also be directly supplied to `matchit()` through the new `s.weights` argument. A new vignette describing how to using `MatchIt` with sampling weights is available at `vignette("sampling-weights")`. * The included dataset, `lalonde`, now uses a `race` variable instead of separate `black` and `hispan` variables. This makes it easier to see how character variables are treated by `MatchIt` functions. * Added extensive documentation for every function, matching method, and distance specification. Documentation no longer links to `gking.harvard.edu/matchit` as it now stands alone. ## `matchit()` * An argument to `data` is no longer required if the variables in `formula` are present in the environment. * When missing values are present in the dataset but not in the treatment or matching variables, the error that used to appear no longer does. * The `exact` argument can be supplied either as a character vector of names of variables in `data` or as a one-sided formula. A full cross of all included variables will be used to create bins within which matching will take place. * The `mahvars` argument can also be supplied either as a character vector of names of variables in `data` or as a one-sided formula. Mahalanobis distance matching will occur on the variables in the formula, processed by `model.matrix()`. Use this when performing Mahalanobis distance matching on some variables within a caliper defined by the propensity scores estimated from the variables in the main `formula` using the argument to `distance`. For regular Mahalanobis distance matching (without a propensity score caliper), supply the variables in the main `formula` and set `distance = "mahalanobis"`. * The `caliper` argument can now be specified as a numeric vector with a caliper for each variable named in it. This means you can separately impose calipers on individual variables as well as or instead of the propensity score. For example, to require that units within pairs must be no more than .2 standard deviations of `X1` away from each other, one could specify `caliper = c(X1 = .2)`. A new option `std.caliper` allows the choice of whether the caliper is in standard deviation units or not, and one value per entry in `caliper` can be supplied. An unnamed entry to `caliper` applies the caliper to the propensity score and the default of `std.caliper` is `FALSE`, so this doesn't change the behavior of old code. These options only apply to the methods that accept calipers, namely `"nearest"`, `"genetic"`, and `"full"`. * A new `estimand` argument can be supplied to specify the target estimand of the analysis. For all methods, the ATT and ATC are available with the ATT as the default, consistent with prior behavior. For some methods, the ATE is additionally available. Note that setting the estimand doesn't actually mean that estimand is being targeted; if calipers, common support, or other restrictions are applied, the target population will shift from that requested. `estimand` just triggers the choice of which level of the treatment is focal and what formula should be used to compute weights from subclasses. * In methods that accept it, `m.order` can be set to "`data`", which matches in the order the data appear. With `distance = "mahalanobis"`, `m.order` can be "`random`" or "`data`", with "`data`" as the default. Otherwise, `m.order` can be `"largest"`, `"smallest"`, `"random"`, or `"data"`, with `"largest"` as the default (consistent with prior behavior). * The output to `matchit()` has changed slightly; the component `X` is now a data frame, the result of a call to `model.frame()` with the formula provided. If `exact` or `mahvars` are specified, their variables are included as well, if not already present. It is included for all methods and is the same for all methods. In the past, it was the result of a call to `model.matrix()` and was only included for some methods. * When key arguments are supplied to methods that don't accept them, a warning will be thrown. * `method` can be set to `NULL` to not perform matching but create a `matchit` object, possibly with a propensity score estimated using `distance` or with a common support restriction using `discard`, for the purpose of supplying to `summary.matchit()` to assess balance prior to matching. ### `method = "nearest"` * Matching is much faster due to re-programming with `Rcpp`. * With `method = "nearest"`, a `subclass` component containing pair membership is now included in the output when `replace = FALSE` (the default), as it has been with optimal and full matching. * When using `method = "nearest"` with `distance = "mahalanobis"`, factor variables can now be included in the main `formula`. The design matrix no longer has to be full rank because a generalized inverse is used to compute the Mahalanobis distance. * Unless `m.order = "random"`, results will be identical across runs. Previously, several random choices would occur to break ties. Ties are broken based on the order of the data; shuffling the order of the data may therefore yield different matches. * When using `method = "nearest"` with a caliper specified, the nearest control unit will be matched to the treated unit if one is available. Previously, a random control unit within the caliper would be selected. This eliminates the need for the `calclosest` argument, which has been removed. * Variable ratio extremal matching as described by Ming & Rosenbaum (2000) can be implemented using the new `min.controls` and `max.controls` arguments. * Added ability to display a progress bar during matching, which can be activated by setting `verbose = TRUE`. ### `method = "optimal"` and `method = "full"` * Fixed bug in `method = "optimal"`, which produced results that did not match `optmatch`. Now they do. * Added support for optimal and full Mahalanobis distance matching by setting `method = "mahalanobis"` with `method = "optimal"` and `method = "full"`. Previously, both methods would perform a random match if `method` was set to `"mahalanobis"`. Now they use the native support in `optmatch::pairmatch()` and `optmatch::fullmatch()` for Mahalanobis distance matching. * Added support for exact matching with `method = "optimal"` and `method = "full"`. As with `method = "nearest"`, the names of the variables for which exact matches are required should be supplied to the `exact` argument. This relies on `optmatch::exactMatch()`. * The warning that used to occur about the order of the match not guaranteed to be the same as the original data no longer occurs. * For `method = "full"`, the `estimand` argument can be set to `"ATT"`, `"ATC"`, or `"ATE"` to compute matching weights that correspond to the given estimand. See `?matchit` for details on how weights are computed for each `estimand`. ### `method = "genetic"` * Fixed a bug with `method = "genetic"` that caused an error with some `ratio` greater than 1. * The default of `replace` in `method = "genetic"` is now `FALSE`, as it is with `method = "nearest"`. * When `verbose = FALSE`, the default, no output is printed with `method = "genetic"`. With `verbose = TRUE`, the printed output of `Matching::GenMatch()` with `print.level = 2` is displayed. * The `exact` argument now correctly functions with `method = "genetic"`. Previously, it would have to be specified in accordance with its use in `Matching::GenMatch()`. * Different ways to match on variables are now allowed with `method = "genetic"`, similar to how they are with `method = "nearest"`. If `distance = "mahalanobis"`, no propensity score will be computed, and genetic matching will be performed just on the variables supplied to `formula`. If `mahvars` is specified, genetic matching will be performed on the variables supplied to `mahvars`, but balance will be optimized on all covariates supplied to `formula`. Otherwise, genetic matching will be performed on the variables supplied to `formula` and the propensity score. Previously, `mahvars` was ignored. Balance is now always optimized on the variables included in `formula` and never on the propensity score, whereas in the past the propensity score was always included in the balance optimization. * The `caliper` argument now works as it does with `method = "nearest"` and other methods rather than needing to be supplied in a way that `Matching::Match()` would accept. * A `subclass` component is now included in the output when `replace = FALSE` (the default), as it has been with optimal and full matching. ### `method = "cem"` and `method = "exact"` * With `method = "cem"`, the `k2k` argument is now recognized. Previously it was ignored unless an argument to `k2k.method` was supplied. * The `estimand` argument can be set to `"ATT"`, `"ATC"`, or `"ATE"` to compute matching weights that correspond to the given estimand. Previously only ATT weights were computed. See `?matchit` for details on how weights are computed for each `estimand`. ### `method = "subclass"` * Performance improvements. * A new argument, `min.n`, can be supplied, which controls the minimum size a treatment group can be in each subclass. When any estimated subclass doesn't have enough members from a treatment group, units from other subclasses are pulled to fill it so that every subclass will have at least `min.n` units from each treatment group. This uses the same mechanism as is used in `WeightIt`. The default `min.n` is 1 to ensure there are at least one treated and control unit in each subclass. * Rather than producing warnings and just using the default number of subclasses (6), when an inappropriate argument is supplied to `subclass`, an error will occur. * The new `subclass` argument to `summary()` can be used to control whether subclass balance statistics are computed; it can be `TRUE` (display balance for all subclasses), `FALSE` (display balance for no subclasses), or a vector of subclass indices on which to assess balance. The default is `FALSE`. * With `summary()`, balance aggregating across subclasses is now computed using subclass weights instead of by combining the subclass-specific balance statistics. * The `sub.by` argument has been replaced with `estimand`, which can be set to `"ATT"`, `"ATC"`, or `"ATE"` to replace the `sub.by` inputs of `"treat"`, `"control"`, and `"all"`, respectively. Previously, weights for `sub.by` that wasn't `"treat"` were incorrect; they are now correctly computed for all inputs to `estimand`. ### `distance` * The allowable options to `distance` have changed slightly. The input should be either `"mahalanobis"` for Mahalanobis distance matching (without a propensity score caliper), a numeric vector of distance values (i.e., values whose absolute pairwise differences form the distances), or one of the allowable options. The new allowable values include `"glm"` for propensity scores estimated with `glm()`, `"gam"` for propensity scores estimated with `mgcv::gam()`, `"rpart"` for propensity scores estimated with `rpart::rpart()`, `"nnet"` for propensity scores estimated with `nnet::nnet()`, `"cbps"` for propensity scores estimated with `CBPS::CBPS()`, or `bart` for propensity scores estimated with `dbarts::bart2()`. To specify a link (e.g., for probit regression), specify an argument to the new `link` parameter. For linear versions of the propensity score, specify `link` as `"linear.{link}"`. For example, for linear probit regression propensity scores, one should specify `distance = "glm", link = "linear.probit"`. The default `distance` is `"glm"` and the default link is `"logit"`, so these can be omitted if either is desired. Not all methods accept a `link`, and for those that don't, it will be ignored. If an old-style `distance` is supplied, it will be converted to an appropriate specification with a warning (except for `distance = "logit"`, which will be converted without a warning). * Added `"cbps"` as option for `distance`. This estimates propensity scores using the covariate balancing propensity score (CBPS) algorithm as implemented in the `CBPS` package. Set `link = "linear"` to use a linear version of the CBPS. * Added `"bart"` as an option for `distance`. This estimates propensity scores using Bayesian Additive Regression Trees (BART) as implemented in the `dbarts` package. * Added `"randomforest"` as an option for `distance`. This estimates propensity scores using random forests as implemented in the `randomForest` package. * Bugs in `distance = "rpart"` have been fixed. ## `summary.matchit()` * When `interactions = TRUE`, interactions are no longer computed with the distance measure or between dummy variables of the same factor. Variable names are cleaned up and easier to read. * The argument to `addlvariables` can be specified as a data frame or matrix of covariates, a formula with the additional covariates (and transformations) on the right side, or a character vector containing the names of the additional covariates. For the latter two, if the variables named do not exist in the `X` component of the `matchit` output object or in the environment, an argument to `data` can be supplied to `summary()` that contains these variables. * The output for `summary()` is now the same for all methods (except subclassification). Previously there were different methods for a few different types of matching. * The eCDF median (and QQ median) statistics have been replaced with the variance ratio, which is better studied and part of several sets of published recommendations. The eCDF and QQ median statistics provide little information above and beyond the corresponding mean statistics. The variance ratio uses the variances weighted by the matching weights. * The eCDF and QQ statistics have been adjusted. Both now use the weights that were computed as part of the matching. The eCDF and QQ statistics for binary variables are set to the difference in group proportions. The standard deviation of the control group has been removed from the output. * The default for `standardize` is now `TRUE`, so that standardized mean differences and eCDF statistics will be displayed by default. * A new column for the average absolute pair difference for each covariate is included in the output. The values indicate how far treated and control units within pairs are from each other. An additional argument to `summary.matchit()`, `pair.dist`, controls whether this value is computed. It can take a long time for some matching methods and could be omitted to speed up computation. * Balance prior to matching can now be suppressed by setting `un = FALSE`. * Percent balance improvement can now be suppressed by setting `improvement = FALSE`. When `un = FALSE`, `improvement` is automatically set to `FALSE`. ## `plot.matchit()` * Plots now use weighted summaries when weights are present, removing the need for the `num.draws` argument. * Added a new plot type, `"ecdf"`, which creates empirical CDF plots before and after matching. * The appearance of some plots has improved (e.g., text is appropriately centered, axes are more clearly labeled). For eQQ plots with binary variables or variables that take on only a few values, the plots look more like clusters than snakes. * The argument to `type` can be abbreviated (e.g., `"j"` for jitter). * Fixed a bug that caused all plots generated after using `plot(., type = "hist")` to be small. * When specifying an argument to `which.xs` to control for which variables balance is displayed graphically, the input should be the name of the original variable rather than the version that appears in the `summary()` output. In particular, if a factor variable was supplied to `matchit()`, it should be referred to by its name rather than the names of its split dummies. This makes it easier to view balance on factor variables without having to know or type the names of all their levels. * eQQ plots can now be used with all matching methods. Previously, attempting `plot()` after `method = "exact"` would fail. ## `plot.summary.matchit()` * The summary plot has been completely redesigned. It is now a Love plot made using `graphics::dotchart()`. A few options are available for ordering the variables, presenting absolute or raw standardized mean differences, and placing threshold lines on the plots. For a more sophisticated interface, see `cobalt::love.plot()`, which natively supports `matchit` objects and uses `ggplot2` as its engine. MatchIt/MD50000644000176200001440000001103514172363674012130 0ustar liggesusersc053897863df058d669ddfb69391cbde *DESCRIPTION cd0a09a9e4c0878803eba41b2be8fbee *NAMESPACE 9b9ffe6a7bf3a30646afd66aadc94926 *NEWS.md 5488c87ea67aacf8c157fbb218cd32fb *R/RcppExports.R 3e4a236f0f74af04ce900634d7875e6d *R/add_s.weights.R f5b1663318937fe334103a3e493ea1d4 *R/aux_functions.R 2ee2843e8c9d29685aef32c27679e92a *R/cardinality_matchit.R 0461a19f913087b8b5da8d3a9ceb94e8 *R/cem_matchit.R 347cb921b954ba13cc1a48dc83e6beb7 *R/class_functions.R 080151a9d8b3e1cf8427c3847ec39bd3 *R/discard.R c8df1d73b540e92e274b39900fc4ab22 *R/distance2_methods.R b6f68cb0248b9e41e8839f801d5a07c8 *R/hist.pscore.R b51f60d55ab41f8f7d77f6c78ee3e876 *R/jitter.pscore.R 61b84fdf669f46b4b10d6afe3141d8b1 *R/match.data.R cec5a1f399fe4329d83e32f163544bc5 *R/match.qoi.R 81b881d4744e94fd2c7b4b7be3f908f3 *R/matchit.R 1283ecad0addcc99fab4396085868959 *R/matchit2_methods.R e24392471c8762f3b9d67aacef3405a6 *R/plots.R 49119b5473ea8688a7c9330495cc0608 *R/weights.matrix.R 347fcdd56372ff802f9dd607ed084187 *R/weights.subclass.R 50725d802d280c4f3142f5af0bc41c35 *build/partial.rdb cd9c7fb6307951e65346eb0a2c1f8335 *build/vignette.rds 0aa041b2d13d2489b60afea0103660f7 *data/lalonde.tab c012f1e21141a52e56be963a3d870592 *inst/CITATION 518a7c5072d52b9acb7b03dfe343e5dd *inst/doc/MatchIt.R 5c5f58f3a1a1b1bebbb417a9ad4b5662 *inst/doc/MatchIt.Rmd a6f52e2d7f4eede52f562ea5a0795544 *inst/doc/MatchIt.html 558ee8f1f73a1f7f15f7ab85289459a9 *inst/doc/assessing-balance.R 8687630460a299fef26d8500e78f7028 *inst/doc/assessing-balance.Rmd debbaa3fb89ae85006cd1a3b299cf792 *inst/doc/assessing-balance.html 3018b76020bd0baa12aa0fbd27162467 *inst/doc/estimating-effects.R 5c8742e737ffe1c916ed24f668f33ac3 *inst/doc/estimating-effects.Rmd f359b20f7d3cdf98c3e9c901541001ab *inst/doc/estimating-effects.html e841bc8370bf9a724c927f5eedf72d05 *inst/doc/matching-methods.R f4a15c7ff0715de09ac815570047b01e *inst/doc/matching-methods.Rmd 7fadf1b52a32d8c540ca3478a5c83768 *inst/doc/matching-methods.html 98dc83a0a9f75a45b0432f60defe4e2e *inst/doc/sampling-weights.R 6cc4205e870f60d349159dab0151f57b *inst/doc/sampling-weights.Rmd ec032b9ff033673bdba9ef1bf2fc96df *inst/doc/sampling-weights.html d8542009af1579378e6011688530d1e4 *inst/figures/README-unnamed-chunk-2-1.png 8030f0a6025faf870c8488c021ecc73b *inst/figures/README-unnamed-chunk-2-2.png 1dbdd9f0910baa73633ea8c40d23c342 *inst/figures/README-unnamed-chunk-4-1.png 0920f6ee93789de8c475279701801c56 *inst/figures/README-unnamed-chunk-6-1.png 3959b8f5e0ce16d378d0e100deed1f88 *inst/include/MatchIt.h 9fad12433d3f9297c7d649ab27613924 *inst/include/MatchIt_RcppExports.h 85e27733109311b892efb69ea846e9c3 *man/add_s.weights.Rd c21911d3a7d8389b2452df866bdfcc18 *man/distance.Rd 3425523d337ca3d85f23b912c098b0b6 *man/figures/README-unnamed-chunk-4-1.png 8a5ab043a606b307af89068246b7f967 *man/figures/README-unnamed-chunk-5-1.png 0920f6ee93789de8c475279701801c56 *man/figures/README-unnamed-chunk-6-1.png ebc218d2cedb2de3a0ac224ba680906a *man/figures/logo.png 52403f8f8e6b219ef257a4100e382212 *man/lalonde.Rd 639939933d8f24d72c3e607bbd93ce55 *man/macros/macros.Rd 825e7d920d8514cc954eac375a5c9792 *man/match.data.Rd 011dfe71ae45c32875e0003352f9278b *man/matchit.Rd 822d30fa3f10b9e01646420b6b5e5550 *man/method_cardinality.Rd e958d7cf17d227ce71f56586f505dfe8 *man/method_cem.Rd a738303fc1b23bf33077d45b78e8f5d1 *man/method_exact.Rd 2940a90270c73118da0ee6aee4446966 *man/method_full.Rd 58ec0fb0642f4d10008e523b0d3bca11 *man/method_genetic.Rd 3e791f37ae66d767f7500f4db8bf50cb *man/method_nearest.Rd 28204c230dda90341dcef0ef9f2a793d *man/method_optimal.Rd 22d8107f940559f096e582da99f518cb *man/method_subclass.Rd 79d9d2deba4e226f4f7d715dcec4861d *man/plot.matchit.Rd 211350e0e3d5e3b7dc45ab65d9256f80 *man/plot.summary.matchit.Rd 0daa94ab658b273033c8bc199d472fa0 *man/rbind.matchdata.Rd fe7c5ac196889b11e1c86c86ecb0927c *man/summary.matchit.Rd b432231ca9a5e6fdd90632cca685d619 *src/RcppExports.cpp 7fc781b1e4b863ec58fbe571e0581d11 *src/internal.cpp bb42db55f58f51bca6639c8d8c52bdb4 *src/internal.h 478604c0d95be32aa5fcc82fc704bdff *src/nn_matchC.cpp 87ab25d47c5914441effe3f3ad9b8b1e *src/pairdistC.cpp 9af6c99cb065d1c45625b1392c351f90 *src/subclass2mm.cpp 56ee747fd113623d8f1226795d25f45a *src/tabulateC.cpp 385f6bba27cbce19ac7255b7f8496a3e *src/weights_matrixC.cpp 5c5f58f3a1a1b1bebbb417a9ad4b5662 *vignettes/MatchIt.Rmd 8687630460a299fef26d8500e78f7028 *vignettes/assessing-balance.Rmd 5c8742e737ffe1c916ed24f668f33ac3 *vignettes/estimating-effects.Rmd f4a15c7ff0715de09ac815570047b01e *vignettes/matching-methods.Rmd 192b38c117a27e2359f1b5de816fec16 *vignettes/references.bib 6cc4205e870f60d349159dab0151f57b *vignettes/sampling-weights.Rmd MatchIt/inst/0000755000176200001440000000000014170752616012571 5ustar liggesusersMatchIt/inst/doc/0000755000176200001440000000000014170752616013336 5ustar liggesusersMatchIt/inst/doc/assessing-balance.Rmd0000644000176200001440000010140214142761517017361 0ustar liggesusers--- title: "Assessing Balance" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Assessing Balance} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE, message = FALSE, fig.width=7, fig.height=5) options(width = 200, digits = 4) ``` ```{=html} ``` ## Introduction Covariate balance is the degree to which the distribution of covariates is similar across levels of the treatment. It has three main roles in causal effect estimation using matching: 1) as a target to optimize with matching, 2) as a method of assessing the quality of the resulting matches, and 3) as evidence to an audience that the estimated effect is close to the true effect. When covariate balance is achieved, the resulting effect estimate is less sensitive to model misspecification and ideally close to true treatment effect. The benefit of randomization is that covariate balance is achieved automatically (in expectation), which is why unadjusted effects estimated from randomized trial data (in the absence of drop-out) can be validly interpreted as causal effects. When using matching to recover causal effect estimates form observational data, balance is not guaranteed and must be assessed. This document provides instructions for assessing and reporting covariate balance as part of a matching analysis. The tools available in `MatchIt` for balance assessment should be used during the process of selecting a good matching scheme and ensuring that the chosen scheme is adequate. These tools implement the recommendations of @ho2007 and others for assessing balance. In addition to the tools available in `matchIt`, the `cobalt` package has a suite of functions designed to assess and display balance and is directly compatible with `MatchIt` objects. `cobalt` has extensive documentation, but we describe some of its functionality here as a complement to the tools in `MatchIt`. The structure of this document is as follows: first, we describe some of the recommendations for balance checking and their rationale; next, we describe the tools for assessing balance present in `MatchIt` and display their use in evaluating several matching schemes; finally; we briefly describe some of the functionality in `cobalt` to extend that in `MatchIt`. ## Recommendations for Balance Assessment Assessing balance involves assessing whether the distributions of covariates are similar between the treated and control groups. Balance is typically assessed by examining univariate balance summary statistics for each covariate, though more complicated methods exist for assessing joint distributional balance as well. Visual depictions of distributional balance can be a helpful complement to numerical summaries, especially for hard to balance and prognostically important covariates. Many recommendations for balance assessment have been described in the methodological literature. Unfortunately, there is no single best way to assess balance or to weigh balance summary statistics because the degree and form of balance that will yield the least bias in an effect estimate depends on unknown qualities of the outcome data-generating model. Nonetheless, there are a number of valuable recommendations that can be implemented to ensure matching is successful at eliminating or reducing bias. We review some of these here. Common recommendations for assessing balance include the following: - **Standardized mean differences**. The standardized mean difference (SMD) is the difference in the means of each covariate between treatment groups standardized by a standardization factor so that it is on the same scale for all covariates. The standardization factor is typically the standard deviation of the covariate in the treated group when targeting the ATT or the pooled standard deviation across both groups when targeting the ATE. The standardization factor should be the same before and after matching to ensure changes in the mean difference are not confounded by changes in the standard deviation of the covariate. SMDs close to zero indicate good balance. Several recommended thresholds have been published in the literature; we recommend .1 and .05 for prognostically important covariates. Higher values may be acceptable when using covariate adjustment in the matched sample. In addition to computing SMDs on the covariates themselves, it is important to compute them on squares, cubes, and higher exponents as well as interactions between covariates. Several empirical studies have examined the appropriateness for using SMDs in balance assessment, including @belitser2011, @ali2014, and @stuart2013; in general, there is often a high correlation between the mean or maximum absolute SMD and the degree of bias in the treatment effect. - **Variance Ratios**. The variance ratio is the ratio of the variance of a covariate in one group to that in the other. Variance ratios close to 1 indicate good balance because they imply the variances of the samples are similar [@austin2009]. - **Empirical CDF Statistics**. Statistics related to the difference in the empirical cumulative density functions (eCDFs) of each covariate between groups allow assessment of imbalance across the entire covariate distribution of that covariate rather than just its mean or variance. The maximum eCDF difference, also known as the Kolmogorov-Smirnov statistic, is sometimes recommended as a useful supplement to SMDs for assessing balance [@austin2015] and is often used as a criterion to use in propensity score methods that attempt to optimize balance [e.g., @mccaffrey2004; @diamond2013]. Although the mean eCDF difference has not been as well studied, it provides a summary of imbalance that may be missed by relying solely on the maximum difference. - **Visual Diagnostics**. Visual diagnostics such as eCDF plots, empirical quantile-quantile (eQQ) plots, and kernel density plots can be used to see exactly how the covariate distributions differ from each other, i.e., where in the distribution the greatest imbalances are [@ho2007; @austin2009]. This can help to figure out how to tailor a matching method to target imbalance in a specific region of the covariate distribution. - **Prognostic scores**. The prognostic score is an estimate of the potential outcome under control for each unit [@hansen2008]. Balance on the prognostic score has been shown to be highly correlated with bias in the effect estimate, making it a useful tool in balance assessment [@stuart2013]. Estimating the prognostic score requires having access to the outcome data, and using it may be seen as violating the principle of separating the design and analysis stages of a matching analysis [@rubin2001]. However, because only the outcome values from the control group are required to use the prognostic score, some separation is maintained. Several multivariate statistics exist that summarize balance across the entire joint covariate distribution. These can be functions of the above measures, like the mean or maximum absolute SMD or the generalized weighted distance [GWD; @franklin2014], which is the sum of SMDs for the covariates and their squares and interactions, or separate statistics that measure quantities that abstract away from the distribution of individual covariates, like the L1 distance [@iacus2011], cross-match test [@heller2010], or energy distance [@huling2020]. Balance on the propensity score has often been considered a useful measure of balance, but we do not necessarily recommend it except as a supplement to balance on the covariates. Propensity score balance will generally be good with any matching method regardless of the covariate balancing potential of the propensity score, so a balanced propensity score does not imply balanced covariates [@austin2009]. Similarly, it may happen that covariates may be well balanced even if the propensity score is not balanced, such as when covariates are prioritized above the propensity score in the matching specification (e.g., with genetic matching). Given these observations, the propensity score should not be relied upon for assessing covariate balance. Simulation studies by @stuart2013 provide evidence for this recommendation against relying on propensity score balance. There has been some debate about the use of hypothesis tests, such as t-tests or Kolmogorov-Smirnov tests, for assessing covariate balance. The idea is that balance tests test the null hypothesis that the matched sample has equivalent balance to a randomized experiment. There are several problems with balance tests, described by @ho2007 and @imai2008: 1) balance is a property of the sample, not a of a population from which the sample was drawn; 2) the power of balance tests depends on the sample size, which changes during matching even if balance does not change; and 3) the use of hypothesis tests implies a uniform decision criterion for rejecting the null hypothesis (e.g., p-value less than .05, potentially with corrections for multiple comparisons), when balance should be improved without limit. `MatchIt` does not report any balance tests or p-values, instead relying on the descriptive statistics described above. ## Recommendations for Balance Reporting A variety of methods should be used when assessing balance to try to find an optimal matched set that will ideally yield a low-error estimate of the desired effect. However, reporting every balance statistic or plot in a research report or publication can be burdensome and unnecessary. That said, it is critical to report balance to demonstrate to readers that the resulting estimate is approximately unbiased and relies little on extrapolation or correct outcome model specification. We recommend the following in reporting balance in a matching analysis: - Report SMDs before and after matching for each covariate, any prognostically important interactions between covariates, and the prognostic score; this can be reported in a table or in a Love plot. - Report summaries of balance for other statistics, e.g., the largest mean and maximum eCDF difference among the covariates and the largest SMD among squares, cubes, and interactions of the covariates. `MatchIt` provides tools for calculating each of these statistics so they can be reported with ease in a manuscript or report. ## Assessing Balance with `MatchIt` `MatchIt` contains several tools to assess balance numerically and graphically. The primary balance assessment function is `summary.matchit()`, which is called when using `summary()` on a `MatchIt` object and produces several tables of balance statistics before and after matching. `plot.summary.matchit()` generates a Love plot using R's base graphics system containing the standardized mean differences resulting from a call to `summary.matchit()` and provides a nice way to display balance visually for inclusion in an article or report. `plot.matchit()` generates several plots that display different elements of covariate balance, including propensity score overlap and distribution plots of the covariates. These functions together form a suite that can be used to assess and report balance in a variety of ways. To demonstrate `MatchIt`'s balance assessment capabilities, we will use the Lalonde data included in `MatchIt` and used in `vignette("MatchIt")`. We will perform full matching on the propensity score, though the functionality is identical across all matching methods except propensity score subclassification, which we illustrate at the end. ```{r} library("MatchIt") data("lalonde", package = "MatchIt") #Full matching on a logistic regression PS m.out <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "full") m.out ``` ### `summary.matchit()` When `summary()` is called on a `matchit` object, several tables of information are displayed. These include balance statistics for each covariate before matching, balance statistics for each covariate after matching, the percent reduction in imbalance after matching, and the sample sizes before and after matching. `summary.matchit()` has four additional arguments that control how balance is computed: - `interactions` controls whether balance statistics for all squares and pairwise interactions of covariates are to be displayed in addition to the covariates. The default is `FALSE`, and setting to `TRUE` can make the output massive when many covariates are present, but it is important to ensure no important interactions remain imbalanced. - `addlvariables` allows for balance to be assessed on variables other than those inside the `matchit` object. For example, if the distance between units only relied on a subset of covariates but balance needed to be achieved on all covariates, `addlvariables` could be used to supply these additional covariates. In addition to adding other variables, `addlvariables` can be used to request balance on specific functions of the covariates already in the `matchit` object, such as polynomial terms or interactions. The input to `addlvariables` can be a one-sided formula with the covariates and any desired transformations thereof on the right hand side, just like a model formula (e.g., `addlvariables = ~ X1 + X2 + I(X1^2)` would request balance on `X1`, `X2`, and the square of `X1`). Additional variables supplied to `addlvariables` but not present in the `matchit` object can be supplied as a data frame using the `data` argument. - `standardize` controls whether standardized or unstandardized statistics are to displayed. Standardized statistics include the standardized mean difference and eCDF statistics; unstandardized statistics include the raw difference in means and eQQ plot statistics. (Regardless, the variance ratio will always be displayed.). The default is `TRUE` for standardized statistics, which are more common to report because they are all on the same scale regardless of the scale of the covariates[^1]. - `pair.dist` controls whether within-pair distances should be computed and displayed. These reflect the average distance between units within the same pair, standardized or unstandardized according to the argument to `standardize`. The default is `TRUE`. With full matching, exact matching, coarsened exact matching, and propensity score subclassification, computing pair distances can take a long time, and so it may be beneficial to set to `FALSE` in these cases. [^1]: Note that versions of `MatchIt` before 4.0.0 had `standardize` set to `FALSE` by default. In addition, the arguments `un` and `improvement` control whether balance prior to matching should be displayed and whether the percent balance improvement after matching should be displayed. These can be set to `FALSE` to reduce the output. Below, we call `summary.matchit()` with `addlvariables` to display balance on covariates and a few functions of them in the matched sample. In particular, we request balance on the square of `age`, the variables representing whether `re74` and `re75` were equal to 0, and the interaction between `educ` and `race`. ```{r} summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + I(re75==0) + educ:race) ``` Let's examine the output in detail. The first table (`Summary of Balance for All Data`) provides balance in the sample prior to matching. The included statistics are the mean of the covariates in the treated group (`Means Treated`), the mean of the covariate in the control group (`Means Control`), the SMDs (`Std. Mean Diff.`), the variance ratio (`Var. Ratio`), the average distance between the eCDFs of the covariate across the groups (`eCDF Mean`), and the largest distance between the eCDFs (`eCDF Max`). Setting `un = FALSE` would have suppressed the creation of this table. The second table (`Summary of Balance for Matched Data`) contains all the same statistics in the matched sample. Because we implicitly request pair distance, an additional column for standardized pair distances (`Std. Pair Dist.`) is displayed. The third table (`Percent Balance Improvement`) contains the percent balance improvement for each covariate. This is computed as $100\frac{|\theta_M| - |\theta_U|}{|\theta_U|}$, where $\theta_M$ is a given balance statistic in the matched sample and $\theta_U$ is a the same balance statistic in the unmatched sample. Values between 0 and 100 indicate that balance improved after matching as measured by the statistic; values less than 0 indicate that balance got worse after matching. When balance is good on a covariate prior to matching, it can sometimes look like balance got a lot worse after matching even though the balance statistic is quite low, so these values should not be taken too seriously and should be used primarily as heuristics. Setting `un = FALSE` or `improvement = FALSE` would have suppressed the creation of this table. The final table (`Sample Sizes`) contains the sizes of the samples before (`All`) and after (`Matched`) matching, as well as the number of units left unmatched (`Unmatched`) and the number of units dropped due to a common support restriction (`Discarded`). The SMDs are computed as the difference mean divided by a standardization factor computed in the **unmatched** sample. An absolute SMD close to 0 indicates good balance; although a number of recommendations for acceptable values have appeared in the literature, we recommend absolute values less than .1 and less than .05 for potentially prognostically important variables. The variance ratios are computed as the ratio of the variance of the treated group to that of the control group for each covariate. Variance ratios are not computed for binary covariates because they are a function of the prevalence in each group, which is captured in the mean difference and eCDF statistics. A variance ratio close to 1 indicates good balance; a commonly used recommendation is for variance ratios to be between .5 and 2. The eCDF statistics correspond to the difference in the overall distributions of the covariates between the treatment groups. The values of both statistics range from 0 to 1, with values closer to zero indicating better balance. There are no specific recommendations for the values these statistics should take, though notably high values may indicate imbalance on higher moments of the covariates. The eQQ statistics produced when `standardize = FALSE` are interpreted similarly but are on the scale of the covariate. All these statistics should be considered together. Imbalance as measured by any of them may indicate a potential failure of the matching scheme to achieve distributional balance. ### `plot.summary.matchit()` A Love plot is a clean way to visually summarize balance. Using `plot` on the output of a call to `summary()` on a `matchit` object produces a Love plot of the standardized mean differences. `plot.summary.matchit()` has several additional arguments that can be used to customize the plot. - `abs` controls whether standardized mean difference should be displayed in absolute value or not. Default is `TRUE`. - `var.order` controls how the variables are ordered on the y-axis. The options are `"data"` (the default), which orders the variables as they appear the in the `summary.matchit()` output; `"unmatched"`, which orders the variables based on their standardized mean differences before matching; `"matched"`, which orders the variables based on their standardized mean differences after matching; and `"alphabetical"`, which orders the variables alphabetically. Using `"unmatched"` tends to result in attractive plots and ensures the legend doesn't overlap with points in its default position. - `threshold` controls where vertical lines indicating chosen thresholds should appear on the x-axis. Should be a numeric vector. The default is `c(.1, .05)`, which display vertical lines at .1 and .05 standardized mean difference units. - `position` controls the position of the legend. The default is `"bottomright"`, which puts the legend in the bottom right corner of the plot, and any keyword value available to supplied to `x` in `legend()` is allowed. Below we create a Love plot of the covariates. ```{r} m.sum <- summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + I(re75==0) + educ:race) plot(m.sum, var.order = "unmatched") ``` From this plot it is clear to see that balance was quite poor prior to matching, but full matching improved balance on all covariates, and most within a threshold of .1. To make the variable names cleaner, the original variables should be renamed prior to matching. `cobalt` provides many additional options to generate and customize Love plots using the `love.plot()` function and should be used if a plot beyond what is available with `plot.summary.matchit()` is desired. ### `plot.matchit()` In addition to numeric summaries of balance, `MatchIt` offers graphical summaries as well using `plot.matchit()` (i.e., using `plot()` on a `matchit` object). We can create eQQ plots, eCDF plots, or density plots of the covariates and histograms or jitter plots of the propensity score. The covariate plots can provide a summary of the balance of the full marginal distribution of a covariate beyond just the mean and variance. `plot.matchit()` has a few arguments to customize the output: - `type` corresponds to the type of plot desired. Options include `"qq"` for eQQ plots (the default), `"ecdf"` for eCDF plots, `"density"` for density plots, `"jitter"` for jitter plots, and `"histogram"` for histograms. - `interactive` controls whether the plot is interactive or not. For eQQ, eCDF, and density plots, this allows us to control when the next page of covariates is to be displayed since only three can appear at a time. For jitter plots, this can allow us to select individual units with extreme values for further inspection. The default is `TRUE`. - `which.xs` is used to specify for which covariates to display balance in eQQ, eCDF, and density plots. The default is to display balance on all, but we can request balance just on a specific subset. If three or fewer are requested, `interactive` is ignored. Below, we demonstrate the eQQ plot: ```{r} #eQQ plot plot(m.out, type = "qq", which.xs = c("age", "nodegree", "re74")) ``` The y-axis displays the each value of the covariate for the treated units, and the x-axis displays the the value of the covariate at the corresponding quantile in the control group. When values fall on the 45 degree line, the groups are balanced. Above, we can see that `age` remains somewhat imbalanced, but `nodegree` and `re74` have much better balance after matching than before. The difference between the x and y values of each point are used to compute the eQQ difference statistics that are displayed in `summary.matchit()` with `standardize = FALSE`. Below, we demonstrate the eCDF plot: ```{r} #eCDF plot plot(m.out, type = "ecdf", which.xs = c("educ", "married", "re75")) ``` The x-axis display the covariate values and the y-axis displays the proportion of the sample at or less than that covariate value. Perfectly overlapping lines indicate good balance. The black line corresponds to the treated group and the gray line to the control group. Although `educ` and `re75` were fairly well balanced before matching, their balance has improved nonetheless. `married` appears far better balanced after matching than before. The vertical difference between the eCDFs lines of each treatment group is used to compute the eCDF difference statistics that are displayed in `summary.matchit()` with `standardize = TRUE`. Below, we demonstrate the density plot: ```{r} #density plot plot(m.out, type = "density", which.xs = c("age", "educ", "married")) ``` The x-axis display the covariate values and the y-axis displays the density of the sample at that covariate value. For binary variables, the y-axis displays the proportion of the sample at that covariate value. Perfectly overlapping lines indicate good balance. The black line corresponds to the treated group and the gray line to the control group. Density plots display similar information to eCDF plots but may be more intuitive for some users because of their link to histograms. ## Assessing Balance After Subclassification With subclassification, balance can be checked both within each subclass and overall. With `summary.matchit()`, we can request to view balance only in aggregate or in each subclass. The latter can help us decide if we can interpret effects estimated within each subclass as unbiased. The `plot.summary.matchit()` and `plot.matchit()` outputs can be requested either in aggregate or for each subclass. We demonstrate this below. First we will perform propensity score subclassification using 4 subclasses (typically more is beneficial). ```{r} #Subclassification on a logistic regression PS s.out <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "subclass", subclass = 4) s.out ``` When using `summary()`, the default is to display balance only in aggregate using the subclassification weights. This balance output looks similar to that for other matching methods. ```{r} summary(s.out) ``` An additional option in `summary()`, `subclass`, allows us to request balance for individual subclasses. `subclass` can be set to `TRUE` to display balance for all subclasses or the indices of individual subclasses for which balance is to be displayed. Below we call `summary()` and request balance to be displayed on all subclasses (setting `un = FALSE` to suppress balance in the original sample): ```{r} summary(s.out, subclass = TRUE, un = FALSE) ``` We can plot the standardized mean differences in a Love plot that also displays balance for the subclasses using `plot.summary.matchit()` on a `summary.matchit()` object with `subclass = TRUE`. ```{r} s <- summary(s.out, subclass = TRUE) plot(s, var.order = "unmatched", abs = FALSE) ``` Note that for some variables, while the groups are balanced in aggregate (black dots), the individual subclasses (gray numbers) may not be balanced, in which case unadjusted effect estimates within these subclasses should not be interpreted as unbiased. When we plot distributional balance using `plot.matchit()`, again we can choose whether balance should be displayed in aggregate or within subclasses again using the `subclass` option, which functions the same as it does with `summary.matchit()`. Below we demonstrate checking balance within a subclass. ```{r} plot(s.out, type = "density", which.xs = c("educ", "married", "re75"), subclass = 1) ``` If we had set `subclass = FALSE`, plots would have been displayed in aggregate using the subclassification weights. If `subclass` is unspecified, a prompt will ask us for which subclass we want to see balance. ## Assessing Balance with `cobalt` The `cobalt` package was designed specifically for checking balance before and after matching (and weighting). It offers three main functions, `bal.tab()`, `love.plot()`, and `bal.plot()`, which perform similar actions to `summary.matchit()`, `plot.summary.matchit()`, and `plot.matchit()`, respectively. These functions directly interface with `matchit` objects, making `cobalt` straightforward to use in conjunction with `MatchIt`. `cobalt` can be used as a complement to `MatchIt`, especially for more advanced uses that are not accommodated by `MatchIt`, such as comparing balance across different matching schemes and even different packages, assessing balance in clustered or multiply imputed data, and assessing balance with multi-category, continuous, and time-varying treatments. The main `cobalt` vignette contains many examples of its use with `MatchIt` objects, so we only provide a short demonstration of its capabilities here. ```{r, message = F} library("cobalt") ``` ### `bal.tab()` `bal.tab()` produces tables of balance statistics similar to `summary.matchit()`. The columns displayed can be customized to limit how much information is displayed and isolate desired information. We call `bal.tab()` with a few of its options specified below: ```{r} bal.tab(m.out, un = TRUE, stats = c("m", "v", "ks")) ``` The output is very similar to that of `summary.matchit()`, except that the balance statistics computed before matching (with the suffix `.Un`) and those computed after matching (with the suffix `.Adj`) are in the same table. By default, only SMDs after matching (`Diff.Adj`) are displayed; by setting `un = TRUE`, we requested that the balance statistics before matching also be displayed, and by setting `stats = c("m", "v", "ks")` we requested mean differences, variance ratios, and Kolmogorov-Smirnov statistics. Other balance statistics and summary statistics can be requested as well. One important detail to note is that the default for binary covariates is to print the raw difference in proportion rather than the standardized mean difference, so there will be an apparent discrepancy for these variables between `bal.tab()` and `summary.matchit()` output, though this behavior can be changed by setting `binary = "std"` in the call to `bal.tab()`. Functionality for producing balance statistics for additional variables and for powers and interactions of the covariates is available using the `addl`, `poly`, and `int` options. `bal.tab()` and other `cobalt` functions can produce balance not just on a single `matchit` object but on several at the same time, which facilitates comparing balance across several matching specifications. For example, if we wanted to compare the full matching results to the results of nearest neighbor matching without replacement, we could supply both to `bal.tab()`, which we demonstrate below: ```{r} #Nearest neighbor (NN) matching on the PS m.out2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde) #Balance on covariates after full and NN matching bal.tab(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, un = TRUE, weights = list(full = m.out, nn = m.out2)) ``` This time, we supplied `bal.tab()` with the covariates and dataset and supplied the `matchit` output objects in the `weights` argument (which extracts the matching weights from the objects). Here we can see that full matching yields better balance than nearest neighbor matching overall, though balance is slightly worse for `age` and `maried` and the effective sample size is lower. ### `love.plot` `love.plot()` creates a Love plot of chosen balance statistics. It offers many options for customization, including the shape and colors of the points, how the variable names are displayed, and for which statistics balance is to be displayed. Below is an example of its basic use: ```{r} love.plot(m.out, binary = "std") ``` The syntax is straightforward and similar to that of `bal.tab()`. Below we demonstrate a more advanced use that customizes the appearance of the plot and displays balance not only on mean differences but also on Kolmogorov-Smirnov statistics and for both full matching and nearest neighbor matching simultaneously. ```{r, fig.width=7} love.plot(m.out, stats = c("m", "ks"), poly = 2, abs = TRUE, weights = list(nn = m.out2), drop.distance = TRUE, thresholds = c(m = .1), var.order = "unadjusted", binary = "std", shapes = c("triangle", "square", "circle"), colors = c("blue", "darkgreen", "red"), sample.names = c("Full Matching", "NN Matching", "Original"), position = "bottom") ``` The `love.plot()` documentation explains what each of these arguments do and the several other ones available. `cobalt` contains a vignette for other advanced customization of `love.plot()`. ### `bal.plot()` `bal.plot()` displays distributional balance for a single covariate, similar to `plot.matchit()`. Its default is to display kernel density plots for continuous variables and bar graphs for categorical variables. It can also display eCDF plots and histograms. Below we demonstrate some of its uses: ```{r} #Density plot for continuous variables bal.plot(m.out, var.name = "educ", which = "both") #Bar graph for categorical variables bal.plot(m.out, var.name = "race", which = "both") #Mirrored histogram bal.plot(m.out, var.name = "distance", which = "both", type = "histogram", mirror = TRUE) ``` These plots help illuminate the specific ways in which the covariate distributions differ between treatment groups, which can aid in interpreting the balance statistics provided by `bal.tab()` and `summary.matchit()`. ## Conclusion The goal of matching is to achieve covariate balance, similarity between the covariate distributions of the treated and control groups. Balance should be assessed during the matching phase to find a matching specification that works. Balance must also be reported in the write-up of a matching analysis to demonstrate to readers that matching was successful. `MatchIt` and `cobalt` each offer a suite of functions to implement best practices in balance assessment and reporting. ## References MatchIt/inst/doc/MatchIt.Rmd0000644000176200001440000006136314145073537015344 0ustar liggesusers--- title: 'MatchIt: Getting Started' author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: yes vignette: | %\VignetteIndexEntry{MatchIt: Getting Started} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE, message = FALSE, fig.width=7, fig.height=5) options(width = 200) ``` ```{=html} ``` ## Introduction `MatchIt` implements the suggestions of @ho2007 for improving parametric statistical models for estimating treatment effects in observational studies and reducing model dependence by preprocessing data with semi-parametric and non-parametric matching methods. After appropriately preprocessing with `MatchIt`, researchers can use whatever parametric model they would have used without `MatchIt` and produce inferences that are more robust and less sensitive to modeling assumptions. `MatchIt` reduces the dependence of causal inferences on commonly made, but hard-to-justify, statistical modeling assumptions using a large range of sophisticated matching methods. The package includes several popular approaches to matching and provides access to methods implemented in other packages through its single, unified, and easy-to-use interface. Matching is used in the context of estimating the causal effect of a binary treatment or exposure on an outcome while controlling for measured pre-treatment variables, typically confounding variables or variables prognostic of the outcome. Here and throughout the `MatchIt` documentation we use the word "treatment" to refer to the focal causal variable of interest, with "treated" and "control" reflecting the names of the treatment groups. The goal of matching is to produce *covariate balance*, that is, for the distributions of covariates in the two groups to be approximately equal to each other, as they would be in a successful randomized experiment. The importance of covariate balance is that it allows for increased robustness to the choice of model used to estimate the treatment effect; in perfectly balanced samples, a simple difference in means can be a valid treatment effect estimate. Here we do not aim to provide a full introduction to matching or causal inference theory, but simply to explain how to use `MatchIt` to perform nonparametric preprocessing. For excellent and accessible introductions to matching, see @stuart2010 and Austin @austin2011b. A matching analysis involves four primary steps: 1) planning, 2) matching, 3) assessing the quality of matches, and 4) estimating the treatment effect and its uncertainty. Here we briefly discuss these steps and how they can be implemented with `MatchIt`; in the other included vignettes, these steps are discussed in more detail. We will use Lalonde's data on the evaluation of the National Supported Work program to demonstrate `MatchIt`'s capabilities. First, we load `MatchIt` and bring in the `lalonde` dataset. ```{r} library("MatchIt") data("lalonde") head(lalonde) ``` The statistical quantity of interest is the causal effect of the treatment (`treat`) on 1978 earnings (`re78`). The other variables are pre-treatment covariates. See `?lalonde` for more information on this dataset. In particular, the analysis is concerned with the marginal, total effect of the treatment for those who actually received the treatment. In what follows, we briefly describe the four steps of a matching analysis and how to implement them in `MatchIt`. For more details, we recommend reading the other vignettes, `vignette("matching-methods")`, `vignette("assessing-balance")`, and `vignette("estimating-effects")`, especially for users less familiar with matching methods. For the use of `MatchIt` with sampling weights, also see `vignette("sampling-weights")`. It is important to recognize that the ease of using `MatchIt` does not imply the simplicity of matching methods; advanced statistical methods like matching that require many decisions to be made and caution in their use should only be performed by those with statistical training. ## Planning The planning phase of a matching analysis involves selecting the type of effect to be estimated, selecting the target population to which the treatment effect is to generalize, and selecting the covariates for which balance is required for an unbiased estimate of the treatment effect. Each of these are theoretical steps that do not involve performing analyses on the data. Ideally, they should be considered prior to data collection in the planning stage of a study. Thinking about them early can aid in performing a complete and cost-effective analysis. **Selecting the type of effect to be estimated.** There are a few different types of effects to be estimated. In the presence of mediating variables, one might be interested in the direct effect of the treatment that does not pass through the mediating variables or the total effect of the treatment across all causal pathways. Matching is well suited for estimating total effects, and specific mediation methods may be better suited for other mediation-related quantities. One may be interested in a conditional effect or a marginal effect. A conditional effect is the effect of a treatment within some strata of other prognostic variables (e.g., at the patient level), and a marginal effect is the average effect of a treatment in a population (e.g., for implementing a broad policy change). Different types of matching are well suited for each of these, but the most common forms are best used for estimating marginal treatment effects; for conditional treatment effects, typically modeling assumptions are required or matching must be done within strata of the conditioning variables. Matching can reduce the reliance on correct model specification for conditional effects. **Selecting a target population.** The target population is the population to which the effect estimate is to generalize. Typically, an effect estimated in a sample generalizes to the population from which the sample is a probability sample. If the sample is not a probability sample from any population (e.g., it is a convenience sample or involves patients from an arbitrary hospital), the target population can be unclear. Often, the target population is a group of units who are eligible for the treatment (or a subset thereof). Causal estimands are defined by the target population to which they generalize. The average treatment effect in the population (ATE) is the average effect of the treatment for all units in the target population. The average treatment effect in the treated (ATT) is the average effect of the treatment for units like those who actually were treated. The most common forms of matching are best suited for estimating the ATT, though some are also available for estimating the ATE. Some matching methods distort the sample in such a way that the estimated treatment effect corresponds neither to the ATE nor to the ATT, but rather to the effect in an unspecified population (sometimes called the ATM, or average treatment effect in the remaining matched sample). When the target population is not so important (e.g., in the case of treatment effect discovery), such methods may be attractive; otherwise, care should be taken in ensuring the effect generalizes to the target population of interest. Different matching methods allow for different target populations, so it is important to choose a matching method that allows one to estimate the desired effect. See @greiferChoosingEstimandWhen2021 for guidance on making this choice. **Selecting covariates to balance.** Selecting covariates carefully is critical for ensuring the resulting treatment effect estimate is free of confounding and can be validly interpreted as a causal effect. To estimate total causal effects, all covariates must be measured prior to treatment (or otherwise not be affected by the treatment). Covariates should be those that cause variation in the outcome and selection into treatment group; these are known as confounding variables. See @vanderweele2019 for a guide on covariate selection. Ideally these covariates are measured without error and are free of missingness. ## Check Initial Imbalance After planning and prior to matching, it can be a good idea to view the initial imbalance in one's data that matching is attempting to eliminate. We can do this using the code below: ```{r} # No matching; constructing a pre-match matchit object m.out0 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = NULL, distance = "glm") ``` The first argument is a `formula` relating the treatment to the covariates used in estimating the propensity score and for which balance is to be assessed. The `data` argument specifies the dataset where these variables exist. Typically, the `method` argument specifies the method of matching to be performed; here, we set it to `NULL` so we can assess balance prior to matching[^1]. The `distance` argument specifies the method for estimating the propensity score, a one-dimensional summary of all the included covariates, computed as the predicted probability of being the treated group given the covariates; here, we set it to `"glm"` for generalized linear model, which implements logistic regression by default[^2] (see `?distance` for other options). [^1]: Note that the default for `method` is `"nearest"` to perform nearest neighbor matching. To prevent any matching from taking place in order to assess pre-matching imbalance, `method` must be set to `NULL`. [^2]: Note that setting `distance = "logit"`, which was the default in `MatchIt` version prior to 4.0.0, will also estimate logistic regression propensity scores. Because it is the default, the `distance` argument can actually be omitted if logistic regression propensity scores are desired. Below we assess balance on the unmatched data using `summary()`: ```{r} # Checking balance prior to matching summary(m.out0) ``` We can see severe imbalances as measured by the standardized mean differences (`Std. Mean Diff.`), variance ratios (`Var. Ratio`), and empirical cumulative density function (eCDF) statistics. Values of standardized mean differences and eCDF statistics close to zero and values of variance ratios close to one indicate good balance, and here many of them are far from their ideal values. ## Matching Now, matching can be performed. There are several different classes and methods of matching, described in `vignette("matching-methods")`. Here, we begin by briefly demonstrating 1:1 nearest neighbor (NN) matching on the propensity score, which is appropriate for estimating the ATT. One by one, each treated unit is paired with an available control unit that has the closest propensity score to it. Any remaining control units are left unmatched and excluded from further analysis. Due to the theoretical balancing properties of the propensity score described by @rosenbaum1983, propensity score matching can be an effective way to achieve covariate balance in the treatment groups. Below we demonstrate the use of `matchit()` to perform nearest neighbor propensity score matching. ```{r} # 1:1 NN PS matching w/o replacement m.out1 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "nearest", distance = "glm") ``` We use the same syntax as before, but this time specify `method = "nearest"` to implement nearest neighbor matching, again using a logistic regression propensity score. Many other arguments are available for tuning the matching method and method of propensity score estimation. The matching outputs are contained in the `m.out1` object. Printing this object gives a description of the type of matching performed: ```{r} m.out1 ``` The key components of the `m.out1` object are `weights` (the computed matching weights), `subclass` (matching pair membership), `distance` (the estimated propensity score), and `match.matrix` (which control units are matched to each treated unit). How these can be used for estimating the effect of the treatment after matching is detailed in `vignette("estimating-effects")`. ## Assessing the Quality of Matches Although matching on the propensity score is often effective at eliminating differences between the treatment groups to achieve covariate balance, its performance in this regard must be assessed. If covariates remain imbalanced after matching, the matching is considered unsuccessful, and a different matching specification should be tried. `MatchIt` offers a few tools for the assessment of covariate balance after matching. These include graphical and statistical methods. More detail on the interpretation of the included plots and statistics can be found in `vignette("assessing-balance")`. In addition to covariate balance, the quality of the match is determined by how many units remain after matching. Matching often involves discarding units that are not paired with other units, and some matching options, such as setting restrictions for common support or calipers, can further decrease the number of remaining units. If, after matching, the remaining sample size is small, the resulting effect estimate may be imprecise. In many cases, there will be a trade-off between balance and remaining sample size. How to optimally choose among them is an instance of the fundamental bias-variance trade-off problem that cannot be resolved without substantive knowledge of the phenomena under study. Prospective power analyses can be used to determine how small a sample can be before necessary precision is sacrificed. To assess the quality of the resulting matches numerically, we can use the `summary()` function on `m.out1` as before. Here we set `un = FALSE` to suppress display of the balance before matching for brevity and because we already saw it. (Leaving it as `TRUE`, its default, would display balance both before and after matching.) ```{r} # Checking balance after NN matching summary(m.out1, un = FALSE) ``` At the top is a summary of covariate balance after matching. Although balance has improved for some covariates, in general balance is still quite poor, indicating that nearest neighbor propensity score matching is not sufficient for removing confounding in this dataset. The final column, `Std. Pair Diff`, displays the average absolute within-pair difference of each covariate. When these values are small, better balance is typically achieved and estimated effects are more robust to misspecification of the outcome model [@king2019; @rubin1973a]. Next is a table of the sample sizes before and after matching. The matching procedure left 244 control units unmatched. Ideally, unmatched units would be those far from the treated units and would require greater extrapolation were they to have been retained. We can visualize the distribution of propensity scores of those who were matched using `plot()` with `type = "jitter"`: ```{r} plot(m.out1, type = "jitter", interactive = FALSE) ``` We can visually examine balance on the covariates using `plot()` with `type = "qq"`: ```{r} plot(m.out1, type = "qq", interactive = FALSE, which.xs = c("age", "married", "re75")) ``` Points far from the solid diagonal line are the areas of the covariate distributions that differ between the treatment groups. Although `married` and `re75` appear to have improved balance after matching, the case is mixed for `age`. ### Trying a Different Matching Specification Given the poor performance of nearest neighbor matching in this example, we can try a different matching method or make other changes to the matching algorithm or distance specification. Below, we'll try full matching, which matches every treated unit to at least one control and every control to at least one treated unit [@hansen2004; @stuart2008a]. We'll also try a different link (probit) for the propensity score model. ```{r} # Full matching on a probit PS m.out2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "full", distance = "glm", link = "probit") m.out2 ``` We can examine balance on this new matching specification. ```{r} # Checking balance after full matching summary(m.out2, un = FALSE) ``` Balance is far better, as determined by the lower standardized mean differences and eCDF statistics. The balance should be reported when publishing the results of a matching analysis. This can be done either in a table, using the values resulting from `summary()`, or in a plot, such as a Love plot, which we can make by calling `plot()` on the `summary()` output: ```{r} plot(summary(m.out2)) ``` Love plots are a simple and straightforward way to summarize balance visually. See `vignette("assessing-balance")` for more information on how to customize `MatchIt`'s Love plot and how to use `cobalt`, a package designed specifically for balance assessment and reporting that is compatible with `MatchIt`. ## Estimating the Treatment Effect How treatment effects are estimated depends on what form of matching was performed. See `vignette("estimating-effects")` for information on the variety of way to estimate effects and standard errors after each type of matching and for several outcome types. After 1:1 matching without replacement (i.e., the first matching specification above), we can run a simple regression of the outcome on the treatment in the matched sample (i.e., including the matching weights). With continuous outcomes, it is often a good idea to also include the covariates used in the matching in the effect estimation, as doing so can provide additional robustness to slight imbalances remaining after the matching and can improve precision. Even though the 1:1 matching was not successful, we'll demonstrate here how to estimate a treatment effect after performing such an analysis. First, we'll extract the matched dataset from the `matchit` object using `match.data()`. This dataset only contains the matched units and adds columns for `distance`, `weights`, and `subclass` (described previously). ```{r} m.data1 <- match.data(m.out1) head(m.data1) ``` We can then estimate a treatment effect in this dataset using the standard regression functions in R, like `lm()` or `glm()`, being sure to include the matching weights (stored in the `weights` variable of the `match.data()` output) in the estimation[^3]. We recommend using cluster-robust standard errors for most analyses, with pair membership as the clustering variable; the `lmtest` and `sandwich` packages together make this straightforward. [^3]: With 1:1 nearest neighbor matching without replacement, excluding the matching weights does not change the estimates. For all other forms of matching, they are required, so we recommend always including them for consistency. ```{r} library("lmtest") #coeftest library("sandwich") #vcovCL fit1 <- lm(re78 ~ treat + age + educ + race + married + nodegree + re74 + re75, data = m.data1, weights = weights) coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ``` The coefficient on `treat` is the estimated ATT. The other coefficients and tests should not be interpreted or reported. Estimating standard errors with matched data is an area of ongoing development. Generally, the approach demonstrated above works well for continuous outcomes. See `vignette("estimating-effects")` for more information on how to estimate standard errors with each type of matching and with different outcome types. A benefit of matching is that the outcome model used to estimate the treatment effect is robust to misspecification when balance has been achieved. With 1:1 nearest neighbor matching, we failed to achieve balance, so one should be cautious about trusting the estimated effect. With full matching, we were able to achieve balance, so the effect estimate should depend less on the form of the outcome model used. Below we estimate the effect and standard error of the treatment effect after full matching. As before, we'll use functions from the `lmtest` and `sandwich` packages here because they provide a fairly general interface to estimating coefficients and standard errors. ```{r, message = FALSE} m.data2 <- match.data(m.out2) fit2 <- lm(re78 ~ treat + age + educ + race + married + nodegree + re74 + re75, data = m.data2, weights = weights) coeftest(fit2, vcov. = vcovCL, cluster = ~subclass) ``` Given the results of these two estimates, we would be inclined to trust the one resulting from the second analysis, i.e., using full matching, because better balance was achieved on all the variables, making the effect estimate less sensitive to the form of the outcome model we used. Effect estimation with nonlinear models (e.g., for binary or time-to-event event outcomes) is more complicated due to noncollapsibility of the estimated effects; including additional covariates in the model can change the meaning of the estimated effect. See `vignette("estimating-effects")` for more details. Note that for some models, effect and standard error estimation is still being researched. ## Reporting Results To report matching results in a manuscript or research report, a few key pieces of information are required. One should be as detailed as possible about the matching procedure and the decisions made to ensure the analysis is replicable and can be adequately assessed for soundness by the audience. Key pieces of information to include are 1) the matching specification used (including the method and any additional options, like calipers or common support restrictions), 2) the distance measure used (including how it was estimated e.g., using logistic regression for propensity scores), 3) which other matching methods were tried prior to settling on a final specification and how the choices were made, 4) the balance of the final matching specification (including standardized mean differences and other balance statistics for the variables, their powers, and their interactions; some of these can be reported as summaries rather than in full detail), 5) the number of matched, unmatched, and discarded units included in the effect estimation, and 6) the method of estimating the treatment effect and standard error or confidence interval (including the specific model used and the specific type of standard error). See @thoemmes2011 for a complete list of specific details to report. Below is an example of how we might write up the prior analysis: > We used propensity score matching to estimate the average marginal effect of the treatment on 1978 earnings on those who received it accounting for confounding by the included covariates. We first attempted 1:1 nearest neighbor propensity score matching without replacement with a propensity score estimated using logistic regression of the treatment on the covariates. This matching yielded poor balance, so we instead tried full matching on the propensity score, which yielded adequate balance, as indicated in Table 1 and Figure 1. The propensity score was estimated using a probit regression of the treatment on the covariates, which yielded better balance than did a logistic regression. After matching, all standardized mean differences for the covariates were below 0.1 and all standardized mean differences for squares and two-way interactions between covariates were below .15, indicating adequate balance. Full matching uses all treated and all control units, so no units were discarded by the matching. > > To estimate the treatment effect and its standard error, we fit a linear regression model with 1978 earnings as the outcome and the treatment and the covariates as additive predictors and included the full matching weights in the estimation. The coefficient on the treatment was taken to be the estimate of the treatment effect. The `lm()` function was used to estimate the effect, and a cluster-robust variance as implemented in the `vcovCL()` function in the `sandwich` package was used to estimate its standard error with matching stratum membership as the clustering variable. > > The estimated effect was \$1980 (SE = 756.1, p = .009), indicating that the average effect of the treatment for those who received it is to increase earnings. ## Conclusion Although we have covered the basics of performing a matching analysis here, to use matching to its full potential, the more advanced methods available in `MatchIt` should be considered. We recommend reading the other vignettes included here to gain a better understand of all the `MatchIt` has to offer and how to use it responsibly and effectively. As previously stated, the ease of using `MatchIt` does not imply that matching or causal inference in general are simple matters; matching is an advanced statistical technique that should be used with care and caution. We hope the capabilities of `MatchIt` ease and encourage the use of nonparametric preprocessing for estimating causal effects in a robust and well-justified way. ## References MatchIt/inst/doc/matching-methods.Rmd0000644000176200001440000014032314145075073017235 0ustar liggesusers--- title: "Matching Methods" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Matching Methods} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) options(width = 200) ``` ## Introduction `MatchIt` implements several matching methods with a variety of options. Though the help pages for the individual methods describes each method and how they can be used, this vignette provides a broad overview of the available matching methods and their associated options. The choice of matching method depends on the goals of the analysis (e.g., the estimand, whether low bias or high precision is important) and the unique qualities of each dataset to be analyzed, so there is no single optimal choice for any given analysis. A benefit of nonparametric preprocessing through matching is that a number of matching methods can be tried and their quality assessed without consulting the outcome, reducing the possibility of capitalizing on chance while allowing for the benefits of an exploratory analysis in the design phase [@ho2007]. This vignette describes each matching method available in `MatchIt` and the various options that are allowed with matching methods and the consequences of their use. For a brief introduction to the use of `MatchIt` functions, see `vignette("MatchIt")`. For details on how to assess and report covariate balance, see `vignette("assessing-balance")`. For details on how to estimate treatment effects and standard errors after matching, see `vignette("estimating-effects")`. ## Matching Matching as implemented in `MatchIt` is a form of *subset selection*, that is, the pruning and weighting of units to arrive at a (weighted) subset of the units from the original dataset. Ideally, and if done successfully, subset selection produces a new sample where the treatment is unassociated with the covariates so that a comparison of the outcomes treatment and control groups is not confounded by the measured and balanced covariates. Although statistical estimation methods like regression can also be used to remove confounding due to measured covariates, @ho2007 argue that fitting regression models in matched samples reduces the dependence of the validity of the estimated treatment effect on the correct specification of the model. Matching is nonparametric in the sense that the estimated weights and pruning of the sample are not direct functions of estimated model parameters but rather depend on the organization of discrete units in the sample; this is in contrast to propensity score weighting (also known as inverse probability weighting), where the weights come more directly from the estimated propensity score model and therefore are more sensitive to its correct specification. These advantages, as well as the intuitive understanding of matching by the public compared to regression or weighting, make it a robust and effective way to estimate treatment effects. It is important to note that this implementation of matching differs from the methods described by Abadie and Imbens [-@abadie2006; -@abadie2016] and implemented in the `Matching` R package and `teffects` routine in Stata. That form of matching is *matching imputation*, where the missing potential outcomes for each unit are imputed using the observed outcomes of paired units. This is a critical distinction because matching imputation is a specific estimation method with its own effect and standard error estimators, in contrast to subset selection, which is a preprocessing method that does not require specific estimators and is broadly compatible with other parametric and nonparametric analyses. The benefits of matching imputation are that its theoretical properties (i.e., the rate of convergence and asymptotic variance of the estimator) are well understood, it can be used in a straightforward way to estimate not just the average treatment effect in the treated (ATT) but also the average treatment effect in the population (ATE), and additional effective matching methods can be used in the imputation (e.g., kernel matching). The benefits of matching as nonparametric preprocessing are that it is far more flexible with respect to the types of effects that can be estimated because it does not involve any specific estimator, its empirical and finite-sample performance has been examined in depth and is generally well understood, and it aligns well with the design of experiments, which are more familiar to non-technical audiences. In addition to subset selection, matching often (though not always) involves a form of *stratification*, the assignment of units to pairs or strata containing multiple units. The distinction between subset selection and stratification is described by @zubizarreta2014, who separate them into two separate steps. In `MatchIt`, with almost all matching methods, subset selection is performed by stratification; for example, treated units are paired with control units, and unpaired units are then dropped from the matched sample. With some methods, subclasses are used to assign matching or stratification weights to individual units, which increase or decrease each unit's leverage in a subsequent analysis. There has been some debate about the importance of stratification after subset selection; while some authors have argued that, with some forms of matching, pair membership is incidental [@stuart2008; @schafer2008], others have argued that correctly incorporating pair membership into effect estimation can improve the quality of inferences [@austin2014a; @wan2019]. For methods that allow it, `MatchIt` includes stratum membership as an additional output of each matching specification. How these strata can be used is detailed in `vignette("Estimating Effects")`. At the heart of `MatchIt` are three classes of methods: distance matching, stratum matching, and pure subset selection. *Distance matching* involves considering a focal group (usually the treated group) and selecting members of the non-focal group (i.e., the control group) to pair with each member of the focal group based on the *distance* between units, which can be computed in one of several ways. Members of either group that are not paired are dropped from the sample. Nearest neighbor (`method = "nearest"`), optimal pair (`method = "optimal"`), optimal full (`method = "full"`), and genetic matching (`method = "genetic"`) are the methods of distance matching implemented in `MatchIt`. Typically, only the average treatment in the treated (ATT) or average treatment in the control (ATC), if the control group is the focal group, can be estimated after distance matching in `MatchIt` (full matching is an exception, described later). *Stratum matching* involves creating strata based on unique values of the covariates and assigning units with those covariate values into those strata. Any units that are in strata that lack either treated or control units are then dropped from the sample. Strata can be formed using the raw covariates (`method = "exact"`), coarsened versions of the covariates (`method = "cem"`), or coarsened versions of the propensity score (`method = "subclass"`). When no units are discarded, either the ATT, ATC, or average treatment effect in the population (ATE) can be estimated after stratum matching, though often some units are discarded, especially with exact and coarsened exact matching, making the estimand less clear. For use in estimating marginal treatment effects after exact matching, stratification weights are computed for the matched units first by computing a new "stratum propensity score" for each unit, which is the proportion of treated units in its stratum. The formulas for computing inverse probability weights from standard propensity scores are then applied to the new stratum propensity scores to form the new weights. Pure subset selection involves selecting a subset of units form the original sample without considering the distance between individual units or strata that units might fall into. Subsets are selected to optimize a criterion subject to constraint on balance and remaining sample size. Cardinality and template matching (`method = "cardinality"`) are the methods of pure subset selection implemented in `MatchIt`. Both methods allow the user to specify the largest imbalance allowed in the resulting matched sample, and an optimization routine attempts to find the largest matched sample that satisfies those balance constraints. While cardinality matching does not target a specific estimand, template matching can be used to target the ATT, ATC, or ATE. Below, we describe each of the matching methods implemented in `MatchIt`. ## Matching Methods ### Nearest Neighbor Matching (`method = "nearest"`) Nearest neighbor matching is also known as greedy matching. It involves running through the list of treated units and selecting the closest eligible control unit to be paired with each treated unit. It is greedy in the sense that each pairing occurs without reference to how other units will be or have been paired, and therefore does not aim to optimize any criterion. Nearest neighbor matching is the most common form of matching used [@thoemmes2011; @zakrison2018] and has been extensively studied through simulations. See `?method_nearest` for the documentation for `matchit()` with `method = "nearest"`. Nearest neighbor matching requires the specification of a distance measure to define which control unit is closest to each treated unit. The default and most common distance is the *propensity score difference*, which is the difference between the propensity scores of each treated and control unit [@stuart2010]. Another popular distance is the Mahalanobis distance, described in the section "Mahalanobis distance matching" below. The order in which the treated units are to be paired must also be specified and has the potential to change the quality of the matches [@austin2013b; @rubin1973]; this is specified by the `m.order` argument. With propensity score matching, the default is to go in descending order from the highest propensity score; doing so allows the units that would have the hardest time finding close matches to be matched first [@rubin1973]. Other orderings are possible, including random ordering, which can be tried multiple times until an adequate matched sample is found. When matching with replacement (i.e., where each control unit can be reused to be matched with any number of treated units), the matching order doesn't matter. When using a matching ratio greater than 1 (i.e., when more than 1 control units are requested to be matched to each treated unit), matching occurs in a cycle, where each treated unit is first paired with one control unit, and then each treated unit is paired with a second control unit, etc. Ties are broken deterministically based on the order of the units in the dataset to ensure that multiple runs of the same specification yield the same result (unless the matching order is requested to be random). ### Optimal Pair Matching (`method = "optimal"`) Optimal pair matching (often just called optimal matching) is very similar to nearest neighbor matching in that it attempts to pair each treated unit with one or more control units. Unlike nearest neighbor matching, however, it is "optimal" rather than greedy; it is optimal in the sense that it attempts to choose matches that collectively optimize an overall criterion [@hansen2006; @gu1993]. The criterion used is the sum of the absolute pair distances in the matched sample. See `?method_optimal` for the documentation for `matchit()` with `method = "optimal"`. Optimal pair matching in `MatchIt` depends on the `fullmatch()` function in the `optmatch` package [@hansen2006]. Like nearest neighbor matching, optimal pair matching requires the specification of a distance measure between units. Optimal pair matching can be thought of simply as an alternative to selecting the order of the matching for nearest neighbor matching. Optimal pair matching and nearest neighbor matching often yield the same or very similar matched samples; indeed, some research has indicated that optimal pair matching is not much better than nearest neighbor matching at yielding balanced matched samples [@austin2013b]. The `tol` argument in `fullmatch()` can be supplied to `matchit()` with `method = "optimal"`; this controls the numerical tolerance used to determine whether the optimal solution has been found. The default is fairly high and, for smaller problems, should be set much lower (e.g., by setting `tol = 1e-7`). ### Optimal Full Matching (`method = "full"`) Optimal full matching (often just called full matching) assigns every treated and control unit in the sample to one subclass each [@hansen2004; @stuart2008a]. Each subclass contains one treated unit and one or more control units or one control units and one or more treated units. It is optimal in the sense that the chosen number of subclasses and the assignment of units to subclasses minimize the sum of the absolute within-subclass distances in the matched sample. Weights are computed based on subclass membership, and these weights then function like propensity score weights and can be used to estimate a weighted treatment effect, ideally free of confounding by the measured covariates. See `?method_full` for the documentation for `matchit()` with `method = "full"`. Optimal full matching in `MatchIt` depends on the `fullmatch()` function in the `optmatch` package [@hansen2006]. Like the other distance matching methods, optimal full matching requires the specification of a distance measure between units. It can be seen a combination of distance matching and stratum matching: subclasses are formed with varying numbers of treated and control units, as with stratum matching, but the subclasses are formed based on minimizing within-pair distances and do not involve forming strata based on any specific variable, similar to distance matching. Unlike other distance matching methods, full matching can be used to estimate the ATE. Full matching can also be seen as a form of propensity score weighting that is less sensitive to the form of the propensity score model because the original propensity scores are used just to create the subclasses, not to form the weights directly [@austin2015a]. In addition, full matching does not have to rely on estimated propensity scores to form the subclasses and weights; other distance measures are allowed as well. Although full matching uses all available units, there is a loss in precision due to the weights. Units may be weighted in such a way that they contribute less to the sample than would unweighted units, so the effective sample size (ESS) of the full matching weighted sample may be lower than even that of 1:1 pair matching. Balance is often far better after full matching than it is with 1:k matching, making full matching a good option to consider especially when 1:k matching is not effective or when the ATE is the target estimand. The specification of the full matching optimization problem can be customized by supplying additional arguments that are passed to `optmatch::fullmatch()`, such as `min.controls`, `max.controls`, `mean.controls`, and `omit.fraction`. As with optimal pair matching, the numerical tolerance value can be set much lower than the default with small problems by setting, e.g., `tol = 1e-7`. ### Genetic Matching (`method = "genetic"`) Genetic matching is less a specific form of matching and more a way of specifying a distance measure for another form of matching. In practice, though, the form of matching used is nearest neighbor pair matching. Genetic matching uses a genetic algorithm, which is an optimization routine used for non-differentiable objective functions, to find scaling factors for each variable in a generalized Mahalanobis distance formula [@diamond2013]. The criterion optimized by the algorithm is one based on covariate balance. Once the scaling factors have been found, nearest neighbor matching is performed on the scaled generalized Mahalanobis distance. See `?method_genetic` for the documentation for `matchit()` with `method = "genetic"`. Genetic matching in `MatchIt` depends on the `GenMatch()` function in the `Matching` package [@sekhon2011] to perform the genetic search and uses the `Matching` function to perform the nearest neighbor match using the scaled generalized Mahalanobis distance. Genetic matching considers the generalized Mahalanobis distance between a treated unit $i$ and a control unit $j$ as $$\delta_{GMD}(\mathbf{x}_i,\mathbf{x}_j, \mathbf{W})=\sqrt{(\mathbf{x}_i - \mathbf{x}_j)'(\mathbf{S}^{-1/2})'\mathbf{W}(\mathbf{S}^{-1/2})(\mathbf{x}_i - \mathbf{x}_j)}$$ where $\mathbf{x}$ is a $p \times 1$ vector containing the value of each of the $p$ included covariates for that unit, $\mathbf{S}^{-1/2}$ is the Cholesky decomposition of the covariance matrix $\mathbf{S}$ of the covariates, and $\mathbf{W}$ is a diagonal matrix with scaling factors $w$ on the diagonal: $$ \mathbf{W}=\begin{bmatrix} w_1 & & & \\ & w_2 & & \\ & & \ddots &\\ & & & w_p \\ \end{bmatrix} $$ When $w_k=1$ for all covariates $k$, the computed distance is the standard Mahalanobis distance between units. Genetic matching estimates the optimal values of the $w_k$s, where a user-specified criterion is used to define what is optimal. The default is to maximize the smallest p-value among balance tests for the covariates in the matched sample (both Kolmogorov-Smirnov tests and t-tests for each covariate). In `MatchIt`, if a propensity score is specified, the default is to include the propensity score and the covariates in $\mathbf{x}$ and to optimize balance on the covariates. When `distance = "mahalanobis"` or the `mahvars` argument is specified, the propensity score is left out of $\mathbf{x}$. In all other respects, genetic matching functions just like nearest neighbor matching except that the matching itself is carried out by `Matching::Match()` instead of by `MatchIt`. When using `method = "genetic"` in `MatchIt`, additional arguments passed to `Matching::GenMatch()` to control the genetic search process should be specified; in particular, the `pop.size` argument should be increased from its default of 100 to a much higher value. Doing so will make the algorithm take more time to finish but will generally improve the quality of the resulting matches. Different functions can be supplied to be used as the objective in the optimization using the `fit.func` argument. ### Exact Matching (`method = "exact"`) Exact matching is a form of stratum matching that involves creating subclasses based on unique combinations of covariate values and assigning each unit into their corresponding subclass so that only units with identical covariate values are placed into the same subclass. Any units that are in subclasses lacking either treated or control units will be dropped. Exact matching is the most powerful matching method in that no functional form assumptions are required on either the treatment or outcome model for the method to remove confounding due to the measured covariates; the covariate distributions are exactly balanced. The problem with exact matching is that in general, few if any units will remain after matching, so the estimated effect will only generalize to a very limited population and can lack precision. Exact matching is particularly ineffective with continuous covariates, for which it might be that no two units have the same value, and with many covariates, for which it might be the case that no two units have the same combination of all covariates; this latter problem is known as the "curse of dimensionality". See `?method_exact` for the documentation for `matchit()` with `method = "exact"`. It is possible to use exact matching on some covariates and another form of matching on the rest. This makes it possible to have exact balance on some covariates (typically categorical) and approximate balance on others, thereby gaining the benefits of both exact matching and the other matching method used. To do so, the other matching method should be specified in the `method` argument to `matchit()` and the `exact` argument should be specified to contain the variables on which exact matching is to be done. ### Coarsened Exact Matching (`method = "cem"`) Coarsened exact matching (CEM) is a form of stratum matching that involves first coarsening the covariates by creating bins and then performing exact matching on the new coarsened versions of the covariates [@iacus2012]. The degree and method of coarsening can be controlled by the user to manage the trade-off between exact and approximate balancing. For example, coarsening a covariate to two bins will mean that units that differ greatly on the covariate might be placed into the same subclass, while coarsening a variable to five bins may require units to be dropped due to not finding matches. Like exact matching, CEM is susceptible to the curse of dimensionality, making it a less viable solution with many covariates, especially with few units. Dropping units can also change the target population of the estimated effect. See `?method_cem` for the documentation for `matchit()` with `method = "cem"`. CEM in `MatchIt` does not depend on any other package to perform the coarsening and matching, though it used to rely on the `cem` package. ### Subclassification (`method = "subclass"`) Propensity score subclassification can be thought of as a form of coarsened exact matching with the propensity score as the sole covariate to be coarsened and matched on. The bins are usually based on specified quantiles of the propensity score distribution either in the treated group, control group, or overall, depending on the desired estimand. Propensity score subclassification is an old and well-studied method, though it can perform poorly compared to other, more modern propensity score methods such as full matching and weighting [@austin2010]. See `?method_subclass` for the documentation for `matchit()` with `method = "subclass"`. The binning of the propensity scores is typically based on dividing the distribution of covariates into approximately equally sized bins. The user specifies the number of subclasses using the `subclass` argument and which group should be used to compute the boundaries of the bins using the `estimand` argument. Sometimes, subclasses can end up with no units from one of the treatment groups; by default, `matchit()` moves a unit from an adjacent subclass into the lacking one to ensure that each subclass has at least one unit from each treatment group. The minimum number of units required in each subclass can be chosen by the `min.n` argument to `matchit()`. If set to 0, an error will be thrown if any subclass lacks units from one of the treatment groups. Moving units from one subclass to another generally worsens the balance in the subclasses but can increase precision. The default number of subclasses is 6, which is arbitrary and should not be taken as a recommended value. Although early theory has recommended the use of 5 subclasses, in general there is an optimal number of subclasses that is typically much larger than 5 but that varies among datasets [@orihara2021]. Rather than trying to figure this out for oneself, one can use optimal full matching (i.e., with `method = "full"`) to optimally create subclasses with one treated or one control unit that optimize a within-subclass distance criterion. The output of propensity score subclassification includes the assigned subclasses and the subclassification weights. Effects can be estimated either within each subclass and then averaged across them, or a single marginal effect can be estimated using the subclassification weights. This latter method has been called marginal mean weighting through subclassification [MMWS; @hong2010] and fine stratification weighting [@desai2017]. It is also implemented in the `WeightIt` package. ### Cardinality and Template Matching (`method = "cardinality"`) Cardinality and template matching are pure subset selection methods that involve selecting a subset of the original sample without considering the distance between individual units or assigning units to pairs or subclasses. They can be thought of as a weighting method where the weights are restricted to be zero or one. Cardinality matching involves finding the largest sample that satisfies user-supplied balance constraints and constraints on the ratio of matched treated to matched control units [@zubizarretaMatchingBalancePairing2014]. It does not consider a specific estimand and can be a useful alternative to matching with a caliper for handling data with little overlap [@visconti2018]. Template matching involves identifying a target distribution (e.g., the full sample for the ATE or the treated units for the ATT) and finding the largest subset of the treated and control groups that satisfy user-supplied balance constraints with respect to that target [@bennettBuildingRepresentativeMatched2020]. See `?method_cardinality` for the documentation for using `matchit()` with `method = "cardinality"`, including which inputs are required to request either cardinality matching or template matching. Subset selection is performed by solving a mixed integer programming optimization problem with linear constraints. The problem involves maximizing the size of the matched sample subject to constraints on balance and sample size. For cardinality matching, the balance constraints refer to the mean difference for each covariate between the matched treated and control groups, and the sample size constraints require the matched treated and control groups to be the same size (or differ by a user-supplied factor). For template matching, the balance constraints refer to the mean difference for each covariate between each treatment group and the target distribution; for the ATE, this requires the mean of each covariate in each treatment group to be within a given tolerance of the mean of the covariate in the full sample, and for the ATT, this requires the mean of each covariate in the control group to be within a given tolerance of the mean of the covariate in the treated group, which is left intact. The balance tolerances are controlled by the `tols` and `std.tols` arguments. The optimization problem requires a special solver to solve. Currently, the available options in `MatchIt` are the GLPK solver (through the `Rglpk` package), the SYMPHONY solver (through the `Rsymphony` package), and the Gurobi solver (through the `gurobi` package). The differences among the solvers are in performance; Gurobi is by far the best (fastest, least likely to fail to find a solution), but it is proprietary (though has a free trial and academic license) and is a bit more complicated to install. The `designmatch` package also provides an implementation of cardinality matching with more options than `MatchIt` offers. ## Customizing the Matching Specification In addition to the specific matching method, other options are available for many of the matching methods to further customize the matching specification. These include different specifications of the distance measure, methods to perform alternate forms of matching in addition to the main method, prune units far from other units prior to matching, restrict possible matches, etc. Not all options are compatible with all matching methods. ### Specifying the propensity score or other distance measure (`distance`) The distance measure is used to define how close two units are. In nearest neighbor matching, this is used to choose the nearest control unit to each treated unit. In optimal matching, this is used in the criterion that is optimized. By default, the distance measure is the propensity score difference, and the argument supplied to `distance` corresponds to the method of estimating the propensity score. In `MatchIt`, propensity scores are often labeled as "distance" values, even though the propensity score itself is not a distance measure. This is to reflect that the propensity score is used in creating the distance value, but other scores could be used, such as prognostic scores for prognostic score matching [@hansen2008a]. The propensity score is more like a "position" value, in that it reflects the position of each unit in the matching space, and the difference between positions is the distance between them. If the the argument to `distance` is one of the allowed values (see `?distance` for these values) other than `"mahalanobis"` or is a numeric vector with one value per unit, the distance between units will be computed as the pairwise difference between propensity scores or the supplied values. Propensity scores are also used in propensity score subclassification and can optionally be used in genetic matching as a component of the generalized Mahalanobis distance. For exact, coarsened exact, and cardinality matching, the `distance` argument is ignored. The default `distance` argument is `"glm"`, which estimates propensity scores using logistic regression or another generalized linear model. The `link` and `distance.options` arguments can be supplied to further specify the options for the propensity score models, including whether to use the raw propensity score or a linearized version of it (e.g., the logit of a logistic regression propensity score, which has been commonly referred to and recommended in the propensity score literature [@austin2011a; @stuart2010]). Allowable options for the propensity score model include parametric and machine learning-based models, each of which have their strengths and limitations and may perform differently depending on the unique qualities of each dataset. We recommend multiple types of models be tried to find one that yields the best balance, as there is no way to make a single recommendation that will work for all cases. The `distance` argument can also be specified as `"mahalanobis"`. For nearest neighbor and optimal (full or pair) matching, this triggers `matchit()` not to estimate propensity scores but rather to use just the Mahalanobis distance, which is defined for a treated unit $i$ and a control unit $j$ as $$\delta_{MD}(\mathbf{x}_i,\mathbf{x}_j)=\sqrt{(\mathbf{x}_i - \mathbf{x}_j)'S^{-1}(\mathbf{x}_i - \mathbf{x}_j)}$$ where $\mathbf{x}$ is a $p \times 1$ vector containing the value of each of the $p$ included covariates for that unit, $S$ is the covariance matrix among all the covariates, and $S^{-1}$ is the (generalized) inverse of $S$. The R function `mahalanobis()` is used to compute this. Mahalanobis distance matching tends to work better with continuous covariates than with categorical covariates. For creating close pairs, Mahalanobis distance matching tends work better than propensity score matching because Mahalanobis distance-paired units will have close values on all of the covariates, whereas propensity score-paired units may be close on the propensity score but not on any of the covariates themselves. This feature was the basis of King and Nielsen's [-@king2019] warning against using propensity scores for matching. `distance` can also be supplied as a matrix of distance values between units. This makes it possible to use handcrafted distance matrices or distances created outside `MatchIt`, e.g., using `optmatch::match_on()`. Only nearest neighbor, optimal pair, and optimal full matching allow this specification. The propensity score can have uses other than as the basis for matching. It can be used to define a region of common support, outside which units are dropped prior to matching; this is implemented by the `discard` option. It can also be used to define a caliper, the maximum distance two units can be before they are prohibited from being paired with each other; this is implemented by the `caliper` argument. To estimate or supply a propensity score for one of these purposes but not use it as the distance measure for matching (e.g., to perform Mahalanobis distance matching instead), the `mahvars` argument can be specified. These options are described below. ### Implementing common support restrictions (`discard`) The region of *common support* is the region of overlap between treatment groups. A common support restriction discards units that fall outside of the region of common support, preventing them from being matched to other units and included in the matched sample. This can reduce the potential for extrapolation and help the matching algorithms to avoid overly distant matches from occurring. In `MatchIt`, the `discard` option implements a common support restriction based on the propensity score. The argument can be supplied as `"treated"`, `"control"`, or `"both"`, which discards units in the corresponding group that fall outside the region of common support for the propensity score. The `reestimate` argument can be supplied to choose whether to re-estimate the propensity score in the remaining units. **If units from the treated group are discarded based on a common support restriction, the estimand no longer corresponds to the ATT.** ### Caliper matching (`caliper`) A *caliper* can be though of as a ring around each unit that limits to which other units that unit can be paired. Calipers are based on the propensity score or other covariates. Two units whose distance on a calipered covariate is larger than the caliper width for that covariate are not allowed to be matched to each other. Any units for which there are no available matches within the caliper are dropped from the matched sample. Calipers ensure paired units are close to each other on the calipered covariates, which can ensure good balance in the matched sample. Multiple variables can be supplied to `caliper` to enforce calipers on all of them simultaneously. Using calipers can be a good alternative to exact or coarsened exact matching to ensure only similar units are paired with each other. The `std.caliper` argument controls whether the provided calipers are in raw units or standard deviation units. **If units from the treated group are left unmatched due to a caliper, the estimand no longer corresponds to the ATT.** ### Mahalanobis distance matching (`mahvars`) To perform Mahalanobis distance matching without the need to estimate or use a propensity score, the `distance` argument can be set to `"mahalanobis"`. If a propensity score is to be estimated or used for a different purpose, such as in a common support restriction or a caliper, but you still want to perform Mahalanobis distance matching, variables should be supplied to the `mahvars` argument. The propensity scores will be generated using the `distance` specification, and matching will occur not on the covariates supplied to the main formula of `matchit()` but rather on the covariates supplied to `mahvars`. To perform Mahalanobis distance matching within a propensity score caliper, for example, the `distance` argument should be set to the method of estimating the propensity score (e.g., `"glm"` for logistic regression), the `caliper` argument should be specified to the desired caliper width, and `mahvars` should be specified to perform Mahalanobis distance matching on the desired covariates within the caliper. ### Exact matching (`exact`) To perform exact matching on all supplied covariates, the `method` argument can be set to `"exact"`. To perform exact matching only on some covariates and some other form of matching within exact matching strata on other covariates, the `exact` argument can be used. Covariates supplied to the `exact` argument will be matched exactly, and the form of matching specified by `method` (e.g., `"nearest"` for nearest neighbor matching) will take place within each exact matching stratum. This can be a good way to gain some of the benefits of exact matching without completely succumbing to the curse of dimensionality. As with exact matching performed with `method = "exact"`, any units in strata lacking members of one of the treatment groups will be left unmatched. Note that although matching occurs within each exact matching stratum, propensity score estimation and computation of the Mahalanobis distance occur in the full sample. **If units from the treated group are unmatched due to an exact matching restriction, the estimand no longer corresponds to the ATT.** ### Anti-exact matching (`antiexact`) Anti-exact matching adds a restriction such that a treated and control unit with same values of any of the specified anti-exact matching variables cannot be paired. This can be useful when finding comparison units outside of a unit's group, such as when matching units in one group to units in another when units within the same group might otherwise be close matches. See examples [here](https://stackoverflow.com/questions/66526115/propensity-score-matching-with-panel-data) and [here](https://stackoverflow.com/questions/61120201/avoiding-duplicates-from-propensity-score-matching?rq=1). ### Matching with replacement (`replace`) Nearest neighbor matching and genetic matching have the option of matching with or without replacement, and this is controlled by the `replace` argument. Matching without replacement means that each control unit is matched to only one treated unit, while matching with replacement means that control units can be reused and matched to multiple treated units. Matching without replacement carries certain statistical benefits in that weights for each unit can be omitted or are more straightforward to include and dependence between units depends only on pair membership. Special standard error estimators are sometimes required for estimating effects after matching with replacement [@austin2020a], and methods for accounting for uncertainty are not well understood for non-continuous outcomes. Matching with replacement will tend to yield better balance though, because the problem of "running out" of close control units to match to treated units is avoided, though the reuse of control units will decrease the effect sample size, thereby worsening precision. (This problem occurs in the Lalonde dataset used in `vignette("MatchIt")`, which is why nearest neighbor matching without replacement is not very effective there.) After matching with replacement, control units are assigned to more than one subclass, so the `get_matches()` function should be used instead of `match.data()` after matching with replacement if subclasses are to be used in follow-up analyses; see `vignette("Estimating Effects")` for details. The `reuse.max` argument can also be used with `method = "nearest"` to control how many times each control unit can be reused as a match. Setting `reuse.max = 1` is equivalent to requiring matching without replacement (i.e., because each control can be used only once). Other values allow control units to be matched more than once, though only up to the specified number of times. Higher values will tend to improve balance at the cost of precision. ### $k$:1 matching (`ratio`) The most common form of matching, 1:1 matching, involves pairing one control unit with each treated unit. To perform $k$:1 matching (e.g., 2:1 or 3:1), which pairs (up to) $k$ control units with each treated unit, the `ratio` argument can be specified. Performing $k$:1 matching can preserve precision by preventing too many control units from being unmatched and dropped from the matched sample, though the gain in precision by increasing $k$ diminishes rapidly after 4 [@rosenbaum2020]. Importantly, for $k>1$, the matches after the first match will generally be worse than the first match in terms of closeness to the treated unit, so increasing $k$ can also worsen balance. @austin2010a found that 1:1 or 1:2 matching generally performed best in terms of mean squared error. In general, it makes sense to use higher values of $k$ while ensuring that balance is satisfactory. With nearest neighbor and optimal pair matching, variable $k$:1 matching, in which the number of controls matched to each treated unit varies, can also be used; this can have improved performance over "fixed" $k$:1 matching [@ming2000]. See `?method_nearest` and `?method_optimal` for information on implementing variable $k$:1 matching. ## Choosing a Matching Method Choosing the best matching method for one's data depends on the unique characteristics of the dataset as well as the goals of the analysis. For example, because different matching methods can target different estimands, when certain estimands are desired, specific methods must be used. On the other hand, some methods may be more effective than others when retaining the target estimand is less important. Below we provide some guidance on choosing a matching method. Remember that multiple methods can (and should) be tried as long as the treatment effect is not estimated until a method has been settled on. The criteria on which a matching specification should be judged are balance and remaining (effective) sample size after matching. Assessing balance is described in `vignette("assessing-balance")`. A typical workflow is similar to that demonstrated in `vignette("MatchIt")`: try a matching method, and if it yields poor balance or an unacceptably low remaining sample size, try another, until a satisfactory specification has been found. It is important to assess balance broadly (i.e., beyond comparing the means of the covariates in the treated and control groups), and the search for a matching specification should not stop when a threshold is reached, but should attempt to come as close as possible to perfect balance [@ho2007]. Even if the first matching specification appears successful at reducing imbalance, there may be another specification that could reduce it even further, thereby increasing the robustness of the inference and the plausibility of an unbiased effect estimate. If the target of inference is the ATE, full matching, subclassification, and template matching can be used. If the target of inference is the ATT or ATC, any matching method may be used. When retaining the target estimand is not so important, additional options become available that involve discarding units in such a way that the original estimand is distorted. These include matching with a caliper, matching within a region of common support, cardinality matching, or exact or coarsened exact matching, perhaps on a subset of the covariates. Because exact and coarsened exact matching aim to balance the entire joint distribution of covariates, they are the most powerful methods. If it is possible to perform exact matching, this method should be used. If continuous covariates are present, coarsened exact matching can be tried. Care should be taken with retaining the target population and ensuring enough matched units remain; unless the control pool is much larger than the treated pool, it is likely some (or many) treated units will be discarded, thereby changing the estimand and possibly dramatically reducing precision. These methods are typically only available in the most optimistic of circumstances, but they should be used first when those circumstances arise. It may also be useful to combine exact or coarsened exact matching on some covariates with another form of matching on the others (i.e., by using the `exact` argument). When estimating the ATE, either subclassification, full matching, or template matching can be used. Full matching can be effective because it optimizes a balance criterion, often leading to better balance. With full matching, it's also possible to exact match on some variables and match using the Mahalanobis distance, eliminating the need to estimate propensity scores. Template matching also ensures good balance, but because units are only given weights of zero one, a solution may not be feasible and many units may have to be discarded. For large datasets, neither full matching nor template matching may be possible, in which case subclassification is a faster solution. When using subclassification, the number of subclasses should be varied. With large samples, higher numbers of subclasses tend to yield better performance; one should not immediately settle for the default (6) or the often-cited recommendation of 5 without trying several other numbers. When estimating the ATT, a variety of methods can be tried. Genetic matching can perform well at achieving good balance because it directly optimizes covariate balance. With larger datasets, it may take a long time to reach a good solution (though that solution will tend to be good as well). Template matching also will achieve good balance if a solution is feasible because balance is controlled by the user. Optimal pair matching and nearest neighbor matching without replacement tend to perform similarly to each other; nearest neighbor matching may be preferable for large datasets that cannot be handled by optimal matching. Nearest neighbor, optimal, and genetic matching allow some customizations like including covariates on which to exactly match, using the Mahalanobis distance instead of a propensity score difference, and performing $k$:1 matching with $k>1$. Nearest neighbor matching with replacement, full matching, and subclassification all involve weighting the control units with nonuniform weights, which often allows for improved balancing capabilities but can be accompanied by a loss in effective sample size, even when all units are retained. There is no reason not to try many of these methods, varying parameters here and there, in search of good balance and high remaining sample size. As previously mentioned, no single method can be recommended above all others because the optimal specification depends on the unique qualities of each dataset. When the target population is less important, for example, when engaging in treatment effect discovery or when when the sampled population is not of particular interest (e.g., it corresponds to an arbitrarily chosen hospital or school; see @mao2018 for these and other reasons why retaining the target population may not be important), other methods that do not retain the characteristics of the original sample become available. These include matching with a caliper (on the propensity score or on the covariates themselves), cardinality matching, and more restrictive forms of matching like exact and coarsened exact matching, either on all covariates or just a subset, that are prone to discard units from the sample in such a way that the target population is changed. @austin2013b and Austin and Stuart [-@austin2015c; -@austin2015a] have found that caliper matching can be a particularly effective modification to nearest neighbor matching for eliminating imbalance and reducing bias when the target population is less relevant, but when inference to a specific target population is desired, using calipers can induce bias due to incomplete matching [@rosenbaum1985; @wang2020]. Cardinality matching can be particularly effective in data with little overlap between the treatment groups [@visconti2018] and can perform better than caliper matching [@delosangelesresaDirectStableWeight2020]. It is important not to rely excessively on theoretical or simulation-based findings or specific recommendations when making choices about the best matching method to use. For example, although nearest neighbor matching without replacement balance covariates better than did subclassification with five or ten subclasses in Austin's [-@austin2009c] simulation, this does not imply it will be superior in all datasets. Likewise, though @rosenbaum1985a and @austin2011a both recommend using a caliper of .2 standard deviations of the logit of the propensity score, this does not imply that caliper will be optimal in all scenarios, and other widths should be tried, though it should be noted that tightening the caliper on the propensity score can sometimes degrade performance [@king2019]. ## Reporting the Matching Specification When reporting the results of a matching analysis, it is important to include the relevant details of the final matching specification and the process of arriving at it. Using `print()` on the `matchit` object synthesizes information on how the above arguments were used to provide a description of the matching specification. It is best to be as specific as possible to ensure the analysis is replicable and to allow audiences to assess its validity. Although citations recommending specific matching methods can be used to help justify a choice, the only sufficient justification is adequate balance and remaining sample size, regardless of published recommendations for specific methods. See `vignette("assessing-balance")` for instructions on how to assess and report the quality of a matching specification. After matching and estimating an effect, details of the effect estimation must be included as well; see `vignette("estimating-effects")` for instructions on how to perform and report on the analysis of a matched dataset. ## References MatchIt/inst/doc/MatchIt.html0000644000176200001440000101016514170752535015561 0ustar liggesusers MatchIt: Getting Started

MatchIt: Getting Started

Noah Greifer

2022-01-16

Introduction

MatchIt implements the suggestions of Ho et al. (2007) for improving parametric statistical models for estimating treatment effects in observational studies and reducing model dependence by preprocessing data with semi-parametric and non-parametric matching methods. After appropriately preprocessing with MatchIt, researchers can use whatever parametric model they would have used without MatchIt and produce inferences that are more robust and less sensitive to modeling assumptions. MatchIt reduces the dependence of causal inferences on commonly made, but hard-to-justify, statistical modeling assumptions using a large range of sophisticated matching methods. The package includes several popular approaches to matching and provides access to methods implemented in other packages through its single, unified, and easy-to-use interface.

Matching is used in the context of estimating the causal effect of a binary treatment or exposure on an outcome while controlling for measured pre-treatment variables, typically confounding variables or variables prognostic of the outcome. Here and throughout the MatchIt documentation we use the word “treatment” to refer to the focal causal variable of interest, with “treated” and “control” reflecting the names of the treatment groups. The goal of matching is to produce covariate balance, that is, for the distributions of covariates in the two groups to be approximately equal to each other, as they would be in a successful randomized experiment. The importance of covariate balance is that it allows for increased robustness to the choice of model used to estimate the treatment effect; in perfectly balanced samples, a simple difference in means can be a valid treatment effect estimate. Here we do not aim to provide a full introduction to matching or causal inference theory, but simply to explain how to use MatchIt to perform nonparametric preprocessing. For excellent and accessible introductions to matching, see Stuart (2010) and Austin Austin (2011).

A matching analysis involves four primary steps: 1) planning, 2) matching, 3) assessing the quality of matches, and 4) estimating the treatment effect and its uncertainty. Here we briefly discuss these steps and how they can be implemented with MatchIt; in the other included vignettes, these steps are discussed in more detail.

We will use Lalonde’s data on the evaluation of the National Supported Work program to demonstrate MatchIt’s capabilities. First, we load MatchIt and bring in the lalonde dataset.

library("MatchIt")
data("lalonde")

head(lalonde)
##      treat age educ   race married nodegree re74 re75       re78
## NSW1     1  37   11  black       1        1    0    0  9930.0460
## NSW2     1  22    9 hispan       0        1    0    0  3595.8940
## NSW3     1  30   12  black       0        0    0    0 24909.4500
## NSW4     1  27   11  black       0        1    0    0  7506.1460
## NSW5     1  33    8  black       0        1    0    0   289.7899
## NSW6     1  22    9  black       0        1    0    0  4056.4940

The statistical quantity of interest is the causal effect of the treatment (treat) on 1978 earnings (re78). The other variables are pre-treatment covariates. See ?lalonde for more information on this dataset. In particular, the analysis is concerned with the marginal, total effect of the treatment for those who actually received the treatment.

In what follows, we briefly describe the four steps of a matching analysis and how to implement them in MatchIt. For more details, we recommend reading the other vignettes, vignette("matching-methods"), vignette("assessing-balance"), and vignette("estimating-effects"), especially for users less familiar with matching methods. For the use of MatchIt with sampling weights, also see vignette("sampling-weights"). It is important to recognize that the ease of using MatchIt does not imply the simplicity of matching methods; advanced statistical methods like matching that require many decisions to be made and caution in their use should only be performed by those with statistical training.

Planning

The planning phase of a matching analysis involves selecting the type of effect to be estimated, selecting the target population to which the treatment effect is to generalize, and selecting the covariates for which balance is required for an unbiased estimate of the treatment effect. Each of these are theoretical steps that do not involve performing analyses on the data. Ideally, they should be considered prior to data collection in the planning stage of a study. Thinking about them early can aid in performing a complete and cost-effective analysis.

Selecting the type of effect to be estimated. There are a few different types of effects to be estimated. In the presence of mediating variables, one might be interested in the direct effect of the treatment that does not pass through the mediating variables or the total effect of the treatment across all causal pathways. Matching is well suited for estimating total effects, and specific mediation methods may be better suited for other mediation-related quantities. One may be interested in a conditional effect or a marginal effect. A conditional effect is the effect of a treatment within some strata of other prognostic variables (e.g., at the patient level), and a marginal effect is the average effect of a treatment in a population (e.g., for implementing a broad policy change). Different types of matching are well suited for each of these, but the most common forms are best used for estimating marginal treatment effects; for conditional treatment effects, typically modeling assumptions are required or matching must be done within strata of the conditioning variables. Matching can reduce the reliance on correct model specification for conditional effects.

Selecting a target population. The target population is the population to which the effect estimate is to generalize. Typically, an effect estimated in a sample generalizes to the population from which the sample is a probability sample. If the sample is not a probability sample from any population (e.g., it is a convenience sample or involves patients from an arbitrary hospital), the target population can be unclear. Often, the target population is a group of units who are eligible for the treatment (or a subset thereof). Causal estimands are defined by the target population to which they generalize.

The average treatment effect in the population (ATE) is the average effect of the treatment for all units in the target population. The average treatment effect in the treated (ATT) is the average effect of the treatment for units like those who actually were treated. The most common forms of matching are best suited for estimating the ATT, though some are also available for estimating the ATE. Some matching methods distort the sample in such a way that the estimated treatment effect corresponds neither to the ATE nor to the ATT, but rather to the effect in an unspecified population (sometimes called the ATM, or average treatment effect in the remaining matched sample). When the target population is not so important (e.g., in the case of treatment effect discovery), such methods may be attractive; otherwise, care should be taken in ensuring the effect generalizes to the target population of interest. Different matching methods allow for different target populations, so it is important to choose a matching method that allows one to estimate the desired effect. See Greifer and Stuart (2021) for guidance on making this choice.

Selecting covariates to balance. Selecting covariates carefully is critical for ensuring the resulting treatment effect estimate is free of confounding and can be validly interpreted as a causal effect. To estimate total causal effects, all covariates must be measured prior to treatment (or otherwise not be affected by the treatment). Covariates should be those that cause variation in the outcome and selection into treatment group; these are known as confounding variables. See VanderWeele (2019) for a guide on covariate selection. Ideally these covariates are measured without error and are free of missingness.

Check Initial Imbalance

After planning and prior to matching, it can be a good idea to view the initial imbalance in one’s data that matching is attempting to eliminate. We can do this using the code below:

# No matching; constructing a pre-match matchit object
m.out0 <- matchit(treat ~ age + educ + race + married + 
                   nodegree + re74 + re75, data = lalonde,
                 method = NULL, distance = "glm")

The first argument is a formula relating the treatment to the covariates used in estimating the propensity score and for which balance is to be assessed. The data argument specifies the dataset where these variables exist. Typically, the method argument specifies the method of matching to be performed; here, we set it to NULL so we can assess balance prior to matching1. The distance argument specifies the method for estimating the propensity score, a one-dimensional summary of all the included covariates, computed as the predicted probability of being the treated group given the covariates; here, we set it to "glm" for generalized linear model, which implements logistic regression by default2 (see ?distance for other options).

Below we assess balance on the unmatched data using summary():

# Checking balance prior to matching
summary(m.out0)
## 
## Call:
## matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = NULL, distance = "glm")
## 
## Summary of Balance for All Data:
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance          0.5774        0.1822          1.7941     0.9211    0.3774   0.6444
## age              25.8162       28.0303         -0.3094     0.4400    0.0813   0.1577
## educ             10.3459       10.2354          0.0550     0.4959    0.0347   0.1114
## raceblack         0.8432        0.2028          1.7615          .    0.6404   0.6404
## racehispan        0.0595        0.1422         -0.3498          .    0.0827   0.0827
## racewhite         0.0973        0.6550         -1.8819          .    0.5577   0.5577
## married           0.1892        0.5128         -0.8263          .    0.3236   0.3236
## nodegree          0.7081        0.5967          0.2450          .    0.1114   0.1114
## re74           2095.5737     5619.2365         -0.7211     0.5181    0.2248   0.4470
## re75           1532.0553     2466.4844         -0.2903     0.9563    0.1342   0.2876
## 
## 
## Sample Sizes:
##           Control Treated
## All           429     185
## Matched       429     185
## Unmatched       0       0
## Discarded       0       0

We can see severe imbalances as measured by the standardized mean differences (Std. Mean Diff.), variance ratios (Var. Ratio), and empirical cumulative density function (eCDF) statistics. Values of standardized mean differences and eCDF statistics close to zero and values of variance ratios close to one indicate good balance, and here many of them are far from their ideal values.

Matching

Now, matching can be performed. There are several different classes and methods of matching, described in vignette("matching-methods"). Here, we begin by briefly demonstrating 1:1 nearest neighbor (NN) matching on the propensity score, which is appropriate for estimating the ATT. One by one, each treated unit is paired with an available control unit that has the closest propensity score to it. Any remaining control units are left unmatched and excluded from further analysis. Due to the theoretical balancing properties of the propensity score described by Rosenbaum and Rubin (1983), propensity score matching can be an effective way to achieve covariate balance in the treatment groups. Below we demonstrate the use of matchit() to perform nearest neighbor propensity score matching.

# 1:1 NN PS matching w/o replacement
m.out1 <- matchit(treat ~ age + educ + race + married + 
                   nodegree + re74 + re75, data = lalonde,
                 method = "nearest", distance = "glm")

We use the same syntax as before, but this time specify method = "nearest" to implement nearest neighbor matching, again using a logistic regression propensity score. Many other arguments are available for tuning the matching method and method of propensity score estimation.

The matching outputs are contained in the m.out1 object. Printing this object gives a description of the type of matching performed:

m.out1
## A matchit object
##  - method: 1:1 nearest neighbor matching without replacement
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 614 (original), 370 (matched)
##  - target estimand: ATT
##  - covariates: age, educ, race, married, nodegree, re74, re75

The key components of the m.out1 object are weights (the computed matching weights), subclass (matching pair membership), distance (the estimated propensity score), and match.matrix (which control units are matched to each treated unit). How these can be used for estimating the effect of the treatment after matching is detailed in vignette("estimating-effects").

Assessing the Quality of Matches

Although matching on the propensity score is often effective at eliminating differences between the treatment groups to achieve covariate balance, its performance in this regard must be assessed. If covariates remain imbalanced after matching, the matching is considered unsuccessful, and a different matching specification should be tried. MatchIt offers a few tools for the assessment of covariate balance after matching. These include graphical and statistical methods. More detail on the interpretation of the included plots and statistics can be found in vignette("assessing-balance").

In addition to covariate balance, the quality of the match is determined by how many units remain after matching. Matching often involves discarding units that are not paired with other units, and some matching options, such as setting restrictions for common support or calipers, can further decrease the number of remaining units. If, after matching, the remaining sample size is small, the resulting effect estimate may be imprecise. In many cases, there will be a trade-off between balance and remaining sample size. How to optimally choose among them is an instance of the fundamental bias-variance trade-off problem that cannot be resolved without substantive knowledge of the phenomena under study. Prospective power analyses can be used to determine how small a sample can be before necessary precision is sacrificed.

To assess the quality of the resulting matches numerically, we can use the summary() function on m.out1 as before. Here we set un = FALSE to suppress display of the balance before matching for brevity and because we already saw it. (Leaving it as TRUE, its default, would display balance both before and after matching.)

# Checking balance after NN matching
summary(m.out1, un = FALSE)
## 
## Call:
## matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = "nearest", distance = "glm")
## 
## Summary of Balance for Matched Data:
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max Std. Pair Dist.
## distance          0.5774        0.3629          0.9739     0.7566    0.1321   0.4216          0.9740
## age              25.8162       25.3027          0.0718     0.4568    0.0847   0.2541          1.3938
## educ             10.3459       10.6054         -0.1290     0.5721    0.0239   0.0757          1.2474
## raceblack         0.8432        0.4703          1.0259          .    0.3730   0.3730          1.0259
## racehispan        0.0595        0.2162         -0.6629          .    0.1568   0.1568          1.0743
## racewhite         0.0973        0.3135         -0.7296          .    0.2162   0.2162          0.8390
## married           0.1892        0.2108         -0.0552          .    0.0216   0.0216          0.8281
## nodegree          0.7081        0.6378          0.1546          .    0.0703   0.0703          1.0106
## re74           2095.5737     2342.1076         -0.0505     1.3289    0.0469   0.2757          0.7965
## re75           1532.0553     1614.7451         -0.0257     1.4956    0.0452   0.2054          0.7381
## 
## Sample Sizes:
##           Control Treated
## All           429     185
## Matched       185     185
## Unmatched     244       0
## Discarded       0       0

At the top is a summary of covariate balance after matching. Although balance has improved for some covariates, in general balance is still quite poor, indicating that nearest neighbor propensity score matching is not sufficient for removing confounding in this dataset. The final column, Std. Pair Diff, displays the average absolute within-pair difference of each covariate. When these values are small, better balance is typically achieved and estimated effects are more robust to misspecification of the outcome model (King and Nielsen 2019; Rubin 1973).

Next is a table of the sample sizes before and after matching. The matching procedure left 244 control units unmatched. Ideally, unmatched units would be those far from the treated units and would require greater extrapolation were they to have been retained. We can visualize the distribution of propensity scores of those who were matched using plot() with type = "jitter":

plot(m.out1, type = "jitter", interactive = FALSE)

We can visually examine balance on the covariates using plot() with type = "qq":

plot(m.out1, type = "qq", interactive = FALSE,
     which.xs = c("age", "married", "re75"))

Points far from the solid diagonal line are the areas of the covariate distributions that differ between the treatment groups. Although married and re75 appear to have improved balance after matching, the case is mixed for age.

Trying a Different Matching Specification

Given the poor performance of nearest neighbor matching in this example, we can try a different matching method or make other changes to the matching algorithm or distance specification. Below, we’ll try full matching, which matches every treated unit to at least one control and every control to at least one treated unit (Hansen 2004; Stuart and Green 2008). We’ll also try a different link (probit) for the propensity score model.

# Full matching on a probit PS
m.out2 <- matchit(treat ~ age + educ + race + married + 
                   nodegree + re74 + re75, data = lalonde,
                 method = "full", distance = "glm", link = "probit")
m.out2
## A matchit object
##  - method: Optimal full matching
##  - distance: Propensity score
##              - estimated with probit regression
##  - number of obs.: 614 (original), 614 (matched)
##  - target estimand: ATT
##  - covariates: age, educ, race, married, nodegree, re74, re75

We can examine balance on this new matching specification.

# Checking balance after full matching
summary(m.out2, un = FALSE)
## 
## Call:
## matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = "full", distance = "glm", 
##     link = "probit")
## 
## Summary of Balance for Matched Data:
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max Std. Pair Dist.
## distance          0.5773        0.5765          0.0040     0.9943    0.0042   0.0541          0.0198
## age              25.8162       25.6722          0.0201     0.4614    0.0848   0.2846          1.2741
## educ             10.3459       10.3693         -0.0116     0.6173    0.0194   0.0597          1.2233
## raceblack         0.8432        0.8389          0.0119          .    0.0043   0.0043          0.0162
## racehispan        0.0595        0.0500          0.0402          .    0.0095   0.0095          0.4985
## racewhite         0.0973        0.1111         -0.0467          .    0.0138   0.0138          0.3911
## married           0.1892        0.1580          0.0797          .    0.0312   0.0312          0.4866
## nodegree          0.7081        0.6898          0.0404          .    0.0184   0.0184          0.9593
## re74           2095.5737     2103.5534         -0.0016     1.3513    0.0328   0.2159          0.8533
## re75           1532.0553     1552.4673         -0.0063     1.5678    0.0496   0.2013          0.8279
## 
## Sample Sizes:
##               Control Treated
## All            429.       185
## Matched (ESS)   53.51     185
## Matched        429.       185
## Unmatched        0.         0
## Discarded        0.         0

Balance is far better, as determined by the lower standardized mean differences and eCDF statistics. The balance should be reported when publishing the results of a matching analysis. This can be done either in a table, using the values resulting from summary(), or in a plot, such as a Love plot, which we can make by calling plot() on the summary() output:

plot(summary(m.out2))

Love plots are a simple and straightforward way to summarize balance visually. See vignette("assessing-balance") for more information on how to customize MatchIt’s Love plot and how to use cobalt, a package designed specifically for balance assessment and reporting that is compatible with MatchIt.

Estimating the Treatment Effect

How treatment effects are estimated depends on what form of matching was performed. See vignette("estimating-effects") for information on the variety of way to estimate effects and standard errors after each type of matching and for several outcome types. After 1:1 matching without replacement (i.e., the first matching specification above), we can run a simple regression of the outcome on the treatment in the matched sample (i.e., including the matching weights). With continuous outcomes, it is often a good idea to also include the covariates used in the matching in the effect estimation, as doing so can provide additional robustness to slight imbalances remaining after the matching and can improve precision.

Even though the 1:1 matching was not successful, we’ll demonstrate here how to estimate a treatment effect after performing such an analysis. First, we’ll extract the matched dataset from the matchit object using match.data(). This dataset only contains the matched units and adds columns for distance, weights, and subclass (described previously).

m.data1 <- match.data(m.out1)

head(m.data1)
##      treat age educ   race married nodegree re74 re75       re78  distance weights subclass
## NSW1     1  37   11  black       1        1    0    0  9930.0460 0.6387699       1        1
## NSW2     1  22    9 hispan       0        1    0    0  3595.8940 0.2246342       1       98
## NSW3     1  30   12  black       0        0    0    0 24909.4500 0.6782439       1      109
## NSW4     1  27   11  black       0        1    0    0  7506.1460 0.7763241       1      120
## NSW5     1  33    8  black       0        1    0    0   289.7899 0.7016387       1      131
## NSW6     1  22    9  black       0        1    0    0  4056.4940 0.6990699       1      142

We can then estimate a treatment effect in this dataset using the standard regression functions in R, like lm() or glm(), being sure to include the matching weights (stored in the weights variable of the match.data() output) in the estimation3. We recommend using cluster-robust standard errors for most analyses, with pair membership as the clustering variable; the lmtest and sandwich packages together make this straightforward.

library("lmtest") #coeftest
library("sandwich") #vcovCL

fit1 <- lm(re78 ~ treat + age + educ + race + married + nodegree + 
             re74 + re75, data = m.data1, weights = weights)

coeftest(fit1, vcov. = vcovCL, cluster = ~subclass)
## 
## t test of coefficients:
## 
##                Estimate  Std. Error t value Pr(>|t|)   
## (Intercept) -2.5816e+03  3.3209e+03 -0.7774 0.437439   
## treat        1.3449e+03  7.3084e+02  1.8403 0.066552 . 
## age          7.8035e+00  4.4148e+01  0.1768 0.859797   
## educ         6.0220e+02  2.1007e+02  2.8667 0.004391 **
## racehispan   1.5335e+03  1.0248e+03  1.4964 0.135417   
## racewhite    4.6943e+02  8.9854e+02  0.5224 0.601687   
## married     -1.5825e+02  9.3354e+02 -0.1695 0.865482   
## nodegree     9.2328e+02  1.1496e+03  0.8032 0.422412   
## re74         2.6362e-02  1.6646e-01  0.1584 0.874257   
## re75         2.2068e-01  1.6771e-01  1.3158 0.189069   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

The coefficient on treat is the estimated ATT. The other coefficients and tests should not be interpreted or reported. Estimating standard errors with matched data is an area of ongoing development. Generally, the approach demonstrated above works well for continuous outcomes. See vignette("estimating-effects") for more information on how to estimate standard errors with each type of matching and with different outcome types.

A benefit of matching is that the outcome model used to estimate the treatment effect is robust to misspecification when balance has been achieved. With 1:1 nearest neighbor matching, we failed to achieve balance, so one should be cautious about trusting the estimated effect. With full matching, we were able to achieve balance, so the effect estimate should depend less on the form of the outcome model used. Below we estimate the effect and standard error of the treatment effect after full matching. As before, we’ll use functions from the lmtest and sandwich packages here because they provide a fairly general interface to estimating coefficients and standard errors.

m.data2 <- match.data(m.out2)

fit2 <- lm(re78 ~ treat + age + educ + race + married + nodegree + 
             re74 + re75, data = m.data2, weights = weights)

coeftest(fit2, vcov. = vcovCL, cluster = ~subclass)
## 
## t test of coefficients:
## 
##                Estimate  Std. Error t value  Pr(>|t|)    
## (Intercept)  2.8493e+03  3.1547e+03  0.9032 0.3667819    
## treat        1.9797e+03  7.5611e+02  2.6183 0.0090589 ** 
## age         -4.5799e+01  3.7917e+01 -1.2079 0.2275592    
## educ         2.3234e+02  2.0245e+02  1.1477 0.2515594    
## racehispan   9.6380e+02  1.4435e+03  0.6677 0.5045794    
## racewhite    1.7067e+03  8.2231e+02  2.0755 0.0383636 *  
## married      9.0378e+02  1.1858e+03  0.7622 0.4462384    
## nodegree    -1.2712e+03  1.2691e+03 -1.0017 0.3169017    
## re74        -1.1459e-02  1.4547e-01 -0.0788 0.9372369    
## re75         5.4080e-01  1.4212e-01  3.8053 0.0001561 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Given the results of these two estimates, we would be inclined to trust the one resulting from the second analysis, i.e., using full matching, because better balance was achieved on all the variables, making the effect estimate less sensitive to the form of the outcome model we used.

Effect estimation with nonlinear models (e.g., for binary or time-to-event event outcomes) is more complicated due to noncollapsibility of the estimated effects; including additional covariates in the model can change the meaning of the estimated effect. See vignette("estimating-effects") for more details. Note that for some models, effect and standard error estimation is still being researched.

Reporting Results

To report matching results in a manuscript or research report, a few key pieces of information are required. One should be as detailed as possible about the matching procedure and the decisions made to ensure the analysis is replicable and can be adequately assessed for soundness by the audience. Key pieces of information to include are 1) the matching specification used (including the method and any additional options, like calipers or common support restrictions), 2) the distance measure used (including how it was estimated e.g., using logistic regression for propensity scores), 3) which other matching methods were tried prior to settling on a final specification and how the choices were made, 4) the balance of the final matching specification (including standardized mean differences and other balance statistics for the variables, their powers, and their interactions; some of these can be reported as summaries rather than in full detail), 5) the number of matched, unmatched, and discarded units included in the effect estimation, and 6) the method of estimating the treatment effect and standard error or confidence interval (including the specific model used and the specific type of standard error). See Thoemmes and Kim (2011) for a complete list of specific details to report. Below is an example of how we might write up the prior analysis:

We used propensity score matching to estimate the average marginal effect of the treatment on 1978 earnings on those who received it accounting for confounding by the included covariates. We first attempted 1:1 nearest neighbor propensity score matching without replacement with a propensity score estimated using logistic regression of the treatment on the covariates. This matching yielded poor balance, so we instead tried full matching on the propensity score, which yielded adequate balance, as indicated in Table 1 and Figure 1. The propensity score was estimated using a probit regression of the treatment on the covariates, which yielded better balance than did a logistic regression. After matching, all standardized mean differences for the covariates were below 0.1 and all standardized mean differences for squares and two-way interactions between covariates were below .15, indicating adequate balance. Full matching uses all treated and all control units, so no units were discarded by the matching.

To estimate the treatment effect and its standard error, we fit a linear regression model with 1978 earnings as the outcome and the treatment and the covariates as additive predictors and included the full matching weights in the estimation. The coefficient on the treatment was taken to be the estimate of the treatment effect. The lm() function was used to estimate the effect, and a cluster-robust variance as implemented in the vcovCL() function in the sandwich package was used to estimate its standard error with matching stratum membership as the clustering variable.

The estimated effect was $1980 (SE = 756.1, p = .009), indicating that the average effect of the treatment for those who received it is to increase earnings.

Conclusion

Although we have covered the basics of performing a matching analysis here, to use matching to its full potential, the more advanced methods available in MatchIt should be considered. We recommend reading the other vignettes included here to gain a better understand of all the MatchIt has to offer and how to use it responsibly and effectively. As previously stated, the ease of using MatchIt does not imply that matching or causal inference in general are simple matters; matching is an advanced statistical technique that should be used with care and caution. We hope the capabilities of MatchIt ease and encourage the use of nonparametric preprocessing for estimating causal effects in a robust and well-justified way.

References

Austin, Peter C. 2011. “An Introduction to Propensity Score Methods for Reducing the Effects of Confounding in Observational Studies.” Multivariate Behavioral Research 46 (3): 399–424. https://doi.org/10.1080/00273171.2011.568786.
Greifer, Noah, and Elizabeth A. Stuart. 2021. “Choosing the Estimand When Matching or Weighting in Observational Studies.” arXiv:2106.10577 [Stat], June. https://arxiv.org/abs/2106.10577.
Hansen, Ben B. 2004. “Full Matching in an Observational Study of Coaching for the SAT.” Journal of the American Statistical Association 99 (467): 609–18. https://doi.org/10.1198/016214504000000647.
Ho, Daniel E., Kosuke Imai, Gary King, and Elizabeth A. Stuart. 2007. “Matching as Nonparametric Preprocessing for Reducing Model Dependence in Parametric Causal Inference.” Political Analysis 15 (3): 199–236. https://doi.org/10.1093/pan/mpl013.
King, Gary, and Richard Nielsen. 2019. “Why Propensity Scores Should Not Be Used for Matching.” Political Analysis, May, 1–20. https://doi.org/10.1017/pan.2019.11.
Rosenbaum, Paul R., and Donald B. Rubin. 1983. “The Central Role of the Propensity Score in Observational Studies for Causal Effects.” Biometrika 70 (1): 41–55. https://doi.org/10.1093/biomet/70.1.41.
Rubin, Donald B. 1973. “Matching to Remove Bias in Observational Studies.” Biometrics 29 (1): 159. https://doi.org/10.2307/2529684.
Stuart, Elizabeth A. 2010. “Matching Methods for Causal Inference: A Review and a Look Forward.” Statistical Science 25 (1): 1–21. https://doi.org/10.1214/09-STS313.
Stuart, Elizabeth A., and Kerry M. Green. 2008. “Using Full Matching to Estimate Causal Effects in Nonexperimental Studies: Examining the Relationship Between Adolescent Marijuana Use and Adult Outcomes.” Developmental Psychology 44 (2): 395–406. https://doi.org/10.1037/0012-1649.44.2.395.
Thoemmes, Felix J., and Eun Sook Kim. 2011. “A Systematic Review of Propensity Score Methods in the Social Sciences.” Multivariate Behavioral Research 46 (1): 90–118. https://doi.org/10.1080/00273171.2011.540475.
VanderWeele, Tyler J. 2019. “Principles of Confounder Selection.” European Journal of Epidemiology 34 (3): 211–19. https://doi.org/10.1007/s10654-019-00494-6.

  1. Note that the default for method is "nearest" to perform nearest neighbor matching. To prevent any matching from taking place in order to assess pre-matching imbalance, method must be set to NULL.↩︎

  2. Note that setting distance = "logit", which was the default in MatchIt version prior to 4.0.0, will also estimate logistic regression propensity scores. Because it is the default, the distance argument can actually be omitted if logistic regression propensity scores are desired.↩︎

  3. With 1:1 nearest neighbor matching without replacement, excluding the matching weights does not change the estimates. For all other forms of matching, they are required, so we recommend always including them for consistency.↩︎

MatchIt/inst/doc/matching-methods.R0000644000176200001440000000040214170752610016702 0ustar liggesusers## ----setup, include=FALSE----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- knitr::opts_chunk$set(echo = TRUE) options(width = 200) MatchIt/inst/doc/sampling-weights.Rmd0000644000176200001440000003036714067017372017273 0ustar liggesusers--- title: "Matching with Sampling Weights" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Matching with Sampling Weights} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{=html} ``` ```{r, include = FALSE} knitr::opts_chunk$set(echo = TRUE, eval=T) options(width = 200, digits = 4) #Generatng data similar to Austin (2009) for demonstrating treatment effect estimation with sampling weights gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 gen_SW <- function(X) { e <- rbinom(nrow(X), 1, .3) 1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e + -log(.5)*e*X[,2] + log(.6)*e*X[,4]) } set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) SW <- gen_SW(X) Y_C <- gen_Y_C(A, X) d <- data.frame(A, X, Y_C, SW) ``` ## Introduction Sampling weights (also known as survey weights) frequently appear when using large, representative datasets. They are required to ensure any estimated quantities generalize to a target population defined by the weights. Evidence suggests that sampling weights need to be incorporated into a propensity score matching analysis to obtain valid and unbiased estimates of the treatment effect in the sampling weighted population [@dugoff2014; @austin2016; @lenis2019]. In this guide, we demonstrate how to use sampling weights with `MatchIt` for propensity score estimation, balance assessment, and effect estimation. Fortunately, doing so is not complicated, but some care must be taken to ensure sampling weights are incorporated correctly. It is assumed one has read the other vignettes explaining matching (`vignette("matching-methods")`), balance assessment (`vignette("assessing-balance")`), and effect estimation (`vignette("estimating-effects")`. We will use the same simulated toy dataset used in `vignette("estimating-effects")` except with the addition of a sampling weights variable, `SW`, which is used to generalize the sample to a specific target population with a distribution of covariates different from that of the sample. Code to generate the covariates, treatment, and outcome is at the bottom of `vignette("estimating-effects")` and code to generate the sampling weights is at the end of this document. We will consider the effect of binary treatment `A` on continuous outcome `Y_C`, adjusting for confounders `X1`-`X9`. ```{r,message=FALSE,warning=FALSE} head(d) library("MatchIt") library("lmtest") library("sandwich") ``` ## Matching When using sampling weights with propensity score matching, one has the option of including the sampling weights in the model used to estimate the propensity scores. Although evidence is mixed on whether this is required [@austin2016; @lenis2019], it can be a good idea. The choice should depend on whether including the sampling weights improves the quality of the matches. Specifications including and excluding sampling weights should be tried to determine which is preferred. To supply sampling weights to the propensity score-estimating function in `matchit()`, the sampling weights variable should be supplied to the `s.weights` argument. It can be supplied either as a numerical vector containing the sampling weights, or a string or one-sided formula with the name of the sampling weights variable in the supplied dataset. Below we demonstrate including sampling weights into propensity scores estimated using logistic regression for optimal full matching for the average treatment effect in the population (ATE) (note that all methods and steps apply the same way to all forms of matching and all estimands). ```{r} mF_s <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", distance = "glm", estimand = "ATE", s.weights = ~SW) mF_s ``` Notice that the description of the matching specification when the `matchit` object is printed includes lines indicating that the sampling weights were included in the estimation of the propensity score and that they are present in the `matchit` object. It is stored in the `s.weights` component of the `matchit` object. Note that at this stage, the matching weights (stored in the `weights` component of the `matchit` object) do not incorporate the sampling weights; they are calculated simply as a result of the matching. Now let's perform full matching on a propensity score that does not include the sampling weights in its estimation. Here we use the same specification as was used in `vignette("estimating-effects")`. ```{r} mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", distance = "glm", estimand = "ATE") mF ``` Notice that there is no mention of sampling weights in the description of the matching specification. However, to properly assess balance and estimate effects, we need the sampling weights to be included in the `matchit` object, even if they were not used at all in the matching. To do so, we use the function `add_s.weights()`, which adds sampling weights to the supplied `matchit` objects. ```{r} mF <- add_s.weights(mF, ~SW) mF ``` Now when we print the `matchit` object, we can see lines have been added identifying that sampling weights are present but they were not used in the estimation of the propensity score used in the matching. Note that not all methods can involve sampling weights in the estimation. Only methods that use the propensity score will be affected by sampling weights; coarsened exact matching or Mahalanobis distance optimal pair matching, for example, ignore the sampling weights, and some propensity score estimation methods, like `randomForest` and `bart` (as presently implemented), cannot incorporate sampling weights. Sampling weights should still be supplied to `matchit()` even when using these methods to avoid having to use `add_s.weights()` and remembering which methods do or do not involve sampling weights. ## Assessing Balance Now we need to decide which matching specification is the best to use for effect estimation. We do this by selecting the one that yields the best balance without sacrificing remaining effective sample size. Because the sampling weights are incorporated into the `matchit` object, the balance assessment tools in `plot.matchit()` and `summary.matchit()` incorporate them into their output. We'll use `summary()` to examine balance on the two matching specifications. With sampling weights included, the balance statistics for the unmatched data are weighted by the sampling weights. The balance statistics for the matched data are weighted by the product of the sampling weights and the matching weights. It is the product of these weights that will be used in estimating the treatment effect. Below we use `summary()` to display balance for the two matching specifications. No additional arguments to `summary()` are required for it to use the sampling weights; as long as they are in the `matchit` object (either due to being supplied with the `s.weights` argument in the call to `matchit()` or to being added afterward by `add_s.weights()`), they will be correctly incorporated into the balance statistics. ```{r} #Balance before matching and for the SW propensity score full matching summary(mF_s, improvement = FALSE) #Balance for the non-SW propensity score full matching summary(mF, un = FALSE) ``` The results of the two matching specifications are similar. Balance appears to be slightly better when using the sampling weight-estimated propensity scores than when using the unweighted propensity scores. However, the effective sample size for the control group is larger when using the unweighted propensity scores. Neither propensity score specification achieves excellent balance, and more fiddling with the matching specification (e.g., by changing the method of estimating propensity scores, the type of matching, or the options used with the matching) might yield a better matched set. For the purposes of this analysis, we will move forward with the matching that used the sampling weight-estimated propensity scores (`mF_s`) because of its superior balance. Some of the remaining imbalance may be eliminated by adjusting for the covariates in the outcome model. Note that had we not added sampling weights to `mF`, the matching specification that did not include the sampling weights, our balance assessment would be inaccurate because the balance statistics would not include the sampling weights. In this case, in fact, assessing balance on `mF` without incorporated the sampling weights would have yielded radically different results and a different conclusion. It is critical to incorporate sampling weights into the `matchit` object using `add_s.weights()` even if they are not included in the propensity score estimation. ## Estimating the Effect Estimating the treatment effect after matching is straightforward when using sampling weights. Effects are estimated in the same way as when sampling weights are excluded, except that the matching weights must be multiplied by the sampling weights to yield accurate, generalizable estimates. `match.data()` and `get_matches()` do this automatically, so the weights produced by these functions already are a product of the matching weights and the sampling weights. Note this will only be true if sampling weights are incorporated into the `matchit` object. Below we estimate the effect of `A` on `Y_C` in the matched and sampling weighted sample, adjusting for the covariates to improve precision and decrease bias. ```{r} md_F_s <- match.data(mF_s) fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_F_s, weights = weights) coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ``` Note that `match.data()` and `get_weights()` have the option `include.s.weights`, which, when set to `FALSE`, makes it so the returned weights do not incorporate the sampling weights and are simply the matching weights. To incorporate sampling weights into the effect estimation with this option, one must multiply the returned weights by the sampling weights when including them in the outcome model estimation. We demonstrate this below: ```{r} md_F_s <- match.data(mF_s, include.s.weights = FALSE) fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_F_s, weights = weights * SW) coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ``` We get the same estimates using this syntax as we did above. Because one might to forget to multiply the two sets of weights together, it is easier to just use the default of `include.s.weights = TRUE` and ignore the sampling weights in the rest of the analysis (because they are already included in the returned weights). ## Code to Generate Data used in Examples ```{r, eval = FALSE} #Generatng data similar to Austin (2009) for demonstrating #treatment effect estimation with sampling weights gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 gen_SW <- function(X) { e <- rbinom(nrow(X), 1, .3) 1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e + -log(.5)*e*X[,2] + log(.6)*e*X[,4]) } set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) SW <- gen_SW(X) Y_C <- gen_Y_C(A, X) d <- data.frame(A, X, Y_C, SW) ``` ## References MatchIt/inst/doc/sampling-weights.R0000644000176200001440000001161514170752615016746 0ustar liggesusers## ---- include = FALSE--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- knitr::opts_chunk$set(echo = TRUE, eval=T) options(width = 200, digits = 4) #Generatng data similar to Austin (2009) for demonstrating treatment effect estimation with sampling weights gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 gen_SW <- function(X) { e <- rbinom(nrow(X), 1, .3) 1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e + -log(.5)*e*X[,2] + log(.6)*e*X[,4]) } set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) SW <- gen_SW(X) Y_C <- gen_Y_C(A, X) d <- data.frame(A, X, Y_C, SW) ## ----message=FALSE,warning=FALSE---------------------------------------------------------------------------------------------------------------------------------------------------------------------- head(d) library("MatchIt") library("lmtest") library("sandwich") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mF_s <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", distance = "glm", estimand = "ATE", s.weights = ~SW) mF_s ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", distance = "glm", estimand = "ATE") mF ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mF <- add_s.weights(mF, ~SW) mF ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Balance before matching and for the SW propensity score full matching summary(mF_s, improvement = FALSE) #Balance for the non-SW propensity score full matching summary(mF, un = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- md_F_s <- match.data(mF_s) fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_F_s, weights = weights) coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- md_F_s <- match.data(mF_s, include.s.weights = FALSE) fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_F_s, weights = weights * SW) coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ## ---- eval = FALSE------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # #Generatng data similar to Austin (2009) for demonstrating # #treatment effect estimation with sampling weights # gen_X <- function(n) { # X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) # X[,5] <- as.numeric(X[,5] < .5) # X # } # # #~20% treated # gen_A <- function(X) { # LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + # log(2)*X[,7] - log(1.5)*X[,8] # P_A <- plogis(LP_A) # rbinom(nrow(X), 1, P_A) # } # # # Continuous outcome # gen_Y_C <- function(A, X) { # 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) # } # #Conditional: # # MD: 2 # #Marginal: # # MD: 2 # # gen_SW <- function(X) { # e <- rbinom(nrow(X), 1, .3) # 1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e + # -log(.5)*e*X[,2] + log(.6)*e*X[,4]) # } # # set.seed(19599) # # n <- 2000 # X <- gen_X(n) # A <- gen_A(X) # SW <- gen_SW(X) # # Y_C <- gen_Y_C(A, X) # # d <- data.frame(A, X, Y_C, SW) MatchIt/inst/doc/matching-methods.html0000644000176200001440000024653214170752611017466 0ustar liggesusers Matching Methods

Matching Methods

Noah Greifer

2022-01-16

Introduction

MatchIt implements several matching methods with a variety of options. Though the help pages for the individual methods describes each method and how they can be used, this vignette provides a broad overview of the available matching methods and their associated options. The choice of matching method depends on the goals of the analysis (e.g., the estimand, whether low bias or high precision is important) and the unique qualities of each dataset to be analyzed, so there is no single optimal choice for any given analysis. A benefit of nonparametric preprocessing through matching is that a number of matching methods can be tried and their quality assessed without consulting the outcome, reducing the possibility of capitalizing on chance while allowing for the benefits of an exploratory analysis in the design phase (Ho et al. 2007).

This vignette describes each matching method available in MatchIt and the various options that are allowed with matching methods and the consequences of their use. For a brief introduction to the use of MatchIt functions, see vignette("MatchIt"). For details on how to assess and report covariate balance, see vignette("assessing-balance"). For details on how to estimate treatment effects and standard errors after matching, see vignette("estimating-effects").

Matching

Matching as implemented in MatchIt is a form of subset selection, that is, the pruning and weighting of units to arrive at a (weighted) subset of the units from the original dataset. Ideally, and if done successfully, subset selection produces a new sample where the treatment is unassociated with the covariates so that a comparison of the outcomes treatment and control groups is not confounded by the measured and balanced covariates. Although statistical estimation methods like regression can also be used to remove confounding due to measured covariates, Ho et al. (2007) argue that fitting regression models in matched samples reduces the dependence of the validity of the estimated treatment effect on the correct specification of the model.

Matching is nonparametric in the sense that the estimated weights and pruning of the sample are not direct functions of estimated model parameters but rather depend on the organization of discrete units in the sample; this is in contrast to propensity score weighting (also known as inverse probability weighting), where the weights come more directly from the estimated propensity score model and therefore are more sensitive to its correct specification. These advantages, as well as the intuitive understanding of matching by the public compared to regression or weighting, make it a robust and effective way to estimate treatment effects.

It is important to note that this implementation of matching differs from the methods described by Abadie and Imbens (2006, 2016) and implemented in the Matching R package and teffects routine in Stata. That form of matching is matching imputation, where the missing potential outcomes for each unit are imputed using the observed outcomes of paired units. This is a critical distinction because matching imputation is a specific estimation method with its own effect and standard error estimators, in contrast to subset selection, which is a preprocessing method that does not require specific estimators and is broadly compatible with other parametric and nonparametric analyses. The benefits of matching imputation are that its theoretical properties (i.e., the rate of convergence and asymptotic variance of the estimator) are well understood, it can be used in a straightforward way to estimate not just the average treatment effect in the treated (ATT) but also the average treatment effect in the population (ATE), and additional effective matching methods can be used in the imputation (e.g., kernel matching). The benefits of matching as nonparametric preprocessing are that it is far more flexible with respect to the types of effects that can be estimated because it does not involve any specific estimator, its empirical and finite-sample performance has been examined in depth and is generally well understood, and it aligns well with the design of experiments, which are more familiar to non-technical audiences.

In addition to subset selection, matching often (though not always) involves a form of stratification, the assignment of units to pairs or strata containing multiple units. The distinction between subset selection and stratification is described by Zubizarreta, Paredes, and Rosenbaum (2014a), who separate them into two separate steps. In MatchIt, with almost all matching methods, subset selection is performed by stratification; for example, treated units are paired with control units, and unpaired units are then dropped from the matched sample. With some methods, subclasses are used to assign matching or stratification weights to individual units, which increase or decrease each unit’s leverage in a subsequent analysis. There has been some debate about the importance of stratification after subset selection; while some authors have argued that, with some forms of matching, pair membership is incidental (Stuart 2008; Schafer and Kang 2008), others have argued that correctly incorporating pair membership into effect estimation can improve the quality of inferences (Austin and Small 2014; Wan 2019). For methods that allow it, MatchIt includes stratum membership as an additional output of each matching specification. How these strata can be used is detailed in vignette("Estimating Effects").

At the heart of MatchIt are three classes of methods: distance matching, stratum matching, and pure subset selection. Distance matching involves considering a focal group (usually the treated group) and selecting members of the non-focal group (i.e., the control group) to pair with each member of the focal group based on the distance between units, which can be computed in one of several ways. Members of either group that are not paired are dropped from the sample. Nearest neighbor (method = "nearest"), optimal pair (method = "optimal"), optimal full (method = "full"), and genetic matching (method = "genetic") are the methods of distance matching implemented in MatchIt. Typically, only the average treatment in the treated (ATT) or average treatment in the control (ATC), if the control group is the focal group, can be estimated after distance matching in MatchIt (full matching is an exception, described later).

Stratum matching involves creating strata based on unique values of the covariates and assigning units with those covariate values into those strata. Any units that are in strata that lack either treated or control units are then dropped from the sample. Strata can be formed using the raw covariates (method = "exact"), coarsened versions of the covariates (method = "cem"), or coarsened versions of the propensity score (method = "subclass"). When no units are discarded, either the ATT, ATC, or average treatment effect in the population (ATE) can be estimated after stratum matching, though often some units are discarded, especially with exact and coarsened exact matching, making the estimand less clear. For use in estimating marginal treatment effects after exact matching, stratification weights are computed for the matched units first by computing a new “stratum propensity score” for each unit, which is the proportion of treated units in its stratum. The formulas for computing inverse probability weights from standard propensity scores are then applied to the new stratum propensity scores to form the new weights.

Pure subset selection involves selecting a subset of units form the original sample without considering the distance between individual units or strata that units might fall into. Subsets are selected to optimize a criterion subject to constraint on balance and remaining sample size. Cardinality and template matching (method = "cardinality") are the methods of pure subset selection implemented in MatchIt. Both methods allow the user to specify the largest imbalance allowed in the resulting matched sample, and an optimization routine attempts to find the largest matched sample that satisfies those balance constraints. While cardinality matching does not target a specific estimand, template matching can be used to target the ATT, ATC, or ATE.

Below, we describe each of the matching methods implemented in MatchIt.

Matching Methods

Nearest Neighbor Matching (method = "nearest")

Nearest neighbor matching is also known as greedy matching. It involves running through the list of treated units and selecting the closest eligible control unit to be paired with each treated unit. It is greedy in the sense that each pairing occurs without reference to how other units will be or have been paired, and therefore does not aim to optimize any criterion. Nearest neighbor matching is the most common form of matching used (Thoemmes and Kim 2011; Zakrison, Austin, and McCredie 2018) and has been extensively studied through simulations. See ?method_nearest for the documentation for matchit() with method = "nearest".

Nearest neighbor matching requires the specification of a distance measure to define which control unit is closest to each treated unit. The default and most common distance is the propensity score difference, which is the difference between the propensity scores of each treated and control unit (Stuart 2010). Another popular distance is the Mahalanobis distance, described in the section “Mahalanobis distance matching” below. The order in which the treated units are to be paired must also be specified and has the potential to change the quality of the matches (Austin 2013; Rubin 1973); this is specified by the m.order argument. With propensity score matching, the default is to go in descending order from the highest propensity score; doing so allows the units that would have the hardest time finding close matches to be matched first (Rubin 1973). Other orderings are possible, including random ordering, which can be tried multiple times until an adequate matched sample is found. When matching with replacement (i.e., where each control unit can be reused to be matched with any number of treated units), the matching order doesn’t matter.

When using a matching ratio greater than 1 (i.e., when more than 1 control units are requested to be matched to each treated unit), matching occurs in a cycle, where each treated unit is first paired with one control unit, and then each treated unit is paired with a second control unit, etc. Ties are broken deterministically based on the order of the units in the dataset to ensure that multiple runs of the same specification yield the same result (unless the matching order is requested to be random).

Optimal Pair Matching (method = "optimal")

Optimal pair matching (often just called optimal matching) is very similar to nearest neighbor matching in that it attempts to pair each treated unit with one or more control units. Unlike nearest neighbor matching, however, it is “optimal” rather than greedy; it is optimal in the sense that it attempts to choose matches that collectively optimize an overall criterion (Hansen and Klopfer 2006; Gu and Rosenbaum 1993). The criterion used is the sum of the absolute pair distances in the matched sample. See ?method_optimal for the documentation for matchit() with method = "optimal". Optimal pair matching in MatchIt depends on the fullmatch() function in the optmatch package (Hansen and Klopfer 2006).

Like nearest neighbor matching, optimal pair matching requires the specification of a distance measure between units. Optimal pair matching can be thought of simply as an alternative to selecting the order of the matching for nearest neighbor matching. Optimal pair matching and nearest neighbor matching often yield the same or very similar matched samples; indeed, some research has indicated that optimal pair matching is not much better than nearest neighbor matching at yielding balanced matched samples (Austin 2013).

The tol argument in fullmatch() can be supplied to matchit() with method = "optimal"; this controls the numerical tolerance used to determine whether the optimal solution has been found. The default is fairly high and, for smaller problems, should be set much lower (e.g., by setting tol = 1e-7).

Optimal Full Matching (method = "full")

Optimal full matching (often just called full matching) assigns every treated and control unit in the sample to one subclass each (Hansen 2004; Stuart and Green 2008). Each subclass contains one treated unit and one or more control units or one control units and one or more treated units. It is optimal in the sense that the chosen number of subclasses and the assignment of units to subclasses minimize the sum of the absolute within-subclass distances in the matched sample. Weights are computed based on subclass membership, and these weights then function like propensity score weights and can be used to estimate a weighted treatment effect, ideally free of confounding by the measured covariates. See ?method_full for the documentation for matchit() with method = "full". Optimal full matching in MatchIt depends on the fullmatch() function in the optmatch package (Hansen and Klopfer 2006).

Like the other distance matching methods, optimal full matching requires the specification of a distance measure between units. It can be seen a combination of distance matching and stratum matching: subclasses are formed with varying numbers of treated and control units, as with stratum matching, but the subclasses are formed based on minimizing within-pair distances and do not involve forming strata based on any specific variable, similar to distance matching. Unlike other distance matching methods, full matching can be used to estimate the ATE. Full matching can also be seen as a form of propensity score weighting that is less sensitive to the form of the propensity score model because the original propensity scores are used just to create the subclasses, not to form the weights directly (Austin and Stuart 2015a). In addition, full matching does not have to rely on estimated propensity scores to form the subclasses and weights; other distance measures are allowed as well.

Although full matching uses all available units, there is a loss in precision due to the weights. Units may be weighted in such a way that they contribute less to the sample than would unweighted units, so the effective sample size (ESS) of the full matching weighted sample may be lower than even that of 1:1 pair matching. Balance is often far better after full matching than it is with 1:k matching, making full matching a good option to consider especially when 1:k matching is not effective or when the ATE is the target estimand.

The specification of the full matching optimization problem can be customized by supplying additional arguments that are passed to optmatch::fullmatch(), such as min.controls, max.controls, mean.controls, and omit.fraction. As with optimal pair matching, the numerical tolerance value can be set much lower than the default with small problems by setting, e.g., tol = 1e-7.

Genetic Matching (method = "genetic")

Genetic matching is less a specific form of matching and more a way of specifying a distance measure for another form of matching. In practice, though, the form of matching used is nearest neighbor pair matching. Genetic matching uses a genetic algorithm, which is an optimization routine used for non-differentiable objective functions, to find scaling factors for each variable in a generalized Mahalanobis distance formula (Diamond and Sekhon 2013). The criterion optimized by the algorithm is one based on covariate balance. Once the scaling factors have been found, nearest neighbor matching is performed on the scaled generalized Mahalanobis distance. See ?method_genetic for the documentation for matchit() with method = "genetic". Genetic matching in MatchIt depends on the GenMatch() function in the Matching package (Sekhon 2011) to perform the genetic search and uses the Matching function to perform the nearest neighbor match using the scaled generalized Mahalanobis distance.

Genetic matching considers the generalized Mahalanobis distance between a treated unit \(i\) and a control unit \(j\) as \[\delta_{GMD}(\mathbf{x}_i,\mathbf{x}_j, \mathbf{W})=\sqrt{(\mathbf{x}_i - \mathbf{x}_j)'(\mathbf{S}^{-1/2})'\mathbf{W}(\mathbf{S}^{-1/2})(\mathbf{x}_i - \mathbf{x}_j)}\] where \(\mathbf{x}\) is a \(p \times 1\) vector containing the value of each of the \(p\) included covariates for that unit, \(\mathbf{S}^{-1/2}\) is the Cholesky decomposition of the covariance matrix \(\mathbf{S}\) of the covariates, and \(\mathbf{W}\) is a diagonal matrix with scaling factors \(w\) on the diagonal: \[ \mathbf{W}=\begin{bmatrix} w_1 & & & \\ & w_2 & & \\ & & \ddots &\\ & & & w_p \\ \end{bmatrix} \]

When \(w_k=1\) for all covariates \(k\), the computed distance is the standard Mahalanobis distance between units. Genetic matching estimates the optimal values of the \(w_k\)s, where a user-specified criterion is used to define what is optimal. The default is to maximize the smallest p-value among balance tests for the covariates in the matched sample (both Kolmogorov-Smirnov tests and t-tests for each covariate).

In MatchIt, if a propensity score is specified, the default is to include the propensity score and the covariates in \(\mathbf{x}\) and to optimize balance on the covariates. When distance = "mahalanobis" or the mahvars argument is specified, the propensity score is left out of \(\mathbf{x}\).

In all other respects, genetic matching functions just like nearest neighbor matching except that the matching itself is carried out by Matching::Match() instead of by MatchIt. When using method = "genetic" in MatchIt, additional arguments passed to Matching::GenMatch() to control the genetic search process should be specified; in particular, the pop.size argument should be increased from its default of 100 to a much higher value. Doing so will make the algorithm take more time to finish but will generally improve the quality of the resulting matches. Different functions can be supplied to be used as the objective in the optimization using the fit.func argument.

Exact Matching (method = "exact")

Exact matching is a form of stratum matching that involves creating subclasses based on unique combinations of covariate values and assigning each unit into their corresponding subclass so that only units with identical covariate values are placed into the same subclass. Any units that are in subclasses lacking either treated or control units will be dropped. Exact matching is the most powerful matching method in that no functional form assumptions are required on either the treatment or outcome model for the method to remove confounding due to the measured covariates; the covariate distributions are exactly balanced. The problem with exact matching is that in general, few if any units will remain after matching, so the estimated effect will only generalize to a very limited population and can lack precision. Exact matching is particularly ineffective with continuous covariates, for which it might be that no two units have the same value, and with many covariates, for which it might be the case that no two units have the same combination of all covariates; this latter problem is known as the “curse of dimensionality.” See ?method_exact for the documentation for matchit() with method = "exact".

It is possible to use exact matching on some covariates and another form of matching on the rest. This makes it possible to have exact balance on some covariates (typically categorical) and approximate balance on others, thereby gaining the benefits of both exact matching and the other matching method used. To do so, the other matching method should be specified in the method argument to matchit() and the exact argument should be specified to contain the variables on which exact matching is to be done.

Coarsened Exact Matching (method = "cem")

Coarsened exact matching (CEM) is a form of stratum matching that involves first coarsening the covariates by creating bins and then performing exact matching on the new coarsened versions of the covariates (Iacus, King, and Porro 2012). The degree and method of coarsening can be controlled by the user to manage the trade-off between exact and approximate balancing. For example, coarsening a covariate to two bins will mean that units that differ greatly on the covariate might be placed into the same subclass, while coarsening a variable to five bins may require units to be dropped due to not finding matches. Like exact matching, CEM is susceptible to the curse of dimensionality, making it a less viable solution with many covariates, especially with few units. Dropping units can also change the target population of the estimated effect. See ?method_cem for the documentation for matchit() with method = "cem". CEM in MatchIt does not depend on any other package to perform the coarsening and matching, though it used to rely on the cem package.

Subclassification (method = "subclass")

Propensity score subclassification can be thought of as a form of coarsened exact matching with the propensity score as the sole covariate to be coarsened and matched on. The bins are usually based on specified quantiles of the propensity score distribution either in the treated group, control group, or overall, depending on the desired estimand. Propensity score subclassification is an old and well-studied method, though it can perform poorly compared to other, more modern propensity score methods such as full matching and weighting (Austin 2010a). See ?method_subclass for the documentation for matchit() with method = "subclass".

The binning of the propensity scores is typically based on dividing the distribution of covariates into approximately equally sized bins. The user specifies the number of subclasses using the subclass argument and which group should be used to compute the boundaries of the bins using the estimand argument. Sometimes, subclasses can end up with no units from one of the treatment groups; by default, matchit() moves a unit from an adjacent subclass into the lacking one to ensure that each subclass has at least one unit from each treatment group. The minimum number of units required in each subclass can be chosen by the min.n argument to matchit(). If set to 0, an error will be thrown if any subclass lacks units from one of the treatment groups. Moving units from one subclass to another generally worsens the balance in the subclasses but can increase precision.

The default number of subclasses is 6, which is arbitrary and should not be taken as a recommended value. Although early theory has recommended the use of 5 subclasses, in general there is an optimal number of subclasses that is typically much larger than 5 but that varies among datasets (Orihara and Hamada 2021). Rather than trying to figure this out for oneself, one can use optimal full matching (i.e., with method = "full") to optimally create subclasses with one treated or one control unit that optimize a within-subclass distance criterion.

The output of propensity score subclassification includes the assigned subclasses and the subclassification weights. Effects can be estimated either within each subclass and then averaged across them, or a single marginal effect can be estimated using the subclassification weights. This latter method has been called marginal mean weighting through subclassification [MMWS; Hong (2010)] and fine stratification weighting (Desai et al. 2017). It is also implemented in the WeightIt package.

Cardinality and Template Matching (method = "cardinality")

Cardinality and template matching are pure subset selection methods that involve selecting a subset of the original sample without considering the distance between individual units or assigning units to pairs or subclasses. They can be thought of as a weighting method where the weights are restricted to be zero or one. Cardinality matching involves finding the largest sample that satisfies user-supplied balance constraints and constraints on the ratio of matched treated to matched control units (Zubizarreta, Paredes, and Rosenbaum 2014b). It does not consider a specific estimand and can be a useful alternative to matching with a caliper for handling data with little overlap (Visconti and Zubizarreta 2018). Template matching involves identifying a target distribution (e.g., the full sample for the ATE or the treated units for the ATT) and finding the largest subset of the treated and control groups that satisfy user-supplied balance constraints with respect to that target (Bennett, Vielma, and Zubizarreta 2020). See ?method_cardinality for the documentation for using matchit() with method = "cardinality", including which inputs are required to request either cardinality matching or template matching.

Subset selection is performed by solving a mixed integer programming optimization problem with linear constraints. The problem involves maximizing the size of the matched sample subject to constraints on balance and sample size. For cardinality matching, the balance constraints refer to the mean difference for each covariate between the matched treated and control groups, and the sample size constraints require the matched treated and control groups to be the same size (or differ by a user-supplied factor). For template matching, the balance constraints refer to the mean difference for each covariate between each treatment group and the target distribution; for the ATE, this requires the mean of each covariate in each treatment group to be within a given tolerance of the mean of the covariate in the full sample, and for the ATT, this requires the mean of each covariate in the control group to be within a given tolerance of the mean of the covariate in the treated group, which is left intact. The balance tolerances are controlled by the tols and std.tols arguments.

The optimization problem requires a special solver to solve. Currently, the available options in MatchIt are the GLPK solver (through the Rglpk package), the SYMPHONY solver (through the Rsymphony package), and the Gurobi solver (through the gurobi package). The differences among the solvers are in performance; Gurobi is by far the best (fastest, least likely to fail to find a solution), but it is proprietary (though has a free trial and academic license) and is a bit more complicated to install. The designmatch package also provides an implementation of cardinality matching with more options than MatchIt offers.

Customizing the Matching Specification

In addition to the specific matching method, other options are available for many of the matching methods to further customize the matching specification. These include different specifications of the distance measure, methods to perform alternate forms of matching in addition to the main method, prune units far from other units prior to matching, restrict possible matches, etc. Not all options are compatible with all matching methods.

Specifying the propensity score or other distance measure (distance)

The distance measure is used to define how close two units are. In nearest neighbor matching, this is used to choose the nearest control unit to each treated unit. In optimal matching, this is used in the criterion that is optimized. By default, the distance measure is the propensity score difference, and the argument supplied to distance corresponds to the method of estimating the propensity score. In MatchIt, propensity scores are often labeled as “distance” values, even though the propensity score itself is not a distance measure. This is to reflect that the propensity score is used in creating the distance value, but other scores could be used, such as prognostic scores for prognostic score matching (Hansen 2008). The propensity score is more like a “position” value, in that it reflects the position of each unit in the matching space, and the difference between positions is the distance between them. If the the argument to distance is one of the allowed values (see ?distance for these values) other than "mahalanobis" or is a numeric vector with one value per unit, the distance between units will be computed as the pairwise difference between propensity scores or the supplied values. Propensity scores are also used in propensity score subclassification and can optionally be used in genetic matching as a component of the generalized Mahalanobis distance. For exact, coarsened exact, and cardinality matching, the distance argument is ignored.

The default distance argument is "glm", which estimates propensity scores using logistic regression or another generalized linear model. The link and distance.options arguments can be supplied to further specify the options for the propensity score models, including whether to use the raw propensity score or a linearized version of it (e.g., the logit of a logistic regression propensity score, which has been commonly referred to and recommended in the propensity score literature (Austin 2011; Stuart 2010)). Allowable options for the propensity score model include parametric and machine learning-based models, each of which have their strengths and limitations and may perform differently depending on the unique qualities of each dataset. We recommend multiple types of models be tried to find one that yields the best balance, as there is no way to make a single recommendation that will work for all cases.

The distance argument can also be specified as "mahalanobis". For nearest neighbor and optimal (full or pair) matching, this triggers matchit() not to estimate propensity scores but rather to use just the Mahalanobis distance, which is defined for a treated unit \(i\) and a control unit \(j\) as \[\delta_{MD}(\mathbf{x}_i,\mathbf{x}_j)=\sqrt{(\mathbf{x}_i - \mathbf{x}_j)'S^{-1}(\mathbf{x}_i - \mathbf{x}_j)}\]

where \(\mathbf{x}\) is a \(p \times 1\) vector containing the value of each of the \(p\) included covariates for that unit, \(S\) is the covariance matrix among all the covariates, and \(S^{-1}\) is the (generalized) inverse of \(S\). The R function mahalanobis() is used to compute this. Mahalanobis distance matching tends to work better with continuous covariates than with categorical covariates. For creating close pairs, Mahalanobis distance matching tends work better than propensity score matching because Mahalanobis distance-paired units will have close values on all of the covariates, whereas propensity score-paired units may be close on the propensity score but not on any of the covariates themselves. This feature was the basis of King and Nielsen’s (2019) warning against using propensity scores for matching.

distance can also be supplied as a matrix of distance values between units. This makes it possible to use handcrafted distance matrices or distances created outside MatchIt, e.g., using optmatch::match_on(). Only nearest neighbor, optimal pair, and optimal full matching allow this specification.

The propensity score can have uses other than as the basis for matching. It can be used to define a region of common support, outside which units are dropped prior to matching; this is implemented by the discard option. It can also be used to define a caliper, the maximum distance two units can be before they are prohibited from being paired with each other; this is implemented by the caliper argument. To estimate or supply a propensity score for one of these purposes but not use it as the distance measure for matching (e.g., to perform Mahalanobis distance matching instead), the mahvars argument can be specified. These options are described below.

Implementing common support restrictions (discard)

The region of common support is the region of overlap between treatment groups. A common support restriction discards units that fall outside of the region of common support, preventing them from being matched to other units and included in the matched sample. This can reduce the potential for extrapolation and help the matching algorithms to avoid overly distant matches from occurring. In MatchIt, the discard option implements a common support restriction based on the propensity score. The argument can be supplied as "treated", "control", or "both", which discards units in the corresponding group that fall outside the region of common support for the propensity score. The reestimate argument can be supplied to choose whether to re-estimate the propensity score in the remaining units. If units from the treated group are discarded based on a common support restriction, the estimand no longer corresponds to the ATT.

Caliper matching (caliper)

A caliper can be though of as a ring around each unit that limits to which other units that unit can be paired. Calipers are based on the propensity score or other covariates. Two units whose distance on a calipered covariate is larger than the caliper width for that covariate are not allowed to be matched to each other. Any units for which there are no available matches within the caliper are dropped from the matched sample. Calipers ensure paired units are close to each other on the calipered covariates, which can ensure good balance in the matched sample. Multiple variables can be supplied to caliper to enforce calipers on all of them simultaneously. Using calipers can be a good alternative to exact or coarsened exact matching to ensure only similar units are paired with each other. The std.caliper argument controls whether the provided calipers are in raw units or standard deviation units. If units from the treated group are left unmatched due to a caliper, the estimand no longer corresponds to the ATT.

Mahalanobis distance matching (mahvars)

To perform Mahalanobis distance matching without the need to estimate or use a propensity score, the distance argument can be set to "mahalanobis". If a propensity score is to be estimated or used for a different purpose, such as in a common support restriction or a caliper, but you still want to perform Mahalanobis distance matching, variables should be supplied to the mahvars argument. The propensity scores will be generated using the distance specification, and matching will occur not on the covariates supplied to the main formula of matchit() but rather on the covariates supplied to mahvars. To perform Mahalanobis distance matching within a propensity score caliper, for example, the distance argument should be set to the method of estimating the propensity score (e.g., "glm" for logistic regression), the caliper argument should be specified to the desired caliper width, and mahvars should be specified to perform Mahalanobis distance matching on the desired covariates within the caliper.

Exact matching (exact)

To perform exact matching on all supplied covariates, the method argument can be set to "exact". To perform exact matching only on some covariates and some other form of matching within exact matching strata on other covariates, the exact argument can be used. Covariates supplied to the exact argument will be matched exactly, and the form of matching specified by method (e.g., "nearest" for nearest neighbor matching) will take place within each exact matching stratum. This can be a good way to gain some of the benefits of exact matching without completely succumbing to the curse of dimensionality. As with exact matching performed with method = "exact", any units in strata lacking members of one of the treatment groups will be left unmatched. Note that although matching occurs within each exact matching stratum, propensity score estimation and computation of the Mahalanobis distance occur in the full sample. If units from the treated group are unmatched due to an exact matching restriction, the estimand no longer corresponds to the ATT.

Anti-exact matching (antiexact)

Anti-exact matching adds a restriction such that a treated and control unit with same values of any of the specified anti-exact matching variables cannot be paired. This can be useful when finding comparison units outside of a unit’s group, such as when matching units in one group to units in another when units within the same group might otherwise be close matches. See examples here and here.

Matching with replacement (replace)

Nearest neighbor matching and genetic matching have the option of matching with or without replacement, and this is controlled by the replace argument. Matching without replacement means that each control unit is matched to only one treated unit, while matching with replacement means that control units can be reused and matched to multiple treated units. Matching without replacement carries certain statistical benefits in that weights for each unit can be omitted or are more straightforward to include and dependence between units depends only on pair membership. Special standard error estimators are sometimes required for estimating effects after matching with replacement (Austin and Cafri 2020), and methods for accounting for uncertainty are not well understood for non-continuous outcomes. Matching with replacement will tend to yield better balance though, because the problem of “running out” of close control units to match to treated units is avoided, though the reuse of control units will decrease the effect sample size, thereby worsening precision. (This problem occurs in the Lalonde dataset used in vignette("MatchIt"), which is why nearest neighbor matching without replacement is not very effective there.) After matching with replacement, control units are assigned to more than one subclass, so the get_matches() function should be used instead of match.data() after matching with replacement if subclasses are to be used in follow-up analyses; see vignette("Estimating Effects") for details.

The reuse.max argument can also be used with method = "nearest" to control how many times each control unit can be reused as a match. Setting reuse.max = 1 is equivalent to requiring matching without replacement (i.e., because each control can be used only once). Other values allow control units to be matched more than once, though only up to the specified number of times. Higher values will tend to improve balance at the cost of precision.

\(k\):1 matching (ratio)

The most common form of matching, 1:1 matching, involves pairing one control unit with each treated unit. To perform \(k\):1 matching (e.g., 2:1 or 3:1), which pairs (up to) \(k\) control units with each treated unit, the ratio argument can be specified. Performing \(k\):1 matching can preserve precision by preventing too many control units from being unmatched and dropped from the matched sample, though the gain in precision by increasing \(k\) diminishes rapidly after 4 (Rosenbaum 2020). Importantly, for \(k>1\), the matches after the first match will generally be worse than the first match in terms of closeness to the treated unit, so increasing \(k\) can also worsen balance. Austin (2010b) found that 1:1 or 1:2 matching generally performed best in terms of mean squared error. In general, it makes sense to use higher values of \(k\) while ensuring that balance is satisfactory. With nearest neighbor and optimal pair matching, variable \(k\):1 matching, in which the number of controls matched to each treated unit varies, can also be used; this can have improved performance over “fixed” \(k\):1 matching (Ming and Rosenbaum 2000). See ?method_nearest and ?method_optimal for information on implementing variable \(k\):1 matching.

Choosing a Matching Method

Choosing the best matching method for one’s data depends on the unique characteristics of the dataset as well as the goals of the analysis. For example, because different matching methods can target different estimands, when certain estimands are desired, specific methods must be used. On the other hand, some methods may be more effective than others when retaining the target estimand is less important. Below we provide some guidance on choosing a matching method. Remember that multiple methods can (and should) be tried as long as the treatment effect is not estimated until a method has been settled on.

The criteria on which a matching specification should be judged are balance and remaining (effective) sample size after matching. Assessing balance is described in vignette("assessing-balance"). A typical workflow is similar to that demonstrated in vignette("MatchIt"): try a matching method, and if it yields poor balance or an unacceptably low remaining sample size, try another, until a satisfactory specification has been found. It is important to assess balance broadly (i.e., beyond comparing the means of the covariates in the treated and control groups), and the search for a matching specification should not stop when a threshold is reached, but should attempt to come as close as possible to perfect balance (Ho et al. 2007). Even if the first matching specification appears successful at reducing imbalance, there may be another specification that could reduce it even further, thereby increasing the robustness of the inference and the plausibility of an unbiased effect estimate.

If the target of inference is the ATE, full matching, subclassification, and template matching can be used. If the target of inference is the ATT or ATC, any matching method may be used. When retaining the target estimand is not so important, additional options become available that involve discarding units in such a way that the original estimand is distorted. These include matching with a caliper, matching within a region of common support, cardinality matching, or exact or coarsened exact matching, perhaps on a subset of the covariates.

Because exact and coarsened exact matching aim to balance the entire joint distribution of covariates, they are the most powerful methods. If it is possible to perform exact matching, this method should be used. If continuous covariates are present, coarsened exact matching can be tried. Care should be taken with retaining the target population and ensuring enough matched units remain; unless the control pool is much larger than the treated pool, it is likely some (or many) treated units will be discarded, thereby changing the estimand and possibly dramatically reducing precision. These methods are typically only available in the most optimistic of circumstances, but they should be used first when those circumstances arise. It may also be useful to combine exact or coarsened exact matching on some covariates with another form of matching on the others (i.e., by using the exact argument).

When estimating the ATE, either subclassification, full matching, or template matching can be used. Full matching can be effective because it optimizes a balance criterion, often leading to better balance. With full matching, it’s also possible to exact match on some variables and match using the Mahalanobis distance, eliminating the need to estimate propensity scores. Template matching also ensures good balance, but because units are only given weights of zero one, a solution may not be feasible and many units may have to be discarded. For large datasets, neither full matching nor template matching may be possible, in which case subclassification is a faster solution. When using subclassification, the number of subclasses should be varied. With large samples, higher numbers of subclasses tend to yield better performance; one should not immediately settle for the default (6) or the often-cited recommendation of 5 without trying several other numbers.

When estimating the ATT, a variety of methods can be tried. Genetic matching can perform well at achieving good balance because it directly optimizes covariate balance. With larger datasets, it may take a long time to reach a good solution (though that solution will tend to be good as well). Template matching also will achieve good balance if a solution is feasible because balance is controlled by the user. Optimal pair matching and nearest neighbor matching without replacement tend to perform similarly to each other; nearest neighbor matching may be preferable for large datasets that cannot be handled by optimal matching. Nearest neighbor, optimal, and genetic matching allow some customizations like including covariates on which to exactly match, using the Mahalanobis distance instead of a propensity score difference, and performing \(k\):1 matching with \(k>1\). Nearest neighbor matching with replacement, full matching, and subclassification all involve weighting the control units with nonuniform weights, which often allows for improved balancing capabilities but can be accompanied by a loss in effective sample size, even when all units are retained. There is no reason not to try many of these methods, varying parameters here and there, in search of good balance and high remaining sample size. As previously mentioned, no single method can be recommended above all others because the optimal specification depends on the unique qualities of each dataset.

When the target population is less important, for example, when engaging in treatment effect discovery or when when the sampled population is not of particular interest (e.g., it corresponds to an arbitrarily chosen hospital or school; see Mao, Li, and Greene (2018) for these and other reasons why retaining the target population may not be important), other methods that do not retain the characteristics of the original sample become available. These include matching with a caliper (on the propensity score or on the covariates themselves), cardinality matching, and more restrictive forms of matching like exact and coarsened exact matching, either on all covariates or just a subset, that are prone to discard units from the sample in such a way that the target population is changed. Austin (2013) and Austin and Stuart (2015b, 2015a) have found that caliper matching can be a particularly effective modification to nearest neighbor matching for eliminating imbalance and reducing bias when the target population is less relevant, but when inference to a specific target population is desired, using calipers can induce bias due to incomplete matching (Rosenbaum and Rubin 1985a; Wang 2020). Cardinality matching can be particularly effective in data with little overlap between the treatment groups (Visconti and Zubizarreta 2018) and can perform better than caliper matching (de los Angeles Resa and Zubizarreta 2020).

It is important not to rely excessively on theoretical or simulation-based findings or specific recommendations when making choices about the best matching method to use. For example, although nearest neighbor matching without replacement balance covariates better than did subclassification with five or ten subclasses in Austin’s (2009) simulation, this does not imply it will be superior in all datasets. Likewise, though Rosenbaum and Rubin (1985b) and Austin (2011) both recommend using a caliper of .2 standard deviations of the logit of the propensity score, this does not imply that caliper will be optimal in all scenarios, and other widths should be tried, though it should be noted that tightening the caliper on the propensity score can sometimes degrade performance (King and Nielsen 2019).

Reporting the Matching Specification

When reporting the results of a matching analysis, it is important to include the relevant details of the final matching specification and the process of arriving at it. Using print() on the matchit object synthesizes information on how the above arguments were used to provide a description of the matching specification. It is best to be as specific as possible to ensure the analysis is replicable and to allow audiences to assess its validity. Although citations recommending specific matching methods can be used to help justify a choice, the only sufficient justification is adequate balance and remaining sample size, regardless of published recommendations for specific methods. See vignette("assessing-balance") for instructions on how to assess and report the quality of a matching specification. After matching and estimating an effect, details of the effect estimation must be included as well; see vignette("estimating-effects") for instructions on how to perform and report on the analysis of a matched dataset.

References

Abadie, Alberto, and Guido W. Imbens. 2006. “Large Sample Properties of Matching Estimators for Average Treatment Effects.” Econometrica 74 (1): 235–67. https://doi.org/10.1111/j.1468-0262.2006.00655.x.
———. 2016. “Matching on the Estimated Propensity Score.” Econometrica 84 (2): 781–807. https://doi.org/10.3982/ECTA11293.
Austin, Peter C. 2009. “The Relative Ability of Different Propensity Score Methods to Balance Measured Covariates Between Treated and Untreated Subjects in Observational Studies.” Medical Decision Making 29 (6): 661–77. https://doi.org/10.1177/0272989x09341755.
———. 2010a. “The Performance of Different Propensity-Score Methods for Estimating Differences in Proportions (Risk Differences or Absolute Risk Reductions) in Observational Studies.” Statistics in Medicine 29 (20): 2137–48. https://doi.org/10.1002/sim.3854.
———. 2010b. “Statistical Criteria for Selecting the Optimal Number of Untreated Subjects Matched to Each Treated Subject When Using Many-to-One Matching on the Propensity Score.” American Journal of Epidemiology 172 (9): 1092–97. https://doi.org/10.1093/aje/kwq224.
———. 2011. “Optimal Caliper Widths for Propensity-Score Matching When Estimating Differences in Means and Differences in Proportions in Observational Studies.” Pharmaceutical Statistics 10 (2): 150–61. https://doi.org/10.1002/pst.433.
———. 2013. “A Comparison of 12 Algorithms for Matching on the Propensity Score.” Statistics in Medicine 33 (6): 1057–69. https://doi.org/10.1002/sim.6004.
Austin, Peter C., and Guy Cafri. 2020. “Variance Estimation When Using Propensity-Score Matching with Replacement with Survival or Time-to-Event Outcomes.” Statistics in Medicine 39 (11): 1623–40. https://doi.org/10.1002/sim.8502.
Austin, Peter C., and Dylan S. Small. 2014. “The Use of Bootstrapping When Using Propensity-Score Matching Without Replacement: A Simulation Study.” Statistics in Medicine 33 (24): 4306–19. https://doi.org/10.1002/sim.6276.
Austin, Peter C., and Elizabeth A. Stuart. 2015a. “The Performance of Inverse Probability of Treatment Weighting and Full Matching on the Propensity Score in the Presence of Model Misspecification When Estimating the Effect of Treatment on Survival Outcomes.” Statistical Methods in Medical Research 26 (4): 1654–70. https://doi.org/10.1177/0962280215584401.
———. 2015b. “Estimating the Effect of Treatment on Binary Outcomes Using Full Matching on the Propensity Score.” Statistical Methods in Medical Research 26 (6): 2505–25. https://doi.org/10.1177/0962280215601134.
Bennett, Magdalena, Juan Pablo Vielma, and José R. Zubizarreta. 2020. “Building Representative Matched Samples With Multi-Valued Treatments in Large Observational Studies.” Journal of Computational and Graphical Statistics, May, 1–29. https://doi.org/10.1080/10618600.2020.1753532.
de los Angeles Resa, María, and José R. Zubizarreta. 2020. “Direct and Stable Weight Adjustment in Non-Experimental Studies with Multivalued Treatments: Analysis of the Effect of an Earthquake on Post-Traumatic Stress.” Journal of the Royal Statistical Society: Series A (Statistics in Society) n/a (n/a). https://doi.org/10.1111/rssa.12561.
Desai, Rishi J., Kenneth J. Rothman, Brian T. Bateman, Sonia Hernandez-Diaz, and Krista F. Huybrechts. 2017. “A Propensity-Score-Based Fine Stratification Approach for Confounding Adjustment When Exposure Is Infrequent:” Epidemiology 28 (2): 249–57. https://doi.org/10.1097/EDE.0000000000000595.
Diamond, Alexis, and Jasjeet S. Sekhon. 2013. “Genetic Matching for Estimating Causal Effects: A General Multivariate Matching Method for Achieving Balance in Observational Studies.” Review of Economics and Statistics 95 (3): 932945. https://doi.org/10.1162/REST_a_00318.
Gu, Xing Sam, and Paul R. Rosenbaum. 1993. “Comparison of Multivariate Matching Methods: Structures, Distances, and Algorithms.” Journal of Computational and Graphical Statistics 2 (4): 405. https://doi.org/10.2307/1390693.
Hansen, Ben B. 2004. “Full Matching in an Observational Study of Coaching for the SAT.” Journal of the American Statistical Association 99 (467): 609–18. https://doi.org/10.1198/016214504000000647.
———. 2008. “The Prognostic Analogue of the Propensity Score.” Biometrika 95 (2): 481–88. https://doi.org/10.1093/biomet/asn004.
Hansen, Ben B., and Stephanie O. Klopfer. 2006. “Optimal Full Matching and Related Designs via Network Flows.” Journal of Computational and Graphical Statistics 15 (3): 609–27. https://doi.org/10.1198/106186006X137047.
Ho, Daniel E., Kosuke Imai, Gary King, and Elizabeth A. Stuart. 2007. “Matching as Nonparametric Preprocessing for Reducing Model Dependence in Parametric Causal Inference.” Political Analysis 15 (3): 199–236. https://doi.org/10.1093/pan/mpl013.
Hong, Guanglei. 2010. “Marginal Mean Weighting Through Stratification: Adjustment for Selection Bias in Multilevel Data.” Journal of Educational and Behavioral Statistics 35 (5): 499–531. https://doi.org/10.3102/1076998609359785.
Iacus, Stefano M., Gary King, and Giuseppe Porro. 2012. “Causal Inference Without Balance Checking: Coarsened Exact Matching.” Political Analysis 20 (1): 1–24. https://doi.org/10.1093/pan/mpr013.
King, Gary, and Richard Nielsen. 2019. “Why Propensity Scores Should Not Be Used for Matching.” Political Analysis, May, 1–20. https://doi.org/10.1017/pan.2019.11.
Mao, Huzhang, Liang Li, and Tom Greene. 2018. “Propensity Score Weighting Analysis and Treatment Effect Discovery.” Statistical Methods in Medical Research, June, 096228021878117. https://doi.org/10.1177/0962280218781171.
Ming, Kewei, and Paul R. Rosenbaum. 2000. “Substantial Gains in Bias Reduction from Matching with a Variable Number of Controls.” Biometrics 56 (1): 118–24. https://doi.org/10.1111/j.0006-341X.2000.00118.x.
Orihara, Shunichiro, and Etsuo Hamada. 2021. “Determination of the Optimal Number of Strata for Propensity Score Subclassification.” Statistics & Probability Letters 168 (January): 108951. https://doi.org/10.1016/j.spl.2020.108951.
Rosenbaum, Paul R. 2020. “Modern Algorithms for Matching in Observational Studies.” Annual Review of Statistics and Its Application 7 (1): 143–76. https://doi.org/10.1146/annurev-statistics-031219-041058.
Rosenbaum, Paul R., and Donald B. Rubin. 1985a. “The Bias Due to Incomplete Matching.” Biometrics 41 (1): 103–16. https://doi.org/10.2307/2530647.
———. 1985b. “Constructing a Control Group Using Multivariate Matched Sampling Methods That Incorporate the Propensity Score.” The American Statistician 39 (1): 33. https://doi.org/10.2307/2683903.
Rubin, Donald B. 1973. “Matching to Remove Bias in Observational Studies.” Biometrics 29 (1): 159. https://doi.org/10.2307/2529684.
Schafer, Joseph L., and Joseph Kang. 2008. “Average Causal Effects from Nonrandomized Studies: A Practical Guide and Simulated Example.” Psychological Methods 13 (4): 279–313. https://doi.org/10.1037/a0014268.
Sekhon, Jasjeet S. 2011. “Multivariate and Propensity Score Matching Software with Automated Balance Optimization: The Matching Package for R.” Journal of Statistical Software 42 (1): 1–52. https://doi.org/10.18637/jss.v042.i07.
Stuart, Elizabeth A. 2008. “Developing Practical Recommendations for the Use of Propensity Scores: Discussion of A Critical Appraisal of Propensity Score Matching in the Medical Literature Between 1996 and 2003 by Peter Austin,Statistics in Medicine.” Statistics in Medicine 27 (12): 2062–65. https://doi.org/10.1002/sim.3207.
———. 2010. “Matching Methods for Causal Inference: A Review and a Look Forward.” Statistical Science 25 (1): 1–21. https://doi.org/10.1214/09-STS313.
Stuart, Elizabeth A., and Kerry M. Green. 2008. “Using Full Matching to Estimate Causal Effects in Nonexperimental Studies: Examining the Relationship Between Adolescent Marijuana Use and Adult Outcomes.” Developmental Psychology 44 (2): 395–406. https://doi.org/10.1037/0012-1649.44.2.395.
Thoemmes, Felix J., and Eun Sook Kim. 2011. “A Systematic Review of Propensity Score Methods in the Social Sciences.” Multivariate Behavioral Research 46 (1): 90–118. https://doi.org/10.1080/00273171.2011.540475.
Visconti, Giancarlo, and José R. Zubizarreta. 2018. “Handling Limited Overlap in Observational Studies with Cardinality Matching.” Observational Studies 4 (1): 217–49. https://doi.org/10.1353/obs.2018.0012.
Wan, Fei. 2019. “Matched or Unmatched Analyses with Propensity-Scorematched Data?” Statistics in Medicine 38 (2): 289–300. https://doi.org/10.1002/sim.7976.
Wang, Jixian. 2020. “To Use or Not to Use Propensity Score Matching?” Pharmaceutical Statistics, August. https://doi.org/10.1002/pst.2051.
Zakrison, T. L., Peter C. Austin, and V. A. McCredie. 2018. “A Systematic Review of Propensity Score Methods in the Acute Care Surgery Literature: Avoiding the Pitfalls and Proposing a Set of Reporting Guidelines.” European Journal of Trauma and Emergency Surgery 44 (3): 385–95. https://doi.org/10.1007/s00068-017-0786-6.
Zubizarreta, José R., Ricardo D. Paredes, and Paul R. Rosenbaum. 2014a. “Matching for Balance, Pairing for Heterogeneity in an Observational Study of the Effectiveness of for-Profit and Not-for-Profit High Schools in Chile.” The Annals of Applied Statistics 8 (1): 204–31. https://doi.org/10.1214/13-AOAS713.
———. 2014b. “Matching for Balance, Pairing for Heterogeneity in an Observational Study of the Effectiveness of for-Profit and Not-for-Profit High Schools in Chile.” The Annals of Applied Statistics 8 (1): 204–31. https://doi.org/10.1214/13-AOAS713.
MatchIt/inst/doc/estimating-effects.Rmd0000644000176200001440000024562714170731327017577 0ustar liggesusers--- title: "Estimating Effects After Matching" author: "Noah Greifer" date: "`r Sys.Date()`" output: html_vignette: toc: true vignette: > %\VignetteIndexEntry{Estimating Effects After Matching} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib link-citations: true --- ```{=html} ``` ```{r, include = FALSE} knitr::opts_chunk$set(echo = TRUE, eval=T) options(width = 200, digits= 4) #Generating data similar to Austin (2009) for demonstrating treatment effect estimation gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 # Binary outcome gen_Y_B <- function(A, X) { LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] P_B <- plogis(LP_B) rbinom(length(A), 1, P_B) } #Conditional: # OR: 2.4 # logOR: .875 #Marginal: # RD: .144 # RR: 1.54 # logRR: .433 # OR: 1.92 # logOR .655 # Survival outcome gen_Y_S <- function(A, X) { LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] sqrt(-log(runif(length(A)))*2e4*exp(-LP_S)) } #Conditional: # HR: 2.4 # logHR: .875 #Marginal: # HR: 1.57 # logHR: .452 set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) Y_C <- gen_Y_C(A, X) Y_B <- gen_Y_B(A, X) Y_S <- gen_Y_S(A, X) d <- data.frame(A, X, Y_C, Y_B, Y_S) ``` ## Introduction After assessing balance and deciding on a matching specification, it comes time to estimate the effect of the treatment in the matched sample. How the effect is estimated and interpreted depends on the desired estimand and the type of model used (if any). In addition to estimating effects, estimating the uncertainty of the effects is critical in communicating them and assessing whether the observed effect is compatible with there being no effect in the population. This guide explains how to estimate effects after various forms of matching and with various outcome types. There may be situations that are not covered here for which additional methodological research may be required, but some of the recommended methods here can be used to guide such applications. This guide is structured as follows: first, information on the concepts related to effect and standard error estimation is presented below. Then, instructions for how to estimate effects and standard errors are described. This section is split up first by the type of matching method performed (matching without replacement, matching with replacement, full matching, and subclassification) and then by the type of outcome (continuous, binary, and survival). Finally recommendations for reporting results and tips to avoid making common mistakes are presented. ### Identifying the estimand Before an effect is estimated, the estimand must be specified and clarified. Although some aspects of the estimand depend not only on how the effect is estimated after matching but also on the matching method itself, other aspects must be considered at the tine of effect estimation and interpretation. Here, we consider three aspects of the estimand: the population the effect is meant to generalize to (the target population), the effect measure, and whether the effect is marginal or conditional. **The target population.** Different matching methods allow you to estimate effects that can generalize to different target populations. The most common estimand in matching the average treatment effect in the treated (ATT), which is the average effect of treatment for those who receive treatment. This estimand is estimable for matching methods that do not change the treated units (i.e., by weighting or discarding units) and is requested in `matchit()` by setting `estimand = "ATT"` (which is the default). The average treatment effect in the population (ATE) is the average effect of treatment for the population from which the sample is a random sample. This estimand is estimable only for methods that allow the ATE and either do not discard units from the sample or explicit target full sample balance, which in `MatchIt` is limited to full matching, subclassification, and template matching when setting `estimand = "ATE"`. When treated units are discarded (e.g., through the use of common support restrictions, calipers, cardinality matching, or [coarsened] exact matching), the estimand corresponds to neither the population ATT nor the population ATE, but rather to an average treatment effect in the remaining matched sample (ATM), which may not correspond to any specific target population. **Marginal and conditional effects.** A marginal effect is a comparison between the expected potential outcome under treatment and the expected potential outcome under control. This is the same quantity estimated in randomized trials without blocking or covariate adjustment and is particularly useful for quantifying the overall effect of a policy or population-wide intervention. A conditional effect is the comparison between the expected potential outcomes in the treatment groups within strata. This is useful for identifying the effect of a treatment for an individual patient or a subset of the population. **Effect measures.** The outcome types we consider here are continuous, with the effect measured by the mean difference; binary, with the effect measured by the risk difference (RD), risk ratio (RR), or odds ratio (OR); and time-to-event (i.e., survival), with the effect measured by the hazard ratio (HR). The RR, OR, and HR are *noncollapsible* effect measures, which means the marginal effect on that scale is not a (possibly) weighted average of the conditional effects within strata, even if the stratum-specific effects are of the same magnitude. For these effect measures, it is critical to distinguish between marginal and conditional effects because different statistical methods target different types of effects. The mean difference and RD are *collapsible* effect measures, so the same methods can be used to estimate marginal and conditional effects. In this guide, we will provide examples for estimating the ATT, ATE, and ATM for each of the three outcomes types. Our primary focus will be on marginal effects, which are appropriate for all effect measures, easily interpretable, and require few modeling assumptions. We will include a short section on estimating the conditional OR. The "Common Mistakes" section includes examples of commonly used methods that estimate conditional rather than marginal effects and should not be used when marginal effects are desired. ### Modeling the Outcome The type and form of the model used to estimate the treatment effect depend on the effect measure desired, whether a marginal or conditional effect is desired, whether covariates are to be included in the model, and whether effect modification by the covariates is present. For continuous outcomes, one can use a linear model regressing the outcome on the treatment; for binary outcomes, one can use a generalized linear model with a link function appropriate for the desired effect measure (e.g., a logistic link for the OR); for time-to-event outcomes, one can use a Cox proportional hazards model. An additional decision to make is whether (and how) to include covariates in the outcome model. One may ask, why use matching at all if you are going to model the outcome with covariates anyway? Matching reduces the dependence of the effect estimate on correct specification of the outcome model; this is the central thesis of @ho2007. Including covariates in the outcome model after matching has several functions: it can increase precision in the effect estimate, reduce the bias due to residual imbalance, and make the effect estimate "doubly robust", which means it is consistent if either the matching reduces sufficient imbalance in the covariates or if the outcome model is correct. For these reasons, we recommend covariate adjustment after matching when possible. There is some evidence that covariate adjustment is most helpful for covariates with standardized mean differences greater than .1 [@nguyen2017], so these covariates and covariates thought to be highly predictive of the outcome should be prioritized in treatment effect models if not all can be included due to sample size constraints. For continuous outcomes, we can simply include the covariates in the regression of the outcome on the treatment in the matched sample (i.e., using the matching weights). Because the mean difference is collapsible, the effect estimate conditioning on the covariates is still a marginal effect estimate. This is not the case with binary and time-to-event outcomes; including covariates in the outcome model makes the treatment effect a conditional effect. To recover a marginal effect while including covariates in an outcome model, one has to perform a marginal effects procedure, also known as g-computation [@snowden2011] or regression estimation [@schafer2008], which involves simulating the average potential outcomes under each treatment level and computing the effect as a contrast between those average potential outcomes. G-computation can also be used to estimate marginal effects when modeling effect modification by the covariates. We demonstrate examples of this below. Although there are many possible ways to include covariates (i.e., not just main effects but interactions, smoothing terms like splines, or other nonlinear transformations), it is important not to engage in specification search (i.e., trying many outcomes models in search of the "best" one). Doing so can invalidate results and yield a conclusion that fails to replicate. For this reason, we recommend only including the same terms included in the propensity score model unless there is a strong *a priori* and justifiable reason to model the outcome differently. Second, it is important not to interpret the coefficients and tests of the other covariates in the outcome model. These are not causal effects and their estimates may be severely confounded. Only the treatment effect estimate can be interpreted as causal assuming the relevant assumptions about unconfoundedness are met. Inappropriately interpreting the coefficients of covariates in the outcome model is known as the Table 2 fallacy [@westreich2013]. To avoid this, in all examples that incorporate covariates in the outcome model, we restrict the output of outcome regression models to just the treatment coefficient. ### Estimating Standard Errors and Confidence Intervals Uncertainty estimation (i.e., of standard errors, confidence intervals, and p-values) may consider the variety of sources of uncertainty present in the analysis, including (but not limited to!) estimation of the propensity score (if used), matching (i.e., because treated units might be matched to different control units if others had been sampled), and estimation of the treatment effect (i.e., because of sampling error). In general, there are no analytic solutions to all these issues, so much of the research done on uncertainty estimation after matching has relied on simulation studies. The two primary methods that have been shown to perform well in matched samples are using cluster-robust standard errors and the bootstrap. #### Robust and Cluster-Robust Standard Errors **Robust standard errors.** Also known as sandwich standard errors (due to the form of the formula for computing them), heteroscedasticity-consistent standard errors, or Huber-White standard errors, robust standard errors are an adjustment to the usual maximum likelihood or ordinary least squares standard errors that are robust to violations of some of the assumptions required for usual standard errors to be valid [@mackinnon1985]. Although there has been some debate about their utility [@king2015], robust standard errors rarely degrade inferences and often improve them. Generally, robust standard errors **must** be used when any non-uniform weights are included in the estimation (e.g., with full matching or inverse probability weighting). **Cluster-robust standard errors.** A version of robust standard errors known as cluster-robust standard errors [@liang1986] can be used to account for dependence between observations within clusters (e.g., matched pairs). @abadie2019 demonstrate analytically that cluster-robust standard errors are generally valid after matching, whereas regular robust standard errors can over- or under-estimate the true sampling variability of the effect estimator depending on the specification of the outcome model (if any) and degree of effect modification. A plethora of simulation studies have further confirmed the validity of cluster-robust standard errors after matching [e.g., @austin2009a; @austin2014; @gayat2012; @wan2019; @austin2013]. Given this evidence favoring the use of cluster-robust standard errors, we recommend them in most cases and use them judiciously in the this guide[^1]. [^1]: Because they are only appropriate with a large number of clusters, cluster-robust standard errors are generally not used with subclassification methods. Regular robust standard errors are valid with these methods when using the subclassification weights to estimate marginal effects. Here, we will use linear and generalized linear models as implemented by `lm()` and `glm()` and use `lmtest::coeftest()` and `lmtest::coefci()` along with `sandwich::vcovHC()` for robust standard errors and `sandwich::vcovCL()` for cluster-robust standard errors. There are several "types" (e.g., HC0, HC1, etc.) of robust standard errors that adjust the original standard errors in different ways; although more research is required on the benefits of using different types for estimating standard errors after matching, we use the default types here. #### Bootstrapping Bootstrapping is a technique used to simulate the sampling distribution of an estimator by repeatedly drawing samples with replacement and estimating the effect in each bootstrap sample [@efron1993]. From the bootstrap distribution, standard errors and confidence interval can be computed in several ways, including using the standard deviation of the bootstrap estimates as the standard error estimate or using the 2.5 and 97.5 percentiles as 95% confidence interval bounds. Bootstrapping tends to be most useful when no analytic estimator of a standard error is possible or has been derived yet. Although @abadie2008 found analytically that the bootstrap is inappropriate for matched samples, simulation evidence has found it to be adequate in many cases [@hill2006; @austin2014; @austin2017]. It is the most accessible method for computing standard errors after g-computation for nonlinear models. Typically, bootstrapping involves performing the entire estimation process in each bootstrap sample, including propensity score estimation, matching, and effect estimation. This tends to be the most straightforward route, though intervals from this method may be conservative in some cases (i.e., they are wider than necessary to achieve nominal coverage) [@austin2014]. Less conservative and more accurate intervals have been found when using different forms of the bootstrap, including the wild bootstrap develop by @bodory2020 and the matched bootstrap described by @austin2014 and @abadie2019. The block bootstrap involves sampling matched pairs/strata of units from the matched sample and performing the analysis within each sample composed of the sampled pairs. @abadie2019 derived analytically that the block bootstrap is valid for estimating SEs and CIs in the same circumstances cluster robust SEs are; indeed, the block bootstrap SE is known to approximate the cluster-robust SE [@cameron2015]. We use `boot()` from the `boot` package to implement bootstrapping. With bootstrapping, more bootstrap replications are always better but can take time and increase the chances that at least one error will occur within the bootstrap analysis (e.g., a bootstrap sample with zero treated units or zero units with an event). In general, numbers of replications upwards of 999 are recommended, with values one less than a multiple of 100 preferred to avoid interpolation when using the percentiles as confidence interval limits [@mackinnon2006]. There are several methods of computing bootstrap confidence intervals, but the bias-corrected accelerated (BCa) bootstrap confidence interval often performs best [@austin2014; @carpenter2000] and is easy to implement, simply by setting `type = "bca"` in the call to `boot.ci()` after running `boot()`[^2]. [^2]: Sometimes, an error will occur with this method, which usually means more bootstrap replications are required. The number of replicates must be greater than the original sample size when using the full bootstrap and greater than the number of pairs/strata when using the block bootstrap. ## Estimating Treatment Effects and Standard Errors After Matching Below, we describe effect estimation after several methods of matching. We consider four broad types of matching that require their own specific methods for estimation effects: 1) pair matching without replacement, 2) pair matching with replacement, 3) full matching, and 4) stratification. In some cases, methods for estimating effects are similar across methods, and we err on the side of redundancy here in our instructions. We also consider three different outcome types: 1) continuous, 2) binary, and 3) survival. We'll be using a simulated toy dataset `d` with several outcome types. Code to generate the dataset is at the end of this document. The focus here is not on evaluating the methods but simply on demonstrating them. In all cases, the correct propensity score model is used. Below we display the first six rows of `d`: ```{r} head(d) ``` `A` is the treatment variable, `X1` through `X9` are covariates, `Y_C` is a continuous outcome, `Y_B` is a binary outcome, and `Y_S` is a survival outcome. We will need to the following packages to perform the desired analyses: - `lmtest` provides the `coeftest()` and `coefci()` functions for estimating coefficients, standard errors, and confidence intervals incorporating robust standard errors - `sandwich` provides robust and cluster robust standard errors through the `vcovHC()` and `vcovCL()` functions, respectively - `boot` provides the `boot()` and `boot.ci()` functions for performing bootstrapping and estimating bootstrap effects, standard errors, and confidence intervals. - `survival` provides the functions `survdiff()` and `coxph()` to perform the log-rank test for differences in survival curves and estimate the coefficients in a Cox-proportional hazards model for the marginal hazard ratio, respectively, which we will use for survival outcomes. Of course, we also need `MatchIt` to perform the matching. ```{r,message=FALSE,warning=FALSE} library("MatchIt") library("lmtest") library("sandwich") library("boot") library("survival") ``` Other packages may be of use but are not used here. The `margins` package can be useful for computing marginal effects and standard errors without bootstrapping. Some examples using `margins` are presented. The `survey` package can be used to estimate robust standard errors incorporating weights and provides functions for survey-weighted generalized linear models and Cox-proportional hazards models. It is often used with propensity score weighting. The `Zelig` package provides a broad interface to estimating marginal and conditional effects using simulation and was included in the original documentation for `MatchIt`. The `lme4` (and `lmerTest`) package performs mixed effects modeling, which can be useful for accounting for pair membership or other clustering features of the data. Because different matching methods require different treatments, instructions for each method are organized in the following sections, as designated by the table below. It is important to ensure the right methods are used with the matching specification used in order for the estimated effects and standard errors to be valid. | Section | Matching Method | |-------------------------------------------------------------------------------------|-----------------------------------------------------------| | [After Pair Matching Without Replacement](#after-pair-matching-without-replacement) | Nearest neighbor matching without replacement | | \- | Optimal matching | | \- | Genetic matching without replacement | | \- | Coarsened exact matching with `k2k = TRUE` | | [After Pair Matching With Replacement](#after-pair-matching-with-replacement) | Nearest neighbor matching with replacement | | \- | Genetic matching with replacement | | [After Full Matching](#after-full-matching) | Full matching | | | Cardinality and template matching | | [After Stratum Matching](#after-stratum-matching) | Propensity score subclassification | | \- | Exact matching | | \- | Coarsened exact matching with `k2k = FALSE` (the default) | ### After Pair Matching Without Replacement {#after-pair-matching-without-replacement} Pair matching without replacement yields the simplest way to estimate treatment effects and standard errors. In general, whether a caliper, common support restriction, exact matching specification, or $k$:1 matching specification is used, estimating the effect in the matched dataset (i.e., the output of `match.data()`) is straightforward and involves fitting a model for the outcome that incorporates the matching weights[^3]. [^3]: The matching weights are not necessary when performing 1:1 matching, but we include them here for generality. When weights are not necessary, including them does not affect the estimates. Because it may not always be clear when weights are required, we recommend always including them. First, we will perform nearest 1:1 neighbor propensity score matching without replacement. ```{r} mNN <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d) mNN md <- match.data(mNN) head(md) ``` Typically one would assess balance and ensure that this matching specification works, but we will skip that step here to focus on effect estimation. See `vignette("MatchIt")` and `vignette("assessing-balance")` for more information on this necessary step. Because we did not use a caliper, the target estimand is the ATT. #### For continuous outcomes For continuous outcomes, estimating effects is fairly straightforward using linear regression. We perform all analyses using the matched dataset, `md`, which contains only units retained in the sample. **Without covariate adjustment.** To estimate the effect without covariate adjustment, we can run the following: ```{r} #Linear model without covariates fit1 <- lm(Y_C ~ A, data = md, weights = weights) ``` First, we will estimate the standard errors while accounting for pair membership using cluster-robust standard errors. ```{r} #Cluster-robust standard errors coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ``` **Using the bootstrap.** We demonstrate bootstrapping here using the block bootstrap as recommended by @abadie2019. The process is generalizable to other matching methods for which cluster-robust standard errors are valid and to other outcome types. We first need to write a function, `est_fun`, which takes in a vector of pair identifiers and an ordering and outputs an effect estimate. This function should include the effect estimation, though no standard error is required. ```{r} #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Effect estimation fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights) #Return the coefficient on treatment return(coef(fit_boot)["A"]) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ``` The value `t1*` in the `Bootstrap Statistics` table contains the effect estimate and its bootstrap standard error, and the interval in the confidence interval output is the bootstrap confidence interval computed using the specified type (here, `"bca"`). **With covariate adjustment.** Including covariates in the outcome model is straightforward with a continuous outcome. We can include main effects for each variable and use the coefficient on the treatment as the treatment effect estimate. It can also be helpful to include interactions between the covariates and treatment if effect modification is suspected, though it is important to center the covariates at their means in the focal (i.e., treated) group if the coefficient on the treatment is to be interpreted as an average treatment effect estimate (which we demonstrate with full matching). A marginal effects procedure can also be used, which we demonstrate with binary outcomes. Below we simply include main effects of each covariate, which is the most typical way to include covariates. ```{r} #Linear model with covariates fit3 <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md, weights = weights) coeftest(fit3, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ``` As previously mentioned, it is important to avoid interpreting coefficients in the outcome model on predictors other than the treatment, as the coefficients do no correspond to causal effects and are likely confounded. In the code above, we restricted the output to just the treatment effect. Note that sometimes the intercept of the outcome model can be useful as an estimate of the average potential outcome under control, but the covariates in the model (if any) must be centered at their means in the target population to allow for this interpretation. Without centering the covariates, the intercept is uninterpretable. #### For binary outcomes For binary outcomes, effect estimation can be a bit more challenging, especially when including covariates. There are several measures of the effect one can consider, which include the odds ratio (OR), risk ratio/relative risk (RR), and risk difference (RD). **Without covariate adjustment.** When omitting covariates from the outcome model, effect and standard error estimation is fairly straightforward. Below we demonstrate how to estimate the marginal log OR after nearest neighbor matching without replacement. ```{r} #Generalized linear model without covariates fit4 <- glm(Y_B ~ A, data = md, weights = weights, family = binomial(link = "logit")) ``` By specifying `link = "logit"`, we fit a logistic regression model to estimate the marginal OR for treatment. To estimate the marginal RR we can specify `link = "log"`, and to estimate the RD we can specify `link = "identity"` or use `lm()` instead of `glm()`. Below we estimate cluster-robust standard errors, though it should be noted that these standard errors can be biased for the OR and should be used with caution [@austin2007a]. Because we want the OR and the effects are estimated on the log OR scale, we have to exponentiate the coefficient on treatment to arrive at the OR (one would do this when estimating the OR or RR, but not the RD). ```{r} #Cluster-robust standard errors coeftest(fit4, vcov. = vcovCL, cluster = ~subclass) exp(coef(fit4)) #OR ``` **With covariate adjustment and bootstrapping.** If we want to include covariates in the model, we have to do some additional work to estimate the effect and its standard error. This is because the coefficient on treatment in a nonlinear model with covariates corresponds to the conditional rather than marginal effect. To estimate a marginal effect, we have to perform a marginal effects procedure, which is equivalent to g-computation in the matched set. First we estimate an outcome model including the covariates, and then we use the predictions from the outcome model to compute the contrast of the average potential outcomes under treatment and control. Bootstrapping is the most straightforward method to estimate standard errors. We demonstrate this below using the block bootstrap as recommended by @abadie2019. ```{r} #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Fitting outcome the model fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_boot, family = binomial(link = "logit"), weights = weights) #Estimate potential outcomes for each unit #Under control md_boot$A <- 0 P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds0 <- P0 / (1 - P0) #Under treatment md_boot$A <- 1 P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ``` To use bootstrapping for the RR or RD, the part of the code that computes the marginal OR can be replaced with code to compute the marginal RR (`P1 / P0`) or marginal RD (`P1 - P0`). Interactions between the treatment and covariates can be included in the outcome model; no other part of the procedure needs to be changed. Doing so can add robustness when effect modification is possible. #### For survival outcomes There are several measures of effect size for survival outcomes. When using the Cox proportional hazards model, the quantity of interest is the HR between the treated and control groups. As with the OR, the HR is non-collapsible, which means the estimated HR will only be a valid estimate of the marginal HR when no other covariates are included in the model. Other effect measures, such as the difference in mean survival times or probability of survival after a given time, can be treated just like continuous and binary outcomes as previously described. Here we describe estimating the marginal HR. **Without covariate adjustment.** Below we demonstrate estimation of the marginal hazard ratio using `coxph()` from the `survival` package. To request cluster-robust standard errors as recommended by @austin2013a, we need to supply pair membership (stored in the `subclass` column of `md`) to the `cluster` argument and set `robust = TRUE`. ```{r} #Cox Regression for marginal HR coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights, cluster = subclass) ``` The `coef` column contains the log HR, and `exp(coef)` contains the HR. Remember to always use the `robust se` for the standard error of the log HR. The displayed z-test p-value results from using the robust standard error. **Using the bootstrap.** @austin2014 found bootstrap confidence intervals to work well for marginal hazard ratios. Below we demonstrate this using similar code as before to implement the block bootstrap: ```{r, eval = FALSE} #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Effect estimation cox_fit_boot <- coxph(Surv(Y_S) ~ A, data = md_boot, weights = weights) #Compute the marginal HR by exponentiating the coefficient #on treatment HR <- exp(coef(cox_fit_boot)["A"]) #Return the HR return(HR) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ``` **With covariate adjustment.** As with binary outcomes, if covariates are included in the model, the resulting hazard ratios will be conditional rather than marginal. @austin2020 describe several ways of including covariates in a model to estimate the marginal HR, but because they do not develop standard errors and little research has been done on this method, we will not present it here. ### After Pair Matching With Replacement {#after-pair-matching-with-replacement} Pair matching with replacement makes estimating effects and standard errors a bit less straightforward. Control units paired with multiple treated units belong to multiple pairs at the same time and appear multiple times in the matched dataset. Effect and standard error estimation need to account for control unit multiplicity (i.e., repeated use) and within-pair correlations [@hill2006; @austin2020a]. `MatchIt` provides two interfaces for extracting the matched dataset after matching with replacement. The first uses `match.data()` and provides the same output as when used after matching without replacement, except that the `subclass` column is absent because units are not assigned to a single subclass but rather to several. Control units will have weights that differ from 1 to reflect their use as matches for multiple treated units. When using the `match.data()` interface, including the weights in the estimation of effects is crucial. Because pair membership is omitted, accounting for it (if desired) must be done by conditioning on covariates used to match the pairs or by bootstrapping the entire process. The second interface uses `get_matches()`, which functions similarly to `match.data()` except that the dataset contains one row per unit per pair, so control units matched to multiple treated units will have multiple rows in the dataset, and a `subclass` column is included denoting pair membership. In the `get_matches()` output, each reused control unit will have multiple rows, identical to each other except with different `subclass` membership. When performing k:1 matching, weights must also be included in effect estimation when using `get_matches()`. Here we will demonstrate the estimation of effects using both `match.data()` and `get_matches()` output. The `match.data()` output is preferred when pair membership is not directly included in the analysis, and the `get_matches()` output is preferred when pair membership is to be included. Here will will use 3:1 matching with replacement and with a caliper; note that because a caliper was used, the estimand corresponds to neither the ATT nor the ATE but rather to an ATM. ```{r} mNNr <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) mNNr #match.data output md <- match.data(mNNr) nrow(md) head(md) #get_matches output gm <- get_matches(mNNr) nrow(gm) head(gm) ``` The `get_matches()` output provides some additional information about the match. We can count how many times control units are reused and how many units are in each match strata (not all will have 3 control units due to the caliper). ```{r} #Number of time control units are rematched table(table(gm$id[gm$A == 0])) ``` Here we can see that 332 control units were only used in one pair each, and one control unit was paired with 14 treated units (i.e., heavily reused). ```{r} #Number of control units in each match stratum table(table(gm$subclass[gm$A == 0])) ``` Here we can see that 409 treated units have three matches, nine have two matches, and nine only have one match. The caliper did not end up restricting too many matches. #### For continuous outcomes **Without covariate adjustment.** For continuous outcomes, we can regress the outcome on the treatment and include the weights in the estimation. We do this regardless of whether we are using the `match.data()` output or the `get_matches()` output (if we were doing 1:1 matching, the weights would not be necessary when using the `get_matches()` output, but they don't change the results if included). If we don't mind ignoring pair membership, we can use the `match.data()` output to estimate the effect and standard errors. Here we use `vcovHC()` to estimate regular robust standard errors that ignoring pairing. @hill2006 found these standard errors to be conservative for continuous outcomes. ```{r} #match.data() output fit1md <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1md, vcov. = vcovHC) ``` If we want to incorporate pair membership into the standard error estimation, we have to use the `get_matches()` output. In addition to supplying pair membership (`subclass`) to the standard error estimator, we also supply unit ID (`id`) to account for the fact that several rows may refer to the same control unit. ```{r} #get_matches() output fit1gm <- lm(Y_C ~ A, data = gm, weights = weights) coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id) ``` Note that the effect estimates are identical; only the standard errors and p-values differ between the approaches[^4]. [^4]: It is possible to exactly reproduce the `match.data()` standard error using the `get_matches()` data, but doing so may require some fiddling due to the defaults in `sandwich`. **Using the bootstrap.** We can also use bootstrapping to estimate standard errors and confidence intervals. Although @abadie2008 demonstrated analytically that bootstrap standard errors may be invalid for matching with replacement, simulation work by @hill2006 and @bodory2020 has found that bootstrap standard errors are adequate and generally slightly conservative. Given this disagreement, it may be worth proceeding with caution when using bootstrapping to estimate standard errors after matching with replacement. Simulation experiments with matching with replacement have only considered the full bootstrap and found it to yield confidence intervals with approximately nominal coverage, so we recommend this approach over the block bootstrap for now and demonstrate it below. ```{r} #Full bootstrap confidence interval # library(boot) est_fun <- function(data, i) { #Matching function mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) md_boot <- match.data(mNNr_boot) #Effect estimation fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights) #Return the coefficient on treatment return(coef(fit_boot)["A"]) } boot_est <- boot(d, est_fun, R = 499) boot_est boot.ci(boot_est, type = "perc") ``` **With covariate adjustment.** We can include covariates in the outcome model just as we could when matching without replacement. @hill2006 found that standard errors that fail to account for pair membership can have lower the nominal confidence interval coverage, so we recommend using the `get_matches()` output to address both multiplicity and clustering. ```{r} fit2md <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = gm, weights = weights) coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id)["A",,drop = FALSE] ``` Remember that the coefficients and tests on the predictors other than the treatment should not be interpreted because they may be subject to confounding even if the treatment is not, so we omit them here. #### For binary outcomes The primary difference between dealing with binary and continuous outcomes is the noncollapsibility of the effect measures for binary outcomes, meaning that including covariates in the outcome model is less straightforward because the coefficient on treatment does not correspond to the marginal treatment effect. Similar to continuous outcomes, when estimating the treatment effect, we can use either the output of `match.data()` or the output of `get_matches()`, only the latter of which allows us to account both for multiplicity in the control units and for pair membership. Below we'll demonstrate estimating the marginal OR accounting for pair membership using the `get_matches()` output. Then we will demonstrate using the bootstrap to estimate standard errors that include covariates in the model. Note that there has been little research on the use of matching with replacement with binary outcomes, so one should proceed with caution and consider using a better-studied matching method. **Without covariate adjustment.** We include the weights in the call to `glm()` and we include both `subclass` and `id` as clustering variables in computing the cluster-robust standard errors, just as we would with a continuous outcome. We can use `family = quasibinomial` instead of `family = binomial` to avoid a warning due to the use of weights; the estimates will be the same either way. To estimate the marginal RR or RD, `"logit"` would be replaced with `"log"` or `"identity"`, respectively. ```{r} fit3gm <- glm(Y_B ~ A, data = gm, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3gm, vcov. = vcovCL, cluster = ~ subclass + id) exp(coef(fit3gm)) #OR ``` **With covariate adjustment and bootstrapping.** To include covariates, we can use the bootstrap as we did before when matching without replacement. It doesn't matter whether the `match.data()` or `get_matches()` output is used because, as we saw before, both yield the same effect estimate in each bootstrap replication. Here we use the full bootstrap rather than the block bootstrap because the performance of the block bootstrap after matching with replacement has not been evaluated. ```{r, eval = FALSE} #Bootstrap confidence intervals # library(boot) est_fun <- function(data, i) { #Matching function mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) md_boot <- match.data(mNNr_boot) #Fitting the model fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_boot, family = quasibinomial(link = "logit"), weights = weights) #Estimate potential outcomes for each unit md_boot$A <- 0 P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds0 <- P0 / (1 - P0) md_boot$A <- 1 P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(d, est_fun, R = 4999) boot_est boot.ci(boot_est, type = "bca") ``` As before, to use bootstrapping for the RR or RD, the part of the code that computes the marginal OR can be replaced with code to compute the marginal RR (`P1 / P0`) or marginal RD (`P1 - P0`). The outcome model can additionally include treatment-covariate interactions if desired. #### For survival outcomes Standard error estimation for the marginal HR after matching with replacement is not a well-studied area, with @austin2020a providing the sole examination into appropriate methods for doing so. With survival outcomes, other matching methods may be more appropriate until matching with replacement is better understood. Here we provide an example that implements the recommendations by @austin2020a. Any other methods (e.g., bootstrap) should be used with caution until they have been formally evaluated. **Without covariate adjustment.** According to the results of Austin and Cafri's [-@austin2020a] simulation studies, when prevalence of the treatment is low (\<30%), a standard error that does not involve pair membership is sufficient. When treatment prevalence is higher, the standard error that ignores pair membership may be too low, and the authors recommend a custom standard error estimator that uses information about both multiplicity and pairing. For the continuous and binary outcomes, accounting for both multiplicity and pair membership is fairly straightforward thanks to the ability of the `sandwich` package functions to include multiple sources of clustering. Unfortunately, this must be done manually for survival models. We perform this analysis below, adapting code from the appendix of @austin2020a to the `get_matches()` output. ```{r} #Austin & Cafri's (2020) SE estimator fs <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights, cluster = subclass) Vs <- fs$var ks <- nlevels(gm$subclass) fi <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights, cluster = id) Vi <- fi$var ki <- length(unique(gm$id)) fc <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights) Vc <- fc$var kc <- nrow(gm) #Compute the variance V <- (ks/(ks-1))*Vs + (ki/(ki-1))*Vi - (kc/(kc-1))*Vc #Sneak it back into the fit object fc$var <- V fc ``` The `robust se` column contains the computed standard error, and the reported Z-test uses this standard error. The `se(coef)` column should be ignored. ### After Full Matching {#after-full-matching} ```{r, include=FALSE} #In case optmatch goes offline, don't run lines below if (!requireNamespace("optmatch", quietly = TRUE)) knitr::opts_chunk$set(eval = FALSE) ``` Full matching presents fairly straightforward methods of effect and standard error estimation. The most common and recommended way to estimate effects after full matching is to use the computed matching weights to estimate weighted effects. These matching weights function essentially like inverse probability weights and can be treated as such in all analyses, including with respect to standard error estimation. For empirical comparisons between full matching and propensity score weighting, see Austin and Stuart [-@austin2015b; -@austin2017; -@austin2015a]. Standard error estimation involves using a cluster-robust standard error as recommended by @austin2017. Including covariates can improve precision and add robustness when valid. Note that the methods described here also work for other estimators that rely on matching weights, including cardinality and template matching and matching without replacement. The main difference is that full matching can be used to estimate the ATE, which we demonstrate below. Below, we perform optimal full propensity score matching. Here we use full matching to estimate the ATE, but the procedure is nearly identical when estimating the ATT, and we point out any differences. ```{r} mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", estimand = "ATE") mF md <- match.data(mF) head(md) ``` A benefit of full matching is that no units are discarded, which has the potential to improve precision and prevent bias due to incomplete matching. However, the "effective" sample size implied by the matching weights is lower than the actual remaining sample size, so one should not always expect full matching to yield more precise estimates than other forms of matching. #### For continuous outcomes **Without covariate adjustment.** Estimating effects and standard errors for continuous outcomes after full matching involves including the matching weights in the outcome model and using a cluster-robust standard error. For cardinality or template matching, a regular robust standard error can be requested using `vcovHC` and omitting the `cluster` argument in the code below. ```{r} fit1 <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ``` **Using the bootstrap.** Computing bootstrap standard errors and confidence intervals after full matching is identical to doing so after pair matching without replacement, so we do not demonstrate it again here. Covariates can be included in the outcome model used in the bootstrap replications. See below for a note on centering the covariates when including treatment-covariate interactions, as this must be done when using the bootstrap as well. **With covariate adjustment.** As previously mentioned, it is generally acceptable to include just the main effects, but including interactions between the treatment and covariates can be beneficial when effect modification by the covariates may be present. Because we have not demonstrated this strategy so far, we demonstrate it below. In order to interpret the coefficient on treatment as a marginal effect estimate, we need to center the covariates at their means in the target population (i.e., the original sample for the ATE, the treated units for the ATT, or the retained units for an ATM); we could also use a marginal effects procedure as has been demonstrated with binary outcomes for other matching methods. Below we use the strategy of centering the covariates at their means. Note that when factor predictors are present, they need to be split into dummy (0/1) variables prior to centering. The `splitfactor()` function in `cobalt` can make this straightforward. Although this procedure is more involved compared to simply including main effects, it can provide extra precision and robustness. ```{r} #Estimating a covariate-adjusted marginal effect #with treatment-covariate interactions #Create a new dataset for centered variables md_cen <- md covs_to_center <- c("X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9") md_cen[covs_to_center] <- scale(md_cen[covs_to_center], scale = FALSE) #Fit the model with every covariate interacting with treatment fit2 <- lm(Y_C ~ A * (X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9), data = md_cen, weights = weights) #Only output the intercept and coefficient on treatment coeftest(fit2, vcov. = vcovCL, cluster = ~subclass)[1:2,] ``` Remember not to interpret the coefficients on the covariates or the treatment-covariate interactions, as they are likely confounded. To keep the output clean, above we restricted the output to just the intercept and coefficient on treatment. Another benefit of centering the covariates is that we can interpret the intercept as an estimate of the average potential outcome under control. Note that the above strategy can be applied to all matching methods when analyzing continuous outcomes (but not binary or survival outcomes, which require bootstrapping to validly estimate standard errors with covariate adjustment). It is critical to center the covariates at their means in the target group, which may require some additional programming for estimands other than the ATE. #### For binary outcomes Using full matching with binary outcomes was described by @austin2017. In general, the procedures look similar to how they do with other matching methods. **Without covariate adjustment.** We can use a weighted generalized linear model regressing the outcome on the treatment with a link function appropriate to the effect measure of interest. Below we demonstrate estimating the marginal OR after full matching in a model without covariates: ```{r} fit3 <- glm(Y_B ~ A, data = md, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3, vcov. = vcovCL, cluster = ~subclass) exp(coef(fit3)) #OR ``` As with matching with replacement, we include weights in the call to `glm()` and set `family = quasibinomial()` to prevent a warning that occurs when using weights with binomial regression models (though the results do not differ). Setting `link = "logit"` provides the marginal OR; for the marginal RR, we would replace `"logit"` with `"log"`, and for the marginal RD, we would replace `"logit"` with `"identity"` and not exponentiate the coefficient. **With covariate adjustment and bootstrapping.** To include covariates in the model, a marginal effects procedure must be used with bootstrapping to recover the marginal effect because the coefficient on treatment in such a model corresponds to a conditional effect. A bootstrap must be used to estimate the standard error and confidence interval of the marginal effect. Either the full bootstrap or block bootstrap can be used, though the performance of the latter has not been formally evaluated (but because it is an approximation to cluster-robust standard errors, which are valid, it is likely valid). The code for the full bootstrap with full matching is identical to the code for bootstrapping with binary outcomes for pair matching with replacement (except that the call to `matchit()` in the `est_fun` function must be adjusted to perform full matching), and the code for the block bootstrap with full matching is identical to the code for bootstrapping with binary outcomes for pair matching without replacement, so we do not repeat it here. #### For survival outcomes @austin2015b describe the use of the full matching with survival outcomes. **Without covariate adjustment.** To estimate the marginal HR, we can regress the outcome on the treatment in a Cox regression model weighted by the matching weights and including subclasses as a cluster. Below we demonstrate how to estimate the marginal HR and its standard error after full matching. ```{r} coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights, cluster = subclass) ``` To perform the log-rank test or compute survival curves after full matching, functions designed for performing these tasks with inverse probability weights can be used with the matching weights; the `RISCA` package offers functionality for this purpose. Including covariates in the model is less straightforward because the resulting HR estimate is conditional rather than marginal. ```{r, include=FALSE, eval=TRUE} knitr::opts_chunk$set(eval = TRUE) ``` ### After Stratum Matching {#after-stratum-matching} Stratum matching includes exact matching, coarsened exact matching, and propensity score subclassification. There are two natural ways to estimate marginal effects after stratum matching: the first is to estimate stratum-specific treatment effects and pool them, and the second is to use the stratum weights to estimate a single marginal effect. This latter approach is also known as marginal mean weighting through stratification (MMWS), and is described in detail by @hong2010[^5]. When done properly, both methods should yield similar or identical estimates of the treatment effect. MMWS is generally preferable because it is far simpler to implement and avoids issues of noncollapsibility with non-continuous outcomes. All of the methods described above for use with full matching also work with MMWS because the formation of the weights is the same; the only difference is that it is not appropriate to use cluster-robust standard errors with MMWS because of how few clusters are present. [^5]: It is also known as fine stratification weighting, described by Desai et al. [-\@desai2017]. Unless exact matching is used, estimating stratum-specific treatment effects can be fraught because balance may not be achieved within strata even if balance is achieved across strata. Stratum-specific effects should be interpreted with caution. Stratum-specific effects are conditional effects, but conditional on stratum membership, which may not always be a useful conditioning variable. For each outcome type, we focus on estimating marginal effects using MMWS and using the strata directly. Below, we perform propensity score subclassification for the ATT using 8 subclasses. ```{r} mS <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "subclass", estimand = "ATT", subclass = 8) mS md <- match.data(mS) head(md) ``` #### For continuous outcomes For continuous outcomes, we can use either MMWS or compute the weighted average of within-subclass effects. First we illustrate weighting. **Without covariate adjustment.** With weighting, we can supply the weights that are in the `match.data()` output to a call to `lm()` to perform weighted least squares regression, as we did with full matching. We need a robust standard error estimator to account for the weights. Note that the subclasses don't even need to enter this analysis; they are fully incorporated through the MMWS weights[^6]. We use `vcovHC()` to estimate the regular robust standard error instead of the cluster-robust standard error used with other methods. [^6]: Including subclass as a main effect in the MMWS-weighted regression will not change the effect estimate but may slightly decrease its estimated standard error. ```{r} fit1 <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1, vcov. = vcovHC) ``` We can also fit a model within each subclass to estimate the within-stratum treatment effects and then compute a weighted average of them to be used as the marginal effect. The stratum weights in the weighted average must be equal to the proportion of treated units in each subclass if the ATT is targeted. If the ATE is targeted, the weights must be equal to the proportion of all units in each subclass. There are other ways to construct weight to minimize variance at the expense of losing the original target population [@rudolph2016]. Instead of fitting separate models for each subclass, we can fit a single model that fully interacts the treatment with subclass membership and then perform a linear hypothesis test. To do so, we use the form `Y ~ S + S:A - 1` in the call to `lm()`. This includes main effects for subclass and treatment interaction terms for each subclass and omits an intercept. The fit of this model is equivalent to that of a traditional full factorial model, so no information is lost using this parameterization and using it makes it easier to construct the stratum weights. This estimates the subclass-specific intercept and subclass-specific treatment effect in each subclass. We would use a robust standard error to account for different residual variance across subclasses. ```{r} fit2 <- lm(Y_C ~ subclass + subclass:A - 1, data = md) #Within-subclass effects # coeftest(fit2, vcov. = vcovHC) ``` The within-subclass effects should only be trusted if balance is achieved in each subclass. In this example, balance has not been achieved within some subclasses, so we would not interpret these effects. Next we construct the weights to form the weighted average of the subclass effects. The weights take the form of a linear contrast matrix with zeroes for the subclass-specific intercepts and the subclass proportions for the corresponding subclass-specific treatment effect coefficients. ```{r} #Subclass weights for ATT sub_w <- with(md, c(rep(0, nlevels(subclass)), table(subclass[A==1])/sum(A==1))) #Subclass weights for ATE (requires estimand = "ATE" in matchit()) # sub_w <- with(md, c(rep(0, nlevels(subclass)), # table(subclass)/nrow(md))) #Marginal effect (est <- weighted.mean(coef(fit2), sub_w)) #SE of marginal effect (se <- sqrt(drop(sub_w %*% vcovHC(fit2) %*% sub_w))) #CI c(ci_low = est - 1.96*se, ci_hi = est + 1.96*se) ``` The following lines would have produced the same output but require the `margins` package: ```{r,eval=FALSE} #Using margins() from margins summary(margins::margins(fit2, variables = "A", data = md[md$A == 1,], vcov = vcovHC(fit2))) #For ATE, omit the second line. ``` **With covariate adjustment.** To include covariates in the model when using MMWS, we can modify the code used for weighting after full matching, the only difference being that regular robust standard errors should be used with MMWS. As before, treatment-covariate interactions are optional but can reduce bias and improve precision when there effect modification by the covariates. When including these interactions in the outcome model, it is important to center the covariates at their means in the target population (i.e., the full sample for the ATE and the treated units for the ATT) in order to interpret the coefficient on treatment as a marginal effect. To include covariates in the model when combining subclass-specific effect estimates, it can be challenging to correctly parameterize the model so that the linear contrast matrix method works as expected. The simplest way would be to include covariates as desired, which include as main effects, interactions with treatment, interactions with subclass, or all three, and use the `margins()` code above, which should automatically provide the correct output. #### For binary outcomes Using stratification with binary outcomes is slightly more complicated than it is with continuous outcomes. This is because the OR and RR are not collapsible, so the marginal OR and RR cannot be computed as the weighted average of the stratum-specific effects. Instead, one must compute the average of the predicted stratum-specific risks under each treatment and then compute the marginal effect estimate from these marginal risks. Although stratum-specific conditional ORs are valid effect measures, they generally do not correspond to meaningful subpopulation effects unless the strata themselves are meaningful subpopulations. After exact matching or coarsened exact matching, strata may be meaningful because they correspond to specific combinations of covariates that may come close to designating specific patient attributes, but after propensity score subclassification, the strata correspond to propensity score bins, which are generally not meaningful. Although some researchers have interpreted stratum-specific effects after propensity score subclassification as representing effects at various risks or propensities for treatment, because the primary purpose of the propensity score is as a balancing score and not as an accurate estimate of propensity for treatment, such an interpretation should be regarded with caution. @austin2007a compared several methods of propensity score adjustment for estimating marginal ORs, including two methods based on propensity score stratification, one of which involved the stratified Mantel-Haenszel estimator, and the other of which involved averaging stratum-specific effects. Both of these estimate a common conditional OR, not a marginal OR [@forbes2008; @stampf2010], and both yielded positively biased effect estimates for non-null treatment effects, a common pattern when using conditional effect estimates as estimates of marginal effects. Given the difficulties in estimating marginal ORs after stratification, the most straightforward way to do so is to use MMWS. We do, however, also demonstrate estimating the marginal odds ratio and its standard error using bootstrapping in a way that can incorporate covariate adjustment and allow for differential effect modification across strata. **Without covariate adjustment.** As before, we can supply the stratification weights to a weighted generalized linear model with just the treatment as the sole predictor, and the coefficient on treatment will correspond to a marginal treatment effect. We use `vcovHC()` to estimate the regular robust standard error. ```{r} fit3 <- glm(Y_B ~ A, data = md, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3, vcov. = vcovHC) exp(coef(fit3)) ``` As with other matching methods, we include weights in the call to `glm()` and set `family = quasibinomial()` to prevent a warning that occurs when using weights with binomial regression models (though the results do not differ). Setting `link = "logit"` provides the marginal OR; for the marginal RR, we would replace `"logit"` with `"log"`, and for the marginal RD, we would replace `"logit"` with `"identity"` and not exponentiate the coefficient. **With covariate adjustment and bootstrapping.** We can use bootstrapping to estimate the marginal OR and its standard error by estimating the average of the stratum-specific risks under each treatment level, computing the marginal risks under each treatment, and computing marginal effects from the marginal risks. This also makes it fairly straightforward to include covariates in the model. Below we illustrate bootstrapping for the marginal OR with covariates included in the outcome model. ```{r, eval = FALSE} #Bootstrap confidence intervals library(boot) est_fun <- function(data, i) { #Subclassification function mS_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], method = "subclass", estimand = "ATT", subclass = 8) md_boot <- match.data(mS_boot) #Fitting the model fit_boot <- glm(Y_B ~ A * (subclass + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9), data = md_boot, family = quasibinomial(link = "logit")) #Estimate potential outcomes for each unit ## Subset to just the treated for the ATT; remove this for the ATE md_boot <- md_boot[md_boot$A == 1,] ## md_boot$A <- 0 P0 <- mean(predict(fit_boot, md_boot, type = "response")) Odds0 <- P0 / (1 - P0) md_boot$A <- 1 P1 <- mean(predict(fit_boot, md_boot, type = "response")) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(d, est_fun, R = 4999) boot_est boot.ci(boot_est, type = "bca") ``` In this example, we included interactions between treatment and subclass and between treatment and each covariate. Note that because we are interested in the ATT, we restricted the sample used to compute the predicted marginal risks (`P0`) and (`P1`) to just those with `A = 1`. If we were instead estimating the ATE, we would supply `"ATE"` that to the `estimand` argument in the call to `matchit()` and skip the step of restricting the data used for prediction of the marginal risks. As with other methods, to estimate the marginal RR or RD using the above code, the returned object can instead be specified as `P1 / P0` or `P1 - P0`, respectively. #### For survival outcomes Like ORs, HRs are not collapsible, so it is not straightforward to estimate marginal HRs using within-stratum HRs. @austin2013 examined the performance of several propensity score subclassification-based estimators of the marginal HR and found all to be positively biased for non-null effects, consistent with the use of conditional effect estimates as estimates of marginal effects; indeed, the subclassification methods examined all relied on pooling stratum-specific effects. Given these difficulties, the most straightforward method to estimate marginal HRs is to use MMWS weights. We demonstrate this below using essentially the same syntax as used with full matching, only omitting subclass membership as a clustering variable. ```{r} coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights) ``` The robust standard error must be used because of the MMWS weights. ### Estimating Conditional Effects As discussed previously, with binary and time-to-event outcomes, marginal and conditional effects generally differ. Though we have primarily focused on marginal effects, we offer some guidance here on estimating conditional effect as well. Estimating conditional effects typically involves modeling the outcome, and inferences are dependent on this model being correct, even in the presence is perfect covariate balance. Matching provides robustness to some forms of misspecification, however, by limiting the range of the covariate space. So, even though outcome modeling is required, matching prior to modeling the outcome can be beneficial in terms of reducing bias due to model misspecification. On some effect measures, conditional effects typically vary depending on the covariate values at which the conditional effect is to be estimated; for example, the decrease in the risk of death (i.e., on the RD scale) corresponding to receipt of a treatment for a patient with a high baseline risk of death will by higher than that for a patient with a low baseline risk of death. In this sense, there is necessarily effect modification of the RD by baseline risk (which depends on a patient's covariate values). Similarly, for an exposure that increases the risk by a factor (e.g., doubles the risk, for a RR of 2), a patient with a high baseline risk cannot experience the same change in risk as a patient with a low baseline risk could; for example, a RR of 2 is impossible for a patient with a baseline risk of .6 but is certainly plausible for a patient with a baseline risk of .02. In this sense, there is necessarily effect modification of the RR by baseline risk. This is not true for the OR; it is plausible that the effect on the OR scale could be consistent across levels of levels of the predictors because any OR is compatible with possible baselines risks. For this reason, we only consider estimating the conditional OR and not other effect measures. Although several methods exist for estimating conditional ORs after matching or subclassification, the one that generally works well is to run a logistic regression of the outcome on the treatment and covariates and use the coefficient on treatment as an estimate of the effect on the log OR scale. We demonstrate this below using full matching for the ATT: ```{r} mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", estimand = "ATT") md <- match.data(mF) fitcond <- lm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md, weights = weights) coeftest(fitcond, vcov. = vcovCL, cluster = ~subclass)["A",,drop = FALSE] exp(coef(fitcond)["A"]) ``` As with other covariate-adjusted models, it is important not to interpret the coefficients on covariates other than treatment to avoid committing the table 2 fallacy. Other causes of the outcome can be included in the outcome model even if they were not the focus of matching as long as they are not (even possibly) caused by the treatment. Alternative methods of estimating conditional effects include conditional logistic regression after matching and estimating stratum-specific effects after subclassification. We recommend using covariate-adjusted logistic regression models instead because the conditional effect is better defined (i.e., conditional on the specific covariates included in the model) and depends less on the estimand targeted [@forbes2008]. ### Moderation Analysis Moderation analysis involves determining whether a treatment effect differs across levels of another variable. The use of matching with moderation analysis is described in @greenExaminingModerationAnalyses2014. The goal is to achieve balance within each subgroup of the potential moderating variable, and there are several ways of doing so. Broadly, one can either perform matching in the full dataset, perhaps requiring exact matching on the moderator, or one can perform completely separate analyses in each subgroup. We'll demonstrate both approaches below. The chosen approach should be that which achieves the best balance, though we don't demonstrate assessing balance here to maintain focus on effect estimation. We'll consider the binary variable `X5` to be the potential moderator of the effect of `A` on `Y_C`. The first approach involves pooling information across subgroups. This could involve estimating propensity scores using a single model for both groups but exact matching on the potential moderator. The propensity score model could include moderator-by-covariate interactions to allow the propensity score model to vary across subgroups on some covariates. Below, we'll estimate a propensity score using a single propensity score model with a few moderator-by-covariate interactions and exact matching on the moderator, `X5`. We'll perform nearest neighbor matching on the propensity score. ```{r} mP <- matchit(A ~ X1 + X2 + X5*X3 + X4 + X5*X6 + X7 + X5*X8 + X9, data = d, exact = ~X5, method = "nearest") mP ``` Although it is straightforward to assess balance overall using `summary()`, it is more challenging to assess balance within subgroups. The easiest way to check subgroup balance would be to use `cobalt::bal.tab()`, which has a `cluster` argument that can be used to assess balance within subgroups, e.g., by `cobalt::bal.tab(mP, cluster = "X5")`. See the vignette "Appendix 2: Using cobalt with Clustered, Multiply Imputed, and Other Segmented Data" on the `cobalt` [website](https://ngreifer.github.io/cobalt/index.html) for details. If we are satisfied with balance, we can then estimate the subgroup effects using an outcome model with an interaction between the treatment and the moderator. ```{r} mdP <- match.data(mP) fitP <- lm(Y_C ~ A * X5, data = mdP, weights = weights) coeftest(fitP, vcov. = vcovCL, cluster = ~subclass) ``` A second approach is to perform the matching analyses separately and then combine the matched samples. We demonstrate this below. First, we split the data by levels of `X5`. ```{r} d_X5_0 <- subset(d, X5 == 0) d_X5_1 <- subset(d, X5 == 1) ``` Next we perform separate matching analyses in each new dataset, ```{r} mS0 <- matchit(A ~ X1 + X2 + X3 + X4 + X6 + X7 + X8 + X9, data = d_X5_0, method = "nearest") mS1 <- matchit(A ~ X1 + X2 + X3 + X4 + X6 + X7 + X8 + X9, data = d_X5_1, method = "nearest") ``` It is straightforward to assess balance within each subgroup using `summary()`, and `cobalt` functions can be used as well. We omit this step here, but you should not! To estimate the subgroup effects, we need to combine the matched datasets, which we can do using `rbind.matchdata()` (a special `rbind()` method for `matchdata` objects). Then we estimate the moderation effect just as we did previously. ```{r} mdS0 <- match.data(mS0) mdS1 <- match.data(mS1) mdS <- rbind(mdS0, mdS1) fitS <- lm(Y_C ~ A * X5, data = mdS, weights = weights) coeftest(fitS, vcov. = vcovCL, cluster = ~subclass) ``` There are benefits to using either approach, and @greenExaminingModerationAnalyses2014 find that either can be successful at balancing the subgroups. The first approach may be most effective with small samples, where separate propensity score models would be fit with greater uncertainty and an increased possibility of perfect prediction or failure to converge [@wangRelativePerformancePropensity2018]. The second approach may be more effective with larger samples or with matching methods that target balance in the matched sample, such as genetic matching [@kreifMethodsEstimatingSubgroup2012]. With genetic matching, separate subgroup analyses ensure balance is optimized within each subgroup rather than just overall. ### Reporting Results It is important to be as thorough and complete as possible when describing the methods of estimating the treatment effect and the results of the analysis. This improves transparency and replicability of the analysis. Results should at least include the following: - a description of the outcome model used (e.g., logistic regression, a linear model with treatment-covariate interactions and mean-centered covariates, a Cox proportional hazards model with the matching weights applied) - the way the effect was estimated (e.g., as the coefficient on treatment in the outcome model, as the result of a marginal effects procedure) - the way standard errors and confidence intervals were estimated (e.g., using robust standard errors, using cluster-robust standard errors with pair membership as the cluster, using the BCa bootstrap with 4999 bootstrap replications and the entire process of matching and effect estimation included in each replication) - R packages and functions used in estimating the effect and its standard error (e.g., `glm()` in base R, `vcovCL()` in `sandwich`, `boot()` and `boot.ci()` in `boot`) - The effect and its standard error and confidence interval All this is in addition to information about the matching method, propensity score estimation procedure (if used), balance assessment, etc. mentioned in the other vignettes. ## Common Mistakes There are a few common mistakes that should be avoided. It is important not only to avoid these mistakes in one's own research but also to be able to spot these mistakes in others' analyses. ### 1. Failing to include weights Several methods involve weights that are to be used in estimating the treatment effect. With full matching and stratification matching (when analyzed using MMWS), the weights do the entire work of balancing the covariates across the treatment groups. Omitting weights essentially ignores the entire purpose of matching. Some cases are less obvious. When performing matching with replacement and estimating the treatment effect using the `match.data()` output, weights must be included to ensure control units matched to multiple treated units are weighted accordingly. Similarly, when performing k:1 matching where not all treated units receive k matches, weights are required to account for the differential weight of the matched control units. The only time weights can be omitted after pair matching is when performing 1:1 matching without replacement. Including weights even in this scenario will not affect the analysis and it can be good practice to always include weights to prevent this error from occurring. There are some scenarios where weights are not useful because the conditioning occurs through some other means, such as when using the pooling strategy rather than MMWS for estimating marginal effects after stratification. ### 2. Failing to use robust or cluster-robust standard errors Robust standard errors are required when using weights to estimate the treatment effect. The model-based standard errors resulting from weighted least squares or maximum likelihood are inaccurate when using matching weights because they assume weights are frequency weights rather than probability weights. Cluster-robust standard errors account for both the matching weights and pair membership and should be used when appropriate (i.e., with all matching methods other than stratification matching). Sometimes, researchers use functions in the `survey` package to estimate robust standard errors, especially with inverse probability weighting; this is a valid way to compute robust standard errors and will give similar results to `sandwich::vcovHC()`. ### 3. Interpreting conditional effects as marginal effects The distinction between marginal and conditional effects is not always clear both in methodological and applied papers. Some statistical methods are valid only for estimating conditional effects and they should not be used to estimate marginal effects (without further modification). Sometimes conditional effects are desirable, and such methods may be useful for them, but when marginal effects are the target of inference, it is critical not to inappropriately interpret estimates resulting from statistical methods aimed at estimating conditional effects as marginal effects. Although this issue is particularly salient with binary and survival outcomes due to the general noncollapsibility of the OR, RR, and HR, this can also occur with linear models for continuous outcomes or the RD. The following methods estimate **conditional effects** for binary or survival outcomes (with noncollapsible effect measures) and should **not** be used to estimate marginal effects: - Logistic regression or Cox proportional hazards model with covariates and/or the propensity score included, using the coefficient on treatment as the effect estimate - Conditional logistic regression after matching - Stratified Cox regression after matching - Averaging stratum-specific effect estimates after stratification, including using Mantel-Haenszel OR pooling - Including pair or stratum fixed or random effects in a logistic regression model, using the coefficient on treatment as the effect estimate In addition, with continuous outcomes, conditional effects can be mistakenly interpreted as marginal effect estimates when treatment-covariate interactions are present in the outcome model. If the covariates are not centered at their mean in the target population (e.g., the treated group for the ATT, the full sample for the ATE, or the remaining matched sample for an ATM), the coefficient on treatment will not correspond to the marginal effect in the target population; it will correspond to the effect of treatment when the covariate values are equal to zero, which may not be meaningful or plausible. Marginal effects procedures (e.g., using the `margins` package or manually with bootstrapping as demonstrated above) are always the safest way to include covariates in the outcome model, especially in the presence of treatment-covariate interactions. Appropriately centering the covariates is a shortcut that is required when using the coefficient on treatment as a marginal effect estimate for continuous outcomes (demonstrated previously for full matching). ## References ::: {#refs} ::: ## Code to Generate Data used in Examples ```{r, eval = FALSE} #Generating data similar to Austin (2009) for demonstrating treatment effect estimation gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 # Binary outcome gen_Y_B <- function(A, X) { LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] P_B <- plogis(LP_B) rbinom(length(A), 1, P_B) } #Conditional: # OR: 2.4 # logOR: .875 #Marginal: # RD: .144 # RR: 1.54 # logRR: .433 # OR: 1.92 # logOR .655 # Survival outcome gen_Y_S <- function(A, X) { LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] sqrt(-log(runif(length(A)))*2e4*exp(-LP_S)) } #Conditional: # HR: 2.4 # logHR: .875 #Marginal: # HR: 1.57 # logHR: .452 set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) Y_C <- gen_Y_C(A, X) Y_B <- gen_Y_B(A, X) Y_S <- gen_Y_S(A, X) d <- data.frame(A, X, Y_C, Y_B, Y_S) ``` MatchIt/inst/doc/sampling-weights.html0000644000176200001440000013073514170752616017517 0ustar liggesusers Matching with Sampling Weights

Matching with Sampling Weights

Noah Greifer

2022-01-16

Introduction

Sampling weights (also known as survey weights) frequently appear when using large, representative datasets. They are required to ensure any estimated quantities generalize to a target population defined by the weights. Evidence suggests that sampling weights need to be incorporated into a propensity score matching analysis to obtain valid and unbiased estimates of the treatment effect in the sampling weighted population (DuGoff, Schuler, and Stuart 2014; Austin, Jembere, and Chiu 2016; Lenis et al. 2019). In this guide, we demonstrate how to use sampling weights with MatchIt for propensity score estimation, balance assessment, and effect estimation. Fortunately, doing so is not complicated, but some care must be taken to ensure sampling weights are incorporated correctly. It is assumed one has read the other vignettes explaining matching (vignette("matching-methods")), balance assessment (vignette("assessing-balance")), and effect estimation (vignette("estimating-effects").

We will use the same simulated toy dataset used in vignette("estimating-effects") except with the addition of a sampling weights variable, SW, which is used to generalize the sample to a specific target population with a distribution of covariates different from that of the sample. Code to generate the covariates, treatment, and outcome is at the bottom of vignette("estimating-effects") and code to generate the sampling weights is at the end of this document. We will consider the effect of binary treatment A on continuous outcome Y_C, adjusting for confounders X1-X9.

head(d)
##   A      X1      X2      X3       X4 X5      X6      X7      X8       X9     Y_C     SW
## 1 0  0.1725 -1.4283 -0.4103 -2.36059  1 -1.1199  0.6398 -0.4840 -0.59385 -3.5907  1.675
## 2 0 -1.0959  0.8463  0.2456 -0.12333  1 -2.2687 -1.4491 -0.5514 -0.31439 -1.5481  1.411
## 3 0  0.1768  0.7905 -0.8436  0.82366  1 -0.2221  0.2971 -0.6966 -0.69516  6.0714  2.332
## 4 0 -0.4595  0.1726  1.9542 -0.62661  1 -0.4019 -0.8294 -0.5384  0.20729  2.4906  1.644
## 5 1  0.3563 -1.8121  0.8135 -0.67189  1 -0.8297  1.7297 -0.6439 -0.02648 -0.6687  2.722
## 6 0 -2.4313 -1.7984 -1.2940  0.04609  1 -1.2419 -1.1252 -1.8659 -0.56513 -9.8504 14.773
library("MatchIt")
library("lmtest")
library("sandwich")

Matching

When using sampling weights with propensity score matching, one has the option of including the sampling weights in the model used to estimate the propensity scores. Although evidence is mixed on whether this is required (Austin, Jembere, and Chiu 2016; Lenis et al. 2019), it can be a good idea. The choice should depend on whether including the sampling weights improves the quality of the matches. Specifications including and excluding sampling weights should be tried to determine which is preferred.

To supply sampling weights to the propensity score-estimating function in matchit(), the sampling weights variable should be supplied to the s.weights argument. It can be supplied either as a numerical vector containing the sampling weights, or a string or one-sided formula with the name of the sampling weights variable in the supplied dataset. Below we demonstrate including sampling weights into propensity scores estimated using logistic regression for optimal full matching for the average treatment effect in the population (ATE) (note that all methods and steps apply the same way to all forms of matching and all estimands).

mF_s <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                  X6 + X7 + X8 + X9, data = d,
                method = "full", distance = "glm",
                estimand = "ATE", s.weights = ~SW)
mF_s
## A matchit object
##  - method: Optimal full matching
##  - distance: Propensity score
##              - estimated with logistic regression
##              - sampling weights included in estimation
##  - number of obs.: 2000 (original), 2000 (matched)
##  - sampling weights: present
##  - target estimand: ATE
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9

Notice that the description of the matching specification when the matchit object is printed includes lines indicating that the sampling weights were included in the estimation of the propensity score and that they are present in the matchit object. It is stored in the s.weights component of the matchit object. Note that at this stage, the matching weights (stored in the weights component of the matchit object) do not incorporate the sampling weights; they are calculated simply as a result of the matching.

Now let’s perform full matching on a propensity score that does not include the sampling weights in its estimation. Here we use the same specification as was used in vignette("estimating-effects").

mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                X6 + X7 + X8 + X9, data = d,
              method = "full", distance = "glm",
              estimand = "ATE")
mF
## A matchit object
##  - method: Optimal full matching
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 2000 (original), 2000 (matched)
##  - target estimand: ATE
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9

Notice that there is no mention of sampling weights in the description of the matching specification. However, to properly assess balance and estimate effects, we need the sampling weights to be included in the matchit object, even if they were not used at all in the matching. To do so, we use the function add_s.weights(), which adds sampling weights to the supplied matchit objects.

mF <- add_s.weights(mF, ~SW)

mF
## A matchit object
##  - method: Optimal full matching
##  - distance: Propensity score
##              - estimated with logistic regression
##              - sampling weights not included in estimation
##  - number of obs.: 2000 (original), 2000 (matched)
##  - sampling weights: present
##  - target estimand: ATE
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9

Now when we print the matchit object, we can see lines have been added identifying that sampling weights are present but they were not used in the estimation of the propensity score used in the matching.

Note that not all methods can involve sampling weights in the estimation. Only methods that use the propensity score will be affected by sampling weights; coarsened exact matching or Mahalanobis distance optimal pair matching, for example, ignore the sampling weights, and some propensity score estimation methods, like randomForest and bart (as presently implemented), cannot incorporate sampling weights. Sampling weights should still be supplied to matchit() even when using these methods to avoid having to use add_s.weights() and remembering which methods do or do not involve sampling weights.

Assessing Balance

Now we need to decide which matching specification is the best to use for effect estimation. We do this by selecting the one that yields the best balance without sacrificing remaining effective sample size. Because the sampling weights are incorporated into the matchit object, the balance assessment tools in plot.matchit() and summary.matchit() incorporate them into their output.

We’ll use summary() to examine balance on the two matching specifications. With sampling weights included, the balance statistics for the unmatched data are weighted by the sampling weights. The balance statistics for the matched data are weighted by the product of the sampling weights and the matching weights. It is the product of these weights that will be used in estimating the treatment effect. Below we use summary() to display balance for the two matching specifications. No additional arguments to summary() are required for it to use the sampling weights; as long as they are in the matchit object (either due to being supplied with the s.weights argument in the call to matchit() or to being added afterward by add_s.weights()), they will be correctly incorporated into the balance statistics.

#Balance before matching and for the SW propensity score full matching
summary(mF_s, improvement = FALSE)
## 
## Call:
## matchit(formula = A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + 
##     X9, data = d, method = "full", distance = "glm", estimand = "ATE", 
##     s.weights = ~SW)
## 
## Summary of Balance for All Data:
##          Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance         0.470         0.225           1.200      1.533     0.281    0.467
## X1               0.185        -0.120           0.287      1.309     0.105    0.171
## X2              -0.565        -0.202          -0.368      0.854     0.108    0.194
## X3              -0.059        -0.057          -0.003      0.739     0.041    0.083
## X4               0.852         0.150           0.680      1.016     0.180    0.293
## X5               0.567         0.713          -0.307          .     0.146    0.146
## X6               0.169        -0.008           0.171      1.014     0.048    0.103
## X7               0.378        -0.089           0.474      1.205     0.130    0.210
## X8              -0.376        -0.142          -0.216      1.190     0.071    0.129
## X9               0.088        -0.001           0.090      0.945     0.027    0.066
## 
## 
## Summary of Balance for Matched Data:
##          Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max Std. Pair Dist.
## distance         0.316         0.292           0.117      1.042     0.030    0.073           0.007
## X1               0.025        -0.035           0.057      1.211     0.038    0.086           0.974
## X2              -0.386        -0.274          -0.113      1.058     0.031    0.091           1.079
## X3              -0.101        -0.015          -0.092      0.656     0.046    0.109           1.133
## X4               0.353         0.338           0.014      1.186     0.023    0.068           0.910
## X5               0.669         0.667           0.003          .     0.002    0.002           0.830
## X6               0.043         0.061          -0.017      1.148     0.028    0.083           1.177
## X7               0.156         0.030           0.128      1.185     0.029    0.074           1.068
## X8              -0.171        -0.224           0.048      1.242     0.017    0.046           0.979
## X9               0.000         0.036          -0.037      1.139     0.026    0.086           1.146
## 
## Sample Sizes:
##               Control Treated
## All (ESS)       987.1   177.1
## All            1559.    441. 
## Matched (ESS)   517.4   152. 
## Matched        1559.    441. 
## Unmatched         0.      0. 
## Discarded         0.      0.
#Balance for the non-SW propensity score full matching
summary(mF, un = FALSE)
## 
## Call:
## matchit(formula = A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + 
##     X9, data = d, method = "full", distance = "glm", estimand = "ATE")
## 
## Summary of Balance for Matched Data:
##          Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max Std. Pair Dist.
## distance         0.285         0.275           0.051      0.995     0.021    0.057           0.009
## X1              -0.101         0.016          -0.110      1.082     0.030    0.066           0.932
## X2              -0.317        -0.250          -0.067      0.973     0.029    0.090           1.117
## X3              -0.037        -0.067           0.032      0.699     0.039    0.080           1.137
## X4               0.462         0.286           0.171      1.136     0.047    0.128           0.987
## X5               0.656         0.669          -0.026          .     0.012    0.012           0.806
## X6               0.036        -0.025           0.059      1.077     0.025    0.059           1.123
## X7               0.054         0.031           0.023      1.042     0.012    0.056           0.986
## X8              -0.200        -0.213           0.012      1.077     0.022    0.078           0.977
## X9              -0.026         0.042          -0.069      0.894     0.034    0.105           1.098
## 
## Sample Sizes:
##               Control Treated
## All (ESS)       987.1   177.1
## All            1559.    441. 
## Matched (ESS)   589.1   152.7
## Matched        1559.    441. 
## Unmatched         0.      0. 
## Discarded         0.      0.

The results of the two matching specifications are similar. Balance appears to be slightly better when using the sampling weight-estimated propensity scores than when using the unweighted propensity scores. However, the effective sample size for the control group is larger when using the unweighted propensity scores. Neither propensity score specification achieves excellent balance, and more fiddling with the matching specification (e.g., by changing the method of estimating propensity scores, the type of matching, or the options used with the matching) might yield a better matched set. For the purposes of this analysis, we will move forward with the matching that used the sampling weight-estimated propensity scores (mF_s) because of its superior balance. Some of the remaining imbalance may be eliminated by adjusting for the covariates in the outcome model.

Note that had we not added sampling weights to mF, the matching specification that did not include the sampling weights, our balance assessment would be inaccurate because the balance statistics would not include the sampling weights. In this case, in fact, assessing balance on mF without incorporated the sampling weights would have yielded radically different results and a different conclusion. It is critical to incorporate sampling weights into the matchit object using add_s.weights() even if they are not included in the propensity score estimation.

Estimating the Effect

Estimating the treatment effect after matching is straightforward when using sampling weights. Effects are estimated in the same way as when sampling weights are excluded, except that the matching weights must be multiplied by the sampling weights to yield accurate, generalizable estimates. match.data() and get_matches() do this automatically, so the weights produced by these functions already are a product of the matching weights and the sampling weights. Note this will only be true if sampling weights are incorporated into the matchit object.

Below we estimate the effect of A on Y_C in the matched and sampling weighted sample, adjusting for the covariates to improve precision and decrease bias.

md_F_s <- match.data(mF_s)

fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + 
             X6 + X7 + X8 + X9, data = md_F_s,
          weights = weights)

coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE]
##   Estimate Std. Error t value Pr(>|t|)
## A     1.74     0.3815    4.56 5.43e-06

Note that match.data() and get_weights() have the option include.s.weights, which, when set to FALSE, makes it so the returned weights do not incorporate the sampling weights and are simply the matching weights. To incorporate sampling weights into the effect estimation with this option, one must multiply the returned weights by the sampling weights when including them in the outcome model estimation. We demonstrate this below:

md_F_s <- match.data(mF_s, include.s.weights = FALSE)

fit <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + 
             X6 + X7 + X8 + X9, data = md_F_s,
          weights = weights * SW)

coeftest(fit, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE]
##   Estimate Std. Error t value Pr(>|t|)
## A     1.74     0.3815    4.56 5.43e-06

We get the same estimates using this syntax as we did above. Because one might to forget to multiply the two sets of weights together, it is easier to just use the default of include.s.weights = TRUE and ignore the sampling weights in the rest of the analysis (because they are already included in the returned weights).

Code to Generate Data used in Examples

#Generatng data similar to Austin (2009) for demonstrating 
#treatment effect estimation with sampling weights
gen_X <- function(n) {
  X <- matrix(rnorm(9 * n), nrow = n, ncol = 9)
  X[,5] <- as.numeric(X[,5] < .5)
  X
}

#~20% treated
gen_A <- function(X) {
  LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + 
    log(2)*X[,7] - log(1.5)*X[,8]
  P_A <- plogis(LP_A)
  rbinom(nrow(X), 1, P_A)
}

# Continuous outcome
gen_Y_C <- function(A, X) {
  2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5)
}
#Conditional:
#  MD: 2
#Marginal:
#  MD: 2

gen_SW <- function(X) {
  e <- rbinom(nrow(X), 1, .3)
  1/plogis(log(1.4)*X[,2] + log(.7)*X[,4] + log(.9)*X[,6] + log(1.5)*X[,8] + log(.9)*e +
             -log(.5)*e*X[,2] + log(.6)*e*X[,4])
}

set.seed(19599)

n <- 2000
X <- gen_X(n)
A <- gen_A(X)
SW <- gen_SW(X)

Y_C <- gen_Y_C(A, X)

d <- data.frame(A, X, Y_C, SW)

References

Austin, Peter C., Nathaniel Jembere, and Maria Chiu. 2016. “Propensity Score Matching and Complex Surveys.” Statistical Methods in Medical Research 27 (4): 1240–57. https://doi.org/10.1177/0962280216658920.
DuGoff, Eva H., Megan Schuler, and Elizabeth A. Stuart. 2014. “Generalizing Observational Study Results: Applying Propensity Score Methods to Complex Surveys.” Health Services Research 49 (1): 284–303. https://doi.org/10.1111/1475-6773.12090.
Lenis, David, Trang Quynh Nguyen, Nianbo Dong, and Elizabeth A. Stuart. 2019. “Its All about Balance: Propensity Score Matching in the Context of Complex Survey Data.” Biostatistics 20 (1): 147–63. https://doi.org/10.1093/biostatistics/kxx063.
MatchIt/inst/doc/assessing-balance.html0000644000176200001440000252432214170752541017615 0ustar liggesusers Assessing Balance

Assessing Balance

Noah Greifer

2022-01-16

Introduction

Covariate balance is the degree to which the distribution of covariates is similar across levels of the treatment. It has three main roles in causal effect estimation using matching: 1) as a target to optimize with matching, 2) as a method of assessing the quality of the resulting matches, and 3) as evidence to an audience that the estimated effect is close to the true effect. When covariate balance is achieved, the resulting effect estimate is less sensitive to model misspecification and ideally close to true treatment effect. The benefit of randomization is that covariate balance is achieved automatically (in expectation), which is why unadjusted effects estimated from randomized trial data (in the absence of drop-out) can be validly interpreted as causal effects. When using matching to recover causal effect estimates form observational data, balance is not guaranteed and must be assessed.

This document provides instructions for assessing and reporting covariate balance as part of a matching analysis. The tools available in MatchIt for balance assessment should be used during the process of selecting a good matching scheme and ensuring that the chosen scheme is adequate. These tools implement the recommendations of Ho et al. (2007) and others for assessing balance.

In addition to the tools available in matchIt, the cobalt package has a suite of functions designed to assess and display balance and is directly compatible with MatchIt objects. cobalt has extensive documentation, but we describe some of its functionality here as a complement to the tools in MatchIt.

The structure of this document is as follows: first, we describe some of the recommendations for balance checking and their rationale; next, we describe the tools for assessing balance present in MatchIt and display their use in evaluating several matching schemes; finally; we briefly describe some of the functionality in cobalt to extend that in MatchIt.

Recommendations for Balance Assessment

Assessing balance involves assessing whether the distributions of covariates are similar between the treated and control groups. Balance is typically assessed by examining univariate balance summary statistics for each covariate, though more complicated methods exist for assessing joint distributional balance as well. Visual depictions of distributional balance can be a helpful complement to numerical summaries, especially for hard to balance and prognostically important covariates.

Many recommendations for balance assessment have been described in the methodological literature. Unfortunately, there is no single best way to assess balance or to weigh balance summary statistics because the degree and form of balance that will yield the least bias in an effect estimate depends on unknown qualities of the outcome data-generating model. Nonetheless, there are a number of valuable recommendations that can be implemented to ensure matching is successful at eliminating or reducing bias. We review some of these here.

Common recommendations for assessing balance include the following:

  • Standardized mean differences. The standardized mean difference (SMD) is the difference in the means of each covariate between treatment groups standardized by a standardization factor so that it is on the same scale for all covariates. The standardization factor is typically the standard deviation of the covariate in the treated group when targeting the ATT or the pooled standard deviation across both groups when targeting the ATE. The standardization factor should be the same before and after matching to ensure changes in the mean difference are not confounded by changes in the standard deviation of the covariate. SMDs close to zero indicate good balance. Several recommended thresholds have been published in the literature; we recommend .1 and .05 for prognostically important covariates. Higher values may be acceptable when using covariate adjustment in the matched sample. In addition to computing SMDs on the covariates themselves, it is important to compute them on squares, cubes, and higher exponents as well as interactions between covariates. Several empirical studies have examined the appropriateness for using SMDs in balance assessment, including Belitser et al. (2011), Ali et al. (2014), and Stuart, Lee, and Leacy (2013); in general, there is often a high correlation between the mean or maximum absolute SMD and the degree of bias in the treatment effect.

  • Variance Ratios. The variance ratio is the ratio of the variance of a covariate in one group to that in the other. Variance ratios close to 1 indicate good balance because they imply the variances of the samples are similar (Austin 2009).

  • Empirical CDF Statistics. Statistics related to the difference in the empirical cumulative density functions (eCDFs) of each covariate between groups allow assessment of imbalance across the entire covariate distribution of that covariate rather than just its mean or variance. The maximum eCDF difference, also known as the Kolmogorov-Smirnov statistic, is sometimes recommended as a useful supplement to SMDs for assessing balance (Austin and Stuart 2015) and is often used as a criterion to use in propensity score methods that attempt to optimize balance (e.g., McCaffrey, Ridgeway, and Morral 2004; Diamond and Sekhon 2013). Although the mean eCDF difference has not been as well studied, it provides a summary of imbalance that may be missed by relying solely on the maximum difference.

  • Visual Diagnostics. Visual diagnostics such as eCDF plots, empirical quantile-quantile (eQQ) plots, and kernel density plots can be used to see exactly how the covariate distributions differ from each other, i.e., where in the distribution the greatest imbalances are (Ho et al. 2007; Austin 2009). This can help to figure out how to tailor a matching method to target imbalance in a specific region of the covariate distribution.

  • Prognostic scores. The prognostic score is an estimate of the potential outcome under control for each unit (Hansen 2008). Balance on the prognostic score has been shown to be highly correlated with bias in the effect estimate, making it a useful tool in balance assessment (Stuart, Lee, and Leacy 2013). Estimating the prognostic score requires having access to the outcome data, and using it may be seen as violating the principle of separating the design and analysis stages of a matching analysis (Rubin 2001). However, because only the outcome values from the control group are required to use the prognostic score, some separation is maintained.

Several multivariate statistics exist that summarize balance across the entire joint covariate distribution. These can be functions of the above measures, like the mean or maximum absolute SMD or the generalized weighted distance [GWD; Franklin et al. (2014)], which is the sum of SMDs for the covariates and their squares and interactions, or separate statistics that measure quantities that abstract away from the distribution of individual covariates, like the L1 distance (Iacus, King, and Porro 2011), cross-match test (Heller, Rosenbaum, and Small 2010), or energy distance (Huling and Mak 2020).

Balance on the propensity score has often been considered a useful measure of balance, but we do not necessarily recommend it except as a supplement to balance on the covariates. Propensity score balance will generally be good with any matching method regardless of the covariate balancing potential of the propensity score, so a balanced propensity score does not imply balanced covariates (Austin 2009). Similarly, it may happen that covariates may be well balanced even if the propensity score is not balanced, such as when covariates are prioritized above the propensity score in the matching specification (e.g., with genetic matching). Given these observations, the propensity score should not be relied upon for assessing covariate balance. Simulation studies by Stuart, Lee, and Leacy (2013) provide evidence for this recommendation against relying on propensity score balance.

There has been some debate about the use of hypothesis tests, such as t-tests or Kolmogorov-Smirnov tests, for assessing covariate balance. The idea is that balance tests test the null hypothesis that the matched sample has equivalent balance to a randomized experiment. There are several problems with balance tests, described by Ho et al. (2007) and Imai, King, and Stuart (2008): 1) balance is a property of the sample, not a of a population from which the sample was drawn; 2) the power of balance tests depends on the sample size, which changes during matching even if balance does not change; and 3) the use of hypothesis tests implies a uniform decision criterion for rejecting the null hypothesis (e.g., p-value less than .05, potentially with corrections for multiple comparisons), when balance should be improved without limit. MatchIt does not report any balance tests or p-values, instead relying on the descriptive statistics described above.

Recommendations for Balance Reporting

A variety of methods should be used when assessing balance to try to find an optimal matched set that will ideally yield a low-error estimate of the desired effect. However, reporting every balance statistic or plot in a research report or publication can be burdensome and unnecessary. That said, it is critical to report balance to demonstrate to readers that the resulting estimate is approximately unbiased and relies little on extrapolation or correct outcome model specification. We recommend the following in reporting balance in a matching analysis:

  • Report SMDs before and after matching for each covariate, any prognostically important interactions between covariates, and the prognostic score; this can be reported in a table or in a Love plot.

  • Report summaries of balance for other statistics, e.g., the largest mean and maximum eCDF difference among the covariates and the largest SMD among squares, cubes, and interactions of the covariates.

MatchIt provides tools for calculating each of these statistics so they can be reported with ease in a manuscript or report.

Assessing Balance with MatchIt

MatchIt contains several tools to assess balance numerically and graphically. The primary balance assessment function is summary.matchit(), which is called when using summary() on a MatchIt object and produces several tables of balance statistics before and after matching. plot.summary.matchit() generates a Love plot using R’s base graphics system containing the standardized mean differences resulting from a call to summary.matchit() and provides a nice way to display balance visually for inclusion in an article or report. plot.matchit() generates several plots that display different elements of covariate balance, including propensity score overlap and distribution plots of the covariates. These functions together form a suite that can be used to assess and report balance in a variety of ways.

To demonstrate MatchIt’s balance assessment capabilities, we will use the Lalonde data included in MatchIt and used in vignette("MatchIt"). We will perform full matching on the propensity score, though the functionality is identical across all matching methods except propensity score subclassification, which we illustrate at the end.

library("MatchIt")
data("lalonde", package = "MatchIt")

#Full matching on a logistic regression PS
m.out <- matchit(treat ~ age + educ + race + married + 
                   nodegree + re74 + re75, data = lalonde,
                 method = "full")
m.out
## A matchit object
##  - method: Optimal full matching
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 614 (original), 614 (matched)
##  - target estimand: ATT
##  - covariates: age, educ, race, married, nodegree, re74, re75

summary.matchit()

When summary() is called on a matchit object, several tables of information are displayed. These include balance statistics for each covariate before matching, balance statistics for each covariate after matching, the percent reduction in imbalance after matching, and the sample sizes before and after matching. summary.matchit() has four additional arguments that control how balance is computed:

  • interactions controls whether balance statistics for all squares and pairwise interactions of covariates are to be displayed in addition to the covariates. The default is FALSE, and setting to TRUE can make the output massive when many covariates are present, but it is important to ensure no important interactions remain imbalanced.
  • addlvariables allows for balance to be assessed on variables other than those inside the matchit object. For example, if the distance between units only relied on a subset of covariates but balance needed to be achieved on all covariates, addlvariables could be used to supply these additional covariates. In addition to adding other variables, addlvariables can be used to request balance on specific functions of the covariates already in the matchit object, such as polynomial terms or interactions. The input to addlvariables can be a one-sided formula with the covariates and any desired transformations thereof on the right hand side, just like a model formula (e.g., addlvariables = ~ X1 + X2 + I(X1^2) would request balance on X1, X2, and the square of X1). Additional variables supplied to addlvariables but not present in the matchit object can be supplied as a data frame using the data argument.
  • standardize controls whether standardized or unstandardized statistics are to displayed. Standardized statistics include the standardized mean difference and eCDF statistics; unstandardized statistics include the raw difference in means and eQQ plot statistics. (Regardless, the variance ratio will always be displayed.). The default is TRUE for standardized statistics, which are more common to report because they are all on the same scale regardless of the scale of the covariates1.
  • pair.dist controls whether within-pair distances should be computed and displayed. These reflect the average distance between units within the same pair, standardized or unstandardized according to the argument to standardize. The default is TRUE. With full matching, exact matching, coarsened exact matching, and propensity score subclassification, computing pair distances can take a long time, and so it may be beneficial to set to FALSE in these cases.

In addition, the arguments un and improvement control whether balance prior to matching should be displayed and whether the percent balance improvement after matching should be displayed. These can be set to FALSE to reduce the output.

Below, we call summary.matchit() with addlvariables to display balance on covariates and a few functions of them in the matched sample. In particular, we request balance on the square of age, the variables representing whether re74 and re75 were equal to 0, and the interaction between educ and race.

summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + 
          I(re75==0) + educ:race)
## 
## Call:
## matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = "full")
## 
## Summary of Balance for All Data:
##                  Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance                 0.577         0.182           1.794      0.921     0.377    0.644
## age                     25.816        28.030          -0.309      0.440     0.081    0.158
## educ                    10.346        10.235           0.055      0.496     0.035    0.111
## raceblack                0.843         0.203           1.762          .     0.640    0.640
## racehispan               0.059         0.142          -0.350          .     0.083    0.083
## racewhite                0.097         0.655          -1.882          .     0.558    0.558
## married                  0.189         0.513          -0.826          .     0.324    0.324
## nodegree                 0.708         0.597           0.245          .     0.111    0.111
## re74                  2095.574      5619.237          -0.721      0.518     0.225    0.447
## re75                  1532.055      2466.484          -0.290      0.956     0.134    0.288
## I(age^2)               717.395       901.779          -0.428      0.363     0.081    0.158
## I(re74 == 0)TRUE         0.708         0.261           0.983          .     0.447    0.447
## I(re75 == 0)TRUE         0.600         0.312           0.587          .     0.288    0.288
## educ:raceblack           8.697         2.047           1.580      0.980     0.354    0.645
## educ:racehispan          0.578         1.263          -0.294      0.487     0.046    0.078
## educ:racewhite           1.070         6.925          -1.767      0.365     0.279    0.555
## 
## 
## Summary of Balance for Matched Data:
##                  Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max Std. Pair Dist.
## distance                 0.577         0.576           0.006      0.992     0.004    0.049           0.019
## age                     25.816        24.693           0.157      0.485     0.084    0.322           1.261
## educ                    10.346        10.323           0.012      0.558     0.023    0.062           1.220
## raceblack                0.843         0.835           0.024          .     0.009    0.009           0.038
## racehispan               0.059         0.058           0.005          .     0.001    0.001           0.564
## racewhite                0.097         0.107          -0.033          .     0.010    0.010           0.417
## married                  0.189         0.129           0.155          .     0.061    0.061           0.481
## nodegree                 0.708         0.704           0.009          .     0.004    0.004           0.914
## re74                  2095.574      2199.713          -0.021      1.201     0.038    0.235           0.867
## re75                  1532.055      1524.836           0.002      2.005     0.065    0.231           0.793
## I(age^2)               717.395       713.248           0.010      0.420     0.084    0.322           1.263
## I(re74 == 0)TRUE         0.708         0.473           0.517          .     0.235    0.235           1.026
## I(re75 == 0)TRUE         0.600         0.369           0.471          .     0.231    0.231           0.857
## educ:raceblack           8.697         8.493           0.048      0.866     0.026    0.062           0.208
## educ:racehispan          0.578         0.579          -0.000      0.906     0.006    0.015           0.536
## educ:racewhite           1.070         1.250          -0.054      0.787     0.009    0.022           0.777
## 
## Percent Balance Improvement:
##                  Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance                    99.7       90.0      99.0     92.5
## age                         49.3       11.9      -3.0   -104.2
## educ                        78.9       16.8      32.4     44.3
## raceblack                   98.7          .      98.7     98.7
## racehispan                  98.6          .      98.6     98.6
## racewhite                   98.2          .      98.2     98.2
## married                     81.2          .      81.2     81.2
## nodegree                    96.3          .      96.3     96.3
## re74                        97.0       72.2      83.0     47.4
## re75                        99.2    -1456.4      51.5     19.8
## I(age^2)                    97.8       14.5      -3.0   -104.2
## I(re74 == 0)TRUE            47.4          .      47.4     47.4
## I(re75 == 0)TRUE            19.8          .      19.8     19.8
## educ:raceblack              96.9     -612.1      92.7     90.4
## educ:racehispan             99.9       86.3      87.1     80.4
## educ:racewhite              96.9       76.2      96.6     96.0
## 
## Sample Sizes:
##               Control Treated
## All            429.       185
## Matched (ESS)   53.33     185
## Matched        429.       185
## Unmatched        0.         0
## Discarded        0.         0

Let’s examine the output in detail. The first table (Summary of Balance for All Data) provides balance in the sample prior to matching. The included statistics are the mean of the covariates in the treated group (Means Treated), the mean of the covariate in the control group (Means Control), the SMDs (Std. Mean Diff.), the variance ratio (Var. Ratio), the average distance between the eCDFs of the covariate across the groups (eCDF Mean), and the largest distance between the eCDFs (eCDF Max). Setting un = FALSE would have suppressed the creation of this table.

The second table (Summary of Balance for Matched Data) contains all the same statistics in the matched sample. Because we implicitly request pair distance, an additional column for standardized pair distances (Std. Pair Dist.) is displayed.

The third table (Percent Balance Improvement) contains the percent balance improvement for each covariate. This is computed as \(100\frac{|\theta_M| - |\theta_U|}{|\theta_U|}\), where \(\theta_M\) is a given balance statistic in the matched sample and \(\theta_U\) is a the same balance statistic in the unmatched sample. Values between 0 and 100 indicate that balance improved after matching as measured by the statistic; values less than 0 indicate that balance got worse after matching. When balance is good on a covariate prior to matching, it can sometimes look like balance got a lot worse after matching even though the balance statistic is quite low, so these values should not be taken too seriously and should be used primarily as heuristics. Setting un = FALSE or improvement = FALSE would have suppressed the creation of this table.

The final table (Sample Sizes) contains the sizes of the samples before (All) and after (Matched) matching, as well as the number of units left unmatched (Unmatched) and the number of units dropped due to a common support restriction (Discarded).

The SMDs are computed as the difference mean divided by a standardization factor computed in the unmatched sample. An absolute SMD close to 0 indicates good balance; although a number of recommendations for acceptable values have appeared in the literature, we recommend absolute values less than .1 and less than .05 for potentially prognostically important variables.

The variance ratios are computed as the ratio of the variance of the treated group to that of the control group for each covariate. Variance ratios are not computed for binary covariates because they are a function of the prevalence in each group, which is captured in the mean difference and eCDF statistics. A variance ratio close to 1 indicates good balance; a commonly used recommendation is for variance ratios to be between .5 and 2.

The eCDF statistics correspond to the difference in the overall distributions of the covariates between the treatment groups. The values of both statistics range from 0 to 1, with values closer to zero indicating better balance. There are no specific recommendations for the values these statistics should take, though notably high values may indicate imbalance on higher moments of the covariates. The eQQ statistics produced when standardize = FALSE are interpreted similarly but are on the scale of the covariate.

All these statistics should be considered together. Imbalance as measured by any of them may indicate a potential failure of the matching scheme to achieve distributional balance.

plot.summary.matchit()

A Love plot is a clean way to visually summarize balance. Using plot on the output of a call to summary() on a matchit object produces a Love plot of the standardized mean differences. plot.summary.matchit() has several additional arguments that can be used to customize the plot.

  • abs controls whether standardized mean difference should be displayed in absolute value or not. Default is TRUE.
  • var.order controls how the variables are ordered on the y-axis. The options are "data" (the default), which orders the variables as they appear the in the summary.matchit() output; "unmatched", which orders the variables based on their standardized mean differences before matching; "matched", which orders the variables based on their standardized mean differences after matching; and "alphabetical", which orders the variables alphabetically. Using "unmatched" tends to result in attractive plots and ensures the legend doesn’t overlap with points in its default position.
  • threshold controls where vertical lines indicating chosen thresholds should appear on the x-axis. Should be a numeric vector. The default is c(.1, .05), which display vertical lines at .1 and .05 standardized mean difference units.
  • position controls the position of the legend. The default is "bottomright", which puts the legend in the bottom right corner of the plot, and any keyword value available to supplied to x in legend() is allowed.

Below we create a Love plot of the covariates.

m.sum <- summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + 
          I(re75==0) + educ:race)
plot(m.sum, var.order = "unmatched")

From this plot it is clear to see that balance was quite poor prior to matching, but full matching improved balance on all covariates, and most within a threshold of .1. To make the variable names cleaner, the original variables should be renamed prior to matching. cobalt provides many additional options to generate and customize Love plots using the love.plot() function and should be used if a plot beyond what is available with plot.summary.matchit() is desired.

plot.matchit()

In addition to numeric summaries of balance, MatchIt offers graphical summaries as well using plot.matchit() (i.e., using plot() on a matchit object). We can create eQQ plots, eCDF plots, or density plots of the covariates and histograms or jitter plots of the propensity score. The covariate plots can provide a summary of the balance of the full marginal distribution of a covariate beyond just the mean and variance.

plot.matchit() has a few arguments to customize the output:

  • type corresponds to the type of plot desired. Options include "qq" for eQQ plots (the default), "ecdf" for eCDF plots, "density" for density plots, "jitter" for jitter plots, and "histogram" for histograms.
  • interactive controls whether the plot is interactive or not. For eQQ, eCDF, and density plots, this allows us to control when the next page of covariates is to be displayed since only three can appear at a time. For jitter plots, this can allow us to select individual units with extreme values for further inspection. The default is TRUE.
  • which.xs is used to specify for which covariates to display balance in eQQ, eCDF, and density plots. The default is to display balance on all, but we can request balance just on a specific subset. If three or fewer are requested, interactive is ignored.

Below, we demonstrate the eQQ plot:

#eQQ plot
plot(m.out, type = "qq", which.xs = c("age", "nodegree", "re74"))

The y-axis displays the each value of the covariate for the treated units, and the x-axis displays the the value of the covariate at the corresponding quantile in the control group. When values fall on the 45 degree line, the groups are balanced. Above, we can see that age remains somewhat imbalanced, but nodegree and re74 have much better balance after matching than before. The difference between the x and y values of each point are used to compute the eQQ difference statistics that are displayed in summary.matchit() with standardize = FALSE.

Below, we demonstrate the eCDF plot:

#eCDF plot
plot(m.out, type = "ecdf", which.xs = c("educ", "married", "re75"))

The x-axis display the covariate values and the y-axis displays the proportion of the sample at or less than that covariate value. Perfectly overlapping lines indicate good balance. The black line corresponds to the treated group and the gray line to the control group. Although educ and re75 were fairly well balanced before matching, their balance has improved nonetheless. married appears far better balanced after matching than before. The vertical difference between the eCDFs lines of each treatment group is used to compute the eCDF difference statistics that are displayed in summary.matchit() with standardize = TRUE.

Below, we demonstrate the density plot:

#density plot
plot(m.out, type = "density", which.xs = c("age", "educ", "married"))

The x-axis display the covariate values and the y-axis displays the density of the sample at that covariate value. For binary variables, the y-axis displays the proportion of the sample at that covariate value. Perfectly overlapping lines indicate good balance. The black line corresponds to the treated group and the gray line to the control group. Density plots display similar information to eCDF plots but may be more intuitive for some users because of their link to histograms.

Assessing Balance After Subclassification

With subclassification, balance can be checked both within each subclass and overall. With summary.matchit(), we can request to view balance only in aggregate or in each subclass. The latter can help us decide if we can interpret effects estimated within each subclass as unbiased. The plot.summary.matchit() and plot.matchit() outputs can be requested either in aggregate or for each subclass. We demonstrate this below. First we will perform propensity score subclassification using 4 subclasses (typically more is beneficial).

#Subclassification on a logistic regression PS
s.out <- matchit(treat ~ age + educ + race + married + 
                   nodegree + re74 + re75, data = lalonde,
                 method = "subclass", subclass = 4)
s.out
## A matchit object
##  - method: Subclassification (4 subclasses)
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 614 (original), 614 (matched)
##  - target estimand: ATT
##  - covariates: age, educ, race, married, nodegree, re74, re75

When using summary(), the default is to display balance only in aggregate using the subclassification weights. This balance output looks similar to that for other matching methods.

summary(s.out)
## 
## Call:
## matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = "subclass", subclass = 4)
## 
## Summary of Balance for All Data:
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance           0.577         0.182           1.794      0.921     0.377    0.644
## age               25.816        28.030          -0.309      0.440     0.081    0.158
## educ              10.346        10.235           0.055      0.496     0.035    0.111
## raceblack          0.843         0.203           1.762          .     0.640    0.640
## racehispan         0.059         0.142          -0.350          .     0.083    0.083
## racewhite          0.097         0.655          -1.882          .     0.558    0.558
## married            0.189         0.513          -0.826          .     0.324    0.324
## nodegree           0.708         0.597           0.245          .     0.111    0.111
## re74            2095.574      5619.237          -0.721      0.518     0.225    0.447
## re75            1532.055      2466.484          -0.290      0.956     0.134    0.288
## 
## Summary of Balance Across Subclasses
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance           0.577         0.539           0.173      0.678     0.062    0.126
## age               25.816        24.975           0.118      0.465     0.085    0.297
## educ              10.346        10.433          -0.043      0.582     0.023    0.061
## raceblack          0.843         0.767           0.210          .     0.076    0.076
## racehispan         0.059         0.042           0.076          .     0.018    0.018
## racewhite          0.097         0.191          -0.318          .     0.094    0.094
## married            0.189         0.196          -0.017          .     0.007    0.007
## nodegree           0.708         0.657           0.113          .     0.051    0.051
## re74            2095.574      2557.709          -0.095      0.968     0.048    0.264
## re75            1532.055      1490.040           0.013      1.505     0.035    0.146
## 
## Percent Balance Improvement:
##            Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance              90.4       26.4      83.5     80.4
## age                   62.0       -5.7      -4.7    -88.2
## educ                  20.9      -17.4      32.4     45.4
## raceblack             88.1          .      88.1     88.1
## racehispan            78.4          .      78.4     78.4
## racewhite             83.1          .      83.1     83.1
## married               98.0          .      98.0     98.0
## nodegree              54.0          .      54.0     54.0
## re74                  86.9      -86.9      78.7     40.8
## re75                  95.5      -57.4      74.2     49.1
## 
## Sample Sizes:
##               Control Treated
## All             429.      185
## Matched (ESS)   102.3     185
## Matched         429.      185
## Unmatched         0.        0
## Discarded         0.        0

An additional option in summary(), subclass, allows us to request balance for individual subclasses. subclass can be set to TRUE to display balance for all subclasses or the indices of individual subclasses for which balance is to be displayed. Below we call summary() and request balance to be displayed on all subclasses (setting un = FALSE to suppress balance in the original sample):

summary(s.out, subclass = TRUE, un = FALSE)
## 
## Call:
## matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = "subclass", subclass = 4)
## 
## Summary of Balance by Subclass:
## 
## - Subclass 1
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance           0.239         0.095           0.653      2.494     0.313    0.508
## age               26.478        28.800          -0.324      0.394     0.090    0.160
## educ              10.304        10.214           0.045      0.620     0.025    0.084
## raceblack          0.370         0.063           0.843          .     0.307    0.307
## racehispan         0.239         0.167           0.304          .     0.072    0.072
## racewhite          0.391         0.770          -1.277          .     0.379    0.379
## married            0.370         0.589          -0.560          .     0.219    0.219
## nodegree           0.587         0.584           0.007          .     0.003    0.003
## re74            5430.539      6363.913          -0.191      1.298     0.087    0.284
## re75            2929.039      2699.399           0.071      1.587     0.047    0.144
## 
## - Subclass 2
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance           0.604         0.612          -0.035      0.905     0.083    0.195
## age               25.556        24.409           0.160      0.461     0.114    0.370
## educ               9.933         9.773           0.080      0.448     0.084    0.188
## raceblack          1.000         1.000           0.000          .     0.000    0.000
## racehispan         0.000         0.000           0.000          .     0.000    0.000
## racewhite          0.000         0.000           0.000          .     0.000    0.000
## married            0.378         0.091           0.732          .     0.287    0.287
## nodegree           0.667         0.500           0.367          .     0.167    0.167
## re74            1777.422      2516.589          -0.151      0.433     0.076    0.280
## re75             972.344      1131.077          -0.049      0.666     0.034    0.086
## 
## - Subclass 3
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance           0.693         0.691           0.012      1.281     0.055    0.189
## age               24.021        22.964           0.148      0.509     0.128    0.281
## educ              10.170        10.286          -0.057      1.040     0.038    0.099
## raceblack          1.000         1.000           0.000          .     0.000    0.000
## racehispan         0.000         0.000           0.000          .     0.000    0.000
## racewhite          0.000         0.000           0.000          .     0.000    0.000
## married            0.021         0.107          -0.219          .     0.086    0.086
## nodegree           0.681         0.750          -0.152          .     0.069    0.069
## re74             939.969       888.947           0.010      2.038     0.059    0.216
## re75            1217.455      1285.387          -0.021      1.535     0.038    0.188
## 
## - Subclass 4
##            Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean eCDF Max
## distance           0.767         0.753           0.063      2.961     0.165    0.445
## age               27.213        23.786           0.479      0.521     0.150    0.459
## educ              10.957        11.429          -0.234      0.701     0.059    0.126
## raceblack          1.000         1.000           0.000          .     0.000    0.000
## racehispan         0.000         0.000           0.000          .     0.000    0.000
## racewhite          0.000         0.000           0.000          .     0.000    0.000
## married            0.000         0.000           0.000          .     0.000    0.000
## nodegree           0.894         0.786           0.237          .     0.108    0.108
## re74             291.783       540.618          -0.051      0.917     0.083    0.280
## re75            1015.289       854.751           0.050      3.523     0.112    0.266
## 
## Sample Sizes by Subclass:
##           1  2  3  4 All
## Control 365 22 28 14 429
## Treated  46 45 47 47 185
## Total   411 67 75 61 614

We can plot the standardized mean differences in a Love plot that also displays balance for the subclasses using plot.summary.matchit() on a summary.matchit() object with subclass = TRUE.

s <- summary(s.out, subclass = TRUE)
plot(s, var.order = "unmatched", abs = FALSE)

Note that for some variables, while the groups are balanced in aggregate (black dots), the individual subclasses (gray numbers) may not be balanced, in which case unadjusted effect estimates within these subclasses should not be interpreted as unbiased.

When we plot distributional balance using plot.matchit(), again we can choose whether balance should be displayed in aggregate or within subclasses again using the subclass option, which functions the same as it does with summary.matchit(). Below we demonstrate checking balance within a subclass.

plot(s.out, type = "density", which.xs = c("educ", "married", "re75"),
     subclass = 1)

If we had set subclass = FALSE, plots would have been displayed in aggregate using the subclassification weights. If subclass is unspecified, a prompt will ask us for which subclass we want to see balance.

Assessing Balance with cobalt

The cobalt package was designed specifically for checking balance before and after matching (and weighting). It offers three main functions, bal.tab(), love.plot(), and bal.plot(), which perform similar actions to summary.matchit(), plot.summary.matchit(), and plot.matchit(), respectively. These functions directly interface with matchit objects, making cobalt straightforward to use in conjunction with MatchIt. cobalt can be used as a complement to MatchIt, especially for more advanced uses that are not accommodated by MatchIt, such as comparing balance across different matching schemes and even different packages, assessing balance in clustered or multiply imputed data, and assessing balance with multi-category, continuous, and time-varying treatments. The main cobalt vignette contains many examples of its use with MatchIt objects, so we only provide a short demonstration of its capabilities here.

library("cobalt")

bal.tab()

bal.tab() produces tables of balance statistics similar to summary.matchit(). The columns displayed can be customized to limit how much information is displayed and isolate desired information. We call bal.tab() with a few of its options specified below:

bal.tab(m.out, un = TRUE, stats = c("m", "v", "ks"))
## Call
##  matchit(formula = treat ~ age + educ + race + married + nodegree + 
##     re74 + re75, data = lalonde, method = "full")
## 
## Balance Measures
##                 Type Diff.Un V.Ratio.Un KS.Un Diff.Adj V.Ratio.Adj KS.Adj
## distance    Distance   1.794      0.921 0.644    0.006       0.992  0.049
## age          Contin.  -0.309      0.440 0.158    0.157       0.485  0.322
## educ         Contin.   0.055      0.496 0.111    0.012       0.558  0.062
## race_black    Binary   0.640          . 0.640    0.009           .  0.009
## race_hispan   Binary  -0.083          . 0.083    0.001           .  0.001
## race_white    Binary  -0.558          . 0.558   -0.010           .  0.010
## married       Binary  -0.324          . 0.324    0.061           .  0.061
## nodegree      Binary   0.111          . 0.111    0.004           .  0.004
## re74         Contin.  -0.721      0.518 0.447   -0.021       1.201  0.235
## re75         Contin.  -0.290      0.956 0.288    0.002       2.005  0.231
## 
## Sample sizes
##                      Control Treated
## All                   429.       185
## Matched (ESS)          53.33     185
## Matched (Unweighted)  429.       185

The output is very similar to that of summary.matchit(), except that the balance statistics computed before matching (with the suffix .Un) and those computed after matching (with the suffix .Adj) are in the same table. By default, only SMDs after matching (Diff.Adj) are displayed; by setting un = TRUE, we requested that the balance statistics before matching also be displayed, and by setting stats = c("m", "v", "ks") we requested mean differences, variance ratios, and Kolmogorov-Smirnov statistics. Other balance statistics and summary statistics can be requested as well. One important detail to note is that the default for binary covariates is to print the raw difference in proportion rather than the standardized mean difference, so there will be an apparent discrepancy for these variables between bal.tab() and summary.matchit() output, though this behavior can be changed by setting binary = "std" in the call to bal.tab(). Functionality for producing balance statistics for additional variables and for powers and interactions of the covariates is available using the addl, poly, and int options.

bal.tab() and other cobalt functions can produce balance not just on a single matchit object but on several at the same time, which facilitates comparing balance across several matching specifications. For example, if we wanted to compare the full matching results to the results of nearest neighbor matching without replacement, we could supply both to bal.tab(), which we demonstrate below:

#Nearest neighbor (NN) matching on the PS
m.out2 <- matchit(treat ~ age + educ + race + married + 
                   nodegree + re74 + re75, data = lalonde)

#Balance on covariates after full and NN matching
bal.tab(treat ~ age + educ + race + married + 
          nodegree + re74 + re75, data = lalonde, 
        un = TRUE, weights = list(full = m.out, nn = m.out2))
## Balance Measures
##                Type Diff.Un Diff.full Diff.nn
## age         Contin.  -0.309     0.157   0.072
## educ        Contin.   0.055     0.012  -0.129
## race_black   Binary   0.640     0.009   0.373
## race_hispan  Binary  -0.083     0.001  -0.157
## race_white   Binary  -0.558    -0.010  -0.216
## married      Binary  -0.324     0.061  -0.022
## nodegree     Binary   0.111     0.004   0.070
## re74        Contin.  -0.721    -0.021  -0.050
## re75        Contin.  -0.290     0.002  -0.026
## 
## Effective sample sizes
##      Control Treated
## All   429.       185
## full   53.33     185
## nn    185.       185

This time, we supplied bal.tab() with the covariates and dataset and supplied the matchit output objects in the weights argument (which extracts the matching weights from the objects). Here we can see that full matching yields better balance than nearest neighbor matching overall, though balance is slightly worse for age and maried and the effective sample size is lower.

love.plot

love.plot() creates a Love plot of chosen balance statistics. It offers many options for customization, including the shape and colors of the points, how the variable names are displayed, and for which statistics balance is to be displayed. Below is an example of its basic use:

love.plot(m.out, binary = "std")

The syntax is straightforward and similar to that of bal.tab(). Below we demonstrate a more advanced use that customizes the appearance of the plot and displays balance not only on mean differences but also on Kolmogorov-Smirnov statistics and for both full matching and nearest neighbor matching simultaneously.

love.plot(m.out, stats = c("m", "ks"), poly = 2, abs = TRUE,
          weights = list(nn = m.out2),
          drop.distance = TRUE, thresholds = c(m = .1),
          var.order = "unadjusted", binary = "std",
          shapes = c("triangle", "square", "circle"), 
          colors = c("blue", "darkgreen", "red"),
          sample.names = c("Full Matching", "NN Matching", "Original"),
          position = "bottom")

The love.plot() documentation explains what each of these arguments do and the several other ones available. cobalt contains a vignette for other advanced customization of love.plot().

bal.plot()

bal.plot() displays distributional balance for a single covariate, similar to plot.matchit(). Its default is to display kernel density plots for continuous variables and bar graphs for categorical variables. It can also display eCDF plots and histograms. Below we demonstrate some of its uses:

#Density plot for continuous variables
bal.plot(m.out, var.name = "educ", which = "both")

#Bar graph for categorical variables
bal.plot(m.out, var.name = "race", which = "both")

#Mirrored histogram
bal.plot(m.out, var.name = "distance", which = "both",
         type = "histogram", mirror = TRUE)

These plots help illuminate the specific ways in which the covariate distributions differ between treatment groups, which can aid in interpreting the balance statistics provided by bal.tab() and summary.matchit().

Conclusion

The goal of matching is to achieve covariate balance, similarity between the covariate distributions of the treated and control groups. Balance should be assessed during the matching phase to find a matching specification that works. Balance must also be reported in the write-up of a matching analysis to demonstrate to readers that matching was successful. MatchIt and cobalt each offer a suite of functions to implement best practices in balance assessment and reporting.

References

Ali, M. Sanni, Rolf H. H. Groenwold, Wiebe R. Pestman, Svetlana V. Belitser, Kit C. B. Roes, Arno W. Hoes, Anthonius de Boer, and Olaf H. Klungel. 2014. “Propensity Score Balance Measures in Pharmacoepidemiology: A Simulation Study.” Pharmacoepidemiology and Drug Safety 23 (8): 802–11. https://doi.org/10.1002/pds.3574.
Austin, Peter C. 2009. “Balance Diagnostics for Comparing the Distribution of Baseline Covariates Between Treatment Groups in Propensity-Score Matched Samples.” Statistics in Medicine 28 (25): 3083–3107. https://doi.org/10.1002/sim.3697.
Austin, Peter C., and Elizabeth A. Stuart. 2015. “Moving Towards Best Practice When Using Inverse Probability of Treatment Weighting (IPTW) Using the Propensity Score to Estimate Causal Treatment Effects in Observational Studies.” Statistics in Medicine 34 (28): 3661–79. https://doi.org/10.1002/sim.6607.
Belitser, Svetlana V., Edwin P. Martens, Wiebe R. Pestman, Rolf H.H. Groenwold, Anthonius de Boer, and Olaf H. Klungel. 2011. “Measuring Balance and Model Selection in Propensity Score Methods.” Pharmacoepidemiology and Drug Safety 20 (11): 1115–29. https://doi.org/10.1002/pds.2188.
Diamond, Alexis, and Jasjeet S. Sekhon. 2013. “Genetic Matching for Estimating Causal Effects: A General Multivariate Matching Method for Achieving Balance in Observational Studies.” Review of Economics and Statistics 95 (3): 932945. https://doi.org/10.1162/REST_a_00318.
Franklin, Jessica M., Jeremy A. Rassen, Diana Ackermann, Dorothee B. Bartels, and Sebastian Schneeweiss. 2014. “Metrics for Covariate Balance in Cohort Studies of Causal Effects.” Statistics in Medicine 33 (10): 1685–99. https://doi.org/10.1002/sim.6058.
Hansen, Ben B. 2008. “The Prognostic Analogue of the Propensity Score.” Biometrika 95 (2): 481–88. https://doi.org/10.1093/biomet/asn004.
Heller, Ruth, Paul R. Rosenbaum, and Dylan S. Small. 2010. “Using the Cross-Match Test to Appraise Covariate Balance in Matched Pairs.” The American Statistician 64 (4): 299–309. https://doi.org/10.1198/tast.2010.09210.
Ho, Daniel E., Kosuke Imai, Gary King, and Elizabeth A. Stuart. 2007. “Matching as Nonparametric Preprocessing for Reducing Model Dependence in Parametric Causal Inference.” Political Analysis 15 (3): 199–236. https://doi.org/10.1093/pan/mpl013.
Huling, Jared D., and Simon Mak. 2020. “Energy Balancing of Covariate Distributions.” arXiv:2004.13962 [Stat], April. https://arxiv.org/abs/2004.13962.
Iacus, Stefano M., Gary King, and Giuseppe Porro. 2011. “Multivariate Matching Methods That Are Monotonic Imbalance Bounding.” Journal of the American Statistical Association 106 (493): 345–61. https://doi.org/10.1198/jasa.2011.tm09599.
Imai, Kosuke, Gary King, and Elizabeth A. Stuart. 2008. “Misunderstandings Between Experimentalists and Observationalists about Causal Inference.” Journal of the Royal Statistical Society. Series A (Statistics in Society) 171 (2): 481–502. https://doi.org/10.1111/j.1467-985X.2007.00527.x.
McCaffrey, Daniel F., Greg Ridgeway, and Andrew R. Morral. 2004. “Propensity Score Estimation With Boosted Regression for Evaluating Causal Effects in Observational Studies.” Psychological Methods 9 (4): 403–25. https://doi.org/10.1037/1082-989X.9.4.403.
Rubin, Donald B. 2001. “Using Propensity Scores to Help Design Observational Studies: Application to the Tobacco Litigation.” Health Services and Outcomes Research Methodology 2 (3-4): 169–88. https://doi.org/10.1023/A:1020363010465.
Stuart, Elizabeth A., Brian K. Lee, and Finbarr P. Leacy. 2013. “Prognostic Score-Based Balance Measures Can Be a Useful Diagnostic for Propensity Score Methods in Comparative Effectiveness Research.” Journal of Clinical Epidemiology 66 (8): S84. https://doi.org/10.1016/j.jclinepi.2013.01.013.

  1. Note that versions of MatchIt before 4.0.0 had standardize set to FALSE by default.↩︎

MatchIt/inst/doc/estimating-effects.html0000644000176200001440000063036614170752610020015 0ustar liggesusers Estimating Effects After Matching

Estimating Effects After Matching

Noah Greifer

2022-01-16

Introduction

After assessing balance and deciding on a matching specification, it comes time to estimate the effect of the treatment in the matched sample. How the effect is estimated and interpreted depends on the desired estimand and the type of model used (if any). In addition to estimating effects, estimating the uncertainty of the effects is critical in communicating them and assessing whether the observed effect is compatible with there being no effect in the population. This guide explains how to estimate effects after various forms of matching and with various outcome types. There may be situations that are not covered here for which additional methodological research may be required, but some of the recommended methods here can be used to guide such applications.

This guide is structured as follows: first, information on the concepts related to effect and standard error estimation is presented below. Then, instructions for how to estimate effects and standard errors are described. This section is split up first by the type of matching method performed (matching without replacement, matching with replacement, full matching, and subclassification) and then by the type of outcome (continuous, binary, and survival). Finally recommendations for reporting results and tips to avoid making common mistakes are presented.

Identifying the estimand

Before an effect is estimated, the estimand must be specified and clarified. Although some aspects of the estimand depend not only on how the effect is estimated after matching but also on the matching method itself, other aspects must be considered at the tine of effect estimation and interpretation. Here, we consider three aspects of the estimand: the population the effect is meant to generalize to (the target population), the effect measure, and whether the effect is marginal or conditional.

The target population. Different matching methods allow you to estimate effects that can generalize to different target populations. The most common estimand in matching the average treatment effect in the treated (ATT), which is the average effect of treatment for those who receive treatment. This estimand is estimable for matching methods that do not change the treated units (i.e., by weighting or discarding units) and is requested in matchit() by setting estimand = "ATT" (which is the default). The average treatment effect in the population (ATE) is the average effect of treatment for the population from which the sample is a random sample. This estimand is estimable only for methods that allow the ATE and either do not discard units from the sample or explicit target full sample balance, which in MatchIt is limited to full matching, subclassification, and template matching when setting estimand = "ATE". When treated units are discarded (e.g., through the use of common support restrictions, calipers, cardinality matching, or [coarsened] exact matching), the estimand corresponds to neither the population ATT nor the population ATE, but rather to an average treatment effect in the remaining matched sample (ATM), which may not correspond to any specific target population.

Marginal and conditional effects. A marginal effect is a comparison between the expected potential outcome under treatment and the expected potential outcome under control. This is the same quantity estimated in randomized trials without blocking or covariate adjustment and is particularly useful for quantifying the overall effect of a policy or population-wide intervention. A conditional effect is the comparison between the expected potential outcomes in the treatment groups within strata. This is useful for identifying the effect of a treatment for an individual patient or a subset of the population.

Effect measures. The outcome types we consider here are continuous, with the effect measured by the mean difference; binary, with the effect measured by the risk difference (RD), risk ratio (RR), or odds ratio (OR); and time-to-event (i.e., survival), with the effect measured by the hazard ratio (HR). The RR, OR, and HR are noncollapsible effect measures, which means the marginal effect on that scale is not a (possibly) weighted average of the conditional effects within strata, even if the stratum-specific effects are of the same magnitude. For these effect measures, it is critical to distinguish between marginal and conditional effects because different statistical methods target different types of effects. The mean difference and RD are collapsible effect measures, so the same methods can be used to estimate marginal and conditional effects.

In this guide, we will provide examples for estimating the ATT, ATE, and ATM for each of the three outcomes types. Our primary focus will be on marginal effects, which are appropriate for all effect measures, easily interpretable, and require few modeling assumptions. We will include a short section on estimating the conditional OR. The “Common Mistakes” section includes examples of commonly used methods that estimate conditional rather than marginal effects and should not be used when marginal effects are desired.

Modeling the Outcome

The type and form of the model used to estimate the treatment effect depend on the effect measure desired, whether a marginal or conditional effect is desired, whether covariates are to be included in the model, and whether effect modification by the covariates is present. For continuous outcomes, one can use a linear model regressing the outcome on the treatment; for binary outcomes, one can use a generalized linear model with a link function appropriate for the desired effect measure (e.g., a logistic link for the OR); for time-to-event outcomes, one can use a Cox proportional hazards model.

An additional decision to make is whether (and how) to include covariates in the outcome model. One may ask, why use matching at all if you are going to model the outcome with covariates anyway? Matching reduces the dependence of the effect estimate on correct specification of the outcome model; this is the central thesis of Ho et al. (2007). Including covariates in the outcome model after matching has several functions: it can increase precision in the effect estimate, reduce the bias due to residual imbalance, and make the effect estimate “doubly robust,” which means it is consistent if either the matching reduces sufficient imbalance in the covariates or if the outcome model is correct. For these reasons, we recommend covariate adjustment after matching when possible. There is some evidence that covariate adjustment is most helpful for covariates with standardized mean differences greater than .1 (Nguyen et al. 2017), so these covariates and covariates thought to be highly predictive of the outcome should be prioritized in treatment effect models if not all can be included due to sample size constraints.

For continuous outcomes, we can simply include the covariates in the regression of the outcome on the treatment in the matched sample (i.e., using the matching weights). Because the mean difference is collapsible, the effect estimate conditioning on the covariates is still a marginal effect estimate. This is not the case with binary and time-to-event outcomes; including covariates in the outcome model makes the treatment effect a conditional effect. To recover a marginal effect while including covariates in an outcome model, one has to perform a marginal effects procedure, also known as g-computation (Snowden, Rose, and Mortimer 2011) or regression estimation (Schafer and Kang 2008), which involves simulating the average potential outcomes under each treatment level and computing the effect as a contrast between those average potential outcomes. G-computation can also be used to estimate marginal effects when modeling effect modification by the covariates. We demonstrate examples of this below.

Although there are many possible ways to include covariates (i.e., not just main effects but interactions, smoothing terms like splines, or other nonlinear transformations), it is important not to engage in specification search (i.e., trying many outcomes models in search of the “best” one). Doing so can invalidate results and yield a conclusion that fails to replicate. For this reason, we recommend only including the same terms included in the propensity score model unless there is a strong a priori and justifiable reason to model the outcome differently. Second, it is important not to interpret the coefficients and tests of the other covariates in the outcome model. These are not causal effects and their estimates may be severely confounded. Only the treatment effect estimate can be interpreted as causal assuming the relevant assumptions about unconfoundedness are met. Inappropriately interpreting the coefficients of covariates in the outcome model is known as the Table 2 fallacy (Westreich and Greenland 2013). To avoid this, in all examples that incorporate covariates in the outcome model, we restrict the output of outcome regression models to just the treatment coefficient.

Estimating Standard Errors and Confidence Intervals

Uncertainty estimation (i.e., of standard errors, confidence intervals, and p-values) may consider the variety of sources of uncertainty present in the analysis, including (but not limited to!) estimation of the propensity score (if used), matching (i.e., because treated units might be matched to different control units if others had been sampled), and estimation of the treatment effect (i.e., because of sampling error). In general, there are no analytic solutions to all these issues, so much of the research done on uncertainty estimation after matching has relied on simulation studies. The two primary methods that have been shown to perform well in matched samples are using cluster-robust standard errors and the bootstrap.

Robust and Cluster-Robust Standard Errors

Robust standard errors. Also known as sandwich standard errors (due to the form of the formula for computing them), heteroscedasticity-consistent standard errors, or Huber-White standard errors, robust standard errors are an adjustment to the usual maximum likelihood or ordinary least squares standard errors that are robust to violations of some of the assumptions required for usual standard errors to be valid (MacKinnon and White 1985). Although there has been some debate about their utility (King and Roberts 2015), robust standard errors rarely degrade inferences and often improve them. Generally, robust standard errors must be used when any non-uniform weights are included in the estimation (e.g., with full matching or inverse probability weighting).

Cluster-robust standard errors. A version of robust standard errors known as cluster-robust standard errors (Liang and Zeger 1986) can be used to account for dependence between observations within clusters (e.g., matched pairs). Abadie and Spiess (2019) demonstrate analytically that cluster-robust standard errors are generally valid after matching, whereas regular robust standard errors can over- or under-estimate the true sampling variability of the effect estimator depending on the specification of the outcome model (if any) and degree of effect modification. A plethora of simulation studies have further confirmed the validity of cluster-robust standard errors after matching (e.g., Austin 2009, 2013a; Austin and Small 2014; Gayat et al. 2012; Wan 2019). Given this evidence favoring the use of cluster-robust standard errors, we recommend them in most cases and use them judiciously in the this guide1.

Here, we will use linear and generalized linear models as implemented by lm() and glm() and use lmtest::coeftest() and lmtest::coefci() along with sandwich::vcovHC() for robust standard errors and sandwich::vcovCL() for cluster-robust standard errors. There are several “types” (e.g., HC0, HC1, etc.) of robust standard errors that adjust the original standard errors in different ways; although more research is required on the benefits of using different types for estimating standard errors after matching, we use the default types here.

Bootstrapping

Bootstrapping is a technique used to simulate the sampling distribution of an estimator by repeatedly drawing samples with replacement and estimating the effect in each bootstrap sample (Efron and Tibshirani 1993). From the bootstrap distribution, standard errors and confidence interval can be computed in several ways, including using the standard deviation of the bootstrap estimates as the standard error estimate or using the 2.5 and 97.5 percentiles as 95% confidence interval bounds. Bootstrapping tends to be most useful when no analytic estimator of a standard error is possible or has been derived yet. Although Abadie and Imbens (2008) found analytically that the bootstrap is inappropriate for matched samples, simulation evidence has found it to be adequate in many cases (Hill and Reiter 2006; Austin and Small 2014; Austin and Stuart 2017). It is the most accessible method for computing standard errors after g-computation for nonlinear models.

Typically, bootstrapping involves performing the entire estimation process in each bootstrap sample, including propensity score estimation, matching, and effect estimation. This tends to be the most straightforward route, though intervals from this method may be conservative in some cases (i.e., they are wider than necessary to achieve nominal coverage) (Austin and Small 2014). Less conservative and more accurate intervals have been found when using different forms of the bootstrap, including the wild bootstrap develop by Bodory et al. (2020) and the matched bootstrap described by Austin and Small (2014) and Abadie and Spiess (2019). The block bootstrap involves sampling matched pairs/strata of units from the matched sample and performing the analysis within each sample composed of the sampled pairs. Abadie and Spiess (2019) derived analytically that the block bootstrap is valid for estimating SEs and CIs in the same circumstances cluster robust SEs are; indeed, the block bootstrap SE is known to approximate the cluster-robust SE (Cameron and Miller 2015). We use boot() from the boot package to implement bootstrapping.

With bootstrapping, more bootstrap replications are always better but can take time and increase the chances that at least one error will occur within the bootstrap analysis (e.g., a bootstrap sample with zero treated units or zero units with an event). In general, numbers of replications upwards of 999 are recommended, with values one less than a multiple of 100 preferred to avoid interpolation when using the percentiles as confidence interval limits (MacKinnon 2006). There are several methods of computing bootstrap confidence intervals, but the bias-corrected accelerated (BCa) bootstrap confidence interval often performs best (Austin and Small 2014; Carpenter and Bithell 2000) and is easy to implement, simply by setting type = "bca" in the call to boot.ci() after running boot()2.

Estimating Treatment Effects and Standard Errors After Matching

Below, we describe effect estimation after several methods of matching. We consider four broad types of matching that require their own specific methods for estimation effects: 1) pair matching without replacement, 2) pair matching with replacement, 3) full matching, and 4) stratification. In some cases, methods for estimating effects are similar across methods, and we err on the side of redundancy here in our instructions. We also consider three different outcome types: 1) continuous, 2) binary, and 3) survival.

We’ll be using a simulated toy dataset d with several outcome types. Code to generate the dataset is at the end of this document. The focus here is not on evaluating the methods but simply on demonstrating them. In all cases, the correct propensity score model is used. Below we display the first six rows of d:

head(d)
##   A      X1      X2      X3       X4 X5      X6      X7      X8       X9      Y_C Y_B     Y_S
## 1 0  0.1725 -1.4283 -0.4103 -2.36059  1 -1.1199  0.6398 -0.4840 -0.59385  0.07104   0  278.46
## 2 0 -1.0959  0.8463  0.2456 -0.12333  1 -2.2687 -1.4491 -0.5514 -0.31439  0.15619   0  330.63
## 3 0  0.1768  0.7905 -0.8436  0.82366  1 -0.2221  0.2971 -0.6966 -0.69516 -0.85180   1  369.94
## 4 0 -0.4595  0.1726  1.9542 -0.62661  1 -0.4019 -0.8294 -0.5384  0.20729 -2.35184   0   91.06
## 5 1  0.3563 -1.8121  0.8135 -0.67189  1 -0.8297  1.7297 -0.6439 -0.02648  0.68058   0  182.73
## 6 0 -2.4313 -1.7984 -1.2940  0.04609  1 -1.2419 -1.1252 -1.8659 -0.56513 -5.62260   0 2563.73

A is the treatment variable, X1 through X9 are covariates, Y_C is a continuous outcome, Y_B is a binary outcome, and Y_S is a survival outcome.

We will need to the following packages to perform the desired analyses:

  • lmtest provides the coeftest() and coefci() functions for estimating coefficients, standard errors, and confidence intervals incorporating robust standard errors
  • sandwich provides robust and cluster robust standard errors through the vcovHC() and vcovCL() functions, respectively
  • boot provides the boot() and boot.ci() functions for performing bootstrapping and estimating bootstrap effects, standard errors, and confidence intervals.
  • survival provides the functions survdiff() and coxph() to perform the log-rank test for differences in survival curves and estimate the coefficients in a Cox-proportional hazards model for the marginal hazard ratio, respectively, which we will use for survival outcomes.

Of course, we also need MatchIt to perform the matching.

library("MatchIt")
library("lmtest")
library("sandwich")
library("boot")
library("survival")

Other packages may be of use but are not used here. The margins package can be useful for computing marginal effects and standard errors without bootstrapping. Some examples using margins are presented. The survey package can be used to estimate robust standard errors incorporating weights and provides functions for survey-weighted generalized linear models and Cox-proportional hazards models. It is often used with propensity score weighting. The Zelig package provides a broad interface to estimating marginal and conditional effects using simulation and was included in the original documentation for MatchIt. The lme4 (and lmerTest) package performs mixed effects modeling, which can be useful for accounting for pair membership or other clustering features of the data.

Because different matching methods require different treatments, instructions for each method are organized in the following sections, as designated by the table below. It is important to ensure the right methods are used with the matching specification used in order for the estimated effects and standard errors to be valid.

Section Matching Method
After Pair Matching Without Replacement Nearest neighbor matching without replacement
- Optimal matching
- Genetic matching without replacement
- Coarsened exact matching with k2k = TRUE
After Pair Matching With Replacement Nearest neighbor matching with replacement
- Genetic matching with replacement
After Full Matching Full matching
Cardinality and template matching
After Stratum Matching Propensity score subclassification
- Exact matching
- Coarsened exact matching with k2k = FALSE (the default)

After Pair Matching Without Replacement

Pair matching without replacement yields the simplest way to estimate treatment effects and standard errors. In general, whether a caliper, common support restriction, exact matching specification, or \(k\):1 matching specification is used, estimating the effect in the matched dataset (i.e., the output of match.data()) is straightforward and involves fitting a model for the outcome that incorporates the matching weights3.

First, we will perform nearest 1:1 neighbor propensity score matching without replacement.

mNN <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = d)

mNN
## A matchit object
##  - method: 1:1 nearest neighbor matching without replacement
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 2000 (original), 882 (matched)
##  - target estimand: ATT
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9
md <- match.data(mNN)

head(md)
##    A       X1      X2      X3      X4 X5      X6      X7      X8       X9      Y_C Y_B     Y_S distance weights subclass
## 1  0  0.17254 -1.4283 -0.4103 -2.3606  1 -1.1199  0.6398 -0.4840 -0.59385  0.07104   0  278.46  0.08461       1      167
## 3  0  0.17677  0.7905 -0.8436  0.8237  1 -0.2221  0.2971 -0.6966 -0.69516 -0.85180   1  369.94  0.22210       1      210
## 5  1  0.35631 -1.8121  0.8135 -0.6719  1 -0.8297  1.7297 -0.6439 -0.02648  0.68058   0  182.73  0.43291       1      322
## 9  0  0.78080  1.3137  0.6580  0.8540  1  0.9495 -0.5731 -0.2362 -0.14580 15.89771   1   67.53  0.15751       1        3
## 10 1 -0.56513 -0.1053 -0.1369  1.6233  1 -0.5304 -0.3342  0.4184  0.46308  1.07888   1  113.70  0.16697       1        1
## 11 0  0.07053 -0.1320 -1.8882  0.3077  0 -1.6558  0.8405 -0.5723 -1.01382 -6.55597   0 1632.69  0.45623       1      298

Typically one would assess balance and ensure that this matching specification works, but we will skip that step here to focus on effect estimation. See vignette("MatchIt") and vignette("assessing-balance") for more information on this necessary step. Because we did not use a caliper, the target estimand is the ATT.

For continuous outcomes

For continuous outcomes, estimating effects is fairly straightforward using linear regression. We perform all analyses using the matched dataset, md, which contains only units retained in the sample.

Without covariate adjustment. To estimate the effect without covariate adjustment, we can run the following:

#Linear model without covariates
fit1 <- lm(Y_C ~ A, data = md, weights = weights)

First, we will estimate the standard errors while accounting for pair membership using cluster-robust standard errors.

#Cluster-robust standard errors
coeftest(fit1, vcov. = vcovCL, cluster = ~subclass)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    1.779      0.303    5.88  5.8e-09 ***
## A              2.114      0.409    5.18  2.8e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Using the bootstrap. We demonstrate bootstrapping here using the block bootstrap as recommended by Abadie and Spiess (2019). The process is generalizable to other matching methods for which cluster-robust standard errors are valid and to other outcome types. We first need to write a function, est_fun, which takes in a vector of pair identifiers and an ordering and outputs an effect estimate. This function should include the effect estimation, though no standard error is required.

#Block bootstrap confidence interval
# library(boot)

pair_ids <- levels(md$subclass)

est_fun <- function(pairs, i) {
  
  #Compute number of times each pair is present
  numreps <- table(pairs[i])
  
  #For each pair p, copy corresponding md row indices numreps[p] times
  ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)],
                       function(p) rep(which(md$subclass == p), 
                                              numreps[p])))
  
  #Subset md with block bootstrapped ids
  md_boot <- md[ids,]
  
  #Effect estimation
  fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights)
  
  #Return the coefficient on treatment
  return(coef(fit_boot)["A"])
}

boot_est <- boot(pair_ids, est_fun, R = 499)
boot_est
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = pair_ids, statistic = est_fun, R = 499)
## 
## 
## Bootstrap Statistics :
##     original   bias    std. error
## t1*    2.114 0.003777      0.3862
boot.ci(boot_est, type = "bca")
## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
## Based on 499 bootstrap replicates
## 
## CALL : 
## boot.ci(boot.out = boot_est, type = "bca")
## 
## Intervals : 
## Level       BCa          
## 95%   ( 1.317,  2.758 )  
## Calculations and Intervals on Original Scale
## Some BCa intervals may be unstable

The value t1* in the Bootstrap Statistics table contains the effect estimate and its bootstrap standard error, and the interval in the confidence interval output is the bootstrap confidence interval computed using the specified type (here, "bca").

With covariate adjustment. Including covariates in the outcome model is straightforward with a continuous outcome. We can include main effects for each variable and use the coefficient on the treatment as the treatment effect estimate. It can also be helpful to include interactions between the covariates and treatment if effect modification is suspected, though it is important to center the covariates at their means in the focal (i.e., treated) group if the coefficient on the treatment is to be interpreted as an average treatment effect estimate (which we demonstrate with full matching). A marginal effects procedure can also be used, which we demonstrate with binary outcomes. Below we simply include main effects of each covariate, which is the most typical way to include covariates.

#Linear model with covariates
fit3 <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + 
             X6 + X7 + X8 + X9, data = md,
           weights = weights)

coeftest(fit3, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE]
##   Estimate Std. Error t value  Pr(>|t|)
## A    2.029     0.3202   6.338 3.735e-10

As previously mentioned, it is important to avoid interpreting coefficients in the outcome model on predictors other than the treatment, as the coefficients do no correspond to causal effects and are likely confounded. In the code above, we restricted the output to just the treatment effect. Note that sometimes the intercept of the outcome model can be useful as an estimate of the average potential outcome under control, but the covariates in the model (if any) must be centered at their means in the target population to allow for this interpretation. Without centering the covariates, the intercept is uninterpretable.

For binary outcomes

For binary outcomes, effect estimation can be a bit more challenging, especially when including covariates. There are several measures of the effect one can consider, which include the odds ratio (OR), risk ratio/relative risk (RR), and risk difference (RD).

Without covariate adjustment. When omitting covariates from the outcome model, effect and standard error estimation is fairly straightforward. Below we demonstrate how to estimate the marginal log OR after nearest neighbor matching without replacement.

#Generalized linear model without covariates
fit4 <- glm(Y_B ~ A, data = md, weights = weights, 
            family = binomial(link = "logit"))

By specifying link = "logit", we fit a logistic regression model to estimate the marginal OR for treatment. To estimate the marginal RR we can specify link = "log", and to estimate the RD we can specify link = "identity" or use lm() instead of glm(). Below we estimate cluster-robust standard errors, though it should be noted that these standard errors can be biased for the OR and should be used with caution (Austin 2007). Because we want the OR and the effects are estimated on the log OR scale, we have to exponentiate the coefficient on treatment to arrive at the OR (one would do this when estimating the OR or RR, but not the RD).

#Cluster-robust standard errors
coeftest(fit4, vcov. = vcovCL, cluster = ~subclass)
## 
## z test of coefficients:
## 
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -0.939      0.106   -8.85  < 2e-16 ***
## A              0.834      0.143    5.82  5.7e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
exp(coef(fit4)) #OR
## (Intercept)           A 
##      0.3912      2.3030

With covariate adjustment and bootstrapping. If we want to include covariates in the model, we have to do some additional work to estimate the effect and its standard error. This is because the coefficient on treatment in a nonlinear model with covariates corresponds to the conditional rather than marginal effect. To estimate a marginal effect, we have to perform a marginal effects procedure, which is equivalent to g-computation in the matched set. First we estimate an outcome model including the covariates, and then we use the predictions from the outcome model to compute the contrast of the average potential outcomes under treatment and control. Bootstrapping is the most straightforward method to estimate standard errors. We demonstrate this below using the block bootstrap as recommended by Abadie and Spiess (2019).

#Block bootstrap confidence interval
# library(boot)

pair_ids <- levels(md$subclass)

est_fun <- function(pairs, i) {
  
  #Compute number of times each pair is present
  numreps <- table(pairs[i])
  
  #For each pair p, copy corresponding md row indices numreps[p] times
  ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)],
                       function(p) rep(which(md$subclass == p), 
                                              numreps[p])))
  
  #Subset md with block bootstrapped ids
  md_boot <- md[ids,]
  
  #Fitting outcome the model
  fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + 
                    X6 + X7 + X8 + X9, data = md_boot, 
                  family = binomial(link = "logit"),
                  weights = weights)
  
  #Estimate potential outcomes for each unit
  #Under control
  md_boot$A <- 0
  P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"),
                      w = md_boot$weights)
  Odds0 <- P0 / (1 - P0)
  
  #Under treatment
  md_boot$A <- 1
  P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"),
                      w = md_boot$weights)
  Odds1 <- P1 / (1 - P1)

  #Return marginal odds ratio
  return(Odds1 / Odds0)
}

boot_est <- boot(pair_ids, est_fun, R = 499)
boot_est
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = pair_ids, statistic = est_fun, R = 499)
## 
## 
## Bootstrap Statistics :
##     original  bias    std. error
## t1*    2.225  0.0291      0.2727
boot.ci(boot_est, type = "bca")
## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
## Based on 499 bootstrap replicates
## 
## CALL : 
## boot.ci(boot.out = boot_est, type = "bca")
## 
## Intervals : 
## Level       BCa          
## 95%   ( 1.785,  2.827 )  
## Calculations and Intervals on Original Scale
## Some BCa intervals may be unstable

To use bootstrapping for the RR or RD, the part of the code that computes the marginal OR can be replaced with code to compute the marginal RR (P1 / P0) or marginal RD (P1 - P0). Interactions between the treatment and covariates can be included in the outcome model; no other part of the procedure needs to be changed. Doing so can add robustness when effect modification is possible.

For survival outcomes

There are several measures of effect size for survival outcomes. When using the Cox proportional hazards model, the quantity of interest is the HR between the treated and control groups. As with the OR, the HR is non-collapsible, which means the estimated HR will only be a valid estimate of the marginal HR when no other covariates are included in the model. Other effect measures, such as the difference in mean survival times or probability of survival after a given time, can be treated just like continuous and binary outcomes as previously described. Here we describe estimating the marginal HR.

Without covariate adjustment. Below we demonstrate estimation of the marginal hazard ratio using coxph() from the survival package. To request cluster-robust standard errors as recommended by Austin (2013b), we need to supply pair membership (stored in the subclass column of md) to the cluster argument and set robust = TRUE.

#Cox Regression for marginal HR
coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, 
      weights = weights, cluster = subclass)
## Call:
## coxph(formula = Surv(Y_S) ~ A, data = md, weights = weights, 
##     robust = TRUE, cluster = subclass)
## 
##   coef exp(coef) se(coef) robust se z     p
## A 0.43      1.53     0.07      0.07 6 1e-09
## 
## Likelihood ratio test=39  on 1 df, p=5e-10
## n= 882, number of events= 882

The coef column contains the log HR, and exp(coef) contains the HR. Remember to always use the robust se for the standard error of the log HR. The displayed z-test p-value results from using the robust standard error.

Using the bootstrap. Austin and Small (2014) found bootstrap confidence intervals to work well for marginal hazard ratios. Below we demonstrate this using similar code as before to implement the block bootstrap:

#Block bootstrap confidence interval
# library(boot)

pair_ids <- levels(md$subclass)

est_fun <- function(pairs, i) {
  
  #Compute number of times each pair is present
  numreps <- table(pairs[i])
  
  #For each pair p, copy corresponding md row indices numreps[p] times
  ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)],
                       function(p) rep(which(md$subclass == p), 
                                              numreps[p])))
  
  #Subset md with block bootstrapped ids
  md_boot <- md[ids,]
  
  #Effect estimation
  cox_fit_boot <- coxph(Surv(Y_S) ~ A, data = md_boot, weights = weights)
  
  #Compute the marginal HR by exponentiating the coefficient
  #on treatment
  HR <- exp(coef(cox_fit_boot)["A"])
  
  #Return the HR
  return(HR)
}

boot_est <- boot(pair_ids, est_fun, R = 499)
boot_est
boot.ci(boot_est, type = "bca")

With covariate adjustment. As with binary outcomes, if covariates are included in the model, the resulting hazard ratios will be conditional rather than marginal. Austin, Thomas, and Rubin (2020) describe several ways of including covariates in a model to estimate the marginal HR, but because they do not develop standard errors and little research has been done on this method, we will not present it here.

After Pair Matching With Replacement

Pair matching with replacement makes estimating effects and standard errors a bit less straightforward. Control units paired with multiple treated units belong to multiple pairs at the same time and appear multiple times in the matched dataset. Effect and standard error estimation need to account for control unit multiplicity (i.e., repeated use) and within-pair correlations (Hill and Reiter 2006; Austin and Cafri 2020).

MatchIt provides two interfaces for extracting the matched dataset after matching with replacement. The first uses match.data() and provides the same output as when used after matching without replacement, except that the subclass column is absent because units are not assigned to a single subclass but rather to several. Control units will have weights that differ from 1 to reflect their use as matches for multiple treated units. When using the match.data() interface, including the weights in the estimation of effects is crucial. Because pair membership is omitted, accounting for it (if desired) must be done by conditioning on covariates used to match the pairs or by bootstrapping the entire process.

The second interface uses get_matches(), which functions similarly to match.data() except that the dataset contains one row per unit per pair, so control units matched to multiple treated units will have multiple rows in the dataset, and a subclass column is included denoting pair membership. In the get_matches() output, each reused control unit will have multiple rows, identical to each other except with different subclass membership. When performing k:1 matching, weights must also be included in effect estimation when using get_matches().

Here we will demonstrate the estimation of effects using both match.data() and get_matches() output. The match.data() output is preferred when pair membership is not directly included in the analysis, and the get_matches() output is preferred when pair membership is to be included. Here will will use 3:1 matching with replacement and with a caliper; note that because a caliper was used, the estimand corresponds to neither the ATT nor the ATE but rather to an ATM.

mNNr <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = d,
               link = "linear.logit", caliper = .1,
               ratio = 3, replace = TRUE)

mNNr
## A matchit object
##  - method: 3:1 nearest neighbor matching with replacement
##  - distance: Propensity score [caliper]
##              - estimated with logistic regression and linearized
##  - caliper: <distance> (0.13)
##  - number of obs.: 2000 (original), 1040 (matched)
##  - target estimand: ATT
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9
#match.data output
md <- match.data(mNNr)
nrow(md)
## [1] 1040
head(md)
##    A      X1      X2      X3      X4 X5      X6      X7      X8       X9      Y_C Y_B    Y_S distance weights
## 1  0  0.1725 -1.4283 -0.4103 -2.3606  1 -1.1199  0.6398 -0.4840 -0.59385  0.07104   0 278.46   -2.381  0.9571
## 3  0  0.1768  0.7905 -0.8436  0.8237  1 -0.2221  0.2971 -0.6966 -0.69516 -0.85180   1 369.94   -1.253  0.4785
## 5  1  0.3563 -1.8121  0.8135 -0.6719  1 -0.8297  1.7297 -0.6439 -0.02648  0.68058   0 182.73   -0.270  1.0000
## 7  0  1.8402  1.7601 -1.0746 -1.6428  1  1.4482  0.7131  0.6972 -0.94673  4.28651   1  97.49   -2.281  0.4785
## 9  0  0.7808  1.3137  0.6580  0.8540  1  0.9495 -0.5731 -0.2362 -0.14580 15.89771   1  67.53   -1.677  0.4785
## 10 1 -0.5651 -0.1053 -0.1369  1.6233  1 -0.5304 -0.3342  0.4184  0.46308  1.07888   1 113.70   -1.607  1.0000
#get_matches output
gm <- get_matches(mNNr)
nrow(gm)
## [1] 1681
head(gm)
##    id subclass weights A       X1      X2       X3      X4 X5      X6      X7      X8       X9     Y_C Y_B    Y_S distance
## 1   5        1  1.0000 1  0.35631 -1.8121  0.81349 -0.6719  1 -0.8297  1.7297 -0.6439 -0.02648  0.6806   0 182.73  -0.2700
## 2 740        1  0.3333 0  0.02725 -0.4570 -0.32506  1.1203  1  0.3795  0.6468 -0.9992  0.01917 -0.9042   1 215.25  -0.2736
## 3 939        1  0.3333 0  0.92888 -0.2562  0.39077  0.3305  1  0.2654  1.2038  0.1720  1.21006  7.8923   0  83.85  -0.2736
## 4 860        1  0.3333 0 -0.88047  0.9117 -0.38871  1.0696  0  1.7953  0.2470 -1.6115  1.49297  5.3808   0 236.01  -0.2752
## 5  10        2  1.0000 1 -0.56513 -0.1053 -0.13685  1.6233  1 -0.5304 -0.3342  0.4184  0.46308  1.0789   1 113.70  -1.6073
## 6 399        2  0.3333 0  0.80396 -0.8760  0.03938 -0.5895  1  0.2819 -0.6540 -0.5423  1.16852  4.4816   1 117.28  -1.6065

The get_matches() output provides some additional information about the match. We can count how many times control units are reused and how many units are in each match strata (not all will have 3 control units due to the caliper).

#Number of time control units are rematched
table(table(gm$id[gm$A == 0]))
## 
##   1   2   3   4   5   6   7   8   9  10  13  14 
## 332 138  62  29  19   9  14   4   2   2   1   1

Here we can see that 332 control units were only used in one pair each, and one control unit was paired with 14 treated units (i.e., heavily reused).

#Number of control units in each match stratum
table(table(gm$subclass[gm$A == 0]))
## 
##   1   2   3 
##   9   9 409

Here we can see that 409 treated units have three matches, nine have two matches, and nine only have one match. The caliper did not end up restricting too many matches.

For continuous outcomes

Without covariate adjustment. For continuous outcomes, we can regress the outcome on the treatment and include the weights in the estimation. We do this regardless of whether we are using the match.data() output or the get_matches() output (if we were doing 1:1 matching, the weights would not be necessary when using the get_matches() output, but they don’t change the results if included).

If we don’t mind ignoring pair membership, we can use the match.data() output to estimate the effect and standard errors. Here we use vcovHC() to estimate regular robust standard errors that ignoring pairing. Hill and Reiter (2006) found these standard errors to be conservative for continuous outcomes.

#match.data() output
fit1md <- lm(Y_C ~ A, data = md, weights = weights)

coeftest(fit1md, vcov. = vcovHC)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    1.798      0.420    4.28  2.0e-05 ***
## A              2.065      0.514    4.02  6.3e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

If we want to incorporate pair membership into the standard error estimation, we have to use the get_matches() output. In addition to supplying pair membership (subclass) to the standard error estimator, we also supply unit ID (id) to account for the fact that several rows may refer to the same control unit.

#get_matches() output
fit1gm <- lm(Y_C ~ A, data = gm, weights = weights)

coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    1.798      0.416    4.32  1.7e-05 ***
## A              2.065      0.510    4.05  5.4e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Note that the effect estimates are identical; only the standard errors and p-values differ between the approaches4.

Using the bootstrap. We can also use bootstrapping to estimate standard errors and confidence intervals. Although Abadie and Imbens (2008) demonstrated analytically that bootstrap standard errors may be invalid for matching with replacement, simulation work by Hill and Reiter (2006) and Bodory et al. (2020) has found that bootstrap standard errors are adequate and generally slightly conservative. Given this disagreement, it may be worth proceeding with caution when using bootstrapping to estimate standard errors after matching with replacement. Simulation experiments with matching with replacement have only considered the full bootstrap and found it to yield confidence intervals with approximately nominal coverage, so we recommend this approach over the block bootstrap for now and demonstrate it below.

#Full bootstrap confidence interval
# library(boot)

est_fun <- function(data, i) {
  #Matching function
  mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                         X6 + X7 + X8 + X9, data = data[i,],
                       link = "linear.logit", caliper = .1,
                       ratio = 3, replace = TRUE)
  md_boot <- match.data(mNNr_boot)
  
  #Effect estimation
  fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights)
  
  #Return the coefficient on treatment
  return(coef(fit_boot)["A"])
}

boot_est <- boot(d, est_fun, R = 499)
boot_est
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = d, statistic = est_fun, R = 499)
## 
## 
## Bootstrap Statistics :
##     original  bias    std. error
## t1*    2.065 -0.1287      0.4398
boot.ci(boot_est, type = "perc")
## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
## Based on 499 bootstrap replicates
## 
## CALL : 
## boot.ci(boot.out = boot_est, type = "perc")
## 
## Intervals : 
## Level     Percentile     
## 95%   ( 1.049,  2.796 )  
## Calculations and Intervals on Original Scale

With covariate adjustment. We can include covariates in the outcome model just as we could when matching without replacement. Hill and Reiter (2006) found that standard errors that fail to account for pair membership can have lower the nominal confidence interval coverage, so we recommend using the get_matches() output to address both multiplicity and clustering.

fit2md <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = gm, 
             weights = weights)

coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id)["A",,drop = FALSE]
##   Estimate Std. Error t value  Pr(>|t|)
## A    2.065       0.51   4.048 5.392e-05

Remember that the coefficients and tests on the predictors other than the treatment should not be interpreted because they may be subject to confounding even if the treatment is not, so we omit them here.

For binary outcomes

The primary difference between dealing with binary and continuous outcomes is the noncollapsibility of the effect measures for binary outcomes, meaning that including covariates in the outcome model is less straightforward because the coefficient on treatment does not correspond to the marginal treatment effect. Similar to continuous outcomes, when estimating the treatment effect, we can use either the output of match.data() or the output of get_matches(), only the latter of which allows us to account both for multiplicity in the control units and for pair membership. Below we’ll demonstrate estimating the marginal OR accounting for pair membership using the get_matches() output. Then we will demonstrate using the bootstrap to estimate standard errors that include covariates in the model. Note that there has been little research on the use of matching with replacement with binary outcomes, so one should proceed with caution and consider using a better-studied matching method.

Without covariate adjustment. We include the weights in the call to glm() and we include both subclass and id as clustering variables in computing the cluster-robust standard errors, just as we would with a continuous outcome. We can use family = quasibinomial instead of family = binomial to avoid a warning due to the use of weights; the estimates will be the same either way. To estimate the marginal RR or RD, "logit" would be replaced with "log" or "identity", respectively.

fit3gm <- glm(Y_B ~ A, data = gm, weights = weights,
              family = quasibinomial(link = "logit"))

coeftest(fit3gm, vcov. = vcovCL, cluster = ~ subclass + id)
## 
## z test of coefficients:
## 
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -0.766      0.139   -5.50  3.9e-08 ***
## A              0.639      0.169    3.78  0.00016 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
exp(coef(fit3gm)) #OR
## (Intercept)           A 
##      0.4648      1.8954

With covariate adjustment and bootstrapping. To include covariates, we can use the bootstrap as we did before when matching without replacement. It doesn’t matter whether the match.data() or get_matches() output is used because, as we saw before, both yield the same effect estimate in each bootstrap replication. Here we use the full bootstrap rather than the block bootstrap because the performance of the block bootstrap after matching with replacement has not been evaluated.

#Bootstrap confidence intervals
# library(boot)

est_fun <- function(data, i) {
  #Matching function
  mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = data[i,],
               link = "linear.logit", caliper = .1,
               ratio = 3, replace = TRUE)
  md_boot <- match.data(mNNr_boot)
  
  #Fitting the model
  fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + 
                    X6 + X7 + X8 + X9, data = md_boot, 
                  family = quasibinomial(link = "logit"),
                  weights = weights)
  
  #Estimate potential outcomes for each unit
  md_boot$A <- 0
  P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"),
                      w = md_boot$weights)
  Odds0 <- P0 / (1 - P0)
  
  md_boot$A <- 1
  P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"),
                      w = md_boot$weights)
  Odds1 <- P1 / (1 - P1)

  #Return marginal odds ratio
  return(Odds1 / Odds0)
}

boot_est <- boot(d, est_fun, R = 4999)
boot_est
boot.ci(boot_est, type = "bca")

As before, to use bootstrapping for the RR or RD, the part of the code that computes the marginal OR can be replaced with code to compute the marginal RR (P1 / P0) or marginal RD (P1 - P0). The outcome model can additionally include treatment-covariate interactions if desired.

For survival outcomes

Standard error estimation for the marginal HR after matching with replacement is not a well-studied area, with Austin and Cafri (2020) providing the sole examination into appropriate methods for doing so. With survival outcomes, other matching methods may be more appropriate until matching with replacement is better understood. Here we provide an example that implements the recommendations by Austin and Cafri (2020). Any other methods (e.g., bootstrap) should be used with caution until they have been formally evaluated.

Without covariate adjustment. According to the results of Austin and Cafri’s (2020) simulation studies, when prevalence of the treatment is low (<30%), a standard error that does not involve pair membership is sufficient. When treatment prevalence is higher, the standard error that ignores pair membership may be too low, and the authors recommend a custom standard error estimator that uses information about both multiplicity and pairing.

For the continuous and binary outcomes, accounting for both multiplicity and pair membership is fairly straightforward thanks to the ability of the sandwich package functions to include multiple sources of clustering. Unfortunately, this must be done manually for survival models. We perform this analysis below, adapting code from the appendix of Austin and Cafri (2020) to the get_matches() output.

#Austin & Cafri's (2020) SE estimator
fs <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, 
      weights = weights, cluster = subclass)
Vs <- fs$var
ks <- nlevels(gm$subclass)

fi <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, 
      weights = weights, cluster = id)
Vi <- fi$var
ki <- length(unique(gm$id))

fc <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, 
      weights = weights)
Vc <- fc$var
kc <- nrow(gm)

#Compute the variance
V <- (ks/(ks-1))*Vs + (ki/(ki-1))*Vi - (kc/(kc-1))*Vc

#Sneak it back into the fit object
fc$var <- V

fc
## Call:
## coxph(formula = Surv(Y_S) ~ A, data = gm, weights = weights, 
##     robust = TRUE)
## 
##   coef exp(coef) se(coef) robust se z     p
## A 0.44      1.55     0.07      0.07 6 1e-09
## 
## Likelihood ratio test=40  on 1 df, p=2e-10
## n= 1681, number of events= 1681

The robust se column contains the computed standard error, and the reported Z-test uses this standard error. The se(coef) column should be ignored.

After Full Matching

Full matching presents fairly straightforward methods of effect and standard error estimation. The most common and recommended way to estimate effects after full matching is to use the computed matching weights to estimate weighted effects. These matching weights function essentially like inverse probability weights and can be treated as such in all analyses, including with respect to standard error estimation. For empirical comparisons between full matching and propensity score weighting, see Austin and Stuart (2015b, 2017, 2015a).

Standard error estimation involves using a cluster-robust standard error as recommended by Austin and Stuart (2017). Including covariates can improve precision and add robustness when valid. Note that the methods described here also work for other estimators that rely on matching weights, including cardinality and template matching and matching without replacement. The main difference is that full matching can be used to estimate the ATE, which we demonstrate below.

Below, we perform optimal full propensity score matching. Here we use full matching to estimate the ATE, but the procedure is nearly identical when estimating the ATT, and we point out any differences.

mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = d,
              method = "full", estimand = "ATE")

mF
## A matchit object
##  - method: Optimal full matching
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 2000 (original), 2000 (matched)
##  - target estimand: ATE
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9
md <- match.data(mF)

head(md)
##   A      X1      X2      X3       X4 X5      X6      X7      X8       X9      Y_C Y_B     Y_S distance weights subclass
## 1 0  0.1725 -1.4283 -0.4103 -2.36059  1 -1.1199  0.6398 -0.4840 -0.59385  0.07104   0  278.46  0.08461  0.8166       56
## 2 0 -1.0959  0.8463  0.2456 -0.12333  1 -2.2687 -1.4491 -0.5514 -0.31439  0.15619   0  330.63  0.01855  0.7899      140
## 3 0  0.1768  0.7905 -0.8436  0.82366  1 -0.2221  0.2971 -0.6966 -0.69516 -0.85180   1  369.94  0.22210  1.0393       96
## 4 0 -0.4595  0.1726  1.9542 -0.62661  1 -0.4019 -0.8294 -0.5384  0.20729 -2.35184   0   91.06  0.04180  0.8445      135
## 5 1  0.3563 -1.8121  0.8135 -0.67189  1 -0.8297  1.7297 -0.6439 -0.02648  0.68058   0  182.73  0.43291  0.3307      202
## 6 0 -2.4313 -1.7984 -1.2940  0.04609  1 -1.2419 -1.1252 -1.8659 -0.56513 -5.62260   0 2563.73  0.04998  0.7990      167

A benefit of full matching is that no units are discarded, which has the potential to improve precision and prevent bias due to incomplete matching. However, the “effective” sample size implied by the matching weights is lower than the actual remaining sample size, so one should not always expect full matching to yield more precise estimates than other forms of matching.

For continuous outcomes

Without covariate adjustment. Estimating effects and standard errors for continuous outcomes after full matching involves including the matching weights in the outcome model and using a cluster-robust standard error. For cardinality or template matching, a regular robust standard error can be requested using vcovHC and omitting the cluster argument in the code below.

fit1 <- lm(Y_C ~ A, data = md, weights = weights)

coeftest(fit1, vcov. = vcovCL, cluster = ~subclass)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    1.436      0.250    5.75    1e-08 ***
## A              1.892      0.519    3.65  0.00027 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Using the bootstrap. Computing bootstrap standard errors and confidence intervals after full matching is identical to doing so after pair matching without replacement, so we do not demonstrate it again here. Covariates can be included in the outcome model used in the bootstrap replications. See below for a note on centering the covariates when including treatment-covariate interactions, as this must be done when using the bootstrap as well.

With covariate adjustment. As previously mentioned, it is generally acceptable to include just the main effects, but including interactions between the treatment and covariates can be beneficial when effect modification by the covariates may be present. Because we have not demonstrated this strategy so far, we demonstrate it below.

In order to interpret the coefficient on treatment as a marginal effect estimate, we need to center the covariates at their means in the target population (i.e., the original sample for the ATE, the treated units for the ATT, or the retained units for an ATM); we could also use a marginal effects procedure as has been demonstrated with binary outcomes for other matching methods. Below we use the strategy of centering the covariates at their means. Note that when factor predictors are present, they need to be split into dummy (0/1) variables prior to centering. The splitfactor() function in cobalt can make this straightforward. Although this procedure is more involved compared to simply including main effects, it can provide extra precision and robustness.

#Estimating a covariate-adjusted marginal effect
#with treatment-covariate interactions

#Create a new dataset for centered variables
md_cen <- md

covs_to_center <- c("X1", "X2", "X3", "X4", "X5",
                    "X6", "X7", "X8", "X9")
md_cen[covs_to_center] <- scale(md_cen[covs_to_center], 
                                scale = FALSE)

#Fit the model with every covariate interacting with treatment
fit2 <- lm(Y_C ~ A * (X1 + X2 + X3 + X4 + X5 + 
                      X6 + X7 + X8 + X9),
           data = md_cen, weights = weights)

#Only output the intercept and coefficient on treatment
coeftest(fit2, vcov. = vcovCL, cluster = ~subclass)[1:2,]
##             Estimate Std. Error t value  Pr(>|t|)
## (Intercept)    1.400     0.1645   8.511 3.349e-17
## A              1.979     0.4169   4.748 2.207e-06

Remember not to interpret the coefficients on the covariates or the treatment-covariate interactions, as they are likely confounded. To keep the output clean, above we restricted the output to just the intercept and coefficient on treatment. Another benefit of centering the covariates is that we can interpret the intercept as an estimate of the average potential outcome under control.

Note that the above strategy can be applied to all matching methods when analyzing continuous outcomes (but not binary or survival outcomes, which require bootstrapping to validly estimate standard errors with covariate adjustment). It is critical to center the covariates at their means in the target group, which may require some additional programming for estimands other than the ATE.

For binary outcomes

Using full matching with binary outcomes was described by Austin and Stuart (2017). In general, the procedures look similar to how they do with other matching methods.

Without covariate adjustment. We can use a weighted generalized linear model regressing the outcome on the treatment with a link function appropriate to the effect measure of interest. Below we demonstrate estimating the marginal OR after full matching in a model without covariates:

fit3 <- glm(Y_B ~ A, data = md, weights = weights,
            family = quasibinomial(link = "logit"))

coeftest(fit3, vcov. = vcovCL, cluster = ~subclass)
## 
## z test of coefficients:
## 
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -0.9146     0.0826  -11.07   <2e-16 ***
## A             0.5545     0.1722    3.22   0.0013 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
exp(coef(fit3)) #OR
## (Intercept)           A 
##      0.4007      1.7410

As with matching with replacement, we include weights in the call to glm() and set family = quasibinomial() to prevent a warning that occurs when using weights with binomial regression models (though the results do not differ). Setting link = "logit" provides the marginal OR; for the marginal RR, we would replace "logit" with "log", and for the marginal RD, we would replace "logit" with "identity" and not exponentiate the coefficient.

With covariate adjustment and bootstrapping. To include covariates in the model, a marginal effects procedure must be used with bootstrapping to recover the marginal effect because the coefficient on treatment in such a model corresponds to a conditional effect. A bootstrap must be used to estimate the standard error and confidence interval of the marginal effect. Either the full bootstrap or block bootstrap can be used, though the performance of the latter has not been formally evaluated (but because it is an approximation to cluster-robust standard errors, which are valid, it is likely valid). The code for the full bootstrap with full matching is identical to the code for bootstrapping with binary outcomes for pair matching with replacement (except that the call to matchit() in the est_fun function must be adjusted to perform full matching), and the code for the block bootstrap with full matching is identical to the code for bootstrapping with binary outcomes for pair matching without replacement, so we do not repeat it here.

For survival outcomes

Austin and Stuart (2015b) describe the use of the full matching with survival outcomes.

Without covariate adjustment. To estimate the marginal HR, we can regress the outcome on the treatment in a Cox regression model weighted by the matching weights and including subclasses as a cluster. Below we demonstrate how to estimate the marginal HR and its standard error after full matching.

coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, 
      weights = weights, cluster = subclass)
## Call:
## coxph(formula = Surv(Y_S) ~ A, data = md, weights = weights, 
##     robust = TRUE, cluster = subclass)
## 
##   coef exp(coef) se(coef) robust se z     p
## A 0.42      1.52     0.05      0.09 5 2e-06
## 
## Likelihood ratio test=54  on 1 df, p=2e-13
## n= 2000, number of events= 2000

To perform the log-rank test or compute survival curves after full matching, functions designed for performing these tasks with inverse probability weights can be used with the matching weights; the RISCA package offers functionality for this purpose.

Including covariates in the model is less straightforward because the resulting HR estimate is conditional rather than marginal.

After Stratum Matching

Stratum matching includes exact matching, coarsened exact matching, and propensity score subclassification. There are two natural ways to estimate marginal effects after stratum matching: the first is to estimate stratum-specific treatment effects and pool them, and the second is to use the stratum weights to estimate a single marginal effect. This latter approach is also known as marginal mean weighting through stratification (MMWS), and is described in detail by Hong (2010)5. When done properly, both methods should yield similar or identical estimates of the treatment effect. MMWS is generally preferable because it is far simpler to implement and avoids issues of noncollapsibility with non-continuous outcomes. All of the methods described above for use with full matching also work with MMWS because the formation of the weights is the same; the only difference is that it is not appropriate to use cluster-robust standard errors with MMWS because of how few clusters are present.

Unless exact matching is used, estimating stratum-specific treatment effects can be fraught because balance may not be achieved within strata even if balance is achieved across strata. Stratum-specific effects should be interpreted with caution. Stratum-specific effects are conditional effects, but conditional on stratum membership, which may not always be a useful conditioning variable.

For each outcome type, we focus on estimating marginal effects using MMWS and using the strata directly. Below, we perform propensity score subclassification for the ATT using 8 subclasses.

mS <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = d,
              method = "subclass", estimand = "ATT",
              subclass = 8)

mS
## A matchit object
##  - method: Subclassification (8 subclasses)
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 2000 (original), 2000 (matched)
##  - target estimand: ATT
##  - covariates: X1, X2, X3, X4, X5, X6, X7, X8, X9
md <- match.data(mS)

head(md)
##   A      X1      X2      X3       X4 X5      X6      X7      X8       X9      Y_C Y_B     Y_S distance weights subclass
## 1 0  0.1725 -1.4283 -0.4103 -2.36059  1 -1.1199  0.6398 -0.4840 -0.59385  0.07104   0  278.46  0.08461  0.2458        1
## 2 0 -1.0959  0.8463  0.2456 -0.12333  1 -2.2687 -1.4491 -0.5514 -0.31439  0.15619   0  330.63  0.01855  0.2458        1
## 3 0  0.1768  0.7905 -0.8436  0.82366  1 -0.2221  0.2971 -0.6966 -0.69516 -0.85180   1  369.94  0.22210  1.0742        3
## 4 0 -0.4595  0.1726  1.9542 -0.62661  1 -0.4019 -0.8294 -0.5384  0.20729 -2.35184   0   91.06  0.04180  0.2458        1
## 5 1  0.3563 -1.8121  0.8135 -0.67189  1 -0.8297  1.7297 -0.6439 -0.02648  0.68058   0  182.73  0.43291  1.0000        5
## 6 0 -2.4313 -1.7984 -1.2940  0.04609  1 -1.2419 -1.1252 -1.8659 -0.56513 -5.62260   0 2563.73  0.04998  0.2458        1

For continuous outcomes

For continuous outcomes, we can use either MMWS or compute the weighted average of within-subclass effects. First we illustrate weighting.

Without covariate adjustment. With weighting, we can supply the weights that are in the match.data() output to a call to lm() to perform weighted least squares regression, as we did with full matching. We need a robust standard error estimator to account for the weights. Note that the subclasses don’t even need to enter this analysis; they are fully incorporated through the MMWS weights6. We use vcovHC() to estimate the regular robust standard error instead of the cluster-robust standard error used with other methods.

fit1 <- lm(Y_C ~ A, data = md, weights = weights)

coeftest(fit1, vcov. = vcovHC)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    1.964      0.287    6.85  9.8e-12 ***
## A              1.930      0.407    4.74  2.2e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

We can also fit a model within each subclass to estimate the within-stratum treatment effects and then compute a weighted average of them to be used as the marginal effect. The stratum weights in the weighted average must be equal to the proportion of treated units in each subclass if the ATT is targeted. If the ATE is targeted, the weights must be equal to the proportion of all units in each subclass. There are other ways to construct weight to minimize variance at the expense of losing the original target population (Rudolph et al. 2016).

Instead of fitting separate models for each subclass, we can fit a single model that fully interacts the treatment with subclass membership and then perform a linear hypothesis test. To do so, we use the form Y ~ S + S:A - 1 in the call to lm(). This includes main effects for subclass and treatment interaction terms for each subclass and omits an intercept. The fit of this model is equivalent to that of a traditional full factorial model, so no information is lost using this parameterization and using it makes it easier to construct the stratum weights. This estimates the subclass-specific intercept and subclass-specific treatment effect in each subclass. We would use a robust standard error to account for different residual variance across subclasses.

fit2 <- lm(Y_C ~ subclass + subclass:A - 1, data = md)

#Within-subclass effects
# coeftest(fit2, vcov. = vcovHC)

The within-subclass effects should only be trusted if balance is achieved in each subclass. In this example, balance has not been achieved within some subclasses, so we would not interpret these effects. Next we construct the weights to form the weighted average of the subclass effects. The weights take the form of a linear contrast matrix with zeroes for the subclass-specific intercepts and the subclass proportions for the corresponding subclass-specific treatment effect coefficients.

#Subclass weights for ATT
sub_w <- with(md, c(rep(0, nlevels(subclass)), 
                    table(subclass[A==1])/sum(A==1)))

#Subclass weights for ATE (requires estimand = "ATE" in matchit())
# sub_w <- with(md, c(rep(0, nlevels(subclass)), 
#                     table(subclass)/nrow(md)))

#Marginal effect
(est <- weighted.mean(coef(fit2), sub_w))
## [1] 1.93
#SE of marginal effect
(se <- sqrt(drop(sub_w %*% vcovHC(fit2) %*% sub_w)))
## [1] 0.4036
#CI
c(ci_low = est - 1.96*se, ci_hi = est + 1.96*se)
## ci_low  ci_hi 
##  1.139  2.721

The following lines would have produced the same output but require the margins package:

#Using margins() from margins
summary(margins::margins(fit2, variables = "A", 
                         data = md[md$A == 1,],
                         vcov = vcovHC(fit2)))
#For ATE, omit the second line. 

With covariate adjustment. To include covariates in the model when using MMWS, we can modify the code used for weighting after full matching, the only difference being that regular robust standard errors should be used with MMWS. As before, treatment-covariate interactions are optional but can reduce bias and improve precision when there effect modification by the covariates. When including these interactions in the outcome model, it is important to center the covariates at their means in the target population (i.e., the full sample for the ATE and the treated units for the ATT) in order to interpret the coefficient on treatment as a marginal effect.

To include covariates in the model when combining subclass-specific effect estimates, it can be challenging to correctly parameterize the model so that the linear contrast matrix method works as expected. The simplest way would be to include covariates as desired, which include as main effects, interactions with treatment, interactions with subclass, or all three, and use the margins() code above, which should automatically provide the correct output.

For binary outcomes

Using stratification with binary outcomes is slightly more complicated than it is with continuous outcomes. This is because the OR and RR are not collapsible, so the marginal OR and RR cannot be computed as the weighted average of the stratum-specific effects. Instead, one must compute the average of the predicted stratum-specific risks under each treatment and then compute the marginal effect estimate from these marginal risks. Although stratum-specific conditional ORs are valid effect measures, they generally do not correspond to meaningful subpopulation effects unless the strata themselves are meaningful subpopulations. After exact matching or coarsened exact matching, strata may be meaningful because they correspond to specific combinations of covariates that may come close to designating specific patient attributes, but after propensity score subclassification, the strata correspond to propensity score bins, which are generally not meaningful. Although some researchers have interpreted stratum-specific effects after propensity score subclassification as representing effects at various risks or propensities for treatment, because the primary purpose of the propensity score is as a balancing score and not as an accurate estimate of propensity for treatment, such an interpretation should be regarded with caution.

Austin (2007) compared several methods of propensity score adjustment for estimating marginal ORs, including two methods based on propensity score stratification, one of which involved the stratified Mantel-Haenszel estimator, and the other of which involved averaging stratum-specific effects. Both of these estimate a common conditional OR, not a marginal OR (Forbes and Shortreed 2008; Stampf et al. 2010), and both yielded positively biased effect estimates for non-null treatment effects, a common pattern when using conditional effect estimates as estimates of marginal effects. Given the difficulties in estimating marginal ORs after stratification, the most straightforward way to do so is to use MMWS. We do, however, also demonstrate estimating the marginal odds ratio and its standard error using bootstrapping in a way that can incorporate covariate adjustment and allow for differential effect modification across strata.

Without covariate adjustment. As before, we can supply the stratification weights to a weighted generalized linear model with just the treatment as the sole predictor, and the coefficient on treatment will correspond to a marginal treatment effect. We use vcovHC() to estimate the regular robust standard error.

fit3 <- glm(Y_B ~ A, data = md, weights = weights,
            family = quasibinomial(link = "logit"))

coeftest(fit3, vcov. = vcovHC)
## 
## z test of coefficients:
## 
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -0.831      0.108   -7.71  1.3e-14 ***
## A              0.726      0.144    5.04  4.6e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
exp(coef(fit3))
## (Intercept)           A 
##      0.4357      2.0675

As with other matching methods, we include weights in the call to glm() and set family = quasibinomial() to prevent a warning that occurs when using weights with binomial regression models (though the results do not differ). Setting link = "logit" provides the marginal OR; for the marginal RR, we would replace "logit" with "log", and for the marginal RD, we would replace "logit" with "identity" and not exponentiate the coefficient.

With covariate adjustment and bootstrapping. We can use bootstrapping to estimate the marginal OR and its standard error by estimating the average of the stratum-specific risks under each treatment level, computing the marginal risks under each treatment, and computing marginal effects from the marginal risks. This also makes it fairly straightforward to include covariates in the model. Below we illustrate bootstrapping for the marginal OR with covariates included in the outcome model.

#Bootstrap confidence intervals
library(boot)

est_fun <- function(data, i) {
  #Subclassification function
  mS_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                       X6 + X7 + X8 + X9, data = data[i,],
                     method = "subclass", estimand = "ATT",
                     subclass = 8)
  md_boot <- match.data(mS_boot)
  
  #Fitting the model
  fit_boot <- glm(Y_B ~ A * (subclass + X1 + X2 + X3 + X4 + X5 + 
                    X6 + X7 + X8 + X9), data = md_boot,
                  family = quasibinomial(link = "logit"))
  
  #Estimate potential outcomes for each unit
  
  ## Subset to just the treated for the ATT; remove this for the ATE
  md_boot <- md_boot[md_boot$A == 1,]
  ##
  
  md_boot$A <- 0
  P0 <- mean(predict(fit_boot, md_boot, type = "response"))
  Odds0 <- P0 / (1 - P0)
  
  md_boot$A <- 1
  P1 <- mean(predict(fit_boot, md_boot, type = "response"))
  Odds1 <- P1 / (1 - P1)

  #Return marginal odds ratio
  return(Odds1 / Odds0)
}

boot_est <- boot(d, est_fun, R = 4999)
boot_est
boot.ci(boot_est, type = "bca")

In this example, we included interactions between treatment and subclass and between treatment and each covariate. Note that because we are interested in the ATT, we restricted the sample used to compute the predicted marginal risks (P0) and (P1) to just those with A = 1. If we were instead estimating the ATE, we would supply "ATE" that to the estimand argument in the call to matchit() and skip the step of restricting the data used for prediction of the marginal risks.

As with other methods, to estimate the marginal RR or RD using the above code, the returned object can instead be specified as P1 / P0 or P1 - P0, respectively.

For survival outcomes

Like ORs, HRs are not collapsible, so it is not straightforward to estimate marginal HRs using within-stratum HRs. Austin (2013a) examined the performance of several propensity score subclassification-based estimators of the marginal HR and found all to be positively biased for non-null effects, consistent with the use of conditional effect estimates as estimates of marginal effects; indeed, the subclassification methods examined all relied on pooling stratum-specific effects. Given these difficulties, the most straightforward method to estimate marginal HRs is to use MMWS weights. We demonstrate this below using essentially the same syntax as used with full matching, only omitting subclass membership as a clustering variable.

coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, 
      weights = weights)
## Call:
## coxph(formula = Surv(Y_S) ~ A, data = md, weights = weights, 
##     robust = TRUE)
## 
##   coef exp(coef) se(coef) robust se z     p
## A 0.46      1.58     0.05      0.07 7 2e-11
## 
## Likelihood ratio test=64  on 1 df, p=1e-15
## n= 2000, number of events= 2000

The robust standard error must be used because of the MMWS weights.

Estimating Conditional Effects

As discussed previously, with binary and time-to-event outcomes, marginal and conditional effects generally differ. Though we have primarily focused on marginal effects, we offer some guidance here on estimating conditional effect as well. Estimating conditional effects typically involves modeling the outcome, and inferences are dependent on this model being correct, even in the presence is perfect covariate balance. Matching provides robustness to some forms of misspecification, however, by limiting the range of the covariate space. So, even though outcome modeling is required, matching prior to modeling the outcome can be beneficial in terms of reducing bias due to model misspecification.

On some effect measures, conditional effects typically vary depending on the covariate values at which the conditional effect is to be estimated; for example, the decrease in the risk of death (i.e., on the RD scale) corresponding to receipt of a treatment for a patient with a high baseline risk of death will by higher than that for a patient with a low baseline risk of death. In this sense, there is necessarily effect modification of the RD by baseline risk (which depends on a patient’s covariate values). Similarly, for an exposure that increases the risk by a factor (e.g., doubles the risk, for a RR of 2), a patient with a high baseline risk cannot experience the same change in risk as a patient with a low baseline risk could; for example, a RR of 2 is impossible for a patient with a baseline risk of .6 but is certainly plausible for a patient with a baseline risk of .02. In this sense, there is necessarily effect modification of the RR by baseline risk. This is not true for the OR; it is plausible that the effect on the OR scale could be consistent across levels of levels of the predictors because any OR is compatible with possible baselines risks. For this reason, we only consider estimating the conditional OR and not other effect measures.

Although several methods exist for estimating conditional ORs after matching or subclassification, the one that generally works well is to run a logistic regression of the outcome on the treatment and covariates and use the coefficient on treatment as an estimate of the effect on the log OR scale. We demonstrate this below using full matching for the ATT:

mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = d,
              method = "full", estimand = "ATT")

md <- match.data(mF)

fitcond <- lm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + 
                 X6 + X7 + X8 + X9, data = md, 
             weights = weights)

coeftest(fitcond, vcov. = vcovCL, cluster = ~subclass)["A",,drop = FALSE]
##   Estimate Std. Error t value Pr(>|t|)
## A   0.1219    0.04171   2.922 0.003516
exp(coef(fitcond)["A"])
##    A 
## 1.13

As with other covariate-adjusted models, it is important not to interpret the coefficients on covariates other than treatment to avoid committing the table 2 fallacy. Other causes of the outcome can be included in the outcome model even if they were not the focus of matching as long as they are not (even possibly) caused by the treatment.

Alternative methods of estimating conditional effects include conditional logistic regression after matching and estimating stratum-specific effects after subclassification. We recommend using covariate-adjusted logistic regression models instead because the conditional effect is better defined (i.e., conditional on the specific covariates included in the model) and depends less on the estimand targeted (Forbes and Shortreed 2008).

Moderation Analysis

Moderation analysis involves determining whether a treatment effect differs across levels of another variable. The use of matching with moderation analysis is described in Green and Stuart (2014). The goal is to achieve balance within each subgroup of the potential moderating variable, and there are several ways of doing so. Broadly, one can either perform matching in the full dataset, perhaps requiring exact matching on the moderator, or one can perform completely separate analyses in each subgroup. We’ll demonstrate both approaches below. The chosen approach should be that which achieves the best balance, though we don’t demonstrate assessing balance here to maintain focus on effect estimation. We’ll consider the binary variable X5 to be the potential moderator of the effect of A on Y_C.

The first approach involves pooling information across subgroups. This could involve estimating propensity scores using a single model for both groups but exact matching on the potential moderator. The propensity score model could include moderator-by-covariate interactions to allow the propensity score model to vary across subgroups on some covariates. Below, we’ll estimate a propensity score using a single propensity score model with a few moderator-by-covariate interactions and exact matching on the moderator, X5. We’ll perform nearest neighbor matching on the propensity score.

mP <- matchit(A ~ X1 + X2 + X5*X3 + X4 + 
                X5*X6 + X7 + X5*X8 + X9, data = d,
              exact = ~X5, method = "nearest")
mP
## A matchit object
##  - method: 1:1 nearest neighbor matching without replacement
##  - distance: Propensity score
##              - estimated with logistic regression
##  - number of obs.: 2000 (original), 882 (matched)
##  - target estimand: ATT
##  - covariates: X1, X2, X5, X3, X4, X6, X7, X8, X9

Although it is straightforward to assess balance overall using summary(), it is more challenging to assess balance within subgroups. The easiest way to check subgroup balance would be to use cobalt::bal.tab(), which has a cluster argument that can be used to assess balance within subgroups, e.g., by cobalt::bal.tab(mP, cluster = "X5"). See the vignette “Appendix 2: Using cobalt with Clustered, Multiply Imputed, and Other Segmented Data” on the cobalt website for details.

If we are satisfied with balance, we can then estimate the subgroup effects using an outcome model with an interaction between the treatment and the moderator.

mdP <- match.data(mP)

fitP <- lm(Y_C ~ A * X5, data = mdP, weights = weights)

coeftest(fitP, vcov. = vcovCL, cluster = ~subclass)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)   
## (Intercept)   0.7500     0.4515    1.66   0.0970 . 
## A             2.2073     0.6703    3.29   0.0010 **
## X5            1.7413     0.6070    2.87   0.0042 **
## A:X5         -0.0275     0.8791   -0.03   0.9751   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

A second approach is to perform the matching analyses separately and then combine the matched samples. We demonstrate this below. First, we split the data by levels of X5.

d_X5_0 <- subset(d, X5 == 0)
d_X5_1 <- subset(d, X5 == 1)

Next we perform separate matching analyses in each new dataset,

mS0 <- matchit(A ~ X1 + X2 + X3 + X4 + 
                 X6 + X7 + X8 + X9, data = d_X5_0,
               method = "nearest")

mS1 <- matchit(A ~ X1 + X2 + X3 + X4 + 
                 X6 + X7 + X8 + X9, data = d_X5_1,
               method = "nearest")

It is straightforward to assess balance within each subgroup using summary(), and cobalt functions can be used as well. We omit this step here, but you should not!

To estimate the subgroup effects, we need to combine the matched datasets, which we can do using rbind.matchdata() (a special rbind() method for matchdata objects). Then we estimate the moderation effect just as we did previously.

mdS0 <- match.data(mS0)
mdS1 <- match.data(mS1)

mdS <- rbind(mdS0, mdS1)

fitS <- lm(Y_C ~ A * X5, data = mdS, weights = weights)

coeftest(fitS, vcov. = vcovCL, cluster = ~subclass)
## 
## t test of coefficients:
## 
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    0.720      0.421    1.71  0.08766 .  
## A              2.237      0.641    3.49  0.00051 ***
## X5             2.160      0.576    3.75  0.00019 ***
## A:X5          -0.446      0.842   -0.53  0.59670    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

There are benefits to using either approach, and Green and Stuart (2014) find that either can be successful at balancing the subgroups. The first approach may be most effective with small samples, where separate propensity score models would be fit with greater uncertainty and an increased possibility of perfect prediction or failure to converge (Wang et al. 2018). The second approach may be more effective with larger samples or with matching methods that target balance in the matched sample, such as genetic matching (Kreif et al. 2012). With genetic matching, separate subgroup analyses ensure balance is optimized within each subgroup rather than just overall.

Reporting Results

It is important to be as thorough and complete as possible when describing the methods of estimating the treatment effect and the results of the analysis. This improves transparency and replicability of the analysis. Results should at least include the following:

  • a description of the outcome model used (e.g., logistic regression, a linear model with treatment-covariate interactions and mean-centered covariates, a Cox proportional hazards model with the matching weights applied)
  • the way the effect was estimated (e.g., as the coefficient on treatment in the outcome model, as the result of a marginal effects procedure)
  • the way standard errors and confidence intervals were estimated (e.g., using robust standard errors, using cluster-robust standard errors with pair membership as the cluster, using the BCa bootstrap with 4999 bootstrap replications and the entire process of matching and effect estimation included in each replication)
  • R packages and functions used in estimating the effect and its standard error (e.g., glm() in base R, vcovCL() in sandwich, boot() and boot.ci() in boot)
  • The effect and its standard error and confidence interval

All this is in addition to information about the matching method, propensity score estimation procedure (if used), balance assessment, etc. mentioned in the other vignettes.

Common Mistakes

There are a few common mistakes that should be avoided. It is important not only to avoid these mistakes in one’s own research but also to be able to spot these mistakes in others’ analyses.

1. Failing to include weights

Several methods involve weights that are to be used in estimating the treatment effect. With full matching and stratification matching (when analyzed using MMWS), the weights do the entire work of balancing the covariates across the treatment groups. Omitting weights essentially ignores the entire purpose of matching. Some cases are less obvious. When performing matching with replacement and estimating the treatment effect using the match.data() output, weights must be included to ensure control units matched to multiple treated units are weighted accordingly. Similarly, when performing k:1 matching where not all treated units receive k matches, weights are required to account for the differential weight of the matched control units. The only time weights can be omitted after pair matching is when performing 1:1 matching without replacement. Including weights even in this scenario will not affect the analysis and it can be good practice to always include weights to prevent this error from occurring. There are some scenarios where weights are not useful because the conditioning occurs through some other means, such as when using the pooling strategy rather than MMWS for estimating marginal effects after stratification.

2. Failing to use robust or cluster-robust standard errors

Robust standard errors are required when using weights to estimate the treatment effect. The model-based standard errors resulting from weighted least squares or maximum likelihood are inaccurate when using matching weights because they assume weights are frequency weights rather than probability weights. Cluster-robust standard errors account for both the matching weights and pair membership and should be used when appropriate (i.e., with all matching methods other than stratification matching). Sometimes, researchers use functions in the survey package to estimate robust standard errors, especially with inverse probability weighting; this is a valid way to compute robust standard errors and will give similar results to sandwich::vcovHC().

3. Interpreting conditional effects as marginal effects

The distinction between marginal and conditional effects is not always clear both in methodological and applied papers. Some statistical methods are valid only for estimating conditional effects and they should not be used to estimate marginal effects (without further modification). Sometimes conditional effects are desirable, and such methods may be useful for them, but when marginal effects are the target of inference, it is critical not to inappropriately interpret estimates resulting from statistical methods aimed at estimating conditional effects as marginal effects. Although this issue is particularly salient with binary and survival outcomes due to the general noncollapsibility of the OR, RR, and HR, this can also occur with linear models for continuous outcomes or the RD.

The following methods estimate conditional effects for binary or survival outcomes (with noncollapsible effect measures) and should not be used to estimate marginal effects:

  • Logistic regression or Cox proportional hazards model with covariates and/or the propensity score included, using the coefficient on treatment as the effect estimate
  • Conditional logistic regression after matching
  • Stratified Cox regression after matching
  • Averaging stratum-specific effect estimates after stratification, including using Mantel-Haenszel OR pooling
  • Including pair or stratum fixed or random effects in a logistic regression model, using the coefficient on treatment as the effect estimate

In addition, with continuous outcomes, conditional effects can be mistakenly interpreted as marginal effect estimates when treatment-covariate interactions are present in the outcome model. If the covariates are not centered at their mean in the target population (e.g., the treated group for the ATT, the full sample for the ATE, or the remaining matched sample for an ATM), the coefficient on treatment will not correspond to the marginal effect in the target population; it will correspond to the effect of treatment when the covariate values are equal to zero, which may not be meaningful or plausible. Marginal effects procedures (e.g., using the margins package or manually with bootstrapping as demonstrated above) are always the safest way to include covariates in the outcome model, especially in the presence of treatment-covariate interactions. Appropriately centering the covariates is a shortcut that is required when using the coefficient on treatment as a marginal effect estimate for continuous outcomes (demonstrated previously for full matching).

References

Abadie, Alberto, and Guido W. Imbens. 2008. “On the Failure of the Bootstrap for Matching Estimators.” Econometrica 76 (6): 1537–57. https://www.jstor.org/stable/40056514.
Abadie, Alberto, and Jann Spiess. 2019. “Robust Post-Matching Inference,” January, 34.
Austin, Peter C. 2007. “The Performance of Different Propensity Score Methods for Estimating Marginal Odds Ratios.” Statistics in Medicine 26 (16): 3078–94. https://doi.org/10.1002/sim.2781.
———. 2009. “Type i Error Rates, Coverage of Confidence Intervals, and Variance Estimation in Propensity-Score Matched Analyses.” The International Journal of Biostatistics 5 (1). https://doi.org/10.2202/1557-4679.1146.
———. 2013a. “The Performance of Different Propensity Score Methods for Estimating Marginal Hazard Ratios.” Statistics in Medicine 32 (16): 2837–49. https://doi.org/10.1002/sim.5705.
———. 2013b. “The Use of Propensity Score Methods with Survival or Time-to-Event Outcomes: Reporting Measures of Effect Similar to Those Used in Randomized Experiments.” Statistics in Medicine 33 (7): 1242–58. https://doi.org/10.1002/sim.5984.
Austin, Peter C., and Guy Cafri. 2020. “Variance Estimation When Using Propensity-Score Matching with Replacement with Survival or Time-to-Event Outcomes.” Statistics in Medicine 39 (11): 1623–40. https://doi.org/10.1002/sim.8502.
Austin, Peter C., and Dylan S. Small. 2014. “The Use of Bootstrapping When Using Propensity-Score Matching Without Replacement: A Simulation Study.” Statistics in Medicine 33 (24): 4306–19. https://doi.org/10.1002/sim.6276.
Austin, Peter C., and Elizabeth A. Stuart. 2015a. “The Performance of Inverse Probability of Treatment Weighting and Full Matching on the Propensity Score in the Presence of Model Misspecification When Estimating the Effect of Treatment on Survival Outcomes.” Statistical Methods in Medical Research 26 (4): 1654–70. https://doi.org/10.1177/0962280215584401.
———. 2015b. “Optimal Full Matching for Survival Outcomes: A Method That Merits More Widespread Use.” Statistics in Medicine 34 (30): 3949–67. https://doi.org/10.1002/sim.6602.
———. 2017. “Estimating the Effect of Treatment on Binary Outcomes Using Full Matching on the Propensity Score.” Statistical Methods in Medical Research 26 (6): 2505–25. https://doi.org/10.1177/0962280215601134.
Austin, Peter C., Neal Thomas, and Donald B. Rubin. 2020. “Covariate-Adjusted Survival Analyses in Propensity-Score Matched Samples: Imputing Potential Time-to-Event Outcomes.” Statistical Methods in Medical Research 29 (3): 728–51. https://doi.org/10.1177/0962280218817926.
Bodory, Hugo, Lorenzo Camponovo, Martin Huber, and Michael Lechner. 2020. “The Finite Sample Performance of Inference Methods for Propensity Score Matching and Weighting Estimators.” Journal of Business & Economic Statistics 38 (1): 183–200. https://doi.org/10.1080/07350015.2018.1476247.
Cameron, A. Colin, and Douglas L. Miller. 2015. “A Practitioners Guide to Cluster-Robust Inference.” Journal of Human Resources 50 (2): 317–72. https://doi.org/10.3368/jhr.50.2.317.
Carpenter, James, and John Bithell. 2000. “Bootstrap Confidence Intervals: When, Which, What? A Practical Guide for Medical Statisticians.” Statistics in Medicine 19 (9): 1141–64. https://doi.org/10.1002/(SICI)1097-0258(20000515)19:9<1141::AID-SIM479>3.0.CO;2-F.
Efron, Bradley, and Robert J. Tibshirani. 1993. An Introduction to the Bootstrap. Springer US.
Forbes, Andrew, and Susan Shortreed. 2008. “Inverse Probability Weighted Estimation of the Marginal Odds Ratio: Correspondence Regarding The Performance of Different Propensity Score Methods for Estimating Marginal Odds Ratios by P. Austin, Statictics in Medicine, 2007; 26:30783094.” Statistics in Medicine 27 (26): 5556–59. https://doi.org/10.1002/sim.3362.
Gayat, Etienne, Matthieu Resche-Rigon, Jean-Yves Mary, and Raphaël Porcher. 2012. “Propensity Score Applied to Survival Data Analysis Through Proportional Hazards Models: A Monte Carlo Study.” Pharmaceutical Statistics 11 (3): 222–29. https://doi.org/10.1002/pst.537.
Green, Kerry M., and Elizabeth A. Stuart. 2014. “Examining Moderation Analyses in Propensity Score Methods: Application to Depression and Substance Use.” Journal of Consulting and Clinical Psychology, Advances in Data Analytic Methods, 82 (5): 773–83. https://doi.org/10.1037/a0036515.
Hill, Jennifer, and Jerome P. Reiter. 2006. “Interval Estimation for Treatment Effects Using Propensity Score Matching.” Statistics in Medicine 25 (13): 2230–56. https://doi.org/10.1002/sim.2277.
Ho, Daniel E., Kosuke Imai, Gary King, and Elizabeth A. Stuart. 2007. “Matching as Nonparametric Preprocessing for Reducing Model Dependence in Parametric Causal Inference.” Political Analysis 15 (3): 199–236. https://doi.org/10.1093/pan/mpl013.
Hong, Guanglei. 2010. “Marginal Mean Weighting Through Stratification: Adjustment for Selection Bias in Multilevel Data.” Journal of Educational and Behavioral Statistics 35 (5): 499–531. https://doi.org/10.3102/1076998609359785.
King, Gary, and Margaret E. Roberts. 2015. “How Robust Standard Errors Expose Methodological Problems They Do Not Fix, and What to Do About It.” Political Analysis 23 (2): 159–79. https://doi.org/10.1093/pan/mpu015.
Kreif, Noemi, Richard Grieve, Rosalba Radice, Zia Sadique, Roland Ramsahai, and Jasjeet S. Sekhon. 2012. “Methods for Estimating Subgroup Effects in Cost-Effectiveness Analyses That Use Observational Data.” Medical Decision Making 32 (6): 750–63. https://doi.org/10.1177/0272989X12448929.
Liang, Kung-Yee, and Scott L. Zeger. 1986. “Longitudinal Data Analysis Using Generalized Linear Models.” Biometrika 73 (1): 13–22. https://doi.org/10.1093/biomet/73.1.13.
MacKinnon, James G. 2006. “Bootstrap Methods in Econometrics*.” Economic Record 82 (s1): S2–18. https://doi.org/10.1111/j.1475-4932.2006.00328.x.
MacKinnon, James G., and Halbert White. 1985. “Some Heteroskedasticity-Consistent Covariance Matrix Estimators with Improved Finite Sample Properties.” Journal of Econometrics 29 (3): 305–25. https://doi.org/10.1016/0304-4076(85)90158-7.
Nguyen, Tri-Long, Gary S. Collins, Jessica Spence, Jean-Pierre Daurès, P. J. Devereaux, Paul Landais, and Yannick Le Manach. 2017. “Double-Adjustment in Propensity Score Matching Analysis: Choosing a Threshold for Considering Residual Imbalance.” BMC Medical Research Methodology 17: 78. https://doi.org/10.1186/s12874-017-0338-0.
Rudolph, Kara E., K. Ellicott Colson, Elizabeth A. Stuart, and Jennifer Ahern. 2016. “Optimally Combining Propensity Score Subclasses.” Statistics in Medicine 35 (27): 4937–47. https://doi.org/10.1002/sim.7046.
Schafer, Joseph L., and Joseph Kang. 2008. “Average Causal Effects from Nonrandomized Studies: A Practical Guide and Simulated Example.” Psychological Methods 13 (4): 279–313. https://doi.org/10.1037/a0014268.
Snowden, Jonathan M., Sherri Rose, and Kathleen M. Mortimer. 2011. “Implementation of G-Computation on a Simulated Data Set: Demonstration of a Causal Inference Technique.” American Journal of Epidemiology 173 (7): 731–38. https://doi.org/10.1093/aje/kwq472.
Stampf, Susanne, Erika Graf, Claudia Schmoor, and Martin Schumacher. 2010. “Estimators and Confidence Intervals for the Marginal Odds Ratio Using Logistic Regression and Propensity Score Stratification.” Statistics in Medicine 29 (7-8): 760–69. https://doi.org/10.1002/sim.3811.
Wan, Fei. 2019. “Matched or Unmatched Analyses with Propensity-Scorematched Data?” Statistics in Medicine 38 (2): 289–300. https://doi.org/10.1002/sim.7976.
Wang, Shirley V., Yinzhu Jin, Bruce Fireman, Susan Gruber, Mengdong He, Richard Wyss, HoJin Shin, et al. 2018. “Relative Performance of Propensity Score Matching Strategies for Subgroup Analyses.” American Journal of Epidemiology 187 (8): 1799–1807. https://doi.org/10.1093/aje/kwy049.
Westreich, D., and S. Greenland. 2013. “The Table 2 Fallacy: Presenting and Interpreting Confounder and Modifier Coefficients.” American Journal of Epidemiology 177 (4): 292–98. https://doi.org/10.1093/aje/kws412.

Code to Generate Data used in Examples

#Generating data similar to Austin (2009) for demonstrating treatment effect estimation
gen_X <- function(n) {
  X <- matrix(rnorm(9 * n), nrow = n, ncol = 9)
  X[,5] <- as.numeric(X[,5] < .5)
  X
}

#~20% treated
gen_A <- function(X) {
  LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8]
  P_A <- plogis(LP_A)
  rbinom(nrow(X), 1, P_A)
}

# Continuous outcome
gen_Y_C <- function(A, X) {
  2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5)
}
#Conditional:
#  MD: 2
#Marginal:
#  MD: 2

# Binary outcome
gen_Y_B <- function(A, X) {
  LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6]
  P_B <- plogis(LP_B)
  rbinom(length(A), 1, P_B)
}
#Conditional:
#  OR:   2.4
#  logOR: .875
#Marginal:
#  RD:    .144
#  RR:   1.54
#  logRR: .433
#  OR:   1.92
#  logOR  .655

# Survival outcome
gen_Y_S <- function(A, X) {
  LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6]
  sqrt(-log(runif(length(A)))*2e4*exp(-LP_S))
}
#Conditional:
#  HR:   2.4
#  logHR: .875
#Marginal:
#  HR:   1.57
#  logHR: .452

set.seed(19599)

n <- 2000
X <- gen_X(n)
A <- gen_A(X)

Y_C <- gen_Y_C(A, X)
Y_B <- gen_Y_B(A, X)
Y_S <- gen_Y_S(A, X)

d <- data.frame(A, X, Y_C, Y_B, Y_S)

  1. Because they are only appropriate with a large number of clusters, cluster-robust standard errors are generally not used with subclassification methods. Regular robust standard errors are valid with these methods when using the subclassification weights to estimate marginal effects.↩︎

  2. Sometimes, an error will occur with this method, which usually means more bootstrap replications are required. The number of replicates must be greater than the original sample size when using the full bootstrap and greater than the number of pairs/strata when using the block bootstrap.↩︎

  3. The matching weights are not necessary when performing 1:1 matching, but we include them here for generality. When weights are not necessary, including them does not affect the estimates. Because it may not always be clear when weights are required, we recommend always including them.↩︎

  4. It is possible to exactly reproduce the match.data() standard error using the get_matches() data, but doing so may require some fiddling due to the defaults in sandwich.↩︎

  5. It is also known as fine stratification weighting, described by Desai et al. [-@desai2017].↩︎

  6. Including subclass as a main effect in the MMWS-weighted regression will not change the effect estimate but may slightly decrease its estimated standard error.↩︎

MatchIt/inst/doc/assessing-balance.R0000644000176200001440000001376614170752541017055 0ustar liggesusers## ----setup, include=FALSE----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- knitr::opts_chunk$set(echo = TRUE, message = FALSE, fig.width=7, fig.height=5) options(width = 200, digits = 4) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- library("MatchIt") data("lalonde", package = "MatchIt") #Full matching on a logistic regression PS m.out <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "full") m.out ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + I(re75==0) + educ:race) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- m.sum <- summary(m.out, addlvariables = ~ I(age^2) + I(re74==0) + I(re75==0) + educ:race) plot(m.sum, var.order = "unmatched") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #eQQ plot plot(m.out, type = "qq", which.xs = c("age", "nodegree", "re74")) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #eCDF plot plot(m.out, type = "ecdf", which.xs = c("educ", "married", "re75")) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #density plot plot(m.out, type = "density", which.xs = c("age", "educ", "married")) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Subclassification on a logistic regression PS s.out <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "subclass", subclass = 4) s.out ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- summary(s.out) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- summary(s.out, subclass = TRUE, un = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- s <- summary(s.out, subclass = TRUE) plot(s, var.order = "unmatched", abs = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- plot(s.out, type = "density", which.xs = c("educ", "married", "re75"), subclass = 1) ## ---- message = F------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- library("cobalt") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bal.tab(m.out, un = TRUE, stats = c("m", "v", "ks")) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Nearest neighbor (NN) matching on the PS m.out2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde) #Balance on covariates after full and NN matching bal.tab(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, un = TRUE, weights = list(full = m.out, nn = m.out2)) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- love.plot(m.out, binary = "std") ## ---- fig.width=7------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- love.plot(m.out, stats = c("m", "ks"), poly = 2, abs = TRUE, weights = list(nn = m.out2), drop.distance = TRUE, thresholds = c(m = .1), var.order = "unadjusted", binary = "std", shapes = c("triangle", "square", "circle"), colors = c("blue", "darkgreen", "red"), sample.names = c("Full Matching", "NN Matching", "Original"), position = "bottom") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Density plot for continuous variables bal.plot(m.out, var.name = "educ", which = "both") #Bar graph for categorical variables bal.plot(m.out, var.name = "race", which = "both") #Mirrored histogram bal.plot(m.out, var.name = "distance", which = "both", type = "histogram", mirror = TRUE) MatchIt/inst/doc/estimating-effects.R0000644000176200001440000006003114170752607017242 0ustar liggesusers## ---- include = FALSE--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- knitr::opts_chunk$set(echo = TRUE, eval=T) options(width = 200, digits= 4) #Generating data similar to Austin (2009) for demonstrating treatment effect estimation gen_X <- function(n) { X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) X[,5] <- as.numeric(X[,5] < .5) X } #~20% treated gen_A <- function(X) { LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] P_A <- plogis(LP_A) rbinom(nrow(X), 1, P_A) } # Continuous outcome gen_Y_C <- function(A, X) { 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) } #Conditional: # MD: 2 #Marginal: # MD: 2 # Binary outcome gen_Y_B <- function(A, X) { LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] P_B <- plogis(LP_B) rbinom(length(A), 1, P_B) } #Conditional: # OR: 2.4 # logOR: .875 #Marginal: # RD: .144 # RR: 1.54 # logRR: .433 # OR: 1.92 # logOR .655 # Survival outcome gen_Y_S <- function(A, X) { LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] sqrt(-log(runif(length(A)))*2e4*exp(-LP_S)) } #Conditional: # HR: 2.4 # logHR: .875 #Marginal: # HR: 1.57 # logHR: .452 set.seed(19599) n <- 2000 X <- gen_X(n) A <- gen_A(X) Y_C <- gen_Y_C(A, X) Y_B <- gen_Y_B(A, X) Y_S <- gen_Y_S(A, X) d <- data.frame(A, X, Y_C, Y_B, Y_S) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- head(d) ## ----message=FALSE,warning=FALSE---------------------------------------------------------------------------------------------------------------------------------------------------------------------- library("MatchIt") library("lmtest") library("sandwich") library("boot") library("survival") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mNN <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d) mNN md <- match.data(mNN) head(md) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Linear model without covariates fit1 <- lm(Y_C ~ A, data = md, weights = weights) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Cluster-robust standard errors coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Effect estimation fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights) #Return the coefficient on treatment return(coef(fit_boot)["A"]) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Linear model with covariates fit3 <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md, weights = weights) coeftest(fit3, vcov. = vcovCL, cluster = ~subclass)["A",,drop=FALSE] ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Generalized linear model without covariates fit4 <- glm(Y_B ~ A, data = md, weights = weights, family = binomial(link = "logit")) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Cluster-robust standard errors coeftest(fit4, vcov. = vcovCL, cluster = ~subclass) exp(coef(fit4)) #OR ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Block bootstrap confidence interval # library(boot) pair_ids <- levels(md$subclass) est_fun <- function(pairs, i) { #Compute number of times each pair is present numreps <- table(pairs[i]) #For each pair p, copy corresponding md row indices numreps[p] times ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], function(p) rep(which(md$subclass == p), numreps[p]))) #Subset md with block bootstrapped ids md_boot <- md[ids,] #Fitting outcome the model fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md_boot, family = binomial(link = "logit"), weights = weights) #Estimate potential outcomes for each unit #Under control md_boot$A <- 0 P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds0 <- P0 / (1 - P0) #Under treatment md_boot$A <- 1 P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), w = md_boot$weights) Odds1 <- P1 / (1 - P1) #Return marginal odds ratio return(Odds1 / Odds0) } boot_est <- boot(pair_ids, est_fun, R = 499) boot_est boot.ci(boot_est, type = "bca") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Cox Regression for marginal HR coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights, cluster = subclass) ## ---- eval = FALSE------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # #Block bootstrap confidence interval # # library(boot) # # pair_ids <- levels(md$subclass) # # est_fun <- function(pairs, i) { # # #Compute number of times each pair is present # numreps <- table(pairs[i]) # # #For each pair p, copy corresponding md row indices numreps[p] times # ids <- unlist(lapply(pair_ids[pair_ids %in% names(numreps)], # function(p) rep(which(md$subclass == p), # numreps[p]))) # # #Subset md with block bootstrapped ids # md_boot <- md[ids,] # # #Effect estimation # cox_fit_boot <- coxph(Surv(Y_S) ~ A, data = md_boot, weights = weights) # # #Compute the marginal HR by exponentiating the coefficient # #on treatment # HR <- exp(coef(cox_fit_boot)["A"]) # # #Return the HR # return(HR) # } # # boot_est <- boot(pair_ids, est_fun, R = 499) # boot_est # boot.ci(boot_est, type = "bca") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mNNr <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) mNNr #match.data output md <- match.data(mNNr) nrow(md) head(md) #get_matches output gm <- get_matches(mNNr) nrow(gm) head(gm) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Number of time control units are rematched table(table(gm$id[gm$A == 0])) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Number of control units in each match stratum table(table(gm$subclass[gm$A == 0])) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #match.data() output fit1md <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1md, vcov. = vcovHC) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #get_matches() output fit1gm <- lm(Y_C ~ A, data = gm, weights = weights) coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Full bootstrap confidence interval # library(boot) est_fun <- function(data, i) { #Matching function mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = data[i,], link = "linear.logit", caliper = .1, ratio = 3, replace = TRUE) md_boot <- match.data(mNNr_boot) #Effect estimation fit_boot <- lm(Y_C ~ A, data = md_boot, weights = weights) #Return the coefficient on treatment return(coef(fit_boot)["A"]) } boot_est <- boot(d, est_fun, R = 499) boot_est boot.ci(boot_est, type = "perc") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit2md <- lm(Y_C ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = gm, weights = weights) coeftest(fit1gm, vcov. = vcovCL, cluster = ~subclass + id)["A",,drop = FALSE] ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit3gm <- glm(Y_B ~ A, data = gm, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3gm, vcov. = vcovCL, cluster = ~ subclass + id) exp(coef(fit3gm)) #OR ## ---- eval = FALSE------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # #Bootstrap confidence intervals # # library(boot) # # est_fun <- function(data, i) { # #Matching function # mNNr_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + # X6 + X7 + X8 + X9, data = data[i,], # link = "linear.logit", caliper = .1, # ratio = 3, replace = TRUE) # md_boot <- match.data(mNNr_boot) # # #Fitting the model # fit_boot <- glm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + # X6 + X7 + X8 + X9, data = md_boot, # family = quasibinomial(link = "logit"), # weights = weights) # # #Estimate potential outcomes for each unit # md_boot$A <- 0 # P0 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), # w = md_boot$weights) # Odds0 <- P0 / (1 - P0) # # md_boot$A <- 1 # P1 <- weighted.mean(predict(fit_boot, md_boot, type = "response"), # w = md_boot$weights) # Odds1 <- P1 / (1 - P1) # # #Return marginal odds ratio # return(Odds1 / Odds0) # } # # boot_est <- boot(d, est_fun, R = 4999) # boot_est # boot.ci(boot_est, type = "bca") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Austin & Cafri's (2020) SE estimator fs <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights, cluster = subclass) Vs <- fs$var ks <- nlevels(gm$subclass) fi <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights, cluster = id) Vi <- fi$var ki <- length(unique(gm$id)) fc <- coxph(Surv(Y_S) ~ A, data = gm, robust = TRUE, weights = weights) Vc <- fc$var kc <- nrow(gm) #Compute the variance V <- (ks/(ks-1))*Vs + (ki/(ki-1))*Vi - (kc/(kc-1))*Vc #Sneak it back into the fit object fc$var <- V fc ## ---- include=FALSE----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #In case optmatch goes offline, don't run lines below if (!requireNamespace("optmatch", quietly = TRUE)) knitr::opts_chunk$set(eval = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", estimand = "ATE") mF md <- match.data(mF) head(md) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit1 <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Estimating a covariate-adjusted marginal effect #with treatment-covariate interactions #Create a new dataset for centered variables md_cen <- md covs_to_center <- c("X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9") md_cen[covs_to_center] <- scale(md_cen[covs_to_center], scale = FALSE) #Fit the model with every covariate interacting with treatment fit2 <- lm(Y_C ~ A * (X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9), data = md_cen, weights = weights) #Only output the intercept and coefficient on treatment coeftest(fit2, vcov. = vcovCL, cluster = ~subclass)[1:2,] ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit3 <- glm(Y_B ~ A, data = md, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3, vcov. = vcovCL, cluster = ~subclass) exp(coef(fit3)) #OR ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights, cluster = subclass) ## ---- include=FALSE, eval=TRUE------------------------------------------------------------------------------------------------------------------------------------------------------------------------ knitr::opts_chunk$set(eval = TRUE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mS <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "subclass", estimand = "ATT", subclass = 8) mS md <- match.data(mS) head(md) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit1 <- lm(Y_C ~ A, data = md, weights = weights) coeftest(fit1, vcov. = vcovHC) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit2 <- lm(Y_C ~ subclass + subclass:A - 1, data = md) #Within-subclass effects # coeftest(fit2, vcov. = vcovHC) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Subclass weights for ATT sub_w <- with(md, c(rep(0, nlevels(subclass)), table(subclass[A==1])/sum(A==1))) #Subclass weights for ATE (requires estimand = "ATE" in matchit()) # sub_w <- with(md, c(rep(0, nlevels(subclass)), # table(subclass)/nrow(md))) #Marginal effect (est <- weighted.mean(coef(fit2), sub_w)) #SE of marginal effect (se <- sqrt(drop(sub_w %*% vcovHC(fit2) %*% sub_w))) #CI c(ci_low = est - 1.96*se, ci_hi = est + 1.96*se) ## ----eval=FALSE--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # #Using margins() from margins # summary(margins::margins(fit2, variables = "A", # data = md[md$A == 1,], # vcov = vcovHC(fit2))) # #For ATE, omit the second line. ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fit3 <- glm(Y_B ~ A, data = md, weights = weights, family = quasibinomial(link = "logit")) coeftest(fit3, vcov. = vcovHC) exp(coef(fit3)) ## ---- eval = FALSE------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # #Bootstrap confidence intervals # library(boot) # # est_fun <- function(data, i) { # #Subclassification function # mS_boot <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + # X6 + X7 + X8 + X9, data = data[i,], # method = "subclass", estimand = "ATT", # subclass = 8) # md_boot <- match.data(mS_boot) # # #Fitting the model # fit_boot <- glm(Y_B ~ A * (subclass + X1 + X2 + X3 + X4 + X5 + # X6 + X7 + X8 + X9), data = md_boot, # family = quasibinomial(link = "logit")) # # #Estimate potential outcomes for each unit # # ## Subset to just the treated for the ATT; remove this for the ATE # md_boot <- md_boot[md_boot$A == 1,] # ## # # md_boot$A <- 0 # P0 <- mean(predict(fit_boot, md_boot, type = "response")) # Odds0 <- P0 / (1 - P0) # # md_boot$A <- 1 # P1 <- mean(predict(fit_boot, md_boot, type = "response")) # Odds1 <- P1 / (1 - P1) # # #Return marginal odds ratio # return(Odds1 / Odds0) # } # # boot_est <- boot(d, est_fun, R = 4999) # boot_est # boot.ci(boot_est, type = "bca") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- coxph(Surv(Y_S) ~ A, data = md, robust = TRUE, weights = weights) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mF <- matchit(A ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = d, method = "full", estimand = "ATT") md <- match.data(mF) fitcond <- lm(Y_B ~ A + X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9, data = md, weights = weights) coeftest(fitcond, vcov. = vcovCL, cluster = ~subclass)["A",,drop = FALSE] exp(coef(fitcond)["A"]) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mP <- matchit(A ~ X1 + X2 + X5*X3 + X4 + X5*X6 + X7 + X5*X8 + X9, data = d, exact = ~X5, method = "nearest") mP ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mdP <- match.data(mP) fitP <- lm(Y_C ~ A * X5, data = mdP, weights = weights) coeftest(fitP, vcov. = vcovCL, cluster = ~subclass) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- d_X5_0 <- subset(d, X5 == 0) d_X5_1 <- subset(d, X5 == 1) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mS0 <- matchit(A ~ X1 + X2 + X3 + X4 + X6 + X7 + X8 + X9, data = d_X5_0, method = "nearest") mS1 <- matchit(A ~ X1 + X2 + X3 + X4 + X6 + X7 + X8 + X9, data = d_X5_1, method = "nearest") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mdS0 <- match.data(mS0) mdS1 <- match.data(mS1) mdS <- rbind(mdS0, mdS1) fitS <- lm(Y_C ~ A * X5, data = mdS, weights = weights) coeftest(fitS, vcov. = vcovCL, cluster = ~subclass) ## ---- eval = FALSE------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # #Generating data similar to Austin (2009) for demonstrating treatment effect estimation # gen_X <- function(n) { # X <- matrix(rnorm(9 * n), nrow = n, ncol = 9) # X[,5] <- as.numeric(X[,5] < .5) # X # } # # #~20% treated # gen_A <- function(X) { # LP_A <- - 1.2 + log(2)*X[,1] - log(1.5)*X[,2] + log(2)*X[,4] - log(2.4)*X[,5] + log(2)*X[,7] - log(1.5)*X[,8] # P_A <- plogis(LP_A) # rbinom(nrow(X), 1, P_A) # } # # # Continuous outcome # gen_Y_C <- function(A, X) { # 2*A + 2*X[,1] + 2*X[,2] + 2*X[,3] + 1*X[,4] + 2*X[,5] + 1*X[,6] + rnorm(length(A), 0, 5) # } # #Conditional: # # MD: 2 # #Marginal: # # MD: 2 # # # Binary outcome # gen_Y_B <- function(A, X) { # LP_B <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] # P_B <- plogis(LP_B) # rbinom(length(A), 1, P_B) # } # #Conditional: # # OR: 2.4 # # logOR: .875 # #Marginal: # # RD: .144 # # RR: 1.54 # # logRR: .433 # # OR: 1.92 # # logOR .655 # # # Survival outcome # gen_Y_S <- function(A, X) { # LP_S <- -2 + log(2.4)*A + log(2)*X[,1] + log(2)*X[,2] + log(2)*X[,3] + log(1.5)*X[,4] + log(2.4)*X[,5] + log(1.5)*X[,6] # sqrt(-log(runif(length(A)))*2e4*exp(-LP_S)) # } # #Conditional: # # HR: 2.4 # # logHR: .875 # #Marginal: # # HR: 1.57 # # logHR: .452 # # set.seed(19599) # # n <- 2000 # X <- gen_X(n) # A <- gen_A(X) # # Y_C <- gen_Y_C(A, X) # Y_B <- gen_Y_B(A, X) # Y_S <- gen_Y_S(A, X) # # d <- data.frame(A, X, Y_C, Y_B, Y_S) MatchIt/inst/doc/MatchIt.R0000644000176200001440000001114514170752535015014 0ustar liggesusers## ----setup, include=FALSE----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- knitr::opts_chunk$set(echo = TRUE, message = FALSE, fig.width=7, fig.height=5) options(width = 200) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- library("MatchIt") data("lalonde") head(lalonde) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # No matching; constructing a pre-match matchit object m.out0 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = NULL, distance = "glm") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # Checking balance prior to matching summary(m.out0) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # 1:1 NN PS matching w/o replacement m.out1 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "nearest", distance = "glm") ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- m.out1 ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # Checking balance after NN matching summary(m.out1, un = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- plot(m.out1, type = "jitter", interactive = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- plot(m.out1, type = "qq", interactive = FALSE, which.xs = c("age", "married", "re75")) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # Full matching on a probit PS m.out2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = "full", distance = "glm", link = "probit") m.out2 ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # Checking balance after full matching summary(m.out2, un = FALSE) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- plot(summary(m.out2)) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- m.data1 <- match.data(m.out1) head(m.data1) ## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- library("lmtest") #coeftest library("sandwich") #vcovCL fit1 <- lm(re78 ~ treat + age + educ + race + married + nodegree + re74 + re75, data = m.data1, weights = weights) coeftest(fit1, vcov. = vcovCL, cluster = ~subclass) ## ---- message = FALSE--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- m.data2 <- match.data(m.out2) fit2 <- lm(re78 ~ treat + age + educ + race + married + nodegree + re74 + re75, data = m.data2, weights = weights) coeftest(fit2, vcov. = vcovCL, cluster = ~subclass) MatchIt/inst/include/0000755000176200001440000000000014030160501014171 5ustar liggesusersMatchIt/inst/include/MatchIt.h0000644000176200001440000000037214030160501015675 0ustar liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #ifndef RCPP_MatchIt_H_GEN_ #define RCPP_MatchIt_H_GEN_ #include "MatchIt_RcppExports.h" #endif // RCPP_MatchIt_H_GEN_ MatchIt/inst/include/MatchIt_RcppExports.h0000644000176200001440000000165014030160501020246 0ustar liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #ifndef RCPP_MatchIt_RCPPEXPORTS_H_GEN_ #define RCPP_MatchIt_RCPPEXPORTS_H_GEN_ #include namespace MatchIt { using namespace Rcpp; namespace { void validateSignature(const char* sig) { Rcpp::Function require = Rcpp::Environment::base_env()["require"]; require("MatchIt", Rcpp::Named("quietly") = true); typedef int(*Ptr_validate)(const char*); static Ptr_validate p_validate = (Ptr_validate) R_GetCCallable("MatchIt", "_MatchIt_RcppExport_validate"); if (!p_validate(sig)) { throw Rcpp::function_not_exported( "C++ function with signature '" + std::string(sig) + "' not found in MatchIt"); } } } } #endif // RCPP_MatchIt_RCPPEXPORTS_H_GEN_ MatchIt/inst/CITATION0000644000176200001440000000152714127216623013726 0ustar liggesuserscitHeader("To cite MatchIt in publications use:") citEntry(entry = "Article", title = "{MatchIt}: Nonparametric Preprocessing for Parametric Causal Inference", author = personList(as.person("Daniel E. Ho"), as.person("Kosuke Imai"), as.person("Gary King"), as.person("Elizabeth A. Stuart")), journal = "Journal of Statistical Software", year = "2011", volume = "42", number = "8", pages = "1--28", doi = "10.18637/jss.v042.i08", textVersion = paste("Daniel E. Ho, Kosuke Imai, Gary King, Elizabeth A. Stuart (2011).", "MatchIt: Nonparametric Preprocessing for Parametric Causal Inference.", "Journal of Statistical Software, Vol. 42, No. 8, pp. 1-28.", "https://doi.org/10.18637/jss.v042.i08") ) MatchIt/inst/figures/0000755000176200001440000000000013750577451014242 5ustar liggesusersMatchIt/inst/figures/README-unnamed-chunk-6-1.png0000644000176200001440000010674213750577451020753 0ustar liggesusersPNG  IHDRz4iCCPkCGColorSpaceGenericRGB8U]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_@IDATxֆ9g3H$I**HI*\EID%+9I9(9+3;;;;|yfޙoNc( @\?!    M7 _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2     P$@$@$@$@~%@W< (    +nHHHH _ P7OF$@$@$@$@{HHHH(@'#   =@$@$@$@$W~͓ P    + PHHHH(@y _qd$@$@$@$@HHHHJԯy2    DZw.k֬ ?|Pݻ'I$ģG0 /e޽+qıuAf$  ĉ… %EQ*q0yBH6̟?[n'ʸqnݻWu1cO Bj,C$@$@$@$3 PхngʁB{N$@$@$-xQFfk   h6BV@$@$@$@$Qż$@$@$@$@&@mHHHH *(@ByIHHHMOB6ЫuҼy8{L$@$@$#(@cchU(Q"F$@$@$@/XHHHHg>cA    _PBeHHHH|&@3-n:1cF`IHHE4ZBݻeʕyHHH JƋOK:Hҥp-HC;I$@$@fĈ2m4ɚ5,]TJ*%Wʕ+SpҶm[ɔ)j_|K?>yW4*UH)$wڋwe}[n:fqڵwY>|TMV2g,.ӆ*pSdII&mҤIɘ1TVMƏ/ڵKjժϓ?~߿ܿј6 -K,RP!d @oxyE5kAbjx,wȑ޽{ҨQ#^nܳgsM-&M*pC:iѢ>G#r7|SQ^|Ey'믿ĔnݺU^~e 9΁Ѿ}nJƍ<ǎ~@Au]f͚@Ж+WNp5J Чax^v:uDp6nh#:!E7֭[f6W^uIcy~~:% ]S,Xq^u9RBm߾]/^,tU>pYbV6&}GۈPfMɑ#d˖M ɓk9b90 sO)D+Ws?^~7} "V3~3h"iذ 2D{nǎ@$I.G7B^^ .!ѵۃdǎLa(=ɏӿ? #Ls9͎ISqDQ|yv)o7n\C o*Bo+F|#Gw)% 5& JV2W?ӧO|3gNmQttIE1<ߕp3dȠϭ s/JZY 5\`G{)7:JGN,K$@$@~'йsgcРAϛ.]:C9<{ŋ7q6!s; ~U-AĜ=چGsٲe(Q"ywębŊ4+<3O^ǛIEʍcv9`uaz0?bM1|!VecIvD[U_MyHHHq|w.m+Qw:-)@1Tnqۻp1b1߻w|ǂ+0 q}·55ŤoW^Luis玎{ĉ5l0{ueI@RJc%K&kז%K<6$@$@$ ڢe˖Rzu=gDyߕp4arZ 8R>S:>{LCl{ylz&40;^PXKi! # K(`^p|Ν8b5U !b:w4mjVCr0ajɰ EϞ==b |Cc[O˸q\8h0|+J5kL{03VVXĠ^zYK"`"~TPAgQ,a_~E  1c輞r?߻wo)Ly8ܘXrѣҥKLRzK;9F _(DHHH NZpϊ>駟 l}"K ^ӓ #yD>x13݌%mժꥅ(Ŏ;(D+eְ+~ .!n_ ÒM86sLQµhԩSq4   5q0Mɝ2HX\ָD EWBSLi&}x_Q7~%}I=q챰?&[u@<>޽5 C{#  &Hхw몪liIy'4X><ù)HHHBh_ 'OB*X6  HNk"F$@$@$@p$$_:2$@$@$@$@A40[I$@$@$@AC4h.%;B$@$@$@A40ZyYFƐ   ЀTi… eԨQi[B$@$@$P(@r$@$@$@$(@$@$@$@$P(@r$@$@$@$(@$@$@$@$P$\hl֭yh [A$@$@$p(@='JH /8 5!   => =3gɓ'%wܒ1cF'Npw:r߿_RJ%y Df'  $@=^߸qCj׮-۶mĉ ־}$^x }J ( ϟǏk-[PA~ ;Qy%W\"E )]̘1å?ԫWOҦM+5k֔իWK2eرcV]vIZtKqnw~z}\|Y ÐCIɒ%5uօ^׮]eĈɆ Je޽yHHb#k״1u+!bbQ*U\A9r؋sHHHH1p7,.wG9()̙Mqx{XHHH 8х28x,'ַm&3gΈX 3$ B `ٳ ^4    C$   p'@N$@$@$@$@J c+H,X1& @d(@##a.\X /8 5!   XHHHHPc     P.t ={V8s   h,pB5jThv&  6 h#d$@$@$@$@Q!@ZK$@$@$@$mA%@Ν+qđ/F +    \>vuG˫*yvIu<('Nԓr%[z*ys$@$@$@Q#@5^!{ƌ駟J߾}@xbɑ#nb3a„r=?΋<4   0 8v͛ҹsgɝ;dȐA6l('N0ۭgSKN^x9H]~w)S\|J߼y4iD2f(ժUˣGʕ+E/Ay2}t4iTXtB|}}VZ=!d 7|_1cCZzu}AJ#   yE7լiٶmnuU/PGAbhbLyb^RsefCͼAO K,:t!cA    _PQ[dĉGN8v?sܹ%Fs+M42bĈh$@$@$@$|οFB0CsY9pHH- P>cGŋRzu[[N2g,Wm]p5ʫD$@$@N'vZ)[dȐAҥK'*TSN9>G P<,_\e&UVK.ɽ{_~RxqI,(P@z-uunܸ!ݻw׳/ӧO[o޼);wܹsaÆ۷m&˗ԩSK5d׮]V޴ ek௾Zʽ*k駟[Zi1^ӧ5kh'<4k֌a1  vNx7%{iӦC 6ґիqFi߾TXQRL)iӦVZĉE2e}Ì3F>cѣGҼys;w[+s̑.]Z=NCǏx.4h@&O,ׂ.d#kJ*Ʉ ڵk6j>}7ސRJa$^x>> b4'a$@$@$j֬w[l[y %:g-2TwCZmSH'|;v%Jilb -5g!f}z&Mj:i:O׮], 5IիN?Σb! oڥ$IC bNcb_Wȑ#uÇ7~CCcܸqVޘx',Y Gɓ'ϻʕ+q`8RC,D=z0FmC oʛXx<;??"5y33gZZ(N$~E5on=MS"Q/(ʱcDx=.رC5Dz.RCJ2SdIi%ڵk[ɓ'fnx.LAPsƍb8qb9~nrJ8p4iD{?~<Sꂗn" ܍ɏ Ϙ]<]ȇs`v58>)ڎ6z2K5AƖOpL0al p;7>n4X^M Yf6mH)|("ye…R^=/^ ,\E]_7oJrF7.yGHH}EI&I |("f͒'|R&L GFI3i1G?*UȡCח̙3c$HH#ڵkg ug˖MV*.]{I~x,Y2)P[r- 7{C?~߿>}:/TΝ%wZ@6lPN8a778 5k֔ԩSK嵇6|=l>}zɓo2tUgϮ_zYz4kLΝ;8?r*U;9stM/_| >\3GmM40|HHH 8R^zU6n(۷+Jʔ)%mڴҪU+8qrSLBn̘1#i޼̝;Wz-}9sH.]q|?s: 1Ǐ/Jٲe]#2KZ蚢qra]ϵkd˖-r]=IC.]#}v-J!~=ۮ];Yh"_]nݪuݺueQ7C>QFɒ%K?:vYjQiWGwIJ<<6J4)/WPPm6+|J4%bŊYHl۶+W.}ܛ!#)]K=O?DǛ?*lH&n3RJe(ϝJ0vRg6WJWSܔҰa/IJ&v󦼙?z)Otի[ov>?Tyy=}iʙd~:À3SYc tCy:S:ߠAM߬bp+ 2D{1K^bV.LX^QY1FH$@$@&04gΜ2h M$?b",u7eo0 A&q5s^_,儵E:3O Q0n8-R}T_}Śxq_B7]tҺukq|7)/m2hxK99Y/ @٩4Fx"0'<)nxb< "}0 q}ȑ#z۷p+   3,D    /ԋ>9.g;al <:,$͈SAXjF$@$@$@ZګD#   _PBeHHHH|&@3:$   /XHHHHg> ݂x{O#   _p&_x 8vHHH :=%   2 (#c    =%   2 (#c    =U]vRlkI&1ʳ~zYvhӦM={Z^   ÅhѢ+VL3ȡCp9۷S9k%  z~_|ԫWO0 qݻwk׮ҸqcYfTXQ6m @Hسg.Κ5ɓGڷo/p9Rbx^˗Kl٤jժr%wO/.ɒ% [o%nݲӍ7{Rpaɟ?_N>mytYr-2d ʉ'#GH2edÆ VEI[pT\Y4^t?/gΜ1Ijdʔ)˗֭[[y&M$J)RHr-{dʔI+p<|P}ޥKiY/ ɓ'hѢRti=~پ}$MT.^h|W^7_̥LRҦM+Z' ! nj#2V͛7sJ޽o߾2gҥ>s=PF ˗(=u,Yĺ[lbLPu/ywm۶ګn:iԨ]LΝ j5jпx=zhСW ^yp| ׮],h$@$@$@GN\rõ^-_|z4e˖ұc;jVq6jMy.'|;v%J5kiJrJ0Zy~7CyK}>MCꗆNz׌g}}eԩgeɄPrHX ^O_ j]{@~W-y2޾C%JH{W'0n8s姺u!olX޼y5#GxS3f)#:'b`F$@$@$=sN$VLX&-`(~W1ԞK-]0AHJ޵k:" ֮][OF2K~X43EXg0x@a?o̜9SA &h*1ڨfkoXsԲN8'lԨQfg>   ` q,VɓG3xA@k u0:b&$Hn O)yAb֥)0í(@c[Ӱ>fC\{j/:/fޛF+y !ƻgC"ޅF$@$@${2uTS-S^  O9?c>oO$@$@$@D ` *J$@$@$@$>  @,!X`*H7oRN$@$@$@eX*rcXHHH p>.L$@$@$@M4[O$@$@$@G4.L$@$@$@M4ciuc;yR   '@ؽ{~3  `  Xl*  `  XNij,YPBNiA$@$@$`$`Nhn H@IDATHH @ ΅С.]:'7kLjԨ}$   '@h8bŊ5kpwao>ٳgKNʖ-+ɒ% Nw$M>K:$2e?`$IZYHh8׵u KڵeÆ 0aBy\vM9"sγw$#;wHݺuرc޽{e׮]r9ɘ1 /G_^ʗ/=p5k֔iJZѣuVZ0tUN>m]{I~xcW@y뭷֭[:ڵkg˗Klt=.]J*ɂ B /_>Yd|ꫯZuccҤIRT)I"+WN.\rbmȐ!RX1ɑ# 8P 8LށV\]rEO49OzhA\(D-X}d޼yf裏dҦMAŋO?dwŋBǏiӦIϞ=ݳpBet 6l0ҥ?ޞm  EWD>x"{EDhǎO>nݺ?r>}HFo%Jڵ^{=2dիW {ꩧd:tPiٲ̚5K a['h=q/'A =?h wǰ{ҤI퇬h!EĆ^~:a{'z>|؞$#n,O~|`T) 2 ?ox?bN7Gǀ>? kիga9, dz ~7oZe!|w4n\z@\a2Ζ-[E9shsCܾ4ih/˗4'l v< fao~wɘj7Ib3톴'xB{9}6ו@wv9ˆq`1JF3gd:^=pO&l͚5ڛ qkab&57tz si*`RXc :Н0a͛WLJ#|n:t'$@$NԽZJ0kiӦz"ODtpº8fc|akNt 7 jBcN}_`U]i}4h*mF&zH%ϜKu t @XSࡃ0`^25zi~2LӍ6oެ'BaX>QD:\0a8 sc38k+X"FA/?Yk)"  qڋ ʨQzNHHH xPϵdOHHHH Peb#IHHH xPϵdOHHHH Peb#IHHH x2Lsi,߼ys6-#  p4 PG_g6ŋF$@$@$@/XHHHHg>cA    _PBeHHHH|&@3-n:1cF`IHHE4ZBݻeʕyHHH (@ D@ йsJ8qŋQļ$@$@$@$@1D h qc5nv)XTRҠA={[!ƍ^xAU&={s9l 9.D86%K)TU֭[t/^裏e˖˖-[t={=;w^ jMN~yKMH~}<3gu+(h4lؼyZj7n\cرJ@?QhQsWk׮>6"<.FsGygZXPdk?<bB IU*rksD~5n̚5+LU9  $ŋ7Mv$$x0aBg/֭[+wCکSҥKڛawˠxϟ_|؆1JazqLZqٳ[IMBn v;ݥ E\ !a-Y(P@7 ?5ӜfHH(diҤky^|9aOdf ?bv6lX$H  18iZ3 "4oFoiӦ偖D bm$̖wBׯ/? NEo嘔 MdHHH 864"8y 8HL wT1k֬q 3MrB!}노ϦM0A =3լݛ^19ܴɓkO1XȜos;&a2fΝ[_~^P   `&?]K%aravoӦ+VL*V{X* 31yO?ԥW^ѳ1{_CE-2e0314    XO @p>zXHHHH (@EԳDA$@$@$@> [hZp^?)$@$@$@ˑ D'l,D$@$@$@$+ P_ɱ O(@}B$@$@$@$@: r[͛0vHHH :(@C/D&JH 6?CO. m~ݻw?1w: ИcɚHHH kNZl))R~?Ξ>}Z(cǎ}MA%@*ӦM#_|x0VZ%ժU+Wʳ>8HHH<H8ԩSGJ*9Sc *g?|b_rEƌ#+WvI͛7m۶bFcL{z=gΜfp^*'N,YH4i$n\ν  uၢE ~-[޽%I$|S t{ѿK.\ ǏD$)&? y敃JdӦM AG# `'HWڵkg˗Kl٤jժr%}-&M]7)W౐ي+Ǎ'I&u̙3ww4^y{t5-*TH3GLXbO?-;vׯŋ'֭[Nn:F$@AOgƍҾ}{XLRҦM+G:h/Ɣ)SB /ȼy\(x?aÆab;E#G/0C<ΦMKFN&l2ٷoj,ݓ'O 0 <>!_ `($#̶5kK{٨Q#ӧOK,CIΝի`R믿X' u.|ܹSnܸ1T :uJP4}{y3ȑ#zvj$aygݺuzh8q\5_LBs(O|ӺP }v-B˖-+[l1H"2gh_kԩ+W.Q֟1cL.Bh]Ɨtɭ FE^ެvǹM60aB6bc<7?y_ϻ?>xSBqh"LK7;fm: ^jbWŋjtcȐ!V6Ԭw#{~7ORɺjK`ɘ1qYk*NPBdž4YƓO>ip2#e"I <jqlC5 < D@ɒ% ~E!J>37 L 8jOYzcX̴s mڴ1u cx wna= R7Sv9pćׯ__/w^=y QҪU+'5m! #P+QpaxP !ٳgKN3Mۺu(OAo3h1  ta׮]zhC5A)>y/BpҥK >GSm$  8jSqs֤Yfyf֭(7˳1kDbFf~ܹs6bO96y<;I6H@x@„ zX{zY֭[ p<B%wDCxQ|F 3;|D鐋f pEb 1dn7 b4(ubf7!9ρ 3 P1PwC(49w>0OODt/}pXBEF*vO6M?3vjg$@$@$1QJ(!'N83:@@ PG Fݾ}[?0̮ DVY~O^7~    j~Z^~e03Er̙(Onݺ+h`]/HHVR)X|7zDtu3fI ϥ͓'%d/^x$G+X B֭]^`IcfΜ)6mҏ]ԬY3X~ [evIѢE?ԅ5jȠAP8u<#GH ^d ?xGGbǟ|IyHHH3G ЫWƍ}RbEI2MVF-:t Ȕ)SB 4o޼0Wy6l(>uܹs?ԟ{93j1taPY%KqբJ<~\~]>kxqo4  (N81Oxz&ن-5իY6ҥK9)bJ=n >x7=.۷o8 lޫV9'*}!8솵r_lݺZ?G980͛kۻwo۷̙3G;u#+ >\cTkچ&(עE r : 07f5ڦ'MgʔIB%`a/A+UAÿ̥pc{?P߯k׮HHdEL`T3%Z4  `&{?͒p(E}ݫW/ٰa<? (h%0W<kc]gѭr,*@rbbB N}O(G'V|:y' +&V02̫v  7n,,_)͚5 *p)>qVڟ hF%*&QF0>~N# y={:󭵾,XNjʽKL)++c9FK/-ZX.SfF'XRi :eڵi-q>_% >o!9 |e]@^ @y={4;In)x[f:۴i#SLYT"W]&nv̞b7#{k%xxNwF)Y;ⱔub@ӭr h!d[IHHrC=#ZvCTBLb*~; B!7SOk2׎GL){ܣ}h3ʅTk8@L'GY(@mDsПI&jc   B J qg8Nyn.)R  @$@$@$@QPF<$@$@$@$@ PFFnj$@$@$@$@QPF<$@$@$@$@ pRdtk׮t $@$@$@!@Zz%]v D!)(ԘHHHH 2 蘑HHHH  (ԘHHHH 2 J7{'7n,]9 @pRfӧ/,&L(M5 hҤw^vm!ݻwĉsXc櫢`ZD`ΝYfqjےCҥ4o\ϟ:Xp >.` \\f=$@$@$@$@F    )nVF$@$@$@$@ @N P7+#   gHHHH (@s P3@$@$@$@$StDS̟Ҳe5- ̱59C͊B(@C0xI$@$@$@$}>c@$@$@$@$"@K    >c@$@$@$@$"@K    >c@$@$@$@$"@K    >c@$@$@$@$"@K    >c@$@$@$@$"@K    >Xr;r%̐˜`}%;wR'޽{۷/dKhĉ{R~}ܹ̝;ZJ'5-[ȑ#+$e,N*m۶x%pKƍ@MZ3Ql޼Y;0yg*m*E P+eX`\}?AƎ+&L3[oߞV&&.ba(RڵsjL] -<@SuAmr 'u,{.w-7M6^z`& qƹ s0cƌJJ1Ap >yٰas9 -ݻ̜93!.|3gիԭ[7/>@/_"%;vwL;K`ʕh"y'dRFrQ?7>?Eo!CTb~$}6Pfj |7͚5Kh m۶M˄x|~w_￟ϛx2c|>6n(j/c(<we>}n|Mڅ3a XBF-kNn~2@4X'|b׿Cg}QBoϧqAP7'((c٢E kz״iSҥ^:!7I 8IfP㞏^'֙^nݺ5|c<1 * ַPoM Xb\w$[y]<|n  \q=Og͚P3vw-!.|SVV&gNؤ2ԍt)1'e,1~5=\3>fI 禚U2{; FQMɀ+'SEbMK;uHԽAuM0`@S79] >StRݘ1c<( 錥:_puj9묳ktf$կ~թk8[wD;`?$E&w@QwAt k={СCMP&">`Bo!4GS+W?m4tӔ޽{(/h%7il jyQ(j*I(ح?=HByi"] K X"3P˧ąK-B &J=OS(@i4   (܄T& @1-d_HHHHP $@$@$@$PL(@i4   (0Hl" "[ݻS=f Ėhl #HE7Z馛R=8@~ߧ|:uwߝv_~?^᭷ޒ] e8"k_{챲lٲ?Q߿ qٺSNJ?ۭFo۬.GH C(@3Ő 7;tĉ `=qH,V!Ž?xٹsfK/ɸqaÆvŋ(B~c)n?$PRa%&|r#Beɒ%}OO.zThAt L3g.m۶"J>}#.]X<ꄵ+g (l$PR`lݺtI_єK.=RVB[n6])+B>裠,X).9ꨣ,MΝ''_ >\ꪄ^x,(_WS&M >rq@TW6R'$;CL#\wu2ow l/2 =4q[n{N9qN-nڴi?iy*N8r7TdŨXtz;Pqiez Mڵ-/"tJة bԩGu7nt:nн꫖v̘1և /ҫhtjt1ͻTX;RBŴSQNbsq*l p׿}6-&M8A;1\:wje ׿vsxSNş5GA 5zh裏:wV M]f͜Zl aj*zjWn]w饗:ꫯ?kZ-AdFQ:]PHgbٝx7 Zyv>o=Vڎ:uݮ /i =A 6X[u- M% Ç}-Z/h@<Fe@B#TE"ɓ^`?؄e]f A3V .q|^@F p ^HO=b OLabBT$}<]1CT +Q%xNrfSBU/؁z7WXо7xC4jE+WZ#]vS-}3ϔ5̴` ?\T#= `Jb@x-1>/acyf[€XP+p(SE/w#6has1oBWoP\Ю/H2B2 dQkP\cǎ&fsr {7}w/K:Pqx/">^B`GLFTz:ZźWE/ }XTx dhf8 ,ZtxB&`]ED+CQ;NNsaÆ0T4N ©7p1@x+J^HہpH#ƧSXb*ְ’ \@d"秽}~~WZ?vTw~%C{ߙuԯDX稻㭍G; o;Nu>?֢ X&HiӦ>}L `\cXb'dA4B?~|@xd1?䓭XF<;w~k˫$@ /W - R1TdlZ֜:`\ rvXj4<iY: >>(cjYzQ\ " ^Ŝ!Eymڴ1D&Cq\sMj)4O(BT;> pA*L4ju?SO]víR8Y"Xu:mg n.P^36ڻ?R%;˩oݮw#Ҕ+,_~9`=anPxbzVv?SO(Jp?ayweҲeKyWL=mJP&3 @.P2 -qF{#;RG9ƪ8症Yg%s,{]ve$ G-]vxh}ړe &NW >s=WM&z|Bg{13p;lC>}رᄛ7o)o툩)SIF)7|uQ5j$gq(SO<,'5jKH 7n(zֻ`z^g?]W7XO>dsb% zEzκs2}tu&s̑k֎1Æ ڋt} :Tڵk'GyvL/t|'7 x[G$b}ҥAmڞ?~={~e$Gym] u, >(?ϖÇ[=Gw^ٴiSPGyQGI&MdȐ!&vQ/, gKrUW ̣['I'${6kիWˀdʕ&,!oر?ko`Yw  $@$PBjժe}W访}o>_n̘13fi*ׯԢiq3gtjtMˇ7x6Nzմ||'7 x[G$C_j̚5\:p@sWAp)4bsDu8Y;1_5}:aOv[ 뮳u~iA83#[oe'U!+Qn1 H^JW\qMGc (Gm. /Lc3|k =,E qD9^XyE{$c 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_@IDATx4]۶m۶m۶m۶wm.nԿ{gTUVduɈȬ$HB@! @!0HbB@! @B@TB@! @" ڣp0! B@P=B@! =h­„B@! D@ ! B@("= B@! 3 B@!У(*L! B@TπB@! @" ڣp0! B@P=B@! =h­„B@! D@ ! B@("= B@! 3 B@!У(*L! B@TπB@! @" ڣp0! B@P=B@! =h­„B@! D@ ! B@("= B@! 3 B@!У(*L! B@TπB@! @" ڣp0! B@P=B@! =h­„B@! D@ ! B@("= B@! 3 B@!У ֣0!ЇꫯškٮC 1Dilf00ô_^;a7ns֋7af)1 2HJ;]v%,r5UoQF%8)i0φ<0pns.ҬO8pm7`1#aems?_t\Fa[oO>9+_?|hꫯ6|_*K!P(B&>h?N8a4&> K~Wc=guָ֚$nFYwJ裏fag}vyoj;3%kYf=x?ƑG9Ì_tE+KeϋM@⯿Z1tsQ9j6wqGQ~i?Zy1WdM\r`./'^7 7^ +ΧzjV7{SM5U83dZ3շ_ᅬFX#?SՆwGPXM4{De&jIkwAe5$?4ԙDo2o=\sMŀ%6}l^Z~믿G Mn5ǁFLU^-NcsԦ{.C=gy&7ӷo"P_s裏:,xw"n@ټDF@Io3cR8vw!d%]yqI&^z13oZS:'OO=gab)R^?~%[f +FI柘ҙk<Q]My>Y8y衇/toUV>x֞:5X^% ywƥZ*^J;C[,^J 1㭷88ꨣ4LH-e."8S~"_^5 tAS~N:iJQ soMeqM}UcK,馛.1߸ $2/ui273g+^?l:~&<*{.*=G/rǜ^w9N|DKKq'>gg`c=_=q ÔFwqHVit.zg舀ҐuY'{kmh%>(({@Ğ^.eËy駳GcLB\-Ji;ure,-yI2d0W^g&tkey#e?`OG?L$nm4yAGRk(xW,kSm!Rʣ>di˗?Edm˧3oBEs)1p,!sO^!K>a .ئ&.@/# =3{Qr-qe/-"CvAB EvZ_~K6 'tRC:"*!hN~'ISu5Lj )OY=裣6OO>y $O 6NjAV_5Lr|ɛE[wvY9yJ㖱뮻Fry"x'7ވY[<,-<yWlYSN9e0q FZiҞ>EA4){˒p0vfobiI1QKF^ӑ߈݃i}D$&]ٹt={j #?SXp iÜsLsY}4dthY!>;! vʲ"/w`J=66%m/^^1w$^yE^>ns3g@^L_Y)JlLV[mRI>Yȏ{Nd ID?'M`φmcL >&]w]|k 9nbەU<-K/Z5O013~xꩧy~U]o U G& cǿ;+D'B+;!m\O͞c96kj9AxYgf3õK^ty4^ ׼آ /U31\̜ i:dGDqλRfY~ +e3<bY3⧝vvD"-{"x5 O?NĎk+-J-C\7saX[oeUϑ1~)2֑:sR^O.yg7bI]//ᚯyp‘ K#n~'WAmwznc=6!iC_&Z. FL\hl4ؾ'.b՞#N61⩔|+fzV:ZCpG4-  @xtI .`k)]g͟4 YɨOD|iީ =0%B@Ԁ@~NEs#Ti|ve -;^j)~? e>v[ǰS~C~'h҄NoB[zi;('^},ݬ^wa52}Ye:Cv;0sk)>-⋐H\lAسU~;|փ=sm4y;աsQ9J<,BUvm6ipN||sʶg:{v ȗc_Q˶?P@3 *!gUWM~h}:5 NӜKd ",,ȋ͙ E8S$7%meSN9%IMa,`V2@)~1Zv ږAI;)^iю847lt5~|}2юYмB ƿџd%f_#AiѲg*imϲsa4uOxy>vR=GnhKo||AׄV‘3<3s,B}b;_#vE JW~3"mɺ=XpR(C6u^^ n`벼:A$ YVS0*C^ ?hij)<;evTjMcgqm؀(ϒ/SPL5БאF 3Fɿ""/H.M=pO`. KH:[O|'w_qMG\bձxյND@]-AhE/m礑D[_e! @=rB@!X(_As}mHDE@gViB@nC7 Voي'+q``Ph6MBU U{VB@ lV@M-4-эjB ?Yl} SZ(B@B@CB@! @# } ! BmPeB@!~B@! h*D@;T! B@>"jB@!  ЦUF! hZ(B@B@CB@! @# } ! BmPeB@!~B@! h*D@;T! B@>"jB@!  ЦUF! hZ(B@B@CB@! @# } ! BmPeB@!~B@! h*D@;T! B@>"jB@!  ЦUF! hZ(B@B`M)<0#ZB@! @#0PC[o5 ?MѤik}a&} ! ,' 38c6_Nv` Fa0\su2%B@! @b)ishUvB@!  ! B@4섀B@! # ZE]4;E8K! z-BUF3O |ߖ[nXb,]v%L8awfy켻No6h*eR4Rw X1D@XB@#r}eW9vqA%k7Bߤ5 70˿/}Vi~LP@#8Cþ⊀oGK/tb~ /00 aq o}"~zjtMN<, ' jhʘ_)_3Bw<99sB -F}ᅦ.cI' {W;"VuYfI1i|oM6 c5V2 {]:G{-;L6d;LIz'O믿ϰ{'v,;SŠH;Cxl>쳚%Zj0ꨣW\1|)m~?+?puO}zFUQ!ED@ !@)ĭ aO\ve.F|j'FnkQea34Sϴz\{L#_~EQh_EFVZ).2uGqF4!Oeۢ6~A8}eZmjsIB4myVsGJ4ž'|ji > LL"1#zA$3)FP׆r#]cE ,k44?Cӻ=G L3M0XENLFlF&F u.Po.`J#W^I##hezvZ򥽘Zko9Zj1?-8`́hsj!"履~J6h~C9d:/ y4~3&z܃BH0x㍔u^p|PIeU5qC0Q !Q'<~WᦛnJ2 .>ct2 p WLkoM{^9LusT~fgxFlC=B@,7rU}^ܕ| ]"+;Ko(Z;9?BJ=>4y!m~N yGC{ca2^MӘO-fϢA ]vXe'&O?4imTKhYy騭F\9·7 w~ɗ]霾×_~x~uCI~(\C@VIBO"yvgr4}K^#M;3,2`b|f /aaŕϤgS-nBLkRYݘ]L,k)8)EK!ŐO &{Nj%_LGylߑm.Ȼ_tW>uG-hZY[+[os$ͰDG@@5MF~lI{zDĦ㘺!: [*!iZژ\sʹ-Y1_~cʠC6L_׼OcNVR2xn%Y6$-Ť{%[+}E2d M5{3x\!}Gf]wME0tYpD|v=-\H21+-jJuJ=]-bߣ!oȓ] @F?0 |A%B@4AthJg08DKYm[eeӘiS#?),+d&δj@=63q4b1Li]v,sVV64ie 4RU-Qv͉iGӪs4ҙAôF|=JJiEiR6بU&+O \g|3mWFR* bn  ȃ %>ҙF<#RV[sΙ0*|J_*x|a*m Սc=&ЦP]B/6HDYaQ",ra=-h;YCKaL0iJi'yLd[#PL=! qr(G^,*TQ#m=4G#  q9,>uZo=A@3#%\\fެ5Th*x@:"weD0"a{S5) JL<$8}[nUe}ς0B*! B@:,EB@! :"]P9! B@ԁh`)B@! @:A! BD@KQB@! h1TB@! u ZX*B@!uD@rB@! :RT! B@# u B@! @ ! B@tЮcB@! @@U! B ,B@駟;4Ha0#<|'OkR{r}`p:hM-~ꫯG9,aqƩ)mGg}>0QG$dHd! W` xV_}0Xc\0姟~JKdQz衩O?}aR&`p'.(;l䭷 n|rD bts=;?? n0{ôEo /f}6ۄF-!QRo&$@fDJ%B@oi⯿GӒũ*TcDơ:6`ޛnow9B|ch4]66kW_:j^}h$/r(+8d '01_Lٿ qOv8. G.I'469E1 wnk]$>0&#<Ÿ|_O?='|r|(4ٽ_~%a 3}ݳxdfY` "MIh9n+EY$^xe]pd@b|SO ebBrqǥsHҵꪫv-V.>rYf%a6.R4]=ܓIr!#y8)+LGI=19 \r%鞹._=eC G}h.5'XcTA${%G}t\oR9cBN 4ctviC=n9yqc?|CLD@^_WB@4 c9>h#7t{ͼO>iLIF 38#*L4/|j"eᆋWXa;4{Ѣ<3q?|4Ÿ+fb\_d-\ ;eJqCk v]vY"r009#2syQL3MʏDM>9Q=8lŅZ(<]w]'wygJ ! D:ZRozZk4rK /CZ־bZ!x^-0Ř\ ugP=K/Ӯ%A#3 uJF–|wuW0L̼4Ҳ[o F¥^LnjZ`l3:+ mY0|勮X 8^4}U!9駟~n֢ax䃇/ݼΛ/j>Lhxbx^[O{ў9qVh0Ւ&o!>rDkшoD/h0%DI8x iV1pg7Y>$3~=hr"dVNQo׶h* dN b|oĈ}*?]\\cF 1Q>m#>ԗ/ c1tm&.FF=#Y2龞F£M#ΚNC_nw3`M;~v_y<="R@_Ѐ2ːtN$-&%X"N>4Eq饗N6/8Ih<Ӳh,NLÕ qA 43<麞$|hi $ŜϕVZ)]w';"6sa&]cQ dkʘV9i&r6?ѴF♯E6die. oD*aQ F,8ˋV[m8s9gj8Q7B Idxq YGLȗvBIgS&;-&w0Y "i\sd_&L / o7(1p mO+@%.g#E@f%d8Xi _\T{M/Юu ~MLI*]l 6 "Dm)LD `r)gRvȪkßr IbqASp䮒1 70'Ty]XMeGhK^|ҁϪ7uʯH DRc/00Hԁ|YgT-EQ\cbI<; J?\FR>^vmqW:gru (1Ws1L¢)tym{^6KOtC=D@Te٧`*f<`n3)Ա"G S&,pf ` kHH=b~qL89IɄp\ PL.gz8 bE0C!E 4A#H`G3~kNjVzCȰke g! n5h?ңeG9y^evXv?ˆ㤔}@ ~ʐDvi9`]wE;J&^&GJܝkG8G uj>)L8 U=%lv&Pzb}-b"n 0Obz-f2I`1Z2 UZ \a^Fm'D+/hx1osd&b=+!i\sM?IV8'堝RٔvۥkVeßILh.F4" q:-Jd-0{߷ bQH7I\&Kd˵e XRS@) vrƐ-h/B%y@}81O-JyF)bi;Haԃ~p? &Shwq}P@tO\;Vnt%J1_,p-DzwWjtZF#D6Qg*hecxH$Q&P!)K1!nqh+a n V˗& HʆB] 1d}L$4s>Wup`#qhkC L€X[N]Y2 . b<`DP $zHػn@b$ f%>Lj*HS5ut([ _auAs9s\ rmO{uW0+#;SJP&yC`ТqN|@jiWQxES;Dp\cǑI E,E>acB,AB6gh 9Rdqa,*⋓ h`Gem<'\ 7}α~:6ӵh3F"`@]Dqؼ DM%]G-k0m?8eӭg}6i,A)lU>-ZE4}OYL&fdVM`D3'NÜ 2!.v| !4L4h 0#h\}>uh)z zʠC vFCxBim$'Dm&ӳ>hI `v&r \$‰v0Ҁ.4ZI~bF=x%h"q!?jBz7-Ѩw}Lo)ZY\ZEh,\5.0!bĊN(}֕iG0fɫ^wxU[_ʋ.( Y CJ] *?7JeG̳T] 2_'$;cEq} s-dm+ߣ2m^%Bg̿ď:D_{b!f榍eaPѼK Θ!Nh&mtA̪44^.&l~\ % JHZzl gasZL_^rIe:h~,fqg2΂-(u,0/LH:=! @>1}6N_ZIkf -M0Q0LLstL]޴c=4B4R#HPx'9m(ad,__N;1rХ/Ky?`a39&85̥LK:Lis F _C+J/d{/ߴ 0m<3 ,# #K:$#E0ґ7Vy1L̤dU psQȢ^ĴL+OFD}bD2}&>Fҗ FvlKl1MfʑjzRl3ɧ#Kbc}i΄Mg'4Hh*hԗߴkL#/h%Iz{$@@N$B#ņdtSo﫯xU_B̂kIk-# i|]KE[A#WwA-Dhgyv%"o;c wjY[o#Q Pf"N  ДDq0wɡIA@ (@Ӄ`vsh6c&p}6!yqQH[^ R/%- YXlSM =y>.y\#H0~d'Ql*u-k;>/ymLLxN|0I'Gv hW}|CLw'HhW!,TLD@B/u"NɄ@F _8^9x@мIS>-J[@~+}hٓ sNTP%]9!N gׄLE fPg6E7ǗxC(m`;'|dRqB@Ԁ؂@6mTZe~muKDL DXœ21mU0Uv+^Ab#Hn`azj4,1x0M\# } Q9c8`EPF<g|h'yOh0"L+dx7O?ˎsm[Fۛ4c,2LLXd>RM |c`466S]FTӢ/@pEa,6BئAԏ|ߴeգf[cj"lZ`cAUh0{" kMl$hj>gв훽1#ڮf6O^ZU4,7 b좥?eb1ma[>X- Zgcv%q0c/ LL"mzbC|GoElSN9%yuLHy/p(Za*}AEHcNdB#x(/}V> w!)͊1~3(lce)S5?ŠdZbpMɋ?i; ϗ0ۋ鼘N/!G ڽuc }i(bmNr#Uh=B)t?y4Mo2ۧ1i?nS|'S=n,kD#اhfNh'S2! P:Į+$Li[63-T*%W^yelD  E'5nm.DU5D@_SmQ儀BO|!MD?ZO_tBi".h5MAV/Pq@IDATGB ! MLyOV ! B5UB@! '"j! B@" B@! @D@Z-B@^C@נWB@! ~WB@!k*X! jB@! z ^^ ! B" ?]B@! @! kЫ`! B@OD@gB@! 5D@{ z,B@hwZ! hA@k!036ڨaytAS?4NY<ᅲnvw2>v@:Ҭ6xT68T;K62' >h\{â.Z1o&N*ۆQF^G9}F%P'B@4"M h  gzk5o zk lMMrpWdc5Vs9k-Z/gy»ۭtW뮻n~S}-݅`A@` ?px衇iή0lg}vU@#5\?:(L8MbOI1WN˪ے5D'B@C@v(@"0&v 7⫯JtVkM7 j{2˄7|3Ż[~p饗9aꩧO>yg}'|~;,aFJuB3_~%iV'x0裇VZ)|G(? lI@9묳{キ^`GKI~7^Xh·~ ,~0s$r?3af;lb)N;~naW_ K,D1믿C9$i'`pvJceXr {'laO[c_|qŶ<3|+)MAB@43";ꪉTp̙f)L4DmZtׇW^9L7ts [lExy LHy?S: {Gkµ^7*HQG mzxRbt|1şqg i SN9%ld?~曇y70 JHsV\qŀl;@1?1bD&mF YxqDž]v%}٩M);#XAwnfݐ/6tL^zTw!emaeaIЎ+auB `!>H$B50o|'$^uUiӽv! qrᇧ8J_~y6g62{tͿ?60ъFH}fԦ:qv[M4bwf-6`Je]aU;1"XcvQ(=GqhZ,#4):Y~z838c4mo 3 8FioiO^xhhш`v4 SHfaœ:q'okݍ(gD;&9G6d 6u4"myꩧ:{s(+f!6kLTM!W,`L<@835_h^{;臘bR|^^z)L2$LYdO?Y_M}n+S5LFd=a}58|V1 h %]((Z|f5Fm+$fI!ZYX qc0 H'M3F}%4#&vvpwZ,~E# +$-ukG-)ha@" K.I`>餓#K?d`o|E{3N{t}u>D h1ls{OM`.vmW#! QtbC˄{1Eϕ |]Oo5ʡ:EE֏?ۼ 5P55Fjxp+,K;j* _Dg% ,sπf-~}%(^7&O,^@?[o%mJ\yK=B(&T_FhR_*`ULD{W ! "}TS!gT:S/?BX@jvX 9yS;][L5TiKL.]hji6!( jԃPf{ې=wDfx[IL\LX-dS-8J+k fR7-/(-:hPk*Sz G'Cfٶ- aNaU:cEc]/$Yld lr!L>mBJ1{@lW©@|0i cNxVsi/ @؎|! +ĐmlP+.`̖Qb[ؠn\(.J. B wJ5} Jwc+ޓfYa;$Hi 6Ch#7mD#&;3VdML2<ů{RwD Ad4 }Lܼoi-esId1udOʆM (8F mзzD6!xa·㧋_+l E!ܮLJb4l+3Dž(ǮH-ԙp;hvmWv])Si~ӿ%0>>}}!hЀbf?Jm(Z1̼h!9%OSyY}T\CqSkj$B@!Є6aJB@! heD@[w6! B@4!"M)B@! ZV]M! Mhv$B@VF@{WmB@! @" ڄ* ! BmUۄB@!Є6aJB@! heD@[w6! B@4!"M)B@! ZV]M! Mhv$B@VF@{$B@! ,"E{Fy7sw} ! :oXc08ㄉ&(l_~i<Ȱ+Z+wy!&߰ 6 }>hsykkg4+k&<UB"" G?2K<h~{'0pW_MW_}5lFaoy}y 7xcp é瞰^{'8>r -PXtEAe^{x㍗6ZR&t}`RKe]6ՇwhDԅ%@`抏?x'R$;o U믏6 uY'9qa ,@|*Eonp~ 8C%\2~w/0Ѵ hd=7|D"pe)230V4֐BLmBͲW_}8CG#U?tmL3MdAPaơ*vimb-2,4..bms1ij>;>)M74[o0zEJ{7?)4)mNFSDZ;iVFl7NHU,]罈 3mbҋ5hfN _iĠ'218 *ŷYy<3S]wݕZz _uO~C 1D| ꪫO?M;rZeU!ep ?X@Wwq8qKd\{wC~i"ZS_|E#hmqǍLf!7pC+SC~z4ץ +v!+qIGq6άKi2=< ;TSMBnG}T_n Hn>s=3K/}Y",yI&)m7AWmeq=0F?үGJ m MK! x/ Xh"F0kg6K}&馛M<n- ?|>,wdP,ܔv-KÉPf+80.).y2}Wm /wuפ!jj ^6O=TOۼjʠG99<%3^ʐ(Z&h-Z4&G<Ƙ4s9gn c,\i҄wWN[nIQF%BFx~|ca2}6;⠃]3w嗧thQGuԤդyq=My\gON, w9^}qEIjW\qEs8$,-Gֈh@hD@D{ L+=7X$nVhޅaN1249]K1M_f(]!0k}GL~Ų .I4d<8XDHlV'kfl&L,砃JG(ZC cM\|mD|Ho--$0X 0E_ns?[m1W63XfIcm,M[+P70)?dEf|bvg抯V.O\4 ȚkLfm`S6Ǽ/0p#[q_M/2#믧gXh!H L:p駟nw|-~db'V6LjU6A^a믟*=gRvEE+c i$~H ̶z=YLQcZGI=R>*X4k׎22$?H2jO8w{fĔ|mi^nŤgE}m}$ubE+QReKE|^y/4.GammGK]GYpM/d A^>haxex-^ .`zbV# ,#4I-d2Joq_Qȃ 2+Yxk!nwrI9Gs?)2G/ngKX5c>"/O&弆烲+=. {\3ifD#vƎ =\S5RwB w[mސw1cqC|XWQh7a3#hu|V `H0HAּ&*1Z,::4)1@s{Haځ B7̚ɟA35y`aQi5'O /78矟lV2X}/;y睩}LH ؏E_-BQ!%S}!+ў G̉e1w4]yʧh#u]7K4gLD!X>&'H1Sj8A|lAFgc0Lzs'>lʦ<^mo-cdwOޗZ}=V >h:˿'d|{n&O Ӊ,n򵫆Va3Ӯjf3>:/pF%.SYdVǾtI(&&l |[pvy#=yڂ O2-'`W _tE) Yw uRoYapA n#wl20̡&We 0aeIҞ620k-/+o 5&=&&`E__ E.+YMoL~KC7]09G3K@ YTD:f>9c)J9c)Q*%׃0"Ose&?QlP碰PWhr6J6h w~+PWBxhYRԒUPsA*hp0١D}Kbá,_/A)ym ;/#.>ϑ0/>b'^hFA>AiҊ D0b1/V@%T~`QY9eK}4; N>T2Ëfh~Cy֣Z:bĔr{8D]v`v%VĐah:#h$IXkk0){ fcLaefN lMLxȃ-Eaf ˷M(if1J8BȊ9Hg|z 9-&~4xtq!/V̄L4xY`g9X[|  E,(*HQ  cEBXa>!@%燖 +k֫xq-X'&Z`)(L"l~e 1|8q" Hg,H'ZM$+ &4hݵ^F62=@>д'a؈}i8Bf7x㴨3<Ae }K_gLޙt>&@'B[n-guɏu^gE2b|-½Jِ݄Am*/7wgK(I"_AភQb28|0|!q4B xQtVcu+;BBfUnvVfYƪLV|1)29rZ >*5bRP'wW'kPn4XDxfyV_&g~ [P,7Z7 ,Dhi?7o1݇| 60aY! -' dA/L} ).U bgJAN8cW',HC-89vU֍q6"LTd1w3(Z&܍B.hel] =)L_>躆#b/2l%(n+jM}1ċ"eEBo(ęOZAJ9Z!XJט\ɹ狝 HkD8/0^8eVAJìK5/EL l( 3H ϛ!!=.$t.) XB,͢h 1dab/L t2RR)m~VL?fee C*dG+P]o ^vL@$-c]עF|_2 OxzGwqW;OeA}P/ن؀1 -ZQH /h'',g#|R6Y&nzd/dL_ urBbِkQ '_/d&M?V"O#u+ N3'@q!H|/. jlM+$-A363D0025{&.Qjm'GK ԑ8w20# LEXp(k0LdlJ:F@cl ]njx%efs^>4jLA2¦RY 5?h~88/J$R."u^vI-mreG~7^ne/+|r^Eab6緘k0&o{Q "b!$Ny!_h!?#J1u2T_ ^4s﹞?A1i5!C+ d{[&.$M4zs4ݍKߑM&ԅ6codr _T]KX8adPܩ!]+ Bz"%ֿ! 0(/#}` ),WOYxW5geiq)֌fe,6gw $>Qbf:xirxfߎZ&^.Ta%EZV||FmL\h7/\&%2whg5$(C/f'^9k:xpOqmqИ1g|CNBH]_0'UuS;C x&yJyrQG]!q5>.F2ħB62p4hcqvIķ3g hs. 񅉸h֔DMW=hCsc m,33`y=2isY̨ܿmĜ@J wQ#P-/4hxA hEy.b XYƊ8h\+xR<3ٺ`"x.>n֎ʣWFS$ӭ+>O,ꊯcd* 1ddi6h7C6˄M?m;ZWŅ p+"--R'^/H-ϊ]FB,u,8(c s(,(%ha%Vx]vI,SZ1wSV>/Ihm!{0J_d,b#Z @ :o)^=3^辨*ZY`ט1SCU±b X0/&sI>6Jh7wNh9+Ǽ}~D@^+A:'`Ho Z; [udgHϧ蜅/̉ ?&$%7xFоG"Gyl!ybpO2|dřLNC]4C6ޭLdOZ {wE@{)UG`nX!` [}`dϋ/:>N̯Ҁ Q;" d{-7[@yñNh!ܯ:$ͼ@7{3NqĞsV3qVI-&u,h|i#2W$4Mq@{{w1aekC@@t{$u"`* 'pB(`dykl0SZ0ղ#`%u!)6>L 3`mZfa`Z΄iL3;#6?Yw4H\0bW=) FKm邙#}4Hrk R]̟4m#(ۓ3`` iRYj< p53ꊀv9NdB@>߬v!fyfHYE>l{0`Z`lvJ' `mj ! B Cm'Fkm1Q_VN@?B@~jB@! fA! ~jB@! fA! ~jB@! fA! ~jB@! fA! ~jB@! fA! ~jB@! fA! ~jB@! fA! ~jB@! fAG૯ _~e˷S B@Ԃh-()5#036ڨ4t?.S_.ViwߝJw%nHy7U[nezcs0O?jެJ|ͩ}UnN*v0(ޫ[Yg5OY9n,Ѻ70333snaa03㆙903n_^ic{lϩԒN˭G}t!  OF4[o5߃o `]%@䮹暮.k?CXfe[oO;&-X~^FYZhJ+y͂ .}%BFmQSh^xz(,juuׅ;<һ:ukY}o^x0S +O>$l^ :j\wwZ l{7xa1'B}}V-=f?` ~5X#7tSb` /|M?;|M>N;{i lZ*>^'4YyWK/<%\veXcV^y_t8?<߁|>lm.hmkB<瞰;I&v}ZA<@ D&l3/ե;zhoW\pc1r->RQ~xp ïĉ>8$b yBSO׎hkj+Z4ʬ&g=L>qׇUW]5<W\1L9~QF!{g{õ^W[m0묳: ݚk{=;|;뮻a^rŒ\h]v1s=^`;G´Nf0cysm(~PuY'yfm@!p|8"?H8ºK:AviNL>Y-DvG8O" ׼aa n'=O@qg3Uܩ[@{#`31QÆhlxGǫ}~δwU:vL#|w*Dӊ1_R?odf%4Hшe뮻iJzW_iq[#~BO3M3[:ώiA 鐞9?oQ)ӭ8sF33y&LHbkoH12oK8PC9F)Ѵi4ROJe!MtnO쐞@Frh9_p4mt)w-p 4~ /,]?dE#ޥcscmQ4 v)M;B@_lmpHdUG SL1k; :B0`V3Nxwq'L;1&"A3VԈ[/~0tӕҲ>h_Gy"Q]dE%`zhfeu?#z/2ع|!3<_>}J`vb4ݙ`~K 5اj߻u 't4@ЊĴz+?%h#$K5T+Mu $ ifi#H\VqLfm6?[o1KQ}ǥ<f_O`^egGa?߳ɥ}Her/HO?Nwz"NcK_Ih=D@[Tc!2È&ho1;uX(/,it=$ F Me%33|ەfSfy睾&(/ hykK:_eIZIY{hx\0f=[.8 ^JrH8Si_A@E:J3^` )"DzGy䴖(iYmfe։@v b (*A0)m#s?gq'' R{%aƫGY(ôfі4A-&,Ie=X֕uK`HcnD[h c9+T/So&q¿vV7ב哦 m*_!z1K뵥5f}?kheTh ˑ.+!MY싙ғ̷]/ߥ''[6ʵχT>S엓1LA:-^S T> ``S.o&.`¤ AKe2մB`D:D6c)fIRy y5%P;餓p5j%_.$;R<[mqw|>S>mdb/GB@4!7*Q! BmTSB@! B/B@! h#D@ۨ3! B@"KB@! 6L5E! h+(B@6B@:SMB@! @+  : ! BmTSB@! B/B@! h#D@ۨ3! B@"KB@! m5E!4___|EzÔSNՐ4K+믿'x" 807xaE 6Z/ECIIUvC1?3@ ou裏k~VoJvҹ&O?=,a?C۵U *5\]t0c '0l>+Cϼaf tPxgiF}׹de/G@1QJ}yo oQF%am;kX`0TS Qag zhX~; GuT{>82,S[xgC{0Ö@o;:xsQF;=Z"'xbwqû&dRz~nc1on%:L;DM oY@IDAT&38aVrHU}pqDž=#zaK8#^p>RN7^uȆ(뢋.#B -뮰2ˤ䊷L c5\{8'JnYTmXŝ2!*/_y84}7Oq9猳:k\j38bL14o/xz7|]ve9xnx=7tSto't8<馛4\4q5ֈ $i7-]Cgܳ{ƍ6ڨl[nhؿ/,{NTMP_Ʒz+ѴvI'ŗ^z)ڏNJ 2D>@DDllRgh7{f,{.._~qWg}v̖8aи^{'|l jh8H#y{{7פ1e $67$6c7|h`f6IG)܎ĉ&()w)3<-J/@q4g' S z#f w(9b>c/-+ĉDU>CRM䃢iQG 3$Wrz Z2p0Ѵ~ioQM4=ˁ{lMپi`3#'܏Msw+ /w1#`ZȤ94yH1abE߮g{M7 TN `zw O>d?81VADz!?7*YQM{x?qWf= 9\phRytrdcviK`^r%jhY7|'~ns"wG{Vn,,^>BD+z^m]<qt6I)&f={igVR[Mj" OBo# :GyĉbDsΉػ5{ܷN;řg9^wuGfgAY~ ʼ!b bv+Gyk6vu׸J+:[H$/I*/ /kr|Fs7z@JH c^2hy1允>F#bnLt;v 91hd櫯Ckhv\=nHZM `3I4}tO+=h>0^&M#>yf4za@0dyZ:Ya,_JFcJl愗1r"y/Dd"Tʧ.meO?>e{}W},3¬^{FH3ythc7:f饗1_Nɏ m|2]9jIrr&4"{L?h):ZVLP|0CFع"Ky`d :a:d szdg%`>d2RK3S'^$^/jLDAR^rf]^IИ m(tkM$ #VrIAy~h4?k=,K/ 5'13f}#lk W^ft Or1$$iE5cIdi?txr{J/}z9eaif QL~cM 0gE@;MXX&sC7riD@X뺉<^t8ل5\94/6cTs>s0q 4h)1`qmS%D{LwHEZk1K]hZI9o'dmWDyQ!hƸ a(/$ߋrVRL?̠I.'%RfSfk)md4& ?Hɣ Rik--1;煼N9O$&KNL.>a ,[&y7iⳒ3 &xY6!הAe$ғچKBAV<5*[>hE)@IDM[0޻7q&y)1V"G$Ј|0-q[@&!>qpgs /FeЅ?fpF׊&&dRli*ݪrQF^\ :[4&^ ; sBiw޼KG; mf;!m+."L|xVQp伟.6+s,'p7|Iz4NR9eHbd}Й$[l16$$d k YxꩧzHr/0Cȗq-&tA9f @;`\m>!)=m!h$rˈKu"liuB{d 'Z__uEI-g.wz~zH Mْ7P:iJfh*+L2h~0A 2Xт22~X i@!ᏈKl݄ƠH'aŁ$R֑CA ||^& hR]V/?O[)ERwV]uU`o0أʚ)|DQW9*zmAn7BNjov$("?2#[֎ rA/X X-U'@fI:,ס- .EykV7`LrC3MPdbM9} X<{|CT>בg2Cɇt$ss& YIS5X;S~Lj;(J΄cB2.r@;ǤHs .u+tѦE&;" :mA:Nacq5O^ CBP ?-RkL= B%}bfZZY0 f \|Y'1Z|Zɇ:'^D!gZ_Q$/jɤ$\PM[m~6x>@^;cr33HƴemFF44X~cԥh'xCXΨ:UW]1pgIcccld^lbOwg|t;.)e Q6[~gu}At[i)dA?HG#~I+K:cJ23q5kPt%i2N$q"?=+rʳKc5 db*|r9;$# Z9V-we;PLD̸!:E50x1D-eD* a'O12gE8s$9XJB90m2:ulEK3<rA$1d63Xf% +Rhy1̷hI ) xJ$ a΋ 61e5  m' r>mEyT9T~h"STW5p޶L+cI1_z ]I뱢CɄ# M &K Y r d<oBV1dW !4\G{(2G:V!r3>u<'Oԑ:X@F@. i~RIMd-hoBji;4icAJY?Sԇy0С1e(tJk|H X$h!ŕJҺbBd'- XBN9qf6/"8Rd&IcUrA˻mV kdȞCcʳ|纻1Abl,4˨A,@iB☀K$ zaJ@qbP6y2&InNs83.τ8x W ).xf1M"drs|CěEgr_` BO7M#&z1rɤ9f͒]E,$™n+Zn-qW;Fbf`֚Y8> #fhA ??+0?e3`NLˠ0CfOZs.4s@1K218AͿ6s ZK>ܟ?"Oҩ/AP@[ hO@٤nAKD^23b4 J|'A` Z"ґi- (&^Y&]MpԠd`b!p+IF/ZlS^~VeR]L iPD lCcĖg %_LV~"CWo':&9b 4d́T&% [ou)Ir"\ FHc܃l3~ƪL!땐> I)@>ܟ&YhZK5?Xk# R2D@+é%j 5/ @!̎BcȠS 3uDr-<Bx1/ 2/:hҠ rh-Ww~s"aGI.sCL?I~H"M A-_~,!?~h2aDy]zc`<͸撱 r`6G%`2>ᣘR 9StpC rW4P81fe e@@ά~OpN`j6:%Dh^Ӫ(HMhWԓB&7ϟN_Ji-FKG"V})ݻS˳W"kO_=JDDk ˂Y3 4Ⳃv#D5hQ+/Qe3g- lJ @ h0Ed$yi8Ȁ dOo쑗'`/7 g[};Lzdϗ"5hXy/O-6׃9*Ϙq }=h,G+7NGq@)2 =v^xr[HܵJ/F@HmJG0Kf`^ckA˘K̝H)x)HcF|p bAHٜ6ݛ4 y-Dmd&Ƚ @2!hɓv,[>H` ~9IJ^2I;vyH$< 2^Ðb00ASŋㆀ֝3޼PS UOq)bi&ps-V*/DrBPuDG&q` $2z yϼ'\sC 9"CY.!kn䗉|<=yI -]ŏ3͜<3,EZ6÷hx~G ;bgTwd ?~`ltx?fȄkI0=h 7)c h PeH!۔wbDw<]@?wD%" ~shI!mLz _xK>ƴ<11%(HD`"YO<+JIx c+uLSn8AFy/XaL7 ސtwh^ D*eƋ6/MXB $?|pE`U$R1l"\2@Py%\ אʽ|u&}-E'VTzQ'zC2Ch,)Rټ[eQ9I/4)3 ij>(/h&yuʂA@!Z!i OJvg+Y" Ac@;&Mh{GJ˓ZVJ Z=dLHqE(w&i󼉛Iy1,m) H"k)I~i2k uE ZES"I6A ܇ٟ>1"I@G9J$7'h^1G &WڛL!Y78@b?lV{/=Xeg >"ǰism`GC7g;)Egg`f%h4wBoaBY$h/tT15ue#-yq42Hcɹy*K_F ^3 soH?4Ƌ5bo"6Zk|WymJ Uh ( aNDDL=="*&I*$+i?9331A2d2ՙp&[ɋv&LV܋Z:4C\R@ =cYK&҉3Md4_> j"ڀD8CB>9Nec,&h^xA<=g |Qel.ӄ,eACvu EDN2u\?D@m,Z AFiYf~2L g xq3E󂆈$.1t [(тOP8G imɤ)m4Ֆ`B;$HD|Ĭ^Hhl$TxѪ!@R p2_o7!띂QCi$?s9+ bI$:--+_(]1M(݊$a= g 乄1fwǔzBP0D >{ѷY f!h=@32hmڣ" XZhp3wqA+tphN1bIT$'ALCx||Mgnv؂Z̡|ڡ,x~xxo B#3@\&lMeTz@! [4F U!<.`债-3&`sкA@V:A#hv|֓A'tHX&m $hy>s`3!O)?ݟS?9c $S@ h_95Njʋ-%?Pɭ%h!|NrM. h_!<\ g~" ڳ׵tк[6s$h0(YL ?*'h7;VB'V@ۃgS{3fKZ $i)̠$QurpQ/iۓe`er4V7L "`k ٬y:I|a+ K/$҆qZT.ɟv%ІR7W&r 'P&A} O_\Jyk<\pI[;2XJ`+i,AM $̬gn83dH -x(Dc*oςttLld=4= fI vLS_ucm9 } vzW961 06V3"UD)1ڦS}6J `&`k#u`ڢ&Q)- 0 s̥"/y0JoיOOܛ)cnB0T=Pm.,Bu5Gs̟D kUnG! B@thB@! @m-M! B D@HB@! jhmTnB@! ] @:-B@!P[D@krB@! .i! B@" Z[<B@! @vN ! B@܄B@! @@ tZ! BO&B@!"]B@! E@x*7! @|n壛͌h3&o&'tI5n˜cYxщo83Dm !f衇guVp ?O}OFi曇{!Ũnpꩧd% -Pzȱ %na*E]T:֎hkj)@\s5am OweOo¬OҮ;׼Q2J+ðV[#BZUm#vs GuT8S;P<w܀vyu .l\wwZ l{7xa-h%B@ҀVB%8C]wW9#1asԀE]4`zLfmpW_5 7^Xa€-馛 /&[~Ûon喰>K/N;f!L34a} |K~RK |zk:_~5SL1Egq*>໹馛{oE7xcoo?i4O~I}v6e g:¾#\pxC~·~Zd.fq0(y7PzɓO>XaeȖyᇇ 7Ы;|0P;|g lc[b-zzUY !P%"Uۄ(D ͥp@x'škY_ti8#K/t _|E/ UW]5[j.f}v/^pr8ꨣ:hG/I¾~[lk? {챇{{=(/NtK= ??p@/뾿+)}f!d @/M *F|ǫSCaa+x≅7hZҹ6,ZM4b)ȄaӌD#c#b~~9,c1O{뭷<4^4FCFش~i+g%ZO4i뮻y#fYOcKo5#8b4MO;c߾}iEK\ve~RZv駟M.oD*5(30o)vTGF#ܞ|^֟ _/҃=C/! B4=A's9j hv"Y3/{A駟>UcG[I~%M'd2=A0ml΄!s`viNG ƘBhPɫҵ5NʇEz6Qg\ ,6Qᄏ5Ĕ2X`~"nڏٟ%-r]HO@?x_:+آ{ijZy睁tdmGq-DgU Q&,%BymPM@S#@_!0z0mn6f4ֳIY JF]o:#R4yGWWr+Gwu}< -mg,¨\(!&YIhkUZW !Lz B@! hCVaB@! "zB@! hCVaB@! "zB@! hCVaB@! "zB@! hCVaB@! "zB@! hCVaB@! "zB@! hCVaB@! "zB@! hCVaB@! }ABছn vX°0,y7awxgÍ7>0裇˯uY,j=XxÛoyN;af 10c#?L0AmPCuQw~f5SL1E80ч~>0䓇I&k⅀BmF}_pW:N |p-"2(_~!?x__O?91GuorJ'/<{᪫rB߆i& aaV}Wag }]o$c {G-GҶaZ\p;~ 'Љ5d/ OWIu B@T*[whf4cn|r,{~qǍ}pH8ִt#ѴѴfd83(]C\wuESG#q+ r-^#~i;2rE#~.xRxƽu]Ug4-t\lQGmڞJןz}&/&&=U5+Z_|LJT@+пDYg8;UiE;;st8\&6>״qfrB14%O?};SO]"Ӧsw\4歷iz[-&e~'M9k4q\iws( paPi=^3ȡyqnOncs'xb44MnQB@tV (i?.b7M3vy1[Y,zO%_PM1cG矀(i#9g#aUVq35|qo裏J5 Czr\ ]tk;ރhn>x6瞀ˆMd…^DTU6g|(7|soͨJC?'yӆ~8<4jW^KŗId%mIK!%@WZitڷ+9=sku`NLA?Cwyg'/>;Oku?U}I5; bYR{b$IAo <+s^tMW_ݬUk{'f_u-T !  6~ pz`OkMFgCyB0QV~⋇#<#!H\B$FHy@2.ž"&u)^ ,6N@AY^3s`>@+ oTV/RÊ h!YG8s6C5K.$ns LYabI*!kf}B@4 T@+}h^w|:Y|tuYB UdM<)'ʱe"WS?Z3 ֈWtI=zyf:-S'pBh?=xz⯊+JAZ;(kmQ$#^'spLkd&e2&{05\S꧔?hh߯Iak+7x\[5! ZVպ)lGE4VSZZQ5W*iy뭷t|gҲaGN,4᳂i#'t墲TrKJ2I/u:8㸆ւ_V뮻RL CӴ{50ӳV;>$X~jv*];$F+iEPl1O5TY&?tZ#W W:g5@imw4Զ CzllfGB+&V^z j=L={KoܬSO9ț^\|KY3y"L0o&M3wH*hRaT|i!hS}뭷<;7S&~ 9A,.R!oL¯!C*Չ{7pC8蠃oX '۔7q/uf; 3>U&QkFdRgHi3]ځkj~k'3m08D!PwZSnv<ۼy˹61zy,n7 p̔ikKMn67MZ%x≸G{G2g]jhZZOÄٝeL[M 1W+V[m>G#ڵ%7a=O0fs}Y)jeaMV#njY}%w|W=ih&2?TڽF͛YHlRhE&@ ! &x3*hvJ6h#/{b%>u+->Yd|t?Q/_E7-,OyPP6Bx'Ipיi[(Bl!&Yfȹ$>h4M:!Znw暑2KWO5:l.e|j}ߘ &ii$ʀFm-&0u|Ӿ{>M3-za,ݟS`v2n3y6|=QBW!  r%g=!L9h !'5{|B=h,"tl$Mg^vqh]KXtS]wa׀i/`?93>_^@̿ԏY(ZRHSj3y!K=!&P!/-9e8SJew.Vh9=ur}饗VU5a#iX'δ Doy9SCբ nI N4zMnnWz7\"@>MC+PHQ4˖/e!gSy6-C8S?i Vܡ=L^OlrDÔ4ZDod' LO?t8ooҖbEIcH"D&M:|K>|y)ik! Ų@t?铠hF1 dO &IDAT y҉6.EYC:zE+ء5l^sIH&1Y?YYm ۀOz}Hx[̏[Tz(_!Ўc6x!wV/'-hSvv?!׆0Mrl3U=VRK/Ȥ̿1aybF Nb- Mb&Q[o}Cm~/KPER''@w}yOAt('> hX .?L|v3! t/)fT99bI!D9.TH/|3z^t/# Z=vMg-46 W.E}ו`@hmlD6 n4$mO r9gR_@φ泯ؖr tŘm)Smg&j4~>ؐ >Zj,:$%Hlʼnh_*:4! *Dal; ߺf=L@5muXONӎ]_ 4*Y*oz:h7-bnJ  G`hRPʴCiԑiEӄ3}ՂJwXO=igy|% ߠgTd>_1Sx,:IX%X4HkX}F|Ytm[p`"!|k4π{|Jm}/q(ї,5ya-W>G +Z<Wc"!^"@#P!e9]MB 5Z5ա ,%圴)ž%XNv/첹^!&fFegf>9L/Cx>_IGKBJ~qF,blMT0.rᛌ7 d`Y~(mHqIw $-)2~6IF N%|ꏿDh aշW=JBXFg(2gϧ}́i?MlzzYg)- xjk2C+O"1Qwy^O#fPADS+9b 1s <,c9'J~:;f=R";+ vɖ<{Gdy+Ӥ׬:"385`JEyu7B5cGZohwB7! tp%_1-1[dV$Yl̚гQ_mJ\yiOI5`̀؈6G#S|`,Wւz:ULc/_g B[h u瓖] R TғR5|4+E*.ϛٿTt!Psͦ'z-l!vf/5|OIW>Z+I ZeHן?>ϛK#᪫&u{Ӓk Js5M]+̅13]UA^*C/G[. _ì@]B ǖ^63t[ȿmp{|BdyBٷ7b]D$sxkAԼ )K,y7f%bک`&S!!i}ɩG+20e|-(*ͤ.f2Xk@hBJ4`k^p 5l0G+,d4ϫf//)mm $J0BZHq"]i40'dy-=y4`ZI'daAn#1-Bb$ؿM7|k2/ʫiOu`t^! BrZTҕ-3Qj|IB@D@AHٯjB@! ЦUL! 퉀h{Z%B@E@iFB@! @{" ڞV ! BimڮQńB@!ОgUB@! hZD@kT1! B@'"ٯjB@! ЦUL! 퉀h{Z%B@E@iFB@! @{" ڞV ! BimڮQńB@!ОgUB@! hZD@kT1! B@'"ٯjB@! ЦUL! 퉀h{Z%B@E@iFB@! @{" ڞV ! BimڮQńB@!ОgUB@! hZD@kT1! B@'"ٯjB@! ЦUL! 퉀h{Z%B@E@iFB@! @{" ڞV ! BimڮQńB@!ОgUB@! hZ6m͚bG0`@x皪wKTSMTReFO> c9faXW4 }]c4uREF1/N:i늦B`РAaUV :jSի*6Su "ZK׉gqF袋B߾^xVZa #4RkUr:r$Z{裏/ZWm[oJKhA`v 3=B@! @oE@-B@B@WB@! ֞WB@!C*V! jB@! z^ ! B" [{^B@! @!{zwX`ٿPrRkԚƧ8?0Ӷfzq|M0 ӋQh~B@! h)doReB@!~B@! h)D@[TY! B@>"߇jB@! Z Ж.UV! hZ B@B@KB@! @# }! BmReB@!~B@! h)D@[e{5R}Ք{GZ3SJCZI6[ V%T@W{_UMB@[5^x!a1SN9e8C?k04ӄ13jo>]P;_=,aQG #8b{ý[J).o_~ SO=u~,K/8;;o0`@|>>5Xa'{GOOjJhW2Ji’K.?%M$-̢θzEƋ.(1rH-ay8f-Vt6To6AN8a4h3⊱o߾gMŔKtcSEn,Kz(;O/bvmJ$Loī*N;&nQzf=89TSk&u]qyOzK#!5#$4i>eKa#3\`>0xҴS?+G}`J`Ro}IҼ6o V3u`&]*wy'38NBͼl@5fm: ?tvno\)ײ{Cؘ.dꫯB>, +#OB:#PXd / rKu]\sX`:XwݹY636%[*?/&$AL"%SM+D0q=l7ϟqs}/<+u ~o$+yI0gŌI7O5}]o›oЎJ@cOuꫯaV U# h5F @ C=t1tHO\y"/u\l50ZRxF.{ZuB/6,ya&S͔mg{C^:.̇C&.@dT3V|s9lZR_1Bkտ*jg&Ugg@f|]&`~+wO6wj-_K}M紭/vX '_յk(ַ%+w~oH~LReF%|\pAwH1"|ujJ 4r-W\\sM@ir 2d 1o6OSL1Ea R)i^x{X[7j6m饗|ahgնbʭSYa"} f4LMcc*cA.7GB@@oG suYf`)I&D@&rUZl\ u»8\ `ů) Ģ.#Zm!D>Ae淃 VѤɑH 1+٘Ǣj5%}9WR+ 6#,k]AYJhTrw}h'W>jJ[" E/!E#i|dWI"蛷o kf-E3'-2ڌt-bRi<-Zjhژy92eLKJp*L:餱>maXPKddazџýtS6!+_KHcFճNtdٸ">+>k ToJ#N8&(i#CTa\_-/:D!Fڱ>sS<~FD\XD@JB@! @G@AHB@! h,"[ ! B#  B@" XUB@! z="B@! h,"[ ! B#  B@" XUB@! z="B@! h,"[ ! B#  B@" XUB@! z="B@! h,"[ ! B#  B@" XUB@! z="B@! h,"[ ! B#  B@" XUB@! z="B@! h,"[ ! B#  B@" XUB@! z="B@;?c۽j-h u*zB>}JC 5T}s9眶{vޮ7yV;0SF-L2$]c5w}׭|uBn_!P/fapx1?뮻.lV[֫;DM6$2(^5\v۰FU]*DZ&pp:+7|_}P5Qn!حtB 0Ch!h~+믿~H#TUVZi^x) /0l-XJV!Pdo(*L")~Yf g裏\0`ƞxC~·~pI'g5;o;袋nc ]tQk8 œsY[o-]OF,2&0l Vgn:'>{8Ky|S 7u]~oe]ش~{{/\ W^mY|о/2%u^ve8}>p>QǔG#΋SO=u4qgN:i<\s}~M> Tm}dXV@jK'<+2F3ӉݡZjdka~x)[/?R@@Mͬ3t}Lq7.gg5׌)4(uQki?dw/].c=%\YJi(~Ly31r-W:.ځ 1~/u4ׅxFH| {.%Efof<Ӣi}?]`QqrLZQT]O[! z2ۨ,B9 Pfu?jvmW_#n`5$/b믿i$So σiKFJ;HaРA)N>>3<Z;R>YJ"·~O?y"o(c9f2*{I`/Pd7 ~{aC9$,"KbZ|Ž gKOB*|T]Lt @@@!H!КH;nmH_:$;+8駟Sؿw!Qdm_:O:Yva43{^G}t; /GO0 i;ku_PDY ؕ@)?K/ SN9%q7Eu _V @C96LO]BO =!hGqZ٭M~AN+!&/M0Mw p9ލgpif• Anj 5|c]ȣ_]Eq)-]W#_[.e~'@g@*HU%R&ț/`{%0ufi{KIJRB) 3ݧedL@.{K+y(ey}ʴ_UB̂vp[kNi,?<<*s~u~~KsMlH0fjȵ~||tЗ- RS<W.]{#8+WL1*ôsP-!OpN$=Uuz+ɩOT{z1tNuɣS%k7g׬y|_d2T{Qe|Ƭ ///}Kf}jgϱ|e89OB?W3Le}HԠ5lF2UN^ ϯ~ONGalwm)ogϹsv35eծ׿nO_@zߡ+ @@.V RJYO$, 1{>;'!Y&LI$h8v3 ,|]~Y.F* '@?N @ 6 @j%u @ 6 @=@ 0T@:` @P @PPn @@ @CC F u @ 6 @=@ 0T@:` @P @PPn @@ @CC F u @  ;ï󜴗IENDB`MatchIt/inst/figures/README-unnamed-chunk-2-1.png0000644000176200001440000015601713712625277020745 0ustar liggesusersPNG  IHDRz4iCCPkCGColorSpaceGenericRGB8U]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_@IDATxx+vuSfffffffnoᖙn񖙙ᔙKd88ΫIlk4fiy%,!P9*FD@D@D@D@" P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J@Gq0 P" " " " =J``D/Zk r0#g=l6aam> 묳N` &lxK/gS>pǴ` ab._~lAQG54Hz1l= p@u3OSn6Gs̰j[xՋ믿N:/W^y%0a'kF|c{ի,#"P@ "P> Qտ&(3&w}7?g}ms?p6le뭷^doq^{衇:jM?fO6Cgof~Z=>eQrfb-V&^ڤ-_lO*z[3UlgYSC: hahF`'K-T0a~裏~;{k?fEtW慄fjV<6|cSO=u42Kcc vX]=oo&֧TkG0x\Yfhm)NT龨tumf.8aUV ?R{.ZAPh kႅqέ*6Ux~mf5쯿*Mj]@̻# (u,k%ʦZӱ<0gZ@k9NZΡ~%y4y7sUW;Z@]ap K?Tk?jS‡~:oC9'|2?6 39~ꫯ2ڡ&"ʻÌ@@Ѽ@  PN7h|1_b<8nw%]~٤N?c5Vfiy;5SNy<_;̦rʘLr-.Rjf3?k#Dw51cuߟ}%cj6C30dc=v.l*2?8~'f{(+rSAl2mK裏j>3B_,V"=e#d)lyb)!BظĪX%,~Bgʤ.sN̗Yn.+'1n3r<ϴ=YL=ؼΛaA3{sRц1e,Ĵ+aOy^te]%}=y3x,E렧Wdz^JyI? A3X\Ҷ{DH:jjmv5 ^/+"$$@!T'P=_/!nxtAQPV? S(b7 Í)}Bg(+18s.ȃۯ٧Sd |XΖy 7ke_|]ir_ߎp 'jEw P< ^ )+[ӈ m[Ŵa[Җ]tQ'ļe+GLwGnx?h9#Vf^8l[xygx[׋ c|gګZ3~v}1w5 oG" }D^><+؃2q6ոi Lۜ3BuVm>ն|0_4e{ޤ٫س1 h&b4."j`Sq5{Ȳ뮋{O/- fq;D]~֎>;jVM z$buE@3 ر1B(g>}-ِ}l* `*o^$63N;/DoDy9ce'mlux93Z310_˰V[E 3<3`5#Yiv-]Wg4آ#6?Tm1SmJuE!okj%sK_JӔNe3؆Nl:=Bfm7?tӵ촕'7--L2I_ozGag+-Z#\i7saX[og5#1!`S/O+cSg}vWjԃGjL#`[ ڝlo>ke6]z<*& hʿ% BÅ^lm`J%&۲u:묁D^s~dqS.Ok&t:j{v H˱_Q˷h@gxU,֫(@w`-bjt68dA`-4ޜt8"M%p *dY8O&Rc,X?+'7s+up oc]4 ЦjUFD@D@D@Zh뷱PD@D@D@hS5*#" " " O@XW(" " " ME@C' m+" T͡ʈ@m6@SmPeD@D@D@D H~ E@D@D@DH6Us2" " " "$@[u" " " "T$@9Th}ƺBh*M> oc]4 ЦjUFD@D@D@Zh뷱PD@D@D@M'80H#Z" " " N`衇7|saRdvM\9#zMFG,HxL3Դ/ hfaG s=wsi" " " "PSN9e3s3Pe'" " " "Phu>:*" " " "PguD@D@D@D@GGE ,ba+? 0 }ݥ{:zb١c9}g:ve[o`Wo*;q_~e gw2O,-fm :jJ2J8ꨣ!O7"  ' p*@D0zY5g9\ve+L9䐆c- W]uU˩j[/0 UZ(GZ[],}w܀/n6q~w(6Z;S*V @" 0}1VȥZ*?޾.03oiz0ӇN8!?7 Zxɳ6[ rη;;蠃FmDt sNXxc6`@~װvzL6da=l'd?0묳rsθysB '0pz?tMay O>yb/RXr%SL{)QLK38ae ZL;/,>O>{m+Xbxeg{^}Q1辨~m }''2K`}e ^{!^tE'|2,B'd&o&?_oD@G@ql.F|5Xc Cy#<2laVX;ϊ+%pa7|3 VytI~8E=3BVZ)#׃8nxxyyk|TSM$"!uVguVx󢮽ڰ*D1N:ip~ %m?Gj<Ѕ+\s5FieEʽF]} >h~/}UV~)a6{ﻮI牀tMK(tYGy gh.hf2 ?ӿ1RtMgi̲rM0]fw͏1Ͳ?50fa29KF(Ed&2`VlW*efQnfV/Lf&Z4_u6pef]㪽1+]{Y?fW~U3z<\}11,Yp|5Ә/~6̬yo?q{zCC 5T<ޏmNH> ?;n![d`(4?ƖE@>+†/a"/=T}K2 @u]1i%1,itNb$< b7p?vD"ywnWw衇"YpuW=#Xi0?BqRnXV/qps,=gm,_.MMլ~^s5H?p ;3| ?T7!>SvL¨hcz֊?H"a{W<C_Oc==mN%*O3;*ǙZ'_y[-i+\8>S]z饹_(!=uQ-yۯ9잀ae__|Rm[î5w ^‚n " % X]?\|*B@f3Έn ' oQTڐrl=kaTK+;_͵mqWc@e]"έ%.L=fBa,ϷrKfFDtRR׮/}k/Z\$\Xù<⨪4q`W$,)₤W9t|At:2E_M<"jaQeWD(g6Ow : ⢶ZT:h634Sڬf%"c&h+X<eBX==S-PNY1lOGb)FWܺkYD|w" 4_E" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE" " " "  "  VD@D@D@Dh9Ŋ4h*[r\+" " " "  lE@D@D@D@ HsQ@H6(' ZE"  ?< 0 8T̵RC_~.RL{,4c9fr-KUeQQGwE@zhϱVI"Ч "~p=>iwE\uUam W$MH;5\g-r /[3?3;᭷ꬷzafe{G!0hr~@:8s뮻.,RmjuׇYf%8SO~p ')#FLcźɀf)0 P'tPh -P;5L0]wݵ",.袊ГO>ȇ{{oUy"  RZ*nGK'X}v~iC9d`_L3MXr%c>L+cA%ȴ>@>+bꫯqva7ߌky'tR9laum'; O‘Gȟ4XXbp)* ]v%_S#p>U $:=x|tx,Fkd̴MZL~bŲkwB_K^J#"8cE% qjQ";N]Y^{- ,1zqis8#1 "Vdn=_Tbi4  NLyPFt_vZLZ=80E&LwfhǬ~7ϲ+.\{eҾE4}VwqGt=mBP\<" =K@>=[@'0TSL,FatM&|vŢ"V"X }* P,d87(HӴ,A|ٴ~q9iꘔA6-wdilwBTo%u╅I4u?Џ?8Z7JQQW;^oᆊSC~q ӢL1#>݂Bí[[̏  fk IaqyA`U;PC-E3KwyQ.̰aM_+Fk b/=mZ_Db{(erw걒w5!_qe`&"p^" " A@9A>E "ӦXZ^dsEV?%lSD`yml O+"Јl͢o V+[1Տ8B8?qK--Ʊ<VGK~ğ⊀\;Dت6t_-jn뭷_~y9{]1`@r,nŬt~= ᏿0x۝o|uLDtXjƖsg$}kD[RhX;وK_3,,j~Nμ!VJDLDB` /d,wJWu# ׆nQGeVX#}q!YGutb>Em"  xmOo߬5TD@4yv,)6kzϾ?W×guJQZ8瑦⓼jaDjWWK(ez~Zv#)4M7_F" " " "$@[yuq" " " "|$@MT#hi-ݼ8h>&4 Жn^]4 kHD@D@D@ZhK7.ND@D@D@hj$" " " -M@W'" " " G@D5& ͫ# |m@Kmʼn@|UR?#~ SN9efaz䲳, 0GS!" "~@S}x+o& hk?G}^{/oԃ>&|+[/ ; .o!%aF  #8b8cbYTQ" "PW}Yu]s\ppG] ?sx뭷?'|2묳Ns=ÛooM79p O?to(F}0l:u6zZyO>+:#̾VH:VYe0Ç:aOoFx饗+SO=5h_8a{ng'xbN M4Q}L@+ Dwq^? |M믿s=g&C9$xKOlHc~p '>8wC9ddz:+\zEs1iB -ԋ/xd| 7y7M6dϤ]ї_~9L343 5X# 12> f}7bwߍ"t)3NXuUcÆn8a9 뮻n86xXc=6WwW_}^x>7߼}ڝtn^ l=#]8yNϙ=мR֑ĸ/4?]@lYG-3Ϝu">lMsj8c^~= W^9i:<7jFyfJq}h]fʶvl6l@u/ Wv5d?~fB#dI:{ꩧilp0x㍗,G>LGf}4f`:3xvLg6} l뭷vqo[lDRf3B[lj 6RX7p` _i s'lX 3 }7le0O=<<b>:j8  ㌾g웝<̓]|1 5y"3nlk o&pf8?ƵT6c&"jzAԃ}8qc1F^w %6ۼdz @<3z::":zD?Si|aX`ҁx0؉qU+%"oA v)5◎.yh6S̊2,!袋fwuW3Ue]vY3d34Sl{\yQ E6+wᡇGDwwĸk6~Fl4~&_D*ϲpgz"Φ~lN:( ui1hР젃6tݵY6gt̗|+oٴr|#D\we.? :S;.rA|fi(@=l@fGL>ymv)r-3fmYV[E1IAJzìs̑[q kVXtMoC.l,%?e@BYo?m C Uh;hl@;+zצc)7t<\p7glA̻I l+p't 1-(Kg7 3DA#bZ>zSO;!:5<>X{>Hc>+Md)X:?TgvEź_uUR>U{MkF+Po\3s=7)̦6c=zX Uq/+R2G]w] `hy!`?\W1xqK|??D o@ ;lv Z⊘735w(gnb`x≣O>apbk9qμx=5 ~&Φphfp5W Vq/l"m{W<A[(V^x̯(8?2Ni:/UfyWywX:?zØW.9S[1:e:$Pr1 , hy(4*`1p%R &N(z2`5škH]z) k磏>ZӧEZi:G^lg)" WAش?"[7QR`f1+ +Bg/iL#Ȩ<9 6yEqŴ3W_}g_(`J&n=X {Ve"L{D-Ʌb`& itΛuYc>\ǒK.6$hOGyXGjJ̧sq #+SBQ<3"O\p<-Wԙgy㕾"F1V g)yqϐK/)Bx&^j(a=`\bH{T:)<4 ; =_{%h`B[Af_4(s=yZs^iwH3OP|ϣ!SS|L}!hၛ1x.#sN CDž_'")ٲcݍcw:HIGe]NÎ~Y=n]Ð@IDATXA;4 ,.kIK1["`iqq'3 I`ud0xrD߲ne%`:| LKe@]uc%G$)~ZP[^>#q. 8溰"<8:R駟qQ|Dɗ&>,ӎ^F#N=[I`.pbsWx^if=0j" x8㌘q# ģ&8EQ j:ZEܣ`qC SC[{ 0kA[uk b/bN"b1zYoSL{aZqLQS0<`2mUK)5Pfd}:e:ֲ@9L34,i`jezZ򆰦aF Kvi8-X,p߷q70_Y(ο" 7e?,J@{*| ; #+kZmϔ;v@0-LI@H ⨫E[>z\A:bJ`QOXݢbpt*8|3b5 !S(ڋ{u-"D}-yW,#8Hs.- q;|] ҅E3WW aCg~~s|ow~|f @[p7>30HӤq?J2& 5<+s5H6kԡ^"@AJ O1E Z|ѢwXXx!T,2( ,t(>MGXR)?,g)+tG0y~\kæ3b>#:< F- ڄ)(oϑQ.cy\?@W8(Fϴ{zm,bڟܟ nw܏ڄ.VHlK^5RlŹXmlp* g5 4[\cC1(j`jo #eяZ& sb;L_@@L VOXr/Apw=H}b`F|V`"!yFԹ 5\9o?" ]`i^H"ū8px ׁ:_!ܩJ=X0挈_b@%0F& 8|.mO85;'oO !B; ʑi! (LSGsS e.Kq ,h5L|h_6:_: (x JѬC_8>#R2([, auΕ0Arw2gG> : 4݇i,mDZyle>+CYb~<A½6^Dm=;+]MnD40A@3Mw5u,X4}[] .,i\NE#f|F,ǹ>cB1Vv#bSшAaI;e: P+}b,~wI!qL.QE'cT~ zYw8GǙvu+xHwKk>e@Cb[>|wyը8 Fm|5S|:DGȐXg<`-A(x^I?bAQ-NJ>3C:6:+tL{a)M7V l " 1G]bѣt߻~O^Rm"Ժ Ҁjxzi: nYFgޙ[!.uVʹmUZJ>,Dp xK܃L9"pc) zeщ(}]b`wK=LLM^~=J-²!x \Y@Q ڀ,q.{"HK N裉b=b-,aE#DAHE!2Y{gMڍ0&YD'iC.}ug0M9]>VYq kϗA=GylN<`'{9Wx-̹ԓ=dyS&beү"zmi;w?M_8Y!\O|ad3 ԏ7,)`(|/:s8 О" ' ^۸f::ٲt|<K?D::bcL)1*F`}CWhy0Ĕh82:q 'FiEϷޯn:x+!`}Ϯu},WK,VcDWS_ .$_W#6AC,@e{2=WD4|g  ?} XZ?s/# >C_Qf.C0H$O#"[}8M?џϢ\!o5-%=|]%"g|!fY .qLIiLw#ɛtuq3#'# YζLE9~gŹJ]GLo)2M3ŭtf@"17ɍ#Lf]n)6h ”>/>?ӚhS4Cc*h-B~#[uqq:uF<a3m5/= y< LHirya APt2L?RG1<y6=Sm>0|f /o(b;c}! _| VJףvܓngzBO˭YX_Ff +N迂ªI(!BXLg},1E}K!\Z⩘7uՐ̈́w!$ \RwV &?3r2ricԅ{aiOɹ>KwˏzymCz1s{!e1N{"mˤ} UҺX%-а{\?i'!GB]F3`\/xc\oѠ8&DtD[> wR&}["xŊw {pJ֔eWAJhrɟ Ba{H۫q>VP|(bGh0~4Xb[$.$2ೋ={=x|SO|[ŞN"MN$} ?zxEPSx<‘F|9'kz~W оJ]hepJq d:$w4t"l#%2S1+14yyß S|:b@Puq@A{-(+!+"ЭC|[3t=a[H3}(^>=}U шLZpKE]< e"Z=(Fr~=A‚ 3Ha 'SlD :f "່%bA=xw6?s( X>~- X`03|snq+,~b B<׃[wD\""b\C>2e&B᏾ O_zZ^3ý K\hDŀՙc α|},qEWEJ+@zrꟙABg31HӸfCqb(<>ǰ3x%w<@R_o'!t19~}"eA(&ݲOv֒ٲ+ژwwl 5EJy!`^C0uM9ífSXRk&b/s#8NӾ^̗sɃ-->^ X5"@S am^eыc4 Rԁ:DZ3VS~瞴3W²J:#/eezMxґ[ a b<|pbp!)gsqQ*­-ƥ3CJwvt\Xc.ȃ03Y/ ˮ4-ڄ#>u. CI>0f&ԋgX"=0M#gq{?zË|{b 0;C@2h7Gbh i\&t:|YCצg -G)ΔQ_j@k) ŀ$<*a<|1=Eԗ"D]DzVT 3!Jy icUFѿO]~KX!YPG^  @dFU. 52 9n c\-8nYCŀ0eBe#kB¡.Zo(1J塅}U:?mV6`&'EFĵF F Yý7e_>Pd1 YePY`3(>D@@1^-pQ˄4vbIJimr\z5d.VRtP}qd2MFa$η s?oE)A:ڌσ%S'B̖Yr}aD1YιN>7knNS7*ibYh ѸX\)Vѧ>qկ'_E'e碣@ .VXg?—.zCϱaqehqe`A@aLcD,ug¨((fXǂ1,s-oFbKCIr~A3ulSe;VO#(l~Fe!Yij}Nچ<#u#9ab W_X?=? t d IP&5 " - 65̿: &m'G C`#p0f0W`ۦ*yYۂ`sM/u~<&n4ؖ[{"/,~5'/v Lx f) f x)ЊTlAS0`JI `#+sU }[B۽ g0kk`b7 &NB `{h&S:i}ҬW.Ŗ"8&"Ч ؂`` K-VԦ{AfS 6]z`@U'e4`+pY`]ΫND`#Dl(7rh -ܸ4DnU]D@D@D@DH{," " " hlv] cE@D@D@D_ͮ# {UKu" " " "{$@{J~I@_6.ZD@D@D@zhW" " " "/ HfE@=*YD@D@D@% ~h=^%@$ /]-" " " G@ثd~D/y?b]T& Zt, 6xҳ?04|Ai/2\xeJXƻ[z;]w] ur-4Ls0Xc/viU2T΍7* \7m|'nmuQKUb-l͖~C=" Z ßٮ>_}wꪫ:J4ǿRK;'LYdvۅuk_coŠ+ow[oI^D lkѥ4 ?<-XZ]s5agO=T> Zj7x#(lV_#8b~oz7hce?% h붭Lzs=w`: A"LW_}4:•q',^vap})Y'|ǰ;))"Qܥ#Xb0#:aMK/\rXIr饗6ZXig}xpΊu=*^B -Fi@=c؝w~0Ŀ]w5x]xay@&Dw1#^;3ץo9䐰>.,1(eY&{yQvX` ?ۉtPpa SC 2dygOQ0yG;E{Ogy#b) [ou}q /pg{.l馑 BwqDžN:)&)c+uF!_< dᩧE;.z"4.ڈi/<裁@W^y% 9amX= w=3qDyG~^E@=@;#<҅3u6 ,j̡f&4 6KcvQGeW\qEA;]tEa$zC$@Pc"0}bl]Ʒ~]w8-ChezWx,h#OcU[|Rq e1Sˆf6Bĥjaf_(?&u)˓?04 =(hD C0 ?itQYX2Y퍟g#.'6-`DY-Xa֢ (5,ſC=4.\)`~Z|Ȳ)fY➠*PbҵWJ~S|vchZoJr=f\rI\tO>9M" }hi(US"i6N$eWXy%^ĥ.'2]ewȏ<"; ǚɎMҫ\spGV+ɛDX 3 \̦*/2*KG+F0 3R4M7n ,ݥDroo,,8Ê. 믿~S[Id(&" D@+`D?K)qbt^B19L#׿NZqE5+f2jAUϪxV6~Ũ/ebdU.RJpV*Vvgu7Ql9 dZCQylׄޱ" Nv|5"ɖH_~y>{WYK7p(Xı"ɓl}Vu@# L5>Eixc/= AxAu]';}G[Y4BF9l3?s?~zNς&D_bX=vGlC~^W`!d׃Ea3[y`ۧc9&拈ʾ0FEF$g"Ţ0m2f+8[K_u֚7 AQ'M,bT~IAD`S/}ZzU>=Z&-B1Tx%Ew") L2MxGɗߥg/)C6*Z5>lcdROC_S)mJB⦲a烮70(%ʲmZWh bHf&g3$@qy(>I]EN8a6IjHWWNĨ,r<w) XT]FrYD t~uT" " " " -D@S"" " " }h_h%QD@D@D@Zh 5.ED@D@D@ оJ jL]}TGh!-Ԙ $@B+" " " "B$@[1u)" " " "HVRE@D@D@D[1u)" " " ~imtMfm0p$K_o۴4^D@D@DW :0묳;#| [lExi+R!Dc*W \tEa} G}tX{`Q^wuᮻjwbH6Qc*" "Po^{0CI&$ 9䐕6MNcSO}BTk„N[oK*|u/ 嗰袋v)|?-\hTu\r%a9;leQ².Eb=/nixgB%a}nɣG Hцkj[o wygv= +bXc5⃲y6*F믿~K/cF|?+Gy$ P^z#<2 ҷz(F;.ZܻiȦ `_Ip'O>o =X~N裏¡u]7uYvLoᆱio84ӄ'x{g}vXm,QU_uQjBl~WrЅ3uJJ:jw[,a{{I~}y]ٱ߿y^YgQfӚy|o{b̲K6#f?xoTGeG=|}Yj=̶zv񍌸3ĖaSTngO>y6xE]&\{wܚ|_tww JKKҒJ7 ]JKJw=>Μ8{Ϲ:gf֬{f뉵kh-鞒֬XpN@K:h$8N=qw%sN4Su:oI#1砃[ D|'z[Sfe]~9xz< `qȑy*,Bw'I6ZIpOa7QOiKxxqIhxG7|s%owN9h>M=5mc/<V-gc9:+>$>r;3ly'_~Db Ewqgq03ׅ5s5HɂiSM7Iqif}xqUV7cԎDu]4\̻F\j"d313EY$ZO_wi8tnm<+ŝZ+DRqS?{b!^`=C6E@k?TfpDfHG7bĈTNmiƆ +LM54- hX6dN[H')Bj^7ehz7pꩧF &ch1y7 &qK̶q=vzPlhK/&h;4x} wg!lIǾE;raԨQcdYuU&+dahwc4?:T9؜To֩.4hBy.g&Q}wRzn饗/QpAITvEz뭗>☈x38bVBkX`fJc9f.X>r袋K +5p8c{Lm9t ϫP4S?4C/+i+@BH"QADcϞ%L&+FiQx>,H'iJ6qp/fW 4t!<Xox>?H6i|S[+&k-"m!͸FCMNO_|ӳSM5U"Fs=7I S-2͟Gy}j:,.:07!,4` X 8kТ7Q1@ZfڤV;o{S|D+ /. ƊvaƤ߶M&p@G3' -ϻ; riID<.h!qwN>e~^-fY.X#C9$ON+2)f%Ϊde@[8F 6 iL+lo8oE@m22h1K,~nC,PLc>1)C>x" }ȩ^/O@Oy7SiKfE߸<`0f`vASڶLQ-kq_* 2m_d S<о&=^т^jG{Fp NY, 9LE⁉7[& 9ys-I.Ѣ+,+jih'zY_m94f1넍m1z/"KK} §r yF[ rJhl)>DcLݺ}'=A qf ĴN:)b H6wn74LgO ]#?\ϋ;/%ٿPT)ܷ IP -!K)@cSaLD^[k3}\sJjX!BJD[reȗBzs*w&2nL4~ <_]v%}h/cQcW ͳo</nptxIӺJ+wJL2dEigiE?%6]x7="|'y]p$sq,4~1L )Ӹd3?ZGL 6b~U<ϸ"@rYQ 06km0( >|tac`Z鍊Xp Y0cVhcˎpLrL(0Ae9FW%aM봅MK^7[P BȀ{7xS+>ܹu-P>}xh0_s}K>xKD0#WCŻW&0"Q+ %]袁5(2Dɵp=Zw//ONv2^aGGr2F1=|N:ęP,9䱟L/Ml3aq;L_N;"C >LxI b$t4hmk9fL0v')ZoRO"K6,ĽԆhm8ueD@t`pLMf(s~ã!syh}Mxt<~A,à6> B| ¹@ ?ѹ; V9v! j|}|A>>(ǀHT' bCVQ( yhcLY4:yzgr1b" ?ϝh죹'D jZiP: !7ӊtHqKbI4lu* *_I-j䏉A=c Qϕm!7 Řٺ/aD wtR7D[ixF2I&h #Qg KȪ ϖ>#/ف l_.oQna c0gPd0y?ڐAJ ̤1,!DQg0gD;POmTͽ@IDAT )uB"ٮU`2?Fs/-\x6"놀 TB >} Z.b۟rƇLy'e#mv A(?k4s\;||0մ+xo"C'5kR;})wLN?/naGj Q d~AlD&uʳɳyZadD-` =뾬8}o![X1j 8xaYaU,dZD@'Ch(P !>E}3/F3o̽1[2  Q$ڐI>:)OM;}|Ȏ:RFH1eI^ҺI~Dh9@ 3GDf( &\+%Dc +[`XVH]r1╛!U>*׊v661 B n&4`D>$X0,zs271d LLPZyL1Nu$˲u /̺ಒ@DGPߊ;EnJ"z̀kiFy: 0@B ںUF\@ h9+ Mx@?Xu_]6Zpm,h>?os33+ &AG˔m aA$aEYϜ1"0,;' كT!'qk̵x m&#S4 "Zmȶ 59{P]3 u7⋎ !~~/|w&~̞X # X4 r|`~ď37S|5ca߱ϟGiV  |ьV|)ˀ6@ 0v?_Mhr( ?\Vd+Q27lu?D6>ָ/>&XQXcIQ Z`FR0A]Q"@C0FI9}|ey LQECCo5G)YmćI;}M &d}?2W4g&OQ.]/[9X^ǽɐI*"ȅ&M D6)H>{| D[? < idb枛}i̶њG 6hYF'ay>m`dr( dmR@# 2Awou䡬ݔ 򁹟%O}Ni5h9׾/AKkpk3֌(]H|hR &=`|)t97"}g e0` a6Ee¬l,1A)mR= &3| b?dž鐿"q9i7v9+&rI{`aKt41LLDy^r.' (HZ?dBgSq@qML!U ܍C81׌_pE6wMl}\ hOzr=kWu^ B#~y0U,|s$! Z^]{P@gf0[f-A D2 P܃hh' C-ت' -(7 beq_F1s)?O0 A4\xDD:ʐ&l,uP?ş2۬ ~z\er2 X EJi59B^pzk"sj 5hk1W Hk_vuJ`/*n&rsj1E֒}'ܣdHcɭ|-a/E }LhYFy<&lkFXIeĸF4<(_><5Ѣ}pI/;AVvOj_䓼h9 t4.IQ0A`+B`h!`YG/'кH]MB+[~)P0`A23t02lͅ#;2Us0~0-{0`A%5r0`eYJ3LgFinD3[.l愑iT;lE@`AHy0pjHQ`7};-?Q18`? R-ҫ `Z`+*" S1! @ `?3l `Z`ccMѽM#L,3>0tfULo5`f`5Y@W0D4M"o D[X%%fJ? 8&h:@@ ۬N! PCVfN {f`!aC3'i?]U5?!k1`nR#+P3'ͧs(-`li`C@o4 bB@.BZ@&Y鼋.m]_cJ hm]Dq%k ؔ~^! hm8)B@DH[6#֍~(E'b_?l`!nuwuU!  @:l%8O_SN>}L=B@!د[CҀ6J ! B@4hB@! @c6J ! B@4hB@! @c6J ! B@4hB@! @c6J ! B@4hB@! @c6J ! B@4hB@! @c6J ! B@4hB@! @c6J ! Z^|_~U*D@;.Bxwh>Ҟa'/=o6x"W\^xZםg1'tR򗿄aI' :k` V[m~֛^q w\,?яv[o.=_6ۄ[rgqFX;B@t"qK! jDࢋ. o}oj,1{Z(\z ]r%׿w1r km6p=!  1Bou|OS8cN;TK,zv?iXm*61bD~!  i@~B+8F wdMV_}m]n /.駟_ai kf9rI.,,O5X#<W\o9Nqe]|k>W_}5O?tXy啓 {^+T~'I:l*ᥗ^-"L;L|78_v3,Rawul0SwnI&Iײ^{/O/È:Yf@gqư +w}7iqDM\r@[%sOg j<yrH|Svyp뭷⋇^{-|ᗿe W\1{ュBAD@NńD \;lᆕB?p" aKǫJCx7>.[o SN9%~򓟤,L#f!h6t#瞩O_|q,oO&p:sϥ1ưJ+mfX^C 6 I <@iF}뮻EY$C%r8'|ю_J]þ > zIwa=H{ugzsdrGq7?jԨ㏧Z+>gIFO)%BUwuW%UDtF [񨣎*\FrVmтoi+iF&RFRh1~W؈X:袋VʘF,=S)4^4FGFұi=S>Ӟ|RJ hxuץF+Ԧ4Ȧ4\UW]Uc8GLicM+Zs9rF*i>_F"1pkBjR/ko%vTGF#)yǎ>g0vG2{M7$6 , O䄁g0Mt4-Yf%rl<>裕4GweJj#DGS.n;b"uk-.ieOs1 b"yZ9¿}Xs[o] 9fW^y{݋2+ϩaKP_ג+$tH!}*^Rɏs/RS8+o 4k:yhgBt<A]$s9c^A>#ىhgLԮrwyQNȌm%Q[4M8hPf{#־!ӇvXl|"RDcʹK[@,*H|"ѠRWkkQ5B8i2Ki/M6$Eay8_Mgy}QALic饗Ng("1~m"Fey CX/H#<2-$ljF?%_k6ڌp-Y~Ic QYd?dKwID@;^'B `_!0z0mWJ>hF7#"ڕ}GGuT뮻V_}?L8a9 c5V)K/Yf%4Laѻoc ?xxSL\r0D^B@ GD@]NS[mR\0N6dqgFdY;wߍAoV4ӑ},v 7hh6,3]!r-ͺ.mm,BєD3t}.tuB$,6-/ӏʏ\sht ,LGMsW^y889}H4k@kiqhh)2Q~3Mpz{ '\Rb ej9%! BkvJ{7*:LEą^81mA5nn# _sOt_Li\pA%[wL_|[/]wy'M^7ePӬR#ފ;.ic瞛DNv|!s=fNKyw\Bd@iJN9唉ikf1GXcT/VLd>tM=A6H;LP`裏eD.X D{f H 'ͽ"̡5kpV\Je뮻.=Y=* ~tOx9tM+Bu~ꩧǸN;E$eL4[eByV NW_MJC@toy˝N@swI܉\[k^}y1~|B1 3lsbK~\ﴒ>TSULP균h~RW h:6d`JUю/7v[4뮻Fܧ~{ó {w&~e2g{'! Bk(!@z^eBЃ}{2ҒklLB_q'Z' S&|Zk`Zq-r׆/W?l'h[oTs{Hw@ԅx r->64}fF82+ I*ʘ;Ki|'L G##C8餓O4e׿LVQ^p7N+$iO]zgz p F>kʴ >d ڳF_ j9 CY?|",b#hIr0`iiLV$1פ` `.8.$!LB&+ llJ*;lA0+O:*S H@ ?;]j4͜eZz}Er}U5%S4 jfumQk4FP#Xe%~vqNiJpR.~q; (lVܷm;p̽0r4cL~etAq});m_\;mՄ(tO0>uQ0A;53Jq+o0MÀ4w4X N}ɔIklJ?w_t*wh30?^*-!n;kŲ%(TY&?O駟.8X2yZȋ ӷGw/˲S`(J^\$vTAH >LN@DArX:a}t~H0=LH_1?gNb_'n213PL}qLÑ1'c ms8nEN;DR>^ԏ韕 \ bɜ}> flL>br[񫃸sMmJ׍7hǭ*,F?oL$0DPe i˖EK#Ӟc:iaB  r{׃@0arg$ן2bz>gK'wgw4`h.0ܗ\pOdWc4eb?fb|qiǽG?Qcd<[rP'r>Pu`8{_Ǥy~># *8o^~}fO7J>|'x"# aPТDk ]4{2o)ʘ:}),4cm>QF%m;Z6= @\ܓ:?4-gmNve8ӧWn!hZ!y q?^L.HD_6&h%4 dѐB ~7?>RV H/2I;o6w; h9L.fZ4ކ[)Z֢l1&-H%)& ~$s["w I/ˤCM\4. 2"N;>f" :N'\4LL|\a>W#ֹɀa^= $m@9IC뀰N"s:N4nBKyX:DcGsiroO?=i0A> A#>ɏD# )Ә^ wANJ'/AM̖5&w"%.V Zg4e$s,$cun@pfW&?\ D  y e|:bC,}&@AVXs-z!v0nBM1ŀ`6H! l> ռEla皊K.$}Fibm0=hpmF|R[Zf&f0Αgy-$$?`ʽ@;1aRIv DFVnexʖ@Yw|eGLxvpG 8W}H'S4N 7މ@nimJ ׀:a XKPڥ} :J.hИ1eyă[pvLa3k9 ){F<\7WGߐ>03;)~JnDB:ZbcG[ Wڬuih9:>C뇌?_ V·kYY |!{K]@< H0 6?c! q.WN̏벥RxѾ펜9rd>7&rerP[V-M93V!gX=~g2?VeW /򈥣ϬJg1 tʟkּ?)ؽ|_[>h |M@Ab_|,mZB1ט %bޗ>abE :!6-)d4u_D }' `CCCCHfp]|xp ŵ\+{ԨQG~袋ҵpm&`/1^3'9Awa_tD6"[9DL μ?sØS|hkk/Nu/õe eAژE6ӄ_|\p@ \1Uꚦ2uWۢޖ g.8H$^c O|"U`A~4@0 -`{I@޾[hkpZ;2E󅆁 ;/'~+9f4yy |Cs2 i! Y@`78oGM*a`.'<n4h4'a6C64_|{j!~&|$,Uᙁ ˲4$\(,3P d++l}(^™ǟ٦s:[Ɨ%xsQ>梂 GQ8<瘼@r\sulVJ,1u^=2b&s|X֟grS;?~=6MQ3ly9<(t:8b0 6B(꿁W^ 4h'܌v6qM=LWDkEDnBCH $H-m!Ȱe`BV :L$3"&29NN.h3|yD a =}!AuQŇCcQ&w2!25N'V r6p-~I{0eˎyuEި{T \ӄV%p7ϸw:b@x.X e 1#8;d+Nr}b|uSgqe(7R縌c O-V9K=]! >RPU#GNr脩D$#X_15.@1Ӗ1m"G\>{g9'taW?\ RQ-&8!hM!ԛk!<Dd|Y1kAtm7)<| K`F3fe}g\ж^Q >8G&]1G\&`pUP/c{5:.% %]zƸE]L\ Ĥ/<D@|H B0~S@,2J:D&OΡ5 \uB1s6?j fs>Dou>Es}m>Sh!B&օ8U BB43/%;~*%km>Đ8Hq_u,嶪 q&@y檑uV-oTpů&sP-`ZcA4N`|h-e?&eA;O>Ncǟ d Մk&<ף,ųwgXNǭE@jN@%u<\Y!5{cvqN~!rLJMu, ښ̑?_I^iB_8E!:e"2.h|T*r-y睗.I   \#)E{/+lFb#b CIL9gD?,'e䤿r硏Lʖ<_a2YƧ1 2aפ i<؈4wk.24Hy b?U;"cu9*ȐH>'?t&BlQL223/n0x{:]2-$< 2餗 E<́,iTiĈbѪV1ྸ@Z:H` HF&X07-ul}]VX9X6R̳v!X`:s4McǬGLXh,$:c! :n!|`K#Nb>7t}~F >(C-V<@ &PM~&fN>Qz}dW1q.u/'̮i+E^0_c!@Sm[7a#}&IjZiV_Nܙ@ ĉQf:iNLLvد P F‚-?0&JaD/:F-) F`)ؒ>|$EJ pf u Sn>,'(3MfFS-5Ͱjz[$&1A9 fJB7r0 O*oZ`77N[_2i=f3X{0Єi'S}B```|u{`V4v&؄,/g0`f`R-,4Tmt+-\ EӢ@ w"x6"#4)?U"B@ >@@|ԃFȬޕfQ9.eV/B@! @^oB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!   z*+B@!P7"uCB@!  0f3s/"92<_>0stTԙxWOo3+G {o&L9'ux03Y9: ^x!a'~uRg}NNi_D@Ka?NgqFs΂/ =P`+.04ӄ &:>{[o%:s$?/O>:ކz*)Yb Q]v%,UvFhѤ3^  M6Y:FV\qpVXf8$k*o#=Xxã>V@ ; 34@T: jV! zuB@! $D@ x5+B@ኀpn! B@ "B@! pE@ty]B@!  A^ ! B`" :\N[! $[,+˝ nWԝwOԝ|sݝ0{O_Bk1 "uB@! *dۥ ! B{+B@! @W! UKB@! @# PW B@B@n:+BIIDAT@G@@! ]hW.uV! ݏhC]B@!  Ю]B@! ! B@t"]uHiKez#(~mʔVwm@r:hF5Ij Цkz(lfa& >{8k0\s'~4Zk8cx``:ZEwLj';ۖ2 v[{_>;iZ+}YiRK-oxĹ;ڄZ0|'cxE뮻..qg|P $a +S]E .)2~c#G=^ve'x"}JvZ@#{Gw_c~ap #Fz}w 1IK ߯曯q\`[lg'G,ŗ_~~W'RjJvZ@#c>N;ұ_)+i 5ynᆰjW^{o￿3& y7>\s54Fwp 'W:y&߯iu4`7<2hJIvXoz:}yguVhi*py饗4LSINhdtI)Yi;+>KE@sMLHf!wLy0TS%7nx7$FWUu]^z54;8<#Fs*o1ﭷ  .`Kšk޳C=4|7^-F]sKKO?=\qav -XXz[cU c6SXeۋ`6%ooɕ>WN$Z;ܷbf13n $ju}wnp-蕪7b cdL&幘q20˃'M{wO#Xi|wՁ^]|GXcNa_PcѣaL{}='B/ }˻9s=,0"38iF>[nefeLRl342y@wopg"%Gˢ.Ȧ!mH+@H{wZ-4@ofpU*jv<Wunz<媕~4r߼UXR_Z?mkha3+{'}IqbkdxJ?Ɨ(7^ZgeI~.Ri;042VZQRwya 7Lqh;-s`:ZZL-5A,xf+m %\@F-첥e84rڴUVY%i C[>k`;Dwǖ F \[492FocA6Gs! qS~9*od;\n򪫮,ޓ@RK+b҂NXx2 &\\ 4&_~yOҶ4rjAD/N䳅7JՍ;y?GF$GZ 1\X1E jښi9Wz+ 6#,.:oe(FFB{xwG[$q",k>h= A7A/mIhсZZkwjo!K @X^B~%ѿ#P˻{ƣ:jgXRkV%'?hxUWEsH~J{ű򩧞>z4N,LW_Uiu42Vj\(R4"sK?YUҹh!ν7=3ߖE3'm&ڌr q1m%4іIW^yhژy۽ޛϩ:c{Y3R;;3aI' /x8䓇7xc>]׷~N<7u^xam$Lfi` {T*,h1@兀B` >>p%m6m6j0 ᗿehR]tQ~qM6$qᇇ駟>|ᤓN K-TxØc0*(@SfzlB@4#G }P98fm>04tYkvx饗C=^駟bp-W\ѓB@ BY0/gMUqae gqư +w}7|7裏?,.g_~pe%7SHg#vZXtE+\y啕ioOZG4I.Nk<,KlYV!xRrp KᅬCvVM%ׄʢiC,[7r)߃z+c! >"C C9r뭷t}7m!f'=!hZ.S~uQ}Rm" bgY8-BRt=}c@[,O4|.!.M0NIC&O nKxZq;PձCСquB@dj,'L_|HG<`0bw}Sm,Id>i)!QBHEC[? D-ॴf"TUN==3]3KCavE·W^9}{eQ hOJ\_}גTϯ&].@#0tB@tՖa*# 9;EpfFz' ނRԻ |R+iFFAT(x4waJ/bZej|;iEeeP22ڣ8#Dg4Mi:GkdRG{v #C@dDg}"MȢ$R |M4R\ZcwaE꾯F@%n,_wzt~r}=8]#bU<}B{+BAr`m)fdg)%,$B@t3Z.@|?! @# hA_xR{&l yiAڦ VbB@AE@tPWB@! ~^ca+B@! h#"m[M ! B@ @! Bn5&B@! g@! Bn5&B@! g@! Bn5&B@! g@! Bn5&B@! g@! Bn5&B@! g@! Bn5&B@! g@! Bn5&B@! g@! Bn5&B@! g@! B?Ȁ߬LIENDB`MatchIt/inst/figures/README-unnamed-chunk-2-2.png0000644000176200001440000010634513712625277020745 0ustar liggesusersPNG  IHDRz4iCCPkCGColorSpaceGenericRGB8U]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_@IDATxֆϒsΒ% $(YQEAI"\~PL+A "rUs(9g꯯=|<]]U]v7ΩR:  @ !    C7 @P P7OF$@$@$@$@{HHHH (@'#   =@$@$@$@$TA͓ P    * РHHHH(@y hPqd$@$@$@$@HHHHJ4y2     P$@$@$@$@A%@T< (     *nHHHH @P P7OF$@$@$@$@{HHHH (@'#   =@$@$@$@$TA͓ P    * РHHHH(@y hPqd$@$@$@$@HHHHJ4y2     P$@$@$@$@A%@T< (     *nHHHH @P P7OF$@$@$@$@{HHHH (@'#   =@$@$@$@$TA͓ P    * РHHHH(@y hPqd$@$@$@$@HHHHJ4y2     P$@$@$@$@A%@T< (     *nHHHH @P P7OF$@$@$@$@{HHHH (@'#   =@$@$@$@$TA͓ P    * РHHHH(@y hPqd$@$@$@$@HHHHJ4y2     P$@$@$@$@A%@T< (     *nHHHH @P P7OF$@$@$@$@{HHHH (@'#   =@$@$@$@$TA͓ P    * РHHHH(@y hPqd$@$@$@$@HHHHJ4y2    DY#VTRy˗%]t̛7oJʔ)}/C ^3HHH ҤI#-3ƽrjDit.e& .)@7n,͓̙3ǩuԑgq-G6}C9))RJ.\vj L%ep+]~*6/]$W\gO~c_~~Ǒ#G$UT2}tmnΝ+/vˏe'G P[7|#O< 2vX,Ỉ zX"Q&`mEhذ,XPo_ dʕƚ&W_}XJ!jϝ;:׉'7!l!B!22e˖kĉM(:a޽ucR$?y򤛸eIF>4,_q۶mF{4ÇYO~~~F[K̙3(.߿!*hKZ)m~v뗶*퇨ҥTGXbjԨQj߾}nušSūZbVIdPW)שS'UP!mꫯLm5YeʔQBj6zݵo"sn=M΅h* & vءҧOoƅ_C5iĭ|vJ:uc;$@$@$2ʒ%h}nJF7|J-$iE@ ))eʔ_,K.-f_6sVy`¿"_|#G3oY$pU1{9~+G5CXk S_U%eJժU,oD$@$@$82eTcVVςX SpU`"   *Cޝ(N˂/@I_bSv6} 1gC@pnD1 @p|_P?KA(^9HHO#CcL$@$@$@$Z@2$@$@$@$@#@0lHHHHPb     nhʕ\>oHH&@7*@Æ (Θ  7HHHHA4y     P n h0($@$@$@$@..HGJԩ D Љ=ٳ')> @ a @H*e?IKd޽fdɒ*U*"QgϞ=.]:)^_u5    4AX9=Zr->Ԯ]ۈ۷؝_] ( ݺuG}_bÃ$@$@$@'XzAyǤp’1cF\̞=ۍ_-͚5lٲ ăAVRE:*sNiԨ)נA͛ >}ȭ[X,̝;W^~er劜?^.\`jTPAVZ̙3eȐ!k.ٴi߿_N*%J͛7{L   !H Ayq?3f*m׮@uV#Ps!~.]ڈQWZV>}Z`1۷{Ϛ8SL۷oA-AtmQ_|1ڱsΙk~1ɗ/x.]d2tPW7HHH#5k$sٱcGڵ̚5駟K/$w}oѢ[v#Wڴiݻ K*l$&޽m\ xE|LOny'OϩnԿsN{kJ d߱m4}x 'X3ѾT M ]&dɒiW(|`i1ȤtUٲeUɕVZJ;Ç+ V}n}ז.-|f弽V7RvtS>TNy\#-|zz|e޸qCޮS7VULˣ>ê}A$@$@J|J:}TrҥrYYlZMj'= (_O+kBD4SԫWXע+/vPP1nyc>A᫊ZxƍJ   H4 .mI&<`lܱctD%K!PK*%h k>Z/mٲŭ.wF?<@֢nD-rI>׊X pпjk'I$@$@$(qocѢE"%{HAGXs,%x ٳKD믿bŊ1Xx~ݺuq5|~T @pE͛7 % &aZ~!i~MڴicGeډ +9sXbRR%c!CD}u#C$@$@$@$"Uמ6`?'  p$7E)x i߱'G#bN8!,[nu"Z`A{un $GNX`Ây5:zHHH‘-1G@ 덩uD7\PX"~͑#g$@$@$@q%h/&    S𡁗$   $@I$@$@$@$@J4QFN&Lڪs9R  HMY9`v @x4/GG$@$@$@#@K @x ё P:fV\ɅCұ$@$@$tAG'lذ!"   8R6H$@$@$@$ И @ /$**JN>pPlHHHH 0}`8U+|[Nr!ZƍƸvZ?_RhQܹ7HHHH 6aem<;;wHٲeW_X}iҤ˦ٳ~F6mT~W)XZJRN XHHH 8V^|Yz!E9sJ˖-?p`_Gٳg-Zȉ'܎2Dt?H*Uٳ7J6m$wF\M?uT!xIHHA4Ӽ<ZK8n۶Mq#!#/޽{Ν;'6m,CIfŋ{I:uO>駟 n6l`ϊ=m4yw@nǞ{9U=ݾ}['{_ĮUɏ󃟟[/s^)ێ/3_7Bsҥr%3faׯ_?cɜ9sAsSFИ|-+VFvf;uH?ҫ>ʕ+1b׺5jP|c2~3HHH ʗ/n(xL?^WM.\XΜ9#7Ĵ=u]X1ƼyRNk]߂   UXÊD$@$@$@p͒%@hܹ%@ !:}.P<͛7ݲ ^,Y2ɐ!ViL2ٳ~;eʔ-φ+_~]RJ%4~xUbEۭ[7e͚5`D$@$@$@p(_ҥ3iӚ , ATB}]p؞={ETRzg;vWW^gG6jԩҷo_Jzj޵<`w}|[ ̲F$HHH a7t0ҁAf͚JΡC*-L1 3EJ/tzwߨ2ec; R3f4y^=0@9rDig?~5rH;B%c0V.~q*t%4.XHH# >wdBѽkĢJ*)J{7nʗ/)om+Ie%Ky Pj SXS6h\JHH'̆`<|=XkSb[A.\ilٲ |P5ܷo`q$<AH7>L$@$@$@!7YfwVd&)PB1m4 AL#uZHHHHMd     x@c    ?;$    x@c    ?;ִkGٲI$@$@$@^ Pz̸8y$֔k5'  @x9d   HJII&   $@C&   $@&Lc8h @ 8Qq']&љyZ   P#@ h]1HHHBh_@vHHHB[dDEEq/Ҕ9~? ʚ51": 8QߺuKΟ?/oߎ+WBRb  'ڹs~sBñxU8K.C=$ӧܹsKΜ9Q1 Q11  $'pUygERti)^ 4H9:~eѣ)R-[zn߲eԨQCd"<ܹՆƍ7$~oA3Ȋ+3(ȕ+ҿܚR۷ό{o3*=8~ʔ)R_ĉnyUڵk*u~~{[eYg1;|Yx>{?Ѽys/k\YlYk& <)i-TtvNIHpі8駟Lˣ>t9#M0 U6F{hwÆ 5w۷+nߵ3:(d_E뮻ԇ~hΫE 6dv탪0>26~-#q >1M  h .^1!{Sԅ aÆ (1@1cƘ%0|X1-uD_HX k" AFFZh`i*UVjժٳENɟ~΁e:w,9r0듢裷>c&ypjq֑~>HHA.fBG`)iDA'ͩwVtW[2>͔ o~Vϰ<|M!2aûz TRٛ6Ƌ|q`ƴ !rqHHHC@/Q)f;5BO1I|,%._":"~!j*w_[z&7SLqmG$@$@$^BN~HHH"h]sHHHhIHHH PF5O㱩g"   [K<? C&+( @P %"   ?XHHHH `(@ CJ,+ ]D$@$@$@Eɓ, D6 Ⱦ= hБ$@$@$@$(@#s$@$@$@$tD(9tǑv-[fodFք $eʔN'  %2wuҁˢE|IӦM%o޼4HHO4L22}x *>lܸQ]&ɓ'WʠA7AģZs=RR%YxtIv)e˖GB$@$@$#W^-w`K֫WOΜ9c֙8p/_^ҧO/%J_|Q\ۥKO>Rti)^MGq|C)"9s攖-[:nm߿_6l(Yd5julݺuRJݰaԭ[XD.gϞc .vI9N:2c ql` Ɔs|w /H̫_~rM? .\0Dz7eĉ mޑa̕+i\W_}U 7o+WN~G"  $]KM*TH=J "G-pVW;v“jС۷MY-ZPiӦbŊ-ZwQZL6lڨ]Cik|M-pU߾}ɓUŊUԩՁL-M_UikkҤOU}ʜ9yyK,S{B}SNٳծ]U V*O>fwm U=ɲ*{ y~?~C ?,0?-KZk%;ne6l*Zh:ٳWڿU%@ϐ!1bɳ Ћ/ K+Dؾ}\ Ƿz5~BYW렯8SVQOjժŴk݊ڍAݺuXjӦMkMBFH@`Bٓvpշշ;Cդ;v0y[q~&ރ9~!//O?FN:: ~_߲f/'|@š)gXV K+O[ ;FwVBTRhVMݺu=Mk^=z4nU.| fՅߪ=!B]ڳbXPǞENuǎ^m߾hJf;E=ofVZ˗cG5T2YNu~=xBU'G!YҨQ#GѾa3⢧teozN9 k@%Xknr9r i޼6o{BPجqU'.h>3Ӿ'>5vظ42e ,hׯoڦXr>\c&   pkR;wΈ϶mK',Pt)ZT)сB}\UaaVJ,+(Ħ4/+DͿ:qXv.:,+N,gv'}Oš>rHXf͚&u*k:Z^t9\ i~bJ믿6?'~{ ,L$@$@$@%2B/$aS혺/1ckvbM/YLiCP cŲ̖֮-Y[Z?x ]X3> BXwXD:ٳKz2gl[0%›W0s (DFW% K#z>c',(ofܳn|p;J=a)Lc{ɛ5߱ŷisX(bB9  @`ٳf͊ځ9S[qG;A 1 Ŗ<=c*>I/$xŔ+;6_O$@$@$@@ dC&H$@$@$@$;>$駟Lz @ X)6 t#QO-ԟ'e$@$@$@!I4$/:=ez{D$@$@$$N D " 8B$@$@$@@4.r0X^=< @h2LuۥK:o ސ @ K P:z7$@$@$@$(@s$@$@$@$, ;wUJ֬YeĈ2k׮իWa#rƍp @$@$@$(@0o׮s=Lx^}'™w'OR*x'HHHBh.ݻ+͚53gHsPOL2ric5jT$@$lTΝ[T"|;ޑ@p4˖-Lb3gΘ)ށJ%}RD yʕ+.ܗ.]>}Hҥx2h 9r˗GRHə3lRs~30֭s_xqEI:uL+Aʙ={vyGѣ!_̜9F&W_:L>|e̘QU&8GO?-{1h0->ydٳF>|X,Y|6m2bB1Ecǎj͚5[e֭rqI*8H֭M1cH׮]Ƹj֬)-Z H-[VO?vy$@$yu]+W.qE;vlI@2%mu3>|orʕ+&NF TÆ Ml2Rw^5w\s[ KNii}Yu[… ΁yU)өS']\uUZ*cdɢ/g<?4ΝS3gVj?Zs[zjЯԩS>_VZbv˻}?D:uJa|d5snc}{Uɏ-JC&?q6~ݷNqT 0n%-5D41,zjQcm۶M-jz 4b-Y\R3Sji+ HD:1u/H߾}6SZɱcǤy)D R?ieXƖ`VL[ؚq{\b^ 6HocpBc8ߵˀz7,o @ L0AiW*⣏>z$:VP-h❒'O J->5~۷X/B kgBPW4~_w6PP?,_@ɓGUVC"**m?.;X# mcm#21, 1`Q@1 @nQ{@x A8" p}(+VOhڶmkab־}pLQDil,tRіK)}ҦMk}4mT~# ֭k$EгGꫯ\}B͛'ݺu6i.2eJټy@x'K 2/lwc,H$@aB-bXg7d"?X LO?LDc$k&Dcy&2毿* 0(5iDy4 吰VƍEO (V9~u>X* äI$ݻY e;ܹsg9 p<|`& g"H aÆ (1&MFV@>H,VR:rPRNwW^ƍe ((ߑi (5dOeΜ9bML:XU!GL;dȐ8RQ=S0S{ƚ$@$t`4ϘÌUB?n$<3 8@eե{7X>&OXJQΚ 5K`ڏc"S9_p@ E佯qȑbA$Xj/\`hub"   D̚5KJN흗~A)F%<(dT&13(QB^|Er)zjdٲe?~Ι3g̗W_}%5k֔bŊɒ%K7ސgy66O.*U3JjdѢEnEk=# C/?BJTR;չsDAܹkf7} 4HgGkߥsFz<  pkÆ Ҹqc#^yٺujJ{1X1cL8E} ]v2sLS7XBPbJK.)L2qeѣ$OXk 4f_(D-چXmѢ,X*"s 7_=~'m߾]8* D= C͛ҫW/%W\*Aϔ7|OrQ $!=*Di[My}*m4/_VʕSZcB J[QMŋMÇw+.]:u*= "Uik9}wF֭UMɓ'KJ ^Wm]UׯK 0vJ[ƙ={vu)s\[ne;֭[疇)RP-?׮]SSV~55*R]_*7b;U>~+w Ç+mnYdQxz??"-S҆8p>KcZYf< !hI mY:dٮ)|ku,a}jժi&WU}e97}QY+ٳg]yN؀5Ȟ`eBZҦMk6AX8fO*Z=X]mj֦}ǭ ?';ŋBSaOx[c'x g,>xw(VTKWfϞmcZƎhru}EQUz\aMũ6`} nyv j9S{jʔ)f{~Ν[+-xׁC ͸$ q4i|2qie#f1׿ UǧLGB  0 aZ@ cy6{qARҥhݺu~Ҟ<8ӹ`DZj B2;?Z< |A{v%[Inz##GAzj*Ty<$_3AXAaĈ&wߕ?|es`' 0# 0=R+m۶5vJpD-"!u&|> D(:V^O0ARL5h7GV8@bL30 i?m pooDmHHK]`ϓa&yX" bt /2,TpaqFݻՁ4__t:XHlNF- 釓b&,qGz&"Vn1A$@$PacTaÌrH74!`ԩҼysӧYIGK+D PTbxc"w   m8p鷄L bHZ,~KX^zLb*i }BT xÂ摛Lf"   ̚5K6S`Bnw<&2F$   %6S!?lrJj !  H46O8#GK$@$@$@{HHHH (@'#   =@$@$@$@$TA͓ PG D$@$@$@E  "  p< P_"vHHH‹hx]OHHHO$   "@^3FӧOuV'&  s)S۷GvHHH(@q    '@ ӧOGE@IHHHI"N: Sk.i߾TTIz!?S~aHGNjR* m   (@3խ[WfϞ-[lZx ۷o\ի'ɒt)뮻2dر ri ğ#ڵkzriܸdɒE*T ,pիXʜ9/^XnܸVI&'hBN8v;;wFIlL; 7oݹsG.{͛Wz-iUvW_I͚5Xbds,Qȟ2%؜?ޜ3hܸqg<ҥK%eʔ>;qΜ9W^J e˖yY>}> @pڰasѣGK4i)S&1cSݪU+Eٶm<6 &#/86+ժU3>8,}<쳮ro[O˄ dӦMݻwʠ?=zɓKɒ%PmʸN L-gOȻx|lm3ga~%cV}e^*׮]s+ B kIϡʟߟ?/8rzr.j׮]e֬Yҹsgc}e˖B(O> i+W4ְ6O{}vf~z?=w;&l޼U… n{C9ɓ'ڭɟ?o;T'>?x_OZ9.iKZn[ߴTi8.]:5fQk9rD+3gӶ3ehTzUmܸZbjĉJOZ֟N:eZ?/koFJ I,ZȌ=_ڲUkfjJ:u|'*k֬J[epBUFh   (_F4Gw32,*U*_+Wql#rA ǭ6`bü|"_PT@W?mS`66lhƪh]"R<s X ``"  <G PKx[[X@g,Z \4g#[i_ݳ)T3w\#|$q^+Az)O(=ϑi!XW   Dw-˗/w+ XG Zʭ " QKwٶm[ ,(gЁ5E|mx|=o-&M2Ŕ`I4o|qOժUb9&&   D"DTGu#F0y:ijUSfŢ{7e%Ki+,Qz&hΛ7v}R:pFzULM;ZzVIJSZrʹ?}lӶ?eMNL>hZ/W,6q9|*t%%h\ʲ$@$@$*B4$-: FFek' eK3!aI&d"ͱVСC1nXtp0u{k]K,ԡC㏥[nf='|DϛOJZXV8G6b*>Ŗ|mO{n#( KygYcb$E`_.a֦YHHH :`HûSSZ@-EQ h e ,&>aÆ͈@x6oEbMڶWӶ@]; sMl׾2uTcYfqxЀ>9 EuG#ЧO=xuBdz1+&|Mc+Vv$@$@$@ IF4i"*U^B`„ Di;Xųo߾ܹs2n8S[>v._,;v|񝡔3g|>/Yf]G"N>-,XP2eQ`IH'ViӦ=azlٲ_ 6mZl pUiժ|>}z{O^z%3fHV$%?رC#;w_~YF!QQQŹO$@$zjdٲeV^=cuõ>}1ߤZjh"4'M$ҥs+?Ȝ9suwuaw͛7DiӦIժUeѬ^bfr -o޼rq#Bϟ?/F^zx9   pŗK.RV-3-[6caڵ(QBfΜ)5k֔-ZȂ `Bٖ-[Fxzd|eFt|rsNC4*Gb#9?,_f"  ;G Ptdޝ;w|WQ #D}f*0`}Lf{ɒ%aԣGh'JԩSc} Bar%~MiiwOZ 4VYڵ5ߪj*yWKzDz,͚5hXy׏_|0%_h=L[i֭FbwӦMV)SFϟ/Nr[k֬YRpay\eiٳgv v<ࣗ!Ch?6eʔLT||<ׁ׷*=˞9U/uԂ5%[9/?=iW~Uzq\Zx1~RC6c |o 6kÆzW P:]@M{ڇQ ḓ;wnu1CӾ|Jh?3\qFz1e\v\ ;J2z/: )1f +*m (Ə/_^iÝN8)x-j,rr?I/d_|9޽{w* 7=sN>}dƍn.o믿hw!6ٲedΜ,U(HHHNҥK]b z͛7Ouf"7om4Vޱn%/+AM7eM5]@#aO6Aʕ3K=2vX6lX؏$ ;GڇSxqiݺ^·KXzmzkwgԞ,? YH!n{!>Gm?L,d~x EGXvʔ)&7E @)0uTi޼`2uAڶm+C q2BӭwDbV/ a ʸ7CFX^^HF"~d2,Y?i-Q4#,K? S8-!  gP/tfI=ѳG4rk<<cAG'x  'n۶M*T`@B@H ]>y$@9@  xS6lh+W.ZX7#Bhh]/HHBz%j^w<=/cƌr8Oã6mjqBhh]/HHBYdI̲XO?E={z}dvLkDk6mL;4|Lzԡ"  p amF~b\2Z~L~ǚx!=Sf 8LC4t{B,D$@$@v1ܧMfe=PE .vڕk?…bIH  <L\g?\p fīPB(޼yg]py7$@$@$@Zx`LbrwjiƔsQF |ݎqǙ(@y]+  x!ɱt(֍>u,[Lʏ?(#GO`M4<_8h_vHHŸ@n駟ݻw1 K&,oMQD{&GsO2Ew :bňG2 /2djժ'E|>Hj׮- |kN>qf:-κ!<… Wm' p,XNcJXno_~=<hM<% D2 H; $ $S @$+Ç'eo>|7j3{J*%w,͝={V^y3g}zCF/]\Va/fٞ©n~ gm)Sư [cM?2?ߚ)cz?qℼ)H5jԐw}7ZFLc3 c?/ln޼cqixZjoZ0<}*=%z :k;WYlha .薯B+_Шdɒ֭[u#wرcцUʔ) K|iK? ճ2ߨ7kTԩ*BZ`ry횶{=L "6mp|37 M{[VP)Ѫ5h@YMsLh}eRH}ߚs@S:zL*ǫ^z<^|yuVǝpP}=2skfx(*qlS=iQ*={4SV~޼y%K_,++av) )S뗬J?\M+/%,L$@$`,T>|$&ܿ^t]w ,'\R03*ÝXd޽o1+|k#G}tRxq4h`Ԟ·GQ }K0[Hə3nބ|ҤIA0/ a)h/w+e͚ljzd%ܔɟ{9+ kMû2L?8`ϐ! ")>ֺXj0\.4>>á'oKϟo XfLT_M ^vdN,Cܴ Γ'KX e  kTW4 JMz7|S'X 8/7R$'*#Gʆ :5kH_-~ | |iׄ~5ayDj$ VQ,#'H+X0Z n5SwPVGy#Aٮ^):0D@xb CV(͛77e`y%V_,0X]XdY@W)d(߸~<䓂`:/K|?%=zWF%LΉLcʝ?wݿ,E$@FٰJug> "$=a4Vǿɺr劙>[L*j]Ik[ u '("׿Z{۶mM`[f8z ޓԩSͯdԡCA(do TJEa3:̋jՁ\d   "5LA;`ɴaS|XtwYhL$@$@$iӌo+̒F q|1 ,|<3W:s=s桘oET| E棇ˌJ+QҤiV(K3A%e-L\VTY{4s8sι23[3gϞ={3jU i\V[QNѪiOE:eܹȭ|x\3SEz/JOruO(jtnZT;CV=!LF @(h:@ "@# @(h:@ "@ PGEW0@ PQ?qeG&@ k @G @{7' @@r @ (h.nv/o2@@ KqoaFr˖-sk_@8p9r$?j*wWYtЮIi !pM77xmڛD] @"лwoF _y*s@ u k֬ C7G}tL@.] (P"㏻!C^z*pނxݠAܾ[篚bwC潇~SNuӧOyXv[paCBRU>|{Ga>?%K(0#0w\rʚ=gXx[hb-ڧ]N;^]\j˛umVdf-X߿͠=@.s%=ܓ_B*nXKH99Q@wqǹ 2ĝu[+:n뭷vGq{GbhWX uI'L'po[5VP2ڵkaƍW|ET } Yk*0W_}ynsfr;C9sN>lw-+mSOu|իWQl>~a7~x馛&vmdXp;3?6s^{lʕ^{˘1c6>6 p7ܿkIk;}:TVADziBx5S~iaNXJL^M<@n=ݤIB`RzGi`ѐ4s5u"*<[ ߅5Y]ʎ"7꥗^ЄKQ N? 41ڧj彸w=;6Gu7`;\3ړ-whyC_9l?ך#Ѫ %ז6..f]|ii/ח6,|IOc4s!Z IZ>ڢ:M>lt9rdcXwܑꩧC=VD6JE@J ]qX\wuN"T?dA h^n)qr(*0ކ2駟-Ϟ=dpoQIY? $y~[%cDŽP97g/@k>VH+8Z_'N,'$ZՉO?]uk6|!ru: "`?RuP[J59X|yYMoA"&x'3gNy:mIy<$;~ԨQ<~6I KN~ EC׊^Z^"Dߨ%@kVHdƒl@-wK)Sz)ž6!~'&wg>D!L+HMs RuGKhR.yGr)\Ik @@ . @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO. @\OZ@rO.@WvO>[jU@x ㏻˗'e=gM>T'΅T>z7;}:p n} s3vZн뙏Q}dMܮ6tS{Yd/~!>\wu_^x.?Eeƍs#GTاO29=yرIZzCLezH[}/EKufm;NwW;=Фmr_n-p׿-ZveN;9 u@{!k mo /kr^{;ݚ5k oKI0k/wwy2eJx}ewE$?woGDiW7p@7Di֡w{[ߚ]pڛ?0ao}I>6 VO&;y<_9 [o]uUޫUBov/r0lذP>>w9KtNyfcƌM׿|>#8"x>ce5~x7yd}⊘%?sNuxb{o||Z ?o'pz;ꨣf_|ŪWP;Cj﮻rnfa3{饗Rf%y׻YI y%+u~;?}Gi\^h?;O*SZlY#V>k8Ik8mjolvax̙5u}~ZLgz0=#a{ܹ`wݝ}Ns+VtΘ1Ýznuw*']fm=\r%z+ltP״}W4mvaG ?Ms=םuYnm :^izxO7yqD7cb1ntIlm>eg 7 *Bo"/ٯ޽{{n޼! |0䳛!\o7Lo7poћ&;`CD7!FML{,aĉ7am-hěG.AuѶWoB;9Oo7Q_zy&y66'ۜP; &q袋po.7{p<ބ|jl6-\#Myb[ u1Mx{x e\қ{oS&`Bjlޭ?cQthT1c7k7)?~O-K?m~wp@(#^wb162Ot Wm㳴g#UF"uv59mQL7RlC+ Q+K,@(EJ^veK"yDlCPNzhR `Q̇?h1#N&$YPp̦ K_ Lf!NU$<?Rᩴ؞t}VA[JO{6u~hr=h T]+2lD%ob6))^%ezoۤ2yMgS9W͡ /˝sɛ-~mt%hWҤ,_aE"_bah6C4P+hڏ|#A@ɓ-)f اbAT)H@CVȨ4yٔ؉\ >y%4`֒5&9$FffA$Z4x(G7J(ԐQZ^֣hqYQ2L vIi*~F,8DwTe:Ģȓ,#AnQaja&/|KZJ7h:HL/a:zej[NI[ "SK^Z+oڮ'[pJ(NehI.Q5rpy}0~@}VӒK/|!x彵R~h@77Q, d'KU;džC\ r1gEk-Zn\I6O/D̚ ˼hNk#ϊ5Oh(LX&<.a&W"u>J i^0Oa6qkJVyzܽQIz̅._ wPEJml8G\F;mCXzHֲPA y2LZ@K,