Tools/Python (38)

반응형

Pandas 에서 반복을 효율적으로 처리하는 방법


Pandas 를 통해 데이터 프로세싱을 할 때 종종 해야할일은 행에 반복적으로 접근을 하면서 그 값을 조작하는 일이다. 예를 들어, missing value 가 0 으로 코딩이 되어있는데, 이를 다른 값으로 바꾸고 싶을 경우 또는 A 컬럼의 값이 missing 일 때, B 컬럼의 값을 수정하고 싶은 경우 등이 있다. 이러한 작업을 하기 위해서는 모든 행을 조회 하면서 값을 조회하고 수정하는 일이 필요하다. 이번 포스팅에서는 이러한 반복작업이 필요한 상황에서 어떤 방법이 가장 효율적일지에 대해 정리해보려고한다.


사용할 데이터

diabetes.csv


1) pd.iterrows()


가장 기본적이고 많이 사용하는 방법이 iterrows 함수를 이용하는 것이다. 하지만 iterrows 함수는 다른 방법에 비해 느린 편이다. 

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

diabetes = pd.read_csv("diabetes.csv")
diabetes.head()


PregnanciesGlucoseBloodPressureSkinThicknessInsulinBMIDiabetesPedigreeFunctionAgeOutcome
061487235033.60.627501
11856629026.60.351310
28183640023.30.672321
318966239428.10.167210
40137403516843.12.288331


missing value 가 0 으로 코딩이 되어있는데, 이를 nan 으로 바꾸는 코드를 iterrows 를 이용해서 짜보자. 

def fix_missing(df, col):
    for i, row in df.iterrows():
        val = row[col]
        if val == 0:
            df.loc[i, col] = np.nan

%timeit fix_missing(diabetes, "SkinThickness")


33.9 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)



2) pd.loc[]/pd.iloc[]


두 번째 방법은 index 를 통해 for 문을 돌면서, loc 또는 iloc 함수를 이용해 dataframe의 row에 접근하는 방법이다. 

def fix_missing2(df, col):
    for i in df.index:
        val = df.loc[i, col]
        if val == 0 :
            df.loc[i, col] = np.nan

%timeit fix_missing2(diabetes, "Insulin")


9.54 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)


1) iterrow 방법에 비해 약 3배 빨라졌다는 것을 알 수 있다. 따라서 iterrows 가 익숙하다고 하더라도 다른 방법으로 바꾸는 것이 같은 작업을 더 빠르게 실행할 수 있어 효율적이다.  


3) pd.get_value()/pd.set_value()


다음은 위 방법과 마찬가지로 index를 통해 for 문을 돌면서 get_value 와 set_value 함수를 이용하는 방법이다. 2) 방법이 내부적으로 get_value, set_value를 호출하는 것이기 때문에 3) 이를 직접적으로 호출하는 방법이므로 더욱 빠르다. 

def fix_missing3(df, col):
    for i in df.index:
        val = df.get_value(i, col)
        if val == 0:
            df.set_value(i, col, np.nan)

%timeit fix_missing3(diabetes, "BMI")


3.65 ms ± 31.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


2)에 비해 3배 정도 빨라졌으며, 1)에 비해서는 거의 10배 정도의 속도차이가 난다. 


4) pd.apply()


네 번째 방법은 apply 를 이용하는 것이다. apply 를 이용하는 것은 특별한 형태의 function 을 필요로 하는데 (이를 helper function 이라고도 한다), 이것은 Series 혹은 Dataframe의 각 원소마다 적용시킬 함수이다. 

def fix_missing4(x):
    if x == 0 : 
        return -999
    else: return x
    
%timeit diabetes.Age.apply(fix_missing4)
483 µs ± 3.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3) 방법과 비교하여 3650/484 = 7.5 배 속도가 증가했다. apply 함수를 이용하는 것의 장점은 작업이 "비교적 간단할 때" 유용하다. 만약, 여러개의 column 에서 if 문을 적용하서 값을 다이나믹하게 바꾸어야하는 작업에 있어서는 apply 함수보다 for 문을 이용하는 방법이 더 적절할 수 있다. 

정리 
  • Default 방법으로 index 를 돌면서 set_value 와 get_value 를 호출하는 방법을 추천
  • 비교적 큰 데이터에셋에서 비교적 간단한 작업을 할 때, apply 함수가 가장 효율적


참고

https://medium.com/@rtjeannier/pandas-101-cont-9d061cb73bfc

반응형
반응형



Jupyter Lab – 단축키와 매직 기능

단축키 (Shortcut) 을 숙지하고 잘 사용할 수 있다면 더욱 생산적인 작업을 가능하게 할 것입니다. 본 포스팅에서는 자주 사용하는 Jupyter lab 의 단축키를 정리해보고자 합니다. 예를 들어, 주피터 랩에서는 새로운 cell 을 만들거나 작업이 끝난 cell 을 삭제할 일이 많습니다. 단축키를 사용하면, 단축키를 사용하지 않고 키보드를 통해 새로운 cell 을 추가하고 삭제하는 것보다 훨씬 빠르게 작업을 할 수 있습니다. 그리고 외우는데 시간이 오래걸리지도 않습니다!

Jupyter Lab의 단축키

  1. ESC  를 눌러 커맨드 모드로 진입하여 ENTER 를 통해 cell 을 수정할 수 있습니다. 아래 커맨드는 커맨드 모드에서 동작합니다. 
  2. A 는 현재 cell 위에 새로운 cell 을 추가합니다.
  3. B 는 현재 cell 밑에 새로운 cell 을 추가합니다.
  4. D + D D를 연속해서 두번 누르면 현재 cell 을 삭제합니다. 
  5. M 은 Markdown 셀로 변환하며, Y 는 Code 셀로 변환하고  R 은 Raw Cell 로 변환합니다.
  6. CTRL + B 화면을 더 크게 사용할 수 있습니다. 왼쪽 파일 탐색기가 사라집니다.
  7. SHIFT + M 두 개의 셀을 한개의 셀로 Merge 합니다.
  8. CTRL + SHIFT + – 현재 커서 위치를 기준으로 두 개의 셀로 구분합니다. 
  9. SHIFT+J or SHIFT + DOWN 현재 셀에서 아래쪽 위치로 새로운 셀을 같이 선택합니다. 
  10. SHIFT + K or SHIFT + UP 현재 셀에서 위쪽 위치로 새로운 셀을 같이 선택합니다. 
  11. CTRL + / 선택한 코드를 주석처리합니다.

