python pandas menghapus kolom duplikat

126

Apa cara termudah untuk menghapus kolom duplikat dari kerangka data?

Saya membaca file teks yang memiliki kolom duplikat melalui:

import pandas as pd

df=pd.read_table(fname)

Nama kolomnya adalah:

Time, Time Relative, N2, Time, Time Relative, H2, etc...

Semua kolom Relatif Waktu dan Waktu berisi data yang sama. Saya ingin:

Time, Time Relative, N2, H2

Semua upaya saya untuk menjatuhkan, menghapus, dll seperti:

df=df.T.drop_duplicates().T

Menghasilkan kesalahan indeks dengan nilai unik:

Reindexing only valid with uniquely valued index objects

Maaf telah menjadi noob Pandas. Setiap Saran akan dihargai.


detil tambahan

Versi Pandas: 0.9.0
Python Versi: 2.7.3
Windows 7
(diinstal melalui Pythonxy 2.7.3.0)

file data (catatan: di file asli, kolom dipisahkan oleh tab, di sini dipisahkan oleh 4 spasi):

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
Onlyjus
sumber
Versi panda apa yang Anda miliki? ( import pandas as pd; pd.__version__ )
beardc
1
@BirdJaguarIV, saya menggunakan pandas versi 0.9.0
Onlyjus
Anda mungkin ingin mencoba meningkatkan ke 0,10. Versi saya membuat kolom unik dengan read_tablecontoh yang saya buat.
beardc
Berhati-hatilah karena df = df.T.drop_duplicates (). T tidak mempertimbangkan nama kolom. Jika Anda memiliki dua kolom dengan data yang sama tetapi nama yang berbeda, salah satunya akan dihapus secara keliru.
Joylove

Jawaban:

392

Ada solusi satu baris untuk masalah ini. Ini berlaku jika beberapa nama kolom diduplikasi dan Anda ingin menghapusnya:

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

Bagaimana itu bekerja:

Misalkan kolom dari bingkai data adalah ['alpha','beta','alpha']

df.columns.duplicated()mengembalikan array boolean: a Trueatau Falseuntuk setiap kolom. Jika sudah Falsemaka nama kolom tersebut unik sampai pada titik tersebut, jika sudah Truemaka nama kolom tersebut sudah terduplikasi tadi. Misalnya, dengan menggunakan contoh yang diberikan, nilai yang dikembalikan adalah [False,False,True].

Pandasmemungkinkan seseorang untuk mengindeks menggunakan nilai boolean di mana ia hanya memilih Truenilainya. Karena kita ingin mempertahankan kolom yang tidak digandakan, kita membutuhkan array boolean di atas untuk dibalik (yaitu [True, True, False] = ~[False,False,True])

Terakhir, df.loc[:,[True,True,False]]pilih hanya kolom yang tidak diduplikasi menggunakan kemampuan pengindeksan yang disebutkan di atas.

Catatan : di atas hanya memeriksa nama kolom, bukan nilai kolom.

Gene Burinsky
sumber
16
Jawaban yang ideal juga dapat digunakan untuk nilai duplikat, bukan hanya nama.
GrimSqueaker
7
@ GrimSqueaker: Jika Anda ingin mempertimbangkan apakah nilai diduplikasi, Anda menginginkan sesuatu seperti df.T.drop_duplicates().T.
John Zwinck
3
Sejauh ini solusi tercepat
AtotheSiv
2
@ VaidøtasIvøška silakan lihat jawaban kedua untuk pertanyaan
Gene Burinsky
2
@JohnZwinck: ini hanya berfungsi untuk dataframe kecil, karena ada batasan jumlah kolom yang dapat Anda miliki. Bagi saya itu gagal untuk kerangka data dengan 100.000 baris misalnya, karena ini menghasilkan 100.000 kolom setelah transposing, yang tidak mungkin
Eelco van Vliet
40

Sepertinya Anda sudah mengetahui nama kolom yang unik. Jika itu masalahnya, maka df = df['Time', 'Time Relative', 'N2']akan berhasil.

Jika tidak, solusi Anda akan berhasil:

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

Anda mungkin memiliki sesuatu yang spesifik untuk data Anda yang mengacaukannya. Kami dapat memberikan bantuan lebih lanjut jika ada detail lebih lanjut yang dapat Anda berikan kepada kami tentang data.

