통계는 데이터를 이해하는 바탕이 되는 수리적 기법이다.

 

데이터셋을 설명하는 가장 간단한 방법은 데이터 자체를 보여주는 것이다. 데이터의 수가 적다면 위 방법이 가장 좋은 방법이다. 데이터가 많을 때 통계를 사용하면, 데이터를 정제해서 중요한 정보만 전달해 줄 수 있다.

 

 

행렬

  • 행렬은 2차원으로 구성된 숫자의 집합이며, list의 list로 표현할 수 있다.
  • list 안의 list들은 행렬의 행을 나타내며 모두 같은 길이를 가지게 된다. 예를 들어, A라는 행렬에서 A[i][j]는 i번째 행과 j번째 열에 속한 숫자를 의미한다.

수학에서는 관습적으로 행렬을 대문자로 표기한다.

A = [[1,2,3],
     [4,5,6]] #A는 2개의 행과 3개의 열로 구성되어있다.
B = [[1,2],
     [3,4],
     [5,6]] #B는 3개의 행과 2개의 열로 구성되어있다.

수학에서는 첫번째 행을 '행 1', 첫번째 열을 '열 1'로 표기하지만 파이썬의 list에서는 0부터 시작하기 때문에 첫번째 행을 '행 0', 첫번째 열을 '열 0'으로 표기한다.

 

행렬을 list들의 list로 나타내는 경우, 행렬 A는 len(A)개의 행과 len(A[0])개의 열로 구성되어있다. 이와 같은 행렬의 형태는 다음과 같이 계산한다.

def shape(A):
    num_rows = len(A)
    num_cols = len(A[0]) #첫번째 행이 갖고있는 원소의 수
    return num_rows, num_cols

 

행렬이 n개의 행과 k개의 열로 구성되어 있다면, n X k 행렬이라고 부른다. n X k 행렬에서 각 행의 길이는 k이고 각 열의 길이는 n이다.

def get_row(A,i):
    return A[i]          #A[i]는 i번째 행을 나타낸다
def get_column(A,j):
    return [A_i[j]       #A[i]행의 j번째 원소  
           for A_i in A] #각 A_i행에 대해

 

이제 형태가 주어졌을 때, 형태에 맞는 행렬을 생성하고 각 원소를 채워 넣는 함수를 만든다. 중첩된 list comprehension을 사용해서 만든다.

def make_matrix(num_rows, num_cols, entry_fn):
    #(i,j)번째 원소가 entry_fn(i,j)인 num_rows X num_cols list를 반환
    return [[entry_fn(i,j)            #i가 주어졌을 때 list를 생성한다
            for j in range(num_cols)] #[entry_fn(i,0),...]
            for i in range(num_rows)] #각 i에 대해 하나의 list를 생성한다

위 함수를 사용해서 단위 행렬을 생성할 수 있다.

def is_diagonal(i,j):
    #대각선의 원소는 1, 나머지 원소는 0
    return 1 if i == j else 0
identy_matrix = make_matrix(5,5,is_diagonal)

 

각 벡터의 행렬의 행으로 나타냄으로써 여러 벡터로 구성된 데이터셋을 행렬로 표현할 수 있다. 예를 들어, 1,000명에 대한 키, 몸무게, 나이가 주어졌다면 1,000 X 3 행렬로 표현할 수 있다.

k차원의 벡터를 n차원 벡터로 변환해주는 선형함수를 n X k 행렬로 표현할 수 있다.

 

행렬로 이진 관계(binary relationship)를 나타낼 수 있다.

