이번 포스팅에서는 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

이번 포스팅에서는 (1) text 또는 csv 포맷으로 저장된 텍스트 파일을 Python의 string methods 를 사용하여 파일을 열어서 파싱하여 matrix 로 저장하고,  (2) 숫자형 데이터를 표준화(standardization) 혹은 정규화(normalization) 하는 사용자 정의함수를 만들어보겠습니다. 

 

예제로 사용할 text 파일은 전복의 성별과 length, diameter, height, whole_weight, shucked_weight, viscera_weight, shell_weight, rings 를 측정한 abalone.txt 파일 입니다. 

abalone.txt
0.18MB

1. text 파일을 읽어서 숫자형 값으로 만든 matrix, 라벨을 저장한 vector를 만들기

물론, Pandas 모듈의 read_csv() 함수를 이용하여 편리하게 text, csv 포맷의 파일을 읽어올 수 있습니다. 

# importing modules
import numpy as np
import pandas as pd
import os

# setting directory
base_dir = '/Users/ihongdon/Documents/Python'
work_dir = 'dataset'
path = os.path.join(base_dir, work_dir)

# reading text file using pandas read_csv() function
df = pd.read_csv(os.path.join(path, 'abalone.txt'), 
                 sep=',', 
                 names=['sex', 'length', 'diameter', 'height', 'whole_weight', 
                        'shucked_weight', 'viscera_weight', 'shell_weight', 'rings'], 
                 header=None)
                 
# check first 5 lines
df.head()
sex	length	diameter	height	whole_weight	shucked_weight	viscera_weight	shell_weight	rings
0	M	0.455	0.365	0.095	0.5140	0.2245	0.1010	0.150	15
1	M	0.350	0.265	0.090	0.2255	0.0995	0.0485	0.070	7
2	F	0.530	0.420	0.135	0.6770	0.2565	0.1415	0.210	9
3	M	0.440	0.365	0.125	0.5160	0.2155	0.1140	0.155	10
4	I	0.330	0.255	0.080	0.2050	0.0895	0.0395	0.055	7

 

 

위의 Pandas 의 함수 말고, 아래에는 Python의 string methods 를 사용해서 파일을 열고, 파싱하는 간단한 사용자 정의함수를 직접 만들어보았습니다.

위의 abalone.txt 파일의 데이터 형태를 참고해서 파일 이름, 숫자형 변수의 개수, 숫자형 변수의 시작 위치, 숫자형 변수의 끝나는 위치, 라벨 변수의 우치를 인자로 받는 사용자 정의함수를 정의하였습니다. 분석을 하려는 각 데이터셋에 맞게 인자와 함수 code block 을 수정하면 좀더 유연하고 데이터 특성에 적합하게 파일을 불어올 수 있는 사용자 정의함수를 만들 수 있습니다. 

def file2matrix(filename, val_col_num, val_col_st_idx, val_col_end_idx, label_idx):
    """
    - filename: directory and file name
    - val_col_num: the number of columns which contains numeric values
    - val_col_st_idx: the index of starting column which contains numeric values
    - val_col_end_idx: the index of ending column which contains numeric values
    - label_idx: the index of label column
    """
    # open file
    file_opened = open(filename)
    lines_num = len(file_opened.readlines())
    
    # blank matrix and vector to store
    matrix_value = np.zeros((lines_num, val_col_num))
    vector_label = []
    
    # splits and appends value and label using for loop statement
    file_opened = open(filename)
    idx = 0
    for line in file_opened.readlines():
        # removes all whitespace in string
        line = line.strip()
        
        # splits string according to delimiter str
        list_from_line = line.split(sep=',')
        
        # appends value to matrix and label to vector
        matrix_value[idx, :] = list_from_line[val_col_st_idx : (val_col_end_idx+1)]
        vector_label.append(list_from_line[label_idx])
        idx += 1
        
    return matrix_value, vector_label

 

Python의 문자열 메소드 (string methods)는 https://rfriend.tistory.com/327 를 참고하세요. 

 

위의 file2matrix() 사용자 정의 함수를 사용하여 abalone.txt 파일을 읽어와서 (a) matrix_value, (b) vector_label 을 반환하여 보겠습니다. 

# run file2matrix() UDF
matrix_value, vector_label = file2matrix(os.path.join(path, 'abalone.txt'), 8, 1, 8, 0)

#--- matrix_value
# type
type(matrix_value)
numpy.ndarray

