Python Pandas eliminar columnas duplicadas

¿Cuál es la forma más fácil de eliminar columnas duplicadas de un dataframe?

Estoy leyendo un archivo de texto que tiene columnas duplicadas a través de:

import pandas as pd df=pd.read_table(fname) 

Los nombres de las columnas son:

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

Todas las columnas Relative de Time y Time contienen los mismos datos. Quiero:

 Time, Time Relative, N2, H2 

Todos mis bashs de eliminar, eliminar, etc., tales como:

 df=df.T.drop_duplicates().T 

Resultado en errores de índice de valor único:

 Reindexing only valid with uniquely valued index objects 

Lo siento por ser un pandas noob. Cualquier sugerencia sera apreciada.


Detalles adicionales

Versión de pandas: 0.9.0
Versión de Python: 2.7.3
Windows 7
(instalado a través de Pythonxy 2.7.3.0)

archivo de datos (nota: en el archivo real, las columnas están separadas por tabs, aquí están separadas por 4 espacios):

 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 

Todo lo anterior parece ser métodos innecesariamente pesados ​​y tediosos: hay una solución de una sola línea para el problema. Esto se aplica si algunos nombres de columna están duplicados y desea eliminarlos:

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

[actualización] Cómo funciona:

Supongamos que las columnas del dataframe son ['alpha','beta','alpha']

df.columns.duplicated() devuelve una matriz booleana: una True o False para cada columna. Si es False , el nombre de la columna es único hasta ese punto, si es True , el nombre de la columna se duplica antes. Por ejemplo, utilizando el ejemplo dado, el valor devuelto sería [False,False,True] .

Pandas permiten a uno indexar usando valores booleanos mediante los cuales selecciona solo los valores True . Dado que queremos mantener las columnas no duplicadas, necesitamos que se invierta la matriz booleana anterior (es decir, [True, True, False] = ~[False,False,True] )

Finalmente, df.loc[:,[True,True,False]] selecciona solo las columnas no duplicadas utilizando la capacidad de indexación mencionada anteriormente.

Nota : lo anterior solo verifica los nombres de las columnas, no los valores de las columnas.

Parece que ya sabes los nombres de columna únicos. Si ese es el caso, entonces df = df['Time', 'Time Relative', 'N2'] funcionaría.

Si no, su solución debería funcionar:

 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 

Probablemente tengas algo específico a tus datos que lo está arruinando. Podríamos brindarle más ayuda si hay más detalles que nos pueda dar sobre los datos.

Edición: como dijo Andy, el problema probablemente se deba a los títulos de columna duplicados.

Para un archivo de tabla de muestra ‘dummy.csv’ hice:

 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 

el uso de read_table da columnas únicas y funciona correctamente:

 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 

Si su versión no lo permite, puede hackear una solución para hacerlos únicos:

 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 

Si no me equivoco, lo siguiente hace lo que se pidió sin los problemas de memoria de la solución de transposición y con menos líneas que la función de @kalu, manteniendo la primera de las columnas con nombres similares.

 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 transposición es ineficiente para grandes DataFrames. Aquí hay una 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 

Úsalo así:

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

Editar

Una versión de memoria eficiente que trata a nans como cualquier otro valor:

 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