네트워크의 엣지(edge)들을 (i,j)쌍의 집합으로 표현했다. 이러한 네트워크의 구조를 행렬로 나타낼 수 있다. 예를 들어, i와 j가 연결되어 있다면 A[i][j]의 값이 1이고 그렇지 않다면 0인 행렬 A로 네트워크를 표현할 수 있다. 만약 네트워크에서 연결된 사용자의 수가 적다면, 행렬의 수많은 0값을 저장해야 하기 때문에 네트워크를 표현하기에 훨씬 더 비효율적이다. 하지만, 행렬에서는 두 사용자가 연결되어 있는지 훨씬 빠르게 확인해 볼 수 있다. 모든 엣지를 살펴보지 않고, 직접 행렬의 값을 확인해 보면 된다.

friendships[0][2] == 1 #참, 사용자 0과 2는 친구이다
friendships[0][8] == 0 #거짓, 사용자 0과 8은 친구가 아니다

사용자가 누구와 연결되어 있는지 알아보기 위해서는 해당 사용자를 나타내는 열(또는 행)만 살펴보면 된다.

friends_of_five = [i
                  for i, is_friend in enumerate(friendships[5]) #5는 5번째 사용자의 행
                  if is_friend]                                 #하나의 행만 살펴보면 된다

네트워크의 크기가 커지거나 형태가 지속족으로 변화한다면 이러한 방법은 매우 비효율적이고 관리하기가 힘들 것이다.

'Data Science > 선형대수' 카테고리의 다른 글

벡터  (0) 2020.05.27

 

선형대수는 벡터 공간을 다루는 수학의 한 분야이다.

선형대수는 다양한 데이터 과학 기술과 개념을 뒷받침 해주는 분야이다.

 

벡터

  • 벡터는 벡터끼리 더하거나 상수와 곱해지면 새로운 벡터를 생성하는 개념적인 도구이다.
  • 벡터는 어떤 유한한 차원의 공간에 존재하는 점들이다. 대부분의 데이터, 특히 숫자로 표현된 데이터는 벡터로 표현할 수 있다.
  • ex) 키, 몸무게, 나이로 구성된 데이터는 3차원 벡터로 표현할 수 있다.
  • ex2) 시험을 네 번 보는 수업에서 시험1 점수, 시험2 점수, 시험3 점수, 시험4 점수로 4차원 벡터로 표현할 수 있다.

벡터를 가장 간단하게 표현하는 방법은 숫자로 구성된 list로 표현하는 것이다. 3차원 벡터는 세 개의 숫자로 구성된 list로 표현할 수 있다.

height_weight_age = [70,170,40] #인치,파운드,나이
grades = [95,80,75,62] #시험 1~4까지의 각 점수

list로 벡터를 표현하는 방법의 문제점은 list를 통해 벡터 연산을 할 수 없다는 점이다. 파이썬 list는 벡터가 아니기 때문에, 벡터 연산을 해주는 기본적인 도구가 없다.

두 개의 벡터를 더한다는 것은 각 벡터 상에서 같은 위치에 있는 성분끼리 더한다는 것이다. 두 벡터의 길이가 다르면 차원이 다른 것이므로 더할 수 없다. [1,2]로 구성된 벡터와 [2,1]로 구성된 벡터를 더하면 [3,3]으로 구성된 벡터가 계산된다. [1,2]의 위치로부터 [2,1]의 위치를 더하면 [3,3]이 되기 때문이다.

 

벡터 덧셈은 zip을 사용해서 두 벡터를 묶은 뒤, 두 배열의 각 성분끼리 더하는 list comprehension을 적용한다.

def vector_add(v,w):
    #각 성분끼리 더한다
    return [v_i+w_i
           for v_i,w_i in zip(v,w)]
def vector_subtract(v,w):
    #각 성분끼리 뺀다
    return [v_i-w_i
           for v_i,w_i in zip(v,w)]

모든 벡터의 각 성분을 더한다는 것의 의미는 새로운 벡터의 첫 성분은 모든 벡터의 첫번째 성분을 더한 값, 두번째 성분은 모든 벡터의 두번째 성분을 더한 값으로 구성된다. 여러 벡터를 더하는 다양한 방법 중에서 벡터를 하나씩 더해가는 방법이 가장 간단하다.