기타 잘 알려지지 않은 단축키

  1. CTRL+D: 한줄 삭제하는 단축키입니다. 
  2. CTRL+[ or CTRL+]: 단체 indentation / Tab과 Shift+Tab 으로도 가능하지만, text editor 에서는 Shift+tab 이 안먹혀서 CTRL+[ 을 쓰면 유용합니다.  

Jupyter Lab 매직 기능 (Magic function)

매직 기능은 Ipython kernel 에서 제공하는 것이며, Jupyter lab 과 Jupyhter notebook 모두에서 작동합니다. 이는 어떤 언어를 선택하든 (예를 들어, Jupyter lab 에서 R을 사용하든 Python 을 사용하든) 동작합니다.

  1. %matplotlib inline 플롯을 화면 안에서 보여준다.
  2. %lsmagic 매직 기능에 어떤것들이 있는지 출력해준다. 
  3. %env 

    • %env 모든 환경변수를 출력한다.
    • %env var 해당 이름의 환경변수를 출력한다.
    • (%env var val) or (%env var=val) 환경변수를 설정한다.
  4. %run 

    • %run file_name 해당 이름의 .py 파일 또는 .ipynb 파일을 셀 안에서 실행한다. 
  5. %load 

    • %load source 해당 파일을 셀 안에 로드한다.
  6. %who = will list all variables that exist in the global scope. It can be used to see what all data_frames or any other variable is there in memory. 
    • %who: 현재 전역 환경의 모든 변수를 리스트한다. (메모리에 어떤 변수들이 올라와 있나 확인할 수 있다.)
    • %who df: 현재 선언된 dataframe 을 볼 수 있다. 
    • %whos: %who 와 비슷하지만 각 변수들에 대해 상세한 설명을 볼 수 있다. 
  7. %time 한 셀이 실행된 시간을 볼 수 있다. 
  8. %timeit 10만 번 실행하여 평균 시간을 잰다. 
  9. %writefile 

    • %writefile file_name 해당 파일의 셀의 아웃풋을 쓴다.
    • %writefile -a file_name 해당 파일의 셀의 아웃풋을 덧붙인다.

Jupyter configuration file (환경설정) 변경하기

주피터 랩에서는 print 문을 쓰지 않고 어떤 변수를 cell 에 입력했을 때, 가장 마지막 줄만 실행해서 결과로 내보냅니다. 이것이 불편하다면 아래 문장을 실행하면 됩니다. 개인적으로 유용하다고 생각합니다. 이렇게 하면 일일히 print 문으로 감싸줄 필요가 없게 됩니다.   

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

하지만 위 코드를 실행하는 것은 영구적으로 설정을 변경하지는 않는데, 영구적으로 설정을 변경하기 위해서는 configuration file 을 변경하면 됩니다. 이에 대한 설명은 해당 링크를 참고하시면 됩니다. configuration file 을 찾은 후에 아래 코드를 삽입해줍니다.

c.InteractiveShell.ast_node_interactivity = "all"

이외에도 주피터 랩에서는 더 많은 단축키와 매직 기능이 있습니다.



반응형
반응형


PEP 은 무엇인가?


PEP 8 -- Style Guide for Python Code

PEP:8
Title:Style Guide for Python Code
Author:Guido van Rossum <guido at python.org>, Barry Warsaw <barry at python.org>, Nick Coghlan <ncoghlan at gmail.com>
Status:Active
Type:Process
Created:05-Jul-2001
Post-History:05-Jul-2001, 01-Aug-2013

PEP8은  Python Enhancement Proposals (PEP) 의 8 번째 내용으로, 더 나은 파이썬 코드를 작성하기 위한 하나의 코딩 규약 (Style Guide for Python Code)입니다. (https://www.python.org/dev/peps/pep-0008/)


Python PEP8 code convention cheat sheet


아래 파이썬 코드는 PEP8 style 을 활용한 코딩 예입니다. 일종의 cheat sheet로 이 코드만 알면 PEP8 에서 지향하는 파이썬 코딩 스타일의 대부분을 알 수 있습니다!


내용을 간단하게 요약하면 아래와 같습니다.


1. 모듈과 패키지 이름은 짧고 lower_case_with_underscore 이다. 

2. 다른 모듈에서 import 할 때 보일 필요가 없는 함수, 변수는 변수명 앞에 _를 추가한다. 

3. 상수는 A_CONSTANT 

4. 함수는 naming_convention

5. 클래스는 NamingConvention

6. 한 라인의 길이가 79 길이가 넘지 않도록 한다. (이것은 대략적인 A4 용지 사이즈이다.)


#! /usr/bin/env python # -*- coding: utf-8 -*- """This module's docstring summary line. This is a multi-line docstring. Paragraphs are separated with blank lines. Lines conform to 79-column limit. Module and packages names should be short, lower_case_with_underscores. Notice that this in not PEP8-cheatsheet.py Seriously, use flake8. Atom.io with https://atom.io/packages/linter-flake8 is awesome! See http://www.python.org/dev/peps/pep-0008/ for more PEP-8 details """ import os # STD lib imports first import sys # alphabetical import some_third_party_lib # 3rd party stuff next import some_third_party_other_lib # alphabetical import local_stuff # local stuff last import more_local_stuff import dont_import_two, modules_in_one_line # IMPORTANT! from pyflakes_cannot_handle import * # and there are other reasons it should be avoided # noqa # Using # noqa in the line above avoids flake8 warnings about line length! _a_global_var = 2 # so it won't get imported by 'from foo import *' _b_global_var = 3 A_CONSTANT = 'ugh.' # 2 empty lines between top-level funcs + classes def naming_convention(): """Write docstrings for ALL public classes, funcs and methods.     Functions use snake_case.     """ if x == 4: # x is blue <== USEFUL 1-liner comment (2 spaces before #) x, y = y, x # inverse x and y <== USELESS COMMENT (1 space after #) c = (a + b) * (a - b) # operator spacing should improve readability. dict['key'] = dict[0] = {'x': 2, 'cat': 'not a dog'} class NamingConvention(object): """First line of a docstring is short and next to the quotes.     Class and exception names are CapWords.     Closing quotes are on their own line     """ a = 2 b = 4 _internal_variable = 3 class_ = 'foo' # trailing underscore to avoid conflict with builtin # this will trigger name mangling to further discourage use from outside # this is also very useful if you intend your class to be subclassed, and # the children might also use the same var name for something else; e.g. # for simple variables like 'a' above. Name mangling will ensure that # *your* a and the children's a will not collide. __internal_var = 4 # NEVER use double leading and trailing underscores for your own names __nooooooodontdoit__ = 0 # don't call anything (because some fonts are hard to distiguish): l = 1 O = 2 I = 3 # some examples of how to wrap code to conform to 79-columns limit: def __init__(self, width, height, color='black', emphasis=None, highlight=0): if width == 0 and height == 0 and \ color == 'red' and emphasis == 'strong' or \ highlight > 100: raise ValueError('sorry, you lose') if width == 0 and height == 0 and (color == 'red' or emphasis is None): raise ValueError("I don't think so -- values are %s, %s" % (width, height)) Blob.__init__(self, width, height, color, emphasis, highlight) # empty lines within method to enhance readability; no set rule short_foo_dict = {'loooooooooooooooooooong_element_name': 'cat', 'other_element': 'dog'} long_foo_dict_with_many_elements = { 'foo': 'cat', 'bar': 'dog' } # 1 empty line between in-class def'ns def foo_method(self, x, y=None): """Method and function names are lower_case_with_underscores.         Always use self as first arg.         """ pass @classmethod def bar(cls): """Use cls!""" pass # a 79-char ruler: # 34567891123456789212345678931234567894123456789512345678961234567897123456789 """ Common naming convention names: snake_case MACRO_CASE camelCase CapWords """ # Newline at end of file


출처 - https://gist.github.com/RichardBronosky/454964087739a449da04



반응형
반응형


Python PDPbox 패키지 설치시 문제 해결


문제


 PDPbox 패키지를 설치하던 도중 아래 에러가 발생하면서 설치가 완료되지 않음


/usr/bin/ld: cannot find -lpython3.5m

 collect2: error: ld returned 1 exit status

 error: command 'gcc' failed with exit status 1


해결


sudo apt install python3.5-dev



구글링해도 안나와서 올립니다.


도움을 얻은 페이지

https://github.com/giampaolo/psutil/issues/1143

반응형
반응형



Python 중고급 속성 정리 (3) 가변길이 인수목록 받기(*args, **kargs)


이번 포스팅에서는 알아두면 정말 유용한 가변길이 인수목록 받기를 정리해보겠습니다. 파이썬 코드를 보다보면 가끔 *args, **kargs를 보실 수 있습니다. 이것은 함수의 인자(parameter 또는 arguments)가 가변 길이일 때 사용합니다. args, kargs는 원하는 이름대로 쓸 수 있고, *, **는 각각 non-keworded arguments, keworded arguments를 뜻합니다. 


예를 들어 아래 파이썬 코드를 보시면 쉽게 이해하실 수 있습니다. 


*args


def test_var_args(f_arg, *args):
    print("first normal arg:", f_arg)
    for arg in args:
        print("another arg through *argv:", arg)

test_var_args('yasoob', 'python', 'eggs', 'test')

first normal arg: yasoob

another arg through *argv: python

another arg through *argv: eggs

another arg through *argv: test


f_arg를 통해 하나의 인자를 전달 받고, *args를 통해 가변길이 인자를 전달 받습니다. 전달받은 인자들은 함수내에서 list처럼 다룰 수 있습니다. 


또한 아래 코드처럼, 인자들을 미리 정의한 후 전달할 수도 있습니다. 이를 통해 파라미터 정의부함수 실행부를 분리할 수 있다는 장점이 있습니다. 조금 더 깔끔하게 파이썬 코드를 관리할 수 있겠죠.


param = ['yasoob', 'python', 'eggs', 'test']
test_var_args('hi', *param)


*kargs


다음으로는 **, 별표가 2개 붙은 keworded argments 사용법입니다. **kwargs로 전달받은 인자는 함수 내에서 dictionary 처럼 다룰 수 있습니다.

def greet_me(**kwargs):
    print(kwargs.items())
    for key, value in kwargs.items():
        print("{0} = {1}".format(key, value))
        
greet_me(name="yasoob", school="snu")

dict_items([('name', 'yasoob'), ('school', 'snu')])

name = yasoob

school = snu


kwargs = {"name": 'yasoob', 'school' :"snu"}
greet_me(**kwargs)

이런식으로 dict로 미리 정의한 후에 ** 를 통해 함수의 인자로 넘길 수 있습니다. 매우 유용하죠.



Decorator와 결합하여 사용하기


*args와 **kargs는 decorator와 결합하여 사용하면 더 유용하게 사용할 수 있습니다. 예를 들어, 함수를 실행할 때, 그 함수의 이름을 출력하고 함수를 실행하는 decorator를 만든다고 해봅시다. 근데, decorator 함수에서는 함수의 인자를 지정해줘야하기 때문에 decoration하는 함수의 인자의 수가 모두 같아야합니다. 이럴 때, *args와 **kargs를 이용하면 decoration 하는 함수의 인자의 길이에 상관없이 decorator를 만들 수 있습니다. 


logging_decorator는 함수를 실행할 때, 그 함수의 이름을 출력하는 기능을 갖는 decorator입니다. 이 decorator 함수에 *args, **kargs를 지정하고 이것을 그대로 원래 함수로 넘겨주면 인자의 길이에 상관없이 decorator를 구현할 수 있습니다.


Decorator와 *args, **kargs 사용 예제


이 예제에서 squared_number와 add_number는 인자의 수가 다른데 이를 *args를 이용하여 해결하였습니다. 만약 *args를 활용하지 않는다면, 각각에 대하여 decorator를 만들어야하죠. 또한 마지막 함수(add_numbers) 의 경우 함수 안에 keword를 지정하고 있습니다. 또 함수 실행 시 **kargs 를 통하여 인자를 넘기는데, 이 때 decorator에서 **kargs를 정의해 놓으면 keworded arguments 들을 그대로 decoration 함수를 전달할 수 있기 때문에 가변길이 인자를 처리할 수 있게 됩니다.

from functools import wraps

def logging_decorator(f) : 
    @wraps(f)
    def wrapper_function(*args, **kargs) : 
        print(f.__name__ + " was called") 
        print("결과 : ", f(*args, **kargs))
    return wrapper_function

@logging_decorator
def square_number(x) :
    return x**2

square_number(2)

@logging_decorator
def add_number(x,y,z=0) :
    return x+y

add_number(2,3)

@logging_decorator
def add_numbers(x=3, y=4) : 
    return x+y

add_numbers()

kwargs = {"x": 3, "y": 5}

add_numbers(**kwargs)

square_number was called

결과 :  4

add_number was called

결과 :  5

add_numbers was called

결과 :  7

add_numbers was called

결과 :  8

반응형
반응형


Decorator는 무엇인가? 


- 다른 function의 기능을 조작하여 새로운 function을 만드는 것.

- 이 방법은 코드를 더욱 간결하게 만들며, 더욱 Pythonic 한 코드를 만들 수 있다

- 이러한 형태의 일종의 코드 Refactoring 및 중복 줄이기는 소프트웨어 공학에서 매우 중요하다!


Decoration을 안 한 초보 파이썬 코더의 코드


# 기존의 코드를 사용 안 하고, b_function()을 새롭게 정의함.
# 이렇게 하면 문제가, my foul smell을 삭제하고 싶으면, 함수 2개에서 모두 삭제해야한다. 
# 코드의 중복이 생김.
 
def a_function_requiring_decoration(): 
    print("I am the function which needs some decoration to remove my foul smell") 
 
def b_function(): 
    print("I am doing some boring work before executing a_func()") 
    print("I am the function which needs some decoration to remove my foul smell")   
    print("I am doing some boring work after executing a_func()")
 
b_function()

- 이 방법의 문제점은 코드의 중복이 생겨 수정이 필요할 시에 두 함수 모두를 수정해야한다는 것이다. 


간단한 Decoration의 구현 


# 아래 함수를 기능을 추가해서 decoration 해주는 함수 def a_new_decorator(a_func):


# 함수 안에 함수를 정의하고 함수를 리턴한다 def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()")   return wrapTheFunction   # 이 함수를 decoration (기능을 추가) 하고 싶음 def a_function_requiring_decoration(): print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) a_function_requiring_decoration()

결과


I am doing some boring work before executing a_func()

I am the function which needs some decoration to remove my foul smell

I am doing some boring work after executing a_func()


- 이를 해결하는 방법이 바로 decoration 이다. 

- a_new_decorator 함수에 a_function_requiring_decoration 함수를 넘기는 방법을 통해 내용이 한 번만 쓰이게 된다. 

- 이를 통해 코드의 중복을 줄일 수 있다.



@ 키워드를 통한 decoration


# @를 붙임으로써 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 이걸 안 해도 된다. @a_new_decorator def a_function_requiring_decoration(): """Hey you! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell")   a_function_requiring_decoration()   # 근데 함수명이 이상하게 나옴. wrapTheFunction print(a_function_requiring_decoration.__name__)

- @ 키워드를 통해 a_function_requiring_decoration를 재정의 하지 않아도 된다.

- @ [함수명] 을 decoration 하고 싶은 함수 위에 붙여주면 된다. 

- 근데 함수 명이 wrapTheFunction 으로 decoration 한 함수의 이름이 그대로 나오게 된다. 

- 이를 해결하기 위해 wraps 를 이용한다.


# wraps를 이용해 함수명이 제대로 나오게 할 수 있음
from functools import wraps
# 최종적인 decorator의 일반적인 형태
# a_function_requiring_decoration을 a_new_decorator로 decorating 한다는 것이다. 이 때 decorate 할 함수는 a_func에 지정하고 이를 wraps로 받아서 그 아래 함수로 decoration 함
 
def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction(): 
        print("I am doing some boring work before executing a_func()") 
        a_func() 
        print("I am doing some boring work after executing a_func()")
 
    return wrapTheFunction
 
@a_new_decorator 
def a_function_requiring_decoration(): 
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to " "remove my foul smell")
 
a_function_requiring_decoration() 
 
print(a_function_requiring_decoration.__name__)  # a_function_requiring_decoration


- 이렇게 decorator에 wraps를 붙여주면, 그 함수를 decoration 해주는 함수로 인식을 하게 된다. 

- 함수명도 기존의 함수 명인 a_function_requiring_decoration 을 따르게 된다.


Decoration 활용의 좋은 예 - Authentication


authentication_check라는 함수를 만들고 이곳에서는 웹어플리케이션서의 사용자 인증을 체크한다고 하자. 만약 다른 함수를 실행할 때, 그 함수의 위에다가 위에다가 @authentication_check 만 붙이면, authentication을 알아서 해주게 된다. 즉, Authentication - function 실행 순으로 알아서 만들어 준다.  이것이 좋은 점은 각 함수마다 authentication check를 안해도되고, authentication check logic을 딱 한 번만 쓰면 된다. 이런건 Java에서는 보통 상속을 이용해서 하는데, python에서는 decorator로 할 수 있다.


""" Use case : Authorization Now let’s take a look at the areas where decorators really shine and their usage makes something really easy to manage. Decorators can help to check whether someone is authorized to use an endpoint in a web application. They are extensively used in Flask web framework and Django. Here is an example to employ decorator based authentication:   """   # decorator 함수 def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated

- 위 require_auth 함수는 어떤 함수 f를 받아서 그 전에 authorization 과정을 수행해주는 decorator이다. 



참고 - Intermediate Python

반응형
반응형


Python 중고급 - map, filter, reduce 


파이썬의 기초를 익힌 후, 파이썬의 중고급 문법을 선택적으로 배운다면 기본 문법으로도 구현할 수 있는 로직들을 더욱 쉽고, 간편하게 구현해볼 수 있습니다. 이번 포스팅에서는 list를 다루는 함수인 map, filter, reduce에 대해 간단하게 정리해보겠습니다. 물론 map, filter, reduce를 안 쓰고 코딩하는 것도 가능하며, 그것이 편하시면 그렇게 하셔도 좋습니다. 하지만 이를 사용하는 프로그래머나 데이터 분석가들이 꽤 있기 때문에 이러한 문법들을 알아두면 기존의 코드를 이해하는데 큰 도움이 될 수 있을 것입니다. 


Map


map의 경우, list의 element에 함수를 적용시켜 결과를 반환하고 싶을 때 사용합니다. 만약, 어떤 리스트의 원소를 제곱한 새로운 리스트를 만들고 싶다면, 일반적인 C언어 스타일의 해결법은 아래와 같습니다. 


# Map function
# 문제와 일반적인 해결법
items = [1, 2, 3, 4, 5]
squared = []
for i in items:
    squared.append(i**2)

하지만 map function을 통해 짧게 구현할 수 있습니다. map함수의 구조는 map(function_to_apply, list_of_inputs) 입니다.


items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))

