programing

조건에 따라 목록을 분할(분할, 분할)하려면 어떻게 해야 합니까?

instargram 2023. 6. 6. 00:25
반응형

조건에 따라 목록을 분할(분할, 분할)하려면 어떻게 해야 합니까?

다음과 같은 코드가 있습니다.

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

는 다음내분것입다니는할하용의 을 나누는 것입니다.mylist조건을 충족하는지 여부에 따라 두 개의 다른 목록으로 분류됩니다.

어떻게 하면 더 우아하게 할 수 있을까요?두 번 반복하는 것을 피할 수 있습니까?mylist그렇게 함으로써 성능을 향상시킬 수 있습니까?

조건을 사용하여 각 요소를 추가할 목록을 선택하여 수동으로 반복합니다.

good, bad = [], []
for x in mylist:
    (bad, good)[x in goodvals].append(x)
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바이트 JPEG를 모두 제거하고 싶을 때는 다음과 같은 것을 추가하면 됩니다.

if f[1] == 0:
    continue

게으른 반복자 접근법은 다음과 같습니다.

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

제안된 모든 솔루션의 문제는 필터링 기능을 두 번 스캔하고 적용한다는 것입니다.나는 다음과 같은 간단한 작은 기능을 만들 것입니다.

def split_into_two_lists(lst, f):
    a = []
    b = []
    for elem in lst:
        if f(elem):
            a.append(elem)
        else:
            b.append(elem)
    return a, b

이렇게 하면 어떤 것도 두 번 처리하지 않고 코드도 반복하지 않습니다.

내 생각엔.나는 게으른 싱글 패스를 제안합니다.partition함수 - 출력 시퀀스에서 상대 순서를 유지합니다.

요구사항

요구사항은 다음과 같습니다.

  • 요소의 상대적 순서 유지(세트 및 사전 없음)
  • 요소에 을 한 않는 (사용하지 않음).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 descripted

내부적으로 한 번에 두 개의 시퀀스를 구축해야 하므로 하나의 출력 시퀀스만 사용하면 다른 하나도 계산됩니다.또한 사용자 요청(처리되었지만 아직 요청되지 않은 요소 저장) 간의 상태를 유지해야 합니다.하기 위해 두 개의 큐를 합니다.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()하지만 우리가 이번에 어떤 종류의 요소를 원하는지 지정할 수 있습니다.씬(scene) 뒤에는 거부된 요소를 폐기하지 않고 두 개의 대기열 중 하나에 배치합니다.

    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두의 제너레이터를 및 두 개의 제너레이터를 반환합니다.첫 번째 생성기는 조건이 유지되는 요소의 연속성을 생성하고, 두 번째 생성기는 보완적인 연속성을 생성합니다.반복기와 생성기는 길고 무한한 시퀀스의 느린 분할을 허용합니다.

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검정 함수를 첫 번째 인수로 사용합니다.

나는 기본적으로 앤더스의 접근법이 매우 일반적이기 때문에 좋아합니다.필터 구문과 일치하도록 분류기를 먼저 배치하고 기본 딕트(가져온 것으로 가정)를 사용하는 버전이 있습니다.

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

번째 실행(사전 작업 편집):집합 사용:

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

그러면 성능이 향상됩니다.그렇지 않으면, 당신이 가지고 있는 것은 괜찮아 보입니다.

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

다른 솔루션과 마찬가지로 키 함수를 정의하여 원하는 수의 그룹으로 나눌 수 있습니다.

우아하고 빠른

Dan Salmo의 의견에 영감을 받아 간결하고 우아하면서도 동시에 가장 빠른 솔루션 중 하나인 솔루션이 있습니다.

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

팁: 터닝goodvals한 세트로 만들면 속도가 빨라집니다.

가장 빠른

최대 속도를 위해 가장 빠른 답변을 취하고 good_list를 집합으로 변환하여 터보차지합니다.이것만으로도 40% 이상의 속도 향상 효과를 얻을 수 있으며, 읽기 쉬운 상태를 유지하는 동안에도 가장 느린 솔루션보다 5.5배 이상 빠른 솔루션을 얻을 수 있습니다.

good_list_set = set(good_list)  # 40%+ 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)  # 40%+ 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%       --

Python 3.7의 전체 벤치마크 코드(FunkySayu에서 수정됨):

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(" "))
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)
bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]

append는 None을 반환하므로 작동합니다.

저는 개적으인, 저당이이목가미가, 때신인좋다습니의 했을 때,이 좋습니다.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세트의 방법도 잘 작동할 것입니다.

