[Pandas 기초] 시리즈(Series)와 데이터프레임(Dataframe)

업데이트:

개요

jpg

판다스(pandas)의 기본 자료구조

판다스는 R을 모티브로하여 만든 파이썬 라이브러리이다. 데이터분석을 위해 수집, 전처리 등의 과정은 대부분 데이터프레임의 형태로 이루어지는 경우가 많다. 여기서 말하는 데이터프레임은 흔히 많이 들어본 행과 열로 이루어진 표를 의미한다.
시리즈는 데이터프레임의 하위 자료형으로, 1개의 열이 시리즈이고 이 시리즈가 다수 모여 데이터프레임을 형성한다고 이해하면 쉽다.
R을 주로 다뤘던 사람으로서 R보다 분명 불편한 점도 많다. 하지만 개발언어를 기초로 하고 있는 파이썬이 이제는 데이터분석도 무리없이 수행할 수 있다는 점에 손을 들어주고 싶다.


1. 시리즈(series)

1-1. 딕셔너리로 만들기

라이브러리 불러오기

import pandas as pd

딕셔너리로 Series 생성

dict_data = {'a':1,'b':2,'c':3}
series_data = pd.Series(dict_data)

확인

print(type(series_data))
print('\n')
print(series_data)
[Output]
<class 'pandas.core.series.Series'>


a    1
b    2
c    3
dtype: int64

위와 같이 결과를 보면 딕셔너리의 key값이 인덱스(index)로, value값이 값으로 들어간 것을 알 수있다.
결국 시리즈는 딕셔너리 구조와 비슷하다고 생각할 수 있다.

인덱스와 값은 다음으로 확인할 수 있다.

print(series_data.index)
print(series_data.values)
[Output]
Index(['a', 'b', 'c'], dtype='object')
array([1, 2, 3], dtype=int64)

1-2. 리스트로 만들기

마찬가지 방법으로 리스트를 만들고, 시리즈를 생성해보자.

list_data = ['2019-01-02',3.14,'ABC',100,True]
series_data = pd.Series(list_data)

확인

print(series_data)
0    2019-01-02
1          3.14
2           ABC
3           100
4          True
dtype: object

이와 같이 리스트는 딕셔너리와 달리 key값이 없는 순서형 자료형이므로, 인덱스는 자동으로 0부터 부여된다.

마찬가지로 인덱스와 값 확인

idx = series_data.index
val = series_data.values
print(idx)
print('\n')
print(val)
[Output]
RangeIndex(start=0, stop=5, step=1)


['2019-01-02' 3.14 'ABC' 100 True]

1-3. 튜플로 만들기

tup_data = ('용근','1995-02-08','남',True)
series_data = pd.Series(tup_data, index = ['이름','생년월일','성별','학생여부'])
print(series_data)
이름              용근
생년월일    1995-02-08
성별               남
학생여부          True
dtype: object

튜플도 리스트와 차이는 없다.
이번에는 pd.Series()함수의 옵션으로 index명을 지정해 주었다.

1-4. 원소선택, 인덱싱

tup_data = ('용근','1995-02-08','남',True)
series_data = pd.Series(tup_data, index = ['이름','생년월일','성별','학생여부'])
print(series_data[0])
print(series_data['이름'])
[Output]
용근
용근

인덱싱은 [위치] 또는 [‘인덱스명’]으로 인덱싱을 할 수 있다.
전자는 리스트와 같은 원리, 후자는 딕셔너리와 같은 원리이다.

여러 값을 인덱싱 할떄는 앞에서 처럼 값만 반환하지 않고 짝을 이루는 인덱스와 값을 모두 반환한다.

처음 사용할때 헷갈리기도하고 주의해야할 점이 있다.
각각의 위치 또는 이름으로 인덱싱할때는 괄호를 두개 [[]] 씌워야한다.

print(series_data[[1,2]])
print('\n')
print(series_data[['생년월일','성별']])
[Output]
생년월일    1995-02-08
성별               남
dtype: object


생년월일    1995-02-08
성별               남
dtype: object

:로 인덱싱 할때는 대괄호 하나만 필요하다.

print(series_data[1:2])
print('\n')
print(series_data['생년월일':'성별'])
#인덱스 이름으로 사용하면 범위 끝이 포함됨
생년월일    1995-02-08
dtype: object


생년월일    1995-02-08
성별               남
dtype: object


2. 데이터프레임(dataframe)

처음에 말했듯이 데이터프레임은 시리즈가 여러개 합쳐진 자료형이라고 생각하면 된다.
즉 데이터프레임에서 하나의 열만 가져온다면, 그건 시리즈가 된다.

2-1. 딕셔너리로 만들기

import pandas as pd

dict_data = {'c0':[1,2,3],'c1':[4,5,6],'c2':[7,8,9],'c3':[10,11,12],'c4':[13,14,15]}
df = pd.DataFrame(dict_data)

