devtools::load_all() # if using the rproject dowloaded from the slides
# source("utils-glm.R") # if using a standard setup
library(here)
library(tidyr) # for data manipulation
library(dplyr) # for data manipulation
library(ggplot2) # plotting
library(car) # general utilities
library(effects) # for extracting and plotting effects 
library(emmeans) # for marginal means
dat <- read.csv(here("data", "dropout.csv"))

Overview

This dataset dropout.csv contains data about dropouts during high school for nrow(dat) adolescents. We want to understand the impact of the parenting style (permissive, neglectful, authoritative, authoritarian) and the academic performance (high, low) on the probability of dropout (0 = no dropout, 1 = dropout).

  1. Importing data and overall check
  2. Exploratory data analysis of predictors and the relationships between predictors and the number of words
  3. Compute the odds ratio manually comparing the academic performances for each parenting style
  4. Model fitting with glm() using the dataset in the binary form
  5. Model fitting with glm() using the dataset in the aggregated form
  6. Plotting and interpreting effects of both models
    • is there any difference? try to understand why
  7. Write a brief paragraph reporting the effects with your interpretation

1. Importing data and overall check

dat <- read.csv("data/dropout.csv")
str(dat)
## 'data.frame':    500 obs. of  4 variables:
##  $ id       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ parenting: chr  "permissive" "neglectful" "authoritative" "neglectful" ...
##  $ academic : chr  "high" "low" "high" "low" ...
##  $ drop     : int  0 0 0 1 0 0 0 0 0 0 ...

Check for NA values:

count_na(dat) # from utils-glm.R
##        id parenting  academic      drop 
##         0         0         0         0
# equivalent to sapply(dat, function(x) sum(is.na(x)))

Everything seems good, we do not have NA values.

Let’s convert categorical variables into factor setting the appropriate order:

  • parenting: neglectful, permissive, authoritative, authoritarian
  • academic: low, high
dat$parenting <- factor(dat$parenting, levels = c("neglectful",
                                                  "permissive",
                                                  "authoritative",
                                                  "authoritarian"))
dat$academic <- factor(dat$academic, levels = c("low", "high"))

levels(dat$parenting)
## [1] "neglectful"    "permissive"    "authoritative" "authoritarian"
levels(dat$academic)
## [1] "low"  "high"

2. Exploratory data analysis

summary(dat) # not really meaningful
##        id                parenting   academic        drop      
##  Min.   :  1.0   neglectful   :119   low :237   Min.   :0.000  
##  1st Qu.:125.8   permissive   :152   high:263   1st Qu.:0.000  
##  Median :250.5   authoritative:124              Median :0.000  
##  Mean   :250.5   authoritarian:105              Mean   :0.166  
##  3rd Qu.:375.2                                  3rd Qu.:0.000  
##  Max.   :500.0                                  Max.   :1.000

With categorical variables we need to use absolute/relative frequencies and contingency tables.

Let’s start by univariate EDA:

# distribution of parenting styles
table(dat$parenting)
## 
##    neglectful    permissive authoritative authoritarian 
##           119           152           124           105
table(dat$parenting)/nrow(dat)
## 
##    neglectful    permissive authoritative authoritarian 
##         0.238         0.304         0.248         0.210
# distribution of academic performance
table(dat$academic)
## 
##  low high 
##  237  263
table(dat$academic)/nrow(dat)
## 
##   low  high 
## 0.474 0.526
# overall dropout rate
table(dat$drop)
## 
##   0   1 
## 417  83
table(dat$drop)/nrow(dat)
## 
##     0     1 
## 0.834 0.166
# mean(dat$drop) # directly

Let’s create an overall plot:

par(mfrow = c(3,1))
barplot(table(dat$parenting)/nrow(dat), main = "Parenting")
barplot(table(dat$academic)/nrow(dat), main = "Academic")
barplot(table(dat$drop)/nrow(dat), main = "Dropout")

How to interpret?

Let’s now explore the bivariate relationships:

table(dat$parenting, dat$academic)
##                
##                 low high
##   neglectful     95   24
##   permissive     77   75
##   authoritative  20  104
##   authoritarian  45   60
table(dat$academic, dat$parenting)
##       
##        neglectful permissive authoritative authoritarian
##   low          95         77            20            45
##   high         24         75           104            60

We can create tables with relative frequencies:

prop.table(table(dat$parenting, dat$academic), 1) # by row
##                
##                       low      high
##   neglectful    0.7983193 0.2016807
##   permissive    0.5065789 0.4934211
##   authoritative 0.1612903 0.8387097
##   authoritarian 0.4285714 0.5714286
prop.table(table(dat$parenting, dat$academic), 2) # by column
##                
##                        low       high
##   neglectful    0.40084388 0.09125475
##   permissive    0.32489451 0.28517110
##   authoritative 0.08438819 0.39543726
##   authoritarian 0.18987342 0.22813688

…and some plots:

barplot(prop.table(table(dat$parenting, dat$academic), 2), 
        beside = TRUE,
        col = c("firebrick", "lightblue", "darkgreen", "pink"))

legend(4.5,0.4, legend = levels(dat$parenting), 
       fill = c("firebrick", "lightblue", "darkgreen", "pink"))

Of course, we can compute the relative frequencies in multiple ways (total, row or column wise).

Then the bivariate relationships with the drop variable:

table(dat$parenting, dat$drop)
##                
##                   0   1
##   neglectful     76  43
##   permissive    136  16
##   authoritative 117   7
##   authoritarian  88  17
prop.table(table(dat$parenting, dat$drop), 1)
##                
##                          0          1
##   neglectful    0.63865546 0.36134454
##   permissive    0.89473684 0.10526316
##   authoritative 0.94354839 0.05645161
##   authoritarian 0.83809524 0.16190476
table(dat$academic, dat$drop)
##       
##          0   1
##   low  174  63
##   high 243  20
prop.table(table(dat$academic, dat$drop), 1)
##       
##                 0          1
##   low  0.73417722 0.26582278
##   high 0.92395437 0.07604563

And the plots:

barplot(prop.table(table(dat$parenting, dat$drop), 1), 
        beside = TRUE,
        col = c("firebrick", "lightblue", "darkgreen", "pink"))

legend(7, 0.8, legend = levels(dat$parenting), 
       fill = c("firebrick", "lightblue", "darkgreen", "pink"))

barplot(prop.table(table(dat$parenting, dat$drop), 1), 
        beside = TRUE,
        col = c("firebrick", "lightblue", "darkgreen", "pink"))

legend(7, 0.8, legend = levels(dat$parenting), 
       fill = c("firebrick", "lightblue", "darkgreen", "pink"))

barplot(prop.table(table(dat$academic, dat$drop), 1), 
        beside = TRUE,
        col = c("red", "blue"))

legend(4, 0.5, legend = levels(dat$academic), 
       fill =  c("red", "blue"))

Finally we can represent the full relationship:

table(dat$drop, dat$parenting, dat$academic)
## , ,  = low
## 
##    
##     neglectful permissive authoritative authoritarian
##   0         59         62            17            36
##   1         36         15             3             9
## 
## , ,  = high
## 
##    
##     neglectful permissive authoritative authoritarian
##   0         17         74           100            52
##   1          7          1             4             8
interaction.plot(dat$parenting, dat$academic, dat$drop)

Comments? Main effects? Interactions?

3. Compute the odds ratio manually comparing the academic performances for each parenting style

Firstly we compute the probability of dropout for each category:

agg <- aggregate(drop ~ parenting + academic, FUN = mean, data = dat)
agg
##       parenting academic       drop
## 1    neglectful      low 0.37894737
## 2    permissive      low 0.19480519
## 3 authoritative      low 0.15000000
## 4 authoritarian      low 0.20000000
## 5    neglectful     high 0.29166667
## 6    permissive     high 0.01333333
## 7 authoritative     high 0.03846154
## 8 authoritarian     high 0.13333333

Then we can compute the odds of the probabilities and the odds ratios

odds <- function(p) p / (1 - p)
agg$odds <- odds(agg$drop)

ors <- agg$odds[agg$academic == "high"] / agg$odds[agg$academic == "low"]
names(ors) <- unique(agg$parenting)
ors
##    neglectful    permissive authoritative authoritarian 
##    0.67483660    0.05585586    0.22666667    0.61538462

Comments?

4. Model fitting with glm() using the dataset in the binary form

Let’s start fitting the null model:

