आप एक सूची से डुप्लिकेट कैसे हटाते हैं जो ऑर्डर को संरक्षित करते हैं?


769

क्या कोई अंतर्निहित अंतर्निहित पायथन सूची में डुप्लिकेट को हटाता है, जबकि संरक्षण क्रम है? मुझे पता है कि मैं डुप्लिकेट को निकालने के लिए एक सेट का उपयोग कर सकता हूं, लेकिन यह मूल आदेश को नष्ट कर देता है। मुझे यह भी पता है कि मैं इस तरह से अपना रोल कर सकती हूं:

def uniq(input):
  output = []
  for x in input:
    if x not in output:
      output.append(x)
  return output

( उस कोड नमूने के लिए अवकाश के लिए धन्यवाद ।)

लेकिन अगर संभव हो तो मैं एक अंतर्निहित या अधिक पायथोनिक मुहावरे का लाभ उठाना चाहता हूं।

संबंधित प्रश्न: पायथन में, किसी सूची से डुप्लिकेट को हटाने के लिए सबसे तेज़ एल्गोरिथम क्या है ताकि आदेश को संरक्षित करते समय सभी तत्व अद्वितीय हों ?

जवाबों:


762

यहां आपके पास कुछ विकल्प हैं: http://www.peterbe.com/plog/uniqifiers-benchmark

सबसे तेज़ एक:

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

सिर्फ कॉल seen.addकरने के seen_addबजाय क्यों असाइन करें seen.add? पायथन एक गतिशील भाषा है, और seen.addप्रत्येक पुनरावृत्ति को हल करना स्थानीय चर को हल करने की तुलना में अधिक महंगा है। seen.addपुनरावृत्तियों के बीच परिवर्तन हो सकता है, और रनटाइम उस नियम को पूरा करने के लिए पर्याप्त स्मार्ट नहीं है। इसे सुरक्षित खेलने के लिए, इसे हर बार ऑब्जेक्ट को जांचना होगा।

यदि आप एक ही डेटासेट पर इस फ़ंक्शन का उपयोग करने की योजना बनाते हैं, तो शायद आप एक ऑर्डर किए गए सेट के साथ बेहतर होंगे: http://code.activestate.com/recipes/528878/

(1) सम्मिलन, विलोपन और सदस्य-जांच प्रति ऑपरेशन।

(छोटा अतिरिक्त नोट: seen.add()हमेशा लौटता है None, इसलिए orउपरोक्त केवल एक सेट अपडेट का प्रयास करने के तरीके के रूप में है, न कि तार्किक परीक्षण के अभिन्न अंग के रूप में।)


20
@JesseDhillon seen.addपुनरावृत्तियों के बीच बदल सकता था, और रनटाइम उस नियम को बनाने के लिए पर्याप्त स्मार्ट नहीं है। सुरक्षित खेलने के लिए, इसे हर बार ऑब्जेक्ट को जांचना होगा। - यदि आप के साथ bytecode dis.dis(f)को देखते हैं, तो आप देख सकते हैं कि यह प्रत्येक पुनरावृत्ति पर सदस्य के LOAD_ATTRलिए निष्पादित करता है addideone.com/tz1Tll
Markus Jarderot

5
जब मैं इसे प्राप्त होने वाली सूचियों की सूची पर आज़माता हूँ: टाइपर्रर: अस्वाभाविक प्रकार: 'सूची'
जेन्स टिमरमैन

7
आपका समाधान सबसे तेज़ नहीं है। पायथन 3 में (2 का परीक्षण नहीं किया गया) यह तेज़ है (300k प्रविष्टियाँ सूची - 0.045s (तुम्हारा) बनाम 0.035s (यह एक): देखा = सेट (); वापसी [x के लिए x लाइनों में x अगर x में नहीं देखा और नहीं; saw.add (x)]। मैंने आपके द्वारा देखे गए_add लाइन का कोई भी स्पीड इफेक्ट नहीं पाया।
user136036

3
@ user136036 कृपया अपने परीक्षणों का लिंक दें। आपने उन्हें कितनी बार चलाया? seen_addएक सुधार है लेकिन समय पर सिस्टम संसाधनों से समय प्रभावित हो सकता है। पूरी टाइमिंग देखने के लिए इच्छुक होंगे
जामिलाक

2
जो कोई भी पायथन कोड लिख रहा है, आपको वास्तव में पठनीयता का त्याग करने से पहले दो बार सोचना चाहिए और आमतौर पर सहमत पायथन सम्मेलनों को प्रति पाश कुछ और नैनोसेकेंड निचोड़ने के लिए। seen_add = seen.addपैदावार के साथ और बिना पैदावार के परीक्षण केवल गति में 1% की वृद्धि करता है। यह शायद ही महत्वपूर्ण है।
स्लीब्लैक

343

2016 को संपादित करें

जैसा कि रेमंड ने बताया , अजगर 3.5+ में, जहां OrderedDictसी में लागू किया गया है, सूची बोध दृष्टिकोण दृष्टिकोण की तुलना में धीमा होगा OrderedDict(जब तक कि आपको वास्तव में सूची की आवश्यकता नहीं है - और फिर भी, केवल तभी जब इनपुट बहुत कम हो)। तो 3.5+ के लिए सबसे अच्छा समाधान है OrderedDict

महत्वपूर्ण संपादन 2015

@Abarnert नोटों के रूप में , more_itertoolsलाइब्रेरी ( pip install more_itertools) में एक unique_everseenफ़ंक्शन होता है जो इस समस्या को बिना किसी अपठनीय ( not seen.add) म्यूटेशन के सूची बोध में हल करने के लिए बनाया गया है । यह सबसे तेज़ समाधान भी है:

>>> from  more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]

