이번 포스팅에서는 빠르고 메모리를 효율적으로 사용해서 반복자((fast and memory-efficient iterator)를 만들어주는 itertools 모듈을 사용해서 리스트 원소를 n번 반복하고 묶어서 새로운 리스트를 만드는 4가지 유형의 방법을 소개하겠습니다. 


먼저 itertools 모듈에서 chain(), repeat() 함수를 불러오고, 예제로 사용할 반복할 리스트(numbers)와 반복할 회수(n)을 만들어놓겠습니다. 

 In [1]: from itertools import chain, repeat

In [2]: numbers = [1, 2, 3]


In [3]: n = 3


  (1) itertools.repeat(object, times) : object를 times 만큼 반복하기


itertools의 repeat() 함수로 [1, 2, 3] 리스트를 3번 반복하면, 아래처럼 리스트 안에 [1, 2, 3] 리스트가 각 각 분리([1, 2, 3], [1, 2, 3], [1, 2, 3])되어서 들어가 있습니다. 

 

In [4]: list(repeat(numbers, n))

Out[4]: [[1, 2, 3], [1, 2, 3], [1, 2, 3]]




  (2) itertools.chain.from_iterable(repeat(object, times))) 
      : times 만큼 "object 전체"를 반복한 연속된 서열을 하나의 서열로 묶어줌


위의 (1)번과는 다르게, [1, 2, 3, 1, 2, 3, 1, 2, 3] 의 단 하나의 리스트로 묶였습니다. 


In [5]: list(chain.from_iterable(repeat(numbers, n)))

Out[5]: [1, 2, 3, 1, 2, 3, 1, 2, 3]

 



  (3) itertools.chain.from_iterable((repeat(object, times) for object in objects)))
       : "objects 내 각 원소"를 times만큼 반복한 연속된 서열을 하나의 서열로 묶어줌

list comprehension 으로 for loop을 이용하여서 numbers 리스트 안의 각 원소인 1, 2, 3 별로 3번씩 반복한 후, 이를 하나의 리스트로 묶어준 경우입니다. 


In [6]: list(chain.from_iterable((repeat(number, n) for number in numbers)))

Out[6]: [1, 1, 1, 2, 2, 2, 3, 3, 3]

 



  (4) itertools.chain.from_iterable((repeat(object, time)
                                              for (object, time) in zip(objects, times))))

  : objects 내 각 원소 다른 수의 times 만큼 반복한 연속된 서열을 하나의 서열로 묶어줌

반복할 수 times 인자를 반복할 대상 objects 내 원소별로 다르게 하고 싶을 때가 있습니다. 이럴 때는 zip() 으로 반복할 대상 object와 반복할 회수 time을 짝으로 묶어서 반복을 시켜주면 됩니다. 


In [7]: numbers = [1, 2, 3]

   ...: n_list = [3, 5, 7]


In [8]: list(chain.from_iterable((repeat(number, n) for (number, n) in zip(numbers, n_list))))

Out[8]: [1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3] 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. :-)


Posted by R Friend R_Friend

이번 포스팅에서는 Windows 환경에서 Anaconda Python 배포판으로 Python을 설치했을 경우에, Anaconda Prompt 를 이용하여 Python x.x 버전의 새로운 가상환경(Virtual Environment)을 만드는 방법을 소개하겠습니다. 

가상환경(Virtual Environment)을 이용하면 Python 버전 간의 의존성을 고려해서 가상의 격리된 환경을 만들어줌으로써 버전이 다름으로 인해 발생할 수 있는 호환이나 충돌 문제를 미연에 방지할 수 있도록 해줍니다. Anaconda Python 배포판에서는 conda command 로 가상환경, Python 패키지 설치를 관리할 수 있습니다. 


Anaconda Navigator에서 UI를 가지고 좀더 쉽게 가상환경을 만들 수도 있기는 한데요, 가끔 보면 Windows OS 노트북 사용하는 분들 중에 Anaconda Navigator 를 실행시켜면 화면으로 뜨는데 5분~10분 정도씩 걸리는 경우도 있더라구요. 그래서 빠르게 바로 실행시킬 수 있는 Anaconda Prompt 를 사용해서 가상환경 만드는 방법을 소개하겠습니다.  

먼저 Windows 실행 버튼을 눌러서 Anaconda Prompt 메뉴를 찾아 실행합니다.  python -V 커맨드로 확인해보니 저는 python 3.5.2 버전을 쓰고 있습니다. Python 2.7 버전으로 새로운 가상환경을 만들어보겠습니다. 

 > python -V



1. Conda 버전 확인 및 Conda Update


conda 를 최신버전으로 업데이트를 해줍니다. 중간에 Proceed ([y]/n)? 질문이 나오면 'y' 를 입력해줍니다. 

conda -V

conda update conda

Proceed ([y]/n)? y



 2. 가상환경 목록 확인 (Check virtual environments list)


 > conda env list

또는 

conda info -e



 3. conda로 가상환경 새로 만들기 (Create a new virtual environment using conda)

> conda create -n [virtual environment name] python=[python version] anaconda

의 형식과 순서대로 입력해줍니다. 저는 새로운 가상환경 이름을 'py_2.7'로 하였고, python 2.7 버전으로 anaconda 배포판을 이용해서 가상환경을 만들어보겠습니다. 

 conda create  -n  py_2.7  python=2.7  anaconda

