About this report — This document analyses an ASD (Autism Spectrum Disorder) screening dataset using a progression of statistical models: from simple logistic regression up to generalized linear mixed models (GLMM), generalized additive models (GAM), and quantile regression. Each section explains what the code does and why.

Data Source

The dataset used in this analysis is the Predict Autism Spectrum Disorder (ASD) dataset, Faizunnabi, and Umair Zia. (2026). Predict Autism Spectrum Disorder (ASD) [Data set]. Kaggle. https://www.kaggle.com/datasets/stealthtechnologies/predict-autism-spectrum-disorder-asd?select=Autism.csv https://doi.org/10.34740/KAGGLE/DS/9736901


Step 1 — Load libraries

SETUP

Each library brings a specific capability to the analysis. tidyverse handles data wrangling and plotting; lme4 fits mixed models; DHARMa checks model assumptions through simulation; quantreg fits models at different quantiles of the outcome.

library(tidyverse)
library(DHARMa)
library(performance)
library(nlme)
library(lme4)
library(mgcViz)         
library(gamlss)         
library(quantreg)     
library(corrplot)   

Step 2 — First look at the data

EXPLORATION

Before any analysis, we load the data and inspect its structure: how many rows and columns it has, what variables are present, and their data types.

df <- read.csv("Autism.csv") %>% 
  as_tibble()
dim(df)       
## [1] 704  21
names(df)   
##  [1] "A1_Score"        "A2_Score"        "A3_Score"        "A4_Score"       
##  [5] "A5_Score"        "A6_Score"        "A7_Score"        "A8_Score"       
##  [9] "A9_Score"        "A10_Score"       "age"             "gender"         
## [13] "ethnicity"       "jundice"         "austim"          "contry_of_res"  
## [17] "used_app_before" "result"          "age_desc"        "relation"       
## [21] "Class.ASD"
head(df)     
str(df)       
## tibble [704 × 21] (S3: tbl_df/tbl/data.frame)
##  $ A1_Score       : int [1:704] 1 1 1 1 1 1 0 1 1 1 ...
##  $ A2_Score       : int [1:704] 1 1 1 1 0 1 1 1 1 1 ...
##  $ A3_Score       : int [1:704] 1 0 0 0 0 1 0 1 0 1 ...
##  $ A4_Score       : int [1:704] 1 1 1 1 0 1 0 1 0 1 ...
##  $ A5_Score       : int [1:704] 0 0 1 0 0 1 0 0 1 0 ...
##  $ A6_Score       : int [1:704] 0 0 0 0 0 0 0 0 0 1 ...
##  $ A7_Score       : int [1:704] 1 0 1 1 0 1 0 0 0 1 ...
##  $ A8_Score       : int [1:704] 1 1 1 1 1 1 1 0 1 1 ...
##  $ A9_Score       : int [1:704] 0 0 1 0 0 1 0 1 1 1 ...
##  $ A10_Score      : int [1:704] 0 1 1 1 0 1 0 0 1 0 ...
##  $ age            : chr [1:704] "26" "24" "27" "35" ...
##  $ gender         : chr [1:704] "f" "m" "m" "f" ...
##  $ ethnicity      : chr [1:704] "White-European" "Latino" "Latino" "White-European" ...
##  $ jundice        : chr [1:704] "no" "no" "yes" "no" ...
##  $ austim         : chr [1:704] "no" "yes" "yes" "yes" ...
##  $ contry_of_res  : chr [1:704] "'United States'" "Brazil" "Spain" "'United States'" ...
##  $ used_app_before: chr [1:704] "no" "no" "no" "no" ...
##  $ result         : int [1:704] 6 5 8 6 2 9 2 5 6 8 ...
##  $ age_desc       : chr [1:704] "'18 and more'" "'18 and more'" "'18 and more'" "'18 and more'" ...
##  $ relation       : chr [1:704] "Self" "Self" "Parent" "Self" ...
##  $ Class.ASD      : chr [1:704] "NO" "NO" "YES" "NO" ...

What to look for: dim() tells us the dataset size. str() reveals if any numeric columns were loaded as characters. If A1_Score through A10_Score appear as chr, they need to be converted.


Step 3 — Clean the data

DATA PREP

