기술스택을 쌓아보자/Python

pandas 번역: 10분만에 판다스 보기 (pandas User guide 번역/pandas 기초 입문/Pandas 간단 요약)

소리331 2020. 9. 6. 20:49
반응형

주니어로서 어떤 능력을 갖추는게 좋을까요?

라고 과장님께 질문을 했더니

pandas cookbook을 빌려주시며, 나즈막하게 '이 책을 뜯어드시면 됩니다..'라고 말씀하시고 떠났다.

그래서 pandas User guide를 번역해보기로 했다. 덩달아 영어 실력도 좋아질 것 같다. 오늘이 그 시작!

 

10 minutes to pandas

해당 문서는 새로운 유저에게 맞춰진 짧은 pandas 소개입니다. 좀 더 복잡한 부분은 cookbook에서 확인해볼 수 있습니다. 보통 아래와 같이 import 하여 사용합니다.

import numpy as np
import pandas as pd

Object creation(객체 생성)

자세한 내용은 Data Structure Intro section 을 참고하세요.

list를 통하여, 인덱스의 초기 설정이 정수인 Series 를 만들 수 있습니다.

seires = pd.Series([1, 3, 5, np.nan, 6, 8])
#result
0    1.0
1    3.0
2    5.0
3    NaN
4    6.0
5    8.0
dtype: float64

Numpy array(2D array)를 통해 인덱스가 날짜거나, 열의 이름이 설정되어 있는 DataFrame을 만들 수 있습니다.

In [5]: dates = pd.date_range('20130101', periods=6)

In [6]: dates
Out[6]: 
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
               '2013-01-05', '2013-01-06'],
              dtype='datetime64[ns]', freq='D')

In [7]: df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))

In [8]: df
Out[8]: 
                   A         B         C         D
2013-01-01  0.469112 -0.282863 -1.509059 -1.135632
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804
2013-01-04  0.721555 -0.706771 -1.039575  0.271860
2013-01-05 -0.424972  0.567020  0.276232 -1.087401
2013-01-06 -0.673690  0.113648 -1.478427  0.524988

Series 처럼 표현될 수 있는 dict 객체를 통해 DataFrame을 만들 수 있습니다.( 각 key에 있는 value들이 Series를 만들 수 있는 형태라는 점에 집중하시면 좋을 것 같습니다. )

In [9]: df2 = pd.DataFrame({'A': 1.,
   ...:                     'B': pd.Timestamp('20130102'),
   ...:                     'C': pd.Series(1, index=list(range(4)), dtype='float32'),
   ...:                     'D': np.array([3] * 4, dtype='int32'),
   ...:                     'E': pd.Categorical(["test", "train", "test", "train"]),
   ...:                     'F': 'foo'})
   ...: 

In [10]: df2
Out[10]: 
     A          B    C  D      E    F
0  1.0 2013-01-02  1.0  3   test  foo
1  1.0 2013-01-02  1.0  3  train  foo
2  1.0 2013-01-02  1.0  3   test  foo
3  1.0 2013-01-02  1.0  3  train  foo

DataFrame의 각 열도 각자 다른 dtype(데이터타입)을 가지고 있습니다.

In [11]: df2.dtypes
Out[11]: 
A           float64
B    datetime64[ns]
C           float32
D             int32
E          category
F            object
dtype: object

만일 IPython을 사용하고 있다면, Tab 키를 통해 attribute의 이름을 작성하는 것 뿐만 아니라 열의 이름도 자동완성으로 작성하는 것이 가능합니다. 

In [12]: df2.<TAB>  # noqa: E225, E999
df2.A                  df2.bool
df2.abs                df2.boxplot
df2.add                df2.C
df2.add_prefix         df2.clip
df2.add_suffix         df2.columns
df2.align              df2.copy
df2.all                df2.count
df2.any                df2.combine
df2.append             df2.D
df2.apply              df2.describe
df2.applymap           df2.diff
df2.B                  df2.duplicated

위의 예시에서 보신 것처럼, 열의 이름에 해당되는 A,B,C,D가 포함되어 있는 것을 볼 수 있습니다. 예시로 나온 df2에는 E와 F도 있지만, 글의 간결성을 위해 d이후의 내용은 삭제했습니다.

Viewing data

Basics section. 을 참고하세요

데이터프레임의 상단과 하단을 보는 방법입니다!

In [13]: df.head()
Out[13]: 
                   A         B         C         D
2013-01-01  0.469112 -0.282863 -1.509059 -1.135632
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804
2013-01-04  0.721555 -0.706771 -1.039575  0.271860
2013-01-05 -0.424972  0.567020  0.276232 -1.087401

In [14]: df.tail(3)
Out[14]: 
                   A         B         C         D
2013-01-04  0.721555 -0.706771 -1.039575  0.271860
2013-01-05 -0.424972  0.567020  0.276232 -1.087401
2013-01-06 -0.673690  0.113648 -1.478427  0.524988

인덱스와 열을 보는 방법입니다.

In [15]: df.index
Out[15]: 
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
               '2013-01-05', '2013-01-06'],
              dtype='datetime64[ns]', freq='D')

In [16]: df.columns
Out[16]: Index(['A', 'B', 'C', 'D'], dtype='object')

