본문 바로가기

Programming Language/Python_library

파이썬_응용단계 ep.2 Numpy 튜토리얼

320x100
서론

 

추후 Pandas를 원활하게 쓰기 위해, numpy는 중간단계로서 거쳐갑니다.

 

왜 numpy가 필요하다고 했죠?

 

 

강력한 행렬 조작, 연산을 시작할 수 있기 때문에 필요하다고 했습니다.

 

즉, 목적지가 아닙니다.

위로 올라가기 위한 단계이기 때문에 기능과 계산식들을 눈에 익히고

올라가는 것만으로도 충 분🔥 합니다.

 

 

들어가기 전에

 

100원짜리 동전을 넣고 돌릴 수 있는 오락실 게임이

유행한 적이 있습니다.

 

2000년대~ 2010년대쯤, 초등학교 앞에서는 분식집과 함께

조이스틱으로 되어있는 게임기들이 우리들의 시선을 강타했죠.

심지어 100원짜리 콩알 캔디가 게임기 안에 들어있어서, 

게임을 하다가 허기질 때는 먹을것도 충족시켜주었죠?

 

 

우리의 시각적 쾌락과 미각적 쾌락을 동시에 충족시켜주는 멋진

도구임에는 틀림 없습니다.

 

스노우 브라더스, 철권, 스트리트파이터, 메탈슬러그 등등

여러분이 가장 즐겨 했던 게임은 무엇이었나요?

 

필자는 메탈슬러그를 주로 했었는데요.

가장 히트를 친 시리즈, 메탈슬러그3를 시작하면 가장 먼저 꽃게(가재)들이

우리앞에 등장합니다.

 

 

한 줄을 서서 올곧고 정직하게, 앞으로만 움직입니다.

꽃게들이 하늘을 날아다니진 않죠? 앞뒤로만 움직입니다.

 

자 꽃게 친구들을 기억해주세요!

 

하지만  바다나 하늘로 접근하는 순간 상황이 달라집니다.

 

 

이럴수가, 해파리들은 좌우,앞뒤로 모두 움직입니다

 

바다속에서 따로 고정되어 있지 않고, 왼쪽 오른쪽 위 아래

쉼없이 움직입니다.

 

이 해파리 친구도 기억해주세요.

 

 

3차원의 형태는 메탈슬러그에서 구현되기 어려울 것 같습니다.

마인크래프트를 가져와 보았습니다.

 

이제 적들은 좌우,앞뒤, 위아래로도 움직입니다.

좋습니다. 이제는 귀여운 친구를 기억해주세요.


자 여기서 꽃게는 배열을 통해, 해파리는 행렬을 통해 움직임을 나타냅니다.

벌 은 3차원 배열을 통해 움직입니다.

 

배열 이란 무엇일까요?

다르게 표현해보겠습니다.

 

 

vector(배열) : 1차원 데이터 구조

 

 

Matrix(행렬) : 2차원 데이터 구조 

 

 

자 여기서 한가지 축(axis)을 더하면 3차원 데이터가 표현됩니다.

 

 

 

tensor(3D tensor) : 3차원 데이터 구조. 쉽게 말하자면 텐서입니다.

 

행렬에서 한가지 축을 높인것입니다.

 

우리는 Numpy를 통해 3차원 행렬까지 배우며, 여기에 있는 데이터를

자유롭게 다루게 됩니다. 

 

필자는 꽃게 친구를 1차원 배열, 해파리 친구를 2차원 배열, 벌 친구를 3차원 배열

로 통일해서 부르겠습니다.

 

준비되셨죠?

 


 

1. 1차원 배열

 

 

앞서 봤듯이, 꽃게가 걸어온다고 생각해보면 어떨까요?

2의 자리에는 캐릭터가, 3의 자리에는 꽃게가 있네요.

 

array(1,2,3,4)

 

축이 한 개인 리스트나 튜플처럼 1차 배열입니다

 

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

 

좋습니다. 다른 형태의 배열을 확인해보겠습니다.

dtype 이 U ??가 반환된다는 것은 이것이 유니코드 문자열

이라는 뜻입니다.

 

np.array([1,2,'3'])
array(['1', '2', '3'], dtype='<U21')

 

for문을 써서 만들어보겠습니다.

 

arr = np.array([i for i in range(10)])
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

 

type(arr) # ndarray → numpy를 통해서 생성된 배열.
numpy.ndarray

 

타입은 ndarrady로 만들었기 때문에, numpy.ndarray죠