fit0 <- glm(drop ~ 1, data = dat, family = binomial(link = "logit"))
summary(fit0)
## 
## Call:
## glm(formula = drop ~ 1, family = binomial(link = "logit"), data = dat)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -0.6025  -0.6025  -0.6025  -0.6025   1.8951  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -1.6142     0.1202  -13.43   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 449.49  on 499  degrees of freedom
## Residual deviance: 449.49  on 499  degrees of freedom
## AIC: 451.49
## 
## Number of Fisher Scoring iterations: 3

The intercept is the overall odds of dropout:

exp(coef(fit0))
## (Intercept) 
##   0.1990408
plogis(coef(fit0))
## (Intercept) 
##       0.166
mean(dat$drop)
## [1] 0.166

Let’s now fit a model with the two main effects:

fit1 <- glm(drop ~ academic + parenting, data = dat, family = binomial(link = "logit"))
summary(fit1)
## 
## Call:
## glm(formula = drop ~ academic + parenting, family = binomial(link = "logit"), 
##     data = dat)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.0161  -0.5696  -0.3506  -0.3036   2.4902  
## 
## Coefficients:
##                        Estimate Std. Error z value Pr(>|z|)    
## (Intercept)             -0.3921     0.1985  -1.975 0.048216 *  
## academichigh            -1.0221     0.3030  -3.373 0.000742 ***
## parentingpermissive     -1.3446     0.3335  -4.031 5.55e-05 ***
## parentingauthoritative  -1.6402     0.4670  -3.512 0.000444 ***
## parentingauthoritarian  -0.7550     0.3412  -2.212 0.026935 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 449.49  on 499  degrees of freedom
## Residual deviance: 392.66  on 495  degrees of freedom
## AIC: 402.66
## 
## Number of Fisher Scoring iterations: 5

Comments?

Let’s now fit the interaction model:

fit2 <- glm(drop ~ academic * parenting, data = dat, family = binomial(link = "logit"))
summary(fit2)
## 
## Call:
## glm(formula = drop ~ academic * parenting, family = binomial(link = "logit"), 
##     data = dat)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -0.9760  -0.6583  -0.2801  -0.1638   2.9385  
## 
## Coefficients:
##                                     Estimate Std. Error z value Pr(>|z|)   
## (Intercept)                         -0.49402    0.21149  -2.336  0.01950 * 
## academichigh                        -0.39328    0.49639  -0.792  0.42820   
## parentingpermissive                 -0.92507    0.35710  -2.590  0.00958 **
## parentingauthoritative              -1.24058    0.66097  -1.877  0.06053 . 
## parentingauthoritarian              -0.89228    0.42850  -2.082  0.03731 * 
## academichigh:parentingpermissive    -2.49170    1.15811  -2.152  0.03144 * 
## academichigh:parentingauthoritative -1.09099    0.94793  -1.151  0.24976   
## academichigh:parentingauthoritarian -0.09222    0.72769  -0.127  0.89915   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 449.49  on 499  degrees of freedom
## Residual deviance: 384.58  on 492  degrees of freedom
## AIC: 400.58
## 
## Number of Fisher Scoring iterations: 6

5. Model fitting with glm() using the dataset in the aggregated form

In this case we can easily fit the same model using the aggregated form. The aggregated form is a dataset without 1s and 0s but counting the number of 1s for each condition.

dat_agg <- dat |> 
    group_by(academic, parenting) |> 
    summarise(drop_1 = sum(drop),
              drop_0 = sum(drop == 0)) |> 
    data.frame()

dat_agg$drop_tot <- dat_agg$drop_1 + dat_agg$drop_0

Now we have a column with the number of 1s and a column with the total. Then we can also compute the number of 0s:

dat_agg
##   academic     parenting drop_1 drop_0 drop_tot
## 1      low    neglectful     36     59       95
## 2      low    permissive     15     62       77
## 3      low authoritative      3     17       20
## 4      low authoritarian      9     36       45
## 5     high    neglectful      7     17       24
## 6     high    permissive      1     74       75
## 7     high authoritative      4    100      104
## 8     high authoritarian      8     52       60

The two dataset (dat and dat_agg) contains the same information. Let’s now fit the same models as before:

fit0_agg <- glm(cbind(drop_1, drop_0) ~ 1, data = dat_agg, family = binomial(link = "logit"))
summary(fit0)
## 
## Call:
## glm(formula = drop ~ 1, family = binomial(link = "logit"), data = dat)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -0.6025  -0.6025  -0.6025  -0.6025   1.8951  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -1.6142     0.1202  -13.43   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 449.49  on 499  degrees of freedom
## Residual deviance: 449.49  on 499  degrees of freedom
## AIC: 451.49
## 
## Number of Fisher Scoring iterations: 3

Let’s now fit a model with the two main effects:

fit1_agg <- glm(cbind(drop_1, drop_0) ~ academic + parenting, data = dat_agg, family = binomial(link = "logit"))
summary(fit1_agg)
## 
## Call:
## glm(formula = cbind(drop_1, drop_0) ~ academic + parenting, family = binomial(link = "logit"), 
##     data = dat_agg)
## 
## Deviance Residuals: 
##       1        2        3        4        5        6        7        8  
## -0.4840   1.0677   0.4590  -0.6573   1.1269  -2.0280  -0.3309   0.7549  
## 
## Coefficients:
##                        Estimate Std. Error z value Pr(>|z|)    
## (Intercept)             -0.3921     0.1985  -1.975 0.048216 *  
## academichigh            -1.0221     0.3030  -3.373 0.000742 ***
## parentingpermissive     -1.3446     0.3335  -4.031 5.54e-05 ***
## parentingauthoritative  -1.6402     0.4670  -3.512 0.000444 ***
## parentingauthoritarian  -0.7550     0.3412  -2.212 0.026935 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 64.902  on 7  degrees of freedom
## Residual deviance:  8.079  on 3  degrees of freedom
## AIC: 46.507
## 
## Number of Fisher Scoring iterations: 4

Comments?

Let’s now fit the interaction model:

fit2_agg <- glm(cbind(drop_1, drop_0) ~ academic * parenting, data = dat_agg, family = binomial(link = "logit"))
summary(fit2_agg)
## 
## Call:
## glm(formula = cbind(drop_1, drop_0) ~ academic * parenting, family = binomial(link = "logit"), 
##     data = dat_agg)
## 
## Deviance Residuals: 
## [1]  0  0  0  0  0  0  0  0
## 
## Coefficients:
##                                     Estimate Std. Error z value Pr(>|z|)   
## (Intercept)                         -0.49402    0.21149  -2.336  0.01950 * 
## academichigh                        -0.39328    0.49639  -0.792  0.42820   
## parentingpermissive                 -0.92507    0.35710  -2.590  0.00958 **
## parentingauthoritative              -1.24058    0.66097  -1.877  0.06053 . 
## parentingauthoritarian              -0.89228    0.42850  -2.082  0.03731 * 
## academichigh:parentingpermissive    -2.49170    1.15876  -2.150  0.03153 * 
## academichigh:parentingauthoritative -1.09099    0.94793  -1.151  0.24976   
## academichigh:parentingauthoritarian -0.09222    0.72769  -0.127  0.89915   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 6.4902e+01  on 7  degrees of freedom
## Residual deviance: 1.3101e-14  on 0  degrees of freedom
## AIC: 44.428
## 
## Number of Fisher Scoring iterations: 4

Do you notice any difference with the previous models?

6. Plotting and interpreting effects of both models

Let’s start by plotting the full model (in both forms):

plot(allEffects(fit2))

plot(allEffects(fit2_agg))

Let’s compare the coefficients:

car::compareCoefs(fit2, fit2_agg)
## Calls:
## 1: glm(formula = drop ~ academic * parenting, family = binomial(link = 
##   "logit"), data = dat)
## 2: glm(formula = cbind(drop_1, drop_0) ~ academic * parenting, family = 
##   binomial(link = "logit"), data = dat_agg)
## 
##                                     Model 1 Model 2
## (Intercept)                          -0.494  -0.494
## SE                                    0.211   0.211
##                                                    
## academichigh                         -0.393  -0.393
## SE                                    0.496   0.496
##                                                    
## parentingpermissive                  -0.925  -0.925
## SE                                    0.357   0.357
##                                                    
## parentingauthoritative               -1.241  -1.241
## SE                                    0.661   0.661
##                                                    
## parentingauthoritarian               -0.892  -0.892
## SE                                    0.429   0.429
##                                                    
## academichigh:parentingpermissive      -2.49   -2.49
## SE                                     1.16    1.16
##                                                    
## academichigh:parentingauthoritative  -1.091  -1.091
## SE                                    0.948   0.948
##                                                    
## academichigh:parentingauthoritarian -0.0922 -0.0922
## SE                                   0.7277  0.7277
## 

