Repository: bids-standard/pybids

Issue:
First line omitted when using get_df() method of BIDSDataFile object
Hi,

The method `get_df() `of the BIDSDataFile object omits the first line of the data file. This issue arises when calling the function `read_csv()` of Pandas, as header is set to 0 if no names are passed:

```
self.data = pd.read_csv(self.path, sep='\t', na_values='n/a',
                                dtype=dtype)
```

This behaviour can be problematic if one is working with the physiological recording data, since they  don't have a header line and therefore the first observation would be always lost. 

This could be solved by modifying the piece of code above as follows:

```
if self.entities['suffix'] == 'physio' or 'stim':
    self.data = pd.read_csv(self.path, sep='\t', na_values='n/a', 
                            header=None, dtype=dtype)
else:
    self.data = pd.read_csv(self.path, sep='\t', na_values='n/a',
                            dtype=dtype)
```

Does this modification make sense?


Discussion:

Patch:
diff --git a/bids/layout/models.py b/bids/layout/models.py
index 766edd14..cd39426f 100644
--- a/bids/layout/models.py
+++ b/bids/layout/models.py
@@ -309,7 +309,7 @@ class BIDSDataFile(BIDSFile):
     }
 
     def get_df(self, include_timing=True, adjust_onset=False,
-               enforce_dtypes=True):
+               enforce_dtypes=True, **pd_args):
         """Return the contents of a tsv file as a pandas DataFrame.
 
         Parameters
@@ -326,6 +326,8 @@ class BIDSDataFile(BIDSFile):
             If True, enforces the data types defined in
             the BIDS spec on core columns (e.g., subject_id and session_id
             must be represented as strings).
+        pd_args : dict
+            Optional keyword arguments to pass onto pd.read_csv().
 
         Returns
         -------
@@ -347,8 +349,10 @@ class BIDSDataFile(BIDSFile):
         # TODO: memoize this for efficiency. (Note: caching is insufficient,
         # because the dtype enforcement will break if we ignore the value of
         # enforce_dtypes).
+        suffix = self.entities['suffix']
+        header = None if suffix in {'physio', 'stim'} else 'infer'
         self.data = pd.read_csv(self.path, sep='\t', na_values='n/a',
-                                dtype=dtype)
+                                dtype=dtype, header=header, **pd_args)
 
         data = self.data.copy()
 


Test patch:
diff --git a/bids/layout/tests/test_models.py b/bids/layout/tests/test_models.py
index b6a15e73..770999fd 100644
--- a/bids/layout/tests/test_models.py
+++ b/bids/layout/tests/test_models.py
@@ -176,7 +176,7 @@ def test_bidsfile_get_df_from_tsv_gz(layout_synthetic):
     df1 = bf.get_df()
     df2 = bf.get_df(include_timing=True)
     assert df1.equals(df2)
-    assert df1.shape == (1599, 3)
+    assert df1.shape == (1600, 3)
     assert set(df1.columns) == {'onset', 'respiratory', 'cardiac'}
     assert df1.iloc[0, 0] == 0.
     assert df1.iloc[1, 0] - df1.iloc[0, 0] == 0.1
@@ -190,6 +190,7 @@ def test_bidsfile_get_df_from_tsv_gz(layout_synthetic):
 def test_bidsdatafile_enforces_dtype(layout_synthetic):
     bf = layout_synthetic.get(suffix='participants', extension='tsv')[0]
     df = bf.get_df(enforce_dtypes=False)
+    assert df.shape[0] == 5
     assert df.loc[:, 'subject_id'].dtype == int
     assert df.loc[:, 'subject_id'][0] == 1
     df = bf.get_df(enforce_dtypes=True)