@@ -19,9 +19,11 @@ import numpy as np
1919import nibabel as nib
2020
2121from math import ceil
22+ from collections import defaultdict
2223from optparse import OptionParser , Option
2324from io import StringIO
2425from nibabel .py3k import asunicode
26+ from nibabel .externals .six .moves import xrange
2527
2628__author__ = 'Yaroslav Halchenko'
2729__copyright__ = 'Copyright (c) 2011-2016 Yaroslav Halchenko ' \
@@ -31,7 +33,13 @@ __license__ = 'MIT'
3133
3234# global verbosity switch
3335verbose_level = 0
36+ MAX_UNIQUE = 1000 # maximal number of unique values to report for --counts
3437
38+ def _err (msg = None ):
39+ """To return a string to signal "error" in output table"""
40+ if msg is None :
41+ msg = 'error'
42+ return '!' + msg
3543
3644def verbose (l , msg ):
3745 """Print `s` if `l` is less than the `verbose_level`
@@ -159,6 +167,10 @@ def get_opt_parser():
159167 help = "Output counts - number of entries for each numeric value "
160168 "(useful for int ROI maps)" ),
161169
170+ Option ("--all-counts" ,
171+ action = "store_true" , dest = 'all_counts' , default = False ,
172+ help = "Output all counts, even if number of unique values > %d" % MAX_UNIQUE ),
173+
162174 Option ("-z" , "--zeros" ,
163175 action = "store_true" , dest = 'stats_zeros' , default = False ,
164176 help = "Include zeros into output basic data statistics (--stats, --counts)" ),
@@ -210,7 +222,7 @@ def proc_file(f, opts):
210222 try :
211223 row += [str (h [f ])]
212224 except (KeyError , ValueError ):
213- row += ['error' ]
225+ row += [_err () ]
214226
215227 try :
216228 if (hasattr (h , 'get_qform' ) and hasattr (h , 'get_sform' ) and
@@ -223,26 +235,34 @@ def proc_file(f, opts):
223235 if isinstance (h , nib .AnalyzeHeader ):
224236 row += ['' ]
225237 else :
226- row += ['error' ]
238+ row += [_err () ]
227239
228240 if opts .stats or opts .counts :
229241 # We are doomed to load data
230242 try :
231243 d = vol .get_data ()
232244 if not opts .stats_zeros :
233245 d = d [np .nonzero (d )]
246+ else :
247+ # at least flatten it -- functionality below doesn't
248+ # depend on the original shape, so let's use a flat view
249+ d = d .reshape (- 1 )
234250 if opts .stats :
235251 # just # of elements
236252 row += ["@l[%d]" % np .prod (d .shape )]
237253 # stats
238254 row += [len (d ) and '@l[%.2g, %.2g]' % (np .min (d ), np .max (d )) or '-' ]
239255 if opts .counts :
240256 items , inv = np .unique (d , return_inverse = True )
241- freq = np .bincount (inv )
242- row += ["@l" + " " .join ("%g:%d" % (i , f ) for i , f in zip (items , freq ))]
257+ if len (items ) > 1000 and not opts .all_counts :
258+ counts = _err ("%d uniques. Use --all-counts" % len (items ))
259+ else :
260+ freq = np .bincount (inv )
261+ counts = " " .join ("%g:%d" % (i , f ) for i , f in zip (items , freq ))
262+ row += ["@l" + counts ]
243263 except IOError as e :
244264 verbose (2 , "Failed to obtain stats/counts -- %s" % str (e ))
245- row += ['error' ]
265+ row += [_err () ]
246266 return row
247267
248268
0 commit comments