본문 바로가기

😀 기초/넘파이(NumPy)

3장 배열의 연산(3.3)

3.3 배열의 연산

 

1. 백터화 연산
- 앞서 넘파이가 백터화 연산을 지원한다고 이야기 함.
- 백터화 연산을 쓰면 명시적으로 반복문을 사용하지 않아도 배열의 모든 원소에 대해 반복연산이 가능
- 백터화 연산의 또다른 장점은 선형 대수 공식과 동일한 아주 간단한 파이썬 코드를 작성할 수 있음.

- 예를 들어 선형 대해수에서 두 벡터의 합은 다음과 같이 구함.

 

- (그림 참고)
- 만약 백터화 연산을 사용하지 않으면 반복문을 사용하여 다음과 같이 만들어야 한다.
- 이 코드에서 %%time은 셀 코드 실행시간 측정하는 아이파이썬 매직 명령이다.

x = np.arange(1, 10001)
y = np.arange(10001, 20001)

%%time
z = np.zeros_like(x)
for i in range(10000):
    z[i] = x[i] + y[i]
#결과
Wall time: 4 ms
    
z[:10]
#결과
array([10002, 10004, 10006, 10008, 10010, 10012, 10014, 10016, 10018,
       10020])

- 그러나 백터화 연산을 사용하면 덧셈 연산 하나로 끝!

%%time
z = x + y
#결과
Wall time: 989 µs

z[:10]
#결과
array([10002, 10004, 10006, 10008, 10010, 10012, 10014, 10016, 10018,
       10020])
       
# 사칙 연산뿐만이 아니라 비교 연산도 백터화 연산이 가능

a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])

a == b
#결과
array([False, False, False, False])

a >= b
#결과
array([False, False, False, False])

# 모든 원소 다 같이 비교는 all 명령어

a = np.array([1, 2, 3, 4])
b = np.array([4, 2, 2, 4])
c = np.array([1, 2, 3, 4])

np.all(a == b)
False
#결과

np.all(a == c)
#결과
True

# 지수 함수, 로그 함수 등의 수학 함수도
# 백터화 연산을 지원한다.

a = np.arange(5)
a
#결과
array([0, 1, 2, 3, 4])

np.exp(a)
#결과
array([ 1.        ,  2.71828183,  7.3890561 , 20.08553692, 54.59815003])

10 ** a
#결과
array([    1,    10,   100,  1000, 10000], dtype=int32)


np.log(a + 1)
#결과
array([0.        , 0.69314718, 1.09861229, 1.38629436, 1.60943791])

2. 스칼라와 벡터/행렬의 곱셈
- 스칼라와 벡터/행렬의 곱도 선형 대수에서 사용하는 식과 넘파이 코드가 일치

x = np.arange(10)
x

#결과
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

x = np.arange(12).reshape(3, 4)
x
#결과
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

100 * x
#결과
array([[   0,  100,  200,  300],
       [ 400,  500,  600,  700],
       [ 800,  900, 1000, 1100]])

3. 브로드캐스팅
- 백터(또는 행렬)끼리 덧셈 등을 하려면 크기가 같아야 함.
- 넘파이엥서는 서로 다른 크기 가진 두 배열의 사칙 연산도 지원.
- 이 기능을 브로드캐스팅(brodcasting)

x = np.arange(5)
x
#결과
array([0, 1, 2, 3, 4])

y = np.ones_like(x)
y
#결과
array([1, 1, 1, 1, 1])

x + y
#결과
array([1, 2, 3, 4, 5])

x + 1
#결과
array([1, 2, 3, 4, 5])

# 😂
x = np.vstack([range(7)[i:i + 3] for i in range(5)])
x
#결과
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6]])

# 😂
y = np.arange(5)[:, np.newaxis]
y
#결과
array([[0],
       [1],
       [2],
       [3],
       [4]])
       
x + y
#결과
array([[ 0,  1,  2],
       [ 2,  3,  4],
       [ 4,  5,  6],
       [ 6,  7,  8],
       [ 8,  9, 10]])

4. 차원 축소 연산
- 넘파이는 다음과 같은 차원 축소 연산 명령 혹은 메서드를 지원한다.
- 최대/최소 : min, max, argmin, argmax
- 통계 : sum, mean, median, std, var
- 불리언 : all, any

x = np.array([1, 2, 3, 4])
x

#결과
array([1, 2, 3, 4])

np.sum(x)
#결과
10

x = np.array([1, 3, 2])
x.min()
#결과
1

x.max()
#결과
3

x.argmin() #최솟값의 위치
#결과
0

x.argmax() #최솟값의 위치
#결과
1

x = np.array([1, 2, 3, 1])
x.mean()
#결과
1.75

np.median(x)
#결과
1.5

np.all([True,True, False])
#결과
False

np.any([True, True, False])
#결과
True

a = np.zeros((100, 100), dtype=np.int)
a
#결과
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]])
       
np.all(a == a)
#결과
True

np.any(a != 0)
#결과
False

a = np.array([1, 2, 3, 2])
b = np.array([2, 2, 3, 2])
c = np.array([6, 4, 4, 5])

((a <= b) & (b <= c)).all()
#결과
True

- 연산의 대상이 2차원 이상인 경우에는 어느 차원으로 계싼을 할 지를 axis 인수를 사용하여 지시
- axis=0인 경우는 열 연산. 디폴트 값은 axis=0
- axis=1인 경우는 행 연산.

x = np.array([[1, 1], [2,2]])
x

#결과
array([[1, 1],
       [2, 2]])
       
x.sum()
#결과
6

x.sum(axis=0) # 열 합계
#결과
array([3, 3])

x.sum(axis=1) # 행 합계
#결과
array([2, 4])

 

5. 정렬
- sort함수나 메서드를 사용하여 배열 안의 원소를 크기에 따라 정렬하여 새로운 배열을 만들 수 있다.
- 2차원 이상인 경우 axis인수를 사용
- 디폴트 값은 -1 즉 가장 안쪽(나중)의 차원이다.

a = np.array([[4, 3, 5, 7],
             [1, 12, 11, 9],
             [2, 15, 1, 14]])
a
#결과
array([[ 4,  3,  5,  7],
       [ 1, 12, 11,  9],
       [ 2, 15,  1, 14]])

np.sort(a) # axis=-1 또는 axis=1 과 동일
#결과
array([[ 3,  4,  5,  7],
       [ 1,  9, 11, 12],
       [ 1,  2, 14, 15]])

np.sort(a, axis=0)
#결과
array([[ 1,  3,  1,  7],
       [ 2, 12,  5,  9],
       [ 4, 15, 11, 14]])

# sort메서드는 해당 객체의 자료 자체가
# 변화하는 자체변화(in-place) 메서드이므로 사용할 때 주의

a.sort(axis=1)
a
#결과
array([[ 3,  4,  5,  7],
       [ 1,  9, 11, 12],
       [ 1,  2, 14, 15]])

# 만약 자료를 정렬하는 것이 아니라 
# 순서만 알고 싶다면? argsort 사용
a = np.array([42, 38, 12, 25])
j = np.argsort(a)
j
#결과
array([2, 3, 1, 0], dtype=int64)

a[j]
#결과
array([12, 25, 38, 42])

np.sort(a)
#결과
array([12, 25, 38, 42])