@@ -317,7 +317,7 @@ def group_ohlc_{{name}}(ndarray[{{dest_type2}}, ndim=2] out,
317
317
{{endfor}}
318
318
319
319
#----------------------------------------------------------------------
320
- # group_nth, group_last, group_rank
320
+ # group_nth, group_last, group_rank, group_fillna
321
321
#----------------------------------------------------------------------
322
322
323
323
{{py:
@@ -618,6 +618,75 @@ def group_rank_{{name}}(ndarray[float64_t, ndim=2] out,
618
618
for i in range(N):
619
619
out[i, 0] = out[i, 0] / grp_sizes[i, 0]
620
620
{{endif}}
621
+
622
+ @cython.wraparound(False)
623
+ @cython.boundscheck(False)
624
+ def group_fillna_{{name}}(ndarray[{{dest_type2}}, ndim=2] out,
625
+ ndarray[{{c_type}}, ndim=2] values,
626
+ ndarray[int64_t] labels,
627
+ object method,
628
+ int64_t limit):
629
+ """Fills values forwards or backwards within a group
630
+
631
+ Parameters
632
+ ----------
633
+ out : array of {{dest_type2}} values which this method will write its
634
+ results to
635
+ values : array of {{c_type}} values which may require filling
636
+ labels : array containing unique label for each group, with its ordering
637
+ matching up to the corresponding record in `values`
638
+ method : {'ffill', 'bfill'}
639
+ Direction for fill to be applied (forwards or backwards, respectively)
640
+ limit : Consecutive values to fill before stopping, or -1 for no limit
641
+
642
+ Notes
643
+ -----
644
+ This method modifies the `out` parameter rather than returning an object
645
+ """
646
+ cdef:
647
+ Py_ssize_t i, N
648
+ ndarray[uint8_t] mask
649
+ ndarray[int64_t] sorted_labels
650
+ {{dest_type2}} curr_fill_val = {{nan_val}}
651
+ int64_t idx, filled_vals=0
652
+
653
+ N, K = (<object> values).shape
654
+
655
+ {{if name=='int64'}}
656
+ mask = (values[:, 0] == {{nan_val}}).astype(np.uint8)
657
+ {{elif name=='object'}}
658
+ mask = np.array([x != x for x in values[:, 0]]).astype(np.uint8)
659
+ {{else}}
660
+ mask = np.isnan(values[:, 0]).astype(np.uint8)
661
+ {{endif}}
662
+
663
+ sorted_labels = np.argsort(labels)
664
+ if method == 'bfill':
665
+ sorted_labels[::-1].sort()
666
+
667
+ {{if name == 'object'}}
668
+ if True: # make templating happy
669
+ {{else}}
670
+ with nogil:
671
+ {{endif}}
672
+ for i in range(N):
673
+ idx = sorted_labels[i]
674
+ if mask[idx]: # is missing
675
+ if limit == -1 or filled_vals < limit:
676
+ out[idx, 0] = curr_fill_val
677
+ else:
678
+ out[idx, 0] == {{nan_val}}
679
+ filled_vals += 1
680
+ else: # reset items when not missing
681
+ filled_vals = 0
682
+ curr_fill_val = values[idx, 0]
683
+ out[idx, 0] = values[idx, 0]
684
+
685
+ # If we move to the next group, reset
686
+ # the fill_val and counter
687
+ if i == N - 1 or labels[idx] != labels[sorted_labels[i+1]]:
688
+ curr_fill_val = {{nan_val}}
689
+ filled_vals = 0
621
690
{{endfor}}
622
691
623
692
0 commit comments