# shape
matrix_value.shape
(4177, 8)

# samples
matrix_value[:3]
array([[ 0.455 ,  0.365 ,  0.095 ,  0.514 ,  0.2245,  0.101 ,  0.15  , 15.    ],
       [ 0.35  ,  0.265 ,  0.09  ,  0.2255,  0.0995,  0.0485,  0.07  ,  7.    ],
       [ 0.53  ,  0.42  ,  0.135 ,  0.677 ,  0.2565,  0.1415,  0.21  ,  9.    ]])
       
#--- vector_label
# type
type(vector_label)
list

# number of labels
len(vector_label)
4177

# samples
vector_label[:3]
['M', 'M', 'F']

 

 

2-1. 숫자형 데이터를 표준화(Standardization) 하기

위의 숫자형 데이터로 이루어진 matrix_value 를 numpy를 이용해서 표준화, 정규화하는 사용자 정의함수를 작성해보겠습니다. (물론 scipy.stats 의 zscore() 나 sklearn.preprocessing 의 StandardScaler() 함수를 사용해도 됩니다.)

 

아래의 사용자 정의 함수는 숫자형 데이터로 이루어진 데이터셋을 인자로 받으면, 평균(mean)과 표준편차(standard deviation)를 구하고, standardized_value = (x - mean) / standard_deviation 으로 표준화를 합니다. 그리고 표준화한 matrix, 각 칼럼별 평균과 표준편차를 반환합니다. 

def standardize(numeric_dataset):

    # standardized_value = (x - mean)/ standard_deviation
    
    # calculate mean and standard deviation per numeric columns
    mean_val = numeric_dataset.mean(axis=0)
    std_dev_val = numeric_dataset.std(axis=0)
    
    # standardization
    matrix_standardized = (numeric_dataset - mean_val)/ std_dev_val
    
    return matrix_standardized, mean_val, std_dev_val

 

위의 standardize() 함수를 사용하여 matrix_value 다차원배열을 표준화해보겠습니다. 

# rund standardize() UDF
matrix_standardized, mean_val, std_dev_val = standardize(matrix_value)

# matrix after standardization
matrix_standardized
array([[-0.57455813, -0.43214879, -1.06442415, ..., -0.72621157,
        -0.63821689,  1.57154357],
       [-1.44898585, -1.439929  , -1.18397831, ..., -1.20522124,
        -1.21298732, -0.91001299],
       [ 0.05003309,  0.12213032, -0.10799087, ..., -0.35668983,
        -0.20713907, -0.28962385],
       ...,
       [ 0.6329849 ,  0.67640943,  1.56576738, ...,  0.97541324,
         0.49695471, -0.28962385],
       [ 0.84118198,  0.77718745,  0.25067161, ...,  0.73362741,
         0.41073914,  0.02057072],
       [ 1.54905203,  1.48263359,  1.32665906, ...,  1.78744868,
         1.84048058,  0.64095986]])
 
 # mean per columns
 mean_val
 array([0.5239921 , 0.40788125, 0.1395164 , 0.82874216, 0.35936749,
       0.18059361, 0.23883086, 9.93368446])
       
 # standard deviation per columns
 std_dev_val
 array([0.12007854, 0.09922799, 0.04182205, 0.49033031, 0.22193638,
       0.10960113, 0.13918601, 3.22378307])

 

2-2. 숫자형 데이터를 정규화(Normalization) 하기

다음으로 척도, 범위가 다른 숫자형 데이터를 [0, 1] 사이의 값으로 변환하는 정규화(Normalization)를 해보겠습니다. normalized_value = (x - minimum_value) / (maximum_value - minimum_value) 로 계산합니다. 

def normalize(numeric_dataset):
    
    # normalized_value = (x - minimum_value) / (maximum_value - minimum_value)
    
    # calculate mean and standard deviation per numeric columns
    min_val = numeric_dataset.min(axis=0)
    max_val = numeric_dataset.max(axis=0)
    ranges = max_val - min_val
    
    # normalization, min_max_scaling
    matrix_normalized = (numeric_dataset - min_val)/ ranges
    
    return matrix_normalized, ranges, min_val

 

위의 normalize() 사용자 정의 함수에 matrix_value 다차원배열을 적용해서 정규화 변환을 해보겠습니다. 정규화된 다차원배열과 범위(range = max_val - min_val), 최소값을 동시에 반환합니다. 

