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

웹에서 자동화 된 날씨 데이터를 읽고 있습니다. 관측치는 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(또는 ???) 문을 알아 내야합니다 .



답변

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


답변

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

간단한 해결책은 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()


답변

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

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


답변

불행히도, 나는 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!


답변

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

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

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

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


답변

중복 제거 (먼저 유지)

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


답변