डुप्लिकेट सूचकांकों वाली पंक्तियों को निकालें (पंडों डेटाफ्रैम और टाइमसेरीज)


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
डुप्लिकेट प्राप्त करने का एक और तरीका रात में डेटा है जब घड़ियों को दिन के समय की बचत के लिए वापस सेट किया जाता है: 1 AM, 2, 3, 2, 3 फिर से, 4 ...
denis

जवाबों:


467

मैं पंडों इंडेक्स पर डुप्लिकेट किए गए तरीके का उपयोग करने का सुझाव दूंगा:

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

जबकि अन्य सभी विधियां काम करती हैं, वर्तमान में स्वीकृत जवाब प्रदान किए गए उदाहरण के लिए अब तक का सबसे कम प्रदर्शन है। इसके अलावा, जबकि ग्रुपबी विधि केवल थोड़ी कम परफॉर्मेंट है, मुझे लगता है कि डुप्लिकेटेड विधि अधिक पठनीय होगी।

उपलब्ध कराए गए नमूना डेटा का उपयोग करना:

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

ध्यान रखें कि आप अंतिम तर्क को बदलकर अंतिम तत्व रख सकते हैं।

यह भी ध्यान दिया जाना चाहिए कि यह विधि साथ 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
यह बहुत बड़ी समय-श्रृंखला के लिए इसका उपयोग करने के लिए समझ में आएगा जहां डुप्लिकेट आमतौर पर सिर्फ पहले या अंतिम मान हैं?
चीज 12

1
df3 = df3.loc में क्या करता है [~ df3.index.duplicated (Keep = 'first')] अगर किसी को जवाब देने में कोई आपत्ति नहीं है?
jsl5703

3
@ jsl5703 यह मुखौटा को उलट देता है। तो यह सब कुछ बदल जाता है जो सच गलत था और इसके विपरीत। इस मामले में, इसका मतलब है कि हम उन लोगों का चयन करेंगे जो विधि के अनुसार नकल नहीं करते हैं।
n8yoder

115

मेरा मूल उत्तर, जो अब पुराना हो चुका है, संदर्भ के लिए रखा गया है।

उपयोग करने के लिए एक सरल उपाय है drop_duplicates

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

मेरे लिए, यह बड़े डेटा सेट पर जल्दी से संचालित होता है।

इसके लिए आवश्यक है कि 'पंक्तिबद्ध' डुप्लिकेट वाला कॉलम हो। संशोधित उदाहरण में, 'पंक्तिबद्ध' में कोई डुप्लिकेट नहीं है, इसलिए कुछ भी समाप्त नहीं होता है। हम वास्तव में जो चाहते हैं, वह यह है कि सूचकांक में 'कोल्स' को सेट किया जाए। मुझे केवल इंडेक्स पर विचार करने के लिए 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')
लुसियानो

हालांकि यह विधि काम करती है, यह DataFrame की दो अस्थायी प्रतियां भी बनाती है और वैकल्पिक उत्तर के रूप में सुझाए गए डुप्लिकेट इंडेक्स या ग्रुपबी विधियों का उपयोग करने की तुलना में काफी कम प्रदर्शन करती है।
n8yoder

यदि आपका अनुक्रमणिका एक MultiIndex है, तो 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)भी यह मूर्खतापूर्ण नहीं है (अनाम मल्टीएंडेक्स के लिए काम नहीं करेगा)।
hobs

1
इंडेक्स को एक कॉलम में ले जाना, डुप्लिकेट को साफ़ करना, और इंडेक्स को रीसेट करना बहुत ही बढ़िया था, ठीक उसी तरह जो मुझे चाहिए था!
mxplusb 19

यह देखते हुए idx = df.index.name or 'index', कोई भी df2 = df.reset_index(); df2.drop_duplicates(idx, inplace=True); df2.set_index(idx, inplace=True)इंटरमीडिएट की कॉपियों से बचने के लिए ऐसा नहीं कर सकता था (- के कारण inplace=True)
अनखंड

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

यदि उनके नाम हैं, अन्यथा (यदि एक नाम कोई नहीं है) level=[0,1]तो कहने दें कि यदि 2 स्तर हैं तो काम करेंगे df1.groupby(level=[0,1]).last()। यह drop_duplicates
पांडवों के

@ दादाजी हाँ। df.index.namesइंडेक्स के सभी स्तरों द्वारा समूह का उपयोग करना एक आसान तरीका है।
पॉल एच।

महान समाधान, धन्यवाद! मैं यह भी जोड़ूंगा कि यह xarrayडुप्लीकेट डेटाइम इंडेक्स के साथ काम करने के लिए काम करता है और साथ ही यह विफल हो जाता है ds.resampleऔर ds.groupbyसंचालन विफल हो जाता है
dr

मेरे पहले टिप्पणी में संशोधन: यह काम करता है xarrayजब तक आप को बदलने के रूप में grouped = df3.groupby(level=0)करने के लिए grouped = df3.groupby(dim='time')या जो कुछ भी आयाम है कि डुप्लिकेट शामिल
drg

4

दुर्भाग्य से, मुझे नहीं लगता कि पंडों ने सूचकांकों से किसी को भी ड्रॉप करने की अनुमति दी है। मैं निम्नलिखित सुझाव दूंगा:

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.. क्या यह वास्तव में आपके लिए काम कर रहा था?
ओनो एबरहार्ड

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

टेस्ट: ओपी के डेटा का उपयोग करके 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.