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

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

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 (通过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

当前回答

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

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

工作原理:

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

df.columns. replicated()返回一个布尔数组:每一列为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()

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

更新和警告:请小心应用此。根据评论中dr . what提供的反例,这种解决方案可能在所有情况下都没有理想的结果。

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()

其他回答

@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

虽然@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)

如果您想检查重复的列,这段代码可能很有用

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)

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

df=df.loc[:, ~df.columns[::-1].duplicated()[::-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)

Edit

一个内存高效的版本,像对待其他值一样对待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