अजगर: एक शर्त के आधार पर एक सूची विभाजित करें?


272

एक सशर्त के आधार पर कई सूचियों में आइटम की सूची को विभाजित करने के लिए, सौंदर्यशास्त्र और प्रदर्शन के दृष्टिकोण से, सबसे अच्छा तरीका क्या है? के बराबर:

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

यह करने के लिए एक और अधिक सुंदर तरीका है?

अद्यतन: यहाँ का वास्तविक उपयोग मामला है, बेहतर ढंग से समझाने के लिए कि मैं क्या करने की कोशिश कर रहा हूँ:

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

5
सेट बिल्डर स्टेटमेंट में एक शर्त की तलाश में यहाँ उतरा, आपके सवाल ने मेरे सवाल का जवाब दिया :)
अनुव्रत पाराशर

5
विभाजन इस ऑपरेशन का एक दुर्भाग्यपूर्ण विवरण है, क्योंकि यह पहले से ही पायथन तार के संबंध में एक विशिष्ट अर्थ रखता है। मुझे लगता है कि इस ऑपरेशन का वर्णन करने के लिए डिवाइड एक अधिक सटीक (या पायथन पुनरावृत्तियों के संदर्भ में कम से कम अतिभारित) है। मैं यहां सूची के बराबर सूची के लिए उतरा str.split(), लगातार उप-सूचियों के एक आदेश संग्रह में सूची को विभाजित करने के लिए। जैसे split([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6]), किसी सूची के तत्वों को श्रेणी के अनुसार विभाजित करने का विरोध ।
स्टूज

अजगर-सूची पर एक ही विषय पर चर्चा
Xiong Chiamiov

IMAGE_TYPES एक टपल के बजाय एक सेट होना चाहिए: IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png')। n (o / 2) के बजाय n (1), व्यावहारिक रूप से पठनीयता में कोई अंतर नहीं है।
चैमग

जवाबों:


110
good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

यह करने के लिए एक और अधिक सुंदर तरीका है?

यह कोड पूरी तरह से पठनीय है, और बेहद स्पष्ट है!

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

फिर, यह ठीक है!

सेटों का उपयोग करते हुए मामूली प्रदर्शन में सुधार हो सकता है, लेकिन यह एक तुच्छ अंतर है, और मुझे यह सूची समझ में आती है कि इसे पढ़ना बहुत आसान है, और आपको इस आदेश के बारे में चिंता करने की ज़रूरत नहीं है कि यह गड़बड़ हो रहा है, डुप्लिकेट को हटा दिया जा रहा है।

वास्तव में, मैं एक और कदम "पिछड़ा" जा सकता हूं, और बस एक सरल लूप का उपयोग करूंगा:

images, anims = [], []

for f in files:
    if f.lower() in IMAGE_TYPES:
        images.append(f)
    else:
        anims.append(f)

एक सूची-समझ या उपयोग set()तब तक ठीक है जब तक आपको कुछ अन्य चेक या किसी अन्य तर्क को जोड़ने की आवश्यकता नहीं है - कहते हैं कि आप सभी 0-बाइट जेपीई को हटाना चाहते हैं, आप बस कुछ ऐसा जोड़ते हैं ..

if f[1] == 0:
    continue

44
क्या सूची में दो बार लूप होने के बिना कोई सूची समझ नहीं है?
बाल्की जूल

35
समस्या यह है कि यह DRY सिद्धांत का उल्लंघन करता है। यह अच्छा होगा अगर ऐसा करने का एक बेहतर तरीका था।
एंटिमोनी

21
एक बार फ़ंक्शनल प्रोग्रामिंग (हास्केल), या फ़ंक्शनल स्टाइल (LINQ) के लिए भूख बढ़ने पर, हम इसकी उम्र के लिए पायथन को सूंघना शुरू कर देते हैं - [x for x in blah if ...]- क्रिया, lambdaअनाड़ी और सीमित ... यह 1995 से आज तक की सबसे कूल कार चलाने का मन करता है। वापस तो वही नहीं है।
टॉमस गैंडर

6
@ टॉमाज़गैंडर एफटीआर, हास्केल अजगर से अधिक पुराना है (और वास्तव में इसके डिजाइन को प्रभावित किया है)। मुझे लगता है कि सूची की समझ और लंबोदर के लिए वाक्यविन्यास को जानबूझकर क्रिया पक्ष पर थोड़ा रखा गया था, शायद उन्हें अधिक उपयोग करने को हतोत्साहित करने के लिए। जो वास्तव में जोखिम का एक सा है ... जितना मैं हास्केल को पसंद करता हूं, मैं देख सकता हूं कि क्यों कई लोग पायथन को आमतौर पर अधिक पठनीय पाते हैं।
लेफ्टनैबाउटआउट

4
लूप के लिए सबसे अच्छा तरीका यह करना है ... एक एकल लूप, बहुत स्पष्ट और पठनीय
एन्ट्रोपिक

217
good, bad = [], []
for x in mylist:
    (bad, good)[x in goodvals].append(x)

