2015년 8월 29일 토요일

대상을 새로운 차원으로 사상 시키는 SVM 비선형 분류 기법

1편 "대상을 바라보는 시선과 정량적 대상 비교"에서 우리의 시선과 다른 차원을 받아들이는 연습이 되었다면, 차원을 응용하는 다음 예제를 보자.

참고 : https://ko.wikipedia.org/wiki/서포트_벡터_머신

1. 2차원의 purple/green으로 분류된 학습 데이터를 SVM 비선형 분류 기법을 통해 학습 모델을 만든다.
* 커널(Kernel)은 RBF 방사형 함수를 사용하고, 새로운 차원으로 사상될 값의 크기를 시각적으로 표현하기 위해 확률값을 출력하도록 옵션을 주었다.
2. 커널함수의 실체를 시각화 하기 위해 그리드(Grid)한 테스트 데이터를 만들어, 실제 사상되는 값을 통해 3D 화면을 그린다.
* 커널(Kernel)은 새로운 차원 공간으로의 매핑 방법을 뜻한다.  OS가 커널을 통해서 S/W가 H/W라는 다른 차원을 조절하는 것도 같은 의미로 보인다.

참조 라이브러리 로딩
In [2]:
import pandas as pd
from sklearn import svm
from mpl_toolkits.mplot3d import Axes3D
학습(트레이닝) 데이터 생성
In [3]:
X1 = pd.DataFrame([(1,1),(1,-1),(-1,1),(-1,-1)],columns=['x','y'])
X1['c'] = 'purple'
X2 = pd.DataFrame([(2,2),(2,-2),(-2,2),(-2,-2)],columns=['x','y'])
X2['c'] = 'green'
df1 = pd.concat([X1,X2])
2D 플롯을 통해서 시각화
In [4]:
scatter(x=df1['x'],y=df1['y'],color=df1['c'],s=100)
axhline(y=0,xmin=-3,xmax=3,color='black')
axvline(x=0,ymin=-3,ymax=3,color='black')
text(x=3,y=0,s=' X Axis ')
text(x=0,y=3,s=' Y Axis ')
axis('off')
Out[4]:
(-3.0, 3.0, -3.0, 3.0)

학습 데이터를 서포트 벡터 머신(SVM)을 통해서 분류(Classification) 모델 생성
In [5]:
# 커널 함수로는 방사형을 사용
clf = svm.SVC(probability=True,kernel='rbf')
clf.fit(df1[['x','y']], df1['c'])  
Out[5]:
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0, degree=3, gamma=0.0,
  kernel='rbf', max_iter=-1, probability=True, random_state=None,
  shrinking=True, tol=0.001, verbose=False)
분류 모델을 통한 데이터 분류 및 시각화를 위한 확률값 추출
In [6]:
# 시각화를 위해 학습 데이터에 대한 확률값(z축) 반영하기
df1['z'] = df1.apply(lambda r: clf.predict_proba((r[0],r[1]))[0][1],axis=1)
# 커널 함수에 대한 시각적인 이해를 위해 그리드(Grid)한 테스트 데이터 분류
net = []
for i in arange(-3,3,.1):
    for j in arange(-3,3,.1):
        net.append((i,j,clf.predict((i,j))[0],clf.predict_proba((i,j))[0][1]))
df2 = pd.DataFrame(net,columns=['x','y','c','z'])
In [7]:
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xs=df1['x'],ys=df1['y'],zs=df1['z'],color=df1['c'],s=100)
ax.scatter(xs=df2['x'],ys=df2['y'],zs=df2['z'],color=df2['c'],alpha=.1,marker='.')
ax.set_zlabel('Z Axis')
ax.set_xlabel('X Axis')
ax.set_ylabel('Y Axis')
Out[7]:
<mpl_toolkits.mplot3d.art3d.Text3D at 0x7f2a085a7550>

SVM은 Z라는 새로운 차원 공간을 만들고 학습 데이터를 분류할 수 있는 최적의 커널함수 계수(coef)와 함께 모델을 만들었다. 모델(model)을 통한 분류(classification)의 확률치가 가장 높은 분류 군집을 예측치(predict)로 제공한다. 경계의 데이터는 작은 차이에도 다른 군집으로 들어갈 수 있으니, 학습 데이터의 정확성이 중요하다. 일반적으로, 학습형 분류에 있어서 학습 데이터의 가치는 80% 이상이라고 필자는 주장한다.

차원(dimension)의 계념은 우리의 일상을 정량화 할 수 있고, 다양한 기법들을 활용해 문제 해결에도 영향을 주며, 학문의 벽을 허무는 연결고리에 대한 아이디어를 준다. 일상과 IT의 경계의 연결고리를 발견하게 해준 Jerry 선배님께 이 글을 바친다.

대상을 바라보는 시선과 정량적 대상 비교

필자는 인상주의 작품을 좋아한다. 사물을 바라 보는 작가의 생각이 담겨 있기 때문이다. 작가의 인생 스토리와 연결고리를 찾았을 때는 작품이 주는 감성은 배로 다가온다. 최근 들어 자주 언급되던 프리다 칼로의 작품은 마음이 너무 무거워서 오랫동안 보지도 못한다.

