Data

df.response_blind_adults <- read.csv("../../../data/blind_adults/PROCESSED_DATA/response_lc.csv")
df.response_sighted_adults <- read.csv("../../../data/sighted_adults/PROCESSED_DATA/response_lc.csv")
df.response_blind_kids <- read.csv("../../../data/blind_children/PROCESSED_DATA/response_lc.csv")

df.response <- bind_rows(df.response_blind_adults, 
                         df.response_sighted_adults,
                         df.response_blind_kids)

Data quantity check

Kids’ age range: 6-13. Number of kids

df.response |> 
  filter(group == "blind_children") |>
  group_by(age_years, PID) |> 
  summarise() |> 
  group_by(age_years) |> 
  summarise(n_kids = n())
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by age_years and PID.
## ℹ Output is grouped by age_years.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(age_years, PID))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
## # A tibble: 8 × 2
##   age_years n_kids
##       <int>  <int>
## 1         6      7
## 2         7      4
## 3         8      1
## 4         9      8
## 5        10     13
## 6        11      9
## 7        12     11
## 8        13      2

Language Comprehension - Duck task:

n_trials_lcd <- df.response |> 
  filter(task == "Language Comprehension - Duck") |>
  group_by(group, PID) |> 
  summarize(n_trials = n()) |> 
  ungroup() |>
  mutate(is_full_dataset = ifelse(n_trials == 16, "yes", "no")) |> 
  mutate(is_full_dataset = ifelse(
    group == "sighted_adults" & PID %in% c("SLCA04", "SLCA05",
                             "SLCA06", "SLCA08",
                             "SLCA10", "SLCA11", 
                             "SLCA12"), "yes, given feedback on geocentric trials", is_full_dataset)) |>
  group_by(group) |>
  count(is_full_dataset)
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by group and PID.
## ℹ Output is grouped by group.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(group, PID))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
n_trials_lcd
## # A tibble: 6 × 3
## # Groups:   group [3]
##   group          is_full_dataset                              n
##   <chr>          <chr>                                    <int>
## 1 blind_adults   no                                          13
## 2 blind_adults   yes                                         31
## 3 blind_children no                                           8
## 4 blind_children yes                                         47
## 5 sighted_adults yes                                         13
## 6 sighted_adults yes, given feedback on geocentric trials     7
n_trials_lcd_no_raw_resp <- df.response |> 
  filter(LCD.raw_response_not_recorded) |> 
  group_by(group) |> 
  summarize(n_PIDs_without_raw_response = length(unique(PID)))

n_trials_lcd_no_raw_resp
## # A tibble: 3 × 2
##   group          n_PIDs_without_raw_response
##   <chr>                                <int>
## 1 blind_adults                             6
## 2 blind_children                           4
## 3 sighted_adults                           6

Language Comprehension - Self task: Sighted adults were tested on both geocentric and egocentric terms, blind participants were only tested on egocentric terms.

n_trials_lcs <- df.response |> 
  filter(task == "Language Comprehension - Self") |>
  group_by(group, PID) |> 
  summarize(n_trials = n()) |> 
  ungroup() |>
  mutate(is_full_dataset = case_when (
    group == "sighted_adults" & n_trials == 16 ~ "yes", 
    group == "sighted_adults" & n_trials == 8 ~ "no", 
    n_trials == 8 ~ "yes", 
    TRUE ~ "no")) |>
  group_by(group) |>
  count(is_full_dataset)
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by group and PID.
## ℹ Output is grouped by group.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(group, PID))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
n_trials_lcs
## # A tibble: 4 × 3
## # Groups:   group [3]
##   group          is_full_dataset     n
##   <chr>          <chr>           <int>
## 1 blind_adults   yes                44
## 2 blind_children yes                55
## 3 sighted_adults no                  7
## 4 sighted_adults yes                13

There is 11 partial vision kids and 5 partial vision adults, not enough to run partial vs. no vision analyses (minimum was 20).

df.demog_age <- df.response %>%
  group_by(PID, group) %>%
  slice(1) %>%
  ungroup()

df.demog_age_summary_by_group <- df.demog_age %>%
  group_by(group) %>%
  summarise(mean_age = mean(age_years, na.rm = T),
            sd_age = sd(age_years, na.rm = T),
            min_age = min(age_years, na.rm = T),
            max_age = max(age_years, na.rm = T))
## Warning: There were 2 warnings in `summarise()`.
## The first warning was:
## ℹ In argument: `min_age = min(age_years, na.rm = T)`.
## ℹ In group 3: `group = "sighted_adults"`.
## Caused by warning in `min()`:
## ! no non-missing arguments to min; returning Inf
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
df.demog_gender <- df.response %>%
  group_by(PID, group) %>%
  slice(1) %>%
  ungroup()

df.demog_gender_summary_by_group <- df.demog_gender %>%
  group_by(group, sex) %>%
  count()

df.demog_vision <- df.response %>%
  group_by(PID, group) %>%
  slice(1) %>%
  ungroup()

df.demog_vision_summary_by_group <- df.demog_vision %>%
  group_by(group, vision_group) %>%
  count()

Combined Data Plots

Language Comprehension - Duck

Blind adults were successful in egocentric terms but not geocentric terms, in both English and Hindi.

Blind children and sighted adults were not successful in either FoR, in either language.

df.response_lcd <- df.response %>%
  filter(task == "Language Comprehension - Duck") %>% 
  # the blind groups got a shortened version, so filter out 
  # trials 10-13 & trials 14-16
  filter(!(group %in% c("blind_adults", "blind_children") & (trial %in% 10:12 | trial %in% 14:16))) %>%
  filter(!(group == "sighted_adults" & experimenter_LC == "OC" & (trial %in% 10:12 | trial %in% 14:16)))