Now let’s interpret the effects. The “new” component is the interaction between two categorical variable. If the coefficients with one categorical variable is the log(Odds Ratio), the interaction is the difference between the two odds ratios. When transformed on the probability scale, the parameter is the ratio between odds ratios.

This is the odds ratio for the academic effect with neglectful parenting (i.e., the reference level):

coefs <- coef(fit2_agg)

coefs["academichigh"] # log odds ratio
## academichigh 
##   -0.3932847
exp(coefs["academichigh"]) # odds ratio
## academichigh 
##    0.6748366
agg
##       parenting academic       drop       odds
## 1    neglectful      low 0.37894737 0.61016949
## 2    permissive      low 0.19480519 0.24193548
## 3 authoritative      low 0.15000000 0.17647059
## 4 authoritarian      low 0.20000000 0.25000000
## 5    neglectful     high 0.29166667 0.41176471
## 6    permissive     high 0.01333333 0.01351351
## 7 authoritative     high 0.03846154 0.04000000
## 8 authoritarian     high 0.13333333 0.15384615
low <- agg$odds[agg$parenting == "neglectful" & agg$academic == "low"]
high <- agg$odds[agg$parenting == "neglectful" & agg$academic == "high"]

high/low
## [1] 0.6748366
log(high/low)
## [1] -0.3932847

Then the academichigh:parentingpermissive is the difference of the log odds ratios for low vs high for neglectful and permissive parenting styles.

coefs["academichigh:parentingpermissive"]
## academichigh:parentingpermissive 
##                        -2.491696
exp(coefs["academichigh:parentingpermissive"])
## academichigh:parentingpermissive 
##                       0.08276945
low_neg <- agg$odds[agg$parenting == "neglectful" & agg$academic == "low"]
high_neg <- agg$odds[agg$parenting == "neglectful" & agg$academic == "high"]
low_per <- agg$odds[agg$parenting == "permissive" & agg$academic == "low"]
high_per <- agg$odds[agg$parenting == "permissive" & agg$academic == "high"]

log((high_per / low_per)) - log((high_neg / low_neg))
## [1] -2.491696
(high_per / low_per) / (high_neg / low_neg)
## [1] 0.08276945

Similarly to the odds ratio, the ratio between two odds ratios can be interpreted in the same way:

  • OR1 / OR2 > 1: the odds ratio for the numerator condition is x times higher than the odds ratio for the denominator condition
  • OR1 / OR2 < 1: the odds ratio for the numerator condition is x times lower than the odds ratio for the denominator condition

Of course, the best way is using the predict() function:

preds <- expand.grid(parenting = c("neglectful", "permissive"),
                     academic = c("low", "high"))
preds$pr <- predict(fit2_agg, newdata = preds)

with(preds, (exp(pr)[4] / exp(pr)[2]) / (exp(pr)[3] / exp(pr)[1]))
## [1] 0.08276945

Why the residual deviance is different between the aggregated and the binary model?

deviance(fit2)
## [1] 384.5843
deviance(fit2_agg)
## [1] 1.310063e-14

This is the main difference between the two approaches. Actually we do not have to compare the deviance of the two models e.g., the aggregated form is better because it is closer to 0 but we always need to compare the model with the null deviance.

anova(fit0, fit2, test = "LRT")
## Analysis of Deviance Table
## 
## Model 1: drop ~ 1
## Model 2: drop ~ academic * parenting
##   Resid. Df Resid. Dev Df Deviance  Pr(>Chi)    
## 1       499     449.49                          
## 2       492     384.58  7   64.902 1.573e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(fit0_agg, fit2_agg, test = "LRT")
## Analysis of Deviance Table
## 
## Model 1: cbind(drop_1, drop_0) ~ 1
## Model 2: cbind(drop_1, drop_0) ~ academic * parenting
##   Resid. Df Resid. Dev Df Deviance  Pr(>Chi)    
## 1         7     64.902                          
## 2         0      0.000  7   64.902 1.573e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

As you can see the ratio is the same, thus the two deviances are on a different scale. The two models explains the same amount of (relative) deviance.

Why?

The reason is that we are computing the residual deviance from observed 0 and 1 vs observed counts.

# aggregated model deviance
-2*(sum(log(dbinom(dat_agg$drop_1, dat_agg$drop_tot, fitted(fit2_agg))) - log(dbinom(dat_agg$drop_1, dat_agg$drop_tot, dat_agg$drop_1/dat_agg$drop_tot))))
## [1] 0
# binary model deviance
-2*(sum(log(dbinom(dat$drop, 1, fitted(fit2))) - log(dbinom(dat$drop, 1, dat$drop))))
## [1] 384.5843

In a way it is more difficult to predict 0 and 1 compared to counts thus the residuals and the residual deviance will be always higher. Model coefficients, standard error and tests are the same.

Where the two models are not the same? Depends on the type of variables. Let’s add a new column to our binary dataset with the age of each student:

dat$age <- round(runif(nrow(dat), 12, 18))

Now, if we want to include the age as predictor, we need to use the binary form because we have one value for each student. We are including a predictor at the level of the 0-1 values.

fit3 <- glm(drop ~ academic * parenting + age , data = dat, family = binomial(link = "logit"))
summary(fit3)
## 
## Call:
## glm(formula = drop ~ academic * parenting + age, family = binomial(link = "logit"), 
##     data = dat)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -0.9921  -0.6603  -0.2852  -0.1648   2.9395  
## 
## Coefficients:
##                                     Estimate Std. Error z value Pr(>|z|)  
## (Intercept)                         -0.71255    1.14001  -0.625   0.5319  
## academichigh                        -0.39441    0.49647  -0.794   0.4269  
## parentingpermissive                 -0.91797    0.35890  -2.558   0.0105 *
## parentingauthoritative              -1.23589    0.66141  -1.869   0.0617 .
## parentingauthoritarian              -0.89715    0.42929  -2.090   0.0366 *
## age                                  0.01443    0.07396   0.195   0.8453  
## academichigh:parentingpermissive    -2.49863    1.15868  -2.156   0.0310 *
## academichigh:parentingauthoritative -1.09852    0.94875  -1.158   0.2469  
## academichigh:parentingauthoritarian -0.08633    0.72837  -0.119   0.9057  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 449.49  on 499  degrees of freedom
## Residual deviance: 384.55  on 491  degrees of freedom
## AIC: 402.55
## 
## Number of Fisher Scoring iterations: 6

When we have predictors at the 0-1 levels, we need to use the binary form.

A little (visual) demonstration:

x <- seq(0, 1, 0.01)
dat <- data.frame(
    x = rep(x, 10)
)

dat$lp <- plogis(qlogis(0.01) + 8*dat$x)
dat$y <- rbinom(nrow(dat), 1, dat$lp)
head(dat)
##      x         lp y
## 1 0.00 0.01000000 0
## 2 0.01 0.01082386 0
## 3 0.02 0.01171478 0
## 4 0.03 0.01267810 0
## 5 0.04 0.01371954 0
## 6 0.05 0.01484523 1

Let’s fit the model in the binary form:

# model prediction
fit <- glm(y ~ x, data = dat, family = binomial())

# equivalent to predict()
pi <- plogis(coef(fit)[1] + coef(fit)[2]*unique(dat$x))

Let’s fit the mode in the binomial form:

# aggregated form
dat_agg <- aggregate(y ~ x, FUN = sum, data = dat)
dat_agg$n <- 10 # total trials
dat_agg$f <- dat_agg$n - dat_agg$y
dat_agg$p <- dat_agg$y / dat_agg$n

head(dat_agg)
##      x y  n  f   p
## 1 0.00 0 10 10 0.0
## 2 0.01 0 10 10 0.0
## 3 0.02 0 10 10 0.0
## 4 0.03 0 10 10 0.0
## 5 0.04 1 10  9 0.1
## 6 0.05 1 10  9 0.1
fit2 <- glm(cbind(y, f) ~ x, data = dat_agg, family = binomial())
pi <- plogis(coef(fit2)[1] + coef(fit2)[2]*dat_agg$x)

The residuals (thus the residual deviance) will be always larger in the binary model (but the coefficients are the same):

par(mfrow = c(1,2))

jit <- runif(nrow(dat), -0.03, 0.03)
plot((y + jit) ~ x, data = dat, ylab = "y", xlab = "x",
     main = "Binary Form")
