
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "examples\datasets\plot_data_beyeler2019.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_examples_datasets_plot_data_beyeler2019.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_examples_datasets_plot_data_beyeler2019.py:


===============================================================================
Phosphene drawings from Beyeler et al. (2019)
===============================================================================

This example shows how to use the Beyeler et al. (2019) dataset.

[Beyeler2019]_ asked Argus I/II users to draw what they see in response to
single-electrode stimulation.

.. important ::

    For this dataset you will need to install both
    `Pandas <https://pandas.pydata.org>`_ (``pip install pandas``) and
    `HDF4 for Python <https://www.h5py.org>`_ (``pip install h5py``).

Loading the dataset
-------------------

Due to its size (66 MB), the dataset is not included with pulse2percept, but
can be downloaded from the Open Science Framework (OSF).

By default, the dataset will be stored in a local directory
‘~/pulse2percept_data/’ within your user directory (but a different path can be
specified).
This way, the datasets is only downloaded once, and future calls to the fetch
function will load the dataset from your local copy.

The data itself will be provided as a Pandas ``DataFrame``:

.. GENERATED FROM PYTHON SOURCE LINES 32-38

.. code-block:: Python


    from pulse2percept.datasets import fetch_beyeler2019

    data = fetch_beyeler2019()
    print(data)





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

        subject  amp   area  compactness           date  eccentricity electrode  ...   img_shape  implant_type_str implant_x  implant_y  implant_rot         xrange         yrange
    0        S1  2.0   1614     0.604340  b'2008/07/02'      0.865177        C3  ...  (192, 192)            ArgusI     -1527       -556       -64.74  (-36.9, 36.9)  (-36.9, 36.9)
    1        S1  2.0   3691     0.432559  b'2008/07/02'      0.906236        D1  ...  (192, 192)            ArgusI     -1527       -556       -64.74  (-36.9, 36.9)  (-36.9, 36.9)
    2        S1  2.0   1812     0.585561  b'2008/07/02'      0.857745        D4  ...  (192, 192)            ArgusI     -1527       -556       -64.74  (-36.9, 36.9)  (-36.9, 36.9)
    3        S1  2.0   1970     0.485596  b'2008/07/02'      0.817662        C1  ...  (192, 192)            ArgusI     -1527       -556       -64.74  (-36.9, 36.9)  (-36.9, 36.9)
    4        S1  2.0   1554     0.600936  b'2008/07/02'      0.847038        C3  ...  (192, 192)            ArgusI     -1527       -556       -64.74  (-36.9, 36.9)  (-36.9, 36.9)
    ..      ...  ...    ...          ...            ...           ...       ...  ...         ...               ...       ...        ...          ...            ...            ...
    395      S4  2.0    902     0.121365  b'2010/02/04'      0.997936        F6  ...  (384, 512)           ArgusII     -1945        469       -34.00      (-32, 32)      (-24, 24)
    396      S4  2.0   8312     0.771017  b'2010/02/04'      0.597785        A7  ...  (384, 512)           ArgusII     -1945        469       -34.00      (-32, 32)      (-24, 24)
    397      S4  2.0   8754     0.356872  b'2010/02/04'      0.927432        F9  ...  (384, 512)           ArgusII     -1945        469       -34.00      (-32, 32)      (-24, 24)
    398      S4  2.0    888     0.119325  b'2010/02/04'      0.998050        F6  ...  (384, 512)           ArgusII     -1945        469       -34.00      (-32, 32)      (-24, 24)
    399      S4  2.0  12954     0.611507  b'2010/02/04'      0.735943        A7  ...  (384, 512)           ArgusII     -1945        469       -34.00      (-32, 32)      (-24, 24)

    [400 rows x 22 columns]




.. GENERATED FROM PYTHON SOURCE LINES 40-45

Inspecting the DataFrame tells us that there are 400 phosphene drawings
(the rows) each with 16 different attributes (the columns).

These attributes include specifiers such as "subject", "electrode", and
"image". We can print all column names using:

.. GENERATED FROM PYTHON SOURCE LINES 46-49

.. code-block:: Python


    data.columns