이렇게 하면 아래처럼 Python 2.7 버전과 호환이 검증된 다른 Python libraries 들이 버전을 맞추어서 자동으로 수십개가 설치가 됩니다. 


중간에 Proceed ([y]/n)? 라고 묻거든 'y' 라고 쿨하게 답해주세요. 


혹시 SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed 라는 SSLError 가 발생하면 아래의 "conda config --set ssl_verify false"라는 command를 실행하고 난 후에, 위의 "conda create -n py_2.7 python=2.7 anaconda" 커맨드를 다시 실행시켜 보세요. 

 > conda config --set ssl_verify false

혹은 Anaconda Navigator의 Preference에서 SSL Certificate Verify 옵션의 체크박스를 해제해주고 "conda create -n py_2.7 python=2.7 anaconda"를 다시 실행해보기 바랍니다. 


"py_2.7" 이라는 이름의 가상환경이 잘 만들어졌는지 확인해볼까요? (이번에는 "conda env list" 대신에 "conda info -e"를 사용해서 가상환경 목록 확인)

 


 4. 가상환경 활성화 하기 (Activate Virtual Environment)


"activate [virtual environment name]" 형식으로 커맨드를 입력해주면 해당 이름/ Python 버전의 가상환경이 활성화되어서 이용 가능해집니다. 방금 새로 만든 python 2.7 버전으로 설치한 "py_2.7" 이름의 가상환경을 활성화해보겠습니다. 
(참고로, Mac OS에서는 > source activate [virtual_env_name] 으로서, Windows랑 조금 다릅니다.)

 > activate py_2.7


아래 화면 캡쳐해놓은 것처럼 activate py_2.7 을 하고 나면 "(py_2.7) C:\Users\admin>" 과 같이 커서 화면이 바뀝니다. 



 5. IDE (Jupyter Notebook, Spyder, IPython) 실행하기

conda prompt 창에서 커맨드 명령문으로 "py_2.7" 이라는 이름의 가상환경에서 python 2.7 버전을 사용할 수 있게 Jupyter Notebook, Spyder, IPython 등의 IDE 를 실행시킬 수 있습니다. 

5-1. Jupyter Notebook 실행

 (py_2.7) > jupyter notebook 

아래처럼 웹브라우저가 뜨면서 Jupyter Notebook이 실행되면 우측 상단의 'New' 메뉴에서 'Python 2'를 선택해서 새로운 Notebook 화면을 생성합니다.

아래처럼 새로운 Notebook 화면이 뜨면 이름 설정해주고 사용하면 되겠습니다. 탐색적 데이터 분석이나 교육용으로 사용하기에는 Jupyter Notebook이 제격이지요. 

Jupyter Notebook 다 사용하였으면 저장하고, 노트북 홈 화면에서 작업한 파일 shutdown 하구요, conda prompt 창에서 'Ctrl + C' 해주면 jupyter notebook kernel 종료(kernel shutdown)하고 빠져나올 수 있습니다. 


5-2. Spyer 실행

 (py_2.7) > spyder

Python으로 Project별로 Production 하기에는 Spyder나 Pycharm 이 작업하기에 더 편한거 같습니다. 


5-3. IPython 실행

 (py_2.7) > IPython

그냥 가볍고, 깔끔하고, interactive 하게 코딩하고 싶으면 IPython 이용하면 되겠습니다. 



 6. 가상환경 비활성화/ 종료하기 (Deactivate a Virtual Environment)

가상환경을 종료하려면 > deactivate 해줍니다. 

(참고로, MacOS 사용자라면 > source deactivate 로서, Windows와는 조금 다릅니다)

 (py_2.7) > deactivate

아래 화면 캡쳐한 것처럼 가상환경을 deactivate 해주면 "C:\Users\admin>" 으로 커서가 빠져나옵니다. 



 7. 가상환경 제거 하기 (Delete a Virtual Environment)

특정 Python 버전을 쓰는 프로젝트가 끝나서 더이상 쓸모가 없거나, 오래된 버전이라서 사용하지 않게 된 가상환경을 삭제하려면 > conda env remove -n [virtual environment name]  이라는 커맨드 명령어를 실행시켜줍니다. 

 > conda env remove -n py_2.7

그러면 자동으로 전체 가상환경 내 library 들을 지워주며, 다른 가상환경과는 격리되어 있기 때문에 아무런 영향없이 지워줍니다. 좋지요?! :-)


자동으로 버전 맞춰서 설치되었던 Python libraries 들을 지울건데 정말로 지워도 되냐고 확인 (Proceed ([y]/n)?)하려고 물어보는데요, y 라고 답해주기 바랍니다. 


> conda env list  로 가상환경 목록을 확인해보니 'py_2.7' 이라는 이름의 python 2.7 버전의 가상환경이 안보이네요. 잘 제거된거 맞네요. 


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


Posted by R Friend R_Friend

이번 포스팅에서는 Python pandas의 DataFrame을 Postgresql, Greenplum DB에 Copy 해서 집어넣는 방법을 소개하겠습니다. 

먼저 간단한 예제 pandas DataFrame을 만들어보겠습니다. 


import numpy as np

import pandas as pd


# make a DataFrame

school = pd.DataFrame({'region': ['gangnam', 'secho', 'bundang', 'mokdong'], 

                       'student_cnt': [100, 120, 150, 90], 

                       'math_score': [91, 95, 92, 93]}, 

                        columns=['region', 'student_cnt', 'math_score'])


school

Out[02]: 

    region  student_cnt  math_score