위 코드는 items 리스트의 element 각각에 대해 제곱을 해서 새로운 리스트를 만들어 반환합니다. filter와 reduce도 이와 같은 구조를 갖습니다. 


이 때, map 앞에 list 함수를 통해 list 자료형으로 바꾸는 이유는 map 이 반환하는 것이 실제로는 list 자료형이 아니기 때문입니다. map 함수는 Iterator 를 반환하는데, 이를 list로 변환해서 list 자료형으로 만드는 것입니다. Iterator는 next() 함수를 갖는 파이썬 객체로 꼭 메모리에 올릴 데이터만 올려서 메모리를 효율적으로 이용할 수 있는 파이썬의 대표적인 객체입니다. 바로 list로 반환하는 것이 아니라 Iterator로 보다 상위의 객체를 리턴하는 것은, 다른 map 함수의 리턴값을 리스트가 아닌 다른 자료구조로 변환시킬 수도 있도록 하기 위해서입니다. 예를 들어, set 자료구조로도 변환시킬 수 있습니다. 


map_it = map(lambda x: x**2, items)
next(map_it)

map 함수의 결과는 Iterator 이므로, next 함수를 위와 같이 실행할 수 있습니다. 위 코드의 결과는 1입니다.


Iterator 에 대해서는 다음에 다루어 보도록 하겠습니다. 