DataFrame.to_numpy()는 가지고 있는 데이터를 numpy의 형태로 보여줍니다(array). Pandas와 Numpy에는 근본적인 차이가 하나 있습니다. pandas는 각 열마다 각기 다른 데이터 타입을 가질 수 있지만, numpy의 경우 전체 array 내의 데ㅣ터가 모두 동일한 데이터 타입을 가져야 합니다. 이렇기 때문에, pandas의 dataframe을 단순히 numpy형태로 변경하는거는 expensive operation(제가 이해하기로는 메모리가 많이 드는 연산)이 될 수 있습니다. DataFrame.to_numpy() 를 호출할 때 pandas 는 모든 데이터타입을 저장할 수 있는 numpy datatype을 찾으려고 할 것입니다. 결국에는 파이썬 객체의 모든 데이터타입을 casting하는 object 타입으로 귀결될 가능성이 높습니다. 

앞의 예시에서 나왔던 df의 경우, 모든 값이 float 형태이기 때문에, DataFrame.to_numpy()는 빠르고, 데이터를 복사할 필요도 없습니다.

In [17]: df.to_numpy()
Out[17]: 
array([[ 0.4691, -0.2829, -1.5091, -1.1356],
       [ 1.2121, -0.1732,  0.1192, -1.0442],
       [-0.8618, -2.1046, -0.4949,  1.0718],
       [ 0.7216, -0.7068, -1.0396,  0.2719],
       [-0.425 ,  0.567 ,  0.2762, -1.0874],
       [-0.6737,  0.1136, -1.4784,  0.525 ]])

그러나 각 열마다 데이터 타입이 달랐던 df2의 경우에는, DataFrame.to_numpy()는 상대적으로 비쌉니다.(expensive, 들어가는 작업이 많다, 무겁다라는 의미로 이해했습니다.)

