Repository: mne-tools/mne-python

Issue:
ENH: read_stc should return <Souce Estimat> not dict.
For consistency it would be nice to consuct the class instead of reading the dict.
Wdyt?



Discussion:
+1 I was assuming it already returns a SourceEstimate..

If I remember correctly the idea was that you should use `read_source_estimate()`, while `read_stc()` was kept the way it was for backwards compatibility. Functionally the difference is that `read_stc` only reads one hemisphere while `read_source_estimate` reads both, so if reading only one hemi sould be possible it might be better to have a name indicating that difference rather than `read_stc` vs `read_source_estimate`?

the logic was that SourceEstimate expects one stc per hemisphere for
reading and saves both by default. I agree there is something to fix /
deprecate...

the question should read_stc be deprecated and made private ? what
would you want to use read_stc?

I think i mistook it for the appropriate function to read in source
estimats. +1 for making it private and deprecating its diect call.

On Fri, Jan 25, 2013 at 3:55 PM, Alexandre Gramfort <
notifications@github.com> wrote:

> the question should read_stc be deprecated and made private ? what
> would you want to use read_stc?
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/mne-tools/mne-python/issues/406#issuecomment-12703721.

> I think i mistook it for the appropriate function to read in source
> estimats. +1 for making it private and deprecating its diect call.

ok good. PR welcome then.

btw I am flying to helsinki on sunday for 3 days to teach python/mne-python.

if you have time to prepare a few ipython notebooks starting by converting
the python-tutorial to a notebook that would be of great help... I am
struggling with time as you figured...

cc @mluessi @Eric89GXL @christianmbrodbeck @joewalter @dengemann

I will look into converting the tutorial to a notebook. I tried before, but the output was much too verbose (`mne.set_log_level()` should be helpful for this). Having a notebook will be handy for future courses etc :-)

@agramfort see #407 for the notebook

Great, will have some chance to play with it later. Back to the issue. Deprecating seems not as trivial as i thought. The meg pysurfer example depends on read_stc. Option i) keep it as is. ii) add an arg to read_source_estimates that allows loading only one surface. iii) add a second surface to the pysurfer example. makes sense? 

pysurfer implements read_stc in surfer.io so we can use this one for an
example depending on pysurfer.

right, didn't look that carefully.

Namespaces are one honking great idea -- let's do more of those!

On Sun, Jan 27, 2013 at 10:37 AM, Alexandre Gramfort <
notifications@github.com> wrote:

> pysurfer implements read_stc in surfer.io so we can use this one for an
> example depending on pysurfer.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/mne-tools/mne-python/issues/406#issuecomment-12751921.


Resolving patch:
diff --git a/mne/label.py b/mne/label.py
--- a/mne/label.py
+++ b/mne/label.py
@@ -15,7 +15,7 @@
 logger = logging.getLogger('mne')
 
 from .utils import get_subjects_dir, _check_subject
-from .source_estimate import read_stc, mesh_edges, mesh_dist, morph_data, \
+from .source_estimate import _read_stc, mesh_edges, mesh_dist, morph_data, \
                              SourceEstimate
 from .surface import read_surface
 from . import verbose
@@ -511,7 +511,7 @@ def label_time_courses(labelfile, stcfile):
     vertices : array
         The indices of the vertices corresponding to the time points.
     """
-    stc = read_stc(stcfile)
+    stc = _read_stc(stcfile)
     lab = read_label(labelfile)
 
     vertices = np.intersect1d(stc['vertices'], lab.vertices)
diff --git a/mne/source_estimate.py b/mne/source_estimate.py
--- a/mne/source_estimate.py
+++ b/mne/source_estimate.py
@@ -19,12 +19,15 @@
 from .parallel import parallel_func
 from .surface import read_surface
 from .utils import get_subjects_dir, _check_subject, \
-                   _check_pandas_index_arguments, _check_pandas_installed
+                   _check_pandas_index_arguments, _check_pandas_installed, \
+                   deprecated
 from .viz import plot_source_estimates
 from . import verbose
 from . fixes import in1d
 
 
+@deprecated('read_stc is deprecated and will be removed with version 0.7. '
+            'Please use read_source_estimate instead.')
 def read_stc(filename):
     """Read an STC file and return as dict
 
