For data distribution, we’re using a negative binomial distribution which should be good for count data without the forced mean-variance link that poisson has.
Examples
sample <-expand_grid(mean =c(3), dispersion =c(.5, 1, 5, 10, 100), d =c(.2, .3, .4)) |>mutate(dat =pmap(list(mean, dispersion, d), \(m, disp, d) { var <- m + m**2/ disp sd <-sqrt(var) delta <- .5* d *sqrt(var) m_lower <- m - delta dat_1 <-rnbinom(10000, size = disp, mu = m_lower) m_upper <- m + delta dat_2 <-rnbinom(10000, size = disp, mu = m_upper)tibble(dat = dat_1, type ="lower") |>bind_rows(tibble(dat = dat_2, type ="upper")) |>mutate(sd = sd) })) |>unnest(dat)sample_2 <-expand_grid(mean =c(1,3,9), dispersion =c(.5, 1, 10, 100)) |>mutate(dat =pmap(list(mean, dispersion), \(m, disp) { dat <-rnbinom(10000, size = disp, mu = m)tibble(dat = dat) })) |>unnest(dat)
here’s an example of what these distributions look like for different cohen’s d, assuming mean of 3 and trying different dispersion values.
sample |>ggplot(aes(x = dat, fill = type)) +geom_histogram(binwidth =1, alpha = .5, position =position_identity()) +facet_grid(dispersion ~ d) +coord_cartesian(xlim =c(0, 20))
sample_2 |>ggplot(aes(x = dat)) +geom_histogram(binwidth =1, alpha = .5, position =position_identity()) +facet_grid(str_c("mean=",mean)~str_c("dispersion=",dispersion)) +coord_cartesian(xlim =c(0, 20))+labs(x="Number of events", y="")+theme(axis.title.y=element_blank(), axis.ticks.y=element_blank(),axis.text.y=element_blank())
also, as a check, we can recover roughly the same cohen’s d (using the normal calculation method for pooling sd, but we’re at least ballpark okay). This does assume independence.
`summarise()` has grouped output by 'type', 'dispersion', 'd'. You can override
using the `.groups` argument.
check |>ggplot(aes(x = d, y = d_calc, color =as.character(dispersion))) +geom_point() +geom_abline()
Simulation
to start with, let’s just simulate one set of data and explore it.
Parameter selections: we need to choose values for these, for a first attempt these are some guesses.
(experiment size)
number of labs: 10
number of infants / lab / age: 16
(distributional)
mean: 3
dispersion: 1 (looks kinda reasonable from above)
max value: 15 (since we’re going to be pretty dispersed, we’re going to replace values above 15 with 15)
(fixed effects, all in units of SD)
interaction (match/mismatch) effect: .4
simple effect (A > B for child): .4
simple effect (A > B for adult): 0
age main effect: .2
age x interaction: .2
age x simple effect: .2
(random effects, all in units of SD)
sd of labs (intercept): .2
sd of labs (adult): .1
sd of labs (child): 0
sd of labs (adult x child): .2
sd of labs (age): 0
sd of labs (age x adult): 0
sd of labs (age x child): 0
sd of labs (age x adult x child): 0
sd of infants (intercept): .2
sample_neg_binom <-function(effect, mean, dispersion, max) {# assumes effect in units of sd# mean and dispersion as parameters for nbinom# max is the max value to return -- samples above this are replaced with max# returns a sampled value var <- mean + mean**2/ dispersion delta <- effect *sqrt(var) new_mean <-max(mean + delta, 0) test_val <-rnbinom(n =1, size = dispersion, mu = new_mean)ifelse(test_val > max, max, test_val)}
NOTE: Results may be misleading due to involvement in interactions
pairs(EMM, by="kid_did_num")
kid_did_num = -0.5:
contrast estimate SE df z.ratio p.value
(adult_did_num-0.5) - adult_did_num0.5 0.487 0.0987 Inf 4.936 <.0001
kid_did_num = 0.5:
contrast estimate SE df z.ratio p.value
(adult_did_num-0.5) - adult_did_num0.5 -0.219 0.0933 Inf -2.345 0.0190
Results are averaged over the levels of: kid_age_num
Results are given on the log (not the response) scale.
this seems to get reversed sign, but consistent values with the individual version, so we could skip the individual versions and just use the emmeans to get the two halves out.
individual mixed effects models
A is coded negative, so a negative coefficient on adult_did_num is supportive of imitation
chi_sq_data <- prep_chi_sq_data |>group_by(A, B) |>tally()# overall goodness of fit test comparing to all equal# note there are objections because 0's are not as likelychisq.test(chi_sq_data$n, p =rep(1/9, 9))
Chi-squared test for given probabilities
data: chi_sq_data$n
X-squared = 121.79, df = 8, p-value < 2.2e-16
# both match versus both mismatchmatch_mismatch <- chi_sq_data |>filter(A =="more_A"& B =="more_B"| A =="less_A"& B =="less_B")# goodness of fit for match-match v mismatch-mismatchchisq.test(match_mismatch$n, p =c(.5, .5))
Chi-squared test for given probabilities
data: match_mismatch$n
X-squared = 9.1513, df = 1, p-value = 0.002485
age as split and test each separately
chi_sq_data_age <- prep_chi_sq_data |>group_by(A, B, kid_age) |>tally()# younger# overall goodness of fit test comparing to all equal# note there are objections because 0's are not as likelychisq.test(chi_sq_data_age |>filter(kid_age =="younger") |>pull(n), p =rep(1/9, 9))
Chi-squared test for given probabilities
data: pull(filter(chi_sq_data_age, kid_age == "younger"), n)
X-squared = 65.45, df = 8, p-value = 3.932e-11
chisq.test(chi_sq_data_age |>filter(kid_age =="older") |>pull(n), p =rep(1/9, 9))
Chi-squared test for given probabilities
data: pull(filter(chi_sq_data_age, kid_age == "older"), n)
X-squared = 64.1, df = 8, p-value = 7.268e-11
# both match versus both mismatchmatch_mismatch_age <- chi_sq_data_age |>filter(A =="more_A"& B =="more_B"| A =="less_A"& B =="less_B")# goodness of fit for match-match v mismatch-mismatchchisq.test(match_mismatch_age |>filter(kid_age =="younger") |>pull(n), p =c(.5, .5))
Chi-squared test for given probabilities
data: pull(filter(match_mismatch_age, kid_age == "younger"), n)
X-squared = 6, df = 1, p-value = 0.01431
chisq.test(match_mismatch_age |>filter(kid_age =="older") |>pull(n), p =c(.5, .5))
Chi-squared test for given probabilities
data: pull(filter(match_mismatch_age, kid_age == "older"), n)
X-squared = 3.4615, df = 1, p-value = 0.06281
wilcoxon matched-pairs signed-rank test
again, not clear what the intended age thing is, so try together & split
Wilcoxon signed rank test with continuity correction
data: type_A$adult_A and type_A$adult_B
V = 22533, p-value = 2.115e-06
alternative hypothesis: true location shift is not equal to 0
Wilcoxon signed rank test with continuity correction
data: pull(filter(type_A, kid_age == "younger"), adult_A) and pull(filter(type_A, kid_age == "younger"), adult_B)
V = 5399.5, p-value = 0.0006122
alternative hypothesis: true location shift is not equal to 0
Wilcoxon signed rank test with continuity correction
data: pull(filter(type_A, kid_age == "older"), adult_A) and pull(filter(type_A, kid_age == "older"), adult_B)
V = 5912, p-value = 0.001036
alternative hypothesis: true location shift is not equal to 0
# within type-B, all ageswilcox.test(type_B$adult_A, type_A$adult_B, paired = T)
Wilcoxon signed rank test with continuity correction
data: type_B$adult_A and type_A$adult_B
V = 23894, p-value = 9.998e-09
alternative hypothesis: true location shift is not equal to 0
Wilcoxon signed rank test with continuity correction
data: pull(filter(type_B, kid_age == "younger"), adult_A) and pull(filter(type_B, kid_age == "younger"), adult_B)
V = 4192.5, p-value = 0.1217
alternative hypothesis: true location shift is not equal to 0
Wilcoxon signed rank test with continuity correction
data: pull(filter(type_B, kid_age == "older"), adult_A) and pull(filter(type_A, kid_age == "older"), adult_B)
V = 6050, p-value = 4.56e-06
alternative hypothesis: true location shift is not equal to 0
what are priors for neg binomial
library(brms)
Loading required package: Rcpp
Loading 'brms' package (version 2.22.0). Useful instructions
can be found by typing help('brms'). A more detailed introduction
to the package is available through vignette('brms_overview').
Attaching package: 'brms'
The following object is masked from 'package:lme4':
ngrps
The following object is masked from 'package:stats':
ar
prior class coef group
(flat) b
(flat) b adult_did_num
(flat) b adult_did_num:kid_did_num
(flat) b kid_age_num
(flat) b kid_age_num:adult_did_num
(flat) b kid_age_num:adult_did_num:kid_did_num
(flat) b kid_age_num:kid_did_num
(flat) b kid_did_num
lkj(1) cor
lkj(1) cor site
student_t(3, 0.7, 2.5) Intercept
student_t(3, 0, 2.5) sd
student_t(3, 0, 2.5) sd kid
student_t(3, 0, 2.5) sd Intercept kid
student_t(3, 0, 2.5) sd site
student_t(3, 0, 2.5) sd adult_did_num site
student_t(3, 0, 2.5) sd adult_did_num:kid_did_num site
student_t(3, 0, 2.5) sd Intercept site
student_t(3, 0, 2.5) sd kid_age_num site
student_t(3, 0, 2.5) sd kid_age_num:adult_did_num site
student_t(3, 0, 2.5) sd kid_age_num:adult_did_num:kid_did_num site
student_t(3, 0, 2.5) sd kid_age_num:kid_did_num site
student_t(3, 0, 2.5) sd kid_did_num site
inv_gamma(0.4, 0.3) shape
resp dpar nlpar lb ub source
default
(vectorized)
(vectorized)
(vectorized)
(vectorized)
(vectorized)
(vectorized)
(vectorized)
default
(vectorized)
default
0 default
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 (vectorized)
0 default
unclear if Bayesian makes sense as a robustness effect, but need to choose priors if we do.
Future
Interpretation
mixed effects models
if I understand correctly, the predictions of imitation are in particular a cross-over (type) effect – i.e. that for each child behavior, it is higher in it’s own category than the other category. Notably, this will be a subset of the cases with a credible interaction effect in the mixed effect model.
So, we really need the two separate models / the emmeans for each half.
In our testing, may want to reparameterize how we feed in values! to distinguish / not have centered values
child does A>B:
effect of adult A>B on child A:
effect of adult A>B on child B:
(these three with age)
chisq
the 3x3 chisq is not very sensitive (because we don’t know how many 0s to expect)
the pairwise – harder to think about, might depend on variances? definitely will want to simulate
kids generally do lots of A-A, some B-A, equal A-B and B-B (A is more arousing to watch, kid_A indexes arousal, kid_B does not) –> 2/3 of kids do A-A > B-A; 1/2 of kids do A-B, half of kids do B-B (even distributions) A-A + B-B : 2/3 * 1/2 B-A + A-B : 1/3 * 1/2 will come out? as AA-BB >> BA-AB
I think this again detects (some?) interaction effects, but doesn’t distinguish interactions that would get different interpretations.
wilcoxon
I think this is fine? again depends on what we do with age and the imitation theory needs both kid_A and kid_B tests to have the predicted direction.
What to use for wider effect-recovery / power-analysis simulations
The above tried one sampling on one set of params. Once we have a sense of
what parameter ranges are relevant and
what outcomes are relevant (ex. significance on which tests/contrasts)
I can do power-analysis / aggregate across many simulations.
May want to check ex. ostenberg or davis meta-analysis to get ideas on # of actions/ unit time (mean & max plausible) and estimate of variance from different sources and estimates of simple effects (how common overall are MO v TP).
will definitely want to check for a variety of combos of main effects/types of interaction
will want to check against different levels of background variability
may want to power-analyse for different expt sizes
Parameters:
(experiment size)
number of labs: 10
number of infants / lab / age: 16
(distributional)
mean: 3
dispersion: 1 (looks kinda reasonable from above)
max value: 15 (since we’re going to be pretty dispersed, we’re going to replace values above 15 with 15)