# run normalize() UDF
matrix_normalized, ranges, min_val = normalize(matrix_value)

# normalized matrix
matrix_normalized
array([[0.51351351, 0.5210084 , 0.0840708 , ..., 0.1323239 , 0.14798206,
        0.5       ],
       [0.37162162, 0.35294118, 0.07964602, ..., 0.06319947, 0.06826109,
        0.21428571],
       [0.61486486, 0.61344538, 0.11946903, ..., 0.18564845, 0.2077728 ,
        0.28571429],
       ...,
       [0.70945946, 0.70588235, 0.18141593, ..., 0.37788018, 0.30543099,
        0.28571429],
       [0.74324324, 0.72268908, 0.13274336, ..., 0.34298881, 0.29347285,
        0.32142857],
       [0.85810811, 0.84033613, 0.17256637, ..., 0.49506254, 0.49177877,
        0.39285714]])
        
# ranges
ranges
array([ 0.74  ,  0.595 ,  1.13  ,  2.8235,  1.487 ,  0.7595,  1.0035,  28.    ])

# minimum value
min_val
array([7.5e-02, 5.5e-02, 0.0e+00, 2.0e-03, 1.0e-03, 5.0e-04, 1.5e-03, 1.0e+00])

 

다음번 포스팅에서는 텍스트 파일을 파싱해서 One-Hot Encoding 하는 방법을 소개하겠습니다. 

 

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

Posted by R Friend R_Friend

CNN(Convolutional Neural Network)으로 이미지 분류 모델링할 때 보통 tensorflow나 keras 라이브러리에 이미 포함되어 있는 MNIST, CIFAR-10 같은 이미지를 간단하게 load 하는 함수를 이용해서 toy project로 연습을 해보셨을 겁니다. 

그런데, 실제 이미지, 그림 파일을 분석해야 될 경우 '어? 이미지를 어떻게 업로드 하고, 어떻게 전처리하며, 어떻게 시각화해야 하는거지?'라는 의문을 한번쯤은 가져보셨을 듯 합니다. 

이번 포스팅에서는 바로 이 의문에 대한 답변 소개입니다. 


필요한 Python 라이브러리를 불러오겠습니다. 

 import numpy as np

 import pandas as pd

 import matplotlib.pyplot as plt

 import keras 



 1. 개와 고양이 사진 다운로드 (download dogs and cats images from Kaggle)

개와 고양이 사진을 아래의 Kaggle 사이트에서 다운로드 해주세요. Kaggle 회원가입을 먼저 해야지 다운로드 할 수 있습니다. 개는 1, 고양이는 0으로 라벨링이 되어 있는 25,000 개의 이미지를 다운받을 수 있습니다. 

https://www.kaggle.com/c/dogs-vs-cats/data



2. 개와 고양이 이미지 30개만 선택해서 별도 경로(폴더)에 복사하기


downloads 폴더에 들어있는 압축된 다운로드 파일을 압축 해제(unzip)해 주세요. 


윈도우 탐색기로 미리보기를 해보면 고양이 반, 개 반 입니다. 


directory, path 관리하는데 필요한 os 라이브러리, 파일을 source에서 destination 경로로 복사하는데 필요한 shutil 라이브러리를 불러오겠습니다. 

 import os # miscellaneous operating system interfaces

 import shutil # high-level file operations


이미지를 가져올 경로를 설정해보겠습니다. ('Downdoads/dogs-vs-cats/train' 경로에 train 폴더를 압축해제해 놓았습니다. 폴더 경로 확인 요함.)

# The path to the directory where the original dataset was uncompressed

 base_dir = '/Users/admin/Downloads'

 img_dir = '/Users/admin/Downloads/dogs-vs-cats/train'


train 폴더에 들어있는 개와 고양이 이미지가 총 25,000개 임을 확인했으며,  img_dir 경로에 포함되어 있는 이미지 중에서 10개만 indexing 해서 파일 제목을 확인해보았습니다. 

 len(os.listdir(img_dir))

 25000

os.listdir(img_dir)[:10]

['dog.8011.jpg',
 'cat.5077.jpg',
 'dog.7322.jpg',
 'cat.2718.jpg',
 'cat.10151.jpg',
 'cat.3406.jpg',
 'dog.1753.jpg',
 'cat.4369.jpg',
 'cat.7660.jpg',
 'dog.5535.jpg']