@@ -48,6 +51,12 @@ def read_stc(filename):
     --------
     read_source_estimate
     """
+    return _read_stc(filename)
+
+
+def _read_stc(filename):
+    """ Aux Function
+    """
     fid = open(filename, 'rb')
 
     stc = dict()
@@ -86,9 +95,34 @@ def read_stc(filename):
     return stc
 
 
+@deprecated('write_stc is deprecated and will be removed with version 0.7. '
+            'Please use SourceEstimate.save instead.')
 def write_stc(filename, tmin, tstep, vertices, data):
     """Write an STC file
 
+    Parameters
+    ----------
+    filename : string
+        The name of the STC file.
+    tmin : float
+        The first time point of the data in seconds.
+    tstep : float
+        Time between frames in seconds.
+    vertices : array of integers
+        Vertex indices (0 based).
+    data : 2D array
+        The data matrix (nvert * ntime).
+
+    See Also
+    --------
+    SourceEstimate.save (instance method)
+    """
+    return _write_stc(filename, tmin, tstep, vertices, data)
+
+
+def _write_stc(filename, tmin, tstep, vertices, data):
+    """Write an STC file
+
     Parameters
     ----------
     filename : string
@@ -134,12 +168,35 @@ def _read_3(fid):
     return out
 
 
+@deprecated('read_w is deprecated and will be removed with version 0.7. '
+            'Please use read_source_estimate instead.')
 def read_w(filename):
     """Read a w file and return as dict
 
     w files contain activations or source reconstructions for a single time
     point.
 
+    Parameters
+    ----------
+    filename : string
+        The name of the w file.
+
+    Returns
+    -------
+    data: dict
+        The w structure. It has the following keys:
+           vertices       vertex indices (0 based)
+           data           The data matrix (nvert long)
+    """
+    return _read_w(filename)
+
+
+def _read_w(filename):
+    """Read a w file and return as dict
+
+    w files contain activations or source reconstructions for a single time
+    point.
+
     Parameters
     ----------
     filename : string
@@ -191,6 +248,8 @@ def _write_3(fid, val):
     fid.write(f_bytes.tostring())
 
 
+@deprecated('read_w is deprecated and will be removed with version 0.7. '
+            'Please use SoureEstimate.save instead.')
 def write_w(filename, vertices, data):
     """Read a w file
 
@@ -208,7 +267,25 @@ def write_w(filename, vertices, data):
 
     See Also
     --------
-    read_source_estimate
+    SourceEstimate.save (instance method)
+    """
+    return _write_w(filename, vertices, data)
+
+
+def _write_w(filename, vertices, data):
+    """Read a w file
+
+    w files contain activations or source reconstructions for a single time
+    point.
+
+    Parameters
+    ----------
+    filename: string
+        The name of the w file.
+    vertices: array of int
+        Vertex indices (0 based).
+    data: 1D array
+        The data array (nvert).
     """
 
     assert(len(vertices) == len(data))
@@ -258,10 +335,6 @@ def read_source_estimate(fname, subject=None):
      - for single time point .w files, ``fname`` should follow the same
        pattern as for surface estimates, except that files are named
        '*-lh.w' and '*-rh.w'.
-
-    See Also
-    --------
-    read_stc, read_w
     """
     fname_arg = fname
 
@@ -306,25 +379,25 @@ def read_source_estimate(fname, subject=None):
     # read the files
     if ftype == 'volume':  # volume source space
         if fname.endswith('.stc'):
-            kwargs = read_stc(fname)
+            kwargs = _read_stc(fname)
         elif fname.endswith('.w'):
-            kwargs = read_w(fname)
+            kwargs = _read_w(fname)
             kwargs['data'] = kwargs['data'][:, np.newaxis]
             kwargs['tmin'] = 0.0
             kwargs['tstep'] = 0.0
         else:
             raise IOError('Volume source estimate must end with .stc or .w')
     elif ftype == 'surface':  # stc file with surface source spaces
