python pandas ลบคอลัมน์ที่ซ้ำกัน


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

ขอโทษที่เป็นนุ่น noob ข้อเสนอแนะใด ๆ จะได้รับการชื่นชม


รายละเอียดเพิ่มเติม

เวอร์ชัน 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

ระวังว่า df = df.T.drop_duplicates () T ไม่พิจารณาชื่อคอลัมน์ หากคุณมีคอลัมน์สองคอลัมน์ที่มีข้อมูลเดียวกัน แต่ชื่อต่างกันคอลัมน์หนึ่งคอลัมน์จะตกหล่นอย่างไม่ถูกต้อง
Joylove

คำตอบ:


392

มีวิธีแก้ปัญหาแบบบรรทัดเดียว สิ่งนี้จะมีผลหากชื่อคอลัมน์บางชื่อซ้ำกันและคุณต้องการลบออก:

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

7
@GrimSqueaker: df.T.drop_duplicates().Tถ้าคุณต้องการที่จะพิจารณาว่าค่าที่จะทำซ้ำที่คุณต้องการสิ่งที่ต้องการ
John Zwinck

3
วิธีแก้ปัญหาที่เร็วที่สุด
AtotheSiv

2
@ VaidøtasIvøškaโปรดดูคำตอบที่ 2 สำหรับคำถาม
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

คุณอาจมีบางอย่างเฉพาะกับข้อมูลของคุณที่ทำให้ข้อมูลยุ่งเหยิง เราสามารถให้ความช่วยเหลือเพิ่มเติมหากมีรายละเอียดเพิ่มเติมที่คุณสามารถแจ้งให้เราทราบเกี่ยวกับข้อมูล

แก้ไข: เช่นเดียวกับที่ Andy กล่าวว่าปัญหาอาจเกิดจากชื่อคอลัมน์ที่ซ้ำกัน

สำหรับไฟล์ตารางตัวอย่าง '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 ทั้งหมด
Andy Hayden

ใช่มันค่อนข้างน่าเบื่อ ... หวังว่าจะเป็นเพียงความแตกต่างของเวอร์ชัน
beardc

2
การใช้การเปลี่ยนสองครั้งอาจมีผลข้างเคียงที่ไม่ได้ตั้งใจเช่นการแปลงประเภทตัวเลขเป็นวัตถุในกรณีที่คุณมี df ที่มีประเภทผสมกัน ดู: stackoverflow.com/questions/24682396/…
Petergavinkin

วิธีนี้ทำให้ฉันมีปัญหากับดาต้าเฟรมขนาดใหญ่: RecursionError: maximum recursion depth exceeded
สก็อตต์

การเปลี่ยนกรอบข้อมูลขนาดใหญ่จะดำเนินการช้า
Kush Patel

13

การถ่ายโอนข้อมูลไม่มีประสิทธิภาพสำหรับ DataFrames ขนาดใหญ่ นี่คือทางเลือก:

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จะแขวนบนดาต้าเฟรมขนาดใหญ่
จะ

1
ทางออกที่น่ารัก แต่เมื่อวันที่ 26 เมษายน 2017 ฉันได้รับ /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 ได้ดี
George Fisher

@GeorgeFisher รหัสอ้างอิงสำหรับarray_equivalentยังคงมีอยู่ใน repo สาธารณะอาจอยู่ในสาขาที่เก่ากว่าหรือไม่
kalu

@kalu ตอนนี้มีกระแสnumpy.array_equiv; สำหรับแพนด้าฉันไม่เห็นสาขาที่วางจำหน่ายก่อนหน้านี้บน GitHub pandas.core.commonแต่อาจมีที่อื่นให้ดู
George Fisher

12

ถ้าฉันจำไม่ผิดสิ่งต่อไปนี้จะทำสิ่งที่ถามโดยไม่มีปัญหาหน่วยความจำของโซลูชันการเปลี่ยนภาพและมีบรรทัดน้อยกว่าฟังก์ชันของ @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: label ['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

ฉันพบปัญหานี้ซึ่งซับหนึ่งที่ให้มาในคำตอบแรกทำงานได้ดี อย่างไรก็ตามฉันมีความซับซ้อนเพิ่มเติมที่สำเนาที่สองของคอลัมน์มีข้อมูลทั้งหมด สำเนาแรกไม่ได้

วิธีแก้ปัญหาคือการสร้างเฟรมข้อมูลสองเฟรมโดยการแยกเฟรมข้อมูลหนึ่งโดยการสลับตัวดำเนินการปฏิเสธ เมื่อฉันมีสองเฟรมข้อมูลฉันก็รันคำสั่ง join โดยใช้lsuffix . ด้วยวิธีนี้ฉันสามารถอ้างอิงและลบคอลัมน์โดยไม่มีข้อมูลได้

- จ


0

วิธีการด้านล่างนี้จะระบุคอลัมน์ที่หลอกลวงเพื่อตรวจสอบสิ่งที่ผิดพลาดในการสร้างดาต้าเฟรมในตอนแรก

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.