0  gangnam          100          91

1    secho          120          95

2  bundang          150          92

3  mokdong           90          93



이제 school 이라는 pandas DataFrame을 아래의 순서대로 DB에 Copy해서 넣어보겠습니다. 

(1) DataFrame을 CSV 파일로 내보내기 (export a DataFrame to CSV file)

(2) Postgresql, Greenplum DB에 연결하고 Table 만들기

(3) Postgresql, Greenplum DB의 Table에 CSV file을 Copy해서 집어넣기


  (1) DataFrame을 CSV 파일로 내보내기 (export a DataFrame to CSV file)


pandas의 to_csv() 메소드를 이용하였으며, index와 header 옵션은 False로 설정해서 CSV 파일에는 포함시키지 않도록 하겠습니다. 


school.to_csv('C:/Users/admin/Documents/data/school.csv', 

              sep=",", 

              na_rep="NaN", 

              index=False

              header=False)

 



  (2) Postgresql, Greenplum DB에 연결하고 Table 만들기

psycopg2 라이브러리를 이용해서 Postgresql, Greenplum DB에 연결해보겠습니다. 아래의 connect() 에는 본인의 DB 설정 정보를 바꾸어서 입력해주면 됩니다. 


# Postgresql DB connect using psycopg2

from psycopg2 import connect

conn = connect(host='localhost',  # set yours

               port=5432, 

               database='postgres', 

               user='postgres', 

               password='postgres')


cur = conn.cursor()


# Create a table at Postgresql public schema with school name

cur.execute("""

    DROP TABLE IF EXISTS school;

    CREATE TABLE school (

        region varchar(100), 

        student_cnt numeric, 

        math_score numeric

    )

""")

conn.commit()

 



  (3) Postgresql, Greenplum DB의 Table에 CSV file을 Copy해서 집어넣기

with open() 으로 로컬에 저장해놓은 school.csv 파일을 읽고, cursor.copy_expert() 를 이용하여 "COPY school FROM STDIN DELIMITER ',' CSV;" 쿼리문을 실행시켜서 CSV 파일을 Table 에 copy 해주겠습니다. 


query = """

    COPY school FROM STDIN DELIMITER ',' CSV;

"""


with open('C:/Users/admin/Documents/data/school.csv', 'r') as f:

    cur.copy_expert(query, f)

    

conn.commit()


# close connection

conn.close()

 


PGAdmin 에 들어가서 school 테이블을 조회해보니 아래처럼 데이터가 잘 copy 되서 들어가 있네요. 


Python에서 DB connect해서 데이터 조회하고 DataFrame으로 만들어서 한번 더 확인을 해보았습니다. 아래와 같이 데이터가 Postgresql DB의 school table에 잘 들어가 있음을 확인할 수 있습니다. 


# check 

cur.execute("SELECT * FROM school;")

school_df = cur.fetchall()

school_df

Out[39]: 

[('gangnam', Decimal('100'), Decimal('91')),

 ('secho', Decimal('120'), Decimal('95')),

 ('bundang', Decimal('150'), Decimal('92')),

 ('mokdong', Decimal('90'), Decimal('93'))] 



많은 도움이 되었기를 바랍니다. 



Posted by R Friend R_Friend

이번 포스팅에서는 Python pandas DataFrame의 index를 reset_index() 하여 칼럼으로 가져오고, 이렇게 가져온 index에 새로운 이름을 부여하는 3가지 방법을 소개하겠습니다. 





먼저, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd


df = pd.DataFrame(np.arange(10).reshape(5, 2), 

                 columns=['x1', 'x2'], 

                 index=['a', 'b', 'c', 'd', 'e'])


df


x1x2
a01
b23
c45
d67
e89

 




이제 index 를 칼럼으로 가져오고, 가져온 index의 이름으로 'id'라는 이름을 부여하는 3가지 방법을 차례대로 소개하겠습니다. 


  (1) reset_index() 한 후에 rename()으로 새로운 이름 부여하기



# (1) reset_index() and rename

df.reset_index().rename(columns={"index": "id"})

 

idx1x2
0a01
1b23
2c45
3d67
4e89





  (2) rename_axis() 로 index의 이름을 먼저 바꾸고, 이후에 reset_index() 하기



# (2) rename_axis() first, reset_index() second

df_1 = df.rename_axis('id').reset_index()

df_1

 

idx1x2
0a01
1b23
2c45
3d67
4e89





  (3) df.index.name 으로 index에 이름 할당하고, 다음으로 reset_index() 하기



# (3) assing index name and reset_index()

df.index.name = 'id'

df_2 = df.reset_index()

df_2

 

idx1x2
0a01
1b23
2c45
3d67
4e89



많은 도움이 되었기를 바랍니다. 



Posted by R Friend R_Friend

이번 포스팅에서는 Python의 for loop 반복문의 진척율을 콘솔창에 출력해서 확인하는 방법을 소개하겠습니다. 


먼저, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd


df = pd.DataFrame(np.arange(10000).reshape(-1, 2),

                  columns=['x1', 'x2'])


df.head()


x1x2
001
123
245
367
489

 




for loop 예문은 DataFrame의 매 3의 배수 행은 'x1'과 'x2'를 더해서 'x3'를 만들고, 3의 배수 행이 아닌 경우는 'x1'에서 'x2'를 뺀 값으로 'x3'를 만드는 것입니다. 



for i in range(df.shape[0]):

    if i % 3 == 0:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] + df.loc[i, 'x2']

    else:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] - df.loc[i, 'x2']



