I think you can get away without Cython and vectorize the whole thing with numpy doing something along the lines of:

```
idx = self.row * self.shape[0] + self.col
order = np.argsort(idx)
idx = idx[order]
unq_idx, unq_inv = np.unique(idx, return_inverse=True)
unq_cnt = np.bincount(unq_inv)
if np.any(unq_cnt > 1):
    add_idx = np.concatenate(([0], np.cumsum(unq_cnt[:-1])))
    self.data = np.add.reduceat(self.data[order], add_idx)
    self.row = unq_idx // self.shape[0]
    self.col = unq_idx % self.shape[0]
```