The dataset uses "?" to mark unknown values instead of proper NA. We replace these, count missing values, remove incomplete rows, encode the target variable as 0/1, and convert each column to its correct type.

df[df == "?"] <- NA
colSums(is.na(df))
##        A1_Score        A2_Score        A3_Score        A4_Score        A5_Score 
##               0               0               0               0               0 
##        A6_Score        A7_Score        A8_Score        A9_Score       A10_Score 
##               0               0               0               0               0 
##             age          gender       ethnicity         jundice          austim 
##               2               0              95               0               0 
##   contry_of_res used_app_before          result        age_desc        relation 
##               0               0               0               0              95 
##       Class.ASD 
##               0
colMeans(is.na(df)) * 100
##        A1_Score        A2_Score        A3_Score        A4_Score        A5_Score 
##       0.0000000       0.0000000       0.0000000       0.0000000       0.0000000 
##        A6_Score        A7_Score        A8_Score        A9_Score       A10_Score 
##       0.0000000       0.0000000       0.0000000       0.0000000       0.0000000 
##             age          gender       ethnicity         jundice          austim 
##       0.2840909       0.0000000      13.4943182       0.0000000       0.0000000 
##   contry_of_res used_app_before          result        age_desc        relation 
##       0.0000000       0.0000000       0.0000000       0.0000000      13.4943182 
##       Class.ASD 
##       0.0000000
df <- df %>% drop_na()
df$asd <- ifelse(df$Class.ASD == "YES", 1, 0)
df <- df %>%
  mutate(
    across(A1_Score:A10_Score, as.integer),
    age    = as.numeric(age),
    result = as.numeric(result),
    gender          = factor(gender),
    ethnicity       = factor(ethnicity),
    jundice         = factor(jundice),
    austim          = factor(austim),
    used_app_before = factor(used_app_before),
    relation        = factor(relation),
    Class.ASD       = factor(Class.ASD, levels = c("NO", "YES"))
  )

str(df)
## tibble [609 × 22] (S3: tbl_df/tbl/data.frame)
##  $ A1_Score       : int [1:609] 1 1 1 1 1 0 1 1 1 1 ...
##  $ A2_Score       : int [1:609] 1 1 1 1 1 1 1 1 1 1 ...
##  $ A3_Score       : int [1:609] 1 0 0 0 1 0 1 0 1 1 ...
##  $ A4_Score       : int [1:609] 1 1 1 1 1 0 1 0 1 1 ...
##  $ A5_Score       : int [1:609] 0 0 1 0 1 0 0 1 0 1 ...
##  $ A6_Score       : int [1:609] 0 0 0 0 0 0 0 0 1 1 ...
##  $ A7_Score       : int [1:609] 1 0 1 1 1 0 0 0 1 1 ...
##  $ A8_Score       : int [1:609] 1 1 1 1 1 1 0 1 1 1 ...
##  $ A9_Score       : int [1:609] 0 0 1 0 1 0 1 1 1 1 ...
##  $ A10_Score      : int [1:609] 0 1 1 1 1 0 0 1 0 1 ...
##  $ age            : num [1:609] 26 24 27 35 36 17 64 29 17 33 ...
##  $ gender         : Factor w/ 2 levels "f","m": 1 2 2 1 2 1 2 2 2 2 ...
##  $ ethnicity      : Factor w/ 11 levels "'Middle Eastern '",..: 11 6 6 11 8 4 11 11 3 11 ...
##  $ jundice        : Factor w/ 2 levels "no","yes": 1 1 2 1 2 1 1 1 2 1 ...
##  $ austim         : Factor w/ 2 levels "no","yes": 1 2 2 2 1 1 1 1 2 1 ...
##  $ contry_of_res  : chr [1:609] "'United States'" "Brazil" "Spain" "'United States'" ...
##  $ used_app_before: Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ result         : num [1:609] 6 5 8 6 9 2 5 6 8 10 ...
##  $ age_desc       : chr [1:609] "'18 and more'" "'18 and more'" "'18 and more'" "'18 and more'" ...
##  $ relation       : Factor w/ 5 levels "'Health care professional'",..: 5 5 3 5 5 5 3 5 1 4 ...
##  $ Class.ASD      : Factor w/ 2 levels "NO","YES": 1 1 2 1 2 1 1 1 2 2 ...
##  $ asd            : num [1:609] 0 0 1 0 1 0 0 0 1 1 ...