Filter


Filter의 경우, list의 element에서 어떤 함수의 조건에 일치하는 값만 반환하고 싶을 때 사용합니다. 


As the name suggests, filter creates a list of elements for which a function returns true.


만약 어떤 리스트에서 '음수만 골라내고 싶다' 라고 할 때, filter 함수를 사용한 코딩 방법은 아래와 같습니다. 


number_list = range(-5, 5)
less_than_zero = list(filter(lambda x: x < 0, number_list))
print(less_than_zero)

이 때, less_than_zero 는 -5, -4, -3, -2, -1 을 갖습니다. 


만약 Map과 Filter를 보고 저거 list comprehension 으로 할 수 있는 거 아니야? 라고 생각하실 수 있습니다. 맞습니다. Map과 Filter가 자신과 맞지 않다고 생각하는 경우 list comprehension 만으로도 위 코드들을 훨씬 더 간결하게 구현할 수 있습니다.


# Map을 list comprehension으로 구현 items = [1, 2, 3, 4, 5] squared = list(map(lambda x: x**2, items)) print(squared)   squared = [x**2 for x in items] print(squared)   # Filter를 list comprehension으로 구현 number_list = range(-5, 5) less_than_zero = list(filter(lambda x: x < 0, number_list)) print(less_than_zero)   less_than_zero = [x for x in number_list if x <0] print(less_than_zero)  

