Python熊猫删除重复的列

从数据框架中删除重复列的最简单方法是什么?

我正在阅读一个文本文件,通过重复的列:

import pandas as pd


df=pd.read_table(fname)

列名为:

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

所有“时间”和“时间相对”列包含相同的数据。我想要:

Time, Time Relative, N2, H2

我所有的尝试删除,删除等,如:

df=df.T.drop_duplicates().T

导致唯一值的索引错误:

Reindexing only valid with uniquely valued index objects

对不起,我是熊猫的菜鸟。任何建议将不胜感激。


额外的细节

熊猫版本:0.9.0
Python版本:2.7.3
Windows 7 < br > (通过Pythonxy 2.7.3.0安装)

数据文件(注:在实际文件中,列之间以制表符分隔,此处以4个空格分隔):

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
374838 次浏览

听起来好像您已经知道了唯一的列名。如果是这种情况,那么df = df['Time', 'Time Relative', 'N2']将工作。

如果不是,你的解决方案应该工作:

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

您可能有一些特定于您的数据的东西搞砸了。如果你能提供更多关于数据的细节,我们会给予更多的帮助。

<强>编辑: 就像Andy说的,问题可能是重复的列标题

对于一个示例表文件'dummy.csv',我创建了:

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

使用read_table提供唯一的列并正常工作:

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

如果你的版本不允许,你可以拼凑一个解决方案,使它们独一无二:

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

转置对于大数据帧来说效率很低。这里有一个替代方案:

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

像这样使用它:

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

编辑

一个内存高效的版本,像对待其他值一样对待nan:

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

如果我没有弄错的话,下面的代码没有转置解决方案的内存问题,并且比@kalu函数的行数更少,保留任何类似名称列的第一个。

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)

下面是一个基于重复列名删除列的单行解决方案:

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

工作原理:

假设数据帧的列为['alpha','beta','alpha']

df.columns.duplicated()返回一个布尔数组:每个列的TrueFalse。如果它是False,则列名在此之前是唯一的,如果它是True,则列名在之前被复制。例如,使用给定的示例,返回值将是[False,False,True]

Pandas允许使用布尔值进行索引,因此它只选择True值。由于我们想要保留未复制的列,我们需要翻转上面的布尔数组(即[True, True, False] = ~[False,False,True])。

最后,df.loc[:,[True,True,False]]使用前面提到的索引功能只选择非重复的列。

最后一个.copy()用于复制数据帧,以(主要)避免在稍后尝试修改现有数据帧时出错。

请注意:上面只检查列名,列值。

删除重复索引

因为它足够相似,所以在索引上做同样的事情:

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

通过检查值而不换位来删除重复项

df = df.loc[:,~df.apply(lambda x: x.duplicated(),axis=1).all()].copy()

这避免了转位的问题。它快吗?不。这有用吗?是的。来,试试这个:

# create a large(ish) dataframe
ldf = pd.DataFrame(np.random.randint(0,100,size= (736334,1312)))




#to see size in gigs
#ldf.memory_usage().sum()/1e9 #it's about 3 gigs


# duplicate a column
ldf.loc[:,'dup'] = ldf.loc[:,101]


# take out duplicated columns by values
ldf = ldf.loc[:,~ldf.apply(lambda x: x.duplicated(),axis=1).all()].copy()

第一步:-读取第一行,即所有列,删除所有重复的列。

第二步:最后只读那些专栏。

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

我遇到了这个问题,第一个答案提供的一行工作得很好。但是,我遇到了额外的复杂情况,即列的第二个副本拥有所有数据。第一份拷贝没有。

解决方案是通过切换否定操作符来分割一个数据帧,从而创建两个数据帧。一旦我有了这两个数据帧,我使用lsuffix运行了一个连接语句。这样,我就可以引用和删除没有数据的列。

- E

2021年3月更新

@CircArgs随后的帖子可能提供了一个简洁的单行程序来完成我在这里描述的内容。

看来你的选择是对的。这是你一直在寻找的一句俏皮话:

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

但是由于没有生成引用错误消息Reindexing only valid with uniquely valued index objects的示例数据帧,因此很难确切地说什么可以解决问题。如果恢复原始索引对你来说很重要,那么这样做:

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

下面的方法将识别dupe列,以检查最初构建数据框架时出错的地方。

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

快速和简单的方法删除复制列的值:

df = df.T.drop_duplicates().T

更多信息:Pandas DataFrame drop_duplicate manual

请注意,Gene Burinsky的答案(在编写所选答案时)保留了每个重复列的第一列。保留最后一个:

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

@kalu的回答更新了一下,用了最新的熊猫:

def find_duplicated_columns(df):
dupes = []


columns = df.columns


for i in range(len(columns)):
col1 = df.iloc[:, i]
for j in range(i + 1, len(columns)):
col2 = df.iloc[:, j]
# break early if dtypes aren't the same (helps deal with
# categorical dtypes)
if col1.dtype is not col2.dtype:
break
# otherwise compare values
if col1.equals(col2):
dupes.append(columns[i])
break


return dupes

以防有人还在寻找如何在Python中为Pandas数据帧的列中寻找重复值的答案,我想出了这个解决方案:

def get_dup_columns(m):
'''
This will check every column in data frame
and verify if you have duplicated columns.
can help whenever you are cleaning big data sets of 50+ columns
and clean up a little  bit for you
The result will be a list of tuples showing what columns are duplicates
for example
(column A, Column C)
That means that column A is duplicated with column C
more info go to https://wanatux.com
'''
headers_list = [x for x in m.columns]
duplicate_col2 = []
y = 0
while y <= len(headers_list)-1:
for x in range(1,len(headers_list)-1):
if m[headers_list[y]].equals(m[headers_list[x]]) == False:
continue
else:
duplicate_col2.append((headers_list[y],headers_list[x]))
headers_list.pop(0)
return duplicate_col2

你可以像这样强制转换定义:

duplicate_col = get_dup_columns(pd_excel)

它将显示如下结果:

 [('column a', 'column k'),
('column a', 'column r'),
('column h', 'column m'),
('column k', 'column r')]
虽然@Gene Burinsky的回答很好,但它有一个潜在的问题,重新分配的df可能是原始df的副本或视图。 这意味着像df['newcol'] = 1这样的后续赋值会生成SettingWithCopy警告,并且可能会失败(https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#why-does-assignment-fail-when-using-chained-indexing)。以下解决方案可以避免该问题:

duplicate_cols = df.columns[df.columns.duplicated()]
df.drop(columns=duplicate_cols, inplace=True)

我不知道为什么吉恩·布林斯基的答案对我不起作用。我得到了相同的原始数据框架与重复的列。我的解决方法是强制选择ndarray并返回数据框架。

df = pd.DataFrame(df.values[:,~df.columns.duplicated()], columns=df.columns[~df.columns.duplicated()])

简单的列比较是检查按值复制列最有效的方法(就记忆与时间而言)。这里有一个例子:

import numpy as np
import pandas as pd
from itertools import combinations as combi


df = pd.DataFrame(np.random.uniform(0,1, (100,4)), columns=['a','b','c','d'])
df['a'] = df['d'].copy()  # column 'a' is equal to column 'd'


# to keep the first
dupli_cols = [cc[1] for cc in combi(df.columns, r=2) if (df[cc[0]] == df[cc[1]]).all()]


# to keep the last
dupli_cols = [cc[0] for cc in combi(df.columns, r=2) if (df[cc[0]] == df[cc[1]]).all()]
            

df = df.drop(columns=dupli_cols)