코드 출처: http://scikit-learn.org/stable/auto_examples/tree/plot_iris.html#example-tree-plot-iris-py 

 저번 포스팅이 data를 tree를 생성하기에 적합하게 데이터를 load하는 것에 관한 것이었다면 이번 포스팅은 tree를 생성하고 그것을plotting 하는 것까지의 내용을 담고 있다. 또한 그 과정에서 나타나는 다양한 python 사용법에 대한 설명도 함께 정리한다.

######################################### print(__doc__)

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier

# Parameters
n_classes = 3
plot_colors = "bry"
plot_step = 0.02
# Load data
iris = load_iris()
#########################################

Decision tree를 생성하기 전에, data를 불러오고 그래프를 그리기 위한 설정을 하는 부분이다. plot_colors = "bry"는 코드 뒷부분에 'blue', 'red', 'yellow' 라는 색을 나타내기 위해 쓰이며 plot_step은 축의 단위를 설정해주는 부분이다.

#########################################
for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3],
[1, 2], [1, 3], [2, 3]]):
# We only take the two corresponding features
X = iris.data[:, pair]
y = iris.target
#########################################

 enumerate는 입력값으로 시퀀스자료형(리스트, 터플, 문자열)을 입력으로 받아 enumerate객체를 리턴한다. enumerate객체는 첫번째로 그 순서값, 두번째로 그 순서값에 해당되는 시퀀스 자료형의 실제값을 갖는 객체이다. (출처: https://wikidocs.net/32) iris의 feature dimension은 4이다. 4개의 feature를 2개씩 짝지어 비교하여(4C2) 그래프를 그리고 그 그래프들을 한 화면에 나타나기 위해 enumerate 함수를 사용했다. 
 X = iris.data[:, pair]의 경우 하나의 pair에 들어가는 값이 만약 [0, 2] 라면 첫 번째, 세 번째 feature만 선택해서 X에 할당하겠다는 의미이다.

#########################################     # Shuffle
idx = np.arange(X.shape[0]) np.random.seed(13) np.random.shuffle(idx) X = X[idx] y = y[idx]

# Standardize
mean = X.mean(axis=0)
std = X.std(axis=0)
X = (X - mean) / std
    # Train
clf = DecisionTreeClassifier().fit(X, y)
#########################################

현재 X(Data), Y(Target)은 class 순서대로 정렬이 되어 있으므로 shuffle을 해준다. 사실 shuffle을 하나 하지 않으나 plotting 결과는 동일하다. 꽃받침과 꽃잎은 길이의 scale 차이가 있으므로 표준화 작업을 해준다. DecisionTreeClassifier는 코드 윗 부분에서 이미 import해서 바로 쓸수 있고 fit에는 (Data, Target) 형식으로 입력을 해주면 Decision Tree를 return 한다.

#########################################
# Plot the decision boundary
plt.subplot(2, 3, pairidx + 1)

x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
np.arange(y_min, y_max, plot_step))

Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.Paired)
#########################################

