본문 바로가기

😀 기초/넘파이(NumPy)

3장 넘파이 배열 프로그래밍(3.1)

3.0 넘파이 배열 프로그래밍

 

1. 3장 넘파이 배열 프로그밍
- 이 장에서는 넘파이 패키지를 사용한 배열 프로그래밍에 대해 공부할 예정.
- 배열 프로그래밍은 대량의 데이터를 빠르게 조작하기 위한 필수적 과정이다.
- 파이썬의 데이터 관련 명령은 모두 넘파이 기능을 사용하므로 데이터 분석을위해서는

  이 장에서 설명하는 모든 내용을 숙지해야한다.

 

2. 학습 목표
- 배열과 리스트의 차이점을 알고 배열을 사용하는 이유를 이해한다.
- 배열을 생성하고 다루는 방법을 익힌다.
- 넘파이를 사용하여 기술 통계를 낼 수 있다.
- 난수를 발생시키고 그 결과를 분석하는 방법을 공부한다.


3.1 넘파이 배열

 

1. 3.1 넘파이 배열
- 많은 숫자 데이터를 하나의 변수에 넣고 관리하는 리스트는 메모리 차지가 많고 속도가 느림.
- 이를 극복한게 배열(array)이다.
- 단, 리스트와 비슷하지만 두 가지 다른점이 있다.
- * 모든 원소가 같은 자료형이어야 한다.
- * 원소의 객수를 바꿀 수 없다.

- 파이썬 자체적으로 배열 자료형을 제공하지 않아서 배열을 사용할 수 있는 넘파이를 import 함.

 

2. 넘파이 패키지 임포트
- 배열을 사용하기 위해서 다음과 같이 임포트한다.

import numpy as np

3. 1차원 배열 만들기
- 넘파이 array 함수에 리스트를 널으면 ndarray 클래스 객체 즉, 배열로 변환해준다.
- 따라서 1 차원 배열을 만든는 방법은 다음과 같다.

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

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

- 리스트와 비슷해 보이지만 type 명령으로 자료형을 살펴보면 ndarray임을 알 수 있다.

type(ar)
#결과 :
numpy.ndarray

- 리스트와 동일한 구조처럼 보임.
- 그러나 배열 객체와 리스트 객체는 많은 차이

- 우선 리스트 클래스 객체는 각각의 원소가 다른 자료형이 될 수 있음
- 하지만 배열 객체는 연속적인 메모리 배치를 가지기에 모든 원소가 같은 자료형이어야함.
- 대신 원소에 대한 접근과 반복문이 실행이 빨라짐

 

4. 백터와 연산
- 배열 객체는 배열의 각 원소에 대한 반복 연산을 하나의 명령어로 처리하는 벡터화 연산을 지원함.
- 예를 들어 다음처럼 여러개의 데이터를 모두 2배 해야하는 경우 생각해보자!

data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

answer = []
for di in data:
    answer.append(2 * di)
answer

#결과 : 
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

- 하지만 백터화 연산을 사용하면 다음과 같이 for문 없이 한번에 연산으로 할 수 있다.
- 계산속도도 빠르다.

x = np.array(data)
x

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

--------------------------------
x * 2

#결과:
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])


# 참고로 일반 리스트 객체에 정수를 곱하면
# 객체의 크기가 정수배 만큼 증가한다.

L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(2 * L)

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

- 백터화 연산은 비교 연산과 논리 연산을 포함한 모든 종류의 수학 연산에 대해 적용된다.
- 선형 대수에 적용되는 백터화 연산에 대해서는 나중에 살펴보자

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

2 * a + b
#결과 : 
array([12, 24, 36])

a == 2
# 결과 :
array([False,  True, False])

b > 10
# 결과 :
array([False,  True,  True])

(a == 2) & (b > 10)
# 결과 :
array([False,  True, False])

- 2차원 배열의 행과 열의 갯수 구하기

# 행의 갯수
len(c)

# 정답 : 2

# 열의 갯수
len(c[0])

# 정답 : 3

5. 3차원 배열 만들기
- 리스트의 리스트의 리스트를 이용하면 3차원 배열도 생성 가능
크게를 나타낼 때는 가장 바깥쪽 리스트의 길이부터 가장 안쪽 리스트 길이의 순서로 표시
- 2 x 3 x 4 배열은 다음과 같이 만든다.

d = 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]]]) # 2 x 3 x 4 array

- 3차원 배열의 깊이, 행, 열은 다음과 같이 구할 수 있다.

len(d), len(d[0]), len(d[0][0])

#결과
(2, 3, 4)

6. 배열의 차원과 크기 알아내기
- 배열의 차원 및 크기를 구하는 더 간단한 방법
ndim 속성과 shape 속성을 이용
- ndim 속성 : 배열의 차원
- shape 속성 : 배열의 크기

# a = np.array([1, 2, 3])
print(a.ndim)
print(a.shape)

#결과
1
(3,)

# c = np.array([[0, 1, 2], [3, 4, 5]])
print(c.ndim)
print(c.shape)

#결과
2
(2, 3)

print(d.ndim)
print(d.shape)

#결과
3
(2, 3, 4)

7. 배열의 인덱싱
- 일차원 배열의 인덱싱은 리스트의 인덱싱과 같다

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

a[-1]
#결과
4