14
यह अविश्वसनीय रूप से सरल है! मुझे यह समझने में थोड़ा समय लगा कि हालांकि क्या हो रहा था। मैं जानना चाहता हूं कि क्या दूसरों को लगता है कि इसे पठनीय कोड माना जा सकता है या नहीं।
jgpaiva

171
good.append(x) if x in goodvals else bad.append(x)अधिक पठनीय है।
डैंसलमो

21
@ डंसाल्मो खासकर जब से आप इसे फोर-साइकल के साथ एक-लाइनर बना सकते हैं, और यदि आप किसी चीज़ को अधिक जटिल xबनाना चाहते हैं, तो आप इसे appendकेवल एक में बना सकते हैं :for x in mylist: (good if isgood(x) else bad).append(x)
यो '

2
@MLister, उस मामले में आपको संभवतः विशेषता लुकअप शामिल करना चाहिए(bad.append, good.append)
जॉन ला

11
थोड़ा कम बदलाव:(good if x in goodvals else bad).append(x)
Pi Delport

104

यहाँ आलसी पुनरावृत्ति दृष्टिकोण है:

from itertools import tee

def split_on_condition(seq, condition):
    l1, l2 = tee((condition(item), item) for item in seq)
    return (i for p, i in l1 if p), (i for p, i in l2 if not p)

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

क्योंकि यह आलसी है आप इसे किसी भी पुनरावृत्ति पर उपयोग कर सकते हैं, यहां तक ​​कि एक अनंत:

from itertools import count, islice

def is_prime(n):
    return n > 1 and all(n % i for i in xrange(2, n))

primes, not_primes = split_on_condition(count(), is_prime)
print("First 10 primes", list(islice(primes, 10)))
print("First 10 non-primes", list(islice(not_primes, 10)))

आमतौर पर हालांकि गैर-आलसी सूची वापसी दृष्टिकोण बेहतर है:

def split_on_condition(seq, condition):
    a, b = [], []
    for item in seq:
        (a if condition(item) else b).append(item)
    return a, b

संपादित करें: कुछ कुंजी द्वारा अलग-अलग सूचियों में वस्तुओं को विभाजित करने के आपके अधिक विशिष्ट उपयोग के लिए, एक सामान्य कार्य करता है जो यह करता है:

DROP_VALUE = lambda _:_
def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
    """Split a sequence into lists based on a key function.

        seq - input sequence
        resultmapping - a dictionary that maps from target lists to keys that go to that list
        keyfunc - function to calculate the key of an input value
        default - the target where items that don't have a corresponding key go, by default they are dropped
    """
    result_lists = dict((key, []) for key in resultmapping)
    appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)

    if default is not DROP_VALUE:
        result_lists.setdefault(default, [])
        default_action = result_lists[default].append
    else:
        default_action = DROP_VALUE

    for item in seq:
        appenders.get(keyfunc(item), default_action)(item)

    return result_lists

उपयोग:

def file_extension(f):
    return f[2].lower()

split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
print split_files['images']
print split_files['anims']

आप शायद सही हैं कि यह YAGNI सिद्धांत का उल्लंघन करता है। यह इस धारणा पर आधारित है कि विभिन्न सूचियों की संख्या जिन्हें भविष्य में विभाजित किया जा सकता है।
आंटे जुस 4'09

17
यह बहुत कोड हो सकता है लेकिन अगर [ x for x in my_list if ExpensiveOperation(x) ]चलाने में लंबा समय लगता है, तो आप निश्चित रूप से इसे दो बार नहीं करना चाहते हैं!
डैश-टॉम-बैंग

1
+1 पुनरावृत्त-आधारित और एक विशिष्ट "X" समाधान सहित कई विविधताओं की पेशकश के लिए। ओपी का "अच्छाईयों में" छोटा हो सकता है, लेकिन इसे बहुत बड़े शब्दकोश के साथ बदलना या महंगा विधेय महंगा हो सकता है। इसके अलावा, यह हर जगह दो बार सूची समझ लिखने की आवश्यकता को कम कर देता है , इस प्रकार टाइपोस / उपयोगकर्ता त्रुटि को पेश करने की संभावना को कम करता है। अच्छा समाधान है। धन्यवाद!
cod3monk3y

3
ध्यान दें कि teeपुनरावृत्तियों के बीच सभी मूल्यों को संग्रहीत करता है, इसलिए यह वास्तव में मेमोरी को नहीं बचाएगा यदि आप एक पूरे जनरेटर और फिर दूसरे पर लूप करते हैं।
जॉन ला रोय

25

सभी प्रस्तावित समाधानों के साथ समस्या यह है कि यह दो बार फ़िल्टरिंग फ़ंक्शन को स्कैन और लागू करेगा। मैं इस तरह से एक छोटा सा कार्य करूँगा:

def SplitIntoTwoLists(l, f):
  a = []
  b = []
  for i in l:
    if f(i):
      a.append(i)
    else:
      b.append(i)
 return (a,b)

