Py-Numpy

영은·2023년 9월 24일
0

데이터 전처리

목록 보기
2/10

1 Numpy 다루기


  • 배열(array)는 벡터(1D) 또는 행렬(2D이상)
    - List와 유사하나 List는 이종의 자료형이 가능하고, 수치 연산의 형태가 상이함
    - 원소의 개수를 바꿀 수 없음
  • Numpy는 배열 연산과 관련된 편리한 기능을 제공
    - 적은 메모리 사용으로 연산속도가 빠름
    - 벡터화 연산, 배열 인덱싱들을 통한 질의가 가능함
  • 파이썬에서는 기본적으로 배열 자료형을 제공하지 않기 때문에 배열을 다루기 위해서는 numpy를 이용함

1.1 배열(Array) 만들기

값을 이용하여 배열 만들기

  • 배열을 생성하고 유형 확인하기
import numpy as np

a = np.array([[1, 2, 3], [4, 5, 6]]) # 입력한 데이터 유형에 따라 자동으로 dtype이 설정됨
print(a)
print(a.shape)
print(a.dtype)
결과
[[1 2 3]
 [4 5 6]]
(2, 3)
int32
	a = np.array([[1.0, 2, 3], [4, 5, 6]]) # 입력한 데이터 유형에 따라 자동으로 dtype이 설정됨
    print(a)
    print(a.shape)
    print(a.dtype)
	결과
    [[1. 2. 3.]
     [4. 5. 6.]]
    (2, 3)
    float64
  • 배열의 유형을 변경하기
	a = a.astype(np.int32) # dtype을 변경할 수 있음
    print(a)
    print(a.dtype)
	결과
    [[1 2 3]
     [4 5 6]]
    int32
  • 유형을 지정하여 생성하기
	a = np.array([[1.2, 2.5, 3.7], [4, 5, 6]], dtype=np.int32) # 데이터는 반올림하지 않고 절삭함
    print(a)
    print(a.shape)
    print(a.dtype)
	결과
    [[1 2 3]
     [4 5 6]]
    (2, 3)
    int32
  • 배열의 크기를 확인하기
	print(a.shape) # (행의 크기, 열의 크기)를 튜플로 반환
    print(a.size) # 데이터의 갯수를 값으로 반환
    print(len(a)) # inatance(행)의 갯수를 값으로 반환
	결과
    (2, 3)
    6
    2

등간격의 배열 만들기

  • arrange(처음값, 마지막 값, 간격) : 처음값부터 (마지막 값-1)까지 간격(default=1)으로 정수 생성
 	np.arange(0,10,2) # 0부터 10까지 2씩 증가하는 1차원 배열 생성
	결과
    array([0, 2, 4, 6, 8])
	np.arange(0,10)
	결과
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
	np.arange(0,10.8,0.7)
	결과
    array([ 0. ,  0.7,  1.4,  2.1,  2.8,  3.5,  4.2,  4.9,  5.6,  6.3,  7. ,
    7.7,  8.4,  9.1,  9.8, 10.5])
	np.arange(10,0,-1)
	결과
    array([10,  9,  8,  7,  6,  5,  4,  3,  2,  1])
	np.arange(10) # 시작값 디폴트 0, 증가값 디폴트 1
	결과
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  • linspace(시작값, 마지막값, 갯수) : 시작값(포함)부터 마지막값(포함)까지 등간격으로 갯수(default=50)만큼 값 생성
	np.linspace(0,10,10)
	결과
    array([ 0.        ,  1.11111111,  2.22222222,  3.33333333,  4.44444444,
    5.55555556,  6.66666667,  7.77777778,  8.88888889, 10.        ])
	np.linspace(5,-5,9)
	결과
    array([ 5.  ,  3.75,  2.5 ,  1.25,  0.  , -1.25, -2.5 , -3.75, -5.  ])