plt.subplot(행, 열, index) 로 지정해주면 해당 index 위치에 plot이 생성된다. x_min, x_max 부분은 plot의 처음과 끝 값을 지정해주기 위한 부분이다. np.meshgrid(x(1d_array), y(1d_array) 함수는 두 개의 2D matrix들을 return한다. xx행렬은 x가 y의 dimension 크기의 행의 개수 만큼 반복되어 있고 yy행렬은 y가 x의 dimension 크기의 열의 개수 만큼 반복 되어 있다. xx.ravel() 함수는 행렬의 1행부터 순차적으로 원소 값을 불러와서 1차원 array를 만드는 함수이다. Reshape 함수를 사용하여 Z를 xx와 같은 차원으로 만들어 준다. clf에는 위에서 형성한 decision tree가 할당되어 있다. predict 함수를 쓰면 새로운 인풋에 대해 그 인풋이 갖는 class가 어떤 값인지 return한다. contourf 는 xx, yy가 각각 x축, y축 좌표에 해당하며 Z에 해당하는 값을 cmap에서 지정해준 색으로 mapping해주는 함수이다. 이 경우 xx, yy가 매우 촘촘하게 되어 있어서 색칠을 하는 역할을 한다. (그런데 왜 2D matrix가input이 되는 지는 좀 더 살펴 봐야 할 거 같다.)

#########################################
plt.xlabel(iris.feature_names[pair[0]])
plt.ylabel(iris.feature_names[pair[1]])
plt.axis("tight")

    # Plot the training points
for i, color in zip(range(n_classes), plot_colors):
idx = np.where(y == i)
plt.scatter(X[idx, 0], X[idx, 1], c=color, label=iris.target_names[i],
cmap=plt.cm.Paired)

plt.axis("tight")

plt.suptitle("Decision surface of a decision tree using paired features")
plt.legend()
plt.show()
#########################################

plt.axis("tight")는 어떤 기능인지 좀 더 조사를 해봐야 한다. plt.scatter는 위에서 색칠한 영역에 training data를 표시해준다. 위의 코드를 전부 실행하면 다음과 같은 결과를 얻는다.



Decision Tree로 classification을 수행하기 위해서는 Tree에 data를 preprocessing하여 전달해야 한다. 아래 그림은 http://scikit-learn.org/stable/modules/tree.html page에서 예시로 들고 있는 decision tree의 예이다. 이 post에서는 load_iris() 함수가 어떤식으로 tree에 data를 전달하는지 살펴보려고 한다.

>>> from sklearn.datasets import load_iris
>>> from sklearn import tree
>>> iris = load_iris()
>>> clf = tree.DecisionTreeClassifier()
>>> clf = clf.fit(iris.data, iris.target)

def load_iris():

"""Load and return the iris dataset (classification).
The iris dataset is a classic and very easy multi-class classification
dataset.
===============================
Classes 3
Samples per class 50
Samples total 150
Dimensionality 4
Features real, positive
===============================
Returns
-------
data : Bunch
Dictionary-like object, the interesting attributes are:
'data', the data to learn, 'target', the classification labels,
'target_names', the meaning of the labels, 'feature_names', the
meaning of the features, and 'DESCR', the
full description of the dataset.

Examples
--------
Let's say you are interested in the samples 10, 25, and 50, and want to
know their class name.
>>> from sklearn.datasets import load_iris
>>> data = load_iris()
>>> data.target[[10, 25, 50]]
array([0, 0, 1])
>>> list(data.target_names)
['setosa', 'versicolor', 'virginica']
"""
module_path = dirname(__file__)
with open(join(module_path, 'data', 'iris.csv')) as csv_file:
data_file = csv.reader(csv_file)
temp = next(data_file) n_samples = int(temp[0])         n_features = int(temp[1]) target_names = np.array(temp[2:]) data = np.empty((n_samples, n_features)) target = np.empty((n_samples,), dtype=np.int)

#########################################

With와 as 구문을 사용하여 .csv 파일을 여는 구문이다. join 함수는 나중에 다루기로 하자. 여기서 next는 읽어온 파일의 첫 번째 줄을 읽어오는 기능을 하는 함수이다. 위의 코드를 보면 n_samples, n_features에 첫 줄의 첫 번째, 두 번째 값을 저장하고 있다. 참고로 iris.csv의 첫 번째 줄은 150,4,setosa,versicolor,virginica 이다. Sample의 갯수 150개, feature의 dimension은 4, 그리고 class들의 이름이 나타나있다. numpy (as np)를 이용하여 data[150 X 4], target[150] 배열을 만든다.

#########################################

for i, ir in enumerate(data_file):

data[i] = np.asarray(ir[:-1], dtype=np.float)
target[i] = np.asarray(ir[-1], dtype=np.int)

with open(join(module_path, 'descr', 'iris.rst')) as rst_file:
fdescr = rst_file.read()

return Bunch(data=data, target=target,
target_names=target_names,
DESCR=fdescr,
feature_names=['sepal length (cm)', 'sepal width (cm)',
'petal length (cm)', 'petal width (cm)'])

#########################################

enumerate를 이용해서 data, target array에 값을 넣어준다. data_file의 경우 위에서 next(data_file) 처리를 했기 때문에 가장 첫 행은 이 반복문에 포함되지 않는다. 마지막에는 Bunch 형태로 return 한다.


The submodules often need to refer to each other. For example, the surround module might use the echo module. In fact, such references are so common that the import statement first looks in the containing package before looking in the standard module search path. Thus, the surround module can simply use import echo or from echo import echofilter. If the imported module is not found in the current package (the package of which the current module is a submodule), the import statement looks for a top-level module with the given name.

When packages are structured into subpackages (as with the sound package in the example), you can use absolute imports to refer to submodules of siblings packages. For example, if the module sound.filters.vocoderneeds to use the echo module in the sound.effects package, it can use from sound.effects import echo.

Starting with Python 2.5, in addition to the implicit relative imports described above, you can write explicit relative imports with the from module import name form of import statement. These explicit relative imports use leading dots to indicate the current and parent packages involved in the relative import. From the surround module for example, you might use:

from . import echo
from .. import formats
from ..filters import equalizer

Note that both explicit and implicit relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application should always use absolute imports.


출처: https://docs.python.org/2/tutorial/modules.html


1. Series

Series는 다음과 같은 방식으로 생성할 수 있다.

In [20]: sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}