इस तरह आप दो बार कुछ भी प्रोसेस नहीं कर रहे हैं और कोड को भी नहीं दोहरा रहे हैं।


मैं सहमत हूँ। मैं एक "सुरुचिपूर्ण" (यानी यहाँ संक्षिप्त और अंतर्निहित / अंतर्निहित) तरीके की तलाश कर रहा था ताकि सूची को दो बार स्कैन किए बिना ऐसा किया जा सके, लेकिन ऐसा लगता है (बिना रूपरेखा के) जाने का रास्ता। बेशक यह केवल बड़ी मात्रा में डेटा के लिए ही मायने रखेगा।
मैथ्यू फ्लैशेन

IMHO, यदि आप इसे कम सीपीयू उपयोग (और इस तरह कम बिजली नाली) के साथ करने का एक तरीका जानते हैं, तो इसका उपयोग न करने का कोई कारण नहीं है।
जुआन

2
@ रिवाइंड ... मेरे सारे पायथन को सी। पोर्टिंग;)
इलियट कैमरन

19

मेरा उस पर लेना। मैं एक आलसी, एकल-पास का प्रस्ताव करता हूं,partition फ़ंक्शन , जो आउटपुट के बाद के क्रम में सापेक्ष आदेश को संरक्षित करता है।

1. आवश्यकताएँ

मुझे लगता है कि आवश्यकताओं हैं:

  • तत्वों के सापेक्ष क्रम को बनाए रखें (इसलिए, कोई सेट और शब्दकोश नहीं)
  • हर तत्व के लिए केवल एक बार स्थिति का मूल्यांकन करना (इसलिए उपयोग नहीं करना i) ( filterया groupby)
  • या तो अनुक्रम के आलसी उपभोग के लिए अनुमति दें (यदि हम उन्हें रोकना बर्दाश्त कर सकते हैं, तो भोले कार्यान्वयन स्वीकार्य होने की संभावना है)

2. splitपुस्तकालय

मेरे partitionकार्य (नीचे प्रस्तुत) और इसी तरह के अन्य कार्यों ने इसे एक छोटे पुस्तकालय में बनाया है:

यह सामान्य रूप से PyPI के माध्यम से इंस्टॉल करने योग्य है:

pip install --user split

शर्त पर सूची के आधार को विभाजित करने के लिए, partitionफ़ंक्शन का उपयोग करें:

>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]

3. partitionसमारोह समझाया

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

from collections import deque

SplitSeq क्लास हाउसकीपिंग का ध्यान रखती है:

class SplitSeq:
    def __init__(self, condition, sequence):
        self.cond = condition
        self.goods = deque([])
        self.bads = deque([])
        self.seq = iter(sequence)

जादू अपनी .getNext()विधि में होता है । यह लगभग .next() पुनरावृत्तियों की तरह है, लेकिन यह निर्दिष्ट करने की अनुमति देता है कि इस बार हम किस तरह का तत्व चाहते हैं। दृश्य के पीछे यह अस्वीकार किए गए तत्वों को नहीं छोड़ता है, बल्कि उन्हें दो कतारों में से एक में डालता है:

    def getNext(self, getGood=True):
        if getGood:
            these, those, cond = self.goods, self.bads, self.cond
        else:
            these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
        if these:
            return these.popleft()
        else:
            while 1: # exit on StopIteration
                n = self.seq.next()
                if cond(n):
                    return n
                else:
                    those.append(n)

अंतिम उपयोगकर्ता partitionफ़ंक्शन का उपयोग करने वाला है । यह एक कंडीशन फंक्शन और एक सीक्वेंस लेता है (जैसे mapया filter), और दो जनरेटर लौटाता है। पहला जनरेटर तत्वों की एक बादता बनाता है जिसके लिए स्थिति रखती है, दूसरा एक पूरक बाद में बनाता है। Iterators और जनरेटर भी लंबे या अनंत दृश्यों के आलसी विभाजन के लिए अनुमति देते हैं।

def partition(condition, sequence):
    cond = condition if condition else bool  # evaluate as bool if condition == None
    ss = SplitSeq(cond, sequence)
    def goods():
        while 1:
            yield ss.getNext(getGood=True)
    def bads():
        while 1:
            yield ss.getNext(getGood=False)
    return goods(), bads()

मैंने भविष्य में आंशिक अनुप्रयोग की सुविधा के लिए पहला तर्क होने के लिए परीक्षण फ़ंक्शन को चुना ( पहले तर्क के रूप में परीक्षण फ़ंक्शन कैसे mapऔर कैसे filterकिया जाए)।


15

मैं मूल रूप से एंडर्स के दृष्टिकोण को पसंद करता हूं क्योंकि यह बहुत सामान्य है। यहां एक ऐसा संस्करण है जो सबसे पहले वर्गीकृतकर्ता को (फिल्टर सिंटैक्स से मिलान करने के लिए) डालता है और एक डिफ़ॉल्ट का उपयोग करता है (आयातित माना जाता है)।

