중복 인덱스가있는 행 제거 (Pandas DataFrame 및 TimeSeries)


252

웹에서 자동화 된 날씨 데이터를 읽고 있습니다. 관측치는 5 분마다 발생하며 각 기상 관측소에 대한 월별 파일로 컴파일됩니다. 파일 파싱을 마치면 DataFrame은 다음과 같습니다.

                      Sta  Precip1hr  Precip5min  Temp  DewPnt  WindSpd  WindDir  AtmPress
Date                                                                                      
2001-01-01 00:00:00  KPDX          0           0     4       3        0        0     30.31
2001-01-01 00:05:00  KPDX          0           0     4       3        0        0     30.30
2001-01-01 00:10:00  KPDX          0           0     4       3        4       80     30.30
2001-01-01 00:15:00  KPDX          0           0     3       2        5       90     30.30
2001-01-01 00:20:00  KPDX          0           0     3       2       10      110     30.28

내가 겪고있는 문제는 때로는 과학자가 잘못된 행을 편집하는 것이 아니라 파일 끝에 중복 행을 추가하여 관측을 수정한다는 것입니다. 이러한 경우의 간단한 예는 다음과 같습니다.

import pandas 
import datetime
startdate = datetime.datetime(2001, 1, 1, 0, 0)
enddate = datetime.datetime(2001, 1, 1, 5, 0)
index = pandas.DatetimeIndex(start=startdate, end=enddate, freq='H')
data1 = {'A' : range(6), 'B' : range(6)}
data2 = {'A' : [20, -30, 40], 'B' : [-50, 60, -70]}
df1 = pandas.DataFrame(data=data1, index=index)
df2 = pandas.DataFrame(data=data2, index=index[:3])
df3 = df2.append(df1)
df3
                       A   B
2001-01-01 00:00:00   20 -50
2001-01-01 01:00:00  -30  60
2001-01-01 02:00:00   40 -70
2001-01-01 03:00:00    3   3
2001-01-01 04:00:00    4   4
2001-01-01 05:00:00    5   5
2001-01-01 00:00:00    0   0
2001-01-01 01:00:00    1   1
2001-01-01 02:00:00    2   2

그래서 나는 df3고르게 해야 합니다.

                       A   B
2001-01-01 00:00:00    0   0
2001-01-01 01:00:00    1   1
2001-01-01 02:00:00    2   2
2001-01-01 03:00:00    3   3
2001-01-01 04:00:00    4   4
2001-01-01 05:00:00    5   5

행 번호 열 ( df3['rownum'] = range(df3.shape[0])) 을 추가하면의 값에 대해 맨 아래 행을 선택하는 데 도움이 될 것이라고 생각 DatetimeIndex했지만 그 작업을 수행하기 위해 group_by또는 pivot(또는 ???) 문을 알아 내야합니다 .


1
3 1을 다시 AM, 2, 3, 2, 4 ... : 중복을 얻는 또 다른 방법은 시계가 일광 절약 시간 설정 돌아 밤에 시간당 데이터입니다
데니스

답변:


467

Pandas Index 자체 에서 복제 된 방법을 사용하는 것이 좋습니다 .

df3 = df3.loc[~df3.index.duplicated(keep='first')]

다른 모든 방법이 작동하지만 현재 허용되는 답변 은 제공된 예제에서 성능이 가장 낮습니다. 또한 groupby 방법 은 성능이 약간 떨어지지 만 복제 된 방법이 더 읽기 쉽습니다.

제공된 샘플 데이터 사용 :

>>> %timeit df3.reset_index().drop_duplicates(subset='index', keep='first').set_index('index')
1000 loops, best of 3: 1.54 ms per loop

>>> %timeit df3.groupby(df3.index).first()
1000 loops, best of 3: 580 µs per loop

>>> %timeit df3[~df3.index.duplicated(keep='first')]
1000 loops, best of 3: 307 µs per loop

keep 인수를 변경하여 마지막 요소를 유지할 수 있습니다.

