Come ottengo un elenco delle righe duplicate nei panda?
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
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])
reset_index
quindi groupby
aggiungiagg
l = df.reset_index().groupby(list(df))['index'].agg(list).tolist()
Out[291]: [[1000084, 1000092, 1000116], [1000096, 1000110]]
pandas
ha una propria funzione duplicated()
che restituirebbe tutte le righe duplicate.
duplicated_rows = df[df.duplicated(subset=['col1', 'col2', 'col3'], keep=False)]
Secondo la documentazione ,
subset
può essere un elenco delle colonne selezionate che devono essere controllate per i duplicati. Per impostazione predefinita, utilizza tutte le colonne.keep
è impostato perFalse
mantenere 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.
Aggiungendo un altro approccio con pd.factorize
eIndex.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}