자료 안내: 여기서 다루는 내용은 아래 사이트의 내용을 일부 참고하여 생성되었음.

http://www.scipy-lectures.org/

넘파이 활용 기초


In [1]:
import matplotlib.pyplot as plt
import numpy as np

In [2]:
%matplotlib inline

주요 내용

실전 문제를 넘파이 모율을 이용하여 해결하는 두 가지 예제를 살펴본다. 넘파이 모듈을 사용하면 이전에 복잡하게 했던 일을 훨씬 간단하게, 그리고 빠르게 처리할 수 있음에 주의해야 한다.

여기서는 아래 함수 또는 방법들의 활용을 배운다.

  • np.linspace()
  • np.loadtxt()
  • 전치행렬 생성하는 방법

넘파이 활용: 함수 그래프 그리기

np.linspace 함수를 이용하여 간단한 점, 또는 선 그래프를 그려보자.

예제

아래 함수의 그래프를 그려보자.

times_3(x) = 3*x

먼저 x축의 구간을 정한다. 예를 들어, [0, 3]의 구간으로 하자. 이제 그 구간을 균등하게 쪼개어 x축 상에서 30개의 x좌표를 구해 보자.


In [3]:
xs = np.linspace(0, 3, 30)
xs


Out[3]:
array([ 0.        ,  0.10344828,  0.20689655,  0.31034483,  0.4137931 ,
        0.51724138,  0.62068966,  0.72413793,  0.82758621,  0.93103448,
        1.03448276,  1.13793103,  1.24137931,  1.34482759,  1.44827586,
        1.55172414,  1.65517241,  1.75862069,  1.86206897,  1.96551724,
        2.06896552,  2.17241379,  2.27586207,  2.37931034,  2.48275862,
        2.5862069 ,  2.68965517,  2.79310345,  2.89655172,  3.        ])

이제 y = 3 * x 함수를 정의하자.


In [4]:
def times_3(x):
    return 3*x

이제 y축 상에 x좌표에 해당하는 30개의 y좌표를 구할 수 있다.

힌트: 넘파이 모듈에서 함수에 어레이를 인자로 사용하면, 함수는 각각의 항목을 인자로 사용하여 새로운 어레이를 생성하여 리턴한다.


In [5]:
ys = times_3(xs)
ys


Out[5]:
array([ 0.        ,  0.31034483,  0.62068966,  0.93103448,  1.24137931,
        1.55172414,  1.86206897,  2.17241379,  2.48275862,  2.79310345,
        3.10344828,  3.4137931 ,  3.72413793,  4.03448276,  4.34482759,
        4.65517241,  4.96551724,  5.27586207,  5.5862069 ,  5.89655172,
        6.20689655,  6.51724138,  6.82758621,  7.13793103,  7.44827586,
        7.75862069,  8.06896552,  8.37931034,  8.68965517,  9.        ])

주의: ys는 아래와 같이 정의할 수도 있다.


In [6]:
ys = 3 * xs
ys


Out[6]:
array([ 0.        ,  0.31034483,  0.62068966,  0.93103448,  1.24137931,
        1.55172414,  1.86206897,  2.17241379,  2.48275862,  2.79310345,
        3.10344828,  3.4137931 ,  3.72413793,  4.03448276,  4.34482759,
        4.65517241,  4.96551724,  5.27586207,  5.5862069 ,  5.89655172,
        6.20689655,  6.51724138,  6.82758621,  7.13793103,  7.44827586,
        7.75862069,  8.06896552,  8.37931034,  8.68965517,  9.        ])

3 * xsxs의 각각의 항목에 3을 곱한다. 이와 같이 어레이의 연산은 각각의 항목에 대해 실행된다.

이제 xsys를 이용하여 times_3 함수의 그래프를 다음과 같이 그릴 수 있다.


In [7]:
plt.plot(xs, ys)


Out[7]:
[<matplotlib.lines.Line2D at 0x10d4886a0>]