In [21]: obj3 = Series(sdata)

In [22]: obj3

Out[22]:

Ohio 35000

Oregon 16000

Texas 71000

Utah 50000

아래와 같이 index를 따로 지정할 수 있으며 index에 없넌 Utah는 나타나지 않으며 index에는 있지만 dictionary에 없는 California는 값을 갖지 않은체로 표시 된다. 

In [23]: states = ['California', 'Ohio', 'Oregon', 'Texas']

In [24]: obj4 = Series(sdata, index=states)

In [25]: obj4

Out[25]:

California NaN

Ohio 35000

Oregon 16000

Texas 71000

2. DataFrame

DataFrame은 다음과 같은 방식으로 생성할 수 있다.

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],

'year': [2000, 2001, 2002, 2001, 2002],

'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame = DataFrame(data)

In [38]: frame

Out[38]:

pop state year

0 1.5 Ohio 2000

1 1.7 Ohio 2001

2 3.6 Ohio 2002

3 2.4 Nevada 2001

4 2.9 Nevada 2002

특정 column이나 row에 있는 값들을 호출하고 싶을 땐 다음과 같이 하면 된다.

In [40]: frame2 = DataFrame(data, columns=['year', 'state', 'pop', 'debt'],

....:                                index=['one', 'two', 'three', 'four', 'five'])

In [41]: frame2

Out[41]:

year state pop debt

one 2000 Ohio 1.5 NaN

two 2001 Ohio 1.7 NaN

three 2002 Ohio 3.6 NaN

four 2001 Nevada 2.4 NaN

five 2002 Nevada 2.9 NaN


In [42]: frame2.columns

Out[42]: Index([year, state, pop, debt], dtype=object)


In [43]: frame2['state']                 In [44]: frame2.year

Out[43]:                                     Out[44]:

one Ohio                                     one 2000

two Ohio                                      two 2001

three Ohio                                    three 2002

four Nevada                                 four 2001

five Nevada                                  five 2002

Name: state                                 Name: year


In [45]: frame2.ix['three']

Out[45]:

year 2002

state Ohio

pop 3.6

debt NaN

Name: three

최근에 python study를 하면서 정리도 할겸 포스팅을 하기로 했다. 잊지 않으려면, 나중에 찾아보려면 이렇게 기록으로 남기는것이 최선일 것이다.

1. Basic Indexing and Slicing

In [51]: arr = np.arange(10)

In [52]: arr

Out[52]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [53]: arr[5]

Out[53]: 5

In [54]: arr[5:8]

Out[54]: array([5, 6, 7])

In [55]: arr[5:8] = 12

In [56]: arr

Out[56]: array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])


 python에서 slicing을 할 경우 마지막 index는 수학 표현으로 치면 개구간이다. In[54]의 결과를 보면 8은 결과에 포함되어 있지 않음을 알 수 있다. 

다음은 Numpy에서 array를 call by reference로 처리하고 있다는 것을 보여주는 예제이다.

In [57]: arr_slice = arr[5:8]

In [58]: arr_slice[1] = 12345

In [59]: arr

Out[59]: array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])

 혹여 이런식으로 'view'를 생성해서 원래 값과의 연결됨을 원치 않는다면 arr_slice = arr[5:8].copy() 명령어를 사용하면 된다.

2. Expressing Conditional Logic as Array Operations

