티스토리 뷰
구조적 데이터 표시와 처리에 강한 pandas
우리는 지금 데이터가 넘쳐나는 데이터 시대에 살고 있다. 데이터를 얼마나 잘 처리하느냐가 개인적으로나 업무적으로 중요해졌다. 파이썬을 이용하면 다량의 데이터를 손쉽게 처리할 수 있다. 파이썬에서 데이터 분석과 처리를 쉽게 할 수 있게 도와주는 것이 바로 pandas 라이브러리이다. pandas는 Numpy를 기반으로 만들어졌지만 좀 더 복잡한 데이터 분석에 특화돼 있다. Numpy가 같은 데이터 타입의 배열만 처리할 수 있는 데 반해 pandas는 데이터 타입이 다양하게 섞여 있을 때도 처리할 수 있다. pandas에는 아주 많은 기능이 있다. 따라서 여기서는 NumPy와 마찬가지로 pandas의 기본적인 활용법 위주로 살펴보겠다. 좀 더 자세한 정보가 필요하다면 pandas 홈페이지(http://pandas.pydata.org)를 방문하길 바란다.
구조적 데이터 생산하기
pandas를 이용해 데이터를 생성하는 방법을 살펴보겠다. pandas를 이용하려면 따로 설치해야 하지만 NumPy처럼 pandas도 아나콘다를 설치할 때 이미 설치됐으므로 따로 설치할 필요가 없다.
①series를 활용한 데이터 생성
앞에서 살펴본 Numpy처럼 pandas도 사용하려면 먼저 다음과 같이 pandas를 불러와야 한다.
Numpy처럼 pandas도 보통 다음과 같이 'import ~ as ~ ' 형식으로 불러온다.
In: import pandas as pd
위와 같이 pandas를 불러오면 이제 pandas를 이용할 때 pandas 대신 pd를 이용할 수 있다.
pandas에서 데이터를 생성하는 가장 기본적인 방법은 Series()를 이용하는 것이다. series()를 이용하면 series 형식의 구조적 데이터(라벨을 갖는 1차원 데이터)를 생성할 수 있다.
다음은 series()를 이용해 series 형식의 데이터를 생성하는 방법이다.
s=pd.Series(seq_data) |
Series()의 인자로는 시퀀스 데이터(seq_data)가 들어간다. 시퀀스 데이터(seq_data)로는 리스트와 튜플 타입의 데이터를 모두 사용할 수 있지만 주로 리스트 데이터를 이용한다. 위와 같은 형식으로 지정하면 인자로 넣은 시퀀스 데이터(seq_data)에 순서를 표시하는 라벨이 자동으로 부여된다. Series 데이터에서는 세로출 라벨을 index라고 하고, 입력한 시퀀스 데이터를 values라고 한다.
다음의 예를 살펴보겠다.
In: s1=pd.Series([10, 20, 30, 40, 50]) Out: 0 10
s1
1 20
2 30
3 40
4 50
dtype: int64
위의 결과에서 볼 수 있듯이 Series 데이터를 출력하면 데이터 앞에 index가 함께 표시된다. 이 index는 Series 데이터 생성 시 자동으로 만들어진 것으로 데이터를 처리할 때 이용하게 될 것이다.
Series 데이터는 index와 values를 분리해서 가져올 수 있다. Series 데이터를 s라고 할 때 index는 's.index'로 values는 's.values'로 가져올 수 있다. 다음은 앞에서 만든 Series 데이터에서 index를 가져온 예이다.
In:
|
s1.index print(s1.index) |
Out: |
RangeIndex(start=0, stop=5, step=1) |
출력 결과에서 RangeIndex는 index를 범위로 표시했음을 의미한다. index의 범위는 'start ~ stop-1'이며 간격은 step만큼씩 증가한다.
다음은 Series 데이터에서 values를 가져온 예이다.
In: s1.values Out: array([10, 20, 30, 40, 50], dtype=int64)
출력된 결과가 NumPy의 배열과 형식이 같다.
NumPy의 경우 배열의 모든 원소가 데이터 타입이 같아야 했지만 pandas의 경우에는 원소의 데이터 타입이 달라도 된다. 따라서 Series( )로 데이터를 생성할 때 문자와 숫자가 혼합된 리스트를 인자로 이용할 수 있다.
다음 예제를 보자.
In:
|
s2 = pd.Series(['a', 'b', 'c', 1, 2, 3]) s2 |
Out:
|
0 a |
변수 s2에는 문자열과 숫자가 혼합돼 있다.
데이터가 없으면 NumPy를 임포트한 후에 np.nan으로 데이터가 없다고 표시할 수도 있다. 다음은 np.nan를 이용해서 Series 데이터에 특정 원소가 없음을 표시한 예이다.
In: import numpy as np s3 = pd.Series([np.nan, 10, 30]) Out: 0 NaN
s3
1 10.0
2 30.0
dtype: float64
출력 결과에서 NaN은 데이터가 없다는 것을 의미합니다. 즉, 데이터를 위한 자리(index)는 있지만 실제 값은 없다.
Series 데이터를 생성할 때 다음과 같이 인자로 index를 추가할 수 있다.
s = pd.Series(seq_data, index =index_seq) |
위와 같이 인자로 index를 명시적으로 입력하면 Series 변수(s)의 index에는 자동 생성되는 index 대신 index_seq가 들어가게 된다. index_seeq도 리스트와 튜플 타입의 데이터를 모두 사용할 수 있지만 주로 리스트 데이터를 이용한다. 주의할 점은 seq_data의 항목 개수와 index_seq의 항목 개수는 같아야 한다는 것이다. 그렇지 않으면 오류가 발생한다.
다음 예제는 어느 가게의 날짜별 판매량을 pandas의 Series 형식으로 입력한 것이다. 하루는 판매량 데이터가 없어서 np.nan를 입력했다.
In: index_date = ['2018-10-07', '2018-10-08', '2018-10-09', '2018-10-10'] Out:
s4 = pd.Series([200, 195, np.nan, 205], index =index_date)
s4
2018-10-07 200.0
2018-10-08 195.0
2018-10-09 NaN
2018-10-10 205.0
dtype: float64
출력된 결과에서 index에는 입력 인자 index의 리스트 데이터가 지정됐음을 확인할 수 있다.
앞에서는 리스트 형식의 데이터와 index를 따로 입력했지만 파이썬의 딕셔너리를 이용하면 데이터와 index를 함께 입력할 수 있다.
s = pd.Series(dict_data) |
위와 같이 입력 인자로 딕셔너리 데이터를 입력하면 딕셔너리 데이터의 키(keys)와 값(values)이 각각 Series 데이터의 index와 values로 들어간다.
다음 예제로 보자.
In: s5 = pd.Series({'국어': 100, '영어': 95, '수학': 90}) Out:
s5
국어 100
영어 95
수학 90
dtype: int64
위에서 Series 데이터의 index는 '국어', '영어', '수학'으로 지정됐고 values는 100, 90, 95로 지정된 것을 확인할 수 있다.
②날짜 자동 생성: date_range
앞에서는 index에 날짜를 입력할 때 'index = ['2018-10-07','2018-10-08','2018-10-09','2018-10-10']'처럼 문자열을 하나씩 입력했다. 입력해야 하는 날짜가 몇 개 없다면 일일이 입력해도 괜찮지만 날짜의 개수가 증가하면 날자를 입력하기가 쉽지 않다. 이때 사용할 수 있는 것이 pandas에서 제공하는 date_range()이다. 몇 가지 설정만 하면 원하는 날짜를 자동으로 생성하므로 날자 데이터를 입력 할 때 편리하다. 다음은 pandas의 date_range()를 이용하는 방법이다.
pd.date_range(start=None, end=None, periods=None, freq='d') |
여기서 start는 시작 날짜, end는 끝 날짜, periods는 날짜 데이터 생성 기간, freq는 날짜 데이터 생성 주기를 나타낸다. start는 반드시 있어야 하며 end나 periods는 둘 중 하나만 있어도 된다. 또한 freq를 입력하지 않으면 'D' 옵션이 설정돼 달력 날짜 기준으로 하루식 증가한다. date_range()의 자세한 사용법은 예를 통해 설명하겠다.
우선 시작 날짜와 끝 날짜를 지정해 날짜 데이터를 생성해 보겠다.
In:
|
import pandas as pd
pd.date_range(start='2019-01-01',end='2019-01-07') |
Out:
|
DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04', |
실행 결과에서 시작 날짜(start)에서 끝 날짜(end)까지 하루씩 증가한 날짜 데이터가 생성된 것을 볼 수 있다.
날짜를 입력할 때 yyyy-mm-dd 형식 외에도 yyyy/mm/dd, yyyy.mm.dd, mm-dd-yyyy, mm/dd/yyyy, mm.dd.yyyy 같은 형식으로도 입력할 수 있다. 그러나 생성된 날짜 데이터 형식은 모두 yyyy-mm-dd 이다.
다음 예제를 보자.
In: |
pd.date_range(start='2019/01/01',end='2019.01.07') |
Out:
|
DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04', '2019-01-05', '2019-01-06', '2019-01-07'], dtype='datetime64[ns]', freq='D') |
In: |
pd.date_range(start='01-01-2019',end='01/07/2019') |
Out:
|
DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04', |
In: pd.date_range(start='2019-01-01',end='01.07.2019') Out: DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04',
'2019-01-05', '2019-01-06', '2019-01-07'],
dtype='datetime64[ns]', freq='D')
보다시피 다양한 형식으로 연도, 월, 일을 입력할 수 있지만 생성된 날짜 데이터의 형식은 모두 yyyy-mm-dd가 된다.
지금가지 시작 날짜와 끝 날짜를 지정해 날짜 데이터를 생성했는데 끝 날자를 지정하지 않고 periods를 입력해서 날짜를 생성할 수 있다.
다음 예제를 보자.
In: pd.date_range(start='2019-01-01', periods = 7) Out: DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04',
'2019-01-05', '2019-01-06', '2019-01-07'],
dtype='datetime64[ns]', freq='D')
코드에서는 시작 날짜(start)를 '2019-01-01'로, 날짜 생성 기간(periods)을 7로 입력했다. 따라서 시작 날짜로부터 7개의 날짜가 하루씩 증가해 생성됐다. 앞에서도 설명했지만 freq 옵션을 쓰지 않으면 기본적으로 'D'가 지정되어 하루씩 날짜가 증가한다.
앞에서 설명한 freq 옵션 중에 몇 가지 예를 살펴보겠다. 먼저 2일씩 증가하는 날짜를 생성해 보겠다.
In: |
pd.date_range(start='2019-01-01', periods = 4, freq ='2D') |
Out: |
DatetimeIndex(['2019-01-01', '2019-01-03', '2019-01-05', '2019-01-07'], dtype='datetime64[ns]', freq='2D') |
다음으로 달력 기준으로 일주일씩 증가하는 날짜를 생성해 보겠다.
In: pd.date_range(start='2019-01-01', periods = 4, freq ='W') Out: DatetimeIndex(['2019-01-06', '2019-01-13', '2019-01-20', '2019-01-27'], dtype='datetime64[ns]', freq='W-SUN')
다음은 업무일 기준 2개월 월말 주기로 12개의 날짜를 생성한 예이다.
In: pd.date_range(start='2019-01-01', periods = 12, freq ='2BM') Out:
DatetimeIndex(['2019-01-31', '2019-03-29', '2019-05-31', '2019-07-31',
'2019-09-30', '2019-11-29', '2020-01-31', '2020-03-31',
'2020-05-29', '2020-07-31', '2020-09-30', '2020-11-30'],
dtype='datetime64[ns]', freq='2BM')
다음은 분기 시작일을 기준으로 4개의 날짜를 생성한 예이다.
In: pd.date_range(start='2019-01-01', periods = 4, freq ='QS') Out:
DatetimeIndex(['2019-01-01', '2019-04-01', '2019-07-01', '2019-10-01'], dtype='datetime64[ns]', freq='QS-JAN')
다음은 연도의 첫날을 기준으로 1년 주기로 3개의 날짜를 생성한 예이다.
In: |
pd.date_range(start='2019-01-01', periods = 3, freq ='AS') |
Out: |
DatetimeIndex(['2019-01-01', '2020-01-01', '2021-01-01'], dtype='datetime64[ns]', freq='AS-JAN') |
앞에서는 date_range( )를 이용해 날짜를 생성하는 방법을 살펴봤다. 이어서 date_range()를 이용해 날짜뿐만 아니라 시간을 생성하는 방법을 살펴보겠다.
다음은 1시간 주기로 10개의 시간을 생성한 예이다.
In: pd.date_range(start='2019-01-01 08:00', periods = 10, freq ='H') Out: DatetimeIndex(['2019-01-01 08:00:00', '2019-01-01 09:00:00',
'2019-01-01 10:00:00', '2019-01-01 11:00:00',
'2019-01-01 12:00:00', '2019-01-01 13:00:00',
'2019-01-01 14:00:00', '2019-01-01 15:00:00',
'2019-01-01 16:00:00', '2019-01-01 17:00:00'],
dtype='datetime64[ns]', freq='H')
다음은 업무 시간을 기준으로 1시간 주기로 10개의 시간을 생성하는 예이다.
In: pd.date_range(start='2019-01-01 08:00', periods = 10, freq ='BH') Out: DatetimeIndex(['2019-01-01 09:00:00', '2019-01-01 10:00:00',
'2019-01-01 11:00:00', '2019-01-01 12:00:00',
'2019-01-01 13:00:00', '2019-01-01 14:00:00',
'2019-01-01 15:00:00', '2019-01-01 16:00:00',
'2019-01-02 09:00:00', '2019-01-02 10:00:00'],
dtype='datetime64[ns]', freq='BH')
업무 시간은 9시부터 17시까지이므로 start 시간을 9시 이전으로 설정해도 9시부터 표시된다. 또한 업무 시간은 17시까지이므로 17시 이후의 시간은 표시되지 않는다. 더 표시할 시간이 있다면 다음 날로 넘어간다.
다음은 30분 단위로 4개의 시간을 생성한 예이다.
In: pd.date_range(start='2019-01-01 10:00', periods = 4, freq ='30min') Out: DatetimeIndex(['2019-01-01 10:00:00', '2019-01-01 10:30:00',
'2019-01-01 11:00:00', '2019-01-01 11:30:00'],
dtype='datetime64[ns]', freq='30T')
출력 결과에서 입력은 freq='30min'로 했는데 출력은 freq='30T'인 것을 볼 수 있다. 위의 표에서처럼 분 단위를 입력하려면 'min'이나 'T'를 모두 쓸 수 있지만 출력은 'T'로 된다.
다음과 같이 분 단위를 'T'로 입력해도 결과는 같다.
In: pd.date_range(start='2019-01-01 10:00', periods = 4, freq ='30T') Out:
DatetimeIndex(['2019-01-01 10:00:00', '2019-01-01 10:30:00',
'2019-01-01 11:00:00', '2019-01-01 11:30:00'],
dtype='datetime64[ns]', freq='30T')
마지막으로 다음은 10초 단위로 증가하면서 4개의 시간을 생성한 예이다.
In: pd.date_range(start='2019-01-01 10:00:00', periods = 4, freq ='10S') Out: DatetimeIndex(['2019-01-01 10:00:00', '2019-01-01 10:00:10',
'2019-01-01 10:00:20', '2019-01-01 10:00:30'],
dtype='datetime64[ns]', freq='10S')
다음은 date_range()를 이용해 Series의 index를 지정한 예이다. 특정 날짜부터 5일간의 판매량을 표시하려면 다음과 같이 입력하면 된다.
In: index_date = pd.date_range(start='2019-01-01', periods = 5, freq ='D') Out:
pd.Series([51, 62, 55, 49, 58], index = index_date)
2019-01-01 51
2019-01-02 62
2019-01-03 55
2019-01-04 49
2019-01-05 58
Freq: D, dtype: int64
지금까지 pandas의 date_range()를 이용해 날짜와 시간을 자동으로 생성하는 방법을 알아봤다. 이를 pandas 데이터를 생성할 때 이용한다면 날짜를 일일이 입력하지 않아도 돼서 편리하다.
③DataFrame을 활용한 데이터 생성
앞에서 Series를 이용해 1차원 데이터를 생성했다. pandas에서는 표(Table)와 같은 2차원 데이터 처리를 위해 DataFrame을 제공했다. DataFrame은 이름에서 알 수 있듯이 자료(Data)를 담는 틀(Frame)이다. DataFrame을 이용하면 라벨이 있는 2차원 데이터를 생성하고 처리할 수 있다.
다음은 DataFrame을 이용해 데이터를 생성하는 방법이다.
df = pd.DataFrame(data [, index = index_data, columns = columns_data]) |
DataFrame()의 인자인 data에는 리스트와 형태가 유사한 데이터 타입은 모두 사용할 수 있다. 즉, 리스트와 딕셔너리 타입의 데이터, NumPy의 배열 데이터, Series나 DataFrame 타입의 데이터를 입력할 수 있다. DataFrame의 세로축 라벨을 index라고 하고 가로축 라벨을 columns라고 한다. 또한 index와 columns를 제외한 부분을 values라고 한다. 이 values가 관심 있는 데이터이다. index와 columns에는 1차원 배열과 유사한 데이터 타입의 데이터를 입력할 수 있다. 한 가지 주의 할 점은 DataFrame의 data의 행 개수와 index 요소의 개수, data 열의 개수와 columns 요소의 개수가 일치해야 한다는 것이다. index와 columns는 선택 사항이므로 입력하지 않을 수 있는데 그러면 index와 columns에는 자동으로 0부터 숫자가 생성되어 채워진다.
DataFrame 데이터를 생성하는 예를 살펴보겠다. 우선 리스트를 이용해 DataFrame의 데이터를 생성하는 예이다.
In:
|
import pandas as pd pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | ||||||||||||||||
Out:
|
|
출력 결과에서 입력한 데이터는 보기 좋게 표 형식으로 출력됐습니다. values 부분에는 입력한 data가 순서대로 입력돼 있고 가장 좌측의 열과 가장 윗줄의 행에는 각각 숫자가 자동으로 생성되어 index와 columns를 구성했다. 이처럼 명시적으로 index와 columns를 입력하지 않더라도 자동으로 index와 columns가 생성된다.
다음은 NumPy의 배열 데이터를 입력해 생성한 DataFrame 데이터의 예이다.
In:
|
import numpy as np
data_list = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]]) | |||||||||||||||
Out:
|
|
이번에는 data 뿐만 아니라 index와 columns도 지정해 보겠다.
In:
|
import numpy as np
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]) index_date = pd.date_range('2019-09-01',periods=4) columns_list = ['A', 'B', 'C'] pd.DataFrame(data, index=index_date, columns=columns_list) | ||||||||||||||||||||
Out:
|
|
출력 결과에서 index와 columns에 지정한 값이 들어간 것을 볼 수 있다. index에는 date_range( )로 생성한 날짜를, columns에는 리스트 데이터(['A', 'B', 'C'])를 입력했다.
다음은 딕셔너리 타입으로 2차원 데이터를 입력한 예이다.
In:
|
table_data = {'연도': [2015, 2016, 2017, 2017, 2017], '지사': ['한국', '한국', '미국', '한국', '미국'], '고객 수': [200, 250, 450, 300, 500]} table_data |
Out:
|
{'연도': [2015, 2016, 2017, 2017, 2017], |
이제 위에서 만든 딕셔너리 데이터를 이용해 DataFrame 형식의 데이터를 생성하겠다.
In: |
pd.DataFrame(table_data) | ||||||||||||||||||||||||
Out:
|
|
출력 결과를 보면 index는 자동으로 생성됐고 딕셔너리 데이터의 키(keys)는 DataFrame에서 columns로 지정돼 표에서 각 열의 제목(고객 수, 연도, 지사)처럼 들어간 것을 볼 수 있다. 자세히 살펴보면 DataFrame의 데이터는 입력한 데이터의 순서대로 출력되지 않는다. 이것은 딕셔너리 데이터가 키(keys)에 따라서 자동으로 정렬됐기 때문이다. 한글은 가나다순으로 영어는 알파벳 순으로 정렬된다. 데이터의 정렬 순서는 다음과 같이 'columns = columns_list'를 이용해 키의 순서를 지정할 수 있다.
In:
|
df = pd.DataFrame(table_data, columns=['연도', '지사', '고객 수']) | ||||||||||||||||||||||||
Out:
|
|
이제 columns에 지정한 순서대로 DataFrame의 데이터가 정렬됐다.
DataFrame 데이터에서 index, columns, values는 각각 DataFrame_data.index, DataFrame_data.columns, DataFrame_data.values로 확인할 수 있다. 다음은 DataFrame 데이터에서 index, columns, values를 각각 구한 예이다.
In: |
df.index |
Out: |
RangeIndex(start=0, stop=5, step=1) |
In: |
df.columns |
Out: |
Index(['연도', '지사', '고객 수'], dtype='object') |
In: |
df.values |
Out:
|
array([[2015, '한국', 200], |
지금까지는 pandas의 Series와 DataFrame을 이용해 데이터를 생성하는 방법을 살펴봤다. 다음으로 생성된 데이터를 어떻게 이용하는지 알아보겠다.
출처: 파이썬 철저 입문
'beginner > 파이썬 분석' 카테고리의 다른 글
데이터 시각화 (0) | 2019.01.17 |
---|---|
데이터 분석을 위한 패키지-pandas ② (0) | 2019.01.17 |
모듈 (0) | 2019.01.15 |
문자열과 텍스트 파일 데이터 다루기 ② (0) | 2019.01.15 |
문자열과 텍스트 파일 데이터 다루기 ① (0) | 2019.01.14 |