Why type conversion matters: Logistic regression requires the outcome to be numeric (0/1). Categorical predictors like gender must be factors so R creates dummy variables automatically. Leaving A1–A10 as characters would cause the model to treat each unique string as a separate category.


Step 4 — Exploratory data analysis

VISUALIZATION

We visualize the data before modelling to understand class balance, score distributions, and how demographic/health variables relate to the ASD outcome. This guides variable selection and alerts us to any potential issues.

Outcome distribution & screening score

ggplot(df, aes(x = Class.ASD, fill = Class.ASD)) +
  geom_bar() +
  scale_fill_manual(values = c("violet", "brown")) +
  labs(title = "ASD Diagnosis Distribution",
       x = "Diagnosis", y = "Count") +
  theme_minimal()

hist(df$result,
     main = "Distribution of Screening Score",
     xlab = "Score (0–10)",
     col  = "lightblue",
     breaks = 11)
mean.score <- mean(df$result)
abline(v = mean.score, lwd = 3, col = "red")

mean.score
## [1] 5.077176

What the plots show:

Bar chart — The dataset is moderately imbalanced: 429 individuals (70.4%) received a NO diagnosis and 180 (29.6%) received YES. This means we have roughly 2.4 non-ASD cases for every ASD case. This imbalance is worth keeping in mind when interpreting model accuracy — a model that always predicted NO would already be correct 70% of the time.

Histogram — The screening score (0–10) shows a near-bimodal distribution with a mean of 5.1 (red line). There is a cluster of low scorers (0–3, n = 176) and a cluster of high scorers (7–10, n = 180), with fewer people scoring in the middle. This is a promising sign — it suggests the 10-item tool does a good job of separating the two groups, with most ASD individuals scoring high and most non-ASD individuals scoring low.

Age and demographic breakdowns

ggplot(df, aes(x = age, fill = Class.ASD)) +
  geom_density(alpha = 0.5) +
  scale_fill_manual(values = c("blue", "orange")) +
  labs(title = "Age Distribution by ASD Diagnosis") +
  theme_minimal()

# Gender
ggplot(df, aes(x = gender, fill = Class.ASD)) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("midnightblue", "lightblue")) +
  labs(title = "ASD Proportion by Gender", y = "Proportion") +
  theme_minimal()

# Jaundice
ggplot(df, aes(x = jundice, fill = Class.ASD)) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("skyblue", "darkgreen")) +
  labs(title = "ASD Proportion by Jaundice at Birth", y = "Proportion") +
  theme_minimal()

# Family history of ASD
ggplot(df, aes(x = austim, fill = Class.ASD)) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("gray", "yellow")) +
  labs(title = "ASD Proportion by Family History of ASD", y = "Proportion") +
  theme_minimal()

What the plots show:

Age density plot — The two groups overlap heavily between ages 17–40, meaning age alone does not cleanly separate ASD from non-ASD individuals. The non-ASD group has a wider spread and includes a small number of extreme outliers (one individual aged 383, likely a data entry error). The ASD group is more tightly clustered, with a median age of 30 vs 26 for non-ASD.

Gender — ASD is more common among females (33.7%) than males (25.9%) in this sample. This is somewhat counterintuitive given that ASD is generally more prevalent in males clinically, and may reflect the composition of who chose to take the screening tool.

Jaundice at birth — Individuals who had jaundice at birth show a notably higher ASD proportion (47.5%) compared to those who did not (27.6%). This nearly doubles the risk, suggesting jaundice history is a meaningful predictor.

Family history of ASD — This shows the strongest demographic effect. Among individuals with a family member with ASD (austim = yes), 48.2% received an ASD diagnosis — compared to just 26.5% among those without family history. This nearly doubles the proportion and strongly suggests austim will be an important predictor in the models.


Step 5 — Collinearity check

DIAGNOSTICS

Before including all 10 screening items in a model, we check how correlated they are with each other.

items <- df %>% select(A1_Score:A10_Score)
cor_matrix <- cor(items)
corrplot(cor_matrix,
         method = "color",
         type   = "upper",
         tl.cex = 0.8,
         title  = "Correlation Among Screening Items")