print(type(df))
print('\n')
print(df)
<class 'pandas.core.frame.DataFrame'>


   c0  c1  c2  c3  c4
0   1   4   7  10  13
1   2   5   8  11  14
2   3   6   9  12  15

1개의 열들이 1개의 시리즈라고 했으므로, 마찬가지로 key값이 열이름이 된다.

2-2. 리스트로 만들기

df = pd.DataFrame([[14,'남','염경중'],[17,'여','양동중']],
                  index = ['준서','예은'],
                  columns = ['나이','성별','학교'])
print(df)
print('\n')
print(df.index)
print('\n')
print(df.columns)
[Output]
    나이 성별   학교
준서  14  남  염경중
예은  17  여  양동중


Index(['준서', '예은'], dtype='object')


Index(['나이', '성별', '학교'], dtype='object')

리스트들로 데이터프레임을 만들게되면 리스트들이 하나의 행(row)가 된다.

정리하자면, 딕셔너리로 만들면 데이터프레임의 열(column)을 하나씩 쌓아가는 형태이고,
리스트로 만들면 행(row)을 하나씩 쌓아가는 형태라고 생각할 수 있다.

2-3. 행,열 이름 변경 : 기본

먼저 방금 만든 데이터 프레임을 확인해보자.

print(df.index)
print(df.columns)
[Output]
Index(['준서', '예은'], dtype='object')
Index(['나이', '성별', '학교'], dtype='object')

둘다 인덱스 객체이다.

변경하려면,

df.index = ['학생1','학생2']
df.columns = ['연령','남녀','소속']
df
연령 남녀 소속
학생1 14 염경중
학생2 17 양동중

인덱스(index)와 열(columns)이름의 수에 맞게 지정해주기만 하면 된다.

2-4. 행,열 이름 변경 : rename()함수

다시 데이터프레임 생성

df = pd.DataFrame([[14,'남','염경중'],[17,'여','양동중']],
                  index = ['준서','예은'],
                  columns = ['나이','성별','학교'])

df
나이 성별 학교
준서 14 염경중
예은 17 양동중

rename()함수는 데이터프레임 자료형에서 제공하는 함수로,
df.rename(columns={변경 전 열이름, 변경 후 열이름}, inplace=True)로 사용할 수 있고 inplace=True옵션을 지정해주면 df에 바로 적용된다.

열이름 변경

df.rename(columns={'나이':'연령','성별':'남녀', '학교':'소속'}, inplace=True)
df
연령 남녀 소속
준서 14 염경중
예은 17 양동중

행이름 변경시 columns=가 아닌 index=로 지정해 주면 된다.

df.rename(index={'준서':'학생1', '예은':'학생2'}, inplace=True)
df
연령 남녀 소속
학생1 14 염경중
학생2 17 양동중

2-5. 행삭제 : drop()함수

다시 새로운 데이터프레임을 만들어보자.

