python pandas 删除重复的列
- 2025-01-08 08:50:00
- admin 原创
- 97
问题描述:
从数据框中删除重复列的最简单方法是什么?
我正在通过以下方式读取包含重复列的文本文件:
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
抱歉,我是 Pandas 菜鸟。如有任何建议,我们将不胜感激。
更多详细信息
Pandas 版本:0.9.0
Python 版本:2.7.3
Windows 7
(通过 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
解决方案 1:
以下是根据重复列名删除列的一行解决方案:
df = df.loc[:,~df.columns.duplicated()].copy()
工作原理:
假设数据框的列为['alpha','beta','alpha']
df.columns.duplicated()
返回一个布尔数组:每个列为True
或False
。如果是,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()
通过检查值而不转置来删除重复项
更新和警告:请小心应用此方法。根据 DrWhat 在评论中提供的反例,此解决方案可能并非在所有情况下都能获得预期结果。
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()
解决方案 2:
听起来您已经知道唯一的列名。如果是这样,那么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
您的数据可能存在一些问题。如果您能提供更多有关数据的详细信息,我们可以提供更多帮助。
编辑:
就像安迪说的,问题可能出在重复的列标题上。
对于示例表文件“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
解决方案 3:
对于大型 DataFrames 来说,转置效率低下。这里有一个替代方案:
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
解决方案 4:
如果我没记错的话,下面的操作会完成所要求的操作,而不会出现转置解决方案的内存问题,并且比@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)
解决方案 5:
看来你走对了路。以下是你要找的一行代码:
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
解决方案 6:
请注意,Gene Burinsky 的答案(在撰写所选答案时)保留每个重复列的第一个。要保留最后一个:
df=df.loc[:, ~df.columns[::-1].duplicated()[::-1]]
解决方案 7:
@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
解决方案 8:
尽管@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)
解决方案 9:
我遇到了这个问题,第一个答案提供的一行代码运行良好。然而,我遇到了额外的麻烦,即列的第二个副本包含所有数据。第一个副本没有。
解决方案是通过切换否定运算符来拆分一个数据框,从而创建两个数据框。有了两个数据框后,我使用 运行了连接语句lsuffix
。这样,我就可以引用和删除没有数据的列。
-E
2021 年 3 月更新
@CircArgs 的后续帖子可能提供了简洁的一行代码来完成我在此处描述的内容。
解决方案 10:
第一步: - 读取第一行,即所有列,删除所有重复的列。
第二步:- 最后只读取该列。
cols = pd.read_csv("file.csv", header=None, nrows=1).iloc[0].drop_duplicates()
df = pd.read_csv("file.csv", usecols=cols)
解决方案 11:
下面的方法将识别重复的列,以检查最初构建数据框时出了什么问题。
dupes = pd.DataFrame(df.columns)
dupes[dupes.duplicated()]
解决方案 12:
万一有人仍在寻找如何在 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')]
解决方案 13:
我不确定为什么 Gene Burinsky 的答案对我不起作用。我得到的是相同的原始数据框,但有重复的列。我的解决方法是强制选择 ndarray 并返回数据框。
df = pd.DataFrame(df.values[:,~df.columns.duplicated()], columns=df.columns[~df.columns.duplicated()])
解决方案 14:
简单的列比较是按值检查重复列的最有效方法(就内存和时间而言) 。这里有一个例子:
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)
解决方案 15:
如果你想检查重复的列,此代码可能会有用
columns_to_drop= []
for cname in sorted(list(df)):
for cname2 in sorted(list(df))[::-1]:
if df[cname].equals(df[cname2]) and cname!=cname2 and cname not in columns_to_drop:
columns_to_drop.append(cname2)
print(cname,cname2,'Are equal')
df = df.drop(columns_to_drop, axis=1)
解决方案 16:
如果您确定列中的值相同,并且只想根据列名进行过滤,则可以尝试以下操作:
# generate a list of unique column names
l_h = list(set(df.columns.tolist()))
# determine list length to be used for filtering the data frame
index = len(l_h)
# filter dataframe
df[l_h].iloc[:,-index:]
解决方案 17:
快速简便地根据值删除重复列的方法:
df = df.T.drop_duplicates().T
更多信息:Pandas DataFrame drop_duplicates 手册。