지난번 포스팅에서는 DataFrame 의 행과 열 기준으로 데이터 선택해서 가져오기 (indexing and selection)에 대해서 알아보았습니다.

 

index를 처음 만들기는 했는데요, 필요에 따라서 수정해야 할 필요가 생길 수도 있겠지요?

 

이번 포스팅에서는

 

 - (1) index 재설정하기 (reindex)

 

 - (2) reindex 과정에서 생기는 결측값 채우기
       (fill in missing values)

 

방법에 대해서 소개하겠습니다.

 

 

먼저, 필요한 library를 import 하고, dit와 index를 사용해서 간5행, 2열을 가진 간단한 DataFrame을 만들어보겠습니다.

 

 

##-- Make a new index and reindex the dataframe

 

In [1]: import numpy as np

   ...: import pandas as pd

   ...: from pandas import DataFrame

 

In [2]: idx = ['r0', 'r1', 'r2', 'r3', 'r4']

   ...:

   ...: df_1 = pd.DataFrame({

   ...: 'c1': np.arange(5),

   ...: 'c2': np.random.randn(5)},

   ...: index=idx)

 

In [3]: df_1

Out[3]:

    c1        c2
r0   0  1.182716
r1   1  0.244398
r2   2 -1.494202
r3   3  0.146152
r4   4 -0.352680

 

 

 

 

위 예에서 df_1 DataFrame의 행 index 가 ['r0', 'r1', 'r2', 'r3', 'r4'] 인데요, ['r3', 'r4']를 빼고 ['r5', 'r6']를 새로 추가하고 싶다고 해봅시다.  이때 사용하는 것이 'reindex' 입니다.

 

 

  (1-1) index 재설정하기 : reindex

 

 

##-- Make a new index and reindex the dataframe

 

In [4]: new_idx= ['r0', 'r1', 'r2', 'r5', 'r6']


In [5]: df_1.reindex(new_idx)

Out[5]:

     c1        c2
r0  0.0  1.182716
r1  1.0  0.244398
r2  2.0 -1.494202
r5  NaN       NaN
r6  NaN       NaN

 

 

이전에 없던 ['r5', 'r6'] index가 추가되자 'NaN' 값이 디폴트로 채워쳤습니다.  'NaN' 대신에 fill_value 파라미터를 사용해서 '0', 혹은 'missing', 'NA' 등으로 바꿔서 채워보겠습니다.

 

 

 

  (1-2) reindex 과정에서 생긴 결측값 채우기 (fill in missing values) : fill_value

 

 

##-- Fill in the missing values by passing a value to the keyword fill_value

 

In [8]: df_1.reindex(new_idx, fill_value=0)

Out[8]:

    c1        c2
r0   0  1.182716
r1   1  0.244398
r2   2 -1.494202
r5   0  0.000000
r6   0  0.000000

 

 

 

 

In [9]: df_1.reindex(new_idx, fill_value='missing')

Out[9]:

         c1        c2
r0        0   1.18272
r1        1  0.244398
r2        2   -1.4942
r5  missing   missing
r6  missing   missing

 

 

 

In [10]: df_1.reindex(new_idx, fill_value='NA')
Out[10]:

    c1        c2
r0   0   1.18272
r1   1  0.244398
r2   2   -1.4942
r5  NA        NA
r6  NA        NA

 

 

 


 

시계열 데이터 (TimeSeries Data)는 DataFrame의 index 만들 때 pd.date_range(date, periods, freq) 를 사용합니다. (시계열 데이터 처리, 분석은 나중에 따로 많이 포스팅하겠습니다.)

 

먼저, 시계열 데이터로 DataFrame 만들어보겠습니다.

 

 

In [11]: date_idx = pd.date_range('11/27/2016', periods=5, freq='D')


In [12]: date_idx

Out[12]:

DatetimeIndex(['2016-11-27', '2016-11-28', '2016-11-29', '2016-11-30',

'2016-12-01'],

dtype='datetime64[ns]', freq='D')


In [13]: df_2 = pd.DataFrame({"c1": [10, 20, 30, 40, 50]}, index=date_idx)


In [14]: df_2

Out[14]:

                c1

2016-11-27 10

2016-11-28 20

2016-11-29 30

2016-11-30 40

2016-12-01 50

 

 

 

 

위에서 만든 시계열 데이터 DataFrame 의 date 앞/뒤로 reindex 를 사용해서 날짜 몇 개를 새로 추가해보겠습니다.

 

  (2-1) 시계열 데이터 index 재설정 하기 (reindex of TimeSeries Data)

 

 

In [15]: date_idx_2 = pd.date_range('11/25/2016', periods=10, freq='D')


In [16]: df_2.reindex(date_idx_2)

Out[16]:

                 c1

2016-11-25 NaN

2016-11-26 NaN

2016-11-27 10.0

2016-11-28 20.0

2016-11-29 30.0

2016-11-30 40.0

2016-12-01 50.0

2016-12-02 NaN

2016-12-03 NaN

2016-12-04 NaN

 

 

 

 

  (2-2) 시계열 데이터 reindex 과정에서 생긴 결측값 채우기 : method='ffill', 'bfill'
         (fill in missing value of TimeSeries Data)

 

reindex 하면서 결측값을 채우는 방법으로 method='ffill'을 사용해서 결측값 직전의 값으로 이후 결측값을 채워보겠습니다.

 

 

In [17]: df_2.reindex(date_idx_2, method='ffill') # forward-propagation

Out[17]:

                   c1
2016-11-25   NaN
2016-11-26   NaN
2016-11-27  10.0
2016-11-28  20.0
2016-11-29  30.0
2016-11-30  40.0
2016-12-01  50.0
2016-12-02  50.0
2016-12-03  50.0
2016-12-04  50.0

 

 

 

 

이번에는 reindex 하면서 method='bfill' 을 사용해서 시간 뒷 순서의 결측값으로 이전 결측값을 채워보겠습니다.  

 

 

In [18]: df_2.reindex(date_idx_2, method='bfill') # back-propagation

Out[18]:

                  c1
2016-11-25  10.0
2016-11-26  10.0
2016-11-27  10.0
2016-11-28  20.0
2016-11-29  30.0
2016-11-30  40.0
2016-12-01  50.0
2016-12-02   NaN
2016-12-03   NaN
2016-12-04   NaN

 

 

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

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas의 DataFrame 만들기, Attributes 조회하기에 대해서 알아보았습니다.

 

이번 포스팅에서는 DataFrame의 데이터를

 

 - (1) 행(row) 기준으로 선택해서 가져오기

 - (2) 열(column) 기준으로 선택해서 가져오기

 

방법(DataFrame objects indexing and selection by rows or columns)에 대해서 소개하겠습니다.

 

 

먼저, 필요한 Libraries 를 importing하고, 간단한 5행 3열의 DataFrame을 만들어 보겠습니다.

 

 

In [1]: import numpy as np

   ...: import pandas as pd

   ...: from pandas import DataFrame

   ...:

   ...: ##-- Making DataFrame

   ...: df_2 = DataFrame({'class_1': ['a', 'a', 'b', 'b', 'c'],

   ...:                          'var_1': np.arange(5),

   ...:                          'var_2': np.random.randn(5)},

   ...:                          index = ['r0', 'r1', 'r2', 'r3', 'r4'])

   ...:

   ...: df_2

Out[1]:

   class_1  var_1     var_2
r0       a      0  2.896618
r1       a      1 -0.113472
r2       b      2  0.261695
r3       b      3 -0.260788
r4       c      4 -0.791744

 

 

 

 

 

 (1) 행 기준으로 선택해서 가져오기 (indexing and selection by row)

 

DataFrame의 index 를 확인해보겠습니다.

 

 

In [2]: df_2.index # returning index

Out[2]: Index(['r0', 'r1', 'r2', 'r3', 'r4'], dtype='object')

 

 

 

'ix'를 사용하면 행 기준 indexing할 때 정수(int)와 행 이름(row label) 모두 사용할 수 있어서 편리합니다.

조건을 조금씩 달리해가면서 몇 가지 예를 아래에 들어보겠습니다. 서로 다른 점을 유심히 살펴보시면 어렵지 않게 사용법을 이해하실 수 있을 겁니다. 어렵지 않아요.

 

 

 

In [4]: df_2.ix[2:] # indexing from int. position to end

Out[4]:

   class_1  var_1     var_2
r2       b      2  0.261695
r3       b      3 -0.260788
r4       c      4 -0.791744

 

 

 

 

In [5]: df_2.ix[2] # indexing specific row with int. position

Out[5]:

class_1           b
var_1             2
var_2      0.261695
Name: r2, dtype: object

 

 

 

 

In [6]: df_2.ix['r2'] # indexing specific row with row label

Out[6]:

class_1           b
var_1             2
var_2      0.261695
Name: r2, dtype: object

 

 

 

 

데이터가 매우 많은 수의 행을 가지고 있을 경우에 위로 부터 n개의 행만 보고 싶은 때는 head(n) 메소드를 사용하면 됩니다.

 

 

In [7]: df_2.head(2) # Returns first n rows

Out[7]:

   class_1  var_1     var_2
r0       a      0  2.896618
r1       a      1 -0.113472

 

 

 

 

tail(n) 메소드는 행의 제일 마지막부터 n번째까지의 행 기준 데이터를 반환합니다.

 

 

In [8]: df_2.tail(2) # Returns last n rows

 

Out[8]:   

   class_1  var_1     var_2
r3       b      3 -0.260788
r4       c      4 -0.791744

 

 

 

 

 

 

 

  (2) 열 기준으로 선택해서 가져오기 (indexing and selection by column)

 

 

df_2 DataFrame의 열을 .columns 로 확인해 보겠습니다.

 

 

In [12]: df_2.columns
Out[12]: Index(['class_1', 'var_1', 'var_2'], dtype='object')

 

 

 

 

열(column) 기준으로 indexing할때는 '[ ]' 안에 열 이름(column label)을 'string' 형식으로 입력해주면 됩니다.

 

 

In [13]: df_2['class_1']

Out[13]:

r0    a
r1    a
r2    b
r3    b
r4    c
Name: class_1, dtype: object

 

 

 

 

두 개이상의 열(columns)을 가져오고 싶을 때는 튜플(tuple)을 사용해서 열의 이름을 나열해 주면 됩니다.

 

 

In [14]: df_2[['class_1', 'var_1']]

Out[14]:

   class_1  var_1