exam_data = {'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data, index = ['학생1','학생2','학생3'])
df
수학 영어 음악 체육
학생1 90 98 85 100
학생2 80 89 95 90
학생3 70 95 100 90

drop('행 또는 열이름', axis = 0 or 1, inplace =True)와 같이 사용한다.
행을 삭제하려면 axis=0(디폴트값), 열을 삭제하려면 axis=1을 꼭 지정해 주어야한다.

df2 = df.copy()   # df[:]로 복사할 시에 판다스는 오류를 반환한다 왜그럴까?
df2.drop('학생2',inplace=True)  #행삭제는 axis옵션 생략가능
df2
수학 영어 음악 체육
학생1 90 98 85 100
학생3 70 95 100 90

이번에는 열삭제
참고로 두개이상의 열을 drop하고 싶으면 대괄호[]로 표시해주어야 한다.

df3 = df.copy()
df3.drop(['수학','음악'],axis=1,inplace=True)
df3
영어 체육
학생1 98 100
학생2 89 90
학생3 95 90

2-6. 데이터프레임 인덱싱 : lociloc

lociloc는 가장많이 사용되는 함수라고 생각한다. 데이터 전처리를 할때 인덱싱이 항상 사용되기 때문이다.
간단하게 말해서 loc은 이름으로, iloc은 숫자로 인덱싱한다고 먼저 기억하자.

아까와 같은 데이터프레임을 다시 만들어보자.

exam_data = {'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data, index = ['서준','우현','인아'])
df
수학 영어 음악 체육
서준 90 98 85 100
우현 80 89 95 90
인아 70 95 100 90

첫번째 행(‘서준’인덱스의 행)을 선택해보자.

label1 = df.loc['서준']
position1 = df.iloc[0]

print(label1)
print('\n')
print(position1)
[Output]
수학     90
영어     98
음악     85
체육    100
Name: 서준, dtype: int64


수학     90
영어     98
음악     85
체육    100
Name: 서준, dtype: int64

결과값이 마치 열을 선택한 것처럼 나와서 처음보면 헷갈릴 수도 있다. 하지만 서준이라는 행을 인덱스명과 행위치(index)로 선택한 것을 알 수 있다.

선택한 행은 이제 시리즈 자료형이다. 이를 리스트, 튜플, 딕셔너리 형태로 변형할 수 있다.

print(list(label1))
print(tuple(label1))
print(dict(label1))
[Output]
[90, 98, 85, 100]
(90, 98, 85, 100)
{'수학': 90, '영어': 98, '음악': 85, '체육': 100}

재미있는 점은 딕셔너리는 알아서 인덱스로 columns명이 부여됐다.

두개 이상의 행을 선택할때는 아까 설명한 것처럼 대괄호를 신경 써야한다.
각각 선택은 두개[[]], 범위(:)로 선택은 한개[] 다.
첫번째와 두번쨰행을 선택해보자.

label2 = df.loc[['서준','우현']]
position2 = df.iloc[[0,1]]

print(label2)
print('\n')
print(position2)
[Output]
    수학  영어  음악   체육
서준  90  98  85  100
우현  80  89  95   90


    수학  영어  음악   체육
서준  90  98  85  100
우현  80  89  95   90

여기서 행위치로 범위:를 이용해 인덱싱 할때는, 끝값이 포함되지 않는다는 점을 주의하자.

label3 = df.loc['서준':'우현']
position3 = df.iloc[0:1]

print(label3)
print('\n')
print(position3)
[Output]
    수학  영어  음악   체육
서준  90  98  85  100
우현  80  89  95   90


    수학  영어  음악   체육
서준  90  98  85  100

2-7. 열 선택

열선택은 따로 함수가 필요없다.
df.열이름 또는 df[‘열이름’]으로 선택이 가능하다.

exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)

수학 열만 선택

math1 = df['수학']
print(math1)
print(type(math1))
print('\n')

영어 열만 선택

english = df.영어
print(english)
print(type(english))
[Output]
   이름  수학  영어   음악   체육
0  서준  90  98   85  100
1  우현  80  89   95   90
2  인아  70  95  100   90
<class 'pandas.core.frame.DataFrame'>


0    90
1    80
2    70
Name: 수학, dtype: int64
<class 'pandas.core.series.Series'>


0    98
1    89
2    95
Name: 영어, dtype: int64
<class 'pandas.core.series.Series'>

두개이상의 열 선택하기

music_gym = df[['음악','체육']]
print(music_gym)
print(type(music_gym))
print('\n')

math2 = df[['수학']]
print(math2)
print(type(math2))
[Outpit]
    음악   체육
0   85  100
1   95   90
2  100   90
<class 'pandas.core.frame.DataFrame'>


   수학
0  90
1  80
2  70
<class 'pandas.core.frame.DataFrame'>

참고로 df[['수학']]는 한개의 열만 선택했는데도 이중괄호를 사용했다.
type을 확인해보면 하나의 열이지만 시리즈가 아닌 데이터프레임인 것을 알 수 있다.
별것 아니라 생각할 수도 있지만 사용되는 경우가 은근 있는 것 같다.

2-8. 범위 슬라이싱

계속 같은 데이터프레임으로 연습해보자.

exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)
df
이름 수학 영어 음악 체육
0 서준 90 98 85 100
1 우현 80 89 95 90
2 인아 70 95 100 90

모든 행을 2행간격으로 선택

df.iloc[: : 2]
이름 수학 영어 음악 체육
0 서준 90 98 85 100
2 인아 70 95 100 90

0행부터 2행까지 2행간격으로 선택

df.iloc[0:3:2]  
이름 수학 영어 음악 체육
0 서준 90 98 85 100
2 인아 70 95 100 90

모든행을 -1행간격으로,
즉 역순으로 정렬과 같은 기능이라고 할 수 있다.

df.iloc[: : -1]
이름 수학 영어 음악 체육
2 인아 70 95 100 90
1 우현 80 89 95 90
0 서준 90 98 85 100

2-9. 원소선택

아래의 set_index()함수는 특정열을 인덱스로 부여해주는 함수이다.
날짜정보의 열이나, key값을 가진 열을 인덱스로 부여하고 싶은 경우에 사용한다.

exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)

# 이름을 인덱스로 지정
df.set_index('이름', inplace=True)
df
수학 영어 음악 체육
이름
서준 90 98 85 100
우현 80 89 95 90
인아 70 95 100 90

표모양이 하나 밀려서 조금 이상하게 보이지만, 맞게 된거다.

앞에서 loc,iloc이나 행,열 선택과정에서 두개이상의 행이나 두개이상의 열을 선택하려면 [[]]을 이용해아 한다고 했었다. 하지만 하나의 대괄호 []만 쓴다면 어떻게 될까