위 코드처럼 list comprehension 만을 통해 map, filter가 하는 것을 할 수 있습니다. 


Filter가 마음에 들지 않는 경우, list comprehension을 쓸 수도 있습니다 하지만, map, filter가 있다는 것을 알아 두면 좋습니다.


Reduce


reduce는 어떤 list에 대해서 computation을 해서 결과를 내보낼 때, 즉 결과를 어떤 함수로 computation해서 결과를 축약하기 위해서 사용됩니다. 이 때, reduce함수에 input으로 들어가는 함수는 두 element를 연산하는 로직을 넣어야 합니다. 이것은 list comprehension으로 하지 못하는 것입니다. 


아래 코드는 reduce함수를 이용하는 것을 포함하여 파이썬으로 1-100 까지의 합을 구하는 총 3가지 방법입니다. 


# 1-100 까지의 합을 구하라   # C 언어 스타일의 해법 sum_value = 0 for i in range(1, 101) : sum_value += i print(sum_value)   # python 스러운 해법 sum_value = reduce((lambda x,y : x+y), [x for x in range(1,101)]) print(sum_value)   # 하지만 Data scientist가 가장 먼저 생각해는 해법 import numpy as np print(np.sum([x for x in range(1,101)])


참고 


Intermediate python (http://book.pythontips.com/en/latest/)

반응형
반응형


주피터 nbextension 설치


우선 가상환경을 쓰시는 분들은 가상환경을 activate하시기 바랍니다.


source activate [가상환경이름]


pip install jupyter_nbextensions_configurator jupyter_contrib_nbextensions

jupyter contrib nbextension install --user

jupyter nbextensions_configurator enable --user


위 커맨드를 입력하시면 nbextension이 설치됩니다. 원래는 Clusters 옆에 nbextension 탭이 생기게 됩니다.



저의 경우에는 왜 인지는 모르겠지만 자동으로 nbextension 탭이 안 생겼는데

[아이피]:[포트]/nbextensions 로 들어가니까 아래처럼 nbextension 메뉴를 볼 수 있었습니다. 


메뉴에서 사용 가능한 extension 들이 나와있고, 클릭을 통해 enable/disable을 할 수 있습니다.

처음 사용하실 때는 각각의 모듈들을 설치하셔야 합니다.


주피터 테마 설치


주피터 테마

https://github.com/dunovank/jupyter-themes


마찬가지로 가상환경 activate 후에


pip install jupyterthemes


커맨드 옵션은 아래와 같습니다.


jt  [-h] [-l] [-t THEME] [-f MONOFONT] [-fs MONOSIZE] [-nf NBFONT]

    [-nfs NBFONTSIZE] [-tf TCFONT] [-tfs TCFONTSIZE] [-dfs DFFONTSIZE]

    [-m MARGINS] [-cursw CURSORWIDTH] [-cursc CURSORCOLOR] [-vim]

    [-cellw CELLWIDTH] [-lineh LINEHEIGHT] [-altp] [-altmd] [-altout]

    [-P] [-T] [-N] [-r] [-dfonts]


jt -l 로 적용가능한 테마들을 볼 수 있습니다.


저의 경우에는 onedork 테마가 마음에 들어서


jt -t onedork -fs 95 -tfs 11 -nfs 115 -cellw 70% -T

이러한 옵션으로 사용중인데, 폰트 사이즈도 적절하고 화면이 주피터 노트북 default보다 조금 넓은 정도여서 편리하게 사용하고 있습니다.


각각의 옵션이 무엇을 뜻하는지에 대한 설명은

https://github.com/dunovank/jupyter-themes 을 참조하시면 됩니다.


또 테마를 설치한 후, output화면에서 왼쪽이 약간 잘려 나오는 문제가 있었는데

~/.jupyter/custom/custom.css 에서

div.output_area {
display: -webkit-box;
padding-left: 20px;
}

를 추가하여 해결하였습니다. 위 파일을 수정하면 jupyter notebook의 css 설정을 직접 바꾸어 자신만의 테마를 만들어 볼 수도 있습니다.


반응형
반응형

Python으로 하는 탐색적 자료 분석 (Exploratory Data Analysis)


Python을 통해 탐색적 자료분석을 할 때, 무엇을 해야하고, 순서는 어떻게 해야하는지 막막한 경우가 많은데요. 탐색적 자료분석의 기본은 바로 변수 별로 분포를 그려보는 것이겠죠. 수치형 데이터의 경우는 히스토그램을, 명목형 데이터의 경우는 빈도표를 통해 데이터의 분포를 살펴보게 됩니다. 본 포스팅에서는 파이썬을 통해 탐색적 자료 분석을 하는 방법을 유명한 데이터셋인 타이타닉 데이터를 통하여 차근차근 알아보겠습니다. 


titanic.csv



기본적인 탐색적 자료 분석의 순서는 아래와 같이 정리해보았습니다. 


1. 데이터를 임포트하여 메모리에 올린다.

2. 데이터의 모양을 확인 한다.

3. 데이터의 타입을 확인한다.

4. 데이터의 Null 값을 체크한다. 

5. 종속변수의 분포를 살펴본다.

6. 독립변수 - 명목형 변수의 분포를 살펴본다. 

7. 독립변수 - 수치형 변수의 분포를 살펴본다. 

8. 수치형, 명목형 변수간의 관계를 파악한다. 


1. 데이터를 임포트한다.


아래와 같이 패키지와 데이터를 임포트합니다. numpy, pandas, matplotlib, seaborn은 이 4가지의 패키지는 파이썬을 통한 EDA에서 거의 필수적으로 사용하는 라이브러리입니다.


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
 
titanic = pd.read_csv("titanic.csv")


2. 데이터의 모양을 확인한다. 


titanic.head()

PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS



3. 데이터의 타입을 체크한다.


titanic.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            891 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB


데이터의 타입을 체크하는 이유는 해당 변수의 타입을 제대로 맞추어주기 위해서입니다. 범주형 변수의 경우 object 또는 string, 수치형 변수의 경우 int64 혹은 float 64로 맞추어주면 됩니다. 범주형 변수의 경우 값이 문자열로 들어가 있으면 알아서 object 타입이 되지만, 만약의 숫자로된 범주형 변수의 경우 int64 등으로 잘못 타입이 들어가 있는 경우가 있습니다.  


위 데이터의 경우 Survived와 PClass 변수가 범주형 int64로 잘못 되어있으므로 형변환을 합니다.


titanic['Survived'] = titanic['Survived'].astype(object)
titanic['Pclass'] = titanic['Pclass'].astype(object)


4. 데이터의 Null 값을 체크한다. 


Null Check도 매우 중요한 작업 중 하나입니다. 단순히 Tutorial이나 학습을 위해 제작된 데이터셋이아닌 현실의 데이터셋의 경우, 많은 부분이 Null 인 경우가 많습니다. 따라서 이 Null 값을 어떻게 처리하느냐가 매우 중요합니다. 


titanic.isnull().sum()

PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age              0
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64



Cabin 변수가  687 행이 missing이고 Embarked가 2개의 행이 missing인 것을 확인하였습니다.


Null 값이 있는 경우, 크게 그 값을 빼고 하는지, 혹은 결측치를 대치하는지 2개의 방법으로 나눌 수 있습니다. 각각의 방법에 대한 이름이 다르긴한데 보통 첫 번째 방법을 complete data analysis, 두 번째 방법을 Imputation이라고 이름 붙입니다. 


missing_df = titanic.isnull().sum().reset_index()
missing_df.columns = ['column', 'count']
missing_df['ratio'] = missing_df['count'] / titanic.shape[0]
missing_df.loc[missing_df['ratio'] != 0]

columncountratio
10Cabin6870.771044
11Embarked20.002245


위 명령어를 통해 전체의 몇 %가 missing 인지를 확인할 수 있습니다. 




5. 종속변수 체크


titanic['Survived'].value_counts().plot(kind='bar') plt.show()


기본적으로 종속변수의 분포를 살펴봅니다. 종속변수란 다른 변수들의 관계를 주로 추론하고, 최종적으로는 예측하고자 하는 변수입니다. 



6. 명목형 변수의 분포 살펴보기

 


단변수 탐색


category_feature = [ col for col in titanic.columns if titanic[col].dtypes == "object"]
category_feature
['Survived', 'Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked']

앞에서 명목형 변수의 형을 object로 모두 변경했기 때문에 이처럼 컬럼 중에서 object 타입을 가진 컬럼만 뽑아서 명목형 변수의 리스트를 만듭니다. 이 때, 데이터의 기본키(인덱스), 종속변수 등을 제외하고 분석하는 것이 좋습니다. 



category_feature = list(set(category_feature) - set(['PassengerId','Survived']))
category_feature
  ['Cabin', 'Embarked', 'Ticket', 'Sex', 'Name', 'Pclass']



다음으로는 그래프를 통해 명목형 변수의 분포를 살펴보는 것입니다. 


for col in categorical_feature: titanic[col].value_counts().plot(kind='bar') plt.title(col) plt.show()




이렇게 살펴봄으로써 명목형 변수를 어떻게 다룰지를 판단할 수 있습니다. 예를 들어, 카테고리수가 너무 많고, 종속변수와 별로 관련이 없어보이는 독립 변수들은 빼고 분석하는 것이 나을 수도 있습니다.



이변수 탐색



sex_df = titanic.groupby(['Sex','Survived'])['Survived'].count().unstack('Survived')
sex_df.plot(kind='bar', figsize=(20,10))
plt.title('Sex')
plt.show()


성별-생존의 관계 파악처럼 두 변수의 관계를 파악하기 위해서는 위와 같이 확인할 수 있습니다.


7. 수치형 변수의 분포 살펴보기


단변수 탐색


단변수 탐색은 seaborn 패키지의 distplot 함수를 이용하면 매우 편합니다.


우선 이와 같이 전체 변수 중에서 범주형 변수와 기타 인덱스 변수, 종속변수들을 제외하고 수치형 변수만 골라냅니다.

numerical_feature = list(set(titanic.columns) - set(category_feature) - set(['PassengerId','Survived']))
numerical_feature = np.sort(numerical_feature)
numerical_feature

변수별로 for문을 돌면서 distplot을 그립니다


for col in numerical_feature:
    sns.distplot(titanic.loc[titanic[col].notnull(), col])
    plt.title(col)
    plt.show()


이변수, 삼변수 탐색


seaborn 패키지의 pairplot을 통해 종속변수를 포함한 3개의 변수를 한 번에 볼 수 있도록 플로팅합니다.


sns.pairplot(titanic[list(numerical_feature) + ['Survived']], hue='Survived', 
             x_vars=numerical_feature, y_vars=numerical_feature)
plt.show()



pairplot은 어러 변수의 관계를 한 번에 파악할 수 있으며,  hue 파라미터를 통해 종속변수를 지정함으로써 세 변수의 관계를 파악할 수 있습니다.



8. 수치형, 명목형 변수 간의 관계 탐색


앞서서 수치형-수치형  간의 관계, 그리고 명목형-명목형 간의 관계에 종속변수까지 포함해서 보았습니다. 이 번에는 수치형-명목형 간의 관계를 파악해 보는 것입니다. 예를 들어, 성별, 나이, 생존여부 3개의 변수를 동시에 탐색하고 싶을 수 있습니다. 이 경우에 명목형 변수에 따라 수치형변수의 boxplot을 그려봄으로써 대략적인 데이터의 형태를 살펴볼 수 있습니다. 


unique_list = titanic['Sex'].unique()
 
for col in numerical_feature:
    plt.figure(figsize=(12,6))
    sns.boxplot(x='Sex', y=col, hue='Survived', data=titanic.dropna())
    plt.title("Sex - {}".format(col))
    plt.show()


반응형
반응형

Pandas (Python Data Analysis Library)



앞선 포스팅에 이어 Pandas 중고급 문법 튜토리얼입니다. 본 포스팅은 해당 포스팅을 참고하였습니다.


5. 컬럼명, 인덱스명 수정하기


df = pd.DataFrame(data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), columns=['A', 'B', 'C'])
display(df)