plt.plot 함수에 세번 째 인자를 옵션으로 사용하여 그래프 모양을 변경할 수 있다. 예를 들어, 'o' 옵션은 점으로 좌표를 찍으라는 것을 나타낸다.


In [8]:
plt.plot(xs, ys, 'o')


Out[8]:
[<matplotlib.lines.Line2D at 0x10d576a90>]

두 경우를 합쳐서 그래프를 그려서 그래프가 어떻게 그려졌는지 쉽게 확인할 수도 있다.


In [9]:
plt.plot(xs, ys)
plt.plot(xs, ys, 'o')


Out[9]:
[<matplotlib.lines.Line2D at 0x10d4fca90>]

예제

아래 함수의 그래프를 그려보자.

squar(x) = x ** 2

이번에는 x좌표값의 구간을 [-1, 1]로 정한 후, 그 구간에서 균등하게 20개의 x좌표값을 구하자.


In [10]:
xs = np.linspace(-1, 1, 20)
xs


Out[10]:
array([-1.        , -0.89473684, -0.78947368, -0.68421053, -0.57894737,
       -0.47368421, -0.36842105, -0.26315789, -0.15789474, -0.05263158,
        0.05263158,  0.15789474,  0.26315789,  0.36842105,  0.47368421,
        0.57894737,  0.68421053,  0.78947368,  0.89473684,  1.        ])

이제 숫자를 제곱해서 리턴하는 함수를 정의한다.


In [11]:
def square(x):
    return x**2

이제 x좌표값들에 대응하는 y좌표값들을 구하자.


In [12]:
ys = square(xs)
ys


Out[12]:
array([ 1.        ,  0.80055402,  0.6232687 ,  0.46814404,  0.33518006,
        0.22437673,  0.13573407,  0.06925208,  0.02493075,  0.00277008,
        0.00277008,  0.02493075,  0.06925208,  0.13573407,  0.22437673,
        0.33518006,  0.46814404,  0.6232687 ,  0.80055402,  1.        ])

주의: ys를 아래와 같이 선언할 수 있다.


In [13]:
ys ** 2
ys


Out[13]:
array([ 1.        ,  0.80055402,  0.6232687 ,  0.46814404,  0.33518006,
        0.22437673,  0.13573407,  0.06925208,  0.02493075,  0.00277008,
        0.00277008,  0.02493075,  0.06925208,  0.13573407,  0.22437673,
        0.33518006,  0.46814404,  0.6232687 ,  0.80055402,  1.        ])

이제 각각 xsys로 이루어진 x좌표값 y좌표값들을 이용하여 제곱함수의 그래프를 그려보자.


In [14]:
plt.plot(xs, ys)
plt.plot(xs, ys, 'o')


Out[14]:
[<matplotlib.lines.Line2D at 0x10d5afe48>]

넘파이 활용: 통계 분석

어레이를 활용하면 통계 분석이 매우 간단해 진다.

예제

먼저 아래 사이트에서 populations.txt 파일을 다운 받는다. 파일을 다운 받으려면 링크에 마우스를 가져간 다음 마우스 오른쪽 버튼을 눌러 링크에 연결된 파일을 다운받으면 된다.

https://github.com/scipy-lectures/scipy-lecture-notes/blob/master/data/populations.txt

해당 파일은 1900년부터 1920년까지 캐나다 북부지역에서 서식한 산토끼(hare)와 스라소니(lynx)의 숫자, 그리고 채소인 당근(carrot)의 재배숫자를 순수 텍스트 데이터로 담고 있다.

파일의 내용은 아래와 같다.

주의: 아래 코드는 파이썬 코드가 아님에 주의할 것.

  • !type: 윈도우
  • !cat: 맥 또는 리눅스

In [15]:
!cat data/populations.txt
# !type populations.txt