def categorize(func, seq):
    """Return mapping from categories to lists
    of categorized items.
    """
    d = defaultdict(list)
    for item in seq:
        d[func(item)].append(item)
    return d

मैं पायन के ज़ेन के उन बयानों को निकालने की कोशिश करने जा रहा था जो यहाँ लागू होते हैं, लेकिन यह एक टिप्पणी के लिए बहुत अधिक है। =) कोड का बहुत बढ़िया टुकड़ा।
jpmc26

13

पहले जाओ (पूर्व- OP- संपादित): सेट का उपयोग करें:

mylist = [1,2,3,4,5,6,7]
goodvals = [1,3,7,8,9]

myset = set(mylist)
goodset = set(goodvals)

print list(myset.intersection(goodset))  # [1, 3, 7]
print list(myset.difference(goodset))    # [2, 4, 5, 6]

यह पठनीयता (IMHO) और प्रदर्शन दोनों के लिए अच्छा है।

दूसरा जाना (पोस्ट-ओपी-एडिट):

एक सेट के रूप में अच्छे एक्सटेंशन की अपनी सूची बनाएं:

IMAGE_TYPES = set(['.jpg','.jpeg','.gif','.bmp','.png'])

और इससे प्रदर्शन में वृद्धि होगी। नहीं तो जो तुम्हें ठीक लगे, वही करो।


4
सबसे अच्छा समाधान नहीं है यदि सूची विभाजन से पहले कुछ क्रम में थी और आपको उन्हें उसी क्रम में रहने की आवश्यकता है।
दानीयार

8
क्या वह डुप्लिकेट नहीं निकालेगा?
मावन

सेट बनाना O (n log n) है। सूची को दो बार बदलना हे (n) है। सेट समाधान अधिक सुरुचिपूर्ण हो सकता है (जब यह पहली जगह में सही होता है) लेकिन n के रूप में सबसे निश्चित रूप से धीमा है।
डैश-टॉम-बैंग

1
@ डैश-टॉम-बैंग I सूची को ओ (n * n) है। ऐसा इसलिए है क्योंकि सूची में प्रत्येक आइटम की तुलना प्रत्येक आइटम के साथ करने की आवश्यकता हो सकती है goodvals
चैमग

@ChaimG अच्छा बिंदु, हालांकि हमें चौराहे और अंतर संचालन की लागत पर भी विचार करने की आवश्यकता है (जो मुझे हाथ से पता नहीं है, लेकिन मुझे पूरा यकीन है कि वे सुपरलाइनर भी हैं)।
डैश-टॉम-बैंग

10

itertools.groupby लगभग वही करता है जो आप चाहते हैं, इसके अलावा यह सुनिश्चित करने के लिए कि आपको एक एकल सन्निहित सीमा मिलनी चाहिए, इसलिए आपको अपनी कुंजी पहले सॉर्ट करने की आवश्यकता है (अन्यथा आपको प्रत्येक प्रकार के लिए एकाधिक इंटरलेयर्ड समूह मिलेंगे)। जैसे।

def is_good(f):
    return f[2].lower() in IMAGE_TYPES

files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file3.gif', 123L, '.gif')]

for key, group in itertools.groupby(sorted(files, key=is_good), key=is_good):
    print key, list(group)

देता है:

False [('file2.avi', 999L, '.avi')]
True [('file1.jpg', 33L, '.jpg'), ('file3.gif', 123L, '.gif')]

अन्य समाधानों के समान, कुंजी फ़ंक को आपके इच्छित किसी भी समूह में विभाजित करने के लिए परिभाषित किया जा सकता है।


6
good.append(x) if x in goodvals else bad.append(x)

@Dansalmo द्वारा इस सुरुचिपूर्ण और संक्षिप्त जवाब को टिप्पणियों में दफन कर दिया गया था, इसलिए मैं इसे केवल यहां एक उत्तर के रूप में दोहरा रहा हूं ताकि यह प्रमुखता प्राप्त कर सके, जो विशेष रूप से नए पाठकों के लिए है।

पूरा उदाहरण:

good, bad = [], []
for x in my_list:
    good.append(x) if x in goodvals else bad.append(x)

5

यदि आप इसे FP शैली में बनाना चाहते हैं:

good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
                                        for y in mylist)) ]

सबसे पठनीय समाधान नहीं है, लेकिन कम से कम केवल एक बार mylist के माध्यम से पुनरावृत्त होता है।


1
हालाँकि यह केवल एक बार सूची के माध्यम से पुनरावृत्त करता है, लेकिन सूची में सुधार के कारण प्रदर्शन उतना अच्छा नहीं है। एक सूची में लागू करना संभावित रूप से महंगा ऑपरेशन है (जब उदाहरण के लिए deque.append के साथ तुलना की जाती है)। वास्तव में, यह समाधान अत्यंत धीमा है जब यहां अन्य समाधानों के साथ तुलना की जाती है (100000 रैंडम पूर्णांकों पर 21.4s और उनके मूल्य का परीक्षण)।
rlat

5