empty, zeros, ones 배열 만들기

  • empty(빈배열) : 현재 메모리에 있는 값 그대로 빈 공간만 생성
	e = np.empty([3,3])
	print(e)
	결과
    [[ 5.    3.75  2.5 ]
     [ 1.25  0.   -1.25]
     [-2.5  -3.75 -5.  ]]
  • zeros(영배열) : 모든 원소가 0인 배열 생성
	z = np.zeros([3,3], dtype=np.int32)  # dtype의 디폴트는 float64
    print(z)
	결과
    [[0 0 0]
     [0 0 0]
     [0 0 0]]
  • ones(1배열) : 모든 원소가 1인 배열 생성
	o = np.ones([3,3])
	print(o)
	결과
    [[1. 1. 1.]
     [1. 1. 1.]
     [1. 1. 1.]]

랜덤 배열 만들기

  • seed는 랜덤값을 생성할 때, 시작점을 의미
  • seed를 지정해 주지 않으면, 매번 실행할 때마다 값이 변경됨
    - 테스트할 때마다 값이 변경되므로 비교 평가가 불가함
    - 따라서 seed를 지정해서 실험을 하고, 실제로 실행할 경우에는 이를 해제함
	np.random.seed(0)  
  • random.randint(): 지정한 값 사이의 정수를 랜덤으로 생성
	np.random.randint(10) # 0에서 (10-1)사이의 랜덤 정수 생성
	결과
    7
	np.random.randint(0,10,15) #0에서 (10-1)사이의 랜덤 정수 15개 생성
	결과
    array([8, 0, 8, 5, 9, 3, 7, 1, 8, 2, 6, 1, 6, 2, 7])
  • random.rand(): 0 이상 1 미만의 임의의 값을 생성
	np.random.rand(10) # 10개의 랜덤값을 가진 어레이 생성
	결과
    array([0.35433176, 0.75517944, 0.15649   , 0.05942972, 0.22688245,
   0.72483354, 0.64809509, 0.77758691, 0.34826044, 0.54150907])
	np.random.rand(2,5) # 랜덤 행렬 생성
	결과
    array([[0.08217991, 0.29054503, 0.45048709, 0.27329039, 0.37529013],
   [0.53922297, 0.40800884, 0.26610577, 0.51821411, 0.95936814]])
   

1.2 실습

	a = np.array([[1,2,3],[4,5,6]]) # 2차원 배열 생성
	print(a)  
	결과
    [[1 2 3]
	 [4 5 6]]
	np.arange(10).reshape(2,5) # 1부터 10까지 1차원 배열 생성 후 2 by 5로 변환
	결과
    array([[0, 1, 2, 3, 4],
   		   [5, 6, 7, 8, 9]])
	np.arange(2,3,0.1)  # 2이상 3미만 0.1간격 배열생성
	결과
    array([2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])
	np.linspace(1.0,4.0,6) # 1.0이상 4.0이하 등간격 6원소 배열생성
	결과
    array([1. , 1.6, 2.2, 2.8, 3.4, 4. ])
	np.zeros((2,2),int) # 2 by 2 정수 영행렬 생성
	결과
    array([[0, 0],
   		   [0, 0]])
	np.zeros((2,2),float) # 2by2 float 0행렬
	결과
    array([[0., 0.],
           [0., 0.]])
	np.ones((2,2)) # 2by2 float 1행렬
	결과
    array([[1., 1.],
           [1., 1.]])
	np.full((2,2), 7) # 2by2 행렬을 7로 채움
	결과
    array([[7, 7],
           [7, 7]])
	np.eye(2) # 2by2 eigen 행렬
	결과
    array([[1., 0.],
           [0., 1.]])
	np.random.random((2,2)) # 2by2 random 생성
	결과
    array([[0.42961291, 0.6709883 ],
           [0.71483539, 0.01489149]])

1.3 배열 다루기

