Panda: Drop consecutivi duplicati

Qual è il modo più efficace per eliminare solo consecutivi duplicati in panda?

drop_duplicates dà questo:

In [3]: a = pandas.Series([1,2,2,3,2], index=[1,2,3,4,5])

In [4]: a.drop_duplicates()
Out[4]: 
1    1
2    2
4    3
dtype: int64

Ma io voglio questo:

In [4]: a.something()
Out[4]: 
1    1
2    2
4    3
5    2
dtype: int64



4 Replies
  1. 69

    Utilizzare maiusc:

    a.loc[a.shift(-1) != a]
    
    Out[3]:
    
    1    1
    3    2
    4    3
    5    2
    dtype: int64

    Così usi di cui sopra boolean criteri, si confronta il dataframe contro il dataframe spostato da -1 righe per creare la maschera

    Un altro metodo è quello di utilizzare differenza:

    In [82]:
    
    a.loc[a.diff() != 0]
    Out[82]:
    1    1
    2    2
    4    3
    5    2
    dtype: int64

    Ma questo è più lento rispetto all’originale metodo se si dispone di un gran numero di righe.

    Aggiornamento

    Grazie per Bjarke Ebert per indicare un sottile errore, io in realtà dovrebbe utilizzare shift(1) o semplicemente shift() come il valore di default è un periodo di 1, questo restituisce il primo consecutivi valore:

    In [87]:
    
    a.loc[a.shift() != a]
    Out[87]:
    1    1
    2    2
    4    3
    5    2
    dtype: int64

    Nota la differenza nei valori di indice, grazie @BjarkeEbert!

    • Ho avuto lo stesso problema e google piacerebbe trovare questa domanda. Wow, che bello e semplice soluzione! 😀
    • ma hm, si potrebbe voler dire.loc[a.shift(1) != a], al fine di ottenere il primo dei valori consecutivi, come specificato nella domanda 🙂
    • nessuna preoccupazione, felice di aiutare, ho notato il tuo commento e hai ragione, la riga risultante valori è stato quello che l’OP voleva, ma i valori dell’indice sono state sbagliate le righe come hai correttamente potined fuori, è interessante notare che utilizzando diff è stata più lenta per un 50k serie, probabilmente a causa del valore di confronto
    • Inoltre, df.col != df.col.shift() è molto più generale. Utilizzando diff funziona solo per i numeri interi, mentre shift opere per i galleggianti, stringhe, etc.
    • sì, è vero, grazie per la segnalazione
    • Che è una grande soluzione. Ma qui il problema ha una sola colonna. Se non ci sono più colonne ? Dovrebbe cambiare lavoro ? Io sono sempre ValueError: Non è indice con multidimensionale chiave
    • non funziona in questo caso, se si dispone di una questione, quindi, si dovrebbe inserire una domanda, difficile commentare senza vedere un esempio
    • Bene – cosa che ho fatto è stata – ho creato una nuova colonna che hanno aderito tutte le altre colonne. E poi ho usato come di seguito df = df.loc[df[‘allthecols’].shift() != df[‘allthecols’]]
    • Con i valori NaN, questo non funziona, dal momento che np.nan == np.nan restituisce False. C’è un modo per trattare questi valori NaN come identico vuoto?
    • Dovresti fare a.loc[(a.notnull()) & (a.shift() != a)] per gestire il NaN righe
    • Wow, era davvero facile!

  2. 10

    Qui è un aggiornamento che permetterà di lavorare con più colonne. Utilizzare “.qualsiasi(asse=1)” per combinare i risultati di ogni colonna:

    cols = ["col1","col2","col3"]
    de_dup = a[cols].loc[(a[cols].shift() != a[cols]).any(axis=1)]
    • questo sembra causare un KeyError
    • Si può aggiungere un po ‘ più di contesto?
    • Funziona troppo bene, ho 4 colonne e la necessità di rimuovere il ‘prossimo’ riga se 3 colonne partita. johnml lavorato, ma ho perso la 4 ° colonna.
    • si può fare de_dup = a.loc[(a[cols].shift() != a[cols]).any(axis=1)]
  3. 4

    Dal momento che stiamo andando per most efficient way, vale a dire le prestazioni, proviamo ad usare l’array di dati di sfruttare NumPy. Noi fetta one-off fette e confrontare, simile a shifting metodo descritto in precedenza in @EdChum's post. Ma con NumPy affettare finiremmo con meno array, quindi abbiamo bisogno di concatenare con un True elemento di avvio per selezionare il primo elemento e quindi ci sarebbe un’implementazione simile così

    def drop_consecutive_duplicates(a):
        ar = a.values
        return a[np.concatenate(([True],ar[:-1]!= ar[1:]))]

    Campione di esecuzione

    In [149]: a
    Out[149]: 
    1    1
    2    2
    3    2
    4    3
    5    2
    dtype: int64
    
    In [150]: drop_consecutive_duplicates(a)
    Out[150]: 
    1    1
    2    2
    4    3
    5    2
    dtype: int64

    Tempi sulle matrici di grandi dimensioni a confronto @EdChum soluzione

    In [142]: a = pd.Series(np.random.randint(1,5,(1000000)))
    
    In [143]: %timeit a.loc[a.shift() != a]
    100 loops, best of 3: 12.1 ms per loop
    
    In [144]: %timeit drop_consecutive_duplicates(a)
    100 loops, best of 3: 11 ms per loop
    
    In [145]: a = pd.Series(np.random.randint(1,5,(10000000)))
    
    In [146]: %timeit a.loc[a.shift() != a]
    10 loops, best of 3: 136 ms per loop
    
    In [147]: %timeit drop_consecutive_duplicates(a)
    10 loops, best of 3: 114 ms per loop

    Così, c’è qualche miglioramento!

    Ottenere spinta importante per valori solo!

    Se solo i valori sono necessari, si potrebbe ottenere un notevole impulso, semplicemente indicizzazione nella matrice di dati, in questo modo –

    def drop_consecutive_duplicates(a):
        ar = a.values
        return ar[np.concatenate(([True],ar[:-1]!= ar[1:]))]

    Campione di esecuzione

    In [170]: a = pandas.Series([1,2,2,3,2], index=[1,2,3,4,5])
    
    In [171]: drop_consecutive_duplicates(a)
    Out[171]: array([1, 2, 3, 2])

    Tempi –

    In [173]: a = pd.Series(np.random.randint(1,5,(10000000)))
    
    In [174]: %timeit a.loc[a.shift() != a]
    10 loops, best of 3: 137 ms per loop
    
    In [175]: %timeit drop_consecutive_duplicates(a)
    10 loops, best of 3: 61.3 ms per loop
    • Non capisco perché timing per [147] e [175] differiscono? Puoi spiegare di quale modifica hai fatto perchè io non vedo tutte? Forse un errore di battitura?
    • Inoltre, il vostro lavoro di funzione per dataframes?
    • è con la modifica della Get major boost for values only! sezione uno, quindi il tempo diff. L’originale funziona su panda-Serie mentre quello modificato su array come indicato anche nel post.
    • Oh, vedo. Difficile notare il cambiamento da return a[...] vs return ar[....]. Fa il tuo lavoro di funzione per dataframes?
    • Per dataframes, se siete alla ricerca di righe duplicate, abbiamo semplicemente bisogno di utilizzare slicing : ar[:,:-1]!= ar[:,1:], alongwith ALL riduzione.
    • Grazie. Proverò

  4. 0

    Per altri Stack esploratori, edificio johnml1135 la risposta di sopra. Questo rimuoverà la prossima duplicati da più colonne, ma non cadere tutte le colonne. Quando il dataframe è ordinato terrà la prima fila, ma la goccia la seconda riga se il “cols” partita, anche se ci sono più colonne con non corrispondenti informazioni.

    cols = ["col1","col2","col3"]
    df = df.loc[(df[cols].shift() != df[cols]).any(axis=1)]

Lascia un commento