df.head(10)

x1x2x3
0011.0
123-1.0
245-1.0
36713.0
489-1.0
51011-1.0
6121325.0
71415-1.0
81617-1.0
9181937.0





가장 간단하게 for loop 반복문의 진척율을 확인할 수 있는 방법은 전체 행 중에서 반복문이 수행 중인 행의 위치를 매번 콘솔창에 프린트하도록 하는 방법입니다. 



for i in range(df.shape[0]):

    if i % 3 == 0:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] + df.loc[i, 'x2']

    else:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] - df.loc[i, 'x2']

    

    progress = 100*(i+1)/df.shape[0]

    print("===== %d%% completed =====" % progress)

 

 



그런데 위처럼 프린트문을 사용하면 for loop문이 실행되는 매 행마다 한줄씩 프린트가 되므로 콘솔 창이 매우 길게 늘어질 수 있습니다. 


이때 sys.stdout 을 사용하면 아래 예시처럼 매 진척율을 같은 줄에서 진척율만 숫자가 바뀌면서 나타낼 수 있습니다. 



from sys import stdout


for i in range(df.shape[0]):

    if i % 3 == 0:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] + df.loc[i, 'x2']

    else:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] - df.loc[i, 'x2']

    

    progress = 100*(i+1)/df.shape[0]

    stdout.write("\r ===== %d%% completed =====" % progress)

    stdout.flush()

stdout.write("\n")

 




많은 도움이 되었기를 바랍니다. 

Posted by R Friend R_Friend

이번 포스팅에서는 Python pandas의 DataFrame을 


(1) 특정 칼럼을 기준으로 행을 정렬한 후에 (sort DataFrame by value in ascending/descending order)

==> (2) 각 그룹별로 상위 N개 행을 가져오기 (select top N rows by group)


을 하는 방법을 소개하겠습니다. 





먼저 'a'와 'b' 두 개의 그룹별로 5개의 값을 가진 간단한 예제 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd


# make a sample DataFrame

df = pd.DataFrame({'grp': ["a", "a", "a", "a", "a", "b", "b", "b", "b", "b"], 

                           'val': np.random.uniform(0, 10, 10)})


df


grpval
0a0.275704
1a5.334576
2a5.386807
3a6.033636
4a2.140798
5b2.089792
6b6.396985
7b3.088498
8b5.895689
9b1.157073

 




이제 "val" 변수를 기준으로 내림차순 정렬(sort by 'val' in descending order) 한 후에, 'grp' 칼럼의 'a', 'b' 그룹별로 상위 3개의 값을 가져와서 새로운 데이터프레임을 만들어보겠습니다. 



# sort by value in descending order per group, and select top 3 values per group

df_sort_group_top3 = df.sort_values(by="val", ascending=False).groupby("grp").head(3)


df_sort_group_top3


grpval
6b6.396985
3a6.033636
8b5.895689
2a5.386807
1a5.334576
7b3.088498

 



위의 df_sort_group_top3 결과를 좀더 보기에 좋도록 'a', 'b' 그룹 순서대로, 각 그룹 내에서는 내림차순으로 정렬해보겠습니다. 



df_sort_group_top3.sort_values(by=["grp", "val"], ascending=[True, False])


grpval
3a6.033636
2a5.386807
1a5.334576
6b6.396985
8b5.895689
7b3.088498

 



많은 도움이 되었기를 바랍니다.  

Posted by R Friend R_Friend

이번 포스팅에서는 Python pandas의 pivot_table() 함수를 사용할 때 


- (1) 'DataError: No numeric types to aggregate' 에러가 왜 생기는지

- (2) 'DataError: No numeric types to aggregate' 에러 대응방법은 무엇인지에 대해서 알아보겠습니다. 



먼저 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



In [1]: import numpy as np

   ...: import pandas as pd


In [2]: df = pd.DataFrame({'id': [1, 1, 2, 2, 3, 3], 

   ...:                    'col': ['x1', 'x2', 'x1', 'x2', 'x1', 'x2'], 

   ...:                    'sum': [30, 10, 70, 40, 20, 80], 

   ...:                    'name': ['a', 'a', 'b', 'b', 'c', 'c']})

   ...: 

   ...: df

Out[2]: 

  col  id name  sum

0  x1   1    a   30

1  x2   1    a   10

2  x1   2    b   70

3  x2   2    b   40

4  x1   3    c   20

5  x2   3    c   80

 



이제 pandas의 pivot_table() 함수를 이용해서 'id'변수를 index로, 'col' 변수를 열(column)로 하여 'sum'이라는 숫자형 데이터 값(values)을 재구조화(pivot) 해보겠습니다.  pivot_table() 함수의 집계함수(aggregation function)의 디폴트 설정은 평균(aggfunc='mean')으로 되어 있습니다. 아래 코드는 문제 없이 잘 수행이 되었습니다. 



In [3]: df.pivot_table(index = 'id', columns='col', values=['sum'])

Out[3]: 

    sum    

col  x1  x2

id         

1    30  10

2    70  40

3    20  80


In [4]: df.pivot_table(index = 'id', columns='col', values=['sum'], aggfunc='mean')

   ...: 

Out[4]: 

    sum    

col  x1  x2

id         

1    30  10

2    70  40

3    20  80

 



 

 (1) 'DataError: No numeric types to aggregate' 에러가 왜 생기는가?


