목차


데이터 프레임에 익숙해지기 위해서는 데이터 프레임을 다루는 실습문제를 많이 풀어보는 것을 권장합니다. 다루는 내용이 많아 총 3번에 나눠 작성할 예정이며 따라서 이번 글과 다음 글에서는 데이터 프레임에서 사용할 수 있는 함수와 메서드 등에 중심을 둘 것이고 세번째 글에서는 앞에서 다루었던 내용에 대한 실습문제를 추가할 예정입니다.


데이터 프레임 (DataFrame)

데이터프레임(DataFrame)을 모듈 pandas 내에 내장되어 있는 행과 열을 갖는 2차원 자료 구조로 서로 다른 데이터 타입을 허용하는데, 컬럼(열)의 구성 단위가 시리즈이기 때문에 한 컬럼에는 같은 데이터 타입만 가능합니다.

시리즈에서처럼 편의상 데이터프레임만 다시 불러옵시다.

from pandas import DataFrame


** 프로필 만들기

numpy, pandas에 시리즈에 데이터 프레임까지… 매번 python 실행마다 하나하나 입력하는 것은 굉장히 지루하고 비효율적인 과정입니다. 그러나 자신이 자주 사용하는 모듈을 파일에 담아서 코드 한 줄로 불러온다면 더 편해질 것입니다. 이런 작업으로 만든 파일을 프로필(profile)이라고 하는데 기본 디렉토리에 프로필을 저장한 후 run 프로필만 실행하면 파일 안에 있는 모듈들이 모두 실행됩니다.

import numpy as np
import pandas as pd
from numpy import nan as NA
from pandas import Series
from pandas import DataFrame

여태 불러왔던 모듈들의 이름입니다. 코드 작성 후 파일로 기본 디렉토리에 저장하되 profile이라는 이름은 피하시길 바랍니다. 원인이 자세하게는 기억 안나지만 기본 설정과 엉켜버려서 python 실행 자체에 문제를 야기할 수 있습니다. 이런 문제로 실습 시간에 python 실행이 안되서 몇 시간 고민했었는데 프로필 이름을 수정하니 해결되는 단순한 해프닝이 있었습니다. 저는 profile1이라는 이름으로 저장했는데 이름 정도는 개인 취향대로 하셔도 됩니다.

앞으로는 python을 킬 때마다 아래 코드만 실행하면 등록된 모듈이 모두 불러와집니다.

run profile1

이후 배우는 모듈 중에서 자주 사용하는 모듈도 등록 후 저장하시면 똑같이 코드 한 줄로 불러올 수 있습니다.


1. 생성

데이터 프레임을 생성하는 법은 다음 코드와 같습니다. DataFrame() 괄호 안에 딕셔너리 형식으로 넣어주시면 되는데 Key-Value하나가 시리즈이며, Key값이 컬럼명이 되고 Value값이 내용이 됩니다.

# 딕셔너리 생성
d1 = {'col1':[1,2,3,4], 'col2':[5,6,7,8]}

# 데이터프레임에 넣기
df1 = DataFrame(d1)

df1
Out: 
   col1  col2
0     1     5
1     2     6
2     3     7
3     4     8

#※ 데이터프레임 추가 생성시 주의
#생성된 데이터프레임에 대해 아래 문장을 실행하면 시리즈처럼 column명이 수정되는 것이 아니라 해당 컬럼명의 데이터를 조회한다.

DataFrame(df1, columns=['COL1','COL2'])
Out:
   COL1  COL2
0   NaN   NaN
1   NaN   NaN
2   NaN   NaN
3   NaN   NaN


2. 기본 메소드

#index 추출
df1.index
Out : RangeIndex(start=0, stop=4, step=1)

#column명 추출

df1.columns
Out : Index(['col1', 'col2'], dtype='object')

#각 컬럼의 데이터 타입 확인하는 메소드
df1.dtypes
Out :
col1 int64
col2 int64
dtype: object
#Oracle의 desc, R의 str과 비슷함


#Key값 제외 Value만 배열형식 출력

