IP 주소를 위도와 경도로 변환

Web Scraping with Python을 읽다가 IP 주소를 위도와 경도로 알려주는 사이트를 봤다.
csv, xml, json의 형태로 데이터를 반환하기 때문에
수업 시간에 다양하게 활용할 수 있을 것 같아 정리해 둔다.

import requests
import json

# 네이버 202.131.30.11
# 구글 173.194.126.240
# 용인 수지 121.165.11.161

# 내 컴퓨터 IP 주소
recvd = requests.get('http://ip.jsontest.com/')
my_ip = json.loads(recvd.text)
print(my_ip['ip'])

# ----------------------------------------------------------------- #

# ip가 가리키는 geo 정보 획득

# host 이름과 ip 주소 모두 사용 가능
hosts = ['www.naver.com', 'github.com', '202.131.30.11', '173.194.126.240', '121.165.11.161']

for host in hosts:
recvd = requests.get('http://www.freegeoip.net/xml/' + host)
geo =recvd.text
print(geo)

# <Response>
# <IP>23.193.193.7</IP>
# <CountryCode>US</CountryCode>
# <CountryName>United States</CountryName>
# <RegionCode>MA</RegionCode>
# <RegionName>Massachusetts</RegionName>
# <City>Cambridge</City>
# <ZipCode>02142</ZipCode>
# <TimeZone>America/New_York</TimeZone>
# <Latitude>42.3626</Latitude>
# <Longitude>-71.0843</Longitude>
# <MetroCode>506</MetroCode>
# </Response>
#
# <Response>
# <IP>192.30.253.112</IP>
# <CountryCode>US</CountryCode>
# <CountryName>United States</CountryName>
# <RegionCode>CA</RegionCode>
# <RegionName>California</RegionName>
# <City>San Francisco</City>
# <ZipCode>94107</ZipCode>
# <TimeZone>America/Los_Angeles</TimeZone>
# <Latitude>37.7697</Latitude>
# <Longitude>-122.3933</Longitude>
# <MetroCode>807</MetroCode>
# </Response>
#
# <Response>
# <IP>202.131.30.11</IP>
# <CountryCode>KR</CountryCode>
# <CountryName>Republic of Korea</CountryName>
# <RegionCode></RegionCode>
# <RegionName></RegionName>
# <City></City>
# <ZipCode></ZipCode>
# <TimeZone>Asia/Seoul</TimeZone>
# <Latitude>37.5112</Latitude>
# <Longitude>126.9741</Longitude>
# <MetroCode>0</MetroCode>
# </Response>
#
# <Response>
# <IP>173.194.126.240</IP>
# <CountryCode>US</CountryCode>
# <CountryName>United States</CountryName>
# <RegionCode>CA</RegionCode>
# <RegionName>California</RegionName>
# <City>Mountain View</City>
# <ZipCode>94043</ZipCode>
# <TimeZone>America/Los_Angeles</TimeZone>
# <Latitude>37.4192</Latitude>
# <Longitude>-122.0574</Longitude>
# <MetroCode>807</MetroCode>
# </Response>
#
# <Response>
# <IP>121.165.11.161</IP>
# <CountryCode>KR</CountryCode>
# <CountryName>Republic of Korea</CountryName>
# <RegionCode>41</RegionCode>
# <RegionName>Gyeonggi-do</RegionName>
# <City>Suwon</City>
# <ZipCode></ZipCode>
# <TimeZone>Asia/Seoul</TimeZone>
# <Latitude>37.2911</Latitude>
# <Longitude>127.0089</Longitude>
# <MetroCode>0</MetroCode>
# </Response>


for host in hosts:
recvd = requests.get('http://www.freegeoip.net/json/' + host)
geo = json.loads(recvd.text)
print(geo)

# {'longitude': -71.0843, 'zip_code': '02142', 'time_zone': 'America/New_York', 'ip': '23.193.193.7', 'latitude': 42.3626, 'city': 'Cambridge', 'country_code': 'US', 'country_name': 'United States', 'metro_code': 506, 'region_code': 'MA', 'region_name': 'Massachusetts'}
# {'longitude': -122.3933, 'zip_code': '94107', 'time_zone': 'America/Los_Angeles', 'ip': '192.30.253.112', 'latitude': 37.7697, 'city': 'San Francisco', 'country_code': 'US', 'country_name': 'United States', 'metro_code': 807, 'region_code': 'CA', 'region_name': 'California'}
# {'longitude': 126.9741, 'zip_code': '', 'time_zone': 'Asia/Seoul', 'ip': '202.131.30.11', 'latitude': 37.5112, 'city': '', 'country_code': 'KR', 'country_name': 'Republic of Korea', 'metro_code': 0, 'region_code': '', 'region_name': ''}
# {'longitude': -122.0574, 'zip_code': '94043', 'time_zone': 'America/Los_Angeles', 'ip': '173.194.126.240', 'latitude': 37.4192, 'city': 'Mountain View', 'country_code': 'US', 'country_name': 'United States', 'metro_code': 807, 'region_code': 'CA', 'region_name': 'California'}
# {'longitude': 127.0089, 'zip_code': '', 'time_zone': 'Asia/Seoul', 'ip': '121.165.11.161', 'latitude': 37.2911, 'city': 'Suwon', 'country_code': 'KR', 'country_name': 'Republic of Korea', 'metro_code': 0, 'region_code': '41', 'region_name': 'Gyeonggi-do'}


for host in hosts:
recvd = requests.get('http://www.freegeoip.net/csv/' + host)
print(recvd.text)

# 23.193.193.7,US,United States,MA,Massachusetts,Cambridge,02142,America/New_York,42.3626,-71.0843,506
# 192.30.253.112,US,United States,CA,California,San Francisco,94107,America/Los_Angeles,37.7697,-122.3933,807
# 202.131.30.11,KR,Republic of Korea,,,,,Asia/Seoul,37.5112,126.9741,0
# 173.194.126.240,US,United States,CA,California,Mountain View,94043,America/Los_Angeles,37.4192,-122.0574,807
# 121.165.11.161,KR,Republic of Korea,41,Gyeonggi-do,Suwon,,Asia/Seoul,37.2911,127.0089,0

'파이썬' 카테고리의 다른 글