피카소의 2D의 캔버스에 그려진 3D의 표현은 사물의 숨겨진 차원(Dimension)을 표현했다는 점에서 놀라운 작품으로 본다. 약간 오버하자면, 피카소 이전에 고흐의 작품에서도 시각을 벗어난 감정이라는 차원을 작품에 녹여냈다는 생각이 있다. 이 번에는 차원에 대한 수학적인 연결고리를 부족하지만 담아 보려는 시도 한다.

주제 영역이 넒어서 다음의 2가지 순서로 나눠서 설명한다.

  1. 대상을 바라보는 시선과 정량적 대상 비교
  2. 대상을 새로운 차원으로 사상 시키는 SVM 비선형 분류 기법

먼저 첫 번째 주제에 대해서 다음 그림을 본다.

blue, green 두 가지 색상의 선의 길이가 다르게 보인다.  실제로는 같은 길이지만, 우리의 시선이 있는 지점에서는 다른 것이다. 우리의 눈은 그림처럼 2D 캔버스 처럼 세상을 보고 판단한다. 머리가 복잡 할 수 있으니 실제 실행한 코드를 보자.

fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection='3d')
ax.plot(xs=[1,3],ys=[3,1],zs=[2,4],lw=5,marker='p',color='blue')
ax.plot(xs=[1,3],ys=[1,3],zs=[1,3],lw=5,marker='p',color='green')

3D를 표현하기 위한 캔버스를 선언하고, blue 라인과 green 라인을 같은 캔버스에 그린다. 정량적인 비교를 위해서 피타고라스 정리를 참조 한다.
https://ko.wikipedia.org/wiki/피타고라스의_정리

blue line : (1-3)^2 + (3-1)^2 + (2-4)^2
green line : (1-3)^2 + (1-3)^2 + (1-3)^2

색이 다른 것 이외에 정량적으로는 같은 크기(길이)의 선을 뜻한다. 눈으로만 비교하면 혼란이 올수 있으니, 실제로 플롯팅을 해보는 것이 도움이 된다. 차원에 대한 상상이 n차원까지 확대 된다면 다음 과정으로 넘어가자. (아마 3에서 4차원으로 넘어 갈때 잠시 걸리고, 5차원 부터는 아주 쉽게 상상 될 것이다.)

"돈의 비밀" 영상에 대한 사견

"돈의 비밀"에 대한 영상을 감명있게 보고, 사견을 달아 봅니다.
https://www.youtube.com/watch?v=1_0li7LByTM

* 진정한 부는 시간과 자유이다. 돈은 시간을 거래하는 도구일 뿐이다.
* 진정한 돈과 거리가 있는 화폐를 통해 우리는 기만당하고 있다.

영상에 대한 찬/반 논쟁이 있으나, 몇 가지를 제외하고는 매우 잘 만든 영상이라는 공통된 의견이 있다. 시청 후 첫 반응은 "금을 사야겠다" 였고, 영상 제작한 곳이 http://goldsilver.com 라는 것을 확인 후 치우침 없이 볼려는 노력을 했다.

영상에서 소개한 10% 지급 준비율을 한국 지급 준비율 7%로 화폐의 버블을 확인해 보았다.
https://ko.wikipedia.org/wiki/지급준비제도

In [1]:
def bank_bubble(money):
    # 2014년 기준 한국 지급 준비금 7%
    READY = 7
    if money > 1 :
        return money + bank_bubble(money * (100 - READY) / 100 )
    else:
        return money
In [2]:
bank_bubble(100)
Out[2]:
1415.7984078715401

100$가 1400$로 버블 되는 것을 확인 했다. 신용을 통해서 커지는 화폐의 버블은 확인 가능했고, 이것이 문제인가?에 대한 인식은 조금 달리해 본다. 화폐의 특징 중에 중요한 하나는 필요할때 언제든 사용할 수 있어야 한다. 지급준비제도의 화폐의 평창은 순 기능적인 측면이 크다는 생각이 들고, 화폐의 문제에 대한 인식은 제도 자체보다는 제도를 악용하는 계층의 탐욕에서 비롯된다는 생각을 해본다.

2015년 8월 26일 수요일

최소 노력으로 유용한 가계부 만드는 방법

"적을 알고 나를 알면 백전백승이다."라는 말처럼 모든 일에 가장 중요한 시작은 현황을 객관적으로 파학하는 것이라 봅니다. 스타크래프트에서 초반 (몇 마리 없는) 드론 한마리를 보내서 상대방 진영을 파학하는 것도 현황 파학의 중요성을 보여줍니다.

우리의 생활도 현황을 먼저 알면 미래에 대한 전략을 더 잘 세울 수 있다는 가정 하에, 가계부를 만들어 봤습니다. 막연한 생각을 수치로 바라보는 순간, 걱정보다는 확신이 들었습니다. 두려움이라는 것이 불확실성에서 비롯된다는 어느 현자의 말을 다시금 떠올려 봅니다.

가계부의 중요성은 공감하지만 입/출력 마다 DB 트리거처럼 동작하는 일련의 행위는 배보다 배꼽이 더 커보였습니다. 고민을 거듭하다가 필자는 다음의 전제를 통해서 문제를 해결합니다.

모든 입금과 출금은 다양한 곳에서 이루어 질 수 있다.
BUT, 모든 입금은 하나의 통장을 거치고 다시 흩어져서 출금된다.