배열 모양 바꾸기

  • 배열 풀기 -> 열벡터로 변환
	a = np.array([[1,2,3],[4,5,6]]) # 2차원 배열 생성
    print(f'{a = }')
    print(f'{a.reshape(-1) = }') # 1차원 배열로 변환
    print(f'{a = }') # 원본은 변하지 않음
	결과
    a = array([[1, 2, 3],
               [4, 5, 6]])
    a.reshape(-1) = array([1, 2, 3, 4, 5, 6])
    a = array([[1, 2, 3],
               [4, 5, 6]])
  • 배열은 변경 후 별도의 저장을 하지 않으면, 변경되지 않음
	a_61 = a.reshape(-1) 
    print(f'{a_61 = }')
    print(f'{a_61.shape = }')
    print(f'{a = }') # 원본은 변하지 않음
	결과
    a_61 = array([1, 2, 3, 4, 5, 6])
    a_61.shape = (6,)
    a = array([[1, 2, 3],
           	   [4, 5, 6]])
  • 배열의 모양을 바꾸기 -> 열벡터로 변환 후 재배정함
	a_32 = a.reshape(3,2)
	print(a_32)
	결과
    [[1 2]
     [3 4]
     [5 6]]

2 인덱싱과 슬라이싱


2.1 인덱싱(Indexing)

  • 배열에서 원소를 찾는 것
    - 인덱스는 0부터 시작
    - 맨 뒤는 -1
	a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
	print('a = \n', a)
    print(f'{a[2,2] = }') # 인덱스 2행, 인덱스 2열 원소
    print(f'{a[(2,2)] = }') # 인덱스 2행, 인덱스 2열 원소
    print(f'{a[1,2] = }') # 인덱스 1행, 인덱스 2열 원소
    print(f'{a[-1,-1] = }') # 마지막행, 마지막열 원소
	결과
    a = 
     [[ 1  2  3  4]
     [ 5  6  7  8]
     [ 9 10 11 12]]
    a[2,2] = 11
    a[(2,2)] = 11
    a[1,2] = 7
    a[-1,-1] = 12
    

2.2 슬라이싱(Slicing)

  • 배열에서 부분집합을 가져오는 것
    - i:j -> i에서 (j-1)까지
	print('a = \n', a)
    print(f'{a[1,0:1] = }') # 슬라이싱은 부분을 취하는 것이므로 원본과 같은 어레이가 반환됨
    print(f'{a[1,:] = }') # 인덱스 1행, 모든열
    print(f'{a[:,1] = }') # 모든행, 인덱스 1열
	결과
    a = 
     [[ 1  2  3  4]
     [ 5  6  7  8]
     [ 9 10 11 12]]
    a[1,0:1] = array([5])
    a[1,:] = array([5, 6, 7, 8])
    a[:,1] = array([ 2,  6, 10])
	print(f'{a[:2,:] = }') # 인덱스 0부터 인덱스 2미만행, 모든열
	결과
    a[:2,:] = array([[1, 2, 3, 4],
   					 [5, 6, 7, 8]])
	print(f'{a[:2,1:3] = }') # 인덱스 0부터 인덱스 2미만행,  인덱스 1부터 3미만 열
    print(f'{a[:,0:3:2] = }') # 모든행, 0부터 3미만 2간격 모든열
    print(f'{a[:,-1:-4:-2]= }') # 모든행, 마지막열부터 -4미만 -2간격 모든열
	결과
    a[:2,1:3] = array([[2, 3],
   					   [6, 7]])
    a[:,0:3:2] = array([[ 1,  3],
                        [ 5,  7],
                        [ 9, 11]])
    a[:,-1:-4:-2]= array([[ 4,  2],
                       	  [ 8,  6],
                       	  [12, 10]])
	print(f'{a[:,[3,0,2,1]] = }') # 모든행, 3,0,2,1열 순서로
    print(f'{a[[0,1,2],[0,1,2]] = }') # [0,0],[1,1],[1,2] 원소
	결과
    a[:,[3,0,2,1]] = array([[ 4,  1,  3,  2],
           [ 8,  5,  7,  6],
           [12,  9, 11, 10]])
    a[[0,1,2],[0,1,2]] = array([ 1,  6, 11])
	print(f'{a = }')
    a[0,0] = 100  # 인덱스 0행, 인덱스 0열 원소를 100으로 변경
    print(f'{a = }')
	결과
    a = array([[ 1,  2,  3,  4],
               [ 5,  6,  7,  8],
               [ 9, 10, 11, 12]])
    a = array([[100,   2,   3,   4],
               [  5,   6,   7,   8],
               [  9,  10,  11,  12]])