import 정리  (0) 2017.05.01
현재 파일에 정의된 main 함수 호출  (0) 2017.04.19
줄 바꿈 없이 계속 출력  (0) 2017.04.19
numba - 성능 업!  (0) 2017.04.18
pprint : 쉽고 예쁘게 출력하기  (0) 2017.04.09

import 정리

교육생의 질문에 답변하기 위해 import 관련된 것을 찾아 보았는데..
바보같게도 질문을 엉뚱하게 해석하는 바람에
import 예제를 짧게 만들어서 정리하게 되었다. 검색한 시간이 아까워서.

두 개의 파일이 있다.
second.py 파일에서 first.py에 포함된 odds, evens, dummy 객체에 접근하는 문법을 설명한다.
코드와 설명이 간단해서 따로 코드에 대한 설명은 하지 않는다.


# first.py
def dummy(a):
print('dummy :', a)

odds = [1, 3, 5, 7, 9]
evens = [0, 2, 4, 6, 8]

if __name__ == '__main__':
# if문 안에 있지 않으면, 모든 형태의 import에 대해서 호출됨.
print('===== {} ====='.format(__name__))


# second.py
# 1번
# first 파일에 포함된 모든 내용을 가져옴
# 파일에 포함된 내용에 접근할 때, 파일 이름과 함께 사용해야 함
import first

print(first.odds) # 변수 접근
first.dummy(123) # 함수 접근


# second.py
# 2번
# first 파일에 포함된 모든 내용을 가져오는 것은 동일.
# 파일에 포함된 내용에 접근할 때, 파일 이름을 사용하지 않아도 됨.
# 다만 함수 이름이 중복될 수 있기 때문에 주의가 필요함.
from first import *

print(odds)
dummy(123)


# second.py
# 3번
# first 파일로부터 원하는 내용만 가져옴.
# 명시하지 않은 내용에는 접근할 수 없음.
# 2번과 마찬가지로 접근할 때는 파일 이름 없이 접근.
from first import odds, dummy

print(odds)
dummy(123)
# print(evens) # NameError: name 'evens' is not defined

'파이썬' 카테고리의 다른 글

IP 주소를 위도와 경도로 변환  (0) 2017.06.25
현재 파일에 정의된 main 함수 호출  (0) 2017.04.19
줄 바꿈 없이 계속 출력  (0) 2017.04.19
numba - 성능 업!  (0) 2017.04.18
pprint : 쉽고 예쁘게 출력하기  (0) 2017.04.09

현재 파일에 정의된 main 함수 호출

머신러닝 코드를 분석하다 보니
잘 모르고 있던 파이썬 문법을 보게 된다.
머신러닝이 느는 것 같지는 않지만, 파이썬이라도 건져서 다행이다.

sys 모듈에 modules라는 속성이 있는데..
현재 로딩되어 있는 모듈을 딕셔너리 형태로 갖고 있다.
먼저 간단하게 출력을 하는 코드이다.

import sys

print(type(sys.modules))

for k, v in sys.modules.items():
print('{}\n **{}'.format(k, v))

# [출력 결과]
# <class 'dict'>
# zipimport
# **<module 'zipimport' (built-in)>
# apport_python_hook
# **<module 'apport_python_hook' from '/usr/lib/python3/dist-packages/apport_python_hook.py'>
#
# 중략...
#
# encodings
# **<module 'encodings' from '/usr/lib/python3.5/encodings/__init__.py'>
# _collections
# **<module '_collections' (built-in)>


다음 코드는 sys.modules에 포함된 값을 사용해서
현재 파일에 정의된 main 함수를 호출한다.
main과 sub 함수를 정의하지 않았다면, 호출 시점에서 에러가 발생한다.

# t_4.py
import sys

def main(_):
print('hello, main...')

def sub():
print('goodbye, sub...')

print(sys.modules[__name__])
print(sys.modules[__name__].main)
sys.modules[__name__].main(None)

print(sys.modules['__main__'])
print(sys.modules['__main__'].sub)
sys.modules['__main__'].sub()

# [출력 결과]
# <module '__main__' from '/home/python-kim/바탕화면/ml/t_4.py'>
# <function main at 0x7f868790df28>
# hello, main...
# <module '__main__' from '/home/python-kim/바탕화면/ml/t_4.py'>
# <function sub at 0x7f8687903048>
# goodbye, sub...

현재 파일의 이름은 t_4.py이고,
파일이 하나뿐이므로 __name__ 변수에는 '__main__' 문자열이 들어가 있다.
다시 말해 __name__으로 호출하나 '__main__'으로 호출하나 차이가 없다.
modules에 포함된 현재 파일을 사용해서 함수를 호출하는 코드일 뿐이다.


조금 어려워지는데..
다음 코드는 t_4.py 파일을 포함하고 있는 새로운 파일이다.
여기서 t_4.py 파일의 내용을 가져오기 때문에
t_4.py 파일에서 __name__은 '__main__'이 되는 것이 아니라
파일의 실제 이름인 't_4.py'가 된다.
이 말은 sys.modules[__name__].main은 정상적으로 동작하지만
sys.modules['__main__'].sub는 에러가 발생한다는 뜻이다.

# t_6.py
import t_4

print('end of t_6')

# [출력 결과]
# <module 't_4' from '/home/python-kim/바탕화면/ml/t_4.py'>
# <function main at 0x7f11b784f048>
# hello, main...
# <module '__main__' from '/home/python-kim/바탕화면/ml/t_6.py'>
# Traceback (most recent call last):
# File "/home/python-kim/바탕화면/ml/t_6.py", line 1, in <module>
# import t_4
# File "/home/python-kim/바탕화면/ml/t_4.py", line 22, in <module>
# print(sys.modules['__main__'].sub)
# AttributeError: module '__main__' has no attribute 'sub'

'파이썬' 카테고리의 다른 글

IP 주소를 위도와 경도로 변환  (0) 2017.06.25
import 정리  (0) 2017.05.01
줄 바꿈 없이 계속 출력  (0) 2017.04.19
numba - 성능 업!  (0) 2017.04.18
pprint : 쉽고 예쁘게 출력하기  (0) 2017.04.09

줄 바꿈 없이 계속 출력

이게 C 언어에서도 가능할텐데..
왜 모르고 살았는지.. 이해가 안 가는 기능을 하나 찾았다.