व्यक्तिगत रूप से, मुझे आपके द्वारा उद्धृत संस्करण पसंद है, यह मानते हुए कि आपके पास पहले से ही goodvalsघूमने की सूची है । यदि नहीं, तो ऐसा कुछ:

good = filter(lambda x: is_good(x), mylist)
bad = filter(lambda x: not is_good(x), mylist)

बेशक, यह वास्तव में एक सूची समझ का उपयोग करने के समान है जैसा कि आपने मूल रूप से किया था, लेकिन एक लुकअप के बजाय एक फ़ंक्शन के साथ:

good = [x for x in mylist if is_good(x)]
bad  = [x for x in mylist if not is_good(x)]

सामान्य तौर पर, मुझे लगता है कि सूची के सौंदर्यशास्त्र बहुत ही मनभावन हैं। बेशक, आप वास्तव में आदेश देने के संरक्षण की जरूरत नहीं है और का उपयोग करके डुप्लीकेट जरूरत नहीं है, intersectionऔर differenceसेट पर तरीकों अच्छी तरह से भी काम करेगा।


बेशक, filter(lambda x: is_good(x), mylist)कम किया जा सकता हैfilter(is_good, mylist)
लूटें

अतिरिक्त फ़ंक्शन कॉल को जोड़ना वास्तव में डुप्लिकेट (!) निष्पादन समय की तुलना में, सूची की समझ की तुलना में, जो मैंने प्रोफाइलिंग में देखा है। यह एक सूची समझ को हरा करने के लिए कठिन है, ज्यादातर समय।
कॉर्ली ब्रिग्मैन

4

मुझे लगता है कि एन स्थितियों के आधार पर एक पुनरावृत्ति को विभाजित करने का एक सामान्यकरण आसान है

from collections import OrderedDict
def partition(iterable,*conditions):
    '''Returns a list with the elements that satisfy each of condition.
       Conditions are assumed to be exclusive'''
    d= OrderedDict((i,list())for i in range(len(conditions)))        
    for e in iterable:
        for i,condition in enumerate(conditions):
            if condition(e):
                d[i].append(e)
                break                    
    return d.values()

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

ints,floats,other = partition([2, 3.14, 1, 1.69, [], None],
                              lambda x: isinstance(x, int), 
                              lambda x: isinstance(x, float),
                              lambda x: True)

print " ints: {}\n floats:{}\n other:{}".format(ints,floats,other)

 ints: [2, 1]
 floats:[3.14, 1.69]
 other:[[], None]

यदि तत्व कई स्थितियों को संतुष्ट कर सकता है, तो ब्रेक को हटा दें।


3
def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

इसकी जाँच करें


3

कभी-कभी, ऐसा लगता है कि सूची समझ का उपयोग करना सबसे अच्छी बात नहीं है!

मैंने इस विषय को दिए गए उत्तर के आधार पर थोड़ा परीक्षण किया, एक यादृच्छिक उत्पन्न सूची पर परीक्षण किया। इस सूची की पीढ़ी है (ऐसा करने के लिए शायद एक बेहतर तरीका है, लेकिन यह बात नहीं है):

good_list = ('.jpg','.jpeg','.gif','.bmp','.png')

