Repository: LSSTDESC/sacc

Issue:
Add method to remove tracer (and the combinations that involve it)
I would find very convenient to have a method to remove a tracer and its tracer combinations. At this moment the only way I have found is:
1. loop over all the tracer combinations,  check if the selected tracer is present and then remove the combination with `s.remove_selection(tracers=tracers)`. 
2. Then, remove the tracer with `del s.tracers['tracer_to_be_removed']`

If there is a better way of doing this, I have missed it.

Discussion:

Patch:
diff --git a/sacc/sacc.py b/sacc/sacc.py
index 4242d03..f20fffc 100644
--- a/sacc/sacc.py
+++ b/sacc/sacc.py
@@ -641,6 +641,46 @@ class Sacc:
         indices = self.indices(data_type=data_type)
         return unique_list(self.data[i].tracers for i in indices)
 
+    def remove_tracers(self, names):
+        """
+        Remove the tracer objects and their associated data points
+
+        Parameters
+        -----------
+        names: list
+            A list of string names of the tracers to be removed
+
+        """
+
+        for trs in self.get_tracer_combinations():
+            for tri in trs:
+                if tri in names:
+                    self.remove_selection(tracers=trs)
+
+        for name in names:
+            del self.tracers[name]
+
+    def keep_tracers(self, names):
+        """
+        Keep only the tracer objects and their associated data points.
+
+        Parameters
+        -----------
+        names: list
+            A list of string names of the tracers to be kept
+
+        """
+
+        for trs in self.get_tracer_combinations():
+            for tri in trs:
+                if tri not in names:
+                    self.remove_selection(tracers=trs)
+
+        trs_names = list(self.tracers.keys())
+        for name in trs_names:
+            if name not in names:
+                del self.tracers[name]
+
     def rename_tracer(self, name, new_name):
         """
         Get the tracer object with the given name


Test patch:
diff --git a/test/test_sacc2.py b/test/test_sacc2.py
index 9cc600e..74bfde3 100644
--- a/test/test_sacc2.py
+++ b/test/test_sacc2.py
@@ -39,6 +39,8 @@ def get_filled_sacc():
     s.add_tracer('NZ', 'source_0', z, nz)
     s.add_tracer('NZ', 'source_1', z, nz,
                  quantity='cluster_density')
+    s.add_tracer('NZ', 'source_2', z, nz,
+                 quantity='cluster_density')
 
     for i in range(20):
         ee = 0.1 * i
@@ -55,6 +57,11 @@ def get_filled_sacc():
         tracers = ('source_1', 'source_1', 'source_0')
         s.add_data_point(sacc.standard_types.galaxy_shear_cl_ee,
                          tracers, ee, ell=10.0*i)
+    for i in range(20):
+        bb = 0.4 * i
+        tracers = ('source_2', 'source_2')
+        s.add_data_point(sacc.standard_types.galaxy_shear_cl_bb,
+                         tracers, bb, ell=10.0*i)
 
     return s
 
@@ -400,6 +407,18 @@ def test_keep_remove():
     ind = s2.indices(tracers=('source_2', 'source_2'), ell__lt=45)
     assert len(ind) == 5
 
+def test_remove_keep_tracers():
+    s = get_filled_sacc()
+
+    s.remove_tracers(['source_0'])
+
+    assert ['source_1', 'source_2'] == list(s.tracers.keys())
+    assert [('source_2', 'source_2')] == s.get_tracer_combinations()
+
+    s = get_filled_sacc()
+    s.keep_tracers(['source_0'])
+    assert ['source_0'] == list(s.tracers.keys())
+    assert [('source_0',)] == s.get_tracer_combinations()
 
 def test_cutting_block_cov():
     covmat = [np.random.uniform(size=(50, 50)),