-        lh = read_stc(fname + '-lh.stc')
-        rh = read_stc(fname + '-rh.stc')
+        lh = _read_stc(fname + '-lh.stc')
+        rh = _read_stc(fname + '-rh.stc')
         assert lh['tmin'] == rh['tmin']
         assert lh['tstep'] == rh['tstep']
         kwargs = lh.copy()
         kwargs['data'] = np.r_[lh['data'], rh['data']]
         kwargs['vertices'] = [lh['vertices'], rh['vertices']]
     elif ftype == 'w':  # w file with surface source spaces
-        lh = read_w(fname + '-lh.w')
-        rh = read_w(fname + '-rh.w')
+        lh = _read_w(fname + '-lh.w')
+        rh = _read_w(fname + '-rh.w')
         kwargs = lh.copy()
         kwargs['data'] = np.atleast_2d(np.r_[lh['data'], rh['data']]).T
         kwargs['vertices'] = [lh['vertices'], rh['vertices']]
@@ -496,19 +569,19 @@ def save(self, fname, ftype='stc', verbose=None):
 
             if ftype == 'stc':
                 logger.info('Writing STC to disk...')
-                write_stc(fname + '-lh.stc', tmin=self.tmin, tstep=self.tstep,
-                          vertices=self.lh_vertno, data=lh_data)
-                write_stc(fname + '-rh.stc', tmin=self.tmin, tstep=self.tstep,
-                          vertices=self.rh_vertno, data=rh_data)
+                _write_stc(fname + '-lh.stc', tmin=self.tmin, tstep=self.tstep,
+                           vertices=self.lh_vertno, data=lh_data)
+                _write_stc(fname + '-rh.stc', tmin=self.tmin, tstep=self.tstep,
+                           vertices=self.rh_vertno, data=rh_data)
             elif ftype == 'w':
                 if self.shape[1] != 1:
                     raise ValueError('w files can only contain a single time '
                                      'point')
                 logger.info('Writing STC to disk (w format)...')
-                write_w(fname + '-lh.w', vertices=self.lh_vertno,
-                        data=lh_data[:, 0])
-                write_w(fname + '-rh.w', vertices=self.rh_vertno,
-                        data=rh_data[:, 0])
+                _write_w(fname + '-lh.w', vertices=self.lh_vertno,
+                         data=lh_data[:, 0])
+                _write_w(fname + '-rh.w', vertices=self.rh_vertno,
+                         data=rh_data[:, 0])
         else:
             if isinstance(self.vertno, list):
                 write_vertices = self.vertno[0]
@@ -519,14 +592,14 @@ def save(self, fname, ftype='stc', verbose=None):
                 if not (fname.endswith('-vl.stc')
                         or fname.endswith('-vol.stc')):
                     fname += '-vl.stc'
-                write_stc(fname, tmin=self.tmin, tstep=self.tstep,
-                          vertices=write_vertices, data=self.data)
+                _write_stc(fname, tmin=self.tmin, tstep=self.tstep,
+                           vertices=write_vertices, data=self.data)
             elif ftype == 'w':
                 logger.info('Writing STC to disk (w format)...')
                 if not (fname.endswith('-vl.w')
                         or fname.endswith('-vol.w')):
                     fname += '-vl.w'
-                write_w(fname, vertices=write_vertices, data=self.data)
+                _write_w(fname, vertices=write_vertices, data=self.data)
 
         logger.info('[done]')
 


Test patch:
diff --git a/mne/tests/test_proj.py b/mne/tests/test_proj.py
--- a/mne/tests/test_proj.py
+++ b/mne/tests/test_proj.py
@@ -3,7 +3,7 @@
 import warnings
 
 import numpy as np
-from numpy.testing import assert_array_almost_equal
+from numpy.testing import assert_array_almost_equal, assert_allclose
 
 import copy as cp
 
@@ -13,7 +13,7 @@
 from mne import compute_proj_epochs, compute_proj_evoked, compute_proj_raw
 from mne.fiff.proj import make_projector, activate_proj
 from mne.proj import read_proj, write_proj
-from mne import read_events, Epochs, sensitivity_map
+from mne import read_events, Epochs, sensitivity_map, read_source_estimate
 from mne.utils import _TempDir
 
 base_dir = op.join(op.dirname(__file__), '..', 'fiff', 'tests', 'data')