df1.values 
Out : 
array([[1, 5],
[2, 6],
[3, 7],
[4, 8]], dtype=int64)


3. 인덱스

데이터 프레임은 2차원 구조이고 행과 열이 다 있습니다. 인덱스와 컬럼명 모두 설정할 수가 있는데 각각에 대한 속성에 대한 이름 또한 설정할 수가 있습니다. 예를 들어 인덱스가 [‘서울’,’부산’,’대구’] 이렇게 되어있다면 인덱스를 설명할 수 있는 인덱스 이름(index.name)으로 ‘도시’라고 입력할 수가 있습니다. 마찬가지로 컬럼에 대해서도 컬럼들의 이름(columns.name)을 설정할 수 있습니다. 코드를 살펴보면서 어떻게 이름을 부여하는지 확인해보도록 합시다.

#index 부여
df1.index = [1,2,3,4]

df1.index.name = 'month'
#index 이름 부여

#index 컬럼
df1.columns.name = 'columns'

df1
Out :
columns col1 col2
month 
1          1    5
2          2    6
3          3    7
4          4    8


4. 색인

데이터 프레임에서의 색인은 배열이나 리스트와는 달리 인덱스와 컬럼명에 대한 설명이 필요해서 뒤에 배치하였습니다. 우선 기존에 알던 방식의 색인을 설명드리고 다음에는 선호되는 색인 방식을 적어보았습니다.

1. 비선호 방식

#기존 색인방식, R에서의 방식과 유사
df1['col1']
Out :
month
0      1
1      2
2      3
3      4
#df1.col1 도 같습니다.

df1[0:3]
Out :
columns col1 col2
month 
1          1    5
2          2    6
3          3    7

df1[0,0]
#(하단 참조)

무수한 오류 메시지들의 악수의 요청이..


다음으로는 판다스에서 제공되는 색인 메소드가 있는데 이 방법이 더 선호되어집니다. .iloc.loc 인데요. 순서대로 행/열 번호에 대한 색인, 행/열 이름에 대한 색인 + 조건 색인을 지원하는 메소드입니다. 참고로 예전 문헌을 보면 .ix라는 메소드도 확인할 수 있는데 지금은 없는 특성(has no attribute)라고 나오는 걸 보면 모듈 내에서 삭제된 것 같습니다.

현재는 사라진 .ix


  1. pandas의 메소드를 이용한 색인(선호 방식)
# 1. iloc 를 이용한 색인

# 1행 1열의 원소 출력
df1.iloc[0,0]
Out : 1

# 1행의 모든 열 출력
df1.iloc[0,:]
Out :
col1       1
col2       5

# 1~3행의 1열 출력 {슬라이싱 색인}
df1.iloc[0:3,0]
Out :
1        1
2        2
3        3

# 1~2행의 1~2열 출력
df1.iloc[[0,1],[0,1]]
Out :
columns col1 col2
1          1    5
2          2    6

# 마지막 행 색인 {역순(Reverse) 색인}
df1.iloc[-1,:]
Out :
col1       4
col2       8

# 2. loc를 이용한 색인

# label indexing : 행 이름이 1,  열 이름이 col1 출력

df1.loc[1, 'col1']
Out :
1

# col1의 Value값이 3보다 큰 경우 모든 행 출력
df1.loc[df1.col1 > 3, :]
Out :
columns col1 col2
4          4    8


5. 제거

데이터프레임에서는 행과 열을 제거하는 메소드 drop이 제공됩니다.

#행 이름이 'col1'인 행 제거
df1.drop('col1', axis = 1)

#열 이름이 1인 열 제거
df1.drop(1, axis = 0) 


주의 : drop 메소드 사용시 첫 인자에는 이름값(Label)만 전달할 수 있으며 위치값 전달이 불가능합니다.



6. 구조 수정

데이터 프레임에 행을 추가하거나 데이터 프레임끼리 합쳐보는 부분입니다. 다음 데이터프레임을 이용하여 예제를 살펴봅시다.