# year	hare	lynx	carrot
1900	30e3	4e3	48300
1901	47.2e3	6.1e3	48200
1902	70.2e3	9.8e3	41500
1903	77.4e3	35.2e3	38200
1904	36.3e3	59.4e3	40600
1905	20.6e3	41.7e3	39800
1906	18.1e3	19e3	38600
1907	21.4e3	13e3	42300
1908	22e3	8.3e3	44500
1909	25.4e3	9.1e3	42100
1910	27.1e3	7.4e3	46000
1911	40.3e3	8e3	46800
1912	57e3	12.3e3	43800
1913	76.6e3	19.5e3	40900
1914	52.3e3	45.7e3	39400
1915	19.5e3	51.1e3	39000
1916	11.2e3	29.7e3	36700
1917	7.6e3	15.8e3	41800
1918	14.6e3	9.7e3	43300
1919	16.2e3	10.1e3	41300
1920	24.7e3	8.6e3	47300

앞서 설명한 대로 각 연도별로 산토끼, 스라소니, 당근의 개체수를 담고 있다.

이제 해당 파일을 파이썬으로 불러 들이자. 여기서는 파이썬의 open 함수 대신에 numpyloadtxt 함수를 이용한다.


In [16]:
data = np.loadtxt('data/populations.txt')

data를 확인해 보면 주석처리, 즉, 무시 해야 할 첫째 행을 제외한 나머지가 2차원 어레이로 저장되었음을 확인할 수 있다.


In [17]:
data


Out[17]:
array([[  1900.,  30000.,   4000.,  48300.],
       [  1901.,  47200.,   6100.,  48200.],
       [  1902.,  70200.,   9800.,  41500.],
       [  1903.,  77400.,  35200.,  38200.],
       [  1904.,  36300.,  59400.,  40600.],
       [  1905.,  20600.,  41700.,  39800.],
       [  1906.,  18100.,  19000.,  38600.],
       [  1907.,  21400.,  13000.,  42300.],
       [  1908.,  22000.,   8300.,  44500.],
       [  1909.,  25400.,   9100.,  42100.],
       [  1910.,  27100.,   7400.,  46000.],
       [  1911.,  40300.,   8000.,  46800.],
       [  1912.,  57000.,  12300.,  43800.],
       [  1913.,  76600.,  19500.,  40900.],
       [  1914.,  52300.,  45700.,  39400.],
       [  1915.,  19500.,  51100.,  39000.],
       [  1916.,  11200.,  29700.,  36700.],
       [  1917.,   7600.,  15800.,  41800.],
       [  1918.,  14600.,   9700.,  43300.],
       [  1919.,  16200.,  10100.,  41300.],
       [  1920.,  24700.,   8600.,  47300.]])

1900년부터 1920년까지, 즉, 21년 동안 산토끼, 스라소니, 당근의 개체수를 조사한 데이터이다. 따라서 위 데이터는 21 x 4 모양의 2차원 행렬에 해당한다.

실제로 위 데이터의 모양(shape)를 확인하면 동일한 결과를 얻는다.


In [18]:
data.shape


Out[18]:
(21, 4)

이제 위 데이터를 이용하여 1900년부터 1920년 사이에 각 개체별로 어떤 변화가 발생하였는지를 분석하고자 한다. 분석을 위해서는 그래프를 이용하는 것이 가장 효율적이다.

전치(transposition) 함수 활용

각 개체별 개체수의 21년간의 데이터를 먼저 구해야 한다. 그러기 위해 전치(transposition) 함수를 이용한다.

data.T

주의: 전치함수는 행과 열을 서로 바꾼다.


In [19]:
data.T


