{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Recording objects\n\nThe :py:class:`~spikeinterface.core.BaseRecording` is the basic class for handling recorded data.\nHere is how it works.\n\nA RecordingExtractor handles:\n\n  * traces retrieval across segments\n  * dumping to/loading from dict-json\n  * saving (caching)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n\nimport numpy as np\nimport spikeinterface.extractors as se"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We will create a :code:`RecordingExtractor` object from scratch using :code:`numpy` and the\n:py:class:`~spikeinterface.core.NumpyRecording`.\n\nLet's define the properties of the dataset:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "num_channels = 7\nsampling_frequency = 30000.  # in Hz\ndurations = [10., 15.]  # \u00a0in s for 2 segments\nnum_segments = 2\nnum_timepoints = [int(sampling_frequency * d) for d in durations]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can generate a pure-noise timeseries dataset for 2 segments with 2\ndifferent durations:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "traces0 = np.random.normal(0, 10, (num_timepoints[0], num_channels))\ntraces1 = np.random.normal(0, 10, (num_timepoints[1], num_channels))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "And instantiate a :py:class:`~spikeinterface.core.NumpyRecording`.\u00a0Each object has a pretty print to\nsummarize its content:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "recording = se.NumpyRecording(traces_list=[traces0, traces1], sampling_frequency=sampling_frequency)\nprint(recording)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can now print properties that the :code:`RecordingExtractor` retrieves from the underlying recording.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "print('Num. channels = {}'.format(len(recording.get_channel_ids())))\nprint('Sampling frequency = {} Hz'.format(recording.get_sampling_frequency()))\nprint('Num. timepoints seg0= {}'.format(recording.get_num_segments()))\nprint('Num. timepoints seg0= {}'.format(recording.get_num_frames(segment_index=0)))\nprint('Num. timepoints seg1= {}'.format(recording.get_num_frames(segment_index=1)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The geometry of the Probe is handle with the :probeinterface:`ProbeInterface <>`.\nLet's generate a linear probe:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from probeinterface import generate_linear_probe\nfrom probeinterface.plotting import plot_probe\n\nprobe = generate_linear_probe(num_elec=7, ypitch=20, contact_shapes='circle', contact_shape_params={'radius': 6})\n\n# the probe has to be wired to the recording\nprobe.set_device_channel_indices(np.arange(7))\n\nrecording = recording.set_probe(probe)\nplot_probe(probe)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Some extractors also implement a :code:`write` function.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "file_paths = ['traces0.raw', 'traces1.raw']\nse.BinaryRecordingExtractor.write_recording(recording, file_paths)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can read the written recording back with the proper extractor.\nNote that this new recording is now \"on disk\" and not \"in memory\" as the Numpy recording.\nThis means that the loading is \"lazy\" and the data are not loaded in memory.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "recording2 = se.BinaryRecordingExtractor(file_paths, sampling_frequency, num_channels, traces0.dtype)\nprint(recording2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Loading traces in memory is done on demand:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# entire segment 0\ntraces0 = recording2.get_traces(segment_index=0)\n# part of segment 1\ntraces1_short = recording2.get_traces(segment_index=1, end_frame=50)\nprint(traces0.shape)\nprint(traces1_short.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "A recording internally has :code:`channel_ids`: these are a vector that can have\ndtype int or str:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "print('chan_ids (dtype=int):', recording.get_channel_ids())\n\nrecording3 = se.NumpyRecording(traces_list=[traces0, traces1],\n                               sampling_frequency=sampling_frequency,\n                               channel_ids=['a', 'b', 'c', 'd', 'e', 'f', 'g'])\nprint('chan_ids (dtype=str):', recording3.get_channel_ids())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ":code:`channel_ids` are used to retrieve information (e.g. traces) only on a\nsubset of channels:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "traces = recording3.get_traces(segment_index=1, end_frame=50, channel_ids=['a', 'd'])\nprint(traces.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "You can also get a a recording with a subset of channel (a channel slice):\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "recording4 = recording3.channel_slice(channel_ids=['a', 'c', 'e'])\nprint(recording4)\nprint(recording4.get_channel_ids())\n\n# which is equivalent to\nfrom spikeinterface import ChannelSliceRecording\n\nrecording4 = ChannelSliceRecording(recording3, channel_ids=['a', 'c', 'e'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Another possibility is to split a recording based on a certain property (e.g. 'group')\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "recording3.set_property('group', [0, 0, 0, 1, 1, 1, 2])\n\nrecordings = recording3.split_by(property='group')\nprint(recordings)\nprint(recordings[0].get_channel_ids())\nprint(recordings[1].get_channel_ids())\nprint(recordings[2].get_channel_ids())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "A recording can be \"dumped\" (exported) to:\n * a dict\n\u00a0* a json file\n * a pickle file\n\nThe \"dump\" operation is lazy, i.e., the traces are not exported.\nOnly the information about how to reconstruct the recording are dumped:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from spikeinterface import load_extractor\nfrom pprint import pprint\n\nd = recording2.to_dict()\npprint(d)\n\nrecording2_loaded = load_extractor(d)\nprint(recording2_loaded)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The dictionary can also be dumped directly to a JSON file on disk:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "recording2.dump('my_recording.json')\n\nrecording2_loaded = load_extractor('my_recording.json')\nprint(recording2_loaded)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**IMPORTANT**: the \"dump\" operation DOES NOT copy the traces to disk!\n\nIf you wish to also store the traces in a compact way you need to use the\n:code:`save()` function. This operation is very useful to save traces obtained\nafter long computation (e.g. filtering):\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "recording2.save(folder='./my_recording')\n\nimport os\n\npprint(os.listdir('./my_recording'))\n\nrecording2_cached = load_extractor('my_recording.json')\nprint(recording2_cached)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.8.13"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}