def vector_sum(vectors):
    #모든 벡터의 각 성분들끼리 더한다
    result = vectors[0]                    #첫번째 벡터부터 시작해서
    for vector in vectors[1:]:             #나버지 벡터들을
        result = vector_add(result,vector) #더해준다
    return result

위의 방법은 벡터 list에 vector_add를 누적시켜서 적용하는 것과 같다.

아래의 reduce를 사용해서 더 간략하고 일반적인 함수로 표현할 수 있다.

def vector_sum(vectors):
    return reduce(vector_add,vectors)
vector_sum = partial(reduce, vector_add)

위의 방법처럼 표현할 수도 있다.

 

벡터에 스칼라를 곱해 줄 수 있어야 한다. 스칼라 곱셈은 벡터의 각 원소마다 스칼라 값을 곱해주는 방법으로 간단하게 구현할 수 있다.

def scalar_multiply(c,v):
    #c는 숫자, v는 벡터
    return [c*v_i for v_i in v]

 

같은 길이의 벡터로 구성된 list가 주어졌을 때 각 성분별 평균을 구할 수도 있다.

def vector_mean(vectors):
    #i번째 성분이 입력된 모든 벡터의 i번째 성분의 평균을 의미하는 벡터를 계산해준다
    n = len(vectors)
    return scalar_multiply(1/n, vector_sum(vectors))

 

내적은 벡터의 각 성분별 곱한 값을 모두 더해준 값이다.

def dot(v,w):
    #v_1*w_1+...+v_n*w_n
    return sum(v_i*w_i
              for v_i, w_i in zip(v,w))

내적은 벡터 v가 벡터 w방향으로 얼마나 멀리 뻗어 나가는지를 나타낸다. 예를 들어, w=[1,0]이면 dot(v,w)는 v의 첫번째 성분이다. 내적은 v가 w로 투영된 벡터의 길이를 나타낸다.

 

내적의 개념을 사용하면, 각 성분의 제곱 값의 합을 쉽게 구할 수 있다.

def sum_of_squares(v):
    #v_1*v_1+...+v_n*v_n
    return dot(v,v)

 

제곱 값의 합을 이용하면 벡터의 크기를 계산할 수 있다.

import math
def magnitude(v):
    return math.sqrt(sum_of_squares(v)) #math.sqrt는 제곱근을 계산해주는 함수

 

두 벡터 간의 거리는 다음과 같이 정의된다.

 

두 벡터 간의 거리를 계산한다.

import math
def magnitude(v):
    return math.sqrt(sum_of_squares(v)) #math.sqrt는 제곱근을 계산해주는 함수
def squared_distance(v,w):
    #(v_1=w_1)**2+...+(v_n-w_n)**2
    return sum_of_squares(vector_subtract(v,w))
def distance(v,w):
    return math.sqrt(squared_distance(v,w))
def distance(v,w):
    return magnitude(vector_subtract(v,w))

 

  • 벡터를 list로 표현하는 것은 벡터의 원리를 설명하는데 편리하지만 성능은 좋지 않다.
  • 실제로 코딩을 할 때는 성능도 좋고, 다양한 연산히 구현된 numpy 라이브러리를 활용한다.

'Data Science > 선형대수' 카테고리의 다른 글

행렬  (0) 2020.05.27

 

데이터 시각화의 목적

- 데이터 탐색 (exploration)

- 데이터 전달 (communication)

 

matplotlib

- 간단한 막대 그래프, 선 그래프, 산점도를 그릴 때 유용

- 복잡하고 인터랙티브한 시각화를 만들기에는 적합하지 않다.

pyplot

- 시각화를 단계별로 간편하게 만들 수 있는 구조

- savefig() : 그래프 저장

- show() : 화면에 띄울 수 있다.

 

ex)

from matplotlib import pyplot as plt
years = [1950, 1960, 1970, 1980, 1990, 2000, 2010]
gdp = [300.2, 543.4, 1075.9, 2862.5, 5979.6, 10289.7, 14958.3]

