(6주차) 4월11일
numpy (@, concat, stack, sum, mean, std, max, min, prod, argmax, argmin, cumsum, cumprod, diff)
-
(1/6) 2차원 배열과 연립1차 방정식, @의 유연성, 차원
-
(2/6) concat
-
(3/6) stack
-
(4/6) sum, mean, std, max, min, prod
-
(5/6) argmax, argmin, cumsum, cumprod, diff
-
(6/6) 숙제설명
import numpy as np
-
아래의 연립방정식 고려
$\begin{cases} y+z+w = 3 \\ x+z+w = 3 \\ x+y+w = 3 \\ x+y+z = 3 \end{cases}$
-
행렬표현?
$\begin{bmatrix} 0 & 1 & 1 & 1 \\ 1 & 0 & 1 & 1 \\ 1 & 1 & 0 & 1 \\ 1 & 1 & 1 & 0 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ w \end{bmatrix} = \begin{bmatrix} 3 \\ 3 \\ 3 \\ 3 \end{bmatrix}$
-
풀이
A = np.array([[0,1,1,1],[1,0,1,1],[1,1,0,1],[1,1,1,0]])
A
b= np.array([3,3,3,3]).reshape(4,1)
b
np.linalg.inv(A) @ b
-
다른풀이
b를 아래와 같이 만들어도 된다.
b=np.array([3,3,3,3])
b
b.shape # b.shape은 길이가 1인 튜플로 나온다.
np.linalg.inv(A) @ b
-
엄밀하게는 아래의 행렬곱이 가능하다.
- (2,2) @ (2,1) => (2,1)
- (1,2) @ (2,2) => (1,2)
A = np.array([1,2,3,4]).reshape(2,2)
b = np.array([1,2]).reshape(2,1)
A@b
A.shape, b.shape, (A@b).shape
A = np.array([1,2,3,4]).reshape(2,2)
b = np.array([1,2]).reshape(1,2)
b@A
A.shape, b.shape, (b@A).shape
-
당연히 아래는 성립안한다.
A = np.array([1,2,3,4]).reshape(2,2)
b = np.array([1,2]).reshape(2,1)
b@A
A = np.array([1,2,3,4]).reshape(2,2)
b = np.array([1,2]).reshape(1,2)
A@b
-
아래는 어떨까? 계산가능할까? $\to$ 모두 계산가능!
- (2,) @ (2,2) = (2,)
- (2,2) @ (2,) = (2,)
A = np.array([1,2,3,4]).reshape(2,2)
b = np.array([1,2])
A@b
A.shape, b.shape, (A@b).shape
- b를 마치 (2,1)처럼 해석하여 행렬곱하고 결과는 다시 (2,) 로 만든것 같다.
b@A
A.shape, b.shape, (b@A).shape
- 이때는 $b$를 마치 (1,2)처럼 해석하여 행렬곱하고 결과는 다시 (2,)로 만든것 같다.
-
아래는 어떠할까?
b1 = np.array([1,2,3,4])
b2 = np.array([1,2,3,4])
b1@b2
b1.shape, b2.shape, (b1@b2).shape
- (1,4) @ (4,1) = (1,1) 로 생각
-
즉 위는 아래와 같이 해석하고 행렬곱한것과 결과가 같다.
b1 = np.array([1,2,3,4]).reshape(1,4)
b2 = np.array([1,2,3,4]).reshape(4,1)
b1@b2
b1.shape, b2.shape, (b1@b2).shape
-
때로는 (4,1) @ (1,4)와 같은 계산결과를 얻고 싶을 수 있는데 이때는 차원을 명시해야함
b1 = np.array([1,2,3,4]).reshape(4,1)
b2 = np.array([1,2,3,4]).reshape(1,4)
b1@b2
-
넘파이배열의 차원은 .shape 으로 확인가능
-
아래는 모두 미묘하게 다르다.
a=np.array(3.14) # 스칼라, 0d array
a, a.shape
a=np.array([3.14]) # 벡터, 1d array
a, a.shape
a=np.array([[3.14]]) # 매트릭스, 2d array
a, a.shape
a=np.array([[[3.14]]]) # 텐서, 3d array
a, a.shape
-
기본예제
a=np.array([1,2])
b=-a
np.concatenate([a,b])
-
응용
a=np.array([1,2])
b=-a
c=np.array([3,4,5])
np.concatenate([a,b,c])
- 여기까진 딱히 칸캐터네이트의 메리트가 없어보임
- 리스트였다면 a+b+c 하면 되는 기능이니까?
-
2d array에 적용해보자.
a=np.arange(4).reshape(2,2)
b=-a
np.concatenate([a,b])
-
옆으로 붙일려면?
np.concatenate([a,b],axis=1)
-
위의 코드에서 axis=1 이 뭐지? axis=0,2 등을 치면 결과가 어떻게 될까?
np.concatenate([a,b],axis=0)
- 이건 그냥 np.concatenate([a,b])와 같다.
- np.concatenate([a,b])는 np.concatenate([a,b],axis=0)의 생략버전이군?
np.concatenate([a,b],axis=2)
- 이런건 없다.
-
axis의 의미가 뭔지 궁금함. 좀 더 예제를 살펴보자.
a=np.array(range(2*3*4)).reshape(2,3,4)
a
b=-a
b
np.concatenate([a,b],axis=0)
np.concatenate([a,b],axis=1)
np.concatenate([a,b],axis=2)
- 이번에는 axis=2까지 된다?
np.concatenate([a,b],axis=3)
- axis=3까지는 안된다?
-
뭔가 나름의 방식으로 합쳐지는데 원리가 뭘까?
(분석1) np.concatenate([a,b],axis=0)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
a.shape, b.shape, np.concatenate([a,b],axis=0).shape
- 첫번째차원이 바뀌었다 => 첫번째 축이 바뀌었다 => axis=0 (파이썬은 0부터 시작하니까!)
(분석2) np.concatenate([a,b],axis=1)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
a.shape, b.shape, np.concatenate([a,b],axis=1).shape
- 두번째차원이 바뀌었다 => 두번째 축이 바뀌었다 => axis=1
(분석3) np.concatenate([a,b],axis=2)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
a.shape, b.shape, np.concatenate([a,b],axis=2).shape
- 세번째차원이 바뀌었다 => 세번째 축이 바뀌었다 => axis=2
(분석4) np.concatenate([a,b],axis=3)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
a.shape, b.shape, np.concatenate([a,b],axis=3).shape
- 네번째차원이 없다 => 네번째 축이 없다 => axis=3으로 하면 에러가 난다.
(보너스1)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
np.concatenate([a,b],axis=-1)
a.shape, b.shape, np.concatenate([a,b],axis=-1).shape
- 마지막 차원이 바뀌었다 => 마지막 축이 바뀌었다 => axis = -1
(보너스2)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
np.concatenate([a,b],axis=-2)
a.shape, b.shape, np.concatenate([a,b],axis=-2).shape
- 마지막에서 2번째 차원이 바뀌었다 => 마지막에서 2번째 축이 바뀌었다 => axis = -2
(보너스3)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
np.concatenate([a,b],axis=-3)
a.shape, b.shape, np.concatenate([a,b],axis=-3).shape
- 마지막에서 3번째 차원이 바뀌었다 => 마지막에서 3번째 축이 바뀌었다 => axis = -3
(보너스3)
a=np.array(range(2*3*4)).reshape(2,3,4)
b=-a
np.concatenate([a,b],axis=-4)
- 마지막에서 4번째 차원은 없다 => 마지막에서 4번째 축이 없다 => axis = -4는 에러가 난다.
-
0차원은 축이 없으므로 concatenate를 쓸 수 없다.
a= np.array(1)
b= np.array(-1)
a.shape, b.shape
np.concatenate([a,b])
-
꼭 a,b가 같은 차원일 필요는 없다.
a=np.array(range(4)).reshape(2,2)
b=np.array(range(2)).reshape(2,1)
np.concatenate([a,b],axis=1)
a.shape, b.shape, np.concatenate([a,b],axis=1).shape
-
혹시 아래가 가능할까?
- (3,) 결합 (3,) => (3,2)
a=np.array([1,2,3])
b=-a
a,b
np.concatenate([a,b],axis=1)
- 불가능
-
아래와 같이 하면 해결가능
a=np.array([1,2,3]).reshape(3,1)
b=-a
a,b
np.concatenate([a,b],axis=1)
- 분석: (3) (3) => (3,1) (3,1) => (3,1) concat (3,1)
-
위의 과정을 줄여서 아래와 같이 할 수 있다.
a=np.array([1,2,3])
b=-a
np.stack([a,b],axis=1)
-
아래도 가능
np.stack([a,b],axis=0)
-
분석해보고 외우자
(분석1)
a=np.array([1,2,3])
b=-a
a.shape, b.shape, np.stack([a,b],axis=0).shape
- (3) (3) => 첫 위치에 축을 추가 (axis=0) => (1,3) (1,3) => (2,3)
(분석2)
a=np.array([1,2,3])
b=-a
a.shape, b.shape, np.stack([a,b],axis=1).shape
- (3) (3) => 두 위치에 축을 추가 (axis=1) => (3,1) (3,1) => (3,2)
-
고차원예제
a=np.arange(3*4*5).reshape(3,4,5)
b=-a
a.shape, b.shape
np.stack([a,b],axis=0).shape # (3,4,5) => (1,3,4,5) // 첫 위치에 축이 추가되고 스택
np.stack([a,b],axis=1).shape # (3,4,5) => (3,1,4,5) // 두번째 위치에 축이 추가되고 스택
np.stack([a,b],axis=2).shape # (3,4,5) => (3,4,1,5) // 세번째 위치에 축이 추가되고 스택
np.stack([a,b],axis=3).shape # (3,4,5) => (3,4,5,1) // 네번째 위치에 축이 추가되고 스택
np.stack([a,b],axis=-1).shape # axis=-1 <=> axis=3
np.stack([a,b],axis=-2).shape # axis=-2 <=> axis=2
np.concatenate 는 축의 총 갯수를 유지하면서 결합, np.stack은 축의 갯수를 하나 증가시키면서 결합
-
1차원
a = np.array([1,2,3])
a
a.sum()
a.sum(axis=0)
-
2차원
a=np.array(range(6)).reshape(2,3)
a
a.sum() # 전체합
a.sum(axis=0)
a.sum(axis=1)
-
2차원 결과 분석
a.shape, a.sum(axis=0).shape
- 첫번째 축이 삭제됨 => axis=0
a.shape, a.sum(axis=1).shape
- 두번째 축이 삭제됨 => axis=1
-
연습
a=np.array(range(10)).reshape(5,2)
a
(문제1) 1열의 합, 2열의 합을 계산하고 싶다면?
(풀이) 차원이 (5,2) => (2,) 로 나와야 한다. (그럼 첫번째 축이 삭제되어야 하네?)
a.sum(axis=0)
(문제2) 1행의 합, 2행의 합, ... , 5행의 합을 계산하고 싶다면?
(풀이) 차원이 (5,2) => (5,)로 나와야 한다. (그럼 두번째 축이 삭제되어야 하네?)
a.sum(axis=1)
(문제3) a의 모든원소의 합을 계산하고 싶다면?
(풀이) 차원이 (5,2) => () 로 나와야 한다. (첫번째축, 두번째축이 모두 삭제되어야 하네?)
a.sum(axis=(0,1))
a.sum() # 즉 a.sum(axis=(0,1))이 디폴트값임
-
모두 sum이랑 유사한 논리
a=np.array(range(10)).reshape(5,2)
a
a.mean(axis=0), a.std(axis=0), a.max(axis=0), a.min(axis=0), a.prod(axis=0)
a.mean(axis=1), a.std(axis=1), a.max(axis=1), a.min(axis=1), a.prod(axis=1)
-
참고로 std는 분포를 n으로 나눈다.
a=np.array([1,2,3,4])
a.std()
np.sqrt(sum((a-a.mean())**2)/4)
-
분모를 n-1로 나눌려면?
a=np.array([1,2,3,4])
a.std(ddof=1)
np.sqrt(sum((a-a.mean())**2)/3)
-
1차원
a= np.array([1,-2,3,10,4])
a
a.argmax() # 가장 큰 값이 위치한 원소의 인덱스를 리턴
a.argmin() # 가장 작은 값이 위치한 원소의 인덱스를 리턴
-
2차원
np.random.seed(43052)
a=np.random.randn(4*5).reshape(4,5)
a
a.argmin(), a.min()
a.argmax(), a.max()
a.argmin(axis=0), a.argmin(axis=1)
a.argmax(axis=0), a.argmax(axis=1)
-
1차원
a=np.array([1,2,3,4])
a
a.cumsum()
a.cumprod()
-
2차원
a=np.array(range(3*4)).reshape(3,4)
a
a.cumsum(axis=0), a.cumsum(axis=1)
a.cumprod(axis=0), a.cumprod(axis=1)
-
1차차분
a=np.array([1,2,4,6,7])
a
np.diff(a)
-
2차차분
np.diff(np.diff(a))
-
prepend, append
a=np.array([1,2,4,6,7])
a
np.diff(a,prepend=100)
#np.diff(np.array([100]+a.tolist()) )
- [1,2,4,6,7] -> [100,1,2,3,4,6] -> np.diff
np.diff(a,append=100)
#np.diff(np.array(a.tolist()+[100]) )
(예제) a=[1,2,4,6,7]의 앞에 1을 추가하여 차분하라.
np.diff(a,prepend=a[0])
#np.diff(a,prepend=1)
(예제) a=[1,2,4,6,7]의 뒤에 7을 추가하여 차분하라.
np.diff(a,append=a[-1])
#np.diff(a,append=7)
-
2차원 array의 차분
a=np.arange(24).reshape(4,6)
a
np.diff(a,axis=0)
np.diff(a,axis=1)
(숙제)
a=np.arange(24).reshape(4,6)
a
에서 axis=1 옵션으로 np.diff를 적용하여 (4,5) array를 만들고 왼쪽열에 1이 포함된 column을 추가하여 최종 결과가 아래와 같이 되도록 하라.
array([[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1]])