Source code for treem.commands.check

"""Implementation of CLI check command."""

import os
import json
import warnings

import numpy as np

from treem.io import SWC, TreemEncoder


[docs]def check(args): """Checks morphology reconstruction for structural consistency.""" # pylint: disable=too-many-branches # pylint: disable=too-many-statements data = None err = {} while True: if not os.path.exists(args.file) or not os.path.isfile(args.file): err['no_file'] = [args.file] break if not args.file.lower().endswith('swc'): err['not_swc_ext'] = [args.file.split('.')[-1]] try: with warnings.catch_warnings(): warnings.simplefilter("ignore") data = np.loadtxt(args.file) except ValueError: err['not_array'] = [True] break if data.shape[0] == 0: err['no_data'] = [True] break if len(data.shape) == 1: err['single_point'] = [data[SWC.I].astype(int)] break if data.shape[1] != len(SWC.COLS): err['not_swc_cols'] = [data.shape[1]] break first = data[0] if first[SWC.I] != 1: err['node1_not_id1'] = [first[SWC.I].astype(int)] if first[SWC.P] != -1: err['node1_has_parent'] = [first[SWC.P].astype(int)] if first[SWC.T] != SWC.SOMA: err['node1_not_soma'] = [first[SWC.T].astype(int)] soma_nodes = data[data[:, SWC.T] == SWC.SOMA] soma_ids = soma_nodes[:, SWC.I].astype(int) if len(soma_ids > 1): inc = soma_ids[1:] - soma_ids[:-1] if not (inc == 1).all(): err['non_sequential_soma_ids'] = soma_ids[np.where(inc != 1)[0] + 1] types = set(data[:, SWC.T].astype(int)) if not types.issubset(SWC.TYPES): err['not_valid_types'] =\ [data[x][SWC.I].astype(int) for t in types.difference(SWC.TYPES) for x in np.where(data[:, SWC.T] == t)[0]] ids = data[:, SWC.I].astype(int) if not (ids > 0).all(): err['not_valid_ids'] = ids[ids <= 0] break idp = data[1:, SWC.P].astype(int) if not (idp > 0).all(): err['not_valid_parent_ids'] = ids[1:][idp <= 0] break ids_set = set(ids) idp_set = set(idp) if len(ids_set) != len(ids): seen = set() err['non_unique_ids'] =\ {x for x in ids if x in seen or seen.add(x)} break if not idp_set.issubset(ids_set): err['undef_parent_ids'] =\ [data[x][SWC.I].astype(int) for p in idp_set.difference(ids_set) for x in np.where(data[:, SWC.P] == p)[0]] break inc = ids[1:] - ids[:-1] if not (inc > 0).all(): err['non_increasing_ids'] = ids[np.where(inc <= 0)[0] + 1] break if not (inc == 1).all(): err['non_sequential_ids'] = ids[np.where(inc != 1)[0] + 1] break if not (ids[1:] > idp).all(): err['non_descendant'] = ids[np.where(ids[1:] <= idp)[0] + 1] break if [data[x][SWC.I].astype(int) for t in types.difference([1]) for x in np.where(data[:, SWC.T] == t)[0][:1] if data[x][SWC.P] != 1]: err['non_stem_neurite'] =\ [data[x][SWC.I].astype(int) for t in types.difference([1]) for x in np.where(data[:, SWC.T] == t)[0][:1] if data[x][SWC.P] != 1] break if not args.quiet: for k in err: # pylint: disable=C0206 print(f'{k}:', end=' ') print(*err[k]) if args.out: with open(args.out, 'w', encoding='utf-8') as file: json.dump(err, file, cls=TreemEncoder) return len(err)