पायथन सूची घटाव ऑपरेशन


227

मैं ऐसा ही कुछ करना चाहता हूं:

>>> x = [1,2,3,4,5,6,7,8,9,0]  
>>> x  
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]  
>>> y = [1,3,5,7,9]  
>>> y  
[1, 3, 5, 7, 9]  
>>> y - x   # (should return [2,4,6,8,0])

लेकिन यह अजगर सूचियों द्वारा समर्थित नहीं है इसे करने का सबसे अच्छा तरीका क्या है?


@ezdazuzena यह विकल्प नहीं है। यह दो सूचियों के बीच का अंतर है। आपका बँटवारा इस सवाल का कोई बँटवारा नहीं है।
सेलिक

1
[२, २] - [२] क्या करना चाहिए? []? [2]?
मैकके

@ मैकके [2,2] - [2] को वापस आना चाहिए [2]। [२,२] - [१,२,२,३] को वापस लौटना चाहिए []
रॉबिनो

यह प्रश्न सूची घटाव के बारे में है लेकिन स्वीकृत उत्तर घटाव सेट करने के करीब है।
रॉबिनो

2
क्या करना चाहिए [2, 1, 2, 3, 2, 4, 2] - [2, 3, 2] वापसी, और क्यों? क्या इसे बीच में 232 ढूंढना चाहिए और 2142 वापस करना चाहिए? या इसे हर बार पहली बार ढूंढना चाहिए और 1242 वापस करना चाहिए? या कुछ और? मैं यह कह रहा हूं कि ये स्पष्ट उत्तर नहीं हैं और जरूरत पर निर्भर हैं।
मैकके

जवाबों:


330

सूची समझ का उपयोग करें:

[item for item in x if item not in y]

यदि आप -infix सिंटैक्स का उपयोग करना चाहते हैं , तो आप कर सकते हैं:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(args)

    def __sub__(self, other):
        return self.__class__(*[item for item in self if item not in other])

फिर आप इसका उपयोग कर सकते हैं जैसे:

x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y   

लेकिन अगर आपको पूरी तरह से सूची के गुणों (उदाहरण के लिए, ऑर्डर करने) की आवश्यकता नहीं है, तो सेट का उपयोग करें जैसा कि अन्य उत्तर सुझाते हैं।


10
@admica, listपरिवर्तनीय नामों के लिए उपयोग न करें क्योंकि यह listकंस्ट्रक्टर को छाया देता है । यदि आप 'सूची' का उपयोग करते हैं, तो कृपया इसे अंडरस्कोर के साथ पूर्ववर्ती करें। इसके अलावा, ड्रॉप करने से *, आपने मेरा कोड तोड़ दिया ...
aaronasterling

19
यदि आप करते हैं तो [1,1,2,2] - [1,2]आपको खाली सूची मिलेगी। [1,1,2,2] - [2]देता है [1,1]तो यह वास्तव में सूची घटाव नहीं है, यह अधिक की तरह है "सूची से सूची एक्स सेट से तत्व के बिना Y "
अल्फ्रेड जिएन

@AlfredZien ने क्या कहा
रेट्रोकोड

सूची समझ विधि निर्धारित अंतर विधि की तुलना में धीमी (मेरे उदाहरण में) तरीका है।
redfiloux

1
@BarnabasSzabolcs: यही कारण है, एक बात नहीं की बचत होगी, क्योंकि यह परिवर्तित कर देंगे yएक करने के लिए setपहले हर जांच (जो मूल काम के लिए समान लागत है)। आपको या तो yset = set(y)listcomp के बाहर करने की आवश्यकता होगी , फिर परीक्षण करें if item not in yset, या एक [item for yset in [set(y)] for item in x if item not in yset]अहंकारी हैक के रूप में, ऐसा करें जो नेस्टेड comcomps को ysetएक-लाइनर के रूप में कैश करने के लिए दुरुपयोग करता है। थोड़ा कम बदसूरत एक-लाइनर समाधान जो पर्याप्त रूप से प्रदर्शन करता है, वह उपयोग करना होगा list(itertools.filterfalse(set(y).__contains__, x))क्योंकि तर्क filterfalseकेवल एक बार निर्मित होता है।
शैडो रेंजर

259

सेट अंतर का उपयोग करें

>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]

या आपके पास सिर्फ x और y सेट हो सकते हैं ताकि आपको कोई रूपांतरण न करना पड़े।


50
यह किसी भी आदेश को खो देगा। यह संदर्भ के आधार पर हो सकता है या नहीं।
औरोनस्टरलिंग

63
यह किसी भी संभावित डुप्लिकेट को ढीला कर देगा जिसे बनाए रखने की आवश्यकता / पड़ सकती है।
ओपल

मैंTypeError: unhashable type: 'dict'
हवनार