개행문자를 출력하면 줄이 바뀐다.
윈도우와 리눅스는 개행문자를 표기하는 방법이 틀린데..
윈도우는 \r\n을 쓰고, 리눅스는 \n만 사용한다.

\r은 CR(Carriage Return)이라 부르고, 입력 위치를 줄의 처음으로 옮긴다.
\n은 LF(Line Feed)라 부르고, 입력 위치를 다음 줄로 옮긴다.
옛날 타자기를 본따서 입력 장치를 만들었기 때문에 생긴 현상이다.

표기하는 방법은 틀리지만
리눅스나 윈도우에서 모두, 입력 위치를 다음 줄의 첫 번째로 옮기는 역할을 한다.
리눅스 파일을 윈도우에서 열 때
줄 바꿈에 문제가 발생하는 경우가 있는데, 이것 때문에 그렇다.


아래 코드를 구동시키면,
숫자가 오른쪽으로 이동하면서 출력되는 것이 아니라
같은 자리에서 계속해서 바뀌는 것을 볼 수 있다.
놀라움의 비밀은 \r에 있다. 줄 바꿈 없이 줄의 처음으로 이동하기 때문에 가능하다.
터미널에서 파일을 다운로드할 때,
같은 위치에 다운로드한 파일의 크기를 계속해서 수정할 때 주로 사용된다.


import sys
import time

for i in range(10):
print('{}\r'.format(i), end='')
sys.stdout.flush()
time.sleep(1)

sys.stdout.flush 함수는 호출하지 않아도 된다.
미뤄두었다 출력하는 것이 아니라
바로 출력하라는 코드인데.. 여기서는 중요하지 않다.

'파이썬' 카테고리의 다른 글

import 정리  (0) 2017.05.01
현재 파일에 정의된 main 함수 호출  (0) 2017.04.19
numba - 성능 업!  (0) 2017.04.18
pprint : 쉽고 예쁘게 출력하기  (0) 2017.04.09
enum 클래스  (0) 2017.04.09

numba - 성능 업!

파이썬을 사용하는 목적에 성능은 포함되어 있지 않다고 엄청 강조한다.
그렇지만 가끔은 성능이 좋아졌으면 하고 바랄 때가 있다.
결과를 보기 위해 몇 시간을 구동해야 한다면 말이다.

"High Performance Python(고성능 파이썬)"을 읽다 보니
책 제목답게 성능과 관련한 내용이 많았다.
당연히 나에게는 중요하지 않았다.
그런데, 파이썬 코드를 컴파일하는 챕터에서
거저 먹을 수 있는 몇 가지 방법을 알려주고 있었다.

numba 홈페이지에 가면 조금 무섭다.
괜히 설치되지 않을 것 같은 느낌!

  numba 홈페이지

정말 다행스럽게 파이참에서 바로 설치할 수 있었다.
어.. 일단 설치는 우분투 파이참에서 성공했다.
설정 들어가서 인터프리터로 이동한 다음 numba 검색 후에 설치.
혹시 맥이나 윈도우에서는 안될 수도 있겠다.
홈페이지의 분위기로 봐서는.
(안타깝게도 맥에서는 실패. 파이참에서 자동 설치가 되지 않는다.
터미널에서 설치할 때, llvmlite 모듈이 필요한데.. 이거 설치하면서 에러가 발생한다.
다른 사람들은 잘 되는 듯.. 나중에 다시 도전!!)

두 가지만 기억한다.

  1. from numba import jit
    파일 상단에 numba를 import 하는데 사용하는 문법
  2. @jit
    컴파일을 적용하려고 하는 함수 앞에 추가하는 문법
    numba는 jit(just in time) 컴파일러 기반이라서, 적용 함수의 실행 코드를 미리 생성한다는 뜻이다.

numba 홈페이지에 있는 코드 두 가지를 소개한다.
첫 번째는 기본 문법이 적용되는지 확인하기 위한 코드로
앞에서 언급한 jit를 import하고 @jit를 함수 앞에 추가한 부분만 보면 된다.

from numba import jit
from numpy import arange

# jit decorator tells Numba to compile this function.
# The argument types will be inferred by Numba when function is called.
@jit
def sum2d(arr):
M, N = arr.shape
result = 0.0
for i in range(M):
for j in range(N):
result += arr[i,j]
return result

a = arange(9).reshape(3,3)
print(sum2d(a))

# [출력 결과]
# 36.0


두 번째 코드는 만델브로트 집합을 이미지로 표시하는 코드이다.
numba를 적용했을 때와 하지 않았을 때의 성능을 비교했다.
와우!!
26.5배 차이가 났다. 몇 줄 넣었을 뿐인데..
numba를 적용하기 위해 코드를 하나도 수정하지 않았는데..

#! /usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import

from timeit import default_timer as timer
from matplotlib.pylab import imshow, jet, show, ion
import numpy as np

from numba import jit


@jit
def mandel(x, y, max_iters):
"""
Given the real and imaginary parts of a complex number,
determine if it is a candidate for membership in the Mandelbrot
set given a fixed number of iterations.
"""
i = 0
c = complex(x,y)
z = 0.0j
for i in range(max_iters):
z = z*z + c
if (z.real*z.real + z.imag*z.imag) >= 4:
return i

return 255

@jit
def create_fractal(min_x, max_x, min_y, max_y, image, iters):
height = image.shape[0]
width = image.shape[1]

pixel_size_x = (max_x - min_x) / width
pixel_size_y = (max_y - min_y) / height
for x in range(width):
real = min_x + x * pixel_size_x
for y in range(height):
imag = min_y + y * pixel_size_y
color = mandel(real, imag, iters)
image[y, x] = color

return image

image = np.zeros((500 * 2, 750 * 2), dtype=np.uint8)
s = timer()
create_fractal(-2.0, 1.0, -1.0, 1.0, image, 20)
e = timer()
print(e - s)
imshow(image)
# jet()
# ion()
show()
# [출력 결과]
# 적용 후 : 0.18618797000090126
# 적용 전 : 4.946132005999971

함수 앞에 적용한 @jit 데코레이터가 있을 때와 없을 때로 각각 구동했다.
적용했을 때는 결과가 바로 표시된다.
반면 적용하지 않았을 때는 꽤나 기다려야 된다.

