@@ -1488,7 +1488,6 @@ def get_fselection_numpy(self, key: list | np.ndarray) -> np.ndarray: # noqa: C
14881488 _slice = ndindex .ndindex (key ).expand (shape )
14891489 out_shape = _slice .newshape (shape )
14901490 _slice = _slice .raw
1491- shape = np .array (shape )
14921491
14931492 if np .all ([isinstance (s , (slice , np .ndarray )) for s in _slice ]) and np .all (
14941493 [s .dtype is not bool for s in _slice if isinstance (s , np .ndarray )]
@@ -1518,20 +1517,24 @@ def get_fselection_numpy(self, key: list | np.ndarray) -> np.ndarray: # noqa: C
15181517 end = num
15191518 f_ = 2
15201519 flat_shape += (arr .shape [- 1 ],)
1521- flat_shape += ((i .stop - i .start ) // i .step ,)
1520+ # k in [1,step], stop = start + n*step + k
1521+ # stop - start + step - 1 = (n+1)*step + k-1 = (n+1)*step + [0, step - 1]
1522+ flat_shape += ((i .stop - i .start + (i .step - 1 )) // i .step ,)
15221523 if not isinstance (arr , np .ndarray ): # might have missed last part of loop
15231524 arr = np .stack (arr )
15241525 flat_shape += (arr .shape [- 1 ],)
15251526 # out_shape could have new dims if indexing arrays are not all 1D
15261527 # (we have just flattened them so need to handle accordingly)
1528+ divider = chunks [begin :end ]
1529+ chunked_arr = arr .T // divider
1530+ unique_chunks , chunk_nitems = np .unique (chunked_arr , axis = 0 , return_counts = True )
15271531 idx_order = np .lexsort (
1528- tuple (a for a in reversed (arr ))
1532+ tuple (a for a in reversed (chunked_arr . T ))
15291533 ) # sort by column with priority to first column
15301534 sorted_idxs = arr [:, idx_order ]
15311535 out = np .empty (flat_shape , dtype = self .dtype )
1536+ shape = np .array (shape )
15321537
1533- divider = chunks [begin :end ]
1534- unique_chunks , chunk_nitems = np .unique (sorted_idxs .T // divider , axis = 0 , return_counts = True )
15351538 chunk_nitems_cumsum = np .cumsum (chunk_nitems )
15361539 prior_tuple = _slice [:begin ]
15371540 post_tuple = _slice [end :] if end is not None else ()
@@ -1547,13 +1550,23 @@ def get_fselection_numpy(self, key: list | np.ndarray) -> np.ndarray: # noqa: C
15471550 start = 0 if chunk_i == 0 else chunk_nitems_cumsum [chunk_i - 1 ]
15481551 stop = chunk_nitems_cumsum [chunk_i ]
15491552 selection = sorted_idxs [:, start :stop ].T
1550- out_selection = idx_order [start :stop ].T
1553+ mid_out_selection = idx_order [start :stop ].T
1554+ chunk_begin = chunk_idx * chunks [begin :end ]
1555+ chunk_end = np .minimum ((chunk_idx + 1 ) * chunks [begin :end ], shape [begin :end ])
15511556 # loop over chunks coming from slices before and after array indices
15521557 for cprior_tuple in product (* cprior_slices ):
15531558 prior_selection = selector (cprior_tuple , prior_tuple , chunks [:begin ])
15541559 # selection relative to coordinates of out (necessarily step = 1)
1560+ # stop = start + step * n + k => n = (stop - start - 1) // step
1561+ # hence, out_stop = out_start + n + 1
1562+ # ps.start = pt.start + out_start * step
15551563 out_prior_selection = tuple (
1556- slice (ps .start - pt .start , ps .stop - pt .start , 1 )
1564+ slice (
1565+ (ps .start - pt .start + pt .step - 1 ) // pt .step ,
1566+ (ps .start - pt .start + pt .step - 1 ) // pt .step
1567+ + (ps .stop - ps .start + ps .step - 1 ) // ps .step ,
1568+ 1 ,
1569+ )
15571570 for ps , pt in zip (prior_selection , prior_tuple , strict = True )
15581571 )
15591572 for cpost_tuple in product (* cpost_slices ):
@@ -1562,11 +1575,14 @@ def get_fselection_numpy(self, key: list | np.ndarray) -> np.ndarray: # noqa: C
15621575 )
15631576 # selection relative to coordinates of out (necessarily step = 1)
15641577 out_post_selection = tuple (
1565- slice (ps .start - pt .start , ps .stop - pt .start , 1 )
1578+ slice (
1579+ (ps .start - pt .start + pt .step - 1 ) // pt .step ,
1580+ (ps .start - pt .start + pt .step - 1 ) // pt .step
1581+ + (ps .stop - ps .start + ps .step - 1 ) // ps .step ,
1582+ 1 ,
1583+ )
15661584 for ps , pt in zip (post_selection , post_tuple , strict = True )
15671585 )
1568- chunk_begin = chunk_idx * chunks [begin :end ]
1569- chunk_end = np .minimum ((chunk_idx + 1 ) * chunks [begin :end ], shape [begin :end ])
15701586 locbegin = np .hstack (
15711587 (
15721588 [s .start for s in prior_selection ],
@@ -1581,7 +1597,7 @@ def get_fselection_numpy(self, key: list | np.ndarray) -> np.ndarray: # noqa: C
15811597 casting = "unsafe" ,
15821598 dtype = "int64" ,
15831599 )
1584- out_selection = out_prior_selection + (out_selection ,) + out_post_selection
1600+ out_selection = out_prior_selection + (mid_out_selection ,) + out_post_selection
15851601 to_be_loaded = np .empty (locend - locbegin , dtype = self .dtype )
15861602 # basically load whole chunk, except for slice part at beginning and end
15871603 super ().get_slice_numpy (
@@ -4615,7 +4631,21 @@ def slice_to_chunktuple(s, n):
46154631
46164632
46174633def selector (ctuple , _tuple , chunks ):
4618- return tuple (
4619- slice (builtins .max (s .start , i * csize ), builtins .min (csize * (i + 1 ), s .stop ), s .step )
4620- for i , s , csize in zip (ctuple , _tuple , chunks , strict = True )
4621- )
4634+ # we assume that at least one element of chunk intersects with the slice
4635+ # (as a consequence of only looping over intersecting chunks)
4636+ result = ()
4637+ for i , s , csize in zip (ctuple , _tuple , chunks , strict = True ):
4638+ # we need to advance to first element within chunk that intersects with slice, not
4639+ # necessarily the first element of chunk
4640+ # i * csize = s.start + n*step + k, already added n+1 elements, k in [1, step]
4641+ np1 = (i * csize - s .start + s .step - 1 ) // s .step # gives (n + 1)
4642+ # can have n = -1 if s.start > i * csize, but never < -1 since have to intersect with chunk
4643+ result += (
4644+ slice (
4645+ builtins .max (s .start , s .start + np1 * s .step ), # start+(n+1)*step gives i*csize if k=step
4646+ builtins .min (csize * (i + 1 ), s .stop ),
4647+ s .step ,
4648+ ),
4649+ )
4650+
4651+ return result
0 commit comments