또한이 방법은 Paul의 예제에MultiIndex 지정된 df1을 사용하여 작동 합니다 .

>>> %timeit df1.groupby(level=df1.index.names).last()
1000 loops, best of 3: 771 µs per loop

>>> %timeit df1[~df1.index.duplicated(keep='last')]
1000 loops, best of 3: 365 µs per loop

3
loc필요하지 않을 수도 있습니다. 간단히 수행 df3 = df3[~df3.index.duplicated(keep='first')]하면 첫 번째 항목을 제외하고 중복 인덱스가있는 모든 행이 삭제됩니다.
lingjiankong

1
중복이 일반적으로 첫 번째 또는 마지막 값인 매우 큰 시계열에 이것을 사용하는 것이 합리적입니까?
cheesus

1
누군가 대답에 신경 쓰지 않으면 df3 = df3.loc [~ df3.index.duplicated (keep = 'first')]에서 무엇을합니까?
jsl5703

3
@ jsl5703 마스크를 뒤집습니다. 따라서 True False와 그 반대의 모든 것을 돌립니다. 이 경우 방법에 따라 복제되지 않은 것을 선택합니다.
n8yoder

115

구식 인 원래의 대답은 참조 용으로 유지되었습니다.

간단한 해결책은 drop_duplicates

df4 = df3.drop_duplicates(subset='rownum', keep='last')

나에게 이것은 큰 데이터 세트에서 빠르게 작동했습니다.

이를 위해서는 'rownum'이 중복 된 열이어야합니다. 수정 된 예에서 'rownum'에는 복제본이 없으므로 제거 할 것이 없습니다. 우리가 정말로 원하는 것은 'cols'를 인덱스로 설정하는 것입니다. drop_duplicates에게 인덱스 만 고려하도록 지시하는 방법을 찾지 못했습니다.

다음은 인덱스를 데이터 프레임 열로 추가하고 중복을 제거한 다음 새 열을 제거하는 솔루션입니다.

df3 = df3.reset_index().drop_duplicates(subset='index', keep='last').set_index('index')

그리고 올바른 순서로 물건을 다시 원하면 sort데이터 프레임을 호출 하십시오.

df3 = df3.sort()

10
이에 대한 또 다른 변형은 다음과 같습니다.df.reset_index().drop_duplicates(cols='index',take_last=True).set_index('index')
Luciano

이 방법이 작동하는 동안 DataFrame의 두 개의 임시 복사본도 만들어지며 대체 답변으로 제안 된 중복 된 인덱스 또는 groupby 방법을 사용하는 것보다 성능이 떨어집니다.
n8yoder

색인이 다중 색인 인 경우 reset_index()level_0, level_1 등의 열을 추가합니다. 색인 이름이 있으면 색인 이름이 "인덱스"레이블 대신 사용됩니다. 따라서 모든 DataFrame에 대해 올바른 작업을 수행하는 것이 하나의 라이너 이상입니다. index_label = getattr(df.index, 'names', getattr(df.index, 'name', 'index'))그런 cols=index_label다음 set_index(index_labels)조차도 완벽하지 않습니다 (이름없는 다중 색인에는 작동하지 않음).
호브

1
인덱스를 열로 옮기고 중복을 지우고 인덱스를 재설정하는 것이 굉장했습니다.
mxplusb

주어진 idx = df.index.name or 'index', df2 = df.reset_index(); df2.drop_duplicates(idx, inplace=True); df2.set_index(idx, inplace=True)중간 사본을 피하기 위해 할 수도 있습니다 (으로 인해 inplace=True)
Anakhand

67

어머. 이것은 실제로 매우 간단합니다!

grouped = df3.groupby(level=0)
df4 = grouped.last()
df4
                      A   B  rownum

2001-01-01 00:00:00   0   0       6
2001-01-01 01:00:00   1   1       7
2001-01-01 02:00:00   2   2       8
2001-01-01 03:00:00   3   3       3
2001-01-01 04:00:00   4   4       4
2001-01-01 05:00:00   5   5       5