df1 = DataFrame(np.arange(1,13).reshape(4,3))
df2 = DataFrame(np.arange(13,19).reshape(2,3))
df3 = DataFrame(np.arange(10,90,10).reshape(4,2))

#데이터 프레임간 합치기

#1. df1에 df2를 붙이기
#append : = 연산자 없이 바로 추가가 적용되는 메소드이니 사용시 주의바랍니다.
df1.append(df2)
Out: 
    0   1   2
0   1   2   3
1   4   5   6
2   7   8   9
3  10  11  12
0  13  14  15
1  16  17  18

#1-1. 행번호 무시(Value에는 지장 없음)
df1.append(df2, ignore_index = True)
Out: 
    0   1   2
0   1   2   3
1   4   5   6
2   7   8   9
3  10  11  12
4  13  14  15
5  16  17  18


#2. 새로운 열 추가
df1['a'] = [11,21,32,41]; df1
Out[17]: 
    0   1   2   a
0   1   2   3  11
1   4   5   6  21
2   7   8   9  32
3  10  11  12  41

7. 산술 연산

  • 연산자를 이용한 데이터 프레임간 연산과 메소드를 이용한 연산 모두 살펴보도록 하겠습니다.

1. + 연산자

# 컬럼명 수정
df1.columns = ['a','b','c','d']
df3.columns = ['a','b']

# + 연산자를 이용한 데이터 프레임 간 덧셈 연산
df1 + df3
Out: 
    a   b   c   d
0  11  22 NaN NaN
1  34  45 NaN NaN
2  57  68 NaN NaN
3  80  91 NaN NaN
#같은 컬럼끼리만 연산


df4 = DataFrame(np.array([100,101,102]).reshape(1,3))
df2 + df4
Out: 
       0      1      2
0  113.0  115.0  117.0
1    NaN    NaN    NaN

2. 산술 연산 메소드를 이용한 연산(add, sub, div, mul)

산술 연산 메소드에 숫자를 입력하는 경우에는 모든 Value값에 메소드에 해당하는 연산이 진행이 되고 다른 데이터프레임을 입력하는 경우에는 인덱스와 컬럼명이 일치하는 Value끼리 연산이 이루어집니다. 그런데 연산을 하다보면 연산에 이용되는 데이터프레임들의 구조가 서로 달라서 연산에 오류가 생겨 NaN이 발생하는 경우가 있습니다. 산술 연산 메소드에서는 이를 해결할 수 있는 fill_value라는 옵션이 있는데요. 인덱스나 컬럼명의 차이로 연산결과가 NaN이 나오는 경우 어떤 값을 채워넣을지 정하는 옵션입니다. 예를 들어 fill_value = 0으로 설정하면 인덱스 차이로 1 + NaN로 연산이 되어 NaN이 될 값이 1 + 0이 되어 1이 나오게 됩니다.

# add를 이용한 덧셈 연산
df2.add(df4)
Out :
       0      1      2
0  113.0  115.0  117.0
1    NaN    NaN    NaN

df2.add(df4, fill_value = 0)
# NaN가 0으로 치환
Out :
       0      1      2
0  113.0  115.0  117.0
1   16.0   17.0   18.0
# 예시 변경후 연산)
df1 = DataFrame(np.arange(1,13).reshape(3,4), columns = ['a','b','c','d'])
df2 = DataFrame(np.arange(1,9).reshape(2,4), columns = ['a','b','c','d'])
df3 = df1.append(df2, ignore_index = True)

# 빼기
df3.sub(df4, fill_value = 0)
Out[24]: 
     a    b    c    d
0  0.0  0.0  0.0  0.0
1  5.0  6.0  7.0  8.0
2  0.0  0.0  0.0  0.0
3  1.0  2.0  3.0  4.0
4  0.0  0.0  0.0  0.0

# 곱셉
df2.mul(2)
Out[27]: 
    a   b   c   d
0   2   4   6   8
1  10  12  14  16

# 나눗셈
df1.div(2)
Out[26]: 
     a    b    c    d
0  0.5  1.0  1.5  2.0
1  2.5  3.0  3.5  4.0
2  4.5  5.0  5.5  6.0


