Browse Source

format code with black

https://github.com/fphammerle/freesurfer-stats/pull/11
Fabian Peter Hammerle 1 month ago
parent
commit
aef10c88f4
8 changed files with 380 additions and 295 deletions
  1. 15 0
      .github/workflows/code-format.yml
  2. 0 2
      .style.yapf
  3. 3 1
      Pipfile
  4. 22 30
      Pipfile.lock
  5. 2 0
      README.rst
  6. 45 35
      freesurfer_stats/__init__.py
  7. 23 28
      setup.py
  8. 270 199
      tests/test_cortical_parcellation_stats.py

+ 15 - 0
.github/workflows/code-format.yml

@@ -0,0 +1,15 @@
+on:
+  push:
+  pull_request:
+
+jobs:
+  build:
+    runs-on: ubuntu-18.04
+    steps:
+    - uses: actions/checkout@v1
+    - uses: actions/setup-python@v1
+      with:
+        python-version: 3.7
+    - run: pip install black==19.10b0
+    - run: pip freeze
+    - run: black --check .

+ 0 - 2
.style.yapf

@@ -1,2 +0,0 @@
-[style]
-based_on_style = pep8

+ 3 - 1
Pipfile

@@ -10,6 +10,9 @@ freesurfer-stats = {editable = true, path = "."}
 pandas = "<1"
 
 [dev-packages]
+# black requires python>=3.6
+# https://github.com/psf/black/commit/e74117f172e29e8a980e2c9de929ad50d3769150#diff-2eeaed663bd0d25b7e608891384b7298R51
+#black = "==19.10b0"
 isort = "*"
 # >=2.3.0 due to false+ assignment-from-no-return
 # https://github.com/PyCQA/pylint/issues/2694
@@ -18,7 +21,6 @@ pylint-import-requirements = "<3"
 pytest = "<5"
 # >=2 for --cov-fail-under
 pytest-cov = "<3,>=2"
-yapf = "*"
 # v2.0.0 dropped python<3.6 support
 # https://github.com/jaraco/zipp/commit/05a3c52b4d41690e0471a2e283cffb500dc0329a
 zipp = "<2"

+ 22 - 30
Pipfile.lock

@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "92527a78f94a76d103e9dd259d1470c4dc10b441a4192a92c6f1d59109ba1cff"
+            "sha256": "2a979d1b7c6b04e20ac4e271ea948c9429ac4ecf45fe306b6e0c898756c6bb33"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -87,10 +87,10 @@
         },
         "six": {
             "hashes": [
-                "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
-                "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
+                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
+                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
             ],
-            "version": "==1.14.0"
+            "version": "==1.15.0"
         }
     },
     "develop": {
@@ -202,18 +202,18 @@
         },
         "more-itertools": {
             "hashes": [
-                "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c",
-                "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"
+                "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be",
+                "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"
             ],
             "markers": "python_version > '2.7'",
-            "version": "==8.2.0"
+            "version": "==8.3.0"
         },
         "packaging": {
             "hashes": [
-                "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3",
-                "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"
+                "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
+                "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
             ],
-            "version": "==20.3"
+            "version": "==20.4"
         },
         "pluggy": {
             "hashes": [
@@ -254,33 +254,33 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339",
-                "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"
+                "sha256:0031b5021e1204f12e5a044500fbf31e3f64c6f2d69ded910af003abd0d91ae4",
+                "sha256:a24f6d11abff602da7b8aa33520734d604d25c4f070222e23667bb9782ba763e"
             ],
             "index": "pypi",
-            "version": "==4.6.9"
+            "version": "==4.6.10"
         },
         "pytest-cov": {
             "hashes": [
-                "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b",
-                "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"
+                "sha256:b6a814b8ed6247bd81ff47f038511b57fe1ce7f4cc25b9106f1a4b106f1d9322",
+                "sha256:c87dfd8465d865655a8213859f1b4749b43448b5fae465cb981e16d52a811424"
             ],
             "index": "pypi",
-            "version": "==2.8.1"
+            "version": "==2.9.0"
         },
         "six": {
             "hashes": [
-                "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
-                "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
+                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
+                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
             ],
-            "version": "==1.14.0"
+            "version": "==1.15.0"
         },
         "toml": {
             "hashes": [
-                "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
-                "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+                "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+                "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
             ],
-            "version": "==0.10.0"
+            "version": "==0.10.1"
         },
         "typed-ast": {
             "hashes": [
@@ -322,14 +322,6 @@
             ],
             "version": "==1.12.1"
         },
