Python_numpy_02(인덱싱과 슬라이싱을 이용한 배열의 원소 조회 및 변경)
인덱싱과 슬라이싱을 이용한 배열의 원소 조회 및 변경
배열 인덱싱(Indexing)
- index
- 배열내의 원소의 식별번호
- 0부터 시작 - indexing – index를 이용해 원소 조회
- [] 표기법 사용 - 구문
- ndarray[index]
- 양수는 지정한 index의 값을 조회한다.
- 음수는 뒤부터 조회한다.
- 마지막 index가 -1
- 2차원배열의 경우
- arr[0축 index, 1축 index]
- 파이썬 리스트와 차이점
- N차원 배열의 경우
- arr[0축 index, 1축 index, ..., n축 index] - 팬시(fancy) 인덱싱
- 여러개의 원소를 한번에 조회할 경우 리스트에 담아 전달한다.
- 다차원 배열의 경우 각 축별로 list로 지정
- arr[[1,2,3,4,5]]
- 1차원 배열(vector): 1,2,3,4,5 번 index의 원소들 한번에 조회
- arr[[0,3], [1,4]]
- [0,3] - 1번축 index list, [1,4] - 2번축 index list
- 2차원 배열(matrix): [0,1], [3,4] 의 원소들 조회
배열 a 생성
import numpy as np
a = np.arange(30)
a
a[0]
a[-1]
a[[1, 5, -1, -3]]
값 변경
# 변경
a[0] = 100
a
2차원 배열 생성
a2 = np.array([
[1, 2, 3, 4, 5]
,[10, 20, 30, 40, 50]
,[100, 200, 300, 400, 500]
])
a2.shape
한 줄씩 실행 후 결과 확인
a2[0] # 0 axis의 index 지정 -> 1번 축 다가져온다
a2[[0,2]] # 0 axis
a2[0, 1] # 0 axis : index 0, 1 axis : index 1
a2[:, -1] # a2[, -1] 앞의 축의 index는 생략하지 못한다. 앞의 축의 모든 값을 다 가져와라 -> a2[:, -1]
a2[[0, 2], 2]
a2[[0, 1, 2], [0, 2, 4]]
슬라이싱
- 배열의 원소들을 범위로 조회한다.
- ndarry[start : stop : step ]
- start : 시작 인덱스. 기본값 0
- stop : 끝 index. stop은 포함하지 않는다. 기본값 마지막 index
- step : 증감 간격. 기본값 1
다차원 배열 슬라이싱
- 각 축에 slicing 문법 적용
- 2차원의 경우
- arr [0축 slicing, 1축 slicing]
- arr[:3, :]
- , 로 축을 구분한 다중 슬라이싱 사용 - 다차원의 경우
- arr[0축 slicing, 1축 slicing, ..., n축 slicing] - slicing과 indexing 문법은 같이 쓸 수 있다.
슬라이싱은 원본에 대한 View
- slicing한 결과는 새로운 배열을 생성하는 것이 아니라 기존 배열을 참조한다.
- slicing한 배열의 원소를 변경하면 원본 배열의 것도 바뀐다.
- 배열.copy()
- 배열을 복사한 새로운 배열 생성
- 복사후 처리하면 원본이 바뀌지 않는다.
a2[:2] # 0축 : 0 ~ 2-1, 1번 축 다 가져온다.
a2[0, 1:3] # 0축 : 0, 1측 : 1 ~ 3-1
원본 배열 변경되는 경우
b = a[:10]
b[-1] = 90000
b
a # 참조 배열 b의 값 변경이 원본 배열 a의 값도 함께 변경시킨다.
원본 배열이 변경되지 않는 경우 -> deep copy
b2 = a[:10].copy() # copy() -> deep copy, 복사본을 만든다.
b2[-1] = 9
b2 # deep copy -> 참조 배열의 값 변경이 원본 배열의 값을 변경시키지 않는다.
a
boolean indexing
- Index 연산자에 같은 형태(shape)의 Boolean 배열을 넣으면 True인 index의 값만 조회 (False가 있는 index는 조회하지 않는다.)
- ndarray내의 원소 중에서 원하는 조건의 값들만 조회할 때 사용
- ndarray는 element-wise 연산을 지원한다. 이를 이용해 boolean indexing으로 원하는 조건의 값들을 조회할 수 있다. - boolean indexing을 masking이라고도 한다.
넘파이 비교연산자
- 파이썬의 and, or, not은 사용할 수 없다.
- &: and연산
- |: or 연산
- ~: not 연산
- 피연산자는 ( )로 묶어야 한다.
element-wise(원소별) 연산
a = np.array([10, 5, -10, 2, 7])
# element-wise(원소별) 연산
a + 10
a > 0 # 원소별로 비교
# pandas와 같이 python의 논리연산자(and, or, not)는 사용불가.
(a > 0) & (a < 5)
# boolean indexing
a[(a > 0) & (a < 5)]
boolean indexing
arr = np.random.randint(-100, 100, (3, 10))
arr.shape
# 양수만 조회
arr[arr >= 0] # boolean indexing 의 결과: 1차원 배열
arr[(arr>=-10) & (arr<=10)]
np.where()
- True의 index 조회
- np.where(boolean 배열) - True인 index를 반환
- 반환타입: Tuple . True인 index들을 담은 ndarray를 축별로 Tuple에 묶어서 반환한다.
- boolean연산과 같이사용하여 배열내에 특정 조건을 만족하는 값들을 index(위치)를 조회할 때 사용한다. - True와 False를 다른 값으로 변환
- np.where(boolean 배열, True를 대체할 값, False를 대체할 값)
- 배열내의 True를 True를 대체할 값으로 False를 False를 대체할 값 으로 변환한다.
arr.shape
# Boolean 배열 -> True의 index를 반환.
np.where(arr > 0) # tuple -> (축별 index 배열, )
np.where(arr > 0, '양수', '음수') # True : 양수, False : 음수 변환
기타
- np.any(boolean 배열)
- 배열에 True가 하나라도 있으면 True 반환
- 배열내에 특정조건을 만족하는 값이 하나 이상 있는지 확인할 때 사용 - np.all(boolean 배열)
- 배열의 모든 원소가 True이면 True 반환
- 배열내의 모든 원소가 특정 조건을 만족하는지 확인 할 때 사용
np.any
np.any([True, False, False]) # True가 하나 이상이면 True, 모두 False이면 False
np.any([False, False])
np.all
np.all([True, False, False]) # 모두 True일 때만 True, 나머지 경우에는 False
np.all([True, True])
np.any & np.all 활용
# arr이 모두 양수인지?
np.all(arr>0)
# arr의 원소 중 양수가 있는지?
np.any(arr>0)
상황별 활용
- 특정 조건이 True인 값들을 조회 -> boolean indexing
- 특정 조건이 True인 값들의 index -> np.where
- 특정 조건의 값이 하나라도 있는지 -> np.any
- 모든 값들이 특정 조건을 만족하는지(True) -> np.all
배열의 형태(shape) 변경
- 배열의 원소의 개수를 유지하는 상태에서 shape을 변경할 수있다.
- 예) (16, ) -> (4,4) -> (2,2,4) -> (2,2,2,2), -> (4,4,1) -> (1, 16)
reshape()을 이용한 차원 변경
- numpy.reshape(a, newshape) 또는 ndarray.reshape(newshape)
- a: 형태를 변경할 배열
- newshape : 변경할 형태 설정.
- 원소의 개수를 유지하는 shape으로만 변환 가능하다.
- 각 axis(축)의 size를 지정할 때 하나의 축의 size를 -1로 줄 수있다. 그러면 알아서 축 size를 설정해 준다. (전체 size / 지정한 axis들 size의 곱)
- 둘다 원본을 바꾸지 않고 reshape한 새로운 배열을 만들어 반환한다.
numpy import, x 생성
import numpy as np
x = np.arange(20)
print(x.shape)
x
numpy.reshape(a, newshape) 형태
r1 = np.reshape(x # shape변환할 대상
,(4,5) # 변경할 shape -> size(원소개수)가 바뀌면 안됨
)
print(r1.shape)
r1
ndarray.reshape(newshape) 형태 (메소드 사용)
r2 = x.reshape(4, 5) # 메소드 사용 -> 가변인자를 이용해서 변환할 shape을 지정
print(r2.shape)
r2
축 자동 지정
# 변경할 축 중에 하나는 -1로 지정할 수 있다.
# ex) 원소개수 20개, 하나의 축의 size를 4로 지정했다면 다른 축의 size는 자동으로 5가 되기에 편하게 -1로 지정해도 5가 되어 변환된다.
x.reshape(4, -1)
x.reshape(2, 2, -1)
# (20, ) -> 2차원 (1, 20) # 차원만 늘리는 형태, size가 1인 축(axis) : dummy axis
x.reshape(1, -1)
x.reshape(-1, 1) # (20, 1)
배열의 원소의 개수를 유지하는 상태에서 shape을 변경할 수있다
x.reshape(2, 8) # 20 -> 16 (개수가 바뀜) -> 오류 발생
사이즈가 바뀌지 않는 한 차원은 계속 늘릴 수 있다.
r3 = x.reshape(1, 2, 2, 5, 1, 1)
print(r3.shape)
r3
r3.reshape(-1) # r3를 1차원
r3.reshape(2,10)
ndarray.flatten() : 다차원 ndarray를 1차원으로
r3.flatten() # 다차원 -> 1차원
차원 늘리기(확장)
- Dummy axis(축)을 늘린다.
- Dummy axis: size가 1인 axis 를 말한다. - reshape() 을 이용해 늘릴 수 있다.
- indexer와 np.newaxis 변수를 이용해 늘린다.
- ndarray[..., np.newaxis] 또는 ndarray[np.newaxis, ...]
- 맨앞 또는 맨 마지막에 dummy axis(축)을 늘릴때 사용한다.
- 축을 늘리려는 위치에 np.newaxis를 지정하고 ... 으로 원본 배열의 shape을 유지함을 알려준다.
dummy axis
r1 = x.reshape(1, -1) # 앞에 dummy axis 추가
r1.shape
r2 = r1.reshape(-1, 1)
r2.shape
a = np.arange(20).reshape(4, 5)
a.shape
# (4, 5) -> (1, 4, 5)
a.reshape(1, a.shape[0], -1)
a.shape
b = a[np.newaxis, ...]
b.shape
a.shape
c = a[..., np.newaxis, np.newaxis]
c.shape
원하는 축을 늘리기
# dummy axis를 추가하는 함수. np.expand_dims(대상대열, 추가할 축)
a.shape
d = np.expand_dims(a, axis=1)
d.shape
차원 줄이기(축소)
- numpy.squeeze(배열, axis=None), 배열객체.squeeze(axis=None)
- 배열에서 지정한 축(axis)을 제거하여 차원(rank)를 줄인다.
- 제거하려는 축의 size는 1이어야 한다.
- 축을 지정하지 않으면 size가 1인 모든 축을 제거한다.
- (3,1,1,2) => (3,2)
numpy.squeeze() -> dummy axis 제거
b.shape
r = np.squeeze(b)
r.shape
dummy axis 여러개 제거, 축을 지정하지 않으면 모든 dummy axis가 제거된다.
c = np.arange(20).reshape(1, 4, 5, 1, 1)
c.shape
r2 = np.squeeze(c)
print(r2.shape)
축을 지정하여 dummy axis 제거
r3 = np.squeeze(c, axis = 0)
r3.shape
배열 연산
벡터화 - 벡터 연산
- 배열과 scalar 간의 연산은 원소단위로 계산한다.
- 배열간의 연산은 같은 index의 원소끼리 계산 한다.
- Element-wise(원소별) 연산 이라고도 한다.
- 배열간의 연산시 배열의 형태(shape)가 같아야 한다.
- 배열의 형태가 다른 경우 Broadcast 조건을 만족하면 연산이 가능하다.
사용할 배열 x, y, z 생성
import numpy as np
x = np.arange(10).reshape(2, 5)
y = np.arange(10,20).reshape(2, 5)
z = np.arange(8).reshape(2, 4)
배열과 스칼라간의 연산
# 배열과 스칼라간 연산
x + 10
x > 5
# x >= 2 and x <= 5
(x >= 2) & (x <= 5)
x ** 2
배열 간의 연산
# 배열 간의 연산 -> 같은 index값 끼리 계산
x - y
x > y
shape이 다를 경우 연산 -> 오류 발생
x - z # shape이 다르면 계산이 되지 않는다.
내적 (Dot product) 연산
- @ 연산자 또는 numpy.dot(벡터/행렬, 벡터/행렬) 함수 사용
- 같은 index의 원소끼리 곱한뒤 결과를 모두 더한다.
- 벡터간의 내적의 결과는 스칼라가 된다.
- 조건
- 두 벡터의 차원(원소의개수)가 같아야 한다.
- 앞의 벡터는 행벡터 뒤의 벡터는 열벡터 이어야 한다.
- numpy 에서는 vector 끼리 연산시 앞의 벡터는 행벡터로 뒤의 벡터는 열벡터로 인식해 처리한다.
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
# 내적 (Dot product) 연산 -> 결과 : 스칼라
r1 = x @ y
r2 = np.dot(x, y)
print(r1, r2)
행렬 곱
- 같은 index의 앞 행렬의 행과 뒤 행렬의 열간에 내적을 한다.
- 행렬과 행렬을 내적하면 그 결과는 행렬이 된다.
- 앞 행렬의 열수와 뒤 행렬의 행수가 같아야 한다.
- 내적의 결과의 형태(shape)는 앞행렬의 행수와 뒤 행렬의 열의 형태를 가진다.
- (3 x 2)와 (2 x 5) = (3 x 5)
- (1 x 5)와 (5 x 1) = (1 x 1)
A = np.arange(1, 7).reshape(2, 3)
B = np.arange(1, 7).reshape(3, 2)
print(A.shape, B.shape) # 앞 행렬의 0번 축과 뒤 행렬의 1번 축의 size가 같아야 한다.
A @ B
np.dot(A, B)
행렬 곱의 조건(앞 행렬의 열수와 뒤 행렬의 행수가 같아야 한다.)에 만족하지 못하여 오류 발생
C = np.arange(1, 7).reshape(2, 3)
A.shape, C.shape
A @ C
내적의 예
- 가중합
가격: 사과 2000, 귤 1000, 수박 10000
개수: 사과 10, 귤 20, 수박 2
총가격?
2000*10 + 1000 * 20 + 10000 * 2
price = np.array([2000, 1000, 10000])
cnt = np.array([10, 20, 2])
price * cnt
총 가격
np.sum(price * cnt)
price @ cnt
여러 사람이 과일을 구매했을 경우 각각 내야 하는 금액은?
cnts = np.array([
[10, 20, 2] # 첫 번째 사람이 산 개수
,[5, 2, 10] # 두 번째
,[7, 30, 10]# 세 번째
,[20, 10, 0]# 네 번째
])
cnts.shape
price_2 = price[..., np.newaxis] # price.shape(-1, 1)
price_2.shape
r = cnts @ price_2
r
r[0] # 첫 번째 사람이 내야하는 금액
r[2] # 세 번째 사람이 내야하는 금액
기술통계함수
- 통계 결과를 계산해 주는 함수들
- 구문
1. np.전용함수(배열)
- np.sum(x)
2. 일부는 배열.전용함수() 구문 지원
- x.sum()
- 공통 매개변수
- axis=None: 다차원 배열일 때 통계값을 계산할 axis(축)을 지정한다. None(기본값)은 flatten후 계산한다. - 배열의 원소 중 누락된 값(NaN - Not a Number) 있을 경우 연산의 결과는 NaN으로 나온다.
- 안전모드 함수
- 배열내 누락된 값(NaN)을 무시하고 계산 - https://docs.scipy.org/doc/numpy-1.15.1/reference/routines.statistics.html
사용할 array a 생성
np.random.seed(0)
a = np.random.randint(100, size = 10).astype('float32')
a
여러가지 메소드 활용
np.sum(a), a.sum()
a.min(), a.max()
a.argmin(), a.argmax() # min, max값의 index
새로운 array b 생성
# 평균
b = np.array([80, 90, 100])
b.mean()
가중치 array c 생성, 가중평균 구하기
# 가중치 부여한 평균 (가중평균)
c = np.array([3, 3, 1])
np.average(a = b, weights = c)
# 가중평균
(b @ c)/c.sum()
array a에 결측치 추가
# a에 결측치를 추가
a[1] = np.nan
a
# 결측치가 껴있어서 모두 결측치가 결과로 나온다.
a.mean(), a.sum(), a.std(), a.min(), a.max(), a.argmax()
안전모드 함수(결측치 제외한 결과값 출력)
# 결측치를 제외한 결과값이 나온다
np.nanmean(a), np.nansum(a), np.nanstd(a), np.nanmin(a), np.nanmax(a), np.nanargmax(a)
사용할 array arr 생성
arr = np.arange(20).reshape(4, 5)
arr.shape
# 2차원 -> 0차원(상수-스칼라)
arr.sum() # 다차원 배열 -> 기본 : 전체집계
# 2차원 -> 1차원
arr.sum(axis=0) # 0번 축을 기준으로 집계 -> 0번 축의 index가 다르고 나머지 축의 index는 같은 값끼리 계산.
arr.sum(axis=1) # 1번 축을 기준으로 집계 -> 1번 축의 index가 다르고 나머지 축의 index는 같은 값끼리 계산.
keepdims = True -> 배열의 차원 유지
# 2차원 -> 2차원
r = arr.sum(axis = 0
,keepdims = True # arr배열의 차원을 유지
)
print(r.shape)
r
r = arr.sum(axis = 1, keepdims = True)
print(r.shape)
r
브로드캐스팅
- 사전적의미 : 퍼트린다. 전파한다.
- 형태(shape)가 다른 배열 연산시 배열의 형태를 맞춰 연산이 가능하도록 한다.
- 모든 형태를 다 맞추는 것은 아니고 조건이 맞아야 한다. - 조건
1. 두 배열의 축의 개수가 다르면 작은 축의개수를 가진 배열의 형태(shape)의 앞쪽을 1로 채운다.
- (2, 3) + (3, ) => (2, 3) + (1, 3)
2. 두 배열의 차원 수가 같지만 각 차원의 크기가 다른 경우 어느 한 쪽에 1이 있으면 그 1이 다른 배열의 크기와 일치하도록 늘어난다.
- 1 이외의 나머지 축의 크기는 같아야 한다.
- 늘리면서 원소는 복사한다.
- (2, 3) + (1, 3) => (2, 3)+(2, 3)
브로드캐스팅에 사용할 x,y 생성
x = np.array([0, 1, 2])
y = np.ones(shape=(3,3))
print(x.shape, y.shape)
x + y
다른 array 두개 생성 후 브로드캐스팅
a = np.arange(3).reshape(3,1)
b = np.arange(3)
print(a.shape, b.shape)
a + b