# Define the new names of your columns
newcols = {
    'A': 'new_column_1', 
    'B': 'new_column_2', 
    'C': 'new_column_3'
}

# Use `rename()` to rename your columns
df.rename(columns=newcols, inplace=True)

# Rename your index
df.rename(index={1: 'a'})



이번엔 기본기를 넘어 pandas의 고급 문법들을 살펴 보자.



6. 데이터 Formatting


Replace


dataframe의 특정 문자열을 바꾸고 싶을 때는 replace를 이용한다.


df = pd.DataFrame(data=np.array([["Awful", "Poor", "OK"], ["OK", "Acceptable", "Perfect"], ["Poor", "Poor", "OK"]]), columns=['A', 'B', 'C'])
display(df)

display(df.replace("Awful", "Nice"))

# Replace the strings by numerical values (0-4)
display(df.replace(['Awful', 'Poor', 'OK', 'Acceptable', 'Perfect'], [0, 1, 2, 3, 4]))



String의 일부 제거


map function과 lambda를 이용해 특정 컬럼의 모든 데이터에 함수를 적용시킬 수 있다. 


df = pd.DataFrame(data=np.array([[1, 2, "+6a"], [4, 5, "+3b"], [5, 5, "+2C"]]), columns=['A', 'B', 'result'])