In [18]: df2.to_numpy()
Out[18]: 
array([[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
       [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo'],
       [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
       [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo']],
      dtype=object)
주의!  DataFrame.to_numpy()는, output에 index와 columns을 포함하지 않습니다!

describe() 는 여러분의 데이터에 대해 간결한 통계 요약을 보여줍니다.

In [19]: df.describe()
Out[19]: 
              A         B         C         D
count  6.000000  6.000000  6.000000  6.000000
mean   0.073711 -0.431125 -0.687758 -0.233103
std    0.843157  0.922818  0.779887  0.973118
min   -0.861849 -2.104569 -1.509059 -1.135632
25%   -0.611510 -0.600794 -1.368714 -1.076610
50%    0.022070 -0.228039 -0.767252 -0.386188
75%    0.658444  0.041933 -0.034326  0.461706
max    1.212112  0.567020  0.276232  1.071804
Transposing your data:

아래는 여러분의 데이터를 역치하여 보여주는 방식입니다.(행과 열을 전환하여 보여줌, 역치행렬의 개념)

In [20]: df.T
Out[20]: 
   2013-01-01  2013-01-02  2013-01-03  2013-01-04  2013-01-05  2013-01-06
A    0.469112    1.212112   -0.861849    0.721555   -0.424972   -0.673690
B   -0.282863   -0.173215   -2.104569   -0.706771    0.567020    0.113648
C   -1.509059    0.119209   -0.494929   -1.039575    0.276232   -1.478427
D   -1.135632   -1.044236    1.071804    0.271860   -1.087401    0.524988

축에 따라 정렬하는 방식입니다.

In [21]: df.sort_index(axis=1, ascending=False)
Out[21]: 
                   D         C         B         A
2013-01-01 -1.135632 -1.509059 -0.282863  0.469112
2013-01-02 -1.044236  0.119209 -0.173215  1.212112
2013-01-03  1.071804 -0.494929 -2.104569 -0.861849
2013-01-04  0.271860 -1.039575 -0.706771  0.721555
2013-01-05 -1.087401  0.276232  0.567020 -0.424972
2013-01-06  0.524988 -1.478427  0.113648 -0.673690

값에 따라 정렬하는 방식입니다.

In [22]: df.sort_values(by='B') # B는 기준이 되는 열의 이름
Out[22]: 
                   A         B         C         D
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804
2013-01-04  0.721555 -0.706771 -1.039575  0.271860
2013-01-01  0.469112 -0.282863 -1.509059 -1.135632
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-06 -0.673690  0.113648 -1.478427  0.524988
2013-01-05 -0.424972  0.567020  0.276232 -1.087401

Selection(Indexing, 인덱싱)

Python과 numpy가 직관적이고 interactive work에 단순한 방법을 제공하지만, pandas에서는 실제 코딩 진행시 pandas 데이터에 최적화 된 .at, .iat, .loc .iloc. 메서드를 추천합니다.(0.23 버전 아래의 경우 .ix 메서드도 있습니다)

인덱싱과 관련된 부분은  Indexing and Selecting Data and MultiIndex / Advanced Indexing. 를 참고하세용

Getting

df.A는, 데이터 프레임 내의 하나의 열을 선택한 것이며, series(시리즈) 객체를 반환합니다:

In [23]: df['A']
Out[23]: 
2013-01-01    0.469112
2013-01-02    1.212112
2013-01-03   -0.861849
2013-01-04    0.721555
2013-01-05   -0.424972
2013-01-06   -0.673690
Freq: D, Name: A, dtype: float64

대괄호 [ ] 를 통해 행 또한 슬라이싱할 수 있습니다.

In [24]: df[0:3]
Out[24]: 
                   A         B         C         D
2013-01-01  0.469112 -0.282863 -1.509059 -1.135632
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804

In [25]: df['20130102':'20130104']
Out[25]: 
                   A         B         C         D
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804
2013-01-04  0.721555 -0.706771 -1.039575  0.271860

Selection by label

자세한 내용은  Selection by Label. 참조

라벨을 이용하여 횡단면으로 보고 싶은 경우:

In [26]: df.loc[dates[0]]
Out[26]: 
A    0.469112
B   -0.282863
C   -1.509059
D   -1.135632
Name: 2013-01-01 00:00:00, dtype: float64

축들을 교차로 하여 보고 싶을 경우:

In [27]: df.loc[:, ['A', 'B']]
Out[27]: 
                   A         B
2013-01-01  0.469112 -0.282863
2013-01-02  1.212112 -0.173215
2013-01-03 -0.861849 -2.104569
2013-01-04  0.721555 -0.706771
2013-01-05 -0.424972  0.567020
2013-01-06 -0.673690  0.113648

위의 예시에서 특정 행만 인덱싱하는것도 가능합니다

In [28]: df.loc['20130102':'20130104', ['A', 'B']]
Out[28]: 
                   A         B
2013-01-02  1.212112 -0.173215
2013-01-03 -0.861849 -2.104569
2013-01-04  0.721555 -0.706771

스칼라 값을 얻고 싶으시다면:

In [30]: df.loc[dates[0], 'A']
Out[30]: 0.4691122999071863

스칼라 값에 빠르게 접근하고 싶다면(이전과 동일하게):

In [31]: df.at[dates[0], 'A']
Out[31]: 0.4691122999071863

Selection by position(데이터의 위치)

자세한 내용은 Selection by Position

위치를 정수형으로 표현하여 접근하는 방법

In [32]: df.iloc[3]
Out[32]: 
A    0.721555
B   -0.706771
C   -1.039575
D    0.271860
Name: 2013-01-04 00:00:00, dtype: float64

numpy/python의 스타일처럼 정수(위치)의 리스트로 접근하는 법:

In [34]: df.iloc[[1, 2, 4], [0, 2]]
Out[34]: 
                   A         C
2013-01-02  1.212112  0.119209
2013-01-03 -0.861849 -0.494929
2013-01-05 -0.424972  0.276232

행만 단독으로 슬라이싱하기:

In [35]: df.iloc[1:3, :]
Out[35]: 
                   A         B         C         D
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804

열만 단독으로 슬라이싱하기:

In [36]: df.iloc[:, 1:3]
Out[36]: 
                   B         C
2013-01-01 -0.282863 -1.509059
2013-01-02 -0.173215  0.119209
2013-01-03 -2.104569 -0.494929
2013-01-04 -0.706771 -1.039575
2013-01-05  0.567020  0.276232
2013-01-06  0.113648 -1.478427

값(하나의 cell)만 단독으로 슬라이싱하기(스칼라값으로 접근):

In [37]: df.iloc[1, 1]
Out[37]: -0.17321464905330858

스칼라 값에 정수형 인덱스를 가지고 접근하는 빠른 방법: 

In [38]: df.iat[1, 1]
Out[38]: -0.17321464905330858

Boolean indexing

데이터를 선택하기 위해, 하나의 열의 값을 이용합니다.

# A 열에서 0보다 큰 값을 인덱싱하면, 해당 행이 출력됩니다

In [39]: df[df['A'] > 0]
Out[39]: 
                   A         B         C         D
2013-01-01  0.469112 -0.282863 -1.509059 -1.135632
2013-01-02  1.212112 -0.173215  0.119209 -1.044236
2013-01-04  0.721555 -0.706771 -1.039575  0.271860

해당 조건이 충족되는 값'만' 선택하기

In [40]: df[df > 0]
Out[40]: 
                   A         B         C         D
2013-01-01  0.469112       NaN       NaN       NaN
2013-01-02  1.212112       NaN  0.119209       NaN
2013-01-03       NaN       NaN       NaN  1.071804
2013-01-04  0.721555       NaN       NaN  0.271860
2013-01-05       NaN  0.567020  0.276232       NaN
2013-01-06       NaN  0.113648       NaN  0.524988

데이터프레임을 필터링하기 위해서는  isin() 메서드를 사용합니다.

In [41]: df2 = df.copy()

In [42]: df2['E'] = ['one', 'one', 'two', 'three', 'four', 'three']

In [43]: df2
Out[43]: 
                   A         B         C         D      E
2013-01-01  0.469112 -0.282863 -1.509059 -1.135632    one
2013-01-02  1.212112 -0.173215  0.119209 -1.044236    one
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804    two
2013-01-04  0.721555 -0.706771 -1.039575  0.271860  three
2013-01-05 -0.424972  0.567020  0.276232 -1.087401   four
2013-01-06 -0.673690  0.113648 -1.478427  0.524988  three

In [44]: df2[df2['E'].isin(['two', 'four'])]
Out[44]: 
                   A         B         C         D     E
2013-01-03 -0.861849 -2.104569 -0.494929  1.071804   two
2013-01-05 -0.424972  0.567020  0.276232 -1.087401  four

Setting

기존 데이터 프레임에 새로운 열을 기존 인덱스에 정렬되도록 추가하는 법 

In [45]: s1 = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('20130102', periods=6))

In [46]: s1
Out[46]: 
2013-01-02    1
2013-01-03    2
2013-01-04    3
2013-01-05    4
2013-01-06    5
2013-01-07    6
Freq: D, dtype: int64

열과 행의 이름(label)으로 한 칸의 값을 설정하는 법: 

In [48]: df.at[dates[0], 'A'] = 0

특정 위치의 값을 설정하는 법:

In [49]: df.iat[0, 1] = 0

 Numpy 배열을 할당하여 설정하는 법;

df.loc[:, 'D'] = np.array([5] * len(df))

이전 연산들의 결과를 모으면 다음과 같습니다!

In [51]: df
Out[51]: 
                   A         B         C  D    F
2013-01-01  0.000000  0.000000 -1.509059  5  NaN
2013-01-02  1.212112 -0.173215  0.119209  5  1.0
2013-01-03 -0.861849 -2.104569 -0.494929  5  2.0
2013-01-04  0.721555 -0.706771 -1.039575  5  3.0
2013-01-05 -0.424972  0.567020  0.276232  5  4.0
2013-01-06 -0.673690  0.113648 -1.478427  5  5.0

where 연산을 setting에 사용하는 경우

In [52]: df2 = df.copy()

In [53]: df2[df2 > 0] = -df2

In [54]: df2
Out[54]: 
                   A         B         C  D    F
2013-01-01  0.000000  0.000000 -1.509059 -5  NaN
2013-01-02 -1.212112 -0.173215 -0.119209 -5 -1.0
2013-01-03 -0.861849 -2.104569 -0.494929 -5 -2.0
2013-01-04 -0.721555 -0.706771 -1.039575 -5 -3.0
2013-01-05 -0.424972 -0.567020 -0.276232 -5 -4.0
2013-01-06 -0.673690 -0.113648 -1.478427 -5 -5.0

Missing data

pandas에서는 결측치를 np.nan을 이용하여 나타냅니다. 기본적으로 연산에 포함되지 않습니다. 자세한 내용은 Missing Data section 를 읽어보세요

reindex(데이터프레임의 인덱스를 다시 지정하는 것)는 특정 축의 인덱스를 바꾸고/더하고/삭제할 수 있도록 합니다. 데이터의 원본이 변하는 것이 아니라, 데이터의 복사본을 반환합니다.  

In [55]: df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ['E'])

In [56]: df1.loc[dates[0]:dates[1], 'E'] = 1

In [57]: df1
Out[57]: 
                   A         B         C  D    F    E
2013-01-01  0.000000  0.000000 -1.509059  5  NaN  1.0
2013-01-02  1.212112 -0.173215  0.119209  5  1.0  1.0
2013-01-03 -0.861849 -2.104569 -0.494929  5  2.0  NaN
2013-01-04  0.721555 -0.706771 -1.039575  5  3.0  NaN

결측치가 있는 행을 삭제하는 방법:

In [58]: df1.dropna(how='any')
Out[58]: 
                   A         B         C  D    F    E
2013-01-02  1.212112 -0.173215  0.119209  5  1.0  1.0

결측치를 채우는 방법:

In [59]: df1.fillna(value=5)
Out[59]: 
                   A         B         C  D    F    E
2013-01-01  0.000000  0.000000 -1.509059  5  5.0  1.0
2013-01-02  1.212112 -0.173215  0.119209  5  1.0  1.0
2013-01-03 -0.861849 -2.104569 -0.494929  5  2.0  5.0
2013-01-04  0.721555 -0.706771 -1.039575  5  3.0  5.0

np.nan인 곳의 위치를 True, 아닌 곳을 False로 출력하는 방법:

In [60]: pd.isna(df1)
Out[60]: 
                A      B      C      D      F      E
2013-01-01  False  False  False  False   True  False
2013-01-02  False  False  False  False  False  False
2013-01-03  False  False  False  False  False   True
2013-01-04  False  False  False  False  False   True

Operations

자세한 내용은 Basic section on Binary Ops.

Stats

보통 결측치를 제외하고 연산합니다.

기술통계량 보여주기:

In [61]: df.mean() # 열 기준
Out[61]: 
A   -0.004474
B   -0.383981
C   -0.687758
D    5.000000
F    3.000000
dtype: float64

#축을 다르게, 행 기준
In [62]: df.mean(1)
Out[62]: 
2013-01-01    0.872735
2013-01-02    1.431621
2013-01-03    0.707731
2013-01-04    1.395042
2013-01-05    1.883656
2013-01-06    1.592306
Freq: D, dtype: float64

Operating with objects that have different dimensionality and need alignment. In addition, pandas automatically broadcasts along the specified dimension.(다른 차원을 가진 객체들을 가지고 연산을 할 때에는 정렬이 필요합니다. 또한 pandas는 자동으로 특정 차원에 대해 알려줍니다, series와 dataframe은 사실상 다른 차원이지만 연산을 가능하게 해준다는 의미 같네요!)

In [63]: s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2)

In [64]: s
Out[64]: 
2013-01-01    NaN
2013-01-02    NaN
2013-01-03    1.0
2013-01-04    3.0
2013-01-05    5.0
2013-01-06    NaN
Freq: D, dtype: float64

In [65]: df.sub(s, axis='index')
Out[65]: 
                   A         B         C    D    F
2013-01-01       NaN       NaN       NaN  NaN  NaN
2013-01-02       NaN       NaN       NaN  NaN  NaN
2013-01-03 -1.861849 -3.104569 -1.494929  4.0  1.0
2013-01-04 -2.278445 -3.706771 -4.039575  2.0  0.0
2013-01-05 -5.424972 -4.432980 -4.723768  0.0 -1.0
2013-01-06       NaN       NaN       NaN  NaN  NaN

Apply

함수를 데이터에 적용하기:

In [66]: df.apply(np.cumsum)
Out[66]: 
                   A         B         C   D     F
2013-01-01  0.000000  0.000000 -1.509059   5   NaN
2013-01-02  1.212112 -0.173215 -1.389850  10   1.0
2013-01-03  0.350263 -2.277784 -1.884779  15   3.0
2013-01-04  1.071818 -2.984555 -2.924354  20   6.0
2013-01-05  0.646846 -2.417535 -2.648122  25  10.0
2013-01-06 -0.026844 -2.303886 -4.126549  30  15.0

In [67]: df.apply(lambda x: x.max() - x.min())
Out[67]: 
A    2.073961
B    2.671590
C    1.785291
D    0.000000
F    4.000000
dtype: float64

Histogramming

자세한 내용은 Histogramming and Discretization.

# 0~6까지에서 랜덤한 정수를 10개 뽑아 시리즈로 생성
In [68]: s = pd.Series(np.random.randint(0, 7, size=10))

In [69]: s
Out[69]: 
0    4
1    2
2    1
3    2
4    6
5    4
6    4
7    6
8    4
9    4
dtype: int64

# 각 정수가 몇개가 있는지 value_counts
In [70]: s.value_counts()
Out[70]: 
4    5
6    2
2    2
1    1
dtype: int64

String Methods

아래 코드 예시처럼, Series 객체는 배열 내의 문자에 대해 더욱 쉽게 연산할 수 있도록 하는 처리 메서드를 갖추고 있습니다. str에서 문자열의 패턴을 매칭하는 경우, 일반적으로 regular expressions 을 디폴트로 사용한다는 걸 기억하세요!(일부는 항상 정규표현식을 써야할 때가 있습니다. ) 자세한 내용은 Vectorized String Methods

In [71]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])

