Digamos que tengo la siguiente matriz de enteros de orden ascendente (algunos pueden ser negativos):
a = np.array([ 1, 1, 1, 1, 10, 10, 20, 20, 20, 30, 40, 40, 40, 40])Quiero convertirlo en esto:
a = np.array([ 1, 2, 3, 4, 10, 11, 20, 21, 22, 30, 40, 41, 42, 43])...donde cada entero en cada grupo de los mismos enteros se incrementa, así que para los primeros 1:
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¿Hay una manera más eficiente de hacer esto que la siguiente?
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()Tenga en cuenta que la matriz siempre estará en orden ascendente (de menor a mayor), y los números siempre serán enteros, y algunos pueden ser negativos.
Explicación, si no entiendes:
De hecho, ya lo hice funcionar, con la ayuda de esta publicación . Lo que estoy haciendo ahora es generar una matriz como esta, donde 0 marca el primero de un grupo de números idénticos y 1 marca el resto:
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 40y luego usando la técnica de la publicación vinculada anteriormente para acumular todos los que están en esa matriz:
# 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])Ahora, podemos agregar esa matriz resultante a la original:
>>> 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])Usar np.unique podría ser un poco más elegante:
u, i = np.unique(a, return_index=True) # Indices where the sums restart b = np.ones_like(a) b[i] = u b[i[1:]] -= np.add.reduceat(b, i)[:-1] # Subtract the sum of the prior region from the next result = b.cumsum() Dado que la matriz ya está ordenada, puede acceder directamente a esa parte de np.unique :
i = np.r_[0, np.flatnonzero(np.diff(a)) + 1] # Get the indices directly from the diff b = np.ones_like(a) b[i] = a[i] b[i[1:]] -= np.add.reduceat(b, i)[:-1] result = b.cumsum()Pero espera, la suma de cada región es solo la longitud más el valor inicial menos uno. Eso elimina la necesidad de sumar dos veces:
i = np.r_[0, np.flatnonzero(np.diff(a)) + 1] b = np.ones_like(a) b[i] = a[i] b[i[1:]] -= np.diff(i) + a[i[:-1]] - 1 # Simpler way to sum the prior region result = b.cumsum() Puedes simplificar un poco más. Dado que a[i[k]] es el comienzo de una ejecución, a[i[k] - 1] es lo mismo que a[i[k - 1]] . En otras palabras, el inicio de la ejecución anterior es el mismo que el último elemento de la ejecución anterior:
d = np.diff(a) i = np.r_[0, np.flatnonzero(d) + 1] b = np.ones_like(a) b[0] = a[0] b[i[1:]] = d[i[1:] - 1] - np.diff(i) + 1 # Current region minus prior, reusing diff result = b.cumsum()Cualquiera de las dos últimas versiones debería ser mejor que lo que está haciendo actualmente.
El código anterior está escrito para simplificar y acelerar. Si desea hacerlo más corto e ilegible, y está utilizando Python 3.8+, puede comenzar a usar el operador morsa:
i = np.r_[0, np.flatnonzero(d := np.diff(a)) + 1] (b := np.ones_like(a))[0] = a[0] b[i[1:]] = d[i[1:] - 1] - np.diff(i) + 1 result = b.cumsum()Dado que walrus evalúa de izquierda a derecha, puede crear una parodia final:
(b := np.ones_like(a))[0] = a[0] b[(i := np.r_[0, np.flatnonzero(d := np.diff(a)) + 1])[1:]] = d[i[1:] - 1] - np.diff(i) + 1 result = b.cumsum()Similar para el otro enfoque:
(b := np.ones_like(a))[i := np.r_[0, np.flatnonzero(np.diff(a)) + 1]] = a[i] b[i[1:]] -= np.diff(i) + a[i[:-1]] - 1 result = b.cumsum()No estoy seguro de si esto es muy eficiente, pero es una sola línea:
np.hstack([x + np.r_[:x.size] for x in np.split(a, np.flatnonzero(np.diff(a))+1)])