ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CHAPTER 10]넘파이(numpy)
    Deep Learning/인공지능 입문 코딩 2021. 5. 30. 21:29
    728x90

    넘파이는 리스트 대신 쓸 수 있는 라이브러리이다.

    numpy는 파이썬에서 수치 데이터를 다루는 가장 기본적이고 강력한 패키지이다.

    데이터 과학에서는 파이썬의 기본 리스트로 충분하지 않다.

    데이터를 처리할 때는 리스트와 리스트 간의 다양한 연산이 팔요한데, 파이썬의 기본 리스트 자료형은 이러한 기능이 부족하며, 연산 속도도 빠르지 않다.


    numpy를 사용하는 이유

    1. 리스트보다 연산이 훨씬 빠르다.
    2. 대용량의 배열과 행렬연산을 빠르게 수행한다.
    3. 고차원적인 수학 연산자와 함수를 포함하고 있다.
    4. 데이터 분석을 위한 패키지인 판다스pands나 기계학습을 위한 scikit-learn, tensorflow등이 넘파이 위에서 작동한다.

    numpy vs list

     


    numpy를 사용해보자.

    먼저 nuimpy 라이브러리를 설치한다.

    C:\> pip install numpy

     

    일반적으로 numpy는 np로 줄여서 사용한다.

    array() 함수를 이용하여 넘파이 배열을 만들 수 있다.

    >>> import numpy as np
    >>> a = np.array([1, 2, 3])
    >>> a
    array([1, 2, 3])

    numpy 연산

    넘파이의 덧셈 연산은 각 요소별로 더한다.

    뺄셈, 곱셈, 나눗셈도 마찬가지이다. 아주아주 편한 기능이다.

    >>> a = np.array([1, 2, 3])
    >>> b = np.array([3, 4, 5])
    
    >>> a + b
    array([4, 6, 8])
    
    >>> a - b
    array([-2, -2, -2])
    
    >>> a * b
    array([ 3,  8, 15])
    
    >>> a / b
    array([0.33333333, 0.5       , 0.6       ])

     

    스칼라 값 10을 더하면 모든 요소에 10씩 더해준다.

    빼기 곱하기 나누기도 마찬가지이다.

    >>> nums = np.array([100, 200, 300])
    >>> nums += 10
    >>> nums
    array([110, 210, 310])
    
    >>> nums = np.array([100, 200, 300])
    >>> nums *= 2
    >>> nums
    array([200, 400, 600])
    
    >>> nums = np.array([100, 200, 300])
    >>> nums -= 30
    >>> nums
    array([ 70, 170, 270])
    
    >>> nums = np.array([100.0, 200.0, 300.0])
    >>> nums /= 10
    >>> nums
    array([10., 20., 30.])

    나누기에서 100.0과 같이 .0을 붙여준 이유는 data type을 float로 정해주기 위해서이다.

    int type은 실수 나누기를 할 수 없기 때문에 /= 연산을 할 때 에러가 난다.

     

    다차원 배열

    넘파이의 핵심이 되는 다차원 배열(ndarray)은 다음과 같은 속성을 가지고 있다.

    >>> a = np.array([1, 2, 3])
    
    >>> a.shape	# a 객체의 형태(shape)
    (3,)
    
    >>> a.ndim	# a 객체의 차원
    1
    
    >>> a.dtype	# a 객체 내부 자료형
    dtype('int32')
    
    >>> a.itemsize	# a 객체 내부 자료형이 차지하는 메모리 크기(byte)
    4
    
    >>> a.size	# a 객체의 전체 크기(항목의 수)
    3

     

    2차원 배열은 다음과 같이 만들 수 있다. 수학에서의 행렬(matrix)과 비슷하다. numpy는 행렬의 관한 연산도 지원한다.

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

     

    인덱싱 슬라이싱

    당연히 인덱싱과 슬라이싱도 numpy에서 할 수 있다.

    >>> nums = np.array([1, 2, 3, 4, 5 , 6 , 7 , 8 , 9])
    >>> nums[0]
    1
    >>> nums[:]
    array([1, 2, 3, 4, 5, 6, 7, 8, 9])
    >>> nums[2:4]
    array([3, 4])
    >>> nums[2:7:-1]
    array([], dtype=int32)
    >>> nums[7:2:-1]
    array([8, 7, 6, 5, 4])

     

    다음은 2차원 배열의 인덱싱이다.

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

     

    numpy 스타일의 인덱싱과 슬라이싱

    numpy에서 많이 사용되는 표기법이 있다.

    인덱싱부터 살펴보자.

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

     

    numpy는 리스트와 달리, numpy의 모든 항목은 동일한 자료형을 가진다.

    int type의 np_array에 float인 1.23을 삽입하면 강제 형변환되어 1로 바뀐다.

    >>> np_array
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    >>> np_array[2, 2] = 1.23
    >>> np_array
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 1]])

     

    2차원 배열에관한 numpy 스타일의 인덱싱이다.

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

     

    리스트 슬라이싱과 넘파이 스타일의 슬라이싱을 적용했을 때, 주의해야할 점이 있다.

    코드를 통해 살펴보자.

    >>> np_array = np.array([[ 1,  2,  3,  4], 
                         [ 5,  6,  7,  8], 
                         [ 9, 10, 11, 12], 
                         [13, 14, 15, 16]])
    >>> print(np_array[::2][::2]) # 첫 슬라이싱: 0행, 2행 선택, 두 번째 슬라이싱: 그 중 0행 선택
    [[1 2 3 4]]
    >>> print(np_array[::2, ::2])  # 행 슬라이싱: 0행, 2행 선택, 열 슬라이싱: 0열 2열 선택
    [[ 1  3]
     [ 9 11]]

     

    논리적인 인덱싱

    어떤 조건을 주어서 배열에서 원하는 값을 추려내는 것을 논리적인 인덱싱이라고 하는데

    예제를 보는게 이해가 빠를것 같다.

     

    ages라는 배열에서 20살 이상인 사람만 고르려고 하면 다음과 같은 조건식을 써준다.

    >>> ages = np.array([18, 25, 19, 30, 28])
    >>> y = ages > 20
    >>> y
    array([False,  True, False,  True,  True])

     

    ages의 각 요소에 '> 20' 연산을 하여서 그 결과값을 numpy 배열로 만들고 y로 참조하고있다.

     

    조건을 주어 만족하는 항목만 뽑아내는 방법은 다음과 같다.

    부울형 배열을 인덱스로 하여 배열 ages에 보내면 된다.

    >>> ages[ ages > 20 ]
    array([25, 30, 28])

     

    2차원 배열에서도 논리적인 인덱싱을 살펴보자.

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

     

    2차원 배열에서 5보다 큰값을 뽑아내면 1차원 배열이 반환된다.

    >>> np_array[ np_array > 5 ]
    array([6, 7, 8, 9])
    

     

    마지막 세 번째 열에 대해서만 5보다 큰 값을 뽑아내고 싶을 때 다음과 같이 할 수 있다.

    >>> np_array[:, 2]
    array([3, 6, 9])
    
    >>> np_array[:, 2] > 5
    array([False,  True,  True])

     

    짝수만을 뽑아내보자.

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

    numpy 함수

    numpy의 다양한 함수

    arange()

    특정한 범위의 정수를 가지는 numpy 배열을 쉽게 만들 수 있다.

    numpy.arange(start, stop, step) 의 형태로 사용한다.

    start : 시작할 값, 생략시 0으로 처리

    stop : 멈출 값, 생략할 수 없음

    step : 생성 간격, 생략시 1으로 처리

    >>> np.arange(5)
    array([0, 1, 2, 3, 4])
    
    >>> np.arange(1, 6)
    array([1, 2, 3, 4, 5])
    
    >>> np.arange(1, 10, 2)
    array([1, 3, 5, 7, 9])

    linspace()

    시작 값부터 끝 값까지 균일한 간격으로 지정된 개수만큼 배열을 생성한다.

    numpy.linspace(start, stop, num=50) 의 형태로 사용한다.

    start : 시작할 값, 생랼할 수 없음

    stop : 멈출 값, 생략할 수 없음

    num : 생성 개수, 생략시 50

     

    0부터 10까지 총 20개의 수로 생성

    >>> np.linspace(0, 10, 20)
    array([ 0.        ,  0.52631579,  1.05263158,  1.57894737,  2.10526316,
            2.63157895,  3.15789474,  3.68421053,  4.21052632,  4.73684211,
            5.26315789,  5.78947368,  6.31578947,  6.84210526,  7.36842105,
            7.89473684,  8.42105263,  8.94736842,  9.47368421, 10.        ])

    logspace()

    로그 스케일로 수를 생성한다.

    numpy.logspace(start, stop, num=50) 의 형태로 사용한다.

    start : 시작할 값 10의 start제곱 부터 시작한다. 생랼할 수 없음

    stop : 멈출 값, 10의 stop제곱 까지 한다. 생략할 수 없음

    num : 생성 개수, 생략시 50

     

    10의 0승부터 10의 5승까지 10개 생성한다.

    >>> np.logspace(0, 5, 10)
    array([1.00000000e+00, 3.59381366e+00, 1.29154967e+01, 4.64158883e+01,
           1.66810054e+02, 5.99484250e+02, 2.15443469e+03, 7.74263683e+03,
           2.78255940e+04, 1.00000000e+05])

    reshape()

    데이터의 개수는 유지한 채로 배열의 차원과 형태를 변경한다.

     

    1차원 배열 y를 (3, 4) 형태로 변경한다.

    >>> y = np.arange(12)
    >>> y
    array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
    >>> y.reshape(3, 4)
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])

     

    -1을 전달하면 자동으로 결정해준다.

    >>> y.reshape(6, -1)
    array([[ 0,  1],
           [ 2,  3],
           [ 4,  5],
           [ 6,  7],
           [ 8,  9],
           [10, 11]])

     

    원래의 배열과 인자로 넘겨준 형태가 호환되지 않는 경우 에러가 난다.

    flatten()

    2차원 이상의 고차원 배열을 1차원 배열로 만들어준다.

    >>> y = np.array([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11]])
    >>> y.flatten()
    array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

     

    zeros()

    배열을 0으로 채워서 만드는 함수이다.

    >>> np.zeros((5,3))
    array([[0., 0., 0.],
           [0., 0., 0.],
           [0., 0., 0.],
           [0., 0., 0.],
           [0., 0., 0.]])

     

    평균값과 중앙값

    mean()

    배열의 평균 값을 반환한다.

    >>> np_array = np.array([10, 20, 30, 50, 100, 15])
    >>> np.mean(np_array)
    37.5

    median()

    배열의 가운데 값을 반환한다. 짝수개 라면 가운데에서 양 옆의 평균 값을 반환한다.

    >>> np_array = np.array([10, 20, 30, 50, 100, 15])
    >>> np.median(np_array)
    25.0

    난수 생성

    np.random

    난수를 생성하기 위해서는 np.random 모듈을 사용한다. random에 있는 함수들을 살펴보자.

    np.random.rand()

    0.0에서 1.0사이의 값으로 난수를 생성한다. 인자로는 만들 배열의 shape을 전달한다.

    >>> np.random.rand(5)
    array([0.49751499, 0.31398873, 0.45012847, 0.31606539, 0.06677681])
    
    >>> np.random.rand(5, 3)
    array([[0.25414674, 0.40959008, 0.70517544],
           [0.97714843, 0.5646511 , 0.90605363],
           [0.45351476, 0.10091817, 0.16510527],
           [0.03597925, 0.03488961, 0.39528701],
           [0.71854897, 0.60254816, 0.97728744]])

    np.random.randint()

    정수의 난수를 생성하려면 randint()를 사용한다. 인자로는 수의 범위와 만들 배열의 shape을 전달한다.

    >>> np.random.randint(1, 7, size=10)
    array([3, 4, 4, 1, 1, 5, 5, 5, 1, 5])
    
    >>> np.random.randint(1, 11, size=(4, 7))
    array([[2, 4, 8, 9, 8, 1, 8],
           [6, 9, 2, 2, 2, 9, 2],
           [5, 4, 2, 5, 7, 1, 2],
           [2, 6, 9, 7, 2, 8, 6]])

    np.random.randn()

    정규분포를 따르는 난수를 생성하는 함수이다. 인자로는 만들 배열의 shape을 전달한다.

    >>> np.random.randn(5)
    array([-0.08394584,  1.95022772, -0.82862685, -0.78388742, -0.67895819])
    
    >>> np.random.randn(5, 4)
    array([[-1.09660329, -2.56716166,  0.53713945, -0.25978302],
           [-0.3685374 ,  0.72779684, -0.25958997, -0.17508518],
           [ 0.17866193, -0.09862476,  0.18201602,  0.68600289],
           [ 0.37499333, -0.77584093,  1.39493713,  0.84276932],
           [-0.92075815,  1.48575966, -1.84414796, -0.24720447]])

     

    평균값이 m이고 표준편차가 s인 정규분포를 따르는 난수 생성

    >>> m = 10
    >>> s = 2
    >>> randoms = m + s * np.random.randn(5, 4)
    >>> randoms
    array([[10.34485064,  7.90504946,  7.32753766, 13.06963837],
           [11.85322192,  7.81467728, 12.26945792, 11.13919031],
           [12.07424941,  9.52899411,  5.830724  ,  9.50796013],
           [ 7.55042608,  7.68533965,  8.781901  , 12.4769719 ],
           [ 8.81252884, 13.49935319,  9.27500074,  9.65235054]])

    상관관계

    상관관계는 두개 변수가 선형 관계가 있는(상수 비율에서 함께 변경됨을 의미함) 범위를 표현하는 통계적 측도이다.

     

    corrcoeff(x,y) 함수는 요소들의 상관관계를 계산한다.

    x와 x의 상관관계를 Cxx, x와 y의 상관관계를 Cxy, y와 x의 상관관계를 Cyx, y와 y의 상관관계를 Cyy 라고 할때 다음과 같은 행렬을 반환한다.

     

    변수가 3개일 때는 다음과 같다.

    >>> x = [i for i in range(100)]	# 0, 1, 2, 3, ... 99
    >>> y = [i ** 2 for i in range(100)]	# 0, 1, 4, 9, ... 99^2
    >>> z = [100 * np.sin(3.14 * i / 100) for i in range(100)]
    >>> result = np.corrcoef([x, y, z])
    >>> result
    array([[ 1.        ,  0.96764439,  0.03763255],
           [ 0.96764439,  1.        , -0.21532645],
           [ 0.03763255, -0.21532645,  1.        ]])

    x : 0 ~ 99 의 값

    y : 0 ~ 99 의 제곱의 값

    z : sin함수의 100배의 값

    x가 증가하면 y의 값도 증가하므로 둘의 상관관계는 매우 높다.

    x와 z, y와 z는 상관관계의 값이 낮다.


    지금까지 배운 것들로

    2차원 배열에서 특정 조건을 만족하는 행을 추출하기

    선수 들의 키와 몸무게에서 80kg이 넘는 선수들과 키가 180이상인 선수들의 정보를 추출 해보자.

    import numpy as np 
    
    players = [[170, 76.4], 
               [183, 86.2], 
               [181, 78.5], 
               [176, 80.1]] 
    
    np_players = np.array(players) 
    
    print('몸무게가 80 이상인 선수 정보');
    print(np_players[ np_players[:, 1] >= 80.0 ])
    
    print('키가 180 이상인 선수 정보');
    print(np_players[ np_players[:, 0] >= 180.0 ])
    몸무게가 80 이상인 선수 정보
    [[183.   86.2]
     [176.   80.1]]
    키가 180 이상인 선수 정보
    [[183.   86.2]
     [181.   78.5]]

     

    4개의 데이터의 상관관계를 분석하기

    import numpy as np
    
    x1 = [i for i in range(100)]
    x2 = [i + np.random.randint(1, 10) for i in range(100)]
    x3 = [i + np.random.randint(1, 50) for i in range(100)]
    x4 = [np.random.randint(1, 100) for i in range(100)]
    
    print(np.corrcoef([x1, x2, x3, x4]))
    
    [[ 1.          0.99596869  0.89737899 -0.01597915]
     [ 0.99596869  1.          0.88740142 -0.02033749]
     [ 0.89737899  0.88740142  1.         -0.01669295]
     [-0.01597915 -0.02033749 -0.01669295  1.        ]]

    결과를 보아 x1, x2의 상관관계가 가장 높음을 알 수 있다.

    728x90

    댓글

Designed by Tistory.