3
$\begingroup$

I have a large dataset of countries, each categorized into one of two regions, and I'm interested in how the effect of a predictor variable 𝑋 differs between the two regions. I plan to fit separate regression models for each region. While there are alternatives, such as incorporating interaction terms between region and 𝑋 in a single model, I have chosen to start with two distinct models.

Here's the issue: The regions have different numbers of countries — Region 1 has 70 countries, while Region 2 has 20. In my analysis, the main effect of 𝑋 is significant in the larger region (70 countries) but not significant in the smaller region (20 countries). I am concerned that this difference might simply be due to the disparity in sample size rather than reflecting a real difference in the effect of 𝑋.

Approach: To assess whether the observed difference in significance is truly meaningful and not a result of the sample size disparity, I’m considering downsampling the larger region (N = 70) to match the smaller region (N = 20) and refitting the model. Since a single downsampling run may be subject to random variation and introduce biases, I would repeat the downsampling process multiple times (e.g., 𝑘 = 100) to get a more reliable picture.

Questions:

  1. Is this a methodologically sound approach?
    • Does downsampling in this way provide a robust way to compare the results across the two regions?
    • Are there any alternative approaches that I should consider to ensure the comparability of the results?
  2. How should one report the results of repeated downsampling?
    • Given that I plan to fit 100 regression models for the downsampled data, what are the best practices for summarizing the results across these models? (Tables, Figures)
    • Should I report the average coefficients, confidence intervals, and/or p-values across the 100 models? What other metrics could provide meaningful insights?

Minimal Example in R (using mtcars): For illustration of my attempt, I’ve used the mtcars dataset in R. This dataset includes a binary variable am (denoting 0 = automatic vs. 1 = manual transmission), which I’m treating as equivalent to 'region' in my actual dataframe. My goal is to downsample the cars with am = 1 (representing the larger region) to match the number of cars with am = 0 (representing the smaller region) and fit separate regression models in each case. Then, I repeat the downsampling procedure, in this case 𝑘 = 10 times.

Here's a simplified version of my approach in R, which should theoretically resemble the ideas behind Monte Carlo simulations.

You can copy the code here.

# Load necessary libraries
library(tidyverse)
library(broom)
library(binom)

# Information on dataframe
?mtcars

# Increase the imbalance in mtcars to N = 19 automatic and N = 8 manual cars
df <- mtcars %>% 
  mutate(car_model = rownames(mtcars)) %>%
  filter(!car_model %in% c('Lotus Europa', 'Ford Pantera L', 'Ferrari Dino', 'Maserati Bora', 'Volvo 142E'))

# Fit two models separately for automatic and manual cars
model_big <- lm(mpg ~ disp, data = df %>% filter(am == 0))
model_small <- lm(mpg ~ disp, data = df %>% filter(am == 1))

# Display summary of the original models
summary(model_big)
summary(model_small)

# Get the sample sizes
n_small <- nrow(df %>% filter(am == 1))
n_big <- nrow(df %>% filter(am == 0))

# Set the number of iterations for downsampling
iterations <- 10

# Create an empty list to store the results
results <- list()

# Downsample the big group (am == 0) to the size of the small group (am == 1) and fit OLS models
set.seed(123)

for (i in 1:iterations) {
  # Downsample automatic cars (am == 0) to the size of the manual cars group
  downsampled_data <- df %>% 
    filter(am == 0) %>%
    sample_n(n_small)
  
  # Fit the model to the downsampled data
  refitted_model <- lm(mpg ~ disp, data = downsampled_data)
  
  # Extract model statistics for the disp coefficient
  model_summary <- tidy(refitted_model)
  disp_info <- model_summary %>% filter(term == 'disp')
  
  # Store results: estimate and p-value for disp
  results[[i]] <- data.frame(
    iteration = i,
    estimate = disp_info$estimate,
    p_value = disp_info$p.value
  )
}

# Combine results into a single data frame
results_df <- bind_rows(results)

# Summarize the results across iterations
summary_stats <- results_df %>%
  summarise(
    mean_estimate = mean(estimate),
    sd_estimate = sd(estimate),
    mean_p_value = mean(p_value),
    prop_significant = mean(p_value < 0.05)
  )