이번에는 'id' 변수를 index로, 'col'변수를 열(column)변수로 하는 것은 위와 동일하나, 재구조화하는 테이블의 값(value)으로 숫자형(nemeric)이 아니라 문자형(character)인 'name' 변수를 사용해보겠습니다. 그러면 아래와 같이 'DataError: No numeric types to aggregate'라는 DataError가 납니다. 왜냐하면 값(values) 으로 사용하려는 'name' 변수가 집계가 불가능한 문자형 데이터이기 때문입니다. (numeric only)



In [5]: df.pivot_table(index = 'id', columns='col', values=['name']) # default aggfunc='mean'

Traceback (most recent call last):


  File "<ipython-input-5-9a2cccdff2ef>", line 1, in <module>

    df.pivot_table(index = 'id', columns='col', values=['name'])


  File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\groupby.py", line 3048, in _cython_agg_general

    how, numeric_only=numeric_only)


    .... 중간 생략 ....


DataError: No numeric types to aggregate

 




 (2) 'DataError: No numeric types to aggregate' 에러 대응방법은?


집계함수를 aggfunc='first' 로 명시적으로 설정해 줌으로써, 디폴트인 'mean' 을 사용해서 집계하는 것이 아니라 재구조화하는 기준의 테이블의 각 cell의 첫번째 값('first')을 그냥 가져오게끔 해주면 됩니다. 



In [6]: df.pivot_table(index = 'id', columns='col', values=['name'], aggfunc='first')

Out[6]: 

    name   

col   x1 x2

id         

1      a  a

2      b  b

3      c  c

 



집계함수 aggfunc='first' 로 해서 pivot 한 테이블의 값(values)을 하나가 아니라 여러개로 할 수도 있습니다. (이렇게 하면 숫자형 변수 'sum'도 집계를 하는 것이 아니라 각 테이블 cell의 첫번째 값을 가져오게 됨)



In [7]: df.pivot_table(index = 'id', columns='col', values=['name', 'sum'], aggfunc='first')

Out[7]: 

    name    sum    

col   x1 x2  x1  x2

id                 

1      a  a  30  10

2      b  b  70  40

3      c  c  20  8

 



많은 도움이 되었기를 바랍니다. 

Posted by R Friend R_Friend

이번 포스팅에서는 Windows10 OS 에서

(1) Python으로 Postgresql, Greenplum DB connect 하고 Query 결과 가져오는 방법

(2) Python으로 MySQL DB connect 하고 Query 결과 가져오는 방법

(3) Python으로 IBM DB2 DB connect 하고 Query 결과 가져오는 방법

(4) Python으로 Presto, Hive DB connect 하고 Query 결과 가져오는 방법

을 소개하겠습니다.

 

 (1) Python으로 Postgresql, Greenplum DB connect 하고 Query 결과 가져오는 방법

먼저, 명령 프롬프트 창에서 psycopg2 라이브러리를 설치해줍니다.

 $ pip install psycopg2

Spyder 등의 Python IDE에서 PostgreSQL, Greenplum DB에 접속하고 query를 실행하여 결과를 pandas DataFrame으로 받아와서 저장하는 사용자 정의함수를 정의합니다.

( * Reference : PostgreSQL Python: Connect to PostgreSQL Database Server )

 

[ UDF of connecting to Postgresql, GPDB & Getting query result as a DataFrame ]

def postgresql_query(query): 

    import psycopg2 as pg
    import pandas as pd

    # Postgresql, Greenplum DB Connect
    connection_string = "postgresql://{user}:{password}@{host}/{db}".\
        format(user='gpadmin',  # put your info
                 password='changeme', 
                 host='localhost', 
                 db='gpadmin')

    conn = pg.connect(connection_string)

    cursor = conn.cursor()

    #conn.autocommit = True

    # execute a query and get it as a pandas' DataFrame
    cursor.execute(query)
    col_names = [desc[0] for desc in cur.description]
    result = pd.DataFrame(cur.fetchall(), columns=col_names)

   cursor.close()

   conn.close()


    return result


 

아래는 Query를 실행해서 결과를 가져오는 간단한 예시입니다.

query = """
    SELECT * FROM mytable WHERE grp == 'A' LIMIT 100;

    """

postgresql_query(query)grp_A = postgresql_query(query)

 

 

 (2) Python으로 MySQL DB connect 하고 Query 결과 가져오는 방법

먼저, 명령 프롬프트 창에서 mysql 라이브러리를 설치해줍니다.

$ pip install mysql


다음으로 MySQL DB에 접속하고 query를 실행시켜서 결과를 DataFrame으로 가져오는 사용자 정의함수를 정의합니다.

( * Reference : Connecting to MySQL Using Connector/Python )


def mysql_query(query):

    import mysql.connector

    import pandas as pd


    cnx = mysql.connector.connect(user='userid',

                                             password='changeme',

                                             host='12.34.567.890',

                                             database='mydb')

 

    cursor = cnx.cursor()

 

     # execute a query and get it as a pandas' DataFrame
     cursor.execute(query)
     col_names = [desc[0] for desc in cur.description]
     result = pd.DataFrame(cur.fetchall(), columns=col_names)

 

    cursor.close()

    cnx.close()

 

    return result

 


위에서 정의한 사용자 정의함수를 사용하여 MySQL DB에 접속하고, Query로 조회한 결과를 result 라는 이름의 DataFrame으로 저장하는 예시입니다.

 

query = """

    SELECT * FROM mydb WHERE age >= 20 ORDER BY age;

    """

 