lines(unique(dat$x), pi, lwd = 2, col = "red")

plot(y/n ~ x, data = dat_agg, ylab = "y",
     main = "Binomial Form")
lines(dat_agg$x, pi, lwd = 2, col = "red")

This is the reason why binary model have also strange residuals:

# also residuals
par(mfrow = c(1,2))
plot(fitted(fit), residuals(fit), main = "Binary Form")
plot(fitted(fit2), residuals(fit2), main = "Binomial Form")

LS0tDQp0aXRsZTogIlN0YXRpc3RpY2FsIE1ldGhvZHMgYW5kIERhdGEgQW5hbHlzaXMgaW4gRGV2ZWxvcG1lbnRhbCBQc3ljaG9sb2d5Ig0Kc3VidGl0bGU6ICJMYWIgMTIiDQphdXRob3I6ICJGaWxpcHBvIEdhbWJhcm90YSINCm91dHB1dDogDQogICAgaHRtbF9kb2N1bWVudDoNCiAgICAgICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgICAgIHRvYzogdHJ1ZQ0KICAgICAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KZGF0ZTogIlVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAiDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBkZXYgPSAic3ZnIikNCmBgYA0KDQpgYGB7ciBwYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRldnRvb2xzOjpsb2FkX2FsbCgpICMgaWYgdXNpbmcgdGhlIHJwcm9qZWN0IGRvd2xvYWRlZCBmcm9tIHRoZSBzbGlkZXMNCiMgc291cmNlKCJ1dGlscy1nbG0uUiIpICMgaWYgdXNpbmcgYSBzdGFuZGFyZCBzZXR1cA0KbGlicmFyeShoZXJlKQ0KbGlicmFyeSh0aWR5cikgIyBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZHBseXIpICMgZm9yIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KGdncGxvdDIpICMgcGxvdHRpbmcNCmxpYnJhcnkoY2FyKSAjIGdlbmVyYWwgdXRpbGl0aWVzDQpsaWJyYXJ5KGVmZmVjdHMpICMgZm9yIGV4dHJhY3RpbmcgYW5kIHBsb3R0aW5nIGVmZmVjdHMgDQpsaWJyYXJ5KGVtbWVhbnMpICMgZm9yIG1hcmdpbmFsIG1lYW5zDQpgYGANCg0KYGBge3Igb3B0aW9ucywgaW5jbHVkZSA9IEZBTFNFfQ0KdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTUpKQ0KYGBgDQoNCmBgYHtyIHNjcmlwdCwgZWNobz1GQUxTRX0NCiMjIExpbmsgaW4gR2l0aHViIHJlcG8NCmRvd25sb2FkdGhpczo6ZG93bmxvYWRfbGluaygNCiAgbGluayA9IGRvd25sb2FkX2xpbmsoImh0dHBzOi8vZ2l0aHViLmNvbS9zdGF0LXRlYWNoaW5nL1NNREEtMjAyMy9ibG9iL21hc3Rlci9sYWJzL2xhYjEyLlIiKSwNCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIHRoZSBSIHNjcmlwdCIsDQogIGJ1dHRvbl90eXBlID0gImRhbmdlciIsDQogIGhhc19pY29uID0gVFJVRSwNCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwNCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQ0KKQ0KYGBgDQoNCmBgYHtyIGRhdGEtZG93biwgZWNobz1GQUxTRX0NCiMjIExpbmsgaW4gR2l0aHViIHJlcG8NCmRvd25sb2FkdGhpczo6ZG93bmxvYWRfZmlsZSgNCiAgcGF0aCA9IGhlcmU6OmhlcmUoImRhdGEiLCAiZHJvcG91dC5jc3YiKSwNCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIHRoZSBkYXRhc2V0IiwNCiAgYnV0dG9uX3R5cGUgPSAiZGFuZ2VyIiwNCiAgaGFzX2ljb24gPSBUUlVFLA0KICBpY29uID0gImZhIGZhLXNhdmUiLA0KICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFDQopDQpgYGANCg0KYGBge3IgbG9hZGluZy1kYXRhfQ0KZGF0IDwtIHJlYWQuY3N2KGhlcmUoImRhdGEiLCAiZHJvcG91dC5jc3YiKSkNCmBgYA0KDQojIE92ZXJ2aWV3DQoNClRoaXMgZGF0YXNldCBgZHJvcG91dC5jc3ZgIGNvbnRhaW5zIGRhdGEgYWJvdXQgZHJvcG91dHMgZHVyaW5nIGhpZ2ggc2Nob29sIGZvciBgbnJvdyhkYXQpYCBhZG9sZXNjZW50cy4gV2Ugd2FudCB0byB1bmRlcnN0YW5kIHRoZSBpbXBhY3Qgb2YgdGhlIHBhcmVudGluZyBzdHlsZSAoYHIgcGFzdGUodW5pcXVlKGRhdCRwYXJlbnRpbmcpLCBjb2xsYXBzZSA9ICIsICIpYCkgYW5kIHRoZSBhY2FkZW1pYyBwZXJmb3JtYW5jZSAoYHIgcGFzdGUodW5pcXVlKGRhdCRhY2FkZW1pYyksIGNvbGxhcHNlID0gIiwgIilgKSBvbiB0aGUgcHJvYmFiaWxpdHkgb2YgZHJvcG91dCAoMCA9IG5vIGRyb3BvdXQsIDEgPSBkcm9wb3V0KS4NCg0KMS4gSW1wb3J0aW5nIGRhdGEgYW5kIG92ZXJhbGwgY2hlY2sNCjIuIEV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgb2YgcHJlZGljdG9ycyBhbmQgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBwcmVkaWN0b3JzIGFuZCB0aGUgbnVtYmVyIG9mIHdvcmRzDQozLiBDb21wdXRlIHRoZSBvZGRzIHJhdGlvIG1hbnVhbGx5IGNvbXBhcmluZyB0aGUgYWNhZGVtaWMgcGVyZm9ybWFuY2VzIGZvciBlYWNoIHBhcmVudGluZyBzdHlsZQ0KNC4gTW9kZWwgZml0dGluZyB3aXRoIGBnbG0oKWAgdXNpbmcgdGhlIGRhdGFzZXQgaW4gdGhlIGJpbmFyeSBmb3JtDQo1LiBNb2RlbCBmaXR0aW5nIHdpdGggYGdsbSgpYCB1c2luZyB0aGUgZGF0YXNldCBpbiB0aGUgYWdncmVnYXRlZCBmb3JtDQo2LiBQbG90dGluZyBhbmQgaW50ZXJwcmV0aW5nIGVmZmVjdHMgb2YgYm90aCBtb2RlbHMNCiAgICAtIGlzIHRoZXJlIGFueSBkaWZmZXJlbmNlPyB0cnkgdG8gdW5kZXJzdGFuZCB3aHkNCjcuIFdyaXRlIGEgYnJpZWYgcGFyYWdyYXBoIHJlcG9ydGluZyB0aGUgZWZmZWN0cyB3aXRoIHlvdXIgaW50ZXJwcmV0YXRpb24NCg0KIyAxLiBJbXBvcnRpbmcgZGF0YSBhbmQgb3ZlcmFsbCBjaGVjaw0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KZGF0IDwtIHJlYWQuY3N2KCJkYXRhL2Ryb3BvdXQuY3N2IikNCmBgYA0KDQpgYGB7cn0NCnN0cihkYXQpDQpgYGANCg0KQ2hlY2sgZm9yIGBOQWAgdmFsdWVzOg0KDQpgYGB7cn0NCmNvdW50X25hKGRhdCkgIyBmcm9tIHV0aWxzLWdsbS5SDQoNCiMgZXF1aXZhbGVudCB0byBzYXBwbHkoZGF0LCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQ0KYGBgDQoNCkV2ZXJ5dGhpbmcgc2VlbXMgZ29vZCwgd2UgZG8gbm90IGhhdmUgYE5BYCB2YWx1ZXMuDQoNCkxldCdzIGNvbnZlcnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGludG8gZmFjdG9yIHNldHRpbmcgdGhlIGFwcHJvcHJpYXRlIG9yZGVyOg0KDQotIGBwYXJlbnRpbmdgOiBuZWdsZWN0ZnVsLCBwZXJtaXNzaXZlLCBhdXRob3JpdGF0aXZlLCBhdXRob3JpdGFyaWFuDQotIGBhY2FkZW1pY2A6IGxvdywgaGlnaA0KDQpgYGB7cn0NCmRhdCRwYXJlbnRpbmcgPC0gZmFjdG9yKGRhdCRwYXJlbnRpbmcsIGxldmVscyA9IGMoIm5lZ2xlY3RmdWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVybWlzc2l2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdXRob3JpdGF0aXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImF1dGhvcml0YXJpYW4iKSkNCmRhdCRhY2FkZW1pYyA8LSBmYWN0b3IoZGF0JGFjYWRlbWljLCBsZXZlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKQ0KDQpsZXZlbHMoZGF0JHBhcmVudGluZykNCmxldmVscyhkYXQkYWNhZGVtaWMpDQpgYGANCg0KIyAyLiBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzDQoNCmBgYHtyfQ0Kc3VtbWFyeShkYXQpICMgbm90IHJlYWxseSBtZWFuaW5nZnVsDQpgYGANCg0KV2l0aCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2UgbmVlZCB0byB1c2UgYWJzb2x1dGUvcmVsYXRpdmUgZnJlcXVlbmNpZXMgYW5kIGNvbnRpbmdlbmN5IHRhYmxlcy4NCg0KTGV0J3Mgc3RhcnQgYnkgdW5pdmFyaWF0ZSBFREE6DQoNCmBgYHtyfQ0KIyBkaXN0cmlidXRpb24gb2YgcGFyZW50aW5nIHN0eWxlcw0KdGFibGUoZGF0JHBhcmVudGluZykNCg0KdGFibGUoZGF0JHBhcmVudGluZykvbnJvdyhkYXQpDQoNCiMgZGlzdHJpYnV0aW9uIG9mIGFjYWRlbWljIHBlcmZvcm1hbmNlDQp0YWJsZShkYXQkYWNhZGVtaWMpDQp0YWJsZShkYXQkYWNhZGVtaWMpL25yb3coZGF0KQ0KDQojIG92ZXJhbGwgZHJvcG91dCByYXRlDQp0YWJsZShkYXQkZHJvcCkNCnRhYmxlKGRhdCRkcm9wKS9ucm93KGRhdCkNCiMgbWVhbihkYXQkZHJvcCkgIyBkaXJlY3RseQ0KYGBgDQoNCkxldCdzIGNyZWF0ZSBhbiBvdmVyYWxsIHBsb3Q6DQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpwYXIobWZyb3cgPSBjKDMsMSkpDQpiYXJwbG90KHRhYmxlKGRhdCRwYXJlbnRpbmcpL25yb3coZGF0KSwgbWFpbiA9ICJQYXJlbnRpbmciKQ0KYmFycGxvdCh0YWJsZShkYXQkYWNhZGVtaWMpL25yb3coZGF0KSwgbWFpbiA9ICJBY2FkZW1pYyIpDQpiYXJwbG90KHRhYmxlKGRhdCRkcm9wKS9ucm93KGRhdCksIG1haW4gPSAiRHJvcG91dCIpDQpgYGANCg0KSG93IHRvIGludGVycHJldD8NCg0KTGV0J3Mgbm93IGV4cGxvcmUgdGhlIGJpdmFyaWF0ZSByZWxhdGlvbnNoaXBzOg0KDQpgYGB7cn0NCnRhYmxlKGRhdCRwYXJlbnRpbmcsIGRhdCRhY2FkZW1pYykNCnRhYmxlKGRhdCRhY2FkZW1pYywgZGF0JHBhcmVudGluZykNCmBgYA0KDQpXZSBjYW4gY3JlYXRlIHRhYmxlcyB3aXRoIHJlbGF0aXZlIGZyZXF1ZW5jaWVzOg0KDQpgYGB7cn0NCnByb3AudGFibGUodGFibGUoZGF0JHBhcmVudGluZywgZGF0JGFjYWRlbWljKSwgMSkgIyBieSByb3cNCnByb3AudGFibGUodGFibGUoZGF0JHBhcmVudGluZywgZGF0JGFjYWRlbWljKSwgMikgIyBieSBjb2x1bW4NCmBgYA0KDQouLi5hbmQgc29tZSBwbG90czoNCg0KYGBge3J9DQpiYXJwbG90KHByb3AudGFibGUodGFibGUoZGF0JHBhcmVudGluZywgZGF0JGFjYWRlbWljKSwgMiksIA0KICAgICAgICBiZXNpZGUgPSBUUlVFLA0KICAgICAgICBjb2wgPSBjKCJmaXJlYnJpY2siLCAibGlnaHRibHVlIiwgImRhcmtncmVlbiIsICJwaW5rIikpDQoNCmxlZ2VuZCg0LjUsMC40LCBsZWdlbmQgPSBsZXZlbHMoZGF0JHBhcmVudGluZyksIA0KICAgICAgIGZpbGwgPSBjKCJmaXJlYnJpY2siLCAibGlnaHRibHVlIiwgImRhcmtncmVlbiIsICJwaW5rIikpDQpgYGANCg0KT2YgY291cnNlLCB3ZSBjYW4gY29tcHV0ZSB0aGUgcmVsYXRpdmUgZnJlcXVlbmNpZXMgaW4gbXVsdGlwbGUgd2F5cyAodG90YWwsIHJvdyBvciBjb2x1bW4gd2lzZSkuDQoNClRoZW4gdGhlIGJpdmFyaWF0ZSByZWxhdGlvbnNoaXBzIHdpdGggdGhlIGBkcm9wYCB2YXJpYWJsZToNCg0KYGBge3J9DQp0YWJsZShkYXQkcGFyZW50aW5nLCBkYXQkZHJvcCkNCg0KcHJvcC50YWJsZSh0YWJsZShkYXQkcGFyZW50aW5nLCBkYXQkZHJvcCksIDEpDQoNCnRhYmxlKGRhdCRhY2FkZW1pYywgZGF0JGRyb3ApDQoNCnByb3AudGFibGUodGFibGUoZGF0JGFjYWRlbWljLCBkYXQkZHJvcCksIDEpDQpgYGANCg0KQW5kIHRoZSBwbG90czoNCg0KYGBge3J9DQpiYXJwbG90KHByb3AudGFibGUodGFibGUoZGF0JHBhcmVudGluZywgZGF0JGRyb3ApLCAxKSwgDQogICAgICAgIGJlc2lkZSA9IFRSVUUsDQogICAgICAgIGNvbCA9IGMoImZpcmVicmljayIsICJsaWdodGJsdWUiLCAiZGFya2dyZWVuIiwgInBpbmsiKSkNCg0KbGVnZW5kKDcsIDAuOCwgbGVnZW5kID0gbGV2ZWxzKGRhdCRwYXJlbnRpbmcpLCANCiAgICAgICBmaWxsID0gYygiZmlyZWJyaWNrIiwgImxpZ2h0Ymx1ZSIsICJkYXJrZ3JlZW4iLCAicGluayIpKQ0KDQoNCmJhcnBsb3QocHJvcC50YWJsZSh0YWJsZShkYXQkcGFyZW50aW5nLCBkYXQkZHJvcCksIDEpLCANCiAgICAgICAgYmVzaWRlID0gVFJVRSwNCiAgICAgICAgY29sID0gYygiZmlyZWJyaWNrIiwgImxpZ2h0Ymx1ZSIsICJkYXJrZ3JlZW4iLCAicGluayIpKQ0KDQpsZWdlbmQoNywgMC44LCBsZWdlbmQgPSBsZXZlbHMoZGF0JHBhcmVudGluZyksIA0KICAgICAgIGZpbGwgPSBjKCJmaXJlYnJpY2siLCAibGlnaHRibHVlIiwgImRhcmtncmVlbiIsICJwaW5rIikpDQoNCg0KYmFycGxvdChwcm9wLnRhYmxlKHRhYmxlKGRhdCRhY2FkZW1pYywgZGF0JGRyb3ApLCAxKSwgDQogICAgICAgIGJlc2lkZSA9IFRSVUUsDQogICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIikpDQoNCmxlZ2VuZCg0LCAwLjUsIGxlZ2VuZCA9IGxldmVscyhkYXQkYWNhZGVtaWMpLCANCiAgICAgICBmaWxsID0gIGMoInJlZCIsICJibHVlIikpDQpgYGANCg0KRmluYWxseSB3ZSBjYW4gcmVwcmVzZW50IHRoZSBmdWxsIHJlbGF0aW9uc2hpcDoNCg0KYGBge3J9DQp0YWJsZShkYXQkZHJvcCwgZGF0JHBhcmVudGluZywgZGF0JGFjYWRlbWljKQ0KaW50ZXJhY3Rpb24ucGxvdChkYXQkcGFyZW50aW5nLCBkYXQkYWNhZGVtaWMsIGRhdCRkcm9wKQ0KYGBgDQoNCkNvbW1lbnRzPyBNYWluIGVmZmVjdHM/IEludGVyYWN0aW9ucz8NCg0KIyAzLiBDb21wdXRlIHRoZSBvZGRzIHJhdGlvIG1hbnVhbGx5IGNvbXBhcmluZyB0aGUgYWNhZGVtaWMgcGVyZm9ybWFuY2VzIGZvciBlYWNoIHBhcmVudGluZyBzdHlsZQ0KDQpGaXJzdGx5IHdlIGNvbXB1dGUgdGhlIHByb2JhYmlsaXR5IG9mIGRyb3BvdXQgZm9yIGVhY2ggY2F0ZWdvcnk6DQoNCmBgYHtyfQ0KYWdnIDwtIGFnZ3JlZ2F0ZShkcm9wIH4gcGFyZW50aW5nICsgYWNhZGVtaWMsIEZVTiA9IG1lYW4sIGRhdGEgPSBkYXQpDQphZ2cNCmBgYA0KDQpUaGVuIHdlIGNhbiBjb21wdXRlIHRoZSBvZGRzIG9mIHRoZSBwcm9iYWJpbGl0aWVzIGFuZCB0aGUgb2RkcyByYXRpb3MNCg0KYGBge3J9DQpvZGRzIDwtIGZ1bmN0aW9uKHApIHAgLyAoMSAtIHApDQphZ2ckb2RkcyA8LSBvZGRzKGFnZyRkcm9wKQ0KDQpvcnMgPC0gYWdnJG9kZHNbYWdnJGFjYWRlbWljID09ICJoaWdoIl0gLyBhZ2ckb2Rkc1thZ2ckYWNhZGVtaWMgPT0gImxvdyJdDQpuYW1lcyhvcnMpIDwtIHVuaXF1ZShhZ2ckcGFyZW50aW5nKQ0Kb3JzDQpgYGANCg0KQ29tbWVudHM/DQoNCiMgNC4gTW9kZWwgZml0dGluZyB3aXRoIGBnbG0oKWAgdXNpbmcgdGhlIGRhdGFzZXQgaW4gdGhlIGJpbmFyeSBmb3JtDQoNCkxldCdzIHN0YXJ0IGZpdHRpbmcgdGhlIG51bGwgbW9kZWw6DQoNCmBgYHtyfQ0KZml0MCA8LSBnbG0oZHJvcCB+IDEsIGRhdGEgPSBkYXQsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkNCnN1bW1hcnkoZml0MCkNCmBgYA0KDQpUaGUgaW50ZXJjZXB0IGlzIHRoZSBvdmVyYWxsIG9kZHMgb2YgZHJvcG91dDoNCg0KYGBge3J9DQpleHAoY29lZihmaXQwKSkNCnBsb2dpcyhjb2VmKGZpdDApKQ0KbWVhbihkYXQkZHJvcCkNCmBgYA0KDQpMZXQncyBub3cgZml0IGEgbW9kZWwgd2l0aCB0aGUgdHdvIG1haW4gZWZmZWN0czoNCg0KYGBge3J9DQpmaXQxIDwtIGdsbShkcm9wIH4gYWNhZGVtaWMgKyBwYXJlbnRpbmcsIGRhdGEgPSBkYXQsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkNCnN1bW1hcnkoZml0MSkNCmBgYA0KDQpDb21tZW50cz8NCg0KTGV0J3Mgbm93IGZpdCB0aGUgaW50ZXJhY3Rpb24gbW9kZWw6DQoNCmBgYHtyfQ0KZml0MiA8LSBnbG0oZHJvcCB+IGFjYWRlbWljICogcGFyZW50aW5nLCBkYXRhID0gZGF0LCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IikpDQpzdW1tYXJ5KGZpdDIpDQpgYGANCg0KIyA1LiBNb2RlbCBmaXR0aW5nIHdpdGggYGdsbSgpYCB1c2luZyB0aGUgZGF0YXNldCBpbiB0aGUgYWdncmVnYXRlZCBmb3JtDQoNCkluIHRoaXMgY2FzZSB3ZSBjYW4gZWFzaWx5IGZpdCB0aGUgc2FtZSBtb2RlbCB1c2luZyB0aGUgYWdncmVnYXRlZCBmb3JtLiBUaGUgYWdncmVnYXRlZCBmb3JtIGlzIGEgZGF0YXNldCB3aXRob3V0IDFzIGFuZCAwcyBidXQgY291bnRpbmcgdGhlIG51bWJlciBvZiAxcyBmb3IgZWFjaCBjb25kaXRpb24uDQoNCmBgYHtyfQ0KZGF0X2FnZyA8LSBkYXQgfD4gDQogICAgZ3JvdXBfYnkoYWNhZGVtaWMsIHBhcmVudGluZykgfD4gDQogICAgc3VtbWFyaXNlKGRyb3BfMSA9IHN1bShkcm9wKSwNCiAgICAgICAgICAgICAgZHJvcF8wID0gc3VtKGRyb3AgPT0gMCkpIHw+IA0KICAgIGRhdGEuZnJhbWUoKQ0KDQpkYXRfYWdnJGRyb3BfdG90IDwtIGRhdF9hZ2ckZHJvcF8xICsgZGF0X2FnZyRkcm9wXzANCmBgYA0KDQpOb3cgd2UgaGF2ZSBhIGNvbHVtbiB3aXRoIHRoZSBudW1iZXIgb2YgMXMgYW5kIGEgY29sdW1uIHdpdGggdGhlIHRvdGFsLiBUaGVuIHdlIGNhbiBhbHNvIGNvbXB1dGUgdGhlIG51bWJlciBvZiAwczoNCg0KYGBge3J9DQpkYXRfYWdnDQpgYGANCg0KVGhlIHR3byBkYXRhc2V0IChgZGF0YCBhbmQgYGRhdF9hZ2dgKSBjb250YWlucyB0aGUgc2FtZSBpbmZvcm1hdGlvbi4gTGV0J3Mgbm93IGZpdCB0aGUgc2FtZSBtb2RlbHMgYXMgYmVmb3JlOg0KDQpgYGB7cn0NCmZpdDBfYWdnIDwtIGdsbShjYmluZChkcm9wXzEsIGRyb3BfMCkgfiAxLCBkYXRhID0gZGF0X2FnZywgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQ0Kc3VtbWFyeShmaXQwKQ0KYGBgDQoNCkxldCdzIG5vdyBmaXQgYSBtb2RlbCB3aXRoIHRoZSB0d28gbWFpbiBlZmZlY3RzOg0KDQpgYGB7cn0NCmZpdDFfYWdnIDwtIGdsbShjYmluZChkcm9wXzEsIGRyb3BfMCkgfiBhY2FkZW1pYyArIHBhcmVudGluZywgZGF0YSA9IGRhdF9hZ2csIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkNCnN1bW1hcnkoZml0MV9hZ2cpDQpgYGANCg0KQ29tbWVudHM/DQoNCkxldCdzIG5vdyBmaXQgdGhlIGludGVyYWN0aW9uIG1vZGVsOg0KDQpgYGB7cn0NCmZpdDJfYWdnIDwtIGdsbShjYmluZChkcm9wXzEsIGRyb3BfMCkgfiBhY2FkZW1pYyAqIHBhcmVudGluZywgZGF0YSA9IGRhdF9hZ2csIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkNCnN1bW1hcnkoZml0Ml9hZ2cpDQpgYGANCg0KRG8geW91IG5vdGljZSBhbnkgZGlmZmVyZW5jZSB3aXRoIHRoZSBwcmV2aW91cyBtb2RlbHM/DQoNCiMgNi4gUGxvdHRpbmcgYW5kIGludGVycHJldGluZyBlZmZlY3RzIG9mIGJvdGggbW9kZWxzDQoNCkxldCdzIHN0YXJ0IGJ5IHBsb3R0aW5nIHRoZSBmdWxsIG1vZGVsIChpbiBib3RoIGZvcm1zKToNCg0KYGBge3J9DQpwbG90KGFsbEVmZmVjdHMoZml0MikpDQpwbG90KGFsbEVmZmVjdHMoZml0Ml9hZ2cpKQ0KYGBgDQoNCkxldCdzIGNvbXBhcmUgdGhlIGNvZWZmaWNpZW50czoNCg0KYGBge3J9DQpjYXI6OmNvbXBhcmVDb2VmcyhmaXQyLCBmaXQyX2FnZykNCmBgYA0KDQpOb3cgbGV0J3MgaW50ZXJwcmV0IHRoZSBlZmZlY3RzLiBUaGUgIm5ldyIgY29tcG9uZW50IGlzIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHR3byBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gSWYgdGhlIGNvZWZmaWNpZW50cyB3aXRoIG9uZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyB0aGUgbG9nKE9kZHMgUmF0aW8pLCB0aGUgaW50ZXJhY3Rpb24gaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIG9kZHMgcmF0aW9zLiBXaGVuIHRyYW5zZm9ybWVkIG9uIHRoZSBwcm9iYWJpbGl0eSBzY2FsZSwgdGhlIHBhcmFtZXRlciBpcyB0aGUgcmF0aW8gYmV0d2VlbiBvZGRzIHJhdGlvcy4NCg0KVGhpcyBpcyB0aGUgb2RkcyByYXRpbyBmb3IgdGhlIGFjYWRlbWljIGVmZmVjdCB3aXRoIG5lZ2xlY3RmdWwgcGFyZW50aW5nIChpLmUuLCB0aGUgcmVmZXJlbmNlIGxldmVsKToNCg0KYGBge3J9DQpjb2VmcyA8LSBjb2VmKGZpdDJfYWdnKQ0KDQpjb2Vmc1siYWNhZGVtaWNoaWdoIl0gIyBsb2cgb2RkcyByYXRpbw0KZXhwKGNvZWZzWyJhY2FkZW1pY2hpZ2giXSkgIyBvZGRzIHJhdGlvDQphZ2cNCg0KbG93IDwtIGFnZyRvZGRzW2FnZyRwYXJlbnRpbmcgPT0gIm5lZ2xlY3RmdWwiICYgYWdnJGFjYWRlbWljID09ICJsb3ciXQ0KaGlnaCA8LSBhZ2ckb2Rkc1thZ2ckcGFyZW50aW5nID09ICJuZWdsZWN0ZnVsIiAmIGFnZyRhY2FkZW1pYyA9PSAiaGlnaCJdDQoNCmhpZ2gvbG93DQpsb2coaGlnaC9sb3cpDQpgYGANCg0KVGhlbiB0aGUgYGFjYWRlbWljaGlnaDpwYXJlbnRpbmdwZXJtaXNzaXZlYCBpcyB0aGUgZGlmZmVyZW5jZSBvZiB0aGUgbG9nIG9kZHMgcmF0aW9zIGZvciBsb3cgdnMgaGlnaCBmb3IgbmVnbGVjdGZ1bCBhbmQgcGVybWlzc2l2ZSBwYXJlbnRpbmcgc3R5bGVzLg0KDQpgYGB7cn0NCmNvZWZzWyJhY2FkZW1pY2hpZ2g6cGFyZW50aW5ncGVybWlzc2l2ZSJdDQpleHAoY29lZnNbImFjYWRlbWljaGlnaDpwYXJlbnRpbmdwZXJtaXNzaXZlIl0pDQpsb3dfbmVnIDwtIGFnZyRvZGRzW2FnZyRwYXJlbnRpbmcgPT0gIm5lZ2xlY3RmdWwiICYgYWdnJGFjYWRlbWljID09ICJsb3ciXQ0KaGlnaF9uZWcgPC0gYWdnJG9kZHNbYWdnJHBhcmVudGluZyA9PSAibmVnbGVjdGZ1bCIgJiBhZ2ckYWNhZGVtaWMgPT0gImhpZ2giXQ0KbG93X3BlciA8LSBhZ2ckb2Rkc1thZ2ckcGFyZW50aW5nID09ICJwZXJtaXNzaXZlIiAmIGFnZyRhY2FkZW1pYyA9PSAibG93Il0NCmhpZ2hfcGVyIDwtIGFnZyRvZGRzW2FnZyRwYXJlbnRpbmcgPT0gInBlcm1pc3NpdmUiICYgYWdnJGFjYWRlbWljID09ICJoaWdoIl0NCg0KbG9nKChoaWdoX3BlciAvIGxvd19wZXIpKSAtIGxvZygoaGlnaF9uZWcgLyBsb3dfbmVnKSkNCihoaWdoX3BlciAvIGxvd19wZXIpIC8gKGhpZ2hfbmVnIC8gbG93X25lZykNCmBgYA0KDQpTaW1pbGFybHkgdG8gdGhlIG9kZHMgcmF0aW8sIHRoZSByYXRpbyBiZXR3ZWVuIHR3byBvZGRzIHJhdGlvcyBjYW4gYmUgaW50ZXJwcmV0ZWQgaW4gdGhlIHNhbWUgd2F5Og0KDQotIE9SMSAvIE9SMiA+IDE6IHRoZSBvZGRzIHJhdGlvIGZvciB0aGUgbnVtZXJhdG9yIGNvbmRpdGlvbiBpcyB4IHRpbWVzIGhpZ2hlciB0aGFuIHRoZSBvZGRzIHJhdGlvIGZvciB0aGUgZGVub21pbmF0b3IgY29uZGl0aW9uDQotIE9SMSAvIE9SMiA8IDE6IHRoZSBvZGRzIHJhdGlvIGZvciB0aGUgbnVtZXJhdG9yIGNvbmRpdGlvbiBpcyB4IHRpbWVzIGxvd2VyIHRoYW4gdGhlIG9kZHMgcmF0aW8gZm9yIHRoZSBkZW5vbWluYXRvciBjb25kaXRpb24NCg0KT2YgY291cnNlLCB0aGUgYmVzdCB3YXkgaXMgdXNpbmcgdGhlIGBwcmVkaWN0KClgIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCnByZWRzIDwtIGV4cGFuZC5ncmlkKHBhcmVudGluZyA9IGMoIm5lZ2xlY3RmdWwiLCAicGVybWlzc2l2ZSIpLA0KICAgICAgICAgICAgICAgICAgICAgYWNhZGVtaWMgPSBjKCJsb3ciLCAiaGlnaCIpKQ0KcHJlZHMkcHIgPC0gcHJlZGljdChmaXQyX2FnZywgbmV3ZGF0YSA9IHByZWRzKQ0KDQp3aXRoKHByZWRzLCAoZXhwKHByKVs0XSAvIGV4cChwcilbMl0pIC8gKGV4cChwcilbM10gLyBleHAocHIpWzFdKSkNCmBgYA0KDQpXaHkgdGhlIHJlc2lkdWFsIGRldmlhbmNlIGlzIGRpZmZlcmVudCBiZXR3ZWVuIHRoZSBhZ2dyZWdhdGVkIGFuZCB0aGUgYmluYXJ5IG1vZGVsPw0KDQpgYGB7cn0NCmRldmlhbmNlKGZpdDIpDQpkZXZpYW5jZShmaXQyX2FnZykNCmBgYA0KVGhpcyBpcyB0aGUgbWFpbiBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byBhcHByb2FjaGVzLiBBY3R1YWxseSB3ZSBkbyBub3QgaGF2ZSB0byBjb21wYXJlIHRoZSBkZXZpYW5jZSBvZiB0aGUgdHdvIG1vZGVscyBlLmcuLCB0aGUgYWdncmVnYXRlZCBmb3JtIGlzIGJldHRlciBiZWNhdXNlIGl0IGlzIGNsb3NlciB0byAwIGJ1dCB3ZSBhbHdheXMgbmVlZCB0byBjb21wYXJlIHRoZSBtb2RlbCB3aXRoIHRoZSBudWxsIGRldmlhbmNlLg0KDQpgYGB7cn0NCmFub3ZhKGZpdDAsIGZpdDIsIHRlc3QgPSAiTFJUIikNCmFub3ZhKGZpdDBfYWdnLCBmaXQyX2FnZywgdGVzdCA9ICJMUlQiKQ0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlIHRoZSByYXRpbyBpcyB0aGUgc2FtZSwgdGh1cyB0aGUgdHdvIGRldmlhbmNlcyBhcmUgb24gYSBkaWZmZXJlbnQgc2NhbGUuIFRoZSB0d28gbW9kZWxzIGV4cGxhaW5zIHRoZSBzYW1lIGFtb3VudCBvZiAocmVsYXRpdmUpIGRldmlhbmNlLg0KDQpXaHk/DQoNClRoZSByZWFzb24gaXMgdGhhdCB3ZSBhcmUgY29tcHV0aW5nIHRoZSByZXNpZHVhbCBkZXZpYW5jZSBmcm9tIG9ic2VydmVkIDAgYW5kIDEgdnMgb2JzZXJ2ZWQgY291bnRzLg0KDQpgYGB7cn0NCiMgYWdncmVnYXRlZCBtb2RlbCBkZXZpYW5jZQ0KLTIqKHN1bShsb2coZGJpbm9tKGRhdF9hZ2ckZHJvcF8xLCBkYXRfYWdnJGRyb3BfdG90LCBmaXR0ZWQoZml0Ml9hZ2cpKSkgLSBsb2coZGJpbm9tKGRhdF9hZ2ckZHJvcF8xLCBkYXRfYWdnJGRyb3BfdG90LCBkYXRfYWdnJGRyb3BfMS9kYXRfYWdnJGRyb3BfdG90KSkpKQ0KDQojIGJpbmFyeSBtb2RlbCBkZXZpYW5jZQ0KLTIqKHN1bShsb2coZGJpbm9tKGRhdCRkcm9wLCAxLCBmaXR0ZWQoZml0MikpKSAtIGxvZyhkYmlub20oZGF0JGRyb3AsIDEsIGRhdCRkcm9wKSkpKQ0KYGBgDQoNCkluIGEgd2F5IGl0IGlzIG1vcmUgZGlmZmljdWx0IHRvIHByZWRpY3QgMCBhbmQgMSBjb21wYXJlZCB0byBjb3VudHMgdGh1cyB0aGUgcmVzaWR1YWxzIGFuZCB0aGUgcmVzaWR1YWwgZGV2aWFuY2Ugd2lsbCBiZSBhbHdheXMgaGlnaGVyLiBNb2RlbCBjb2VmZmljaWVudHMsIHN0YW5kYXJkIGVycm9yIGFuZCB0ZXN0cyBhcmUgdGhlIHNhbWUuDQoNCldoZXJlIHRoZSB0d28gbW9kZWxzIGFyZSBub3QgdGhlIHNhbWU/IERlcGVuZHMgb24gdGhlIHR5cGUgb2YgdmFyaWFibGVzLiBMZXQncyBhZGQgYSBuZXcgY29sdW1uIHRvIG91ciBiaW5hcnkgZGF0YXNldCB3aXRoIHRoZSBhZ2Ugb2YgZWFjaCBzdHVkZW50Og0KDQpgYGB7cn0NCmRhdCRhZ2UgPC0gcm91bmQocnVuaWYobnJvdyhkYXQpLCAxMiwgMTgpKQ0KYGBgDQoNCk5vdywgaWYgd2Ugd2FudCB0byBpbmNsdWRlIHRoZSBgYWdlYCBhcyBwcmVkaWN0b3IsIHdlIG5lZWQgdG8gdXNlIHRoZSBiaW5hcnkgZm9ybSBiZWNhdXNlIHdlIGhhdmUgb25lIHZhbHVlIGZvciBlYWNoIHN0dWRlbnQuIFdlIGFyZSBpbmNsdWRpbmcgYSBwcmVkaWN0b3IgYXQgdGhlIGxldmVsIG9mIHRoZSAwLTEgdmFsdWVzLg0KDQpgYGB7cn0NCmZpdDMgPC0gZ2xtKGRyb3AgfiBhY2FkZW1pYyAqIHBhcmVudGluZyArIGFnZSAsIGRhdGEgPSBkYXQsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkNCnN1bW1hcnkoZml0MykNCmBgYA0KDQpXaGVuIHdlIGhhdmUgcHJlZGljdG9ycyBhdCB0aGUgMC0xIGxldmVscywgd2UgbmVlZCB0byB1c2UgdGhlIGJpbmFyeSBmb3JtLg0KDQpBIGxpdHRsZSAodmlzdWFsKSBkZW1vbnN0cmF0aW9uOg0KDQpgYGB7cn0NCnggPC0gc2VxKDAsIDEsIDAuMDEpDQpkYXQgPC0gZGF0YS5mcmFtZSgNCiAgICB4ID0gcmVwKHgsIDEwKQ0KKQ0KDQpkYXQkbHAgPC0gcGxvZ2lzKHFsb2dpcygwLjAxKSArIDgqZGF0JHgpDQpkYXQkeSA8LSByYmlub20obnJvdyhkYXQpLCAxLCBkYXQkbHApDQpoZWFkKGRhdCkNCmBgYA0KDQpMZXQncyBmaXQgdGhlIG1vZGVsIGluIHRoZSBiaW5hcnkgZm9ybToNCg0KYGBge3J9DQojIG1vZGVsIHByZWRpY3Rpb24NCmZpdCA8LSBnbG0oeSB+IHgsIGRhdGEgPSBkYXQsIGZhbWlseSA9IGJpbm9taWFsKCkpDQoNCiMgZXF1aXZhbGVudCB0byBwcmVkaWN0KCkNCnBpIDwtIHBsb2dpcyhjb2VmKGZpdClbMV0gKyBjb2VmKGZpdClbMl0qdW5pcXVlKGRhdCR4KSkNCmBgYA0KDQpMZXQncyBmaXQgdGhlIG1vZGUgaW4gdGhlIGJpbm9taWFsIGZvcm06DQoNCmBgYHtyfQ0KIyBhZ2dyZWdhdGVkIGZvcm0NCmRhdF9hZ2cgPC0gYWdncmVnYXRlKHkgfiB4LCBGVU4gPSBzdW0sIGRhdGEgPSBkYXQpDQpkYXRfYWdnJG4gPC0gMTAgIyB0b3RhbCB0cmlhbHMNCmRhdF9hZ2ckZiA8LSBkYXRfYWdnJG4gLSBkYXRfYWdnJHkNCmRhdF9hZ2ckcCA8LSBkYXRfYWdnJHkgLyBkYXRfYWdnJG4NCg0KaGVhZChkYXRfYWdnKQ0KDQpmaXQyIDwtIGdsbShjYmluZCh5LCBmKSB+IHgsIGRhdGEgPSBkYXRfYWdnLCBmYW1pbHkgPSBiaW5vbWlhbCgpKQ0KcGkgPC0gcGxvZ2lzKGNvZWYoZml0MilbMV0gKyBjb2VmKGZpdDIpWzJdKmRhdF9hZ2ckeCkNCmBgYA0KDQpUaGUgcmVzaWR1YWxzICh0aHVzIHRoZSByZXNpZHVhbCBkZXZpYW5jZSkgd2lsbCBiZSBhbHdheXMgbGFyZ2VyIGluIHRoZSBiaW5hcnkgbW9kZWwgKGJ1dCB0aGUgY29lZmZpY2llbnRzIGFyZSB0aGUgc2FtZSk6DQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygxLDIpKQ0KDQpqaXQgPC0gcnVuaWYobnJvdyhkYXQpLCAtMC4wMywgMC4wMykNCnBsb3QoKHkgKyBqaXQpIH4geCwgZGF0YSA9IGRhdCwgeWxhYiA9ICJ5IiwgeGxhYiA9ICJ4IiwNCiAgICAgbWFpbiA9ICJCaW5hcnkgRm9ybSIpDQpsaW5lcyh1bmlxdWUoZGF0JHgpLCBwaSwgbHdkID0gMiwgY29sID0gInJlZCIpDQoNCnBsb3QoeS9uIH4geCwgZGF0YSA9IGRhdF9hZ2csIHlsYWIgPSAieSIsDQogICAgIG1haW4gPSAiQmlub21pYWwgRm9ybSIpDQpsaW5lcyhkYXRfYWdnJHgsIHBpLCBsd2QgPSAyLCBjb2wgPSAicmVkIikNCmBgYA0KDQpUaGlzIGlzIHRoZSByZWFzb24gd2h5IGJpbmFyeSBtb2RlbCBoYXZlIGFsc28gc3RyYW5nZSByZXNpZHVhbHM6DQoNCmBgYHtyfQ0KIyBhbHNvIHJlc2lkdWFscw0KcGFyKG1mcm93ID0gYygxLDIpKQ0KcGxvdChmaXR0ZWQoZml0KSwgcmVzaWR1YWxzKGZpdCksIG1haW4gPSAiQmluYXJ5IEZvcm0iKQ0KcGxvdChmaXR0ZWQoZml0MiksIHJlc2lkdWFscyhmaXQyKSwgbWFpbiA9ICJCaW5vbWlhbCBGb3JtIikNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=