데이터 엔지니어링 과정/python

[9일차] 배열 데이터를 효과적으로 다루는 Numpy 패키지

오리는짹짹 2023. 1. 2. 16:58
목차
1. 배열 생성하기
2. 배열의 연산
3. 배열의 인덱싱과 슬라이싱

1. 배열 생성하기

0. Numpy 호출

: Numpy를 패키지에서 불러와서 별칭을 np로 설정

import numpy as np

1. 시퀀스 데이터로부터 배열 생성

arr_obj = np.array(seq_data)

import numpy as np

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

>>> array([0, 1, 2, 3, 4, 5])

(1) 실수가 혼합된 배열 생성

data2 = [0.1, 5, 4, 12, 0.5]
a2= np.array(data2)
a2

>>> array([ 0.1,  5. ,  4. , 12. ,  0.5])

➡ 정수인 다른 숫자들도 온점(.)이 뒤에 붙으면서 실수처럼 처리됐다. 

(2) 배열의 속성 표현

a1.dtype
>>> dtype('int32')

a2.dtype
>>> dtype('float64')

(3) 데이터를 직접 넣어서 배열 객체 생성

np.array([0.5, 2, 0.01, 8])
>>> array([0.5 , 2.  , 0.01, 8.  ])

(4) 2차원 배열 생성 방법

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

2. 범위를 지정해 배열 생성

arr_obj = np.arange([start,] stop [,step])

np.arange(0, 10, 2)
>>> array([0, 2, 4, 6, 8])

np.arange(1, 10) # step 생략
>>> array([1, 2, 3, 4, 5, 6, 7, 8, 9])

np.arange(5) #start, step 생략
>>> array([0, 1, 2, 3, 4])

(1) reshape를 추가해 1차 배열을 2차 배열로 변경하기

np.arange(12).reshape(4,3) #배열의 원소 개수와 reshape(m,n)의 m*n의 개수가 같아야 한다
>>> array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 6,  7,  8],
           [ 9, 10, 11]])
           
#1부터 배열이 시작되게 하려면
np.arange(1,13).reshape(4,3)
>>> array([[ 1,  2,  3],
           [ 4,  5,  6],
           [ 7,  8,  9],
           [10, 11, 12]])
           
#1부터 1000까지 짝수만 20*25으로
np.arange(2,1001,2).reshape(20,25)

(2) ndarray.shape 배열의 형태 출력 

b1 = np.arange(12).reshape(4,3)
b1.shape

>>> (4, 3)
  • 1차원 배열인 경우
b2 = np.arange(5)
b2.shape

>>>(5, )

(3) 범위의 시작과 끝을 지정한 후, 데이터의 개수를 지정해 Numpy 배열을 생성
 arr_obj = np.linspace(start,stop [, num])

# 0~pi 까지 동일한 간격으로 20개로 나누기
# 'np.pi'는 Numpy에서 π를 입력할 때 이용

np.linspace(0,np.pi, 20)

>>> array([0.        , 0.16534698, 0.33069396, 0.49604095, 0.66138793,
           0.82673491, 0.99208189, 1.15742887, 1.32277585, 1.48812284,
           1.65346982, 1.8188168 , 1.98416378, 2.14951076, 2.31485774,
           2.48020473, 2.64555171, 2.81089869, 2.97624567, 3.14159265])
#1부터 50까지 50번
np.linspace(1,50)
>>> array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13.,
           14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26.,
           27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38., 39.,
           40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50.])
       
       
#0부터 50까지
np.linspace(0,51)
>>> array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12.,
           13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25.,
           26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38.,
           39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50.])

3. 특별한 형태의 배열

  • zeros() 모든 원소가 0인 다차원 배열을 만들어준다.
np.zeros(10)
>>> array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

np.zeros((3,4))
>>> array([[0., 0., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0., 0.]])
  • ones() 모든 원소가 1인 다차원 배열을 만들어준다.
np.ones(5)
>>> array([1., 1., 1., 1., 1.])

np.ones((3,5))
>>> array([[1., 1., 1., 1., 1.],
           [1., 1., 1., 1., 1.],
           [1., 1., 1., 1., 1.]])
  • eye() : 단위행렬(Identity matrix) 생성
np.eye(3)
>>> array([[1., 0., 0.],
           [0., 1., 0.],
           [0., 0., 1.]])

  👀단위 행렬: n*n인 정사각형 행렬에서 주 대각선이 모두 1이고 나머지가 0인 행렬

4. 배열의 데이터 타입 변환

(1) 문자열이 원소인 Numpy 배열