numpy.where function은 어떤 조건에 따라 x 혹은 y를 경우게 맞게 빠르게 보여주는 기능을 제공한다. Python에 기본적으로 탑재되어 있는 기능을 사용하면 다음과 같이 구현해야 한다.

In [143]: result = [(x if c else y)

.....: for x, y, c in zip(xarr, yarr, cond)]

In [144]: result

Out[144]: [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]

 이 방법은 large array에 대해서 빠르지 않을 뿐더러 multidimensional arrays에 적합하지 않다. numpy.where 함수를 쓰면 다음과 같이 구현 가능하다.

In [145]: result = np.where(cond, xarr, yarr)

In [146]: result

Out[146]: array([ 1.1, 2.2, 1.3, 1.4, 2.5])

numpy.where는 반드시 array를 필요로 하지 않는다.

In [147]: arr = randn(4, 4)

In [148]: arr

Out[148]:

array([[ 0.6372, 2.2043, 1.7904, 0.0752],

[-1.5926, -1.1536, 0.4413, 0.3483],

[-0.1798, 0.3299, 0.7827, -0.7585],

[ 0.5857, 0.1619, 1.3583, -1.3865]])

In [149]: np.where(arr > 0, 2, -2)

Out[149]:

array([[ 2, 2, 2, 2],

    [-2, -2, 2, 2],

    [-2, 2, 2, -2],

    [ 2, 2, 2, -2]])

이런식으로 where에 자유롭게 input을 입력하여 사용할 수 있다.

3. Mathematical and Statistical Methods

Numpy에서는 sum, mean, std 같은 수학적 연산 함수를 제공한다. 

In [151]: arr = np.random.randn(5, 4) # normally-distributed data

In [152]: arr.mean()

Out[152]: 0.062814911084854597

In [153]: np.mean(arr)

Out[153]: 0.062814911084854597

In [154]: arr.sum()

Out[154]: 1.2562982216970919

mean(), sum()과 같이 method에 인수를 지정하지 않으면 대상 array에 모든 값을 대상으로 수학연산을 행한다. 그런데 method에 인수로 정수값을 할당하면 'axis'를 따라 연산을 행한다. 예를들면 다음과 같다.

In [157]: arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])

In [158]: arr.cumsum(0)                 In [159]: arr.cumprod(1)

Out[158]:                                     Out[159]:

array([[ 0, 1, 2],                           array([[ 0, 0, 0],

   [ 3, 5, 7],                                     [ 3, 12, 60],

   [ 9, 12, 15]])                                [ 6, 42, 336]])

cumsumcumprod는 이름에서 알 수 있듯이 누적해서 합, 곱을 해주는 method이다. 여기서 좀 헷갈렸던 점이 'axis'를 따라 연산을 한다는 점이었다. 보통 2d-array를 취급할 때 앞에 오는 숫자가 대부분 행, 뒤 숫자가 열을 의미하기에 결과를 보기 전에 cumsum(0)을 입력하면 [0,1,3],[3,7,12] 이런식으로 출력이 될거라고 예상했었다. 그런데 'axis'를 따라서 연산한다는 의미를 잘 생각해보면 x축, y축 처럼 그 축위를 움직이면서 그 위치에 해당하는 값들에 대해 연산해 준다는 것이었다. 설명은 명확히 쓰여 있었으나 그 동안 해오던 것과 달라서 헷갈렸던 부분이었다.

최근에 "Python for Data Analysis" 라는 책을 이용하여 python study를 시작하게 되었다. 이 책에서 가장 먼저 공부하기로 한 부분은 Chapter 4. NumPy Basics: Arrays and Vectorized Computation이다. 이 Chapter의 내용을 실습해보려면 numpy를 설치해야 하는데 이 부분에서 자꾸 에러가 발생하였다. 내가 사용한 명령어는 다음과 같다.