यह उन मामलों में तेजी से होता है जहां सूची की तुलना बड़ी है
JqueryToAddNumbers

2
यदि सूची में वस्तुओं का ऑर्डर करना और डुप्लिकेट करना संदर्भ के लिए महत्वपूर्ण नहीं है, तो यह एक महान उत्तर है और यह बहुत पठनीय है।
वाट इमसुरी

37

यह एक "सेट घटाव" ऑपरेशन है। उसके लिए सेट डेटा संरचना का उपयोग करें।

पायथन 2.7 में:

x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y

आउटपुट:

>>> print x - y
set([0, 8, 2, 4, 6])

1
सूची (सेट ([१,२,३,४,५]]) - सेट ([१,२३,३]) = [४, ५] ताकि प्रत्येक को पहले सेट करने के लिए सूचीबद्ध किया जाए, फिर घटाना (या एक तरफ़ा अंतर ) और सूची पर वापस।
भूतल

2
यदि आप x सेट के मूल आइटम क्रम को बनाए रखना चाहते हैं तो अच्छा नहीं है।
ज़हरान

34

यदि डुप्लिकेट और ऑर्डरिंग आइटम समस्या हैं:

[i for i in a if not i in b or b.remove(i)]

a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]

2
यह काम करता है, हालांकि यह O(m * n)रनटाइम है (और जब भी किसी सूची में साइड-इफेक्ट्स शामिल होते हैं, तो मैं खराब हो जाता हूं); आपcollections.CounterO(m + n) रनटाइम पाने के लिए इस पर सुधार कर सकते हैं
शैडो रेंजर

मुझे यह समझने में कठिन समय हो रहा है, क्या कोई समझा सकता है?
अनुष्का

20

कई उपयोग मामलों के लिए, आपको जो उत्तर चाहिए वह है:

ys = set(y)
[item for item in x if item not in ys]

यह एरोनस्टरलिंग के उत्तर और क्वांटमसाउप के उत्तर के बीच एक संकर है ।

aaronasterling का संस्करण len(y)प्रत्येक तत्व के लिए आइटम की तुलना करता है x, इसलिए इसमें द्विघात समय लगता है। quantumSoup का संस्करण सेट का उपयोग करता है, इसलिए यह प्रत्येक तत्व के लिए एक निरंतर-समय सेट लुकअप करता है x- क्योंकि यह दोनों को x और yसेट में परिवर्तित करता है, यह आपके तत्वों का क्रम खो देता है।

केवल yएक सेट में परिवर्तित करके , और xक्रम में पुनरावृत्ति करके, आप दोनों दुनियाओं में से सबसे अच्छा प्राप्त करते हैं - रैखिक समय, और आदेश संरक्षण *।


हालाँकि, यह अभी भी क्वांटमसाउप के संस्करण से एक समस्या है: इसके लिए आपके तत्वों को धोने योग्य होना चाहिए। यह सेटों की प्रकृति में बहुत अधिक निर्मित है। ** यदि आप कोशिश कर रहे हैं, जैसे, किसी अन्य सूची के डिट्स की सूची को घटाएं, लेकिन घटाने की सूची बड़ी है, तो आप क्या करते हैं?

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

ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]

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


यदि आपके आइटम नहीं हैं, और बनाया नहीं जा सकता है, धो सकते हैं, लेकिन वे तुलनीय हैं, तो आप कम से कम लॉग-रैखिक समय प्राप्त कर सकते हैं ( O(N*log M), जो O(N*M)सूची समाधान के समय की तुलना में बहुत बेहतर है , लेकिन उतना अच्छा नहीं है O(N+M)सेट समाधान का समय) छाँटकर और इस्तेमाल करके bisect:

ys = sorted(y)
def bisect_contains(seq, item):
    index = bisect.bisect(seq, item)
    return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]

यदि आपके आइटम न तो धोने योग्य हैं और न ही तुलनीय हैं, तो आप द्विघात समाधान के साथ फंस गए हैं।


* ध्यान दें कि आप एक जोड़ी OrderedSetवस्तुओं का उपयोग करके भी ऐसा कर सकते हैं, जिसके लिए आप व्यंजनों और तीसरे पक्ष के मॉड्यूल पा सकते हैं। लेकिन मुझे लगता है कि यह सरल है।

** कारण सेट लुकअप लगातार समय है कि यह सब करना है hash मूल्य है और देखें कि क्या वहाँ हैश के लिए एक प्रविष्टि है। यदि यह मान नहीं है, यह काम नहीं करेगा।


7

सेटों में मानों को देखते हुए उन्हें सूचियों में देखने से अधिक तेज़ है:

[item for item in x if item not in set(y)]

मेरा मानना ​​है कि यह इससे थोड़ा बेहतर होगा:

[item for item in x if item not in y]

