Come allungare elementi specifici dell'array numpy con decremento?

Sep 05 2020

Dato il valore limite k, esiste un modo vettorizzato per sostituire ogni numero ncon numeri discendenti consecutivi da n-1a k? Ad esempio, se kè 0, vorrei sostituire np.array([3,4,2,2,1,3,1])con np.array([2,1,0,3,2,1,0,1,0,1,0,0,2,1,0,0]). Ogni elemento della matrice di input è maggiore di k.

Ho provato la combinazione di np.repeate np.cumsumma sembra una soluzione evasiva:

x = np.array([3,4,2,2,1,3,1])
y = np.repeat(x, x)
t = -np.ones(y.shape[0])
t[np.r_[0, np.cumsum(x)[:-1]]] = x-1
np.cumsum(t)

C'è un altro modo? Mi aspetto che smth come l'inverso di np.add.reduceatquello sia in grado di trasmettere interi a sequenze decrescenti invece di minimizzarle.

Risposte

2 Divakar Sep 05 2020 at 15:03

Ecco un altro modo con l'assegnazione di array per saltare la parte di ripetizione:

def func1(a):
    l = a.sum()
    out = np.full(l, -1, dtype=int)
    out[0] = a[0]-1
    idx = a.cumsum()[:-1]
    out[idx] = a[1:]-1
    return out.cumsum()

Analisi comparativa

# OP's soln
def OP(x):
    y = np.repeat(x, x)
    t = -np.ones(y.shape[0], dtype=int)
    t[np.r_[0, np.cumsum(x)[:-1]]] = x-1
    return np.cumsum(t)

Utilizzo del benchitpacchetto (pochi strumenti di benchmarking confezionati insieme; disclaimer: sono il suo autore) per confrontare le soluzioni proposte.

import benchit

a = np.array([3,4,2,2,1,3,1])
in_ = [np.resize(a,n) for n in [10, 100, 1000, 10000]]
funcs = [OP, func1]
t = benchit.timings(funcs, in_)
t.plot(logx=True, save='timings.png')

Estendi per prendere kcome arg

def func1(a, k):
    l = a.sum()+len(a)*(-k)
    out = np.full(l, -1, dtype=int)
    out[0] = a[0]-1
    idx = (a-k).cumsum()[:-1]
    out[idx] = a[1:]-1-k
    return out.cumsum()

Esempio di corsa -

In [120]: a
Out[120]: array([3, 4, 2, 2, 1, 3, 1])

In [121]: func1(a, k=-1)
Out[121]: 
array([ 2,  1,  0, -1,  3,  2,  1,  0, -1,  1,  0, -1,  1,  0, -1,  0, -1,
        2,  1,  0, -1,  0, -1])
anon01 Sep 05 2020 at 14:13

Questo è conciso e probabilmente va bene per l'efficienza; Non penso che applysia vettorializzato qui, quindi sarai limitato principalmente dal numero di elementi nell'array originale (meno che il loro valore sia la mia ipotesi):

import pandas as pd
x = np.array([3,4,2,2,1,3,1])

values = pd.Series(x).apply(lambda val: np.arange(val-1,-1,-1)).values
output = np.concatenate(values)