Pythonパンダは重複した列を削除します


126

データフレームから重複する列を削除する最も簡単な方法は何ですか?

私は重複した列を持つテキストファイルを読んでいます:

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

0.10にアップグレードしてみてください。私のバージョンでは、read_table私が作成した例で列を一意にしています。
beardc 2013

df = df.T.drop_duplicates()。Tは列名を考慮しないことに注意してください。同じデータで名前が異なる2つの列がある場合、1つが誤ってドロップされます。
Joylove

回答:


392

この問題には1行の解決策があります。これは、一部の列名が重複していて、それらを削除したい場合に適用されます。

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

使い方:

データフレームの列が ['alpha','beta','alpha']

df.columns.duplicated()ブール配列を返します:a TrueまたはFalse各列。そうであるFalse場合、列名はその時点まで一意であり、そうであるTrue場合、列名は以前に複製されます。たとえば、指定された例を使用すると、戻り値はになります[False,False,True]

Pandasブール値を使用してインデックスを作成できるため、True値のみが選択されます。重複しない列を保持したいので、上記のブール配列を反転する必要があります(つまり[True, True, False] = ~[False,False,True]

最後に、df.loc[:,[True,True,False]]前述の索引付け機能を使用して、重複していない列のみを選択します。

:上記は列名ではなく列名のみをチェックします。


16
理想的な答えは、名前だけでなく重複した値にも有効です。
GrimSqueaker 2018年

7
@GrimSqueaker:値が重複しているかどうかを検討する場合は、のようなものが必要ですdf.T.drop_duplicates().T
ジョンズウィンク2018

3
断然最速のソリューション
AtotheSiv 2018年

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 2013

2
二重転置を使用すると、タイプが混在するdfがある場合に数値タイプをオブジェクトに変換するなど、意図しない副作用が発生する可能性があります。参照:stackoverflow.com/questions/24682396/…–
Petergavinkin

このソリューションは、大規模なデータフレームの上に私に問題を与える: RecursionError: maximum recursion depth exceeded
スコット

大きなデータフレームの転置は処理が遅くなります
Kush Patel

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)

編集する

ナンを他の値と同様に扱うメモリ効率の良いバージョン:

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すると、大きなデータフレームでハングアップします。
ウィル

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
ジョージフィッシャー

と置き換えif array_equivalent(ia, ja):if np.array_equal(ia, ja):も同じ結果が得られるようですが、NaNをうまく処理できないことを読みました。
ジョージフィッシャー

@GeorgeFisherの基礎となるコードはarray_equivalent、おそらく古いリポジトリのパブリックリポジトリでまだ利用できますか?
kalu

@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:labels ['toDROP'] not included in axis」という値が表示されます
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

最初のステップ:-最初の行、つまりすべての列を読み取り、重複する列をすべて削除します。

2番目のステップ:-最後に、その列のみを読み取ります。

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

0

私は最初の回答で提供された1つのライナーがうまく機能するこの問題に遭遇しました。ただし、列の2番目のコピーにすべてのデータが含まれていたため、さらに複雑になりました。最初のコピーはしませんでした。

解決策は、否定演算子を切り替えて1つのデータフレームを分割して2つのデータフレームを作成することでした。2つのデータフレームを取得したら、を使用して結合ステートメントを実行しましたlsuffix。このようにして、データのない列を参照して削除できます。

-E


0

以下の方法では、重複する列を特定して、データフレームの最初の構築で何が問題になっているのかを確認します。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.