import random
import string
my_origin_list = []
for i in xrange(10000):
    fname = ''.join(random.choice(string.lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(good_list)
    else:
        fext = "." + ''.join(random.choice(string.lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

और अब हम चले

# Parand
def f1():
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def f2():
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def f3():
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# Ants Aasma
def f4():
    l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
    return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def f5():
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def f6():
    return filter(lambda e: e[2] in good_list, my_origin_list), filter(lambda e: not e[2] in good_list, my_origin_list)

Cmpthese फ़ंक्शन का उपयोग करना , सबसे अच्छा परिणाम है dbr उत्तर:

f1     204/s  --    -5%   -14%   -15%   -20%   -26%
f6     215/s     6%  --    -9%   -11%   -16%   -22%
f3     237/s    16%    10%  --    -2%    -7%   -14%
f4     240/s    18%    12%     2%  --    -6%   -13%
f5     255/s    25%    18%     8%     6%  --    -8%
f2     277/s    36%    29%    17%    15%     9%  --

यहां अद्यतन बेंचमार्क के साथ तेज़ कार्य करता है
चैमग

2

फिर भी इस समस्या का एक और समाधान। मुझे ऐसे समाधान की आवश्यकता थी जो जितनी जल्दी हो सके। इसका मतलब है कि सूची में केवल एक पुनरावृत्ति और अधिमानतः परिणामी सूचियों में से एक में डेटा जोड़ने के लिए O (1)। यह बहुत छोटे को छोड़कर, sastanin द्वारा प्रदान किए गए समाधान के समान है :

from collections import deque

def split(iterable, function):
    dq_true = deque()
    dq_false = deque()

    # deque - the fastest way to consume an iterator and append items
    deque((
      (dq_true if function(item) else dq_false).append(item) for item in iterable
    ), maxlen=0)

    return dq_true, dq_false

फिर, आप निम्न तरीके से फ़ंक्शन का उपयोग कर सकते हैं:

lower, higher = split([0,1,2,3,4,5,6,7,8,9], lambda x: x < 5)

selected, other = split([0,1,2,3,4,5,6,7,8,9], lambda x: x in {0,4,9})

यदि आप परिणामी dequeवस्तु के साथ ठीक नहीं हैं , तो आप इसे आसानी से (जैसे उदाहरण के लिए ) list, में परिवर्तित कर सकते हैं । रूपांतरण बहुत तेज़ है, जो सीधे सूचियों का निर्माण करता है।setlist(lower)

यह विधियाँ वस्तुओं के क्रम के साथ-साथ किसी भी डुप्लिकेट को बनाए रखती हैं।


2

उदाहरण के लिए, समान और विषम द्वारा विभाजन सूची

arr = range(20)
even, odd = reduce(lambda res, next: res[next % 2].append(next) or res, arr, ([], []))

या सामान्य रूप में:

def split(predicate, iterable):
    return reduce(lambda res, e: res[predicate(e)].append(e) or res, iterable, ([], []))

लाभ:

  • सबसे छोटा पॉसिबल तरीका
  • प्रत्येक तत्व के लिए केवल एक बार ही समर्पित होता है

नुकसान

  • कार्यात्मक कार्यक्रम प्रतिमान के ज्ञान की आवश्यकता है

2

@ Gnibbler के महान (लेकिन ट्रिक !) उत्तर से प्रेरित होकर , हम उस दृष्टिकोण को कई विभाजनों में लागू कर सकते हैं:

from collections import defaultdict

def splitter(l, mapper):
    """Split an iterable into multiple partitions generated by a callable mapper."""

    results = defaultdict(list)

    for x in l:
        results[mapper(x)] += [x]

    return results

फिर splitterनिम्नानुसार इस्तेमाल किया जा सकता है:

>>> l = [1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 3, 2, 3]
>>> split = splitter(l, lambda x: x % 2 == 0)  # partition l into odds and evens
>>> split.items()
>>> [(False, [1, 3, 3, 5, 3, 3]), (True, [2, 4, 2, 4, 6, 4, 2])]

यह अधिक जटिल मैपिंग (और पुनरावृत्तियों पर भी) के साथ दो से अधिक विभाजनों के लिए काम करता है:

>>> import math
>>> l = xrange(1, 23)
>>> split = splitter(l, lambda x: int(math.log10(x) * 5))
>>> split.items()
[(0, [1]),
 (1, [2]),
 (2, [3]),
 (3, [4, 5, 6]),
 (4, [7, 8, 9]),
 (5, [10, 11, 12, 13, 14, 15]),
 (6, [16, 17, 18, 19, 20, 21, 22])]

या नक्शे में एक शब्दकोश का उपयोग कर:

>>> map = {'A': 1, 'X': 2, 'B': 3, 'Y': 1, 'C': 2, 'Z': 3}
>>> l = ['A', 'B', 'C', 'C', 'X', 'Y', 'Z', 'A', 'Z']
>>> split = splitter(l, map.get)
>>> split.items()
(1, ['A', 'Y', 'A']), (2, ['C', 'C', 'X']), (3, ['B', 'Z', 'Z'])]

... बस देखा कि यह मूल रूप से @ alan-isaac पहले ही उत्तर दे चुका है।
जोश बोड

2
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]

परिशिष्ट कोई नहीं देता है, इसलिए यह काम करता है।


1

पूर्णता के लिए, प्रयास करें itertools

Itertools मॉड्यूल तेजी का एक मुख्य सेट, स्मृति कुशल उपकरण है कि खुद से या संयोजन में उपयोगी होते हैं मानकीकृत करता है। साथ में, वे एक "पुनरावृति बीजगणित" बनाते हैं, जो विशेष रूप से शुद्ध पायथन में कुशलतापूर्वक और कुशलता से निर्माण करना संभव बनाता है।

Itertools.ifilter या imap देखें ।

itertools.ifilter (विधेय, चलने योग्य)

एक पुनरावृत्ति करें जो पुनरावृत्ति से तत्वों को फ़िल्टर करता है केवल उन्हीं के लिए, जिनके लिए विधेय सत्य है


IFilter / IMAP (और सामान्य रूप में जनरेटर) मेरी रूपरेखा में, बहुत धीमी गति से कर रहे हैं ... सामान्य रूप में, अगर आप की तरह एक सूची समझ लेना [x for x in a if x > 50000]100000 पूर्णांकों (के माध्यम से random.shuffle) का एक सरल सरणी पर, filter(lambda x: x> 50000, a)जब तक 2x ले जाएगा, ifilter(lambda x: x> 50000, a); list(result)ले जाता है के रूप में लंबे समय के बारे में 2.3x। अजीब बात है लेकिन सच है।
Corley Brigman

1

कभी-कभी आपको सूची के अन्य आधे हिस्से की आवश्यकता नहीं होगी। उदाहरण के लिए:

import sys
from itertools import ifilter

trustedPeople = sys.argv[1].split(',')
newName = sys.argv[2]

myFriends = ifilter(lambda x: x.startswith('Shi'), trustedPeople)

print '%s is %smy friend.' % (newName, newName not in myFriends 'not ' or '')

1

यह सबसे तेज़ तरीका है।

यह if else(dbr के उत्तर की तरह) का उपयोग करता है , लेकिन पहले एक सेट बनाता है। एक सेट ओ (एम * एन) से ओ (लॉग एम) + ओ (एन) से संचालन की संख्या को कम करता है, जिसके परिणामस्वरूप गति में 45% + वृद्धि होती है।

good_list_set = set(good_list)  # 45% faster than a tuple.

good, bad = [], []
for item in my_origin_list:
    if item in good_list_set:
        good.append(item)
    else:
        bad.append(item)

थोड़ा छोटा:

good_list_set = set(good_list)  # 45% faster than a tuple.

good, bad = [], []
for item in my_origin_list:
    out = good if item in good_list_set else bad
    out.append(item)

बेंचमार्क परिणाम:

filter_BJHomer                  80/s       --   -3265%   -5312%   -5900%   -6262%   -7273%   -7363%   -8051%   -8162%   -8244%
zip_Funky                       118/s    4848%       --   -3040%   -3913%   -4450%   -5951%   -6085%   -7106%   -7271%   -7393%
two_lst_tuple_JohnLaRoy         170/s   11332%    4367%       --   -1254%   -2026%   -4182%   -4375%   -5842%   -6079%   -6254%
if_else_DBR                     195/s   14392%    6428%    1434%       --    -882%   -3348%   -3568%   -5246%   -5516%   -5717%
two_lst_compr_Parand            213/s   16750%    8016%    2540%     967%       --   -2705%   -2946%   -4786%   -5083%   -5303%
if_else_1_line_DanSalmo         292/s   26668%   14696%    7189%    5033%    3707%       --    -331%   -2853%   -3260%   -3562%
tuple_if_else                   302/s   27923%   15542%    7778%    5548%    4177%     343%       --   -2609%   -3029%   -3341%
set_1_line                      409/s   41308%   24556%   14053%   11035%    9181%    3993%    3529%       --    -569%    -991%
set_shorter                     434/s   44401%   26640%   15503%   12303%   10337%    4836%    4345%     603%       --    -448%
set_if_else                     454/s   46952%   28358%   16699%   13349%   11290%    5532%    5018%    1100%     469%       --

पायथॉन 3.7 (फंकीस्की से संशोधित) के लिए पूर्ण बेंचमार्क कोड:

good_list = ['.jpg','.jpeg','.gif','.bmp','.png']

import random
import string
my_origin_list = []
for i in range(10000):
    fname = ''.join(random.choice(string.ascii_lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(list(good_list))
    else:
        fext = "." + ''.join(random.choice(string.ascii_lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

# Parand
def two_lst_compr_Parand(*_):
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def if_else_DBR(*_):
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def two_lst_tuple_JohnLaRoy(*_):
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# # Ants Aasma
# def f4():
#     l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
#     return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def zip_Funky(*_):
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def filter_BJHomer(*_):
    return list(filter(lambda e: e[2] in good_list, my_origin_list)), list(filter(lambda e: not e[2] in good_list,                                                                             my_origin_list))

# ChaimG's answer; as a list.
def if_else_1_line_DanSalmo(*_):
    good, bad = [], []
    for e in my_origin_list:
        _ = good.append(e) if e[2] in good_list else bad.append(e)
    return good, bad

# ChaimG's answer; as a set.
def set_1_line(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        _ = good.append(e) if e[2] in good_list_set else bad.append(e)
    return good, bad

# ChaimG set and if else list.
def set_shorter(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        out = good if e[2] in good_list_set else bad
        out.append(e)
    return good, bad

# ChaimG's best answer; if else as a set.
def set_if_else(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        if e[2] in good_list_set:
            good.append(e)
        else:
            bad.append(e)
    return good, bad

# ChaimG's best answer; if else as a set.
def tuple_if_else(*_):
    good_list_tuple = tuple(good_list)
    good, bad = [], []
    for e in my_origin_list:
        if e[2] in good_list_tuple:
            good.append(e)
        else:
            bad.append(e)
    return good, bad

def cmpthese(n=0, functions=None):
    results = {}
    for func_name in functions:
        args = ['%s(range(256))' % func_name, 'from __main__ import %s' % func_name]
        t = Timer(*args)
        results[func_name] = 1 / (t.timeit(number=n) / n) # passes/sec

    functions_sorted = sorted(functions, key=results.__getitem__)
    for f in functions_sorted:
        diff = []
        for func in functions_sorted:
            if func == f:
                diff.append("--")
            else:
                diff.append(f"{results[f]/results[func]*100 - 100:5.0%}")
        diffs = " ".join(f'{x:>8s}' for x in diff)

        print(f"{f:27s} \t{results[f]:,.0f}/s {diffs}")


if __name__=='__main__':
    from timeit import Timer
cmpthese(1000, 'two_lst_compr_Parand if_else_DBR two_lst_tuple_JohnLaRoy zip_Funky filter_BJHomer if_else_1_line_DanSalmo set_1_line set_if_else tuple_if_else set_shorter'.split(" "))

0

यदि आप चतुर पर जोर देते हैं, तो आप विंडन का समाधान और बस थोड़ा चतुर चतुराई ले सकते हैं:

def splay(l, f, d=None):
  d = d or {}
  for x in l: d.setdefault(f(x), []).append(x)
  return d

3
"D या {}" थोड़ा खतरनाक है। यदि कोई खाली डिक्टेट पास हो जाता है, तो उसे जगह पर म्यूट नहीं किया जाएगा।
ब्रायन

सच है, लेकिन यह वापस आ जाता है, इसलिए ... वास्तव में, यह सही उदाहरण है कि आप अपने कोड में अधिक चतुर क्यों नहीं जोड़ना चाहते हैं। :-P
एंडर्स यूरेनियस

0

पहले से ही यहाँ कुछ समाधान हैं, लेकिन फिर भी ऐसा करने का एक और तरीका होगा -

anims = []
images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]

सूची में केवल एक बार Iterates, और थोड़ा अधिक पाइथोनिक लगता है और इसलिए मेरे लिए पठनीय है।

>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file1.bmp', 33L, '.bmp')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> anims = []
>>> images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
>>> print '\n'.join([str(anims), str(images)])
[('file2.avi', 999L, '.avi')]
[('file1.jpg', 33L, '.jpg'), ('file1.bmp', 33L, '.bmp')]
>>>

0

मैं 2-पास तरीका अपनाऊंगा, सूची को छानने से विधेय के मूल्यांकन को अलग करना:

def partition(pred, iterable):
    xs = list(zip(map(pred, iterable), iterable))
    return [x[1] for x in xs if x[0]], [x[1] for x in xs if not x[0]]

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

अभिव्यक्ति-वार, यह अभिव्यक्तियों और मानचित्रण जैसे अभिव्यंजक मुहावरों का लाभ उठाता है।


0

उपाय

from itertools import tee

def unpack_args(fn):
    return lambda t: fn(*t)

def separate(fn, lx):
    return map(
        unpack_args(
            lambda i, ly: filter(
                lambda el: bool(i) == fn(el),
                ly)),
        enumerate(tee(lx, 2)))

परीक्षा

[even, odd] = separate(
    lambda x: bool(x % 2),
    [1, 2, 3, 4, 5])
print(list(even) == [2, 4])
print(list(odd) == [1, 3, 5])

0

अगर आपको किसी बाहरी पुस्तकालय का उपयोग करने में कोई आपत्ति नहीं है, तो मुझे पता है कि इस ऑपरेशन को स्वाभाविक रूप से लागू किया जाएगा:

>>> files = [ ('file1.jpg', 33, '.jpg'), ('file2.avi', 999, '.avi')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
  • iteration_utilities.partition:

    >>> from iteration_utilities import partition
    >>> notimages, images = partition(files, lambda x: x[2].lower() in IMAGE_TYPES)
    >>> notimages
    [('file2.avi', 999, '.avi')]
    >>> images
    [('file1.jpg', 33, '.jpg')]
  • more_itertools.partition

    >>> from more_itertools import partition
    >>> notimages, images = partition(lambda x: x[2].lower() in IMAGE_TYPES, files)
    >>> list(notimages)  # returns a generator so you need to explicitly convert to list.
    [('file2.avi', 999, '.avi')]
    >>> list(images)
    [('file1.jpg', 33, '.jpg')]

0

यकीन नहीं होता कि यह एक अच्छा तरीका है लेकिन इसे इस तरह से भी किया जा सकता है

IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi')]
images, anims = reduce(lambda (i, a), f: (i + [f], a) if f[2] in IMAGE_TYPES else (i, a + [f]), files, ([], []))

0

यदि सूची समूहों और आंतरायिक विभाजकों से बनी है, तो आप इसका उपयोग कर सकते हैं:

def split(items, p):
    groups = [[]]
    for i in items:
        if p(i):
            groups.append([])
        groups[-1].append(i)
    return groups

उपयोग:

split(range(1,11), lambda x: x % 3 == 0)
# gives [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

0
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f not in images]

अच्छा है जब हालत लंबी होती है, जैसे कि आपके उदाहरण में। पाठक को नकारात्मक स्थिति का पता लगाने की आवश्यकता नहीं है और क्या यह अन्य सभी मामलों को पकड़ता है।


0

फिर भी एक और उत्तर, संक्षिप्त लेकिन "बुराई" (सूची-बोध के दुष्प्रभावों के लिए)।

digits = list(range(10))
odd = [x.pop(i) for i, x in enumerate(digits) if x % 2]

>>> odd
[1, 3, 5, 7, 9]

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