-        "yapf": {
-            "hashes": [
-                "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427",
-                "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"
-            ],
-            "index": "pypi",
-            "version": "==0.30.0"
-        },
         "zipp": {
             "hashes": [
                 "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1",

+ 2 - 0
README.rst

@@ -1,6 +1,8 @@
 freesurfer-stats
 ================
 
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+   :target: https://github.com/psf/black
 .. image:: https://travis-ci.org/fphammerle/freesurfer-stats.svg?branch=master
    :target: https://travis-ci.org/fphammerle/freesurfer-stats
 .. image:: https://coveralls.io/repos/github/fphammerle/freesurfer-stats/badge.svg?branch=master

+ 45 - 35
freesurfer_stats/__init__.py

@@ -60,34 +60,38 @@ from freesurfer_stats.version import __version__
 
 class CorticalParcellationStats:
 
-    _HEMISPHERE_PREFIX_TO_SIDE = {'lh': 'left', 'rh': 'right'}
+    _HEMISPHERE_PREFIX_TO_SIDE = {"lh": "left", "rh": "right"}
     _GENERAL_MEASUREMENTS_REGEX = re.compile(
-        r'^Measure \S+, ([^,\s]+),? ([^,]+), ([\d\.]+), (\S+)$')
-    _COLUMN_NAMES_NON_SAFE_REGEX = re.compile(r'\s+')
+        r"^Measure \S+, ([^,\s]+),? ([^,]+), ([\d\.]+), (\S+)$"
+    )
+    _COLUMN_NAMES_NON_SAFE_REGEX = re.compile(r"\s+")
 
     def __init__(self):
-        self.headers \
-            = {}  # type: typing.Dict[str, typing.Union[str, datetime.datetime]]
-        self.whole_brain_measurements \
-            = {}  # type: typing.Dict[str, typing.Tuple[float, int]]
-        self.structural_measurements \
-            = {}  # type: typing.Union[pandas.DataFrame, None]
+        self.headers = (
+            {}
+        )  # type: typing.Dict[str, typing.Union[str, datetime.datetime]]
+        self.whole_brain_measurements = (
+            {}
+        )  # type: typing.Dict[str, typing.Tuple[float, int]]
+        self.structural_measurements = {}  # type: typing.Union[pandas.DataFrame, None]
 
     @property
     def hemisphere(self) -> str:
-        return self._HEMISPHERE_PREFIX_TO_SIDE[self.headers['hemi']]
+        return self._HEMISPHERE_PREFIX_TO_SIDE[self.headers["hemi"]]
 
     @staticmethod
     def _read_header_line(stream: typing.TextIO) -> str:
         line = stream.readline()
-        assert line.startswith('# ')
+        assert line.startswith("# ")
         return line[2:].rstrip()
 
     @classmethod
-    def _read_column_header_line(cls, stream: typing.TextIO) -> typing.Tuple[int, str, str]:
+    def _read_column_header_line(
+        cls, stream: typing.TextIO,
+    ) -> typing.Tuple[int, str, str]:
         line = cls._read_header_line(stream)
-        assert line.startswith('TableCol'), line
-        line = line[len('TableCol '):].lstrip()
+        assert line.startswith("TableCol"), line
+        line = line[len("TableCol ") :].lstrip()
         index, key, value = line.split(maxsplit=2)
         return int(index), key, value
 
@@ -95,23 +99,25 @@ class CorticalParcellationStats:
         self.headers = {}
         while True:
             line = self._read_header_line(stream)
-            if line.startswith('Measure'):
+            if line.startswith("Measure"):
                 break
             if line:
                 attr_name, attr_value = line.split(" ", maxsplit=1)
                 attr_value = attr_value.lstrip()
-                if attr_name in ['cvs_version', 'mrisurf.c-cvs_version']:
-                    attr_value = attr_value.strip('$').rstrip()
-                if attr_name == 'CreationTime':
+                if attr_name in ["cvs_version", "mrisurf.c-cvs_version"]:
+                    attr_value = attr_value.strip("$").rstrip()
+                if attr_name == "CreationTime":
                     attr_dt = datetime.datetime.strptime(
-                        attr_value, '%Y/%m/%d-%H:%M:%S-%Z')
+                        attr_value, "%Y/%m/%d-%H:%M:%S-%Z",
+                    )
                     if attr_dt.tzinfo is None:
-                        assert attr_value.endswith('-GMT')
+                        assert attr_value.endswith("-GMT")
                         attr_dt = attr_dt.replace(tzinfo=datetime.timezone.utc)
                     attr_value = attr_dt
-                if attr_name == 'AnnotationFileTimeStamp':
+                if attr_name == "AnnotationFileTimeStamp":
                     attr_value = datetime.datetime.strptime(
-                        attr_value, '%Y/%m/%d %H:%M:%S')
+                        attr_value, "%Y/%m/%d %H:%M:%S",
+                    )
                 self.headers[attr_name] = attr_value
 
     @classmethod
@@ -138,14 +144,14 @@ class CorticalParcellationStats:
         return column_name, pandas.to_numeric([value], errors="raise")
 
     @classmethod