result = mysql_query(query)

 

 

 (3) Python으로 IBM DB2 DB connect 하고 Query 결과 가져오는 방법

먼저, 명령 프롬프트 창에서 ibm_db_dbi 라이브러리를 설치해줍니다.

$ pip install ibm_db_dbi


다음으로 DB2에 접속해서 Query를 실행하고, 결과를 pandas DataFrame으로 가져오는 사용자 정의함수를 정의합니다.

( * Reference : Connecting to an IBM database server in Python)


def db2_query(query):
    

    import ibm_db_dbi as db

    import pandas as pd

    conn = db.connect('DATABASE=mydb;' 
                             'HOSTNAME=12.34.567.890;' 
                             'PORT=50000;' 
                             'PROTOCOL=TCPIP;' 
                             'UID = secret;' 
                             'PWD= changeme;', '', ' ')

     cursor = conn.cursor()
     cursor.execute(query)
     col_names = [desc[0] for desc in cursor.description]

     result = pd.DataFrame(cursor.fetchall(), columns=col_names)

     cursor.close()
     conn.close()
 
     return result

 

 

Python에서 Query를 실행시켜서 결과를 pandas DataFrame을 가져오는 예시는 아래와 같습니다.

query = """

    SELECT school_nm, count(*) as student_cnt

    FROM school

    WHERE school_nm LIKE 'seoul%';

    """

 

school = db2_query(query)

 

 

 (4) Python으로 Presto, Hive DB connect 하고 Query 결과 가져오는 방법

먼저 명령 프롬프트 창에서 pyhive 라이브러리를 설치해줍니다.

$ pip install pyhive


Presto 혹은 Hive에 접속하고 Query를 실행해서 결과를 pandas DataFrame으로 가져오는 사용자 정의함수를 정의합니다.

( * Reference : PyHive is a collection of Python DB-API and SQLAlchemy interfaces for Presto and Hive  )

 


def presto_query(query):

     from pyhive import presto
     import pandas as pd

     cursor = presto.connect('12.34.567.890').cursor()
    

     # execute a query and get a result as a DataFrame

     cursor.execute(query)
     col_names = [ desc[0] for desc in cursor.description ]
     result = pd.DataFrame(cursor.fetchall(), columns=col_names)

     cursor.close()
 
     return result

 


Python에서 위의 사용자 정의 함수를 사용하여 query를 실행시키고 결과를 DataFrame으로 가져오는 예제입니다.

 

query = """

    WITH

        t1 AS (SELECT a, MAX(b) AS b FROM x GROUP BY a),

        t2 AS (SELECT a, AVG(d) AS d FROM y GROUP BY a)

    SELECT t1.*, t2.* FROM t1 JOIN t2 ON t1.a = t2.a;

    """

result = presto_query(query)

 

 

혹시 pip install 하는 단계에서 'error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/' 와 같은 에러가 나면 안내에 나와있는 사이트에 가서 Microsoft Visual C++ 을 다운받아 설치하시기 바랍니다.

 

많은 도움이 되었기를 바랍니다.

Posted by R Friend R_Friend

이번 포스팅에서는 Python pandas 의 DataFrame에서 문자열(string)을 데이터 형태로 가지는 칼럼을 특정 기준(separator, delimiter) 분할(split a string)하여, 그 중의 일부분을 가져다가 DataFrame에 새로운 칼럼으로 만들어서 붙이는 2가지 방법을 소개하겠습니다. 

 

(1) Vectorization을 이용한 pandas DataFrame 문자열 칼럼 분할하기

(2) For Loop operation을 통한 pandas DataFrame 문자열 칼럼 분할하기

 

Python pandas DataFrame: Split string column and make a new column using part of it.

 

 

(1) Vectorization을 이용한 pandas DataFrame 문자열 칼럼 분할하기 (빠름 ^^)


예제로 사용할 문자열 'id' 와 숫자형 'val' 의 두 개 칼럼으로 이루어진 DataFrame을 만들어보겠습니다. 그리고 문자열 'id' 칼럼을 구분자(separator) '_' 를 기준으로 str.split('_') 메소드를 사용하여 분할(split) 한 후에, 앞부분([0])을 가져다가 'grp'라는 칼럼을 추가하여 만들어보겠습니다. 

 

import numpy as np
import pandas as pd

 

df = pd.DataFrame({'id': ['A_001', 'A_002', 'A_003', 'B_001', 'C_001', 'C_002'], 
                          'val': np.arange(6)})

 

print(df)

   id       val

0 A_001  0

1 A_002  1

2 A_003  2

3 B_001  3

4 C_001  4

5 C_002  5

 

# 1. vectorization
df['grp'] = df.id.str.split('_').str[0]

print(df)

   id       val  grp

0 A_001  0    A

1 A_002  1    A

2 A_003  2    A

3 B_001  3    B

4 C_001  4    C

5 C_002  5    C

 

 

만약 리스트(list)로 만들고 싶으면 분할한 객체에 대해 tolist() 메소드를 사용하면 됩니다. 

# tolist()
grp_list = df.id.str.split('_').str[0].tolist()
print(grp_list)

['A', 'A', 'A', 'B', 'C', 'C']

 

 

 

(2) For Loop operation을 통한 pandas DataFrame 문자열 칼럼 분할하기 (느림 -_-;;;)