"하나의 통장"의 내역서 엑셀을 가지고 모든 자금을 추적 할 수 있었습니다. 하지만, 내역서에는 계정 과목을 제공하지 않습니다. 이 부분은 "송금메모"나 "보낸분/받는분" 필드의 텍스트를 이용해서 태그를 달아야 했습니다.

계정과목 "의료비"의 경우 다음과 같은 정규식을 사용해 봤습니다.
약국$|병원$|의원$|이비인후과
>> "약국"/"병원"/"의원"으로 끝나거나 "이비인후과"라는 단어가 포함된 텍스트를 의미합니다.

원하는 계정과목에 대한 정규식 패턴 추가 후에,  다음과 같은 형태의 테이블을 자동으로 만들 수 있었고, 추가된 태그/월별/요일별/시간별 컬럼으로 흥미로운 리포팅이 가능했습니다.

거래일시
적요
보낸분/받는분
송금메모
출금액
입금액
잔액
거래점
구분
태그
월별
요일별
시간별
2015-04-30 16:55:10 체크카드 아이플리테일

5000 0 15440188 KB카드

기타 201504 4 16
2015-04-30 13:02:54 체크카드 로뚜뚜커피

5000 0 15445188 KB카드

커피숍 201504 4 13
2015-04-30 12:01:34 체크카드 바이더웨이역삼포은점

2550 0 15450188 KB카드

편의점 201504 4 12
2015-04-29 12:03:48 체크카드 ()파인푸드컴퍼니

48500 0 15452738 KB카드

외식비 201504 3 12

"보낸분/받는분" 필드 값에 대한 "계정과목"의 깔끔한 매핑만 있으면 일반인을 대상으로 하는 서비스도 고려해 볼 수 있을 듯 합니다.

컨셉은 이것으로 충분해 보이고, 맛벌이 기준의 중요 포인트를 소개합니다.

  • 배우자에게 컨셉을 충분히 설명합니다.
  • "하나의 통장"에서 발급 받은 "체크카드" 사용을 기본으로 합니다.
  • 자금 사용이 투명해도 되는지? 스스로 물어 봅니다. ^^


한 달에 한번 자금 흐름을 체크하면서, "기타" 계정 과목 중에 영향력 있는 "보낸분/받는분" 또는 "송금메모"에 대한 정규식 패턴 추가만 하면, 손쉽게 가계부를 유지 할 수 있는 장점이 있습니다. 초기에 "하나의 통장"을 만드는 과정에서 통장들을 파학하고 정리하는 수고의 과정만 잘  넘기면, 자금의 흐름을 한눈에 파학 하실 수 있습니다.


참고

1. 이해를 돕기 위한 필자의 샘플 코드
https://bitbucket.org/snippets/juhoon26/doqpa

2. 국민은행 기준의 "적요" 필드 도메인 조사 내용
  • FBS출금 : 기업과 은행간의 연계를 통해 출금된 금액
  • ATM출금 : ATM 장비를 통해 출금된 금액
  • CD공동 : CD 장비를 통해 출금된 금액
  • 인터넷입금이체 : 국민은행 내부에서 일어나는 입금 이체된 금액
  • 인터넷출금이체 : 이체를 통해 출금된 금액
  • 전자금융 : 전자금융망을 통해서 입금되는 금액
  • 통신요금 : 통신사로 출금 이체되는 금액 (KT의 경우 케이블 방송 수신비)
  • 투신출금 : 투신사로 출금 이체되는 금액
  • 체크카드 : 체크카드에서 사용한 금액
  • 국민카드 : 신용카드에서 사용한 금액


2015년 8월 25일 화요일

파이톤 프로퍼티(property)의 이해를 위한 예제

파이톤(python)의 프로퍼티(property)의 유용함에 대해서 예제와 함께 다룹니다.
파이톤에서 property란 "사물의 고유한 특성"을 나타내는 말입니다.

무지개라는 클래스를 상상해 봅니다.
class rainbow:
    colors = ['red','yellow','green','blue']

심플하게 4가지 색상으로 정의를 했습니다. 정말 아름다운 언어입니다.

하지만, 프로그램 개발자 입장에서는 매우 위험하다는 생각을 할 수 있습니다. 속성(성질)에 대한 좀 더 명확한 정의를 property를 통해서 만들어 봅니다.

무지개 색상(colors)에 대한 property 정의
In [1]:
class rainbow:
    def __init__(self):
        self.COLORS = ['red','yellow','green','blue']
        self._colors = self.COLORS
        
    @property
    def colors(self):
        return self._colors
    
    @colors.setter
    def colors(self,IN_colors):
        self._colors = [ c for c in IN_colors if c in self.COLORS ]

r = rainbow()
색상들을 입력함
In [2]:
r.colors = ['black','red','yellow']
무지개 색상에 있는 색만 등록됨
In [3]:
r.colors
Out[3]:
['red', 'yellow']

코드가 늘어 났지만 색상에 대한 속성(property) 정의를 통해서 잘 정의된 클래스로 보입니다. 속성 정의를 통해서 클래스를 명확하게 정의하는 것이 프로그램의 완성도에 많은 기여를 할 것으로 보입니다.

작은 사회에서는 구두 약속 만으로 충분하지만, 사회가 커질수록 (번거럽더라도) 절차를 따르는 것이 필요 하듯이, 프로그램 개발도 비슷해 보입니다. 반대로, 천원 빌려 주면서 차용증을 쓸 필요는 없었으면 합니다.


