python pandas删除重复的列


125

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

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

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

您有什么版本的熊猫?(import pandas as pd; pd.__version__
beardc

1
@BirdJaguarIV,我使用的是熊猫版本0.9.0
Onlyjus 2013年

您可能要尝试升级到0.10。read_table对于我组成的示例,我的版本使列具有唯一性。
beardc

请注意,df = df.T.drop_duplicates()。T不会考虑列名。如果您有两列具有相同的数据但名称不同,则将错误地删除其中一列。
Joylove

Answers:


390

有一个解决方案。如果某些列名重复并且您希望删除它们,则适用此规则:

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

这个怎么运作:

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

df.columns.duplicated()返回一个布尔数组:a TrueFalse每列。如果是,False则该列名称在该点之前是唯一的;如果是,True则该列名称在前面已重复。例如,使用给定的示例,返回值为[False,False,True]

Pandas允许使用布尔值建立索引,从而仅选择True值。由于我们要保留不重复的列,因此需要翻转上面的布尔数组(即[True, True, False] = ~[False,False,True]

最后,df.loc[:,[True,True,False]]使用上述索引功能仅选择非重复列。

注意:以上内容仅检查列名称,而不检查列值。


16
理想的答案也适用于重复的值,而不仅仅是名称。
GrimSqueaker

7
@GrimSqueaker:如果要考虑值是否重复,则需要类似的内容df.T.drop_duplicates().T
John Zwinck '18年

3
迄今为止最快的解决方案
AtotheSiv

2
@VaidøtasIvøška,请参阅该问题
Gene Burinsky,

2
@JohnZwinck:这仅适用于小型数据框,因为您可以拥有的列数是有限的。对我来说,它对于包含100,000行的数据帧失败,因为这在转置后会产生100,000列,这是不可能的
Eelco van Vliet

40

听起来您已经知道唯一的列名。如果是这样,那就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 

5
不幸的是,df['Time']选择了所有时间序列(即返回一个DataFrame),df['Time', ..]这将返回整个DataFrame。
安迪·海登

是的,这很繁琐...希望这只是版本差异。
beardc

2
使用双重转置可能会产生意想不到的副作用,例如在具有混合类型的df的情况下将数字类型转换为对象。参见:stackoverflow.com/questions/24682396/…–
Petergavinkin

这种解决方案在大型数据帧上给我带来了问题: RecursionError: maximum recursion depth exceeded
Scott

大数据帧的转置将是缓慢的过程
库什·帕特尔

13

对于大型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)

编辑

一种高效的内存版本,可像其他任何值一样对待nans:

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

3
作品就像魅力,非常有效率!使用my_df.T.drop_duplicates().T将挂在大型数据帧上。
2016年

1
可爱的解决方案,但在2017年4月26日,我得到了 /usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:17: DeprecationWarning: 'pandas.core.common.array_equivalent' is deprecated and is no longer public API
George Fisher

更换if array_equivalent(ia, ja):if np.array_equal(ia, ja):似乎产生相同的结果,但我读了它不能处理的NaN好。
乔治·费舍尔

@GeorgeFisher是否array_equivalent仍可在公共存储库中使用基础代码,可能还可以在较旧的分支上使用?
kalu '17

@kalu现在有潮流了numpy.array_equiv;对于熊猫,我在GitHub上看不到任何较早的发行版本,pandas.core.common但也许还有其他地方值得一看
George Fisher

11

如果我没有记错的话,下面的操作可以解决问题,而不会出现转置解决方案的内存问题,并且行数少于@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:在轴中未包含标签['toDROP']”
NuValue

4

看来您在正确的道路上。这是您要寻找的一线客:

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

0

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

第二步:-最后仅读取该列。

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

0

我遇到了这个问题,第一个答案提供的衬里效果很好。但是,我的麻烦之处在于该列的第二个副本包含所有数据。第一份没有。

解决方案是通过切换否定运算符拆分一个数据帧来创建两个数据帧。拥有两个数据框后,我使用lsuffix。这样,我就可以引用和删除没有数据的列。

-E


0

下面的方法将识别重复列,以查看最初构建数据框时出了什么问题。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.