-    def _read_column_attributes(cls, num: int, stream: typing.TextIO) \
-            -> typing.List[typing.Dict[str, str]]:
+    def _read_column_attributes(
+        cls, num: int, stream: typing.TextIO,
+    ) -> typing.List[typing.Dict[str, str]]:
         columns = []
         for column_index in range(1, int(num) + 1):
             column_attrs = {}
             for _ in range(3):
-                column_index_line, key, value \
-                    = cls._read_column_header_line(stream)
+                column_index_line, key, value = cls._read_column_header_line(stream)
                 assert column_index_line == column_index
                 assert key not in column_attrs
                 column_attrs[key] = value
@@ -153,9 +159,11 @@ class CorticalParcellationStats:
         return columns
 
     def _read(self, stream: typing.TextIO) -> None:
-        assert stream.readline().rstrip() \
-            == '# Table of FreeSurfer cortical parcellation anatomical statistics'
-        assert stream.readline().rstrip() == '#'
+        assert (
+            stream.readline().rstrip()
+            == "# Table of FreeSurfer cortical parcellation anatomical statistics"
+        )
+        assert stream.readline().rstrip() == "#"
         self._read_headers(stream)
         self.whole_brain_measurements = pandas.DataFrame()
         line = self._read_header_line(stream)
@@ -172,14 +180,16 @@ class CorticalParcellationStats:
                 assert column_name not in self.whole_brain_measurements, column_name
                 self.whole_brain_measurements[column_name] = value
             line = self._read_header_line(stream)
-        columns = self._read_column_attributes(
-            int(line[len('NTableCols '):]), stream)
-        assert self._read_header_line(stream) \
-            == 'ColHeaders ' + ' '.join(c['ColHeader'] for c in columns)
+        columns = self._read_column_attributes(int(line[len("NTableCols ") :]), stream)
+        assert self._read_header_line(stream) == "ColHeaders " + " ".join(
+            c["ColHeader"] for c in columns
+        )
         self.structural_measurements = pandas.DataFrame(
             (line.rstrip().split() for line in stream),
-            columns=[self._format_column_name(c['FieldName'], c['Units']) for c in columns]) \
-            .apply(pandas.to_numeric, errors='ignore')
+            columns=[
+                self._format_column_name(c["FieldName"], c["Units"]) for c in columns
+            ],
+        ).apply(pandas.to_numeric, errors="ignore")
 
     @classmethod
     def read(cls, path: typing.Union[str, pathlib.Path]) -> "CorticalParcellationStats":

+ 23 - 28
setup.py

@@ -19,38 +19,38 @@ import os
 
 import setuptools
 