बस एक साधारण पुस्तकालय आयात और कोई हैक्स नहीं। यह इटर्स्टूल नुस्खा के कार्यान्वयन से आता है unique_everseenजो दिखता है:

def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

पायथन 2.7+में स्वीकृत सामान्य मुहावरा (जो काम करता है लेकिन गति के लिए अनुकूलित नहीं है, मैं अब इसका उपयोग करूंगा unique_everseen) collections.OrderedDict:

रनटाइम: O (N)

>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]

इससे बहुत अच्छा लगता है:

seen = set()
[x for x in seq if x not in seen and not seen.add(x)]

और बदसूरत हैक का उपयोग नहीं करेगा :

not seen.add(x)

जो इस तथ्य पर निर्भर करता है कि set.addएक इन-प्लेस विधि है जो हमेशा रिटर्न करती है Noneताकि उसका not Noneमूल्यांकन किया जा सके True

नोट करें कि हैक सॉल्यूशन कच्ची गति में तेज है, हालांकि इसमें समान रनटाइम जटिलता O (N) है।


5
केवल चाबियाँ लेने के लिए कुछ कस्टम प्रकार के तानाशाही में बदलना? बस एक और बैसाखी।
नकीलोन

3
@ नाकिलोन मैं वास्तव में नहीं देखता कि यह कैसे एक बैसाखी है। यह किसी भी परिवर्तनशील अवस्था को उजागर नहीं करता है, इसलिए उस अर्थ में यह बहुत साफ है। आंतरिक रूप से, पायथन सेटों को तानाशाह () ( stackoverflow.com/questions/3949310/… ) के साथ लागू किया जाता है , इसलिए मूल रूप से आप सिर्फ वही कर रहे हैं जो दुभाषिया ने वैसे भी किया होगा।
इमरान

बस साइड इफेक्ट का उपयोग करें और करते हैं [seen.add(x) for x in seq if x not in seen], या यदि आप समझ दुष्प्रभाव पसंद नहीं सिर्फ एक का उपयोग forपाश: for x in seq: seen.add(x) if x not in seen else None, (अभी भी एक एक लाइनर हालांकि इस मामले में मुझे लगता है कि एक लाइनर सत्ता एक में है करने के लिए प्रयास करने के लिए एक मूर्खतापूर्ण संपत्ति है समाधान।
एली

@ वे आदेश संरक्षित नहीं करते। तुम बस के रूप में अच्छी तरह से कर सकता है seen = set(seq)
flornquake

1
@CommuSoft मैं सहमत हूं, हालांकि व्यावहारिक रूप से यह लगभग हमेशा ओ (एन) सुपर अत्यधिक संभावना नहीं होने के कारण सबसे खराब स्थिति है
जैमाइलक

110

पायथन 2.7 में, मूल क्रम में रखते हुए पुनरावृत्तियों को डुप्लिकेट से निकालने का नया तरीका है:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

पायथन 3.5 में , ऑर्डरडीडक्ट का सी कार्यान्वयन है। मेरी टाइमिंग बताती है कि यह अब पायथन 3.5 के लिए विभिन्न दृष्टिकोणों में सबसे तेज और सबसे छोटा है।

पायथन 3.6 में , नियमित रूप से तानाशाह आदेश और कॉम्पैक्ट दोनों बन गया। (यह सुविधा CPython और PyPy के लिए है, लेकिन अन्य कार्यान्वयन में मौजूद नहीं हो सकती है)। यह हमें आदेश को बनाए रखते हुए कटौती करने का एक नया सबसे तेज़ तरीका देता है:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

पायथॉन 3.7 में , सभी आदेशों पर आदेश दिए गए दोनों के लिए नियमित रूप से तानाशाही की गारंटी है। तो, सबसे छोटा और सबसे तेज़ समाधान है:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

@Max का जवाब: एक बार जब आप 3.6 या 3.7 पर जाते हैं और ऑर्डरडेड के बजाय नियमित रूप से तानाशाही का उपयोग करते हैं , तो आप वास्तव में किसी अन्य तरीके से प्रदर्शन को हरा नहीं सकते हैं। शब्दकोश घने है और आसानी से लगभग कोई उपरि के साथ एक सूची में परिवर्तित हो जाता है। लक्ष्य सूची पूर्व-आकार के लेन (डी) के लिए है जो एक सूची समझ में आने वाले सभी आकार को बचाता है। इसके अलावा, चूंकि आंतरिक कुंजी सूची सघन है, इसलिए सूची कॉपी के रूप में पॉइंटर्स को कॉपी करना लगभग तेज है।


यह मेरी मशीन (अजगर 3.5) पर किसी भी अन्य दृष्टिकोण की तुलना में तेज़ है जब तक कि मैं OrderedDictअंत में एक सूची में परिवर्तित नहीं होता। अगर मुझे इसे एक सूची में बदलने की आवश्यकता है, तो छोटे इनपुट के लिए सूची समझ दृष्टिकोण अभी भी 1.5 गुना तक तेज है। उस ने कहा, यह समाधान बहुत क्लीनर है।
अधिकतम

7
एकमात्र गोचा यह है कि चलने योग्य "तत्वों" को धोने योग्य होना चाहिए
मध्यस्थ

एक आदेश पर सम्मिलन आदेश पुनरावृत्ति कार्यक्षमता प्रदान करता है जो डुप्लिकेट को हटाने की तुलना में अधिक उपयोग-मामलों को सेवा प्रदान करता है। उदाहरण के लिए, वैज्ञानिक विश्लेषण प्रतिलिपि प्रस्तुत करने योग्य गणनाओं पर भरोसा करते हैं जो गैर-नियतात्मक तानाशाही समर्थन का समर्थन नहीं करता है। कम्प्यूटेशनल वैज्ञानिक मॉडलिंग में Reproducibility एक प्रमुख वर्तमान उद्देश्य है, इसलिए हम इस नई सुविधा का स्वागत करते हैं। हालांकि मुझे पता है कि यह एक नियतकालिक तानाशाही, एक उच्च प्रदर्शन, निर्धारक के साथ बनाने के लिए तुच्छ है set()और अधिक भोले उपयोगकर्ताओं को प्रतिलिपि प्रस्तुत करने योग्य कोड विकसित करने में मदद करेगा।
आर्थर

41
sequence = ['1', '2', '3', '3', '6', '4', '5', '6']
unique = []
[unique.append(item) for item in sequence if item not in unique]

अद्वितीय → ['1', '2', '3', '6', '4', '5']


28
यह ध्यान देने योग्य है कि यह रनn^2
गॉनोकॉपल

25
Ick। 2 हमलों: (का एक और सूची निर्माण सदस्यता परीक्षण (धीमी गति से, हे (एन)) के लिए एक सूची का उपयोग करना और दुष्प्रभावों के लिए एक सूची समझ का उपयोग कर None! प्रक्रिया में संदर्भ)
मार्टिन पीटर्स

1
मैं @MartijnPieters के साथ सहमत हूँ साइड इफेक्ट्स के साथ सूची समझ का कोई कारण नहीं हैforइसके बजाय एक लूप का उपयोग करें
जमालिक

31

मृत घोड़े को लात मारने के लिए नहीं (यह प्रश्न बहुत पुराना है और पहले से ही बहुत सारे अच्छे उत्तर हैं), लेकिन यहां पांडा का उपयोग करके एक समाधान है जो कई परिस्थितियों में काफी तेज है और उपयोग करने के लिए मृत है।

import pandas as pd

my_list = [0, 1, 2, 3, 4, 1, 2, 3, 5]

>>> pd.Series(my_list).drop_duplicates().tolist()
# Output:
# [0, 1, 2, 3, 4, 5]

27
from itertools import groupby
[ key for key,_ in groupby(sortedList)]

सूची को क्रमबद्ध करने की भी आवश्यकता नहीं है , पर्याप्त शर्त यह है कि समान मूल्यों को एक साथ समूहीकृत किया जाए।

संपादित करें: मैंने माना कि "संरक्षण आदेश" का अर्थ है कि सूची वास्तव में आदेशित है। यदि यह मामला नहीं है, तो MizardX से समाधान सही है।

सामुदायिक संपादन: हालांकि यह "एकल तत्व में लगातार तत्वों की नकल करने" का सबसे सुंदर तरीका है।


1
लेकिन यह आदेश को संरक्षित नहीं करता है!

1
हर्म, यह समस्याग्रस्त है, क्योंकि मैं यह गारंटी नहीं दे सकता कि समान मानों को सूची में एक बार लूप किए बिना एक साथ समूहीकृत किया जाता है, जिस समय तक मैं डुप्लिकेट को काट सकता था।
जोश ग्लोवर

मैंने मान लिया कि "संरक्षण आदेश" का अर्थ है कि सूची वास्तव में आदेशित है।
राफेल डोवगर्ड

1
हो सकता है कि इनपुट सूची का विनिर्देश थोड़ा अस्पष्ट हो। मूल्यों को भी एक साथ समूहीकृत करने की आवश्यकता नहीं है: [2, 1, 3, 1]। तो कौन सा मान रखने के लिए और कौन सा हटाना है?

1
@igorkf जोड़ी के दूसरे तत्व की अनदेखी करना।
राफेल डोवगर्ड

24

मुझे लगता है कि यदि आप आदेश बनाए रखना चाहते हैं,

आप यह कोशिश कर सकते हैं:

list1 = ['b','c','d','b','c','a','a']    
list2 = list(set(list1))    
list2.sort(key=list1.index)    
print list2

या इसी तरह आप यह कर सकते हैं:

list1 = ['b','c','d','b','c','a','a']  
list2 = sorted(set(list1),key=list1.index)  
print list2 

आप यह भी कर सकते हैं:

list1 = ['b','c','d','b','c','a','a']    
list2 = []    
for i in list1:    
    if not i in list2:  
        list2.append(i)`    
print list2

इसे इस प्रकार भी लिखा जा सकता है:

list1 = ['b','c','d','b','c','a','a']    
list2 = []    
[list2.append(i) for i in list1 if not i in list2]    
print list2 

3
आपके पहले दो उत्तर मानते हैं कि सूची के क्रम को एक छँटाई फ़ंक्शन का उपयोग करके फिर से बनाया जा सकता है, लेकिन ऐसा नहीं हो सकता है।
रिचर्ड

5
अधिकांश उत्तर प्रदर्शन पर केंद्रित होते हैं। उन सूचियों के लिए जो प्रदर्शन के बारे में चिंता करने के लिए पर्याप्त नहीं हैं, क्रमबद्ध (सेट (list1), कुंजी = list1.index) मैंने देखी सबसे अच्छी चीज़ है। कोई अतिरिक्त आयात, कोई अतिरिक्त कार्य नहीं, कोई अतिरिक्त चर नहीं है, और यह काफी सरल और पठनीय है।
डेरेक वीट

23

में अजगर 3.7 और इसके बाद के संस्करण, शब्दकोशों रहे हैं गारंटी उनके कुंजी प्रविष्टि आदेश में याद करने के लिए। इस प्रश्न का उत्तर मामलों की वर्तमान स्थिति का सारांश देता है।

इस OrderedDictप्रकार समाधान अप्रचलित हो जाता है और बिना किसी आयात के बयान हम बस जारी कर सकते हैं:

>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> list(dict.fromkeys(lst))
[1, 2, 3, 4]

11

एक और बहुत पुराने प्रश्न के उत्तर के लिए बहुत देर से:

itertoolsव्यंजनों एक समारोह है कि इस करता है, का उपयोग कर seenसेट तकनीक है, लेकिन:

  • एक मानक keyकार्य संभालता है ।
  • बिना किसी हैक के उपयोग करता है।
  • लूप seen.addको एन-बार देखने के बजाय पूर्व-बाइंडिंग द्वारा अनुकूलित करता है । ( f7यह भी करता है, लेकिन कुछ संस्करण नहीं हैं।)
  • लूप का उपयोग करके अनुकूलन करता है ifilterfalse, इसलिए आपको केवल उन सभी के बजाय पायथन में अद्वितीय तत्वों पर लूप करना होगा। (आप अभी भी उन सभी के अंदर पुनरावृति करते हैं ifilterfalse, निश्चित रूप से, लेकिन वह सी में है, और बहुत तेज है।)

क्या यह वास्तव में की तुलना में तेज है f7? यह आपके डेटा पर निर्भर करता है, इसलिए आपको इसे देखना होगा और देखना होगा। यदि आप अंत में एक सूची चाहते हैं, तो एक सूची का f7उपयोग करता है, और यहाँ ऐसा करने का कोई तरीका नहीं है। (आप सीधे आईएनजी के appendबजाय कर सकते yieldहैं, या आप listफ़ंक्शन में जनरेटर को फीड कर सकते हैं , लेकिन न तो कोई सूची के अंदर LIST_APPEND जितना तेज़ हो सकता है।) किसी भी दर पर, आमतौर पर, कुछ माइक्रोसेकंड निचोड़ने के रूप में नहीं जा रहा है। आसानी से समझने योग्य, पुन: प्रयोज्य, पहले से लिखे गए फ़ंक्शन के रूप में महत्वपूर्ण है जिसे सजाने के लिए आपको डीएसयू की आवश्यकता नहीं है।

सभी व्यंजनों के साथ, यह भी उपलब्ध है more-iterools

यदि आप केवल keyमामला नहीं चाहते हैं , तो आप इसे सरल बना सकते हैं:

def unique(iterable):
    seen = set()
    seen_add = seen.add
    for element in itertools.ifilterfalse(seen.__contains__, iterable):
        seen_add(element)
        yield element

मैंने पूरी तरह से अनदेखी की more-itertoolsयह स्पष्ट रूप से सबसे अच्छा जवाब है। एक सरल from more_itertools import unique_everseen list(unique_everseen(items))मेरा बहुत तेज़ दृष्टिकोण है और स्वीकृत उत्तर की तुलना में बहुत बेहतर है, मुझे लगता है कि लाइब्रेरी डाउनलोड इसके लायक है। मैं अपना जवाब देने के लिए समुदाय में जा रहा हूं और इसे
जोड़ूंगा

11

बस एक बाहरी मॉड्यूल से एक और (बहुत performant) इस तरह के एक कार्यक्षमता के कार्यान्वयन को जोड़ने के लिए 1 : iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen
>>> lst = [1,1,1,2,3,2,2,2,1,3,4]

>>> list(unique_everseen(lst))
[1, 2, 3, 4]

समय

मैंने कुछ समय किया (पायथन 3.6) और ये दिखाते हैं कि यह मेरे द्वारा परीक्षण किए गए अन्य सभी विकल्पों की तुलना में तेज़ है OrderedDict.fromkeys, f7और more_itertools.unique_everseen:

%matplotlib notebook

from iteration_utilities import unique_everseen
from collections import OrderedDict
from more_itertools import unique_everseen as mi_unique_everseen

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

def iteration_utilities_unique_everseen(seq):
    return list(unique_everseen(seq))

def more_itertools_unique_everseen(seq):
    return list(mi_unique_everseen(seq))

def odict(seq):
    return list(OrderedDict.fromkeys(seq))

from simple_benchmark import benchmark

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: list(range(2**i)) for i in range(1, 20)},
              'list size (no duplicates)')
b.plot()

यहां छवि विवरण दर्ज करें

और सिर्फ यह सुनिश्चित करने के लिए कि मैंने यह जांचने के लिए अधिक डुप्लिकेट के साथ एक परीक्षण किया कि क्या इससे कोई फर्क पड़ता है:

import random

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(1, 20)},
              'list size (lots of duplicates)')
b.plot()

यहां छवि विवरण दर्ज करें

और केवल एक मूल्य वाला:

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [1]*(2**i) for i in range(1, 20)},
              'list size (only duplicates)')
b.plot()

यहां छवि विवरण दर्ज करें

इन सभी मामलों में iteration_utilities.unique_everseenफ़ंक्शन सबसे तेज़ है (मेरे कंप्यूटर पर)।


यह iteration_utilities.unique_everseenफ़ंक्शन इनपुट में अनसैबल वैल्यूज़ को भी हैंडल कर सकता है (हालाँकि वैल्यूज़ वॉश होने पर O(n*n)परफॉर्मेंस के बजाय परफॉर्मेंस के साथ O(n))।

>>> lst = [{1}, {1}, {2}, {1}, {3}]

>>> list(unique_everseen(lst))
[{1}, {2}, {3}]

1 अस्वीकरण: मैं उस पैकेज का लेखक हूं।


मुझे इस लाइन की आवश्यकता समझ में नहीं आती है: seen_add = seen.add- क्या बेंचमार्क के लिए यह आवश्यक है?
एलेक्स

@ एलेक्स इस उत्तर में दिया गया दृष्टिकोण है । यह वहाँ पूछने के लिए और अधिक समझ में आता है। मैंने उस उत्तर के दृष्टिकोण का उपयोग केवल समय की तुलना करने के लिए किया।
MSeifert

क्या आप dict.fromkeys()कृपया अपने चार्ट में विधि जोड़ सकते हैं ?
बोरिस

मुझे वास्तव में यकीन नहीं है कि अगर मेरे पास जल्द ही टाइमिंग करने के लिए समान है। क्या आपको लगता है कि यह बहुत तेज है ordereddict.fromkeys?
MSeifert

6

MizardX के आधार पर कोई हैश प्रकार (जैसे सूचियों की सूची) के लिए:

def f7_noHash(seq)
    seen = set()
    return [ x for x in seq if str( x ) not in seen and not seen.add( str( x ) )]

3

nubसूचियों के लिए हास्केल के कार्य को निश्चित करने में उपयोग किए गए पुनरावर्ती विचार को उधार लेना , यह एक पुनरावर्ती दृष्टिकोण होगा:

def unique(lst):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: x!= lst[0], lst[1:]))

उदाहरण के लिए:

In [118]: unique([1,5,1,1,4,3,4])
Out[118]: [1, 5, 4, 3]

मैंने इसे बढ़ते हुए डेटा आकारों के लिए आज़माया और उप-रेखीय समय-जटिलता (निश्चित नहीं, लेकिन यह सामान्य डेटा के लिए ठीक होना चाहिए) को देखा।

In [122]: %timeit unique(np.random.randint(5, size=(1)))
10000 loops, best of 3: 25.3 us per loop

In [123]: %timeit unique(np.random.randint(5, size=(10)))
10000 loops, best of 3: 42.9 us per loop

In [124]: %timeit unique(np.random.randint(5, size=(100)))
10000 loops, best of 3: 132 us per loop

In [125]: %timeit unique(np.random.randint(5, size=(1000)))
1000 loops, best of 3: 1.05 ms per loop

In [126]: %timeit unique(np.random.randint(5, size=(10000)))
100 loops, best of 3: 11 ms per loop

मुझे यह भी लगता है कि यह दिलचस्प है कि इसे अन्य कार्यों द्वारा विशिष्टता के लिए सामान्यीकृत किया जा सकता है। ऐशे ही:

import operator
def unique(lst, cmp_op=operator.ne):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: cmp_op(x, lst[0]), lst[1:]), cmp_op)

उदाहरण के लिए, आप एक फ़ंक्शन में पास हो सकते हैं जो एक ही पूर्णांक पर गोलाई की धारणा का उपयोग करता है जैसे कि यह विशिष्टता उद्देश्यों के लिए "समानता" था, जैसे:

def test_round(x,y):
    return round(x) != round(y)

तब अद्वितीय (some_list, test_round) उस सूची के अद्वितीय तत्वों को प्रदान करेगा जहाँ विशिष्टता का अब पारंपरिक समानता नहीं है (जो कि इस समस्या के किसी भी प्रकार के सेट-आधारित या तानाशाही-कुंजी-आधारित दृष्टिकोण का उपयोग करके निहित है) लेकिन इसके बजाय इसका मतलब है केवल पहला तत्व जो प्रत्येक संभव पूर्णांक K के लिए K को राउंड करता है, जो कि तत्वों को गोल कर सकता है, जैसे:

In [6]: unique([1.2, 5, 1.9, 1.1, 4.2, 3, 4.8], test_round)
Out[6]: [1.2, 5, 1.9, 4.2, 3]

1
ध्यान दें कि प्रदर्शन खराब हो जाएगा जब अद्वितीय तत्वों की संख्या तत्वों की कुल संख्या के सापेक्ष बहुत बड़ी है, क्योंकि प्रत्येक क्रमिक पुनरावर्ती कॉल का उपयोग filterमुश्किल से पिछले कॉल से बिल्कुल लाभ होगा। लेकिन अगर अद्वितीय तत्वों की संख्या सरणी आकार के सापेक्ष छोटी है, तो यह बहुत अच्छा प्रदर्शन करना चाहिए।
ely

3

5 x तेजी से भिन्न लेकिन अधिक परिष्कृत को कम करते हैं

>>> l = [5, 6, 6, 1, 1, 2, 2, 3, 4]
>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

स्पष्टीकरण:

default = (list(), set())
# use list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

>>> reduce(reducer, l, default)[0]
[5, 6, 1, 2, 3, 4]

3

आप सूची समझ का संदर्भ दे सकते हैं क्योंकि यह प्रतीक '_ [1]' द्वारा बनाया जा रहा है।
उदाहरण के लिए, निम्नलिखित फ़ंक्शन अद्वितीय-ifies अपनी सूची समझ को संदर्भित करके अपना क्रम बदले बिना तत्वों की एक सूची बनाता है।

def unique(my_list): 
    return [x for x in my_list if x not in locals()['_[1]']]

डेमो:

l1 = [1, 2, 3, 4, 1, 2, 3, 4, 5]
l2 = [x for x in l1 if x not in locals()['_[1]']]
print l2

आउटपुट:

[1, 2, 3, 4, 5]

2
यह भी ध्यान दें कि यह इसे एक O (n ^ 2) ऑपरेशन बना देगा, जहां एक सेट / तानाशाही (जिसमें निरंतर समय लग रहा है) बनाने के लिए और केवल पहले के अनदेखे तत्वों को जोड़ने से रैखिक होगा।
एली

यह पायथन 2.6 है, केवल मुझे विश्वास है। और हाँ यह ओ (एन ^ 2) है
जामिलाक

2

MizardX का जवाब कई दृष्टिकोणों का एक अच्छा संग्रह देता है।

मैं यही सोचकर आया हूं:

mylist = [x for i,x in enumerate(mylist) if x not in mylist[i+1:]]

आपका समाधान अच्छा है, लेकिन यह प्रत्येक तत्व का अंतिम रूप लेता है। पहली उपस्थिति का उपयोग करने के लिए: [x for i, x in enumerate (mylist) यदि x mylist में नहीं है [: i]
रिवका

7
चूंकि एक सूची में खोज एक O(n)ऑपरेशन है और आप इसे प्रत्येक आइटम पर करते हैं, जिसके परिणामस्वरूप आपके समाधान की जटिलता होगी O(n^2)। इस तरह की एक तुच्छ समस्या के लिए यह अस्वीकार्य है।
निकिता वोल्कोव

2

यहाँ यह करने के लिए एक सरल तरीका है:

list1 = ["hello", " ", "w", "o", "r", "l", "d"]
sorted(set(list1 ), key=lambda x:list1.index(x))

यह आउटपुट देता है:

["hello", " ", "w", "o", "r", "l", "d"]

1

आप एक प्रकार की बदसूरत सूची समझ हैक कर सकते हैं।

[l[i] for i in range(len(l)) if l.index(l[i]) == i]

के लिए पसंद i,e in enumerate(l)करते हैं l[i] for i in range(len(l))
एवपोक 17

1

_sorted_एक numpyसरणियों के साथ अपेक्षाकृत प्रभावी दृष्टिकोण :

b = np.array([1,3,3, 8, 12, 12,12])    
numpy.hstack([b[0], [x[0] for x in zip(b[1:], b[:-1]) if x[0]!=x[1]]])

आउटपुट:

array([ 1,  3,  8, 12])

1
l = [1,2,2,3,3,...]
n = []
n.extend(ele for ele in l if ele not in set(n))

एक जनरेटर अभिव्यक्ति जो ओ (1) का उपयोग करता है, यह निर्धारित करने के लिए सेट है कि नई सूची में एक तत्व शामिल है या नहीं।


1
extendएक जनरेटर अभिव्यक्ति के साथ चतुर उपयोग जो विस्तारित (तो +1) होने वाली बात पर निर्भर करता है, लेकिन set(n)प्रत्येक चरण (जो रैखिक है) पर पुन: विवादास्पद होता है और यह समग्र दृष्टिकोण द्विघात होने का संकेत देता है। वास्तव में, यह लगभग निश्चित रूप से केवल उपयोग करने से भी बदतर है ele in n। एकल सदस्यता परीक्षण के लिए एक सेट बनाना सेट निर्माण के खर्च के लायक नहीं है। फिर भी - यह एक दिलचस्प दृष्टिकोण है।
जॉन कोलमैन


1

एक अनुक्रम में डुप्लिकेट मानों को खत्म करना, लेकिन शेष वस्तुओं के क्रम को संरक्षित करना। सामान्य प्रयोजन जनरेटर फ़ंक्शन का उपयोग।

# for hashable sequence
def remove_duplicates(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

a = [1, 5, 2, 1, 9, 1, 5, 10]
list(remove_duplicates(a))
# [1, 5, 2, 9, 10]



# for unhashable sequence
def remove_duplicates(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)

a = [ {'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
list(remove_duplicates(a, key=lambda d: (d['x'],d['y'])))
# [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]

1

पांडा उपयोगकर्ताओं को बाहर की जाँच करनी चाहिए pandas.unique

>>> import pandas as pd
>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> pd.unique(lst)
array([1, 2, 3, 4])

फ़ंक्शन एक NumPy सरणी देता है। यदि आवश्यक हो, तो आप इसे tolistविधि के साथ एक सूची में बदल सकते हैं ।


1
अच्छा है। मैं उसके लिए पांडा का उपयोग करने की कल्पना कभी नहीं करूंगा, लेकिन यह काम करता है
सर्लाक

0

यदि आपको एक लाइनर की आवश्यकता है तो शायद यह मदद करेगा:

reduce(lambda x, y: x + y if y[0] not in x else x, map(lambda x: [x],lst))

... काम करना चाहिए लेकिन अगर मैं गलत हूं तो मुझे सुधारो


यह एक सशर्त अभिव्यक्ति है इसलिए यह अच्छा है
कोड 22

0

यदि आप नियमित रूप से उपयोग करते हैं pandas, और सौंदर्यशास्त्र को प्रदर्शन से अधिक पसंद किया जाता है, तो अंतर्निहित फ़ंक्शन पर विचार करें pandas.Series.drop_duplicates:

    import pandas as pd
    import numpy as np

    uniquifier = lambda alist: pd.Series(alist).drop_duplicates().tolist()

    # from the chosen answer 
    def f7(seq):
        seen = set()
        seen_add = seen.add
        return [ x for x in seq if not (x in seen or seen_add(x))]

    alist = np.random.randint(low=0, high=1000, size=10000).tolist()

    print uniquifier(alist) == f7(alist)  # True

समय:

    In [104]: %timeit f7(alist)
    1000 loops, best of 3: 1.3 ms per loop
    In [110]: %timeit uniquifier(alist)
    100 loops, best of 3: 4.39 ms per loop

0

यह ओ (एन) समय में आदेश को संरक्षित करेगा और चलाएगा। मूल रूप से यह विचार है कि जहां कहीं भी डुप्लिकेट पाया जाता है वहां एक छेद बनाएं और इसे नीचे की ओर सिंक करें। रीड एंड राइट पॉइंटर का उपयोग करता है। जब भी कोई डुप्लिकेट पाया जाता है तो केवल रीड पॉइंटर एडवांस और राइट पॉइंटर लिखते हैं डुप्लिकेट प्रविष्टि पर इसे अधिलेखित करने के लिए रहता है।

def deduplicate(l):
    count = {}
    (read,write) = (0,0)
    while read < len(l):
        if l[read] in count:
            read += 1
            continue
        count[l[read]] = True
        l[write] = l[read]
        read += 1
        write += 1
    return l[0:write]

0

आयातित मॉड्यूल या सेट का उपयोग किए बिना एक समाधान:

text = "ask not what your country can do for you ask what you can do for your country"
sentence = text.split(" ")
noduplicates = [(sentence[i]) for i in range (0,len(sentence)) if sentence[i] not in sentence[:i]]
print(noduplicates)

आउटपुट देता है:

['ask', 'not', 'what', 'your', 'country', 'can', 'do', 'for', 'you']

यह ओ (एन ** 2) जटिलता + सूची हर बार टुकड़ा करना है।
जीन फ़्राँस्वा Fabre

0

एक जगह विधि

यह विधि द्विघात है, क्योंकि हमारे पास सूची के प्रत्येक तत्व के लिए सूची में एक रेखीय खोज है (क्योंकि हमें सूची को फिर से व्यवस्थित करने की लागत को जोड़ना होगा क्योंकि del s के )।

उस स्थान पर काम करना संभव है, अगर हम सूची के अंत से शुरू करते हैं और प्रत्येक शब्द को उप-सूची में मौजूद हर शब्द को हटाने के लिए आगे बढ़ते हैं

कोड में यह विचार बस है

for i in range(len(l)-1,0,-1): 
    if l[i] in l[:i]: del l[i] 

कार्यान्वयन का एक सरल परीक्षण

In [91]: from random import randint, seed                                                                                            
In [92]: seed('20080808') ; l = [randint(1,6) for _ in range(12)] # Beijing Olympics                                                                 
In [93]: for i in range(len(l)-1,0,-1): 
    ...:     print(l) 
    ...:     print(i, l[i], l[:i], end='') 
    ...:     if l[i] in l[:i]: 
    ...:          print( ': remove', l[i]) 
    ...:          del l[i] 
    ...:     else: 
    ...:          print() 
    ...: print(l)
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5, 2]
11 2 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]
10 5 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4]: remove 5
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4]
9 4 [6, 5, 1, 4, 6, 1, 6, 2, 2]: remove 4
[6, 5, 1, 4, 6, 1, 6, 2, 2]
8 2 [6, 5, 1, 4, 6, 1, 6, 2]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2]
7 2 [6, 5, 1, 4, 6, 1, 6]
[6, 5, 1, 4, 6, 1, 6, 2]
6 6 [6, 5, 1, 4, 6, 1]: remove 6
[6, 5, 1, 4, 6, 1, 2]
5 1 [6, 5, 1, 4, 6]: remove 1
[6, 5, 1, 4, 6, 2]
4 6 [6, 5, 1, 4]: remove 6
[6, 5, 1, 4, 2]
3 4 [6, 5, 1]
[6, 5, 1, 4, 2]
2 1 [6, 5]
[6, 5, 1, 4, 2]
1 5 [6]
[6, 5, 1, 4, 2]

In [94]:                                                                                                                             

पोस्ट करने से पहले मैंने बिना किसी लाभ के 'जगह' के उत्तरों की बॉडी को खोजा है। यदि अन्य लोगों ने भी इसी तरह से समस्या का समाधान किया है तो कृपया मुझे सतर्क करें और मैं अपना उत्तर निकाल दूंगा।
gboffi

l[:] = <one of the the faster methods>यदि आप इन-प्लेस ऑपरेशन चाहते हैं तो आप इसका उपयोग कर सकते हैं , नहीं?
टाइमजैब

@timgeb हां और नहीं ... जब मैं करता हूं a=[1]; b=a; a[:]=[2]तब b==[2]मूल्य होता है Trueऔर हम कह सकते हैं कि हम इसे इन-प्लेस कर रहे हैं, फिर भी आप जो प्रस्ताव देते हैं वह नई सूची के लिए नए स्थान का उपयोग कर रहा है, पुराने डेटा को नए डेटा के साथ बदलें और चिह्नित करें कचरा संग्रहण के लिए पुराना डेटा क्योंकि किसी भी चीज़ का कोई संदर्भ नहीं है, इसलिए यह कहना कि यह अंदर चल रहा है, अवधारणा wrt को थोड़ा खींच रहा है जो मैंने दिखाया है वह संभव है ... क्या यह अक्षम है? हाँ, लेकिन मैंने पहले ही बता दिया है।
gboffi

0

zmk का दृष्टिकोण सूची समझ का उपयोग करता है जो बहुत तेज़ है, फिर भी आदेश को स्वाभाविक रूप से रखता है। केस संवेदी तार के लिए आवेदन करने के लिए इसे आसानी से संशोधित किया जा सकता है। यह मूल मामले को भी संरक्षित करता है।

def DelDupes(aseq) :
    seen = set()
    return [x for x in aseq if (x.lower() not in seen) and (not seen.add(x.lower()))]

निकटता से जुड़े कार्य हैं:

def HasDupes(aseq) :
    s = set()
    return any(((x.lower() in s) or s.add(x.lower())) for x in aseq)

def GetDupes(aseq) :
    s = set()
    return set(x for x in aseq if ((x.lower() in s) or s.add(x.lower())))

0

एक लाइनर सूची समझ:

values_non_duplicated = [value for index, value in enumerate(values) if value not in values[ : index]]

बस यह मानने के लिए एक सशर्त जोड़ें कि मान पिछली स्थिति पर नहीं है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.