pprint : 쉽고 예쁘게 출력하기

문자열에 포함된 format 함수를 사용하면
일반적으로 가장 보기 좋은 출력 형태를 생성할 수 있다.
그런데, format 함수는 전달해야 할 것이 많아서 번거로운 느낌이 들 때가 많다.
이럴 때 조금 부족하기는 하지만,
pprint 모듈을 사용하면 상당 부분 절충하는 것이 가능하다.
pprint 모듈을 정리하면서 개인적으로 사용하던 unpack 문법을 함께 정리했다.

리스트와 딕셔너리에서 요소가 많을 경우
한 줄로 출력하는 것은 읽기가 힘들어서 의미 없는 경우가 많다.
핵심은 각각의 요소를 다른 줄에 출력하는 것이다.
리스트와 딕셔너리에 대한 예제를 각각 정리했다.


첫 번째는 리스트를 간결하게 출력하는 코드이다. 출력 결과는 코드 바로 아래 실어 두었다.

import pprint

numbers = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
print(numbers)
print(*numbers)
print(*numbers, sep='\n')

# 출력 결과
# [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
# [1, 2, 3] [4, 5] [6, 7, 8, 9]
# [1, 2, 3]
# [4, 5]
# [6, 7, 8, 9]

print('-'*50)

print('** pprint **')
pprint.pprint(numbers)
pprint.pprint(numbers, width=20)
pprint.pprint(numbers, width=20, indent=4)

# 출력 결과
# [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
# [[1, 2, 3],
# [4, 5],
# [6, 7, 8, 9]]
# [ [1, 2, 3],
# [4, 5],
# [6, 7, 8, 9]]

print('** pprint.PrettyPrinter **')
pp = pprint.PrettyPrinter(width=20, indent=4)
pp.pprint(numbers)

# 출력 결과
# [ [1, 2, 3],
# [4, 5],
# [6, 7, 8, 9]]


두 번째는 딕셔너리를 출력하는 코드이다.

import pprint

info = dict(name='kim', age=20, addr='yongin')

print(info)
print(*info)

# 출력 결과
# {'addr': 'yongin', 'age': 20, 'name': 'kim'}
# addr age name

print('-'*50)

print([k for k in info])
print([(k, info[k]) for k in info])
print(*[(k, info[k]) for k in info])

# 출력 결과
# ['addr', 'age', 'name']
# [('addr', 'yongin'), ('age', 20), ('name', 'kim')]
# ('addr', 'yongin') ('age', 20) ('name', 'kim')

print('-'*50)

print(*[(k, info[k]) for k in info], sep='\n')
print(*['{}: {}'.format(k, v) for k, v in info.items()], sep='\n')

# 출력 결과
# ('addr', 'yongin')
# ('age', 20)
# ('name', 'kim')
# addr: yongin
# age: 20
# name: kim

print('-'*50)

print('** pprint **')
pprint.pprint(info)
pprint.pprint(info, width=20)
pprint.pprint(info, width=20, indent=4)

# 출력 결과
# {'addr': 'yongin', 'age': 20, 'name': 'kim'}
# {'addr': 'yongin',
# 'age': 20,
# 'name': 'kim'}
# { 'addr': 'yongin',
# 'age': 20,
# 'name': 'kim'}

print('** pprint.PrettyPrinter **')
pp = pprint.PrettyPrinter(width=20, indent=4)
pp.pprint(info)

# 출력 결과
# { 'addr': 'yongin',
# 'age': 20,
# 'name': 'kim'}

'파이썬' 카테고리의 다른 글

줄 바꿈 없이 계속 출력  (0) 2017.04.19
numba - 성능 업!  (0) 2017.04.18
enum 클래스  (0) 2017.04.09
구조체로 사용할 수 있는 collections.namedtuple  (0) 2017.04.09
defaultdict 사용법  (0) 2017.04.06

enum 클래스

수업할 때마다 파이썬에서는 상수를 정의할 수 없다라고 했느데,
이제는 그런 말을 할 수 없게 되었다.
3.4버전부터 추가된 내용으로 파이썬에서도 상수의 즐거움을 누릴 수 있게 되었다.
"파이썬 라이브러리 레시피"에서 발견한 두 번째 유익한 코드를 정리해 봤다.

enum은 상수를 정의할 때 쓰는 막강한 클래스를 말한다.
enumerate의 약자로 "값을 나열한다"는 뜻을 갖고 있다.
참.. 우리 말로는 열거형이라고 부른다.


import enum

# 여기서는 편의상 정수로만 지정했지만, 데이터 종류에 상관없이 상수를 지정할 수 있다.
# 실수, 문자열, 리스트 등의 모든 자료형을 사용할 수 있다.
class Color(enum.Enum):
RED = 0
GREEN = 1
BLUE = 2

print(Color) # <enum 'Color'>
print(Color.RED, Color.BLUE, Color.GREEN) # Color.RED Color.BLUE Color.GREEN

c = Color.RED
print(c) # Color.RED
c = Color.BLUE
print(c) # Color.BLUE

print(c.name) # BLUE
print(c.value) # 2
print(repr(Color.RED)) # <Color.RED: 0>
print(type(Color.RED)) # <enum 'Color'>
print(Color(0), Color(2)) # Color.RED Color.BLUE
print(Color['RED'], Color['BLUE']) # Color.RED Color.BLUE

if c == Color.RED: # Color is BLUE.
print('Color is RED.')
elif c == Color.GREEN:
print('Color is GREEN.')
else:
print('Color is BLUE.')

# enum을 정수와 비교하는 것은 항상 False.
if c == c.value: # DIFF.
print('SAME.')
else:
print('DIFF.')

# 리스트 인덱스로 사용
a = ['red', 'green', 'blue']

# print(a[c]) # error
# print(a[int(c)]) # error
print(a[c.value]) # blue
print('-'*50)

# 반복문에 적용할 수 있고, 아래와 같이 출력된다.
# Color.RED, Color.GREEN, Color.BLUE
for name in Color:
print(name)

names = list(Color)
print(names) # [<Color.RED: 0>, <Color.GREEN: 1>, <Color.BLUE: 2>]
print(names[0], names[-1]) # Color.RED Color.BLUE
print(names[0].name, names[0].value) # RED 0

