python pandas删除重复的列

Posted

技术标签:

【中文标题】python pandas删除重复的列【英文标题】:python pandas remove duplicate columns 【发布时间】:2013-02-05 16:56:55 【问题描述】:

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

我正在通过以下方式读取具有重复列的文本文件:

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 菜鸟。任何建议将不胜感激。


其他详情

熊猫版本:0.9.0 Python 版本:2.7.3 视窗 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()]

工作原理:

假设数据框的列是['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]] 使用上述索引功能仅选择非重复列。

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

【讨论】:

理想的答案也适用于重复值,而不仅仅是名称。 @GrimSqueaker:如果你想考虑值是否重复,你需要像df.T.drop_duplicates().T这样的东西。 @JohnZwinck:这仅适用于小型数据帧,因为您可以拥有的列数是有限的。对我来说,例如,对于具有 100,000 行的数据框,它失败了,因为这在转置后会产生 100,000 列,这是不可能的 获取内存错误。 MemoryError: Unable to allocate 480. GiB for an array with shape (87494, 736334) and data type object 。这是我的数据框的形状(736334, 1312)【参考方案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 

【讨论】:

不幸的是df['Time'] 选择了所有时间序列(即返回一个DataFrame),而df['Time', ..] 这将返回整个DataFrame。 是的,这很乏味......希望这只是版本差异。 使用双转置可能会产生意想不到的副作用,例如在您拥有混合类型的 df 的情况下将数字类型转换为对象。见:***.com/questions/24682396/… 这个解决方案在大型数据帧上给我带来了问题:RecursionError: maximum recursion depth exceeded 大数据帧的转置会很慢【参考方案3】:

转置对于大型 DataFrame 效率低下。这是一个替代方案:

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)

【讨论】:

您的解决方案在我的情况下不起作用,它在执行最后一行后向我显示:“ValueError: labels ['toDROP'] not contains in axis”【参考方案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 的答案更新,它使用了最新的 pandas:

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

【讨论】:

这比@kalu 的回答慢了大约 10 倍 慢是 Python 的一个特性,而不是一个错误。 ;-)【参考方案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】:

按值删除重复列的快速简便方法:

df = df.T.drop_duplicates().T

更多信息:Pandas DataFrame drop_duplicates manual。

【讨论】:

以上是关于python pandas删除重复的列的主要内容,如果未能解决你的问题,请参考以下文章

删除pandas数据帧中的重复项后,替换特定的列值

如何在pandas DataFrame中选择和删除具有重复名称的列

Pandas - 具有重复值的列的外部连接

Python/Pandas - 按列值删除重复行

有条件地删除重复的pandas python

有条件地删除重复的pandas python