दोनों सूचियों के क्रम को संरक्षित करते हैं।


क्या यह कैश होगा set(y)और yप्रत्येक लूप पर एक नए सेट में परिवर्तित नहीं होगा ? अन्यथा, आप की जरूरत abarnert का जवाब चाहते हैं: ys = set(y); [i for i in x if i not in ys]
जैकटोज

2
कुछ मोटे परीक्षण से पता चलता है कि if i not in set(y)25% से अधिक समय लगता है if i not in y(जहां yएक सूची है)। सेट को पहले से परिवर्तित करने में 55% कम समय लगता है। बहुत कम xऔर के साथ परीक्षण किया गया है y, लेकिन मतभेदों को लंबाई के साथ अधिक स्पष्ट होना चाहिए, अगर कुछ भी।
जैकटोज़

1
@Jacktose: हाँ, यह समाधान अधिक काम करता है, क्योंकि इसमें प्रत्येक तत्व के yलिए हर तत्व को iterate और hash करना है x; जब तक समानता की तुलना हैश अभिकलन के सापेक्ष वास्तव में महंगी नहीं होती, तब तक यह हमेशा के लिए खो जाएगा item not in y
शैडो रेंजर

@ शादो रेंजर जो समझ में आता है। यदि सेट रूपांतरण उस चेक को करने के लिए एक बहुत तेज़ तरीका था, तो आपको लगता है कि कंपाइलर हमेशा उस तरह से चेक करेगा।
15

5

यदि सूचियाँ डुप्लिकेट तत्वों की अनुमति देती हैं, तो आप संग्रह से काउंटर का उपयोग कर सकते हैं:

from collections import Counter
result = list((Counter(x)-Counter(y)).elements())

यदि आपको x से तत्वों के क्रम को संरक्षित करने की आवश्यकता है:

result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]

यह अच्छा है, हालांकि यह आदेश खोना नहीं है; जिसे ठीक करना थोड़ा और जटिल है
शैडो रेंजर

@ शादो रेंजर, यह वास्तव में है। लेकिन बस थोड़ा सा।
एलन टी।

मुझे कोई आपत्ति नहीं है, मैं कैशिंग और साइड-इफेक्ट्स के साथ लिस्टकॉम्प्स में सिर्फ कंपकंपी में जा रहा हूं (हालांकि मुझे लगता है कि दोनों के संयोजन बाहरी रूप से दिखाई देने वाले साइड-इफेक्ट्स को हटा देते हैं?)। :-)
शैडो रेंजर

इसके अलावा, यह कोड लिखित रूप में काम नहीं करेगा; Counter.subtractशून्य मूल्यवान तत्वों को नहीं हटाता ( -और -=करते हैं, लेकिन नहीं subtract), इसलिए आप तत्वों को निकालना कभी बंद नहीं करेंगे। आप के not v in cसाथ प्रतिस्थापित करना चाहते हैं not c[v](जो गैर-मौजूद तत्वों के लिए शून्य देता है, ताकि आप "शून्यकाल" के लिए वापसी का सुरक्षित रूप से परीक्षण कर सकें not)।
शैडो रेंजर

@ShadowRanger, अच्छा कैच! अब निश्चित कर दिया।
अलाईन टी।

3

अन्य समाधानों में कुछ समस्याएं हैं:

  1. वे आदेश को संरक्षित नहीं करते हैं, या
  2. वे तत्वों की एक सटीक गणना नहीं निकालते हैं, उदाहरण के लिए x = [1, 2, 2, 2]और y = [2, 2]वे yएक में परिवर्तित करते हैं set, और या तो सभी मिलान तत्वों को हटाते हैं ( [1]केवल छोड़कर ) या प्रत्येक अद्वितीय तत्व (छोड़ते हुए [1, 2, 2]) में से एक को हटा दें , जब उचित व्यवहार 2दो बार निकालना होगा , छोड़ना [1, 2], या
  3. वे O(m * n)काम करते हैं, जहां एक इष्टतम समाधान O(m + n)काम कर सकता है

AlainCounter # 2 और # 3 को हल करने के लिए सही रास्ते पर था , लेकिन वह समाधान आदेश को खो देगा। समाधान जो आदेश को संरक्षित करता है (हटाने के nलिए मूल्यों के nदोहराव के लिए प्रत्येक मान की पहली प्रतियां listनिकालता है):

from collections import Counter

x = [1,2,3,4,3,2,1]  
y = [1,2,2]  
remaining = Counter(y)

out = []
for val in x:
    if remaining[val]:
        remaining[val] -= 1
    else:
        out.append(val)
# out is now [3, 4, 3, 1], having removed the first 1 and both 2s.

इसे ऑनलाइन आज़माएं!