#x축에 연도, y축에 GDP가 있는 선 그래프를 만든다.
plt.plot(years, gdp, color='green', marker='o', linestyle='solid')

#제목 추가
plt.title("Nominal GDP")

#y축에 레이블 추가
plt.ylabel("Billions of $")
plt.show()

※ matplotlib를 사용해서 그래프 속에 그래프를 그리거나, 복잡한 구조의 그래프를 만들거나, 인터랙티브한 시각화를 만들 수 있다.

 

막대 그래프

- 막대 그래프(bar charts)는 이산적인 항목들에 대한 변화를 보여 줄 때 사용하면 좋다.

ex)

from matplotlib import pyplot as plt
movies = ["Annie Hall", "Ben-Hur", "Casablanca", "Gandhi", "West Side Story"]
num_oscars = [5,11,3,8,10]

#막대 너비의 기본값이 0.8이므로, 막대가 가운데로 올 수 있도록 왼쪽 좌표에 0.1씩 더한다.
xs = [i+0.1 for i, _ in enumerate(movies)]

#왼편으로부터 x축이 위치가 xs이고, 높이가 num_oscars인 막대를 그린다.
plt.bar(xs, num_oscars)
plt.ylabel("# of Academy Awards")
plt.title("My Favorite Movies")

#막대의 가운데에 오도록 영화 제목 레이블을 단다
plt.xticks(xs, movies)
plt.show()

 

- 히스토그램이란 정해진 구간에 해당되는 항목의 갯수를 보여줌으로써 값의 분포를 관찰할 수 있는 그래프의 형태

ex)

from matplotlib import pyplot as plt
from collections import Counter
grades = [83, 95, 91, 87, 70, 0, 85, 82, 100, 67, 73, 77, 0]
decile = lambda grade:grade // 10*10
histogram = Counter(decile(grade) for grade in grades)
plt.bar([x for x in histogram.keys()], #각 막대를 옮기려면 x옆에 숫자를 붙인다
       histogram.values(),             #각 막대의 높이를 정해준다(grades)
       8)                              #높이는 8로 한다
plt.axis([-5, 105, 0, 5])              #x축은 -5부터 105, y축은 0부터 5
plt.xticks([10*i for i in range(11)])  #x축의 레이블은 0, 10, 20, ..., 100
plt.xlabel("Decile")
plt.ylabel("# of Students")
plt.title("Distribution of Exam 1 Grades")
plt.show()

1) plt.bar의 세번째 인자는 막대의 너비를 정한다. 여기서는 각 구간의 너비가 10이므로, 8로 정해서 막대 간에 공간이 생기게 했다.

2) 만약 막대들의 중점이 맞지 않는다면, -(왼쪽), +(오른쪽)으로 이동해서 중점으로 만들어준다.

3) plt.axis는 x축의 범위를 -5에서 105로 한다. 0, 100에 해당하는 막대가 잘리지 않도록 그리기 위해서이다. 또한 y축의 범위를 0부터 5로 정한다.

4) plt.xticks는 x축의 레이블이 0, 10, 20, ..., 100이 되게 했다.

 

- plt.axis를 사용할 때는 신중해야 하는데, y축이 0에서 시작하지 않으면 오해를 불러일으키기 쉽기 때문이다.

from matplotlib import pyplot as plt
mentions = [500, 505]
years = [2013, 2014]
plt.bar([2013, 2014], mentions, 0.8)
plt.xticks(years)
plt.ylabel("# of times I ehard someone say 'data science'")

#이렇게 하지 않으면 matplotlib이 x축에 0,1레이블을 달고 주변부에 +2.013e3라고 표기할 것이다
plt.ticklabel_format(useOffset=False)

#오해를 불러일으키는 y축은 500 이상의 부분만 보여줄 것이다
plt.axis([2012.5, 2014.5, 499, 506])
plt.title("Look at the 'Huge' Increase!")
plt.show()