배열을 출력해보겠습니다.

 

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

 

ndarray는 리스트와 동일해보이지만 차이가 많습니다.

 

리스트(list)

각각의 원소가 다른 자료형이 될 수 있습니다

 

 

배열(array)

연속적인 메모리 배치를 가지기 때문에, 모든 원소가 같은 자료형이어야 합니다.

원소에 대한 접근과 반복문 실행이 빨라집니다

 

배열을 써야만 하는 이유, 여기에 있습니다.

 

cpu의 동작시간을 체크하는 메서드 %time을 쓰면확인해 볼 수 있습니다.

 

예시문

 

%%time
data = [0,1,2,3,4,5,6,7,8,9]
x = np.array(data)
x

 

벡터화 연산

 

ndarray에서는 각 원소들을 하나하나 일괄적으로 연산하는 기능이 있습니다.

 

Vector (배열) + -화 (그것으로 만들어서) + 계산합니다.

 

이를 벡터화 연산이라고 합니다.

 

먼저 배열을 하나 호출하겠습니다.

 

data = [0,1,2,3,4,5,6,7,8,9]
x = np.array(data)
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

여기에 갖가지 연산을 해보면 알 수 있습니다.

 

x / 2, x + 2, x ^ 2, x ** 2
(array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5]),
 array([ 2,  3,  4,  5,  6,  7,  8,  9, 10, 11]),
 array([ 2,  3,  0,  1,  6,  7,  4,  5, 10, 11]),
 array([ 0,  1,  4,  9, 16, 25, 36, 49, 64, 81]))

 

자 이 방법이 왜 필요할까요?

 

a = [1,2,3]
a
a+1
TypeError: can only concatenate list (not "int") to list

 

이렇게 리스트만 단순히 연산을 진행하려고 하면,

오류가 나기 때문입니다!

 

리스트에 하나하나 더하는 기능은 없습니다.

만약 더한다면 리스트와 리스트 사이의 연결만이 가능합니다.

 

이러한 벡터화 연산은 사칙연산 뿐만아니라, 비교 연산과

논리 연산을 포함한 모든 종류의 수학 연산에 대해서도 적용됩니다.

 

두 배열을 만들어보겠습니다.

 

a = np.array([1,2,3,4])
b = np.array([10,20,30,40])
a, b
(array([1, 2, 3, 4]), array([10, 20, 30, 40]))

 

산술 연산

 

2 * (a + b) # 산술 연산
array([22, 44, 66, 88])

 

비교 연산

 

a == 2, b> 10 # 비교 연산
(array([False,  True, False, False]), array([False,  True,  True,  True]))

 

논리 연산 and

 

(a == 2) & (b > 10) # 논리 연산 and

 

 
(같은 위치의 원소가) 둘다 True인 경우

 

array([False,  True, False, False])

 

논리 연산 or

 

(a == 2) | (b >10) # 논리 연산 or

 

(같은 위치의 원소가) 둘 중 하나가 True인 경우

 

array([False,  True,  True,  True])

 

1차원 배열에서의 벡터화연산은 여기까지 입니다.


 

 

2. 2차원 배열

 

이제 해파리 친구를 떠올려 보겠습니다.

해파리들은 위, 아래로도 움직였죠?

 

 

이렇듯 2차원 배열은 열 이 추가됩니다. 

세로선이 추가된 것이죠!

 

ndarray에 대해서 잠깐 보자면,

 

n개의 + Dimension + array

 

이렇게 n차 차원의 배열을 생성할 수 있습니다.

엑셀의 스프레드 시트와 같은 기능을 지원하는 것입니다.

다만 형태는 약간 다릅니다.

 

list는 1차원 배열, list of list는 2차원 배열이 됩니다.

 

예시를 봐볼까요?

 

m = np.array([
    [0, 1, 2],
    [3, 4, 5]
])   # 2 x 3 행렬/ m x n : m 행의 갯수 , n : 열의 갯수
m
array([[0, 1, 2],
       [3, 4, 5]])

 

2차원 배열에서의 행의 갯수, 열의 갯수는

어떻게 알 수 있을까요?

 

# 행의 갯수, 열의 갯수
print(len(m)) # 행의 갯수 - 가장 겉에 있는 리스트의 원소 갯수
print(len(m[0])) # 열의 갯수 - 가장 안에 있는 리스트의 원소 갯수
2
3

 

len(length) 즉 길이를 세는 것으로 갯수를 잡아냅니다.

 


 

