관리 메뉴

HAMA 블로그

파이썬 코딩으로 말하는 데이터 분석 - 5. 데이터 다루기 (기본,척도조절,차원축소) 본문

통계 & 머신러닝 & 딥러닝

파이썬 코딩으로 말하는 데이터 분석 - 5. 데이터 다루기 (기본,척도조절,차원축소)

[하마] 이승현 (wowlsh93@gmail.com) 2017. 1. 22. 13:04



순서 

1. 통계 - 카운팅,min,max,평균,중앙값,산포도,분산,편차,공분산,상관관계 

2. 가설과 추론 (베이지언 - 사후확률,우도) 

3. 군집화 (K-Means)

4. 연관 (Apriori)

5. 함수형으로 데이터 다루기 

6. 경사하강법

7. 회귀분석

8. 은닉 마코프법 (HMM) 

9. k-NN

10. DTW 

 * 참고로 "밑바닥부터 배우는 데이터과학" 서적은 numpy,scikit-learn 등의 외부라이브러리를 활용은 배제되었습니다.




데이터 다루기


대부분의 언어들이 함수형을 차용하고 있으며 , 파이썬도 빠질리는 없다. 파이썬의 함수형 도구들을 사용하여 데이 터 분석을 해보자.

참고: 

파이썬, C++ ,Java 8은 모두 기본적으로는 객체지향언어에 가깝지만 모두 함수형 파라다임을 차용하고 있다. 스칼라 같은 언어는 함수형 하이브리드언어라고 볼 수 있으며, 클로저는 거의 함수형언어라고 본다. 함수형언어는 불변(부수효과가 없는)을 지향하고 있다. 이 말인 즉 객체지향이 어떠한 객체를 상정해두고 그 객체안의 메소드를 이용하고 속성들을 변화시키면서 진행되는 반면 함수형 언어는 여러 순수함수들을 통과하면서 목적을 이루기위해 진행된다. 순수함수는 항상 x 를 대입하면 y 를 내뱉는것을 확신시켜준다. 


주요 도구: List comprehension 

함수형 (고차함수) 의 특징인 lambda , filter, map, reduce 알고리즘을 리스트로 간단히 구현하는 방법입니다.

예를 들면  

a = [1,2,3,4,5] 중에서 홀수만 골라낸다고 할때 

람다식을 이용하면 아래와 같이 됩니다.

filter (lambda x : x % 2 , a)   

근데 이것을 List comprehension 으로 표현하면

 [x for x in a if x % 2] 

이렇게 됩니다. 즉 a 라는 컬렉션에서 하나씩 값(x) 을 가져와서 , 만약 짝수면 리스트의 요소로 추가합니다.

파이썬에서는 좀 황당한게 List comprehension 이 더 사용성 (가독성) 이 좋아서 오래전 부터 고차함수들을 제꼈(?)습니다. @@


collection.Counter 란? 

c = Counter("문재인","문재인","안희정","안희정","안철수","이재명","이재명","이재명","이재명") 

c["문재인"] 은 2 가 되며 , c["안철수"] 는 1 , c["이재명"] 는 4  이 되는 

결국 키는 요소가 되며 value 는 그 요소의 갯수를 자동으로 누적해서 가지고 있다.

c.most_common(2) 를 통해 가장 많이 누적된 상위 2개를 알 수 도 있는 굉장히 편리한 컬렉션 클래스이다




코드로 말해요. 


@ 아래와 같은 주식 데이터가 있다고 하자.  / 종가 / 날짜 / 심볼로 이루어져있다.