python -m pip install numpy

 Powershell에서 위의 명령어를 입력하면 뭔가 설치되는 듯 하다가 에러가 발생하였다. 에러 내용은 ~~를 찾을 수 없다.. 뭐 이런식이었다. 언제나 그렇듯 막힌 부분을 해결하기 위해 구글링을 하다보니 많은 사람들이 나와 같은 문제를 겪어왔다는 것을 확인할 수 있었다. 내가 numpy 설치를 시도한 노트북과 내 연구실의 PC모두 64비트 컴퓨터여서 에러가 발생하는 것이었다. 검색을 하다가 http://goo.gl/HG1Q1o 이 블로그에서 64비트 컴퓨터에 numpy를 설치하는 방법을 알게되었다. 

 그러나 이 때, 연구실 친구가 winPython을 써보라는 조언을 해주었다. winPython을 설치하면 유용한 package들이 한꺼번에 몽땅 설치된다는 것이었다. 그래서 구글에서 winpython을 입력하여 홈페이지에 접속해 보았더니 이 페이지에서는 친절하게 64비트용 유저를 위한 버전도 구별해서 설치파일을 올려두고 있었다.

 설치 파일을 다운 받고 본격적으로 책에 있는 내용을 실습해보려고 하니 아직도 numpy를 찾을 수 없다고 한다. 문제는 환경변수였다. winPython을 설치했지만 지금 내 PC에서 인식하고 있는 python은 얼마 전에 설치한 'C:\Python' 에 있는 파일들이었다. 서 이 파일들을 지우고 winPython이 설치된 폴더에서 python 실행파일이 있는 하위 폴더를 환경 변수에 추가해주고 Scripts 폴더도 추가해주니 powershell에서 아주 훌륭하게 작동하였다.

winPython을 설치하고 나면 설치되는 package들 (더 있지만 그림 크기상 담지 못하였음)

 위와 같이 package들을 설치해준다. 또한 이 책에서 실습용으로 사용하는 IDE인 iPython도 제공한다. winPython을 설치한 뒤에 shell에서 ipython을 입력하면 된다. winPython 정말 유용하다.


요즘 'Learn python the hard way'를 통해서 python을 공부하던 중 46번째 exercise에서 막혔다.

이 과정에서 1번부터 어떻게 하는지 몰라서 다음 과정을 진행하지 못했었다. 1번의 경우 아직 어떤 package도 설치되어 있지 않은 상태이기 때문에 저 링크를 통해 들어간 웹사이트에서 get-pip.py라는 파일을 직접 다운로드를 받아서 실행해야 되는 것이었다. get-pip.py를 실행하고 나서 환경 변수 설정을 마치면 설치가 완료된다. 환경 변수는 두 가지 방법으로 설정할 수 있다.

1. 시스템 > 고급 시스템 설정 > 환경 변수 > Path 에 도달한 뒤 편집을 하는 방법

경로 뒤에 ;을 붙이고 파이썬 설치 경로에 \Scripts 을 붙이고 추가해주면 된다.

2. 명령어로 직접 설정하는  

[Environment]::SetEnvironmentVariable("Path","$env:Path;C:\Python27\Scripts", "User") 을 shell에서 직접 입력하면 된다.

위와 같이 환경 변수를 설정해야 하는 이유는 pip 파일이 없는 곳에서도 pip를 사용할 수 있게 하기 위함이다.

이렇게 pip를 설치하면 2, 3, 4번에 나와있는 'distribute', 'nose', virtualenv' package들은 다음과 같은 명령어로 쉽게 설치 할 수 있다.

python -m pip install 'SomePackage'

모든 package의 설치를 마치고나서 exercise46의 지시사항데로 디렉토리와 파일을 작성하고 test를 해보았으나 또 에러가 발생하였다.

이 에러와 관련해서 검색을 많이 해봤지만 대부분의 답변이 package들을 제대로 설치하라는 것이었다. 내 경우는 이미 설치는 제대로 되어 있어서 원인이 무엇일까 생각해 본 결과 '권한의 문제가 아닐까'라는 생각을 하게 되었다. 리눅스의 경우는 실행하려는 명령어 앞에서 'sudo'를 붙이면 관리자 권한을 실행이 되는데 windows의 powershell에서는 이것을 가능하게 하는 방법을 찾던 중 다음과 같은 두 가지 방법을 알아냈다.

1. Powershell을 우클릭한 후 관리자 권한으로 실행

2. Powershell을 실행하고  Start-Process powershell -Verb runAs를 입력

위 두 방법 중 한 가지를 택해서 powershell을 실행하고 테스트를 한 결과는 다음과 같다.

learn python 홈페이지에 나와있는 결과물을 그대로 확인 할 수 있다.


+ Recent posts