3. 3차원 배열

 

이제 축이 하나 더 늘어났습니다.

3차원 배열은 (깊이 x 행 x 열) 과 같이 표현됩니다.

 

하나 더 제작합니다.

t = np.array([[[1,2,3,4],
               [5,6,7,8],
               [9,10,11,12]],
              [[11,12,13,14],
               [15,16,17,18],
               [19,20,21,22]]])
t  # 2 x 3 x 4 배열
array([[[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]],

       [[11, 12, 13, 14],
        [15, 16, 17, 18],
        [19, 20, 21, 22]]])


깊이와 행, 열도 알아볼까요?

# 깊이, 행, 열
len(t), len(t[0]), len(t[0] [0])
(2, 3, 4)

 

좋습니다.

이번에는 속성을 사용해서, 1~3차원 배열의 차원 수와

크기에 대해서 알아보겠습니다

 

배열의 차원과 크기 알아내기

 

구조

 

ndim, shape 속성 사용

 

ndim : 배열의 차원
shape : 배열의 크기 (행 x 열)

 

1차원을 표현

a라는 ndarray는 방금 만들었죠?

 

a.ndim, a.shape   # 1차원
(1, (4,))

 

2차원을 표현

 

m.ndim, m.shape   # 2차원
(2, (2, 3))

 

3차원을 표현

 

t.ndim, t.shape   # 3차원
(3, (2, 3, 4))

 

3차원 배열은 깊이, 행, 열까지 표현됩니다

배열의 복사

 

배열의 복사에는 얕은 복사와 깊은 복사가 있습니다

 

배열을 복사하면 연결관계만 복사해주는 복사와, 아예 새로 만들어서

연결관계를 끊어주는 형태의 복사가 있습니다.

 

전자는 얕은 복사, 후자는 깊은 복사라고 합니다.

 

1, '', 0.1 # 원시자료형
# 변수 - 연결관계 -> '객체'를 복사한다
a = [1, 2, 3]
a
[1, 2, 3]

 

b = a # a라고 하는 값이 아니라 a 변수에 지정된 주소값
b
[1, 2, 3]

 

자 a와 b리스트가 있습니다.

 

b가 a의 영향을 받을 때 이를 얕은 복사라고 합니다.

 

a.append(4)
b
[1, 2, 3, 4]

 

a
[1, 2, 3, 4]

 

이를 보게되면, a에만 4을 추가해도, b에도 4가 추가되는 것을 볼 수 있습니다.

 

깊은 복사는 서로가 독립적으로 가는것입니다.

 

# 깊은 복사 : 아예 새로 만들어줘서 연결 관계 끊기
c = ['a','b','c']
c
['a', 'b', 'c']

 

d= " ".join(c).split()
d
['a', 'b', 'c']

 

자 c와 d를 생성했습니다.

c에 join()과 split() 메서드를 통해 복사해줄 수 있습니다.

 

c에 값 하나만 추가해보겠습니다.

 

c.append('d')
print(c)
['a', 'b', 'c', 'd']

 

c에는 추가됐죠?

 

print(d)
['a', 'b', 'c']

 

허나, d에는 추가되지 않은 것을 알 수 있습니다.

 

np.array로 보겠습니다.

 

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

 

arr2 = arr
arr2
array([1, 2, 3, 4])

 

arr과 arr2는 얕은복사죠?

 

arr[0] = 10
arr2
array([10,  2,  3,  4])

 

arr3를 보겠습니다.

 

 속성은 () NO, 메소드 (함수) () YES

arr3 = arr.copy()
arr3
array([10,  2,  3,  4])

 

이 경우, arr과 arr3 는 깊은 복사입니다.

왜 일까요?

 

arr[0] = 100
arr3
array([10,  2,  3,  4])

 

바로 데이터가 따로 놀기 때문이죠.

 

배열의 데이터 소유 여부

 

x와 y를 보세요.

 

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

x = arr.copy()
y = arr.view()

print(x.base)
print(y.base)
None
[1 2 3 4 5]

 

copy와 view를 통해 깊은 복사 vs 얕은 복사를 

시작할 수 있습니다.

 


 

자 이렇게 해서, Numpy의 기본적인 개념.

높아지는 차원에 따른 배열과 차원, 크기 알아보기, 

벡터화 연산과 배열의 복사까지 알아보았습니다.

 

다음 이시간에는 각 배열의 인덱싱과 슬라이싱, 검색, 

생성, 변형까지 보겠습니다🚗🚗

728x90