@@ -225,10 +225,13 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
225225 e = int (e )
226226 except :
227227 raise IndexError (IndexErrorMsg )
228- ea = np .asarray (e )
229- # Raise error if multidimensional indexing is used.
230- if ea .ndim > 1 :
231- raise IndexError ("Index cannot be multidimensional" )
228+ # plain integers, slices and ellipsis need no numpy conversion.
229+ simple = type (e ) is int or type (e ) is slice or e is Ellipsis
230+ if not simple :
231+ ea = np .asarray (e )
232+ # Raise error if multidimensional indexing is used.
233+ if ea .ndim > 1 :
234+ raise IndexError ("Index cannot be multidimensional" )
232235 # set unlim to True if dimension is unlimited and put==True
233236 # (called from __setitem__)
234237 if hasunlim and put and dimensions :
@@ -239,42 +242,11 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
239242 unlim = False
240243 else :
241244 unlim = False
242- # convert boolean index to integer array.
243- if np .iterable (ea ) and ea .dtype .kind == 'b' :
244- # check that boolean array not too long
245- if not unlim and shape [i ] != len (ea ):
246- msg = """
247- Boolean array must have the same shape as the data along this dimension."""
248- raise IndexError (msg )
249- ea = np .flatnonzero (ea )
250- # an iterable (non-scalar) integer array.
251- if np .iterable (ea ) and ea .dtype .kind == 'i' :
252- # convert negative indices in 1d array to positive ones.
253- ea = np .where (ea < 0 , ea + shape [i ], ea )
254- if np .any (ea < 0 ):
255- raise IndexError ("integer index out of range" )
256- # if unlim, let integer index be longer than current dimension
257- # length.
258- if ea .shape != (0 ,):
259- elen = shape [i ]
260- if unlim :
261- elen = max (ea .max ()+ 1 ,elen )
262- if ea .max ()+ 1 > elen :
263- msg = "integer index exceeds dimension size"
264- raise IndexError (msg )
265- newElem .append (ea )
266245 # integer scalar
267- elif ea . dtype . kind == 'i' :
246+ if type ( e ) is int :
268247 newElem .append (e )
269- # Scalar floats retain compatibility when they represent an exact
270- # integer, but non-integral values are invalid indices. Letting
271- # int(e) handle both cases silently truncates values such as 0.8.
272- elif ea .dtype .kind == 'f' and not np .iterable (ea ):
273- if not np .isfinite (e ) or e != np .floor (e ):
274- raise IndexError (IndexErrorMsg )
275- newElem .append (int (e ))
276248 # slice or ellipsis object
277- elif type ( e ) == slice or type ( e ) == type ( Ellipsis ) :
249+ elif simple :
278250 if not use_get_vars and type (e ) == slice and e .step not in [None ,- 1 ,1 ] and \
279251 dimensions is not None and grp is not None :
280252 # convert strided slice to integer sequence if possible
@@ -294,12 +266,60 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
294266 except :
295267 pass
296268 newElem .append (e )
297- else : # castable to a scalar int, otherwise invalid
298- try :
299- e = int (e )
269+ else :
270+ # classify the converted index once; ndim is 0 or 1 here.
271+ kind = ea .dtype .kind
272+ # convert boolean index to integer array.
273+ if ea .ndim and kind == 'b' :
274+ # check that boolean array not too long
275+ if not unlim and shape [i ] != len (ea ):
276+ msg = """
277+ Boolean array must have the same shape as the data along this dimension."""
278+ raise IndexError (msg )
279+ # nonzero positions are never negative.
280+ ea = np .flatnonzero (ea )
281+ kind = None
282+ # an iterable (non-scalar) integer array.
283+ elif ea .ndim and kind == 'i' :
284+ if ea .dtype == np .intp and type (shape [i ]) is int and \
285+ not (ea < 0 ).any ():
286+ # nothing to wrap; just avoid aliasing the caller's array.
287+ if ea is e :
288+ ea = ea .copy ()
289+ else :
290+ # convert negative indices in 1d array to positive ones.
291+ ea = np .where (ea < 0 , ea + shape [i ], ea )
292+ if np .any (ea < 0 ):
293+ raise IndexError ("integer index out of range" )
294+ kind = None
295+ if kind is None :
296+ # if unlim, let integer index be longer than current dimension
297+ # length.
298+ if len (ea ):
299+ elen = shape [i ]
300+ emax = ea .max ()+ 1
301+ if unlim :
302+ elen = max (emax ,elen )
303+ if emax > elen :
304+ msg = "integer index exceeds dimension size"
305+ raise IndexError (msg )
306+ newElem .append (ea )
307+ # integer scalar
308+ elif kind == 'i' :
300309 newElem .append (e )
301- except :
302- raise IndexError (IndexErrorMsg )
310+ # Scalar floats retain compatibility when they represent an exact
311+ # integer, but non-integral values are invalid indices. Letting
312+ # int(e) handle both cases silently truncates values such as 0.8.
313+ elif kind == 'f' and not ea .ndim :
314+ if not np .isfinite (e ) or e != np .floor (e ):
315+ raise IndexError (IndexErrorMsg )
316+ newElem .append (int (e ))
317+ else : # castable to a scalar int, otherwise invalid
318+ try :
319+ e = int (e )
320+ newElem .append (e )
321+ except :
322+ raise IndexError (IndexErrorMsg )
303323 if type (e )== type (Ellipsis ):
304324 i += 1 + nDims - len (elem )
305325 else :
@@ -309,13 +329,16 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
309329 # replace Ellipsis and integer arrays with slice objects, if possible.
310330 newElem = []
311331 for e in elem :
312- ea = np .asarray (e )
332+ if type (e ) is int or type (e ) is slice :
333+ newElem .append (e )
313334 # Replace ellipsis with slices.
314- if type (e ) == type (Ellipsis ):
335+ elif type (e ) == type (Ellipsis ):
315336 # The ellipsis stands for the missing dimensions.
316337 newElem .extend ((slice (None , None , None ),) * (nDims - len (elem ) + 1 ))
338+ elif not np .iterable (e ):
339+ newElem .append (e )
317340 # Replace sequence of indices with slice object if possible.
318- elif np . iterable ( e ) and len (e ) > 1 :
341+ elif len (e ) > 1 :
319342 start = e [0 ]
320343 stop = e [- 1 ]+ 1
321344 step = e [1 ]- e [0 ]
@@ -332,14 +355,12 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
332355 newElem .append (slice (start ,stop ,step ))
333356 else :
334357 newElem .append (e )
335- elif np . iterable ( e ) and len (e ) == 1 :
358+ elif len (e ) == 1 :
336359 newElem .append (slice (e [0 ], e [0 ] + 1 , 1 ))
337- elif np . iterable ( e ) and len ( e ) == 0 :
360+ else :
338361 # Retain counts and scalar squeeze markers for the other axes.
339362 # A zero-length chunk axis would discard all of that metadata.
340363 newElem .append (slice (0 , 0 , 1 ))
341- else :
342- newElem .append (e )
343364 elem = newElem
344365
345366 # If slice doesn't cover all dims, assume ellipsis for rest of dims.
@@ -354,28 +375,26 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
354375 # Compute the dimensions of the start, count, stride and indices arrays.
355376 # The number of elements in the first n dimensions corresponds to the
356377 # number of times the _get method will be called.
357- sdim = []
358- for i , e in enumerate (elem ):
359- # at this stage e is a slice, a scalar integer, or a 1d integer array.
360- # integer array: _get call for each True value
361- if np .iterable (e ):
362- sdim .append (len (e ))
363- # Scalar int or slice, just a single _get call
364- else :
365- sdim .append (1 )
378+ # at this stage e is a slice, a scalar integer, or a 1d integer array.
379+ iterable = [type (e ) is not int and type (e ) is not slice and np .iterable (e )
380+ for e in elem ]
381+ # integer array: _get call for each True value
382+ # Scalar int or slice, just a single _get call
383+ sdim = [len (e ) if it else 1 for e , it in zip (elem , iterable )]
366384
367385 # broadcast data shape when assigned to full variable (issue #919)
368- try :
369- fullslice = elem .count (slice (None ,None ,None )) == len (elem )
370- except : # fails if elem contains a numpy array.
371- fullslice = False
372- if fullslice and datashape and put and not hasunlim :
373- datashape = broadcasted_shape (shape , datashape )
386+ if datashape and put and not hasunlim :
387+ try :
388+ fullslice = elem .count (slice (None ,None ,None )) == len (elem )
389+ except : # fails if elem contains a numpy array.
390+ fullslice = False
391+ if fullslice :
392+ datashape = broadcasted_shape (shape , datashape )
374393
375394 # pad datashape with zeros for dimensions not being sliced (issue #906)
376395 # only used when data covers slice over subset of dimensions
377- padding_dims = sum (1 for e in elem if type (e ) == slice )
378396 if datashape and len (datashape ) != len (elem ):
397+ padding_dims = sum (1 for e in elem if type (e ) == slice )
379398 for i , e in enumerate (elem ):
380399 if type (e ) != slice :
381400 continue
@@ -404,15 +423,13 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
404423 # integer index arrays into their Cartesian chunk grid if it is nonempty.
405424 sdim .append (max (nDims , 1 ))
406425 compact = [1 ] * nDims + [nDims ]
407- start = np . empty ( compact , dtype = np . intp )
408- count = np . empty ( compact , dtype = np . intp )
409- stride = np . empty ( compact , dtype = np . intp )
410- indices = np . empty ( compact , dtype = object )
426+ start = []
427+ count = []
428+ stride = []
429+ indices = []
411430
412431 for i , e in enumerate (elem ):
413432
414- ea = np .asarray (e )
415-
416433 # set unlim to True if dimension is unlimited and put==True
417434 # (called from __setitem__). Note: grp and dimensions must be set.
418435 if hasunlim and put and dimensions :
@@ -451,47 +468,58 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
451468 beg , end , inc = e .indices (length )
452469 n = len (range (beg ,end ,inc ))
453470
454- start [..., i ] = beg
455- count [..., i ] = n
456- stride [..., i ] = inc
457- indices [..., i ] = slice (None )
471+ start . append ( beg )
472+ count . append ( n )
473+ stride . append ( inc )
474+ indices . append ( slice (None ) )
458475
459476 # ITERABLE #
460- elif np . iterable ( e ) and np .array (e ).dtype .kind in 'i' : # Sequence of integers
461- start [..., i ] = e [0 ]
462- count [..., i ] = len (e )
463- stride [..., i ] = 1
464- indices [..., i ] = 0 # preserve the distinction from a full slice
477+ elif iterable [ i ] and np .asarray (e ).dtype .kind in 'i' : # Sequence of integers
478+ start . append ( e [0 ])
479+ count . append ( len (e ) )
480+ stride . append ( 1 )
481+ indices . append ( 0 ) # preserve the distinction from a full slice
465482
466483 # all that's left is SCALAR INTEGER #
467484 else :
468485 if e >= 0 :
469- start [..., i ] = e
486+ start . append ( e )
470487 elif e < 0 and (- e <= shape [i ]) :
471- start [..., i ] = e + shape [i ]
488+ start . append ( e + shape [i ])
472489 else :
473490 raise IndexError ("Index out of range" )
474491
475- count [...,i ] = 1
476- stride [...,i ] = 1
477- indices [...,i ] = - 1 # Use -1 instead of 0 to indicate that
478- # this dimension shall be squeezed.
492+ count .append (1 )
493+ stride .append (1 )
494+ indices .append (- 1 ) # Use -1 instead of 0 to indicate that
495+ # this dimension shall be squeezed.
496+
497+ # Convert the per-dimension values once, rather than writing each one.
498+ empty = 0 in count
499+ start , count , stride = np .array ((start , count , stride ),
500+ dtype = np .intp ).reshape ([3 ] + compact )
501+ indices_ = np .empty (nDims , dtype = object )
502+ indices_ [:] = indices
503+ indices = indices_ .reshape (compact )
479504
480- if 0 in count and not put :
505+ if empty and not put :
481506 # Empty reads bypass native bounds checks, including those for vlens.
482507 for i , e in enumerate (elem ):
483- if type (e ) != slice and not np . iterable ( e ) and e >= shape [i ]:
508+ if type (e ) != slice and not iterable [ i ] and e >= shape [i ]:
484509 raise IndexError ("integer index exceeds dimension size" )
485510
486- if 0 not in count and sdim != compact :
511+ if not empty and sdim != compact :
487512 start = np .broadcast_to (start , sdim ).copy ()
488513 count = np .broadcast_to (count , sdim ).copy ()
489514 stride = np .broadcast_to (stride , sdim ).copy ()
490515 indices = np .broadcast_to (indices , sdim ).copy ()
491516 for i , e in enumerate (elem ):
492- if np .iterable (e ):
493- start [...,i ] = np .apply_along_axis (lambda x : e * x , i , np .ones (sdim [:- 1 ]))
494- indices [...,i ] = np .apply_along_axis (lambda x : np .arange (sdim [i ])* x , i , np .ones (sdim [:- 1 ], int ))
517+ if iterable [i ]:
518+ # broadcast the index values along their own chunk axis.
519+ axis = [1 ] * nDims
520+ axis [i ] = sdim [i ]
521+ start [...,i ] = np .reshape (e , axis )
522+ indices [...,i ] = np .arange (sdim [i ]).reshape (axis )
495523 count [...,i ] = 1
496524
497525 if return_selection :
0 commit comments