.. rst-class:: sphx-glr-script-out

 .. code-block:: none


    Index(['subject', 'amp', 'area', 'compactness', 'date', 'eccentricity',
           'electrode', 'filename', 'freq', 'image', 'orientation', 'pdur',
           'stim_class', 'x_center', 'y_center', 'img_shape', 'implant_type_str',
           'implant_x', 'implant_y', 'implant_rot', 'xrange', 'yrange'],
          dtype='object')



.. GENERATED FROM PYTHON SOURCE LINES 50-56

.. note ::

    The meaning of all column names is explained in the docstring of
    the :py:func:`~pulse2percept.datasets.fetch_beyeler2019` function.

For example, "subject" contains the different subject IDs used in the study:

.. GENERATED FROM PYTHON SOURCE LINES 56-59

.. code-block:: Python


    data.subject.unique()





.. rst-class:: sphx-glr-script-out

 .. code-block:: none


    array(['S1', 'S2', 'S3', 'S4'], dtype=object)



.. GENERATED FROM PYTHON SOURCE LINES 60-62

To select all drawings from Subject 2, we can index into the DataFrame as
follows:

.. GENERATED FROM PYTHON SOURCE LINES 62-65

.. code-block:: Python


    print(data[data.subject == 'S2'])





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

        subject  amp  area  compactness           date  eccentricity electrode  ...   img_shape  implant_type_str implant_x  implant_y  implant_rot     xrange         yrange
    60       S2  2.0   548     0.181188  b'2010/01/27'      0.970663        D7  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    61       S2  2.0  7875     0.529941  b'2010/01/27'      0.939527        A1  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    62       S2  2.0  4494     0.403850  b'2010/01/27'      0.972984        B1  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    63       S2  2.0   801     0.119632  b'2010/01/27'      0.977781        A1  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    64       S2  2.0  3969     0.574477  b'2010/01/27'      0.640912        A6  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    ..      ...  ...   ...          ...            ...           ...       ...  ...         ...               ...       ...        ...          ...        ...            ...
    165      S2  2.0  1535     0.329306  b'2010/02/03'      0.959245        F4  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    166      S2  2.0  1574     0.513694  b'2010/02/03'      0.828150        E3  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    167      S2  2.0  3985     0.776046  b'2010/02/03'      0.522466        E3  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    168      S2  2.0   583     0.175066  b'2010/02/03'      0.802291       E10  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)
    169      S2  2.0  1705     0.551216  b'2010/02/03'      0.396574       E10  ...  (384, 512)           ArgusII     -1896       -542        -44.0  (-30, 30)  (-22.5, 22.5)

    [110 rows x 22 columns]




.. GENERATED FROM PYTHON SOURCE LINES 66-71

This leaves us with 110 rows, each of which correspond to one phosphene
drawings from a number of different electrodes and trials.

An alternative to indexing into the DataFrame is to load only a subset of
the data:

.. GENERATED FROM PYTHON SOURCE LINES 71-74

.. code-block:: Python


    print(fetch_beyeler2019(subjects='S2'))





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

        subject  amp  area  compactness           date  eccentricity electrode  ...   img_shape  implant_type_str implant_x  implant_y  implant_rot     xrange         yrange
    0        S2  2.0   548     0.181188  b'2010/01/27'      0.970663        D7  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    1        S2  2.0  7875     0.529941  b'2010/01/27'      0.939527        A1  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    2        S2  2.0  4494     0.403850  b'2010/01/27'      0.972984        B1  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    3        S2  2.0   801     0.119632  b'2010/01/27'      0.977781        A1  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    4        S2  2.0  3969     0.574477  b'2010/01/27'      0.640912        A6  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    ..      ...  ...   ...          ...            ...           ...       ...  ...         ...               ...       ...        ...          ...        ...            ...
    105      S2  2.0  1535     0.329306  b'2010/02/03'      0.959245        F4  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    106      S2  2.0  1574     0.513694  b'2010/02/03'      0.828150        E3  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    107      S2  2.0  3985     0.776046  b'2010/02/03'      0.522466        E3  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    108      S2  2.0   583     0.175066  b'2010/02/03'      0.802291       E10  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)
    109      S2  2.0  1705     0.551216  b'2010/02/03'      0.396574       E10  ...  (384, 512)           ArgusII     -1896       -542          -44  (-30, 30)  (-22.5, 22.5)

    [110 rows x 22 columns]