df.loc['서준','음악']
[Output]
85

바로 ‘서준’행의 ‘음악’열의 값, 즉 해당 원소하나를 가져오라는 의미다.
정말 편한 기능이다.

df.iloc[0,2]
[Output]
85

iloc도 마찬가지다.

연습으로 서준의 음악, 체육점수를 찾는 4가지방법을 앞에서 배운 것을 이용해서 수행해보자.

# 1
df.loc['서준', ['음악','체육']]
[Output]
음악     85
체육    100
Name: 서준, dtype: int64
# 2
df.loc['서준', '음악':'체육']
[Output]
음악     85
체육    100
Name: 서준, dtype: int64
# 3
df.iloc[0,[2,3]]
[Output]
음악     85
체육    100
Name: 서준, dtype: int64
# 4
df.iloc[0,2:4]
[Output]
음악     85
체육    100
Name: 서준, dtype: int64

두개 이상의 행과 열 선택할때도 어려울 건 없다.

print(df.loc[['서준','우현'],['음악','체육']])
print(df.loc['서준':'우현','음악':'체육'])
print(df.iloc[[0,1],[2,3]])
print(df.iloc[0:2,2:4])
[Output]
    음악   체육
이름         
서준  85  100
우현  95   90
    음악   체육
이름         
서준  85  100
우현  95   90
    음악   체육
이름         
서준  85  100
우현  95   90
    음악   체육
이름         
서준  85  100
우현  95   90

2-10. 행, 열 추가

열추가는 df['새로운 열이름'] = 값으로 쉽게 추가해줄 수 있다.
물론 새로운 열이름이 아니라 기존 열이름이라면 덮어씌워진다.

exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)
df['국어'] = 80
df
이름 수학 영어 음악 체육 국어
0 서준 90 98 85 100 80
1 우현 80 89 95 90 80
2 인아 70 95 100 90 80
exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)
df.loc[3] = 10
df.loc[4] = ['용근',100,100,100,100]
df
이름 수학 영어 음악 체육
0 서준 90 98 85 100
1 우현 80 89 95 90
2 인아 70 95 100 90
3 10 10 10 10 10
4 용근 100 100 100 100

이처럼 행추가를 할때 단일값은 그 값으로 모두 들어가고, 그게 아니라면 행또는 열의 수에 맞게 원소를 추가시켜야 한다.

2-11. 원소 값 변경하기

exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)
# 이름을 인덱스로 지정
df.set_index('이름', inplace=True)

print(df)
[Output]
    수학  영어   음악   체육
이름                  
서준  90  98   85  100
우현  80  89   95   90
인아  70  95  100   90

이번에는 원소를 변경 시키는데, 앞에서 원소값을 인덱싱하는 방법을 설명했으니 그냥 거기에 값을 넣으면 끝이다.

서준의 체육값 변경 4가지 방법이다.

df.iloc[0][3] = 80
print(df)
print('\n')

df.iloc[0,3] = 88
print(df)
print('\n')

df.loc['서준']['체육'] = 90
print(df)
print('\n')

df.loc['서준','체육'] = 100
print(df)
[Output]
    수학  영어   음악  체육
이름                 
서준  90  98   85  80
우현  80  89   95  90
인아  70  95  100  90


    수학  영어   음악  체육
이름                 
서준  90  98   85  88
우현  80  89   95  90
인아  70  95  100  90


    수학  영어   음악  체육
이름                 
서준  90  98   85  90
우현  80  89   95  90
인아  70  95  100  90


    수학  영어   음악   체육
이름                  
서준  90  98   85  100
우현  80  89   95   90
인아  70  95  100   90

2-12. 행과 열 바꾸기 : 전치행렬 transpose()함수, .T

전치행렬은 df.transpose(), df.T로 만들 수 있다.

exam_data = {'이름':['서준','우현','인아']
             ,'수학':[90,80,70], '영어':[98,89,95],
            '음악':[85,95,100], '체육':[100,90,90]}
df = pd.DataFrame(exam_data)
print(df)
print('\n')

df = df.transpose()
print(df)
print('\n')

df = df.T
print(df)
print('\n')
[Output]
   이름  수학  영어   음악   체육
0  서준  90  98   85  100
1  우현  80  89   95   90
2  인아  70  95  100   90


      0   1    2
이름   서준  우현   인아
수학   90  80   70
영어   98  89   95
음악   85  95  100
체육  100  90   90


   이름  수학  영어   음악   체육
0  서준  90  98   85  100
1  우현  80  89   95   90
2  인아  70  95  100   90

물론 전치를 두번하면 원상태로 돌아온다.


Reference

도서 [파이썬 머신러닝 판다스 데이터 분석]을 공부하며 작성하였습니다.

댓글남기기