2016년 1월 7일 목요일

네이버 뉴스 출처 성향 보기

네이버의 10년간 TOP 뉴스를 대상으로 각 출처에 대한 성향을 조사했다.
 - TOP 뉴스는 하루 기준으로 섹션(7) * 탑(30) = 210개가 발생된다.
 - 총 296개의 출처가 있었고, 섹션 중에 하나가 최소 100건 이상만 선별했다.

2016년 1월 5일 화요일

기상청 날씨 API 개선방안

 기상청 날씨 서비스를 위한 성능 개선 방안을 다룬다.


먼저 기본적인 사용 방법이다.


1. “위/경도” 또는 “지명 이름”를 “격좌 좌표”로 변한
2. “격좌 좌표”를 기상청 API에 조회 (날씨)

조회가 발생할 때마다 기상청 API를 호출하는 것은 비효율적이다.


다음은 성능 개선 방안이다.


1. “지역 이름”에 해당하는 “날씨 정보”를 일괄 수집한다. (총 3772, 시단위 84)
=> 수집된 데이터는 “지역 이름”, “위/경도”, “격자 좌표”,  “날씨 정보” 속성을 가진다.
2. 위/경도를 역지오코딩으로 “지역 이름”을 추출한다.
3. “지역 이름”에 해당되는 “날씨 정보”를 반환한다.



만들어 놓은 것만 쓰다보니 그림과 같은 1차 발상을 한다.
역지오코딩을 통해 "지역 이름"을 추출하는 것이 API 서비스를 통해서 다음/네이버에서 제공된다. 속도가 느린 것이 당연한 API 서비스다.!!

더 낳은 방안을 찾다.


날씨에 중요한 건 “지역 이름”이 아니라 관측 장소와 거리이다.
“위/경도” 좌표 기준으로 최단 거리에 해당하는 지역의 "날씨 정보"를 반환하자.

최단 거리는 min() 구하는 로직이라 시간 복잡도 O(n)으로 충분하다.

사무실에 가만히 공상하는 것이 낯선 환경에서는 생각은 짧고 손은 분주하다.


검증을 위해 구현한 예제 코드이다.

import pandas as pd
import sqlite3
conn = sqlite3.connect('kma.db')
df = pd.read_sql('select * from loc_map_book',con=conn)

df = df [ df['lev3'] == '-' ]
df['city'] = df['lev2'].apply(lambda i: i[-1] == '시' or i[-1] == '-')
TPS = df[ df['city'] ].to_dict(orient = 'records')

df.head(3)
OUT>>

lev1
lev2
lev3
nx
ny
lon
lat
city
0
서울특별시
-
-
60
127
126.980008
37.563569
TRUE
1
서울특별시
종로구
-
60
127
126.981642
37.570378
FALSE
19
서울특별시
중구
-
60
127
126.999642
37.561003
FALSE

def closest_pair(lon,lat):
    _min_dist = pow(TPS[0]['lon'] - lon, 2) + pow(TPS[0]['lat'] - lat, 2)
    
    for tp in TPS[1:]:
        _cur_dist = pow( tp['lon'] - lon,2 ) + pow( tp['lat'] - lat, 2 )
        
        if _cur_dist < _min_dist:
            _min_dist = _cur_dist
            _out_pair = tp
    
    return _out_pair

closest_pair(132,37) # 울릉도
OUT>>
{'city': True,
 'lat': 37.44708611111111,
 'lev1': '강원도',
 'lev2': '삼척시',
 'lev3': '-',
 'lon': 129.16748888888887,
 'nx': 98,
 'ny': 125}

울릉도의 경우 가장 가까운 "강원도 삼척시"가 나온다.



웹 서비스의 논리적인 구조

프로그래머와 의사 소통을 위해서 수만은 솔루션들을 기억 할 수는 없었다.
비 프로그래머 입장에서는 논리적인 구조를 통해서 대화를 풀어보자.

본문은 웹 서비스의 논리적인 계층에 대한 이해를 목표로 한다.
각 계층를 대변하는 솔루션들은 그들 중의 하나로 예기 할 수 있을 것이다.

프로그래머가 바라보는 웹 구조는 어떨까?

프로그래머 관점에서 보는 레이어(계층) 구조이다.




파이썬 Flask 웹 프레임워크의 구조 예제이다.
Presentation Layer : Jinja
Business Layer : flask
Data Access Layer : SQLAlchemy
Persistence Layer : py-postgresql

중요한 것은 데이터를 다루는 일이다.




잘 만들어진 아키 그림은 코멘트가 가비지 일 수 있다.
읽지 않고 보는 것으로 충분해 보인다.

References

이미지 조회
http://www.teradatamagazine.com/v09n01/Tech2Tech/DAOs-enable-active-integration/

2015년 12월 30일 수요일

구글 API 서비스 사용 퀵가이드

구글의 어마무시한 서비스 API를 사용하는 퀵가이드를 다룬다.
요약하면 다음과 같다.

1. 관리 콘솔을 통한 인증 정보 발급 받기
2. 프로그램 언어 및 사용 방법에 따른 인증
3. 서비스 API 사용 가이드에 따른 개발

Google Developer Console

구글 개발을 위한 관리 콘솔

1. 프로젝트 생성