참고

* https://docs.python.org/3.4/library/functions.html#property

2015년 8월 24일 월요일

파이톤 패키지를 PyPI에 배포하는 방법

파이톤 패키지를 PyPI(Python Package Index)에 배포하는 방법을 소개 합니다.

참고 :
* https://docs.python.org/3.4/distutils/
* https://docs.python.org/3.4/distutils/setupscript.html
* https://docs.python.org/3.4/distutils/packageindex.html

다음과 같은 절차로 진행 합니다.
  1. PyPI에 계정을 생성
  2. setup.py 파일을 생성
  3. PyPI에 패키지를 업로드

간단한 예제를 바탕으로 설명합니다.

예제 폴더 구조
$ tree
.
├── data_rider
│   └── __init__.py
└── setup.py

테스트를 위한 패키지 코드
$ cat data_rider/__init__.py
print ('Hello {name}.'.format(name='세상'))

패키지 배포를 위한 코드
$ cat setup.py
from distutils.core import setup
setup(name='data-rider',
      version='1',
      description='Sources for riding on data.',
      author='Mark',
      author_email='juhoon.kim@me.com',
      url='http://data-rider.blogspot.kr',
      packages=['data_rider'],
     )

1. PyPI에 계정 생성

$ python setup.py register CLI 명령
또는
https://pypi.python.org/pypi 웹 페이지에서 가입 합니다.

로그인 정보는 계정 홈 디렉토리에 .pypirc 파일에 등록해서 사용하면 편합니다.
$ cat ~/.pypirc 
[server-login]
username:<username>
password:<password>

2. setup.py 파일 생성

name은 다운로드 및 관리 이름이고, 설치 후에는 packages 이름을 코드에서 사용하게 됩니다.

3. PyPI에 패키지 업로드

$ python setup.py sdist register upload 명령을 통해서 배포합니다.
sdist는 source 배포를 하겠다는 것이고, bdist를 통해서 binary 배포 가능합니다.
.pypirc에 등록하면 따로 register 명령 없이 가능합니다.

* 공유 영역에 배포됨으로 배포 이름은 유일(unique)하게 지어서 사용해야 합니다.

위 과정이 잘 끝났다면 다음과 같이 사용할 수 있습니다.
$ pip install data-rider
$ python -c "import data_rider"
Hello 세상.

2015년 8월 21일 금요일

구글 태그 매니저(GTM)에 대한 샘플 예제

웹 분석(Web Analytics)을 위해서 태그 생성 및 변경을 도와주는 GTM(Google Tag Manager)의 개괄적인 특징과 예제를 다룹니다.

먼저, GTM의 아름다운 구조(Architecture)를 사용자 입장에서 설명합니다.


준비 작업으로 GA와 GTM의 계정 그리고,
GTM의 컨테이너에서 발급 받은 태그 코드를 분석하고자 하는 모든 페이지에 붙여 넣습니다.
-END-

GTM 컨테이너에서 트리거(TRIGGER)/변수(VARIABLE)를 활용해 태그(TAG)들을 생성하고 게시(Publishing)합니다.
페이지에서 규칙을 만족하는 태그들은 실시간으로 GA로 전송됩니다.

달리 표현해서, 웹브라우저에서 태그 매니저 코드가 있는 사이트에 접속을 하면, GTM 서버로 부터 (사용자가 웹으로 설정한) 태그 정보를 받게 되고,  규칙에 해당되는 태그들을 GA로 발송(Fire) 합니다. 

다음은 데이터 영역 변수(Data Layer)를 활용한 버튼 클릭 이벤트를 예제와 함께 다룹니다.

  1. 사용할 변수 및 트리거를 생성합니다.
  2. 생성한 변수와 트리거를 가지고 태그를 만듭니다.
  3. 미리 보기를 통해서 태그가 잘 동작하는지 확인 합니다.
  4. 생성한 태그를 게시 하고 GA 실시간 이벤트를 확인 합니다. 

버튼 클릭 트리거를 만들고, 데이터 영역 변수 값들을 태그를 통해서 보내기 위한 설정입니다.


* 트리거 : "id"가 "demo"이고 "class name"이 "GTM click"인 클릭에 대해서 동작합니다.
* 변수 : 태그에서 사용할 GA 추적 ID를 상수로 미리 지정했고, 페이지의 데이터 영역(Data Layer)에 myLabel,myValue 두 가지 변수를 정의 했습니다.
* 태그 : "버튼 클릭" 트리거가 발생했을때, "my*" 변수를 "GAID"로 전송하는 "버튼 클릭" 태그를 만들었습니다. "링크 클릭"은 무시하면 됩니다.

GTM 웹에서 제공하는 미리 보기 기능으로 잘 동작하는지 확인 합니다.
* 이벤트 버튼을 클릭하면 실행되는 태그와 변수들에 대한 정보를 바로 보여줍니다.

GTM 컨테이너 게시 후에는 GA의 실시간 이벤트 보기에서도 확인 가능합니다.

일반적인 태그의 추가나 변경 작업 시에 IT 부서에 티켓을 발행하고 기다릴 필요가 없습니다. 미리 보기 기능으로 오류를 미연에 방지하고, 버전 관리 기능으로 언제든 복구 가능합니다.