r0       a      0
r1       a      1
r2       b      2
r3       b      3
r4       c      4

 

 

 

이상으로 DataFrame Indexing and Selection에 대해서 마치겠습니다.

 

다음번 포스팅에서는 DataFrame index의 reindexing에 대해서 알아보겠습니다.

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas에서 가장 중요하게 사용되는 Data 구조인

 

 - (1) DataFrame을 만들어보고,

 

 - (2) 다양한 Attributes 를 조회

 

하는 방법에 대해서 알아보겠습니다.

 

 

먼저 필요한 Library 들을 importing 하겠습니다.

 

 

In [1]: import numpy as np

   ...: import pandas as pd

   ...: from pandas import DataFrame as df

 

 

 

 

  (1) pandas DataFrame 만들기

 

pd.DataFrame() 에서 사용하는 Paraeter 들에는 (1) data, (2) index, (3) columns, (4) dtype, (5) copy 의 5가지가 있습니다.

 

(1-1) data : numpy ndarray, dict, DataFrame 등의 data source

(1-2) index : 행(row) 이름, 만약 명기하지 않으면 np.arange(n)이 자동으로 할당 됨

(1-3) column : 열(column) 이름, 만약 명기하지 않으면 역시 np.arnage(n)이 자동으로 할당 됨

(1-4) dtype : 데이터 형태(type), 만약 지정하지 않으면 Python이 자동으로 추정해서 넣어줌

(1-5) copy : 입력 데이터를 복사할지 지정. 디폴트는 False 임. (복사할 거 아니면 메모리 관리 차원에서 디폴트인 False 설정 사용하면 됨)

 

 

3행 4열짜리 간단한 DataFrame을 만들어보겠습니다.  data  란에 input data 지정은 필수로 해줘야 하구요, 나머지 index, columns, dtype, copy는 별도로 명기를 안해줘도 디폴트 세팅이 적용되어서 DataFrame이 생성이 되긴 합니다.

 

 

In [2]: df_1 = df(data=np.arange(12).reshape(3, 4),

   ...: index=['r0', 'r1', 'r2'], # Will default to np.arange(n) if no indexing

   ...: columns=['c0', 'c1', 'c2', 'c3'],

   ...: dtype='int', # Data type to force, otherwise infer

   ...: copy=False) # Copy data from inputs

 

In [3]: df_1

Out[3]: 
    c0  c1  c2  c3
r0   0   1   2   3
r1   4   5   6   7
r2   8   9  10  11

 

 

 

 

  (2) DataFrame 의 Attributes 조회하기

 

 

다음으로 DataFrame의 Attributes을 조회하는 방법을 소개하겠습니다.

참고로, 아래 Attributes의 끝에는 괄호 ()를 붙이지 않으니 헷갈리지 않도록 조심하세요.

 

 

(2-1) T : 행과 열 전치 (transpose)

 

 

In [5]: df_1.T # Transpose index and columns

Out[5]:

c3   3   7  11
c0   0   4   8
c1   1   5   9
c2   2   6  10
c3   3   7  11 

 

 

 

(2-2) axes : 행과 열 이름을 리스트로 반환

 

 

In [6]: df_1.axes

Out[6]:

[Index(['r0', 'r1', 'r2'], dtype='object'),

Index(['c0', 'c1', 'c2', 'c3'], dtype='object')]

 

 

 

 

(2-3) dtypes : 데이터 형태 반환

 

 

In [7]: df_1.dtypes # Return the dtypes in this object

Out[7]:

c0 int32

c1 int32

c2 int32

c3 int32

dtype: object

 

 

 

 

(2-4) shape : 행과 열의 개수(차원)을 튜플로 반환

 

 

In [22]: df_1.shape # Return a tuple representing the dimensionality of the DataFrame

Out[22]: (3, 4)

 

 

 

 

(2-5) size : NDFrame의 원소의 개수를 반환

 

 

In [23]: df_1.size # number of elements in the NDFrame

Out[23]: 12

 

 

 

 

(2-6) values : NDFrame의 원소를 numpy 형태로 반환

 

 

In [24]: df_1.values # Numpy representation of NDFrame

Out[24]:

array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

 

 

 

다음번 포스팅에서는 DataFrame에서 indexing 하는 방법을 소개하겠습니다.

 

 

728x90
반응형
Posted by Rfriend
,

이전 포스팅에서는 (1) Python의 pandas read_csv() 함수를 사용해서 외부 text, csv 파일을 읽어들이는 방법과, (2) DB connection 해서 DB로 부터 직접 Data를 읽어와서 DataFrame으로 만드는 방법을 소개하였습니다.

 

이번 포스팅에서는 이전과는 반대로 Python의 pandas library 를 사용해서 DataFrame을 csv 파일로 내보내는 방법을 소개하겠습니다.

 

pandas의 DataFrame.to_csv() 함수를 사용합니다.

 

 

먼저 실습에 필요한 Python library를 import 하겠습니다.

 

 

In [51]: import pandas as pd


In [52]: from pandas import DataFrame

 

 

 

 

다음으로, csv 파일로 내보내는데 사용할 간단한 DataFrame을 dict를 사용해서 만들어보겠습니다.

 

 

In [53]: data = {'ID': ['A1', 'A2', 'A3', 'A4', 'A5'],

    ...: 'X1': [1, 2, 3, 4, 5],

    ...: 'X2': [3.0, 4.5, 3.2, 4.0, 3.5]}


In [54]: data_df = DataFrame(data, index=['a', 'b', 'c', 'd', 'e']) # converting to DataFrame


In [55]: data_df

Out[55]:

ID X1 X2

a A1 1 3.0

b A2 2 4.5

c A3 3 3.2

d A4 4 4.0

e A5 5 3.5

 

 

 

 

결측값(Missing Value)을 csv 파일로 내보낼 때 표기 지정하는 매개변수 설명을 위해서, 제일 마지막 행(row)에 결측값을 추가해보겠습니다.

 

 

In [56]: data_df_2 = data_df.reindex(['a', 'b', 'c', 'd', 'e', 'f'])


In [57]: data_df_2 # 'f' : NaN

Out[57]:

ID X1 X2

a A1 1.0 3.0

b A2 2.0 4.5

c A3 3.0 3.2

d A4 4.0 4.0

e A5 5.0 3.5

f NaN NaN NaN

 

 

 

 

자, 이제 'data_df_2' 라는 DataFrame을 to_csv() 를 사용해서 csv 파일로 내보내보겠습니다.

DataFrame.to_csv('path\\file_name.csv', sep=',', na_rep='NaN') 의 형식으로 설정해주면 됩니다.

 

 

In [60]: data_df_2.to_csv('C:\\Documents\\Python\\data_df_2.csv', # file path, file name

    ...: sep=',',   # seperator, delimiter (구분자)

    ...: na_rep='NaN')   # missing data representation (결측값 표기)

 

 

 

 

위에 지정해준 경로에 가서 'data_df_2.csv' 파일을 열어보니 아래처럼 데이터가 잘 들어가 있음을 확인할 수 있습니다.

 

 

 

아래는 디폴트 설정으로서 참고하시기 바랍니다. (아래 디폴트 설정과 다를때만 False 로 명기하면 되며, 그 외에는 별도 명기 필요 없음)

 

header = True (첫번째 줄을 칼럼 이름으로 사용)

columns = 특정 칼럼만 csv 로 쓰기 (내보내기) 할 때 칼럼 이름을 list에 적어줌

index = True (행의 이름 index 도 같이 내보냄. index 내보내기 싫으면 False 명기)

float_format = '%.2f' (예: float8 을 소수점 둘째 자리까지 표기)

encoding = 'utf-8' (on Python 3)

line_terminator = '\n' (엔터로 줄 바꿈)

date_format = None (datetime 객체에 대한 format 설정하지 않음)

 

data_df_2.to_csv('C:/Users/admin/Documents/data_df_x2.csv',

                 sep=',',

                 na_rep='NaN', 

                 float_format = '%.2f', # 2 decimal places

                 columns = ['ID', 'X2'], # columns to write

                 index = False) # do not write index





더 많은 to_csv() 매개변수를 보기 원하면 아래 Reference 사이트를 참고하세요.

 

[Reference] http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_csv.html

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas 로 다양한 종류의 DB에 접속해서 SQL query로 Data를 select 해서 Python pandas의 DataFrame 으로 불러오는 방법을 소개하도록 하겠습니다.

 

만약 DB에 Python으로 직접 접속하지 않는 다면 DB에 있는 Data를 이용하기 위해서는 (1) DB에서 csv 파일로 데이터 exporting 하기, (2) csv 파일을 Python pandas 의 pd.csv_read() 로 불러오기의 두 단계의 절차를 거쳐야 합니다. (지난번 포스팅에서 client PC에 있는 text 혹은 csv 파일을 불러오는 방법을 소개했었습니다. )

 

이번 포스팅의 DB connection & SQL Query in Python 방법을 사용하면 csv 파일로 내리고 다시 불러오는 단계가 줄어들기 때문에 좀더 편리하게 DB로 부터 Data 불러오기를 할 수 있을 것입니다.

 

 

 

 

Oracle DB, IBM DB2, PostgreSQL, MariaDB 의 순서대로 소개하겠습니다.

사용자 정의 함수의 query 부분에 사용하고자 하는 SQL query 를 작성해서 실행하면 됩니다.

 

 

  1. Oracle DB에 Python으로 접속하여 SQL query 해서 pandas DataFrame 만들기

 

명령 프롬프트 창에서 cx_Oracle 라이브러리를 설치합니다. 


 $ pip install cx_Oracle



다음으로, 컴퓨터에서 tnsnames.ora 파일에서 연결 정보(connection information)을 찾습니다. 
아래 예시에서 색깔 칠한 부분에서 Host Name, Port Number, Service Name을 찾을 수 있습니다. 