data = [ {'closing_price' : 102.06,

'date' : datetime.datetime(2014, 8, 29, 0, 0),

'symbol' : 'AAPL'}, ...

...

* 파일에 저장된 모습 (아래 다운로드 받을 수 있다)

symbol date closing_price
AAPL 2015-01-23 112.98
AAPL 2015-01-22 112.4
AAPL 2015-01-21 109.55
AAPL 2015-01-20 108.72
AAPL 2015-01-16 105.99
AAPL 2015-01-15 106.82
AAPL 2015-01-14 109.8
..

stocks.txt


@ 심볼이 'AAPL' 인 것들 중에서  가장 높은 가격 산출

max_aapl_price = max(row["closing_price"]
for row in data if row["symbol"] == "AAPL")
print "max aapl price", max_aapl_price

data 컬렉션에서 값(row) 을 하나 가져와서  심볼이 "AAPL" 인것들 중에서 값이 가장 높은것


@ 심볼별로 데이터 그룹화

# group rows by symbol
by_symbol = defaultdict(list)

for row in data:
by_symbol[row["symbol"]].append(row)

심볼이 같은것 끼리 묶어 줍니다. 즉 { "AAPL" : [ ... , ... ]  , "BBPL" : [ ..., ... ]  .......} 이런식으로요~

defaultdict 은 파이썬에서 제공하는 컬렉션 중에 하나로써 , 만약 키 값이 없더라도 새로 생성해서 append 하게 해 줍니다. 일반 딕셔너리로 저렇게 코딩하면 에러죠. 



@ 그룹별 가장 높은 가격 

max_price_by_symbol = {symbol: max(row["closing_price"] for row in grouped_rows)
for symbol, grouped_rows in by_symbol.iteritems()}

위에서 심볼별 리스트들을 가져와서 각각에 대한 가장 높은 값을 구해줍니다.

결과는 {'AAPL': 119.0, 'FB': 38.23, 'MSFT': 22.78}


@ 특정필드를 리스트로 추출  

def picker(field_name):
return lambda row: row[field_name]

def pluck(field_name, rows):
return map(picker(field_name), rows)

picker 는 특정 필드의 값을 리턴해주는 함수를 반환합니다.

pluck 는 rows 들 중 특정 필드의 값 만으로 리스트를 만들어 줍니다.  

즉 field_name 에 "closing_price" 를 입력해주면 , 그 값들로만 이루어진 리스트를 리턴함. 


@ 데이터를 그룹화시켜서 모으고 , value_transform 공식에 의해서 변경시킨다. 


def group_by(grouper, rows, value_transform=None):
grouped = defaultdict(list)
for row in rows:
grouped[grouper(row)].append(row)
if value_transform is None:
return grouped
else:
return {key: value_transform(rows)
for key, rows in grouped.iteritems()}

심볼이라든지 어떤 특정 것을 구분하여 그룹화시키고 , 각 그룹을 value_transform 에 의해 계산합니다.
value_transform  가 그룹의 max 값을 찾는 것이라면 {"AAPL" : 1030 , "BBPL" : 2000 ..} 뭐 이런식으로 결과가 나오겠지요.


@ 그룹된 로우를 date 에 의해 정렬한 후에 이전날과 비교한 오늘의 종가의 증감비율을 추가한다.  

위에 언급한 value_tansform 로써 사용된다. 


def percent_price_change(yesterday, today):
return today["closing_price"] / yesterday["closing_price"] - 1

def day_over_day_changes(grouped_rows):
# sort the rows by date
ordered = sorted(grouped_rows, key=picker("date"))
# zip with an offset to get pairs of consecutive days
return [{"symbol": today["symbol"],
"date": today["date"],
"change": percent_price_change(yesterday, today)}
for yesterday, today in zip(ordered, ordered[1:])]


@ 데이터를 "심볼" 별로 그룹핑하고, 이전날과 비교한 오늘의 종가의 증감비율을 계산하 day_over_say_changes 를  value_tansform 로 넘겨준다. change 키에 그 값들을 넣어준다.

changes_by_symbol = group_by(picker("symbol"), data, day_over_day_changes)


@ 모든 그룹들의  "Change"  의 값들중 max,min 구해서 하나의 거대한 리스트에  담는다.

all_changes = [change
for changes in changes_by_symbol.values()
for change in changes]

print "max change", max(all_changes, key=picker("change"))
print "min change", min(all_changes, key=picker("change"))



척도 조절

x 축과 y 축으로 사용할 데이터들 간에 척도가 다를 경우 데이터 분석에 차질이 생길 수 있다. 
이 경우 평균을 0 으로 만들고, 표준편차를 최대 1로 만들면, 비교하기 편해질 것이다.
data = [[1, 20, 2],
[1, 30, 3],
[1, 40, 4]]

def shape(A):
num_rows = len(A)
num_cols = len(A[0]) if A else 0
return num_rows, num_cols

def scale(data_matrix):
num_rows, num_cols = shape(data_matrix)
means = [mean(get_column(data_matrix,j))
for j in range(num_cols)]
stdevs = [standard_deviation(get_column(data_matrix,j))
for j in range(num_cols)]
return means, stdevs

def rescale(data_matrix):
means, stdevs = scale(data_matrix)

def rescaled(i, j):
if stdevs[j] > 0:
return (data_matrix[i][j] - means[j]) / stdevs[j]
else:
return data_matrix[i][j]

num_rows, num_cols = shape(data_matrix)
return make_matrix(num_rows, num_cols, rescaled)

original:  [[1, 20, 2], [1, 30, 3], [1, 40, 4]]
scale:  ([1.0, 30.0, 3.0], [0.0, 10.0, 1.0])
rescaled:  [[1, -1.0, -1.0], [1, 0.0, 0.0], [1, 1.0, 1.0]]

차원 축소 및 PCA 


X = [
[20.9666776351559, -13.1138080189357],
[22.7719907680008, -19.8890894944696],
[25.6687103160153, -11.9956004517219],
[18.0019794950564, -18.1989191165133],
....

[15.6563953929008, -17.2196961218915],
[25.2049825789225, -14.1592086208169]
]

def shape(A):
num_rows = len(A)
num_cols = len(A[0]) if A else 0
return num_rows, num_cols

def magnitude(v):
return math.sqrt(sum_of_squares(v))

def de_mean_matrix(A):
    """A 행렬의 모든 값에 해당 컬럼의 평균을 뺀 행렬을 리턴합니다"
    nr, nc = shape(A)
    column_means, _ = scale(A)
     return make_matrix(nr, nc, lambda i, j: A[i][j] - column_means[j])

def negate(f):
"""-f(x) 로 리턴한다. """
return lambda *args, **kwargs: -f(*args, **kwargs)


def negate_all(f):
"""모든 리턴을 -y 로 한다."""
return lambda *args, **kwargs: [-y for y in f(*args, **kwargs)

# 8.4. 적절한 스텝 크기 정하기

def safe(f):
"""f 를 감싸고 it 를 리턴하는 새 함수를 정의한다."""

def safe_f(*args, **kwargs):
try:
return f(*args, **kwargs)
except:
return float('inf') # this means "infinity" in Python
return safe_f

def minimize_batch(target_fn, gradient_fn, theta_0, tolerance=0.000001):
""" 경사하강법을 이용하여 타겟 함수를 최소화할 세타를 찾는다."""

step_sizes = [100, 10, 1, 0.1, 0.01, 0.001, 0.0001, 0.00001]

theta = theta_0 # 세타 초기값
target_fn = safe(target_fn) # target_fn 의 safe 버전
value = target_fn(theta) # 최소화할 값

while True:
gradient = gradient_fn(theta)
next_thetas = [step(theta, gradient, -step_size)
for step_size in step_sizes]

# choose the one that minimizes the error function
next_theta = min(next_thetas, key=target_fn)
next_value = target_fn(next_theta)

# stop if we're "converging"
if abs(value - next_value) < tolerance:
return theta
else:
theta, value = next_theta, next_value

def maximize_batch(target_fn, gradient_fn, theta_0, tolerance=0.000001):
return minimize_batch(negate(target_fn),
negate_all(gradient_fn),
theta_0,
tolerance)

# partial 은 functools 에서 제공하는 함수로 # https://www.pydanny.com/python-partials-are-fun.html 참고

def first_principal_component(X):
guess = [1 for _ in X[0]]
unscaled_maximizer = maximize_batch(
partial(directional_variance, X), # is now a function of w
partial(directional_variance_gradient, X), # is now a function of w
guess)
return direction(unscaled_maximizer)

def principal_component_analysis(X, num_components):
components = []
for _ in range(num_components):
component = first_principal_component(X)
components.append(component)
X = remove_projection(X
, component)

return components

Y = de_mean_matrix(X)
components = principal_component_analysis(Y
, 2)
print "principal components", components
print "first point", Y[0]
print "first point transformed", transform_vector(Y[0], components)

PCA 결과

[[0.9238554090431896, 0.382741666377781], [-0.3827224539579983, 0.9238633682728025]]

[0.6663708720254604, 1.6869418499129427]

[1.2612932692676448, 1.3034686841532082]


전체 소스는 아래에 있다.

https://github.com/insightbook/Data-Science-from-Scratch/blob/master/code/ch10_working_with_data.py





레퍼런스:

밑바닥부터 배우는 데이터과학

Comments