The DoOR.functions
package provides tools for the analysis of the data provided by the
DoOR.data
package.
First we need to load packages and data:
identify_sensillum()
{#identify_sensillum} Imagine we perform an electrophysiological
recording from a Drosophila sensillum (single sensillum
recording, SSR) and we are not sure what sensillum we are recording
from. In order to identify the sensillum we used several diagnostic
odorants (maybe selected using private_odorant()
) and got
responses from the different sensory neurons the sensillum houses. We
can now pass our recorded data to identify_sensillum()
.
Let’s make up some simple fake data. We pretend to have recorded with
three odorants (2,3-butanedione, ethanoic acid and carbon dioxide) and
we could separate the responses of two units. Unit1 responded strongly
to 2,3-butanedione only, unit2 only responded to carbon dioxide. We
create a data.frame that contains a column called odorants
with the InChIKeys of our test odorants, and one column for each unit
(name the colnames as you like, e.g. unit1-n or Aneuro if you are sure
about the neuron).
recording <- data.frame(
odorants = c(trans_id(c("BEDN", "ETAS"), "Code"),
trans_id("carbon dioxide", "Name")),
unit1 = c(.9,.1,.1),
unit2 = c(0, .1, 1)
)
Next we feed the recording to the function:
## found correlations above 0.9 for all 2 units in: ab1
Note that the function tells us that it found hits for all units in ab1
and ab5, meaning that e.g. within the four neurons housed in the ab1
sensillum both of our units had good matches. You can set this
correlation threshold with min.cor
. If we increase the
threshold to 0.99 only ab1 is returned as a double match:
## found correlations above 0.99 for all 2 units in: ab1
We can define the number of best hits that we want to get returned (the default is 10):
## found correlations above 0.9 for all 2 units in: ab1
And if we know e.g. that we are recording from a basiconic sensillum we can restrict the search to one or a few sensilla types:
## found correlations above 0.9 for all 2 units in: ab1
Instead of correlations we can also use the Euclidean distance as a (dis)similarity measure:
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_bar()`).
We can also return the correlation/distance data instead of the plot
when setting plot =FALSE
:
sensillumX <-
identify_sensillum(recording,
method = "dist",
sub = "ab",
plot = FALSE)
head(sensillumX)
## receptor sensillum OSN unit1 unit2
## Or7a Or7a ab4 ab4A 0.7131111 0.9430477
## Or9a Or9a ab8 ab8B 0.5688357 1.0031663
## Or10a Or10a ab1 ab1D 0.8497591 0.9363670
## Or13a Or13a ab6 ab6A 0.6763409 0.8639653
## Or22a Or22a ab3 ab3A 0.6596756 0.9979447
## Or22b Or22b ab3 ab3A 0.1732051 1.7320508
So apparently our fake recording came from the ab1 sensillum, which was admittedly quite obvious as we had a strong carbon dioxide response and ab1 houses the carbon dioxide receptor :)
private_odorant()
There may be several cases where we might be interested in so called
private odorants, odorants that specifically activate a given
receptor or sensory neuron. Maybe we are looking for diagnostic odorants
for sensillum identification or we want to activate a specific neuronal
pathway, private_odorant()
returns candidate odorants for
that task.
Let’s say we want to specifically activate Or22a neurons:
## Or22a max.others n difference
## GQKZRWSUJHVIPE-UHFFFAOYSA-N 0.43570150 -0.001626778 4 0.4373283
## SHZIWNPUGXLXDT-UHFFFAOYSA-N 0.78193674 0.417131636 29 0.3648051
## JGHZJRVDZXSNKQ-UHFFFAOYSA-N 0.52492990 0.243725635 28 0.2812043
## HCRBXQFHJMCTLF-UHFFFAOYSA-N 0.53683628 0.368857494 20 0.1679788
## SMQUZDBALVYZAC-UHFFFAOYSA-N 0.07433042 -0.075854326 1 0.1501847
We might want to return the odorant names instead of InChiKeys:
## Or22a max.others n difference
## sec-amyl acetate 0.43570150 -0.001626778 4 0.4373283
## ethyl hexanoate 0.78193674 0.417131636 29 0.3648051
## methyl octanoate 0.52492990 0.243725635 28 0.2812043
## ethyl 2-methylbutanoate 0.53683628 0.368857494 20 0.1679788
## salicyl aldehyde 0.07433042 -0.075854326 1 0.1501847
So according to the function sec-amyl acetate would be a good candidate. It activates Or22a at 0.4 (DoOR response, max is 1) while the maximum activation in all other tested responding units (receptors, neurons, glomeruli) is 0.016, a difference of 0.40. Sounds good, but it was tested only in 4 other responding units, so I would rather go for ethyl hexanoate with about the same difference but being tested in 29 other responding units.
We can also restrict the search to the sensillum the responding units of interest is related to:
##
## >> Checking only against the ab3 sensillum (Or22a, Or22b, Or85b) <<
## Or22a max.others n difference
## ethyl hexanoate 0.7819367 0.23844314 1 0.5434936
## ethyl 2-methylbutanoate 0.5368363 0.03107633 1 0.5057599
## diethyl succinate 0.4659812 0.05652431 1 0.4094569
## methyl octanoate 0.5249299 0.14747177 1 0.3774581
## E2-hexenyl acetate 0.4271672 0.05042492 1 0.3767423
Ethyl 2-methylbutanoate sounds like a good hit, it has the same difference to the other units as ethyl hexanoate but hardly elicits a response at all from the other neuron. The n of 1 is fine as there are only 2 neurons housed in the ab3 sensillum.
map_receptor()
{#map_receptor} Similar to identify_sensillum()
,
map_receptor()
correlates a response vector to all
responding units of the existing DoOR consensus data. Let’s grab a data
set from Or22a and see where it ends up:
data <- data.frame(odorants = Or22a$InChIKey, responses = Or22a$Hallem.2006.EN)
data <- na.omit(data)
head(data)
## odorants responses
## 1 SFR 4
## 3 VHUUQVKOLVNVRT-UHFFFAOYSA-N 17
## 4 KIDHWZJUCRJVML-UHFFFAOYSA-N 16
## 5 VHRGRCVQAFMJIZ-UHFFFAOYSA-N 17
## 11 YEJRWHAVMIAJKC-UHFFFAOYSA-N 47
## 12 JBFHTYHTHYHCDJ-UHFFFAOYSA-N 144
## skipped Or22c, Or24a, Or67d, pb2A as overlap (n) was < 3
## responding.unit n cor p.value
## Or22a Or22a 111 0.9658036 9.929338e-66
## Or47a Or47a 111 0.7087026 3.270693e-18
## Or59c Or59c 18 0.6657733 2.561096e-03
## Or43b Or43b 111 0.6322969 9.835057e-14
## Or2a Or2a 111 0.6157970 6.342287e-13
This example was a bit circular as the tested data contributed to the consensus data…
back_project()
{#back_project} The DoOR consensus data is normalized to values
between 0 and 1. If we want to compare the DoOR data to one of our own
recordings, it would be great to have the DoOR data in the same unit as
our owne data (e.g. spikerate). This is what we can do with
back_project()
, we can rescale the DoOR data to fit a given
response template.
As an example, let’s take the data Hallem et al. recorded from Or22a
via calcium imaging and rescale the DoOR responses accordingly.
The template has to have 2 columns named odorants
and
responses
:
template <- data.frame(odorants = Or22a$InChIKey,
responses = Or22a$Hallem.2006.EN)
bp <- back_project(template, responding.unit = "Or22a")
## Warning in getInitial.selfStart(func, data, mCall = as.list(match.call(func, :
## selfStart initializing functions should have a final '...' argument since R
## 4.1.0
## Warning in getInitial.selfStart(func, data, mCall = as.list(match.call(func, :
## selfStart initializing functions should have a final '...' argument since R
## 4.1.0
plot(bp$back_projected$original.data,
bp$back_projected$back_projected.data,
xlab = "DoOR consensus response",
ylab = "back_projected data [spikes, Hallem.2006.EN]"
)
## odorants back_projected.data original.data template.data
## 1 SFR 6.620103 0.08448516 4
## 2 XLYOFNOQVPJJNP-UHFFFAOYSA-N 133.355630 0.36799124 NA
## 3 VHUUQVKOLVNVRT-UHFFFAOYSA-N 29.409493 0.18498406 17
## 4 KIDHWZJUCRJVML-UHFFFAOYSA-N 27.357834 0.17511431 16
## 5 VHRGRCVQAFMJIZ-UHFFFAOYSA-N 16.069298 0.08989517 17
## 6 QGZKDVFQNNGYKY-UHFFFAOYSA-N 13.558241 0.08805285 NA
## fitted.data
## 1 0.06872184
## 2 0.51358830
## 3 0.14871705
## 4 0.14151533
## 5 0.10189036
## 6 0.09307606
All the yellow lines in the first plot represent odorant responses that were not available in the original data set but were projected onto the fitted function and rescaled to the units in “Hallem.2006.EN”. The second plot shows the relationship between the rescaled data and the original consensus responses.
sparse()
The width of a tuning curve, i.e. for example to how many odorants a
receptor shows strong responses, can be quantified using different
sparseness measures. We implemented kurtosis1 and sparseness2 in
sparse()
. A high kurtosis or sparseness value indicates a
narrow tuning curve (see also dplot_tuningCurve()
in the DoOR visualization
vignette). While kurtosis is able to deal with negative values,
sparseness can’t and thus all values need to be be transformed to
absolute values first. Sparseness scales between 0 and 1, kurtosis
between -∞ and ∞, a kurtisis of 0 corresponds to the Gaussian
distribution.
rm.SFRreset <- reset_sfr(door_response_matrix, "SFR")
sparse(x = rm.SFRreset[,"Or69a"], method = "ltk")
## [1] -0.2615938
## [1] 0.1486392
## [1] 23.57197
## [1] 0.7596749
Willmore, B., Tolhurst, D.J., 2001. Characterizing the sparseness of neural codes. Network 12, 255–270. dx.doi.org/10.1080/net.12.3.255.270↩︎
Bhandawat, V., Olsen, S.R., Gouwens, N.W., Schlief, M.L., Wilson, R.I., 2007. Sensory processing in the Drosophila antennal lobe increases reliability and separability of ensemble odor representations. Nature neuroscience 10, 1474–82. dx.doi.org/10.1038/nn1976↩︎
identify_sensillum()
private_odorant()
map_receptor()
back_project()
sparse()