-with open('README.rst', 'r') as readme:
+with open("README.rst", "r") as readme:
     LONG_DESCRIPTION = readme.read()
 
 setuptools.setup(
     name="freesurfer-stats",
     use_scm_version={
-        'write_to': os.path.join('freesurfer_stats', 'version.py'),
+        "write_to": os.path.join("freesurfer_stats", "version.py"),
         # `version` triggers pylint C0103
-        'write_to_template': "__version__ = '{version}'\n",
+        "write_to_template": "__version__ = '{version}'\n",
     },
     description="Python Library to Read FreeSurfer's cortical parcellation anatomical statistics",
     long_description=LONG_DESCRIPTION,
-    author='Fabian Peter Hammerle',
-    author_email='fabian@hammerle.me',
-    url='https://github.com/fphammerle/freesurfer-stats',
+    author="Fabian Peter Hammerle",
+    author_email="fabian@hammerle.me",
+    url="https://github.com/fphammerle/freesurfer-stats",
     license="GPLv3+",
     keywords=[
-        'anatomy',
-        'aparc',
-        'area',
-        'brain',
-        'cortex',
-        'dataframe',
-        'freesurfer',
-        'mris_anatomical_stats',
-        'neuroimaging',
-        'pandas',
-        'parcellation',
-        'reader',
-        'statistics',
-        'surface',
-        'volume',
+        "anatomy",
+        "aparc",
+        "area",
+        "brain",
+        "cortex",
+        "dataframe",
+        "freesurfer",
+        "mris_anatomical_stats",
+        "neuroimaging",
+        "pandas",
+        "parcellation",
+        "reader",
+        "statistics",
+        "surface",
+        "volume",
     ],
     classifiers=[
         "Development Status :: 4 - Beta",
@@ -72,11 +72,6 @@ setuptools.setup(
         # TODO verify lower version constraint
         "pandas>=0.21,<2",
     ],
-    setup_requires=[
-        'setuptools_scm',
-    ],
-    tests_require=[
-        'pytest-cov<3,>=2',
-        'pytest<5',
-    ],
+    setup_requires=["setuptools_scm"],
+    tests_require=["pytest<5"],
 )

+ 270 - 199
tests/test_cortical_parcellation_stats.py

@@ -30,183 +30,246 @@ from freesurfer_stats import CorticalParcellationStats
 
 
 @pytest.mark.parametrize(
-    ('path', 'headers', 'hemisphere',
-     'whole_brain_measurements', 'structural_measurements_length',
-     'structural_measurements_subset'),
-    [(os.path.join(SUBJECTS_DIR, 'fabian', 'stats', 'lh.aparc.DKTatlas.stats.short'),
-      {'CreationTime': datetime.datetime(2019, 5, 9, 21, 5, 54, tzinfo=datetime.timezone.utc),
-       'generating_program': 'mris_anatomical_stats',
-       'cvs_version': 'Id: mris_anatomical_stats.c,v 1.79 2016/03/14 15:15:34 greve Exp',
-       'mrisurf.c-cvs_version': 'Id: mrisurf.c,v 1.781.2.6 2016/12/27 16:47:14 zkaufman Exp',
-       'cmdline': 'mris_anatomical_stats -th3 -mgz -cortex ../label/lh.cortex.label'
-                  ' -f ../stats/lh.aparc.DKTatlas.stats -b -a ../label/lh.aparc.DKTatlas.annot'
-                  ' -c ../label/aparc.annot.DKTatlas.ctab fabian lh white',
-       'sysname': 'Linux',
-       'hostname': 'another-hostname',
-       'machine': 'x86_64',
-       'user': 'some-username',
-       'SUBJECTS_DIR': '/home/some-username/freesurfer-subjects',
-       'anatomy_type': 'surface',
-       'subjectname': 'fabian',
-       'hemi': 'lh',
-       'AnnotationFile': '../label/lh.aparc.DKTatlas.annot',
-       'AnnotationFileTimeStamp': datetime.datetime(2019, 5, 9, 23, 5, 40)},
-      'left',
-      {'white_surface_total_area_mm^2': 98553,
-       'mean_thickness_mm': 2.50462,
-       'brain_segmentation_volume_mm^3': 1327432.000000,
-       'brain_segmentation_volume_without_ventricles_mm^3': 1316285.000000,
-       'brain_segmentation_volume_without_ventricles_from_surf_mm^3': 1315572.548920,
-       'total_cortical_gray_matter_volume_mm^3': 553998.311189,
-       'supratentorial_volume_mm^3': 1172669.548920,
-       'supratentorial_volume_without_ventricles_mm^3': 1164180.548920,
-       'estimated_total_intracranial_volume_mm^3': 1670487.274486},
-      3,
-      [{'structure_name': 'caudalanteriorcingulate',
-        'number_of_vertices': 2061,
-        'surface_area_mm^2': 1472,
-        'gray_matter_volume_mm^3': 4258,
-        'average_thickness_mm': 2.653,
-        'thickness_stddev_mm': 0.644,
-        'integrated_rectified_mean_curvature_mm^-1': 0.135,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.020,
-        'folding_index': 27,
-        'intrinsic_curvature_index': 1.6},
-       {'structure_name': 'caudalmiddlefrontal',
-        'number_of_vertices': 4451,
-        'surface_area_mm^2': 3039,
-        'gray_matter_volume_mm^3': 8239,
-        'average_thickness_mm': 2.456,
-        'thickness_stddev_mm': 0.486,
-        'integrated_rectified_mean_curvature_mm^-1': 0.116,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.020,
-        'folding_index': 42,
-        'intrinsic_curvature_index': 3.7},
-       {'structure_name': 'insula',
-        'number_of_vertices': 3439,
-        'surface_area_mm^2': 2304,
-        'gray_matter_volume_mm^3': 7594,
-        'average_thickness_mm': 3.193,
-        'thickness_stddev_mm': 0.620,
-        'integrated_rectified_mean_curvature_mm^-1': 0.116,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.027,
-        'folding_index': 33,
-        'intrinsic_curvature_index': 3.5}]),
-     (os.path.join(
-         SUBJECTS_DIR, 'fabian', 'stats', 'rh.aparc.pial.stats.short'),
-      {'CreationTime': datetime.datetime(2019, 5, 9, 21, 3, 42, tzinfo=datetime.timezone.utc),
-       'generating_program': 'mris_anatomical_stats',
-       'cvs_version': 'Id: mris_anatomical_stats.c,v 1.79 2016/03/14 15:15:34 greve Exp',
-       'mrisurf.c-cvs_version': 'Id: mrisurf.c,v 1.781.2.6 2016/12/27 16:47:14 zkaufman Exp',
-       'cmdline': 'mris_anatomical_stats -th3 -mgz -cortex ../label/rh.cortex.label'
-                  ' -f ../stats/rh.aparc.pial.stats -b -a ../label/rh.aparc.annot'
-                  ' -c ../label/aparc.annot.ctab fabian rh pial',
-       'sysname': 'Linux',
-       'hostname': 'some-hostname',
-       'machine': 'x86_64',
-       'user': 'some-username',
-       'SUBJECTS_DIR': '/home/some-username/freesurfer-subjects',
-       'anatomy_type': 'surface',
-       'subjectname': 'fabian',
-       'hemi': 'rh',
-       'AnnotationFile': '../label/rh.aparc.annot',
-       'AnnotationFileTimeStamp': datetime.datetime(2019, 5, 9, 22, 27, 28)},
-      'right',
-      {'pial_surface_total_area_mm^2': 121260,
-       'mean_thickness_mm': 2.4817,
-       'brain_segmentation_volume_mm^3': 1327432.000000,
-       'brain_segmentation_volume_without_ventricles_mm^3': 1316285.000000,
-       'brain_segmentation_volume_without_ventricles_from_surf_mm^3': 1315572.548920,
-       'total_cortical_gray_matter_volume_mm^3': 553998.311189,
-       'supratentorial_volume_mm^3': 1172669.548920,
-       'supratentorial_volume_without_ventricles_mm^3': 1164180.548920,
-       'estimated_total_intracranial_volume_mm^3': 1670487.274486},
-      2,
-      [{'structure_name': 'bankssts',
-        'number_of_vertices': 1344,
-        'surface_area_mm^2': 825,
-        'gray_matter_volume_mm^3': 2171,
-        'average_thickness_mm': 2.436,
-        'thickness_stddev_mm': 0.381,
-        'integrated_rectified_mean_curvature_mm^-1': 0.115,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.028,
-        'folding_index': 19,
-        'intrinsic_curvature_index': 1.7},
-       {'structure_name': 'transversetemporal',
-        'number_of_vertices': 651,
-        'surface_area_mm^2': 545,
-        'gray_matter_volume_mm^3': 1061,
-        'average_thickness_mm': 2.251,
-        'thickness_stddev_mm': 0.317,
-        'integrated_rectified_mean_curvature_mm^-1': 0.110,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.021,
-        'folding_index': 3,
-        'intrinsic_curvature_index': 0.6}]),
-     (os.path.join(
-         SUBJECTS_DIR, 'soichi', 'stats', 'rh.aparc.stats'),
-      {'CreationTime': datetime.datetime(2020, 5, 4, 22, 20, 26, tzinfo=datetime.timezone.utc),
-       'AnnotationFileTimeStamp': datetime.datetime(2020, 5, 4, 21, 58, 13),
-       'AnnotationFile': '../label/rh.aparc.annot',
-       'SUBJECTS_DIR': '/N/dc2/scratch/hayashis/bigred3-workflows'
-                       '/5eb0689676c10ead933d673c/5eb068b076c10e7b013d673f',
-       'anatomy_type': 'surface',
-       'cmdline': 'mris_anatomical_stats -th3 -mgz -cortex ../label/rh.cortex.label '
-                  '-f ../stats/rh.aparc.stats -b -a ../label/rh.aparc.annot -c '
-                  '../label/aparc.annot.ctab output rh white',
-       'cvs_version': '7.0.0',
-       'generating_program': 'mris_anatomical_stats',
-       'hemi': 'rh',
-       'hostname': 'nid00762',
-       'machine': 'x86_64',
-       'mrisurf.c-cvs_version': '7.0.0',
-       'subjectname': 'output',
-       'sysname': 'Linux',
-       'user': 'hayashis',
-       'BrainVolStatsFixed': 'NotNeeded because voxelvolume=1mm3'},
-      'right',
-      {'white_surface_total_area_mm^2': 83579.2,
-       'mean_thickness_mm': 2.35815,
-       'brain_segmentation_volume_mm^3': 1169408.0,
-       'brain_segmentation_volume_without_ventricles_mm^3': 1157593.0,
-       'brain_segmentation_volume_without_ventricles_from_surf_mm^3': 1157593.0,
-       'total_cortical_gray_matter_volume_mm^3': 454587.696158,
-       'supratentorial_volume_mm^3': 1023873.0,
-       'supratentorial_volume_without_ventricles_mm^3': 1012058.0,
-       'estimated_total_intracranial_volume_mm^3': 1420434.160521},
-      34,
-      [{'structure_name': 'bankssts',
-        'number_of_vertices': 1094,
-        'surface_area_mm^2': 757,
-        'gray_matter_volume_mm^3': 1725,
-        'average_thickness_mm': 2.215,
-        'thickness_stddev_mm': 0.544,
-        'integrated_rectified_mean_curvature_mm^-1': 0.109,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.025,
-        'folding_index': 9,
-        'intrinsic_curvature_index': 1.1},
-       {'structure_name': 'caudalanteriorcingulate',
-        'number_of_vertices': 1137,
-        'surface_area_mm^2': 780,
-        'gray_matter_volume_mm^3': 2327,
-        'average_thickness_mm': 2.842,
-        'thickness_stddev_mm': 0.667,
-        'integrated_rectified_mean_curvature_mm^-1': 0.116,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.021,
-        'folding_index': 11,
-        'intrinsic_curvature_index': 1.0},
-       {'structure_name': 'caudalmiddlefrontal',
-        'number_of_vertices': 3126,
-        'surface_area_mm^2': 2218,
-        'gray_matter_volume_mm^3': 5978,
-        'average_thickness_mm': 2.447,
-        'thickness_stddev_mm': 0.605,
-        'integrated_rectified_mean_curvature_mm^-1': 0.122,
-        'integrated_rectified_gaussian_curvature_mm^-2': 0.024,
-        'folding_index': 28,
-        'intrinsic_curvature_index': 3.1}])],
+    (
+        "path",
+        "headers",
+        "hemisphere",
+        "whole_brain_measurements",
+        "structural_measurements_length",
+        "structural_measurements_subset",
+    ),
+    [
+        (
+            os.path.join(
+                SUBJECTS_DIR, "fabian", "stats", "lh.aparc.DKTatlas.stats.short"
+            ),
+            {
+                "CreationTime": datetime.datetime(
+                    2019, 5, 9, 21, 5, 54, tzinfo=datetime.timezone.utc
+                ),
+                "generating_program": "mris_anatomical_stats",
+                "cvs_version": "Id: mris_anatomical_stats.c,v 1.79"
+                " 2016/03/14 15:15:34 greve Exp",
+                "mrisurf.c-cvs_version": "Id: mrisurf.c,v 1.781.2.6"
+                " 2016/12/27 16:47:14 zkaufman Exp",
+                "cmdline": "mris_anatomical_stats -th3 -mgz -cortex ../label/lh.cortex.label"
+                " -f ../stats/lh.aparc.DKTatlas.stats -b -a ../label/lh.aparc.DKTatlas.annot"
+                " -c ../label/aparc.annot.DKTatlas.ctab fabian lh white",
+                "sysname": "Linux",
+                "hostname": "another-hostname",
+                "machine": "x86_64",
+                "user": "some-username",
+                "SUBJECTS_DIR": "/home/some-username/freesurfer-subjects",
+                "anatomy_type": "surface",
+                "subjectname": "fabian",
+                "hemi": "lh",
+                "AnnotationFile": "../label/lh.aparc.DKTatlas.annot",
+                "AnnotationFileTimeStamp": datetime.datetime(2019, 5, 9, 23, 5, 40),
+            },
+            "left",
+            {
+                "white_surface_total_area_mm^2": 98553,
+                "mean_thickness_mm": 2.50462,
+                "brain_segmentation_volume_mm^3": 1327432.000000,
+                "brain_segmentation_volume_without_ventricles_mm^3": 1316285.000000,
+                "brain_segmentation_volume_without_ventricles_from_surf_mm^3": 1315572.548920,
+                "total_cortical_gray_matter_volume_mm^3": 553998.311189,
+                "supratentorial_volume_mm^3": 1172669.548920,
+                "supratentorial_volume_without_ventricles_mm^3": 1164180.548920,
+                "estimated_total_intracranial_volume_mm^3": 1670487.274486,
+            },
+            3,
+            [
+                {
+                    "structure_name": "caudalanteriorcingulate",
+                    "number_of_vertices": 2061,
+                    "surface_area_mm^2": 1472,
+                    "gray_matter_volume_mm^3": 4258,
+                    "average_thickness_mm": 2.653,
+                    "thickness_stddev_mm": 0.644,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.135,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.020,
+                    "folding_index": 27,
+                    "intrinsic_curvature_index": 1.6,
+                },
+                {
+                    "structure_name": "caudalmiddlefrontal",
+                    "number_of_vertices": 4451,
+                    "surface_area_mm^2": 3039,
+                    "gray_matter_volume_mm^3": 8239,
+                    "average_thickness_mm": 2.456,
+                    "thickness_stddev_mm": 0.486,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.116,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.020,
+                    "folding_index": 42,
+                    "intrinsic_curvature_index": 3.7,
+                },
+                {
+                    "structure_name": "insula",
+                    "number_of_vertices": 3439,
+                    "surface_area_mm^2": 2304,
+                    "gray_matter_volume_mm^3": 7594,
+                    "average_thickness_mm": 3.193,
+                    "thickness_stddev_mm": 0.620,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.116,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.027,
+                    "folding_index": 33,
+                    "intrinsic_curvature_index": 3.5,
+                },
+            ],
+        ),
+        (
+            os.path.join(SUBJECTS_DIR, "fabian", "stats", "rh.aparc.pial.stats.short"),
+            {
+                "CreationTime": datetime.datetime(
+                    2019, 5, 9, 21, 3, 42, tzinfo=datetime.timezone.utc
+                ),
+                "generating_program": "mris_anatomical_stats",
+                "cvs_version": "Id: mris_anatomical_stats.c,v 1.79"
+                " 2016/03/14 15:15:34 greve Exp",
+                "mrisurf.c-cvs_version": "Id: mrisurf.c,v 1.781.2.6"
+                " 2016/12/27 16:47:14 zkaufman Exp",
+                "cmdline": "mris_anatomical_stats -th3 -mgz -cortex ../label/rh.cortex.label"
+                " -f ../stats/rh.aparc.pial.stats -b -a ../label/rh.aparc.annot"
+                " -c ../label/aparc.annot.ctab fabian rh pial",
+                "sysname": "Linux",
+                "hostname": "some-hostname",
+                "machine": "x86_64",
+                "user": "some-username",
+                "SUBJECTS_DIR": "/home/some-username/freesurfer-subjects",
+                "anatomy_type": "surface",
+                "subjectname": "fabian",
+                "hemi": "rh",
+                "AnnotationFile": "../label/rh.aparc.annot",
+                "AnnotationFileTimeStamp": datetime.datetime(2019, 5, 9, 22, 27, 28),
+            },
+            "right",
+            {
+                "pial_surface_total_area_mm^2": 121260,
+                "mean_thickness_mm": 2.4817,
+                "brain_segmentation_volume_mm^3": 1327432.000000,
+                "brain_segmentation_volume_without_ventricles_mm^3": 1316285.000000,
+                "brain_segmentation_volume_without_ventricles_from_surf_mm^3": 1315572.548920,
+                "total_cortical_gray_matter_volume_mm^3": 553998.311189,
+                "supratentorial_volume_mm^3": 1172669.548920,
+                "supratentorial_volume_without_ventricles_mm^3": 1164180.548920,
+                "estimated_total_intracranial_volume_mm^3": 1670487.274486,
+            },
+            2,
+            [
+                {
+                    "structure_name": "bankssts",
+                    "number_of_vertices": 1344,
+                    "surface_area_mm^2": 825,
+                    "gray_matter_volume_mm^3": 2171,
+                    "average_thickness_mm": 2.436,
+                    "thickness_stddev_mm": 0.381,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.115,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.028,
+                    "folding_index": 19,
+                    "intrinsic_curvature_index": 1.7,
+                },
+                {
+                    "structure_name": "transversetemporal",
+                    "number_of_vertices": 651,
+                    "surface_area_mm^2": 545,
+                    "gray_matter_volume_mm^3": 1061,
+                    "average_thickness_mm": 2.251,
+                    "thickness_stddev_mm": 0.317,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.110,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.021,
+                    "folding_index": 3,
+                    "intrinsic_curvature_index": 0.6,
+                },
+            ],
+        ),
+        (
+            os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.aparc.stats"),
+            {
+                "CreationTime": datetime.datetime(
+                    2020, 5, 4, 22, 20, 26, tzinfo=datetime.timezone.utc
+                ),
+                "AnnotationFileTimeStamp": datetime.datetime(2020, 5, 4, 21, 58, 13),
+                "AnnotationFile": "../label/rh.aparc.annot",
+                "SUBJECTS_DIR": "/N/dc2/scratch/hayashis/bigred3-workflows"
+                "/5eb0689676c10ead933d673c/5eb068b076c10e7b013d673f",
+                "anatomy_type": "surface",
+                "cmdline": "mris_anatomical_stats -th3 -mgz -cortex ../label/rh.cortex.label "
+                "-f ../stats/rh.aparc.stats -b -a ../label/rh.aparc.annot -c "
+                "../label/aparc.annot.ctab output rh white",
+                "cvs_version": "7.0.0",
+                "generating_program": "mris_anatomical_stats",
+                "hemi": "rh",
+                "hostname": "nid00762",
+                "machine": "x86_64",
+                "mrisurf.c-cvs_version": "7.0.0",
+                "subjectname": "output",
+                "sysname": "Linux",
+                "user": "hayashis",
+                "BrainVolStatsFixed": "NotNeeded because voxelvolume=1mm3",
+            },
+            "right",
+            {
+                "white_surface_total_area_mm^2": 83579.2,
+                "mean_thickness_mm": 2.35815,
+                "brain_segmentation_volume_mm^3": 1169408.0,
+                "brain_segmentation_volume_without_ventricles_mm^3": 1157593.0,
+                "brain_segmentation_volume_without_ventricles_from_surf_mm^3": 1157593.0,
+                "total_cortical_gray_matter_volume_mm^3": 454587.696158,
+                "supratentorial_volume_mm^3": 1023873.0,
+                "supratentorial_volume_without_ventricles_mm^3": 1012058.0,
+                "estimated_total_intracranial_volume_mm^3": 1420434.160521,
+            },
+            34,
+            [
+                {
+                    "structure_name": "bankssts",
+                    "number_of_vertices": 1094,
+                    "surface_area_mm^2": 757,
+                    "gray_matter_volume_mm^3": 1725,
+                    "average_thickness_mm": 2.215,
+                    "thickness_stddev_mm": 0.544,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.109,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.025,
+                    "folding_index": 9,
+                    "intrinsic_curvature_index": 1.1,
+                },
+                {
+                    "structure_name": "caudalanteriorcingulate",
+                    "number_of_vertices": 1137,
+                    "surface_area_mm^2": 780,
+                    "gray_matter_volume_mm^3": 2327,
+                    "average_thickness_mm": 2.842,
+                    "thickness_stddev_mm": 0.667,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.116,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.021,
+                    "folding_index": 11,
+                    "intrinsic_curvature_index": 1.0,
+                },
+                {
+                    "structure_name": "caudalmiddlefrontal",
+                    "number_of_vertices": 3126,
+                    "surface_area_mm^2": 2218,
+                    "gray_matter_volume_mm^3": 5978,
+                    "average_thickness_mm": 2.447,
+                    "thickness_stddev_mm": 0.605,
+                    "integrated_rectified_mean_curvature_mm^-1": 0.122,
+                    "integrated_rectified_gaussian_curvature_mm^-2": 0.024,
+                    "folding_index": 28,
+                    "intrinsic_curvature_index": 3.1,
+                },
+            ],
+        ),
+    ],
 )
-def test_read(path, headers, hemisphere, whole_brain_measurements, structural_measurements_length,
-              structural_measurements_subset):
+def test_read(
+    path,
+    headers,
+    hemisphere,
+    whole_brain_measurements,
+    structural_measurements_length,
+    structural_measurements_subset,
+):
     stats = CorticalParcellationStats.read(path)
     assert headers == stats.headers
     assert hemisphere == stats.hemisphere
@@ -218,16 +281,16 @@ def test_read(path, headers, hemisphere, whole_brain_measurements, structural_me
         check_names=True,
     )
     assert list(stats.structural_measurements.columns) == [
-        'structure_name',
-        'number_of_vertices',
-        'surface_area_mm^2',
-        'gray_matter_volume_mm^3',
-        'average_thickness_mm',
-        'thickness_stddev_mm',
-        'integrated_rectified_mean_curvature_mm^-1',
-        'integrated_rectified_gaussian_curvature_mm^-2',
-        'folding_index',
-        'intrinsic_curvature_index',
+        "structure_name",
+        "number_of_vertices",
+        "surface_area_mm^2",
+        "gray_matter_volume_mm^3",
+        "average_thickness_mm",
+        "thickness_stddev_mm",
+        "integrated_rectified_mean_curvature_mm^-1",
+        "integrated_rectified_gaussian_curvature_mm^-2",
+        "folding_index",
+        "intrinsic_curvature_index",
     ]
     assert len(stats.structural_measurements) == structural_measurements_length
     pandas.util.testing.assert_frame_equal(
@@ -240,19 +303,27 @@ def test_read(path, headers, hemisphere, whole_brain_measurements, structural_me
 
 
 @pytest.mark.parametrize(
-    ('path', 'structural_measurements_length'),
-    [(os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'lh.BA_exvivo.stats'), 14),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'lh.BA_exvivo.thresh.stats'), 14),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'lh.aparc.DKTatlas.stats'), 31),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'lh.aparc.a2009s.stats'), 74),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'lh.aparc.pial.stats'), 34),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'lh.aparc.stats'), 34),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'rh.BA_exvivo.stats'), 14),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'rh.BA_exvivo.thresh.stats'), 14),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'rh.aparc.DKTatlas.stats'), 31),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'rh.aparc.a2009s.stats'), 74),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'rh.aparc.pial.stats'), 34),
-     (os.path.join(SUBJECTS_DIR, 'soichi', 'stats', 'rh.aparc.stats'), 34)],
+    ("path", "structural_measurements_length"),
+    [
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "lh.BA_exvivo.stats"), 14),
+        (
+            os.path.join(SUBJECTS_DIR, "soichi", "stats", "lh.BA_exvivo.thresh.stats"),
+            14,
+        ),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "lh.aparc.DKTatlas.stats"), 31),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "lh.aparc.a2009s.stats"), 74),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "lh.aparc.pial.stats"), 34),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "lh.aparc.stats"), 34),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.BA_exvivo.stats"), 14),
+        (
+            os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.BA_exvivo.thresh.stats"),
+            14,
+        ),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.aparc.DKTatlas.stats"), 31),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.aparc.a2009s.stats"), 74),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.aparc.pial.stats"), 34),
+        (os.path.join(SUBJECTS_DIR, "soichi", "stats", "rh.aparc.stats"), 34),
+    ],
 )
 def test_read_structural_measurements_length(path, structural_measurements_length):
     # simple test to verify no exception gets raised, see test_read for comprehensive test