# Check out your DataFrame
display(df)

# Delete unwanted parts from the strings in the `result` column
df['result'] = df['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))

# Check out the result again
display(df)



7. 열 또는 행에 함수 적용하기


map 또는 apply 함수를 이용한다.

df = pd.DataFrame(data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), columns=['A', 'B', 'C'])
doubler = lambda x: x*2
print(df.head())
print(df['A'].map(doubler))
print(df.apply(doubler))
print(df.iloc[1].apply(doubler))
  A  B  C
0  1  2  3
1  4  5  6
2  7  8  9
0     2
1     8
2    14
Name: A, dtype: int64
    A   B   C
0   2   4   6
1   8  10  12
2  14  16  18
A     8
B    10
C    12
Name: 1, dtype: int64



8. 빈 데이터 프레임 만들기 (Creating empty dataframe)


가끔 빈 데이터 프레임을 만들고, 그 곳에 값을 채워야할 경우가 있다. 이것을 하기 위해서는 인덱스와 컬럼을 만든 후, np.nan 을 채워주면 된다.

df = pd.DataFrame(np.nan, index=[0,1,2,3], columns=['A'])
print(df)

그 속에 값을 채워넣고 싶으면 아래처럼 할 수 있다.

df.loc[1, 'A'] = "A"


9. 데이터 임포트시, 날짜, 시간 parsing 하기