# Calculate binomial confidence intervals for the proportion of significant p-values 
significant_count <- sum(results_df$p_value < 0.05)
total_count <- nrow(results_df)

# Exact binomial confidence interval
binom_ci_exact <- binom.test(significant_count, total_count, conf.level = 0.95)$conf.int

# Wilson confidence interval
binom_ci_wilson <- binom.confint(significant_count, total_count, method = 'wilson')[c('lower', 'upper')]

# Display summary statistics
summary_table <- tibble(
  Statistic = c(
    'Mean Estimate', 
    'SD of Estimate', 
    'Mean p-value', 
    'Proportion of Significant p-values', 
    'Confidence Interval (Exact, 95%)', 
    'Confidence Interval (Wilson, 95%)'
  ),
  Value = c(
    round(summary_stats$mean_estimate, 2),
    round(summary_stats$sd_estimate, 2),
    round(summary_stats$mean_p_value, 2),
    round(summary_stats$prop_significant, 2),
    paste("[", round(binom_ci_exact[1], 3), ",", round(binom_ci_exact[2], 3), "]"),
    paste("[", round(binom_ci_wilson$lower, 3), ",", round(binom_ci_wilson$upper, 3), "]")
  )
)

# Display the summary statistics table
print(summary_table)

# Plot a histogram of p-values
ggplot(data = results_df, aes(x = p_value)) +
  geom_histogram(aes(y = after_stat(count) / sum(after_stat(count)) * 100), 
                 binwidth = 0.05, fill = 'lightgrey', color = 'darkgray') +
  geom_vline(xintercept = 0.05, linetype = 'dashed', color = 'deeppink') +
  labs(title = 'Histogram of p-values with α = 0.05',
       x = 'P-value',
       y = 'Relative frequency') +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  theme_minimal()
$\endgroup$
6
  • 1
    $\begingroup$ Statistical significance is supposed to depend on sample size. Are you trying to use the p-value as a proxy for practical significance? $\endgroup$
    – Dave
    Commented Sep 13 at 11:51
  • $\begingroup$ Thanks for that question. So, my intention with downsampling is to assess whether the difference in p-values is due to the sample size disparity rather than actual differences in the predictor's effect across the two regions. I'm aware that p-values alone don't capture practical significance, hence I would consider other metrics as well, such as effect sizes and confidence intervals or the overall model fit. I am just wondering if this is a valid approach to ascertain whether there are real-world cross-regional differences or whether this is just an artefact of my data and group imbalance. $\endgroup$ Commented Sep 13 at 11:59
  • 1
    $\begingroup$ Are your residuals normally distributed? If not, your assumptions for estimating uncertainty and statistical significance may not be valid. Instead of down sampling the larger group, you might want to try boot strapping the smaller group to get an estimate of the effect uncertainty that does not require any assumptions. $\endgroup$
    – noNameTed
    Commented Sep 13 at 13:42
  • $\begingroup$ Could you elaborate on this? Is this refering to the original models prior to the downsampling or the residuals within my downsample models? $\endgroup$ Commented Sep 13 at 14:24
  • 2
    $\begingroup$ What are the p-values you're observing in each group? And about "While there are alternatives, such as incorporating interaction terms between region and 𝑋 in a single model, I have chosen to start with two distinct models": why? $\endgroup$
    – J-J-J
    Commented Sep 13 at 15:53

2 Answers 2

7
$\begingroup$

It's certainly not a valid way of answering your question. Your separate analyses show that there is a significant effect in region 1, but no significant effect in region 2.

If your question is whether the effect in region 1 is significantly different from the effect in region 2, you must fit a model to the full dataset, and include an interaction term capturing the difference between the two effects.

library(tidyverse)
df <- mtcars %>% 
  mutate(car_model = rownames(mtcars)) %>%
  filter(!car_model %in% c('Lotus Europa', 'Ford Pantera L', 'Ferrari Dino', 'Maserati Bora', 'Volvo 142E'))

