i panda python rimuovono le colonne duplicate


126

Qual è il modo più semplice per rimuovere colonne duplicate da un dataframe?

Sto leggendo un file di testo che ha colonne duplicate tramite:

import pandas as pd

df=pd.read_table(fname)

I nomi delle colonne sono:

Time, Time Relative, N2, Time, Time Relative, H2, etc...

Tutte le colonne Time e Time Relative contengono gli stessi dati. Voglio:

Time, Time Relative, N2, H2

Tutti i miei tentativi di eliminare, eliminare, ecc. Come:

df=df.T.drop_duplicates().T

Risultato in errori di indice valutati in modo univoco:

Reindexing only valid with uniquely valued index objects

Scusa per essere un noob dei Panda. Tutti i suggerimenti sarebbero apprezzati.


dettagli aggiuntivi

Versione Pandas: 0.9.0
Versione Python: 2.7.3
Windows 7
(installato tramite Pythonxy 2.7.3.0)

file di dati (nota: nel file reale, le colonne sono separate da tabulazioni, qui sono separate da 4 spazi):

Time    Time Relative [s]    N2[%]    Time    Time Relative [s]    H2[ppm]
2/12/2013 9:20:55 AM    6.177    9.99268e+001    2/12/2013 9:20:55 AM    6.177    3.216293e-005    
2/12/2013 9:21:06 AM    17.689    9.99296e+001    2/12/2013 9:21:06 AM    17.689    3.841667e-005    
2/12/2013 9:21:18 AM    29.186    9.992954e+001    2/12/2013 9:21:18 AM    29.186    3.880365e-005    
... etc ...
2/12/2013 2:12:44 PM    17515.269    9.991756+001    2/12/2013 2:12:44 PM    17515.269    2.800279e-005    
2/12/2013 2:12:55 PM    17526.769    9.991754e+001    2/12/2013 2:12:55 PM    17526.769    2.880386e-005
2/12/2013 2:13:07 PM    17538.273    9.991797e+001    2/12/2013 2:13:07 PM    17538.273    3.131447e-005

Quale versione di panda hai? ( import pandas as pd; pd.__version__ )
barba

1
@BirdJaguarIV, sto usando la versione 0.9.0 dei panda
Onlyjus

Potresti provare ad aggiornare a 0.10. La mia versione rende uniche le colonne con read_tableper l'esempio che ho composto.
barba

Attenzione che df = df.T.drop_duplicates (). T non considera il nome della colonna. Se hai due colonne con gli stessi dati ma nomi diversi, una verrà eliminata per errore.
Joylove

Risposte:


392

C'è una soluzione di una riga al problema. Ciò si applica se alcuni nomi di colonna sono duplicati e desideri rimuoverli:

df = df.loc[:,~df.columns.duplicated()]

Come funziona:

Supponiamo che le colonne del frame di dati siano ['alpha','beta','alpha']

df.columns.duplicated()restituisce un array booleano: a Trueo Falseper ogni colonna. Se lo è, Falseil nome della colonna è univoco fino a quel momento, se lo è Trueil nome della colonna viene duplicato in precedenza. Ad esempio, utilizzando l'esempio fornito, il valore restituito sarebbe [False,False,True].

Pandasconsente di indicizzare utilizzando valori booleani per cui seleziona solo i Truevalori. Dato che vogliamo mantenere le colonne non duplicate, abbiamo bisogno che l'array booleano sopra sia capovolto (cioè [True, True, False] = ~[False,False,True])

Infine, df.loc[:,[True,True,False]]seleziona solo le colonne non duplicate utilizzando la suddetta capacità di indicizzazione.

Nota : quanto sopra controlla solo i nomi delle colonne, non i valori delle colonne.


16
Una risposta ideale funzionerebbe anche per i valori duplicati, non solo per i nomi.
GrimSqueaker

7
@GrimSqueaker: Se vuoi considerare se i valori sono duplicati, vuoi qualcosa di simile df.T.drop_duplicates().T.
John Zwinck,

3
Di gran lunga la soluzione più veloce
AtotheSiv

2
@ VaidøtasIvøška per favore guarda la seconda risposta a questa domanda
Gene Burinsky

2
@ JohnZwinck: funziona solo per piccoli dataframe, poiché c'è un limite al numero di colonne che puoi avere. Per me, ad esempio, non è riuscito per un dataframe con 100.000 righe, poiché questo produce 100.000 colonne dopo la trasposizione, il che non è possibile
Eelco van Vliet