In [72]: s.str.lower()
Out[72]: 
0       a
1       b
2       c
3    aaba
4    baca
5     NaN
6    caba
7     dog
8     cat
dtype: object

Merge

Concat

pandas provides various facilities for easily combining together Series and DataFrame objects with various kinds of set logic for the indexes and relational algebra functionality in the case of join / merge-type operations.See the Merging section.

(pandas는 Series와 Dataframe 객체를 다양한 방식으로 결합하기 쉽도록 메서드를 제공합니다!)

pandas 객체들을 concat()으로 합치기:

In [73]: df = pd.DataFrame(np.random.randn(10, 4))

In [74]: df
Out[74]: 
          0         1         2         3
0 -0.548702  1.467327 -1.015962 -0.483075
1  1.637550 -1.217659 -0.291519 -1.745505
2 -0.263952  0.991460 -0.919069  0.266046
3 -0.709661  1.669052  1.037882 -1.705775
4 -0.919854 -0.042379  1.247642 -0.009920
5  0.290213  0.495767  0.362949  1.548106
6 -1.131345 -0.089329  0.337863 -0.945867
7 -0.932132  1.956030  0.017587 -0.016692
8 -0.575247  0.254161 -1.143704  0.215897
9  1.193555 -0.077118 -0.408530 -0.862495