np.array(['1.5', '0.62', '2', '3.14', '3.141592'])
>>> array(['1.5', '0.62', '2', '3.14', '3.141592'], dtype='<U8')

(2) Numpy 데이터의 형식

(3) astype() 배열의 형 변환
num_arr = str_arr.astype(float)

str_a1 = np.array(['1.567', '0.123', '5.123', '9', '8'])
num_a1 = str_a1.astype(float)
num_a1
>>> array([1.567, 0.123, 5.123, 9.   , 8.   ])

str_a1.dtype
>>> dtype('<U5')

num_a1.dtype
>>> dtype('float64')

👀 문자열을 정수로

str_a2 = np.array(['1', '3', '5', '7', '9'])
num_a2 = str_a2.astype(int)
num_a2
>>> array([1, 3, 5, 7, 9])

str_a2.dtype
>>> dtype('<U1')

num_a2.dtype
>>> dtype('int32')

👀 실수를 정수로

num_f1 = np.array([10,21,0.549, 4.75, 5.98])
num_i1 = num_f1.astype(int)
num_i1
>>> array([10, 21,  0,  4,  5])

num_f1.dtype
>>> dtype('float64')

num_i1.dtype
>>> dtype('int32')

5. 난수 배열의 생성

  • rand.rand() 예
#2*3 형태로 출력
np.random.rand(2,3)
>>> array([[0.21528453, 0.71300004, 0.69764024],
           [0.51306462, 0.22018718, 0.62818844]])
       
#랜덤으로 하나       
np.random.rand()
>>> 0.620216720329649

#2개의 층을 가진 3*4
np.random.rand(2,3,4)
>>> array([[[0.98373438, 0.91075289, 0.50400202, 0.17318287],
            [0.79773969, 0.40807503, 0.41524508, 0.27032026],
            [0.36456861, 0.33602553, 0.00361297, 0.04439129]],

           [[0.47685719, 0.92842377, 0.61546502, 0.18723697],
            [0.00763824, 0.08685401, 0.57211559, 0.83891686],
            [0.81909688, 0.10079134, 0.81381526, 0.80090411]]])
  • rand.int() 예
np.random.randint(10, size=(3,4))
>>> array([[2, 6, 0, 8],
           [4, 0, 7, 5],
           [1, 8, 0, 5]])


np.random.randint(1, 30) # 사이즈를 지정하지 않으면 1
>>> 13

 

2. 배열의 연산

1. 기본 연산

: 배열의 형태가 같다면 연산이 가능

  • 배열 2개 생성
arr1 = np.array([10, 20, 30, 40])
arr2 = np.array([1, 2, 3, 4])
  • 두 배열의 합
arr1 + arr2
>>> array([11, 22, 33, 44])
  • 두 배열의 차
arr1 - arr2
>>> array([ 9, 18, 27, 36])
  • 배열에 상수 곱하기
arr2 * 2
>>> array([2, 4, 6, 8])
  • 배열의 거듭제곱
arr2 ** 2
>>> array([ 1,  4,  9, 16], dtype=int32)
  • 두 배열의 곱셈
arr1 * arr2
>>> array([ 10,  40,  90, 160])
  • 복합 연산
arr1 / (arr2**2)
>>> array([10.        ,  5.        ,  3.33333333,  2.5       ])
  • 비교 연산
arr1 > 20
>>> array([False, False,  True,  True])

2. 통계를 위한 연산

arr3 = np.arange(5)
arr3

>>> array([0, 1, 2, 3, 4])

(1) 합계 sum()과 평균 mean()

[arr3.sum(), arr3.mean()]
>>> [10, 2.0]

(2) 표준편차 std()와 분산 var()

  • 표준편차 = 평균을 중심으로 퍼져있는 정도
  • 분산 = 변랑이 평균으로부터 떨어져 있는 정도
[arr3.std(), arr3.var()]
>>> [1.4142135623730951, 2.0]

(3) 최솟값 min()과 최댓값 max()

[arr3.min(), arr3.max()]
>>> [0, 4]

(4) 누적 합 cumsum()과 누적 곱cumprod()

arr4 = np.arange(1,5)
arr4
>>> array([1, 2, 3, 4])

arr4.cumsum() # 누적 합
>>> array([ 1,  3,  6, 10], dtype=int32)

arr4.cumprod() # 누적 곱
array([ 1,  2,  6, 24], dtype=int32)

3. 행렬 연산

A = np.array([0, 1, 2, 3]).reshape(2,2)
A
>>> array([[0, 1],
           [2, 3]])