40

Sembra che tu conosca già i nomi di colonna univoci. Se è così, allora df = df['Time', 'Time Relative', 'N2']funzionerebbe.

In caso contrario, la tua soluzione dovrebbe funzionare:

In [101]: vals = np.random.randint(0,20, (4,3))
          vals
Out[101]:
array([[ 3, 13,  0],
       [ 1, 15, 14],
       [14, 19, 14],
       [19,  5,  1]])

In [106]: df = pd.DataFrame(np.hstack([vals, vals]), columns=['Time', 'H1', 'N2', 'Time Relative', 'N2', 'Time'] )
          df
Out[106]:
   Time  H1  N2  Time Relative  N2  Time
0     3  13   0              3  13     0
1     1  15  14              1  15    14
2    14  19  14             14  19    14
3    19   5   1             19   5     1

In [107]: df.T.drop_duplicates().T
Out[107]:
   Time  H1  N2
0     3  13   0
1     1  15  14
2    14  19  14
3    19   5   1

Probabilmente hai qualcosa di specifico nei tuoi dati che lo sta rovinando. Potremmo fornire più aiuto se ci fossero più dettagli che potresti fornirci sui dati.

Modifica: come ha detto Andy, il problema è probabilmente con i titoli delle colonne duplicati.

Per un file di tabella di esempio 'dummy.csv' ho creato:

Time    H1  N2  Time    N2  Time Relative
3   13  13  3   13  0
1   15  15  1   15  14
14  19  19  14  19  14
19  5   5   19  5   1

using read_tablefornisce colonne uniche e funziona correttamente:

In [151]: df2 = pd.read_table('dummy.csv')
          df2
Out[151]:
         Time  H1  N2  Time.1  N2.1  Time Relative
      0     3  13  13       3    13              0
      1     1  15  15       1    15             14
      2    14  19  19      14    19             14
      3    19   5   5      19     5              1
In [152]: df2.T.drop_duplicates().T
Out[152]:
             Time  H1  Time Relative
          0     3  13              0
          1     1  15             14
          2    14  19             14
          3    19   5              1  

Se la tua versione non ti consente, puoi hackerare insieme una soluzione per renderli unici:

In [169]: df2 = pd.read_table('dummy.csv', header=None)
          df2
Out[169]:
              0   1   2     3   4              5
        0  Time  H1  N2  Time  N2  Time Relative
        1     3  13  13     3  13              0
        2     1  15  15     1  15             14
        3    14  19  19    14  19             14
        4    19   5   5    19   5              1
In [171]: from collections import defaultdict
          col_counts = defaultdict(int)
          col_ix = df2.first_valid_index()
In [172]: cols = []
          for col in df2.ix[col_ix]:
              cnt = col_counts[col]
              col_counts[col] += 1
              suf = '_' + str(cnt) if cnt else ''
              cols.append(col + suf)
          cols
Out[172]:
          ['Time', 'H1', 'N2', 'Time_1', 'N2_1', 'Time Relative']
In [174]: df2.columns = cols
          df2 = df2.drop([col_ix])
In [177]: df2
Out[177]:
          Time  H1  N2 Time_1 N2_1 Time Relative
        1    3  13  13      3   13             0
        2    1  15  15      1   15            14
        3   14  19  19     14   19            14
        4   19   5   5     19    5             1
In [178]: df2.T.drop_duplicates().T
Out[178]:
          Time  H1 Time Relative
        1    3  13             0
        2    1  15            14
        3   14  19            14
        4   19   5             1 

5
Sfortunatamente df['Time']seleziona tutte le serie temporali (cioè restituisce un DataFrame), e df['Time', ..]questo restituirà l'intero DataFrame.
Andy Hayden

Sì, è piuttosto noioso ... si spera sia solo una differenza di versione.
barba

2
L'utilizzo di doppie trasposizioni potrebbe avere effetti collaterali indesiderati come la conversione di tipi numerici in oggetti nel caso in cui si dispone di un df con tipi misti. Vedi: stackoverflow.com/questions/24682396/...
Petergavinkin

Questa soluzione mi dà problemi su frame di dati di grandi dimensioni: RecursionError: maximum recursion depth exceeded
Scott

La trasposizione di un frame di dati di grandi dimensioni sarà un processo lento
Kush Patel

13