30개의 이미지만 샘플로 선별해서 다른 폴더로 복사해보겠습니다. 먼저, 30개 고양이 이미지를 담아둘 경로/ 폴더(cats30_dir) 를 만들어보겠습니다. 

# Directory with 30 cat pictures

 cats30_dir = os.path.join(base_dir, 'cats30')


 # Make a path directory

 os.mkdir(cats30_dir)


이제 source 경로에서 destination 경로로 shutil.copyfile(src, dst) 함수를 사용하여 고양이 이미지 30개만 이미지를 복사하겠습니다.  

# Copy first 30 cat images to cats30_dir

 fnames = ['cat.{}.jpg'.format(i) for i in range(30)]

 

 for fname in fnames:

     src = os.path.join(img_dir, fname)

     dst = os.path.join(cats30_dir, fname)

     shutil.copyfile(src, dst)


cats30_dir 경로로 복사한 30개의 고양이 이미지 파일 목록을 확인해 보았습니다. 

# check if pictures were copied well in cats30 directory

 os.listdir(cats30_dir)

['cat.6.jpg',
 'cat.24.jpg',
 'cat.18.jpg',
 'cat.19.jpg',
 'cat.25.jpg',
 'cat.7.jpg',
 'cat.5.jpg',
 'cat.27.jpg',
 'cat.26.jpg',
 'cat.4.jpg',
 'cat.0.jpg',
 'cat.22.jpg',
 'cat.23.jpg',
 'cat.1.jpg',
 'cat.3.jpg',
 'cat.21.jpg',
 'cat.20.jpg',
 'cat.2.jpg',
 'cat.11.jpg',
 'cat.10.jpg',
 'cat.12.jpg',
 'cat.13.jpg',
 'cat.9.jpg',
 'cat.17.jpg',
 'cat.16.jpg',
 'cat.8.jpg',
 'cat.28.jpg',
 'cat.14.jpg',
 'cat.15.jpg',
 'cat.29.jpg']



 3. 이미지 파일을 로딩, float array 로 변환 후 전처리하기
    (load image file and convert image data to float array format) 

Keras preprocessing 에 있는 image 클래스를 불러온 후, load_img() 함수를 사용해서 이미지 파일을 로딩하고, img_to_array() 함수를 사용해서 array 로 변환해보겠습니다. (Python OpenCV 라이브러리로도 가능함)

# a picture of one cat as an example

 img_name = 'cat.10.jpg'

 img_path = os.path.join(cats30_dir, img_name)


 # Preprocess the image into a 4D tensor using keras.preprocessing

 from keras.preprocessing import image


 img = image.load_img(img_path, target_size=(250, 250))

 img_tensor = image.img_to_array(img)


3차원 array에 이미지 샘플을 구분할 수 있도록 np.expand_dims() 함수를 사용하여 1개 차원을 추가하겠습니다. 그리고 [0, 1] 값 범위 내에 값이 존재하도록 array 값을 255.로 나누어서 표준화해주었습니다. 

  # expand a dimension (3D -> 4D)

 img_tensor = np.expand_dims(img_tensor, axis=0)

 img_tensor.shape

 (1, 250, 250, 3)

 

 # scaling into [0, 1]

 img_tensor /= 255.


첫번째 고양이 이미지의 array 데이터를 출력해보면 아래처럼 생겼습니다. 꼭 영화 메트릭스의 숫자들이 주루룩 내려오는 장면 같이 생겼습니다. 

img_tensor[0]

