[python] Python 그룹 별

인덱스 0 이 값이고 인덱스 1 이 유형 인 데이터 쌍 집합이 있다고 가정합니다 .

input = [
          ('11013331', 'KAT'),
          ('9085267',  'NOT'),
          ('5238761',  'ETH'),
          ('5349618',  'ETH'),
          ('11788544', 'NOT'),
          ('962142',   'ETH'),
          ('7795297',  'ETH'),
          ('7341464',  'ETH'),
          ('9843236',  'KAT'),
          ('5594916',  'ETH'),
          ('1550003',  'ETH')
        ]

다음과 같이 유형별로 (첫 번째 인덱싱 된 문자열 기준) 그룹화하고 싶습니다.

result = [
           {
             type:'KAT',
             items: ['11013331', '9843236']
           },
           {
             type:'NOT',
             items: ['9085267', '11788544']
           },
           {
             type:'ETH',
             items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003']
           }
         ] 

이를 효율적으로 달성하려면 어떻게해야합니까?



답변

2 단계로 수행하십시오. 먼저 사전을 만듭니다.

>>> input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')]
>>> from collections import defaultdict
>>> res = defaultdict(list)
>>> for v, k in input: res[k].append(v)
...

그런 다음 해당 사전을 예상 형식으로 변환하십시오.