इसे प्रत्येक तत्व की अंतिम प्रतियां निकालने के लिए , बस forलूप को बदल दें और लूप से बाहर निकलने के तुरंत बाद for val in reversed(x):जोड़ें ।out.reverse()for

निर्माण Counterकर रहा है O(n)के संदर्भ में yकी लंबाई, बार-बार दोहराना xहै O(n)के मामले में xरों लंबाई ', और Counterसदस्यता के परीक्षण और उत्परिवर्तन हैं O(1), जबकि list.appendपरिशोधित है O(1)(एक दिया appendहो सकता है O(n)कई के लिए है, लेकिन appendहै, कुल मिलाकर बड़ी-ओ औसत O(1)के बाद से कम और कम उनमें से एक वास्तविक स्थान की आवश्यकता होती है), इसलिए किया गया समग्र कार्य है O(m + n)

आप यह निर्धारित करने के लिए भी परीक्षण कर सकते हैं कि क्या कोई तत्व yथे xजो परीक्षण से हटाए नहीं गए थे :

remaining = +remaining  # Removes all keys with zero counts from Counter
if remaining:
    # remaining contained elements with non-zero counts

नोट: यह करता है hashable होने के लिए मान की आवश्यकता होती है, लेकिन किसी भी समाधान है कि hashable वस्तुओं की आवश्यकता नहीं है या तो सामान्य प्रयोजन नहीं है (उदाहरण के लिए भरोसा कर सकते हैं intनिश्चित लंबाई सरणी में रों) या अधिक से अधिक करना है O(m + n)काम (जैसे अगला सबसे अच्छा बड़ा -O के लिए एक listविशिष्ट मूल्य / गिनती जोड़े बनाने के लिए होगा , जो कि बाइनरी खोजों O(1) dictमें लुकअप बदलते हैं O(log n); आपको उनकी गणना के साथ विशिष्ट मानों की आवश्यकता होगी, न कि केवल गैर-विशिष्ट मानों को छांटना, क्योंकि अन्यथा आप O(n)लागतों को निकालने के लिए भुगतान करेंगे । तत्वों को हल से list)।
शैडो रेंजर

2

इसे इस्तेमाल करे।

def subtract_lists(a, b):
    """ Subtracts two lists. Throws ValueError if b contains items not in a """
    # Terminate if b is empty, otherwise remove b[0] from a and recurse
    return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:]) 
                                  for i in [a.index(b[0])]][0]

>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0]
>>> x = [1,2,3,4,5,6,7,8,9,0,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0, 9]     #9 is only deleted once
>>>

2

मुझे लगता है कि इसे प्राप्त करने का सबसे आसान तरीका सेट () का उपयोग करना है।

>>> x = [1,2,3,4,5,6,7,8,9,0]  
>>> y = [1,3,5,7,9]  
>>> list(set(x)- set(y))
[0, 2, 4, 6, 8]

1

@Aronasterling द्वारा प्रदान किया गया उत्तर अच्छा लग रहा है, हालांकि, यह सूची के डिफ़ॉल्ट इंटरफ़ेस के साथ संगत नहीं है: x = MyList(1, 2, 3, 4)बनाम x = MyList([1, 2, 3, 4])। इस प्रकार, नीचे दिए गए कोड का उपयोग अधिक अजगर-सूची के अनुकूल के रूप में किया जा सकता है:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(*args)

    def __sub__(self, other):
        return self.__class__([item for item in self if item not in other])

उदाहरण:

x = MyList([1, 2, 3, 4])
y = MyList([2, 5, 2])
z = x - y

0

मुझे लगता है कि यह तेज है:

In [1]: a = [1,2,3,4,5]

In [2]: b = [2,3,4,5]

In [3]: c = set(a) ^ set(b)

In [4]: c
Out[4]: {1}

यह घटाव नहीं है। वास्तव में, यह दो सूचियों के बीच सममित अंतर है।
पार्थ चौहान

इसके अलावा यह केवल सूचियों के अंदर
हैशेबल

-1

यह उदाहरण दो सूचियों को घटाता है:

# List of pairs of points
list = []
list.append([(602, 336), (624, 365)])
list.append([(635, 336), (654, 365)])
list.append([(642, 342), (648, 358)])
list.append([(644, 344), (646, 356)])
list.append([(653, 337), (671, 365)])
list.append([(728, 13), (739, 32)])
list.append([(756, 59), (767, 79)])

itens_to_remove = []
itens_to_remove.append([(642, 342), (648, 358)])
itens_to_remove.append([(644, 344), (646, 356)])

print("Initial List Size: ", len(list))

for a in itens_to_remove:
    for b in list:
        if a == b :
            list.remove(b)

print("Final List Size: ", len(list))

8
इससे बचें, यह ओ (एन ^ 2) है
अलेक्जेंडर - मोनिका
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.