{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# import some basic libraries\n", "import os\n", "import pandas as pd\n", "import seaborn as sns\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# 3. ANOVA tables and post-hoc comparisons\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "

Note

ANOVAs and post-hoc tests are only available for `Lmer` models estimated using the `factors` argument of `model.fit()` and rely on implementations in R

\n", "\n", "In the previous tutorial where we looked at categorical predictors, behind the scenes `pymer4` was using the `factor` functionality in R. This means the output of `model.fit()` looks a lot like `summary()` in R applied to a model with categorical predictors. But what if we want to compute an F-test across *all levels* of our categorical predictor? \n", "\n", "`pymer4` makes this easy to do, and makes it easy to ensure Type III sums of squares infereces are valid. It also makes it easy to follow up omnibus tests with post-hoc pairwise comparisons. \n", "\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "## ANOVA tables and orthogonal contrasts\n", "\n", "Because ANOVA is just regression, `pymer4` can estimate ANOVA tables with F-results using the `.anova()` method on a fitted model. This will compute a Type-III SS table given the coding scheme provided when the model was initially fit. Based on the distribution of data across factor levels and the specific coding-scheme used, this may produce invalid Type-III SS computations. For this reason the `.anova()` method has a `force-orthogonal=True` argument that will reparameterize and refit the model using orthogonal polynomial contrasts prior to computing an ANOVA table.\n", "\n", "Here we first estimate a mode with dummy-coded categories and suppress the summary output of `.fit()`. Then we use `.anova()` to examine the F-test results. \n", "\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SS Type III Analysis of Variance Table with Satterthwaite approximated degrees of freedom:\n", "(NOTE: Using original model contrasts, orthogonality not guaranteed)\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SSMSNumDFDenomDFF-statP-valSig
IV32359.7781351179.8890672515.05.2962840.005287**
\n", "
" ], "text/plain": [ " SS MS NumDF DenomDF F-stat P-val Sig\n", "IV3 2359.778135 1179.889067 2 515.0 5.296284 0.005287 **" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# import model and sample data\n", "from pymer4.utils import get_resource_path\n", "from pymer4.models import Lmer\n", "\n", "# IV3 is a categorical predictors with 3 levels in the sample data\n", "df = pd.read_csv(os.path.join(get_resource_path(), 'sample_data.csv'))\n", "\n", "# # We're going to fit a multi-level regression using the \n", "# categorical predictor (IV3) which has 3 levels\n", "model = Lmer('DV ~ IV3 + (1|Group)', data=df)\n", "\n", "# Using dummy-coding; suppress summary output\n", "model.fit(factors={\n", " 'IV3': ['1.0', '0.5', '1.5']\n", "}, summarize=False)\n", "\n", "# Get ANOVA table\n", "model.anova()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "Type III SS inferences will only be valid if data are fully balanced across levels or if contrasts between levels are orthogonally coded and sum to 0. Below we tell `pymer4` to respecify our contrasts to ensure this before estimating the ANOVA. `pymer4` also saves the last set of contrasts used priory to forcing orthogonality. \n", "\n", "Because the sample data is balanced across factor levels and there are not interaction terms, in this case orthogonal contrast coding doesn't change the results.\n", "\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SS Type III Analysis of Variance Table with Satterthwaite approximated degrees of freedom:\n", "(NOTE: Model refit with orthogonal polynomial contrasts)\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SSMSNumDFDenomDFF-statP-valSig
IV32359.7781351179.8890672515.0000015.2962840.005287**
\n", "
" ], "text/plain": [ " SS MS NumDF DenomDF F-stat P-val Sig\n", "IV3 2359.778135 1179.889067 2 515.000001 5.296284 0.005287 **" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Get ANOVA table, but this time force orthogonality \n", "# for valid SS III inferences\n", "# In this case the data are balanced so nothing changes\n", "model.anova(force_orthogonal=True)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "data": { "text/plain": [ "{'IV3': ['0.5', '1.0', '1.5']}" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Checkout current contrast scheme (for first contrast)\n", "# Notice how it's simply a linear contrast across levels\n", "model.factors" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "data": { "text/plain": [ "{'IV3': ['1.0', '0.5', '1.5']}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Checkout previous contrast scheme \n", "# which was a treatment contrast with 1.0\n", "# as the reference level\n", "model.factors_prev_" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "Marginal estimates and post-hoc comparisons\n", "-------------------------------------------\n", "`pymer4` leverages the `emmeans` package in order to compute marginal estimates (\"cell means\" in ANOVA lingo) and pair-wise comparisons of models that contain categorical terms and/or interactions. This can be performed by using the `.post_hoc()` method on fitted models. Let's see an example: \n", "\n", "First we'll quickly create a second categorical IV to demo with and estimate a 3x3 ANOVA to get main effects and the interaction.\n", "\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SS Type III Analysis of Variance Table with Satterthwaite approximated degrees of freedom:\n", "(NOTE: Using original model contrasts, orthogonality not guaranteed)\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SSMSNumDFDenomDFF-statP-valSig
IV4449.771051224.8855252510.8977751.0069430.366058
IV32486.1243181243.0621592508.9930805.5659100.004063**
IV4:IV3553.852530138.4631324511.0736240.6199800.648444
\n", "
" ], "text/plain": [ " SS MS NumDF DenomDF F-stat P-val Sig\n", "IV4 449.771051 224.885525 2 510.897775 1.006943 0.366058 \n", "IV3 2486.124318 1243.062159 2 508.993080 5.565910 0.004063 **\n", "IV4:IV3 553.852530 138.463132 4 511.073624 0.619980 0.648444 " ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Fix the random number generator \n", "# for reproducibility\n", "import numpy as np\n", "np.random.seed(10)\n", "\n", "# Create a new categorical variable with 3 levels\n", "df = df.assign(IV4=np.random.choice(['1', '2', '3'], size=df.shape[0]))\n", "\n", "# Estimate model with orthogonal polynomial contrasts\n", "model = Lmer('DV ~ IV4*IV3 + (1|Group)', data=df)\n", "model.fit(factors={\n", " 'IV4': ['1', '2', '3'],\n", " 'IV3': ['1.0', '0.5', '1.5']},\n", " ordered=True,\n", " summarize=False\n", ")\n", "# Get ANOVA table\n", "# We can ignore the note in the output because\n", "# we manually specified polynomial contrasts\n", "model.anova()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "### Example 1\n", "\n", "Compare each level of IV3 to each other level of IV3, *within* each level of IV4. Use default Tukey HSD p-values.\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "P-values adjusted by tukey method for family of 3 estimates\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
IV3IV4Estimate2.5_ci97.5_ciSEDF
11.0142.55433.77851.3304.39868.140
20.5145.45536.64454.2664.41769.299
31.5140.90432.19649.6124.36165.943
41.0242.09233.30150.8824.40668.609
50.5241.49532.82950.1614.33964.626
61.5238.78629.96147.6124.42569.746
71.0343.42434.74152.1074.34865.149
80.5346.00837.26154.7554.38367.208
91.5338.11929.38446.8544.37666.801
\n", "
" ], "text/plain": [ " IV3 IV4 Estimate 2.5_ci 97.5_ci SE DF\n", "1 1.0 1 42.554 33.778 51.330 4.398 68.140\n", "2 0.5 1 45.455 36.644 54.266 4.417 69.299\n", "3 1.5 1 40.904 32.196 49.612 4.361 65.943\n", "4 1.0 2 42.092 33.301 50.882 4.406 68.609\n", "5 0.5 2 41.495 32.829 50.161 4.339 64.626\n", "6 1.5 2 38.786 29.961 47.612 4.425 69.746\n", "7 1.0 3 43.424 34.741 52.107 4.348 65.149\n", "8 0.5 3 46.008 37.261 54.755 4.383 67.208\n", "9 1.5 3 38.119 29.384 46.854 4.376 66.801" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Compute post-hoc tests\n", "marginal_estimates, comparisons = model.post_hoc(marginal_vars='IV3', grouping_vars='IV4')\n", "\n", "# \"Cell\" means of the ANOVA\n", "marginal_estimates" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ContrastIV4Estimate2.5_ci97.5_ciSEDFT-statP-valSig
11.0 - 0.51-2.901-9.5233.7212.817510.016-1.0300.558
21.0 - 1.511.650-4.7508.0502.723510.1370.6060.817
30.5 - 1.514.552-1.95111.0542.766510.2671.6450.228
41.0 - 0.520.596-5.7496.9422.700510.2490.2210.973
51.0 - 1.523.305-3.3879.9982.847510.8831.1610.477
60.5 - 1.522.709-3.7499.1662.747510.7320.9860.586
71.0 - 0.53-2.584-8.8933.7252.684510.213-0.9630.601
81.0 - 1.535.305-1.00611.6152.685510.7101.9760.119
90.5 - 1.537.8891.43714.3402.745510.6632.8740.012*
\n", "
" ], "text/plain": [ " Contrast IV4 Estimate 2.5_ci 97.5_ci SE DF T-stat P-val Sig\n", "1 1.0 - 0.5 1 -2.901 -9.523 3.721 2.817 510.016 -1.030 0.558 \n", "2 1.0 - 1.5 1 1.650 -4.750 8.050 2.723 510.137 0.606 0.817 \n", "3 0.5 - 1.5 1 4.552 -1.951 11.054 2.766 510.267 1.645 0.228 \n", "4 1.0 - 0.5 2 0.596 -5.749 6.942 2.700 510.249 0.221 0.973 \n", "5 1.0 - 1.5 2 3.305 -3.387 9.998 2.847 510.883 1.161 0.477 \n", "6 0.5 - 1.5 2 2.709 -3.749 9.166 2.747 510.732 0.986 0.586 \n", "7 1.0 - 0.5 3 -2.584 -8.893 3.725 2.684 510.213 -0.963 0.601 \n", "8 1.0 - 1.5 3 5.305 -1.006 11.615 2.685 510.710 1.976 0.119 \n", "9 0.5 - 1.5 3 7.889 1.437 14.340 2.745 510.663 2.874 0.012 *" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Pairwise comparisons\n", "comparisons" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "### Example 2\n", "\n", "Compare each unique IV3,IV4 \"cell mean\" to every other IV3,IV4 \"cell mean\" and used FDR correction for multiple comparisons:\n", "\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "P-values adjusted by fdr method for 36 comparisons\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ContrastEstimate2.5_ci97.5_ciSEDFT-statP-valSig
11.0,1 - 0.5,1-2.901-11.9576.1552.817510.016-1.0300.535
21.0,1 - 1.5,11.650-7.10210.4032.723510.1370.6060.726
31.0,1 - 1.0,20.463-8.6579.5822.837511.1030.1630.871
41.0,1 - 0.5,21.059-7.6499.7662.709510.4350.3910.835
51.0,1 - 1.5,23.768-5.36412.8992.841510.7371.3260.473
\n", "
" ], "text/plain": [ " Contrast Estimate 2.5_ci 97.5_ci SE DF T-stat P-val Sig\n", "1 1.0,1 - 0.5,1 -2.901 -11.957 6.155 2.817 510.016 -1.030 0.535 \n", "2 1.0,1 - 1.5,1 1.650 -7.102 10.403 2.723 510.137 0.606 0.726 \n", "3 1.0,1 - 1.0,2 0.463 -8.657 9.582 2.837 511.103 0.163 0.871 \n", "4 1.0,1 - 0.5,2 1.059 -7.649 9.766 2.709 510.435 0.391 0.835 \n", "5 1.0,1 - 1.5,2 3.768 -5.364 12.899 2.841 510.737 1.326 0.473 " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Compute post-hoc tests\n", "marginal_estimates, comparisons = model.post_hoc(marginal_vars=['IV3', 'IV4'], p_adjust='fdr')\n", "\n", "# Pairwise comparisons, print the head because there are a lot\n", "# marginal_estimates are the same as the previous example\n", "comparisons.head()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "### Example 3\n", "\n", "For this example we'll estimate a more complicated ANOVA with 1 continuous IV and 2 categorical IVs with 3 levels each. This is the same model as before but with IV2 thrown into the mix. Now, pairwise comparisons reflect changes in the *slope* of the continuous IV (IV2) between levels of the categorical IVs (IV3 and IV4).\n", "\n", "First let's get the ANOVA table\n", "\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SS Type III Analysis of Variance Table with Satterthwaite approximated degrees of freedom:\n", "(NOTE: Using original model contrasts, orthogonality not guaranteed)\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SSMSNumDFDenomDFF-statP-valSig
IV246010.24547146010.2454711535.763367306.7654511.220547e-54***
IV3726.318000363.1590002500.5739972.4213018.984551e-02.
IV4143.37993271.6899662502.2972910.4779816.203159e-01
IV2:IV3613.455876306.7279382500.4034432.0450561.304528e-01
IV2:IV44.9149002.4574502502.3006640.0163859.837494e-01
IV3:IV492.22532723.0563324502.9507710.1537249.612985e-01
IV2:IV3:IV4368.08556992.0213924503.3548650.6135376.530638e-01
\n", "
" ], "text/plain": [ " SS MS NumDF DenomDF F-stat P-val Sig\n", "IV2 46010.245471 46010.245471 1 535.763367 306.765451 1.220547e-54 ***\n", "IV3 726.318000 363.159000 2 500.573997 2.421301 8.984551e-02 .\n", "IV4 143.379932 71.689966 2 502.297291 0.477981 6.203159e-01 \n", "IV2:IV3 613.455876 306.727938 2 500.403443 2.045056 1.304528e-01 \n", "IV2:IV4 4.914900 2.457450 2 502.300664 0.016385 9.837494e-01 \n", "IV3:IV4 92.225327 23.056332 4 502.950771 0.153724 9.612985e-01 \n", "IV2:IV3:IV4 368.085569 92.021392 4 503.354865 0.613537 6.530638e-01 " ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = Lmer('DV ~ IV2*IV3*IV4 + (1|Group)', data=df)\n", "# Only need to polynomial contrasts for IV3 and IV4\n", "# because IV2 is continuous\n", "model.fit(factors={\n", " 'IV4': ['1', '2', '3'],\n", " 'IV3': ['1.0', '0.5', '1.5']},\n", " ordered=True,\n", " summarize=False\n", ")\n", "\n", "# Get ANOVA table\n", "model.anova()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "Now we can compute the pairwise difference in slopes \n", "\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "Collapsed": "false", "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "P-values adjusted by bonf method for 3 comparisons\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ContrastIV4Estimate2.5_ci97.5_ciSEDFT-statP-valSig
11.0 - 0.51-0.053-0.2540.1470.084502.345-0.6381.000
21.0 - 1.51-0.131-0.3130.0500.076502.494-1.7340.250
30.5 - 1.51-0.078-0.2780.1220.083502.821-0.9331.000
41.0 - 0.52-0.038-0.2100.1340.072501.096-0.5261.000
51.0 - 1.520.002-0.1840.1890.078502.7450.0311.000
60.5 - 1.520.040-0.1420.2220.076502.8360.5301.000
71.0 - 0.53-0.134-0.3290.0610.081502.956-1.6460.301
81.0 - 1.53-0.110-0.3020.0830.080502.109-1.3680.516
90.5 - 1.530.024-0.1660.2140.079502.5380.3041.000
\n", "
" ], "text/plain": [ " Contrast IV4 Estimate 2.5_ci 97.5_ci SE DF T-stat P-val Sig\n", "1 1.0 - 0.5 1 -0.053 -0.254 0.147 0.084 502.345 -0.638 1.000 \n", "2 1.0 - 1.5 1 -0.131 -0.313 0.050 0.076 502.494 -1.734 0.250 \n", "3 0.5 - 1.5 1 -0.078 -0.278 0.122 0.083 502.821 -0.933 1.000 \n", "4 1.0 - 0.5 2 -0.038 -0.210 0.134 0.072 501.096 -0.526 1.000 \n", "5 1.0 - 1.5 2 0.002 -0.184 0.189 0.078 502.745 0.031 1.000 \n", "6 0.5 - 1.5 2 0.040 -0.142 0.222 0.076 502.836 0.530 1.000 \n", "7 1.0 - 0.5 3 -0.134 -0.329 0.061 0.081 502.956 -1.646 0.301 \n", "8 1.0 - 1.5 3 -0.110 -0.302 0.083 0.080 502.109 -1.368 0.516 \n", "9 0.5 - 1.5 3 0.024 -0.166 0.214 0.079 502.538 0.304 1.000 " ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Compute post-hoc tests with bonferroni correction\n", "marginal_estimates, comparisons = model.post_hoc(marginal_vars='IV2',\n", " grouping_vars=['IV3', 'IV4'],\n", " p_adjust='bonf')\n", "\n", "# Pairwise comparisons\n", "comparisons" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 4 }