>>> [{'type':k, 'items':v} for k,v in res.items()]
[{'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}]

itertools.groupby로도 가능하지만 입력을 먼저 정렬해야합니다.

>>> sorted_input = sorted(input, key=itemgetter(1))
>>> groups = groupby(sorted_input, key=itemgetter(1))
>>> [{'type':k, 'items':[x[0] for x in v]} for k, v in groups]
[{'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}]

이 두 가지 모두 키의 원래 순서를 따르지 않습니다. 주문을 유지하려면 OrderedDict가 필요합니다.

>>> from collections import OrderedDict
>>> res = OrderedDict()
>>> for v, k in input:
...   if k in res: res[k].append(v)
...   else: res[k] = [v]
...
>>> [{'type':k, 'items':v} for k,v in res.items()]
[{'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}]


답변

파이썬의 내장 itertools모듈은 실제로 groupby함수를 가지고 있지만, 그룹화 할 요소는 먼저 그룹화 할 요소가 목록에서 연속되도록 정렬되어야합니다.

from operator import itemgetter
sortkeyfn = itemgetter(1)
input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'),
 ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'),
 ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')]
input.sort(key=sortkeyfn)

이제 입력은 다음과 같습니다.

[('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'),
 ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH'), ('11013331', 'KAT'),
 ('9843236', 'KAT'), ('9085267', 'NOT'), ('11788544', 'NOT')]

groupby형식의 2- 튜플 시퀀스를 반환합니다 (key, values_iterator). 우리가 원하는 것은 이것을 ‘type’이 키이고 ‘items’가 values_iterator에 의해 반환 된 튜플의 0 번째 요소의 목록 인 딕셔너리 목록으로 바꾸는 것입니다. 이렇게 :

from itertools import groupby
result = []
for key,valuesiter in groupby(input, key=sortkeyfn):
    result.append(dict(type=key, items=list(v[0] for v in valuesiter)))

이제 result질문에 명시된대로 원하는 사전이 포함되어 있습니다.

그러나 유형별로 키가 지정된 단일 사전과 값 목록을 포함하는 각 값을 만드는 것을 고려할 수 있습니다. 현재 양식에서 특정 유형의 값을 찾으려면 목록을 반복하여 일치하는 ‘유형’키가 포함 된 사전을 찾은 다음 여기에서 ‘항목’요소를 가져와야합니다. 1- 항목 사전 목록 대신 단일 사전을 사용하는 경우 마스터 사전에 대한 단일 키 조회로 특정 유형의 항목을 찾을 수 있습니다. 를 사용하면 groupby다음과 같습니다.

result = {}
for key,valuesiter in groupby(input, key=sortkeyfn):
    result[key] = list(v[0] for v in valuesiter)

result이제이 dict가 포함되어 있습니다 ( res@KennyTM의 대답 의 중간 defaultdict 와 유사합니다 ).

{'NOT': ['9085267', '11788544'],
 'ETH': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'],
 'KAT': ['11013331', '9843236']}

(이를 한 줄로 줄이려면 다음을 수행 할 수 있습니다.

result = dict((key,list(v[0] for v in valuesiter)
              for key,valuesiter in groupby(input, key=sortkeyfn))

또는 새로운 dict-comprehension 형식을 사용합니다.

result = {key:list(v[0] for v in valuesiter)
              for key,valuesiter in groupby(input, key=sortkeyfn)}


답변

나는 또한 pandas 간단한 그룹화를 좋아했습니다 . 강력하고 간단하며 대규모 데이터 세트에 가장 적합합니다.

result = pandas.DataFrame(input).groupby(1).groups


답변

이 답변은 @PaulMcG의 답변 과 유사 하지만 입력 정렬이 필요하지 않습니다.

함수형 프로그래밍의 경우 groupBy한 줄로 작성할 수 있으며 (가져 오기 제외!) itertools.groupby입력을 정렬 할 필요가 없습니다.

from functools import reduce # import needed for python3; builtin in python2
from collections import defaultdict

def groupBy(key, seq):
 return reduce(lambda grp, val: grp[key(val)].append(val) or grp, seq, defaultdict(list))

(대한 이유 ... or grp 에서 lambda이것에 대한 것입니다 reduce()하려면 lambda때문이다 요구는 첫 번째 인수를 반환 list.append()항상 반환 Noneor 항상 반환됩니다 grp. 즉 그것은 람다가 하나의 표현식을 평가에만 할 수있는 파이썬의 제한을 해결하기 위해 해킹입니다.)

이것은 주어진 함수를 평가하여 키가 발견되고 그 값이 원래 순서의 원래 항목 목록 인 dict를 리턴합니다. OP의 예에서 이것을 호출하면 groupBy(lambda pair: pair[1], input)다음 dict가 반환됩니다.

{'KAT': [('11013331', 'KAT'), ('9843236', 'KAT')],
 'NOT': [('9085267', 'NOT'), ('11788544', 'NOT')],
 'ETH': [('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH')]}

그리고 @PaulMcG의 답변에 따라 OP의 요청 된 형식은 목록 이해로 래핑하여 찾을 수 있습니다. 그래서 이것은 그것을 할 것입니다 :

result = {key: [pair[0] for pair in values],
          for key, values in groupBy(lambda pair: pair[1], input).items()}


답변

다음 함수는 인덱스가있는 키로 모든 길이의 튜플을 빠르게 그룹화합니다 ( 정렬 필요 없음 ).

# given a sequence of tuples like [(3,'c',6),(7,'a',2),(88,'c',4),(45,'a',0)],
# returns a dict grouping tuples by idx-th element - with idx=1 we have:
# if merge is True {'c':(3,6,88,4),     'a':(7,2,45,0)}
# if merge is False {'c':((3,6),(88,4)), 'a':((7,2),(45,0))}
def group_by(seqs,idx=0,merge=True):
    d = dict()
    for seq in seqs:
        k = seq[idx]
        v = d.get(k,tuple()) + (seq[:idx]+seq[idx+1:] if merge else (seq[:idx]+seq[idx+1:],))
        d.update({k:v})
    return d

질문의 경우 그룹화하려는 키의 색인은 1이므로 다음과 같습니다.

group_by(input,1)

준다

{'ETH': ('5238761','5349618','962142','7795297','7341464','5594916','1550003'),
 'KAT': ('11013331', '9843236'),
 'NOT': ('9085267', '11788544')}

요청한 출력은 정확하지 않지만 요구 사항에 적합 할 수 있습니다.


답변

result = []
# Make a set of your "types":
input_set = set([tpl[1] for tpl in input])
>>> set(['ETH', 'KAT', 'NOT'])
# Iterate over the input_set
for type_ in input_set:
    # a dict to gather things:
    D = {}
    # filter all tuples from your input with the same type as type_
    tuples = filter(lambda tpl: tpl[1] == type_, input)
    # write them in the D:
    D["type"] = type_
    D["itmes"] = [tpl[0] for tpl in tuples]
    # append D to results:
    result.append(D)

result
>>> [{'itmes': ['9085267', '11788544'], 'type': 'NOT'}, {'itmes': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'itmes': ['11013331', '9843236'], 'type': 'KAT'}]


답변