만약 날짜 시간으로된 컬럼을 datatime 으로 parsing 하기 위해서는 read_csv 메소드의 parse_dates 를 이용할 수 있다.

import pandas as pd
dateparser = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

# Which makes your read command:
pd.read_csv(infile, parse_dates=['columnName'], date_parser=dateparse)

# Or combine two columns into a single DateTime column
pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)  


10. 데이터프레임 재구성하기


pivot 함수를 통해 index, column, values 를 지정하여 재구성할 수 있다.

import pandas as pd

products = pd.DataFrame({'category': ['Cleaning', 'Cleaning', 'Entertainment', 'Entertainment', 'Tech', 'Tech'],
        'store': ['Walmart', 'Dia', 'Walmart', 'Fnac', 'Dia','Walmart'],
        'price':[11.42, 23.50, 19.99, 15.95, 55.75, 111.55],
        'testscore': [4, 3, 5, 7, 5, 8]})

print(products)

pivot_products = products.pivot(index='category', columns='store', values='price')
print(pivot_products)
 category    store   price  testscore
0       Cleaning  Walmart   11.42          4
1       Cleaning      Dia   23.50          3
2  Entertainment  Walmart   19.99          5
3  Entertainment     Fnac   15.95          7
4           Tech      Dia   55.75          5
5           Tech  Walmart  111.55          8
store            Dia   Fnac  Walmart
category                            
Cleaning       23.50    NaN    11.42
Entertainment    NaN  15.95    19.99
Tech           55.75    NaN   111.55


Stack & Unstack


pivot 함수를 이용할 수도 있지만, stack 과 unstack 함수를 이용하여 pivoting 을 할 수 있다. stack 은 column index를 raw index로 바꾸어 데이터프레임이 길어지고, unstack 은 raw index를 column index로 바꾸어 데이터프레임이 넓어진다. 


Stack 과 Unstack 의 개념도


본 포스팅에서는 간단한 개념만 설명하며, 테이블 pivoting 관련해서는 이 페이지 (https://nikgrozev.com/2015/07/01/reshaping-in-pandas-pivot-pivot-table-stack-and-unstack-explained-with-pictures/) 가 정리가 잘 되어있으니 참고하시면 좋을 것 같다.


melt 함수


melt 함수는 column 을 row로 바꾸어준다. 녹으면 흘러내리니까 column 이 raw로 흘러려 column 이 짧아지고 raw 는 길어진다고 이해하면 쉽다. 

# The `people` DataFrame
people = pd.DataFrame({'FirstName' : ['John', 'Jane'],
                       'LastName' : ['Doe', 'Austen'],
                       'BloodType' : ['A-', 'B+'],
                       'Weight' : [90, 64]})

print(people)

# Use `melt()` on the `people` DataFrame
print(pd.melt(people, id_vars=['FirstName', 'LastName'], var_name='measurements'))
FirstName LastName BloodType  Weight
0      John      Doe        A-      90
1      Jane   Austen        B+      64
  FirstName LastName measurements value
0      John      Doe    BloodType    A-
1      Jane   Austen    BloodType    B+
2      John      Doe       Weight    90
3      Jane   Austen       Weight    64



11. 데이터 프레임 반복하기


iterrows 함수를 이용하여 iteration 할 수 있다. 매우 유용하다.

df = pd.DataFrame(data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), columns=['A', 'B', 'C'])

for index, row in df.iterrows() :
    print(row['A'], row['B'])
1 2
4 5
7 8


반응형
반응형