print('-'*50)

# enum 클래스가 유일한 값들로만 구성되어야 한다면 unique 장식자(decoration) 사용.
# @enum.unique를 지정하면 아래 코드는 에러가 발생한다. 장식자는 클래스 바로 앞에 사용해야 한다.

# @enum.unique
class Foods(enum.Enum):
SNACK = 0
COKE = 1
CIDER = 1
JUICE = 1
PIZZA = 2
BURGER = 2


# 정수로만 구성된 상수 클래스 정의. 실수를 지정하면 정수로 자동 변환된다.
class City(enum.IntEnum):
SEOUL = 1
PUSAN = 2
INCHON = 3.3

print(City.SEOUL, City.PUSAN, City.INCHON) # City.SEOUL City.PUSAN City.INCHON
print(City.INCHON.name, City.INCHON.value) # INCHON 3
print('-'*50)

# dict 클래스(딕셔너리)의 key로 사용 가능
apples = {}
apples[Color.RED] = 'red delicious'
apples[Color.GREEN] = 'granny smith'
print(apples) # {<Color.GREEN: 1>: 'granny smith', <Color.RED: 0>: 'red delicious'}

# 3.6 버전에서는 auto 함수 가능. 1부터 시작하고 1씩 증가.
class NewColor(enum.Enum):
RED = enum.auto()
GREEN = enum.auto()
BLUE = enum.auto()

print(list(NewColor)) # [<NewColor.RED: 1>, <NewColor.BLUE: 2>, <NewColor.GREEN: 3>]

'파이썬' 카테고리의 다른 글

numba - 성능 업!  (0) 2017.04.18
pprint : 쉽고 예쁘게 출력하기  (0) 2017.04.09
구조체로 사용할 수 있는 collections.namedtuple  (0) 2017.04.09
defaultdict 사용법  (0) 2017.04.06
정규분포와 누적분포 비교 그래프  (0) 2017.04.06

구조체로 사용할 수 있는 collections.namedtuple

여러 개의 데이터 멤버를 갖는 초간단 클래스를 C 언어에서는 구조체라고 한다.
파이썬에서도 초간단 클래스가 필요하고
namedtuple 모듈을 사용해서 구조체 관련 기능을 지원한다.
늘 이런 기능이 있으면 좋겠다고 생각했었는데.. 찾아볼 생각을 하지 못했다.
"파이썬 라이브러리 레시피"를 읽는 중에 발견한 첫 번째 유용한 기능이다. ^^


def namedtuple(typename, field_names, verbose=False, rename=False)

  • typename : 클래스(자료형)에 사용할 이름
  • field_names : 요소로 사용할 이름들. 리스트 또는 튜플 등의 iterable 객체 전달
  • verbose : 생성된 클래스 명세를 출력할 것인지 말 것인지. True라면 출력.
  • rename : 잘못된 요소 이름을 자동으로 변환. True라면 변환.


from collections import namedtuple

# 'Coordinate'를 전달하는데, coord 변수에 저장하는 것이 처음에는 무척 이상했다.
# coord는 Coordinate 클래스의 별명이라고 보면 된다.
# Coordinate 클래스를 직접 노출시킬 수 없어서 coord로 대신 사용할 뿐이다.
# 그래서, namedtuple 모듈을 사용할 때는
# 변수와 자료형 이름을 동일하게 주는 것이 일반적이다. 다음처럼.
# Coordinate = namedtuple('Coordinate', 'x y z')

# namedtuple을 생성할 수 있는 여러 가지 방법. 문자열 요소 지정도 가능하다.
# coord = namedtuple('Coordinate', 'x y z')
# coord = namedtuple('Coordinate', 'x, y, z')
coord = namedtuple('Coordinate', ['x', 'y', 'z'])

# 변수를 만드는 여러 가지 방법
# pos = coord(1.2, 3.4, 5.6) # position argument
# pos = coord(x=1.2, y=3.4, z=5.6) # keyword argument
pos = coord._make((1, 2, 3)) # class method

print(pos) # Coordinate(x=1, y=2, z=3)
print(pos.x, pos.y, pos.z) # 1 2 3

# 쓸모는 없지만, 반복문에 적용할 수 있다.
for i in pos:
print(i) # 1 2 3

print(pos._fields) # ('x', 'y', 'z')
print('coord :', coord) # coord : <class '__main__.Coordinate'>

print('-'*50)
# locals 함수를 호출하면, coord는 있지만 Coordinate는 보이지 않는다.
# 그래서, Coordinate 클래스를 직접 사용할 수 있는 방법은 존재하지 않는다.
# 중요한 점은 coord가 <class '__main__.Coordinate'>라고 되어 있기 때문에
# Coordinate 클래스처럼 사용할 수 있다는 점이다.
print(locals()) # {'pos': Coordinate(x=1, y=2, z=3),
# 'namedtuple': <function namedtuple at 0x10cb977b8>,
# 'coord': <class '__main__.Coordinate'>,
# 'i': 3}

print('-'*50)

# 일반 클래스를 만들어서 확인해 보면
# 클래스 변수 d는 출력에 object라고 되어 있고 메모리 주소가 표시된다.
# 메모리를 할당 받은 변수라는 뜻이다.
# 반면 클래스 이름인 Dummy는 클래스 이름만 출력된다.
# 다시 말해 클래스 이름만 출력되면, 클래스 변수를 만들 수 있는 키워드라는 뜻이 되는 것이다.

class Dummy:
pass

d = Dummy()
print(d) # <__main__.Dummy object at 0x10fce4b70>
print(type(d)) # <class '__main__.Dummy'>
print(Dummy) # <class '__main__.Dummy'>


아래 코드는 namedtuple 함수가 생성하는 클래스 명세다.
verbose 매개변수를 True로 설정하면 볼 수 있다.

# coord = namedtuple('Coordinate', 'x, y, z', verbose=True)

from builtins import property as _property, tuple as _tuple
from operator import itemgetter as _itemgetter
from collections import OrderedDict

class Coordinate(tuple):
'Coordinate(x, y, z)'

__slots__ = ()

_fields = ('x', 'y', 'z')

def __new__(_cls, x, y, z):
'Create new instance of Coordinate(x, y, z)'
return _tuple.__new__(_cls, (x, y, z))