FP 스타일로 제작하고자 하는 경우:

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

가장 읽기 쉬운 해결책은 아니지만 적어도 내 목록을 한 번만 반복합니다.

때때로, 목록 이해력은 사용하기에 가장 좋은 것이 아닌 것처럼 보입니다!

저는 사람들이 이 주제에 대해 한 대답에 기초하여 무작위로 생성된 목록에서 테스트했습니다.다음은 목록의 생성입니다(더 나은 방법이 있을 수 있지만 요점은 아닙니다).

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)

cmpte 함수를 사용하면 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%  --
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)

확인합니다.

나는 N 조건을 기준으로 A를 분할하는 일반화가 유용하다고 생각합니다.

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]

요소가 여러 조건을 만족할 수 있는 경우 브레이크를 제거합니다.

이 문제에 대한 또 다른 해결책입니다.저는 가능한 한 빠른 해결책이 필요했습니다.즉, 목록 위에 한 번만 반복하고 결과 목록 중 하나에 데이터를 추가하는 경우에는 O(1)가 좋습니다.이것은 훨씬 짧은 것을 제외하고는 사스타닌이 제공하는 솔루션과 매우 유사합니다.

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,set이 좋아하는이든지 (예를 들어, 예것는좋하아들어 (를당이신예들어)list(lower). 구성합니다.) 목록을 직접 구성하는 변환이 훨씬 빠릅니다.

이 방법은 항목의 순서와 중복 항목의 순서를 유지합니다.

외부 라이브러리를 사용해도 괜찮으시다면 두 가지 작업을 기본적으로 구현하는 것으로 알고 있습니다.

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

예: 짝수와 홀수로 리스트 분할

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, ([], []))

장점:

  • 최단 가능한 방법
  • 술어는 각 요소에 대해 한 번만 적용됩니다.

단점들

  • 기능적 프로그래밍 패러다임에 대한 지식이 필요합니다.

@gnibler의 훌륭한 답변(Butterse!)에서 영감을 받아 여러 파티션에 매핑할 수 있는 접근 방식을 적용할 수 있습니다.

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

해결책

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

목록이 그룹 및 간헐적 구분 기호로 구성된 경우 다음을 사용할 수 있습니다.

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

부울 논리를 사용하여 데이터를 두 배열에 할당

>>> images, anims = [[i for i in files if t ^ (i[2].lower() in IMAGE_TYPES) ] for t in (0, 1)]
>>> images
[('file1.jpg', 33, '.jpg')]
>>> anims
[('file2.avi', 999, '.avi')]

성능을 위해 사용해 보십시오.itertools.

iiter 도구 모듈은 자체적으로 또는 조합하여 유용한 빠르고 메모리 효율적인 핵심 도구 세트를 표준화합니다.이들은 함께 "반복자 대수"를 형성하여 순수 파이썬에서 전문화된 도구를 간결하고 효율적으로 구성할 수 있습니다.

iter tools.iffilter 또는 imap을 참조하십시오.

itertools.iffilter(복제, 반복 가능)

조건이 True인 요소만 반환하는 반복기 만들기

만약 당신이 영리하다고 고집한다면, 당신은 윈든의 해결책과 약간의 거짓된 영리함을 취할 수 있습니다.

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

때로는 목록의 나머지 절반이 필요하지 않을 수도 있습니다.예:

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 '')

여기에는 이미 많은 해결책이 있지만, 또 다른 방법은 다음과 같습니다.

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

목록을 한 번만 반복하고, 조금 더 비단결처럼 보이기 때문에 읽을 수 있습니다.

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

저는 술어의 평가와 목록 필터링을 분리하는 2-pass 접근법을 택합니다.

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)는 많은 논리를 인터프리터에서 고도로 최적화된 반복 및 매핑 코드로 이동시키는 것입니다.이렇게 하면답변에 설명된 것처럼 긴 반복 가능한 항목의 반복 속도를 높일 수 있습니다.

표현력 측면에서, 그것은 이해와 매핑과 같은 표현 관용구를 활용합니다.

이것이 좋은 접근법인지 확실하지 않지만 이러한 방식으로도 수행될 수 있습니다.

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, ([], []))

또 다른 대답은 짧지만 "악"(목록 이해 부작용)입니다.

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]

언급URL : https://stackoverflow.com/questions/949098/how-can-i-partition-split-up-divide-a-list-based-on-a-condition

반응형