3 얕은 복사(shallow copy)와 깊은 복사(deep copy)


3.1 mutable과 immutable

  • mutable : 배열에서 원소의 수정 가능한 자료형
    - 리스트(List), 집합(Set), 딕셔너리(Dictionary)
  • immutable : 배열에서 원소의 수정이 불가능한 자료형
    - 문자열(String), 튜플(Tuple), Boolean, Number

mutable 동작원리

	ls = [1,2,3] # 리스트 생성
    arr = np.array(ls)  # 리스트로 배열 생성
    print(f'{ls = }')
    print(f'{arr = }')
    print(f'{id(ls) = }') # 리스트의 주소 확인
    print(f'{id(arr) = }') # 배열의 주소 확인, ls와 arr은 완전히 다른 객체
	결과
    ls = [1, 2, 3]
    arr = array([1, 2, 3])
    id(ls) = 2448791471360
    id(arr) = 2448738516496
  • 변수의 값을 변경하더라도 변수의 주소는 동일하고 배열의 값만 바뀜
	ls[0] = 10
    arr[0] = 100
    print(f'{ls = }')
    print(f'{arr = }')
    print(f'{id(ls) = }') # 리스트의 주소 확인
    print(f'{id(arr) = }') # 배열의 주소 확인
	결과
    ls = [10, 2, 3]
    arr = array([100,   2,   3])
    id(ls) = 2448791471360
    id(arr) = 2448738516496
  • 다른 배열을 할당하면, 주소가 바뀜
	ls = [4,5,6] # 리스트에 다른 값 배정
    print(f'{ls = }')
    print(f'{arr = }')
    print(f'{id(ls) = }') # 리스트의 주소 확인
    print(f'{id(arr) = }') # 배열의 주소 확인
	결과
    ls = [4, 5, 6]
    arr = array([100,   2,   3])
    id(ls) = 2448791484224
    id(arr) = 2448738516496
	arr = np.array([4,5,6])  # 다른 배열 배정
    print(f'{ls = }')
    print(f'{arr = }')
    print(f'{id(ls) = }') # 리스트의 주소 확인
    print(f'{id(arr) = }') # 배열의 주소 확인
	결과
    ls = [4, 5, 6]
    arr = array([4, 5, 6])
    id(ls) = 2448791484224
    id(arr) = 2448791602448

3.2 얕은 복사(shallow copy)

  • 다른 변수로 복사하면 주소는 동일함
	arr = np.array([1,2,3])  # 배열 배정
    print(f'{arr = }')
    print(f'{id(arr) = }') # 배열의 주소 확인

    arr_shallow_copied = arr

    print(f'{arr_shallow_copied = }')
    print(f'{id(arr_shallow_copied) = }') # 배열의 주소 확인, arr과 동일 주소
	결과
    arr = array([1, 2, 3])
    id(arr) = 2448791602256
    arr_shallow_copied = array([1, 2, 3])
    id(arr_shallow_copied) = 2448791602256
  • 두 변수는 동일한 주소를 가지고 있으므로, 하나의 값을 변경하면 다른 하나도 변경됨
	arr[0] = 500
    arr_shallow_copied[2] = 1000

    print(f'{arr = }') # 원본 복사본 모두 변경됨
    print(f'{arr_shallow_copied = }') # 원본 복사본 모두 변경됨
	결과
    arr = array([ 500,    2, 1000])
    arr_shallow_copied = array([ 500,    2, 1000])

3.3 깊은 복사(deep copy)

  • 이러한 현상을 해소하기 위해서는 deep copy를 해야함
	arr = np.array([1,2,3])  # 배열 배정
    print(f'{arr = }')
    print(f'{id(arr) = }') # 배열의 주소 확인

    import copy
    arr_deep_copied = copy.deepcopy(arr)
    arr_copied = arr.copy()  # copy() 메소드를 사용하여 복사, 파이썬 내장함수, 1차원 배열만 가능

    print(f'{arr_deep_copied = }')
    print(f'{id(arr_deep_copied) = }') # 배열의 주소 확인, arr과 다른 주소

    print(f'{arr_copied = }')
    print(f'{id(arr_copied) = }') # 배열의 주소 확인, arr과 다른 주소
	결과
    arr = array([1, 2, 3])
    id(arr) = 2448791602832
    arr_deep_copied = array([1, 2, 3])
    id(arr_deep_copied) = 2448791602448
    arr_copied = array([1, 2, 3])
    id(arr_copied) = 2448791603024
	arr[0] = 500
    arr_deep_copied[2] = 1000

    print(f'{arr = }') # 각각의 값만 변경됨
    print(f'{arr_deep_copied = }') # 각각의 값만 변경됨
	결과
    arr = array([500,   2,   3])
    arr_deep_copied = array([   1,    2, 1000])