SYSTEM_OCON =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = Host Name)(PORT = Port Number))
(LOAD_BALANCE = YES)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = Service Name)
(FAILOVER_MODE =
(TYPE = SELECT)
(METHOD = BASIC)
(RETRIES = 180)

(DELAY = 5) 


(* Reference : How to Connect Python to an Oracle Database using cx_Oracle)


이제 준비가 되었으니 Oracle DB에 Python으로 접속해서 Query를 실행시키고, 조회 결과를 Pandas DataFrame으로 가져와서 저장하는 사용자 정의함수를 정의해보겠습니다. 

##-- User defined function for Oracle DB SQL query

 

def query_OracleSQL(query):
     
     import pandas as pd
     import cx_Oracle as co
     from datetime import datetime

     start_tm = datetime.now()

     #  DB Connecion
     dsn_tns = co.makedsn("Host Name", "Port Number", service_name="Service Name")
     conn = co.connect(user="User Name", password="Personal Password", dsn=dnsStr)

     # Get a dataframe
     query_result = pd.read_sql(query, conn)

     # Close connection
     conn.close()

     end_tm = datetime.now()

     print('START: ', str(start_tm))
     print('END: ', str(end_tm))
     print('ELAP: ', str(end_tm - start_tm))


     return query_result

 

 

##-- SQL query
query = """
     SELECT var1, var2, ymd, count(*) as cnt
          FROM myOracleDB
          WHERE ymd BETWEEN
               to_date('2016-11-22T00:00:00', 'YYYY-MM-DD"T"HH24:MI:SS')
               AND
               to_date('2016-11-22T23:59:59', 'YYYY-MM-DD"T"HH24:MI:SS')
GROUP BY var1
ORDER BY cnt
               """

##-- Excute OracleDB SQL in Python
query_OracleSQL(query)

 

 

 

 

  2. IBM DB2에 Python으로 접속하여 SQL query 해서 pandas DataFrame 만들기

 

##-- User defined function for IBM DB2 SQL query
def query_DB2(query):
     import sqlalchemy as sa
     import pandas as pd
     from datetime import datetime

     # DB Connetion
     engine = sa.create_engine('ibm_db_sa://xx(id):xx(pw)@xx.xx.xxx.xx(ip):xxxx(port)/xxx(DB)', echo=False)
     conn = engineconnect()
     start_tm = datetime.now()

     # Get a dataframe
     execonn = engine.execute(query)

     query_result = df(execonn.fetchall())
     query_result.columns = execonn.keys()

     # Close connection

     end_tm = datetime.now() 
     print('START: ', str(start_tm))
     print('END: ', str(end_tm))
     print('ELAP: ', str(end_tm - start_tm))
     conn.close()
     return query_result

##-- SQL query
query = """
     SELECT var1, var2, ymd, count(*) as cnt
          FROM IBMDB2_DB
          WHERE ymd = "2016-11-22" 
 """

##-- Excute IBMDB2 SQL in Python
query_DB2(query)
 
 

 

 

  3. PostgreSQL에 Python으로 접속하여 SQL query 해서 pandas DataFrame 만들기

 

##-- User defined function for PostgreSQL DB SQL query

def query_postgreSQL(query):
     import pandas as pd
     import psycopg2 as pg
     from datetime import datetime
    

  

    # DB Connection
     conn = pg.connect(host='xx.xxx.xxx.xx',
                                        port='xxxx',
                                        dbname='xxx',
                                        user='xxxx',
                                        password='xxxx')
     start_tm = datetime.now()

     # Get a DataFrame

     query_result = pd.read_sql(query, conn)
 
     # Close connection
     end_tm = datetime.now()

     print('START: ', str(start_tm))
     print('END: ', str(end_tm))
     print('ELAP: ', str(end_tm - start_tm))
     conn.close()
 
     return query_result

##-- SQL query
query = """
     SELECT var1, var2, ymd, count(*) as cnt
          FROM PostgreSQL_DB
          WHERE ymd = "2016-11-22" 
 """

##-- Excute PostgreSQL SQL in Python
query_postgreSQL(query)

 

 

 

  4. MariaDB에 Python으로 접속하여 SQL query 해서 pandas DataFrame 만들기

 

##-- User defined function for MariaDB SQL query

def query_MariaDB(query):

     import pandas as pd
     import pymysql
     from datetime import datetime


     # DB Connection
     conn = pymysql.connect(host='xx.xxx.xxx.xxx', port=xxxx,
          user='xxxx'
          password='xxxxx'
          database='xxxxx')


     # start time
     start_tm = datetime.now()


     # Get a DataFrame
     global query_result


     query_result = pd.read_sql(query, conn)

 

     # Close connection
     end_tm = datetime.now()


     print('START TIME : ', str(start_tm))
     print('END TIME : ', str(end_tm))
     print('ELAP time :', str(end_tm - start_tm))
     conn.close()


     return query_result

 


##-- SQL query
query = """
     SELECT var1, var2, ymd, count(*) as cnt
          FROM MariaDB
          WHERE ymd = "2016-11-22" 
 """


##-- Excute PostgreSQL SQL in Python

query_postgreSQL(query)

 

 

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

 

 


728x90
반응형
Posted by Rfriend
,

Python 을 가지고 분석에 활용한다고 했을 때 데이터 전처리에 NumPy와 pandas library를 많이 사용합니다.  특히, 행과 열로 구성이 되어있는 DataFrame type 데이터를 입력, 처리, 조작할 때 pandas 가 매우 강력하고 편리합니다.

 

 

Python의 pandas library의 read_csv() 함수를 사용해서 외부 text 파일, csv 파일을 불러와서 DataFrame으로 저장하는 방법에 대해서 소개하겠습니다.

 

 

 1. csv 파일 불러오기 : read_csv()

 

 

아래와 같이 ID, LAST_NAME, AGE 3개의 열(column)을 가지고 있고, 5개의 행(row) 가지고 있는, 콤마로 구분된 CSV 파일(comma sepeated file)을 예제로 사용하겠습니다.

 

[예제 CSV 파일 : test_csv_file.csv => test_csv_file.csv   ]

 

ID

LAST_NAME

AGE

1

KIM

30

2

CHOI

25

3

LEE

41

4

PARK

19

5

LIM

36

 

 

import 로 pandas library를 호출한 다음에 read_csv() 함수에 파일 경로파일 이름을 적어주면 됩니다. csv 파일은 구분자(separator, delimiter)를 명시적으로 ',' (comma)라고 지정해주지 않아도 알아서 잘 불러옵니다.

 

 

>>> import pandas as pd
>>> csv_test = pd.read_csv('C:/Users/Administrator/Documents/Python/test_csv_file.csv')

 

 

 

 

DataFrame.shape 을 사용해서 행(row)과 열(column)의 개수를 확인해보고, 행과 열이 몇 개 안되므로 indexing 없이 전체를 호출해보겠습니다.

 

 

>>> csv_test.shape # number of rows, columns
(5, 3)
>>> csv_test 
   ID LAST_NAME  AGE
0   1       KIM   30
1   2      CHOI   25
2   3       LEE   41
3   4      PARK   19
4   5       LIM   36

 

 

 

 

Spyder (Python 3.5) 의 'Variable explorer' 창에 보면 csv 라는 이름의 DataFrame 이 신규로 생성되었으며, Size 란에 보면 (5, 3) 으로서 5개 행(rows), 3개 열(columns)으로 구성되어 있음을 알 수 있습니다.

 

csv DataFrame 이름을 클릭하면 아래 그림처럼 행과 열로 구성된 2차원이 DataFrame을 열어서 볼 수 있습니다.

 

 

 

 

 

 2. 구분자 '|' 인 text 파일 불러오기 : sep='|'

 

이번에는 구분자가 콤마(,)가 아닌 다른 기호, 가령, 수직 막대기 '|' 인 경우의 text 파일을 불러와보도록 하겠습니다. 

 

[ 예제 test_text_file.txt  => test_text_file.txt   ]

ID|A|B|C|D
C1|1|2|3|4
C2|5|6|7|8
C3|1|3|5|7

 

 

 

read_csv() 함수는 동일하며, 파일 경로와 text 파일 이름을 써주고, 구분자(separator, delimiter)에 sep='|' 를 추가해줍니다.

 

 

>>> text_test = pd.read_csv('C:/Users/Administrator/Documents/Python/test_text_file.txt', sep='|')
>>> text_test
   ID  A  B  C  D
0  C1  1  2  3  4
1  C2  5  6  7  8
2  C3  1  3  5  7

 

 


만약 구분자가 탭(tab) 이라면 sep = '\t' 을 입력해줍니다.  



 

 3. 파일 불러올 때 index 지정해주기 : index_col

 

 

만약에 위의 예에서 첫번째 열인 'ID'라는 이름의 변수를 Index 로 지정해주고 싶으면 index_col=0 (위치)이나 index_col='ID' 처럼 직접 변수 이름을 지정해주면 됩니다.

 

 

>>> # pass the column number you wish to use as the index:
... pd.read_csv('C:/Users/Administrator/Documents/Python/test_text_file.txt', sep='|', index_col=0)
    A  B  C  D
ID           
C1  1  2  3  4
C2  5  6  7  8
C3  1  3  5  7

 

 

 

>>> # pass the column name you wish to use as the index:
... pd.read_csv('C:/Users/Administrator/Documents/Python/test_text_file.txt', sep='|', index_col='ID')
    A  B  C  D
ID           
C1  1  2  3  4
C2  5  6  7  8
C3  1  3  5  7

 

 

 

 

 

 4. 변수 이름(column name, header) 이 없는 파일 불러올 때 이름 부여하기

     : names=['X1', 'X2', ... ], header=None

 

 

[ 예제 : 변수 이름이 없는 text 파일(no header)  =>  text_without_column_name.txt ]

 

C1|1|2|3|4
C2|5|6|7|8
C3|1|3|5|7

 

 

names=['ID', 'A', 'B', 'C', 'D'] 와 같이 칼럼 이름을 부여해줍니다.  header=None 은 칼럼 이름이 없다는 뜻이며, 만약 1번째 행이 칼럼 이름이라면 header=0 으로 지정해주면 됩니다.

 

 

>>> # naming columns :
... pd.read_csv('C:/Users/Administrator/Documents/Python/text_without_column_name.txt', sep='|', names=['ID', 'A', 'B', 'C', 'D'], header=None, index_col='ID')
    A  B  C  D
ID           
C1  1  2  3  4
C2  5  6  7  8
C3  1  3  5  7

 

 

 

 

 5. 유니코드 디코드 에러, UnicodeDecodeError: 'utf-8' codec can't decode byte

 

불러오려는 text, csv 파일의 encoding 설정과 Python encoding 설정이 서로 맞지 않으면 UnicodeDecodeError 가 발생합니다.  한글은 보통 'utf-8' 을 많이 사용하는데요, 만약 아래처럼 'utf-8' 코덱을 decode 할 수 없다고 에러 메시지가 나오는 경우가 있습니다.

 

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 26: invalid start byte 

이럴 경우에는 Windows에서 많이 사용하는 'CP949'로 아래처럼 encoding을 설정해서 text, csv 파일 불러오기를 해보시기 바랍니다.

 

 

f = pd.read_csv('directory/file', sep='|'', encoding='CP949')

 

 


혹시 encoding='CP949' 로 해도 안되면 encoding='latin' ('ISO-8859-1' 의 alias) 도 한번 시도해보시기 바랍니다. 


f = pd.read_csv('directory/file', sep='|'', encoding='latin')

 


코덱을 모두 설명하기에는 너무 양이 많으므로 이하 생략합니다.



 6. 특정 줄은 제외하고 불러오기: skiprows = [x, x]


skip rows 옵션을 사용하여 첫번째와 두번째 줄은 제외하고 csv 파일을 DataFrame으로 불러와보겠습니다. 



# skip 1st and 2nd rows (do not read 1, 2 rows)

csv_2 = pd.read_csv("C:/Users/admin/Documents/data/test_csv_file.csv", 

                           skiprows = [1, 2])  







 7. n 개의 행만 불러오기: nrows = n


csv 파일의 위에서 부터 3개의 행(rows) 만 DataFrame으로 불어와보겠습니다. 


 

# read top 3 rows only

csv_3 = pd.read_csv("C:/Users/admin/Documents/data/test_csv_file.csv", 

                    nrows = 3)







  8. 사용자 정의 결측값 기호 (custom missing value symbols) 


불러오려는 데이터셋 파일에 다양한 모양, 기호의 결측값이 들어있을 수 있습니다. 이때 사용자 정의 결측값 기호를 표기해줌으로써 이들 특정 기호를 pandas가 결측값으로 인식할 수 있도록 해줍니다. 가령  어떤 문서에 숫자형 변수에 결측값이 '??'라는 표시로 입력이 되어있다고 한다면, 이를 pandas DataFrame으로 불러읽어들였을 경우 float나 int로 인식되어 불러오는 것이 아니라 string으로 인식해서 '??'를 결측값이 아니라 문자형으로 불러오게 됩니다. 이럴 경우 '??'를 결측값이라고 인식하라고 알려주는 역할이 na_values = ['??'] 옵션입니다. 



df = pd.read_csv('C:/Users/Administrator/Documents/Python/test_text_file.txt', 

                             na_values = ['?', '??', 'N/A', 'NA', 'nan', 'NaN', '-nan', '-NaN', 'null')

 


물론 데이터를 읽어들인 후에 후행적으로 결측값으로 인식되어야 할 것들(예: '?', 'N/A' 등)이 문자열로 잘못 인식되어 잘못 불어와졌을 경우 pandas의 데이터변환 함수를 사용해서 전처리할 수도 있습다만, 자칫 결측값이 있는 줄도 모르고 결측값 처리를 안하고 다음번 분석으로 넘어갈 실수를 할 수도 있으므로 가급적 데이터를 불러오는 단계에서 결측값 기호를 사전에 파악하시고 '사용자 정의 결측값 기호 na_values = [] 옵션'을 사용해서 결측값으로 인식해서 불러오는 것이 가장 좋은 방법이라고 생각합니다. 



  9. 데이터 유형 설정 (Setting the data type per each column)


pandas는 데이터셋을 읽어들일 때 첫번째 행의 데이터를 기준으로 각 칼럼별 데이터 유형을 추정해서 자동으로 세팅을 해줍니다. 대부분의 경우는 잘 맞는 편인데요, 가끔 분석가가 의도한 데이터유형으로 설정되지 않는 경우도 있습니다.(가령, 위의 8번 결측값 기호를 string object로 잘못 인식한다든지...)  DB 사용자라면 데이터 유형을 명시적으로 설정해주는 것에 익숙하실 텐데요, pandas의 pd.read_csv()에도 사용자가 dtpye 옵션으로 사전형(dictionary)으로 각 칼럼(key) 별 데이터 유형(value)를 짝을 지어서 명시적으로 설정해 줄 수 있습니다. 


df = pd.read_csv('C:/Users/Administrator/Documents/Python/test_text_file.txt', 

                            dtype = {"ID": int, 

                                          "LAST_NAME": str, 

                                          "AGE": float}

                            )

 



날짜/시간 형태(date/time format)의 데이터의 경우 infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates 등의 시계열 데이터 형태에 특화된 옵션들이 있습니다. 자세한 내용은 아래의 pandas 매뉴얼을 참고하시기 바랍니다. 언제 시간이 되면 시계열데이터 전처리 및 분석은 별도의 세션으로 여러차례 연재를 해보겠습니다. 




Python pandas 라이브러리의 read_csv() 함수를 이용한 text, csv 파일 불러오기 소개를 마치겠습니다.

 

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

 

 


728x90
반응형
Posted by Rfriend
,

며칠 전에 내년도 생일 선물을 올 해 미리 사달라고 와이프에게 졸라서 전자책 리더기를 하나 장만 하였습니다.

 

작년부터 Amazon Kindle paper white 를 사려고 했었으나, 해외출장 갔던 직장동료에게 부탁했더니... 자기들 선물 사느라 한도 초과되어서 저의 킨들 부탁은 짤리기를 여러번... 직구 하자니 수수료랑 배송료가 좀 아깝고... 밍기적 거리다가 2년이 훌쩍 지났습니다. (해외출장 기회가 왜 이리 없냐고요. -_-;)

 

그러다가 우연히 다른 사람 블로그에서 한국에서도 전자책 리더기가 이제 좀 쓸만한 게 나왔다는 포스팅을 보게 되었습니다.

 

검색을 해보니 "리디북스 페이퍼"와 한국이퍼브의 "크레마 카르타"가 많이 언급이 되길레 성능, 가격, UI/UX 등을 비교해보다가 결국 "크레마 카르타"로 결정하고 구매하게 되었습니다.  (출시된지 1년이 넘었는데 왜 할인을 안하냐고요. -_-;)

 

 

 

전자책 리더기, 케이블, 설명서, 간촐합니다.

 

 

전자책 리더기를 써보니 나쁜 점은,

  • 책장 넘길 때 버벅 거리고 번짐이 있어서 신경 쓰임

 

 

전자책 리더기 좋은 점을 들라면,

  • 배터리가 아~주 오래 간다 (스마트폰의 Kindle app으로 읽을 때와 비교 불가)

  • 가볍다 (종이책 대비. 특히 여행이나 출장갈 때 아주 좋겠죠)

  • 눈 피로가 덜하다 (스마트폰 Kindle app 대비)

  • 가격도 착하다 (스마트폰 대비... 단통법 발의한 새누리당 망해라!!!  -_-")

  • 인터넷 서핑 유혹에서 자유롭다 (스마트폰, 아이패드 대비... 어쩌면 이게 제일 중요한 것일 수도...-_-b)

 

두 어달 썼는데 아주 만족하고 있습니다.  식구들한테도 각자 하나씩 사줄까 고민할 정도로요.

 

 

오늘 포스팅의 주제는 크레마 카르타 구매하면서 생각해 보게된 "플랫폼 비즈니스 모델 (Platform business model)", "플랫폼 기업 (Platform company)" 에 대한 것입니다.  일요일이 다 가고 있으므로 짧게 쓰겠습니다. (과연? -_-?)

 

플랫폼 비즈니스 모델을 정의를 해보자면, seller와 buyer의 양쪽 이해당사자 간의 거래비용(transaction cost)를 줄여주어 가치를 창출하는, 양면 시장(two-sided markets)을 다루는 비즈니스 모델이라고 할 수 있습니다.  (데이빗 에번스와 리처드 슈말렌지는 ‘Catalyst Code’라는 책에서 ‘Match-maker’, ‘Audience-maker’, ‘Market-maker’의 세 개의 형태로 세분화하고 있습니다.)

 

(참고로, 거래비용 개념은 1930년대에 영국의 경제학자 로널드 코우즈가 기업이 생기는 이유를 설명할 때 제시한 개념임)

 

 

[ 플랫폼 비즈니스 모델 개념도 (Platform Biz. Model Architecture) ]

 

 

 

요즘에 소위 뜨는 기업, 잘 나가는 기업의 상당수가 바로 '플랫폼 기업(platform company)' 입니다.

 

이글을 쓰는 2016년 11월 5일 기준으로 미국에서 Market Capitalization 기준으로 Top 20 기업 중에서 8개가 플랫폼 기업이군요. (Apple 이 1등이라고 나오는데요, Alphabet 모회사와 Google 두개를 합치면 Alphabet Inc. 가 1등이라고 할 수 있겠네요.)

 

* Source : https://www.theonlineinvestor.com/large_caps/

 

 

요즘 미국 실리콘밸리에서 Hot 하다고 하는 회사들을 들라면, Google, Apple, Facebook, AirBnB, Uber, Ebay... 다 플랫폼 회사들이예요.  한국에서도 플랫폼 기업 Naver나 Kakao 가 현대-기아자동차를 넘어서는 날이 언젠가 올거라고 예상합니다.

 

 

플랫폼 회사의 핵심 성공 키워드는 '개방(Openness)''협력(Collaboration)' 입니다. 특히, 우리는 요즘 인터넷이랑 모바일이 실시간으로 연결되어 거래비용이 거의 '0'에 수렴하는 시대(zero transaction cost economy)에 살고 있습니다.  거래비용이 '0'이다 보니 양면시장을 다루는 플랫폼 기업의 특성 상 양면시장의 양쪽 이해당사자(two players of two-sided market) 의 수 간에 증폭적 피드백 루프 (amplifying feedback loop machanism)이 작동하게 됩니다.  '선순환 고리(virtuous cycle)'와 '악순환 고리(vicious cycle)'가 작동해서 '1등 기업이 시장의 대부분을 싹쓸이'하고 2등 이하부터는 별 영향력을 행사하지 못하게 되는 현상이 발생하게 됩니다. "가진 자가 더 가지게 될 것"이라는 마태효과가 발생하는 것입니다. 이게 참 무섭고도 매력적입니다. (1등 에게만... -_-)

 

 

[ 플랫폼 기업의 성공 키워드 : 개방과 협력 (Openness and Collabaration) ]

 

 

 

 

(위에서 크레마 카르타 샀다고 자랑질 하다가 ^^v) 왜 난데없이 '플랫폼 비즈니스 모델'을 얘기하냐고요?

 

제가 국내 전자책 리더기 중에서 구매 후보로 삼았던게 '리디북스 페이퍼(RIDIBOOKS Paper)'와 한국이퍼브의 '크레마 카르타 (crema carta)' 였습니다.  '리디북스 페이퍼'나 '크레마 카르타' 둘 다 '플랫폼 회사'의 제품입니다만, '크레마 카르타'가 '개방(openness)'과 '협력(collaboration)' 관점에서 '리디북스 페이퍼'를 압도하기 때문에 생각난 김에 정리도 할 겸 글을 쓰게 되었습니다.

 

저는 처음에 HW 기능 (페이지 넘기는 물리 버튼, (상대적으로 조금 빠른) 페이지 넘어가는 속도) 과 가격때문에 '리디북스 페이퍼'에 호감이 조금 더 갔습니다.

 

하지만, '크레마 카르타'가 제공하는 '열린 서재' 기능을 보고 나서는 아무런 주저함이나 고민없이 '크레마 카르타' 사는 걸로 마음을 정했습니다. '열린 서재' 기능은 (알리딘, Yes24, 반디앤루니스, 교보문고 등의 한국이퍼브 회원사에서 구매한 책뿐만 아니라) Amazon Kindle, 인터파크, 심지어 리디북스에서 구매한 책들도 app 을 다운받아 설치해서 읽을 수 있도록 지원해주는 기능입니다.

 

저는 Amazon 에서 구매한 책이 몇 권 있고, 알라딘과 Yes24를 이용하므로 자연스레 '크레마 카르타'로 결정한 것입니다.

 

 

[크레마 카르타의 '기본 책장' ]

 

 

 

 

 

[ 크레마 카르타의 '열린 서재' ]

 

 

 

 

 

제가 긴 얘기를 단 한 줄로 줄여서 요약하자면, '제품력(product merit)'은 '비즈니스 모델과 전략(business model & strategy)'을 이길 수 없다는 것입니다.  

 

(저에게 있어) '리디북스 페이퍼'가 비록 H/W 성능과 가격이 '크레마 카르타' 대비 조금 좋다고 생각하기는 했습니다만, 이건 비교 우위 '5점' 정도 뿐입니다. 

 

(저에게 있어) '크레마 카르타'의 '열린 서재' 기능 ('개방'과 '협력'의 비즈니스 모델과 전략)은 리디북스에서 산 책만 읽을 수 있는 '리디북스 페이퍼'에 비해 '20점' 이상의 비교 우위를 주었습니다.

 

한글로 된 eBook만 읽고, 리디북스에서 eBook을 사는 고객이라면 '리디북스 페이퍼'가 기능이나 가격면에서의 효용이 있으므로 좋은 선택이 될 것입니다.

 

하지만, 저같이 이미 Amazon, 알라딘, Yes24 에서 책을 사던 고객이라면 '크레마 카르타'이 '열린 서재'가 (H/W나 가격의 비교 열위를 감안하더라도..) 압도적인 비교우위를 제공한다고 생각합니다. (저같은 사람이 얼마나 될지는 모르겠습니다만... -,-???)

 

 

혹시나 해서 네이버 트렌드(Naver Trend)에 들어가서 '크레마 카르타'와 '리디북스 페이퍼'의 두 개 키워드로 1년치 검색 빈도 시계열 추이를 분석해보았습니다.

 

 

 

[ 네이버 키워드 트렌드 분석 : 크레마 카르타 vs. 리디북스 페이퍼 ]

 

 

 

 

 

위의 Naver 키워드 트렌드 분석 결과를 보면 올 해 2016년 들어서는 '크레마 카르타'가 '리디북스 페이퍼' 대비 2배 정도의 키워드 검색 빈도를 보여주고 있음을 알 수 있습니다.

(실제 양 제품의 판매량은 제가 내부자가 아니므로 당연히 모릅니다.)

 

 

'리디북스 페이퍼' 담당 마케팅이나 사업기획 담당하시는 분께서 이 포스팅을 보신다면, '개방'과 '협력'에 대해서 내부에서 치열한 고민을 해보시면 좋을 것 같습니다.  결국 사업 경쟁은 '50.1% vs. 49.9%'의 싸움이며, 경쟁사 제품과 단 '0.2%'의 차이가 자사 제품이 고객으로 부터 구매 선택을 받느냐(all) 아니면 못 받느냐(nothing)의 냉혹한 성적표를 받게 됩니다.

 

다시한번 강조하자면, '제품력'은 결코 '비즈니스 모델과 전략'을 이길 수 없습니다.

 

 

 

한국이퍼브의 '크레마 카르타' 담당 마케팅이나 사업기획 담당하시는 분께서 이 포스팅을 보신다면, '전략적 가격 설정'에 대해서 내부에서 치열한 고민을 해보시면 좋을 것 같습니다.  마케팅 책에 보면 ‘원가 기반 가격결정법(Cost-plus pricing)’, ‘목표수익률에 의한 가격결정법(Target-return pricing)’, ‘경쟁가격을 고려한 가격결정법(Going-rate pricing)’, ‘소비자가 느끼는 가치에 의한 가격결정법(Perceived-value pricing)’ 등이 있습니다만, 플랫폼 기업에 적합한 가격 전략은 없다고 생각합니다.  "닭이냐 달걀이냐"라는딜레마를 풀기 위해 여러 플랫폼 기업들이 (초반 한쪽 시장손해를 감수해야 할 수도 있는) 공격적이면서도 전략적인 가격 정책을 쓰고 있습니다. (예: 남자와 여자 부킹을 주선하는 나이트클럽에서 남자 손님에게는 비싼 입장료를 받고, 여자 손님은 무료 입장 시키죠.  나이트클럽 가 봐서 아는게 아니고 책 봐서 아는 거임. 의심 금지 -_-;)

 

플랫폼 비즈니스에서는 (전문용어로?) "닭이 먼저냐, 달걀이 먼저냐?"의 딜레마가 존재합니다.  팔려는 상인(seller)이 별로 없으면 선택의 폭이 줄어들므로 사려는 고객(buyer, customer)이 안오려고 합니다.  사려는 고객(buyer, customer)이 별로 없으면 팔려는 상인(seller)이 안오려고 하겠지요.  "닭(상인, seller)이 먼저냐, 달걀(고객, customer)이 먼저냐, 그것이 문제로다" 인 것이지요.

 

이 "닭이 먼저냐, 달걀이 먼저냐"라는 딜레마를 깨는데 비교적 적용하기 용이한 전략이 가격전략일 것입니다.

 

 '크레마 카르타'라는 H/W 단말기 가격(마진)을 높게 책정해서 돈 벌 생각하지 마시고, '크레마 카르타'를 원가 혹은 원가 이하의 전략적 가격으로 승부수를 던져서 시장에 단기간에 확산을 시키고요, 고객들이 '크레마 카르타'에서 읽을 전자책을 살 테니 컨텐츠 판매(추가 제작비, 배송비 등의 거래비용 거의 '0'원)에서 수익을 얻으라는 말입니다.  '크레마 카르타' 판매가 늘어날 수록 '경험곡선효과'에 의해서 생산단가도 떨어지는 부수적인 이익 증가 요인도 있습니다. (난 이미 15만 9천원 다 주고 샀는데... 지금 가격을 낮추면 속이 쓰릴 듯... ㅜ,ㅜ)

 

 

만약, '크레마 카르타' H/W 단말기 담당 조직에게 '수익'이라는 KPI 지표가 할당되었다면 아마도 '전자책 담당 마케팅 부서'와 '크레마 카르타 담당 마케팅 부서'의 이해관계가 상충하게 될텐데요, 이럴 경우에는 전사 차원에서 KPI 조율을 해줘야 겠지요. '크레마 카르타' 담당 부서에는 경쟁사에 상응하거나 상회하는 기능 설계/제작, 신속한 버그 개선 등의 지표를 KPI로 할당하고, '수익' 관점의 KPI 지표를 부여하면 안되겠지요. 

 

 

 

그리고요, 브랜드 이름이 '크레마 카르타'인게 좀 실책인거 같습니다.  "기억하기 쉽고, 발음하기 쉽고, 짧고, 전자책 리더기 연상이 자연스레 되는" 브랜드 이름이 더 있을 법도 한데... 굳이 "기억하기 어렵고, 발음하기 어렵고, 길고(무려 6음절~!), 자책 리더기라고 연상이 잘 안되는"... '크레마 카르타'를 선택한 이유가 뭔가요?  교양있어 보이고 세련되게 들려서? 

 

브랜드 이름 정할 때는 내부 담당 직원들끼리 투표하지 마시고요, 실제로 사용할 고객들한테 물어보는게 좋다고 생각합니다. 몇 개의 후보들 중에서 뭐가 제일 좋은거 같냐고요. (혹시 이미 고객 서베이 결과로 '크레마 카르타'를 선택한거면은 제가 헛다리 짚은 거고요. 깨겡... ^^;)

 

 

제가 주제넘게 말이 길어졌네요.  ㅎㅎ

 

 

ps. 사족을 덧붙이자면,

11번가에서 '크레마 카르타' 커버로 1만원짜리 중국산 싸구려를 장만했더니 아주 가관입니다.  '크레마 카르타'의 전원 단추가 하단에 있는데요, 커버의 고정 밴드가 전원 단추를 가려버리고 있습니다.  만약 '크레마 카르타' 커버 디자이너가 "단 한번 만이라도" 자신이 디자인한 커버를 '크레마 카르타'에 사용을 해봤더라면 이처럼 어처구니없게 디자인 하지는 않았을텐데요. (1만원 짜리 싸구려 커버에 무얼 더 바라겠습니까만은... 에휴... -_-;)  혹시 전자책리더기 살 계획이 있는 분은 돈 좀 쓰더라도 정식 커버 사서 쓰시는거 권합니다.

 

 

("UX에 대한 기본이 안되어 있는 망할 놈의 디자이너 같으니라구"하면서 씩씩거리고 있는 저를 어여삐 여긴 와이프가 전원 위치의 커버 밴드를 왼쪽으로 살짝 옮겨서 바늘로 박음질을 새로 해줘서 지금은 잘 쓰고 있어요. 이자리를 빌어 와이프에게 다시 한번 감사를... ^^~♡  쏠로들에게는 심심한 위로를... -_-;)

 

 

전자책 리더기 살지 말지 고민하는 분이라면, "본전 뽑고도 남으니 사서 책 읽는 즐거움"을 만끽하시라고 권하고 싶습니다.  밤에 전기장판 틀어놓은 따끈따끈한 침대에서 백라이트 켜놓고 책 읽으면 무릎도 안시럽고 아주 좋아요. ㅎㅎ



##===================  2017.09.03 . 추가로 몇 자 적습니다 ==============##


2019.5월 달에 "리디북스가 한국 전자책 시장에서 제일 잘하고 있다"는 임정욱 스타트업 얼라이언스 센터장님께서 쓰신 글이 있네요. 


아무래도 이번 포스팅은 시장의 성적표로 평가해봤을 때 잘못 쓴 글이 되어버렸네요. ^^; 


리디북스 직원 150명 중 50명이 소프트웨어 개발자! 

로맨스·SF·판타지·BL 등 장르물에 선택과 집중!


역시 1등은 뭔가가 있어요. 리디북스, 정말 대단합니다!  

전자책시장 평정한 리디북스의 비결은?…‘압도적 수준의 가독성’

임정욱 스타트업 얼라이언스 센터장2017년 05월호


http://eiec.kdi.re.kr/publish/nara/column/view.jsp?idx=11021

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 dplyr 패키지의 bind_rows() 함수와 bind_cols() 함수에 대해서 알아보겠습니다.

 

dplyr 패키지의 bind_rows() 함수는 두개 이상의 데이터 프레임을 행 기준(위 - 아래 - 아래 ...)로 합칠 때 사용하는 함수이며, {base] 패키지의 rbind() 함수와 유사한 기능을 수행합니다.

 

dplyr 패키지의 bind_cols() 함수의 두개 이상의 데이터 프레임을 열 기준(왼쪽 - 오른쪽 - 오른쪽 ...)로 합칠 때 사용하는 함수이며, {base} 패키지의 cbind() 함수와 유사한 기능을 수행합니다.

 

dplry 패키지는 데이터 프레임의 데이터 구조에 특화된 패키지이므로 아래에 제시한 예시는 모두 데이터 프레임에만 해당이 됩니다.

(참고로, {base} 패키지의 rbind(), cbind() 는 두 개 이상의 vector에 대해서도 실행이 되며, vector를 rbind(), cbind() 할 경우 matrix 를 반환합니다.  {dplyr} 의 bind_rows(), bind_cols()를 vector 에 적용하면 실행 안됨.)

 

 

[ bind_rows(), bind_cols() in {dplyr}
: 효과적으로 다수의 데이터 프레임 행 기준, 열 기준으로 합치기 ]

 

 

 

 

1. bind_rows() : 다수의 데이터 프레임을 행 기준으로 합치기 (binding multiple data frames by row)

 

기본적인 사용법은 bind_rows(dataframe 1, dataframe 2, ...) 입니다.  {base} 패키지의 rbind()와 동일합니다.

 

 

> ##------------------------------------------------------ > ## R {dplyr} package > ## bind() : Efficiently bind multiple data frames by row and column. > ##------------------------------------------------------ > # install.packages("dplyr") > library(dplyr) > > # making 2 data.frame examples > df_1 <- data.frame(x = 1:3, y = 1:3) > df_2 <- data.frame(x = 4:6, y = 4:6) > > # rbind() in {base} package > rbind(df_1, df_2) x y 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 > > # bind_rows in {dplyr} package > bind_rows(df_1, df_2) x y 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6

 

 

 

 

{base} 패키지의 rbind()와 동일한 결과를 반환한다면 왜 굳이 dplyr 패키지의 bind_rows() 함수를 써야할 필요가 있을까 싶을 것입니다. 

 

{base} 패키지의 rbind() 대비 dplyr 패키지의 bind_rows() 가 좋은 점 세가지를 소개하겠습니다.

(꼭 dplyr 패키지 영업사원 된 듯한 기분...ㅋㅋ)

 

  • (1-1) 열(columns)이 서로 동일하지 않아도 행(rows) 기준으로 합칠 수 있음
    (--> 합치는 과정에서 열이 달라서 빈 자리는 NA 값 처리됨)
  • (1-2) 'id' 매개변수를 사용해 합쳐지기 전 데이터 프레임의 원천을 알 수 있음
    (--> source를 알 수 있는 새로운 변수 생성함)
  • (1-3) dplyr 패키지의 처리 속도가 {base} 패키지 대비 상대적으로 엄청나게 빠름
    (100배 이상 빠름!!!)

 

위 세가지 dplyr 패키지의 bind() 함수의 좋은 점 세 가지를 예를 들어서 설명하겠습니다.

 

 

 

(1-1) 열(columns)이 서로 동일하지 않아도 행(rows) 기준으로 합칠 수 있음
       (--> 합치는 과정에서 열이 달라서 빈 자리는 NA 값 처리됨)

 

 

> # In case columns do not match b/w data frames
> df_1 <- data.frame(x = 1:3, y = 1:3)
> df_3 <- data.frame(x = 7:9, z = 7:9)
> 
> # rbind(): Columns need to match
> # if not, Error in match.names(clabs, names(xi))
> rbind(df_1, df_3) # Not run
Error in match.names(clabs, names(xi)) : 
  names do not match previous names
> 
> # bind_rows(): Columns don't need to match when row-binding
> bind_rows(df_1, df_3)
  x  y  z
1 1  1 NA
2 2  2 NA
3 3  3 NA
4 7 NA  7
5 8 NA  8
6 9 NA  9

 

 

 

 

 

(1-2) 'id' 매개변수를 사용해 합쳐지기 전 데이터 프레임의 원천을 알 수 있음
(--> source를 알 수 있는 새로운 변수 생성함)

 

 

> # When you supply a column name with the `.id` argument, a new
> # column is created to link each row to its original data frame
> df_1 <- data.frame(x = 1:3, y = 1:3)
> df_2 <- data.frame(x = 4:6, y = 4:6)
> df_3 <- data.frame(x = 7:9, z = 7:9)
> 
> bind_rows(list(grp_1 = df_1, grp_2 = df_2, grp_3 = df_3), .id="group_id")
  group_id x  y  z
1    grp_1 1  1 NA
2    grp_1 2  2 NA
3    grp_1 3  3 NA
4    grp_2 4  4 NA
5    grp_2 5  5 NA
6    grp_2 6  6 NA
7    grp_3 7 NA  7
8    grp_3 8 NA  8
9    grp_3 9 NA  9

 

 

 

 

 

 

(1-3) dplyr 패키지의 처리 속도가 {base} 패키지 대비 상대적으로 엄청나게 빠름
(100배 이상 빠름!!!)

 

system.time() 함수를 사용해서 {base} 패키지의 rbind() 함수와 {dplyr} 패키지의 bind_rows() 함수의 CPU 실행시간을 알아보겠습니다.(사용자 + 시스템 = elapsed 이므로 elapsed 결과를 비교하면 됨. 실행을 시킬 때마다 숫자가 조금씩 달라지기는 하지만, 차이가 워낙 커서 경향성이 뒤집히지는 않을 것이므로 한번만 실행시켜보고 비교해보겠음

 

데이터 프레임의 행의 개수가 너무 작으면 차이가 티가 잘 안나므로, 백만개의 행을 가진 데이터 프레임 두 개를 만들어서 비교해보겠습니다.

 

{base} 패키지의 rbind() 가 7.85초 걸렸고, {dplyr} 패키지의 bind_rows()는 0.03초가 걸렸으니 261배 차이가 났군요. (2.6배나 26배가 아니라 261배 차이임. 안 놀라는 사람은 뭡니까? ⊙⊙;)

 

크기가 작은 데이터라면 rbind()와 bind_rows() 함수의 실행 시간 차이를 아마 거의 느끼지 못할 것입니다만, 대용량 데이터의 경우 C 기반으로 짜여진 {dplyr} 패키지가 훨~씬 빠르고 처리 속도 차이가 피부로 느껴질 것입니다.  

 

 

> # system.time : rbind() in {base} vs bind_rows() in {dplyr} > # System operation time of dplyr bind_rows is extremely shorter than that of rbind > one <- data.frame(c(x = c(1:1000000), y = c(1:1000000))) > two <- data.frame(c(x = c(1:1000000), y = c(1:1000000))) > > system.time(rbind(one, two)) # elapsed 7.85 사용자 시스템 elapsed 7.02 0.16 7.85 > > system.time(bind_rows(one, two)) # elapsed 0.03 사용자 시스템 elapsed 0.02 0.02 0.03

> 7.85/0.03
[1] 261.6667

 

 

 

 

2. bind_cols() : 다수의 데이터 프레임을 열 기준으로 합치기 (binding multiple data frames by columns)

 

cbind()와 기능 및 활용법은 유사하며, 기본 활용법은 bind_cols(dataframe 1, dataframe 2, ...) 입니다.

 

 

> # binding data frames by column
> bind_cols(df_1, df_2, df_3)
  x y x y x z
1 1 1 4 4 7 7
2 2 2 5 5 8 8
3 3 3 6 6 9 9

 

 

 

 

{base} 패키지의 cbind()와 {dplyr} 패키지의 bind_cols() 의 CPU 실행시간을 비교해보겠습니다. 

(참고로, 실행시킬 때마다 아주 조금씩 달라짐)

 

{base} 패키지의 cbind()는 0.61초, {dplyr} 패키지의 bind_cols() 는 0.0001초가 걸린걸 보면, 역시 {dplyr} 패키지가 월등히 빠름을 알 수 있습니다.

 

 

> # system.time comparison : cbind() vs. bind_cols()
> one <- data.frame(c(x = c(1:1000000), y = c(1:1000000)))
> two <- data.frame(c(x = c(1:1000000), y = c(1:1000000)))
> 
> system.time(cbind(one, two)) # elapsed 0.61
 사용자  시스템 elapsed 
   0.47    0.03    0.61 
> system.time(bind_cols(one, two)) #elapsed 0
 사용자  시스템 elapsed 
      0       0       0

 

 

 

 

bind_cols() 함수는 서로 합칠 데이터 프레임의 행(rows)의 개수가 서로 같아야만 하므로 주의를 요합니다.  만약 행의 개수가 서로 다를 경우에는 "Error in eval(substitute(expr), envir, enclos) :  incompatible number of rows (4, expecting 3)" 에러가 납니다.

 

> # bind_cols() : Rows do need to match when column-binding > bind_cols(data.frame(x = 1:3), data.frame(y = 1:3)) # run x y 1 1 1 2 2 2 3 3 3 > > bind_cols(data.frame(x = 1:3), data.frame(y = 1:4)) # Not run Error in eval(substitute(expr), envir, enclos) : incompatible number of rows (4, expecting 3)

 

 

 

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

 

이번 포스팅이 도움이 되셨다면 아래의 '공감 ♡'를 눌러주세요. ^^

 

 

 

728x90
반응형
Posted by Rfriend
,

R의 dplyr 패키지에 대해서 연재를 하고 있는데요, dplyr 패키지 매뉴얼에 보니 유용한 함수들이 여럿 더 있네요. 그동안 dplyr 패키지 함수들에 대해서 소개했던것 외에 눈에 띄는 함수들을 서너번 더 나누어서 소개할까 합니다.

 

두 개의 데이터 프레임이 있을 때 "동일한 값을 가진 데이터 프레임인가?" 아니면 "서로 다른 값을 가진 데이터 프레임인가?"를 확인할 때 사용할 수 있는 {dplyr} 패키지의 all_equal() 함수를 소개하겠습니다.

 

비교하고자 하는 두 개의 데이터 프레임에 관측치와 변수가 모두 몇 개씩 밖에 안된다면 "눈으로 확인"하는 것도 가능할 것입니다.  하지만 관측치가 몇 백개를 넘어가거나, 혹은 변수가 몇 백개를 넘어간다면 육안으로 일일이 확인한다는게 피같은 시간을 낭비하는 것이 되며, 또 실수를 할 수도 있습니다.  더욱이 "데이터셋의 값은 동일한데 관측치의 순서만 다르건", 혹은 "데이터셋의 값은 동일한테 변수의 순서만 다른" 경우, 혹은 "데이터셋의 값은 동일한데 관측치의 순서와 변수의 순서만 서로 다른" 경우, 이건 뭐 육안으로 확인한다는게... 미치는거지요. ㅋㅋ

 

 

 

 

 

이때 간단하게 두 데이터 프레임 내의 값들이 서로 같은지를 간단하게 비교해서 TRUE, FALSE 를 반환해주는 함수가 dplyr 패키지의 all_equal() 함수입니다.

 

 

# all_equal() usage

 

all_equal(targetcurrent, # two data frame to compare

            ignore_col_order = TRUE, # Should order of columns be ignored?

            ignore_row_order = TRUE, # Should order of rows be ignored?
            convert = FALSE # Should similar classes be converted?)

 

 

 

 

{datasets} 패키지에 내장되어 있는 (1) mtcars 데이터 프레임 원본(target)과 (2) mtcars 의 행과 열을 임의로 순서만 바꾼 (단, 데이터는 변동없이 동일함) ran_sam_mtcars 데이터 프레임(current)을 만들어서 all_equal() 함수를 사용하여 비교를 해보겠습니다.

 

 

(1) 비교할 기준 target 데이터 프레임 : mtcars (원본)

 

 

 

> ##-----------------------------------------------------------
> ## R {dplyr} package 
> ## > all_equal : flexible equality comparison for data frames
> ##-----------------------------------------------------------
> # install.packages("dplyr") # for the first time user of dplyr package
> library(dplyr)
> 
> # original data frame : mtcars
> mtcars
                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

 

 

 

 

 

(2) 비교하고자 하는 현재(current) 데이터 프레임 만들기 : ran_sam_mtcars

(행과 열을 무작위로 섞어 놓았으며, 데이터 삭제나 추가 등의 변동은 없이 동일함)

 

위의 (1)번 원본 target과 동일한 데이터 프레임인데요, 눈으로 동일한 데이터셋인지 확인하라면 어떤 마음이 들까요? (노트북 던지고 싶은 마음? -,-;;;)

 

 

> # random sampled dataframe : ran_sam_mtcars
> random_sample <- function(x) x[sample(nrow(x)), sample(ncol(x))]
> 
> set.seed(1234)
> ran_sam_mtcars <- random_sample(mtcars)
> ran_sam_mtcars
                    cyl  qsec    wt drat gear  hp  mpg vs carb  disp am
Fiat 128              4 19.47 2.200 4.08    4  66 32.4  1    1  78.7  1
Merc 230              4 22.90 3.150 3.92    4  95 22.8  1    2 140.8  0
Lotus Europa          4 16.90 1.513 3.77    5 113 30.4  1    2  95.1  1
Maserati Bora         8 14.60 3.570 3.54    5 335 15.0  0    8 301.0  1
Camaro Z28            8 15.41 3.840 3.73    3 245 13.3  0    4 350.0  0
Merc 240D             4 20.00 3.190 3.69    4  62 24.4  1    2 146.7  0
Duster 360            8 15.84 3.570 3.21    3 245 14.3  0    4 360.0  0
Hornet Sportabout     8 17.02 3.440 3.15    3 175 18.7  0    2 360.0  0
Valiant               6 20.22 3.460 2.76    3 105 18.1  1    1 225.0  0
Porsche 914-2         4 16.70 2.140 4.43    5  91 26.0  0    2 120.3  1
Fiat X1-9             4 18.90 1.935 4.08    4  66 27.3  1    1  79.0  1
Hornet 4 Drive        6 19.44 3.215 3.08    3 110 21.4  1    1 258.0  0
Mazda RX4             6 16.46 2.620 3.90    4 110 21.0  0    4 160.0  1
Pontiac Firebird      8 17.05 3.845 3.08    3 175 19.2  0    2 400.0  0
Cadillac Fleetwood    8 17.98 5.250 2.93    3 205 10.4  0    4 472.0  0
Ford Pantera L        8 14.50 3.170 4.22    5 264 15.8  0    4 351.0  1
Volvo 142E            4 18.60 2.780 4.11    4 109 21.4  1    2 121.0  1
Merc 450SL            8 17.60 3.730 3.07    3 180 17.3  0    3 275.8  0
Toyota Corolla        4 19.90 1.835 4.22    4  65 33.9  1    1  71.1  1
Ferrari Dino          6 15.50 2.770 3.62    5 175 19.7  0    6 145.0  1
Toyota Corona         4 20.01 2.465 3.70    3  97 21.5  1    1 120.1  0
Merc 450SE            8 17.40 4.070 3.07    3 180 16.4  0    3 275.8  0
Lincoln Continental   8 17.82 5.424 3.00    3 215 10.4  0    4 460.0  0
Mazda RX4 Wag         6 17.02 2.875 3.90    4 110 21.0  0    4 160.0  1
Dodge Challenger      8 16.87 3.520 2.76    3 150 15.5  0    2 318.0  0
Chrysler Imperial     8 17.42 5.345 3.23    3 230 14.7  0    4 440.0  0
AMC Javelin           8 17.30 3.435 3.15    3 150 15.2  0    2 304.0  0
Honda Civic           4 18.52 1.615 4.93    4  52 30.4  1    2  75.7  1
Merc 280C             6 18.90 3.440 3.92    4 123 17.8  1    4 167.6  0
Merc 280              6 18.30 3.440 3.92    4 123 19.2  1    4 167.6  0
Datsun 710            4 18.61 2.320 3.85    4  93 22.8  1    1 108.0  1
Merc 450SLC           8 18.00 3.780 3.07    3 180 15.2  0    3 275.8  0

 

 

 

 

 

(3) 두 개의 데이터 프레임, 즉 (1) target 데이터 프레임 mtcars 와, (2) current 데이터 프레임 ran_sam_mtcars 이 동일한 데이터를 가지고 있는건지 아닌지를 {dplyr} 패키지의 all_equal() 함수를 써서 확인해보기

 

 

> # all_equal() : flexible equality comparison for data frames
> # By default, ordering of rows and columns ignored
> all_equal(mtcars, ran_sam_mtcars)
[1] TRUE

 

 

행과 열의 순서만 바뀌었을 뿐 값 자체에 대한 변동은 없으므로 당연히 "TRUE"라고 나와야 겠지요.

 

비교하는 두 개의 데이터 프레임이 행의 순서가 같은지(ignore_row_order = TRUE), 혹은 열의 순서가 같은지(ignore_col_order = TRUE)는 확인하지 않고 무시하게끔 default 옵션이 설정되어 있습니다.

 

 

 

 

(4) 행의 순서가 같은지 확인해보기
    : all_equal
(target, current, ignore_row_order = FALSE),

    열의 순서가 같은지 확인해보기
    : all_equal(
target, current, ignore_col_order = FALSE)

 

 

> # To check the equality of row's sequence : ignore_row_order = F
> all_equal(mtcars, ran_sam_mtcars, ignore_row_order = FALSE)
[1] "Same row values, but different order"
> 
> 
> # To check the equality of column's sequence : ignore_col_order = F
> all_equal(mtcars, ran_sam_mtcars, ignore_col_order = FALSE)
[1] "Same column names, but different order"

 

 

 

 

마지막으로, convert 매개변수는 두 개의 데이터 프레임 내 동일한 변수 이름을 가지고 있고, 값도 서로 같은데, 속성만 하나는 "factor", 하나는 "character"인 경우 "factor"를 "character"로 변경해주어서 => all_equal() 함수 최종 평가 결과를 "TRUE"로 반환해줄 수 있도록 자동으로 속성 변환해서 비교해주는 기능을 수행합니다.

 

[Reference] https://cran.r-project.org/web/packages/dplyr/dplyr.pdf

 

 

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

 

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

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 R dplyr 패키지의 Window function 중에서 행 전체를 위로 올릴 때 사용하는 lead() 함수, 행 전체를 아래로 내릴 때 사용하는 lag() 함수에 대해서 알아보았습니다.

(☞ 바로 가기 : http://rfriend.tistory.com/242 )

(* 참고 : Window function : n개의 행을 input으로 받아서 n개의 행을 output으로 반환하는 함수)

 

이번에는 R dplyr 패키지의 Window funciton 에 대한 마지막 포스팅으로

 - (3) Cumulative aggregates : cumall() 함수, cumany() 함수, cummean() 함수와 

 - (4) Recycled aggrerates 에 대해서 소개하겠습니다.

 

 

[ Types of Window functions in {dplyr} package ]

 

 

 

예제로 사용할 데이터를 먼저 간단히 소개하고 나서 cumulative aggretages, recycled aggregates 함수로 넘어가겠습니다. 예제로 사용할 데이터는 MASS 패키지에 내장되어 있는 Cars93 데이터프레임의 차종(Type), 가격(Price) 변수입니다.

 

 
> ##------------------------------------------------
> ## R dplyr package 
> ## > window function - Cumulative aggregates 
> ##  : cumany(), cumall(), cummean()
> ##------------------------------------------------
> 
> # install.packages("dplyr")
> library(dplyr)
> library(MASS)
> 
> str(Cars93)
'data.frame':	93 obs. of  27 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
> table(Cars93$Type)

Compact   Large Midsize   Small  Sporty     Van 
     16      11      22      21      14       9

 

 

 

 

직관적으로 결과를 비교하기 편하도록 Cars93 데이터프레임에서 (a) 차종(Type), 가격(Price) 의 두개의 변수만 선별하고, (b) 차종(Tpye) 중에서 관측치 개수가 적은 'Large'와 'Van' 만 남긴 후에, (c) 차종(Type), 가격(Price) 를 기준으로 오름차순으로 정렬하여 "Cars93_1" 이라는 새로운 데이터프레임을 만들어 보겠습니다.

 

 
> # select 'Type', 'Price' variable from Cars93 dataframe
> select <- dplyr::select # to avoid conflict select() function b/w dplyr and MASS
> 
> Cars93_1 <- Cars93 %>% 
+   select(Type, Price) %>% 
+   filter(Type %in% c("Large", "Van")) %>% 
+   arrange(Type, Price)
> 
> Cars93_1
    Type Price
1  Large  18.4
2  Large  18.8
3  Large  19.3
4  Large  20.7
5  Large  20.8
6  Large  20.9
7  Large  23.7
8  Large  24.4
9  Large  29.5
10 Large  34.7
11 Large  36.1
12   Van  16.3
13   Van  16.6
14   Van  19.0
15   Van  19.1
16   Van  19.1
17   Van  19.5
18   Van  19.7
19   Van  19.9
20   Van  22.7

 

 

 

 

간소화해서 새로 만든 Cars93_1 데이터프레임에서 차종(Type) 별로 최소(min) 가격, 평균(mean) 가격, 중앙값(median) 가격, 최대(max) 가격을 구해보겠습니다.

 

> # calculating min/mean/median/max Price by Type
> Cars93_1 %>% 
+   group_by(Type) %>% 
+   summarise(n = n(), # number by Type
+             Price_min = min(Price, na.rm = T), 
+             Price_mean = mean(Price, na.rm = T), 
+             Price_median = median(Price, na.rm = T), 
+             Price_max = max(Price, na.rm = T))
# A tibble: 2 x 6
    Type     n Price_min Price_mean Price_median Price_max
  <fctr> <int>     <dbl>      <dbl>        <dbl>     <dbl>
1  Large    11      18.4       24.3         20.9      36.1
2    Van     9      16.3       19.1         19.1      22.7

 

 

 

 

자, 이제 데이터셋 준비가 다 되었으니 본론으로 넘어가보겠습니다. 

 

함수에 대해서 말로 설명해놓기는 했습니다만, 잘 안 와닿을 것 같습니다.  예제를 보면서 각 함수가 어떤 기능을 하는지 찬찬히 살펴보시면 도움이 될거 같아요.  위의 요약통계량을 보니 가격 최소값 '18'을 조건으로 cumall()과 cumany()를 사용하면 "Large"와 "Van"이 서로 다른 결과를 반환하겠네요.

 

 

 (1) Cumulative aggregates : cumall() 함수, cumany() 함수, cummean() 함수

 

(1-1) cumall () 함수 : 조건을 모두 만족(cumulative &&)하는 (그룹의) 전체 행 반환

 

 
> # cumall()
> Cars93_1 %>% 
+   group_by(Type) %>% 
+   filter(cumall(Price > 18))
Source: local data frame [11 x 2]
Groups: Type [1]

     Type Price
   <fctr> <dbl>
1   Large  18.4
2   Large  18.8
3   Large  19.3
4   Large  20.7
5   Large  20.8
6   Large  20.9
7   Large  23.7
8   Large  24.4
9   Large  29.5
10  Large  34.7
11  Large  36.1

 

 

 

좀더 이해하기 쉽도록 아래에 원래의 Cars93_1 데이터프레임과 차종(Type)별 cumall(Price > 18) 조건으로 선별(filtering)을 한 후의 결과를 비교해놓았습니다.  "Van" 차종의 경우 가격(Price)이 18 이하인 관측치(12번, 13번) 2개 존재하므로 cumall(Price > 18) 에서 제시한 "모든 관측치가 만족(%%)" 조건을 만족하지 않으므로 "모든 행이 제외"되었습니다.

 

 

 

 

(1-2) cumany() 함수 : 조건을 만족(cumulative ||)하는 (그룹 내) 행만 반환

 

 
> # cumany()
> Cars93_1 %>% 
+   group_by(Type) %>% 
+   filter(cumany(Price > 18))
Source: local data frame [18 x 2]
Groups: Type [2]

     Type Price
   <fctr> <dbl>
1   Large  18.4
2   Large  18.8
3   Large  19.3
4   Large  20.7
5   Large  20.8
6   Large  20.9
7   Large  23.7
8   Large  24.4
9   Large  29.5
10  Large  34.7
11  Large  36.1
12    Van  19.0
13    Van  19.1
14    Van  19.1
15    Van  19.5
16    Van  19.7
17    Van  19.9
18    Van  22.7

 

 

 

 

좀더 이해하기 쉽도록 아래에 원래의 Cars93_1 데이터프레임과 차종(Type)별 cumany(Price > 18) 조건으로 필터링한 결과를 비교해놓았습니다. 12번째, 13번째 행의 "Van" 차종의 관측치가 cumany(Price > 18) 조건을 만족하지 않으므로 제외되었습니다.

 

 

 

 

 

(1-3) cummean() 함수 : (그룹별로) 행을 하나씩 이동해가면서 누적으로 평균 반환

 

mutate() 함수와 함께 사용해서 새로운 cummean.Price 변수를 만들어보겠습니다.

 

 
> # cummean()
> Cars93_1 %>% 
+   group_by(Type) %>% 
+   mutate(cummean.Price = cummean(Price))
Source: local data frame [20 x 3]
Groups: Type [2]

     Type Price cummean.Price
   <fctr> <dbl>         <dbl>
1   Large  18.4      18.40000
2   Large  18.8      18.60000
3   Large  19.3      18.83333
4   Large  20.7      19.30000
5   Large  20.8      19.60000
6   Large  20.9      19.81667
7   Large  23.7      20.37143
8   Large  24.4      20.87500
9   Large  29.5      21.83333
10  Large  34.7      23.12000
11  Large  36.1      24.30000
12    Van  16.3      16.30000
13    Van  16.6      16.45000
14    Van  19.0      17.30000
15    Van  19.1      17.75000
16    Van  19.1      18.02000
17    Van  19.5      18.26667
18    Van  19.7      18.47143
19    Van  19.9      18.65000
20    Van  22.7      19.10000

 

 

 

 

group_by(Type) 함수를 같이 써서 차종(Type)별로 행을 하나씩 아래로 내려가면서 그룹 내 첫 행부터 행당 행까지의 누적 관측치를 모두 사용해서 평균(cumulative mean)을 구했음을 알 수 있습니다. (말로 설명하려니 힘든데요, 말로 된 설명만 봐서는 무슨 말이지 좀 어렵지요? ^^;;;  아래 설명 그림 참고하세요)

 

 

 

 

Cumulative aggregates 소개는 마치고, 이제 Recycled aggregates로 넘아가보겠습니다.

평균이나 중앙값과 같이 (그룹별) 요약통계량을 생성한 후에, 이 요약통계량 벡터를 재활용(Recycling)해서 조건을 부여해 filtering 하는 방법을 아래에 소개합니다.  이름은 Recycled aggregates 라고 거창하게 붙이긴 했는데요, 아래의 예시를 보시면 뭐 별거 없습니다.

 

 

(2) Recycled aggregates

     : group_by(factor) %>% filter(dataframe, x > mean(x)),
     : group_by(factor) %>% filter(dataframe, x > median(x))
 

 

(2-1) group_by(factor) %>% filter(dataframe, x > mean(x))
       : 그룹별로 평균 값(mean)보다 큰 행(rows)만 선별

 

 

> ##------------------------------------------------ > ## R dplyr package > ## > window function - Recycled aggregates > ## : filter(dataframe, x > mean(x)) > ## : filter(dataframe, x > median(x)) > ##------------------------------------------------ > Cars93_1 %>% + group_by(Type) %>% + summarise(n = n(), # number by Type + Price_mean = mean(Price, na.rm = T), + Price_median = median(Price, na.rm = T)) # A tibble: 2 x 4 Type n Price_mean Price_median <fctr> <int> <dbl> <dbl> 1 Large 11 24.3 20.9 2 Van 9 19.1 19.1 > > > # filter(dataframe, x > mean(x)) > Cars93_1 %>% + group_by(Type) %>% + filter(Price > mean(Price)) Source: local data frame [8 x 2] Groups: Type [2] Type Price <fctr> <dbl> 1 Large 24.4 2 Large 29.5 3 Large 34.7 4 Large 36.1 5 Van 19.5 6 Van 19.7 7 Van 19.9 8 Van 22.7 >

 

 

 

 

 

(2-2) group_by(factor) %>% filter(dataframe, x > median(x))

      : 그룹별로 중앙값(median) 보다 큰 행(rows)만 선별

 

 

> # filter(dataframe, x > median(x))
> Cars93_1 %>% 
+   group_by(Type) %>% 
+   filter(Price > median(Price))
Source: local data frame [9 x 2]
Groups: Type [2]

    Type Price
  <fctr> <dbl>
1  Large  23.7
2  Large  24.4
3  Large  29.5
4  Large  34.7
5  Large  36.1
6    Van  19.5
7    Van  19.7
8    Van  19.9
9    Van  22.7

 

 

 

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

 

 

참고로, {base} package에 기본으로 내장되어 있는 함수들인
 - 누적 합 (cumulative sums) : cumsum()
 - 누적 곱 (cumulative products) : cumprod()
 - 누적 최소값 (cumulative minima) : cummin()
 - 누적 최대값 (cumulative maxima) : cummax()

에 대해서는 여기( ☞ http://rfriend.tistory.com/231 )를 참고하세요.

 

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

 

 

728x90
반응형
Posted by Rfriend
,