B = np.array([3, 2, 0, 1]).reshape(2,2)
B
>>> array([[3, 2],
           [0, 1]])

(1) 행렬곱 (matrix product) : 두개의 행렬에서 한 개의 행렬을 만들어내는 이항연산

  • A.dot(B) 
    np.dot(A, B)
A.dot(B)
>>> array([[0, 1],
           [6, 7]])


np.dot(A,B)
>>> array([[0, 1],
           [6, 7]])
       
np.dot(B,A)
>>> array([[4, 9],
           [2, 3]])
# 순서가 바뀌면 행렬 곱의 결과도 바뀐다.

(2) 전치 행렬 (traspose matrix) : 행과 열의 위치를 바꾸는 것

np.transpose(A)
>>> array([[0, 2],
           [1, 3]])
           
A.transpose()
>>> array([[0, 2],
           [1, 3]])

(3) 역행렬 : 행렬의 곱셈에 대한 역원

np.linalg.inv(A)
>>> array([[-1.5,  0.5],
           [ 1. ,  0. ]])

(4) 행렬식 (determinant) : 행렬식의 값이 0인 경우에는 역행렬을 가질 수 없다.

np.linalg.det(A)
>>> -2.0

 

3. 배열의 인덱싱과 슬라이싱

1. 배열의 인덱싱 : 배열의 위치나 조건을 지정해 배열의 원소를 선택하는 것

배열명[위치]

a1 = np.array([0, 10, 20, 30, 40, 50])
a1
>>> array([ 0, 10, 20, 30, 40, 50])

a1[0]
>>> 0

a1[4]
>>> 40

(1) 원소 변경하기

a1[5] = 70
a1
>>> array([ 0, 10, 20, 30, 40, 70])

(2) 1차원 배열에서 여러 개의 원소 선택하기

a1[[1,3,4]]
>>> array([10, 30, 40])

(3) 2차원 배열 생성

a2 = np.arange(10, 100, 10).reshape(3,3)
a2
>>> array([[10, 20, 30],
           [40, 50, 60],
           [70, 80, 90]])
           

a2[0,2]
>>> 30

(4) 2차원에서 원소 값 변경

a2[2,2] = 95
a2
>>> array([[10, 20, 30],
           [40, 50, 60],
           [70, 80, 95]])

(5) 하나만 입력하면 행위치를 지정한다.

a2[1]
>>> array([40, 50, 60])

(6) 2차원에서 특정 행을 변경

a2[1]=np.array([45,55,65])
a2
>>> array([[10, 20, 30],
           [45, 55, 65],
           [70, 80, 95]])
           
           
a2[1] = [47, 57, 67]
a2
>>> array([[10, 20, 30],
           [47, 57, 67],
           [70, 80, 95]])

(7) 2차원 배열에서 여러 원소 선택하기
배열명 [행위치1, 행_위치2, ..., 행위치[n]], [열_위치1, 열_위치2, ..., 열_위치n]]

a2[[0,2],[0,1]]
>>> array([10, 80])

(8) 배열 [조건]

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

>>> array([4, 5, 6])

(9) 짝수만 출력

a[(a%2)==0]
>>> array([2, 4, 6])

2. 배열의 슬라이싱 : 범위를 지정해 배열의 일부분을 선택하는 슬라이싱

배열 [시작:끝]

b1=np.array([0, 10, 20, 30, 40, 50])
b1
>>> array([ 0, 10, 20, 30, 40, 50])

b1[:3]
>>> array([ 0, 10, 20])

b1[2:]
>>> array([20, 30, 40, 50])

(1) 슬라이싱으로 원소 변경

b1[2:5] = np.array([25,35,45])
b1
>>> array([ 0, 10, 25, 35, 45, 50])

b1[3:] =60
b1
>>> array([ 0, 10, 25, 60, 60, 60])

(2) 2차 배열에서의 슬라이싱
      배열 [행시작: 행 끝, 열시작: 열 끝]

b2= np.arange(10, 100, 10).reshape(3,3)
b2
>>> array([[10, 20, 30],
           [40, 50, 60],
           [70, 80, 90]])
           
b2[1:3, 1:3]
>>> array([[50, 60],
           [80, 90]])

b2[:3, 1:]
>>> array([[20, 30],
           [50, 60],
           [80, 90]])

(3) 행을 지정하고 열을 슬라이싱

b2[1][0:2]
>>> array([40, 50])

(4) 슬라이싱 된 배열에 값 지정하기

b2[0:2, 1:3] =np.array([[25, 35], [55, 65]])
b2
>>> array([[10, 25, 35],
           [40, 55, 65],
           [70, 80, 90]])