ggplot(df.response_lcd %>% 
         group_by(PID, language, group, word_meaning, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
               height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(group~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, group, word_meaning, and
##   FoR.
## ℹ Output is grouped by PID, language, group, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, group, word_meaning, FoR))` for
##   per-operation grouping (`?dplyr::dplyr_by`) instead.
## Warning: Removed 8 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 8 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 8 rows containing missing values or values outside the scale range
## (`geom_point()`).

When faceted into whether the duck is facing the participant or not, the source of failure for some groups becomes clearer.

Blind adults and blind kids were successful in egocentric terms in both English and Hindi, but only when the duck is facing the same direction as them. Otherwise, they are not successful. This makes sense, because it might be more difficult to notice that the duck has been rotated when they have to touch the duck (though doesn’t explain why sighted adults are bad at egocentric terms in either direction.

Both groups fail on geocentric terms in either language.

However, sighted adults are not still not successful in any cell. This could be because there were fewer sighted adults data compared to blind participant data (about half).

ggplot(df.response_lcd %>% 
         group_by(PID, language, group, LCD.duck_pos, word_meaning, FoR) %>%
         mutate(
           LCD.duck_pos = recode_factor(LCD.duck_pos, "same" = "Duck facing same dir.", "toward" = "Duck facing opposite dir."),
           word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
               height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(group~language + LCD.duck_pos) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, group, LCD.duck_pos,
##   word_meaning, and FoR.
## ℹ Output is grouped by PID, language, group, LCD.duck_pos, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, group, LCD.duck_pos, word_meaning,
##   FoR))` for per-operation grouping (`?dplyr::dplyr_by`) instead.
## Warning: Removed 16 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 16 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 16 rows containing missing values or values outside the scale range
## (`geom_point()`).

When participants are incorrect, are they picking the incorrect axis? (Note that there are a few participants not included in this analysis because we did not have their raw response data. This affected 6 blind adults, 4 blind kids, and 6 sighted adults.)

Blind kids never pick the incorrect axis.

Blind adults only pick the incorrect axis for geocentric terms.

Sighted adults pick the incorrect axis on both egocentric terms and geocentric terms, and are more likely to pick the incorrect axis on egocentric terms.

#when ppts are incorrect, they are picking the incorrect axis for geocentric 20% of the time, but not for egocentric
df.response_lcd %>%
  filter(response == 0) %>%
  group_by(group, language, word_meaning, FoR) %>%
  summarise(
    prop_incorrect_axis = sum(LCD.incorrect_axis == 1, na.rm = TRUE) / n(),
  )
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by group, language, word_meaning, and FoR.
## ℹ Output is grouped by group, language, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(group, language, word_meaning, FoR))` for
##   per-operation grouping (`?dplyr::dplyr_by`) instead.
## # A tibble: 24 × 5
## # Groups:   group, language, word_meaning [24]
##    group          language word_meaning FoR        prop_incorrect_axis
##    <chr>          <chr>    <chr>        <chr>                    <dbl>
##  1 blind_adults   english  left         egocentric              0     
##  2 blind_adults   english  north        geocentric              0.0909
##  3 blind_adults   english  right        egocentric              0     
##  4 blind_adults   english  south        geocentric              0.2   
##  5 blind_adults   hindi    left         egocentric              0     
##  6 blind_adults   hindi    north        geocentric              0.167 
##  7 blind_adults   hindi    right        egocentric              0     
##  8 blind_adults   hindi    south        geocentric              0.364 
##  9 blind_children english  left         egocentric              0     
## 10 blind_children english  north        geocentric              0     
## # ℹ 14 more rows
ggplot(df.response_lcd %>%
  filter(response == 0) %>%
  group_by(group, language, word_meaning, FoR) %>%
  mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
    summarise(
    prop_incorrect_axis = sum(LCD.incorrect_axis == 1, na.rm = TRUE) / n(),
  ), 
  aes(x = word_meaning, y = prop_incorrect_axis, label = round(prop_incorrect_axis, 2))) + 
  geom_col() + 
  geom_text(vjust = -0.25) +
  facet_grid(group~language) + 
  coord_cartesian(ylim = c(0,1))
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by group, language, word_meaning, and FoR.
## ℹ Output is grouped by group, language, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(group, language, word_meaning, FoR))` for
##   per-operation grouping (`?dplyr::dplyr_by`) instead.

No clear age trends in kids (age 6-13). But also, very few kids in each age group in the first place.

ggplot(df.response_lcd %>% 
         filter(group == "blind_children") %>%
         group_by(PID, language, LCD.duck_pos, age_months, word_meaning, FoR) %>%
         mutate(
           LCD.duck_pos = recode_factor(LCD.duck_pos, "same" = "Duck facing same dir.", "toward" = "Duck facing opposite dir."),
           word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = round(age_months / 12, 2), y = mean_score, color = FoR, fill = FoR)) + 
  geom_smooth(method = "lm") + 
  facet_grid(~language + LCD.duck_pos) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted") + 
  labs(x = "Age (years)") + 
  coord_cartesian(ylim = c(0,1))
## `summarise()` has regrouped the output.
## `geom_smooth()` using formula = 'y ~ x'
## ℹ Summaries were computed grouped by PID, language, LCD.duck_pos, age_months,
##   word_meaning, and FoR.
## ℹ Output is grouped by PID, language, LCD.duck_pos, age_months, and
##   word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, LCD.duck_pos, age_months, word_meaning,
##   FoR))` for per-operation grouping (`?dplyr::dplyr_by`) instead.

Language Comprehension - Self

All groups are better at egocentric terms (in both English and Hindi) when applied to their own body parts.

df.response_lcs <- df.response %>% 
  filter(task == "Language Comprehension - Self")

ggplot(df.response_lcs %>% 
         group_by(PID, language, group, word_meaning, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
               height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(group~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, group, word_meaning, and
##   FoR.
## ℹ Output is grouped by PID, language, group, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, group, word_meaning, FoR))` for
##   per-operation grouping (`?dplyr::dplyr_by`) instead.
## Warning: Removed 6 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 6 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 6 rows containing missing values or values outside the scale range
## (`geom_point()`).

Blind adults seem to do as well when the experimenter is facing them vs. standing behind.

Blind kids seem to be a little better when the experimenter is behind them.

Sighted adults don’t seem to differ, but weirdly enough is at chance for ‘right’ in Hindi when the experimenter is facing them (probably an artifact of too few trials). They fail at geocentric terms regardless.

ggplot(df.response_lcs %>% 
         group_by(PID, language, group, LCS.expt_pos, word_meaning, FoR) %>%
         mutate(
           LCS.expt_pos = recode_factor(LCS.expt_pos, 
                                        "facing" = "Experimenter facing", 
                                        "behind" = "Experimenter behind"),
           word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
               height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(group~language + LCS.expt_pos) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, group, LCS.expt_pos,
##   word_meaning, and FoR.
## ℹ Output is grouped by PID, language, group, LCS.expt_pos, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, group, LCS.expt_pos, word_meaning,
##   FoR))` for per-operation grouping (`?dplyr::dplyr_by`) instead.
## Warning: Removed 14 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 14 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 14 rows containing missing values or values outside the scale range
## (`geom_point()`).

No clear age trends in kids (age 6-13).

ggplot(df.response_lcs %>% 
         filter(group == "blind_children") %>%
         group_by(PID, language, LCS.expt_pos, age_months, word_meaning, FoR) %>%
         mutate(
           LCS.expt_pos = recode_factor(LCS.expt_pos, 
                                        "facing" = "Experimenter facing", 
                                        "behind" = "Experimenter behind"),
           word_meaning = factor(word_meaning, 
                                      levels = c("left", "right"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = round(age_months / 12, 2), y = mean_score, fill = FoR, color = FoR)) + 
  geom_smooth(method = "lm") + 
  facet_grid(~language + LCS.expt_pos) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  labs(x = "Age (years)") + 
  coord_cartesian(ylim = c(0,1))
## `summarise()` has regrouped the output.
## `geom_smooth()` using formula = 'y ~ x'
## ℹ Summaries were computed grouped by PID, language, LCS.expt_pos, age_months,
##   word_meaning, and FoR.
## ℹ Output is grouped by PID, language, LCS.expt_pos, age_months, and
##   word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, LCS.expt_pos, age_months, word_meaning,
##   FoR))` for per-operation grouping (`?dplyr::dplyr_by`) instead.

Relational Direction

Sighted adults were shown “north” and tested on east / west / south. For comparison we are only looking at their “south” trials. “Chance” is also different: blind participants chose between two coins and sighted adults chose between 4 coins. We also only have this data from

Blind adults and sighted adults succeed at “south” when north is already established, but not vice versa. They don’t succeed anywhere else. Common mistake is rotating the axis when asked for “north” again and after the duck is rotated.

Blind kids completely failed.

df.response_blind_rd <- df.response %>% 
  filter(task == "Language Comprehension - Duck" & FoR == "geocentric" & 
           group != "sighted_adults") %>%
  #exclude participants who did not have recorded raw response
  filter(is.na(LCD.raw_response_not_recorded))

df.response_sighted_adults_rd <- df.response_sighted_adults %>% 
  filter(task == "Relational Direction" & word_meaning == "south") %>%
  mutate(LCD.correct_relational_position = response) %>%
  mutate(LCD.prompted_reference_word_meaning = "north")

df.response_combined_rd <- bind_rows(df.response_blind_rd,
                                     df.response_sighted_adults_rd)
  
ggplot(df.response_combined_rd %>% 
         filter(!(trial %in% c(9, 13))) %>%
         group_by(PID, language, group, LCD.duck_pos, LCD.prompted_reference_word_meaning, word_meaning) %>%
         summarise(mean_score = mean(LCD.correct_relational_position, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = LCD.prompted_reference_word_meaning)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
              height = 0) + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange", 
               position = position_dodge(0.9)) +
  facet_grid(group~language + LCD.duck_pos) +
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted") + 
  labs(fill = "Reference word meaning")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, group, LCD.duck_pos,
##   LCD.prompted_reference_word_meaning, and word_meaning.
## ℹ Output is grouped by PID, language, group, LCD.duck_pos, and
##   LCD.prompted_reference_word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, group, LCD.duck_pos,
##   LCD.prompted_reference_word_meaning, word_meaning))` for per-operation
##   grouping (`?dplyr::dplyr_by`) instead.
## Warning: Removed 26 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 26 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 26 rows containing missing values or values outside the scale range
## (`geom_point()`).

Sighted adults succeeded in ‘south’ and ‘west’ in Hindi, but not ‘east’ in Hindi, and not in any terms in English. They only succeeded (in ‘south’ and ‘west’ in Hindi) when the duck is facing them.

df.response_sighted_adults_rd_all <- df.response_sighted_adults %>% 
  filter(task == "Relational Direction") %>%
  mutate(LCD.prompted_reference_word_meaning = "north") %>%
#exclude participants who did not have recorded raw response
  filter(is.na(LCD.raw_response_not_recorded))
  
ggplot(df.response_sighted_adults_rd_all %>% 
         group_by(PID, language, word_meaning, LCD.prompted_reference_word_meaning) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = LCD.prompted_reference_word_meaning)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
              height = 0) + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language) + 
  geom_hline(yintercept = 0.25, linetype = "dotted") +   
  labs(fill = "Reference word meaning")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, word_meaning, and
##   LCD.prompted_reference_word_meaning.
## ℹ Output is grouped by PID, language, and word_meaning.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, word_meaning,
##   LCD.prompted_reference_word_meaning))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
## Warning: Removed 9 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 9 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 9 rows containing missing values or values outside the scale range
## (`geom_point()`).

ggplot(df.response_sighted_adults_rd_all %>% 
         group_by(PID, language, word_meaning, LCD.duck_pos, LCD.prompted_reference_word_meaning) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = LCD.prompted_reference_word_meaning)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
              height = 0) + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language + LCD.duck_pos) + 
  geom_hline(yintercept = 0.25, linetype = "dotted") +   
  labs(fill = "Reference word meaning")
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID, language, word_meaning, LCD.duck_pos,
##   and LCD.prompted_reference_word_meaning.
## ℹ Output is grouped by PID, language, word_meaning, and LCD.duck_pos.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language, word_meaning, LCD.duck_pos,
##   LCD.prompted_reference_word_meaning))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
## Warning: Removed 21 rows containing non-finite outside the scale range
## (`stat_ydensity()`).
## Warning: Removed 21 rows containing non-finite outside the scale range
## (`stat_summary()`).
## Warning: Removed 21 rows containing missing values or values outside the scale range
## (`geom_point()`).

Sighted adults’ performance: more than 60% of trial sets show knowledge that east/west is opposite each other in Hindi. But only ~40% trial sets show this in English.

df.east_west <- df.response_sighted_adults_rd_all %>%
  filter(word_meaning %in% c("east", "west")) %>%
  group_by(PID, language, LCD.duck_pos) %>%
  summarise(
    east_position = LCD.response_position[word_meaning == "east"][1],
    west_position = LCD.response_position[word_meaning == "west"][1],
    degrees_away = ((west_position - east_position) %% 4) * 90,
    .groups = "drop"
  )

df.east_west %>%
  group_by(language, LCD.duck_pos) %>%
  summarise(
    n_trials = n(),
    n_180 = sum(degrees_away == 180, na.rm = TRUE),
    proportion_180 = n_180 / n_trials,
    .groups = "drop"
  )
## # A tibble: 4 × 5
##   language LCD.duck_pos n_trials n_180 proportion_180
##   <chr>    <chr>           <int> <int>          <dbl>
## 1 english  same               13     6          0.462
## 2 english  toward             13     5          0.385
## 3 hindi    same               13     8          0.615
## 4 hindi    toward             13     8          0.615
ggplot(
  df.east_west,
  aes(x = degrees_away)
) +
  geom_histogram(
    binwidth = 45,
  ) +
  facet_grid(
    LCD.duck_pos ~ language
  ) +
  scale_x_continuous(
    breaks = c(0, 90, 180, 270),
  ) +
  labs(
    x = "Degrees away",
    y = "Number of participants"
  ) 
## Warning: Removed 8 rows containing non-finite outside the scale range
## (`stat_bin()`).

df.east_west_by_ppt <- df.east_west %>%
  mutate(correct_east_west = case_when(
    is.na(degrees_away) ~ 0, 
    degrees_away == 180 ~ 1, 
    degrees_away != 180 ~ 0,
    TRUE ~ NA
  )) %>%
  group_by(PID, language) %>%
  summarise(mean_correct_east_west = mean(correct_east_west))
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by PID and language.
## ℹ Output is grouped by PID.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(PID, language))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.

Exploring raw response, first from blind participants. Red dots = correct answers. Grey dots = incorrect. Grey dots at the very top indicate participants that said “I don’t know” or did not provide a response.

Blind kids are completely guessing. Blind adults is a little better with Hindi (aligning with the plot above).

df.plot_blind_rd <- df.response_combined_rd %>%
  filter(group != "sighted_adults") %>%
  group_by(PID) %>%
  mutate(
    endpoint = case_when(
      LCD.degrees_away_from_ref == 0 &
        word_meaning == LCD.prompted_reference_word_meaning ~ "Consistent",
      LCD.degrees_away_from_ref == 180 &
        word_meaning != LCD.prompted_reference_word_meaning ~ "Consistent",
      
      TRUE ~ "Other")
  ) %>%
  ungroup()
  
ggplot(
  df.plot_blind_rd %>%
    filter(group == "blind_adults") %>%
    filter(
      trial %in% c(10:12, 14:16),
      #!is.na(LCD.degrees_away_from_ref)
    ) %>% 
    mutate(LCD.degrees_away_from_ref = ifelse(is.na(LCD.degrees_away_from_ref), 360, LCD.degrees_away_from_ref)),
  aes(
    x = word_meaning,
    y = LCD.degrees_away_from_ref,
    fill = endpoint
  )
) +
  geom_dotplot(
    binaxis = "y",
    stackdir = "center",
    binwidth = 10, 
    dotsize = 2
  ) +
  facet_grid(
     language ~LCD.duck_pos + LCD.prompted_reference_word_meaning
  ) +
  scale_y_continuous(
    name = "Degrees away",
    breaks = c(0, 90, 180, 270),
  ) +
  scale_fill_manual(
    values = c(
      "Consistent" = "red",
      "Other" = "gray70"
    )) +
  labs(
    x = "Word meaning",
    title = "Blind Adults"
  ) +
  theme(
    legend.position = "none",
  )

ggplot(
  df.plot_blind_rd %>%
    filter(group == "blind_children") %>%
    filter(
      trial %in% c(10:12, 14:16),
      #!is.na(LCD.degrees_away_from_ref)
    ) %>% 
    mutate(LCD.degrees_away_from_ref = ifelse(is.na(LCD.degrees_away_from_ref), 360, LCD.degrees_away_from_ref)),
  aes(
    x = word_meaning,
    y = LCD.degrees_away_from_ref,
    fill = endpoint
  )
) +
  geom_dotplot(
    binaxis = "y",
    stackdir = "center",
    binwidth = 10, 
    dotsize = 1.5
  ) +
  facet_grid(
     language ~LCD.duck_pos + LCD.prompted_reference_word_meaning
  ) +
  scale_y_continuous(
    name = "Degrees away",
    breaks = c(0, 90, 180, 270),
  ) +
  scale_fill_manual(
    values = c(
      "Consistent" = "red",
      "Other" = "gray70"
    )) +
  labs(
    x = "Word meaning",
    title = "Blind Children"
  ) +
  theme(
    legend.position = "none",
  )

Same plot for sighted adults’ raw responses. In Hindi, sighted adults seem to understand that east-west is a different axis compared to north-south (more red + yellow dots compared to gray dots). Red indicates correct location choice, yellow indicates incorrect location choice but correct axis, grey indicates wrong location choice or no response.

df.plot_sighted_rd <- df.response_sighted_adults %>%
  filter(task == "Relational Direction") %>%
  mutate(LCD.prompted_reference_word_meaning = "north") %>%
  group_by(PID) %>%
  mutate(
    endpoint = case_when(
      #never asked for north, but here for completion
      LCD.degrees_away_from_ref == 0 & word_meaning == "north" ~ "Consistent",
      LCD.degrees_away_from_ref == 180 & word_meaning == "north" ~ "Correct Axis",
      LCD.degrees_away_from_ref == 180 & word_meaning == "south" ~ "Consistent",
      LCD.degrees_away_from_ref == 0 & word_meaning == "south" ~ "Correct Axis",
      word_meaning == "east" & LCD.degrees_away_from_ref == 90 ~ "Consistent",
      word_meaning == "east" & LCD.degrees_away_from_ref == 270 ~ "Correct Axis",
      word_meaning == "west" & LCD.degrees_away_from_ref == 270 ~ "Consistent",
      word_meaning == "west" & LCD.degrees_away_from_ref == 90 ~ "Correct Axis",
      TRUE ~ "Other")
  ) %>%
  ungroup()

ggplot(
  df.plot_sighted_rd %>%
    mutate(LCD.degrees_away_from_ref = ifelse(is.na(LCD.degrees_away_from_ref), 360, LCD.degrees_away_from_ref)),
  #df.plot_sighted_rd %>%
    #filter(!is.na(LCD.degrees_away_from_ref)),
  aes(
    x = word_meaning,
    y = LCD.degrees_away_from_ref,
    fill = endpoint
  )
) +
  geom_dotplot(
    binaxis = "y",
    stackdir = "center",
    binwidth = 10,
    dotsize = 1.5
  ) +
  facet_grid(
    language ~ LCD.duck_pos
  ) +
  scale_y_continuous(
    name = "Degrees away",
    breaks = c(0, 90, 180, 270),
  ) +
  scale_fill_manual(
    values = c(
      "Consistent" = "red",
      "Correct Axis" = "yellow",
      "Other" = "gray70"
    )) +
  labs(
    x = "Word meaning",
  ) +
  theme(
    legend.position = "none"
  )

Regressions

Blind adults

Model: Language Comprehension - Object response (0/1) ~ Label FoR (Egocentric / Geocentric) * Language (English / Hindi) + (1|site/participant) Model: Language Comprehension - Self response (0/1) ~ Language (English / Hindi) + (1|site/participant)

Adults performed worse on geocentric terms, no other effects. Might need to remove site random effects due to singularity.

fit.lcd_adults <- glmer(response ~ FoR * language + (1|PID) + (1|site),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Duck" & group == "blind_adults"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))
## boundary (singular) fit: see help('isSingular')
summary(fit.lcd_adults)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ FoR * language + (1 | PID) + (1 | site)
##    Data: df.response %>% filter(task == "Language Comprehension - Duck" &  
##     group == "blind_adults")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     766.8     793.2    -377.4     754.8       594 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -2.0498 -1.0172  0.5316  0.7430  1.4490 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 0.3733   0.611   
##  site   (Intercept) 0.0000   0.000   
## Number of obs: 600, groups:  PID, 44; site, 2
## 
## Fixed effects:
##                             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                  0.94339    0.19289   4.891    1e-06 ***
## FoRgeocentric               -0.87589    0.25353  -3.455 0.000551 ***
## languagehindi                0.02946    0.23980   0.123 0.902213    
## FoRgeocentric:languagehindi  0.28649    0.35411   0.809 0.418483    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) FRgcnt lngghn
## FoRgeocntrc -0.584              
## languagehnd -0.618  0.470       
## FRgcntrc:ln  0.419 -0.693 -0.677
## optimizer (bobyqa) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')

Exploratory: add duck position –> better fit. Adults performed better with egocentric terms and when the duck is facing the same direction as them. There is a FoR x LCD.duck_pos significant interaction effect – they performed better on egocentric terms when the duck is facing the same direction as them compared to opposite direction, but duck position does not make a difference with geocentric terms.

fit.lcd_adults_duck_pos <- glmer(response ~ FoR * language * LCD.duck_pos + (1|PID),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Duck" & group == "blind_adults"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))

summary(fit.lcd_adults_duck_pos)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ FoR * language * LCD.duck_pos + (1 | PID)
##    Data: df.response %>% filter(task == "Language Comprehension - Duck" &  
##     group == "blind_adults")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     700.9     740.5    -341.5     682.9       591 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.3468 -0.8220  0.2988  0.7143  1.5989 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 0.5825   0.7632  
## Number of obs: 600, groups:  PID, 44
## 
## Fixed effects:
##                                                Estimate Std. Error z value
## (Intercept)                                      2.2656     0.3705   6.115
## FoRgeocentric                                   -2.0961     0.4416  -4.747
## languagehindi                                    0.2608     0.5109   0.511
## LCD.duck_postoward                              -2.1587     0.4178  -5.167
## FoRgeocentric:languagehindi                     -0.1163     0.6365  -0.183
## FoRgeocentric:LCD.duck_postoward                 2.0146     0.5636   3.575
## languagehindi:LCD.duck_postoward                -0.3125     0.6033  -0.518
## FoRgeocentric:languagehindi:LCD.duck_postoward   0.6768     0.8090   0.837
##                                                Pr(>|z|)    
## (Intercept)                                    9.65e-10 ***
## FoRgeocentric                                  2.07e-06 ***
## languagehindi                                  0.609669    
## LCD.duck_postoward                             2.38e-07 ***
## FoRgeocentric:languagehindi                    0.855030    
## FoRgeocentric:LCD.duck_postoward               0.000351 ***
## languagehindi:LCD.duck_postoward               0.604471    
## FoRgeocentric:languagehindi:LCD.duck_postoward 0.402815    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) FRgcnt lngghn LCD.d_ FRgcn: FR:LCD l:LCD.
## FoRgeocntrc -0.742                                          
## languagehnd -0.623  0.523                                   
## LCD.dck_pst -0.798  0.657  0.552                            
## FRgcntrc:ln  0.501 -0.676 -0.803 -0.444                     
## FRgcn:LCD._  0.590 -0.775 -0.409 -0.740  0.530              
## lnggh:LCD._  0.527 -0.443 -0.847 -0.671  0.680  0.498       
## FRgc::LCD._ -0.390  0.530  0.632  0.498 -0.787 -0.685 -0.746
fit.lcd_adults_duck_pos %>% 
  emmeans(specs = pairwise ~ FoR + LCD.duck_pos, 
          adjust = "none")
## NOTE: Results may be misleading due to involvement in interactions
## $emmeans
##  FoR        LCD.duck_pos emmean    SE  df asymp.LCL asymp.UCL
##  egocentric same         2.3960 0.291 Inf     1.826     2.966
##  geocentric same         0.2417 0.232 Inf    -0.213     0.696
##  egocentric toward       0.0811 0.198 Inf    -0.306     0.469
##  geocentric toward       0.2798 0.233 Inf    -0.177     0.737
## 
## Results are averaged over the levels of: language 
## Results are given on the logit (not the response) scale. 
## Confidence level used: 0.95 
## 
## $contrasts
##  contrast                              estimate    SE  df z.ratio p.value
##  egocentric same - geocentric same       2.1543 0.326 Inf   6.608 <0.0001
##  egocentric same - egocentric toward     2.3149 0.310 Inf   7.460 <0.0001
##  egocentric same - geocentric toward     2.1162 0.327 Inf   6.480 <0.0001
##  geocentric same - egocentric toward     0.1606 0.256 Inf   0.627  0.5305
##  geocentric same - geocentric toward    -0.0381 0.269 Inf  -0.142  0.8875
##  egocentric toward - geocentric toward  -0.1987 0.257 Inf  -0.773  0.4395
## 
## Results are averaged over the levels of: language 
## Results are given on the log odds ratio (not the response) scale.
anova(fit.lcd_adults_duck_pos, fit.lcd_adults)
## Data: df.response %>% filter(task == "Language Comprehension - Duck" &  ...
## Models:
## fit.lcd_adults: response ~ FoR * language + (1 | PID) + (1 | site)
## fit.lcd_adults_duck_pos: response ~ FoR * language * LCD.duck_pos + (1 | PID)
##                         npar    AIC    BIC  logLik -2*log(L)  Chisq Df
## fit.lcd_adults             6 766.82 793.21 -377.41    754.82          
## fit.lcd_adults_duck_pos    9 700.90 740.47 -341.45    682.90 71.924  3
##                         Pr(>Chisq)    
## fit.lcd_adults                        
## fit.lcd_adults_duck_pos  1.652e-15 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Self: Performed better in English compared to Hindi.

fit.lcs_adults <- glmer(response ~ language + (1|PID) + (1|site),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Self" & group == "blind_adults"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))
## boundary (singular) fit: see help('isSingular')
summary(fit.lcs_adults)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ language + (1 | PID) + (1 | site)
##    Data: df.response %>% filter(task == "Language Comprehension - Self" &  
##     group == "blind_adults")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     109.5     125.0     -50.8     101.5       348 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -11.5244   0.0053   0.0302   0.0302   1.3348 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 31.9     5.648   
##  site   (Intercept)  0.0     0.000   
## Number of obs: 352, groups:  PID, 44; site, 2
## 
## Fixed effects:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)     10.376      1.846   5.620 1.91e-08 ***
## languagehindi   -3.500      1.035  -3.383 0.000717 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr)
## languagehnd -0.497
## optimizer (bobyqa) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')

Exploratory: add experimenter position –> better fit, but convergence issues. Need to return to this.

fit.lcs_adults_expt_pos <- glmer(response ~ language * LCS.expt_pos + (1|PID),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Self" & group == "blind_adults"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))
## Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, :
## unable to evaluate scaled gradient
## Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, : Model failed to converge: degenerate  Hessian with 1 negative eigenvalues
##   See ?lme4::convergence and ?lme4::troubleshooting.
summary(fit.lcs_adults_expt_pos)
## Warning in vcov.merMod(object, use.hessian = use.hessian): variance-covariance matrix computed from finite-difference Hessian is
## not positive definite or contains NA values: falling back to var-cov estimated from RX
## Warning in vcov.merMod(object, correlation = correlation, sigm = sig): variance-covariance matrix computed from finite-difference Hessian is
## not positive definite or contains NA values: falling back to var-cov estimated from RX
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ language * LCS.expt_pos + (1 | PID)
##    Data: df.response %>% filter(task == "Language Comprehension - Self" &  
##     group == "blind_adults")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     104.7     124.0     -47.4      94.7       347 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -8.3978  0.0000  0.0057  0.0329  1.2504 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 39.22    6.262   
## Number of obs: 352, groups:  PID, 44
## 
## Fixed effects:
##                                  Estimate Std. Error z value Pr(>|z|)
## (Intercept)                         23.71     614.38   0.039    0.969
## languagehindi                      -15.46     614.38  -0.025    0.980
## LCS.expt_posfacing                 -13.50     614.38  -0.022    0.982
## languagehindi:LCS.expt_posfacing    11.97     614.38   0.019    0.984
## 
## Correlation of Fixed Effects:
##             (Intr) lngghn LCS.x_
## languagehnd -1.000              
## LCS.xpt_psf -1.000  1.000       
## lnggh:LCS._  1.000 -1.000 -1.000
## optimizer (bobyqa) convergence code: 0 (OK)
## unable to evaluate scaled gradient
## Model failed to converge: degenerate  Hessian with 1 negative eigenvalues
##   See ?lme4::convergence and ?lme4::troubleshooting.
anova(fit.lcs_adults_expt_pos, fit.lcs_adults)
## Data: df.response %>% filter(task == "Language Comprehension - Self" &  ...
## Models:
## fit.lcs_adults: response ~ language + (1 | PID) + (1 | site)
## fit.lcs_adults_expt_pos: response ~ language * LCS.expt_pos + (1 | PID)
##                         npar    AIC    BIC  logLik -2*log(L)  Chisq Df
## fit.lcs_adults             4 109.52 124.97 -50.758   101.517          
## fit.lcs_adults_expt_pos    5 104.72 124.04 -47.362    94.724 6.7924  1
##                         Pr(>Chisq)   
## fit.lcs_adults                       
## fit.lcs_adults_expt_pos   0.009154 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Blind kids

Model: Language Comprehension - Object response (0/1) ~ Population (Blind / Sighted) * Label FoR (Egocentric / Geocentric) * Language (English / Hindi) + Age + (1|site/participant) Model: Language Comprehension - Self response (0/1) ~ Population (Blind / Sighted) * Language (English / Hindi) + Age + (1|site/participant)

Duck task: No significant effects, including age.

fit.lcd_kids <- glmer(response ~ FoR * language + age_zscored + (1|PID),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Duck" & group == "blind_children"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))
## boundary (singular) fit: see help('isSingular')
summary(fit.lcd_kids)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ FoR * language + age_zscored + (1 | PID)
##    Data: df.response %>% filter(task == "Language Comprehension - Duck" &  
##     group == "blind_children")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##    1115.5    1143.7    -551.7    1103.5       810 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -1.3911 -1.1548  0.7744  0.8495  1.0104 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 0        0       
## Number of obs: 816, groups:  PID, 55
## 
## Fixed effects:
##                             Estimate Std. Error z value Pr(>|z|)   
## (Intercept)                  0.40825    0.13777   2.963  0.00304 **
## FoRgeocentric                0.08630    0.20417   0.423  0.67250   
## languagehindi               -0.09404    0.19399  -0.485  0.62783   
## age_zscored                 -0.08670    0.07110  -1.219  0.22273   
## FoRgeocentric:languagehindi -0.27833    0.28585  -0.974  0.33021   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) FRgcnt lngghn ag_zsc
## FoRgeocntrc -0.675                     
## languagehnd -0.710  0.479              
## age_zscored -0.021  0.030  0.001       
## FRgcntrc:ln  0.482 -0.713 -0.679  0.003
## optimizer (bobyqa) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')

Exploratory: add duck position –> better fit. Kids performed better with egocentric terms and when the duck is facing the same direction as them. There is a FoR x LCD.duck_pos significant interaction effect – kids performed better on egocentric terms when the duck is facing the same direction as them compared to opposite direction, but duck position does not make a difference with geocentric terms. No age effects.

fit.lcd_kids_duck_pos <- glmer(response ~ FoR * language * LCD.duck_pos + age_zscored + (1|PID),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Duck" & group == "blind_children"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))

summary(fit.lcd_kids_duck_pos)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ FoR * language * LCD.duck_pos + age_zscored + (1 |  
##     PID)
##    Data: df.response %>% filter(task == "Language Comprehension - Duck" &  
##     group == "blind_children")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##    1074.7    1121.8    -527.4    1054.7       806 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -2.0566 -0.9518  0.5479  0.8171  1.2872 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 0.005268 0.07258 
## Number of obs: 816, groups:  PID, 55
## 
## Fixed effects:
##                                                Estimate Std. Error z value
## (Intercept)                                     1.28223    0.23197   5.528
## FoRgeocentric                                  -0.71787    0.31580  -2.273
## languagehindi                                  -0.38629    0.31245  -1.236
## LCD.duck_postoward                             -1.61141    0.30229  -5.331
## age_zscored                                    -0.09171    0.07390  -1.241
## FoRgeocentric:languagehindi                     0.11750    0.43302   0.271
## FoRgeocentric:LCD.duck_postoward                1.47510    0.42690   3.455
## languagehindi:LCD.duck_postoward                0.49784    0.41479   1.200
## FoRgeocentric:languagehindi:LCD.duck_postoward -0.70541    0.59113  -1.193
##                                                Pr(>|z|)    
## (Intercept)                                    3.25e-08 ***
## FoRgeocentric                                  0.023016 *  
## languagehindi                                  0.216342    
## LCD.duck_postoward                             9.78e-08 ***
## age_zscored                                    0.214570    
## FoRgeocentric:languagehindi                    0.786116    
## FoRgeocentric:LCD.duck_postoward               0.000549 ***
## languagehindi:LCD.duck_postoward               0.230051    
## FoRgeocentric:languagehindi:LCD.duck_postoward 0.232744    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) FRgcnt lngghn LCD.d_ ag_zsc FRgcn: FR:LCD l:LCD.
## FoRgeocntrc -0.730                                                 
## languagehnd -0.738  0.541                                          
## LCD.dck_pst -0.767  0.560  0.567                                   
## age_zscored -0.022  0.027  0.003  0.014                            
## FRgcntrc:ln  0.532 -0.729 -0.721 -0.408  0.000                     
## FRgcn:LCD._  0.543 -0.739 -0.401 -0.708 -0.009  0.539              
## lnggh:LCD._  0.556 -0.408 -0.753 -0.726 -0.003  0.543  0.514       
## FRgc::LCD._ -0.391  0.534  0.529  0.510  0.003 -0.733 -0.721 -0.702
fit.lcd_kids_duck_pos %>% 
  emmeans(specs = pairwise ~ FoR + LCD.duck_pos, 
          adjust = "none")
## NOTE: Results may be misleading due to involvement in interactions
## $emmeans
##  FoR        LCD.duck_pos emmean    SE  df asymp.LCL asymp.UCL
##  egocentric same          1.091 0.157 Inf    0.7825   1.39911
##  geocentric same          0.432 0.151 Inf    0.1359   0.72751
##  egocentric toward       -0.272 0.137 Inf   -0.5398  -0.00349
##  geocentric toward        0.192 0.149 Inf   -0.0997   0.48289
## 
## Results are averaged over the levels of: language 
## Results are given on the logit (not the response) scale. 
## Confidence level used: 0.95 
## 
## $contrasts
##  contrast                              estimate    SE  df z.ratio p.value
##  egocentric same - geocentric same        0.659 0.217 Inf   3.041  0.0024
##  egocentric same - egocentric toward      1.362 0.208 Inf   6.543 <0.0001
##  egocentric same - geocentric toward      0.899 0.215 Inf   4.175 <0.0001
##  geocentric same - egocentric toward      0.703 0.204 Inf   3.456  0.0005
##  geocentric same - geocentric toward      0.240 0.211 Inf   1.140  0.2543
##  egocentric toward - geocentric toward   -0.463 0.202 Inf  -2.296  0.0217
## 
## Results are averaged over the levels of: language 
## Results are given on the log odds ratio (not the response) scale.
anova(fit.lcd_kids_duck_pos, fit.lcd_kids)
## Data: df.response %>% filter(task == "Language Comprehension - Duck" &  ...
## Models:
## fit.lcd_kids: response ~ FoR * language + age_zscored + (1 | PID)
## fit.lcd_kids_duck_pos: response ~ FoR * language * LCD.duck_pos + age_zscored + (1 | PID)
##                       npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
## fit.lcd_kids             6 1115.5 1143.7 -551.73    1103.5                     
## fit.lcd_kids_duck_pos   10 1074.7 1121.8 -527.36    1054.7 48.726  4  6.659e-10
##                          
## fit.lcd_kids             
## fit.lcd_kids_duck_pos ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Self: Performed better in English compared to Hindi.

fit.lcs_kids <- glmer(response ~ language + age_zscored + (1|PID),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Self" & group == "blind_children"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))

summary(fit.lcs_kids)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ language + age_zscored + (1 | PID)
##    Data: df.response %>% filter(task == "Language Comprehension - Self" &  
##     group == "blind_children")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     362.2     378.5    -177.1     354.2       436 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -4.1581  0.1068  0.2074  0.3629  2.1064 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 5.894    2.428   
## Number of obs: 440, groups:  PID, 55
## 
## Fixed effects:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)    3.22332    0.54566   5.907 3.48e-09 ***
## languagehindi -1.32713    0.32465  -4.088 4.35e-05 ***
## age_zscored    0.01601    0.38918   0.041    0.967    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) lngghn
## languagehnd -0.478       
## age_zscored -0.020 -0.001

Exploratory: add experimenter position –> better fit. Kids performed better with English terms, and when the experimenter is facing the same direction as them. No interaction effects or age effects.

fit.lcs_kids_expt_pos <- glmer(response ~ language * LCS.expt_pos + age_zscored + (1|PID),
                    data = df.response %>%
                      filter(task == "Language Comprehension - Self" & group == "blind_children"), 
                    family = binomial(link = 'logit'), 
                  control=glmerControl(optimizer="bobyqa",optCtrl=list(maxfun=100000)))

summary(fit.lcs_kids_expt_pos)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: response ~ language * LCS.expt_pos + age_zscored + (1 | PID)
##    Data: df.response %>% filter(task == "Language Comprehension - Self" &  
##     group == "blind_children")
## Control: glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+05))
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     355.5     380.1    -171.8     343.5       434 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -6.0419  0.0702  0.1542  0.2656  1.7786 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  PID    (Intercept) 6.548    2.559   
## Number of obs: 440, groups:  PID, 55
## 
## Fixed effects:
##                                  Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                       4.01103    0.67725   5.923 3.17e-09 ***
## languagehindi                    -1.57365    0.50860  -3.094  0.00197 ** 
## LCS.expt_posfacing               -1.20149    0.51201  -2.347  0.01895 *  
## age_zscored                       0.01663    0.40896   0.041  0.96756    
## languagehindi:LCS.expt_posfacing  0.31186    0.64792   0.481  0.63028    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) lngghn LCS.x_ ag_zsc
## languagehnd -0.566                     
## LCS.xpt_psf -0.533  0.640              
## age_zscored -0.017 -0.001 -0.001       
## lnggh:LCS._  0.360 -0.751 -0.773  0.000
anova(fit.lcs_kids_expt_pos, fit.lcs_kids)
## Data: df.response %>% filter(task == "Language Comprehension - Self" &  ...
## Models:
## fit.lcs_kids: response ~ language + age_zscored + (1 | PID)
## fit.lcs_kids_expt_pos: response ~ language * LCS.expt_pos + age_zscored + (1 | PID)
##                       npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
## fit.lcs_kids             4 362.19 378.53 -177.09    354.19                     
## fit.lcs_kids_expt_pos    6 355.54 380.06 -171.77    343.54 10.642  2   0.004888
##                         
## fit.lcs_kids            
## fit.lcs_kids_expt_pos **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

——————-EARLIER ANALYSES——————–

Blind Adults

Language Comprehension - Duck

df.response_blind_adults_lcd <- df.response_blind_adults %>% 
  filter(task == "Language Comprehension - Duck") %>% 
  # filter out geocentric trials that come after feedback for each language.
  filter(!(trial %in% 10:12 | trial %in% 14:16))
df.response_blind_adults_lcd |> pull(PID) |> unique() |> length()
df.response_blind_children_lcd |> pull(PID) |> unique() |> length()
  
ggplot(df.response_blind_adults_lcd %>% 
         group_by(PID, language, word_meaning, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_blind_adults_lcd %>% 
         group_by(PID, language, FoR) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = FoR, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_blind_adults_lcd %>% 
         group_by(PID, language, word_meaning, LCD.duck_pos, experimenter_LC, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(experimenter_LC~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")


ggplot(df.response_blind_adults_lcd %>% 
         filter(LCD.incorrect_axis != 1 | is.na(LCD.incorrect_axis)) %>% # only look at people who are correct or wrong, but answer in the correct axis
         group_by(PID, language, word_meaning, FoR, LCD.duck_pos) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCD.duck_pos~language) +
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.response_blind_adults_lcd %>% filter(response == 0 & is.na(LCD.incorrect_axis))

#when ppts are incorrect, they are picking the incorrect axis for geocentric 20% of the time, but not for egocentric
df.response_blind_adults_lcd %>%
  filter(response == 0) %>%
  group_by(language, word_meaning, FoR) %>%
  summarise(
    prop_incorrect_axis = sum(LCD.incorrect_axis == 1, na.rm = TRUE) / n(),
  )

df.response_blind_adults_lcd %>% 
  group_by(FoR, word_meaning) %>% 
  count(LCD.response_position)

Language Comprehension - Self

df.response_blind_adults_lcs <- df.response_blind_adults %>% 
  filter(task == "Language Comprehension - Self")
#%>%
  #if there is a note, save judgment
  # mutate(response = case_when(
  #   notes != "" & response == "" ~ NA, 
  #   response != "" ~ as.numeric(response), 
  #   .default = 0))
  
ggplot(df.response_blind_adults_lcs %>% 
         group_by(PID, language, word_meaning, FoR, LCS.expt_pos) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCS.expt_pos~language)

Relational Direction

6 participants did not have the raw data for all trials.

df.response_blind_adults_rd <- df.response_blind_adults %>% 
  filter(task == "Language Comprehension - Duck" & FoR == "geocentric")
  
ggplot(df.response_blind_adults_rd %>% 
         group_by(PID, language, LCD.duck_pos, LCD.prompted_reference_word_meaning, word_meaning) %>%
         summarise(mean_score = mean(LCD.correct_relational_position, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = LCD.prompted_reference_word_meaning)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange", 
               position = position_dodge(0.9)) +
  facet_grid(language~LCD.duck_pos) +
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_blind_adults_rd %>% 
         group_by(PID, trial, language, word_meaning) %>%
         summarise(mean_score = mean(LCD.correct_relational_position, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = language)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange", 
               position = position_dodge(0.9)) +
  facet_grid(~trial) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.plot <- df.response_blind_adults_rd %>%
  group_by(PID) %>%
  mutate(
    endpoint = case_when(
      LCD.degrees_away_from_ref == 0 &
        word_meaning == LCD.prompted_reference_word_meaning ~ "Consistent",
      
      LCD.degrees_away_from_ref == 180 &
        word_meaning != LCD.prompted_reference_word_meaning ~ "Consistent",
      
      TRUE ~ "Other")
  ) %>%
  ungroup()
  
ggplot(
  df.plot %>%
    filter(
      trial %in% c(10:12, 14:16),
      !is.na(LCD.degrees_away_from_ref)
    ),
  aes(
    x = word_meaning,
    y = LCD.degrees_away_from_ref,
    fill = endpoint
  )
) +
  geom_dotplot(
    binaxis = "y",
    stackdir = "center",
    binwidth = 10, 
  ) +
  facet_grid(
    language ~ LCD.duck_pos + LCD.prompted_reference_word_meaning
  ) +
  scale_y_continuous(
    name = "Degrees away",
    breaks = c(0, 90, 180, 270),
  ) +
  scale_fill_manual(
    values = c(
      "Consistent" = "red",
      "Other" = "gray70"
    )) +
  labs(
    x = "Word meaning",
  ) +
  theme(
    legend.position = "none"
  )

Sighted Adults

Language Comprehension - Duck

df.response_sighted_adults_lcd <- df.response_sighted_adults %>% 
  filter(task == "Language Comprehension - Duck") %>%
  filter(!(group == "sighted_adults" & experimenter_LC == "OC" & (trial %in% 10:12 | trial %in% 14:16)))

df.response_sighted_adults_lcd$PID %>% unique()
#%>%
  #if there is a note, save judgment
  # mutate(response = case_when(
  #   notes != "" & response == "" ~ NA, 
  #   response != "" ~ as.numeric(response), 
  #   .default = 0))
  

df.response_sighted_adults_lcd %>% 
         group_by(language, FoR, LCD.duck_pos) %>%
         summarise(mean_score = mean(response, na.rm = T))
ggplot(df.response_sighted_adults_lcd %>% 
         group_by(PID, language, LCD.duck_pos, word_meaning, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCD.duck_pos~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.response_sighted_adults_lcd %>% 
         group_by(language, FoR, LCD.duck_pos) %>%
         summarise(mean_score = mean(response, na.rm = T))

ggplot(df.response_sighted_adults_lcd %>% 
         group_by(PID, language, experimenter_LC, LCD.duck_pos, word_meaning, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, 
               height = 0) + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(experimenter_LC~language + LCD.duck_pos) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_sighted_adults_lcd %>% 
         group_by(PID, language, LCD.duck_pos, FoR) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = FoR, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCD.duck_pos~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_sighted_adults_lcd %>% 
         filter(LCD.incorrect_axis != 1 | is.na(LCD.incorrect_axis)) %>% # only look at people who are correct or wrong, but answer in the correct axis
         group_by(PID, language, word_meaning, FoR, LCD.duck_pos) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCD.duck_pos~language) +
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.response_sighted_adults_lcd %>% filter(response == 0 & is.na(LCD.incorrect_axis))

#when ppts are incorrect, they are picking the incorrect axis for geocentric half the time in Hindi. But errors in English are more spread out...
df.response_sighted_adults_lcd %>%
  filter(response == 0) %>%
  group_by(language, word_meaning, FoR) %>%
  filter(!is.na(LCD.incorrect_axis)) %>%
  summarise(
    prop_incorrect_axis = sum(LCD.incorrect_axis == 1, na.rm = TRUE) / n(),
  )

df.response_sighted_adults_lcd %>% 
  group_by(language, FoR, word_meaning) %>% 
  count(LCD.response_position)

x <- df.response_sighted_adults_lcd %>%
  filter(language == "hindi" & FoR == "egocentric" & LCD.duck_pos == "toward") %>%
  dplyr::select(PID, language, FoR, trial, response, LCD.correct_response_position, LCD.correct_response) 
  hist(x$response)

Language Comprehension - Self

df.response_sighted_adults_lcs <- df.response_sighted_adults %>% 
  filter(task == "Language Comprehension - Self")
#%>%
  #if there is a note, save judgment
  # mutate(response = case_when(
  #   notes != "" & response == "" ~ NA, 
  #   response != "" ~ as.numeric(response), 
  #   .default = 0))
  
ggplot(df.response_sighted_adults_lcs %>% 
         group_by(PID, language, word_meaning, FoR, LCS.expt_pos) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCS.expt_pos~language)

Relational Direction

df.response_sighted_adults_rd <- df.response_sighted_adults %>% 
  filter(task == "Relational Direction")

df.response_sighted_adults_rd$PID %>% unique() %>% length() # only 13 participants
  
ggplot(df.response_sighted_adults_rd %>% 
         group_by(PID, language, word_meaning, FoR) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language) + 
  geom_hline(yintercept = 1/3, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_sighted_adults_rd %>%
         group_by(PID, language, word_meaning, FoR, LCD.duck_pos) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCD.duck_pos~language) +
  geom_hline(yintercept = 1/3, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.response_sighted_adults_rd %>% 
  group_by(FoR, word_meaning) %>% 
  count(LCD.response_position)


df.east_west <- df.response_sighted_adults_rd %>%
  filter(word_meaning %in% c("east", "west")) %>%
  group_by(PID, language, LCD.duck_pos) %>%
  summarise(
    east_position = LCD.response_position[word_meaning == "east"][1],
    west_position = LCD.response_position[word_meaning == "west"][1],
    
    degrees_away = ((west_position - east_position) %% 4) * 90,
    
    .groups = "drop"
  )
  
ggplot(
  df.east_west,
  aes(x = degrees_away)
) +
  geom_histogram(
    binwidth = 45,
  ) +
  facet_grid(
    LCD.duck_pos ~ language
  ) +
  scale_x_continuous(
    breaks = c(0, 90, 180, 270),
  ) +
  labs(
    x = "Degrees away",
    y = "Number of participants"
  ) +
  theme_classic()

Blind Kids

Language Comprehension - Duck

df.response_blind_kids_lcd <- df.response_blind_kids %>% 
  filter(task == "Language Comprehension - Duck") %>% 
  filter(!(trial %in% 10:12 | trial %in% 14:16))
df.response_blind_kids_lcd |> pull(PID) |> unique() |> length()
df.response_sighted_adults_lcd |> pull(PID) |> unique() |> length()
#%>%
  #if there is a note, save judgment
  # mutate(response = case_when(
  #   notes != "" & response == "" ~ NA, 
  #   response != "" ~ as.numeric(response), 
  #   .default = 0))
  
ggplot(df.response_blind_kids_lcd %>% 
         group_by(PID, language, word_meaning, FoR) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_blind_kids_lcd %>% 
         group_by(PID, language, FoR) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = FoR, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  geom_jitter(alpha = 0.3, height = 0) +
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(~language) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_blind_kids_lcd %>% 
         filter(LCD.incorrect_axis != 1 | is.na(LCD.incorrect_axis)) %>% # only look at people who are correct or wrong, but answer in the correct axis
         group_by(PID, language, word_meaning, FoR, LCD.duck_pos) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right", "north", "south"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCD.duck_pos~language) +
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.response_blind_kids_lcd %>% filter(response == 0 & is.na(LCD.incorrect_axis))

df.response_blind_kids_lcd %>%
  filter(response == 0) %>%
  group_by(language, word_meaning, FoR) %>%
  summarise(
    prop_incorrect_axis = sum(LCD.incorrect_axis == 1, na.rm = TRUE) / n(),
  )

df.response_blind_kids_lcd %>% 
  group_by(FoR, word_meaning) %>% 
  count(LCD.response_position)

Language Comprehension - Self

df.response_blind_kids_lcs <- df.response_blind_kids %>% 
  filter(task == "Language Comprehension - Self")
#%>%
  #if there is a note, save judgment
  # mutate(response = case_when(
  #   notes != "" & response == "" ~ NA, 
  #   response != "" ~ as.numeric(response), 
  #   .default = 0))
  
ggplot(df.response_blind_kids_lcs %>% 
         group_by(PID, language, word_meaning, FoR, LCS.expt_pos) %>%
         mutate(word_meaning = factor(word_meaning, 
                                      levels = c("left", "right"))) %>%
         summarise(mean_score = mean(response, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = FoR)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange") +
  facet_grid(LCS.expt_pos~language)

Relational Direction

4 participants did not have this data for all trials…

df.response_blind_kids_rd <- df.response_blind_kids %>% 
  filter(task == "Language Comprehension - Duck" & FoR == "geocentric")
#%>%
  #if there is a note, save judgment
  # mutate(response = case_when(
  #   notes != "" & response == "" ~ NA, 
  #   response != "" ~ as.numeric(response), 
  #   .default = 0))
  
ggplot(df.response_blind_kids_rd %>% 
         group_by(PID, language, LCD.duck_pos, LCD.prompted_reference_word_meaning, word_meaning) %>%
         summarise(mean_score = mean(LCD.correct_relational_position, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = LCD.prompted_reference_word_meaning)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange", 
               position = position_dodge(0.9)) +
  facet_grid(language~LCD.duck_pos) +
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

ggplot(df.response_blind_kids_rd %>% 
         group_by(PID, trial, language, word_meaning) %>%
         summarise(mean_score = mean(LCD.correct_relational_position, na.rm = T)), 
       aes(x = word_meaning, y = mean_score, fill = language)) + 
  geom_violin() + 
  stat_summary(fun.data = "mean_cl_boot", 
               geom = "pointrange", 
               position = position_dodge(0.9)) +
  facet_grid(~trial) + 
  geom_hline(yintercept = 0.5, linetype = "dashed") + 
  geom_hline(yintercept = 0.25, linetype = "dotted")

df.plot <- df.response_blind_kids_rd %>%
  group_by(PID) %>%
  mutate(
    endpoint = case_when(
      LCD.degrees_away_from_ref == 0 &
        word_meaning == LCD.prompted_reference_word_meaning ~ "Consistent",
      
      LCD.degrees_away_from_ref == 180 &
        word_meaning != LCD.prompted_reference_word_meaning ~ "Consistent",
      
      TRUE ~ "Other")
  ) %>%
  ungroup()
  
ggplot(
  df.plot %>%
    filter(
      trial %in% c(10:12, 14:16),
      !is.na(LCD.degrees_away_from_ref)
    ),
  aes(
    x = word_meaning,
    y = LCD.degrees_away_from_ref,
    fill = endpoint
  )
) +
  geom_dotplot(
    binaxis = "y",
    stackdir = "center",
    binwidth = 10, 
  ) +
  facet_grid(
    language ~ LCD.duck_pos + LCD.prompted_reference_word_meaning
  ) +
  scale_y_continuous(
    name = "Degrees away",
    breaks = c(0, 90, 180, 270),
  ) +
  scale_fill_manual(
    values = c(
      "Consistent" = "red",
      "Other" = "gray70"
    )) +
  labs(
    x = "Word meaning",
  ) +
  theme(
    legend.position = "none"
  )

Session Info

session_info()