Python에서 반복기의 요소 수 가져오기
일반적으로 파이썬의 반복기에서 각 요소를 반복하고 계산하지 않고 몇 개의 요소가 있는지 알 수 있는 효율적인 방법이 있습니까?
이 코드는 작동해야 합니다.
>>> iter = (i for i in range(50))
>>> sum(1 for _ in iter)
50
각 항목을 반복해서 세어 보기는 하지만, 가장 빠른 방법입니다.
또한 반복기에 항목이 없는 경우에도 작동합니다.
>>> sum(1 for _ in range(0))
0
물론 무한 입력을 위해 영원히 실행되므로 반복기는 무한할 수 있습니다.
>>> sum(1 for _ in itertools.count())
[nothing happens, forever]
또한 이 작업을 수행하면 반복기가 모두 소모되고 이후 사용 시도 시 요소가 표시되지 않습니다.그것은 파이썬 반복기 설계의 피할 수 없는 결과입니다.요소를 보관하려면 목록 같은 데 저장해야 합니다.
아니요, 불가능해요
예:
import random
def gen(n):
for i in xrange(n):
if random.randint(0, 1) == 0:
yield i
iterator = gen(10)
길이:iterator
반복하기 전까지는 알 수 없습니다.
아니요, 모든 방법을 사용하려면 모든 결과를 해결해야 합니다.할수있습니다
iter_length = len(list(iterable))
하지만 무한 반복기에서 실행하면 당연히 다시는 돌아오지 않을 것입니다.또한 반복기를 사용하므로 내용을 사용하려면 재설정해야 합니다.
어떤 실제 문제를 해결하려고 하는지 알려주면 실제 목표를 달성할 수 있는 더 나은 방법을 찾는 데 도움이 될 수 있습니다.
사용하기: 사용하기list()
한 번에 전체 편집 가능한 내용을 메모리에 읽어 들이기 때문에 바람직하지 않을 수 있습니다.또 다른 방법은 하는 것입니다.
sum(1 for _ in iterable)
다른 사람이 투고한 바와 같이그것은 기억에 남는 것을 피할 것입니다.
할 수 없습니다. 단, 특정 반복기 유형은 가능한 특정 메서드를 구현합니다.
일반적으로 반복기를 사용하는 경우에만 반복기 항목을 계산할 수 있습니다.가장 효율적인 방법 중 하나는 다음과 같습니다.
import itertools
from collections import deque
def count_iter_items(iterable):
"""
Consume an iterable not reading it into memory; return the number of items.
"""
counter = itertools.count()
deque(itertools.izip(iterable, counter), maxlen=0) # (consume at C speed)
return next(counter)
3 대체(Python 3.x의 경우)itertools.izip
와 함께zip
).
약간. 확인할 수 있어요.__length_hint__
그러나 (적어도 Python 3.4까지는) 문서화되지 않은 구현 세부 사항이며(스레드에 있는 메시지 뒤에 있음), 그 대신에 매우 잘 사라지거나 비음 악마를 소환할 수 있다는 점에 주의해야 합니다.
그렇지 않으면, 안 됩니다.반복기는 메소드만 노출하는 개체입니다.필요한 횟수만큼 호출할 수 있으며 결국 상승하거나 상승하지 않을 수 있습니다.다행히도, 이 행동은 대부분 코더에게 투명합니다.:)
빠른 벤치마크:
import collections
import itertools
def count_iter_items(iterable):
counter = itertools.count()
collections.deque(itertools.izip(iterable, counter), maxlen=0)
return next(counter)
def count_lencheck(iterable):
if hasattr(iterable, '__len__'):
return len(iterable)
d = collections.deque(enumerate(iterable, 1), maxlen=1)
return d[0][0] if d else 0
def count_sum(iterable):
return sum(1 for _ in iterable)
iter = lambda y: (x for x in xrange(y))
%timeit count_iter_items(iter(1000))
%timeit count_lencheck(iter(1000))
%timeit count_sum(iter(1000))
결과:
10000 loops, best of 3: 37.2 µs per loop
10000 loops, best of 3: 47.6 µs per loop
10000 loops, best of 3: 61 µs per loop
즉, 간단한 count_iter_items가 방법입니다.
python3에 대해 이 값 조정:
61.9 µs ± 275 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
74.4 µs ± 190 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
82.6 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
저는 이것에 대한 카디널리티 패키지가 마음에 듭니다. 매우 가볍고, 사용 가능한 범위에 따라 가능한 가장 빠른 구현을 사용하려고 합니다.
용도:
>>> import cardinality
>>> cardinality.count([1, 2, 3])
3
>>> cardinality.count(i for i in range(500))
500
>>> def gen():
... yield 'hello'
... yield 'world'
>>> cardinality.count(gen())
2
실제.count()
구현은 다음과 같습니다.
def count(iterable):
if hasattr(iterable, '__len__'):
return len(iterable)
d = collections.deque(enumerate(iterable, 1), maxlen=1)
return d[0][0] if d else 0
그래서, 그 토론의 요약을 알고 싶은 사람들을 위해.다음을 사용하여 5천만 길이의 제너레이터 식을 계산한 최종 최고 점수:
len(list(gen))
,len([_ for _ in gen])
,sum(1 for _ in gen),
ilen(gen)
(more_iter 도구에서),reduce(lambda c, i: c + 1, gen, 0)
,
실행 성능(메모리 소비 포함)에 따라 정렬되어 있으면 다음과 같이 놀라게 됩니다.
```
1: test_list.py:8: 0.492 KiB
gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))
('list, sec', 1.9684218849870376)
2: test_list_message.py:8: 0.867 KiB
gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])
('list_message, sec', 2.5885991149989422)
3: test_sum.py:8: 0.859KiB
gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()
('sum, sec', 3.441088170016883)
4: more_iterools/more.py:413 : 1.266 KiB
d = deque(enumerate(iterable, 1), maxlen=1)
test_ilen.py:10: 0.875 KiB
gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)
('ilen, sec', 9.81225685 1990242)
5: test_proxy.py:8: 0.859KiB
gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)
('sec, sec', 13.436614598002052) '''
그렇게,len(list(gen))
가 높고 이 적습니다.
반복기는 일종의 버퍼 또는 스트림에 의해 읽힐 다음 개체에 대한 포인터를 가진 객체일 뿐입니다. 이는 링크된 목록과 같습니다. 링크된 목록을 반복할 때까지 얼마나 많은 것이 있는지 알 수 없습니다.반복기는 인덱싱을 사용하는 대신 참조를 통해 다음에 무엇이 있는지 알려주는 것뿐이므로 효율적입니다(그러나 본 대로 다음에 몇 개의 항목이 있는지 확인할 수 없음).
당신의 원래 질문과 관련하여, 대답은 여전히 파이썬에서 반복기의 길이를 알 수 있는 일반적인 방법이 없다는 것입니다.
당신의 질문이 pysam 라이브러리의 응용 프로그램에 의해 동기 부여되었다는 것을 고려할 때, 저는 더 구체적인 답변을 드릴 수 있습니다.저는 PySAM에 기여하고 있으며, 최종적인 답변은 SAM/BAM 파일이 정확한 정렬된 읽기 수를 제공하지 않는다는 것입니다.또한 BAM 인덱스 파일에서 이 정보를 쉽게 사용할 수 없습니다.가장 좋은 방법은 여러 개의 정렬을 읽은 후 파일 포인터의 위치를 사용하여 대략적인 정렬 수를 추정하고 파일의 전체 크기를 기준으로 추정하는 것입니다.이는 진행 표시줄을 구현하기에는 충분하지만 일정한 시간에 정렬을 카운트하는 방법은 아닙니다.
컴퓨터에서 "어떤 것"의 길이를 얻는 두 가지 방법이 있습니다.
첫 번째 방법은 카운트를 저장하는 것입니다. 이를 수정하려면 파일/데이터를 터치하는 모든 항목(또는 인터페이스만 노출하는 클래스)이 필요하지만 결국에는 동일한 항목으로 귀결됩니다.
다른 방법은 그것을 반복해서 그것이 얼마나 큰지 세어보는 것입니다.
저는 여기서 언급한 여러 접근 방식의 실행 시간을 비교하는 마이크로 벤치마크를 갖는 것이 가치가 있을 수 있다고 생각했습니다.
고지 사항:사용 중simple_benchmark
벤치마크를 위해 (내가 쓴 라이브러리) 그리고 또한 포함합니다.iteration_utilities.count_items
(내가 작성한 타사 문서의 함수).
좀 더 차별화된 결과를 제공하기 위해 두 가지 벤치마크를 수행했습니다. 하나는 폐기하기 위해 중간 컨테이너를 구축하지 않는 접근 방식과 다음과 같은 방법을 포함했습니다.
from simple_benchmark import BenchmarkBuilder
import more_itertools as mi
import iteration_utilities as iu
b1 = BenchmarkBuilder()
b2 = BenchmarkBuilder()
@b1.add_function()
@b2.add_function()
def summation(it):
return sum(1 for _ in it)
@b1.add_function()
def len_list(it):
return len(list(it))
@b1.add_function()
def len_listcomp(it):
return len([_ for _ in it])
@b1.add_function()
@b2.add_function()
def more_itertools_ilen(it):
return mi.ilen(it)
@b1.add_function()
@b2.add_function()
def iteration_utilities_count_items(it):
return iu.count_items(it)
@b1.add_arguments('length')
@b2.add_arguments('length')
def argument_provider():
for exp in range(2, 18):
size = 2**exp
yield size, [0]*size
r1 = b1.run()
r2 = b2.run()
import matplotlib.pyplot as plt
f, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=[15, 18])
r1.plot(ax=ax2)
r2.plot(ax=ax1)
plt.savefig('result.png')
결과는 다음과 같습니다.
로그 로그 축을 사용하여 모든 범위(작은 값, 큰 값)를 검사할 수 있습니다.그림은 정성적 비교를 위한 것이므로 실제 값은 그다지 흥미롭지 않습니다.일반적으로 y축(수직)은 시간을 나타내고 x축(수평)은 입력 "가급"의 요소 수를 나타냅니다.수직 축이 낮으면 속도가 빨라집니다.
위쪽 그림은 중간 리스트가 사용되지 않은 접근법을 보여줍니다.그것은 그것을 보여줍니다.iteration_utilities
, 접이가빨고랐장, ▁by▁approach▁was다니▁followed,이었습▁fastest.more_itertools
그리고 가장 느린 것은 사용하는 것이었습니다.sum(1 for _ in iterator)
.
아래 그림에는 다음과 같은 방법도 포함되어 있습니다.len()
한 번은 중간 목록에 있습니다.list
그리고 한번은 목록 이해력과 함께.에 대한 접근 방식len(list)
여기서 가장 빨랐지만, 차이점은iteration_utilities
접근은 거의 무시할 수 있습니다.이해력을 사용하는 접근법은 사용하는 것보다 훨씬 느렸습니다.list
직접적으로.
요약
여기서 언급된 모든 접근 방식은 입력 길이에 대한 의존성을 보여주었고 반복 가능한 모든 요소에 대해 반복되었습니다.반복이 숨겨지더라도 반복 없이는 길이를 얻을 수 없습니다.
않는 에는 타사확원않하는경우다사음다용니합을지장을다를 사용합니다.len(list(iterable))
테스트된 접근 방식 중 가장 빠른 접근 방식이지만, 훨씬 더 많은 메모리를 사용할 수 있는 중간 목록을 생성합니다.
만약 당신이 추가 패키지를 개의치 않으셔도 됩니다.iteration_utilities.count_items
거의 그것만큼 빠를 것입니다.len(list(...))
추가 메모리가 필요하지 않습니다.
그러나 마이크로벤치마크가 목록을 입력으로 사용했다는 점에 유의해야 합니다.벤치마크의 결과는 길이를 얻고자 하는 반복 가능성에 따라 다를 수 있습니다.로 테스트도 했습니다.range
그리고 단순한 유전자 표현과 경향은 매우 비슷했지만, 입력의 종류에 따라 시기가 변하지 않을 것이라는 점을 배제할 수 없습니다.
최신 버전의 Python에서 이에 대한 벤치마크를 다시 실행하기로 결정하고 벤치마크의 거의 완전한 역전을 발견했습니다.
다음 명령을 실행했습니다.
py -m timeit -n 10000000 -s "it = iter(range(1000000))" -s "from collections import deque" -s "from itertools import count" -s "def itlen(x):" -s " return len(tuple(x))" -- "itlen(it)"
py -m timeit -n 10000000 -s "it = iter(range(1000000))" -s "from collections import deque" -s "from itertools import count" -s "def itlen(x):" -s " return len(list(x))" -- "itlen(it)"
py -m timeit -n 10000000 -s "it = iter(range(1000000))" -s "from collections import deque" -s "from itertools import count" -s "def itlen(x):" -s " return sum(map(lambda i: 1, x))" -- "itlen(it)"
py -m timeit -n 10000000 -s "it = iter(range(1000000))" -s "from collections import deque" -s "from itertools import count" -s "def itlen(x):" -s " return sum(1 for _ in x)" -- "itlen(it)"
py -m timeit -n 10000000 -s "it = iter(range(1000000))" -s "from collections import deque" -s "from itertools import count" -s "def itlen(x):" -s " d = deque(enumerate(x, 1), maxlen=1)" -s " return d[0][0] if d else 0" -- "itlen(it)"
py -m timeit -n 10000000 -s "it = iter(range(1000000))" -s "from collections import deque" -s "from itertools import count" -s "def itlen(x):" -s " counter = count()" -s " deque(zip(x, counter), maxlen=0)" -s " return next(counter)" -- "itlen(it)"
다음 각 항목의 타이밍에 해당합니다.itlen*(it)
함수:
it = iter(range(1000000))
from collections import deque
from itertools import count
def itlen1(x):
return len(tuple(x))
def itlen2(x):
return len(list(x))
def itlen3(x):
return sum(map(lambda i: 1, x))
def itlen4(x):
return sum(1 for _ in x)
def itlen5(x):
d = deque(enumerate(x, 1), maxlen=1)
return d[0][0] if d else 0
def itlen6(x):
counter = count()
deque(zip(x, counter), maxlen=0)
return next(counter)
AMD Ryzen 75800H 및 16GB RAM이 있는 Windows 11, Python 3.11 시스템에서 다음 출력을 받았습니다.
10000000 loops, best of 5: 103 nsec per loop
10000000 loops, best of 5: 107 nsec per loop
10000000 loops, best of 5: 138 nsec per loop
10000000 loops, best of 5: 164 nsec per loop
10000000 loops, best of 5: 338 nsec per loop
10000000 loops, best of 5: 425 nsec per loop
그 말은 그 말이len(list(x))
그리고.len(tuple(x))
동점, 그 뒤를 따릅니다.sum(map(lambda i: 1, x))
그리고 바로 옆에sum(1 for _ in x)
그러면 다른 답변에 의해 언급되거나 카디널리티에 사용되는 다른 더 복잡한 방법들은 적어도 2배 더 느립니다.
이러한 유형의 정보를 파일 헤더에 넣고 pysam에서 이에 대한 액세스 권한을 제공하는 것이 일반적입니다.형식은 잘 모르겠지만 API는 확인하셨나요?
다른 사람들이 말했듯이, 당신은 반복기로 길이를 알 수 없습니다.
이는 개체에 대한 포인터인 반복기의 정의와 다음 개체로 이동하는 방법에 대한 정보에 반하는 것입니다.
반복자는 종료될 때까지 몇 번 더 반복할 수 있는지 알 수 없습니다.이것은 무한대일 수 있습니다. 따라서 무한대가 여러분의 답이 될 수 있습니다.
일반적으로 요청한 작업을 수행할 수는 없지만, 반복된 항목 수를 세어 보는 것이 유용한 경우가 많습니다.이를 위해 jaraco.iter 도구를 사용할 수 있습니다.카운터 또는 이와 유사.다음은 Python 3과 rwt를 사용하여 패키지를 로드하는 예입니다.
$ rwt -q jaraco.itertools -- -q
>>> import jaraco.itertools
>>> items = jaraco.itertools.Counter(range(100))
>>> _ = list(counted)
>>> items.count
100
>>> import random
>>> def gen(n):
... for i in range(n):
... if random.randint(0, 1) == 0:
... yield i
...
>>> items = jaraco.itertools.Counter(gen(100))
>>> _ = list(counted)
>>> items.count
48
이것은 이론적으로 불가능합니다. 사실 이것은 정지 문제입니다.
증명
로 어떤 길이)를합니다.g
를 사용하기len(g)
.
모든 프로그램에 대해P
이제 전환하겠습니다.P
발전기에.g(P)
반점또종대해의 반환점 P
값을 반환하는 대신 산출합니다.
한다면len(g(P)) == infinity
P는 멈추지 않습니다.
이것은 불가능한 것으로 알려진 멈춤 문제를 해결합니다. 위키백과를 참조하십시오.모순.
따라서 제네릭 제너레이터를 반복하지 않고는 제네릭 제너레이터의 요소를 셀 수 없습니다(==프로그램을 통해 실행 중인 모듈).
좀 더 구체적으로, 고려해 보세요.
def g():
while True:
yield "more?"
길이는 무한합니다.이러한 발전기는 무한히 많습니다.
한 가지 간단한 방법은 내장 기능을 사용하는 것입니다.set()
또는list()
:
A: set()
인 방법복기에 중복된 항목이 없는 경우(반복적인 방법)
iter = zip([1,2,3],['a','b','c'])
print(len(set(iter)) # set(iter) = {(1, 'a'), (2, 'b'), (3, 'c')}
Out[45]: 3
또는
iter = range(1,10)
print(len(set(iter)) # set(iter) = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Out[47]: 9
B: list()
에
iter = (1,2,1,2,1,2,1,2)
print(len(list(iter)) # list(iter) = [1, 2, 1, 2, 1, 2, 1, 2]
Out[49]: 8
# compare with set function
print(len(set(iter)) # set(iter) = {1, 2}
Out[51]: 2
def count_iter(iter):
sum = 0
for _ in iter: sum += 1
return sum
반복기가 소진되지 않도록 반복하지 않고 항목 수를 세고 나중에 다시 사용하는 것이 좋습니다.은 이에서가다니합으로 가능합니다.copy
또는deepcopy
import copy
def get_iter_len(iterator):
return sum(1 for _ in copy.copy(iterator))
###############################################
iterator = range(0, 10)
print(get_iter_len(iterator))
if len(tuple(iterator)) > 1:
print("Finding the length did not exhaust the iterator!")
else:
print("oh no! it's all gone")
출은입 "입니다.Finding the length did not exhaust the iterator!
"
선택적으로(그리고 불필요하게) 기본 제공되는 기능을 음영 처리할 수 있습니다.len
기능은 다음과 같습니다.
import copy
def len(obj, *, len=len):
try:
if hasattr(obj, "__len__"):
r = len(obj)
elif hasattr(obj, "__next__"):
r = sum(1 for _ in copy.copy(obj))
else:
r = len(obj)
finally:
pass
return r
언급URL : https://stackoverflow.com/questions/3345785/getting-number-of-elements-in-an-iterator-in-python
'programing' 카테고리의 다른 글
Oracle: 패키지 내부의 호출 저장 프로시저 (0) | 2023.06.13 |
---|---|
어떻게 줄리아에게 엑셀을 읽습니까? (0) | 2023.06.13 |
루비-oci8을 설치하는 방법은? (0) | 2023.06.13 |
openpyxl을 사용하여 워크시트를 한 워크북에서 다른 워크북으로 복사하는 방법은 무엇입니까? (0) | 2023.06.13 |
이벤트 이미터와 이벤트 이미터의 차이점은 무엇입니까? (0) | 2023.06.13 |