pandas Python supprimer les colonnes en double
Quel est le moyen le plus simple de supprimer les colonnes en double d'un dataframe?
Je lis un fichier texte qui a des colonnes en double via:
import pandas as pd
df=pd.read_table(fname)
Les noms des colonnes sont:
Time, Time Relative, N2, Time, Time Relative, H2, etc...
Toutes les colonnes Time et Time Relative contiennent les mêmes données. Je veux:
Time, Time Relative, N2, H2
Toutes mes tentatives de suppression, de suppression, etc. telles que:
df=df.T.drop_duplicates().T
Résultat des erreurs d'index à valeur unique:
Reindexing only valid with uniquely valued index objects
Désolé d'être un Pandas noob. Toutes les Suggestions seraient apprécier.
Détails Supplémentaires
Version Pandas: 0.9.0
Version Python: 2.7.3
Windows 7
(installé via pythonxy 2.7.3.0)
Fichier de données (note: dans le fichier réel, les colonnes sont séparées par des onglets, ici elles sont séparées par 4 espaces):
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
4 réponses
On dirait que vous connaissez déjà les noms de colonnes uniques. Si c'est le cas, alors df = df['Time', 'Time Relative', 'N2']
fonctionnerait.
Sinon, votre solution devrait fonctionner:
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
Vous avez probablement quelque chose de spécifique à vos données qui le gâche. Nous pourrions donner plus d'aide s'il y a plus de détails que vous pourriez nous donner sur les données.
Modifier: Comme Andy l'a dit, le problème est probablement avec les titres de colonne en double.
Pour un exemple de fichier de table 'dummy.csv " j'ai fait des en haut:
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
Utiliser read_table
donne des colonnes uniques et fonctionne correctement:
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 votre version ne le laisse pas, vous pouvez pirater une solution pour les rendre uniques:
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
Tout ce qui précède semble inutilement lourd et fastidieux méthodes-il y a une solution d'une ligne au problème. Cela s'applique si certains noms de colonnes sont dupliqués et que vous souhaitez les supprimer:
df = df.loc[:,~df.columns.duplicated()]
[update] comment ça marche:
Supposons que les colonnes de la trame de données sont ['alpha','beta','alpha']
df.columns.duplicated()
retourne un booléen tableau: un True
ou False
pour chaque colonne. Si c'est False
alors le nom de la colonne est unique jusqu'à ce point, s'il est True
alors le nom de la colonne est dupliqué plus tôt. Par exemple, en utilisant l'exemple donné, la valeur retournée serait [False,False,True]
.
Pandas
permet d'indexer en utilisant des valeurs booléennes par lesquelles il ne sélectionne que les valeurs True
. Puisque nous voulons garder les colonnes non dupliquées, nous avons besoin du tableau booléen ci-dessus pour être retourné (ie [True, True, False] = ~[False,False,True]
)
Enfin, df.loc[:,[True,True,False]]
sélectionne uniquement les colonnes non dupliquées en utilisant la capacité d'indexation susmentionnée.
Note : ce qui précède ne vérifie que les noms de colonnes, pas colonne valeur.
Si Je ne me trompe pas, ce qui suit fait ce qui a été demandé sans les problèmes de mémoire de la solution de transposition et avec moins de lignes que la fonction de @kalu, en gardant la première de toutes les colonnes nommées de la même manière.
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 transposition est inefficace pour les grandes trames de données. Voici une alternative:
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
Utilisez-le comme ceci:
dups = duplicate_columns(frame)
frame = frame.drop(dups, axis=1)
Modifier
Une version efficace en mémoire qui traite les Nan comme toute autre valeur:
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