@@ -35,62 +35,36 @@
 def test_sensitivity_maps():
     """Test sensitivity map computation"""
     fwd = mne.read_forward_solution(fwd_fname, surf_ori=True)
-    projs = None
     proj_eog = read_proj(eog_fname)
     decim = 6
     for ch_type in ['eeg', 'grad', 'mag']:
-        w_lh = mne.read_w(sensmap_fname % (ch_type, 'lh'))
-        w_rh = mne.read_w(sensmap_fname % (ch_type, 'rh'))
-        w = np.r_[w_lh['data'], w_rh['data']]
-        stc = sensitivity_map(fwd, projs=projs, ch_type=ch_type,
+        w = read_source_estimate(sensmap_fname % (ch_type, 'lh')).data
+        stc = sensitivity_map(fwd, projs=None, ch_type=ch_type,
                               mode='free', exclude='bads')
-        assert_array_almost_equal(stc.data.ravel(), w, decim)
+        assert_array_almost_equal(stc.data, w, decim)
         assert_true(stc.subject == 'sample')
         # let's just make sure the others run
         if ch_type == 'grad':
-            # fixed
-            w_lh = mne.read_w(sensmap_fname % (ch_type, '2-lh'))
-            w_rh = mne.read_w(sensmap_fname % (ch_type, '2-rh'))
-            w = np.r_[w_lh['data'], w_rh['data']]
-            stc = sensitivity_map(fwd, projs=projs, mode='fixed',
+            # fixed (2)
+            w = read_source_estimate(sensmap_fname % (ch_type, '2-lh')).data
+            stc = sensitivity_map(fwd, projs=None, mode='fixed',
                                   ch_type=ch_type, exclude='bads')
-            assert_array_almost_equal(stc.data.ravel(), w, decim)
+            assert_array_almost_equal(stc.data, w, decim)
         if ch_type == 'mag':
-            # ratio
-            w_lh = mne.read_w(sensmap_fname % (ch_type, '3-lh'))
-            w_rh = mne.read_w(sensmap_fname % (ch_type, '3-rh'))
-            w = np.r_[w_lh['data'], w_rh['data']]
-            stc = sensitivity_map(fwd, projs=projs, mode='ratio',
+            # ratio (3)
+            w = read_source_estimate(sensmap_fname % (ch_type, '3-lh')).data
+            stc = sensitivity_map(fwd, projs=None, mode='ratio',
                                   ch_type=ch_type, exclude='bads')
-            assert_array_almost_equal(stc.data.ravel(), w, decim)
+            assert_array_almost_equal(stc.data, w, decim)
         if ch_type == 'eeg':
-            # radiality (4)
-            w_lh = mne.read_w(sensmap_fname % (ch_type, '4-lh'))
-            w_rh = mne.read_w(sensmap_fname % (ch_type, '4-rh'))
-            w = np.r_[w_lh['data'], w_rh['data']]
-            stc = sensitivity_map(fwd, projs=projs, mode='radiality',
-                                  ch_type=ch_type, exclude='bads')
-            # angle (5)
-            w_lh = mne.read_w(sensmap_fname % (ch_type, '5-lh'))
-            w_rh = mne.read_w(sensmap_fname % (ch_type, '5-rh'))
-            w = np.r_[w_lh['data'], w_rh['data']]
-            stc = sensitivity_map(fwd, projs=proj_eog, mode='angle',
-                                  ch_type=ch_type, exclude='bads')
-            assert_array_almost_equal(stc.data.ravel(), w, decim)
-            # remaining (6)
-            w_lh = mne.read_w(sensmap_fname % (ch_type, '6-lh'))
-            w_rh = mne.read_w(sensmap_fname % (ch_type, '6-rh'))
-            w = np.r_[w_lh['data'], w_rh['data']]
-            stc = sensitivity_map(fwd, projs=proj_eog, mode='remaining',
-                                  ch_type=ch_type, exclude='bads')
-            assert_array_almost_equal(stc.data.ravel(), w, decim)
-            # dampening (7)
-            w_lh = mne.read_w(sensmap_fname % (ch_type, '7-lh'))
-            w_rh = mne.read_w(sensmap_fname % (ch_type, '7-rh'))
-            w = np.r_[w_lh['data'], w_rh['data']]
-            stc = sensitivity_map(fwd, projs=proj_eog, mode='dampening',
-                                  ch_type=ch_type, exclude='bads')
-            assert_array_almost_equal(stc.data.ravel(), w, decim)
+            # radiality (4), angle (5), remaining (6), and  dampening (7)
+            modes = ['radiality', 'angle', 'remaining', 'dampening']
+            ends = ['4-lh', '5-lh', '6-lh', '7-lh']
+            for mode, end in zip(modes, ends):
+                w = read_source_estimate(sensmap_fname % (ch_type, end)).data
+                stc = sensitivity_map(fwd, projs=proj_eog, mode=mode,
+                                      ch_type=ch_type, exclude='bads')
+                assert_array_almost_equal(stc.data, w, decim)
 
 
 def test_compute_proj_epochs():
@@ -145,13 +119,14 @@ def test_compute_proj_epochs():
     projs = read_proj(proj_fname)
 
     projs_evoked = compute_proj_evoked(evoked, n_grad=1, n_mag=1, n_eeg=0)
+    assert_true(len(projs_evoked) == 2)
     # XXX : test something
 
     # test parallelization
     projs = compute_proj_epochs(epochs, n_grad=1, n_mag=1, n_eeg=0, n_jobs=2)
     projs = activate_proj(projs)
     proj_par, _, _ = make_projector(projs, epochs.ch_names, bads=[])
-    assert_true(np.allclose(proj, proj_par, rtol=1e-8, atol=1e-16))
+    assert_allclose(proj, proj_par, rtol=1e-8, atol=1e-16)
 
 
 def test_compute_proj_raw():
diff --git a/mne/tests/test_source_estimate.py b/mne/tests/test_source_estimate.py
--- a/mne/tests/test_source_estimate.py
+++ b/mne/tests/test_source_estimate.py
@@ -4,15 +4,14 @@
 from copy import deepcopy
 
 import numpy as np
-from numpy.testing import assert_array_almost_equal, assert_array_equal,\
+from numpy.testing import assert_array_almost_equal, assert_array_equal, \
                           assert_allclose
 
 from scipy.fftpack import fft
 
 from mne.datasets import sample
 from mne import stats, SourceEstimate, Label
-from mne import read_stc, write_stc, read_source_estimate, morph_data,\
-                extract_label_time_course
+from mne import read_source_estimate, morph_data, extract_label_time_course
 from mne.source_estimate import spatio_temporal_tris_connectivity, \
                                 spatio_temporal_src_connectivity, \
                                 compute_morph_matrix, grade_to_vertices, \
@@ -85,16 +84,16 @@ def test_expand():
 def test_io_stc():
     """Test IO for STC files
     """
-    stc = read_stc(fname)
-
-    write_stc(op.join(tempdir, "tmp.stc"), stc['tmin'], stc['tstep'],
-              stc['vertices'], stc['data'])
-    stc2 = read_stc(op.join(tempdir, "tmp.stc"))
-
-    assert_array_almost_equal(stc['data'], stc2['data'])
-    assert_array_almost_equal(stc['tmin'], stc2['tmin'])
-    assert_array_almost_equal(stc['vertices'], stc2['vertices'])
-    assert_array_almost_equal(stc['tstep'], stc2['tstep'])
+    stc = read_source_estimate(fname)
+    stc.save(op.join(tempdir, "tmp.stc"))
+    stc2 = read_source_estimate(op.join(tempdir, "tmp.stc"))
+
+    assert_array_almost_equal(stc.data, stc2.data)
+    assert_array_almost_equal(stc.tmin, stc2.tmin)
+    assert_true(len(stc.vertno) == len(stc2.vertno))
+    for v1, v2 in zip(stc.vertno, stc2.vertno):
+        assert_array_almost_equal(v1, v2)
+    assert_array_almost_equal(stc.tstep, stc2.tstep)
 
 
 def test_io_w():