후속 조치 편집 2013-10-29 상당히 복잡한 MultiIndex경우 groupby접근 방식을 선호한다고 생각합니다 . 후손에 대한 간단한 예는 다음과 같습니다.

import numpy as np
import pandas

# fake index
idx = pandas.MultiIndex.from_tuples([('a', letter) for letter in list('abcde')])

# random data + naming the index levels
df1 = pandas.DataFrame(np.random.normal(size=(5,2)), index=idx, columns=['colA', 'colB'])
df1.index.names = ['iA', 'iB']

# artificially append some duplicate data
df1 = df1.append(df1.select(lambda idx: idx[1] in ['c', 'e']))
df1
#           colA      colB
#iA iB                    
#a  a  -1.297535  0.691787
#   b  -1.688411  0.404430
#   c   0.275806 -0.078871
#   d  -0.509815 -0.220326
#   e  -0.066680  0.607233
#   c   0.275806 -0.078871  # <--- dup 1
#   e  -0.066680  0.607233  # <--- dup 2

여기 중요한 부분이 있습니다

# group the data, using df1.index.names tells pandas to look at the entire index
groups = df1.groupby(level=df1.index.names)  
groups.last() # or .first()
#           colA      colB
#iA iB                    
#a  a  -1.297535  0.691787
#   b  -1.688411  0.404430
#   c   0.275806 -0.078871
#   d  -0.509815 -0.220326
#   e  -0.066680  0.607233

이름이 있다면, 그렇지 않으면 (하나의 이름이 None이라면) level=[0,1]2 개의 레벨이 있으면 작동 할 것 입니다 df1.groupby(level=[0,1]).last(). 이것은 무료로 Pandas의 일부가되어야합니다.drop_duplicates
dashesy

@dashesy 예. 사용 df.index.names은 모든 수준의 색인으로 그룹화하는 쉬운 방법입니다.
Paul H

좋은 해결책, 감사합니다! 또한이 작품 것을 추가 할 것 xarray같은 중복 날짜 시간 인덱스를 다루는 잘 만드는 것이 ds.resampleds.groupby작업이 실패
DRG

내 이전 의견에 개정 : 그것은 작동 xarray당신이 변경만큼 grouped = df3.groupby(level=0)으로 grouped = df3.groupby(dim='time')또는 중복 포함 어떤 차원이
DRG

4

불행히도, 나는 Pandas가 인덱스에서 딥을 떨어 뜨릴 수 있다고 생각하지 않습니다. 나는 다음을 제안 할 것이다 :

df3 = df3.reset_index() # makes date column part of your data
df3.columns = ['timestamp','A','B','rownum'] # set names
df3 = df3.drop_duplicates('timestamp',take_last=True).set_index('timestamp') #done!

1

나와 같은 사람이 팬더 점 표기법 (파이프와 같은)을 사용하여 체인 가능한 데이터 조작을 좋아한다면 다음이 유용 할 수 있습니다.

df3 = df3.query('~index.duplicated()')

이를 통해 다음과 같은 체인 문을 사용할 수 있습니다.

df3.assign(C=2).query('~index.duplicated()').mean()

나는 이것을 시도했지만 그것을 작동시키지 못했습니다. 나는 다음과 같은 오류가 발생합니다 : TypeError: 'Series' objects are mutable, thus they cannot be hashed.. 이것이 실제로 당신을 위해 작동 했습니까?
Onno Eberhard

1

중복 제거 (먼저 유지)

idx = np.unique( df.index.values, return_index = True )[1]
df = df.iloc[idx]

중복 제거 (최종 보관)

df = df[::-1]
df = df.iloc[ np.unique( df.index.values, return_index = True )[1] ]

테스트 : OP 데이터를 사용한 10k 루프

numpy method - 3.03 seconds
df.loc[~df.index.duplicated(keep='first')] - 4.43 seconds
df.groupby(df.index).first() - 21 seconds
reset_index() method - 29 seconds
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.