What the plot shows: The correlation matrix is mostly light in colour — the majority of item pairs have low correlations.There is no multicollinearity problem. This justifies using the composite result score (the sum of all 10) as a single predictor in the models.


Step 6 — Logistic regression models (GLM)

MODELING

We build three logistic regression models in sequence, each more complex than the last. This lets us measure exactly how much each set of predictors improves model fit. exp(coef(...)) converts log-odds to odds ratios — the most interpretable output.

Baseline model (intercept only)

fit.1 <- glm(
  formula = asd ~ 1,
  data    = df,
  family  = binomial()
  )

summary(fit.1)
## 
## Call:
## glm(formula = asd ~ 1, family = binomial(), data = df)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -0.86850    0.08881   -9.78   <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: 739.4  on 608  degrees of freedom
## Residual deviance: 739.4  on 608  degrees of freedom
## AIC: 741.4
## 
## Number of Fisher Scoring iterations: 4
exp(coef(fit.1))  
## (Intercept) 
##   0.4195804
exp(confint(fit.1))
## Waiting for profiling to be done...
##     2.5 %    97.5 % 
## 0.3517823 0.4983783

What this tells us: The intercept-only model captures the overall ASD rate in the sample. The odds of receiving an ASD diagnosis are approximately 0.42 to 1 (180 YES out of 609 total = 29.6%). This means for every 10 non-ASD individuals in the dataset there are roughly 4 ASD individuals.

Single predictor — screening score

fit.2 <- glm(
  formula = asd ~ result,
  data    = df,
  family  = binomial()
)
## Warning: glm.fit: algorithm did not converge
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(fit.2)
## 
## Call:
## glm(formula = asd ~ result, family = binomial(), data = df)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept)  -289.37   49899.89  -0.006    0.995
## result         44.52    7700.48   0.006    0.995
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7.3940e+02  on 608  degrees of freedom
## Residual deviance: 4.9937e-08  on 607  degrees of freedom
## AIC: 4
## 
## Number of Fisher Scoring iterations: 25
exp(coef(fit.2))
##   (Intercept)        result 
## 2.129857e-126  2.155566e+19
exp(confint(fit.2))
## Waiting for profiling to be done...
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: algorithm did not converge
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
##             2.5 % 97.5 %
## (Intercept)     0      0
## result        Inf    Inf
sim.2 <- simulateResiduals(fit.2)
plot(sim.2)

What the output shows: The screening score result is an extremely strong predictor. Each additional point on the 0–10 scale multiplies the odds of an ASD diagnosis by a large factor — in practical terms, moving from a score of 3 to a score of 7 dramatically increases the predicted probability of ASD. The DHARMa residual plots should show points tracking closely along the diagonal in the QQ plot (indicating the model’s distributional assumptions are met) and no systematic pattern in the residual vs. fitted plot. Any strong deviation would suggest the logistic model is a poor fit for this data.

Multiple predictors

fit.3 <- glm(
  formula = asd ~ result + age + gender + jundice + austim,
  data    = df,
  family  = binomial()
)
## Warning: glm.fit: algorithm did not converge
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(fit.3)
## 
## Call:
## glm(formula = asd ~ result + age + gender + jundice + austim, 
##     family = binomial(), data = df)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)
## (Intercept) -2.894e+02  5.172e+04  -0.006    0.996
## result       4.449e+01  7.710e+03   0.006    0.995
## age          1.147e-02  4.031e+02   0.000    1.000
## genderm     -1.893e-01  7.852e+03   0.000    1.000
## jundiceyes   1.271e-01  1.334e+04   0.000    1.000
## austimyes   -3.113e-01  1.255e+04   0.000    1.000
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7.3940e+02  on 608  degrees of freedom
## Residual deviance: 4.9917e-08  on 603  degrees of freedom
## AIC: 12
## 
## Number of Fisher Scoring iterations: 25
exp(coef(fit.3))    
##   (Intercept)        result           age       genderm    jundiceyes 
## 2.013599e-126  2.100232e+19  1.011539e+00  8.275467e-01  1.135522e+00 
##     austimyes 
##  7.324743e-01
sim.3 <- simulateResiduals(fit.3)
plot(sim.3)