2. 사용자 인증 정보 생성

API 키
 - API 호출시 사용할 토큰을 발급한다.
OAuth 2.0 클라이언트 ID
 - 인증 정보가 있는 client_secrets.json을 다운로드 한다.

API Client Library for Python

파이썬을 위한 API 클라이언트 가이드

3. 설치형 어플리케이션을 위한 OAuth 2.0 인증 예제

import webbrowser
import httplib2

from apiclient import discovery
from oauth2client import client

flow = client.flow_from_clientsecrets(
        'client_secrets.json',
        scope='https://www.googleapis.com/auth/blogger.readonly',
        redirect_uri='urn:ietf:wg:oauth:2.0:oob')

auth_uri = flow.step1_get_authorize_url()
webbrowser.open(auth_uri)

# Python 2.x raw_input()
auth_code = input('Enter the auth code: ')

credentials = flow.step2_exchange(auth_code)
http_auth = credentials.authorize(httplib2.Http())

blogger_service = discovery.build('blogger', 'v3', http=http_auth)
blogger_service.users().get(userId='self').execute()

OUTPUT >>
{'about': '',
 'blogs': {'selfLink': 'https://www.googleapis.com/blogger/v3/users/g107745812829425907154/blogs'},
 'displayName': '김주훈',
 'id': 'g107745812829425907154',
 'kind': 'blogger#user',
 'selfLink': 'https://www.googleapis.com/blogger/v3/users/g107745812829425907154',
 'url': 'https://www.blogger.com/profile/14043740031094632533'}


References

구글 개발 콘솔
https://console.developers.google.com

구글 API 탐색
https://developers.google.com/apis-explorer/

구글 Python 클라이언트 라이브러리 가이드
https://developers.google.com/api-client-library/python/




2015년 12월 29일 화요일

파이썬 단순 문장 표현 정리

파이썬에서는 싱글 라인에 로직을 구현하는 단순 문장 표현을 제공한다.
본문은 원문 가이드 내용 중에 부가 설명이 필요한 부분을 다룬다.

Simple statements

단순함을 지향하는 파이썬을 더욱 단순하게 만들어 주는 한 줄 코드에 대한 내용이다.

Assignment statements

어싸인 문장을 통해서 로직을 쉽게 표현 할 수 있다.

# 교환 로직
temp = a 
a = b
b = temp

=> 

a, b = b, a

The assert statement

어서트 문장을 통해서 디버깅 코드를 간단하게 삽입 할 수 있다.

if __debug__:
   if not expression: raise AssertionError(<message>)

=> 

assert expression, <message>


Future statements

퓨처 문장을 통해서 미래 버전의 문법을 강제 할 수 있다.

Python 2.x에서
from __future__ import print_function
print 1
  File "<ipython-input-2-08890221e919>", line 1
    print 1
          ^
SyntaxError: invalid syntax


이건 장난 같은 거다.
미래에 갈호를 사용할 것인가? 그럴 기회는 없다.

from __future__ import braces
  File "<ipython-input-34-2aebb3fc8ecf>", line 1
    from __future__ import braces
SyntaxError: not a chance


References

단순 문장
https://docs.python.org/3.5/reference/simple_stmts.html


파이썬 PostgreSQL 라이브러리 py-postgresql 퀵 가이드

PostgreSQL을 사용 위해서 psycopg2 라이브러리 구성시 pg_config가 필요한 이유로 패키지를 추가 설치하는 불편함이 있었다.
본문은 (신속하고 편리한 사용을 위해)  py-postgresql 라이브러리로 작업하는 샘플을 요약한다.

py-postgresql 사용

간단한 설치 방법이다.

pip install py-postgresql

문서에서 제공되는 메인 예제이다.

import postgresql



db = postgresql.open("pq://user:password@host/name_of_database")

db.execute("CREATE TABLE emp (emp_name text PRIMARY KEY, emp_salary numeric)")



# Create the statements.

make_emp = db.prepare("INSERT INTO emp VALUES ($1, $2)")

raise_emp = db.prepare("UPDATE emp SET emp_salary = emp_salary + $2 WHERE emp_name = $1")

get_emp_with_salary_lt = db.prepare("SELECT emp_name FROM emp WHERE emp_salay < $1")



# Create some employees, but do it in a transaction--all or nothing.

with db.xact():

    make_emp("John Doe", "150,000")

    make_emp("Jane Doe", "150,000")

    make_emp("Andrew Doe", "55,000")

    make_emp("Susan Doe", "60,000")



# Give some raises

with db.xact():

    for row in get_emp_with_salary_lt("125,000"):

    print(row["emp_name"])

    raise_emp(row["emp_name"], "10,000")

동적 쿼리가 필요한 경우는 prepare 대신에 다음을 사용할 수 있다.

for row in db.query.rows(<query>):
    #row는 dict 형태로 사용됨
    pass

Pandas와 함께 사용하는 방법이다.

from sqlalchemy import create_engine
db = create_engine(“postgresql+pypostgresql://user:password@host/name_of_database”)

참고 자료

SQLAlchemy ORM (Object Relational Mapper)
 - http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html
py-postgresql 라이브러리
 - http://pythonhosted.org/py-postgresql/index.html


커버 사진