참고

* 본문에서 사용한 HTML 코드
https://bitbucket.org/snippets/juhoon26/5Rkjg

* 구글 공식 도움말
https://support.google.com/tagmanager

* 구글 태그 매니저 학습 코스
https://analyticsacademy.withgoogle.com

2015년 8월 20일 목요일

인스타그램 데이터 수집하는 방법을 소개 합니다. [DEADED]

June 1, 2016 부터 적절한 권한 승인을 받지 못한 앱의 서비스는 정지 되었습니다.
특히, "해쉬 태그"에 대한 미디어 수집이 새롭게 추가된 public_content 범주의 승인이 필요합니다.
승인 절차가 까다로워서 당분간 지켜봐야겠습니다.

**
일부 전문가들은 비 API 적으로 웹 파싱(parsing)을 시도하고 있습니다.

기존 블로깅에서 인스타그램 수집 절차에 대해서 개괄적으로 다루었던 부분을 상세하게 요약합니다.

전체 절차는 다음과 같습니다.

1. 관리 클라이언트(Manage Client) 만들기
2. 관리 클라이언트에 엑세스할 토큰 발행하기
3. 토큰을 가지고 API 사용하기

* 마치 트렌드 처럼 트위터, 페이스북 그리고 인스타그램 모두 위와 같은 절차를 가집니다.
관리 클라이언트는 DB의 뷰(View)처럼 유용하게 활용되는 컨셉으로 이해하면 됩니다.

실습을 통해서 상세하게 다루도록 하겠습니다.


1. 관리 클라이언트 만들기.

 - https://instagram.com/developer/clients/manage/

CLIENT_ID와 REDIRECT_URL 정보를 다음 과정에서 사용 합니다.


2. 관리 클라이언트에 엑세스할 토큰 발행하기

 - https://instagram.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=token



인증 후 발급받은 ACCESS_TOKEN 정보를 다음 과정에서 사용합니다. Redirect Url로 변수를 던졌으나 지정한 http://localhost 에서 변수를 받을 수 없으니 에러 페이지가 발생합니다. 변수르 받는 웹페이지를 구현할 필요 없이, 주소창에서 던져진 변수(토큰)을 카피해서 사용하면 됩니다.
토큰 발급 이후에는 보안 강화를 위해, 1번 과정에서 해제한 Security 탭에 Disable implicit OAuth를 다시 활성화 합니다.


3. 토큰을 가지고 API 사용하기.

 - https://api.instagram.com/v1/tags/TAG_NAME/media/recent?access_token=ACCESS_TOKEN

반환되는 json 포멧의 데이터는 3가지 영역을 가집니다. ['pagination','meta','data']
수집 대상인 data는 문서(media)에 대한 정보 상위 20개를 보여주고, meta는 상태를 알려주며, pagination은 DB의 커서(cursor) 처럼 결과 전후 페이지(page)를 탐색할 수 있는 방법을 제공합니다.

오피셜(Official)하게 제공되는 API로만 개발하려면 pagination을 참고하고, 일반적으로는 편리하게 사용할 수 있도록 Wrapping 되어 있는 API 사용을 권장합니다.

참고

인스타그램에서 제공되는 상세 API 설명서
 - https://instagram.com/developer/endpoints/

파이톤 API
 - https://github.com/Instagram/python-instagram

파이톤 python-instagram API를 활용한 예제 코드
 - https://bitbucket.org/snippets/juhoon26/BA9ep

SNS 공유 버튼에 대한 성능 고민

정보의 범람에 노아의 방주라 불릴만한 다양한 네트워크(SNS)가 믿음을 요구하는 세상이다.
인스타그램(instagram)은 태그(tag)를 통해 정보를 구분짓는 방법을 제공하고, 출처의 제약을 통해서 쓰레기 데이터들을 막아내고 있다. 핀터레스트(pinterest)는 보드이라는 사탕을 주면서 정보를 구분짓을 일을 유저에게 일임한다. 각각 자신만의 스타일로 방주를 자처하고 있지만 혼란한 시기에는 지켜보는 것이 현명해 보인다.

오늘은 컨텐츠를 여러 방주로 흘려 보내는 방법을 간략히 정리해 봅니다.

대표적인 SNS의 공유버튼(SHARE BUTTON) API 가이드 페이지입니다.
* 페이스북 - https://developers.facebook.com/docs/plugins?locale=ko_KR
* 트윗터 - https://about.twitter.com/ko/resources/buttons
* 구글플러스 - https://developers.google.com/+/web/share/
* 핀터레스트 - https://business.pinterest.com/ko/widget-builder#do_pin_it_button
* 링크드인- https://www.linkedin.com/publishers

제공되는 코드는 자바스크립트(Javascript)를 100% 활용한 코드들로, 개선 할 수 있는 방법을 찾아서 비교해 봅니다.
포인트는 "상품을 보는 사용자 대비 공유 버튼을 누르는 사용자는 매우 제한적일 것이다"라는 것에서, 불필요하게 로딩되는 자바스크립트는 브라우저에 부담 줄 것이라는 생각입니다.


왼편 이미지는 custom 한 방식으로 사용자가 원하는 스타일로 제작할 수 있습니다. 추가적으로 스타일시트(CSS)와 가볍고 멋진 이미지만 붙이면 됩니다.
오른편 이미지는 official하게 제공되는 코드를 사용해서, 편하게 쓸 수 있지만 성능 개선과 스타일을  꾸미는데 제약이 있습니다.