model = lm(mpg ~ disp * am, data = df)
summary(model)
#> 
#> Call:
#> lm(formula = mpg ~ disp * am, data = df)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -3.8803 -1.6546 -0.3443  1.6032  5.0764 
#> 
#> Coefficients:
#>              Estimate Std. Error t value Pr(>|t|)    
#> (Intercept) 25.157064   1.607486  15.650 9.38e-14 ***
#> disp        -0.027584   0.005193  -5.312 2.16e-05 ***
#> am          14.612772   3.203135   4.562 0.000139 ***
#> disp:am     -0.093616   0.025252  -3.707 0.001160 ** 
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 2.427 on 23 degrees of freedom
#> Multiple R-squared:  0.8608, Adjusted R-squared:  0.8426 
#> F-statistic: 47.41 on 3 and 23 DF,  p-value: 5.236e-10

In this specific example, the model shows a significant effect of disp when am = 0 (b = -0.027), and an effect that is significantly lower by -0.093 (so an effect of -0.120) when am = 1.

The Difference Between “Significant” and “Not Significant” is not Itself Statistically Significant is a nice 3-and-a-half page paper on exactly this issue.

$\endgroup$
4
  • $\begingroup$ This is very interesting, thanks! So, then I guess the question I have is how to best model this. My main predictor is actually already an interaction between X1 and X2, so it'd be a 3-way interaction once I include region and I'm unsure if I have a big enough sample to do this. Another question is if this should be a hierarchical model with a cross-level interaction where region (level-2) might moderate the effect of X1*X2 (level-1). $\endgroup$ Commented Sep 13 at 14:34
  • 2
    $\begingroup$ @Dr.FabianHabersack - Above you described the issue as being with "the main effect of X." In the comment above you say the issue is with an interaction. Could you please clear up this discrepancy. $\endgroup$
    – rolando2
    Commented Sep 13 at 14:42
  • $\begingroup$ @rolando2 - That's a fair point. In my original post, including the minimal example, I aimed to provide a simplified description of my dataframe. I assumed that any solution for this case would be transferable to my actual data, but I see now that this is more complicated. Apologies for the confusion! In my actual case the main effect is that of X1 * X2 on Y (potentially moderated by region). $\endgroup$ Commented Sep 13 at 15:21
  • $\begingroup$ Yes, in that case, the effect you're intested in is the 3-way interaction - testing whether the 2-way interaction for region 1 differs significantly from the 2-way interaction for region 2. Unfortunately, estimates for higher-level interactions are noisier than estimates for lower-level effects, so with only 20 observations in one of your regions, you would probably need a pretty big effect size to have reasonable statistical power here. $\endgroup$
    – Eoin
    Commented Sep 14 at 15:27
6
$\begingroup$

I see downsides.

  1. The decrease in data quantity lowers the estimate precision (probably).

  2. There is a further randomness introduced by sub-sampling the sample. At least the data are the data. You can't go back and collect the data again, while you can draw an enormous number of sub-samples. Why is your sub-sample a good one?

  3. (2b) This exposes you to criticism that you hacked the sub-sample to get favorable results (even if you were perfectly ethical).

Thus, you have a lot of downside all to do something that you don't even need to do when you can just look at the effect sizes and confidence intervals (credible intervals if you take a Bayesian approach) based on the original data.

$\endgroup$
2
  • $\begingroup$ Re 1: Right, but this should be a feature not a bug of the mentioned approach, no? I am exploring how robust the effect is to downsampling, which should constitute a hard test as we'd assume uncertainty to increase. If the effects remain highly significant, then there must be sth. distinguishing the regions. Re 2 and 2b: Would set.seed(123), k = 100 resamples, and providing the script not resolve this issue? Re Bayesian: Could you provide more details on this approach including how I'd implement it (maybe on mtcars)? $\endgroup$ Commented Sep 13 at 13:16
  • $\begingroup$ The point about simply comparing effect sizes across models is a good one. Though, I'm not sure this would be enough of a test of the reliability of the claim that this is all due to cross-regional differences rather than mere group imbalance. $\endgroup$ Commented Sep 13 at 13:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.