# break it into pieces
In [75]: pieces = [df[:3], df[3:7], df[7:]]

In [76]: pd.concat(pieces)
Out[76]: 
          0         1         2         3
0 -0.548702  1.467327 -1.015962 -0.483075
1  1.637550 -1.217659 -0.291519 -1.745505
2 -0.263952  0.991460 -0.919069  0.266046
3 -0.709661  1.669052  1.037882 -1.705775
4 -0.919854 -0.042379  1.247642 -0.009920
5  0.290213  0.495767  0.362949  1.548106
6 -1.131345 -0.089329  0.337863 -0.945867
7 -0.932132  1.956030  0.017587 -0.016692
8 -0.575247  0.254161 -1.143704  0.215897
9  1.193555 -0.077118 -0.408530 -0.862495
위의 방식보다, Dataframe에 열을 붙이는 방식이 상대적으로 더 빠릅니다. 하지만, 행을 합치는 경우에는 데이터의 복사가 요구되거나 더 expensive 할 수 있습니다. Dataframe에 반복적인 작업으로 열을 추가하는 것보다, 사전에 합칠 내용들을 리스트로 만들어 놓고 위와 같은 방식을 사용하는 것을 추천합니다. 자세한 부분은 Appending to dataframe 참조

Join

join는 SQL 스타일의 병합입니다. 자세한 내용은  Database style joining을 참고하세요

In [77]: left = pd.DataFrame({'key': ['foo', 'foo'], 'lval': [1, 2]})

In [78]: right = pd.DataFrame({'key': ['foo', 'foo'], 'rval': [4, 5]})

In [79]: left
Out[79]: 
   key  lval
0  foo     1
1  foo     2

In [80]: right
Out[80]: 
   key  rval
0  foo     4
1  foo     5

In [81]: pd.merge(left, right, on='key')
Out[81]: 
   key  lval  rval
0  foo     1     4
1  foo     1     5
2  foo     2     4
3  foo     2     5