웹 성능 측정 페이지(http://tools.pingdom.com/fpt/)를 통해서 로딩 테스트를 해 보았습니다.

로딩 시간은 여러번의 샘플링 중에서 중간 정도를 선택했습니다. 오피셜(offical)한 방식은 로딩시간이 상황에 따라 1~3초 사이로 편차가 꽤 있었습니다. 각 SNS의 서비스 제공 지역이 흩어져 있고, 상대적으로 복잡한 자바스크립트 코드의 영향으로 판단됩니다. 중요한 부분은 요청횟수(Requests) 부분으로 보입니다. custom 방식으로 실제 코드를 완성 후에 비교해보면 흥미로울 듯 합니다.

본문에서 사용한 코드는 비트버켓(bitbucket)을 통해서 공유합니다.
https://bitbucket.org/snippets/juhoon26/RoxbG/sns-share-buttons

2015년 8월 19일 수요일

크로스 플랫폼 개발 Kivy 오프닝 예제

다수의 플랫폼에서 동작하는 단 하나의 코드, 파이톤이 지향하는 편의성을 추구하는 Kivy 오픈소스에 대한 "Hello World!" 오프닝 시연 영상을 담아 봅니다.

홈페이지 : http://kivy.org/#home

간략한 앱 하나를 만들어서 배포하는 것을 목표로 조사 중이 었는데, 다양한 이유로 흔적만 남기는 것으로 일단락 합니다.


2015년 8월 18일 화요일

프로그래밍 언어의 필요성과 코드 관리 방법

데이터를 가공하고 관리하는 툴들은 정말 많다. 용도에 따라 다양하고 강력한 툴들이 존재한다.
하지만, 툴로만 해결 하기에는 제약으로 인한 비효율적인 상황이 자주 발생된다.

UI적으로 해결이 안되는 부분에 대한 고민은 "액셀의 VB 매크로"가 대표적이다.
최근에 블로깅한 나임(KNIME)에서도 외부 확장 프로그램 언어 노드를 통해서, 데이터를 다루기 위한 만능 툴을 만들었다.

데이터를 다루는 사람이라면 어떤 식으로든 프로그래밍 언어에 대한 깊은 이해가 필요하다고 예기하고 싶다.

생각 (THINK) => 프로그램 언어 (LANGUAGE) => 응용 프로그램 (APP) => 데이터 다루기 (DATA)

* 자연 속에 현상을 유심히 관찰한 그 경험들이 프로그래밍에 반영된다.
* 프로그래밍 경험을 통해서 응용 프로그램들을 더 잘 이해하고 다룰 수 있다.
* 만들어진 프로그램에는 제약이 발생하고, 해결을 위해서는 프로그램을 만들 필요가 있다.

KNIME에서 프로그래밍 언어를 사용한 것과 UI만을 이용한 것,두 가지 방식을 통해서 부연 설명을 한다.
작은 단위로 모듈화 된 것은 복잡한 프로젝트에서 장점이 될 수 있지만, 논지는 구현해야할 새로운 기능(예제, 피봇팅)에 있다.
UI 기능을 통해서 피봇(PIVOT)을 하면, PIVOT에 대한 UI 용어를 이해해야 하고, 귀찮을 만큼의 마우스 클릭이 필요하다.
반면, 프로그래밍 언어를 사용하면, 익숙한 언어를 통해 PIVOT 부분을 한 줄로 표현할 수 있다.
CODE# out = df.pivot_table(index='time',columns=['server_ip','item'],values='temp')
심플한 예제지만, 이 것이 말하는 편의성에 대해서는 각자의 상상에 맡겨 본다.
그리고, 툴에서 제공되지 않는 기능이 필요할 때는 이 것이 툴의 봉인을 불어 줄 것이다.

프로그램 언어를 한다는 것은 코드를 관리해야 한다는 필연을 끌어내고, 코드 관리를 위한 툴을 익힐 필요가 있다.

리누스 토발즈에 의해 고안된 GIT라는 툴이 있다.  개발자들은 누구나 아는 툴이지만, 데이터를 다루는 사람에게는 생소할 수도 있다.
GIT의 대표적인 온라인 서버인  GITHUB와 BITBUCKET를 활용해 보자.

* 개인적인 코드는 무료로 개별(PRIVATE)적인 저장소 (REPOSITORY)를 제공하는 BITBUCKET을 사용한다.
* 많은 유저에게 오픈하고 공동 작업을 진행 할때는 더 다양한 기능과 많은 사용자들이 있는 GITHUB를 사용한다.

모두 GIT을 기반으로 함으로, 사용 방법은 동일하다.

2015년 8월 13일 목요일

셀레니움(selenium)을 통한 인스타그램 자동 로그인 예제

편리한 웹 어플리케이션 테스팅 프레임워크(framework)인 셀레니움(selenium)을 통해서,
인스타그램 페이지에 자동 로그인 하는 예제를 다룬다.

일반적으로 크라울링(수집) 할때, 주소(url)에 의한 질의(Request)와 응답(Response) 이라는 단순한 패턴을 통해서 이루어진다. 하지만, 로그인과 같은 정보 제출(submit) 패턴을 만나면 난감해 진다.
이때, 셀레니움은 어려움에 처한 당신에게 구원에 손길을 제공할 것이고, 겸허한 마음으로 그 손길을 잡으면, 신세계를 경험할 수 있을 것이다.

예제는 인스타그램에 로그인 하는 패턴의 코드와 호출하는 과정을 담았다.
그리고, 수행 및 구동하는 화면을 캡처링 해 보았다.

참고 : http://www.seleniumhq.org

인스타그램 로그인 클래스 작성

In [2]:
from selenium import webdriver
#from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
import time

class instagram():
    def __init__(self):
        self.drv = webdriver.Firefox()
        self.drv.get('https://instagram.com')
    
    def login(self):
        drv = self.drv
        
        # 로그인 화면으로 전환하기,
        lo_login_button = WebDriverWait(driver=drv,timeout=10) \
            .until(lambda drv: drv.find_element_by_xpath("//a[contains(@href,'/login')]"))
        lo_login_button.click()
        time.sleep(3)
        
        # 로그인 정보 입력하기,
        lo_user = WebDriverWait(driver=drv,timeout=10) \
            .until(lambda drv: drv.find_element_by_xpath("//input[@name='username']"))
        lo_user.send_keys(g_username)
        lo_pass = WebDriverWait(driver=drv,timeout=10) \
            .until(lambda drv: drv.find_element_by_xpath("//input[@name='password']"))
        lo_pass.send_keys(g_password)
        time.sleep(3)
        
        # 로그인 하기,
        lo_login_submit = WebDriverWait(driver=drv,timeout=10) \
            .until(lambda drv: drv.find_element_by_xpath("//button[@tabindex='3']"))
        lo_login_submit.click()
        
        time.sleep(5)

수행하기

In [3]:
# 클래스 로딩
firefox = instagram()
# 로그인 액션 수행
firefox.login()
In [4]:
# 종료
firefox.drv.quit()

수행 화면 보기


코드 중에 g_username과 g_password는 (짐작하는 바와 같이) 본인의 개인 계정을 입력하면 된다.

자료 작성 중에 문득 이런 생각이 들었다.
프락시와 셀레니움 조합으로 광고 클릭 수를 늘릴 수 있을까? 구글은 어떤 방식으로 봇을 막고 있을까?

2015년 8월 12일 수요일

오픈소스 통합 분석툴 나임(KNIME) 소개

강호의 고수에게 소개 받은 분석 끝판왕 툴을 소개 하고자 합니다.

툴 이름은 KNIME
* 사이트 : https://www.knime.org

간략히 한 줄로 표현하자면,
데이터 분석 전과정(수집,전처리,분석,시각화,리포트)이 단위 기능(함수)으로 세분화(노드) 되고,
파이프라인을 통해, 멀티 데이터 플로우(Data Flow)를 시각적으로 표현하고 수행합니다.

기본적으로 Weka의 기계학습, R의 통계 패키지들이 제공되고, 파이톤/펄과 같은 코드를 노드에 담을 수 있습니다.
무한한 확장 가능성과 한 눈에 들어오는 데이터 플로우, 그리고 사용자 경험은 입가에 미소를 머금게 할 것이라 확신 합니다.

전에 블로깅한 "판다곰(Pandas) 주식 시세 확인"을 KNIME으로 구현한 예제 화면 입니다.
* 전번 블로깅 : http://data-rider.blogspot.kr/2015/07/pandas.html

파이톤 노드의 경우 판다곰의 데이터프레임(DataFrame)으로 인아웃(In/Out)을 처리 할 수 있으니, 판다곰 중독자들에게는 축복과도 같은 종합 선물 세트로 봐도 무방할 듯 합니다.

맥용 용량이 1.6G로 다운로드 시간이 꽤 걸리지만, 참는 자에게 복이 있으리....

2015년 8월 11일 화요일

데이터베이스 DEAD LOCK에 대한 간략한 시연

DW(Data Warehouse)를 위한 분산 데이터베이스인 그린플럼(Greenplum)을 다루던 시절에,
코끼리(pgadmin) 창 2개를 열고 시연하던, 트랜잭션(Transaction)과 락(Lock) 매커니즘으로 인한 DEAD LOCK 발생에 대해서 구현해 보았다.

테스트 과정을 간략히 설명하면,
* 시연 트랜잭션 - INSERT TRUNCATE 수행

1. 2개의 트랜잭션이 들어 온다.
    => INSERT (ROW EXCLUSIVE)는 동시 수행이 수행된다.
2. 1개의 트랜잭션에 INSERT가 끝나고 TRUNCATE가 들어온다.
    => INSERT (ROW EXCLUSIVE) <-> TRUNCATE (ACCESS EXCLUSIVE)는 상호 배재 관계 임으로, 다른 트랜잭션에 수행중인 INSERT가 끝나길 기다린다.
3. 나머지 1개의 트랜잭션에 INSERT가 끝나고 TRUNCATE가 들어온다.
    => 서로 다른 트랜잭션이 끝나길 기다리지만, 영원히 기다려야한다.
4. 코끼리는 DEAD LOCK을 발견 후, 메세지를 남기고 끊어 버린다.

참고 : http://www.postgresql.org/docs/9.4/static/explicit-locking.html

테스트 코드는 다름과 같다.

준비 작업

In [1]:
# 필요한 모듈 로딩
from sqlalchemy import create_engine
from multiprocessing import Pool
In [2]:
def insert_truncate_query(i):
    # 하나의 트랜젝션에 상호배제되는 LOCK을 유도하기 위한 함수
    engine = create_engine('postgresql://chef:fork@cook:5432/cook')
    connection = engine.connect()
    # 트랜잭션 구문 시작
    trans = connection.begin()
    try:
        # 세션 프로세스 아이디 출력
        print(i,connection.execute("select pg_backend_pid() as pid;").fetchall()[0][0])
        # LOCK MODE - ROW EXCLUSIVE 
        connection.execute("insert into target select * from source limit 200000;")
        # LOCK MODE - ACCESS EXCLUSIVE
        connection.execute("truncate target;")
        trans.commit()
    except:
        trans.rollback()
        raise

테스트1 - 단일 수행에 대한 결과

In [3]:
L = ['job I']
with Pool(processes=2) as pool:
    out = pool.map(insert_truncate_query,L)
job I 23183

테스트2 - 동시 수행에 대한 결과

In [4]:
L = ['job I','job II']
with Pool(processes=2) as pool:
    out = pool.map(insert_truncate_query,L)
job II 23196
job I 23197
---------------------------------------------------------------------------
RemoteTraceback                           Traceback (most recent call last)
RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
    context)
  File "/usr/local/lib/python3.4/site-packages/sqlalchemy/engine/default.py", line 450, in do_execute
    cursor.execute(statement, parameters)
