Intro


파이썬의 수많은 라이브러리 중, 엑셀데이터를 수집하고 정리하는 데 최적화된 도구가 판다스 라이브러리라고 볼 수 있다. 판다스를 배우면 데이터과학의 80~90% 업무를 처리할 수 있고, 데이터과학자에게 필요한 기본적이면서도 아주 중요한 도구를 갖출 수 있다. 데이터베이스 관점에서 접근하면, 관계형 데이터베이스를 보다 쉽고 직관적으로 처리하는데 도움을 준다. 판다스가 처리할 수 있는 데이터는 다음과 같다.

  • SQL 테이블 또는 Excel 스프레드 시트에서와 같이 유형이 다른 열이있는 테이블 형식 데이터

  • 순서에 상관없이 정렬된 시계열 데이터

  • 행과 열 라벨이 있는 임의의 행렬 데이터

  • 통계 데이터

판다스의 두 가지 기본 데이터 구조인 Series(1차원) 및 DataFrame (2차원)은 재무, 통계, 사회 과학 및 여러 엔지니어링 분야에서 대부분의 데이터를 처리하는데 적합하다. 판다스는 파이썬의 NumPy를 기반으로 구축되었으며 과학 컴퓨팅 환경 내에서 다른 많은 타사 라이브러리와 잘 통합되도록 설계되었다.

 

1차원 배열의 시리즈


Series는 모든 데이터 유형 (정수, 문자열, 부동 소수점 숫자, Python 객체 등)을 보유 할 수있는 1차원 레이블이 지정된 배열이다. 이때, 축 레이블을 총칭하여 인덱스라고 부른다.

 

[그림 1-1] 시리즈와 데이터프레임 구조

<출처: https://www.kdnuggets.com/2017/01/pandas-cheat-sheet.html>

시리즈 만들기


시리즈를 만드는 방법은 다음과 같다.

s = pd.Series(data, index=index)

 

보통 딕셔너리와 시리즈의 구조가 비슷하기 때문에 딕셔너리를 시리즈로 변환하는 방법을 사용하기는 하지만, 엄밀히 말하면 data가 더 적정한 표현이다. 다만, data에 용어 정의는 조금 더 숙지할 필요가 있다.

여기에서 data는 크게 3가지를 의미한다.

  • a Python Dictionary

  • an ndarray

  • a scalar value

A Python Dictionary


import pandas as pd

d = {'b': 1, 'a': 0, 'c': 2}
pd.Series(d)
b    1
a    0
c    2
dtype: int64

여기서 주의해야 할 점이 있다. 만약에 index가 알파벳 순인 ['a', 'b', 'c'] 형태로 정렬이 되었다면 판다스와 파이썬의 버전을 확인하기를 바란다. 독자의 편의상 판다스 메뉴얼 원어를 그대로 인용하기로 한다.

Note: When the data is a dict, and an index is not passed, the Series index will be ordered by the dict’s insertion order, if you’re using Python version >= 3.6 and Pandas version >= 0.23.
If you’re using Python < 3.6 or Pandas < 0.23, and an index is not passed, the Series index will be the lexically ordered list of dict keys.

다른 형태의 Series를 보도록 한다.

data = {'a': 0., 'b': 1., 'c': 2.}
pd.Series(data)
a    0.0
b    1.0
c    2.0
dtype: float64
pd.Series(data, index=['b', 'c', 'd', 'a'])
b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64

Note: 저장된 시리즈 객체 data에 index인자와 함께 인덱스 d를 추가하였더니, NaN이라는 문자가 나타났는데, 이는 판다스에서 사용되는 결측치에 해당하는 일종의 표준어라 생각하면 된다.

An ndarray


ndarray는 NumPy에서 파생된 객체로써, 이때에는 인덱스와 ndarray가 같은 길이로 매칭되어야 하며, 만약에 매칭되는 인덱스가 없다면 자동으로 [0, ..., len(data) - 1] 형태로 생성될 것이다.

import numpy as np

data = np.random.randn(5)
s1 = pd.Series(data, index=['a', 'b', 'c', 'd', 'e'])
print(s1)
a    0.250122
b    0.952477
c   -0.225997
d    0.798407
e    0.979913
dtype: float64