.. GENERATED FROM PYTHON SOURCE LINES 75-83

Plotting the data
-----------------

Arguably the most important column is "image". This is the phosphene drawing
obtained during a particular trial.

Each phosphene drawing is a 2D black-and-white NumPy array, so we can just
plot it using Matplotlib like any other image:

.. GENERATED FROM PYTHON SOURCE LINES 83-87

.. code-block:: Python


    import matplotlib.pyplot as plt
    plt.imshow(data.loc[0, 'image'], cmap='gray')




.. image-sg:: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_001.png
   :alt: plot data beyeler2019
   :srcset: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_001.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none


    <matplotlib.image.AxesImage object at 0x00000251C5AB6050>



.. GENERATED FROM PYTHON SOURCE LINES 88-98

However, we might be more interested in seeing how phosphene shape differs
for different electrodes.
For this we can use :py:func:`~pulse2percept.viz.plot_argus_phosphenes` from
the :py:mod:`~pulse2percept.viz` module.
In addition to the ``data`` matrix, the function will also want an
:py:class:`~pulse2percept.implants.ArgusII` object implanted at the correct
location.

Consulting [Beyeler2019]_ tells us that the prosthesis was roughly implanted
in the following location:

.. GENERATED FROM PYTHON SOURCE LINES 98-102

.. code-block:: Python


    from pulse2percept.implants import ArgusII
    argus = ArgusII(x=-1331, y=-850, rot=-28.4, eye='RE')








.. GENERATED FROM PYTHON SOURCE LINES 103-104

For now, let's focus on the data from Subject 2:

.. GENERATED FROM PYTHON SOURCE LINES 104-107

.. code-block:: Python


    data = fetch_beyeler2019(subjects='S2')








.. GENERATED FROM PYTHON SOURCE LINES 108-113

Passing both ``data`` and ``argus`` to
:py:func:`~pulse2percept.viz.plot_argus_phosphenes` will then allow the
function to overlay the phosphene drawings over a schematic of the implant.
Here, phosphene drawings from different trials are averaged, and aligned with
the center of the electrode that was used to obtain the drawing:

.. GENERATED FROM PYTHON SOURCE LINES 113-117

.. code-block:: Python


    from pulse2percept.viz import plot_argus_phosphenes
    plot_argus_phosphenes(data, argus)




.. image-sg:: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_002.png
   :alt: plot data beyeler2019
   :srcset: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_002.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [-2.2000000000000006..1.0].

    <Axes: >



.. GENERATED FROM PYTHON SOURCE LINES 118-129

Great! We have just reproduced a panel from Figure 2 in [Beyeler2019]_.

As [Beyeler2019]_ went on to show, the orientation of these phosphenes is
well aligned with the map of nerve fiber bundles (NFBs) in each subject's
eye.

To see how the phosphene drawings line up with the NFBs, we can also pass an
:py:class:`~pulse2percept.models.AxonMapModel` to the function.
Of course, we need to make sure that we use the correct dimensions. Subject
S2 had their optic disc center located 16.2 deg nasally, 1.38 deg superior
from the fovea:

.. GENERATED FROM PYTHON SOURCE LINES 129-134

.. code-block:: Python


    from pulse2percept.models import AxonMapModel
    model = AxonMapModel(loc_od=(16.2, 1.38))
    plot_argus_phosphenes(data, argus, axon_map=model)




.. image-sg:: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_003.png
   :alt: plot data beyeler2019
   :srcset: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_003.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [-2.2000000000000006..1.0].

    <Axes: >



.. GENERATED FROM PYTHON SOURCE LINES 135-141

Predicting phosphene shape
--------------------------

In addition, the :py:class:`~pulse2percept.models.AxonMapModel` is well
suited to predict the shape of individual phosphenes. Using the values given
in [Beyeler2019]_, we can tailor the axon map parameters to Subject 2:

