{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Analyzing Receptive Fields in Neuropixels Visual Coding Data\n", "\n", "## Tutorial overview\n", "\n", "One of the most important features of visually responsive neurons is the location and extent of their receptive field. Is it highly localized or spatially distributed? Is it centered on the stimulus display, or is it on an edge? How much does it overlap with the receptive fields of neurons in other regions? Obtaining answers to these questions is a crucial step for interpreting a results related to neurons' visual coding properties.\n", "\n", "This Jupyter notebook will cover the following topics:\n", "* Understanding the receptive field stimulus used in the Neuropixels Visual Coding experiments\n", "* Plotting receptive fields for individual units\n", "* Obtaining pre-computed metrics related to receptive fields (coming soon)\n", "* Finding experiments of interest based on receptive field overlap (coming soon)\n", "\n", "This tutorial assumes you've already created a data cache, or are working with the files on AWS. If you haven't reached that step yet, we recommend going through the [data access tutorial](./ecephys_data_access.ipynb) first.\n", "\n", "Functions related to additional aspects of data analysis will be covered in other tutorials. For a full list of available tutorials, see the [SDK documentation](https://allensdk.readthedocs.io/en/latest/visual_coding_neuropixels.html).\n", "\n", "Let's start by creating an `EcephysProjectCache` object, and pointing it to a new or existing manifest file:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "import numpy as np\n", "import pandas as pd\n", "\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "\n", "from allensdk.brain_observatory.ecephys.ecephys_project_cache import EcephysProjectCache" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you're not sure what a manifest file is or where to put it, please check out [this tutorial](./ecephys_data_access.ipynb) before going further." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Example cache directory path, it determines where downloaded data will be stored\n", "data_directory = '/local1/ecephys_cache_dir/'\n", "\n", "manifest_path = os.path.join(data_directory, \"manifest.json\")\n", "\n", "cache = EcephysProjectCache.from_warehouse(manifest=manifest_path)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's load the `sessions` table and grab the data for one experiment in the list:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING:root:downloading a 1.78e+03mb file from http://api.brain-map.org//api/v2/well_known_file_download/1026123377\n" ] } ], "source": [ "sessions = cache.get_session_table()\n", "\n", "session = cache.get_session_data(sessions.index.values[20])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Receptive field mapping stimulus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `session` object contains all of the spike data for one recording session, as well as information about the stimulus, mouse behavior, and probes.\n", "\n", "Because receptive field analysis is so central to interpreting results related to visual coding, every experiment in the Neuropixels Visual Coding dataset includes a standardized receptive field mapping stimulus. This stimulus is always shown at the beginning of the session, and uses the same parameters for every mouse. \n", "\n", "We can look at the `stimulus_presentations` DataFrame in order to examine the parameters of the receptive field mapping stimulus in more detail. The receptive field mapping stimulus consists of drifting Gabor patches with a circular mask, so we're going to filter the DataFrame based on `stimulus_name == 'gabors'`:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3645" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rf_stim_table = session.stimulus_presentations[session.stimulus_presentations.stimulus_name == 'gabors']\n", "\n", "len(rf_stim_table)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are 3645 trials for the receptive field mapping stimulus. What combination of stimulus parameters is used across these trials? Let's see which parameters actually vary for this stimulus:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['orientation',\n", " 'start_time',\n", " 'stop_time',\n", " 'x_position',\n", " 'y_position',\n", " 'duration',\n", " 'stimulus_condition_id']" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "keys = rf_stim_table.keys()\n", "[key for key in keys if len(np.unique(rf_stim_table[key])) > 1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can ignore the parameters related to stimulus timing (`start_time`, `stop_time`, and `duration`), as well as `stimulus_condition_id`, which is used to find presentations with the same parameters. So we're left with `orientation`, `x_position`, and `y_position`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Unique orientations : [0.0, 45.0, 90.0]\n", "Unique x positions : [-40.0, -30.0, -20.0, -10.0, 0.0, 10.0, 20.0, 30.0, 40.0]\n", "Unique y positions : [-40.0, -30.0, -20.0, -10.0, 0.0, 10.0, 20.0, 30.0, 40.0]\n" ] } ], "source": [ "print('Unique orientations : ' + str(list(np.sort(rf_stim_table.orientation.unique()))))\n", "print('Unique x positions : ' + str(list(np.sort(rf_stim_table.x_position.unique()))))\n", "print('Unique y positions : ' + str(list(np.sort(rf_stim_table.y_position.unique()))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have 3 orientations and a 9 x 9 grid of spatial locations. Note that these locations are relative to the center of the screen, not the mouse's center of gaze. How many repeats are there for each condition?" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15.0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(rf_stim_table) / (3 * 9 * 9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This should match the number we get when dividing the length of the DataFrame by the total number of conditions:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15.0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(rf_stim_table) / len(np.unique(rf_stim_table.stimulus_condition_id))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What about the drifting grating parameters that don't vary, such as size (in degrees), spatial frequency (in cycles/degree), temporal frequency (in Hz), and contrast?" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Spatial frequency: 0.08\n", "Temporal frequency: 4.0\n", "Size: [20.0, 20.0]\n", "Contrast: 0.8\n" ] } ], "source": [ "print('Spatial frequency: ' + str(rf_stim_table.spatial_frequency.unique()[0]))\n", "print('Temporal frequency: ' + str(rf_stim_table.temporal_frequency.unique()[0]))\n", "print('Size: ' + str(rf_stim_table['size'].unique()[0]))\n", "print('Contrast: ' + str(rf_stim_table['contrast'].unique()[0]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This stimulus is designed to drive neurons reliably across a wide variety of visual areas. Because of the large size (20 degree diameter), it lacks spatial precision. It also cannot be used to map on/off subfields on neurons. However, this is a reasonable compromise to allow us to map receptive fields with high reliability across all visual areas we're recording from.\n", "\n", "Now that we have a better understanding of the stimulus, let's look at receptive fields for some neurons." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['color', 'contrast', 'frame', 'orientation', 'phase', 'size',\n", " 'spatial_frequency', 'start_time', 'stimulus_block', 'stimulus_name',\n", " 'stop_time', 'temporal_frequency', 'x_position', 'y_position',\n", " 'duration', 'stimulus_condition_id'],\n", " dtype='object')" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rf_stim_table.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting receptive fields" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to visualize receptive fields, we're going to use a function in the `ReceptiveFieldMapping` class, one of the stimulus-specific analysis classes in the AllenSDK. Let's import it and create a `rf_mapping` object based on the `session` we loaded earlier:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "from allensdk.brain_observatory.ecephys.stimulus_analysis.receptive_field_mapping import ReceptiveFieldMapping\n", "\n", "rf_mapping = ReceptiveFieldMapping(session)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `rf_mapping` object contains a variety of methods related to receptive field mapping. Its `stim_table` property holds the same DataFrame we created earlier." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | contrast | \n", "orientation | \n", "phase | \n", "size | \n", "spatial_frequency | \n", "start_time | \n", "stimulus_block | \n", "stimulus_name | \n", "stop_time | \n", "temporal_frequency | \n", "x_position | \n", "y_position | \n", "duration | \n", "stimulus_condition_id | \n", "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
stimulus_presentation_id | \n", "\n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " |
1 | \n", "0.8 | \n", "0 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "84.804349 | \n", "0 | \n", "gabors | \n", "85.037870 | \n", "4 | \n", "-10 | \n", "-40 | \n", "0.233521 | \n", "1 | \n", "
2 | \n", "0.8 | \n", "0 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "85.037870 | \n", "0 | \n", "gabors | \n", "85.288070 | \n", "4 | \n", "30 | \n", "40 | \n", "0.250201 | \n", "2 | \n", "
3 | \n", "0.8 | \n", "0 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "85.288070 | \n", "0 | \n", "gabors | \n", "85.538271 | \n", "4 | \n", "0 | \n", "0 | \n", "0.250201 | \n", "3 | \n", "
4 | \n", "0.8 | \n", "45 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "85.538271 | \n", "0 | \n", "gabors | \n", "85.788472 | \n", "4 | \n", "-20 | \n", "20 | \n", "0.250201 | \n", "4 | \n", "
5 | \n", "0.8 | \n", "0 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "85.788472 | \n", "0 | \n", "gabors | \n", "86.038686 | \n", "4 | \n", "-40 | \n", "40 | \n", "0.250214 | \n", "5 | \n", "
... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
3641 | \n", "0.8 | \n", "90 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "995.598539 | \n", "0 | \n", "gabors | \n", "995.848745 | \n", "4 | \n", "30 | \n", "-10 | \n", "0.250206 | \n", "175 | \n", "
3642 | \n", "0.8 | \n", "45 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "995.848745 | \n", "0 | \n", "gabors | \n", "996.098950 | \n", "4 | \n", "30 | \n", "10 | \n", "0.250206 | \n", "228 | \n", "
3643 | \n", "0.8 | \n", "0 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "996.098950 | \n", "0 | \n", "gabors | \n", "996.349156 | \n", "4 | \n", "30 | \n", "-20 | \n", "0.250206 | \n", "156 | \n", "
3644 | \n", "0.8 | \n", "90 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "996.349156 | \n", "0 | \n", "gabors | \n", "996.599362 | \n", "4 | \n", "10 | \n", "20 | \n", "0.250206 | \n", "231 | \n", "
3645 | \n", "0.8 | \n", "0 | \n", "[3644.93333333, 3644.93333333] | \n", "[20.0, 20.0] | \n", "0.08 | \n", "996.599362 | \n", "0 | \n", "gabors | \n", "996.849568 | \n", "4 | \n", "-40 | \n", "-30 | \n", "0.250207 | \n", "72 | \n", "
3645 rows × 14 columns
\n", "