두번째는 For Loop 연산을 사용하여 한 행, 한 행씩(row by row) 분할하고, 앞 부분 가져다가 'grp' 칼럼에 채워넣고... 를 반복하는 방법입니다. 위의 (1)번의 한꺼번에 처리하는 vectorization 대비 (2)번의 for loop은 시간이 상대적으로 많이 걸립니다. 데이터셋이 작으면 티가 잘 안나는데요, 수백~수천만건이 되는 자료에서 하면 느린 티가 많이 납니다. 

 

# 2. for loop
df = pd.DataFrame({'id': ['A_001', 'A_002', 'A_003', 'B_001', 'C_001', 'C_002'], 
                  'val': np.arange(6)})

 

for i in range(df.shape[0]):
    df.loc[i, 'grp'] = str(df.loc[i, 'id']).split('_')[0]

 

print(df)

   id       val  grp

0 A_001  0    A

1 A_002  1    A

2 A_003  2    A

3 B_001  3    B

4 C_001  4    C

5 C_002  5    C

 

많은 도움이 되었기를 바랍니다.

Posted by R Friend R_Friend

텍스트 분석을 할 때 제일 처음 하는 일이 문서, 텍스트를 분석에 적합한 형태로 전처리 하는 일입니다. 

이번 포스팅에서는 (1) 텍스트 데이터를 Python의 string methods 를 이용하여 단어 단위로 파싱(parsing text at word-level) 한 후에, 단어별 index를 만들고, (2) 텍스트를 단어 단위로 one-hot encoding 을 해보겠습니다. 

one-hot encoding of text at a word-level

 

1. 텍스트 데이터를 Python string methods를 사용하여 단어 단위로 파싱하고,  단어별 token index 만들기

예제로 사용할 텍스트는 Wikipedia 에서 검색한 Python 영문 소개자료 입니다. 

python_wikipedia.txt
0.00MB

# import modules
import numpy as np
import os

# set directory
base_dir = '/Users/ihongdon/Documents/Python/dataset'
file_name = 'python_wikipedia.txt'
path = os.path.join(base_dir, file_name)

# open file and print it as an example
file_opened = open(path)
for line in file_opened.readlines():
    print(line)

Python programming language, from wikipedia


Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python's design philosophy emphasizes code readability with its notable use of significant whitespace. Its language constructs and object-oriented approach aims to help programmers write clear, logical code for small and large-scale projects.[26]


Python is dynamically typed and garbage-collected. It supports multiple programming paradigms, including procedural, object-oriented, and functional programming. Python is often described as a "batteries included" language due to its comprehensive standard library.[27]


Python was conceived in the late 1980s as a successor to the ABC language. Python 2.0, released 2000, introduced features like list comprehensions and a garbage collection system capable of collecting reference cycles. Python 3.0, released 2008, was a major revision of the language that is not completely backward-compatible, and much Python 2 code does not run unmodified on Python 3. Due to concern about the amount of code written for Python 2, support for Python 2.7 (the last release in the 2.x series) was extended to 2020. Language developer Guido van Rossum shouldered sole responsibility for the project until July 2018 but now shares his leadership as a member of a five-person steering council.[28][29][30]


Python interpreters are available for many operating systems. A global community of programmers develops and maintains CPython, an open source[31] reference implementation. A non-profit organization, the Python Software Foundation, manages and directs resources for Python and CPython development.

 

아래는 Python string method를 사용해서 텍스트에서 단어를 파싱하고 전처리할 수 있는 사용자 정의 함수 예시입니다. 가령, 대문자를 소문자로 바꾸기, stop words 제거하기, 기호 제거하기, 숫자 제거하기 등을 차례대로 적용할 수 있는 기본적인 예시입니다. (이 역시 텍스트 분석용 Python module 에 잘 정의된 함수들 사용하면 되긴 합니다. ^^;) 

# UDF of word preprocessing
def word_preprocess(word):
    # lower case
    word = word.lower()
        
    # remove stop-words
    stop_words = ['a', 'an', 'the', 'in', 'with', 'to', 'for', 'from', 'of', 'at', 'on',
                  'until', 'by', 'and', 'but', 'is', 'are', 'was', 'were', 'it', 'that', 'this', 
                  'my', 'his', 'her', 'our', 'as', 'not'] # make your own list
    for stop_word in stop_words:
        if word != stop_word:
            word = word
        else:
            word = ''
    
    # remove symbols such as comma, period, etc.
    symbols = [',', '.', ':', '-', '+', '/', '*', '&', '%', '[', ']', '(', ')'] # make your own list
    for symbol in symbols:
        word = word.replace(symbol, '')
    
    # remove numbers
    if word.isnumeric():
        word = ''
    
    return word

 

다음으로, python_wikipedia.txt 파일을 열어서(open) 각 줄 단위로 읽고(readlines), 좌우 공백을 제거(strip)한 후에, 단어 단위로 분할(split) 하여, 위에서 정의한 word_preprocess() 사용자 정의 함수를 적용하여 전처리를 한 후, token_idx 사전에 단어를 Key로, Index를 Value로 저장합니다. 

# blank dictionary to store
token_idx = {}

# opening the file
file_opened = open(path)

# catching words and storing the index at token_idx dictionary
for line in file_opened.readlines():
    # strip leading and trailing edge spaces
    line = line.strip()
        
    # split the line into word with a space delimiter
    for word in line.split():
        
        word = word_preprocess(word) # UDF defined above
        
        # put word into token_index
        if word not in token_idx:
            if word != '':
                token_idx[word] = len(token_idx) + 1

 

