Skip to content

Commit df7d9a6

Browse files
committed
Merge pull request #829 from oesteban/enh/AddCSVRow
AddCSVRow interface
2 parents af406fe + 764eab6 commit df7d9a6

File tree

4 files changed

+150
-3
lines changed

4 files changed

+150
-3
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Next Release
22
============
33

4+
* ENH: New miscelaneous interface: AddCSVRow
45
* ENH: FUGUE interface has been refactored to use the name_template system, 3 examples
56
added to doctests, some bugs solved.
67
* ENH: Added new interfaces (fsl.utils.WarpUtils, ConvertWarp) to fnirtfileutils and convertwarp

nipype/algorithms/misc.py

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@
2626
import itertools
2727
import scipy.stats as stats
2828

29-
from .. import logging
29+
from nipype import logging
3030

3131
import warnings
3232

3333
import metrics as nam
3434
from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File,
3535
InputMultiPath, OutputMultiPath,
36-
BaseInterfaceInputSpec, isdefined)
37-
from ..utils.filemanip import fname_presuffix, split_filename
36+
BaseInterfaceInputSpec, isdefined,
37+
DynamicTraitedSpec )
38+
from nipype.utils.filemanip import fname_presuffix, split_filename
3839
iflogger = logging.getLogger('interface')
3940

4041

@@ -782,6 +783,121 @@ def _list_outputs(self):
782783
return outputs
783784

784785

786+
class AddCSVRowInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):
787+
in_file = traits.File(mandatory=True, desc='Input comma-separated value (CSV) files')
788+
_outputs = traits.Dict( traits.Any, value={}, usedefault=True )
789+
790+
def __setattr__(self, key, value):
791+
if key not in self.copyable_trait_names():
792+
if not isdefined(value):
793+
super(AddCSVRowInputSpec, self).__setattr__(key, value)
794+
self._outputs[key] = value
795+
else:
796+
if key in self._outputs:
797+
self._outputs[key] = value
798+
super(AddCSVRowInputSpec, self).__setattr__(key, value)
799+
800+
class AddCSVRowOutputSpec(TraitedSpec):
801+
csv_file = File(desc='Output CSV file containing rows ')
802+
803+
class AddCSVRow(BaseInterface):
804+
"""Simple interface to add an extra row to a csv file
805+
806+
.. note:: Requires `pandas <http://pandas.pydata.org/>`_
807+
808+
.. warning:: Multi-platform thread-safe execution is possible with
809+
`lockfile <https://pythonhosted.org/lockfile/lockfile.html>`_. Please recall that (1)
810+
this module is alpha software; and (2) it should be installed for thread-safe writing.
811+
If lockfile is not installed, then the interface is not thread-safe.
812+
813+
814+
Example
815+
-------
816+
817+
>>> import nipype.algorithms.misc as misc
818+
>>> addrow = misc.AddCSVRow()
819+
>>> addrow.inputs.in_file = 'scores.csv'
820+
>>> addrow.inputs.si = 0.74
821+
>>> addrow.inputs.di = 0.93
822+
>>> addrow.subject_id = 'S400'
823+
>>> addrow.inputs.list_of_values = [ 0.4, 0.7, 0.3 ]
824+
>>> addrow.run() # doctest: +SKIP
825+
"""
826+
input_spec = AddCSVRowInputSpec
827+
output_spec = AddCSVRowOutputSpec
828+
829+
def __init__(self, infields=None, force_run=True, **kwargs):
830+
super(AddCSVRow, self).__init__(**kwargs)
831+
undefined_traits = {}
832+
self._infields = infields
833+
self._have_lock = False
834+
self._lock = None
835+
836+
if infields:
837+
for key in infields:
838+
self.inputs.add_trait(key, traits.Any)
839+
self.inputs._outputs[key] = Undefined
840+
undefined_traits[key] = Undefined
841+
self.inputs.trait_set(trait_change_notify=False, **undefined_traits)
842+
843+
if force_run:
844+
self._always_run = True
845+
846+
def _run_interface(self, runtime):
847+
try:
848+
import pandas as pd
849+
except ImportError:
850+
raise ImportError('This interface requires pandas (http://pandas.pydata.org/) to run.')
851+
852+
try:
853+
import lockfile as pl
854+
self._have_lock = True
855+
except ImportError:
856+
import warnings
857+
warnings.warn(('Python module lockfile was not found: AddCSVRow will not be thread-safe '
858+
'in multi-processor execution'))
859+
860+
input_dict = {}
861+
for key, val in self.inputs._outputs.items():
862+
# expand lists to several columns
863+
if isinstance(val, list):
864+
for i,v in enumerate(val):
865+
input_dict['%s_%d' % (key,i)]=v
866+
else:
867+
input_dict[key] = val
868+
869+
df = pd.DataFrame([input_dict])
870+
871+
if self._have_lock:
872+
self._lock = pl.FileLock(self.inputs.in_file)
873+
874+
# Acquire lock
875+
self._lock.acquire()
876+
877+
if op.exists(self.inputs.in_file):
878+
formerdf = pd.read_csv(self.inputs.in_file, index_col=0)
879+
df = pd.concat([formerdf, df], ignore_index=True )
880+
881+
with open(self.inputs.in_file, 'w') as f:
882+
df.to_csv(f)
883+
884+
if self._have_lock:
885+
self._lock.release()
886+
887+
return runtime
888+
889+
def _list_outputs(self):
890+
outputs = self.output_spec().get()
891+
outputs['csv_file'] = self.inputs.in_file
892+
return outputs
893+
894+
def _outputs(self):
895+
return self._add_output_traits(super(AddCSVRow, self)._outputs())
896+
897+
def _add_output_traits(self, base):
898+
return base
899+
900+
785901
class CalculateNormalizedMomentsInputSpec(TraitedSpec):
786902
timeseries_file = File(
787903
exists=True, mandatory=True,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from nipype.testing import assert_equal
3+
from nipype.algorithms.misc import AddCSVRow
4+
5+
def test_AddCSVRow_inputs():
6+
input_map = dict(_outputs=dict(usedefault=True,
7+
),
8+
ignore_exception=dict(nohash=True,
9+
usedefault=True,
10+
),
11+
in_file=dict(mandatory=True,
12+
),
13+
)
14+
inputs = AddCSVRow.input_spec()
15+
16+
for key, metadata in input_map.items():
17+
for metakey, value in metadata.items():
18+
yield assert_equal, getattr(inputs.traits()[key], metakey), value
19+
20+
def test_AddCSVRow_outputs():
21+
output_map = dict(csv_file=dict(),
22+
)
23+
outputs = AddCSVRow.output_spec()
24+
25+
for key, metadata in output_map.items():
26+
for metakey, value in metadata.items():
27+
yield assert_equal, getattr(outputs.traits()[key], metakey), value
28+

nipype/pipeline/engine.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def _check_outputs(self, parameter):
190190
return hasattr(self.outputs, parameter)
191191

192192
def _check_inputs(self, parameter):
193+
if isinstance(self.inputs, DynamicTraitedSpec):
194+
return True
193195
return hasattr(self.inputs, parameter)
194196

195197
def _verify_name(self, name):

0 commit comments

Comments
 (0)