array([[[0.10196079, 0.11764706, 0.15294118],
        [0.07450981, 0.09019608, 0.1254902 ],
        [0.03137255, 0.04705882, 0.09019608],
        ...,
        [0.5058824 , 0.6313726 , 0.61960787],
        [0.49411765, 0.61960787, 0.60784316],
        [0.49019608, 0.6156863 , 0.6039216 ]],

       [[0.11764706, 0.13333334, 0.16862746],
        [0.13725491, 0.15294118, 0.1882353 ],
        [0.08627451, 0.10196079, 0.13725491],
        ...,
        [0.50980395, 0.63529414, 0.62352943],
        [0.49803922, 0.62352943, 0.6117647 ],
        [0.4862745 , 0.6117647 , 0.6       ]],

       [[0.11372549, 0.14117648, 0.16470589],
        [0.16470589, 0.19215687, 0.22352941],
        [0.15294118, 0.18039216, 0.21176471],
        ...,
        [0.50980395, 0.63529414, 0.62352943],
        [0.5019608 , 0.627451  , 0.6156863 ],
        [0.49019608, 0.6156863 , 0.6039216 ]],

       ...,

       [[0.69411767, 0.6431373 , 0.46666667],
        [0.6862745 , 0.63529414, 0.45882353],
        [0.6627451 , 0.6117647 , 0.4392157 ],
        ...,
        [0.7254902 , 0.70980394, 0.04313726],
        [0.6745098 , 0.6509804 , 0.03921569],
        [0.64705884, 0.6156863 , 0.05490196]],

       [[0.64705884, 0.5921569 , 0.45490196],
        [0.6117647 , 0.5568628 , 0.4117647 ],
        [0.5686275 , 0.5176471 , 0.3529412 ],
        ...,
        [0.7254902 , 0.7137255 , 0.01960784],
        [0.6862745 , 0.67058825, 0.00784314],
        [0.6509804 , 0.6313726 , 0.        ]],

       [[0.6039216 , 0.54901963, 0.4117647 ],
        [0.5882353 , 0.53333336, 0.3882353 ],
        [0.5803922 , 0.5294118 , 0.3647059 ],
        ...,
        [0.7254902 , 0.7137255 , 0.01960784],
        [0.6862745 , 0.67058825, 0.00784314],
        [0.6509804 , 0.6313726 , 0.        ]]], dtype=float32)



  4. 한개의 이미지 파일의 array 를 시각화하기 (visualizing an image array data)

matplotlib 라이브러리를 이용하여 위의 3번에서 이미지의 array 변환/ 전처리한 데이터를 시각화해보겠습니다. 예제로서 img_tensor[0] 으로 첫번째 고양이 이미지의 데이터를 시각화했습니다. 

# Image show

 import matplotlib.pyplot as plt

 plt.rcParams['figure.figsize'] = (10, 10) # set figure size

 

 plt.imshow(img_tensor[0])

 plt.show()




  5. 30개의 이미지 데이터를 6*5 격자에 나누어서 시각화하기 
    (visualizing 30 image data at 6*5 grid layout)

위의 3번에서 했던 이미지 파일 로딩, array로 변환, 1개 차원 추가, [0, 1] 범위로 표준화하는 전처리를 preprocess_img() 라는 이름의 사용자정의함수(UDF)로 만들었습니다. 

# UDF of pre-processing image into a 4D tensor

 def preprocess_img(img_path, target_size=100):

     from keras.preprocessing import image

     

     img = image.load_img(img_path, target_size=(target_size, target_size))

     img_tensor = image.img_to_array(img)

    

     # expand a dimension

     img_tensor = np.expand_dims(img_tensor, axis=0)

     

     # scaling into [0, 1]

     img_tensor /= 255.

     

     return img_tensor


이제 30개의 고양이 이미지 array 데이터를 사용해서 행(row) 6개 * 열(column) 5개의 격자 배열(grid layout) 에 시각화를 해보겠습니다. 이때 가독성을 높이기 위해서 고양이 사진 간에 검정색 구분선을 넣어서 시각화를 해보겠습니다. 

참고로, 아래 코드의 for loop 중간에 방금 전에 위에서 정의한 preprocess_img() 사용자정의함수 (빨간색으로 표기) 가 사용되었습니다. 

# layout

n_pic = 30

n_col = 5

n_row = int(np.ceil(n_pic / n_col))


# plot & margin size

target_size = 100

margin = 3


# blank matrix to store results

total = np.zeros((n_row * target_size + (n_row - 1) * margin, n_col * target_size + (n_col - 1) * margin, 3))


# append the image tensors to the 'total matrix'

img_seq = 0


for i in range(n_row):

    for j in range(n_col):


        fname = 'cat.{}.jpg'.format(img_seq)

        img_path = os.path.join(cats30_dir, fname)


        img_tensor = preprocess_img(img_path, target_size)


        horizontal_start = i * target_size + i * margin

        horizontal_end = horizontal_start + target_size

        vertical_start = j * target_size + j * margin

        vertical_end = vertical_start + target_size


        total[horizontal_start : horizontal_end, vertical_start : vertical_end, :] = img_tensor[0]

        

        img_seq += 1


# display the pictures in grid

plt.figure(figsize=(200, 200))

plt.imshow(total)

plt.show()


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

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


Posted by R Friend R_Friend