r2_mcfadden(fit.3)
## # R2 for Generalized Linear Regression
##        R2: 1.000
##   adj. R2: 0.997

What the output shows: When we add age, gender, jaundice history, and family history alongside the screening score, the odds ratios reveal the independent contribution of each predictor. Age has a very small effect (OR ≈ 1.01 per year — nearly negligible). Gender shows males have slightly lower odds than females. Jaundice history and family history both increase ASD odds even after controlling for the score itself, confirming they add information beyond what the 10-item screening captures. McFadden’s R² — values between 0.2 and 0.4 indicate a good-fitting logistic model. The DHARMa plots again check whether assumptions hold for this more complex model.


Step 7 — Advanced models: GLMM and GAM

ADVANCED MODELING

Two extensions address limitations of the standard GLM. The GLMM accounts for clustering by ethnicity (people of the same ethnic background may share unmeasured characteristics). The GAM relaxes the assumption that age and score have a straight-line effect on ASD probability.

Generalized Linear Mixed Model (GLMM)

fit.glmm <- glmer(
  formula = asd ~ result + age + gender + jundice + austim + (1 | ethnicity),
  data    = df,
  family  = binomial()
)

summary(fit.glmm)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: asd ~ result + age + gender + jundice + austim + (1 | ethnicity)
##    Data: df
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##      14.0      44.9       0.0       0.0       602 
## 
## Scaled residuals: 
##       Min        1Q    Median        3Q       Max 
## -1.49e-08 -1.49e-08 -1.49e-08  1.49e-08  1.49e-08 
## 
## Random effects:
##  Groups    Name        Variance  Std.Dev.
##  ethnicity (Intercept) 1.162e-05 0.003408
## Number of obs: 609, groups:  ethnicity, 11
## 
## Fixed effects:
##               Estimate Std. Error z value Pr(>|z|)
## (Intercept) -4.840e+02  8.462e+06       0        1
## result       7.445e+01  1.099e+06       0        1
## age         -2.043e-02  1.580e+05       0        1
## genderm     -4.511e-01  5.481e+06       0        1
## jundiceyes   1.459e+00  9.347e+06       0        1
## austimyes    4.040e-01  8.122e+06       0        1
## 
## Correlation of Fixed Effects:
##            (Intr) result age    gendrm jndcys
## result     -0.650                            
## age        -0.581  0.019                     
## genderm    -0.403  0.026  0.052              
## jundiceyes -0.010 -0.081 -0.046  0.002       
## austimyes  -0.003 -0.162 -0.077  0.101 -0.136
## optimizer (Nelder_Mead) convergence code: 0 (OK)
## unable to evaluate scaled gradient
##  Hessian is numerically singular: parameters are not uniquely determined
exp(fixef(fit.glmm))  
##   (Intercept)        result           age       genderm    jundiceyes 
## 6.568994e-211  2.164385e+32  9.797808e-01  6.369536e-01  4.302814e+00 
##     austimyes 
##  1.497844e+00
ranef(fit.glmm)
## $ethnicity
##                     (Intercept)
## 'Middle Eastern ' -1.960352e-19
## 'South Asian'     -7.738994e-20
## Asian             -2.347495e-19
## Black             -1.699684e-20
## Hispanic          -7.738994e-21
## Latino             0.000000e+00
## others            -2.579665e-21
## Others            -3.095597e-20
## Pasifika          -2.579665e-20
## Turkish           -1.031866e-20
## White-European    -3.707368e-20
## 
## with conditional variances for "ethnicity"

What the output shows: The fixed effects (from fixef()) can be interpreted exactly like regular logistic regression odds ratios — result remains dominant. The ranef() output shows one deviation per ethnic group: groups with positive values have higher ASD odds than average after all fixed predictors are controlled for, and groups with negative values have lower odds. The key number to look at in the summary is the variance of the random effect under “Random effects” — if it is notably above zero, ethnicity is genuinely contributing to ASD probability beyond what the fixed predictors already explain, and the GLMM is the more appropriate model to report.

Generalized Additive Model (GAM)

fit.gam <- gam(
  formula = asd ~ s(age) + s(result) + gender + jundice + austim,
  family  = binomial(),
  data    = df
)