[broadcast 기능]

DataFrame에서는 broadcast를 기본적으로는 지원하지 않음. 산술 연산 메소드에 추가적인 옵션 설정을 통해 추가해야합니다.

※연산에 사용될 데이터프레임을 차원축소하지 않으면 오류가 발생하기 때문에 주의합시다.

  배열 데이터 프레임
생성 arr1 = np.arange(1,9).reshape(4,2) df1 = DataFrame(arr1)
broadcast arr1 + arr1[:,0:1] df1.add(df1.iloc[:,0], axis = 0)
df1 + df1.iloc[:,0:1]


8. 인덱스 변경(reindex)

reindex는 인덱스를 재배치할때 사용하는 메소드입니다.

# 1, 행 재배치
df1.reindex(['b','c','d','e'], fill_value = 0)
  a b c d
b 0 0 0 0
c 0 0 0 0
d 0 0 0 0
e 0 0 0 0

# 2. 열 재배치
# 컬럼명이 b,c,d,e에 해당하는 컬럼 출력
df1.loc[:,['b','c','d','e']]
# 현재는 지원하지 않는 문법

DataFrame(df1, columns = ['b','c','d','e'])
Out :
    b   c   d   e
0   2   3   4 NaN
1   6   7   8 NaN
2  10  11  12 NaN

# fill_value를 이용한 NaN처리
df1.reindex(columns = ['b','c','d','e'], fill_value = 0)
df1.reindex(['b','c','d','e'], axis = 1, fill_value = 0)
Out :
    b   c   d  e
0   2   3   4  0
1   6   7   8  0
2  10  11  12  0

reindex 메소드는 행, 열의 순서 변경이나 아래 코드처럼 산술 연산자를 이용한 데이터 프레임간 연산 시 NaN 처리하는 경우에도 사용합니다.

df1**df2
Out:
        a        b         c           d
0     1.0      4.0      27.0       256.0
1  3125.0  46656.0  823543.0  16777216.0
2     NaN      NaN       NaN         NaN

df1**df2.reindex(df1.index, fill_value = 1)
Out: 
      a      b       c         d
0     1      4      27       256
1  3125  46656  823543  16777216
2     9     10      11        12


9. 정렬

정렬은 행/열 내의 Value를 이용하여 데이터 프레임의 순서를 정렬하는 것으로 SQL에서 ORDER BY 절기능이라고 생각하시면 됩니다. 정렬을 수행하는 메소드는 sort_values입니다.

# 데이터프레임 새로 생성
df1 = DataFrame({'col1':[3,2,4,1],
                 'col2':['a','c','d','b']})

# sort_values 문법 설명
df1.sort_values(by,                   # 정렬대상
                axis,                 # 정렬 축 방향
                ascending = True,     # 오름/내림차순 (True시 오름차순)
                inplace=False,        # 정렬 후 원본 대체
                na_position = 'last') # NA 배치 순서


# 원본 수정 안됨
df1.sort_values('col1', ascending = False); df1
Out:
   col1 col2
0     3    a
1     2    c
2     4    d
3     1    b

# 메소드 실행후 원본 수정
df1.sort_values('col1', ascending = False, inplace = True)
Out :
   col1 col2
2     4    d
0     3    a
1     2    c
3     1    b

# 컬럼별 정렬 기준 전달
df1.sort_values(['col1','col2'], ascending = [False,True])
Out :
   col1 col2
2     4    d
0     3    a
1     2    c
3     1    b
# 순서대로 정렬이 전달되어 col1만 적용된 것으로 보임


글 하나로 끝내기에 양이 많아 다른 메소드에 대한 내용은 다음 글에서 다루도록 하겠습니다. 산술연산 외에 다른 함수를 Value들에 어떻게 적용하는지, csv 파일을 불러오거나 멀티 인덱스에 대한 개념 등 보다 심화된 내용을 다룰 것이니 이번 글의 내용을 충분히 숙지하셔야 다음 글의 내용을 이해하시는데 도움이 될 것입니다.