@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new Coordinate object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != 3:
raise TypeError('Expected 3 arguments, got %d' % len(result))
return result

def _replace(_self, **kwds):
'Return a new Coordinate object replacing specified fields with new values'
result = _self._make(map(kwds.pop, ('x', 'y', 'z'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % list(kwds))
return result

def __repr__(self):
'Return a nicely formatted representation string'
return self.__class__.__name__ + '(x=%r, y=%r, z=%r)' % self

def _asdict(self):
'Return a new OrderedDict which maps field names to their values.'
return OrderedDict(zip(self._fields, self))

def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self)

x = _property(_itemgetter(0), doc='Alias for field number 0')

y = _property(_itemgetter(1), doc='Alias for field number 1')

z = _property(_itemgetter(2), doc='Alias for field number 2')

'파이썬' 카테고리의 다른 글

pprint : 쉽고 예쁘게 출력하기  (0) 2017.04.09
enum 클래스  (0) 2017.04.09
defaultdict 사용법  (0) 2017.04.06
정규분포와 누적분포 비교 그래프  (0) 2017.04.06
defaultdict와 딕셔너리 응용 코드  (0) 2017.04.06

defaultdict 사용법

dict 클래스에 비해 별거 아닐 수 있지만 기특해 보이는 좋은 기능을 defaultdict 클래스가 갖고 있다.
잊어버릴 것 같아서 정리했다. 자주 사용할 수 있도록 하자. 아자!


from collections import Counter, defaultdict
from operator import itemgetter

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
print(s)

# 출력 결과
# [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]

# 1. 전통적인 dict 사용
t = {}
# for k, v in s:
# if k in t:
# t[k] += v
# else:
# t[k] = v

for k, v in s:
if not k in t:
t[k] = 0
t[k] += v

# 2. 전통적인 dict 사용. 불편하지만 오히려 쉬울 수도 있는 방법
# d = {}
# for k, v in s:
# d.setdefault(k, []).append(v)
#
# t = {}
# for k, v in d.items():
# t[k] = sum(v)

# 3. defaultdict 사용
t = defaultdict(int)
for k, v in s:
t[k] += v

print(sorted(t.items(), key=itemgetter(1)))

# 출력 결과
[('red', 1), ('yellow', 4), ('blue', 6)]

# --------------------------------------------------- #

# 같은 글자 갯수 세기
m = 'mississippi'

t1 = {} # dict
for c in m:
if not c in t1:
t1[c] = 0
t1[c] += 1

t2 = defaultdict(int) # defaultdict
for c in m:
t2[c] += 1

t3 = Counter(m) # Counter

print(sorted(t1.items(), key=itemgetter(1)))
print(sorted(t2.items(), key=itemgetter(1)))
print(sorted(t3.items(), key=itemgetter(1)))

# 출력 결과
# [('m', 1), ('p', 2), ('i', 4), ('s', 4)]
# [('m', 1), ('p', 2), ('i', 4), ('s', 4)]
# [('m', 1), ('p', 2), ('i', 4), ('s', 4)]


'파이썬' 카테고리의 다른 글

enum 클래스  (0) 2017.04.09
구조체로 사용할 수 있는 collections.namedtuple  (0) 2017.04.09
정규분포와 누적분포 비교 그래프  (0) 2017.04.06
defaultdict와 딕셔너리 응용 코드  (0) 2017.04.06
matplotlib colormap  (0) 2017.04.05

정규분포와 누적분포 비교 그래프

통계는 머신러닝에 필수라고 생각하지 않고 싶지만, 하다 보면 필수인 것 같은 느낌을 지울 수가 없다.
용어에 대한 이해가 선행되면 훨씬 쉽게 머신러닝을 할 수 있을텐데..
코딩을 직업으로 삼아서인지
모든 개념을 코드로 구현해야 명확해지는 병을 얻게 되었다.

"밑바닥부터 시작하는 데이터 과학"에서 발췌했고,
정규분포와 누적분포를 그래프를 사용해서 시각적으로 표시하는 코드이다.


import math
import matplotlib.pyplot as plt

# 정규분포의 밀도 함수
def normal_pdf(x, mu=0, sigma=1):
return math.exp(((x-mu) ** 2) / (2 * sigma ** 2)) / (math.sqrt(2 * math.pi) * sigma)

# 정규분포의 누적분포 함수
# erf : 오차 함수(error function)는 확률론, 통계학, 편미분 방정식등에서 사용하는 비초등 함수이다. 가우스 오차 함수라고도 한다.
# 오차함수는 정규 분포의 누적분포함수와 본질적으로 동일하다.
def normal_cdf(x, mu=0, sigma=1):
# erf 공식에서는 mu와 sigma는 빠져있다. 여기서는 평행 이동 및 밀도를 보여주기 때문에 추가되었다.
return (1 + math.erf((x-mu)/math.sqrt(2) / sigma)) / 2


xs = [x/10 for x in range(-50, 50)]

plt.subplot(211)
plt.plot(xs, [normal_pdf(x, sigma=1) for x in xs], '-', label='mu=0, sigma=1')
plt.plot(xs, [normal_pdf(x, sigma=2) for x in xs], '--', label='mu=0, sigma=2')
plt.plot(xs, [normal_pdf(x, sigma=0.5) for x in xs], ':', label='mu=0, sigma=0.5')
plt.plot(xs, [normal_pdf(x, mu=-1) for x in xs], '-', label='mu=-1, sigma=1')
plt.legend()

plt.subplot(212)
plt.plot(xs, [normal_cdf(x, sigma=1) for x in xs], '-', label='mu=0, sigma=1')
plt.plot(xs, [normal_cdf(x, sigma=2) for x in xs], '--', label='mu=0, sigma=2')
plt.plot(xs, [normal_cdf(x, sigma=0.5) for x in xs], ':', label='mu=0, sigma=0.5')
plt.plot(xs, [normal_cdf(x, mu=-1) for x in xs], '-', label='mu=-1, sigma=1')
plt.legend(loc=4)

plt.show()

'파이썬' 카테고리의 다른 글

enum 클래스  (0) 2017.04.09
구조체로 사용할 수 있는 collections.namedtuple  (0) 2017.04.09
defaultdict 사용법  (0) 2017.04.06
defaultdict와 딕셔너리 응용 코드  (0) 2017.04.06
matplotlib colormap  (0) 2017.04.05

defaultdict와 딕셔너리 응용 코드

"밑바닥부터 시작하는 데이터 과학"의 챕터 1에서 발췌 및 수정.
이해하기 쉬운 문제들 일부 추출하고, 일부는 새롭게 구성.
구글에서 "id": 0, "name": "Hero" 검색하면 소스코드로 바로 이동할 수 있다.


아래 코드는 뒤에서 제안할 문제에서 사용하는 데이터 정의.
이번 코드를 통해 dict 클래스 확장판인 defaultdict 클래스에 대해 알게 되면 좋겠다.

users = [
{ "id": 0, "name": "Hero" },
{ "id": 1, "name": "Dunn" },
{ "id": 2, "name": "Sue" },
{ "id": 3, "name": "Chi" },
{ "id": 4, "name": "Thor" },
{ "id": 5, "name": "Clive" },
{ "id": 6, "name": "Hicks" },
{ "id": 7, "name": "Devin" },
{ "id": 8, "name": "Kate" },
{ "id": 9, "name": "Klein" },
{ "id": 10, "name": "Jen" }
]

friendships = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (3, 4),
(4, 5), (5, 6), (5, 7), (6, 8), (7, 8), (8, 9)]