4 배열 연산하기


4.1 사칙연산

	a = np.arange(9).reshape(3, 3)
	a
	결과
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
  • 배열과 수치의 연산
	a+3
	결과
    array([[ 3,  4,  5],
           [ 6,  7,  8],
           [ 9, 10, 11]])
	a+3.0
	결과
    array([[ 3.,  4.,  5.],
           [ 6.,  7.,  8.],
           [ 9., 10., 11.]])
	a*3
	결과
    array([[ 0,  3,  6],
           [ 9, 12, 15],
           [18, 21, 24]])
	a/3
	결과
    array([[0.        , 0.33333333, 0.66666667],
           [1.        , 1.33333333, 1.66666667],
           [2.        , 2.33333333, 2.66666667]])
	a**2
	결과
    array([[ 0,  1,  4],
           [ 9, 16, 25],
           [36, 49, 64]])
  • 배열과 벡터의 연산(브로드캐스팅 : shape가 다른 배열 간에도 자동반복하여 연산이 가능하게 하는 것)
	a = np.arange(9).reshape(3, 3)
    b = np.array([100, 200, 300])
    print(f'{a = }')
    print(f'{b = }')
	결과
    a = array([[0, 1, 2],
               [3, 4, 5],
               [6, 7, 8]])
    b = array([100, 200, 300])
	a+b  # 각 행마다 벡터를 원소끼리 더함
	결과
    array([[100, 201, 302],
           [103, 204, 305],
           [106, 207, 308]])
	a*b  # 각 행마다 벡터를 원소끼리 곱함
	결과
    array([[   0,  200,  600],
           [ 300,  800, 1500],
           [ 600, 1400, 2400]])
  • 배열과 배열의 연산
	a = np.arange(9).reshape(3, 3)
    print(f'{a = }')
    c = np.arange(0,90,10).reshape(3, 3)
    print(f'{c = }')
	결과
    a = array([[0, 1, 2],
               [3, 4, 5],
               [6, 7, 8]])
    c = array([[ 0, 10, 20],
               [30, 40, 50],
               [60, 70, 80]])
	a+c  # 동일 위치의 원소끼리 더함
	결과
    array([[ 0, 11, 22],
           [33, 44, 55],
           [66, 77, 88]])
	a*c  # 동일 위치의 원소끼리 곱함
	결과
    array([[  0,  10,  40],
           [ 90, 160, 250],
           [360, 490, 640]])

4.2 메서드를 이용한 연산

  • 배열은 일종의 클래스 오브젝트로서 자체 변수와 메서드를 가지고 있음
	a = np.arange(9).reshape(3, 3)
	a
	결과
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
	a.sum() # 모든 원소의 합
	결과
    36
	a.sum(axis=0)  # 열별 합
	결과
    array([ 9, 12, 15])
	a.sum(axis=1)  # 행별 합
	결과
	array([ 3, 12, 21])
	print(f'{a.min()= }, {a.max()= }, {a.mean()= }, {a.std()= }, {a.var()= }')
	결과
    a.min()= 0, a.max()= 8, a.mean()= 4.0, a.std()= 2.581988897471611, a.var()= 6.666666666666667