index=['a', 'b', 'c', 'd', 'e']가 존재하기 때문에, 정확하게 a~e형태로 출력되는 것을 확인할 수 있다.

s2 = pd.Series(data)
print(s2)
0    0.250122
1    0.952477
2   -0.225997
3    0.798407
4    0.979913
dtype: float64

인덱스가 존재하지 않기 때문에 자동으로 숫자가 생성되었지만, 이 때 특이한점은 인덱스가 0부터 시작한다는 것이다.

A scalar value


만약에 데이터가 scalar value (like 5)라면 반드시 인덱스가 존재해야 한다. 또한, 이때 scalar value는 인덱스의 길이 만큼 반복된다.

pd.Series(5., index=['a', 'b', 'c', 'd', 'e'])
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64

여기서 잠깐! Pandas와 pd의 차이는?

더보기

만약에서 파이썬 파일(확장자.py)에서 판다스를 활용하려면 import 명령어를 사용한다. 앞의 예제에서 import pandas as pd or import numpy as np와 같은 형식을 입력했는데, 이는 약어를 의미한다. 만약에 as pd와 같은 약어를 사용하지 않는다면 pd.Series() 대신, pandas.Series()라고 사용을 해야 한다. 그런데, 실무에서는 약칭을 자주 사용하기 때문에 본 책에서도 약어 위주로 소스 코드를 입력하려고 한다.


원소 선택 - 배열과 같은 Series


Series는 ndarray와 매우 유사하게 작동하며 대부분의 NumPy 함수 유사한 인자를 가지고 있다. 그런데, 슬라이싱과 같은 작업을 할 때에는 인덱스도 같이 슬라이스를 해야 한다. 아래의 코드들은 원소 선택과 연관이 있기 때문에 주의 깊게 볼 필요가 있다.

s[0]
-0.8691605170316093
s[:2]
a   -0.869161
b    2.064238
dtype: float64
s[s > s.median()]
b    2.064238
c    1.011246
dtype: float64
s[[4, 3, 1]]
e   -0.116053
d   -0.524454
b    2.064238
dtype: float64
np.exp(s)
a    0.419303
b    7.879294
c    2.749024
d    0.591878
e    0.890428
dtype: float64

NumPy의 배열처럼, 판다스의 Series도 dtype를 가지고 있다.

s.dtype
dtype('float64')

만약에 Series를 지원하는 행렬을 사용하고 싶다면, 이 때에는 Series.array를 사용한다.

s.array
[ -0.8691605170316093,    2.064238263144146,   1.0112458349198605,
    -0.52445402723024, -0.11605276365619435]
Length: 5, dtype: float64

인덱스없이 일부 작업을 수행해야하는 경우 배열로 접근하는 것은 매우 유용 할 수 있다. 만약에 Series.array가 아닌 NumPy의 ndarray로 사용해야 하는 경우에는 Series.to_numpy() 한다.

s.to_numpy()
array([-0.86916052,  2.06423826,  1.01124583, -0.52445403, -0.11605276])

원소 선택 - 딕셔너리와 같은 Series

배열에서 특정 값(value)을 가져올 때는 보통 배열의 숫자를 통해서 값을 조회했습니다만, 인덱스를 활용하여 문자를 통해 값을 조회 한다. 객체 s에서 ['a']를 입력하여 값을 가져오도록 한다.

s['a']
-0.8691605170316093

이번에는 새로운 값과 인덱스를 생성한다. 

s['e'] = 12.
print(s)
a    -0.869161
b     2.064238
c     1.011246
d    -0.524454
e    12.000000
dtype: float64
'e' in s # True
'f' in s # False

만약에 특정 라벨이 포함되지 않았다면, 예외가 발생할 것이다. 'f'를 대입하여 실행해본다. 

s['f']
KeyError: 'f'

이 때, `KeyError: 'f'`라는 에러가 발생하는 것을 확인할 수 있다. `get()` 사용하면, 해당하는 값이 반환이 되지 않거나 또는 `None`을 반환한다.

s.get('f') # Nothing displayed
print(s.get('f')) # None

위 두개의 코드를 각각 실행 한 후 결과값을 비교해 보도록 한다. 

 

- End of Document - 

+ Recent posts