knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning=FALSE) library(tidyverse) library(patchwork) ephys <- qs::qread(here::here("data-raw", "ephys.qs"))
color <- ephys %>% filter(correct) %>% group_by(date, color, task, period, region, electrode, unit, id) %>% summarise( firing_rate = mean(firing_rate), N = n(), .groups = "drop") nrs <- color %>% select(-N) %>% pivot_wider(names_from = task, values_from = firing_rate, names_prefix = "task_") %>% filter(!is.na(task_color), !is.na(task_motion)) %>% group_nest(date, period, region, electrode, unit) %>% rowwise() %>% mutate(nr = nrow(data)) %>% ungroup()
I've run just a few simple analyses. First, I excluded all neurons that were shown only 1 matching color in each of the motion and color tasks. This excluded some neurons because the activity of many neurons was only recorded when the monkey was shown a single color, or different colors for their motion and color trials. Estimating a slope requires at least 2 colors.
# distribution of colors available nrs %>% ggplot(aes(x=nr)) + facet_wrap(~region) + geom_bar() + xlab("Number of matching colors")
On these filtered data, I then averaged firing rates to prepare for the orthogonal regression. The fMRI data comprised a single beta for each orientation and level of contrast, within a run (stimuli were presented multiple times, but only a single beta was used for all repetitions). So, for the fMRI data it was straightforward to construct pairs of beta values. In contrast, within a single day, monkeys, were asked to do the same task for some combinations of color/motion stimuli repeatedly. So, I averaged all repetitions within a day (results were similar with medians). For many neurons, this average included over 50 trials, although a few neurons had many fewer trials.
color %>% ggplot() + facet_wrap(~region) + geom_histogram(aes(x=N)) + xlab("Number of Trials")
slopes <- nrs %>% filter(nr > 1) %>% mutate( value = map(data, ~pracma::odregress(.x$task_color, .x$task_motion)), slope = map_dbl(value, ~.x$coeff[[1]]), intercept = map_dbl(value, ~.x$coeff[[2]]), avg_diff = map_dbl(data, ~mean(.x$task_motion - .x$task_color)), angle = atan2(slope, 1)) %>% select(-data, -value) %>% pivot_longer(c(slope, intercept, angle, avg_diff), names_to = "coeff", values_to = "estimate") %>% mutate(estimate = if_else(coeff == "angle", circular::deg(estimate), estimate)) ang_medians <- slopes %>% filter(coeff == "angle") %>% mutate( estimate = circular::circular(.data$estimate, type="angles", units = "degrees", modulo = "pi")) %>% group_by(coeff, period, region) %>% summarise( med = circular::median.circular(estimate), .groups = "drop") %>% mutate(med = as.numeric(med)) medians <- slopes %>% filter(!coeff == "angle") %>% group_by(coeff, period, region) %>% summarise( med = median(estimate), .groups = "drop") %>% bind_rows(ang_medians) %>% mutate( label = glue::glue("{round(med, digits=1)}"), vjust = if_else(region=="V4", 2, 4))
Then, I calculated the slope and intercept from orthogonal regression. I did this separately for the activity during the cue period and the sample period. These period were defined by Jefferson: the 'cue' was the second half of the cue period (i.e., a period of 500ms before stimulus onset), and the 'sample' was from 0-280ms after stimulus onset.
plot_estimate <- function(d, medians, co){ medians <- medians %>% filter(coeff=={{co}}) d %>% filter(coeff=={{co}}) %>% ggplot() + facet_wrap(~period, ncol=1, strip.position = "right") + geom_histogram(aes(x=estimate, fill=region), position=position_dodge()) + geom_vline( aes(xintercept=med, color=region), linetype="dashed", data = medians) + geom_text( aes(label=label, x=med, color=region, vjust=vjust), y=Inf, hjust = -.4, data = medians) + xlab(rlang::as_name(co)) } s <- plot_estimate(slopes, medians, "slope") + xlim(-20, 20) i <- plot_estimate(slopes, medians, "intercept") + xlim(-100, 100) a <- plot_estimate(slopes, medians, "angle") + xlim(-90, 90) ad <- plot_estimate(slopes, medians, "avg_diff") s + i + a + ad + plot_layout(guides = 'collect') + xlim(-20,20) + ggsave( "parameters.png", height = 7)
ephys %>% nmmr::WISEsummary( dependentvars = "firing_rate", betweenvars = "region", withinvars = c("task", "period", "correct"), idvar = "id") %>% ggplot(aes(x=period, color = task)) + facet_grid(correct~region, labeller = label_both) + geom_errorbar( aes( ymin = firing_rate_CI_lower, ymax = firing_rate_CI_upper), position = position_dodge(), width = 0.6) + ylim(0,NA)
ephys %>% group_by(id, correct, period, region, task) %>% summarise( sdeviation = sd(firing_rate), .groups = "drop") %>% pivot_wider(names_from = task, values_from = sdeviation) %>% ggplot(aes(x=color, y = motion)) + coord_fixed() + geom_abline(slope = 1, intercept = 0) + geom_point(aes(color = region), alpha = 0.2) + facet_grid(correct ~ period)
color <- ephys %>% group_by(date, color, task, period, region, electrode, unit, id, correct) %>% summarise( firing_rate = mean(firing_rate), N = n(), .groups = "drop")
color %>% ggplot() + facet_grid(correct~region) + geom_histogram(aes(x=N)) + xlab("Number of Trials")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.