diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab3c52e05..416dce683 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,24 @@
# Change Log
All notable changes to this project will be documented in this file.
+## [1.6.0] = 2020-03-23
+
+### Added
+- tutorial for optotagging for ecephys notebook
+- get\_receptive\_field() method in ecephys receptive field mapping
+
+### Changed
+- remove redundant sham\_change column in behavior sessions.trials table
+- versions for NWB output for ecephys and ophys behavior.
+- monitor delay is now calculated for BehaviorOphysLimsApi rather than defaulting to 0.0351
+
+### Bug Fixes
+- Fixed a bug where auto-rewarded trials were not properly attributed in the rewards property of a visual behavior
+- return None rather than raise exception if no container id was returned from lims id for given ophys id
+- Project caches no longer accept arbitrary keywords
+- matplotloib.pyplot.hist parameter normed no longer supported
+
+
## [1.5.0] = 2020-02-10
### Added
diff --git a/allensdk/__init__.py b/allensdk/__init__.py
index a36b0c4c4..ad41116d0 100644
--- a/allensdk/__init__.py
+++ b/allensdk/__init__.py
@@ -36,7 +36,7 @@
import logging
-__version__ = '1.5.1'
+__version__ = '1.6.0'
try:
diff --git a/allensdk/brain_observatory/behavior/behavior_project_cache.py b/allensdk/brain_observatory/behavior/behavior_project_cache.py
index c24a3ed71..15f305759 100644
--- a/allensdk/brain_observatory/behavior/behavior_project_cache.py
+++ b/allensdk/brain_observatory/behavior/behavior_project_cache.py
@@ -2,7 +2,8 @@
import os.path
import csv
from functools import partial
-from typing import Type, Callable, Optional, List, Any, Dict
+from typing import Type, Optional, List, Any, Dict, Union
+from pathlib import Path
import pandas as pd
import time
import logging
@@ -15,8 +16,7 @@
import BehaviorProjectBase
from allensdk.api.caching_utilities import one_file_call_caching, call_caching
from allensdk.core.exceptions import MissingDataError
-from allensdk.core.auth_config import LIMS_DB_CREDENTIAL_MAP
-from allensdk.core.authentication import credential_injector, DbCredentials
+from allensdk.core.authentication import DbCredentials
BehaviorProjectApi = Type[BehaviorProjectBase]
@@ -64,11 +64,17 @@ def __init__(
self,
fetch_api: Optional[BehaviorProjectApi] = None,
fetch_tries: int = 2,
- **kwargs):
+ manifest: Optional[Union[str, Path]] = None,
+ version: Optional[str] = None,
+ cache: bool = True):
""" Entrypoint for accessing visual behavior data. Supports
access to summaries of session data and provides tools for
downloading detailed session data (such as dff traces).
+ Likely you will want to use a class constructor, such as `from_lims`,
+ to initialize a BehaviorProjectCache, rather than calling this
+ directly.
+
--- NOTE ---
Because NWB files are not currently supported for this project (as of
11/2019), this cache will not actually save any files of session data
@@ -87,38 +93,88 @@ def __init__(
Used to pull data from remote sources, after which it is locally
cached. Any object inheriting from BehaviorProjectBase is
suitable. Current options are:
- EcephysProjectLimsApi :: Fetches bleeding-edge data from the
+ BehaviorProjectLimsApi :: Fetches bleeding-edge data from the
Allen Institute"s internal database. Only works if you are
on our internal network.
fetch_tries :
Maximum number of times to attempt a download before giving up and
- raising an exception. Note that this is total tries, not retries
- **kwargs :
- manifest : str or Path
- full path at which manifest json will be stored
- version : str
- version of manifest file. If this mismatches the version
- recorded in the file at manifest, an error will be raised.
- other kwargs are passed to allensdk.api.cache.Cache
+ raising an exception. Note that this is total tries, not retries.
+ Default=2.
+ manifest : str or Path
+ full path at which manifest json will be stored. Defaults
+ to "behavior_project_manifest.json" in the local directory.
+ version : str
+ version of manifest file. If this mismatches the version
+ recorded in the file at manifest, an error will be raised.
+ Defaults to the manifest version in the class.
+ cache : bool
+ Whether to write to the cache. Default=True.
"""
- kwargs["manifest"] = kwargs.get("manifest",
- "behavior_project_manifest.json")
- kwargs["version"] = kwargs.get("version", self.MANIFEST_VERSION)
+ manifest_ = manifest or "behavior_project_manifest.json"
+ version_ = version or self.MANIFEST_VERSION
- super().__init__(**kwargs)
- self.fetch_api = fetch_api or BehaviorProjectLimsApi.default()
+ super().__init__(manifest=manifest_, version=version_, cache=cache)
+ self.fetch_api = fetch_api
self.fetch_tries = fetch_tries
self.logger = logging.getLogger(self.__class__.__name__)
@classmethod
- def from_lims(cls, lims_credentials: Optional[DbCredentials] = None,
+ def from_lims(cls, manifest: Optional[Union[str, Path]] = None,
+ version: Optional[str] = None,
+ cache: bool = True,
+ fetch_tries: int = 2,
+ lims_credentials: Optional[DbCredentials] = None,
mtrain_credentials: Optional[DbCredentials] = None,
- app_kwargs: Dict[str, Any] = None, **kwargs):
- return cls(fetch_api=BehaviorProjectLimsApi.default(
+ host: Optional[str] = None,
+ scheme: Optional[str] = None,
+ asynchronous: bool = True) -> "BehaviorProjectCache":
+ """
+ Construct a BehaviorProjectCache with a lims api. Use this method
+ to create a BehaviorProjectCache instance rather than calling
+ BehaviorProjectCache directly.
+
+ Parameters
+ ==========
+ manifest : str or Path
+ full path at which manifest json will be stored
+ version : str
+ version of manifest file. If this mismatches the version
+ recorded in the file at manifest, an error will be raised.
+ cache : bool
+ Whether to write to the cache
+ fetch_tries : int
+ Maximum number of times to attempt a download before giving up and
+ raising an exception. Note that this is total tries, not retries
+ lims_credentials : DbCredentials
+ Optional credentials to access LIMS database.
+ If not set, will look for credentials in environment variables.
+ mtrain_credentials: DbCredentials
+ Optional credentials to access mtrain database.
+ If not set, will look for credentials in environment variables.
+ host : str
+ Web host for the app_engine. Currently unused. This argument is
+ included for consistency with EcephysProjectCache.from_lims.
+ scheme : str
+ URI scheme, such as "http". Currently unused. This argument is
+ included for consistency with EcephysProjectCache.from_lims.
+ asynchronous : bool
+ Whether to fetch from web asynchronously. Currently unused.
+ Returns
+ =======
+ BehaviorProjectCache
+ BehaviorProjectCache instance with a LIMS fetch API
+ """
+ if host and scheme:
+ app_kwargs = {"host": host, "scheme": scheme,
+ "asynchronous": asynchronous}
+ else:
+ app_kwargs = None
+ fetch_api = BehaviorProjectLimsApi.default(
lims_credentials=lims_credentials,
mtrain_credentials=mtrain_credentials,
- app_kwargs=app_kwargs),
- **kwargs)
+ app_kwargs=app_kwargs)
+ return cls(fetch_api=fetch_api, manifest=manifest, version=version,
+ cache=cache, fetch_tries=fetch_tries)
def get_session_table(
self,
diff --git a/allensdk/brain_observatory/behavior/rewards_processing.py b/allensdk/brain_observatory/behavior/rewards_processing.py
index 46238c396..103ce2764 100644
--- a/allensdk/brain_observatory/behavior/rewards_processing.py
+++ b/allensdk/brain_observatory/behavior/rewards_processing.py
@@ -3,15 +3,15 @@
def get_rewards(data, stimulus_rebase_function):
- trial_df = pd.DataFrame(data["items"]["behavior"]['trial_log'])
- rewards_dict = {'volume': [], 'timestamps': [], 'autorewarded': []}
+ trial_df = pd.DataFrame(data["items"]["behavior"]["trial_log"])
+ rewards_dict = {"volume": [], "timestamps": [], "autorewarded": []}
for idx, trial in trial_df.iterrows():
rewards = trial["rewards"] # as i write this there can only ever be one reward per trial
if rewards:
rewards_dict["volume"].append(rewards[0][0])
rewards_dict["timestamps"].append(stimulus_rebase_function(rewards[0][1]))
- rewards_dict["autorewarded"].append('auto_rewarded' in trial['trial_params'])
+ rewards_dict["autorewarded"].append(trial["trial_params"]["auto_reward"])
- df = pd.DataFrame(rewards_dict).set_index('timestamps', drop=True)
+ df = pd.DataFrame(rewards_dict).set_index("timestamps", drop=True)
return df
diff --git a/allensdk/brain_observatory/behavior/trials_processing.py b/allensdk/brain_observatory/behavior/trials_processing.py
index 11769fd36..f4327f494 100644
--- a/allensdk/brain_observatory/behavior/trials_processing.py
+++ b/allensdk/brain_observatory/behavior/trials_processing.py
@@ -345,6 +345,7 @@ def get_trials(data, licks_df, rewards_df, stimulus_presentations_df, rebase):
trials = pd.DataFrame(all_trial_data).set_index('trial')
trials.index = trials.index.rename('trials_id')
+ del trials["sham_change"]
return trials
diff --git a/allensdk/brain_observatory/ecephys/copy_utility/__main__.py b/allensdk/brain_observatory/ecephys/copy_utility/__main__.py
index 2ec28d362..6f07fbaf1 100644
--- a/allensdk/brain_observatory/ecephys/copy_utility/__main__.py
+++ b/allensdk/brain_observatory/ecephys/copy_utility/__main__.py
@@ -12,11 +12,17 @@
from allensdk.brain_observatory.argschema_utilities import write_or_print_outputs
-def hash_file(path, hasher_cls):
- with open(path, 'rb') as file_obj:
- hasher = hasher_cls()
- hasher.update(file_obj.read())
- return hasher.digest()
+def hash_file(path, hasher_cls, blocks_per_chunk=128):
+ """
+
+ """
+ hasher = hasher_cls()
+ with open(path, 'rb') as f:
+ # TODO: Update to new assignment syntax if drop < python 3.8 support
+ for chunk in iter(
+ lambda: f.read(hasher.block_size*blocks_per_chunk), b""):
+ hasher.update(chunk)
+ return hasher.digest()
def walk_fs_tree(root, fn):
diff --git a/allensdk/brain_observatory/ecephys/ecephys_project_cache.py b/allensdk/brain_observatory/ecephys/ecephys_project_cache.py
index e615ced9f..e8d7d23b5 100644
--- a/allensdk/brain_observatory/ecephys/ecephys_project_cache.py
+++ b/allensdk/brain_observatory/ecephys/ecephys_project_cache.py
@@ -1,6 +1,6 @@
from functools import partial
from pathlib import Path
-from typing import Any, List, Optional
+from typing import Any, List, Optional, Union, Callable
import ast
import pandas as pd
@@ -9,14 +9,11 @@
import pynwb
from allensdk.api.cache import Cache
-
+from allensdk.core.authentication import DbCredentials
from allensdk.brain_observatory.ecephys.ecephys_project_api import (
- EcephysProjectApi, EcephysProjectLimsApi, EcephysProjectWarehouseApi,
+ EcephysProjectApi, EcephysProjectLimsApi, EcephysProjectWarehouseApi,
EcephysProjectFixedApi
)
-from allensdk.brain_observatory.ecephys.ecephys_project_api.rma_engine import (
- AsyncRmaEngine,
-)
from allensdk.brain_observatory.ecephys.ecephys_project_api.http_engine import (
write_bytes_from_coroutine, write_from_stream
)
@@ -74,11 +71,13 @@ class EcephysProjectCache(Cache):
)
def __init__(
- self,
- fetch_api: EcephysProjectApi = EcephysProjectWarehouseApi.default(),
- fetch_tries: int = 2,
- stream_writer = write_from_stream,
- **kwargs):
+ self,
+ fetch_api: EcephysProjectApi = EcephysProjectWarehouseApi.default(),
+ fetch_tries: int = 2,
+ stream_writer: Callable = write_from_stream,
+ manifest: Optional[Union[str, Path]] = None,
+ version: Optional[str] = None,
+ cache: bool = True):
""" Entrypoint for accessing ecephys (neuropixels) data. Supports
access to cross-session data (like stimulus templates) and high-level
summaries of sessionwise data and provides tools for downloading detailed
@@ -88,33 +87,34 @@ def __init__(
==========
fetch_api :
Used to pull data from remote sources, after which it is locally
- cached. Any object exposing the EcephysProjectApi interface is
+ cached. Any object exposing the EcephysProjectApi interface is
suitable. Standard options are:
- EcephysProjectWarehouseApi :: The default. Fetches publically
+ EcephysProjectWarehouseApi :: The default. Fetches publically
available Allen Institute data
- EcephysProjectFixedApi :: Refuses to fetch any data - only the
- existing local cache is accessible. Useful if you want to
- settle on a fixed dataset for analysis.
- EcephysProjectLimsApi :: Fetches bleeding-edge data from the
- Allen Institute's internal database. Only works if you are
+ EcephysProjectFixedApi :: Refuses to fetch any data - only the
+ existing local cache is accessible. Useful if you want to
+ settle on a fixed dataset for analysis
+ EcephysProjectLimsApi :: Fetches bleeding-edge data from the
+ Allen Institute's internal database. Only works if you are
on our internal network.
- fetch_tries :
- Maximum number of times to attempt a download before giving up and
+ fetch_tries : int
+ Maximum number of times to attempt a download before giving up and
raising an exception. Note that this is total tries, not retries
- **kwargs :
- manifest : str or Path
- full path at which manifest json will be stored
- version : str
- version of manifest file. If this mismatches the version
- recorded in the file at manifest, an error will be raised.
- other kwargs are passed to allensdk.api.cache.Cache
-
+ manifest : str or Path
+ full path at which manifest json will be stored (default =
+ "ecephys_project_manifest.json" in the local directory.)
+ version : str
+ version of manifest file. If this mismatches the version
+ recorded in the file at manifest, an error will be raised.
+ cache: bool
+ Whether to write to the cache (default=True)
"""
+ manifest_ = manifest or "ecephys_project_manifest.json"
+ version_ = version or self.MANIFEST_VERSION
- kwargs['manifest'] = kwargs.get('manifest', 'ecephys_project_manifest.json')
- kwargs['version'] = kwargs.get('version', self.MANIFEST_VERSION)
-
- super(EcephysProjectCache, self).__init__(**kwargs)
+ super(EcephysProjectCache, self).__init__(manifest=manifest_,
+ version=version_,
+ cache=cache)
self.fetch_api = fetch_api
self.fetch_tries = fetch_tries
self.stream_writer = stream_writer
@@ -516,7 +516,7 @@ def _from_http_source_default(cls, fetch_api_cls, fetch_api_kwargs, **kwargs):
"asynchronous": True
} if fetch_api_kwargs is None else fetch_api_kwargs
- if "stream_writer" not in kwargs:
+ if kwargs.get("stream_writer") is None:
if fetch_api_kwargs.get("asynchronous", True):
kwargs["stream_writer"] = write_bytes_from_coroutine
else:
@@ -528,21 +528,124 @@ def _from_http_source_default(cls, fetch_api_cls, fetch_api_kwargs, **kwargs):
)
@classmethod
- def from_lims(cls, lims_kwargs=None, **kwargs):
+ def from_lims(cls, lims_credentials: Optional[DbCredentials] = None,
+ scheme: Optional[str] = None,
+ host: Optional[str] = None,
+ asynchronous: bool = True,
+ manifest: Optional[str] = None,
+ version: Optional[str] = None,
+ cache: bool = True,
+ fetch_tries: int = 2):
+ """
+ Create an instance of EcephysProjectCache with an
+ EcephysProjectLimsApi. Retrieves bleeding-edge data stored
+ locally on Allen Institute servers. Only available for use
+ on-site at the Allen Institute or through a vpn. Requires Allen
+ Institute database credentials.
+
+ Parameters
+ ==========
+ lims_credentials : DbCredentials
+ Credentials to access LIMS database. If not provided will
+ attempt to find credentials in environment variables.
+ scheme : str
+ URI scheme, such as "http". Defaults to
+ EcephysProjectLimsApi.default value if unspecified.
+ Will not be used unless `host` is also specified.
+ host : str
+ Web host. Defaults to EcephysProjectLimsApi.default
+ value if unspecified. Will not be used unless `scheme` is
+ also specified.
+ asynchronous : bool
+ Whether to fetch file asynchronously. Defaults to True.
+ manifest : str or Path
+ full path at which manifest json will be stored
+ version : str
+ version of manifest file. If this mismatches the version
+ recorded in the file at manifest, an error will be raised.
+ cache: bool
+ Whether to write to the cache (default=True)
+ fetch_tries : int
+ Maximum number of times to attempt a download before giving up and
+ raising an exception. Note that this is total tries, not retries
+ """
+ if scheme and host:
+ app_kwargs = {"scheme": scheme, "host": host}
+ else:
+ app_kwargs = None
return cls._from_http_source_default(
- EcephysProjectLimsApi, lims_kwargs, **kwargs
- )
+ EcephysProjectLimsApi,
+ {"lims_credentials": lims_credentials,
+ "app_kwargs": app_kwargs,
+ "asynchronous": asynchronous,
+ }, # expects dictionary of kwargs
+ manifest=manifest, version=version, cache=cache,
+ fetch_tries=fetch_tries)
@classmethod
- def from_warehouse(cls, warehouse_kwargs=None, **kwargs):
+ def from_warehouse(cls,
+ scheme: Optional[str] = None,
+ host: Optional[str] = None,
+ asynchronous: bool = True,
+ manifest: Optional[Union[str, Path]] = None,
+ version: Optional[str] = None,
+ cache: bool = True,
+ fetch_tries: int = 2):
+ """
+ Create an instance of EcephysProjectCache with an
+ EcephysProjectWarehouseApi. Retrieves released data stored in
+ the warehouse.
+
+ Parameters
+ ==========
+ scheme : str
+ URI scheme, such as "http". Defaults to
+ EcephysProjectWarehouseAPI.default value if unspecified.
+ Will not be used unless `host` is also specified.
+ host : str
+ Web host. Defaults to EcephysProjectWarehouseApi.default
+ value if unspecified. Will not be used unless `scheme` is also
+ specified.
+ asynchronous : bool
+ Whether to fetch file asynchronously. Defaults to True.
+ manifest : str or Path
+ full path at which manifest json will be stored
+ version : str
+ version of manifest file. If this mismatches the version
+ recorded in the file at manifest, an error will be raised.
+ cache: bool
+ Whether to write to the cache (default=True)
+ fetch_tries : int
+ Maximum number of times to attempt a download before giving up and
+ raising an exception. Note that this is total tries, not retries
+ """
+ if scheme and host:
+ app_kwargs = {"scheme": scheme, "host": host,
+ "asynchronous": asynchronous}
+ else:
+ app_kwargs = None
return cls._from_http_source_default(
- EcephysProjectWarehouseApi, warehouse_kwargs, **kwargs
+ EcephysProjectWarehouseApi, app_kwargs, manifest=manifest,
+ version=version, cache=cache, fetch_tries=fetch_tries
)
-
@classmethod
- def fixed(cls, **kwargs):
- return cls(fetch_api=EcephysProjectFixedApi(), **kwargs)
+ def fixed(cls, manifest=None, version=None):
+ """
+ Creates a EcephysProjectCache that refuses to fetch any data
+ - only the existing local cache is accessible. Useful if you
+ want to settle on a fixed dataset for analysis.
+
+ Parameters
+ ==========
+ manifest : str or Path
+ full path to existing manifest json
+ version : str
+ version of manifest file. If this mismatches the version
+ recorded in the file at manifest, an error will be raised.
+ """
+ return cls(fetch_api=EcephysProjectFixedApi(), manifest=manifest,
+ version=version)
def count_owned(this, other, foreign_key, count_key, inplace=False):
diff --git a/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_extension.yaml b/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_extension.yaml
index 3f0c12718..ae7c47caa 100644
--- a/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_extension.yaml
+++ b/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_extension.yaml
@@ -3,10 +3,6 @@ groups:
neurodata_type_inc: ElectrodeGroup
doc: A group consisting of the channels on a single neuropixels probe.
attributes:
- - name: help
- dtype: text
- value: A physical grouping of channels
- doc: Value is 'Metadata about a physical grouping of channels'
- name: description
dtype: text
doc: description of this electrode group
diff --git a/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_namespace.yaml b/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_namespace.yaml
index 658ea895b..142bad888 100644
--- a/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_namespace.yaml
+++ b/allensdk/brain_observatory/ecephys/nwb/AIBS_ecephys_namespace.yaml
@@ -1,5 +1,6 @@
namespaces:
- doc: ""
+ version: 0.2.0
name: AIBS_ecephys
schema:
- namespace: core
diff --git a/allensdk/brain_observatory/ecephys/stimulus_analysis/receptive_field_mapping.py b/allensdk/brain_observatory/ecephys/stimulus_analysis/receptive_field_mapping.py
index c7cd4ae3b..3d849c1ad 100644
--- a/allensdk/brain_observatory/ecephys/stimulus_analysis/receptive_field_mapping.py
+++ b/allensdk/brain_observatory/ecephys/stimulus_analysis/receptive_field_mapping.py
@@ -184,6 +184,12 @@ def _get_stim_table_stats(self):
self._pos_x = np.sort(self.stimulus_conditions.loc[self.stimulus_conditions[self._col_pos_x]
!= 'null'][self._col_pos_x].unique())
+ def get_receptive_field(self, unit_id):
+ """ Alias for _get_rf
+ """
+
+ return self._get_rf(unit_id)
+
def _get_rf(self, unit_id):
""" Extract the receptive field for one unit
diff --git a/allensdk/brain_observatory/ecephys/write_nwb/__main__.py b/allensdk/brain_observatory/ecephys/write_nwb/__main__.py
index ba9b21b47..3473fa504 100644
--- a/allensdk/brain_observatory/ecephys/write_nwb/__main__.py
+++ b/allensdk/brain_observatory/ecephys/write_nwb/__main__.py
@@ -625,7 +625,7 @@ def write_probe_lfp_file(session_start_time, log_level, probe):
with pynwb.NWBHDF5IO(probe['lfp']['output_path'], 'w') as lfp_writer:
logging.info(f"writing probe lfp file to {probe['lfp']['output_path']}")
- lfp_writer.write(nwbfile)
+ lfp_writer.write(nwbfile, cache_spec=True)
return {"id": probe["id"], "nwb_path": probe["lfp"]["output_path"]}
@@ -851,10 +851,9 @@ def write_ecephys_nwb(
eye_gaze_data=eye_gaze_data)
Manifest.safe_make_parent_dirs(output_path)
- io = pynwb.NWBHDF5IO(output_path, mode='w')
- logging.info(f"writing session nwb file to {output_path}")
- io.write(nwbfile)
- io.close()
+ with pynwb.NWBHDF5IO(output_path, mode='w') as io:
+ logging.info(f"writing session nwb file to {output_path}")
+ io.write(nwbfile, cache_spec=True)
probes_with_lfp = [p for p in probes if p["lfp"] is not None]
probe_outputs = write_probewise_lfp_files(probes_with_lfp, session_start_time, pool_size=pool_size)
diff --git a/allensdk/brain_observatory/nwb/AIBS_ophys_behavior_namespace.yaml b/allensdk/brain_observatory/nwb/AIBS_ophys_behavior_namespace.yaml
index b9f3d1822..b6c5c45cd 100644
--- a/allensdk/brain_observatory/nwb/AIBS_ophys_behavior_namespace.yaml
+++ b/allensdk/brain_observatory/nwb/AIBS_ophys_behavior_namespace.yaml
@@ -2,6 +2,7 @@ namespaces:
- doc: "LabMetaData extensions: ['OphysBehaviorMetaData', 'OphysBehaviorTaskParameters']\
\ (AIBS_ophys_behavior)"
name: AIBS_ophys_behavior
+ version: 0.1.0
schema:
- namespace: core
- source: AIBS_ophys_behavior_extension.yaml
diff --git a/allensdk/brain_observatory/observatory_plots.py b/allensdk/brain_observatory/observatory_plots.py
index cefeb7f83..25ef1a834 100644
--- a/allensdk/brain_observatory/observatory_plots.py
+++ b/allensdk/brain_observatory/observatory_plots.py
@@ -260,7 +260,7 @@ def plot_condition_histogram(vals, bins, color=STIM_COLOR):
n, hbins, patches = plt.hist(vals,
bins=np.arange(len(bins)+1)+1,
align='left',
- normed=False,
+ density=False,
rwidth=.8,
color=color,
zorder=3)
@@ -287,7 +287,7 @@ def plot_selectivity_cumulative_histogram(sis,
# orientation selectivity cumulative histogram
if len(sis) > 0:
- n, bins, patches = plt.hist(sis, normed=True, bins=bins,
+ n, bins, patches = plt.hist(sis, density=True, bins=bins,
cumulative=True, histtype='stepfilled',
color=color)
plt.xlim(si_range)
diff --git a/allensdk/core/brain_observatory_nwb_data_set.py b/allensdk/core/brain_observatory_nwb_data_set.py
index bf8a1227f..69d437c4a 100755
--- a/allensdk/core/brain_observatory_nwb_data_set.py
+++ b/allensdk/core/brain_observatory_nwb_data_set.py
@@ -218,7 +218,7 @@ def get_stimulus_epoch_table(self):
'duration':duration_signature_list,
'interval':interval_signature_list})
- # Gaps are ininformative; remove them:
+ # Gaps are uninformative; remove them:
interval_df = interval_df[interval_df.stimulus != 'gap']
interval_df['start'] = [x[0] for x in interval_df['interval'].values]
interval_df['end'] = [x[1] for x in interval_df['interval'].values]
diff --git a/allensdk/internal/api/behavior_data_lims_api.py b/allensdk/internal/api/behavior_data_lims_api.py
index 3c544632f..a2d7087b3 100644
--- a/allensdk/internal/api/behavior_data_lims_api.py
+++ b/allensdk/internal/api/behavior_data_lims_api.py
@@ -93,6 +93,8 @@ def _get_ids(self) -> Dict[str, Optional[Union[int, List[int]]]]:
WHERE ophys_session_id = {ids_dict["ophys_session_id"]};
"""
oed = self.lims_db.fetchall(oed_query)
+ if len(oed) == 0:
+ oed = None
container_query = f"""
SELECT DISTINCT
@@ -102,7 +104,10 @@ def _get_ids(self) -> Dict[str, Optional[Union[int, List[int]]]]:
WHERE
ophys_experiment_id IN ({",".join(set(map(str, oed)))});
"""
- container_id = self.lims_db.fetchone(container_query, strict=True)
+ try:
+ container_id = self.lims_db.fetchone(container_query, strict=True)
+ except OneResultExpectedError:
+ container_id = None
ids_dict.update({"ophys_experiment_ids": oed,
"ophys_container_id": container_id})
diff --git a/allensdk/internal/api/behavior_ophys_api.py b/allensdk/internal/api/behavior_ophys_api.py
index 53bbd5fbd..268073bc9 100644
--- a/allensdk/internal/api/behavior_ophys_api.py
+++ b/allensdk/internal/api/behavior_ophys_api.py
@@ -10,6 +10,7 @@
from allensdk.internal.api.ophys_lims_api import OphysLimsApi
from allensdk.brain_observatory.behavior.sync import (
get_sync_data, get_stimulus_rebase_function, frame_time_offset)
+from allensdk.internal.brain_observatory.time_sync import OphysTimeAligner
from allensdk.brain_observatory.behavior.stimulus_processing import get_stimulus_presentations, get_stimulus_templates, get_stimulus_metadata
from allensdk.brain_observatory.behavior.metadata_processing import get_task_parameters
from allensdk.brain_observatory.behavior.running_processing import get_running_df
@@ -38,8 +39,10 @@ def get_sync_data(self):
@memoize
def get_stimulus_timestamps(self):
- monitor_delay = .0351
- return self.get_sync_data()['stimulus_times_no_delay'] + monitor_delay
+ sync_path = self.get_sync_file()
+ timestamps, _, _ = (OphysTimeAligner(sync_file=sync_path)
+ .corrected_stim_timestamps)
+ return timestamps
@memoize
def get_ophys_timestamps(self):
diff --git a/allensdk/internal/brain_observatory/time_sync.py b/allensdk/internal/brain_observatory/time_sync.py
index d7aa449b6..3133fb3e8 100644
--- a/allensdk/internal/brain_observatory/time_sync.py
+++ b/allensdk/internal/brain_observatory/time_sync.py
@@ -15,7 +15,7 @@
REG_PHOTODIODE_MAX = 2.1 # seconds
PHOTODIODE_ANOMALY_THRESHOLD = 0.5 # seconds
LONG_STIM_THRESHOLD = 0.2 # seconds
-ASSUMED_DELAY = 0.0351 # seconds
+ASSUMED_DELAY = 0.0215 # seconds
MAX_MONITOR_DELAY = 0.07 # seconds
VERSION_1_KEYS = {
diff --git a/allensdk/test/brain_observatory/behavior/test_rewards_processing.py b/allensdk/test/brain_observatory/behavior/test_rewards_processing.py
index fcb7cbd68..1d7bac4ee 100644
--- a/allensdk/test/brain_observatory/behavior/test_rewards_processing.py
+++ b/allensdk/test/brain_observatory/behavior/test_rewards_processing.py
@@ -9,21 +9,26 @@ def test_get_rewards():
"behavior": {
"trial_log": [
{
- 'rewards': [(0.007, 1085.965144219165, 64775)],
+ 'rewards': [(0.007, 1085.96, 64775)],
'trial_params': {
'catch': False, 'auto_reward': False,
'change_time': 5}},
+ {
+ 'rewards': [(0.007, 1090.01, 64780)],
+ 'trial_params': {
+ 'catch': False, 'auto_reward': True,
+ 'change_time': 6}},
{
'rewards': [],
'trial_params': {
'catch': False, 'auto_reward': False,
- 'change_time': 4}
- }
+ 'change_time': 4},
+ },
]
}}}
expected = pd.DataFrame(
- {"volume": [0.007],
- "timestamps": [1086.965144219165],
- "autorewarded": False}).set_index("timestamps", drop=True)
+ {"volume": [0.007, 0.007],
+ "timestamps": [1086.96, 1091.01],
+ "autorewarded": [False, True]}).set_index("timestamps", drop=True)
pd.testing.assert_frame_equal(expected, get_rewards(data, lambda x: x+1.0))
diff --git a/allensdk/test/brain_observatory/ecephys/test_ecephys_project_cache.py b/allensdk/test/brain_observatory/ecephys/test_ecephys_project_cache.py
index 85c56992d..6ff10a082 100644
--- a/allensdk/test/brain_observatory/ecephys/test_ecephys_project_cache.py
+++ b/allensdk/test/brain_observatory/ecephys/test_ecephys_project_cache.py
@@ -373,7 +373,7 @@ def test_from_lims_default(tmpdir_factory):
tmpdir = str(tmpdir_factory.mktemp("test_from_lims_default"))
cache = epc.EcephysProjectCache.from_lims(
- manifest_path=os.path.join(tmpdir, "manifest.json")
+ manifest=os.path.join(tmpdir, "manifest.json")
)
assert isinstance(cache.fetch_api.app_engine, AsyncHttpEngine)
assert cache.stream_writer is epc.write_bytes_from_coroutine
\ No newline at end of file
diff --git a/doc_template/examples_root/examples/internal/Lims Behavior Project Cache.ipynb b/doc_template/examples_root/examples/internal/Lims Behavior Project Cache.ipynb
index b6228fd06..ef9348104 100644
--- a/doc_template/examples_root/examples/internal/Lims Behavior Project Cache.ipynb
+++ b/doc_template/examples_root/examples/internal/Lims Behavior Project Cache.ipynb
@@ -892,7 +892,7 @@
],
"source": [
"# But it will work if we use one that already exists\n",
- "cache.get_session_data(978244684, fixed=True)"
+ "cache.get_session_data(latest.name, fixed=True)"
]
},
{
diff --git a/doc_template/examples_root/examples/nb/ecephys_optotagging.ipynb b/doc_template/examples_root/examples/nb/ecephys_optotagging.ipynb
new file mode 100644
index 000000000..8455d12cc
--- /dev/null
+++ b/doc_template/examples_root/examples/nb/ecephys_optotagging.ipynb
@@ -0,0 +1,1312 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Optotagging Analysis\n",
+ "\n",
+ "## Tutorial overview\n",
+ "\n",
+ "This Jupyter notebook will demonstrate how to analyze responses to optotagging stimuli in Neuropixels Brain Observatory datasets. Optotagging makes it possible to link _in vivo_ spike trains to genetically defined cell classes. By expressing a light-gated ion channel (in this case, ChR2) in a Cre-dependent manner, we can activate Cre+ neurons with light pulses delivered to the cortical surface. Units that fire action potentials in response to these light pulses are likely to express the gene of interest.\n",
+ "\n",
+ "Of course, there are some shortcomings to this approach, most notably that the presence of light artifacts can create the appearance of false positives, and that false negatives (cells that are Cre+ but do not respond to light) are nearly impossible to avoid. We will explain how to deal with these caveats in order to incorporate the available cell type information into your analyses.\n",
+ "\n",
+ "This tutorial will cover the following topics:\n",
+ "\n",
+ "* Finding datasets of interest\n",
+ "* Types of optotagging stimuli\n",
+ "* Aligning spikes to light pulses\n",
+ "* Identifying Cre+ units\n",
+ "* Differences across genotypes\n",
+ "\n",
+ "This tutorial assumes you've already created a data cache, or are working with NWB 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 analyzing responses to visual stimuli 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)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First, let's deal with the necessary imports:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import xarray as xr\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": [
+ "Next, we'll create an `EcephysProjectCache` object that points to a new or existing manifest file.\n",
+ "\n",
+ "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": [
+ "data_directory = '/mnt/nvme0/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": [
+ "## Finding datasets of interest"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `sessions` table contains information about all the experiments available in the `EcephysProjectCache`. The `full_genotype` column contains information about the genotype of the mouse used in each experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "wt/wt 30\n",
+ "Sst-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt 12\n",
+ "Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt 8\n",
+ "Vip-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt 8\n",
+ "Name: full_genotype, dtype: int64"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sessions = cache.get_session_table()\n",
+ "\n",
+ "sessions.full_genotype.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "About half the mice are wild type (`wt/wt`), while the other half are a cross between a Cre line and the Ai32 reporter line. The Cre mice express ChR2 in one of three interneuron subtypes: Parvalbumin-positive neurons (`Pvalb`), Somatostatin-positive neurons (`Sst`), and Vasoactive Intestinal Polypeptide neurons (`Vip`). We know that these genes are expressed in largely non-overlapping populations of inhibitory cells, and that, taken together, they [cover nearly the entire range of cortical GABAergic neurons](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3556905/#!po=8.92857), with the caveat that VIP+ cells are a subset of a larger group of 5HT3aR-expressing cells.\n",
+ "\n",
+ "To find experiments performed on a specific genotype, we can filter the sessions table on the `full_genotype` column:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
published_at
\n",
+ "
specimen_id
\n",
+ "
session_type
\n",
+ "
age_in_days
\n",
+ "
sex
\n",
+ "
full_genotype
\n",
+ "
unit_count
\n",
+ "
channel_count
\n",
+ "
probe_count
\n",
+ "
ecephys_structure_acronyms
\n",
+ "
\n",
+ "
\n",
+ "
id
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
721123822
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
707296982
\n",
+ "
brain_observatory_1.1
\n",
+ "
125.0
\n",
+ "
M
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
444
\n",
+ "
2229
\n",
+ "
6
\n",
+ "
[MB, SCig, PPT, NOT, DG, CA1, VISam, nan, LP, ...
\n",
+ "
\n",
+ "
\n",
+ "
746083955
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
726170935
\n",
+ "
brain_observatory_1.1
\n",
+ "
98.0
\n",
+ "
F
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
582
\n",
+ "
2216
\n",
+ "
6
\n",
+ "
[VPM, TH, LGd, CA3, CA2, CA1, VISal, nan, grey...
\n",
+ "
\n",
+ "
\n",
+ "
760345702
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
739783171
\n",
+ "
brain_observatory_1.1
\n",
+ "
103.0
\n",
+ "
M
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
501
\n",
+ "
1862
\n",
+ "
5
\n",
+ "
[MB, TH, PP, PIL, DG, CA3, CA1, VISal, nan, gr...
\n",
+ "
\n",
+ "
\n",
+ "
773418906
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
757329624
\n",
+ "
brain_observatory_1.1
\n",
+ "
124.0
\n",
+ "
F
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
546
\n",
+ "
2232
\n",
+ "
6
\n",
+ "
[PPT, NOT, SUB, ProS, CA1, VISam, nan, APN, DG...
\n",
+ "
\n",
+ "
\n",
+ "
797828357
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
776061251
\n",
+ "
brain_observatory_1.1
\n",
+ "
107.0
\n",
+ "
M
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
611
\n",
+ "
2232
\n",
+ "
6
\n",
+ "
[PPT, MB, APN, NOT, HPF, ProS, CA1, VISam, nan...
\n",
+ "
\n",
+ "
\n",
+ "
829720705
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
811322619
\n",
+ "
functional_connectivity
\n",
+ "
112.0
\n",
+ "
M
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
529
\n",
+ "
1841
\n",
+ "
5
\n",
+ "
[SCig, SCop, SCsg, SCzo, POST, VISp, nan, CA1,...
\n",
+ "
\n",
+ "
\n",
+ "
839557629
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
821469666
\n",
+ "
functional_connectivity
\n",
+ "
115.0
\n",
+ "
M
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
450
\n",
+ "
1853
\n",
+ "
5
\n",
+ "
[APN, NOT, MB, DG, CA1, VISam, nan, VISpm, LGd...
\n",
+ "
\n",
+ "
\n",
+ "
840012044
\n",
+ "
2019-10-03T00:00:00Z
\n",
+ "
820866121
\n",
+ "
functional_connectivity
\n",
+ "
116.0
\n",
+ "
M
\n",
+ "
Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt
\n",
+ "
758
\n",
+ "
2298
\n",
+ "
6
\n",
+ "
[APN, DG, CA1, VISam, nan, LP, VISpm, VISp, LG...
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " published_at specimen_id session_type \\\n",
+ "id \n",
+ "721123822 2019-10-03T00:00:00Z 707296982 brain_observatory_1.1 \n",
+ "746083955 2019-10-03T00:00:00Z 726170935 brain_observatory_1.1 \n",
+ "760345702 2019-10-03T00:00:00Z 739783171 brain_observatory_1.1 \n",
+ "773418906 2019-10-03T00:00:00Z 757329624 brain_observatory_1.1 \n",
+ "797828357 2019-10-03T00:00:00Z 776061251 brain_observatory_1.1 \n",
+ "829720705 2019-10-03T00:00:00Z 811322619 functional_connectivity \n",
+ "839557629 2019-10-03T00:00:00Z 821469666 functional_connectivity \n",
+ "840012044 2019-10-03T00:00:00Z 820866121 functional_connectivity \n",
+ "\n",
+ " age_in_days sex full_genotype \\\n",
+ "id \n",
+ "721123822 125.0 M Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "746083955 98.0 F Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "760345702 103.0 M Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "773418906 124.0 F Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "797828357 107.0 M Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "829720705 112.0 M Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "839557629 115.0 M Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "840012044 116.0 M Pvalb-IRES-Cre/wt;Ai32(RCL-ChR2(H134R)_EYFP)/wt \n",
+ "\n",
+ " unit_count channel_count probe_count \\\n",
+ "id \n",
+ "721123822 444 2229 6 \n",
+ "746083955 582 2216 6 \n",
+ "760345702 501 1862 5 \n",
+ "773418906 546 2232 6 \n",
+ "797828357 611 2232 6 \n",
+ "829720705 529 1841 5 \n",
+ "839557629 450 1853 5 \n",
+ "840012044 758 2298 6 \n",
+ "\n",
+ " ecephys_structure_acronyms \n",
+ "id \n",
+ "721123822 [MB, SCig, PPT, NOT, DG, CA1, VISam, nan, LP, ... \n",
+ "746083955 [VPM, TH, LGd, CA3, CA2, CA1, VISal, nan, grey... \n",
+ "760345702 [MB, TH, PP, PIL, DG, CA3, CA1, VISal, nan, gr... \n",
+ "773418906 [PPT, NOT, SUB, ProS, CA1, VISam, nan, APN, DG... \n",
+ "797828357 [PPT, MB, APN, NOT, HPF, ProS, CA1, VISam, nan... \n",
+ "829720705 [SCig, SCop, SCsg, SCzo, POST, VISp, nan, CA1,... \n",
+ "839557629 [APN, NOT, MB, DG, CA1, VISam, nan, VISpm, LGd... \n",
+ "840012044 [APN, DG, CA1, VISam, nan, LP, VISpm, VISp, LG... "
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pvalb_sessions = sessions[sessions.full_genotype.str.match('Pvalb')]\n",
+ "\n",
+ "pvalb_sessions"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The table above contains 8 sessions, 5 of which used the `brain_observatory_1.1` visual stimulus, and 3 of which used the `functional_connectivity` stimulus. Any experiments with the same stimulus set are identical across genotypes. Importantly, the optotagging stimulus does not occur until the end of the experiment, so any changes induced by activating a specific set of interneurons will not affect the visual responses that we measure."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Types of optotagging stimuli"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's load one of the above sessions to see how to extract information about the optotagging stimuli that were delivered."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "session = cache.get_session_data(pvalb_sessions.index.values[-3])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The optotagging stimulus table is stored separately from the visual stimulus table. So instead of calling `session.stimulus_presentations`, we will use `session.optogenetic_stimulation_epochs` to load a DataFrame that contains the information about the optotagging stimuli:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
start_time
\n",
+ "
stop_time
\n",
+ "
condition
\n",
+ "
level
\n",
+ "
name
\n",
+ "
duration
\n",
+ "
\n",
+ "
\n",
+ "
id
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
9208.46044
\n",
+ "
9208.46544
\n",
+ "
a single square pulse
\n",
+ "
2.0
\n",
+ "
pulse
\n",
+ "
0.005
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
9210.64062
\n",
+ "
9210.65062
\n",
+ "
a single square pulse
\n",
+ "
1.7
\n",
+ "
pulse
\n",
+ "
0.010
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
9212.37064
\n",
+ "
9213.37064
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
1.7
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
9214.40076
\n",
+ "
9215.40076
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
1.3
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
9216.55091
\n",
+ "
9217.55091
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
2.0
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
\n",
+ "
\n",
+ "
295
\n",
+ "
9778.77516
\n",
+ "
9779.77516
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
2.0
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
296
\n",
+ "
9780.72530
\n",
+ "
9781.72530
\n",
+ "
half-period of a cosine wave
\n",
+ "
2.0
\n",
+ "
raised_cosine
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
297
\n",
+ "
9782.66528
\n",
+ "
9782.67028
\n",
+ "
a single square pulse
\n",
+ "
1.3
\n",
+ "
pulse
\n",
+ "
0.005
\n",
+ "
\n",
+ "
\n",
+ "
298
\n",
+ "
9784.81538
\n",
+ "
9784.82038
\n",
+ "
a single square pulse
\n",
+ "
1.3
\n",
+ "
pulse
\n",
+ "
0.005
\n",
+ "
\n",
+ "
\n",
+ "
299
\n",
+ "
9786.60547
\n",
+ "
9786.61547
\n",
+ "
a single square pulse
\n",
+ "
1.3
\n",
+ "
pulse
\n",
+ "
0.010
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
300 rows × 6 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " start_time stop_time condition level \\\n",
+ "id \n",
+ "0 9208.46044 9208.46544 a single square pulse 2.0 \n",
+ "1 9210.64062 9210.65062 a single square pulse 1.7 \n",
+ "2 9212.37064 9213.37064 2.5 ms pulses at 10 Hz 1.7 \n",
+ "3 9214.40076 9215.40076 2.5 ms pulses at 10 Hz 1.3 \n",
+ "4 9216.55091 9217.55091 2.5 ms pulses at 10 Hz 2.0 \n",
+ ".. ... ... ... ... \n",
+ "295 9778.77516 9779.77516 2.5 ms pulses at 10 Hz 2.0 \n",
+ "296 9780.72530 9781.72530 half-period of a cosine wave 2.0 \n",
+ "297 9782.66528 9782.67028 a single square pulse 1.3 \n",
+ "298 9784.81538 9784.82038 a single square pulse 1.3 \n",
+ "299 9786.60547 9786.61547 a single square pulse 1.3 \n",
+ "\n",
+ " name duration \n",
+ "id \n",
+ "0 pulse 0.005 \n",
+ "1 pulse 0.010 \n",
+ "2 fast_pulses 1.000 \n",
+ "3 fast_pulses 1.000 \n",
+ "4 fast_pulses 1.000 \n",
+ ".. ... ... \n",
+ "295 fast_pulses 1.000 \n",
+ "296 raised_cosine 1.000 \n",
+ "297 pulse 0.005 \n",
+ "298 pulse 0.005 \n",
+ "299 pulse 0.010 \n",
+ "\n",
+ "[300 rows x 6 columns]"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "session.optogenetic_stimulation_epochs"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This returns a table with information about each optotagging trial. To find the unique conditions across all trials, we can use the following Pandas syntax:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
condition
\n",
+ "
level
\n",
+ "
name
\n",
+ "
duration
\n",
+ "
\n",
+ "
\n",
+ "
id
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
3
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
1.3
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
1.7
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
2.5 ms pulses at 10 Hz
\n",
+ "
2.0
\n",
+ "
fast_pulses
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
17
\n",
+ "
a single square pulse
\n",
+ "
1.3
\n",
+ "
pulse
\n",
+ "
0.005
\n",
+ "
\n",
+ "
\n",
+ "
7
\n",
+ "
a single square pulse
\n",
+ "
1.7
\n",
+ "
pulse
\n",
+ "
0.005
\n",
+ "
\n",
+ "
\n",
+ "
0
\n",
+ "
a single square pulse
\n",
+ "
2.0
\n",
+ "
pulse
\n",
+ "
0.005
\n",
+ "
\n",
+ "
\n",
+ "
13
\n",
+ "
a single square pulse
\n",
+ "
1.3
\n",
+ "
pulse
\n",
+ "
0.010
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
a single square pulse
\n",
+ "
1.7
\n",
+ "
pulse
\n",
+ "
0.010
\n",
+ "
\n",
+ "
\n",
+ "
8
\n",
+ "
a single square pulse
\n",
+ "
2.0
\n",
+ "
pulse
\n",
+ "
0.010
\n",
+ "
\n",
+ "
\n",
+ "
5
\n",
+ "
half-period of a cosine wave
\n",
+ "
1.3
\n",
+ "
raised_cosine
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
14
\n",
+ "
half-period of a cosine wave
\n",
+ "
1.7
\n",
+ "
raised_cosine
\n",
+ "
1.000
\n",
+ "
\n",
+ "
\n",
+ "
6
\n",
+ "
half-period of a cosine wave
\n",
+ "
2.0
\n",
+ "
raised_cosine
\n",
+ "
1.000
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " condition level name duration\n",
+ "id \n",
+ "3 2.5 ms pulses at 10 Hz 1.3 fast_pulses 1.000\n",
+ "2 2.5 ms pulses at 10 Hz 1.7 fast_pulses 1.000\n",
+ "4 2.5 ms pulses at 10 Hz 2.0 fast_pulses 1.000\n",
+ "17 a single square pulse 1.3 pulse 0.005\n",
+ "7 a single square pulse 1.7 pulse 0.005\n",
+ "0 a single square pulse 2.0 pulse 0.005\n",
+ "13 a single square pulse 1.3 pulse 0.010\n",
+ "1 a single square pulse 1.7 pulse 0.010\n",
+ "8 a single square pulse 2.0 pulse 0.010\n",
+ "5 half-period of a cosine wave 1.3 raised_cosine 1.000\n",
+ "14 half-period of a cosine wave 1.7 raised_cosine 1.000\n",
+ "6 half-period of a cosine wave 2.0 raised_cosine 1.000"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "columns = ['name', 'duration','level']\n",
+ "\n",
+ "session.optogenetic_stimulation_epochs.drop_duplicates(columns).sort_values(by=columns).drop(columns=['start_time','stop_time'])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The optotagging portion of the experiment includes four categories of blue light stimuli: 2.5 ms pulses delivered at 10 Hz for one second, a single 5 ms pulse, a single 10 ms pulse, and a raised cosine pulse lasting 1 second. All of these stimuli are delivered through a 400 micron-diameter fiber optic cable positioned to illuminate the surface of visual cortex. Each stimulus is delivered at one of three power levels, defined by the peak voltage of the control signal delivered to the light source, not the actual light power at the tip of the fiber.\n",
+ "\n",
+ "Unfortunately, light power has not been perfectly matched across experiments. A little more than halfway through the data collection process, we switched from delivering light through an LED (maximum power at fiber tip = 4 mW) to a laser (maximum power at fiber tip = 35 mW), in order to evoke more robust optotagging responses. To check whether or not a particular experiment used a laser, you can use the following filter:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([False, False, False, False, False, False, False, False, False,\n",
+ " False, False, False, False, False, False, False, False, False,\n",
+ " False, False, False, False, False, False, False, False, False,\n",
+ " False, False, False, False, False, False, False, False, False,\n",
+ " False, False, False, False, True, True, True, True, True,\n",
+ " True, True, True, True, True, True, True, True, True,\n",
+ " True, True, True, True])"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sessions.index.values >= 789848216"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We realize that this makes it more difficult to compare results across experiments, but we decided it was better to improve the optotagging yield for later sessions than continue to use light levels that were not reliably driving spiking responses."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Aligning spikes to light pulses"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Aligning spikes to light pulses is a bit more involved than aligning spikes to visual stimuli. This is because we haven't yet created convenience functions for performing this alignment automatically, such as `session.presentationwise_spike_times` or `sesssion.presentationwise_spike_counts`. We are planning to incorporate such functions into the AllenSDK in the future, but for now, you'll have to write your own code for extracting spikes around light pulses (or copy the code below).\n",
+ "\n",
+ "Let's choose a stimulus condition (10 ms pulses) and a set of units (visual cortex only), then create a DataArray containing binned spikes aligned to the start of each stimulus."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "trials = session.optogenetic_stimulation_epochs[(session.optogenetic_stimulation_epochs.duration > 0.009) & \\\n",
+ " (session.optogenetic_stimulation_epochs.duration < 0.02)]\n",
+ "\n",
+ "units = session.units[session.units.ecephys_structure_acronym.str.match('VIS')]\n",
+ "\n",
+ "time_resolution = 0.0005 # 0.5 ms bins\n",
+ "\n",
+ "bin_edges = np.arange(-0.01, 0.025, time_resolution)\n",
+ "\n",
+ "def optotagging_spike_counts(bin_edges, trials, units):\n",
+ " \n",
+ " time_resolution = np.mean(np.diff(bin_edges))\n",
+ "\n",
+ " spike_matrix = np.zeros( (len(trials), len(bin_edges), len(units)) )\n",
+ "\n",
+ " for unit_idx, unit_id in enumerate(units.index.values):\n",
+ "\n",
+ " spike_times = session.spike_times[unit_id]\n",
+ "\n",
+ " for trial_idx, trial_start in enumerate(trials.start_time.values):\n",
+ "\n",
+ " in_range = (spike_times > (trial_start + bin_edges[0])) * \\\n",
+ " (spike_times < (trial_start + bin_edges[-1]))\n",
+ "\n",
+ " binned_times = ((spike_times[in_range] - (trial_start + bin_edges[0])) / time_resolution).astype('int')\n",
+ " spike_matrix[trial_idx, binned_times, unit_idx] = 1\n",
+ "\n",
+ " return xr.DataArray(\n",
+ " name='spike_counts',\n",
+ " data=spike_matrix,\n",
+ " coords={\n",
+ " 'trial_id': trials.index.values,\n",
+ " 'time_relative_to_stimulus_onset': bin_edges,\n",
+ " 'unit_id': units.index.values\n",
+ " },\n",
+ " dims=['trial_id', 'time_relative_to_stimulus_onset', 'unit_id']\n",
+ " )\n",
+ "\n",
+ "da = optotagging_spike_counts(bin_edges, trials, units)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can use this DataArray to plot the average firing rate for each unit as a function of time:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "