सूचियों की सूची से डुप्लिकेट निकालना


116

मेरे पास पायथन में सूचियों की एक सूची है:

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

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

यह सबसे कुशल तरीके से कैसे किया जा सकता है?

उपरोक्त सूची का परिणाम होना चाहिए:

k = [[5, 6, 2], [1, 2], [3], [4]]

मैं संरक्षण के आदेश के बारे में परवाह नहीं है।

नोट: यह प्रश्न समान है लेकिन मुझे इसकी आवश्यकता नहीं है। एसओ की खोज की लेकिन सही डुप्लिकेट नहीं मिला।


बेंचमार्किंग:

import itertools, time


class Timer(object):
    def __init__(self, name=None):
        self.name = name

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        if self.name:
            print '[%s]' % self.name,
        print 'Elapsed: %s' % (time.time() - self.tstart)


k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] * 5
N = 100000

print len(k)

with Timer('set'):
    for i in xrange(N):
        kt = [tuple(i) for i in k]
        skt = set(kt)
        kk = [list(i) for i in skt]


with Timer('sort'):
    for i in xrange(N):
        ks = sorted(k)
        dedup = [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]


with Timer('groupby'):
    for i in xrange(N):
        k = sorted(k)
        dedup = list(k for k, _ in itertools.groupby(k))

with Timer('loop in'):
    for i in xrange(N):
        new_k = []
        for elem in k:
            if elem not in new_k:
                new_k.append(elem)

"लूप इन" (द्विघात विधि) छोटी सूचियों के लिए सबसे तेज। लंबी सूचियों के लिए यह तेजी से है, फिर समूह विधि को छोड़कर सभी को। इसका कोई मतलब भी है क्या?

छोटी सूची के लिए (कोड में एक), 100000 पुनरावृत्तियों:

[set] Elapsed: 1.3900001049
[sort] Elapsed: 0.891000032425
[groupby] Elapsed: 0.780999898911
[loop in] Elapsed: 0.578000068665

लंबी सूची के लिए (कोड को 5 बार दोहराया गया):

[set] Elapsed: 3.68700003624
[sort] Elapsed: 3.43799996376
[groupby] Elapsed: 1.03099989891
[loop in] Elapsed: 1.85900020599

1
"यह तेज़ नहीं है", क्या आपका मतलब है कि आपने इसे समय दिया है और यह आपके आवेदन के लिए पर्याप्त तेज़ नहीं है, या आपको लगता है कि यह तेज़ नहीं है?
— टॉर्स्टन मारेक

@ टॉर्स्टन, यह सिर्फ स्मार्ट तरीके से बहुत ज्यादा नकल करने जैसा लगता है। क्षमा करें, आंत की भावना। कॉपी सूचियों को ट्यूपल्स में, फिर सेट में, फिर वापस सूचियों की सूची में (कॉपी फिर से ट्यूपल्स टू लिस्ट्स)
— zaharpopov

@zaharpopov: ऐसा नहीं है कि पायथन कैसे काम करता है, कुछ भी कॉपी नहीं किया जाएगा , मौजूदा तत्वों के लिए सिर्फ नए कंटेनर (हालांकि ints के लिए, यह बहुत ज्यादा समान है)
— जोचेन रिट्जेल

3
1. छँटाई का उपयोग करने के तरीकों के लिए समय को हटा दिया जाता है, क्योंकि "k" क्रमबद्ध संस्करण के लिए प्रतिक्षेप है। 2. अंतिम विधि अधिक तेज़ है क्योंकि आप जिस तरह से परीक्षण डेटा उत्पन्न करते हैं वह आपको अधिकतम 4 अलग-अलग तत्वों के साथ छोड़ देता है। Sth की कोशिश करो। जैसे K = [[int (u) in u for str (random.randrange (1, 1000))] _ के लिए रेंज में (100)]
— Torsten Marek

@ टिप्पणी: निश्चित धन्यवाद। लेकिन फिर भी लूप विधि तेज है, यहां तक ​​कि 10 की सूची में केवल एक डुप्लिकेट है
— zaharpopov

जवाबों:


167
>>> k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
>>> import itertools
>>> k.sort()
>>> list(k for k,_ in itertools.groupby(k))
[[1, 2], [3], [4], [5, 6, 2]]

itertoolsअक्सर इस तरह की समस्याओं का सबसे तेज़ और सबसे शक्तिशाली समाधान प्रदान करता है, और अच्छी तरह से परिचित होने लायक है! -

संपादित करें : जैसा कि मैंने एक टिप्पणी में उल्लेख किया है, सामान्य अनुकूलन प्रयास बड़े इनपुट (बिग-ओ दृष्टिकोण) पर केंद्रित हैं क्योंकि यह इतना आसान है कि यह प्रयासों पर अच्छा रिटर्न प्रदान करता है। लेकिन कभी-कभी (अनिवार्य रूप से "त्रैमासिक महत्वपूर्ण बाधाओं के लिए" कोड के गहरे आंतरिक छोरों में, जो प्रदर्शन सीमाओं की सीमाओं को आगे बढ़ा रहे हैं) किसी को अधिक विस्तार में जाने की आवश्यकता हो सकती है, संभावना वितरण प्रदान करते हुए, जो प्रदर्शन के उपायों को अनुकूलित करने का निर्णय लेते हैं (शायद ऊपरी सीमा या 90 वाँ सेंटीमीटर औसत या मंझला की तुलना में अधिक महत्वपूर्ण है, जो किसी के ऐप्स पर निर्भर करता है), इनपुट डेटा विशेषताओं के आधार पर अलग-अलग एल्गोरिदम चुनने के लिए शुरुआत में संभवतः-हीरुस्टिक जांच करता है, और इसके बाद।

"बिंदु" प्रदर्शन (एक विशिष्ट इनपुट के लिए कोड ए बनाम कोड बी) की सावधानीपूर्वक माप इस अत्यंत महंगी प्रक्रिया का एक हिस्सा है, और मानक पुस्तकालय मॉड्यूल timeitयहां मदद करता है। हालाँकि, शेल प्रॉम्प्ट पर इसका उपयोग करना आसान है। उदाहरण के लिए, इस समस्या के लिए सामान्य दृष्टिकोण दिखाने के लिए यहां एक छोटा मॉड्यूल है, इसे इस प्रकार सहेजें nodup.py:

import itertools

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

def doset(k, map=map, list=list, set=set, tuple=tuple):
  return map(list, set(map(tuple, k)))

def dosort(k, sorted=sorted, xrange=xrange, len=len):
  ks = sorted(k)
  return [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]

def dogroupby(k, sorted=sorted, groupby=itertools.groupby, list=list):
  ks = sorted(k)
  return [i for i, _ in itertools.groupby(ks)]

def donewk(k):
  newk = []
  for i in k:
    if i not in newk:
      newk.append(i)
  return newk

# sanity check that all functions compute the same result and don't alter k
if __name__ == '__main__':
  savek = list(k)
  for f in doset, dosort, dogroupby, donewk:
    resk = f(k)
    assert k == savek
    print '%10s %s' % (f.__name__, sorted(resk))

ध्यान दें कि पवित्रता की जाँच करें (जब आप बस करते हैं python nodup.py) और बुनियादी उत्थापन तकनीक (गति के लिए प्रत्येक फ़ंक्शन के लिए निरंतर वैश्विक नाम बनाते हैं) समान पायदान पर चीजों को लगाने के लिए।

अब हम छोटे उदाहरण सूची पर जाँच चला सकते हैं:

$ python -mtimeit -s'import nodup' 'nodup.doset(nodup.k)'
100000 loops, best of 3: 11.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort(nodup.k)'
100000 loops, best of 3: 9.68 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby(nodup.k)'
100000 loops, best of 3: 8.74 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.donewk(nodup.k)'
100000 loops, best of 3: 4.44 usec per loop

यह पुष्टि करते हुए कि कुछ दोहरे मूल्यों के साथ छोटी सूचियों को आकर्षक बनाने के लिए द्विघात दृष्टिकोण में छोटे-पर्याप्त अवरोध हैं। डुप्लिकेट के बिना एक छोटी सूची के साथ:

$ python -mtimeit -s'import nodup' 'nodup.donewk([[i] for i in range(12)])'
10000 loops, best of 3: 25.4 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby([[i] for i in range(12)])'
10000 loops, best of 3: 23.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.doset([[i] for i in range(12)])'
10000 loops, best of 3: 31.3 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort([[i] for i in range(12)])'
10000 loops, best of 3: 25 usec per loop

द्विघात दृष्टिकोण बुरा नहीं है, लेकिन सॉर्ट और ग्रुपबी बेहतर हैं। आदि आदि।

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

इसके लिए एक अलग प्रतिनिधित्व रखने पर भी विचार करना उचित है k- पहले स्थान पर टुपल्स के बजाय सूचियों की सूची क्यों होनी चाहिए? यदि डुप्लिकेट हटाने का कार्य अक्सर होता है, और प्रोफाइलिंग इसे कार्यक्रम के प्रदर्शन की अड़चन के रूप में दिखाता है, हर समय टुपल्स का एक सेट रखने और केवल और यदि आवश्यक हो, तो इससे सूचियों की एक सूची प्राप्त करना, उदाहरण के लिए, तेजी से हो सकता है।


@alex विकल्प के लिए धन्यवाद। इस विधि के बारे में एक ही गति के रूप में danben's, कुछ% तेज
— zaharpopov

@alex: अजीब रूप से यह छोटी सूचियों के लिए एक अनुभवहीन द्विघात विधि की तुलना में धीमा है (प्रश्न संपादित करें देखें)
— zaharpopov

@ फैहरपोपोव: यह केवल आपके विशेष मामले में है, cf. सवाल के लिए मेरी टिप्पणी।
— टॉर्स्टन मारेक

@zaharpopov, अगर आप सूची की एक संभाव्यता वितरण और सबलिस्ट लंबाई और डुप्लिकेट का मौका देते हैं, तो किसी भी कोड के लिए रनटाइम्स प्रायिकता वितरण की गणना करना / मापना संभव है (जो कि आपके लिए आवश्यक उपाय का चयन करें) सेंटाइल, जो भी हो)। यह शायद ही कभी बहुत कम आरओआई के कारण किया गया है: आम तौर पर एक बड़े इनपुट (बिग-ओ दृष्टिकोण) के बहुत आसान मामले पर ध्यान केंद्रित करता है, जहां अवर एल्गोरिदम वास्तव में प्रदर्शन को बहुत चोट पहुंचाते हैं। और मुझे नहीं लगता कि आप अपने क्यू में किसी भी संभावना वितरण को निर्दिष्ट करते हैं ;-)।
— एलेक्स मार्टेली

@zaharpov, खुशी है कि आपको यह पसंद आया!
— एलेक्स मार्टेली

21

इसे मैन्युअल रूप से करना, एक नई kसूची बनाना और अब तक नहीं मिली प्रविष्टियों को जोड़ना:

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
new_k = []
for elem in k:
    if elem not in new_k:
        new_k.append(elem)
k = new_k
print k
# prints [[1, 2], [4], [5, 6, 2], [3]]

समझने के लिए सरल है, और आप प्रत्येक तत्व की पहली घटना के क्रम को संरक्षित करते हैं जो उपयोगी होना चाहिए, लेकिन मुझे लगता है कि यह जटिलता में द्विघात है क्योंकि आप new_kप्रत्येक तत्व के लिए संपूर्ण खोज कर रहे हैं ।


@ अंपुल: बहुत ही अजीब - यह तरीका अन्य सभी की तुलना में तेज़ है
— zaharpopov

मुझे संदेह है कि यह विधि बहुत लंबी सूचियों के लिए तेज़ नहीं होगी। यह आपके आवेदन पर निर्भर करेगा: यदि आपके पास वास्तव में दो डुप्लिकेट के साथ केवल छह-तत्व सूचियां हैं, तो किसी भी समाधान के काफी तेज होने की संभावना है और आपको स्पष्ट कोड के साथ जाना चाहिए।
— पॉल स्टीफेंसन

@zaharpopov, यह आपके बेंचमार्क में द्विघात नहीं है क्योंकि आप एक ही सूची को बार-बार दोहराते हैं। आप एक रैखिक कोने के मामले में बेंचमार्किंग कर रहे हैं।
— माइक ग्राहम

k = ([[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] +[[x] for x in range(1000)]) *5क्वात्रैटिक व्यवहार को अच्छी तरह से दिखाएंगे
— जॉन ला रोय

17
>>> k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
>>> k = sorted(k)
>>> k
[[1, 2], [1, 2], [3], [4], [4], [5, 6, 2]]
>>> dedup = [k[i] for i in range(len(k)) if i == 0 or k[i] != k[i-1]]
>>> dedup
[[1, 2], [3], [4], [5, 6, 2]]

मुझे नहीं पता कि यह आवश्यक रूप से तेज़ है, लेकिन आपको ट्यूपल्स और सेट्स का उपयोग करने की आवश्यकता नहीं है।


धन्‍यवाद। यह तेजी से ट्यूपल्स की ओर जाता है फिर 'सेट' के बाद वापस सूचियों में?
— ज़हरपॉपोव

आप आसानी से परीक्षण कर सकते हैं कि - दोनों डिडअपिंग विधियों को लिखें, उपयोग करने वाले कुछ यादृच्छिक सूचियां उत्पन्न करें randomऔर इसके साथ समय दें time।
— डेनबेन

4

setइस समस्या के सभी संबंधित समाधान इस प्रकार अब तक setपुनरावृत्ति से पहले एक पूरे बनाने की आवश्यकता है ।

इस आलसी को बनाना संभव है, और एक ही समय में आदेश की रक्षा करना, सूचियों की सूची को पुनरावृत्त करना और "आरा" में जोड़ना set। तब केवल एक सूची प्राप्त करें यदि वह इस ट्रैकर में नहीं मिली है set।

यह unique_everseenनुस्खा itertools डॉक्स में उपलब्ध है । यह तीसरे पक्ष के toolzपुस्तकालय में भी उपलब्ध है :

from toolz import unique

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

# lazy iterator
res = map(list, unique(map(tuple, k)))

print(list(res))

[[1, 2], [4], [5, 6, 2], [3]]

ध्यान दें कि tupleरूपांतरण आवश्यक है क्योंकि सूचियाँ उपलब्ध नहीं हैं।


3

यहां तक ​​कि आपकी "लंबी" सूची भी बहुत कम है। इसके अलावा, क्या आपने उन्हें वास्तविक डेटा से मेल खाने के लिए चुना था? ये डेटा वास्तव में क्या दिखते हैं, इसके साथ प्रदर्शन अलग-अलग होंगे। उदाहरण के लिए, आपके पास लंबी सूची बनाने के लिए बार-बार एक छोटी सूची होती है। इसका मतलब है कि द्विघात समाधान आपके मानदंड में रैखिक है, लेकिन वास्तविकता में नहीं।

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

संयोग से, मैं एक ध्यान देने योग्य स्पीडअप देख रहा हूं यदि मैं सेट बनाने के लिए एक मध्यवर्ती सूची नहीं बनाता हूं, तो यह कहना है कि क्या मैं प्रतिस्थापित करता हूं

kt = [tuple(i) for i in k]
skt = set(kt)

साथ में

skt = set(tuple(i) for i in k)

वास्तविक समाधान अधिक जानकारी पर निर्भर हो सकता है: क्या आप सुनिश्चित हैं कि सूचियों की एक सूची वास्तव में आपके लिए आवश्यक प्रतिनिधित्व है?


3

डुप्लिकेट को निकालने के लिए टपल और {} की सूची का उपयोग किया जा सकता है

>>> [list(tupl) for tupl in {tuple(item) for item in k }]
[[1, 2], [5, 6, 2], [3], [4]]
>>> 

1

कुंजी के रूप में टपल के साथ एक शब्दकोश बनाएं, और कुंजी प्रिंट करें।

  • मूल्य के रूप में कुंजी और सूचकांक के रूप में टपल के साथ शब्दकोश बनाएं
  • शब्दकोश की कुंजियों की प्रिंट सूची

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

dict_tuple = {tuple(item): index for index, item in enumerate(k)}

print [list(itm) for itm in dict_tuple.keys()]

# prints [[1, 2], [5, 6, 2], [3], [4]]

1

यह काम करना चाहिए।

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

k_cleaned = []
for ele in k:
    if set(ele) not in [set(x) for x in k_cleaned]:
        k_cleaned.append(ele)
print(k_cleaned)

# output: [[1, 2], [4], [5, 6, 2], [3]]

0

अजीब बात है, ऊपर दिए गए उत्तर 'डुप्लिकेट' को हटा देते हैं लेकिन क्या होगा अगर मैं डुप्लिकेट किए गए मान को भी निकालना चाहता हूं ?? निम्नलिखित उपयोगी होना चाहिए और स्मृति में एक नई वस्तु नहीं बनाता है!

def dictRemoveDuplicates(self):
    a=[[1,'somevalue1'],[1,'somevalue2'],[2,'somevalue1'],[3,'somevalue4'],[5,'somevalue5'],[5,'somevalue1'],[5,'somevalue1'],[5,'somevalue8'],[6,'somevalue9'],[6,'somevalue0'],[6,'somevalue1'],[7,'somevalue7']]


print(a)
temp = 0
position = -1
for pageNo, item in a:
    position+=1
    if pageNo != temp:
        temp = pageNo
        continue
    else:
        a[position] = 0
        a[position - 1] = 0
a = [x for x in a if x != 0]         
print(a)

और ओ / पी है:

[[1, 'somevalue1'], [1, 'somevalue2'], [2, 'somevalue1'], [3, 'somevalue4'], [5, 'somevalue5'], [5, 'somevalue1'], [5, 'somevalue1'], [5, 'somevalue8'], [6, 'somevalue9'], [6, 'somevalue0'], [6, 'somevalue1'], [7, 'somevalue7']]
[[2, 'somevalue1'], [3, 'somevalue4'], [7, 'somevalue7']]

-1

एक और शायद अधिक सामान्य और सरल समाधान वस्तुओं के स्ट्रिंग संस्करण द्वारा कुंजीबद्ध और अंत में मूल्यों को प्राप्त करने के लिए एक शब्दकोश बनाना है:

>>> dict([(unicode(a),a) for a in [["A", "A"], ["A", "A"], ["A", "B"]]]).values()
[['A', 'B'], ['A', 'A']]

पकड़ यह है कि यह केवल उन वस्तुओं के लिए काम करता है जिनका स्ट्रिंग प्रतिनिधित्व एक अच्छी-पर्याप्त अनूठी कुंजी है (जो अधिकांश मूल वस्तुओं के लिए सही है)।

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