interests = [
(0, "Hadoop"), (0, "Big Data"), (0, "HBase"), (0, "Java"),
(0, "Spark"), (0, "Storm"), (0, "Cassandra"),
(1, "NoSQL"), (1, "MongoDB"), (1, "Cassandra"), (1, "HBase"),
(1, "Postgres"), (2, "Python"), (2, "scikit-learn"), (2, "scipy"),
(2, "numpy"), (2, "statsmodels"), (2, "pandas"), (3, "R"), (3, "Python"),
(3, "statistics"), (3, "regression"), (3, "probability"),
(4, "machine learning"), (4, "regression"), (4, "decision trees"),
(4, "libsvm"), (5, "Python"), (5, "R"), (5, "Java"), (5, "C++"),
(5, "Haskell"), (5, "programming languages"), (6, "statistics"),
(6, "probability"), (6, "mathematics"), (6, "theory"),
(7, "machine learning"), (7, "scikit-learn"), (7, "Mahout"),
(7, "neural networks"), (8, "neural networks"), (8, "deep learning"),
(8, "Big Data"), (8, "artificial intelligence"), (9, "Hadoop"),
(9, "Java"), (9, "MapReduce"), (9, "Big Data")
]

# 근속연수에 대한 연봉 : 사용하지 않는 데이터
salaries_and_tenures = [(83000, 8.7), (88000, 8.1),
(48000, 0.7), (76000, 6),
(69000, 6.5), (76000, 7.5),
(60000, 2.5), (83000, 10),
(48000, 1.9), (63000, 4.2)]


[문제 1번] users에 각각의 회원들에 대한 친구 필드를 추가하세요.

from collections import defaultdict

def find_member(users, member_id):
for u in users:
if u['id'] == member_id:
return u
return None


for u in users:
u['friend'] = []

# for i, j in friendships: # id가 순서를 보장할 수 없다면,
# u1 = find_member(users, i)
# u2 = find_member(users, j)
#
# u1['friend'].append(j)
# u2['friend'].append(i)

for i, j in friendships: # id가 순서를 보장한다면,
users[i]['friend'].append(j)
users[j]['friend'].append(i)

print(*users, sep='\n')
print('-'*50)

# [출력 결과]
# {'friend': [1, 2], 'id': 0, 'name': 'Hero'}
# {'friend': [0, 2, 3], 'id': 1, 'name': 'Dunn'}
# {'friend': [0, 1, 3], 'id': 2, 'name': 'Sue'}
# {'friend': [1, 2, 4], 'id': 3, 'name': 'Chi'}
# {'friend': [3, 5], 'id': 4, 'name': 'Thor'}
# {'friend': [4, 6, 7], 'id': 5, 'name': 'Clive'}
# {'friend': [5, 8], 'id': 6, 'name': 'Hicks'}
# {'friend': [5, 8], 'id': 7, 'name': 'Devin'}
# {'friend': [6, 7, 9], 'id': 8, 'name': 'Kate'}
# {'friend': [8], 'id': 9, 'name': 'Klein'}
# {'friend': [], 'id': 10, 'name': 'Jen'}


[문제 2] 친구가 가장 많은 사람과 적은 사람을 출력하세요.

users_by_friends = sorted(users, key=lambda d: len(d['friend']), reverse=True)
print(users[0])
print(users[-1])
print('.'*50)
print(*users_by_friends, sep='\n')
print('-'*50)

# [출력 결과]
# {'friend': [1, 2], 'id': 0, 'name': 'Hero'}
# {'friend': [], 'id': 10, 'name': 'Jen'}
# ..................................................
# {'friend': [0, 2, 3], 'id': 1, 'name': 'Dunn'}
# {'friend': [0, 1, 3], 'id': 2, 'name': 'Sue'}
# {'friend': [1, 2, 4], 'id': 3, 'name': 'Chi'}
# {'friend': [4, 6, 7], 'id': 5, 'name': 'Clive'}
# {'friend': [6, 7, 9], 'id': 8, 'name': 'Kate'}
# {'friend': [1, 2], 'id': 0, 'name': 'Hero'}
# {'friend': [3, 5], 'id': 4, 'name': 'Thor'}
# {'friend': [5, 8], 'id': 6, 'name': 'Hicks'}
# {'friend': [5, 8], 'id': 7, 'name': 'Devin'}
# {'friend': [8], 'id': 9, 'name': 'Klein'}
# {'friend': [], 'id': 10, 'name': 'Jen'}


[문제 3] 친구가 한 명도 없는 사람들을 출력하세요.

no_friend = [d for d in users if not d['friend']]
print(*no_friend, sep='\n')
print('-'*50)

# [출력 결과]
# {'friend': [], 'id': 10, 'name': 'Jen'}


[문제 4] 친구가 3명 이상인 사람들을 출력하세요.

more_3 = [d for d in users if len(d['friend']) >= 3]
print(*more_3, sep='\n')
print('-'*50)