psycopg2.extensions.TransactionRollbackError: deadlock detected
DETAIL:  Process 23196 waits for AccessExclusiveLock on relation 6099669 of database 16384; blocked by process 23197.
Process 23197 waits for AccessExclusiveLock on relation 6099669 of database 16384; blocked by process 23196.
HINT:  See server log for query details.
번역하자면, Job II는 target 테이블에 Truncate를 하기 위해 기다리는데, Job I이 막고 있다. 반대로 Job I의 상황도 같다.

보통 분산 디비의 경우 기술적인 복잡성을 이유로 LOCK 모드와 같은 다양한 제약사항이 있다.
비 오라클 DBA 분들에게 제약사항이란, DB를 좀 더 깊이 이해할 수 있는 계기가 아닐까? 희망적인 메세지를 남겨본다.

2015년 8월 7일 금요일

networkx 라이브러리를 이용한 뉴스 요약 예제

네트워크 그래프를 그리는데 유용한 networkx 라이브러리(library)와 함께,
문장 단위의 명사구 네트워크를 그리는 간략한 예제를 소개합니다.

아이디어는 같은 문장의 명사구들은 서로 관계가 있다라는 것에서 시작합니다.
문장의 명사구를 (network theory의) 노드(node|vertex)로 보고,
각 노드의 쌍을 edge로 사전을 미리 제작하였습니다.