Sunting: Seperti kata Andy, masalahnya mungkin dengan judul kolom duplikat.

Untuk file tabel sampel 'dummy.csv' saya membuat:

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

menggunakan read_tablememberikan kolom unik dan berfungsi dengan baik:

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  

Jika versi Anda tidak memungkinkan, Anda dapat meretas solusi untuk membuatnya unik:

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 
jenggot
sumber
5
Sayangnya df['Time']memilih semua Time series (yaitu mengembalikan DataFrame), dan df['Time', ..]ini akan mengembalikan seluruh DataFrame.
Andy Hayden
Ya, itu cukup membosankan ... semoga itu hanya perbedaan versi.
beardc
2
Menggunakan transposisi ganda dapat memiliki efek samping yang tidak diinginkan seperti mengubah tipe numerik menjadi objek jika Anda memiliki df dengan tipe campuran. Lihat: stackoverflow.com/questions/24682396/…
Petergavinkin
Solusi ini memberi saya masalah pada kerangka data besar: RecursionError: maximum recursion depth exceeded
Scott
Transposisi bingkai data besar akan menjadi proses yang lambat
Kush Patel
13

Transposing tidak efisien untuk DataFrames besar. Berikut ini alternatifnya:

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       

Gunakan seperti ini:

dups = duplicate_columns(frame)
frame = frame.drop(dups, axis=1)

Edit

Versi hemat memori yang memperlakukan nans seperti nilai lainnya:

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
kalu
sumber
3
Bekerja seperti pesona, sangat efisien! Menggunakan my_df.T.drop_duplicates().Takan tergantung pada dataframe besar.
Akankah
1
Solusi yang bagus tetapi pada 26 April 2017 saya mendapat /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
mengganti if array_equivalent(ia, ja):dengan if np.array_equal(ia, ja):tampaknya menghasilkan hasil yang sama tetapi saya membaca bahwa itu tidak menangani NaN dengan baik.
George Fisher
@GeorgeFisher Apakah kode yang mendasari array_equivalentmasih tersedia di repo publik, mungkin di cabang yang lebih lama?
kalu
@kalu sekarang ada arus numpy.array_equiv; untuk panda, saya tidak melihat cabang rilis sebelumnya di GitHub, pandas.core.commontetapi mungkin ada tempat lain untuk dilihat
George Fisher
12

Jika saya tidak salah, berikut ini melakukan apa yang diminta tanpa masalah memori dari solusi transpose dan dengan lebih sedikit baris dari fungsi @kalu, menjaga kolom pertama dari setiap kolom bernama serupa.

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)
Elliott Collins
sumber
Solusi Anda tidak berfungsi dalam kasus saya, ini menunjukkan kepada saya: "ValueError: label ['toDROP'] tidak terdapat dalam sumbu" setelah menjalankan baris terakhir
NuValue
4

Sepertinya Anda berada di jalan yang benar. Ini satu baris yang Anda cari:

df.reset_index().T.drop_duplicates().T

Tetapi karena tidak ada kerangka data contoh yang menghasilkan pesan kesalahan yang direferensikan Reindexing only valid with uniquely valued index objects, sulit untuk mengatakan dengan tepat apa yang akan menyelesaikan masalah. jika memulihkan indeks asli penting bagi Anda, lakukan ini:

original_index = df.index.names
df.reset_index().T.drop_duplicates().reset_index(original_index).T
Tony B
sumber
0

Langkah pertama: - Baca baris pertama, yaitu semua kolom, hapus semua kolom duplikat.

Langkah kedua: - Akhirnya hanya membaca kolom itu.

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

Saya mengalami masalah ini di mana satu baris yang disediakan oleh jawaban pertama bekerja dengan baik. Namun, saya memiliki kerumitan ekstra di mana salinan kolom kedua memiliki semua data. Salinan pertama tidak.

Solusinya adalah membuat dua bingkai data dengan memisahkan satu bingkai data dengan mengubah operator negasi. Setelah saya memiliki dua bingkai data, saya menjalankan pernyataan gabungan menggunakan lsuffix. Dengan cara ini, saya kemudian dapat mereferensikan dan menghapus kolom tanpa data.

- E

Edmund's Echo
sumber
0

Cara di bawah ini akan mengidentifikasi kolom dupe untuk meninjau apa yang salah dalam membangun kerangka data aslinya.

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