@@ -242,69 +242,83 @@ def _range_str_to_range(s, stack_depth=1):
242
242
return generalized_range (start , stop , step )
243
243
244
244
245
- def _range_to_slice (seq , length = None ):
245
+ def _normalize_idx (idx , length ):
246
+ if idx < - length :
247
+ return - length - 1
248
+ elif - length <= idx < 0 :
249
+ return idx + length
250
+ elif 0 <= idx < length :
251
+ return idx
252
+ elif idx > length :
253
+ return length
254
+
255
+
256
+ def _idx_seq_to_slice (seq , length ):
246
257
r"""
247
- Returns a slice if possible (including for sequences of 1 element) otherwise returns the input sequence itself
258
+ Transform a sequence of indices into a slice if possible and normalize it.
259
+
260
+ If a constant step between indices of the sequence can be inferred (the sequence is something like
261
+ [start, start+step, start+2*step, ...]), a normalized (with has few negative indices as possible) slice is
262
+ returned, otherwise the sequence is returned unmodified.
248
263
249
264
Parameters
250
265
----------
251
- seq : sequence-like of int
252
- List, tuple or ndarray of integers representing the range.
253
- It should be something like [start, start+step, start+2*step, ...]
254
- length : int, optional
255
- length of sequence of positions. This is only useful when you must be able to transform decreasing sequences
256
- which can stop at 0.
266
+ seq : sequence-like of integers
267
+ Sequence (list, tuple or ndarray) of indices.
268
+ length : int
269
+ Length of the sequence the indices refer to.
257
270
258
271
Returns
259
272
-------
260
273
slice or sequence-like
261
- return the input sequence if a slice cannot be defined
274
+ return the input sequence if a slice cannot be defined.
262
275
263
276
Examples
264
277
--------
265
- >>> _range_to_slice ([3, 4, 5])
278
+ >>> _idx_seq_to_slice ([3, 4, 5], 10 )
266
279
slice(3, 6, None)
267
- >>> _range_to_slice ([3, 4, 6])
280
+ >>> _idx_seq_to_slice ([3, 4, 6], 10 )
268
281
[3, 4, 6]
269
- >>> _range_to_slice ([3, 5, 7])
270
- slice(3, 9 , 2)
271
- >>> _range_to_slice ([-3, -2])
272
- slice(-3, -1 , None)
273
- >>> _range_to_slice ([-1, -2])
274
- slice(-1, -3 , -1)
275
- >>> _range_to_slice ([2, 1])
282
+ >>> _idx_seq_to_slice ([3, 5, 7], 10 )
283
+ slice(3, 8 , 2)
284
+ >>> _idx_seq_to_slice ([-3, -2], 10 )
285
+ slice(7, 9 , None)
286
+ >>> _idx_seq_to_slice ([-1, -2], 10 )
287
+ slice(9, 7 , -1)
288
+ >>> _idx_seq_to_slice ([2, 1], 10 )
276
289
slice(2, 0, -1)
277
- >>> _range_to_slice([1, 0], 4)
278
- slice(-3, -5, -1)
279
- >>> _range_to_slice([1, 0])
280
- [1, 0]
281
- >>> _range_to_slice([1])
290
+ >>> _idx_seq_to_slice([1, 0], 10)
291
+ slice(1, -11, -1)
292
+ >>> _idx_seq_to_slice([1], 10)
282
293
slice(1, 2, None)
283
- >>> _range_to_slice ([])
294
+ >>> _idx_seq_to_slice ([], 10 )
284
295
[]
296
+ >>> _idx_seq_to_slice([3, 1], 10)
297
+ slice(3, 0, -2)
285
298
"""
286
299
if len (seq ) < 1 :
287
300
return seq
288
- start = seq [0 ]
301
+ first_idx = _normalize_idx ( seq [0 ], length )
289
302
if len (seq ) == 1 :
290
- return slice (start , start + 1 )
291
- second = seq [1 ]
292
- step = second - start
293
- prev_value = second
294
- for value in seq [2 :]:
295
- if value != prev_value + step :
296
- return seq
297
- prev_value = value
298
- stop = prev_value + step
299
- if prev_value == 0 and step < 0 :
300
- if length is None :
303
+ return slice (first_idx , first_idx + 1 )
304
+ second_idx = _normalize_idx (seq [1 ], length )
305
+ step = second_idx - first_idx
306
+ prev_idx = second_idx
307
+ for raw_idx in seq [2 :]:
308
+ idx = _normalize_idx (raw_idx , length )
309
+ if idx != prev_idx + step :
301
310
return seq
302
- else :
303
- stop -= length
304
- start -= length
311
+ prev_idx = idx
312
+ last_idx = prev_idx
313
+ step_sign = 1 if step > 0 else - 1
314
+ # we stop just after last_idx instead of using a full "step" because for negative steps, it could get us a negative
315
+ # stop which isn't what we want
316
+ stop = last_idx + step_sign
317
+ if last_idx == 0 and step < 0 :
318
+ stop -= length
305
319
if step == 1 :
306
320
step = None
307
- return slice (start , stop , step )
321
+ return slice (first_idx , stop , step )
308
322
309
323
310
324
def _is_object_array (array ):
0 commit comments