.. GENERATED FROM PYTHON SOURCE LINES 141-148

.. code-block:: Python


    import numpy as np
    model = AxonMapModel(rho=315, lam=500, loc_od=(16.2, 1.38),
                         xrange=(-30, 30), yrange=(-22.5, 22.5),
                         thresh_percept=1 / np.sqrt(np.e))
    model.build()





.. rst-class:: sphx-glr-script-out

 .. code-block:: none


    AxonMapModel(ax_segments_range=(0, 50), 
                 axon_pickle='axons.pickle', 
                 axons_range=(-180, 180), eye='RE', 
                 grid_type='rectangular', ignore_pickle=False, 
                 lam=500, loc_od=(np.float64(16.2), 1.38), 
                 min_ax_sensitivity=0.001, 
                 min_current_spread=1e-08, n_ax_segments=500, 
                 n_axons=1000, n_gray=None, n_jobs=14, 
                 n_threads=14, ndim=[2], noise=None, rho=315, 
                 spatial=AxonMapSpatial, step=0.25, 
                 temporal=None, 
                 thresh_percept=0.6065306597126334, 
                 verbose=True, vfmap=Watson2014Map(ndim=2), 
                 xrange=(-30, 30), yrange=(-22.5, 22.5))



.. GENERATED FROM PYTHON SOURCE LINES 149-156

Now we need to activate one electrode at a time, and predict the resulting
percept. We could build a :py:class:`~pulse2percept.stimuli.Stimulus` object
with a for loop that does just that, or we can use the following trick.

The stimulus' data container is a (electrodes, timepoints) shaped 2D NumPy
array. Activating one electrode at a time is therefore the same as an
identity matrix whose size is equal to the number of electrodes. In code:

.. GENERATED FROM PYTHON SOURCE LINES 156-164

.. code-block:: Python


    # Find the names of all the electrodes in the dataset:
    electrodes = data.electrode.unique()
    # Activate one electrode at a time:
    import numpy as np
    from pulse2percept.stimuli import Stimulus
    argus.stim = Stimulus(np.eye(len(electrodes)), electrodes=electrodes)








.. GENERATED FROM PYTHON SOURCE LINES 165-169

Using the model's
:py:func:`~pulse2percept.models.AxonMapModel.predict_percept`, we then get
a Percept object where each frame is the percept generated from activating
a single electrode:

.. GENERATED FROM PYTHON SOURCE LINES 169-173

.. code-block:: Python


    percepts = model.predict_percept(argus)
    percepts.play()






