Pandas에서 Series는 굉장히 강력한 활용성을 갖고 있지만 어떻게 보면 같은 타입의 데이터만 모아져 있는 매우 단순한 1차원 배열이다. 즉, 기본적인 리스트보다도 더 한정된 개념이다. 그럼에도 불구하고 단순한 1차원 배열이 데이터사이언스에 활용될 수 있는 이유는 Pandas에서 Series 개념으로 확장하였기 때문이다.
1. Series 란?
같은 타입의 데이터만 나열되어 있는 배열에 Pandas는 레이블과 인덱스를 추가하였다. 그리고, Series들을 모아 DataFrame을 구성하며 Dictionary의 Key/Value 개념까지 추가가 되고, 다양한 통계 함수들이 포함되며 단순한 1차원 배열이 강력한 객체로 확장된 것이다.
- Series 생성
Series를 생성할때 생성자에 리스트, 딕셔너리, 넘파이 등 다양한 형태를 넣을 수 있다. 주의! Series는 넣는 순서를 인덱스로 기억한다. 넣는 순서가 중요함을 기억하자
import pandas as pd
import numpy as np
a = np.array([100, 200, 300, 400, 500])
a = ['a', 'b', 'c', 'd', 'e']
a = {'a': 100, 'b': 200, 'c': 300, 'd': 400, 'e': 500}
a = (100, 200, 300, 400, 500)
series = pd.Series(a)
print(series)
-----------
0 100
1 200
2 300
3 400
4 500
dtype: int64
특히, Dicionary를 넣으면 key 값이 자동으로 인덱스로 활용된다.
a 100
b 200
c 300
d 400
e 500
dtype: int64
참고로, Series의 기본 생성자 파라미터들은 다음과 같다. 위에서 넣었던 값들은 data=... 으로 들어간 것이다.
Parameters
----------
data : array-like, Iterable, dict, or scalar value
Contains data stored in Series. If data is a dict, argument order is
maintained.
index : array-like or Index (1d)
Values must be hashable and have the same length as `data`.
Non-unique index values are allowed. Will default to
RangeIndex (0, 1, 2, ..., n) if not provided. If data is dict-like
and index is None, then the keys in the data are used as the index. If the
index is not None, the resulting Series is reindexed with the index values.
dtype : str, numpy.dtype, or ExtensionDtype, optional
Data type for the output Series. If not specified, this will be
inferred from `data`.
See the :ref:`user guide <basics.dtypes>` for more usages.
name : str, optional
The name to give to the Series.
copy : bool, default False
Copy input data. Only affects Series or 1d ndarray input. See examples.
- Series 이름
생성자로도 할 수 있고 이름 파라미터에 바로 넣을 수도 있다.
a = np.array([100, 200, 300, 400, 500])
s = pd.Series(a, name='numbers')
# s.name = 'numbers'
-----------
0 100
1 200
2 300
3 400
4 500
Name: numbers, dtype: int64
- Series 인덱스
Series는 아무것도 지정하지 않으면 기본적으로 숫자 인덱스를 지정한다. 옵션이 아니라 무조건 생성되는 것이다. 단, 숫자 인덱스는 레이블을 달아줄 수도 있고, 다른 Series를 인덱스로 대체할 수도 있도록 유연하게 활용이 가능하도록 하고 있다.
s.index.name = 'no'
예를들어, 요일마다 신는 신발 브랜드를 정리한 Series를 만들 수 있다.
shoes = ['Nike', 'Adidas', 'New Balance', 'Puma', 'Reebok']
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
series = pd.Series(shoes, index=days, name='shoes')
------------------
Mon Nike
Tue Adidas
Wed New Balance
Thu Puma
Fri Reebok
Name: shoes, dtype: object
Missing data 표현하기
Series는 데이터와 함께 결측값인 missing data도 함께 포함할 수 있다. 나중에 데이터 분석할때에 현실의 데이터에서는 missing data가 많기 때문에 이러한 값들을 함께 다루는 것에 익숙해져야 한다.
temperatures = [29, 31, 33, 28, np.nan, 32]
s = pd.Series(temperatures, name='temperature')
-------------
0 29.0
1 31.0
2 33.0
3 28.0
4 NaN
5 32.0
Name: temperature, dtype: float6
2. 파이썬 객체에서 Pandas Series 생성하기
아래는 모두 같은 결과를 갖는다.
# Dictionary
clothes_sizes = {'Alice': 'small', 'Bob': 'large', 'Charlie': 'medium'}
s = pd.Series(clothes_sizes, name='size')
print(s)
# List
clothes_sizes = ['small', 'large', 'medium']
s = pd.Series(clothes_sizes, index=['Alice', 'Bob', 'Charlie'], name='size')
print(s)
# Tuple
clothes_sizes = ('small', 'large', 'medium')
s = pd.Series(clothes_sizes, index=['Alice', 'Bob', 'Charlie'], name='size')
print(s)
---------------
Alice small
Bob large
Charlie medium
Name: size, dtype: object
당연히 Numpy 객체도 자유롭게 가능하다.
# NumPy array
numbers = np.random.randint(1, 100, 5)
s = pd.Series(numbers, index=['a', 'b', 'c', 'd', 'e'], name='numbers')
print(s)
--------------------
a 89
b 7
c 3
d 75
e 36
Name: numbers, dtype: int32
3. Series 속성
재미있는 것은 Pandas는 값을 Numpy로 저장한다는 것이다. 즉, Pandas도 빠른 연산을 위해 Numpy를 사용하고 있는 일종의 Numpy Wrapper 인 것이다. Pandas 자체 객체는 Index 등이 있는데 type으로 서로 구분되어서 볼 수 있다.
calrories = {'Cereal': 120, 'Bacon': 250, 'Eggs': 160, 'Fruit': 80, 'Sausage': 300}
series = pd.Series(calrories, name='calories')
print(type(series.index))
<class 'pandas.core.indexes.base.Index'>
print(type(series.values))
<class 'numpy.ndarray'>
참고로, 숫자값이 아니어도 numpy로 저장된다. 즉, 모~~~든 값은 numpy로 저장되어 numpy 연산을 한다고 보면 된다.
a = ['Nike', 'Adidas', 'New Balance', 'Puma', 'Reebok']
s = pd.Series(a, name='shoes')
print(type(s.values))
<class 'numpy.ndarray'>
dtype, size, shape
calrories = {'Cereal': 120, 'Bacon': 250, 'Eggs': 160, 'Fruit': 80, 'Sausage': 300}
series = pd.Series(calrories, name='calories')
print(series.dtype)
print(series.size)
print(series.shape)
-------------
int64
5
(5,)
기타, 유용한 속성
Series에 중복 항목이 있는지 is_unique으로 체크할 수 있고, 값들이 자신의 이전 값보다 큰지 체크하는 is_monotonic 이 있다.
calrories = {'Cereal': 120, 'Bacon': 250, 'Eggs': 350, 'Fruit': 360, 'Sausage': 400}
series = pd.Series(calrories, name='calories')
print(series.is_monotonic_increasing)
True
print(series.is_monotonic_decreasing)
False
4. 편리한 연산
여러 계산은 아래의 코드를 참조하면 된다. 특히, count는 nan은 제외하고 세기 때문에 유용하다.
a = [1, 2, 3, np.nan, 4, 5]
series = pd.Series(a)
print(series.size)
6
print(series.count())
5
a = [1, 2, 3, np.nan, 4, 5]
series = pd.Series(a)
print(f'count() = {series.count()}')
print(f'min() = {series.min()}')
print(f'max() = {series.max()}')
print(f'median() = {series.median()}')
print(f'mean() = {series.mean()}')
print(f'std() = {series.std()}')
print(f'var() = {series.var()}')
print(f'product() = {series.product()}')
print(f'cumsum() = \n{series.cumsum()}')
----------------------------------
count() = 5
min() = 1
max() = 5
median() = 3.0
mean() = 3.0
std() = 1.5811388300841898
var() = 2.5
product() = 120
cumsum() =
0 1
1 3
2 6
3 10
4 15
dtype: int64
- 브로드캐스팅 연산: 라디오방송이 모든 청취자에게 동일하게 퍼지는 것처럼 한번의 연산 기호를 모든 행에 동일하게 파생시킨다는 것을 의미한다.
a = [1, 2, 3, 4, 5]
series = pd.Series(a)
series = series+10
print(series)
------------------
0 11
1 12
2 13
3 14
4 15
dtype: int64
series = series*10
print(series)
------------------
0 110
1 120
2 130
3 140
4 150
dtype: int64
- 시리즈 브로드캐스팅: 시리즈도 간단하게 서로 브로드캐스팅 연산이 가능하다
a = [1, 2, 3, 4, 5]
b = [10, 20, 30, 40, 50]
series1 = pd.Series(a)
series2 = pd.Series(b)
sum = series1 + series2
print(sum)
------------------------
0 11
1 22
2 33
3 44
4 55
dtype: int64
- 리스트 및 딕셔너리 만들기
calrories = {'Cereal': 100, 'Bacon': 200, 'Eggs': 300, 'Fruit': 400, 'Sausage': 500}
series = pd.Series(calrories, name='calories')
print(f'list: {list(series)}')
print(f'to_list(): {series.to_list()}')
print(f'dict: {dict(series)}')
print(f'to_dict: {series.to_dict()}')
----------------------------------
list: [100, 200, 300, 400, 500]
to_list(): [100, 200, 300, 400, 500]
dict: {'Cereal': 100, 'Bacon': 200, 'Eggs': 300, 'Fruit': 400, 'Sausage': 500}
to_dict: {'Cereal': 100, 'Bacon': 200, 'Eggs': 300, 'Fruit': 400, 'Sausage': 500}
댓글