반복되는 edge들에는 빈도 가중치(weight)를 적용해서,
빈도의 순위를 통해 대상을 추리는 작업을 할 수 있도록 했습니다.

참고
* networkx 라이브러리 - http://networkx.readthedocs.org/en/latest/overview.html

사용 라이브러리들

In [1]:
import pandas as pd
import networkx as nx
import mpld3

미리 제작한 Edge 가중치 사전

In [2]:
# Pandas의 DataFrame
df.sort('weight',ascending=False).head(3)
Out[2]:
edge weight
6708 전망대,한수진 7
5520 삼성물산,엘리엇 6
9302 朴대통령,국회법 6

네트워크 그래프 오브젝트에 데이터 적재하기

In [3]:
# 그래프 종류 선언
G=nx.Graph()

# 미리 제작한 Edge 가중치 사전을 네트워크 그래프 오브젝트로 적재
for nodes,weight in df.sort('weight',ascending=False).head(50).get_values():
    node1,node2 = nodes.split(',')
    G.add_edge(node1,node2,weight=weight)

네트워크 그래프 그리기

In [4]:
mpld3.enable_notebook()
fig,ax = subplots(1,2,figsize=(10,4))
def drawing(ax):
    ax.set_title('뉴스 이슈 - June 2015',fontsize=14)
    layout = nx.spring_layout(G,k=.4)
    nx.draw_networkx_nodes(G,pos=layout,node_size=1500,node_color='green',alpha=.1,ax=ax)
    nx.draw_networkx_edges(G,pos=layout,width=2,edge_color='purple',alpha=.2,ax=ax)
    nx.draw_networkx_labels(G,pos=layout,font_size=8,font_color='black',ax=ax)
    ax.text(fontsize=20,family='Comic Sans MS',rotation=0,
         horizontalalignment='center',verticalalignment='center',
         x=0,y=0,s='data-rider.blogspot.kr',alpha=0.3,color='purple')
drawing(ax[0])
drawing(ax[1])
#mpld3.save_html(fig,'june2015.html')

최근에 블로깅한 mpld3 라이브러리의 장점를 설명하고자 2개의 동일 플롯을 그리고,
우측(right)에 확대 화면을 담아 봤습니다.
HTML로 저장 후, 넒은 화면에서 인터랙티브(interactive)한 기능을 이용한다면, 꽤 흡족한 사용자 경험을 느낄 수 있을 것으로 기대해봅니다.