Grouping

"group by"를 통하여 우리는 아래와 같은 것들을 추가적으로 진행할 수 있습니다. 자세한 내용은 Grouping section.

- Splitting 데이터를 특정 기준으로 나눠진 그룹단위로 쪼개기

- Applying 하나의 그룹에 독립적으로 함수 적용하기

- Combining 결과를 데이터 구조 안으로 결합하기 

In [87]: df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar',
   ....:                          'foo', 'bar', 'foo', 'foo'],
   ....:                    'B': ['one', 'one', 'two', 'three',
   ....:                          'two', 'two', 'one', 'three'],
   ....:                    'C': np.random.randn(8),
   ....:                    'D': np.random.randn(8)})
   ....: 

In [88]: df
Out[88]: 
     A      B         C         D
0  foo    one  1.346061 -1.577585
1  bar    one  1.511763  0.396823
2  foo    two  1.627081 -0.105381
3  bar  three -0.990582 -0.532532
4  foo    two -0.441652  1.453749
5  bar    two  1.211526  1.208843
6  foo    one  0.268520 -0.080952
7  foo  three  0.024580 -0.264610

그룹핑한 결과에 대해 sum() 함수를 적용하기:

In [89]: df.groupby('A').sum()
Out[89]: 
            C         D
A                      
bar  1.732707  1.073134
foo  2.824590 -0.574779

계층에 따라 그룹핑 한 후에도 함수 적용이 가능합니다:

In [90]: df.groupby(['A', 'B']).sum()
Out[90]: 
                  C         D
A   B                        
bar one    1.511763  0.396823
    three -0.990582 -0.532532
    two    1.211526  1.208843
foo one    1.614581 -1.658537
    three  0.024580 -0.264610
    two    1.185429  1.348368

Reshaping

자세한 내용은 Hierarchical Indexing and Reshaping.

Stack

In [91]: tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
   ....:                      'foo', 'foo', 'qux', 'qux'],
   ....:                     ['one', 'two', 'one', 'two',
   ....:                      'one', 'two', 'one', 'two']]))
   ....: 

In [92]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])

In [93]: df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])

In [94]: df2 = df[:4]

In [95]: df2
Out[95]: 
                     A         B
first second                    
bar   one    -0.727965 -0.589346
      two     0.339969 -0.693205
baz   one    -0.339355  0.593616
      two     0.884345  1.591431

 stack()메서드는 데이터프레임 열의 계층을 "압축" 하기도 합니다.

In [96]: stacked = df2.stack()

In [97]: stacked
Out[97]: 
first  second   
bar    one     A   -0.727965
               B   -0.589346
       two     A    0.339969
               B   -0.693205
baz    one     A   -0.339355
               B    0.593616
       two     A    0.884345
               B    1.591431
dtype: float64

이미 한번 "stacked"된 Dataframe이나 Series(인덱스가 multi index인 경우), 반대의 연산은 unstack()으로, 가장 마지막 계층을 unstack 해줍니다.:

In [98]: stacked.unstack()
Out[98]: 
                     A         B
first second                    
bar   one    -0.727965 -0.589346
      two     0.339969 -0.693205
baz   one    -0.339355  0.593616
      two     0.884345  1.591431

In [99]: stacked.unstack(1)
Out[99]: 
second        one       two
first                      
bar   A -0.727965  0.339969
      B -0.589346 -0.693205
baz   A -0.339355  0.884345
      B  0.593616  1.591431

In [100]: stacked.unstack(0)
Out[100]: 
first          bar       baz
second                      
one    A -0.727965 -0.339355
       B -0.589346  0.593616
two    A  0.339969  0.884345
       B -0.693205  1.591431

Pivot tables

자세한 내용은 Pivot Tables. 참조

In [101]: df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 3,
   .....:                    'B': ['A', 'B', 'C'] * 4,
   .....:                    'C': ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2,
   .....:                    'D': np.random.randn(12),
   .....:                    'E': np.random.randn(12)})
   .....: 

In [102]: df
Out[102]: 
        A  B    C         D         E
0     one  A  foo -1.202872  0.047609
1     one  B  foo -1.814470 -0.136473
2     two  C  foo  1.018601 -0.561757
3   three  A  bar -0.595447 -1.623033
4     one  B  bar  1.395433  0.029399
5     one  C  bar -0.392670 -0.542108
6     two  A  foo  0.007207  0.282696
7   three  B  foo  1.928123 -0.087302
8     one  C  foo -0.055224 -1.575170
9     one  A  bar  2.395985  1.771208
10    two  B  bar  1.552825  0.816482
11  three  C  bar  0.166599  1.100230

위의 데이터들을 아주 간단!하게 피봇테이블로 만들 수 있습니다.

In [103]: pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'])
Out[103]: 
C             bar       foo
A     B                    
one   A  2.395985 -1.202872
      B  1.395433 -1.814470
      C -0.392670 -0.055224
three A -0.595447       NaN
      B       NaN  1.928123
      C  0.166599       NaN
two   A       NaN  0.007207
      B  1.552825       NaN
      C       NaN  1.018601

Time series

pandas 에는 freq를 변환하기 위한 간단하고, 강력하고 효율적인 resample 연산 메서드들이 있습니다. 금융 분야에서 주로 사용되고 있습니다. 자세한 내용은 Time Series section.

