Come ottengo un elenco delle righe duplicate nei panda?

Aug 21 2020

Ho un frame di big data, con oltre 100 mila variabili (righe) in 358 campioni.

Voglio sapere quali variabili sono identiche (duplicate) in tutti i campioni.

Un frame di dati di esempio è come:

         Sample1 Sample2 Sample3 Sample4 Sample5
1000084  0.0     0.0     0.0     0.0     0.0    
1000092  0.0     0.0     0.0     0.0     0.0
1000096  0.0     0.0     1.0     0.0     0.0
1000110  0.0     0.0     1.0     0.0     0.0
1000116  0.0     0.0     0.0     0.0     0.0

Il risultato di cui ho bisogno potrebbe essere qualcosa del genere: o un elenco di elenchi di righe identiche

 {1000084:[1000092, 1000116], 1000096:[1000110]}

Ho provato il metodo duplicato dai panda, ma lascerà solo gli elementi unici o gli elementi unici più il primo o l'ultimo duplicato.

Ho provato con questo codice ma sta impiegando anni:

duplicated_index = set()
duplicates = {}

for i, pos in enumerate(df.index, 0):
    #check if the row has marked as duplicate, if so, ignore it
    if i in duplicated_index:
        continue
    for j in range(i+1, df.shape[0]):
        if all(df.iloc[i] == df.iloc[j]):
             duplicated_index.add(j)
             tmp = duplicates.setdefault(pos, [])
             duplicates[pos].append(df.iloc[j].name)

Esiste un modo più appropriato per ottenere questo elenco e identificare quali righe sono identiche a quale altro?

Risposte

3 wwii Aug 21 2020 at 21:43

Raggruppa tutte le colonne; trova gruppi con più di un elemento e inseriscili in un elenco. Utilizza un ciclo for.

>>> gb = df.groupby(df.columns.to_list())
>>> d = {}
>>> for a,b in gb:
...     if len(b) > 1:
...         d[b.index[0]] = b.index[1:].to_list()

        
>>> d
{1000084: [1000092, 1000116], 1000096: [1000110]}
>>>

Utilizzando lo stesso groupby come sopra, scrivi una funzione per restituire l'indice per un gruppo e costruisci un dizionario utilizzando il metodo aggregato .

def f(thing):
    return thing.index.to_list()

>>> {key:val for key,*val in gb.aggregate(f) if val}
{1000084: [1000092, 1000116], 1000096: [1000110]}

Sembra che il tempo di esecuzione per questo scala linearmente con il numero di colonne e righe (numero di elementi).


Ecco un grande DataFrame per i test. Sfortunatamente non vuole produrre righe duplicate - forse questo è il caso peggiore per groupby quindi iterare?

import itertools,string
import numpy as np
nrows,ncols = 100000,300

a = np.random.randint(1,3,(nrows,ncols))
# or using the new random stuff
#from numpy.random import default_rng
#rng = default_rng()
#a = rng.integers(1,3,(nrows,ncols))

index = np.arange(1000000,1000000+nrows,dtype=np.int64)
cols = [''.join(thing) for thing in itertools.combinations(string.ascii_letters,3)]
df2 = pd.DataFrame(data=a,index=index,columns=cols[:ncols])
2 BENY Aug 21 2020 at 21:37

reset_indexquindi groupbyaggiungiagg

l = df.reset_index().groupby(list(df))['index'].agg(list).tolist()
Out[291]: [[1000084, 1000092, 1000116], [1000096, 1000110]]
1 gunsodo Aug 21 2020 at 21:29

pandasha una propria funzione duplicated()che restituirebbe tutte le righe duplicate.

duplicated_rows = df[df.duplicated(subset=['col1', 'col2', 'col3'], keep=False)]

Secondo la documentazione ,

  • subsetpuò essere un elenco delle colonne selezionate che devono essere controllate per i duplicati. Per impostazione predefinita, utilizza tutte le colonne.
  • keepè impostato per Falsemantenere tutte le occorrenze.

Se vuoi il risultato come un elenco di elenchi, potrebbe essere una piccola modifica del codice che hai abbozzato sopra probabilmente risolverebbe il tuo problema.

1 anky Aug 21 2020 at 22:33

Aggiungendo un altro approccio con pd.factorizeeIndex.groupby

idx = pd.factorize(list(map(tuple,df.to_numpy().tolist())))[0]

d = {g[0]: [*g[1:]] for _,g in df.index.groupby(idx).items() if len(g)>1}

{1000084: [1000092, 1000116], 1000096: [1000110]}

O con df.to_records()ma può essere più lento del metodo precedente:

idx = pd.factorize(df.to_records(index=False))[0]
d = {g[0]: [*g[1:]] for _,g in df.index.groupby(idx).items() if len(g)>1}