제너레이터를 이용한 지연 실행
728x90
"""
메모리 사용 측면에서 제너레이터가 유리한 경우는 현재 값만 필요한 경우
제너레이터를 사용한 피보나치 수열 계산에서도 항시 현재 값만 사용할 뿐 수열 앞쪽에 나온 다른 값을 참조 할수 없다.
이런 알고리즘을 단일 패스 single pass or online 이라고 한다.
이런 특징 때문에 제너레이터를 사용하기 어려운 경우가 있는데, 그럴 때 도움 이 되는 모듈이나 함수가 많다.
그중 표준 라이브러리인 itertools 가 가장 대표적이다. 대표적인 유용한 함수는 다음과 같다.
초 단위로 기록된 20년 치의 데이터를 분석한다고 하면 처리해야 할 데이터의 수는 631,152,000개 입니다.
파일에 저장되어 있고 초단위로 한 줄씩 기록되어 있다.
이 전부를 메모리에 올릴 수는 없는 상황이다.
이데이터에서 간단한 특이점을 찾아야 한다고 가정하면 리스트 할당 없이 제너레이터 만으로 해결할 수 있다
"""
"""
데이터 파일은 "타임스탬프, 값"의 형태로 저장되어 있고 일 평균에서 3시그마를 벗어난 날짜를 찾는 것이 목표다.
먼저 파일을 줄 단위로 읽는 코드를 작성하는 것부터 시작하자. 그리고 각 줄의 값을 파이썬 객체로 출력한다.
알고리즘을 테스트할 수 있도록 가짜 데이터를 생산할 read_fake_data 제너레이터를 작성하자.
read_data 함수와 같은 모양이 되도록 filename parameter 를 받도록 했지만 사용하지 않을것이다.
제너레이터의 next가 호출되어야만 실제로 파일에서 한줄을 읽거나 가짜 데이터를생성하는 지연실행 을 구현했다.
"""
import math
from random import (normalvariate, random)
from datetime import (date, datetime)
from itertools import (count, groupby, islice)
def read_data(filename):
with open(filename) as fd:
for line in fd:
data = line.strip().split(',')
yield list(map(int, data))
def read_fake_data(filename):
for i in count():
sigma = random() * 2
yield (i, normalvariate(0, sigma))
"""
이제 itertools의 groupby 함수를 사용하여 같은 날의 타임스탬프를 하나로 묶는다.
이 함수는 연속적인 항목과 이 항목들을 그룹 지을 키를 인자로 받고, 결과로는 튜플을 생성하는 제너레이터를 반환한다.
이 튜플은 그룹의 키, 그리고 그 그룹의 항목을 생성해주는 제너레이터가 담겨 있다.
키 함수로 date 객체를 반환하는 lambda 함수를 만들고 날짜가 같으면 date 객체도 같으므로 날짜별로 항목을 묶을수 있따.
key 함수는 시간별로 묶거나 년도별 또는 실제 값의 어떤 속성을 이용해서도 만들 수 있다.
유일한 제약은 그룹으로 묶이는 데이터는 연속적이어야 한다는 점이다.
즉, 입력이 A A A A A B B A A 이고 groupby로 글자 별로 묶으면 (A, [A, A, A, A]), (B,[B, B]), (A,[A, A]) 이렇게 세 그룹이 생긴다.
"""
def day_grouper(iterable):
key = lambda timestamp_value: date.fromtimestamp(timestamp_value[0])
return groupby(iterable, key)
"""
이제 특이점을 찾는 코드를 작성해보자. 날짜별로 값을 살펴보고 평균과 최댓값을 계쏙 유지한다.
평균은 온라인 평균 알고리즘과 표준편차 알고리즘으로 계산할 것
최대값은 특이점을 찾기 위해 사용한다.
만약 최댓값이 평군보다 3시그마 이상 벌어지면 해당 날짜의 date객체를 반환하고, 그렇지 않다면 False를 반환하거나 그냥
함수를 종료 해도 된다(자연스레 None을 반환한다). check_anomaly함수는 데이터 필터 역활을 하도록 설계하였으므로
특이점인 경우 True를 반환하고, 그렇지 않으면 무시하도록 False를 반환했다.
이렇게 해서 원래 데이터 중 조건에 맞는 날짜만 걸러내도록 했다.
"""
def check_anomaly(xxx_todo_changeme):
"""
해당 날짜의 평균, 표준편차, 최댓값을 구한다.
해당 날짜의 데이터를 한 번만 읽어서 계산할 수 있또록
단일 패스 평균/표준편차 알고리즘을 사용한다.
"""
(day, day_data) = xxx_todo_changeme
n = 0
mean = 0
M2 = 0
max_value = None
for timestamp, value in day_data:
n += 1
delta = value - mean
mean = mean + delta / n
M2 += delta * (value - mean)
if max_value:
max_value = max(max_value, value)
else:
max_value = value
variance = M2 / (n - 1)
standard_deviation = math.sqrt(variance)
# 다음은 실제로 해당 날짜의 데이터가 특이점이라면 해당 날짜를 반환하고
# 그렇지 않다면 False 반환한다.
if max_value > mean + 3 * standard_deviation:
return day
return False
# 모두 함께 연결하기
if __name__ == "__main__":
print("Using day_grouper:")
data = read_fake_data("fake_filename")
data_day = day_grouper(data)
"""
filter는 주어진 필터 조건을 충족하지 못하는 항목을 제거합니다.
기본값으로 (첫번째 인자로 None을 넘기면 기본값으로 동작한다) False인 항목을 제거한다.
이렇게 해서 check_anomaly에서 정상이라고 판단한 날짜는 포함하지 않는다.
"""
anomalous_dates = filter(None, map(check_anomaly, data_day))
first_anomalous_date = next(anomalous_dates)
print("The first anomalous date is: ", first_anomalous_date)
728x90
'Python > python - pythonic' 카테고리의 다른 글
데커레이터 함수 이해하기 (0) | 2020.11.27 |
---|---|
메서드타입 - class method (0) | 2020.11.27 |
파이썬에서 효율적으로 피보나치 수열 접근하기 (0) | 2020.11.25 |
bisect 모듈을 이용한 가까운 값 찾기 (0) | 2020.11.25 |
ctyhon - cProfile 모듈 사용하기 (0) | 2020.11.24 |
댓글
이 글 공유하기
다른 글
-
데커레이터 함수 이해하기
데커레이터 함수 이해하기
2020.11.27 -
메서드타입 - class method
메서드타입 - class method
2020.11.27 -
파이썬에서 효율적으로 피보나치 수열 접근하기
파이썬에서 효율적으로 피보나치 수열 접근하기
2020.11.25 -
bisect 모듈을 이용한 가까운 값 찾기
bisect 모듈을 이용한 가까운 값 찾기
2020.11.25