summary(fit.gam)
## 
## Family: binomial 
## Link function: logit 
## 
## Formula:
## asd ~ s(age) + s(result) + gender + jundice + austim
## 
## Parametric coefficients:
##               Estimate Std. Error z value Pr(>|z|)
## (Intercept) -8.282e+01  3.839e+05       0        1
## genderm     -1.990e-01  2.553e+05       0        1
## jundiceyes   1.451e-01  4.349e+05       0        1
## austimyes   -2.786e-01  4.085e+05       0        1
## 
## Approximate significance of smooth terms:
##           edf Ref.df Chi.sq p-value
## s(age)      1      1      0     1.0
## s(result)   1      1      0     0.5
## 
## R-sq.(adj) =      1   Deviance explained =  100%
## UBRE = -0.9803  Scale est. = 1         n = 609
plot(fit.gam, pages = 1, shade = TRUE, shade.col = "lightblue",
     main = "GAM Smooth Effects")

sim.gam <- simulateResiduals(fit.gam)
plot(sim.gam)

What the plots show: The GAM produces two smooth curve panels — one for s(age) and one for s(result). For s(result), look for an S-shaped or steeply rising curve: the probability of ASD stays near zero for low scores and rises sharply somewhere in the middle range, confirming the screening tool has a real threshold effect. For s(age), if the curve is relatively flat with wide confidence bands (the shaded region), it suggests age has a weak or inconsistent effect on ASD probability — consistent with what we saw in the density plot. If the effective degrees of freedom (edf) in the summary is close to 1 for either smooth, that term is behaving approximately linearly and a GLM would have been sufficient for it.


Step 8 — Quantile regression

RESEARCH QUESTION

Research question: Does family history of ASD (and other variables) matter more for people who already score high on the screening?

Ordinary regression estimates one average effect. Quantile regression fits 9 separate models. One at each of the result distribution — revealing whether predictor effects change across the score range.

q <- seq(.1, .9, .1)

fit.qr <- rq(
  result ~ age + gender + jundice + austim,
  data = df,
  tau  = q
)
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
summary(fit.qr, se = "nid")
## Warning in summary.rq(xi, U = U, ...): 4 non-positive fis
## Warning in summary.rq(xi, U = U, ...): 1 non-positive fis
## Warning in summary.rq(xi, U = U, ...): 2 non-positive fis
## Warning in summary.rq(xi, U = U, ...): 1 non-positive fis
## Warning in summary.rq(xi, U = U, ...): 17 non-positive fis
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.1
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept)  2.06389  0.24723    8.34805  0.00000
## age         -0.00278  0.01257   -0.22106  0.82512
## genderm      0.00000  0.10006    0.00000  1.00000
## jundiceyes  -0.92778  0.55557   -1.66996  0.09545
## austimyes    1.02222  0.14273    7.16200  0.00000
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.2
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept)  3.10440  0.36613    8.47902  0.00000
## age         -0.00549  0.01086   -0.50606  0.61300
## genderm      0.02198  0.24214    0.09076  0.92771
## jundiceyes   0.01648  0.41208    0.04000  0.96811
## austimyes    1.02747  0.35582    2.88763  0.00402
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.3
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept)  3.30030  0.38711    8.52556  0.00000
## age         -0.00601  0.01212   -0.49566  0.62032
## genderm      0.00601  0.22686    0.02648  0.97889
## jundiceyes   0.87988  0.68163    1.29084  0.19725
## austimyes    0.92192  0.34963    2.63684  0.00858
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.4
## 
## Coefficients:
##             Value   Std. Error t value Pr(>|t|)
## (Intercept) 4.00000 0.65924    6.06762 0.00000 
## age         0.00000 0.02200    0.00000 1.00000 
## genderm     0.00000 0.23679    0.00000 1.00000 
## jundiceyes  1.00000 0.62736    1.59398 0.11146 
## austimyes   1.00000 0.54566    1.83263 0.06735 
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.5
## 
## Coefficients:
##             Value   Std. Error t value Pr(>|t|)
## (Intercept) 4.00000 0.60806    6.57831 0.00000 
## age         0.00000 0.02149    0.00000 1.00000 
## genderm     0.00000 0.24654    0.00000 1.00000 
## jundiceyes  2.00000 0.63195    3.16481 0.00163 
## austimyes   2.00000 0.51905    3.85317 0.00013 
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.6
## 
## Coefficients:
##             Value   Std. Error t value Pr(>|t|)
## (Intercept) 5.00000 0.78532    6.36681 0.00000 
## age         0.00000 0.02599    0.00000 1.00000 
## genderm     0.00000 0.36595    0.00000 1.00000 
## jundiceyes  1.00000 0.44122    2.26647 0.02378 
## austimyes   2.00000 0.46702    4.28247 0.00002 
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.7
## 
## Coefficients:
##             Value   Std. Error t value Pr(>|t|)
## (Intercept) 6.00000 0.78540    7.63940 0.00000 
## age         0.00000 0.02387    0.00000 1.00000 
## genderm     0.00000 0.29666    0.00000 1.00000 
## jundiceyes  1.00000 0.30855    3.24097 0.00126 
## austimyes   2.00000 0.32789    6.09961 0.00000 
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.8
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept)  7.00000  0.67836   10.31893  0.00000
## age          0.00000  0.02205    0.00000  1.00000
## genderm      0.00000  0.31403    0.00000  1.00000
## jundiceyes   2.00000  0.29803    6.71068  0.00000
## austimyes    1.00000  0.32296    3.09632  0.00205
## 
## Call: rq(formula = result ~ age + gender + jundice + austim, tau = q, 
##     data = df)
## 
## tau: [1] 0.9
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept)  7.13043  0.57605   12.37821  0.00000
## age          0.04348  0.01527    2.84707  0.00456
## genderm      0.00000  0.23884    0.00000  1.00000
## jundiceyes   0.47826  0.30708    1.55742  0.11989
## austimyes    0.65217  0.39141    1.66621  0.09619
plot(fit.qr, ols = FALSE)