- 다차원 배열일 때는 다음과 같이 콤마(,)를 사용하여 접근할 수 있다.
- 콤마로 구분된 차원을 축(axis)이라고도 한다.
- 그래프 x축과 y축을 떠올리면 될 것.

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

#결과
array([[0, 1, 2],
       [3, 4, 5]])
       
a[0, 0] # 첫 번째 행의 첫 번째 열
#결과
0

a[0, 1] # 첫 번째 행의 두 번째 열
#결과
1

a[-1, -1] # 마지막 행의 마지막 열
#결과
5

8. 배열 슬라이싱
- 배열 객체로 구현한 다차원 배열의 원소 중 복수 개를 접근하려면 

  일반적인 파이썬 슬라이싱(slicing)과 comma(,)를 함께 사용하면 된다.

a = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
a
#결과
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
       
a[0, :]  # 첫번째 행 전체
#결과
array([0, 1, 2, 3])


a[:, 1]  # 두번째 열 전체
#결과
array([1, 5])

a[1, 1:]  # 두번째 행의 두번째 열부터 끝열까지
#결과
array([5, 6, 7])

a[:2, :2]
#결과
array([[0, 1],
       [4, 5]])
# 연습문제 
m = np.array([[ 0,  1,  2,  3,  4],
            [ 5,  6,  7,  8,  9],
            [10, 11, 12, 13, 14]])

# 이 행렬에서 값 7 을 인덱싱한다.
m[1, 2]

# 이 행렬에서 값 14 을 인덱싱한다.
m[2, 4]

# 이 행렬에서 배열 [6, 7] 을 슬라이싱한다.
m[1, 1:3]

# 이 행렬에서 배열 [7, 12] 을 슬라이싱한다.
m[1:, 2]

# 이 행렬에서 배열 [[3, 4], [8, 9]] 을 슬라이싱한다.
m[0, 3:5], m[1, 3:5]

#결과
(array([3, 4]), array([8, 9]))

9. 배열 인덱싱
- 넘파이 배열 객체의 또다른 강력한 기능은 팬시 인덱싱(Fancy indexing)이라고도 부르는 배열 인덱싱 방법이다.
- 인덱싱이라는 이름이 붙었지만 사실 데이터베이스의 질의(Query) 기능을 수행한다.
- 배열 인덱싱에서는 대괄호(Bracket [])안의 인덱스 정보로 숫자나 슬라이스가 아니라

  위치 정보를 나타내는 또 다른 ndarray배열을 받을 수 있다.
- 여기에서는 배열을 편의상 인덱스 배열이라고 부르겠다.
- 배열 인덱싱의 방식에는 불리언(boolean) 배열 방식과 정수 배열 방식 두가지
- 다음은 짝수 사용법

a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
idx = np.array([True, False, True, False, True,
               False, True, False, True, False])
a[idx]

#결과
array([0, 2, 4, 6, 8])

# 조건문 연산을 사용하면 간단하게 쓸 수 있다.

a % 2

#결과
array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1], dtype=int32)

a % 2 == 0

#결과
array([ True, False,  True, False,  True, False,  True, False,  True,
       False])
       
a[a % 2 == 0]

#결과
array([0, 2, 4, 6, 8])

- 정수 배열 인덱싱에서는 인덱스 배열의 원소 각각이 원래 

  ndarray 객체 원소 하나를 가리키는 인덱스 정수이여야 한다.
- 예를 들어 1차원 배열에서 홀수번째 원소만 골라는 것은 다음과 같다

a = np.array([11, 22, 33, 44, 55, 66, 77, 88, 99])
idx = np.array([0, 2, 4, 6, 8])
a[idx]

#결과
array([11, 33, 55, 77, 99])

- 이 때 배열 인덱스의 크기가 원래 배열 크기와 달라도 상관없다
- 반복해서 가리키는 경우에는 배열 인덱스가 더 커지기도 한다.

a = np.array([11, 22, 33, 44, 55 ,66, 77, 88 ,99])
idx = np.array([0, 0, 0, 2, 2, 4, 6, 6])
a[idx]

#결과
array([11, 11, 11, 33, 33, 55, 77, 77])

- 배열 인덱싱은 다차원 배열의 각 차원에 대해서도 할 수 있다.

a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
a

#결과
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
       
a[:, [True, False, False, True]]

#결과
array([[ 1,  4],
       [ 5,  8],
       [ 9, 12]])
       
# 😂 이해x
a[[2, 0, 1], :]

#결과
array([[ 9, 10, 11, 12],
       [ 1,  2,  3,  4],
       [ 5,  6,  7,  8]])
# 연습문제
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
             11, 12, 13, 14, 15, 16, 17, 18, 19, 20])

# 이 배열에서 3의 배수를 찾아라.
x[x % 3 == 0]

# 이 배열에서 4로 나누면 1이 남는 수를 찾아라.
x[x % 4 == 1]

# 이 배열에서 3으로 나누면 
# 나누어지고 4로 나누면 1이 남는 수를 찾아라.

x[(x % 3 == 0) & (x % 4 == 1)]

 

'😀 기초 > 넘파이(NumPy)' 카테고리의 다른 글

3장 난수 발생과 카운팅(3.5)  (0) 2022.01.25
3장 배열의 연산(3.3)  (0) 2022.01.25
3장 넘파이 배열의 생성과 변형(3.2)  (0) 2022.01.25
NumPy 한번에 끝내기  (0) 2022.01.18