Say I have the following array of ascending-sort integers (some may be negative):
a = np.array([ 1, 1, 1, 1, 10, 10, 20, 20, 20, 30, 40, 40, 40, 40])
I want to turn it into this:
a = np.array([ 1, 2, 3, 4, 10, 11, 20, 21, 22, 30, 40, 41, 42, 43])
...where each integer in each group of the same integers gets incremented, so for the first 1's:
1 1 1 1 <--- these are the numbers from the array
+ 0 1 2 3 <--- these are counts of the number for its group
-------
1 2 3 4
Is there a more efficient way to do this than the below?
a = np.array([ 1, 1, 1, 1, 10, 10, 20, 20, 20, 30, 40, 40, 40, 40])
ones = (a == np.pad(a, (1,0))[:-1]).astype(int)
ones[ones == 0] = -np.diff(np.concatenate(([0.], np.cumsum(ones != 0)[ones == 0])))
new_a = a + ones.cumsum()
Note that array will always be in ascending order (lowest to highest), and the numbers will always be integers, and some may be negative.
Explanation, if you don't understand:
I actually already got this working, with the help of this post. What I'm doing right now is generating an array like this, where 0 marks the first of a group of identical numbers and 1 marks the rest:
1 1 1 1 10 10 20 20 20 30 40 40 40 40
0 1 1 1 0 1 0 1 1 0 0 1 1 1
^ first 1 ^ first 10 ^ first 30
^ first 20 ^ first 40
and then using the technique from the above-linked post to cumsum all the ones in that array:
# Shift `a` by one and compare it with the original array
>>> ones = (a == np.pad(a, (1,0))[:-1]).astype(int)
>>> ones
array([0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1])
# This line is from the linked post (modified, of course)
>>> ones[ones == 0] = -np.diff(np.concatenate(([0.], np.cumsum(ones != 0)[ones == 0])))
>>> ones
array([ 0, 1, 1, 1, -3, 1, -1, 1, 1, -2, 0, 1, 1, 1])
>>> ones.cumsum()
array([0, 1, 2, 3, 0, 1, 0, 1, 2, 0, 0, 1, 2, 3])
Now, we can add that resulting array to the original one:
>>> a
array([ 1, 1, 1, 1, 10, 10, 20, 20, 20, 30, 40, 40, 40, 40])
>>> a + ones.cumsum()
array([ 1, 2, 3, 4, 10, 11, 20, 21, 22, 30, 40, 41, 42, 43])