단어를 Key, Index를 Value로 해서 생성된 token_idx Dictionary는 아래와 같습니다. 

token_idx
{'"batteries': 48,
 '1980s': 56,
 '2x': 87,
 'abc': 58,
 'about': 80,
 'aims': 28,
 'amount': 81,
 'approach': 27,
 'available': 104,
 'backwardcompatible': 74,
 'capable': 67,
 'clear': 32,
 'code': 18,
 
 .... 중간 생략 ....
 
 'successor': 57,
 'support': 83,
 'supports': 40,
 'system': 66,
 'systems': 107,
 'the': 84,
 'typed': 38,
 'unmodified': 78,
 'use': 22,
 'van': 10,
 'whitespace': 24,
 'wikipedia': 4,
 'write': 31,
 'written': 82}

 

token_idx.values()
dict_values([104, 96, 102, 112, 68, 111, 21, 18, 8, 15, 20, 47, 37, 16, 74, 89, 57, 117, 19, 93, 83, 76, 91, 43, 30, 32, 54, 33, 35, 98, 64, 80, 17, 34, 10, 61, 50, 46, 49, 23, 72, 67, 119, 95, 14, 3, 116, 81, 85, 1, 99, 51, 77, 38, 90, 118, 120, 100, 101, 9, 39, 12, 123, 84, 122, 69, 26, 115, 88, 13, 36, 60, 5, 6, 75, 103, 66, 94, 78, 97, 121, 55, 108, 109, 58, 4, 82, 41, 79, 87, 29, 106, 114, 113, 105, 73, 45, 71, 24, 2, 53, 31, 86, 11, 22, 42, 59, 7, 110, 40, 56, 70, 92, 28, 27, 48, 62, 44, 107, 65, 25, 52, 63])

 

총 123개의 단어가 있으며, 이 중에서 'python'이라는 단어는 token_idx에 '1' 번으로 등록이 되어있습니다. 

max(token_idx.values())
123
token_idx.get('python')
1

 

 

2. 텍스트를 단어 단위로 One-hot encoding 하기

하나의 텍스트 문장에서 고려할 단어의 최대 개수로 max_len = 40 을 설정하였습니다. (한 문장에서 41번째 부터 나오는 단어는 무시함). 그리고 One-hot encoding 한 결과를 저장할 빈 one_hot_encoded 다차원 배열을 np.zeros() 로 만들어두었습니다. 

# consider only the first max_length words in texts            
max_len = 40

# array to store the one_hot_encoded results
file_opened = open(path)

one_hot_encoded = np.zeros(shape=(len(file_opened.readlines()), 
                                  max_len, 
                                  max(token_idx.values())+1))

 

one_hot_encoded 는 (5, 40, 124) 의 다차원 배열입니다. 5개의 텍스트 문장으로 되어 있고, 40개의 최대 단어 길이(max_len) 만을 고려하며, 총 124개의 token index 에 대해서 해당 단어가 있으면 '1', 없으면 '0'으로 one-hot encoding을 하게 된다는 뜻입니다. 

one_hot_encoded.shape
(5, 40, 124)

 

아래는 파일을 열고 텍스트를 줄 별로 읽어 들인 후에, for loop 을 돌면서 각 줄에서 단어를 분할하고 전처리하여, token_idx.get(word) 를 사용해서 해당 단어(word)의 token index를 가져온 후, 해당 텍스트(i), 단어(j), token index(idx)에 '1'을 입력하여 one_hot_encoded 다차원 배열을 업데이트 합니다. 

file_opened = open(path)
for i, line in enumerate(file_opened.readlines()):
    # strip leading and trailing edge spaces
    line = line.strip()
    
    for j, word in list(enumerate(line.split()))[:max_len]:
        
        # preprocess the word
        word = word_preprocess(word)
        
        # put word into token_index
        if word != '':
            idx = token_idx.get(word)
            one_hot_encoded[i, j, idx] = 1.

 

이렇게 생성한 one_hot_encoded 다차원배열의 결과는 아래와 같습니다. 

one_hot_encoded
array([[[0., 1., 0., ..., 0., 0., 0.],
        [0., 0., 1., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]],

       [[0., 1., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]],

       [[0., 1., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]],

       [[0., 1., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]],

       [[0., 1., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 1.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]]])
type(one_hot_encoded)
numpy.ndarray

 

이해를 돕기 위하여 python_wikipedia.txt 파일의 첫번째 줄의, 앞에서 부터 40개 단어까지의 단어 중에서, token_idx 의 1번~10번 까지만 one-hot encoding이 어떻게 되었나를 단어와 token_idx 까지 설명을 추가하여 프린트해보았습니다. (말로 설명하려니 어렵네요. ㅜ_ㅜ) 

# sort token_idx dictionary by value
import operator
sorted_token_idx = sorted(token_idx.items(), key=operator.itemgetter(1))

# print out 10 words & token_idx of 1st text's 40 words as an example
for i in range(10):
    print('word & token_idx:', sorted_token_idx[i])
    print(one_hot_encoded[0, :, i+1])
word & token_idx: ('python', 1)
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('programming', 2)
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('language', 3)
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('wikipedia', 4)
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('interpreted', 5)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('highlevel', 6)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('generalpurpose', 7)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('created', 8)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('guido', 9)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
word & token_idx: ('van', 10)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]


많은 도움이 되었기를 바랍니다. 

 

Posted by R Friend R_Friend