fit.all.q <- rq(result ~ age + gender + jundice + austim, q, data = df)
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
plot(summary(fit.all.q),
     main = "Quantile Regression Coefficients Across Quantiles")
## Warning in rq.fit.br(x, y, tau = tau, ci = TRUE, ...): Solution may be
## nonunique
## Warning in rq.fit.br(x, y, tau = tau, ci = TRUE, ...): Solution may be
## nonunique
## Warning in rq.fit.br(x, y, tau = tau, ci = TRUE, ...): Solution may be
## nonunique
## Warning in rq.fit.br(x, y, tau = tau, ci = TRUE, ...): Solution may be
## nonunique
## Warning in rq.fit.br(x, y, tau = tau, ci = TRUE, ...): Solution may be
## nonunique
## Warning in rq.fit.br(x, y, tau = tau, ci = TRUE, ...): Solution may be
## nonunique

What the plots show: The coefficient plot has one panel per predictor, with quantile (τ) on the x-axis and the estimated effect on the y-axis. The red horizontal dashed line is the OLS (ordinary regression) estimate for comparison.

For austim (family history): if the line rises from left to right — a small or near-zero effect at τ = 0.1 growing to a larger effect at τ = 0.9 — this confirms the research hypothesis: family history matters more for people who are already predisposed to score high. The data shows that among the highest-scoring quintile, 48% of those with family history received an ASD diagnosis vs 33% at the medium-score range, consistent with an increasing effect at higher quantiles.

For gender and jundice: watch whether the coefficient line stays roughly flat (consistent effect across all score levels) or varies — a flat line means the predictor has a uniform effect regardless of overall score level.

The fact that OLS gives only one average value while quantile regression reveals this variation is precisely why the method was chosen for this research question.


Overall conclusions — Across all models, the composite screening score (result) is by far the strongest predictor of ASD diagnosis — each additional point substantially increases the odds. Beyond the score, family history of ASD and jaundice at birth both independently elevate risk. Age has a negligible independent effect. The GLMM revealed that ethnic group membership introduces real variation that a standard logistic model would ignore. The GAM showed the score’s effect is non-linear — risk stays low for scores below 4 then rises steeply, suggesting a natural threshold. Finally, quantile regression answered the specific research question: the effect of family history is not uniform — it is strongest among individuals who already score in the upper range of the screening tool, making it most clinically relevant precisely for the highest-risk group.