.. raw:: html

    <div class="output_subarea output_html rendered_html output_result">

    <div class="p2p-anim" id="p2p-anim-0ed5289132064f3daba991ae2d935974">
      <div class="p2p-stage">
        <img class="p2p-bg" src="data:image/png;base64,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" alt="">
        <canvas class="p2p-canvas" width="800" height="500"></canvas>
      </div>
      <div class="p2p-controls">
        <button class="p2p-btn" data-p2p-go="first"
                title="First frame">&#9198;</button>
        <button class="p2p-btn" data-p2p-go="prev"
                title="Previous frame">&#9194;</button>
        <button class="p2p-btn p2p-toggle" title="Play/Pause">&#9654;</button>
        <button class="p2p-btn" data-p2p-go="next"
                title="Next frame">&#9193;</button>
        <button class="p2p-btn" data-p2p-go="last"
                title="Last frame">&#9197;</button>
        <input class="p2p-slider" type="range" min="0" max="21" value="0"
               step="1">
        <span class="p2p-count">1/22</span>
        <select class="p2p-mode" title="Loop mode">
          <option value="once">Once</option>
          <option value="loop">Loop</option>
          <option value="reflect">Reflect</option>
        </select>
      </div>
    </div>
    <style>
    #p2p-anim-0ed5289132064f3daba991ae2d935974 { display: inline-block; max-width: 100%; width: 800px;
            font-family: sans-serif; font-size: 13px; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-stage { position: relative; line-height: 0; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-bg { width: 100%; height: auto; display: block; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-canvas { position: absolute; left: 0; top: 0;
                        width: 100%; height: 100%; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-controls { display: flex; align-items: center; gap: 4px;
                          padding-top: 4px; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-btn { cursor: pointer; border: 1px solid rgba(128,128,128,0.4);
                     border-radius: 3px; background: transparent; color: inherit;
                     padding: 1px 6px; font-size: 13px; line-height: 1.4; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-btn:hover { background: rgba(128,128,128,0.2); }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-slider { flex: 1 1 auto; min-width: 40px; margin: 0 4px; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-count { font-variant-numeric: tabular-nums; opacity: 0.7;
                       white-space: nowrap; }
    #p2p-anim-0ed5289132064f3daba991ae2d935974 .p2p-mode { background: transparent; color: inherit; font-size: 12px;
                      border-radius: 3px;
                      border: 1px solid rgba(128,128,128,0.4); }
    </style>
    <script>
    (function () {
      var cfg = {"n": 22, "ncols": 5, "fw": 241, "fh": 181, "sw": 241, "sh": 181, "rect": [100, 66, 497, 373], "smooth": true, "interval": 1.0, "mode": "loop", "title": {"rect": [0, 43, 800, 20], "x": 348, "y": 53, "align": "center", "font": "normal 16.7px \"DejaVu Sans\", Verdana, sans-serif", "color": "#000000"}, "labels": ["t = 0.00 ms", "t = 1.00 ms", "t = 2.00 ms", "t = 3.00 ms", "t = 4.00 ms", "t = 5.00 ms", "t = 6.00 ms", "t = 7.00 ms", "t = 8.00 ms", "t = 9.00 ms", "t = 10.00 ms", "t = 11.00 ms", "t = 12.00 ms", "t = 13.00 ms", "t = 14.00 ms", "t = 15.00 ms", "t = 16.00 ms", "t = 17.00 ms", "t = 18.00 ms", "t = 19.00 ms", "t = 20.00 ms", "t = 21.00 ms"]};
      var root = document.getElementById("p2p-anim-0ed5289132064f3daba991ae2d935974");
      if (!root) { return; }
      var ctx = root.querySelector(".p2p-canvas").getContext("2d");
      var slider = root.querySelector(".p2p-slider");
      var counter = root.querySelector(".p2p-count");
      var mode = root.querySelector(".p2p-mode");
      var toggle = root.querySelector(".p2p-toggle");
      var frame = 0, dir = 1, timer = null, sheet = new Image();

      function draw() {
        if (!sheet.complete || !sheet.naturalWidth) { return; }
        var col = frame % cfg.ncols, row = (frame - col) / cfg.ncols;
        ctx.imageSmoothingEnabled = cfg.smooth;
        // Frames with an alpha channel are composited onto the static background,
        // so the previous frame has to go first or they stack up:
        ctx.clearRect(cfg.rect[0], cfg.rect[1], cfg.rect[2], cfg.rect[3]);
        ctx.drawImage(sheet, col * cfg.sw, row * cfg.sh, cfg.fw, cfg.fh,
                      cfg.rect[0], cfg.rect[1], cfg.rect[2], cfg.rect[3]);
        if (cfg.title) {
          ctx.clearRect(cfg.title.rect[0], cfg.title.rect[1],
                        cfg.title.rect[2], cfg.title.rect[3]);
          ctx.font = cfg.title.font;
          ctx.fillStyle = cfg.title.color;
          ctx.textAlign = cfg.title.align;
          ctx.textBaseline = "middle";
          ctx.fillText(cfg.labels[frame], cfg.title.x, cfg.title.y);
        }
      }

      function show(i) {
        frame = Math.max(0, Math.min(cfg.n - 1, i));
        slider.value = frame;
        counter.textContent = (frame + 1) + "/" + cfg.n;
        draw();
      }

      function pause() {
        if (timer !== null) { clearInterval(timer); timer = null; }
        toggle.innerHTML = "&#9654;";
      }

      function play() {
        if (timer !== null) { return; }
        if (mode.value === "once" && frame === cfg.n - 1) { show(0); }
        toggle.innerHTML = "&#10074;&#10074;";
        timer = setInterval(advance, Math.max(1, cfg.interval));
      }

      function advance() {
        var next = frame + dir;
        if (next > cfg.n - 1 || next < 0) {
          if (mode.value === "loop") {
            next = dir > 0 ? 0 : cfg.n - 1;
          } else if (mode.value === "reflect" && cfg.n > 1) {
            dir = -dir;
            next = frame + dir;
          } else {
            pause();
            return;
          }
        }
        show(next);
      }

      root.querySelectorAll("[data-p2p-go]").forEach(function (btn) {
        btn.addEventListener("click", function () {
          pause();
          dir = 1;
          var go = btn.dataset.p2pGo;
          show(go === "first" ? 0 : go === "last" ? cfg.n - 1 :
               go === "next" ? frame + 1 : frame - 1);
        });
      });
      toggle.addEventListener("click", function () {
        if (timer === null) { play(); } else { pause(); }
      });
      slider.addEventListener("input", function () {
        pause();
        dir = 1;
        show(parseInt(slider.value, 10));
      });
      mode.value = cfg.mode;
      sheet.onload = draw;
      sheet.src = "data:image/png;base64,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";
      show(0);
    })();
    </script>

    </div>
    <br />
    <br />

.. GENERATED FROM PYTHON SOURCE LINES 174-176

Finally, we can visualize the ground-truth and simulated phosphenes
side-by-side:

.. GENERATED FROM PYTHON SOURCE LINES 176-184

.. code-block:: Python


    from pulse2percept.viz import plot_argus_simulated_phosphenes
    fig, (ax_data, ax_sim) = plt.subplots(ncols=2, figsize=(15, 5))
    plot_argus_phosphenes(data, argus, scale=0.75, ax=ax_data)
    plot_argus_simulated_phosphenes(percepts, argus, scale=1.25, ax=ax_sim)
    ax_data.set_title('Ground-truth phosphenes')
    ax_sim.set_title('Simulated phosphenes')




.. image-sg:: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_004.png
   :alt: Ground-truth phosphenes, Simulated phosphenes
   :srcset: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_004.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [-1.1999999999999997..1.0].
    Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [-1.4311562180519104..1.0].

    Text(0.5, 1.0, 'Simulated phosphenes')



.. GENERATED FROM PYTHON SOURCE LINES 185-202

Analyzing phosphene shape
-------------------------

The phosphene drawings also come annotated with different shape descriptors:
area, orientation, and elongation.
Elongation is also called eccentricity in the computer vision literature,
which is not to be confused with retinal eccentricity. It is simply a number
between 0 and 1, where 0 corresponds to a circle and 1 corresponds to an
infinitesimally thin line (note that the Methods section of [Beyeler2019]_
got it wrong).

[Beyeler2019]_ made the point that if each phosphene could be considered a
pixel (or essentially a blob), as is so often assumed in the literature, then
most phosphenes should have zero elongation.

Instead, using Matplotlib's histogram function, we can convince ourselves
that most phosphenes are in fact elongated:

.. GENERATED FROM PYTHON SOURCE LINES 202-207

.. code-block:: Python


    data = fetch_beyeler2019()
    data.eccentricity.plot(kind='hist')
    plt.xlabel('phosphene elongation')




.. image-sg:: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_005.png
   :alt: plot data beyeler2019
   :srcset: /examples/datasets/images/sphx_glr_plot_data_beyeler2019_005.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none


    Text(0.5, 23.52222222222222, 'phosphene elongation')



.. GENERATED FROM PYTHON SOURCE LINES 208-210

Phosphenes are not pixels!
And with that we have just reproduced Fig. 3C of [Beyeler2019]_.


.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 16.468 seconds)


.. _sphx_glr_download_examples_datasets_plot_data_beyeler2019.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: plot_data_beyeler2019.ipynb <plot_data_beyeler2019.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: plot_data_beyeler2019.py <plot_data_beyeler2019.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: plot_data_beyeler2019.zip <plot_data_beyeler2019.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