- 아래의 그래프는 위의 그래프보다 더 적합한 축을 사용했고, 더 합리적인 그래프가 되었다.

from matplotlib import pyplot as plt
mentions = [500, 505]
years = [2013, 2014]
plt.bar([2013, 2014], mentions, 0.8)
plt.xticks(years)
plt.ylabel("# of times I ehard someone say 'data science'")
plt.ticklabel_format(useOffset=False)
plt.axis([2012.5, 2014.5, 490, 550])
plt.title("Not So Huge Anymore")
plt.show()

 

선 그래프

- plt.plot()을 이용하면 선 그래프를 그릴 수 있다.

- 어떤 경향을 보여줄 때 유용하다.

from matplotlib import pyplot as plt
variance = [1,2,4,8,16,32,64,128,256]
bias_squared = [256,128,64,32,16,8,4,2,1]
total_error = [x+y for x, y in zip(variance, bias_squared)]
xs = [i for i, _ in enumerate(variance)]

#한 차트에 여러 개의 시리즈를 그리기 위해 plt.plot을 여러번 호출할 수 있다.
plt.plot(xs, variance, 'g-', label='variance')       #초록색 실선
plt.plot(xs, bias_squared, 'r-', label='bias^2')     #붉은색 점선
plt.plot(xs, total_error, 'b:', label='total error') #파란색 점선

#각 시리즈에 label을 미리 달아놨기 때문에 범례(legend)를 그릴 수 있다.
#loc=9는 위쪽 중앙을 의미한다
plt.legend(loc=9)
plt.xlabel("model complexity")
plt.title("The Bias-Variance Tradeoff")
plt.show()

 

산점도

- 두 변수 간의 연관관계를 보여주고 싶을 때 적합하다.

from matplotlib import pyplot as plt
friends = [70,65,72,63,71,64,60,64,67]
minutes = [175,170,205,120,220,130,105,145,190]
labels = ['a','b','c','d','e','f','g','h','i']
plt.scatter(friends, minutes)

#각 포인트에 레이블을 단다
for label,friend_count,minute_count in zip(labels,friends,minutes):
    plt.annotate(label,
                xy=(friend_count,minute_count), #label을 데이터 포인트 근처에 둔다
                xytext=(5,-5),                  #약건 떨어져 있게 한다
                textcoords='offset points')
plt.title("Daily Minutes vs. Number of Friends")
plt.xlabel("# of friends")
plt.ylabel("daily minutes spent on the site")
plt.show()

※ 위 그래프는 각 사용자의 친구 수와 그들이 매일 사이트에서 체류하는 시간 사이의 연관성을 보여준다.

 

- 변수들끼리 비교할 때 matplotlib이 자동으로 축의 범위를 설정하게 하면 아래의 그래프같이 공정한 비교를 하지 못할 수 있다.

from matplotlib import pyplot as plt
test_1_grades = [99,90,85,97,80]
test_2_grades = [100,85,60,90,70]
plt.scatter(test_1_grades,test_2_grades)
plt.title("Axes Aren't Comparable")
plt.xlabel("test 1 grade")
plt.ylabel("test 2 grade")
plt.show()

- plt.axis("equal") 명령을 추가하면 아래의 그래프처럼 공정한 비교를 할 수 있게 된다.

from matplotlib import pyplot as plt
test_1_grades = [99,90,85,97,80]
test_2_grades = [100,85,60,90,70]
plt.scatter(test_1_grades,test_2_grades)
plt.axis("equal")
plt.title("Axes Aren't Comparable")
plt.xlabel("test 1 grade")
plt.ylabel("test 2 grade")
plt.show()

※ 위 그래프를 보면 test2에서 대부분의 편차가 발생했다는 사실을 알 수 있다.

test1의 범위(80~97.5), test2의 범위(60~100)

+ Recent posts