6.9. 유니버설 함수(universal function)¶
유니버설 함수(ufunc) 는 한 번의 호출로 배열의 모든 요소에 적용되는 수학 함수입니다. 앞 페이지의 산술 연산자들은 연산자 구문을 입은 유니버설 함수입니다. 이 페이지는 삼각함수, 지수/로그, 반올림 등을 다루는 이름 붙은 함수들의 목록입니다.
각 ufunc는 스칼라, Python 이터러블, 또는 ndarray 를 받아들이고, 입력이 스칼라였을 때는 단일 float를, 그렇지 않으면 float ndarray 를 반환합니다:
from ulab import numpy as np
np.exp(2.0) # 7.389...
np.sin(range(4)) # 1-D float ndarray
np.sqrt([1, 4, 9, 16]) # array([1.0, 2.0, 3.0, 4.0])
a = np.arange(9).reshape((3, 3))
np.exp(a) # 3x3 float ndarray
6.9.1. 함수 목록¶
numpy 는 임베디드 애플리케이션이 가장 자주 사용하는 수학 함수들을 제공합니다:
삼각함수 –
sin(),cos(),tan(),asin(),acos(),atan(),arctan2(),sinh(),cosh(),tanh(),asinh(),acosh(),atanh(),sinc().
각 함수는 한 번의 라이브러리 호출로 배열 전체를 처리합니다. math.sin() 을 요소별로 호출하는 Python 리스트 컴프리헨션 대비 속도 향상은 일반적인 버퍼에서 10~30배입니다.
6.9.2. out= 키워드¶
각 ufunc 호출은 보통 출력을 담을 새 결과 배열을 할당합니다. 초당 여러 번 실행되는 루프에서는 이러한 할당이 누적되어 RAM을 낭비합니다. 입력과 동일한 형태의 이미 존재하는 float 배열인 out= 을 전달하면 새 배열을 할당하는 대신 그 배열에 결과를 씁니다:
x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)
while True:
np.sin(x, out=y)
# use y ...
out 의 dtype이나 형태가 잘못되면 함수는 예외를 발생시킵니다. 이 키워드는 이 페이지의 모든 ufunc에서 지원되며, 스트리밍 신호 처리 루프를 할당 없이 유지하는 가장 깔끔한 방법입니다.
6.9.3. 두 인수 ufunc¶
arctan2() 는 위 목록에서 유일한 진짜 두 인수 ufunc입니다. 이 함수는 y / x 의 사분면을 고려한 아크탄젠트를 반환하고 두 피연산자를 브로드캐스트합니다:
y = np.array([1, 2.2, 33.33, 444.444])
np.arctan2(y, 1.0) # against a scalar
np.arctan2(1.0, y) # the other way
np.arctan2(y, y) # against another array
6.9.4. 유니버설 함수 조합하기¶
유니버설 함수는 다른 배열 표현식과 마찬가지로 조합됩니다. 카메라에서 자주 등장하는 몇 가지 패턴이 있습니다:
감마 보정(float 공간에서)
gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma
간단한 저역 통과 평활기(alpha 가 1.0 에 가까울수록 느린 업데이트를 의미함):
alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample
시그모이드
sigmoid = 1.0 / (1.0 + np.exp(-x))
dB 단위 파워 스펙트럼
spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)
6.9.5. np.vectorize¶
일반적인 Python 함수는 vectorize() 로 ufunc 형태로 승격될 수 있습니다. 그 결과 호출 가능한 객체는 스칼라, 이터러블, 또는 ndarray 값을 받아들입니다:
def f(x):
return x * x
vf = np.vectorize(f)
vf(44.0) # array([1936.0])
vf(np.array([1, 2, 3, 4])) # array([1.0, 4.0, 9.0, 16.0])
vf([2, 3, 4]) # array([4.0, 9.0, 16.0])
기본적으로 결과 dtype은 float 입니다. otypes= 로 이를 재정의할 수 있습니다:
vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)
Python 함수는 단일 인수를 받아 단일 숫자를 반환해야 합니다.
vectorize() 는 대부분 문법적 인 것입니다. 래핑된 Python 함수는 여전히 요소당 한 번씩 실행되어야 하므로, 진짜 ufunc가 피하는 요소별 인터프리터 비용의 대부분이 다시 발생합니다. 리스트 컴프리헨션 대비 진짜 유니버설 함수의 30배가 아니라 30%~50% 정도의 적당한 속도 향상을 기대하십시오. 이는 하나의 함수가 동일한 이름으로 스칼라, 리스트, 그리고 배열에 대해 동작해야 할 때 적합한 도구이며, 순수한 속도가 목표일 때는 적합하지 않습니다.
위에 나열된 모든 유니버설 함수의 전체 호출 시그니처는 numpy — numpy 호환 배열 연산 를 참조하십시오.