In [104]: rng = pd.date_range('1/1/2012', periods=100, freq='S')

In [105]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)

In [106]: ts.resample('5Min').sum()
Out[106]: 
2012-01-01    24182
Freq: 5T, dtype: int64

타임존도 표현할 수 있다.

In [107]: rng = pd.date_range('3/6/2012 00:00', periods=5, freq='D')

In [108]: ts = pd.Series(np.random.randn(len(rng)), rng)

In [109]: ts
Out[109]: 
2012-03-06    1.857704
2012-03-07   -1.193545
2012-03-08    0.677510
2012-03-09   -0.153931
2012-03-10    0.520091
Freq: D, dtype: float64

In [110]: ts_utc = ts.tz_localize('UTC')

In [111]: ts_utc
Out[111]: 
2012-03-06 00:00:00+00:00    1.857704
2012-03-07 00:00:00+00:00   -1.193545
2012-03-08 00:00:00+00:00    0.677510
2012-03-09 00:00:00+00:00   -0.153931
2012-03-10 00:00:00+00:00    0.520091
Freq: D, dtype: float64

다른 타임존으로 변경하기:

In [112]: ts_utc.tz_convert('US/Eastern')
Out[112]: 
2012-03-05 19:00:00-05:00    1.857704
2012-03-06 19:00:00-05:00   -1.193545
2012-03-07 19:00:00-05:00    0.677510
2012-03-08 19:00:00-05:00   -0.153931
2012-03-09 19:00:00-05:00    0.520091
Freq: D, dtype: float64

표현되는 기간 단위 : (%y%m , %y%m%d 등)

In [113]: rng = pd.date_range('1/1/2012', periods=5, freq='M')

In [114]: ts = pd.Series(np.random.randn(len(rng)), index=rng)

In [115]: ts
Out[115]: 
2012-01-31   -1.475051
2012-02-29    0.722570
2012-03-31   -0.322646
2012-04-30   -1.601631
2012-05-31    0.778033
Freq: M, dtype: float64

In [116]: ps = ts.to_period()

In [117]: ps
Out[117]: 
2012-01   -1.475051
2012-02    0.722570
2012-03   -0.322646
2012-04   -1.601631
2012-05    0.778033
Freq: M, dtype: float64

In [118]: ps.to_timestamp()
Out[118]: 
2012-01-01   -1.475051
2012-02-01    0.722570
2012-03-01   -0.322646
2012-04-01   -1.601631
2012-05-01    0.778033
Freq: MS, dtype: float64

이렇게 period와 timestamp 단위를 변환해주는 것은 몇몇 수학적 연산을 가능하게 해줍니다. 

In [119]: prng = pd.period_range('1990Q1', '2000Q4', freq='Q-NOV')

In [120]: ts = pd.Series(np.random.randn(len(prng)), prng)

In [121]: ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9

In [122]: ts.head()
Out[122]: 
1990-03-01 09:00   -0.289342
1990-06-01 09:00    0.233141
1990-09-01 09:00   -0.223540
1990-12-01 09:00    0.542054
1991-03-01 09:00   -0.688585
Freq: H, dtype: float64

Categoricals

pandas에는 범주형 데이터 또한 사용이 가능합니다. 자세한 내용은  categorical introduction and the API documentation. 를 참고하세요

In [123]: df = pd.DataFrame({"id": [1, 2, 3, 4, 5, 6],
   .....:                    "raw_grade": ['a', 'b', 'b', 'a', 'a', 'e']})
   .....: 

위의 raw_grade를 범주형 타입으로 바꿔줍니다:

In [124]: df["grade"] = df["raw_grade"].astype("category")

In [125]: df["grade"]
Out[125]: 
0    a
1    b
2    b
3    a
4    a
5    e
Name: grade, dtype: category
Categories (3, object): ['a', 'b', 'e']

범주형 데이터의 명칭을 좀 더 실용적으로 쓰일 수 있도록 바꿔줄 수 있습니다.

In [126]: df["grade"].cat.categories = ["very good", "good", "very bad"]

카테고리를 지정합과 동시에 기존에 포함되어 있지 않은 카테고리를 새롭게 지정할 수 있습니다. (Series.cat()과 관련된 메서드들은 기본적으로 series 형태를 반환합니다)

In [127]: df["grade"] = df["grade"].cat.set_categories(["very bad", "bad", "medium",
   .....:                                               "good", "very good"])
   .....: 

In [128]: df["grade"]
Out[128]: 
0    very good
1         good
2         good
3    very good
4    very good
5     very bad
Name: grade, dtype: category
Categories (5, object): ['very bad', 'bad', 'medium', 'good', 'very good']

문자열 순서대로가 아니라, 카테고리 내의 순서대로 정렬이 가능합니다

In [129]: df.sort_values(by="grade")
Out[129]: 
   id raw_grade      grade
5   6         e   very bad
1   2         b       good
2   3         b       good
0   1         a  very good
3   4         a  very good
4   5         a  very good

카테고리끼리 그룹으로 묶었을 때, 데이터가 없는 명목형 변수 또한 표시해줍니다.

In [130]: df.groupby("grade").size()
Out[130]: 
grade
very bad     1
bad          0  # 데이터가 없는 명목형 변수
medium       0  # 데이터가 없는 명목형 변수
good         2
very good    3
dtype: int64

Plotting

자세한 내용은 Plotting 을 참조