Out[19]:
array([[  1900.,   1901.,   1902.,   1903.,   1904.,   1905.,   1906.,
          1907.,   1908.,   1909.,   1910.,   1911.,   1912.,   1913.,
          1914.,   1915.,   1916.,   1917.,   1918.,   1919.,   1920.],
       [ 30000.,  47200.,  70200.,  77400.,  36300.,  20600.,  18100.,
         21400.,  22000.,  25400.,  27100.,  40300.,  57000.,  76600.,
         52300.,  19500.,  11200.,   7600.,  14600.,  16200.,  24700.],
       [  4000.,   6100.,   9800.,  35200.,  59400.,  41700.,  19000.,
         13000.,   8300.,   9100.,   7400.,   8000.,  12300.,  19500.,
         45700.,  51100.,  29700.,  15800.,   9700.,  10100.,   8600.],
       [ 48300.,  48200.,  41500.,  38200.,  40600.,  39800.,  38600.,
         42300.,  44500.,  42100.,  46000.,  46800.,  43800.,  40900.,
         39400.,  39000.,  36700.,  41800.,  43300.,  41300.,  47300.]])

즉, data.T4 x 21 모양의 2차원 어레이이며, 각각의 행은 차례대로 년도, 산토끼 수, 스라소니 수, 당근 수를 연도별로 담고 있다. 각각의 행을 독립된 어레이로 추출해야 하는데, 넘파이는 각각의 행에 대해 변수를 선언할 수 있는 기능을 제공한다.

주의: 튜플의 경우 각각의 항목에 변수를 선언할 수 있는 것과 동일하다.


In [20]:
year, hares, lynxes, carrots = data.T

예를 들어 year 변수에는 연도들로 이루어진 1차원 어레이가 할당된다.


In [21]:
year


Out[21]:
array([ 1900.,  1901.,  1902.,  1903.,  1904.,  1905.,  1906.,  1907.,
        1908.,  1909.,  1910.,  1911.,  1912.,  1913.,  1914.,  1915.,
        1916.,  1917.,  1918.,  1919.,  1920.])

hares 변수에는 각 연도별 개체수로 이루어진 1차원 어레이가 할당된다.


In [22]:
hares


Out[22]:
array([ 30000.,  47200.,  70200.,  77400.,  36300.,  20600.,  18100.,
        21400.,  22000.,  25400.,  27100.,  40300.,  57000.,  76600.,
        52300.,  19500.,  11200.,   7600.,  14600.,  16200.,  24700.])

lynxes, carrots 경우도 동일하게 작동한다.

그래프 그리기

아래 그림은 연도별 산토끼의 개체수, 스라소니의 개체수, 당근의 재배수를 각각 파란색, 녹색, 빨간색 그래프를 이용하여 동시에 표현한 것이다.

plot, axes, legned 함수의 활용방법에 대해서는 우선 너무 많은 신경을 쓰지 않아도 된다. 여기서는 기본 사용법만 알면 된다.

  • plot 함수: 여러 개의 선 그래프를 동시에 그릴 수 있다.

  • axes 함수: 셋째, 넷째 인자는 그래프의 크기를 결정한다. 반면에 첫째, 둘째 인자는 그래프의 위치를 지정할 때 사용한다. 하지만 plot 함수 등 그래프를 그리는 함수가 한 번만 호출된 경우에는 별 의미가 없다.

  • legend 함수: 범례를 추가하며, loc 키워드 인자는 범례의 위치를 지정한다.


In [23]:
plt.axes([0.2, 0.1, 0.5, 0.8])
plt.plot(year, hares, year, lynxes, year, carrots)
plt.legend(('Hare', 'Lynx', 'Carrot'), loc=(1.05, 0.5))


Out[23]:
<matplotlib.legend.Legend at 0x10d7d0128>

그래프 분석

산토끼의 개체수와 스라소니의 개체수, 그리고 당근의 재배수의 관계를 위 그림을 통해 어느정도 쉽게 알아낼 수 있다. (연습문제 참조)

연습문제

연습

수학에서 많이 사용되는 싸인(sin()) 함수의 그래프를 그려보아라.

연습

산토끼, 스라소니, 당근의 예제에서 1900년부터 1921년 사이에 개체별 개체수의 변화에 대해 어떤 분석을 할 수 있는지 그래프를 이용하여 설명하라.