4.3 numpy 함수를 이용한 연산

	print('a = \n', a)
	print('c = \n', c)
	결과
    a = 
     [[0 1 2]
     [3 4 5]
     [6 7 8]]
    c = 
     [[ 0 10 20]
     [30 40 50]
     [60 70 80]]
	np.sqrt(a) # 원소별 연산
	결과
    array([[0.        , 1.        , 1.41421356],
           [1.73205081, 2.        , 2.23606798],
           [2.44948974, 2.64575131, 2.82842712]])
	np.dot(a, c.transpose()) # 행렬 연산
	결과
    array([[  50,  140,  230],
           [ 140,  500,  860],
           [ 230,  860, 1490]])
	np.dot(a, c.T) # 행렬 연산
	결과
    array([[  50,  140,  230],
           [ 140,  500,  860],
           [ 230,  860, 1490]])

4.3-1 기타 유용한 배열 연산

찾기 및 추출

	a = np.arange(9).reshape(3, 3)
	print('a = \n', a)
	결과
    a = 
       [[0 1 2]
       [3 4 5]
       [6 7 8]]
	a>5 # a의 원소 중 5보다 큰 것
	결과
    array([[False, False, False],
           [False, False, False],
           [ True,  True,  True]])
	a[a>5] # a의 원소 중 5보다 큰 것만 뽑아냄
	결과
    array([6, 7, 8])
	(a>3)&(a<=7) # a의 원소 중 3보다 크고 7보다 작거나 같은 것
	결과
    array([[False, False, False],
           [False,  True,  True],
           [ True,  True, False]])
	a[(a>3)&(a<=7)] # a의 원소 중 3보다 크고 7보다 작거나 같은 것만 뽑아냄
	결과
    array([4, 5, 6, 7])

4.4 기타 함수

	np.empty_like(a)   # a와 동일한 shape를 가지며 비어있는 행렬 생성
	결과
    array([[        0,         1,         0],
           [  5570652,      1544,         0],
           [      768,       181, 572533794]])
	np.tile(a, (3, 2)) # a 를 3행 2열로 쌓음
	결과
    array([[0, 1, 2, 0, 1, 2],
           [3, 4, 5, 3, 4, 5],
           [6, 7, 8, 6, 7, 8],
           [0, 1, 2, 0, 1, 2],
           [3, 4, 5, 3, 4, 5],
           [6, 7, 8, 6, 7, 8],
           [0, 1, 2, 0, 1, 2],
           [3, 4, 5, 3, 4, 5],
           [6, 7, 8, 6, 7, 8]])
	a = np.arange(6).reshape(2, 3)
    b = np.arange(0,60,10).reshape(2, 3)
    print('a = \n', a)
    print('b = \n', b)
	결과
    a = 
     [[0 1 2]
     [3 4 5]]
    b = 
     [[ 0 10 20]
     [30 40 50]]
	np.vstack((a,b)) # 수직으로 쌓음
	결과
    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 0, 10, 20],
           [30, 40, 50]])
	np.hstack((a,b)) # 수평으로 쌓음
	결과
    array([[ 0,  1,  2,  0, 10, 20],
           [ 3,  4,  5, 30, 40, 50]])
	np.concatenate((a,b), axis=0) # 수직으로 쌓음
	결과
    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 0, 10, 20],
           [30, 40, 50]])
	np.concatenate((a,b), axis=1) # 수평으로 쌓음
	결과
    array([[ 0,  1,  2,  0, 10, 20],
   		   [ 3,  4,  5, 30, 40, 50]])
	np.concatenate((a,b), axis=None) # 1차원 배열로 쌓음
	결과
    array([ 0,  1,  2,  3,  4,  5,  0, 10, 20, 30, 40, 50])
	np.r_[a,b] # 수직으로 쌓음
	결과
    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 0, 10, 20],
           [30, 40, 50]])
	np.c_[a,b] # 수평으로 쌓음	
	결과
    array([[ 0,  1,  2,  0, 10, 20],
   		   [ 3,  4,  5, 30, 40, 50]])
	np.ravel(a, order='C') # 1차원 배열로 변환 (C: row 우선 디폴트, F: column 우선)
	결과
    array([0, 1, 2, 3, 4, 5])
	np.ravel(a, order='F') # 1차원 배열로 변환 (C: row 우선 디폴트, F: column 우선)
	결과
    array([0, 3, 1, 4, 2, 5])
profile
나의 기록장

0개의 댓글

관련 채용 정보