아래 예시에는, 일반적으로 사용되는 matplotlib api 를 사용했습니다.

In [131]: import matplotlib.pyplot as plt

In [132]: plt.close('all')
In [133]: ts = pd.Series(np.random.randn(1000),
   .....:                index=pd.date_range('1/1/2000', periods=1000))
   .....: 

In [134]: ts = ts.cumsum()

In [135]: ts.plot()
Out[135]: <matplotlib.axes._subplots.AxesSubplot at 0x7fe295d72160>

DataFrame에서, plot() 메서드는 모든 라벨의 columns를 편하게 그래프로 표현할 수 있습니다. 

In [136]: df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index,
   .....:                   columns=['A', 'B', 'C', 'D'])
   .....: 

In [137]: df = df.cumsum()

In [138]: plt.figure()
Out[138]: <Figure size 640x480 with 0 Axes>

In [139]: df.plot()
Out[139]: <matplotlib.axes._subplots.AxesSubplot at 0x7fe295d17c10>

In [140]: plt.legend(loc='best')
Out[140]: <matplotlib.legend.Legend at 0x7fe295c94fd0>

Getting data in/out

Writing to a csv file.csv 파일을 만드는 방법

In [141]: df.to_csv('foo.csv')

Reading from a csv file. csv 파일 읽어오는 방법

In [142]: pd.read_csv('foo.csv')
Out[142]: 
     Unnamed: 0          A          B          C          D
0    2000-01-01   0.350262   0.843315   1.798556   0.782234
1    2000-01-02  -0.586873   0.034907   1.923792  -0.562651
2    2000-01-03  -1.245477  -0.963406   2.269575  -1.612566
3    2000-01-04  -0.252830  -0.498066   3.176886  -1.275581
4    2000-01-05  -1.044057   0.118042   2.768571   0.386039
..          ...        ...        ...        ...        ...
995  2002-09-22 -48.017654  31.474551  69.146374 -47.541670
996  2002-09-23 -47.207912  32.627390  68.505254 -48.828331
997  2002-09-24 -48.907133  31.990402  67.310924 -49.391051
998  2002-09-25 -50.146062  33.716770  67.717434 -49.037577
999  2002-09-26 -49.724318  33.479952  68.108014 -48.822030

[1000 rows x 5 columns]

HDF5

hdf5를 쓰는 방법  HDFStores.

In [143]: df.to_hdf('foo.h5', 'df')

hdf5를 읽는 방법:

In [144]: pd.read_hdf('foo.h5', 'df')
Out[144]: 
                    A          B          C          D
2000-01-01   0.350262   0.843315   1.798556   0.782234
2000-01-02  -0.586873   0.034907   1.923792  -0.562651
2000-01-03  -1.245477  -0.963406   2.269575  -1.612566
2000-01-04  -0.252830  -0.498066   3.176886  -1.275581
2000-01-05  -1.044057   0.118042   2.768571   0.386039
...               ...        ...        ...        ...
2002-09-22 -48.017654  31.474551  69.146374 -47.541670
2002-09-23 -47.207912  32.627390  68.505254 -48.828331
2002-09-24 -48.907133  31.990402  67.310924 -49.391051
2002-09-25 -50.146062  33.716770  67.717434 -49.037577
2002-09-26 -49.724318  33.479952  68.108014 -48.822030

[1000 rows x 4 columns]

Excel

 MS Excel

엑셀 파일을 작성하는 방법

In [145]: df.to_excel('foo.xlsx', sheet_name='Sheet1')

엑셀 파일을 읽는 방법

In [146]: pd.read_excel('foo.xlsx', 'Sheet1', index_col=None, na_values=['NA'])
Out[146]: 
    Unnamed: 0          A          B          C          D
0   2000-01-01   0.350262   0.843315   1.798556   0.782234
1   2000-01-02  -0.586873   0.034907   1.923792  -0.562651
2   2000-01-03  -1.245477  -0.963406   2.269575  -1.612566
3   2000-01-04  -0.252830  -0.498066   3.176886  -1.275581
4   2000-01-05  -1.044057   0.118042   2.768571   0.386039
..         ...        ...        ...        ...        ...
995 2002-09-22 -48.017654  31.474551  69.146374 -47.541670
996 2002-09-23 -47.207912  32.627390  68.505254 -48.828331
997 2002-09-24 -48.907133  31.990402  67.310924 -49.391051
998 2002-09-25 -50.146062  33.716770  67.717434 -49.037577
999 2002-09-26 -49.724318  33.479952  68.108014 -48.822030

[1000 rows x 5 columns]

Gotchas

간혹 작업을 진행하다 보면 아래와 같은 exception이 나올 수 있습니다. 

>>> if pd.Series([False, True, False]):
...     print("I was true")
Traceback
    ...
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().

그런 경우,  Comparisons ,  Gotchas를 보시고 해결방법을 찾아보세요!


저도 pandas 를 다룬지 몇 개월 되어가지만, 제가 얼마나 부족했는지 깨닫게 되는 번역작업이었네요! 

화이팅 주니어 호승 


참고문헌

 

10 minutes to pandas — pandas 1.1.1 documentation

This is a short introduction to pandas, geared mainly for new users. You can see more complex recipes in the Cookbook. Time series pandas has simple, powerful, and efficient functionality for performing resampling operations during frequency conversion (e.

pandas.pydata.org

 

 

반응형