# [출력 결과]
# {'friend': [0, 2, 3], 'id': 1, 'name': 'Dunn'}
# {'friend': [0, 1, 3], 'id': 2, 'name': 'Sue'}
# {'friend': [1, 2, 4], 'id': 3, 'name': 'Chi'}
# {'friend': [4, 6, 7], 'id': 5, 'name': 'Clive'}
# {'friend': [6, 7, 9], 'id': 8, 'name': 'Kate'}


[문제 5] 회원이 관심 갖는 주제들을 출력하세요.

def find_interest_by_member(interests, member_id):
finds = []
for m_id, interest in interests:
if m_id == member_id:
finds.append(interest)

return finds

print(find_interest_by_member(interests, 1))

users_by_member = defaultdict(list)
for m_id, interest in interests:
users_by_member[m_id].append(interest)

# print(*users_by_member.items(), sep='\n')
print(users_by_member[1])
print('-'*50)

# [출력 결과]
# ['NoSQL', 'MongoDB', 'Cassandra', 'HBase', 'Postgres']
# ['NoSQL', 'MongoDB', 'Cassandra', 'HBase', 'Postgres']


[문제 6] hadoop에 관심 있는 사람들을 출력하세요.

def find_members_by_interest(interests, word):
finds = []
for m_id, interest in interests:
if interest == word:
finds.append(m_id)

return finds

print(find_members_by_interest(interests, 'Hadoop'))

interest_users = defaultdict(list)
for m_id, interest in interests:
interest_users[interest].append(m_id)

# print(*interest_users.items(), sep='\n')
print(interest_users['Hadoop'])
print('-'*50)

# [출력 결과]
# [0, 9]
# [0, 9]


[문제 7] 가장 많은 관심을 갖는 주제 3가지를 출력하세요.

from collections import Counter
from operator import itemgetter

c = defaultdict(int)
for _, word in interests:
c[word] += 1

# c = Counter([word for _, word in interests])
c = sorted(c.items(), key=itemgetter(1))
print(c[:-4:-1])
print(c[::-1])

# [출력 결과]
# [('Python', 3), ('Java', 3), ('Big Data', 3)]
# [('Python', 3), ('Java', 3), ('Big Data', 3), ('R', 2), <이하 생략>

'파이썬' 카테고리의 다른 글

enum 클래스  (0) 2017.04.09
구조체로 사용할 수 있는 collections.namedtuple  (0) 2017.04.09
defaultdict 사용법  (0) 2017.04.06
정규분포와 누적분포 비교 그래프  (0) 2017.04.06
matplotlib colormap  (0) 2017.04.05

matplotlib colormap

colormap에 대한 설명을 찾기가 어렵다.
자세하게 설명을 붙여서 정리하고 싶은데, colormap 사용법은 많은데 설명은 없다.
matplotlib 소스코드를 분석하는 중에
colormap을 알아야 하는 상황에 처하게 되서 부족하지만 정리해 본다.


내가 생각할 때는
숫자를 색상에 매핑시키기 위한 색상 지도이다.
다만 테이블처럼 2차원 형태가 아니라 30cm 자와 같이 1차원 형태의 색상 배열이다.

갖고 있는 데이터를 그래프에 표시할 때
일반적인 색상을 사용해서 표시하는 것은 의미가 없다.
데이터를 색상으로 표현하는 것은 말이 되지 않는다.
그러나, 잘 정리된 colormap을 사용한다면 시각적으로 엄청난 효과를 거둘 수 있다.

가령, 지구를 평균 기온에 따라 표현한다고 했을 때
추운 지역은 파란색으로, 더운 지역은 빨간색으로 표시할 수 있다.
결국 지구상의 모든 영역은 파랑과 빨강이 연결된 그라데이션에 포함된 어떤 색상이 된다.
이와 같이 특정 데이터에 대해 사용될 수 있는 색상표를 colormap이라고 부른다.


# matplotlib 도움말


# 과학 데이터를 표시하기에 적합한 colormap 모음.
미국 지도에 인구 밀도에 따른 색상으로 표시한 그래픽은 압권.


# colormap 한글 설명.
많은 설명은 아니지만, 일부 매핑에 대해 상세한 설명을 제공한다.


아래 코드는 matplotlib 홈페이지에서 제공하는 colormap을 표시하는 코드로
구글링으로 나오는 코드 중에서 가장 간결하고 단순한 코드이다.
약간의 코드를 수정했고, 몰랐던 부분에 대해 주석을 붙였다.

import numpy as np
import matplotlib.pyplot as plt

a = np.linspace(0, 1, 256).reshape(1, -1) # (1, 256)
a = np.vstack((a,a)) # (2, 256)

# colormap 목록 표시. '_r'로 끝나는 이름은 reverse 버전.
maps = sorted(m for m in plt.cm.datad if not m.endswith('_r'))
nmaps = len(maps) + 1
print(*maps, sep='\n')

fig = plt.figure(figsize=(5, 10))
fig.subplots_adjust(top=0.99, bottom=0.01, left=0.2, right=0.99)

for i, m in enumerate(maps, 1):
ax = plt.subplot(nmaps, 1, i)
plt.axis("off") # 가로축과 세로축에 표시되는 숫자 및 테두리 제거

plt.imshow(a, aspect='auto', cmap=plt.get_cmap(m), origin='lower')

# subplot 영역 계산. x, 너비, 높이는 같고, y만 바뀜.
bounds = list(ax.get_position().bounds)
x, y, cx, cy = bounds

# y축에 출력되는 colormap 이름이 y축에 달라붙지 않도록 간격 띄움.
# 정렬(left, center, right)은 x 좌표가 왼쪽, 가운데, 오른쪽에 있음을 명시하는 것임.
# right를 줬다면 출력 문자열의 오른쪽에 x 좌표가 오게 된다.
fig.text(x - 0.01, y, m, fontsize=10, horizontalalignment='right')

plt.show()

'파이썬' 카테고리의 다른 글

enum 클래스  (0) 2017.04.09
구조체로 사용할 수 있는 collections.namedtuple  (0) 2017.04.09
defaultdict 사용법  (0) 2017.04.06
정규분포와 누적분포 비교 그래프  (0) 2017.04.06
defaultdict와 딕셔너리 응용 코드  (0) 2017.04.06