La trasposizione è inefficiente per DataFrame di grandi dimensioni. Ecco un'alternativa:

def duplicate_columns(frame):
    groups = frame.columns.to_series().groupby(frame.dtypes).groups
    dups = []
    for t, v in groups.items():
        dcols = frame[v].to_dict(orient="list")

        vs = dcols.values()
        ks = dcols.keys()
        lvs = len(vs)

        for i in range(lvs):
            for j in range(i+1,lvs):
                if vs[i] == vs[j]: 
                    dups.append(ks[i])
                    break

    return dups       

Usalo in questo modo:

dups = duplicate_columns(frame)
frame = frame.drop(dups, axis=1)

modificare

Una versione efficiente in termini di memoria che tratta nans come qualsiasi altro valore:

from pandas.core.common import array_equivalent

def duplicate_columns(frame):
    groups = frame.columns.to_series().groupby(frame.dtypes).groups
    dups = []

    for t, v in groups.items():

        cs = frame[v].columns
        vs = frame[v]
        lcs = len(cs)

        for i in range(lcs):
            ia = vs.iloc[:,i].values
            for j in range(i+1, lcs):
                ja = vs.iloc[:,j].values
                if array_equivalent(ia, ja):
                    dups.append(cs[i])
                    break

    return dups

3
Funziona come un incantesimo, molto efficiente! L'utilizzo my_df.T.drop_duplicates().Tsi bloccherebbe su frame di dati di grandi dimensioni.
Will

1
Bella soluzione ma il 26 aprile 2017 ho ricevuto /usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:17: DeprecationWarning: 'pandas.core.common.array_equivalent' is deprecated and is no longer public API
George Fisher,

sostituire if array_equivalent(ia, ja):con if np.array_equal(ia, ja):sembra produrre gli stessi risultati ma ho letto che non gestisce bene NaNs.
George Fisher,

@GeorgeFisher Il codice sottostante per è array_equivalentancora disponibile nel repository pubblico, possibilmente su un ramo più vecchio?
kalu

@kalu ora c'è una corrente numpy.array_equiv; per i panda, non vedo alcun ramo di rilascio precedente su GitHub pandas.core.commonma forse ci sono altri posti in cui cercare
George Fisher

11

Se non sbaglio, quanto segue fa ciò che è stato chiesto senza i problemi di memoria della soluzione di trasposizione e con meno righe della funzione di @kalu, mantenendo la prima di tutte le colonne con nome simile.

Cols = list(df.columns)
for i,item in enumerate(df.columns):
    if item in df.columns[:i]: Cols[i] = "toDROP"
df.columns = Cols
df = df.drop("toDROP",1)

La tua soluzione non funziona nel mio caso, mi mostra: "ValueError: etichette ['toDROP'] non contenute nell'asse" dopo aver eseguito l'ultima riga
NuValue

4

Sembra che tu fossi sulla strada giusta. Ecco il one-liner che stavi cercando:

df.reset_index().T.drop_duplicates().T

Ma poiché non esiste un frame di dati di esempio che produca il messaggio di errore di riferimento Reindexing only valid with uniquely valued index objects, è difficile dire esattamente cosa risolverebbe il problema. se il ripristino dell'indice originale è importante per te, fai questo:

original_index = df.index.names
df.reset_index().T.drop_duplicates().reset_index(original_index).T

0

Primo passo: - Leggi la prima riga, cioè tutte le colonne, rimuovi tutte le colonne duplicate.

Secondo passo: - Infine leggi solo quelle colonne.

cols = pd.read_csv("file.csv", header=None, nrows=1).iloc[0].drop_duplicates()
df = pd.read_csv("file.csv", usecols=cols)

0

Mi sono imbattuto in questo problema in cui l'unico rivestimento fornito dalla prima risposta ha funzionato bene. Tuttavia, ho avuto la complicazione extra in cui la seconda copia della colonna aveva tutti i dati. La prima copia no.

La soluzione era creare due frame di dati suddividendo l'unico frame di dati attivando l'operatore di negazione. Una volta ottenuti i due frame di dati, ho eseguito un'istruzione di join utilizzando il filelsuffix . In questo modo, potrei quindi fare riferimento ed eliminare la colonna senza i dati.

- E


0

Il modo seguente identificherà le colonne duplicate per rivedere cosa sta andando storto nella costruzione del dataframe originariamente.

dupes = pd.DataFrame(df.columns)
dupes[dupes.duplicated()]

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.